1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
  14  */
  15 
  16 /*
  17  * Copyright 2019 Joyent, Inc.
  18  */
  19 
  20 /*
  21  * od - octal dump.  Not really just octal anymore; read the POSIX
  22  * specification for it -- its more complex than you think!
  23  *
  24  * NB: We followed the POSIX semantics fairly strictly, where the
  25  * legacy code's behavior was in conflict.  In many cases the legacy
  26  * Solaris code was so completely broken as to be completely unusable.
  27  * (For example, the long double support was broken beyond
  28  * imagination!)  Note that GNU coreutils violates POSIX in a few
  29  * interesting ways, such as changing the numbering of the addresses
  30  * when skipping.  (Address starts should always be at 0, according to
  31  * the sample output in the Open Group man page.)
  32  */
  33 
  34 #include <stdio.h>
  35 #include <stdlib.h>
  36 #include <sys/types.h>
  37 #include <string.h>
  38 #include <err.h>
  39 #include <wchar.h>
  40 #include <locale.h>
  41 #include <unistd.h>
  42 #include <sys/stat.h>
  43 
  44 #define _(x)    gettext(x)
  45 
  46 
  47 #ifndef TEXT_DOMAIN
  48 #define TEXT_DOMAIN     "SYS_TEST"
  49 #endif
  50 
  51 /* address format */
  52 static char *afmt  =    "%07llo";
  53 static char *cfmt  =    "       ";
  54 
  55 static FILE *input = NULL;
  56 static size_t lcm = 1;
  57 static size_t blocksize = 16;
  58 static int numfiles = 0;
  59 static int curfile = 0;
  60 static char **files = NULL;
  61 static off_t limit = -1;
  62 
  63 /*
  64  * This structure describes our ring buffer.  Its always a power of 2
  65  * in size to make wrap around calculations fast using a mask instead
  66  * of doing modulo.
  67  *
  68  * The size is calculated thusly: We need three "blocks" of data, as
  69  * we process a block at a time (one block == one line of od output.)
  70  *
  71  * We need lookahead of an extra block to support multibyte chars.  We
  72  * also have a look behind so that we can avoid printing lines that
  73  * are identical to what we've already printed.  Finally, we need the
  74  * current block.
  75  *
  76  * The block size is determined by the least common multiple of the
  77  * data items being displayed.  Usually it will be 16, but sometimes
  78  * it is 24 (when 12-byte long doubles are presented.)
  79  *
  80  * The data buffer is allocaed via memalign to make sure it is
  81  * properly aligned.
  82  */
  83 typedef struct buffer {
  84         char    *data;          /* data buffer */
  85         int     prod;           /* producer index */
  86         int     cons;           /* consumer index */
  87         int     mask;           /* buffer size - 1, wraparound index */
  88         int     navail;         /* total bytes avail */
  89 } buffer_t;
  90 
  91 /*
  92  * This structure is used to provide information on a specific output
  93  * format.  We link them together in a list representing the output
  94  * formats that the user has selected.
  95  */
  96 typedef struct output {
  97         int     width;                          /* bytes consumed per call */
  98         void    (*func)(buffer_t *, int);       /* output function */
  99         struct output   *next;                  /* link node */
 100 } output_t;
 101 
 102 /*
 103  * Specifiers
 104  */
 105 
 106 typedef unsigned char           u8;
 107 typedef unsigned short          u16;
 108 typedef unsigned int            u32;
 109 typedef unsigned long long      u64;
 110 typedef char                    s8;
 111 typedef short                   s16;
 112 typedef int                     s32;
 113 typedef long long               s64;
 114 typedef float                   fF;
 115 typedef double                  fD;
 116 typedef long double             fL;
 117 
 118 static void
 119 usage(void)
 120 {
 121         (void) fprintf(stderr, _("usage: od [-bcCdDfFoOsSvxX] "
 122             "[-t types ]... [-A base] [-j skip] [-N count] [file]...\n"));
 123         exit(1);
 124 }
 125 
 126 #define DECL_GET(typ)                                                   \
 127 static typ                                                              \
 128 get_ ## typ(buffer_t *b, int index)                                     \
 129 {                                                                       \
 130         typ val = *(typ *)(void *)(b->data + index);                 \
 131         return (val);                                                   \
 132 }
 133 DECL_GET(u8)
 134 DECL_GET(u16)
 135 DECL_GET(u32)
 136 DECL_GET(u64)
 137 DECL_GET(s8)
 138 DECL_GET(s16)
 139 DECL_GET(s32)
 140 DECL_GET(s64)
 141 DECL_GET(fF)
 142 DECL_GET(fD)
 143 DECL_GET(fL)
 144 
 145 #define DECL_OUT(nm, typ, fmt)                                  \
 146 static void                                                     \
 147 do_ ## nm(buffer_t *buf, int index)                             \
 148 {                                                               \
 149         typ v = get_ ## typ(buf, index);                        \
 150         (void) printf(fmt, v);                                  \
 151 }                                                               \
 152                                                                 \
 153 static output_t output_ ## nm =  {                              \
 154         sizeof (typ), do_ ## nm                                 \
 155 };
 156 
 157 DECL_OUT(oct_b, u8, " %03o")
 158 DECL_OUT(oct_w, u16, " %06ho")
 159 DECL_OUT(oct_d, u32, " %011o")
 160 DECL_OUT(oct_q, u64, " %022llo")
 161 DECL_OUT(dec_b, u8, " %03u")
 162 DECL_OUT(dec_w, u16, " %05hu")
 163 DECL_OUT(dec_d, u32, " %010u")
 164 DECL_OUT(dec_q, u64, " %020llu")
 165 DECL_OUT(sig_b, s8, " %03d")
 166 DECL_OUT(sig_w, s16, " %6.05hd")
 167 DECL_OUT(sig_d, s32, " %11.010d")
 168 DECL_OUT(sig_q, s64, " %20.019lld")
 169 DECL_OUT(hex_b, u8, " %02x")
 170 DECL_OUT(hex_w, u16, " %04hx")
 171 DECL_OUT(hex_d, s32, " %08x")
 172 DECL_OUT(hex_q, s64, " %016llx")
 173 DECL_OUT(float, fF, " %14.7e")
 174 DECL_OUT(double, fD, " %21.14e")
 175 DECL_OUT(ldouble, fL, " %24.14Le")
 176 
 177 static char *ascii[] = {
 178         "nul", "soh", "stx", "etx", "eot", "enq", "ack", " be",
 179         " bs", " ht", " lf", " vt", " ff", " cr", " so", " si",
 180         "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
 181         "can", " em", "sub", "esc", " fs", " gs", " rs", " us",
 182         " sp", "  !", "  \"", "  #", "  $", "  %", "  &", "  '",
 183         "  (", "  )", "  *", "  +", "  ,", "  -", "  .", "  /",
 184         "  0", "  1", "  2", "  3", "  4", "  5", "  6", "  7",
 185         "  8", "  9", "  :", "  ;", "  <", "  =", "  >", "  ?",
 186         "  @", "  A", "  B", "  C", "  D", "  E", "  F", "  G",
 187         "  H", "  I", "  J", "  K", "  L", "  M", "  N", "  O",
 188         "  P", "  Q", "  R", "  S", "  T", "  U", "  V", "  W",
 189         "  X", "  Y", "  Z", "  [", "  \\", "  ]", "  ^", "  _",
 190         "  `", "  a", "  b", "  c", "  d", "  e", "  f", "  g",
 191         "  h", "  i", "  j", "  k", "  l", "  m", "  n", "  o",
 192         "  p", "  q", "  r", "  s", "  t", "  u", "  v", "  w",
 193         "  x", "  y", "  z", "  {", "  |", "  }", "  ~", "del"
 194 };
 195 
 196 static void
 197 do_ascii(buffer_t *buf, int index)
 198 {
 199         uint8_t v = get_u8(buf, index);
 200 
 201         (void) fputc(' ', stdout);
 202         (void) fputs(ascii[v & 0x7f], stdout);
 203 }
 204 
 205 static output_t output_ascii = {
 206         1, do_ascii,
 207 };
 208 
 209 static void
 210 do_char(buffer_t *buf, int index)
 211 {
 212         static int      nresid = 0;
 213         static int      printable = 0;
 214         int             cnt;
 215         int             avail;
 216         int             nb;
 217         char            scratch[10];
 218         wchar_t         wc;
 219         int             which;
 220 
 221         uint8_t v = get_u8(buf, index);
 222 
 223         /*
 224          * If there were residual bytes from an earlier
 225          * character, then just display the ** continuation
 226          * indication.
 227          */
 228         if (nresid) {
 229                 if (printable) {
 230                         (void) fputs("  **", stdout);
 231                 } else {
 232                         (void) printf(" %03o", v);
 233                 }
 234                 nresid--;
 235                 return;
 236         }
 237 
 238         /*
 239          * Peek ahead up to MB_CUR_MAX characters.  This has to be
 240          * done carefully because we might need to look into the next
 241          * block to really know for sure.
 242          */
 243         scratch[0] = v;
 244         avail = buf->navail;
 245         if (avail > MB_CUR_MAX)
 246                 avail = MB_CUR_MAX;
 247         for (cnt = 1, which = index + 1; cnt < avail; cnt++, which++) {
 248                 scratch[cnt] = buf->data[which & buf->mask];
 249         }
 250 
 251         /* now see if the value is a real character */
 252         nresid = 0;
 253         wc = 0;
 254         nb = mbtowc(&wc, scratch, avail);
 255         if (nb < 0) {
 256                 (void) printf(" %03o", v);
 257                 return;
 258         }
 259         if (nb == 0) {
 260                 (void) fputs("  \\0", stdout);
 261                 return;
 262         }
 263         nresid = nb - 1;
 264         if (nb && iswprint(wc)) {
 265                 scratch[nb] = 0;
 266                 (void) fputs("   ", stdout);
 267                 (void) fputs(scratch, stdout);
 268                 printable = 1;
 269                 return;
 270         }
 271         printable = 0;
 272         if (wc == 0) {
 273                 (void) fputs("  \\0", stdout);
 274         } else if (wc == '\b') {
 275                 (void) fputs("  \\b", stdout);
 276         } else if (wc == '\f') {
 277                 (void) fputs("  \\f", stdout);
 278         } else if (wc == '\n') {
 279                 (void) fputs("  \\n", stdout);
 280         } else if (wc == '\r') {
 281                 (void) fputs("  \\r", stdout);
 282         } else if (wc == '\t') {
 283                 (void) fputs("  \\t", stdout);
 284         } else {
 285                 (void) printf(" %03o", v);
 286         }
 287 }
 288 
 289 static output_t output_char = {
 290         1, do_char,
 291 };
 292 
 293 /*
 294  * List of output formatting structures.
 295  */
 296 static output_t *head = NULL;
 297 static output_t **tailp = &head;
 298 
 299 static void
 300 add_out(output_t *src)
 301 {
 302         output_t        *out;
 303         int             m;
 304 
 305         if ((out = calloc(1, sizeof (*src))) == NULL) {
 306                 err(1, "malloc");
 307         }
 308 
 309         m = lcm;
 310         while ((m % src->width) != 0) {
 311                 m += lcm;
 312         }
 313         lcm = m;
 314         blocksize = lcm;
 315         while (blocksize < 16)
 316                 blocksize *= 2;
 317 
 318         (void) memcpy(out, src, sizeof (*src));
 319         *tailp = out;
 320         tailp = &out->next;
 321 }
 322 
 323 static FILE *
 324 next_input(void)
 325 {
 326         for (;;) {
 327                 if (curfile >= numfiles)
 328                         return (NULL);
 329 
 330                 if (input != NULL) {
 331                         if ((input = freopen(files[curfile], "r", input)) !=
 332                             NULL) {
 333                                 curfile++;
 334                                 return (input);
 335                         }
 336                 } else {
 337                         if ((input = fopen(files[curfile], "r")) != NULL) {
 338                                 curfile++;
 339                                 return (input);
 340                         }
 341                 }
 342                 warn("open: %s", files[curfile]);
 343                 curfile++;
 344         }
 345 }
 346 
 347 static void
 348 refill(buffer_t *b)
 349 {
 350         int     n;
 351         int     want;
 352         int     zero;
 353 
 354         /*
 355          * If we have 2 blocks of bytes available, we're done.  Note
 356          * that each iteration usually loads up 16 bytes, unless we
 357          * run out of data.
 358          */
 359         while ((input != NULL) && (b->navail < (2 * blocksize))) {
 360 
 361                 /* we preload the next one in advance */
 362 
 363                 if (limit == 0) {
 364                         (void) fclose(input);
 365                         input = NULL;
 366                         continue;
 367                 }
 368 
 369                 /* we want to read a whole block if possible */
 370                 want = blocksize;
 371                 if ((limit >= 0) && (want > limit)) {
 372                         want = limit;
 373                 }
 374                 zero = blocksize;
 375 
 376                 while (want && input) {
 377                         int     c;
 378                         b->prod &= b->mask;
 379                         c = (b->prod + want > (b->mask + 1)) ?
 380                             b->mask - b->prod :
 381                             want;
 382 
 383                         n = fread(b->data + b->prod, 1, c, input);
 384                         if (n < 0) {
 385                                 warn("read: %s",
 386                                     files ? files[curfile-1] : "stdin");
 387                                 input = next_input();
 388                                 continue;
 389                         }
 390                         if (n == 0) {
 391                                 input = next_input();
 392                                 continue;
 393                         }
 394                         if (limit >= 0)
 395                                 limit -= n;
 396                         b->navail += n;
 397                         b->prod += n;
 398                         want -= n;
 399                         zero -= n;
 400                 }
 401 
 402                 while (zero) {
 403                         b->data[b->prod & b->mask] = 0;
 404                         b->prod++;
 405                         b->prod &= b->mask;
 406                         zero--;
 407                 }
 408         }
 409 }
 410 
 411 #define STR1    "C1"
 412 #define STR2    "S2"
 413 #ifdef  _LP64
 414 #define STR8    "L8"
 415 #define STR4    "I4"
 416 #else
 417 #define STR8    "8"
 418 #define STR4    "IL4"
 419 #endif
 420 
 421 static void
 422 do_type_string(char *typestr)
 423 {
 424         if (*typestr == 0) {
 425                 errx(1, _("missing type string"));
 426         }
 427         while (*typestr) {
 428                 switch (*typestr) {
 429                 case 'a':
 430                         typestr++;
 431                         add_out(&output_ascii);
 432                         break;
 433                 case 'c':
 434                         add_out(&output_char);
 435                         typestr++;
 436                         break;
 437                 case 'f':
 438                         typestr++;
 439                         switch (*typestr) {
 440                         case 'F':
 441                         case '4':
 442                                 add_out(&output_float);
 443                                 typestr++;
 444                                 break;
 445                         case '8':
 446                         case 'D':
 447                                 add_out(&output_double);
 448                                 typestr++;
 449                                 break;
 450                         case 'L':
 451                                 add_out(&output_ldouble);
 452                                 typestr++;
 453                                 break;
 454                         default:
 455                                 add_out(&output_float);
 456                                 break;
 457                         }
 458                         break;
 459 
 460 
 461                 case 'd':
 462                         typestr++;
 463                         if (strchr(STR1, *typestr)) {
 464                                 typestr++;
 465                                 add_out(&output_sig_b);
 466                         } else if (strchr(STR2, *typestr)) {
 467                                 typestr++;
 468                                 add_out(&output_sig_w);
 469                         } else if (strchr(STR4, *typestr)) {
 470                                 typestr++;
 471                                 add_out(&output_sig_d);
 472                         } else if (strchr(STR8, *typestr)) {
 473                                 typestr++;
 474                                 add_out(&output_sig_q);
 475                         } else {
 476                                 add_out(&output_sig_d);
 477                         }
 478                         break;
 479 
 480                 case 'u':
 481                         typestr++;
 482                         if (strchr(STR1, *typestr)) {
 483                                 typestr++;
 484                                 add_out(&output_dec_b);
 485                         } else if (strchr(STR2, *typestr)) {
 486                                 typestr++;
 487                                 add_out(&output_dec_w);
 488                         } else if (strchr(STR4, *typestr)) {
 489                                 typestr++;
 490                                 add_out(&output_dec_d);
 491                         } else if (strchr(STR8, *typestr)) {
 492                                 typestr++;
 493                                 add_out(&output_dec_q);
 494                         } else {
 495                                 add_out(&output_dec_d);
 496                         }
 497                         break;
 498 
 499                 case 'o':
 500                         typestr++;
 501                         if (strchr(STR1, *typestr)) {
 502                                 typestr++;
 503                                 add_out(&output_oct_b);
 504                         } else if (strchr(STR2, *typestr)) {
 505                                 typestr++;
 506                                 add_out(&output_oct_w);
 507                         } else if (strchr(STR4, *typestr)) {
 508                                 typestr++;
 509                                 add_out(&output_oct_d);
 510                         } else if (strchr(STR8, *typestr)) {
 511                                 typestr++;
 512                                 add_out(&output_oct_q);
 513                         } else {
 514                                 add_out(&output_oct_d);
 515                         }
 516                         break;
 517 
 518                 case 'x':
 519                         typestr++;
 520                         if (strchr(STR1, *typestr)) {
 521                                 typestr++;
 522                                 add_out(&output_hex_b);
 523                         } else if (strchr(STR2, *typestr)) {
 524                                 typestr++;
 525                                 add_out(&output_hex_w);
 526                         } else if (strchr(STR4, *typestr)) {
 527                                 typestr++;
 528                                 add_out(&output_hex_d);
 529                         } else if (strchr(STR8, *typestr)) {
 530                                 typestr++;
 531                                 add_out(&output_hex_q);
 532                         } else {
 533                                 add_out(&output_hex_d);
 534                         }
 535                         break;
 536 
 537                 default:
 538                         errx(1, _("unrecognized type string character: %c"),
 539                             *typestr);
 540                 }
 541         }
 542 }
 543 
 544 int
 545 main(int argc, char **argv)
 546 {
 547         int             c;
 548         int             i;
 549         buffer_t        buffer;
 550         boolean_t       first = B_TRUE;
 551         boolean_t       doall = B_FALSE;
 552         boolean_t       same = B_FALSE;
 553         boolean_t       newarg = B_FALSE;
 554         off_t           offset = 0;
 555         off_t           skip = 0;
 556         char            *eptr;
 557         char            *offstr = 0;
 558 
 559         input = stdin;
 560 
 561         (void) setlocale(LC_ALL, "");
 562         (void) textdomain(TEXT_DOMAIN);
 563 
 564         while ((c = getopt(argc, argv, "A:bCcdDfFj:N:oOsSxXvt:")) != EOF) {
 565                 switch (c) {
 566                 case 'A':
 567                         newarg = B_TRUE;
 568                         if (strlen(optarg) > 1) {
 569                                 afmt = NULL;
 570                         }
 571                         switch (*optarg) {
 572                         case 'o':
 573                                 afmt = "%07llo";
 574                                 cfmt = "       ";
 575                                 break;
 576                         case 'd':
 577                                 afmt = "%07lld";
 578                                 cfmt = "       ";
 579                                 break;
 580                         case 'x':
 581                                 afmt = "%07llx";
 582                                 cfmt = "       ";
 583                                 break;
 584                         case 'n':
 585                                 /*
 586                                  * You could argue that the code should
 587                                  * use the same 7 spaces.  Legacy uses 8
 588                                  * though.  Oh well.  Better to avoid
 589                                  * gratuitous change.
 590                                  */
 591                                 afmt = "        ";
 592                                 cfmt = "        ";
 593                                 break;
 594                         default:
 595                                 afmt = NULL;
 596                                 break;
 597                         }
 598                         if (strlen(optarg) != 1) {
 599                                 afmt = NULL;
 600                         }
 601                         if (afmt == NULL)
 602                                 warnx(_("invalid address base, "
 603                                     "must be o, d, x, or n"));
 604                         break;
 605 
 606                 case 'b':
 607                         add_out(&output_oct_b);
 608                         break;
 609 
 610                 case 'c':
 611                 case 'C':
 612                         add_out(&output_char);
 613                         break;
 614 
 615                 case 'f':
 616                         add_out(&output_float);
 617                         break;
 618 
 619                 case 'F':
 620                         add_out(&output_double);
 621                         break;
 622 
 623                 case 'd':
 624                         add_out(&output_dec_w);
 625                         break;
 626 
 627                 case 'D':
 628                         add_out(&output_dec_d);
 629                         break;
 630 
 631                 case 't':
 632                         newarg = B_TRUE;
 633                         do_type_string(optarg);
 634                         break;
 635 
 636                 case 'o':
 637                         add_out(&output_oct_w);
 638                         break;
 639 
 640                 case 'O':
 641                         add_out(&output_oct_d);
 642                         break;
 643 
 644                 case 's':
 645                         add_out(&output_sig_w);
 646                         break;
 647 
 648                 case 'S':
 649                         add_out(&output_sig_d);
 650                         break;
 651 
 652                 case 'x':
 653                         add_out(&output_hex_w);
 654                         break;
 655 
 656                 case 'X':
 657                         add_out(&output_hex_d);
 658                         break;
 659 
 660                 case 'v':
 661                         doall = B_TRUE;
 662                         break;
 663 
 664                 case 'j':
 665                         newarg = B_TRUE;
 666                         skip = strtoll(optarg, &eptr, 0);
 667                         if (*eptr == 'b') {
 668                                 skip <<= 9;       /* 512 bytes */
 669                                 eptr++;
 670                         } else if (*eptr == 'k') {
 671                                 skip <<= 10;      /* 1k */
 672                                 eptr++;
 673                         } else if (*eptr == 'm') {
 674                                 skip <<= 20;      /* 1m */
 675                                 eptr++;
 676                         } else if (*eptr == 'g') {
 677                                 skip <<= 30;      /* 1g */
 678                                 eptr++;
 679                         }
 680                         if ((skip < 0) || (eptr[0] != 0)) {
 681                                 warnx(_("invalid skip count '%s' specified"),
 682                                     optarg);
 683                                 exit(1);
 684                         }
 685                         break;
 686 
 687                 case 'N':
 688                         newarg = B_TRUE;
 689                         limit = strtoll(optarg, &eptr, 0);
 690                         /*
 691                          * POSIX doesn't specify this, but I think these
 692                          * may be helpful.
 693                          */
 694                         if (*eptr == 'b') {
 695                                 limit <<= 9;
 696                                 eptr++;
 697                         } else if (*eptr == 'k') {
 698                                 limit <<= 10;
 699                                 eptr++;
 700                         } else if (*eptr == 'm') {
 701                                 limit <<= 20;
 702                                 eptr++;
 703                         } else if (*eptr == 'g') {
 704                                 limit <<= 30;
 705                                 eptr++;
 706                         }
 707                         if ((limit < 0) || (eptr[0] != 0)) {
 708                                 warnx(_("invalid byte count '%s' specified"),
 709                                     optarg);
 710                                 exit(1);
 711                         }
 712                         break;
 713 
 714                 default:
 715                         usage();
 716                         break;
 717                 }
 718         }
 719 
 720         /* this finds the smallest power of two size we can use */
 721         buffer.mask = (1 << (ffs(blocksize * 3) + 1)) - 1;
 722         buffer.data = memalign(16, buffer.mask + 1);
 723         if (buffer.data == NULL) {
 724                 err(1, "memalign");
 725         }
 726 
 727 
 728         /*
 729          * Wow.  This option parsing is hideous.
 730          *
 731          * If the we've not seen a new option, and there is just one
 732          * operand, if it starts with a "+", then treat it as an
 733          * offset.  Otherwise if two operands, and the second operand
 734          * starts with + or a digit, then it is an offset.
 735          */
 736         if (!newarg) {
 737                 if (((argc - optind) == 1) && (argv[optind][0] == '+')) {
 738                         offstr = argv[optind];
 739                         argc--;
 740                 } else if (((argc - optind) == 2) &&
 741                     (strchr("+0123456789", (argv[optind + 1][0])) != NULL)) {
 742                         offstr = argv[optind + 1];
 743                         argc--;
 744                 }
 745         }
 746         if (offstr) {
 747                 int base = 0;
 748                 int mult = 1;
 749                 int l;
 750                 if (*offstr == '+') {
 751                         offstr++;
 752                 }
 753                 l = strlen(offstr);
 754                 if ((strncmp(offstr, "0x", 2) == 0)) {
 755                         afmt = "%07llx";
 756                         base = 16;
 757                         offstr += 2;
 758                         if (offstr[l - 1] == 'B') {
 759                                 offstr[l - 1] = 0;
 760                                 l--;
 761                                 mult = 512;
 762                         }
 763                 } else {
 764                         base = 8;
 765                         afmt = "%07llo";
 766                         if ((offstr[l - 1] == 'B') || (offstr[l - 1] == 'b')) {
 767                                 offstr[l - 1] = 0;
 768                                 l--;
 769                                 mult = 512;
 770                         }
 771                         if (offstr[l - 1] == '.') {
 772                                 offstr[l - 1] = 0;
 773                                 base = 10;
 774                                 afmt = "%07lld";
 775                         }
 776                 }
 777                 skip = strtoll(offstr, &eptr, base);
 778                 if (*eptr != '\0') {
 779                         errx(1, _("invalid offset string specified"));
 780                 }
 781                 skip *= mult;
 782                 offset += skip;
 783         }
 784 
 785         /*
 786          * Allocate an array for all the input files.
 787          */
 788         if (argc > optind) {
 789                 files = calloc(sizeof (char *), argc - optind);
 790                 for (i = 0; i < argc - optind; i++) {
 791                         files[i] = argv[optind + i];
 792                         numfiles++;
 793                 }
 794                 input = next_input();
 795         } else {
 796                 input = stdin;
 797         }
 798 
 799         /*
 800          * We need to seek ahead.  fseek would be faster.
 801          */
 802         while (skip && (input != NULL)) {
 803                 struct stat sbuf;
 804 
 805                 /*
 806                  * Only fseek() on regular files.  (Others
 807                  * we have to read().
 808                  */
 809                 if (fstat(fileno(input), &sbuf) < 0) {
 810                         warn("fstat: %s", files[curfile-1]);
 811                         input = next_input();
 812                         continue;
 813                 }
 814                 if (S_ISREG(sbuf.st_mode)) {
 815                         /*
 816                          * No point in seeking a file that is too
 817                          * short to begin with.
 818                          */
 819                         if (sbuf.st_size < skip) {
 820                                 skip -= sbuf.st_size;
 821                                 input = next_input();
 822                                 continue;
 823                         }
 824                         if (fseeko(input, skip, SEEK_SET) < 0) {
 825                                 err(1, "fseek:%s", files[curfile-1]);
 826                         }
 827                         /* Done seeking. */
 828                         skip = 0;
 829                         break;
 830                 }
 831 
 832                 /*
 833                  * fgetc seems like it would be slow, but it uses
 834                  * buffered I/O, so it should be fast enough.
 835                  */
 836                 flockfile(input);
 837                 while (skip) {
 838                         if (getc_unlocked(input) == EOF) {
 839                                 funlockfile(input);
 840                                 if (ferror(input)) {
 841                                         warn("read: %s", files[curfile-1]);
 842                                 }
 843                                 input = next_input();
 844                                 if (input != NULL) {
 845                                         flockfile(input);
 846                                 }
 847                                 break;
 848                         }
 849                         skip--;
 850                 }
 851                 if (input != NULL)
 852                         funlockfile(input);
 853         }
 854 
 855         if (head == NULL) {
 856                 add_out(&output_oct_w);
 857         }
 858 
 859         buffer.navail = 0;
 860         buffer.prod = 0;
 861         buffer.cons = 0;
 862 
 863         for (refill(&buffer); buffer.navail > 0; refill(&buffer)) {
 864                 output_t *out;
 865                 int     mx;
 866                 int     j, k;
 867 
 868                 /*
 869                  * If this buffer was the same as last, then just
 870                  * dump an asterisk.
 871                  */
 872                 if ((!first) && (buffer.navail >= blocksize) && (!doall)) {
 873                         j = buffer.cons;
 874                         k = j - blocksize;
 875                         for (i = 0; i < blocksize; i++) {
 876                                 if (buffer.data[j & buffer.mask] !=
 877                                     buffer.data[k & buffer.mask]) {
 878                                         break;
 879                                 }
 880                                 j++;
 881                                 k++;
 882                         }
 883                         if (i == blocksize) {
 884                                 if (!same) {
 885                                         (void) fputs("*\n", stdout);
 886                                         same = B_TRUE;
 887                                 }
 888                                 buffer.navail -= blocksize;
 889                                 offset += blocksize;
 890                                 buffer.cons += blocksize;
 891                                 buffer.cons &= buffer.mask;
 892                                 continue;
 893                         }
 894                 }
 895 
 896                 first = B_FALSE;
 897                 same = B_FALSE;
 898                 mx = (buffer.navail > blocksize) ? blocksize : buffer.navail;
 899 
 900                 for (out = head; out != NULL; out = out->next) {
 901 
 902                         if (out == head) {
 903                                 /*LINTED E_SEC_PRINTF_VAR_FMT*/
 904                                 (void) printf(afmt, offset);
 905                         } else {
 906                                 (void) fputs(cfmt, stdout);
 907                         }
 908                         for (i = 0, j = buffer.cons; i < mx; i += out->width) {
 909                                 out->func(&buffer, j);
 910                                 j += out->width;
 911                                 j &= buffer.mask;
 912                         }
 913                         (void) fputs("\n", stdout);
 914                 }
 915                 buffer.cons += mx;
 916                 buffer.cons &= buffer.mask;
 917                 offset += mx;
 918                 buffer.navail -= mx;
 919         }
 920         /*LINTED E_SEC_PRINTF_VAR_FMT*/
 921         (void) printf(afmt, offset);
 922         (void) fputs("\n", stdout);
 923         return (0);
 924 }