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