changeset 13221:879ee5195278

354 need an open od(1) Reviewed by: richlowe@richlowe.net Reviewed by: roland.mainz@nrubsig.org Reviewed by: gwr@nexenta.com Approved by: gwr@nexenta.com
author Garrett D'Amore <garrett@nexenta.com>
date Wed, 20 Oct 2010 23:13:10 -0700
parents fed843d791e9
children 02526851ba75
files exception_lists/closed-bins usr/src/Makefile.lint usr/src/cmd/Makefile usr/src/cmd/od/Makefile usr/src/cmd/od/od.c usr/src/pkg/manifests/system-xopen-xcu4.mf
diffstat 6 files changed, 976 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/exception_lists/closed-bins	Tue Oct 19 22:35:04 2010 -0700
+++ b/exception_lists/closed-bins	Wed Oct 20 23:13:10 2010 -0700
@@ -37,6 +37,7 @@
 ./usr/lib/localedef/src/iso_8859_1/extension.src
 ./usr/lib/localedef/src/iso_8859_1/localedef.src
 ./usr/bin/localedef
+./usr/bin/od
 ./usr/bin/printf
 ./usr/bin/tr
 ./usr/bin/tail
@@ -50,6 +51,7 @@
 ./usr/xpg4/bin/hash
 ./usr/xpg4/bin/jobs
 ./usr/xpg4/bin/kill
+./usr/xpg4/bin/od
 ./usr/xpg4/bin/read
 ./usr/xpg4/bin/sh
 ./usr/xpg4/bin/tail
--- a/usr/src/Makefile.lint	Tue Oct 19 22:35:04 2010 -0700
+++ b/usr/src/Makefile.lint	Wed Oct 20 23:13:10 2010 -0700
@@ -219,6 +219,7 @@
 	cmd/nl \
 	cmd/nohup \
 	cmd/nscd \
+	cmd/od \
 	cmd/pagesize \
 	cmd/passwd \
 	cmd/pathchk \
--- a/usr/src/cmd/Makefile	Tue Oct 19 22:35:04 2010 -0700
+++ b/usr/src/cmd/Makefile	Wed Oct 20 23:13:10 2010 -0700
@@ -284,6 +284,7 @@
 	nscd		\
 	oamuser		\
 	oawk		\
