xref: /titanic_53/usr/src/cmd/od/od.c (revision 01335b0d1c4e0c0f16325a830b24ea2a4076fd38)
1*01335b0dSGarrett D'Amore /*
2*01335b0dSGarrett D'Amore  * This file and its contents are supplied under the terms of the
3*01335b0dSGarrett D'Amore  * Common Development and Distribution License ("CDDL"), version 1.0.
4*01335b0dSGarrett D'Amore  * You may only use this file in accordance with the terms version
5*01335b0dSGarrett D'Amore  * 1.0 of the CDDL.
6*01335b0dSGarrett D'Amore  *
7*01335b0dSGarrett D'Amore  * A full copy of the text of the CDDL should have accompanied this
8*01335b0dSGarrett D'Amore  * source.  A copy of the CDDL is also available via the Internet
9*01335b0dSGarrett D'Amore  * http://www.illumos.org/license/CDDL.
10*01335b0dSGarrett D'Amore  */
11*01335b0dSGarrett D'Amore 
12*01335b0dSGarrett D'Amore /*
13*01335b0dSGarrett D'Amore  * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
14*01335b0dSGarrett D'Amore  */
15*01335b0dSGarrett D'Amore 
16*01335b0dSGarrett D'Amore /*
17*01335b0dSGarrett D'Amore  * od - octal dump.  Not really just octal anymore; read the POSIX
18*01335b0dSGarrett D'Amore  * specification for it -- its more complex than you think!
19*01335b0dSGarrett D'Amore  *
20*01335b0dSGarrett D'Amore  * NB: We followed the POSIX semantics fairly strictly, where the
21*01335b0dSGarrett D'Amore  * legacy code's behavior was in conflict.  In many cases the legacy
22*01335b0dSGarrett D'Amore  * Solaris code was so completely broken as to be completely unusable.
23*01335b0dSGarrett D'Amore  * (For example, the long double support was broken beyond
24*01335b0dSGarrett D'Amore  * imagination!)  Note that GNU coreutils violates POSIX in a few
25*01335b0dSGarrett D'Amore  * interesting ways, such as changing the numbering of the addresses
26*01335b0dSGarrett D'Amore  * when skipping.  (Address starts should always be at 0, according to
27*01335b0dSGarrett D'Amore  * the sample output in the Open Group man page.)
28*01335b0dSGarrett D'Amore  */
29*01335b0dSGarrett D'Amore 
30*01335b0dSGarrett D'Amore #include <stdio.h>
31*01335b0dSGarrett D'Amore #include <stdlib.h>
32*01335b0dSGarrett D'Amore #include <sys/types.h>
33*01335b0dSGarrett D'Amore #include <string.h>
34*01335b0dSGarrett D'Amore #include <err.h>
35*01335b0dSGarrett D'Amore #include <wchar.h>
36*01335b0dSGarrett D'Amore #include <locale.h>
37*01335b0dSGarrett D'Amore #include <unistd.h>
38*01335b0dSGarrett D'Amore #include <sys/stat.h>
39*01335b0dSGarrett D'Amore 
40*01335b0dSGarrett D'Amore #define	_(x)	gettext(x)
41*01335b0dSGarrett D'Amore 
42*01335b0dSGarrett D'Amore /* address format */
43*01335b0dSGarrett D'Amore static char *afmt  =	"%07llo";
44*01335b0dSGarrett D'Amore static char *cfmt  =    "       ";
45*01335b0dSGarrett D'Amore 
46*01335b0dSGarrett D'Amore static FILE *input = NULL;
47*01335b0dSGarrett D'Amore static size_t lcm = 1;
48*01335b0dSGarrett D'Amore static size_t blocksize = 16;
49*01335b0dSGarrett D'Amore static int numfiles = 0;
50*01335b0dSGarrett D'Amore static int curfile = 0;
51*01335b0dSGarrett D'Amore static char **files = NULL;
52*01335b0dSGarrett D'Amore static off_t limit = -1;
53*01335b0dSGarrett D'Amore 
54*01335b0dSGarrett D'Amore /*
55*01335b0dSGarrett D'Amore  * This structure describes our ring buffer.  Its always a power of 2
56*01335b0dSGarrett D'Amore  * in size to make wrap around calculations fast using a mask instead
57*01335b0dSGarrett D'Amore  * of doing modulo.
58*01335b0dSGarrett D'Amore  *
59*01335b0dSGarrett D'Amore  * The size is calculated thusly: We need three "blocks" of data, as
60*01335b0dSGarrett D'Amore  * we process a block at a time (one block == one line of od output.)
61*01335b0dSGarrett D'Amore  *
62*01335b0dSGarrett D'Amore  * We need lookahead of an extra block to support multibyte chars.  We
63*01335b0dSGarrett D'Amore  * also have a look behind so that we can avoid printing lines that
64*01335b0dSGarrett D'Amore  * are identical to what we've already printed.  Finally, we need the
65*01335b0dSGarrett D'Amore  * current block.
66*01335b0dSGarrett D'Amore  *
67*01335b0dSGarrett D'Amore  * The block size is determined by the least common multiple of the
68*01335b0dSGarrett D'Amore  * data items being displayed.  Usually it will be 16, but sometimes
69*01335b0dSGarrett D'Amore  * it is 24 (when 12-byte long doubles are presented.)
70*01335b0dSGarrett D'Amore  *
71*01335b0dSGarrett D'Amore  * The data buffer is allocaed via memalign to make sure it is
72*01335b0dSGarrett D'Amore  * properly aligned.
73*01335b0dSGarrett D'Amore  */
74*01335b0dSGarrett D'Amore typedef struct buffer {
75*01335b0dSGarrett D'Amore 	char	*data;		/* data buffer */
76*01335b0dSGarrett D'Amore 	int	prod;		/* producer index */
77*01335b0dSGarrett D'Amore 	int	cons;		/* consumer index */
78*01335b0dSGarrett D'Amore 	int	mask;		/* buffer size - 1, wraparound index */
79*01335b0dSGarrett D'Amore 	int	navail;		/* total bytes avail */
80*01335b0dSGarrett D'Amore } buffer_t;
81*01335b0dSGarrett D'Amore 
82*01335b0dSGarrett D'Amore /*
83*01335b0dSGarrett D'Amore  * This structure is used to provide information on a specific output
84*01335b0dSGarrett D'Amore  * format.  We link them together in a list representing the output
85*01335b0dSGarrett D'Amore  * formats that the user has selected.
86*01335b0dSGarrett D'Amore  */
87*01335b0dSGarrett D'Amore typedef struct output {
88*01335b0dSGarrett D'Amore 	int	width;				/* bytes consumed per call */
89*01335b0dSGarrett D'Amore 	void	(*func)(buffer_t *, int);	/* output function */
90*01335b0dSGarrett D'Amore 	struct output	*next;			/* link node */
91*01335b0dSGarrett D'Amore } output_t;
92*01335b0dSGarrett D'Amore 
93*01335b0dSGarrett D'Amore /*
94*01335b0dSGarrett D'Amore  * Specifiers
95*01335b0dSGarrett D'Amore  */
96*01335b0dSGarrett D'Amore 
97*01335b0dSGarrett D'Amore typedef unsigned char		u8;
98*01335b0dSGarrett D'Amore typedef unsigned short		u16;
99*01335b0dSGarrett D'Amore typedef unsigned int		u32;
100*01335b0dSGarrett D'Amore typedef unsigned long long	u64;
101*01335b0dSGarrett D'Amore typedef char			s8;
102*01335b0dSGarrett D'Amore typedef short			s16;
103*01335b0dSGarrett D'Amore typedef int			s32;
104*01335b0dSGarrett D'Amore typedef long long		s64;
105*01335b0dSGarrett D'Amore typedef float			fF;
106*01335b0dSGarrett D'Amore typedef	double			fD;
107*01335b0dSGarrett D'Amore typedef long double		fL;
108*01335b0dSGarrett D'Amore 
109*01335b0dSGarrett D'Amore static void
110*01335b0dSGarrett D'Amore usage(void)
111*01335b0dSGarrett D'Amore {
112*01335b0dSGarrett D'Amore 	(void) fprintf(stderr, _("usage: od [-bcCdDfFoOsSvxX] "
113*01335b0dSGarrett D'Amore 	    "[-t types ]... [-A base] [-j skip] [-N count] [file]...\n"));
114*01335b0dSGarrett D'Amore 	exit(1);
115*01335b0dSGarrett D'Amore }
116*01335b0dSGarrett D'Amore 
117*01335b0dSGarrett D'Amore #define	DECL_GET(typ)							\
118*01335b0dSGarrett D'Amore static typ								\
119*01335b0dSGarrett D'Amore get_ ## typ(buffer_t *b, int index)					\
120*01335b0dSGarrett D'Amore {									\
121*01335b0dSGarrett D'Amore 	typ val = *(typ *)(void *)(b->data + index);			\
122*01335b0dSGarrett D'Amore 	return (val);							\
123*01335b0dSGarrett D'Amore }
124*01335b0dSGarrett D'Amore DECL_GET(u8)
125*01335b0dSGarrett D'Amore DECL_GET(u16)
126*01335b0dSGarrett D'Amore DECL_GET(u32)
127*01335b0dSGarrett D'Amore DECL_GET(u64)
128*01335b0dSGarrett D'Amore DECL_GET(s8)
129*01335b0dSGarrett D'Amore DECL_GET(s16)
130*01335b0dSGarrett D'Amore DECL_GET(s32)
131*01335b0dSGarrett D'Amore DECL_GET(s64)
132*01335b0dSGarrett D'Amore DECL_GET(fF)
133*01335b0dSGarrett D'Amore DECL_GET(fD)
134*01335b0dSGarrett D'Amore DECL_GET(fL)
135*01335b0dSGarrett D'Amore 
136*01335b0dSGarrett D'Amore #define	DECL_OUT(nm, typ, fmt)					\
137*01335b0dSGarrett D'Amore static void							\
138*01335b0dSGarrett D'Amore do_ ## nm(buffer_t *buf, int index)				\
139*01335b0dSGarrett D'Amore {								\
140*01335b0dSGarrett D'Amore 	typ v = get_ ## typ(buf, index);			\
141*01335b0dSGarrett D'Amore 	(void) printf(fmt, v);					\
142*01335b0dSGarrett D'Amore }								\
143*01335b0dSGarrett D'Amore 								\
144*01335b0dSGarrett D'Amore static output_t output_ ## nm =  {				\
145*01335b0dSGarrett D'Amore 	sizeof (typ), do_ ## nm					\
146*01335b0dSGarrett D'Amore };
147*01335b0dSGarrett D'Amore 
148*01335b0dSGarrett D'Amore DECL_OUT(oct_b, u8, " %03o")
149*01335b0dSGarrett D'Amore DECL_OUT(oct_w, u16, " %06ho")
150*01335b0dSGarrett D'Amore DECL_OUT(oct_d, u32, " %011o")
151*01335b0dSGarrett D'Amore DECL_OUT(oct_q, u64, " %022llo")
152*01335b0dSGarrett D'Amore DECL_OUT(dec_b, u8, " %03u")
153*01335b0dSGarrett D'Amore DECL_OUT(dec_w, u16, " %05hu")
154*01335b0dSGarrett D'Amore DECL_OUT(dec_d, u32, " %010u")
155*01335b0dSGarrett D'Amore DECL_OUT(dec_q, u64, " %020llu")
156*01335b0dSGarrett D'Amore DECL_OUT(sig_b, s8, " %03d")
157*01335b0dSGarrett D'Amore DECL_OUT(sig_w, s16, " %6.05hd")
158*01335b0dSGarrett D'Amore DECL_OUT(sig_d, s32, " %11.010d")
159*01335b0dSGarrett D'Amore DECL_OUT(sig_q, s64, " %20.019lld")
160*01335b0dSGarrett D'Amore DECL_OUT(hex_b, u8, " %02x")
161*01335b0dSGarrett D'Amore DECL_OUT(hex_w, u16, " %04hx")
162*01335b0dSGarrett D'Amore DECL_OUT(hex_d, s32, " %08x")
163*01335b0dSGarrett D'Amore DECL_OUT(hex_q, s64, " %016llx")
164*01335b0dSGarrett D'Amore DECL_OUT(float, fF, " %14.7e")
165*01335b0dSGarrett D'Amore DECL_OUT(double, fD, " %21.14e")
166*01335b0dSGarrett D'Amore DECL_OUT(ldouble, fL, " %24.14Le")
167*01335b0dSGarrett D'Amore 
168*01335b0dSGarrett D'Amore static char *ascii[] = {
169*01335b0dSGarrett D'Amore 	"nul", "soh", "stx", "etx", "eot", "enq", "ack", " be",
170*01335b0dSGarrett D'Amore 	" bs", " ht", " lf", " vt", " ff", " cr", " so", " si",
171*01335b0dSGarrett D'Amore 	"dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
172*01335b0dSGarrett D'Amore 	"can", " em", "sub", "esc", " fs", " gs", " rs", " us",
173*01335b0dSGarrett D'Amore 	" sp", "  !", "  \"", "  #", "  $", "  %", "  &", "  '",
174*01335b0dSGarrett D'Amore 	"  (", "  )", "  *", "  +", "  ,", "  -", "  .", "  /",
175*01335b0dSGarrett D'Amore 	"  0", "  1", "  2", "  3", "  4", "  5", "  6", "  7",
176*01335b0dSGarrett D'Amore 	"  8", "  9", "  :", "  ;", "  <", "  =", "  >", "  ?",
177*01335b0dSGarrett D'Amore 	"  @", "  A", "  B", "  C", "  D", "  E", "  F", "  G",
178*01335b0dSGarrett D'Amore 	"  H", "  I", "  J", "  K", "  L", "  M", "  N", "  O",
179*01335b0dSGarrett D'Amore 	"  P", "  Q", "  R", "  S", "  T", "  U", "  V", "  W",
180*01335b0dSGarrett D'Amore 	"  X", "  Y", "  Z", "  [", "  \\", "  ]", "  ^", "  _",
181*01335b0dSGarrett D'Amore 	"  `", "  a", "  b", "  c", "  d", "  e", "  f", "  g",
182*01335b0dSGarrett D'Amore 	"  h", "  i", "  j", "  k", "  l", "  m", "  n", "  o",
183*01335b0dSGarrett D'Amore 	"  p", "  q", "  r", "  s", "  t", "  u", "  v", "  w",
184*01335b0dSGarrett D'Amore 	"  x", "  y", "  z", "  {", "  |", "  }", "  ~", "del"
185*01335b0dSGarrett D'Amore };
186*01335b0dSGarrett D'Amore 
187*01335b0dSGarrett D'Amore static void
188*01335b0dSGarrett D'Amore do_ascii(buffer_t *buf, int index)
189*01335b0dSGarrett D'Amore {
190*01335b0dSGarrett D'Amore 	uint8_t v = get_u8(buf, index);
191*01335b0dSGarrett D'Amore 
192*01335b0dSGarrett D'Amore 	(void) fputc(' ', stdout);
193*01335b0dSGarrett D'Amore 	(void) fputs(ascii[v & 0x7f], stdout);
194*01335b0dSGarrett D'Amore }
195*01335b0dSGarrett D'Amore 
196*01335b0dSGarrett D'Amore static output_t output_ascii = {
197*01335b0dSGarrett D'Amore 	1, do_ascii,
198*01335b0dSGarrett D'Amore };
199*01335b0dSGarrett D'Amore 
200*01335b0dSGarrett D'Amore static void
201*01335b0dSGarrett D'Amore do_char(buffer_t *buf, int index)
202*01335b0dSGarrett D'Amore {
203*01335b0dSGarrett D'Amore 	static int	nresid = 0;
204*01335b0dSGarrett D'Amore 	static int	printable = 0;
205*01335b0dSGarrett D'Amore 	int		cnt;
206*01335b0dSGarrett D'Amore 	int		avail;
207*01335b0dSGarrett D'Amore 	int		nb;
208*01335b0dSGarrett D'Amore 	char		scratch[10];
209*01335b0dSGarrett D'Amore 	wchar_t		wc;
210*01335b0dSGarrett D'Amore 	int		which;
211*01335b0dSGarrett D'Amore 
212*01335b0dSGarrett D'Amore 	uint8_t v = get_u8(buf, index);
213*01335b0dSGarrett D'Amore 
214*01335b0dSGarrett D'Amore 	/*
215*01335b0dSGarrett D'Amore 	 * If there were residual bytes from an earlier
216*01335b0dSGarrett D'Amore 	 * character, then just display the ** continuation
217*01335b0dSGarrett D'Amore 	 * indication.
218*01335b0dSGarrett D'Amore 	 */
219*01335b0dSGarrett D'Amore 	if (nresid) {
220*01335b0dSGarrett D'Amore 		if (printable) {
221*01335b0dSGarrett D'Amore 			(void) fputs("  **", stdout);
222*01335b0dSGarrett D'Amore 		} else {
223*01335b0dSGarrett D'Amore 			(void) printf(" %03o", v);
224*01335b0dSGarrett D'Amore 		}
225*01335b0dSGarrett D'Amore 		nresid--;
226*01335b0dSGarrett D'Amore 		return;
227*01335b0dSGarrett D'Amore 	}
228*01335b0dSGarrett D'Amore 
229*01335b0dSGarrett D'Amore 	/*
230*01335b0dSGarrett D'Amore 	 * Peek ahead up to MB_CUR_MAX characters.  This has to be
231*01335b0dSGarrett D'Amore 	 * done carefully because we might need to look into the next
232*01335b0dSGarrett D'Amore 	 * block to really know for sure.
233*01335b0dSGarrett D'Amore 	 */
234*01335b0dSGarrett D'Amore 	scratch[0] = v;
235*01335b0dSGarrett D'Amore 	avail = buf->navail;
236*01335b0dSGarrett D'Amore 	if (avail > MB_CUR_MAX)
237*01335b0dSGarrett D'Amore 		avail = MB_CUR_MAX;
238*01335b0dSGarrett D'Amore 	for (cnt = 1, which = index + 1; cnt < avail; cnt++, which++) {
239*01335b0dSGarrett D'Amore 		scratch[cnt] = buf->data[which & buf->mask];
240*01335b0dSGarrett D'Amore 	}
241*01335b0dSGarrett D'Amore 
242*01335b0dSGarrett D'Amore 	/* now see if the value is a real character */
243*01335b0dSGarrett D'Amore 	nresid = 0;
244*01335b0dSGarrett D'Amore 	wc = 0;
245*01335b0dSGarrett D'Amore 	nb = mbtowc(&wc, scratch, avail);
246*01335b0dSGarrett D'Amore 	if (nb < 0) {
247*01335b0dSGarrett D'Amore 		(void) printf(" %03o", v);
248*01335b0dSGarrett D'Amore 		return;
249*01335b0dSGarrett D'Amore 	}
250*01335b0dSGarrett D'Amore 	if (nb == 0) {
251*01335b0dSGarrett D'Amore 		(void) fputs("  \\0", stdout);
252*01335b0dSGarrett D'Amore 		return;
253*01335b0dSGarrett D'Amore 	}
254*01335b0dSGarrett D'Amore 	nresid = nb - 1;
255*01335b0dSGarrett D'Amore 	if (nb && iswprint(wc)) {
256*01335b0dSGarrett D'Amore 		scratch[nb] = 0;
257*01335b0dSGarrett D'Amore 		(void) fputs("   ", stdout);
258*01335b0dSGarrett D'Amore 		(void) fputs(scratch, stdout);
259*01335b0dSGarrett D'Amore 		printable = 1;
260*01335b0dSGarrett D'Amore 		return;
261*01335b0dSGarrett D'Amore 	}
262*01335b0dSGarrett D'Amore 	printable = 0;
263*01335b0dSGarrett D'Amore 	if (wc == 0) {
264*01335b0dSGarrett D'Amore 		(void) fputs("  \\0", stdout);
265*01335b0dSGarrett D'Amore 	} else if (wc == '\b') {
266*01335b0dSGarrett D'Amore 		(void) fputs("  \\b", stdout);
267*01335b0dSGarrett D'Amore 	} else if (wc == '\f') {
268*01335b0dSGarrett D'Amore 		(void) fputs("  \\f", stdout);
269*01335b0dSGarrett D'Amore 	} else if (wc == '\n') {
270*01335b0dSGarrett D'Amore 		(void) fputs("  \\n", stdout);
271*01335b0dSGarrett D'Amore 	} else if (wc == '\r') {
272*01335b0dSGarrett D'Amore 		(void) fputs("  \\r", stdout);
273*01335b0dSGarrett D'Amore 	} else if (wc == '\t') {
274*01335b0dSGarrett D'Amore 		(void) fputs("  \\t", stdout);
275*01335b0dSGarrett D'Amore 	} else {
276*01335b0dSGarrett D'Amore 		(void) printf(" %03o", v);
277*01335b0dSGarrett D'Amore 	}
278*01335b0dSGarrett D'Amore }
279*01335b0dSGarrett D'Amore 
280*01335b0dSGarrett D'Amore static output_t output_char = {
281*01335b0dSGarrett D'Amore 	1, do_char,
282*01335b0dSGarrett D'Amore };
283*01335b0dSGarrett D'Amore 
284*01335b0dSGarrett D'Amore /*
285*01335b0dSGarrett D'Amore  * List of output formatting structures.
286*01335b0dSGarrett D'Amore  */
287*01335b0dSGarrett D'Amore static output_t *head = NULL;
288*01335b0dSGarrett D'Amore static output_t **tailp = &head;
289*01335b0dSGarrett D'Amore 
290*01335b0dSGarrett D'Amore static void
291*01335b0dSGarrett D'Amore add_out(output_t *src)
292*01335b0dSGarrett D'Amore {
293*01335b0dSGarrett D'Amore 	output_t	*out;
294*01335b0dSGarrett D'Amore 	int		m;
295*01335b0dSGarrett D'Amore 
296*01335b0dSGarrett D'Amore 	if ((out = calloc(1, sizeof (*src))) == NULL) {
297*01335b0dSGarrett D'Amore 		err(1, "malloc");
298*01335b0dSGarrett D'Amore 	}
299*01335b0dSGarrett D'Amore 
300*01335b0dSGarrett D'Amore 	m = lcm;
301*01335b0dSGarrett D'Amore 	while ((m % src->width) != 0) {
302*01335b0dSGarrett D'Amore 		m += lcm;
303*01335b0dSGarrett D'Amore 	}
304*01335b0dSGarrett D'Amore 	lcm = m;
305*01335b0dSGarrett D'Amore 	blocksize = lcm;
306*01335b0dSGarrett D'Amore 	while (blocksize < 16)
307*01335b0dSGarrett D'Amore 		blocksize *= 2;
308*01335b0dSGarrett D'Amore 
309*01335b0dSGarrett D'Amore 	(void) memcpy(out, src, sizeof (*src));
310*01335b0dSGarrett D'Amore 	*tailp = out;
311*01335b0dSGarrett D'Amore 	tailp = &out->next;
312*01335b0dSGarrett D'Amore }
313*01335b0dSGarrett D'Amore 
314*01335b0dSGarrett D'Amore static FILE *
315*01335b0dSGarrett D'Amore next_input(void)
316*01335b0dSGarrett D'Amore {
317*01335b0dSGarrett D'Amore 	for (;;) {
318*01335b0dSGarrett D'Amore 		if (curfile >= numfiles)
319*01335b0dSGarrett D'Amore 			return (NULL);
320*01335b0dSGarrett D'Amore 
321*01335b0dSGarrett D'Amore 		if (input != NULL) {
322*01335b0dSGarrett D'Amore 			if ((input = freopen(files[curfile], "r", input)) !=
323*01335b0dSGarrett D'Amore 			    NULL) {
324*01335b0dSGarrett D'Amore 				curfile++;
325*01335b0dSGarrett D'Amore 				return (input);
326*01335b0dSGarrett D'Amore 			}
327*01335b0dSGarrett D'Amore 		} else {
328*01335b0dSGarrett D'Amore 			if ((input = fopen(files[curfile], "r")) != NULL) {
329*01335b0dSGarrett D'Amore 				curfile++;
330*01335b0dSGarrett D'Amore 				return (input);
331*01335b0dSGarrett D'Amore 			}
332*01335b0dSGarrett D'Amore 		}
333*01335b0dSGarrett D'Amore 		warn("open: %s", files[curfile]);
334*01335b0dSGarrett D'Amore 		curfile++;
335*01335b0dSGarrett D'Amore 	}
336*01335b0dSGarrett D'Amore }
337*01335b0dSGarrett D'Amore 
338*01335b0dSGarrett D'Amore static void
339*01335b0dSGarrett D'Amore refill(buffer_t *b)
340*01335b0dSGarrett D'Amore {
341*01335b0dSGarrett D'Amore 	int	n;
342*01335b0dSGarrett D'Amore 	int	want;
343*01335b0dSGarrett D'Amore 	int	zero;
344*01335b0dSGarrett D'Amore 
345*01335b0dSGarrett D'Amore 	/*
346*01335b0dSGarrett D'Amore 	 * If we have 2 blocks of bytes available, we're done.  Note
347*01335b0dSGarrett D'Amore 	 * that each iteration usually loads up 16 bytes, unless we
348*01335b0dSGarrett D'Amore 	 * run out of data.
349*01335b0dSGarrett D'Amore 	 */
350*01335b0dSGarrett D'Amore 	while ((input != NULL) && (b->navail < (2 * blocksize))) {
351*01335b0dSGarrett D'Amore 
352*01335b0dSGarrett D'Amore 		/* we preload the next one in advance */
353*01335b0dSGarrett D'Amore 
354*01335b0dSGarrett D'Amore 		if (limit == 0) {
355*01335b0dSGarrett D'Amore 			(void) fclose(input);
356*01335b0dSGarrett D'Amore 			input = NULL;
357*01335b0dSGarrett D'Amore 			continue;
358*01335b0dSGarrett D'Amore 		}
359*01335b0dSGarrett D'Amore 
360*01335b0dSGarrett D'Amore 		/* we want to read a whole block if possible */
361*01335b0dSGarrett D'Amore 		want = blocksize;
362*01335b0dSGarrett D'Amore 		if ((limit >= 0) && (want > limit)) {
363*01335b0dSGarrett D'Amore 			want = limit;
364*01335b0dSGarrett D'Amore 		}
365*01335b0dSGarrett D'Amore 		zero = blocksize;
366*01335b0dSGarrett D'Amore 
367*01335b0dSGarrett D'Amore 		while (want && input) {
368*01335b0dSGarrett D'Amore 			int	c;
369*01335b0dSGarrett D'Amore 			b->prod &= b->mask;
370*01335b0dSGarrett D'Amore 			c = (b->prod + want > (b->mask + 1)) ?
371*01335b0dSGarrett D'Amore 			    b->mask - b->prod :
372*01335b0dSGarrett D'Amore 			    want;
373*01335b0dSGarrett D'Amore 
374*01335b0dSGarrett D'Amore 			n = fread(b->data + b->prod, 1, c, input);
375*01335b0dSGarrett D'Amore 			if (n < 0) {
376*01335b0dSGarrett D'Amore 				warn("read: %s",
377*01335b0dSGarrett D'Amore 				    files ? files[curfile-1] : "stdin");
378*01335b0dSGarrett D'Amore 				input = next_input();
379*01335b0dSGarrett D'Amore 				continue;
380*01335b0dSGarrett D'Amore 			}
381*01335b0dSGarrett D'Amore 			if (n == 0) {
382*01335b0dSGarrett D'Amore 				input = next_input();
383*01335b0dSGarrett D'Amore 				continue;
384*01335b0dSGarrett D'Amore 			}
385*01335b0dSGarrett D'Amore 			if (limit >= 0)
386*01335b0dSGarrett D'Amore 				limit -= n;
387*01335b0dSGarrett D'Amore 			b->navail += n;
388*01335b0dSGarrett D'Amore 			b->prod += n;
389*01335b0dSGarrett D'Amore 			want -= n;
390*01335b0dSGarrett D'Amore 			zero -= n;
391*01335b0dSGarrett D'Amore 		}
392*01335b0dSGarrett D'Amore 
393*01335b0dSGarrett D'Amore 		while (zero) {
394*01335b0dSGarrett D'Amore 			b->data[b->prod & b->mask] = 0;
395*01335b0dSGarrett D'Amore 			b->prod++;
396*01335b0dSGarrett D'Amore 			b->prod &= b->mask;
397*01335b0dSGarrett D'Amore 			zero--;
398*01335b0dSGarrett D'Amore 		}
399*01335b0dSGarrett D'Amore 	}
400*01335b0dSGarrett D'Amore }
401*01335b0dSGarrett D'Amore 
402*01335b0dSGarrett D'Amore #define	STR1	"C1"
403*01335b0dSGarrett D'Amore #define	STR2	"S2"
404*01335b0dSGarrett D'Amore #ifdef	_LP64
405*01335b0dSGarrett D'Amore #define	STR8	"L8"
406*01335b0dSGarrett D'Amore #define	STR4	"I4"
407*01335b0dSGarrett D'Amore #else
408*01335b0dSGarrett D'Amore #define	STR8	"8"
409*01335b0dSGarrett D'Amore #define	STR4	"IL4"
410*01335b0dSGarrett D'Amore #endif
411*01335b0dSGarrett D'Amore 
412*01335b0dSGarrett D'Amore static void
413*01335b0dSGarrett D'Amore do_type_string(char *typestr)
414*01335b0dSGarrett D'Amore {
415*01335b0dSGarrett D'Amore 	if (*typestr == 0) {
416*01335b0dSGarrett D'Amore 		errx(1, _("missing type string"));
417*01335b0dSGarrett D'Amore 	}
418*01335b0dSGarrett D'Amore 	while (*typestr) {
419*01335b0dSGarrett D'Amore 		switch (*typestr) {
420*01335b0dSGarrett D'Amore 		case 'a':
421*01335b0dSGarrett D'Amore 			typestr++;
422*01335b0dSGarrett D'Amore 			add_out(&output_ascii);
423*01335b0dSGarrett D'Amore 			break;
424*01335b0dSGarrett D'Amore 		case 'c':
425*01335b0dSGarrett D'Amore 			add_out(&output_char);
426*01335b0dSGarrett D'Amore 			typestr++;
427*01335b0dSGarrett D'Amore 			break;
428*01335b0dSGarrett D'Amore 		case 'f':
429*01335b0dSGarrett D'Amore 			typestr++;
430*01335b0dSGarrett D'Amore 			switch (*typestr) {
431*01335b0dSGarrett D'Amore 			case 'F':
432*01335b0dSGarrett D'Amore 			case '4':
433*01335b0dSGarrett D'Amore 				add_out(&output_float);
434*01335b0dSGarrett D'Amore 				typestr++;
435*01335b0dSGarrett D'Amore 				break;
436*01335b0dSGarrett D'Amore 			case '8':
437*01335b0dSGarrett D'Amore 			case 'D':
438*01335b0dSGarrett D'Amore 				add_out(&output_double);
439*01335b0dSGarrett D'Amore 				typestr++;
440*01335b0dSGarrett D'Amore 				break;
441*01335b0dSGarrett D'Amore 			case 'L':
442*01335b0dSGarrett D'Amore 				add_out(&output_ldouble);
443*01335b0dSGarrett D'Amore 				typestr++;
444*01335b0dSGarrett D'Amore 				break;
445*01335b0dSGarrett D'Amore 			default:
446*01335b0dSGarrett D'Amore 				add_out(&output_float);
447*01335b0dSGarrett D'Amore 				break;
448*01335b0dSGarrett D'Amore 			}
449*01335b0dSGarrett D'Amore 			break;
450*01335b0dSGarrett D'Amore 
451*01335b0dSGarrett D'Amore 
452*01335b0dSGarrett D'Amore 		case 'd':
453*01335b0dSGarrett D'Amore 			typestr++;
454*01335b0dSGarrett D'Amore 			if (strchr(STR1, *typestr)) {
455*01335b0dSGarrett D'Amore 				typestr++;
456*01335b0dSGarrett D'Amore 				add_out(&output_sig_b);
457*01335b0dSGarrett D'Amore 			} else if (strchr(STR2, *typestr)) {
458*01335b0dSGarrett D'Amore 				typestr++;
459*01335b0dSGarrett D'Amore 				add_out(&output_sig_w);
460*01335b0dSGarrett D'Amore 			} else if (strchr(STR4, *typestr)) {
461*01335b0dSGarrett D'Amore 				typestr++;
462*01335b0dSGarrett D'Amore 				add_out(&output_sig_d);
463*01335b0dSGarrett D'Amore 			} else if (strchr(STR8, *typestr)) {
464*01335b0dSGarrett D'Amore 				typestr++;
465*01335b0dSGarrett D'Amore 				add_out(&output_sig_q);
466*01335b0dSGarrett D'Amore 			} else {
467*01335b0dSGarrett D'Amore 				add_out(&output_sig_d);
468*01335b0dSGarrett D'Amore 			}
469*01335b0dSGarrett D'Amore 			break;
470*01335b0dSGarrett D'Amore 
471*01335b0dSGarrett D'Amore 		case 'u':
472*01335b0dSGarrett D'Amore 			typestr++;
473*01335b0dSGarrett D'Amore 			if (strchr(STR1, *typestr)) {
474*01335b0dSGarrett D'Amore 				typestr++;
475*01335b0dSGarrett D'Amore 				add_out(&output_dec_b);
476*01335b0dSGarrett D'Amore 			} else if (strchr(STR2, *typestr)) {
477*01335b0dSGarrett D'Amore 				typestr++;
478*01335b0dSGarrett D'Amore 				add_out(&output_dec_w);
479*01335b0dSGarrett D'Amore 			} else if (strchr(STR4, *typestr)) {
480*01335b0dSGarrett D'Amore 				typestr++;
481*01335b0dSGarrett D'Amore 				add_out(&output_dec_d);
482*01335b0dSGarrett D'Amore 			} else if (strchr(STR8, *typestr)) {
483*01335b0dSGarrett D'Amore 				typestr++;
484*01335b0dSGarrett D'Amore 				add_out(&output_dec_q);
485*01335b0dSGarrett D'Amore 			} else {
486*01335b0dSGarrett D'Amore 				add_out(&output_dec_d);
487*01335b0dSGarrett D'Amore 			}
488*01335b0dSGarrett D'Amore 			break;
489*01335b0dSGarrett D'Amore 
490*01335b0dSGarrett D'Amore 		case 'o':
491*01335b0dSGarrett D'Amore 			typestr++;
492*01335b0dSGarrett D'Amore 			if (strchr(STR1, *typestr)) {
493*01335b0dSGarrett D'Amore 				typestr++;
494*01335b0dSGarrett D'Amore 				add_out(&output_oct_b);
495*01335b0dSGarrett D'Amore 			} else if (strchr(STR2, *typestr)) {
496*01335b0dSGarrett D'Amore 				typestr++;
497*01335b0dSGarrett D'Amore 				add_out(&output_oct_w);
498*01335b0dSGarrett D'Amore 			} else if (strchr(STR4, *typestr)) {
499*01335b0dSGarrett D'Amore 				typestr++;
500*01335b0dSGarrett D'Amore 				add_out(&output_oct_d);
501*01335b0dSGarrett D'Amore 			} else if (strchr(STR8, *typestr)) {
502*01335b0dSGarrett D'Amore 				typestr++;
503*01335b0dSGarrett D'Amore 				add_out(&output_oct_q);
504*01335b0dSGarrett D'Amore 			} else {
505*01335b0dSGarrett D'Amore 				add_out(&output_oct_d);
506*01335b0dSGarrett D'Amore 			}
507*01335b0dSGarrett D'Amore 			break;
508*01335b0dSGarrett D'Amore 
509*01335b0dSGarrett D'Amore 		case 'x':
510*01335b0dSGarrett D'Amore 			typestr++;
511*01335b0dSGarrett D'Amore 			if (strchr(STR1, *typestr)) {
512*01335b0dSGarrett D'Amore 				typestr++;
513*01335b0dSGarrett D'Amore 				add_out(&output_hex_b);
514*01335b0dSGarrett D'Amore 			} else if (strchr(STR2, *typestr)) {
515*01335b0dSGarrett D'Amore 				typestr++;
516*01335b0dSGarrett D'Amore 				add_out(&output_hex_w);
517*01335b0dSGarrett D'Amore 			} else if (strchr(STR4, *typestr)) {
518*01335b0dSGarrett D'Amore 				typestr++;
519*01335b0dSGarrett D'Amore 				add_out(&output_hex_d);
520*01335b0dSGarrett D'Amore 			} else if (strchr(STR8, *typestr)) {
521*01335b0dSGarrett D'Amore 				typestr++;
522*01335b0dSGarrett D'Amore 				add_out(&output_hex_q);
523*01335b0dSGarrett D'Amore 			} else {
524*01335b0dSGarrett D'Amore 				add_out(&output_hex_d);
525*01335b0dSGarrett D'Amore 			}
526*01335b0dSGarrett D'Amore 			break;
527*01335b0dSGarrett D'Amore 
528*01335b0dSGarrett D'Amore 		default:
529*01335b0dSGarrett D'Amore 			errx(1, _("unrecognized type string character: %c"),
530*01335b0dSGarrett D'Amore 			    *typestr);
531*01335b0dSGarrett D'Amore 			exit(1);
532*01335b0dSGarrett D'Amore 		}
533*01335b0dSGarrett D'Amore 	}
534*01335b0dSGarrett D'Amore }
535*01335b0dSGarrett D'Amore 
536*01335b0dSGarrett D'Amore int
537*01335b0dSGarrett D'Amore main(int argc, char **argv)
538*01335b0dSGarrett D'Amore {
539*01335b0dSGarrett D'Amore 	int		c;
540*01335b0dSGarrett D'Amore 	int		i;
541*01335b0dSGarrett D'Amore 	buffer_t	buffer;
542*01335b0dSGarrett D'Amore 	boolean_t	first = B_TRUE;
543*01335b0dSGarrett D'Amore 	boolean_t	doall = B_FALSE;
544*01335b0dSGarrett D'Amore 	boolean_t	same = B_FALSE;
545*01335b0dSGarrett D'Amore 	boolean_t	newarg = B_FALSE;
546*01335b0dSGarrett D'Amore 	off_t		offset = 0;
547*01335b0dSGarrett D'Amore 	off_t		skip = 0;
548*01335b0dSGarrett D'Amore 	char		*eptr;
549*01335b0dSGarrett D'Amore 	char		*offstr = 0;
550*01335b0dSGarrett D'Amore 
551*01335b0dSGarrett D'Amore 	input = stdin;
552*01335b0dSGarrett D'Amore 
553*01335b0dSGarrett D'Amore 	(void) setlocale(LC_ALL, "");
554*01335b0dSGarrett D'Amore 
555*01335b0dSGarrett D'Amore 	while ((c = getopt(argc, argv, "A:bCcdDfFj:N:oOsSxXvt:")) != EOF) {
556*01335b0dSGarrett D'Amore 		switch (c) {
557*01335b0dSGarrett D'Amore 		case 'A':
558*01335b0dSGarrett D'Amore 			newarg = B_TRUE;
559*01335b0dSGarrett D'Amore 			if (strlen(optarg) > 1) {
560*01335b0dSGarrett D'Amore 				afmt = NULL;
561*01335b0dSGarrett D'Amore 			}
562*01335b0dSGarrett D'Amore 			switch (*optarg) {
563*01335b0dSGarrett D'Amore 			case 'o':
564*01335b0dSGarrett D'Amore 				afmt = "%07llo";
565*01335b0dSGarrett D'Amore 				cfmt = "       ";
566*01335b0dSGarrett D'Amore 				break;
567*01335b0dSGarrett D'Amore 			case 'd':
568*01335b0dSGarrett D'Amore 				afmt = "%07lld";
569*01335b0dSGarrett D'Amore 				cfmt = "       ";
570*01335b0dSGarrett D'Amore 				break;
571*01335b0dSGarrett D'Amore 			case 'x':
572*01335b0dSGarrett D'Amore 				afmt = "%07llx";
573*01335b0dSGarrett D'Amore 				cfmt = "       ";
574*01335b0dSGarrett D'Amore 				break;
575*01335b0dSGarrett D'Amore 			case 'n':
576*01335b0dSGarrett D'Amore 				/*
577*01335b0dSGarrett D'Amore 				 * You could argue that the code should
578*01335b0dSGarrett D'Amore 				 * use the same 7 spaces.  Legacy uses 8
579*01335b0dSGarrett D'Amore 				 * though.  Oh well.  Better to avoid
580*01335b0dSGarrett D'Amore 				 * gratuitous change.
581*01335b0dSGarrett D'Amore 				 */
582*01335b0dSGarrett D'Amore 				afmt = "        ";
583*01335b0dSGarrett D'Amore 				cfmt = "        ";
584*01335b0dSGarrett D'Amore 				break;
585*01335b0dSGarrett D'Amore 			default:
586*01335b0dSGarrett D'Amore 				afmt = NULL;
587*01335b0dSGarrett D'Amore 				break;
588*01335b0dSGarrett D'Amore 			}
589*01335b0dSGarrett D'Amore 			if (strlen(optarg) != 1) {
590*01335b0dSGarrett D'Amore 				afmt = NULL;
591*01335b0dSGarrett D'Amore 			}
592*01335b0dSGarrett D'Amore 			if (afmt == NULL)
593*01335b0dSGarrett D'Amore 				warnx(_("invalid address base, "
594*01335b0dSGarrett D'Amore 				    "must be o, d, x, or n"));
595*01335b0dSGarrett D'Amore 			break;
596*01335b0dSGarrett D'Amore 
597*01335b0dSGarrett D'Amore 		case 'b':
598*01335b0dSGarrett D'Amore 			add_out(&output_oct_b);
599*01335b0dSGarrett D'Amore 			break;
600*01335b0dSGarrett D'Amore 
601*01335b0dSGarrett D'Amore 		case 'c':
602*01335b0dSGarrett D'Amore 		case 'C':
603*01335b0dSGarrett D'Amore 			add_out(&output_char);
604*01335b0dSGarrett D'Amore 			break;
605*01335b0dSGarrett D'Amore 
606*01335b0dSGarrett D'Amore 		case 'f':
607*01335b0dSGarrett D'Amore 			add_out(&output_float);
608*01335b0dSGarrett D'Amore 			break;
609*01335b0dSGarrett D'Amore 
610*01335b0dSGarrett D'Amore 		case 'F':
611*01335b0dSGarrett D'Amore 			add_out(&output_double);
612*01335b0dSGarrett D'Amore 			break;
613*01335b0dSGarrett D'Amore 
614*01335b0dSGarrett D'Amore 		case 'd':
615*01335b0dSGarrett D'Amore 			add_out(&output_dec_w);
616*01335b0dSGarrett D'Amore 			break;
617*01335b0dSGarrett D'Amore 
618*01335b0dSGarrett D'Amore 		case 'D':
619*01335b0dSGarrett D'Amore 			add_out(&output_dec_d);
620*01335b0dSGarrett D'Amore 			break;
621*01335b0dSGarrett D'Amore 
622*01335b0dSGarrett D'Amore 		case 't':
623*01335b0dSGarrett D'Amore 			newarg = B_TRUE;
624*01335b0dSGarrett D'Amore 			do_type_string(optarg);
625*01335b0dSGarrett D'Amore 			break;
626*01335b0dSGarrett D'Amore 
627*01335b0dSGarrett D'Amore 		case 'o':
628*01335b0dSGarrett D'Amore 			add_out(&output_oct_w);
629*01335b0dSGarrett D'Amore 			break;
630*01335b0dSGarrett D'Amore 
631*01335b0dSGarrett D'Amore 		case 'O':
632*01335b0dSGarrett D'Amore 			add_out(&output_oct_d);
633*01335b0dSGarrett D'Amore 			break;
634*01335b0dSGarrett D'Amore 
635*01335b0dSGarrett D'Amore 		case 's':
636*01335b0dSGarrett D'Amore 			add_out(&output_sig_w);
637*01335b0dSGarrett D'Amore 			break;
638*01335b0dSGarrett D'Amore 
639*01335b0dSGarrett D'Amore 		case 'S':
640*01335b0dSGarrett D'Amore 			add_out(&output_sig_d);
641*01335b0dSGarrett D'Amore 			break;
642*01335b0dSGarrett D'Amore 
643*01335b0dSGarrett D'Amore 		case 'x':
644*01335b0dSGarrett D'Amore 			add_out(&output_hex_w);
645*01335b0dSGarrett D'Amore 			break;
646*01335b0dSGarrett D'Amore 
647*01335b0dSGarrett D'Amore 		case 'X':
648*01335b0dSGarrett D'Amore 			add_out(&output_hex_d);
649*01335b0dSGarrett D'Amore 			break;
650*01335b0dSGarrett D'Amore 
651*01335b0dSGarrett D'Amore 		case 'v':
652*01335b0dSGarrett D'Amore 			doall = B_TRUE;
653*01335b0dSGarrett D'Amore 			break;
654*01335b0dSGarrett D'Amore 
655*01335b0dSGarrett D'Amore 		case 'j':
656*01335b0dSGarrett D'Amore 			newarg = B_TRUE;
657*01335b0dSGarrett D'Amore 			skip = strtoll(optarg, &eptr, 0);
658*01335b0dSGarrett D'Amore 			if (*eptr == 'b') {
659*01335b0dSGarrett D'Amore 				skip <<= 9;	/* 512 bytes */
660*01335b0dSGarrett D'Amore 				eptr++;
661*01335b0dSGarrett D'Amore 			} else if (*eptr == 'k') {
662*01335b0dSGarrett D'Amore 				skip <<= 10;	/* 1k */
663*01335b0dSGarrett D'Amore 				eptr++;
664*01335b0dSGarrett D'Amore 			} else if (*eptr == 'm') {
665*01335b0dSGarrett D'Amore 				skip <<= 20;	/* 1m */
666*01335b0dSGarrett D'Amore 				eptr++;
667*01335b0dSGarrett D'Amore 			} else if (*eptr == 'g') {
668*01335b0dSGarrett D'Amore 				skip <<= 30;	/* 1g */
669*01335b0dSGarrett D'Amore 				eptr++;
670*01335b0dSGarrett D'Amore 			}
671*01335b0dSGarrett D'Amore 			if ((skip < 0) || (eptr[0] != 0)) {
672*01335b0dSGarrett D'Amore 				warnx(_("invalid skip count '%s' specified"),
673*01335b0dSGarrett D'Amore 				    optarg);
674*01335b0dSGarrett D'Amore 				exit(1);
675*01335b0dSGarrett D'Amore 			}
676*01335b0dSGarrett D'Amore 			break;
677*01335b0dSGarrett D'Amore 
678*01335b0dSGarrett D'Amore 		case 'N':
679*01335b0dSGarrett D'Amore 			newarg = B_TRUE;
680*01335b0dSGarrett D'Amore 			limit = strtoll(optarg, &eptr, 0);
681*01335b0dSGarrett D'Amore 			/*
682*01335b0dSGarrett D'Amore 			 * POSIX doesn't specify this, but I think these
683*01335b0dSGarrett D'Amore 			 * may be helpful.
684*01335b0dSGarrett D'Amore 			 */
685*01335b0dSGarrett D'Amore 			if (*eptr == 'b') {
686*01335b0dSGarrett D'Amore 				limit <<= 9;
687*01335b0dSGarrett D'Amore 				eptr++;
688*01335b0dSGarrett D'Amore 			} else if (*eptr == 'k') {
689*01335b0dSGarrett D'Amore 				limit <<= 10;
690*01335b0dSGarrett D'Amore 				eptr++;
691*01335b0dSGarrett D'Amore 			} else if (*eptr == 'm') {
692*01335b0dSGarrett D'Amore 				limit <<= 20;
693*01335b0dSGarrett D'Amore 				eptr++;
694*01335b0dSGarrett D'Amore 			} else if (*eptr == 'g') {
695*01335b0dSGarrett D'Amore 				limit <<= 30;
696*01335b0dSGarrett D'Amore 				eptr++;
697*01335b0dSGarrett D'Amore 			}
698*01335b0dSGarrett D'Amore 			if ((limit < 0) || (eptr[0] != 0)) {
699*01335b0dSGarrett D'Amore 				warnx(_("invalid byte count '%s' specified"),
700*01335b0dSGarrett D'Amore 				    optarg);
701*01335b0dSGarrett D'Amore 				exit(1);
702*01335b0dSGarrett D'Amore 			}
703*01335b0dSGarrett D'Amore 			break;
704*01335b0dSGarrett D'Amore 
705*01335b0dSGarrett D'Amore 		default:
706*01335b0dSGarrett D'Amore 			usage();
707*01335b0dSGarrett D'Amore 			break;
708*01335b0dSGarrett D'Amore 		}
709*01335b0dSGarrett D'Amore 	}
710*01335b0dSGarrett D'Amore 
711*01335b0dSGarrett D'Amore 	/* this finds the smallest power of two size we can use */
712*01335b0dSGarrett D'Amore 	buffer.mask = (1 << (ffs(blocksize * 3) + 1)) - 1;
713*01335b0dSGarrett D'Amore 	buffer.data = memalign(16, buffer.mask + 1);
714*01335b0dSGarrett D'Amore 	if (buffer.data == NULL) {
715*01335b0dSGarrett D'Amore 		err(1, "memalign");
716*01335b0dSGarrett D'Amore 	}
717*01335b0dSGarrett D'Amore 
718*01335b0dSGarrett D'Amore 
719*01335b0dSGarrett D'Amore 	/*
720*01335b0dSGarrett D'Amore 	 * Wow.  This option parsing is hideous.
721*01335b0dSGarrett D'Amore 	 *
722*01335b0dSGarrett D'Amore 	 * If the we've not seen a new option, and there is just one
723*01335b0dSGarrett D'Amore 	 * operand, if it starts with a "+", then treat it as an
724*01335b0dSGarrett D'Amore 	 * offset.  Otherwise if two operands, and the second operand
725*01335b0dSGarrett D'Amore 	 * starts with + or a digit, then it is an offset.
726*01335b0dSGarrett D'Amore 	 */
727*01335b0dSGarrett D'Amore 	if (!newarg) {
728*01335b0dSGarrett D'Amore 		if (((argc - optind) == 1) && (argv[optind][0] == '+')) {
729*01335b0dSGarrett D'Amore 			offstr = argv[optind];
730*01335b0dSGarrett D'Amore 			argc--;
731*01335b0dSGarrett D'Amore 		} else if (((argc - optind) == 2) &&
732*01335b0dSGarrett D'Amore 		    (strchr("+0123456789", (argv[optind + 1][0])) != NULL)) {
733*01335b0dSGarrett D'Amore 			offstr = argv[optind + 1];
734*01335b0dSGarrett D'Amore 			argc--;
735*01335b0dSGarrett D'Amore 		}
736*01335b0dSGarrett D'Amore 	}
737*01335b0dSGarrett D'Amore 	if (offstr) {
738*01335b0dSGarrett D'Amore 		int base = 0;
739*01335b0dSGarrett D'Amore 		int mult = 1;
740*01335b0dSGarrett D'Amore 		int l;
741*01335b0dSGarrett D'Amore 		if (*offstr == '+') {
742*01335b0dSGarrett D'Amore 			offstr++;
743*01335b0dSGarrett D'Amore 		}
744*01335b0dSGarrett D'Amore 		l = strlen(offstr);
745*01335b0dSGarrett D'Amore 		if ((strncmp(offstr, "0x", 2) == 0)) {
746*01335b0dSGarrett D'Amore 			afmt = "%07llx";
747*01335b0dSGarrett D'Amore 			base = 16;
748*01335b0dSGarrett D'Amore 			offstr += 2;
749*01335b0dSGarrett D'Amore 			if (offstr[l - 1] == 'B') {
750*01335b0dSGarrett D'Amore 				offstr[l - 1] = 0;
751*01335b0dSGarrett D'Amore 				l--;
752*01335b0dSGarrett D'Amore 				mult = 512;
753*01335b0dSGarrett D'Amore 			}
754*01335b0dSGarrett D'Amore 		} else {
755*01335b0dSGarrett D'Amore 			base = 8;
756*01335b0dSGarrett D'Amore 			afmt = "%07llo";
757*01335b0dSGarrett D'Amore 			if ((offstr[l - 1] == 'B') || (offstr[l - 1] == 'b')) {
758*01335b0dSGarrett D'Amore 				offstr[l - 1] = 0;
759*01335b0dSGarrett D'Amore 				l--;
760*01335b0dSGarrett D'Amore 				mult = 512;
761*01335b0dSGarrett D'Amore 			}
762*01335b0dSGarrett D'Amore 			if (offstr[l - 1] == '.') {
763*01335b0dSGarrett D'Amore 				offstr[l - 1] = 0;
764*01335b0dSGarrett D'Amore 				base = 10;
765*01335b0dSGarrett D'Amore 				afmt = "%07lld";
766*01335b0dSGarrett D'Amore 			}
767*01335b0dSGarrett D'Amore 		}
768*01335b0dSGarrett D'Amore 		skip = strtoll(offstr, &eptr, base);
769*01335b0dSGarrett D'Amore 		if (*eptr != '\0') {
770*01335b0dSGarrett D'Amore 			errx(1, _("invalid offset string specified"));
771*01335b0dSGarrett D'Amore 		}
772*01335b0dSGarrett D'Amore 		skip *= mult;
773*01335b0dSGarrett D'Amore 		offset += skip;
774*01335b0dSGarrett D'Amore 	}
775*01335b0dSGarrett D'Amore 
776*01335b0dSGarrett D'Amore 	/*
777*01335b0dSGarrett D'Amore 	 * Allocate an array for all the input files.
778*01335b0dSGarrett D'Amore 	 */
779*01335b0dSGarrett D'Amore 	if (argc > optind) {
780*01335b0dSGarrett D'Amore 		files = calloc(sizeof (char *), argc - optind);
781*01335b0dSGarrett D'Amore 		for (i = 0; i < argc - optind; i++) {
782*01335b0dSGarrett D'Amore 			files[i] = argv[optind + i];
783*01335b0dSGarrett D'Amore 			numfiles++;
784*01335b0dSGarrett D'Amore 		}
785*01335b0dSGarrett D'Amore 		input = next_input();
786*01335b0dSGarrett D'Amore 	} else {
787*01335b0dSGarrett D'Amore 		input = stdin;
788*01335b0dSGarrett D'Amore 	}
789*01335b0dSGarrett D'Amore 
790*01335b0dSGarrett D'Amore 	/*
791*01335b0dSGarrett D'Amore 	 * We need to seek ahead.  fseek would be faster.
792*01335b0dSGarrett D'Amore 	 */
793*01335b0dSGarrett D'Amore 	while (skip && (input != NULL)) {
794*01335b0dSGarrett D'Amore 		struct stat sbuf;
795*01335b0dSGarrett D'Amore 
796*01335b0dSGarrett D'Amore 		/*
797*01335b0dSGarrett D'Amore 		 * Only fseek() on regular files.  (Others
798*01335b0dSGarrett D'Amore 		 * we have to read().
799*01335b0dSGarrett D'Amore 		 */
800*01335b0dSGarrett D'Amore 		if (fstat(fileno(input), &sbuf) < 0) {
801*01335b0dSGarrett D'Amore 			warn("fstat: %s", files[curfile-1]);
802*01335b0dSGarrett D'Amore 			input = next_input();
803*01335b0dSGarrett D'Amore 			continue;
804*01335b0dSGarrett D'Amore 		}
805*01335b0dSGarrett D'Amore 		if (S_ISREG(sbuf.st_mode)) {
806*01335b0dSGarrett D'Amore 			/*
807*01335b0dSGarrett D'Amore 			 * No point in seeking a file that is too
808*01335b0dSGarrett D'Amore 			 * short to begin with.
809*01335b0dSGarrett D'Amore 			 */
810*01335b0dSGarrett D'Amore 			if (sbuf.st_size < skip) {
811*01335b0dSGarrett D'Amore 				skip -= sbuf.st_size;
812*01335b0dSGarrett D'Amore 				input = next_input();
813*01335b0dSGarrett D'Amore 				continue;
814*01335b0dSGarrett D'Amore 			}
815*01335b0dSGarrett D'Amore 			if (fseeko(input, skip, SEEK_SET) < 0) {
816*01335b0dSGarrett D'Amore 				err(1, "fseek:%s", files[curfile-1]);
817*01335b0dSGarrett D'Amore 			}
818*01335b0dSGarrett D'Amore 			/* Done seeking. */
819*01335b0dSGarrett D'Amore 			skip = 0;
820*01335b0dSGarrett D'Amore 			break;
821*01335b0dSGarrett D'Amore 		}
822*01335b0dSGarrett D'Amore 
823*01335b0dSGarrett D'Amore 		/*
824*01335b0dSGarrett D'Amore 		 * fgetc seems like it would be slow, but it uses
825*01335b0dSGarrett D'Amore 		 * buffered I/O, so it should be fast enough.
826*01335b0dSGarrett D'Amore 		 */
827*01335b0dSGarrett D'Amore 		flockfile(input);
828*01335b0dSGarrett D'Amore 		while (skip) {
829*01335b0dSGarrett D'Amore 			if (getc_unlocked(input) == EOF) {
830*01335b0dSGarrett D'Amore 				funlockfile(input);
831*01335b0dSGarrett D'Amore 				if (ferror(input)) {
832*01335b0dSGarrett D'Amore 					warn("read: %s", files[curfile-1]);
833*01335b0dSGarrett D'Amore 				}
834*01335b0dSGarrett D'Amore 				input = next_input();
835*01335b0dSGarrett D'Amore 				if (input != NULL) {
836*01335b0dSGarrett D'Amore 					flockfile(input);
837*01335b0dSGarrett D'Amore 				}
838*01335b0dSGarrett D'Amore 				break;
839*01335b0dSGarrett D'Amore 			}
840*01335b0dSGarrett D'Amore 			skip--;
841*01335b0dSGarrett D'Amore 		}
842*01335b0dSGarrett D'Amore 		if (input != NULL)
843*01335b0dSGarrett D'Amore 			funlockfile(input);
844*01335b0dSGarrett D'Amore 	}
845*01335b0dSGarrett D'Amore 
846*01335b0dSGarrett D'Amore 	if (head == NULL) {
847*01335b0dSGarrett D'Amore 		add_out(&output_oct_w);
848*01335b0dSGarrett D'Amore 	}
849*01335b0dSGarrett D'Amore 
850*01335b0dSGarrett D'Amore 	buffer.navail = 0;
851*01335b0dSGarrett D'Amore 	buffer.prod = 0;
852*01335b0dSGarrett D'Amore 	buffer.cons = 0;
853*01335b0dSGarrett D'Amore 
854*01335b0dSGarrett D'Amore 	for (refill(&buffer); buffer.navail > 0; refill(&buffer)) {
855*01335b0dSGarrett D'Amore 		output_t *out;
856*01335b0dSGarrett D'Amore 		int	mx;
857*01335b0dSGarrett D'Amore 		int	j, k;
858*01335b0dSGarrett D'Amore 
859*01335b0dSGarrett D'Amore 		/*
860*01335b0dSGarrett D'Amore 		 * If this buffer was the same as last, then just
861*01335b0dSGarrett D'Amore 		 * dump an asterisk.
862*01335b0dSGarrett D'Amore 		 */
863*01335b0dSGarrett D'Amore 		if ((!first) && (buffer.navail >= blocksize) && (!doall)) {
864*01335b0dSGarrett D'Amore 			j = buffer.cons;
865*01335b0dSGarrett D'Amore 			k = j - blocksize;
866*01335b0dSGarrett D'Amore 			for (i = 0; i < blocksize; i++) {
867*01335b0dSGarrett D'Amore 				if (buffer.data[j & buffer.mask] !=
868*01335b0dSGarrett D'Amore 				    buffer.data[k & buffer.mask]) {
869*01335b0dSGarrett D'Amore 					break;
870*01335b0dSGarrett D'Amore 				}
871*01335b0dSGarrett D'Amore 				j++;
872*01335b0dSGarrett D'Amore 				k++;
873*01335b0dSGarrett D'Amore 			}
874*01335b0dSGarrett D'Amore 			if (i == blocksize) {
875*01335b0dSGarrett D'Amore 				if (!same) {
876*01335b0dSGarrett D'Amore 					(void) fputs("*\n", stdout);
877*01335b0dSGarrett D'Amore 					same = B_TRUE;
878*01335b0dSGarrett D'Amore 				}
879*01335b0dSGarrett D'Amore 				buffer.navail -= blocksize;
880*01335b0dSGarrett D'Amore 				offset += blocksize;
881*01335b0dSGarrett D'Amore 				buffer.cons += blocksize;
882*01335b0dSGarrett D'Amore 				buffer.cons &= buffer.mask;
883*01335b0dSGarrett D'Amore 				continue;
884*01335b0dSGarrett D'Amore 			}
885*01335b0dSGarrett D'Amore 		}
886*01335b0dSGarrett D'Amore 
887*01335b0dSGarrett D'Amore 		first = B_FALSE;
888*01335b0dSGarrett D'Amore 		same = B_FALSE;
889*01335b0dSGarrett D'Amore 		mx = (buffer.navail > blocksize) ? blocksize : buffer.navail;
890*01335b0dSGarrett D'Amore 
891*01335b0dSGarrett D'Amore 		for (out = head; out != NULL; out = out->next) {
892*01335b0dSGarrett D'Amore 
893*01335b0dSGarrett D'Amore 			if (out == head) {
894*01335b0dSGarrett D'Amore 				/*LINTED E_SEC_PRINTF_VAR_FMT*/
895*01335b0dSGarrett D'Amore 				(void) printf(afmt, offset);
896*01335b0dSGarrett D'Amore 			} else {
897*01335b0dSGarrett D'Amore 				(void) fputs(cfmt, stdout);
898*01335b0dSGarrett D'Amore 			}
899*01335b0dSGarrett D'Amore 			for (i = 0, j = buffer.cons; i < mx; i += out->width) {
900*01335b0dSGarrett D'Amore 				out->func(&buffer, j);
901*01335b0dSGarrett D'Amore 				j += out->width;
902*01335b0dSGarrett D'Amore 				j &= buffer.mask;
903*01335b0dSGarrett D'Amore 			}
904*01335b0dSGarrett D'Amore 			(void) fputs("\n", stdout);
905*01335b0dSGarrett D'Amore 		}
906*01335b0dSGarrett D'Amore 		buffer.cons += mx;
907*01335b0dSGarrett D'Amore 		buffer.cons &= buffer.mask;
908*01335b0dSGarrett D'Amore 		offset += mx;
909*01335b0dSGarrett D'Amore 		buffer.navail -= mx;
910*01335b0dSGarrett D'Amore 	}
911*01335b0dSGarrett D'Amore 	/*LINTED E_SEC_PRINTF_VAR_FMT*/
912*01335b0dSGarrett D'Amore 	(void) printf(afmt, offset);
913*01335b0dSGarrett D'Amore 	(void) fputs("\n", stdout);
914*01335b0dSGarrett D'Amore 	return (0);
915*01335b0dSGarrett D'Amore }
916