blob: 942cf038eb8184edbdaf936b91c42c91d38a175e [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
511 a description of that format in *TSPEC, make *NEXT point at the
512 character following the just-decoded format (if *NEXT is non-NULL),
Denis Vlasenko601ae132006-11-28 23:37:46 +0000513 and return zero. For example, if S were "d4afL"
514 *NEXT would be set to "afL" 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 Vlasenko601ae132006-11-28 23:37:46 +0000524static void
Denis Vlasenko1114de72006-10-10 23:26:05 +0000525decode_one_format(const char *s_orig, const char *s, const char **next,
526 struct tspec *tspec)
527{
528 enum size_spec size_spec;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000529 unsigned size;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000530 enum output_format fmt;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000531 const char *p;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000532 char *end;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000533 char *fmt_string = NULL;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000534 void (*print_function) (size_t, const char *, const char *);
535 unsigned c;
536 unsigned field_width = 0;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000537 int pos;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000538
539 assert(tspec != NULL);
540
541 switch (*s) {
542 case 'd':
543 case 'o':
544 case 'u':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000545 case 'x': {
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000546 static const char CSIL[] ALIGN1 = "CSIL";
Denis Vlasenko601ae132006-11-28 23:37:46 +0000547
548 c = *s++;
549 p = strchr(CSIL, *s);
550 if (!p) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000551 size = sizeof(int);
552 if (isdigit(s[0])) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000553 size = bb_strtou(s, &end, 0);
554 if (errno == ERANGE
555 || MAX_INTEGRAL_TYPE_SIZE < size
Denis Vlasenko1114de72006-10-10 23:26:05 +0000556 || integral_type_size[size] == NO_SIZE
557 ) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000558 bb_error_msg_and_die("invalid type string '%s'; "
559 "%u-byte %s type is not supported",
560 s_orig, size, "integral");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000561 }
562 s = end;
563 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000564 } else {
565 static const uint8_t CSIL_sizeof[] = {
566 sizeof(char),
567 sizeof(short),
568 sizeof(int),
569 sizeof(long),
570 };
571 size = CSIL_sizeof[p - CSIL];
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
719 if (next != NULL)
720 *next = s;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000721}
722
723/* Decode the modern od format string S. Append the decoded
724 representation to the global array SPEC, reallocating SPEC if
Denis Vlasenko97bd0e02008-02-08 15:41:01 +0000725 necessary. */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000726
Denis Vlasenko601ae132006-11-28 23:37:46 +0000727static void
Denis Vlasenko1114de72006-10-10 23:26:05 +0000728decode_format_string(const char *s)
729{
730 const char *s_orig = s;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000731
732 while (*s != '\0') {
733 struct tspec tspec;
734 const char *next;
735
Denis Vlasenko601ae132006-11-28 23:37:46 +0000736 decode_one_format(s_orig, s, &next, &tspec);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000737
738 assert(s != next);
739 s = next;
740 n_specs++;
741 spec = xrealloc(spec, n_specs * sizeof(*spec));
742 memcpy(&spec[n_specs-1], &tspec, sizeof *spec);
743 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000744}
745
746/* Given a list of one or more input filenames FILE_LIST, set the global
747 file pointer IN_STREAM to position N_SKIP in the concatenation of
748 those files. If any file operation fails or if there are fewer than
749 N_SKIP bytes in the combined input, give an error message and return
750 nonzero. When possible, use seek rather than read operations to
751 advance IN_STREAM. */
752
Denis Vlasenko601ae132006-11-28 23:37:46 +0000753static void
754skip(off_t n_skip)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000755{
Denis Vlasenko1114de72006-10-10 23:26:05 +0000756 if (n_skip == 0)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000757 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000758
Denis Vlasenko601ae132006-11-28 23:37:46 +0000759 while (in_stream) { /* !EOF */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000760 struct stat file_stats;
761
762 /* First try seeking. For large offsets, this extra work is
763 worthwhile. If the offset is below some threshold it may be
764 more efficient to move the pointer by reading. There are two
765 issues when trying to seek:
766 - the file must be seekable.
767 - before seeking to the specified position, make sure
768 that the new position is in the current file.
769 Try to do that by getting file's size using fstat.
770 But that will work only for regular files. */
771
Denis Vlasenko1114de72006-10-10 23:26:05 +0000772 /* The st_size field is valid only for regular files
773 (and for symbolic links, which cannot occur here).
774 If the number of bytes left to skip is at least
775 as large as the size of the current file, we can
776 decrement n_skip and go on to the next file. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000777 if (fstat(fileno(in_stream), &file_stats) == 0
Denis Vlasenko97bd0e02008-02-08 15:41:01 +0000778 && S_ISREG(file_stats.st_mode) && file_stats.st_size > 0
Denis Vlasenko601ae132006-11-28 23:37:46 +0000779 ) {
780 if (file_stats.st_size < n_skip) {
781 n_skip -= file_stats.st_size;
Denis Vlasenko97bd0e02008-02-08 15:41:01 +0000782 /* take "check & close / open_next" route */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000783 } else {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000784 if (fseeko(in_stream, n_skip, SEEK_CUR) != 0)
785 ioerror = 1;
786 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000787 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000788 } else {
Denis Vlasenko97bd0e02008-02-08 15:41:01 +0000789 /* If it's not a regular file with positive size,
Denis Vlasenko601ae132006-11-28 23:37:46 +0000790 position the file pointer by reading. */
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +0000791 char buf[1024];
792 size_t n_bytes_to_read = 1024;
793 size_t n_bytes_read;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000794
Denis Vlasenko601ae132006-11-28 23:37:46 +0000795 while (n_skip > 0) {
796 if (n_skip < n_bytes_to_read)
797 n_bytes_to_read = n_skip;
798 n_bytes_read = fread(buf, 1, n_bytes_to_read, in_stream);
799 n_skip -= n_bytes_read;
800 if (n_bytes_read != n_bytes_to_read)
Denis Vlasenko8e858e22007-03-07 09:35:43 +0000801 break; /* EOF on this file or error */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000802 }
803 }
804 if (n_skip == 0)
805 return;
806
807 check_and_close();
808 open_next_file();
Denis Vlasenko1114de72006-10-10 23:26:05 +0000809 }
810
Denis Vlasenko601ae132006-11-28 23:37:46 +0000811 if (n_skip)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000812 bb_error_msg_and_die("cannot skip past end of combined input");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000813}
814
Denis Vlasenko601ae132006-11-28 23:37:46 +0000815
816typedef void FN_format_address(off_t address, char c);
817
Denis Vlasenko1114de72006-10-10 23:26:05 +0000818static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000819format_address_none(off_t address ATTRIBUTE_UNUSED, char c ATTRIBUTE_UNUSED)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000820{
821}
822
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000823static char address_fmt[] ALIGN1 = "%0n"OFF_FMT"xc";
Denis Vlasenko601ae132006-11-28 23:37:46 +0000824/* Corresponds to 'x' above */
825#define address_base_char address_fmt[sizeof(address_fmt)-3]
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000826/* Corresponds to 'n' above */
827#define address_pad_len_char address_fmt[2]
Denis Vlasenko601ae132006-11-28 23:37:46 +0000828
Denis Vlasenko1114de72006-10-10 23:26:05 +0000829static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000830format_address_std(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000831{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000832 /* Corresponds to 'c' */
833 address_fmt[sizeof(address_fmt)-2] = c;
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000834 printf(address_fmt, address);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000835}
836
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000837#if ENABLE_GETOPT_LONG
838/* only used with --traditional */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000839static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000840format_address_paren(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000841{
842 putchar('(');
843 format_address_std(address, ')');
Denis Vlasenko0f5905e2006-12-17 19:21:13 +0000844 if (c) putchar(c);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000845}
846
847static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000848format_address_label(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000849{
850 format_address_std(address, ' ');
851 format_address_paren(address + pseudo_offset, c);
852}
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000853#endif
Denis Vlasenko601ae132006-11-28 23:37:46 +0000854
855static void
856dump_hexl_mode_trailer(size_t n_bytes, const char *block)
857{
858 fputs(" >", stdout);
859 while (n_bytes--) {
860 unsigned c = *(unsigned char *) block++;
861 c = (ISPRINT(c) ? c : '.');
862 putchar(c);
863 }
864 putchar('<');
865}
866
Denis Vlasenko1114de72006-10-10 23:26:05 +0000867/* Write N_BYTES bytes from CURR_BLOCK to standard output once for each
868 of the N_SPEC format specs. CURRENT_OFFSET is the byte address of
869 CURR_BLOCK in the concatenation of input files, and it is printed
870 (optionally) only before the output line associated with the first
871 format spec. When duplicate blocks are being abbreviated, the output
872 for a sequence of identical input blocks is the output for the first
873 block followed by an asterisk alone on a line. It is valid to compare
874 the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK.
875 That condition may be false only for the last input block -- and then
876 only when it has not been padded to length BYTES_PER_BLOCK. */
877
878static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000879write_block(off_t current_offset, size_t n_bytes,
Denis Vlasenko1114de72006-10-10 23:26:05 +0000880 const char *prev_block, const char *curr_block)
881{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000882 static char first = 1;
883 static char prev_pair_equal = 0;
884 size_t i;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000885
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +0000886 if (!verbose && !first
Denis Vlasenko1114de72006-10-10 23:26:05 +0000887 && n_bytes == bytes_per_block
888 && memcmp(prev_block, curr_block, bytes_per_block) == 0
889 ) {
890 if (prev_pair_equal) {
891 /* The two preceding blocks were equal, and the current
892 block is the same as the last one, so print nothing. */
893 } else {
894 puts("*");
895 prev_pair_equal = 1;
896 }
897 } else {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000898 first = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000899 prev_pair_equal = 0;
900 for (i = 0; i < n_specs; i++) {
901 if (i == 0)
902 format_address(current_offset, '\0');
903 else
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000904 printf("%*s", address_pad_len_char - '0', "");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000905 (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string);
906 if (spec[i].hexl_mode_trailer) {
907 /* space-pad out to full line width, then dump the trailer */
908 int datum_width = width_bytes[spec[i].size];
909 int blank_fields = (bytes_per_block - n_bytes) / datum_width;
910 int field_width = spec[i].field_width + 1;
911 printf("%*s", blank_fields * field_width, "");
912 dump_hexl_mode_trailer(n_bytes, curr_block);
913 }
914 putchar('\n');
915 }
916 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000917}
918
Denis Vlasenko601ae132006-11-28 23:37:46 +0000919static void
Denis Vlasenko1114de72006-10-10 23:26:05 +0000920read_block(size_t n, char *block, size_t *n_bytes_in_buffer)
921{
Denis Vlasenko1114de72006-10-10 23:26:05 +0000922 assert(0 < n && n <= bytes_per_block);
923
924 *n_bytes_in_buffer = 0;
925
926 if (n == 0)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000927 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000928
929 while (in_stream != NULL) { /* EOF. */
930 size_t n_needed;
931 size_t n_read;
932
933 n_needed = n - *n_bytes_in_buffer;
934 n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, in_stream);
935 *n_bytes_in_buffer += n_read;
936 if (n_read == n_needed)
937 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000938 /* error check is done in check_and_close */
939 check_and_close();
940 open_next_file();
Denis Vlasenko1114de72006-10-10 23:26:05 +0000941 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000942}
943
944/* Return the least common multiple of the sizes associated
945 with the format specs. */
946
947static int
948get_lcm(void)
949{
950 size_t i;
951 int l_c_m = 1;
952
953 for (i = 0; i < n_specs; i++)
954 l_c_m = lcm(l_c_m, width_bytes[(int) spec[i].size]);
955 return l_c_m;
956}
957
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000958#if ENABLE_GETOPT_LONG
Denis Vlasenko1114de72006-10-10 23:26:05 +0000959/* If S is a valid traditional offset specification with an optional
960 leading '+' return nonzero and set *OFFSET to the offset it denotes. */
961
962static int
Denis Vlasenko601ae132006-11-28 23:37:46 +0000963parse_old_offset(const char *s, off_t *offset)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000964{
965 static const struct suffix_mult Bb[] = {
966 { "B", 1024 },
967 { "b", 512 },
Denis Vlasenkof8689632007-07-27 15:06:25 +0000968 { }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000969 };
Denis Vlasenko601ae132006-11-28 23:37:46 +0000970 char *p;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000971 int radix;
972
Denis Vlasenko1114de72006-10-10 23:26:05 +0000973 /* Skip over any leading '+'. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000974 if (s[0] == '+') ++s;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000975
976 /* Determine the radix we'll use to interpret S. If there is a '.',
Denis Vlasenko601ae132006-11-28 23:37:46 +0000977 * it's decimal, otherwise, if the string begins with '0X'or '0x',
978 * it's hexadecimal, else octal. */
979 p = strchr(s, '.');
980 radix = 8;
981 if (p) {
982 p[0] = '\0'; /* cheating */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000983 radix = 10;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000984 } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
985 radix = 16;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000986
Denis Vlasenko601ae132006-11-28 23:37:46 +0000987 *offset = xstrtooff_sfx(s, radix, Bb);
988 if (p) p[0] = '.';
989
990 return (*offset >= 0);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000991}
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000992#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +0000993
994/* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
995 formatted block to standard output, and repeat until the specified
996 maximum number of bytes has been read or until all input has been
997 processed. If the last block read is smaller than BYTES_PER_BLOCK
998 and its size is not a multiple of the size associated with a format
999 spec, extend the input block with zero bytes until its length is a
1000 multiple of all format spec sizes. Write the final block. Finally,
1001 write on a line by itself the offset of the byte after the last byte
Denis Vlasenko97bd0e02008-02-08 15:41:01 +00001002 read. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001003
Denis Vlasenko601ae132006-11-28 23:37:46 +00001004static void
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001005dump(off_t current_offset, off_t end_offset)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001006{
1007 char *block[2];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001008 int idx;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001009 size_t n_bytes_read;
1010
1011 block[0] = xmalloc(2*bytes_per_block);
1012 block[1] = block[0] + bytes_per_block;
1013
Denis Vlasenko1114de72006-10-10 23:26:05 +00001014 idx = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001015 if (limit_bytes_to_format) {
1016 while (1) {
1017 size_t n_needed;
1018 if (current_offset >= end_offset) {
1019 n_bytes_read = 0;
1020 break;
1021 }
1022 n_needed = MIN(end_offset - current_offset,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001023 (off_t) bytes_per_block);
1024 read_block(n_needed, block[idx], &n_bytes_read);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001025 if (n_bytes_read < bytes_per_block)
1026 break;
1027 assert(n_bytes_read == bytes_per_block);
1028 write_block(current_offset, n_bytes_read,
1029 block[!idx], block[idx]);
1030 current_offset += n_bytes_read;
1031 idx = !idx;
1032 }
1033 } else {
1034 while (1) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001035 read_block(bytes_per_block, block[idx], &n_bytes_read);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001036 if (n_bytes_read < bytes_per_block)
1037 break;
1038 assert(n_bytes_read == bytes_per_block);
1039 write_block(current_offset, n_bytes_read,
1040 block[!idx], block[idx]);
1041 current_offset += n_bytes_read;
1042 idx = !idx;
1043 }
1044 }
1045
1046 if (n_bytes_read > 0) {
1047 int l_c_m;
1048 size_t bytes_to_write;
1049
1050 l_c_m = get_lcm();
1051
1052 /* Make bytes_to_write the smallest multiple of l_c_m that
1053 is at least as large as n_bytes_read. */
1054 bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m);
1055
1056 memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
1057 write_block(current_offset, bytes_to_write,
1058 block[!idx], block[idx]);
1059 current_offset += n_bytes_read;
1060 }
1061
1062 format_address(current_offset, '\n');
1063
1064 if (limit_bytes_to_format && current_offset >= end_offset)
Denis Vlasenko601ae132006-11-28 23:37:46 +00001065 check_and_close();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001066
1067 free(block[0]);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001068}
1069
Denis Vlasenko601ae132006-11-28 23:37:46 +00001070/* Read a single byte into *C from the concatenation of the input files
1071 named in the global array FILE_LIST. On the first call to this
1072 function, the global variable IN_STREAM is expected to be an open
1073 stream associated with the input file INPUT_FILENAME. If IN_STREAM
1074 is at end-of-file, close it and update the global variables IN_STREAM
1075 and INPUT_FILENAME so they correspond to the next file in the list.
1076 Then try to read a byte from the newly opened file. Repeat if
1077 necessary until EOF is reached for the last file in FILE_LIST, then
Denis Vlasenko97bd0e02008-02-08 15:41:01 +00001078 set *C to EOF and return. Subsequent calls do likewise. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001079
1080static void
1081read_char(int *c)
1082{
1083 while (in_stream) { /* !EOF */
1084 *c = fgetc(in_stream);
1085 if (*c != EOF)
1086 return;
1087 check_and_close();
1088 open_next_file();
1089 }
1090 *c = EOF;
1091}
1092
1093/* Read N bytes into BLOCK from the concatenation of the input files
1094 named in the global array FILE_LIST. On the first call to this
1095 function, the global variable IN_STREAM is expected to be an open
1096 stream associated with the input file INPUT_FILENAME. If all N
1097 bytes cannot be read from IN_STREAM, close IN_STREAM and update
1098 the global variables IN_STREAM and INPUT_FILENAME. Then try to
1099 read the remaining bytes from the newly opened file. Repeat if
1100 necessary until EOF is reached for the last file in FILE_LIST.
1101 On subsequent calls, don't modify BLOCK and return zero. Set
1102 *N_BYTES_IN_BUFFER to the number of bytes read. If an error occurs,
1103 it will be detected through ferror when the stream is about to be
1104 closed. If there is an error, give a message but continue reading
1105 as usual and return nonzero. Otherwise return zero. */
1106
Denis Vlasenko1114de72006-10-10 23:26:05 +00001107/* STRINGS mode. Find each "string constant" in the input.
1108 A string constant is a run of at least 'string_min' ASCII
1109 graphic (or formatting) characters terminated by a null.
1110 Based on a function written by Richard Stallman for a
Denis Vlasenko97bd0e02008-02-08 15:41:01 +00001111 traditional version of od. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001112
Denis Vlasenko601ae132006-11-28 23:37:46 +00001113static void
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001114dump_strings(off_t address, off_t end_offset)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001115{
1116 size_t bufsize = MAX(100, string_min);
1117 char *buf = xmalloc(bufsize);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001118
Denis Vlasenko1114de72006-10-10 23:26:05 +00001119 while (1) {
1120 size_t i;
1121 int c;
1122
1123 /* See if the next 'string_min' chars are all printing chars. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001124 tryline:
1125 if (limit_bytes_to_format && (end_offset - string_min <= address))
Denis Vlasenko1114de72006-10-10 23:26:05 +00001126 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001127 i = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001128 while (!limit_bytes_to_format || address < end_offset) {
1129 if (i == bufsize) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001130 bufsize += bufsize/8;
1131 buf = xrealloc(buf, bufsize);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001132 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001133 read_char(&c);
1134 if (c < 0) { /* EOF */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001135 free(buf);
Denis Vlasenko601ae132006-11-28 23:37:46 +00001136 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001137 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001138 address++;
1139 if (!c)
1140 break;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001141 if (!ISPRINT(c))
1142 goto tryline; /* It isn't; give up on this string. */
1143 buf[i++] = c; /* String continues; store it all. */
1144 }
1145
Denis Vlasenko601ae132006-11-28 23:37:46 +00001146 if (i < string_min) /* Too short! */
1147 goto tryline;
1148
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001149 /* If we get here, the string is all printable and NUL-terminated,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001150 * so print it. It is all in 'buf' and 'i' is its length. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001151 buf[i] = 0;
1152 format_address(address - i - 1, ' ');
1153
1154 for (i = 0; (c = buf[i]); i++) {
1155 switch (c) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001156 case '\007': fputs("\\a", stdout); break;
1157 case '\b': fputs("\\b", stdout); break;
1158 case '\f': fputs("\\f", stdout); break;
1159 case '\n': fputs("\\n", stdout); break;
1160 case '\r': fputs("\\r", stdout); break;
1161 case '\t': fputs("\\t", stdout); break;
1162 case '\v': fputs("\\v", stdout); break;
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001163 default: putchar(c);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001164 }
1165 }
1166 putchar('\n');
1167 }
1168
1169 /* We reach this point only if we search through
1170 (max_bytes_to_format - string_min) bytes before reaching EOF. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001171 free(buf);
1172
Denis Vlasenko601ae132006-11-28 23:37:46 +00001173 check_and_close();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001174}
1175
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001176int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko06af2162007-02-03 17:28:39 +00001177int od_main(int argc, char **argv)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001178{
1179 static const struct suffix_mult bkm[] = {
1180 { "b", 512 },
1181 { "k", 1024 },
1182 { "m", 1024*1024 },
Denis Vlasenkof8689632007-07-27 15:06:25 +00001183 { }
Denis Vlasenko1114de72006-10-10 23:26:05 +00001184 };
Denis Vlasenko601ae132006-11-28 23:37:46 +00001185 enum {
1186 OPT_A = 1 << 0,
1187 OPT_N = 1 << 1,
1188 OPT_a = 1 << 2,
1189 OPT_b = 1 << 3,
1190 OPT_c = 1 << 4,
1191 OPT_d = 1 << 5,
1192 OPT_f = 1 << 6,
1193 OPT_h = 1 << 7,
1194 OPT_i = 1 << 8,
1195 OPT_j = 1 << 9,
1196 OPT_l = 1 << 10,
1197 OPT_o = 1 << 11,
1198 OPT_t = 1 << 12,
1199 OPT_v = 1 << 13,
1200 OPT_x = 1 << 14,
1201 OPT_s = 1 << 15,
1202 OPT_S = 1 << 16,
1203 OPT_w = 1 << 17,
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001204 OPT_traditional = (1 << 18) * ENABLE_GETOPT_LONG,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001205 };
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001206#if ENABLE_GETOPT_LONG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001207 static const char od_longopts[] ALIGN1 =
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +00001208 "skip-bytes\0" Required_argument "j"
1209 "address-radix\0" Required_argument "A"
1210 "read-bytes\0" Required_argument "N"
1211 "format\0" Required_argument "t"
1212 "output-duplicates\0" No_argument "v"
1213 "strings\0" Optional_argument "S"
1214 "width\0" Optional_argument "w"
1215 "traditional\0" No_argument "\xff"
Denis Vlasenko990d0f62007-07-24 15:54:42 +00001216 ;
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001217#endif
Denis Vlasenko601ae132006-11-28 23:37:46 +00001218 char *str_A, *str_N, *str_j, *str_S;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001219 llist_t *lst_t = NULL;
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001220 unsigned opt;
1221 int l_c_m;
1222 /* The old-style 'pseudo starting address' to be printed in parentheses
1223 after any true address. */
1224 off_t pseudo_start = pseudo_start; // for gcc
1225 /* The number of input bytes to skip before formatting and writing. */
1226 off_t n_bytes_to_skip = 0;
1227 /* The offset of the first byte after the last byte to be formatted. */
1228 off_t end_offset = 0;
1229 /* The maximum number of bytes that will be formatted. */
1230 off_t max_bytes_to_format = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001231
1232 spec = NULL;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001233 format_address = format_address_std;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001234 address_base_char = 'o';
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001235 address_pad_len_char = '7';
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +00001236 /* flag_dump_strings = 0; - already is */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001237
Denis Vlasenko601ae132006-11-28 23:37:46 +00001238 /* Parse command line */
Denis Vlasenko1d426652008-03-17 09:09:09 +00001239 opt_complementary = "w+:t::"; /* -w N, -t is a list */
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001240#if ENABLE_GETOPT_LONG
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +00001241 applet_long_options = od_longopts;
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001242#endif
Denis Vlasenkofe7cd642007-08-18 15:32:12 +00001243 opt = getopt32(argv, "A:N:abcdfhij:lot:vxsS:"
Denis Vlasenko601ae132006-11-28 23:37:46 +00001244 "w::", // -w with optional param
1245 // -S was -s and also had optional parameter
1246 // but in coreutils 6.3 it was renamed and now has
1247 // _mandatory_ parameter
Denis Vlasenko1d426652008-03-17 09:09:09 +00001248 &str_A, &str_N, &str_j, &lst_t, &str_S, &bytes_per_block);
Denis Vlasenko601ae132006-11-28 23:37:46 +00001249 argc -= optind;
1250 argv += optind;
1251 if (opt & OPT_A) {
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001252 static const char doxn[] ALIGN1 = "doxn";
1253 static const char doxn_address_base_char[] ALIGN1 = {
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001254 'u', 'o', 'x', /* '?' fourth one is not important */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001255 };
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001256 static const uint8_t doxn_address_pad_len_char[] ALIGN1 = {
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001257 '7', '7', '6', /* '?' */
1258 };
Denis Vlasenko601ae132006-11-28 23:37:46 +00001259 char *p;
1260 int pos;
1261 p = strchr(doxn, str_A[0]);
1262 if (!p)
1263 bb_error_msg_and_die("bad output address radix "
1264 "'%c' (must be [doxn])", str_A[0]);
1265 pos = p - doxn;
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001266 if (pos == 3) format_address = format_address_none;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001267 address_base_char = doxn_address_base_char[pos];
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001268 address_pad_len_char = doxn_address_pad_len_char[pos];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001269 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001270 if (opt & OPT_N) {
1271 limit_bytes_to_format = 1;
1272 max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm);
1273 }
1274 if (opt & OPT_a) decode_format_string("a");
1275 if (opt & OPT_b) decode_format_string("oC");
1276 if (opt & OPT_c) decode_format_string("c");
1277 if (opt & OPT_d) decode_format_string("u2");
1278 if (opt & OPT_f) decode_format_string("fF");
1279 if (opt & OPT_h) decode_format_string("x2");
1280 if (opt & OPT_i) decode_format_string("d2");
1281 if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm);
1282 if (opt & OPT_l) decode_format_string("d4");
1283 if (opt & OPT_o) decode_format_string("o2");
1284 //if (opt & OPT_t)...
Denis Vlasenko601ae132006-11-28 23:37:46 +00001285 while (lst_t) {
1286 decode_format_string(lst_t->data);
1287 lst_t = lst_t->link;
1288 }
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +00001289 if (opt & OPT_v) verbose = 1;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001290 if (opt & OPT_x) decode_format_string("x2");
1291 if (opt & OPT_s) decode_format_string("d2");
1292 if (opt & OPT_S) {
1293 string_min = 3;
1294 string_min = xstrtou_sfx(str_S, 0, bkm);
1295 flag_dump_strings = 1;
1296 }
1297 //if (opt & OPT_w)...
1298 //if (opt & OPT_traditional)...
Denis Vlasenko1114de72006-10-10 23:26:05 +00001299
1300 if (flag_dump_strings && n_specs > 0)
1301 bb_error_msg_and_die("no type may be specified when dumping strings");
1302
Denis Vlasenko1114de72006-10-10 23:26:05 +00001303 /* If the --traditional option is used, there may be from
Denis Vlasenko601ae132006-11-28 23:37:46 +00001304 * 0 to 3 remaining command line arguments; handle each case
1305 * separately.
Denis Vlasenko8e858e22007-03-07 09:35:43 +00001306 * od [file] [[+]offset[.][b] [[+]label[.][b]]]
Denis Vlasenko601ae132006-11-28 23:37:46 +00001307 * The offset and pseudo_start have the same syntax.
1308 *
1309 * FIXME: POSIX 1003.1-2001 with XSI requires support for the
1310 * traditional syntax even if --traditional is not given. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001311
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001312#if ENABLE_GETOPT_LONG
Denis Vlasenko601ae132006-11-28 23:37:46 +00001313 if (opt & OPT_traditional) {
1314 off_t o1, o2;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001315
Denis Vlasenko601ae132006-11-28 23:37:46 +00001316 if (argc == 1) {
1317 if (parse_old_offset(argv[0], &o1)) {
1318 n_bytes_to_skip = o1;
1319 --argc;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001320 ++argv;
1321 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001322 } else if (argc == 2) {
1323 if (parse_old_offset(argv[0], &o1)
1324 && parse_old_offset(argv[1], &o2)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001325 ) {
1326 n_bytes_to_skip = o1;
1327 flag_pseudo_start = 1;
1328 pseudo_start = o2;
1329 argv += 2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001330 argc -= 2;
1331 } else if (parse_old_offset(argv[1], &o2)) {
Denis Vlasenko1114de72006-10-10 23:26:05 +00001332 n_bytes_to_skip = o2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001333 --argc;
1334 argv[1] = argv[0];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001335 ++argv;
1336 } else {
1337 bb_error_msg_and_die("invalid second operand "
Denis Vlasenko601ae132006-11-28 23:37:46 +00001338 "in compatibility mode '%s'", argv[1]);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001339 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001340 } else if (argc == 3) {
1341 if (parse_old_offset(argv[1], &o1)
1342 && parse_old_offset(argv[2], &o2)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001343 ) {
1344 n_bytes_to_skip = o1;
1345 flag_pseudo_start = 1;
1346 pseudo_start = o2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001347 argv[2] = argv[0];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001348 argv += 2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001349 argc -= 2;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001350 } else {
1351 bb_error_msg_and_die("in compatibility mode "
1352 "the last two arguments must be offsets");
1353 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001354 } else if (argc > 3) {
Denis Vlasenko1114de72006-10-10 23:26:05 +00001355 bb_error_msg_and_die("compatibility mode supports "
1356 "at most three arguments");
1357 }
1358
1359 if (flag_pseudo_start) {
1360 if (format_address == format_address_none) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001361 address_base_char = 'o';
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001362 address_pad_len_char = '7';
Denis Vlasenko1114de72006-10-10 23:26:05 +00001363 format_address = format_address_paren;
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001364 } else
1365 format_address = format_address_label;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001366 }
1367 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001368#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +00001369
1370 if (limit_bytes_to_format) {
1371 end_offset = n_bytes_to_skip + max_bytes_to_format;
1372 if (end_offset < n_bytes_to_skip)
1373 bb_error_msg_and_die("skip-bytes + read-bytes is too large");
1374 }
1375
1376 if (n_specs == 0) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001377 decode_format_string("o2");
Denis Vlasenko1114de72006-10-10 23:26:05 +00001378 n_specs = 1;
1379 }
1380
Denis Vlasenko601ae132006-11-28 23:37:46 +00001381 /* If no files were listed on the command line,
1382 set the global pointer FILE_LIST so that it
1383 references the null-terminated list of one name: "-". */
Denis Vlasenkod0a071a2008-03-17 09:33:45 +00001384 file_list = bb_argv_dash;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001385 if (argc > 0) {
Denis Vlasenko1114de72006-10-10 23:26:05 +00001386 /* Set the global pointer FILE_LIST so that it
1387 references the first file-argument on the command-line. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001388 file_list = (char const *const *) argv;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001389 }
1390
1391 /* open the first input file */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001392 open_next_file();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001393 /* skip over any unwanted header bytes */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001394 skip(n_bytes_to_skip);
1395 if (!in_stream)
Bernhard Reutner-Fischer7c2db5c2007-11-16 12:39:16 +00001396 return EXIT_FAILURE;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001397
1398 pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0);
1399
1400 /* Compute output block length. */
1401 l_c_m = get_lcm();
1402
Denis Vlasenko601ae132006-11-28 23:37:46 +00001403 if (opt & OPT_w) { /* -w: width */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001404 if (!bytes_per_block || bytes_per_block % l_c_m != 0) {
Denis Vlasenko97bd0e02008-02-08 15:41:01 +00001405 bb_error_msg("warning: invalid width %u; using %d instead",
1406 (unsigned)bytes_per_block, l_c_m);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001407 bytes_per_block = l_c_m;
1408 }
1409 } else {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001410 bytes_per_block = l_c_m;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001411 if (l_c_m < DEFAULT_BYTES_PER_BLOCK)
Denis Vlasenko601ae132006-11-28 23:37:46 +00001412 bytes_per_block *= DEFAULT_BYTES_PER_BLOCK / l_c_m;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001413 }
1414
1415#ifdef DEBUG
1416 for (i = 0; i < n_specs; i++) {
1417 printf("%d: fmt=\"%s\" width=%d\n",
1418 i, spec[i].fmt_string, width_bytes[spec[i].size]);
1419 }
1420#endif
1421
Denis Vlasenko601ae132006-11-28 23:37:46 +00001422 if (flag_dump_strings)
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001423 dump_strings(n_bytes_to_skip, end_offset);
Denis Vlasenko601ae132006-11-28 23:37:46 +00001424 else
Denis Vlasenkoe4bc6032007-12-24 12:14:24 +00001425 dump(n_bytes_to_skip, end_offset);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001426
Denis Vlasenko601ae132006-11-28 23:37:46 +00001427 if (fclose(stdin) == EOF)
1428 bb_perror_msg_and_die(bb_msg_standard_input);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001429
Denis Vlasenkobcb66ec2007-07-24 12:28:03 +00001430 return ioerror;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001431}