[T106][ZXW-22]7520V3SCV2.01.01.02P42U09_VEC_V0.8_AP_VEC origin source commit

Change-Id: Ic6e05d89ecd62fc34f82b23dcf306c93764aec4b
diff --git a/ap/app/busybox/src/libbb/dump.c b/ap/app/busybox/src/libbb/dump.c
new file mode 100644
index 0000000..7e43564
--- /dev/null
+++ b/ap/app/busybox/src/libbb/dump.c
@@ -0,0 +1,837 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Support code for the hexdump and od applets,
+ * based on code from util-linux v 2.11l
+ *
+ * Copyright (c) 1989
+ * The Regents of the University of California.  All rights reserved.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+
+#include "libbb.h"
+#include "dump.h"
+
+static const char index_str[] ALIGN1 = ".#-+ 0123456789";
+
+static const char size_conv_str[] ALIGN1 =
+"\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG";
+
+static const char lcc[] ALIGN1 = "diouxX";
+
+
+typedef struct priv_dumper_t {
+	dumper_t pub;
+
+	char **argv;
+	FU *endfu;
+	off_t savaddress;        /* saved address/offset in stream */
+	off_t eaddress;          /* end address */
+	off_t address;           /* address/offset in stream */
+	int blocksize;
+	smallint exitval;        /* final exit value */
+
+	/* former statics */
+	smallint next__done;
+	smallint get__ateof; // = 1;
+	unsigned char *get__curp;
+	unsigned char *get__savp;
+} priv_dumper_t;
+
+dumper_t* FAST_FUNC alloc_dumper(void)
+{
+	priv_dumper_t *dumper = xzalloc(sizeof(*dumper));
+	dumper->pub.dump_length = -1;
+	dumper->pub.dump_vflag = FIRST;
+	dumper->get__ateof = 1;
+	return &dumper->pub;
+}
+
+
+static NOINLINE int bb_dump_size(FS *fs)
+{
+	FU *fu;
+	int bcnt, cur_size;
+	char *fmt;
+	const char *p;
+	int prec;
+
+	/* figure out the data block bb_dump_size needed for each format unit */
+	for (cur_size = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
+		if (fu->bcnt) {
+			cur_size += fu->bcnt * fu->reps;
+			continue;
+		}
+		for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
+			if (*fmt != '%')
+				continue;
+			/*
+			 * skip any special chars -- save precision in
+			 * case it's a %s format.
+			 */
+			while (strchr(index_str + 1, *++fmt))
+				continue;
+			if (*fmt == '.' && isdigit(*++fmt)) {
+				prec = atoi(fmt);
+				while (isdigit(*++fmt))
+					continue;
+			}
+			p = strchr(size_conv_str + 12, *fmt);
+			if (!p) {
+				if (*fmt == 's') {
+					bcnt += prec;
+				} else if (*fmt == '_') {
+					++fmt;
+					if ((*fmt == 'c') || (*fmt == 'p') || (*fmt == 'u')) {
+						bcnt += 1;
+					}
+				}
+			} else {
+				bcnt += size_conv_str[p - (size_conv_str + 12)];
+			}
+		}
+		cur_size += bcnt * fu->reps;
+	}
+	return cur_size;
+}
+
+static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
+{
+	enum { NOTOKAY, USEBCNT, USEPREC } sokay;
+	FU *fu;
+	PR *pr;
+	char *p1, *p2, *p3;
+	char savech, *fmtp;
+	const char *byte_count_str;
+	int nconv, prec = 0;
+
+	for (fu = fs->nextfu; fu; fu = fu->nextfu) {
+		/*
+		 * break each format unit into print units; each
+		 * conversion character gets its own.
+		 */
+		for (nconv = 0, fmtp = fu->fmt; *fmtp; ) {
+			/* NOSTRICT */
+			/* DBU:[dvae@cray.com] zalloc so that forward ptrs start out NULL*/
+			pr = xzalloc(sizeof(PR));
+			if (!fu->nextpr)
+				fu->nextpr = pr;
+
+			/* skip preceding text and up to the next % sign */
+			for (p1 = fmtp; *p1 && *p1 != '%'; ++p1)
+				continue;
+
+			/* only text in the string */
+			if (!*p1) {
+				pr->fmt = fmtp;
+				pr->flags = F_TEXT;
+				break;
+			}
+
+			/*
+			 * get precision for %s -- if have a byte count, don't
+			 * need it.
+			 */
+			if (fu->bcnt) {
+				sokay = USEBCNT;
+				/* skip to conversion character */
+				for (++p1; strchr(index_str, *p1); ++p1)
+					continue;
+			} else {
+				/* skip any special chars, field width */
+				while (strchr(index_str + 1, *++p1))
+					continue;
+				if (*p1 == '.' && isdigit(*++p1)) {
+					sokay = USEPREC;
+					prec = atoi(p1);
+					while (isdigit(*++p1))
+						continue;
+				} else
+					sokay = NOTOKAY;
+			}
+
+			p2 = p1 + 1; /* set end pointer */
+
+			/*
+			 * figure out the byte count for each conversion;
+			 * rewrite the format as necessary, set up blank-
+			 * pbb_dump_adding for end of data.
+			 */
+			if (*p1 == 'c') {
+				pr->flags = F_CHAR;
+ DO_BYTE_COUNT_1:
+				byte_count_str = "\001";
+ DO_BYTE_COUNT:
+				if (fu->bcnt) {
+					do {
+						if (fu->bcnt == *byte_count_str) {
+							break;
+						}
+					} while (*++byte_count_str);
+				}
+				/* Unlike the original, output the remainder of the format string. */
+				if (!*byte_count_str) {
+					bb_error_msg_and_die("bad byte count for conversion character %s", p1);
+				}
+				pr->bcnt = *byte_count_str;
+			} else if (*p1 == 'l') {
+				++p2;
+				++p1;
+ DO_INT_CONV:
+				{
+					const char *e;
+					e = strchr(lcc, *p1);
+					if (!e) {
+						goto DO_BAD_CONV_CHAR;
+					}
+					pr->flags = F_INT;
+					if (e > lcc + 1) {
+						pr->flags = F_UINT;
+					}
+					byte_count_str = "\004\002\001";
+					goto DO_BYTE_COUNT;
+				}
+				/* NOTREACHED */
+			} else if (strchr(lcc, *p1)) {
+				goto DO_INT_CONV;
+			} else if (strchr("eEfgG", *p1)) {
+				pr->flags = F_DBL;
+				byte_count_str = "\010\004";
+				goto DO_BYTE_COUNT;
+			} else if (*p1 == 's') {
+				pr->flags = F_STR;
+				if (sokay == USEBCNT) {
+					pr->bcnt = fu->bcnt;
+				} else if (sokay == USEPREC) {
+					pr->bcnt = prec;
+				} else {   /* NOTOKAY */
+					bb_error_msg_and_die("%%s requires a precision or a byte count");
+				}
+			} else if (*p1 == '_') {
+				++p2;
+				switch (p1[1]) {
+				case 'A':
+					dumper->endfu = fu;
+					fu->flags |= F_IGNORE;
+					/* FALLTHROUGH */
+				case 'a':
+					pr->flags = F_ADDRESS;
+					++p2;
+					if ((p1[2] != 'd') && (p1[2] != 'o') && (p1[2] != 'x')) {
+						goto DO_BAD_CONV_CHAR;
+					}
+					*p1 = p1[2];
+					break;
+				case 'c':
+					pr->flags = F_C;
+					/* *p1 = 'c';   set in conv_c */
+					goto DO_BYTE_COUNT_1;
+				case 'p':
+					pr->flags = F_P;
+					*p1 = 'c';
+					goto DO_BYTE_COUNT_1;
+				case 'u':
+					pr->flags = F_U;
+					/* *p1 = 'c';   set in conv_u */
+					goto DO_BYTE_COUNT_1;
+				default:
+					goto DO_BAD_CONV_CHAR;
+				}
+			} else {
+ DO_BAD_CONV_CHAR:
+				bb_error_msg_and_die("bad conversion character %%%s", p1);
+			}
+
+			/*
+			 * copy to PR format string, set conversion character
+			 * pointer, update original.
+			 */
+			savech = *p2;
+			p1[1] = '\0';
+			pr->fmt = xstrdup(fmtp);
+			*p2 = savech;
+			//Too early! xrealloc can move pr->fmt!
+			//pr->cchar = pr->fmt + (p1 - fmtp);
+
+			/* DBU:[dave@cray.com] w/o this, trailing fmt text, space is lost.
+			 * Skip subsequent text and up to the next % sign and tack the
+			 * additional text onto fmt: eg. if fmt is "%x is a HEX number",
+			 * we lose the " is a HEX number" part of fmt.
+			 */
+			for (p3 = p2; *p3 && *p3 != '%'; p3++)
+				continue;
+			if (p3 > p2) {
+				savech = *p3;
+				*p3 = '\0';
+				pr->fmt = xrealloc(pr->fmt, strlen(pr->fmt) + (p3-p2) + 1);
+				strcat(pr->fmt, p2);
+				*p3 = savech;
+				p2 = p3;
+			}
+
+			pr->cchar = pr->fmt + (p1 - fmtp);
+			fmtp = p2;
+
+			/* only one conversion character if byte count */
+			if (!(pr->flags & F_ADDRESS) && fu->bcnt && nconv++) {
+				bb_error_msg_and_die("byte count with multiple conversion characters");
+			}
+		}
+		/*
+		 * if format unit byte count not specified, figure it out
+		 * so can adjust rep count later.
+		 */
+		if (!fu->bcnt)
+			for (pr = fu->nextpr; pr; pr = pr->nextpr)
+				fu->bcnt += pr->bcnt;
+	}
+	/*
+	 * if the format string interprets any data at all, and it's
+	 * not the same as the blocksize, and its last format unit
+	 * interprets any data at all, and has no iteration count,
+	 * repeat it as necessary.
+	 *
+	 * if rep count is greater than 1, no trailing whitespace
+	 * gets output from the last iteration of the format unit.
+	 */
+	for (fu = fs->nextfu; fu; fu = fu->nextfu) {
+		if (!fu->nextfu
+		 && fs->bcnt < dumper->blocksize
+		 && !(fu->flags & F_SETREP)
+		 && fu->bcnt
+		) {
+			fu->reps += (dumper->blocksize - fs->bcnt) / fu->bcnt;
+		}
+		if (fu->reps > 1 && fu->nextpr) {
+			for (pr = fu->nextpr;; pr = pr->nextpr)
+				if (!pr->nextpr)
+					break;
+			for (p1 = pr->fmt, p2 = NULL; *p1; ++p1)
+				p2 = isspace(*p1) ? p1 : NULL;
+			if (p2)
+				pr->nospace = p2;
+		}
+		if (!fu->nextfu)
+			break;
+	}
+}
+
+static void do_skip(priv_dumper_t *dumper, const char *fname, int statok)
+{
+	struct stat sbuf;
+
+	if (statok) {
+		xfstat(STDIN_FILENO, &sbuf, fname);
+		if (!(S_ISCHR(sbuf.st_mode) || S_ISBLK(sbuf.st_mode) || S_ISFIFO(sbuf.st_mode))
+		 && dumper->pub.dump_skip >= sbuf.st_size
+		) {
+			/* If bb_dump_size valid and pub.dump_skip >= size */
+			dumper->pub.dump_skip -= sbuf.st_size;
+			dumper->address += sbuf.st_size;
+			return;
+		}
+	}
+	if (fseek(stdin, dumper->pub.dump_skip, SEEK_SET)) {
+		bb_simple_perror_msg_and_die(fname);
+	}
+	dumper->address += dumper->pub.dump_skip;
+	dumper->savaddress = dumper->address;
+	dumper->pub.dump_skip = 0;
+}
+
+static NOINLINE int next(priv_dumper_t *dumper)
+{
+	int statok;
+
+	for (;;) {
+		if (*dumper->argv) {
+			dumper->next__done = statok = 1;
+			if (!(freopen(*dumper->argv, "r", stdin))) {
+				bb_simple_perror_msg(*dumper->argv);
+				dumper->exitval = 1;
+				++dumper->argv;
+				continue;
+			}
+		} else {
+			if (dumper->next__done)
+				return 0; /* no next file */
+			dumper->next__done = 1;
+			statok = 0;
+		}
+		if (dumper->pub.dump_skip)
+			do_skip(dumper, statok ? *dumper->argv : "stdin", statok);
+		if (*dumper->argv)
+			++dumper->argv;
+		if (!dumper->pub.dump_skip)
+			return 1;
+	}
+	/* NOTREACHED */
+}
+
+static unsigned char *get(priv_dumper_t *dumper)
+{
+	int n;
+	int need, nread;
+	int blocksize = dumper->blocksize;
+
+	if (!dumper->get__curp) {
+		dumper->address = (off_t)0; /*DBU:[dave@cray.com] initialize,initialize..*/
+		dumper->get__curp = xmalloc(blocksize);
+		dumper->get__savp = xzalloc(blocksize); /* need to be initialized */
+	} else {
+		unsigned char *tmp = dumper->get__curp;
+		dumper->get__curp = dumper->get__savp;
+		dumper->get__savp = tmp;
+		dumper->savaddress += blocksize;
+		dumper->address = dumper->savaddress;
+	}
+	need = blocksize;
+	nread = 0;
+	while (1) {
+		/*
+		 * if read the right number of bytes, or at EOF for one file,
+		 * and no other files are available, zero-pad the rest of the
+		 * block and set the end flag.
+		 */
+		if (!dumper->pub.dump_length || (dumper->get__ateof && !next(dumper))) {
+			if (need == blocksize) {
+				return NULL;
+			}
+			if (dumper->pub.dump_vflag != ALL && !memcmp(dumper->get__curp, dumper->get__savp, nread)) {
+				if (dumper->pub.dump_vflag != DUP) {
+					puts("*");
+				}
+				return NULL;
+			}
+			memset(dumper->get__curp + nread, 0, need);
+			dumper->eaddress = dumper->address + nread;
+			return dumper->get__curp;
+		}
+		n = fread(dumper->get__curp + nread, sizeof(unsigned char),
+				dumper->pub.dump_length == -1 ? need : MIN(dumper->pub.dump_length, need), stdin);
+		if (!n) {
+			if (ferror(stdin)) {
+				bb_simple_perror_msg(dumper->argv[-1]);
+			}
+			dumper->get__ateof = 1;
+			continue;
+		}
+		dumper->get__ateof = 0;
+		if (dumper->pub.dump_length != -1) {
+			dumper->pub.dump_length -= n;
+		}
+		need -= n;
+		if (!need) {
+			if (dumper->pub.dump_vflag == ALL || dumper->pub.dump_vflag == FIRST
+			 || memcmp(dumper->get__curp, dumper->get__savp, blocksize)
+			) {
+				if (dumper->pub.dump_vflag == DUP || dumper->pub.dump_vflag == FIRST) {
+					dumper->pub.dump_vflag = WAIT;
+				}
+				return dumper->get__curp;
+			}
+			if (dumper->pub.dump_vflag == WAIT) {
+				puts("*");
+			}
+			dumper->pub.dump_vflag = DUP;
+			dumper->savaddress += blocksize;
+			dumper->address = dumper->savaddress;
+			need = blocksize;
+			nread = 0;
+		} else {
+			nread += n;
+		}
+	}
+}
+
+static void bpad(PR *pr)
+{
+	char *p1, *p2;
+
+	/*
+	 * remove all conversion flags; '-' is the only one valid
+	 * with %s, and it's not useful here.
+	 */
+	pr->flags = F_BPAD;
+	*pr->cchar = 's';
+	for (p1 = pr->fmt; *p1 != '%'; ++p1)
+		continue;
+	for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1)
+		if (pr->nospace)
+			pr->nospace--;
+	while ((*p2++ = *p1++) != 0)
+		continue;
+}
+
+static const char conv_str[] ALIGN1 =
+	"\0\\0\0"
+	"\007\\a\0"  /* \a */
+	"\b\\b\0"
+	"\f\\b\0"
+	"\n\\n\0"
+	"\r\\r\0"
+	"\t\\t\0"
+	"\v\\v\0"
+	;
+
+
+static void conv_c(PR *pr, unsigned char *p)
+{
+	const char *str = conv_str;
+	char buf[10];
+
+	do {
+		if (*p == *str) {
+			++str;
+			goto strpr;
+		}
+		str += 4;
+	} while (*str);
+
+	if (isprint_asciionly(*p)) {
+		*pr->cchar = 'c';
+		printf(pr->fmt, *p);
+	} else {
+		sprintf(buf, "%03o", (int) *p);
+		str = buf;
+ strpr:
+		*pr->cchar = 's';
+		printf(pr->fmt, str);
+	}
+}
+
+static void conv_u(PR *pr, unsigned char *p)
+{
+	static const char list[] ALIGN1 =
+		"nul\0soh\0stx\0etx\0eot\0enq\0ack\0bel\0"
+		"bs\0_ht\0_lf\0_vt\0_ff\0_cr\0_so\0_si\0_"
+		"dle\0dcl\0dc2\0dc3\0dc4\0nak\0syn\0etb\0"
+		"can\0em\0_sub\0esc\0fs\0_gs\0_rs\0_us";
+
+	/* od used nl, not lf */
+	if (*p <= 0x1f) {
+		*pr->cchar = 's';
+		printf(pr->fmt, list + (4 * (int)*p));
+	} else if (*p == 0x7f) {
+		*pr->cchar = 's';
+		printf(pr->fmt, "del");
+	} else if (*p < 0x7f) { /* isprint() */
+		*pr->cchar = 'c';
+		printf(pr->fmt, *p);
+	} else {
+		*pr->cchar = 'x';
+		printf(pr->fmt, (int) *p);
+	}
+}
+
+static void display(priv_dumper_t* dumper)
+{
+	FS *fs;
+	FU *fu;
+	PR *pr;
+	int cnt;
+	unsigned char *bp, *savebp;
+	off_t saveaddress;
+	unsigned char savech = '\0';
+
+	while ((bp = get(dumper)) != NULL) {
+		fs = dumper->pub.fshead;
+		savebp = bp;
+		saveaddress = dumper->address;
+		for (; fs; fs = fs->nextfs, bp = savebp, dumper->address = saveaddress) {
+			for (fu = fs->nextfu; fu; fu = fu->nextfu) {
+				if (fu->flags & F_IGNORE) {
+					break;
+				}
+				for (cnt = fu->reps; cnt; --cnt) {
+					for (pr = fu->nextpr; pr; dumper->address += pr->bcnt,
+								bp += pr->bcnt, pr = pr->nextpr) {
+						if (dumper->eaddress && dumper->address >= dumper->eaddress
+						 && !(pr->flags & (F_TEXT | F_BPAD))
+						) {
+							bpad(pr);
+						}
+						if (cnt == 1 && pr->nospace) {
+							savech = *pr->nospace;
+							*pr->nospace = '\0';
+						}
+/*                      PRINT; */
+						switch (pr->flags) {
+						case F_ADDRESS:
+							printf(pr->fmt, (unsigned) dumper->address);
+							break;
+						case F_BPAD:
+							printf(pr->fmt, "");
+							break;
+						case F_C:
+							conv_c(pr, bp);
+							break;
+						case F_CHAR:
+							printf(pr->fmt, *bp);
+							break;
+						case F_DBL: {
+							double dval;
+							float fval;
+
+							switch (pr->bcnt) {
+							case 4:
+								memcpy(&fval, bp, sizeof(fval));
+								printf(pr->fmt, fval);
+								break;
+							case 8:
+								memcpy(&dval, bp, sizeof(dval));
+								printf(pr->fmt, dval);
+								break;
+							}
+							break;
+						}
+						case F_INT: {
+							int ival;
+							short sval;
+
+							switch (pr->bcnt) {
+							case 1:
+								printf(pr->fmt, (int) *bp);
+								break;
+							case 2:
+								memcpy(&sval, bp, sizeof(sval));
+								printf(pr->fmt, (int) sval);
+								break;
+							case 4:
+								memcpy(&ival, bp, sizeof(ival));
+								printf(pr->fmt, ival);
+								break;
+							}
+							break;
+						}
+						case F_P:
+							printf(pr->fmt, isprint_asciionly(*bp) ? *bp : '.');
+							break;
+						case F_STR:
+							printf(pr->fmt, (char *) bp);
+							break;
+						case F_TEXT:
+							printf(pr->fmt);
+							break;
+						case F_U:
+							conv_u(pr, bp);
+							break;
+						case F_UINT: {
+							unsigned ival;
+							unsigned short sval;
+
+							switch (pr->bcnt) {
+							case 1:
+								printf(pr->fmt, (unsigned) *bp);
+								break;
+							case 2:
+								memcpy(&sval, bp, sizeof(sval));
+								printf(pr->fmt, (unsigned) sval);
+								break;
+							case 4:
+								memcpy(&ival, bp, sizeof(ival));
+								printf(pr->fmt, ival);
+								break;
+							}
+							break;
+						}
+						}
+						if (cnt == 1 && pr->nospace) {
+							*pr->nospace = savech;
+						}
+					}
+				}
+			}
+		}
+	}
+	if (dumper->endfu) {
+		/*
+		 * if eaddress not set, error or file size was multiple
+		 * of blocksize, and no partial block ever found.
+		 */
+		if (!dumper->eaddress) {
+			if (!dumper->address) {
+				return;
+			}
+			dumper->eaddress = dumper->address;
+		}
+		for (pr = dumper->endfu->nextpr; pr; pr = pr->nextpr) {
+			switch (pr->flags) {
+			case F_ADDRESS:
+				printf(pr->fmt, (unsigned) dumper->eaddress);
+				break;
+			case F_TEXT:
+				printf(pr->fmt);
+				break;
+			}
+		}
+	}
+}
+
+#define dumper ((priv_dumper_t*)pub_dumper)
+int FAST_FUNC bb_dump_dump(dumper_t *pub_dumper, char **argv)
+{
+	FS *tfs;
+	int blocksize;
+
+	/* figure out the data block bb_dump_size */
+	blocksize = 0;
+	tfs = dumper->pub.fshead;
+	while (tfs) {
+		tfs->bcnt = bb_dump_size(tfs);
+		if (blocksize < tfs->bcnt) {
+			blocksize = tfs->bcnt;
+		}
+		tfs = tfs->nextfs;
+	}
+	dumper->blocksize = blocksize;
+
+	/* rewrite the rules, do syntax checking */
+	for (tfs = dumper->pub.fshead; tfs; tfs = tfs->nextfs) {
+		rewrite(dumper, tfs);
+	}
+
+	dumper->argv = argv;
+	display(dumper);
+
+	return dumper->exitval;
+}
+
+void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
+{
+	const char *p;
+	char *p1;
+	char *p2;
+	FS *tfs;
+	FU *tfu, **nextfupp;
+	const char *savep;
+
+	/* start new linked list of format units */
+	tfs = xzalloc(sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */
+	if (!dumper->pub.fshead) {
+		dumper->pub.fshead = tfs;
+	} else {
+		FS *fslast = dumper->pub.fshead;
+		while (fslast->nextfs)
+			fslast = fslast->nextfs;
+		fslast->nextfs = tfs;
+	}
+	nextfupp = &tfs->nextfu;
+
+	/* take the format string and break it up into format units */
+	p = fmt;
+	for (;;) {
+		p = skip_whitespace(p);
+		if (*p == '\0') {
+			break;
+		}
+
+		/* allocate a new format unit and link it in */
+		/* NOSTRICT */
+		/* DBU:[dave@cray.com] zalloc so that forward pointers start out NULL */
+		tfu = xzalloc(sizeof(FU));
+		*nextfupp = tfu;
+		nextfupp = &tfu->nextfu;
+		tfu->reps = 1;
+
+		/* if leading digit, repetition count */
+		if (isdigit(*p)) {
+			for (savep = p; isdigit(*p); ++p)
+				continue;
+			if (!isspace(*p) && *p != '/') {
+				bb_error_msg_and_die("bad format {%s}", fmt);
+			}
+			/* may overwrite either white space or slash */
+			tfu->reps = atoi(savep);
+			tfu->flags = F_SETREP;
+			/* skip trailing white space */
+			p = skip_whitespace(++p);
+		}
+
+		/* skip slash and trailing white space */
+		if (*p == '/') {
+			p = skip_whitespace(p + 1);
+		}
+
+		/* byte count */
+		if (isdigit(*p)) {
+// TODO: use bb_strtou
+			savep = p;
+			while (isdigit(*++p))
+				continue;
+			if (!isspace(*p)) {
+				bb_error_msg_and_die("bad format {%s}", fmt);
+			}
+			tfu->bcnt = atoi(savep);
+			/* skip trailing white space */
+			p = skip_whitespace(p + 1);
+		}
+
+		/* format */
+		if (*p != '"') {
+			bb_error_msg_and_die("bad format {%s}", fmt);
+		}
+		for (savep = ++p; *p != '"';) {
+			if (*p++ == '\0') {
+				bb_error_msg_and_die("bad format {%s}", fmt);
+			}
+		}
+		tfu->fmt = xstrndup(savep, p - savep);
+/*      escape(tfu->fmt); */
+
+		p1 = tfu->fmt;
+
+		/* alphabetic escape sequences have to be done in place */
+		for (p2 = p1;; ++p1, ++p2) {
+			if (*p1 == '\0') {
+				*p2 = *p1;
+				break;
+			}
+			if (*p1 == '\\') {
+				const char *cs = conv_str + 4;
+				++p1;
+				*p2 = *p1;
+				do {
+					if (*p1 == cs[2]) {
+						*p2 = cs[0];
+						break;
+					}
+					cs += 4;
+				} while (*cs);
+			}
+		}
+
+		p++;
+	}
+}
+
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */