96a4186e2cf863a1bd414a5353ab08ac12a93d74
[platform/upstream/busybox.git] / coreutils / od_bloaty.c
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
38 enum {
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
75 typedef long double longdouble_t;
76 typedef unsigned long long ulonglong_t;
77 typedef 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
103 enum 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
116 enum 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.  */
128 struct 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
147 static 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
150 static 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
153 static 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
156 static 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.  */
160 static 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.  */
173 struct 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
177 static smallint exit_code;
178
179 static size_t string_min;
180
181 /* An array of specs describing how to format each input block.  */
182 static size_t n_specs;
183 static struct tspec *spec;
184
185 /* Function that accepts an address and an optional following char,
186    and prints the address and char to stdout.  */
187 static 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
191 static off_t pseudo_offset;
192 #else
193 enum { 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.  */
202 static 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.  */
205 static const char *const *file_list;
206
207 /* The input stream associated with the current file.  */
208 static FILE *in_stream;
209
210 #define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t)
211 static 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)
228 static 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
236 static unsigned
237 gcd(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.  */
249 static unsigned
250 lcm(unsigned u, unsigned v) {
251         unsigned t = gcd(u, v);
252         if (t == 0)
253                 return 0;
254         return u * v / t;
255 }
256
257 static void
258 print_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
267 static void
268 print_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
277 static void
278 print_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
288 static void
289 print_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
299 static void
300 print_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
313 static void
314 print_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
328 static void
329 print_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
340 static void
341 print_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
351 static void
352 print_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
362 static void
363 print_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
377 static void
378 print_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
417 static void
418 print_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
477 static void
478 open_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
501 static void
502 check_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(bb_msg_write_error);
518                 exit_code = 1;
519         }
520 }
521
522 /* If S points to a single valid modern od format string, put
523    a description of that format in *TSPEC, return pointer to
524    character following the just-decoded format.
525    For example, if S were "d4afL", we will return a rtp to "afL"
526    and *TSPEC would be
527         {
528                 fmt = SIGNED_DECIMAL;
529                 size = INT or LONG; (whichever integral_type_size[4] resolves to)
530                 print_function = print_int; (assuming size == INT)
531                 fmt_string = "%011d%c";
532         }
533    S_ORIG is solely for reporting errors.  It should be the full format
534    string argument. */
535
536 static NOINLINE const char *
537 decode_one_format(const char *s_orig, const char *s, struct tspec *tspec)
538 {
539         enum size_spec size_spec;
540         unsigned size;
541         enum output_format fmt;
542         const char *p;
543         char *end;
544         char *fmt_string = NULL;
545         void (*print_function) (size_t, const char *, const char *);
546         unsigned c;
547         unsigned field_width = 0;
548         int pos;
549
550
551         switch (*s) {
552         case 'd':
553         case 'o':
554         case 'u':
555         case 'x': {
556                 static const char CSIL[] ALIGN1 = "CSIL";
557
558                 c = *s++;
559                 p = strchr(CSIL, *s);
560                 /* if *s == NUL, p != NULL! Testcase: "od -tx" */
561                 if (!p || *p == '\0') {
562                         size = sizeof(int);
563                         if (isdigit(s[0])) {
564                                 size = bb_strtou(s, &end, 0);
565                                 if (errno == ERANGE
566                                  || MAX_INTEGRAL_TYPE_SIZE < size
567                                  || integral_type_size[size] == NO_SIZE
568                                 ) {
569                                         bb_error_msg_and_die("invalid type string '%s'; "
570                                                 "%u-byte %s type is not supported",
571                                                 s_orig, size, "integral");
572                                 }
573                                 s = end;
574                         }
575                 } else {
576                         static const uint8_t CSIL_sizeof[4] = {
577                                 sizeof(char),
578                                 sizeof(short),
579                                 sizeof(int),
580                                 sizeof(long),
581                         };
582                         size = CSIL_sizeof[p - CSIL];
583                         s++; /* skip C/S/I/L */
584                 }
585
586 #define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \
587         ((Spec) == LONG_LONG ? (Max_format) \
588         : ((Spec) == LONG ? (Long_format) : (Min_format)))
589
590 #define FMT_BYTES_ALLOCATED 9
591                 size_spec = integral_type_size[size];
592
593                 {
594                         static const char doux[] ALIGN1 = "doux";
595                         static const char doux_fmt_letter[][4] = {
596                                 "lld", "llo", "llu", "llx"
597                         };
598                         static const enum output_format doux_fmt[] = {
599                                 SIGNED_DECIMAL,
600                                 OCTAL,
601                                 UNSIGNED_DECIMAL,
602                                 HEXADECIMAL,
603                         };
604                         static const uint8_t *const doux_bytes_to_XXX[] = {
605                                 bytes_to_signed_dec_digits,
606                                 bytes_to_oct_digits,
607                                 bytes_to_unsigned_dec_digits,
608                                 bytes_to_hex_digits,
609                         };
610                         static const char doux_fmtstring[][sizeof(" %%0%u%s")] = {
611                                 " %%%u%s",
612                                 " %%0%u%s",
613                                 " %%%u%s",
614                                 " %%0%u%s",
615                         };
616
617                         pos = strchr(doux, c) - doux;
618                         fmt = doux_fmt[pos];
619                         field_width = doux_bytes_to_XXX[pos][size];
620                         p = doux_fmt_letter[pos] + 2;
621                         if (size_spec == LONG) p--;
622                         if (size_spec == LONG_LONG) p -= 2;
623                         fmt_string = xasprintf(doux_fmtstring[pos], field_width, p);
624                 }
625
626                 switch (size_spec) {
627                 case CHAR:
628                         print_function = (fmt == SIGNED_DECIMAL
629                                     ? print_s_char
630                                     : print_char);
631                         break;
632                 case SHORT:
633                         print_function = (fmt == SIGNED_DECIMAL
634                                     ? print_s_short
635                                     : print_short);
636                         break;
637                 case INT:
638                         print_function = print_int;
639                         break;
640                 case LONG:
641                         print_function = print_long;
642                         break;
643                 default: /* case LONG_LONG: */
644                         print_function = print_long_long;
645                         break;
646                 }
647                 break;
648         }
649
650         case 'f': {
651                 static const char FDL[] ALIGN1 = "FDL";
652
653                 fmt = FLOATING_POINT;
654                 ++s;
655                 p = strchr(FDL, *s);
656                 if (!p) {
657                         size = sizeof(double);
658                         if (isdigit(s[0])) {
659                                 size = bb_strtou(s, &end, 0);
660                                 if (errno == ERANGE || size > MAX_FP_TYPE_SIZE
661                                  || fp_type_size[size] == NO_SIZE
662                                 ) {
663                                         bb_error_msg_and_die("invalid type string '%s'; "
664                                                 "%u-byte %s type is not supported",
665                                                 s_orig, size, "floating point");
666                                 }
667                                 s = end;
668                         }
669                 } else {
670                         static const uint8_t FDL_sizeof[] = {
671                                 sizeof(float),
672                                 sizeof(double),
673                                 sizeof(longdouble_t),
674                         };
675
676                         size = FDL_sizeof[p - FDL];
677                 }
678
679                 size_spec = fp_type_size[size];
680
681                 switch (size_spec) {
682                 case FLOAT_SINGLE:
683                         print_function = print_float;
684                         field_width = FLT_DIG + 8;
685                         /* Don't use %#e; not all systems support it.  */
686                         fmt_string = xasprintf(" %%%d.%de", field_width, FLT_DIG);
687                         break;
688                 case FLOAT_DOUBLE:
689                         print_function = print_double;
690                         field_width = DBL_DIG + 8;
691                         fmt_string = xasprintf(" %%%d.%de", field_width, DBL_DIG);
692                         break;
693                 default: /* case FLOAT_LONG_DOUBLE: */
694                         print_function = print_long_double;
695                         field_width = LDBL_DIG + 8;
696                         fmt_string = xasprintf(" %%%d.%dLe", field_width, LDBL_DIG);
697                         break;
698                 }
699                 break;
700         }
701
702         case 'a':
703                 ++s;
704                 fmt = NAMED_CHARACTER;
705                 size_spec = CHAR;
706                 print_function = print_named_ascii;
707                 field_width = 3;
708                 break;
709         case 'c':
710                 ++s;
711                 fmt = CHARACTER;
712                 size_spec = CHAR;
713                 print_function = print_ascii;
714                 field_width = 3;
715                 break;
716         default:
717                 bb_error_msg_and_die("invalid character '%c' "
718                                 "in type string '%s'", *s, s_orig);
719         }
720
721         tspec->size = size_spec;
722         tspec->fmt = fmt;
723         tspec->print_function = print_function;
724         tspec->fmt_string = fmt_string;
725
726         tspec->field_width = field_width;
727         tspec->hexl_mode_trailer = (*s == 'z');
728         if (tspec->hexl_mode_trailer)
729                 s++;
730
731         return s;
732 }
733
734 /* Decode the modern od format string S.  Append the decoded
735    representation to the global array SPEC, reallocating SPEC if
736    necessary.  */
737
738 static void
739 decode_format_string(const char *s)
740 {
741         const char *s_orig = s;
742
743         while (*s != '\0') {
744                 struct tspec tspec;
745                 const char *next;
746
747                 next = decode_one_format(s_orig, s, &tspec);
748
749                 assert(s != next);
750                 s = next;
751                 spec = xrealloc_vector(spec, 4, n_specs);
752                 memcpy(&spec[n_specs], &tspec, sizeof(spec[0]));
753                 n_specs++;
754         }
755 }
756
757 /* Given a list of one or more input filenames FILE_LIST, set the global
758    file pointer IN_STREAM to position N_SKIP in the concatenation of
759    those files.  If any file operation fails or if there are fewer than
760    N_SKIP bytes in the combined input, give an error message and return
761    nonzero.  When possible, use seek rather than read operations to
762    advance IN_STREAM.  */
763
764 static void
765 skip(off_t n_skip)
766 {
767         if (n_skip == 0)
768                 return;
769
770         while (in_stream) { /* !EOF */
771                 struct stat file_stats;
772
773                 /* First try seeking.  For large offsets, this extra work is
774                    worthwhile.  If the offset is below some threshold it may be
775                    more efficient to move the pointer by reading.  There are two
776                    issues when trying to seek:
777                         - the file must be seekable.
778                         - before seeking to the specified position, make sure
779                           that the new position is in the current file.
780                           Try to do that by getting file's size using fstat.
781                           But that will work only for regular files.  */
782
783                         /* The st_size field is valid only for regular files
784                            (and for symbolic links, which cannot occur here).
785                            If the number of bytes left to skip is at least
786                            as large as the size of the current file, we can
787                            decrement n_skip and go on to the next file.  */
788                 if (fstat(fileno(in_stream), &file_stats) == 0
789                  && S_ISREG(file_stats.st_mode) && file_stats.st_size > 0
790                 ) {
791                         if (file_stats.st_size < n_skip) {
792                                 n_skip -= file_stats.st_size;
793                                 /* take "check & close / open_next" route */
794                         } else {
795                                 if (fseeko(in_stream, n_skip, SEEK_CUR) != 0)
796                                         exit_code = 1;
797                                 return;
798                         }
799                 } else {
800                         /* If it's not a regular file with positive size,
801                            position the file pointer by reading.  */
802                         char buf[1024];
803                         size_t n_bytes_to_read = 1024;
804                         size_t n_bytes_read;
805
806                         while (n_skip > 0) {
807                                 if (n_skip < n_bytes_to_read)
808                                         n_bytes_to_read = n_skip;
809                                 n_bytes_read = fread(buf, 1, n_bytes_to_read, in_stream);
810                                 n_skip -= n_bytes_read;
811                                 if (n_bytes_read != n_bytes_to_read)
812                                         break; /* EOF on this file or error */
813                         }
814                 }
815                 if (n_skip == 0)
816                         return;
817
818                 check_and_close();
819                 open_next_file();
820         }
821
822         if (n_skip)
823                 bb_error_msg_and_die("can't skip past end of combined input");
824 }
825
826
827 typedef void FN_format_address(off_t address, char c);
828
829 static void
830 format_address_none(off_t address UNUSED_PARAM, char c UNUSED_PARAM)
831 {
832 }
833
834 static char address_fmt[] ALIGN1 = "%0n"OFF_FMT"xc";
835 /* Corresponds to 'x' above */
836 #define address_base_char address_fmt[sizeof(address_fmt)-3]
837 /* Corresponds to 'n' above */
838 #define address_pad_len_char address_fmt[2]
839
840 static void
841 format_address_std(off_t address, char c)
842 {
843         /* Corresponds to 'c' */
844         address_fmt[sizeof(address_fmt)-2] = c;
845         printf(address_fmt, address);
846 }
847
848 #if ENABLE_LONG_OPTS
849 /* only used with --traditional */
850 static void
851 format_address_paren(off_t address, char c)
852 {
853         putchar('(');
854         format_address_std(address, ')');
855         if (c) putchar(c);
856 }
857
858 static void
859 format_address_label(off_t address, char c)
860 {
861         format_address_std(address, ' ');
862         format_address_paren(address + pseudo_offset, c);
863 }
864 #endif
865
866 static void
867 dump_hexl_mode_trailer(size_t n_bytes, const char *block)
868 {
869         fputs("  >", stdout);
870         while (n_bytes--) {
871                 unsigned c = *(unsigned char *) block++;
872                 c = (ISPRINT(c) ? c : '.');
873                 putchar(c);
874         }
875         putchar('<');
876 }
877
878 /* Write N_BYTES bytes from CURR_BLOCK to standard output once for each
879    of the N_SPEC format specs.  CURRENT_OFFSET is the byte address of
880    CURR_BLOCK in the concatenation of input files, and it is printed
881    (optionally) only before the output line associated with the first
882    format spec.  When duplicate blocks are being abbreviated, the output
883    for a sequence of identical input blocks is the output for the first
884    block followed by an asterisk alone on a line.  It is valid to compare
885    the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK.
886    That condition may be false only for the last input block -- and then
887    only when it has not been padded to length BYTES_PER_BLOCK.  */
888
889 static void
890 write_block(off_t current_offset, size_t n_bytes,
891                 const char *prev_block, const char *curr_block)
892 {
893         static char first = 1;
894         static char prev_pair_equal = 0;
895         size_t i;
896
897         if (!(option_mask32 & OPT_v)
898          && !first
899          && n_bytes == bytes_per_block
900          && memcmp(prev_block, curr_block, bytes_per_block) == 0
901         ) {
902                 if (prev_pair_equal) {
903                         /* The two preceding blocks were equal, and the current
904                            block is the same as the last one, so print nothing.  */
905                 } else {
906                         puts("*");
907                         prev_pair_equal = 1;
908                 }
909         } else {
910                 first = 0;
911                 prev_pair_equal = 0;
912                 for (i = 0; i < n_specs; i++) {
913                         if (i == 0)
914                                 format_address(current_offset, '\0');
915                         else
916                                 printf("%*s", address_pad_len_char - '0', "");
917                         (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string);
918                         if (spec[i].hexl_mode_trailer) {
919                                 /* space-pad out to full line width, then dump the trailer */
920                                 int datum_width = width_bytes[spec[i].size];
921                                 int blank_fields = (bytes_per_block - n_bytes) / datum_width;
922                                 int field_width = spec[i].field_width + 1;
923                                 printf("%*s", blank_fields * field_width, "");
924                                 dump_hexl_mode_trailer(n_bytes, curr_block);
925                         }
926                         putchar('\n');
927                 }
928         }
929 }
930
931 static void
932 read_block(size_t n, char *block, size_t *n_bytes_in_buffer)
933 {
934         assert(0 < n && n <= bytes_per_block);
935
936         *n_bytes_in_buffer = 0;
937
938         if (n == 0)
939                 return;
940
941         while (in_stream != NULL) { /* EOF.  */
942                 size_t n_needed;
943                 size_t n_read;
944
945                 n_needed = n - *n_bytes_in_buffer;
946                 n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, in_stream);
947                 *n_bytes_in_buffer += n_read;
948                 if (n_read == n_needed)
949                         break;
950                 /* error check is done in check_and_close */
951                 check_and_close();
952                 open_next_file();
953         }
954 }
955
956 /* Return the least common multiple of the sizes associated
957    with the format specs.  */
958
959 static int
960 get_lcm(void)
961 {
962         size_t i;
963         int l_c_m = 1;
964
965         for (i = 0; i < n_specs; i++)
966                 l_c_m = lcm(l_c_m, width_bytes[(int) spec[i].size]);
967         return l_c_m;
968 }
969
970 /* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
971    formatted block to standard output, and repeat until the specified
972    maximum number of bytes has been read or until all input has been
973    processed.  If the last block read is smaller than BYTES_PER_BLOCK
974    and its size is not a multiple of the size associated with a format
975    spec, extend the input block with zero bytes until its length is a
976    multiple of all format spec sizes.  Write the final block.  Finally,
977    write on a line by itself the offset of the byte after the last byte
978    read.  */
979
980 static void
981 dump(off_t current_offset, off_t end_offset)
982 {
983         char *block[2];
984         int idx;
985         size_t n_bytes_read;
986
987         block[0] = xmalloc(2*bytes_per_block);
988         block[1] = block[0] + bytes_per_block;
989
990         idx = 0;
991         if (option_mask32 & OPT_N) {
992                 while (1) {
993                         size_t n_needed;
994                         if (current_offset >= end_offset) {
995                                 n_bytes_read = 0;
996                                 break;
997                         }
998                         n_needed = MIN(end_offset - current_offset,
999                                 (off_t) bytes_per_block);
1000                         read_block(n_needed, block[idx], &n_bytes_read);
1001                         if (n_bytes_read < bytes_per_block)
1002                                 break;
1003                         assert(n_bytes_read == bytes_per_block);
1004                         write_block(current_offset, n_bytes_read,
1005                                block[!idx], block[idx]);
1006                         current_offset += n_bytes_read;
1007                         idx = !idx;
1008                 }
1009         } else {
1010                 while (1) {
1011                         read_block(bytes_per_block, block[idx], &n_bytes_read);
1012                         if (n_bytes_read < bytes_per_block)
1013                                 break;
1014                         assert(n_bytes_read == bytes_per_block);
1015                         write_block(current_offset, n_bytes_read,
1016                                block[!idx], block[idx]);
1017                         current_offset += n_bytes_read;
1018                         idx = !idx;
1019                 }
1020         }
1021
1022         if (n_bytes_read > 0) {
1023                 int l_c_m;
1024                 size_t bytes_to_write;
1025
1026                 l_c_m = get_lcm();
1027
1028                 /* Make bytes_to_write the smallest multiple of l_c_m that
1029                          is at least as large as n_bytes_read.  */
1030                 bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m);
1031
1032                 memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
1033                 write_block(current_offset, bytes_to_write,
1034                                    block[!idx], block[idx]);
1035                 current_offset += n_bytes_read;
1036         }
1037
1038         format_address(current_offset, '\n');
1039
1040         if ((option_mask32 & OPT_N) && current_offset >= end_offset)
1041                 check_and_close();
1042
1043         free(block[0]);
1044 }
1045
1046 /* Read a single byte into *C from the concatenation of the input files
1047    named in the global array FILE_LIST.  On the first call to this
1048    function, the global variable IN_STREAM is expected to be an open
1049    stream associated with the input file INPUT_FILENAME.  If IN_STREAM
1050    is at end-of-file, close it and update the global variables IN_STREAM
1051    and INPUT_FILENAME so they correspond to the next file in the list.
1052    Then try to read a byte from the newly opened file.  Repeat if
1053    necessary until EOF is reached for the last file in FILE_LIST, then
1054    set *C to EOF and return.  Subsequent calls do likewise.  */
1055
1056 static void
1057 read_char(int *c)
1058 {
1059         while (in_stream) { /* !EOF */
1060                 *c = fgetc(in_stream);
1061                 if (*c != EOF)
1062                         return;
1063                 check_and_close();
1064                 open_next_file();
1065         }
1066         *c = EOF;
1067 }
1068
1069 /* Read N bytes into BLOCK from the concatenation of the input files
1070    named in the global array FILE_LIST.  On the first call to this
1071    function, the global variable IN_STREAM is expected to be an open
1072    stream associated with the input file INPUT_FILENAME.  If all N
1073    bytes cannot be read from IN_STREAM, close IN_STREAM and update
1074    the global variables IN_STREAM and INPUT_FILENAME.  Then try to
1075    read the remaining bytes from the newly opened file.  Repeat if
1076    necessary until EOF is reached for the last file in FILE_LIST.
1077    On subsequent calls, don't modify BLOCK and return zero.  Set
1078    *N_BYTES_IN_BUFFER to the number of bytes read.  If an error occurs,
1079    it will be detected through ferror when the stream is about to be
1080    closed.  If there is an error, give a message but continue reading
1081    as usual and return nonzero.  Otherwise return zero.  */
1082
1083 /* STRINGS mode.  Find each "string constant" in the input.
1084    A string constant is a run of at least 'string_min' ASCII
1085    graphic (or formatting) characters terminated by a null.
1086    Based on a function written by Richard Stallman for a
1087    traditional version of od.  */
1088
1089 static void
1090 dump_strings(off_t address, off_t end_offset)
1091 {
1092         size_t bufsize = MAX(100, string_min);
1093         char *buf = xmalloc(bufsize);
1094
1095         while (1) {
1096                 size_t i;
1097                 int c;
1098
1099                 /* See if the next 'string_min' chars are all printing chars.  */
1100  tryline:
1101                 if ((option_mask32 & OPT_N) && (end_offset - string_min <= address))
1102                         break;
1103                 i = 0;
1104                 while (!(option_mask32 & OPT_N) || address < end_offset) {
1105                         if (i == bufsize) {
1106                                 bufsize += bufsize/8;
1107                                 buf = xrealloc(buf, bufsize);
1108                         }
1109                         read_char(&c);
1110                         if (c < 0) { /* EOF */
1111                                 free(buf);
1112                                 return;
1113                         }
1114                         address++;
1115                         if (!c)
1116                                 break;
1117                         if (!ISPRINT(c))
1118                                 goto tryline;   /* It isn't; give up on this string.  */
1119                         buf[i++] = c;           /* String continues; store it all.  */
1120                 }
1121
1122                 if (i < string_min)             /* Too short! */
1123                         goto tryline;
1124
1125                 /* If we get here, the string is all printable and NUL-terminated,
1126                  * so print it.  It is all in 'buf' and 'i' is its length.  */
1127                 buf[i] = 0;
1128                 format_address(address - i - 1, ' ');
1129
1130                 for (i = 0; (c = buf[i]); i++) {
1131                         switch (c) {
1132                         case '\007': fputs("\\a", stdout); break;
1133                         case '\b': fputs("\\b", stdout); break;
1134                         case '\f': fputs("\\f", stdout); break;
1135                         case '\n': fputs("\\n", stdout); break;
1136                         case '\r': fputs("\\r", stdout); break;
1137                         case '\t': fputs("\\t", stdout); break;
1138                         case '\v': fputs("\\v", stdout); break;
1139                         default: putchar(c);
1140                         }
1141                 }
1142                 putchar('\n');
1143         }
1144
1145         /* We reach this point only if we search through
1146            (max_bytes_to_format - string_min) bytes before reaching EOF.  */
1147         free(buf);
1148
1149         check_and_close();
1150 }
1151
1152 #if ENABLE_LONG_OPTS
1153 /* If S is a valid traditional offset specification with an optional
1154    leading '+' return nonzero and set *OFFSET to the offset it denotes.  */
1155
1156 static int
1157 parse_old_offset(const char *s, off_t *offset)
1158 {
1159         static const struct suffix_mult Bb[] = {
1160                 { "B", 1024 },
1161                 { "b", 512 },
1162                 { "", 0 }
1163         };
1164         char *p;
1165         int radix;
1166
1167         /* Skip over any leading '+'. */
1168         if (s[0] == '+') ++s;
1169         if (!isdigit(s[0])) return 0; /* not a number */
1170
1171         /* Determine the radix we'll use to interpret S.  If there is a '.',
1172          * it's decimal, otherwise, if the string begins with '0X'or '0x',
1173          * it's hexadecimal, else octal.  */
1174         p = strchr(s, '.');
1175         radix = 8;
1176         if (p) {
1177                 p[0] = '\0'; /* cheating */
1178                 radix = 10;
1179         } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
1180                 radix = 16;
1181
1182         *offset = xstrtooff_sfx(s, radix, Bb);
1183         if (p) p[0] = '.';
1184
1185         return (*offset >= 0);
1186 }
1187 #endif
1188
1189 int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1190 int od_main(int argc UNUSED_PARAM, char **argv)
1191 {
1192         static const struct suffix_mult bkm[] = {
1193                 { "b", 512 },
1194                 { "k", 1024 },
1195                 { "m", 1024*1024 },
1196                 { "", 0 }
1197         };
1198 #if ENABLE_LONG_OPTS
1199         static const char od_longopts[] ALIGN1 =
1200                 "skip-bytes\0"        Required_argument "j"
1201                 "address-radix\0"     Required_argument "A"
1202                 "read-bytes\0"        Required_argument "N"
1203                 "format\0"            Required_argument "t"
1204                 "output-duplicates\0" No_argument       "v"
1205                 "strings\0"           Optional_argument "S"
1206                 "width\0"             Optional_argument "w"
1207                 "traditional\0"       No_argument       "\xff"
1208                 ;
1209 #endif
1210         char *str_A, *str_N, *str_j, *str_S;
1211         llist_t *lst_t = NULL;
1212         unsigned opt;
1213         int l_c_m;
1214         /* The number of input bytes to skip before formatting and writing.  */
1215         off_t n_bytes_to_skip = 0;
1216         /* The offset of the first byte after the last byte to be formatted.  */
1217         off_t end_offset = 0;
1218         /* The maximum number of bytes that will be formatted.  */
1219         off_t max_bytes_to_format = 0;
1220
1221         spec = NULL;
1222         format_address = format_address_std;
1223         address_base_char = 'o';
1224         address_pad_len_char = '7';
1225
1226         /* Parse command line */
1227         opt_complementary = "w+:t::"; /* -w N, -t is a list */
1228 #if ENABLE_LONG_OPTS
1229         applet_long_options = od_longopts;
1230 #endif
1231         opt = OD_GETOPT32();
1232         argv += optind;
1233         if (opt & OPT_A) {
1234                 static const char doxn[] ALIGN1 = "doxn";
1235                 static const char doxn_address_base_char[] ALIGN1 = {
1236                         'u', 'o', 'x', /* '?' fourth one is not important */
1237                 };
1238                 static const uint8_t doxn_address_pad_len_char[] ALIGN1 = {
1239                         '7', '7', '6', /* '?' */
1240                 };
1241                 char *p;
1242                 int pos;
1243                 p = strchr(doxn, str_A[0]);
1244                 if (!p)
1245                         bb_error_msg_and_die("bad output address radix "
1246                                 "'%c' (must be [doxn])", str_A[0]);
1247                 pos = p - doxn;
1248                 if (pos == 3) format_address = format_address_none;
1249                 address_base_char = doxn_address_base_char[pos];
1250                 address_pad_len_char = doxn_address_pad_len_char[pos];
1251         }
1252         if (opt & OPT_N) {
1253                 max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm);
1254         }
1255         if (opt & OPT_a) decode_format_string("a");
1256         if (opt & OPT_b) decode_format_string("oC");
1257         if (opt & OPT_c) decode_format_string("c");
1258         if (opt & OPT_d) decode_format_string("u2");
1259         if (opt & OPT_f) decode_format_string("fF");
1260         if (opt & OPT_h) decode_format_string("x2");
1261         if (opt & OPT_i) decode_format_string("d2");
1262         if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm);
1263         if (opt & OPT_l) decode_format_string("d4");
1264         if (opt & OPT_o) decode_format_string("o2");
1265         while (lst_t) {
1266                 decode_format_string(llist_pop(&lst_t));
1267         }
1268         if (opt & OPT_x) decode_format_string("x2");
1269         if (opt & OPT_s) decode_format_string("d2");
1270         if (opt & OPT_S) {
1271                 string_min = xstrtou_sfx(str_S, 0, bkm);
1272         }
1273
1274         // Bloat:
1275         //if ((option_mask32 & OPT_S) && n_specs > 0)
1276         //      bb_error_msg_and_die("no type may be specified when dumping strings");
1277
1278         /* If the --traditional option is used, there may be from
1279          * 0 to 3 remaining command line arguments;  handle each case
1280          * separately.
1281          * od [FILE] [[+]OFFSET[.][b] [[+]LABEL[.][b]]]
1282          * The offset and pseudo_start have the same syntax.
1283          *
1284          * FIXME: POSIX 1003.1-2001 with XSI requires support for the
1285          * traditional syntax even if --traditional is not given.  */
1286
1287 #if ENABLE_LONG_OPTS
1288         if (opt & OPT_traditional) {
1289                 if (argv[0]) {
1290                         off_t pseudo_start = -1;
1291                         off_t o1, o2;
1292
1293                         if (!argv[1]) { /* one arg */
1294                                 if (parse_old_offset(argv[0], &o1)) {
1295                                         /* od --traditional OFFSET */
1296                                         n_bytes_to_skip = o1;
1297                                         argv++;
1298                                 }
1299                                 /* od --traditional FILE */
1300                         } else if (!argv[2]) { /* two args */
1301                                 if (parse_old_offset(argv[0], &o1)
1302                                  && parse_old_offset(argv[1], &o2)
1303                                 ) {
1304                                         /* od --traditional OFFSET LABEL */
1305                                         n_bytes_to_skip = o1;
1306                                         pseudo_start = o2;
1307                                         argv += 2;
1308                                 } else if (parse_old_offset(argv[1], &o2)) {
1309                                         /* od --traditional FILE OFFSET */
1310                                         n_bytes_to_skip = o2;
1311                                         argv[1] = NULL;
1312                                 } else {
1313                                         bb_error_msg_and_die("invalid second argument '%s'", argv[1]);
1314                                 }
1315                         } else if (!argv[3]) { /* three args */
1316                                 if (parse_old_offset(argv[1], &o1)
1317                                  && parse_old_offset(argv[2], &o2)
1318                                 ) {
1319                                         /* od --traditional FILE OFFSET LABEL */
1320                                         n_bytes_to_skip = o1;
1321                                         pseudo_start = o2;
1322                                         argv[1] = NULL;
1323                                 } else {
1324                                         bb_error_msg_and_die("the last two arguments must be offsets");
1325                                 }
1326                         } else { /* >3 args */
1327                                 bb_error_msg_and_die("too many arguments");
1328                         }
1329
1330                         if (pseudo_start >= 0) {
1331                                 if (format_address == format_address_none) {
1332                                         address_base_char = 'o';
1333                                         address_pad_len_char = '7';
1334                                         format_address = format_address_paren;
1335                                 } else {
1336                                         format_address = format_address_label;
1337                                 }
1338                                 pseudo_offset = pseudo_start - n_bytes_to_skip;
1339                         }
1340                 }
1341                 /* else: od --traditional (without args) */
1342         }
1343 #endif
1344
1345         if (option_mask32 & OPT_N) {
1346                 end_offset = n_bytes_to_skip + max_bytes_to_format;
1347                 if (end_offset < n_bytes_to_skip)
1348                         bb_error_msg_and_die("SKIP + SIZE is too large");
1349         }
1350
1351         if (n_specs == 0) {
1352                 decode_format_string("o2");
1353                 /*n_specs = 1; - done by decode_format_string */
1354         }
1355
1356         /* If no files were listed on the command line,
1357            set the global pointer FILE_LIST so that it
1358            references the null-terminated list of one name: "-".  */
1359         file_list = bb_argv_dash;
1360         if (argv[0]) {
1361                 /* Set the global pointer FILE_LIST so that it
1362                    references the first file-argument on the command-line.  */
1363                 file_list = (char const *const *) argv;
1364         }
1365
1366         /* Open the first input file */
1367         open_next_file();
1368         /* Skip over any unwanted header bytes */
1369         skip(n_bytes_to_skip);
1370         if (!in_stream)
1371                 return EXIT_FAILURE;
1372
1373         /* Compute output block length */
1374         l_c_m = get_lcm();
1375
1376         if (opt & OPT_w) { /* -w: width */
1377                 if (!bytes_per_block || bytes_per_block % l_c_m != 0) {
1378                         bb_error_msg("warning: invalid width %u; using %d instead",
1379                                         (unsigned)bytes_per_block, l_c_m);
1380                         bytes_per_block = l_c_m;
1381                 }
1382         } else {
1383                 bytes_per_block = l_c_m;
1384                 if (l_c_m < DEFAULT_BYTES_PER_BLOCK)
1385                         bytes_per_block *= DEFAULT_BYTES_PER_BLOCK / l_c_m;
1386         }
1387
1388 #ifdef DEBUG
1389         for (i = 0; i < n_specs; i++) {
1390                 printf("%d: fmt=\"%s\" width=%d\n",
1391                         i, spec[i].fmt_string, width_bytes[spec[i].size]);
1392         }
1393 #endif
1394
1395         if (option_mask32 & OPT_S)
1396                 dump_strings(n_bytes_to_skip, end_offset);
1397         else
1398                 dump(n_bytes_to_skip, end_offset);
1399
1400         if (fclose(stdin) == EOF)
1401                 bb_perror_msg_and_die(bb_msg_standard_input);
1402
1403         return exit_code;
1404 }