]> git.ipfire.org Git - thirdparty/util-linux.git/blob - text-utils/hexdump-parse.c
unshare: make sure map_range.next is initialized [coverity scan]
[thirdparty/util-linux.git] / text-utils / hexdump-parse.c
1 /*
2 * Copyright (c) 1989 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 /* 1999-02-22 Arkadiusz Miƛkiewicz <misiek@pld.ORG.PL>
35 * - added Native Language Support
36 */
37
38 #include <sys/types.h>
39 #include <sys/file.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <ctype.h>
43 #include <string.h>
44 #include "hexdump.h"
45 #include "nls.h"
46 #include "xalloc.h"
47 #include "strutils.h"
48 #include "colors.h"
49
50 static void escape(char *p1);
51 static struct list_head *color_fmt(char *cfmt, int bcnt);
52
53 static void __attribute__ ((__noreturn__)) badcnt(const char *s)
54 {
55 errx(EXIT_FAILURE, _("bad byte count for conversion character %s"), s);
56 }
57
58 static void __attribute__ ((__noreturn__)) badsfmt(void)
59 {
60 errx(EXIT_FAILURE, _("%%s requires a precision or a byte count"));
61 }
62
63 static void __attribute__ ((__noreturn__)) badfmt(const char *fmt)
64 {
65 errx(EXIT_FAILURE, _("bad format {%s}"), fmt);
66 }
67
68 static void __attribute__ ((__noreturn__)) badconv(const char *ch)
69 {
70 errx(EXIT_FAILURE, _("bad conversion character %%%s"), ch);
71 }
72
73 #define first_letter(s,f) strchr(f, *(s))
74
75 struct hexdump_fu *endfu; /* format at end-of-data */
76
77 void addfile(char *name, struct hexdump *hex)
78 {
79 char *fmt, *buf = NULL;
80 FILE *fp;
81 size_t n = 0;
82
83 if ((fp = fopen(name, "r")) == NULL)
84 err(EXIT_FAILURE, _("can't read %s"), name);
85
86 while (getline(&buf, &n, fp) != -1) {
87 fmt = buf;
88
89 while (*fmt && isspace(*fmt))
90 ++fmt;
91 if (!*fmt || *fmt == '#')
92 continue;
93
94 add_fmt(fmt, hex);
95 }
96
97 free(buf);
98 fclose(fp);
99 }
100
101 static char *next_number(const char *str, int *num)
102 {
103 char *end = NULL;
104
105 errno = 0;
106 *num = strtol(str, &end, 10);
107
108 if (errno || !end || end == str)
109 return NULL;
110 return end;
111 }
112
113 void add_fmt(const char *fmt, struct hexdump *hex)
114 {
115 const char *p, *savep;
116 struct hexdump_fs *tfs;
117 struct hexdump_fu *tfu;
118
119 /* Start new linked list of format units. */
120 tfs = xcalloc(1, sizeof(struct hexdump_fs));
121 INIT_LIST_HEAD(&tfs->fslist);
122 INIT_LIST_HEAD(&tfs->fulist);
123 list_add_tail(&tfs->fslist, &hex->fshead);
124
125 /* Take the format string and break it up into format units. */
126 p = fmt;
127 while (TRUE) {
128 /* Skip leading white space. */
129 if (!*(p = skip_space(p)))
130 break;
131
132 /* Allocate a new format unit and link it in. */
133 tfu = xcalloc(1, sizeof(struct hexdump_fu));
134 tfu->reps = 1;
135
136 INIT_LIST_HEAD(&tfu->fulist);
137 INIT_LIST_HEAD(&tfu->prlist);
138 list_add_tail(&tfu->fulist, &tfs->fulist);
139
140 /* If leading digit, repetition count. */
141 if (isdigit(*p)) {
142 p = next_number(p, &tfu->reps);
143 if (!p || (!isspace(*p) && *p != '/'))
144 badfmt(fmt);
145
146 /* may overwrite either white space or slash */
147 tfu->flags = F_SETREP;
148 /* skip trailing white space */
149 p = skip_space(++p);
150 }
151
152 /* Skip slash and trailing white space. */
153 if (*p == '/')
154 p = skip_space(++p);
155
156 /* byte count */
157 if (isdigit(*p)) {
158 p = next_number(p, &tfu->bcnt);
159 if (!p || !isspace(*p))
160 badfmt(fmt);
161 /* skip trailing white space */
162 p = skip_space(++p);
163 }
164
165 /* format */
166 if (*p != '"')
167 badfmt(fmt);
168 savep = ++p;
169 while (*p != '"') {
170 if (!*p++)
171 badfmt(fmt);
172 }
173 tfu->fmt = xmalloc(p - savep + 1);
174 xstrncpy(tfu->fmt, savep, p - savep + 1);
175 escape(tfu->fmt);
176 ++p;
177 }
178 }
179
180 static const char *spec = ".#-+ 0123456789";
181
182 int block_size(struct hexdump_fs *fs)
183 {
184 struct hexdump_fu *fu;
185 int bcnt, prec, cursize = 0;
186 char *fmt;
187 struct list_head *p;
188
189 /* figure out the data block size needed for each format unit */
190 list_for_each (p, &fs->fulist) {
191 fu = list_entry(p, struct hexdump_fu, fulist);
192 if (fu->bcnt) {
193 cursize += fu->bcnt * fu->reps;
194 continue;
195 }
196 bcnt = prec = 0;
197 fmt = fu->fmt;
198 while (*fmt) {
199 if (*fmt != '%') {
200 ++fmt;
201 continue;
202 }
203 /*
204 * skip any special chars -- save precision in
205 * case it's a %s format.
206 */
207 while (strchr(spec + 1, *++fmt) && *fmt != '\0')
208 ;
209 if (*fmt == '.' && isdigit(*++fmt))
210 fmt = next_number(fmt, &prec);
211 if (*fmt == '\0')
212 badfmt(fu->fmt);
213 if (first_letter(fmt, "diouxX"))
214 bcnt += 4;
215 else if (first_letter(fmt, "efgEG"))
216 bcnt += 8;
217 else if (*fmt == 's')
218 bcnt += prec;
219 else if (*fmt == 'c' || (*fmt == '_' && first_letter(++fmt, "cpu")))
220 ++bcnt;
221 ++fmt;
222 }
223 cursize += bcnt * fu->reps;
224 }
225 return(cursize);
226 }
227
228 void rewrite_rules(struct hexdump_fs *fs, struct hexdump *hex)
229 {
230 enum { NOTOKAY, USEBCNT, USEPREC } sokay;
231 struct hexdump_pr *pr;
232 struct hexdump_fu *fu;
233 struct list_head *p, *q;
234 char *p1, *p2, *fmtp;
235 char savech, cs[4];
236 int nconv, prec = 0;
237
238 list_for_each (p, &fs->fulist) {
239 fu = list_entry(p, struct hexdump_fu, fulist);
240 /*
241 * Break each format unit into print units; each
242 * conversion character gets its own.
243 */
244 nconv = 0;
245 fmtp = fu->fmt;
246 while (*fmtp) {
247 pr = xcalloc(1, sizeof(struct hexdump_pr));
248 INIT_LIST_HEAD(&pr->prlist);
249 list_add_tail(&pr->prlist, &fu->prlist);
250
251 /* Skip preceding text and up to the next % sign. */
252 p1 = fmtp;
253 while (*p1 && *p1 != '%')
254 ++p1;
255
256 /* Only text in the string. */
257 if (!*p1) {
258 pr->fmt = xstrdup(fmtp);
259 pr->flags = F_TEXT;
260 break;
261 }
262
263 /*
264 * Get precision for %s -- if have a byte count, don't
265 * need it.
266 */
267 if (fu->bcnt) {
268 sokay = USEBCNT;
269 /* skip to conversion character */
270 for (p1++; strchr(spec, *p1); p1++)
271 ;
272 } else {
273 /* skip any special chars, field width */
274 while (strchr(spec + 1, *++p1))
275 ;
276 if (*p1 == '.' && isdigit(*++p1)) {
277 sokay = USEPREC;
278 p1 = next_number(p1, &prec);
279 } else
280 sokay = NOTOKAY;
281 }
282
283 p2 = p1 + 1; /* Set end pointer. */
284 cs[0] = *p1; /* Set conversion string. */
285 cs[1] = 0;
286
287 /*
288 * Figure out the byte count for each conversion;
289 * rewrite the format as necessary, set up blank-
290 * padding for end of data.
291 */
292 if (*cs == 'c') {
293 pr->flags = F_CHAR;
294 switch(fu->bcnt) {
295 case 0:
296 case 1:
297 pr->bcnt = 1;
298 break;
299 default:
300 p1[1] = '\0';
301 badcnt(p1);
302 }
303 } else if (first_letter(cs, "di")) {
304 pr->flags = F_INT;
305 goto isint;
306 } else if (first_letter(cs, "ouxX")) {
307 pr->flags = F_UINT;
308 isint: cs[3] = '\0';
309 cs[2] = cs[0];
310 cs[1] = 'l';
311 cs[0] = 'l';
312 switch(fu->bcnt) {
313 case 0:
314 pr->bcnt = 4;
315 break;
316 case 1:
317 case 2:
318 case 4:
319 case 8:
320 pr->bcnt = fu->bcnt;
321 break;
322 default:
323 p1[1] = '\0';
324 badcnt(p1);
325 }
326 } else if (first_letter(cs, "efgEG")) {
327 pr->flags = F_DBL;
328 switch(fu->bcnt) {
329 case 0:
330 pr->bcnt = 8;
331 break;
332 case 4:
333 case 8:
334 pr->bcnt = fu->bcnt;
335 break;
336 default:
337 p1[1] = '\0';
338 badcnt(p1);
339 }
340 } else if(*cs == 's') {
341 pr->flags = F_STR;
342 switch(sokay) {
343 case NOTOKAY:
344 badsfmt();
345 case USEBCNT:
346 pr->bcnt = fu->bcnt;
347 break;
348 case USEPREC:
349 pr->bcnt = prec;
350 break;
351 }
352 } else if (*cs == '_') {
353 ++p2;
354 switch(p1[1]) {
355 case 'A':
356 endfu = fu;
357 fu->flags |= F_IGNORE;
358 /* fallthrough */
359 case 'a':
360 pr->flags = F_ADDRESS;
361 ++p2;
362 if (first_letter(p1 + 2, "dox")) {
363 cs[0] = 'l';
364 cs[1] = 'l';
365 cs[2] = p1[2];
366 cs[3] = '\0';
367 } else {
368 p1[3] = '\0';
369 badconv(p1);
370 }
371 break;
372 case 'c':
373 pr->flags = F_C;
374 /* cs[0] = 'c'; set in conv_c */
375 goto isint2;
376 case 'p':
377 pr->flags = F_P;
378 cs[0] = 'c';
379 goto isint2;
380 case 'u':
381 pr->flags = F_U;
382 /* cs[0] = 'c'; set in conv_u */
383 isint2: switch(fu->bcnt) {
384 case 0:
385 case 1:
386 pr->bcnt = 1;
387 break;
388 default:
389 p1[2] = '\0';
390 badcnt(p1);
391 }
392 break;
393 default:
394 p1[2] = '\0';
395 badconv(p1);
396 }
397 } else {
398 p1[1] = '\0';
399 badconv(p1);
400 }
401
402 /* Color unit(s) specified */
403 if (*p2 == '_' && p2[1] == 'L') {
404 if (colors_wanted()) {
405 char *a;
406
407 /* "cut out" the color_unit(s) */
408 a = strchr(p2, '[');
409 p2 = strrchr(p2, ']');
410 if (a++ && p2)
411 pr->colorlist = color_fmt(xstrndup(a, p2++ - a), pr->bcnt);
412 else
413 badconv(p2);
414 }
415 /* we don't want colors, quietly skip over them */
416 else {
417 p2 = strrchr(p2, ']');
418 /* be a bit louder if we don't know how to skip over them */
419 if (!p2)
420 badconv("_L");
421 ++p2;
422 }
423 }
424 /*
425 * Copy to hexdump_pr format string, set conversion character
426 * pointer, update original.
427 */
428 savech = *p2;
429 p1[0] = '\0';
430 pr->fmt = xmalloc(strlen(fmtp) + strlen(cs) + 1);
431 strcpy(pr->fmt, fmtp);
432 strcat(pr->fmt, cs);
433 *p2 = savech;
434 pr->cchar = pr->fmt + (p1 - fmtp);
435 fmtp = p2;
436
437 /* Only one conversion character if byte count */
438 if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++)
439 errx(EXIT_FAILURE,
440 _("byte count with multiple conversion characters"));
441 }
442 /*
443 * If format unit byte count not specified, figure it out
444 * so can adjust rep count later.
445 */
446 if (!fu->bcnt)
447 list_for_each(q, &fu->prlist)
448 fu->bcnt
449 += (list_entry(q, struct hexdump_pr, prlist))->bcnt;
450 }
451 /*
452 * If the format string interprets any data at all, and it's
453 * not the same as the blocksize, and its last format unit
454 * interprets any data at all, and has no iteration count,
455 * repeat it as necessary.
456 *
457 * If rep count is greater than 1, no trailing whitespace
458 * gets output from the last iteration of the format unit.
459 */
460 list_for_each (p, &fs->fulist) {
461 fu = list_entry(p, struct hexdump_fu, fulist);
462
463 if (list_entry_is_last(&fu->fulist, &fs->fulist) &&
464 fs->bcnt < hex->blocksize &&
465 !(fu->flags&F_SETREP) && fu->bcnt)
466 fu->reps += (hex->blocksize - fs->bcnt) / fu->bcnt;
467 if (fu->reps > 1 && !list_empty(&fu->prlist)) {
468 pr = list_last_entry(&fu->prlist, struct hexdump_pr, prlist);
469 if (!pr)
470 continue;
471 for (p1 = pr->fmt, p2 = NULL; *p1; ++p1)
472 p2 = isspace(*p1) ? p1 : NULL;
473 if (p2)
474 pr->nospace = p2;
475 }
476 }
477 }
478
479 /* [!]color[:string|:hex_number|:oct_number][@offt|@offt_start-offt_end],... */
480 static struct list_head *color_fmt(char *cfmt, int bcnt)
481 {
482 struct hexdump_clr *hc, *hcnext;
483 struct list_head *ret_head;
484 char *clr, *fmt;
485
486 ret_head = xmalloc(sizeof(struct list_head));
487 hcnext = hc = xcalloc(1, sizeof(struct hexdump_clr));
488
489 INIT_LIST_HEAD(&hc->colorlist);
490 INIT_LIST_HEAD(ret_head);
491 list_add_tail(&hc->colorlist, ret_head);
492
493 fmt = cfmt;
494 while (cfmt && *cfmt) {
495 char *end;
496 /* invert this condition */
497 if (*cfmt == '!') {
498 hcnext->invert = 1;
499 ++cfmt;
500 }
501
502 clr = xstrndup(cfmt, strcspn(cfmt, ":@,"));
503 cfmt += strlen(clr);
504 hcnext->fmt = color_sequence_from_colorname(clr);
505 free(clr);
506
507 if (!hcnext->fmt)
508 return NULL;
509
510 /* only colorize this specific value */
511 if (*cfmt == ':') {
512 ++cfmt;
513 /* a hex or oct value */
514 if (*cfmt == '0') {
515 /* hex */
516 errno = 0;
517 end = NULL;
518 if (cfmt[1] == 'x' || cfmt[1] == 'X')
519 hcnext->val = strtoul(cfmt + 2, &end, 16);
520 else
521 hcnext->val = strtoul(cfmt, &end, 8);
522 if (errno || end == cfmt)
523 badfmt(fmt);
524 cfmt = end;
525 /* a string */
526 } else {
527 off_t fmt_end;
528 char endchar;
529 char *endstr;
530
531 hcnext->val = -1;
532 /* temporarily null-delimit the format, so we can reverse-search
533 * for the start of an offset specifier */
534 fmt_end = strcspn(cfmt, ",");
535 endchar = cfmt[fmt_end];
536 cfmt[fmt_end] = '\0';
537 endstr = strrchr(cfmt, '@');
538
539 if (endstr) {
540 if (endstr[1] != '\0')
541 --endstr;
542 hcnext->str = xstrndup(cfmt, endstr - cfmt + 1);
543 } else
544 hcnext->str = xstrndup(cfmt, fmt_end);
545
546 /* restore the character */
547 cfmt[fmt_end] = endchar;
548 cfmt += strlen(hcnext->str);
549 }
550
551 /* no specific value */
552 } else
553 hcnext->val = -1;
554
555 /* only colorize at this offset */
556 hcnext->range = bcnt;
557 if (cfmt && *cfmt == '@') {
558 errno = 0;
559 hcnext->offt = strtoul(++cfmt, &cfmt, 10);
560 if (errno)
561 badfmt(fmt);
562
563 /* offset range */
564 if (*cfmt == '-') {
565 ++cfmt;
566 errno = 0;
567
568 hcnext->range =
569 strtoul(cfmt, &cfmt, 10) - hcnext->offt + 1;
570 if (errno)
571 badfmt(fmt);
572 /* offset range must be between 0 and format byte count */
573 if (hcnext->range < 0)
574 badcnt("_L");
575 /* the offset extends over several print units, clone
576 * the condition, link it in and adjust the address/offset */
577 while (hcnext->range > bcnt) {
578 hc = xcalloc(1, sizeof(struct hexdump_clr));
579 memcpy(hc, hcnext, sizeof(struct hexdump_clr));
580
581 hc->range = bcnt;
582
583 INIT_LIST_HEAD(&hc->colorlist);
584 list_add_tail(&hc->colorlist, ret_head);
585
586 hcnext->offt += bcnt;
587 hcnext->range -= bcnt;
588 }
589 }
590 /* no specific offset */
591 } else
592 hcnext->offt = (off_t)-1;
593
594 /* check if the string we're looking for is the same length as the range */
595 if (hcnext->str && (int)strlen(hcnext->str) != hcnext->range)
596 badcnt("_L");
597
598 /* link in another condition */
599 if (cfmt && *cfmt == ',') {
600 ++cfmt;
601
602 hcnext = xcalloc(1, sizeof(struct hexdump_clr));
603 INIT_LIST_HEAD(&hcnext->colorlist);
604 list_add_tail(&hcnext->colorlist, ret_head);
605 }
606 }
607 return ret_head;
608 }
609
610 static void escape(char *p1)
611 {
612 char *p2;
613
614 /* alphabetic escape sequences have to be done in place */
615 p2 = p1;
616 while (TRUE) {
617 if (!*p1) {
618 *p2 = *p1;
619 break;
620 }
621 if (*p1 == '\\')
622 switch(*++p1) {
623 case 'a':
624 /* *p2 = '\a'; */
625 *p2 = '\007';
626 break;
627 case 'b':
628 *p2 = '\b';
629 break;
630 case 'f':
631 *p2 = '\f';
632 break;
633 case 'n':
634 *p2 = '\n';
635 break;
636 case 'r':
637 *p2 = '\r';
638 break;
639 case 't':
640 *p2 = '\t';
641 break;
642 case 'v':
643 *p2 = '\v';
644 break;
645 default:
646 *p2 = *p1;
647 break;
648 }
649 ++p1; ++p2;
650 }
651 }