fix "variable 'foo' set but not used" warnings
[platform/upstream/busybox.git] / libbb / dump.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Support code for the hexdump and od applets,
4  * based on code from util-linux v 2.11l
5  *
6  * Copyright (c) 1989
7  * The Regents of the University of California.  All rights reserved.
8  *
9  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
10  *
11  * Original copyright notice is retained at the end of this file.
12  */
13
14 #include "libbb.h"
15 #include "dump.h"
16
17 static const char index_str[] ALIGN1 = ".#-+ 0123456789";
18
19 static const char size_conv_str[] ALIGN1 =
20 "\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG";
21
22 static const char lcc[] ALIGN1 = "diouxX";
23
24
25 typedef struct priv_dumper_t {
26         dumper_t pub;
27
28         char **argv;
29         FU *endfu;
30         off_t savaddress;        /* saved address/offset in stream */
31         off_t eaddress;          /* end address */
32         off_t address;           /* address/offset in stream */
33         int blocksize;
34         smallint exitval;        /* final exit value */
35
36         /* former statics */
37         smallint next__done;
38         smallint get__ateof; // = 1;
39         unsigned char *get__curp;
40         unsigned char *get__savp;
41 } priv_dumper_t;
42
43 dumper_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
53 static 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         /* figure out the data block bb_dump_size needed for each format unit */
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                          * skip any special chars -- save precision in
72                          * case it's a %s format.
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
99 static 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                  * break each format unit into print units; each
112                  * conversion character gets its own.
113                  */
114                 for (nconv = 0, fmtp = fu->fmt; *fmtp; ) {
115                         /* NOSTRICT */
116                         /* DBU:[dvae@cray.com] zalloc so that forward ptrs start out NULL*/
117                         pr = xzalloc(sizeof(PR));
118                         if (!fu->nextpr)
119                                 fu->nextpr = pr;
120
121                         /* skip preceding text and up to the next % sign */
122                         for (p1 = fmtp; *p1 && *p1 != '%'; ++p1)
123                                 continue;
124
125                         /* only text in the string */
126                         if (!*p1) {
127                                 pr->fmt = fmtp;
128                                 pr->flags = F_TEXT;
129                                 break;
130                         }
131
132                         /*
133                          * get precision for %s -- if have a byte count, don't
134                          * need it.
135                          */
136                         if (fu->bcnt) {
137                                 sokay = USEBCNT;
138                                 /* skip to conversion character */
139                                 for (++p1; strchr(index_str, *p1); ++p1)
140                                         continue;
141                         } else {
142                                 /* skip any special chars, field width */
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; /* set end pointer */
155
156                         /*
157                          * figure out the byte count for each conversion;
158                          * rewrite the format as necessary, set up blank-
159                          * pbb_dump_adding for end of data.
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                                 /* Unlike the original, output the remainder of the format string. */
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                                 /* NOTREACHED */
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 {   /* NOTOKAY */
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                                         /* FALLTHROUGH */
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                                         /* *p1 = 'c';   set in conv_c */
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                                         /* *p1 = 'c';   set in conv_u */
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                          * copy to PR format string, set conversion character
248                          * pointer, update original.
249                          */
250                         savech = *p2;
251                         p1[1] = '\0';
252                         pr->fmt = xstrdup(fmtp);
253                         *p2 = savech;
254                         //Too early! xrealloc can move pr->fmt!
255                         //pr->cchar = pr->fmt + (p1 - fmtp);
256
257                         /* DBU:[dave@cray.com] w/o this, trailing fmt text, space is lost.
258                          * Skip subsequent text and up to the next % sign and tack the
259                          * additional text onto fmt: eg. if fmt is "%x is a HEX number",
260                          * we lose the " is a HEX number" part of fmt.
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                         /* only one conversion character if byte count */
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                  * if format unit byte count not specified, figure it out
283                  * so can adjust rep count later.
284                  */
285                 if (!fu->bcnt)
286                         for (pr = fu->nextpr; pr; pr = pr->nextpr)
287                                 fu->bcnt += pr->bcnt;
288         }
289         /*
290          * if the format string interprets any data at all, and it's
291          * not the same as the blocksize, and its last format unit
292          * interprets any data at all, and has no iteration count,
293          * repeat it as necessary.
294          *
295          * if, rep count is greater than 1, no trailing whitespace
296          * gets output from the last iteration of the format unit.
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
318 static 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                         /* If bb_dump_size valid and pub.dump_skip >= size */
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
341 static 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; /* no next file */
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         /* NOTREACHED */
368 }
369
370 static 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; /*DBU:[dave@cray.com] initialize,initialize..*/
378                 dumper->get__curp = xmalloc(blocksize);
379                 dumper->get__savp = xzalloc(blocksize); /* need to be initialized */
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                  * if read the right number of bytes, or at EOF for one file,
392                  * and no other files are available, zero-pad the rest of the
393                  * block and set the end flag.
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
446 static void bpad(PR *pr)
447 {
448         char *p1, *p2;
449
450         /*
451          * remove all conversion flags; '-' is the only one valid
452          * with %s, and it's not useful here.
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
465 static const char conv_str[] ALIGN1 =
466         "\0\\0\0"
467         "\007\\a\0"  /* \a */
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
477 static 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
502 static 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         /* od used nl, not lf */
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) { /* isprint() */
518                 *pr->cchar = 'c';
519                 printf(pr->fmt, *p);
520         } else {
521                 *pr->cchar = 'x';
522                 printf(pr->fmt, (int) *p);
523         }
524 }
525
526 static 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 /*                      PRINT; */
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                  * if eaddress not set, error or file size was multiple
649                  * of blocksize, and no partial block ever found.
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)
671 int FAST_FUNC bb_dump_dump(dumper_t *pub_dumper, char **argv)
672 {
673         FS *tfs;
674         int blocksize;
675
676         /* figure out the data block bb_dump_size */
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         /* rewrite the rules, do syntax checking */
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
699 void 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         /* start new linked list of format units */
709         tfs = xzalloc(sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */
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         /* take the format string and break it up into format units */
721         p = fmt;
722         for (;;) {
723                 p = skip_whitespace(p);
724                 if (!*p) {
725                         break;
726                 }
727
728                 /* allocate a new format unit and link it in */
729                 /* NOSTRICT */
730                 /* DBU:[dave@cray.com] zalloc so that forward pointers start out NULL */
731                 tfu = xzalloc(sizeof(FU));
732                 *nextfupp = tfu;
733                 nextfupp = &tfu->nextfu;
734                 tfu->reps = 1;
735
736                 /* if leading digit, repetition count */
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                         /* may overwrite either white space or slash */
744                         tfu->reps = atoi(savep);
745                         tfu->flags = F_SETREP;
746                         /* skip trailing white space */
747                         p = skip_whitespace(++p);
748                 }
749
750                 /* skip slash and trailing white space */
751                 if (*p == '/') {
752                         p = skip_whitespace(++p);
753                 }
754
755                 /* byte count */
756                 if (isdigit(*p)) {
757 // TODO: use bb_strtou
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                         /* skip trailing white space */
766                         p = skip_whitespace(++p);
767                 }
768
769                 /* format */
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 /*      escape(tfu->fmt); */
780
781                 p1 = tfu->fmt;
782
783                 /* alphabetic escape sequences have to be done in place */
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  * Copyright (c) 1989 The Regents of the University of California.
809  * All rights reserved.
810  *
811  * Redistribution and use in source and binary forms, with or without
812  * modification, are permitted provided that the following conditions
813  * are met:
814  * 1. Redistributions of source code must retain the above copyright
815  *    notice, this list of conditions and the following disclaimer.
816  * 2. Redistributions in binary form must reproduce the above copyright
817  *    notice, this list of conditions and the following disclaimer in the
818  *    documentation and/or other materials provided with the distribution.
819  * 3. Neither the name of the University nor the names of its contributors
820  *    may be used to endorse or promote products derived from this software
821  *    without specific prior written permission.
822  *
823  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
824  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
825  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
826  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
827  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
828  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
829  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
830  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
831  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
832  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
833  * SUCH DAMAGE.
834  */