cleanup, get padding and archive creation right
[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 *
f15db449 5 * %sccs.include.redist.c%
7fca17f6
KB
6 */
7
8#ifndef lint
864336d0 9static 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
20FU *endfu; /* format at end-of-data */
21
22addfile(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
49add(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
123static char *spec = ".#-+ 0123456789";
124size(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
177rewrite(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;
270sw1: 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 */
342sw2: 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
416escape(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
458badcnt(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
466badsfmt()
467{
468 (void)fprintf(stderr,
469 "hexdump: %%s requires a precision or a byte count.\n");
470 exit(1);
471}
472
473badfmt(fmt)
474 char *fmt;
475{
476 (void)fprintf(stderr, "hexdump: bad format {%s}\n", fmt);
477 exit(1);
478}
479
480badconv(ch)
481 char *ch;
482{
f2a54e79 483 (void)fprintf(stderr, "hexdump: bad conversion character %%%s.\n", ch);
7fca17f6
KB
484 exit(1);
485}