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