1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1989, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #ifndef lint
33 #if 0
34 static char sccsid[] = "@(#)parse.c 8.1 (Berkeley) 6/6/93";
35 #endif
36 #endif /* not lint */
37 #include <sys/cdefs.h>
38 #include <sys/types.h>
39
40 #include <err.h>
41 #include <fcntl.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <ctype.h>
45 #include <string.h>
46 #include "hexdump.h"
47
48 FU *endfu; /* format at end-of-data */
49
50 void
addfile(const char * name)51 addfile(const char *name)
52 {
53 unsigned char *p;
54 FILE *fp;
55 int ch;
56 char buf[2048 + 1];
57
58 if ((fp = fopen(name, "r")) == NULL)
59 err(1, "%s", name);
60 while (fgets(buf, sizeof(buf), fp)) {
61 if (!(p = strchr(buf, '\n'))) {
62 warnx("line too long");
63 while ((ch = getchar()) != '\n' && ch != EOF);
64 continue;
65 }
66 *p = '\0';
67 for (p = buf; *p && isspace(*p); ++p);
68 if (!*p || *p == '#')
69 continue;
70 add(p);
71 }
72 (void)fclose(fp);
73 }
74
75 void
add(const char * fmt)76 add(const char *fmt)
77 {
78 unsigned const char *p, *savep;
79 static FS **nextfs;
80 FS *tfs;
81 FU *tfu, **nextfu;
82
83 /* start new linked list of format units */
84 if ((tfs = calloc(1, sizeof(FS))) == NULL)
85 err(1, NULL);
86 if (!fshead)
87 fshead = tfs;
88 else
89 *nextfs = tfs;
90 nextfs = &tfs->nextfs;
91 nextfu = &tfs->nextfu;
92
93 /* take the format string and break it up into format units */
94 for (p = fmt;;) {
95 /* skip leading white space */
96 for (; isspace(*p); ++p);
97 if (!*p)
98 break;
99
100 /* allocate a new format unit and link it in */
101 if ((tfu = calloc(1, sizeof(FU))) == NULL)
102 err(1, NULL);
103 *nextfu = tfu;
104 nextfu = &tfu->nextfu;
105 tfu->reps = 1;
106
107 /* if leading digit, repetition count */
108 if (isdigit(*p)) {
109 for (savep = p; isdigit(*p); ++p);
110 if (!isspace(*p) && *p != '/')
111 badfmt(fmt);
112 /* may overwrite either white space or slash */
113 tfu->reps = atoi(savep);
114 tfu->flags = F_SETREP;
115 /* skip trailing white space */
116 for (++p; isspace(*p); ++p);
117 }
118
119 /* skip slash and trailing white space */
120 if (*p == '/')
121 while (isspace(*++p));
122
123 /* byte count */
124 if (isdigit(*p)) {
125 for (savep = p; isdigit(*p); ++p);
126 if (!isspace(*p))
127 badfmt(fmt);
128 tfu->bcnt = atoi(savep);
129 /* skip trailing white space */
130 for (++p; isspace(*p); ++p);
131 }
132
133 /* format */
134 if (*p != '"')
135 badfmt(fmt);
136 for (savep = ++p; *p != '"';)
137 if (*p++ == 0)
138 badfmt(fmt);
139 if (!(tfu->fmt = malloc(p - savep + 1)))
140 err(1, NULL);
141 (void) strlcpy(tfu->fmt, savep, p - savep + 1);
142 escape(tfu->fmt);
143 p++;
144 }
145 }
146
147 static const char *spec = ".#-+ 0123456789";
148
149 int
size(FS * fs)150 size(FS *fs)
151 {
152 FU *fu;
153 int bcnt, cursize;
154 unsigned char *fmt;
155 int prec;
156
157 /* figure out the data block size needed for each format unit */
158 for (cursize = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
159 if (fu->bcnt) {
160 cursize += fu->bcnt * fu->reps;
161 continue;
162 }
163 for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
164 if (*fmt != '%')
165 continue;
166 /*
167 * skip any special chars -- save precision in
168 * case it's a %s format.
169 */
170 while (*++fmt != 0 && strchr(spec + 1, *fmt) != NULL)
171 ;
172 if (*fmt == 0)
173 badnoconv();
174 if (*fmt == '.' && isdigit(*++fmt)) {
175 prec = atoi(fmt);
176 while (isdigit(*++fmt));
177 }
178 switch(*fmt) {
179 case 'c':
180 bcnt += 1;
181 break;
182 case 'd': case 'i': case 'o': case 'u':
183 case 'x': case 'X':
184 bcnt += 4;
185 break;
186 case 'e': case 'E': case 'f': case 'g': case 'G':
187 bcnt += 8;
188 break;
189 case 's':
190 bcnt += prec;
191 break;
192 case '_':
193 switch(*++fmt) {
194 case 'c': case 'p': case 'u':
195 bcnt += 1;
196 break;
197 }
198 }
199 }
200 cursize += bcnt * fu->reps;
201 }
202 return (cursize);
203 }
204
205 void
rewrite(FS * fs)206 rewrite(FS *fs)
207 {
208 enum { NOTOKAY, USEBCNT, USEPREC } sokay;
209 PR *pr, **nextpr;
210 FU *fu;
211 unsigned char *p1, *p2, *fmtp;
212 char savech, cs[3];
213 int nconv, prec;
214
215 prec = 0;
216
217 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
218 /*
219 * Break each format unit into print units; each conversion
220 * character gets its own.
221 */
222 nextpr = &fu->nextpr;
223 for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) {
224 if ((pr = calloc(1, sizeof(PR))) == NULL)
225 err(1, NULL);
226 *nextpr = pr;
227
228 /* Skip preceding text and up to the next % sign. */
229 for (p1 = fmtp; *p1 && *p1 != '%'; ++p1);
230
231 /* Only text in the string. */
232 if (!*p1) {
233 pr->fmt = fmtp;
234 pr->flags = F_TEXT;
235 break;
236 }
237
238 /*
239 * Get precision for %s -- if have a byte count, don't
240 * need it.
241 */
242 if (fu->bcnt) {
243 sokay = USEBCNT;
244 /* Skip to conversion character. */
245 while (*++p1 != 0 && strchr(spec, *p1) != NULL)
246 ;
247 if (*p1 == 0)
248 badnoconv();
249 } else {
250 /* Skip any special chars, field width. */
251 while (*++p1 != 0 && strchr(spec + 1, *p1) != NULL)
252 ;
253 if (*p1 == 0)
254 badnoconv();
255 if (*p1 == '.' && isdigit(*++p1)) {
256 sokay = USEPREC;
257 prec = atoi(p1);
258 while (isdigit(*++p1));
259 } else
260 sokay = NOTOKAY;
261 }
262
263 p2 = *p1 ? p1 + 1 : p1; /* Set end pointer -- make sure
264 * that it's non-NUL/-NULL first
265 * though. */
266 cs[0] = *p1; /* Set conversion string. */
267 cs[1] = '\0';
268
269 /*
270 * Figure out the byte count for each conversion;
271 * rewrite the format as necessary, set up blank-
272 * padding for end of data.
273 */
274 switch(cs[0]) {
275 case 'c':
276 pr->flags = F_CHAR;
277 switch(fu->bcnt) {
278 case 0: case 1:
279 pr->bcnt = 1;
280 break;
281 default:
282 p1[1] = '\0';
283 badcnt(p1);
284 }
285 break;
286 case 'd': case 'i':
287 pr->flags = F_INT;
288 goto isint;
289 case 'o': case 'u': case 'x': case 'X':
290 pr->flags = F_UINT;
291 isint: cs[2] = '\0';
292 cs[1] = cs[0];
293 cs[0] = 'q';
294 switch(fu->bcnt) {
295 case 0: case 4:
296 pr->bcnt = 4;
297 break;
298 case 1:
299 pr->bcnt = 1;
300 break;
301 case 2:
302 pr->bcnt = 2;
303 break;
304 case 8:
305 pr->bcnt = 8;
306 break;
307 default:
308 p1[1] = '\0';
309 badcnt(p1);
310 }
311 break;
312 case 'e': case 'E': case 'f': case 'g': case 'G':
313 pr->flags = F_DBL;
314 switch(fu->bcnt) {
315 case 0: case 8:
316 pr->bcnt = 8;
317 break;
318 case 4:
319 pr->bcnt = 4;
320 break;
321 default:
322 if (fu->bcnt == sizeof(long double)) {
323 cs[2] = '\0';
324 cs[1] = cs[0];
325 cs[0] = 'L';
326 pr->bcnt = sizeof(long double);
327 } else {
328 p1[1] = '\0';
329 badcnt(p1);
330 }
331 }
332 break;
333 case 's':
334 pr->flags = F_STR;
335 switch(sokay) {
336 case NOTOKAY:
337 badsfmt();
338 case USEBCNT:
339 pr->bcnt = fu->bcnt;
340 break;
341 case USEPREC:
342 pr->bcnt = prec;
343 break;
344 }
345 break;
346 case '_':
347 ++p2;
348 switch(p1[1]) {
349 case 'A':
350 endfu = fu;
351 fu->flags |= F_IGNORE;
352 /* FALLTHROUGH */
353 case 'a':
354 pr->flags = F_ADDRESS;
355 ++p2;
356 switch(p1[2]) {
357 case 'd': case 'o': case'x':
358 cs[0] = 'q';
359 cs[1] = p1[2];
360 cs[2] = '\0';
361 break;
362 default:
363 p1[3] = '\0';
364 badconv(p1);
365 }
366 break;
367 case 'c':
368 pr->flags = F_C;
369 /* cs[0] = 'c'; set in conv_c */
370 goto isint2;
371 case 'p':
372 pr->flags = F_P;
373 cs[0] = 'c';
374 goto isint2;
375 case 'u':
376 pr->flags = F_U;
377 /* cs[0] = 'c'; set in conv_u */
378 isint2: switch(fu->bcnt) {
379 case 0: case 1:
380 pr->bcnt = 1;
381 break;
382 default:
383 p1[2] = '\0';
384 badcnt(p1);
385 }
386 break;
387 default:
388 p1[2] = '\0';
389 badconv(p1);
390 }
391 break;
392 default:
393 p1[1] = '\0';
394 badconv(p1);
395 }
396
397 /*
398 * Copy to PR format string, set conversion character
399 * pointer, update original.
400 */
401 savech = *p2;
402 p1[0] = '\0';
403 if (asprintf(&pr->fmt, "%s%s", fmtp, cs) == -1)
404 err(1, NULL);
405 *p2 = savech;
406 pr->cchar = pr->fmt + (p1 - fmtp);
407 fmtp = p2;
408
409 /* Only one conversion character if byte count. */
410 if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++)
411 errx(1, "byte count with multiple conversion characters");
412 }
413 /*
414 * If format unit byte count not specified, figure it out
415 * so can adjust rep count later.
416 */
417 if (!fu->bcnt)
418 for (pr = fu->nextpr; pr; pr = pr->nextpr)
419 fu->bcnt += pr->bcnt;
420 }
421 /*
422 * If the format string interprets any data at all, and it's
423 * not the same as the blocksize, and its last format unit
424 * interprets any data at all, and has no iteration count,
425 * repeat it as necessary.
426 *
427 * If, rep count is greater than 1, no trailing whitespace
428 * gets output from the last iteration of the format unit.
429 */
430 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
431 if (!fu->nextfu && fs->bcnt < blocksize &&
432 !(fu->flags&F_SETREP) && fu->bcnt)
433 fu->reps += (blocksize - fs->bcnt) / fu->bcnt;
434 if (fu->reps > 1) {
435 for (pr = fu->nextpr;; pr = pr->nextpr)
436 if (!pr->nextpr)
437 break;
438 for (p1 = pr->fmt, p2 = NULL; *p1; ++p1)
439 p2 = isspace(*p1) ? p1 : NULL;
440 if (p2)
441 pr->nospace = p2;
442 }
443 }
444 #ifdef DEBUG
445 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
446 (void)printf("fmt:");
447 for (pr = fu->nextpr; pr; pr = pr->nextpr)
448 (void)printf(" {%s}", pr->fmt);
449 (void)printf("\n");
450 }
451 #endif
452 }
453
454 void
escape(char * p1)455 escape(char *p1)
456 {
457 char *p2;
458
459 /* alphabetic escape sequences have to be done in place */
460 for (p2 = p1;; p1++, p2++) {
461 if (*p1 == '\\') {
462 p1++;
463 switch(*p1) {
464 case '\0':
465 *p2 = '\\';
466 *++p2 = '\0';
467 return;
468 case 'a':
469 /* *p2 = '\a'; */
470 *p2 = '\007';
471 break;
472 case 'b':
473 *p2 = '\b';
474 break;
475 case 'f':
476 *p2 = '\f';
477 break;
478 case 'n':
479 *p2 = '\n';
480 break;
481 case 'r':
482 *p2 = '\r';
483 break;
484 case 't':
485 *p2 = '\t';
486 break;
487 case 'v':
488 *p2 = '\v';
489 break;
490 default:
491 *p2 = *p1;
492 break;
493 }
494 } else {
495 *p2 = *p1;
496 if (*p1 == '\0')
497 return;
498 }
499 }
500 }
501
502 void
badcnt(const char * s)503 badcnt(const char *s)
504 {
505 errx(1, "%s: bad byte count", s);
506 }
507
508 void
badsfmt(void)509 badsfmt(void)
510 {
511 errx(1, "%%s: requires a precision or a byte count");
512 }
513
514 void
badfmt(const char * fmt)515 badfmt(const char *fmt)
516 {
517 errx(1, "\"%s\": bad format", fmt);
518 }
519
520 void
badconv(const char * ch)521 badconv(const char *ch)
522 {
523 errx(1, "%%%s: bad conversion character", ch);
524 }
525
526 void
badnoconv(void)527 badnoconv(void)
528 {
529 errx(1, "missing conversion character");
530 }
531