Magellan Linux

Contents of /trunk/mkinitrd-magellan/busybox/coreutils/od_bloaty.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 532 - (show annotations) (download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 38809 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd

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