]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - text-utils/hexdump-parse.c
2 * Copyright (c) 1989 The Regents of the University of California.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
34 /* 1999-02-22 Arkadiusz MiĆkiewicz <misiek@pld.ORG.PL>
35 * - added Native Language Support
38 #include <sys/types.h>
50 static void escape(char *p1
);
51 static struct list_head
*color_fmt(char *cfmt
, int bcnt
);
53 static void __attribute__ ((__noreturn__
)) badcnt(const char *s
)
55 errx(EXIT_FAILURE
, _("bad byte count for conversion character %s"), s
);
58 static void __attribute__ ((__noreturn__
)) badsfmt(void)
60 errx(EXIT_FAILURE
, _("%%s requires a precision or a byte count"));
63 static void __attribute__ ((__noreturn__
)) badfmt(const char *fmt
)
65 errx(EXIT_FAILURE
, _("bad format {%s}"), fmt
);
68 static void __attribute__ ((__noreturn__
)) badconv(const char *ch
)
70 errx(EXIT_FAILURE
, _("bad conversion character %%%s"), ch
);
73 #define first_letter(s,f) strchr(f, *(s))
75 struct hexdump_fu
*endfu
; /* format at end-of-data */
77 void addfile(char *name
, struct hexdump
*hex
)
79 char *fmt
, *buf
= NULL
;
83 if ((fp
= fopen(name
, "r")) == NULL
)
84 err(EXIT_FAILURE
, _("can't read %s"), name
);
86 while (getline(&buf
, &n
, fp
) != -1) {
89 while (*fmt
&& isspace(*fmt
))
91 if (!*fmt
|| *fmt
== '#')
101 static char *next_number(const char *str
, int *num
)
106 *num
= strtol(str
, &end
, 10);
108 if (errno
|| !end
|| end
== str
)
113 void add_fmt(const char *fmt
, struct hexdump
*hex
)
115 const char *p
, *savep
;
116 struct hexdump_fs
*tfs
;
117 struct hexdump_fu
*tfu
;
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
);
125 /* Take the format string and break it up into format units. */
128 /* Skip leading white space. */
129 if (!*(p
= skip_space(p
)))
132 /* Allocate a new format unit and link it in. */
133 tfu
= xcalloc(1, sizeof(struct hexdump_fu
));
136 INIT_LIST_HEAD(&tfu
->fulist
);
137 INIT_LIST_HEAD(&tfu
->prlist
);
138 list_add_tail(&tfu
->fulist
, &tfs
->fulist
);
140 /* If leading digit, repetition count. */
142 p
= next_number(p
, &tfu
->reps
);
143 if (!p
|| (!isspace(*p
) && *p
!= '/'))
146 /* may overwrite either white space or slash */
147 tfu
->flags
= F_SETREP
;
148 /* skip trailing white space */
152 /* Skip slash and trailing white space. */
158 p
= next_number(p
, &tfu
->bcnt
);
159 if (!p
|| !isspace(*p
))
161 /* skip trailing white space */
173 tfu
->fmt
= xmalloc(p
- savep
+ 1);
174 xstrncpy(tfu
->fmt
, savep
, p
- savep
+ 1);
180 static const char *spec
= ".#-+ 0123456789";
182 int block_size(struct hexdump_fs
*fs
)
184 struct hexdump_fu
*fu
;
185 int bcnt
, prec
, cursize
= 0;
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
);
193 cursize
+= fu
->bcnt
* fu
->reps
;
204 * skip any special chars -- save precision in
205 * case it's a %s format.
207 while (strchr(spec
+ 1, *++fmt
) && *fmt
!= '\0')
209 if (*fmt
== '.' && isdigit(*++fmt
))
210 fmt
= next_number(fmt
, &prec
);
213 if (first_letter(fmt
, "diouxX"))
215 else if (first_letter(fmt
, "efgEG"))
217 else if (*fmt
== 's')
219 else if (*fmt
== 'c' || (*fmt
== '_' && first_letter(++fmt
, "cpu")))
223 cursize
+= bcnt
* fu
->reps
;
228 void rewrite_rules(struct hexdump_fs
*fs
, struct hexdump
*hex
)
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
;
238 list_for_each (p
, &fs
->fulist
) {
239 fu
= list_entry(p
, struct hexdump_fu
, fulist
);
241 * Break each format unit into print units; each
242 * conversion character gets its own.
247 pr
= xcalloc(1, sizeof(struct hexdump_pr
));
248 INIT_LIST_HEAD(&pr
->prlist
);
249 list_add_tail(&pr
->prlist
, &fu
->prlist
);
251 /* Skip preceding text and up to the next % sign. */
253 while (*p1
&& *p1
!= '%')
256 /* Only text in the string. */
258 pr
->fmt
= xstrdup(fmtp
);
264 * Get precision for %s -- if have a byte count, don't
269 /* skip to conversion character */
270 for (p1
++; strchr(spec
, *p1
); p1
++)
273 /* skip any special chars, field width */
274 while (strchr(spec
+ 1, *++p1
))
276 if (*p1
== '.' && isdigit(*++p1
)) {
278 p1
= next_number(p1
, &prec
);
283 p2
= p1
+ 1; /* Set end pointer. */
284 cs
[0] = *p1
; /* Set conversion string. */
288 * Figure out the byte count for each conversion;
289 * rewrite the format as necessary, set up blank-
290 * padding for end of data.
303 } else if (first_letter(cs
, "di")) {
306 } else if (first_letter(cs
, "ouxX")) {
326 } else if (first_letter(cs
, "efgEG")) {
340 } else if(*cs
== 's') {
352 } else if (*cs
== '_') {
357 fu
->flags
|= F_IGNORE
;
360 pr
->flags
= F_ADDRESS
;
362 if (first_letter(p1
+ 2, "dox")) {
374 /* cs[0] = 'c'; set in conv_c */
382 /* cs[0] = 'c'; set in conv_u */
383 isint2
: switch(fu
->bcnt
) {
402 /* Color unit(s) specified */
403 if (*p2
== '_' && p2
[1] == 'L') {
404 if (colors_wanted()) {
407 /* "cut out" the color_unit(s) */
409 p2
= strrchr(p2
, ']');
411 pr
->colorlist
= color_fmt(xstrndup(a
, p2
++ - a
), pr
->bcnt
);
415 /* we don't want colors, quietly skip over them */
417 p2
= strrchr(p2
, ']');
418 /* be a bit louder if we don't know how to skip over them */
425 * Copy to hexdump_pr format string, set conversion character
426 * pointer, update original.
430 pr
->fmt
= xmalloc(strlen(fmtp
) + strlen(cs
) + 1);
431 strcpy(pr
->fmt
, fmtp
);
434 pr
->cchar
= pr
->fmt
+ (p1
- fmtp
);
437 /* Only one conversion character if byte count */
438 if (!(pr
->flags
&F_ADDRESS
) && fu
->bcnt
&& nconv
++)
440 _("byte count with multiple conversion characters"));
443 * If format unit byte count not specified, figure it out
444 * so can adjust rep count later.
447 list_for_each(q
, &fu
->prlist
)
449 += (list_entry(q
, struct hexdump_pr
, prlist
))->bcnt
;
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.
457 * If rep count is greater than 1, no trailing whitespace
458 * gets output from the last iteration of the format unit.
460 list_for_each (p
, &fs
->fulist
) {
461 fu
= list_entry(p
, struct hexdump_fu
, fulist
);
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
);
471 for (p1
= pr
->fmt
, p2
= NULL
; *p1
; ++p1
)
472 p2
= isspace(*p1
) ? p1
: NULL
;
479 /* [!]color[:string|:hex_number|:oct_number][@offt|@offt_start-offt_end],... */
480 static struct list_head
*color_fmt(char *cfmt
, int bcnt
)
482 struct hexdump_clr
*hc
, *hcnext
;
483 struct list_head
*ret_head
;
486 ret_head
= xmalloc(sizeof(struct list_head
));
487 hcnext
= hc
= xcalloc(1, sizeof(struct hexdump_clr
));
489 INIT_LIST_HEAD(&hc
->colorlist
);
490 INIT_LIST_HEAD(ret_head
);
491 list_add_tail(&hc
->colorlist
, ret_head
);
494 while (cfmt
&& *cfmt
) {
496 /* invert this condition */
502 clr
= xstrndup(cfmt
, strcspn(cfmt
, ":@,"));
504 hcnext
->fmt
= color_sequence_from_colorname(clr
);
510 /* only colorize this specific value */
513 /* a hex or oct value */
518 if (cfmt
[1] == 'x' || cfmt
[1] == 'X')
519 hcnext
->val
= strtoul(cfmt
+ 2, &end
, 16);
521 hcnext
->val
= strtoul(cfmt
, &end
, 8);
522 if (errno
|| end
== cfmt
)
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
, '@');
540 if (endstr
[1] != '\0')
542 hcnext
->str
= xstrndup(cfmt
, endstr
- cfmt
+ 1);
544 hcnext
->str
= xstrndup(cfmt
, fmt_end
);
546 /* restore the character */
547 cfmt
[fmt_end
] = endchar
;
548 cfmt
+= strlen(hcnext
->str
);
551 /* no specific value */
555 /* only colorize at this offset */
556 hcnext
->range
= bcnt
;
557 if (cfmt
&& *cfmt
== '@') {
559 hcnext
->offt
= strtoul(++cfmt
, &cfmt
, 10);
569 strtoul(cfmt
, &cfmt
, 10) - hcnext
->offt
+ 1;
572 /* offset range must be between 0 and format byte count */
573 if (hcnext
->range
< 0)
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
));
583 INIT_LIST_HEAD(&hc
->colorlist
);
584 list_add_tail(&hc
->colorlist
, ret_head
);
586 hcnext
->offt
+= bcnt
;
587 hcnext
->range
-= bcnt
;
590 /* no specific offset */
592 hcnext
->offt
= (off_t
)-1;
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
)
598 /* link in another condition */
599 if (cfmt
&& *cfmt
== ',') {
602 hcnext
= xcalloc(1, sizeof(struct hexdump_clr
));
603 INIT_LIST_HEAD(&hcnext
->colorlist
);
604 list_add_tail(&hcnext
->colorlist
, ret_head
);
610 static void escape(char *p1
)
614 /* alphabetic escape sequences have to be done in place */