od.c revision 01335b0d1c4e0c0f16325a830b24ea2a4076fd38
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet
*/
/*
* Copyright 2010 Nexenta Systems, Inc. All rights reserved.
*/
/*
* od - octal dump. Not really just octal anymore; read the POSIX
* specification for it -- its more complex than you think!
*
* NB: We followed the POSIX semantics fairly strictly, where the
* legacy code's behavior was in conflict. In many cases the legacy
* Solaris code was so completely broken as to be completely unusable.
* (For example, the long double support was broken beyond
* imagination!) Note that GNU coreutils violates POSIX in a few
* interesting ways, such as changing the numbering of the addresses
* when skipping. (Address starts should always be at 0, according to
* the sample output in the Open Group man page.)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <wchar.h>
#include <locale.h>
#include <unistd.h>
#define _(x) gettext(x)
/* address format */
static char *afmt = "%07llo";
static char *cfmt = " ";
static int numfiles = 0;
static int curfile = 0;
/*
* This structure describes our ring buffer. Its always a power of 2
* in size to make wrap around calculations fast using a mask instead
* of doing modulo.
*
* The size is calculated thusly: We need three "blocks" of data, as
* we process a block at a time (one block == one line of od output.)
*
* We need lookahead of an extra block to support multibyte chars. We
* also have a look behind so that we can avoid printing lines that
* are identical to what we've already printed. Finally, we need the
* current block.
*
* The block size is determined by the least common multiple of the
* data items being displayed. Usually it will be 16, but sometimes
* it is 24 (when 12-byte long doubles are presented.)
*
* The data buffer is allocaed via memalign to make sure it is
* properly aligned.
*/
typedef struct buffer {
char *data; /* data buffer */
int prod; /* producer index */
int cons; /* consumer index */
int mask; /* buffer size - 1, wraparound index */
int navail; /* total bytes avail */
} buffer_t;
/*
* This structure is used to provide information on a specific output
* format. We link them together in a list representing the output
* formats that the user has selected.
*/
typedef struct output {
int width; /* bytes consumed per call */
} output_t;
/*
* Specifiers
*/
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef char s8;
typedef short s16;
typedef int s32;
typedef long long s64;
typedef float fF;
typedef double fD;
typedef long double fL;
static void
usage(void)
{
"[-t types ]... [-A base] [-j skip] [-N count] [file]...\n"));
exit(1);
}
static typ \
{ \
return (val); \
}
static void \
{ \
} \
\
};
static char *ascii[] = {
"nul", "soh", "stx", "etx", "eot", "enq", "ack", " be",
" bs", " ht", " lf", " vt", " ff", " cr", " so", " si",
"dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
"can", " em", "sub", "esc", " fs", " gs", " rs", " us",
" sp", " !", " \"", " #", " $", " %", " &", " '",
" (", " )", " *", " +", " ,", " -", " .", " /",
" 0", " 1", " 2", " 3", " 4", " 5", " 6", " 7",
" 8", " 9", " :", " ;", " <", " =", " >", " ?",
" @", " A", " B", " C", " D", " E", " F", " G",
" H", " I", " J", " K", " L", " M", " N", " O",
" P", " Q", " R", " S", " T", " U", " V", " W",
" X", " Y", " Z", " [", " \\", " ]", " ^", " _",
" `", " a", " b", " c", " d", " e", " f", " g",
" h", " i", " j", " k", " l", " m", " n", " o",
" p", " q", " r", " s", " t", " u", " v", " w",
" x", " y", " z", " {", " |", " }", " ~", "del"
};
static void
{
}
static output_t output_ascii = {
1, do_ascii,
};
static void
{
static int nresid = 0;
static int printable = 0;
int cnt;
int avail;
int nb;
char scratch[10];
int which;
/*
* If there were residual bytes from an earlier
* character, then just display the ** continuation
* indication.
*/
if (nresid) {
if (printable) {
} else {
(void) printf(" %03o", v);
}
nresid--;
return;
}
/*
* Peek ahead up to MB_CUR_MAX characters. This has to be
* done carefully because we might need to look into the next
* block to really know for sure.
*/
scratch[0] = v;
if (avail > MB_CUR_MAX)
avail = MB_CUR_MAX;
}
/* now see if the value is a real character */
nresid = 0;
wc = 0;
if (nb < 0) {
(void) printf(" %03o", v);
return;
}
if (nb == 0) {
return;
}
printable = 1;
return;
}
printable = 0;
if (wc == 0) {
} else if (wc == '\b') {
} else if (wc == '\f') {
} else if (wc == '\n') {
} else if (wc == '\r') {
} else if (wc == '\t') {
} else {
(void) printf(" %03o", v);
}
}
static output_t output_char = {
1, do_char,
};
/*
* List of output formatting structures.
*/
static void
{
int m;
}
m = lcm;
m += lcm;
}
lcm = m;
while (blocksize < 16)
blocksize *= 2;
}
static FILE *
next_input(void)
{
for (;;) {
return (NULL);
NULL) {
curfile++;
return (input);
}
} else {
curfile++;
return (input);
}
}
curfile++;
}
}
static void
{
int n;
int want;
int zero;
/*
* If we have 2 blocks of bytes available, we're done. Note
* that each iteration usually loads up 16 bytes, unless we
* run out of data.
*/
/* we preload the next one in advance */
if (limit == 0) {
continue;
}
/* we want to read a whole block if possible */
}
int c;
want;
if (n < 0) {
warn("read: %s",
input = next_input();
continue;
}
if (n == 0) {
input = next_input();
continue;
}
if (limit >= 0)
limit -= n;
b->navail += n;
b->prod += n;
want -= n;
zero -= n;
}
while (zero) {
b->prod++;
zero--;
}
}
}
#define STR1 "C1"
#define STR2 "S2"
#ifdef _LP64
#define STR8 "L8"
#define STR4 "I4"
#else
#define STR8 "8"
#define STR4 "IL4"
#endif
static void
do_type_string(char *typestr)
{
if (*typestr == 0) {
}
while (*typestr) {
switch (*typestr) {
case 'a':
typestr++;
break;
case 'c':
typestr++;
break;
case 'f':
typestr++;
switch (*typestr) {
case 'F':
case '4':
typestr++;
break;
case '8':
case 'D':
typestr++;
break;
case 'L':
typestr++;
break;
default:
break;
}
break;
case 'd':
typestr++;
typestr++;
typestr++;
typestr++;
typestr++;
} else {
}
break;
case 'u':
typestr++;
typestr++;
typestr++;
typestr++;
typestr++;
} else {
}
break;
case 'o':
typestr++;
typestr++;
typestr++;
typestr++;
typestr++;
} else {
}
break;
case 'x':
typestr++;
typestr++;
typestr++;
typestr++;
typestr++;
} else {
}
break;
default:
*typestr);
exit(1);
}
}
}
int
{
int c;
int i;
char *eptr;
char *offstr = 0;
switch (c) {
case 'A':
}
switch (*optarg) {
case 'o':
afmt = "%07llo";
cfmt = " ";
break;
case 'd':
afmt = "%07lld";
cfmt = " ";
break;
case 'x':
afmt = "%07llx";
cfmt = " ";
break;
case 'n':
/*
* You could argue that the code should
* use the same 7 spaces. Legacy uses 8
* though. Oh well. Better to avoid
* gratuitous change.
*/
afmt = " ";
cfmt = " ";
break;
default:
break;
}
}
warnx(_("invalid address base, "
"must be o, d, x, or n"));
break;
case 'b':
break;
case 'c':
case 'C':
break;
case 'f':
break;
case 'F':
break;
case 'd':
break;
case 'D':
break;
case 't':
break;
case 'o':
break;
case 'O':
break;
case 's':
break;
case 'S':
break;
case 'x':
break;
case 'X':
break;
case 'v':
break;
case 'j':
if (*eptr == 'b') {
eptr++;
} else if (*eptr == 'k') {
eptr++;
} else if (*eptr == 'm') {
eptr++;
} else if (*eptr == 'g') {
eptr++;
}
warnx(_("invalid skip count '%s' specified"),
optarg);
exit(1);
}
break;
case 'N':
/*
* POSIX doesn't specify this, but I think these
* may be helpful.
*/
if (*eptr == 'b') {
limit <<= 9;
eptr++;
} else if (*eptr == 'k') {
limit <<= 10;
eptr++;
} else if (*eptr == 'm') {
limit <<= 20;
eptr++;
} else if (*eptr == 'g') {
limit <<= 30;
eptr++;
}
warnx(_("invalid byte count '%s' specified"),
optarg);
exit(1);
}
break;
default:
usage();
break;
}
}
/* this finds the smallest power of two size we can use */
}
/*
* Wow. This option parsing is hideous.
*
* If the we've not seen a new option, and there is just one
* operand, if it starts with a "+", then treat it as an
* offset. Otherwise if two operands, and the second operand
* starts with + or a digit, then it is an offset.
*/
if (!newarg) {
argc--;
argc--;
}
}
if (offstr) {
int base = 0;
int mult = 1;
int l;
if (*offstr == '+') {
offstr++;
}
afmt = "%07llx";
base = 16;
offstr += 2;
offstr[l - 1] = 0;
l--;
mult = 512;
}
} else {
base = 8;
afmt = "%07llo";
offstr[l - 1] = 0;
l--;
mult = 512;
}
offstr[l - 1] = 0;
base = 10;
afmt = "%07lld";
}
}
if (*eptr != '\0') {
}
}
/*
* Allocate an array for all the input files.
*/
numfiles++;
}
input = next_input();
} else {
}
/*
* We need to seek ahead. fseek would be faster.
*/
/*
* Only fseek() on regular files. (Others
* we have to read().
*/
input = next_input();
continue;
}
/*
* No point in seeking a file that is too
* short to begin with.
*/
input = next_input();
continue;
}
}
/* Done seeking. */
skip = 0;
break;
}
/*
* fgetc seems like it would be slow, but it uses
* buffered I/O, so it should be fast enough.
*/
while (skip) {
}
input = next_input();
}
break;
}
skip--;
}
}
}
int mx;
int j, k;
/*
* If this buffer was the same as last, then just
* dump an asterisk.
*/
k = j - blocksize;
for (i = 0; i < blocksize; i++) {
break;
}
j++;
k++;
}
if (i == blocksize) {
if (!same) {
}
continue;
}
}
/*LINTED E_SEC_PRINTF_VAR_FMT*/
} else {
}
}
}
}
/*LINTED E_SEC_PRINTF_VAR_FMT*/
return (0);
}