Commit | Line | Data |
---|---|---|
7fca17f6 KB |
1 | /* |
2 | * Copyright (c) 1989 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
f15db449 | 5 | * %sccs.include.redist.c% |
7fca17f6 KB |
6 | */ |
7 | ||
8 | #ifndef lint | |
864336d0 | 9 | static char sccsid[] = "@(#)parse.c 5.6 (Berkeley) %G%"; |
7fca17f6 KB |
10 | #endif /* not lint */ |
11 | ||
12 | #include <sys/types.h> | |
13 | #include <sys/file.h> | |
14 | #include <stdio.h> | |
864336d0 | 15 | #include <stdlib.h> |
7fca17f6 | 16 | #include <ctype.h> |
38dde0cd | 17 | #include <string.h> |
7fca17f6 KB |
18 | #include "hexdump.h" |
19 | ||
20 | FU *endfu; /* format at end-of-data */ | |
21 | ||
22 | addfile(name) | |
23 | char *name; | |
24 | { | |
25 | register char *p; | |
26 | FILE *fp; | |
27 | int ch; | |
28 | char buf[2048 + 1]; | |
29 | ||
30 | if (!(fp = fopen(name, "r"))) { | |
31 | (void)fprintf(stderr, "hexdump: can't read %s.\n", name); | |
32 | exit(1); | |
33 | } | |
34 | while (fgets(buf, sizeof(buf), fp)) { | |
35 | if (!(p = index(buf, '\n'))) { | |
36 | (void)fprintf(stderr, "hexdump: line too long.\n"); | |
37 | while ((ch = getchar()) != '\n' && ch != EOF); | |
38 | continue; | |
39 | } | |
40 | *p = '\0'; | |
41 | for (p = buf; *p && isspace(*p); ++p); | |
42 | if (!*p || *p == '#') | |
43 | continue; | |
44 | add(p); | |
45 | } | |
46 | (void)fclose(fp); | |
47 | } | |
48 | ||
864336d0 KB |
49 | add(fmt) |
50 | char *fmt; | |
7fca17f6 KB |
51 | { |
52 | register char *p; | |
53 | static FS **nextfs; | |
54 | FS *tfs; | |
55 | FU *tfu, **nextfu; | |
864336d0 | 56 | char *savep, *emalloc(); |
7fca17f6 KB |
57 | |
58 | /* start new linked list of format units */ | |
59 | /* NOSTRICT */ | |
60 | tfs = (FS *)emalloc(sizeof(FS)); | |
61 | if (!fshead) | |
62 | fshead = tfs; | |
63 | else | |
64 | *nextfs = tfs; | |
65 | nextfs = &tfs->nextfs; | |
66 | nextfu = &tfs->nextfu; | |
67 | ||
68 | /* take the format string and break it up into format units */ | |
69 | for (p = fmt;;) { | |
70 | /* skip leading white space */ | |
71 | for (; isspace(*p); ++p); | |
72 | if (!*p) | |
73 | break; | |
74 | ||
75 | /* allocate a new format unit and link it in */ | |
76 | /* NOSTRICT */ | |
77 | tfu = (FU *)emalloc(sizeof(FU)); | |
78 | *nextfu = tfu; | |
79 | nextfu = &tfu->nextfu; | |
80 | tfu->reps = 1; | |
81 | ||
82 | /* if leading digit, repetition count */ | |
83 | if (isdigit(*p)) { | |
84 | for (savep = p; isdigit(*p); ++p); | |
85 | if (!isspace(*p) && *p != '/') | |
86 | badfmt(fmt); | |
87 | /* may overwrite either white space or slash */ | |
7fca17f6 KB |
88 | tfu->reps = atoi(savep); |
89 | tfu->flags = F_SETREP; | |
7fca17f6 KB |
90 | /* skip trailing white space */ |
91 | for (++p; isspace(*p); ++p); | |
92 | } | |
93 | ||
94 | /* skip slash and trailing white space */ | |
95 | if (*p == '/') | |
96 | while (isspace(*++p)); | |
97 | ||
98 | /* byte count */ | |
99 | if (isdigit(*p)) { | |
100 | for (savep = p; isdigit(*p); ++p); | |
101 | if (!isspace(*p)) | |
102 | badfmt(fmt); | |
7fca17f6 | 103 | tfu->bcnt = atoi(savep); |
7fca17f6 KB |
104 | /* skip trailing white space */ |
105 | for (++p; isspace(*p); ++p); | |
106 | } | |
107 | ||
108 | /* format */ | |
109 | if (*p != '"') | |
110 | badfmt(fmt); | |
864336d0 KB |
111 | for (savep = ++p; *p != '"';) |
112 | if (*p++ == 0) | |
113 | badfmt(fmt); | |
114 | if (!(tfu->fmt = malloc(p - savep + 1))) | |
7fca17f6 | 115 | nomem(); |
864336d0 KB |
116 | (void) strncpy(tfu->fmt, savep, p - savep); |
117 | tfu->fmt[p - savep] = '\0'; | |
7fca17f6 | 118 | escape(tfu->fmt); |
864336d0 | 119 | p++; |
7fca17f6 KB |
120 | } |
121 | } | |
122 | ||
123 | static char *spec = ".#-+ 0123456789"; | |
124 | size(fs) | |
125 | FS *fs; | |
126 | { | |
127 | register FU *fu; | |
128 | register int bcnt, cursize; | |
129 | register char *fmt; | |
130 | int prec; | |
131 | ||
132 | /* figure out the data block size needed for each format unit */ | |
133 | for (cursize = 0, fu = fs->nextfu; fu; fu = fu->nextfu) { | |
134 | if (fu->bcnt) { | |
135 | cursize += fu->bcnt * fu->reps; | |
136 | continue; | |
137 | } | |
138 | for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) { | |
139 | if (*fmt != '%') | |
140 | continue; | |
141 | /* | |
142 | * skip any special chars -- save precision in | |
143 | * case it's a %s format. | |
144 | */ | |
145 | while (index(spec + 1, *++fmt)); | |
146 | if (*fmt == '.' && isdigit(*++fmt)) { | |
147 | prec = atoi(fmt); | |
148 | while (isdigit(*++fmt)); | |
149 | } | |
150 | switch(*fmt) { | |
151 | case 'c': | |
152 | bcnt += 1; | |
153 | break; | |
154 | case 'd': case 'i': case 'o': case 'u': | |
155 | case 'x': case 'X': | |
156 | bcnt += 4; | |
157 | break; | |
158 | case 'e': case 'E': case 'f': case 'g': case 'G': | |
159 | bcnt += 8; | |
160 | break; | |
161 | case 's': | |
162 | bcnt += prec; | |
163 | break; | |
164 | case '_': | |
165 | switch(*++fmt) { | |
166 | case 'c': case 'p': case 'u': | |
167 | bcnt += 1; | |
168 | break; | |
169 | } | |
170 | } | |
171 | } | |
172 | cursize += bcnt * fu->reps; | |
173 | } | |
174 | return(cursize); | |
175 | } | |
176 | ||
177 | rewrite(fs) | |
178 | FS *fs; | |
179 | { | |
180 | enum { NOTOKAY, USEBCNT, USEPREC } sokay; | |
181 | register PR *pr, **nextpr; | |
182 | register FU *fu; | |
183 | register char *p1, *p2; | |
184 | char savech, *fmtp; | |
185 | int nconv, prec; | |
186 | ||
187 | for (fu = fs->nextfu; fu; fu = fu->nextfu) { | |
188 | /* | |
189 | * break each format unit into print units; each | |
190 | * conversion character gets its own. | |
191 | */ | |
192 | for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) { | |
193 | /* NOSTRICT */ | |
194 | pr = (PR *)emalloc(sizeof(PR)); | |
195 | if (!fu->nextpr) | |
196 | fu->nextpr = pr; | |
197 | else | |
198 | *nextpr = pr; | |
199 | ||
200 | /* skip preceding text and up to the next % sign */ | |
201 | for (p1 = fmtp; *p1 && *p1 != '%'; ++p1); | |
202 | ||
203 | /* only text in the string */ | |
204 | if (!*p1) { | |
205 | pr->fmt = fmtp; | |
206 | pr->flags = F_TEXT; | |
207 | break; | |
208 | } | |
209 | ||
210 | /* | |
211 | * get precision for %s -- if have a byte count, don't | |
212 | * need it. | |
213 | */ | |
214 | if (fu->bcnt) { | |
215 | sokay = USEBCNT; | |
216 | /* skip to conversion character */ | |
217 | for (++p1; index(spec, *p1); ++p1); | |
218 | } else { | |
219 | /* skip any special chars, field width */ | |
220 | while (index(spec + 1, *++p1)); | |
221 | if (*p1 == '.' && isdigit(*++p1)) { | |
222 | sokay = USEPREC; | |
223 | prec = atoi(p1); | |
224 | while (isdigit(*++p1)); | |
225 | } | |
226 | else | |
227 | sokay = NOTOKAY; | |
228 | } | |
229 | ||
230 | p2 = p1 + 1; /* set end pointer */ | |
231 | ||
232 | /* | |
233 | * figure out the byte count for each conversion; | |
234 | * rewrite the format as necessary, set up blank- | |
235 | * padding for end of data. | |
236 | */ | |
237 | switch(*p1) { | |
238 | case 'c': | |
239 | pr->flags = F_CHAR; | |
240 | switch(fu->bcnt) { | |
241 | case 0: case 1: | |
242 | pr->bcnt = 1; | |
243 | break; | |
244 | default: | |
245 | p1[1] = '\0'; | |
246 | badcnt(p1); | |
247 | } | |
248 | break; | |
249 | case 'd': case 'i': | |
250 | pr->flags = F_INT; | |
251 | goto sw1; | |
f2a54e79 KB |
252 | case 'l': |
253 | ++p2; | |
254 | switch(p1[1]) { | |
255 | case 'd': case 'i': | |
256 | ++p1; | |
257 | pr->flags = F_INT; | |
258 | goto sw1; | |
259 | case 'o': case 'u': case 'x': case 'X': | |
260 | ++p1; | |
261 | pr->flags = F_UINT; | |
262 | goto sw1; | |
263 | default: | |
264 | p1[2] = '\0'; | |
265 | badconv(p1); | |
266 | } | |
267 | /* NOTREACHED */ | |
7fca17f6 KB |
268 | case 'o': case 'u': case 'x': case 'X': |
269 | pr->flags = F_UINT; | |
270 | sw1: switch(fu->bcnt) { | |
271 | case 0: case 4: | |
272 | pr->bcnt = 4; | |
273 | break; | |
274 | case 1: | |
275 | pr->bcnt = 1; | |
276 | break; | |
277 | case 2: | |
278 | pr->bcnt = 2; | |
279 | break; | |
280 | default: | |
281 | p1[1] = '\0'; | |
282 | badcnt(p1); | |
283 | } | |
284 | break; | |
285 | case 'e': case 'E': case 'f': case 'g': case 'G': | |
286 | pr->flags = F_DBL; | |
287 | switch(fu->bcnt) { | |
288 | case 0: case 8: | |
289 | pr->bcnt = 8; | |
290 | break; | |
291 | case 4: | |
292 | pr->bcnt = 4; | |
293 | break; | |
294 | default: | |
295 | p1[1] = '\0'; | |
296 | badcnt(p1); | |
297 | } | |
298 | break; | |
299 | case 's': | |
300 | pr->flags = F_STR; | |
301 | switch(sokay) { | |
302 | case NOTOKAY: | |
303 | badsfmt(); | |
304 | case USEBCNT: | |
305 | pr->bcnt = fu->bcnt; | |
306 | break; | |
307 | case USEPREC: | |
308 | pr->bcnt = prec; | |
309 | break; | |
310 | } | |
311 | break; | |
312 | case '_': | |
313 | ++p2; | |
314 | switch(p1[1]) { | |
315 | case 'A': | |
316 | endfu = fu; | |
317 | fu->flags |= F_IGNORE; | |
318 | /* FALLTHROUGH */ | |
319 | case 'a': | |
320 | pr->flags = F_ADDRESS; | |
321 | ++p2; | |
322 | switch(p1[2]) { | |
323 | case 'd': case 'o': case'x': | |
324 | *p1 = p1[2]; | |
325 | break; | |
326 | default: | |
327 | p1[3] = '\0'; | |
328 | badconv(p1); | |
329 | } | |
330 | break; | |
331 | case 'c': | |
332 | pr->flags = F_C; | |
333 | /* *p1 = 'c'; set in conv_c */ | |
334 | goto sw2; | |
335 | case 'p': | |
336 | pr->flags = F_P; | |
337 | *p1 = 'c'; | |
338 | goto sw2; | |
339 | case 'u': | |
340 | pr->flags = F_U; | |
341 | /* *p1 = 'c'; set in conv_u */ | |
342 | sw2: switch(fu->bcnt) { | |
343 | case 0: case 1: | |
344 | pr->bcnt = 1; | |
345 | break; | |
346 | default: | |
347 | p1[2] = '\0'; | |
348 | badcnt(p1); | |
349 | } | |
350 | break; | |
351 | default: | |
352 | p1[2] = '\0'; | |
353 | badconv(p1); | |
354 | } | |
355 | break; | |
356 | default: | |
357 | p1[1] = '\0'; | |
358 | badconv(p1); | |
359 | } | |
360 | ||
361 | /* | |
362 | * copy to PR format string, set conversion character | |
363 | * pointer, update original. | |
364 | */ | |
365 | savech = *p2; | |
366 | p1[1] = '\0'; | |
367 | if (!(pr->fmt = strdup(fmtp))) | |
368 | nomem(); | |
369 | *p2 = savech; | |
370 | pr->cchar = pr->fmt + (p1 - fmtp); | |
371 | fmtp = p2; | |
372 | ||
373 | /* only one conversion character if byte count */ | |
374 | if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++) { | |
375 | (void)fprintf(stderr, | |
376 | "hexdump: byte count with multiple conversion characters.\n"); | |
377 | exit(1); | |
378 | } | |
379 | } | |
380 | /* | |
381 | * if format unit byte count not specified, figure it out | |
382 | * so can adjust rep count later. | |
383 | */ | |
384 | if (!fu->bcnt) | |
385 | for (pr = fu->nextpr; pr; pr = pr->nextpr) | |
386 | fu->bcnt += pr->bcnt; | |
387 | } | |
388 | /* | |
389 | * if the format string interprets any data at all, and it's | |
390 | * not the same as the blocksize, and its last format unit | |
391 | * interprets any data at all, and has no iteration count, | |
392 | * repeat it as necessary. | |
393 | * | |
394 | * if, rep count is greater than 1, no trailing whitespace | |
395 | * gets output from the last iteration of the format unit. | |
396 | */ | |
397 | for (fu = fs->nextfu;; fu = fu->nextfu) { | |
398 | if (!fu->nextfu && fs->bcnt < blocksize && | |
399 | !(fu->flags&F_SETREP) && fu->bcnt) | |
400 | fu->reps += (blocksize - fs->bcnt) / fu->bcnt; | |
401 | if (fu->reps > 1) { | |
402 | for (pr = fu->nextpr;; pr = pr->nextpr) | |
403 | if (!pr->nextpr) | |
404 | break; | |
405 | for (p1 = pr->fmt, p2 = NULL; *p1; ++p1) | |
406 | p2 = isspace(*p1) ? p1 : NULL; | |
407 | if (p2) | |
408 | pr->nospace = p2; | |
409 | } | |
410 | if (!fu->nextfu) | |
411 | break; | |
412 | } | |
413 | } | |
414 | ||
415 | ||
416 | escape(p1) | |
417 | register char *p1; | |
418 | { | |
419 | register char *p2; | |
420 | ||
421 | /* alphabetic escape sequences have to be done in place */ | |
422 | for (p2 = p1;; ++p1, ++p2) { | |
423 | if (!*p1) { | |
424 | *p2 = *p1; | |
425 | break; | |
426 | } | |
427 | if (*p1 == '\\') | |
428 | switch(*++p1) { | |
429 | case 'a': | |
430 | /* *p2 = '\a'; */ | |
431 | *p2 = '\007'; | |
432 | break; | |
433 | case 'b': | |
434 | *p2 = '\b'; | |
435 | break; | |
436 | case 'f': | |
437 | *p2 = '\f'; | |
438 | break; | |
439 | case 'n': | |
440 | *p2 = '\n'; | |
441 | break; | |
442 | case 'r': | |
443 | *p2 = '\r'; | |
444 | break; | |
445 | case 't': | |
446 | *p2 = '\t'; | |
447 | break; | |
448 | case 'v': | |
449 | *p2 = '\v'; | |
450 | break; | |
451 | default: | |
452 | *p2 = *p1; | |
453 | break; | |
454 | } | |
455 | } | |
456 | } | |
457 | ||
458 | badcnt(s) | |
459 | char *s; | |
460 | { | |
461 | (void)fprintf(stderr, | |
462 | "hexdump: bad byte count for conversion character %s.\n", s); | |
463 | exit(1); | |
464 | } | |
465 | ||
466 | badsfmt() | |
467 | { | |
468 | (void)fprintf(stderr, | |
469 | "hexdump: %%s requires a precision or a byte count.\n"); | |
470 | exit(1); | |
471 | } | |
472 | ||
473 | badfmt(fmt) | |
474 | char *fmt; | |
475 | { | |
476 | (void)fprintf(stderr, "hexdump: bad format {%s}\n", fmt); | |
477 | exit(1); | |
478 | } | |
479 | ||
480 | badconv(ch) | |
481 | char *ch; | |
482 | { | |
f2a54e79 | 483 | (void)fprintf(stderr, "hexdump: bad conversion character %%%s.\n", ch); |
7fca17f6 KB |
484 | exit(1); |
485 | } |