parse.c revision 1.1.1.2 1 /*
2 * Copyright (c) 1989, 1993
3 * The Regents of the University of California. 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 #ifndef lint
35 static char sccsid[] = "@(#)parse.c 8.1 (Berkeley) 6/6/93";
36 #endif /* not lint */
37
38 #include <sys/types.h>
39
40 #include <errno.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
51 addfile(name)
52 char *name;
53 {
54 register char *p;
55 FILE *fp;
56 int ch;
57 char buf[2048 + 1];
58
59 if ((fp = fopen(name, "r")) == NULL)
60 err("%s: %s\n", name, strerror(errno));
61 while (fgets(buf, sizeof(buf), fp)) {
62 if (!(p = index(buf, '\n'))) {
63 (void)fprintf(stderr, "hexdump: line too long.\n");
64 while ((ch = getchar()) != '\n' && ch != EOF);
65 continue;
66 }
67 *p = '\0';
68 for (p = buf; *p && isspace(*p); ++p);
69 if (!*p || *p == '#')
70 continue;
71 add(p);
72 }
73 (void)fclose(fp);
74 }
75
76 void
77 add(fmt)
78 char *fmt;
79 {
80 register char *p;
81 static FS **nextfs;
82 FS *tfs;
83 FU *tfu, **nextfu;
84 char *savep;
85
86 /* start new linked list of format units */
87 tfs = emalloc(sizeof(FS));
88 if (!fshead)
89 fshead = tfs;
90 else
91 *nextfs = tfs;
92 nextfs = &tfs->nextfs;
93 nextfu = &tfs->nextfu;
94
95 /* take the format string and break it up into format units */
96 for (p = fmt;;) {
97 /* skip leading white space */
98 for (; isspace(*p); ++p);
99 if (!*p)
100 break;
101
102 /* allocate a new format unit and link it in */
103 tfu = emalloc(sizeof(FU));
104 *nextfu = tfu;
105 nextfu = &tfu->nextfu;
106 tfu->reps = 1;
107
108 /* if leading digit, repetition count */
109 if (isdigit(*p)) {
110 for (savep = p; isdigit(*p); ++p);
111 if (!isspace(*p) && *p != '/')
112 badfmt(fmt);
113 /* may overwrite either white space or slash */
114 tfu->reps = atoi(savep);
115 tfu->flags = F_SETREP;
116 /* skip trailing white space */
117 for (++p; isspace(*p); ++p);
118 }
119
120 /* skip slash and trailing white space */
121 if (*p == '/')
122 while (isspace(*++p));
123
124 /* byte count */
125 if (isdigit(*p)) {
126 for (savep = p; isdigit(*p); ++p);
127 if (!isspace(*p))
128 badfmt(fmt);
129 tfu->bcnt = atoi(savep);
130 /* skip trailing white space */
131 for (++p; isspace(*p); ++p);
132 }
133
134 /* format */
135 if (*p != '"')
136 badfmt(fmt);
137 for (savep = ++p; *p != '"';)
138 if (*p++ == 0)
139 badfmt(fmt);
140 if (!(tfu->fmt = malloc(p - savep + 1)))
141 nomem();
142 (void) strncpy(tfu->fmt, savep, p - savep);
143 tfu->fmt[p - savep] = '\0';
144 escape(tfu->fmt);
145 p++;
146 }
147 }
148
149 static char *spec = ".#-+ 0123456789";
150
151 int
152 size(fs)
153 FS *fs;
154 {
155 register FU *fu;
156 register int bcnt, cursize;
157 register char *fmt;
158 int prec;
159
160 /* figure out the data block size needed for each format unit */
161 for (cursize = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
162 if (fu->bcnt) {
163 cursize += fu->bcnt * fu->reps;
164 continue;
165 }
166 for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
167 if (*fmt != '%')
168 continue;
169 /*
170 * skip any special chars -- save precision in
171 * case it's a %s format.
172 */
173 while (index(spec + 1, *++fmt));
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
206 rewrite(fs)
207 FS *fs;
208 {
209 enum { NOTOKAY, USEBCNT, USEPREC } sokay;
210 register PR *pr, **nextpr;
211 register FU *fu;
212 register char *p1, *p2;
213 char savech, *fmtp, cs[3];
214 int nconv, prec;
215
216 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
217 /*
218 * Break each format unit into print units; each conversion
219 * character gets its own.
220 */
221 for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) {
222 pr = emalloc(sizeof(PR));
223 if (!fu->nextpr)
224 fu->nextpr = pr;
225 else
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 for (++p1; index(spec, *p1); ++p1);
246 } else {
247 /* Skip any special chars, field width. */
248 while (index(spec + 1, *++p1));
249 if (*p1 == '.' && isdigit(*++p1)) {
250 sokay = USEPREC;
251 prec = atoi(p1);
252 while (isdigit(*++p1));
253 } else
254 sokay = NOTOKAY;
255 }
256
257 p2 = p1 + 1; /* Set end pointer. */
258 cs[0] = *p1; /* Set conversion string. */
259 cs[1] = '\0';
260
261 /*
262 * Figure out the byte count for each conversion;
263 * rewrite the format as necessary, set up blank-
264 * padding for end of data.
265 */
266 switch(cs[0]) {
267 case 'c':
268 pr->flags = F_CHAR;
269 switch(fu->bcnt) {
270 case 0: case 1:
271 pr->bcnt = 1;
272 break;
273 default:
274 p1[1] = '\0';
275 badcnt(p1);
276 }
277 break;
278 case 'd': case 'i':
279 pr->flags = F_INT;
280 goto isint;
281 case 'o': case 'u': case 'x': case 'X':
282 pr->flags = F_UINT;
283 isint: cs[2] = '\0';
284 cs[1] = cs[0];
285 cs[0] = 'q';
286 switch(fu->bcnt) {
287 case 0: case 4:
288 pr->bcnt = 4;
289 break;
290 case 1:
291 pr->bcnt = 1;
292 break;
293 case 2:
294 pr->bcnt = 2;
295 break;
296 default:
297 p1[1] = '\0';
298 badcnt(p1);
299 }
300 break;
301 case 'e': case 'E': case 'f': case 'g': case 'G':
302 pr->flags = F_DBL;
303 switch(fu->bcnt) {
304 case 0: case 8:
305 pr->bcnt = 8;
306 break;
307 case 4:
308 pr->bcnt = 4;
309 break;
310 default:
311 p1[1] = '\0';
312 badcnt(p1);
313 }
314 break;
315 case 's':
316 pr->flags = F_STR;
317 switch(sokay) {
318 case NOTOKAY:
319 badsfmt();
320 case USEBCNT:
321 pr->bcnt = fu->bcnt;
322 break;
323 case USEPREC:
324 pr->bcnt = prec;
325 break;
326 }
327 break;
328 case '_':
329 ++p2;
330 switch(p1[1]) {
331 case 'A':
332 endfu = fu;
333 fu->flags |= F_IGNORE;
334 /* FALLTHROUGH */
335 case 'a':
336 pr->flags = F_ADDRESS;
337 ++p2;
338 switch(p1[2]) {
339 case 'd': case 'o': case'x':
340 cs[0] = 'q';
341 cs[1] = p1[2];
342 cs[2] = '\0';
343 break;
344 default:
345 p1[3] = '\0';
346 badconv(p1);
347 }
348 break;
349 case 'c':
350 pr->flags = F_C;
351 /* cs[0] = 'c'; set in conv_c */
352 goto isint2;
353 case 'p':
354 pr->flags = F_P;
355 cs[0] = 'c';
356 goto isint2;
357 case 'u':
358 pr->flags = F_U;
359 /* cs[0] = 'c'; set in conv_u */
360 isint2: switch(fu->bcnt) {
361 case 0: case 1:
362 pr->bcnt = 1;
363 break;
364 default:
365 p1[2] = '\0';
366 badcnt(p1);
367 }
368 break;
369 default:
370 p1[2] = '\0';
371 badconv(p1);
372 }
373 break;
374 default:
375 p1[1] = '\0';
376 badconv(p1);
377 }
378
379 /*
380 * Copy to PR format string, set conversion character
381 * pointer, update original.
382 */
383 savech = *p2;
384 p1[0] = '\0';
385 pr->fmt = emalloc(strlen(fmtp) + 2);
386 (void)strcpy(pr->fmt, fmtp);
387 (void)strcat(pr->fmt, cs);
388 *p2 = savech;
389 pr->cchar = pr->fmt + (p1 - fmtp);
390 fmtp = p2;
391
392 /* Only one conversion character if byte count. */
393 if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++)
394 err("byte count with multiple conversion characters");
395 }
396 /*
397 * If format unit byte count not specified, figure it out
398 * so can adjust rep count later.
399 */
400 if (!fu->bcnt)
401 for (pr = fu->nextpr; pr; pr = pr->nextpr)
402 fu->bcnt += pr->bcnt;
403 }
404 /*
405 * If the format string interprets any data at all, and it's
406 * not the same as the blocksize, and its last format unit
407 * interprets any data at all, and has no iteration count,
408 * repeat it as necessary.
409 *
410 * If, rep count is greater than 1, no trailing whitespace
411 * gets output from the last iteration of the format unit.
412 */
413 for (fu = fs->nextfu;; fu = fu->nextfu) {
414 if (!fu->nextfu && fs->bcnt < blocksize &&
415 !(fu->flags&F_SETREP) && fu->bcnt)
416 fu->reps += (blocksize - fs->bcnt) / fu->bcnt;
417 if (fu->reps > 1) {
418 for (pr = fu->nextpr;; pr = pr->nextpr)
419 if (!pr->nextpr)
420 break;
421 for (p1 = pr->fmt, p2 = NULL; *p1; ++p1)
422 p2 = isspace(*p1) ? p1 : NULL;
423 if (p2)
424 pr->nospace = p2;
425 }
426 if (!fu->nextfu)
427 break;
428 }
429 #ifdef DEBUG
430 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
431 (void)printf("fmt:");
432 for (pr = fu->nextpr; pr; pr = pr->nextpr)
433 (void)printf(" {%s}", pr->fmt);
434 (void)printf("\n");
435 }
436 #endif
437 }
438
439 void
440 escape(p1)
441 register char *p1;
442 {
443 register char *p2;
444
445 /* alphabetic escape sequences have to be done in place */
446 for (p2 = p1;; ++p1, ++p2) {
447 if (!*p1) {
448 *p2 = *p1;
449 break;
450 }
451 if (*p1 == '\\')
452 switch(*++p1) {
453 case 'a':
454 /* *p2 = '\a'; */
455 *p2 = '\007';
456 break;
457 case 'b':
458 *p2 = '\b';
459 break;
460 case 'f':
461 *p2 = '\f';
462 break;
463 case 'n':
464 *p2 = '\n';
465 break;
466 case 'r':
467 *p2 = '\r';
468 break;
469 case 't':
470 *p2 = '\t';
471 break;
472 case 'v':
473 *p2 = '\v';
474 break;
475 default:
476 *p2 = *p1;
477 break;
478 }
479 }
480 }
481
482 void
483 badcnt(s)
484 char *s;
485 {
486 err("%s: bad byte count", s);
487 }
488
489 void
490 badsfmt()
491 {
492 err("%%s: requires a precision or a byte count\n");
493 }
494
495 void
496 badfmt(fmt)
497 char *fmt;
498 {
499 err("\"%s\": bad format\n", fmt);
500 }
501
502 void
503 badconv(ch)
504 char *ch;
505 {
506 err("%%%s: bad conversion character\n", ch);
507 }
508