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