1 /* vi: set sw=4 ts=4: */
2 /*
3  * Support code for the hexdump and od applets,
4  * based on code from util-linux v 2.11l
5  *
6  * Copyright (c) 1989
7  * The Regents of the University of California.  All rights reserved.
8  *
9  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
10  *
11  * Original copyright notice is retained at the end of this file.
12  */
13 #include "libbb.h"
14 #include "dump.h"
15 
16 #define	F_IGNORE	0x01		/* %_A */
17 #define	F_SETREP	0x02		/* rep count set, not default */
18 #define	F_ADDRESS	0x001		/* print offset */
19 #define	F_BPAD		0x002		/* blank pad */
20 #define	F_C		0x004		/* %_c */
21 #define	F_CHAR		0x008		/* %c */
22 #define	F_DBL		0x010		/* %[EefGf] */
23 #define	F_INT		0x020		/* %[di] */
24 #define	F_P		0x040		/* %_p */
25 #define	F_STR		0x080		/* %s */
26 #define	F_U		0x100		/* %_u */
27 #define	F_UINT		0x200		/* %[ouXx] */
28 #define	F_TEXT		0x400		/* no conversions */
29 
30 typedef struct priv_dumper_t {
31 	dumper_t pub;
32 
33 	char **argv;
34 	FU *endfu;
35 	off_t savaddress;        /* saved address/offset in stream */
36 	off_t eaddress;          /* end address */
37 	int blocksize;
38 	smallint exitval;        /* final exit value */
39 
40 	/* former statics */
41 	smallint next__done;
42 	smallint get__ateof; // = 1;
43 	unsigned char *get__curp;
44 	unsigned char *get__savp;
45 } priv_dumper_t;
46 
47 static const char dot_flags_width_chars[] ALIGN1 = ".#-+ 0123456789";
48 
49 static const char size_conv_str[] ALIGN1 =
50 "\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG";
51 
52 static const char int_convs[] ALIGN1 = "diouxX";
53 
alloc_dumper(void)54 dumper_t* FAST_FUNC alloc_dumper(void)
55 {
56 	priv_dumper_t *dumper = xzalloc(sizeof(*dumper));
57 	dumper->pub.dump_length = -1;
58 	dumper->pub.dump_vflag = FIRST;
59 	dumper->get__ateof = 1;
60 	return &dumper->pub;
61 }
62 
bb_dump_size(FS * fs)63 static NOINLINE int bb_dump_size(FS *fs)
64 {
65 	FU *fu;
66 	int bcnt, cur_size;
67 	char *fmt;
68 	const char *p;
69 	int prec;
70 
71 	/* figure out the data block size needed for each format unit */
72 	for (cur_size = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
73 		if (fu->bcnt) {
74 			cur_size += fu->bcnt * fu->reps;
75 			continue;
76 		}
77 		for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
78 			if (*fmt != '%')
79 				continue;
80 			/*
81 			 * skip any special chars -- save precision in
82 			 * case it's a %s format.
83 			 */
84 			while (strchr(dot_flags_width_chars + 1, *++fmt))
85 				continue;
86 			if (*fmt == '.' && isdigit(*++fmt)) {
87 				prec = atoi(fmt);
88 				while (isdigit(*++fmt))
89 					continue;
90 			}
91 			p = strchr(size_conv_str + 12, *fmt);
92 			if (!p) {
93 				if (*fmt == 's') {
94 					bcnt += prec;
95 				}
96 				if (*fmt == '_') {
97 					++fmt;
98 					if ((*fmt == 'c') || (*fmt == 'p') || (*fmt == 'u')) {
99 						bcnt += 1;
100 					}
101 				}
102 			} else {
103 				bcnt += p[-12];
104 			}
105 		}
106 		cur_size += bcnt * fu->reps;
107 	}
108 	return cur_size;
109 }
110 
rewrite(priv_dumper_t * dumper,FS * fs)111 static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
112 {
113 	FU *fu;
114 
115 	for (fu = fs->nextfu; fu; fu = fu->nextfu) {
116 		PR *pr;
117 		char *p1, *p2, *p3;
118 		char *fmtp;
119 		int nconv = 0;
120 		/*
121 		 * break each format unit into print units; each
122 		 * conversion character gets its own.
123 		 */
124 		for (fmtp = fu->fmt; *fmtp; ) {
125 			unsigned len;
126 			const char *prec;
127 			const char *byte_count_str;
128 
129 			/* DBU:[dvae@cray.com] zalloc so that forward ptrs start out NULL */
130 			pr = xzalloc(sizeof(*pr));
131 			if (!fu->nextpr)
132 				fu->nextpr = pr;
133 
134 			/* skip preceding text and up to the next % sign */
135 			p1 = strchr(fmtp, '%');
136 			if (!p1) { /* only text in the string */
137 				pr->fmt = fmtp;
138 				pr->flags = F_TEXT;
139 				break;
140 			}
141 
142 			/*
143 			 * get precision for %s -- if have a byte count, don't
144 			 * need it.
145 			 */
146 			prec = NULL;
147 			if (fu->bcnt) {
148 				/* skip to conversion character */
149 				while (strchr(dot_flags_width_chars, *++p1))
150 					continue;
151 			} else {
152 				/* skip any special chars, field width */
153 				while (strchr(dot_flags_width_chars + 1, *++p1))
154 					continue;
155 				if (*p1 == '.' && isdigit(*++p1)) {
156 					prec = p1;
157 					while (isdigit(*++p1))
158 						continue;
159 				}
160 			}
161 
162 			p2 = p1 + 1; /* set end pointer */
163 
164 			/*
165 			 * figure out the byte count for each conversion;
166 			 * rewrite the format as necessary, set up blank-
167 			 * padding for end of data.
168 			 */
169 			if (*p1 == 'c') {
170 				pr->flags = F_CHAR;
171  DO_BYTE_COUNT_1:
172 				byte_count_str = "\001";
173  DO_BYTE_COUNT:
174 				if (fu->bcnt) {
175 					for (;;) {
176 						if (fu->bcnt == *byte_count_str)
177 							break;
178 						if (*++byte_count_str == 0)
179 							bb_error_msg_and_die("bad byte count for conversion character %s", p1);
180 					}
181 				}
182 				/* Unlike the original, output the remainder of the format string. */
183 				pr->bcnt = *byte_count_str;
184 			} else
185 			if (*p1 == 'l') { /* %ld etc */
186 				const char *e;
187 
188 				++p2;
189 				++p1;
190  DO_INT_CONV:
191 				e = strchr(int_convs, *p1); /* "diouxX"? */
192 				if (!e)
193 					goto DO_BAD_CONV_CHAR;
194 				pr->flags = F_INT;
195 				if (e > int_convs + 1) /* not d or i? */
196 					pr->flags = F_UINT;
197 				byte_count_str = "\004\002\001";
198 				goto DO_BYTE_COUNT;
199 			} else
200 			if (strchr(int_convs, *p1)) { /* %d etc */
201 				goto DO_INT_CONV;
202 			} else
203 			if (strchr("eEfgG", *p1)) { /* floating point */
204 				pr->flags = F_DBL;
205 				byte_count_str = "\010\004";
206 				goto DO_BYTE_COUNT;
207 			} else
208 			if (*p1 == 's') {
209 				pr->flags = F_STR;
210 				pr->bcnt = fu->bcnt;
211 				if (fu->bcnt == 0) {
212 					if (!prec)
213 						bb_simple_error_msg_and_die("%s needs precision or byte count");
214 					pr->bcnt = atoi(prec);
215 				}
216 			} else
217 			if (*p1 == '_') {
218 				p2++;  /* move past a in "%_a" */
219 				switch (p1[1]) {
220 				case 'A':	/* %_A[dox]: print address and the end */
221 					dumper->endfu = fu;
222 					fu->flags |= F_IGNORE;
223 					/* FALLTHROUGH */
224 				case 'a':	/* %_a[dox]: current address */
225 					pr->flags = F_ADDRESS;
226 					p2++;  /* move past x in "%_ax" */
227 					if ((p1[2] != 'd') && (p1[2] != 'o') && (p1[2] != 'x')) {
228 						goto DO_BAD_CONV_CHAR;
229 					}
230 					*p1++ = 'l';
231 					*p1++ = 'l';
232 					break;
233 				case 'c':	/* %_c: chars, \ooo, \n \r \t etc */
234 					pr->flags = F_C;
235 					/* *p1 = 'c';   set in conv_c */
236 					goto DO_BYTE_COUNT_1;
237 				case 'p':	/* %_p: chars, dots for nonprintable */
238 					pr->flags = F_P;
239 					*p1 = 'c';
240 					goto DO_BYTE_COUNT_1;
241 				case 'u':	/* %_p: chars, 'nul', 'esc' etc for nonprintable */
242 					pr->flags = F_U;
243 					/* *p1 = 'c';   set in conv_u */
244 					goto DO_BYTE_COUNT_1;
245 				default:
246 					goto DO_BAD_CONV_CHAR;
247 				}
248 			} else {
249  DO_BAD_CONV_CHAR:
250 				bb_error_msg_and_die("bad conversion character %%%s", p1);
251 			}
252 
253 			/*
254 			 * copy to PR format string, set conversion character
255 			 * pointer, update original.
256 			 */
257 			len = (p1 - fmtp) + 1;
258 			pr->fmt = xstrndup(fmtp, len);
259 			/* DBU:[dave@cray.com] w/o this, trailing fmt text, space is lost.
260 			 * Skip subsequent text and up to the next % sign and tack the
261 			 * additional text onto fmt: eg. if fmt is "%x is a HEX number",
262 			 * we lose the " is a HEX number" part of fmt.
263 			 */
264 			for (p3 = p2; *p3 && *p3 != '%'; p3++)
265 				continue;
266 			if ((p3 - p2) != 0) {
267 				char *d;
268 				pr->fmt = d = xrealloc(pr->fmt, len + (p3 - p2) + 1);
269 				d += len;
270 				do {
271 					*d++ = *p2++;
272 				} while (p2 != p3);
273 				*d = '\0';
274 				/* now p2 = p3 */
275 			}
276 			pr->cchar = pr->fmt + len - 1; /* must be after realloc! */
277 			fmtp = p2;
278 
279 			/* only one conversion character if byte count */
280 			if (!(pr->flags & F_ADDRESS) && fu->bcnt && nconv++) {
281 				bb_simple_error_msg_and_die("byte count with multiple conversion characters");
282 			}
283 		}
284 		/*
285 		 * if format unit byte count not specified, figure it out
286 		 * so can adjust rep count later.
287 		 */
288 		if (fu->bcnt == 0)
289 			for (pr = fu->nextpr; pr; pr = pr->nextpr)
290 				fu->bcnt += pr->bcnt;
291 	}
292 	/*
293 	 * if the format string interprets any data at all, and it's
294 	 * not the same as the blocksize, and its last format unit
295 	 * interprets any data at all, and has no iteration count,
296 	 * repeat it as necessary.
297 	 *
298 	 * if rep count is greater than 1, no trailing whitespace
299 	 * gets output from the last iteration of the format unit:
300 	 * 2/1 "%02x " prints "XX XX", not "XX XX "
301 	 * 2/1 "%02x\n" prints "XX\nXX", not "XX\nXX\n"
302 	 */
303 	for (fu = fs->nextfu; fu; fu = fu->nextfu) {
304 		if (!fu->nextfu
305 		 && fs->bcnt < dumper->blocksize
306 		 && !(fu->flags & F_SETREP)
307 		 && fu->bcnt
308 		) {
309 			fu->reps += (dumper->blocksize - fs->bcnt) / fu->bcnt;
310 		}
311 		if (fu->reps > 1 && fu->nextpr) {
312 			PR *pr;
313 			char *p1, *p2;
314 
315 			for (pr = fu->nextpr;; pr = pr->nextpr)
316 				if (!pr->nextpr)
317 					break;
318 			p2 = NULL;
319 			for (p1 = pr->fmt; *p1; ++p1)
320 				p2 = isspace(*p1) ? p1 : NULL;
321 			if (p2)
322 				pr->nospace = p2;
323 		}
324 	}
325 }
326 
do_skip(priv_dumper_t * dumper,const char * fname)327 static void do_skip(priv_dumper_t *dumper, const char *fname)
328 {
329 	struct stat sbuf;
330 
331 	xfstat(STDIN_FILENO, &sbuf, fname);
332 	if (S_ISREG(sbuf.st_mode)
333 	 && dumper->pub.dump_skip >= sbuf.st_size
334 	) {
335 		/* If st_size is valid and pub.dump_skip >= st_size */
336 		dumper->pub.dump_skip -= sbuf.st_size;
337 		dumper->pub.address += sbuf.st_size;
338 		return;
339 	}
340 	if (fseeko(stdin, dumper->pub.dump_skip, SEEK_SET)) {
341 		bb_simple_perror_msg_and_die(fname);
342 	}
343 	dumper->pub.address += dumper->pub.dump_skip;
344 	dumper->savaddress = dumper->pub.address;
345 	dumper->pub.dump_skip = 0;
346 }
347 
next(priv_dumper_t * dumper)348 static NOINLINE int next(priv_dumper_t *dumper)
349 {
350 	for (;;) {
351 		const char *fname = *dumper->argv;
352 
353 		if (fname) {
354 			dumper->argv++;
355 			if (NOT_LONE_DASH(fname)) {
356 				if (!freopen(fname, "r", stdin)) {
357 					bb_simple_perror_msg(fname);
358 					dumper->exitval = 1;
359 					dumper->next__done = 1;
360 					continue;
361 				}
362 			}
363 		} else {
364 			if (dumper->next__done)
365 				return 0; /* no next file */
366 		}
367 		dumper->next__done = 1;
368 		if (dumper->pub.dump_skip)
369 			do_skip(dumper, fname ? fname : "stdin");
370 		if (dumper->pub.dump_skip == 0)
371 			return 1;
372 	}
373 	/* NOTREACHED */
374 }
375 
get(priv_dumper_t * dumper)376 static unsigned char *get(priv_dumper_t *dumper)
377 {
378 	int n;
379 	int need, nread;
380 	int blocksize = dumper->blocksize;
381 
382 	if (!dumper->get__curp) {
383 		dumper->pub.address = (off_t)0; /*DBU:[dave@cray.com] initialize,initialize..*/
384 		dumper->get__curp = xmalloc(blocksize);
385 		dumper->get__savp = xzalloc(blocksize); /* need to be initialized */
386 	} else {
387 		unsigned char *tmp = dumper->get__curp;
388 		dumper->get__curp = dumper->get__savp;
389 		dumper->get__savp = tmp;
390 		dumper->savaddress += blocksize;
391 		dumper->pub.address = dumper->savaddress;
392 	}
393 	need = blocksize;
394 	nread = 0;
395 	while (1) {
396 		/*
397 		 * if read the right number of bytes, or at EOF for one file,
398 		 * and no other files are available, zero-pad the rest of the
399 		 * block and set the end flag.
400 		 */
401 		if (!dumper->pub.dump_length || (dumper->get__ateof && !next(dumper))) {
402 			if (need == blocksize) {
403 				return NULL;
404 			}
405 			if (dumper->pub.dump_vflag != ALL   /* not "show all"? */
406 			 && dumper->pub.dump_vflag != FIRST /* not first line? */
407 			 && memcmp(dumper->get__curp, dumper->get__savp, nread) == 0 /* same data? */
408 			) {
409 				if (dumper->pub.dump_vflag != DUP) {
410 					puts("*");
411 				}
412 			}
413 			memset(dumper->get__curp + nread, 0, need);
414 			dumper->eaddress = dumper->pub.address + nread;
415 			return dumper->get__curp;
416 		}
417 		n = fread(dumper->get__curp + nread, sizeof(unsigned char),
418 				dumper->pub.dump_length == -1 ? need : MIN(dumper->pub.dump_length, need), stdin);
419 		if (n == 0) {
420 			if (ferror(stdin)) {
421 				bb_simple_perror_msg(dumper->argv[-1]);
422 			}
423 			dumper->get__ateof = 1;
424 			continue;
425 		}
426 		dumper->get__ateof = 0;
427 		if (dumper->pub.dump_length != -1) {
428 			dumper->pub.dump_length -= n;
429 		}
430 		need -= n;
431 		if (need == 0) {
432 			if (dumper->pub.dump_vflag == ALL   /* "show all"? */
433 			 || dumper->pub.dump_vflag == FIRST /* first line? */
434 			 || memcmp(dumper->get__curp, dumper->get__savp, blocksize) != 0 /* not same data? */
435 			) {
436 				if (dumper->pub.dump_vflag == DUP || dumper->pub.dump_vflag == FIRST) {
437 					dumper->pub.dump_vflag = WAIT;
438 				}
439 				return dumper->get__curp;
440 			}
441 			if (dumper->pub.dump_vflag == WAIT) {
442 				puts("*");
443 			}
444 			dumper->pub.dump_vflag = DUP;
445 			dumper->savaddress += blocksize;
446 			dumper->pub.address = dumper->savaddress;
447 			need = blocksize;
448 			nread = 0;
449 		} else {
450 			nread += n;
451 		}
452 	}
453 }
454 
bpad(PR * pr)455 static void bpad(PR *pr)
456 {
457 	char *p1, *p2;
458 
459 	/*
460 	 * remove all conversion flags; '-' is the only one valid
461 	 * with %s, and it's not useful here.
462 	 */
463 	pr->flags = F_BPAD;
464 	*pr->cchar = 's';
465 	for (p1 = pr->fmt; *p1 != '%'; ++p1)
466 		continue;
467 	for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1)
468 		if (pr->nospace)
469 			pr->nospace--;
470 	while ((*p2++ = *p1++) != '\0')
471 		continue;
472 }
473 
474 static const char conv_str[] ALIGN1 =
475 	"\0"  "\\""0""\0"
476 	"\007""\\""a""\0"  /* \a */
477 	"\b"  "\\""b""\0"
478 	"\f"  "\\""f""\0"
479 	"\n"  "\\""n""\0"
480 	"\r"  "\\""r""\0"
481 	"\t"  "\\""t""\0"
482 	"\v"  "\\""v""\0"
483 	;
484 
conv_c(PR * pr,unsigned char * p)485 static void conv_c(PR *pr, unsigned char *p)
486 {
487 	const char *str = conv_str;
488 
489 	do {
490 		if (*p == *str) {
491 			++str;
492 			goto strpr; /* map e.g. '\n' to "\\n" */
493 		}
494 		str += 4;
495 	} while (*str);
496 
497 	if (isprint_asciionly(*p)) {
498 		*pr->cchar = 'c';
499 		printf(pr->fmt, *p);
500 	} else {
501 		char buf[4];
502 		/* gcc-8.0.1 needs lots of casts to shut up */
503 		sprintf(buf, "%03o", (unsigned)(uint8_t)*p);
504 		str = buf;
505  strpr:
506 		*pr->cchar = 's';
507 		printf(pr->fmt, str);
508 	}
509 }
510 
conv_u(PR * pr,unsigned char * p)511 static void conv_u(PR *pr, unsigned char *p)
512 {
513 	static const char list[] ALIGN1 =
514 		"nul\0soh\0stx\0etx\0eot\0enq\0ack\0bel\0"
515 		"bs\0_ht\0_lf\0_vt\0_ff\0_cr\0_so\0_si\0_"
516 		"dle\0dcl\0dc2\0dc3\0dc4\0nak\0syn\0etb\0"
517 		"can\0em\0_sub\0esc\0fs\0_gs\0_rs\0_us";
518 
519 	/* od used nl, not lf */
520 	if (*p <= 0x1f) {
521 		*pr->cchar = 's';
522 		printf(pr->fmt, list + (4 * (int)*p));
523 	} else if (*p == 0x7f) {
524 		*pr->cchar = 's';
525 		printf(pr->fmt, "del");
526 	} else if (*p < 0x7f) { /* isprint() */
527 		*pr->cchar = 'c';
528 		printf(pr->fmt, *p);
529 	} else {
530 		*pr->cchar = 'x';
531 		printf(pr->fmt, (int) *p);
532 	}
533 }
534 
display(priv_dumper_t * dumper)535 static NOINLINE void display(priv_dumper_t* dumper)
536 {
537 	unsigned char *bp;
538 	unsigned char savech = '\0';
539 
540 	while ((bp = get(dumper)) != NULL) {
541 		FS *fs;
542 		unsigned char *savebp;
543 		off_t saveaddress;
544 
545 		fs = dumper->pub.fshead;
546 		savebp = bp;
547 		saveaddress = dumper->pub.address;
548 		for (; fs; fs = fs->nextfs, bp = savebp, dumper->pub.address = saveaddress) {
549 			FU *fu;
550 			for (fu = fs->nextfu; fu; fu = fu->nextfu) {
551 				int cnt;
552 				if (fu->flags & F_IGNORE) {
553 					break;
554 				}
555 				for (cnt = fu->reps; cnt; --cnt) {
556 					PR *pr;
557 					for (pr = fu->nextpr; pr; dumper->pub.address += pr->bcnt,
558 								bp += pr->bcnt, pr = pr->nextpr) {
559 						if (dumper->eaddress
560 						 && dumper->pub.address >= dumper->eaddress
561 						) {
562 							if (dumper->pub.xxd_eofstring) {
563 								/* xxd support: requested to not pad incomplete blocks */
564 								fputs_stdout(dumper->pub.xxd_eofstring);
565 								return;
566 							}
567 							if (!(pr->flags & (F_TEXT | F_BPAD)))
568 								bpad(pr);
569 						}
570 						if (cnt == 1 && pr->nospace) {
571 							savech = *pr->nospace;
572 							*pr->nospace = '\0';
573 						}
574 						switch (pr->flags) {
575 						case F_ADDRESS:
576 							printf(pr->fmt, (unsigned long long) dumper->pub.address + dumper->pub.xxd_displayoff);
577 							break;
578 						case F_BPAD:
579 							printf(pr->fmt, "");
580 							break;
581 						case F_C:
582 							conv_c(pr, bp);
583 							break;
584 						case F_CHAR:
585 							printf(pr->fmt, *bp);
586 							break;
587 						case F_DBL: {
588 							double dval;
589 							float fval;
590 
591 							switch (pr->bcnt) {
592 							case 4:
593 								memcpy(&fval, bp, sizeof(fval));
594 								printf(pr->fmt, fval);
595 								break;
596 							case 8:
597 								memcpy(&dval, bp, sizeof(dval));
598 								printf(pr->fmt, dval);
599 								break;
600 							}
601 							break;
602 						}
603 						case F_INT: {
604 							int ival;
605 							short sval;
606 
607 							switch (pr->bcnt) {
608 							case 1:
609 								printf(pr->fmt, (int) *bp);
610 								break;
611 							case 2:
612 								memcpy(&sval, bp, sizeof(sval));
613 								printf(pr->fmt, (int) sval);
614 								break;
615 							case 4:
616 								memcpy(&ival, bp, sizeof(ival));
617 								printf(pr->fmt, ival);
618 								break;
619 							}
620 							break;
621 						}
622 						case F_P:
623 							printf(pr->fmt, isprint_asciionly(*bp) ? *bp : '.');
624 							break;
625 						case F_STR:
626 							printf(pr->fmt, (char *) bp);
627 							break;
628 						case F_TEXT:
629 							printf(pr->fmt);
630 							break;
631 						case F_U:
632 							conv_u(pr, bp);
633 							break;
634 						case F_UINT: {
635 							unsigned ival;
636 							unsigned short sval;
637 
638 							switch (pr->bcnt) {
639 							case 1:
640 								printf(pr->fmt, (unsigned) *bp);
641 								break;
642 							case 2:
643 								memcpy(&sval, bp, sizeof(sval));
644 								printf(pr->fmt, (unsigned) sval);
645 								break;
646 							case 4:
647 								memcpy(&ival, bp, sizeof(ival));
648 								printf(pr->fmt, ival);
649 								break;
650 							}
651 							break;
652 						}
653 						}
654 						if (cnt == 1 && pr->nospace) {
655 							*pr->nospace = savech;
656 						}
657 					}
658 				}
659 			}
660 		}
661 	}
662 
663 	if (dumper->endfu) {
664 		PR *pr;
665 		/*
666 		 * if eaddress not set, error or file size was multiple
667 		 * of blocksize, and no partial block ever found.
668 		 */
669 		if (!dumper->eaddress) {
670 			if (!dumper->pub.address) {
671 				return;
672 			}
673 			dumper->eaddress = dumper->pub.address;
674 		}
675 		for (pr = dumper->endfu->nextpr; pr; pr = pr->nextpr) {
676 			switch (pr->flags) {
677 			case F_ADDRESS:
678 				printf(pr->fmt, (unsigned long long) dumper->eaddress + dumper->pub.xxd_displayoff);
679 				break;
680 			case F_TEXT:
681 				printf(pr->fmt);
682 				break;
683 			}
684 		}
685 	}
686 }
687 
688 #define dumper ((priv_dumper_t*)pub_dumper)
bb_dump_dump(dumper_t * pub_dumper,char ** argv)689 int FAST_FUNC bb_dump_dump(dumper_t *pub_dumper, char **argv)
690 {
691 	FS *tfs;
692 	int blocksize;
693 
694 	/* figure out the data block size */
695 	blocksize = 0;
696 	tfs = dumper->pub.fshead;
697 	while (tfs) {
698 		tfs->bcnt = bb_dump_size(tfs);
699 		if (blocksize < tfs->bcnt) {
700 			blocksize = tfs->bcnt;
701 		}
702 		tfs = tfs->nextfs;
703 	}
704 	dumper->blocksize = blocksize;
705 
706 	/* rewrite the rules, do syntax checking */
707 	for (tfs = dumper->pub.fshead; tfs; tfs = tfs->nextfs) {
708 		rewrite(dumper, tfs);
709 	}
710 
711 	dumper->argv = argv;
712 	display(dumper);
713 
714 	return dumper->exitval;
715 }
716 
bb_dump_add(dumper_t * pub_dumper,const char * fmt)717 void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
718 {
719 	const char *p;
720 	FS *tfs;
721 	FU **nextfupp;
722 
723 	/* start new linked list of format units */
724 	tfs = xzalloc(sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */
725 	if (!dumper->pub.fshead) {
726 		dumper->pub.fshead = tfs;
727 	} else {
728 		FS *fslast = dumper->pub.fshead;
729 		while (fslast->nextfs)
730 			fslast = fslast->nextfs;
731 		fslast->nextfs = tfs;
732 	}
733 	nextfupp = &tfs->nextfu;
734 
735 	/* take the format string and break it up into format units */
736 	p = fmt;
737 	for (;;) {
738 		FU *tfu;
739 		const char *savep;
740 
741 		p = skip_whitespace(p);
742 		if (*p == '\0') {
743 			break;
744 		}
745 
746 		/* allocate a new format unit and link it in */
747 		/* NOSTRICT */
748 		/* DBU:[dave@cray.com] zalloc so that forward pointers start out NULL */
749 		tfu = xzalloc(sizeof(FU));
750 		*nextfupp = tfu;
751 		nextfupp = &tfu->nextfu;
752 		tfu->reps = 1;
753 
754 		/* if leading digit, repetition count */
755 		if (isdigit(*p)) {
756 			for (savep = p; isdigit(*p); ++p)
757 				continue;
758 			if (!isspace(*p) && *p != '/') {
759 				bb_error_msg_and_die("bad format {%s}", fmt);
760 			}
761 			/* may overwrite either white space or slash */
762 			tfu->reps = atoi(savep);
763 			tfu->flags = F_SETREP;
764 			/* skip trailing white space */
765 			p = skip_whitespace(++p);
766 		}
767 
768 		/* skip slash and trailing white space */
769 		if (*p == '/') {
770 			p = skip_whitespace(p + 1);
771 		}
772 
773 		/* byte count */
774 		if (isdigit(*p)) {
775 // TODO: use bb_strtou
776 			savep = p;
777 			while (isdigit(*++p))
778 				continue;
779 			if (!isspace(*p)) {
780 				bb_error_msg_and_die("bad format {%s}", fmt);
781 			}
782 // Above check prohibits formats such as '/1"%02x"' - it requires space after 1.
783 // Other than this, formats can be pretty much jammed together:
784 // "%07_ax:"8/2 "%04x|""\n"
785 // but this space is required. The check *can* be removed, but
786 // keeping it to stay compat with util-linux hexdump.
787 			tfu->bcnt = atoi(savep);
788 			/* skip trailing white space */
789 			p = skip_whitespace(p + 1);
790 		}
791 
792 		/* format */
793 		if (*p != '"') {
794 			bb_error_msg_and_die("bad format {%s}", fmt);
795 		}
796 		for (savep = ++p; *p != '"';) {
797 			if (*p++ == '\0') {
798 				bb_error_msg_and_die("bad format {%s}", fmt);
799 			}
800 		}
801 		tfu->fmt = xstrndup(savep, p - savep);
802 
803 		/* alphabetic escape sequences have to be done in place */
804 		strcpy_and_process_escape_sequences(tfu->fmt, tfu->fmt);
805 		/* unknown mappings are not changed: "\z" -> '\\' 'z' */
806 		/* trailing backslash, if any, is preserved */
807 #if 0
808 		char *p1;
809 		char *p2;
810 		p1 = tfu->fmt;
811 		for (p2 = p1;; ++p1, ++p2) {
812 			*p2 = *p1;
813 			if (*p1 == '\0')
814 				break;
815 
816 			if (*p1 == '\\') {
817 				const char *cs;
818 
819 				p1++;
820 				*p2 = *p1;
821 				if (*p1 == '\0') {
822 					/* "...\" trailing backslash. Eaten. */
823 					break;
824 				}
825 				cs = conv_str + 4; /* skip NUL element */
826 				do {
827 					/* map e.g. "\n" -> '\n' */
828 					if (*p1 == cs[2]) {
829 						*p2 = cs[0];
830 						break;
831 					}
832 					cs += 4;
833 				} while (*cs);
834 				/* unknown mappings remove bkslash: "\z" -> 'z' */
835 			}
836 		}
837 #endif
838 
839 		p++;
840 	}
841 }
842 
843 /*
844  * Copyright (c) 1989 The Regents of the University of California.
845  * All rights reserved.
846  *
847  * Redistribution and use in source and binary forms, with or without
848  * modification, are permitted provided that the following conditions
849  * are met:
850  * 1. Redistributions of source code must retain the above copyright
851  *    notice, this list of conditions and the following disclaimer.
852  * 2. Redistributions in binary form must reproduce the above copyright
853  *    notice, this list of conditions and the following disclaimer in the
854  *    documentation and/or other materials provided with the distribution.
855  * 3. Neither the name of the University nor the names of its contributors
856  *    may be used to endorse or promote products derived from this software
857  *    without specific prior written permission.
858  *
859  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
860  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
861  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
862  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
863  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
864  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
865  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
866  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
867  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
868  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
869  * SUCH DAMAGE.
870  */
871