source: MondoRescue/branches/3.2/mindi-busybox/coreutils/od_bloaty.c@ 3232

Last change on this file since 3232 was 3232, checked in by Bruno Cornec, 10 years ago
  • Update mindi-busybox to 1.21.1
  • Property svn:eol-style set to native
File size: 36.2 KB
Line 
1/* od -- dump files in octal and other formats
2 Copyright (C) 92, 1995-2004 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18/* Written by Jim Meyering. */
19/* Busyboxed by Denys Vlasenko, based on od.c from coreutils-5.2.1 */
20
21
22/* #include "libbb.h" - done in od.c */
23#define assert(a) ((void)0)
24
25
26//usage:#if ENABLE_DESKTOP
27//usage:#define od_trivial_usage
28//usage: "[-abcdfhilovxs] [-t TYPE] [-A RADIX] [-N SIZE] [-j SKIP] [-S MINSTR] [-w WIDTH] [FILE]..."
29// We don't support:
30// ... [FILE] [[+]OFFSET[.][b]]
31// Support is buggy for:
32// od --traditional [OPTION]... [FILE] [[+]OFFSET[.][b] [+][LABEL][.][b]]
33
34//usage:#define od_full_usage "\n\n"
35//usage: "Print FILEs (or stdin) unambiguously, as octal bytes by default"
36//usage:#endif
37
38enum {
39 OPT_A = 1 << 0,
40 OPT_N = 1 << 1,
41 OPT_a = 1 << 2,
42 OPT_b = 1 << 3,
43 OPT_c = 1 << 4,
44 OPT_d = 1 << 5,
45 OPT_f = 1 << 6,
46 OPT_h = 1 << 7,
47 OPT_i = 1 << 8,
48 OPT_j = 1 << 9,
49 OPT_l = 1 << 10,
50 OPT_o = 1 << 11,
51 OPT_t = 1 << 12,
52 /* When zero and two or more consecutive blocks are equal, format
53 only the first block and output an asterisk alone on the following
54 line to indicate that identical blocks have been elided: */
55 OPT_v = 1 << 13,
56 OPT_x = 1 << 14,
57 OPT_s = 1 << 15,
58 OPT_S = 1 << 16,
59 OPT_w = 1 << 17,
60 OPT_traditional = (1 << 18) * ENABLE_LONG_OPTS,
61};
62
63#define OD_GETOPT32() getopt32(argv, \
64 "A:N:abcdfhij:lot:vxsS:w::", \
65 /* -w with optional param */ \
66 /* -S was -s and also had optional parameter */ \
67 /* but in coreutils 6.3 it was renamed and now has */ \
68 /* _mandatory_ parameter */ \
69 &str_A, &str_N, &str_j, &lst_t, &str_S, &bytes_per_block)
70
71
72/* Check for 0x7f is a coreutils 6.3 addition */
73#define ISPRINT(c) (((c) >= ' ') && (c) < 0x7f)
74
75typedef long double longdouble_t;
76typedef unsigned long long ulonglong_t;
77typedef long long llong;
78
79#if ENABLE_LFS
80# define xstrtooff_sfx xstrtoull_sfx
81#else
82# define xstrtooff_sfx xstrtoul_sfx
83#endif
84
85/* The default number of input bytes per output line. */
86#define DEFAULT_BYTES_PER_BLOCK 16
87
88/* The number of decimal digits of precision in a float. */
89#ifndef FLT_DIG
90# define FLT_DIG 7
91#endif
92
93/* The number of decimal digits of precision in a double. */
94#ifndef DBL_DIG
95# define DBL_DIG 15
96#endif
97
98/* The number of decimal digits of precision in a long double. */
99#ifndef LDBL_DIG
100# define LDBL_DIG DBL_DIG
101#endif
102
103enum size_spec {
104 NO_SIZE,
105 CHAR,
106 SHORT,
107 INT,
108 LONG,
109 LONG_LONG,
110 FLOAT_SINGLE,
111 FLOAT_DOUBLE,
112 FLOAT_LONG_DOUBLE,
113 N_SIZE_SPECS
114};
115
116enum output_format {
117 SIGNED_DECIMAL,
118 UNSIGNED_DECIMAL,
119 OCTAL,
120 HEXADECIMAL,
121 FLOATING_POINT,
122 NAMED_CHARACTER,
123 CHARACTER
124};
125
126/* Each output format specification (from '-t spec' or from
127 old-style options) is represented by one of these structures. */
128struct tspec {
129 enum output_format fmt;
130 enum size_spec size;
131 void (*print_function) (size_t, const char *, const char *);
132 char *fmt_string;
133 int hexl_mode_trailer;
134 int field_width;
135};
136
137/* Convert the number of 8-bit bytes of a binary representation to
138 the number of characters (digits + sign if the type is signed)
139 required to represent the same quantity in the specified base/type.
140 For example, a 32-bit (4-byte) quantity may require a field width
141 as wide as the following for these types:
142 11 unsigned octal
143 11 signed decimal
144 10 unsigned decimal
145 8 unsigned hexadecimal */
146
147static const uint8_t bytes_to_oct_digits[] ALIGN1 =
148{0, 3, 6, 8, 11, 14, 16, 19, 22, 25, 27, 30, 32, 35, 38, 41, 43};
149
150static const uint8_t bytes_to_signed_dec_digits[] ALIGN1 =
151{1, 4, 6, 8, 11, 13, 16, 18, 20, 23, 25, 28, 30, 33, 35, 37, 40};
152
153static const uint8_t bytes_to_unsigned_dec_digits[] ALIGN1 =
154{0, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39};
155
156static const uint8_t bytes_to_hex_digits[] ALIGN1 =
157{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32};
158
159/* Convert enum size_spec to the size of the named type. */
160static const signed char width_bytes[] ALIGN1 = {
161 -1,
162 sizeof(char),
163 sizeof(short),
164 sizeof(int),
165 sizeof(long),
166 sizeof(ulonglong_t),
167 sizeof(float),
168 sizeof(double),
169 sizeof(longdouble_t)
170};
171/* Ensure that for each member of 'enum size_spec' there is an
172 initializer in the width_bytes array. */
173struct ERR_width_bytes_has_bad_size {
174 char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1];
175};
176
177static smallint exit_code;
178
179static unsigned string_min;
180
181/* An array of specs describing how to format each input block. */
182static size_t n_specs;
183static struct tspec *spec;
184
185/* Function that accepts an address and an optional following char,
186 and prints the address and char to stdout. */
187static void (*format_address)(off_t, char);
188/* The difference between the old-style pseudo starting address and
189 the number of bytes to skip. */
190#if ENABLE_LONG_OPTS
191static off_t pseudo_offset;
192#else
193enum { pseudo_offset = 0 };
194#endif
195/* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all
196 input is formatted. */
197
198/* The number of input bytes formatted per output line. It must be
199 a multiple of the least common multiple of the sizes associated with
200 the specified output types. It should be as large as possible, but
201 no larger than 16 -- unless specified with the -w option. */
202static unsigned bytes_per_block = 32; /* have to use unsigned, not size_t */
203
204/* A NULL-terminated list of the file-arguments from the command line. */
205static const char *const *file_list;
206
207/* The input stream associated with the current file. */
208static FILE *in_stream;
209
210#define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t)
211static const unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] ALIGN1 = {
212 [sizeof(char)] = CHAR,
213#if USHRT_MAX != UCHAR_MAX
214 [sizeof(short)] = SHORT,
215#endif
216#if UINT_MAX != USHRT_MAX
217 [sizeof(int)] = INT,
218#endif
219#if ULONG_MAX != UINT_MAX
220 [sizeof(long)] = LONG,
221#endif
222#if ULLONG_MAX != ULONG_MAX
223 [sizeof(ulonglong_t)] = LONG_LONG,
224#endif
225};
226
227#define MAX_FP_TYPE_SIZE sizeof(longdouble_t)
228static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = {
229 /* gcc seems to allow repeated indexes. Last one wins */
230 [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE,
231 [sizeof(double)] = FLOAT_DOUBLE,
232 [sizeof(float)] = FLOAT_SINGLE
233};
234
235
236static unsigned
237gcd(unsigned u, unsigned v)
238{
239 unsigned t;
240 while (v != 0) {
241 t = u % v;
242 u = v;
243 v = t;
244 }
245 return u;
246}
247
248/* Compute the least common multiple of U and V. */
249static unsigned
250lcm(unsigned u, unsigned v) {
251 unsigned t = gcd(u, v);
252 if (t == 0)
253 return 0;
254 return u * v / t;
255}
256
257static void
258print_s_char(size_t n_bytes, const char *block, const char *fmt_string)
259{
260 while (n_bytes--) {
261 int tmp = *(signed char *) block;
262 printf(fmt_string, tmp);
263 block += sizeof(unsigned char);
264 }
265}
266
267static void
268print_char(size_t n_bytes, const char *block, const char *fmt_string)
269{
270 while (n_bytes--) {
271 unsigned tmp = *(unsigned char *) block;
272 printf(fmt_string, tmp);
273 block += sizeof(unsigned char);
274 }
275}
276
277static void
278print_s_short(size_t n_bytes, const char *block, const char *fmt_string)
279{
280 n_bytes /= sizeof(signed short);
281 while (n_bytes--) {
282 int tmp = *(signed short *) block;
283 printf(fmt_string, tmp);
284 block += sizeof(unsigned short);
285 }
286}
287
288static void
289print_short(size_t n_bytes, const char *block, const char *fmt_string)
290{
291 n_bytes /= sizeof(unsigned short);
292 while (n_bytes--) {
293 unsigned tmp = *(unsigned short *) block;
294 printf(fmt_string, tmp);
295 block += sizeof(unsigned short);
296 }
297}
298
299static void
300print_int(size_t n_bytes, const char *block, const char *fmt_string)
301{
302 n_bytes /= sizeof(unsigned);
303 while (n_bytes--) {
304 unsigned tmp = *(unsigned *) block;
305 printf(fmt_string, tmp);
306 block += sizeof(unsigned);
307 }
308}
309
310#if UINT_MAX == ULONG_MAX
311# define print_long print_int
312#else
313static void
314print_long(size_t n_bytes, const char *block, const char *fmt_string)
315{
316 n_bytes /= sizeof(unsigned long);
317 while (n_bytes--) {
318 unsigned long tmp = *(unsigned long *) block;
319 printf(fmt_string, tmp);
320 block += sizeof(unsigned long);
321 }
322}
323#endif
324
325#if ULONG_MAX == ULLONG_MAX
326# define print_long_long print_long
327#else
328static void
329print_long_long(size_t n_bytes, const char *block, const char *fmt_string)
330{
331 n_bytes /= sizeof(ulonglong_t);
332 while (n_bytes--) {
333 ulonglong_t tmp = *(ulonglong_t *) block;
334 printf(fmt_string, tmp);
335 block += sizeof(ulonglong_t);
336 }
337}
338#endif
339
340static void
341print_float(size_t n_bytes, const char *block, const char *fmt_string)
342{
343 n_bytes /= sizeof(float);
344 while (n_bytes--) {
345 float tmp = *(float *) block;
346 printf(fmt_string, tmp);
347 block += sizeof(float);
348 }
349}
350
351static void
352print_double(size_t n_bytes, const char *block, const char *fmt_string)
353{
354 n_bytes /= sizeof(double);
355 while (n_bytes--) {
356 double tmp = *(double *) block;
357 printf(fmt_string, tmp);
358 block += sizeof(double);
359 }
360}
361
362static void
363print_long_double(size_t n_bytes, const char *block, const char *fmt_string)
364{
365 n_bytes /= sizeof(longdouble_t);
366 while (n_bytes--) {
367 longdouble_t tmp = *(longdouble_t *) block;
368 printf(fmt_string, tmp);
369 block += sizeof(longdouble_t);
370 }
371}
372
373/* print_[named]_ascii are optimized for speed.
374 * Remember, someday you may want to pump gigabytes through this thing.
375 * Saving a dozen of .text bytes here is counter-productive */
376
377static void
378print_named_ascii(size_t n_bytes, const char *block,
379 const char *unused_fmt_string UNUSED_PARAM)
380{
381 /* Names for some non-printing characters. */
382 static const char charname[33][3] ALIGN1 = {
383 "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
384 " bs", " ht", " nl", " vt", " ff", " cr", " so", " si",
385 "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
386 "can", " em", "sub", "esc", " fs", " gs", " rs", " us",
387 " sp"
388 };
389 // buf[N] pos: 01234 56789
390 char buf[12] = " x\0 0xx\0";
391 // actually " x\0 xxx\0", but want to share string with print_ascii.
392 // [12] because we take three 32bit stack slots anyway, and
393 // gcc is too dumb to initialize with constant stores,
394 // it copies initializer from rodata. Oh well.
395
396 while (n_bytes--) {
397 unsigned masked_c = *(unsigned char *) block++;
398
399 masked_c &= 0x7f;
400 if (masked_c == 0x7f) {
401 fputs(" del", stdout);
402 continue;
403 }
404 if (masked_c > ' ') {
405 buf[3] = masked_c;
406 fputs(buf, stdout);
407 continue;
408 }
409 /* Why? Because printf(" %3.3s") is much slower... */
410 buf[6] = charname[masked_c][0];
411 buf[7] = charname[masked_c][1];
412 buf[8] = charname[masked_c][2];
413 fputs(buf+5, stdout);
414 }
415}
416
417static void
418print_ascii(size_t n_bytes, const char *block,
419 const char *unused_fmt_string UNUSED_PARAM)
420{
421 // buf[N] pos: 01234 56789
422 char buf[12] = " x\0 0xx\0";
423
424 while (n_bytes--) {
425 const char *s;
426 unsigned c = *(unsigned char *) block++;
427
428 if (ISPRINT(c)) {
429 buf[3] = c;
430 fputs(buf, stdout);
431 continue;
432 }
433 switch (c) {
434 case '\0':
435 s = " \\0";
436 break;
437 case '\007':
438 s = " \\a";
439 break;
440 case '\b':
441 s = " \\b";
442 break;
443 case '\f':
444 s = " \\f";
445 break;
446 case '\n':
447 s = " \\n";
448 break;
449 case '\r':
450 s = " \\r";
451 break;
452 case '\t':
453 s = " \\t";
454 break;
455 case '\v':
456 s = " \\v";
457 break;
458 case '\x7f':
459 s = " 177";
460 break;
461 default: /* c is never larger than 040 */
462 buf[7] = (c >> 3) + '0';
463 buf[8] = (c & 7) + '0';
464 s = buf + 5;
465 }
466 fputs(s, stdout);
467 }
468}
469
470/* Given a list of one or more input filenames FILE_LIST, set the global
471 file pointer IN_STREAM and the global string INPUT_FILENAME to the
472 first one that can be successfully opened. Modify FILE_LIST to
473 reference the next filename in the list. A file name of "-" is
474 interpreted as standard input. If any file open fails, give an error
475 message and return nonzero. */
476
477static void
478open_next_file(void)
479{
480 while (1) {
481 if (!*file_list)
482 return;
483 in_stream = fopen_or_warn_stdin(*file_list++);
484 if (in_stream) {
485 break;
486 }
487 exit_code = 1;
488 }
489
490 if ((option_mask32 & (OPT_N|OPT_S)) == OPT_N)
491 setbuf(in_stream, NULL);
492}
493
494/* Test whether there have been errors on in_stream, and close it if
495 it is not standard input. Return nonzero if there has been an error
496 on in_stream or stdout; return zero otherwise. This function will
497 report more than one error only if both a read and a write error
498 have occurred. IN_ERRNO, if nonzero, is the error number
499 corresponding to the most recent action for IN_STREAM. */
500
501static void
502check_and_close(void)
503{
504 if (in_stream) {
505 if (ferror(in_stream)) {
506 bb_error_msg("%s: read error", (in_stream == stdin)
507 ? bb_msg_standard_input
508 : file_list[-1]
509 );
510 exit_code = 1;
511 }
512 fclose_if_not_stdin(in_stream);
513 in_stream = NULL;
514 }
515
516 if (ferror(stdout)) {
517 bb_error_msg_and_die(bb_msg_write_error);
518 }
519}
520
521/* If S points to a single valid modern od format string, put
522 a description of that format in *TSPEC, return pointer to
523 character following the just-decoded format.
524 For example, if S were "d4afL", we will return a rtp to "afL"
525 and *TSPEC would be
526 {
527 fmt = SIGNED_DECIMAL;
528 size = INT or LONG; (whichever integral_type_size[4] resolves to)
529 print_function = print_int; (assuming size == INT)
530 fmt_string = "%011d%c";
531 }
532 S_ORIG is solely for reporting errors. It should be the full format
533 string argument. */
534
535static NOINLINE const char *
536decode_one_format(const char *s_orig, const char *s, struct tspec *tspec)
537{
538 enum size_spec size_spec;
539 unsigned size;
540 enum output_format fmt;
541 const char *p;
542 char *end;
543 char *fmt_string = NULL;
544 void (*print_function) (size_t, const char *, const char *);
545 unsigned c;
546 unsigned field_width = 0;
547 int pos;
548
549 switch (*s) {
550 case 'd':
551 case 'o':
552 case 'u':
553 case 'x': {
554 static const char CSIL[] ALIGN1 = "CSIL";
555
556 c = *s++;
557 p = strchr(CSIL, *s);
558 /* if *s == NUL, p != NULL! Testcase: "od -tx" */
559 if (!p || *p == '\0') {
560 size = sizeof(int);
561 if (isdigit(s[0])) {
562 size = bb_strtou(s, &end, 0);
563 if (errno == ERANGE
564 || MAX_INTEGRAL_TYPE_SIZE < size
565 || integral_type_size[size] == NO_SIZE
566 ) {
567 bb_error_msg_and_die("invalid type string '%s'; "
568 "%u-byte %s type is not supported",
569 s_orig, size, "integral");
570 }
571 s = end;
572 }
573 } else {
574 static const uint8_t CSIL_sizeof[4] = {
575 sizeof(char),
576 sizeof(short),
577 sizeof(int),
578 sizeof(long),
579 };
580 size = CSIL_sizeof[p - CSIL];
581 s++; /* skip C/S/I/L */
582 }
583
584#define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \
585 ((Spec) == LONG_LONG ? (Max_format) \
586 : ((Spec) == LONG ? (Long_format) : (Min_format)))
587
588#define FMT_BYTES_ALLOCATED 9
589 size_spec = integral_type_size[size];
590
591 {
592 static const char doux[] ALIGN1 = "doux";
593 static const char doux_fmt_letter[][4] = {
594 "lld", "llo", "llu", "llx"
595 };
596 static const enum output_format doux_fmt[] = {
597 SIGNED_DECIMAL,
598 OCTAL,
599 UNSIGNED_DECIMAL,
600 HEXADECIMAL,
601 };
602 static const uint8_t *const doux_bytes_to_XXX[] = {
603 bytes_to_signed_dec_digits,
604 bytes_to_oct_digits,
605 bytes_to_unsigned_dec_digits,
606 bytes_to_hex_digits,
607 };
608 static const char doux_fmtstring[][sizeof(" %%0%u%s")] = {
609 " %%%u%s",
610 " %%0%u%s",
611 " %%%u%s",
612 " %%0%u%s",
613 };
614
615 pos = strchr(doux, c) - doux;
616 fmt = doux_fmt[pos];
617 field_width = doux_bytes_to_XXX[pos][size];
618 p = doux_fmt_letter[pos] + 2;
619 if (size_spec == LONG) p--;
620 if (size_spec == LONG_LONG) p -= 2;
621 fmt_string = xasprintf(doux_fmtstring[pos], field_width, p);
622 }
623
624 switch (size_spec) {
625 case CHAR:
626 print_function = (fmt == SIGNED_DECIMAL
627 ? print_s_char
628 : print_char);
629 break;
630 case SHORT:
631 print_function = (fmt == SIGNED_DECIMAL
632 ? print_s_short
633 : print_short);
634 break;
635 case INT:
636 print_function = print_int;
637 break;
638 case LONG:
639 print_function = print_long;
640 break;
641 default: /* case LONG_LONG: */
642 print_function = print_long_long;
643 break;
644 }
645 break;
646 }
647
648 case 'f': {
649 static const char FDL[] ALIGN1 = "FDL";
650
651 fmt = FLOATING_POINT;
652 ++s;
653 p = strchr(FDL, *s);
654 if (!p) {
655 size = sizeof(double);
656 if (isdigit(s[0])) {
657 size = bb_strtou(s, &end, 0);
658 if (errno == ERANGE || size > MAX_FP_TYPE_SIZE
659 || fp_type_size[size] == NO_SIZE
660 ) {
661 bb_error_msg_and_die("invalid type string '%s'; "
662 "%u-byte %s type is not supported",
663 s_orig, size, "floating point");
664 }
665 s = end;
666 }
667 } else {
668 static const uint8_t FDL_sizeof[] = {
669 sizeof(float),
670 sizeof(double),
671 sizeof(longdouble_t),
672 };
673
674 size = FDL_sizeof[p - FDL];
675 }
676
677 size_spec = fp_type_size[size];
678
679 switch (size_spec) {
680 case FLOAT_SINGLE:
681 print_function = print_float;
682 field_width = FLT_DIG + 8;
683 /* Don't use %#e; not all systems support it. */
684 fmt_string = xasprintf(" %%%d.%de", field_width, FLT_DIG);
685 break;
686 case FLOAT_DOUBLE:
687 print_function = print_double;
688 field_width = DBL_DIG + 8;
689 fmt_string = xasprintf(" %%%d.%de", field_width, DBL_DIG);
690 break;
691 default: /* case FLOAT_LONG_DOUBLE: */
692 print_function = print_long_double;
693 field_width = LDBL_DIG + 8;
694 fmt_string = xasprintf(" %%%d.%dLe", field_width, LDBL_DIG);
695 break;
696 }
697 break;
698 }
699
700 case 'a':
701 ++s;
702 fmt = NAMED_CHARACTER;
703 size_spec = CHAR;
704 print_function = print_named_ascii;
705 field_width = 3;
706 break;
707 case 'c':
708 ++s;
709 fmt = CHARACTER;
710 size_spec = CHAR;
711 print_function = print_ascii;
712 field_width = 3;
713 break;
714 default:
715 bb_error_msg_and_die("invalid character '%c' "
716 "in type string '%s'", *s, s_orig);
717 }
718
719 tspec->size = size_spec;
720 tspec->fmt = fmt;
721 tspec->print_function = print_function;
722 tspec->fmt_string = fmt_string;
723
724 tspec->field_width = field_width;
725 tspec->hexl_mode_trailer = (*s == 'z');
726 if (tspec->hexl_mode_trailer)
727 s++;
728
729 return s;
730}
731
732/* Decode the modern od format string S. Append the decoded
733 representation to the global array SPEC, reallocating SPEC if
734 necessary. */
735
736static void
737decode_format_string(const char *s)
738{
739 const char *s_orig = s;
740
741 while (*s != '\0') {
742 struct tspec tspec;
743 const char *next;
744
745 next = decode_one_format(s_orig, s, &tspec);
746
747 assert(s != next);
748 s = next;
749 spec = xrealloc_vector(spec, 4, n_specs);
750 memcpy(&spec[n_specs], &tspec, sizeof(spec[0]));
751 n_specs++;
752 }
753}
754
755/* Given a list of one or more input filenames FILE_LIST, set the global
756 file pointer IN_STREAM to position N_SKIP in the concatenation of
757 those files. If any file operation fails or if there are fewer than
758 N_SKIP bytes in the combined input, give an error message and return
759 nonzero. When possible, use seek rather than read operations to
760 advance IN_STREAM. */
761
762static void
763skip(off_t n_skip)
764{
765 if (n_skip == 0)
766 return;
767
768 while (in_stream) { /* !EOF */
769 struct stat file_stats;
770
771 /* First try seeking. For large offsets, this extra work is
772 worthwhile. If the offset is below some threshold it may be
773 more efficient to move the pointer by reading. There are two
774 issues when trying to seek:
775 - the file must be seekable.
776 - before seeking to the specified position, make sure
777 that the new position is in the current file.
778 Try to do that by getting file's size using fstat.
779 But that will work only for regular files. */
780
781 /* The st_size field is valid only for regular files
782 (and for symbolic links, which cannot occur here).
783 If the number of bytes left to skip is at least
784 as large as the size of the current file, we can
785 decrement n_skip and go on to the next file. */
786 if (fstat(fileno(in_stream), &file_stats) == 0
787 && S_ISREG(file_stats.st_mode) && file_stats.st_size > 0
788 ) {
789 if (file_stats.st_size < n_skip) {
790 n_skip -= file_stats.st_size;
791 /* take "check & close / open_next" route */
792 } else {
793 if (fseeko(in_stream, n_skip, SEEK_CUR) != 0)
794 exit_code = 1;
795 return;
796 }
797 } else {
798 /* If it's not a regular file with positive size,
799 position the file pointer by reading. */
800 char buf[1024];
801 size_t n_bytes_to_read = 1024;
802 size_t n_bytes_read;
803
804 while (n_skip > 0) {
805 if (n_skip < n_bytes_to_read)
806 n_bytes_to_read = n_skip;
807 n_bytes_read = fread(buf, 1, n_bytes_to_read, in_stream);
808 n_skip -= n_bytes_read;
809 if (n_bytes_read != n_bytes_to_read)
810 break; /* EOF on this file or error */
811 }
812 }
813 if (n_skip == 0)
814 return;
815
816 check_and_close();
817 open_next_file();
818 }
819
820 if (n_skip)
821 bb_error_msg_and_die("can't skip past end of combined input");
822}
823
824
825typedef void FN_format_address(off_t address, char c);
826
827static void
828format_address_none(off_t address UNUSED_PARAM, char c UNUSED_PARAM)
829{
830}
831
832static char address_fmt[] ALIGN1 = "%0n"OFF_FMT"xc";
833/* Corresponds to 'x' above */
834#define address_base_char address_fmt[sizeof(address_fmt)-3]
835/* Corresponds to 'n' above */
836#define address_pad_len_char address_fmt[2]
837
838static void
839format_address_std(off_t address, char c)
840{
841 /* Corresponds to 'c' */
842 address_fmt[sizeof(address_fmt)-2] = c;
843 printf(address_fmt, address);
844}
845
846#if ENABLE_LONG_OPTS
847/* only used with --traditional */
848static void
849format_address_paren(off_t address, char c)
850{
851 putchar('(');
852 format_address_std(address, ')');
853 if (c) putchar(c);
854}
855
856static void
857format_address_label(off_t address, char c)
858{
859 format_address_std(address, ' ');
860 format_address_paren(address + pseudo_offset, c);
861}
862#endif
863
864static void
865dump_hexl_mode_trailer(size_t n_bytes, const char *block)
866{
867 fputs(" >", stdout);
868 while (n_bytes--) {
869 unsigned c = *(unsigned char *) block++;
870 c = (ISPRINT(c) ? c : '.');
871 putchar(c);
872 }
873 putchar('<');
874}
875
876/* Write N_BYTES bytes from CURR_BLOCK to standard output once for each
877 of the N_SPEC format specs. CURRENT_OFFSET is the byte address of
878 CURR_BLOCK in the concatenation of input files, and it is printed
879 (optionally) only before the output line associated with the first
880 format spec. When duplicate blocks are being abbreviated, the output
881 for a sequence of identical input blocks is the output for the first
882 block followed by an asterisk alone on a line. It is valid to compare
883 the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK.
884 That condition may be false only for the last input block -- and then
885 only when it has not been padded to length BYTES_PER_BLOCK. */
886
887static void
888write_block(off_t current_offset, size_t n_bytes,
889 const char *prev_block, const char *curr_block)
890{
891 static char first = 1;
892 static char prev_pair_equal = 0;
893 size_t i;
894
895 if (!(option_mask32 & OPT_v)
896 && !first
897 && n_bytes == bytes_per_block
898 && memcmp(prev_block, curr_block, bytes_per_block) == 0
899 ) {
900 if (prev_pair_equal) {
901 /* The two preceding blocks were equal, and the current
902 block is the same as the last one, so print nothing. */
903 } else {
904 puts("*");
905 prev_pair_equal = 1;
906 }
907 } else {
908 first = 0;
909 prev_pair_equal = 0;
910 for (i = 0; i < n_specs; i++) {
911 if (i == 0)
912 format_address(current_offset, '\0');
913 else
914 printf("%*s", address_pad_len_char - '0', "");
915 (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string);
916 if (spec[i].hexl_mode_trailer) {
917 /* space-pad out to full line width, then dump the trailer */
918 unsigned datum_width = width_bytes[spec[i].size];
919 unsigned blank_fields = (bytes_per_block - n_bytes) / datum_width;
920 unsigned field_width = spec[i].field_width + 1;
921 printf("%*s", blank_fields * field_width, "");
922 dump_hexl_mode_trailer(n_bytes, curr_block);
923 }
924 putchar('\n');
925 }
926 }
927}
928
929static void
930read_block(size_t n, char *block, size_t *n_bytes_in_buffer)
931{
932 assert(0 < n && n <= bytes_per_block);
933
934 *n_bytes_in_buffer = 0;
935
936 if (n == 0)
937 return;
938
939 while (in_stream != NULL) { /* EOF. */
940 size_t n_needed;
941 size_t n_read;
942
943 n_needed = n - *n_bytes_in_buffer;
944 n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, in_stream);
945 *n_bytes_in_buffer += n_read;
946 if (n_read == n_needed)
947 break;
948 /* error check is done in check_and_close */
949 check_and_close();
950 open_next_file();
951 }
952}
953
954/* Return the least common multiple of the sizes associated
955 with the format specs. */
956
957static int
958get_lcm(void)
959{
960 size_t i;
961 int l_c_m = 1;
962
963 for (i = 0; i < n_specs; i++)
964 l_c_m = lcm(l_c_m, width_bytes[(int) spec[i].size]);
965 return l_c_m;
966}
967
968/* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
969 formatted block to standard output, and repeat until the specified
970 maximum number of bytes has been read or until all input has been
971 processed. If the last block read is smaller than BYTES_PER_BLOCK
972 and its size is not a multiple of the size associated with a format
973 spec, extend the input block with zero bytes until its length is a
974 multiple of all format spec sizes. Write the final block. Finally,
975 write on a line by itself the offset of the byte after the last byte
976 read. */
977
978static void
979dump(off_t current_offset, off_t end_offset)
980{
981 char *block[2];
982 int idx;
983 size_t n_bytes_read;
984
985 block[0] = xmalloc(2 * bytes_per_block);
986 block[1] = block[0] + bytes_per_block;
987
988 idx = 0;
989 if (option_mask32 & OPT_N) {
990 while (1) {
991 size_t n_needed;
992 if (current_offset >= end_offset) {
993 n_bytes_read = 0;
994 break;
995 }
996 n_needed = MIN(end_offset - current_offset, (off_t) bytes_per_block);
997 read_block(n_needed, block[idx], &n_bytes_read);
998 if (n_bytes_read < bytes_per_block)
999 break;
1000 assert(n_bytes_read == bytes_per_block);
1001 write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]);
1002 current_offset += n_bytes_read;
1003 idx ^= 1;
1004 }
1005 } else {
1006 while (1) {
1007 read_block(bytes_per_block, block[idx], &n_bytes_read);
1008 if (n_bytes_read < bytes_per_block)
1009 break;
1010 assert(n_bytes_read == bytes_per_block);
1011 write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]);
1012 current_offset += n_bytes_read;
1013 idx ^= 1;
1014 }
1015 }
1016
1017 if (n_bytes_read > 0) {
1018 int l_c_m;
1019 size_t bytes_to_write;
1020
1021 l_c_m = get_lcm();
1022
1023 /* Make bytes_to_write the smallest multiple of l_c_m that
1024 is at least as large as n_bytes_read. */
1025 bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m);
1026
1027 memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
1028 write_block(current_offset, bytes_to_write,
1029 block[idx ^ 1], block[idx]);
1030 current_offset += n_bytes_read;
1031 }
1032
1033 format_address(current_offset, '\n');
1034
1035 if ((option_mask32 & OPT_N) && current_offset >= end_offset)
1036 check_and_close();
1037
1038 free(block[0]);
1039}
1040
1041/* Read N bytes into BLOCK from the concatenation of the input files
1042 named in the global array FILE_LIST. On the first call to this
1043 function, the global variable IN_STREAM is expected to be an open
1044 stream associated with the input file INPUT_FILENAME. If all N
1045 bytes cannot be read from IN_STREAM, close IN_STREAM and update
1046 the global variables IN_STREAM and INPUT_FILENAME. Then try to
1047 read the remaining bytes from the newly opened file. Repeat if
1048 necessary until EOF is reached for the last file in FILE_LIST.
1049 On subsequent calls, don't modify BLOCK and return zero. Set
1050 *N_BYTES_IN_BUFFER to the number of bytes read. If an error occurs,
1051 it will be detected through ferror when the stream is about to be
1052 closed. If there is an error, give a message but continue reading
1053 as usual and return nonzero. Otherwise return zero. */
1054
1055/* STRINGS mode. Find each "string constant" in the input.
1056 A string constant is a run of at least 'string_min' ASCII
1057 graphic (or formatting) characters terminated by a null.
1058 Based on a function written by Richard Stallman for a
1059 traditional version of od. */
1060
1061static void
1062dump_strings(off_t address, off_t end_offset)
1063{
1064 unsigned bufsize = MAX(100, string_min);
1065 unsigned char *buf = xmalloc(bufsize);
1066
1067 while (1) {
1068 size_t i;
1069 int c;
1070
1071 /* See if the next 'string_min' chars are all printing chars. */
1072 tryline:
1073 if ((option_mask32 & OPT_N) && (end_offset - string_min <= address))
1074 break;
1075 i = 0;
1076 while (!(option_mask32 & OPT_N) || address < end_offset) {
1077 if (i == bufsize) {
1078 bufsize += bufsize/8;
1079 buf = xrealloc(buf, bufsize);
1080 }
1081
1082 while (in_stream) { /* !EOF */
1083 c = fgetc(in_stream);
1084 if (c != EOF)
1085 goto got_char;
1086 check_and_close();
1087 open_next_file();
1088 }
1089 /* EOF */
1090 goto ret;
1091 got_char:
1092 address++;
1093 if (!c)
1094 break;
1095 if (!ISPRINT(c))
1096 goto tryline; /* It isn't; give up on this string. */
1097 buf[i++] = c; /* String continues; store it all. */
1098 }
1099
1100 if (i < string_min) /* Too short! */
1101 goto tryline;
1102
1103 /* If we get here, the string is all printable and NUL-terminated */
1104 buf[i] = 0;
1105 format_address(address - i - 1, ' ');
1106
1107 for (i = 0; (c = buf[i]); i++) {
1108 switch (c) {
1109 case '\007': fputs("\\a", stdout); break;
1110 case '\b': fputs("\\b", stdout); break;
1111 case '\f': fputs("\\f", stdout); break;
1112 case '\n': fputs("\\n", stdout); break;
1113 case '\r': fputs("\\r", stdout); break;
1114 case '\t': fputs("\\t", stdout); break;
1115 case '\v': fputs("\\v", stdout); break;
1116 default: putchar(c);
1117 }
1118 }
1119 putchar('\n');
1120 }
1121
1122 /* We reach this point only if we search through
1123 (max_bytes_to_format - string_min) bytes before reaching EOF. */
1124 check_and_close();
1125 ret:
1126 free(buf);
1127}
1128
1129#if ENABLE_LONG_OPTS
1130/* If S is a valid traditional offset specification with an optional
1131 leading '+' return nonzero and set *OFFSET to the offset it denotes. */
1132
1133static int
1134parse_old_offset(const char *s, off_t *offset)
1135{
1136 static const struct suffix_mult Bb[] = {
1137 { "B", 1024 },
1138 { "b", 512 },
1139 { "", 0 }
1140 };
1141 char *p;
1142 int radix;
1143
1144 /* Skip over any leading '+'. */
1145 if (s[0] == '+') ++s;
1146 if (!isdigit(s[0])) return 0; /* not a number */
1147
1148 /* Determine the radix we'll use to interpret S. If there is a '.',
1149 * it's decimal, otherwise, if the string begins with '0X'or '0x',
1150 * it's hexadecimal, else octal. */
1151 p = strchr(s, '.');
1152 radix = 8;
1153 if (p) {
1154 p[0] = '\0'; /* cheating */
1155 radix = 10;
1156 } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
1157 radix = 16;
1158
1159 *offset = xstrtooff_sfx(s, radix, Bb);
1160 if (p) p[0] = '.';
1161
1162 return (*offset >= 0);
1163}
1164#endif
1165
1166int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1167int od_main(int argc UNUSED_PARAM, char **argv)
1168{
1169 static const struct suffix_mult bkm[] = {
1170 { "b", 512 },
1171 { "k", 1024 },
1172 { "m", 1024*1024 },
1173 { "", 0 }
1174 };
1175#if ENABLE_LONG_OPTS
1176 static const char od_longopts[] ALIGN1 =
1177 "skip-bytes\0" Required_argument "j"
1178 "address-radix\0" Required_argument "A"
1179 "read-bytes\0" Required_argument "N"
1180 "format\0" Required_argument "t"
1181 "output-duplicates\0" No_argument "v"
1182 /* Yes, it's true: -S NUM, but --strings[=NUM]!
1183 * that is, NUM is mandatory for -S but optional for --strings!
1184 */
1185 "strings\0" Optional_argument "S"
1186 "width\0" Optional_argument "w"
1187 "traditional\0" No_argument "\xff"
1188 ;
1189#endif
1190 const char *str_A, *str_N, *str_j, *str_S = "3";
1191 llist_t *lst_t = NULL;
1192 unsigned opt;
1193 int l_c_m;
1194 /* The number of input bytes to skip before formatting and writing. */
1195 off_t n_bytes_to_skip = 0;
1196 /* The offset of the first byte after the last byte to be formatted. */
1197 off_t end_offset = 0;
1198 /* The maximum number of bytes that will be formatted. */
1199 off_t max_bytes_to_format = 0;
1200
1201 spec = NULL;
1202 format_address = format_address_std;
1203 address_base_char = 'o';
1204 address_pad_len_char = '7';
1205
1206 /* Parse command line */
1207 opt_complementary = "w+:t::"; /* -w N, -t is a list */
1208#if ENABLE_LONG_OPTS
1209 applet_long_options = od_longopts;
1210#endif
1211 opt = OD_GETOPT32();
1212 argv += optind;
1213 if (opt & OPT_A) {
1214 static const char doxn[] ALIGN1 = "doxn";
1215 static const char doxn_address_base_char[] ALIGN1 = {
1216 'u', 'o', 'x', /* '?' fourth one is not important */
1217 };
1218 static const uint8_t doxn_address_pad_len_char[] ALIGN1 = {
1219 '7', '7', '6', /* '?' */
1220 };
1221 char *p;
1222 int pos;
1223 p = strchr(doxn, str_A[0]);
1224 if (!p)
1225 bb_error_msg_and_die("bad output address radix "
1226 "'%c' (must be [doxn])", str_A[0]);
1227 pos = p - doxn;
1228 if (pos == 3) format_address = format_address_none;
1229 address_base_char = doxn_address_base_char[pos];
1230 address_pad_len_char = doxn_address_pad_len_char[pos];
1231 }
1232 if (opt & OPT_N) {
1233 max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm);
1234 }
1235 if (opt & OPT_a) decode_format_string("a");
1236 if (opt & OPT_b) decode_format_string("oC");
1237 if (opt & OPT_c) decode_format_string("c");
1238 if (opt & OPT_d) decode_format_string("u2");
1239 if (opt & OPT_f) decode_format_string("fF");
1240 if (opt & OPT_h) decode_format_string("x2");
1241 if (opt & OPT_i) decode_format_string("d2");
1242 if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm);
1243 if (opt & OPT_l) decode_format_string("d4");
1244 if (opt & OPT_o) decode_format_string("o2");
1245 while (lst_t) {
1246 decode_format_string(llist_pop(&lst_t));
1247 }
1248 if (opt & OPT_x) decode_format_string("x2");
1249 if (opt & OPT_s) decode_format_string("d2");
1250 if (opt & OPT_S) {
1251 string_min = xstrtou_sfx(str_S, 0, bkm);
1252 }
1253
1254 // Bloat:
1255 //if ((option_mask32 & OPT_S) && n_specs > 0)
1256 // bb_error_msg_and_die("no type may be specified when dumping strings");
1257
1258 /* If the --traditional option is used, there may be from
1259 * 0 to 3 remaining command line arguments; handle each case
1260 * separately.
1261 * od [FILE] [[+]OFFSET[.][b] [[+]LABEL[.][b]]]
1262 * The offset and pseudo_start have the same syntax.
1263 *
1264 * FIXME: POSIX 1003.1-2001 with XSI requires support for the
1265 * traditional syntax even if --traditional is not given. */
1266
1267#if ENABLE_LONG_OPTS
1268 if (opt & OPT_traditional) {
1269 if (argv[0]) {
1270 off_t pseudo_start = -1;
1271 off_t o1, o2;
1272
1273 if (!argv[1]) { /* one arg */
1274 if (parse_old_offset(argv[0], &o1)) {
1275 /* od --traditional OFFSET */
1276 n_bytes_to_skip = o1;
1277 argv++;
1278 }
1279 /* od --traditional FILE */
1280 } else if (!argv[2]) { /* two args */
1281 if (parse_old_offset(argv[0], &o1)
1282 && parse_old_offset(argv[1], &o2)
1283 ) {
1284 /* od --traditional OFFSET LABEL */
1285 n_bytes_to_skip = o1;
1286 pseudo_start = o2;
1287 argv += 2;
1288 } else if (parse_old_offset(argv[1], &o2)) {
1289 /* od --traditional FILE OFFSET */
1290 n_bytes_to_skip = o2;
1291 argv[1] = NULL;
1292 } else {
1293 bb_error_msg_and_die("invalid second argument '%s'", argv[1]);
1294 }
1295 } else if (!argv[3]) { /* three args */
1296 if (parse_old_offset(argv[1], &o1)
1297 && parse_old_offset(argv[2], &o2)
1298 ) {
1299 /* od --traditional FILE OFFSET LABEL */
1300 n_bytes_to_skip = o1;
1301 pseudo_start = o2;
1302 argv[1] = NULL;
1303 } else {
1304 bb_error_msg_and_die("the last two arguments must be offsets");
1305 }
1306 } else { /* >3 args */
1307 bb_error_msg_and_die("too many arguments");
1308 }
1309
1310 if (pseudo_start >= 0) {
1311 if (format_address == format_address_none) {
1312 address_base_char = 'o';
1313 address_pad_len_char = '7';
1314 format_address = format_address_paren;
1315 } else {
1316 format_address = format_address_label;
1317 }
1318 pseudo_offset = pseudo_start - n_bytes_to_skip;
1319 }
1320 }
1321 /* else: od --traditional (without args) */
1322 }
1323#endif
1324
1325 if (option_mask32 & OPT_N) {
1326 end_offset = n_bytes_to_skip + max_bytes_to_format;
1327 if (end_offset < n_bytes_to_skip)
1328 bb_error_msg_and_die("SKIP + SIZE is too large");
1329 }
1330
1331 if (n_specs == 0) {
1332 decode_format_string("o2");
1333 /*n_specs = 1; - done by decode_format_string */
1334 }
1335
1336 /* If no files were listed on the command line,
1337 set the global pointer FILE_LIST so that it
1338 references the null-terminated list of one name: "-". */
1339 file_list = bb_argv_dash;
1340 if (argv[0]) {
1341 /* Set the global pointer FILE_LIST so that it
1342 references the first file-argument on the command-line. */
1343 file_list = (char const *const *) argv;
1344 }
1345
1346 /* Open the first input file */
1347 open_next_file();
1348 /* Skip over any unwanted header bytes */
1349 skip(n_bytes_to_skip);
1350 if (!in_stream)
1351 return EXIT_FAILURE;
1352
1353 /* Compute output block length */
1354 l_c_m = get_lcm();
1355
1356 if (opt & OPT_w) { /* -w: width */
1357 if (!bytes_per_block || bytes_per_block % l_c_m != 0) {
1358 bb_error_msg("warning: invalid width %u; using %d instead",
1359 (unsigned)bytes_per_block, l_c_m);
1360 bytes_per_block = l_c_m;
1361 }
1362 } else {
1363 bytes_per_block = l_c_m;
1364 if (l_c_m < DEFAULT_BYTES_PER_BLOCK)
1365 bytes_per_block *= DEFAULT_BYTES_PER_BLOCK / l_c_m;
1366 }
1367
1368#ifdef DEBUG
1369 for (i = 0; i < n_specs; i++) {
1370 printf("%d: fmt=\"%s\" width=%d\n",
1371 i, spec[i].fmt_string, width_bytes[spec[i].size]);
1372 }
1373#endif
1374
1375 if (option_mask32 & OPT_S)
1376 dump_strings(n_bytes_to_skip, end_offset);
1377 else
1378 dump(n_bytes_to_skip, end_offset);
1379
1380 if (fclose(stdin))
1381 bb_perror_msg_and_die(bb_msg_standard_input);
1382
1383 return exit_code;
1384}
Note: See TracBrowser for help on using the repository browser.