blob: 501a92b383eb98adba60a32867b77ba4174fb91a [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 Vlasenko6ca409e2007-08-12 20:58:27 +0000132static const uint8_t bytes_to_oct_digits[] ALIGN1 =
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 Vlasenko6ca409e2007-08-12 20:58:27 +0000135static const uint8_t bytes_to_signed_dec_digits[] ALIGN1 =
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 Vlasenko6ca409e2007-08-12 20:58:27 +0000138static const uint8_t bytes_to_unsigned_dec_digits[] ALIGN1 =
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 Vlasenko6ca409e2007-08-12 20:58:27 +0000141static const uint8_t bytes_to_hex_digits[] ALIGN1 =
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. */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000145static const signed char width_bytes[] ALIGN1 = {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000146 -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. */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000158struct ERR_width_bytes_has_bad_size {
159 char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1];
Denis Vlasenko1114de72006-10-10 23:26:05 +0000160};
161
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000162static smallint flag_dump_strings;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000163/* Non-zero if an old-style 'pseudo-address' was specified. */
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000164static smallint flag_pseudo_start;
165static smallint limit_bytes_to_format;
166/* When zero and two or more consecutive blocks are equal, format
Denis Vlasenko1114de72006-10-10 23:26:05 +0000167 only the first block and output an asterisk alone on the following
168 line to indicate that identical blocks have been elided. */
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000169static smallint verbose;
170static smallint ioerror;
171
172static size_t string_min;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000173
174/* An array of specs describing how to format each input block. */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000175static size_t n_specs;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000176static struct tspec *spec;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000177
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000178/* Function that accepts an address and an optional following char,
179 and prints the address and char to stdout. */
180static void (*format_address)(off_t, char);
181/* The difference between the old-style pseudo starting address and
182 the number of bytes to skip. */
183static off_t pseudo_offset;
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000184/* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all
185 input is formatted. */
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000186
Denis Vlasenko1114de72006-10-10 23:26:05 +0000187/* The number of input bytes formatted per output line. It must be
188 a multiple of the least common multiple of the sizes associated with
189 the specified output types. It should be as large as possible, but
190 no larger than 16 -- unless specified with the -w option. */
191static size_t bytes_per_block;
192
Denis Vlasenko1114de72006-10-10 23:26:05 +0000193/* A NULL-terminated list of the file-arguments from the command line. */
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +0000194static const char *const *file_list;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000195
196/* The input stream associated with the current file. */
197static FILE *in_stream;
198
Denis Vlasenko1114de72006-10-10 23:26:05 +0000199#define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t)
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +0000200static const unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] ALIGN1 = {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000201 [sizeof(char)] = CHAR,
202#if USHRT_MAX != UCHAR_MAX
203 [sizeof(short)] = SHORT,
204#endif
205#if UINT_MAX != USHRT_MAX
206 [sizeof(int)] = INT,
207#endif
208#if ULONG_MAX != UINT_MAX
209 [sizeof(long)] = LONG,
210#endif
211#if ULLONG_MAX != ULONG_MAX
212 [sizeof(ulonglong_t)] = LONG_LONG,
213#endif
214};
Denis Vlasenko1114de72006-10-10 23:26:05 +0000215
216#define MAX_FP_TYPE_SIZE sizeof(longdouble_t)
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +0000217static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000218 /* gcc seems to allow repeated indexes. Last one stays */
219 [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE,
220 [sizeof(double)] = FLOAT_DOUBLE,
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000221 [sizeof(float)] = FLOAT_SINGLE
Denis Vlasenko1114de72006-10-10 23:26:05 +0000222};
223
224
225static unsigned
226gcd(unsigned u, unsigned v)
227{
228 unsigned t;
229 while (v != 0) {
230 t = u % v;
231 u = v;
232 v = t;
233 }
234 return u;
235}
236
237/* Compute the least common multiple of U and V. */
238static unsigned
239lcm(unsigned u, unsigned v) {
240 unsigned t = gcd(u, v);
241 if (t == 0)
242 return 0;
243 return u * v / t;
244}
245
246static void
247print_s_char(size_t n_bytes, const char *block, const char *fmt_string)
248{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000249 while (n_bytes--) {
250 int tmp = *(signed char *) block;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000251 printf(fmt_string, tmp);
252 block += sizeof(unsigned char);
253 }
254}
255
256static void
257print_char(size_t n_bytes, const char *block, const char *fmt_string)
258{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000259 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000260 unsigned tmp = *(unsigned char *) block;
261 printf(fmt_string, tmp);
262 block += sizeof(unsigned char);
263 }
264}
265
266static void
267print_s_short(size_t n_bytes, const char *block, const char *fmt_string)
268{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000269 n_bytes /= sizeof(signed short);
270 while (n_bytes--) {
271 int tmp = *(signed short *) block;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000272 printf(fmt_string, tmp);
273 block += sizeof(unsigned short);
274 }
275}
276
277static void
278print_short(size_t n_bytes, const char *block, const char *fmt_string)
279{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000280 n_bytes /= sizeof(unsigned short);
281 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000282 unsigned tmp = *(unsigned short *) block;
283 printf(fmt_string, tmp);
284 block += sizeof(unsigned short);
285 }
286}
287
288static void
289print_int(size_t n_bytes, const char *block, const char *fmt_string)
290{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000291 n_bytes /= sizeof(unsigned);
292 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000293 unsigned tmp = *(unsigned *) block;
294 printf(fmt_string, tmp);
295 block += sizeof(unsigned);
296 }
297}
298
Denis Vlasenko601ae132006-11-28 23:37:46 +0000299#if UINT_MAX == ULONG_MAX
300# define print_long print_int
301#else
Denis Vlasenko1114de72006-10-10 23:26:05 +0000302static void
303print_long(size_t n_bytes, const char *block, const char *fmt_string)
304{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000305 n_bytes /= sizeof(unsigned long);
306 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000307 unsigned long tmp = *(unsigned long *) block;
308 printf(fmt_string, tmp);
309 block += sizeof(unsigned long);
310 }
311}
Denis Vlasenko601ae132006-11-28 23:37:46 +0000312#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +0000313
Denis Vlasenko601ae132006-11-28 23:37:46 +0000314#if ULONG_MAX == ULLONG_MAX
315# define print_long_long print_long
316#else
Denis Vlasenko1114de72006-10-10 23:26:05 +0000317static void
318print_long_long(size_t n_bytes, const char *block, const char *fmt_string)
319{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000320 n_bytes /= sizeof(ulonglong_t);
321 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000322 ulonglong_t tmp = *(ulonglong_t *) block;
323 printf(fmt_string, tmp);
324 block += sizeof(ulonglong_t);
325 }
326}
Denis Vlasenko601ae132006-11-28 23:37:46 +0000327#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +0000328
329static void
330print_float(size_t n_bytes, const char *block, const char *fmt_string)
331{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000332 n_bytes /= sizeof(float);
333 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000334 float tmp = *(float *) block;
335 printf(fmt_string, tmp);
336 block += sizeof(float);
337 }
338}
339
340static void
341print_double(size_t n_bytes, const char *block, const char *fmt_string)
342{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000343 n_bytes /= sizeof(double);
344 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000345 double tmp = *(double *) block;
346 printf(fmt_string, tmp);
347 block += sizeof(double);
348 }
349}
350
351static void
352print_long_double(size_t n_bytes, const char *block, const char *fmt_string)
353{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000354 n_bytes /= sizeof(longdouble_t);
355 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000356 longdouble_t tmp = *(longdouble_t *) block;
357 printf(fmt_string, tmp);
358 block += sizeof(longdouble_t);
359 }
360}
361
Denis Vlasenko601ae132006-11-28 23:37:46 +0000362/* print_[named]_ascii are optimized for speed.
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +0000363 * Remember, someday you may want to pump gigabytes through this thing.
Denis Vlasenko601ae132006-11-28 23:37:46 +0000364 * Saving a dozen of .text bytes here is counter-productive */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000365
366static void
367print_named_ascii(size_t n_bytes, const char *block,
368 const char *unused_fmt_string ATTRIBUTE_UNUSED)
369{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000370 /* Names for some non-printing characters. */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000371 static const char charname[33][3] ALIGN1 = {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000372 "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
373 " bs", " ht", " nl", " vt", " ff", " cr", " so", " si",
374 "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
375 "can", " em", "sub", "esc", " fs", " gs", " rs", " us",
376 " sp"
377 };
378 // buf[N] pos: 01234 56789
379 char buf[12] = " x\0 0xx\0";
380 // actually " x\0 xxx\0", but I want to share the string with below.
381 // [12] because we take three 32bit stack slots anyway, and
382 // gcc is too dumb to initialize with constant stores,
383 // it copies initializer from rodata. Oh well.
Denis Vlasenko1114de72006-10-10 23:26:05 +0000384
Denis Vlasenko601ae132006-11-28 23:37:46 +0000385 while (n_bytes--) {
386 unsigned masked_c = *(unsigned char *) block++;
387
388 masked_c &= 0x7f;
389 if (masked_c == 0x7f) {
390 fputs(" del", stdout);
391 continue;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000392 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000393 if (masked_c > ' ') {
394 buf[3] = masked_c;
395 fputs(buf, stdout);
396 continue;
397 }
398 /* Why? Because printf(" %3.3s") is much slower... */
399 buf[6] = charname[masked_c][0];
400 buf[7] = charname[masked_c][1];
401 buf[8] = charname[masked_c][2];
402 fputs(buf+5, stdout);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000403 }
404}
405
406static void
407print_ascii(size_t n_bytes, const char *block,
408 const char *unused_fmt_string ATTRIBUTE_UNUSED)
409{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000410 // buf[N] pos: 01234 56789
411 char buf[12] = " x\0 0xx\0";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000412
Denis Vlasenko601ae132006-11-28 23:37:46 +0000413 while (n_bytes--) {
414 const char *s;
415 unsigned c = *(unsigned char *) block++;
416
417 if (ISPRINT(c)) {
418 buf[3] = c;
419 fputs(buf, stdout);
420 continue;
421 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000422 switch (c) {
423 case '\0':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000424 s = " \\0";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000425 break;
426 case '\007':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000427 s = " \\a";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000428 break;
429 case '\b':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000430 s = " \\b";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000431 break;
432 case '\f':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000433 s = " \\f";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000434 break;
435 case '\n':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000436 s = " \\n";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000437 break;
438 case '\r':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000439 s = " \\r";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000440 break;
441 case '\t':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000442 s = " \\t";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000443 break;
444 case '\v':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000445 s = " \\v";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000446 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000447 case '\x7f':
448 s = " 177";
449 break;
450 default: /* c is never larger than 040 */
451 buf[7] = (c >> 3) + '0';
452 buf[8] = (c & 7) + '0';
453 s = buf + 5;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000454 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000455 fputs(s, stdout);
456 }
457}
Denis Vlasenko1114de72006-10-10 23:26:05 +0000458
Denis Vlasenko601ae132006-11-28 23:37:46 +0000459/* Given a list of one or more input filenames FILE_LIST, set the global
460 file pointer IN_STREAM and the global string INPUT_FILENAME to the
461 first one that can be successfully opened. Modify FILE_LIST to
462 reference the next filename in the list. A file name of "-" is
463 interpreted as standard input. If any file open fails, give an error
464 message and return nonzero. */
465
466static void
467open_next_file(void)
468{
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000469 while (1) {
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +0000470 if (!*file_list)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000471 return;
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +0000472 in_stream = fopen_or_warn_stdin(*file_list++);
Denis Vlasenko601ae132006-11-28 23:37:46 +0000473 if (in_stream) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000474 break;
475 }
476 ioerror = 1;
477 }
478
479 if (limit_bytes_to_format && !flag_dump_strings)
480 setbuf(in_stream, NULL);
481}
482
483/* Test whether there have been errors on in_stream, and close it if
484 it is not standard input. Return nonzero if there has been an error
485 on in_stream or stdout; return zero otherwise. This function will
486 report more than one error only if both a read and a write error
487 have occurred. IN_ERRNO, if nonzero, is the error number
488 corresponding to the most recent action for IN_STREAM. */
489
490static void
491check_and_close(void)
492{
493 if (in_stream) {
494 if (ferror(in_stream)) {
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +0000495 bb_error_msg("%s: read error", (in_stream == stdin)
496 ? bb_msg_standard_input
497 : file_list[-1]
498 );
Denis Vlasenko601ae132006-11-28 23:37:46 +0000499 ioerror = 1;
500 }
501 fclose_if_not_stdin(in_stream);
502 in_stream = NULL;
503 }
504
505 if (ferror(stdout)) {
506 bb_error_msg("write error");
507 ioerror = 1;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000508 }
509}
510
511/* If S points to a single valid modern od format string, put
512 a description of that format in *TSPEC, make *NEXT point at the
513 character following the just-decoded format (if *NEXT is non-NULL),
Denis Vlasenko601ae132006-11-28 23:37:46 +0000514 and return zero. For example, if S were "d4afL"
515 *NEXT would be set to "afL" and *TSPEC would be
Denis Vlasenko1114de72006-10-10 23:26:05 +0000516 {
517 fmt = SIGNED_DECIMAL;
518 size = INT or LONG; (whichever integral_type_size[4] resolves to)
519 print_function = print_int; (assuming size == INT)
Denis Vlasenko8e858e22007-03-07 09:35:43 +0000520 fmt_string = "%011d%c";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000521 }
522 S_ORIG is solely for reporting errors. It should be the full format
Denis Vlasenko601ae132006-11-28 23:37:46 +0000523 string argument. */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000524
Denis Vlasenko601ae132006-11-28 23:37:46 +0000525static void
Denis Vlasenko1114de72006-10-10 23:26:05 +0000526decode_one_format(const char *s_orig, const char *s, const char **next,
527 struct tspec *tspec)
528{
529 enum size_spec size_spec;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000530 unsigned size;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000531 enum output_format fmt;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000532 const char *p;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000533 char *end;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000534 char *fmt_string = NULL;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000535 void (*print_function) (size_t, const char *, const char *);
536 unsigned c;
537 unsigned field_width = 0;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000538 int pos;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000539
540 assert(tspec != NULL);
541
542 switch (*s) {
543 case 'd':
544 case 'o':
545 case 'u':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000546 case 'x': {
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000547 static const char CSIL[] ALIGN1 = "CSIL";
Denis Vlasenko601ae132006-11-28 23:37:46 +0000548
549 c = *s++;
550 p = strchr(CSIL, *s);
551 if (!p) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000552 size = sizeof(int);
553 if (isdigit(s[0])) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000554 size = bb_strtou(s, &end, 0);
555 if (errno == ERANGE
556 || MAX_INTEGRAL_TYPE_SIZE < size
Denis Vlasenko1114de72006-10-10 23:26:05 +0000557 || integral_type_size[size] == NO_SIZE
558 ) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000559 bb_error_msg_and_die("invalid type string '%s'; "
560 "%u-byte %s type is not supported",
561 s_orig, size, "integral");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000562 }
563 s = end;
564 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000565 } else {
566 static const uint8_t CSIL_sizeof[] = {
567 sizeof(char),
568 sizeof(short),
569 sizeof(int),
570 sizeof(long),
571 };
572 size = CSIL_sizeof[p - CSIL];
Denis Vlasenko1114de72006-10-10 23:26:05 +0000573 }
574
575#define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \
576 ((Spec) == LONG_LONG ? (Max_format) \
577 : ((Spec) == LONG ? (Long_format) : (Min_format)))
578
579#define FMT_BYTES_ALLOCATED 9
Denis Vlasenko1114de72006-10-10 23:26:05 +0000580 size_spec = integral_type_size[size];
581
Denis Vlasenko601ae132006-11-28 23:37:46 +0000582 {
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000583 static const char doux[] ALIGN1 = "doux";
Denis Vlasenko601ae132006-11-28 23:37:46 +0000584 static const char doux_fmt_letter[][4] = {
585 "lld", "llo", "llu", "llx"
586 };
587 static const enum output_format doux_fmt[] = {
588 SIGNED_DECIMAL,
589 OCTAL,
590 UNSIGNED_DECIMAL,
591 HEXADECIMAL,
592 };
593 static const uint8_t *const doux_bytes_to_XXX[] = {
594 bytes_to_signed_dec_digits,
595 bytes_to_oct_digits,
596 bytes_to_unsigned_dec_digits,
597 bytes_to_hex_digits,
598 };
599 static const char doux_fmtstring[][sizeof(" %%0%u%s")] = {
600 " %%%u%s",
601 " %%0%u%s",
602 " %%%u%s",
603 " %%0%u%s",
604 };
Denis Vlasenko1114de72006-10-10 23:26:05 +0000605
Denis Vlasenko601ae132006-11-28 23:37:46 +0000606 pos = strchr(doux, c) - doux;
607 fmt = doux_fmt[pos];
608 field_width = doux_bytes_to_XXX[pos][size];
609 p = doux_fmt_letter[pos] + 2;
610 if (size_spec == LONG) p--;
611 if (size_spec == LONG_LONG) p -= 2;
612 fmt_string = xasprintf(doux_fmtstring[pos], field_width, p);
613 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000614
615 switch (size_spec) {
616 case CHAR:
617 print_function = (fmt == SIGNED_DECIMAL
618 ? print_s_char
619 : print_char);
620 break;
621 case SHORT:
622 print_function = (fmt == SIGNED_DECIMAL
623 ? print_s_short
624 : print_short);
625 break;
626 case INT:
627 print_function = print_int;
628 break;
629 case LONG:
630 print_function = print_long;
631 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000632 default: /* case LONG_LONG: */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000633 print_function = print_long_long;
634 break;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000635 }
636 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000637 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000638
Denis Vlasenko601ae132006-11-28 23:37:46 +0000639 case 'f': {
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000640 static const char FDL[] ALIGN1 = "FDL";
Denis Vlasenko601ae132006-11-28 23:37:46 +0000641
Denis Vlasenko1114de72006-10-10 23:26:05 +0000642 fmt = FLOATING_POINT;
643 ++s;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000644 p = strchr(FDL, *s);
645 if (!p) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000646 size = sizeof(double);
647 if (isdigit(s[0])) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000648 size = bb_strtou(s, &end, 0);
649 if (errno == ERANGE || size > MAX_FP_TYPE_SIZE
Denis Vlasenko1114de72006-10-10 23:26:05 +0000650 || fp_type_size[size] == NO_SIZE
651 ) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000652 bb_error_msg_and_die("invalid type string '%s'; "
653 "%u-byte %s type is not supported",
654 s_orig, size, "floating point");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000655 }
656 s = end;
657 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000658 } else {
659 static const uint8_t FDL_sizeof[] = {
660 sizeof(float),
661 sizeof(double),
662 sizeof(longdouble_t),
663 };
664
665 size = FDL_sizeof[p - FDL];
Denis Vlasenko1114de72006-10-10 23:26:05 +0000666 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000667
Denis Vlasenko1114de72006-10-10 23:26:05 +0000668 size_spec = fp_type_size[size];
669
670 switch (size_spec) {
671 case FLOAT_SINGLE:
672 print_function = print_float;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000673 field_width = FLT_DIG + 8;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000674 /* Don't use %#e; not all systems support it. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000675 fmt_string = xasprintf(" %%%d.%de", field_width, FLT_DIG);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000676 break;
677 case FLOAT_DOUBLE:
678 print_function = print_double;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000679 field_width = DBL_DIG + 8;
680 fmt_string = xasprintf(" %%%d.%de", field_width, DBL_DIG);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000681 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000682 default: /* case FLOAT_LONG_DOUBLE: */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000683 print_function = print_long_double;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000684 field_width = LDBL_DIG + 8;
685 fmt_string = xasprintf(" %%%d.%dLe", field_width, LDBL_DIG);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000686 break;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000687 }
688 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000689 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000690
691 case 'a':
692 ++s;
693 fmt = NAMED_CHARACTER;
694 size_spec = CHAR;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000695 print_function = print_named_ascii;
696 field_width = 3;
697 break;
698 case 'c':
699 ++s;
700 fmt = CHARACTER;
701 size_spec = CHAR;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000702 print_function = print_ascii;
703 field_width = 3;
704 break;
705 default:
Denis Vlasenko601ae132006-11-28 23:37:46 +0000706 bb_error_msg_and_die("invalid character '%c' "
707 "in type string '%s'", *s, s_orig);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000708 }
709
710 tspec->size = size_spec;
711 tspec->fmt = fmt;
712 tspec->print_function = print_function;
713 tspec->fmt_string = fmt_string;
714
715 tspec->field_width = field_width;
716 tspec->hexl_mode_trailer = (*s == 'z');
717 if (tspec->hexl_mode_trailer)
718 s++;
719
720 if (next != NULL)
721 *next = s;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000722}
723
724/* Decode the modern od format string S. Append the decoded
725 representation to the global array SPEC, reallocating SPEC if
Denis Vlasenko97bd0e02008-02-08 15:41:01 +0000726 necessary. */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000727
Denis Vlasenko601ae132006-11-28 23:37:46 +0000728static void
Denis Vlasenko1114de72006-10-10 23:26:05 +0000729decode_format_string(const char *s)
730{
731 const char *s_orig = s;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000732
733 while (*s != '\0') {
734 struct tspec tspec;
735 const char *next;
736
Denis Vlasenko601ae132006-11-28 23:37:46 +0000737 decode_one_format(s_orig, s, &next, &tspec);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000738
739 assert(s != next);
740 s = next;
741 n_specs++;
742 spec = xrealloc(spec, n_specs * sizeof(*spec));
743 memcpy(&spec[n_specs-1], &tspec, sizeof *spec);
744 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000745}
746
747/* Given a list of one or more input filenames FILE_LIST, set the global
748 file pointer IN_STREAM to position N_SKIP in the concatenation of
749 those files. If any file operation fails or if there are fewer than
750 N_SKIP bytes in the combined input, give an error message and return
751 nonzero. When possible, use seek rather than read operations to
752 advance IN_STREAM. */
753
Denis Vlasenko601ae132006-11-28 23:37:46 +0000754static void
755skip(off_t n_skip)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000756{
Denis Vlasenko1114de72006-10-10 23:26:05 +0000757 if (n_skip == 0)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000758 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000759
Denis Vlasenko601ae132006-11-28 23:37:46 +0000760 while (in_stream) { /* !EOF */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000761 struct stat file_stats;
762
763 /* First try seeking. For large offsets, this extra work is
764 worthwhile. If the offset is below some threshold it may be
765 more efficient to move the pointer by reading. There are two
766 issues when trying to seek:
767 - the file must be seekable.
768 - before seeking to the specified position, make sure
769 that the new position is in the current file.
770 Try to do that by getting file's size using fstat.
771 But that will work only for regular files. */
772
Denis Vlasenko1114de72006-10-10 23:26:05 +0000773 /* The st_size field is valid only for regular files
774 (and for symbolic links, which cannot occur here).
775 If the number of bytes left to skip is at least
776 as large as the size of the current file, we can
777 decrement n_skip and go on to the next file. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000778 if (fstat(fileno(in_stream), &file_stats) == 0
Denis Vlasenko97bd0e02008-02-08 15:41:01 +0000779 && S_ISREG(file_stats.st_mode) && file_stats.st_size > 0
Denis Vlasenko601ae132006-11-28 23:37:46 +0000780 ) {
781 if (file_stats.st_size < n_skip) {
782 n_skip -= file_stats.st_size;
Denis Vlasenko97bd0e02008-02-08 15:41:01 +0000783 /* take "check & close / open_next" route */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000784 } else {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000785 if (fseeko(in_stream, n_skip, SEEK_CUR) != 0)
786 ioerror = 1;
787 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000788 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000789 } else {
Denis Vlasenko97bd0e02008-02-08 15:41:01 +0000790 /* If it's not a regular file with positive size,
Denis Vlasenko601ae132006-11-28 23:37:46 +0000791 position the file pointer by reading. */
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +0000792 char buf[1024];
793 size_t n_bytes_to_read = 1024;
794 size_t n_bytes_read;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000795
Denis Vlasenko601ae132006-11-28 23:37:46 +0000796 while (n_skip > 0) {
797 if (n_skip < n_bytes_to_read)
798 n_bytes_to_read = n_skip;
799 n_bytes_read = fread(buf, 1, n_bytes_to_read, in_stream);
800 n_skip -= n_bytes_read;
801 if (n_bytes_read != n_bytes_to_read)
Denis Vlasenko8e858e22007-03-07 09:35:43 +0000802 break; /* EOF on this file or error */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000803 }
804 }
805 if (n_skip == 0)
806 return;
807
808 check_and_close();
809 open_next_file();
Denis Vlasenko1114de72006-10-10 23:26:05 +0000810 }
811
Denis Vlasenko601ae132006-11-28 23:37:46 +0000812 if (n_skip)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000813 bb_error_msg_and_die("cannot skip past end of combined input");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000814}
815
Denis Vlasenko601ae132006-11-28 23:37:46 +0000816
817typedef void FN_format_address(off_t address, char c);
818
Denis Vlasenko1114de72006-10-10 23:26:05 +0000819static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000820format_address_none(off_t address ATTRIBUTE_UNUSED, char c ATTRIBUTE_UNUSED)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000821{
822}
823
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000824static char address_fmt[] ALIGN1 = "%0n"OFF_FMT"xc";
Denis Vlasenko601ae132006-11-28 23:37:46 +0000825/* Corresponds to 'x' above */
826#define address_base_char address_fmt[sizeof(address_fmt)-3]
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000827/* Corresponds to 'n' above */
828#define address_pad_len_char address_fmt[2]
Denis Vlasenko601ae132006-11-28 23:37:46 +0000829
Denis Vlasenko1114de72006-10-10 23:26:05 +0000830static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000831format_address_std(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000832{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000833 /* Corresponds to 'c' */
834 address_fmt[sizeof(address_fmt)-2] = c;
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000835 printf(address_fmt, address);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000836}
837
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000838#if ENABLE_GETOPT_LONG
839/* only used with --traditional */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000840static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000841format_address_paren(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000842{
843 putchar('(');
844 format_address_std(address, ')');
Denis Vlasenko0f5905e2006-12-17 19:21:13 +0000845 if (c) putchar(c);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000846}
847
848static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000849format_address_label(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000850{
851 format_address_std(address, ' ');
852 format_address_paren(address + pseudo_offset, c);
853}
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000854#endif
Denis Vlasenko601ae132006-11-28 23:37:46 +0000855
856static void
857dump_hexl_mode_trailer(size_t n_bytes, const char *block)
858{
859 fputs(" >", stdout);
860 while (n_bytes--) {
861 unsigned c = *(unsigned char *) block++;
862 c = (ISPRINT(c) ? c : '.');
863 putchar(c);
864 }
865 putchar('<');
866}
867
Denis Vlasenko1114de72006-10-10 23:26:05 +0000868/* Write N_BYTES bytes from CURR_BLOCK to standard output once for each
869 of the N_SPEC format specs. CURRENT_OFFSET is the byte address of
870 CURR_BLOCK in the concatenation of input files, and it is printed
871 (optionally) only before the output line associated with the first
872 format spec. When duplicate blocks are being abbreviated, the output
873 for a sequence of identical input blocks is the output for the first
874 block followed by an asterisk alone on a line. It is valid to compare
875 the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK.
876 That condition may be false only for the last input block -- and then
877 only when it has not been padded to length BYTES_PER_BLOCK. */
878
879static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000880write_block(off_t current_offset, size_t n_bytes,
Denis Vlasenko1114de72006-10-10 23:26:05 +0000881 const char *prev_block, const char *curr_block)
882{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000883 static char first = 1;
884 static char prev_pair_equal = 0;
885 size_t i;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000886
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000887 if (!verbose && !first
Denis Vlasenko1114de72006-10-10 23:26:05 +0000888 && n_bytes == bytes_per_block
889 && memcmp(prev_block, curr_block, bytes_per_block) == 0
890 ) {
891 if (prev_pair_equal) {
892 /* The two preceding blocks were equal, and the current
893 block is the same as the last one, so print nothing. */
894 } else {
895 puts("*");
896 prev_pair_equal = 1;
897 }
898 } else {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000899 first = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000900 prev_pair_equal = 0;
901 for (i = 0; i < n_specs; i++) {
902 if (i == 0)
903 format_address(current_offset, '\0');
904 else
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000905 printf("%*s", address_pad_len_char - '0', "");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000906 (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string);
907 if (spec[i].hexl_mode_trailer) {
908 /* space-pad out to full line width, then dump the trailer */
909 int datum_width = width_bytes[spec[i].size];
910 int blank_fields = (bytes_per_block - n_bytes) / datum_width;
911 int field_width = spec[i].field_width + 1;
912 printf("%*s", blank_fields * field_width, "");
913 dump_hexl_mode_trailer(n_bytes, curr_block);
914 }
915 putchar('\n');
916 }
917 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000918}
919
Denis Vlasenko601ae132006-11-28 23:37:46 +0000920static void
Denis Vlasenko1114de72006-10-10 23:26:05 +0000921read_block(size_t n, char *block, size_t *n_bytes_in_buffer)
922{
Denis Vlasenko1114de72006-10-10 23:26:05 +0000923 assert(0 < n && n <= bytes_per_block);
924
925 *n_bytes_in_buffer = 0;
926
927 if (n == 0)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000928 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000929
930 while (in_stream != NULL) { /* EOF. */
931 size_t n_needed;
932 size_t n_read;
933
934 n_needed = n - *n_bytes_in_buffer;
935 n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, in_stream);
936 *n_bytes_in_buffer += n_read;
937 if (n_read == n_needed)
938 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000939 /* error check is done in check_and_close */
940 check_and_close();
941 open_next_file();
Denis Vlasenko1114de72006-10-10 23:26:05 +0000942 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000943}
944
945/* Return the least common multiple of the sizes associated
946 with the format specs. */
947
948static int
949get_lcm(void)
950{
951 size_t i;
952 int l_c_m = 1;
953
954 for (i = 0; i < n_specs; i++)
955 l_c_m = lcm(l_c_m, width_bytes[(int) spec[i].size]);
956 return l_c_m;
957}
958
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000959#if ENABLE_GETOPT_LONG
Denis Vlasenko1114de72006-10-10 23:26:05 +0000960/* If S is a valid traditional offset specification with an optional
961 leading '+' return nonzero and set *OFFSET to the offset it denotes. */
962
963static int
Denis Vlasenko601ae132006-11-28 23:37:46 +0000964parse_old_offset(const char *s, off_t *offset)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000965{
966 static const struct suffix_mult Bb[] = {
967 { "B", 1024 },
968 { "b", 512 },
Denis Vlasenkof8689632007-07-27 15:06:25 +0000969 { }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000970 };
Denis Vlasenko601ae132006-11-28 23:37:46 +0000971 char *p;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000972 int radix;
973
Denis Vlasenko1114de72006-10-10 23:26:05 +0000974 /* Skip over any leading '+'. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000975 if (s[0] == '+') ++s;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000976
977 /* Determine the radix we'll use to interpret S. If there is a '.',
Denis Vlasenko601ae132006-11-28 23:37:46 +0000978 * it's decimal, otherwise, if the string begins with '0X'or '0x',
979 * it's hexadecimal, else octal. */
980 p = strchr(s, '.');
981 radix = 8;
982 if (p) {
983 p[0] = '\0'; /* cheating */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000984 radix = 10;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000985 } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
986 radix = 16;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000987
Denis Vlasenko601ae132006-11-28 23:37:46 +0000988 *offset = xstrtooff_sfx(s, radix, Bb);
989 if (p) p[0] = '.';
990
991 return (*offset >= 0);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000992}
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000993#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +0000994
995/* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
996 formatted block to standard output, and repeat until the specified
997 maximum number of bytes has been read or until all input has been
998 processed. If the last block read is smaller than BYTES_PER_BLOCK
999 and its size is not a multiple of the size associated with a format
1000 spec, extend the input block with zero bytes until its length is a
1001 multiple of all format spec sizes. Write the final block. Finally,
1002 write on a line by itself the offset of the byte after the last byte
Denis Vlasenko97bd0e02008-02-08 15:41:01 +00001003 read. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001004
Denis Vlasenko601ae132006-11-28 23:37:46 +00001005static void
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001006dump(off_t current_offset, off_t end_offset)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001007{
1008 char *block[2];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001009 int idx;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001010 size_t n_bytes_read;
1011
1012 block[0] = xmalloc(2*bytes_per_block);
1013 block[1] = block[0] + bytes_per_block;
1014
Denis Vlasenko1114de72006-10-10 23:26:05 +00001015 idx = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001016 if (limit_bytes_to_format) {
1017 while (1) {
1018 size_t n_needed;
1019 if (current_offset >= end_offset) {
1020 n_bytes_read = 0;
1021 break;
1022 }
1023 n_needed = MIN(end_offset - current_offset,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001024 (off_t) bytes_per_block);
1025 read_block(n_needed, block[idx], &n_bytes_read);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001026 if (n_bytes_read < bytes_per_block)
1027 break;
1028 assert(n_bytes_read == bytes_per_block);
1029 write_block(current_offset, n_bytes_read,
1030 block[!idx], block[idx]);
1031 current_offset += n_bytes_read;
1032 idx = !idx;
1033 }
1034 } else {
1035 while (1) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001036 read_block(bytes_per_block, block[idx], &n_bytes_read);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001037 if (n_bytes_read < bytes_per_block)
1038 break;
1039 assert(n_bytes_read == bytes_per_block);
1040 write_block(current_offset, n_bytes_read,
1041 block[!idx], block[idx]);
1042 current_offset += n_bytes_read;
1043 idx = !idx;
1044 }
1045 }
1046
1047 if (n_bytes_read > 0) {
1048 int l_c_m;
1049 size_t bytes_to_write;
1050
1051 l_c_m = get_lcm();
1052
1053 /* Make bytes_to_write the smallest multiple of l_c_m that
1054 is at least as large as n_bytes_read. */
1055 bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m);
1056
1057 memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
1058 write_block(current_offset, bytes_to_write,
1059 block[!idx], block[idx]);
1060 current_offset += n_bytes_read;
1061 }
1062
1063 format_address(current_offset, '\n');
1064
1065 if (limit_bytes_to_format && current_offset >= end_offset)
Denis Vlasenko601ae132006-11-28 23:37:46 +00001066 check_and_close();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001067
1068 free(block[0]);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001069}
1070
Denis Vlasenko601ae132006-11-28 23:37:46 +00001071/* Read a single byte into *C from the concatenation of the input files
1072 named in the global array FILE_LIST. On the first call to this
1073 function, the global variable IN_STREAM is expected to be an open
1074 stream associated with the input file INPUT_FILENAME. If IN_STREAM
1075 is at end-of-file, close it and update the global variables IN_STREAM
1076 and INPUT_FILENAME so they correspond to the next file in the list.
1077 Then try to read a byte from the newly opened file. Repeat if
1078 necessary until EOF is reached for the last file in FILE_LIST, then
Denis Vlasenko97bd0e02008-02-08 15:41:01 +00001079 set *C to EOF and return. Subsequent calls do likewise. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001080
1081static void
1082read_char(int *c)
1083{
1084 while (in_stream) { /* !EOF */
1085 *c = fgetc(in_stream);
1086 if (*c != EOF)
1087 return;
1088 check_and_close();
1089 open_next_file();
1090 }
1091 *c = EOF;
1092}
1093
1094/* Read N bytes into BLOCK from the concatenation of the input files
1095 named in the global array FILE_LIST. On the first call to this
1096 function, the global variable IN_STREAM is expected to be an open
1097 stream associated with the input file INPUT_FILENAME. If all N
1098 bytes cannot be read from IN_STREAM, close IN_STREAM and update
1099 the global variables IN_STREAM and INPUT_FILENAME. Then try to
1100 read the remaining bytes from the newly opened file. Repeat if
1101 necessary until EOF is reached for the last file in FILE_LIST.
1102 On subsequent calls, don't modify BLOCK and return zero. Set
1103 *N_BYTES_IN_BUFFER to the number of bytes read. If an error occurs,
1104 it will be detected through ferror when the stream is about to be
1105 closed. If there is an error, give a message but continue reading
1106 as usual and return nonzero. Otherwise return zero. */
1107
Denis Vlasenko1114de72006-10-10 23:26:05 +00001108/* STRINGS mode. Find each "string constant" in the input.
1109 A string constant is a run of at least 'string_min' ASCII
1110 graphic (or formatting) characters terminated by a null.
1111 Based on a function written by Richard Stallman for a
Denis Vlasenko97bd0e02008-02-08 15:41:01 +00001112 traditional version of od. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001113
Denis Vlasenko601ae132006-11-28 23:37:46 +00001114static void
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001115dump_strings(off_t address, off_t end_offset)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001116{
1117 size_t bufsize = MAX(100, string_min);
1118 char *buf = xmalloc(bufsize);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001119
Denis Vlasenko1114de72006-10-10 23:26:05 +00001120 while (1) {
1121 size_t i;
1122 int c;
1123
1124 /* See if the next 'string_min' chars are all printing chars. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001125 tryline:
1126 if (limit_bytes_to_format && (end_offset - string_min <= address))
Denis Vlasenko1114de72006-10-10 23:26:05 +00001127 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001128 i = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001129 while (!limit_bytes_to_format || address < end_offset) {
1130 if (i == bufsize) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001131 bufsize += bufsize/8;
1132 buf = xrealloc(buf, bufsize);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001133 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001134 read_char(&c);
1135 if (c < 0) { /* EOF */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001136 free(buf);
Denis Vlasenko601ae132006-11-28 23:37:46 +00001137 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001138 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001139 address++;
1140 if (!c)
1141 break;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001142 if (!ISPRINT(c))
1143 goto tryline; /* It isn't; give up on this string. */
1144 buf[i++] = c; /* String continues; store it all. */
1145 }
1146
Denis Vlasenko601ae132006-11-28 23:37:46 +00001147 if (i < string_min) /* Too short! */
1148 goto tryline;
1149
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001150 /* If we get here, the string is all printable and NUL-terminated,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001151 * so print it. It is all in 'buf' and 'i' is its length. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001152 buf[i] = 0;
1153 format_address(address - i - 1, ' ');
1154
1155 for (i = 0; (c = buf[i]); i++) {
1156 switch (c) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001157 case '\007': fputs("\\a", stdout); break;
1158 case '\b': fputs("\\b", stdout); break;
1159 case '\f': fputs("\\f", stdout); break;
1160 case '\n': fputs("\\n", stdout); break;
1161 case '\r': fputs("\\r", stdout); break;
1162 case '\t': fputs("\\t", stdout); break;
1163 case '\v': fputs("\\v", stdout); break;
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001164 default: putchar(c);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001165 }
1166 }
1167 putchar('\n');
1168 }
1169
1170 /* We reach this point only if we search through
1171 (max_bytes_to_format - string_min) bytes before reaching EOF. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001172 free(buf);
1173
Denis Vlasenko601ae132006-11-28 23:37:46 +00001174 check_and_close();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001175}
1176
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001177int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko06af2162007-02-03 17:28:39 +00001178int od_main(int argc, char **argv)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001179{
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001180 static char const *const default_file_list[] = { "-", NULL };
Denis Vlasenko1114de72006-10-10 23:26:05 +00001181 static const struct suffix_mult bkm[] = {
1182 { "b", 512 },
1183 { "k", 1024 },
1184 { "m", 1024*1024 },
Denis Vlasenkof8689632007-07-27 15:06:25 +00001185 { }
Denis Vlasenko1114de72006-10-10 23:26:05 +00001186 };
Denis Vlasenko601ae132006-11-28 23:37:46 +00001187 enum {
1188 OPT_A = 1 << 0,
1189 OPT_N = 1 << 1,
1190 OPT_a = 1 << 2,
1191 OPT_b = 1 << 3,
1192 OPT_c = 1 << 4,
1193 OPT_d = 1 << 5,
1194 OPT_f = 1 << 6,
1195 OPT_h = 1 << 7,
1196 OPT_i = 1 << 8,
1197 OPT_j = 1 << 9,
1198 OPT_l = 1 << 10,
1199 OPT_o = 1 << 11,
1200 OPT_t = 1 << 12,
1201 OPT_v = 1 << 13,
1202 OPT_x = 1 << 14,
1203 OPT_s = 1 << 15,
1204 OPT_S = 1 << 16,
1205 OPT_w = 1 << 17,
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001206 OPT_traditional = (1 << 18) * ENABLE_GETOPT_LONG,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001207 };
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001208#if ENABLE_GETOPT_LONG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001209 static const char od_longopts[] ALIGN1 =
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +00001210 "skip-bytes\0" Required_argument "j"
1211 "address-radix\0" Required_argument "A"
1212 "read-bytes\0" Required_argument "N"
1213 "format\0" Required_argument "t"
1214 "output-duplicates\0" No_argument "v"
1215 "strings\0" Optional_argument "S"
1216 "width\0" Optional_argument "w"
1217 "traditional\0" No_argument "\xff"
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001218 ;
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001219#endif
Denis Vlasenko601ae132006-11-28 23:37:46 +00001220 char *str_A, *str_N, *str_j, *str_S;
1221 char *str_w = NULL;
1222 llist_t *lst_t = NULL;
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001223 unsigned opt;
1224 int l_c_m;
1225 /* The old-style 'pseudo starting address' to be printed in parentheses
1226 after any true address. */
1227 off_t pseudo_start = pseudo_start; // for gcc
1228 /* The number of input bytes to skip before formatting and writing. */
1229 off_t n_bytes_to_skip = 0;
1230 /* The offset of the first byte after the last byte to be formatted. */
1231 off_t end_offset = 0;
1232 /* The maximum number of bytes that will be formatted. */
1233 off_t max_bytes_to_format = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001234
1235 spec = NULL;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001236 format_address = format_address_std;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001237 address_base_char = 'o';
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001238 address_pad_len_char = '7';
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +00001239 /* flag_dump_strings = 0; - already is */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001240
Denis Vlasenko601ae132006-11-28 23:37:46 +00001241 /* Parse command line */
1242 opt_complementary = "t::"; // list
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001243#if ENABLE_GETOPT_LONG
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +00001244 applet_long_options = od_longopts;
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001245#endif
Denis Vlasenkofe7cd642007-08-18 15:32:12 +00001246 opt = getopt32(argv, "A:N:abcdfhij:lot:vxsS:"
Denis Vlasenko601ae132006-11-28 23:37:46 +00001247 "w::", // -w with optional param
1248 // -S was -s and also had optional parameter
1249 // but in coreutils 6.3 it was renamed and now has
1250 // _mandatory_ parameter
1251 &str_A, &str_N, &str_j, &lst_t, &str_S, &str_w);
1252 argc -= optind;
1253 argv += optind;
1254 if (opt & OPT_A) {
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001255 static const char doxn[] ALIGN1 = "doxn";
1256 static const char doxn_address_base_char[] ALIGN1 = {
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001257 'u', 'o', 'x', /* '?' fourth one is not important */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001258 };
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001259 static const uint8_t doxn_address_pad_len_char[] ALIGN1 = {
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001260 '7', '7', '6', /* '?' */
1261 };
Denis Vlasenko601ae132006-11-28 23:37:46 +00001262 char *p;
1263 int pos;
1264 p = strchr(doxn, str_A[0]);
1265 if (!p)
1266 bb_error_msg_and_die("bad output address radix "
1267 "'%c' (must be [doxn])", str_A[0]);
1268 pos = p - doxn;
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001269 if (pos == 3) format_address = format_address_none;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001270 address_base_char = doxn_address_base_char[pos];
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001271 address_pad_len_char = doxn_address_pad_len_char[pos];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001272 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001273 if (opt & OPT_N) {
1274 limit_bytes_to_format = 1;
1275 max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm);
1276 }
1277 if (opt & OPT_a) decode_format_string("a");
1278 if (opt & OPT_b) decode_format_string("oC");
1279 if (opt & OPT_c) decode_format_string("c");
1280 if (opt & OPT_d) decode_format_string("u2");
1281 if (opt & OPT_f) decode_format_string("fF");
1282 if (opt & OPT_h) decode_format_string("x2");
1283 if (opt & OPT_i) decode_format_string("d2");
1284 if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm);
1285 if (opt & OPT_l) decode_format_string("d4");
1286 if (opt & OPT_o) decode_format_string("o2");
1287 //if (opt & OPT_t)...
Denis Vlasenko601ae132006-11-28 23:37:46 +00001288 while (lst_t) {
1289 decode_format_string(lst_t->data);
1290 lst_t = lst_t->link;
1291 }
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +00001292 if (opt & OPT_v) verbose = 1;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001293 if (opt & OPT_x) decode_format_string("x2");
1294 if (opt & OPT_s) decode_format_string("d2");
1295 if (opt & OPT_S) {
1296 string_min = 3;
1297 string_min = xstrtou_sfx(str_S, 0, bkm);
1298 flag_dump_strings = 1;
1299 }
1300 //if (opt & OPT_w)...
1301 //if (opt & OPT_traditional)...
Denis Vlasenko1114de72006-10-10 23:26:05 +00001302
1303 if (flag_dump_strings && n_specs > 0)
1304 bb_error_msg_and_die("no type may be specified when dumping strings");
1305
Denis Vlasenko1114de72006-10-10 23:26:05 +00001306 /* If the --traditional option is used, there may be from
Denis Vlasenko601ae132006-11-28 23:37:46 +00001307 * 0 to 3 remaining command line arguments; handle each case
1308 * separately.
Denis Vlasenko8e858e22007-03-07 09:35:43 +00001309 * od [file] [[+]offset[.][b] [[+]label[.][b]]]
Denis Vlasenko601ae132006-11-28 23:37:46 +00001310 * The offset and pseudo_start have the same syntax.
1311 *
1312 * FIXME: POSIX 1003.1-2001 with XSI requires support for the
1313 * traditional syntax even if --traditional is not given. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001314
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001315#if ENABLE_GETOPT_LONG
Denis Vlasenko601ae132006-11-28 23:37:46 +00001316 if (opt & OPT_traditional) {
1317 off_t o1, o2;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001318
Denis Vlasenko601ae132006-11-28 23:37:46 +00001319 if (argc == 1) {
1320 if (parse_old_offset(argv[0], &o1)) {
1321 n_bytes_to_skip = o1;
1322 --argc;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001323 ++argv;
1324 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001325 } else if (argc == 2) {
1326 if (parse_old_offset(argv[0], &o1)
1327 && parse_old_offset(argv[1], &o2)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001328 ) {
1329 n_bytes_to_skip = o1;
1330 flag_pseudo_start = 1;
1331 pseudo_start = o2;
1332 argv += 2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001333 argc -= 2;
1334 } else if (parse_old_offset(argv[1], &o2)) {
Denis Vlasenko1114de72006-10-10 23:26:05 +00001335 n_bytes_to_skip = o2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001336 --argc;
1337 argv[1] = argv[0];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001338 ++argv;
1339 } else {
1340 bb_error_msg_and_die("invalid second operand "
Denis Vlasenko601ae132006-11-28 23:37:46 +00001341 "in compatibility mode '%s'", argv[1]);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001342 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001343 } else if (argc == 3) {
1344 if (parse_old_offset(argv[1], &o1)
1345 && parse_old_offset(argv[2], &o2)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001346 ) {
1347 n_bytes_to_skip = o1;
1348 flag_pseudo_start = 1;
1349 pseudo_start = o2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001350 argv[2] = argv[0];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001351 argv += 2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001352 argc -= 2;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001353 } else {
1354 bb_error_msg_and_die("in compatibility mode "
1355 "the last two arguments must be offsets");
1356 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001357 } else if (argc > 3) {
Denis Vlasenko1114de72006-10-10 23:26:05 +00001358 bb_error_msg_and_die("compatibility mode supports "
1359 "at most three arguments");
1360 }
1361
1362 if (flag_pseudo_start) {
1363 if (format_address == format_address_none) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001364 address_base_char = 'o';
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001365 address_pad_len_char = '7';
Denis Vlasenko1114de72006-10-10 23:26:05 +00001366 format_address = format_address_paren;
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001367 } else
1368 format_address = format_address_label;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001369 }
1370 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001371#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +00001372
1373 if (limit_bytes_to_format) {
1374 end_offset = n_bytes_to_skip + max_bytes_to_format;
1375 if (end_offset < n_bytes_to_skip)
1376 bb_error_msg_and_die("skip-bytes + read-bytes is too large");
1377 }
1378
1379 if (n_specs == 0) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001380 decode_format_string("o2");
Denis Vlasenko1114de72006-10-10 23:26:05 +00001381 n_specs = 1;
1382 }
1383
Denis Vlasenko601ae132006-11-28 23:37:46 +00001384 /* If no files were listed on the command line,
1385 set the global pointer FILE_LIST so that it
1386 references the null-terminated list of one name: "-". */
1387 file_list = default_file_list;
1388 if (argc > 0) {
Denis Vlasenko1114de72006-10-10 23:26:05 +00001389 /* Set the global pointer FILE_LIST so that it
1390 references the first file-argument on the command-line. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001391 file_list = (char const *const *) argv;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001392 }
1393
1394 /* open the first input file */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001395 open_next_file();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001396 /* skip over any unwanted header bytes */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001397 skip(n_bytes_to_skip);
1398 if (!in_stream)
Bernhard Reutner-Fischer7c2db5c2007-11-16 12:39:16 +00001399 return EXIT_FAILURE;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001400
1401 pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0);
1402
1403 /* Compute output block length. */
1404 l_c_m = get_lcm();
1405
Denis Vlasenko601ae132006-11-28 23:37:46 +00001406 if (opt & OPT_w) { /* -w: width */
1407 bytes_per_block = 32;
1408 if (str_w)
1409 bytes_per_block = xatou(str_w);
1410 if (!bytes_per_block || bytes_per_block % l_c_m != 0) {
Denis Vlasenko97bd0e02008-02-08 15:41:01 +00001411 bb_error_msg("warning: invalid width %u; using %d instead",
1412 (unsigned)bytes_per_block, l_c_m);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001413 bytes_per_block = l_c_m;
1414 }
1415 } else {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001416 bytes_per_block = l_c_m;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001417 if (l_c_m < DEFAULT_BYTES_PER_BLOCK)
Denis Vlasenko601ae132006-11-28 23:37:46 +00001418 bytes_per_block *= DEFAULT_BYTES_PER_BLOCK / l_c_m;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001419 }
1420
1421#ifdef DEBUG
1422 for (i = 0; i < n_specs; i++) {
1423 printf("%d: fmt=\"%s\" width=%d\n",
1424 i, spec[i].fmt_string, width_bytes[spec[i].size]);
1425 }
1426#endif
1427
Denis Vlasenko601ae132006-11-28 23:37:46 +00001428 if (flag_dump_strings)
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001429 dump_strings(n_bytes_to_skip, end_offset);
Denis Vlasenko601ae132006-11-28 23:37:46 +00001430 else
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001431 dump(n_bytes_to_skip, end_offset);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001432
Denis Vlasenko601ae132006-11-28 23:37:46 +00001433 if (fclose(stdin) == EOF)
1434 bb_perror_msg_and_die(bb_msg_standard_input);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001435
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +00001436 return ioerror;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001437}