BSD 4_4 release
[unix-history] / usr / src / usr.bin / hexdump / parse.c
CommitLineData
7fca17f6 1/*
ad787160
C
2 * Copyright (c) 1989, 1993
3 * The Regents of the University of California. All rights reserved.
7fca17f6 4 *
ad787160
C
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.
7fca17f6
KB
32 */
33
34#ifndef lint
ad787160 35static char sccsid[] = "@(#)parse.c 8.1 (Berkeley) 6/6/93";
7fca17f6
KB
36#endif /* not lint */
37
38#include <sys/types.h>
f6c118b0
KB
39
40#include <errno.h>
41#include <fcntl.h>
7fca17f6 42#include <stdio.h>
864336d0 43#include <stdlib.h>
7fca17f6 44#include <ctype.h>
38dde0cd 45#include <string.h>
7fca17f6
KB
46#include "hexdump.h"
47
48FU *endfu; /* format at end-of-data */
49
f6c118b0 50void
7fca17f6
KB
51addfile(name)
52 char *name;
53{
54 register char *p;
55 FILE *fp;
56 int ch;
57 char buf[2048 + 1];
58
f6c118b0
KB
59 if ((fp = fopen(name, "r")) == NULL)
60 err("%s: %s\n", name, strerror(errno));
7fca17f6
KB
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
f6c118b0 76void
864336d0
KB
77add(fmt)
78 char *fmt;
7fca17f6
KB
79{
80 register char *p;
81 static FS **nextfs;
82 FS *tfs;
83 FU *tfu, **nextfu;
f6c118b0 84 char *savep;
7fca17f6
KB
85
86 /* start new linked list of format units */
f6c118b0 87 tfs = emalloc(sizeof(FS));
7fca17f6
KB
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 */
f6c118b0 103 tfu = emalloc(sizeof(FU));
7fca17f6
KB
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 */
7fca17f6
KB
114 tfu->reps = atoi(savep);
115 tfu->flags = F_SETREP;
7fca17f6
KB
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);
7fca17f6 129 tfu->bcnt = atoi(savep);
7fca17f6
KB
130 /* skip trailing white space */
131 for (++p; isspace(*p); ++p);
132 }
133
134 /* format */
135 if (*p != '"')
136 badfmt(fmt);
864336d0
KB
137 for (savep = ++p; *p != '"';)
138 if (*p++ == 0)
139 badfmt(fmt);
140 if (!(tfu->fmt = malloc(p - savep + 1)))
7fca17f6 141 nomem();
864336d0
KB
142 (void) strncpy(tfu->fmt, savep, p - savep);
143 tfu->fmt[p - savep] = '\0';
7fca17f6 144 escape(tfu->fmt);
864336d0 145 p++;
7fca17f6
KB
146 }
147}
148
149static char *spec = ".#-+ 0123456789";
f6c118b0
KB
150
151int
7fca17f6
KB
152size(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 }
f6c118b0 202 return (cursize);
7fca17f6
KB
203}
204
f6c118b0 205void
7fca17f6
KB
206rewrite(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;
f6c118b0 213 char savech, *fmtp, cs[3];
7fca17f6
KB
214 int nconv, prec;
215
216 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
217 /*
f6c118b0
KB
218 * Break each format unit into print units; each conversion
219 * character gets its own.
7fca17f6
KB
220 */
221 for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) {
f6c118b0 222 pr = emalloc(sizeof(PR));
7fca17f6
KB
223 if (!fu->nextpr)
224 fu->nextpr = pr;
225 else
226 *nextpr = pr;
227
f6c118b0 228 /* Skip preceding text and up to the next % sign. */
7fca17f6
KB
229 for (p1 = fmtp; *p1 && *p1 != '%'; ++p1);
230
f6c118b0 231 /* Only text in the string. */
7fca17f6
KB
232 if (!*p1) {
233 pr->fmt = fmtp;
234 pr->flags = F_TEXT;
235 break;
236 }
237
238 /*
f6c118b0 239 * Get precision for %s -- if have a byte count, don't
7fca17f6
KB
240 * need it.
241 */
242 if (fu->bcnt) {
243 sokay = USEBCNT;
f6c118b0 244 /* Skip to conversion character. */
7fca17f6
KB
245 for (++p1; index(spec, *p1); ++p1);
246 } else {
f6c118b0 247 /* Skip any special chars, field width. */
7fca17f6
KB
248 while (index(spec + 1, *++p1));
249 if (*p1 == '.' && isdigit(*++p1)) {
250 sokay = USEPREC;
251 prec = atoi(p1);
252 while (isdigit(*++p1));
f6c118b0 253 } else
7fca17f6
KB
254 sokay = NOTOKAY;
255 }
256
f6c118b0
KB
257 p2 = p1 + 1; /* Set end pointer. */
258 cs[0] = *p1; /* Set conversion string. */
259 cs[1] = '\0';
7fca17f6
KB
260
261 /*
f6c118b0 262 * Figure out the byte count for each conversion;
7fca17f6
KB
263 * rewrite the format as necessary, set up blank-
264 * padding for end of data.
265 */
f6c118b0 266 switch(cs[0]) {
7fca17f6
KB
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;
f6c118b0 280 goto isint;
7fca17f6
KB
281 case 'o': case 'u': case 'x': case 'X':
282 pr->flags = F_UINT;
f6c118b0
KB
283isint: cs[2] = '\0';
284 cs[1] = cs[0];
285 cs[0] = 'q';
286 switch(fu->bcnt) {
7fca17f6
KB
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':
f6c118b0
KB
340 cs[0] = 'q';
341 cs[1] = p1[2];
342 cs[2] = '\0';
7fca17f6
KB
343 break;
344 default:
345 p1[3] = '\0';
346 badconv(p1);
347 }
348 break;
349 case 'c':
350 pr->flags = F_C;
f6c118b0
KB
351 /* cs[0] = 'c'; set in conv_c */
352 goto isint2;
7fca17f6
KB
353 case 'p':
354 pr->flags = F_P;
f6c118b0
KB
355 cs[0] = 'c';
356 goto isint2;
7fca17f6
KB
357 case 'u':
358 pr->flags = F_U;
f6c118b0
KB
359 /* cs[0] = 'c'; set in conv_u */
360isint2: switch(fu->bcnt) {
7fca17f6
KB
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 /*
f6c118b0 380 * Copy to PR format string, set conversion character
7fca17f6
KB
381 * pointer, update original.
382 */
383 savech = *p2;
f6c118b0
KB
384 p1[0] = '\0';
385 pr->fmt = emalloc(strlen(fmtp) + 2);
386 (void)strcpy(pr->fmt, fmtp);
387 (void)strcat(pr->fmt, cs);
7fca17f6
KB
388 *p2 = savech;
389 pr->cchar = pr->fmt + (p1 - fmtp);
390 fmtp = p2;
391
f6c118b0
KB
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");
7fca17f6
KB
395 }
396 /*
f6c118b0 397 * If format unit byte count not specified, figure it out
7fca17f6
KB
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 /*
f6c118b0 405 * If the format string interprets any data at all, and it's
7fca17f6
KB
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 *
f6c118b0 410 * If, rep count is greater than 1, no trailing whitespace
7fca17f6
KB
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 }
f6c118b0
KB
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
7fca17f6
KB
437}
438
f6c118b0 439void
7fca17f6
KB
440escape(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
f6c118b0 482void
7fca17f6
KB
483badcnt(s)
484 char *s;
485{
f6c118b0 486 err("%s: bad byte count", s);
7fca17f6
KB
487}
488
f6c118b0 489void
7fca17f6
KB
490badsfmt()
491{
f6c118b0 492 err("%%s: requires a precision or a byte count\n");
7fca17f6
KB
493}
494
f6c118b0 495void
7fca17f6
KB
496badfmt(fmt)
497 char *fmt;
498{
f6c118b0 499 err("\"%s\": bad format\n", fmt);
7fca17f6
KB
500}
501
f6c118b0 502void
7fca17f6
KB
503badconv(ch)
504 char *ch;
505{
f6c118b0 506 err("%%%s: bad conversion character\n", ch);
7fca17f6 507}