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: src/usr.bin/hexdump/odsyntax.c,v 1.16 2002/09/04 23:29:01 dwmalone Exp $");
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 _n[] = "%_n", 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 = _n; /* mimic regular output */
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			else if (*end == 'g')
149				skip *= 1073741824;
150			else if (*end != '\0')
151				skip = -1;
152			if (errno != 0 || skip < 0 || strlen(end) > 1)
153				errx(1, "%s: invalid skip amount", optarg);
154			break;
155		case 'N':
156			if ((length = atoi(optarg)) <= 0)
157				errx(1, "%s: invalid length", optarg);
158			break;
159		case 'O':
160			odformat("o4");
161			break;
162		case 's':
163			odformat("d2");
164			break;
165		case 't':
166			odformat(optarg);
167			break;
168		case 'v':
169			vflag = ALL;
170			break;
171		case '?':
172		default:
173			odusage();
174		}
175
176	if (fshead->nextfs->nextfs == NULL)
177		odformat("oS");
178
179	argc -= optind;
180	*argvp += optind;
181
182	if (argc)
183		odoffset(argc, argvp);
184}
185
186static void
187odusage(void)
188{
189
190	fprintf(stderr,
191"usage: od [-aBbcDdeFfHhIiLlOosvXx] [-A base] [-j skip] [-N length] [-t type]\n");
192	fprintf(stderr,
193"          [[+]offset[.][Bb]] [file ...]\n");
194	exit(1);
195}
196
197static void
198odoffset(int argc, char ***argvp)
199{
200	unsigned char *p, *q, *num, *end;
201	int base;
202
203	/*
204	 * The offset syntax of od(1) was genuinely bizarre.  First, if
205	 * it started with a plus it had to be an offset.  Otherwise, if
206	 * there were at least two arguments, a number or lower-case 'x'
207	 * followed by a number makes it an offset.  By default it was
208	 * octal; if it started with 'x' or '0x' it was hex.  If it ended
209	 * in a '.', it was decimal.  If a 'b' or 'B' was appended, it
210	 * multiplied the number by 512 or 1024 byte units.  There was
211	 * no way to assign a block count to a hex offset.
212	 *
213	 * We assume it's a file if the offset is bad.
214	 */
215	p = (unsigned char *)(argc == 1 ? (*argvp)[0] : (*argvp)[1]);
216
217	if (*p != '+' && (argc < 2 ||
218	    (!isdigit(p[0]) && (p[0] != 'x' || !isxdigit(p[1])))))
219		return;
220
221	base = 0;
222	/*
223	 * skip over leading '+', 'x[0-9a-fA-f]' or '0x', and
224	 * set base.
225	 */
226	if (p[0] == '+')
227		++p;
228	if (p[0] == 'x' && isxdigit(p[1])) {
229		++p;
230		base = 16;
231	} else if (p[0] == '0' && p[1] == 'x') {
232		p += 2;
233		base = 16;
234	}
235
236	/* skip over the number */
237	if (base == 16)
238		for (num = p; isxdigit(*p); ++p);
239	else
240		for (num = p; isdigit(*p); ++p);
241
242	/* check for no number */
243	if (num == p)
244		return;
245	q = p;
246	/* if terminates with a '.', base is decimal */
247	if (*q == '.') {
248		if (base)
249			return;
250		base = 10;
251		q++;
252	}
253
254	skip = strtoll((const char *)num, (char **)&end, base ? base : 8);
255
256	/* if end isn't the same as p, we got a non-octal digit */
257	if (end != p) {
258		skip = 0;
259		return;
260	}
261
262	if (*q) {
263		if (*q == 'B') {
264			skip *= 1024;
265			++p;
266		} else if (*q == 'b') {
267			skip *= 512;
268			++q;
269		}
270	}
271
272	if (*q) {
273		skip = 0;
274		return;
275	}
276
277	/*
278	 * If the offset uses a non-octal base, the base of the offset
279	 * is changed as well.  This isn't pretty, but it's easy.
280	 */
281	if (base == 16) {
282		fshead->nextfu->fmt[TYPE_OFFSET] = 'x';
283		fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'x';
284	} else if (base == 10) {
285		fshead->nextfu->fmt[TYPE_OFFSET] = 'd';
286		fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'd';
287	}
288
289	/* Terminate file list. */
290	(*argvp)[1] = NULL;
291}
292
293static void
294odformat(const char *fmt)
295{
296	char fchar;
297
298	while (*fmt != '\0') {
299		switch ((fchar = *fmt++)) {
300		case 'a':
301			odadd("16/1 \"%3_u \" \"\\n\"");
302			break;
303		case 'c':
304			odadd("16/1 \"%3_c \" \"\\n\"");
305			break;
306		case 'o': case 'u': case 'd': case 'x':
307			fmt = odformatint(fchar, fmt);
308			break;
309		case 'f':
310			fmt = odformatfp(fchar, fmt);
311			break;
312		default:
313			errx(1, "%c: unrecognised format character", fchar);
314		}
315	}
316}
317
318static const char *
319odformatfp(char fchar __unused, const char *fmt)
320{
321	size_t isize;
322	int digits;
323	char *end, *hdfmt;
324
325	isize = sizeof(double);
326	switch (*fmt) {
327	case 'F':
328		isize = sizeof(float);
329		fmt++;
330		break;
331	case 'D':
332		isize = sizeof(double);
333		fmt++;
334		break;
335	case 'L':
336		isize = sizeof(long double);
337		fmt++;
338		break;
339	default:
340		if (isdigit((unsigned char)*fmt)) {
341			errno = 0;
342			isize = (size_t)strtoul(fmt, &end, 10);
343			if (errno != 0 || isize == 0)
344				errx(1, "%s: invalid size", fmt);
345			fmt = (const char *)end;
346		}
347	}
348	switch (isize) {
349	case sizeof(float):
350		digits = FLT_DIG;
351		break;
352	case sizeof(double):
353		digits = DBL_DIG;
354		break;
355	default:
356		if (isize == sizeof(long double))
357			digits = LDBL_DIG;
358		else
359			errx(1, "unsupported floating point size %lu",
360			    (u_long)isize);
361	}
362
363	asprintf(&hdfmt, "%lu/%lu \" %%%d.%de \" \"\\n\"",
364	    16UL / (u_long)isize, (u_long)isize, digits + 8, digits);
365	if (hdfmt == NULL)
366		err(1, NULL);
367	odadd(hdfmt);
368	free(hdfmt);
369
370	return (fmt);
371}
372
373static const char *
374odformatint(char fchar, const char *fmt)
375{
376	unsigned long long n;
377	size_t isize;
378	int digits;
379	char *end, *hdfmt;
380
381	isize = sizeof(int);
382	switch (*fmt) {
383	case 'C':
384		isize = sizeof(char);
385		fmt++;
386		break;
387	case 'I':
388		isize = sizeof(int);
389		fmt++;
390		break;
391	case 'L':
392		isize = sizeof(long);
393		fmt++;
394		break;
395	case 'S':
396		isize = sizeof(short);
397		fmt++;
398		break;
399	default:
400		if (isdigit((unsigned char)*fmt)) {
401			errno = 0;
402			isize = (size_t)strtoul(fmt, &end, 10);
403			if (errno != 0 || isize == 0)
404				errx(1, "%s: invalid size", fmt);
405			if (isize != sizeof(char) && isize != sizeof(short) &&
406			    isize != sizeof(int) && isize != sizeof(long))
407				errx(1, "unsupported int size %lu",
408				    (u_long)isize);
409			fmt = (const char *)end;
410		}
411	}
412
413	/*
414	 * Calculate the maximum number of digits we need to
415	 * fit the number. Overestimate for decimal with log
416	 * base 8. We need one extra space for signed numbers
417	 * to store the sign.
418	 */
419	n = (1ULL << (8 * isize)) - 1;
420	digits = 0;
421	while (n != 0) {
422		digits++;
423		n >>= (fchar == 'x') ? 4 : 3;
424	}
425	if (fchar == 'd')
426		digits++;
427	asprintf(&hdfmt, "%lu/%lu \"%*s%%%s%d%c\" \"\\n\"",
428	    16UL / (u_long)isize, (u_long)isize, (int)(4 * isize - digits),
429	    "", (fchar == 'd' || fchar == 'u') ? "" : "0", digits, fchar);
430	if (hdfmt == NULL)
431		err(1, NULL);
432	odadd(hdfmt);
433	free(hdfmt);
434
435	return (fmt);
436}
437
438static void
439odadd(const char *fmt)
440{
441	static int needpad;
442
443	if (needpad)
444		add("\""PADDING"\"");
445	add(fmt);
446	needpad = 1;
447}
448