busybox/libbb/dump.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Support code for the hexdump and od applets,
   4 * based on code from util-linux v 2.11l
   5 *
   6 * Copyright (c) 1989
   7 * The Regents of the University of California.  All rights reserved.
   8 *
   9 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  10 *
  11 * Original copyright notice is retained at the end of this file.
  12 */
  13#include "libbb.h"
  14#include "dump.h"
  15
  16#define F_IGNORE        0x01            /* %_A */
  17#define F_SETREP        0x02            /* rep count set, not default */
  18#define F_ADDRESS       0x001           /* print offset */
  19#define F_BPAD          0x002           /* blank pad */
  20#define F_C             0x004           /* %_c */
  21#define F_CHAR          0x008           /* %c */
  22#define F_DBL           0x010           /* %[EefGf] */
  23#define F_INT           0x020           /* %[di] */
  24#define F_P             0x040           /* %_p */
  25#define F_STR           0x080           /* %s */
  26#define F_U             0x100           /* %_u */
  27#define F_UINT          0x200           /* %[ouXx] */
  28#define F_TEXT          0x400           /* no conversions */
  29
  30typedef struct priv_dumper_t {
  31        dumper_t pub;
  32
  33        char **argv;
  34        FU *endfu;
  35        off_t savaddress;        /* saved address/offset in stream */
  36        off_t eaddress;          /* end address */
  37        int blocksize;
  38        smallint exitval;        /* final exit value */
  39
  40        /* former statics */
  41        smallint next__done;
  42        smallint get__ateof; // = 1;
  43        unsigned char *get__curp;
  44        unsigned char *get__savp;
  45} priv_dumper_t;
  46
  47static const char dot_flags_width_chars[] ALIGN1 = ".#-+ 0123456789";
  48
  49static const char size_conv_str[] ALIGN1 =
  50"\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG";
  51
  52static const char int_convs[] ALIGN1 = "diouxX";
  53
  54dumper_t* FAST_FUNC alloc_dumper(void)
  55{
  56        priv_dumper_t *dumper = xzalloc(sizeof(*dumper));
  57        dumper->pub.dump_length = -1;
  58        dumper->pub.dump_vflag = FIRST;
  59        dumper->get__ateof = 1;
  60        return &dumper->pub;
  61}
  62
  63static NOINLINE int bb_dump_size(FS *fs)
  64{
  65        FU *fu;
  66        int bcnt, cur_size;
  67        char *fmt;
  68        const char *p;
  69        int prec;
  70
  71        /* figure out the data block size needed for each format unit */
  72        for (cur_size = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
  73                if (fu->bcnt) {
  74                        cur_size += fu->bcnt * fu->reps;
  75                        continue;
  76                }
  77                for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
  78                        if (*fmt != '%')
  79                                continue;
  80                        /*
  81                         * skip any special chars -- save precision in
  82                         * case it's a %s format.
  83                         */
  84                        while (strchr(dot_flags_width_chars + 1, *++fmt))
  85                                continue;
  86                        if (*fmt == '.' && isdigit(*++fmt)) {
  87                                prec = atoi(fmt);
  88                                while (isdigit(*++fmt))
  89                                        continue;
  90                        }
  91                        p = strchr(size_conv_str + 12, *fmt);
  92                        if (!p) {
  93                                if (*fmt == 's') {
  94                                        bcnt += prec;
  95                                }
  96                                if (*fmt == '_') {
  97                                        ++fmt;
  98                                        if ((*fmt == 'c') || (*fmt == 'p') || (*fmt == 'u')) {
  99                                                bcnt += 1;
 100                                        }
 101                                }
 102                        } else {
 103                                bcnt += p[-12];
 104                        }
 105                }
 106                cur_size += bcnt * fu->reps;
 107        }
 108        return cur_size;
 109}
 110
 111static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
 112{
 113        FU *fu;
 114
 115        for (fu = fs->nextfu; fu; fu = fu->nextfu) {
 116                PR *pr;
 117                char *p1, *p2, *p3;
 118                char *fmtp;
 119                int nconv = 0;
 120                /*
 121                 * break each format unit into print units; each
 122                 * conversion character gets its own.
 123                 */
 124                for (fmtp = fu->fmt; *fmtp; ) {
 125                        unsigned len;
 126                        const char *prec;
 127                        const char *byte_count_str;
 128
 129                        /* DBU:[dvae@cray.com] zalloc so that forward ptrs start out NULL */
 130                        pr = xzalloc(sizeof(*pr));
 131                        if (!fu->nextpr)
 132                                fu->nextpr = pr;
 133
 134                        /* skip preceding text and up to the next % sign */
 135                        p1 = strchr(fmtp, '%');
 136                        if (!p1) { /* only text in the string */
 137                                pr->fmt = fmtp;
 138                                pr->flags = F_TEXT;
 139                                break;
 140                        }
 141
 142                        /*
 143                         * get precision for %s -- if have a byte count, don't
 144                         * need it.
 145                         */
 146                        prec = NULL;
 147                        if (fu->bcnt) {
 148                                /* skip to conversion character */
 149                                while (strchr(dot_flags_width_chars, *++p1))
 150                                        continue;
 151                        } else {
 152                                /* skip any special chars, field width */
 153                                while (strchr(dot_flags_width_chars + 1, *++p1))
 154                                        continue;
 155                                if (*p1 == '.' && isdigit(*++p1)) {
 156                                        prec = p1;
 157                                        while (isdigit(*++p1))
 158                                                continue;
 159                                }
 160                        }
 161
 162                        p2 = p1 + 1; /* set end pointer */
 163
 164                        /*
 165                         * figure out the byte count for each conversion;
 166                         * rewrite the format as necessary, set up blank-
 167                         * padding for end of data.
 168                         */
 169                        if (*p1 == 'c') {
 170                                pr->flags = F_CHAR;
 171 DO_BYTE_COUNT_1:
 172                                byte_count_str = "\001";
 173 DO_BYTE_COUNT:
 174                                if (fu->bcnt) {
 175                                        for (;;) {
 176                                                if (fu->bcnt == *byte_count_str)
 177                                                        break;
 178                                                if (*++byte_count_str == 0)
 179                                                        bb_error_msg_and_die("bad byte count for conversion character %s", p1);
 180                                        }
 181                                }
 182                                /* Unlike the original, output the remainder of the format string. */
 183                                pr->bcnt = *byte_count_str;
 184                        } else
 185                        if (*p1 == 'l') { /* %ld etc */
 186                                const char *e;
 187
 188                                ++p2;
 189                                ++p1;
 190 DO_INT_CONV:
 191                                e = strchr(int_convs, *p1); /* "diouxX"? */
 192                                if (!e)
 193                                        goto DO_BAD_CONV_CHAR;
 194                                pr->flags = F_INT;
 195                                if (e > int_convs + 1) /* not d or i? */
 196                                        pr->flags = F_UINT;
 197                                byte_count_str = "\004\002\001";
 198                                goto DO_BYTE_COUNT;
 199                        } else
 200                        if (strchr(int_convs, *p1)) { /* %d etc */
 201                                goto DO_INT_CONV;
 202                        } else
 203                        if (strchr("eEfgG", *p1)) { /* floating point */
 204                                pr->flags = F_DBL;
 205                                byte_count_str = "\010\004";
 206                                goto DO_BYTE_COUNT;
 207                        } else
 208                        if (*p1 == 's') {
 209                                pr->flags = F_STR;
 210                                pr->bcnt = fu->bcnt;
 211                                if (fu->bcnt == 0) {
 212                                        if (!prec)
 213                                                bb_simple_error_msg_and_die("%s needs precision or byte count");
 214                                        pr->bcnt = atoi(prec);
 215                                }
 216                        } else
 217                        if (*p1 == '_') {
 218                                p2++;  /* move past a in "%_a" */
 219                                switch (p1[1]) {
 220                                case 'A':       /* %_A[dox]: print address and the end */
 221                                        dumper->endfu = fu;
 222                                        fu->flags |= F_IGNORE;
 223                                        /* FALLTHROUGH */
 224                                case 'a':       /* %_a[dox]: current address */
 225                                        pr->flags = F_ADDRESS;
 226                                        p2++;  /* move past x in "%_ax" */
 227                                        if ((p1[2] != 'd') && (p1[2] != 'o') && (p1[2] != 'x')) {
 228                                                goto DO_BAD_CONV_CHAR;
 229                                        }
 230                                        *p1++ = 'l';
 231                                        *p1++ = 'l';
 232                                        break;
 233                                case 'c':       /* %_c: chars, \ooo, \n \r \t etc */
 234                                        pr->flags = F_C;
 235                                        /* *p1 = 'c';   set in conv_c */
 236                                        goto DO_BYTE_COUNT_1;
 237                                case 'p':       /* %_p: chars, dots for nonprintable */
 238                                        pr->flags = F_P;
 239                                        *p1 = 'c';
 240                                        goto DO_BYTE_COUNT_1;
 241                                case 'u':       /* %_p: chars, 'nul', 'esc' etc for nonprintable */
 242                                        pr->flags = F_U;
 243                                        /* *p1 = 'c';   set in conv_u */
 244                                        goto DO_BYTE_COUNT_1;
 245                                default:
 246                                        goto DO_BAD_CONV_CHAR;
 247                                }
 248                        } else {
 249 DO_BAD_CONV_CHAR:
 250                                bb_error_msg_and_die("bad conversion character %%%s", p1);
 251                        }
 252
 253                        /*
 254                         * copy to PR format string, set conversion character
 255                         * pointer, update original.
 256                         */
 257                        len = (p1 - fmtp) + 1;
 258                        pr->fmt = xstrndup(fmtp, len);
 259                        /* DBU:[dave@cray.com] w/o this, trailing fmt text, space is lost.
 260                         * Skip subsequent text and up to the next % sign and tack the
 261                         * additional text onto fmt: eg. if fmt is "%x is a HEX number",
 262                         * we lose the " is a HEX number" part of fmt.
 263                         */
 264                        for (p3 = p2; *p3 && *p3 != '%'; p3++)
 265                                continue;
 266                        if ((p3 - p2) != 0) {
 267                                char *d;
 268                                pr->fmt = d = xrealloc(pr->fmt, len + (p3 - p2) + 1);
 269                                d += len;
 270                                do {
 271                                        *d++ = *p2++;
 272                                } while (p2 != p3);
 273                                *d = '\0';
 274                                /* now p2 = p3 */
 275                        }
 276                        pr->cchar = pr->fmt + len - 1; /* must be after realloc! */
 277                        fmtp = p2;
 278
 279                        /* only one conversion character if byte count */
 280                        if (!(pr->flags & F_ADDRESS) && fu->bcnt && nconv++) {
 281                                bb_simple_error_msg_and_die("byte count with multiple conversion characters");
 282                        }
 283                }
 284                /*
 285                 * if format unit byte count not specified, figure it out
 286                 * so can adjust rep count later.
 287                 */
 288                if (fu->bcnt == 0)
 289                        for (pr = fu->nextpr; pr; pr = pr->nextpr)
 290                                fu->bcnt += pr->bcnt;
 291        }
 292        /*
 293         * if the format string interprets any data at all, and it's
 294         * not the same as the blocksize, and its last format unit
 295         * interprets any data at all, and has no iteration count,
 296         * repeat it as necessary.
 297         *
 298         * if rep count is greater than 1, no trailing whitespace
 299         * gets output from the last iteration of the format unit:
 300         * 2/1 "%02x " prints "XX XX", not "XX XX "
 301         * 2/1 "%02x\n" prints "XX\nXX", not "XX\nXX\n"
 302         */
 303        for (fu = fs->nextfu; fu; fu = fu->nextfu) {
 304                if (!fu->nextfu
 305                 && fs->bcnt < dumper->blocksize
 306                 && !(fu->flags & F_SETREP)
 307                 && fu->bcnt
 308                ) {
 309                        fu->reps += (dumper->blocksize - fs->bcnt) / fu->bcnt;
 310                }
 311                if (fu->reps > 1 && fu->nextpr) {
 312                        PR *pr;
 313                        char *p1, *p2;
 314
 315                        for (pr = fu->nextpr;; pr = pr->nextpr)
 316                                if (!pr->nextpr)
 317                                        break;
 318                        p2 = NULL;
 319                        for (p1 = pr->fmt; *p1; ++p1)
 320                                p2 = isspace(*p1) ? p1 : NULL;
 321                        if (p2)
 322                                pr->nospace = p2;
 323                }
 324        }
 325}
 326
 327static void do_skip(priv_dumper_t *dumper, const char *fname)
 328{
 329        struct stat sbuf;
 330
 331        xfstat(STDIN_FILENO, &sbuf, fname);
 332        if (S_ISREG(sbuf.st_mode)
 333         && dumper->pub.dump_skip >= sbuf.st_size
 334        ) {
 335                /* If st_size is valid and pub.dump_skip >= st_size */
 336                dumper->pub.dump_skip -= sbuf.st_size;
 337                dumper->pub.address += sbuf.st_size;
 338                return;
 339        }
 340        if (fseeko(stdin, dumper->pub.dump_skip, SEEK_SET)) {
 341                bb_simple_perror_msg_and_die(fname);
 342        }
 343        dumper->pub.address += dumper->pub.dump_skip;
 344        dumper->savaddress = dumper->pub.address;
 345        dumper->pub.dump_skip = 0;
 346}
 347
 348static NOINLINE int next(priv_dumper_t *dumper)
 349{
 350        for (;;) {
 351                const char *fname = *dumper->argv;
 352
 353                if (fname) {
 354                        dumper->argv++;
 355                        if (NOT_LONE_DASH(fname)) {
 356                                if (!freopen(fname, "r", stdin)) {
 357                                        bb_simple_perror_msg(fname);
 358                                        dumper->exitval = 1;
 359                                        dumper->next__done = 1;
 360                                        continue;
 361                                }
 362                        }
 363                } else {
 364                        if (dumper->next__done)
 365                                return 0; /* no next file */
 366                }
 367                dumper->next__done = 1;
 368                if (dumper->pub.dump_skip)
 369                        do_skip(dumper, fname ? fname : "stdin");
 370                if (dumper->pub.dump_skip == 0)
 371                        return 1;
 372        }
 373        /* NOTREACHED */
 374}
 375
 376static unsigned char *get(priv_dumper_t *dumper)
 377{
 378        int n;
 379        int need, nread;
 380        int blocksize = dumper->blocksize;
 381
 382        if (!dumper->get__curp) {
 383                dumper->pub.address = (off_t)0; /*DBU:[dave@cray.com] initialize,initialize..*/
 384                dumper->get__curp = xmalloc(blocksize);
 385                dumper->get__savp = xzalloc(blocksize); /* need to be initialized */
 386        } else {
 387                unsigned char *tmp = dumper->get__curp;
 388                dumper->get__curp = dumper->get__savp;
 389                dumper->get__savp = tmp;
 390                dumper->savaddress += blocksize;
 391                dumper->pub.address = dumper->savaddress;
 392        }
 393        need = blocksize;
 394        nread = 0;
 395        while (1) {
 396                /*
 397                 * if read the right number of bytes, or at EOF for one file,
 398                 * and no other files are available, zero-pad the rest of the
 399                 * block and set the end flag.
 400                 */
 401                if (!dumper->pub.dump_length || (dumper->get__ateof && !next(dumper))) {
 402                        if (need == blocksize) {
 403                                return NULL;
 404                        }
 405                        if (dumper->pub.dump_vflag != ALL   /* not "show all"? */
 406                         && dumper->pub.dump_vflag != FIRST /* not first line? */
 407                         && memcmp(dumper->get__curp, dumper->get__savp, nread) == 0 /* same data? */
 408                        ) {
 409                                if (dumper->pub.dump_vflag != DUP) {
 410                                        puts("*");
 411                                }
 412                        }
 413                        memset(dumper->get__curp + nread, 0, need);
 414                        dumper->eaddress = dumper->pub.address + nread;
 415                        return dumper->get__curp;
 416                }
 417                n = fread(dumper->get__curp + nread, sizeof(unsigned char),
 418                                dumper->pub.dump_length == -1 ? need : MIN(dumper->pub.dump_length, need), stdin);
 419                if (n == 0) {
 420                        if (ferror(stdin)) {
 421                                bb_simple_perror_msg(dumper->argv[-1]);
 422                        }
 423                        dumper->get__ateof = 1;
 424                        continue;
 425                }
 426                dumper->get__ateof = 0;
 427                if (dumper->pub.dump_length != -1) {
 428                        dumper->pub.dump_length -= n;
 429                }
 430                need -= n;
 431                if (need == 0) {
 432                        if (dumper->pub.dump_vflag == ALL   /* "show all"? */
 433                         || dumper->pub.dump_vflag == FIRST /* first line? */
 434                         || memcmp(dumper->get__curp, dumper->get__savp, blocksize) != 0 /* not same data? */
 435                        ) {
 436                                if (dumper->pub.dump_vflag == DUP || dumper->pub.dump_vflag == FIRST) {
 437                                        dumper->pub.dump_vflag = WAIT;
 438                                }
 439                                return dumper->get__curp;
 440                        }
 441                        if (dumper->pub.dump_vflag == WAIT) {
 442                                puts("*");
 443                        }
 444                        dumper->pub.dump_vflag = DUP;
 445                        dumper->savaddress += blocksize;
 446                        dumper->pub.address = dumper->savaddress;
 447                        need = blocksize;
 448                        nread = 0;
 449                } else {
 450                        nread += n;
 451                }
 452        }
 453}
 454
 455static void bpad(PR *pr)
 456{
 457        char *p1, *p2;
 458
 459        /*
 460         * remove all conversion flags; '-' is the only one valid
 461         * with %s, and it's not useful here.
 462         */
 463        pr->flags = F_BPAD;
 464        *pr->cchar = 's';
 465        for (p1 = pr->fmt; *p1 != '%'; ++p1)
 466                continue;
 467        for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1)
 468                if (pr->nospace)
 469                        pr->nospace--;
 470        while ((*p2++ = *p1++) != '\0')
 471                continue;
 472}
 473
 474static const char conv_str[] ALIGN1 =
 475        "\0"  "\\""0""\0"
 476        "\007""\\""a""\0"  /* \a */
 477        "\b"  "\\""b""\0"
 478        "\f"  "\\""f""\0"
 479        "\n"  "\\""n""\0"
 480        "\r"  "\\""r""\0"
 481        "\t"  "\\""t""\0"
 482        "\v"  "\\""v""\0"
 483        ;
 484
 485static void conv_c(PR *pr, unsigned char *p)
 486{
 487        const char *str = conv_str;
 488
 489        do {
 490                if (*p == *str) {
 491                        ++str;
 492                        goto strpr; /* map e.g. '\n' to "\\n" */
 493                }
 494                str += 4;
 495        } while (*str);
 496
 497        if (isprint_asciionly(*p)) {
 498                *pr->cchar = 'c';
 499                printf(pr->fmt, *p);
 500        } else {
 501                char buf[4];
 502                /* gcc-8.0.1 needs lots of casts to shut up */
 503                sprintf(buf, "%03o", (unsigned)(uint8_t)*p);
 504                str = buf;
 505 strpr:
 506                *pr->cchar = 's';
 507                printf(pr->fmt, str);
 508        }
 509}
 510
 511static void conv_u(PR *pr, unsigned char *p)
 512{
 513        static const char list[] ALIGN1 =
 514                "nul\0soh\0stx\0etx\0eot\0enq\0ack\0bel\0"
 515                "bs\0_ht\0_lf\0_vt\0_ff\0_cr\0_so\0_si\0_"
 516                "dle\0dcl\0dc2\0dc3\0dc4\0nak\0syn\0etb\0"
 517                "can\0em\0_sub\0esc\0fs\0_gs\0_rs\0_us";
 518
 519        /* od used nl, not lf */
 520        if (*p <= 0x1f) {
 521                *pr->cchar = 's';
 522                printf(pr->fmt, list + (4 * (int)*p));
 523        } else if (*p == 0x7f) {
 524                *pr->cchar = 's';
 525                printf(pr->fmt, "del");
 526        } else if (*p < 0x7f) { /* isprint() */
 527                *pr->cchar = 'c';
 528                printf(pr->fmt, *p);
 529        } else {
 530                *pr->cchar = 'x';
 531                printf(pr->fmt, (int) *p);
 532        }
 533}
 534
 535static void display(priv_dumper_t* dumper)
 536{
 537        unsigned char *bp;
 538        unsigned char savech = '\0';
 539
 540        while ((bp = get(dumper)) != NULL) {
 541                FS *fs;
 542                unsigned char *savebp;
 543                off_t saveaddress;
 544
 545                fs = dumper->pub.fshead;
 546                savebp = bp;
 547                saveaddress = dumper->pub.address;
 548                for (; fs; fs = fs->nextfs, bp = savebp, dumper->pub.address = saveaddress) {
 549                        FU *fu;
 550                        for (fu = fs->nextfu; fu; fu = fu->nextfu) {
 551                                int cnt;
 552                                if (fu->flags & F_IGNORE) {
 553                                        break;
 554                                }
 555                                for (cnt = fu->reps; cnt; --cnt) {
 556                                        PR *pr;
 557                                        for (pr = fu->nextpr; pr; dumper->pub.address += pr->bcnt,
 558                                                                bp += pr->bcnt, pr = pr->nextpr) {
 559                                                if (dumper->eaddress
 560                                                 && dumper->pub.address >= dumper->eaddress
 561                                                ) {
 562                                                        if (dumper->pub.xxd_eofstring) {
 563                                                                /* xxd support: requested to not pad incomplete blocks */
 564                                                                fputs_stdout(dumper->pub.xxd_eofstring);
 565                                                                return;
 566                                                        }
 567                                                        if (!(pr->flags & (F_TEXT | F_BPAD)))
 568                                                                bpad(pr);
 569                                                }
 570                                                if (cnt == 1 && pr->nospace) {
 571                                                        savech = *pr->nospace;
 572                                                        *pr->nospace = '\0';
 573                                                }
 574                                                switch (pr->flags) {
 575                                                case F_ADDRESS:
 576                                                        printf(pr->fmt, (unsigned long long) dumper->pub.address + dumper->pub.xxd_displayoff);
 577                                                        break;
 578                                                case F_BPAD:
 579                                                        printf(pr->fmt, "");
 580                                                        break;
 581                                                case F_C:
 582                                                        conv_c(pr, bp);
 583                                                        break;
 584                                                case F_CHAR:
 585                                                        printf(pr->fmt, *bp);
 586                                                        break;
 587                                                case F_DBL: {
 588                                                        double dval;
 589                                                        float fval;
 590
 591                                                        switch (pr->bcnt) {
 592                                                        case 4:
 593                                                                memcpy(&fval, bp, sizeof(fval));
 594                                                                printf(pr->fmt, fval);
 595                                                                break;
 596                                                        case 8:
 597                                                                memcpy(&dval, bp, sizeof(dval));
 598                                                                printf(pr->fmt, dval);
 599                                                                break;
 600                                                        }
 601                                                        break;
 602                                                }
 603                                                case F_INT: {
 604                                                        int ival;
 605                                                        short sval;
 606
 607                                                        switch (pr->bcnt) {
 608                                                        case 1:
 609                                                                printf(pr->fmt, (int) *bp);
 610                                                                break;
 611                                                        case 2:
 612                                                                memcpy(&sval, bp, sizeof(sval));
 613                                                                printf(pr->fmt, (int) sval);
 614                                                                break;
 615                                                        case 4:
 616                                                                memcpy(&ival, bp, sizeof(ival));
 617                                                                printf(pr->fmt, ival);
 618                                                                break;
 619                                                        }
 620                                                        break;
 621                                                }
 622                                                case F_P:
 623                                                        printf(pr->fmt, isprint_asciionly(*bp) ? *bp : '.');
 624                                                        break;
 625                                                case F_STR:
 626                                                        printf(pr->fmt, (char *) bp);
 627                                                        break;
 628                                                case F_TEXT:
 629                                                        printf(pr->fmt);
 630                                                        break;
 631                                                case F_U:
 632                                                        conv_u(pr, bp);
 633                                                        break;
 634                                                case F_UINT: {
 635                                                        unsigned ival;
 636                                                        unsigned short sval;
 637
 638                                                        switch (pr->bcnt) {
 639                                                        case 1:
 640                                                                printf(pr->fmt, (unsigned) *bp);
 641                                                                break;
 642                                                        case 2:
 643                                                                memcpy(&sval, bp, sizeof(sval));
 644                                                                printf(pr->fmt, (unsigned) sval);
 645                                                                break;
 646                                                        case 4:
 647                                                                memcpy(&ival, bp, sizeof(ival));
 648                                                                printf(pr->fmt, ival);
 649                                                                break;
 650                                                        }
 651                                                        break;
 652                                                }
 653                                                }
 654                                                if (cnt == 1 && pr->nospace) {
 655                                                        *pr->nospace = savech;
 656                                                }
 657                                        }
 658                                }
 659                        }
 660                }
 661        }
 662
 663        if (dumper->endfu) {
 664                PR *pr;
 665                /*
 666                 * if eaddress not set, error or file size was multiple
 667                 * of blocksize, and no partial block ever found.
 668                 */
 669                if (!dumper->eaddress) {
 670                        if (!dumper->pub.address) {
 671                                return;
 672                        }
 673                        dumper->eaddress = dumper->pub.address;
 674                }
 675                for (pr = dumper->endfu->nextpr; pr; pr = pr->nextpr) {
 676                        switch (pr->flags) {
 677                        case F_ADDRESS:
 678                                printf(pr->fmt, (unsigned long long) dumper->eaddress + dumper->pub.xxd_displayoff);
 679                                break;
 680                        case F_TEXT:
 681                                printf(pr->fmt);
 682                                break;
 683                        }
 684                }
 685        }
 686}
 687
 688#define dumper ((priv_dumper_t*)pub_dumper)
 689int FAST_FUNC bb_dump_dump(dumper_t *pub_dumper, char **argv)
 690{
 691        FS *tfs;
 692        int blocksize;
 693
 694        /* figure out the data block size */
 695        blocksize = 0;
 696        tfs = dumper->pub.fshead;
 697        while (tfs) {
 698                tfs->bcnt = bb_dump_size(tfs);
 699                if (blocksize < tfs->bcnt) {
 700                        blocksize = tfs->bcnt;
 701                }
 702                tfs = tfs->nextfs;
 703        }
 704        dumper->blocksize = blocksize;
 705
 706        /* rewrite the rules, do syntax checking */
 707        for (tfs = dumper->pub.fshead; tfs; tfs = tfs->nextfs) {
 708                rewrite(dumper, tfs);
 709        }
 710
 711        dumper->argv = argv;
 712        display(dumper);
 713
 714        return dumper->exitval;
 715}
 716
 717void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
 718{
 719        const char *p;
 720        FS *tfs;
 721        FU **nextfupp;
 722
 723        /* start new linked list of format units */
 724        tfs = xzalloc(sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */
 725        if (!dumper->pub.fshead) {
 726                dumper->pub.fshead = tfs;
 727        } else {
 728                FS *fslast = dumper->pub.fshead;
 729                while (fslast->nextfs)
 730                        fslast = fslast->nextfs;
 731                fslast->nextfs = tfs;
 732        }
 733        nextfupp = &tfs->nextfu;
 734
 735        /* take the format string and break it up into format units */
 736        p = fmt;
 737        for (;;) {
 738                FU *tfu;
 739                const char *savep;
 740
 741                p = skip_whitespace(p);
 742                if (*p == '\0') {
 743                        break;
 744                }
 745
 746                /* allocate a new format unit and link it in */
 747                /* NOSTRICT */
 748                /* DBU:[dave@cray.com] zalloc so that forward pointers start out NULL */
 749                tfu = xzalloc(sizeof(FU));
 750                *nextfupp = tfu;
 751                nextfupp = &tfu->nextfu;
 752                tfu->reps = 1;
 753
 754                /* if leading digit, repetition count */
 755                if (isdigit(*p)) {
 756                        for (savep = p; isdigit(*p); ++p)
 757                                continue;
 758                        if (!isspace(*p) && *p != '/') {
 759                                bb_error_msg_and_die("bad format {%s}", fmt);
 760                        }
 761                        /* may overwrite either white space or slash */
 762                        tfu->reps = atoi(savep);
 763                        tfu->flags = F_SETREP;
 764                        /* skip trailing white space */
 765                        p = skip_whitespace(++p);
 766                }
 767
 768                /* skip slash and trailing white space */
 769                if (*p == '/') {
 770                        p = skip_whitespace(p + 1);
 771                }
 772
 773                /* byte count */
 774                if (isdigit(*p)) {
 775// TODO: use bb_strtou
 776                        savep = p;
 777                        while (isdigit(*++p))
 778                                continue;
 779                        if (!isspace(*p)) {
 780                                bb_error_msg_and_die("bad format {%s}", fmt);
 781                        }
 782// Above check prohibits formats such as '/1"%02x"' - it requires space after 1.
 783// Other than this, formats can be pretty much jammed together:
 784// "%07_ax:"8/2 "%04x|""\n"
 785// but this space is required. The check *can* be removed, but
 786// keeping it to stay compat with util-linux hexdump.
 787                        tfu->bcnt = atoi(savep);
 788                        /* skip trailing white space */
 789                        p = skip_whitespace(p + 1);
 790                }
 791
 792                /* format */
 793                if (*p != '"') {
 794                        bb_error_msg_and_die("bad format {%s}", fmt);
 795                }
 796                for (savep = ++p; *p != '"';) {
 797                        if (*p++ == '\0') {
 798                                bb_error_msg_and_die("bad format {%s}", fmt);
 799                        }
 800                }
 801                tfu->fmt = xstrndup(savep, p - savep);
 802
 803                /* alphabetic escape sequences have to be done in place */
 804                strcpy_and_process_escape_sequences(tfu->fmt, tfu->fmt);
 805                /* unknown mappings are not changed: "\z" -> '\\' 'z' */
 806                /* trailing backslash, if any, is preserved */
 807#if 0
 808                char *p1;
 809                char *p2;
 810                p1 = tfu->fmt;
 811                for (p2 = p1;; ++p1, ++p2) {
 812                        *p2 = *p1;
 813                        if (*p1 == '\0')
 814                                break;
 815
 816                        if (*p1 == '\\') {
 817                                const char *cs;
 818
 819                                p1++;
 820                                *p2 = *p1;
 821                                if (*p1 == '\0') {
 822                                        /* "...\" trailing backslash. Eaten. */
 823                                        break;
 824                                }
 825                                cs = conv_str + 4; /* skip NUL element */
 826                                do {
 827                                        /* map e.g. "\n" -> '\n' */
 828                                        if (*p1 == cs[2]) {
 829                                                *p2 = cs[0];
 830                                                break;
 831                                        }
 832                                        cs += 4;
 833                                } while (*cs);
 834                                /* unknown mappings remove bkslash: "\z" -> 'z' */
 835                        }
 836                }
 837#endif
 838
 839                p++;
 840        }
 841}
 842
 843/*
 844 * Copyright (c) 1989 The Regents of the University of California.
 845 * All rights reserved.
 846 *
 847 * Redistribution and use in source and binary forms, with or without
 848 * modification, are permitted provided that the following conditions
 849 * are met:
 850 * 1. Redistributions of source code must retain the above copyright
 851 *    notice, this list of conditions and the following disclaimer.
 852 * 2. Redistributions in binary form must reproduce the above copyright
 853 *    notice, this list of conditions and the following disclaimer in the
 854 *    documentation and/or other materials provided with the distribution.
 855 * 3. Neither the name of the University nor the names of its contributors
 856 *    may be used to endorse or promote products derived from this software
 857 *    without specific prior written permission.
 858 *
 859 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
 860 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 861 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 862 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 863 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 864 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 865 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 866 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 867 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 868 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 869 * SUCH DAMAGE.
 870 */
 871