1 /* @(#)hdump.c	1.46 21/02/07 Copyright 1986-2021 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)hdump.c	1.46 21/02/07 Copyright 1986-2021 J. Schilling";
6 #endif
7 /*
8  *	hex dump for files
9  *	od "octal" dump
10  *
11  *	This is an attempt to implement POSIX behavior as well as traditional
12  *	Solaris od(1) behavior.
13  *
14  *	Note that we do not implement bugs and we do not implement apparent
15  *	behavior from other implementations but documented behavior for the
16  *	programs /usr/bin/od and /usr/xpg4/bin/od on Solaris and we of course
17  *	follow the POSIX standard.
18  *
19  *	As we use getexecname() to distinct between the POSIX interface and the
20  *	traditional Solaris interface, /usr/bin/od and /usr/xpg4/bin/od must be
21  *	hard linked. Symlinks would be followed by getexecname().
22  *
23  *	Copyright (c) 1986-2021 J. Schilling
24  */
25 /*
26  * The contents of this file are subject to the terms of the
27  * Common Development and Distribution License, Version 1.0 only
28  * (the "License").  You may not use this file except in compliance
29  * with the License.
30  *
31  * See the file CDDL.Schily.txt in this distribution for details.
32  * A copy of the CDDL is also available via the Internet at
33  * http://www.opensource.org/licenses/cddl1.txt
34  *
35  * When distributing Covered Code, include this CDDL HEADER in each
36  * file and include the License file CDDL.Schily.txt from this distribution.
37  */
38 
39 #include <schily/stdio.h>
40 #include <schily/standard.h>
41 #include <schily/stdlib.h>
42 #include <schily/unistd.h>
43 #include <schily/fcntl.h>	/* O_BINARY */
44 #include <schily/types.h>	/* To make off_t available */
45 #include <schily/utypes.h>
46 #include <schily/schily.h>
47 #include <schily/getargs.h>
48 #include <schily/align.h>
49 #include <schily/nlsdefs.h>
50 #include <schily/io.h>		/* for setmode() prototype */
51 #include <schily/limits.h>	/* for  MB_LEN_MAX	*/
52 #include <schily/ctype.h>	/* For isprint()	*/
53 #include <schily/wchar.h>	/* wchar_t		*/
54 #include <schily/wctype.h>	/* For iswprint()	*/
55 
56 /*
57  * K&R cpp does not permit us to use spaces in the K&R concat method below,
58  * so we are forced to use the special CSTYLED comment in order to keep the
59  * indentation lint program "cstyle" quiet.
60  */
61 #if	defined(PROTOTYPES)
62 #define	CONCAT(a, b)	a##b
63 #else
64 			/* CSTYLED */
65 #define	CONCAT(a, b)	a/**/b
66 #endif
67 
68 #define	octdig(x)	(x >= '0' && x <= '7')
69 #ifndef	TRUE
70 #define	TRUE		1
71 #define	FALSE		0
72 #endif
73 
74 /*
75  * The current display state
76  */
77 typedef struct dstate {
78 	FILE	*f;		/* The current input file		*/
79 	int	imcnt;		/* Count from intermediate buffer	*/
80 	int	rest;		/* Unprinted octets + 1			*/
81 	BOOL	printable;	/* Partial character was printable	*/
82 	int	blocksize;	/* The display buffer size		*/
83 	const char *inname;	/* The current input file name		*/
84 	int	argc;		/* argc for the rest of the files	*/
85 	char	*const *argv;	/* argv[0] points to current file	*/
86 	int	excode;		/* The exit code for delayed errors	*/
87 } dst_t;
88 
89 typedef	int	(*pfun)	__PR((const char *fmt, void *valp));
90 typedef	int	(*pbfun)__PR((int cnt, char *buf, dst_t *dstp));
91 
92 typedef struct pr pr_t;
93 struct pr {
94 	size_t	pr_size;	/* Object size for one element		*/
95 	int	pr_fieldw;	/* Output-fieldwidth for one element	*/
96 	char	*pr_fmt;	/* Printf() format for pr_out		*/
97 	pfun	pr_out;		/* Element output function		*/
98 	pbfun	pr_block;	/* Line-block output function		*/
99 	int	pr_fill;	/* Number of ' ' fill characters	*/
100 	pr_t	*pr_next;
101 };
102 
103 /*
104  * The K&R CONCAT does not permit us to put a space after the after the comma
105  * in a CONCAT() call, so we are forced to use the special CSTYLED comment in
106  * order to keep the indentation lint program "cstyle" quiet.
107  */
108 #define	DEF_PR(name, width, type, format)				\
109 			/* CSTYLED */					\
110 		LOCAL int CONCAT(pr_,name) __PR((const char *fmt, void *vp));\
111 		LOCAL int						\
112 			/* CSTYLED */					\
113 		CONCAT(pr_,name) (fmt, vp)				\
114 			const char *fmt;				\
115 			void	*vp;					\
116 		{							\
117 			return (printf(fmt, *(type *)vp));		\
118 		}							\
119 				/* CSTYLED */				\
120 		LOCAL	pr_t	CONCAT(d_,name) = { sizeof (type),	\
121 					width,				\
122 					format,				\
123 					/* CSTYLED */			\
124 					CONCAT(pr_,name),		\
125 					0,				\
126 					0, 0 }
127 
128 
129 LOCAL BOOL	is_od = FALSE;		/* This is "od" and not "hdump"	   */
130 LOCAL BOOL	is_xpg4 = FALSE;	/* Called as /usr/xpg4/bin/od	   */
131 LOCAL BOOL	is_posix = FALSE;	/* Used POSIX option (-A/-j/-N/-t) */
132 LOCAL BOOL	is_pipe;		/* Whether stdout is a pipe	   */
133 LOCAL int	curradix;		/* Radix found by myatoll()	   */
134 LOCAL int	maxline = 0;		/* Max with in output formats	   */
135 LOCAL char	*addrfmt;		/* Address printf() format	   */
136 LOCAL char	*samefmt;		/* printf() format for same data   */
137 LOCAL off_t	pos = (off_t)0;		/* Position used for address label */
138 
139 LOCAL BOOL	dflag = FALSE;	/* -d od -tu2 / hdump -d (decimal)	   */
140 LOCAL BOOL	oflag = FALSE;	/* -o hdump: switch to octal		   */
141 LOCAL BOOL	lflag = FALSE;	/* -l hdump: switch to long		   */
142 LOCAL BOOL	uflag = FALSE;	/* -u hdump: switch to unsigned		   */
143 LOCAL BOOL	tflag = FALSE;	/* -t was seen, POSIX interface requested  */
144 LOCAL BOOL	lenflag = FALSE; /* A bytecount limiting argument was seen */
145 LOCAL BOOL	vflag = FALSE;	/* -v show all input data		   */
146 
147 LOCAL	void	usage	__PR((int exitcode));
148 LOCAL	const char *filename __PR((const char *name));
149 EXPORT	int	main	__PR((int ac, char **av));
150 LOCAL	void	checkfill __PR((dst_t *dstp));
151 LOCAL	int	gettype	__PR((const char *arg, void *valp));
152 LOCAL	void	add_dout __PR((pr_t *this));
153 LOCAL	int	add_out	__PR((const char *arg, long *valp));
154 LOCAL	int	add_fmt	__PR((const char *arg, long *valp));
155 LOCAL	int	opt_b	__PR((const char *arg, long *valp));
156 LOCAL	int	opt_c	__PR((const char *arg, long *valp));
157 LOCAL	int	opt_d	__PR((const char *arg, long *valp));
158 LOCAL	int	opt_l	__PR((const char *arg, long *valp));
159 LOCAL	int	opt_o	__PR((const char *arg, long *valp));
160 LOCAL	int	opt_u	__PR((const char *arg, long *valp));
161 LOCAL	void	dump	__PR((off_t len, dst_t *dstp));
162 LOCAL	void	prbuf	__PR((int cnt, char *obuf, dst_t *dstp));
163 LOCAL	int	prasc	__PR((int cnt, char *buf, dst_t *dstp));
164 LOCAL	int	prch	__PR((int cnt, char *buf, dst_t *dstp));
165 LOCAL	int	prmbch	__PR((int cnt, char *buf, dst_t *dstp));
166 LOCAL	int	prcbytes __PR((int cnt, char *buf, dst_t *dstp));
167 LOCAL	int	prbbytes __PR((int cnt, char *buf, dst_t *dstp));
168 LOCAL	int	prascii	__PR((int cnt, char *buf, dst_t *dstp));
169 LOCAL	int	prlong	__PR((int cnt, char *buf, dst_t *dstp));
170 LOCAL	int	prshort	__PR((int cnt, char *buf, dst_t *dstp));
171 LOCAL	BOOL	bufeql	__PR((long *b1, long *b2, int cnt));
172 LOCAL	Llong	myatoll	__PR((char *s));
173 LOCAL	int	read_input __PR((dst_t *dstp, char *bp, int cnt));
174 LOCAL	BOOL	open_next __PR((dst_t *dstp));
175 LOCAL	off_t	advance	__PR((dst_t *dstp, off_t xpos));
176 LOCAL	off_t	doadvance __PR((FILE *f, off_t xpos, BOOL is_last));
177 LOCAL	off_t	doskip	__PR((FILE *f, off_t xpos));
178 LOCAL	BOOL	out_ispipe __PR((void));
179 LOCAL	int	ptype	__PR((const char *arg));
180 LOCAL	int	illsize	__PR((int size));
181 LOCAL	int	illfsize __PR((int size));
182 LOCAL	int	illtype	__PR((int type));
183 LOCAL	void	setaddrfmt __PR((int Aflag, int lradix));
184 
185 /*
186  * These output descriptors are object oriented.
187  * They are called once per element.
188  */
189 
190 /*
191  * Decimal formats
192  */
193 DEF_PR(c_d,  4, char,  " %3hhd");
194 DEF_PR(s_d,  7, short, " %6.5hd");
195 DEF_PR(i_d, 12, int,   " %11.10d");
196 #if	SIZEOF_LONG_INT > SIZEOF_INT
197 DEF_PR(l_d, 21, long,  " %20.18ld");
198 #else
199 #define	d_l_d	d_i_d
200 #endif
201 #if	SIZEOF_LONG_LONG > SIZEOF_LONG_INT
202 DEF_PR(ll_d, 21, Llong, " %20.18lld");
203 #endif
204 
205 /*
206  * Octal formats
207  */
208 DEF_PR(c_o,  4, char,  " %3.3hho");
209 DEF_PR(s_o,  7, short, " %6.6ho");
210 DEF_PR(i_o, 12, int,   " %11.11o");
211 #if	SIZEOF_LONG_INT > SIZEOF_INT
212 DEF_PR(l_o, 23, long,  " %22.22lo");
213 #else
214 #define	d_l_o	d_i_o
215 #endif
216 #if	SIZEOF_LONG_LONG > SIZEOF_LONG_INT
217 DEF_PR(ll_o, 23, Llong, " %22.22llo");
218 #endif
219 
220 /*
221  * Unsigned decimal formats
222  */
223 DEF_PR(c_u,  4, char,  " %3.3hhu");
224 DEF_PR(s_u,  6, short, " %5.5hu");
225 DEF_PR(i_u, 11, int,   " %10.10u");
226 #if	SIZEOF_LONG_INT > SIZEOF_INT
227 DEF_PR(l_u, 21, long,   " %20.20lu");
228 #else
229 #define	d_l_u	d_i_u
230 #endif
231 #if	SIZEOF_LONG_LONG > SIZEOF_LONG_INT
232 DEF_PR(ll_u, 21, Llong, " %20.20llu");
233 #endif
234 
235 /*
236  * Hexadecimal formats
237  */
238 DEF_PR(c_x,  3, char,  " %2.2hhx");
239 DEF_PR(s_x,  5, short, " %4.4hx");
240 DEF_PR(i_x,  9, int,   " %8.8x");
241 #if	SIZEOF_LONG_INT > SIZEOF_INT
242 DEF_PR(l_x, 17, long,  " %16.16lx");
243 #else
244 #define	d_l_x	d_i_x
245 #endif
246 #if	SIZEOF_LONG_LONG > SIZEOF_LONG_INT
247 DEF_PR(ll_x, 17, Llong, " %16.16llx");
248 #endif
249 
250 /*
251  * Floatingpoint formats
252  */
253 #ifndef	NO_FLOATINGPOINT
254 #define	FLOAT_AS_ON_SUNOS
255 #ifdef	FLOAT_AS_ON_SUNOS
256 DEF_PR(f_f, 15, float, " %14.7e");
257 #else
258 DEF_PR(f_f, 14, float, " %13.6e");
259 #endif
260 DEF_PR(d_f, 23, double, " %22.14e");
261 #ifdef	HAVE_LONGDOUBLE
262 DEF_PR(ld_f, 24, long double, " %23.14Le");
263 #endif
264 #endif
265 
266 /*
267  * These output descriptors are block oriented.
268  * They are called once per line.
269  */
270 LOCAL	pr_t	d_asc = { sizeof (char), 4, "", 0, prasc };	/* od -ta   */
271 LOCAL	pr_t	d_ch  = { sizeof (char), 4, "", 0, prch };	/* od -c    */
272 LOCAL	pr_t	d_mbch = { sizeof (char), 4, "", 0, prmbch };	/* od -C/-tc */
273 
274 LOCAL	pr_t	d_ascii = { sizeof (char), 3, "", 0, prascii };	/* hd -a    */
275 LOCAL	pr_t	d_bbytes = { sizeof (char), 4, "", 0, prbbytes }; /* hd -b  */
276 LOCAL	pr_t	d_cbytes = { sizeof (char), 4, "", 0, prcbytes }; /* hd -c  */
277 LOCAL	pr_t	d_short = { sizeof (short), 4, "", 0, prshort }; /* hd	    */
278 LOCAL	pr_t	d_long = { sizeof (long), 4, "", 0, prlong };	/* hd -l    */
279 
280 LOCAL	pr_t	*pr_root;
281 LOCAL	pr_t	**pr_tail = &pr_root;
282 
283 
284 LOCAL void
usage(exitcode)285 usage(exitcode)
286 	int	exitcode;
287 {
288 	error(_(
289 	"Usage:	%s [options] [file] [[+]starting address[.][b|B]%s]\n"),
290 	is_od?"od":"hdump", is_od?"":_(" [count]]"));
291 	error(_(
292 "Usage:	%s [options] [-t type]... [-A base] [-j skip] [-N count] [file...]\n"),
293 	is_od?"od":"hdump");
294 	error(_("Options:\n"));
295 	error(_("\t-A c\tSet address base c ('d', 'o', 'n' or 'x')\n"));
296 	error(_("\t-j skip\tSkip input for the files\n"));
297 	error(_("\t-N n\tOnly process n bytes\n"));
298 	error(_("\t-t type\tSpecify output format type\n"));
299 	error(_("\t-a\tDisplay content also in characters\n"));
300 	error(_("\t-b\tDisplay content in bytes\n"));
301 	error(_("\t-c\tDisplay content as %s quoted characters\n"),
302 			is_xpg4 ? _("single or multi byte") : _("single byte"));
303 	error(_("\t-C\tDisplay content as %s quoted characters\n"),
304 				_("single or multi byte"));
305 	error(_("\t-d\tDisplay content in decimal%s\n"),
306 			is_od ? " -tu2" : "");
307 	error(_("\t-D\tDisplay content in decimal -tu4\n"));
308 #ifndef	NO_FLOATINGPOINT
309 	error(_("\t-f\tDisplay content as floats\n"));
310 	error(_("\t-F\tDisplay content as doubles\n"));
311 #endif
312 	if (!is_od)
313 	error(_("\t-l\tDisplay content as longs\n"));
314 	error(_("\t-o\tDisplay content in octal%s\n"),
315 			is_od ? " -to2" : "");
316 	error(_("\t-O\tDisplay content in octal -to4\n"));
317 	error(_("\t-s\tDisplay content in decimal -td2\n"));
318 	error(_("\t-S\tDisplay content in decimal -td4\n"));
319 	if (!is_od)
320 	error(_("\t-u\tDisplay content as unsigned\n"));
321 	error(_("\t-v\tShow all data even if it is identical\n"));
322 	error(_("\t-x\tDisplay content in hexadecimal -tx2\n"));
323 	error(_("\t-X\tDisplay content in hexadecimal -tx4\n"));
324 	error(_("\t-help\tPrint this help.\n"));
325 	error(_("\t-version\tPrint version number.\n"));
326 	error(_("'b' after starting address multiplies with 512\n"));
327 	exit(exitcode);
328 }
329 
330 #include <schily/string.h>
331 LOCAL const char *
filename(name)332 filename(name)
333 	const char	*name;
334 {
335 	char	*p;
336 
337 	if ((p = strrchr(name, '/')) == NULL)
338 		return (name);
339 	return (++p);
340 }
341 
342 EXPORT int
main(ac,av)343 main(ac, av)
344 	int	ac;
345 	char	*av[];
346 {
347 	off_t	len = (off_t)0;
348 	char	*options =
349 "A?,j&,N&,t&,a~,b~,c~,C~,d~,D~,f~,F~,l~,o~,O~,s~,S~,u~,v,x~,X~,help,version";
350 	BOOL	help = FALSE;
351 	BOOL	prversion = FALSE;
352 	BOOL	didoffset = FALSE;
353 	char	Aflag = '\0';
354 	Llong	skip = 0;
355 	Llong	nbytes = 0;
356 	int	lradix = 16;		/* hdump defaults to hex. radix */
357 	int	cac;
358 	char	* const * cav;
359 #if	defined(USE_NLS)
360 	char	*dir;
361 #endif
362 	dst_t	dst;
363 	struct ga_props ga_props;
364 
365 	save_args(ac, av);
366 
367 	(void) setlocale(LC_ALL, "");
368 
369 #if	defined(USE_NLS)
370 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
371 #define	TEXT_DOMAIN "hdump"	/* Use this only if it weren't */
372 #endif
373 	dir = searchfileinpath("share/locale", F_OK,
374 					SIP_ANY_FILE|SIP_NO_PATH, NULL);
375 	if (dir)
376 		(void) bindtextdomain(TEXT_DOMAIN, dir);
377 	else
378 #ifdef	PROTOTYPES
379 	(void) bindtextdomain(TEXT_DOMAIN, INS_BASE "/share/locale");
380 #else
381 	(void) bindtextdomain(TEXT_DOMAIN, "/usr/share/locale");
382 #endif
383 	(void) textdomain(TEXT_DOMAIN);
384 #endif
385 
386 	/*
387 	 * First set defaults related to the current CLI variant.
388 	 */
389 	if (streql(filename(av[0]), "od")) {		/* "od" interface? */
390 		const char	*exname;
391 
392 		is_od = TRUE;				/* "od" behavior   */
393 		lradix = 8;				/* "od" def is octal */
394 #ifdef	HAVE_GETEXECNAME
395 		exname = getexecname();
396 #else
397 		exname = getexecpath();
398 #endif
399 		if (exname &&
400 		    strstr(exname, "/xpg4")) {		/* X-Open interface? */
401 			/*
402 			 * This makes the behavior for the -c option depend
403 			 * on the current locale instead of using single byte
404 			 * characters as with the AT&T "od".
405 			 *
406 			 * It also disables to use "-" as a synonym for stdin
407 			 * and it modifies the way the "offset" argument past
408 			 * a single file argument is parsed.
409 			 */
410 			is_xpg4 = TRUE;
411 		}
412 	}
413 
414 	cac = --ac;
415 	cav = ++av;
416 
417 	/*
418 	 * POSIX doesn't permit +opt and requires support for combined
419 	 * single char options with the one having an argument. If we did
420 	 * use the macro GAF_POSIX instead, we would include GAF_NO_EQUAL.
421 	 */
422 	getarginit(&ga_props, GAF_NO_PLUS|GAF_SINGLEARG);
423 	if (getlargs(&cac, &cav, &ga_props, options,
424 			&Aflag,
425 			getllnum, &skip,	/* -j skip	*/
426 			getllnum, &nbytes,	/* -N bytes	*/
427 			gettype, NULL,		/* -t type	*/
428 			add_out, &d_ascii,	/* -a		*/
429 			opt_b, NULL,		/* -b		*/
430 			opt_c, NULL,		/* -c		*/
431 			add_out, &d_mbch,	/* -C		*/
432 			opt_d, NULL,		/* -d		*/
433 			add_fmt, "u4",		/* -D		*/
434 			add_fmt, "f4",		/* -f		*/
435 			add_fmt, "f8",		/* -F		*/
436 			opt_l, NULL,		/* -l (hdump)	*/
437 			opt_o, NULL,		/* -o		*/
438 			add_fmt, "o4",		/* -O		*/
439 			add_fmt, "d2",		/* -s		*/
440 			add_fmt, "d4",		/* -S		*/
441 			opt_u, NULL,		/* -u (hdump)	*/
442 			&vflag,			/* -v		*/
443 			add_fmt, "x2",		/* -x		*/
444 			add_fmt, "x4",		/* -X		*/
445 						&help, &prversion) < 0) {
446 		error(_("Bad flag: '%s'.\n"), cav[0]);
447 		usage(1);
448 	}
449 	if (help)
450 		usage(0);
451 	if (prversion) {
452 		printf(
453 		_("%s release %s %s (%s-%s-%s) Copyright (C) 1986-2021 %s\n"),
454 				is_od ? "Od":"Hdump",
455 				"1.46", "2021/02/07",
456 				HOST_CPU, HOST_VENDOR, HOST_OS,
457 				_("Joerg Schilling"));
458 		exit(0);
459 	}
460 	is_pipe = out_ispipe();		/* Flush output if we write to a pipe */
461 
462 	if (skip < 0)
463 		comerrno(EX_BAD, _("Invalid offset %lld.\n"), skip);
464 	if (skip > 0) {
465 		is_posix = TRUE;	/* Using -j skip switches to POSIX */
466 		pos = skip;
467 		if (pos != skip) {
468 			comerrno(EX_BAD,
469 			_("Offset %lld is too large for type 'off_t'.\n"),
470 				skip);
471 		}
472 	}
473 	if (nbytes < 0)
474 		comerrno(EX_BAD, _("Invalid number of bytes.\n"));
475 	if (nbytes > 0) {
476 		is_posix = TRUE;	/* Using -N bytes switches to POSIX */
477 		lenflag = TRUE;
478 		len = nbytes;
479 		if (len != nbytes) {
480 			comerrno(EX_BAD,
481 		_("Number of bytes %lld is too large for type 'off_t'.\n"),
482 				nbytes);
483 		}
484 	}
485 	if (Aflag || tflag)
486 		is_posix = TRUE;	/* Using -A/-t switches to POSIX */
487 
488 	cac = ac;
489 	cav = av;
490 	dst.excode = 0;
491 	dst.inname = NULL;
492 	dst.f = (FILE *)NULL;
493 	if (getlfiles(&cac, &cav, &ga_props, options) <= 0) {	/* Skip opts */
494 		dst.argc = -2;
495 		dst.argv = cav;
496 	} else {
497 		if (cac == 1 && cav[0][0] == '+')	/* If called od +off */
498 			dst.argc = -2;			/* mark to use stdin */
499 		else
500 			dst.argc = cac;
501 		dst.argv = cav;
502 	}
503 	open_next(&dst);				/* Open first file */
504 	if (dst.f == (FILE *)NULL)
505 		return (dst.excode);
506 
507 	/*
508 	 * Permit to call "od +offset" (use old skip syntax and dump stdin).
509 	 */
510 	if (!(cac == 1 && cav[0][0] == '+')) {
511 		cac--, cav++;
512 	}
513 	/*
514 	 * Only when not in POSIX CLI mode, use the historical offset arg.
515 	 * POSIX CLI mode is not related to the POSIX binary path, but used
516 	 * when the POSIX options -A/-j/-N/-t are part of the current
517 	 * command line.
518 	 */
519 	if (!is_posix && ((is_od && cac == 1) || (!is_od && cac > 0))) {
520 		char	*arg = cav[0];
521 
522 		if ((is_xpg4 &&		/* This is for /usr/xpg4/bin/od */
523 		    strchr("+0123456789", *arg)) ||
524 		    (!is_xpg4 &&	/* This is for /usr/bin/od and hdump */
525 		    (strchr("+0123456789", *arg) ||
526 		    (arg[0] == 'x' && arg[1] == '\0') ||
527 		    (arg[0] == 'x' && strchr("0123456789abcdef", arg[1])) ||
528 		    (arg[0] == '.' && arg[1] == '\0')))) {
529 			if (*arg == '+')
530 				arg++;
531 			pos = (off_t)myatoll(arg);
532 			if (!is_od)
533 				pos &= ~((off_t)1);
534 			lradix = curradix;
535 			didoffset = TRUE;
536 			cac--, cav++;
537 			dst.argc--;
538 		}
539 	}
540 	if (!is_od && didoffset && cac > 0) {
541 		/*
542 		 * Hdump supports an additional "count" argument when
543 		 * only a single file has been specified.
544 		 */
545 		len = (off_t)myatoll(cav[0]);
546 		lenflag = TRUE;
547 		cac--; cav++;
548 		dst.argc--;
549 
550 		if (cac > 0) {
551 			errmsgno(EX_BAD,
552 				_("Unexpected argument '%s'.\n"), cav[0]);
553 			usage(1);
554 		}
555 	}
556 
557 	setaddrfmt(Aflag, lradix);	/* Set format for address labels */
558 
559 	if (pos > 0) {
560 		off_t	newpos = advance(&dst, pos);
561 
562 		if (newpos > 0)
563 			comerrno(-2, _("Cannot skip past EOF.\n"));
564 		/*
565 		 * In case of a POSIX -j offset spec, start with address
566 		 * label 0, otherwise use the address label that matches pos.
567 		 */
568 		if (skip > 0)
569 			pos = 0;
570 	}
571 	if (pr_root == NULL) {				/* No pr format yet? */
572 		if (is_od) {
573 			add_dout(&d_s_o);		/* od default: -toS  */
574 		} else {
575 			if (lflag)
576 				add_dout(&d_long);
577 			else
578 				add_dout(&d_short);
579 		}
580 	}
581 	checkfill(&dst);
582 	dump(len, &dst);
583 	return (dst.excode);
584 }
585 
586 /*
587  * Check how many fill characters are needed for each format in order to
588  * align the output according to the POSIX standard.
589  */
590 LOCAL void
checkfill(dstp)591 checkfill(dstp)
592 	dst_t	*dstp;
593 {
594 	pr_t	*dp;
595 	int	kgv = 1;
596 	int	n;
597 
598 	for (dp = pr_root; dp != NULL; dp = dp->pr_next) {
599 		if (kgv % dp->pr_size) {
600 			n = kgv;
601 			while (n % dp->pr_size)
602 				n += kgv;
603 			kgv = n;
604 		}
605 	}
606 	while (kgv < 12)
607 		kgv *= 2;
608 	dstp->blocksize = kgv;
609 
610 	for (dp = pr_root, n = 0; dp != NULL; dp = dp->pr_next) {
611 		int	lmax;
612 		int	nmax;
613 
614 		lmax = dstp->blocksize / dp->pr_size * dp->pr_fieldw;
615 		if (lmax > maxline)
616 			maxline = lmax;
617 		nmax = dstp->blocksize / dp->pr_size;
618 		if (nmax > n)
619 			n = nmax;
620 	}
621 	/*
622 	 * Round up to the next value that is dividable by the highest
623 	 * number of objects per line.
624 	 */
625 	while (maxline > (maxline / n * n))
626 		maxline++;
627 	for (dp = pr_root; dp != NULL; dp = dp->pr_next) {
628 		dp->pr_fill =
629 			maxline/(dstp->blocksize/dp->pr_size)-dp->pr_fieldw;
630 	}
631 }
632 
633 /*
634  * Get the type for the -t type option.
635  * Mark that we did call the type parser along with -t.
636  */
637 /* ARGSUSED */
638 LOCAL int
gettype(arg,valp)639 gettype(arg, valp)
640 	const	char	*arg;
641 		void	*valp;
642 {
643 	tflag = TRUE;
644 	return (ptype(arg));
645 }
646 
647 /*
648  * Append an output definition to our current list.
649  */
650 LOCAL void
add_dout(this)651 add_dout(this)
652 	pr_t	*this;
653 {
654 	pr_t	*new = malloc(sizeof (*new));
655 
656 	if (new == NULL)
657 		comerr(_("No memory.\n"));
658 
659 	*new = *this;
660 	new->pr_next = (pr_t *)0;
661 	*pr_tail = new;
662 	pr_tail = &new->pr_next;
663 }
664 
665 /*
666  * Append an output definition to our current list.
667  * This variant is called as getargs() callback.
668  */
669 /* ARGSUSED */
670 LOCAL int
add_out(arg,valp)671 add_out(arg, valp)
672 	const	char	*arg;
673 		long	*valp;
674 {
675 	add_dout((pr_t *)valp);
676 	return (1);
677 }
678 
679 /*
680  * Append an output definition from type string.
681  * This variant is called as getargs() callback.
682  */
683 /* ARGSUSED */
684 LOCAL int
add_fmt(arg,valp)685 add_fmt(arg, valp)
686 	const	char	*arg;
687 		long	*valp;
688 {
689 	return (ptype((char *)valp));
690 }
691 
692 /*
693  * getargs() callback to implement -b for bin/hdump, bin/od and xpg4/bin/od.
694  */
695 /* ARGSUSED */
696 LOCAL int
opt_b(arg,valp)697 opt_b(arg, valp)
698 	const	char	*arg;
699 		long	*valp;
700 {
701 	if (is_od || tflag)
702 		return (ptype("o1"));
703 	add_dout(&d_bbytes);
704 	return (1);
705 }
706 
707 /*
708  * getargs() callback to implement -c for bin/hdump, bin/od and xpg4/bin/od.
709  */
710 /* ARGSUSED */
711 LOCAL int
opt_c(arg,valp)712 opt_c(arg, valp)
713 	const	char	*arg;
714 		long	*valp;
715 {
716 	if (is_od || tflag) {
717 		if (is_xpg4)
718 			add_dout(&d_mbch);
719 		else
720 			add_dout(&d_ch);
721 		return (1);
722 	}
723 	add_dout(&d_cbytes);
724 	return (1);
725 }
726 
727 /*
728  * getargs() callback to implement -d for bin/hdump, bin/od and xpg4/bin/od.
729  */
730 /* ARGSUSED */
731 LOCAL int
opt_d(arg,valp)732 opt_d(arg, valp)
733 	const	char	*arg;
734 		long	*valp;
735 {
736 	if (is_od || tflag)
737 		return (ptype("u2"));
738 	dflag = TRUE;
739 	return (1);
740 }
741 
742 /*
743  * getargs() callback to implement -l for bin/hdump, bin/od and xpg4/bin/od.
744  */
745 /* ARGSUSED */
746 LOCAL int
opt_l(arg,valp)747 opt_l(arg, valp)
748 	const	char	*arg;
749 		long	*valp;
750 {
751 	if (is_od)
752 		return (-1);	/* -l is illegal for od(1) */
753 	lflag = TRUE;
754 	return (1);
755 }
756 
757 /*
758  * getargs() callback to implement -o for bin/hdump, bin/od and xpg4/bin/od.
759  */
760 /* ARGSUSED */
761 LOCAL int
opt_o(arg,valp)762 opt_o(arg, valp)
763 	const	char	*arg;
764 		long	*valp;
765 {
766 	if (is_od || tflag)
767 		return (ptype("o2"));
768 	oflag = TRUE;
769 	return (1);
770 }
771 
772 /*
773  * getargs() callback to implement -u for bin/hdump, bin/od and xpg4/bin/od.
774  */
775 /* ARGSUSED */
776 LOCAL int
opt_u(arg,valp)777 opt_u(arg, valp)
778 	const	char	*arg;
779 		long	*valp;
780 {
781 	if (is_od)
782 		return (-1);	/* -u is illegal for od(1) */
783 	uflag = TRUE;
784 	return (1);
785 }
786 
787 LOCAL void
dump(len,dstp)788 dump(len, dstp)
789 	register off_t	len;
790 		dst_t	*dstp;
791 {
792 		char	obuf[4 * 24];	/* 1x align 2x main buffer 1x ahead */
793 	register char	*buf;
794 	register char	*oldbuf;
795 	register char	*temp;
796 	register int	cnt;
797 
798 	/*
799 	 * Align for the worst case (This is a long double in LP64 mode)
800 	 */
801 	buf	= xalign((obuf), 16, 15);
802 	oldbuf	= buf + dstp->blocksize;
803 
804 	dstp->imcnt = 0;
805 	dstp->rest = 0;
806 	dstp->printable = FALSE;
807 
808 	do {
809 		if (lenflag) {
810 			if (len <= 0)
811 				break;
812 			cnt = len > dstp->blocksize ? dstp->blocksize:(int)len;
813 		} else {
814 			cnt = dstp->blocksize;
815 		}
816 
817 		if (dstp->imcnt > 0) {
818 			movebytes(oldbuf+dstp->blocksize, buf, dstp->imcnt);
819 			cnt = cnt < dstp->imcnt ? cnt:dstp->imcnt;
820 			dstp->imcnt = 0;
821 		} else {
822 			if ((cnt = read_input(dstp, buf, cnt)) == 0)
823 				break;
824 		}
825 		if (vflag ||
826 		    !bufeql((long *)buf, (long *)oldbuf, dstp->blocksize)) {
827 			if (cnt < dstp->blocksize)
828 				fillbytes(buf+cnt, dstp->blocksize-cnt, '\0');
829 			prbuf(cnt, buf, dstp);
830 		}
831 		pos += cnt;
832 		len -= cnt;
833 
834 		temp = oldbuf;
835 		oldbuf = buf;
836 		buf = temp;
837 	} while (!feof(dstp->f));
838 	if (*addrfmt != '\t')
839 		printf(addrfmt, pos);
840 	printf("\n");
841 }
842 
843 LOCAL void
prbuf(cnt,obuf,dstp)844 prbuf(cnt, obuf, dstp)
845 		int	cnt;
846 	register char	*obuf;
847 		dst_t	*dstp;
848 {
849 	register	pr_t	*dp;
850 
851 	/*
852 	 * cnt > 0 is granted by dump()
853 	 */
854 	printf(addrfmt, pos);
855 
856 	for (dp = pr_root; dp; dp = dp->pr_next) {
857 		if (dp->pr_block) {
858 			(*dp->pr_block)(cnt, obuf, dstp);
859 		} else {
860 			register	int	i;
861 			register	char	*cp = obuf;
862 
863 			for (i = 0; i < cnt; ) {
864 				register int k = dp->pr_fill;
865 
866 				while (--k >= 0)
867 					(void) putchar(' ');
868 
869 				(*dp->pr_out)(dp->pr_fmt, cp);
870 				i += dp->pr_size;
871 				cp += dp->pr_size;
872 			}
873 		}
874 		if (dp->pr_next)
875 			printf("\n       ");
876 		else
877 			printf("\n");
878 	}
879 }
880 
881 /*
882  * POSIX names for -ta (named characters).
883  */
884 LOCAL char *ascii_names[] = {
885 	"nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
886 	" bs", " ht", " lf", " vt", " ff", " cr", " so", " si",
887 	"dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
888 	"can", " em", "sub", "esc", " fs", " gs", " rs", " us",
889 	" sp", "  !", "  \"", "  #", "  $", "  %", "  &", "  '",
890 	"  (", "  )", "  *", "  +", "  ,", "  -", "  .", "  /",
891 	"  0", "  1", "  2", "  3", "  4", "  5", "  6", "  7",
892 	"  8", "  9", "  :", "  ;", "  <", "  =", "  >", "  ?",
893 	"  @", "  A", "  B", "  C", "  D", "  E", "  F", "  G",
894 	"  H", "  I", "  J", "  K", "  L", "  M", "  N", "  O",
895 	"  P", "  Q", "  R", "  S", "  T", "  U", "  V", "  W",
896 	"  X", "  Y", "  Z", "  [", "  \\", "  ]", "  ^", "  _",
897 	"  `", "  a", "  b", "  c", "  d", "  e", "  f", "  g",
898 	"  h", "  i", "  j", "  k", "  l", "  m", "  n", "  o",
899 	"  p", "  q", "  r", "  s", "  t", "  u", "  v", "  w",
900 	"  x", "  y", "  z", "  {", "  |", "  }", "  ~", "del"
901 };
902 
903 /*
904  * Print named 7-bit ASCII characters
905  */
906 /* ARGSUSED */
907 LOCAL int
prasc(cnt,buf,dstp)908 prasc(cnt, buf, dstp)
909 	register int	cnt;
910 	register char	*buf;
911 		dst_t	*dstp;
912 {
913 	register short	i;
914 
915 	for (i = 0; i < cnt; i++) {
916 		(void) putchar(' ');
917 		(void) fputs(ascii_names[buf[i]&0177], stdout);
918 	}
919 	return (0);
920 }
921 
922 /*
923  * Print single byte characters, quote non-printable chars.
924  */
925 /* ARGSUSED */
926 LOCAL int
prch(cnt,buf,dstp)927 prch(cnt, buf, dstp)
928 	register int	cnt;
929 	register char	*buf;
930 		dst_t	*dstp;
931 {
932 	register short	i;
933 	register Uchar	c;
934 
935 	for (i = 0; i < cnt; i++) {
936 		c = buf[i];
937 		if (isprint(c))
938 			printf(" %3c", c);
939 		else switch (c) {
940 
941 		case '\0':	printf("  \\0"); break;
942 		case ALERT:	printf("  \\a"); break;
943 		case '\b':	printf("  \\b"); break;
944 		case '\f':	printf("  \\f"); break;
945 		case '\n':	printf("  \\n"); break;
946 		case '\r':	printf("  \\r"); break;
947 		case '\t':	printf("  \\t"); break;
948 		case '\v':	printf("  \\v"); break;
949 		default:
950 				printf(" %3.3hho", c);
951 		}
952 	}
953 	return (0);
954 }
955 
956 /*
957  * Print single byte or multi-byte characters, quote non-printable chars.
958  * The character type is selected from LC_CTYPE.
959  */
960 LOCAL int
prmbch(cnt,buf,dstp)961 prmbch(cnt, buf, dstp)
962 	register int	cnt;
963 	register char	*buf;
964 		dst_t	*dstp;
965 {
966 	register short	i = 0;
967 	register int	n;
968 		wchar_t	wc = 0;
969 
970 	n = dstp->rest;
971 	dstp->rest = 0;
972 
973 	while (--n >= 0 && i < cnt) {
974 		if (dstp->printable)
975 			printf("  **");
976 		else
977 			printf(" %3.3hho", buf[i]);
978 		i++;
979 	}
980 
981 	for (; i < cnt; i++) {
982 again:
983 		n = mbtowc(&wc, &buf[i], cnt - i + dstp->imcnt);
984 		if (n < 0) {
985 			(void) mbtowc(NULL, NULL, 0);
986 			/*
987 			 * Be careful here: Solaris is buggy and sets
988 			 * MB_CUR_MAX to 3 for UTF-8 although mbtowc() may
989 			 * return larger numbers.
990 			 */
991 			if (dstp->imcnt == 0 && (cnt - i) < MB_LEN_MAX) {
992 				dstp->imcnt = read_input(dstp,
993 							buf+dstp->blocksize,
994 							dstp->blocksize);
995 				if (dstp->imcnt > 0)
996 					goto again; /* Don't increment i */
997 			}
998 		}
999 		if (n > 0 && iswprint(wc)) {
1000 			printf("%*c%.*s", 4-wcwidth(wc), ' ', n, &buf[i]);
1001 			while (--n > 0 && ++i < cnt)
1002 				printf("  **");
1003 			dstp->printable = TRUE;
1004 		} else {
1005 			dstp->printable = FALSE;
1006 			n--;
1007 			switch (wc) {
1008 
1009 			case '\0':	printf("  \\0"); break;
1010 			case ALERT:	printf("  \\a"); break;
1011 			case '\b':	printf("  \\b"); break;
1012 			case '\f':	printf("  \\f"); break;
1013 			case '\n':	printf("  \\n"); break;
1014 			case '\r':	printf("  \\r"); break;
1015 			case '\t':	printf("  \\t"); break;
1016 			case '\v':	printf("  \\v"); break;
1017 			default:
1018 				do {
1019 					printf(" %3.3hho", buf[i]);
1020 					if (++i >= cnt)
1021 						break;
1022 				} while (--n >= 0);
1023 				i--;
1024 			}
1025 		}
1026 	}
1027 	dstp->rest = n;
1028 	return (0);
1029 }
1030 
1031 /*
1032  * Implement hdump -c output.
1033  */
1034 /* ARGSUSED */
1035 LOCAL int
prcbytes(cnt,buf,dstp)1036 prcbytes(cnt, buf, dstp)
1037 	register int	cnt;
1038 	register char	*buf;
1039 		dst_t	*dstp;
1040 {
1041 	register short i;
1042 
1043 	for (i = 0; i < cnt; i++) {
1044 		if (buf[i] < ' ' || buf[i] >= '\177')
1045 			printf("  \\%02X", 0377&buf[i]);
1046 		else
1047 			printf("    %c", buf[i]);
1048 		if (i == 7 && cnt > 8)
1049 			printf("\n       ");
1050 	}
1051 	return (0);
1052 }
1053 
1054 /*
1055  * Implement hdump -b output.
1056  */
1057 /* ARGSUSED */
1058 LOCAL int
prbbytes(cnt,buf,dstp)1059 prbbytes(cnt, buf, dstp)
1060 	register int	cnt;
1061 	register char	*buf;
1062 		dst_t	*dstp;
1063 {
1064 	register short i;
1065 
1066 	for (i = 0; i < cnt; i++) {
1067 		if (dflag) {
1068 			if (uflag)
1069 				printf("  %4d", 0377&buf[i]);
1070 			else
1071 				printf("  %4d", buf[i]);
1072 		} else if (oflag) {
1073 			printf(" %04o", 0377&buf[i]);
1074 		} else {
1075 			printf("   %02X", 0377&buf[i]);
1076 		}
1077 		if (i == 7 && cnt > 8)
1078 			printf("\n       ");
1079 	}
1080 	return (0);
1081 }
1082 
1083 /*
1084  * Implement hdump -a output.
1085  */
1086 /* ARGSUSED */
1087 LOCAL int
prascii(cnt,buf,dstp)1088 prascii(cnt, buf, dstp)
1089 	register int	cnt;
1090 	register char	*buf;
1091 		dst_t	*dstp;
1092 {
1093 	register short	i;
1094 	register short	n = dstp->blocksize;
1095 	register char	c;
1096 
1097 	for (i = 0; i < n; i++) {
1098 		if (i >= cnt)
1099 			printf("   ");
1100 		else if (dflag) {
1101 			if (uflag)
1102 				printf("%4u", 0377&buf[i]);
1103 			else
1104 				printf("%4d", buf[i]);
1105 		} else if (oflag) {
1106 			printf(" %03o", 0377&buf[i]);
1107 		} else {
1108 			printf(" %02X", 0377&buf[i]);
1109 		}
1110 		if (i == 7)
1111 			printf("  ");
1112 	}
1113 	if (dflag || oflag)
1114 		printf("\n         ");
1115 	else
1116 		printf("   ");
1117 	for (i = 0; i < cnt; i++) {
1118 		c = buf[i];
1119 		(void) putchar(c < ' ' || c >= 0177 ? '.' : c);
1120 	}
1121 	return (0);
1122 }
1123 
1124 /*
1125  * Implement hdump -l output.
1126  */
1127 /* ARGSUSED */
1128 LOCAL int
prlong(cnt,buf,dstp)1129 prlong(cnt, buf, dstp)
1130 	int	cnt;
1131 	char	*buf;
1132 	dst_t	*dstp;
1133 {
1134 			/* LINTED */
1135 	register long	*obuf = (long *)buf;
1136 	register short	i;
1137 	register int	n = cnt;
1138 
1139 	n /= sizeof (long);
1140 	for (i = 0; i < n; i++) {
1141 last:
1142 		if (dflag) {
1143 			if (uflag)
1144 				printf("%12lu", obuf[i]);
1145 			else
1146 				printf("%12ld", obuf[i]);
1147 		} else if (oflag) {
1148 			printf(" %012lo", obuf[i]);
1149 		} else {
1150 			printf("   %08lX", obuf[i]);
1151 		}
1152 	}
1153 	if ((i = (cnt % sizeof (long))) != 0) {
1154 		fillbytes(&((char *)obuf)[cnt], sizeof (long)-i, '\0');
1155 		cnt -= i;
1156 		i = cnt / sizeof (long);
1157 		goto last;
1158 	}
1159 	return (0);
1160 }
1161 
1162 /*
1163  * Implement hdump default size output.
1164  */
1165 /* ARGSUSED */
1166 LOCAL int
prshort(cnt,buf,dstp)1167 prshort(cnt, buf, dstp)
1168 	int	cnt;
1169 	char	*buf;
1170 	dst_t	*dstp;
1171 {
1172 			/* LINTED */
1173 	register short	*obuf = (short *)buf;
1174 	register short	i;
1175 	register int	n = cnt;
1176 
1177 	n /= sizeof (short);
1178 	for (i = 0; i < n; i++) {
1179 last:
1180 		if (dflag) {
1181 			if (uflag)
1182 				printf("%7hu", obuf[i]);
1183 			else
1184 				printf("%7hd", obuf[i]);
1185 		} else if (oflag) {
1186 			printf(" %06ho", obuf[i]);
1187 		} else {
1188 			printf("   %04hX", obuf[i]);
1189 		}
1190 	}
1191 	if ((i = (cnt % sizeof (short))) != 0) {
1192 		fillbytes(&((char *)obuf)[cnt], sizeof (short)-i, '\0');
1193 		cnt -= i;
1194 		i = cnt / sizeof (short);
1195 		goto last;
1196 	}
1197 	return (0);
1198 }
1199 
1200 LOCAL BOOL
bufeql(b1,b2,cnt)1201 bufeql(b1, b2, cnt)
1202 	register long	*b1;
1203 	register long	*b2;
1204 		int	cnt;
1205 {
1206 	register int	i;
1207 	static	int	dont_print = -1;
1208 
1209 	if (dont_print < 0)
1210 		return (dont_print = FALSE);
1211 	for (i = cnt / sizeof (long); --i >= 0; )
1212 		if (*b1++ != *b2++)
1213 			return (dont_print = FALSE);
1214 	if (!dont_print) {
1215 		printf("%s", samefmt);
1216 		if (is_pipe)	/* Make it immediately visible in $PAGER */
1217 			(void) fflush(stdout);
1218 	}
1219 	return (dont_print = TRUE);
1220 }
1221 
1222 LOCAL Llong
myatoll(s)1223 myatoll(s)
1224 	char	*s;
1225 {
1226 	char	*p;
1227 	Llong	val = 0;
1228 
1229 	if (s[0] == 'x' && s[1] == '\0') {	/* "x" -> "0x0" */
1230 		curradix = 16;
1231 		return (val);
1232 	} else if (s[0] == 'x' &&
1233 		    strchr("0123456789abcdefABCDEF", s[1])) {
1234 		curradix = 16;
1235 		s++;
1236 	} else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
1237 		curradix = 16;
1238 		s += 2;
1239 	} else if (s[0] == '0' && !streql("0", s)) {
1240 		curradix = 8;
1241 	} else if (((p = strchr(s, '.')) != NULL) &&
1242 		    (p[1] == '\0' ||
1243 		    ((p[1] == 'b' || p[1] == 'B') && p[2] == '\0'))) {
1244 		curradix = 10;
1245 	} else {				/* No prefix -> default */
1246 		if (is_od)
1247 			curradix = 8;
1248 		/*
1249 		 * In hdump mode, leave curradix at 0.
1250 		 */
1251 	}
1252 
1253 	p = astollb(s, &val, curradix);
1254 
1255 	if (*p != '\0') {
1256 		if (*p == '.') {
1257 			p++;
1258 			curradix = 10;
1259 		}
1260 		if (*p && (streql(p, "b") || streql(p, "B"))) {
1261 			if (*p == 'b') val *= 512;
1262 			if (*p == 'B') val *= 512;
1263 		} else if (*p)
1264 			comerrno(EX_BAD, _("Bad numeric argument '%s'.\n"), s);
1265 	}
1266 
1267 	return (val);
1268 }
1269 
1270 LOCAL int
read_input(dstp,bp,cnt)1271 read_input(dstp, bp, cnt)
1272 	register dst_t	*dstp;
1273 	register char	*bp;
1274 	register int	cnt;
1275 {
1276 	register int	amt;
1277 	register int	total = 0;
1278 
1279 	while (cnt > 0) {
1280 		amt = fileread(dstp->f, bp, cnt);
1281 		if (amt <= 0) {
1282 			if (amt < 0) {
1283 				errmsg(_("Error reading '%s'.\n"),
1284 					dstp->inname);
1285 			}
1286 			if (!open_next(dstp))
1287 				return (total);
1288 			continue;
1289 		}
1290 		if (feof(dstp->f) && !open_next(dstp))
1291 			return (total+amt);
1292 
1293 		total += amt;
1294 		cnt   -= amt;
1295 		bp    += amt;
1296 	};
1297 	return (total);
1298 }
1299 
1300 LOCAL BOOL
open_next(dstp)1301 open_next(dstp)
1302 	dst_t	*dstp;
1303 {
1304 	FILE	*f;
1305 	char	*inname;
1306 	BOOL	stdinflag;
1307 
1308 	do {
1309 		stdinflag = FALSE;
1310 		if (dstp->argc == -2) {		/* Once only decrement later */
1311 			inname = "/dev/stdin";
1312 			dstp->inname = "stdin";
1313 			stdinflag = TRUE;
1314 		} else if (dstp->argc <= 0) {
1315 			return (FALSE);
1316 		} else {
1317 			dstp->inname = inname = dstp->argv[0];
1318 			dstp->argv++;
1319 		}
1320 		/*
1321 		 * POSIX.1-2008 optionally permits the notation "-" for stdin
1322 		 * but /usr/xpg4/bin/od on Solaris does not. We currently
1323 		 * follow /usr/xpg4/bin/od.
1324 		 */
1325 		if (!is_xpg4 && inname[0] == '-' && inname[1] == '\0') {
1326 			inname = "/dev/stdin";
1327 			dstp->inname = "stdin";
1328 			stdinflag = TRUE;
1329 		}
1330 #ifndef	HAVE__DEV_STDIN
1331 		if (stdinflag) {
1332 			if (dstp->f != (FILE *)NULL) {
1333 				fclose(dstp->f);
1334 				dstp->f = (FILE *)NULL;
1335 			}
1336 			f = fileluopen(dup(STDIN_FILENO), "rb");
1337 		} else
1338 #endif
1339 		if (dstp->f == (FILE *)NULL) {
1340 			f = fileopen(inname, "rb");
1341 		} else {
1342 			f = filereopen(inname, "rb", dstp->f);
1343 		}
1344 		/* LINTED */
1345 		if (stdinflag && f != (FILE *)NULL)
1346 			setmode(fileno(f), O_BINARY);
1347 		if (f == (FILE *)NULL) {
1348 			errmsg(_("Can't open '%s'.\n"), dstp->inname);
1349 			dstp->excode = geterrno();
1350 		}
1351 		dstp->argc--;
1352 	} while (f == (FILE *)NULL);
1353 
1354 	if (f == (FILE *)NULL)
1355 		return (FALSE);
1356 
1357 	dstp->f = f;
1358 	file_raise(dstp->f, FALSE);
1359 #ifdef	_FASCII		/* Mark Williams C */
1360 	dsp->f->_ff &= ~_FASCII;
1361 #endif
1362 	return (TRUE);
1363 }
1364 
1365 /*
1366  * Seek/skip bytes and return the number of unskipped bytes
1367  */
1368 LOCAL off_t
advance(dstp,xpos)1369 advance(dstp, xpos)
1370 	dst_t	*dstp;
1371 	off_t	xpos;
1372 {
1373 	off_t	newpos = xpos;
1374 
1375 	do {
1376 		newpos = doadvance(dstp->f, newpos, dstp->argc <= 0);
1377 	} while (newpos > 0 && open_next(dstp));
1378 	return (newpos);
1379 }
1380 
1381 #include <schily/stat.h>
1382 
1383 /*
1384  * Seek/skip bytes and return the number of unskipped bytes
1385  */
1386 LOCAL off_t
doadvance(f,xpos,is_last)1387 doadvance(f, xpos, is_last)
1388 	FILE	*f;
1389 	off_t	xpos;
1390 	BOOL	is_last;
1391 {
1392 	struct stat sb;
1393 
1394 	if (fstat(fdown(f), &sb) < 0)
1395 		return (doskip(f, xpos));
1396 	if (isatty(fdown(f)))
1397 		return (doskip(f, xpos));
1398 	/*
1399 	 * Skip fast in case this is a plain file smaller than the skip value.
1400 	 */
1401 	if (S_ISREG(sb.st_mode) && sb.st_size < xpos)
1402 		return (xpos - sb.st_size);
1403 	/*
1404 	 * Seekable is any plain file
1405 	 * and the last block or character device in the file name list.
1406 	 */
1407 	if (S_ISREG(sb.st_mode) ||
1408 	    (is_last &&
1409 	    (S_ISBLK(sb.st_mode) ||
1410 	    S_ISCHR(sb.st_mode)))) {
1411 		off_t	newpos = xpos;
1412 
1413 		/*
1414 		 * On raw devices, we need to be aware of the max. sector size.
1415 		 * Make sure that we still need to read something to be able to
1416 		 * verify our position in case we are on a device.
1417 		 */
1418 		if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) {
1419 			newpos = xpos / 4096 * 4096;
1420 			if (newpos == xpos && newpos > 0)
1421 				newpos -= 4096;
1422 		}
1423 		(void) fileseek(f, newpos);
1424 		newpos = filepos(f);
1425 		if (newpos > 0)
1426 			xpos -= newpos;
1427 	}
1428 	return (doskip(f, xpos));
1429 }
1430 
1431 /*
1432  * Skip bytes and return the number of unskipped bytes
1433  */
1434 LOCAL off_t
doskip(f,xpos)1435 doskip(f, xpos)
1436 	FILE	*f;
1437 	off_t	xpos;
1438 {
1439 	char	sbuf[BUFSIZ];
1440 	int	amt;
1441 
1442 	while (xpos > 0) {
1443 		amt = sizeof (sbuf);
1444 		if (xpos < amt)
1445 			amt = (int)xpos;
1446 		amt = fileread(f, sbuf, amt);
1447 		if (amt <= 0)
1448 			break;
1449 		xpos -= amt;
1450 	}
1451 	return (xpos);
1452 }
1453 
1454 LOCAL BOOL
out_ispipe()1455 out_ispipe()
1456 {
1457 	struct stat sb;
1458 
1459 	if (fstat(fdown(stdout), &sb) < 0)
1460 		return (TRUE);
1461 	return (S_ISFIFO(sb.st_mode) || S_ISSOCK(sb.st_mode));
1462 }
1463 
1464 /*
1465  * Fill POSIX size description strings for -t type according to the parameters
1466  * of the current platform.
1467  *
1468  * The typical ILP32 and LP64 implementations do not cause problems as all
1469  * sizes 1 2 4 8 are present as basic types. Tru64 and older Linux versions for
1470  * DEC Alpha are implemented as ILP64 and cause a problem from missing an
1471  * official basic type with 32 bits. There is only __int32.
1472  */
1473 LOCAL char	chartype[3]	= { 'C', sizeof (char) + '0', '\0' };
1474 LOCAL char	shorttype[3]	= { 'S', sizeof (short) + '0', '\0' };
1475 LOCAL char	inttype[3]	= { 'I', sizeof (int) + '0', '\0' };
1476 LOCAL char	longtype[3]	= { 'L', sizeof (long) + '0', '\0' };
1477 #if	SIZEOF_LONG_LONG > SIZEOF_LONG_INT
1478 LOCAL char	longlongtype[3]	= { sizeof (Llong) + '0', '\0' };
1479 #endif
1480 LOCAL char	floattype[3]	= { 'F', sizeof (float) + '0', '\0' };
1481 LOCAL char	doubletype[3]	= { 'D', sizeof (double) + '0', '\0' };
1482 #ifdef	HAVE_LONGDOUBLE
1483 LOCAL char	ldoubletype[3]	= { 'L', '\0' };
1484 #endif
1485 
1486 /*
1487  * POSIX type string to output conversion.
1488  */
1489 LOCAL int
ptype(arg)1490 ptype(arg)
1491 	const	char	*arg;
1492 {
1493 	int	type;
1494 	int	size;
1495 
1496 #define	error(a)
1497 
1498 	while ((type = *arg++) != '\0') {
1499 
1500 		switch (type) {
1501 
1502 		case 'a':
1503 			error("CHAR Named character\n");
1504 			add_dout(&d_asc);
1505 			break;
1506 		case 'c':
1507 			error("CHAR Character\n");
1508 			add_dout(&d_mbch);
1509 			break;
1510 
1511 			/* Int size */
1512 		case 'd':	/* C S I L 1 2 4 8 */
1513 			size = *arg++;
1514 
1515 			if (size == '\0') {
1516 				error("INT Dezimal\n");
1517 				add_dout(&d_i_d);
1518 				arg--;
1519 			} else if (strchr(chartype, size)) {
1520 				error("CHAR Dezimal\n");
1521 				add_dout(&d_c_d);
1522 			} else if (strchr(shorttype, size)) {
1523 				error("SHORT Dezimal\n");
1524 				add_dout(&d_s_d);
1525 			} else if (strchr(inttype, size)) {
1526 				error("INT Dezimal\n");
1527 				add_dout(&d_i_d);
1528 			} else if (strchr(longtype, size)) {
1529 				error("LONG Dezimal\n");
1530 				add_dout(&d_l_d);
1531 #if	SIZEOF_LONG_LONG > SIZEOF_LONG_INT
1532 			} else if (strchr(longlongtype, size)) {
1533 				error("LONG LONG Dezimal\n");
1534 				add_dout(&d_ll_d);
1535 #endif
1536 			} else {
1537 				if (isupper(size) || isdigit(size))
1538 					return (illsize(size));
1539 				error("INT Dezimal\n");
1540 				add_dout(&d_i_d);
1541 				arg--;
1542 			}
1543 			break;
1544 
1545 #ifndef	NO_FLOATINGPOINT
1546 		case 'f':	/* F D L 4 8 */
1547 			size = *arg++;
1548 
1549 			if (size == '\0') {
1550 				error("DOUBLE\n");
1551 				add_dout(&d_d_f);
1552 				arg--;
1553 			} else if (strchr(floattype, size)) {
1554 				error("FLOAT\n");
1555 				add_dout(&d_f_f);
1556 			} else if (strchr(doubletype, size)) {
1557 				error("DOUBLE\n");
1558 				add_dout(&d_d_f);
1559 #ifdef	HAVE_LONGDOUBLE
1560 			} else if (strchr(ldoubletype, size)) {
1561 				error("LONG DOUBLE\n");
1562 				add_dout(&d_ld_f);
1563 #endif
1564 			} else {
1565 				if (isupper(size) || isdigit(size))
1566 					return (illfsize(size));
1567 				error("DOUBLE\n");
1568 				add_dout(&d_d_f);
1569 				arg--;
1570 			}
1571 			break;
1572 #endif
1573 
1574 		case 'o':	/* C S I L 1 2 4 8 */
1575 			size = *arg++;
1576 
1577 			if (size == '\0') {
1578 				error("INT Octal\n");
1579 				add_dout(&d_i_o);
1580 				arg--;
1581 			} else if (strchr(chartype, size)) {
1582 				error("CHAR Octal\n");
1583 				add_dout(&d_c_o);
1584 			} else if (strchr(shorttype, size)) {
1585 				error("SHORT Octal\n");
1586 				add_dout(&d_s_o);
1587 			} else if (strchr(inttype, size)) {
1588 				error("INT Octal\n");
1589 				add_dout(&d_i_o);
1590 			} else if (strchr(longtype, size)) {
1591 				error("LONG Octal\n");
1592 				add_dout(&d_l_o);
1593 #if	SIZEOF_LONG_LONG > SIZEOF_LONG_INT
1594 			} else if (strchr(longlongtype, size)) {
1595 				error("LONG LONG Octal\n");
1596 				add_dout(&d_ll_o);
1597 #endif
1598 			} else {
1599 				if (isupper(size) || isdigit(size))
1600 					return (illsize(size));
1601 				error("INT Octal\n");
1602 				add_dout(&d_i_o);
1603 				arg--;
1604 			}
1605 			break;
1606 
1607 		case 'u':	/* C S I L 1 2 4 8 */
1608 			size = *arg++;
1609 
1610 			if (size == '\0') {
1611 				error("INT Unsigned\n");
1612 				add_dout(&d_i_u);
1613 				arg--;
1614 			} else if (strchr(chartype, size)) {
1615 				error("CHAR Unsigned\n");
1616 				add_dout(&d_c_u);
1617 			} else if (strchr(shorttype, size)) {
1618 				error("SHORT Unsigned\n");
1619 				add_dout(&d_s_u);
1620 			} else if (strchr(inttype, size)) {
1621 				error("INT Unsigned\n");
1622 				add_dout(&d_i_u);
1623 			} else if (strchr(longtype, size)) {
1624 				error("LONG Unsigned\n");
1625 				add_dout(&d_l_u);
1626 #if	SIZEOF_LONG_LONG > SIZEOF_LONG_INT
1627 			} else if (strchr(longlongtype, size)) {
1628 				error("LONG LONG Unsigned\n");
1629 				add_dout(&d_ll_u);
1630 #endif
1631 			} else {
1632 				if (isupper(size) || isdigit(size))
1633 					return (illsize(size));
1634 				error("INT Unsigned\n");
1635 				add_dout(&d_i_u);
1636 				arg--;
1637 			}
1638 			break;
1639 
1640 		case 'x':	/* C S I L 1 2 4 8 */
1641 			size = *arg++;
1642 
1643 			if (size == '\0') {
1644 				error("INT Hex\n");
1645 				add_dout(&d_i_x);
1646 				arg--;
1647 			} else if (strchr(chartype, size)) {
1648 				error("CHAR Hex\n");
1649 				add_dout(&d_c_x);
1650 			} else if (strchr(shorttype, size)) {
1651 				error("SHORT Hex\n");
1652 				add_dout(&d_s_x);
1653 			} else if (strchr(inttype, size)) {
1654 				error("INT Hex\n");
1655 				add_dout(&d_i_x);
1656 			} else if (strchr(longtype, size)) {
1657 				error("LONG Hex\n");
1658 				add_dout(&d_l_x);
1659 #if	SIZEOF_LONG_LONG > SIZEOF_LONG_INT
1660 			} else if (strchr(longlongtype, size)) {
1661 				error("LONG LONG Hex\n");
1662 				add_dout(&d_ll_x);
1663 #endif
1664 			} else {
1665 				if (isupper(size) || isdigit(size))
1666 					return (illsize(size));
1667 				error("INT hex\n");
1668 				add_dout(&d_i_x);
1669 				arg--;
1670 			}
1671 			break;
1672 
1673 		default:
1674 			return (illtype(type));
1675 		}
1676 	}
1677 	return (1);
1678 }
1679 
1680 LOCAL int
illsize(size)1681 illsize(size)
1682 	int	size;
1683 {
1684 	errmsgno(EX_BAD,
1685 		_("Illegal size '%c', use C, S, I, L, 1, 2, 4 or 8.\n"),
1686 		size);
1687 	return (-1);
1688 }
1689 
1690 LOCAL int
illfsize(size)1691 illfsize(size)
1692 	int	size;
1693 {
1694 	errmsgno(EX_BAD,
1695 		_("Illegal size '%c', use F, D L, 4 or 8.\n"),
1696 		size);
1697 	return (-1);
1698 }
1699 
1700 LOCAL int
illtype(type)1701 illtype(type)
1702 	int	type;
1703 {
1704 	errmsgno(EX_BAD,
1705 #ifndef	NO_FLOATINGPOINT
1706 	_("Illegal type '%c', use 'a', 'c', 'd', 'f', 'o', 'u' or 'x'.\n"),
1707 #else
1708 	_("Illegal type '%c', use 'a', 'c', 'd', 'o', 'u' or 'x'.\n"),
1709 #endif
1710 		type);
1711 	return (-1);
1712 }
1713 
1714 LOCAL void
setaddrfmt(Aflag,lradix)1715 setaddrfmt(Aflag, lradix)
1716 	int	Aflag;
1717 	int	lradix;
1718 {
1719 	char	*llfmt;			/* Address format off_t -> Llong   */
1720 	char	*lfmt;			/* Address format off_t -> long	   */
1721 
1722 	if (is_od)
1723 		samefmt = "*\n";
1724 	else
1725 		samefmt = "     *\n";
1726 
1727 	switch (Aflag) {
1728 
1729 	case 'x':
1730 		if (is_od) {
1731 			lfmt = "%7.7lx";
1732 			llfmt = "%7.7llx";
1733 		} else {
1734 			lfmt = "%6lx: ";
1735 			llfmt = "%6llx: ";
1736 		}
1737 		break;
1738 
1739 	case 'd':
1740 		if (is_od) {
1741 			lfmt = "%7.7ld";
1742 			llfmt = "%7.7lld";
1743 		} else {
1744 			lfmt = "%6ld: ";
1745 			llfmt = "%6lld: ";
1746 		}
1747 		break;
1748 
1749 	case 'o':
1750 		if (is_od) {
1751 			lfmt = "%7.7lo";
1752 			llfmt = "%7.7llo";
1753 		} else {
1754 			lfmt = "%6.6lo: ";
1755 			llfmt = "%6.6llo: ";
1756 		}
1757 		break;
1758 
1759 	case 'n':
1760 		if (is_od) {
1761 			llfmt = lfmt = "\t";
1762 		} else {
1763 			llfmt = lfmt = "        ";
1764 		}
1765 		samefmt = "*\n";
1766 		break;
1767 
1768 	case 0:				/* No -Ac format specified */
1769 		if (is_od) {
1770 			llfmt = lradix == 16 ? "%07.7llx" :
1771 					(lradix == 10 ? "%7.7lld" : "%7.7llo");
1772 			lfmt  = lradix == 16 ? "%07.7lx" :
1773 					(lradix == 10 ? "%7.7ld" : "%7.7lo");
1774 		} else {
1775 			llfmt = lradix == 8 ? "%06llo:" :
1776 					(lradix == 10 ? "%6lld:" : "%6llx:");
1777 			lfmt  = lradix == 8 ? "%06lo:" :
1778 					(lradix == 10 ? "%6ld:" : "%6lx:");
1779 		}
1780 		break;
1781 	default:
1782 		llfmt = lfmt = "";	/* Make GCC and lint happy */
1783 
1784 		/* NOTREACHED */
1785 		comerrno(EX_BAD,
1786 		_("-A option only accepts the following:  d, o, n, and x.\n"));
1787 	}
1788 	if (sizeof (pos) > sizeof (long))
1789 		addrfmt = llfmt;
1790 	else
1791 		addrfmt = lfmt;
1792 }
1793