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
;
102 PR
*pr
, **nextpr
= NULL
;
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
; nextpr
= &pr
->nextpr
) {
116 /* DBU:[dvae@cray.com] zalloc so that forward ptrs start out NULL*/
117 pr
= xzalloc(sizeof(PR
));
120 /* ignore nextpr -- its unused inside the loop and is
121 * uninitialized 1st time through.
124 /* skip preceding text and up to the next % sign */
125 for (p1
= fmtp
; *p1
&& *p1
!= '%'; ++p1
)
128 /* only text in the string */
136 * get precision for %s -- if have a byte count, don't
141 /* skip to conversion character */
142 for (++p1
; strchr(index_str
, *p1
); ++p1
)
145 /* skip any special chars, field width */
146 while (strchr(index_str
+ 1, *++p1
))
148 if (*p1
== '.' && isdigit(*++p1
)) {
151 while (isdigit(*++p1
))
157 p2
= p1
+ 1; /* set end pointer */
160 * figure out the byte count for each conversion;
161 * rewrite the format as necessary, set up blank-
162 * pbb_dump_adding for end of data.
167 byte_count_str
= "\001";
171 if (fu
->bcnt
== *byte_count_str
) {
174 } while (*++byte_count_str
);
176 /* Unlike the original, output the remainder of the format string. */
177 if (!*byte_count_str
) {
178 bb_error_msg_and_die("bad byte count for conversion character %s", p1
);
180 pr
->bcnt
= *byte_count_str
;
181 } else if (*p1
== 'l') {
187 e
= strchr(lcc
, *p1
);
189 goto DO_BAD_CONV_CHAR
;
195 byte_count_str
= "\004\002\001";
199 } else if (strchr(lcc
, *p1
)) {
201 } else if (strchr("eEfgG", *p1
)) {
203 byte_count_str
= "\010\004";
205 } else if (*p1
== 's') {
207 if (sokay
== USEBCNT
) {
209 } else if (sokay
== USEPREC
) {
211 } else { /* NOTOKAY */
212 bb_error_msg_and_die("%%s requires a precision or a byte count");
214 } else if (*p1
== '_') {
219 fu
->flags
|= F_IGNORE
;
222 pr
->flags
= F_ADDRESS
;
224 if ((p1
[2] != 'd') && (p1
[2] != 'o') && (p1
[2] != 'x')) {
225 goto DO_BAD_CONV_CHAR
;
231 /* *p1 = 'c'; set in conv_c */
232 goto DO_BYTE_COUNT_1
;
236 goto DO_BYTE_COUNT_1
;
239 /* *p1 = 'c'; set in conv_u */
240 goto DO_BYTE_COUNT_1
;
242 goto DO_BAD_CONV_CHAR
;
246 bb_error_msg_and_die("bad conversion character %%%s", p1
);
250 * copy to PR format string, set conversion character
251 * pointer, update original.
255 pr
->fmt
= xstrdup(fmtp
);
257 //Too early! xrealloc can move pr->fmt!
258 //pr->cchar = pr->fmt + (p1 - fmtp);
260 /* DBU:[dave@cray.com] w/o this, trailing fmt text, space is lost.
261 * Skip subsequent text and up to the next % sign and tack the
262 * additional text onto fmt: eg. if fmt is "%x is a HEX number",
263 * we lose the " is a HEX number" part of fmt.
265 for (p3
= p2
; *p3
&& *p3
!= '%'; p3
++)
270 pr
->fmt
= xrealloc(pr
->fmt
, strlen(pr
->fmt
) + (p3
-p2
) + 1);
276 pr
->cchar
= pr
->fmt
+ (p1
- fmtp
);
279 /* only one conversion character if byte count */
280 if (!(pr
->flags
& F_ADDRESS
) && fu
->bcnt
&& nconv
++) {
281 bb_error_msg_and_die("byte count with multiple conversion characters");
285 * if format unit byte count not specified, figure it out
286 * so can adjust rep count later.
289 for (pr
= fu
->nextpr
; pr
; pr
= pr
->nextpr
)
290 fu
->bcnt
+= pr
->bcnt
;
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.
298 * if, rep count is greater than 1, no trailing whitespace
299 * gets output from the last iteration of the format unit.
301 for (fu
= fs
->nextfu
; fu
; fu
= fu
->nextfu
) {
302 if (!fu
->nextfu
&& fs
->bcnt
< dumper
->blocksize
303 && !(fu
->flags
& F_SETREP
) && fu
->bcnt
305 fu
->reps
+= (dumper
->blocksize
- fs
->bcnt
) / fu
->bcnt
;
308 for (pr
= fu
->nextpr
;; pr
= pr
->nextpr
)
311 for (p1
= pr
->fmt
, p2
= NULL
; *p1
; ++p1
)
312 p2
= isspace(*p1
) ? p1
: NULL
;
321 static void do_skip(priv_dumper_t
*dumper
, const char *fname
, int statok
)
326 xfstat(STDIN_FILENO
, &sbuf
, fname
);
327 if (!(S_ISCHR(sbuf
.st_mode
) || S_ISBLK(sbuf
.st_mode
) || S_ISFIFO(sbuf
.st_mode
))
328 && dumper
->pub
.dump_skip
>= sbuf
.st_size
330 /* If bb_dump_size valid and pub.dump_skip >= size */
331 dumper
->pub
.dump_skip
-= sbuf
.st_size
;
332 dumper
->address
+= sbuf
.st_size
;
336 if (fseek(stdin
, dumper
->pub
.dump_skip
, SEEK_SET
)) {
337 bb_simple_perror_msg_and_die(fname
);
339 dumper
->address
+= dumper
->pub
.dump_skip
;
340 dumper
->savaddress
= dumper
->address
;
341 dumper
->pub
.dump_skip
= 0;
344 static NOINLINE
int next(priv_dumper_t
*dumper
)
350 dumper
->next__done
= statok
= 1;
351 if (!(freopen(*dumper
->argv
, "r", stdin
))) {
352 bb_simple_perror_msg(*dumper
->argv
);
358 if (dumper
->next__done
)
359 return 0; /* no next file */
360 dumper
->next__done
= 1;
363 if (dumper
->pub
.dump_skip
)
364 do_skip(dumper
, statok
? *dumper
->argv
: "stdin", statok
);
367 if (!dumper
->pub
.dump_skip
)
373 static unsigned char *get(priv_dumper_t
*dumper
)
377 int blocksize
= dumper
->blocksize
;
379 if (!dumper
->get__curp
) {
380 dumper
->address
= (off_t
)0; /*DBU:[dave@cray.com] initialize,initialize..*/
381 dumper
->get__curp
= xmalloc(blocksize
);
382 dumper
->get__savp
= xzalloc(blocksize
); /* need to be initialized */
384 unsigned char *tmp
= dumper
->get__curp
;
385 dumper
->get__curp
= dumper
->get__savp
;
386 dumper
->get__savp
= tmp
;
387 dumper
->savaddress
+= blocksize
;
388 dumper
->address
= dumper
->savaddress
;
394 * if read the right number of bytes, or at EOF for one file,
395 * and no other files are available, zero-pad the rest of the
396 * block and set the end flag.
398 if (!dumper
->pub
.dump_length
|| (dumper
->get__ateof
&& !next(dumper
))) {
399 if (need
== blocksize
) {
402 if (dumper
->pub
.dump_vflag
!= ALL
&& !memcmp(dumper
->get__curp
, dumper
->get__savp
, nread
)) {
403 if (dumper
->pub
.dump_vflag
!= DUP
) {
408 memset(dumper
->get__curp
+ nread
, 0, need
);
409 dumper
->eaddress
= dumper
->address
+ nread
;
410 return dumper
->get__curp
;
412 n
= fread(dumper
->get__curp
+ nread
, sizeof(unsigned char),
413 dumper
->pub
.dump_length
== -1 ? need
: MIN(dumper
->pub
.dump_length
, need
), stdin
);
416 bb_simple_perror_msg(dumper
->argv
[-1]);
418 dumper
->get__ateof
= 1;
421 dumper
->get__ateof
= 0;
422 if (dumper
->pub
.dump_length
!= -1) {
423 dumper
->pub
.dump_length
-= n
;
427 if (dumper
->pub
.dump_vflag
== ALL
|| dumper
->pub
.dump_vflag
== FIRST
428 || memcmp(dumper
->get__curp
, dumper
->get__savp
, blocksize
)
430 if (dumper
->pub
.dump_vflag
== DUP
|| dumper
->pub
.dump_vflag
== FIRST
) {
431 dumper
->pub
.dump_vflag
= WAIT
;
433 return dumper
->get__curp
;
435 if (dumper
->pub
.dump_vflag
== WAIT
) {
438 dumper
->pub
.dump_vflag
= DUP
;
439 dumper
->savaddress
+= blocksize
;
440 dumper
->address
= dumper
->savaddress
;
449 static void bpad(PR
*pr
)
454 * remove all conversion flags; '-' is the only one valid
455 * with %s, and it's not useful here.
459 for (p1
= pr
->fmt
; *p1
!= '%'; ++p1
)
461 for (p2
= ++p1
; *p1
&& strchr(" -0+#", *p1
); ++p1
)
464 while ((*p2
++ = *p1
++) != 0)
468 static const char conv_str
[] ALIGN1
=
480 static void conv_c(PR
*pr
, unsigned char *p
)
482 const char *str
= conv_str
;
493 if (isprint_asciionly(*p
)) {
497 sprintf(buf
, "%03o", (int) *p
);
501 printf(pr
->fmt
, str
);
505 static void conv_u(PR
*pr
, unsigned char *p
)
507 static const char list
[] ALIGN1
=
508 "nul\0soh\0stx\0etx\0eot\0enq\0ack\0bel\0"
509 "bs\0_ht\0_lf\0_vt\0_ff\0_cr\0_so\0_si\0_"
510 "dle\0dcl\0dc2\0dc3\0dc4\0nak\0syn\0etb\0"
511 "can\0em\0_sub\0esc\0fs\0_gs\0_rs\0_us";
513 /* od used nl, not lf */
516 printf(pr
->fmt
, list
+ (4 * (int)*p
));
517 } else if (*p
== 0x7f) {
519 printf(pr
->fmt
, "del");
520 } else if (*p
< 0x7f) { /* isprint() */
525 printf(pr
->fmt
, (int) *p
);
529 static void display(priv_dumper_t
* dumper
)
535 unsigned char *bp
, *savebp
;
537 unsigned char savech
= '\0';
539 while ((bp
= get(dumper
)) != NULL
) {
540 fs
= dumper
->pub
.fshead
;
542 saveaddress
= dumper
->address
;
543 for (; fs
; fs
= fs
->nextfs
, bp
= savebp
, dumper
->address
= saveaddress
) {
544 for (fu
= fs
->nextfu
; fu
; fu
= fu
->nextfu
) {
545 if (fu
->flags
& F_IGNORE
) {
548 for (cnt
= fu
->reps
; cnt
; --cnt
) {
549 for (pr
= fu
->nextpr
; pr
; dumper
->address
+= pr
->bcnt
,
550 bp
+= pr
->bcnt
, pr
= pr
->nextpr
) {
551 if (dumper
->eaddress
&& dumper
->address
>= dumper
->eaddress
552 && !(pr
->flags
& (F_TEXT
| F_BPAD
))
556 if (cnt
== 1 && pr
->nospace
) {
557 savech
= *pr
->nospace
;
563 printf(pr
->fmt
, (unsigned) dumper
->address
);
572 printf(pr
->fmt
, *bp
);
580 memcpy(&fval
, bp
, sizeof(fval
));
581 printf(pr
->fmt
, fval
);
584 memcpy(&dval
, bp
, sizeof(dval
));
585 printf(pr
->fmt
, dval
);
596 printf(pr
->fmt
, (int) *bp
);
599 memcpy(&sval
, bp
, sizeof(sval
));
600 printf(pr
->fmt
, (int) sval
);
603 memcpy(&ival
, bp
, sizeof(ival
));
604 printf(pr
->fmt
, ival
);
610 printf(pr
->fmt
, isprint_asciionly(*bp
) ? *bp
: '.');
613 printf(pr
->fmt
, (char *) bp
);
627 printf(pr
->fmt
, (unsigned) *bp
);
630 memcpy(&sval
, bp
, sizeof(sval
));
631 printf(pr
->fmt
, (unsigned) sval
);
634 memcpy(&ival
, bp
, sizeof(ival
));
635 printf(pr
->fmt
, ival
);
641 if (cnt
== 1 && pr
->nospace
) {
642 *pr
->nospace
= savech
;
651 * if eaddress not set, error or file size was multiple
652 * of blocksize, and no partial block ever found.
654 if (!dumper
->eaddress
) {
655 if (!dumper
->address
) {
658 dumper
->eaddress
= dumper
->address
;
660 for (pr
= dumper
->endfu
->nextpr
; pr
; pr
= pr
->nextpr
) {
663 printf(pr
->fmt
, (unsigned) dumper
->eaddress
);
673 #define dumper ((priv_dumper_t*)pub_dumper)
674 int FAST_FUNC
bb_dump_dump(dumper_t
*pub_dumper
, char **argv
)
679 /* figure out the data block bb_dump_size */
681 tfs
= dumper
->pub
.fshead
;
683 tfs
->bcnt
= bb_dump_size(tfs
);
684 if (blocksize
< tfs
->bcnt
) {
685 blocksize
= tfs
->bcnt
;
689 dumper
->blocksize
= blocksize
;
691 /* rewrite the rules, do syntax checking */
692 for (tfs
= dumper
->pub
.fshead
; tfs
; tfs
= tfs
->nextfs
) {
693 rewrite(dumper
, tfs
);
699 return dumper
->exitval
;
702 void FAST_FUNC
bb_dump_add(dumper_t
* pub_dumper
, const char *fmt
)
711 /* start new linked list of format units */
712 tfs
= xzalloc(sizeof(FS
)); /*DBU:[dave@cray.com] start out NULL */
713 if (!dumper
->pub
.fshead
) {
714 dumper
->pub
.fshead
= tfs
;
716 FS
*fslast
= dumper
->pub
.fshead
;
717 while (fslast
->nextfs
)
718 fslast
= fslast
->nextfs
;
719 fslast
->nextfs
= tfs
;
721 nextfupp
= &tfs
->nextfu
;
723 /* take the format string and break it up into format units */
726 p
= skip_whitespace(p
);
731 /* allocate a new format unit and link it in */
733 /* DBU:[dave@cray.com] zalloc so that forward pointers start out NULL */
734 tfu
= xzalloc(sizeof(FU
));
736 nextfupp
= &tfu
->nextfu
;
739 /* if leading digit, repetition count */
741 for (savep
= p
; isdigit(*p
); ++p
)
743 if (!isspace(*p
) && *p
!= '/') {
744 bb_error_msg_and_die("bad format {%s}", fmt
);
746 /* may overwrite either white space or slash */
747 tfu
->reps
= atoi(savep
);
748 tfu
->flags
= F_SETREP
;
749 /* skip trailing white space */
750 p
= skip_whitespace(++p
);
753 /* skip slash and trailing white space */
755 p
= skip_whitespace(++p
);
760 // TODO: use bb_strtou
762 while (isdigit(*++p
))
765 bb_error_msg_and_die("bad format {%s}", fmt
);
767 tfu
->bcnt
= atoi(savep
);
768 /* skip trailing white space */
769 p
= skip_whitespace(++p
);
774 bb_error_msg_and_die("bad format {%s}", fmt
);
776 for (savep
= ++p
; *p
!= '"';) {
778 bb_error_msg_and_die("bad format {%s}", fmt
);
781 tfu
->fmt
= xstrndup(savep
, p
- savep
);
782 /* escape(tfu->fmt); */
786 /* alphabetic escape sequences have to be done in place */
787 for (p2
= p1
;; ++p1
, ++p2
) {
793 const char *cs
= conv_str
+ 4;
811 * Copyright (c) 1989 The Regents of the University of California.
812 * All rights reserved.
814 * Redistribution and use in source and binary forms, with or without
815 * modification, are permitted provided that the following conditions
817 * 1. Redistributions of source code must retain the above copyright
818 * notice, this list of conditions and the following disclaimer.
819 * 2. Redistributions in binary form must reproduce the above copyright
820 * notice, this list of conditions and the following disclaimer in the
821 * documentation and/or other materials provided with the distribution.
822 * 3. Neither the name of the University nor the names of its contributors
823 * may be used to endorse or promote products derived from this software
824 * without specific prior written permission.
826 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
827 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
828 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
829 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
830 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
831 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
832 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
833 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
834 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
835 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF