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
usage(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
do_ascii(buffer_t * buf,int index)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
do_char(buffer_t * buf,int index)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
add_out(output_t * src)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 *
next_input(void)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
refill(buffer_t * b)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
do_type_string(char * typestr)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
main(int argc,char ** argv)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 }
925