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