od: fix -S NUM to not print chars >0x80
[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
550         switch (*s) {
551         case 'd':
552         case 'o':
553         case 'u':
554         case 'x': {
555                 static const char CSIL[] ALIGN1 = "CSIL";
556
557                 c = *s++;
558                 p = strchr(CSIL, *s);
559                 /* if *s == NUL, p != NULL! Testcase: "od -tx" */
560                 if (!p || *p == '\0') {
561                         size = sizeof(int);
562                         if (isdigit(s[0])) {
563                                 size = bb_strtou(s, &end, 0);
564                                 if (errno == ERANGE
565                                  || MAX_INTEGRAL_TYPE_SIZE < size
566                                  || integral_type_size[size] == NO_SIZE
567                                 ) {
568                                         bb_error_msg_and_die("invalid type string '%s'; "
569                                                 "%u-byte %s type is not supported",
570                                                 s_orig, size, "integral");
571                                 }
572                                 s = end;
573                         }
574                 } else {
575                         static const uint8_t CSIL_sizeof[4] = {
576                                 sizeof(char),
577                                 sizeof(short),
578                                 sizeof(int),
579                                 sizeof(long),
580                         };
581                         size = CSIL_sizeof[p - CSIL];
582                         s++; /* skip C/S/I/L */
583                 }
584
585 #define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \
586         ((Spec) == LONG_LONG ? (Max_format) \
587         : ((Spec) == LONG ? (Long_format) : (Min_format)))
588
589 #define FMT_BYTES_ALLOCATED 9
590                 size_spec = integral_type_size[size];
591
592                 {
593                         static const char doux[] ALIGN1 = "doux";
594                         static const char doux_fmt_letter[][4] = {
595                                 "lld", "llo", "llu", "llx"
596                         };
597                         static const enum output_format doux_fmt[] = {
598                                 SIGNED_DECIMAL,
599                                 OCTAL,
600                                 UNSIGNED_DECIMAL,
601                                 HEXADECIMAL,
602                         };
603                         static const uint8_t *const doux_bytes_to_XXX[] = {
604                                 bytes_to_signed_dec_digits,
605                                 bytes_to_oct_digits,
606                                 bytes_to_unsigned_dec_digits,
607                                 bytes_to_hex_digits,
608                         };
609                         static const char doux_fmtstring[][sizeof(" %%0%u%s")] = {
610                                 " %%%u%s",
611                                 " %%0%u%s",
612                                 " %%%u%s",
613                                 " %%0%u%s",
614                         };
615
616                         pos = strchr(doux, c) - doux;
617                         fmt = doux_fmt[pos];
618                         field_width = doux_bytes_to_XXX[pos][size];
619                         p = doux_fmt_letter[pos] + 2;
620                         if (size_spec == LONG) p--;
621                         if (size_spec == LONG_LONG) p -= 2;
622                         fmt_string = xasprintf(doux_fmtstring[pos], field_width, p);
623                 }
624
625                 switch (size_spec) {
626                 case CHAR:
627                         print_function = (fmt == SIGNED_DECIMAL
628                                     ? print_s_char
629                                     : print_char);
630                         break;
631                 case SHORT:
632                         print_function = (fmt == SIGNED_DECIMAL
633                                     ? print_s_short
634                                     : print_short);
635                         break;
636                 case INT:
637                         print_function = print_int;
638                         break;
639                 case LONG:
640                         print_function = print_long;
641                         break;
642                 default: /* case LONG_LONG: */
643                         print_function = print_long_long;
644                         break;
645                 }
646                 break;
647         }
648
649         case 'f': {
650                 static const char FDL[] ALIGN1 = "FDL";
651
652                 fmt = FLOATING_POINT;
653                 ++s;
654                 p = strchr(FDL, *s);
655                 if (!p) {
656                         size = sizeof(double);
657                         if (isdigit(s[0])) {
658                                 size = bb_strtou(s, &end, 0);
659                                 if (errno == ERANGE || size > MAX_FP_TYPE_SIZE
660                                  || fp_type_size[size] == NO_SIZE
661                                 ) {
662                                         bb_error_msg_and_die("invalid type string '%s'; "
663                                                 "%u-byte %s type is not supported",
664                                                 s_orig, size, "floating point");
665                                 }
666                                 s = end;
667                         }
668                 } else {
669                         static const uint8_t FDL_sizeof[] = {
670                                 sizeof(float),
671                                 sizeof(double),
672                                 sizeof(longdouble_t),
673                         };
674
675                         size = FDL_sizeof[p - FDL];
676                 }
677
678                 size_spec = fp_type_size[size];
679
680                 switch (size_spec) {
681                 case FLOAT_SINGLE:
682                         print_function = print_float;
683                         field_width = FLT_DIG + 8;
684                         /* Don't use %#e; not all systems support it.  */
685                         fmt_string = xasprintf(" %%%d.%de", field_width, FLT_DIG);
686                         break;
687                 case FLOAT_DOUBLE:
688                         print_function = print_double;
689                         field_width = DBL_DIG + 8;
690                         fmt_string = xasprintf(" %%%d.%de", field_width, DBL_DIG);
691                         break;
692                 default: /* case FLOAT_LONG_DOUBLE: */
693                         print_function = print_long_double;
694                         field_width = LDBL_DIG + 8;
695                         fmt_string = xasprintf(" %%%d.%dLe", field_width, LDBL_DIG);
696                         break;
697                 }
698                 break;
699         }
700
701         case 'a':
702                 ++s;
703                 fmt = NAMED_CHARACTER;
704                 size_spec = CHAR;
705                 print_function = print_named_ascii;
706                 field_width = 3;
707                 break;
708         case 'c':
709                 ++s;
710                 fmt = CHARACTER;
711                 size_spec = CHAR;
712                 print_function = print_ascii;
713                 field_width = 3;
714                 break;
715         default:
716                 bb_error_msg_and_die("invalid character '%c' "
717                                 "in type string '%s'", *s, s_orig);
718         }
719
720         tspec->size = size_spec;
721         tspec->fmt = fmt;
722         tspec->print_function = print_function;
723         tspec->fmt_string = fmt_string;
724
725         tspec->field_width = field_width;
726         tspec->hexl_mode_trailer = (*s == 'z');
727         if (tspec->hexl_mode_trailer)
728                 s++;
729
730         return s;
731 }
732
733 /* Decode the modern od format string S.  Append the decoded
734    representation to the global array SPEC, reallocating SPEC if
735    necessary.  */
736
737 static void
738 decode_format_string(const char *s)
739 {
740         const char *s_orig = s;
741
742         while (*s != '\0') {
743                 struct tspec tspec;
744                 const char *next;
745
746                 next = decode_one_format(s_orig, s, &tspec);
747
748                 assert(s != next);
749                 s = next;
750                 spec = xrealloc_vector(spec, 4, n_specs);
751                 memcpy(&spec[n_specs], &tspec, sizeof(spec[0]));
752                 n_specs++;
753         }
754 }
755
756 /* Given a list of one or more input filenames FILE_LIST, set the global
757    file pointer IN_STREAM to position N_SKIP in the concatenation of
758    those files.  If any file operation fails or if there are fewer than
759    N_SKIP bytes in the combined input, give an error message and return
760    nonzero.  When possible, use seek rather than read operations to
761    advance IN_STREAM.  */
762
763 static void
764 skip(off_t n_skip)
765 {
766         if (n_skip == 0)
767                 return;
768
769         while (in_stream) { /* !EOF */
770                 struct stat file_stats;
771
772                 /* First try seeking.  For large offsets, this extra work is
773                    worthwhile.  If the offset is below some threshold it may be
774                    more efficient to move the pointer by reading.  There are two
775                    issues when trying to seek:
776                         - the file must be seekable.
777                         - before seeking to the specified position, make sure
778                           that the new position is in the current file.
779                           Try to do that by getting file's size using fstat.
780                           But that will work only for regular files.  */
781
782                         /* The st_size field is valid only for regular files
783                            (and for symbolic links, which cannot occur here).
784                            If the number of bytes left to skip is at least
785                            as large as the size of the current file, we can
786                            decrement n_skip and go on to the next file.  */
787                 if (fstat(fileno(in_stream), &file_stats) == 0
788                  && S_ISREG(file_stats.st_mode) && file_stats.st_size > 0
789                 ) {
790                         if (file_stats.st_size < n_skip) {
791                                 n_skip -= file_stats.st_size;
792                                 /* take "check & close / open_next" route */
793                         } else {
794                                 if (fseeko(in_stream, n_skip, SEEK_CUR) != 0)
795                                         exit_code = 1;
796                                 return;
797                         }
798                 } else {
799                         /* If it's not a regular file with positive size,
800                            position the file pointer by reading.  */
801                         char buf[1024];
802                         size_t n_bytes_to_read = 1024;
803                         size_t n_bytes_read;
804
805                         while (n_skip > 0) {
806                                 if (n_skip < n_bytes_to_read)
807                                         n_bytes_to_read = n_skip;
808                                 n_bytes_read = fread(buf, 1, n_bytes_to_read, in_stream);
809                                 n_skip -= n_bytes_read;
810                                 if (n_bytes_read != n_bytes_to_read)
811                                         break; /* EOF on this file or error */
812                         }
813                 }
814                 if (n_skip == 0)
815                         return;
816
817                 check_and_close();
818                 open_next_file();
819         }
820
821         if (n_skip)
822                 bb_error_msg_and_die("can't skip past end of combined input");
823 }
824
825
826 typedef void FN_format_address(off_t address, char c);
827
828 static void
829 format_address_none(off_t address UNUSED_PARAM, char c UNUSED_PARAM)
830 {
831 }
832
833 static char address_fmt[] ALIGN1 = "%0n"OFF_FMT"xc";
834 /* Corresponds to 'x' above */
835 #define address_base_char address_fmt[sizeof(address_fmt)-3]
836 /* Corresponds to 'n' above */
837 #define address_pad_len_char address_fmt[2]
838
839 static void
840 format_address_std(off_t address, char c)
841 {
842         /* Corresponds to 'c' */
843         address_fmt[sizeof(address_fmt)-2] = c;
844         printf(address_fmt, address);
845 }
846
847 #if ENABLE_LONG_OPTS
848 /* only used with --traditional */
849 static void
850 format_address_paren(off_t address, char c)
851 {
852         putchar('(');
853         format_address_std(address, ')');
854         if (c) putchar(c);
855 }
856
857 static void
858 format_address_label(off_t address, char c)
859 {
860         format_address_std(address, ' ');
861         format_address_paren(address + pseudo_offset, c);
862 }
863 #endif
864
865 static void
866 dump_hexl_mode_trailer(size_t n_bytes, const char *block)
867 {
868         fputs("  >", stdout);
869         while (n_bytes--) {
870                 unsigned c = *(unsigned char *) block++;
871                 c = (ISPRINT(c) ? c : '.');
872                 putchar(c);
873         }
874         putchar('<');
875 }
876
877 /* Write N_BYTES bytes from CURR_BLOCK to standard output once for each
878    of the N_SPEC format specs.  CURRENT_OFFSET is the byte address of
879    CURR_BLOCK in the concatenation of input files, and it is printed
880    (optionally) only before the output line associated with the first
881    format spec.  When duplicate blocks are being abbreviated, the output
882    for a sequence of identical input blocks is the output for the first
883    block followed by an asterisk alone on a line.  It is valid to compare
884    the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK.
885    That condition may be false only for the last input block -- and then
886    only when it has not been padded to length BYTES_PER_BLOCK.  */
887
888 static void
889 write_block(off_t current_offset, size_t n_bytes,
890                 const char *prev_block, const char *curr_block)
891 {
892         static char first = 1;
893         static char prev_pair_equal = 0;
894         size_t i;
895
896         if (!(option_mask32 & OPT_v)
897          && !first
898          && n_bytes == bytes_per_block
899          && memcmp(prev_block, curr_block, bytes_per_block) == 0
900         ) {
901                 if (prev_pair_equal) {
902                         /* The two preceding blocks were equal, and the current
903                            block is the same as the last one, so print nothing.  */
904                 } else {
905                         puts("*");
906                         prev_pair_equal = 1;
907                 }
908         } else {
909                 first = 0;
910                 prev_pair_equal = 0;
911                 for (i = 0; i < n_specs; i++) {
912                         if (i == 0)
913                                 format_address(current_offset, '\0');
914                         else
915                                 printf("%*s", address_pad_len_char - '0', "");
916                         (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string);
917                         if (spec[i].hexl_mode_trailer) {
918                                 /* space-pad out to full line width, then dump the trailer */
919                                 int datum_width = width_bytes[spec[i].size];
920                                 int blank_fields = (bytes_per_block - n_bytes) / datum_width;
921                                 int field_width = spec[i].field_width + 1;
922                                 printf("%*s", blank_fields * field_width, "");
923                                 dump_hexl_mode_trailer(n_bytes, curr_block);
924                         }
925                         putchar('\n');
926                 }
927         }
928 }
929
930 static void
931 read_block(size_t n, char *block, size_t *n_bytes_in_buffer)
932 {
933         assert(0 < n && n <= bytes_per_block);
934
935         *n_bytes_in_buffer = 0;
936
937         if (n == 0)
938                 return;
939
940         while (in_stream != NULL) { /* EOF.  */
941                 size_t n_needed;
942                 size_t n_read;
943
944                 n_needed = n - *n_bytes_in_buffer;
945                 n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, in_stream);
946                 *n_bytes_in_buffer += n_read;
947                 if (n_read == n_needed)
948                         break;
949                 /* error check is done in check_and_close */
950                 check_and_close();
951                 open_next_file();
952         }
953 }
954
955 /* Return the least common multiple of the sizes associated
956    with the format specs.  */
957
958 static int
959 get_lcm(void)
960 {
961         size_t i;
962         int l_c_m = 1;
963
964         for (i = 0; i < n_specs; i++)
965                 l_c_m = lcm(l_c_m, width_bytes[(int) spec[i].size]);
966         return l_c_m;
967 }
968
969 /* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
970    formatted block to standard output, and repeat until the specified
971    maximum number of bytes has been read or until all input has been
972    processed.  If the last block read is smaller than BYTES_PER_BLOCK
973    and its size is not a multiple of the size associated with a format
974    spec, extend the input block with zero bytes until its length is a
975    multiple of all format spec sizes.  Write the final block.  Finally,
976    write on a line by itself the offset of the byte after the last byte
977    read.  */
978
979 static void
980 dump(off_t current_offset, off_t end_offset)
981 {
982         char *block[2];
983         int idx;
984         size_t n_bytes_read;
985
986         block[0] = xmalloc(2*bytes_per_block);
987         block[1] = block[0] + bytes_per_block;
988
989         idx = 0;
990         if (option_mask32 & OPT_N) {
991                 while (1) {
992                         size_t n_needed;
993                         if (current_offset >= end_offset) {
994                                 n_bytes_read = 0;
995                                 break;
996                         }
997                         n_needed = MIN(end_offset - current_offset,
998                                 (off_t) bytes_per_block);
999                         read_block(n_needed, block[idx], &n_bytes_read);
1000                         if (n_bytes_read < bytes_per_block)
1001                                 break;
1002                         assert(n_bytes_read == bytes_per_block);
1003                         write_block(current_offset, n_bytes_read,
1004                                block[!idx], block[idx]);
1005                         current_offset += n_bytes_read;
1006                         idx = !idx;
1007                 }
1008         } else {
1009                 while (1) {
1010                         read_block(bytes_per_block, block[idx], &n_bytes_read);
1011                         if (n_bytes_read < bytes_per_block)
1012                                 break;
1013                         assert(n_bytes_read == bytes_per_block);
1014                         write_block(current_offset, n_bytes_read,
1015                                block[!idx], block[idx]);
1016                         current_offset += n_bytes_read;
1017                         idx = !idx;
1018                 }
1019         }
1020
1021         if (n_bytes_read > 0) {
1022                 int l_c_m;
1023                 size_t bytes_to_write;
1024
1025                 l_c_m = get_lcm();
1026
1027                 /* Make bytes_to_write the smallest multiple of l_c_m that
1028                          is at least as large as n_bytes_read.  */
1029                 bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m);
1030
1031                 memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
1032                 write_block(current_offset, bytes_to_write,
1033                                    block[!idx], block[idx]);
1034                 current_offset += n_bytes_read;
1035         }
1036
1037         format_address(current_offset, '\n');
1038
1039         if ((option_mask32 & OPT_N) && current_offset >= end_offset)
1040                 check_and_close();
1041
1042         free(block[0]);
1043 }
1044
1045 /* Read N bytes into BLOCK from the concatenation of the input files
1046    named in the global array FILE_LIST.  On the first call to this
1047    function, the global variable IN_STREAM is expected to be an open
1048    stream associated with the input file INPUT_FILENAME.  If all N
1049    bytes cannot be read from IN_STREAM, close IN_STREAM and update
1050    the global variables IN_STREAM and INPUT_FILENAME.  Then try to
1051    read the remaining bytes from the newly opened file.  Repeat if
1052    necessary until EOF is reached for the last file in FILE_LIST.
1053    On subsequent calls, don't modify BLOCK and return zero.  Set
1054    *N_BYTES_IN_BUFFER to the number of bytes read.  If an error occurs,
1055    it will be detected through ferror when the stream is about to be
1056    closed.  If there is an error, give a message but continue reading
1057    as usual and return nonzero.  Otherwise return zero.  */
1058
1059 /* STRINGS mode.  Find each "string constant" in the input.
1060    A string constant is a run of at least 'string_min' ASCII
1061    graphic (or formatting) characters terminated by a null.
1062    Based on a function written by Richard Stallman for a
1063    traditional version of od.  */
1064
1065 static void
1066 dump_strings(off_t address, off_t end_offset)
1067 {
1068         unsigned bufsize = MAX(100, string_min);
1069         unsigned char *buf = xmalloc(bufsize);
1070
1071         while (1) {
1072                 size_t i;
1073                 int c;
1074
1075                 /* See if the next 'string_min' chars are all printing chars.  */
1076  tryline:
1077                 if ((option_mask32 & OPT_N) && (end_offset - string_min <= address))
1078                         break;
1079                 i = 0;
1080                 while (!(option_mask32 & OPT_N) || address < end_offset) {
1081                         if (i == bufsize) {
1082                                 bufsize += bufsize/8;
1083                                 buf = xrealloc(buf, bufsize);
1084                         }
1085
1086                         while (in_stream) { /* !EOF */
1087                                 c = fgetc(in_stream);
1088                                 if (c != EOF)
1089                                         goto got_char;
1090                                 check_and_close();
1091                                 open_next_file();
1092                         }
1093                         /* EOF */
1094                         goto ret;
1095  got_char:
1096                         address++;
1097                         if (!c)
1098                                 break;
1099                         if (!ISPRINT(c))
1100                                 goto tryline;   /* It isn't; give up on this string.  */
1101                         buf[i++] = c;           /* String continues; store it all.  */
1102                 }
1103
1104                 if (i < string_min)             /* Too short! */
1105                         goto tryline;
1106
1107                 /* If we get here, the string is all printable and NUL-terminated */
1108                 buf[i] = 0;
1109                 format_address(address - i - 1, ' ');
1110
1111                 for (i = 0; (c = buf[i]); i++) {
1112                         switch (c) {
1113                         case '\007': fputs("\\a", stdout); break;
1114                         case '\b': fputs("\\b", stdout); break;
1115                         case '\f': fputs("\\f", stdout); break;
1116                         case '\n': fputs("\\n", stdout); break;
1117                         case '\r': fputs("\\r", stdout); break;
1118                         case '\t': fputs("\\t", stdout); break;
1119                         case '\v': fputs("\\v", stdout); break;
1120                         default: putchar(c);
1121                         }
1122                 }
1123                 putchar('\n');
1124         }
1125
1126         /* We reach this point only if we search through
1127            (max_bytes_to_format - string_min) bytes before reaching EOF.  */
1128         check_and_close();
1129  ret:
1130         free(buf);
1131 }
1132
1133 #if ENABLE_LONG_OPTS
1134 /* If S is a valid traditional offset specification with an optional
1135    leading '+' return nonzero and set *OFFSET to the offset it denotes.  */
1136
1137 static int
1138 parse_old_offset(const char *s, off_t *offset)
1139 {
1140         static const struct suffix_mult Bb[] = {
1141                 { "B", 1024 },
1142                 { "b", 512 },
1143                 { "", 0 }
1144         };
1145         char *p;
1146         int radix;
1147
1148         /* Skip over any leading '+'. */
1149         if (s[0] == '+') ++s;
1150         if (!isdigit(s[0])) return 0; /* not a number */
1151
1152         /* Determine the radix we'll use to interpret S.  If there is a '.',
1153          * it's decimal, otherwise, if the string begins with '0X'or '0x',
1154          * it's hexadecimal, else octal.  */
1155         p = strchr(s, '.');
1156         radix = 8;
1157         if (p) {
1158                 p[0] = '\0'; /* cheating */
1159                 radix = 10;
1160         } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
1161                 radix = 16;
1162
1163         *offset = xstrtooff_sfx(s, radix, Bb);
1164         if (p) p[0] = '.';
1165
1166         return (*offset >= 0);
1167 }
1168 #endif
1169
1170 int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1171 int od_main(int argc UNUSED_PARAM, char **argv)
1172 {
1173         static const struct suffix_mult bkm[] = {
1174                 { "b", 512 },
1175                 { "k", 1024 },
1176                 { "m", 1024*1024 },
1177                 { "", 0 }
1178         };
1179 #if ENABLE_LONG_OPTS
1180         static const char od_longopts[] ALIGN1 =
1181                 "skip-bytes\0"        Required_argument "j"
1182                 "address-radix\0"     Required_argument "A"
1183                 "read-bytes\0"        Required_argument "N"
1184                 "format\0"            Required_argument "t"
1185                 "output-duplicates\0" No_argument       "v"
1186                 "strings\0"           Optional_argument "S"
1187                 "width\0"             Optional_argument "w"
1188                 "traditional\0"       No_argument       "\xff"
1189                 ;
1190 #endif
1191         char *str_A, *str_N, *str_j, *str_S;
1192         llist_t *lst_t = NULL;
1193         unsigned opt;
1194         int l_c_m;
1195         /* The number of input bytes to skip before formatting and writing.  */
1196         off_t n_bytes_to_skip = 0;
1197         /* The offset of the first byte after the last byte to be formatted.  */
1198         off_t end_offset = 0;
1199         /* The maximum number of bytes that will be formatted.  */
1200         off_t max_bytes_to_format = 0;
1201
1202         spec = NULL;
1203         format_address = format_address_std;
1204         address_base_char = 'o';
1205         address_pad_len_char = '7';
1206
1207         /* Parse command line */
1208         opt_complementary = "w+:t::"; /* -w N, -t is a list */
1209 #if ENABLE_LONG_OPTS
1210         applet_long_options = od_longopts;
1211 #endif
1212         opt = OD_GETOPT32();
1213         argv += optind;
1214         if (opt & OPT_A) {
1215                 static const char doxn[] ALIGN1 = "doxn";
1216                 static const char doxn_address_base_char[] ALIGN1 = {
1217                         'u', 'o', 'x', /* '?' fourth one is not important */
1218                 };
1219                 static const uint8_t doxn_address_pad_len_char[] ALIGN1 = {
1220                         '7', '7', '6', /* '?' */
1221                 };
1222                 char *p;
1223                 int pos;
1224                 p = strchr(doxn, str_A[0]);
1225                 if (!p)
1226                         bb_error_msg_and_die("bad output address radix "
1227                                 "'%c' (must be [doxn])", str_A[0]);
1228                 pos = p - doxn;
1229                 if (pos == 3) format_address = format_address_none;
1230                 address_base_char = doxn_address_base_char[pos];
1231                 address_pad_len_char = doxn_address_pad_len_char[pos];
1232         }
1233         if (opt & OPT_N) {
1234                 max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm);
1235         }
1236         if (opt & OPT_a) decode_format_string("a");
1237         if (opt & OPT_b) decode_format_string("oC");
1238         if (opt & OPT_c) decode_format_string("c");
1239         if (opt & OPT_d) decode_format_string("u2");
1240         if (opt & OPT_f) decode_format_string("fF");
1241         if (opt & OPT_h) decode_format_string("x2");
1242         if (opt & OPT_i) decode_format_string("d2");
1243         if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm);
1244         if (opt & OPT_l) decode_format_string("d4");
1245         if (opt & OPT_o) decode_format_string("o2");
1246         while (lst_t) {
1247                 decode_format_string(llist_pop(&lst_t));
1248         }
1249         if (opt & OPT_x) decode_format_string("x2");
1250         if (opt & OPT_s) decode_format_string("d2");
1251         if (opt & OPT_S) {
1252                 string_min = xstrtou_sfx(str_S, 0, bkm);
1253         }
1254
1255         // Bloat:
1256         //if ((option_mask32 & OPT_S) && n_specs > 0)
1257         //      bb_error_msg_and_die("no type may be specified when dumping strings");
1258
1259         /* If the --traditional option is used, there may be from
1260          * 0 to 3 remaining command line arguments;  handle each case
1261          * separately.
1262          * od [FILE] [[+]OFFSET[.][b] [[+]LABEL[.][b]]]
1263          * The offset and pseudo_start have the same syntax.
1264          *
1265          * FIXME: POSIX 1003.1-2001 with XSI requires support for the
1266          * traditional syntax even if --traditional is not given.  */
1267
1268 #if ENABLE_LONG_OPTS
1269         if (opt & OPT_traditional) {
1270                 if (argv[0]) {
1271                         off_t pseudo_start = -1;
1272                         off_t o1, o2;
1273
1274                         if (!argv[1]) { /* one arg */
1275                                 if (parse_old_offset(argv[0], &o1)) {
1276                                         /* od --traditional OFFSET */
1277                                         n_bytes_to_skip = o1;
1278                                         argv++;
1279                                 }
1280                                 /* od --traditional FILE */
1281                         } else if (!argv[2]) { /* two args */
1282                                 if (parse_old_offset(argv[0], &o1)
1283                                  && parse_old_offset(argv[1], &o2)
1284                                 ) {
1285                                         /* od --traditional OFFSET LABEL */
1286                                         n_bytes_to_skip = o1;
1287                                         pseudo_start = o2;
1288                                         argv += 2;
1289                                 } else if (parse_old_offset(argv[1], &o2)) {
1290                                         /* od --traditional FILE OFFSET */
1291                                         n_bytes_to_skip = o2;
1292                                         argv[1] = NULL;
1293                                 } else {
1294                                         bb_error_msg_and_die("invalid second argument '%s'", argv[1]);
1295                                 }
1296                         } else if (!argv[3]) { /* three args */
1297                                 if (parse_old_offset(argv[1], &o1)
1298                                  && parse_old_offset(argv[2], &o2)
1299                                 ) {
1300                                         /* od --traditional FILE OFFSET LABEL */
1301                                         n_bytes_to_skip = o1;
1302                                         pseudo_start = o2;
1303                                         argv[1] = NULL;
1304                                 } else {
1305                                         bb_error_msg_and_die("the last two arguments must be offsets");
1306                                 }
1307                         } else { /* >3 args */
1308                                 bb_error_msg_and_die("too many arguments");
1309                         }
1310
1311                         if (pseudo_start >= 0) {
1312                                 if (format_address == format_address_none) {
1313                                         address_base_char = 'o';
1314                                         address_pad_len_char = '7';
1315                                         format_address = format_address_paren;
1316                                 } else {
1317                                         format_address = format_address_label;
1318                                 }
1319                                 pseudo_offset = pseudo_start - n_bytes_to_skip;
1320                         }
1321                 }
1322                 /* else: od --traditional (without args) */
1323         }
1324 #endif
1325
1326         if (option_mask32 & OPT_N) {
1327                 end_offset = n_bytes_to_skip + max_bytes_to_format;
1328                 if (end_offset < n_bytes_to_skip)
1329                         bb_error_msg_and_die("SKIP + SIZE is too large");
1330         }
1331
1332         if (n_specs == 0) {
1333                 decode_format_string("o2");
1334                 /*n_specs = 1; - done by decode_format_string */
1335         }
1336
1337         /* If no files were listed on the command line,
1338            set the global pointer FILE_LIST so that it
1339            references the null-terminated list of one name: "-".  */
1340         file_list = bb_argv_dash;
1341         if (argv[0]) {
1342                 /* Set the global pointer FILE_LIST so that it
1343                    references the first file-argument on the command-line.  */
1344                 file_list = (char const *const *) argv;
1345         }
1346
1347         /* Open the first input file */
1348         open_next_file();
1349         /* Skip over any unwanted header bytes */
1350         skip(n_bytes_to_skip);
1351         if (!in_stream)
1352                 return EXIT_FAILURE;
1353
1354         /* Compute output block length */
1355         l_c_m = get_lcm();
1356
1357         if (opt & OPT_w) { /* -w: width */
1358                 if (!bytes_per_block || bytes_per_block % l_c_m != 0) {
1359                         bb_error_msg("warning: invalid width %u; using %d instead",
1360                                         (unsigned)bytes_per_block, l_c_m);
1361                         bytes_per_block = l_c_m;
1362                 }
1363         } else {
1364                 bytes_per_block = l_c_m;
1365                 if (l_c_m < DEFAULT_BYTES_PER_BLOCK)
1366                         bytes_per_block *= DEFAULT_BYTES_PER_BLOCK / l_c_m;
1367         }
1368
1369 #ifdef DEBUG
1370         for (i = 0; i < n_specs; i++) {
1371                 printf("%d: fmt=\"%s\" width=%d\n",
1372                         i, spec[i].fmt_string, width_bytes[spec[i].size]);
1373         }
1374 #endif
1375
1376         if (option_mask32 & OPT_S)
1377                 dump_strings(n_bytes_to_skip, end_offset);
1378         else
1379                 dump(n_bytes_to_skip, end_offset);
1380
1381         if (fclose(stdin))
1382                 bb_perror_msg_and_die(bb_msg_standard_input);
1383
1384         return exit_code;
1385 }