apprentice.c revision 175296
168349Sobrien/* 2133359Sobrien * Copyright (c) Ian F. Darwin 1986-1995. 3133359Sobrien * Software written by Ian F. Darwin and others; 4133359Sobrien * maintained 1995-present by Christos Zoulas and others. 5133359Sobrien * 6133359Sobrien * Redistribution and use in source and binary forms, with or without 7133359Sobrien * modification, are permitted provided that the following conditions 8133359Sobrien * are met: 9133359Sobrien * 1. Redistributions of source code must retain the above copyright 10133359Sobrien * notice immediately at the beginning of the file, without modification, 11133359Sobrien * this list of conditions, and the following disclaimer. 12133359Sobrien * 2. Redistributions in binary form must reproduce the above copyright 13133359Sobrien * notice, this list of conditions and the following disclaimer in the 14133359Sobrien * documentation and/or other materials provided with the distribution. 15133359Sobrien * 16133359Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17133359Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18133359Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19133359Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 20133359Sobrien * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21133359Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22133359Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23133359Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24133359Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25133359Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26133359Sobrien * SUCH DAMAGE. 27133359Sobrien */ 28133359Sobrien/* 2968349Sobrien * apprentice - make one pass through /etc/magic, learning its secrets. 3068349Sobrien */ 3168349Sobrien 3280588Sobrien#include "file.h" 33133359Sobrien#include "magic.h" 34175296Sobrien#include "patchlevel.h" 3568349Sobrien#include <stdlib.h> 3684685Sobrien#ifdef HAVE_UNISTD_H 3784685Sobrien#include <unistd.h> 3884685Sobrien#endif 3968349Sobrien#include <string.h> 40169942Sobrien#include <assert.h> 4168349Sobrien#include <ctype.h> 42103373Sobrien#include <fcntl.h> 43133359Sobrien#include <sys/stat.h> 44133359Sobrien#include <sys/param.h> 4574784Sobrien#ifdef QUICK 4674784Sobrien#include <sys/mman.h> 4774784Sobrien#endif 4868349Sobrien 4968349Sobrien#ifndef lint 50175296SobrienFILE_RCSID("@(#)$File: apprentice.c,v 1.109 2007/12/27 20:52:36 christos Exp $") 5168349Sobrien#endif /* lint */ 5268349Sobrien 5368349Sobrien#define EATAB {while (isascii((unsigned char) *l) && \ 5468349Sobrien isspace((unsigned char) *l)) ++l;} 5568349Sobrien#define LOWCASE(l) (isupper((unsigned char) (l)) ? \ 5668349Sobrien tolower((unsigned char) (l)) : (l)) 5775937Sobrien/* 5875937Sobrien * Work around a bug in headers on Digital Unix. 5975937Sobrien * At least confirmed for: OSF1 V4.0 878 6075937Sobrien */ 6175937Sobrien#if defined(__osf__) && defined(__DECC) 6275937Sobrien#ifdef MAP_FAILED 6375937Sobrien#undef MAP_FAILED 6475937Sobrien#endif 6575937Sobrien#endif 6668349Sobrien 6775937Sobrien#ifndef MAP_FAILED 6875937Sobrien#define MAP_FAILED (void *) -1 6975937Sobrien#endif 7068349Sobrien 7175937Sobrien#ifndef MAP_FILE 7275937Sobrien#define MAP_FILE 0 7375937Sobrien#endif 7475937Sobrien 75133359Sobrien#ifndef MAXPATHLEN 76133359Sobrien#define MAXPATHLEN 1024 7768349Sobrien#endif 7868349Sobrien 79159764Sobrienstruct magic_entry { 80159764Sobrien struct magic *mp; 81159764Sobrien uint32_t cont_count; 82159764Sobrien uint32_t max_count; 83159764Sobrien}; 84159764Sobrien 85169962Sobrienint file_formats[FILE_NAMES_SIZE]; 86169962Sobrienconst size_t file_nformats = FILE_NAMES_SIZE; 87169962Sobrienconst char *file_names[FILE_NAMES_SIZE]; 88169962Sobrienconst size_t file_nnames = FILE_NAMES_SIZE; 89169942Sobrien 90169962Sobrienprivate int getvalue(struct magic_set *ms, struct magic *, const char **, int); 91133359Sobrienprivate int hextoint(int); 92159764Sobrienprivate const char *getstr(struct magic_set *, const char *, char *, int, 93169962Sobrien int *, int); 94159764Sobrienprivate int parse(struct magic_set *, struct magic_entry **, uint32_t *, 95169942Sobrien const char *, size_t, int); 96159764Sobrienprivate void eatsize(const char **); 97133359Sobrienprivate int apprentice_1(struct magic_set *, const char *, int, struct mlist *); 98159764Sobrienprivate size_t apprentice_magic_strength(const struct magic *); 99159764Sobrienprivate int apprentice_sort(const void *, const void *); 100133359Sobrienprivate int apprentice_file(struct magic_set *, struct magic **, uint32_t *, 101133359Sobrien const char *, int); 102133359Sobrienprivate void byteswap(struct magic *, uint32_t); 103133359Sobrienprivate void bs1(struct magic *); 104133359Sobrienprivate uint16_t swap2(uint16_t); 105133359Sobrienprivate uint32_t swap4(uint32_t); 106169942Sobrienprivate uint64_t swap8(uint64_t); 107139368Sobrienprivate char *mkdbname(const char *, char *, size_t, int); 108133359Sobrienprivate int apprentice_map(struct magic_set *, struct magic **, uint32_t *, 109133359Sobrien const char *); 110133359Sobrienprivate int apprentice_compile(struct magic_set *, struct magic **, uint32_t *, 111133359Sobrien const char *); 112169942Sobrienprivate int check_format_type(const char *, int); 113139368Sobrienprivate int check_format(struct magic_set *, struct magic *); 114175296Sobrienprivate int get_op(char); 11568349Sobrien 116133359Sobrienprivate size_t maxmagic = 0; 117133359Sobrienprivate size_t magicsize = sizeof(struct magic); 11868349Sobrien 119159764Sobrien 12080588Sobrien#ifdef COMPILE_ONLY 12174784Sobrien 122103373Sobrienint main(int, char *[]); 12380588Sobrien 12480588Sobrienint 125103373Sobrienmain(int argc, char *argv[]) 12680588Sobrien{ 12780588Sobrien int ret; 128133359Sobrien struct magic_set *ms; 129133359Sobrien char *progname; 13080588Sobrien 13180588Sobrien if ((progname = strrchr(argv[0], '/')) != NULL) 13280588Sobrien progname++; 13380588Sobrien else 13480588Sobrien progname = argv[0]; 13580588Sobrien 13680588Sobrien if (argc != 2) { 137133359Sobrien (void)fprintf(stderr, "Usage: %s file\n", progname); 138133359Sobrien return 1; 13980588Sobrien } 14080588Sobrien 141133359Sobrien if ((ms = magic_open(MAGIC_CHECK)) == NULL) { 142133359Sobrien (void)fprintf(stderr, "%s: %s\n", progname, strerror(errno)); 143133359Sobrien return 1; 144133359Sobrien } 145133359Sobrien ret = magic_compile(ms, argv[1]) == -1 ? 1 : 0; 146133359Sobrien if (ret == 1) 147133359Sobrien (void)fprintf(stderr, "%s: %s\n", progname, magic_error(ms)); 148133359Sobrien magic_close(ms); 149133359Sobrien return ret; 15080588Sobrien} 15180588Sobrien#endif /* COMPILE_ONLY */ 15280588Sobrien 153169962Sobrienstatic const struct type_tbl_s { 154169962Sobrien const char *name; 155169962Sobrien const size_t len; 156169962Sobrien const int type; 157169962Sobrien const int format; 158169962Sobrien} type_tbl[] = { 159169962Sobrien# define XX(s) s, (sizeof(s) - 1) 160169962Sobrien# define XX_NULL NULL, 0 161169962Sobrien { XX("byte"), FILE_BYTE, FILE_FMT_NUM }, 162169962Sobrien { XX("short"), FILE_SHORT, FILE_FMT_NUM }, 163169962Sobrien { XX("default"), FILE_DEFAULT, FILE_FMT_STR }, 164169962Sobrien { XX("long"), FILE_LONG, FILE_FMT_NUM }, 165169962Sobrien { XX("string"), FILE_STRING, FILE_FMT_STR }, 166169962Sobrien { XX("date"), FILE_DATE, FILE_FMT_STR }, 167169962Sobrien { XX("beshort"), FILE_BESHORT, FILE_FMT_NUM }, 168169962Sobrien { XX("belong"), FILE_BELONG, FILE_FMT_NUM }, 169169962Sobrien { XX("bedate"), FILE_BEDATE, FILE_FMT_STR }, 170169962Sobrien { XX("leshort"), FILE_LESHORT, FILE_FMT_NUM }, 171169962Sobrien { XX("lelong"), FILE_LELONG, FILE_FMT_NUM }, 172169962Sobrien { XX("ledate"), FILE_LEDATE, FILE_FMT_STR }, 173169962Sobrien { XX("pstring"), FILE_PSTRING, FILE_FMT_STR }, 174169962Sobrien { XX("ldate"), FILE_LDATE, FILE_FMT_STR }, 175169962Sobrien { XX("beldate"), FILE_BELDATE, FILE_FMT_STR }, 176169962Sobrien { XX("leldate"), FILE_LELDATE, FILE_FMT_STR }, 177169962Sobrien { XX("regex"), FILE_REGEX, FILE_FMT_STR }, 178169962Sobrien { XX("bestring16"), FILE_BESTRING16, FILE_FMT_STR }, 179169962Sobrien { XX("lestring16"), FILE_LESTRING16, FILE_FMT_STR }, 180169962Sobrien { XX("search"), FILE_SEARCH, FILE_FMT_STR }, 181169962Sobrien { XX("medate"), FILE_MEDATE, FILE_FMT_STR }, 182169962Sobrien { XX("meldate"), FILE_MELDATE, FILE_FMT_STR }, 183169962Sobrien { XX("melong"), FILE_MELONG, FILE_FMT_NUM }, 184169962Sobrien { XX("quad"), FILE_QUAD, FILE_FMT_QUAD }, 185169962Sobrien { XX("lequad"), FILE_LEQUAD, FILE_FMT_QUAD }, 186169962Sobrien { XX("bequad"), FILE_BEQUAD, FILE_FMT_QUAD }, 187169962Sobrien { XX("qdate"), FILE_QDATE, FILE_FMT_STR }, 188169962Sobrien { XX("leqdate"), FILE_LEQDATE, FILE_FMT_STR }, 189169962Sobrien { XX("beqdate"), FILE_BEQDATE, FILE_FMT_STR }, 190169962Sobrien { XX("qldate"), FILE_QLDATE, FILE_FMT_STR }, 191169962Sobrien { XX("leqldate"), FILE_LEQLDATE, FILE_FMT_STR }, 192169962Sobrien { XX("beqldate"), FILE_BEQLDATE, FILE_FMT_STR }, 193175296Sobrien { XX("float"), FILE_FLOAT, FILE_FMT_FLOAT }, 194175296Sobrien { XX("befloat"), FILE_BEFLOAT, FILE_FMT_FLOAT }, 195175296Sobrien { XX("lefloat"), FILE_LEFLOAT, FILE_FMT_FLOAT }, 196175296Sobrien { XX("double"), FILE_DOUBLE, FILE_FMT_DOUBLE }, 197175296Sobrien { XX("bedouble"), FILE_BEDOUBLE, FILE_FMT_DOUBLE }, 198175296Sobrien { XX("ledouble"), FILE_LEDOUBLE, FILE_FMT_DOUBLE }, 199169962Sobrien { XX_NULL, FILE_INVALID, FILE_FMT_NONE }, 200169962Sobrien# undef XX 201169962Sobrien# undef XX_NULL 202169962Sobrien}; 20380588Sobrien 204169962Sobrienprivate int 205169962Sobrienget_type(const char *l, const char **t) 206169962Sobrien{ 207169962Sobrien const struct type_tbl_s *p; 208169962Sobrien 209169962Sobrien for (p = type_tbl; p->name; p++) { 210169962Sobrien if (strncmp(l, p->name, p->len) == 0) { 211169962Sobrien if (t) 212169962Sobrien *t = l + p->len; 213169962Sobrien break; 214169962Sobrien } 215169962Sobrien } 216169962Sobrien return p->type; 217169962Sobrien} 218169962Sobrien 219169962Sobrienprivate void 220169962Sobrieninit_file_tables(void) 221169962Sobrien{ 222169962Sobrien static int done = 0; 223169962Sobrien const struct type_tbl_s *p; 224169962Sobrien 225169962Sobrien if (done) 226169962Sobrien return; 227169962Sobrien done++; 228169962Sobrien 229169962Sobrien for (p = type_tbl; p->name; p++) { 230169962Sobrien assert(p->type < FILE_NAMES_SIZE); 231169962Sobrien file_names[p->type] = p->name; 232169962Sobrien file_formats[p->type] = p->format; 233169962Sobrien } 234169962Sobrien} 235169962Sobrien 23674784Sobrien/* 23774784Sobrien * Handle one file. 23874784Sobrien */ 239133359Sobrienprivate int 240133359Sobrienapprentice_1(struct magic_set *ms, const char *fn, int action, 241133359Sobrien struct mlist *mlist) 24274784Sobrien{ 24374784Sobrien struct magic *magic = NULL; 244103373Sobrien uint32_t nmagic = 0; 24574784Sobrien struct mlist *ml; 24674784Sobrien int rv = -1; 247133359Sobrien int mapped; 24874784Sobrien 249133359Sobrien if (magicsize != FILE_MAGICSIZE) { 250133359Sobrien file_error(ms, 0, "magic element size %lu != %lu", 251133359Sobrien (unsigned long)sizeof(*magic), 252133359Sobrien (unsigned long)FILE_MAGICSIZE); 253133359Sobrien return -1; 25474784Sobrien } 255133359Sobrien 256133359Sobrien if (action == FILE_COMPILE) { 257133359Sobrien rv = apprentice_file(ms, &magic, &nmagic, fn, action); 258133359Sobrien if (rv != 0) 259133359Sobrien return -1; 260133359Sobrien rv = apprentice_compile(ms, &magic, &nmagic, fn); 261133359Sobrien free(magic); 262133359Sobrien return rv; 263133359Sobrien } 264159764Sobrien 26580588Sobrien#ifndef COMPILE_ONLY 266133359Sobrien if ((rv = apprentice_map(ms, &magic, &nmagic, fn)) == -1) { 267133359Sobrien if (ms->flags & MAGIC_CHECK) 268139368Sobrien file_magwarn(ms, "using regular magic file `%s'", fn); 269133359Sobrien rv = apprentice_file(ms, &magic, &nmagic, fn, action); 270133359Sobrien if (rv != 0) 271133359Sobrien return -1; 272133359Sobrien } 27374784Sobrien 274133359Sobrien mapped = rv; 27574784Sobrien 276133359Sobrien if (magic == NULL || nmagic == 0) { 277133359Sobrien file_delmagic(magic, mapped, nmagic); 278133359Sobrien return -1; 279133359Sobrien } 280133359Sobrien 28174784Sobrien if ((ml = malloc(sizeof(*ml))) == NULL) { 282133359Sobrien file_delmagic(magic, mapped, nmagic); 283169942Sobrien file_oomem(ms, sizeof(*ml)); 284133359Sobrien return -1; 28574784Sobrien } 28674784Sobrien 28774784Sobrien ml->magic = magic; 28874784Sobrien ml->nmagic = nmagic; 289133359Sobrien ml->mapped = mapped; 29074784Sobrien 291133359Sobrien mlist->prev->next = ml; 292133359Sobrien ml->prev = mlist->prev; 293133359Sobrien ml->next = mlist; 294133359Sobrien mlist->prev = ml; 29574784Sobrien 296133359Sobrien return 0; 29780588Sobrien#endif /* COMPILE_ONLY */ 29874784Sobrien} 29974784Sobrien 300133359Sobrienprotected void 301133359Sobrienfile_delmagic(struct magic *p, int type, size_t entries) 302133359Sobrien{ 303133359Sobrien if (p == NULL) 304133359Sobrien return; 305133359Sobrien switch (type) { 306175296Sobrien#ifdef QUICK 307133359Sobrien case 2: 308133359Sobrien p--; 309133359Sobrien (void)munmap((void *)p, sizeof(*p) * (entries + 1)); 310133359Sobrien break; 311175296Sobrien#endif 312133359Sobrien case 1: 313133359Sobrien p--; 314133359Sobrien /*FALLTHROUGH*/ 315133359Sobrien case 0: 316133359Sobrien free(p); 317133359Sobrien break; 318133359Sobrien default: 319133359Sobrien abort(); 320133359Sobrien } 321133359Sobrien} 32274784Sobrien 323103373Sobrien/* const char *fn: list of magic files */ 324133359Sobrienprotected struct mlist * 325133359Sobrienfile_apprentice(struct magic_set *ms, const char *fn, int action) 32668349Sobrien{ 327133359Sobrien char *p, *mfn, *afn = NULL; 32868349Sobrien int file_err, errs = -1; 329133359Sobrien struct mlist *mlist; 330169942Sobrien static const char mime[] = ".mime"; 33168349Sobrien 332169962Sobrien init_file_tables(); 333169962Sobrien 334133359Sobrien if (fn == NULL) 335133359Sobrien fn = getenv("MAGIC"); 336133359Sobrien if (fn == NULL) 337133359Sobrien fn = MAGIC; 338133359Sobrien 339169962Sobrien if ((mfn = strdup(fn)) == NULL) { 340169942Sobrien file_oomem(ms, strlen(fn)); 341133359Sobrien return NULL; 34268349Sobrien } 343169962Sobrien fn = mfn; 344133359Sobrien 345133359Sobrien if ((mlist = malloc(sizeof(*mlist))) == NULL) { 346133359Sobrien free(mfn); 347169942Sobrien file_oomem(ms, sizeof(*mlist)); 348133359Sobrien return NULL; 349133359Sobrien } 350133359Sobrien mlist->next = mlist->prev = mlist; 351133359Sobrien 35268349Sobrien while (fn) { 35368349Sobrien p = strchr(fn, PATHSEP); 35468349Sobrien if (p) 35568349Sobrien *p++ = '\0'; 356133359Sobrien if (*fn == '\0') 357133359Sobrien break; 358133359Sobrien if (ms->flags & MAGIC_MIME) { 359169942Sobrien size_t len = strlen(fn) + sizeof(mime); 360169942Sobrien if ((afn = malloc(len)) == NULL) { 361133359Sobrien free(mfn); 362133359Sobrien free(mlist); 363169942Sobrien file_oomem(ms, len); 364133359Sobrien return NULL; 365133359Sobrien } 366133359Sobrien (void)strcpy(afn, fn); 367169942Sobrien (void)strcat(afn, mime); 368133359Sobrien fn = afn; 369133359Sobrien } 370133359Sobrien file_err = apprentice_1(ms, fn, action, mlist); 37168349Sobrien if (file_err > errs) 37268349Sobrien errs = file_err; 373133359Sobrien if (afn) { 374133359Sobrien free(afn); 375133359Sobrien afn = NULL; 376133359Sobrien } 37768349Sobrien fn = p; 37868349Sobrien } 379133359Sobrien if (errs == -1) { 380133359Sobrien free(mfn); 381133359Sobrien free(mlist); 382133359Sobrien mlist = NULL; 383133359Sobrien file_error(ms, 0, "could not find any magic files!"); 384133359Sobrien return NULL; 385133359Sobrien } 38668349Sobrien free(mfn); 387133359Sobrien return mlist; 38868349Sobrien} 38968349Sobrien 390169942Sobrien/* 391169942Sobrien * Get weight of this magic entry, for sorting purposes. 392169942Sobrien */ 393159764Sobrienprivate size_t 394159764Sobrienapprentice_magic_strength(const struct magic *m) 395159764Sobrien{ 396169942Sobrien#define MULT 10 397169942Sobrien size_t val = 2 * MULT; /* baseline strength */ 398169942Sobrien 399159764Sobrien switch (m->type) { 400169962Sobrien case FILE_DEFAULT: /* make sure this sorts last */ 401169962Sobrien return 0; 402169962Sobrien 403159764Sobrien case FILE_BYTE: 404169942Sobrien val += 1 * MULT; 405169942Sobrien break; 406159764Sobrien 407159764Sobrien case FILE_SHORT: 408159764Sobrien case FILE_LESHORT: 409159764Sobrien case FILE_BESHORT: 410169942Sobrien val += 2 * MULT; 411169942Sobrien break; 412159764Sobrien 413159764Sobrien case FILE_LONG: 414159764Sobrien case FILE_LELONG: 415159764Sobrien case FILE_BELONG: 416159764Sobrien case FILE_MELONG: 417169942Sobrien val += 4 * MULT; 418169942Sobrien break; 419159764Sobrien 420159764Sobrien case FILE_PSTRING: 421159764Sobrien case FILE_STRING: 422169942Sobrien val += m->vallen * MULT; 423169942Sobrien break; 424169942Sobrien 425159764Sobrien case FILE_BESTRING16: 426159764Sobrien case FILE_LESTRING16: 427169942Sobrien val += m->vallen * MULT / 2; 428169942Sobrien break; 429169942Sobrien 430159764Sobrien case FILE_SEARCH: 431169942Sobrien case FILE_REGEX: 432169942Sobrien val += m->vallen; 433169942Sobrien break; 434159764Sobrien 435159764Sobrien case FILE_DATE: 436159764Sobrien case FILE_LEDATE: 437159764Sobrien case FILE_BEDATE: 438159764Sobrien case FILE_MEDATE: 439159764Sobrien case FILE_LDATE: 440159764Sobrien case FILE_LELDATE: 441159764Sobrien case FILE_BELDATE: 442159764Sobrien case FILE_MELDATE: 443175296Sobrien case FILE_FLOAT: 444175296Sobrien case FILE_BEFLOAT: 445175296Sobrien case FILE_LEFLOAT: 446169942Sobrien val += 4 * MULT; 447169942Sobrien break; 448159764Sobrien 449169942Sobrien case FILE_QUAD: 450169942Sobrien case FILE_BEQUAD: 451169942Sobrien case FILE_LEQUAD: 452169942Sobrien case FILE_QDATE: 453169942Sobrien case FILE_LEQDATE: 454169942Sobrien case FILE_BEQDATE: 455169942Sobrien case FILE_QLDATE: 456169942Sobrien case FILE_LEQLDATE: 457169942Sobrien case FILE_BEQLDATE: 458175296Sobrien case FILE_DOUBLE: 459175296Sobrien case FILE_BEDOUBLE: 460175296Sobrien case FILE_LEDOUBLE: 461169942Sobrien val += 8 * MULT; 462169942Sobrien break; 463169942Sobrien 464159764Sobrien default: 465169942Sobrien val = 0; 466169942Sobrien (void)fprintf(stderr, "Bad type %d\n", m->type); 467169942Sobrien abort(); 468159764Sobrien } 469169942Sobrien 470169942Sobrien switch (m->reln) { 471169942Sobrien case 'x': /* matches anything penalize */ 472169942Sobrien val = 0; 473169942Sobrien break; 474169942Sobrien 475169942Sobrien case '!': 476169942Sobrien case '=': /* Exact match, prefer */ 477169942Sobrien val += MULT; 478169942Sobrien break; 479169942Sobrien 480169942Sobrien case '>': 481169942Sobrien case '<': /* comparison match reduce strength */ 482169942Sobrien val -= 2 * MULT; 483169942Sobrien break; 484169942Sobrien 485169942Sobrien case '^': 486169942Sobrien case '&': /* masking bits, we could count them too */ 487169942Sobrien val -= MULT; 488169942Sobrien break; 489169942Sobrien 490169942Sobrien default: 491169942Sobrien (void)fprintf(stderr, "Bad relation %c\n", m->reln); 492169942Sobrien abort(); 493169942Sobrien } 494169962Sobrien 495169962Sobrien if (val == 0) /* ensure we only return 0 for FILE_DEFAULT */ 496169962Sobrien val = 1; 497169962Sobrien 498169942Sobrien return val; 499159764Sobrien} 500159764Sobrien 501169942Sobrien/* 502169942Sobrien * Sort callback for sorting entries by "strength" (basically length) 503169942Sobrien */ 504159764Sobrienprivate int 505159764Sobrienapprentice_sort(const void *a, const void *b) 506159764Sobrien{ 507159764Sobrien const struct magic_entry *ma = a; 508159764Sobrien const struct magic_entry *mb = b; 509159764Sobrien size_t sa = apprentice_magic_strength(ma->mp); 510159764Sobrien size_t sb = apprentice_magic_strength(mb->mp); 511159764Sobrien if (sa == sb) 512159764Sobrien return 0; 513159764Sobrien else if (sa > sb) 514159764Sobrien return -1; 515159764Sobrien else 516159764Sobrien return 1; 517159764Sobrien} 518159764Sobrien 51974784Sobrien/* 52074784Sobrien * parse from a file 521103373Sobrien * const char *fn: name of magic file 52274784Sobrien */ 523133359Sobrienprivate int 524133359Sobrienapprentice_file(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp, 525133359Sobrien const char *fn, int action) 52668349Sobrien{ 527133359Sobrien private const char hdr[] = 52868349Sobrien "cont\toffset\ttype\topcode\tmask\tvalue\tdesc"; 52968349Sobrien FILE *f; 530175296Sobrien char line[BUFSIZ]; 53168349Sobrien int errs = 0; 532159764Sobrien struct magic_entry *marray; 533169942Sobrien uint32_t marraycount, i, mentrycount = 0; 534169942Sobrien size_t lineno = 0; 53568349Sobrien 536169942Sobrien ms->flags |= MAGIC_CHECK; /* Enable checks for parsed files */ 537169942Sobrien 538139368Sobrien f = fopen(ms->file = fn, "r"); 53974784Sobrien if (f == NULL) { 54068349Sobrien if (errno != ENOENT) 541133359Sobrien file_error(ms, errno, "cannot read magic file `%s'", 542133359Sobrien fn); 54368349Sobrien return -1; 54468349Sobrien } 54568349Sobrien 546169942Sobrien maxmagic = MAXMAGIS; 547159825Sobrien if ((marray = calloc(maxmagic, sizeof(*marray))) == NULL) { 548133359Sobrien (void)fclose(f); 549169942Sobrien file_oomem(ms, maxmagic * sizeof(*marray)); 550133359Sobrien return -1; 55174784Sobrien } 552159764Sobrien marraycount = 0; 55374784Sobrien 554133359Sobrien /* print silly verbose header for USG compat. */ 555133359Sobrien if (action == FILE_CHECK) 556133359Sobrien (void)fprintf(stderr, "%s\n", hdr); 557133359Sobrien 558169942Sobrien /* read and parse this file */ 559175296Sobrien for (ms->line = 1; fgets(line, sizeof(line), f) != NULL; ms->line++) { 560139368Sobrien size_t len; 561139368Sobrien len = strlen(line); 562169942Sobrien if (len == 0) /* null line, garbage, etc */ 56368349Sobrien continue; 564169942Sobrien if (line[len - 1] == '\n') { 565169942Sobrien lineno++; 566159764Sobrien line[len - 1] = '\0'; /* delete newline */ 567169942Sobrien } 568169942Sobrien if (line[0] == '\0') /* empty, do not parse */ 569169942Sobrien continue; 570169942Sobrien if (line[0] == '#') /* comment, do not parse */ 571169942Sobrien continue; 572169942Sobrien if (parse(ms, &marray, &marraycount, line, lineno, action) != 0) 573159764Sobrien errs++; 57468349Sobrien } 57568349Sobrien 576133359Sobrien (void)fclose(f); 577159764Sobrien if (errs) 578159764Sobrien goto out; 579159764Sobrien 580159764Sobrien#ifndef NOORDER 581159764Sobrien qsort(marray, marraycount, sizeof(*marray), apprentice_sort); 582169962Sobrien /* 583169962Sobrien * Make sure that any level 0 "default" line is last (if one exists). 584169962Sobrien */ 585169962Sobrien for (i = 0; i < marraycount; i++) { 586169962Sobrien if (marray[i].mp->cont_level == 0 && 587169962Sobrien marray[i].mp->type == FILE_DEFAULT) { 588169962Sobrien while (++i < marraycount) 589169962Sobrien if (marray[i].mp->cont_level == 0) 590169962Sobrien break; 591169962Sobrien if (i != marraycount) { 592169962Sobrien ms->line = marray[i].mp->lineno; /* XXX - Ugh! */ 593169962Sobrien file_magwarn(ms, 594169962Sobrien "level 0 \"default\" did not sort last"); 595169962Sobrien } 596169962Sobrien break; 597169962Sobrien } 598169962Sobrien } 599159764Sobrien#endif 600159764Sobrien 601169942Sobrien for (i = 0; i < marraycount; i++) 602159764Sobrien mentrycount += marray[i].cont_count; 603159764Sobrien 604159764Sobrien if ((*magicp = malloc(sizeof(**magicp) * mentrycount)) == NULL) { 605169942Sobrien file_oomem(ms, sizeof(**magicp) * mentrycount); 606159764Sobrien errs++; 607159764Sobrien goto out; 608159764Sobrien } 609159764Sobrien 610159764Sobrien mentrycount = 0; 611159764Sobrien for (i = 0; i < marraycount; i++) { 612159764Sobrien (void)memcpy(*magicp + mentrycount, marray[i].mp, 613159764Sobrien marray[i].cont_count * sizeof(**magicp)); 614159764Sobrien mentrycount += marray[i].cont_count; 615159764Sobrien } 616159764Sobrienout: 617159764Sobrien for (i = 0; i < marraycount; i++) 618159764Sobrien free(marray[i].mp); 619159764Sobrien free(marray); 62074784Sobrien if (errs) { 62174784Sobrien *magicp = NULL; 62274784Sobrien *nmagicp = 0; 623159764Sobrien return errs; 624159764Sobrien } else { 625159764Sobrien *nmagicp = mentrycount; 626159764Sobrien return 0; 62774784Sobrien } 628159764Sobrien 62968349Sobrien} 63068349Sobrien 63168349Sobrien/* 63268349Sobrien * extend the sign bit if the comparison is to be signed 63368349Sobrien */ 634169942Sobrienprotected uint64_t 635169942Sobrienfile_signextend(struct magic_set *ms, struct magic *m, uint64_t v) 63668349Sobrien{ 637169962Sobrien if (!(m->flag & UNSIGNED)) { 63868349Sobrien switch(m->type) { 63968349Sobrien /* 64068349Sobrien * Do not remove the casts below. They are 64168349Sobrien * vital. When later compared with the data, 64268349Sobrien * the sign extension must have happened. 64368349Sobrien */ 644133359Sobrien case FILE_BYTE: 64568349Sobrien v = (char) v; 64668349Sobrien break; 647133359Sobrien case FILE_SHORT: 648133359Sobrien case FILE_BESHORT: 649133359Sobrien case FILE_LESHORT: 65068349Sobrien v = (short) v; 65168349Sobrien break; 652133359Sobrien case FILE_DATE: 653133359Sobrien case FILE_BEDATE: 654133359Sobrien case FILE_LEDATE: 655159764Sobrien case FILE_MEDATE: 656133359Sobrien case FILE_LDATE: 657133359Sobrien case FILE_BELDATE: 658133359Sobrien case FILE_LELDATE: 659159764Sobrien case FILE_MELDATE: 660133359Sobrien case FILE_LONG: 661133359Sobrien case FILE_BELONG: 662133359Sobrien case FILE_LELONG: 663159764Sobrien case FILE_MELONG: 664175296Sobrien case FILE_FLOAT: 665175296Sobrien case FILE_BEFLOAT: 666175296Sobrien case FILE_LEFLOAT: 667103373Sobrien v = (int32_t) v; 66868349Sobrien break; 669169942Sobrien case FILE_QUAD: 670169942Sobrien case FILE_BEQUAD: 671169942Sobrien case FILE_LEQUAD: 672169942Sobrien case FILE_QDATE: 673169942Sobrien case FILE_QLDATE: 674169942Sobrien case FILE_BEQDATE: 675169942Sobrien case FILE_BEQLDATE: 676169942Sobrien case FILE_LEQDATE: 677169942Sobrien case FILE_LEQLDATE: 678175296Sobrien case FILE_DOUBLE: 679175296Sobrien case FILE_BEDOUBLE: 680175296Sobrien case FILE_LEDOUBLE: 681169942Sobrien v = (int64_t) v; 682169942Sobrien break; 683133359Sobrien case FILE_STRING: 684133359Sobrien case FILE_PSTRING: 685139368Sobrien case FILE_BESTRING16: 686139368Sobrien case FILE_LESTRING16: 687133359Sobrien case FILE_REGEX: 688159764Sobrien case FILE_SEARCH: 689169962Sobrien case FILE_DEFAULT: 690103373Sobrien break; 69168349Sobrien default: 692133359Sobrien if (ms->flags & MAGIC_CHECK) 693139368Sobrien file_magwarn(ms, "cannot happen: m->type=%d\n", 694133359Sobrien m->type); 695133359Sobrien return ~0U; 69668349Sobrien } 697169962Sobrien } 69868349Sobrien return v; 69968349Sobrien} 70068349Sobrien 701169962Sobrienprivate int 702169962Sobrienstring_modifier_check(struct magic_set *ms, struct magic const *m) 703169962Sobrien{ 704169962Sobrien if ((ms->flags & MAGIC_CHECK) == 0) 705169962Sobrien return 0; 706169962Sobrien 707169962Sobrien switch (m->type) { 708169962Sobrien case FILE_BESTRING16: 709169962Sobrien case FILE_LESTRING16: 710169962Sobrien if (m->str_flags != 0) { 711169962Sobrien file_magwarn(ms, "no modifiers allowed for 16-bit strings\n"); 712169962Sobrien return -1; 713169962Sobrien } 714169962Sobrien break; 715169962Sobrien case FILE_STRING: 716169962Sobrien case FILE_PSTRING: 717169962Sobrien if ((m->str_flags & REGEX_OFFSET_START) != 0) { 718169962Sobrien file_magwarn(ms, "'/%c' only allowed on regex and search\n", 719169962Sobrien CHAR_REGEX_OFFSET_START); 720169962Sobrien return -1; 721169962Sobrien } 722169962Sobrien break; 723169962Sobrien case FILE_SEARCH: 724169962Sobrien break; 725169962Sobrien case FILE_REGEX: 726169962Sobrien if ((m->str_flags & STRING_COMPACT_BLANK) != 0) { 727169962Sobrien file_magwarn(ms, "'/%c' not allowed on regex\n", 728169962Sobrien CHAR_COMPACT_BLANK); 729169962Sobrien return -1; 730169962Sobrien } 731169962Sobrien if ((m->str_flags & STRING_COMPACT_OPTIONAL_BLANK) != 0) { 732169962Sobrien file_magwarn(ms, "'/%c' not allowed on regex\n", 733169962Sobrien CHAR_COMPACT_OPTIONAL_BLANK); 734169962Sobrien return -1; 735169962Sobrien } 736169962Sobrien break; 737169962Sobrien default: 738169962Sobrien file_magwarn(ms, "coding error: m->type=%d\n", 739169962Sobrien m->type); 740169962Sobrien return -1; 741169962Sobrien } 742169962Sobrien return 0; 743169962Sobrien} 744169962Sobrien 745169962Sobrienprivate int 746169962Sobrienget_op(char c) 747169962Sobrien{ 748169962Sobrien switch (c) { 749169962Sobrien case '&': 750169962Sobrien return FILE_OPAND; 751169962Sobrien case '|': 752169962Sobrien return FILE_OPOR; 753169962Sobrien case '^': 754169962Sobrien return FILE_OPXOR; 755169962Sobrien case '+': 756169962Sobrien return FILE_OPADD; 757169962Sobrien case '-': 758169962Sobrien return FILE_OPMINUS; 759169962Sobrien case '*': 760169962Sobrien return FILE_OPMULTIPLY; 761169962Sobrien case '/': 762169962Sobrien return FILE_OPDIVIDE; 763169962Sobrien case '%': 764169962Sobrien return FILE_OPMODULO; 765169962Sobrien default: 766169962Sobrien return -1; 767169962Sobrien } 768169962Sobrien} 769169962Sobrien 770169962Sobrien#ifdef ENABLE_CONDITIONALS 771169962Sobrienprivate int 772169962Sobrienget_cond(const char *l, const char **t) 773169962Sobrien{ 774169962Sobrien static struct cond_tbl_s { 775169962Sobrien const char *name; 776169962Sobrien const size_t len; 777169962Sobrien const int cond; 778169962Sobrien } cond_tbl[] = { 779169962Sobrien { "if", 2, COND_IF }, 780169962Sobrien { "elif", 4, COND_ELIF }, 781169962Sobrien { "else", 4, COND_ELSE }, 782169962Sobrien { NULL, 0, COND_NONE }, 783169962Sobrien }; 784169962Sobrien struct cond_tbl_s *p; 785169962Sobrien 786169962Sobrien for (p = cond_tbl; p->name; p++) { 787169962Sobrien if (strncmp(l, p->name, p->len) == 0 && 788169962Sobrien isspace((unsigned char)l[p->len])) { 789169962Sobrien if (t) 790169962Sobrien *t = l + p->len; 791169962Sobrien break; 792169962Sobrien } 793169962Sobrien } 794169962Sobrien return p->cond; 795169962Sobrien} 796169962Sobrien 797169962Sobrienprivate int 798169962Sobriencheck_cond(struct magic_set *ms, int cond, uint32_t cont_level) 799169962Sobrien{ 800169962Sobrien int last_cond; 801169962Sobrien last_cond = ms->c.li[cont_level].last_cond; 802169962Sobrien 803169962Sobrien switch (cond) { 804169962Sobrien case COND_IF: 805169962Sobrien if (last_cond != COND_NONE && last_cond != COND_ELIF) { 806169962Sobrien if (ms->flags & MAGIC_CHECK) 807169962Sobrien file_magwarn(ms, "syntax error: `if'"); 808169962Sobrien return -1; 809169962Sobrien } 810169962Sobrien last_cond = COND_IF; 811169962Sobrien break; 812169962Sobrien 813169962Sobrien case COND_ELIF: 814169962Sobrien if (last_cond != COND_IF && last_cond != COND_ELIF) { 815169962Sobrien if (ms->flags & MAGIC_CHECK) 816169962Sobrien file_magwarn(ms, "syntax error: `elif'"); 817169962Sobrien return -1; 818169962Sobrien } 819169962Sobrien last_cond = COND_ELIF; 820169962Sobrien break; 821169962Sobrien 822169962Sobrien case COND_ELSE: 823169962Sobrien if (last_cond != COND_IF && last_cond != COND_ELIF) { 824169962Sobrien if (ms->flags & MAGIC_CHECK) 825169962Sobrien file_magwarn(ms, "syntax error: `else'"); 826169962Sobrien return -1; 827169962Sobrien } 828169962Sobrien last_cond = COND_NONE; 829169962Sobrien break; 830169962Sobrien 831169962Sobrien case COND_NONE: 832169962Sobrien last_cond = COND_NONE; 833169962Sobrien break; 834169962Sobrien } 835169962Sobrien 836169962Sobrien ms->c.li[cont_level].last_cond = last_cond; 837169962Sobrien return 0; 838169962Sobrien} 839169962Sobrien#endif /* ENABLE_CONDITIONALS */ 840169962Sobrien 84168349Sobrien/* 84268349Sobrien * parse one line from magic file, put into magic[index++] if valid 84368349Sobrien */ 844133359Sobrienprivate int 845159764Sobrienparse(struct magic_set *ms, struct magic_entry **mentryp, uint32_t *nmentryp, 846169942Sobrien const char *line, size_t lineno, int action) 84768349Sobrien{ 848169962Sobrien#ifdef ENABLE_CONDITIONALS 849169962Sobrien static uint32_t last_cont_level = 0; 850169962Sobrien#endif 851169942Sobrien size_t i; 852159764Sobrien struct magic_entry *me; 85368349Sobrien struct magic *m; 854159764Sobrien const char *l = line; 85584685Sobrien char *t; 856169962Sobrien int op; 857159825Sobrien uint32_t cont_level; 85868349Sobrien 859159764Sobrien cont_level = 0; 86068349Sobrien 86168349Sobrien while (*l == '>') { 86268349Sobrien ++l; /* step over */ 863159764Sobrien cont_level++; 86468349Sobrien } 865169962Sobrien#ifdef ENABLE_CONDITIONALS 866169962Sobrien if (cont_level == 0 || cont_level > last_cont_level) 867169962Sobrien if (file_check_mem(ms, cont_level) == -1) 868169962Sobrien return -1; 869169962Sobrien last_cont_level = cont_level; 870169962Sobrien#endif 87168349Sobrien 872159764Sobrien#define ALLOC_CHUNK (size_t)10 873159764Sobrien#define ALLOC_INCR (size_t)200 874159764Sobrien 875159764Sobrien if (cont_level != 0) { 876159764Sobrien if (*nmentryp == 0) { 877159764Sobrien file_error(ms, 0, "No current entry for continuation"); 878159764Sobrien return -1; 879159764Sobrien } 880159764Sobrien me = &(*mentryp)[*nmentryp - 1]; 881159764Sobrien if (me->cont_count == me->max_count) { 882159764Sobrien struct magic *nm; 883159764Sobrien size_t cnt = me->max_count + ALLOC_CHUNK; 884159764Sobrien if ((nm = realloc(me->mp, sizeof(*nm) * cnt)) == NULL) { 885169942Sobrien file_oomem(ms, sizeof(*nm) * cnt); 886159764Sobrien return -1; 887159764Sobrien } 888159764Sobrien me->mp = m = nm; 889159764Sobrien me->max_count = cnt; 890159764Sobrien } 891159764Sobrien m = &me->mp[me->cont_count++]; 892169962Sobrien (void)memset(m, 0, sizeof(*m)); 893159764Sobrien m->cont_level = cont_level; 894159764Sobrien } else { 895159764Sobrien if (*nmentryp == maxmagic) { 896159764Sobrien struct magic_entry *mp; 897159764Sobrien 898159764Sobrien maxmagic += ALLOC_INCR; 899159764Sobrien if ((mp = realloc(*mentryp, sizeof(*mp) * maxmagic)) == 900159764Sobrien NULL) { 901169942Sobrien file_oomem(ms, sizeof(*mp) * maxmagic); 902159764Sobrien return -1; 903159764Sobrien } 904159764Sobrien (void)memset(&mp[*nmentryp], 0, sizeof(*mp) * 905159764Sobrien ALLOC_INCR); 906159764Sobrien *mentryp = mp; 907159764Sobrien } 908159764Sobrien me = &(*mentryp)[*nmentryp]; 909159764Sobrien if (me->mp == NULL) { 910159764Sobrien if ((m = malloc(sizeof(*m) * ALLOC_CHUNK)) == NULL) { 911169942Sobrien file_oomem(ms, sizeof(*m) * ALLOC_CHUNK); 912159764Sobrien return -1; 913159764Sobrien } 914159764Sobrien me->mp = m; 915159764Sobrien me->max_count = ALLOC_CHUNK; 916159764Sobrien } else 917159764Sobrien m = me->mp; 918169962Sobrien (void)memset(m, 0, sizeof(*m)); 919159764Sobrien m->cont_level = 0; 920159764Sobrien me->cont_count = 1; 921159764Sobrien } 922169942Sobrien m->lineno = lineno; 923159764Sobrien 924169962Sobrien if (*l == '&') { /* m->cont_level == 0 checked below. */ 925159764Sobrien ++l; /* step over */ 926159764Sobrien m->flag |= OFFADD; 927159764Sobrien } 928169962Sobrien if (*l == '(') { 92968349Sobrien ++l; /* step over */ 93068349Sobrien m->flag |= INDIR; 931159764Sobrien if (m->flag & OFFADD) 932159764Sobrien m->flag = (m->flag & ~OFFADD) | INDIROFFADD; 933169962Sobrien 934169962Sobrien if (*l == '&') { /* m->cont_level == 0 checked below */ 935169962Sobrien ++l; /* step over */ 936169962Sobrien m->flag |= OFFADD; 937169962Sobrien } 93868349Sobrien } 939169962Sobrien /* Indirect offsets are not valid at level 0. */ 940169962Sobrien if (m->cont_level == 0 && (m->flag & (OFFADD | INDIROFFADD))) 941169962Sobrien if (ms->flags & MAGIC_CHECK) 942169962Sobrien file_magwarn(ms, "relative offset at level 0"); 94368349Sobrien 94468349Sobrien /* get offset, then skip over it */ 945133359Sobrien m->offset = (uint32_t)strtoul(l, &t, 0); 94668349Sobrien if (l == t) 947133359Sobrien if (ms->flags & MAGIC_CHECK) 948139368Sobrien file_magwarn(ms, "offset `%s' invalid", l); 94968349Sobrien l = t; 95068349Sobrien 95168349Sobrien if (m->flag & INDIR) { 952133359Sobrien m->in_type = FILE_LONG; 95374784Sobrien m->in_offset = 0; 95468349Sobrien /* 95568349Sobrien * read [.lbs][+-]nnnnn) 95668349Sobrien */ 95768349Sobrien if (*l == '.') { 95868349Sobrien l++; 95968349Sobrien switch (*l) { 96068349Sobrien case 'l': 961133359Sobrien m->in_type = FILE_LELONG; 96268349Sobrien break; 96368349Sobrien case 'L': 964133359Sobrien m->in_type = FILE_BELONG; 96568349Sobrien break; 966159764Sobrien case 'm': 967159764Sobrien m->in_type = FILE_MELONG; 968159764Sobrien break; 96968349Sobrien case 'h': 97068349Sobrien case 's': 971133359Sobrien m->in_type = FILE_LESHORT; 97268349Sobrien break; 97368349Sobrien case 'H': 97468349Sobrien case 'S': 975133359Sobrien m->in_type = FILE_BESHORT; 97668349Sobrien break; 97768349Sobrien case 'c': 97868349Sobrien case 'b': 97968349Sobrien case 'C': 98068349Sobrien case 'B': 981133359Sobrien m->in_type = FILE_BYTE; 98268349Sobrien break; 983175296Sobrien case 'e': 984175296Sobrien case 'f': 985175296Sobrien case 'g': 986175296Sobrien m->in_type = FILE_LEDOUBLE; 987175296Sobrien break; 988175296Sobrien case 'E': 989175296Sobrien case 'F': 990175296Sobrien case 'G': 991175296Sobrien m->in_type = FILE_BEDOUBLE; 992175296Sobrien break; 99368349Sobrien default: 994133359Sobrien if (ms->flags & MAGIC_CHECK) 995139368Sobrien file_magwarn(ms, 996139368Sobrien "indirect offset type `%c' invalid", 997133359Sobrien *l); 99868349Sobrien break; 99968349Sobrien } 100068349Sobrien l++; 100168349Sobrien } 1002169962Sobrien 1003169962Sobrien m->in_op = 0; 100480588Sobrien if (*l == '~') { 1005159764Sobrien m->in_op |= FILE_OPINVERSE; 100680588Sobrien l++; 100780588Sobrien } 1008169962Sobrien if ((op = get_op(*l)) != -1) { 1009169962Sobrien m->in_op |= op; 101080588Sobrien l++; 101180588Sobrien } 1012159764Sobrien if (*l == '(') { 1013159764Sobrien m->in_op |= FILE_OPINDIRECT; 1014159764Sobrien l++; 1015159764Sobrien } 1016159764Sobrien if (isdigit((unsigned char)*l) || *l == '-') { 1017159764Sobrien m->in_offset = (int32_t)strtol(l, &t, 0); 1018169962Sobrien if (l == t) 1019169962Sobrien if (ms->flags & MAGIC_CHECK) 1020169962Sobrien file_magwarn(ms, 1021169962Sobrien "in_offset `%s' invalid", l); 1022159764Sobrien l = t; 1023159764Sobrien } 1024159764Sobrien if (*l++ != ')' || 1025159764Sobrien ((m->in_op & FILE_OPINDIRECT) && *l++ != ')')) 1026133359Sobrien if (ms->flags & MAGIC_CHECK) 1027139368Sobrien file_magwarn(ms, 1028139368Sobrien "missing ')' in indirect offset"); 102968349Sobrien } 1030169962Sobrien EATAB; 103168349Sobrien 1032169962Sobrien#ifdef ENABLE_CONDITIONALS 1033169962Sobrien m->cond = get_cond(l, &l); 1034169962Sobrien if (check_cond(ms, m->cond, cont_level) == -1) 1035169962Sobrien return -1; 103668349Sobrien 103768349Sobrien EATAB; 1038169962Sobrien#endif 103968349Sobrien 104068349Sobrien if (*l == 'u') { 104168349Sobrien ++l; 104268349Sobrien m->flag |= UNSIGNED; 104368349Sobrien } 104468349Sobrien 1045169962Sobrien m->type = get_type(l, &l); 1046169962Sobrien if (m->type == FILE_INVALID) { 1047133359Sobrien if (ms->flags & MAGIC_CHECK) 1048139368Sobrien file_magwarn(ms, "type `%s' invalid", l); 104968349Sobrien return -1; 105068349Sobrien } 1051169962Sobrien 105268349Sobrien /* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */ 105380588Sobrien /* New and improved: ~ & | ^ + - * / % -- exciting, isn't it? */ 1054169962Sobrien 1055169962Sobrien m->mask_op = 0; 105680588Sobrien if (*l == '~') { 1057139368Sobrien if (!IS_STRING(m->type)) 1058159764Sobrien m->mask_op |= FILE_OPINVERSE; 1059169962Sobrien else if (ms->flags & MAGIC_CHECK) 1060169962Sobrien file_magwarn(ms, "'~' invalid for string types"); 106168349Sobrien ++l; 106280588Sobrien } 1063169962Sobrien m->str_count = 0; 1064169962Sobrien m->str_flags = 0; 1065169962Sobrien m->num_mask = 0; 1066169962Sobrien if ((op = get_op(*l)) != -1) { 1067169962Sobrien if (!IS_STRING(m->type)) { 1068169962Sobrien uint64_t val; 106980588Sobrien ++l; 1070133359Sobrien m->mask_op |= op; 1071169942Sobrien val = (uint64_t)strtoull(l, &t, 0); 1072159764Sobrien l = t; 1073169962Sobrien m->num_mask = file_signextend(ms, m, val); 107480588Sobrien eatsize(&l); 1075169962Sobrien } 1076169962Sobrien else if (op == FILE_OPDIVIDE) { 1077169962Sobrien int have_count = 0; 1078133359Sobrien while (!isspace((unsigned char)*++l)) { 107968349Sobrien switch (*l) { 1080169962Sobrien /* for portability avoid "case '0' ... '9':" */ 1081169962Sobrien case '0': case '1': case '2': 1082169962Sobrien case '3': case '4': case '5': 1083169962Sobrien case '6': case '7': case '8': 1084169962Sobrien case '9': { 1085169962Sobrien if (have_count && ms->flags & MAGIC_CHECK) 1086169962Sobrien file_magwarn(ms, 1087169962Sobrien "multiple counts"); 1088169962Sobrien have_count = 1; 1089169962Sobrien m->str_count = strtoul(l, &t, 0); 1090169962Sobrien l = t - 1; 109168349Sobrien break; 1092169962Sobrien } 109368349Sobrien case CHAR_COMPACT_BLANK: 1094169962Sobrien m->str_flags |= STRING_COMPACT_BLANK; 109568349Sobrien break; 109668349Sobrien case CHAR_COMPACT_OPTIONAL_BLANK: 1097169962Sobrien m->str_flags |= 109868349Sobrien STRING_COMPACT_OPTIONAL_BLANK; 109968349Sobrien break; 1100169962Sobrien case CHAR_IGNORE_LOWERCASE: 1101169962Sobrien m->str_flags |= STRING_IGNORE_LOWERCASE; 1102169962Sobrien break; 1103169962Sobrien case CHAR_IGNORE_UPPERCASE: 1104169962Sobrien m->str_flags |= STRING_IGNORE_UPPERCASE; 1105169962Sobrien break; 1106169962Sobrien case CHAR_REGEX_OFFSET_START: 1107169962Sobrien m->str_flags |= REGEX_OFFSET_START; 1108169962Sobrien break; 110968349Sobrien default: 1110133359Sobrien if (ms->flags & MAGIC_CHECK) 1111139368Sobrien file_magwarn(ms, 1112139368Sobrien "string extension `%c' invalid", 1113133359Sobrien *l); 111468349Sobrien return -1; 111568349Sobrien } 1116169962Sobrien /* allow multiple '/' for readability */ 1117169962Sobrien if (l[1] == '/' && !isspace((unsigned char)l[2])) 1118169962Sobrien l++; 111968349Sobrien } 1120169962Sobrien if (string_modifier_check(ms, m) == -1) 1121169962Sobrien return -1; 112268349Sobrien } 1123169962Sobrien else { 1124169962Sobrien if (ms->flags & MAGIC_CHECK) 1125169962Sobrien file_magwarn(ms, "invalid string op: %c", *t); 1126169962Sobrien return -1; 1127169962Sobrien } 112880588Sobrien } 1129133359Sobrien /* 1130133359Sobrien * We used to set mask to all 1's here, instead let's just not do 1131133359Sobrien * anything if mask = 0 (unless you have a better idea) 1132133359Sobrien */ 113368349Sobrien EATAB; 113468349Sobrien 113568349Sobrien switch (*l) { 113668349Sobrien case '>': 113768349Sobrien case '<': 113868349Sobrien /* Old-style anding: "0 byte &0x80 dynamically linked" */ 113968349Sobrien case '&': 114068349Sobrien case '^': 114168349Sobrien case '=': 114268349Sobrien m->reln = *l; 114368349Sobrien ++l; 114468349Sobrien if (*l == '=') { 114568349Sobrien /* HP compat: ignore &= etc. */ 114668349Sobrien ++l; 114768349Sobrien } 114868349Sobrien break; 114968349Sobrien case '!': 1150159764Sobrien m->reln = *l; 1151159764Sobrien ++l; 1152159764Sobrien break; 115368349Sobrien default: 1154169962Sobrien m->reln = '='; /* the default relation */ 1155159764Sobrien if (*l == 'x' && ((isascii((unsigned char)l[1]) && 1156159764Sobrien isspace((unsigned char)l[1])) || !l[1])) { 115768349Sobrien m->reln = *l; 115868349Sobrien ++l; 115968349Sobrien } 116068349Sobrien break; 116168349Sobrien } 1162169962Sobrien /* 1163169962Sobrien * Grab the value part, except for an 'x' reln. 1164169962Sobrien */ 1165169962Sobrien if (m->reln != 'x' && getvalue(ms, m, &l, action)) 116668349Sobrien return -1; 1167169962Sobrien 116868349Sobrien /* 116968349Sobrien * TODO finish this macro and start using it! 117068349Sobrien * #define offsetcheck {if (offset > HOWMANY-1) 117168349Sobrien * magwarn("offset too big"); } 117268349Sobrien */ 117368349Sobrien 117468349Sobrien /* 1175169962Sobrien * Now get last part - the description 117668349Sobrien */ 117768349Sobrien EATAB; 117868349Sobrien if (l[0] == '\b') { 117968349Sobrien ++l; 118068349Sobrien m->nospflag = 1; 118168349Sobrien } else if ((l[0] == '\\') && (l[1] == 'b')) { 118268349Sobrien ++l; 118368349Sobrien ++l; 118468349Sobrien m->nospflag = 1; 118568349Sobrien } else 118668349Sobrien m->nospflag = 0; 1187169942Sobrien for (i = 0; (m->desc[i++] = *l++) != '\0' && i < sizeof(m->desc); ) 1188169942Sobrien continue; 1189169942Sobrien if (i == sizeof(m->desc)) { 1190169942Sobrien m->desc[sizeof(m->desc) - 1] = '\0'; 1191169942Sobrien if (ms->flags & MAGIC_CHECK) 1192169942Sobrien file_magwarn(ms, "description `%s' truncated", m->desc); 1193169942Sobrien } 119468349Sobrien 1195169942Sobrien /* 1196169942Sobrien * We only do this check while compiling, or if any of the magic 1197169942Sobrien * files were not compiled. 1198169942Sobrien */ 1199169942Sobrien if (ms->flags & MAGIC_CHECK) { 1200169942Sobrien if (check_format(ms, m) == -1) 1201133359Sobrien return -1; 1202133359Sobrien } 1203103373Sobrien#ifndef COMPILE_ONLY 1204133359Sobrien if (action == FILE_CHECK) { 1205133359Sobrien file_mdump(m); 120668349Sobrien } 1207103373Sobrien#endif 1208159764Sobrien if (m->cont_level == 0) 1209159764Sobrien ++(*nmentryp); /* make room for next */ 121068349Sobrien return 0; 121168349Sobrien} 121268349Sobrien 1213169942Sobrienprivate int 1214169942Sobriencheck_format_type(const char *ptr, int type) 1215169942Sobrien{ 1216169942Sobrien int quad = 0; 1217169942Sobrien if (*ptr == '\0') { 1218169942Sobrien /* Missing format string; bad */ 1219169942Sobrien return -1; 1220169942Sobrien } 1221169942Sobrien 1222169942Sobrien switch (type) { 1223169942Sobrien case FILE_FMT_QUAD: 1224169942Sobrien quad = 1; 1225169942Sobrien /*FALLTHROUGH*/ 1226169942Sobrien case FILE_FMT_NUM: 1227169942Sobrien if (*ptr == '-') 1228169942Sobrien ptr++; 1229169942Sobrien if (*ptr == '.') 1230169942Sobrien ptr++; 1231169942Sobrien while (isdigit((unsigned char)*ptr)) ptr++; 1232169942Sobrien if (*ptr == '.') 1233169942Sobrien ptr++; 1234169942Sobrien while (isdigit((unsigned char)*ptr)) ptr++; 1235169942Sobrien if (quad) { 1236169942Sobrien if (*ptr++ != 'l') 1237169942Sobrien return -1; 1238169942Sobrien if (*ptr++ != 'l') 1239169942Sobrien return -1; 1240169942Sobrien } 1241169942Sobrien 1242169942Sobrien switch (*ptr++) { 1243169942Sobrien case 'l': 1244169942Sobrien switch (*ptr++) { 1245169942Sobrien case 'i': 1246169942Sobrien case 'd': 1247169942Sobrien case 'u': 1248169942Sobrien case 'x': 1249169942Sobrien case 'X': 1250169942Sobrien return 0; 1251169942Sobrien default: 1252169942Sobrien return -1; 1253169942Sobrien } 1254169942Sobrien 1255169942Sobrien case 'h': 1256169942Sobrien switch (*ptr++) { 1257169942Sobrien case 'h': 1258169942Sobrien switch (*ptr++) { 1259169942Sobrien case 'i': 1260169942Sobrien case 'd': 1261169942Sobrien case 'u': 1262169942Sobrien case 'x': 1263169942Sobrien case 'X': 1264169942Sobrien return 0; 1265169942Sobrien default: 1266169942Sobrien return -1; 1267169942Sobrien } 1268169942Sobrien case 'd': 1269169942Sobrien return 0; 1270169942Sobrien default: 1271169942Sobrien return -1; 1272169942Sobrien } 1273169942Sobrien 1274169942Sobrien case 'i': 1275169942Sobrien case 'c': 1276169942Sobrien case 'd': 1277169942Sobrien case 'u': 1278169942Sobrien case 'x': 1279169942Sobrien case 'X': 1280169942Sobrien return 0; 1281169942Sobrien 1282169942Sobrien default: 1283169942Sobrien return -1; 1284169942Sobrien } 1285169942Sobrien 1286175296Sobrien case FILE_FMT_FLOAT: 1287175296Sobrien case FILE_FMT_DOUBLE: 1288175296Sobrien if (*ptr == '-') 1289175296Sobrien ptr++; 1290175296Sobrien if (*ptr == '.') 1291175296Sobrien ptr++; 1292175296Sobrien while (isdigit((unsigned char)*ptr)) ptr++; 1293175296Sobrien if (*ptr == '.') 1294175296Sobrien ptr++; 1295175296Sobrien while (isdigit((unsigned char)*ptr)) ptr++; 1296175296Sobrien 1297175296Sobrien switch (*ptr++) { 1298175296Sobrien case 'e': 1299175296Sobrien case 'E': 1300175296Sobrien case 'f': 1301175296Sobrien case 'F': 1302175296Sobrien case 'g': 1303175296Sobrien case 'G': 1304175296Sobrien return 0; 1305175296Sobrien 1306175296Sobrien default: 1307175296Sobrien return -1; 1308175296Sobrien } 1309175296Sobrien 1310175296Sobrien 1311169942Sobrien case FILE_FMT_STR: 1312169942Sobrien if (*ptr == '-') 1313169942Sobrien ptr++; 1314169942Sobrien while (isdigit((unsigned char )*ptr)) 1315169942Sobrien ptr++; 1316169942Sobrien if (*ptr == '.') { 1317169942Sobrien ptr++; 1318169942Sobrien while (isdigit((unsigned char )*ptr)) 1319169942Sobrien ptr++; 1320169942Sobrien } 1321169942Sobrien 1322169942Sobrien switch (*ptr++) { 1323169942Sobrien case 's': 1324169942Sobrien return 0; 1325169942Sobrien default: 1326169942Sobrien return -1; 1327169942Sobrien } 1328169942Sobrien 1329169942Sobrien default: 1330169942Sobrien /* internal error */ 1331169942Sobrien abort(); 1332169942Sobrien } 1333169942Sobrien /*NOTREACHED*/ 1334169942Sobrien return -1; 1335169942Sobrien} 1336169942Sobrien 1337133359Sobrien/* 1338133359Sobrien * Check that the optional printf format in description matches 1339133359Sobrien * the type of the magic. 1340133359Sobrien */ 1341133359Sobrienprivate int 1342139368Sobriencheck_format(struct magic_set *ms, struct magic *m) 1343133359Sobrien{ 1344133359Sobrien char *ptr; 1345133359Sobrien 1346133359Sobrien for (ptr = m->desc; *ptr; ptr++) 1347133359Sobrien if (*ptr == '%') 1348133359Sobrien break; 1349133359Sobrien if (*ptr == '\0') { 1350133359Sobrien /* No format string; ok */ 1351133359Sobrien return 1; 1352133359Sobrien } 1353169942Sobrien 1354169942Sobrien assert(file_nformats == file_nnames); 1355169942Sobrien 1356169942Sobrien if (m->type >= file_nformats) { 1357169942Sobrien file_error(ms, 0, "Internal error inconsistency between " 1358169942Sobrien "m->type and format strings"); 1359169942Sobrien return -1; 1360133359Sobrien } 1361169942Sobrien if (file_formats[m->type] == FILE_FMT_NONE) { 1362169942Sobrien file_error(ms, 0, "No format string for `%s' with description " 1363169942Sobrien "`%s'", m->desc, file_names[m->type]); 1364169942Sobrien return -1; 1365133359Sobrien } 1366169942Sobrien 1367169942Sobrien ptr++; 1368169942Sobrien if (check_format_type(ptr, file_formats[m->type]) == -1) { 1369169942Sobrien /* 1370169942Sobrien * TODO: this error message is unhelpful if the format 1371169942Sobrien * string is not one character long 1372169942Sobrien */ 1373169942Sobrien file_error(ms, 0, "Printf format `%c' is not valid for type " 1374169942Sobrien " `%s' in description `%s'", *ptr, 1375169942Sobrien file_names[m->type], m->desc); 1376169942Sobrien return -1; 1377169942Sobrien } 1378169942Sobrien 1379133359Sobrien for (; *ptr; ptr++) { 1380169942Sobrien if (*ptr == '%') { 1381169942Sobrien file_error(ms, 0, 1382169942Sobrien "Too many format strings (should have at most one) " 1383169942Sobrien "for `%s' with description `%s'", 1384169942Sobrien file_names[m->type], m->desc); 1385169942Sobrien return -1; 1386133359Sobrien } 1387133359Sobrien } 1388169942Sobrien return 0; 1389133359Sobrien} 1390133359Sobrien 139168349Sobrien/* 139268349Sobrien * Read a numeric value from a pointer, into the value union of a magic 139368349Sobrien * pointer, according to the magic type. Update the string pointer to point 139468349Sobrien * just after the number read. Return 0 for success, non-zero for failure. 139568349Sobrien */ 1396133359Sobrienprivate int 1397169962Sobriengetvalue(struct magic_set *ms, struct magic *m, const char **p, int action) 139868349Sobrien{ 139968349Sobrien int slen; 140068349Sobrien 1401133359Sobrien switch (m->type) { 1402139368Sobrien case FILE_BESTRING16: 1403139368Sobrien case FILE_LESTRING16: 1404133359Sobrien case FILE_STRING: 1405133359Sobrien case FILE_PSTRING: 1406133359Sobrien case FILE_REGEX: 1407159764Sobrien case FILE_SEARCH: 1408169962Sobrien *p = getstr(ms, *p, m->value.s, sizeof(m->value.s), &slen, action); 1409133359Sobrien if (*p == NULL) { 1410133359Sobrien if (ms->flags & MAGIC_CHECK) 1411139368Sobrien file_magwarn(ms, "cannot get string from `%s'", 1412133359Sobrien m->value.s); 1413133359Sobrien return -1; 1414133359Sobrien } 141568349Sobrien m->vallen = slen; 1416133359Sobrien return 0; 1417175296Sobrien case FILE_FLOAT: 1418175296Sobrien case FILE_BEFLOAT: 1419175296Sobrien case FILE_LEFLOAT: 1420175296Sobrien if (m->reln != 'x') { 1421175296Sobrien char *ep; 1422175296Sobrien#ifdef HAVE_STRTOF 1423175296Sobrien m->value.f = strtof(*p, &ep); 1424175296Sobrien#else 1425175296Sobrien m->value.f = (float)strtod(*p, &ep); 1426175296Sobrien#endif 1427175296Sobrien *p = ep; 1428175296Sobrien } 1429175296Sobrien return 0; 1430175296Sobrien case FILE_DOUBLE: 1431175296Sobrien case FILE_BEDOUBLE: 1432175296Sobrien case FILE_LEDOUBLE: 1433175296Sobrien if (m->reln != 'x') { 1434175296Sobrien char *ep; 1435175296Sobrien m->value.d = strtod(*p, &ep); 1436175296Sobrien *p = ep; 1437175296Sobrien } 1438175296Sobrien return 0; 1439133359Sobrien default: 144068349Sobrien if (m->reln != 'x') { 1441159764Sobrien char *ep; 1442169942Sobrien m->value.q = file_signextend(ms, m, 1443169942Sobrien (uint64_t)strtoull(*p, &ep, 0)); 1444159764Sobrien *p = ep; 144568349Sobrien eatsize(p); 144668349Sobrien } 1447133359Sobrien return 0; 1448133359Sobrien } 144968349Sobrien} 145068349Sobrien 145168349Sobrien/* 145268349Sobrien * Convert a string containing C character escapes. Stop at an unescaped 145368349Sobrien * space or tab. 145468349Sobrien * Copy the converted version to "p", returning its length in *slen. 145568349Sobrien * Return updated scan pointer as function result. 145668349Sobrien */ 1457159764Sobrienprivate const char * 1458169962Sobriengetstr(struct magic_set *ms, const char *s, char *p, int plen, int *slen, int action) 145968349Sobrien{ 1460159764Sobrien const char *origs = s; 1461159764Sobrien char *origp = p; 146268349Sobrien char *pmax = p + plen - 1; 146368349Sobrien int c; 146468349Sobrien int val; 146568349Sobrien 146668349Sobrien while ((c = *s++) != '\0') { 146768349Sobrien if (isspace((unsigned char) c)) 146868349Sobrien break; 146968349Sobrien if (p >= pmax) { 1470133359Sobrien file_error(ms, 0, "string too long: `%s'", origs); 1471133359Sobrien return NULL; 147268349Sobrien } 1473169962Sobrien if (c == '\\') { 147468349Sobrien switch(c = *s++) { 147568349Sobrien 147668349Sobrien case '\0': 1477169962Sobrien if (action == FILE_COMPILE) 1478169962Sobrien file_magwarn(ms, "incomplete escape"); 147968349Sobrien goto out; 148068349Sobrien 1481169962Sobrien case '\t': 1482169962Sobrien if (action == FILE_COMPILE) { 1483169962Sobrien file_magwarn(ms, 1484169962Sobrien "escaped tab found, use \\t instead"); 1485169962Sobrien action++; 1486169962Sobrien } 1487169962Sobrien /*FALLTHROUGH*/ 148868349Sobrien default: 1489169962Sobrien if (action == FILE_COMPILE) { 1490169962Sobrien if (isprint((unsigned char)c)) 1491169962Sobrien file_magwarn(ms, 1492169962Sobrien "no need to escape `%c'", c); 1493169962Sobrien else 1494169962Sobrien file_magwarn(ms, 1495169962Sobrien "unknown escape sequence: \\%03o", c); 1496169962Sobrien } 1497169962Sobrien /*FALLTHROUGH*/ 1498169962Sobrien /* space, perhaps force people to use \040? */ 1499169962Sobrien case ' ': 1500169962Sobrien#if 0 1501169962Sobrien /* 1502169962Sobrien * Other things people escape, but shouldn't need to, 1503169962Sobrien * so we disallow them 1504169962Sobrien */ 1505169962Sobrien case '\'': 1506169962Sobrien case '"': 1507169962Sobrien case '?': 1508169962Sobrien#endif 1509169962Sobrien /* Relations */ 1510169962Sobrien case '>': 1511169962Sobrien case '<': 1512169962Sobrien case '&': 1513169962Sobrien case '^': 1514169962Sobrien case '=': 1515169962Sobrien case '!': 1516169962Sobrien /* and baskslash itself */ 1517169962Sobrien case '\\': 151868349Sobrien *p++ = (char) c; 151968349Sobrien break; 152068349Sobrien 1521169962Sobrien case 'a': 1522169962Sobrien *p++ = '\a'; 1523169962Sobrien break; 1524169962Sobrien 1525169962Sobrien case 'b': 1526169962Sobrien *p++ = '\b'; 1527169962Sobrien break; 1528169962Sobrien 1529169962Sobrien case 'f': 1530169962Sobrien *p++ = '\f'; 1531169962Sobrien break; 1532169962Sobrien 153368349Sobrien case 'n': 153468349Sobrien *p++ = '\n'; 153568349Sobrien break; 153668349Sobrien 153768349Sobrien case 'r': 153868349Sobrien *p++ = '\r'; 153968349Sobrien break; 154068349Sobrien 154168349Sobrien case 't': 154268349Sobrien *p++ = '\t'; 154368349Sobrien break; 154468349Sobrien 154568349Sobrien case 'v': 154668349Sobrien *p++ = '\v'; 154768349Sobrien break; 154868349Sobrien 154968349Sobrien /* \ and up to 3 octal digits */ 155068349Sobrien case '0': 155168349Sobrien case '1': 155268349Sobrien case '2': 155368349Sobrien case '3': 155468349Sobrien case '4': 155568349Sobrien case '5': 155668349Sobrien case '6': 155768349Sobrien case '7': 155868349Sobrien val = c - '0'; 155968349Sobrien c = *s++; /* try for 2 */ 1560169962Sobrien if (c >= '0' && c <= '7') { 1561169962Sobrien val = (val << 3) | (c - '0'); 156268349Sobrien c = *s++; /* try for 3 */ 1563169962Sobrien if (c >= '0' && c <= '7') 1564169962Sobrien val = (val << 3) | (c-'0'); 156568349Sobrien else 156668349Sobrien --s; 156768349Sobrien } 156868349Sobrien else 156968349Sobrien --s; 157068349Sobrien *p++ = (char)val; 157168349Sobrien break; 157268349Sobrien 157368349Sobrien /* \x and up to 2 hex digits */ 157468349Sobrien case 'x': 157568349Sobrien val = 'x'; /* Default if no digits */ 157668349Sobrien c = hextoint(*s++); /* Get next char */ 157768349Sobrien if (c >= 0) { 157868349Sobrien val = c; 157968349Sobrien c = hextoint(*s++); 158068349Sobrien if (c >= 0) 158168349Sobrien val = (val << 4) + c; 158268349Sobrien else 158368349Sobrien --s; 158468349Sobrien } else 158568349Sobrien --s; 158668349Sobrien *p++ = (char)val; 158768349Sobrien break; 158868349Sobrien } 158968349Sobrien } else 159068349Sobrien *p++ = (char)c; 159168349Sobrien } 159268349Sobrienout: 159368349Sobrien *p = '\0'; 159468349Sobrien *slen = p - origp; 159568349Sobrien return s; 159668349Sobrien} 159768349Sobrien 159868349Sobrien 159968349Sobrien/* Single hex char to int; -1 if not a hex char. */ 1600133359Sobrienprivate int 1601103373Sobrienhextoint(int c) 160268349Sobrien{ 160368349Sobrien if (!isascii((unsigned char) c)) 160468349Sobrien return -1; 160568349Sobrien if (isdigit((unsigned char) c)) 160668349Sobrien return c - '0'; 1607169962Sobrien if ((c >= 'a') && (c <= 'f')) 160868349Sobrien return c + 10 - 'a'; 1609169962Sobrien if (( c>= 'A') && (c <= 'F')) 161068349Sobrien return c + 10 - 'A'; 161168349Sobrien return -1; 161268349Sobrien} 161368349Sobrien 161468349Sobrien 161568349Sobrien/* 161668349Sobrien * Print a string containing C character escapes. 161768349Sobrien */ 1618133359Sobrienprotected void 1619133359Sobrienfile_showstr(FILE *fp, const char *s, size_t len) 162068349Sobrien{ 162168349Sobrien char c; 162268349Sobrien 162368349Sobrien for (;;) { 162468349Sobrien c = *s++; 1625133359Sobrien if (len == ~0U) { 162668349Sobrien if (c == '\0') 162768349Sobrien break; 162868349Sobrien } 162968349Sobrien else { 163068349Sobrien if (len-- == 0) 163168349Sobrien break; 163268349Sobrien } 1633169962Sobrien if (c >= 040 && c <= 0176) /* TODO isprint && !iscntrl */ 163468349Sobrien (void) fputc(c, fp); 163568349Sobrien else { 163668349Sobrien (void) fputc('\\', fp); 163768349Sobrien switch (c) { 1638169962Sobrien case '\a': 1639169962Sobrien (void) fputc('a', fp); 1640169962Sobrien break; 1641169962Sobrien 1642169962Sobrien case '\b': 1643169962Sobrien (void) fputc('b', fp); 1644169962Sobrien break; 1645169962Sobrien 1646169962Sobrien case '\f': 1647169962Sobrien (void) fputc('f', fp); 1648169962Sobrien break; 1649169962Sobrien 165068349Sobrien case '\n': 165168349Sobrien (void) fputc('n', fp); 165268349Sobrien break; 165368349Sobrien 165468349Sobrien case '\r': 165568349Sobrien (void) fputc('r', fp); 165668349Sobrien break; 165768349Sobrien 165868349Sobrien case '\t': 165968349Sobrien (void) fputc('t', fp); 166068349Sobrien break; 166168349Sobrien 166268349Sobrien case '\v': 166368349Sobrien (void) fputc('v', fp); 166468349Sobrien break; 166568349Sobrien 166668349Sobrien default: 166768349Sobrien (void) fprintf(fp, "%.3o", c & 0377); 166868349Sobrien break; 166968349Sobrien } 167068349Sobrien } 167168349Sobrien } 167268349Sobrien} 167368349Sobrien 167468349Sobrien/* 167568349Sobrien * eatsize(): Eat the size spec from a number [eg. 10UL] 167668349Sobrien */ 1677133359Sobrienprivate void 1678159764Sobrieneatsize(const char **p) 167968349Sobrien{ 1680159764Sobrien const char *l = *p; 168168349Sobrien 168268349Sobrien if (LOWCASE(*l) == 'u') 168368349Sobrien l++; 168468349Sobrien 168568349Sobrien switch (LOWCASE(*l)) { 168668349Sobrien case 'l': /* long */ 168768349Sobrien case 's': /* short */ 168868349Sobrien case 'h': /* short */ 168968349Sobrien case 'b': /* char/byte */ 169068349Sobrien case 'c': /* char/byte */ 169168349Sobrien l++; 169268349Sobrien /*FALLTHROUGH*/ 169368349Sobrien default: 169468349Sobrien break; 169568349Sobrien } 169668349Sobrien 169768349Sobrien *p = l; 169868349Sobrien} 169974784Sobrien 170074784Sobrien/* 1701103373Sobrien * handle a compiled file. 170274784Sobrien */ 1703133359Sobrienprivate int 1704133359Sobrienapprentice_map(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp, 1705133359Sobrien const char *fn) 170674784Sobrien{ 170774784Sobrien int fd; 170874784Sobrien struct stat st; 1709103373Sobrien uint32_t *ptr; 1710103373Sobrien uint32_t version; 171174784Sobrien int needsbyteswap; 1712133359Sobrien char buf[MAXPATHLEN]; 1713139368Sobrien char *dbname = mkdbname(fn, buf, sizeof(buf), 0); 1714133359Sobrien void *mm = NULL; 171574784Sobrien 171680588Sobrien if (dbname == NULL) 171780588Sobrien return -1; 171880588Sobrien 1719159764Sobrien if ((fd = open(dbname, O_RDONLY|O_BINARY)) == -1) 172074784Sobrien return -1; 172174784Sobrien 172274784Sobrien if (fstat(fd, &st) == -1) { 1723133359Sobrien file_error(ms, errno, "cannot stat `%s'", dbname); 172474784Sobrien goto error; 172574784Sobrien } 1726133359Sobrien if (st.st_size < 16) { 1727133359Sobrien file_error(ms, 0, "file `%s' is too small", dbname); 1728133359Sobrien goto error; 1729133359Sobrien } 173074784Sobrien 173180588Sobrien#ifdef QUICK 1732103373Sobrien if ((mm = mmap(0, (size_t)st.st_size, PROT_READ|PROT_WRITE, 173374784Sobrien MAP_PRIVATE|MAP_FILE, fd, (off_t)0)) == MAP_FAILED) { 1734133359Sobrien file_error(ms, errno, "cannot map `%s'", dbname); 173574784Sobrien goto error; 173674784Sobrien } 1737133359Sobrien#define RET 2 173880588Sobrien#else 1739103373Sobrien if ((mm = malloc((size_t)st.st_size)) == NULL) { 1740169942Sobrien file_oomem(ms, (size_t)st.st_size); 174180588Sobrien goto error; 174280588Sobrien } 1743103373Sobrien if (read(fd, mm, (size_t)st.st_size) != (size_t)st.st_size) { 1744133359Sobrien file_badread(ms); 174580588Sobrien goto error; 174680588Sobrien } 1747133359Sobrien#define RET 1 174880588Sobrien#endif 1749103373Sobrien *magicp = mm; 175074784Sobrien (void)close(fd); 175175937Sobrien fd = -1; 1752133359Sobrien ptr = (uint32_t *)(void *)*magicp; 175374784Sobrien if (*ptr != MAGICNO) { 175474784Sobrien if (swap4(*ptr) != MAGICNO) { 1755133359Sobrien file_error(ms, 0, "bad magic in `%s'"); 175674784Sobrien goto error; 175774784Sobrien } 175874784Sobrien needsbyteswap = 1; 175974784Sobrien } else 176074784Sobrien needsbyteswap = 0; 176174784Sobrien if (needsbyteswap) 176274784Sobrien version = swap4(ptr[1]); 176374784Sobrien else 176474784Sobrien version = ptr[1]; 176574784Sobrien if (version != VERSIONNO) { 1766175296Sobrien file_error(ms, 0, "File %d.%d supports only %d version magic " 1767175296Sobrien "files. `%s' is version %d", FILE_VERSION_MAJOR, patchlevel, 1768175296Sobrien VERSIONNO, dbname, version); 176974784Sobrien goto error; 177074784Sobrien } 1771133359Sobrien *nmagicp = (uint32_t)(st.st_size / sizeof(struct magic)) - 1; 177274784Sobrien (*magicp)++; 177374784Sobrien if (needsbyteswap) 177474784Sobrien byteswap(*magicp, *nmagicp); 1775133359Sobrien return RET; 177674784Sobrien 177774784Sobrienerror: 177874784Sobrien if (fd != -1) 177974784Sobrien (void)close(fd); 1780103373Sobrien if (mm) { 178180588Sobrien#ifdef QUICK 1782133359Sobrien (void)munmap((void *)mm, (size_t)st.st_size); 178380588Sobrien#else 1784103373Sobrien free(mm); 178580588Sobrien#endif 178680588Sobrien } else { 178774784Sobrien *magicp = NULL; 178874784Sobrien *nmagicp = 0; 178974784Sobrien } 179074784Sobrien return -1; 179174784Sobrien} 179274784Sobrien 1793133359Sobrienprivate const uint32_t ar[] = { 1794133359Sobrien MAGICNO, VERSIONNO 1795133359Sobrien}; 179674784Sobrien/* 179774784Sobrien * handle an mmaped file. 179874784Sobrien */ 1799133359Sobrienprivate int 1800133359Sobrienapprentice_compile(struct magic_set *ms, struct magic **magicp, 1801133359Sobrien uint32_t *nmagicp, const char *fn) 180274784Sobrien{ 180374784Sobrien int fd; 1804133359Sobrien char buf[MAXPATHLEN]; 1805139368Sobrien char *dbname = mkdbname(fn, buf, sizeof(buf), 1); 180674784Sobrien 180780588Sobrien if (dbname == NULL) 180880588Sobrien return -1; 180980588Sobrien 1810159764Sobrien if ((fd = open(dbname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644)) == -1) { 1811133359Sobrien file_error(ms, errno, "cannot open `%s'", dbname); 181274784Sobrien return -1; 181374784Sobrien } 181474784Sobrien 1815133359Sobrien if (write(fd, ar, sizeof(ar)) != (ssize_t)sizeof(ar)) { 1816133359Sobrien file_error(ms, errno, "error writing `%s'", dbname); 181774784Sobrien return -1; 181874784Sobrien } 181974784Sobrien 1820133359Sobrien if (lseek(fd, (off_t)sizeof(struct magic), SEEK_SET) 1821133359Sobrien != sizeof(struct magic)) { 1822133359Sobrien file_error(ms, errno, "error seeking `%s'", dbname); 182374784Sobrien return -1; 182474784Sobrien } 182574784Sobrien 1826133359Sobrien if (write(fd, *magicp, (sizeof(struct magic) * *nmagicp)) 1827133359Sobrien != (ssize_t)(sizeof(struct magic) * *nmagicp)) { 1828133359Sobrien file_error(ms, errno, "error writing `%s'", dbname); 182974784Sobrien return -1; 183074784Sobrien } 183174784Sobrien 183274784Sobrien (void)close(fd); 183374784Sobrien return 0; 183474784Sobrien} 183574784Sobrien 1836133359Sobrienprivate const char ext[] = ".mgc"; 183774784Sobrien/* 183874784Sobrien * make a dbname 183974784Sobrien */ 1840133359Sobrienprivate char * 1841139368Sobrienmkdbname(const char *fn, char *buf, size_t bufsiz, int strip) 184274784Sobrien{ 1843139368Sobrien if (strip) { 1844139368Sobrien const char *p; 1845139368Sobrien if ((p = strrchr(fn, '/')) != NULL) 1846139368Sobrien fn = ++p; 1847139368Sobrien } 1848139368Sobrien 1849133359Sobrien (void)snprintf(buf, bufsiz, "%s%s", fn, ext); 185074784Sobrien return buf; 185174784Sobrien} 185274784Sobrien 185374784Sobrien/* 185474784Sobrien * Byteswap an mmap'ed file if needed 185574784Sobrien */ 1856133359Sobrienprivate void 1857103373Sobrienbyteswap(struct magic *magic, uint32_t nmagic) 185874784Sobrien{ 1859103373Sobrien uint32_t i; 186074784Sobrien for (i = 0; i < nmagic; i++) 186174784Sobrien bs1(&magic[i]); 186274784Sobrien} 186374784Sobrien 186474784Sobrien/* 186574784Sobrien * swap a short 186674784Sobrien */ 1867133359Sobrienprivate uint16_t 1868103373Sobrienswap2(uint16_t sv) 186974784Sobrien{ 1870103373Sobrien uint16_t rv; 1871133359Sobrien uint8_t *s = (uint8_t *)(void *)&sv; 1872133359Sobrien uint8_t *d = (uint8_t *)(void *)&rv; 187374784Sobrien d[0] = s[1]; 187474784Sobrien d[1] = s[0]; 187574784Sobrien return rv; 187674784Sobrien} 187774784Sobrien 187874784Sobrien/* 187974784Sobrien * swap an int 188074784Sobrien */ 1881133359Sobrienprivate uint32_t 1882103373Sobrienswap4(uint32_t sv) 188374784Sobrien{ 1884103373Sobrien uint32_t rv; 1885133359Sobrien uint8_t *s = (uint8_t *)(void *)&sv; 1886133359Sobrien uint8_t *d = (uint8_t *)(void *)&rv; 188774784Sobrien d[0] = s[3]; 188874784Sobrien d[1] = s[2]; 188974784Sobrien d[2] = s[1]; 189074784Sobrien d[3] = s[0]; 189174784Sobrien return rv; 189274784Sobrien} 189374784Sobrien 189474784Sobrien/* 1895169942Sobrien * swap a quad 1896169942Sobrien */ 1897169942Sobrienprivate uint64_t 1898169942Sobrienswap8(uint64_t sv) 1899169942Sobrien{ 1900169942Sobrien uint32_t rv; 1901169942Sobrien uint8_t *s = (uint8_t *)(void *)&sv; 1902169942Sobrien uint8_t *d = (uint8_t *)(void *)&rv; 1903169942Sobrien d[0] = s[3]; 1904169942Sobrien d[1] = s[2]; 1905169942Sobrien d[2] = s[1]; 1906169942Sobrien d[3] = s[0]; 1907169942Sobrien d[4] = s[7]; 1908169942Sobrien d[5] = s[6]; 1909169942Sobrien d[6] = s[5]; 1910169942Sobrien d[7] = s[4]; 1911169942Sobrien return rv; 1912169942Sobrien} 1913169942Sobrien 1914169942Sobrien/* 191574784Sobrien * byteswap a single magic entry 191674784Sobrien */ 1917133359Sobrienprivate void 1918133359Sobrienbs1(struct magic *m) 191974784Sobrien{ 192074784Sobrien m->cont_level = swap2(m->cont_level); 1921133359Sobrien m->offset = swap4((uint32_t)m->offset); 1922133359Sobrien m->in_offset = swap4((uint32_t)m->in_offset); 1923169962Sobrien m->lineno = swap4((uint32_t)m->lineno); 1924169962Sobrien if (IS_STRING(m->type)) { 1925169962Sobrien m->str_count = swap4(m->str_count); 1926169962Sobrien m->str_flags = swap4(m->str_flags); 1927169962Sobrien } 1928169962Sobrien else { 1929169942Sobrien m->value.q = swap8(m->value.q); 1930169962Sobrien m->num_mask = swap8(m->num_mask); 1931169962Sobrien } 193274784Sobrien} 1933