Start 1.33.0 development cycle
[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() getopt32long(argv, \
65         "A:N:abcdfhij:lot:*vxsS:w:+:", od_longopts, \
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
215         char address_fmt[sizeof("%0n"OFF_FMT"xc")];
216 } FIX_ALIASING;
217 /* Corresponds to 'x' above */
218 #define address_base_char G.address_fmt[sizeof(G.address_fmt)-3]
219 /* Corresponds to 'n' above */
220 #define address_pad_len_char G.address_fmt[2]
221
222 #if !ENABLE_LONG_OPTS
223 enum { G_pseudo_offset = 0 };
224 #endif
225 #define G (*(struct globals*)bb_common_bufsiz1)
226 #define INIT_G() do { \
227         setup_common_bufsiz(); \
228         BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
229         G.bytes_per_block = 32; \
230         strcpy(G.address_fmt, "%0n"OFF_FMT"xc"); \
231 } while (0)
232
233
234 #define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t)
235 static const unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] ALIGN1 = {
236         [sizeof(char)] = CHAR,
237 #if USHRT_MAX != UCHAR_MAX
238         [sizeof(short)] = SHORT,
239 #endif
240 #if UINT_MAX != USHRT_MAX
241         [sizeof(int)] = INT,
242 #endif
243 #if ULONG_MAX != UINT_MAX
244         [sizeof(long)] = LONG,
245 #endif
246 #if ULLONG_MAX != ULONG_MAX
247         [sizeof(ulonglong_t)] = LONG_LONG,
248 #endif
249 };
250
251 #define MAX_FP_TYPE_SIZE sizeof(longdouble_t)
252 static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = {
253         /* gcc seems to allow repeated indexes. Last one wins */
254         [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE,
255         [sizeof(double)] = FLOAT_DOUBLE,
256         [sizeof(float)] = FLOAT_SINGLE
257 };
258
259
260 static unsigned
261 gcd(unsigned u, unsigned v)
262 {
263         unsigned t;
264         while (v != 0) {
265                 t = u % v;
266                 u = v;
267                 v = t;
268         }
269         return u;
270 }
271
272 /* Compute the least common multiple of U and V.  */
273 static unsigned
274 lcm(unsigned u, unsigned v) {
275         unsigned t = gcd(u, v);
276         if (t == 0)
277                 return 0;
278         return u * v / t;
279 }
280
281 static void
282 print_s_char(size_t n_bytes, const char *block, const char *fmt_string)
283 {
284         while (n_bytes--) {
285                 int tmp = *(signed char *) block;
286                 printf(fmt_string, tmp);
287                 block += sizeof(unsigned char);
288         }
289 }
290
291 static void
292 print_char(size_t n_bytes, const char *block, const char *fmt_string)
293 {
294         while (n_bytes--) {
295                 unsigned tmp = *(unsigned char *) block;
296                 printf(fmt_string, tmp);
297                 block += sizeof(unsigned char);
298         }
299 }
300
301 static void
302 print_s_short(size_t n_bytes, const char *block, const char *fmt_string)
303 {
304         n_bytes /= sizeof(signed short);
305         while (n_bytes--) {
306                 int tmp = *(signed short *) block;
307                 printf(fmt_string, tmp);
308                 block += sizeof(unsigned short);
309         }
310 }
311
312 static void
313 print_short(size_t n_bytes, const char *block, const char *fmt_string)
314 {
315         n_bytes /= sizeof(unsigned short);
316         while (n_bytes--) {
317                 unsigned tmp = *(unsigned short *) block;
318                 printf(fmt_string, tmp);
319                 block += sizeof(unsigned short);
320         }
321 }
322
323 static void
324 print_int(size_t n_bytes, const char *block, const char *fmt_string)
325 {
326         n_bytes /= sizeof(unsigned);
327         while (n_bytes--) {
328                 unsigned tmp = *(unsigned *) block;
329                 printf(fmt_string, tmp);
330                 block += sizeof(unsigned);
331         }
332 }
333
334 #if UINT_MAX == ULONG_MAX
335 # define print_long print_int
336 #else
337 static void
338 print_long(size_t n_bytes, const char *block, const char *fmt_string)
339 {
340         n_bytes /= sizeof(unsigned long);
341         while (n_bytes--) {
342                 unsigned long tmp = *(unsigned long *) block;
343                 printf(fmt_string, tmp);
344                 block += sizeof(unsigned long);
345         }
346 }
347 #endif
348
349 #if ULONG_MAX == ULLONG_MAX
350 # define print_long_long print_long
351 #else
352 static void
353 print_long_long(size_t n_bytes, const char *block, const char *fmt_string)
354 {
355         n_bytes /= sizeof(ulonglong_t);
356         while (n_bytes--) {
357                 ulonglong_t tmp = *(ulonglong_t *) block;
358                 printf(fmt_string, tmp);
359                 block += sizeof(ulonglong_t);
360         }
361 }
362 #endif
363
364 static void
365 print_float(size_t n_bytes, const char *block, const char *fmt_string)
366 {
367         n_bytes /= sizeof(float);
368         while (n_bytes--) {
369                 float tmp = *(float *) block;
370                 printf(fmt_string, tmp);
371                 block += sizeof(float);
372         }
373 }
374
375 static void
376 print_double(size_t n_bytes, const char *block, const char *fmt_string)
377 {
378         n_bytes /= sizeof(double);
379         while (n_bytes--) {
380                 double tmp = *(double *) block;
381                 printf(fmt_string, tmp);
382                 block += sizeof(double);
383         }
384 }
385
386 static void
387 print_long_double(size_t n_bytes, const char *block, const char *fmt_string)
388 {
389         n_bytes /= sizeof(longdouble_t);
390         while (n_bytes--) {
391                 longdouble_t tmp = *(longdouble_t *) block;
392                 printf(fmt_string, tmp);
393                 block += sizeof(longdouble_t);
394         }
395 }
396
397 /* print_[named]_ascii are optimized for speed.
398  * Remember, someday you may want to pump gigabytes through this thing.
399  * Saving a dozen of .text bytes here is counter-productive */
400
401 static void
402 print_named_ascii(size_t n_bytes, const char *block,
403                 const char *unused_fmt_string UNUSED_PARAM)
404 {
405         /* Names for some non-printing characters.  */
406         static const char charname[33][3] ALIGN1 = {
407                 "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
408                 " bs", " ht", " nl", " vt", " ff", " cr", " so", " si",
409                 "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
410                 "can", " em", "sub", "esc", " fs", " gs", " rs", " us",
411                 " sp"
412         };
413         // buf[N] pos:  01234 56789
414         char buf[12] = "   x\0 xxx\0";
415         // [12] because we take three 32bit stack slots anyway, and
416         // gcc is too dumb to initialize with constant stores,
417         // it copies initializer from rodata. Oh well.
418         // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65410
419
420         while (n_bytes--) {
421                 unsigned masked_c = *(unsigned char *) block++;
422
423                 masked_c &= 0x7f;
424                 if (masked_c == 0x7f) {
425                         fputs(" del", stdout);
426                         continue;
427                 }
428                 if (masked_c > ' ') {
429                         buf[3] = masked_c;
430                         fputs(buf, stdout);
431                         continue;
432                 }
433                 /* Why? Because printf(" %3.3s") is much slower... */
434                 buf[6] = charname[masked_c][0];
435                 buf[7] = charname[masked_c][1];
436                 buf[8] = charname[masked_c][2];
437                 fputs(buf+5, stdout);
438         }
439 }
440
441 static void
442 print_ascii(size_t n_bytes, const char *block,
443                 const char *unused_fmt_string UNUSED_PARAM)
444 {
445         // buf[N] pos:  01234 56789
446         char buf[12] = "   x\0 xxx\0";
447
448         while (n_bytes--) {
449                 const char *s;
450                 unsigned c = *(unsigned char *) block++;
451
452                 if (ISPRINT(c)) {
453                         buf[3] = c;
454                         fputs(buf, stdout);
455                         continue;
456                 }
457                 switch (c) {
458                 case '\0':
459                         s = "  \\0";
460                         break;
461                 case '\007':
462                         s = "  \\a";
463                         break;
464                 case '\b':
465                         s = "  \\b";
466                         break;
467                 case '\f':
468                         s = "  \\f";
469                         break;
470                 case '\n':
471                         s = "  \\n";
472                         break;
473                 case '\r':
474                         s = "  \\r";
475                         break;
476                 case '\t':
477                         s = "  \\t";
478                         break;
479                 case '\v':
480                         s = "  \\v";
481                         break;
482                 default:
483                         buf[6] = (c >> 6 & 3) + '0';
484                         buf[7] = (c >> 3 & 7) + '0';
485                         buf[8] = (c & 7) + '0';
486                         s = buf + 5;
487                 }
488                 fputs(s, stdout);
489         }
490 }
491
492 /* Given a list of one or more input filenames FILE_LIST, set the global
493    file pointer IN_STREAM and the global string INPUT_FILENAME to the
494    first one that can be successfully opened. Modify FILE_LIST to
495    reference the next filename in the list.  A file name of "-" is
496    interpreted as standard input.  If any file open fails, give an error
497    message and return nonzero.  */
498
499 static void
500 open_next_file(void)
501 {
502         while (1) {
503                 if (!*G.file_list)
504                         return;
505                 G.in_stream = fopen_or_warn_stdin(*G.file_list++);
506                 if (G.in_stream) {
507                         break;
508                 }
509                 G.exit_code = 1;
510         }
511
512         if ((option_mask32 & (OPT_N|OPT_S)) == OPT_N)
513                 setbuf(G.in_stream, NULL);
514 }
515
516 /* Test whether there have been errors on in_stream, and close it if
517    it is not standard input.  Return nonzero if there has been an error
518    on in_stream or stdout; return zero otherwise.  This function will
519    report more than one error only if both a read and a write error
520    have occurred.  IN_ERRNO, if nonzero, is the error number
521    corresponding to the most recent action for IN_STREAM.  */
522
523 static void
524 check_and_close(void)
525 {
526         if (G.in_stream) {
527                 if (ferror(G.in_stream))        {
528                         bb_error_msg("%s: read error", (G.in_stream == stdin)
529                                         ? bb_msg_standard_input
530                                         : G.file_list[-1]
531                         );
532                         G.exit_code = 1;
533                 }
534                 fclose_if_not_stdin(G.in_stream);
535                 G.in_stream = NULL;
536         }
537
538         if (ferror(stdout)) {
539                 bb_simple_error_msg_and_die(bb_msg_write_error);
540         }
541 }
542
543 /* If S points to a single valid modern od format string, put
544    a description of that format in *TSPEC, return pointer to
545    character following the just-decoded format.
546    For example, if S were "d4afL", we will return a rtp to "afL"
547    and *TSPEC would be
548         {
549                 fmt = SIGNED_DECIMAL;
550                 size = INT or LONG; (whichever integral_type_size[4] resolves to)
551                 print_function = print_int; (assuming size == INT)
552                 fmt_string = "%011d%c";
553         }
554    S_ORIG is solely for reporting errors.  It should be the full format
555    string argument. */
556
557 static NOINLINE const char *
558 decode_one_format(const char *s_orig, const char *s, struct tspec *tspec)
559 {
560         enum size_spec size_spec;
561         unsigned size;
562         enum output_format fmt;
563         const char *p;
564         char *end;
565         char *fmt_string = NULL;
566         void (*print_function) (size_t, const char *, const char *);
567         unsigned c;
568         unsigned field_width = 0;
569         int pos;
570
571         switch (*s) {
572         case 'd':
573         case 'o':
574         case 'u':
575         case 'x': {
576                 static const char CSIL[] ALIGN1 = "CSIL";
577
578                 c = *s++;
579                 p = strchr(CSIL, *s);
580                 /* if *s == NUL, p != NULL! Testcase: "od -tx" */
581                 if (!p || *p == '\0') {
582                         size = sizeof(int);
583                         if (isdigit(s[0])) {
584                                 size = bb_strtou(s, &end, 0);
585                                 if (errno == ERANGE
586                                  || MAX_INTEGRAL_TYPE_SIZE < size
587                                  || integral_type_size[size] == NO_SIZE
588                                 ) {
589                                         bb_error_msg_and_die("invalid type string '%s'; "
590                                                 "%u-byte %s type is not supported",
591                                                 s_orig, size, "integral");
592                                 }
593                                 s = end;
594                         }
595                 } else {
596                         static const uint8_t CSIL_sizeof[4] = {
597                                 sizeof(char),
598                                 sizeof(short),
599                                 sizeof(int),
600                                 sizeof(long),
601                         };
602                         size = CSIL_sizeof[p - CSIL];
603                         s++; /* skip C/S/I/L */
604                 }
605
606 #define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \
607         ((Spec) == LONG_LONG ? (Max_format) \
608         : ((Spec) == LONG ? (Long_format) : (Min_format)))
609
610 #define FMT_BYTES_ALLOCATED 9
611                 size_spec = integral_type_size[size];
612
613                 {
614                         static const char doux[] ALIGN1 = "doux";
615                         static const char doux_fmt_letter[][4] = {
616                                 "lld", "llo", "llu", "llx"
617                         };
618                         static const enum output_format doux_fmt[] = {
619                                 SIGNED_DECIMAL,
620                                 OCTAL,
621                                 UNSIGNED_DECIMAL,
622                                 HEXADECIMAL,
623                         };
624                         static const uint8_t *const doux_bytes_to_XXX[] = {
625                                 bytes_to_signed_dec_digits,
626                                 bytes_to_oct_digits,
627                                 bytes_to_unsigned_dec_digits,
628                                 bytes_to_hex_digits,
629                         };
630                         static const char doux_fmtstring[][sizeof(" %%0%u%s")] = {
631                                 " %%%u%s",
632                                 " %%0%u%s",
633                                 " %%%u%s",
634                                 " %%0%u%s",
635                         };
636
637                         pos = strchr(doux, c) - doux;
638                         fmt = doux_fmt[pos];
639                         field_width = doux_bytes_to_XXX[pos][size];
640                         p = doux_fmt_letter[pos] + 2;
641                         if (size_spec == LONG) p--;
642                         if (size_spec == LONG_LONG) p -= 2;
643                         fmt_string = xasprintf(doux_fmtstring[pos], field_width, p);
644                 }
645
646                 switch (size_spec) {
647                 case CHAR:
648                         print_function = (fmt == SIGNED_DECIMAL
649                                     ? print_s_char
650                                     : print_char);
651                         break;
652                 case SHORT:
653                         print_function = (fmt == SIGNED_DECIMAL
654                                     ? print_s_short
655                                     : print_short);
656                         break;
657                 case INT:
658                         print_function = print_int;
659                         break;
660                 case LONG:
661                         print_function = print_long;
662                         break;
663                 default: /* case LONG_LONG: */
664                         print_function = print_long_long;
665                         break;
666                 }
667                 break;
668         }
669
670         case 'f': {
671                 static const char FDL[] ALIGN1 = "FDL";
672
673                 fmt = FLOATING_POINT;
674                 ++s;
675                 p = strchr(FDL, *s);
676                 if (!p || *p == '\0') {
677                         size = sizeof(double);
678                         if (isdigit(s[0])) {
679                                 size = bb_strtou(s, &end, 0);
680                                 if (errno == ERANGE || size > MAX_FP_TYPE_SIZE
681                                  || fp_type_size[size] == NO_SIZE
682                                 ) {
683                                         bb_error_msg_and_die("invalid type string '%s'; "
684                                                 "%u-byte %s type is not supported",
685                                                 s_orig, size, "floating point");
686                                 }
687                                 s = end;
688                         }
689                 } else {
690                         static const uint8_t FDL_sizeof[] = {
691                                 sizeof(float),
692                                 sizeof(double),
693                                 sizeof(longdouble_t),
694                         };
695
696                         size = FDL_sizeof[p - FDL];
697                         s++; /* skip F/D/L */
698                 }
699
700                 size_spec = fp_type_size[size];
701
702                 switch (size_spec) {
703                 case FLOAT_SINGLE:
704                         print_function = print_float;
705                         field_width = FLT_DIG + 8;
706                         /* Don't use %#e; not all systems support it.  */
707                         fmt_string = xasprintf(" %%%d.%de", field_width, FLT_DIG);
708                         break;
709                 case FLOAT_DOUBLE:
710                         print_function = print_double;
711                         field_width = DBL_DIG + 8;
712                         fmt_string = xasprintf(" %%%d.%de", field_width, DBL_DIG);
713                         break;
714                 default: /* case FLOAT_LONG_DOUBLE: */
715                         print_function = print_long_double;
716                         field_width = LDBL_DIG + 8;
717                         fmt_string = xasprintf(" %%%d.%dLe", field_width, LDBL_DIG);
718                         break;
719                 }
720                 break;
721         }
722
723         case 'a':
724                 ++s;
725                 fmt = NAMED_CHARACTER;
726                 size_spec = CHAR;
727                 print_function = print_named_ascii;
728                 field_width = 3;
729                 break;
730         case 'c':
731                 ++s;
732                 fmt = CHARACTER;
733                 size_spec = CHAR;
734                 print_function = print_ascii;
735                 field_width = 3;
736                 break;
737         default:
738                 bb_error_msg_and_die("invalid character '%c' "
739                                 "in type string '%s'", *s, s_orig);
740         }
741
742         tspec->size = size_spec;
743         tspec->fmt = fmt;
744         tspec->print_function = print_function;
745         tspec->fmt_string = fmt_string;
746
747         tspec->field_width = field_width;
748         tspec->hexl_mode_trailer = (*s == 'z');
749         if (tspec->hexl_mode_trailer)
750                 s++;
751
752         return s;
753 }
754
755 /* Decode the modern od format string S.  Append the decoded
756    representation to the global array SPEC, reallocating SPEC if
757    necessary.  */
758
759 static void
760 decode_format_string(const char *s)
761 {
762         const char *s_orig = s;
763
764         while (*s != '\0') {
765                 struct tspec tspec;
766                 const char *next;
767
768                 next = decode_one_format(s_orig, s, &tspec);
769
770                 assert(s != next);
771                 s = next;
772                 G.spec = xrealloc_vector(G.spec, 4, G.n_specs);
773                 memcpy(&G.spec[G.n_specs], &tspec, sizeof(G.spec[0]));
774                 G.n_specs++;
775         }
776 }
777
778 /* Given a list of one or more input filenames FILE_LIST, set the global
779    file pointer IN_STREAM to position N_SKIP in the concatenation of
780    those files.  If any file operation fails or if there are fewer than
781    N_SKIP bytes in the combined input, give an error message and return
782    nonzero.  When possible, use seek rather than read operations to
783    advance IN_STREAM.  */
784
785 static void
786 skip(off_t n_skip)
787 {
788         if (n_skip == 0)
789                 return;
790
791         while (G.in_stream) { /* !EOF */
792                 struct stat file_stats;
793
794                 /* First try seeking.  For large offsets, this extra work is
795                    worthwhile.  If the offset is below some threshold it may be
796                    more efficient to move the pointer by reading.  There are two
797                    issues when trying to seek:
798                         - the file must be seekable.
799                         - before seeking to the specified position, make sure
800                           that the new position is in the current file.
801                           Try to do that by getting file's size using fstat.
802                           But that will work only for regular files.  */
803
804                         /* The st_size field is valid only for regular files
805                            (and for symbolic links, which cannot occur here).
806                            If the number of bytes left to skip is at least
807                            as large as the size of the current file, we can
808                            decrement n_skip and go on to the next file.  */
809                 if (fstat(fileno(G.in_stream), &file_stats) == 0
810                  && S_ISREG(file_stats.st_mode) && file_stats.st_size > 0
811                 ) {
812                         if (file_stats.st_size < n_skip) {
813                                 n_skip -= file_stats.st_size;
814                                 /* take "check & close / open_next" route */
815                         } else {
816                                 if (fseeko(G.in_stream, n_skip, SEEK_CUR) != 0)
817                                         G.exit_code = 1;
818                                 return;
819                         }
820                 } else {
821                         /* If it's not a regular file with positive size,
822                            position the file pointer by reading.  */
823                         char buf[1024];
824                         size_t n_bytes_to_read = 1024;
825                         size_t n_bytes_read;
826
827                         while (n_skip > 0) {
828                                 if (n_skip < n_bytes_to_read)
829                                         n_bytes_to_read = n_skip;
830                                 n_bytes_read = fread(buf, 1, n_bytes_to_read, G.in_stream);
831                                 n_skip -= n_bytes_read;
832                                 if (n_bytes_read != n_bytes_to_read)
833                                         break; /* EOF on this file or error */
834                         }
835                 }
836                 if (n_skip == 0)
837                         return;
838
839                 check_and_close();
840                 open_next_file();
841         }
842
843         if (n_skip)
844                 bb_simple_error_msg_and_die("can't skip past end of combined input");
845 }
846
847
848 typedef void FN_format_address(off_t address, char c);
849
850 static void
851 format_address_none(off_t address UNUSED_PARAM, char c UNUSED_PARAM)
852 {
853 }
854
855 static void
856 format_address_std(off_t address, char c)
857 {
858         /* Corresponds to 'c' */
859         G.address_fmt[sizeof(G.address_fmt)-2] = c;
860         printf(G.address_fmt, address);
861 }
862
863 #if ENABLE_LONG_OPTS
864 /* only used with --traditional */
865 static void
866 format_address_paren(off_t address, char c)
867 {
868         putchar('(');
869         format_address_std(address, ')');
870         if (c) putchar(c);
871 }
872
873 static void
874 format_address_label(off_t address, char c)
875 {
876         format_address_std(address, ' ');
877         format_address_paren(address + G_pseudo_offset, c);
878 }
879 #endif
880
881 static void
882 dump_hexl_mode_trailer(size_t n_bytes, const char *block)
883 {
884         fputs("  >", stdout);
885         while (n_bytes--) {
886                 unsigned c = *(unsigned char *) block++;
887                 c = (ISPRINT(c) ? c : '.');
888                 putchar(c);
889         }
890         putchar('<');
891 }
892
893 /* Write N_BYTES bytes from CURR_BLOCK to standard output once for each
894    of the N_SPEC format specs.  CURRENT_OFFSET is the byte address of
895    CURR_BLOCK in the concatenation of input files, and it is printed
896    (optionally) only before the output line associated with the first
897    format spec.  When duplicate blocks are being abbreviated, the output
898    for a sequence of identical input blocks is the output for the first
899    block followed by an asterisk alone on a line.  It is valid to compare
900    the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK.
901    That condition may be false only for the last input block -- and then
902    only when it has not been padded to length BYTES_PER_BLOCK.  */
903
904 static void
905 write_block(off_t current_offset, size_t n_bytes,
906                 const char *prev_block, const char *curr_block)
907 {
908         unsigned i;
909
910         if (!(option_mask32 & OPT_v)
911          && G.not_first
912          && n_bytes == G.bytes_per_block
913          && memcmp(prev_block, curr_block, G.bytes_per_block) == 0
914         ) {
915                 if (G.prev_pair_equal) {
916                         /* The two preceding blocks were equal, and the current
917                            block is the same as the last one, so print nothing.  */
918                 } else {
919                         puts("*");
920                         G.prev_pair_equal = 1;
921                 }
922         } else {
923                 G.not_first = 1;
924                 G.prev_pair_equal = 0;
925                 for (i = 0; i < G.n_specs; i++) {
926                         if (i == 0)
927                                 G.format_address(current_offset, '\0');
928                         else
929                                 printf("%*s", address_pad_len_char - '0', "");
930                         (*G.spec[i].print_function) (n_bytes, curr_block, G.spec[i].fmt_string);
931                         if (G.spec[i].hexl_mode_trailer) {
932                                 /* space-pad out to full line width, then dump the trailer */
933                                 unsigned datum_width = width_bytes[G.spec[i].size];
934                                 unsigned blank_fields = (G.bytes_per_block - n_bytes) / datum_width;
935                                 unsigned field_width = G.spec[i].field_width + 1;
936                                 printf("%*s", blank_fields * field_width, "");
937                                 dump_hexl_mode_trailer(n_bytes, curr_block);
938                         }
939                         putchar('\n');
940                 }
941         }
942 }
943
944 static void
945 read_block(size_t n, char *block, size_t *n_bytes_in_buffer)
946 {
947         assert(0 < n && n <= G.bytes_per_block);
948
949         *n_bytes_in_buffer = 0;
950
951         if (n == 0)
952                 return;
953
954         while (G.in_stream != NULL) { /* EOF.  */
955                 size_t n_needed;
956                 size_t n_read;
957
958                 n_needed = n - *n_bytes_in_buffer;
959                 n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, G.in_stream);
960                 *n_bytes_in_buffer += n_read;
961                 if (n_read == n_needed)
962                         break;
963                 /* error check is done in check_and_close */
964                 check_and_close();
965                 open_next_file();
966         }
967 }
968
969 /* Return the least common multiple of the sizes associated
970    with the format specs.  */
971
972 static int
973 get_lcm(void)
974 {
975         size_t i;
976         int l_c_m = 1;
977
978         for (i = 0; i < G.n_specs; i++)
979                 l_c_m = lcm(l_c_m, width_bytes[(int) G.spec[i].size]);
980         return l_c_m;
981 }
982
983 /* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
984    formatted block to standard output, and repeat until the specified
985    maximum number of bytes has been read or until all input has been
986    processed.  If the last block read is smaller than BYTES_PER_BLOCK
987    and its size is not a multiple of the size associated with a format
988    spec, extend the input block with zero bytes until its length is a
989    multiple of all format spec sizes.  Write the final block.  Finally,
990    write on a line by itself the offset of the byte after the last byte
991    read.  */
992
993 static void
994 dump(off_t current_offset, off_t end_offset)
995 {
996         char *block[2];
997         int idx;
998         size_t n_bytes_read;
999
1000         block[0] = xmalloc(2 * G.bytes_per_block);
1001         block[1] = block[0] + G.bytes_per_block;
1002
1003         idx = 0;
1004         if (option_mask32 & OPT_N) {
1005                 while (1) {
1006                         size_t n_needed;
1007                         if (current_offset >= end_offset) {
1008                                 n_bytes_read = 0;
1009                                 break;
1010                         }
1011                         n_needed = MIN(end_offset - current_offset, (off_t) G.bytes_per_block);
1012                         read_block(n_needed, block[idx], &n_bytes_read);
1013                         if (n_bytes_read < G.bytes_per_block)
1014                                 break;
1015                         assert(n_bytes_read == G.bytes_per_block);
1016                         write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]);
1017                         current_offset += n_bytes_read;
1018                         idx ^= 1;
1019                 }
1020         } else {
1021                 while (1) {
1022                         read_block(G.bytes_per_block, block[idx], &n_bytes_read);
1023                         if (n_bytes_read < G.bytes_per_block)
1024                                 break;
1025                         assert(n_bytes_read == G.bytes_per_block);
1026                         write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]);
1027                         current_offset += n_bytes_read;
1028                         idx ^= 1;
1029                 }
1030         }
1031
1032         if (n_bytes_read > 0) {
1033                 int l_c_m;
1034                 size_t bytes_to_write;
1035
1036                 l_c_m = get_lcm();
1037
1038                 /* Make bytes_to_write the smallest multiple of l_c_m that
1039                    is at least as large as n_bytes_read.  */
1040                 bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m);
1041
1042                 memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
1043                 write_block(current_offset, bytes_to_write,
1044                                 block[idx ^ 1], block[idx]);
1045                 current_offset += n_bytes_read;
1046         }
1047
1048         G.format_address(current_offset, '\n');
1049
1050         if ((option_mask32 & OPT_N) && current_offset >= end_offset)
1051                 check_and_close();
1052
1053         free(block[0]);
1054 }
1055
1056 /* Read N bytes into BLOCK from the concatenation of the input files
1057    named in the global array FILE_LIST.  On the first call to this
1058    function, the global variable IN_STREAM is expected to be an open
1059    stream associated with the input file INPUT_FILENAME.  If all N
1060    bytes cannot be read from IN_STREAM, close IN_STREAM and update
1061    the global variables IN_STREAM and INPUT_FILENAME.  Then try to
1062    read the remaining bytes from the newly opened file.  Repeat if
1063    necessary until EOF is reached for the last file in FILE_LIST.
1064    On subsequent calls, don't modify BLOCK and return zero.  Set
1065    *N_BYTES_IN_BUFFER to the number of bytes read.  If an error occurs,
1066    it will be detected through ferror when the stream is about to be
1067    closed.  If there is an error, give a message but continue reading
1068    as usual and return nonzero.  Otherwise return zero.  */
1069
1070 /* STRINGS mode.  Find each "string constant" in the input.
1071    A string constant is a run of at least 'string_min' ASCII
1072    graphic (or formatting) characters terminated by a null.
1073    Based on a function written by Richard Stallman for a
1074    traditional version of od.  */
1075
1076 static void
1077 dump_strings(off_t address, off_t end_offset)
1078 {
1079         unsigned bufsize = MAX(100, G.string_min);
1080         unsigned char *buf = xmalloc(bufsize);
1081
1082         while (1) {
1083                 size_t i;
1084                 int c;
1085
1086                 /* See if the next 'G.string_min' chars are all printing chars.  */
1087  tryline:
1088                 if ((option_mask32 & OPT_N) && (end_offset - G.string_min <= address))
1089                         break;
1090                 i = 0;
1091                 while (!(option_mask32 & OPT_N) || address < end_offset) {
1092                         if (i == bufsize) {
1093                                 bufsize += bufsize/8;
1094                                 buf = xrealloc(buf, bufsize);
1095                         }
1096
1097                         while (G.in_stream) { /* !EOF */
1098                                 c = fgetc(G.in_stream);
1099                                 if (c != EOF)
1100                                         goto got_char;
1101                                 check_and_close();
1102                                 open_next_file();
1103                         }
1104                         /* EOF */
1105                         goto ret;
1106  got_char:
1107                         address++;
1108                         if (!c)
1109                                 break;
1110                         if (!ISPRINT(c))
1111                                 goto tryline;   /* It isn't; give up on this string.  */
1112                         buf[i++] = c;           /* String continues; store it all.  */
1113                 }
1114
1115                 if (i < G.string_min)           /* Too short! */
1116                         goto tryline;
1117
1118                 /* If we get here, the string is all printable and NUL-terminated */
1119                 buf[i] = 0;
1120                 G.format_address(address - i - 1, ' ');
1121
1122                 for (i = 0; (c = buf[i]); i++) {
1123                         switch (c) {
1124                         case '\007': fputs("\\a", stdout); break;
1125                         case '\b': fputs("\\b", stdout); break;
1126                         case '\f': fputs("\\f", stdout); break;
1127                         case '\n': fputs("\\n", stdout); break;
1128                         case '\r': fputs("\\r", stdout); break;
1129                         case '\t': fputs("\\t", stdout); break;
1130                         case '\v': fputs("\\v", stdout); break;
1131                         default: putchar(c);
1132                         }
1133                 }
1134                 putchar('\n');
1135         }
1136
1137         /* We reach this point only if we search through
1138            (max_bytes_to_format - G.string_min) bytes before reaching EOF.  */
1139         check_and_close();
1140  ret:
1141         free(buf);
1142 }
1143
1144 #if ENABLE_LONG_OPTS
1145 /* If S is a valid traditional offset specification with an optional
1146    leading '+' return nonzero and set *OFFSET to the offset it denotes.  */
1147
1148 static int
1149 parse_old_offset(const char *s, off_t *offset)
1150 {
1151         static const struct suffix_mult Bb[] = {
1152                 { "B", 1024 },
1153                 { "b", 512 },
1154                 { "", 0 }
1155         };
1156         char *p;
1157         int radix;
1158
1159         /* Skip over any leading '+'. */
1160         if (s[0] == '+') ++s;
1161         if (!isdigit(s[0])) return 0; /* not a number */
1162
1163         /* Determine the radix we'll use to interpret S.  If there is a '.',
1164          * it's decimal, otherwise, if the string begins with '0X'or '0x',
1165          * it's hexadecimal, else octal.  */
1166         p = strchr(s, '.');
1167         radix = 8;
1168         if (p) {
1169                 p[0] = '\0'; /* cheating */
1170                 radix = 10;
1171         } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
1172                 radix = 16;
1173
1174         *offset = xstrtooff_sfx(s, radix, Bb);
1175         if (p) p[0] = '.';
1176
1177         return (*offset >= 0);
1178 }
1179 #endif
1180
1181 int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1182 int od_main(int argc UNUSED_PARAM, char **argv)
1183 {
1184 #if ENABLE_LONG_OPTS
1185         static const char od_longopts[] ALIGN1 =
1186                 "skip-bytes\0"        Required_argument "j"
1187                 "address-radix\0"     Required_argument "A"
1188                 "read-bytes\0"        Required_argument "N"
1189                 "format\0"            Required_argument "t"
1190                 "output-duplicates\0" No_argument       "v"
1191                 /* Yes, it's true: -S NUM, but --strings[=NUM]!
1192                  * that is, NUM is mandatory for -S but optional for --strings!
1193                  */
1194                 "strings\0"           Optional_argument "S"
1195                 "width\0"             Optional_argument "w"
1196                 "traditional\0"       No_argument       "\xff"
1197                 ;
1198 #endif
1199         const char *str_A, *str_N, *str_j, *str_S = "3";
1200         llist_t *lst_t = NULL;
1201         unsigned opt;
1202         int l_c_m;
1203         /* The number of input bytes to skip before formatting and writing.  */
1204         off_t n_bytes_to_skip = 0;
1205         /* The offset of the first byte after the last byte to be formatted.  */
1206         off_t end_offset = 0;
1207         /* The maximum number of bytes that will be formatted.  */
1208         off_t max_bytes_to_format = 0;
1209
1210         INIT_G();
1211
1212         /*G.spec = NULL; - already is */
1213         G.format_address = format_address_std;
1214         address_base_char = 'o';
1215         address_pad_len_char = '7';
1216
1217         /* Parse command line */
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_simple_error_msg_and_die("the last two arguments must be offsets");
1312                                 }
1313                         } else { /* >3 args */
1314                                 bb_simple_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_simple_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         {
1377                 int i;
1378                 for (i = 0; i < G.n_specs; i++) {
1379                         printf("%d: fmt='%s' width=%d\n",
1380                                 i, G.spec[i].fmt_string,
1381                                 width_bytes[G.spec[i].size]);
1382                 }
1383         }
1384 #endif
1385
1386         if (option_mask32 & OPT_S)
1387                 dump_strings(n_bytes_to_skip, end_offset);
1388         else
1389                 dump(n_bytes_to_skip, end_offset);
1390
1391         if (fclose(stdin))
1392                 bb_simple_perror_msg_and_die(bb_msg_standard_input);
1393
1394         return G.exit_code;
1395 }