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