blob: 325085626b0ce40d88f628d21026b16b5ca29607 [file] [log] [blame]
Denis Vlasenko1114de72006-10-10 23:26:05 +00001/* od -- dump files in octal and other formats
2 Copyright (C) 92, 1995-2004 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
17
18/* Written by Jim Meyering. */
19
20/* Busyboxed by Denis Vlasenko
Denis Vlasenko601ae132006-11-28 23:37:46 +000021
22Based on od.c from coreutils-5.2.1
23Top bloat sources:
24
2500000073 t parse_old_offset
260000007b t get_lcm
2700000090 r long_options
2800000092 t print_named_ascii
29000000bf t print_ascii
3000000168 t write_block
3100000366 t decode_format_string
3200000a71 T od_main
33
34Tested for compat with coreutils 6.3
35using this script. Minor differences fixed.
36
37#!/bin/sh
38echo STD
39time /path/to/coreutils/od \
40...params... \
41>std
42echo Exit code $?
43echo BBOX
44time ./busybox od \
45...params... \
46>bbox
47echo Exit code $?
48diff -u -a std bbox >bbox.diff || { echo Different!; sleep 1; }
49
Denis Vlasenko1114de72006-10-10 23:26:05 +000050*/
51
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000052#include "libbb.h"
Denis Vlasenko1114de72006-10-10 23:26:05 +000053#include <getopt.h>
54
55#define assert(a) ((void)0)
Denis Vlasenko601ae132006-11-28 23:37:46 +000056
57/* Check for 0x7f is a coreutils 6.3 addition */
58#define ISPRINT(c) (((c)>=' ') && (c) != 0x7f)
Denis Vlasenko1114de72006-10-10 23:26:05 +000059
60typedef long double longdouble_t;
61typedef unsigned long long ulonglong_t;
Denis Vlasenko601ae132006-11-28 23:37:46 +000062typedef long long llong;
63
64#if ENABLE_LFS
65# define xstrtooff_sfx xstrtoull_sfx
66#else
67# define xstrtooff_sfx xstrtoul_sfx
68#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +000069
70/* The default number of input bytes per output line. */
71#define DEFAULT_BYTES_PER_BLOCK 16
72
73/* The number of decimal digits of precision in a float. */
74#ifndef FLT_DIG
75# define FLT_DIG 7
76#endif
77
78/* The number of decimal digits of precision in a double. */
79#ifndef DBL_DIG
80# define DBL_DIG 15
81#endif
82
83/* The number of decimal digits of precision in a long double. */
84#ifndef LDBL_DIG
85# define LDBL_DIG DBL_DIG
86#endif
87
88enum size_spec {
89 NO_SIZE,
90 CHAR,
91 SHORT,
92 INT,
93 LONG,
94 LONG_LONG,
Denis Vlasenko1114de72006-10-10 23:26:05 +000095 FLOAT_SINGLE,
96 FLOAT_DOUBLE,
97 FLOAT_LONG_DOUBLE,
98 N_SIZE_SPECS
99};
100
101enum output_format {
102 SIGNED_DECIMAL,
103 UNSIGNED_DECIMAL,
104 OCTAL,
105 HEXADECIMAL,
106 FLOATING_POINT,
107 NAMED_CHARACTER,
108 CHARACTER
109};
110
111/* Each output format specification (from '-t spec' or from
112 old-style options) is represented by one of these structures. */
113struct tspec {
114 enum output_format fmt;
115 enum size_spec size;
116 void (*print_function) (size_t, const char *, const char *);
117 char *fmt_string;
118 int hexl_mode_trailer;
119 int field_width;
120};
121
122/* Convert the number of 8-bit bytes of a binary representation to
123 the number of characters (digits + sign if the type is signed)
124 required to represent the same quantity in the specified base/type.
125 For example, a 32-bit (4-byte) quantity may require a field width
126 as wide as the following for these types:
127 11 unsigned octal
128 11 signed decimal
129 10 unsigned decimal
130 8 unsigned hexadecimal */
131
Denis Vlasenko601ae132006-11-28 23:37:46 +0000132static const uint8_t bytes_to_oct_digits[] =
Denis Vlasenko1114de72006-10-10 23:26:05 +0000133{0, 3, 6, 8, 11, 14, 16, 19, 22, 25, 27, 30, 32, 35, 38, 41, 43};
134
Denis Vlasenko601ae132006-11-28 23:37:46 +0000135static const uint8_t bytes_to_signed_dec_digits[] =
Denis Vlasenko1114de72006-10-10 23:26:05 +0000136{1, 4, 6, 8, 11, 13, 16, 18, 20, 23, 25, 28, 30, 33, 35, 37, 40};
137
Denis Vlasenko601ae132006-11-28 23:37:46 +0000138static const uint8_t bytes_to_unsigned_dec_digits[] =
Denis Vlasenko1114de72006-10-10 23:26:05 +0000139{0, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39};
140
Denis Vlasenko601ae132006-11-28 23:37:46 +0000141static const uint8_t bytes_to_hex_digits[] =
Denis Vlasenko1114de72006-10-10 23:26:05 +0000142{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32};
143
144/* Convert enum size_spec to the size of the named type. */
145static const signed char width_bytes[] = {
146 -1,
147 sizeof(char),
148 sizeof(short),
149 sizeof(int),
150 sizeof(long),
151 sizeof(ulonglong_t),
152 sizeof(float),
153 sizeof(double),
154 sizeof(longdouble_t)
155};
156
157/* Ensure that for each member of 'enum size_spec' there is an
158 initializer in the width_bytes array. */
159struct dummy {
160 int assert_width_bytes_matches_size_spec_decl
161 [sizeof width_bytes / sizeof width_bytes[0] == N_SIZE_SPECS ? 1 : -1];
162};
163
Denis Vlasenko1114de72006-10-10 23:26:05 +0000164static size_t string_min;
165static int flag_dump_strings;
166
Denis Vlasenko1114de72006-10-10 23:26:05 +0000167/* Non-zero if an old-style 'pseudo-address' was specified. */
168static int flag_pseudo_start;
169
170/* The difference between the old-style pseudo starting address and
171 the number of bytes to skip. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000172static off_t pseudo_offset;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000173
174/* Function that accepts an address and an optional following char,
175 and prints the address and char to stdout. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000176static void (*format_address) (off_t, char);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000177
178/* The number of input bytes to skip before formatting and writing. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000179static off_t n_bytes_to_skip; // = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000180
181/* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all
182 input is formatted. */
183static int limit_bytes_to_format; // = 0;
184
185/* The maximum number of bytes that will be formatted. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000186static off_t max_bytes_to_format;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000187
188/* The offset of the first byte after the last byte to be formatted. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000189static off_t end_offset;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000190
191/* When nonzero and two or more consecutive blocks are equal, format
192 only the first block and output an asterisk alone on the following
193 line to indicate that identical blocks have been elided. */
194static int abbreviate_duplicate_blocks = 1;
195
196/* An array of specs describing how to format each input block. */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000197static size_t n_specs;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000198static struct tspec *spec;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000199
200/* The number of input bytes formatted per output line. It must be
201 a multiple of the least common multiple of the sizes associated with
202 the specified output types. It should be as large as possible, but
203 no larger than 16 -- unless specified with the -w option. */
204static size_t bytes_per_block;
205
206/* Human-readable representation of *file_list (for error messages).
207 It differs from *file_list only when *file_list is "-". */
208static char const *input_filename;
209
210/* A NULL-terminated list of the file-arguments from the command line. */
211static char const *const *file_list;
212
213/* Initializer for file_list if no file-arguments
214 were specified on the command line. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000215static char const *const default_file_list[] = { "-", NULL };
Denis Vlasenko1114de72006-10-10 23:26:05 +0000216
217/* The input stream associated with the current file. */
218static FILE *in_stream;
219
Denis Vlasenko601ae132006-11-28 23:37:46 +0000220static int ioerror;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000221
222#define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000223static unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] = {
224 [sizeof(char)] = CHAR,
225#if USHRT_MAX != UCHAR_MAX
226 [sizeof(short)] = SHORT,
227#endif
228#if UINT_MAX != USHRT_MAX
229 [sizeof(int)] = INT,
230#endif
231#if ULONG_MAX != UINT_MAX
232 [sizeof(long)] = LONG,
233#endif
234#if ULLONG_MAX != ULONG_MAX
235 [sizeof(ulonglong_t)] = LONG_LONG,
236#endif
237};
Denis Vlasenko1114de72006-10-10 23:26:05 +0000238
239#define MAX_FP_TYPE_SIZE sizeof(longdouble_t)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000240static unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] = {
241 /* gcc seems to allow repeated indexes. Last one stays */
242 [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE,
243 [sizeof(double)] = FLOAT_DOUBLE,
244 [sizeof(float)] = FLOAT_SINGLE,
Denis Vlasenko1114de72006-10-10 23:26:05 +0000245};
246
247
248static unsigned
249gcd(unsigned u, unsigned v)
250{
251 unsigned t;
252 while (v != 0) {
253 t = u % v;
254 u = v;
255 v = t;
256 }
257 return u;
258}
259
260/* Compute the least common multiple of U and V. */
261static unsigned
262lcm(unsigned u, unsigned v) {
263 unsigned t = gcd(u, v);
264 if (t == 0)
265 return 0;
266 return u * v / t;
267}
268
269static void
270print_s_char(size_t n_bytes, const char *block, const char *fmt_string)
271{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000272 while (n_bytes--) {
273 int tmp = *(signed char *) block;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000274 printf(fmt_string, tmp);
275 block += sizeof(unsigned char);
276 }
277}
278
279static void
280print_char(size_t n_bytes, const char *block, const char *fmt_string)
281{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000282 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000283 unsigned tmp = *(unsigned char *) block;
284 printf(fmt_string, tmp);
285 block += sizeof(unsigned char);
286 }
287}
288
289static void
290print_s_short(size_t n_bytes, const char *block, const char *fmt_string)
291{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000292 n_bytes /= sizeof(signed short);
293 while (n_bytes--) {
294 int tmp = *(signed short *) block;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000295 printf(fmt_string, tmp);
296 block += sizeof(unsigned short);
297 }
298}
299
300static void
301print_short(size_t n_bytes, const char *block, const char *fmt_string)
302{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000303 n_bytes /= sizeof(unsigned short);
304 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000305 unsigned tmp = *(unsigned short *) block;
306 printf(fmt_string, tmp);
307 block += sizeof(unsigned short);
308 }
309}
310
311static void
312print_int(size_t n_bytes, const char *block, const char *fmt_string)
313{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000314 n_bytes /= sizeof(unsigned);
315 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000316 unsigned tmp = *(unsigned *) block;
317 printf(fmt_string, tmp);
318 block += sizeof(unsigned);
319 }
320}
321
Denis Vlasenko601ae132006-11-28 23:37:46 +0000322#if UINT_MAX == ULONG_MAX
323# define print_long print_int
324#else
Denis Vlasenko1114de72006-10-10 23:26:05 +0000325static void
326print_long(size_t n_bytes, const char *block, const char *fmt_string)
327{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000328 n_bytes /= sizeof(unsigned long);
329 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000330 unsigned long tmp = *(unsigned long *) block;
331 printf(fmt_string, tmp);
332 block += sizeof(unsigned long);
333 }
334}
Denis Vlasenko601ae132006-11-28 23:37:46 +0000335#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +0000336
Denis Vlasenko601ae132006-11-28 23:37:46 +0000337#if ULONG_MAX == ULLONG_MAX
338# define print_long_long print_long
339#else
Denis Vlasenko1114de72006-10-10 23:26:05 +0000340static void
341print_long_long(size_t n_bytes, const char *block, const char *fmt_string)
342{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000343 n_bytes /= sizeof(ulonglong_t);
344 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000345 ulonglong_t tmp = *(ulonglong_t *) block;
346 printf(fmt_string, tmp);
347 block += sizeof(ulonglong_t);
348 }
349}
Denis Vlasenko601ae132006-11-28 23:37:46 +0000350#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +0000351
352static void
353print_float(size_t n_bytes, const char *block, const char *fmt_string)
354{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000355 n_bytes /= sizeof(float);
356 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000357 float tmp = *(float *) block;
358 printf(fmt_string, tmp);
359 block += sizeof(float);
360 }
361}
362
363static void
364print_double(size_t n_bytes, const char *block, const char *fmt_string)
365{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000366 n_bytes /= sizeof(double);
367 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000368 double tmp = *(double *) block;
369 printf(fmt_string, tmp);
370 block += sizeof(double);
371 }
372}
373
374static void
375print_long_double(size_t n_bytes, const char *block, const char *fmt_string)
376{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000377 n_bytes /= sizeof(longdouble_t);
378 while (n_bytes--) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000379 longdouble_t tmp = *(longdouble_t *) block;
380 printf(fmt_string, tmp);
381 block += sizeof(longdouble_t);
382 }
383}
384
Denis Vlasenko601ae132006-11-28 23:37:46 +0000385/* print_[named]_ascii are optimized for speed.
386 * Remember, someday you may want to pump gigabytes thru this thing.
387 * Saving a dozen of .text bytes here is counter-productive */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000388
389static void
390print_named_ascii(size_t n_bytes, const char *block,
391 const char *unused_fmt_string ATTRIBUTE_UNUSED)
392{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000393 /* Names for some non-printing characters. */
394 static const char charname[33][3] = {
395 "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
396 " bs", " ht", " nl", " vt", " ff", " cr", " so", " si",
397 "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
398 "can", " em", "sub", "esc", " fs", " gs", " rs", " us",
399 " sp"
400 };
401 // buf[N] pos: 01234 56789
402 char buf[12] = " x\0 0xx\0";
403 // actually " x\0 xxx\0", but I want to share the string with below.
404 // [12] because we take three 32bit stack slots anyway, and
405 // gcc is too dumb to initialize with constant stores,
406 // it copies initializer from rodata. Oh well.
Denis Vlasenko1114de72006-10-10 23:26:05 +0000407
Denis Vlasenko601ae132006-11-28 23:37:46 +0000408 while (n_bytes--) {
409 unsigned masked_c = *(unsigned char *) block++;
410
411 masked_c &= 0x7f;
412 if (masked_c == 0x7f) {
413 fputs(" del", stdout);
414 continue;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000415 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000416 if (masked_c > ' ') {
417 buf[3] = masked_c;
418 fputs(buf, stdout);
419 continue;
420 }
421 /* Why? Because printf(" %3.3s") is much slower... */
422 buf[6] = charname[masked_c][0];
423 buf[7] = charname[masked_c][1];
424 buf[8] = charname[masked_c][2];
425 fputs(buf+5, stdout);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000426 }
427}
428
429static void
430print_ascii(size_t n_bytes, const char *block,
431 const char *unused_fmt_string ATTRIBUTE_UNUSED)
432{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000433 // buf[N] pos: 01234 56789
434 char buf[12] = " x\0 0xx\0";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000435
Denis Vlasenko601ae132006-11-28 23:37:46 +0000436 while (n_bytes--) {
437 const char *s;
438 unsigned c = *(unsigned char *) block++;
439
440 if (ISPRINT(c)) {
441 buf[3] = c;
442 fputs(buf, stdout);
443 continue;
444 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000445 switch (c) {
446 case '\0':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000447 s = " \\0";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000448 break;
449 case '\007':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000450 s = " \\a";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000451 break;
452 case '\b':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000453 s = " \\b";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000454 break;
455 case '\f':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000456 s = " \\f";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000457 break;
458 case '\n':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000459 s = " \\n";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000460 break;
461 case '\r':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000462 s = " \\r";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000463 break;
464 case '\t':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000465 s = " \\t";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000466 break;
467 case '\v':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000468 s = " \\v";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000469 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000470 case '\x7f':
471 s = " 177";
472 break;
473 default: /* c is never larger than 040 */
474 buf[7] = (c >> 3) + '0';
475 buf[8] = (c & 7) + '0';
476 s = buf + 5;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000477 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000478 fputs(s, stdout);
479 }
480}
Denis Vlasenko1114de72006-10-10 23:26:05 +0000481
Denis Vlasenko601ae132006-11-28 23:37:46 +0000482/* Given a list of one or more input filenames FILE_LIST, set the global
483 file pointer IN_STREAM and the global string INPUT_FILENAME to the
484 first one that can be successfully opened. Modify FILE_LIST to
485 reference the next filename in the list. A file name of "-" is
486 interpreted as standard input. If any file open fails, give an error
487 message and return nonzero. */
488
489static void
490open_next_file(void)
491{
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000492 while (1) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000493 input_filename = *file_list;
494 if (!input_filename)
495 return;
496 file_list++;
497 in_stream = fopen_or_warn_stdin(input_filename);
498 if (in_stream) {
499 if (in_stream == stdin)
500 input_filename = bb_msg_standard_input;
501 break;
502 }
503 ioerror = 1;
504 }
505
506 if (limit_bytes_to_format && !flag_dump_strings)
507 setbuf(in_stream, NULL);
508}
509
510/* Test whether there have been errors on in_stream, and close it if
511 it is not standard input. Return nonzero if there has been an error
512 on in_stream or stdout; return zero otherwise. This function will
513 report more than one error only if both a read and a write error
514 have occurred. IN_ERRNO, if nonzero, is the error number
515 corresponding to the most recent action for IN_STREAM. */
516
517static void
518check_and_close(void)
519{
520 if (in_stream) {
521 if (ferror(in_stream)) {
522 bb_error_msg("%s: read error", input_filename);
523 ioerror = 1;
524 }
525 fclose_if_not_stdin(in_stream);
526 in_stream = NULL;
527 }
528
529 if (ferror(stdout)) {
530 bb_error_msg("write error");
531 ioerror = 1;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000532 }
533}
534
535/* If S points to a single valid modern od format string, put
536 a description of that format in *TSPEC, make *NEXT point at the
537 character following the just-decoded format (if *NEXT is non-NULL),
Denis Vlasenko601ae132006-11-28 23:37:46 +0000538 and return zero. For example, if S were "d4afL"
539 *NEXT would be set to "afL" and *TSPEC would be
Denis Vlasenko1114de72006-10-10 23:26:05 +0000540 {
541 fmt = SIGNED_DECIMAL;
542 size = INT or LONG; (whichever integral_type_size[4] resolves to)
543 print_function = print_int; (assuming size == INT)
Denis Vlasenko8e858e22007-03-07 09:35:43 +0000544 fmt_string = "%011d%c";
Denis Vlasenko1114de72006-10-10 23:26:05 +0000545 }
546 S_ORIG is solely for reporting errors. It should be the full format
Denis Vlasenko601ae132006-11-28 23:37:46 +0000547 string argument. */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000548
Denis Vlasenko601ae132006-11-28 23:37:46 +0000549static void
Denis Vlasenko1114de72006-10-10 23:26:05 +0000550decode_one_format(const char *s_orig, const char *s, const char **next,
551 struct tspec *tspec)
552{
553 enum size_spec size_spec;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000554 unsigned size;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000555 enum output_format fmt;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000556 const char *p;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000557 char *end;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000558 char *fmt_string = NULL;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000559 void (*print_function) (size_t, const char *, const char *);
560 unsigned c;
561 unsigned field_width = 0;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000562 int pos;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000563
564 assert(tspec != NULL);
565
566 switch (*s) {
567 case 'd':
568 case 'o':
569 case 'u':
Denis Vlasenko601ae132006-11-28 23:37:46 +0000570 case 'x': {
571 static const char CSIL[] = "CSIL";
572
573 c = *s++;
574 p = strchr(CSIL, *s);
575 if (!p) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000576 size = sizeof(int);
577 if (isdigit(s[0])) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000578 size = bb_strtou(s, &end, 0);
579 if (errno == ERANGE
580 || MAX_INTEGRAL_TYPE_SIZE < size
Denis Vlasenko1114de72006-10-10 23:26:05 +0000581 || integral_type_size[size] == NO_SIZE
582 ) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000583 bb_error_msg_and_die("invalid type string '%s'; "
584 "%u-byte %s type is not supported",
585 s_orig, size, "integral");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000586 }
587 s = end;
588 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000589 } else {
590 static const uint8_t CSIL_sizeof[] = {
591 sizeof(char),
592 sizeof(short),
593 sizeof(int),
594 sizeof(long),
595 };
596 size = CSIL_sizeof[p - CSIL];
Denis Vlasenko1114de72006-10-10 23:26:05 +0000597 }
598
599#define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \
600 ((Spec) == LONG_LONG ? (Max_format) \
601 : ((Spec) == LONG ? (Long_format) : (Min_format)))
602
603#define FMT_BYTES_ALLOCATED 9
Denis Vlasenko1114de72006-10-10 23:26:05 +0000604 size_spec = integral_type_size[size];
605
Denis Vlasenko601ae132006-11-28 23:37:46 +0000606 {
607 static const char doux[] = "doux";
608 static const char doux_fmt_letter[][4] = {
609 "lld", "llo", "llu", "llx"
610 };
611 static const enum output_format doux_fmt[] = {
612 SIGNED_DECIMAL,
613 OCTAL,
614 UNSIGNED_DECIMAL,
615 HEXADECIMAL,
616 };
617 static const uint8_t *const doux_bytes_to_XXX[] = {
618 bytes_to_signed_dec_digits,
619 bytes_to_oct_digits,
620 bytes_to_unsigned_dec_digits,
621 bytes_to_hex_digits,
622 };
623 static const char doux_fmtstring[][sizeof(" %%0%u%s")] = {
624 " %%%u%s",
625 " %%0%u%s",
626 " %%%u%s",
627 " %%0%u%s",
628 };
Denis Vlasenko1114de72006-10-10 23:26:05 +0000629
Denis Vlasenko601ae132006-11-28 23:37:46 +0000630 pos = strchr(doux, c) - doux;
631 fmt = doux_fmt[pos];
632 field_width = doux_bytes_to_XXX[pos][size];
633 p = doux_fmt_letter[pos] + 2;
634 if (size_spec == LONG) p--;
635 if (size_spec == LONG_LONG) p -= 2;
636 fmt_string = xasprintf(doux_fmtstring[pos], field_width, p);
637 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000638
639 switch (size_spec) {
640 case CHAR:
641 print_function = (fmt == SIGNED_DECIMAL
642 ? print_s_char
643 : print_char);
644 break;
645 case SHORT:
646 print_function = (fmt == SIGNED_DECIMAL
647 ? print_s_short
648 : print_short);
649 break;
650 case INT:
651 print_function = print_int;
652 break;
653 case LONG:
654 print_function = print_long;
655 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000656 default: /* case LONG_LONG: */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000657 print_function = print_long_long;
658 break;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000659 }
660 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000661 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000662
Denis Vlasenko601ae132006-11-28 23:37:46 +0000663 case 'f': {
664 static const char FDL[] = "FDL";
665
Denis Vlasenko1114de72006-10-10 23:26:05 +0000666 fmt = FLOATING_POINT;
667 ++s;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000668 p = strchr(FDL, *s);
669 if (!p) {
Denis Vlasenko1114de72006-10-10 23:26:05 +0000670 size = sizeof(double);
671 if (isdigit(s[0])) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000672 size = bb_strtou(s, &end, 0);
673 if (errno == ERANGE || size > MAX_FP_TYPE_SIZE
Denis Vlasenko1114de72006-10-10 23:26:05 +0000674 || fp_type_size[size] == NO_SIZE
675 ) {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000676 bb_error_msg_and_die("invalid type string '%s'; "
677 "%u-byte %s type is not supported",
678 s_orig, size, "floating point");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000679 }
680 s = end;
681 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000682 } else {
683 static const uint8_t FDL_sizeof[] = {
684 sizeof(float),
685 sizeof(double),
686 sizeof(longdouble_t),
687 };
688
689 size = FDL_sizeof[p - FDL];
Denis Vlasenko1114de72006-10-10 23:26:05 +0000690 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000691
Denis Vlasenko1114de72006-10-10 23:26:05 +0000692 size_spec = fp_type_size[size];
693
694 switch (size_spec) {
695 case FLOAT_SINGLE:
696 print_function = print_float;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000697 field_width = FLT_DIG + 8;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000698 /* Don't use %#e; not all systems support it. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000699 fmt_string = xasprintf(" %%%d.%de", field_width, FLT_DIG);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000700 break;
701 case FLOAT_DOUBLE:
702 print_function = print_double;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000703 field_width = DBL_DIG + 8;
704 fmt_string = xasprintf(" %%%d.%de", field_width, DBL_DIG);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000705 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000706 default: /* case FLOAT_LONG_DOUBLE: */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000707 print_function = print_long_double;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000708 field_width = LDBL_DIG + 8;
709 fmt_string = xasprintf(" %%%d.%dLe", field_width, LDBL_DIG);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000710 break;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000711 }
712 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000713 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000714
715 case 'a':
716 ++s;
717 fmt = NAMED_CHARACTER;
718 size_spec = CHAR;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000719 print_function = print_named_ascii;
720 field_width = 3;
721 break;
722 case 'c':
723 ++s;
724 fmt = CHARACTER;
725 size_spec = CHAR;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000726 print_function = print_ascii;
727 field_width = 3;
728 break;
729 default:
Denis Vlasenko601ae132006-11-28 23:37:46 +0000730 bb_error_msg_and_die("invalid character '%c' "
731 "in type string '%s'", *s, s_orig);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000732 }
733
734 tspec->size = size_spec;
735 tspec->fmt = fmt;
736 tspec->print_function = print_function;
737 tspec->fmt_string = fmt_string;
738
739 tspec->field_width = field_width;
740 tspec->hexl_mode_trailer = (*s == 'z');
741 if (tspec->hexl_mode_trailer)
742 s++;
743
744 if (next != NULL)
745 *next = s;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000746}
747
748/* Decode the modern od format string S. Append the decoded
749 representation to the global array SPEC, reallocating SPEC if
750 necessary. Return zero if S is valid, nonzero otherwise. */
751
Denis Vlasenko601ae132006-11-28 23:37:46 +0000752static void
Denis Vlasenko1114de72006-10-10 23:26:05 +0000753decode_format_string(const char *s)
754{
755 const char *s_orig = s;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000756
757 while (*s != '\0') {
758 struct tspec tspec;
759 const char *next;
760
Denis Vlasenko601ae132006-11-28 23:37:46 +0000761 decode_one_format(s_orig, s, &next, &tspec);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000762
763 assert(s != next);
764 s = next;
765 n_specs++;
766 spec = xrealloc(spec, n_specs * sizeof(*spec));
767 memcpy(&spec[n_specs-1], &tspec, sizeof *spec);
768 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000769}
770
771/* Given a list of one or more input filenames FILE_LIST, set the global
772 file pointer IN_STREAM to position N_SKIP in the concatenation of
773 those files. If any file operation fails or if there are fewer than
774 N_SKIP bytes in the combined input, give an error message and return
775 nonzero. When possible, use seek rather than read operations to
776 advance IN_STREAM. */
777
Denis Vlasenko601ae132006-11-28 23:37:46 +0000778static void
779skip(off_t n_skip)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000780{
Denis Vlasenko1114de72006-10-10 23:26:05 +0000781 if (n_skip == 0)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000782 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000783
Denis Vlasenko601ae132006-11-28 23:37:46 +0000784 while (in_stream) { /* !EOF */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000785 struct stat file_stats;
786
787 /* First try seeking. For large offsets, this extra work is
788 worthwhile. If the offset is below some threshold it may be
789 more efficient to move the pointer by reading. There are two
790 issues when trying to seek:
791 - the file must be seekable.
792 - before seeking to the specified position, make sure
793 that the new position is in the current file.
794 Try to do that by getting file's size using fstat.
795 But that will work only for regular files. */
796
Denis Vlasenko1114de72006-10-10 23:26:05 +0000797 /* The st_size field is valid only for regular files
798 (and for symbolic links, which cannot occur here).
799 If the number of bytes left to skip is at least
800 as large as the size of the current file, we can
801 decrement n_skip and go on to the next file. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000802 if (fstat(fileno(in_stream), &file_stats) == 0
803 && S_ISREG(file_stats.st_mode) && file_stats.st_size >= 0
804 ) {
805 if (file_stats.st_size < n_skip) {
806 n_skip -= file_stats.st_size;
807 /* take check&close / open_next route */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000808 } else {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000809 if (fseeko(in_stream, n_skip, SEEK_CUR) != 0)
810 ioerror = 1;
811 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000812 }
Denis Vlasenko601ae132006-11-28 23:37:46 +0000813 } else {
814 /* If it's not a regular file with nonnegative size,
815 position the file pointer by reading. */
816 char buf[BUFSIZ];
817 size_t n_bytes_read, n_bytes_to_read = BUFSIZ;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000818
Denis Vlasenko601ae132006-11-28 23:37:46 +0000819 while (n_skip > 0) {
820 if (n_skip < n_bytes_to_read)
821 n_bytes_to_read = n_skip;
822 n_bytes_read = fread(buf, 1, n_bytes_to_read, in_stream);
823 n_skip -= n_bytes_read;
824 if (n_bytes_read != n_bytes_to_read)
Denis Vlasenko8e858e22007-03-07 09:35:43 +0000825 break; /* EOF on this file or error */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000826 }
827 }
828 if (n_skip == 0)
829 return;
830
831 check_and_close();
832 open_next_file();
Denis Vlasenko1114de72006-10-10 23:26:05 +0000833 }
834
Denis Vlasenko601ae132006-11-28 23:37:46 +0000835 if (n_skip)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000836 bb_error_msg_and_die("cannot skip past end of combined input");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000837}
838
Denis Vlasenko601ae132006-11-28 23:37:46 +0000839
840typedef void FN_format_address(off_t address, char c);
841
Denis Vlasenko1114de72006-10-10 23:26:05 +0000842static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000843format_address_none(off_t address ATTRIBUTE_UNUSED, char c ATTRIBUTE_UNUSED)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000844{
845}
846
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000847static char address_fmt[] = "%0n"OFF_FMT"xc";
Denis Vlasenko601ae132006-11-28 23:37:46 +0000848/* Corresponds to 'x' above */
849#define address_base_char address_fmt[sizeof(address_fmt)-3]
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000850/* Corresponds to 'n' above */
851#define address_pad_len_char address_fmt[2]
Denis Vlasenko601ae132006-11-28 23:37:46 +0000852
Denis Vlasenko1114de72006-10-10 23:26:05 +0000853static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000854format_address_std(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000855{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000856 /* Corresponds to 'c' */
857 address_fmt[sizeof(address_fmt)-2] = c;
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000858 printf(address_fmt, address);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000859}
860
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000861#if ENABLE_GETOPT_LONG
862/* only used with --traditional */
Denis Vlasenko1114de72006-10-10 23:26:05 +0000863static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000864format_address_paren(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000865{
866 putchar('(');
867 format_address_std(address, ')');
Denis Vlasenko0f5905e2006-12-17 19:21:13 +0000868 if (c) putchar(c);
Denis Vlasenko1114de72006-10-10 23:26:05 +0000869}
870
871static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000872format_address_label(off_t address, char c)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000873{
874 format_address_std(address, ' ');
875 format_address_paren(address + pseudo_offset, c);
876}
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000877#endif
Denis Vlasenko601ae132006-11-28 23:37:46 +0000878
879static void
880dump_hexl_mode_trailer(size_t n_bytes, const char *block)
881{
882 fputs(" >", stdout);
883 while (n_bytes--) {
884 unsigned c = *(unsigned char *) block++;
885 c = (ISPRINT(c) ? c : '.');
886 putchar(c);
887 }
888 putchar('<');
889}
890
Denis Vlasenko1114de72006-10-10 23:26:05 +0000891/* Write N_BYTES bytes from CURR_BLOCK to standard output once for each
892 of the N_SPEC format specs. CURRENT_OFFSET is the byte address of
893 CURR_BLOCK in the concatenation of input files, and it is printed
894 (optionally) only before the output line associated with the first
895 format spec. When duplicate blocks are being abbreviated, the output
896 for a sequence of identical input blocks is the output for the first
897 block followed by an asterisk alone on a line. It is valid to compare
898 the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK.
899 That condition may be false only for the last input block -- and then
900 only when it has not been padded to length BYTES_PER_BLOCK. */
901
902static void
Denis Vlasenko601ae132006-11-28 23:37:46 +0000903write_block(off_t current_offset, size_t n_bytes,
Denis Vlasenko1114de72006-10-10 23:26:05 +0000904 const char *prev_block, const char *curr_block)
905{
Denis Vlasenko601ae132006-11-28 23:37:46 +0000906 static char first = 1;
907 static char prev_pair_equal = 0;
908 size_t i;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000909
910 if (abbreviate_duplicate_blocks
911 && !first
912 && n_bytes == bytes_per_block
913 && memcmp(prev_block, curr_block, bytes_per_block) == 0
914 ) {
915 if (prev_pair_equal) {
916 /* The two preceding blocks were equal, and the current
917 block is the same as the last one, so print nothing. */
918 } else {
919 puts("*");
920 prev_pair_equal = 1;
921 }
922 } else {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000923 first = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000924 prev_pair_equal = 0;
925 for (i = 0; i < n_specs; i++) {
926 if (i == 0)
927 format_address(current_offset, '\0');
928 else
Denis Vlasenko2425bdc2006-11-29 14:32:01 +0000929 printf("%*s", address_pad_len_char - '0', "");
Denis Vlasenko1114de72006-10-10 23:26:05 +0000930 (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string);
931 if (spec[i].hexl_mode_trailer) {
932 /* space-pad out to full line width, then dump the trailer */
933 int datum_width = width_bytes[spec[i].size];
934 int blank_fields = (bytes_per_block - n_bytes) / datum_width;
935 int field_width = spec[i].field_width + 1;
936 printf("%*s", blank_fields * field_width, "");
937 dump_hexl_mode_trailer(n_bytes, curr_block);
938 }
939 putchar('\n');
940 }
941 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000942}
943
Denis Vlasenko601ae132006-11-28 23:37:46 +0000944static void
Denis Vlasenko1114de72006-10-10 23:26:05 +0000945read_block(size_t n, char *block, size_t *n_bytes_in_buffer)
946{
Denis Vlasenko1114de72006-10-10 23:26:05 +0000947 assert(0 < n && n <= bytes_per_block);
948
949 *n_bytes_in_buffer = 0;
950
951 if (n == 0)
Denis Vlasenko601ae132006-11-28 23:37:46 +0000952 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000953
954 while (in_stream != NULL) { /* EOF. */
955 size_t n_needed;
956 size_t n_read;
957
958 n_needed = n - *n_bytes_in_buffer;
959 n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, in_stream);
960 *n_bytes_in_buffer += n_read;
961 if (n_read == n_needed)
962 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +0000963 /* error check is done in check_and_close */
964 check_and_close();
965 open_next_file();
Denis Vlasenko1114de72006-10-10 23:26:05 +0000966 }
Denis Vlasenko1114de72006-10-10 23:26:05 +0000967}
968
969/* Return the least common multiple of the sizes associated
970 with the format specs. */
971
972static int
973get_lcm(void)
974{
975 size_t i;
976 int l_c_m = 1;
977
978 for (i = 0; i < n_specs; i++)
979 l_c_m = lcm(l_c_m, width_bytes[(int) spec[i].size]);
980 return l_c_m;
981}
982
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000983#if ENABLE_GETOPT_LONG
Denis Vlasenko1114de72006-10-10 23:26:05 +0000984/* If S is a valid traditional offset specification with an optional
985 leading '+' return nonzero and set *OFFSET to the offset it denotes. */
986
987static int
Denis Vlasenko601ae132006-11-28 23:37:46 +0000988parse_old_offset(const char *s, off_t *offset)
Denis Vlasenko1114de72006-10-10 23:26:05 +0000989{
990 static const struct suffix_mult Bb[] = {
991 { "B", 1024 },
992 { "b", 512 },
993 { NULL, 0 }
994 };
Denis Vlasenko601ae132006-11-28 23:37:46 +0000995 char *p;
Denis Vlasenko1114de72006-10-10 23:26:05 +0000996 int radix;
997
Denis Vlasenko1114de72006-10-10 23:26:05 +0000998 /* Skip over any leading '+'. */
Denis Vlasenko601ae132006-11-28 23:37:46 +0000999 if (s[0] == '+') ++s;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001000
1001 /* Determine the radix we'll use to interpret S. If there is a '.',
Denis Vlasenko601ae132006-11-28 23:37:46 +00001002 * it's decimal, otherwise, if the string begins with '0X'or '0x',
1003 * it's hexadecimal, else octal. */
1004 p = strchr(s, '.');
1005 radix = 8;
1006 if (p) {
1007 p[0] = '\0'; /* cheating */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001008 radix = 10;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001009 } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
1010 radix = 16;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001011
Denis Vlasenko601ae132006-11-28 23:37:46 +00001012 *offset = xstrtooff_sfx(s, radix, Bb);
1013 if (p) p[0] = '.';
1014
1015 return (*offset >= 0);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001016}
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001017#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +00001018
1019/* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
1020 formatted block to standard output, and repeat until the specified
1021 maximum number of bytes has been read or until all input has been
1022 processed. If the last block read is smaller than BYTES_PER_BLOCK
1023 and its size is not a multiple of the size associated with a format
1024 spec, extend the input block with zero bytes until its length is a
1025 multiple of all format spec sizes. Write the final block. Finally,
1026 write on a line by itself the offset of the byte after the last byte
1027 read. Accumulate return values from calls to read_block and
1028 check_and_close, and if any was nonzero, return nonzero.
1029 Otherwise, return zero. */
1030
Denis Vlasenko601ae132006-11-28 23:37:46 +00001031static void
Denis Vlasenko1114de72006-10-10 23:26:05 +00001032dump(void)
1033{
1034 char *block[2];
Denis Vlasenko601ae132006-11-28 23:37:46 +00001035 off_t current_offset;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001036 int idx;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001037 size_t n_bytes_read;
1038
1039 block[0] = xmalloc(2*bytes_per_block);
1040 block[1] = block[0] + bytes_per_block;
1041
1042 current_offset = n_bytes_to_skip;
1043
1044 idx = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001045 if (limit_bytes_to_format) {
1046 while (1) {
1047 size_t n_needed;
1048 if (current_offset >= end_offset) {
1049 n_bytes_read = 0;
1050 break;
1051 }
1052 n_needed = MIN(end_offset - current_offset,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001053 (off_t) bytes_per_block);
1054 read_block(n_needed, block[idx], &n_bytes_read);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001055 if (n_bytes_read < bytes_per_block)
1056 break;
1057 assert(n_bytes_read == bytes_per_block);
1058 write_block(current_offset, n_bytes_read,
1059 block[!idx], block[idx]);
1060 current_offset += n_bytes_read;
1061 idx = !idx;
1062 }
1063 } else {
1064 while (1) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001065 read_block(bytes_per_block, block[idx], &n_bytes_read);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001066 if (n_bytes_read < bytes_per_block)
1067 break;
1068 assert(n_bytes_read == bytes_per_block);
1069 write_block(current_offset, n_bytes_read,
1070 block[!idx], block[idx]);
1071 current_offset += n_bytes_read;
1072 idx = !idx;
1073 }
1074 }
1075
1076 if (n_bytes_read > 0) {
1077 int l_c_m;
1078 size_t bytes_to_write;
1079
1080 l_c_m = get_lcm();
1081
1082 /* Make bytes_to_write the smallest multiple of l_c_m that
1083 is at least as large as n_bytes_read. */
1084 bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m);
1085
1086 memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
1087 write_block(current_offset, bytes_to_write,
1088 block[!idx], block[idx]);
1089 current_offset += n_bytes_read;
1090 }
1091
1092 format_address(current_offset, '\n');
1093
1094 if (limit_bytes_to_format && current_offset >= end_offset)
Denis Vlasenko601ae132006-11-28 23:37:46 +00001095 check_and_close();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001096
1097 free(block[0]);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001098}
1099
Denis Vlasenko601ae132006-11-28 23:37:46 +00001100/* Read a single byte into *C from the concatenation of the input files
1101 named in the global array FILE_LIST. On the first call to this
1102 function, the global variable IN_STREAM is expected to be an open
1103 stream associated with the input file INPUT_FILENAME. If IN_STREAM
1104 is at end-of-file, close it and update the global variables IN_STREAM
1105 and INPUT_FILENAME so they correspond to the next file in the list.
1106 Then try to read a byte from the newly opened file. Repeat if
1107 necessary until EOF is reached for the last file in FILE_LIST, then
1108 set *C to EOF and return. Subsequent calls do likewise. The return
1109 value is nonzero if any errors occured, zero otherwise. */
1110
1111static void
1112read_char(int *c)
1113{
1114 while (in_stream) { /* !EOF */
1115 *c = fgetc(in_stream);
1116 if (*c != EOF)
1117 return;
1118 check_and_close();
1119 open_next_file();
1120 }
1121 *c = EOF;
1122}
1123
1124/* Read N bytes into BLOCK from the concatenation of the input files
1125 named in the global array FILE_LIST. On the first call to this
1126 function, the global variable IN_STREAM is expected to be an open
1127 stream associated with the input file INPUT_FILENAME. If all N
1128 bytes cannot be read from IN_STREAM, close IN_STREAM and update
1129 the global variables IN_STREAM and INPUT_FILENAME. Then try to
1130 read the remaining bytes from the newly opened file. Repeat if
1131 necessary until EOF is reached for the last file in FILE_LIST.
1132 On subsequent calls, don't modify BLOCK and return zero. Set
1133 *N_BYTES_IN_BUFFER to the number of bytes read. If an error occurs,
1134 it will be detected through ferror when the stream is about to be
1135 closed. If there is an error, give a message but continue reading
1136 as usual and return nonzero. Otherwise return zero. */
1137
Denis Vlasenko1114de72006-10-10 23:26:05 +00001138/* STRINGS mode. Find each "string constant" in the input.
1139 A string constant is a run of at least 'string_min' ASCII
1140 graphic (or formatting) characters terminated by a null.
1141 Based on a function written by Richard Stallman for a
1142 traditional version of od. Return nonzero if an error
1143 occurs. Otherwise, return zero. */
1144
Denis Vlasenko601ae132006-11-28 23:37:46 +00001145static void
Denis Vlasenko1114de72006-10-10 23:26:05 +00001146dump_strings(void)
1147{
1148 size_t bufsize = MAX(100, string_min);
1149 char *buf = xmalloc(bufsize);
Denis Vlasenko601ae132006-11-28 23:37:46 +00001150 off_t address = n_bytes_to_skip;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001151
Denis Vlasenko1114de72006-10-10 23:26:05 +00001152 while (1) {
1153 size_t i;
1154 int c;
1155
1156 /* See if the next 'string_min' chars are all printing chars. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001157 tryline:
1158 if (limit_bytes_to_format && (end_offset - string_min <= address))
Denis Vlasenko1114de72006-10-10 23:26:05 +00001159 break;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001160 i = 0;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001161 while (!limit_bytes_to_format || address < end_offset) {
1162 if (i == bufsize) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001163 bufsize += bufsize/8;
1164 buf = xrealloc(buf, bufsize);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001165 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001166 read_char(&c);
1167 if (c < 0) { /* EOF */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001168 free(buf);
Denis Vlasenko601ae132006-11-28 23:37:46 +00001169 return;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001170 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001171 address++;
1172 if (!c)
1173 break;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001174 if (!ISPRINT(c))
1175 goto tryline; /* It isn't; give up on this string. */
1176 buf[i++] = c; /* String continues; store it all. */
1177 }
1178
Denis Vlasenko601ae132006-11-28 23:37:46 +00001179 if (i < string_min) /* Too short! */
1180 goto tryline;
1181
Denis Vlasenko1114de72006-10-10 23:26:05 +00001182 /* If we get here, the string is all printable and null-terminated,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001183 * so print it. It is all in 'buf' and 'i' is its length. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001184 buf[i] = 0;
1185 format_address(address - i - 1, ' ');
1186
1187 for (i = 0; (c = buf[i]); i++) {
1188 switch (c) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001189 case '\007': fputs("\\a", stdout); break;
1190 case '\b': fputs("\\b", stdout); break;
1191 case '\f': fputs("\\f", stdout); break;
1192 case '\n': fputs("\\n", stdout); break;
1193 case '\r': fputs("\\r", stdout); break;
1194 case '\t': fputs("\\t", stdout); break;
1195 case '\v': fputs("\\v", stdout); break;
1196 default: putc(c, stdout);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001197 }
1198 }
1199 putchar('\n');
1200 }
1201
1202 /* We reach this point only if we search through
1203 (max_bytes_to_format - string_min) bytes before reaching EOF. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001204 free(buf);
1205
Denis Vlasenko601ae132006-11-28 23:37:46 +00001206 check_and_close();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001207}
1208
Denis Vlasenko06af2162007-02-03 17:28:39 +00001209int od_main(int argc, char **argv);
1210int od_main(int argc, char **argv)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001211{
1212 static const struct suffix_mult bkm[] = {
1213 { "b", 512 },
1214 { "k", 1024 },
1215 { "m", 1024*1024 },
1216 { NULL, 0 }
1217 };
Denis Vlasenko601ae132006-11-28 23:37:46 +00001218 unsigned opt;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001219 int l_c_m;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001220 /* The old-style 'pseudo starting address' to be printed in parentheses
1221 after any true address. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001222 off_t pseudo_start = 0; // only for gcc
1223 enum {
1224 OPT_A = 1 << 0,
1225 OPT_N = 1 << 1,
1226 OPT_a = 1 << 2,
1227 OPT_b = 1 << 3,
1228 OPT_c = 1 << 4,
1229 OPT_d = 1 << 5,
1230 OPT_f = 1 << 6,
1231 OPT_h = 1 << 7,
1232 OPT_i = 1 << 8,
1233 OPT_j = 1 << 9,
1234 OPT_l = 1 << 10,
1235 OPT_o = 1 << 11,
1236 OPT_t = 1 << 12,
1237 OPT_v = 1 << 13,
1238 OPT_x = 1 << 14,
1239 OPT_s = 1 << 15,
1240 OPT_S = 1 << 16,
1241 OPT_w = 1 << 17,
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001242 OPT_traditional = (1 << 18) * ENABLE_GETOPT_LONG,
Denis Vlasenko601ae132006-11-28 23:37:46 +00001243 };
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001244#if ENABLE_GETOPT_LONG
Denis Vlasenko601ae132006-11-28 23:37:46 +00001245 static const struct option long_options[] = {
1246 { "skip-bytes", required_argument, NULL, 'j' },
1247 { "address-radix", required_argument, NULL, 'A' },
1248 { "read-bytes", required_argument, NULL, 'N' },
1249 { "format", required_argument, NULL, 't' },
1250 { "output-duplicates", no_argument, NULL, 'v' },
1251 { "strings", optional_argument, NULL, 'S' },
1252 { "width", optional_argument, NULL, 'w' },
1253 { "traditional", no_argument, NULL, 0xff },
1254 { NULL, 0, NULL, 0 }
1255 };
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001256#endif
Denis Vlasenko601ae132006-11-28 23:37:46 +00001257 char *str_A, *str_N, *str_j, *str_S;
1258 char *str_w = NULL;
1259 llist_t *lst_t = NULL;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001260
1261 spec = NULL;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001262 format_address = format_address_std;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001263 address_base_char = 'o';
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001264 address_pad_len_char = '7';
Denis Vlasenko1114de72006-10-10 23:26:05 +00001265 flag_dump_strings = 0;
1266
Denis Vlasenko601ae132006-11-28 23:37:46 +00001267 /* Parse command line */
1268 opt_complementary = "t::"; // list
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001269#if ENABLE_GETOPT_LONG
Denis Vlasenko601ae132006-11-28 23:37:46 +00001270 applet_long_options = long_options;
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001271#endif
Denis Vlasenko601ae132006-11-28 23:37:46 +00001272 opt = getopt32(argc, argv, "A:N:abcdfhij:lot:vxsS:"
1273 "w::", // -w with optional param
1274 // -S was -s and also had optional parameter
1275 // but in coreutils 6.3 it was renamed and now has
1276 // _mandatory_ parameter
1277 &str_A, &str_N, &str_j, &lst_t, &str_S, &str_w);
1278 argc -= optind;
1279 argv += optind;
1280 if (opt & OPT_A) {
1281 static const char doxn[] = "doxn";
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001282 static const char doxn_address_base_char[] = {
1283 'u', 'o', 'x', /* '?' fourth one is not important */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001284 };
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001285 static const uint8_t doxn_address_pad_len_char[] = {
1286 '7', '7', '6', /* '?' */
1287 };
Denis Vlasenko601ae132006-11-28 23:37:46 +00001288 char *p;
1289 int pos;
1290 p = strchr(doxn, str_A[0]);
1291 if (!p)
1292 bb_error_msg_and_die("bad output address radix "
1293 "'%c' (must be [doxn])", str_A[0]);
1294 pos = p - doxn;
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001295 if (pos == 3) format_address = format_address_none;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001296 address_base_char = doxn_address_base_char[pos];
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001297 address_pad_len_char = doxn_address_pad_len_char[pos];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001298 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001299 if (opt & OPT_N) {
1300 limit_bytes_to_format = 1;
1301 max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm);
1302 }
1303 if (opt & OPT_a) decode_format_string("a");
1304 if (opt & OPT_b) decode_format_string("oC");
1305 if (opt & OPT_c) decode_format_string("c");
1306 if (opt & OPT_d) decode_format_string("u2");
1307 if (opt & OPT_f) decode_format_string("fF");
1308 if (opt & OPT_h) decode_format_string("x2");
1309 if (opt & OPT_i) decode_format_string("d2");
1310 if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm);
1311 if (opt & OPT_l) decode_format_string("d4");
1312 if (opt & OPT_o) decode_format_string("o2");
1313 //if (opt & OPT_t)...
Denis Vlasenko601ae132006-11-28 23:37:46 +00001314 while (lst_t) {
1315 decode_format_string(lst_t->data);
1316 lst_t = lst_t->link;
1317 }
1318 if (opt & OPT_v) abbreviate_duplicate_blocks = 0;
1319 if (opt & OPT_x) decode_format_string("x2");
1320 if (opt & OPT_s) decode_format_string("d2");
1321 if (opt & OPT_S) {
1322 string_min = 3;
1323 string_min = xstrtou_sfx(str_S, 0, bkm);
1324 flag_dump_strings = 1;
1325 }
1326 //if (opt & OPT_w)...
1327 //if (opt & OPT_traditional)...
Denis Vlasenko1114de72006-10-10 23:26:05 +00001328
1329 if (flag_dump_strings && n_specs > 0)
1330 bb_error_msg_and_die("no type may be specified when dumping strings");
1331
Denis Vlasenko1114de72006-10-10 23:26:05 +00001332 /* If the --traditional option is used, there may be from
Denis Vlasenko601ae132006-11-28 23:37:46 +00001333 * 0 to 3 remaining command line arguments; handle each case
1334 * separately.
Denis Vlasenko8e858e22007-03-07 09:35:43 +00001335 * od [file] [[+]offset[.][b] [[+]label[.][b]]]
Denis Vlasenko601ae132006-11-28 23:37:46 +00001336 * The offset and pseudo_start have the same syntax.
1337 *
1338 * FIXME: POSIX 1003.1-2001 with XSI requires support for the
1339 * traditional syntax even if --traditional is not given. */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001340
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001341#if ENABLE_GETOPT_LONG
Denis Vlasenko601ae132006-11-28 23:37:46 +00001342 if (opt & OPT_traditional) {
1343 off_t o1, o2;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001344
Denis Vlasenko601ae132006-11-28 23:37:46 +00001345 if (argc == 1) {
1346 if (parse_old_offset(argv[0], &o1)) {
1347 n_bytes_to_skip = o1;
1348 --argc;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001349 ++argv;
1350 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001351 } else if (argc == 2) {
1352 if (parse_old_offset(argv[0], &o1)
1353 && parse_old_offset(argv[1], &o2)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001354 ) {
1355 n_bytes_to_skip = o1;
1356 flag_pseudo_start = 1;
1357 pseudo_start = o2;
1358 argv += 2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001359 argc -= 2;
1360 } else if (parse_old_offset(argv[1], &o2)) {
Denis Vlasenko1114de72006-10-10 23:26:05 +00001361 n_bytes_to_skip = o2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001362 --argc;
1363 argv[1] = argv[0];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001364 ++argv;
1365 } else {
1366 bb_error_msg_and_die("invalid second operand "
Denis Vlasenko601ae132006-11-28 23:37:46 +00001367 "in compatibility mode '%s'", argv[1]);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001368 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001369 } else if (argc == 3) {
1370 if (parse_old_offset(argv[1], &o1)
1371 && parse_old_offset(argv[2], &o2)
Denis Vlasenko1114de72006-10-10 23:26:05 +00001372 ) {
1373 n_bytes_to_skip = o1;
1374 flag_pseudo_start = 1;
1375 pseudo_start = o2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001376 argv[2] = argv[0];
Denis Vlasenko1114de72006-10-10 23:26:05 +00001377 argv += 2;
Denis Vlasenko601ae132006-11-28 23:37:46 +00001378 argc -= 2;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001379 } else {
1380 bb_error_msg_and_die("in compatibility mode "
1381 "the last two arguments must be offsets");
1382 }
Denis Vlasenko601ae132006-11-28 23:37:46 +00001383 } else if (argc > 3) {
Denis Vlasenko1114de72006-10-10 23:26:05 +00001384 bb_error_msg_and_die("compatibility mode supports "
1385 "at most three arguments");
1386 }
1387
1388 if (flag_pseudo_start) {
1389 if (format_address == format_address_none) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001390 address_base_char = 'o';
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001391 address_pad_len_char = '7';
Denis Vlasenko1114de72006-10-10 23:26:05 +00001392 format_address = format_address_paren;
Denis Vlasenko2425bdc2006-11-29 14:32:01 +00001393 } else
1394 format_address = format_address_label;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001395 }
1396 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +00001397#endif
Denis Vlasenko1114de72006-10-10 23:26:05 +00001398
1399 if (limit_bytes_to_format) {
1400 end_offset = n_bytes_to_skip + max_bytes_to_format;
1401 if (end_offset < n_bytes_to_skip)
1402 bb_error_msg_and_die("skip-bytes + read-bytes is too large");
1403 }
1404
1405 if (n_specs == 0) {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001406 decode_format_string("o2");
Denis Vlasenko1114de72006-10-10 23:26:05 +00001407 n_specs = 1;
1408 }
1409
Denis Vlasenko601ae132006-11-28 23:37:46 +00001410 /* If no files were listed on the command line,
1411 set the global pointer FILE_LIST so that it
1412 references the null-terminated list of one name: "-". */
1413 file_list = default_file_list;
1414 if (argc > 0) {
Denis Vlasenko1114de72006-10-10 23:26:05 +00001415 /* Set the global pointer FILE_LIST so that it
1416 references the first file-argument on the command-line. */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001417 file_list = (char const *const *) argv;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001418 }
1419
1420 /* open the first input file */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001421 open_next_file();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001422 /* skip over any unwanted header bytes */
Denis Vlasenko601ae132006-11-28 23:37:46 +00001423 skip(n_bytes_to_skip);
1424 if (!in_stream)
1425 return 1;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001426
1427 pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0);
1428
1429 /* Compute output block length. */
1430 l_c_m = get_lcm();
1431
Denis Vlasenko601ae132006-11-28 23:37:46 +00001432 if (opt & OPT_w) { /* -w: width */
1433 bytes_per_block = 32;
1434 if (str_w)
1435 bytes_per_block = xatou(str_w);
1436 if (!bytes_per_block || bytes_per_block % l_c_m != 0) {
Mike Frysinger5dd76862006-12-31 11:34:17 +00001437 bb_error_msg("warning: invalid width %zu; using %d instead",
Denis Vlasenko601ae132006-11-28 23:37:46 +00001438 bytes_per_block, l_c_m);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001439 bytes_per_block = l_c_m;
1440 }
1441 } else {
Denis Vlasenko601ae132006-11-28 23:37:46 +00001442 bytes_per_block = l_c_m;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001443 if (l_c_m < DEFAULT_BYTES_PER_BLOCK)
Denis Vlasenko601ae132006-11-28 23:37:46 +00001444 bytes_per_block *= DEFAULT_BYTES_PER_BLOCK / l_c_m;
Denis Vlasenko1114de72006-10-10 23:26:05 +00001445 }
1446
1447#ifdef DEBUG
1448 for (i = 0; i < n_specs; i++) {
1449 printf("%d: fmt=\"%s\" width=%d\n",
1450 i, spec[i].fmt_string, width_bytes[spec[i].size]);
1451 }
1452#endif
1453
Denis Vlasenko601ae132006-11-28 23:37:46 +00001454 if (flag_dump_strings)
1455 dump_strings();
1456 else
1457 dump();
Denis Vlasenko1114de72006-10-10 23:26:05 +00001458
Denis Vlasenko601ae132006-11-28 23:37:46 +00001459 if (fclose(stdin) == EOF)
1460 bb_perror_msg_and_die(bb_msg_standard_input);
Denis Vlasenko1114de72006-10-10 23:26:05 +00001461
Denis Vlasenko601ae132006-11-28 23:37:46 +00001462 return (ioerror != 0); /* err != 0 - return 1 (failure) */
Denis Vlasenko1114de72006-10-10 23:26:05 +00001463}