]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/file/apprentice.c
unfinished sblive driver, playback/mixer only for now - not enabled in
[FreeBSD/FreeBSD.git] / usr.bin / file / apprentice.c
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 #ifndef lint
29 static const char rcsid[] =
30   "$FreeBSD$";
31 #endif /* not lint */
32
33 #include <ctype.h>
34 #include <err.h>
35 #include <errno.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include "file.h"
39
40 #define EATAB {while (isascii((unsigned char) *l) && \
41                       isspace((unsigned char) *l))  ++l;}
42 #define LOWCASE(l) (isupper((unsigned char) (l)) ? \
43                         tolower((unsigned char) (l)) : (l))
44
45
46 static int getvalue     __P((struct magic *, char **));
47 static int hextoint     __P((int));
48 static char *getstr     __P((char *, char *, int, int *));
49 static int parse        __P((char *, int *, int));
50 static void eatsize     __P((char **));
51
52 static int maxmagic = 0;
53 static int alloc_incr = 256;
54
55 static int apprentice_1 __P((char *, int));
56
57 int
58 apprentice(fn, check)
59 char *fn;                       /* list of magic files */
60 int check;                      /* non-zero? checking-only run. */
61 {
62         char *p, *mfn;
63         int file_err, errs = -1;
64
65         maxmagic = MAXMAGIS;
66         magic = (struct magic *) calloc(sizeof(struct magic), maxmagic);
67         mfn = malloc(strlen(fn)+1);
68         if (magic == NULL || mfn == NULL) {
69                 warnx("out of memory");
70                 if (check)
71                         return -1;
72                 else
73                         exit(1);
74         }
75         fn = strcpy(mfn, fn);
76   
77         while (fn) {
78                 p = strchr(fn, ':');
79                 if (p)
80                         *p++ = '\0';
81                 file_err = apprentice_1(fn, check);
82                 if (file_err > errs)
83                         errs = file_err;
84                 fn = p;
85         }
86         if (errs == -1)
87                 warnx("couldn't find any magic files");
88         if (!check && errs)
89                 exit(1);
90
91         free(mfn);
92         return errs;
93 }
94
95 static int
96 apprentice_1(fn, check)
97 char *fn;                       /* name of magic file */
98 int check;                      /* non-zero? checking-only run. */
99 {
100         static const char hdr[] =
101                 "cont\toffset\ttype\topcode\tmask\tvalue\tdesc";
102         FILE *f;
103         char line[BUFSIZ+1];
104         int errs = 0;
105
106         f = fopen(fn, "r");
107         if (f==NULL) {
108                 if (errno != ENOENT)
109                         warn("can't read magic file %s", fn);
110                 return -1;
111         }
112
113         /* parse it */
114         if (check)      /* print silly verbose header for USG compat. */
115                 (void) printf("%s\n", hdr);
116
117         for (lineno = 1;fgets(line, BUFSIZ, f) != NULL; lineno++) {
118                 if (line[0]=='#')       /* comment, do not parse */
119                         continue;
120                 if (strlen(line) <= (unsigned)1) /* null line, garbage, etc */
121                         continue;
122                 line[strlen(line)-1] = '\0'; /* delete newline */
123                 if (parse(line, &nmagic, check) != 0)
124                         errs = 1;
125         }
126
127         (void) fclose(f);
128         return errs;
129 }
130
131 /*
132  * extend the sign bit if the comparison is to be signed
133  */
134 uint32
135 signextend(m, v)
136 struct magic *m;
137 uint32 v;
138 {
139         if (!(m->flag & UNSIGNED))
140                 switch(m->type) {
141                 /*
142                  * Do not remove the casts below.  They are
143                  * vital.  When later compared with the data,
144                  * the sign extension must have happened.
145                  */
146                 case BYTE:
147                         v = (char) v;
148                         break;
149                 case SHORT:
150                 case BESHORT:
151                 case LESHORT:
152                         v = (short) v;
153                         break;
154                 case DATE:
155                 case BEDATE:
156                 case LEDATE:
157                 case LONG:
158                 case BELONG:
159                 case LELONG:
160                         v = (int32) v;
161                         break;
162                 case STRING:
163                         break;
164                 default:
165                         warnx("can't happen: m->type=%d in file %s, line %d",
166                                 m->type, magicfile, lineno);
167                         return -1;
168                 }
169         return v;
170 }
171
172 /*
173  * parse one line from magic file, put into magic[index++] if valid
174  */
175 static int
176 parse(l, ndx, check)
177 char *l;
178 int *ndx, check;
179 {
180         int i = 0, nd = *ndx;
181         struct magic *m;
182         char *t, *s;
183
184         if (nd+1 >= maxmagic){
185             maxmagic += alloc_incr;
186             if ((magic = (struct magic *) realloc(magic, 
187                                                   sizeof(struct magic) * 
188                                                   maxmagic)) == NULL) {
189                 warnx("out of memory");
190                 if (check)
191                         return -1;
192                 else
193                         exit(1);
194             }
195             memset(&magic[*ndx], 0, sizeof(struct magic) * alloc_incr);
196             alloc_incr *= 2;
197         }
198         m = &magic[*ndx];
199         m->flag = 0;
200         m->cont_level = 0;
201
202         while (*l == '>') {
203                 ++l;            /* step over */
204                 m->cont_level++;
205         }
206
207         if (m->cont_level != 0 && *l == '(') {
208                 ++l;            /* step over */
209                 m->flag |= INDIR;
210         }
211         if (m->cont_level != 0 && *l == '&') {
212                 ++l;            /* step over */
213                 m->flag |= ADD;
214         }
215
216         /* get offset, then skip over it */
217         m->offset = (int) strtoul(l,&t,0);
218         if (l == t)
219                 warnx("offset %s invalid in file %s, line %d",
220                         l, magicfile, lineno);
221         l = t;
222
223         if (m->flag & INDIR) {
224                 m->in.type = LONG;
225                 m->in.offset = 0;
226                 /*
227                  * read [.lbs][+-]nnnnn)
228                  */
229                 if (*l == '.') {
230                         l++;
231                         switch (LOWCASE(*l)) {
232                         case 'l':
233                                 m->in.type = LONG;
234                                 break;
235                         case 'h':
236                         case 's':
237                                 m->in.type = SHORT;
238                                 break;
239                         case 'c':
240                         case 'b':
241                                 m->in.type = BYTE;
242                                 break;
243                         default:
244                                 warnx(
245                         "indirect offset type %c invalid in file %s, line %d",
246                                         *l, magicfile, lineno);
247                                 break;
248                         }
249                         l++;
250                 }
251                 s = l;
252                 if (*l == '+' || *l == '-') l++;
253                 if (isdigit((unsigned char)*l)) {
254                         m->in.offset = strtoul(l, &t, 0);
255                         if (*s == '-') m->in.offset = - m->in.offset;
256                 }
257                 else
258                         t = l;
259                 if (*t++ != ')')
260                         warnx(
261                         "missing ')' in indirect offset in file %s, line %d",
262                                 magicfile, lineno);
263                 l = t;
264         }
265
266
267         while (isascii((unsigned char)*l) && isdigit((unsigned char)*l))
268                 ++l;
269         EATAB;
270
271 #define NBYTE           4
272 #define NSHORT          5
273 #define NLONG           4
274 #define NSTRING         6
275 #define NDATE           4
276 #define NBESHORT        7
277 #define NBELONG         6
278 #define NBEDATE         6
279 #define NLESHORT        7
280 #define NLELONG         6
281 #define NLEDATE         6
282
283         if (*l == 'u') {
284                 ++l;
285                 m->flag |= UNSIGNED;
286         }
287
288         /* get type, skip it */
289         if (strncmp(l, "byte", NBYTE)==0) {
290                 m->type = BYTE;
291                 l += NBYTE;
292         } else if (strncmp(l, "short", NSHORT)==0) {
293                 m->type = SHORT;
294                 l += NSHORT;
295         } else if (strncmp(l, "long", NLONG)==0) {
296                 m->type = LONG;
297                 l += NLONG;
298         } else if (strncmp(l, "string", NSTRING)==0) {
299                 m->type = STRING;
300                 l += NSTRING;
301         } else if (strncmp(l, "date", NDATE)==0) {
302                 m->type = DATE;
303                 l += NDATE;
304         } else if (strncmp(l, "beshort", NBESHORT)==0) {
305                 m->type = BESHORT;
306                 l += NBESHORT;
307         } else if (strncmp(l, "belong", NBELONG)==0) {
308                 m->type = BELONG;
309                 l += NBELONG;
310         } else if (strncmp(l, "bedate", NBEDATE)==0) {
311                 m->type = BEDATE;
312                 l += NBEDATE;
313         } else if (strncmp(l, "leshort", NLESHORT)==0) {
314                 m->type = LESHORT;
315                 l += NLESHORT;
316         } else if (strncmp(l, "lelong", NLELONG)==0) {
317                 m->type = LELONG;
318                 l += NLELONG;
319         } else if (strncmp(l, "ledate", NLEDATE)==0) {
320                 m->type = LEDATE;
321                 l += NLEDATE;
322         } else {
323                 warnx("type %s invalid in file %s, line %d", l,
324                         magicfile, lineno);
325                 return -1;
326         }
327         /* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */
328         if (*l == '&') {
329                 ++l;
330                 m->mask = signextend(m, strtoul(l, &l, 0));
331                 eatsize(&l);
332         } else
333                 m->mask = ~0L;
334         EATAB;
335
336         switch (*l) {
337         case '>':
338         case '<':
339         /* Old-style anding: "0 byte &0x80 dynamically linked" */
340         case '&':
341         case '^':
342         case '=':
343                 m->reln = *l;
344                 ++l;
345                 break;
346         case '!':
347                 if (m->type != STRING) {
348                         m->reln = *l;
349                         ++l;
350                         break;
351                 }
352                 /* FALL THROUGH */
353         default:
354                 if (*l == 'x' && isascii((unsigned char)l[1]) &&
355                     isspace((unsigned char)l[1])) {
356                         m->reln = *l;
357                         ++l;
358                         goto GetDesc;   /* Bill The Cat */
359                 }
360                 m->reln = '=';
361                 break;
362         }
363         EATAB;
364
365         if (getvalue(m, &l))
366                 return -1;
367         /*
368          * TODO finish this macro and start using it!
369          * #define offsetcheck {if (offset > HOWMANY-1)
370          *      warnx("offset too big in file %s, line %d",
371          *               magicfile, lineno); }
372          */
373
374         /*
375          * now get last part - the description
376          */
377 GetDesc:
378         EATAB;
379         if (l[0] == '\b') {
380                 ++l;
381                 m->nospflag = 1;
382         } else if ((l[0] == '\\') && (l[1] == 'b')) {
383                 ++l;
384                 ++l;
385                 m->nospflag = 1;
386         } else
387                 m->nospflag = 0;
388         while ((m->desc[i++] = *l++) != '\0' && i<MAXDESC)
389                 /* NULLBODY */;
390
391         if (check) {
392                 mdump(m);
393         }
394         ++(*ndx);               /* make room for next */
395         return 0;
396 }
397
398 /*
399  * Read a numeric value from a pointer, into the value union of a magic
400  * pointer, according to the magic type.  Update the string pointer to point
401  * just after the number read.  Return 0 for success, non-zero for failure.
402  */
403 static int
404 getvalue(m, p)
405 struct magic *m;
406 char **p;
407 {
408         int slen;
409
410         if (m->type == STRING) {
411                 *p = getstr(*p, m->value.s, sizeof(m->value.s), &slen);
412                 m->vallen = slen;
413         } else
414                 if (m->reln != 'x') {
415                         m->value.l = signextend(m, strtoul(*p, p, 0));
416                         eatsize(p);
417                 }
418         return 0;
419 }
420
421 /*
422  * Convert a string containing C character escapes.  Stop at an unescaped
423  * space or tab.
424  * Copy the converted version to "p", returning its length in *slen.
425  * Return updated scan pointer as function result.
426  */
427 static char *
428 getstr(s, p, plen, slen)
429 register char   *s;
430 register char   *p;
431 int     plen, *slen;
432 {
433         char    *origs = s, *origp = p;
434         char    *pmax = p + plen - 1;
435         register int    c;
436         register int    val;
437
438         while ((c = *s++) != '\0') {
439                 if (isspace((unsigned char) c))
440                         break;
441                 if (p >= pmax) {
442                         warnx("string too long: %s", origs);
443                         break;
444                 }
445                 if(c == '\\') {
446                         switch(c = *s++) {
447
448                         case '\0':
449                                 goto out;
450
451                         default:
452                                 *p++ = (char) c;
453                                 break;
454
455                         case 'n':
456                                 *p++ = '\n';
457                                 break;
458
459                         case 'r':
460                                 *p++ = '\r';
461                                 break;
462
463                         case 'b':
464                                 *p++ = '\b';
465                                 break;
466
467                         case 't':
468                                 *p++ = '\t';
469                                 break;
470
471                         case 'f':
472                                 *p++ = '\f';
473                                 break;
474
475                         case 'v':
476                                 *p++ = '\v';
477                                 break;
478
479                         /* \ and up to 3 octal digits */
480                         case '0':
481                         case '1':
482                         case '2':
483                         case '3':
484                         case '4':
485                         case '5':
486                         case '6':
487                         case '7':
488                                 val = c - '0';
489                                 c = *s++;  /* try for 2 */
490                                 if(c >= '0' && c <= '7') {
491                                         val = (val<<3) | (c - '0');
492                                         c = *s++;  /* try for 3 */
493                                         if(c >= '0' && c <= '7')
494                                                 val = (val<<3) | (c-'0');
495                                         else
496                                                 --s;
497                                 }
498                                 else
499                                         --s;
500                                 *p++ = (char)val;
501                                 break;
502
503                         /* \x and up to 2 hex digits */
504                         case 'x':
505                                 val = 'x';      /* Default if no digits */
506                                 c = hextoint(*s++);     /* Get next char */
507                                 if (c >= 0) {
508                                         val = c;
509                                         c = hextoint(*s++);
510                                         if (c >= 0)
511                                                 val = (val << 4) + c;
512                                         else
513                                                 --s;
514                                 } else
515                                         --s;
516                                 *p++ = (char)val;
517                                 break;
518                         }
519                 } else
520                         *p++ = (char)c;
521         }
522 out:
523         *p = '\0';
524         *slen = p - origp;
525         return s;
526 }
527
528
529 /* Single hex char to int; -1 if not a hex char. */
530 static int
531 hextoint(c)
532 int c;
533 {
534         if (!isascii((unsigned char) c))        return -1;
535         if (isdigit((unsigned char) c))         return c - '0';
536         if ((c>='a')&&(c<='f')) return c + 10 - 'a';
537         if ((c>='A')&&(c<='F')) return c + 10 - 'A';
538                                 return -1;
539 }
540
541
542 /*
543  * Print a string containing C character escapes.
544  */
545 void
546 showstr(fp, s, len)
547 FILE *fp;
548 const char *s;
549 int len;
550 {
551         register char   c;
552
553         for (;;) {
554                 c = *s++;
555                 if (len == -1) {
556                         if (c == '\0')
557                                 break;
558                 }
559                 else  {
560                         if (len-- == 0)
561                                 break;
562                 }
563                 if(c >= 040 && c <= 0176)       /* TODO isprint && !iscntrl */
564                         (void) fputc(c, fp);
565                 else {
566                         (void) fputc('\\', fp);
567                         switch (c) {
568
569                         case '\n':
570                                 (void) fputc('n', fp);
571                                 break;
572
573                         case '\r':
574                                 (void) fputc('r', fp);
575                                 break;
576
577                         case '\b':
578                                 (void) fputc('b', fp);
579                                 break;
580
581                         case '\t':
582                                 (void) fputc('t', fp);
583                                 break;
584
585                         case '\f':
586                                 (void) fputc('f', fp);
587                                 break;
588
589                         case '\v':
590                                 (void) fputc('v', fp);
591                                 break;
592
593                         default:
594                                 (void) fprintf(fp, "%.3o", c & 0377);
595                                 break;
596                         }
597                 }
598         }
599 }
600
601 /*
602  * eatsize(): Eat the size spec from a number [eg. 10UL]
603  */
604 static void
605 eatsize(p)
606 char **p;
607 {
608         char *l = *p;
609
610         if (LOWCASE(*l) == 'u') 
611                 l++;
612
613         switch (LOWCASE(*l)) {
614         case 'l':    /* long */
615         case 's':    /* short */
616         case 'h':    /* short */
617         case 'b':    /* char/byte */
618         case 'c':    /* char/byte */
619                 l++;
620                 /*FALLTHROUGH*/
621         default:
622                 break;
623         }
624
625         *p = l;
626 }