1
2
3
4
5
6
7
8
9
10
11
12
13
14#include "libbb.h"
15#include "dump.h"
16
17static const char index_str[] ALIGN1 = ".#-+ 0123456789";
18
19static const char size_conv_str[] ALIGN1 =
20"\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG";
21
22static const char lcc[] ALIGN1 = "diouxX";
23
24
25typedef struct priv_dumper_t {
26 dumper_t pub;
27
28 char **argv;
29 FU *endfu;
30 off_t savaddress;
31 off_t eaddress;
32 off_t address;
33 int blocksize;
34 smallint exitval;
35
36
37 smallint next__done;
38 smallint get__ateof;
39 unsigned char *get__curp;
40 unsigned char *get__savp;
41} priv_dumper_t;
42
43dumper_t* FAST_FUNC alloc_dumper(void)
44{
45 priv_dumper_t *dumper = xzalloc(sizeof(*dumper));
46 dumper->pub.dump_length = -1;
47 dumper->pub.dump_vflag = FIRST;
48 dumper->get__ateof = 1;
49 return &dumper->pub;
50}
51
52
53static NOINLINE int bb_dump_size(FS *fs)
54{
55 FU *fu;
56 int bcnt, cur_size;
57 char *fmt;
58 const char *p;
59 int prec;
60
61
62 for (cur_size = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
63 if (fu->bcnt) {
64 cur_size += fu->bcnt * fu->reps;
65 continue;
66 }
67 for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
68 if (*fmt != '%')
69 continue;
70
71
72
73
74 while (strchr(index_str + 1, *++fmt));
75 if (*fmt == '.' && isdigit(*++fmt)) {
76 prec = atoi(fmt);
77 while (isdigit(*++fmt))
78 continue;
79 }
80 p = strchr(size_conv_str + 12, *fmt);
81 if (!p) {
82 if (*fmt == 's') {
83 bcnt += prec;
84 } else if (*fmt == '_') {
85 ++fmt;
86 if ((*fmt == 'c') || (*fmt == 'p') || (*fmt == 'u')) {
87 bcnt += 1;
88 }
89 }
90 } else {
91 bcnt += size_conv_str[p - (size_conv_str + 12)];
92 }
93 }
94 cur_size += bcnt * fu->reps;
95 }
96 return cur_size;
97}
98
99static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
100{
101 enum { NOTOKAY, USEBCNT, USEPREC } sokay;
102 PR *pr;
103 FU *fu;
104 char *p1, *p2, *p3;
105 char savech, *fmtp;
106 const char *byte_count_str;
107 int nconv, prec = 0;
108
109 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
110
111
112
113
114 for (nconv = 0, fmtp = fu->fmt; *fmtp; ) {
115
116
117 pr = xzalloc(sizeof(PR));
118 if (!fu->nextpr)
119 fu->nextpr = pr;
120
121
122 for (p1 = fmtp; *p1 && *p1 != '%'; ++p1)
123 continue;
124
125
126 if (!*p1) {
127 pr->fmt = fmtp;
128 pr->flags = F_TEXT;
129 break;
130 }
131
132
133
134
135
136 if (fu->bcnt) {
137 sokay = USEBCNT;
138
139 for (++p1; strchr(index_str, *p1); ++p1)
140 continue;
141 } else {
142
143 while (strchr(index_str + 1, *++p1))
144 continue;
145 if (*p1 == '.' && isdigit(*++p1)) {
146 sokay = USEPREC;
147 prec = atoi(p1);
148 while (isdigit(*++p1))
149 continue;
150 } else
151 sokay = NOTOKAY;
152 }
153
154 p2 = p1 + 1;
155
156
157
158
159
160
161 if (*p1 == 'c') {
162 pr->flags = F_CHAR;
163 DO_BYTE_COUNT_1:
164 byte_count_str = "\001";
165 DO_BYTE_COUNT:
166 if (fu->bcnt) {
167 do {
168 if (fu->bcnt == *byte_count_str) {
169 break;
170 }
171 } while (*++byte_count_str);
172 }
173
174 if (!*byte_count_str) {
175 bb_error_msg_and_die("bad byte count for conversion character %s", p1);
176 }
177 pr->bcnt = *byte_count_str;
178 } else if (*p1 == 'l') {
179 ++p2;
180 ++p1;
181 DO_INT_CONV:
182 {
183 const char *e;
184 e = strchr(lcc, *p1);
185 if (!e) {
186 goto DO_BAD_CONV_CHAR;
187 }
188 pr->flags = F_INT;
189 if (e > lcc + 1) {
190 pr->flags = F_UINT;
191 }
192 byte_count_str = "\004\002\001";
193 goto DO_BYTE_COUNT;
194 }
195
196 } else if (strchr(lcc, *p1)) {
197 goto DO_INT_CONV;
198 } else if (strchr("eEfgG", *p1)) {
199 pr->flags = F_DBL;
200 byte_count_str = "\010\004";
201 goto DO_BYTE_COUNT;
202 } else if (*p1 == 's') {
203 pr->flags = F_STR;
204 if (sokay == USEBCNT) {
205 pr->bcnt = fu->bcnt;
206 } else if (sokay == USEPREC) {
207 pr->bcnt = prec;
208 } else {
209 bb_error_msg_and_die("%%s requires a precision or a byte count");
210 }
211 } else if (*p1 == '_') {
212 ++p2;
213 switch (p1[1]) {
214 case 'A':
215 dumper->endfu = fu;
216 fu->flags |= F_IGNORE;
217
218 case 'a':
219 pr->flags = F_ADDRESS;
220 ++p2;
221 if ((p1[2] != 'd') && (p1[2] != 'o') && (p1[2] != 'x')) {
222 goto DO_BAD_CONV_CHAR;
223 }
224 *p1 = p1[2];
225 break;
226 case 'c':
227 pr->flags = F_C;
228
229 goto DO_BYTE_COUNT_1;
230 case 'p':
231 pr->flags = F_P;
232 *p1 = 'c';
233 goto DO_BYTE_COUNT_1;
234 case 'u':
235 pr->flags = F_U;
236
237 goto DO_BYTE_COUNT_1;
238 default:
239 goto DO_BAD_CONV_CHAR;
240 }
241 } else {
242 DO_BAD_CONV_CHAR:
243 bb_error_msg_and_die("bad conversion character %%%s", p1);
244 }
245
246
247
248
249
250 savech = *p2;
251 p1[1] = '\0';
252 pr->fmt = xstrdup(fmtp);
253 *p2 = savech;
254
255
256
257
258
259
260
261
262 for (p3 = p2; *p3 && *p3 != '%'; p3++)
263 continue;
264 if (p3 > p2) {
265 savech = *p3;
266 *p3 = '\0';
267 pr->fmt = xrealloc(pr->fmt, strlen(pr->fmt) + (p3-p2) + 1);
268 strcat(pr->fmt, p2);
269 *p3 = savech;
270 p2 = p3;
271 }
272
273 pr->cchar = pr->fmt + (p1 - fmtp);
274 fmtp = p2;
275
276
277 if (!(pr->flags & F_ADDRESS) && fu->bcnt && nconv++) {
278 bb_error_msg_and_die("byte count with multiple conversion characters");
279 }
280 }
281
282
283
284
285 if (!fu->bcnt)
286 for (pr = fu->nextpr; pr; pr = pr->nextpr)
287 fu->bcnt += pr->bcnt;
288 }
289
290
291
292
293
294
295
296
297
298 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
299 if (!fu->nextfu && fs->bcnt < dumper->blocksize
300 && !(fu->flags & F_SETREP) && fu->bcnt
301 ) {
302 fu->reps += (dumper->blocksize - fs->bcnt) / fu->bcnt;
303 }
304 if (fu->reps > 1) {
305 for (pr = fu->nextpr;; pr = pr->nextpr)
306 if (!pr->nextpr)
307 break;
308 for (p1 = pr->fmt, p2 = NULL; *p1; ++p1)
309 p2 = isspace(*p1) ? p1 : NULL;
310 if (p2)
311 pr->nospace = p2;
312 }
313 if (!fu->nextfu)
314 break;
315 }
316}
317
318static void do_skip(priv_dumper_t *dumper, const char *fname, int statok)
319{
320 struct stat sbuf;
321
322 if (statok) {
323 xfstat(STDIN_FILENO, &sbuf, fname);
324 if (!(S_ISCHR(sbuf.st_mode) || S_ISBLK(sbuf.st_mode) || S_ISFIFO(sbuf.st_mode))
325 && dumper->pub.dump_skip >= sbuf.st_size
326 ) {
327
328 dumper->pub.dump_skip -= sbuf.st_size;
329 dumper->address += sbuf.st_size;
330 return;
331 }
332 }
333 if (fseek(stdin, dumper->pub.dump_skip, SEEK_SET)) {
334 bb_simple_perror_msg_and_die(fname);
335 }
336 dumper->address += dumper->pub.dump_skip;
337 dumper->savaddress = dumper->address;
338 dumper->pub.dump_skip = 0;
339}
340
341static NOINLINE int next(priv_dumper_t *dumper)
342{
343 int statok;
344
345 for (;;) {
346 if (*dumper->argv) {
347 dumper->next__done = statok = 1;
348 if (!(freopen(*dumper->argv, "r", stdin))) {
349 bb_simple_perror_msg(*dumper->argv);
350 dumper->exitval = 1;
351 ++dumper->argv;
352 continue;
353 }
354 } else {
355 if (dumper->next__done)
356 return 0;
357 dumper->next__done = 1;
358 statok = 0;
359 }
360 if (dumper->pub.dump_skip)
361 do_skip(dumper, statok ? *dumper->argv : "stdin", statok);
362 if (*dumper->argv)
363 ++dumper->argv;
364 if (!dumper->pub.dump_skip)
365 return 1;
366 }
367
368}
369
370static unsigned char *get(priv_dumper_t *dumper)
371{
372 int n;
373 int need, nread;
374 int blocksize = dumper->blocksize;
375
376 if (!dumper->get__curp) {
377 dumper->address = (off_t)0;
378 dumper->get__curp = xmalloc(blocksize);
379 dumper->get__savp = xzalloc(blocksize);
380 } else {
381 unsigned char *tmp = dumper->get__curp;
382 dumper->get__curp = dumper->get__savp;
383 dumper->get__savp = tmp;
384 dumper->savaddress += blocksize;
385 dumper->address = dumper->savaddress;
386 }
387 need = blocksize;
388 nread = 0;
389 while (1) {
390
391
392
393
394
395 if (!dumper->pub.dump_length || (dumper->get__ateof && !next(dumper))) {
396 if (need == blocksize) {
397 return NULL;
398 }
399 if (dumper->pub.dump_vflag != ALL && !memcmp(dumper->get__curp, dumper->get__savp, nread)) {
400 if (dumper->pub.dump_vflag != DUP) {
401 puts("*");
402 }
403 return NULL;
404 }
405 memset(dumper->get__curp + nread, 0, need);
406 dumper->eaddress = dumper->address + nread;
407 return dumper->get__curp;
408 }
409 n = fread(dumper->get__curp + nread, sizeof(unsigned char),
410 dumper->pub.dump_length == -1 ? need : MIN(dumper->pub.dump_length, need), stdin);
411 if (!n) {
412 if (ferror(stdin)) {
413 bb_simple_perror_msg(dumper->argv[-1]);
414 }
415 dumper->get__ateof = 1;
416 continue;
417 }
418 dumper->get__ateof = 0;
419 if (dumper->pub.dump_length != -1) {
420 dumper->pub.dump_length -= n;
421 }
422 need -= n;
423 if (!need) {
424 if (dumper->pub.dump_vflag == ALL || dumper->pub.dump_vflag == FIRST
425 || memcmp(dumper->get__curp, dumper->get__savp, blocksize)
426 ) {
427 if (dumper->pub.dump_vflag == DUP || dumper->pub.dump_vflag == FIRST) {
428 dumper->pub.dump_vflag = WAIT;
429 }
430 return dumper->get__curp;
431 }
432 if (dumper->pub.dump_vflag == WAIT) {
433 puts("*");
434 }
435 dumper->pub.dump_vflag = DUP;
436 dumper->savaddress += blocksize;
437 dumper->address = dumper->savaddress;
438 need = blocksize;
439 nread = 0;
440 } else {
441 nread += n;
442 }
443 }
444}
445
446static void bpad(PR *pr)
447{
448 char *p1, *p2;
449
450
451
452
453
454 pr->flags = F_BPAD;
455 *pr->cchar = 's';
456 for (p1 = pr->fmt; *p1 != '%'; ++p1)
457 continue;
458 for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1)
459 if (pr->nospace)
460 pr->nospace--;
461 while ((*p2++ = *p1++) != 0)
462 continue;
463}
464
465static const char conv_str[] ALIGN1 =
466 "\0\\0\0"
467 "\007\\a\0"
468 "\b\\b\0"
469 "\f\\b\0"
470 "\n\\n\0"
471 "\r\\r\0"
472 "\t\\t\0"
473 "\v\\v\0"
474 ;
475
476
477static void conv_c(PR *pr, unsigned char *p)
478{
479 const char *str = conv_str;
480 char buf[10];
481
482 do {
483 if (*p == *str) {
484 ++str;
485 goto strpr;
486 }
487 str += 4;
488 } while (*str);
489
490 if (isprint_asciionly(*p)) {
491 *pr->cchar = 'c';
492 printf(pr->fmt, *p);
493 } else {
494 sprintf(buf, "%03o", (int) *p);
495 str = buf;
496 strpr:
497 *pr->cchar = 's';
498 printf(pr->fmt, str);
499 }
500}
501
502static void conv_u(PR *pr, unsigned char *p)
503{
504 static const char list[] ALIGN1 =
505 "nul\0soh\0stx\0etx\0eot\0enq\0ack\0bel\0"
506 "bs\0_ht\0_lf\0_vt\0_ff\0_cr\0_so\0_si\0_"
507 "dle\0dcl\0dc2\0dc3\0dc4\0nak\0syn\0etb\0"
508 "can\0em\0_sub\0esc\0fs\0_gs\0_rs\0_us";
509
510
511 if (*p <= 0x1f) {
512 *pr->cchar = 's';
513 printf(pr->fmt, list + (4 * (int)*p));
514 } else if (*p == 0x7f) {
515 *pr->cchar = 's';
516 printf(pr->fmt, "del");
517 } else if (*p < 0x7f) {
518 *pr->cchar = 'c';
519 printf(pr->fmt, *p);
520 } else {
521 *pr->cchar = 'x';
522 printf(pr->fmt, (int) *p);
523 }
524}
525
526static void display(priv_dumper_t* dumper)
527{
528 FS *fs;
529 FU *fu;
530 PR *pr;
531 int cnt;
532 unsigned char *bp, *savebp;
533 off_t saveaddress;
534 unsigned char savech = '\0';
535
536 while ((bp = get(dumper)) != NULL) {
537 fs = dumper->pub.fshead;
538 savebp = bp;
539 saveaddress = dumper->address;
540 for (; fs; fs = fs->nextfs, bp = savebp, dumper->address = saveaddress) {
541 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
542 if (fu->flags & F_IGNORE) {
543 break;
544 }
545 for (cnt = fu->reps; cnt; --cnt) {
546 for (pr = fu->nextpr; pr; dumper->address += pr->bcnt,
547 bp += pr->bcnt, pr = pr->nextpr) {
548 if (dumper->eaddress && dumper->address >= dumper->eaddress
549 && !(pr->flags & (F_TEXT | F_BPAD))
550 ) {
551 bpad(pr);
552 }
553 if (cnt == 1 && pr->nospace) {
554 savech = *pr->nospace;
555 *pr->nospace = '\0';
556 }
557
558 switch (pr->flags) {
559 case F_ADDRESS:
560 printf(pr->fmt, (unsigned) dumper->address);
561 break;
562 case F_BPAD:
563 printf(pr->fmt, "");
564 break;
565 case F_C:
566 conv_c(pr, bp);
567 break;
568 case F_CHAR:
569 printf(pr->fmt, *bp);
570 break;
571 case F_DBL: {
572 double dval;
573 float fval;
574
575 switch (pr->bcnt) {
576 case 4:
577 memcpy(&fval, bp, sizeof(fval));
578 printf(pr->fmt, fval);
579 break;
580 case 8:
581 memcpy(&dval, bp, sizeof(dval));
582 printf(pr->fmt, dval);
583 break;
584 }
585 break;
586 }
587 case F_INT: {
588 int ival;
589 short sval;
590
591 switch (pr->bcnt) {
592 case 1:
593 printf(pr->fmt, (int) *bp);
594 break;
595 case 2:
596 memcpy(&sval, bp, sizeof(sval));
597 printf(pr->fmt, (int) sval);
598 break;
599 case 4:
600 memcpy(&ival, bp, sizeof(ival));
601 printf(pr->fmt, ival);
602 break;
603 }
604 break;
605 }
606 case F_P:
607 printf(pr->fmt, isprint_asciionly(*bp) ? *bp : '.');
608 break;
609 case F_STR:
610 printf(pr->fmt, (char *) bp);
611 break;
612 case F_TEXT:
613 printf(pr->fmt);
614 break;
615 case F_U:
616 conv_u(pr, bp);
617 break;
618 case F_UINT: {
619 unsigned ival;
620 unsigned short sval;
621
622 switch (pr->bcnt) {
623 case 1:
624 printf(pr->fmt, (unsigned) *bp);
625 break;
626 case 2:
627 memcpy(&sval, bp, sizeof(sval));
628 printf(pr->fmt, (unsigned) sval);
629 break;
630 case 4:
631 memcpy(&ival, bp, sizeof(ival));
632 printf(pr->fmt, ival);
633 break;
634 }
635 break;
636 }
637 }
638 if (cnt == 1 && pr->nospace) {
639 *pr->nospace = savech;
640 }
641 }
642 }
643 }
644 }
645 }
646 if (dumper->endfu) {
647
648
649
650
651 if (!dumper->eaddress) {
652 if (!dumper->address) {
653 return;
654 }
655 dumper->eaddress = dumper->address;
656 }
657 for (pr = dumper->endfu->nextpr; pr; pr = pr->nextpr) {
658 switch (pr->flags) {
659 case F_ADDRESS:
660 printf(pr->fmt, (unsigned) dumper->eaddress);
661 break;
662 case F_TEXT:
663 printf(pr->fmt);
664 break;
665 }
666 }
667 }
668}
669
670#define dumper ((priv_dumper_t*)pub_dumper)
671int FAST_FUNC bb_dump_dump(dumper_t *pub_dumper, char **argv)
672{
673 FS *tfs;
674 int blocksize;
675
676
677 blocksize = 0;
678 tfs = dumper->pub.fshead;
679 while (tfs) {
680 tfs->bcnt = bb_dump_size(tfs);
681 if (blocksize < tfs->bcnt) {
682 blocksize = tfs->bcnt;
683 }
684 tfs = tfs->nextfs;
685 }
686 dumper->blocksize = blocksize;
687
688
689 for (tfs = dumper->pub.fshead; tfs; tfs = tfs->nextfs) {
690 rewrite(dumper, tfs);
691 }
692
693 dumper->argv = argv;
694 display(dumper);
695
696 return dumper->exitval;
697}
698
699void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
700{
701 const char *p;
702 char *p1;
703 char *p2;
704 FS *tfs;
705 FU *tfu, **nextfupp;
706 const char *savep;
707
708
709 tfs = xzalloc(sizeof(FS));
710 if (!dumper->pub.fshead) {
711 dumper->pub.fshead = tfs;
712 } else {
713 FS *fslast = dumper->pub.fshead;
714 while (fslast->nextfs)
715 fslast = fslast->nextfs;
716 fslast->nextfs = tfs;
717 }
718 nextfupp = &tfs->nextfu;
719
720
721 p = fmt;
722 for (;;) {
723 p = skip_whitespace(p);
724 if (!*p) {
725 break;
726 }
727
728
729
730
731 tfu = xzalloc(sizeof(FU));
732 *nextfupp = tfu;
733 nextfupp = &tfu->nextfu;
734 tfu->reps = 1;
735
736
737 if (isdigit(*p)) {
738 for (savep = p; isdigit(*p); ++p)
739 continue;
740 if (!isspace(*p) && *p != '/') {
741 bb_error_msg_and_die("bad format {%s}", fmt);
742 }
743
744 tfu->reps = atoi(savep);
745 tfu->flags = F_SETREP;
746
747 p = skip_whitespace(++p);
748 }
749
750
751 if (*p == '/') {
752 p = skip_whitespace(++p);
753 }
754
755
756 if (isdigit(*p)) {
757
758 savep = p;
759 while (isdigit(*++p))
760 continue;
761 if (!isspace(*p)) {
762 bb_error_msg_and_die("bad format {%s}", fmt);
763 }
764 tfu->bcnt = atoi(savep);
765
766 p = skip_whitespace(++p);
767 }
768
769
770 if (*p != '"') {
771 bb_error_msg_and_die("bad format {%s}", fmt);
772 }
773 for (savep = ++p; *p != '"';) {
774 if (*p++ == 0) {
775 bb_error_msg_and_die("bad format {%s}", fmt);
776 }
777 }
778 tfu->fmt = xstrndup(savep, p - savep);
779
780
781 p1 = tfu->fmt;
782
783
784 for (p2 = p1;; ++p1, ++p2) {
785 if (!*p1) {
786 *p2 = *p1;
787 break;
788 }
789 if (*p1 == '\\') {
790 const char *cs = conv_str + 4;
791 ++p1;
792 *p2 = *p1;
793 do {
794 if (*p1 == cs[2]) {
795 *p2 = cs[0];
796 break;
797 }
798 cs += 4;
799 } while (*cs);
800 }
801 }
802
803 p++;
804 }
805}
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835