xref: /titanic_44/usr/src/cmd/od/od.c (revision 5aec55eb0591d2fcdd38d7dd5408a6ff3456e596)
101335b0dSGarrett D'Amore /*
201335b0dSGarrett D'Amore  * This file and its contents are supplied under the terms of the
301335b0dSGarrett D'Amore  * Common Development and Distribution License ("CDDL"), version 1.0.
4*5aec55ebSGarrett D'Amore  * You may only use this file in accordance with the terms of version
501335b0dSGarrett D'Amore  * 1.0 of the CDDL.
601335b0dSGarrett D'Amore  *
701335b0dSGarrett D'Amore  * A full copy of the text of the CDDL should have accompanied this
801335b0dSGarrett D'Amore  * source.  A copy of the CDDL is also available via the Internet
901335b0dSGarrett D'Amore  * http://www.illumos.org/license/CDDL.
1001335b0dSGarrett D'Amore  */
1101335b0dSGarrett D'Amore 
1201335b0dSGarrett D'Amore /*
1301335b0dSGarrett D'Amore  * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
1401335b0dSGarrett D'Amore  */
1501335b0dSGarrett D'Amore 
1601335b0dSGarrett D'Amore /*
1701335b0dSGarrett D'Amore  * od - octal dump.  Not really just octal anymore; read the POSIX
1801335b0dSGarrett D'Amore  * specification for it -- its more complex than you think!
1901335b0dSGarrett D'Amore  *
2001335b0dSGarrett D'Amore  * NB: We followed the POSIX semantics fairly strictly, where the
2101335b0dSGarrett D'Amore  * legacy code's behavior was in conflict.  In many cases the legacy
2201335b0dSGarrett D'Amore  * Solaris code was so completely broken as to be completely unusable.
2301335b0dSGarrett D'Amore  * (For example, the long double support was broken beyond
2401335b0dSGarrett D'Amore  * imagination!)  Note that GNU coreutils violates POSIX in a few
2501335b0dSGarrett D'Amore  * interesting ways, such as changing the numbering of the addresses
2601335b0dSGarrett D'Amore  * when skipping.  (Address starts should always be at 0, according to
2701335b0dSGarrett D'Amore  * the sample output in the Open Group man page.)
2801335b0dSGarrett D'Amore  */
2901335b0dSGarrett D'Amore 
3001335b0dSGarrett D'Amore #include <stdio.h>
3101335b0dSGarrett D'Amore #include <stdlib.h>
3201335b0dSGarrett D'Amore #include <sys/types.h>
3301335b0dSGarrett D'Amore #include <string.h>
3401335b0dSGarrett D'Amore #include <err.h>
3501335b0dSGarrett D'Amore #include <wchar.h>
3601335b0dSGarrett D'Amore #include <locale.h>
3701335b0dSGarrett D'Amore #include <unistd.h>
3801335b0dSGarrett D'Amore #include <sys/stat.h>
3901335b0dSGarrett D'Amore 
4001335b0dSGarrett D'Amore #define	_(x)	gettext(x)
4101335b0dSGarrett D'Amore 
4201335b0dSGarrett D'Amore /* address format */
4301335b0dSGarrett D'Amore static char *afmt  =	"%07llo";
4401335b0dSGarrett D'Amore static char *cfmt  =    "       ";
4501335b0dSGarrett D'Amore 
4601335b0dSGarrett D'Amore static FILE *input = NULL;
4701335b0dSGarrett D'Amore static size_t lcm = 1;
4801335b0dSGarrett D'Amore static size_t blocksize = 16;
4901335b0dSGarrett D'Amore static int numfiles = 0;
5001335b0dSGarrett D'Amore static int curfile = 0;
5101335b0dSGarrett D'Amore static char **files = NULL;
5201335b0dSGarrett D'Amore static off_t limit = -1;
5301335b0dSGarrett D'Amore 
5401335b0dSGarrett D'Amore /*
5501335b0dSGarrett D'Amore  * This structure describes our ring buffer.  Its always a power of 2
5601335b0dSGarrett D'Amore  * in size to make wrap around calculations fast using a mask instead
5701335b0dSGarrett D'Amore  * of doing modulo.
5801335b0dSGarrett D'Amore  *
5901335b0dSGarrett D'Amore  * The size is calculated thusly: We need three "blocks" of data, as
6001335b0dSGarrett D'Amore  * we process a block at a time (one block == one line of od output.)
6101335b0dSGarrett D'Amore  *
6201335b0dSGarrett D'Amore  * We need lookahead of an extra block to support multibyte chars.  We
6301335b0dSGarrett D'Amore  * also have a look behind so that we can avoid printing lines that
6401335b0dSGarrett D'Amore  * are identical to what we've already printed.  Finally, we need the
6501335b0dSGarrett D'Amore  * current block.
6601335b0dSGarrett D'Amore  *
6701335b0dSGarrett D'Amore  * The block size is determined by the least common multiple of the
6801335b0dSGarrett D'Amore  * data items being displayed.  Usually it will be 16, but sometimes
6901335b0dSGarrett D'Amore  * it is 24 (when 12-byte long doubles are presented.)
7001335b0dSGarrett D'Amore  *
7101335b0dSGarrett D'Amore  * The data buffer is allocaed via memalign to make sure it is
7201335b0dSGarrett D'Amore  * properly aligned.
7301335b0dSGarrett D'Amore  */
7401335b0dSGarrett D'Amore typedef struct buffer {
7501335b0dSGarrett D'Amore 	char	*data;		/* data buffer */
7601335b0dSGarrett D'Amore 	int	prod;		/* producer index */
7701335b0dSGarrett D'Amore 	int	cons;		/* consumer index */
7801335b0dSGarrett D'Amore 	int	mask;		/* buffer size - 1, wraparound index */
7901335b0dSGarrett D'Amore 	int	navail;		/* total bytes avail */
8001335b0dSGarrett D'Amore } buffer_t;
8101335b0dSGarrett D'Amore 
8201335b0dSGarrett D'Amore /*
8301335b0dSGarrett D'Amore  * This structure is used to provide information on a specific output
8401335b0dSGarrett D'Amore  * format.  We link them together in a list representing the output
8501335b0dSGarrett D'Amore  * formats that the user has selected.
8601335b0dSGarrett D'Amore  */
8701335b0dSGarrett D'Amore typedef struct output {
8801335b0dSGarrett D'Amore 	int	width;				/* bytes consumed per call */
8901335b0dSGarrett D'Amore 	void	(*func)(buffer_t *, int);	/* output function */
9001335b0dSGarrett D'Amore 	struct output	*next;			/* link node */
9101335b0dSGarrett D'Amore } output_t;
9201335b0dSGarrett D'Amore 
9301335b0dSGarrett D'Amore /*
9401335b0dSGarrett D'Amore  * Specifiers
9501335b0dSGarrett D'Amore  */
9601335b0dSGarrett D'Amore 
9701335b0dSGarrett D'Amore typedef unsigned char		u8;
9801335b0dSGarrett D'Amore typedef unsigned short		u16;
9901335b0dSGarrett D'Amore typedef unsigned int		u32;
10001335b0dSGarrett D'Amore typedef unsigned long long	u64;
10101335b0dSGarrett D'Amore typedef char			s8;
10201335b0dSGarrett D'Amore typedef short			s16;
10301335b0dSGarrett D'Amore typedef int			s32;
10401335b0dSGarrett D'Amore typedef long long		s64;
10501335b0dSGarrett D'Amore typedef float			fF;
10601335b0dSGarrett D'Amore typedef	double			fD;
10701335b0dSGarrett D'Amore typedef long double		fL;
10801335b0dSGarrett D'Amore 
10901335b0dSGarrett D'Amore static void
11001335b0dSGarrett D'Amore usage(void)
11101335b0dSGarrett D'Amore {
11201335b0dSGarrett D'Amore 	(void) fprintf(stderr, _("usage: od [-bcCdDfFoOsSvxX] "
11301335b0dSGarrett D'Amore 	    "[-t types ]... [-A base] [-j skip] [-N count] [file]...\n"));
11401335b0dSGarrett D'Amore 	exit(1);
11501335b0dSGarrett D'Amore }
11601335b0dSGarrett D'Amore 
11701335b0dSGarrett D'Amore #define	DECL_GET(typ)							\
11801335b0dSGarrett D'Amore static typ								\
11901335b0dSGarrett D'Amore get_ ## typ(buffer_t *b, int index)					\
12001335b0dSGarrett D'Amore {									\
12101335b0dSGarrett D'Amore 	typ val = *(typ *)(void *)(b->data + index);			\
12201335b0dSGarrett D'Amore 	return (val);							\
12301335b0dSGarrett D'Amore }
12401335b0dSGarrett D'Amore DECL_GET(u8)
12501335b0dSGarrett D'Amore DECL_GET(u16)
12601335b0dSGarrett D'Amore DECL_GET(u32)
12701335b0dSGarrett D'Amore DECL_GET(u64)
12801335b0dSGarrett D'Amore DECL_GET(s8)
12901335b0dSGarrett D'Amore DECL_GET(s16)
13001335b0dSGarrett D'Amore DECL_GET(s32)
13101335b0dSGarrett D'Amore DECL_GET(s64)
13201335b0dSGarrett D'Amore DECL_GET(fF)
13301335b0dSGarrett D'Amore DECL_GET(fD)
13401335b0dSGarrett D'Amore DECL_GET(fL)
13501335b0dSGarrett D'Amore 
13601335b0dSGarrett D'Amore #define	DECL_OUT(nm, typ, fmt)					\
13701335b0dSGarrett D'Amore static void							\
13801335b0dSGarrett D'Amore do_ ## nm(buffer_t *buf, int index)				\
13901335b0dSGarrett D'Amore {								\
14001335b0dSGarrett D'Amore 	typ v = get_ ## typ(buf, index);			\
14101335b0dSGarrett D'Amore 	(void) printf(fmt, v);					\
14201335b0dSGarrett D'Amore }								\
14301335b0dSGarrett D'Amore 								\
14401335b0dSGarrett D'Amore static output_t output_ ## nm =  {				\
14501335b0dSGarrett D'Amore 	sizeof (typ), do_ ## nm					\
14601335b0dSGarrett D'Amore };
14701335b0dSGarrett D'Amore 
14801335b0dSGarrett D'Amore DECL_OUT(oct_b, u8, " %03o")
14901335b0dSGarrett D'Amore DECL_OUT(oct_w, u16, " %06ho")
15001335b0dSGarrett D'Amore DECL_OUT(oct_d, u32, " %011o")
15101335b0dSGarrett D'Amore DECL_OUT(oct_q, u64, " %022llo")
15201335b0dSGarrett D'Amore DECL_OUT(dec_b, u8, " %03u")
15301335b0dSGarrett D'Amore DECL_OUT(dec_w, u16, " %05hu")
15401335b0dSGarrett D'Amore DECL_OUT(dec_d, u32, " %010u")
15501335b0dSGarrett D'Amore DECL_OUT(dec_q, u64, " %020llu")
15601335b0dSGarrett D'Amore DECL_OUT(sig_b, s8, " %03d")
15701335b0dSGarrett D'Amore DECL_OUT(sig_w, s16, " %6.05hd")
15801335b0dSGarrett D'Amore DECL_OUT(sig_d, s32, " %11.010d")
15901335b0dSGarrett D'Amore DECL_OUT(sig_q, s64, " %20.019lld")
16001335b0dSGarrett D'Amore DECL_OUT(hex_b, u8, " %02x")
16101335b0dSGarrett D'Amore DECL_OUT(hex_w, u16, " %04hx")
16201335b0dSGarrett D'Amore DECL_OUT(hex_d, s32, " %08x")
16301335b0dSGarrett D'Amore DECL_OUT(hex_q, s64, " %016llx")
16401335b0dSGarrett D'Amore DECL_OUT(float, fF, " %14.7e")
16501335b0dSGarrett D'Amore DECL_OUT(double, fD, " %21.14e")
16601335b0dSGarrett D'Amore DECL_OUT(ldouble, fL, " %24.14Le")
16701335b0dSGarrett D'Amore 
16801335b0dSGarrett D'Amore static char *ascii[] = {
16901335b0dSGarrett D'Amore 	"nul", "soh", "stx", "etx", "eot", "enq", "ack", " be",
17001335b0dSGarrett D'Amore 	" bs", " ht", " lf", " vt", " ff", " cr", " so", " si",
17101335b0dSGarrett D'Amore 	"dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
17201335b0dSGarrett D'Amore 	"can", " em", "sub", "esc", " fs", " gs", " rs", " us",
17301335b0dSGarrett D'Amore 	" sp", "  !", "  \"", "  #", "  $", "  %", "  &", "  '",
17401335b0dSGarrett D'Amore 	"  (", "  )", "  *", "  +", "  ,", "  -", "  .", "  /",
17501335b0dSGarrett D'Amore 	"  0", "  1", "  2", "  3", "  4", "  5", "  6", "  7",
17601335b0dSGarrett D'Amore 	"  8", "  9", "  :", "  ;", "  <", "  =", "  >", "  ?",
17701335b0dSGarrett D'Amore 	"  @", "  A", "  B", "  C", "  D", "  E", "  F", "  G",
17801335b0dSGarrett D'Amore 	"  H", "  I", "  J", "  K", "  L", "  M", "  N", "  O",
17901335b0dSGarrett D'Amore 	"  P", "  Q", "  R", "  S", "  T", "  U", "  V", "  W",
18001335b0dSGarrett D'Amore 	"  X", "  Y", "  Z", "  [", "  \\", "  ]", "  ^", "  _",
18101335b0dSGarrett D'Amore 	"  `", "  a", "  b", "  c", "  d", "  e", "  f", "  g",
18201335b0dSGarrett D'Amore 	"  h", "  i", "  j", "  k", "  l", "  m", "  n", "  o",
18301335b0dSGarrett D'Amore 	"  p", "  q", "  r", "  s", "  t", "  u", "  v", "  w",
18401335b0dSGarrett D'Amore 	"  x", "  y", "  z", "  {", "  |", "  }", "  ~", "del"
18501335b0dSGarrett D'Amore };
18601335b0dSGarrett D'Amore 
18701335b0dSGarrett D'Amore static void
18801335b0dSGarrett D'Amore do_ascii(buffer_t *buf, int index)
18901335b0dSGarrett D'Amore {
19001335b0dSGarrett D'Amore 	uint8_t v = get_u8(buf, index);
19101335b0dSGarrett D'Amore 
19201335b0dSGarrett D'Amore 	(void) fputc(' ', stdout);
19301335b0dSGarrett D'Amore 	(void) fputs(ascii[v & 0x7f], stdout);
19401335b0dSGarrett D'Amore }
19501335b0dSGarrett D'Amore 
19601335b0dSGarrett D'Amore static output_t output_ascii = {
19701335b0dSGarrett D'Amore 	1, do_ascii,
19801335b0dSGarrett D'Amore };
19901335b0dSGarrett D'Amore 
20001335b0dSGarrett D'Amore static void
20101335b0dSGarrett D'Amore do_char(buffer_t *buf, int index)
20201335b0dSGarrett D'Amore {
20301335b0dSGarrett D'Amore 	static int	nresid = 0;
20401335b0dSGarrett D'Amore 	static int	printable = 0;
20501335b0dSGarrett D'Amore 	int		cnt;
20601335b0dSGarrett D'Amore 	int		avail;
20701335b0dSGarrett D'Amore 	int		nb;
20801335b0dSGarrett D'Amore 	char		scratch[10];
20901335b0dSGarrett D'Amore 	wchar_t		wc;
21001335b0dSGarrett D'Amore 	int		which;
21101335b0dSGarrett D'Amore 
21201335b0dSGarrett D'Amore 	uint8_t v = get_u8(buf, index);
21301335b0dSGarrett D'Amore 
21401335b0dSGarrett D'Amore 	/*
21501335b0dSGarrett D'Amore 	 * If there were residual bytes from an earlier
21601335b0dSGarrett D'Amore 	 * character, then just display the ** continuation
21701335b0dSGarrett D'Amore 	 * indication.
21801335b0dSGarrett D'Amore 	 */
21901335b0dSGarrett D'Amore 	if (nresid) {
22001335b0dSGarrett D'Amore 		if (printable) {
22101335b0dSGarrett D'Amore 			(void) fputs("  **", stdout);
22201335b0dSGarrett D'Amore 		} else {
22301335b0dSGarrett D'Amore 			(void) printf(" %03o", v);
22401335b0dSGarrett D'Amore 		}
22501335b0dSGarrett D'Amore 		nresid--;
22601335b0dSGarrett D'Amore 		return;
22701335b0dSGarrett D'Amore 	}
22801335b0dSGarrett D'Amore 
22901335b0dSGarrett D'Amore 	/*
23001335b0dSGarrett D'Amore 	 * Peek ahead up to MB_CUR_MAX characters.  This has to be
23101335b0dSGarrett D'Amore 	 * done carefully because we might need to look into the next
23201335b0dSGarrett D'Amore 	 * block to really know for sure.
23301335b0dSGarrett D'Amore 	 */
23401335b0dSGarrett D'Amore 	scratch[0] = v;
23501335b0dSGarrett D'Amore 	avail = buf->navail;
23601335b0dSGarrett D'Amore 	if (avail > MB_CUR_MAX)
23701335b0dSGarrett D'Amore 		avail = MB_CUR_MAX;
23801335b0dSGarrett D'Amore 	for (cnt = 1, which = index + 1; cnt < avail; cnt++, which++) {
23901335b0dSGarrett D'Amore 		scratch[cnt] = buf->data[which & buf->mask];
24001335b0dSGarrett D'Amore 	}
24101335b0dSGarrett D'Amore 
24201335b0dSGarrett D'Amore 	/* now see if the value is a real character */
24301335b0dSGarrett D'Amore 	nresid = 0;
24401335b0dSGarrett D'Amore 	wc = 0;
24501335b0dSGarrett D'Amore 	nb = mbtowc(&wc, scratch, avail);
24601335b0dSGarrett D'Amore 	if (nb < 0) {
24701335b0dSGarrett D'Amore 		(void) printf(" %03o", v);
24801335b0dSGarrett D'Amore 		return;
24901335b0dSGarrett D'Amore 	}
25001335b0dSGarrett D'Amore 	if (nb == 0) {
25101335b0dSGarrett D'Amore 		(void) fputs("  \\0", stdout);
25201335b0dSGarrett D'Amore 		return;
25301335b0dSGarrett D'Amore 	}
25401335b0dSGarrett D'Amore 	nresid = nb - 1;
25501335b0dSGarrett D'Amore 	if (nb && iswprint(wc)) {
25601335b0dSGarrett D'Amore 		scratch[nb] = 0;
25701335b0dSGarrett D'Amore 		(void) fputs("   ", stdout);
25801335b0dSGarrett D'Amore 		(void) fputs(scratch, stdout);
25901335b0dSGarrett D'Amore 		printable = 1;
26001335b0dSGarrett D'Amore 		return;
26101335b0dSGarrett D'Amore 	}
26201335b0dSGarrett D'Amore 	printable = 0;
26301335b0dSGarrett D'Amore 	if (wc == 0) {
26401335b0dSGarrett D'Amore 		(void) fputs("  \\0", stdout);
26501335b0dSGarrett D'Amore 	} else if (wc == '\b') {
26601335b0dSGarrett D'Amore 		(void) fputs("  \\b", stdout);
26701335b0dSGarrett D'Amore 	} else if (wc == '\f') {
26801335b0dSGarrett D'Amore 		(void) fputs("  \\f", stdout);
26901335b0dSGarrett D'Amore 	} else if (wc == '\n') {
27001335b0dSGarrett D'Amore 		(void) fputs("  \\n", stdout);
27101335b0dSGarrett D'Amore 	} else if (wc == '\r') {
27201335b0dSGarrett D'Amore 		(void) fputs("  \\r", stdout);
27301335b0dSGarrett D'Amore 	} else if (wc == '\t') {
27401335b0dSGarrett D'Amore 		(void) fputs("  \\t", stdout);
27501335b0dSGarrett D'Amore 	} else {
27601335b0dSGarrett D'Amore 		(void) printf(" %03o", v);
27701335b0dSGarrett D'Amore 	}
27801335b0dSGarrett D'Amore }
27901335b0dSGarrett D'Amore 
28001335b0dSGarrett D'Amore static output_t output_char = {
28101335b0dSGarrett D'Amore 	1, do_char,
28201335b0dSGarrett D'Amore };
28301335b0dSGarrett D'Amore 
28401335b0dSGarrett D'Amore /*
28501335b0dSGarrett D'Amore  * List of output formatting structures.
28601335b0dSGarrett D'Amore  */
28701335b0dSGarrett D'Amore static output_t *head = NULL;
28801335b0dSGarrett D'Amore static output_t **tailp = &head;
28901335b0dSGarrett D'Amore 
29001335b0dSGarrett D'Amore static void
29101335b0dSGarrett D'Amore add_out(output_t *src)
29201335b0dSGarrett D'Amore {
29301335b0dSGarrett D'Amore 	output_t	*out;
29401335b0dSGarrett D'Amore 	int		m;
29501335b0dSGarrett D'Amore 
29601335b0dSGarrett D'Amore 	if ((out = calloc(1, sizeof (*src))) == NULL) {
29701335b0dSGarrett D'Amore 		err(1, "malloc");
29801335b0dSGarrett D'Amore 	}
29901335b0dSGarrett D'Amore 
30001335b0dSGarrett D'Amore 	m = lcm;
30101335b0dSGarrett D'Amore 	while ((m % src->width) != 0) {
30201335b0dSGarrett D'Amore 		m += lcm;
30301335b0dSGarrett D'Amore 	}
30401335b0dSGarrett D'Amore 	lcm = m;
30501335b0dSGarrett D'Amore 	blocksize = lcm;
30601335b0dSGarrett D'Amore 	while (blocksize < 16)
30701335b0dSGarrett D'Amore 		blocksize *= 2;
30801335b0dSGarrett D'Amore 
30901335b0dSGarrett D'Amore 	(void) memcpy(out, src, sizeof (*src));
31001335b0dSGarrett D'Amore 	*tailp = out;
31101335b0dSGarrett D'Amore 	tailp = &out->next;
31201335b0dSGarrett D'Amore }
31301335b0dSGarrett D'Amore 
31401335b0dSGarrett D'Amore static FILE *
31501335b0dSGarrett D'Amore next_input(void)
31601335b0dSGarrett D'Amore {
31701335b0dSGarrett D'Amore 	for (;;) {
31801335b0dSGarrett D'Amore 		if (curfile >= numfiles)
31901335b0dSGarrett D'Amore 			return (NULL);
32001335b0dSGarrett D'Amore 
32101335b0dSGarrett D'Amore 		if (input != NULL) {
32201335b0dSGarrett D'Amore 			if ((input = freopen(files[curfile], "r", input)) !=
32301335b0dSGarrett D'Amore 			    NULL) {
32401335b0dSGarrett D'Amore 				curfile++;
32501335b0dSGarrett D'Amore 				return (input);
32601335b0dSGarrett D'Amore 			}
32701335b0dSGarrett D'Amore 		} else {
32801335b0dSGarrett D'Amore 			if ((input = fopen(files[curfile], "r")) != NULL) {
32901335b0dSGarrett D'Amore 				curfile++;
33001335b0dSGarrett D'Amore 				return (input);
33101335b0dSGarrett D'Amore 			}
33201335b0dSGarrett D'Amore 		}
33301335b0dSGarrett D'Amore 		warn("open: %s", files[curfile]);
33401335b0dSGarrett D'Amore 		curfile++;
33501335b0dSGarrett D'Amore 	}
33601335b0dSGarrett D'Amore }
33701335b0dSGarrett D'Amore 
33801335b0dSGarrett D'Amore static void
33901335b0dSGarrett D'Amore refill(buffer_t *b)
34001335b0dSGarrett D'Amore {
34101335b0dSGarrett D'Amore 	int	n;
34201335b0dSGarrett D'Amore 	int	want;
34301335b0dSGarrett D'Amore 	int	zero;
34401335b0dSGarrett D'Amore 
34501335b0dSGarrett D'Amore 	/*
34601335b0dSGarrett D'Amore 	 * If we have 2 blocks of bytes available, we're done.  Note
34701335b0dSGarrett D'Amore 	 * that each iteration usually loads up 16 bytes, unless we
34801335b0dSGarrett D'Amore 	 * run out of data.
34901335b0dSGarrett D'Amore 	 */
35001335b0dSGarrett D'Amore 	while ((input != NULL) && (b->navail < (2 * blocksize))) {
35101335b0dSGarrett D'Amore 
35201335b0dSGarrett D'Amore 		/* we preload the next one in advance */
35301335b0dSGarrett D'Amore 
35401335b0dSGarrett D'Amore 		if (limit == 0) {
35501335b0dSGarrett D'Amore 			(void) fclose(input);
35601335b0dSGarrett D'Amore 			input = NULL;
35701335b0dSGarrett D'Amore 			continue;
35801335b0dSGarrett D'Amore 		}
35901335b0dSGarrett D'Amore 
36001335b0dSGarrett D'Amore 		/* we want to read a whole block if possible */
36101335b0dSGarrett D'Amore 		want = blocksize;
36201335b0dSGarrett D'Amore 		if ((limit >= 0) && (want > limit)) {
36301335b0dSGarrett D'Amore 			want = limit;
36401335b0dSGarrett D'Amore 		}
36501335b0dSGarrett D'Amore 		zero = blocksize;
36601335b0dSGarrett D'Amore 
36701335b0dSGarrett D'Amore 		while (want && input) {
36801335b0dSGarrett D'Amore 			int	c;
36901335b0dSGarrett D'Amore 			b->prod &= b->mask;
37001335b0dSGarrett D'Amore 			c = (b->prod + want > (b->mask + 1)) ?
37101335b0dSGarrett D'Amore 			    b->mask - b->prod :
37201335b0dSGarrett D'Amore 			    want;
37301335b0dSGarrett D'Amore 
37401335b0dSGarrett D'Amore 			n = fread(b->data + b->prod, 1, c, input);
37501335b0dSGarrett D'Amore 			if (n < 0) {
37601335b0dSGarrett D'Amore 				warn("read: %s",
37701335b0dSGarrett D'Amore 				    files ? files[curfile-1] : "stdin");
37801335b0dSGarrett D'Amore 				input = next_input();
37901335b0dSGarrett D'Amore 				continue;
38001335b0dSGarrett D'Amore 			}
38101335b0dSGarrett D'Amore 			if (n == 0) {
38201335b0dSGarrett D'Amore 				input = next_input();
38301335b0dSGarrett D'Amore 				continue;
38401335b0dSGarrett D'Amore 			}
38501335b0dSGarrett D'Amore 			if (limit >= 0)
38601335b0dSGarrett D'Amore 				limit -= n;
38701335b0dSGarrett D'Amore 			b->navail += n;
38801335b0dSGarrett D'Amore 			b->prod += n;
38901335b0dSGarrett D'Amore 			want -= n;
39001335b0dSGarrett D'Amore 			zero -= n;
39101335b0dSGarrett D'Amore 		}
39201335b0dSGarrett D'Amore 
39301335b0dSGarrett D'Amore 		while (zero) {
39401335b0dSGarrett D'Amore 			b->data[b->prod & b->mask] = 0;
39501335b0dSGarrett D'Amore 			b->prod++;
39601335b0dSGarrett D'Amore 			b->prod &= b->mask;
39701335b0dSGarrett D'Amore 			zero--;
39801335b0dSGarrett D'Amore 		}
39901335b0dSGarrett D'Amore 	}
40001335b0dSGarrett D'Amore }
40101335b0dSGarrett D'Amore 
40201335b0dSGarrett D'Amore #define	STR1	"C1"
40301335b0dSGarrett D'Amore #define	STR2	"S2"
40401335b0dSGarrett D'Amore #ifdef	_LP64
40501335b0dSGarrett D'Amore #define	STR8	"L8"
40601335b0dSGarrett D'Amore #define	STR4	"I4"
40701335b0dSGarrett D'Amore #else
40801335b0dSGarrett D'Amore #define	STR8	"8"
40901335b0dSGarrett D'Amore #define	STR4	"IL4"
41001335b0dSGarrett D'Amore #endif
41101335b0dSGarrett D'Amore 
41201335b0dSGarrett D'Amore static void
41301335b0dSGarrett D'Amore do_type_string(char *typestr)
41401335b0dSGarrett D'Amore {
41501335b0dSGarrett D'Amore 	if (*typestr == 0) {
41601335b0dSGarrett D'Amore 		errx(1, _("missing type string"));
41701335b0dSGarrett D'Amore 	}
41801335b0dSGarrett D'Amore 	while (*typestr) {
41901335b0dSGarrett D'Amore 		switch (*typestr) {
42001335b0dSGarrett D'Amore 		case 'a':
42101335b0dSGarrett D'Amore 			typestr++;
42201335b0dSGarrett D'Amore 			add_out(&output_ascii);
42301335b0dSGarrett D'Amore 			break;
42401335b0dSGarrett D'Amore 		case 'c':
42501335b0dSGarrett D'Amore 			add_out(&output_char);
42601335b0dSGarrett D'Amore 			typestr++;
42701335b0dSGarrett D'Amore 			break;
42801335b0dSGarrett D'Amore 		case 'f':
42901335b0dSGarrett D'Amore 			typestr++;
43001335b0dSGarrett D'Amore 			switch (*typestr) {
43101335b0dSGarrett D'Amore 			case 'F':
43201335b0dSGarrett D'Amore 			case '4':
43301335b0dSGarrett D'Amore 				add_out(&output_float);
43401335b0dSGarrett D'Amore 				typestr++;
43501335b0dSGarrett D'Amore 				break;
43601335b0dSGarrett D'Amore 			case '8':
43701335b0dSGarrett D'Amore 			case 'D':
43801335b0dSGarrett D'Amore 				add_out(&output_double);
43901335b0dSGarrett D'Amore 				typestr++;
44001335b0dSGarrett D'Amore 				break;
44101335b0dSGarrett D'Amore 			case 'L':
44201335b0dSGarrett D'Amore 				add_out(&output_ldouble);
44301335b0dSGarrett D'Amore 				typestr++;
44401335b0dSGarrett D'Amore 				break;
44501335b0dSGarrett D'Amore 			default:
44601335b0dSGarrett D'Amore 				add_out(&output_float);
44701335b0dSGarrett D'Amore 				break;
44801335b0dSGarrett D'Amore 			}
44901335b0dSGarrett D'Amore 			break;
45001335b0dSGarrett D'Amore 
45101335b0dSGarrett D'Amore 
45201335b0dSGarrett D'Amore 		case 'd':
45301335b0dSGarrett D'Amore 			typestr++;
45401335b0dSGarrett D'Amore 			if (strchr(STR1, *typestr)) {
45501335b0dSGarrett D'Amore 				typestr++;
45601335b0dSGarrett D'Amore 				add_out(&output_sig_b);
45701335b0dSGarrett D'Amore 			} else if (strchr(STR2, *typestr)) {
45801335b0dSGarrett D'Amore 				typestr++;
45901335b0dSGarrett D'Amore 				add_out(&output_sig_w);
46001335b0dSGarrett D'Amore 			} else if (strchr(STR4, *typestr)) {
46101335b0dSGarrett D'Amore 				typestr++;
46201335b0dSGarrett D'Amore 				add_out(&output_sig_d);
46301335b0dSGarrett D'Amore 			} else if (strchr(STR8, *typestr)) {
46401335b0dSGarrett D'Amore 				typestr++;
46501335b0dSGarrett D'Amore 				add_out(&output_sig_q);
46601335b0dSGarrett D'Amore 			} else {
46701335b0dSGarrett D'Amore 				add_out(&output_sig_d);
46801335b0dSGarrett D'Amore 			}
46901335b0dSGarrett D'Amore 			break;
47001335b0dSGarrett D'Amore 
47101335b0dSGarrett D'Amore 		case 'u':
47201335b0dSGarrett D'Amore 			typestr++;
47301335b0dSGarrett D'Amore 			if (strchr(STR1, *typestr)) {
47401335b0dSGarrett D'Amore 				typestr++;
47501335b0dSGarrett D'Amore 				add_out(&output_dec_b);
47601335b0dSGarrett D'Amore 			} else if (strchr(STR2, *typestr)) {
47701335b0dSGarrett D'Amore 				typestr++;
47801335b0dSGarrett D'Amore 				add_out(&output_dec_w);
47901335b0dSGarrett D'Amore 			} else if (strchr(STR4, *typestr)) {
48001335b0dSGarrett D'Amore 				typestr++;
48101335b0dSGarrett D'Amore 				add_out(&output_dec_d);
48201335b0dSGarrett D'Amore 			} else if (strchr(STR8, *typestr)) {
48301335b0dSGarrett D'Amore 				typestr++;
48401335b0dSGarrett D'Amore 				add_out(&output_dec_q);
48501335b0dSGarrett D'Amore 			} else {
48601335b0dSGarrett D'Amore 				add_out(&output_dec_d);
48701335b0dSGarrett D'Amore 			}
48801335b0dSGarrett D'Amore 			break;
48901335b0dSGarrett D'Amore 
49001335b0dSGarrett D'Amore 		case 'o':
49101335b0dSGarrett D'Amore 			typestr++;
49201335b0dSGarrett D'Amore 			if (strchr(STR1, *typestr)) {
49301335b0dSGarrett D'Amore 				typestr++;
49401335b0dSGarrett D'Amore 				add_out(&output_oct_b);
49501335b0dSGarrett D'Amore 			} else if (strchr(STR2, *typestr)) {
49601335b0dSGarrett D'Amore 				typestr++;
49701335b0dSGarrett D'Amore 				add_out(&output_oct_w);
49801335b0dSGarrett D'Amore 			} else if (strchr(STR4, *typestr)) {
49901335b0dSGarrett D'Amore 				typestr++;
50001335b0dSGarrett D'Amore 				add_out(&output_oct_d);
50101335b0dSGarrett D'Amore 			} else if (strchr(STR8, *typestr)) {
50201335b0dSGarrett D'Amore 				typestr++;
50301335b0dSGarrett D'Amore 				add_out(&output_oct_q);
50401335b0dSGarrett D'Amore 			} else {
50501335b0dSGarrett D'Amore 				add_out(&output_oct_d);
50601335b0dSGarrett D'Amore 			}
50701335b0dSGarrett D'Amore 			break;
50801335b0dSGarrett D'Amore 
50901335b0dSGarrett D'Amore 		case 'x':
51001335b0dSGarrett D'Amore 			typestr++;
51101335b0dSGarrett D'Amore 			if (strchr(STR1, *typestr)) {
51201335b0dSGarrett D'Amore 				typestr++;
51301335b0dSGarrett D'Amore 				add_out(&output_hex_b);
51401335b0dSGarrett D'Amore 			} else if (strchr(STR2, *typestr)) {
51501335b0dSGarrett D'Amore 				typestr++;
51601335b0dSGarrett D'Amore 				add_out(&output_hex_w);
51701335b0dSGarrett D'Amore 			} else if (strchr(STR4, *typestr)) {
51801335b0dSGarrett D'Amore 				typestr++;
51901335b0dSGarrett D'Amore 				add_out(&output_hex_d);
52001335b0dSGarrett D'Amore 			} else if (strchr(STR8, *typestr)) {
52101335b0dSGarrett D'Amore 				typestr++;
52201335b0dSGarrett D'Amore 				add_out(&output_hex_q);
52301335b0dSGarrett D'Amore 			} else {
52401335b0dSGarrett D'Amore 				add_out(&output_hex_d);
52501335b0dSGarrett D'Amore 			}
52601335b0dSGarrett D'Amore 			break;
52701335b0dSGarrett D'Amore 
52801335b0dSGarrett D'Amore 		default:
52901335b0dSGarrett D'Amore 			errx(1, _("unrecognized type string character: %c"),
53001335b0dSGarrett D'Amore 			    *typestr);
53101335b0dSGarrett D'Amore 			exit(1);
53201335b0dSGarrett D'Amore 		}
53301335b0dSGarrett D'Amore 	}
53401335b0dSGarrett D'Amore }
53501335b0dSGarrett D'Amore 
53601335b0dSGarrett D'Amore int
53701335b0dSGarrett D'Amore main(int argc, char **argv)
53801335b0dSGarrett D'Amore {
53901335b0dSGarrett D'Amore 	int		c;
54001335b0dSGarrett D'Amore 	int		i;
54101335b0dSGarrett D'Amore 	buffer_t	buffer;
54201335b0dSGarrett D'Amore 	boolean_t	first = B_TRUE;
54301335b0dSGarrett D'Amore 	boolean_t	doall = B_FALSE;
54401335b0dSGarrett D'Amore 	boolean_t	same = B_FALSE;
54501335b0dSGarrett D'Amore 	boolean_t	newarg = B_FALSE;
54601335b0dSGarrett D'Amore 	off_t		offset = 0;
54701335b0dSGarrett D'Amore 	off_t		skip = 0;
54801335b0dSGarrett D'Amore 	char		*eptr;
54901335b0dSGarrett D'Amore 	char		*offstr = 0;
55001335b0dSGarrett D'Amore 
55101335b0dSGarrett D'Amore 	input = stdin;
55201335b0dSGarrett D'Amore 
55301335b0dSGarrett D'Amore 	(void) setlocale(LC_ALL, "");
55401335b0dSGarrett D'Amore 
55501335b0dSGarrett D'Amore 	while ((c = getopt(argc, argv, "A:bCcdDfFj:N:oOsSxXvt:")) != EOF) {
55601335b0dSGarrett D'Amore 		switch (c) {
55701335b0dSGarrett D'Amore 		case 'A':
55801335b0dSGarrett D'Amore 			newarg = B_TRUE;
55901335b0dSGarrett D'Amore 			if (strlen(optarg) > 1) {
56001335b0dSGarrett D'Amore 				afmt = NULL;
56101335b0dSGarrett D'Amore 			}
56201335b0dSGarrett D'Amore 			switch (*optarg) {
56301335b0dSGarrett D'Amore 			case 'o':
56401335b0dSGarrett D'Amore 				afmt = "%07llo";
56501335b0dSGarrett D'Amore 				cfmt = "       ";
56601335b0dSGarrett D'Amore 				break;
56701335b0dSGarrett D'Amore 			case 'd':
56801335b0dSGarrett D'Amore 				afmt = "%07lld";
56901335b0dSGarrett D'Amore 				cfmt = "       ";
57001335b0dSGarrett D'Amore 				break;
57101335b0dSGarrett D'Amore 			case 'x':
57201335b0dSGarrett D'Amore 				afmt = "%07llx";
57301335b0dSGarrett D'Amore 				cfmt = "       ";
57401335b0dSGarrett D'Amore 				break;
57501335b0dSGarrett D'Amore 			case 'n':
57601335b0dSGarrett D'Amore 				/*
57701335b0dSGarrett D'Amore 				 * You could argue that the code should
57801335b0dSGarrett D'Amore 				 * use the same 7 spaces.  Legacy uses 8
57901335b0dSGarrett D'Amore 				 * though.  Oh well.  Better to avoid
58001335b0dSGarrett D'Amore 				 * gratuitous change.
58101335b0dSGarrett D'Amore 				 */
58201335b0dSGarrett D'Amore 				afmt = "        ";
58301335b0dSGarrett D'Amore 				cfmt = "        ";
58401335b0dSGarrett D'Amore 				break;
58501335b0dSGarrett D'Amore 			default:
58601335b0dSGarrett D'Amore 				afmt = NULL;
58701335b0dSGarrett D'Amore 				break;
58801335b0dSGarrett D'Amore 			}
58901335b0dSGarrett D'Amore 			if (strlen(optarg) != 1) {
59001335b0dSGarrett D'Amore 				afmt = NULL;
59101335b0dSGarrett D'Amore 			}
59201335b0dSGarrett D'Amore 			if (afmt == NULL)
59301335b0dSGarrett D'Amore 				warnx(_("invalid address base, "
59401335b0dSGarrett D'Amore 				    "must be o, d, x, or n"));
59501335b0dSGarrett D'Amore 			break;
59601335b0dSGarrett D'Amore 
59701335b0dSGarrett D'Amore 		case 'b':
59801335b0dSGarrett D'Amore 			add_out(&output_oct_b);
59901335b0dSGarrett D'Amore 			break;
60001335b0dSGarrett D'Amore 
60101335b0dSGarrett D'Amore 		case 'c':
60201335b0dSGarrett D'Amore 		case 'C':
60301335b0dSGarrett D'Amore 			add_out(&output_char);
60401335b0dSGarrett D'Amore 			break;
60501335b0dSGarrett D'Amore 
60601335b0dSGarrett D'Amore 		case 'f':
60701335b0dSGarrett D'Amore 			add_out(&output_float);
60801335b0dSGarrett D'Amore 			break;
60901335b0dSGarrett D'Amore 
61001335b0dSGarrett D'Amore 		case 'F':
61101335b0dSGarrett D'Amore 			add_out(&output_double);
61201335b0dSGarrett D'Amore 			break;
61301335b0dSGarrett D'Amore 
61401335b0dSGarrett D'Amore 		case 'd':
61501335b0dSGarrett D'Amore 			add_out(&output_dec_w);
61601335b0dSGarrett D'Amore 			break;
61701335b0dSGarrett D'Amore 
61801335b0dSGarrett D'Amore 		case 'D':
61901335b0dSGarrett D'Amore 			add_out(&output_dec_d);
62001335b0dSGarrett D'Amore 			break;
62101335b0dSGarrett D'Amore 
62201335b0dSGarrett D'Amore 		case 't':
62301335b0dSGarrett D'Amore 			newarg = B_TRUE;
62401335b0dSGarrett D'Amore 			do_type_string(optarg);
62501335b0dSGarrett D'Amore 			break;
62601335b0dSGarrett D'Amore 
62701335b0dSGarrett D'Amore 		case 'o':
62801335b0dSGarrett D'Amore 			add_out(&output_oct_w);
62901335b0dSGarrett D'Amore 			break;
63001335b0dSGarrett D'Amore 
63101335b0dSGarrett D'Amore 		case 'O':
63201335b0dSGarrett D'Amore 			add_out(&output_oct_d);
63301335b0dSGarrett D'Amore 			break;
63401335b0dSGarrett D'Amore 
63501335b0dSGarrett D'Amore 		case 's':
63601335b0dSGarrett D'Amore 			add_out(&output_sig_w);
63701335b0dSGarrett D'Amore 			break;
63801335b0dSGarrett D'Amore 
63901335b0dSGarrett D'Amore 		case 'S':
64001335b0dSGarrett D'Amore 			add_out(&output_sig_d);
64101335b0dSGarrett D'Amore 			break;
64201335b0dSGarrett D'Amore 
64301335b0dSGarrett D'Amore 		case 'x':
64401335b0dSGarrett D'Amore 			add_out(&output_hex_w);
64501335b0dSGarrett D'Amore 			break;
64601335b0dSGarrett D'Amore 
64701335b0dSGarrett D'Amore 		case 'X':
64801335b0dSGarrett D'Amore 			add_out(&output_hex_d);
64901335b0dSGarrett D'Amore 			break;
65001335b0dSGarrett D'Amore 
65101335b0dSGarrett D'Amore 		case 'v':
65201335b0dSGarrett D'Amore 			doall = B_TRUE;
65301335b0dSGarrett D'Amore 			break;
65401335b0dSGarrett D'Amore 
65501335b0dSGarrett D'Amore 		case 'j':
65601335b0dSGarrett D'Amore 			newarg = B_TRUE;
65701335b0dSGarrett D'Amore 			skip = strtoll(optarg, &eptr, 0);
65801335b0dSGarrett D'Amore 			if (*eptr == 'b') {
65901335b0dSGarrett D'Amore 				skip <<= 9;	/* 512 bytes */
66001335b0dSGarrett D'Amore 				eptr++;
66101335b0dSGarrett D'Amore 			} else if (*eptr == 'k') {
66201335b0dSGarrett D'Amore 				skip <<= 10;	/* 1k */
66301335b0dSGarrett D'Amore 				eptr++;
66401335b0dSGarrett D'Amore 			} else if (*eptr == 'm') {
66501335b0dSGarrett D'Amore 				skip <<= 20;	/* 1m */
66601335b0dSGarrett D'Amore 				eptr++;
66701335b0dSGarrett D'Amore 			} else if (*eptr == 'g') {
66801335b0dSGarrett D'Amore 				skip <<= 30;	/* 1g */
66901335b0dSGarrett D'Amore 				eptr++;
67001335b0dSGarrett D'Amore 			}
67101335b0dSGarrett D'Amore 			if ((skip < 0) || (eptr[0] != 0)) {
67201335b0dSGarrett D'Amore 				warnx(_("invalid skip count '%s' specified"),
67301335b0dSGarrett D'Amore 				    optarg);
67401335b0dSGarrett D'Amore 				exit(1);
67501335b0dSGarrett D'Amore 			}
67601335b0dSGarrett D'Amore 			break;
67701335b0dSGarrett D'Amore 
67801335b0dSGarrett D'Amore 		case 'N':
67901335b0dSGarrett D'Amore 			newarg = B_TRUE;
68001335b0dSGarrett D'Amore 			limit = strtoll(optarg, &eptr, 0);
68101335b0dSGarrett D'Amore 			/*
68201335b0dSGarrett D'Amore 			 * POSIX doesn't specify this, but I think these
68301335b0dSGarrett D'Amore 			 * may be helpful.
68401335b0dSGarrett D'Amore 			 */
68501335b0dSGarrett D'Amore 			if (*eptr == 'b') {
68601335b0dSGarrett D'Amore 				limit <<= 9;
68701335b0dSGarrett D'Amore 				eptr++;
68801335b0dSGarrett D'Amore 			} else if (*eptr == 'k') {
68901335b0dSGarrett D'Amore 				limit <<= 10;
69001335b0dSGarrett D'Amore 				eptr++;
69101335b0dSGarrett D'Amore 			} else if (*eptr == 'm') {
69201335b0dSGarrett D'Amore 				limit <<= 20;
69301335b0dSGarrett D'Amore 				eptr++;
69401335b0dSGarrett D'Amore 			} else if (*eptr == 'g') {
69501335b0dSGarrett D'Amore 				limit <<= 30;
69601335b0dSGarrett D'Amore 				eptr++;
69701335b0dSGarrett D'Amore 			}
69801335b0dSGarrett D'Amore 			if ((limit < 0) || (eptr[0] != 0)) {
69901335b0dSGarrett D'Amore 				warnx(_("invalid byte count '%s' specified"),
70001335b0dSGarrett D'Amore 				    optarg);
70101335b0dSGarrett D'Amore 				exit(1);
70201335b0dSGarrett D'Amore 			}
70301335b0dSGarrett D'Amore 			break;
70401335b0dSGarrett D'Amore 
70501335b0dSGarrett D'Amore 		default:
70601335b0dSGarrett D'Amore 			usage();
70701335b0dSGarrett D'Amore 			break;
70801335b0dSGarrett D'Amore 		}
70901335b0dSGarrett D'Amore 	}
71001335b0dSGarrett D'Amore 
71101335b0dSGarrett D'Amore 	/* this finds the smallest power of two size we can use */
71201335b0dSGarrett D'Amore 	buffer.mask = (1 << (ffs(blocksize * 3) + 1)) - 1;
71301335b0dSGarrett D'Amore 	buffer.data = memalign(16, buffer.mask + 1);
71401335b0dSGarrett D'Amore 	if (buffer.data == NULL) {
71501335b0dSGarrett D'Amore 		err(1, "memalign");
71601335b0dSGarrett D'Amore 	}
71701335b0dSGarrett D'Amore 
71801335b0dSGarrett D'Amore 
71901335b0dSGarrett D'Amore 	/*
72001335b0dSGarrett D'Amore 	 * Wow.  This option parsing is hideous.
72101335b0dSGarrett D'Amore 	 *
72201335b0dSGarrett D'Amore 	 * If the we've not seen a new option, and there is just one
72301335b0dSGarrett D'Amore 	 * operand, if it starts with a "+", then treat it as an
72401335b0dSGarrett D'Amore 	 * offset.  Otherwise if two operands, and the second operand
72501335b0dSGarrett D'Amore 	 * starts with + or a digit, then it is an offset.
72601335b0dSGarrett D'Amore 	 */
72701335b0dSGarrett D'Amore 	if (!newarg) {
72801335b0dSGarrett D'Amore 		if (((argc - optind) == 1) && (argv[optind][0] == '+')) {
72901335b0dSGarrett D'Amore 			offstr = argv[optind];
73001335b0dSGarrett D'Amore 			argc--;
73101335b0dSGarrett D'Amore 		} else if (((argc - optind) == 2) &&
73201335b0dSGarrett D'Amore 		    (strchr("+0123456789", (argv[optind + 1][0])) != NULL)) {
73301335b0dSGarrett D'Amore 			offstr = argv[optind + 1];
73401335b0dSGarrett D'Amore 			argc--;
73501335b0dSGarrett D'Amore 		}
73601335b0dSGarrett D'Amore 	}
73701335b0dSGarrett D'Amore 	if (offstr) {
73801335b0dSGarrett D'Amore 		int base = 0;
73901335b0dSGarrett D'Amore 		int mult = 1;
74001335b0dSGarrett D'Amore 		int l;
74101335b0dSGarrett D'Amore 		if (*offstr == '+') {
74201335b0dSGarrett D'Amore 			offstr++;
74301335b0dSGarrett D'Amore 		}
74401335b0dSGarrett D'Amore 		l = strlen(offstr);
74501335b0dSGarrett D'Amore 		if ((strncmp(offstr, "0x", 2) == 0)) {
74601335b0dSGarrett D'Amore 			afmt = "%07llx";
74701335b0dSGarrett D'Amore 			base = 16;
74801335b0dSGarrett D'Amore 			offstr += 2;
74901335b0dSGarrett D'Amore 			if (offstr[l - 1] == 'B') {
75001335b0dSGarrett D'Amore 				offstr[l - 1] = 0;
75101335b0dSGarrett D'Amore 				l--;
75201335b0dSGarrett D'Amore 				mult = 512;
75301335b0dSGarrett D'Amore 			}
75401335b0dSGarrett D'Amore 		} else {
75501335b0dSGarrett D'Amore 			base = 8;
75601335b0dSGarrett D'Amore 			afmt = "%07llo";
75701335b0dSGarrett D'Amore 			if ((offstr[l - 1] == 'B') || (offstr[l - 1] == 'b')) {
75801335b0dSGarrett D'Amore 				offstr[l - 1] = 0;
75901335b0dSGarrett D'Amore 				l--;
76001335b0dSGarrett D'Amore 				mult = 512;
76101335b0dSGarrett D'Amore 			}
76201335b0dSGarrett D'Amore 			if (offstr[l - 1] == '.') {
76301335b0dSGarrett D'Amore 				offstr[l - 1] = 0;
76401335b0dSGarrett D'Amore 				base = 10;
76501335b0dSGarrett D'Amore 				afmt = "%07lld";
76601335b0dSGarrett D'Amore 			}
76701335b0dSGarrett D'Amore 		}
76801335b0dSGarrett D'Amore 		skip = strtoll(offstr, &eptr, base);
76901335b0dSGarrett D'Amore 		if (*eptr != '\0') {
77001335b0dSGarrett D'Amore 			errx(1, _("invalid offset string specified"));
77101335b0dSGarrett D'Amore 		}
77201335b0dSGarrett D'Amore 		skip *= mult;
77301335b0dSGarrett D'Amore 		offset += skip;
77401335b0dSGarrett D'Amore 	}
77501335b0dSGarrett D'Amore 
77601335b0dSGarrett D'Amore 	/*
77701335b0dSGarrett D'Amore 	 * Allocate an array for all the input files.
77801335b0dSGarrett D'Amore 	 */
77901335b0dSGarrett D'Amore 	if (argc > optind) {
78001335b0dSGarrett D'Amore 		files = calloc(sizeof (char *), argc - optind);
78101335b0dSGarrett D'Amore 		for (i = 0; i < argc - optind; i++) {
78201335b0dSGarrett D'Amore 			files[i] = argv[optind + i];
78301335b0dSGarrett D'Amore 			numfiles++;
78401335b0dSGarrett D'Amore 		}
78501335b0dSGarrett D'Amore 		input = next_input();
78601335b0dSGarrett D'Amore 	} else {
78701335b0dSGarrett D'Amore 		input = stdin;
78801335b0dSGarrett D'Amore 	}
78901335b0dSGarrett D'Amore 
79001335b0dSGarrett D'Amore 	/*
79101335b0dSGarrett D'Amore 	 * We need to seek ahead.  fseek would be faster.
79201335b0dSGarrett D'Amore 	 */
79301335b0dSGarrett D'Amore 	while (skip && (input != NULL)) {
79401335b0dSGarrett D'Amore 		struct stat sbuf;
79501335b0dSGarrett D'Amore 
79601335b0dSGarrett D'Amore 		/*
79701335b0dSGarrett D'Amore 		 * Only fseek() on regular files.  (Others
79801335b0dSGarrett D'Amore 		 * we have to read().
79901335b0dSGarrett D'Amore 		 */
80001335b0dSGarrett D'Amore 		if (fstat(fileno(input), &sbuf) < 0) {
80101335b0dSGarrett D'Amore 			warn("fstat: %s", files[curfile-1]);
80201335b0dSGarrett D'Amore 			input = next_input();
80301335b0dSGarrett D'Amore 			continue;
80401335b0dSGarrett D'Amore 		}
80501335b0dSGarrett D'Amore 		if (S_ISREG(sbuf.st_mode)) {
80601335b0dSGarrett D'Amore 			/*
80701335b0dSGarrett D'Amore 			 * No point in seeking a file that is too
80801335b0dSGarrett D'Amore 			 * short to begin with.
80901335b0dSGarrett D'Amore 			 */
81001335b0dSGarrett D'Amore 			if (sbuf.st_size < skip) {
81101335b0dSGarrett D'Amore 				skip -= sbuf.st_size;
81201335b0dSGarrett D'Amore 				input = next_input();
81301335b0dSGarrett D'Amore 				continue;
81401335b0dSGarrett D'Amore 			}
81501335b0dSGarrett D'Amore 			if (fseeko(input, skip, SEEK_SET) < 0) {
81601335b0dSGarrett D'Amore 				err(1, "fseek:%s", files[curfile-1]);
81701335b0dSGarrett D'Amore 			}
81801335b0dSGarrett D'Amore 			/* Done seeking. */
81901335b0dSGarrett D'Amore 			skip = 0;
82001335b0dSGarrett D'Amore 			break;
82101335b0dSGarrett D'Amore 		}
82201335b0dSGarrett D'Amore 
82301335b0dSGarrett D'Amore 		/*
82401335b0dSGarrett D'Amore 		 * fgetc seems like it would be slow, but it uses
82501335b0dSGarrett D'Amore 		 * buffered I/O, so it should be fast enough.
82601335b0dSGarrett D'Amore 		 */
82701335b0dSGarrett D'Amore 		flockfile(input);
82801335b0dSGarrett D'Amore 		while (skip) {
82901335b0dSGarrett D'Amore 			if (getc_unlocked(input) == EOF) {
83001335b0dSGarrett D'Amore 				funlockfile(input);
83101335b0dSGarrett D'Amore 				if (ferror(input)) {
83201335b0dSGarrett D'Amore 					warn("read: %s", files[curfile-1]);
83301335b0dSGarrett D'Amore 				}
83401335b0dSGarrett D'Amore 				input = next_input();
83501335b0dSGarrett D'Amore 				if (input != NULL) {
83601335b0dSGarrett D'Amore 					flockfile(input);
83701335b0dSGarrett D'Amore 				}
83801335b0dSGarrett D'Amore 				break;
83901335b0dSGarrett D'Amore 			}
84001335b0dSGarrett D'Amore 			skip--;
84101335b0dSGarrett D'Amore 		}
84201335b0dSGarrett D'Amore 		if (input != NULL)
84301335b0dSGarrett D'Amore 			funlockfile(input);
84401335b0dSGarrett D'Amore 	}
84501335b0dSGarrett D'Amore 
84601335b0dSGarrett D'Amore 	if (head == NULL) {
84701335b0dSGarrett D'Amore 		add_out(&output_oct_w);
84801335b0dSGarrett D'Amore 	}
84901335b0dSGarrett D'Amore 
85001335b0dSGarrett D'Amore 	buffer.navail = 0;
85101335b0dSGarrett D'Amore 	buffer.prod = 0;
85201335b0dSGarrett D'Amore 	buffer.cons = 0;
85301335b0dSGarrett D'Amore 
85401335b0dSGarrett D'Amore 	for (refill(&buffer); buffer.navail > 0; refill(&buffer)) {
85501335b0dSGarrett D'Amore 		output_t *out;
85601335b0dSGarrett D'Amore 		int	mx;
85701335b0dSGarrett D'Amore 		int	j, k;
85801335b0dSGarrett D'Amore 
85901335b0dSGarrett D'Amore 		/*
86001335b0dSGarrett D'Amore 		 * If this buffer was the same as last, then just
86101335b0dSGarrett D'Amore 		 * dump an asterisk.
86201335b0dSGarrett D'Amore 		 */
86301335b0dSGarrett D'Amore 		if ((!first) && (buffer.navail >= blocksize) && (!doall)) {
86401335b0dSGarrett D'Amore 			j = buffer.cons;
86501335b0dSGarrett D'Amore 			k = j - blocksize;
86601335b0dSGarrett D'Amore 			for (i = 0; i < blocksize; i++) {
86701335b0dSGarrett D'Amore 				if (buffer.data[j & buffer.mask] !=
86801335b0dSGarrett D'Amore 				    buffer.data[k & buffer.mask]) {
86901335b0dSGarrett D'Amore 					break;
87001335b0dSGarrett D'Amore 				}
87101335b0dSGarrett D'Amore 				j++;
87201335b0dSGarrett D'Amore 				k++;
87301335b0dSGarrett D'Amore 			}
87401335b0dSGarrett D'Amore 			if (i == blocksize) {
87501335b0dSGarrett D'Amore 				if (!same) {
87601335b0dSGarrett D'Amore 					(void) fputs("*\n", stdout);
87701335b0dSGarrett D'Amore 					same = B_TRUE;
87801335b0dSGarrett D'Amore 				}
87901335b0dSGarrett D'Amore 				buffer.navail -= blocksize;
88001335b0dSGarrett D'Amore 				offset += blocksize;
88101335b0dSGarrett D'Amore 				buffer.cons += blocksize;
88201335b0dSGarrett D'Amore 				buffer.cons &= buffer.mask;
88301335b0dSGarrett D'Amore 				continue;
88401335b0dSGarrett D'Amore 			}
88501335b0dSGarrett D'Amore 		}
88601335b0dSGarrett D'Amore 
88701335b0dSGarrett D'Amore 		first = B_FALSE;
88801335b0dSGarrett D'Amore 		same = B_FALSE;
88901335b0dSGarrett D'Amore 		mx = (buffer.navail > blocksize) ? blocksize : buffer.navail;
89001335b0dSGarrett D'Amore 
89101335b0dSGarrett D'Amore 		for (out = head; out != NULL; out = out->next) {
89201335b0dSGarrett D'Amore 
89301335b0dSGarrett D'Amore 			if (out == head) {
89401335b0dSGarrett D'Amore 				/*LINTED E_SEC_PRINTF_VAR_FMT*/
89501335b0dSGarrett D'Amore 				(void) printf(afmt, offset);
89601335b0dSGarrett D'Amore 			} else {
89701335b0dSGarrett D'Amore 				(void) fputs(cfmt, stdout);
89801335b0dSGarrett D'Amore 			}
89901335b0dSGarrett D'Amore 			for (i = 0, j = buffer.cons; i < mx; i += out->width) {
90001335b0dSGarrett D'Amore 				out->func(&buffer, j);
90101335b0dSGarrett D'Amore 				j += out->width;
90201335b0dSGarrett D'Amore 				j &= buffer.mask;
90301335b0dSGarrett D'Amore 			}
90401335b0dSGarrett D'Amore 			(void) fputs("\n", stdout);
90501335b0dSGarrett D'Amore 		}
90601335b0dSGarrett D'Amore 		buffer.cons += mx;
90701335b0dSGarrett D'Amore 		buffer.cons &= buffer.mask;
90801335b0dSGarrett D'Amore 		offset += mx;
90901335b0dSGarrett D'Amore 		buffer.navail -= mx;
91001335b0dSGarrett D'Amore 	}
91101335b0dSGarrett D'Amore 	/*LINTED E_SEC_PRINTF_VAR_FMT*/
91201335b0dSGarrett D'Amore 	(void) printf(afmt, offset);
91301335b0dSGarrett D'Amore 	(void) fputs("\n", stdout);
91401335b0dSGarrett D'Amore 	return (0);
91501335b0dSGarrett D'Amore }
916