11590Srgrimes/*-
21590Srgrimes * Copyright (c) 1990, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 4. Neither the name of the University nor the names of its contributors
141590Srgrimes *    may be used to endorse or promote products derived from this software
151590Srgrimes *    without specific prior written permission.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271590Srgrimes * SUCH DAMAGE.
281590Srgrimes */
291590Srgrimes
301590Srgrimes#ifndef lint
3127315Scharnier#if 0
3223693Speterstatic char sccsid[] = "@(#)odsyntax.c	8.2 (Berkeley) 5/4/95";
3327315Scharnier#endif
341590Srgrimes#endif /* not lint */
3599112Sobrien#include <sys/cdefs.h>
3699112Sobrien__FBSDID("$FreeBSD$");
371590Srgrimes
381590Srgrimes#include <sys/types.h>
391590Srgrimes
4023693Speter#include <ctype.h>
4127315Scharnier#include <err.h>
4296795Stjr#include <errno.h>
4396795Stjr#include <float.h>
4423693Speter#include <stdio.h>
451590Srgrimes#include <stdlib.h>
4696795Stjr#include <string.h>
4723693Speter#include <unistd.h>
4823693Speter
491590Srgrimes#include "hexdump.h"
501590Srgrimes
5196795Stjr#define PADDING	"         "
5296795Stjr
5396787Stjrint odmode;
541590Srgrimes
5596795Stjrstatic void odadd(const char *);
5696795Stjrstatic void odformat(const char *);
5796795Stjrstatic const char *odformatfp(char, const char *);
5896795Stjrstatic const char *odformatint(char, const char *);
5992920Simpstatic void odoffset(int, char ***);
6096795Stjrstatic void odusage(void);
611590Srgrimes
621590Srgrimesvoid
63102944Sdwmaloneoldsyntax(int argc, char ***argvp)
641590Srgrimes{
6596795Stjr	static char empty[] = "", padding[] = PADDING;
661590Srgrimes	int ch;
6796795Stjr	char **argv, *end;
681590Srgrimes
6996795Stjr	/* Add initial (default) address format. -A may change it later. */
7096795Stjr#define	TYPE_OFFSET	7
7196795Stjr	add("\"%07.7_Ao\n\"");
7296795Stjr	add("\"%07.7_ao  \"");
7396795Stjr
7496787Stjr	odmode = 1;
751590Srgrimes	argv = *argvp;
7696795Stjr	while ((ch = getopt(argc, argv, "A:aBbcDdeFfHhIij:LlN:Oost:vXx")) != -1)
771590Srgrimes		switch (ch) {
7896795Stjr		case 'A':
7996795Stjr			switch (*optarg) {
8096795Stjr			case 'd': case 'o': case 'x':
8196795Stjr				fshead->nextfu->fmt[TYPE_OFFSET] = *optarg;
8296795Stjr				fshead->nextfs->nextfu->fmt[TYPE_OFFSET] =
8396795Stjr				    *optarg;
8496795Stjr				break;
8596795Stjr			case 'n':
8696795Stjr				fshead->nextfu->fmt = empty;
8796795Stjr				fshead->nextfs->nextfu->fmt = padding;
8896795Stjr				break;
8996795Stjr			default:
9096795Stjr				errx(1, "%s: invalid address base", optarg);
9196795Stjr			}
9296795Stjr			break;
931590Srgrimes		case 'a':
9496795Stjr			odformat("a");
951590Srgrimes			break;
961590Srgrimes		case 'B':
971590Srgrimes		case 'o':
9896795Stjr			odformat("o2");
991590Srgrimes			break;
1001590Srgrimes		case 'b':
10196795Stjr			odformat("o1");
1021590Srgrimes			break;
1031590Srgrimes		case 'c':
10496795Stjr			odformat("c");
1051590Srgrimes			break;
1061590Srgrimes		case 'd':
10796795Stjr			odformat("u2");
1081590Srgrimes			break;
1091590Srgrimes		case 'D':
11096795Stjr			odformat("u4");
1111590Srgrimes			break;
1121590Srgrimes		case 'e':		/* undocumented in od */
1131590Srgrimes		case 'F':
11496795Stjr			odformat("fD");
1151590Srgrimes			break;
1161590Srgrimes		case 'f':
11796795Stjr			odformat("fF");
1181590Srgrimes			break;
1191590Srgrimes		case 'H':
1201590Srgrimes		case 'X':
12196795Stjr			odformat("x4");
1221590Srgrimes			break;
1231590Srgrimes		case 'h':
1241590Srgrimes		case 'x':
12596795Stjr			odformat("x2");
1261590Srgrimes			break;
1271590Srgrimes		case 'I':
1281590Srgrimes		case 'L':
1291590Srgrimes		case 'l':
13096795Stjr			odformat("dL");
1311590Srgrimes			break;
1321590Srgrimes		case 'i':
13396795Stjr			odformat("dI");
1341590Srgrimes			break;
13596795Stjr		case 'j':
13696795Stjr			errno = 0;
13796795Stjr			skip = strtoll(optarg, &end, 0);
13896795Stjr			if (*end == 'b')
13996795Stjr				skip *= 512;
14096795Stjr			else if (*end == 'k')
14196795Stjr				skip *= 1024;
14296795Stjr			else if (*end == 'm')
14396795Stjr				skip *= 1048576L;
14496795Stjr			if (errno != 0 || skip < 0 || strlen(end) > 1)
14596795Stjr				errx(1, "%s: invalid skip amount", optarg);
14696795Stjr			break;
14796795Stjr		case 'N':
14896795Stjr			if ((length = atoi(optarg)) <= 0)
14996795Stjr				errx(1, "%s: invalid length", optarg);
15096795Stjr			break;
1511590Srgrimes		case 'O':
15296795Stjr			odformat("o4");
1531590Srgrimes			break;
15496795Stjr		case 's':
15596795Stjr			odformat("d2");
15696795Stjr			break;
15796795Stjr		case 't':
15896795Stjr			odformat(optarg);
15996795Stjr			break;
1601590Srgrimes		case 'v':
1611590Srgrimes			vflag = ALL;
1621590Srgrimes			break;
1631590Srgrimes		case '?':
1641590Srgrimes		default:
16596795Stjr			odusage();
1661590Srgrimes		}
1671590Srgrimes
16896795Stjr	if (fshead->nextfs->nextfs == NULL)
16996795Stjr		odformat("oS");
1701590Srgrimes
1711590Srgrimes	argc -= optind;
1721590Srgrimes	*argvp += optind;
1731590Srgrimes
1741590Srgrimes	if (argc)
1751590Srgrimes		odoffset(argc, argvp);
1761590Srgrimes}
1771590Srgrimes
1781590Srgrimesstatic void
17996795Stjrodusage(void)
18096795Stjr{
18196795Stjr
18296795Stjr	fprintf(stderr,
18396795Stjr"usage: od [-aBbcDdeFfHhIiLlOosvXx] [-A base] [-j skip] [-N length] [-t type]\n");
18496795Stjr	fprintf(stderr,
18596795Stjr"          [[+]offset[.][Bb]] [file ...]\n");
18696795Stjr	exit(1);
18796795Stjr}
18896795Stjr
18996795Stjrstatic void
190102944Sdwmaloneodoffset(int argc, char ***argvp)
1911590Srgrimes{
192132541Sjohan	char *p, *num, *end;
1931590Srgrimes	int base;
1941590Srgrimes
1951590Srgrimes	/*
1961590Srgrimes	 * The offset syntax of od(1) was genuinely bizarre.  First, if
1971590Srgrimes	 * it started with a plus it had to be an offset.  Otherwise, if
1981590Srgrimes	 * there were at least two arguments, a number or lower-case 'x'
1991590Srgrimes	 * followed by a number makes it an offset.  By default it was
2001590Srgrimes	 * octal; if it started with 'x' or '0x' it was hex.  If it ended
2011590Srgrimes	 * in a '.', it was decimal.  If a 'b' or 'B' was appended, it
2021590Srgrimes	 * multiplied the number by 512 or 1024 byte units.  There was
2031590Srgrimes	 * no way to assign a block count to a hex offset.
2041590Srgrimes	 *
2051590Srgrimes	 * We assume it's a file if the offset is bad.
2061590Srgrimes	 */
2071590Srgrimes	p = argc == 1 ? (*argvp)[0] : (*argvp)[1];
2081590Srgrimes
2091590Srgrimes	if (*p != '+' && (argc < 2 ||
2101590Srgrimes	    (!isdigit(p[0]) && (p[0] != 'x' || !isxdigit(p[1])))))
2111590Srgrimes		return;
2121590Srgrimes
2131590Srgrimes	base = 0;
2141590Srgrimes	/*
2151590Srgrimes	 * skip over leading '+', 'x[0-9a-fA-f]' or '0x', and
2161590Srgrimes	 * set base.
2171590Srgrimes	 */
2181590Srgrimes	if (p[0] == '+')
2191590Srgrimes		++p;
2201590Srgrimes	if (p[0] == 'x' && isxdigit(p[1])) {
2211590Srgrimes		++p;
2221590Srgrimes		base = 16;
2231590Srgrimes	} else if (p[0] == '0' && p[1] == 'x') {
2241590Srgrimes		p += 2;
2251590Srgrimes		base = 16;
2261590Srgrimes	}
2271590Srgrimes
2281590Srgrimes	/* skip over the number */
2291590Srgrimes	if (base == 16)
2301590Srgrimes		for (num = p; isxdigit(*p); ++p);
2311590Srgrimes	else
2321590Srgrimes		for (num = p; isdigit(*p); ++p);
2331590Srgrimes
2341590Srgrimes	/* check for no number */
2351590Srgrimes	if (num == p)
2361590Srgrimes		return;
2371590Srgrimes
2381590Srgrimes	/* if terminates with a '.', base is decimal */
2391590Srgrimes	if (*p == '.') {
2401590Srgrimes		if (base)
2411590Srgrimes			return;
2421590Srgrimes		base = 10;
2431590Srgrimes	}
2441590Srgrimes
245132541Sjohan	skip = strtoll(num, &end, base ? base : 8);
2461590Srgrimes
2471590Srgrimes	/* if end isn't the same as p, we got a non-octal digit */
2481590Srgrimes	if (end != p) {
2491590Srgrimes		skip = 0;
2501590Srgrimes		return;
2511590Srgrimes	}
2521590Srgrimes
25348566Sbillf	if (*p) {
2541590Srgrimes		if (*p == 'B') {
2551590Srgrimes			skip *= 1024;
2561590Srgrimes			++p;
2571590Srgrimes		} else if (*p == 'b') {
2581590Srgrimes			skip *= 512;
2591590Srgrimes			++p;
2601590Srgrimes		}
26148566Sbillf	}
2621590Srgrimes
2631590Srgrimes	if (*p) {
2641590Srgrimes		skip = 0;
2651590Srgrimes		return;
2661590Srgrimes	}
2671590Srgrimes
2681590Srgrimes	/*
2691590Srgrimes	 * If the offset uses a non-octal base, the base of the offset
2701590Srgrimes	 * is changed as well.  This isn't pretty, but it's easy.
2711590Srgrimes	 */
2721590Srgrimes	if (base == 16) {
2731590Srgrimes		fshead->nextfu->fmt[TYPE_OFFSET] = 'x';
2741590Srgrimes		fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'x';
2751590Srgrimes	} else if (base == 10) {
2761590Srgrimes		fshead->nextfu->fmt[TYPE_OFFSET] = 'd';
2771590Srgrimes		fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'd';
2781590Srgrimes	}
2791590Srgrimes
2801590Srgrimes	/* Terminate file list. */
2811590Srgrimes	(*argvp)[1] = NULL;
2821590Srgrimes}
2831590Srgrimes
2841590Srgrimesstatic void
28596795Stjrodformat(const char *fmt)
2861590Srgrimes{
28796795Stjr	char fchar;
2881590Srgrimes
28996795Stjr	while (*fmt != '\0') {
29096795Stjr		switch ((fchar = *fmt++)) {
29196795Stjr		case 'a':
29296795Stjr			odadd("16/1 \"%3_u \" \"\\n\"");
29396795Stjr			break;
29496795Stjr		case 'c':
29596795Stjr			odadd("16/1 \"%3_c \" \"\\n\"");
29696795Stjr			break;
29796795Stjr		case 'o': case 'u': case 'd': case 'x':
29896795Stjr			fmt = odformatint(fchar, fmt);
29996795Stjr			break;
30096795Stjr		case 'f':
30196795Stjr			fmt = odformatfp(fchar, fmt);
30296795Stjr			break;
30396795Stjr		default:
30496795Stjr			errx(1, "%c: unrecognised format character", fchar);
30596795Stjr		}
30696795Stjr	}
3071590Srgrimes}
30896795Stjr
30996795Stjrstatic const char *
31096795Stjrodformatfp(char fchar __unused, const char *fmt)
31196795Stjr{
31296795Stjr	size_t isize;
31396795Stjr	int digits;
31496795Stjr	char *end, *hdfmt;
31596795Stjr
31696795Stjr	isize = sizeof(double);
31796795Stjr	switch (*fmt) {
31896795Stjr	case 'F':
31996795Stjr		isize = sizeof(float);
32096795Stjr		fmt++;
32196795Stjr		break;
32296795Stjr	case 'D':
32396795Stjr		isize = sizeof(double);
32496795Stjr		fmt++;
32596795Stjr		break;
32696795Stjr	case 'L':
32796795Stjr		isize = sizeof(long double);
32896795Stjr		fmt++;
32996795Stjr		break;
33096795Stjr	default:
33196795Stjr		if (isdigit((unsigned char)*fmt)) {
33296795Stjr			errno = 0;
33396795Stjr			isize = (size_t)strtoul(fmt, &end, 10);
33496795Stjr			if (errno != 0 || isize == 0)
33596795Stjr				errx(1, "%s: invalid size", fmt);
33696795Stjr			fmt = (const char *)end;
33796795Stjr		}
33896795Stjr	}
33996795Stjr	switch (isize) {
34096795Stjr	case sizeof(float):
34196795Stjr		digits = FLT_DIG;
34296795Stjr		break;
34396795Stjr	case sizeof(double):
34496795Stjr		digits = DBL_DIG;
34596795Stjr		break;
34696795Stjr	default:
34796795Stjr		if (isize == sizeof(long double))
34896795Stjr			digits = LDBL_DIG;
34996795Stjr		else
35096795Stjr			errx(1, "unsupported floating point size %lu",
35196795Stjr			    (u_long)isize);
35296795Stjr	}
35396795Stjr
35496795Stjr	asprintf(&hdfmt, "%lu/%lu \" %%%d.%de \" \"\\n\"",
35596795Stjr	    16UL / (u_long)isize, (u_long)isize, digits + 8, digits);
35696795Stjr	if (hdfmt == NULL)
35796795Stjr		err(1, NULL);
35896795Stjr	odadd(hdfmt);
35996795Stjr	free(hdfmt);
36096795Stjr
36196795Stjr	return (fmt);
36296795Stjr}
36396795Stjr
36496795Stjrstatic const char *
36596795Stjrodformatint(char fchar, const char *fmt)
36696795Stjr{
36796795Stjr	unsigned long long n;
36896795Stjr	size_t isize;
36996795Stjr	int digits;
37096795Stjr	char *end, *hdfmt;
37196795Stjr
37296795Stjr	isize = sizeof(int);
37396795Stjr	switch (*fmt) {
37496795Stjr	case 'C':
37596795Stjr		isize = sizeof(char);
37696795Stjr		fmt++;
37796795Stjr		break;
37896795Stjr	case 'I':
37996795Stjr		isize = sizeof(int);
38096795Stjr		fmt++;
38196795Stjr		break;
38296795Stjr	case 'L':
38396795Stjr		isize = sizeof(long);
38496795Stjr		fmt++;
38596795Stjr		break;
38696795Stjr	case 'S':
38796795Stjr		isize = sizeof(short);
38896795Stjr		fmt++;
38996795Stjr		break;
39096795Stjr	default:
39196795Stjr		if (isdigit((unsigned char)*fmt)) {
39296795Stjr			errno = 0;
39396795Stjr			isize = (size_t)strtoul(fmt, &end, 10);
39496795Stjr			if (errno != 0 || isize == 0)
39596795Stjr				errx(1, "%s: invalid size", fmt);
39696795Stjr			if (isize != sizeof(char) && isize != sizeof(short) &&
39796795Stjr			    isize != sizeof(int) && isize != sizeof(long))
39896795Stjr				errx(1, "unsupported int size %lu",
39996795Stjr				    (u_long)isize);
40096795Stjr			fmt = (const char *)end;
40196795Stjr		}
40296795Stjr	}
40396795Stjr
40496795Stjr	/*
40596795Stjr	 * Calculate the maximum number of digits we need to
40696795Stjr	 * fit the number. Overestimate for decimal with log
40796795Stjr	 * base 8. We need one extra space for signed numbers
40896795Stjr	 * to store the sign.
40996795Stjr	 */
41096795Stjr	n = (1ULL << (8 * isize)) - 1;
41196795Stjr	digits = 0;
41296795Stjr	while (n != 0) {
41396795Stjr		digits++;
41496795Stjr		n >>= (fchar == 'x') ? 4 : 3;
41596795Stjr	}
41696795Stjr	if (fchar == 'd')
41796795Stjr		digits++;
41896798Stjr	asprintf(&hdfmt, "%lu/%lu \"%*s%%%s%d%c\" \"\\n\"",
41996798Stjr	    16UL / (u_long)isize, (u_long)isize, (int)(4 * isize - digits),
42096798Stjr	    "", (fchar == 'd' || fchar == 'u') ? "" : "0", digits, fchar);
42196795Stjr	if (hdfmt == NULL)
42296795Stjr		err(1, NULL);
42396795Stjr	odadd(hdfmt);
42496795Stjr	free(hdfmt);
42596795Stjr
42696795Stjr	return (fmt);
42796795Stjr}
42896795Stjr
42996795Stjrstatic void
43096795Stjrodadd(const char *fmt)
43196795Stjr{
43296795Stjr	static int needpad;
43396795Stjr
43496795Stjr	if (needpad)
43596795Stjr		add("\""PADDING"\"");
43696795Stjr	add(fmt);
43796795Stjr	needpad = 1;
43896795Stjr}
439