+	od		\
 	pack		\
 	pagesize	\
 	passmgmt	\
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/od/Makefile	Wed Oct 20 23:13:10 2010 -0700
@@ -0,0 +1,56 @@
+#
+# 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 at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
+#
+PROG=		od
+XPG4PROG=	$(PROG)
+
+OBJS=		od.o
+SRCS=	 	$(OBJS:%.o=%.c)
+
+include ../Makefile.cmd
+
+CLOBBERFILES=	$(PROG)
+
+
+C99MODE=	-xc99=%all
+C99LMODE=	-Xc99=%all
+CPPFLAGS +=	-D__EXTENSIONS__ -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
+LINTFLAGS +=	-D__EXTENSIONS__
+
+# install rules
+$(ROOTINC)/% : %
+	$(INS.file)
+
+.KEEP_STATE:
+
+.PARALLEL: $(OBJS)
+
+all:		$(PROG) 
+
+$(PROG):	$(OBJS)
+	$(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+	$(POST_PROCESS)
+
+install: all .WAIT $(ROOTPROG) $(ROOTXPG4PROG)
+
+$(ROOTXPG4PROG):
+	-$(RM) $@
+	-$(LN) -s ../../bin/$(PROG) $@
+
+lint: 	lint_SRCS
+
+clean:
+	$(RM) $(OBJS)
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/od/od.c	Wed Oct 20 23:13:10 2010 -0700
@@ -0,0 +1,915 @@
+/*
+ * 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
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * 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 <sys/types.h>
+#include <string.h>
+#include <err.h>
+#include <wchar.h>
+#include <locale.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#define	_(x)	gettext(x)
+
+/* address format */
+static char *afmt  =	"%07llo";
+static char *cfmt  =    "       ";
+
+static FILE *input = NULL;
+static size_t lcm = 1;
+static size_t blocksize = 16;
+static int numfiles = 0;
+static int curfile = 0;
+static char **files = NULL;
+static off_t limit = -1;
+
+/*
+ * 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 */
+	void	(*func)(buffer_t *, int);	/* output function */
+	struct output	*next;			/* link node */
+} 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)
+{
+	(void) fprintf(stderr, _("usage: od [-bcCdDfFoOsSvxX] "
+	    "[-t types ]... [-A base] [-j skip] [-N count] [file]...\n"));
+	exit(1);
+}
+
+#define	DECL_GET(typ)							\
+static typ								\
+get_ ## typ(buffer_t *b, int index)					\
+{									\
+	typ val = *(typ *)(void *)(b->data + index);			\
+	return (val);							\
+}
+DECL_GET(u8)
+DECL_GET(u16)
+DECL_GET(u32)
+DECL_GET(u64)
+DECL_GET(s8)
+DECL_GET(s16)
+DECL_GET(s32)
+DECL_GET(s64)
+DECL_GET(fF)
+DECL_GET(fD)
+DECL_GET(fL)
+
+#define	DECL_OUT(nm, typ, fmt)					\
+static void							\
+do_ ## nm(buffer_t *buf, int index)				\
+{								\
+	typ v = get_ ## typ(buf, index);			\
+	(void) printf(fmt, v);					\
+}								\
+								\
+static output_t output_ ## nm =  {				\
+	sizeof (typ), do_ ## nm					\
+};
+
+DECL_OUT(oct_b, u8, " %03o")
+DECL_OUT(oct_w, u16, " %06ho")
+DECL_OUT(oct_d, u32, " %011o")
+DECL_OUT(oct_q, u64, " %022llo")
+DECL_OUT(dec_b, u8, " %03u")
+DECL_OUT(dec_w, u16, " %05hu")
+DECL_OUT(dec_d, u32, " %010u")
+DECL_OUT(dec_q, u64, " %020llu")
+DECL_OUT(sig_b, s8, " %03d")
+DECL_OUT(sig_w, s16, " %6.05hd")
+DECL_OUT(sig_d, s32, " %11.010d")
+DECL_OUT(sig_q, s64, " %20.019lld")
+DECL_OUT(hex_b, u8, " %02x")
+DECL_OUT(hex_w, u16, " %04hx")
+DECL_OUT(hex_d, s32, " %08x")
+DECL_OUT(hex_q, s64, " %016llx")
+DECL_OUT(float, fF, " %14.7e")
+DECL_OUT(double, fD, " %21.14e")
+DECL_OUT(ldouble, fL, " %24.14Le")
+
+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
+do_ascii(buffer_t *buf, int index)
+{
+	uint8_t v = get_u8(buf, index);
+
+	(void) fputc(' ', stdout);
+	(void) fputs(ascii[v & 0x7f], stdout);
+}
+
+static output_t output_ascii = {
+	1, do_ascii,
+};
+
+static void
+do_char(buffer_t *buf, int index)
+{
+	static int	nresid = 0;
+	static int	printable = 0;
+	int		cnt;
+	int		avail;
+	int		nb;
+	char		scratch[10];
+	wchar_t		wc;
+	int		which;
+
+	uint8_t v = get_u8(buf, index);
+
+	/*
+	 * If there were residual bytes from an earlier
+	 * character, then just display the ** continuation
+	 * indication.
+	 */
+	if (nresid) {
+		if (printable) {
+			(void) fputs("  **", stdout);
+		} 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;
+	avail = buf->navail;
+	if (avail > MB_CUR_MAX)
+		avail = MB_CUR_MAX;
+	for (cnt = 1, which = index + 1; cnt < avail; cnt++, which++) {
+		scratch[cnt] = buf->data[which & buf->mask];
+	}
+
+	/* now see if the value is a real character */
+	nresid = 0;
+	wc = 0;
+	nb = mbtowc(&wc, scratch, avail);
+	if (nb < 0) {
+		(void) printf(" %03o", v);
+		return;
+	}
+	if (nb == 0) {
+		(void) fputs("  \\0", stdout);
+		return;
+	}
+	nresid = nb - 1;
+	if (nb && iswprint(wc)) {
+		scratch[nb] = 0;
+		(void) fputs("   ", stdout);
+		(void) fputs(scratch, stdout);
+		printable = 1;
+		return;
+	}
+	printable = 0;
+	if (wc == 0) {
+		(void) fputs("  \\0", stdout);
+	} else if (wc == '\b') {
+		(void) fputs("  \\b", stdout);
+	} else if (wc == '\f') {
+		(void) fputs("  \\f", stdout);
+	} else if (wc == '\n') {
+		(void) fputs("  \\n", stdout);
+	} else if (wc == '\r') {
+		(void) fputs("  \\r", stdout);
+	} else if (wc == '\t') {
+		(void) fputs("  \\t", stdout);
+	} else {
+		(void) printf(" %03o", v);
+	}
+}
+
+static output_t output_char = {
+	1, do_char,
+};
+
+/*
+ * List of output formatting structures.
+ */
+static output_t *head = NULL;
+static output_t **tailp = &head;
+
+static void
+add_out(output_t *src)
+{
+	output_t	*out;
+	int		m;
+
+	if ((out = calloc(1, sizeof (*src))) == NULL) {
+		err(1, "malloc");
+	}
+
+	m = lcm;
+	while ((m % src->width) != 0) {
+		m += lcm;
+	}
+	lcm = m;
+	blocksize = lcm;
+	while (blocksize < 16)
+		blocksize *= 2;
+
+	(void) memcpy(out, src, sizeof (*src));
+	*tailp = out;
+	tailp = &out->next;
+}
+
+static FILE *
+next_input(void)
+{
+	for (;;) {
+		if (curfile >= numfiles)
+			return (NULL);
+
+		if (input != NULL) {
+			if ((input = freopen(files[curfile], "r", input)) !=
+			    NULL) {
+				curfile++;
+				return (input);
+			}
+		} else {
+			if ((input = fopen(files[curfile], "r")) != NULL) {
+				curfile++;
+				return (input);
+			}
+		}
+		warn("open: %s", files[curfile]);
+		curfile++;
+	}
+}
+
+static void
+refill(buffer_t *b)
+{
+	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.
+	 */
+	while ((input != NULL) && (b->navail < (2 * blocksize))) {
+
+		/* we preload the next one in advance */
+
+		if (limit == 0) {
+			(void) fclose(input);
+			input = NULL;
+			continue;
+		}
+
+		/* we want to read a whole block if possible */
+		want = blocksize;
+		if ((limit >= 0) && (want > limit)) {
+			want = limit;
+		}
+		zero = blocksize;
+
+		while (want && input) {
+			int	c;
+			b->prod &= b->mask;
+			c = (b->prod + want > (b->mask + 1)) ?
+			    b->mask - b->prod :
+			    want;
+
+			n = fread(b->data + b->prod, 1, c, input);
+			if (n < 0) {
+				warn("read: %s",
+				    files ? files[curfile-1] : "stdin");
+				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->data[b->prod & b->mask] = 0;
+			b->prod++;
+			b->prod &= b->mask;
+			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) {
+		errx(1, _("missing type string"));
+	}
+	while (*typestr) {
+		switch (*typestr) {
+		case 'a':
+			typestr++;
+			add_out(&output_ascii);
+			break;
+		case 'c':
+			add_out(&output_char);
+			typestr++;
+			break;
+		case 'f':
+			typestr++;
+			switch (*typestr) {
+			case 'F':
+			case '4':
+				add_out(&output_float);
+				typestr++;
+				break;
+			case '8':
+			case 'D':
+				add_out(&output_double);
+				typestr++;
+				break;
+			case 'L':
+				add_out(&output_ldouble);
+				typestr++;
+				break;
+			default:
+				add_out(&output_float);
+				break;
+			}
+			break;
+
+
+		case 'd':
+			typestr++;
+			if (strchr(STR1, *typestr)) {
+				typestr++;
+				add_out(&output_sig_b);
+			} else if (strchr(STR2, *typestr)) {
+				typestr++;
+				add_out(&output_sig_w);
+			} else if (strchr(STR4, *typestr)) {
+				typestr++;
+				add_out(&output_sig_d);
+			} else if (strchr(STR8, *typestr)) {
+				typestr++;
+				add_out(&output_sig_q);
+			} else {
+				add_out(&output_sig_d);
+			}
+			break;
+
+		case 'u':
+			typestr++;
+			if (strchr(STR1, *typestr)) {
+				typestr++;
+				add_out(&output_dec_b);
+			} else if (strchr(STR2, *typestr)) {
+				typestr++;
+				add_out(&output_dec_w);
+			} else if (strchr(STR4, *typestr)) {
+				typestr++;
+				add_out(&output_dec_d);
+			} else if (strchr(STR8, *typestr)) {
+				typestr++;
+				add_out(&output_dec_q);
+			} else {
+				add_out(&output_dec_d);
+			}
+			break;
+
+		case 'o':
+			typestr++;
+			if (strchr(STR1, *typestr)) {
+				typestr++;
+				add_out(&output_oct_b);
+			} else if (strchr(STR2, *typestr)) {
+				typestr++;
+				add_out(&output_oct_w);
+			} else if (strchr(STR4, *typestr)) {
+				typestr++;
+				add_out(&output_oct_d);
+			} else if (strchr(STR8, *typestr)) {
+				typestr++;
+				add_out(&output_oct_q);
+			} else {
+				add_out(&output_oct_d);
+			}
+			break;
+
+		case 'x':
+			typestr++;
+			if (strchr(STR1, *typestr)) {
+				typestr++;
+				add_out(&output_hex_b);
+			} else if (strchr(STR2, *typestr)) {
+				typestr++;
+				add_out(&output_hex_w);
+			} else if (strchr(STR4, *typestr)) {
+				typestr++;
+				add_out(&output_hex_d);
+			} else if (strchr(STR8, *typestr)) {
+				typestr++;
+				add_out(&output_hex_q);
+			} else {
+				add_out(&output_hex_d);
+			}
+			break;
+
+		default:
+			errx(1, _("unrecognized type string character: %c"),
+			    *typestr);
+			exit(1);
+		}
+	}
+}
+
+int
+main(int argc, char **argv)
+{
+	int		c;
+	int		i;
+	buffer_t	buffer;
+	boolean_t	first = B_TRUE;
+	boolean_t	doall = B_FALSE;
+	boolean_t	same = B_FALSE;
+	boolean_t	newarg = B_FALSE;
+	off_t		offset = 0;
+	off_t		skip = 0;
+	char		*eptr;
+	char		*offstr = 0;
+
+	input = stdin;
+
+	(void) setlocale(LC_ALL, "");
+
+	while ((c = getopt(argc, argv, "A:bCcdDfFj:N:oOsSxXvt:")) != EOF) {
+		switch (c) {
+		case 'A':
+			newarg = B_TRUE;
+			if (strlen(optarg) > 1) {
+				afmt = NULL;
+			}
+			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:
+				afmt = NULL;
+				break;
+			}
+			if (strlen(optarg) != 1) {
+				afmt = NULL;
+			}
+			if (afmt == NULL)
+				warnx(_("invalid address base, "
+				    "must be o, d, x, or n"));
+			break;
+
+		case 'b':
+			add_out(&output_oct_b);
+			break;
+
+		case 'c':
+		case 'C':
+			add_out(&output_char);
+			break;
+
+		case 'f':
+			add_out(&output_float);
+			break;
+
+		case 'F':
+			add_out(&output_double);
+			break;
+
+		case 'd':
+			add_out(&output_dec_w);
+			break;
+
+		case 'D':
+			add_out(&output_dec_d);
+			break;
+
+		case 't':
+			newarg = B_TRUE;
+			do_type_string(optarg);
+			break;
+
+		case 'o':
+			add_out(&output_oct_w);
+			break;
+
+		case 'O':
+			add_out(&output_oct_d);
+			break;
+
+		case 's':
+			add_out(&output_sig_w);
+			break;
+
+		case 'S':
+			add_out(&output_sig_d);
+			break;
+
+		case 'x':
+			add_out(&output_hex_w);
+			break;
+
+		case 'X':
+			add_out(&output_hex_d);
+			break;
+
+		case 'v':
+			doall = B_TRUE;
+			break;
+
+		case 'j':
+			newarg = B_TRUE;
+			skip = strtoll(optarg, &eptr, 0);
+			if (*eptr == 'b') {
+				skip <<= 9;	/* 512 bytes */
+				eptr++;
+			} else if (*eptr == 'k') {
+				skip <<= 10;	/* 1k */
+				eptr++;
+			} else if (*eptr == 'm') {
+				skip <<= 20;	/* 1m */
+				eptr++;
+			} else if (*eptr == 'g') {
+				skip <<= 30;	/* 1g */
+				eptr++;
+			}
+			if ((skip < 0) || (eptr[0] != 0)) {
+				warnx(_("invalid skip count '%s' specified"),
+				    optarg);
+				exit(1);
+			}
+			break;
+
+		case 'N':
+			newarg = B_TRUE;
+			limit = strtoll(optarg, &eptr, 0);
+			/*
+			 * 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++;
+			}
+			if ((limit < 0) || (eptr[0] != 0)) {
+				warnx(_("invalid byte count '%s' specified"),
+				    optarg);
+				exit(1);
+			}
+			break;
+
+		default:
+			usage();
+			break;
+		}
+	}
+
+	/* this finds the smallest power of two size we can use */
+	buffer.mask = (1 << (ffs(blocksize * 3) + 1)) - 1;
+	buffer.data = memalign(16, buffer.mask + 1);
+	if (buffer.data == NULL) {
+		err(1, "memalign");
+	}
+
+
+	/*
+	 * 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) {
+		if (((argc - optind) == 1) && (argv[optind][0] == '+')) {
+			offstr = argv[optind];
+			argc--;
+		} else if (((argc - optind) == 2) &&
+		    (strchr("+0123456789", (argv[optind + 1][0])) != NULL)) {
+			offstr = argv[optind + 1];
+			argc--;
+		}
+	}
+	if (offstr) {
+		int base = 0;
+		int mult = 1;
+		int l;
+		if (*offstr == '+') {
+			offstr++;
+		}
+		l = strlen(offstr);
+		if ((strncmp(offstr, "0x", 2) == 0)) {
+			afmt = "%07llx";
+			base = 16;
+			offstr += 2;
+			if (offstr[l - 1] == 'B') {
+				offstr[l - 1] = 0;
+				l--;
+				mult = 512;
+			}
+		} else {
+			base = 8;
+			afmt = "%07llo";
+			if ((offstr[l - 1] == 'B') || (offstr[l - 1] == 'b')) {
+				offstr[l - 1] = 0;
+				l--;
+				mult = 512;
+			}
+			if (offstr[l - 1] == '.') {
+				offstr[l - 1] = 0;
+				base = 10;
+				afmt = "%07lld";
+			}
+		}
+		skip = strtoll(offstr, &eptr, base);
+		if (*eptr != '\0') {
+			errx(1, _("invalid offset string specified"));
+		}
+		skip *= mult;
+		offset += skip;
+	}
+
+	/*
+	 * Allocate an array for all the input files.
+	 */
+	if (argc > optind) {
+		files = calloc(sizeof (char *), argc - optind);
+		for (i = 0; i < argc - optind; i++) {
+			files[i] = argv[optind + i];
+			numfiles++;
+		}
+		input = next_input();
+	} else {
+		input = stdin;
+	}
+
+	/*
+	 * We need to seek ahead.  fseek would be faster.
+	 */
+	while (skip && (input != NULL)) {
+		struct stat sbuf;
+
+		/*
+		 * Only fseek() on regular files.  (Others
+		 * we have to read().
+		 */
+		if (fstat(fileno(input), &sbuf) < 0) {
+			warn("fstat: %s", files[curfile-1]);
+			input = next_input();
+			continue;
+		}
+		if (S_ISREG(sbuf.st_mode)) {
+			/*
+			 * No point in seeking a file that is too
+			 * short to begin with.
+			 */
+			if (sbuf.st_size < skip) {
+				skip -= sbuf.st_size;
+				input = next_input();
+				continue;
+			}
+			if (fseeko(input, skip, SEEK_SET) < 0) {
+				err(1, "fseek:%s", files[curfile-1]);
+			}
+			/* Done seeking. */
+			skip = 0;
+			break;
+		}
+
+		/*
+		 * fgetc seems like it would be slow, but it uses
+		 * buffered I/O, so it should be fast enough.
+		 */
+		flockfile(input);
+		while (skip) {
+			if (getc_unlocked(input) == EOF) {
+				funlockfile(input);
+				if (ferror(input)) {
+					warn("read: %s", files[curfile-1]);
+				}
+				input = next_input();
+				if (input != NULL) {
+					flockfile(input);
+				}
+				break;
+			}
+			skip--;
+		}
+		if (input != NULL)
+			funlockfile(input);
+	}
+
+	if (head == NULL) {
+		add_out(&output_oct_w);
+	}
+
+	buffer.navail = 0;
+	buffer.prod = 0;
+	buffer.cons = 0;
+
+	for (refill(&buffer); buffer.navail > 0; refill(&buffer)) {
+		output_t *out;
+		int	mx;
+		int	j, k;
+
+		/*
+		 * If this buffer was the same as last, then just
+		 * dump an asterisk.
+		 */
+		if ((!first) && (buffer.navail >= blocksize) && (!doall)) {
+			j = buffer.cons;
+			k = j - blocksize;
+			for (i = 0; i < blocksize; i++) {
+				if (buffer.data[j & buffer.mask] !=
+				    buffer.data[k & buffer.mask]) {
+					break;
+				}
+				j++;
+				k++;
+			}
+			if (i == blocksize) {
+				if (!same) {
+					(void) fputs("*\n", stdout);
+					same = B_TRUE;
+				}
+				buffer.navail -= blocksize;
+				offset += blocksize;
+				buffer.cons += blocksize;
+				buffer.cons &= buffer.mask;
+				continue;
+			}
+		}
+
+		first = B_FALSE;
+		same = B_FALSE;
+		mx = (buffer.navail > blocksize) ? blocksize : buffer.navail;
+
+		for (out = head; out != NULL; out = out->next) {
+
+			if (out == head) {
+				/*LINTED E_SEC_PRINTF_VAR_FMT*/
+				(void) printf(afmt, offset);
+			} else {
+				(void) fputs(cfmt, stdout);
+			}
+			for (i = 0, j = buffer.cons; i < mx; i += out->width) {
+				out->func(&buffer, j);
+				j += out->width;
+				j &= buffer.mask;
+			}
+			(void) fputs("\n", stdout);
+		}
+		buffer.cons += mx;
+		buffer.cons &= buffer.mask;
+		offset += mx;
+		buffer.navail -= mx;
+	}
+	/*LINTED E_SEC_PRINTF_VAR_FMT*/
+	(void) printf(afmt, offset);
+	(void) fputs("\n", stdout);
+	return (0);
+}
--- a/usr/src/pkg/manifests/system-xopen-xcu4.mf	Tue Oct 19 22:35:04 2010 -0700
+++ b/usr/src/pkg/manifests/system-xopen-xcu4.mf	Wed Oct 20 23:13:10 2010 -0700
@@ -63,7 +63,6 @@
 file path=usr/xpg4/bin/nl mode=0555
 file path=usr/xpg4/bin/nm mode=0555
 file path=usr/xpg4/bin/nohup mode=0555
-file path=usr/xpg4/bin/od mode=0555
 file path=usr/xpg4/bin/pr mode=0555
 file path=usr/xpg4/bin/rm mode=0555
 file path=usr/xpg4/bin/sed mode=0555
@@ -87,5 +86,6 @@
 license lic_OSBL license=lic_OSBL
 license lic_OSBL_preamble license=lic_OSBL_preamble
 link path=usr/xpg4/bin/ipcs target=../../bin/ipcs
+link path=usr/xpg4/bin/od target=../../bin/od
 link path=usr/xpg4/bin/tail target=../../bin/tail
 link path=usr/xpg4/bin/tr target=../../bin/tr