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