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
This commit is contained in:
tjr 2002-05-17 07:14:55 +00:00
parent fded097c79
commit 0b8ca07233
5 changed files with 424 additions and 86 deletions

View File

@ -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:

View File

@ -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 &&

View File

@ -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
.Fl w
options, nor is compatibility provided for the ``label'' component
of the offset syntax.
.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
.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 .

View File

@ -43,107 +43,136 @@ static const char rcsid[] =
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <float.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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;
@ -152,6 +181,17 @@ oldsyntax(argc, argvp)
odoffset(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;
@ -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)
{
static int first = 1;
char fchar;
if (first) {
first = 0;
add("\"%07.7_Ao\n\"");
add("\"%07.7_ao \"");
} else
add("\" \"");
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 needpad;
if (needpad)
add("\""PADDING"\"");
add(fmt);
needpad = 1;
}

View File

@ -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':