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