blob: a9a45c8d3abf72ac4227edc392509b3eaa5991a4 [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
Denis Vlasenkod18f52b2008-03-02 12:53:15 +000020/* Busyboxed by Denys 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
54#define assert(a) ((void)0)
Denis Vlasenko601ae132006-11-28 23:37:46 +000055
56/* Check for 0x7f is a coreutils 6.3 addition */
57#define ISPRINT(c) (((c)>=' ') && (c) != 0x7f)
Denis Vlasenko1114de72006-10-10 23:26:05 +000058
59typedef long double longdouble_t;
60typedef unsigned long long ulonglong_t;
Denis Vlasenko601ae132006-11-28 23:37:46 +000061typedef long long llong;
62
63#if ENABLE_LFS
64# define xstrtooff_sfx xstrtoull_sfx
65#else
66# define xstrtooff_sfx xstrtoul_sfx
67#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +000068
69/* The default number of input bytes per output line. */
70#define DEFAULT_BYTES_PER_BLOCK 16
71
72/* The number of decimal digits of precision in a float. */
73#ifndef FLT_DIG
74# define FLT_DIG 7
75#endif
76
77/* The number of decimal digits of precision in a double. */
78#ifndef DBL_DIG
79# define DBL_DIG 15
80#endif
81
82/* The number of decimal digits of precision in a long double. */
83#ifndef LDBL_DIG
84# define LDBL_DIG DBL_DIG
85#endif
86
87enum size_spec {
88 NO_SIZE,
89 CHAR,
90 SHORT,
91 INT,
92 LONG,
93 LONG_LONG,
Denis Vlasenko1114de72006-10-10 23:26:05 +000094 FLOAT_SINGLE,
95 FLOAT_DOUBLE,
96 FLOAT_LONG_DOUBLE,
97 N_SIZE_SPECS
98};
99
100enum output_format {
101 SIGNED_DECIMAL,
102 UNSIGNED_DECIMAL,
103 OCTAL,
104 HEXADECIMAL,
105 FLOATING_POINT,
106 NAMED_CHARACTER,
107 CHARACTER
108};
109
110/* Each output format specification (from '-t spec' or from
111 old-style options) is represented by one of these structures. */
112struct tspec {
113 enum output_format fmt;
114 enum size_spec size;
115 void (*print_function) (size_t, const char *, const char *);
116 char *fmt_string;
117 int hexl_mode_trailer;
118 int field_width;
119};
120
121/* Convert the number of 8-bit bytes of a binary representation to
122 the number of characters (digits + sign if the type is signed)
123 required to represent the same quantity in the specified base/type.
124 For example, a 32-bit (4-byte) quantity may require a field width
125 as wide as the following for these types:
126 11 unsigned octal
127 11 signed decimal
128 10 unsigned decimal
129 8 unsigned hexadecimal */
130
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000131static const uint8_t bytes_to_oct_digits[] ALIGN1 =
Denis Vlasenko1114de72006-10-10 23:26:05 +0000132{0, 3, 6, 8, 11, 14, 16, 19, 22, 25, 27, 30, 32, 35, 38, 41, 43};
133
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000134static const uint8_t bytes_to_signed_dec_digits[] ALIGN1 =
Denis Vlasenko1114de72006-10-10 23:26:05 +0000135{1, 4, 6, 8, 11, 13, 16, 18, 20, 23, 25, 28, 30, 33, 35, 37, 40};
136
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000137static const uint8_t bytes_to_unsigned_dec_digits[] ALIGN1 =
Denis Vlasenko1114de72006-10-10 23:26:05 +0000138{0, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39};
139
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000140static const uint8_t bytes_to_hex_digits[] ALIGN1 =
Denis Vlasenko1114de72006-10-10 23:26:05 +0000141{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32};
142
143/* Convert enum size_spec to the size of the named type. */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000144static const signed char width_bytes[] ALIGN1 = {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000145 -1,
146 sizeof(char),
147 sizeof(short),
148 sizeof(int),
149 sizeof(long),
150 sizeof(ulonglong_t),
151 sizeof(float),
152 sizeof(double),
153 sizeof(longdouble_t)
154};
Denis Vlasenko1114de72006-10-10 23:26:05 +0000155/* Ensure that for each member of 'enum size_spec' there is an
156 initializer in the width_bytes array. */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000157struct ERR_width_bytes_has_bad_size {
158 char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1];
Denis Vlasenko1114de72006-10-10 23:26:05 +0000159};
160
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000161static smallint flag_dump_strings;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000162/* Non-zero if an old-style 'pseudo-address' was specified. */
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000163static smallint flag_pseudo_start;
164static smallint limit_bytes_to_format;
165/* When zero and two or more consecutive blocks are equal, format
Denis Vlasenko1114de72006-10-10 23:26:05 +0000166 only the first block and output an asterisk alone on the following
167 line to indicate that identical blocks have been elided. */
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000168static smallint verbose;
169static smallint ioerror;
170
171static size_t string_min;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000172
173/* An array of specs describing how to format each input block. */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000174static size_t n_specs;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000175static struct tspec *spec;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000176
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000177/* Function that accepts an address and an optional following char,
178 and prints the address and char to stdout. */
179static void (*format_address)(off_t, char);
180/* The difference between the old-style pseudo starting address and
181 the number of bytes to skip. */
182static off_t pseudo_offset;
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000183/* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all
184 input is formatted. */
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000185
Denis Vlasenko1114de72006-10-10 23:26:05 +0000186/* The number of input bytes formatted per output line. It must be
187 a multiple of the least common multiple of the sizes associated with
188 the specified output types. It should be as large as possible, but
189 no larger than 16 -- unless specified with the -w option. */
Denis Vlasenko1d426652008-03-17 09:09:09 +0000190static unsigned bytes_per_block = 32; /* have to use unsigned, not size_t */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000191
Denis Vlasenko1114de72006-10-10 23:26:05 +0000192/* A NULL-terminated list of the file-arguments from the command line. */
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +0000193static const char *const *file_list;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000194
195/* The input stream associated with the current file. */
196static FILE *in_stream;
197
Denis Vlasenko1114de72006-10-10 23:26:05 +0000198#define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t)
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +0000199static const unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] ALIGN1 = {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000200 [sizeof(char)] = CHAR,
201#if USHRT_MAX != UCHAR_MAX
202 [sizeof(short)] = SHORT,
203#endif
204#if UINT_MAX != USHRT_MAX
205 [sizeof(int)] = INT,
206#endif
207#if ULONG_MAX != UINT_MAX
208 [sizeof(long)] = LONG,
209#endif
210#if ULLONG_MAX != ULONG_MAX
211 [sizeof(ulonglong_t)] = LONG_LONG,
212#endif
213};
Denis Vlasenko1114de72006-10-10 23:26:05 +0000214
215#define MAX_FP_TYPE_SIZE sizeof(longdouble_t)
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +0000216static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000217 /* gcc seems to allow repeated indexes. Last one stays */
218 [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE,
219 [sizeof(double)] = FLOAT_DOUBLE,
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000220 [sizeof(float)] = FLOAT_SINGLE
Denis Vlasenko1114de72006-10-10 23:26:05 +0000221};
222
223
224static unsigned
225gcd(unsigned u, unsigned v)
226{
227 unsigned t;
228 while (v != 0) {
229 t = u % v;
230 u = v;
231 v = t;
232 }
233 return u;
234}
235
236/* Compute the least common multiple of U and V. */
237static unsigned
238lcm(unsigned u, unsigned v) {
239 unsigned t = gcd(u, v);
240 if (t == 0)
241 return 0;
242 return u * v / t;
243}
244
245static void
246print_s_char(size_t n_bytes, const char *block, const char *fmt_string)
247{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000248 while (n_bytes--) {
249 int tmp = *(signed char *) block;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000250 printf(fmt_string, tmp);
251 block += sizeof(unsigned char);
252 }
253}
254
255static void
256print_char(size_t n_bytes, const char *block, const char *fmt_string)
257{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000258 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000259 unsigned tmp = *(unsigned char *) block;
260 printf(fmt_string, tmp);
261 block += sizeof(unsigned char);
262 }
263}
264
265static void
266print_s_short(size_t n_bytes, const char *block, const char *fmt_string)
267{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000268 n_bytes /= sizeof(signed short);
269 while (n_bytes--) {
270 int tmp = *(signed short *) block;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000271 printf(fmt_string, tmp);
272 block += sizeof(unsigned short);
273 }
274}
275
276static void
277print_short(size_t n_bytes, const char *block, const char *fmt_string)
278{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000279 n_bytes /= sizeof(unsigned short);
280 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000281 unsigned tmp = *(unsigned short *) block;
282 printf(fmt_string, tmp);
283 block += sizeof(unsigned short);
284 }
285}
286
287static void
288print_int(size_t n_bytes, const char *block, const char *fmt_string)
289{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000290 n_bytes /= sizeof(unsigned);
291 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000292 unsigned tmp = *(unsigned *) block;
293 printf(fmt_string, tmp);
294 block += sizeof(unsigned);
295 }
296}
297
Denis Vlasenko601ae132006-11-28 23:37:46 +0000298#if UINT_MAX == ULONG_MAX
299# define print_long print_int
300#else
Denis Vlasenko1114de72006-10-10 23:26:05 +0000301static void
302print_long(size_t n_bytes, const char *block, const char *fmt_string)
303{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000304 n_bytes /= sizeof(unsigned long);
305 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000306 unsigned long tmp = *(unsigned long *) block;
307 printf(fmt_string, tmp);
308 block += sizeof(unsigned long);
309 }
310}
Denis Vlasenko601ae132006-11-28 23:37:46 +0000311#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +0000312
Denis Vlasenko601ae132006-11-28 23:37:46 +0000313#if ULONG_MAX == ULLONG_MAX
314# define print_long_long print_long
315#else
Denis Vlasenko1114de72006-10-10 23:26:05 +0000316static void
317print_long_long(size_t n_bytes, const char *block, const char *fmt_string)
318{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000319 n_bytes /= sizeof(ulonglong_t);
320 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000321 ulonglong_t tmp = *(ulonglong_t *) block;
322 printf(fmt_string, tmp);
323 block += sizeof(ulonglong_t);
324 }
325}
Denis Vlasenko601ae132006-11-28 23:37:46 +0000326#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +0000327
328static void
329print_float(size_t n_bytes, const char *block, const char *fmt_string)
330{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000331 n_bytes /= sizeof(float);
332 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000333 float tmp = *(float *) block;
334 printf(fmt_string, tmp);
335 block += sizeof(float);
336 }
337}
338
339static void
340print_double(size_t n_bytes, const char *block, const char *fmt_string)
341{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000342 n_bytes /= sizeof(double);
343 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000344 double tmp = *(double *) block;
345 printf(fmt_string, tmp);
346 block += sizeof(double);
347 }
348}
349
350static void
351print_long_double(size_t n_bytes, const char *block, const char *fmt_string)
352{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000353 n_bytes /= sizeof(longdouble_t);
354 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000355 longdouble_t tmp = *(longdouble_t *) block;
356 printf(fmt_string, tmp);
357 block += sizeof(longdouble_t);
358 }
359}
360
Denis Vlasenko601ae132006-11-28 23:37:46 +0000361/* print_[named]_ascii are optimized for speed.
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +0000362 * Remember, someday you may want to pump gigabytes through this thing.
Denis Vlasenko601ae132006-11-28 23:37:46 +0000363 * Saving a dozen of .text bytes here is counter-productive */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000364
365static void
366print_named_ascii(size_t n_bytes, const char *block,
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000367 const char *unused_fmt_string UNUSED_PARAM)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000368{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000369 /* Names for some non-printing characters. */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000370 static const char charname[33][3] ALIGN1 = {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000371 "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
372 " bs", " ht", " nl", " vt", " ff", " cr", " so", " si",
373 "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
374 "can", " em", "sub", "esc", " fs", " gs", " rs", " us",
375 " sp"
376 };
377 // buf[N] pos: 01234 56789
378 char buf[12] = " x\0 0xx\0";
379 // actually " x\0 xxx\0", but I want to share the string with below.
380 // [12] because we take three 32bit stack slots anyway, and
381 // gcc is too dumb to initialize with constant stores,
382 // it copies initializer from rodata. Oh well.
Denis Vlasenko1114de72006-10-10 23:26:05 +0000383
Denis Vlasenko601ae132006-11-28 23:37:46 +0000384 while (n_bytes--) {
385 unsigned masked_c = *(unsigned char *) block++;
386
387 masked_c &= 0x7f;
388 if (masked_c == 0x7f) {
389 fputs(" del", stdout);
390 continue;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000391 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000392 if (masked_c > ' ') {
393 buf[3] = masked_c;
394 fputs(buf, stdout);
395 continue;
396 }
397 /* Why? Because printf(" %3.3s") is much slower... */
398 buf[6] = charname[masked_c][0];
399 buf[7] = charname[masked_c][1];
400 buf[8] = charname[masked_c][2];
401 fputs(buf+5, stdout);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000402 }
403}
404
405static void
406print_ascii(size_t n_bytes, const char *block,
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000407 const char *unused_fmt_string UNUSED_PARAM)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000408{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000409 // buf[N] pos: 01234 56789
410 char buf[12] = " x\0 0xx\0";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000411
Denis Vlasenko601ae132006-11-28 23:37:46 +0000412 while (n_bytes--) {
413 const char *s;
414 unsigned c = *(unsigned char *) block++;
415
416 if (ISPRINT(c)) {
417 buf[3] = c;
418 fputs(buf, stdout);
419 continue;
420 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000421 switch (c) {
422 case '\0':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000423 s = " \\0";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000424 break;
425 case '\007':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000426 s = " \\a";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000427 break;
428 case '\b':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000429 s = " \\b";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000430 break;
431 case '\f':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000432 s = " \\f";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000433 break;
434 case '\n':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000435 s = " \\n";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000436 break;
437 case '\r':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000438 s = " \\r";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000439 break;
440 case '\t':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000441 s = " \\t";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000442 break;
443 case '\v':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000444 s = " \\v";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000445 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000446 case '\x7f':
447 s = " 177";
448 break;
449 default: /* c is never larger than 040 */
450 buf[7] = (c >> 3) + '0';
451 buf[8] = (c & 7) + '0';
452 s = buf + 5;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000453 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000454 fputs(s, stdout);
455 }
456}
Denis Vlasenko1114de72006-10-10 23:26:05 +0000457
Denis Vlasenko601ae132006-11-28 23:37:46 +0000458/* Given a list of one or more input filenames FILE_LIST, set the global
459 file pointer IN_STREAM and the global string INPUT_FILENAME to the
460 first one that can be successfully opened. Modify FILE_LIST to
461 reference the next filename in the list. A file name of "-" is
462 interpreted as standard input. If any file open fails, give an error
463 message and return nonzero. */
464
465static void
466open_next_file(void)
467{
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000468 while (1) {
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +0000469 if (!*file_list)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000470 return;
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +0000471 in_stream = fopen_or_warn_stdin(*file_list++);
Denis Vlasenko601ae132006-11-28 23:37:46 +0000472 if (in_stream) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000473 break;
474 }
475 ioerror = 1;
476 }
477
478 if (limit_bytes_to_format && !flag_dump_strings)
479 setbuf(in_stream, NULL);
480}
481
482/* Test whether there have been errors on in_stream, and close it if
483 it is not standard input. Return nonzero if there has been an error
484 on in_stream or stdout; return zero otherwise. This function will
485 report more than one error only if both a read and a write error
486 have occurred. IN_ERRNO, if nonzero, is the error number
487 corresponding to the most recent action for IN_STREAM. */
488
489static void
490check_and_close(void)
491{
492 if (in_stream) {
493 if (ferror(in_stream)) {
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +0000494 bb_error_msg("%s: read error", (in_stream == stdin)
495 ? bb_msg_standard_input
496 : file_list[-1]
497 );
Denis Vlasenko601ae132006-11-28 23:37:46 +0000498 ioerror = 1;
499 }
500 fclose_if_not_stdin(in_stream);
501 in_stream = NULL;
502 }
503
504 if (ferror(stdout)) {
Denys Vlasenkoea8b2522010-06-02 12:57:26 +0200505 bb_error_msg(bb_msg_write_error);
Denis Vlasenko601ae132006-11-28 23:37:46 +0000506 ioerror = 1;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000507 }
508}
509
510/* If S points to a single valid modern od format string, put
Denis Vlasenko7089c312008-04-14 19:50:06 +0000511 a description of that format in *TSPEC, return pointer to
512 character following the just-decoded format.
513 For example, if S were "d4afL", we will return a rtp to "afL"
514 and *TSPEC would be
Denis Vlasenko1114de72006-10-10 23:26:05 +0000515 {
516 fmt = SIGNED_DECIMAL;
517 size = INT or LONG; (whichever integral_type_size[4] resolves to)
518 print_function = print_int; (assuming size == INT)
Denis Vlasenko8e858e22007-03-07 09:35:43 +0000519 fmt_string = "%011d%c";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000520 }
521 S_ORIG is solely for reporting errors. It should be the full format
Denis Vlasenko601ae132006-11-28 23:37:46 +0000522 string argument. */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000523
Denys Vlasenkob9542cb2010-06-01 23:16:46 +0200524static NOINLINE const char *
Denis Vlasenko7089c312008-04-14 19:50:06 +0000525decode_one_format(const char *s_orig, const char *s, struct tspec *tspec)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000526{
527 enum size_spec size_spec;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000528 unsigned size;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000529 enum output_format fmt;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000530 const char *p;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000531 char *end;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000532 char *fmt_string = NULL;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000533 void (*print_function) (size_t, const char *, const char *);
534 unsigned c;
535 unsigned field_width = 0;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000536 int pos;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000537
Denis Vlasenko1114de72006-10-10 23:26:05 +0000538
539 switch (*s) {
540 case 'd':
541 case 'o':
542 case 'u':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000543 case 'x': {
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000544 static const char CSIL[] ALIGN1 = "CSIL";
Denis Vlasenko601ae132006-11-28 23:37:46 +0000545
546 c = *s++;
547 p = strchr(CSIL, *s);
Denys Vlasenko11f3a8b2009-11-22 15:37:16 +0100548 /* if *s == NUL, p != NULL! Testcase: "od -tx" */
549 if (!p || *p == '\0') {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000550 size = sizeof(int);
551 if (isdigit(s[0])) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000552 size = bb_strtou(s, &end, 0);
553 if (errno == ERANGE
554 || MAX_INTEGRAL_TYPE_SIZE < size
Denis Vlasenko1114de72006-10-10 23:26:05 +0000555 || integral_type_size[size] == NO_SIZE
556 ) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000557 bb_error_msg_and_die("invalid type string '%s'; "
558 "%u-byte %s type is not supported",
559 s_orig, size, "integral");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000560 }
561 s = end;
562 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000563 } else {
Denis Vlasenko7089c312008-04-14 19:50:06 +0000564 static const uint8_t CSIL_sizeof[4] = {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000565 sizeof(char),
566 sizeof(short),
567 sizeof(int),
568 sizeof(long),
569 };
570 size = CSIL_sizeof[p - CSIL];
Denis Vlasenko7089c312008-04-14 19:50:06 +0000571 s++; /* skip C/S/I/L */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000572 }
573
574#define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \
575 ((Spec) == LONG_LONG ? (Max_format) \
576 : ((Spec) == LONG ? (Long_format) : (Min_format)))
577
578#define FMT_BYTES_ALLOCATED 9
Denis Vlasenko1114de72006-10-10 23:26:05 +0000579 size_spec = integral_type_size[size];
580
Denis Vlasenko601ae132006-11-28 23:37:46 +0000581 {
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000582 static const char doux[] ALIGN1 = "doux";
Denis Vlasenko601ae132006-11-28 23:37:46 +0000583 static const char doux_fmt_letter[][4] = {
584 "lld", "llo", "llu", "llx"
585 };
586 static const enum output_format doux_fmt[] = {
587 SIGNED_DECIMAL,
588 OCTAL,
589 UNSIGNED_DECIMAL,
590 HEXADECIMAL,
591 };
592 static const uint8_t *const doux_bytes_to_XXX[] = {
593 bytes_to_signed_dec_digits,
594 bytes_to_oct_digits,
595 bytes_to_unsigned_dec_digits,
596 bytes_to_hex_digits,
597 };
598 static const char doux_fmtstring[][sizeof(" %%0%u%s")] = {
599 " %%%u%s",
600 " %%0%u%s",
601 " %%%u%s",
602 " %%0%u%s",
603 };
Denis Vlasenko1114de72006-10-10 23:26:05 +0000604
Denis Vlasenko601ae132006-11-28 23:37:46 +0000605 pos = strchr(doux, c) - doux;
606 fmt = doux_fmt[pos];
607 field_width = doux_bytes_to_XXX[pos][size];
608 p = doux_fmt_letter[pos] + 2;
609 if (size_spec == LONG) p--;
610 if (size_spec == LONG_LONG) p -= 2;
611 fmt_string = xasprintf(doux_fmtstring[pos], field_width, p);
612 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000613
614 switch (size_spec) {
615 case CHAR:
616 print_function = (fmt == SIGNED_DECIMAL
617 ? print_s_char
618 : print_char);
619 break;
620 case SHORT:
621 print_function = (fmt == SIGNED_DECIMAL
622 ? print_s_short
623 : print_short);
624 break;
625 case INT:
626 print_function = print_int;
627 break;
628 case LONG:
629 print_function = print_long;
630 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000631 default: /* case LONG_LONG: */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000632 print_function = print_long_long;
633 break;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000634 }
635 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000636 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000637
Denis Vlasenko601ae132006-11-28 23:37:46 +0000638 case 'f': {
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000639 static const char FDL[] ALIGN1 = "FDL";
Denis Vlasenko601ae132006-11-28 23:37:46 +0000640
Denis Vlasenko1114de72006-10-10 23:26:05 +0000641 fmt = FLOATING_POINT;
642 ++s;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000643 p = strchr(FDL, *s);
644 if (!p) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000645 size = sizeof(double);
646 if (isdigit(s[0])) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000647 size = bb_strtou(s, &end, 0);
648 if (errno == ERANGE || size > MAX_FP_TYPE_SIZE
Denis Vlasenko1114de72006-10-10 23:26:05 +0000649 || fp_type_size[size] == NO_SIZE
650 ) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000651 bb_error_msg_and_die("invalid type string '%s'; "
652 "%u-byte %s type is not supported",
653 s_orig, size, "floating point");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000654 }
655 s = end;
656 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000657 } else {
658 static const uint8_t FDL_sizeof[] = {
659 sizeof(float),
660 sizeof(double),
661 sizeof(longdouble_t),
662 };
663
664 size = FDL_sizeof[p - FDL];
Denis Vlasenko1114de72006-10-10 23:26:05 +0000665 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000666
Denis Vlasenko1114de72006-10-10 23:26:05 +0000667 size_spec = fp_type_size[size];
668
669 switch (size_spec) {
670 case FLOAT_SINGLE:
671 print_function = print_float;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000672 field_width = FLT_DIG + 8;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000673 /* Don't use %#e; not all systems support it. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000674 fmt_string = xasprintf(" %%%d.%de", field_width, FLT_DIG);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000675 break;
676 case FLOAT_DOUBLE:
677 print_function = print_double;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000678 field_width = DBL_DIG + 8;
679 fmt_string = xasprintf(" %%%d.%de", field_width, DBL_DIG);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000680 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000681 default: /* case FLOAT_LONG_DOUBLE: */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000682 print_function = print_long_double;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000683 field_width = LDBL_DIG + 8;
684 fmt_string = xasprintf(" %%%d.%dLe", field_width, LDBL_DIG);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000685 break;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000686 }
687 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000688 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000689
690 case 'a':
691 ++s;
692 fmt = NAMED_CHARACTER;
693 size_spec = CHAR;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000694 print_function = print_named_ascii;
695 field_width = 3;
696 break;
697 case 'c':
698 ++s;
699 fmt = CHARACTER;
700 size_spec = CHAR;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000701 print_function = print_ascii;
702 field_width = 3;
703 break;
704 default:
Denis Vlasenko601ae132006-11-28 23:37:46 +0000705 bb_error_msg_and_die("invalid character '%c' "
706 "in type string '%s'", *s, s_orig);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000707 }
708
709 tspec->size = size_spec;
710 tspec->fmt = fmt;
711 tspec->print_function = print_function;
712 tspec->fmt_string = fmt_string;
713
714 tspec->field_width = field_width;
715 tspec->hexl_mode_trailer = (*s == 'z');
716 if (tspec->hexl_mode_trailer)
717 s++;
718
Denis Vlasenko7089c312008-04-14 19:50:06 +0000719 return s;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000720}
721
722/* Decode the modern od format string S. Append the decoded
723 representation to the global array SPEC, reallocating SPEC if
Denis Vlasenko97bd0e02008-02-08 15:41:01 +0000724 necessary. */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000725
Denis Vlasenko601ae132006-11-28 23:37:46 +0000726static void
Denis Vlasenko1114de72006-10-10 23:26:05 +0000727decode_format_string(const char *s)
728{
729 const char *s_orig = s;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000730
731 while (*s != '\0') {
732 struct tspec tspec;
733 const char *next;
734
Denis Vlasenko7089c312008-04-14 19:50:06 +0000735 next = decode_one_format(s_orig, s, &tspec);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000736
737 assert(s != next);
738 s = next;
Denis Vlasenkodeeed592008-07-08 05:14:36 +0000739 spec = xrealloc_vector(spec, 4, n_specs);
740 memcpy(&spec[n_specs], &tspec, sizeof(spec[0]));
Denis Vlasenko1114de72006-10-10 23:26:05 +0000741 n_specs++;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000742 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000743}
744
745/* Given a list of one or more input filenames FILE_LIST, set the global
746 file pointer IN_STREAM to position N_SKIP in the concatenation of
747 those files. If any file operation fails or if there are fewer than
748 N_SKIP bytes in the combined input, give an error message and return
749 nonzero. When possible, use seek rather than read operations to
750 advance IN_STREAM. */
751
Denis Vlasenko601ae132006-11-28 23:37:46 +0000752static void
753skip(off_t n_skip)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000754{
Denis Vlasenko1114de72006-10-10 23:26:05 +0000755 if (n_skip == 0)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000756 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000757
Denis Vlasenko601ae132006-11-28 23:37:46 +0000758 while (in_stream) { /* !EOF */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000759 struct stat file_stats;
760
761 /* First try seeking. For large offsets, this extra work is
762 worthwhile. If the offset is below some threshold it may be
763 more efficient to move the pointer by reading. There are two
764 issues when trying to seek:
765 - the file must be seekable.
766 - before seeking to the specified position, make sure
767 that the new position is in the current file.
768 Try to do that by getting file's size using fstat.
769 But that will work only for regular files. */
770
Denis Vlasenko1114de72006-10-10 23:26:05 +0000771 /* The st_size field is valid only for regular files
772 (and for symbolic links, which cannot occur here).
773 If the number of bytes left to skip is at least
774 as large as the size of the current file, we can
775 decrement n_skip and go on to the next file. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000776 if (fstat(fileno(in_stream), &file_stats) == 0
Denis Vlasenko97bd0e02008-02-08 15:41:01 +0000777 && S_ISREG(file_stats.st_mode) && file_stats.st_size > 0
Denis Vlasenko601ae132006-11-28 23:37:46 +0000778 ) {
779 if (file_stats.st_size < n_skip) {
780 n_skip -= file_stats.st_size;
Denis Vlasenko97bd0e02008-02-08 15:41:01 +0000781 /* take "check & close / open_next" route */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000782 } else {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000783 if (fseeko(in_stream, n_skip, SEEK_CUR) != 0)
784 ioerror = 1;
785 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000786 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000787 } else {
Denis Vlasenko97bd0e02008-02-08 15:41:01 +0000788 /* If it's not a regular file with positive size,
Denis Vlasenko601ae132006-11-28 23:37:46 +0000789 position the file pointer by reading. */
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +0000790 char buf[1024];
791 size_t n_bytes_to_read = 1024;
792 size_t n_bytes_read;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000793
Denis Vlasenko601ae132006-11-28 23:37:46 +0000794 while (n_skip > 0) {
795 if (n_skip < n_bytes_to_read)
796 n_bytes_to_read = n_skip;
797 n_bytes_read = fread(buf, 1, n_bytes_to_read, in_stream);
798 n_skip -= n_bytes_read;
799 if (n_bytes_read != n_bytes_to_read)
Denis Vlasenko8e858e22007-03-07 09:35:43 +0000800 break; /* EOF on this file or error */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000801 }
802 }
803 if (n_skip == 0)
804 return;
805
806 check_and_close();
807 open_next_file();
Denis Vlasenko1114de72006-10-10 23:26:05 +0000808 }
809
Denis Vlasenko601ae132006-11-28 23:37:46 +0000810 if (n_skip)
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100811 bb_error_msg_and_die("can't skip past end of combined input");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000812}
813
Denis Vlasenko601ae132006-11-28 23:37:46 +0000814
815typedef void FN_format_address(off_t address, char c);
816
Denis Vlasenko1114de72006-10-10 23:26:05 +0000817static void
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000818format_address_none(off_t address UNUSED_PARAM, char c UNUSED_PARAM)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000819{
820}
821
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000822static char address_fmt[] ALIGN1 = "%0n"OFF_FMT"xc";
Denis Vlasenko601ae132006-11-28 23:37:46 +0000823/* Corresponds to 'x' above */
824#define address_base_char address_fmt[sizeof(address_fmt)-3]
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000825/* Corresponds to 'n' above */
826#define address_pad_len_char address_fmt[2]
Denis Vlasenko601ae132006-11-28 23:37:46 +0000827
Denis Vlasenko1114de72006-10-10 23:26:05 +0000828static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000829format_address_std(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000830{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000831 /* Corresponds to 'c' */
832 address_fmt[sizeof(address_fmt)-2] = c;
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000833 printf(address_fmt, address);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000834}
835
Denys Vlasenkof3b92d32009-06-19 12:10:38 +0200836#if ENABLE_LONG_OPTS
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000837/* only used with --traditional */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000838static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000839format_address_paren(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000840{
841 putchar('(');
842 format_address_std(address, ')');
Denis Vlasenko0f5905e2006-12-17 19:21:13 +0000843 if (c) putchar(c);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000844}
845
846static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000847format_address_label(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000848{
849 format_address_std(address, ' ');
850 format_address_paren(address + pseudo_offset, c);
851}
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000852#endif
Denis Vlasenko601ae132006-11-28 23:37:46 +0000853
854static void
855dump_hexl_mode_trailer(size_t n_bytes, const char *block)
856{
857 fputs(" >", stdout);
858 while (n_bytes--) {
859 unsigned c = *(unsigned char *) block++;
860 c = (ISPRINT(c) ? c : '.');
861 putchar(c);
862 }
863 putchar('<');
864}
865
Denis Vlasenko1114de72006-10-10 23:26:05 +0000866/* Write N_BYTES bytes from CURR_BLOCK to standard output once for each
867 of the N_SPEC format specs. CURRENT_OFFSET is the byte address of
868 CURR_BLOCK in the concatenation of input files, and it is printed
869 (optionally) only before the output line associated with the first
870 format spec. When duplicate blocks are being abbreviated, the output
871 for a sequence of identical input blocks is the output for the first
872 block followed by an asterisk alone on a line. It is valid to compare
873 the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK.
874 That condition may be false only for the last input block -- and then
875 only when it has not been padded to length BYTES_PER_BLOCK. */
876
877static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000878write_block(off_t current_offset, size_t n_bytes,
Denis Vlasenko1114de72006-10-10 23:26:05 +0000879 const char *prev_block, const char *curr_block)
880{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000881 static char first = 1;
882 static char prev_pair_equal = 0;
883 size_t i;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000884
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000885 if (!verbose && !first
Denis Vlasenko1114de72006-10-10 23:26:05 +0000886 && n_bytes == bytes_per_block
887 && memcmp(prev_block, curr_block, bytes_per_block) == 0
888 ) {
889 if (prev_pair_equal) {
890 /* The two preceding blocks were equal, and the current
891 block is the same as the last one, so print nothing. */
892 } else {
893 puts("*");
894 prev_pair_equal = 1;
895 }
896 } else {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000897 first = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000898 prev_pair_equal = 0;
899 for (i = 0; i < n_specs; i++) {
900 if (i == 0)
901 format_address(current_offset, '\0');
902 else
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000903 printf("%*s", address_pad_len_char - '0', "");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000904 (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string);
905 if (spec[i].hexl_mode_trailer) {
906 /* space-pad out to full line width, then dump the trailer */
907 int datum_width = width_bytes[spec[i].size];
908 int blank_fields = (bytes_per_block - n_bytes) / datum_width;
909 int field_width = spec[i].field_width + 1;
910 printf("%*s", blank_fields * field_width, "");
911 dump_hexl_mode_trailer(n_bytes, curr_block);
912 }
913 putchar('\n');
914 }
915 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000916}
917
Denis Vlasenko601ae132006-11-28 23:37:46 +0000918static void
Denis Vlasenko1114de72006-10-10 23:26:05 +0000919read_block(size_t n, char *block, size_t *n_bytes_in_buffer)
920{
Denis Vlasenko1114de72006-10-10 23:26:05 +0000921 assert(0 < n && n <= bytes_per_block);
922
923 *n_bytes_in_buffer = 0;
924
925 if (n == 0)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000926 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000927
928 while (in_stream != NULL) { /* EOF. */
929 size_t n_needed;
930 size_t n_read;
931
932 n_needed = n - *n_bytes_in_buffer;
933 n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, in_stream);
934 *n_bytes_in_buffer += n_read;
935 if (n_read == n_needed)
936 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000937 /* error check is done in check_and_close */
938 check_and_close();
939 open_next_file();
Denis Vlasenko1114de72006-10-10 23:26:05 +0000940 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000941}
942
943/* Return the least common multiple of the sizes associated
944 with the format specs. */
945
946static int
947get_lcm(void)
948{
949 size_t i;
950 int l_c_m = 1;
951
952 for (i = 0; i < n_specs; i++)
953 l_c_m = lcm(l_c_m, width_bytes[(int) spec[i].size]);
954 return l_c_m;
955}
956
Denys Vlasenkof3b92d32009-06-19 12:10:38 +0200957#if ENABLE_LONG_OPTS
Denis Vlasenko1114de72006-10-10 23:26:05 +0000958/* If S is a valid traditional offset specification with an optional
959 leading '+' return nonzero and set *OFFSET to the offset it denotes. */
960
961static int
Denis Vlasenko601ae132006-11-28 23:37:46 +0000962parse_old_offset(const char *s, off_t *offset)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000963{
964 static const struct suffix_mult Bb[] = {
965 { "B", 1024 },
966 { "b", 512 },
Denys Vlasenko043b1e52009-09-06 12:47:55 +0200967 { "", 0 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000968 };
Denis Vlasenko601ae132006-11-28 23:37:46 +0000969 char *p;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000970 int radix;
971
Denis Vlasenko1114de72006-10-10 23:26:05 +0000972 /* Skip over any leading '+'. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000973 if (s[0] == '+') ++s;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000974
975 /* Determine the radix we'll use to interpret S. If there is a '.',
Denis Vlasenko601ae132006-11-28 23:37:46 +0000976 * it's decimal, otherwise, if the string begins with '0X'or '0x',
977 * it's hexadecimal, else octal. */
978 p = strchr(s, '.');
979 radix = 8;
980 if (p) {
981 p[0] = '\0'; /* cheating */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000982 radix = 10;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000983 } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
984 radix = 16;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000985
Denis Vlasenko601ae132006-11-28 23:37:46 +0000986 *offset = xstrtooff_sfx(s, radix, Bb);
987 if (p) p[0] = '.';
988
989 return (*offset >= 0);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000990}
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000991#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +0000992
993/* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
994 formatted block to standard output, and repeat until the specified
995 maximum number of bytes has been read or until all input has been
996 processed. If the last block read is smaller than BYTES_PER_BLOCK
997 and its size is not a multiple of the size associated with a format
998 spec, extend the input block with zero bytes until its length is a
999 multiple of all format spec sizes. Write the final block. Finally,
1000 write on a line by itself the offset of the byte after the last byte
Denis Vlasenko97bd0e02008-02-08 15:41:01 +00001001 read. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001002
Denis Vlasenko601ae132006-11-28 23:37:46 +00001003static void
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001004dump(off_t current_offset, off_t end_offset)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001005{
1006 char *block[2];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001007 int idx;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001008 size_t n_bytes_read;
1009
1010 block[0] = xmalloc(2*bytes_per_block);
1011 block[1] = block[0] + bytes_per_block;
1012
Denis Vlasenko1114de72006-10-10 23:26:05 +00001013 idx = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001014 if (limit_bytes_to_format) {
1015 while (1) {
1016 size_t n_needed;
1017 if (current_offset >= end_offset) {
1018 n_bytes_read = 0;
1019 break;
1020 }
1021 n_needed = MIN(end_offset - current_offset,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001022 (off_t) bytes_per_block);
1023 read_block(n_needed, block[idx], &n_bytes_read);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001024 if (n_bytes_read < bytes_per_block)
1025 break;
1026 assert(n_bytes_read == bytes_per_block);
1027 write_block(current_offset, n_bytes_read,
1028 block[!idx], block[idx]);
1029 current_offset += n_bytes_read;
1030 idx = !idx;
1031 }
1032 } else {
1033 while (1) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001034 read_block(bytes_per_block, block[idx], &n_bytes_read);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001035 if (n_bytes_read < bytes_per_block)
1036 break;
1037 assert(n_bytes_read == bytes_per_block);
1038 write_block(current_offset, n_bytes_read,
1039 block[!idx], block[idx]);
1040 current_offset += n_bytes_read;
1041 idx = !idx;
1042 }
1043 }
1044
1045 if (n_bytes_read > 0) {
1046 int l_c_m;
1047 size_t bytes_to_write;
1048
1049 l_c_m = get_lcm();
1050
1051 /* Make bytes_to_write the smallest multiple of l_c_m that
1052 is at least as large as n_bytes_read. */
1053 bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m);
1054
1055 memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
1056 write_block(current_offset, bytes_to_write,
1057 block[!idx], block[idx]);
1058 current_offset += n_bytes_read;
1059 }
1060
1061 format_address(current_offset, '\n');
1062
1063 if (limit_bytes_to_format && current_offset >= end_offset)
Denis Vlasenko601ae132006-11-28 23:37:46 +00001064 check_and_close();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001065
1066 free(block[0]);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001067}
1068
Denis Vlasenko601ae132006-11-28 23:37:46 +00001069/* Read a single byte into *C from the concatenation of the input files
1070 named in the global array FILE_LIST. On the first call to this
1071 function, the global variable IN_STREAM is expected to be an open
1072 stream associated with the input file INPUT_FILENAME. If IN_STREAM
1073 is at end-of-file, close it and update the global variables IN_STREAM
1074 and INPUT_FILENAME so they correspond to the next file in the list.
1075 Then try to read a byte from the newly opened file. Repeat if
1076 necessary until EOF is reached for the last file in FILE_LIST, then
Denis Vlasenko97bd0e02008-02-08 15:41:01 +00001077 set *C to EOF and return. Subsequent calls do likewise. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001078
1079static void
1080read_char(int *c)
1081{
1082 while (in_stream) { /* !EOF */
1083 *c = fgetc(in_stream);
1084 if (*c != EOF)
1085 return;
1086 check_and_close();
1087 open_next_file();
1088 }
1089 *c = EOF;
1090}
1091
1092/* Read N bytes into BLOCK from the concatenation of the input files
1093 named in the global array FILE_LIST. On the first call to this
1094 function, the global variable IN_STREAM is expected to be an open
1095 stream associated with the input file INPUT_FILENAME. If all N
1096 bytes cannot be read from IN_STREAM, close IN_STREAM and update
1097 the global variables IN_STREAM and INPUT_FILENAME. Then try to
1098 read the remaining bytes from the newly opened file. Repeat if
1099 necessary until EOF is reached for the last file in FILE_LIST.
1100 On subsequent calls, don't modify BLOCK and return zero. Set
1101 *N_BYTES_IN_BUFFER to the number of bytes read. If an error occurs,
1102 it will be detected through ferror when the stream is about to be
1103 closed. If there is an error, give a message but continue reading
1104 as usual and return nonzero. Otherwise return zero. */
1105
Denis Vlasenko1114de72006-10-10 23:26:05 +00001106/* STRINGS mode. Find each "string constant" in the input.
1107 A string constant is a run of at least 'string_min' ASCII
1108 graphic (or formatting) characters terminated by a null.
1109 Based on a function written by Richard Stallman for a
Denis Vlasenko97bd0e02008-02-08 15:41:01 +00001110 traditional version of od. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001111
Denis Vlasenko601ae132006-11-28 23:37:46 +00001112static void
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001113dump_strings(off_t address, off_t end_offset)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001114{
1115 size_t bufsize = MAX(100, string_min);
1116 char *buf = xmalloc(bufsize);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001117
Denis Vlasenko1114de72006-10-10 23:26:05 +00001118 while (1) {
1119 size_t i;
1120 int c;
1121
1122 /* See if the next 'string_min' chars are all printing chars. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001123 tryline:
1124 if (limit_bytes_to_format && (end_offset - string_min <= address))
Denis Vlasenko1114de72006-10-10 23:26:05 +00001125 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001126 i = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001127 while (!limit_bytes_to_format || address < end_offset) {
1128 if (i == bufsize) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001129 bufsize += bufsize/8;
1130 buf = xrealloc(buf, bufsize);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001131 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001132 read_char(&c);
1133 if (c < 0) { /* EOF */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001134 free(buf);
Denis Vlasenko601ae132006-11-28 23:37:46 +00001135 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001136 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001137 address++;
1138 if (!c)
1139 break;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001140 if (!ISPRINT(c))
1141 goto tryline; /* It isn't; give up on this string. */
1142 buf[i++] = c; /* String continues; store it all. */
1143 }
1144
Denis Vlasenko601ae132006-11-28 23:37:46 +00001145 if (i < string_min) /* Too short! */
1146 goto tryline;
1147
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001148 /* If we get here, the string is all printable and NUL-terminated,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001149 * so print it. It is all in 'buf' and 'i' is its length. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001150 buf[i] = 0;
1151 format_address(address - i - 1, ' ');
1152
1153 for (i = 0; (c = buf[i]); i++) {
1154 switch (c) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001155 case '\007': fputs("\\a", stdout); break;
1156 case '\b': fputs("\\b", stdout); break;
1157 case '\f': fputs("\\f", stdout); break;
1158 case '\n': fputs("\\n", stdout); break;
1159 case '\r': fputs("\\r", stdout); break;
1160 case '\t': fputs("\\t", stdout); break;
1161 case '\v': fputs("\\v", stdout); break;
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001162 default: putchar(c);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001163 }
1164 }
1165 putchar('\n');
1166 }
1167
1168 /* We reach this point only if we search through
1169 (max_bytes_to_format - string_min) bytes before reaching EOF. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001170 free(buf);
1171
Denis Vlasenko601ae132006-11-28 23:37:46 +00001172 check_and_close();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001173}
1174
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001175int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko06af2162007-02-03 17:28:39 +00001176int od_main(int argc, char **argv)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001177{
1178 static const struct suffix_mult bkm[] = {
1179 { "b", 512 },
1180 { "k", 1024 },
1181 { "m", 1024*1024 },
Denys Vlasenko043b1e52009-09-06 12:47:55 +02001182 { "", 0 }
Denis Vlasenko1114de72006-10-10 23:26:05 +00001183 };
Denis Vlasenko601ae132006-11-28 23:37:46 +00001184 enum {
1185 OPT_A = 1 << 0,
1186 OPT_N = 1 << 1,
1187 OPT_a = 1 << 2,
1188 OPT_b = 1 << 3,
1189 OPT_c = 1 << 4,
1190 OPT_d = 1 << 5,
1191 OPT_f = 1 << 6,
1192 OPT_h = 1 << 7,
1193 OPT_i = 1 << 8,
1194 OPT_j = 1 << 9,
1195 OPT_l = 1 << 10,
1196 OPT_o = 1 << 11,
1197 OPT_t = 1 << 12,
1198 OPT_v = 1 << 13,
1199 OPT_x = 1 << 14,
1200 OPT_s = 1 << 15,
1201 OPT_S = 1 << 16,
1202 OPT_w = 1 << 17,
Denys Vlasenkof3b92d32009-06-19 12:10:38 +02001203 OPT_traditional = (1 << 18) * ENABLE_LONG_OPTS,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001204 };
Denys Vlasenkof3b92d32009-06-19 12:10:38 +02001205#if ENABLE_LONG_OPTS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001206 static const char od_longopts[] ALIGN1 =
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +00001207 "skip-bytes\0" Required_argument "j"
1208 "address-radix\0" Required_argument "A"
1209 "read-bytes\0" Required_argument "N"
1210 "format\0" Required_argument "t"
1211 "output-duplicates\0" No_argument "v"
1212 "strings\0" Optional_argument "S"
1213 "width\0" Optional_argument "w"
1214 "traditional\0" No_argument "\xff"
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001215 ;
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001216#endif
Denis Vlasenko601ae132006-11-28 23:37:46 +00001217 char *str_A, *str_N, *str_j, *str_S;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001218 llist_t *lst_t = NULL;
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001219 unsigned opt;
1220 int l_c_m;
1221 /* The old-style 'pseudo starting address' to be printed in parentheses
1222 after any true address. */
1223 off_t pseudo_start = pseudo_start; // for gcc
1224 /* The number of input bytes to skip before formatting and writing. */
1225 off_t n_bytes_to_skip = 0;
1226 /* The offset of the first byte after the last byte to be formatted. */
1227 off_t end_offset = 0;
1228 /* The maximum number of bytes that will be formatted. */
1229 off_t max_bytes_to_format = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001230
1231 spec = NULL;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001232 format_address = format_address_std;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001233 address_base_char = 'o';
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001234 address_pad_len_char = '7';
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +00001235 /* flag_dump_strings = 0; - already is */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001236
Denis Vlasenko601ae132006-11-28 23:37:46 +00001237 /* Parse command line */
Denis Vlasenko1d426652008-03-17 09:09:09 +00001238 opt_complementary = "w+:t::"; /* -w N, -t is a list */
Denys Vlasenkof3b92d32009-06-19 12:10:38 +02001239#if ENABLE_LONG_OPTS
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +00001240 applet_long_options = od_longopts;
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001241#endif
Denis Vlasenkofe7cd642007-08-18 15:32:12 +00001242 opt = getopt32(argv, "A:N:abcdfhij:lot:vxsS:"
Denis Vlasenko601ae132006-11-28 23:37:46 +00001243 "w::", // -w with optional param
1244 // -S was -s and also had optional parameter
1245 // but in coreutils 6.3 it was renamed and now has
1246 // _mandatory_ parameter
Denis Vlasenko1d426652008-03-17 09:09:09 +00001247 &str_A, &str_N, &str_j, &lst_t, &str_S, &bytes_per_block);
Denis Vlasenko601ae132006-11-28 23:37:46 +00001248 argc -= optind;
1249 argv += optind;
1250 if (opt & OPT_A) {
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001251 static const char doxn[] ALIGN1 = "doxn";
1252 static const char doxn_address_base_char[] ALIGN1 = {
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001253 'u', 'o', 'x', /* '?' fourth one is not important */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001254 };
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001255 static const uint8_t doxn_address_pad_len_char[] ALIGN1 = {
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001256 '7', '7', '6', /* '?' */
1257 };
Denis Vlasenko601ae132006-11-28 23:37:46 +00001258 char *p;
1259 int pos;
1260 p = strchr(doxn, str_A[0]);
1261 if (!p)
1262 bb_error_msg_and_die("bad output address radix "
1263 "'%c' (must be [doxn])", str_A[0]);
1264 pos = p - doxn;
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001265 if (pos == 3) format_address = format_address_none;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001266 address_base_char = doxn_address_base_char[pos];
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001267 address_pad_len_char = doxn_address_pad_len_char[pos];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001268 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001269 if (opt & OPT_N) {
1270 limit_bytes_to_format = 1;
1271 max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm);
1272 }
1273 if (opt & OPT_a) decode_format_string("a");
1274 if (opt & OPT_b) decode_format_string("oC");
1275 if (opt & OPT_c) decode_format_string("c");
1276 if (opt & OPT_d) decode_format_string("u2");
1277 if (opt & OPT_f) decode_format_string("fF");
1278 if (opt & OPT_h) decode_format_string("x2");
1279 if (opt & OPT_i) decode_format_string("d2");
1280 if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm);
1281 if (opt & OPT_l) decode_format_string("d4");
1282 if (opt & OPT_o) decode_format_string("o2");
1283 //if (opt & OPT_t)...
Denis Vlasenko601ae132006-11-28 23:37:46 +00001284 while (lst_t) {
Denis Vlasenkod50dda82008-06-15 05:40:56 +00001285 decode_format_string(llist_pop(&lst_t));
Denis Vlasenko601ae132006-11-28 23:37:46 +00001286 }
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +00001287 if (opt & OPT_v) verbose = 1;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001288 if (opt & OPT_x) decode_format_string("x2");
1289 if (opt & OPT_s) decode_format_string("d2");
1290 if (opt & OPT_S) {
1291 string_min = 3;
1292 string_min = xstrtou_sfx(str_S, 0, bkm);
1293 flag_dump_strings = 1;
1294 }
1295 //if (opt & OPT_w)...
1296 //if (opt & OPT_traditional)...
Denis Vlasenko1114de72006-10-10 23:26:05 +00001297
1298 if (flag_dump_strings && n_specs > 0)
1299 bb_error_msg_and_die("no type may be specified when dumping strings");
1300
Denis Vlasenko1114de72006-10-10 23:26:05 +00001301 /* If the --traditional option is used, there may be from
Denis Vlasenko601ae132006-11-28 23:37:46 +00001302 * 0 to 3 remaining command line arguments; handle each case
1303 * separately.
Denis Vlasenko8e858e22007-03-07 09:35:43 +00001304 * od [file] [[+]offset[.][b] [[+]label[.][b]]]
Denis Vlasenko601ae132006-11-28 23:37:46 +00001305 * The offset and pseudo_start have the same syntax.
1306 *
1307 * FIXME: POSIX 1003.1-2001 with XSI requires support for the
1308 * traditional syntax even if --traditional is not given. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001309
Denys Vlasenkof3b92d32009-06-19 12:10:38 +02001310#if ENABLE_LONG_OPTS
Denis Vlasenko601ae132006-11-28 23:37:46 +00001311 if (opt & OPT_traditional) {
1312 off_t o1, o2;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001313
Denis Vlasenko601ae132006-11-28 23:37:46 +00001314 if (argc == 1) {
1315 if (parse_old_offset(argv[0], &o1)) {
1316 n_bytes_to_skip = o1;
1317 --argc;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001318 ++argv;
1319 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001320 } else if (argc == 2) {
1321 if (parse_old_offset(argv[0], &o1)
1322 && parse_old_offset(argv[1], &o2)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001323 ) {
1324 n_bytes_to_skip = o1;
1325 flag_pseudo_start = 1;
1326 pseudo_start = o2;
1327 argv += 2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001328 argc -= 2;
1329 } else if (parse_old_offset(argv[1], &o2)) {
Denis Vlasenko1114de72006-10-10 23:26:05 +00001330 n_bytes_to_skip = o2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001331 --argc;
1332 argv[1] = argv[0];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001333 ++argv;
1334 } else {
1335 bb_error_msg_and_die("invalid second operand "
Denis Vlasenko601ae132006-11-28 23:37:46 +00001336 "in compatibility mode '%s'", argv[1]);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001337 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001338 } else if (argc == 3) {
1339 if (parse_old_offset(argv[1], &o1)
1340 && parse_old_offset(argv[2], &o2)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001341 ) {
1342 n_bytes_to_skip = o1;
1343 flag_pseudo_start = 1;
1344 pseudo_start = o2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001345 argv[2] = argv[0];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001346 argv += 2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001347 argc -= 2;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001348 } else {
1349 bb_error_msg_and_die("in compatibility mode "
1350 "the last two arguments must be offsets");
1351 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001352 } else if (argc > 3) {
Denis Vlasenko1114de72006-10-10 23:26:05 +00001353 bb_error_msg_and_die("compatibility mode supports "
1354 "at most three arguments");
1355 }
1356
1357 if (flag_pseudo_start) {
1358 if (format_address == format_address_none) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001359 address_base_char = 'o';
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001360 address_pad_len_char = '7';
Denis Vlasenko1114de72006-10-10 23:26:05 +00001361 format_address = format_address_paren;
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001362 } else
1363 format_address = format_address_label;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001364 }
1365 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001366#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +00001367
1368 if (limit_bytes_to_format) {
1369 end_offset = n_bytes_to_skip + max_bytes_to_format;
1370 if (end_offset < n_bytes_to_skip)
1371 bb_error_msg_and_die("skip-bytes + read-bytes is too large");
1372 }
1373
1374 if (n_specs == 0) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001375 decode_format_string("o2");
Denis Vlasenko1114de72006-10-10 23:26:05 +00001376 n_specs = 1;
1377 }
1378
Denis Vlasenko601ae132006-11-28 23:37:46 +00001379 /* If no files were listed on the command line,
1380 set the global pointer FILE_LIST so that it
1381 references the null-terminated list of one name: "-". */
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001382 file_list = bb_argv_dash;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001383 if (argc > 0) {
Denis Vlasenko1114de72006-10-10 23:26:05 +00001384 /* Set the global pointer FILE_LIST so that it
1385 references the first file-argument on the command-line. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001386 file_list = (char const *const *) argv;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001387 }
1388
1389 /* open the first input file */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001390 open_next_file();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001391 /* skip over any unwanted header bytes */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001392 skip(n_bytes_to_skip);
1393 if (!in_stream)
Bernhard Reutner-Fischer7c2db5c2007-11-16 12:39:16 +00001394 return EXIT_FAILURE;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001395
1396 pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0);
1397
1398 /* Compute output block length. */
1399 l_c_m = get_lcm();
1400
Denis Vlasenko601ae132006-11-28 23:37:46 +00001401 if (opt & OPT_w) { /* -w: width */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001402 if (!bytes_per_block || bytes_per_block % l_c_m != 0) {
Denis Vlasenko97bd0e02008-02-08 15:41:01 +00001403 bb_error_msg("warning: invalid width %u; using %d instead",
1404 (unsigned)bytes_per_block, l_c_m);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001405 bytes_per_block = l_c_m;
1406 }
1407 } else {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001408 bytes_per_block = l_c_m;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001409 if (l_c_m < DEFAULT_BYTES_PER_BLOCK)
Denis Vlasenko601ae132006-11-28 23:37:46 +00001410 bytes_per_block *= DEFAULT_BYTES_PER_BLOCK / l_c_m;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001411 }
1412
1413#ifdef DEBUG
1414 for (i = 0; i < n_specs; i++) {
1415 printf("%d: fmt=\"%s\" width=%d\n",
1416 i, spec[i].fmt_string, width_bytes[spec[i].size]);
1417 }
1418#endif
1419
Denis Vlasenko601ae132006-11-28 23:37:46 +00001420 if (flag_dump_strings)
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001421 dump_strings(n_bytes_to_skip, end_offset);
Denis Vlasenko601ae132006-11-28 23:37:46 +00001422 else
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001423 dump(n_bytes_to_skip, end_offset);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001424
Denis Vlasenko601ae132006-11-28 23:37:46 +00001425 if (fclose(stdin) == EOF)
1426 bb_perror_msg_and_die(bb_msg_standard_input);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001427
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +00001428 return ioerror;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001429}