blob: e3086054462f00b14850fce74253555e585c5963 [file] [log] [blame]
Denis Vlasenko1114de72006-10-10 23:26:05 +00001/* od -- dump files in octal and other formats
2 Copyright (C) 92, 1995-2004 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
17
18/* Written by Jim Meyering. */
19
20/* Busyboxed by Denis Vlasenko
Denis Vlasenko601ae132006-11-28 23:37:46 +000021
22Based on od.c from coreutils-5.2.1
23Top bloat sources:
24
2500000073 t parse_old_offset
260000007b t get_lcm
2700000090 r long_options
2800000092 t print_named_ascii
29000000bf t print_ascii
3000000168 t write_block
3100000366 t decode_format_string
3200000a71 T od_main
33
34Tested for compat with coreutils 6.3
35using this script. Minor differences fixed.
36
37#!/bin/sh
38echo STD
39time /path/to/coreutils/od \
40...params... \
41>std
42echo Exit code $?
43echo BBOX
44time ./busybox od \
45...params... \
46>bbox
47echo Exit code $?
48diff -u -a std bbox >bbox.diff || { echo Different!; sleep 1; }
49
Denis Vlasenko1114de72006-10-10 23:26:05 +000050*/
51
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000052#include "libbb.h"
Denis Vlasenko1114de72006-10-10 23:26:05 +000053#include <getopt.h>
54
55#define assert(a) ((void)0)
Denis Vlasenko601ae132006-11-28 23:37:46 +000056
57/* Check for 0x7f is a coreutils 6.3 addition */
58#define ISPRINT(c) (((c)>=' ') && (c) != 0x7f)
Denis Vlasenko1114de72006-10-10 23:26:05 +000059
60typedef long double longdouble_t;
61typedef unsigned long long ulonglong_t;
Denis Vlasenko601ae132006-11-28 23:37:46 +000062typedef long long llong;
63
64#if ENABLE_LFS
65# define xstrtooff_sfx xstrtoull_sfx
66#else
67# define xstrtooff_sfx xstrtoul_sfx
68#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +000069
70/* The default number of input bytes per output line. */
71#define DEFAULT_BYTES_PER_BLOCK 16
72
73/* The number of decimal digits of precision in a float. */
74#ifndef FLT_DIG
75# define FLT_DIG 7
76#endif
77
78/* The number of decimal digits of precision in a double. */
79#ifndef DBL_DIG
80# define DBL_DIG 15
81#endif
82
83/* The number of decimal digits of precision in a long double. */
84#ifndef LDBL_DIG
85# define LDBL_DIG DBL_DIG
86#endif
87
88enum size_spec {
89 NO_SIZE,
90 CHAR,
91 SHORT,
92 INT,
93 LONG,
94 LONG_LONG,
Denis Vlasenko1114de72006-10-10 23:26:05 +000095 FLOAT_SINGLE,
96 FLOAT_DOUBLE,
97 FLOAT_LONG_DOUBLE,
98 N_SIZE_SPECS
99};
100
101enum output_format {
102 SIGNED_DECIMAL,
103 UNSIGNED_DECIMAL,
104 OCTAL,
105 HEXADECIMAL,
106 FLOATING_POINT,
107 NAMED_CHARACTER,
108 CHARACTER
109};
110
111/* Each output format specification (from '-t spec' or from
112 old-style options) is represented by one of these structures. */
113struct tspec {
114 enum output_format fmt;
115 enum size_spec size;
116 void (*print_function) (size_t, const char *, const char *);
117 char *fmt_string;
118 int hexl_mode_trailer;
119 int field_width;
120};
121
122/* Convert the number of 8-bit bytes of a binary representation to
123 the number of characters (digits + sign if the type is signed)
124 required to represent the same quantity in the specified base/type.
125 For example, a 32-bit (4-byte) quantity may require a field width
126 as wide as the following for these types:
127 11 unsigned octal
128 11 signed decimal
129 10 unsigned decimal
130 8 unsigned hexadecimal */
131
Denis Vlasenko601ae132006-11-28 23:37:46 +0000132static const uint8_t bytes_to_oct_digits[] =
Denis Vlasenko1114de72006-10-10 23:26:05 +0000133{0, 3, 6, 8, 11, 14, 16, 19, 22, 25, 27, 30, 32, 35, 38, 41, 43};
134
Denis Vlasenko601ae132006-11-28 23:37:46 +0000135static const uint8_t bytes_to_signed_dec_digits[] =
Denis Vlasenko1114de72006-10-10 23:26:05 +0000136{1, 4, 6, 8, 11, 13, 16, 18, 20, 23, 25, 28, 30, 33, 35, 37, 40};
137
Denis Vlasenko601ae132006-11-28 23:37:46 +0000138static const uint8_t bytes_to_unsigned_dec_digits[] =
Denis Vlasenko1114de72006-10-10 23:26:05 +0000139{0, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39};
140
Denis Vlasenko601ae132006-11-28 23:37:46 +0000141static const uint8_t bytes_to_hex_digits[] =
Denis Vlasenko1114de72006-10-10 23:26:05 +0000142{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32};
143
144/* Convert enum size_spec to the size of the named type. */
145static const signed char width_bytes[] = {
146 -1,
147 sizeof(char),
148 sizeof(short),
149 sizeof(int),
150 sizeof(long),
151 sizeof(ulonglong_t),
152 sizeof(float),
153 sizeof(double),
154 sizeof(longdouble_t)
155};
Denis Vlasenko1114de72006-10-10 23:26:05 +0000156/* Ensure that for each member of 'enum size_spec' there is an
157 initializer in the width_bytes array. */
158struct dummy {
159 int assert_width_bytes_matches_size_spec_decl
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000160 [ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1];
Denis Vlasenko1114de72006-10-10 23:26:05 +0000161};
162
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000163static smallint flag_dump_strings;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000164/* Non-zero if an old-style 'pseudo-address' was specified. */
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000165static smallint flag_pseudo_start;
166static smallint limit_bytes_to_format;
167/* When zero and two or more consecutive blocks are equal, format
Denis Vlasenko1114de72006-10-10 23:26:05 +0000168 only the first block and output an asterisk alone on the following
169 line to indicate that identical blocks have been elided. */
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000170static smallint verbose;
171static smallint ioerror;
172
173static size_t string_min;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000174
175/* An array of specs describing how to format each input block. */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000176static size_t n_specs;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000177static struct tspec *spec;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000178
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000179/* Function that accepts an address and an optional following char,
180 and prints the address and char to stdout. */
181static void (*format_address)(off_t, char);
182/* The difference between the old-style pseudo starting address and
183 the number of bytes to skip. */
184static off_t pseudo_offset;
185/* The number of input bytes to skip before formatting and writing. */
186static off_t n_bytes_to_skip;
187/* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all
188 input is formatted. */
189/* The maximum number of bytes that will be formatted. */
190static off_t max_bytes_to_format;
191/* The offset of the first byte after the last byte to be formatted. */
192static off_t end_offset;
193
Denis Vlasenko1114de72006-10-10 23:26:05 +0000194/* The number of input bytes formatted per output line. It must be
195 a multiple of the least common multiple of the sizes associated with
196 the specified output types. It should be as large as possible, but
197 no larger than 16 -- unless specified with the -w option. */
198static size_t bytes_per_block;
199
200/* Human-readable representation of *file_list (for error messages).
201 It differs from *file_list only when *file_list is "-". */
202static char const *input_filename;
203
204/* A NULL-terminated list of the file-arguments from the command line. */
205static char const *const *file_list;
206
207/* Initializer for file_list if no file-arguments
208 were specified on the command line. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000209static char const *const default_file_list[] = { "-", NULL };
Denis Vlasenko1114de72006-10-10 23:26:05 +0000210
211/* The input stream associated with the current file. */
212static FILE *in_stream;
213
Denis Vlasenko1114de72006-10-10 23:26:05 +0000214#define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000215static unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] = {
216 [sizeof(char)] = CHAR,
217#if USHRT_MAX != UCHAR_MAX
218 [sizeof(short)] = SHORT,
219#endif
220#if UINT_MAX != USHRT_MAX
221 [sizeof(int)] = INT,
222#endif
223#if ULONG_MAX != UINT_MAX
224 [sizeof(long)] = LONG,
225#endif
226#if ULLONG_MAX != ULONG_MAX
227 [sizeof(ulonglong_t)] = LONG_LONG,
228#endif
229};
Denis Vlasenko1114de72006-10-10 23:26:05 +0000230
231#define MAX_FP_TYPE_SIZE sizeof(longdouble_t)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000232static unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] = {
233 /* gcc seems to allow repeated indexes. Last one stays */
234 [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE,
235 [sizeof(double)] = FLOAT_DOUBLE,
236 [sizeof(float)] = FLOAT_SINGLE,
Denis Vlasenko1114de72006-10-10 23:26:05 +0000237};
238
239
240static unsigned
241gcd(unsigned u, unsigned v)
242{
243 unsigned t;
244 while (v != 0) {
245 t = u % v;
246 u = v;
247 v = t;
248 }
249 return u;
250}
251
252/* Compute the least common multiple of U and V. */
253static unsigned
254lcm(unsigned u, unsigned v) {
255 unsigned t = gcd(u, v);
256 if (t == 0)
257 return 0;
258 return u * v / t;
259}
260
261static void
262print_s_char(size_t n_bytes, const char *block, const char *fmt_string)
263{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000264 while (n_bytes--) {
265 int tmp = *(signed char *) block;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000266 printf(fmt_string, tmp);
267 block += sizeof(unsigned char);
268 }
269}
270
271static void
272print_char(size_t n_bytes, const char *block, const char *fmt_string)
273{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000274 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000275 unsigned tmp = *(unsigned char *) block;
276 printf(fmt_string, tmp);
277 block += sizeof(unsigned char);
278 }
279}
280
281static void
282print_s_short(size_t n_bytes, const char *block, const char *fmt_string)
283{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000284 n_bytes /= sizeof(signed short);
285 while (n_bytes--) {
286 int tmp = *(signed short *) block;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000287 printf(fmt_string, tmp);
288 block += sizeof(unsigned short);
289 }
290}
291
292static void
293print_short(size_t n_bytes, const char *block, const char *fmt_string)
294{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000295 n_bytes /= sizeof(unsigned short);
296 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000297 unsigned tmp = *(unsigned short *) block;
298 printf(fmt_string, tmp);
299 block += sizeof(unsigned short);
300 }
301}
302
303static void
304print_int(size_t n_bytes, const char *block, const char *fmt_string)
305{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000306 n_bytes /= sizeof(unsigned);
307 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000308 unsigned tmp = *(unsigned *) block;
309 printf(fmt_string, tmp);
310 block += sizeof(unsigned);
311 }
312}
313
Denis Vlasenko601ae132006-11-28 23:37:46 +0000314#if UINT_MAX == ULONG_MAX
315# define print_long print_int
316#else
Denis Vlasenko1114de72006-10-10 23:26:05 +0000317static void
318print_long(size_t n_bytes, const char *block, const char *fmt_string)
319{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000320 n_bytes /= sizeof(unsigned long);
321 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000322 unsigned long tmp = *(unsigned long *) block;
323 printf(fmt_string, tmp);
324 block += sizeof(unsigned long);
325 }
326}
Denis Vlasenko601ae132006-11-28 23:37:46 +0000327#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +0000328
Denis Vlasenko601ae132006-11-28 23:37:46 +0000329#if ULONG_MAX == ULLONG_MAX
330# define print_long_long print_long
331#else
Denis Vlasenko1114de72006-10-10 23:26:05 +0000332static void
333print_long_long(size_t n_bytes, const char *block, const char *fmt_string)
334{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000335 n_bytes /= sizeof(ulonglong_t);
336 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000337 ulonglong_t tmp = *(ulonglong_t *) block;
338 printf(fmt_string, tmp);
339 block += sizeof(ulonglong_t);
340 }
341}
Denis Vlasenko601ae132006-11-28 23:37:46 +0000342#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +0000343
344static void
345print_float(size_t n_bytes, const char *block, const char *fmt_string)
346{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000347 n_bytes /= sizeof(float);
348 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000349 float tmp = *(float *) block;
350 printf(fmt_string, tmp);
351 block += sizeof(float);
352 }
353}
354
355static void
356print_double(size_t n_bytes, const char *block, const char *fmt_string)
357{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000358 n_bytes /= sizeof(double);
359 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000360 double tmp = *(double *) block;
361 printf(fmt_string, tmp);
362 block += sizeof(double);
363 }
364}
365
366static void
367print_long_double(size_t n_bytes, const char *block, const char *fmt_string)
368{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000369 n_bytes /= sizeof(longdouble_t);
370 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000371 longdouble_t tmp = *(longdouble_t *) block;
372 printf(fmt_string, tmp);
373 block += sizeof(longdouble_t);
374 }
375}
376
Denis Vlasenko601ae132006-11-28 23:37:46 +0000377/* print_[named]_ascii are optimized for speed.
378 * Remember, someday you may want to pump gigabytes thru this thing.
379 * Saving a dozen of .text bytes here is counter-productive */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000380
381static void
382print_named_ascii(size_t n_bytes, const char *block,
383 const char *unused_fmt_string ATTRIBUTE_UNUSED)
384{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000385 /* Names for some non-printing characters. */
386 static const char charname[33][3] = {
387 "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
388 " bs", " ht", " nl", " vt", " ff", " cr", " so", " si",
389 "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
390 "can", " em", "sub", "esc", " fs", " gs", " rs", " us",
391 " sp"
392 };
393 // buf[N] pos: 01234 56789
394 char buf[12] = " x\0 0xx\0";
395 // actually " x\0 xxx\0", but I want to share the string with below.
396 // [12] because we take three 32bit stack slots anyway, and
397 // gcc is too dumb to initialize with constant stores,
398 // it copies initializer from rodata. Oh well.
Denis Vlasenko1114de72006-10-10 23:26:05 +0000399
Denis Vlasenko601ae132006-11-28 23:37:46 +0000400 while (n_bytes--) {
401 unsigned masked_c = *(unsigned char *) block++;
402
403 masked_c &= 0x7f;
404 if (masked_c == 0x7f) {
405 fputs(" del", stdout);
406 continue;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000407 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000408 if (masked_c > ' ') {
409 buf[3] = masked_c;
410 fputs(buf, stdout);
411 continue;
412 }
413 /* Why? Because printf(" %3.3s") is much slower... */
414 buf[6] = charname[masked_c][0];
415 buf[7] = charname[masked_c][1];
416 buf[8] = charname[masked_c][2];
417 fputs(buf+5, stdout);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000418 }
419}
420
421static void
422print_ascii(size_t n_bytes, const char *block,
423 const char *unused_fmt_string ATTRIBUTE_UNUSED)
424{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000425 // buf[N] pos: 01234 56789
426 char buf[12] = " x\0 0xx\0";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000427
Denis Vlasenko601ae132006-11-28 23:37:46 +0000428 while (n_bytes--) {
429 const char *s;
430 unsigned c = *(unsigned char *) block++;
431
432 if (ISPRINT(c)) {
433 buf[3] = c;
434 fputs(buf, stdout);
435 continue;
436 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000437 switch (c) {
438 case '\0':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000439 s = " \\0";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000440 break;
441 case '\007':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000442 s = " \\a";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000443 break;
444 case '\b':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000445 s = " \\b";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000446 break;
447 case '\f':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000448 s = " \\f";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000449 break;
450 case '\n':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000451 s = " \\n";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000452 break;
453 case '\r':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000454 s = " \\r";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000455 break;
456 case '\t':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000457 s = " \\t";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000458 break;
459 case '\v':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000460 s = " \\v";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000461 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000462 case '\x7f':
463 s = " 177";
464 break;
465 default: /* c is never larger than 040 */
466 buf[7] = (c >> 3) + '0';
467 buf[8] = (c & 7) + '0';
468 s = buf + 5;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000469 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000470 fputs(s, stdout);
471 }
472}
Denis Vlasenko1114de72006-10-10 23:26:05 +0000473
Denis Vlasenko601ae132006-11-28 23:37:46 +0000474/* Given a list of one or more input filenames FILE_LIST, set the global
475 file pointer IN_STREAM and the global string INPUT_FILENAME to the
476 first one that can be successfully opened. Modify FILE_LIST to
477 reference the next filename in the list. A file name of "-" is
478 interpreted as standard input. If any file open fails, give an error
479 message and return nonzero. */
480
481static void
482open_next_file(void)
483{
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000484 while (1) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000485 input_filename = *file_list;
486 if (!input_filename)
487 return;
488 file_list++;
489 in_stream = fopen_or_warn_stdin(input_filename);
490 if (in_stream) {
491 if (in_stream == stdin)
492 input_filename = bb_msg_standard_input;
493 break;
494 }
495 ioerror = 1;
496 }
497
498 if (limit_bytes_to_format && !flag_dump_strings)
499 setbuf(in_stream, NULL);
500}
501
502/* Test whether there have been errors on in_stream, and close it if
503 it is not standard input. Return nonzero if there has been an error
504 on in_stream or stdout; return zero otherwise. This function will
505 report more than one error only if both a read and a write error
506 have occurred. IN_ERRNO, if nonzero, is the error number
507 corresponding to the most recent action for IN_STREAM. */
508
509static void
510check_and_close(void)
511{
512 if (in_stream) {
513 if (ferror(in_stream)) {
514 bb_error_msg("%s: read error", input_filename);
515 ioerror = 1;
516 }
517 fclose_if_not_stdin(in_stream);
518 in_stream = NULL;
519 }
520
521 if (ferror(stdout)) {
522 bb_error_msg("write error");
523 ioerror = 1;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000524 }
525}
526
527/* If S points to a single valid modern od format string, put
528 a description of that format in *TSPEC, make *NEXT point at the
529 character following the just-decoded format (if *NEXT is non-NULL),
Denis Vlasenko601ae132006-11-28 23:37:46 +0000530 and return zero. For example, if S were "d4afL"
531 *NEXT would be set to "afL" and *TSPEC would be
Denis Vlasenko1114de72006-10-10 23:26:05 +0000532 {
533 fmt = SIGNED_DECIMAL;
534 size = INT or LONG; (whichever integral_type_size[4] resolves to)
535 print_function = print_int; (assuming size == INT)
Denis Vlasenko8e858e22007-03-07 09:35:43 +0000536 fmt_string = "%011d%c";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000537 }
538 S_ORIG is solely for reporting errors. It should be the full format
Denis Vlasenko601ae132006-11-28 23:37:46 +0000539 string argument. */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000540
Denis Vlasenko601ae132006-11-28 23:37:46 +0000541static void
Denis Vlasenko1114de72006-10-10 23:26:05 +0000542decode_one_format(const char *s_orig, const char *s, const char **next,
543 struct tspec *tspec)
544{
545 enum size_spec size_spec;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000546 unsigned size;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000547 enum output_format fmt;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000548 const char *p;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000549 char *end;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000550 char *fmt_string = NULL;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000551 void (*print_function) (size_t, const char *, const char *);
552 unsigned c;
553 unsigned field_width = 0;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000554 int pos;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000555
556 assert(tspec != NULL);
557
558 switch (*s) {
559 case 'd':
560 case 'o':
561 case 'u':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000562 case 'x': {
563 static const char CSIL[] = "CSIL";
564
565 c = *s++;
566 p = strchr(CSIL, *s);
567 if (!p) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000568 size = sizeof(int);
569 if (isdigit(s[0])) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000570 size = bb_strtou(s, &end, 0);
571 if (errno == ERANGE
572 || MAX_INTEGRAL_TYPE_SIZE < size
Denis Vlasenko1114de72006-10-10 23:26:05 +0000573 || integral_type_size[size] == NO_SIZE
574 ) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000575 bb_error_msg_and_die("invalid type string '%s'; "
576 "%u-byte %s type is not supported",
577 s_orig, size, "integral");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000578 }
579 s = end;
580 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000581 } else {
582 static const uint8_t CSIL_sizeof[] = {
583 sizeof(char),
584 sizeof(short),
585 sizeof(int),
586 sizeof(long),
587 };
588 size = CSIL_sizeof[p - CSIL];
Denis Vlasenko1114de72006-10-10 23:26:05 +0000589 }
590
591#define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \
592 ((Spec) == LONG_LONG ? (Max_format) \
593 : ((Spec) == LONG ? (Long_format) : (Min_format)))
594
595#define FMT_BYTES_ALLOCATED 9
Denis Vlasenko1114de72006-10-10 23:26:05 +0000596 size_spec = integral_type_size[size];
597
Denis Vlasenko601ae132006-11-28 23:37:46 +0000598 {
599 static const char doux[] = "doux";
600 static const char doux_fmt_letter[][4] = {
601 "lld", "llo", "llu", "llx"
602 };
603 static const enum output_format doux_fmt[] = {
604 SIGNED_DECIMAL,
605 OCTAL,
606 UNSIGNED_DECIMAL,
607 HEXADECIMAL,
608 };
609 static const uint8_t *const doux_bytes_to_XXX[] = {
610 bytes_to_signed_dec_digits,
611 bytes_to_oct_digits,
612 bytes_to_unsigned_dec_digits,
613 bytes_to_hex_digits,
614 };
615 static const char doux_fmtstring[][sizeof(" %%0%u%s")] = {
616 " %%%u%s",
617 " %%0%u%s",
618 " %%%u%s",
619 " %%0%u%s",
620 };
Denis Vlasenko1114de72006-10-10 23:26:05 +0000621
Denis Vlasenko601ae132006-11-28 23:37:46 +0000622 pos = strchr(doux, c) - doux;
623 fmt = doux_fmt[pos];
624 field_width = doux_bytes_to_XXX[pos][size];
625 p = doux_fmt_letter[pos] + 2;
626 if (size_spec == LONG) p--;
627 if (size_spec == LONG_LONG) p -= 2;
628 fmt_string = xasprintf(doux_fmtstring[pos], field_width, p);
629 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000630
631 switch (size_spec) {
632 case CHAR:
633 print_function = (fmt == SIGNED_DECIMAL
634 ? print_s_char
635 : print_char);
636 break;
637 case SHORT:
638 print_function = (fmt == SIGNED_DECIMAL
639 ? print_s_short
640 : print_short);
641 break;
642 case INT:
643 print_function = print_int;
644 break;
645 case LONG:
646 print_function = print_long;
647 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000648 default: /* case LONG_LONG: */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000649 print_function = print_long_long;
650 break;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000651 }
652 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000653 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000654
Denis Vlasenko601ae132006-11-28 23:37:46 +0000655 case 'f': {
656 static const char FDL[] = "FDL";
657
Denis Vlasenko1114de72006-10-10 23:26:05 +0000658 fmt = FLOATING_POINT;
659 ++s;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000660 p = strchr(FDL, *s);
661 if (!p) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000662 size = sizeof(double);
663 if (isdigit(s[0])) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000664 size = bb_strtou(s, &end, 0);
665 if (errno == ERANGE || size > MAX_FP_TYPE_SIZE
Denis Vlasenko1114de72006-10-10 23:26:05 +0000666 || fp_type_size[size] == NO_SIZE
667 ) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000668 bb_error_msg_and_die("invalid type string '%s'; "
669 "%u-byte %s type is not supported",
670 s_orig, size, "floating point");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000671 }
672 s = end;
673 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000674 } else {
675 static const uint8_t FDL_sizeof[] = {
676 sizeof(float),
677 sizeof(double),
678 sizeof(longdouble_t),
679 };
680
681 size = FDL_sizeof[p - FDL];
Denis Vlasenko1114de72006-10-10 23:26:05 +0000682 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000683
Denis Vlasenko1114de72006-10-10 23:26:05 +0000684 size_spec = fp_type_size[size];
685
686 switch (size_spec) {
687 case FLOAT_SINGLE:
688 print_function = print_float;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000689 field_width = FLT_DIG + 8;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000690 /* Don't use %#e; not all systems support it. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000691 fmt_string = xasprintf(" %%%d.%de", field_width, FLT_DIG);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000692 break;
693 case FLOAT_DOUBLE:
694 print_function = print_double;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000695 field_width = DBL_DIG + 8;
696 fmt_string = xasprintf(" %%%d.%de", field_width, DBL_DIG);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000697 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000698 default: /* case FLOAT_LONG_DOUBLE: */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000699 print_function = print_long_double;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000700 field_width = LDBL_DIG + 8;
701 fmt_string = xasprintf(" %%%d.%dLe", field_width, LDBL_DIG);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000702 break;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000703 }
704 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000705 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000706
707 case 'a':
708 ++s;
709 fmt = NAMED_CHARACTER;
710 size_spec = CHAR;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000711 print_function = print_named_ascii;
712 field_width = 3;
713 break;
714 case 'c':
715 ++s;
716 fmt = CHARACTER;
717 size_spec = CHAR;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000718 print_function = print_ascii;
719 field_width = 3;
720 break;
721 default:
Denis Vlasenko601ae132006-11-28 23:37:46 +0000722 bb_error_msg_and_die("invalid character '%c' "
723 "in type string '%s'", *s, s_orig);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000724 }
725
726 tspec->size = size_spec;
727 tspec->fmt = fmt;
728 tspec->print_function = print_function;
729 tspec->fmt_string = fmt_string;
730
731 tspec->field_width = field_width;
732 tspec->hexl_mode_trailer = (*s == 'z');
733 if (tspec->hexl_mode_trailer)
734 s++;
735
736 if (next != NULL)
737 *next = s;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000738}
739
740/* Decode the modern od format string S. Append the decoded
741 representation to the global array SPEC, reallocating SPEC if
742 necessary. Return zero if S is valid, nonzero otherwise. */
743
Denis Vlasenko601ae132006-11-28 23:37:46 +0000744static void
Denis Vlasenko1114de72006-10-10 23:26:05 +0000745decode_format_string(const char *s)
746{
747 const char *s_orig = s;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000748
749 while (*s != '\0') {
750 struct tspec tspec;
751 const char *next;
752
Denis Vlasenko601ae132006-11-28 23:37:46 +0000753 decode_one_format(s_orig, s, &next, &tspec);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000754
755 assert(s != next);
756 s = next;
757 n_specs++;
758 spec = xrealloc(spec, n_specs * sizeof(*spec));
759 memcpy(&spec[n_specs-1], &tspec, sizeof *spec);
760 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000761}
762
763/* Given a list of one or more input filenames FILE_LIST, set the global
764 file pointer IN_STREAM to position N_SKIP in the concatenation of
765 those files. If any file operation fails or if there are fewer than
766 N_SKIP bytes in the combined input, give an error message and return
767 nonzero. When possible, use seek rather than read operations to
768 advance IN_STREAM. */
769
Denis Vlasenko601ae132006-11-28 23:37:46 +0000770static void
771skip(off_t n_skip)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000772{
Denis Vlasenko1114de72006-10-10 23:26:05 +0000773 if (n_skip == 0)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000774 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000775
Denis Vlasenko601ae132006-11-28 23:37:46 +0000776 while (in_stream) { /* !EOF */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000777 struct stat file_stats;
778
779 /* First try seeking. For large offsets, this extra work is
780 worthwhile. If the offset is below some threshold it may be
781 more efficient to move the pointer by reading. There are two
782 issues when trying to seek:
783 - the file must be seekable.
784 - before seeking to the specified position, make sure
785 that the new position is in the current file.
786 Try to do that by getting file's size using fstat.
787 But that will work only for regular files. */
788
Denis Vlasenko1114de72006-10-10 23:26:05 +0000789 /* The st_size field is valid only for regular files
790 (and for symbolic links, which cannot occur here).
791 If the number of bytes left to skip is at least
792 as large as the size of the current file, we can
793 decrement n_skip and go on to the next file. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000794 if (fstat(fileno(in_stream), &file_stats) == 0
795 && S_ISREG(file_stats.st_mode) && file_stats.st_size >= 0
796 ) {
797 if (file_stats.st_size < n_skip) {
798 n_skip -= file_stats.st_size;
799 /* take check&close / open_next route */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000800 } else {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000801 if (fseeko(in_stream, n_skip, SEEK_CUR) != 0)
802 ioerror = 1;
803 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000804 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000805 } else {
806 /* If it's not a regular file with nonnegative size,
807 position the file pointer by reading. */
808 char buf[BUFSIZ];
809 size_t n_bytes_read, n_bytes_to_read = BUFSIZ;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000810
Denis Vlasenko601ae132006-11-28 23:37:46 +0000811 while (n_skip > 0) {
812 if (n_skip < n_bytes_to_read)
813 n_bytes_to_read = n_skip;
814 n_bytes_read = fread(buf, 1, n_bytes_to_read, in_stream);
815 n_skip -= n_bytes_read;
816 if (n_bytes_read != n_bytes_to_read)
Denis Vlasenko8e858e22007-03-07 09:35:43 +0000817 break; /* EOF on this file or error */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000818 }
819 }
820 if (n_skip == 0)
821 return;
822
823 check_and_close();
824 open_next_file();
Denis Vlasenko1114de72006-10-10 23:26:05 +0000825 }
826
Denis Vlasenko601ae132006-11-28 23:37:46 +0000827 if (n_skip)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000828 bb_error_msg_and_die("cannot skip past end of combined input");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000829}
830
Denis Vlasenko601ae132006-11-28 23:37:46 +0000831
832typedef void FN_format_address(off_t address, char c);
833
Denis Vlasenko1114de72006-10-10 23:26:05 +0000834static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000835format_address_none(off_t address ATTRIBUTE_UNUSED, char c ATTRIBUTE_UNUSED)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000836{
837}
838
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000839static char address_fmt[] = "%0n"OFF_FMT"xc";
Denis Vlasenko601ae132006-11-28 23:37:46 +0000840/* Corresponds to 'x' above */
841#define address_base_char address_fmt[sizeof(address_fmt)-3]
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000842/* Corresponds to 'n' above */
843#define address_pad_len_char address_fmt[2]
Denis Vlasenko601ae132006-11-28 23:37:46 +0000844
Denis Vlasenko1114de72006-10-10 23:26:05 +0000845static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000846format_address_std(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000847{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000848 /* Corresponds to 'c' */
849 address_fmt[sizeof(address_fmt)-2] = c;
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000850 printf(address_fmt, address);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000851}
852
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000853#if ENABLE_GETOPT_LONG
854/* only used with --traditional */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000855static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000856format_address_paren(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000857{
858 putchar('(');
859 format_address_std(address, ')');
Denis Vlasenko0f5905e2006-12-17 19:21:13 +0000860 if (c) putchar(c);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000861}
862
863static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000864format_address_label(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000865{
866 format_address_std(address, ' ');
867 format_address_paren(address + pseudo_offset, c);
868}
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000869#endif
Denis Vlasenko601ae132006-11-28 23:37:46 +0000870
871static void
872dump_hexl_mode_trailer(size_t n_bytes, const char *block)
873{
874 fputs(" >", stdout);
875 while (n_bytes--) {
876 unsigned c = *(unsigned char *) block++;
877 c = (ISPRINT(c) ? c : '.');
878 putchar(c);
879 }
880 putchar('<');
881}
882
Denis Vlasenko1114de72006-10-10 23:26:05 +0000883/* Write N_BYTES bytes from CURR_BLOCK to standard output once for each
884 of the N_SPEC format specs. CURRENT_OFFSET is the byte address of
885 CURR_BLOCK in the concatenation of input files, and it is printed
886 (optionally) only before the output line associated with the first
887 format spec. When duplicate blocks are being abbreviated, the output
888 for a sequence of identical input blocks is the output for the first
889 block followed by an asterisk alone on a line. It is valid to compare
890 the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK.
891 That condition may be false only for the last input block -- and then
892 only when it has not been padded to length BYTES_PER_BLOCK. */
893
894static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000895write_block(off_t current_offset, size_t n_bytes,
Denis Vlasenko1114de72006-10-10 23:26:05 +0000896 const char *prev_block, const char *curr_block)
897{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000898 static char first = 1;
899 static char prev_pair_equal = 0;
900 size_t i;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000901
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000902 if (!verbose && !first
Denis Vlasenko1114de72006-10-10 23:26:05 +0000903 && n_bytes == bytes_per_block
904 && memcmp(prev_block, curr_block, bytes_per_block) == 0
905 ) {
906 if (prev_pair_equal) {
907 /* The two preceding blocks were equal, and the current
908 block is the same as the last one, so print nothing. */
909 } else {
910 puts("*");
911 prev_pair_equal = 1;
912 }
913 } else {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000914 first = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000915 prev_pair_equal = 0;
916 for (i = 0; i < n_specs; i++) {
917 if (i == 0)
918 format_address(current_offset, '\0');
919 else
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000920 printf("%*s", address_pad_len_char - '0', "");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000921 (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string);
922 if (spec[i].hexl_mode_trailer) {
923 /* space-pad out to full line width, then dump the trailer */
924 int datum_width = width_bytes[spec[i].size];
925 int blank_fields = (bytes_per_block - n_bytes) / datum_width;
926 int field_width = spec[i].field_width + 1;
927 printf("%*s", blank_fields * field_width, "");
928 dump_hexl_mode_trailer(n_bytes, curr_block);
929 }
930 putchar('\n');
931 }
932 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000933}
934
Denis Vlasenko601ae132006-11-28 23:37:46 +0000935static void
Denis Vlasenko1114de72006-10-10 23:26:05 +0000936read_block(size_t n, char *block, size_t *n_bytes_in_buffer)
937{
Denis Vlasenko1114de72006-10-10 23:26:05 +0000938 assert(0 < n && n <= bytes_per_block);
939
940 *n_bytes_in_buffer = 0;
941
942 if (n == 0)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000943 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000944
945 while (in_stream != NULL) { /* EOF. */
946 size_t n_needed;
947 size_t n_read;
948
949 n_needed = n - *n_bytes_in_buffer;
950 n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, in_stream);
951 *n_bytes_in_buffer += n_read;
952 if (n_read == n_needed)
953 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000954 /* error check is done in check_and_close */
955 check_and_close();
956 open_next_file();
Denis Vlasenko1114de72006-10-10 23:26:05 +0000957 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000958}
959
960/* Return the least common multiple of the sizes associated
961 with the format specs. */
962
963static int
964get_lcm(void)
965{
966 size_t i;
967 int l_c_m = 1;
968
969 for (i = 0; i < n_specs; i++)
970 l_c_m = lcm(l_c_m, width_bytes[(int) spec[i].size]);
971 return l_c_m;
972}
973
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000974#if ENABLE_GETOPT_LONG
Denis Vlasenko1114de72006-10-10 23:26:05 +0000975/* If S is a valid traditional offset specification with an optional
976 leading '+' return nonzero and set *OFFSET to the offset it denotes. */
977
978static int
Denis Vlasenko601ae132006-11-28 23:37:46 +0000979parse_old_offset(const char *s, off_t *offset)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000980{
981 static const struct suffix_mult Bb[] = {
982 { "B", 1024 },
983 { "b", 512 },
Denis Vlasenkof8689632007-07-27 15:06:25 +0000984 { }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000985 };
Denis Vlasenko601ae132006-11-28 23:37:46 +0000986 char *p;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000987 int radix;
988
Denis Vlasenko1114de72006-10-10 23:26:05 +0000989 /* Skip over any leading '+'. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000990 if (s[0] == '+') ++s;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000991
992 /* Determine the radix we'll use to interpret S. If there is a '.',
Denis Vlasenko601ae132006-11-28 23:37:46 +0000993 * it's decimal, otherwise, if the string begins with '0X'or '0x',
994 * it's hexadecimal, else octal. */
995 p = strchr(s, '.');
996 radix = 8;
997 if (p) {
998 p[0] = '\0'; /* cheating */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000999 radix = 10;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001000 } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
1001 radix = 16;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001002
Denis Vlasenko601ae132006-11-28 23:37:46 +00001003 *offset = xstrtooff_sfx(s, radix, Bb);
1004 if (p) p[0] = '.';
1005
1006 return (*offset >= 0);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001007}
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001008#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +00001009
1010/* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
1011 formatted block to standard output, and repeat until the specified
1012 maximum number of bytes has been read or until all input has been
1013 processed. If the last block read is smaller than BYTES_PER_BLOCK
1014 and its size is not a multiple of the size associated with a format
1015 spec, extend the input block with zero bytes until its length is a
1016 multiple of all format spec sizes. Write the final block. Finally,
1017 write on a line by itself the offset of the byte after the last byte
1018 read. Accumulate return values from calls to read_block and
1019 check_and_close, and if any was nonzero, return nonzero.
1020 Otherwise, return zero. */
1021
Denis Vlasenko601ae132006-11-28 23:37:46 +00001022static void
Denis Vlasenko1114de72006-10-10 23:26:05 +00001023dump(void)
1024{
1025 char *block[2];
Denis Vlasenko601ae132006-11-28 23:37:46 +00001026 off_t current_offset;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001027 int idx;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001028 size_t n_bytes_read;
1029
1030 block[0] = xmalloc(2*bytes_per_block);
1031 block[1] = block[0] + bytes_per_block;
1032
1033 current_offset = n_bytes_to_skip;
1034
1035 idx = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001036 if (limit_bytes_to_format) {
1037 while (1) {
1038 size_t n_needed;
1039 if (current_offset >= end_offset) {
1040 n_bytes_read = 0;
1041 break;
1042 }
1043 n_needed = MIN(end_offset - current_offset,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001044 (off_t) bytes_per_block);
1045 read_block(n_needed, block[idx], &n_bytes_read);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001046 if (n_bytes_read < bytes_per_block)
1047 break;
1048 assert(n_bytes_read == bytes_per_block);
1049 write_block(current_offset, n_bytes_read,
1050 block[!idx], block[idx]);
1051 current_offset += n_bytes_read;
1052 idx = !idx;
1053 }
1054 } else {
1055 while (1) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001056 read_block(bytes_per_block, block[idx], &n_bytes_read);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001057 if (n_bytes_read < bytes_per_block)
1058 break;
1059 assert(n_bytes_read == bytes_per_block);
1060 write_block(current_offset, n_bytes_read,
1061 block[!idx], block[idx]);
1062 current_offset += n_bytes_read;
1063 idx = !idx;
1064 }
1065 }
1066
1067 if (n_bytes_read > 0) {
1068 int l_c_m;
1069 size_t bytes_to_write;
1070
1071 l_c_m = get_lcm();
1072
1073 /* Make bytes_to_write the smallest multiple of l_c_m that
1074 is at least as large as n_bytes_read. */
1075 bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m);
1076
1077 memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
1078 write_block(current_offset, bytes_to_write,
1079 block[!idx], block[idx]);
1080 current_offset += n_bytes_read;
1081 }
1082
1083 format_address(current_offset, '\n');
1084
1085 if (limit_bytes_to_format && current_offset >= end_offset)
Denis Vlasenko601ae132006-11-28 23:37:46 +00001086 check_and_close();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001087
1088 free(block[0]);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001089}
1090
Denis Vlasenko601ae132006-11-28 23:37:46 +00001091/* Read a single byte into *C from the concatenation of the input files
1092 named in the global array FILE_LIST. On the first call to this
1093 function, the global variable IN_STREAM is expected to be an open
1094 stream associated with the input file INPUT_FILENAME. If IN_STREAM
1095 is at end-of-file, close it and update the global variables IN_STREAM
1096 and INPUT_FILENAME so they correspond to the next file in the list.
1097 Then try to read a byte from the newly opened file. Repeat if
1098 necessary until EOF is reached for the last file in FILE_LIST, then
1099 set *C to EOF and return. Subsequent calls do likewise. The return
1100 value is nonzero if any errors occured, zero otherwise. */
1101
1102static void
1103read_char(int *c)
1104{
1105 while (in_stream) { /* !EOF */
1106 *c = fgetc(in_stream);
1107 if (*c != EOF)
1108 return;
1109 check_and_close();
1110 open_next_file();
1111 }
1112 *c = EOF;
1113}
1114
1115/* Read N bytes into BLOCK from the concatenation of the input files
1116 named in the global array FILE_LIST. On the first call to this
1117 function, the global variable IN_STREAM is expected to be an open
1118 stream associated with the input file INPUT_FILENAME. If all N
1119 bytes cannot be read from IN_STREAM, close IN_STREAM and update
1120 the global variables IN_STREAM and INPUT_FILENAME. Then try to
1121 read the remaining bytes from the newly opened file. Repeat if
1122 necessary until EOF is reached for the last file in FILE_LIST.
1123 On subsequent calls, don't modify BLOCK and return zero. Set
1124 *N_BYTES_IN_BUFFER to the number of bytes read. If an error occurs,
1125 it will be detected through ferror when the stream is about to be
1126 closed. If there is an error, give a message but continue reading
1127 as usual and return nonzero. Otherwise return zero. */
1128
Denis Vlasenko1114de72006-10-10 23:26:05 +00001129/* STRINGS mode. Find each "string constant" in the input.
1130 A string constant is a run of at least 'string_min' ASCII
1131 graphic (or formatting) characters terminated by a null.
1132 Based on a function written by Richard Stallman for a
1133 traditional version of od. Return nonzero if an error
1134 occurs. Otherwise, return zero. */
1135
Denis Vlasenko601ae132006-11-28 23:37:46 +00001136static void
Denis Vlasenko1114de72006-10-10 23:26:05 +00001137dump_strings(void)
1138{
1139 size_t bufsize = MAX(100, string_min);
1140 char *buf = xmalloc(bufsize);
Denis Vlasenko601ae132006-11-28 23:37:46 +00001141 off_t address = n_bytes_to_skip;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001142
Denis Vlasenko1114de72006-10-10 23:26:05 +00001143 while (1) {
1144 size_t i;
1145 int c;
1146
1147 /* See if the next 'string_min' chars are all printing chars. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001148 tryline:
1149 if (limit_bytes_to_format && (end_offset - string_min <= address))
Denis Vlasenko1114de72006-10-10 23:26:05 +00001150 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001151 i = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001152 while (!limit_bytes_to_format || address < end_offset) {
1153 if (i == bufsize) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001154 bufsize += bufsize/8;
1155 buf = xrealloc(buf, bufsize);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001156 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001157 read_char(&c);
1158 if (c < 0) { /* EOF */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001159 free(buf);
Denis Vlasenko601ae132006-11-28 23:37:46 +00001160 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001161 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001162 address++;
1163 if (!c)
1164 break;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001165 if (!ISPRINT(c))
1166 goto tryline; /* It isn't; give up on this string. */
1167 buf[i++] = c; /* String continues; store it all. */
1168 }
1169
Denis Vlasenko601ae132006-11-28 23:37:46 +00001170 if (i < string_min) /* Too short! */
1171 goto tryline;
1172
Denis Vlasenko1114de72006-10-10 23:26:05 +00001173 /* If we get here, the string is all printable and null-terminated,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001174 * so print it. It is all in 'buf' and 'i' is its length. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001175 buf[i] = 0;
1176 format_address(address - i - 1, ' ');
1177
1178 for (i = 0; (c = buf[i]); i++) {
1179 switch (c) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001180 case '\007': fputs("\\a", stdout); break;
1181 case '\b': fputs("\\b", stdout); break;
1182 case '\f': fputs("\\f", stdout); break;
1183 case '\n': fputs("\\n", stdout); break;
1184 case '\r': fputs("\\r", stdout); break;
1185 case '\t': fputs("\\t", stdout); break;
1186 case '\v': fputs("\\v", stdout); break;
1187 default: putc(c, stdout);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001188 }
1189 }
1190 putchar('\n');
1191 }
1192
1193 /* We reach this point only if we search through
1194 (max_bytes_to_format - string_min) bytes before reaching EOF. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001195 free(buf);
1196
Denis Vlasenko601ae132006-11-28 23:37:46 +00001197 check_and_close();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001198}
1199
Denis Vlasenko06af2162007-02-03 17:28:39 +00001200int od_main(int argc, char **argv);
1201int od_main(int argc, char **argv)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001202{
1203 static const struct suffix_mult bkm[] = {
1204 { "b", 512 },
1205 { "k", 1024 },
1206 { "m", 1024*1024 },
Denis Vlasenkof8689632007-07-27 15:06:25 +00001207 { }
Denis Vlasenko1114de72006-10-10 23:26:05 +00001208 };
Denis Vlasenko601ae132006-11-28 23:37:46 +00001209 unsigned opt;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001210 int l_c_m;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001211 /* The old-style 'pseudo starting address' to be printed in parentheses
1212 after any true address. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001213 off_t pseudo_start = 0; // only for gcc
1214 enum {
1215 OPT_A = 1 << 0,
1216 OPT_N = 1 << 1,
1217 OPT_a = 1 << 2,
1218 OPT_b = 1 << 3,
1219 OPT_c = 1 << 4,
1220 OPT_d = 1 << 5,
1221 OPT_f = 1 << 6,
1222 OPT_h = 1 << 7,
1223 OPT_i = 1 << 8,
1224 OPT_j = 1 << 9,
1225 OPT_l = 1 << 10,
1226 OPT_o = 1 << 11,
1227 OPT_t = 1 << 12,
1228 OPT_v = 1 << 13,
1229 OPT_x = 1 << 14,
1230 OPT_s = 1 << 15,
1231 OPT_S = 1 << 16,
1232 OPT_w = 1 << 17,
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001233 OPT_traditional = (1 << 18) * ENABLE_GETOPT_LONG,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001234 };
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001235#if ENABLE_GETOPT_LONG
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +00001236 static const char od_longopts[] =
1237 "skip-bytes\0" Required_argument "j"
1238 "address-radix\0" Required_argument "A"
1239 "read-bytes\0" Required_argument "N"
1240 "format\0" Required_argument "t"
1241 "output-duplicates\0" No_argument "v"
1242 "strings\0" Optional_argument "S"
1243 "width\0" Optional_argument "w"
1244 "traditional\0" No_argument "\xff"
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001245 ;
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001246#endif
Denis Vlasenko601ae132006-11-28 23:37:46 +00001247 char *str_A, *str_N, *str_j, *str_S;
1248 char *str_w = NULL;
1249 llist_t *lst_t = NULL;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001250
1251 spec = NULL;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001252 format_address = format_address_std;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001253 address_base_char = 'o';
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001254 address_pad_len_char = '7';
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +00001255 /* flag_dump_strings = 0; - already is */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001256
Denis Vlasenko601ae132006-11-28 23:37:46 +00001257 /* Parse command line */
1258 opt_complementary = "t::"; // list
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001259#if ENABLE_GETOPT_LONG
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +00001260 applet_long_options = od_longopts;
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001261#endif
Denis Vlasenko601ae132006-11-28 23:37:46 +00001262 opt = getopt32(argc, argv, "A:N:abcdfhij:lot:vxsS:"
1263 "w::", // -w with optional param
1264 // -S was -s and also had optional parameter
1265 // but in coreutils 6.3 it was renamed and now has
1266 // _mandatory_ parameter
1267 &str_A, &str_N, &str_j, &lst_t, &str_S, &str_w);
1268 argc -= optind;
1269 argv += optind;
1270 if (opt & OPT_A) {
1271 static const char doxn[] = "doxn";
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001272 static const char doxn_address_base_char[] = {
1273 'u', 'o', 'x', /* '?' fourth one is not important */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001274 };
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001275 static const uint8_t doxn_address_pad_len_char[] = {
1276 '7', '7', '6', /* '?' */
1277 };
Denis Vlasenko601ae132006-11-28 23:37:46 +00001278 char *p;
1279 int pos;
1280 p = strchr(doxn, str_A[0]);
1281 if (!p)
1282 bb_error_msg_and_die("bad output address radix "
1283 "'%c' (must be [doxn])", str_A[0]);
1284 pos = p - doxn;
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001285 if (pos == 3) format_address = format_address_none;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001286 address_base_char = doxn_address_base_char[pos];
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001287 address_pad_len_char = doxn_address_pad_len_char[pos];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001288 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001289 if (opt & OPT_N) {
1290 limit_bytes_to_format = 1;
1291 max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm);
1292 }
1293 if (opt & OPT_a) decode_format_string("a");
1294 if (opt & OPT_b) decode_format_string("oC");
1295 if (opt & OPT_c) decode_format_string("c");
1296 if (opt & OPT_d) decode_format_string("u2");
1297 if (opt & OPT_f) decode_format_string("fF");
1298 if (opt & OPT_h) decode_format_string("x2");
1299 if (opt & OPT_i) decode_format_string("d2");
1300 if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm);
1301 if (opt & OPT_l) decode_format_string("d4");
1302 if (opt & OPT_o) decode_format_string("o2");
1303 //if (opt & OPT_t)...
Denis Vlasenko601ae132006-11-28 23:37:46 +00001304 while (lst_t) {
1305 decode_format_string(lst_t->data);
1306 lst_t = lst_t->link;
1307 }
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +00001308 if (opt & OPT_v) verbose = 1;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001309 if (opt & OPT_x) decode_format_string("x2");
1310 if (opt & OPT_s) decode_format_string("d2");
1311 if (opt & OPT_S) {
1312 string_min = 3;
1313 string_min = xstrtou_sfx(str_S, 0, bkm);
1314 flag_dump_strings = 1;
1315 }
1316 //if (opt & OPT_w)...
1317 //if (opt & OPT_traditional)...
Denis Vlasenko1114de72006-10-10 23:26:05 +00001318
1319 if (flag_dump_strings && n_specs > 0)
1320 bb_error_msg_and_die("no type may be specified when dumping strings");
1321
Denis Vlasenko1114de72006-10-10 23:26:05 +00001322 /* If the --traditional option is used, there may be from
Denis Vlasenko601ae132006-11-28 23:37:46 +00001323 * 0 to 3 remaining command line arguments; handle each case
1324 * separately.
Denis Vlasenko8e858e22007-03-07 09:35:43 +00001325 * od [file] [[+]offset[.][b] [[+]label[.][b]]]
Denis Vlasenko601ae132006-11-28 23:37:46 +00001326 * The offset and pseudo_start have the same syntax.
1327 *
1328 * FIXME: POSIX 1003.1-2001 with XSI requires support for the
1329 * traditional syntax even if --traditional is not given. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001330
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001331#if ENABLE_GETOPT_LONG
Denis Vlasenko601ae132006-11-28 23:37:46 +00001332 if (opt & OPT_traditional) {
1333 off_t o1, o2;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001334
Denis Vlasenko601ae132006-11-28 23:37:46 +00001335 if (argc == 1) {
1336 if (parse_old_offset(argv[0], &o1)) {
1337 n_bytes_to_skip = o1;
1338 --argc;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001339 ++argv;
1340 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001341 } else if (argc == 2) {
1342 if (parse_old_offset(argv[0], &o1)
1343 && parse_old_offset(argv[1], &o2)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001344 ) {
1345 n_bytes_to_skip = o1;
1346 flag_pseudo_start = 1;
1347 pseudo_start = o2;
1348 argv += 2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001349 argc -= 2;
1350 } else if (parse_old_offset(argv[1], &o2)) {
Denis Vlasenko1114de72006-10-10 23:26:05 +00001351 n_bytes_to_skip = o2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001352 --argc;
1353 argv[1] = argv[0];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001354 ++argv;
1355 } else {
1356 bb_error_msg_and_die("invalid second operand "
Denis Vlasenko601ae132006-11-28 23:37:46 +00001357 "in compatibility mode '%s'", argv[1]);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001358 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001359 } else if (argc == 3) {
1360 if (parse_old_offset(argv[1], &o1)
1361 && parse_old_offset(argv[2], &o2)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001362 ) {
1363 n_bytes_to_skip = o1;
1364 flag_pseudo_start = 1;
1365 pseudo_start = o2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001366 argv[2] = argv[0];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001367 argv += 2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001368 argc -= 2;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001369 } else {
1370 bb_error_msg_and_die("in compatibility mode "
1371 "the last two arguments must be offsets");
1372 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001373 } else if (argc > 3) {
Denis Vlasenko1114de72006-10-10 23:26:05 +00001374 bb_error_msg_and_die("compatibility mode supports "
1375 "at most three arguments");
1376 }
1377
1378 if (flag_pseudo_start) {
1379 if (format_address == format_address_none) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001380 address_base_char = 'o';
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001381 address_pad_len_char = '7';
Denis Vlasenko1114de72006-10-10 23:26:05 +00001382 format_address = format_address_paren;
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001383 } else
1384 format_address = format_address_label;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001385 }
1386 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001387#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +00001388
1389 if (limit_bytes_to_format) {
1390 end_offset = n_bytes_to_skip + max_bytes_to_format;
1391 if (end_offset < n_bytes_to_skip)
1392 bb_error_msg_and_die("skip-bytes + read-bytes is too large");
1393 }
1394
1395 if (n_specs == 0) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001396 decode_format_string("o2");
Denis Vlasenko1114de72006-10-10 23:26:05 +00001397 n_specs = 1;
1398 }
1399
Denis Vlasenko601ae132006-11-28 23:37:46 +00001400 /* If no files were listed on the command line,
1401 set the global pointer FILE_LIST so that it
1402 references the null-terminated list of one name: "-". */
1403 file_list = default_file_list;
1404 if (argc > 0) {
Denis Vlasenko1114de72006-10-10 23:26:05 +00001405 /* Set the global pointer FILE_LIST so that it
1406 references the first file-argument on the command-line. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001407 file_list = (char const *const *) argv;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001408 }
1409
1410 /* open the first input file */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001411 open_next_file();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001412 /* skip over any unwanted header bytes */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001413 skip(n_bytes_to_skip);
1414 if (!in_stream)
1415 return 1;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001416
1417 pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0);
1418
1419 /* Compute output block length. */
1420 l_c_m = get_lcm();
1421
Denis Vlasenko601ae132006-11-28 23:37:46 +00001422 if (opt & OPT_w) { /* -w: width */
1423 bytes_per_block = 32;
1424 if (str_w)
1425 bytes_per_block = xatou(str_w);
1426 if (!bytes_per_block || bytes_per_block % l_c_m != 0) {
Mike Frysinger5dd76862006-12-31 11:34:17 +00001427 bb_error_msg("warning: invalid width %zu; using %d instead",
Denis Vlasenko601ae132006-11-28 23:37:46 +00001428 bytes_per_block, l_c_m);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001429 bytes_per_block = l_c_m;
1430 }
1431 } else {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001432 bytes_per_block = l_c_m;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001433 if (l_c_m < DEFAULT_BYTES_PER_BLOCK)
Denis Vlasenko601ae132006-11-28 23:37:46 +00001434 bytes_per_block *= DEFAULT_BYTES_PER_BLOCK / l_c_m;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001435 }
1436
1437#ifdef DEBUG
1438 for (i = 0; i < n_specs; i++) {
1439 printf("%d: fmt=\"%s\" width=%d\n",
1440 i, spec[i].fmt_string, width_bytes[spec[i].size]);
1441 }
1442#endif
1443
Denis Vlasenko601ae132006-11-28 23:37:46 +00001444 if (flag_dump_strings)
1445 dump_strings();
1446 else
1447 dump();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001448
Denis Vlasenko601ae132006-11-28 23:37:46 +00001449 if (fclose(stdin) == EOF)
1450 bb_perror_msg_and_die(bb_msg_standard_input);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001451
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +00001452 return ioerror;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001453}