123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428 |
- /* od -- dump files in octal and other formats
- Copyright (C) 92, 1995-2004 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software Foundation,
- Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
- /* Written by Jim Meyering. */
- /* Busyboxed by Denys Vlasenko
- Based on od.c from coreutils-5.2.1
- Top bloat sources:
- 00000073 t parse_old_offset
- 0000007b t get_lcm
- 00000090 r long_options
- 00000092 t print_named_ascii
- 000000bf t print_ascii
- 00000168 t write_block
- 00000366 t decode_format_string
- 00000a71 T od_main
- Tested for compat with coreutils 6.3
- using this script. Minor differences fixed.
- #!/bin/sh
- echo STD
- time /path/to/coreutils/od \
- ...params... \
- >std
- echo Exit code $?
- echo BBOX
- time ./busybox od \
- ...params... \
- >bbox
- echo Exit code $?
- diff -u -a std bbox >bbox.diff || { echo Different!; sleep 1; }
- */
- #include "libbb.h"
- #define assert(a) ((void)0)
- /* Check for 0x7f is a coreutils 6.3 addition */
- #define ISPRINT(c) (((c)>=' ') && (c) != 0x7f)
- typedef long double longdouble_t;
- typedef unsigned long long ulonglong_t;
- typedef long long llong;
- #if ENABLE_LFS
- # define xstrtooff_sfx xstrtoull_sfx
- #else
- # define xstrtooff_sfx xstrtoul_sfx
- #endif
- /* The default number of input bytes per output line. */
- #define DEFAULT_BYTES_PER_BLOCK 16
- /* The number of decimal digits of precision in a float. */
- #ifndef FLT_DIG
- # define FLT_DIG 7
- #endif
- /* The number of decimal digits of precision in a double. */
- #ifndef DBL_DIG
- # define DBL_DIG 15
- #endif
- /* The number of decimal digits of precision in a long double. */
- #ifndef LDBL_DIG
- # define LDBL_DIG DBL_DIG
- #endif
- enum size_spec {
- NO_SIZE,
- CHAR,
- SHORT,
- INT,
- LONG,
- LONG_LONG,
- FLOAT_SINGLE,
- FLOAT_DOUBLE,
- FLOAT_LONG_DOUBLE,
- N_SIZE_SPECS
- };
- enum output_format {
- SIGNED_DECIMAL,
- UNSIGNED_DECIMAL,
- OCTAL,
- HEXADECIMAL,
- FLOATING_POINT,
- NAMED_CHARACTER,
- CHARACTER
- };
- /* Each output format specification (from '-t spec' or from
- old-style options) is represented by one of these structures. */
- struct tspec {
- enum output_format fmt;
- enum size_spec size;
- void (*print_function) (size_t, const char *, const char *);
- char *fmt_string;
- int hexl_mode_trailer;
- int field_width;
- };
- /* Convert the number of 8-bit bytes of a binary representation to
- the number of characters (digits + sign if the type is signed)
- required to represent the same quantity in the specified base/type.
- For example, a 32-bit (4-byte) quantity may require a field width
- as wide as the following for these types:
- 11 unsigned octal
- 11 signed decimal
- 10 unsigned decimal
- 8 unsigned hexadecimal */
- static const uint8_t bytes_to_oct_digits[] ALIGN1 =
- {0, 3, 6, 8, 11, 14, 16, 19, 22, 25, 27, 30, 32, 35, 38, 41, 43};
- static const uint8_t bytes_to_signed_dec_digits[] ALIGN1 =
- {1, 4, 6, 8, 11, 13, 16, 18, 20, 23, 25, 28, 30, 33, 35, 37, 40};
- static const uint8_t bytes_to_unsigned_dec_digits[] ALIGN1 =
- {0, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39};
- static const uint8_t bytes_to_hex_digits[] ALIGN1 =
- {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32};
- /* Convert enum size_spec to the size of the named type. */
- static const signed char width_bytes[] ALIGN1 = {
- -1,
- sizeof(char),
- sizeof(short),
- sizeof(int),
- sizeof(long),
- sizeof(ulonglong_t),
- sizeof(float),
- sizeof(double),
- sizeof(longdouble_t)
- };
- /* Ensure that for each member of 'enum size_spec' there is an
- initializer in the width_bytes array. */
- struct ERR_width_bytes_has_bad_size {
- char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1];
- };
- static smallint flag_dump_strings;
- /* Non-zero if an old-style 'pseudo-address' was specified. */
- static smallint flag_pseudo_start;
- static smallint limit_bytes_to_format;
- /* When zero and two or more consecutive blocks are equal, format
- only the first block and output an asterisk alone on the following
- line to indicate that identical blocks have been elided. */
- static smallint verbose;
- static smallint ioerror;
- static size_t string_min;
- /* An array of specs describing how to format each input block. */
- static size_t n_specs;
- static struct tspec *spec;
- /* Function that accepts an address and an optional following char,
- and prints the address and char to stdout. */
- static void (*format_address)(off_t, char);
- /* The difference between the old-style pseudo starting address and
- the number of bytes to skip. */
- static off_t pseudo_offset;
- /* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all
- input is formatted. */
- /* The number of input bytes formatted per output line. It must be
- a multiple of the least common multiple of the sizes associated with
- the specified output types. It should be as large as possible, but
- no larger than 16 -- unless specified with the -w option. */
- static unsigned bytes_per_block = 32; /* have to use unsigned, not size_t */
- /* A NULL-terminated list of the file-arguments from the command line. */
- static const char *const *file_list;
- /* The input stream associated with the current file. */
- static FILE *in_stream;
- #define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t)
- static const unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] ALIGN1 = {
- [sizeof(char)] = CHAR,
- #if USHRT_MAX != UCHAR_MAX
- [sizeof(short)] = SHORT,
- #endif
- #if UINT_MAX != USHRT_MAX
- [sizeof(int)] = INT,
- #endif
- #if ULONG_MAX != UINT_MAX
- [sizeof(long)] = LONG,
- #endif
- #if ULLONG_MAX != ULONG_MAX
- [sizeof(ulonglong_t)] = LONG_LONG,
- #endif
- };
- #define MAX_FP_TYPE_SIZE sizeof(longdouble_t)
- static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = {
- /* gcc seems to allow repeated indexes. Last one stays */
- [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE,
- [sizeof(double)] = FLOAT_DOUBLE,
- [sizeof(float)] = FLOAT_SINGLE
- };
- static unsigned
- gcd(unsigned u, unsigned v)
- {
- unsigned t;
- while (v != 0) {
- t = u % v;
- u = v;
- v = t;
- }
- return u;
- }
- /* Compute the least common multiple of U and V. */
- static unsigned
- lcm(unsigned u, unsigned v) {
- unsigned t = gcd(u, v);
- if (t == 0)
- return 0;
- return u * v / t;
- }
- static void
- print_s_char(size_t n_bytes, const char *block, const char *fmt_string)
- {
- while (n_bytes--) {
- int tmp = *(signed char *) block;
- printf(fmt_string, tmp);
- block += sizeof(unsigned char);
- }
- }
- static void
- print_char(size_t n_bytes, const char *block, const char *fmt_string)
- {
- while (n_bytes--) {
- unsigned tmp = *(unsigned char *) block;
- printf(fmt_string, tmp);
- block += sizeof(unsigned char);
- }
- }
- static void
- print_s_short(size_t n_bytes, const char *block, const char *fmt_string)
- {
- n_bytes /= sizeof(signed short);
- while (n_bytes--) {
- int tmp = *(signed short *) block;
- printf(fmt_string, tmp);
- block += sizeof(unsigned short);
- }
- }
- static void
- print_short(size_t n_bytes, const char *block, const char *fmt_string)
- {
- n_bytes /= sizeof(unsigned short);
- while (n_bytes--) {
- unsigned tmp = *(unsigned short *) block;
- printf(fmt_string, tmp);
- block += sizeof(unsigned short);
- }
- }
- static void
- print_int(size_t n_bytes, const char *block, const char *fmt_string)
- {
- n_bytes /= sizeof(unsigned);
- while (n_bytes--) {
- unsigned tmp = *(unsigned *) block;
- printf(fmt_string, tmp);
- block += sizeof(unsigned);
- }
- }
- #if UINT_MAX == ULONG_MAX
- # define print_long print_int
- #else
- static void
- print_long(size_t n_bytes, const char *block, const char *fmt_string)
- {
- n_bytes /= sizeof(unsigned long);
- while (n_bytes--) {
- unsigned long tmp = *(unsigned long *) block;
- printf(fmt_string, tmp);
- block += sizeof(unsigned long);
- }
- }
- #endif
- #if ULONG_MAX == ULLONG_MAX
- # define print_long_long print_long
- #else
- static void
- print_long_long(size_t n_bytes, const char *block, const char *fmt_string)
- {
- n_bytes /= sizeof(ulonglong_t);
- while (n_bytes--) {
- ulonglong_t tmp = *(ulonglong_t *) block;
- printf(fmt_string, tmp);
- block += sizeof(ulonglong_t);
- }
- }
- #endif
- static void
- print_float(size_t n_bytes, const char *block, const char *fmt_string)
- {
- n_bytes /= sizeof(float);
- while (n_bytes--) {
- float tmp = *(float *) block;
- printf(fmt_string, tmp);
- block += sizeof(float);
- }
- }
- static void
- print_double(size_t n_bytes, const char *block, const char *fmt_string)
- {
- n_bytes /= sizeof(double);
- while (n_bytes--) {
- double tmp = *(double *) block;
- printf(fmt_string, tmp);
- block += sizeof(double);
- }
- }
- static void
- print_long_double(size_t n_bytes, const char *block, const char *fmt_string)
- {
- n_bytes /= sizeof(longdouble_t);
- while (n_bytes--) {
- longdouble_t tmp = *(longdouble_t *) block;
- printf(fmt_string, tmp);
- block += sizeof(longdouble_t);
- }
- }
- /* print_[named]_ascii are optimized for speed.
- * Remember, someday you may want to pump gigabytes through this thing.
- * Saving a dozen of .text bytes here is counter-productive */
- static void
- print_named_ascii(size_t n_bytes, const char *block,
- const char *unused_fmt_string UNUSED_PARAM)
- {
- /* Names for some non-printing characters. */
- static const char charname[33][3] ALIGN1 = {
- "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
- " bs", " ht", " nl", " vt", " ff", " cr", " so", " si",
- "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
- "can", " em", "sub", "esc", " fs", " gs", " rs", " us",
- " sp"
- };
- // buf[N] pos: 01234 56789
- char buf[12] = " x\0 0xx\0";
- // actually " x\0 xxx\0", but I want to share the string with below.
- // [12] because we take three 32bit stack slots anyway, and
- // gcc is too dumb to initialize with constant stores,
- // it copies initializer from rodata. Oh well.
- while (n_bytes--) {
- unsigned masked_c = *(unsigned char *) block++;
- masked_c &= 0x7f;
- if (masked_c == 0x7f) {
- fputs(" del", stdout);
- continue;
- }
- if (masked_c > ' ') {
- buf[3] = masked_c;
- fputs(buf, stdout);
- continue;
- }
- /* Why? Because printf(" %3.3s") is much slower... */
- buf[6] = charname[masked_c][0];
- buf[7] = charname[masked_c][1];
- buf[8] = charname[masked_c][2];
- fputs(buf+5, stdout);
- }
- }
- static void
- print_ascii(size_t n_bytes, const char *block,
- const char *unused_fmt_string UNUSED_PARAM)
- {
- // buf[N] pos: 01234 56789
- char buf[12] = " x\0 0xx\0";
- while (n_bytes--) {
- const char *s;
- unsigned c = *(unsigned char *) block++;
- if (ISPRINT(c)) {
- buf[3] = c;
- fputs(buf, stdout);
- continue;
- }
- switch (c) {
- case '\0':
- s = " \\0";
- break;
- case '\007':
- s = " \\a";
- break;
- case '\b':
- s = " \\b";
- break;
- case '\f':
- s = " \\f";
- break;
- case '\n':
- s = " \\n";
- break;
- case '\r':
- s = " \\r";
- break;
- case '\t':
- s = " \\t";
- break;
- case '\v':
- s = " \\v";
- break;
- case '\x7f':
- s = " 177";
- break;
- default: /* c is never larger than 040 */
- buf[7] = (c >> 3) + '0';
- buf[8] = (c & 7) + '0';
- s = buf + 5;
- }
- fputs(s, stdout);
- }
- }
- /* Given a list of one or more input filenames FILE_LIST, set the global
- file pointer IN_STREAM and the global string INPUT_FILENAME to the
- first one that can be successfully opened. Modify FILE_LIST to
- reference the next filename in the list. A file name of "-" is
- interpreted as standard input. If any file open fails, give an error
- message and return nonzero. */
- static void
- open_next_file(void)
- {
- while (1) {
- if (!*file_list)
- return;
- in_stream = fopen_or_warn_stdin(*file_list++);
- if (in_stream) {
- break;
- }
- ioerror = 1;
- }
- if (limit_bytes_to_format && !flag_dump_strings)
- setbuf(in_stream, NULL);
- }
- /* Test whether there have been errors on in_stream, and close it if
- it is not standard input. Return nonzero if there has been an error
- on in_stream or stdout; return zero otherwise. This function will
- report more than one error only if both a read and a write error
- have occurred. IN_ERRNO, if nonzero, is the error number
- corresponding to the most recent action for IN_STREAM. */
- static void
- check_and_close(void)
- {
- if (in_stream) {
- if (ferror(in_stream)) {
- bb_error_msg("%s: read error", (in_stream == stdin)
- ? bb_msg_standard_input
- : file_list[-1]
- );
- ioerror = 1;
- }
- fclose_if_not_stdin(in_stream);
- in_stream = NULL;
- }
- if (ferror(stdout)) {
- bb_error_msg("write error");
- ioerror = 1;
- }
- }
- /* If S points to a single valid modern od format string, put
- a description of that format in *TSPEC, return pointer to
- character following the just-decoded format.
- For example, if S were "d4afL", we will return a rtp to "afL"
- and *TSPEC would be
- {
- fmt = SIGNED_DECIMAL;
- size = INT or LONG; (whichever integral_type_size[4] resolves to)
- print_function = print_int; (assuming size == INT)
- fmt_string = "%011d%c";
- }
- S_ORIG is solely for reporting errors. It should be the full format
- string argument. */
- static const char *
- decode_one_format(const char *s_orig, const char *s, struct tspec *tspec)
- {
- enum size_spec size_spec;
- unsigned size;
- enum output_format fmt;
- const char *p;
- char *end;
- char *fmt_string = NULL;
- void (*print_function) (size_t, const char *, const char *);
- unsigned c;
- unsigned field_width = 0;
- int pos;
- switch (*s) {
- case 'd':
- case 'o':
- case 'u':
- case 'x': {
- static const char CSIL[] ALIGN1 = "CSIL";
- c = *s++;
- p = strchr(CSIL, *s);
- if (!p) {
- size = sizeof(int);
- if (isdigit(s[0])) {
- size = bb_strtou(s, &end, 0);
- if (errno == ERANGE
- || MAX_INTEGRAL_TYPE_SIZE < size
- || integral_type_size[size] == NO_SIZE
- ) {
- bb_error_msg_and_die("invalid type string '%s'; "
- "%u-byte %s type is not supported",
- s_orig, size, "integral");
- }
- s = end;
- }
- } else {
- static const uint8_t CSIL_sizeof[4] = {
- sizeof(char),
- sizeof(short),
- sizeof(int),
- sizeof(long),
- };
- size = CSIL_sizeof[p - CSIL];
- s++; /* skip C/S/I/L */
- }
- #define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \
- ((Spec) == LONG_LONG ? (Max_format) \
- : ((Spec) == LONG ? (Long_format) : (Min_format)))
- #define FMT_BYTES_ALLOCATED 9
- size_spec = integral_type_size[size];
- {
- static const char doux[] ALIGN1 = "doux";
- static const char doux_fmt_letter[][4] = {
- "lld", "llo", "llu", "llx"
- };
- static const enum output_format doux_fmt[] = {
- SIGNED_DECIMAL,
- OCTAL,
- UNSIGNED_DECIMAL,
- HEXADECIMAL,
- };
- static const uint8_t *const doux_bytes_to_XXX[] = {
- bytes_to_signed_dec_digits,
- bytes_to_oct_digits,
- bytes_to_unsigned_dec_digits,
- bytes_to_hex_digits,
- };
- static const char doux_fmtstring[][sizeof(" %%0%u%s")] = {
- " %%%u%s",
- " %%0%u%s",
- " %%%u%s",
- " %%0%u%s",
- };
- pos = strchr(doux, c) - doux;
- fmt = doux_fmt[pos];
- field_width = doux_bytes_to_XXX[pos][size];
- p = doux_fmt_letter[pos] + 2;
- if (size_spec == LONG) p--;
- if (size_spec == LONG_LONG) p -= 2;
- fmt_string = xasprintf(doux_fmtstring[pos], field_width, p);
- }
- switch (size_spec) {
- case CHAR:
- print_function = (fmt == SIGNED_DECIMAL
- ? print_s_char
- : print_char);
- break;
- case SHORT:
- print_function = (fmt == SIGNED_DECIMAL
- ? print_s_short
- : print_short);
- break;
- case INT:
- print_function = print_int;
- break;
- case LONG:
- print_function = print_long;
- break;
- default: /* case LONG_LONG: */
- print_function = print_long_long;
- break;
- }
- break;
- }
- case 'f': {
- static const char FDL[] ALIGN1 = "FDL";
- fmt = FLOATING_POINT;
- ++s;
- p = strchr(FDL, *s);
- if (!p) {
- size = sizeof(double);
- if (isdigit(s[0])) {
- size = bb_strtou(s, &end, 0);
- if (errno == ERANGE || size > MAX_FP_TYPE_SIZE
- || fp_type_size[size] == NO_SIZE
- ) {
- bb_error_msg_and_die("invalid type string '%s'; "
- "%u-byte %s type is not supported",
- s_orig, size, "floating point");
- }
- s = end;
- }
- } else {
- static const uint8_t FDL_sizeof[] = {
- sizeof(float),
- sizeof(double),
- sizeof(longdouble_t),
- };
- size = FDL_sizeof[p - FDL];
- }
- size_spec = fp_type_size[size];
- switch (size_spec) {
- case FLOAT_SINGLE:
- print_function = print_float;
- field_width = FLT_DIG + 8;
- /* Don't use %#e; not all systems support it. */
- fmt_string = xasprintf(" %%%d.%de", field_width, FLT_DIG);
- break;
- case FLOAT_DOUBLE:
- print_function = print_double;
- field_width = DBL_DIG + 8;
- fmt_string = xasprintf(" %%%d.%de", field_width, DBL_DIG);
- break;
- default: /* case FLOAT_LONG_DOUBLE: */
- print_function = print_long_double;
- field_width = LDBL_DIG + 8;
- fmt_string = xasprintf(" %%%d.%dLe", field_width, LDBL_DIG);
- break;
- }
- break;
- }
- case 'a':
- ++s;
- fmt = NAMED_CHARACTER;
- size_spec = CHAR;
- print_function = print_named_ascii;
- field_width = 3;
- break;
- case 'c':
- ++s;
- fmt = CHARACTER;
- size_spec = CHAR;
- print_function = print_ascii;
- field_width = 3;
- break;
- default:
- bb_error_msg_and_die("invalid character '%c' "
- "in type string '%s'", *s, s_orig);
- }
- tspec->size = size_spec;
- tspec->fmt = fmt;
- tspec->print_function = print_function;
- tspec->fmt_string = fmt_string;
- tspec->field_width = field_width;
- tspec->hexl_mode_trailer = (*s == 'z');
- if (tspec->hexl_mode_trailer)
- s++;
- return s;
- }
- /* Decode the modern od format string S. Append the decoded
- representation to the global array SPEC, reallocating SPEC if
- necessary. */
- static void
- decode_format_string(const char *s)
- {
- const char *s_orig = s;
- while (*s != '\0') {
- struct tspec tspec;
- const char *next;
- next = decode_one_format(s_orig, s, &tspec);
- assert(s != next);
- s = next;
- spec = xrealloc_vector(spec, 4, n_specs);
- memcpy(&spec[n_specs], &tspec, sizeof(spec[0]));
- n_specs++;
- }
- }
- /* Given a list of one or more input filenames FILE_LIST, set the global
- file pointer IN_STREAM to position N_SKIP in the concatenation of
- those files. If any file operation fails or if there are fewer than
- N_SKIP bytes in the combined input, give an error message and return
- nonzero. When possible, use seek rather than read operations to
- advance IN_STREAM. */
- static void
- skip(off_t n_skip)
- {
- if (n_skip == 0)
- return;
- while (in_stream) { /* !EOF */
- struct stat file_stats;
- /* First try seeking. For large offsets, this extra work is
- worthwhile. If the offset is below some threshold it may be
- more efficient to move the pointer by reading. There are two
- issues when trying to seek:
- - the file must be seekable.
- - before seeking to the specified position, make sure
- that the new position is in the current file.
- Try to do that by getting file's size using fstat.
- But that will work only for regular files. */
- /* The st_size field is valid only for regular files
- (and for symbolic links, which cannot occur here).
- If the number of bytes left to skip is at least
- as large as the size of the current file, we can
- decrement n_skip and go on to the next file. */
- if (fstat(fileno(in_stream), &file_stats) == 0
- && S_ISREG(file_stats.st_mode) && file_stats.st_size > 0
- ) {
- if (file_stats.st_size < n_skip) {
- n_skip -= file_stats.st_size;
- /* take "check & close / open_next" route */
- } else {
- if (fseeko(in_stream, n_skip, SEEK_CUR) != 0)
- ioerror = 1;
- return;
- }
- } else {
- /* If it's not a regular file with positive size,
- position the file pointer by reading. */
- char buf[1024];
- size_t n_bytes_to_read = 1024;
- size_t n_bytes_read;
- while (n_skip > 0) {
- if (n_skip < n_bytes_to_read)
- n_bytes_to_read = n_skip;
- n_bytes_read = fread(buf, 1, n_bytes_to_read, in_stream);
- n_skip -= n_bytes_read;
- if (n_bytes_read != n_bytes_to_read)
- break; /* EOF on this file or error */
- }
- }
- if (n_skip == 0)
- return;
- check_and_close();
- open_next_file();
- }
- if (n_skip)
- bb_error_msg_and_die("cannot skip past end of combined input");
- }
- typedef void FN_format_address(off_t address, char c);
- static void
- format_address_none(off_t address UNUSED_PARAM, char c UNUSED_PARAM)
- {
- }
- static char address_fmt[] ALIGN1 = "%0n"OFF_FMT"xc";
- /* Corresponds to 'x' above */
- #define address_base_char address_fmt[sizeof(address_fmt)-3]
- /* Corresponds to 'n' above */
- #define address_pad_len_char address_fmt[2]
- static void
- format_address_std(off_t address, char c)
- {
- /* Corresponds to 'c' */
- address_fmt[sizeof(address_fmt)-2] = c;
- printf(address_fmt, address);
- }
- #if ENABLE_GETOPT_LONG
- /* only used with --traditional */
- static void
- format_address_paren(off_t address, char c)
- {
- putchar('(');
- format_address_std(address, ')');
- if (c) putchar(c);
- }
- static void
- format_address_label(off_t address, char c)
- {
- format_address_std(address, ' ');
- format_address_paren(address + pseudo_offset, c);
- }
- #endif
- static void
- dump_hexl_mode_trailer(size_t n_bytes, const char *block)
- {
- fputs(" >", stdout);
- while (n_bytes--) {
- unsigned c = *(unsigned char *) block++;
- c = (ISPRINT(c) ? c : '.');
- putchar(c);
- }
- putchar('<');
- }
- /* Write N_BYTES bytes from CURR_BLOCK to standard output once for each
- of the N_SPEC format specs. CURRENT_OFFSET is the byte address of
- CURR_BLOCK in the concatenation of input files, and it is printed
- (optionally) only before the output line associated with the first
- format spec. When duplicate blocks are being abbreviated, the output
- for a sequence of identical input blocks is the output for the first
- block followed by an asterisk alone on a line. It is valid to compare
- the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK.
- That condition may be false only for the last input block -- and then
- only when it has not been padded to length BYTES_PER_BLOCK. */
- static void
- write_block(off_t current_offset, size_t n_bytes,
- const char *prev_block, const char *curr_block)
- {
- static char first = 1;
- static char prev_pair_equal = 0;
- size_t i;
- if (!verbose && !first
- && n_bytes == bytes_per_block
- && memcmp(prev_block, curr_block, bytes_per_block) == 0
- ) {
- if (prev_pair_equal) {
- /* The two preceding blocks were equal, and the current
- block is the same as the last one, so print nothing. */
- } else {
- puts("*");
- prev_pair_equal = 1;
- }
- } else {
- first = 0;
- prev_pair_equal = 0;
- for (i = 0; i < n_specs; i++) {
- if (i == 0)
- format_address(current_offset, '\0');
- else
- printf("%*s", address_pad_len_char - '0', "");
- (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string);
- if (spec[i].hexl_mode_trailer) {
- /* space-pad out to full line width, then dump the trailer */
- int datum_width = width_bytes[spec[i].size];
- int blank_fields = (bytes_per_block - n_bytes) / datum_width;
- int field_width = spec[i].field_width + 1;
- printf("%*s", blank_fields * field_width, "");
- dump_hexl_mode_trailer(n_bytes, curr_block);
- }
- putchar('\n');
- }
- }
- }
- static void
- read_block(size_t n, char *block, size_t *n_bytes_in_buffer)
- {
- assert(0 < n && n <= bytes_per_block);
- *n_bytes_in_buffer = 0;
- if (n == 0)
- return;
- while (in_stream != NULL) { /* EOF. */
- size_t n_needed;
- size_t n_read;
- n_needed = n - *n_bytes_in_buffer;
- n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, in_stream);
- *n_bytes_in_buffer += n_read;
- if (n_read == n_needed)
- break;
- /* error check is done in check_and_close */
- check_and_close();
- open_next_file();
- }
- }
- /* Return the least common multiple of the sizes associated
- with the format specs. */
- static int
- get_lcm(void)
- {
- size_t i;
- int l_c_m = 1;
- for (i = 0; i < n_specs; i++)
- l_c_m = lcm(l_c_m, width_bytes[(int) spec[i].size]);
- return l_c_m;
- }
- #if ENABLE_GETOPT_LONG
- /* If S is a valid traditional offset specification with an optional
- leading '+' return nonzero and set *OFFSET to the offset it denotes. */
- static int
- parse_old_offset(const char *s, off_t *offset)
- {
- static const struct suffix_mult Bb[] = {
- { "B", 1024 },
- { "b", 512 },
- { }
- };
- char *p;
- int radix;
- /* Skip over any leading '+'. */
- if (s[0] == '+') ++s;
- /* Determine the radix we'll use to interpret S. If there is a '.',
- * it's decimal, otherwise, if the string begins with '0X'or '0x',
- * it's hexadecimal, else octal. */
- p = strchr(s, '.');
- radix = 8;
- if (p) {
- p[0] = '\0'; /* cheating */
- radix = 10;
- } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
- radix = 16;
- *offset = xstrtooff_sfx(s, radix, Bb);
- if (p) p[0] = '.';
- return (*offset >= 0);
- }
- #endif
- /* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
- formatted block to standard output, and repeat until the specified
- maximum number of bytes has been read or until all input has been
- processed. If the last block read is smaller than BYTES_PER_BLOCK
- and its size is not a multiple of the size associated with a format
- spec, extend the input block with zero bytes until its length is a
- multiple of all format spec sizes. Write the final block. Finally,
- write on a line by itself the offset of the byte after the last byte
- read. */
- static void
- dump(off_t current_offset, off_t end_offset)
- {
- char *block[2];
- int idx;
- size_t n_bytes_read;
- block[0] = xmalloc(2*bytes_per_block);
- block[1] = block[0] + bytes_per_block;
- idx = 0;
- if (limit_bytes_to_format) {
- while (1) {
- size_t n_needed;
- if (current_offset >= end_offset) {
- n_bytes_read = 0;
- break;
- }
- n_needed = MIN(end_offset - current_offset,
- (off_t) bytes_per_block);
- read_block(n_needed, block[idx], &n_bytes_read);
- if (n_bytes_read < bytes_per_block)
- break;
- assert(n_bytes_read == bytes_per_block);
- write_block(current_offset, n_bytes_read,
- block[!idx], block[idx]);
- current_offset += n_bytes_read;
- idx = !idx;
- }
- } else {
- while (1) {
- read_block(bytes_per_block, block[idx], &n_bytes_read);
- if (n_bytes_read < bytes_per_block)
- break;
- assert(n_bytes_read == bytes_per_block);
- write_block(current_offset, n_bytes_read,
- block[!idx], block[idx]);
- current_offset += n_bytes_read;
- idx = !idx;
- }
- }
- if (n_bytes_read > 0) {
- int l_c_m;
- size_t bytes_to_write;
- l_c_m = get_lcm();
- /* Make bytes_to_write the smallest multiple of l_c_m that
- is at least as large as n_bytes_read. */
- bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m);
- memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
- write_block(current_offset, bytes_to_write,
- block[!idx], block[idx]);
- current_offset += n_bytes_read;
- }
- format_address(current_offset, '\n');
- if (limit_bytes_to_format && current_offset >= end_offset)
- check_and_close();
- free(block[0]);
- }
- /* Read a single byte into *C from the concatenation of the input files
- named in the global array FILE_LIST. On the first call to this
- function, the global variable IN_STREAM is expected to be an open
- stream associated with the input file INPUT_FILENAME. If IN_STREAM
- is at end-of-file, close it and update the global variables IN_STREAM
- and INPUT_FILENAME so they correspond to the next file in the list.
- Then try to read a byte from the newly opened file. Repeat if
- necessary until EOF is reached for the last file in FILE_LIST, then
- set *C to EOF and return. Subsequent calls do likewise. */
- static void
- read_char(int *c)
- {
- while (in_stream) { /* !EOF */
- *c = fgetc(in_stream);
- if (*c != EOF)
- return;
- check_and_close();
- open_next_file();
- }
- *c = EOF;
- }
- /* Read N bytes into BLOCK from the concatenation of the input files
- named in the global array FILE_LIST. On the first call to this
- function, the global variable IN_STREAM is expected to be an open
- stream associated with the input file INPUT_FILENAME. If all N
- bytes cannot be read from IN_STREAM, close IN_STREAM and update
- the global variables IN_STREAM and INPUT_FILENAME. Then try to
- read the remaining bytes from the newly opened file. Repeat if
- necessary until EOF is reached for the last file in FILE_LIST.
- On subsequent calls, don't modify BLOCK and return zero. Set
- *N_BYTES_IN_BUFFER to the number of bytes read. If an error occurs,
- it will be detected through ferror when the stream is about to be
- closed. If there is an error, give a message but continue reading
- as usual and return nonzero. Otherwise return zero. */
- /* STRINGS mode. Find each "string constant" in the input.
- A string constant is a run of at least 'string_min' ASCII
- graphic (or formatting) characters terminated by a null.
- Based on a function written by Richard Stallman for a
- traditional version of od. */
- static void
- dump_strings(off_t address, off_t end_offset)
- {
- size_t bufsize = MAX(100, string_min);
- char *buf = xmalloc(bufsize);
- while (1) {
- size_t i;
- int c;
- /* See if the next 'string_min' chars are all printing chars. */
- tryline:
- if (limit_bytes_to_format && (end_offset - string_min <= address))
- break;
- i = 0;
- while (!limit_bytes_to_format || address < end_offset) {
- if (i == bufsize) {
- bufsize += bufsize/8;
- buf = xrealloc(buf, bufsize);
- }
- read_char(&c);
- if (c < 0) { /* EOF */
- free(buf);
- return;
- }
- address++;
- if (!c)
- break;
- if (!ISPRINT(c))
- goto tryline; /* It isn't; give up on this string. */
- buf[i++] = c; /* String continues; store it all. */
- }
- if (i < string_min) /* Too short! */
- goto tryline;
- /* If we get here, the string is all printable and NUL-terminated,
- * so print it. It is all in 'buf' and 'i' is its length. */
- buf[i] = 0;
- format_address(address - i - 1, ' ');
- for (i = 0; (c = buf[i]); i++) {
- switch (c) {
- case '\007': fputs("\\a", stdout); break;
- case '\b': fputs("\\b", stdout); break;
- case '\f': fputs("\\f", stdout); break;
- case '\n': fputs("\\n", stdout); break;
- case '\r': fputs("\\r", stdout); break;
- case '\t': fputs("\\t", stdout); break;
- case '\v': fputs("\\v", stdout); break;
- default: putchar(c);
- }
- }
- putchar('\n');
- }
- /* We reach this point only if we search through
- (max_bytes_to_format - string_min) bytes before reaching EOF. */
- free(buf);
- check_and_close();
- }
- int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
- int od_main(int argc, char **argv)
- {
- static const struct suffix_mult bkm[] = {
- { "b", 512 },
- { "k", 1024 },
- { "m", 1024*1024 },
- { }
- };
- enum {
- OPT_A = 1 << 0,
- OPT_N = 1 << 1,
- OPT_a = 1 << 2,
- OPT_b = 1 << 3,
- OPT_c = 1 << 4,
- OPT_d = 1 << 5,
- OPT_f = 1 << 6,
- OPT_h = 1 << 7,
- OPT_i = 1 << 8,
- OPT_j = 1 << 9,
- OPT_l = 1 << 10,
- OPT_o = 1 << 11,
- OPT_t = 1 << 12,
- OPT_v = 1 << 13,
- OPT_x = 1 << 14,
- OPT_s = 1 << 15,
- OPT_S = 1 << 16,
- OPT_w = 1 << 17,
- OPT_traditional = (1 << 18) * ENABLE_GETOPT_LONG,
- };
- #if ENABLE_GETOPT_LONG
- static const char od_longopts[] ALIGN1 =
- "skip-bytes\0" Required_argument "j"
- "address-radix\0" Required_argument "A"
- "read-bytes\0" Required_argument "N"
- "format\0" Required_argument "t"
- "output-duplicates\0" No_argument "v"
- "strings\0" Optional_argument "S"
- "width\0" Optional_argument "w"
- "traditional\0" No_argument "\xff"
- ;
- #endif
- char *str_A, *str_N, *str_j, *str_S;
- llist_t *lst_t = NULL;
- unsigned opt;
- int l_c_m;
- /* The old-style 'pseudo starting address' to be printed in parentheses
- after any true address. */
- off_t pseudo_start = pseudo_start; // for gcc
- /* The number of input bytes to skip before formatting and writing. */
- off_t n_bytes_to_skip = 0;
- /* The offset of the first byte after the last byte to be formatted. */
- off_t end_offset = 0;
- /* The maximum number of bytes that will be formatted. */
- off_t max_bytes_to_format = 0;
- spec = NULL;
- format_address = format_address_std;
- address_base_char = 'o';
- address_pad_len_char = '7';
- /* flag_dump_strings = 0; - already is */
- /* Parse command line */
- opt_complementary = "w+:t::"; /* -w N, -t is a list */
- #if ENABLE_GETOPT_LONG
- applet_long_options = od_longopts;
- #endif
- opt = getopt32(argv, "A:N:abcdfhij:lot:vxsS:"
- "w::", // -w with optional param
- // -S was -s and also had optional parameter
- // but in coreutils 6.3 it was renamed and now has
- // _mandatory_ parameter
- &str_A, &str_N, &str_j, &lst_t, &str_S, &bytes_per_block);
- argc -= optind;
- argv += optind;
- if (opt & OPT_A) {
- static const char doxn[] ALIGN1 = "doxn";
- static const char doxn_address_base_char[] ALIGN1 = {
- 'u', 'o', 'x', /* '?' fourth one is not important */
- };
- static const uint8_t doxn_address_pad_len_char[] ALIGN1 = {
- '7', '7', '6', /* '?' */
- };
- char *p;
- int pos;
- p = strchr(doxn, str_A[0]);
- if (!p)
- bb_error_msg_and_die("bad output address radix "
- "'%c' (must be [doxn])", str_A[0]);
- pos = p - doxn;
- if (pos == 3) format_address = format_address_none;
- address_base_char = doxn_address_base_char[pos];
- address_pad_len_char = doxn_address_pad_len_char[pos];
- }
- if (opt & OPT_N) {
- limit_bytes_to_format = 1;
- max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm);
- }
- if (opt & OPT_a) decode_format_string("a");
- if (opt & OPT_b) decode_format_string("oC");
- if (opt & OPT_c) decode_format_string("c");
- if (opt & OPT_d) decode_format_string("u2");
- if (opt & OPT_f) decode_format_string("fF");
- if (opt & OPT_h) decode_format_string("x2");
- if (opt & OPT_i) decode_format_string("d2");
- if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm);
- if (opt & OPT_l) decode_format_string("d4");
- if (opt & OPT_o) decode_format_string("o2");
- //if (opt & OPT_t)...
- while (lst_t) {
- decode_format_string(llist_pop(&lst_t));
- }
- if (opt & OPT_v) verbose = 1;
- if (opt & OPT_x) decode_format_string("x2");
- if (opt & OPT_s) decode_format_string("d2");
- if (opt & OPT_S) {
- string_min = 3;
- string_min = xstrtou_sfx(str_S, 0, bkm);
- flag_dump_strings = 1;
- }
- //if (opt & OPT_w)...
- //if (opt & OPT_traditional)...
- if (flag_dump_strings && n_specs > 0)
- bb_error_msg_and_die("no type may be specified when dumping strings");
- /* If the --traditional option is used, there may be from
- * 0 to 3 remaining command line arguments; handle each case
- * separately.
- * od [file] [[+]offset[.][b] [[+]label[.][b]]]
- * The offset and pseudo_start have the same syntax.
- *
- * FIXME: POSIX 1003.1-2001 with XSI requires support for the
- * traditional syntax even if --traditional is not given. */
- #if ENABLE_GETOPT_LONG
- if (opt & OPT_traditional) {
- off_t o1, o2;
- if (argc == 1) {
- if (parse_old_offset(argv[0], &o1)) {
- n_bytes_to_skip = o1;
- --argc;
- ++argv;
- }
- } else if (argc == 2) {
- if (parse_old_offset(argv[0], &o1)
- && parse_old_offset(argv[1], &o2)
- ) {
- n_bytes_to_skip = o1;
- flag_pseudo_start = 1;
- pseudo_start = o2;
- argv += 2;
- argc -= 2;
- } else if (parse_old_offset(argv[1], &o2)) {
- n_bytes_to_skip = o2;
- --argc;
- argv[1] = argv[0];
- ++argv;
- } else {
- bb_error_msg_and_die("invalid second operand "
- "in compatibility mode '%s'", argv[1]);
- }
- } else if (argc == 3) {
- if (parse_old_offset(argv[1], &o1)
- && parse_old_offset(argv[2], &o2)
- ) {
- n_bytes_to_skip = o1;
- flag_pseudo_start = 1;
- pseudo_start = o2;
- argv[2] = argv[0];
- argv += 2;
- argc -= 2;
- } else {
- bb_error_msg_and_die("in compatibility mode "
- "the last two arguments must be offsets");
- }
- } else if (argc > 3) {
- bb_error_msg_and_die("compatibility mode supports "
- "at most three arguments");
- }
- if (flag_pseudo_start) {
- if (format_address == format_address_none) {
- address_base_char = 'o';
- address_pad_len_char = '7';
- format_address = format_address_paren;
- } else
- format_address = format_address_label;
- }
- }
- #endif
- if (limit_bytes_to_format) {
- end_offset = n_bytes_to_skip + max_bytes_to_format;
- if (end_offset < n_bytes_to_skip)
- bb_error_msg_and_die("skip-bytes + read-bytes is too large");
- }
- if (n_specs == 0) {
- decode_format_string("o2");
- n_specs = 1;
- }
- /* If no files were listed on the command line,
- set the global pointer FILE_LIST so that it
- references the null-terminated list of one name: "-". */
- file_list = bb_argv_dash;
- if (argc > 0) {
- /* Set the global pointer FILE_LIST so that it
- references the first file-argument on the command-line. */
- file_list = (char const *const *) argv;
- }
- /* open the first input file */
- open_next_file();
- /* skip over any unwanted header bytes */
- skip(n_bytes_to_skip);
- if (!in_stream)
- return EXIT_FAILURE;
- pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0);
- /* Compute output block length. */
- l_c_m = get_lcm();
- if (opt & OPT_w) { /* -w: width */
- if (!bytes_per_block || bytes_per_block % l_c_m != 0) {
- bb_error_msg("warning: invalid width %u; using %d instead",
- (unsigned)bytes_per_block, l_c_m);
- bytes_per_block = l_c_m;
- }
- } else {
- bytes_per_block = l_c_m;
- if (l_c_m < DEFAULT_BYTES_PER_BLOCK)
- bytes_per_block *= DEFAULT_BYTES_PER_BLOCK / l_c_m;
- }
- #ifdef DEBUG
- for (i = 0; i < n_specs; i++) {
- printf("%d: fmt=\"%s\" width=%d\n",
- i, spec[i].fmt_string, width_bytes[spec[i].size]);
- }
- #endif
- if (flag_dump_strings)
- dump_strings(n_bytes_to_skip, end_offset);
- else
- dump(n_bytes_to_skip, end_offset);
- if (fclose(stdin) == EOF)
- bb_perror_msg_and_die(bb_msg_standard_input);
- return ioerror;
- }
|