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