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