Merge from vendor branch BIND:
[dragonfly.git] / contrib / file-4 / src / apprentice.c
1 /*
2  * Copyright (c) Ian F. Darwin 1986-1995.
3  * Software written by Ian F. Darwin and others;
4  * maintained 1995-present by Christos Zoulas and others.
5  * 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice immediately at the beginning of the file, without modification,
11  *    this list of conditions, and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *  
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 /*
29  * apprentice - make one pass through /etc/magic, learning its secrets.
30  */
31
32 #include "file.h"
33 #include "magic.h"
34 #include <stdlib.h>
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38 #include <string.h>
39 #include <ctype.h>
40 #include <fcntl.h>
41 #include <sys/stat.h>
42 #include <sys/param.h>
43 #ifdef QUICK
44 #include <sys/mman.h>
45 #endif
46
47 #ifndef lint
48 FILE_RCSID("@(#)$Id: apprentice.c,v 1.84 2005/03/25 18:03:18 christos Exp $")
49 #endif  /* lint */
50
51 #define EATAB {while (isascii((unsigned char) *l) && \
52                       isspace((unsigned char) *l))  ++l;}
53 #define LOWCASE(l) (isupper((unsigned char) (l)) ? \
54                         tolower((unsigned char) (l)) : (l))
55 /*
56  * Work around a bug in headers on Digital Unix.
57  * At least confirmed for: OSF1 V4.0 878
58  */
59 #if defined(__osf__) && defined(__DECC)
60 #ifdef MAP_FAILED
61 #undef MAP_FAILED
62 #endif
63 #endif
64
65 #ifndef MAP_FAILED
66 #define MAP_FAILED (void *) -1
67 #endif
68
69 #ifndef MAP_FILE
70 #define MAP_FILE 0
71 #endif
72
73 #ifndef MAXPATHLEN
74 #define MAXPATHLEN      1024
75 #endif
76
77 #define IS_PLAINSTRING(t) ((t) == FILE_STRING || (t) == FILE_PSTRING || \
78     (t) == FILE_BESTRING16 || (t) == FILE_LESTRING16)
79     
80 #define IS_STRING(t) (IS_PLAINSTRING(t) || (t) == FILE_REGEX || \
81     (t) == FILE_SEARCH)
82
83 private int getvalue(struct magic_set *ms, struct magic *, char **);
84 private int hextoint(int);
85 private char *getstr(struct magic_set *, char *, char *, int, int *);
86 private int parse(struct magic_set *, struct magic **, uint32_t *, char *, int);
87 private void eatsize(char **);
88 private int apprentice_1(struct magic_set *, const char *, int, struct mlist *);
89 private int apprentice_file(struct magic_set *, struct magic **, uint32_t *,
90     const char *, int);
91 private void byteswap(struct magic *, uint32_t);
92 private void bs1(struct magic *);
93 private uint16_t swap2(uint16_t);
94 private uint32_t swap4(uint32_t);
95 private char *mkdbname(const char *, char *, size_t, int);
96 private int apprentice_map(struct magic_set *, struct magic **, uint32_t *,
97     const char *);
98 private int apprentice_compile(struct magic_set *, struct magic **, uint32_t *,
99     const char *);
100 private int check_format(struct magic_set *, struct magic *);
101
102 private size_t maxmagic = 0;
103 private size_t magicsize = sizeof(struct magic);
104
105 #ifdef COMPILE_ONLY
106
107 int main(int, char *[]);
108
109 int
110 main(int argc, char *argv[])
111 {
112         int ret;
113         struct magic_set *ms;
114         char *progname;
115
116         if ((progname = strrchr(argv[0], '/')) != NULL)
117                 progname++;
118         else
119                 progname = argv[0];
120
121         if (argc != 2) {
122                 (void)fprintf(stderr, "Usage: %s file\n", progname);
123                 return 1;
124         }
125
126         if ((ms = magic_open(MAGIC_CHECK)) == NULL) {
127                 (void)fprintf(stderr, "%s: %s\n", progname, strerror(errno));
128                 return 1;
129         }
130         ret = magic_compile(ms, argv[1]) == -1 ? 1 : 0;
131         if (ret == 1)
132                 (void)fprintf(stderr, "%s: %s\n", progname, magic_error(ms));
133         magic_close(ms);
134         return ret;
135 }
136 #endif /* COMPILE_ONLY */
137
138
139 /*
140  * Handle one file.
141  */
142 private int
143 apprentice_1(struct magic_set *ms, const char *fn, int action,
144     struct mlist *mlist)
145 {
146         struct magic *magic = NULL;
147         uint32_t nmagic = 0;
148         struct mlist *ml;
149         int rv = -1;
150         int mapped;
151
152         if (magicsize != FILE_MAGICSIZE) {
153                 file_error(ms, 0, "magic element size %lu != %lu",
154                     (unsigned long)sizeof(*magic),
155                     (unsigned long)FILE_MAGICSIZE);
156                 return -1;
157         }
158
159         if (action == FILE_COMPILE) {
160                 rv = apprentice_file(ms, &magic, &nmagic, fn, action);
161                 if (rv != 0)
162                         return -1;
163                 rv = apprentice_compile(ms, &magic, &nmagic, fn);
164                 free(magic);
165                 return rv;
166         }
167 #ifndef COMPILE_ONLY
168         if ((rv = apprentice_map(ms, &magic, &nmagic, fn)) == -1) {
169                 if (ms->flags & MAGIC_CHECK)
170                         file_magwarn(ms, "using regular magic file `%s'", fn);
171                 rv = apprentice_file(ms, &magic, &nmagic, fn, action);
172                 if (rv != 0)
173                         return -1;
174                 mapped = 0;
175         }
176
177         if (rv == -1)
178                 return rv;
179         mapped = rv;
180              
181         if (magic == NULL || nmagic == 0) {
182                 file_delmagic(magic, mapped, nmagic);
183                 return -1;
184         }
185
186         if ((ml = malloc(sizeof(*ml))) == NULL) {
187                 file_delmagic(magic, mapped, nmagic);
188                 file_oomem(ms);
189                 return -1;
190         }
191
192         ml->magic = magic;
193         ml->nmagic = nmagic;
194         ml->mapped = mapped;
195
196         mlist->prev->next = ml;
197         ml->prev = mlist->prev;
198         ml->next = mlist;
199         mlist->prev = ml;
200
201         return 0;
202 #endif /* COMPILE_ONLY */
203 }
204
205 protected void
206 file_delmagic(struct magic *p, int type, size_t entries)
207 {
208         if (p == NULL)
209                 return;
210         switch (type) {
211         case 2:
212                 p--;
213                 (void)munmap((void *)p, sizeof(*p) * (entries + 1));
214                 break;
215         case 1:
216                 p--;
217                 /*FALLTHROUGH*/
218         case 0:
219                 free(p);
220                 break;
221         default:
222                 abort();
223         }
224 }
225
226
227 /* const char *fn: list of magic files */
228 protected struct mlist *
229 file_apprentice(struct magic_set *ms, const char *fn, int action)
230 {
231         char *p, *mfn, *afn = NULL;
232         int file_err, errs = -1;
233         struct mlist *mlist;
234
235         if (fn == NULL)
236                 fn = getenv("MAGIC");
237         if (fn == NULL)
238                 fn = MAGIC;
239
240         if ((fn = mfn = strdup(fn)) == NULL) {
241                 file_oomem(ms);
242                 return NULL;
243         }
244
245         if ((mlist = malloc(sizeof(*mlist))) == NULL) {
246                 free(mfn);
247                 file_oomem(ms);
248                 return NULL;
249         }
250         mlist->next = mlist->prev = mlist;
251
252         while (fn) {
253                 p = strchr(fn, PATHSEP);
254                 if (p)
255                         *p++ = '\0';
256                 if (*fn == '\0')
257                         break;
258                 if (ms->flags & MAGIC_MIME) {
259                         if ((afn = malloc(strlen(fn) + 5 + 1)) == NULL) {
260                                 free(mfn);
261                                 free(mlist);
262                                 file_oomem(ms);
263                                 return NULL;
264                         }
265                         (void)strcpy(afn, fn);
266                         (void)strcat(afn, ".mime");
267                         fn = afn;
268                 }
269                 file_err = apprentice_1(ms, fn, action, mlist);
270                 if (file_err > errs)
271                         errs = file_err;
272                 if (afn) {
273                         free(afn);
274                         afn = NULL;
275                 }
276                 fn = p;
277         }
278         if (errs == -1) {
279                 free(mfn);
280                 free(mlist);
281                 mlist = NULL;
282                 file_error(ms, 0, "could not find any magic files!");
283                 return NULL;
284         }
285         free(mfn);
286         return mlist;
287 }
288
289 /*
290  * parse from a file
291  * const char *fn: name of magic file
292  */
293 private int
294 apprentice_file(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp,
295     const char *fn, int action)
296 {
297         private const char hdr[] =
298                 "cont\toffset\ttype\topcode\tmask\tvalue\tdesc";
299         FILE *f;
300         char line[BUFSIZ+1];
301         int errs = 0;
302
303         f = fopen(ms->file = fn, "r");
304         if (f == NULL) {
305                 if (errno != ENOENT)
306                         file_error(ms, errno, "cannot read magic file `%s'",
307                             fn);
308                 return -1;
309         }
310
311         maxmagic = MAXMAGIS;
312         *magicp = (struct magic *) calloc(maxmagic, sizeof(struct magic));
313         if (*magicp == NULL) {
314                 (void)fclose(f);
315                 file_oomem(ms);
316                 return -1;
317         }
318
319         /* print silly verbose header for USG compat. */
320         if (action == FILE_CHECK)
321                 (void)fprintf(stderr, "%s\n", hdr);
322
323         /* parse it */
324         for (ms->line = 1; fgets(line, BUFSIZ, f) != NULL; ms->line++) {
325                 size_t len;
326                 if (line[0]=='#')       /* comment, do not parse */
327                         continue;
328                 len = strlen(line);
329                 if (len < 2) /* null line, garbage, etc */
330                         continue;
331                 line[len - 1] = '\0'; /* delete newline */
332                 if (parse(ms, magicp, nmagicp, line, action) != 0)
333                         errs = 1;
334         }
335
336         (void)fclose(f);
337         if (errs) {
338                 free(*magicp);
339                 *magicp = NULL;
340                 *nmagicp = 0;
341         }
342         return errs;
343 }
344
345 /*
346  * extend the sign bit if the comparison is to be signed
347  */
348 protected uint32_t
349 file_signextend(struct magic_set *ms, struct magic *m, uint32_t v)
350 {
351         if (!(m->flag & UNSIGNED))
352                 switch(m->type) {
353                 /*
354                  * Do not remove the casts below.  They are
355                  * vital.  When later compared with the data,
356                  * the sign extension must have happened.
357                  */
358                 case FILE_BYTE:
359                         v = (char) v;
360                         break;
361                 case FILE_SHORT:
362                 case FILE_BESHORT:
363                 case FILE_LESHORT:
364                         v = (short) v;
365                         break;
366                 case FILE_DATE:
367                 case FILE_BEDATE:
368                 case FILE_LEDATE:
369                 case FILE_LDATE:
370                 case FILE_BELDATE:
371                 case FILE_LELDATE:
372                 case FILE_LONG:
373                 case FILE_BELONG:
374                 case FILE_LELONG:
375                         v = (int32_t) v;
376                         break;
377                 case FILE_STRING:
378                 case FILE_PSTRING:
379                 case FILE_BESTRING16:
380                 case FILE_LESTRING16:
381                 case FILE_REGEX:
382                 case FILE_SEARCH:
383                         break;
384                 default:
385                         if (ms->flags & MAGIC_CHECK)
386                             file_magwarn(ms, "cannot happen: m->type=%d\n",
387                                     m->type);
388                         return ~0U;
389                 }
390         return v;
391 }
392
393 /*
394  * parse one line from magic file, put into magic[index++] if valid
395  */
396 private int
397 parse(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp, char *l,
398     int action)
399 {
400         int i = 0;
401         struct magic *m;
402         char *t;
403         private const char *fops = FILE_OPS;
404         uint32_t val;
405
406 #define ALLOC_INCR      200
407         if (*nmagicp + 1 >= maxmagic){
408                 maxmagic += ALLOC_INCR;
409                 if ((m = (struct magic *) realloc(*magicp,
410                     sizeof(struct magic) * maxmagic)) == NULL) {
411                         file_oomem(ms);
412                         if (*magicp)
413                                 free(*magicp);
414                         return -1;
415                 }
416                 *magicp = m;
417                 memset(&(*magicp)[*nmagicp], 0, sizeof(struct magic)
418                     * ALLOC_INCR);
419         }
420         m = &(*magicp)[*nmagicp];
421         m->flag = 0;
422         m->cont_level = 0;
423
424         while (*l == '>') {
425                 ++l;            /* step over */
426                 m->cont_level++; 
427         }
428
429         if (m->cont_level != 0 && *l == '&') {
430                 ++l;            /* step over */
431                 m->flag |= OFFADD;
432         }
433         if (m->cont_level != 0 && *l == '(') {
434                 ++l;            /* step over */
435                 m->flag |= INDIR;
436                 if (m->flag & OFFADD)
437                         m->flag = (m->flag & ~OFFADD) | INDIROFFADD;
438         }
439         if (m->cont_level != 0 && *l == '&') {
440                 ++l;            /* step over */
441                 m->flag |= OFFADD;
442         }
443
444         /* get offset, then skip over it */
445         m->offset = (uint32_t)strtoul(l, &t, 0);
446         if (l == t)
447                 if (ms->flags & MAGIC_CHECK)
448                         file_magwarn(ms, "offset `%s' invalid", l);
449         l = t;
450
451         if (m->flag & INDIR) {
452                 m->in_type = FILE_LONG;
453                 m->in_offset = 0;
454                 /*
455                  * read [.lbs][+-]nnnnn)
456                  */
457                 if (*l == '.') {
458                         l++;
459                         switch (*l) {
460                         case 'l':
461                                 m->in_type = FILE_LELONG;
462                                 break;
463                         case 'L':
464                                 m->in_type = FILE_BELONG;
465                                 break;
466                         case 'h':
467                         case 's':
468                                 m->in_type = FILE_LESHORT;
469                                 break;
470                         case 'H':
471                         case 'S':
472                                 m->in_type = FILE_BESHORT;
473                                 break;
474                         case 'c':
475                         case 'b':
476                         case 'C':
477                         case 'B':
478                                 m->in_type = FILE_BYTE;
479                                 break;
480                         default:
481                                 if (ms->flags & MAGIC_CHECK)
482                                         file_magwarn(ms,
483                                             "indirect offset type `%c' invalid",
484                                             *l);
485                                 break;
486                         }
487                         l++;
488                 }
489                 if (*l == '~') {
490                         m->in_op |= FILE_OPINVERSE;
491                         l++;
492                 }
493                 switch (*l) {
494                 case '&':
495                         m->in_op |= FILE_OPAND;
496                         l++;
497                         break;
498                 case '|':
499                         m->in_op |= FILE_OPOR;
500                         l++;
501                         break;
502                 case '^':
503                         m->in_op |= FILE_OPXOR;
504                         l++;
505                         break;
506                 case '+':
507                         m->in_op |= FILE_OPADD;
508                         l++;
509                         break;
510                 case '-':
511                         m->in_op |= FILE_OPMINUS;
512                         l++;
513                         break;
514                 case '*':
515                         m->in_op |= FILE_OPMULTIPLY;
516                         l++;
517                         break;
518                 case '/':
519                         m->in_op |= FILE_OPDIVIDE;
520                         l++;
521                         break;
522                 case '%':
523                         m->in_op |= FILE_OPMODULO;
524                         l++;
525                         break;
526                 }
527                 if (*l == '(') {
528                         m->in_op |= FILE_OPINDIRECT;
529                         l++;
530                 }
531                 if (isdigit((unsigned char)*l) || *l == '-') 
532                         m->in_offset = (int32_t)strtol(l, &t, 0);
533                 else
534                         t = l;
535                 if (*t++ != ')' ||
536                     ((m->in_op & FILE_OPINDIRECT) && *t++ != ')')) 
537                         if (ms->flags & MAGIC_CHECK)
538                                 file_magwarn(ms,
539                                     "missing ')' in indirect offset");
540                 l = t;
541         }
542
543
544         while (isascii((unsigned char)*l) && isdigit((unsigned char)*l))
545                 ++l;
546         EATAB;
547
548 #define NBYTE           4
549 #define NSHORT          5
550 #define NLONG           4
551 #define NSTRING         6
552 #define NDATE           4
553 #define NBESHORT        7
554 #define NBELONG         6
555 #define NBEDATE         6
556 #define NLESHORT        7
557 #define NLELONG         6
558 #define NLEDATE         6
559 #define NPSTRING        7
560 #define NLDATE          5
561 #define NBELDATE        7
562 #define NLELDATE        7
563 #define NREGEX          5
564 #define NBESTRING16     10
565 #define NLESTRING16     10
566 #define NSEARCH         6
567
568         if (*l == 'u') {
569                 ++l;
570                 m->flag |= UNSIGNED;
571         }
572
573         /* get type, skip it */
574         if (strncmp(l, "char", NBYTE)==0) {     /* HP/UX compat */
575                 m->type = FILE_BYTE;
576                 l += NBYTE;
577         } else if (strncmp(l, "byte", NBYTE)==0) {
578                 m->type = FILE_BYTE;
579                 l += NBYTE;
580         } else if (strncmp(l, "short", NSHORT)==0) {
581                 m->type = FILE_SHORT;
582                 l += NSHORT;
583         } else if (strncmp(l, "long", NLONG)==0) {
584                 m->type = FILE_LONG;
585                 l += NLONG;
586         } else if (strncmp(l, "string", NSTRING)==0) {
587                 m->type = FILE_STRING;
588                 l += NSTRING;
589         } else if (strncmp(l, "date", NDATE)==0) {
590                 m->type = FILE_DATE;
591                 l += NDATE;
592         } else if (strncmp(l, "beshort", NBESHORT)==0) {
593                 m->type = FILE_BESHORT;
594                 l += NBESHORT;
595         } else if (strncmp(l, "belong", NBELONG)==0) {
596                 m->type = FILE_BELONG;
597                 l += NBELONG;
598         } else if (strncmp(l, "bedate", NBEDATE)==0) {
599                 m->type = FILE_BEDATE;
600                 l += NBEDATE;
601         } else if (strncmp(l, "leshort", NLESHORT)==0) {
602                 m->type = FILE_LESHORT;
603                 l += NLESHORT;
604         } else if (strncmp(l, "lelong", NLELONG)==0) {
605                 m->type = FILE_LELONG;
606                 l += NLELONG;
607         } else if (strncmp(l, "ledate", NLEDATE)==0) {
608                 m->type = FILE_LEDATE;
609                 l += NLEDATE;
610         } else if (strncmp(l, "pstring", NPSTRING)==0) {
611                 m->type = FILE_PSTRING;
612                 l += NPSTRING;
613         } else if (strncmp(l, "ldate", NLDATE)==0) {
614                 m->type = FILE_LDATE;
615                 l += NLDATE;
616         } else if (strncmp(l, "beldate", NBELDATE)==0) {
617                 m->type = FILE_BELDATE;
618                 l += NBELDATE;
619         } else if (strncmp(l, "leldate", NLELDATE)==0) {
620                 m->type = FILE_LELDATE;
621                 l += NLELDATE;
622         } else if (strncmp(l, "regex", NREGEX)==0) {
623                 m->type = FILE_REGEX;
624                 l += NREGEX;
625         } else if (strncmp(l, "bestring16", NBESTRING16)==0) {
626                 m->type = FILE_BESTRING16;
627                 l += NBESTRING16;
628         } else if (strncmp(l, "lestring16", NLESTRING16)==0) {
629                 m->type = FILE_LESTRING16;
630                 l += NLESTRING16;
631         } else if (strncmp(l, "search", NSEARCH)==0) {
632                 m->type = FILE_SEARCH;
633                 l += NSEARCH;
634         } else {
635                 if (ms->flags & MAGIC_CHECK)
636                         file_magwarn(ms, "type `%s' invalid", l);
637                 return -1;
638         }
639         /* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */
640         /* New and improved: ~ & | ^ + - * / % -- exciting, isn't it? */
641         if (*l == '~') {
642                 if (!IS_STRING(m->type))
643                         m->mask_op |= FILE_OPINVERSE;
644                 ++l;
645         }
646         if ((t = strchr(fops,  *l)) != NULL) {
647                 uint32_t op = (uint32_t)(t - fops);
648                 if (op != FILE_OPDIVIDE || !IS_PLAINSTRING(m->type)) {
649                         ++l;
650                         m->mask_op |= op;
651                         val = (uint32_t)strtoul(l, &l, 0);
652                         m->mask = file_signextend(ms, m, val);
653                         eatsize(&l);
654                 } else {
655                         m->mask = 0L;
656                         while (!isspace((unsigned char)*++l)) {
657                                 switch (*l) {
658                                 case CHAR_IGNORE_LOWERCASE:
659                                         m->mask |= STRING_IGNORE_LOWERCASE;
660                                         break;
661                                 case CHAR_COMPACT_BLANK:
662                                         m->mask |= STRING_COMPACT_BLANK;
663                                         break;
664                                 case CHAR_COMPACT_OPTIONAL_BLANK:
665                                         m->mask |=
666                                             STRING_COMPACT_OPTIONAL_BLANK;
667                                         break;
668                                 default:
669                                         if (ms->flags & MAGIC_CHECK)
670                                                 file_magwarn(ms,
671                                                 "string extension `%c' invalid",
672                                                 *l);
673                                         return -1;
674                                 }
675                         }
676                         ++l;
677                 }
678         }
679         /*
680          * We used to set mask to all 1's here, instead let's just not do
681          * anything if mask = 0 (unless you have a better idea)
682          */
683         EATAB;
684   
685         switch (*l) {
686         case '>':
687         case '<':
688         /* Old-style anding: "0 byte &0x80 dynamically linked" */
689         case '&':
690         case '^':
691         case '=':
692                 m->reln = *l;
693                 ++l;
694                 if (*l == '=') {
695                    /* HP compat: ignore &= etc. */
696                    ++l;
697                 }
698                 break;
699         case '!':
700                 m->reln = *l;
701                 ++l;
702                 break;
703         default:
704                 if (*l == 'x' && ((isascii((unsigned char)l[1]) && 
705                     isspace((unsigned char)l[1])) || !l[1])) {
706                         m->reln = *l;
707                         ++l;
708                         goto GetDesc;   /* Bill The Cat */
709                 }
710                 m->reln = '=';
711                 break;
712         }
713         EATAB;
714   
715         if (getvalue(ms, m, &l))
716                 return -1;
717         /*
718          * TODO finish this macro and start using it!
719          * #define offsetcheck {if (offset > HOWMANY-1) 
720          *      magwarn("offset too big"); }
721          */
722
723         /*
724          * now get last part - the description
725          */
726 GetDesc:
727         EATAB;
728         if (l[0] == '\b') {
729                 ++l;
730                 m->nospflag = 1;
731         } else if ((l[0] == '\\') && (l[1] == 'b')) {
732                 ++l;
733                 ++l;
734                 m->nospflag = 1;
735         } else
736                 m->nospflag = 0;
737         while ((m->desc[i++] = *l++) != '\0' && i < MAXDESC)
738                 /* NULLBODY */;
739
740         if (ms->flags & MAGIC_CHECK) {
741                 if (!check_format(ms, m))
742                         return -1;
743         }
744 #ifndef COMPILE_ONLY
745         if (action == FILE_CHECK) {
746                 file_mdump(m);
747         }
748 #endif
749         ++(*nmagicp);           /* make room for next */
750         return 0;
751 }
752
753 /*
754  * Check that the optional printf format in description matches
755  * the type of the magic.
756  */
757 private int
758 check_format(struct magic_set *ms, struct magic *m)
759 {
760         static const char *formats[] = { FILE_FORMAT_STRING };
761         static const char *names[] = { FILE_FORMAT_NAME };
762         char *ptr;
763
764         for (ptr = m->desc; *ptr; ptr++)
765                 if (*ptr == '%')
766                         break;
767         if (*ptr == '\0') {
768                 /* No format string; ok */
769                 return 1;
770         }
771         if (m->type >= sizeof(formats)/sizeof(formats[0])) {
772                 file_magwarn(ms, "Internal error inconsistency between m->type"
773                     " and format strings");
774                 return 0;
775         }
776         if (formats[m->type] == NULL) {
777                 file_magwarn(ms, "No format string for `%s' with description "
778                     "`%s'", m->desc, names[m->type]);
779                 return 0;
780         }
781         for (; *ptr; ptr++) {
782                 if (*ptr == 'l' || *ptr == 'h') {
783                         /* XXX: we should really fix this one day */
784                         continue;
785                 }
786                 if (islower((unsigned char)*ptr) || *ptr == 'X')
787                         break;
788         }
789         if (*ptr == '\0') {
790                 /* Missing format string; bad */
791                 file_magwarn(ms, "Invalid format `%s' for type `%s'",
792                         m->desc, names[m->type]);
793                 return 0;
794         }
795         if (strchr(formats[m->type], *ptr) == NULL) {
796                 file_magwarn(ms, "Printf format `%c' is not valid for type `%s'"
797                     " in description `%s'",
798                         *ptr, names[m->type], m->desc);
799                 return 0;
800         }
801         return 1;
802 }
803
804 /* 
805  * Read a numeric value from a pointer, into the value union of a magic 
806  * pointer, according to the magic type.  Update the string pointer to point 
807  * just after the number read.  Return 0 for success, non-zero for failure.
808  */
809 private int
810 getvalue(struct magic_set *ms, struct magic *m, char **p)
811 {
812         int slen;
813
814         switch (m->type) {
815         case FILE_BESTRING16:
816         case FILE_LESTRING16:
817         case FILE_STRING:
818         case FILE_PSTRING:
819         case FILE_REGEX:
820         case FILE_SEARCH:
821                 *p = getstr(ms, *p, m->value.s, sizeof(m->value.s), &slen);
822                 if (*p == NULL) {
823                         if (ms->flags & MAGIC_CHECK)
824                                 file_magwarn(ms, "cannot get string from `%s'",
825                                     m->value.s);
826                         return -1;
827                 }
828                 m->vallen = slen;
829                 return 0;
830         default:
831                 if (m->reln != 'x') {
832                         m->value.l = file_signextend(ms, m,
833                             (uint32_t)strtoul(*p, p, 0));
834                         eatsize(p);
835                 }
836                 return 0;
837         }
838 }
839
840 /*
841  * Convert a string containing C character escapes.  Stop at an unescaped
842  * space or tab.
843  * Copy the converted version to "p", returning its length in *slen.
844  * Return updated scan pointer as function result.
845  */
846 private char *
847 getstr(struct magic_set *ms, char *s, char *p, int plen, int *slen)
848 {
849         char    *origs = s, *origp = p;
850         char    *pmax = p + plen - 1;
851         int     c;
852         int     val;
853
854         while ((c = *s++) != '\0') {
855                 if (isspace((unsigned char) c))
856                         break;
857                 if (p >= pmax) {
858                         file_error(ms, 0, "string too long: `%s'", origs);
859                         return NULL;
860                 }
861                 if(c == '\\') {
862                         switch(c = *s++) {
863
864                         case '\0':
865                                 goto out;
866
867                         default:
868                                 *p++ = (char) c;
869                                 break;
870
871                         case 'n':
872                                 *p++ = '\n';
873                                 break;
874
875                         case 'r':
876                                 *p++ = '\r';
877                                 break;
878
879                         case 'b':
880                                 *p++ = '\b';
881                                 break;
882
883                         case 't':
884                                 *p++ = '\t';
885                                 break;
886
887                         case 'f':
888                                 *p++ = '\f';
889                                 break;
890
891                         case 'v':
892                                 *p++ = '\v';
893                                 break;
894
895                         /* \ and up to 3 octal digits */
896                         case '0':
897                         case '1':
898                         case '2':
899                         case '3':
900                         case '4':
901                         case '5':
902                         case '6':
903                         case '7':
904                                 val = c - '0';
905                                 c = *s++;  /* try for 2 */
906                                 if(c >= '0' && c <= '7') {
907                                         val = (val<<3) | (c - '0');
908                                         c = *s++;  /* try for 3 */
909                                         if(c >= '0' && c <= '7')
910                                                 val = (val<<3) | (c-'0');
911                                         else
912                                                 --s;
913                                 }
914                                 else
915                                         --s;
916                                 *p++ = (char)val;
917                                 break;
918
919                         /* \x and up to 2 hex digits */
920                         case 'x':
921                                 val = 'x';      /* Default if no digits */
922                                 c = hextoint(*s++);     /* Get next char */
923                                 if (c >= 0) {
924                                         val = c;
925                                         c = hextoint(*s++);
926                                         if (c >= 0)
927                                                 val = (val << 4) + c;
928                                         else
929                                                 --s;
930                                 } else
931                                         --s;
932                                 *p++ = (char)val;
933                                 break;
934                         }
935                 } else
936                         *p++ = (char)c;
937         }
938 out:
939         *p = '\0';
940         *slen = p - origp;
941         return s;
942 }
943
944
945 /* Single hex char to int; -1 if not a hex char. */
946 private int
947 hextoint(int c)
948 {
949         if (!isascii((unsigned char) c))
950                 return -1;
951         if (isdigit((unsigned char) c))
952                 return c - '0';
953         if ((c >= 'a')&&(c <= 'f'))
954                 return c + 10 - 'a';
955         if (( c>= 'A')&&(c <= 'F'))
956                 return c + 10 - 'A';
957         return -1;
958 }
959
960
961 /*
962  * Print a string containing C character escapes.
963  */
964 protected void
965 file_showstr(FILE *fp, const char *s, size_t len)
966 {
967         char    c;
968
969         for (;;) {
970                 c = *s++;
971                 if (len == ~0U) {
972                         if (c == '\0')
973                                 break;
974                 }
975                 else  {
976                         if (len-- == 0)
977                                 break;
978                 }
979                 if(c >= 040 && c <= 0176)       /* TODO isprint && !iscntrl */
980                         (void) fputc(c, fp);
981                 else {
982                         (void) fputc('\\', fp);
983                         switch (c) {
984                         
985                         case '\n':
986                                 (void) fputc('n', fp);
987                                 break;
988
989                         case '\r':
990                                 (void) fputc('r', fp);
991                                 break;
992
993                         case '\b':
994                                 (void) fputc('b', fp);
995                                 break;
996
997                         case '\t':
998                                 (void) fputc('t', fp);
999                                 break;
1000
1001                         case '\f':
1002                                 (void) fputc('f', fp);
1003                                 break;
1004
1005                         case '\v':
1006                                 (void) fputc('v', fp);
1007                                 break;
1008
1009                         default:
1010                                 (void) fprintf(fp, "%.3o", c & 0377);
1011                                 break;
1012                         }
1013                 }
1014         }
1015 }
1016
1017 /*
1018  * eatsize(): Eat the size spec from a number [eg. 10UL]
1019  */
1020 private void
1021 eatsize(char **p)
1022 {
1023         char *l = *p;
1024
1025         if (LOWCASE(*l) == 'u') 
1026                 l++;
1027
1028         switch (LOWCASE(*l)) {
1029         case 'l':    /* long */
1030         case 's':    /* short */
1031         case 'h':    /* short */
1032         case 'b':    /* char/byte */
1033         case 'c':    /* char/byte */
1034                 l++;
1035                 /*FALLTHROUGH*/
1036         default:
1037                 break;
1038         }
1039
1040         *p = l;
1041 }
1042
1043 /*
1044  * handle a compiled file.
1045  */
1046 private int
1047 apprentice_map(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp,
1048     const char *fn)
1049 {
1050         int fd;
1051         struct stat st;
1052         uint32_t *ptr;
1053         uint32_t version;
1054         int needsbyteswap;
1055         char buf[MAXPATHLEN];
1056         char *dbname = mkdbname(fn, buf, sizeof(buf), 0);
1057         void *mm = NULL;
1058
1059         if (dbname == NULL)
1060                 return -1;
1061
1062         if ((fd = open(dbname, O_RDONLY)) == -1)
1063                 return -1;
1064
1065         if (fstat(fd, &st) == -1) {
1066                 file_error(ms, errno, "cannot stat `%s'", dbname);
1067                 goto error;
1068         }
1069         if (st.st_size < 16) {
1070                 file_error(ms, 0, "file `%s' is too small", dbname);
1071                 goto error;
1072         }
1073
1074 #ifdef QUICK
1075         if ((mm = mmap(0, (size_t)st.st_size, PROT_READ|PROT_WRITE,
1076             MAP_PRIVATE|MAP_FILE, fd, (off_t)0)) == MAP_FAILED) {
1077                 file_error(ms, errno, "cannot map `%s'", dbname);
1078                 goto error;
1079         }
1080 #define RET     2
1081 #else
1082         if ((mm = malloc((size_t)st.st_size)) == NULL) {
1083                 file_oomem(ms);
1084                 goto error;
1085         }
1086         if (read(fd, mm, (size_t)st.st_size) != (size_t)st.st_size) {
1087                 file_badread(ms);
1088                 goto error;
1089         }
1090 #define RET     1
1091 #endif
1092         *magicp = mm;
1093         (void)close(fd);
1094         fd = -1;
1095         ptr = (uint32_t *)(void *)*magicp;
1096         if (*ptr != MAGICNO) {
1097                 if (swap4(*ptr) != MAGICNO) {
1098                         file_error(ms, 0, "bad magic in `%s'");
1099                         goto error;
1100                 }
1101                 needsbyteswap = 1;
1102         } else
1103                 needsbyteswap = 0;
1104         if (needsbyteswap)
1105                 version = swap4(ptr[1]);
1106         else
1107                 version = ptr[1];
1108         if (version != VERSIONNO) {
1109                 file_error(ms, 0, "version mismatch (%d != %d) in `%s'",
1110                     version, VERSIONNO, dbname);
1111                 goto error;
1112         }
1113         *nmagicp = (uint32_t)(st.st_size / sizeof(struct magic)) - 1;
1114         (*magicp)++;
1115         if (needsbyteswap)
1116                 byteswap(*magicp, *nmagicp);
1117         return RET;
1118
1119 error:
1120         if (fd != -1)
1121                 (void)close(fd);
1122         if (mm) {
1123 #ifdef QUICK
1124                 (void)munmap((void *)mm, (size_t)st.st_size);
1125 #else
1126                 free(mm);
1127 #endif
1128         } else {
1129                 *magicp = NULL;
1130                 *nmagicp = 0;
1131         }
1132         return -1;
1133 }
1134
1135 private const uint32_t ar[] = {
1136     MAGICNO, VERSIONNO
1137 };
1138 /*
1139  * handle an mmaped file.
1140  */
1141 private int
1142 apprentice_compile(struct magic_set *ms, struct magic **magicp,
1143     uint32_t *nmagicp, const char *fn)
1144 {
1145         int fd;
1146         char buf[MAXPATHLEN];
1147         char *dbname = mkdbname(fn, buf, sizeof(buf), 1);
1148
1149         if (dbname == NULL) 
1150                 return -1;
1151
1152         if ((fd = open(dbname, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) {
1153                 file_error(ms, errno, "cannot open `%s'", dbname);
1154                 return -1;
1155         }
1156
1157         if (write(fd, ar, sizeof(ar)) != (ssize_t)sizeof(ar)) {
1158                 file_error(ms, errno, "error writing `%s'", dbname);
1159                 return -1;
1160         }
1161
1162         if (lseek(fd, (off_t)sizeof(struct magic), SEEK_SET)
1163             != sizeof(struct magic)) {
1164                 file_error(ms, errno, "error seeking `%s'", dbname);
1165                 return -1;
1166         }
1167
1168         if (write(fd, *magicp, (sizeof(struct magic) * *nmagicp)) 
1169             != (ssize_t)(sizeof(struct magic) * *nmagicp)) {
1170                 file_error(ms, errno, "error writing `%s'", dbname);
1171                 return -1;
1172         }
1173
1174         (void)close(fd);
1175         return 0;
1176 }
1177
1178 private const char ext[] = ".mgc";
1179 /*
1180  * make a dbname
1181  */
1182 private char *
1183 mkdbname(const char *fn, char *buf, size_t bufsiz, int strip)
1184 {
1185         if (strip) {
1186                 const char *p;
1187                 if ((p = strrchr(fn, '/')) != NULL)
1188                         fn = ++p;
1189         }
1190
1191         (void)snprintf(buf, bufsiz, "%s%s", fn, ext);
1192         return buf;
1193 }
1194
1195 /*
1196  * Byteswap an mmap'ed file if needed
1197  */
1198 private void
1199 byteswap(struct magic *magic, uint32_t nmagic)
1200 {
1201         uint32_t i;
1202         for (i = 0; i < nmagic; i++)
1203                 bs1(&magic[i]);
1204 }
1205
1206 /*
1207  * swap a short
1208  */
1209 private uint16_t
1210 swap2(uint16_t sv)
1211 {
1212         uint16_t rv;
1213         uint8_t *s = (uint8_t *)(void *)&sv; 
1214         uint8_t *d = (uint8_t *)(void *)&rv; 
1215         d[0] = s[1];
1216         d[1] = s[0];
1217         return rv;
1218 }
1219
1220 /*
1221  * swap an int
1222  */
1223 private uint32_t
1224 swap4(uint32_t sv)
1225 {
1226         uint32_t rv;
1227         uint8_t *s = (uint8_t *)(void *)&sv; 
1228         uint8_t *d = (uint8_t *)(void *)&rv; 
1229         d[0] = s[3];
1230         d[1] = s[2];
1231         d[2] = s[1];
1232         d[3] = s[0];
1233         return rv;
1234 }
1235
1236 /*
1237  * byteswap a single magic entry
1238  */
1239 private void
1240 bs1(struct magic *m)
1241 {
1242         m->cont_level = swap2(m->cont_level);
1243         m->offset = swap4((uint32_t)m->offset);
1244         m->in_offset = swap4((uint32_t)m->in_offset);
1245         if (!IS_STRING(m->type))
1246                 m->value.l = swap4(m->value.l);
1247         m->mask = swap4(m->mask);
1248 }