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