whitespace fix
[oweals/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 dot_flags_width_chars[] 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 int_convs[] 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(dot_flags_width_chars + 1, *++fmt))
75                                 continue;
76                         if (*fmt == '.' && isdigit(*++fmt)) {
77                                 prec = atoi(fmt);
78                                 while (isdigit(*++fmt))
79                                         continue;
80                         }
81                         p = strchr(size_conv_str + 12, *fmt);
82                         if (!p) {
83                                 if (*fmt == 's') {
84                                         bcnt += prec;
85                                 }
86                                 if (*fmt == '_') {
87                                         ++fmt;
88                                         if ((*fmt == 'c') || (*fmt == 'p') || (*fmt == 'u')) {
89                                                 bcnt += 1;
90                                         }
91                                 }
92                         } else {
93                                 bcnt += p[-12];
94                         }
95                 }
96                 cur_size += bcnt * fu->reps;
97         }
98         return cur_size;
99 }
100
101 static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
102 {
103         FU *fu;
104
105         for (fu = fs->nextfu; fu; fu = fu->nextfu) {
106                 PR *pr;
107                 char *p1, *p2, *p3;
108                 char *fmtp;
109                 int nconv = 0;
110                 /*
111                  * break each format unit into print units; each
112                  * conversion character gets its own.
113                  */
114                 for (fmtp = fu->fmt; *fmtp; ) {
115                         unsigned len;
116                         const char *prec;
117                         const char *byte_count_str;
118
119                         /* DBU:[dvae@cray.com] zalloc so that forward ptrs start out NULL */
120                         pr = xzalloc(sizeof(*pr));
121                         if (!fu->nextpr)
122                                 fu->nextpr = pr;
123
124                         /* skip preceding text and up to the next % sign */
125                         p1 = strchr(fmtp, '%');
126                         if (!p1) { /* only text in the string */
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                         prec = NULL;
137                         if (fu->bcnt) {
138                                 /* skip to conversion character */
139                                 while (strchr(dot_flags_width_chars, *++p1))
140                                         continue;
141                         } else {
142                                 /* skip any special chars, field width */
143                                 while (strchr(dot_flags_width_chars + 1, *++p1))
144                                         continue;
145                                 if (*p1 == '.' && isdigit(*++p1)) {
146                                         prec = p1;
147                                         while (isdigit(*++p1))
148                                                 continue;
149                                 }
150                         }
151
152                         p2 = p1 + 1; /* set end pointer */
153
154                         /*
155                          * figure out the byte count for each conversion;
156                          * rewrite the format as necessary, set up blank-
157                          * padding for end of data.
158                          */
159                         if (*p1 == 'c') {
160                                 pr->flags = F_CHAR;
161  DO_BYTE_COUNT_1:
162                                 byte_count_str = "\001";
163  DO_BYTE_COUNT:
164                                 if (fu->bcnt) {
165                                         for (;;) {
166                                                 if (fu->bcnt == *byte_count_str)
167                                                         break;
168                                                 if (*++byte_count_str == 0)
169                                                         bb_error_msg_and_die("bad byte count for conversion character %s", p1);
170                                         }
171                                 }
172                                 /* Unlike the original, output the remainder of the format string. */
173                                 pr->bcnt = *byte_count_str;
174                         } else
175                         if (*p1 == 'l') { /* %ld etc */
176                                 const char *e;
177
178                                 ++p2;
179                                 ++p1;
180  DO_INT_CONV:
181                                 e = strchr(int_convs, *p1); /* "diouxX"? */
182                                 if (!e)
183                                         goto DO_BAD_CONV_CHAR;
184                                 pr->flags = F_INT;
185                                 if (e > int_convs + 1) /* not d or i? */
186                                         pr->flags = F_UINT;
187                                 byte_count_str = "\004\002\001";
188                                 goto DO_BYTE_COUNT;
189                         } else
190                         if (strchr(int_convs, *p1)) { /* %d etc */
191                                 goto DO_INT_CONV;
192                         } else
193                         if (strchr("eEfgG", *p1)) { /* floating point */
194                                 pr->flags = F_DBL;
195                                 byte_count_str = "\010\004";
196                                 goto DO_BYTE_COUNT;
197                         } else
198                         if (*p1 == 's') {
199                                 pr->flags = F_STR;
200                                 pr->bcnt = fu->bcnt;
201                                 if (fu->bcnt == 0) {
202                                         if (!prec)
203                                                 bb_error_msg_and_die("%%s needs precision or byte count");
204                                         pr->bcnt = atoi(prec);
205                                 }
206                         } else
207                         if (*p1 == '_') {
208                                 p2++;  /* move past a in "%_a" */
209                                 switch (p1[1]) {
210                                 case 'A':       /* %_A[dox]: print address and the end */
211                                         dumper->endfu = fu;
212                                         fu->flags |= F_IGNORE;
213                                         /* FALLTHROUGH */
214                                 case 'a':       /* %_a[dox]: current address */
215                                         pr->flags = F_ADDRESS;
216                                         p2++;  /* move past x in "%_ax" */
217                                         if ((p1[2] != 'd') && (p1[2] != 'o') && (p1[2] != 'x')) {
218                                                 goto DO_BAD_CONV_CHAR;
219                                         }
220                                         *p1 = p1[2];
221                                         break;
222                                 case 'c':       /* %_c: chars, \ooo, \n \r \t etc */
223                                         pr->flags = F_C;
224                                         /* *p1 = 'c';   set in conv_c */
225                                         goto DO_BYTE_COUNT_1;
226                                 case 'p':       /* %_p: chars, dots for nonprintable */
227                                         pr->flags = F_P;
228                                         *p1 = 'c';
229                                         goto DO_BYTE_COUNT_1;
230                                 case 'u':       /* %_p: chars, 'nul', 'esc' etc for nonprintable */
231                                         pr->flags = F_U;
232                                         /* *p1 = 'c';   set in conv_u */
233                                         goto DO_BYTE_COUNT_1;
234                                 default:
235                                         goto DO_BAD_CONV_CHAR;
236                                 }
237                         } else {
238  DO_BAD_CONV_CHAR:
239                                 bb_error_msg_and_die("bad conversion character %%%s", p1);
240                         }
241
242                         /*
243                          * copy to PR format string, set conversion character
244                          * pointer, update original.
245                          */
246                         len = (p1 - fmtp) + 1;
247                         pr->fmt = xstrndup(fmtp, len);
248                         /* DBU:[dave@cray.com] w/o this, trailing fmt text, space is lost.
249                          * Skip subsequent text and up to the next % sign and tack the
250                          * additional text onto fmt: eg. if fmt is "%x is a HEX number",
251                          * we lose the " is a HEX number" part of fmt.
252                          */
253                         for (p3 = p2; *p3 && *p3 != '%'; p3++)
254                                 continue;
255                         if ((p3 - p2) != 0) {
256                                 char *d;
257                                 pr->fmt = d = xrealloc(pr->fmt, len + (p3 - p2) + 1);
258                                 d += len;
259                                 do {
260                                         *d++ = *p2++;
261                                 } while (p2 != p3);
262                                 *d = '\0';
263                                 /* now p2 = p3 */
264                         }
265                         pr->cchar = pr->fmt + len - 1; /* must be after realloc! */
266                         fmtp = p2;
267
268                         /* only one conversion character if byte count */
269                         if (!(pr->flags & F_ADDRESS) && fu->bcnt && nconv++) {
270                                 bb_error_msg_and_die("byte count with multiple conversion characters");
271                         }
272                 }
273                 /*
274                  * if format unit byte count not specified, figure it out
275                  * so can adjust rep count later.
276                  */
277                 if (fu->bcnt == 0)
278                         for (pr = fu->nextpr; pr; pr = pr->nextpr)
279                                 fu->bcnt += pr->bcnt;
280         }
281         /*
282          * if the format string interprets any data at all, and it's
283          * not the same as the blocksize, and its last format unit
284          * interprets any data at all, and has no iteration count,
285          * repeat it as necessary.
286          *
287          * if rep count is greater than 1, no trailing whitespace
288          * gets output from the last iteration of the format unit.
289          */
290         for (fu = fs->nextfu; fu; fu = fu->nextfu) {
291                 if (!fu->nextfu
292                  && fs->bcnt < dumper->blocksize
293                  && !(fu->flags & F_SETREP)
294                  && fu->bcnt
295                 ) {
296                         fu->reps += (dumper->blocksize - fs->bcnt) / fu->bcnt;
297                 }
298                 if (fu->reps > 1 && fu->nextpr) {
299                         PR *pr;
300                         char *p1, *p2;
301
302                         for (pr = fu->nextpr;; pr = pr->nextpr)
303                                 if (!pr->nextpr)
304                                         break;
305                         p2 = NULL;
306                         for (p1 = pr->fmt; *p1; ++p1)
307                                 p2 = isspace(*p1) ? p1 : NULL;
308                         if (p2)
309                                 pr->nospace = p2;
310                 }
311         }
312 }
313
314 static void do_skip(priv_dumper_t *dumper, const char *fname, int statok)
315 {
316         struct stat sbuf;
317
318         if (statok) {
319                 xfstat(STDIN_FILENO, &sbuf, fname);
320                 if (!(S_ISCHR(sbuf.st_mode) || S_ISBLK(sbuf.st_mode) || S_ISFIFO(sbuf.st_mode))
321                  && dumper->pub.dump_skip >= sbuf.st_size
322                 ) {
323                         /* If bb_dump_size valid and pub.dump_skip >= size */
324                         dumper->pub.dump_skip -= sbuf.st_size;
325                         dumper->address += sbuf.st_size;
326                         return;
327                 }
328         }
329         if (fseeko(stdin, dumper->pub.dump_skip, SEEK_SET)) {
330                 bb_simple_perror_msg_and_die(fname);
331         }
332         dumper->address += dumper->pub.dump_skip;
333         dumper->savaddress = dumper->address;
334         dumper->pub.dump_skip = 0;
335 }
336
337 static NOINLINE int next(priv_dumper_t *dumper)
338 {
339         int statok;
340
341         for (;;) {
342                 if (*dumper->argv) {
343                         dumper->next__done = statok = 1;
344                         if (!(freopen(*dumper->argv, "r", stdin))) {
345                                 bb_simple_perror_msg(*dumper->argv);
346                                 dumper->exitval = 1;
347                                 ++dumper->argv;
348                                 continue;
349                         }
350                 } else {
351                         if (dumper->next__done)
352                                 return 0; /* no next file */
353                         dumper->next__done = 1;
354 //why stat of stdin is specially prohibited?
355                         statok = 0;
356                 }
357                 if (dumper->pub.dump_skip)
358                         do_skip(dumper, statok ? *dumper->argv : "stdin", statok);
359                 if (*dumper->argv)
360                         ++dumper->argv;
361                 if (!dumper->pub.dump_skip)
362                         return 1;
363         }
364         /* NOTREACHED */
365 }
366
367 static unsigned char *get(priv_dumper_t *dumper)
368 {
369         int n;
370         int need, nread;
371         int blocksize = dumper->blocksize;
372
373         if (!dumper->get__curp) {
374                 dumper->address = (off_t)0; /*DBU:[dave@cray.com] initialize,initialize..*/
375                 dumper->get__curp = xmalloc(blocksize);
376                 dumper->get__savp = xzalloc(blocksize); /* need to be initialized */
377         } else {
378                 unsigned char *tmp = dumper->get__curp;
379                 dumper->get__curp = dumper->get__savp;
380                 dumper->get__savp = tmp;
381                 dumper->savaddress += blocksize;
382                 dumper->address = dumper->savaddress;
383         }
384         need = blocksize;
385         nread = 0;
386         while (1) {
387                 /*
388                  * if read the right number of bytes, or at EOF for one file,
389                  * and no other files are available, zero-pad the rest of the
390                  * block and set the end flag.
391                  */
392                 if (!dumper->pub.dump_length || (dumper->get__ateof && !next(dumper))) {
393                         if (need == blocksize) {
394                                 return NULL;
395                         }
396                         if (dumper->pub.dump_vflag != ALL && !memcmp(dumper->get__curp, dumper->get__savp, nread)) {
397                                 if (dumper->pub.dump_vflag != DUP) {
398                                         puts("*");
399                                 }
400                                 return NULL;
401                         }
402                         memset(dumper->get__curp + nread, 0, need);
403                         dumper->eaddress = dumper->address + nread;
404                         return dumper->get__curp;
405                 }
406                 n = fread(dumper->get__curp + nread, sizeof(unsigned char),
407                                 dumper->pub.dump_length == -1 ? need : MIN(dumper->pub.dump_length, need), stdin);
408                 if (!n) {
409                         if (ferror(stdin)) {
410                                 bb_simple_perror_msg(dumper->argv[-1]);
411                         }
412                         dumper->get__ateof = 1;
413                         continue;
414                 }
415                 dumper->get__ateof = 0;
416                 if (dumper->pub.dump_length != -1) {
417                         dumper->pub.dump_length -= n;
418                 }
419                 need -= n;
420                 if (!need) {
421                         if (dumper->pub.dump_vflag == ALL || dumper->pub.dump_vflag == FIRST
422                          || memcmp(dumper->get__curp, dumper->get__savp, blocksize)
423                         ) {
424                                 if (dumper->pub.dump_vflag == DUP || dumper->pub.dump_vflag == FIRST) {
425                                         dumper->pub.dump_vflag = WAIT;
426                                 }
427                                 return dumper->get__curp;
428                         }
429                         if (dumper->pub.dump_vflag == WAIT) {
430                                 puts("*");
431                         }
432                         dumper->pub.dump_vflag = DUP;
433                         dumper->savaddress += blocksize;
434                         dumper->address = dumper->savaddress;
435                         need = blocksize;
436                         nread = 0;
437                 } else {
438                         nread += n;
439                 }
440         }
441 }
442
443 static void bpad(PR *pr)
444 {
445         char *p1, *p2;
446
447         /*
448          * remove all conversion flags; '-' is the only one valid
449          * with %s, and it's not useful here.
450          */
451         pr->flags = F_BPAD;
452         *pr->cchar = 's';
453         for (p1 = pr->fmt; *p1 != '%'; ++p1)
454                 continue;
455         for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1)
456                 if (pr->nospace)
457                         pr->nospace--;
458         while ((*p2++ = *p1++) != 0)
459                 continue;
460 }
461
462 static const char conv_str[] ALIGN1 =
463         "\0"  "\\""0""\0"
464         "\007""\\""a""\0"  /* \a */
465         "\b"  "\\""b""\0"
466         "\f"  "\\""f""\0"
467         "\n"  "\\""n""\0"
468         "\r"  "\\""r""\0"
469         "\t"  "\\""t""\0"
470         "\v"  "\\""v""\0"
471         ;
472
473
474 static void conv_c(PR *pr, unsigned char *p)
475 {
476         const char *str = conv_str;
477         char buf[10];
478
479         do {
480                 if (*p == *str) {
481                         ++str;
482                         goto strpr; /* map e.g. '\n' to "\\n" */
483                 }
484                 str += 4;
485         } while (*str);
486
487         if (isprint_asciionly(*p)) {
488                 *pr->cchar = 'c';
489                 printf(pr->fmt, *p);
490         } else {
491                 sprintf(buf, "%03o", (int) *p);
492                 str = buf;
493  strpr:
494                 *pr->cchar = 's';
495                 printf(pr->fmt, str);
496         }
497 }
498
499 static void conv_u(PR *pr, unsigned char *p)
500 {
501         static const char list[] ALIGN1 =
502                 "nul\0soh\0stx\0etx\0eot\0enq\0ack\0bel\0"
503                 "bs\0_ht\0_lf\0_vt\0_ff\0_cr\0_so\0_si\0_"
504                 "dle\0dcl\0dc2\0dc3\0dc4\0nak\0syn\0etb\0"
505                 "can\0em\0_sub\0esc\0fs\0_gs\0_rs\0_us";
506
507         /* od used nl, not lf */
508         if (*p <= 0x1f) {
509                 *pr->cchar = 's';
510                 printf(pr->fmt, list + (4 * (int)*p));
511         } else if (*p == 0x7f) {
512                 *pr->cchar = 's';
513                 printf(pr->fmt, "del");
514         } else if (*p < 0x7f) { /* isprint() */
515                 *pr->cchar = 'c';
516                 printf(pr->fmt, *p);
517         } else {
518                 *pr->cchar = 'x';
519                 printf(pr->fmt, (int) *p);
520         }
521 }
522
523 static void display(priv_dumper_t* dumper)
524 {
525         FS *fs;
526         FU *fu;
527         PR *pr;
528         int cnt;
529         unsigned char *bp, *savebp;
530         off_t saveaddress;
531         unsigned char savech = '\0';
532
533         while ((bp = get(dumper)) != NULL) {
534                 fs = dumper->pub.fshead;
535                 savebp = bp;
536                 saveaddress = dumper->address;
537                 for (; fs; fs = fs->nextfs, bp = savebp, dumper->address = saveaddress) {
538                         for (fu = fs->nextfu; fu; fu = fu->nextfu) {
539                                 if (fu->flags & F_IGNORE) {
540                                         break;
541                                 }
542                                 for (cnt = fu->reps; cnt; --cnt) {
543                                         for (pr = fu->nextpr; pr; dumper->address += pr->bcnt,
544                                                                 bp += pr->bcnt, pr = pr->nextpr) {
545                                                 if (dumper->eaddress && dumper->address >= dumper->eaddress
546                                                  && !(pr->flags & (F_TEXT | F_BPAD))
547                                                 ) {
548                                                         bpad(pr);
549                                                 }
550                                                 if (cnt == 1 && pr->nospace) {
551                                                         savech = *pr->nospace;
552                                                         *pr->nospace = '\0';
553                                                 }
554 /*                      PRINT; */
555                                                 switch (pr->flags) {
556                                                 case F_ADDRESS:
557                                                         printf(pr->fmt, (unsigned) dumper->address);
558                                                         break;
559                                                 case F_BPAD:
560                                                         printf(pr->fmt, "");
561                                                         break;
562                                                 case F_C:
563                                                         conv_c(pr, bp);
564                                                         break;
565                                                 case F_CHAR:
566                                                         printf(pr->fmt, *bp);
567                                                         break;
568                                                 case F_DBL: {
569                                                         double dval;
570                                                         float fval;
571
572                                                         switch (pr->bcnt) {
573                                                         case 4:
574                                                                 memcpy(&fval, bp, sizeof(fval));
575                                                                 printf(pr->fmt, fval);
576                                                                 break;
577                                                         case 8:
578                                                                 memcpy(&dval, bp, sizeof(dval));
579                                                                 printf(pr->fmt, dval);
580                                                                 break;
581                                                         }
582                                                         break;
583                                                 }
584                                                 case F_INT: {
585                                                         int ival;
586                                                         short sval;
587
588                                                         switch (pr->bcnt) {
589                                                         case 1:
590                                                                 printf(pr->fmt, (int) *bp);
591                                                                 break;
592                                                         case 2:
593                                                                 memcpy(&sval, bp, sizeof(sval));
594                                                                 printf(pr->fmt, (int) sval);
595                                                                 break;
596                                                         case 4:
597                                                                 memcpy(&ival, bp, sizeof(ival));
598                                                                 printf(pr->fmt, ival);
599                                                                 break;
600                                                         }
601                                                         break;
602                                                 }
603                                                 case F_P:
604                                                         printf(pr->fmt, isprint_asciionly(*bp) ? *bp : '.');
605                                                         break;
606                                                 case F_STR:
607                                                         printf(pr->fmt, (char *) bp);
608                                                         break;
609                                                 case F_TEXT:
610                                                         printf(pr->fmt);
611                                                         break;
612                                                 case F_U:
613                                                         conv_u(pr, bp);
614                                                         break;
615                                                 case F_UINT: {
616                                                         unsigned ival;
617                                                         unsigned short sval;
618
619                                                         switch (pr->bcnt) {
620                                                         case 1:
621                                                                 printf(pr->fmt, (unsigned) *bp);
622                                                                 break;
623                                                         case 2:
624                                                                 memcpy(&sval, bp, sizeof(sval));
625                                                                 printf(pr->fmt, (unsigned) sval);
626                                                                 break;
627                                                         case 4:
628                                                                 memcpy(&ival, bp, sizeof(ival));
629                                                                 printf(pr->fmt, ival);
630                                                                 break;
631                                                         }
632                                                         break;
633                                                 }
634                                                 }
635                                                 if (cnt == 1 && pr->nospace) {
636                                                         *pr->nospace = savech;
637                                                 }
638                                         }
639                                 }
640                         }
641                 }
642         }
643         if (dumper->endfu) {
644                 /*
645                  * if eaddress not set, error or file size was multiple
646                  * of blocksize, and no partial block ever found.
647                  */
648                 if (!dumper->eaddress) {
649                         if (!dumper->address) {
650                                 return;
651                         }
652                         dumper->eaddress = dumper->address;
653                 }
654                 for (pr = dumper->endfu->nextpr; pr; pr = pr->nextpr) {
655                         switch (pr->flags) {
656                         case F_ADDRESS:
657                                 printf(pr->fmt, (unsigned) dumper->eaddress);
658                                 break;
659                         case F_TEXT:
660                                 printf(pr->fmt);
661                                 break;
662                         }
663                 }
664         }
665 }
666
667 #define dumper ((priv_dumper_t*)pub_dumper)
668 int FAST_FUNC bb_dump_dump(dumper_t *pub_dumper, char **argv)
669 {
670         FS *tfs;
671         int blocksize;
672
673         /* figure out the data block bb_dump_size */
674         blocksize = 0;
675         tfs = dumper->pub.fshead;
676         while (tfs) {
677                 tfs->bcnt = bb_dump_size(tfs);
678                 if (blocksize < tfs->bcnt) {
679                         blocksize = tfs->bcnt;
680                 }
681                 tfs = tfs->nextfs;
682         }
683         dumper->blocksize = blocksize;
684
685         /* rewrite the rules, do syntax checking */
686         for (tfs = dumper->pub.fshead; tfs; tfs = tfs->nextfs) {
687                 rewrite(dumper, tfs);
688         }
689
690         dumper->argv = argv;
691         display(dumper);
692
693         return dumper->exitval;
694 }
695
696 void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
697 {
698         const char *p;
699         FS *tfs;
700         FU *tfu, **nextfupp;
701         const char *savep;
702
703         /* start new linked list of format units */
704         tfs = xzalloc(sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */
705         if (!dumper->pub.fshead) {
706                 dumper->pub.fshead = tfs;
707         } else {
708                 FS *fslast = dumper->pub.fshead;
709                 while (fslast->nextfs)
710                         fslast = fslast->nextfs;
711                 fslast->nextfs = tfs;
712         }
713         nextfupp = &tfs->nextfu;
714
715         /* take the format string and break it up into format units */
716         p = fmt;
717         for (;;) {
718                 p = skip_whitespace(p);
719                 if (*p == '\0') {
720                         break;
721                 }
722
723                 /* allocate a new format unit and link it in */
724                 /* NOSTRICT */
725                 /* DBU:[dave@cray.com] zalloc so that forward pointers start out NULL */
726                 tfu = xzalloc(sizeof(FU));
727                 *nextfupp = tfu;
728                 nextfupp = &tfu->nextfu;
729                 tfu->reps = 1;
730
731                 /* if leading digit, repetition count */
732                 if (isdigit(*p)) {
733                         for (savep = p; isdigit(*p); ++p)
734                                 continue;
735                         if (!isspace(*p) && *p != '/') {
736                                 bb_error_msg_and_die("bad format {%s}", fmt);
737                         }
738                         /* may overwrite either white space or slash */
739                         tfu->reps = atoi(savep);
740                         tfu->flags = F_SETREP;
741                         /* skip trailing white space */
742                         p = skip_whitespace(++p);
743                 }
744
745                 /* skip slash and trailing white space */
746                 if (*p == '/') {
747                         p = skip_whitespace(p + 1);
748                 }
749
750                 /* byte count */
751                 if (isdigit(*p)) {
752 // TODO: use bb_strtou
753                         savep = p;
754                         while (isdigit(*++p))
755                                 continue;
756                         if (!isspace(*p)) {
757                                 bb_error_msg_and_die("bad format {%s}", fmt);
758                         }
759 // Above check prohibits formats such as '/1"%02x"' - it requires space after 1.
760 // Other than this, formats can be pretty much jammed together:
761 // "%07_ax:"8/2 "%04x|""\n"
762 // but this space is required. The check *can* be removed, but
763 // keeping it to stay compat with util-linux hexdump.
764                         tfu->bcnt = atoi(savep);
765                         /* skip trailing white space */
766                         p = skip_whitespace(p + 1);
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
780                 /* alphabetic escape sequences have to be done in place */
781                 strcpy_and_process_escape_sequences(tfu->fmt, tfu->fmt);
782                 /* unknown mappings are not changed: "\z" -> '\\' 'z' */
783                 /* trailing backslash, if any, is preserved */
784 #if 0
785                 char *p1;
786                 char *p2;
787                 p1 = tfu->fmt;
788                 for (p2 = p1;; ++p1, ++p2) {
789                         *p2 = *p1;
790                         if (*p1 == '\0')
791                                 break;
792
793                         if (*p1 == '\\') {
794                                 const char *cs;
795
796                                 p1++;
797                                 *p2 = *p1;
798                                 if (*p1 == '\0') {
799                                         /* "...\" trailing backslash. Eaten. */
800                                         break;
801                                 }
802                                 cs = conv_str + 4; /* skip NUL element */
803                                 do {
804                                         /* map e.g. "\n" -> '\n' */
805                                         if (*p1 == cs[2]) {
806                                                 *p2 = cs[0];
807                                                 break;
808                                         }
809                                         cs += 4;
810                                 } while (*cs);
811                                 /* unknown mappings remove bkslash: "\z" -> 'z' */
812                         }
813                 }
814 #endif
815
816                 p++;
817         }
818 }
819
820 /*
821  * Copyright (c) 1989 The Regents of the University of California.
822  * All rights reserved.
823  *
824  * Redistribution and use in source and binary forms, with or without
825  * modification, are permitted provided that the following conditions
826  * are met:
827  * 1. Redistributions of source code must retain the above copyright
828  *    notice, this list of conditions and the following disclaimer.
829  * 2. Redistributions in binary form must reproduce the above copyright
830  *    notice, this list of conditions and the following disclaimer in the
831  *    documentation and/or other materials provided with the distribution.
832  * 3. Neither the name of the University nor the names of its contributors
833  *    may be used to endorse or promote products derived from this software
834  *    without specific prior written permission.
835  *
836  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
837  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
838  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
839  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
840  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
841  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
842  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
843  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
844  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
845  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
846  * SUCH DAMAGE.
847  */