xref: /freebsd/usr.bin/hexdump/odsyntax.c (revision 1c05a6ea6b849ff95e539c31adea887c644a6a01)
1 /*-
2  * Copyright (c) 1990, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #ifndef lint
31 #if 0
32 static char sccsid[] = "@(#)odsyntax.c	8.2 (Berkeley) 5/4/95";
33 #endif
34 #endif /* not lint */
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37 
38 #include <sys/types.h>
39 
40 #include <ctype.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <float.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 
49 #include "hexdump.h"
50 
51 #define PADDING	"         "
52 
53 int odmode;
54 
55 static void odadd(const char *);
56 static void odformat(const char *);
57 static const char *odformatfp(char, const char *);
58 static const char *odformatint(char, const char *);
59 static void odoffset(int, char ***);
60 static void odusage(void);
61 
62 void
63 oldsyntax(int argc, char ***argvp)
64 {
65 	static char empty[] = "", padding[] = PADDING;
66 	int ch;
67 	char **argv, *end;
68 
69 	/* Add initial (default) address format. -A may change it later. */
70 #define	TYPE_OFFSET	7
71 	add("\"%07.7_Ao\n\"");
72 	add("\"%07.7_ao  \"");
73 
74 	odmode = 1;
75 	argv = *argvp;
76 	while ((ch = getopt(argc, argv, "A:aBbcDdeFfHhIij:LlN:Oost:vXx")) != -1)
77 		switch (ch) {
78 		case 'A':
79 			switch (*optarg) {
80 			case 'd': case 'o': case 'x':
81 				fshead->nextfu->fmt[TYPE_OFFSET] = *optarg;
82 				fshead->nextfs->nextfu->fmt[TYPE_OFFSET] =
83 				    *optarg;
84 				break;
85 			case 'n':
86 				fshead->nextfu->fmt = empty;
87 				fshead->nextfs->nextfu->fmt = padding;
88 				break;
89 			default:
90 				errx(1, "%s: invalid address base", optarg);
91 			}
92 			break;
93 		case 'a':
94 			odformat("a");
95 			break;
96 		case 'B':
97 		case 'o':
98 			odformat("o2");
99 			break;
100 		case 'b':
101 			odformat("o1");
102 			break;
103 		case 'c':
104 			odformat("c");
105 			break;
106 		case 'd':
107 			odformat("u2");
108 			break;
109 		case 'D':
110 			odformat("u4");
111 			break;
112 		case 'e':		/* undocumented in od */
113 		case 'F':
114 			odformat("fD");
115 			break;
116 		case 'f':
117 			odformat("fF");
118 			break;
119 		case 'H':
120 		case 'X':
121 			odformat("x4");
122 			break;
123 		case 'h':
124 		case 'x':
125 			odformat("x2");
126 			break;
127 		case 'I':
128 		case 'L':
129 		case 'l':
130 			odformat("dL");
131 			break;
132 		case 'i':
133 			odformat("dI");
134 			break;
135 		case 'j':
136 			errno = 0;
137 			skip = strtoll(optarg, &end, 0);
138 			if (*end == 'b')
139 				skip *= 512;
140 			else if (*end == 'k')
141 				skip *= 1024;
142 			else if (*end == 'm')
143 				skip *= 1048576L;
144 			if (errno != 0 || skip < 0 || strlen(end) > 1)
145 				errx(1, "%s: invalid skip amount", optarg);
146 			break;
147 		case 'N':
148 			if ((length = atoi(optarg)) <= 0)
149 				errx(1, "%s: invalid length", optarg);
150 			break;
151 		case 'O':
152 			odformat("o4");
153 			break;
154 		case 's':
155 			odformat("d2");
156 			break;
157 		case 't':
158 			odformat(optarg);
159 			break;
160 		case 'v':
161 			vflag = ALL;
162 			break;
163 		case '?':
164 		default:
165 			odusage();
166 		}
167 
168 	if (fshead->nextfs->nextfs == NULL)
169 		odformat("oS");
170 
171 	argc -= optind;
172 	*argvp += optind;
173 
174 	if (argc)
175 		odoffset(argc, argvp);
176 }
177 
178 static void
179 odusage(void)
180 {
181 
182 	fprintf(stderr,
183 "usage: od [-aBbcDdeFfHhIiLlOosvXx] [-A base] [-j skip] [-N length] [-t type]\n");
184 	fprintf(stderr,
185 "          [[+]offset[.][Bb]] [file ...]\n");
186 	exit(1);
187 }
188 
189 static void
190 odoffset(int argc, char ***argvp)
191 {
192 	char *p, *num, *end;
193 	int base;
194 
195 	/*
196 	 * The offset syntax of od(1) was genuinely bizarre.  First, if
197 	 * it started with a plus it had to be an offset.  Otherwise, if
198 	 * there were at least two arguments, a number or lower-case 'x'
199 	 * followed by a number makes it an offset.  By default it was
200 	 * octal; if it started with 'x' or '0x' it was hex.  If it ended
201 	 * in a '.', it was decimal.  If a 'b' or 'B' was appended, it
202 	 * multiplied the number by 512 or 1024 byte units.  There was
203 	 * no way to assign a block count to a hex offset.
204 	 *
205 	 * We assume it's a file if the offset is bad.
206 	 */
207 	p = argc == 1 ? (*argvp)[0] : (*argvp)[1];
208 
209 	if (*p != '+' && (argc < 2 ||
210 	    (!isdigit(p[0]) && (p[0] != 'x' || !isxdigit(p[1])))))
211 		return;
212 
213 	base = 0;
214 	/*
215 	 * skip over leading '+', 'x[0-9a-fA-f]' or '0x', and
216 	 * set base.
217 	 */
218 	if (p[0] == '+')
219 		++p;
220 	if (p[0] == 'x' && isxdigit(p[1])) {
221 		++p;
222 		base = 16;
223 	} else if (p[0] == '0' && p[1] == 'x') {
224 		p += 2;
225 		base = 16;
226 	}
227 
228 	/* skip over the number */
229 	if (base == 16)
230 		for (num = p; isxdigit(*p); ++p);
231 	else
232 		for (num = p; isdigit(*p); ++p);
233 
234 	/* check for no number */
235 	if (num == p)
236 		return;
237 
238 	/* if terminates with a '.', base is decimal */
239 	if (*p == '.') {
240 		if (base)
241 			return;
242 		base = 10;
243 	}
244 
245 	skip = strtoll(num, &end, base ? base : 8);
246 
247 	/* if end isn't the same as p, we got a non-octal digit */
248 	if (end != p) {
249 		skip = 0;
250 		return;
251 	}
252 
253 	if (*p) {
254 		if (*p == 'B') {
255 			skip *= 1024;
256 			++p;
257 		} else if (*p == 'b') {
258 			skip *= 512;
259 			++p;
260 		}
261 	}
262 
263 	if (*p) {
264 		skip = 0;
265 		return;
266 	}
267 
268 	/*
269 	 * If the offset uses a non-octal base, the base of the offset
270 	 * is changed as well.  This isn't pretty, but it's easy.
271 	 */
272 	if (base == 16) {
273 		fshead->nextfu->fmt[TYPE_OFFSET] = 'x';
274 		fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'x';
275 	} else if (base == 10) {
276 		fshead->nextfu->fmt[TYPE_OFFSET] = 'd';
277 		fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'd';
278 	}
279 
280 	/* Terminate file list. */
281 	(*argvp)[1] = NULL;
282 }
283 
284 static void
285 odformat(const char *fmt)
286 {
287 	char fchar;
288 
289 	while (*fmt != '\0') {
290 		switch ((fchar = *fmt++)) {
291 		case 'a':
292 			odadd("16/1 \"%3_u \" \"\\n\"");
293 			break;
294 		case 'c':
295 			odadd("16/1 \"%3_c \" \"\\n\"");
296 			break;
297 		case 'o': case 'u': case 'd': case 'x':
298 			fmt = odformatint(fchar, fmt);
299 			break;
300 		case 'f':
301 			fmt = odformatfp(fchar, fmt);
302 			break;
303 		default:
304 			errx(1, "%c: unrecognised format character", fchar);
305 		}
306 	}
307 }
308 
309 static const char *
310 odformatfp(char fchar __unused, const char *fmt)
311 {
312 	size_t isize;
313 	int digits;
314 	char *end, *hdfmt;
315 
316 	isize = sizeof(double);
317 	switch (*fmt) {
318 	case 'F':
319 		isize = sizeof(float);
320 		fmt++;
321 		break;
322 	case 'D':
323 		isize = sizeof(double);
324 		fmt++;
325 		break;
326 	case 'L':
327 		isize = sizeof(long double);
328 		fmt++;
329 		break;
330 	default:
331 		if (isdigit((unsigned char)*fmt)) {
332 			errno = 0;
333 			isize = (size_t)strtoul(fmt, &end, 10);
334 			if (errno != 0 || isize == 0)
335 				errx(1, "%s: invalid size", fmt);
336 			fmt = (const char *)end;
337 		}
338 	}
339 	switch (isize) {
340 	case sizeof(float):
341 		digits = FLT_DIG;
342 		break;
343 	case sizeof(double):
344 		digits = DBL_DIG;
345 		break;
346 	default:
347 		if (isize == sizeof(long double))
348 			digits = LDBL_DIG;
349 		else
350 			errx(1, "unsupported floating point size %lu",
351 			    (u_long)isize);
352 	}
353 
354 	asprintf(&hdfmt, "%lu/%lu \" %%%d.%de \" \"\\n\"",
355 	    16UL / (u_long)isize, (u_long)isize, digits + 8, digits);
356 	if (hdfmt == NULL)
357 		err(1, NULL);
358 	odadd(hdfmt);
359 	free(hdfmt);
360 
361 	return (fmt);
362 }
363 
364 static const char *
365 odformatint(char fchar, const char *fmt)
366 {
367 	unsigned long long n;
368 	size_t isize;
369 	int digits;
370 	char *end, *hdfmt;
371 
372 	isize = sizeof(int);
373 	switch (*fmt) {
374 	case 'C':
375 		isize = sizeof(char);
376 		fmt++;
377 		break;
378 	case 'I':
379 		isize = sizeof(int);
380 		fmt++;
381 		break;
382 	case 'L':
383 		isize = sizeof(long);
384 		fmt++;
385 		break;
386 	case 'S':
387 		isize = sizeof(short);
388 		fmt++;
389 		break;
390 	default:
391 		if (isdigit((unsigned char)*fmt)) {
392 			errno = 0;
393 			isize = (size_t)strtoul(fmt, &end, 10);
394 			if (errno != 0 || isize == 0)
395 				errx(1, "%s: invalid size", fmt);
396 			if (isize != sizeof(char) && isize != sizeof(short) &&
397 			    isize != sizeof(int) && isize != sizeof(long))
398 				errx(1, "unsupported int size %lu",
399 				    (u_long)isize);
400 			fmt = (const char *)end;
401 		}
402 	}
403 
404 	/*
405 	 * Calculate the maximum number of digits we need to
406 	 * fit the number. Overestimate for decimal with log
407 	 * base 8. We need one extra space for signed numbers
408 	 * to store the sign.
409 	 */
410 	n = (1ULL << (8 * isize)) - 1;
411 	digits = 0;
412 	while (n != 0) {
413 		digits++;
414 		n >>= (fchar == 'x') ? 4 : 3;
415 	}
416 	if (fchar == 'd')
417 		digits++;
418 	asprintf(&hdfmt, "%lu/%lu \"%*s%%%s%d%c\" \"\\n\"",
419 	    16UL / (u_long)isize, (u_long)isize, (int)(4 * isize - digits),
420 	    "", (fchar == 'd' || fchar == 'u') ? "" : "0", digits, fchar);
421 	if (hdfmt == NULL)
422 		err(1, NULL);
423 	odadd(hdfmt);
424 	free(hdfmt);
425 
426 	return (fmt);
427 }
428 
429 static void
430 odadd(const char *fmt)
431 {
432 	static int needpad;
433 
434 	if (needpad)
435 		add("\""PADDING"\"");
436 	add(fmt);
437 	needpad = 1;
438 }
439