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