blob: 5e2287534a7db38991417195c0c364fd1db740d2 [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,
367 const char *unused_fmt_string ATTRIBUTE_UNUSED)
368{
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,
407 const char *unused_fmt_string ATTRIBUTE_UNUSED)
408{
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)) {
505 bb_error_msg("write error");
506 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
Denis Vlasenko7089c312008-04-14 19:50:06 +0000524static const char *
525decode_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);
548 if (!p) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000549 size = sizeof(int);
550 if (isdigit(s[0])) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000551 size = bb_strtou(s, &end, 0);
552 if (errno == ERANGE
553 || MAX_INTEGRAL_TYPE_SIZE < size
Denis Vlasenko1114de72006-10-10 23:26:05 +0000554 || integral_type_size[size] == NO_SIZE
555 ) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000556 bb_error_msg_and_die("invalid type string '%s'; "
557 "%u-byte %s type is not supported",
558 s_orig, size, "integral");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000559 }
560 s = end;
561 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000562 } else {
Denis Vlasenko7089c312008-04-14 19:50:06 +0000563 static const uint8_t CSIL_sizeof[4] = {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000564 sizeof(char),
565 sizeof(short),
566 sizeof(int),
567 sizeof(long),
568 };
569 size = CSIL_sizeof[p - CSIL];
Denis Vlasenko7089c312008-04-14 19:50:06 +0000570 s++; /* skip C/S/I/L */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000571 }
572
573#define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \
574 ((Spec) == LONG_LONG ? (Max_format) \
575 : ((Spec) == LONG ? (Long_format) : (Min_format)))
576
577#define FMT_BYTES_ALLOCATED 9
Denis Vlasenko1114de72006-10-10 23:26:05 +0000578 size_spec = integral_type_size[size];
579
Denis Vlasenko601ae132006-11-28 23:37:46 +0000580 {
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000581 static const char doux[] ALIGN1 = "doux";
Denis Vlasenko601ae132006-11-28 23:37:46 +0000582 static const char doux_fmt_letter[][4] = {
583 "lld", "llo", "llu", "llx"
584 };
585 static const enum output_format doux_fmt[] = {
586 SIGNED_DECIMAL,
587 OCTAL,
588 UNSIGNED_DECIMAL,
589 HEXADECIMAL,
590 };
591 static const uint8_t *const doux_bytes_to_XXX[] = {
592 bytes_to_signed_dec_digits,
593 bytes_to_oct_digits,
594 bytes_to_unsigned_dec_digits,
595 bytes_to_hex_digits,
596 };
597 static const char doux_fmtstring[][sizeof(" %%0%u%s")] = {
598 " %%%u%s",
599 " %%0%u%s",
600 " %%%u%s",
601 " %%0%u%s",
602 };
Denis Vlasenko1114de72006-10-10 23:26:05 +0000603
Denis Vlasenko601ae132006-11-28 23:37:46 +0000604 pos = strchr(doux, c) - doux;
605 fmt = doux_fmt[pos];
606 field_width = doux_bytes_to_XXX[pos][size];
607 p = doux_fmt_letter[pos] + 2;
608 if (size_spec == LONG) p--;
609 if (size_spec == LONG_LONG) p -= 2;
610 fmt_string = xasprintf(doux_fmtstring[pos], field_width, p);
611 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000612
613 switch (size_spec) {
614 case CHAR:
615 print_function = (fmt == SIGNED_DECIMAL
616 ? print_s_char
617 : print_char);
618 break;
619 case SHORT:
620 print_function = (fmt == SIGNED_DECIMAL
621 ? print_s_short
622 : print_short);
623 break;
624 case INT:
625 print_function = print_int;
626 break;
627 case LONG:
628 print_function = print_long;
629 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000630 default: /* case LONG_LONG: */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000631 print_function = print_long_long;
632 break;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000633 }
634 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000635 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000636
Denis Vlasenko601ae132006-11-28 23:37:46 +0000637 case 'f': {
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000638 static const char FDL[] ALIGN1 = "FDL";
Denis Vlasenko601ae132006-11-28 23:37:46 +0000639
Denis Vlasenko1114de72006-10-10 23:26:05 +0000640 fmt = FLOATING_POINT;
641 ++s;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000642 p = strchr(FDL, *s);
643 if (!p) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000644 size = sizeof(double);
645 if (isdigit(s[0])) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000646 size = bb_strtou(s, &end, 0);
647 if (errno == ERANGE || size > MAX_FP_TYPE_SIZE
Denis Vlasenko1114de72006-10-10 23:26:05 +0000648 || fp_type_size[size] == NO_SIZE
649 ) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000650 bb_error_msg_and_die("invalid type string '%s'; "
651 "%u-byte %s type is not supported",
652 s_orig, size, "floating point");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000653 }
654 s = end;
655 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000656 } else {
657 static const uint8_t FDL_sizeof[] = {
658 sizeof(float),
659 sizeof(double),
660 sizeof(longdouble_t),
661 };
662
663 size = FDL_sizeof[p - FDL];
Denis Vlasenko1114de72006-10-10 23:26:05 +0000664 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000665
Denis Vlasenko1114de72006-10-10 23:26:05 +0000666 size_spec = fp_type_size[size];
667
668 switch (size_spec) {
669 case FLOAT_SINGLE:
670 print_function = print_float;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000671 field_width = FLT_DIG + 8;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000672 /* Don't use %#e; not all systems support it. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000673 fmt_string = xasprintf(" %%%d.%de", field_width, FLT_DIG);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000674 break;
675 case FLOAT_DOUBLE:
676 print_function = print_double;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000677 field_width = DBL_DIG + 8;
678 fmt_string = xasprintf(" %%%d.%de", field_width, DBL_DIG);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000679 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000680 default: /* case FLOAT_LONG_DOUBLE: */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000681 print_function = print_long_double;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000682 field_width = LDBL_DIG + 8;
683 fmt_string = xasprintf(" %%%d.%dLe", field_width, LDBL_DIG);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000684 break;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000685 }
686 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000687 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000688
689 case 'a':
690 ++s;
691 fmt = NAMED_CHARACTER;
692 size_spec = CHAR;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000693 print_function = print_named_ascii;
694 field_width = 3;
695 break;
696 case 'c':
697 ++s;
698 fmt = CHARACTER;
699 size_spec = CHAR;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000700 print_function = print_ascii;
701 field_width = 3;
702 break;
703 default:
Denis Vlasenko601ae132006-11-28 23:37:46 +0000704 bb_error_msg_and_die("invalid character '%c' "
705 "in type string '%s'", *s, s_orig);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000706 }
707
708 tspec->size = size_spec;
709 tspec->fmt = fmt;
710 tspec->print_function = print_function;
711 tspec->fmt_string = fmt_string;
712
713 tspec->field_width = field_width;
714 tspec->hexl_mode_trailer = (*s == 'z');
715 if (tspec->hexl_mode_trailer)
716 s++;
717
Denis Vlasenko7089c312008-04-14 19:50:06 +0000718 return s;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000719}
720
721/* Decode the modern od format string S. Append the decoded
722 representation to the global array SPEC, reallocating SPEC if
Denis Vlasenko97bd0e02008-02-08 15:41:01 +0000723 necessary. */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000724
Denis Vlasenko601ae132006-11-28 23:37:46 +0000725static void
Denis Vlasenko1114de72006-10-10 23:26:05 +0000726decode_format_string(const char *s)
727{
728 const char *s_orig = s;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000729
730 while (*s != '\0') {
731 struct tspec tspec;
732 const char *next;
733
Denis Vlasenko7089c312008-04-14 19:50:06 +0000734 next = decode_one_format(s_orig, s, &tspec);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000735
736 assert(s != next);
737 s = next;
738 n_specs++;
739 spec = xrealloc(spec, n_specs * sizeof(*spec));
740 memcpy(&spec[n_specs-1], &tspec, sizeof *spec);
741 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000742}
743
744/* Given a list of one or more input filenames FILE_LIST, set the global
745 file pointer IN_STREAM to position N_SKIP in the concatenation of
746 those files. If any file operation fails or if there are fewer than
747 N_SKIP bytes in the combined input, give an error message and return
748 nonzero. When possible, use seek rather than read operations to
749 advance IN_STREAM. */
750
Denis Vlasenko601ae132006-11-28 23:37:46 +0000751static void
752skip(off_t n_skip)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000753{
Denis Vlasenko1114de72006-10-10 23:26:05 +0000754 if (n_skip == 0)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000755 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000756
Denis Vlasenko601ae132006-11-28 23:37:46 +0000757 while (in_stream) { /* !EOF */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000758 struct stat file_stats;
759
760 /* First try seeking. For large offsets, this extra work is
761 worthwhile. If the offset is below some threshold it may be
762 more efficient to move the pointer by reading. There are two
763 issues when trying to seek:
764 - the file must be seekable.
765 - before seeking to the specified position, make sure
766 that the new position is in the current file.
767 Try to do that by getting file's size using fstat.
768 But that will work only for regular files. */
769
Denis Vlasenko1114de72006-10-10 23:26:05 +0000770 /* The st_size field is valid only for regular files
771 (and for symbolic links, which cannot occur here).
772 If the number of bytes left to skip is at least
773 as large as the size of the current file, we can
774 decrement n_skip and go on to the next file. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000775 if (fstat(fileno(in_stream), &file_stats) == 0
Denis Vlasenko97bd0e02008-02-08 15:41:01 +0000776 && S_ISREG(file_stats.st_mode) && file_stats.st_size > 0
Denis Vlasenko601ae132006-11-28 23:37:46 +0000777 ) {
778 if (file_stats.st_size < n_skip) {
779 n_skip -= file_stats.st_size;
Denis Vlasenko97bd0e02008-02-08 15:41:01 +0000780 /* take "check & close / open_next" route */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000781 } else {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000782 if (fseeko(in_stream, n_skip, SEEK_CUR) != 0)
783 ioerror = 1;
784 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000785 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000786 } else {
Denis Vlasenko97bd0e02008-02-08 15:41:01 +0000787 /* If it's not a regular file with positive size,
Denis Vlasenko601ae132006-11-28 23:37:46 +0000788 position the file pointer by reading. */
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +0000789 char buf[1024];
790 size_t n_bytes_to_read = 1024;
791 size_t n_bytes_read;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000792
Denis Vlasenko601ae132006-11-28 23:37:46 +0000793 while (n_skip > 0) {
794 if (n_skip < n_bytes_to_read)
795 n_bytes_to_read = n_skip;
796 n_bytes_read = fread(buf, 1, n_bytes_to_read, in_stream);
797 n_skip -= n_bytes_read;
798 if (n_bytes_read != n_bytes_to_read)
Denis Vlasenko8e858e22007-03-07 09:35:43 +0000799 break; /* EOF on this file or error */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000800 }
801 }
802 if (n_skip == 0)
803 return;
804
805 check_and_close();
806 open_next_file();
Denis Vlasenko1114de72006-10-10 23:26:05 +0000807 }
808
Denis Vlasenko601ae132006-11-28 23:37:46 +0000809 if (n_skip)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000810 bb_error_msg_and_die("cannot skip past end of combined input");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000811}
812
Denis Vlasenko601ae132006-11-28 23:37:46 +0000813
814typedef void FN_format_address(off_t address, char c);
815
Denis Vlasenko1114de72006-10-10 23:26:05 +0000816static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000817format_address_none(off_t address ATTRIBUTE_UNUSED, char c ATTRIBUTE_UNUSED)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000818{
819}
820
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000821static char address_fmt[] ALIGN1 = "%0n"OFF_FMT"xc";
Denis Vlasenko601ae132006-11-28 23:37:46 +0000822/* Corresponds to 'x' above */
823#define address_base_char address_fmt[sizeof(address_fmt)-3]
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000824/* Corresponds to 'n' above */
825#define address_pad_len_char address_fmt[2]
Denis Vlasenko601ae132006-11-28 23:37:46 +0000826
Denis Vlasenko1114de72006-10-10 23:26:05 +0000827static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000828format_address_std(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000829{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000830 /* Corresponds to 'c' */
831 address_fmt[sizeof(address_fmt)-2] = c;
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000832 printf(address_fmt, address);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000833}
834
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000835#if ENABLE_GETOPT_LONG
836/* only used with --traditional */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000837static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000838format_address_paren(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000839{
840 putchar('(');
841 format_address_std(address, ')');
Denis Vlasenko0f5905e2006-12-17 19:21:13 +0000842 if (c) putchar(c);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000843}
844
845static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000846format_address_label(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000847{
848 format_address_std(address, ' ');
849 format_address_paren(address + pseudo_offset, c);
850}
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000851#endif
Denis Vlasenko601ae132006-11-28 23:37:46 +0000852
853static void
854dump_hexl_mode_trailer(size_t n_bytes, const char *block)
855{
856 fputs(" >", stdout);
857 while (n_bytes--) {
858 unsigned c = *(unsigned char *) block++;
859 c = (ISPRINT(c) ? c : '.');
860 putchar(c);
861 }
862 putchar('<');
863}
864
Denis Vlasenko1114de72006-10-10 23:26:05 +0000865/* Write N_BYTES bytes from CURR_BLOCK to standard output once for each
866 of the N_SPEC format specs. CURRENT_OFFSET is the byte address of
867 CURR_BLOCK in the concatenation of input files, and it is printed
868 (optionally) only before the output line associated with the first
869 format spec. When duplicate blocks are being abbreviated, the output
870 for a sequence of identical input blocks is the output for the first
871 block followed by an asterisk alone on a line. It is valid to compare
872 the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK.
873 That condition may be false only for the last input block -- and then
874 only when it has not been padded to length BYTES_PER_BLOCK. */
875
876static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000877write_block(off_t current_offset, size_t n_bytes,
Denis Vlasenko1114de72006-10-10 23:26:05 +0000878 const char *prev_block, const char *curr_block)
879{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000880 static char first = 1;
881 static char prev_pair_equal = 0;
882 size_t i;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000883
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000884 if (!verbose && !first
Denis Vlasenko1114de72006-10-10 23:26:05 +0000885 && n_bytes == bytes_per_block
886 && memcmp(prev_block, curr_block, bytes_per_block) == 0
887 ) {
888 if (prev_pair_equal) {
889 /* The two preceding blocks were equal, and the current
890 block is the same as the last one, so print nothing. */
891 } else {
892 puts("*");
893 prev_pair_equal = 1;
894 }
895 } else {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000896 first = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000897 prev_pair_equal = 0;
898 for (i = 0; i < n_specs; i++) {
899 if (i == 0)
900 format_address(current_offset, '\0');
901 else
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000902 printf("%*s", address_pad_len_char - '0', "");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000903 (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string);
904 if (spec[i].hexl_mode_trailer) {
905 /* space-pad out to full line width, then dump the trailer */
906 int datum_width = width_bytes[spec[i].size];
907 int blank_fields = (bytes_per_block - n_bytes) / datum_width;
908 int field_width = spec[i].field_width + 1;
909 printf("%*s", blank_fields * field_width, "");
910 dump_hexl_mode_trailer(n_bytes, curr_block);
911 }
912 putchar('\n');
913 }
914 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000915}
916
Denis Vlasenko601ae132006-11-28 23:37:46 +0000917static void
Denis Vlasenko1114de72006-10-10 23:26:05 +0000918read_block(size_t n, char *block, size_t *n_bytes_in_buffer)
919{
Denis Vlasenko1114de72006-10-10 23:26:05 +0000920 assert(0 < n && n <= bytes_per_block);
921
922 *n_bytes_in_buffer = 0;
923
924 if (n == 0)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000925 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000926
927 while (in_stream != NULL) { /* EOF. */
928 size_t n_needed;
929 size_t n_read;
930
931 n_needed = n - *n_bytes_in_buffer;
932 n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, in_stream);
933 *n_bytes_in_buffer += n_read;
934 if (n_read == n_needed)
935 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000936 /* error check is done in check_and_close */
937 check_and_close();
938 open_next_file();
Denis Vlasenko1114de72006-10-10 23:26:05 +0000939 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000940}
941
942/* Return the least common multiple of the sizes associated
943 with the format specs. */
944
945static int
946get_lcm(void)
947{
948 size_t i;
949 int l_c_m = 1;
950
951 for (i = 0; i < n_specs; i++)
952 l_c_m = lcm(l_c_m, width_bytes[(int) spec[i].size]);
953 return l_c_m;
954}
955
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000956#if ENABLE_GETOPT_LONG
Denis Vlasenko1114de72006-10-10 23:26:05 +0000957/* If S is a valid traditional offset specification with an optional
958 leading '+' return nonzero and set *OFFSET to the offset it denotes. */
959
960static int
Denis Vlasenko601ae132006-11-28 23:37:46 +0000961parse_old_offset(const char *s, off_t *offset)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000962{
963 static const struct suffix_mult Bb[] = {
964 { "B", 1024 },
965 { "b", 512 },
Denis Vlasenkof8689632007-07-27 15:06:25 +0000966 { }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000967 };
Denis Vlasenko601ae132006-11-28 23:37:46 +0000968 char *p;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000969 int radix;
970
Denis Vlasenko1114de72006-10-10 23:26:05 +0000971 /* Skip over any leading '+'. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000972 if (s[0] == '+') ++s;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000973
974 /* Determine the radix we'll use to interpret S. If there is a '.',
Denis Vlasenko601ae132006-11-28 23:37:46 +0000975 * it's decimal, otherwise, if the string begins with '0X'or '0x',
976 * it's hexadecimal, else octal. */
977 p = strchr(s, '.');
978 radix = 8;
979 if (p) {
980 p[0] = '\0'; /* cheating */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000981 radix = 10;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000982 } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
983 radix = 16;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000984
Denis Vlasenko601ae132006-11-28 23:37:46 +0000985 *offset = xstrtooff_sfx(s, radix, Bb);
986 if (p) p[0] = '.';
987
988 return (*offset >= 0);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000989}
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000990#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +0000991
992/* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
993 formatted block to standard output, and repeat until the specified
994 maximum number of bytes has been read or until all input has been
995 processed. If the last block read is smaller than BYTES_PER_BLOCK
996 and its size is not a multiple of the size associated with a format
997 spec, extend the input block with zero bytes until its length is a
998 multiple of all format spec sizes. Write the final block. Finally,
999 write on a line by itself the offset of the byte after the last byte
Denis Vlasenko97bd0e02008-02-08 15:41:01 +00001000 read. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001001
Denis Vlasenko601ae132006-11-28 23:37:46 +00001002static void
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001003dump(off_t current_offset, off_t end_offset)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001004{
1005 char *block[2];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001006 int idx;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001007 size_t n_bytes_read;
1008
1009 block[0] = xmalloc(2*bytes_per_block);
1010 block[1] = block[0] + bytes_per_block;
1011
Denis Vlasenko1114de72006-10-10 23:26:05 +00001012 idx = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001013 if (limit_bytes_to_format) {
1014 while (1) {
1015 size_t n_needed;
1016 if (current_offset >= end_offset) {
1017 n_bytes_read = 0;
1018 break;
1019 }
1020 n_needed = MIN(end_offset - current_offset,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001021 (off_t) bytes_per_block);
1022 read_block(n_needed, block[idx], &n_bytes_read);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001023 if (n_bytes_read < bytes_per_block)
1024 break;
1025 assert(n_bytes_read == bytes_per_block);
1026 write_block(current_offset, n_bytes_read,
1027 block[!idx], block[idx]);
1028 current_offset += n_bytes_read;
1029 idx = !idx;
1030 }
1031 } else {
1032 while (1) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001033 read_block(bytes_per_block, block[idx], &n_bytes_read);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001034 if (n_bytes_read < bytes_per_block)
1035 break;
1036 assert(n_bytes_read == bytes_per_block);
1037 write_block(current_offset, n_bytes_read,
1038 block[!idx], block[idx]);
1039 current_offset += n_bytes_read;
1040 idx = !idx;
1041 }
1042 }
1043
1044 if (n_bytes_read > 0) {
1045 int l_c_m;
1046 size_t bytes_to_write;
1047
1048 l_c_m = get_lcm();
1049
1050 /* Make bytes_to_write the smallest multiple of l_c_m that
1051 is at least as large as n_bytes_read. */
1052 bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m);
1053
1054 memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
1055 write_block(current_offset, bytes_to_write,
1056 block[!idx], block[idx]);
1057 current_offset += n_bytes_read;
1058 }
1059
1060 format_address(current_offset, '\n');
1061
1062 if (limit_bytes_to_format && current_offset >= end_offset)
Denis Vlasenko601ae132006-11-28 23:37:46 +00001063 check_and_close();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001064
1065 free(block[0]);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001066}
1067
Denis Vlasenko601ae132006-11-28 23:37:46 +00001068/* Read a single byte into *C from the concatenation of the input files
1069 named in the global array FILE_LIST. On the first call to this
1070 function, the global variable IN_STREAM is expected to be an open
1071 stream associated with the input file INPUT_FILENAME. If IN_STREAM
1072 is at end-of-file, close it and update the global variables IN_STREAM
1073 and INPUT_FILENAME so they correspond to the next file in the list.
1074 Then try to read a byte from the newly opened file. Repeat if
1075 necessary until EOF is reached for the last file in FILE_LIST, then
Denis Vlasenko97bd0e02008-02-08 15:41:01 +00001076 set *C to EOF and return. Subsequent calls do likewise. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001077
1078static void
1079read_char(int *c)
1080{
1081 while (in_stream) { /* !EOF */
1082 *c = fgetc(in_stream);
1083 if (*c != EOF)
1084 return;
1085 check_and_close();
1086 open_next_file();
1087 }
1088 *c = EOF;
1089}
1090
1091/* Read N bytes into BLOCK from the concatenation of the input files
1092 named in the global array FILE_LIST. On the first call to this
1093 function, the global variable IN_STREAM is expected to be an open
1094 stream associated with the input file INPUT_FILENAME. If all N
1095 bytes cannot be read from IN_STREAM, close IN_STREAM and update
1096 the global variables IN_STREAM and INPUT_FILENAME. Then try to
1097 read the remaining bytes from the newly opened file. Repeat if
1098 necessary until EOF is reached for the last file in FILE_LIST.
1099 On subsequent calls, don't modify BLOCK and return zero. Set
1100 *N_BYTES_IN_BUFFER to the number of bytes read. If an error occurs,
1101 it will be detected through ferror when the stream is about to be
1102 closed. If there is an error, give a message but continue reading
1103 as usual and return nonzero. Otherwise return zero. */
1104
Denis Vlasenko1114de72006-10-10 23:26:05 +00001105/* STRINGS mode. Find each "string constant" in the input.
1106 A string constant is a run of at least 'string_min' ASCII
1107 graphic (or formatting) characters terminated by a null.
1108 Based on a function written by Richard Stallman for a
Denis Vlasenko97bd0e02008-02-08 15:41:01 +00001109 traditional version of od. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001110
Denis Vlasenko601ae132006-11-28 23:37:46 +00001111static void
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001112dump_strings(off_t address, off_t end_offset)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001113{
1114 size_t bufsize = MAX(100, string_min);
1115 char *buf = xmalloc(bufsize);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001116
Denis Vlasenko1114de72006-10-10 23:26:05 +00001117 while (1) {
1118 size_t i;
1119 int c;
1120
1121 /* See if the next 'string_min' chars are all printing chars. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001122 tryline:
1123 if (limit_bytes_to_format && (end_offset - string_min <= address))
Denis Vlasenko1114de72006-10-10 23:26:05 +00001124 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001125 i = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001126 while (!limit_bytes_to_format || address < end_offset) {
1127 if (i == bufsize) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001128 bufsize += bufsize/8;
1129 buf = xrealloc(buf, bufsize);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001130 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001131 read_char(&c);
1132 if (c < 0) { /* EOF */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001133 free(buf);
Denis Vlasenko601ae132006-11-28 23:37:46 +00001134 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001135 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001136 address++;
1137 if (!c)
1138 break;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001139 if (!ISPRINT(c))
1140 goto tryline; /* It isn't; give up on this string. */
1141 buf[i++] = c; /* String continues; store it all. */
1142 }
1143
Denis Vlasenko601ae132006-11-28 23:37:46 +00001144 if (i < string_min) /* Too short! */
1145 goto tryline;
1146
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001147 /* If we get here, the string is all printable and NUL-terminated,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001148 * so print it. It is all in 'buf' and 'i' is its length. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001149 buf[i] = 0;
1150 format_address(address - i - 1, ' ');
1151
1152 for (i = 0; (c = buf[i]); i++) {
1153 switch (c) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001154 case '\007': fputs("\\a", stdout); break;
1155 case '\b': fputs("\\b", stdout); break;
1156 case '\f': fputs("\\f", stdout); break;
1157 case '\n': fputs("\\n", stdout); break;
1158 case '\r': fputs("\\r", stdout); break;
1159 case '\t': fputs("\\t", stdout); break;
1160 case '\v': fputs("\\v", stdout); break;
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001161 default: putchar(c);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001162 }
1163 }
1164 putchar('\n');
1165 }
1166
1167 /* We reach this point only if we search through
1168 (max_bytes_to_format - string_min) bytes before reaching EOF. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001169 free(buf);
1170
Denis Vlasenko601ae132006-11-28 23:37:46 +00001171 check_and_close();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001172}
1173
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001174int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko06af2162007-02-03 17:28:39 +00001175int od_main(int argc, char **argv)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001176{
1177 static const struct suffix_mult bkm[] = {
1178 { "b", 512 },
1179 { "k", 1024 },
1180 { "m", 1024*1024 },
Denis Vlasenkof8689632007-07-27 15:06:25 +00001181 { }
Denis Vlasenko1114de72006-10-10 23:26:05 +00001182 };
Denis Vlasenko601ae132006-11-28 23:37:46 +00001183 enum {
1184 OPT_A = 1 << 0,
1185 OPT_N = 1 << 1,
1186 OPT_a = 1 << 2,
1187 OPT_b = 1 << 3,
1188 OPT_c = 1 << 4,
1189 OPT_d = 1 << 5,
1190 OPT_f = 1 << 6,
1191 OPT_h = 1 << 7,
1192 OPT_i = 1 << 8,
1193 OPT_j = 1 << 9,
1194 OPT_l = 1 << 10,
1195 OPT_o = 1 << 11,
1196 OPT_t = 1 << 12,
1197 OPT_v = 1 << 13,
1198 OPT_x = 1 << 14,
1199 OPT_s = 1 << 15,
1200 OPT_S = 1 << 16,
1201 OPT_w = 1 << 17,
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001202 OPT_traditional = (1 << 18) * ENABLE_GETOPT_LONG,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001203 };
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001204#if ENABLE_GETOPT_LONG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001205 static const char od_longopts[] ALIGN1 =
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +00001206 "skip-bytes\0" Required_argument "j"
1207 "address-radix\0" Required_argument "A"
1208 "read-bytes\0" Required_argument "N"
1209 "format\0" Required_argument "t"
1210 "output-duplicates\0" No_argument "v"
1211 "strings\0" Optional_argument "S"
1212 "width\0" Optional_argument "w"
1213 "traditional\0" No_argument "\xff"
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001214 ;
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001215#endif
Denis Vlasenko601ae132006-11-28 23:37:46 +00001216 char *str_A, *str_N, *str_j, *str_S;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001217 llist_t *lst_t = NULL;
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001218 unsigned opt;
1219 int l_c_m;
1220 /* The old-style 'pseudo starting address' to be printed in parentheses
1221 after any true address. */
1222 off_t pseudo_start = pseudo_start; // for gcc
1223 /* The number of input bytes to skip before formatting and writing. */
1224 off_t n_bytes_to_skip = 0;
1225 /* The offset of the first byte after the last byte to be formatted. */
1226 off_t end_offset = 0;
1227 /* The maximum number of bytes that will be formatted. */
1228 off_t max_bytes_to_format = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001229
1230 spec = NULL;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001231 format_address = format_address_std;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001232 address_base_char = 'o';
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001233 address_pad_len_char = '7';
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +00001234 /* flag_dump_strings = 0; - already is */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001235
Denis Vlasenko601ae132006-11-28 23:37:46 +00001236 /* Parse command line */
Denis Vlasenko1d426652008-03-17 09:09:09 +00001237 opt_complementary = "w+:t::"; /* -w N, -t is a list */
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001238#if ENABLE_GETOPT_LONG
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +00001239 applet_long_options = od_longopts;
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001240#endif
Denis Vlasenkofe7cd642007-08-18 15:32:12 +00001241 opt = getopt32(argv, "A:N:abcdfhij:lot:vxsS:"
Denis Vlasenko601ae132006-11-28 23:37:46 +00001242 "w::", // -w with optional param
1243 // -S was -s and also had optional parameter
1244 // but in coreutils 6.3 it was renamed and now has
1245 // _mandatory_ parameter
Denis Vlasenko1d426652008-03-17 09:09:09 +00001246 &str_A, &str_N, &str_j, &lst_t, &str_S, &bytes_per_block);
Denis Vlasenko601ae132006-11-28 23:37:46 +00001247 argc -= optind;
1248 argv += optind;
1249 if (opt & OPT_A) {
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001250 static const char doxn[] ALIGN1 = "doxn";
1251 static const char doxn_address_base_char[] ALIGN1 = {
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001252 'u', 'o', 'x', /* '?' fourth one is not important */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001253 };
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001254 static const uint8_t doxn_address_pad_len_char[] ALIGN1 = {
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001255 '7', '7', '6', /* '?' */
1256 };
Denis Vlasenko601ae132006-11-28 23:37:46 +00001257 char *p;
1258 int pos;
1259 p = strchr(doxn, str_A[0]);
1260 if (!p)
1261 bb_error_msg_and_die("bad output address radix "
1262 "'%c' (must be [doxn])", str_A[0]);
1263 pos = p - doxn;
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001264 if (pos == 3) format_address = format_address_none;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001265 address_base_char = doxn_address_base_char[pos];
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001266 address_pad_len_char = doxn_address_pad_len_char[pos];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001267 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001268 if (opt & OPT_N) {
1269 limit_bytes_to_format = 1;
1270 max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm);
1271 }
1272 if (opt & OPT_a) decode_format_string("a");
1273 if (opt & OPT_b) decode_format_string("oC");
1274 if (opt & OPT_c) decode_format_string("c");
1275 if (opt & OPT_d) decode_format_string("u2");
1276 if (opt & OPT_f) decode_format_string("fF");
1277 if (opt & OPT_h) decode_format_string("x2");
1278 if (opt & OPT_i) decode_format_string("d2");
1279 if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm);
1280 if (opt & OPT_l) decode_format_string("d4");
1281 if (opt & OPT_o) decode_format_string("o2");
1282 //if (opt & OPT_t)...
Denis Vlasenko601ae132006-11-28 23:37:46 +00001283 while (lst_t) {
1284 decode_format_string(lst_t->data);
1285 lst_t = lst_t->link;
1286 }
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
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001310#if ENABLE_GETOPT_LONG
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}