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 * 4. 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
32static 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
53int odmode;
54
55static void odadd(const char *);
56static void odformat(const char *);
57static const char *odformatfp(char, const char *);
58static const char *odformatint(char, const char *);
59static void odoffset(int, char ***);
60static void odusage(void);
61
62void
63oldsyntax(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
178static void
179odusage(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
189static void
190odoffset(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
284static void
285odformat(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
309static const char *
310odformatfp(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
364static const char *
365odformatint(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
429static void
430odadd(const char *fmt)
431{
432	static int needpad;
433
434	if (needpad)
435		add("\""PADDING"\"");
436	add(fmt);
437	needpad = 1;
438}
439