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 && !memcmp(dumper->get__curp, dumper->get__savp, nread)) {
391 if (dumper->pub.dump_vflag != DUP) {
396 memset(dumper->get__curp + nread, 0, need);
397 dumper->eaddress = dumper->address + nread;
398 return dumper->get__curp;
400 n = fread(dumper->get__curp + nread, sizeof(unsigned char),
401 dumper->pub.dump_length == -1 ? need : MIN(dumper->pub.dump_length, need), stdin);
404 bb_simple_perror_msg(dumper->argv[-1]);
406 dumper->get__ateof = 1;
409 dumper->get__ateof = 0;
410 if (dumper->pub.dump_length != -1) {
411 dumper->pub.dump_length -= n;
415 if (dumper->pub.dump_vflag == ALL || dumper->pub.dump_vflag == FIRST
416 || memcmp(dumper->get__curp, dumper->get__savp, blocksize)
418 if (dumper->pub.dump_vflag == DUP || dumper->pub.dump_vflag == FIRST) {
419 dumper->pub.dump_vflag = WAIT;
421 return dumper->get__curp;
423 if (dumper->pub.dump_vflag == WAIT) {
426 dumper->pub.dump_vflag = DUP;
427 dumper->savaddress += blocksize;
428 dumper->address = dumper->savaddress;
437 static void bpad(PR *pr)
442 * remove all conversion flags; '-' is the only one valid
443 * with %s, and it's not useful here.
447 for (p1 = pr->fmt; *p1 != '%'; ++p1)
449 for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1)
452 while ((*p2++ = *p1++) != 0)
456 static const char conv_str[] ALIGN1 =
458 "\007""\\""a""\0" /* \a */
468 static void conv_c(PR *pr, unsigned char *p)
470 const char *str = conv_str;
476 goto strpr; /* map e.g. '\n' to "\\n" */
481 if (isprint_asciionly(*p)) {
485 sprintf(buf, "%03o", (int) *p);
489 printf(pr->fmt, str);
493 static void conv_u(PR *pr, unsigned char *p)
495 static const char list[] ALIGN1 =
496 "nul\0soh\0stx\0etx\0eot\0enq\0ack\0bel\0"
497 "bs\0_ht\0_lf\0_vt\0_ff\0_cr\0_so\0_si\0_"
498 "dle\0dcl\0dc2\0dc3\0dc4\0nak\0syn\0etb\0"
499 "can\0em\0_sub\0esc\0fs\0_gs\0_rs\0_us";
501 /* od used nl, not lf */
504 printf(pr->fmt, list + (4 * (int)*p));
505 } else if (*p == 0x7f) {
507 printf(pr->fmt, "del");
508 } else if (*p < 0x7f) { /* isprint() */
513 printf(pr->fmt, (int) *p);
517 static void display(priv_dumper_t* dumper)
523 unsigned char *bp, *savebp;
525 unsigned char savech = '\0';
527 while ((bp = get(dumper)) != NULL) {
528 fs = dumper->pub.fshead;
530 saveaddress = dumper->address;
531 for (; fs; fs = fs->nextfs, bp = savebp, dumper->address = saveaddress) {
532 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
533 if (fu->flags & F_IGNORE) {
536 for (cnt = fu->reps; cnt; --cnt) {
537 for (pr = fu->nextpr; pr; dumper->address += pr->bcnt,
538 bp += pr->bcnt, pr = pr->nextpr) {
539 if (dumper->eaddress && dumper->address >= dumper->eaddress
540 && !(pr->flags & (F_TEXT | F_BPAD))
544 if (cnt == 1 && pr->nospace) {
545 savech = *pr->nospace;
551 printf(pr->fmt, (unsigned) dumper->address);
560 printf(pr->fmt, *bp);
568 memcpy(&fval, bp, sizeof(fval));
569 printf(pr->fmt, fval);
572 memcpy(&dval, bp, sizeof(dval));
573 printf(pr->fmt, dval);
584 printf(pr->fmt, (int) *bp);
587 memcpy(&sval, bp, sizeof(sval));
588 printf(pr->fmt, (int) sval);
591 memcpy(&ival, bp, sizeof(ival));
592 printf(pr->fmt, ival);
598 printf(pr->fmt, isprint_asciionly(*bp) ? *bp : '.');
601 printf(pr->fmt, (char *) bp);
615 printf(pr->fmt, (unsigned) *bp);
618 memcpy(&sval, bp, sizeof(sval));
619 printf(pr->fmt, (unsigned) sval);
622 memcpy(&ival, bp, sizeof(ival));
623 printf(pr->fmt, ival);
629 if (cnt == 1 && pr->nospace) {
630 *pr->nospace = savech;
639 * if eaddress not set, error or file size was multiple
640 * of blocksize, and no partial block ever found.
642 if (!dumper->eaddress) {
643 if (!dumper->address) {
646 dumper->eaddress = dumper->address;
648 for (pr = dumper->endfu->nextpr; pr; pr = pr->nextpr) {
651 printf(pr->fmt, (unsigned) dumper->eaddress);
661 #define dumper ((priv_dumper_t*)pub_dumper)
662 int FAST_FUNC bb_dump_dump(dumper_t *pub_dumper, char **argv)
667 /* figure out the data block size */
669 tfs = dumper->pub.fshead;
671 tfs->bcnt = bb_dump_size(tfs);
672 if (blocksize < tfs->bcnt) {
673 blocksize = tfs->bcnt;
677 dumper->blocksize = blocksize;
679 /* rewrite the rules, do syntax checking */
680 for (tfs = dumper->pub.fshead; tfs; tfs = tfs->nextfs) {
681 rewrite(dumper, tfs);
687 return dumper->exitval;
690 void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
697 /* start new linked list of format units */
698 tfs = xzalloc(sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */
699 if (!dumper->pub.fshead) {
700 dumper->pub.fshead = tfs;
702 FS *fslast = dumper->pub.fshead;
703 while (fslast->nextfs)
704 fslast = fslast->nextfs;
705 fslast->nextfs = tfs;
707 nextfupp = &tfs->nextfu;
709 /* take the format string and break it up into format units */
712 p = skip_whitespace(p);
717 /* allocate a new format unit and link it in */
719 /* DBU:[dave@cray.com] zalloc so that forward pointers start out NULL */
720 tfu = xzalloc(sizeof(FU));
722 nextfupp = &tfu->nextfu;
725 /* if leading digit, repetition count */
727 for (savep = p; isdigit(*p); ++p)
729 if (!isspace(*p) && *p != '/') {
730 bb_error_msg_and_die("bad format {%s}", fmt);
732 /* may overwrite either white space or slash */
733 tfu->reps = atoi(savep);
734 tfu->flags = F_SETREP;
735 /* skip trailing white space */
736 p = skip_whitespace(++p);
739 /* skip slash and trailing white space */
741 p = skip_whitespace(p + 1);
746 // TODO: use bb_strtou
748 while (isdigit(*++p))
751 bb_error_msg_and_die("bad format {%s}", fmt);
753 // Above check prohibits formats such as '/1"%02x"' - it requires space after 1.
754 // Other than this, formats can be pretty much jammed together:
755 // "%07_ax:"8/2 "%04x|""\n"
756 // but this space is required. The check *can* be removed, but
757 // keeping it to stay compat with util-linux hexdump.
758 tfu->bcnt = atoi(savep);
759 /* skip trailing white space */
760 p = skip_whitespace(p + 1);
765 bb_error_msg_and_die("bad format {%s}", fmt);
767 for (savep = ++p; *p != '"';) {
769 bb_error_msg_and_die("bad format {%s}", fmt);
772 tfu->fmt = xstrndup(savep, p - savep);
774 /* alphabetic escape sequences have to be done in place */
775 strcpy_and_process_escape_sequences(tfu->fmt, tfu->fmt);
776 /* unknown mappings are not changed: "\z" -> '\\' 'z' */
777 /* trailing backslash, if any, is preserved */
782 for (p2 = p1;; ++p1, ++p2) {
793 /* "...\" trailing backslash. Eaten. */
796 cs = conv_str + 4; /* skip NUL element */
798 /* map e.g. "\n" -> '\n' */
805 /* unknown mappings remove bkslash: "\z" -> 'z' */
815 * Copyright (c) 1989 The Regents of the University of California.
816 * All rights reserved.
818 * Redistribution and use in source and binary forms, with or without
819 * modification, are permitted provided that the following conditions
821 * 1. Redistributions of source code must retain the above copyright
822 * notice, this list of conditions and the following disclaimer.
823 * 2. Redistributions in binary form must reproduce the above copyright
824 * notice, this list of conditions and the following disclaimer in the
825 * documentation and/or other materials provided with the distribution.
826 * 3. Neither the name of the University nor the names of its contributors
827 * may be used to endorse or promote products derived from this software
828 * without specific prior written permission.
830 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
831 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
832 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
833 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
834 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
835 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
836 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
837 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
838 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
839 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF