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.
17 static const char index_str[] ALIGN1 = ".#-+ 0123456789";
19 static const char size_conv_str[] ALIGN1 =
20 "\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG";
22 static const char lcc[] ALIGN1 = "diouxX";
25 typedef struct priv_dumper_t {
30 off_t savaddress; /* saved address/offset in stream */
31 off_t eaddress; /* end address */
32 off_t address; /* address/offset in stream */
34 smallint exitval; /* final exit value */
38 smallint get__ateof; // = 1;
39 unsigned char *get__curp;
40 unsigned char *get__savp;
43 dumper_t* FAST_FUNC alloc_dumper(void)
45 priv_dumper_t *dumper = xzalloc(sizeof(*dumper));
46 dumper->pub.dump_length = -1;
47 dumper->pub.dump_vflag = FIRST;
48 dumper->get__ateof = 1;
53 static NOINLINE int bb_dump_size(FS *fs)
61 /* figure out the data block bb_dump_size needed for each format unit */
62 for (cur_size = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
64 cur_size += fu->bcnt * fu->reps;
67 for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
71 * skip any special chars -- save precision in
72 * case it's a %s format.
74 while (strchr(index_str + 1, *++fmt));
75 if (*fmt == '.' && isdigit(*++fmt)) {
77 while (isdigit(*++fmt))
80 p = strchr(size_conv_str + 12, *fmt);
84 } else if (*fmt == '_') {
86 if ((*fmt == 'c') || (*fmt == 'p') || (*fmt == 'u')) {
91 bcnt += size_conv_str[p - (size_conv_str + 12)];
94 cur_size += bcnt * fu->reps;
99 static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
101 enum { NOTOKAY, USEBCNT, USEPREC } sokay;
106 const char *byte_count_str;
109 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
111 * break each format unit into print units; each
112 * conversion character gets its own.
114 for (nconv = 0, fmtp = fu->fmt; *fmtp; ) {
116 /* DBU:[dvae@cray.com] zalloc so that forward ptrs start out NULL*/
117 pr = xzalloc(sizeof(PR));
121 /* skip preceding text and up to the next % sign */
122 for (p1 = fmtp; *p1 && *p1 != '%'; ++p1)
125 /* only text in the string */
133 * get precision for %s -- if have a byte count, don't
138 /* skip to conversion character */
139 for (++p1; strchr(index_str, *p1); ++p1)
142 /* skip any special chars, field width */
143 while (strchr(index_str + 1, *++p1))
145 if (*p1 == '.' && isdigit(*++p1)) {
148 while (isdigit(*++p1))
154 p2 = p1 + 1; /* set end pointer */
157 * figure out the byte count for each conversion;
158 * rewrite the format as necessary, set up blank-
159 * pbb_dump_adding for end of data.
164 byte_count_str = "\001";
168 if (fu->bcnt == *byte_count_str) {
171 } while (*++byte_count_str);
173 /* Unlike the original, output the remainder of the format string. */
174 if (!*byte_count_str) {
175 bb_error_msg_and_die("bad byte count for conversion character %s", p1);
177 pr->bcnt = *byte_count_str;
178 } else if (*p1 == 'l') {
184 e = strchr(lcc, *p1);
186 goto DO_BAD_CONV_CHAR;
192 byte_count_str = "\004\002\001";
196 } else if (strchr(lcc, *p1)) {
198 } else if (strchr("eEfgG", *p1)) {
200 byte_count_str = "\010\004";
202 } else if (*p1 == 's') {
204 if (sokay == USEBCNT) {
206 } else if (sokay == USEPREC) {
208 } else { /* NOTOKAY */
209 bb_error_msg_and_die("%%s requires a precision or a byte count");
211 } else if (*p1 == '_') {
216 fu->flags |= F_IGNORE;
219 pr->flags = F_ADDRESS;
221 if ((p1[2] != 'd') && (p1[2] != 'o') && (p1[2] != 'x')) {
222 goto DO_BAD_CONV_CHAR;
228 /* *p1 = 'c'; set in conv_c */
229 goto DO_BYTE_COUNT_1;
233 goto DO_BYTE_COUNT_1;
236 /* *p1 = 'c'; set in conv_u */
237 goto DO_BYTE_COUNT_1;
239 goto DO_BAD_CONV_CHAR;
243 bb_error_msg_and_die("bad conversion character %%%s", p1);
247 * copy to PR format string, set conversion character
248 * pointer, update original.
252 pr->fmt = xstrdup(fmtp);
254 //Too early! xrealloc can move pr->fmt!
255 //pr->cchar = pr->fmt + (p1 - fmtp);
257 /* DBU:[dave@cray.com] w/o this, trailing fmt text, space is lost.
258 * Skip subsequent text and up to the next % sign and tack the
259 * additional text onto fmt: eg. if fmt is "%x is a HEX number",
260 * we lose the " is a HEX number" part of fmt.
262 for (p3 = p2; *p3 && *p3 != '%'; p3++)
267 pr->fmt = xrealloc(pr->fmt, strlen(pr->fmt) + (p3-p2) + 1);
273 pr->cchar = pr->fmt + (p1 - fmtp);
276 /* only one conversion character if byte count */
277 if (!(pr->flags & F_ADDRESS) && fu->bcnt && nconv++) {
278 bb_error_msg_and_die("byte count with multiple conversion characters");
282 * if format unit byte count not specified, figure it out
283 * so can adjust rep count later.
286 for (pr = fu->nextpr; pr; pr = pr->nextpr)
287 fu->bcnt += pr->bcnt;
290 * if the format string interprets any data at all, and it's
291 * not the same as the blocksize, and its last format unit
292 * interprets any data at all, and has no iteration count,
293 * repeat it as necessary.
295 * if, rep count is greater than 1, no trailing whitespace
296 * gets output from the last iteration of the format unit.
298 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
299 if (!fu->nextfu && fs->bcnt < dumper->blocksize
300 && !(fu->flags & F_SETREP) && fu->bcnt
302 fu->reps += (dumper->blocksize - fs->bcnt) / fu->bcnt;
305 for (pr = fu->nextpr;; pr = pr->nextpr)
308 for (p1 = pr->fmt, p2 = NULL; *p1; ++p1)
309 p2 = isspace(*p1) ? p1 : NULL;
318 static void do_skip(priv_dumper_t *dumper, const char *fname, int statok)
323 xfstat(STDIN_FILENO, &sbuf, fname);
324 if (!(S_ISCHR(sbuf.st_mode) || S_ISBLK(sbuf.st_mode) || S_ISFIFO(sbuf.st_mode))
325 && dumper->pub.dump_skip >= sbuf.st_size
327 /* If bb_dump_size valid and pub.dump_skip >= size */
328 dumper->pub.dump_skip -= sbuf.st_size;
329 dumper->address += sbuf.st_size;
333 if (fseek(stdin, dumper->pub.dump_skip, SEEK_SET)) {
334 bb_simple_perror_msg_and_die(fname);
336 dumper->address += dumper->pub.dump_skip;
337 dumper->savaddress = dumper->address;
338 dumper->pub.dump_skip = 0;
341 static NOINLINE int next(priv_dumper_t *dumper)
347 dumper->next__done = statok = 1;
348 if (!(freopen(*dumper->argv, "r", stdin))) {
349 bb_simple_perror_msg(*dumper->argv);
355 if (dumper->next__done)
356 return 0; /* no next file */
357 dumper->next__done = 1;
360 if (dumper->pub.dump_skip)
361 do_skip(dumper, statok ? *dumper->argv : "stdin", statok);
364 if (!dumper->pub.dump_skip)
370 static unsigned char *get(priv_dumper_t *dumper)
374 int blocksize = dumper->blocksize;
376 if (!dumper->get__curp) {
377 dumper->address = (off_t)0; /*DBU:[dave@cray.com] initialize,initialize..*/
378 dumper->get__curp = xmalloc(blocksize);
379 dumper->get__savp = xzalloc(blocksize); /* need to be initialized */
381 unsigned char *tmp = dumper->get__curp;
382 dumper->get__curp = dumper->get__savp;
383 dumper->get__savp = tmp;
384 dumper->savaddress += blocksize;
385 dumper->address = dumper->savaddress;
391 * if read the right number of bytes, or at EOF for one file,
392 * and no other files are available, zero-pad the rest of the
393 * block and set the end flag.
395 if (!dumper->pub.dump_length || (dumper->get__ateof && !next(dumper))) {
396 if (need == blocksize) {
399 if (dumper->pub.dump_vflag != ALL && !memcmp(dumper->get__curp, dumper->get__savp, nread)) {
400 if (dumper->pub.dump_vflag != DUP) {
405 memset(dumper->get__curp + nread, 0, need);
406 dumper->eaddress = dumper->address + nread;
407 return dumper->get__curp;
409 n = fread(dumper->get__curp + nread, sizeof(unsigned char),
410 dumper->pub.dump_length == -1 ? need : MIN(dumper->pub.dump_length, need), stdin);
413 bb_simple_perror_msg(dumper->argv[-1]);
415 dumper->get__ateof = 1;
418 dumper->get__ateof = 0;
419 if (dumper->pub.dump_length != -1) {
420 dumper->pub.dump_length -= n;
424 if (dumper->pub.dump_vflag == ALL || dumper->pub.dump_vflag == FIRST
425 || memcmp(dumper->get__curp, dumper->get__savp, blocksize)
427 if (dumper->pub.dump_vflag == DUP || dumper->pub.dump_vflag == FIRST) {
428 dumper->pub.dump_vflag = WAIT;
430 return dumper->get__curp;
432 if (dumper->pub.dump_vflag == WAIT) {
435 dumper->pub.dump_vflag = DUP;
436 dumper->savaddress += blocksize;
437 dumper->address = dumper->savaddress;
446 static void bpad(PR *pr)
451 * remove all conversion flags; '-' is the only one valid
452 * with %s, and it's not useful here.
456 for (p1 = pr->fmt; *p1 != '%'; ++p1)
458 for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1)
461 while ((*p2++ = *p1++) != 0)
465 static const char conv_str[] ALIGN1 =
477 static void conv_c(PR *pr, unsigned char *p)
479 const char *str = conv_str;
490 if (isprint_asciionly(*p)) {
494 sprintf(buf, "%03o", (int) *p);
498 printf(pr->fmt, str);
502 static void conv_u(PR *pr, unsigned char *p)
504 static const char list[] ALIGN1 =
505 "nul\0soh\0stx\0etx\0eot\0enq\0ack\0bel\0"
506 "bs\0_ht\0_lf\0_vt\0_ff\0_cr\0_so\0_si\0_"
507 "dle\0dcl\0dc2\0dc3\0dc4\0nak\0syn\0etb\0"
508 "can\0em\0_sub\0esc\0fs\0_gs\0_rs\0_us";
510 /* od used nl, not lf */
513 printf(pr->fmt, list + (4 * (int)*p));
514 } else if (*p == 0x7f) {
516 printf(pr->fmt, "del");
517 } else if (*p < 0x7f) { /* isprint() */
522 printf(pr->fmt, (int) *p);
526 static void display(priv_dumper_t* dumper)
532 unsigned char *bp, *savebp;
534 unsigned char savech = '\0';
536 while ((bp = get(dumper)) != NULL) {
537 fs = dumper->pub.fshead;
539 saveaddress = dumper->address;
540 for (; fs; fs = fs->nextfs, bp = savebp, dumper->address = saveaddress) {
541 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
542 if (fu->flags & F_IGNORE) {
545 for (cnt = fu->reps; cnt; --cnt) {
546 for (pr = fu->nextpr; pr; dumper->address += pr->bcnt,
547 bp += pr->bcnt, pr = pr->nextpr) {
548 if (dumper->eaddress && dumper->address >= dumper->eaddress
549 && !(pr->flags & (F_TEXT | F_BPAD))
553 if (cnt == 1 && pr->nospace) {
554 savech = *pr->nospace;
560 printf(pr->fmt, (unsigned) dumper->address);
569 printf(pr->fmt, *bp);
577 memcpy(&fval, bp, sizeof(fval));
578 printf(pr->fmt, fval);
581 memcpy(&dval, bp, sizeof(dval));
582 printf(pr->fmt, dval);
593 printf(pr->fmt, (int) *bp);
596 memcpy(&sval, bp, sizeof(sval));
597 printf(pr->fmt, (int) sval);
600 memcpy(&ival, bp, sizeof(ival));
601 printf(pr->fmt, ival);
607 printf(pr->fmt, isprint_asciionly(*bp) ? *bp : '.');
610 printf(pr->fmt, (char *) bp);
624 printf(pr->fmt, (unsigned) *bp);
627 memcpy(&sval, bp, sizeof(sval));
628 printf(pr->fmt, (unsigned) sval);
631 memcpy(&ival, bp, sizeof(ival));
632 printf(pr->fmt, ival);
638 if (cnt == 1 && pr->nospace) {
639 *pr->nospace = savech;
648 * if eaddress not set, error or file size was multiple
649 * of blocksize, and no partial block ever found.
651 if (!dumper->eaddress) {
652 if (!dumper->address) {
655 dumper->eaddress = dumper->address;
657 for (pr = dumper->endfu->nextpr; pr; pr = pr->nextpr) {
660 printf(pr->fmt, (unsigned) dumper->eaddress);
670 #define dumper ((priv_dumper_t*)pub_dumper)
671 int FAST_FUNC bb_dump_dump(dumper_t *pub_dumper, char **argv)
676 /* figure out the data block bb_dump_size */
678 tfs = dumper->pub.fshead;
680 tfs->bcnt = bb_dump_size(tfs);
681 if (blocksize < tfs->bcnt) {
682 blocksize = tfs->bcnt;
686 dumper->blocksize = blocksize;
688 /* rewrite the rules, do syntax checking */
689 for (tfs = dumper->pub.fshead; tfs; tfs = tfs->nextfs) {
690 rewrite(dumper, tfs);
696 return dumper->exitval;
699 void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
708 /* start new linked list of format units */
709 tfs = xzalloc(sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */
710 if (!dumper->pub.fshead) {
711 dumper->pub.fshead = tfs;
713 FS *fslast = dumper->pub.fshead;
714 while (fslast->nextfs)
715 fslast = fslast->nextfs;
716 fslast->nextfs = tfs;
718 nextfupp = &tfs->nextfu;
720 /* take the format string and break it up into format units */
723 p = skip_whitespace(p);
728 /* allocate a new format unit and link it in */
730 /* DBU:[dave@cray.com] zalloc so that forward pointers start out NULL */
731 tfu = xzalloc(sizeof(FU));
733 nextfupp = &tfu->nextfu;
736 /* if leading digit, repetition count */
738 for (savep = p; isdigit(*p); ++p)
740 if (!isspace(*p) && *p != '/') {
741 bb_error_msg_and_die("bad format {%s}", fmt);
743 /* may overwrite either white space or slash */
744 tfu->reps = atoi(savep);
745 tfu->flags = F_SETREP;
746 /* skip trailing white space */
747 p = skip_whitespace(++p);
750 /* skip slash and trailing white space */
752 p = skip_whitespace(++p);
757 // TODO: use bb_strtou
759 while (isdigit(*++p))
762 bb_error_msg_and_die("bad format {%s}", fmt);
764 tfu->bcnt = atoi(savep);
765 /* skip trailing white space */
766 p = skip_whitespace(++p);
771 bb_error_msg_and_die("bad format {%s}", fmt);
773 for (savep = ++p; *p != '"';) {
775 bb_error_msg_and_die("bad format {%s}", fmt);
778 tfu->fmt = xstrndup(savep, p - savep);
779 /* escape(tfu->fmt); */
783 /* alphabetic escape sequences have to be done in place */
784 for (p2 = p1;; ++p1, ++p2) {
790 const char *cs = conv_str + 4;
808 * Copyright (c) 1989 The Regents of the University of California.
809 * All rights reserved.
811 * Redistribution and use in source and binary forms, with or without
812 * modification, are permitted provided that the following conditions
814 * 1. Redistributions of source code must retain the above copyright
815 * notice, this list of conditions and the following disclaimer.
816 * 2. Redistributions in binary form must reproduce the above copyright
817 * notice, this list of conditions and the following disclaimer in the
818 * documentation and/or other materials provided with the distribution.
819 * 3. Neither the name of the University nor the names of its contributors
820 * may be used to endorse or promote products derived from this software
821 * without specific prior written permission.
823 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
824 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
825 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
826 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
827 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
828 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
829 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
830 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
831 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
832 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF