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 }