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