blob: 9f1a582efe01847e0f32abfda35d6d1a60116f19 [file] [log] [blame]
Denis Vlasenko1114de72006-10-10 23:26:05 +00001/* od -- dump files in octal and other formats
2 Copyright (C) 92, 1995-2004 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
17
18/* Written by Jim Meyering. */
19
20/* Busyboxed by Denis Vlasenko
Denis Vlasenko601ae132006-11-28 23:37:46 +000021
22Based on od.c from coreutils-5.2.1
23Top bloat sources:
24
2500000073 t parse_old_offset
260000007b t get_lcm
2700000090 r long_options
2800000092 t print_named_ascii
29000000bf t print_ascii
3000000168 t write_block
3100000366 t decode_format_string
3200000a71 T od_main
33
34Tested for compat with coreutils 6.3
35using this script. Minor differences fixed.
36
37#!/bin/sh
38echo STD
39time /path/to/coreutils/od \
40...params... \
41>std
42echo Exit code $?
43echo BBOX
44time ./busybox od \
45...params... \
46>bbox
47echo Exit code $?
48diff -u -a std bbox >bbox.diff || { echo Different!; sleep 1; }
49
Denis Vlasenko1114de72006-10-10 23:26:05 +000050*/
51
Denis Vlasenko601ae132006-11-28 23:37:46 +000052
Denis Vlasenko1114de72006-10-10 23:26:05 +000053#include "busybox.h"
54#include <getopt.h>
55
56#define assert(a) ((void)0)
Denis Vlasenko601ae132006-11-28 23:37:46 +000057
58/* Check for 0x7f is a coreutils 6.3 addition */
59#define ISPRINT(c) (((c)>=' ') && (c) != 0x7f)
Denis Vlasenko1114de72006-10-10 23:26:05 +000060
61typedef long double longdouble_t;
62typedef unsigned long long ulonglong_t;
Denis Vlasenko601ae132006-11-28 23:37:46 +000063typedef long long llong;
64
65#if ENABLE_LFS
66# define xstrtooff_sfx xstrtoull_sfx
67#else
68# define xstrtooff_sfx xstrtoul_sfx
69#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +000070
71/* The default number of input bytes per output line. */
72#define DEFAULT_BYTES_PER_BLOCK 16
73
74/* The number of decimal digits of precision in a float. */
75#ifndef FLT_DIG
76# define FLT_DIG 7
77#endif
78
79/* The number of decimal digits of precision in a double. */
80#ifndef DBL_DIG
81# define DBL_DIG 15
82#endif
83
84/* The number of decimal digits of precision in a long double. */
85#ifndef LDBL_DIG
86# define LDBL_DIG DBL_DIG
87#endif
88
89enum size_spec {
90 NO_SIZE,
91 CHAR,
92 SHORT,
93 INT,
94 LONG,
95 LONG_LONG,
Denis Vlasenko1114de72006-10-10 23:26:05 +000096 FLOAT_SINGLE,
97 FLOAT_DOUBLE,
98 FLOAT_LONG_DOUBLE,
99 N_SIZE_SPECS
100};
101
102enum output_format {
103 SIGNED_DECIMAL,
104 UNSIGNED_DECIMAL,
105 OCTAL,
106 HEXADECIMAL,
107 FLOATING_POINT,
108 NAMED_CHARACTER,
109 CHARACTER
110};
111
112/* Each output format specification (from '-t spec' or from
113 old-style options) is represented by one of these structures. */
114struct tspec {
115 enum output_format fmt;
116 enum size_spec size;
117 void (*print_function) (size_t, const char *, const char *);
118 char *fmt_string;
119 int hexl_mode_trailer;
120 int field_width;
121};
122
123/* Convert the number of 8-bit bytes of a binary representation to
124 the number of characters (digits + sign if the type is signed)
125 required to represent the same quantity in the specified base/type.
126 For example, a 32-bit (4-byte) quantity may require a field width
127 as wide as the following for these types:
128 11 unsigned octal
129 11 signed decimal
130 10 unsigned decimal
131 8 unsigned hexadecimal */
132
Denis Vlasenko601ae132006-11-28 23:37:46 +0000133static const uint8_t bytes_to_oct_digits[] =
Denis Vlasenko1114de72006-10-10 23:26:05 +0000134{0, 3, 6, 8, 11, 14, 16, 19, 22, 25, 27, 30, 32, 35, 38, 41, 43};
135
Denis Vlasenko601ae132006-11-28 23:37:46 +0000136static const uint8_t bytes_to_signed_dec_digits[] =
Denis Vlasenko1114de72006-10-10 23:26:05 +0000137{1, 4, 6, 8, 11, 13, 16, 18, 20, 23, 25, 28, 30, 33, 35, 37, 40};
138
Denis Vlasenko601ae132006-11-28 23:37:46 +0000139static const uint8_t bytes_to_unsigned_dec_digits[] =
Denis Vlasenko1114de72006-10-10 23:26:05 +0000140{0, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39};
141
Denis Vlasenko601ae132006-11-28 23:37:46 +0000142static const uint8_t bytes_to_hex_digits[] =
Denis Vlasenko1114de72006-10-10 23:26:05 +0000143{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32};
144
145/* Convert enum size_spec to the size of the named type. */
146static const signed char width_bytes[] = {
147 -1,
148 sizeof(char),
149 sizeof(short),
150 sizeof(int),
151 sizeof(long),
152 sizeof(ulonglong_t),
153 sizeof(float),
154 sizeof(double),
155 sizeof(longdouble_t)
156};
157
158/* Ensure that for each member of 'enum size_spec' there is an
159 initializer in the width_bytes array. */
160struct dummy {
161 int assert_width_bytes_matches_size_spec_decl
162 [sizeof width_bytes / sizeof width_bytes[0] == N_SIZE_SPECS ? 1 : -1];
163};
164
Denis Vlasenko1114de72006-10-10 23:26:05 +0000165static size_t string_min;
166static int flag_dump_strings;
167
Denis Vlasenko1114de72006-10-10 23:26:05 +0000168/* Non-zero if an old-style 'pseudo-address' was specified. */
169static int flag_pseudo_start;
170
171/* The difference between the old-style pseudo starting address and
172 the number of bytes to skip. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000173static off_t pseudo_offset;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000174
175/* Function that accepts an address and an optional following char,
176 and prints the address and char to stdout. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000177static void (*format_address) (off_t, char);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000178
179/* The number of input bytes to skip before formatting and writing. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000180static off_t n_bytes_to_skip; // = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000181
182/* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all
183 input is formatted. */
184static int limit_bytes_to_format; // = 0;
185
186/* The maximum number of bytes that will be formatted. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000187static off_t max_bytes_to_format;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000188
189/* The offset of the first byte after the last byte to be formatted. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000190static off_t end_offset;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000191
192/* When nonzero and two or more consecutive blocks are equal, format
193 only the first block and output an asterisk alone on the following
194 line to indicate that identical blocks have been elided. */
195static int abbreviate_duplicate_blocks = 1;
196
197/* An array of specs describing how to format each input block. */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000198static size_t n_specs;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000199static struct tspec *spec;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000200
201/* The number of input bytes formatted per output line. It must be
202 a multiple of the least common multiple of the sizes associated with
203 the specified output types. It should be as large as possible, but
204 no larger than 16 -- unless specified with the -w option. */
205static size_t bytes_per_block;
206
207/* Human-readable representation of *file_list (for error messages).
208 It differs from *file_list only when *file_list is "-". */
209static char const *input_filename;
210
211/* A NULL-terminated list of the file-arguments from the command line. */
212static char const *const *file_list;
213
214/* Initializer for file_list if no file-arguments
215 were specified on the command line. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000216static char const *const default_file_list[] = { "-", NULL };
Denis Vlasenko1114de72006-10-10 23:26:05 +0000217
218/* The input stream associated with the current file. */
219static FILE *in_stream;
220
Denis Vlasenko601ae132006-11-28 23:37:46 +0000221static int ioerror;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000222
223#define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000224static unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] = {
225 [sizeof(char)] = CHAR,
226#if USHRT_MAX != UCHAR_MAX
227 [sizeof(short)] = SHORT,
228#endif
229#if UINT_MAX != USHRT_MAX
230 [sizeof(int)] = INT,
231#endif
232#if ULONG_MAX != UINT_MAX
233 [sizeof(long)] = LONG,
234#endif
235#if ULLONG_MAX != ULONG_MAX
236 [sizeof(ulonglong_t)] = LONG_LONG,
237#endif
238};
Denis Vlasenko1114de72006-10-10 23:26:05 +0000239
240#define MAX_FP_TYPE_SIZE sizeof(longdouble_t)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000241static unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] = {
242 /* gcc seems to allow repeated indexes. Last one stays */
243 [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE,
244 [sizeof(double)] = FLOAT_DOUBLE,
245 [sizeof(float)] = FLOAT_SINGLE,
Denis Vlasenko1114de72006-10-10 23:26:05 +0000246};
247
248
249static unsigned
250gcd(unsigned u, unsigned v)
251{
252 unsigned t;
253 while (v != 0) {
254 t = u % v;
255 u = v;
256 v = t;
257 }
258 return u;
259}
260
261/* Compute the least common multiple of U and V. */
262static unsigned
263lcm(unsigned u, unsigned v) {
264 unsigned t = gcd(u, v);
265 if (t == 0)
266 return 0;
267 return u * v / t;
268}
269
270static void
271print_s_char(size_t n_bytes, const char *block, const char *fmt_string)
272{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000273 while (n_bytes--) {
274 int tmp = *(signed char *) block;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000275 printf(fmt_string, tmp);
276 block += sizeof(unsigned char);
277 }
278}
279
280static void
281print_char(size_t n_bytes, const char *block, const char *fmt_string)
282{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000283 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000284 unsigned tmp = *(unsigned char *) block;
285 printf(fmt_string, tmp);
286 block += sizeof(unsigned char);
287 }
288}
289
290static void
291print_s_short(size_t n_bytes, const char *block, const char *fmt_string)
292{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000293 n_bytes /= sizeof(signed short);
294 while (n_bytes--) {
295 int tmp = *(signed short *) block;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000296 printf(fmt_string, tmp);
297 block += sizeof(unsigned short);
298 }
299}
300
301static void
302print_short(size_t n_bytes, const char *block, const char *fmt_string)
303{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000304 n_bytes /= sizeof(unsigned short);
305 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000306 unsigned tmp = *(unsigned short *) block;
307 printf(fmt_string, tmp);
308 block += sizeof(unsigned short);
309 }
310}
311
312static void
313print_int(size_t n_bytes, const char *block, const char *fmt_string)
314{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000315 n_bytes /= sizeof(unsigned);
316 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000317 unsigned tmp = *(unsigned *) block;
318 printf(fmt_string, tmp);
319 block += sizeof(unsigned);
320 }
321}
322
Denis Vlasenko601ae132006-11-28 23:37:46 +0000323#if UINT_MAX == ULONG_MAX
324# define print_long print_int
325#else
Denis Vlasenko1114de72006-10-10 23:26:05 +0000326static void
327print_long(size_t n_bytes, const char *block, const char *fmt_string)
328{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000329 n_bytes /= sizeof(unsigned long);
330 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000331 unsigned long tmp = *(unsigned long *) block;
332 printf(fmt_string, tmp);
333 block += sizeof(unsigned long);
334 }
335}
Denis Vlasenko601ae132006-11-28 23:37:46 +0000336#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +0000337
Denis Vlasenko601ae132006-11-28 23:37:46 +0000338#if ULONG_MAX == ULLONG_MAX
339# define print_long_long print_long
340#else
Denis Vlasenko1114de72006-10-10 23:26:05 +0000341static void
342print_long_long(size_t n_bytes, const char *block, const char *fmt_string)
343{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000344 n_bytes /= sizeof(ulonglong_t);
345 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000346 ulonglong_t tmp = *(ulonglong_t *) block;
347 printf(fmt_string, tmp);
348 block += sizeof(ulonglong_t);
349 }
350}
Denis Vlasenko601ae132006-11-28 23:37:46 +0000351#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +0000352
353static void
354print_float(size_t n_bytes, const char *block, const char *fmt_string)
355{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000356 n_bytes /= sizeof(float);
357 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000358 float tmp = *(float *) block;
359 printf(fmt_string, tmp);
360 block += sizeof(float);
361 }
362}
363
364static void
365print_double(size_t n_bytes, const char *block, const char *fmt_string)
366{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000367 n_bytes /= sizeof(double);
368 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000369 double tmp = *(double *) block;
370 printf(fmt_string, tmp);
371 block += sizeof(double);
372 }
373}
374
375static void
376print_long_double(size_t n_bytes, const char *block, const char *fmt_string)
377{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000378 n_bytes /= sizeof(longdouble_t);
379 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000380 longdouble_t tmp = *(longdouble_t *) block;
381 printf(fmt_string, tmp);
382 block += sizeof(longdouble_t);
383 }
384}
385
Denis Vlasenko601ae132006-11-28 23:37:46 +0000386/* print_[named]_ascii are optimized for speed.
387 * Remember, someday you may want to pump gigabytes thru this thing.
388 * Saving a dozen of .text bytes here is counter-productive */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000389
390static void
391print_named_ascii(size_t n_bytes, const char *block,
392 const char *unused_fmt_string ATTRIBUTE_UNUSED)
393{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000394 /* Names for some non-printing characters. */
395 static const char charname[33][3] = {
396 "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
397 " bs", " ht", " nl", " vt", " ff", " cr", " so", " si",
398 "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
399 "can", " em", "sub", "esc", " fs", " gs", " rs", " us",
400 " sp"
401 };
402 // buf[N] pos: 01234 56789
403 char buf[12] = " x\0 0xx\0";
404 // actually " x\0 xxx\0", but I want to share the string with below.
405 // [12] because we take three 32bit stack slots anyway, and
406 // gcc is too dumb to initialize with constant stores,
407 // it copies initializer from rodata. Oh well.
Denis Vlasenko1114de72006-10-10 23:26:05 +0000408
Denis Vlasenko601ae132006-11-28 23:37:46 +0000409 while (n_bytes--) {
410 unsigned masked_c = *(unsigned char *) block++;
411
412 masked_c &= 0x7f;
413 if (masked_c == 0x7f) {
414 fputs(" del", stdout);
415 continue;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000416 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000417 if (masked_c > ' ') {
418 buf[3] = masked_c;
419 fputs(buf, stdout);
420 continue;
421 }
422 /* Why? Because printf(" %3.3s") is much slower... */
423 buf[6] = charname[masked_c][0];
424 buf[7] = charname[masked_c][1];
425 buf[8] = charname[masked_c][2];
426 fputs(buf+5, stdout);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000427 }
428}
429
430static void
431print_ascii(size_t n_bytes, const char *block,
432 const char *unused_fmt_string ATTRIBUTE_UNUSED)
433{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000434 // buf[N] pos: 01234 56789
435 char buf[12] = " x\0 0xx\0";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000436
Denis Vlasenko601ae132006-11-28 23:37:46 +0000437 while (n_bytes--) {
438 const char *s;
439 unsigned c = *(unsigned char *) block++;
440
441 if (ISPRINT(c)) {
442 buf[3] = c;
443 fputs(buf, stdout);
444 continue;
445 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000446 switch (c) {
447 case '\0':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000448 s = " \\0";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000449 break;
450 case '\007':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000451 s = " \\a";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000452 break;
453 case '\b':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000454 s = " \\b";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000455 break;
456 case '\f':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000457 s = " \\f";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000458 break;
459 case '\n':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000460 s = " \\n";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000461 break;
462 case '\r':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000463 s = " \\r";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000464 break;
465 case '\t':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000466 s = " \\t";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000467 break;
468 case '\v':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000469 s = " \\v";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000470 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000471 case '\x7f':
472 s = " 177";
473 break;
474 default: /* c is never larger than 040 */
475 buf[7] = (c >> 3) + '0';
476 buf[8] = (c & 7) + '0';
477 s = buf + 5;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000478 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000479 fputs(s, stdout);
480 }
481}
Denis Vlasenko1114de72006-10-10 23:26:05 +0000482
Denis Vlasenko601ae132006-11-28 23:37:46 +0000483/* Given a list of one or more input filenames FILE_LIST, set the global
484 file pointer IN_STREAM and the global string INPUT_FILENAME to the
485 first one that can be successfully opened. Modify FILE_LIST to
486 reference the next filename in the list. A file name of "-" is
487 interpreted as standard input. If any file open fails, give an error
488 message and return nonzero. */
489
490static void
491open_next_file(void)
492{
493 while(1) {
494 input_filename = *file_list;
495 if (!input_filename)
496 return;
497 file_list++;
498 in_stream = fopen_or_warn_stdin(input_filename);
499 if (in_stream) {
500 if (in_stream == stdin)
501 input_filename = bb_msg_standard_input;
502 break;
503 }
504 ioerror = 1;
505 }
506
507 if (limit_bytes_to_format && !flag_dump_strings)
508 setbuf(in_stream, NULL);
509}
510
511/* Test whether there have been errors on in_stream, and close it if
512 it is not standard input. Return nonzero if there has been an error
513 on in_stream or stdout; return zero otherwise. This function will
514 report more than one error only if both a read and a write error
515 have occurred. IN_ERRNO, if nonzero, is the error number
516 corresponding to the most recent action for IN_STREAM. */
517
518static void
519check_and_close(void)
520{
521 if (in_stream) {
522 if (ferror(in_stream)) {
523 bb_error_msg("%s: read error", input_filename);
524 ioerror = 1;
525 }
526 fclose_if_not_stdin(in_stream);
527 in_stream = NULL;
528 }
529
530 if (ferror(stdout)) {
531 bb_error_msg("write error");
532 ioerror = 1;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000533 }
534}
535
536/* If S points to a single valid modern od format string, put
537 a description of that format in *TSPEC, make *NEXT point at the
538 character following the just-decoded format (if *NEXT is non-NULL),
Denis Vlasenko601ae132006-11-28 23:37:46 +0000539 and return zero. For example, if S were "d4afL"
540 *NEXT would be set to "afL" and *TSPEC would be
Denis Vlasenko1114de72006-10-10 23:26:05 +0000541 {
542 fmt = SIGNED_DECIMAL;
543 size = INT or LONG; (whichever integral_type_size[4] resolves to)
544 print_function = print_int; (assuming size == INT)
545 fmt_string = "%011d%c";
546 }
547 S_ORIG is solely for reporting errors. It should be the full format
Denis Vlasenko601ae132006-11-28 23:37:46 +0000548 string argument. */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000549
Denis Vlasenko601ae132006-11-28 23:37:46 +0000550static void
Denis Vlasenko1114de72006-10-10 23:26:05 +0000551decode_one_format(const char *s_orig, const char *s, const char **next,
552 struct tspec *tspec)
553{
554 enum size_spec size_spec;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000555 unsigned size;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000556 enum output_format fmt;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000557 const char *p;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000558 char *end;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000559 char *fmt_string = NULL;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000560 void (*print_function) (size_t, const char *, const char *);
561 unsigned c;
562 unsigned field_width = 0;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000563 int pos;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000564
565 assert(tspec != NULL);
566
567 switch (*s) {
568 case 'd':
569 case 'o':
570 case 'u':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000571 case 'x': {
572 static const char CSIL[] = "CSIL";
573
574 c = *s++;
575 p = strchr(CSIL, *s);
576 if (!p) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000577 size = sizeof(int);
578 if (isdigit(s[0])) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000579 size = bb_strtou(s, &end, 0);
580 if (errno == ERANGE
581 || MAX_INTEGRAL_TYPE_SIZE < size
Denis Vlasenko1114de72006-10-10 23:26:05 +0000582 || integral_type_size[size] == NO_SIZE
583 ) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000584 bb_error_msg_and_die("invalid type string '%s'; "
585 "%u-byte %s type is not supported",
586 s_orig, size, "integral");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000587 }
588 s = end;
589 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000590 } else {
591 static const uint8_t CSIL_sizeof[] = {
592 sizeof(char),
593 sizeof(short),
594 sizeof(int),
595 sizeof(long),
596 };
597 size = CSIL_sizeof[p - CSIL];
Denis Vlasenko1114de72006-10-10 23:26:05 +0000598 }
599
600#define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \
601 ((Spec) == LONG_LONG ? (Max_format) \
602 : ((Spec) == LONG ? (Long_format) : (Min_format)))
603
604#define FMT_BYTES_ALLOCATED 9
Denis Vlasenko1114de72006-10-10 23:26:05 +0000605 size_spec = integral_type_size[size];
606
Denis Vlasenko601ae132006-11-28 23:37:46 +0000607 {
608 static const char doux[] = "doux";
609 static const char doux_fmt_letter[][4] = {
610 "lld", "llo", "llu", "llx"
611 };
612 static const enum output_format doux_fmt[] = {
613 SIGNED_DECIMAL,
614 OCTAL,
615 UNSIGNED_DECIMAL,
616 HEXADECIMAL,
617 };
618 static const uint8_t *const doux_bytes_to_XXX[] = {
619 bytes_to_signed_dec_digits,
620 bytes_to_oct_digits,
621 bytes_to_unsigned_dec_digits,
622 bytes_to_hex_digits,
623 };
624 static const char doux_fmtstring[][sizeof(" %%0%u%s")] = {
625 " %%%u%s",
626 " %%0%u%s",
627 " %%%u%s",
628 " %%0%u%s",
629 };
Denis Vlasenko1114de72006-10-10 23:26:05 +0000630
Denis Vlasenko601ae132006-11-28 23:37:46 +0000631 pos = strchr(doux, c) - doux;
632 fmt = doux_fmt[pos];
633 field_width = doux_bytes_to_XXX[pos][size];
634 p = doux_fmt_letter[pos] + 2;
635 if (size_spec == LONG) p--;
636 if (size_spec == LONG_LONG) p -= 2;
637 fmt_string = xasprintf(doux_fmtstring[pos], field_width, p);
638 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000639
640 switch (size_spec) {
641 case CHAR:
642 print_function = (fmt == SIGNED_DECIMAL
643 ? print_s_char
644 : print_char);
645 break;
646 case SHORT:
647 print_function = (fmt == SIGNED_DECIMAL
648 ? print_s_short
649 : print_short);
650 break;
651 case INT:
652 print_function = print_int;
653 break;
654 case LONG:
655 print_function = print_long;
656 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000657 default: /* case LONG_LONG: */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000658 print_function = print_long_long;
659 break;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000660 }
661 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000662 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000663
Denis Vlasenko601ae132006-11-28 23:37:46 +0000664 case 'f': {
665 static const char FDL[] = "FDL";
666
Denis Vlasenko1114de72006-10-10 23:26:05 +0000667 fmt = FLOATING_POINT;
668 ++s;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000669 p = strchr(FDL, *s);
670 if (!p) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000671 size = sizeof(double);
672 if (isdigit(s[0])) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000673 size = bb_strtou(s, &end, 0);
674 if (errno == ERANGE || size > MAX_FP_TYPE_SIZE
Denis Vlasenko1114de72006-10-10 23:26:05 +0000675 || fp_type_size[size] == NO_SIZE
676 ) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000677 bb_error_msg_and_die("invalid type string '%s'; "
678 "%u-byte %s type is not supported",
679 s_orig, size, "floating point");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000680 }
681 s = end;
682 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000683 } else {
684 static const uint8_t FDL_sizeof[] = {
685 sizeof(float),
686 sizeof(double),
687 sizeof(longdouble_t),
688 };
689
690 size = FDL_sizeof[p - FDL];
Denis Vlasenko1114de72006-10-10 23:26:05 +0000691 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000692
Denis Vlasenko1114de72006-10-10 23:26:05 +0000693 size_spec = fp_type_size[size];
694
695 switch (size_spec) {
696 case FLOAT_SINGLE:
697 print_function = print_float;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000698 field_width = FLT_DIG + 8;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000699 /* Don't use %#e; not all systems support it. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000700 fmt_string = xasprintf(" %%%d.%de", field_width, FLT_DIG);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000701 break;
702 case FLOAT_DOUBLE:
703 print_function = print_double;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000704 field_width = DBL_DIG + 8;
705 fmt_string = xasprintf(" %%%d.%de", field_width, DBL_DIG);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000706 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000707 default: /* case FLOAT_LONG_DOUBLE: */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000708 print_function = print_long_double;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000709 field_width = LDBL_DIG + 8;
710 fmt_string = xasprintf(" %%%d.%dLe", field_width, LDBL_DIG);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000711 break;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000712 }
713 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000714 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000715
716 case 'a':
717 ++s;
718 fmt = NAMED_CHARACTER;
719 size_spec = CHAR;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000720 print_function = print_named_ascii;
721 field_width = 3;
722 break;
723 case 'c':
724 ++s;
725 fmt = CHARACTER;
726 size_spec = CHAR;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000727 print_function = print_ascii;
728 field_width = 3;
729 break;
730 default:
Denis Vlasenko601ae132006-11-28 23:37:46 +0000731 bb_error_msg_and_die("invalid character '%c' "
732 "in type string '%s'", *s, s_orig);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000733 }
734
735 tspec->size = size_spec;
736 tspec->fmt = fmt;
737 tspec->print_function = print_function;
738 tspec->fmt_string = fmt_string;
739
740 tspec->field_width = field_width;
741 tspec->hexl_mode_trailer = (*s == 'z');
742 if (tspec->hexl_mode_trailer)
743 s++;
744
745 if (next != NULL)
746 *next = s;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000747}
748
749/* Decode the modern od format string S. Append the decoded
750 representation to the global array SPEC, reallocating SPEC if
751 necessary. Return zero if S is valid, nonzero otherwise. */
752
Denis Vlasenko601ae132006-11-28 23:37:46 +0000753static void
Denis Vlasenko1114de72006-10-10 23:26:05 +0000754decode_format_string(const char *s)
755{
756 const char *s_orig = s;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000757
758 while (*s != '\0') {
759 struct tspec tspec;
760 const char *next;
761
Denis Vlasenko601ae132006-11-28 23:37:46 +0000762 decode_one_format(s_orig, s, &next, &tspec);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000763
764 assert(s != next);
765 s = next;
766 n_specs++;
767 spec = xrealloc(spec, n_specs * sizeof(*spec));
768 memcpy(&spec[n_specs-1], &tspec, sizeof *spec);
769 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000770}
771
772/* Given a list of one or more input filenames FILE_LIST, set the global
773 file pointer IN_STREAM to position N_SKIP in the concatenation of
774 those files. If any file operation fails or if there are fewer than
775 N_SKIP bytes in the combined input, give an error message and return
776 nonzero. When possible, use seek rather than read operations to
777 advance IN_STREAM. */
778
Denis Vlasenko601ae132006-11-28 23:37:46 +0000779static void
780skip(off_t n_skip)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000781{
Denis Vlasenko1114de72006-10-10 23:26:05 +0000782 if (n_skip == 0)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000783 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000784
Denis Vlasenko601ae132006-11-28 23:37:46 +0000785 while (in_stream) { /* !EOF */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000786 struct stat file_stats;
787
788 /* First try seeking. For large offsets, this extra work is
789 worthwhile. If the offset is below some threshold it may be
790 more efficient to move the pointer by reading. There are two
791 issues when trying to seek:
792 - the file must be seekable.
793 - before seeking to the specified position, make sure
794 that the new position is in the current file.
795 Try to do that by getting file's size using fstat.
796 But that will work only for regular files. */
797
Denis Vlasenko1114de72006-10-10 23:26:05 +0000798 /* The st_size field is valid only for regular files
799 (and for symbolic links, which cannot occur here).
800 If the number of bytes left to skip is at least
801 as large as the size of the current file, we can
802 decrement n_skip and go on to the next file. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000803 if (fstat(fileno(in_stream), &file_stats) == 0
804 && S_ISREG(file_stats.st_mode) && file_stats.st_size >= 0
805 ) {
806 if (file_stats.st_size < n_skip) {
807 n_skip -= file_stats.st_size;
808 /* take check&close / open_next route */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000809 } else {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000810 if (fseeko(in_stream, n_skip, SEEK_CUR) != 0)
811 ioerror = 1;
812 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000813 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000814 } else {
815 /* If it's not a regular file with nonnegative size,
816 position the file pointer by reading. */
817 char buf[BUFSIZ];
818 size_t n_bytes_read, n_bytes_to_read = BUFSIZ;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000819
Denis Vlasenko601ae132006-11-28 23:37:46 +0000820 while (n_skip > 0) {
821 if (n_skip < n_bytes_to_read)
822 n_bytes_to_read = n_skip;
823 n_bytes_read = fread(buf, 1, n_bytes_to_read, in_stream);
824 n_skip -= n_bytes_read;
825 if (n_bytes_read != n_bytes_to_read)
826 break; /* EOF on this file or error */
827 }
828 }
829 if (n_skip == 0)
830 return;
831
832 check_and_close();
833 open_next_file();
Denis Vlasenko1114de72006-10-10 23:26:05 +0000834 }
835
Denis Vlasenko601ae132006-11-28 23:37:46 +0000836 if (n_skip)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000837 bb_error_msg_and_die("cannot skip past end of combined input");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000838}
839
Denis Vlasenko601ae132006-11-28 23:37:46 +0000840
841typedef void FN_format_address(off_t address, char c);
842
Denis Vlasenko1114de72006-10-10 23:26:05 +0000843static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000844format_address_none(off_t address ATTRIBUTE_UNUSED, char c ATTRIBUTE_UNUSED)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000845{
846}
847
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000848static char address_fmt[] = "%0n"OFF_FMT"xc";
Denis Vlasenko601ae132006-11-28 23:37:46 +0000849/* Corresponds to 'x' above */
850#define address_base_char address_fmt[sizeof(address_fmt)-3]
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000851/* Corresponds to 'n' above */
852#define address_pad_len_char address_fmt[2]
Denis Vlasenko601ae132006-11-28 23:37:46 +0000853
Denis Vlasenko1114de72006-10-10 23:26:05 +0000854static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000855format_address_std(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000856{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000857 /* Corresponds to 'c' */
858 address_fmt[sizeof(address_fmt)-2] = c;
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000859 printf(address_fmt, address);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000860}
861
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000862#if ENABLE_GETOPT_LONG
863/* only used with --traditional */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000864static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000865format_address_paren(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000866{
867 putchar('(');
868 format_address_std(address, ')');
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000869 /* BUG in coreutils 5.2.1! must be "if (c) putchar(c);" */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000870 putchar(c);
871}
872
873static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000874format_address_label(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000875{
876 format_address_std(address, ' ');
877 format_address_paren(address + pseudo_offset, c);
878}
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000879#endif
Denis Vlasenko601ae132006-11-28 23:37:46 +0000880
881static void
882dump_hexl_mode_trailer(size_t n_bytes, const char *block)
883{
884 fputs(" >", stdout);
885 while (n_bytes--) {
886 unsigned c = *(unsigned char *) block++;
887 c = (ISPRINT(c) ? c : '.');
888 putchar(c);
889 }
890 putchar('<');
891}
892
Denis Vlasenko1114de72006-10-10 23:26:05 +0000893/* Write N_BYTES bytes from CURR_BLOCK to standard output once for each
894 of the N_SPEC format specs. CURRENT_OFFSET is the byte address of
895 CURR_BLOCK in the concatenation of input files, and it is printed
896 (optionally) only before the output line associated with the first
897 format spec. When duplicate blocks are being abbreviated, the output
898 for a sequence of identical input blocks is the output for the first
899 block followed by an asterisk alone on a line. It is valid to compare
900 the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK.
901 That condition may be false only for the last input block -- and then
902 only when it has not been padded to length BYTES_PER_BLOCK. */
903
904static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000905write_block(off_t current_offset, size_t n_bytes,
Denis Vlasenko1114de72006-10-10 23:26:05 +0000906 const char *prev_block, const char *curr_block)
907{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000908 static char first = 1;
909 static char prev_pair_equal = 0;
910 size_t i;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000911
912 if (abbreviate_duplicate_blocks
913 && !first
914 && n_bytes == bytes_per_block
915 && memcmp(prev_block, curr_block, bytes_per_block) == 0
916 ) {
917 if (prev_pair_equal) {
918 /* The two preceding blocks were equal, and the current
919 block is the same as the last one, so print nothing. */
920 } else {
921 puts("*");
922 prev_pair_equal = 1;
923 }
924 } else {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000925 first = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000926 prev_pair_equal = 0;
927 for (i = 0; i < n_specs; i++) {
928 if (i == 0)
929 format_address(current_offset, '\0');
930 else
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000931 printf("%*s", address_pad_len_char - '0', "");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000932 (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string);
933 if (spec[i].hexl_mode_trailer) {
934 /* space-pad out to full line width, then dump the trailer */
935 int datum_width = width_bytes[spec[i].size];
936 int blank_fields = (bytes_per_block - n_bytes) / datum_width;
937 int field_width = spec[i].field_width + 1;
938 printf("%*s", blank_fields * field_width, "");
939 dump_hexl_mode_trailer(n_bytes, curr_block);
940 }
941 putchar('\n');
942 }
943 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000944}
945
Denis Vlasenko601ae132006-11-28 23:37:46 +0000946static void
Denis Vlasenko1114de72006-10-10 23:26:05 +0000947read_block(size_t n, char *block, size_t *n_bytes_in_buffer)
948{
Denis Vlasenko1114de72006-10-10 23:26:05 +0000949 assert(0 < n && n <= bytes_per_block);
950
951 *n_bytes_in_buffer = 0;
952
953 if (n == 0)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000954 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000955
956 while (in_stream != NULL) { /* EOF. */
957 size_t n_needed;
958 size_t n_read;
959
960 n_needed = n - *n_bytes_in_buffer;
961 n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, in_stream);
962 *n_bytes_in_buffer += n_read;
963 if (n_read == n_needed)
964 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000965 /* error check is done in check_and_close */
966 check_and_close();
967 open_next_file();
Denis Vlasenko1114de72006-10-10 23:26:05 +0000968 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000969}
970
971/* Return the least common multiple of the sizes associated
972 with the format specs. */
973
974static int
975get_lcm(void)
976{
977 size_t i;
978 int l_c_m = 1;
979
980 for (i = 0; i < n_specs; i++)
981 l_c_m = lcm(l_c_m, width_bytes[(int) spec[i].size]);
982 return l_c_m;
983}
984
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000985#if ENABLE_GETOPT_LONG
Denis Vlasenko1114de72006-10-10 23:26:05 +0000986/* If S is a valid traditional offset specification with an optional
987 leading '+' return nonzero and set *OFFSET to the offset it denotes. */
988
989static int
Denis Vlasenko601ae132006-11-28 23:37:46 +0000990parse_old_offset(const char *s, off_t *offset)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000991{
992 static const struct suffix_mult Bb[] = {
993 { "B", 1024 },
994 { "b", 512 },
995 { NULL, 0 }
996 };
Denis Vlasenko601ae132006-11-28 23:37:46 +0000997 char *p;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000998 int radix;
999
Denis Vlasenko1114de72006-10-10 23:26:05 +00001000 /* Skip over any leading '+'. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001001 if (s[0] == '+') ++s;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001002
1003 /* Determine the radix we'll use to interpret S. If there is a '.',
Denis Vlasenko601ae132006-11-28 23:37:46 +00001004 * it's decimal, otherwise, if the string begins with '0X'or '0x',
1005 * it's hexadecimal, else octal. */
1006 p = strchr(s, '.');
1007 radix = 8;
1008 if (p) {
1009 p[0] = '\0'; /* cheating */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001010 radix = 10;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001011 } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
1012 radix = 16;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001013
Denis Vlasenko601ae132006-11-28 23:37:46 +00001014 *offset = xstrtooff_sfx(s, radix, Bb);
1015 if (p) p[0] = '.';
1016
1017 return (*offset >= 0);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001018}
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001019#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +00001020
1021/* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
1022 formatted block to standard output, and repeat until the specified
1023 maximum number of bytes has been read or until all input has been
1024 processed. If the last block read is smaller than BYTES_PER_BLOCK
1025 and its size is not a multiple of the size associated with a format
1026 spec, extend the input block with zero bytes until its length is a
1027 multiple of all format spec sizes. Write the final block. Finally,
1028 write on a line by itself the offset of the byte after the last byte
1029 read. Accumulate return values from calls to read_block and
1030 check_and_close, and if any was nonzero, return nonzero.
1031 Otherwise, return zero. */
1032
Denis Vlasenko601ae132006-11-28 23:37:46 +00001033static void
Denis Vlasenko1114de72006-10-10 23:26:05 +00001034dump(void)
1035{
1036 char *block[2];
Denis Vlasenko601ae132006-11-28 23:37:46 +00001037 off_t current_offset;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001038 int idx;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001039 size_t n_bytes_read;
1040
1041 block[0] = xmalloc(2*bytes_per_block);
1042 block[1] = block[0] + bytes_per_block;
1043
1044 current_offset = n_bytes_to_skip;
1045
1046 idx = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001047 if (limit_bytes_to_format) {
1048 while (1) {
1049 size_t n_needed;
1050 if (current_offset >= end_offset) {
1051 n_bytes_read = 0;
1052 break;
1053 }
1054 n_needed = MIN(end_offset - current_offset,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001055 (off_t) bytes_per_block);
1056 read_block(n_needed, block[idx], &n_bytes_read);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001057 if (n_bytes_read < bytes_per_block)
1058 break;
1059 assert(n_bytes_read == bytes_per_block);
1060 write_block(current_offset, n_bytes_read,
1061 block[!idx], block[idx]);
1062 current_offset += n_bytes_read;
1063 idx = !idx;
1064 }
1065 } else {
1066 while (1) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001067 read_block(bytes_per_block, block[idx], &n_bytes_read);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001068 if (n_bytes_read < bytes_per_block)
1069 break;
1070 assert(n_bytes_read == bytes_per_block);
1071 write_block(current_offset, n_bytes_read,
1072 block[!idx], block[idx]);
1073 current_offset += n_bytes_read;
1074 idx = !idx;
1075 }
1076 }
1077
1078 if (n_bytes_read > 0) {
1079 int l_c_m;
1080 size_t bytes_to_write;
1081
1082 l_c_m = get_lcm();
1083
1084 /* Make bytes_to_write the smallest multiple of l_c_m that
1085 is at least as large as n_bytes_read. */
1086 bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m);
1087
1088 memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
1089 write_block(current_offset, bytes_to_write,
1090 block[!idx], block[idx]);
1091 current_offset += n_bytes_read;
1092 }
1093
1094 format_address(current_offset, '\n');
1095
1096 if (limit_bytes_to_format && current_offset >= end_offset)
Denis Vlasenko601ae132006-11-28 23:37:46 +00001097 check_and_close();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001098
1099 free(block[0]);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001100}
1101
Denis Vlasenko601ae132006-11-28 23:37:46 +00001102/* Read a single byte into *C from the concatenation of the input files
1103 named in the global array FILE_LIST. On the first call to this
1104 function, the global variable IN_STREAM is expected to be an open
1105 stream associated with the input file INPUT_FILENAME. If IN_STREAM
1106 is at end-of-file, close it and update the global variables IN_STREAM
1107 and INPUT_FILENAME so they correspond to the next file in the list.
1108 Then try to read a byte from the newly opened file. Repeat if
1109 necessary until EOF is reached for the last file in FILE_LIST, then
1110 set *C to EOF and return. Subsequent calls do likewise. The return
1111 value is nonzero if any errors occured, zero otherwise. */
1112
1113static void
1114read_char(int *c)
1115{
1116 while (in_stream) { /* !EOF */
1117 *c = fgetc(in_stream);
1118 if (*c != EOF)
1119 return;
1120 check_and_close();
1121 open_next_file();
1122 }
1123 *c = EOF;
1124}
1125
1126/* Read N bytes into BLOCK from the concatenation of the input files
1127 named in the global array FILE_LIST. On the first call to this
1128 function, the global variable IN_STREAM is expected to be an open
1129 stream associated with the input file INPUT_FILENAME. If all N
1130 bytes cannot be read from IN_STREAM, close IN_STREAM and update
1131 the global variables IN_STREAM and INPUT_FILENAME. Then try to
1132 read the remaining bytes from the newly opened file. Repeat if
1133 necessary until EOF is reached for the last file in FILE_LIST.
1134 On subsequent calls, don't modify BLOCK and return zero. Set
1135 *N_BYTES_IN_BUFFER to the number of bytes read. If an error occurs,
1136 it will be detected through ferror when the stream is about to be
1137 closed. If there is an error, give a message but continue reading
1138 as usual and return nonzero. Otherwise return zero. */
1139
Denis Vlasenko1114de72006-10-10 23:26:05 +00001140/* STRINGS mode. Find each "string constant" in the input.
1141 A string constant is a run of at least 'string_min' ASCII
1142 graphic (or formatting) characters terminated by a null.
1143 Based on a function written by Richard Stallman for a
1144 traditional version of od. Return nonzero if an error
1145 occurs. Otherwise, return zero. */
1146
Denis Vlasenko601ae132006-11-28 23:37:46 +00001147static void
Denis Vlasenko1114de72006-10-10 23:26:05 +00001148dump_strings(void)
1149{
1150 size_t bufsize = MAX(100, string_min);
1151 char *buf = xmalloc(bufsize);
Denis Vlasenko601ae132006-11-28 23:37:46 +00001152 off_t address = n_bytes_to_skip;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001153
Denis Vlasenko1114de72006-10-10 23:26:05 +00001154 while (1) {
1155 size_t i;
1156 int c;
1157
1158 /* See if the next 'string_min' chars are all printing chars. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001159 tryline:
1160 if (limit_bytes_to_format && (end_offset - string_min <= address))
Denis Vlasenko1114de72006-10-10 23:26:05 +00001161 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001162 i = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001163 while (!limit_bytes_to_format || address < end_offset) {
1164 if (i == bufsize) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001165 bufsize += bufsize/8;
1166 buf = xrealloc(buf, bufsize);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001167 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001168 read_char(&c);
1169 if (c < 0) { /* EOF */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001170 free(buf);
Denis Vlasenko601ae132006-11-28 23:37:46 +00001171 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001172 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001173 address++;
1174 if (!c)
1175 break;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001176 if (!ISPRINT(c))
1177 goto tryline; /* It isn't; give up on this string. */
1178 buf[i++] = c; /* String continues; store it all. */
1179 }
1180
Denis Vlasenko601ae132006-11-28 23:37:46 +00001181 if (i < string_min) /* Too short! */
1182 goto tryline;
1183
Denis Vlasenko1114de72006-10-10 23:26:05 +00001184 /* If we get here, the string is all printable and null-terminated,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001185 * so print it. It is all in 'buf' and 'i' is its length. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001186 buf[i] = 0;
1187 format_address(address - i - 1, ' ');
1188
1189 for (i = 0; (c = buf[i]); i++) {
1190 switch (c) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001191 case '\007': fputs("\\a", stdout); break;
1192 case '\b': fputs("\\b", stdout); break;
1193 case '\f': fputs("\\f", stdout); break;
1194 case '\n': fputs("\\n", stdout); break;
1195 case '\r': fputs("\\r", stdout); break;
1196 case '\t': fputs("\\t", stdout); break;
1197 case '\v': fputs("\\v", stdout); break;
1198 default: putc(c, stdout);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001199 }
1200 }
1201 putchar('\n');
1202 }
1203
1204 /* We reach this point only if we search through
1205 (max_bytes_to_format - string_min) bytes before reaching EOF. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001206 free(buf);
1207
Denis Vlasenko601ae132006-11-28 23:37:46 +00001208 check_and_close();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001209}
1210
1211int
1212od_main(int argc, char **argv)
1213{
1214 static const struct suffix_mult bkm[] = {
1215 { "b", 512 },
1216 { "k", 1024 },
1217 { "m", 1024*1024 },
1218 { NULL, 0 }
1219 };
Denis Vlasenko601ae132006-11-28 23:37:46 +00001220 unsigned opt;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001221 int l_c_m;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001222 /* The old-style 'pseudo starting address' to be printed in parentheses
1223 after any true address. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001224 off_t pseudo_start = 0; // only for gcc
1225 enum {
1226 OPT_A = 1 << 0,
1227 OPT_N = 1 << 1,
1228 OPT_a = 1 << 2,
1229 OPT_b = 1 << 3,
1230 OPT_c = 1 << 4,
1231 OPT_d = 1 << 5,
1232 OPT_f = 1 << 6,
1233 OPT_h = 1 << 7,
1234 OPT_i = 1 << 8,
1235 OPT_j = 1 << 9,
1236 OPT_l = 1 << 10,
1237 OPT_o = 1 << 11,
1238 OPT_t = 1 << 12,
1239 OPT_v = 1 << 13,
1240 OPT_x = 1 << 14,
1241 OPT_s = 1 << 15,
1242 OPT_S = 1 << 16,
1243 OPT_w = 1 << 17,
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001244 OPT_traditional = (1 << 18) * ENABLE_GETOPT_LONG,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001245 };
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001246#if ENABLE_GETOPT_LONG
Denis Vlasenko601ae132006-11-28 23:37:46 +00001247 static const struct option long_options[] = {
1248 { "skip-bytes", required_argument, NULL, 'j' },
1249 { "address-radix", required_argument, NULL, 'A' },
1250 { "read-bytes", required_argument, NULL, 'N' },
1251 { "format", required_argument, NULL, 't' },
1252 { "output-duplicates", no_argument, NULL, 'v' },
1253 { "strings", optional_argument, NULL, 'S' },
1254 { "width", optional_argument, NULL, 'w' },
1255 { "traditional", no_argument, NULL, 0xff },
1256 { NULL, 0, NULL, 0 }
1257 };
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001258#endif
Denis Vlasenko601ae132006-11-28 23:37:46 +00001259 char *str_A, *str_N, *str_j, *str_S;
1260 char *str_w = NULL;
1261 llist_t *lst_t = NULL;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001262
1263 spec = NULL;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001264 format_address = format_address_std;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001265 address_base_char = 'o';
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001266 address_pad_len_char = '7';
Denis Vlasenko1114de72006-10-10 23:26:05 +00001267 flag_dump_strings = 0;
1268
Denis Vlasenko601ae132006-11-28 23:37:46 +00001269 /* Parse command line */
1270 opt_complementary = "t::"; // list
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001271#if ENABLE_GETOPT_LONG
Denis Vlasenko601ae132006-11-28 23:37:46 +00001272 applet_long_options = long_options;
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001273#endif
Denis Vlasenko601ae132006-11-28 23:37:46 +00001274 opt = getopt32(argc, argv, "A:N:abcdfhij:lot:vxsS:"
1275 "w::", // -w with optional param
1276 // -S was -s and also had optional parameter
1277 // but in coreutils 6.3 it was renamed and now has
1278 // _mandatory_ parameter
1279 &str_A, &str_N, &str_j, &lst_t, &str_S, &str_w);
1280 argc -= optind;
1281 argv += optind;
1282 if (opt & OPT_A) {
1283 static const char doxn[] = "doxn";
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001284 static const char doxn_address_base_char[] = {
1285 'u', 'o', 'x', /* '?' fourth one is not important */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001286 };
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001287 static const uint8_t doxn_address_pad_len_char[] = {
1288 '7', '7', '6', /* '?' */
1289 };
Denis Vlasenko601ae132006-11-28 23:37:46 +00001290 char *p;
1291 int pos;
1292 p = strchr(doxn, str_A[0]);
1293 if (!p)
1294 bb_error_msg_and_die("bad output address radix "
1295 "'%c' (must be [doxn])", str_A[0]);
1296 pos = p - doxn;
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001297 if (pos == 3) format_address = format_address_none;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001298 address_base_char = doxn_address_base_char[pos];
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001299 address_pad_len_char = doxn_address_pad_len_char[pos];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001300 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001301 if (opt & OPT_N) {
1302 limit_bytes_to_format = 1;
1303 max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm);
1304 }
1305 if (opt & OPT_a) decode_format_string("a");
1306 if (opt & OPT_b) decode_format_string("oC");
1307 if (opt & OPT_c) decode_format_string("c");
1308 if (opt & OPT_d) decode_format_string("u2");
1309 if (opt & OPT_f) decode_format_string("fF");
1310 if (opt & OPT_h) decode_format_string("x2");
1311 if (opt & OPT_i) decode_format_string("d2");
1312 if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm);
1313 if (opt & OPT_l) decode_format_string("d4");
1314 if (opt & OPT_o) decode_format_string("o2");
1315 //if (opt & OPT_t)...
1316 lst_t = rev_llist(lst_t);
1317 while (lst_t) {
1318 decode_format_string(lst_t->data);
1319 lst_t = lst_t->link;
1320 }
1321 if (opt & OPT_v) abbreviate_duplicate_blocks = 0;
1322 if (opt & OPT_x) decode_format_string("x2");
1323 if (opt & OPT_s) decode_format_string("d2");
1324 if (opt & OPT_S) {
1325 string_min = 3;
1326 string_min = xstrtou_sfx(str_S, 0, bkm);
1327 flag_dump_strings = 1;
1328 }
1329 //if (opt & OPT_w)...
1330 //if (opt & OPT_traditional)...
Denis Vlasenko1114de72006-10-10 23:26:05 +00001331
1332 if (flag_dump_strings && n_specs > 0)
1333 bb_error_msg_and_die("no type may be specified when dumping strings");
1334
Denis Vlasenko1114de72006-10-10 23:26:05 +00001335 /* If the --traditional option is used, there may be from
Denis Vlasenko601ae132006-11-28 23:37:46 +00001336 * 0 to 3 remaining command line arguments; handle each case
1337 * separately.
1338 * od [file] [[+]offset[.][b] [[+]label[.][b]]]
1339 * The offset and pseudo_start have the same syntax.
1340 *
1341 * FIXME: POSIX 1003.1-2001 with XSI requires support for the
1342 * traditional syntax even if --traditional is not given. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001343
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001344#if ENABLE_GETOPT_LONG
Denis Vlasenko601ae132006-11-28 23:37:46 +00001345 if (opt & OPT_traditional) {
1346 off_t o1, o2;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001347
Denis Vlasenko601ae132006-11-28 23:37:46 +00001348 if (argc == 1) {
1349 if (parse_old_offset(argv[0], &o1)) {
1350 n_bytes_to_skip = o1;
1351 --argc;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001352 ++argv;
1353 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001354 } else if (argc == 2) {
1355 if (parse_old_offset(argv[0], &o1)
1356 && parse_old_offset(argv[1], &o2)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001357 ) {
1358 n_bytes_to_skip = o1;
1359 flag_pseudo_start = 1;
1360 pseudo_start = o2;
1361 argv += 2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001362 argc -= 2;
1363 } else if (parse_old_offset(argv[1], &o2)) {
Denis Vlasenko1114de72006-10-10 23:26:05 +00001364 n_bytes_to_skip = o2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001365 --argc;
1366 argv[1] = argv[0];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001367 ++argv;
1368 } else {
1369 bb_error_msg_and_die("invalid second operand "
Denis Vlasenko601ae132006-11-28 23:37:46 +00001370 "in compatibility mode '%s'", argv[1]);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001371 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001372 } else if (argc == 3) {
1373 if (parse_old_offset(argv[1], &o1)
1374 && parse_old_offset(argv[2], &o2)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001375 ) {
1376 n_bytes_to_skip = o1;
1377 flag_pseudo_start = 1;
1378 pseudo_start = o2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001379 argv[2] = argv[0];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001380 argv += 2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001381 argc -= 2;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001382 } else {
1383 bb_error_msg_and_die("in compatibility mode "
1384 "the last two arguments must be offsets");
1385 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001386 } else if (argc > 3) {
Denis Vlasenko1114de72006-10-10 23:26:05 +00001387 bb_error_msg_and_die("compatibility mode supports "
1388 "at most three arguments");
1389 }
1390
1391 if (flag_pseudo_start) {
1392 if (format_address == format_address_none) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001393 address_base_char = 'o';
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001394 address_pad_len_char = '7';
Denis Vlasenko1114de72006-10-10 23:26:05 +00001395 format_address = format_address_paren;
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001396 } else
1397 format_address = format_address_label;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001398 }
1399 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001400#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +00001401
1402 if (limit_bytes_to_format) {
1403 end_offset = n_bytes_to_skip + max_bytes_to_format;
1404 if (end_offset < n_bytes_to_skip)
1405 bb_error_msg_and_die("skip-bytes + read-bytes is too large");
1406 }
1407
1408 if (n_specs == 0) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001409 decode_format_string("o2");
Denis Vlasenko1114de72006-10-10 23:26:05 +00001410 n_specs = 1;
1411 }
1412
Denis Vlasenko601ae132006-11-28 23:37:46 +00001413 /* If no files were listed on the command line,
1414 set the global pointer FILE_LIST so that it
1415 references the null-terminated list of one name: "-". */
1416 file_list = default_file_list;
1417 if (argc > 0) {
Denis Vlasenko1114de72006-10-10 23:26:05 +00001418 /* Set the global pointer FILE_LIST so that it
1419 references the first file-argument on the command-line. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001420 file_list = (char const *const *) argv;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001421 }
1422
1423 /* open the first input file */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001424 open_next_file();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001425 /* skip over any unwanted header bytes */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001426 skip(n_bytes_to_skip);
1427 if (!in_stream)
1428 return 1;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001429
1430 pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0);
1431
1432 /* Compute output block length. */
1433 l_c_m = get_lcm();
1434
Denis Vlasenko601ae132006-11-28 23:37:46 +00001435 if (opt & OPT_w) { /* -w: width */
1436 bytes_per_block = 32;
1437 if (str_w)
1438 bytes_per_block = xatou(str_w);
1439 if (!bytes_per_block || bytes_per_block % l_c_m != 0) {
1440 bb_error_msg("warning: invalid width %u; using %d instead",
1441 bytes_per_block, l_c_m);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001442 bytes_per_block = l_c_m;
1443 }
1444 } else {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001445 bytes_per_block = l_c_m;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001446 if (l_c_m < DEFAULT_BYTES_PER_BLOCK)
Denis Vlasenko601ae132006-11-28 23:37:46 +00001447 bytes_per_block *= DEFAULT_BYTES_PER_BLOCK / l_c_m;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001448 }
1449
1450#ifdef DEBUG
1451 for (i = 0; i < n_specs; i++) {
1452 printf("%d: fmt=\"%s\" width=%d\n",
1453 i, spec[i].fmt_string, width_bytes[spec[i].size]);
1454 }
1455#endif
1456
Denis Vlasenko601ae132006-11-28 23:37:46 +00001457 if (flag_dump_strings)
1458 dump_strings();
1459 else
1460 dump();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001461
Denis Vlasenko601ae132006-11-28 23:37:46 +00001462 if (fclose(stdin) == EOF)
1463 bb_perror_msg_and_die(bb_msg_standard_input);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001464
Denis Vlasenko601ae132006-11-28 23:37:46 +00001465 return (ioerror != 0); /* err != 0 - return 1 (failure) */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001466}