386BSD 0.1 development
[unix-history] / usr / src / usr.bin / file / apprentice.c
CommitLineData
efd7318d
WJ
1/*
2 * apprentice - make one pass through /etc/magic, learning its secrets.
3 *
4 * Copyright (c) Ian F. Darwin, 1987.
5 * Written by Ian F. Darwin.
6 *
7 * This software is not subject to any license of the American Telephone
8 * and Telegraph Company or of the Regents of the University of California.
9 *
10 * Permission is granted to anyone to use this software for any purpose on
11 * any computer system, and to alter it and redistribute it freely, subject
12 * to the following restrictions:
13 *
14 * 1. The author is not responsible for the consequences of use of this
15 * software, no matter how awful, even if they arise from flaws in it.
16 *
17 * 2. The origin of this software must not be misrepresented, either by
18 * explicit claim or by omission. Since few users ever read sources,
19 * credits must appear in the documentation.
20 *
21 * 3. Altered versions must be plainly marked as such, and must not be
22 * misrepresented as being the original software. Since few users
23 * ever read sources, credits must appear in the documentation.
24 *
25 * 4. This notice may not be removed or altered.
26 */
27
28#include <stdio.h>
29#include <ctype.h>
30#include "file.h"
31
32#ifndef lint
33static char *moduleid =
34 "@(#)$Header: apprentice.c,v 1.8 87/11/06 21:14:34 ian Exp $";
35#endif /* lint */
36
37#define MAXSTR 500
38#define EATAB {while (isascii(*l) && isspace(*l)) ++l;}
39
40extern char *progname;
41extern char *magicfile;
42extern int debug; /* option */
43extern int nmagic; /* number of valid magic[]s */
44extern long strtol();
45
46struct magic magic[MAXMAGIS];
47
48char *getstr();
49
50apprentice(fn, check)
51char *fn; /* name of magic file */
52int check; /* non-zero: checking-only run. */
53{
54 FILE *f;
55 char line[MAXSTR+1];
56 int errs = 0;
57
58 f = fopen(fn, "r");
59 if (f==NULL) {
60 (void) fprintf(stderr, "%s: can't read magic file %s\n",
61 progname, fn);
62 if (check)
63 return -1;
64 else
65 exit(1);
66 }
67
68 /* parse it */
69 if (check) /* print silly verbose header for USG compat. */
70 (void) printf("cont\toffset\ttype\topcode\tvalue\tdesc\n");
71
72 while (fgets(line, MAXSTR, f) != NULL) {
73 if (line[0]=='#') /* comment, do not parse */
74 continue;
75 if (strlen(line) <= 1) /* null line, garbage, etc */
76 continue;
77 line[strlen(line)-1] = '\0'; /* delete newline */
78 if (parse(line, &nmagic, check) != 0)
79 ++errs;
80 }
81
82 (void) fclose(f);
83 return errs ? -1 : 0;
84}
85
86/*
87 * parse one line from magic file, put into magic[index++] if valid
88 */
89int
90parse(l, ndx, check)
91char *l;
92int *ndx, check;
93{
94 int i = 0, nd = *ndx;
95 int slen;
96 static int warned = 0;
97 struct magic *m;
98 extern int errno;
99
100 /*
101 * TODO malloc the magic structures (linked list?) so this can't happen
102 */
103 if (nd+1 >= MAXMAGIS){
104 if (warned++ == 0)
105 warning(
106"magic table overflow - increase MAXMAGIS beyond %d in file/apprentice.c\n",
107 MAXMAGIS);
108 return -1;
109 }
110 m = &magic[*ndx];
111
112 if (*l == '>') {
113 ++l; /* step over */
114 m->contflag = 1;
115 } else
116 m->contflag = 0;
117
118 /* get offset, then skip over it */
119 m->offset = atoi(l);
120 while (isascii(*l) && isdigit(*l))
121 ++l;
122 EATAB;
123
124#define NBYTE 4
125#define NSHORT 5
126#define NLONG 4
127#define NSTRING 6
128 /* get type, skip it */
129 if (strncmp(l, "byte", NBYTE)==0) {
130 m->type = BYTE;
131 l += NBYTE;
132 } else if (strncmp(l, "short", NSHORT)==0) {
133 m->type = SHORT;
134 l += NSHORT;
135 } else if (strncmp(l, "long", NLONG)==0) {
136 m->type = LONG;
137 l += NLONG;
138 } else if (strncmp(l, "string", NSTRING)==0) {
139 m->type = STRING;
140 l += NSTRING;
141 } else {
142 errno = 0;
143 warning("type %s invalid", l);
144 return -1;
145 }
146 EATAB;
147
148 if (*l == '>' || *l == '<' || *l == '&' || *l == '=') {
149 m->reln = *l;
150 ++l;
151 } else
152 m->reln = '=';
153 EATAB;
154
155/*
156 * TODO finish this macro and start using it!
157 * #define offsetcheck {if (offset > HOWMANY-1) warning("offset too big"); }
158 */
159 switch(m->type) {
160 /*
161 * Do not remove the casts below. They are vital.
162 * When later compared with the data, the sign extension must
163 * have happened.
164 */
165 case BYTE:
166 m->value.l = (char) strtol(l,&l,0);
167 break;
168 case SHORT:
169 m->value.l = (short) strtol(l,&l,0);
170 break;
171 case LONG:
172 m->value.l = (long) strtol(l,&l,0);
173 break;
174 case STRING:
175 l = getstr(l, m->value.s, sizeof(m->value.s), &slen);
176 m->vallen = slen;
177 break;
178 default:
179 warning("can't happen: m->type=%d\n", m->type);
180 return -1;
181 }
182
183 /*
184 * now get last part - the description
185 */
186 EATAB;
187 while ((m->desc[i++] = *l++) != '\0' && i<MAXDESC)
188 /* NULLBODY */;
189
190 if (check) {
191 mdump(m);
192 }
193 ++(*ndx); /* make room for next */
194 return 0;
195}
196
197/*
198 * Convert a string containing C character escapes. Stop at an unescaped
199 * space or tab.
200 * Copy the converted version to "p", returning its length in *slen.
201 * Return updated scan pointer as function result.
202 */
203char *
204getstr(s, p, plen, slen)
205register char *s;
206register char *p;
207int plen, *slen;
208{
209 char *origs = s, *origp = p;
210 char *pmax = p + plen - 1;
211 register int c;
212 register int val;
213
214 while((c = *s++) != '\0') {
215 if (isspace(c)) break;
216 if (p >= pmax) {
217 fprintf(stderr, "String too long: %s\n", origs);
218 break;
219 }
220 if(c == '\\') {
221 switch(c = *s++) {
222
223 case '\0':
224 goto out;
225
226 default:
227 *p++ = c;
228 break;
229
230 case 'n':
231 *p++ = '\n';
232 break;
233
234 case 'r':
235 *p++ = '\r';
236 break;
237
238 case 'b':
239 *p++ = '\b';
240 break;
241
242 case 't':
243 *p++ = '\t';
244 break;
245
246 case 'f':
247 *p++ = '\f';
248 break;
249
250 case 'v':
251 *p++ = '\v';
252 break;
253
254 /* \ and up to 3 octal digits */
255 case '0':
256 case '1':
257 case '2':
258 case '3':
259 case '4':
260 case '5':
261 case '6':
262 case '7':
263 val = c - '0';
264 c = *s++; /* try for 2 */
265 if(c >= '0' && c <= '7') {
266 val = (val<<3) | (c - '0');
267 c = *s++; /* try for 3 */
268 if(c >= '0' && c <= '7')
269 val = (val<<3) | (c-'0');
270 else
271 --s;
272 }
273 else
274 --s;
275 *p++ = val;
276 break;
277
278 /* \x and up to 3 hex digits */
279 case 'x':
280 val = 'x'; /* Default if no digits */
281 c = hextoint(*s++); /* Get next char */
282 if (c >= 0) {
283 val = c;
284 c = hextoint(*s++);
285 if (c >= 0) {
286 val = (val << 4) + c;
287 c = hextoint(*s++);
288 if (c >= 0) {
289 val = (val << 4) + c;
290 } else
291 --s;
292 } else
293 --s;
294 } else
295 --s;
296 *p++ = val;
297 break;
298 }
299 } else
300 *p++ = c;
301 }
302out:
303 *p = '\0';
304 *slen = p - origp;
305 return(s);
306}
307
308
309/* Single hex char to int; -1 if not a hex char. */
310int
311hextoint(c)
312 char c;
313{
314 if (!isascii(c)) return -1;
315 if (isdigit(c)) return c - '0';
316 if ((c>='a')&(c<='f')) return c + 10 - 'a';
317 if ((c>='A')&(c<='F')) return c + 10 - 'A';
318 return -1;
319}
320
321
322/*
323 * Print a string containing C character escapes.
324 */
325void
326showstr(s)
327register char *s;
328{
329 register char c;
330
331 while((c = *s++) != '\0') {
332 if(c >= 040 && c <= 0176)
333 putchar(c);
334 else {
335 putchar('\\');
336 switch (c) {
337
338 case '\n':
339 putchar('n');
340 break;
341
342 case '\r':
343 putchar('r');
344 break;
345
346 case '\b':
347 putchar('b');
348 break;
349
350 case '\t':
351 putchar('t');
352 break;
353
354 case '\f':
355 putchar('f');
356 break;
357
358 case '\v':
359 putchar('v');
360 break;
361
362 default:
363 printf("%.3o", c & 0377);
364 break;
365 }
366 }
367 }
368 putchar('\t');
369}