]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/file/apprentice.c
Virgin import of Christos Zoulas's FILE 4.21.
[FreeBSD/FreeBSD.git] / contrib / file / 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 <assert.h>
40 #include <ctype.h>
41 #include <fcntl.h>
42 #include <sys/stat.h>
43 #include <sys/param.h>
44 #ifdef QUICK
45 #include <sys/mman.h>
46 #endif
47
48 #ifndef lint
49 FILE_RCSID("@(#)$File: apprentice.c,v 1.105 2007/05/16 20:51:40 christos Exp $")
50 #endif  /* lint */
51
52 #define EATAB {while (isascii((unsigned char) *l) && \
53                       isspace((unsigned char) *l))  ++l;}
54 #define LOWCASE(l) (isupper((unsigned char) (l)) ? \
55                         tolower((unsigned char) (l)) : (l))
56 /*
57  * Work around a bug in headers on Digital Unix.
58  * At least confirmed for: OSF1 V4.0 878
59  */
60 #if defined(__osf__) && defined(__DECC)
61 #ifdef MAP_FAILED
62 #undef MAP_FAILED
63 #endif
64 #endif
65
66 #ifndef MAP_FAILED
67 #define MAP_FAILED (void *) -1
68 #endif
69
70 #ifndef MAP_FILE
71 #define MAP_FILE 0
72 #endif
73
74 #ifndef MAXPATHLEN
75 #define MAXPATHLEN      1024
76 #endif
77
78 struct magic_entry {
79         struct magic *mp;       
80         uint32_t cont_count;
81         uint32_t max_count;
82 };
83
84 int file_formats[FILE_NAMES_SIZE];
85 const size_t file_nformats = FILE_NAMES_SIZE;
86 const char *file_names[FILE_NAMES_SIZE];
87 const size_t file_nnames = FILE_NAMES_SIZE;
88
89 private int getvalue(struct magic_set *ms, struct magic *, const char **, int);
90 private int hextoint(int);
91 private const char *getstr(struct magic_set *, const char *, char *, int,
92     int *, int);
93 private int parse(struct magic_set *, struct magic_entry **, uint32_t *,
94     const char *, size_t, int);
95 private void eatsize(const char **);
96 private int apprentice_1(struct magic_set *, const char *, int, struct mlist *);
97 private size_t apprentice_magic_strength(const struct magic *);
98 private int apprentice_sort(const void *, const void *);
99 private int apprentice_file(struct magic_set *, struct magic **, uint32_t *,
100     const char *, int);
101 private void byteswap(struct magic *, uint32_t);
102 private void bs1(struct magic *);
103 private uint16_t swap2(uint16_t);
104 private uint32_t swap4(uint32_t);
105 private uint64_t swap8(uint64_t);
106 private char *mkdbname(const char *, char *, size_t, int);
107 private int apprentice_map(struct magic_set *, struct magic **, uint32_t *,
108     const char *);
109 private int apprentice_compile(struct magic_set *, struct magic **, uint32_t *,
110     const char *);
111 private int check_format_type(const char *, int);
112 private int check_format(struct magic_set *, struct magic *);
113
114 private size_t maxmagic = 0;
115 private size_t magicsize = sizeof(struct magic);
116
117
118 #ifdef COMPILE_ONLY
119
120 int main(int, char *[]);
121
122 int
123 main(int argc, char *argv[])
124 {
125         int ret;
126         struct magic_set *ms;
127         char *progname;
128
129         if ((progname = strrchr(argv[0], '/')) != NULL)
130                 progname++;
131         else
132                 progname = argv[0];
133
134         if (argc != 2) {
135                 (void)fprintf(stderr, "Usage: %s file\n", progname);
136                 return 1;
137         }
138
139         if ((ms = magic_open(MAGIC_CHECK)) == NULL) {
140                 (void)fprintf(stderr, "%s: %s\n", progname, strerror(errno));
141                 return 1;
142         }
143         ret = magic_compile(ms, argv[1]) == -1 ? 1 : 0;
144         if (ret == 1)
145                 (void)fprintf(stderr, "%s: %s\n", progname, magic_error(ms));
146         magic_close(ms);
147         return ret;
148 }
149 #endif /* COMPILE_ONLY */
150
151 static const struct type_tbl_s {
152         const char *name;
153         const size_t len;
154         const int type;
155         const int format;
156 } type_tbl[] = {
157 # define XX(s)          s, (sizeof(s) - 1)
158 # define XX_NULL        NULL, 0
159         { XX("byte"),           FILE_BYTE,              FILE_FMT_NUM },
160         { XX("short"),          FILE_SHORT,             FILE_FMT_NUM },
161         { XX("default"),        FILE_DEFAULT,           FILE_FMT_STR },
162         { XX("long"),           FILE_LONG,              FILE_FMT_NUM },
163         { XX("string"),         FILE_STRING,            FILE_FMT_STR },
164         { XX("date"),           FILE_DATE,              FILE_FMT_STR },
165         { XX("beshort"),        FILE_BESHORT,           FILE_FMT_NUM },
166         { XX("belong"),         FILE_BELONG,            FILE_FMT_NUM },
167         { XX("bedate"),         FILE_BEDATE,            FILE_FMT_STR },
168         { XX("leshort"),        FILE_LESHORT,           FILE_FMT_NUM },
169         { XX("lelong"),         FILE_LELONG,            FILE_FMT_NUM },
170         { XX("ledate"),         FILE_LEDATE,            FILE_FMT_STR },
171         { XX("pstring"),        FILE_PSTRING,           FILE_FMT_STR },
172         { XX("ldate"),          FILE_LDATE,             FILE_FMT_STR },
173         { XX("beldate"),        FILE_BELDATE,           FILE_FMT_STR },
174         { XX("leldate"),        FILE_LELDATE,           FILE_FMT_STR },
175         { XX("regex"),          FILE_REGEX,             FILE_FMT_STR },
176         { XX("bestring16"),     FILE_BESTRING16,        FILE_FMT_STR },
177         { XX("lestring16"),     FILE_LESTRING16,        FILE_FMT_STR },
178         { XX("search"),         FILE_SEARCH,            FILE_FMT_STR },
179         { XX("medate"),         FILE_MEDATE,            FILE_FMT_STR },
180         { XX("meldate"),        FILE_MELDATE,           FILE_FMT_STR },
181         { XX("melong"),         FILE_MELONG,            FILE_FMT_NUM },
182         { XX("quad"),           FILE_QUAD,              FILE_FMT_QUAD },
183         { XX("lequad"),         FILE_LEQUAD,            FILE_FMT_QUAD },
184         { XX("bequad"),         FILE_BEQUAD,            FILE_FMT_QUAD },
185         { XX("qdate"),          FILE_QDATE,             FILE_FMT_STR },
186         { XX("leqdate"),        FILE_LEQDATE,           FILE_FMT_STR },
187         { XX("beqdate"),        FILE_BEQDATE,           FILE_FMT_STR },
188         { XX("qldate"),         FILE_QLDATE,            FILE_FMT_STR },
189         { XX("leqldate"),       FILE_LEQLDATE,          FILE_FMT_STR },
190         { XX("beqldate"),       FILE_BEQLDATE,          FILE_FMT_STR },
191         { XX_NULL,              FILE_INVALID,           FILE_FMT_NONE },
192 # undef XX
193 # undef XX_NULL
194 };
195
196 private int
197 get_type(const char *l, const char **t)
198 {
199         const struct type_tbl_s *p;
200
201         for (p = type_tbl; p->name; p++) {
202                 if (strncmp(l, p->name, p->len) == 0) {
203                         if (t)
204                                 *t = l + p->len;
205                         break;
206                 }
207         }
208         return p->type;
209 }
210
211 private void
212 init_file_tables(void)
213 {
214         static int done = 0;
215         const struct type_tbl_s *p;
216
217         if (done)
218                 return;
219         done++;
220
221         for (p = type_tbl; p->name; p++) {
222                 assert(p->type < FILE_NAMES_SIZE);
223                 file_names[p->type] = p->name;
224                 file_formats[p->type] = p->format;
225         }
226 }
227
228 /*
229  * Handle one file.
230  */
231 private int
232 apprentice_1(struct magic_set *ms, const char *fn, int action,
233     struct mlist *mlist)
234 {
235         struct magic *magic = NULL;
236         uint32_t nmagic = 0;
237         struct mlist *ml;
238         int rv = -1;
239         int mapped;
240
241         if (magicsize != FILE_MAGICSIZE) {
242                 file_error(ms, 0, "magic element size %lu != %lu",
243                     (unsigned long)sizeof(*magic),
244                     (unsigned long)FILE_MAGICSIZE);
245                 return -1;
246         }
247
248         if (action == FILE_COMPILE) {
249                 rv = apprentice_file(ms, &magic, &nmagic, fn, action);
250                 if (rv != 0)
251                         return -1;
252                 rv = apprentice_compile(ms, &magic, &nmagic, fn);
253                 free(magic);
254                 return rv;
255         }
256
257 #ifndef COMPILE_ONLY
258         if ((rv = apprentice_map(ms, &magic, &nmagic, fn)) == -1) {
259                 if (ms->flags & MAGIC_CHECK)
260                         file_magwarn(ms, "using regular magic file `%s'", fn);
261                 rv = apprentice_file(ms, &magic, &nmagic, fn, action);
262                 if (rv != 0)
263                         return -1;
264                 mapped = 0;
265         }
266
267         mapped = rv;
268              
269         if (magic == NULL || nmagic == 0) {
270                 file_delmagic(magic, mapped, nmagic);
271                 return -1;
272         }
273
274         if ((ml = malloc(sizeof(*ml))) == NULL) {
275                 file_delmagic(magic, mapped, nmagic);
276                 file_oomem(ms, sizeof(*ml));
277                 return -1;
278         }
279
280         ml->magic = magic;
281         ml->nmagic = nmagic;
282         ml->mapped = mapped;
283
284         mlist->prev->next = ml;
285         ml->prev = mlist->prev;
286         ml->next = mlist;
287         mlist->prev = ml;
288
289         return 0;
290 #endif /* COMPILE_ONLY */
291 }
292
293 protected void
294 file_delmagic(struct magic *p, int type, size_t entries)
295 {
296         if (p == NULL)
297                 return;
298         switch (type) {
299         case 2:
300                 p--;
301                 (void)munmap((void *)p, sizeof(*p) * (entries + 1));
302                 break;
303         case 1:
304                 p--;
305                 /*FALLTHROUGH*/
306         case 0:
307                 free(p);
308                 break;
309         default:
310                 abort();
311         }
312 }
313
314 /* const char *fn: list of magic files */
315 protected struct mlist *
316 file_apprentice(struct magic_set *ms, const char *fn, int action)
317 {
318         char *p, *mfn, *afn = NULL;
319         int file_err, errs = -1;
320         struct mlist *mlist;
321         static const char mime[] = ".mime";
322
323         init_file_tables();
324
325         if (fn == NULL)
326                 fn = getenv("MAGIC");
327         if (fn == NULL)
328                 fn = MAGIC;
329
330         if ((mfn = strdup(fn)) == NULL) {
331                 file_oomem(ms, strlen(fn));
332                 return NULL;
333         }
334         fn = mfn;
335
336         if ((mlist = malloc(sizeof(*mlist))) == NULL) {
337                 free(mfn);
338                 file_oomem(ms, sizeof(*mlist));
339                 return NULL;
340         }
341         mlist->next = mlist->prev = mlist;
342
343         while (fn) {
344                 p = strchr(fn, PATHSEP);
345                 if (p)
346                         *p++ = '\0';
347                 if (*fn == '\0')
348                         break;
349                 if (ms->flags & MAGIC_MIME) {
350                         size_t len = strlen(fn) + sizeof(mime);
351                         if ((afn = malloc(len)) == NULL) {
352                                 free(mfn);
353                                 free(mlist);
354                                 file_oomem(ms, len);
355                                 return NULL;
356                         }
357                         (void)strcpy(afn, fn);
358                         (void)strcat(afn, mime);
359                         fn = afn;
360                 }
361                 file_err = apprentice_1(ms, fn, action, mlist);
362                 if (file_err > errs)
363                         errs = file_err;
364                 if (afn) {
365                         free(afn);
366                         afn = NULL;
367                 }
368                 fn = p;
369         }
370         if (errs == -1) {
371                 free(mfn);
372                 free(mlist);
373                 mlist = NULL;
374                 file_error(ms, 0, "could not find any magic files!");
375                 return NULL;
376         }
377         free(mfn);
378         return mlist;
379 }
380
381 /*
382  * Get weight of this magic entry, for sorting purposes.
383  */
384 private size_t
385 apprentice_magic_strength(const struct magic *m)
386 {
387 #define MULT 10
388         size_t val = 2 * MULT;  /* baseline strength */
389
390         switch (m->type) {
391         case FILE_DEFAULT:      /* make sure this sorts last */
392                 return 0;
393
394         case FILE_BYTE:
395                 val += 1 * MULT;
396                 break;
397
398         case FILE_SHORT:
399         case FILE_LESHORT:
400         case FILE_BESHORT:
401                 val += 2 * MULT;
402                 break;
403
404         case FILE_LONG:
405         case FILE_LELONG:
406         case FILE_BELONG:
407         case FILE_MELONG:
408                 val += 4 * MULT;
409                 break;
410
411         case FILE_PSTRING:
412         case FILE_STRING:
413                 val += m->vallen * MULT;
414                 break;
415
416         case FILE_BESTRING16:
417         case FILE_LESTRING16:
418                 val += m->vallen * MULT / 2;
419                 break;
420
421         case FILE_SEARCH:
422         case FILE_REGEX:
423                 val += m->vallen;
424                 break;
425
426         case FILE_DATE:
427         case FILE_LEDATE:
428         case FILE_BEDATE:
429         case FILE_MEDATE:
430         case FILE_LDATE:
431         case FILE_LELDATE:
432         case FILE_BELDATE:
433         case FILE_MELDATE:
434                 val += 4 * MULT;
435                 break;
436
437         case FILE_QUAD:
438         case FILE_BEQUAD:
439         case FILE_LEQUAD:
440         case FILE_QDATE:
441         case FILE_LEQDATE:
442         case FILE_BEQDATE:
443         case FILE_QLDATE:
444         case FILE_LEQLDATE:
445         case FILE_BEQLDATE:
446                 val += 8 * MULT;
447                 break;
448
449         default:
450                 val = 0;
451                 (void)fprintf(stderr, "Bad type %d\n", m->type);
452                 abort();
453         }
454
455         switch (m->reln) {
456         case 'x':       /* matches anything penalize */
457                 val = 0;
458                 break;
459
460         case '!':
461         case '=':       /* Exact match, prefer */
462                 val += MULT;
463                 break;
464
465         case '>':
466         case '<':       /* comparison match reduce strength */
467                 val -= 2 * MULT;
468                 break;
469
470         case '^':
471         case '&':       /* masking bits, we could count them too */
472                 val -= MULT;
473                 break;
474
475         default:
476                 (void)fprintf(stderr, "Bad relation %c\n", m->reln);
477                 abort();
478         }
479
480         if (val == 0)   /* ensure we only return 0 for FILE_DEFAULT */
481                 val = 1;
482
483         return val;
484 }
485
486 /*  
487  * Sort callback for sorting entries by "strength" (basically length)
488  */
489 private int
490 apprentice_sort(const void *a, const void *b)
491 {
492         const struct magic_entry *ma = a;
493         const struct magic_entry *mb = b;
494         size_t sa = apprentice_magic_strength(ma->mp);
495         size_t sb = apprentice_magic_strength(mb->mp);
496         if (sa == sb)
497                 return 0;
498         else if (sa > sb)
499                 return -1;
500         else
501                 return 1;
502 }
503
504 /*
505  * parse from a file
506  * const char *fn: name of magic file
507  */
508 private int
509 apprentice_file(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp,
510     const char *fn, int action)
511 {
512         private const char hdr[] =
513                 "cont\toffset\ttype\topcode\tmask\tvalue\tdesc";
514         FILE *f;
515         char line[BUFSIZ+1];
516         int errs = 0;
517         struct magic_entry *marray;
518         uint32_t marraycount, i, mentrycount = 0;
519         size_t lineno = 0;
520
521         ms->flags |= MAGIC_CHECK;       /* Enable checks for parsed files */
522
523         f = fopen(ms->file = fn, "r");
524         if (f == NULL) {
525                 if (errno != ENOENT)
526                         file_error(ms, errno, "cannot read magic file `%s'",
527                             fn);
528                 return -1;
529         }
530
531         maxmagic = MAXMAGIS;
532         if ((marray = calloc(maxmagic, sizeof(*marray))) == NULL) {
533                 (void)fclose(f);
534                 file_oomem(ms, maxmagic * sizeof(*marray));
535                 return -1;
536         }
537         marraycount = 0;
538
539         /* print silly verbose header for USG compat. */
540         if (action == FILE_CHECK)
541                 (void)fprintf(stderr, "%s\n", hdr);
542
543         /* read and parse this file */
544         for (ms->line = 1; fgets(line, BUFSIZ, f) != NULL; ms->line++) {
545                 size_t len;
546                 len = strlen(line);
547                 if (len == 0) /* null line, garbage, etc */
548                         continue;
549                 if (line[len - 1] == '\n') {
550                         lineno++;
551                         line[len - 1] = '\0'; /* delete newline */
552                 }
553                 if (line[0] == '\0')    /* empty, do not parse */
554                         continue;
555                 if (line[0] == '#')     /* comment, do not parse */
556                         continue;
557                 if (parse(ms, &marray, &marraycount, line, lineno, action) != 0)
558                         errs++;
559         }
560
561         (void)fclose(f);
562         if (errs)
563                 goto out;
564
565 #ifndef NOORDER
566         qsort(marray, marraycount, sizeof(*marray), apprentice_sort);
567         /*
568          * Make sure that any level 0 "default" line is last (if one exists).
569          */
570         for (i = 0; i < marraycount; i++) {
571                 if (marray[i].mp->cont_level == 0 &&
572                     marray[i].mp->type == FILE_DEFAULT) {
573                         while (++i < marraycount)
574                                 if (marray[i].mp->cont_level == 0)
575                                         break;
576                         if (i != marraycount) {
577                                 ms->line = marray[i].mp->lineno; /* XXX - Ugh! */
578                                 file_magwarn(ms,
579                                     "level 0 \"default\" did not sort last");
580                         }
581                         break;                                      
582                 }
583         }
584 #endif
585
586         for (i = 0; i < marraycount; i++)
587                 mentrycount += marray[i].cont_count;
588
589         if ((*magicp = malloc(sizeof(**magicp) * mentrycount)) == NULL) {
590                 file_oomem(ms, sizeof(**magicp) * mentrycount);
591                 errs++;
592                 goto out;
593         }
594
595         mentrycount = 0;
596         for (i = 0; i < marraycount; i++) {
597                 (void)memcpy(*magicp + mentrycount, marray[i].mp,
598                     marray[i].cont_count * sizeof(**magicp));
599                 mentrycount += marray[i].cont_count;
600         }
601 out:
602         for (i = 0; i < marraycount; i++)
603                 free(marray[i].mp);
604         free(marray);
605         if (errs) {
606                 *magicp = NULL;
607                 *nmagicp = 0;
608                 return errs;
609         } else {
610                 *nmagicp = mentrycount;
611                 return 0;
612         }
613
614 }
615
616 /*
617  * extend the sign bit if the comparison is to be signed
618  */
619 protected uint64_t
620 file_signextend(struct magic_set *ms, struct magic *m, uint64_t v)
621 {
622         if (!(m->flag & UNSIGNED)) {
623                 switch(m->type) {
624                 /*
625                  * Do not remove the casts below.  They are
626                  * vital.  When later compared with the data,
627                  * the sign extension must have happened.
628                  */
629                 case FILE_BYTE:
630                         v = (char) v;
631                         break;
632                 case FILE_SHORT:
633                 case FILE_BESHORT:
634                 case FILE_LESHORT:
635                         v = (short) v;
636                         break;
637                 case FILE_DATE:
638                 case FILE_BEDATE:
639                 case FILE_LEDATE:
640                 case FILE_MEDATE:
641                 case FILE_LDATE:
642                 case FILE_BELDATE:
643                 case FILE_LELDATE:
644                 case FILE_MELDATE:
645                 case FILE_LONG:
646                 case FILE_BELONG:
647                 case FILE_LELONG:
648                 case FILE_MELONG:
649                         v = (int32_t) v;
650                         break;
651                 case FILE_QUAD:
652                 case FILE_BEQUAD:
653                 case FILE_LEQUAD:
654                 case FILE_QDATE:
655                 case FILE_QLDATE:
656                 case FILE_BEQDATE:
657                 case FILE_BEQLDATE:
658                 case FILE_LEQDATE:
659                 case FILE_LEQLDATE:
660                         v = (int64_t) v;
661                         break;
662                 case FILE_STRING:
663                 case FILE_PSTRING:
664                 case FILE_BESTRING16:
665                 case FILE_LESTRING16:
666                 case FILE_REGEX:
667                 case FILE_SEARCH:
668                 case FILE_DEFAULT:
669                         break;
670                 default:
671                         if (ms->flags & MAGIC_CHECK)
672                             file_magwarn(ms, "cannot happen: m->type=%d\n",
673                                     m->type);
674                         return ~0U;
675                 }
676         }
677         return v;
678 }
679
680 private int
681 string_modifier_check(struct magic_set *ms, struct magic const *m)
682 {
683         if ((ms->flags & MAGIC_CHECK) == 0)
684                 return 0;
685
686         switch (m->type) {
687         case FILE_BESTRING16:
688         case FILE_LESTRING16:
689                 if (m->str_flags != 0) {
690                         file_magwarn(ms, "no modifiers allowed for 16-bit strings\n");
691                         return -1;
692                 }
693                 break;
694         case FILE_STRING:
695         case FILE_PSTRING:
696                 if ((m->str_flags & REGEX_OFFSET_START) != 0) {
697                         file_magwarn(ms, "'/%c' only allowed on regex and search\n",
698                             CHAR_REGEX_OFFSET_START);
699                         return -1;
700                 }
701                 break;
702         case FILE_SEARCH:
703                 break;
704         case FILE_REGEX:
705                 if ((m->str_flags & STRING_COMPACT_BLANK) != 0) {
706                         file_magwarn(ms, "'/%c' not allowed on regex\n",
707                             CHAR_COMPACT_BLANK);
708                         return -1;
709                 }
710                 if ((m->str_flags & STRING_COMPACT_OPTIONAL_BLANK) != 0) {
711                         file_magwarn(ms, "'/%c' not allowed on regex\n",
712                             CHAR_COMPACT_OPTIONAL_BLANK);
713                         return -1;
714                 }
715                 break;
716         default:
717                 file_magwarn(ms, "coding error: m->type=%d\n",
718                     m->type);
719                 return -1;
720         }
721         return 0;
722 }
723
724 private int
725 get_op(char c)
726 {
727         switch (c) {
728         case '&':
729                 return FILE_OPAND;
730         case '|':
731                 return FILE_OPOR;
732         case '^':
733                 return FILE_OPXOR;
734         case '+':
735                 return FILE_OPADD;
736         case '-':
737                 return FILE_OPMINUS;
738         case '*':
739                 return FILE_OPMULTIPLY;
740         case '/':
741                 return FILE_OPDIVIDE;
742         case '%':
743                 return FILE_OPMODULO;
744         default:
745                 return -1;
746         }
747 }
748
749 #ifdef ENABLE_CONDITIONALS
750 private int
751 get_cond(const char *l, const char **t)
752 {
753         static struct cond_tbl_s {
754                 const char *name;
755                 const size_t len;
756                 const int cond;
757         } cond_tbl[] = {
758                 { "if",         2,      COND_IF },
759                 { "elif",       4,      COND_ELIF },
760                 { "else",       4,      COND_ELSE },
761                 { NULL,         0,      COND_NONE },
762         };
763         struct cond_tbl_s *p;
764
765         for (p = cond_tbl; p->name; p++) {
766                 if (strncmp(l, p->name, p->len) == 0 &&
767                     isspace((unsigned char)l[p->len])) {
768                         if (t)
769                                 *t = l + p->len;
770                         break;
771                 }
772         }
773         return p->cond;
774 }
775
776 private int
777 check_cond(struct magic_set *ms, int cond, uint32_t cont_level)
778 {
779         int last_cond;
780         last_cond = ms->c.li[cont_level].last_cond;
781
782         switch (cond) {
783         case COND_IF:
784                 if (last_cond != COND_NONE && last_cond != COND_ELIF) {
785                         if (ms->flags & MAGIC_CHECK)
786                                 file_magwarn(ms, "syntax error: `if'");
787                         return -1;
788                 }
789                 last_cond = COND_IF;
790                 break;
791
792         case COND_ELIF:
793                 if (last_cond != COND_IF && last_cond != COND_ELIF) {
794                         if (ms->flags & MAGIC_CHECK)
795                                 file_magwarn(ms, "syntax error: `elif'");
796                         return -1;
797                 }
798                 last_cond = COND_ELIF;
799                 break;
800
801         case COND_ELSE:
802                 if (last_cond != COND_IF && last_cond != COND_ELIF) {
803                         if (ms->flags & MAGIC_CHECK)
804                                 file_magwarn(ms, "syntax error: `else'");
805                         return -1;
806                 }
807                 last_cond = COND_NONE;
808                 break;
809
810         case COND_NONE:
811                 last_cond = COND_NONE;
812                 break;
813         }
814
815         ms->c.li[cont_level].last_cond = last_cond;
816         return 0;
817 }
818 #endif /* ENABLE_CONDITIONALS */
819
820 /*
821  * parse one line from magic file, put into magic[index++] if valid
822  */
823 private int
824 parse(struct magic_set *ms, struct magic_entry **mentryp, uint32_t *nmentryp, 
825     const char *line, size_t lineno, int action)
826 {
827 #ifdef ENABLE_CONDITIONALS
828         static uint32_t last_cont_level = 0;
829 #endif
830         size_t i;
831         struct magic_entry *me;
832         struct magic *m;
833         const char *l = line;
834         char *t;
835         int op;
836         uint32_t cont_level;
837
838         cont_level = 0;
839
840         while (*l == '>') {
841                 ++l;            /* step over */
842                 cont_level++; 
843         }
844 #ifdef ENABLE_CONDITIONALS
845         if (cont_level == 0 || cont_level > last_cont_level)
846                 if (file_check_mem(ms, cont_level) == -1)
847                         return -1;
848         last_cont_level = cont_level;
849 #endif
850
851 #define ALLOC_CHUNK     (size_t)10
852 #define ALLOC_INCR      (size_t)200
853
854         if (cont_level != 0) {
855                 if (*nmentryp == 0) {
856                         file_error(ms, 0, "No current entry for continuation");
857                         return -1;
858                 }
859                 me = &(*mentryp)[*nmentryp - 1];
860                 if (me->cont_count == me->max_count) {
861                         struct magic *nm;
862                         size_t cnt = me->max_count + ALLOC_CHUNK;
863                         if ((nm = realloc(me->mp, sizeof(*nm) * cnt)) == NULL) {
864                                 file_oomem(ms, sizeof(*nm) * cnt);
865                                 return -1;
866                         }
867                         me->mp = m = nm;
868                         me->max_count = cnt;
869                 }
870                 m = &me->mp[me->cont_count++];
871                 (void)memset(m, 0, sizeof(*m));
872                 m->cont_level = cont_level;
873         } else {
874                 if (*nmentryp == maxmagic) {
875                         struct magic_entry *mp;
876
877                         maxmagic += ALLOC_INCR;
878                         if ((mp = realloc(*mentryp, sizeof(*mp) * maxmagic)) ==
879                             NULL) {
880                                 file_oomem(ms, sizeof(*mp) * maxmagic);
881                                 return -1;
882                         }
883                         (void)memset(&mp[*nmentryp], 0, sizeof(*mp) *
884                             ALLOC_INCR);
885                         *mentryp = mp;
886                 }
887                 me = &(*mentryp)[*nmentryp];
888                 if (me->mp == NULL) {
889                         if ((m = malloc(sizeof(*m) * ALLOC_CHUNK)) == NULL) {
890                                 file_oomem(ms, sizeof(*m) * ALLOC_CHUNK);
891                                 return -1;
892                         }
893                         me->mp = m;
894                         me->max_count = ALLOC_CHUNK;
895                 } else
896                         m = me->mp;
897                 (void)memset(m, 0, sizeof(*m));
898                 m->cont_level = 0;
899                 me->cont_count = 1;
900         }
901         m->lineno = lineno;
902
903         if (*l == '&') {  /* m->cont_level == 0 checked below. */
904                 ++l;            /* step over */
905                 m->flag |= OFFADD;
906         }
907         if (*l == '(') {
908                 ++l;            /* step over */
909                 m->flag |= INDIR;
910                 if (m->flag & OFFADD)
911                         m->flag = (m->flag & ~OFFADD) | INDIROFFADD;
912
913                 if (*l == '&') {  /* m->cont_level == 0 checked below */
914                         ++l;            /* step over */
915                         m->flag |= OFFADD;
916                 }
917         }
918         /* Indirect offsets are not valid at level 0. */
919         if (m->cont_level == 0 && (m->flag & (OFFADD | INDIROFFADD)))
920                 if (ms->flags & MAGIC_CHECK)
921                         file_magwarn(ms, "relative offset at level 0");
922
923         /* get offset, then skip over it */
924         m->offset = (uint32_t)strtoul(l, &t, 0);
925         if (l == t)
926                 if (ms->flags & MAGIC_CHECK)
927                         file_magwarn(ms, "offset `%s' invalid", l);
928         l = t;
929
930         if (m->flag & INDIR) {
931                 m->in_type = FILE_LONG;
932                 m->in_offset = 0;
933                 /*
934                  * read [.lbs][+-]nnnnn)
935                  */
936                 if (*l == '.') {
937                         l++;
938                         switch (*l) {
939                         case 'l':
940                                 m->in_type = FILE_LELONG;
941                                 break;
942                         case 'L':
943                                 m->in_type = FILE_BELONG;
944                                 break;
945                         case 'm':
946                                 m->in_type = FILE_MELONG;
947                                 break;
948                         case 'h':
949                         case 's':
950                                 m->in_type = FILE_LESHORT;
951                                 break;
952                         case 'H':
953                         case 'S':
954                                 m->in_type = FILE_BESHORT;
955                                 break;
956                         case 'c':
957                         case 'b':
958                         case 'C':
959                         case 'B':
960                                 m->in_type = FILE_BYTE;
961                                 break;
962                         default:
963                                 if (ms->flags & MAGIC_CHECK)
964                                         file_magwarn(ms,
965                                             "indirect offset type `%c' invalid",
966                                             *l);
967                                 break;
968                         }
969                         l++;
970                 }
971
972                 m->in_op = 0;
973                 if (*l == '~') {
974                         m->in_op |= FILE_OPINVERSE;
975                         l++;
976                 }
977                 if ((op = get_op(*l)) != -1) {
978                         m->in_op |= op;
979                         l++;
980                 }
981                 if (*l == '(') {
982                         m->in_op |= FILE_OPINDIRECT;
983                         l++;
984                 }
985                 if (isdigit((unsigned char)*l) || *l == '-') {
986                         m->in_offset = (int32_t)strtol(l, &t, 0);
987                         if (l == t)
988                                 if (ms->flags & MAGIC_CHECK)
989                                         file_magwarn(ms,
990                                             "in_offset `%s' invalid", l);
991                         l = t;
992                 }
993                 if (*l++ != ')' || 
994                     ((m->in_op & FILE_OPINDIRECT) && *l++ != ')'))
995                         if (ms->flags & MAGIC_CHECK)
996                                 file_magwarn(ms,
997                                     "missing ')' in indirect offset");
998         }
999         EATAB;
1000
1001 #ifdef ENABLE_CONDITIONALS
1002         m->cond = get_cond(l, &l);
1003         if (check_cond(ms, m->cond, cont_level) == -1)
1004                 return -1;
1005
1006         EATAB;
1007 #endif
1008
1009         if (*l == 'u') {
1010                 ++l;
1011                 m->flag |= UNSIGNED;
1012         }
1013
1014         m->type = get_type(l, &l);
1015         if (m->type == FILE_INVALID) {
1016                 if (ms->flags & MAGIC_CHECK)
1017                         file_magwarn(ms, "type `%s' invalid", l);
1018                 return -1;
1019         }
1020
1021         /* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */
1022         /* New and improved: ~ & | ^ + - * / % -- exciting, isn't it? */
1023
1024         m->mask_op = 0;
1025         if (*l == '~') {
1026                 if (!IS_STRING(m->type))
1027                         m->mask_op |= FILE_OPINVERSE;
1028                 else if (ms->flags & MAGIC_CHECK)
1029                         file_magwarn(ms, "'~' invalid for string types");
1030                 ++l;
1031         }
1032         m->str_count = 0;
1033         m->str_flags = 0;
1034         m->num_mask = 0;
1035         if ((op = get_op(*l)) != -1) {
1036                 if (!IS_STRING(m->type)) {
1037                         uint64_t val;
1038                         ++l;
1039                         m->mask_op |= op;
1040                         val = (uint64_t)strtoull(l, &t, 0);
1041                         l = t;
1042                         m->num_mask = file_signextend(ms, m, val);
1043                         eatsize(&l);
1044                 }
1045                 else if (op == FILE_OPDIVIDE) {
1046                         int have_count = 0;
1047                         while (!isspace((unsigned char)*++l)) {
1048                                 switch (*l) {
1049                                 /* for portability avoid "case '0' ... '9':" */
1050                                 case '0':  case '1':  case '2':
1051                                 case '3':  case '4':  case '5':
1052                                 case '6':  case '7':  case '8':
1053                                 case '9': {
1054                                         if (have_count && ms->flags & MAGIC_CHECK)
1055                                                 file_magwarn(ms,
1056                                                     "multiple counts");
1057                                         have_count = 1;
1058                                         m->str_count = strtoul(l, &t, 0);
1059                                         l = t - 1;
1060                                         break;
1061                                 }
1062                                 case CHAR_COMPACT_BLANK:
1063                                         m->str_flags |= STRING_COMPACT_BLANK;
1064                                         break;
1065                                 case CHAR_COMPACT_OPTIONAL_BLANK:
1066                                         m->str_flags |=
1067                                             STRING_COMPACT_OPTIONAL_BLANK;
1068                                         break;
1069                                 case CHAR_IGNORE_LOWERCASE:
1070                                         m->str_flags |= STRING_IGNORE_LOWERCASE;
1071                                         break;
1072                                 case CHAR_IGNORE_UPPERCASE:
1073                                         m->str_flags |= STRING_IGNORE_UPPERCASE;
1074                                         break;
1075                                 case CHAR_REGEX_OFFSET_START:
1076                                         m->str_flags |= REGEX_OFFSET_START;
1077                                         break;
1078                                 default:
1079                                         if (ms->flags & MAGIC_CHECK)
1080                                                 file_magwarn(ms,
1081                                                 "string extension `%c' invalid",
1082                                                 *l);
1083                                         return -1;
1084                                 }
1085                                 /* allow multiple '/' for readability */
1086                                 if (l[1] == '/' && !isspace((unsigned char)l[2]))
1087                                         l++;
1088                         }
1089                         if (string_modifier_check(ms, m) == -1)
1090                                 return -1;
1091                 }
1092                 else {
1093                         if (ms->flags & MAGIC_CHECK)
1094                                 file_magwarn(ms, "invalid string op: %c", *t);
1095                         return -1;
1096                 }
1097         }
1098         /*
1099          * We used to set mask to all 1's here, instead let's just not do
1100          * anything if mask = 0 (unless you have a better idea)
1101          */
1102         EATAB;
1103   
1104         switch (*l) {
1105         case '>':
1106         case '<':
1107         /* Old-style anding: "0 byte &0x80 dynamically linked" */
1108         case '&':
1109         case '^':
1110         case '=':
1111                 m->reln = *l;
1112                 ++l;
1113                 if (*l == '=') {
1114                    /* HP compat: ignore &= etc. */
1115                    ++l;
1116                 }
1117                 break;
1118         case '!':
1119                 m->reln = *l;
1120                 ++l;
1121                 break;
1122         default:
1123                 m->reln = '=';  /* the default relation */
1124                 if (*l == 'x' && ((isascii((unsigned char)l[1]) && 
1125                     isspace((unsigned char)l[1])) || !l[1])) {
1126                         m->reln = *l;
1127                         ++l;
1128                 }
1129                 break;
1130         }
1131         /*
1132          * Grab the value part, except for an 'x' reln.
1133          */
1134         if (m->reln != 'x' && getvalue(ms, m, &l, action))
1135                 return -1;
1136
1137         /*
1138          * TODO finish this macro and start using it!
1139          * #define offsetcheck {if (offset > HOWMANY-1) 
1140          *      magwarn("offset too big"); }
1141          */
1142
1143         /*
1144          * Now get last part - the description
1145          */
1146         EATAB;
1147         if (l[0] == '\b') {
1148                 ++l;
1149                 m->nospflag = 1;
1150         } else if ((l[0] == '\\') && (l[1] == 'b')) {
1151                 ++l;
1152                 ++l;
1153                 m->nospflag = 1;
1154         } else
1155                 m->nospflag = 0;
1156         for (i = 0; (m->desc[i++] = *l++) != '\0' && i < sizeof(m->desc); )
1157                 continue;
1158         if (i == sizeof(m->desc)) {
1159                 m->desc[sizeof(m->desc) - 1] = '\0';
1160                 if (ms->flags & MAGIC_CHECK)
1161                         file_magwarn(ms, "description `%s' truncated", m->desc);
1162         }
1163
1164         /*
1165          * We only do this check while compiling, or if any of the magic
1166          * files were not compiled.
1167          */
1168         if (ms->flags & MAGIC_CHECK) {
1169                 if (check_format(ms, m) == -1)
1170                         return -1;
1171         }
1172 #ifndef COMPILE_ONLY
1173         if (action == FILE_CHECK) {
1174                 file_mdump(m);
1175         }
1176 #endif
1177         if (m->cont_level == 0)
1178                 ++(*nmentryp);          /* make room for next */
1179         return 0;
1180 }
1181
1182 private int
1183 check_format_type(const char *ptr, int type)
1184 {
1185         int quad = 0;
1186         if (*ptr == '\0') {
1187                 /* Missing format string; bad */
1188                 return -1;
1189         }
1190
1191         switch (type) {
1192         case FILE_FMT_QUAD:
1193                 quad = 1;
1194                 /*FALLTHROUGH*/
1195         case FILE_FMT_NUM:
1196                 if (*ptr == '-')
1197                         ptr++;
1198                 if (*ptr == '.')
1199                         ptr++;
1200                 while (isdigit((unsigned char)*ptr)) ptr++;
1201                 if (*ptr == '.')
1202                         ptr++;
1203                 while (isdigit((unsigned char)*ptr)) ptr++;
1204                 if (quad) {
1205                         if (*ptr++ != 'l')
1206                                 return -1;
1207                         if (*ptr++ != 'l')
1208                                 return -1;
1209                 }
1210         
1211                 switch (*ptr++) {
1212                 case 'l':
1213                         switch (*ptr++) {
1214                         case 'i':
1215                         case 'd':
1216                         case 'u':
1217                         case 'x':
1218                         case 'X':
1219                                 return 0;
1220                         default:
1221                                 return -1;
1222                         }
1223                 
1224                 case 'h':
1225                         switch (*ptr++) {
1226                         case 'h':
1227                                 switch (*ptr++) {
1228                                 case 'i':
1229                                 case 'd':
1230                                 case 'u':
1231                                 case 'x':
1232                                 case 'X':
1233                                         return 0;
1234                                 default:
1235                                         return -1;
1236                                 }
1237                         case 'd':
1238                                 return 0;
1239                         default:
1240                                 return -1;
1241                         }
1242
1243                 case 'i':
1244                 case 'c':
1245                 case 'd':
1246                 case 'u':
1247                 case 'x':
1248                 case 'X':
1249                         return 0;
1250                         
1251                 default:
1252                         return -1;
1253                 }
1254                 
1255         case FILE_FMT_STR:
1256                 if (*ptr == '-')
1257                         ptr++;
1258                 while (isdigit((unsigned char )*ptr))
1259                         ptr++;
1260                 if (*ptr == '.') {
1261                         ptr++;
1262                         while (isdigit((unsigned char )*ptr))
1263                                 ptr++;
1264                 }
1265                 
1266                 switch (*ptr++) {
1267                 case 's':
1268                         return 0;
1269                 default:
1270                         return -1;
1271                 }
1272                 
1273         default:
1274                 /* internal error */
1275                 abort();
1276         }
1277         /*NOTREACHED*/
1278         return -1;
1279 }
1280         
1281 /*
1282  * Check that the optional printf format in description matches
1283  * the type of the magic.
1284  */
1285 private int
1286 check_format(struct magic_set *ms, struct magic *m)
1287 {
1288         char *ptr;
1289
1290         for (ptr = m->desc; *ptr; ptr++)
1291                 if (*ptr == '%')
1292                         break;
1293         if (*ptr == '\0') {
1294                 /* No format string; ok */
1295                 return 1;
1296         }
1297
1298         assert(file_nformats == file_nnames);
1299
1300         if (m->type >= file_nformats) {
1301                 file_error(ms, 0, "Internal error inconsistency between "
1302                     "m->type and format strings");              
1303                 return -1;
1304         }
1305         if (file_formats[m->type] == FILE_FMT_NONE) {
1306                 file_error(ms, 0, "No format string for `%s' with description "
1307                     "`%s'", m->desc, file_names[m->type]);
1308                 return -1;
1309         }
1310
1311         ptr++;
1312         if (check_format_type(ptr, file_formats[m->type]) == -1) {
1313                 /*
1314                  * TODO: this error message is unhelpful if the format
1315                  * string is not one character long
1316                  */
1317                 file_error(ms, 0, "Printf format `%c' is not valid for type "
1318                     " `%s' in description `%s'", *ptr,
1319                     file_names[m->type], m->desc);
1320                 return -1;
1321         }
1322         
1323         for (; *ptr; ptr++) {
1324                 if (*ptr == '%') {
1325                         file_error(ms, 0,
1326                             "Too many format strings (should have at most one) "
1327                             "for `%s' with description `%s'",
1328                             file_names[m->type], m->desc);
1329                         return -1;
1330                 }
1331         }
1332         return 0;
1333 }
1334
1335 /* 
1336  * Read a numeric value from a pointer, into the value union of a magic 
1337  * pointer, according to the magic type.  Update the string pointer to point 
1338  * just after the number read.  Return 0 for success, non-zero for failure.
1339  */
1340 private int
1341 getvalue(struct magic_set *ms, struct magic *m, const char **p, int action)
1342 {
1343         int slen;
1344
1345         switch (m->type) {
1346         case FILE_BESTRING16:
1347         case FILE_LESTRING16:
1348         case FILE_STRING:
1349         case FILE_PSTRING:
1350         case FILE_REGEX:
1351         case FILE_SEARCH:
1352                 *p = getstr(ms, *p, m->value.s, sizeof(m->value.s), &slen, action);
1353                 if (*p == NULL) {
1354                         if (ms->flags & MAGIC_CHECK)
1355                                 file_magwarn(ms, "cannot get string from `%s'",
1356                                     m->value.s);
1357                         return -1;
1358                 }
1359                 m->vallen = slen;
1360                 return 0;
1361         default:
1362                 if (m->reln != 'x') {
1363                         char *ep;
1364                         m->value.q = file_signextend(ms, m,
1365                             (uint64_t)strtoull(*p, &ep, 0));
1366                         *p = ep;
1367                         eatsize(p);
1368                 }
1369                 return 0;
1370         }
1371 }
1372
1373 /*
1374  * Convert a string containing C character escapes.  Stop at an unescaped
1375  * space or tab.
1376  * Copy the converted version to "p", returning its length in *slen.
1377  * Return updated scan pointer as function result.
1378  */
1379 private const char *
1380 getstr(struct magic_set *ms, const char *s, char *p, int plen, int *slen, int action)
1381 {
1382         const char *origs = s;
1383         char    *origp = p;
1384         char    *pmax = p + plen - 1;
1385         int     c;
1386         int     val;
1387
1388         while ((c = *s++) != '\0') {
1389                 if (isspace((unsigned char) c))
1390                         break;
1391                 if (p >= pmax) {
1392                         file_error(ms, 0, "string too long: `%s'", origs);
1393                         return NULL;
1394                 }
1395                 if (c == '\\') {
1396                         switch(c = *s++) {
1397
1398                         case '\0':
1399                                 if (action == FILE_COMPILE)
1400                                         file_magwarn(ms, "incomplete escape");
1401                                 goto out;
1402
1403                         case '\t':
1404                                 if (action == FILE_COMPILE) {
1405                                         file_magwarn(ms,
1406                                             "escaped tab found, use \\t instead");
1407                                         action++;
1408                                 }
1409                                 /*FALLTHROUGH*/
1410                         default:
1411                                 if (action == FILE_COMPILE) {
1412                                         if (isprint((unsigned char)c))
1413                                             file_magwarn(ms,
1414                                                 "no need to escape `%c'", c);
1415                                         else
1416                                             file_magwarn(ms,
1417                                                 "unknown escape sequence: \\%03o", c);
1418                                 }
1419                                 /*FALLTHROUGH*/
1420                         /* space, perhaps force people to use \040? */
1421                         case ' ':
1422 #if 0
1423                         /*
1424                          * Other things people escape, but shouldn't need to,
1425                          * so we disallow them
1426                          */
1427                         case '\'':
1428                         case '"':
1429                         case '?':
1430 #endif
1431                         /* Relations */
1432                         case '>':
1433                         case '<':
1434                         case '&':
1435                         case '^':
1436                         case '=':
1437                         case '!':
1438                         /* and baskslash itself */
1439                         case '\\':
1440                                 *p++ = (char) c;
1441                                 break;
1442
1443                         case 'a':
1444                                 *p++ = '\a';
1445                                 break;
1446
1447                         case 'b':
1448                                 *p++ = '\b';
1449                                 break;
1450
1451                         case 'f':
1452                                 *p++ = '\f';
1453                                 break;
1454
1455                         case 'n':
1456                                 *p++ = '\n';
1457                                 break;
1458
1459                         case 'r':
1460                                 *p++ = '\r';
1461                                 break;
1462
1463                         case 't':
1464                                 *p++ = '\t';
1465                                 break;
1466
1467                         case 'v':
1468                                 *p++ = '\v';
1469                                 break;
1470
1471                         /* \ and up to 3 octal digits */
1472                         case '0':
1473                         case '1':
1474                         case '2':
1475                         case '3':
1476                         case '4':
1477                         case '5':
1478                         case '6':
1479                         case '7':
1480                                 val = c - '0';
1481                                 c = *s++;  /* try for 2 */
1482                                 if (c >= '0' && c <= '7') {
1483                                         val = (val << 3) | (c - '0');
1484                                         c = *s++;  /* try for 3 */
1485                                         if (c >= '0' && c <= '7')
1486                                                 val = (val << 3) | (c-'0');
1487                                         else
1488                                                 --s;
1489                                 }
1490                                 else
1491                                         --s;
1492                                 *p++ = (char)val;
1493                                 break;
1494
1495                         /* \x and up to 2 hex digits */
1496                         case 'x':
1497                                 val = 'x';      /* Default if no digits */
1498                                 c = hextoint(*s++);     /* Get next char */
1499                                 if (c >= 0) {
1500                                         val = c;
1501                                         c = hextoint(*s++);
1502                                         if (c >= 0)
1503                                                 val = (val << 4) + c;
1504                                         else
1505                                                 --s;
1506                                 } else
1507                                         --s;
1508                                 *p++ = (char)val;
1509                                 break;
1510                         }
1511                 } else
1512                         *p++ = (char)c;
1513         }
1514 out:
1515         *p = '\0';
1516         *slen = p - origp;
1517         return s;
1518 }
1519
1520
1521 /* Single hex char to int; -1 if not a hex char. */
1522 private int
1523 hextoint(int c)
1524 {
1525         if (!isascii((unsigned char) c))
1526                 return -1;
1527         if (isdigit((unsigned char) c))
1528                 return c - '0';
1529         if ((c >= 'a') && (c <= 'f'))
1530                 return c + 10 - 'a';
1531         if (( c>= 'A') && (c <= 'F'))
1532                 return c + 10 - 'A';
1533         return -1;
1534 }
1535
1536
1537 /*
1538  * Print a string containing C character escapes.
1539  */
1540 protected void
1541 file_showstr(FILE *fp, const char *s, size_t len)
1542 {
1543         char    c;
1544
1545         for (;;) {
1546                 c = *s++;
1547                 if (len == ~0U) {
1548                         if (c == '\0')
1549                                 break;
1550                 }
1551                 else  {
1552                         if (len-- == 0)
1553                                 break;
1554                 }
1555                 if (c >= 040 && c <= 0176)      /* TODO isprint && !iscntrl */
1556                         (void) fputc(c, fp);
1557                 else {
1558                         (void) fputc('\\', fp);
1559                         switch (c) {
1560                         case '\a':
1561                                 (void) fputc('a', fp);
1562                                 break;
1563
1564                         case '\b':
1565                                 (void) fputc('b', fp);
1566                                 break;
1567
1568                         case '\f':
1569                                 (void) fputc('f', fp);
1570                                 break;
1571
1572                         case '\n':
1573                                 (void) fputc('n', fp);
1574                                 break;
1575
1576                         case '\r':
1577                                 (void) fputc('r', fp);
1578                                 break;
1579
1580                         case '\t':
1581                                 (void) fputc('t', fp);
1582                                 break;
1583
1584                         case '\v':
1585                                 (void) fputc('v', fp);
1586                                 break;
1587
1588                         default:
1589                                 (void) fprintf(fp, "%.3o", c & 0377);
1590                                 break;
1591                         }
1592                 }
1593         }
1594 }
1595
1596 /*
1597  * eatsize(): Eat the size spec from a number [eg. 10UL]
1598  */
1599 private void
1600 eatsize(const char **p)
1601 {
1602         const char *l = *p;
1603
1604         if (LOWCASE(*l) == 'u') 
1605                 l++;
1606
1607         switch (LOWCASE(*l)) {
1608         case 'l':    /* long */
1609         case 's':    /* short */
1610         case 'h':    /* short */
1611         case 'b':    /* char/byte */
1612         case 'c':    /* char/byte */
1613                 l++;
1614                 /*FALLTHROUGH*/
1615         default:
1616                 break;
1617         }
1618
1619         *p = l;
1620 }
1621
1622 /*
1623  * handle a compiled file.
1624  */
1625 private int
1626 apprentice_map(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp,
1627     const char *fn)
1628 {
1629         int fd;
1630         struct stat st;
1631         uint32_t *ptr;
1632         uint32_t version;
1633         int needsbyteswap;
1634         char buf[MAXPATHLEN];
1635         char *dbname = mkdbname(fn, buf, sizeof(buf), 0);
1636         void *mm = NULL;
1637
1638         if (dbname == NULL)
1639                 return -1;
1640
1641         if ((fd = open(dbname, O_RDONLY|O_BINARY)) == -1)
1642                 return -1;
1643
1644         if (fstat(fd, &st) == -1) {
1645                 file_error(ms, errno, "cannot stat `%s'", dbname);
1646                 goto error;
1647         }
1648         if (st.st_size < 16) {
1649                 file_error(ms, 0, "file `%s' is too small", dbname);
1650                 goto error;
1651         }
1652
1653 #ifdef QUICK
1654         if ((mm = mmap(0, (size_t)st.st_size, PROT_READ|PROT_WRITE,
1655             MAP_PRIVATE|MAP_FILE, fd, (off_t)0)) == MAP_FAILED) {
1656                 file_error(ms, errno, "cannot map `%s'", dbname);
1657                 goto error;
1658         }
1659 #define RET     2
1660 #else
1661         if ((mm = malloc((size_t)st.st_size)) == NULL) {
1662                 file_oomem(ms, (size_t)st.st_size);
1663                 goto error;
1664         }
1665         if (read(fd, mm, (size_t)st.st_size) != (size_t)st.st_size) {
1666                 file_badread(ms);
1667                 goto error;
1668         }
1669 #define RET     1
1670 #endif
1671         *magicp = mm;
1672         (void)close(fd);
1673         fd = -1;
1674         ptr = (uint32_t *)(void *)*magicp;
1675         if (*ptr != MAGICNO) {
1676                 if (swap4(*ptr) != MAGICNO) {
1677                         file_error(ms, 0, "bad magic in `%s'");
1678                         goto error;
1679                 }
1680                 needsbyteswap = 1;
1681         } else
1682                 needsbyteswap = 0;
1683         if (needsbyteswap)
1684                 version = swap4(ptr[1]);
1685         else
1686                 version = ptr[1];
1687         if (version != VERSIONNO) {
1688                 file_error(ms, 0, "version mismatch (%d != %d) in `%s'",
1689                     version, VERSIONNO, dbname);
1690                 goto error;
1691         }
1692         *nmagicp = (uint32_t)(st.st_size / sizeof(struct magic)) - 1;
1693         (*magicp)++;
1694         if (needsbyteswap)
1695                 byteswap(*magicp, *nmagicp);
1696         return RET;
1697
1698 error:
1699         if (fd != -1)
1700                 (void)close(fd);
1701         if (mm) {
1702 #ifdef QUICK
1703                 (void)munmap((void *)mm, (size_t)st.st_size);
1704 #else
1705                 free(mm);
1706 #endif
1707         } else {
1708                 *magicp = NULL;
1709                 *nmagicp = 0;
1710         }
1711         return -1;
1712 }
1713
1714 private const uint32_t ar[] = {
1715     MAGICNO, VERSIONNO
1716 };
1717 /*
1718  * handle an mmaped file.
1719  */
1720 private int
1721 apprentice_compile(struct magic_set *ms, struct magic **magicp,
1722     uint32_t *nmagicp, const char *fn)
1723 {
1724         int fd;
1725         char buf[MAXPATHLEN];
1726         char *dbname = mkdbname(fn, buf, sizeof(buf), 1);
1727
1728         if (dbname == NULL) 
1729                 return -1;
1730
1731         if ((fd = open(dbname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644)) == -1) {
1732                 file_error(ms, errno, "cannot open `%s'", dbname);
1733                 return -1;
1734         }
1735
1736         if (write(fd, ar, sizeof(ar)) != (ssize_t)sizeof(ar)) {
1737                 file_error(ms, errno, "error writing `%s'", dbname);
1738                 return -1;
1739         }
1740
1741         if (lseek(fd, (off_t)sizeof(struct magic), SEEK_SET)
1742             != sizeof(struct magic)) {
1743                 file_error(ms, errno, "error seeking `%s'", dbname);
1744                 return -1;
1745         }
1746
1747         if (write(fd, *magicp, (sizeof(struct magic) * *nmagicp)) 
1748             != (ssize_t)(sizeof(struct magic) * *nmagicp)) {
1749                 file_error(ms, errno, "error writing `%s'", dbname);
1750                 return -1;
1751         }
1752
1753         (void)close(fd);
1754         return 0;
1755 }
1756
1757 private const char ext[] = ".mgc";
1758 /*
1759  * make a dbname
1760  */
1761 private char *
1762 mkdbname(const char *fn, char *buf, size_t bufsiz, int strip)
1763 {
1764         if (strip) {
1765                 const char *p;
1766                 if ((p = strrchr(fn, '/')) != NULL)
1767                         fn = ++p;
1768         }
1769
1770         (void)snprintf(buf, bufsiz, "%s%s", fn, ext);
1771         return buf;
1772 }
1773
1774 /*
1775  * Byteswap an mmap'ed file if needed
1776  */
1777 private void
1778 byteswap(struct magic *magic, uint32_t nmagic)
1779 {
1780         uint32_t i;
1781         for (i = 0; i < nmagic; i++)
1782                 bs1(&magic[i]);
1783 }
1784
1785 /*
1786  * swap a short
1787  */
1788 private uint16_t
1789 swap2(uint16_t sv)
1790 {
1791         uint16_t rv;
1792         uint8_t *s = (uint8_t *)(void *)&sv; 
1793         uint8_t *d = (uint8_t *)(void *)&rv; 
1794         d[0] = s[1];
1795         d[1] = s[0];
1796         return rv;
1797 }
1798
1799 /*
1800  * swap an int
1801  */
1802 private uint32_t
1803 swap4(uint32_t sv)
1804 {
1805         uint32_t rv;
1806         uint8_t *s = (uint8_t *)(void *)&sv; 
1807         uint8_t *d = (uint8_t *)(void *)&rv; 
1808         d[0] = s[3];
1809         d[1] = s[2];
1810         d[2] = s[1];
1811         d[3] = s[0];
1812         return rv;
1813 }
1814
1815 /*
1816  * swap a quad
1817  */
1818 private uint64_t
1819 swap8(uint64_t sv)
1820 {
1821         uint32_t rv;
1822         uint8_t *s = (uint8_t *)(void *)&sv; 
1823         uint8_t *d = (uint8_t *)(void *)&rv; 
1824         d[0] = s[3];
1825         d[1] = s[2];
1826         d[2] = s[1];
1827         d[3] = s[0];
1828         d[4] = s[7];
1829         d[5] = s[6];
1830         d[6] = s[5];
1831         d[7] = s[4];
1832         return rv;
1833 }
1834
1835 /*
1836  * byteswap a single magic entry
1837  */
1838 private void
1839 bs1(struct magic *m)
1840 {
1841         m->cont_level = swap2(m->cont_level);
1842         m->offset = swap4((uint32_t)m->offset);
1843         m->in_offset = swap4((uint32_t)m->in_offset);
1844         m->lineno = swap4((uint32_t)m->lineno);
1845         if (IS_STRING(m->type)) {
1846                 m->str_count = swap4(m->str_count);
1847                 m->str_flags = swap4(m->str_flags);
1848         }
1849         else {
1850                 m->value.q = swap8(m->value.q);
1851                 m->num_mask = swap8(m->num_mask);
1852         }
1853 }