From 98e1f63634ab9b117258a176e0727c23126bb762 Mon Sep 17 00:00:00 2001 From: tjr Date: Fri, 17 May 2002 07:14:55 +0000 Subject: Overhaul hexdump's od syntax code to handle the -s -A -j -N -t options that SUSv3 requires and give od a proper manual page. PR: 36783 --- usr.bin/hexdump/conv.c | 4 - usr.bin/hexdump/display.c | 10 +- usr.bin/hexdump/od.1 | 195 +++++++++++++++++++++++++++---- usr.bin/hexdump/odsyntax.c | 286 ++++++++++++++++++++++++++++++++++++--------- usr.bin/hexdump/parse.c | 17 +-- 5 files changed, 425 insertions(+), 87 deletions(-) (limited to 'usr.bin') diff --git a/usr.bin/hexdump/conv.c b/usr.bin/hexdump/conv.c index 9cf3e87..eea03be 100644 --- a/usr.bin/hexdump/conv.c +++ b/usr.bin/hexdump/conv.c @@ -57,8 +57,6 @@ conv_c(pr, p) goto strpr; /* case '\a': */ case '\007': - if (odmode) /* od didn't know about \a */ - break; str = "\\a"; goto strpr; case '\b': @@ -77,8 +75,6 @@ conv_c(pr, p) str = "\\t"; goto strpr; case '\v': - if (odmode) - break; str = "\\v"; goto strpr; default: diff --git a/usr.bin/hexdump/display.c b/usr.bin/hexdump/display.c index ec957b7..d2e32fc 100644 --- a/usr.bin/hexdump/display.c +++ b/usr.bin/hexdump/display.c @@ -150,9 +150,11 @@ print(pr, bp) bcopy(bp, &f8, sizeof(f8)); (void)printf(pr->fmt, f8); break; - case sizeof(long double): - bcopy(bp, &ldbl, sizeof(ldbl)); - (void)printf(pr->fmt, ldbl); + default: + if (pr->bcnt == sizeof(long double)) { + bcopy(bp, &ldbl, sizeof(ldbl)); + (void)printf(pr->fmt, ldbl); + } break; } break; @@ -259,6 +261,8 @@ get() * block and set the end flag. */ if (!length || (ateof && !next((char **)NULL))) { + if (odmode && address < skip) + errx(1, "cannot skip past end of input"); if (need == blocksize) return((u_char *)NULL); if (vflag != ALL && diff --git a/usr.bin/hexdump/od.1 b/usr.bin/hexdump/od.1 index ac46aab..f135469 100644 --- a/usr.bin/hexdump/od.1 +++ b/usr.bin/hexdump/od.1 @@ -32,7 +32,7 @@ .\" @(#)od.1 8.1 (Berkeley) 6/6/93 .\" $FreeBSD$ .\" -.Dd May 27, 1994 +.Dd April 17, 2002 .Os .Dt OD 1 .Sh NAME @@ -40,7 +40,11 @@ .Nd octal, decimal, hex, ASCII dump .Sh SYNOPSIS .Nm -.Op Fl aBbcDdeFfHhIiLlOovXx +.Op Fl aBbcDdeFfHhIiLlOosvXx +.Op Fl A Ar base +.Op Fl j Ar skip +.Op Fl N Ar length +.Op Fl t Ar type .Sm off .Oo .Op Cm \&+ @@ -49,32 +53,183 @@ .Op Cm Bb .Oc .Sm on -.Ar file +.Op Ar .Sh DESCRIPTION The -.Nm hexdump -utility, if called as -.Nm , -provides compatibility for the options listed above. +.Nm +utility is a filter which displays the specified files, or standard +input if no files are specified, in a user specified format. .Pp -It does not provide compatibility for the -.Fl s -option (see -.Xr strings 1 ) -or the -.Fl P , -.Fl p , +The options are as follows: +.Bl -tag -width Fl +.It Fl A Ar base +Specify the input address base. +.Ar base +may be one of +.Ql d , +.Ql o , +.Ql x +or +.Ql n , +which specify decimal, octal, hexadecimal +addresses or no address, respectively. +.It Fl a +Output named characters. +Equivalent to +.Fl t Ar a . +.It Fl B , Fl o +Output octal shorts. +Equivalent to +.Fl t Ar o2 . +.It Fl b +Output octal bytes. +Equivalent to +.Fl t Ar o1 . +.It Fl c +Output C-style escaped characters. +Equivalent to +.Fl t Ar c . +.It Fl D +Output unsigned decimal ints. +Equivalent to +.Fl t Ar u4 . +.It Fl e , Fl F +Output double-precision floating point numbers. +Equivalent to +.Fl t Ar fD . +.It Fl f +Output single-precision floating point numbers. +Equivalent to +.Fl t Ar fF . +.It Fl H , Fl X +Output hexadecimal ints. +Equivalent to +.Fl t Ar x4 . +.It Fl h , Fl x +Output hexadecimal shorts. +Equivalent to +.Fl t Ar x2 . +.It Fl I , Fl L , Fl l +Output signed decimal longs. +Equivalent to +.Fl t Ar dL . +.It Fl i +Output signed decimal ints. +Equivalent to +.Fl t Ar dI . +.It Fl j Ar skip +Skip +.Ar skip +bytes of the combined input before dumping. The number may be followed by one +of +.Ql b , +.Ql k or -.Fl w -options, nor is compatibility provided for the ``label'' component -of the offset syntax. +.Ql m +which specify the units of the number as blocks (512 bytes), kilobytes and +megabytes, respectively. +.It Fl N Ar length +Dump at most +.Ar length +bytes of input. +.It Fl O +Output octal ints. +Equivalent to +.Fl t Ar o4 . +.It Fl s +Output signed decimal shorts. +Equivalent to +.Fl t Ar d2 . +.It Fl t Ar type +Specify the output format. +.Ar type +is a string containing one or more of the following kinds of type specifiers: +.Bl -tag -width indent +.It Cm a +Named characters +.Pq Sq ASCII . +Control characters are displayed using the following names: +.Bl -column \&000_nu \&001_so \&002_st \&003_et \&004_eo +.It "\&000\ nul\t001\ soh\t002\ stx\t003\ etx\t004\ eot\t005\ enq +.It "\&006\ ack\t007\ bel\t008\ bs\t009\ ht\t00A\ nl\t00B\ vt +.It "\&00C\ ff\t00D\ cr\t00E\ so\t00F\ si\t010\ dle\t011\ dc1 +.It "\&012\ dc2\t013\ dc3\t014\ dc4\t015\ nak\t016\ syn\t017\ etb +.It "\&018\ can\t019\ em\t01A\ sub\t01B\ esc\t01C\ fs\t01D\ gs +.It "\&01E\ rs\t01F\ us\t020\ sp\t0FF\ del +.El +.It Cm c +Characters in the default character set. Non-printing characters are +represented as 3-digit octal character codes, except the following +characters, which are represented as C escapes: +.Bl -column carriage-return \er +.It NUL Ta \e0 +.It alert Ta \ea +.It backspace Ta \eb +.It newline Ta \en +.It carriage-return Ta \er +.It tab Ta \et +.It vertical tab Ta \ev +.El +.It Cm [d|o|u|x][C|S|I|L| Ns Ar n Ns ] +Signed decimal +.Pq Ql d , +octal +.Pq Ql o , +unsigned decimal +.Pq Ql u +or +hexadecimal +.Pq Ql x . +Followed by an optional size specifier, which may be either +.Ql C +.Pq "char" , +.Ql S +.Pq "short" , +.Ql I +.Pq "int" , +.Ql L +.Pq "long" , +or a byte count as a decimal integer. +.It Cm f[F|D|L| Ns Ar n Ns ] +Floating-point number. +Followed by an optional size specifier, which may be either +.Ql F +.Pq "float" , +.Ql D +.Pq "double" +or +.Ql L +.Pq "long double" . +.El +.It Fl v +Write all input data, instead of replacing lines of duplicate values with a +.Ql * . +.El +.Pp +Multiple options that specify output format may be used; the output will +contain one line for each format. +.Pp +If no output format is specified, +.Fl t Ar oS +is assumed. +.Sh DIAGNOSTICS +.Ex -std +.Sh COMPATIBILITY +The traditional +.Fl s +option to extract string constants is not supported; consider using +.Xr strings 1 +instead. .Sh SEE ALSO .Xr hexdump 1 , .Xr strings 1 -.Sh BUGS -Quite a few. +.Sh STANDARDS +The +.Nm +utility conforms to +.St -p1003.1-2001 . .Sh HISTORY -A +An .Nm command appeared in .At v1 . diff --git a/usr.bin/hexdump/odsyntax.c b/usr.bin/hexdump/odsyntax.c index 214b949..082a0a0 100644 --- a/usr.bin/hexdump/odsyntax.c +++ b/usr.bin/hexdump/odsyntax.c @@ -43,107 +43,136 @@ static const char rcsid[] = #include #include +#include +#include #include #include +#include #include #include "hexdump.h" +#define PADDING " " + int odmode; +static void odadd(const char *); +static void odformat(const char *); +static const char *odformatfp(char, const char *); +static const char *odformatint(char, const char *); static void odoffset(int, char ***); -static void odprecede(void); +static void odusage(void); void oldsyntax(argc, argvp) int argc; char ***argvp; { + static char empty[] = "", padding[] = PADDING; int ch; - char **argv; + char **argv, *end; + + /* Add initial (default) address format. -A may change it later. */ +#define TYPE_OFFSET 7 + add("\"%07.7_Ao\n\""); + add("\"%07.7_ao \""); odmode = 1; argv = *argvp; - while ((ch = getopt(argc, argv, "aBbcDdeFfHhIiLlOoPpswvXx")) != -1) + while ((ch = getopt(argc, argv, "A:aBbcDdeFfHhIij:LlN:Oost:vXx")) != -1) switch (ch) { + case 'A': + switch (*optarg) { + case 'd': case 'o': case 'x': + fshead->nextfu->fmt[TYPE_OFFSET] = *optarg; + fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = + *optarg; + break; + case 'n': + fshead->nextfu->fmt = empty; + fshead->nextfs->nextfu->fmt = padding; + break; + default: + errx(1, "%s: invalid address base", optarg); + } + break; case 'a': - odprecede(); - add("16/1 \"%3_u \" \"\\n\""); + odformat("a"); break; case 'B': case 'o': - odprecede(); - add("8/2 \" %06o \" \"\\n\""); + odformat("o2"); break; case 'b': - odprecede(); - add("16/1 \"%03o \" \"\\n\""); + odformat("o1"); break; case 'c': - odprecede(); - add("16/1 \"%3_c \" \"\\n\""); + odformat("c"); break; case 'd': - odprecede(); - add("8/2 \" %05u \" \"\\n\""); + odformat("u2"); break; case 'D': - odprecede(); - add("4/4 \" %010u \" \"\\n\""); + odformat("u4"); break; case 'e': /* undocumented in od */ case 'F': - odprecede(); - add("2/8 \" %21.14e \" \"\\n\""); + odformat("fD"); break; - case 'f': - odprecede(); - add("4/4 \" %14.7e \" \"\\n\""); + odformat("fF"); break; case 'H': case 'X': - odprecede(); - add("4/4 \" %08x \" \"\\n\""); + odformat("x4"); break; case 'h': case 'x': - odprecede(); - add("8/2 \" %04x \" \"\\n\""); + odformat("x2"); break; case 'I': case 'L': case 'l': - odprecede(); - add("4/4 \" %11d \" \"\\n\""); + odformat("dL"); break; case 'i': - odprecede(); - add("8/2 \" %6d \" \"\\n\""); + odformat("dI"); + break; + case 'j': + errno = 0; + skip = strtoll(optarg, &end, 0); + if (*end == 'b') + skip *= 512; + else if (*end == 'k') + skip *= 1024; + else if (*end == 'm') + skip *= 1048576L; + if (errno != 0 || skip < 0 || strlen(end) > 1) + errx(1, "%s: invalid skip amount", optarg); + break; + case 'N': + if ((length = atoi(optarg)) <= 0) + errx(1, "%s: invalid length", optarg); break; case 'O': - odprecede(); - add("4/4 \" %011o \" \"\\n\""); + odformat("o4"); + break; + case 's': + odformat("d2"); + break; + case 't': + odformat(optarg); break; case 'v': vflag = ALL; break; - case 'P': - case 'p': - case 's': - case 'w': case '?': default: - if (ch != '?') - warnx("hexdump(1) compatibility doesn't support the -%c option%s", - ch, ch == 's' ? "; see strings(1)" : ""); - usage(); + odusage(); } - if (!fshead) { - add("\"%07.7_Ao\n\""); - add("\"%07.7_ao \" 8/2 \"%06o \" \"\\n\""); - } + if (fshead->nextfs->nextfs == NULL) + odformat("oS"); argc -= optind; *argvp += optind; @@ -153,6 +182,17 @@ oldsyntax(argc, argvp) } static void +odusage(void) +{ + + fprintf(stderr, +"usage: od [-aBbcDdeFfHhIiLlOosvXx] [-A base] [-j skip] [-N length] [-t type]\n"); + fprintf(stderr, +" [[+]offset[.][Bb]] [file ...]\n"); + exit(1); +} + +static void odoffset(argc, argvp) int argc; char ***argvp; @@ -237,7 +277,6 @@ odoffset(argc, argvp) * If the offset uses a non-octal base, the base of the offset * is changed as well. This isn't pretty, but it's easy. */ -#define TYPE_OFFSET 7 if (base == 16) { fshead->nextfu->fmt[TYPE_OFFSET] = 'x'; fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'x'; @@ -251,14 +290,157 @@ odoffset(argc, argvp) } static void -odprecede() +odformat(const char *fmt) +{ + char fchar; + + while (*fmt != '\0') { + switch ((fchar = *fmt++)) { + case 'a': + odadd("16/1 \"%3_u \" \"\\n\""); + break; + case 'c': + odadd("16/1 \"%3_c \" \"\\n\""); + break; + case 'o': case 'u': case 'd': case 'x': + fmt = odformatint(fchar, fmt); + break; + case 'f': + fmt = odformatfp(fchar, fmt); + break; + default: + errx(1, "%c: unrecognised format character", fchar); + } + } +} + +static const char * +odformatfp(char fchar __unused, const char *fmt) +{ + size_t isize; + int digits; + char *end, *hdfmt; + + isize = sizeof(double); + switch (*fmt) { + case 'F': + isize = sizeof(float); + fmt++; + break; + case 'D': + isize = sizeof(double); + fmt++; + break; + case 'L': + isize = sizeof(long double); + fmt++; + break; + default: + if (isdigit((unsigned char)*fmt)) { + errno = 0; + isize = (size_t)strtoul(fmt, &end, 10); + if (errno != 0 || isize == 0) + errx(1, "%s: invalid size", fmt); + fmt = (const char *)end; + } + } + switch (isize) { + case sizeof(float): + digits = FLT_DIG; + break; + case sizeof(double): + digits = DBL_DIG; + break; + default: + if (isize == sizeof(long double)) + digits = LDBL_DIG; + else + errx(1, "unsupported floating point size %lu", + (u_long)isize); + } + + asprintf(&hdfmt, "%lu/%lu \" %%%d.%de \" \"\\n\"", + 16UL / (u_long)isize, (u_long)isize, digits + 8, digits); + if (hdfmt == NULL) + err(1, NULL); + odadd(hdfmt); + free(hdfmt); + + return (fmt); +} + +static const char * +odformatint(char fchar, const char *fmt) +{ + unsigned long long n; + size_t isize; + int digits; + char *end, *hdfmt; + + isize = sizeof(int); + switch (*fmt) { + case 'C': + isize = sizeof(char); + fmt++; + break; + case 'I': + isize = sizeof(int); + fmt++; + break; + case 'L': + isize = sizeof(long); + fmt++; + break; + case 'S': + isize = sizeof(short); + fmt++; + break; + default: + if (isdigit((unsigned char)*fmt)) { + errno = 0; + isize = (size_t)strtoul(fmt, &end, 10); + if (errno != 0 || isize == 0) + errx(1, "%s: invalid size", fmt); + if (isize != sizeof(char) && isize != sizeof(short) && + isize != sizeof(int) && isize != sizeof(long)) + errx(1, "unsupported int size %lu", + (u_long)isize); + fmt = (const char *)end; + } + } + + /* + * Calculate the maximum number of digits we need to + * fit the number. Overestimate for decimal with log + * base 8. We need one extra space for signed numbers + * to store the sign. + */ + n = (1ULL << (8 * isize)) - 1; + digits = 0; + while (n != 0) { + digits++; + n >>= (fchar == 'x') ? 4 : 3; + } + if (fchar == 'd') + digits++; + asprintf(&hdfmt, "%lu/%lu \"%%%s%d%c \" \"\\n\"", + 16UL / (u_long)isize, (u_long)isize, + (fchar == 'd' || fchar == 'u') ? "" : "0", digits, fchar); + if (hdfmt == NULL) + err(1, NULL); + odadd(hdfmt); + free(hdfmt); + + return (fmt); +} + +static void +odadd(const char *fmt) { - static int first = 1; - - if (first) { - first = 0; - add("\"%07.7_Ao\n\""); - add("\"%07.7_ao \""); - } else - add("\" \""); + static int needpad; + + if (needpad) + add("\""PADDING"\""); + add(fmt); + needpad = 1; } diff --git a/usr.bin/hexdump/parse.c b/usr.bin/hexdump/parse.c index da93e7b..85e035e 100644 --- a/usr.bin/hexdump/parse.c +++ b/usr.bin/hexdump/parse.c @@ -313,15 +313,16 @@ isint: cs[2] = '\0'; case 4: pr->bcnt = 4; break; - case sizeof(long double): - cs[2] = '\0'; - cs[1] = cs[0]; - cs[0] = 'L'; - pr->bcnt = sizeof(long double); - break; default: - p1[1] = '\0'; - badcnt(p1); + if (fu->bcnt == sizeof(long double)) { + cs[2] = '\0'; + cs[1] = cs[0]; + cs[0] = 'L'; + pr->bcnt = sizeof(long double); + } else { + p1[1] = '\0'; + badcnt(p1); + } } break; case 's': -- cgit v1.1