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