Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 532 - (hide 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 niro 532 /* 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     }