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