odsyntax.c revision 96798
1121472Sume/*-
278064Sume * Copyright (c) 1990, 1993
362914Sume *	The Regents of the University of California.  All rights reserved.
462914Sume *
562914Sume * Redistribution and use in source and binary forms, with or without
662914Sume * modification, are permitted provided that the following conditions
762914Sume * are met:
862914Sume * 1. Redistributions of source code must retain the above copyright
962914Sume *    notice, this list of conditions and the following disclaimer.
1062914Sume * 2. Redistributions in binary form must reproduce the above copyright
1162914Sume *    notice, this list of conditions and the following disclaimer in the
1262914Sume *    documentation and/or other materials provided with the distribution.
1362914Sume * 3. All advertising materials mentioning features or use of this software
1462914Sume *    must display the following acknowledgement:
1562914Sume *	This product includes software developed by the University of
1662914Sume *	California, Berkeley and its contributors.
1762914Sume * 4. Neither the name of the University nor the names of its contributors
1862914Sume *    may be used to endorse or promote products derived from this software
1962914Sume *    without specific prior written permission.
2062914Sume *
2162914Sume * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2262914Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2362914Sume * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2462914Sume * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2562914Sume * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2662914Sume * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2762914Sume * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2862914Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2962914Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3062914Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31216586Scharnier * SUCH DAMAGE.
32216586Scharnier */
33216586Scharnier
34216586Scharnier#ifndef lint
3562914Sume#if 0
3662914Sumestatic char sccsid[] = "@(#)odsyntax.c	8.2 (Berkeley) 5/4/95";
3762914Sume#endif
3862914Sumestatic const char rcsid[] =
3962914Sume  "$FreeBSD: head/usr.bin/hexdump/odsyntax.c 96798 2002-05-17 08:54:32Z tjr $";
40121472Sume#endif /* not lint */
4162914Sume
4262914Sume#include <sys/types.h>
4362914Sume
4462914Sume#include <ctype.h>
4562914Sume#include <err.h>
4662914Sume#include <errno.h>
4762914Sume#include <float.h>
4862914Sume#include <stdio.h>
4962914Sume#include <stdlib.h>
5062914Sume#include <string.h>
5162914Sume#include <unistd.h>
5262914Sume
5362914Sume#include "hexdump.h"
5462914Sume
5562914Sume#define PADDING	"         "
5662914Sume
5762914Sumeint odmode;
58121472Sume
59121472Sumestatic void odadd(const char *);
60121472Sumestatic void odformat(const char *);
61121472Sumestatic const char *odformatfp(char, const char *);
62121472Sumestatic const char *odformatint(char, const char *);
63121472Sumestatic void odoffset(int, char ***);
64121472Sumestatic void odusage(void);
65121472Sume
66121472Sumevoid
67121472Sumeoldsyntax(argc, argvp)
68121472Sume	int argc;
69121472Sume	char ***argvp;
70121472Sume{
71121472Sume	static char empty[] = "", padding[] = PADDING;
72121472Sume	int ch;
73121472Sume	char **argv, *end;
74121472Sume
75121472Sume	/* Add initial (default) address format. -A may change it later. */
76121472Sume#define	TYPE_OFFSET	7
7762914Sume	add("\"%07.7_Ao\n\"");
7862914Sume	add("\"%07.7_ao  \"");
79121472Sume
8062914Sume	odmode = 1;
8162914Sume	argv = *argvp;
8262914Sume	while ((ch = getopt(argc, argv, "A:aBbcDdeFfHhIij:LlN:Oost:vXx")) != -1)
8362914Sume		switch (ch) {
8462914Sume		case 'A':
8562914Sume			switch (*optarg) {
8662914Sume			case 'd': case 'o': case 'x':
8762914Sume				fshead->nextfu->fmt[TYPE_OFFSET] = *optarg;
8862914Sume				fshead->nextfs->nextfu->fmt[TYPE_OFFSET] =
8962914Sume				    *optarg;
9062914Sume				break;
9162914Sume			case 'n':
9262914Sume				fshead->nextfu->fmt = empty;
9362914Sume				fshead->nextfs->nextfu->fmt = padding;
9462914Sume				break;
9562914Sume			default:
9662914Sume				errx(1, "%s: invalid address base", optarg);
9762914Sume			}
9862914Sume			break;
9962914Sume		case 'a':
10062914Sume			odformat("a");
10162914Sume			break;
10262914Sume		case 'B':
103121472Sume		case 'o':
10478064Sume			odformat("o2");
10562914Sume			break;
10662914Sume		case 'b':
107121472Sume			odformat("o1");
10862914Sume			break;
10962914Sume		case 'c':
110121472Sume			odformat("c");
11162914Sume			break;
11262914Sume		case 'd':
11362914Sume			odformat("u2");
11462914Sume			break;
11562914Sume		case 'D':
11662914Sume			odformat("u4");
11762914Sume			break;
11862914Sume		case 'e':		/* undocumented in od */
11962914Sume		case 'F':
12062914Sume			odformat("fD");
12162914Sume			break;
12262914Sume		case 'f':
12362914Sume			odformat("fF");
12462914Sume			break;
12562914Sume		case 'H':
12662914Sume		case 'X':
12778064Sume			odformat("x4");
12862914Sume			break;
12962914Sume		case 'h':
13062914Sume		case 'x':
13162914Sume			odformat("x2");
13262914Sume			break;
13362914Sume		case 'I':
13462914Sume		case 'L':
13562914Sume		case 'l':
13662914Sume			odformat("dL");
13762914Sume			break;
13862914Sume		case 'i':
13962914Sume			odformat("dI");
14062914Sume			break;
14162914Sume		case 'j':
14262914Sume			errno = 0;
14362914Sume			skip = strtoll(optarg, &end, 0);
14462914Sume			if (*end == 'b')
14562914Sume				skip *= 512;
14662914Sume			else if (*end == 'k')
14762914Sume				skip *= 1024;
14862914Sume			else if (*end == 'm')
14962914Sume				skip *= 1048576L;
15062914Sume			if (errno != 0 || skip < 0 || strlen(end) > 1)
15162914Sume				errx(1, "%s: invalid skip amount", optarg);
15262914Sume			break;
15362914Sume		case 'N':
15462914Sume			if ((length = atoi(optarg)) <= 0)
15562914Sume				errx(1, "%s: invalid length", optarg);
15662914Sume			break;
15762914Sume		case 'O':
15862914Sume			odformat("o4");
15962914Sume			break;
16062914Sume		case 's':
16162914Sume			odformat("d2");
16262914Sume			break;
16362914Sume		case 't':
16462914Sume			odformat(optarg);
165121472Sume			break;
166121472Sume		case 'v':
16762914Sume			vflag = ALL;
16862914Sume			break;
16962914Sume		case '?':
17062914Sume		default:
17162914Sume			odusage();
17262914Sume		}
17362914Sume
17462914Sume	if (fshead->nextfs->nextfs == NULL)
17562914Sume		odformat("oS");
17662914Sume
17762914Sume	argc -= optind;
17862914Sume	*argvp += optind;
17962914Sume
18062914Sume	if (argc)
18162914Sume		odoffset(argc, argvp);
18262914Sume}
18362914Sume
184121472Sumestatic void
185121472Sumeodusage(void)
186121472Sume{
187121472Sume
18862914Sume	fprintf(stderr,
189121472Sume"usage: od [-aBbcDdeFfHhIiLlOosvXx] [-A base] [-j skip] [-N length] [-t type]\n");
19062914Sume	fprintf(stderr,
19162914Sume"          [[+]offset[.][Bb]] [file ...]\n");
19262914Sume	exit(1);
193121472Sume}
194121472Sume
19562914Sumestatic void
19662914Sumeodoffset(argc, argvp)
19762914Sume	int argc;
19862914Sume	char ***argvp;
19962914Sume{
20062914Sume	unsigned char *p, *num, *end;
20162914Sume	int base;
20262914Sume
20362914Sume	/*
20462914Sume	 * The offset syntax of od(1) was genuinely bizarre.  First, if
20562914Sume	 * it started with a plus it had to be an offset.  Otherwise, if
20662914Sume	 * there were at least two arguments, a number or lower-case 'x'
20762914Sume	 * followed by a number makes it an offset.  By default it was
20862914Sume	 * octal; if it started with 'x' or '0x' it was hex.  If it ended
20962914Sume	 * in a '.', it was decimal.  If a 'b' or 'B' was appended, it
21062914Sume	 * multiplied the number by 512 or 1024 byte units.  There was
21162914Sume	 * no way to assign a block count to a hex offset.
212121472Sume	 *
213121472Sume	 * We assume it's a file if the offset is bad.
214121472Sume	 */
21562914Sume	p = argc == 1 ? (*argvp)[0] : (*argvp)[1];
216121472Sume
217121472Sume	if (*p != '+' && (argc < 2 ||
218121472Sume	    (!isdigit(p[0]) && (p[0] != 'x' || !isxdigit(p[1])))))
219121472Sume		return;
220121472Sume
221121472Sume	base = 0;
222121472Sume	/*
223121472Sume	 * skip over leading '+', 'x[0-9a-fA-f]' or '0x', and
224121472Sume	 * set base.
225121472Sume	 */
226121472Sume	if (p[0] == '+')
227121472Sume		++p;
228121472Sume	if (p[0] == 'x' && isxdigit(p[1])) {
229121472Sume		++p;
230121472Sume		base = 16;
231121472Sume	} else if (p[0] == '0' && p[1] == 'x') {
232121472Sume		p += 2;
233121472Sume		base = 16;
234121472Sume	}
235121472Sume
236121472Sume	/* skip over the number */
237121472Sume	if (base == 16)
238121472Sume		for (num = p; isxdigit(*p); ++p);
239121472Sume	else
240121472Sume		for (num = p; isdigit(*p); ++p);
241121472Sume
242121472Sume	/* check for no number */
243121472Sume	if (num == p)
244121472Sume		return;
245121472Sume
246121472Sume	/* if terminates with a '.', base is decimal */
247121472Sume	if (*p == '.') {
248121472Sume		if (base)
24962914Sume			return;
25062914Sume		base = 10;
25162914Sume	}
252121472Sume
25362914Sume	skip = strtoll(num, (char **)&end, base ? base : 8);
25462914Sume
25562914Sume	/* if end isn't the same as p, we got a non-octal digit */
25662914Sume	if (end != p) {
25762914Sume		skip = 0;
25862914Sume		return;
25962914Sume	}
26078064Sume
26162914Sume	if (*p) {
26262914Sume		if (*p == 'B') {
26362914Sume			skip *= 1024;
26462914Sume			++p;
265121472Sume		} else if (*p == 'b') {
26662914Sume			skip *= 512;
26762914Sume			++p;
268121472Sume		}
269121472Sume	}
270121472Sume
271121472Sume	if (*p) {
272121472Sume		skip = 0;
273121472Sume		return;
274121472Sume	}
275121472Sume
276121472Sume	/*
277121472Sume	 * If the offset uses a non-octal base, the base of the offset
278121472Sume	 * is changed as well.  This isn't pretty, but it's easy.
279121472Sume	 */
280121472Sume	if (base == 16) {
281121472Sume		fshead->nextfu->fmt[TYPE_OFFSET] = 'x';
282121472Sume		fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'x';
283121472Sume	} else if (base == 10) {
28462914Sume		fshead->nextfu->fmt[TYPE_OFFSET] = 'd';
28562914Sume		fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'd';
286121472Sume	}
28762914Sume
28862914Sume	/* Terminate file list. */
28962914Sume	(*argvp)[1] = NULL;
29062914Sume}
291121472Sume
29262914Sumestatic void
29362914Sumeodformat(const char *fmt)
29462914Sume{
29562914Sume	char fchar;
29662914Sume
29762914Sume	while (*fmt != '\0') {
298121472Sume		switch ((fchar = *fmt++)) {
29962914Sume		case 'a':
30062914Sume			odadd("16/1 \"%3_u \" \"\\n\"");
30162914Sume			break;
30262914Sume		case 'c':
30362914Sume			odadd("16/1 \"%3_c \" \"\\n\"");
30462914Sume			break;
30562914Sume		case 'o': case 'u': case 'd': case 'x':
30662914Sume			fmt = odformatint(fchar, fmt);
30762914Sume			break;
30862914Sume		case 'f':
309121472Sume			fmt = odformatfp(fchar, fmt);
31062914Sume			break;
31162914Sume		default:
31262914Sume			errx(1, "%c: unrecognised format character", fchar);
31362914Sume		}
314121472Sume	}
31562914Sume}
31662914Sume
31762914Sumestatic const char *
31862914Sumeodformatfp(char fchar __unused, const char *fmt)
319121472Sume{
32062914Sume	size_t isize;
32162914Sume	int digits;
32262914Sume	char *end, *hdfmt;
32362914Sume
32462914Sume	isize = sizeof(double);
32562914Sume	switch (*fmt) {
32662914Sume	case 'F':
32762914Sume		isize = sizeof(float);
32862914Sume		fmt++;
32962914Sume		break;
330121472Sume	case 'D':
33162914Sume		isize = sizeof(double);
33262914Sume		fmt++;
33362914Sume		break;
33462914Sume	case 'L':
33562914Sume		isize = sizeof(long double);
33662914Sume		fmt++;
337216586Scharnier		break;
338121472Sume	default:
33962914Sume		if (isdigit((unsigned char)*fmt)) {
34062914Sume			errno = 0;
34162914Sume			isize = (size_t)strtoul(fmt, &end, 10);
34262914Sume			if (errno != 0 || isize == 0)
34362914Sume				errx(1, "%s: invalid size", fmt);
34462914Sume			fmt = (const char *)end;
34562914Sume		}
34662914Sume	}
34762914Sume	switch (isize) {
34862914Sume	case sizeof(float):
349216586Scharnier		digits = FLT_DIG;
35062914Sume		break;
35162914Sume	case sizeof(double):
35262914Sume		digits = DBL_DIG;
35362914Sume		break;
354	default:
355		if (isize == sizeof(long double))
356			digits = LDBL_DIG;
357		else
358			errx(1, "unsupported floating point size %lu",
359			    (u_long)isize);
360	}
361
362	asprintf(&hdfmt, "%lu/%lu \" %%%d.%de \" \"\\n\"",
363	    16UL / (u_long)isize, (u_long)isize, digits + 8, digits);
364	if (hdfmt == NULL)
365		err(1, NULL);
366	odadd(hdfmt);
367	free(hdfmt);
368
369	return (fmt);
370}
371
372static const char *
373odformatint(char fchar, const char *fmt)
374{
375	unsigned long long n;
376	size_t isize;
377	int digits;
378	char *end, *hdfmt;
379
380	isize = sizeof(int);
381	switch (*fmt) {
382	case 'C':
383		isize = sizeof(char);
384		fmt++;
385		break;
386	case 'I':
387		isize = sizeof(int);
388		fmt++;
389		break;
390	case 'L':
391		isize = sizeof(long);
392		fmt++;
393		break;
394	case 'S':
395		isize = sizeof(short);
396		fmt++;
397		break;
398	default:
399		if (isdigit((unsigned char)*fmt)) {
400			errno = 0;
401			isize = (size_t)strtoul(fmt, &end, 10);
402			if (errno != 0 || isize == 0)
403				errx(1, "%s: invalid size", fmt);
404			if (isize != sizeof(char) && isize != sizeof(short) &&
405			    isize != sizeof(int) && isize != sizeof(long))
406				errx(1, "unsupported int size %lu",
407				    (u_long)isize);
408			fmt = (const char *)end;
409		}
410	}
411
412	/*
413	 * Calculate the maximum number of digits we need to
414	 * fit the number. Overestimate for decimal with log
415	 * base 8. We need one extra space for signed numbers
416	 * to store the sign.
417	 */
418	n = (1ULL << (8 * isize)) - 1;
419	digits = 0;
420	while (n != 0) {
421		digits++;
422		n >>= (fchar == 'x') ? 4 : 3;
423	}
424	if (fchar == 'd')
425		digits++;
426	asprintf(&hdfmt, "%lu/%lu \"%*s%%%s%d%c\" \"\\n\"",
427	    16UL / (u_long)isize, (u_long)isize, (int)(4 * isize - digits),
428	    "", (fchar == 'd' || fchar == 'u') ? "" : "0", digits, fchar);
429	if (hdfmt == NULL)
430		err(1, NULL);
431	odadd(hdfmt);
432	free(hdfmt);
433
434	return (fmt);
435}
436
437static void
438odadd(const char *fmt)
439{
440	static int needpad;
441
442	if (needpad)
443		add("\""PADDING"\"");
444	add(fmt);
445	needpad = 1;
446}
447