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