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