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