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