1 /* vi: set sw=4 ts=4: */
3 * Support code for the hexdump and od applets,
4 * based on code from util-linux v 2.11l
7 * The Regents of the University of California. All rights reserved.
9 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
11 * Original copyright notice is retained at the end of this file.
16 static const char dot_flags_width_chars[] ALIGN1 = ".#-+ 0123456789";
18 static const char size_conv_str[] ALIGN1 =
19 "\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG";
21 static const char int_convs[] ALIGN1 = "diouxX";
24 typedef struct priv_dumper_t {
29 off_t savaddress; /* saved address/offset in stream */
30 off_t eaddress; /* end address */
31 off_t address; /* address/offset in stream */
33 smallint exitval; /* final exit value */
37 smallint get__ateof; // = 1;
38 unsigned char *get__curp;
39 unsigned char *get__savp;
42 dumper_t* FAST_FUNC alloc_dumper(void)
44 priv_dumper_t *dumper = xzalloc(sizeof(*dumper));
45 dumper->pub.dump_length = -1;
46 dumper->pub.dump_vflag = FIRST;
47 dumper->get__ateof = 1;
52 static NOINLINE int bb_dump_size(FS *fs)
60 /* figure out the data block size needed for each format unit */
61 for (cur_size = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
63 cur_size += fu->bcnt * fu->reps;
66 for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
70 * skip any special chars -- save precision in
71 * case it's a %s format.
73 while (strchr(dot_flags_width_chars + 1, *++fmt))
75 if (*fmt == '.' && isdigit(*++fmt)) {
77 while (isdigit(*++fmt))
80 p = strchr(size_conv_str + 12, *fmt);
87 if ((*fmt == 'c') || (*fmt == 'p') || (*fmt == 'u')) {
95 cur_size += bcnt * fu->reps;
100 static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
104 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
110 * break each format unit into print units; each
111 * conversion character gets its own.
113 for (fmtp = fu->fmt; *fmtp; ) {
116 const char *byte_count_str;
118 /* DBU:[dvae@cray.com] zalloc so that forward ptrs start out NULL */
119 pr = xzalloc(sizeof(*pr));
123 /* skip preceding text and up to the next % sign */
124 p1 = strchr(fmtp, '%');
125 if (!p1) { /* only text in the string */
132 * get precision for %s -- if have a byte count, don't
137 /* skip to conversion character */
138 while (strchr(dot_flags_width_chars, *++p1))
141 /* skip any special chars, field width */
142 while (strchr(dot_flags_width_chars + 1, *++p1))
144 if (*p1 == '.' && isdigit(*++p1)) {
146 while (isdigit(*++p1))
151 p2 = p1 + 1; /* set end pointer */
154 * figure out the byte count for each conversion;
155 * rewrite the format as necessary, set up blank-
156 * padding for end of data.
161 byte_count_str = "\001";
165 if (fu->bcnt == *byte_count_str)
167 if (*++byte_count_str == 0)
168 bb_error_msg_and_die("bad byte count for conversion character %s", p1);
171 /* Unlike the original, output the remainder of the format string. */
172 pr->bcnt = *byte_count_str;
174 if (*p1 == 'l') { /* %ld etc */
180 e = strchr(int_convs, *p1); /* "diouxX"? */
182 goto DO_BAD_CONV_CHAR;
184 if (e > int_convs + 1) /* not d or i? */
186 byte_count_str = "\004\002\001";
189 if (strchr(int_convs, *p1)) { /* %d etc */
192 if (strchr("eEfgG", *p1)) { /* floating point */
194 byte_count_str = "\010\004";
202 bb_error_msg_and_die("%%s needs precision or byte count");
203 pr->bcnt = atoi(prec);
207 p2++; /* move past a in "%_a" */
209 case 'A': /* %_A[dox]: print address and the end */
211 fu->flags |= F_IGNORE;
213 case 'a': /* %_a[dox]: current address */
214 pr->flags = F_ADDRESS;
215 p2++; /* move past x in "%_ax" */
216 if ((p1[2] != 'd') && (p1[2] != 'o') && (p1[2] != 'x')) {
217 goto DO_BAD_CONV_CHAR;
221 case 'c': /* %_c: chars, \ooo, \n \r \t etc */
223 /* *p1 = 'c'; set in conv_c */
224 goto DO_BYTE_COUNT_1;
225 case 'p': /* %_p: chars, dots for nonprintable */
228 goto DO_BYTE_COUNT_1;
229 case 'u': /* %_p: chars, 'nul', 'esc' etc for nonprintable */
231 /* *p1 = 'c'; set in conv_u */
232 goto DO_BYTE_COUNT_1;
234 goto DO_BAD_CONV_CHAR;
238 bb_error_msg_and_die("bad conversion character %%%s", p1);
242 * copy to PR format string, set conversion character
243 * pointer, update original.
245 len = (p1 - fmtp) + 1;
246 pr->fmt = xstrndup(fmtp, len);
247 /* DBU:[dave@cray.com] w/o this, trailing fmt text, space is lost.
248 * Skip subsequent text and up to the next % sign and tack the
249 * additional text onto fmt: eg. if fmt is "%x is a HEX number",
250 * we lose the " is a HEX number" part of fmt.
252 for (p3 = p2; *p3 && *p3 != '%'; p3++)
254 if ((p3 - p2) != 0) {
256 pr->fmt = d = xrealloc(pr->fmt, len + (p3 - p2) + 1);
264 pr->cchar = pr->fmt + len - 1; /* must be after realloc! */
267 /* only one conversion character if byte count */
268 if (!(pr->flags & F_ADDRESS) && fu->bcnt && nconv++) {
269 bb_error_msg_and_die("byte count with multiple conversion characters");
273 * if format unit byte count not specified, figure it out
274 * so can adjust rep count later.
277 for (pr = fu->nextpr; pr; pr = pr->nextpr)
278 fu->bcnt += pr->bcnt;
281 * if the format string interprets any data at all, and it's
282 * not the same as the blocksize, and its last format unit
283 * interprets any data at all, and has no iteration count,
284 * repeat it as necessary.
286 * if rep count is greater than 1, no trailing whitespace
287 * gets output from the last iteration of the format unit.
289 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
291 && fs->bcnt < dumper->blocksize
292 && !(fu->flags & F_SETREP)
295 fu->reps += (dumper->blocksize - fs->bcnt) / fu->bcnt;
297 if (fu->reps > 1 && fu->nextpr) {
301 for (pr = fu->nextpr;; pr = pr->nextpr)
305 for (p1 = pr->fmt; *p1; ++p1)
306 p2 = isspace(*p1) ? p1 : NULL;
313 static void do_skip(priv_dumper_t *dumper, const char *fname)
317 xfstat(STDIN_FILENO, &sbuf, fname);
318 if (S_ISREG(sbuf.st_mode)
319 && dumper->pub.dump_skip >= sbuf.st_size
321 /* If st_size is valid and pub.dump_skip >= st_size */
322 dumper->pub.dump_skip -= sbuf.st_size;
323 dumper->address += sbuf.st_size;
326 if (fseeko(stdin, dumper->pub.dump_skip, SEEK_SET)) {
327 bb_simple_perror_msg_and_die(fname);
329 dumper->address += dumper->pub.dump_skip;
330 dumper->savaddress = dumper->address;
331 dumper->pub.dump_skip = 0;
334 static NOINLINE int next(priv_dumper_t *dumper)
337 const char *fname = *dumper->argv;
341 if (NOT_LONE_DASH(fname)) {
342 if (!freopen(fname, "r", stdin)) {
343 bb_simple_perror_msg(fname);
349 if (dumper->next__done)
350 return 0; /* no next file */
352 dumper->next__done = 1;
353 if (dumper->pub.dump_skip)
354 do_skip(dumper, fname ? fname : "stdin");
355 if (dumper->pub.dump_skip == 0)
361 static unsigned char *get(priv_dumper_t *dumper)
365 int blocksize = dumper->blocksize;
367 if (!dumper->get__curp) {
368 dumper->address = (off_t)0; /*DBU:[dave@cray.com] initialize,initialize..*/
369 dumper->get__curp = xmalloc(blocksize);
370 dumper->get__savp = xzalloc(blocksize); /* need to be initialized */
372 unsigned char *tmp = dumper->get__curp;
373 dumper->get__curp = dumper->get__savp;
374 dumper->get__savp = tmp;
375 dumper->savaddress += blocksize;
376 dumper->address = dumper->savaddress;
382 * if read the right number of bytes, or at EOF for one file,
383 * and no other files are available, zero-pad the rest of the
384 * block and set the end flag.
386 if (!dumper->pub.dump_length || (dumper->get__ateof && !next(dumper))) {
387 if (need == blocksize) {
390 if (dumper->pub.dump_vflag != ALL /* not "show all"? */
391 && dumper->pub.dump_vflag != FIRST /* not first line? */
392 && memcmp(dumper->get__curp, dumper->get__savp, nread) == 0 /* same data? */
394 if (dumper->pub.dump_vflag != DUP) {
399 memset(dumper->get__curp + nread, 0, need);
400 dumper->eaddress = dumper->address + nread;
401 return dumper->get__curp;
403 n = fread(dumper->get__curp + nread, sizeof(unsigned char),
404 dumper->pub.dump_length == -1 ? need : MIN(dumper->pub.dump_length, need), stdin);
407 bb_simple_perror_msg(dumper->argv[-1]);
409 dumper->get__ateof = 1;
412 dumper->get__ateof = 0;
413 if (dumper->pub.dump_length != -1) {
414 dumper->pub.dump_length -= n;
418 if (dumper->pub.dump_vflag == ALL /* "show all"? */
419 || dumper->pub.dump_vflag == FIRST /* first line? */
420 || memcmp(dumper->get__curp, dumper->get__savp, blocksize) != 0 /* not same data? */
422 if (dumper->pub.dump_vflag == DUP || dumper->pub.dump_vflag == FIRST) {
423 dumper->pub.dump_vflag = WAIT;
425 return dumper->get__curp;
427 if (dumper->pub.dump_vflag == WAIT) {
430 dumper->pub.dump_vflag = DUP;
431 dumper->savaddress += blocksize;
432 dumper->address = dumper->savaddress;
441 static void bpad(PR *pr)
446 * remove all conversion flags; '-' is the only one valid
447 * with %s, and it's not useful here.
451 for (p1 = pr->fmt; *p1 != '%'; ++p1)
453 for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1)
456 while ((*p2++ = *p1++) != 0)
460 static const char conv_str[] ALIGN1 =
462 "\007""\\""a""\0" /* \a */
471 static void conv_c(PR *pr, unsigned char *p)
473 const char *str = conv_str;
478 goto strpr; /* map e.g. '\n' to "\\n" */
483 if (isprint_asciionly(*p)) {
488 /* gcc-8.0.1 needs lots of casts to shut up */
489 sprintf(buf, "%03o", (unsigned)(uint8_t)*p);
493 printf(pr->fmt, str);
497 static void conv_u(PR *pr, unsigned char *p)
499 static const char list[] ALIGN1 =
500 "nul\0soh\0stx\0etx\0eot\0enq\0ack\0bel\0"
501 "bs\0_ht\0_lf\0_vt\0_ff\0_cr\0_so\0_si\0_"
502 "dle\0dcl\0dc2\0dc3\0dc4\0nak\0syn\0etb\0"
503 "can\0em\0_sub\0esc\0fs\0_gs\0_rs\0_us";
505 /* od used nl, not lf */
508 printf(pr->fmt, list + (4 * (int)*p));
509 } else if (*p == 0x7f) {
511 printf(pr->fmt, "del");
512 } else if (*p < 0x7f) { /* isprint() */
517 printf(pr->fmt, (int) *p);
521 static void display(priv_dumper_t* dumper)
527 unsigned char *bp, *savebp;
529 unsigned char savech = '\0';
531 while ((bp = get(dumper)) != NULL) {
532 fs = dumper->pub.fshead;
534 saveaddress = dumper->address;
535 for (; fs; fs = fs->nextfs, bp = savebp, dumper->address = saveaddress) {
536 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
537 if (fu->flags & F_IGNORE) {
540 for (cnt = fu->reps; cnt; --cnt) {
541 for (pr = fu->nextpr; pr; dumper->address += pr->bcnt,
542 bp += pr->bcnt, pr = pr->nextpr) {
543 if (dumper->eaddress && dumper->address >= dumper->eaddress
544 && !(pr->flags & (F_TEXT | F_BPAD))
548 if (cnt == 1 && pr->nospace) {
549 savech = *pr->nospace;
555 printf(pr->fmt, (unsigned) dumper->address);
564 printf(pr->fmt, *bp);
572 memcpy(&fval, bp, sizeof(fval));
573 printf(pr->fmt, fval);
576 memcpy(&dval, bp, sizeof(dval));
577 printf(pr->fmt, dval);
588 printf(pr->fmt, (int) *bp);
591 memcpy(&sval, bp, sizeof(sval));
592 printf(pr->fmt, (int) sval);
595 memcpy(&ival, bp, sizeof(ival));
596 printf(pr->fmt, ival);
602 printf(pr->fmt, isprint_asciionly(*bp) ? *bp : '.');
605 printf(pr->fmt, (char *) bp);
619 printf(pr->fmt, (unsigned) *bp);
622 memcpy(&sval, bp, sizeof(sval));
623 printf(pr->fmt, (unsigned) sval);
626 memcpy(&ival, bp, sizeof(ival));
627 printf(pr->fmt, ival);
633 if (cnt == 1 && pr->nospace) {
634 *pr->nospace = savech;
643 * if eaddress not set, error or file size was multiple
644 * of blocksize, and no partial block ever found.
646 if (!dumper->eaddress) {
647 if (!dumper->address) {
650 dumper->eaddress = dumper->address;
652 for (pr = dumper->endfu->nextpr; pr; pr = pr->nextpr) {
655 printf(pr->fmt, (unsigned) dumper->eaddress);
665 #define dumper ((priv_dumper_t*)pub_dumper)
666 int FAST_FUNC bb_dump_dump(dumper_t *pub_dumper, char **argv)
671 /* figure out the data block size */
673 tfs = dumper->pub.fshead;
675 tfs->bcnt = bb_dump_size(tfs);
676 if (blocksize < tfs->bcnt) {
677 blocksize = tfs->bcnt;
681 dumper->blocksize = blocksize;
683 /* rewrite the rules, do syntax checking */
684 for (tfs = dumper->pub.fshead; tfs; tfs = tfs->nextfs) {
685 rewrite(dumper, tfs);
691 return dumper->exitval;
694 void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
701 /* start new linked list of format units */
702 tfs = xzalloc(sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */
703 if (!dumper->pub.fshead) {
704 dumper->pub.fshead = tfs;
706 FS *fslast = dumper->pub.fshead;
707 while (fslast->nextfs)
708 fslast = fslast->nextfs;
709 fslast->nextfs = tfs;
711 nextfupp = &tfs->nextfu;
713 /* take the format string and break it up into format units */
716 p = skip_whitespace(p);
721 /* allocate a new format unit and link it in */
723 /* DBU:[dave@cray.com] zalloc so that forward pointers start out NULL */
724 tfu = xzalloc(sizeof(FU));
726 nextfupp = &tfu->nextfu;
729 /* if leading digit, repetition count */
731 for (savep = p; isdigit(*p); ++p)
733 if (!isspace(*p) && *p != '/') {
734 bb_error_msg_and_die("bad format {%s}", fmt);
736 /* may overwrite either white space or slash */
737 tfu->reps = atoi(savep);
738 tfu->flags = F_SETREP;
739 /* skip trailing white space */
740 p = skip_whitespace(++p);
743 /* skip slash and trailing white space */
745 p = skip_whitespace(p + 1);
750 // TODO: use bb_strtou
752 while (isdigit(*++p))
755 bb_error_msg_and_die("bad format {%s}", fmt);
757 // Above check prohibits formats such as '/1"%02x"' - it requires space after 1.
758 // Other than this, formats can be pretty much jammed together:
759 // "%07_ax:"8/2 "%04x|""\n"
760 // but this space is required. The check *can* be removed, but
761 // keeping it to stay compat with util-linux hexdump.
762 tfu->bcnt = atoi(savep);
763 /* skip trailing white space */
764 p = skip_whitespace(p + 1);
769 bb_error_msg_and_die("bad format {%s}", fmt);
771 for (savep = ++p; *p != '"';) {
773 bb_error_msg_and_die("bad format {%s}", fmt);
776 tfu->fmt = xstrndup(savep, p - savep);
778 /* alphabetic escape sequences have to be done in place */
779 strcpy_and_process_escape_sequences(tfu->fmt, tfu->fmt);
780 /* unknown mappings are not changed: "\z" -> '\\' 'z' */
781 /* trailing backslash, if any, is preserved */
786 for (p2 = p1;; ++p1, ++p2) {
797 /* "...\" trailing backslash. Eaten. */
800 cs = conv_str + 4; /* skip NUL element */
802 /* map e.g. "\n" -> '\n' */
809 /* unknown mappings remove bkslash: "\z" -> 'z' */
819 * Copyright (c) 1989 The Regents of the University of California.
820 * All rights reserved.
822 * Redistribution and use in source and binary forms, with or without
823 * modification, are permitted provided that the following conditions
825 * 1. Redistributions of source code must retain the above copyright
826 * notice, this list of conditions and the following disclaimer.
827 * 2. Redistributions in binary form must reproduce the above copyright
828 * notice, this list of conditions and the following disclaimer in the
829 * documentation and/or other materials provided with the distribution.
830 * 3. Neither the name of the University nor the names of its contributors
831 * may be used to endorse or promote products derived from this software
832 * without specific prior written permission.
834 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
835 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
836 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
837 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
838 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
839 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
840 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
841 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
842 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
843 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF