More removal of "#if 0" content.
[oweals/busybox.git] / libbb / dump.c
1 /*
2  * Support code for the hexdump and od applets,
3  * based on code from util-linux v 2.11l
4  *
5  * Copyright (c) 1989
6  *      The Regents of the University of California.  All rights reserved.
7  *
8  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
9  *
10  * Original copyright notice is retained at the end of this file.
11  */
12
13 #include "libbb.h"
14 #include <string.h>
15 #include <unistd.h>
16 #include <ctype.h>              /* for isdigit() */
17 #include "dump.h"
18
19 enum _vflag bb_dump_vflag = FIRST;
20 FS *bb_dump_fshead;                             /* head of format strings */
21 static FU *endfu;
22 static char **_argv;
23 static off_t savaddress;        /* saved address/offset in stream */
24 static off_t eaddress;  /* end address */
25 static off_t address;   /* address/offset in stream */
26 off_t bb_dump_skip;                             /* bytes to skip */
27 static int exitval;                     /* final exit value */
28 int bb_dump_blocksize;                  /* data block size */
29 int bb_dump_length = -1;                /* max bytes to read */
30
31 static const char index_str[] = ".#-+ 0123456789";
32
33 static const char size_conv_str[] =
34 "\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG";
35
36 static const char lcc[] = "diouxX";
37
38 int bb_dump_size(FS * fs)
39 {
40         FU *fu;
41         int bcnt, cur_size;
42         char *fmt;
43         const char *p;
44         int prec;
45
46         /* figure out the data block bb_dump_size needed for each format unit */
47         for (cur_size = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
48                 if (fu->bcnt) {
49                         cur_size += fu->bcnt * fu->reps;
50                         continue;
51                 }
52                 for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
53                         if (*fmt != '%')
54                                 continue;
55                         /*
56                          * bb_dump_skip any special chars -- save precision in
57                          * case it's a %s format.
58                          */
59                         while (strchr(index_str + 1, *++fmt));
60                         if (*fmt == '.' && isdigit(*++fmt)) {
61                                 prec = atoi(fmt);
62                                 while (isdigit(*++fmt));
63                         }
64                         if (!(p = strchr(size_conv_str + 12, *fmt))) {
65                                 if (*fmt == 's') {
66                                         bcnt += prec;
67                                 } else if (*fmt == '_') {
68                                         ++fmt;
69                                         if ((*fmt == 'c') || (*fmt == 'p') || (*fmt == 'u')) {
70                                                 bcnt += 1;
71                                         }
72                                 }
73                         } else {
74                                 bcnt += size_conv_str[p - (size_conv_str + 12)];
75                         }
76                 }
77                 cur_size += bcnt * fu->reps;
78         }
79         return (cur_size);
80 }
81
82 static void rewrite(FS * fs)
83 {
84         enum { NOTOKAY, USEBCNT, USEPREC } sokay;
85         PR *pr, **nextpr = NULL;
86         FU *fu;
87         char *p1, *p2, *p3;
88         char savech, *fmtp;
89         const char *byte_count_str;
90         int nconv, prec = 0;
91
92         for (fu = fs->nextfu; fu; fu = fu->nextfu) {
93                 /*
94                  * break each format unit into print units; each
95                  * conversion character gets its own.
96                  */
97                 for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) {
98                         /* NOSTRICT */
99                         /* DBU:[dvae@cray.com] calloc so that forward ptrs start out NULL*/
100                         pr = xzalloc(sizeof(PR));
101                         if (!fu->nextpr)
102                                 fu->nextpr = pr;
103                         /* ignore nextpr -- its unused inside the loop and is
104                          * uninitialized 1st time thru.
105                          */
106
107                         /* bb_dump_skip preceding text and up to the next % sign */
108                         for (p1 = fmtp; *p1 && *p1 != '%'; ++p1);
109
110                         /* only text in the string */
111                         if (!*p1) {
112                                 pr->fmt = fmtp;
113                                 pr->flags = F_TEXT;
114                                 break;
115                         }
116
117                         /*
118                          * get precision for %s -- if have a byte count, don't
119                          * need it.
120                          */
121                         if (fu->bcnt) {
122                                 sokay = USEBCNT;
123                                 /* bb_dump_skip to conversion character */
124                                 for (++p1; strchr(index_str, *p1); ++p1);
125                         } else {
126                                 /* bb_dump_skip any special chars, field width */
127                                 while (strchr(index_str + 1, *++p1));
128                                 if (*p1 == '.' && isdigit(*++p1)) {
129                                         sokay = USEPREC;
130                                         prec = atoi(p1);
131                                         while (isdigit(*++p1));
132                                 } else
133                                         sokay = NOTOKAY;
134                         }
135
136                         p2 = p1 + 1;    /* set end pointer */
137
138                         /*
139                          * figure out the byte count for each conversion;
140                          * rewrite the format as necessary, set up blank-
141                          * pbb_dump_adding for end of data.
142                          */
143
144                         if (*p1 == 'c') {
145                                 pr->flags = F_CHAR;
146                         DO_BYTE_COUNT_1:
147                                 byte_count_str = "\001";
148                         DO_BYTE_COUNT:
149                                 if (fu->bcnt) {
150                                         do {
151                                                 if (fu->bcnt == *byte_count_str) {
152                                                         break;
153                                                 }
154                                         } while (*++byte_count_str);
155                                 }
156                                 /* Unlike the original, output the remainder of the format string. */
157                                 if (!*byte_count_str) {
158                                         bb_error_msg_and_die("bad byte count for conversion character %s.", p1);
159                                 }
160                                 pr->bcnt = *byte_count_str;
161                         } else if (*p1 == 'l') {
162                                 ++p2;
163                                 ++p1;
164                         DO_INT_CONV:
165                                 {
166                                         const char *e;
167                                         if (!(e = strchr(lcc, *p1))) {
168                                                 goto DO_BAD_CONV_CHAR;
169                                         }
170                                         pr->flags = F_INT;
171                                         if (e > lcc + 1) {
172                                                 pr->flags = F_UINT;
173                                         }
174                                         byte_count_str = "\004\002\001";
175                                         goto DO_BYTE_COUNT;
176                                 }
177                                 /* NOTREACHED */
178                         } else if (strchr(lcc, *p1)) {
179                                 goto DO_INT_CONV;
180                         } else if (strchr("eEfgG", *p1)) {
181                                 pr->flags = F_DBL;
182                                 byte_count_str = "\010\004";
183                                 goto DO_BYTE_COUNT;
184                         } else if (*p1 == 's') {
185                                 pr->flags = F_STR;
186                                 if (sokay == USEBCNT) {
187                                         pr->bcnt = fu->bcnt;
188                                 } else if (sokay == USEPREC) {
189                                         pr->bcnt = prec;
190                                 } else {        /* NOTOKAY */
191                                         bb_error_msg_and_die("%%s requires a precision or a byte count.");
192                                 }
193                         } else if (*p1 == '_') {
194                                 ++p2;
195                                 switch (p1[1]) {
196                                 case 'A':
197                                         endfu = fu;
198                                         fu->flags |= F_IGNORE;
199                                         /* FALLTHROUGH */
200                                 case 'a':
201                                         pr->flags = F_ADDRESS;
202                                         ++p2;
203                                         if ((p1[2] != 'd') && (p1[2] != 'o') && (p1[2] != 'x')) {
204                                                 goto DO_BAD_CONV_CHAR;
205                                         }
206                                         *p1 = p1[2];
207                                         break;
208                                 case 'c':
209                                         pr->flags = F_C;
210                                         /* *p1 = 'c';   set in conv_c */
211                                         goto DO_BYTE_COUNT_1;
212                                 case 'p':
213                                         pr->flags = F_P;
214                                         *p1 = 'c';
215                                         goto DO_BYTE_COUNT_1;
216                                 case 'u':
217                                         pr->flags = F_U;
218                                         /* *p1 = 'c';   set in conv_u */
219                                         goto DO_BYTE_COUNT_1;
220                                 default:
221                                         goto DO_BAD_CONV_CHAR;
222                                 }
223                         } else {
224                         DO_BAD_CONV_CHAR:
225                                 bb_error_msg_and_die("bad conversion character %%%s.\n", p1);
226                         }
227
228                         /*
229                          * copy to PR format string, set conversion character
230                          * pointer, update original.
231                          */
232                         savech = *p2;
233                         p1[1] = '\0';
234                         pr->fmt = bb_xstrdup(fmtp);
235                         *p2 = savech;
236                         pr->cchar = pr->fmt + (p1 - fmtp);
237
238                         /* DBU:[dave@cray.com] w/o this, trailing fmt text, space is lost.
239                          * Skip subsequent text and up to the next % sign and tack the
240                          * additional text onto fmt: eg. if fmt is "%x is a HEX number",
241                          * we lose the " is a HEX number" part of fmt.
242                          */
243                         for (p3 = p2; *p3 && *p3 != '%'; p3++);
244                         if (p3 > p2)
245                         {
246                                 savech = *p3;
247                                 *p3 = '\0';
248                                 pr->fmt = xrealloc(pr->fmt, strlen(pr->fmt)+(p3-p2)+1);
249                                 strcat(pr->fmt, p2);
250                                 *p3 = savech;
251                                 p2 = p3;
252                         }
253
254                         fmtp = p2;
255
256                         /* only one conversion character if byte count */
257                         if (!(pr->flags & F_ADDRESS) && fu->bcnt && nconv++) {
258                                 bb_error_msg_and_die("byte count with multiple conversion characters.\n");
259                         }
260                 }
261                 /*
262                  * if format unit byte count not specified, figure it out
263                  * so can adjust rep count later.
264                  */
265                 if (!fu->bcnt)
266                         for (pr = fu->nextpr; pr; pr = pr->nextpr)
267                                 fu->bcnt += pr->bcnt;
268         }
269         /*
270          * if the format string interprets any data at all, and it's
271          * not the same as the bb_dump_blocksize, and its last format unit
272          * interprets any data at all, and has no iteration count,
273          * repeat it as necessary.
274          *
275          * if, rep count is greater than 1, no trailing whitespace
276          * gets output from the last iteration of the format unit.
277          */
278         for (fu = fs->nextfu;; fu = fu->nextfu) {
279                 if (!fu->nextfu && fs->bcnt < bb_dump_blocksize &&
280                         !(fu->flags & F_SETREP) && fu->bcnt)
281                         fu->reps += (bb_dump_blocksize - fs->bcnt) / fu->bcnt;
282                 if (fu->reps > 1) {
283                         for (pr = fu->nextpr;; pr = pr->nextpr)
284                                 if (!pr->nextpr)
285                                         break;
286                         for (p1 = pr->fmt, p2 = NULL; *p1; ++p1)
287                                 p2 = isspace(*p1) ? p1 : NULL;
288                         if (p2)
289                                 pr->nospace = p2;
290                 }
291                 if (!fu->nextfu)
292                         break;
293         }
294 }
295
296 static void do_skip(char *fname, int statok)
297 {
298         struct stat sbuf;
299
300         if (statok) {
301                 if (fstat(STDIN_FILENO, &sbuf)) {
302                         bb_perror_msg_and_die("%s", fname);
303                 }
304                 if ((!(S_ISCHR(sbuf.st_mode) ||
305                            S_ISBLK(sbuf.st_mode) ||
306                            S_ISFIFO(sbuf.st_mode))) && bb_dump_skip >= sbuf.st_size) {
307                         /* If bb_dump_size valid and bb_dump_skip >= size */
308                         bb_dump_skip -= sbuf.st_size;
309                         address += sbuf.st_size;
310                         return;
311                 }
312         }
313         if (fseek(stdin, bb_dump_skip, SEEK_SET)) {
314                 bb_perror_msg_and_die("%s", fname);
315         }
316         savaddress = address += bb_dump_skip;
317         bb_dump_skip = 0;
318 }
319
320 static int next(char **argv)
321 {
322         static int done;
323         int statok;
324
325         if (argv) {
326                 _argv = argv;
327                 return (1);
328         }
329         for (;;) {
330                 if (*_argv) {
331                         if (!(freopen(*_argv, "r", stdin))) {
332                                 bb_perror_msg("%s", *_argv);
333                                 exitval = 1;
334                                 ++_argv;
335                                 continue;
336                         }
337                         statok = done = 1;
338                 } else {
339                         if (done++)
340                                 return (0);
341                         statok = 0;
342                 }
343                 if (bb_dump_skip)
344                         do_skip(statok ? *_argv : "stdin", statok);
345                 if (*_argv)
346                         ++_argv;
347                 if (!bb_dump_skip)
348                         return (1);
349         }
350         /* NOTREACHED */
351 }
352
353 static unsigned char *get(void)
354 {
355         static int ateof = 1;
356         static unsigned char *curp=NULL, *savp; /*DBU:[dave@cray.com]initialize curp */
357         int n;
358         int need, nread;
359         unsigned char *tmpp;
360
361         if (!curp) {
362                 address = (off_t)0; /*DBU:[dave@cray.com] initialize,initialize..*/
363                 curp = (unsigned char *) xmalloc(bb_dump_blocksize);
364                 savp = (unsigned char *) xmalloc(bb_dump_blocksize);
365         } else {
366                 tmpp = curp;
367                 curp = savp;
368                 savp = tmpp;
369                 address = savaddress += bb_dump_blocksize;
370         }
371         for (need = bb_dump_blocksize, nread = 0;;) {
372                 /*
373                  * if read the right number of bytes, or at EOF for one file,
374                  * and no other files are available, zero-pad the rest of the
375                  * block and set the end flag.
376                  */
377                 if (!bb_dump_length || (ateof && !next((char **) NULL))) {
378                         if (need == bb_dump_blocksize) {
379                                 return ((unsigned char *) NULL);
380                         }
381                         if (bb_dump_vflag != ALL && !memcmp(curp, savp, nread)) {
382                                 if (bb_dump_vflag != DUP) {
383                                         printf("*\n");
384                                 }
385                                 return ((unsigned char *) NULL);
386                         }
387                         memset((char *) curp + nread, 0, need);
388                         eaddress = address + nread;
389                         return (curp);
390                 }
391                 n = fread((char *) curp + nread, sizeof(unsigned char),
392                                   bb_dump_length == -1 ? need : MIN(bb_dump_length, need), stdin);
393                 if (!n) {
394                         if (ferror(stdin)) {
395                                 bb_perror_msg("%s", _argv[-1]);
396                         }
397                         ateof = 1;
398                         continue;
399                 }
400                 ateof = 0;
401                 if (bb_dump_length != -1) {
402                         bb_dump_length -= n;
403                 }
404                 if (!(need -= n)) {
405                         if (bb_dump_vflag == ALL || bb_dump_vflag == FIRST
406                                 || memcmp(curp, savp, bb_dump_blocksize)) {
407                                 if (bb_dump_vflag == DUP || bb_dump_vflag == FIRST) {
408                                         bb_dump_vflag = WAIT;
409                                 }
410                                 return (curp);
411                         }
412                         if (bb_dump_vflag == WAIT) {
413                                 printf("*\n");
414                         }
415                         bb_dump_vflag = DUP;
416                         address = savaddress += bb_dump_blocksize;
417                         need = bb_dump_blocksize;
418                         nread = 0;
419                 } else {
420                         nread += n;
421                 }
422         }
423 }
424
425 static void bpad(PR * pr)
426 {
427         char *p1, *p2;
428
429         /*
430          * remove all conversion flags; '-' is the only one valid
431          * with %s, and it's not useful here.
432          */
433         pr->flags = F_BPAD;
434         *pr->cchar = 's';
435         for (p1 = pr->fmt; *p1 != '%'; ++p1);
436         for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1)
437                 if (pr->nospace) pr->nospace--;
438         while ((*p2++ = *p1++) != 0);
439 }
440
441 static const char conv_str[] =
442         "\0\\0\0"
443         "\007\\a\0"                             /* \a */
444         "\b\\b\0"
445         "\f\\b\0"
446         "\n\\n\0"
447         "\r\\r\0"
448         "\t\\t\0"
449         "\v\\v\0"
450         "\0";
451
452
453 static void conv_c(PR * pr, unsigned char * p)
454 {
455         const char *str = conv_str;
456         char buf[10];
457
458         do {
459                 if (*p == *str) {
460                         ++str;
461                         goto strpr;
462                 }
463                 str += 4;
464         } while (*str);
465
466         if (isprint(*p)) {
467                 *pr->cchar = 'c';
468                 (void) printf(pr->fmt, *p);
469         } else {
470                 sprintf(buf, "%03o", (int) *p);
471                 str = buf;
472           strpr:
473                 *pr->cchar = 's';
474                 printf(pr->fmt, str);
475         }
476 }
477
478 static void conv_u(PR * pr, unsigned char * p)
479 {
480         static const char list[] =
481                 "nul\0soh\0stx\0etx\0eot\0enq\0ack\0bel\0"
482                 "bs\0_ht\0_lf\0_vt\0_ff\0_cr\0_so\0_si\0_"
483                 "dle\0dcl\0dc2\0dc3\0dc4\0nak\0syn\0etb\0"
484                 "can\0em\0_sub\0esc\0fs\0_gs\0_rs\0_us";
485
486         /* od used nl, not lf */
487         if (*p <= 0x1f) {
488                 *pr->cchar = 's';
489                 printf(pr->fmt, list + (4 * (int)*p));
490         } else if (*p == 0x7f) {
491                 *pr->cchar = 's';
492                 printf(pr->fmt, "del");
493         } else if (isprint(*p)) {
494                 *pr->cchar = 'c';
495                 printf(pr->fmt, *p);
496         } else {
497                 *pr->cchar = 'x';
498                 printf(pr->fmt, (int) *p);
499         }
500 }
501
502 static void display(void)
503 {
504 /*  extern FU *endfu; */
505         FS *fs;
506         FU *fu;
507         PR *pr;
508         int cnt;
509         unsigned char *bp;
510
511         off_t saveaddress;
512         unsigned char savech = 0, *savebp;
513
514         while ((bp = get()) != NULL) {
515                 for (fs = bb_dump_fshead, savebp = bp, saveaddress = address; fs;
516                          fs = fs->nextfs, bp = savebp, address = saveaddress) {
517                         for (fu = fs->nextfu; fu; fu = fu->nextfu) {
518                                 if (fu->flags & F_IGNORE) {
519                                         break;
520                                 }
521                                 for (cnt = fu->reps; cnt; --cnt) {
522                                         for (pr = fu->nextpr; pr; address += pr->bcnt,
523                                                  bp += pr->bcnt, pr = pr->nextpr) {
524                                                 if (eaddress && address >= eaddress &&
525                                                         !(pr->flags & (F_TEXT | F_BPAD))) {
526                                                         bpad(pr);
527                                                 }
528                                                 if (cnt == 1 && pr->nospace) {
529                                                         savech = *pr->nospace;
530                                                         *pr->nospace = '\0';
531                                                 }
532 /*                      PRINT; */
533                                                 switch (pr->flags) {
534                                                 case F_ADDRESS:
535                                                         printf(pr->fmt, (unsigned int) address);
536                                                         break;
537                                                 case F_BPAD:
538                                                         printf(pr->fmt, "");
539                                                         break;
540                                                 case F_C:
541                                                         conv_c(pr, bp);
542                                                         break;
543                                                 case F_CHAR:
544                                                         printf(pr->fmt, *bp);
545                                                         break;
546                                                 case F_DBL:{
547                                                         double dval;
548                                                         float fval;
549
550                                                         switch (pr->bcnt) {
551                                                         case 4:
552                                                                 memmove((char *) &fval, (char *) bp,
553                                                                           sizeof(fval));
554                                                                 printf(pr->fmt, fval);
555                                                                 break;
556                                                         case 8:
557                                                                 memmove((char *) &dval, (char *) bp,
558                                                                           sizeof(dval));
559                                                                 printf(pr->fmt, dval);
560                                                                 break;
561                                                         }
562                                                         break;
563                                                 }
564                                                 case F_INT:{
565                                                         int ival;
566                                                         short sval;
567
568                                                         switch (pr->bcnt) {
569                                                         case 1:
570                                                                 printf(pr->fmt, (int) *bp);
571                                                                 break;
572                                                         case 2:
573                                                                 memmove((char *) &sval, (char *) bp,
574                                                                           sizeof(sval));
575                                                                 printf(pr->fmt, (int) sval);
576                                                                 break;
577                                                         case 4:
578                                                                 memmove((char *) &ival, (char *) bp,
579                                                                           sizeof(ival));
580                                                                 printf(pr->fmt, ival);
581                                                                 break;
582                                                         }
583                                                         break;
584                                                 }
585                                                 case F_P:
586                                                         printf(pr->fmt, isprint(*bp) ? *bp : '.');
587                                                         break;
588                                                 case F_STR:
589                                                         printf(pr->fmt, (char *) bp);
590                                                         break;
591                                                 case F_TEXT:
592                                                         printf(pr->fmt);
593                                                         break;
594                                                 case F_U:
595                                                         conv_u(pr, bp);
596                                                         break;
597                                                 case F_UINT:{
598                                                         unsigned int ival;
599                                                         unsigned short sval;
600
601                                                         switch (pr->bcnt) {
602                                                         case 1:
603                                                                 printf(pr->fmt, (unsigned int) * bp);
604                                                                 break;
605                                                         case 2:
606                                                                 memmove((char *) &sval, (char *) bp,
607                                                                           sizeof(sval));
608                                                                 printf(pr->fmt, (unsigned int) sval);
609                                                                 break;
610                                                         case 4:
611                                                                 memmove((char *) &ival, (char *) bp,
612                                                                           sizeof(ival));
613                                                                 printf(pr->fmt, ival);
614                                                                 break;
615                                                         }
616                                                         break;
617                                                 }
618                                                 }
619                                                 if (cnt == 1 && pr->nospace) {
620                                                         *pr->nospace = savech;
621                                                 }
622                                         }
623                                 }
624                         }
625                 }
626         }
627         if (endfu) {
628                 /*
629                  * if eaddress not set, error or file bb_dump_size was multiple of
630                  * bb_dump_blocksize, and no partial block ever found.
631                  */
632                 if (!eaddress) {
633                         if (!address) {
634                                 return;
635                         }
636                         eaddress = address;
637                 }
638                 for (pr = endfu->nextpr; pr; pr = pr->nextpr) {
639                         switch (pr->flags) {
640                         case F_ADDRESS:
641                                 (void) printf(pr->fmt, (unsigned int) eaddress);
642                                 break;
643                         case F_TEXT:
644                                 (void) printf(pr->fmt);
645                                 break;
646                         }
647                 }
648         }
649 }
650
651 int bb_dump_dump(char **argv)
652 {
653         FS *tfs;
654
655         /* figure out the data block bb_dump_size */
656         for (bb_dump_blocksize = 0, tfs = bb_dump_fshead; tfs; tfs = tfs->nextfs) {
657                 tfs->bcnt = bb_dump_size(tfs);
658                 if (bb_dump_blocksize < tfs->bcnt) {
659                         bb_dump_blocksize = tfs->bcnt;
660                 }
661         }
662         /* rewrite the rules, do syntax checking */
663         for (tfs = bb_dump_fshead; tfs; tfs = tfs->nextfs) {
664                 rewrite(tfs);
665         }
666
667         next(argv);
668         display();
669
670         return (exitval);
671 }
672
673 void bb_dump_add(const char *fmt)
674 {
675         const char *p;
676         char *p1;
677         char *p2;
678         static FS **nextfs;
679         FS *tfs;
680         FU *tfu, **nextfu;
681         const char *savep;
682
683         /* start new linked list of format units */
684         tfs = xzalloc(sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */
685         if (!bb_dump_fshead) {
686                 bb_dump_fshead = tfs;
687         } else {
688                 *nextfs = tfs;
689         }
690         nextfs = &tfs->nextfs;
691         nextfu = &tfs->nextfu;
692
693         /* take the format string and break it up into format units */
694         for (p = fmt;;) {
695                 /* bb_dump_skip leading white space */
696                 p = skip_whitespace(p);
697                 if (!*p) {
698                         break;
699                 }
700
701                 /* allocate a new format unit and link it in */
702                 /* NOSTRICT */
703                 /* DBU:[dave@cray.com] calloc so that forward pointers start out NULL */
704                 tfu = xzalloc(sizeof(FU));
705                 *nextfu = tfu;
706                 nextfu = &tfu->nextfu;
707                 tfu->reps = 1;
708
709                 /* if leading digit, repetition count */
710                 if (isdigit(*p)) {
711                         for (savep = p; isdigit(*p); ++p);
712                         if (!isspace(*p) && *p != '/') {
713                                 bb_error_msg_and_die("bad format {%s}", fmt);
714                         }
715                         /* may overwrite either white space or slash */
716                         tfu->reps = atoi(savep);
717                         tfu->flags = F_SETREP;
718                         /* bb_dump_skip trailing white space */
719                         p = skip_whitespace(++p);
720                 }
721
722                 /* bb_dump_skip slash and trailing white space */
723                 if (*p == '/') {
724                         p = skip_whitespace(++p);
725                 }
726
727                 /* byte count */
728                 if (isdigit(*p)) {
729                         for (savep = p; isdigit(*p); ++p);
730                         if (!isspace(*p)) {
731                                 bb_error_msg_and_die("bad format {%s}", fmt);
732                         }
733                         tfu->bcnt = atoi(savep);
734                         /* bb_dump_skip trailing white space */
735                         p = skip_whitespace(++p);
736                 }
737
738                 /* format */
739                 if (*p != '"') {
740                         bb_error_msg_and_die("bad format {%s}", fmt);
741                 }
742                 for (savep = ++p; *p != '"';) {
743                         if (*p++ == 0) {
744                                 bb_error_msg_and_die("bad format {%s}", fmt);
745                         }
746                 }
747                 tfu->fmt = xmalloc(p - savep + 1);
748                 strncpy(tfu->fmt, savep, p - savep);
749                 tfu->fmt[p - savep] = '\0';
750 /*      escape(tfu->fmt); */
751
752                 p1 = tfu->fmt;
753
754                 /* alphabetic escape sequences have to be done in place */
755                 for (p2 = p1;; ++p1, ++p2) {
756                         if (!*p1) {
757                                 *p2 = *p1;
758                                 break;
759                         }
760                         if (*p1 == '\\') {
761                                 const char *cs = conv_str + 4;
762                                 ++p1;
763                                 *p2 = *p1;
764                                 do {
765                                         if (*p1 == cs[2]) {
766                                                 *p2 = cs[0];
767                                                 break;
768                                         }
769                                         cs += 4;
770                                 } while (*cs);
771                         }
772                 }
773
774                 p++;
775         }
776 }
777
778 /*
779  * Copyright (c) 1989 The Regents of the University of California.
780  * All rights reserved.
781  *
782  * Redistribution and use in source and binary forms, with or without
783  * modification, are permitted provided that the following conditions
784  * are met:
785  * 1. Redistributions of source code must retain the above copyright
786  *    notice, this list of conditions and the following disclaimer.
787  * 2. Redistributions in binary form must reproduce the above copyright
788  *    notice, this list of conditions and the following disclaimer in the
789  *    documentation and/or other materials provided with the distribution.
790  * 3. Neither the name of the University nor the names of its contributors
791  *    may be used to endorse or promote products derived from this software
792  *    without specific prior written permission.
793  *
794  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
795  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
796  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
797  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
798  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
799  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
800  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
801  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
802  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
803  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
804  * SUCH DAMAGE.
805  */