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" 33191771Sobrien 34191771Sobrien#ifndef lint 35234449SobrienFILE_RCSID("@(#)$File: apprentice.c,v 1.173 2011/12/08 12:38:24 rrt Exp $") 36191771Sobrien#endif /* lint */ 37191771Sobrien 38133359Sobrien#include "magic.h" 3968349Sobrien#include <stdlib.h> 4084685Sobrien#ifdef HAVE_UNISTD_H 4184685Sobrien#include <unistd.h> 4284685Sobrien#endif 4368349Sobrien#include <string.h> 44169942Sobrien#include <assert.h> 4568349Sobrien#include <ctype.h> 46103373Sobrien#include <fcntl.h> 4774784Sobrien#ifdef QUICK 4874784Sobrien#include <sys/mman.h> 4974784Sobrien#endif 50186691Sobrien#include <dirent.h> 5168349Sobrien 5268349Sobrien#define EATAB {while (isascii((unsigned char) *l) && \ 5368349Sobrien isspace((unsigned char) *l)) ++l;} 5468349Sobrien#define LOWCASE(l) (isupper((unsigned char) (l)) ? \ 5568349Sobrien tolower((unsigned char) (l)) : (l)) 5675937Sobrien/* 5775937Sobrien * Work around a bug in headers on Digital Unix. 5875937Sobrien * At least confirmed for: OSF1 V4.0 878 5975937Sobrien */ 6075937Sobrien#if defined(__osf__) && defined(__DECC) 6175937Sobrien#ifdef MAP_FAILED 6275937Sobrien#undef MAP_FAILED 6375937Sobrien#endif 6475937Sobrien#endif 6568349Sobrien 6675937Sobrien#ifndef MAP_FAILED 6775937Sobrien#define MAP_FAILED (void *) -1 6875937Sobrien#endif 6968349Sobrien 7075937Sobrien#ifndef MAP_FILE 7175937Sobrien#define MAP_FILE 0 7275937Sobrien#endif 7375937Sobrien 74159764Sobrienstruct magic_entry { 75159764Sobrien struct magic *mp; 76159764Sobrien uint32_t cont_count; 77159764Sobrien uint32_t max_count; 78159764Sobrien}; 79159764Sobrien 80169962Sobrienint file_formats[FILE_NAMES_SIZE]; 81169962Sobrienconst size_t file_nformats = FILE_NAMES_SIZE; 82169962Sobrienconst char *file_names[FILE_NAMES_SIZE]; 83169962Sobrienconst size_t file_nnames = FILE_NAMES_SIZE; 84169942Sobrien 85169962Sobrienprivate int getvalue(struct magic_set *ms, struct magic *, const char **, int); 86133359Sobrienprivate int hextoint(int); 87192350Sdelphijprivate const char *getstr(struct magic_set *, struct magic *, const char *, 88192350Sdelphij int); 89159764Sobrienprivate int parse(struct magic_set *, struct magic_entry **, uint32_t *, 90169942Sobrien const char *, size_t, int); 91159764Sobrienprivate void eatsize(const char **); 92133359Sobrienprivate int apprentice_1(struct magic_set *, const char *, int, struct mlist *); 93159764Sobrienprivate size_t apprentice_magic_strength(const struct magic *); 94159764Sobrienprivate int apprentice_sort(const void *, const void *); 95234449Sobrien#ifndef COMPILE_ONLY 96234449Sobrienprivate void apprentice_list(struct mlist *, int ); 97234449Sobrien#endif 98186691Sobrienprivate int apprentice_load(struct magic_set *, struct magic **, uint32_t *, 99133359Sobrien const char *, int); 100210761Srpaulo#ifndef COMPILE_ONLY 101133359Sobrienprivate void byteswap(struct magic *, uint32_t); 102133359Sobrienprivate void bs1(struct magic *); 103133359Sobrienprivate uint16_t swap2(uint16_t); 104133359Sobrienprivate uint32_t swap4(uint32_t); 105169942Sobrienprivate uint64_t swap8(uint64_t); 106210761Srpaulo#endif 107191771Sobrienprivate char *mkdbname(struct magic_set *, const char *, int); 108210761Srpaulo#ifndef COMPILE_ONLY 109133359Sobrienprivate int apprentice_map(struct magic_set *, struct magic **, uint32_t *, 110133359Sobrien const char *); 111210761Srpaulo#endif 112133359Sobrienprivate int apprentice_compile(struct magic_set *, struct magic **, uint32_t *, 113133359Sobrien const char *); 114169942Sobrienprivate int check_format_type(const char *, int); 115139368Sobrienprivate int check_format(struct magic_set *, struct magic *); 116175296Sobrienprivate int get_op(char); 117186691Sobrienprivate int parse_mime(struct magic_set *, struct magic_entry *, const char *); 118191771Sobrienprivate int parse_strength(struct magic_set *, struct magic_entry *, const char *); 119191771Sobrienprivate int parse_apple(struct magic_set *, struct magic_entry *, const char *); 12068349Sobrien 121186691Sobrien 122133359Sobrienprivate size_t maxmagic = 0; 123133359Sobrienprivate size_t magicsize = sizeof(struct magic); 12468349Sobrien 125186691Sobrienprivate const char usg_hdr[] = "cont\toffset\ttype\topcode\tmask\tvalue\tdesc"; 126159764Sobrien 127186691Sobrienprivate struct { 128186691Sobrien const char *name; 129186691Sobrien size_t len; 130186691Sobrien int (*fun)(struct magic_set *, struct magic_entry *, const char *); 131186691Sobrien} bang[] = { 132186691Sobrien#define DECLARE_FIELD(name) { # name, sizeof(# name) - 1, parse_ ## name } 133186691Sobrien DECLARE_FIELD(mime), 134191771Sobrien DECLARE_FIELD(apple), 135186691Sobrien DECLARE_FIELD(strength), 136186691Sobrien#undef DECLARE_FIELD 137186691Sobrien { NULL, 0, NULL } 138186691Sobrien}; 139186691Sobrien 14080588Sobrien#ifdef COMPILE_ONLY 14174784Sobrien 142103373Sobrienint main(int, char *[]); 14380588Sobrien 14480588Sobrienint 145103373Sobrienmain(int argc, char *argv[]) 14680588Sobrien{ 14780588Sobrien int ret; 148133359Sobrien struct magic_set *ms; 149133359Sobrien char *progname; 15080588Sobrien 15180588Sobrien if ((progname = strrchr(argv[0], '/')) != NULL) 15280588Sobrien progname++; 15380588Sobrien else 15480588Sobrien progname = argv[0]; 15580588Sobrien 15680588Sobrien if (argc != 2) { 157133359Sobrien (void)fprintf(stderr, "Usage: %s file\n", progname); 158133359Sobrien return 1; 15980588Sobrien } 16080588Sobrien 161133359Sobrien if ((ms = magic_open(MAGIC_CHECK)) == NULL) { 162133359Sobrien (void)fprintf(stderr, "%s: %s\n", progname, strerror(errno)); 163133359Sobrien return 1; 164133359Sobrien } 165133359Sobrien ret = magic_compile(ms, argv[1]) == -1 ? 1 : 0; 166133359Sobrien if (ret == 1) 167133359Sobrien (void)fprintf(stderr, "%s: %s\n", progname, magic_error(ms)); 168133359Sobrien magic_close(ms); 169133359Sobrien return ret; 17080588Sobrien} 17180588Sobrien#endif /* COMPILE_ONLY */ 17280588Sobrien 173169962Sobrienstatic const struct type_tbl_s { 174186691Sobrien const char name[16]; 175169962Sobrien const size_t len; 176169962Sobrien const int type; 177169962Sobrien const int format; 178169962Sobrien} type_tbl[] = { 179169962Sobrien# define XX(s) s, (sizeof(s) - 1) 180186691Sobrien# define XX_NULL "", 0 181169962Sobrien { XX("byte"), FILE_BYTE, FILE_FMT_NUM }, 182169962Sobrien { XX("short"), FILE_SHORT, FILE_FMT_NUM }, 183169962Sobrien { XX("default"), FILE_DEFAULT, FILE_FMT_STR }, 184169962Sobrien { XX("long"), FILE_LONG, FILE_FMT_NUM }, 185169962Sobrien { XX("string"), FILE_STRING, FILE_FMT_STR }, 186169962Sobrien { XX("date"), FILE_DATE, FILE_FMT_STR }, 187169962Sobrien { XX("beshort"), FILE_BESHORT, FILE_FMT_NUM }, 188169962Sobrien { XX("belong"), FILE_BELONG, FILE_FMT_NUM }, 189169962Sobrien { XX("bedate"), FILE_BEDATE, FILE_FMT_STR }, 190169962Sobrien { XX("leshort"), FILE_LESHORT, FILE_FMT_NUM }, 191169962Sobrien { XX("lelong"), FILE_LELONG, FILE_FMT_NUM }, 192169962Sobrien { XX("ledate"), FILE_LEDATE, FILE_FMT_STR }, 193169962Sobrien { XX("pstring"), FILE_PSTRING, FILE_FMT_STR }, 194169962Sobrien { XX("ldate"), FILE_LDATE, FILE_FMT_STR }, 195169962Sobrien { XX("beldate"), FILE_BELDATE, FILE_FMT_STR }, 196169962Sobrien { XX("leldate"), FILE_LELDATE, FILE_FMT_STR }, 197169962Sobrien { XX("regex"), FILE_REGEX, FILE_FMT_STR }, 198169962Sobrien { XX("bestring16"), FILE_BESTRING16, FILE_FMT_STR }, 199169962Sobrien { XX("lestring16"), FILE_LESTRING16, FILE_FMT_STR }, 200169962Sobrien { XX("search"), FILE_SEARCH, FILE_FMT_STR }, 201169962Sobrien { XX("medate"), FILE_MEDATE, FILE_FMT_STR }, 202169962Sobrien { XX("meldate"), FILE_MELDATE, FILE_FMT_STR }, 203169962Sobrien { XX("melong"), FILE_MELONG, FILE_FMT_NUM }, 204169962Sobrien { XX("quad"), FILE_QUAD, FILE_FMT_QUAD }, 205169962Sobrien { XX("lequad"), FILE_LEQUAD, FILE_FMT_QUAD }, 206169962Sobrien { XX("bequad"), FILE_BEQUAD, FILE_FMT_QUAD }, 207169962Sobrien { XX("qdate"), FILE_QDATE, FILE_FMT_STR }, 208169962Sobrien { XX("leqdate"), FILE_LEQDATE, FILE_FMT_STR }, 209169962Sobrien { XX("beqdate"), FILE_BEQDATE, FILE_FMT_STR }, 210169962Sobrien { XX("qldate"), FILE_QLDATE, FILE_FMT_STR }, 211169962Sobrien { XX("leqldate"), FILE_LEQLDATE, FILE_FMT_STR }, 212169962Sobrien { XX("beqldate"), FILE_BEQLDATE, FILE_FMT_STR }, 213175296Sobrien { XX("float"), FILE_FLOAT, FILE_FMT_FLOAT }, 214175296Sobrien { XX("befloat"), FILE_BEFLOAT, FILE_FMT_FLOAT }, 215175296Sobrien { XX("lefloat"), FILE_LEFLOAT, FILE_FMT_FLOAT }, 216175296Sobrien { XX("double"), FILE_DOUBLE, FILE_FMT_DOUBLE }, 217175296Sobrien { XX("bedouble"), FILE_BEDOUBLE, FILE_FMT_DOUBLE }, 218175296Sobrien { XX("ledouble"), FILE_LEDOUBLE, FILE_FMT_DOUBLE }, 219191771Sobrien { XX("leid3"), FILE_LEID3, FILE_FMT_NUM }, 220191771Sobrien { XX("beid3"), FILE_BEID3, FILE_FMT_NUM }, 221191771Sobrien { XX("indirect"), FILE_INDIRECT, FILE_FMT_NONE }, 222169962Sobrien { XX_NULL, FILE_INVALID, FILE_FMT_NONE }, 223169962Sobrien# undef XX 224169962Sobrien# undef XX_NULL 225169962Sobrien}; 22680588Sobrien 227169962Sobrienprivate int 228169962Sobrienget_type(const char *l, const char **t) 229169962Sobrien{ 230169962Sobrien const struct type_tbl_s *p; 231169962Sobrien 232186691Sobrien for (p = type_tbl; p->len; p++) { 233169962Sobrien if (strncmp(l, p->name, p->len) == 0) { 234169962Sobrien if (t) 235169962Sobrien *t = l + p->len; 236169962Sobrien break; 237169962Sobrien } 238169962Sobrien } 239169962Sobrien return p->type; 240169962Sobrien} 241169962Sobrien 242169962Sobrienprivate void 243169962Sobrieninit_file_tables(void) 244169962Sobrien{ 245169962Sobrien static int done = 0; 246169962Sobrien const struct type_tbl_s *p; 247169962Sobrien 248169962Sobrien if (done) 249169962Sobrien return; 250169962Sobrien done++; 251169962Sobrien 252186691Sobrien for (p = type_tbl; p->len; p++) { 253169962Sobrien assert(p->type < FILE_NAMES_SIZE); 254169962Sobrien file_names[p->type] = p->name; 255169962Sobrien file_formats[p->type] = p->format; 256169962Sobrien } 257169962Sobrien} 258169962Sobrien 25974784Sobrien/* 260186691Sobrien * Handle one file or directory. 26174784Sobrien */ 262133359Sobrienprivate int 263133359Sobrienapprentice_1(struct magic_set *ms, const char *fn, int action, 264133359Sobrien struct mlist *mlist) 26574784Sobrien{ 26674784Sobrien struct magic *magic = NULL; 267103373Sobrien uint32_t nmagic = 0; 268210761Srpaulo#ifndef COMPILE_ONLY 26974784Sobrien struct mlist *ml; 270210761Srpaulo#endif 27174784Sobrien int rv = -1; 272210761Srpaulo#ifndef COMPILE_ONLY 273133359Sobrien int mapped; 274210761Srpaulo#endif 27574784Sobrien 276133359Sobrien if (magicsize != FILE_MAGICSIZE) { 277133359Sobrien file_error(ms, 0, "magic element size %lu != %lu", 278133359Sobrien (unsigned long)sizeof(*magic), 279133359Sobrien (unsigned long)FILE_MAGICSIZE); 280133359Sobrien return -1; 28174784Sobrien } 282133359Sobrien 283133359Sobrien if (action == FILE_COMPILE) { 284186691Sobrien rv = apprentice_load(ms, &magic, &nmagic, fn, action); 285133359Sobrien if (rv != 0) 286133359Sobrien return -1; 287133359Sobrien rv = apprentice_compile(ms, &magic, &nmagic, fn); 288133359Sobrien free(magic); 289133359Sobrien return rv; 290133359Sobrien } 291159764Sobrien 29280588Sobrien#ifndef COMPILE_ONLY 293133359Sobrien if ((rv = apprentice_map(ms, &magic, &nmagic, fn)) == -1) { 294133359Sobrien if (ms->flags & MAGIC_CHECK) 295139368Sobrien file_magwarn(ms, "using regular magic file `%s'", fn); 296186691Sobrien rv = apprentice_load(ms, &magic, &nmagic, fn, action); 297133359Sobrien if (rv != 0) 298133359Sobrien return -1; 299133359Sobrien } 30074784Sobrien 301133359Sobrien mapped = rv; 30274784Sobrien 303186691Sobrien if (magic == NULL) { 304133359Sobrien file_delmagic(magic, mapped, nmagic); 305133359Sobrien return -1; 306133359Sobrien } 307133359Sobrien 308186691Sobrien if ((ml = CAST(struct mlist *, malloc(sizeof(*ml)))) == NULL) { 309133359Sobrien file_delmagic(magic, mapped, nmagic); 310169942Sobrien file_oomem(ms, sizeof(*ml)); 311133359Sobrien return -1; 31274784Sobrien } 31374784Sobrien 31474784Sobrien ml->magic = magic; 31574784Sobrien ml->nmagic = nmagic; 316133359Sobrien ml->mapped = mapped; 31774784Sobrien 318133359Sobrien mlist->prev->next = ml; 319133359Sobrien ml->prev = mlist->prev; 320133359Sobrien ml->next = mlist; 321133359Sobrien mlist->prev = ml; 32274784Sobrien 323234449Sobrien if (action == FILE_LIST) { 324234449Sobrien printf("Binary patterns:\n"); 325234449Sobrien apprentice_list(mlist, BINTEST); 326234449Sobrien printf("Text patterns:\n"); 327234449Sobrien apprentice_list(mlist, TEXTTEST); 328234449Sobrien } 329210761Srpaulo#endif /* COMPILE_ONLY */ 330133359Sobrien return 0; 33174784Sobrien} 33274784Sobrien 333133359Sobrienprotected void 334133359Sobrienfile_delmagic(struct magic *p, int type, size_t entries) 335133359Sobrien{ 336133359Sobrien if (p == NULL) 337133359Sobrien return; 338133359Sobrien switch (type) { 339192350Sdelphij case 2: 340175296Sobrien#ifdef QUICK 341133359Sobrien p--; 342133359Sobrien (void)munmap((void *)p, sizeof(*p) * (entries + 1)); 343133359Sobrien break; 344192350Sdelphij#else 345192350Sdelphij (void)&entries; 346192350Sdelphij abort(); 347192350Sdelphij /*NOTREACHED*/ 348175296Sobrien#endif 349133359Sobrien case 1: 350133359Sobrien p--; 351133359Sobrien /*FALLTHROUGH*/ 352133359Sobrien case 0: 353133359Sobrien free(p); 354133359Sobrien break; 355133359Sobrien default: 356133359Sobrien abort(); 357133359Sobrien } 358133359Sobrien} 35974784Sobrien 360186691Sobrien/* const char *fn: list of magic files and directories */ 361133359Sobrienprotected struct mlist * 362133359Sobrienfile_apprentice(struct magic_set *ms, const char *fn, int action) 36368349Sobrien{ 364186691Sobrien char *p, *mfn; 36568349Sobrien int file_err, errs = -1; 366133359Sobrien struct mlist *mlist; 36768349Sobrien 368234449Sobrien if ((fn = magic_getpath(fn, action)) == NULL) 369234449Sobrien return NULL; 370234449Sobrien 371169962Sobrien init_file_tables(); 372169962Sobrien 373169962Sobrien if ((mfn = strdup(fn)) == NULL) { 374169942Sobrien file_oomem(ms, strlen(fn)); 375133359Sobrien return NULL; 37668349Sobrien } 377169962Sobrien fn = mfn; 378133359Sobrien 379186691Sobrien if ((mlist = CAST(struct mlist *, malloc(sizeof(*mlist)))) == NULL) { 380133359Sobrien free(mfn); 381169942Sobrien file_oomem(ms, sizeof(*mlist)); 382133359Sobrien return NULL; 383133359Sobrien } 384133359Sobrien mlist->next = mlist->prev = mlist; 385133359Sobrien 38668349Sobrien while (fn) { 38768349Sobrien p = strchr(fn, PATHSEP); 38868349Sobrien if (p) 38968349Sobrien *p++ = '\0'; 390133359Sobrien if (*fn == '\0') 391133359Sobrien break; 392133359Sobrien file_err = apprentice_1(ms, fn, action, mlist); 393186691Sobrien errs = MAX(errs, file_err); 39468349Sobrien fn = p; 39568349Sobrien } 396133359Sobrien if (errs == -1) { 397133359Sobrien free(mfn); 398133359Sobrien free(mlist); 399133359Sobrien mlist = NULL; 400133359Sobrien file_error(ms, 0, "could not find any magic files!"); 401133359Sobrien return NULL; 402133359Sobrien } 40368349Sobrien free(mfn); 404133359Sobrien return mlist; 40568349Sobrien} 40668349Sobrien 407169942Sobrien/* 408169942Sobrien * Get weight of this magic entry, for sorting purposes. 409169942Sobrien */ 410159764Sobrienprivate size_t 411159764Sobrienapprentice_magic_strength(const struct magic *m) 412159764Sobrien{ 413169942Sobrien#define MULT 10 414169942Sobrien size_t val = 2 * MULT; /* baseline strength */ 415169942Sobrien 416159764Sobrien switch (m->type) { 417169962Sobrien case FILE_DEFAULT: /* make sure this sorts last */ 418186691Sobrien if (m->factor_op != FILE_FACTOR_OP_NONE) 419186691Sobrien abort(); 420169962Sobrien return 0; 421169962Sobrien 422159764Sobrien case FILE_BYTE: 423169942Sobrien val += 1 * MULT; 424169942Sobrien break; 425159764Sobrien 426159764Sobrien case FILE_SHORT: 427159764Sobrien case FILE_LESHORT: 428159764Sobrien case FILE_BESHORT: 429169942Sobrien val += 2 * MULT; 430169942Sobrien break; 431159764Sobrien 432159764Sobrien case FILE_LONG: 433159764Sobrien case FILE_LELONG: 434159764Sobrien case FILE_BELONG: 435159764Sobrien case FILE_MELONG: 436169942Sobrien val += 4 * MULT; 437169942Sobrien break; 438159764Sobrien 439159764Sobrien case FILE_PSTRING: 440159764Sobrien case FILE_STRING: 441169942Sobrien val += m->vallen * MULT; 442169942Sobrien break; 443169942Sobrien 444159764Sobrien case FILE_BESTRING16: 445159764Sobrien case FILE_LESTRING16: 446169942Sobrien val += m->vallen * MULT / 2; 447169942Sobrien break; 448169942Sobrien 449159764Sobrien case FILE_SEARCH: 450169942Sobrien case FILE_REGEX: 451186691Sobrien val += m->vallen * MAX(MULT / m->vallen, 1); 452169942Sobrien break; 453159764Sobrien 454159764Sobrien case FILE_DATE: 455159764Sobrien case FILE_LEDATE: 456159764Sobrien case FILE_BEDATE: 457159764Sobrien case FILE_MEDATE: 458159764Sobrien case FILE_LDATE: 459159764Sobrien case FILE_LELDATE: 460159764Sobrien case FILE_BELDATE: 461159764Sobrien case FILE_MELDATE: 462175296Sobrien case FILE_FLOAT: 463175296Sobrien case FILE_BEFLOAT: 464175296Sobrien case FILE_LEFLOAT: 465169942Sobrien val += 4 * MULT; 466169942Sobrien break; 467159764Sobrien 468169942Sobrien case FILE_QUAD: 469169942Sobrien case FILE_BEQUAD: 470169942Sobrien case FILE_LEQUAD: 471169942Sobrien case FILE_QDATE: 472169942Sobrien case FILE_LEQDATE: 473169942Sobrien case FILE_BEQDATE: 474169942Sobrien case FILE_QLDATE: 475169942Sobrien case FILE_LEQLDATE: 476169942Sobrien case FILE_BEQLDATE: 477175296Sobrien case FILE_DOUBLE: 478175296Sobrien case FILE_BEDOUBLE: 479175296Sobrien case FILE_LEDOUBLE: 480169942Sobrien val += 8 * MULT; 481169942Sobrien break; 482169942Sobrien 483159764Sobrien default: 484169942Sobrien val = 0; 485169942Sobrien (void)fprintf(stderr, "Bad type %d\n", m->type); 486169942Sobrien abort(); 487159764Sobrien } 488169942Sobrien 489169942Sobrien switch (m->reln) { 490169942Sobrien case 'x': /* matches anything penalize */ 491186691Sobrien case '!': /* matches almost anything penalize */ 492169942Sobrien val = 0; 493169942Sobrien break; 494169942Sobrien 495169942Sobrien case '=': /* Exact match, prefer */ 496169942Sobrien val += MULT; 497169942Sobrien break; 498169942Sobrien 499169942Sobrien case '>': 500169942Sobrien case '<': /* comparison match reduce strength */ 501169942Sobrien val -= 2 * MULT; 502169942Sobrien break; 503169942Sobrien 504169942Sobrien case '^': 505169942Sobrien case '&': /* masking bits, we could count them too */ 506169942Sobrien val -= MULT; 507169942Sobrien break; 508169942Sobrien 509169942Sobrien default: 510169942Sobrien (void)fprintf(stderr, "Bad relation %c\n", m->reln); 511169942Sobrien abort(); 512169942Sobrien } 513169962Sobrien 514169962Sobrien if (val == 0) /* ensure we only return 0 for FILE_DEFAULT */ 515169962Sobrien val = 1; 516169962Sobrien 517186691Sobrien switch (m->factor_op) { 518186691Sobrien case FILE_FACTOR_OP_NONE: 519186691Sobrien break; 520186691Sobrien case FILE_FACTOR_OP_PLUS: 521186691Sobrien val += m->factor; 522186691Sobrien break; 523186691Sobrien case FILE_FACTOR_OP_MINUS: 524186691Sobrien val -= m->factor; 525186691Sobrien break; 526186691Sobrien case FILE_FACTOR_OP_TIMES: 527186691Sobrien val *= m->factor; 528186691Sobrien break; 529186691Sobrien case FILE_FACTOR_OP_DIV: 530186691Sobrien val /= m->factor; 531186691Sobrien break; 532186691Sobrien default: 533186691Sobrien abort(); 534186691Sobrien } 535186691Sobrien 536186691Sobrien /* 537186691Sobrien * Magic entries with no description get a bonus because they depend 538186691Sobrien * on subsequent magic entries to print something. 539186691Sobrien */ 540186691Sobrien if (m->desc[0] == '\0') 541186691Sobrien val++; 542169942Sobrien return val; 543159764Sobrien} 544159764Sobrien 545169942Sobrien/* 546169942Sobrien * Sort callback for sorting entries by "strength" (basically length) 547169942Sobrien */ 548159764Sobrienprivate int 549159764Sobrienapprentice_sort(const void *a, const void *b) 550159764Sobrien{ 551186691Sobrien const struct magic_entry *ma = CAST(const struct magic_entry *, a); 552186691Sobrien const struct magic_entry *mb = CAST(const struct magic_entry *, b); 553159764Sobrien size_t sa = apprentice_magic_strength(ma->mp); 554159764Sobrien size_t sb = apprentice_magic_strength(mb->mp); 555159764Sobrien if (sa == sb) 556159764Sobrien return 0; 557159764Sobrien else if (sa > sb) 558159764Sobrien return -1; 559159764Sobrien else 560159764Sobrien return 1; 561159764Sobrien} 562159764Sobrien 563234449Sobrien/* 564234449Sobrien * Shows sorted patterns list in the order which is used for the matching 565234449Sobrien */ 566234449Sobrien#ifndef COMPILE_ONLY 567186691Sobrienprivate void 568234449Sobrienapprentice_list(struct mlist *mlist, int mode) 569234449Sobrien{ 570234449Sobrien uint32_t magindex = 0; 571234449Sobrien struct mlist *ml; 572234449Sobrien for (ml = mlist->next; ml != mlist; ml = ml->next) { 573234449Sobrien for (magindex = 0; magindex < ml->nmagic; magindex++) { 574234449Sobrien struct magic *m = &ml->magic[magindex]; 575234449Sobrien if ((m->flag & mode) != mode) { 576234449Sobrien /* Skip sub-tests */ 577234449Sobrien while (magindex + 1 < ml->nmagic && 578234449Sobrien ml->magic[magindex + 1].cont_level != 0) 579234449Sobrien ++magindex; 580234449Sobrien continue; /* Skip to next top-level test*/ 581234449Sobrien } 582234449Sobrien 583234449Sobrien /* 584234449Sobrien * Try to iterate over the tree until we find item with 585234449Sobrien * description/mimetype. 586234449Sobrien */ 587234449Sobrien while (magindex + 1 < ml->nmagic && 588234449Sobrien ml->magic[magindex + 1].cont_level != 0 && 589234449Sobrien *ml->magic[magindex].desc == '\0' && 590234449Sobrien *ml->magic[magindex].mimetype == '\0') 591234449Sobrien magindex++; 592234449Sobrien 593234449Sobrien printf("Strength = %3" SIZE_T_FORMAT "u : %s [%s]\n", 594234449Sobrien apprentice_magic_strength(m), 595234449Sobrien ml->magic[magindex].desc, 596234449Sobrien ml->magic[magindex].mimetype); 597234449Sobrien } 598234449Sobrien } 599234449Sobrien} 600234449Sobrien#endif /* COMPILE_ONLY */ 601234449Sobrien 602234449Sobrienprivate void 603186691Sobrienset_test_type(struct magic *mstart, struct magic *m) 604186691Sobrien{ 605186691Sobrien switch (m->type) { 606186691Sobrien case FILE_BYTE: 607186691Sobrien case FILE_SHORT: 608186691Sobrien case FILE_LONG: 609186691Sobrien case FILE_DATE: 610186691Sobrien case FILE_BESHORT: 611186691Sobrien case FILE_BELONG: 612186691Sobrien case FILE_BEDATE: 613186691Sobrien case FILE_LESHORT: 614186691Sobrien case FILE_LELONG: 615186691Sobrien case FILE_LEDATE: 616186691Sobrien case FILE_LDATE: 617186691Sobrien case FILE_BELDATE: 618186691Sobrien case FILE_LELDATE: 619186691Sobrien case FILE_MEDATE: 620186691Sobrien case FILE_MELDATE: 621186691Sobrien case FILE_MELONG: 622186691Sobrien case FILE_QUAD: 623186691Sobrien case FILE_LEQUAD: 624186691Sobrien case FILE_BEQUAD: 625186691Sobrien case FILE_QDATE: 626186691Sobrien case FILE_LEQDATE: 627186691Sobrien case FILE_BEQDATE: 628186691Sobrien case FILE_QLDATE: 629186691Sobrien case FILE_LEQLDATE: 630186691Sobrien case FILE_BEQLDATE: 631186691Sobrien case FILE_FLOAT: 632186691Sobrien case FILE_BEFLOAT: 633186691Sobrien case FILE_LEFLOAT: 634186691Sobrien case FILE_DOUBLE: 635186691Sobrien case FILE_BEDOUBLE: 636186691Sobrien case FILE_LEDOUBLE: 637234449Sobrien mstart->flag |= BINTEST; 638234449Sobrien break; 639186691Sobrien case FILE_STRING: 640186691Sobrien case FILE_PSTRING: 641186691Sobrien case FILE_BESTRING16: 642186691Sobrien case FILE_LESTRING16: 643234449Sobrien /* Allow text overrides */ 644234449Sobrien if (mstart->str_flags & STRING_TEXTTEST) 645234449Sobrien mstart->flag |= TEXTTEST; 646234449Sobrien else 647234449Sobrien mstart->flag |= BINTEST; 648186691Sobrien break; 649186691Sobrien case FILE_REGEX: 650186691Sobrien case FILE_SEARCH: 651234449Sobrien /* Check for override */ 652234449Sobrien if (mstart->str_flags & STRING_BINTEST) 653234449Sobrien mstart->flag |= BINTEST; 654234449Sobrien if (mstart->str_flags & STRING_TEXTTEST) 655234449Sobrien mstart->flag |= TEXTTEST; 656234449Sobrien 657234449Sobrien if (mstart->flag & (TEXTTEST|BINTEST)) 658234449Sobrien break; 659234449Sobrien 660186691Sobrien /* binary test if pattern is not text */ 661191771Sobrien if (file_looks_utf8(m->value.us, (size_t)m->vallen, NULL, 662191771Sobrien NULL) <= 0) 663186691Sobrien mstart->flag |= BINTEST; 664234449Sobrien else 665234449Sobrien mstart->flag |= TEXTTEST; 666186691Sobrien break; 667186691Sobrien case FILE_DEFAULT: 668186691Sobrien /* can't deduce anything; we shouldn't see this at the 669186691Sobrien top level anyway */ 670186691Sobrien break; 671186691Sobrien case FILE_INVALID: 672186691Sobrien default: 673186691Sobrien /* invalid search type, but no need to complain here */ 674186691Sobrien break; 675186691Sobrien } 676186691Sobrien} 677186691Sobrien 67874784Sobrien/* 679186691Sobrien * Load and parse one file. 68074784Sobrien */ 681186691Sobrienprivate void 682186691Sobrienload_1(struct magic_set *ms, int action, const char *fn, int *errs, 683186691Sobrien struct magic_entry **marray, uint32_t *marraycount) 684186691Sobrien{ 685234449Sobrien size_t lineno = 0, llen = 0; 686234449Sobrien char *line = NULL; 687234449Sobrien ssize_t len; 688234449Sobrien 689186691Sobrien FILE *f = fopen(ms->file = fn, "r"); 690186691Sobrien if (f == NULL) { 691186691Sobrien if (errno != ENOENT) 692186691Sobrien file_error(ms, errno, "cannot read magic file `%s'", 693186691Sobrien fn); 694186691Sobrien (*errs)++; 695234449Sobrien return; 696234449Sobrien } 697234449Sobrien 698234449Sobrien /* read and parse this file */ 699234449Sobrien for (ms->line = 1; (len = getline(&line, &llen, f)) != -1; 700234449Sobrien ms->line++) { 701234449Sobrien if (len == 0) /* null line, garbage, etc */ 702234449Sobrien continue; 703234449Sobrien if (line[len - 1] == '\n') { 704234449Sobrien lineno++; 705234449Sobrien line[len - 1] = '\0'; /* delete newline */ 706234449Sobrien } 707234449Sobrien switch (line[0]) { 708234449Sobrien case '\0': /* empty, do not parse */ 709234449Sobrien case '#': /* comment, do not parse */ 710234449Sobrien continue; 711234449Sobrien case '!': 712234449Sobrien if (line[1] == ':') { 713186691Sobrien size_t i; 714186691Sobrien 715186691Sobrien for (i = 0; bang[i].name != NULL; i++) { 716234449Sobrien if ((size_t)(len - 2) > bang[i].len && 717186691Sobrien memcmp(bang[i].name, line + 2, 718186691Sobrien bang[i].len) == 0) 719186691Sobrien break; 720186691Sobrien } 721186691Sobrien if (bang[i].name == NULL) { 722186691Sobrien file_error(ms, 0, 723186691Sobrien "Unknown !: entry `%s'", line); 724186691Sobrien (*errs)++; 725186691Sobrien continue; 726186691Sobrien } 727186691Sobrien if (*marraycount == 0) { 728186691Sobrien file_error(ms, 0, 729186691Sobrien "No current entry for :!%s type", 730186691Sobrien bang[i].name); 731186691Sobrien (*errs)++; 732186691Sobrien continue; 733186691Sobrien } 734186691Sobrien if ((*bang[i].fun)(ms, 735186691Sobrien &(*marray)[*marraycount - 1], 736186691Sobrien line + bang[i].len + 2) != 0) { 737186691Sobrien (*errs)++; 738186691Sobrien continue; 739186691Sobrien } 740186691Sobrien continue; 741186691Sobrien } 742234449Sobrien /*FALLTHROUGH*/ 743234449Sobrien default: 744186691Sobrien if (parse(ms, marray, marraycount, line, lineno, 745186691Sobrien action) != 0) 746186691Sobrien (*errs)++; 747234449Sobrien break; 748186691Sobrien } 749186691Sobrien } 750234449Sobrien free(line); 751234449Sobrien (void)fclose(f); 752186691Sobrien} 753186691Sobrien 754186691Sobrien/* 755186691Sobrien * parse a file or directory of files 756186691Sobrien * const char *fn: name of magic file or directory 757186691Sobrien */ 758133359Sobrienprivate int 759234449Sobriencmpstrp(const void *p1, const void *p2) 760234449Sobrien{ 761234449Sobrien return strcmp(*(char *const *)p1, *(char *const *)p2); 762234449Sobrien} 763234449Sobrien 764234449Sobrienprivate int 765186691Sobrienapprentice_load(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp, 766133359Sobrien const char *fn, int action) 76768349Sobrien{ 76868349Sobrien int errs = 0; 769159764Sobrien struct magic_entry *marray; 770186691Sobrien uint32_t marraycount, i, mentrycount = 0, starttest; 771234449Sobrien size_t slen, files = 0, maxfiles = 0; 772234449Sobrien char **filearr = NULL, *mfn; 773186691Sobrien struct stat st; 774186691Sobrien DIR *dir; 775186691Sobrien struct dirent *d; 77668349Sobrien 777169942Sobrien ms->flags |= MAGIC_CHECK; /* Enable checks for parsed files */ 778169942Sobrien 779169942Sobrien maxmagic = MAXMAGIS; 780186691Sobrien if ((marray = CAST(struct magic_entry *, calloc(maxmagic, 781186691Sobrien sizeof(*marray)))) == NULL) { 782169942Sobrien file_oomem(ms, maxmagic * sizeof(*marray)); 783133359Sobrien return -1; 78474784Sobrien } 785159764Sobrien marraycount = 0; 78674784Sobrien 787133359Sobrien /* print silly verbose header for USG compat. */ 788133359Sobrien if (action == FILE_CHECK) 789186691Sobrien (void)fprintf(stderr, "%s\n", usg_hdr); 790133359Sobrien 791186691Sobrien /* load directory or file */ 792186691Sobrien if (stat(fn, &st) == 0 && S_ISDIR(st.st_mode)) { 793186691Sobrien dir = opendir(fn); 794234449Sobrien if (!dir) { 795234449Sobrien errs++; 796234449Sobrien goto out; 797234449Sobrien } 798234449Sobrien while ((d = readdir(dir)) != NULL) { 799234449Sobrien if (asprintf(&mfn, "%s/%s", fn, d->d_name) < 0) { 800234449Sobrien file_oomem(ms, 801234449Sobrien strlen(fn) + strlen(d->d_name) + 2); 802234449Sobrien errs++; 803234449Sobrien closedir(dir); 804234449Sobrien goto out; 805234449Sobrien } 806234449Sobrien if (stat(mfn, &st) == -1 || !S_ISREG(st.st_mode)) { 807234449Sobrien free(mfn); 808234449Sobrien continue; 809234449Sobrien } 810234449Sobrien if (files >= maxfiles) { 811234449Sobrien size_t mlen; 812234449Sobrien maxfiles = (maxfiles + 1) * 2; 813234449Sobrien mlen = maxfiles * sizeof(*filearr); 814234449Sobrien if ((filearr = CAST(char **, 815234449Sobrien realloc(filearr, mlen))) == NULL) { 816234449Sobrien file_oomem(ms, mlen); 817234449Sobrien free(mfn); 818234449Sobrien closedir(dir); 819234449Sobrien errs++; 820234449Sobrien goto out; 821186691Sobrien } 822186691Sobrien } 823234449Sobrien filearr[files++] = mfn; 824234449Sobrien } 825234449Sobrien closedir(dir); 826234449Sobrien qsort(filearr, files, sizeof(*filearr), cmpstrp); 827234449Sobrien for (i = 0; i < files; i++) { 828234449Sobrien load_1(ms, action, filearr[i], &errs, &marray, 829234449Sobrien &marraycount); 830234449Sobrien free(filearr[i]); 831234449Sobrien } 832234449Sobrien free(filearr); 833186691Sobrien } else 834186691Sobrien load_1(ms, action, fn, &errs, &marray, &marraycount); 835186691Sobrien if (errs) 836186691Sobrien goto out; 837186691Sobrien 838186691Sobrien /* Set types of tests */ 839186691Sobrien for (i = 0; i < marraycount; ) { 840186691Sobrien if (marray[i].mp->cont_level != 0) { 841186691Sobrien i++; 84268349Sobrien continue; 843169942Sobrien } 844186691Sobrien 845186691Sobrien starttest = i; 846186691Sobrien do { 847186691Sobrien static const char text[] = "text"; 848186691Sobrien static const char binary[] = "binary"; 849186691Sobrien static const size_t len = sizeof(text); 850186691Sobrien set_test_type(marray[starttest].mp, marray[i].mp); 851186691Sobrien if ((ms->flags & MAGIC_DEBUG) == 0) 852186691Sobrien continue; 853186691Sobrien (void)fprintf(stderr, "%s%s%s: %s\n", 854186691Sobrien marray[i].mp->mimetype, 855186691Sobrien marray[i].mp->mimetype[0] == '\0' ? "" : "; ", 856186691Sobrien marray[i].mp->desc[0] ? marray[i].mp->desc : 857186691Sobrien "(no description)", 858186691Sobrien marray[i].mp->flag & BINTEST ? binary : text); 859186691Sobrien if (marray[i].mp->flag & BINTEST) { 860186691Sobrien char *p = strstr(marray[i].mp->desc, text); 861186691Sobrien if (p && (p == marray[i].mp->desc || 862186691Sobrien isspace((unsigned char)p[-1])) && 863186691Sobrien (p + len - marray[i].mp->desc == 864186691Sobrien MAXstring || (p[len] == '\0' || 865186691Sobrien isspace((unsigned char)p[len])))) 866186691Sobrien (void)fprintf(stderr, "*** Possible " 867186691Sobrien "binary test for text type\n"); 868186691Sobrien } 869186691Sobrien } while (++i < marraycount && marray[i].mp->cont_level != 0); 87068349Sobrien } 87168349Sobrien 872186691Sobrien qsort(marray, marraycount, sizeof(*marray), apprentice_sort); 873159764Sobrien 874169962Sobrien /* 875169962Sobrien * Make sure that any level 0 "default" line is last (if one exists). 876169962Sobrien */ 877169962Sobrien for (i = 0; i < marraycount; i++) { 878169962Sobrien if (marray[i].mp->cont_level == 0 && 879169962Sobrien marray[i].mp->type == FILE_DEFAULT) { 880169962Sobrien while (++i < marraycount) 881169962Sobrien if (marray[i].mp->cont_level == 0) 882169962Sobrien break; 883169962Sobrien if (i != marraycount) { 884234449Sobrien /* XXX - Ugh! */ 885234449Sobrien ms->line = marray[i].mp->lineno; 886169962Sobrien file_magwarn(ms, 887169962Sobrien "level 0 \"default\" did not sort last"); 888169962Sobrien } 889169962Sobrien break; 890169962Sobrien } 891169962Sobrien } 892159764Sobrien 893169942Sobrien for (i = 0; i < marraycount; i++) 894159764Sobrien mentrycount += marray[i].cont_count; 895159764Sobrien 896186691Sobrien slen = sizeof(**magicp) * mentrycount; 897186691Sobrien if ((*magicp = CAST(struct magic *, malloc(slen))) == NULL) { 898186691Sobrien file_oomem(ms, slen); 899159764Sobrien errs++; 900159764Sobrien goto out; 901159764Sobrien } 902159764Sobrien 903159764Sobrien mentrycount = 0; 904159764Sobrien for (i = 0; i < marraycount; i++) { 905159764Sobrien (void)memcpy(*magicp + mentrycount, marray[i].mp, 906159764Sobrien marray[i].cont_count * sizeof(**magicp)); 907159764Sobrien mentrycount += marray[i].cont_count; 908159764Sobrien } 909159764Sobrienout: 910159764Sobrien for (i = 0; i < marraycount; i++) 911159764Sobrien free(marray[i].mp); 912159764Sobrien free(marray); 91374784Sobrien if (errs) { 91474784Sobrien *magicp = NULL; 91574784Sobrien *nmagicp = 0; 916159764Sobrien return errs; 917159764Sobrien } else { 918159764Sobrien *nmagicp = mentrycount; 919159764Sobrien return 0; 92074784Sobrien } 921159764Sobrien 92268349Sobrien} 92368349Sobrien 92468349Sobrien/* 92568349Sobrien * extend the sign bit if the comparison is to be signed 92668349Sobrien */ 927169942Sobrienprotected uint64_t 928169942Sobrienfile_signextend(struct magic_set *ms, struct magic *m, uint64_t v) 92968349Sobrien{ 930169962Sobrien if (!(m->flag & UNSIGNED)) { 93168349Sobrien switch(m->type) { 93268349Sobrien /* 93368349Sobrien * Do not remove the casts below. They are 93468349Sobrien * vital. When later compared with the data, 93568349Sobrien * the sign extension must have happened. 93668349Sobrien */ 937133359Sobrien case FILE_BYTE: 93868349Sobrien v = (char) v; 93968349Sobrien break; 940133359Sobrien case FILE_SHORT: 941133359Sobrien case FILE_BESHORT: 942133359Sobrien case FILE_LESHORT: 94368349Sobrien v = (short) v; 94468349Sobrien break; 945133359Sobrien case FILE_DATE: 946133359Sobrien case FILE_BEDATE: 947133359Sobrien case FILE_LEDATE: 948159764Sobrien case FILE_MEDATE: 949133359Sobrien case FILE_LDATE: 950133359Sobrien case FILE_BELDATE: 951133359Sobrien case FILE_LELDATE: 952159764Sobrien case FILE_MELDATE: 953133359Sobrien case FILE_LONG: 954133359Sobrien case FILE_BELONG: 955133359Sobrien case FILE_LELONG: 956159764Sobrien case FILE_MELONG: 957175296Sobrien case FILE_FLOAT: 958175296Sobrien case FILE_BEFLOAT: 959175296Sobrien case FILE_LEFLOAT: 960103373Sobrien v = (int32_t) v; 96168349Sobrien break; 962169942Sobrien case FILE_QUAD: 963169942Sobrien case FILE_BEQUAD: 964169942Sobrien case FILE_LEQUAD: 965169942Sobrien case FILE_QDATE: 966169942Sobrien case FILE_QLDATE: 967169942Sobrien case FILE_BEQDATE: 968169942Sobrien case FILE_BEQLDATE: 969169942Sobrien case FILE_LEQDATE: 970169942Sobrien case FILE_LEQLDATE: 971175296Sobrien case FILE_DOUBLE: 972175296Sobrien case FILE_BEDOUBLE: 973175296Sobrien case FILE_LEDOUBLE: 974169942Sobrien v = (int64_t) v; 975169942Sobrien break; 976133359Sobrien case FILE_STRING: 977133359Sobrien case FILE_PSTRING: 978139368Sobrien case FILE_BESTRING16: 979139368Sobrien case FILE_LESTRING16: 980133359Sobrien case FILE_REGEX: 981159764Sobrien case FILE_SEARCH: 982169962Sobrien case FILE_DEFAULT: 983191771Sobrien case FILE_INDIRECT: 984103373Sobrien break; 98568349Sobrien default: 986133359Sobrien if (ms->flags & MAGIC_CHECK) 987139368Sobrien file_magwarn(ms, "cannot happen: m->type=%d\n", 988133359Sobrien m->type); 989133359Sobrien return ~0U; 99068349Sobrien } 991169962Sobrien } 99268349Sobrien return v; 99368349Sobrien} 99468349Sobrien 995169962Sobrienprivate int 996186691Sobrienstring_modifier_check(struct magic_set *ms, struct magic *m) 997169962Sobrien{ 998169962Sobrien if ((ms->flags & MAGIC_CHECK) == 0) 999169962Sobrien return 0; 1000169962Sobrien 1001234449Sobrien if (m->type != FILE_PSTRING && (m->str_flags & PSTRING_LEN) != 0) { 1002234449Sobrien file_magwarn(ms, 1003234449Sobrien "'/BHhLl' modifiers are only allowed for pascal strings\n"); 1004234449Sobrien return -1; 1005234449Sobrien } 1006169962Sobrien switch (m->type) { 1007169962Sobrien case FILE_BESTRING16: 1008169962Sobrien case FILE_LESTRING16: 1009169962Sobrien if (m->str_flags != 0) { 1010186691Sobrien file_magwarn(ms, 1011186691Sobrien "no modifiers allowed for 16-bit strings\n"); 1012169962Sobrien return -1; 1013169962Sobrien } 1014169962Sobrien break; 1015169962Sobrien case FILE_STRING: 1016169962Sobrien case FILE_PSTRING: 1017169962Sobrien if ((m->str_flags & REGEX_OFFSET_START) != 0) { 1018186691Sobrien file_magwarn(ms, 1019186691Sobrien "'/%c' only allowed on regex and search\n", 1020169962Sobrien CHAR_REGEX_OFFSET_START); 1021169962Sobrien return -1; 1022169962Sobrien } 1023169962Sobrien break; 1024169962Sobrien case FILE_SEARCH: 1025186691Sobrien if (m->str_range == 0) { 1026186691Sobrien file_magwarn(ms, 1027186691Sobrien "missing range; defaulting to %d\n", 1028186691Sobrien STRING_DEFAULT_RANGE); 1029186691Sobrien m->str_range = STRING_DEFAULT_RANGE; 1030186691Sobrien return -1; 1031186691Sobrien } 1032169962Sobrien break; 1033169962Sobrien case FILE_REGEX: 1034234449Sobrien if ((m->str_flags & STRING_COMPACT_WHITESPACE) != 0) { 1035169962Sobrien file_magwarn(ms, "'/%c' not allowed on regex\n", 1036234449Sobrien CHAR_COMPACT_WHITESPACE); 1037169962Sobrien return -1; 1038169962Sobrien } 1039234449Sobrien if ((m->str_flags & STRING_COMPACT_OPTIONAL_WHITESPACE) != 0) { 1040169962Sobrien file_magwarn(ms, "'/%c' not allowed on regex\n", 1041234449Sobrien CHAR_COMPACT_OPTIONAL_WHITESPACE); 1042169962Sobrien return -1; 1043169962Sobrien } 1044169962Sobrien break; 1045169962Sobrien default: 1046169962Sobrien file_magwarn(ms, "coding error: m->type=%d\n", 1047169962Sobrien m->type); 1048169962Sobrien return -1; 1049169962Sobrien } 1050169962Sobrien return 0; 1051169962Sobrien} 1052169962Sobrien 1053169962Sobrienprivate int 1054169962Sobrienget_op(char c) 1055169962Sobrien{ 1056169962Sobrien switch (c) { 1057169962Sobrien case '&': 1058169962Sobrien return FILE_OPAND; 1059169962Sobrien case '|': 1060169962Sobrien return FILE_OPOR; 1061169962Sobrien case '^': 1062169962Sobrien return FILE_OPXOR; 1063169962Sobrien case '+': 1064169962Sobrien return FILE_OPADD; 1065169962Sobrien case '-': 1066169962Sobrien return FILE_OPMINUS; 1067169962Sobrien case '*': 1068169962Sobrien return FILE_OPMULTIPLY; 1069169962Sobrien case '/': 1070169962Sobrien return FILE_OPDIVIDE; 1071169962Sobrien case '%': 1072169962Sobrien return FILE_OPMODULO; 1073169962Sobrien default: 1074169962Sobrien return -1; 1075169962Sobrien } 1076169962Sobrien} 1077169962Sobrien 1078169962Sobrien#ifdef ENABLE_CONDITIONALS 1079169962Sobrienprivate int 1080169962Sobrienget_cond(const char *l, const char **t) 1081169962Sobrien{ 1082186691Sobrien static const struct cond_tbl_s { 1083186691Sobrien char name[8]; 1084186691Sobrien size_t len; 1085186691Sobrien int cond; 1086169962Sobrien } cond_tbl[] = { 1087169962Sobrien { "if", 2, COND_IF }, 1088169962Sobrien { "elif", 4, COND_ELIF }, 1089169962Sobrien { "else", 4, COND_ELSE }, 1090186691Sobrien { "", 0, COND_NONE }, 1091169962Sobrien }; 1092186691Sobrien const struct cond_tbl_s *p; 1093169962Sobrien 1094186691Sobrien for (p = cond_tbl; p->len; p++) { 1095169962Sobrien if (strncmp(l, p->name, p->len) == 0 && 1096169962Sobrien isspace((unsigned char)l[p->len])) { 1097169962Sobrien if (t) 1098169962Sobrien *t = l + p->len; 1099169962Sobrien break; 1100169962Sobrien } 1101169962Sobrien } 1102169962Sobrien return p->cond; 1103169962Sobrien} 1104169962Sobrien 1105169962Sobrienprivate int 1106169962Sobriencheck_cond(struct magic_set *ms, int cond, uint32_t cont_level) 1107169962Sobrien{ 1108169962Sobrien int last_cond; 1109169962Sobrien last_cond = ms->c.li[cont_level].last_cond; 1110169962Sobrien 1111169962Sobrien switch (cond) { 1112169962Sobrien case COND_IF: 1113169962Sobrien if (last_cond != COND_NONE && last_cond != COND_ELIF) { 1114169962Sobrien if (ms->flags & MAGIC_CHECK) 1115169962Sobrien file_magwarn(ms, "syntax error: `if'"); 1116169962Sobrien return -1; 1117169962Sobrien } 1118169962Sobrien last_cond = COND_IF; 1119169962Sobrien break; 1120169962Sobrien 1121169962Sobrien case COND_ELIF: 1122169962Sobrien if (last_cond != COND_IF && last_cond != COND_ELIF) { 1123169962Sobrien if (ms->flags & MAGIC_CHECK) 1124169962Sobrien file_magwarn(ms, "syntax error: `elif'"); 1125169962Sobrien return -1; 1126169962Sobrien } 1127169962Sobrien last_cond = COND_ELIF; 1128169962Sobrien break; 1129169962Sobrien 1130169962Sobrien case COND_ELSE: 1131169962Sobrien if (last_cond != COND_IF && last_cond != COND_ELIF) { 1132169962Sobrien if (ms->flags & MAGIC_CHECK) 1133169962Sobrien file_magwarn(ms, "syntax error: `else'"); 1134169962Sobrien return -1; 1135169962Sobrien } 1136169962Sobrien last_cond = COND_NONE; 1137169962Sobrien break; 1138169962Sobrien 1139169962Sobrien case COND_NONE: 1140169962Sobrien last_cond = COND_NONE; 1141169962Sobrien break; 1142169962Sobrien } 1143169962Sobrien 1144169962Sobrien ms->c.li[cont_level].last_cond = last_cond; 1145169962Sobrien return 0; 1146169962Sobrien} 1147169962Sobrien#endif /* ENABLE_CONDITIONALS */ 1148169962Sobrien 114968349Sobrien/* 115068349Sobrien * parse one line from magic file, put into magic[index++] if valid 115168349Sobrien */ 1152133359Sobrienprivate int 1153159764Sobrienparse(struct magic_set *ms, struct magic_entry **mentryp, uint32_t *nmentryp, 1154169942Sobrien const char *line, size_t lineno, int action) 115568349Sobrien{ 1156169962Sobrien#ifdef ENABLE_CONDITIONALS 1157169962Sobrien static uint32_t last_cont_level = 0; 1158169962Sobrien#endif 1159169942Sobrien size_t i; 1160159764Sobrien struct magic_entry *me; 116168349Sobrien struct magic *m; 1162159764Sobrien const char *l = line; 116384685Sobrien char *t; 1164169962Sobrien int op; 1165159825Sobrien uint32_t cont_level; 116668349Sobrien 1167159764Sobrien cont_level = 0; 116868349Sobrien 116968349Sobrien while (*l == '>') { 117068349Sobrien ++l; /* step over */ 1171159764Sobrien cont_level++; 117268349Sobrien } 1173169962Sobrien#ifdef ENABLE_CONDITIONALS 1174169962Sobrien if (cont_level == 0 || cont_level > last_cont_level) 1175169962Sobrien if (file_check_mem(ms, cont_level) == -1) 1176169962Sobrien return -1; 1177169962Sobrien last_cont_level = cont_level; 1178169962Sobrien#endif 117968349Sobrien 1180159764Sobrien#define ALLOC_CHUNK (size_t)10 1181159764Sobrien#define ALLOC_INCR (size_t)200 1182159764Sobrien 1183159764Sobrien if (cont_level != 0) { 1184159764Sobrien if (*nmentryp == 0) { 1185159764Sobrien file_error(ms, 0, "No current entry for continuation"); 1186159764Sobrien return -1; 1187159764Sobrien } 1188159764Sobrien me = &(*mentryp)[*nmentryp - 1]; 1189159764Sobrien if (me->cont_count == me->max_count) { 1190159764Sobrien struct magic *nm; 1191159764Sobrien size_t cnt = me->max_count + ALLOC_CHUNK; 1192186691Sobrien if ((nm = CAST(struct magic *, realloc(me->mp, 1193186691Sobrien sizeof(*nm) * cnt))) == NULL) { 1194169942Sobrien file_oomem(ms, sizeof(*nm) * cnt); 1195159764Sobrien return -1; 1196159764Sobrien } 1197159764Sobrien me->mp = m = nm; 1198234449Sobrien me->max_count = CAST(uint32_t, cnt); 1199159764Sobrien } 1200159764Sobrien m = &me->mp[me->cont_count++]; 1201169962Sobrien (void)memset(m, 0, sizeof(*m)); 1202159764Sobrien m->cont_level = cont_level; 1203159764Sobrien } else { 1204159764Sobrien if (*nmentryp == maxmagic) { 1205159764Sobrien struct magic_entry *mp; 1206159764Sobrien 1207159764Sobrien maxmagic += ALLOC_INCR; 1208186691Sobrien if ((mp = CAST(struct magic_entry *, 1209186691Sobrien realloc(*mentryp, sizeof(*mp) * maxmagic))) == 1210159764Sobrien NULL) { 1211169942Sobrien file_oomem(ms, sizeof(*mp) * maxmagic); 1212159764Sobrien return -1; 1213159764Sobrien } 1214159764Sobrien (void)memset(&mp[*nmentryp], 0, sizeof(*mp) * 1215159764Sobrien ALLOC_INCR); 1216159764Sobrien *mentryp = mp; 1217159764Sobrien } 1218159764Sobrien me = &(*mentryp)[*nmentryp]; 1219159764Sobrien if (me->mp == NULL) { 1220186691Sobrien size_t len = sizeof(*m) * ALLOC_CHUNK; 1221186691Sobrien if ((m = CAST(struct magic *, malloc(len))) == NULL) { 1222186691Sobrien file_oomem(ms, len); 1223159764Sobrien return -1; 1224159764Sobrien } 1225159764Sobrien me->mp = m; 1226159764Sobrien me->max_count = ALLOC_CHUNK; 1227159764Sobrien } else 1228159764Sobrien m = me->mp; 1229169962Sobrien (void)memset(m, 0, sizeof(*m)); 1230186691Sobrien m->factor_op = FILE_FACTOR_OP_NONE; 1231159764Sobrien m->cont_level = 0; 1232159764Sobrien me->cont_count = 1; 1233159764Sobrien } 1234234449Sobrien m->lineno = CAST(uint32_t, lineno); 1235159764Sobrien 1236169962Sobrien if (*l == '&') { /* m->cont_level == 0 checked below. */ 1237159764Sobrien ++l; /* step over */ 1238159764Sobrien m->flag |= OFFADD; 1239159764Sobrien } 1240169962Sobrien if (*l == '(') { 124168349Sobrien ++l; /* step over */ 124268349Sobrien m->flag |= INDIR; 1243159764Sobrien if (m->flag & OFFADD) 1244159764Sobrien m->flag = (m->flag & ~OFFADD) | INDIROFFADD; 1245169962Sobrien 1246169962Sobrien if (*l == '&') { /* m->cont_level == 0 checked below */ 1247169962Sobrien ++l; /* step over */ 1248169962Sobrien m->flag |= OFFADD; 1249169962Sobrien } 125068349Sobrien } 1251169962Sobrien /* Indirect offsets are not valid at level 0. */ 1252169962Sobrien if (m->cont_level == 0 && (m->flag & (OFFADD | INDIROFFADD))) 1253169962Sobrien if (ms->flags & MAGIC_CHECK) 1254169962Sobrien file_magwarn(ms, "relative offset at level 0"); 125568349Sobrien 125668349Sobrien /* get offset, then skip over it */ 1257133359Sobrien m->offset = (uint32_t)strtoul(l, &t, 0); 125868349Sobrien if (l == t) 1259133359Sobrien if (ms->flags & MAGIC_CHECK) 1260139368Sobrien file_magwarn(ms, "offset `%s' invalid", l); 126168349Sobrien l = t; 126268349Sobrien 126368349Sobrien if (m->flag & INDIR) { 1264133359Sobrien m->in_type = FILE_LONG; 126574784Sobrien m->in_offset = 0; 126668349Sobrien /* 126768349Sobrien * read [.lbs][+-]nnnnn) 126868349Sobrien */ 126968349Sobrien if (*l == '.') { 127068349Sobrien l++; 127168349Sobrien switch (*l) { 127268349Sobrien case 'l': 1273133359Sobrien m->in_type = FILE_LELONG; 127468349Sobrien break; 127568349Sobrien case 'L': 1276133359Sobrien m->in_type = FILE_BELONG; 127768349Sobrien break; 1278159764Sobrien case 'm': 1279159764Sobrien m->in_type = FILE_MELONG; 1280159764Sobrien break; 128168349Sobrien case 'h': 128268349Sobrien case 's': 1283133359Sobrien m->in_type = FILE_LESHORT; 128468349Sobrien break; 128568349Sobrien case 'H': 128668349Sobrien case 'S': 1287133359Sobrien m->in_type = FILE_BESHORT; 128868349Sobrien break; 128968349Sobrien case 'c': 129068349Sobrien case 'b': 129168349Sobrien case 'C': 129268349Sobrien case 'B': 1293133359Sobrien m->in_type = FILE_BYTE; 129468349Sobrien break; 1295175296Sobrien case 'e': 1296175296Sobrien case 'f': 1297175296Sobrien case 'g': 1298175296Sobrien m->in_type = FILE_LEDOUBLE; 1299175296Sobrien break; 1300175296Sobrien case 'E': 1301175296Sobrien case 'F': 1302175296Sobrien case 'G': 1303175296Sobrien m->in_type = FILE_BEDOUBLE; 1304175296Sobrien break; 1305191771Sobrien case 'i': 1306191771Sobrien m->in_type = FILE_LEID3; 1307191771Sobrien break; 1308191771Sobrien case 'I': 1309191771Sobrien m->in_type = FILE_BEID3; 1310191771Sobrien break; 131168349Sobrien default: 1312133359Sobrien if (ms->flags & MAGIC_CHECK) 1313139368Sobrien file_magwarn(ms, 1314139368Sobrien "indirect offset type `%c' invalid", 1315133359Sobrien *l); 131668349Sobrien break; 131768349Sobrien } 131868349Sobrien l++; 131968349Sobrien } 1320169962Sobrien 1321169962Sobrien m->in_op = 0; 132280588Sobrien if (*l == '~') { 1323159764Sobrien m->in_op |= FILE_OPINVERSE; 132480588Sobrien l++; 132580588Sobrien } 1326169962Sobrien if ((op = get_op(*l)) != -1) { 1327169962Sobrien m->in_op |= op; 132880588Sobrien l++; 132980588Sobrien } 1330159764Sobrien if (*l == '(') { 1331159764Sobrien m->in_op |= FILE_OPINDIRECT; 1332159764Sobrien l++; 1333159764Sobrien } 1334159764Sobrien if (isdigit((unsigned char)*l) || *l == '-') { 1335159764Sobrien m->in_offset = (int32_t)strtol(l, &t, 0); 1336169962Sobrien if (l == t) 1337169962Sobrien if (ms->flags & MAGIC_CHECK) 1338169962Sobrien file_magwarn(ms, 1339169962Sobrien "in_offset `%s' invalid", l); 1340159764Sobrien l = t; 1341159764Sobrien } 1342159764Sobrien if (*l++ != ')' || 1343159764Sobrien ((m->in_op & FILE_OPINDIRECT) && *l++ != ')')) 1344133359Sobrien if (ms->flags & MAGIC_CHECK) 1345139368Sobrien file_magwarn(ms, 1346139368Sobrien "missing ')' in indirect offset"); 134768349Sobrien } 1348169962Sobrien EATAB; 134968349Sobrien 1350169962Sobrien#ifdef ENABLE_CONDITIONALS 1351169962Sobrien m->cond = get_cond(l, &l); 1352169962Sobrien if (check_cond(ms, m->cond, cont_level) == -1) 1353169962Sobrien return -1; 135468349Sobrien 135568349Sobrien EATAB; 1356169962Sobrien#endif 135768349Sobrien 135868349Sobrien if (*l == 'u') { 135968349Sobrien ++l; 136068349Sobrien m->flag |= UNSIGNED; 136168349Sobrien } 136268349Sobrien 1363169962Sobrien m->type = get_type(l, &l); 1364169962Sobrien if (m->type == FILE_INVALID) { 1365133359Sobrien if (ms->flags & MAGIC_CHECK) 1366139368Sobrien file_magwarn(ms, "type `%s' invalid", l); 136768349Sobrien return -1; 136868349Sobrien } 1369169962Sobrien 137068349Sobrien /* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */ 137180588Sobrien /* New and improved: ~ & | ^ + - * / % -- exciting, isn't it? */ 1372169962Sobrien 1373169962Sobrien m->mask_op = 0; 137480588Sobrien if (*l == '~') { 1375139368Sobrien if (!IS_STRING(m->type)) 1376159764Sobrien m->mask_op |= FILE_OPINVERSE; 1377169962Sobrien else if (ms->flags & MAGIC_CHECK) 1378169962Sobrien file_magwarn(ms, "'~' invalid for string types"); 137968349Sobrien ++l; 138080588Sobrien } 1381186691Sobrien m->str_range = 0; 1382234449Sobrien m->str_flags = m->type == FILE_PSTRING ? PSTRING_1_LE : 0; 1383169962Sobrien if ((op = get_op(*l)) != -1) { 1384169962Sobrien if (!IS_STRING(m->type)) { 1385169962Sobrien uint64_t val; 138680588Sobrien ++l; 1387133359Sobrien m->mask_op |= op; 1388169942Sobrien val = (uint64_t)strtoull(l, &t, 0); 1389159764Sobrien l = t; 1390169962Sobrien m->num_mask = file_signextend(ms, m, val); 139180588Sobrien eatsize(&l); 1392169962Sobrien } 1393169962Sobrien else if (op == FILE_OPDIVIDE) { 1394186691Sobrien int have_range = 0; 1395133359Sobrien while (!isspace((unsigned char)*++l)) { 139668349Sobrien switch (*l) { 1397169962Sobrien case '0': case '1': case '2': 1398169962Sobrien case '3': case '4': case '5': 1399169962Sobrien case '6': case '7': case '8': 1400186691Sobrien case '9': 1401186691Sobrien if (have_range && 1402186691Sobrien (ms->flags & MAGIC_CHECK)) 1403169962Sobrien file_magwarn(ms, 1404186691Sobrien "multiple ranges"); 1405186691Sobrien have_range = 1; 1406234449Sobrien m->str_range = CAST(uint32_t, 1407234449Sobrien strtoul(l, &t, 0)); 1408186691Sobrien if (m->str_range == 0) 1409186691Sobrien file_magwarn(ms, 1410186691Sobrien "zero range"); 1411169962Sobrien l = t - 1; 141268349Sobrien break; 1413234449Sobrien case CHAR_COMPACT_WHITESPACE: 1414234449Sobrien m->str_flags |= 1415234449Sobrien STRING_COMPACT_WHITESPACE; 141668349Sobrien break; 1417234449Sobrien case CHAR_COMPACT_OPTIONAL_WHITESPACE: 1418169962Sobrien m->str_flags |= 1419234449Sobrien STRING_COMPACT_OPTIONAL_WHITESPACE; 142068349Sobrien break; 1421169962Sobrien case CHAR_IGNORE_LOWERCASE: 1422169962Sobrien m->str_flags |= STRING_IGNORE_LOWERCASE; 1423169962Sobrien break; 1424169962Sobrien case CHAR_IGNORE_UPPERCASE: 1425169962Sobrien m->str_flags |= STRING_IGNORE_UPPERCASE; 1426169962Sobrien break; 1427169962Sobrien case CHAR_REGEX_OFFSET_START: 1428169962Sobrien m->str_flags |= REGEX_OFFSET_START; 1429169962Sobrien break; 1430234449Sobrien case CHAR_BINTEST: 1431234449Sobrien m->str_flags |= STRING_BINTEST; 1432234449Sobrien break; 1433234449Sobrien case CHAR_TEXTTEST: 1434234449Sobrien m->str_flags |= STRING_TEXTTEST; 1435234449Sobrien break; 1436234449Sobrien case CHAR_PSTRING_1_LE: 1437234449Sobrien if (m->type != FILE_PSTRING) 1438234449Sobrien goto bad; 1439234449Sobrien m->str_flags = (m->str_flags & ~PSTRING_LEN) | PSTRING_1_LE; 1440234449Sobrien break; 1441234449Sobrien case CHAR_PSTRING_2_BE: 1442234449Sobrien if (m->type != FILE_PSTRING) 1443234449Sobrien goto bad; 1444234449Sobrien m->str_flags = (m->str_flags & ~PSTRING_LEN) | PSTRING_2_BE; 1445234449Sobrien break; 1446234449Sobrien case CHAR_PSTRING_2_LE: 1447234449Sobrien if (m->type != FILE_PSTRING) 1448234449Sobrien goto bad; 1449234449Sobrien m->str_flags = (m->str_flags & ~PSTRING_LEN) | PSTRING_2_LE; 1450234449Sobrien break; 1451234449Sobrien case CHAR_PSTRING_4_BE: 1452234449Sobrien if (m->type != FILE_PSTRING) 1453234449Sobrien goto bad; 1454234449Sobrien m->str_flags = (m->str_flags & ~PSTRING_LEN) | PSTRING_4_BE; 1455234449Sobrien break; 1456234449Sobrien case CHAR_PSTRING_4_LE: 1457234449Sobrien if (m->type != FILE_PSTRING) 1458234449Sobrien goto bad; 1459234449Sobrien m->str_flags = (m->str_flags & ~PSTRING_LEN) | PSTRING_4_LE; 1460234449Sobrien break; 1461234449Sobrien case CHAR_PSTRING_LENGTH_INCLUDES_ITSELF: 1462234449Sobrien if (m->type != FILE_PSTRING) 1463234449Sobrien goto bad; 1464234449Sobrien m->str_flags |= PSTRING_LENGTH_INCLUDES_ITSELF; 1465234449Sobrien break; 146668349Sobrien default: 1467234449Sobrien bad: 1468133359Sobrien if (ms->flags & MAGIC_CHECK) 1469139368Sobrien file_magwarn(ms, 1470234449Sobrien "string extension `%c' " 1471234449Sobrien "invalid", *l); 147268349Sobrien return -1; 147368349Sobrien } 1474169962Sobrien /* allow multiple '/' for readability */ 1475186691Sobrien if (l[1] == '/' && 1476186691Sobrien !isspace((unsigned char)l[2])) 1477169962Sobrien l++; 147868349Sobrien } 1479169962Sobrien if (string_modifier_check(ms, m) == -1) 1480169962Sobrien return -1; 148168349Sobrien } 1482169962Sobrien else { 1483169962Sobrien if (ms->flags & MAGIC_CHECK) 1484169962Sobrien file_magwarn(ms, "invalid string op: %c", *t); 1485169962Sobrien return -1; 1486169962Sobrien } 148780588Sobrien } 1488133359Sobrien /* 1489133359Sobrien * We used to set mask to all 1's here, instead let's just not do 1490133359Sobrien * anything if mask = 0 (unless you have a better idea) 1491133359Sobrien */ 149268349Sobrien EATAB; 149368349Sobrien 149468349Sobrien switch (*l) { 149568349Sobrien case '>': 149668349Sobrien case '<': 1497186691Sobrien m->reln = *l; 1498186691Sobrien ++l; 1499186691Sobrien if (*l == '=') { 1500186691Sobrien if (ms->flags & MAGIC_CHECK) { 1501186691Sobrien file_magwarn(ms, "%c= not supported", 1502186691Sobrien m->reln); 1503186691Sobrien return -1; 1504186691Sobrien } 1505186691Sobrien ++l; 1506186691Sobrien } 1507186691Sobrien break; 150868349Sobrien /* Old-style anding: "0 byte &0x80 dynamically linked" */ 150968349Sobrien case '&': 151068349Sobrien case '^': 151168349Sobrien case '=': 151268349Sobrien m->reln = *l; 151368349Sobrien ++l; 151468349Sobrien if (*l == '=') { 151568349Sobrien /* HP compat: ignore &= etc. */ 151668349Sobrien ++l; 151768349Sobrien } 151868349Sobrien break; 151968349Sobrien case '!': 1520159764Sobrien m->reln = *l; 1521159764Sobrien ++l; 1522159764Sobrien break; 152368349Sobrien default: 1524169962Sobrien m->reln = '='; /* the default relation */ 1525159764Sobrien if (*l == 'x' && ((isascii((unsigned char)l[1]) && 1526159764Sobrien isspace((unsigned char)l[1])) || !l[1])) { 152768349Sobrien m->reln = *l; 152868349Sobrien ++l; 152968349Sobrien } 153068349Sobrien break; 153168349Sobrien } 1532169962Sobrien /* 1533169962Sobrien * Grab the value part, except for an 'x' reln. 1534169962Sobrien */ 1535169962Sobrien if (m->reln != 'x' && getvalue(ms, m, &l, action)) 153668349Sobrien return -1; 1537169962Sobrien 153868349Sobrien /* 153968349Sobrien * TODO finish this macro and start using it! 154068349Sobrien * #define offsetcheck {if (offset > HOWMANY-1) 154168349Sobrien * magwarn("offset too big"); } 154268349Sobrien */ 154368349Sobrien 154468349Sobrien /* 1545169962Sobrien * Now get last part - the description 154668349Sobrien */ 154768349Sobrien EATAB; 154868349Sobrien if (l[0] == '\b') { 154968349Sobrien ++l; 1550186691Sobrien m->flag |= NOSPACE; 155168349Sobrien } else if ((l[0] == '\\') && (l[1] == 'b')) { 155268349Sobrien ++l; 155368349Sobrien ++l; 1554186691Sobrien m->flag |= NOSPACE; 1555186691Sobrien } 1556169942Sobrien for (i = 0; (m->desc[i++] = *l++) != '\0' && i < sizeof(m->desc); ) 1557169942Sobrien continue; 1558169942Sobrien if (i == sizeof(m->desc)) { 1559169942Sobrien m->desc[sizeof(m->desc) - 1] = '\0'; 1560169942Sobrien if (ms->flags & MAGIC_CHECK) 1561169942Sobrien file_magwarn(ms, "description `%s' truncated", m->desc); 1562169942Sobrien } 156368349Sobrien 1564169942Sobrien /* 1565169942Sobrien * We only do this check while compiling, or if any of the magic 1566169942Sobrien * files were not compiled. 1567169942Sobrien */ 1568169942Sobrien if (ms->flags & MAGIC_CHECK) { 1569169942Sobrien if (check_format(ms, m) == -1) 1570133359Sobrien return -1; 1571133359Sobrien } 1572103373Sobrien#ifndef COMPILE_ONLY 1573133359Sobrien if (action == FILE_CHECK) { 1574133359Sobrien file_mdump(m); 157568349Sobrien } 1576103373Sobrien#endif 1577186691Sobrien m->mimetype[0] = '\0'; /* initialise MIME type to none */ 1578159764Sobrien if (m->cont_level == 0) 1579159764Sobrien ++(*nmentryp); /* make room for next */ 158068349Sobrien return 0; 158168349Sobrien} 158268349Sobrien 1583186691Sobrien/* 1584186691Sobrien * parse a STRENGTH annotation line from magic file, put into magic[index - 1] 1585186691Sobrien * if valid 1586186691Sobrien */ 1587169942Sobrienprivate int 1588186691Sobrienparse_strength(struct magic_set *ms, struct magic_entry *me, const char *line) 1589186691Sobrien{ 1590186691Sobrien const char *l = line; 1591186691Sobrien char *el; 1592186691Sobrien unsigned long factor; 1593186691Sobrien struct magic *m = &me->mp[0]; 1594186691Sobrien 1595186691Sobrien if (m->factor_op != FILE_FACTOR_OP_NONE) { 1596186691Sobrien file_magwarn(ms, 1597186691Sobrien "Current entry already has a strength type: %c %d", 1598186691Sobrien m->factor_op, m->factor); 1599186691Sobrien return -1; 1600186691Sobrien } 1601186691Sobrien EATAB; 1602186691Sobrien switch (*l) { 1603186691Sobrien case FILE_FACTOR_OP_NONE: 1604186691Sobrien case FILE_FACTOR_OP_PLUS: 1605186691Sobrien case FILE_FACTOR_OP_MINUS: 1606186691Sobrien case FILE_FACTOR_OP_TIMES: 1607186691Sobrien case FILE_FACTOR_OP_DIV: 1608186691Sobrien m->factor_op = *l++; 1609186691Sobrien break; 1610186691Sobrien default: 1611186691Sobrien file_magwarn(ms, "Unknown factor op `%c'", *l); 1612186691Sobrien return -1; 1613186691Sobrien } 1614186691Sobrien EATAB; 1615186691Sobrien factor = strtoul(l, &el, 0); 1616186691Sobrien if (factor > 255) { 1617186691Sobrien file_magwarn(ms, "Too large factor `%lu'", factor); 1618186691Sobrien goto out; 1619186691Sobrien } 1620186691Sobrien if (*el && !isspace((unsigned char)*el)) { 1621186691Sobrien file_magwarn(ms, "Bad factor `%s'", l); 1622186691Sobrien goto out; 1623186691Sobrien } 1624186691Sobrien m->factor = (uint8_t)factor; 1625186691Sobrien if (m->factor == 0 && m->factor_op == FILE_FACTOR_OP_DIV) { 1626186691Sobrien file_magwarn(ms, "Cannot have factor op `%c' and factor %u", 1627186691Sobrien m->factor_op, m->factor); 1628186691Sobrien goto out; 1629186691Sobrien } 1630186691Sobrien return 0; 1631186691Sobrienout: 1632186691Sobrien m->factor_op = FILE_FACTOR_OP_NONE; 1633186691Sobrien m->factor = 0; 1634186691Sobrien return -1; 1635186691Sobrien} 1636186691Sobrien 1637186691Sobrien/* 1638234449Sobrien * Parse an Apple CREATOR/TYPE annotation from magic file and put it into 1639234449Sobrien * magic[index - 1] 1640191771Sobrien */ 1641191771Sobrienprivate int 1642191771Sobrienparse_apple(struct magic_set *ms, struct magic_entry *me, const char *line) 1643191771Sobrien{ 1644191771Sobrien size_t i; 1645191771Sobrien const char *l = line; 1646191771Sobrien struct magic *m = &me->mp[me->cont_count == 0 ? 0 : me->cont_count - 1]; 1647191771Sobrien 1648191771Sobrien if (m->apple[0] != '\0') { 1649234449Sobrien file_magwarn(ms, "Current entry already has a APPLE type " 1650234449Sobrien "`%.8s', new type `%s'", m->mimetype, l); 1651191771Sobrien return -1; 1652191771Sobrien } 1653191771Sobrien 1654191771Sobrien EATAB; 1655234449Sobrien for (i = 0; *l && ((isascii((unsigned char)*l) && 1656234449Sobrien isalnum((unsigned char)*l)) || strchr("-+/.", *l)) && 1657234449Sobrien i < sizeof(m->apple); m->apple[i++] = *l++) 1658191771Sobrien continue; 1659191771Sobrien if (i == sizeof(m->apple) && *l) { 1660234449Sobrien /* We don't need to NUL terminate here, printing handles it */ 1661191771Sobrien if (ms->flags & MAGIC_CHECK) 1662234449Sobrien file_magwarn(ms, "APPLE type `%s' truncated %" 1663234449Sobrien SIZE_T_FORMAT "u", line, i); 1664191771Sobrien } 1665191771Sobrien 1666191771Sobrien if (i > 0) 1667191771Sobrien return 0; 1668191771Sobrien else 1669191771Sobrien return -1; 1670191771Sobrien} 1671191771Sobrien 1672191771Sobrien/* 1673186691Sobrien * parse a MIME annotation line from magic file, put into magic[index - 1] 1674186691Sobrien * if valid 1675186691Sobrien */ 1676186691Sobrienprivate int 1677186691Sobrienparse_mime(struct magic_set *ms, struct magic_entry *me, const char *line) 1678186691Sobrien{ 1679186691Sobrien size_t i; 1680186691Sobrien const char *l = line; 1681186691Sobrien struct magic *m = &me->mp[me->cont_count == 0 ? 0 : me->cont_count - 1]; 1682186691Sobrien 1683186691Sobrien if (m->mimetype[0] != '\0') { 1684186691Sobrien file_magwarn(ms, "Current entry already has a MIME type `%s'," 1685186691Sobrien " new type `%s'", m->mimetype, l); 1686186691Sobrien return -1; 1687186691Sobrien } 1688186691Sobrien 1689186691Sobrien EATAB; 1690234449Sobrien for (i = 0; *l && ((isascii((unsigned char)*l) && 1691234449Sobrien isalnum((unsigned char)*l)) || strchr("-+/.", *l)) && 1692234449Sobrien i < sizeof(m->mimetype); m->mimetype[i++] = *l++) 1693186691Sobrien continue; 1694186691Sobrien if (i == sizeof(m->mimetype)) { 1695234449Sobrien m->mimetype[sizeof(m->mimetype) - 1] = '\0'; 1696186691Sobrien if (ms->flags & MAGIC_CHECK) 1697234449Sobrien file_magwarn(ms, "MIME type `%s' truncated %" 1698234449Sobrien SIZE_T_FORMAT "u", m->mimetype, i); 1699186691Sobrien } else 1700186691Sobrien m->mimetype[i] = '\0'; 1701186691Sobrien 1702186691Sobrien if (i > 0) 1703186691Sobrien return 0; 1704186691Sobrien else 1705186691Sobrien return -1; 1706186691Sobrien} 1707186691Sobrien 1708186691Sobrienprivate int 1709169942Sobriencheck_format_type(const char *ptr, int type) 1710169942Sobrien{ 1711169942Sobrien int quad = 0; 1712169942Sobrien if (*ptr == '\0') { 1713169942Sobrien /* Missing format string; bad */ 1714169942Sobrien return -1; 1715169942Sobrien } 1716169942Sobrien 1717169942Sobrien switch (type) { 1718169942Sobrien case FILE_FMT_QUAD: 1719169942Sobrien quad = 1; 1720169942Sobrien /*FALLTHROUGH*/ 1721169942Sobrien case FILE_FMT_NUM: 1722169942Sobrien if (*ptr == '-') 1723169942Sobrien ptr++; 1724169942Sobrien if (*ptr == '.') 1725169942Sobrien ptr++; 1726169942Sobrien while (isdigit((unsigned char)*ptr)) ptr++; 1727169942Sobrien if (*ptr == '.') 1728169942Sobrien ptr++; 1729169942Sobrien while (isdigit((unsigned char)*ptr)) ptr++; 1730169942Sobrien if (quad) { 1731169942Sobrien if (*ptr++ != 'l') 1732169942Sobrien return -1; 1733169942Sobrien if (*ptr++ != 'l') 1734169942Sobrien return -1; 1735169942Sobrien } 1736169942Sobrien 1737169942Sobrien switch (*ptr++) { 1738169942Sobrien case 'l': 1739169942Sobrien switch (*ptr++) { 1740169942Sobrien case 'i': 1741169942Sobrien case 'd': 1742169942Sobrien case 'u': 1743169942Sobrien case 'x': 1744169942Sobrien case 'X': 1745169942Sobrien return 0; 1746169942Sobrien default: 1747169942Sobrien return -1; 1748169942Sobrien } 1749169942Sobrien 1750169942Sobrien case 'h': 1751169942Sobrien switch (*ptr++) { 1752169942Sobrien case 'h': 1753169942Sobrien switch (*ptr++) { 1754169942Sobrien case 'i': 1755169942Sobrien case 'd': 1756169942Sobrien case 'u': 1757169942Sobrien case 'x': 1758169942Sobrien case 'X': 1759169942Sobrien return 0; 1760169942Sobrien default: 1761169942Sobrien return -1; 1762169942Sobrien } 1763169942Sobrien case 'd': 1764169942Sobrien return 0; 1765169942Sobrien default: 1766169942Sobrien return -1; 1767169942Sobrien } 1768169942Sobrien 1769169942Sobrien case 'i': 1770169942Sobrien case 'c': 1771169942Sobrien case 'd': 1772169942Sobrien case 'u': 1773169942Sobrien case 'x': 1774169942Sobrien case 'X': 1775169942Sobrien return 0; 1776169942Sobrien 1777169942Sobrien default: 1778169942Sobrien return -1; 1779169942Sobrien } 1780169942Sobrien 1781175296Sobrien case FILE_FMT_FLOAT: 1782175296Sobrien case FILE_FMT_DOUBLE: 1783175296Sobrien if (*ptr == '-') 1784175296Sobrien ptr++; 1785175296Sobrien if (*ptr == '.') 1786175296Sobrien ptr++; 1787175296Sobrien while (isdigit((unsigned char)*ptr)) ptr++; 1788175296Sobrien if (*ptr == '.') 1789175296Sobrien ptr++; 1790175296Sobrien while (isdigit((unsigned char)*ptr)) ptr++; 1791175296Sobrien 1792175296Sobrien switch (*ptr++) { 1793175296Sobrien case 'e': 1794175296Sobrien case 'E': 1795175296Sobrien case 'f': 1796175296Sobrien case 'F': 1797175296Sobrien case 'g': 1798175296Sobrien case 'G': 1799175296Sobrien return 0; 1800175296Sobrien 1801175296Sobrien default: 1802175296Sobrien return -1; 1803175296Sobrien } 1804175296Sobrien 1805175296Sobrien 1806169942Sobrien case FILE_FMT_STR: 1807169942Sobrien if (*ptr == '-') 1808169942Sobrien ptr++; 1809169942Sobrien while (isdigit((unsigned char )*ptr)) 1810169942Sobrien ptr++; 1811169942Sobrien if (*ptr == '.') { 1812169942Sobrien ptr++; 1813169942Sobrien while (isdigit((unsigned char )*ptr)) 1814169942Sobrien ptr++; 1815169942Sobrien } 1816169942Sobrien 1817169942Sobrien switch (*ptr++) { 1818169942Sobrien case 's': 1819169942Sobrien return 0; 1820169942Sobrien default: 1821169942Sobrien return -1; 1822169942Sobrien } 1823169942Sobrien 1824169942Sobrien default: 1825169942Sobrien /* internal error */ 1826169942Sobrien abort(); 1827169942Sobrien } 1828169942Sobrien /*NOTREACHED*/ 1829169942Sobrien return -1; 1830169942Sobrien} 1831169942Sobrien 1832133359Sobrien/* 1833133359Sobrien * Check that the optional printf format in description matches 1834133359Sobrien * the type of the magic. 1835133359Sobrien */ 1836133359Sobrienprivate int 1837139368Sobriencheck_format(struct magic_set *ms, struct magic *m) 1838133359Sobrien{ 1839133359Sobrien char *ptr; 1840133359Sobrien 1841133359Sobrien for (ptr = m->desc; *ptr; ptr++) 1842133359Sobrien if (*ptr == '%') 1843133359Sobrien break; 1844133359Sobrien if (*ptr == '\0') { 1845133359Sobrien /* No format string; ok */ 1846133359Sobrien return 1; 1847133359Sobrien } 1848169942Sobrien 1849169942Sobrien assert(file_nformats == file_nnames); 1850169942Sobrien 1851169942Sobrien if (m->type >= file_nformats) { 1852186691Sobrien file_magwarn(ms, "Internal error inconsistency between " 1853169942Sobrien "m->type and format strings"); 1854169942Sobrien return -1; 1855133359Sobrien } 1856169942Sobrien if (file_formats[m->type] == FILE_FMT_NONE) { 1857186691Sobrien file_magwarn(ms, "No format string for `%s' with description " 1858169942Sobrien "`%s'", m->desc, file_names[m->type]); 1859169942Sobrien return -1; 1860133359Sobrien } 1861169942Sobrien 1862169942Sobrien ptr++; 1863169942Sobrien if (check_format_type(ptr, file_formats[m->type]) == -1) { 1864169942Sobrien /* 1865169942Sobrien * TODO: this error message is unhelpful if the format 1866169942Sobrien * string is not one character long 1867169942Sobrien */ 1868186691Sobrien file_magwarn(ms, "Printf format `%c' is not valid for type " 1869192350Sdelphij "`%s' in description `%s'", *ptr ? *ptr : '?', 1870169942Sobrien file_names[m->type], m->desc); 1871169942Sobrien return -1; 1872169942Sobrien } 1873169942Sobrien 1874133359Sobrien for (; *ptr; ptr++) { 1875169942Sobrien if (*ptr == '%') { 1876186691Sobrien file_magwarn(ms, 1877169942Sobrien "Too many format strings (should have at most one) " 1878169942Sobrien "for `%s' with description `%s'", 1879169942Sobrien file_names[m->type], m->desc); 1880169942Sobrien return -1; 1881133359Sobrien } 1882133359Sobrien } 1883169942Sobrien return 0; 1884133359Sobrien} 1885133359Sobrien 188668349Sobrien/* 188768349Sobrien * Read a numeric value from a pointer, into the value union of a magic 188868349Sobrien * pointer, according to the magic type. Update the string pointer to point 188968349Sobrien * just after the number read. Return 0 for success, non-zero for failure. 189068349Sobrien */ 1891133359Sobrienprivate int 1892169962Sobriengetvalue(struct magic_set *ms, struct magic *m, const char **p, int action) 189368349Sobrien{ 1894133359Sobrien switch (m->type) { 1895139368Sobrien case FILE_BESTRING16: 1896139368Sobrien case FILE_LESTRING16: 1897133359Sobrien case FILE_STRING: 1898133359Sobrien case FILE_PSTRING: 1899133359Sobrien case FILE_REGEX: 1900159764Sobrien case FILE_SEARCH: 1901192350Sdelphij *p = getstr(ms, m, *p, action == FILE_COMPILE); 1902133359Sobrien if (*p == NULL) { 1903133359Sobrien if (ms->flags & MAGIC_CHECK) 1904139368Sobrien file_magwarn(ms, "cannot get string from `%s'", 1905133359Sobrien m->value.s); 1906133359Sobrien return -1; 1907133359Sobrien } 1908133359Sobrien return 0; 1909175296Sobrien case FILE_FLOAT: 1910175296Sobrien case FILE_BEFLOAT: 1911175296Sobrien case FILE_LEFLOAT: 1912175296Sobrien if (m->reln != 'x') { 1913175296Sobrien char *ep; 1914175296Sobrien#ifdef HAVE_STRTOF 1915175296Sobrien m->value.f = strtof(*p, &ep); 1916175296Sobrien#else 1917175296Sobrien m->value.f = (float)strtod(*p, &ep); 1918175296Sobrien#endif 1919175296Sobrien *p = ep; 1920175296Sobrien } 1921175296Sobrien return 0; 1922175296Sobrien case FILE_DOUBLE: 1923175296Sobrien case FILE_BEDOUBLE: 1924175296Sobrien case FILE_LEDOUBLE: 1925175296Sobrien if (m->reln != 'x') { 1926175296Sobrien char *ep; 1927175296Sobrien m->value.d = strtod(*p, &ep); 1928175296Sobrien *p = ep; 1929175296Sobrien } 1930175296Sobrien return 0; 1931133359Sobrien default: 193268349Sobrien if (m->reln != 'x') { 1933159764Sobrien char *ep; 1934169942Sobrien m->value.q = file_signextend(ms, m, 1935169942Sobrien (uint64_t)strtoull(*p, &ep, 0)); 1936159764Sobrien *p = ep; 193768349Sobrien eatsize(p); 193868349Sobrien } 1939133359Sobrien return 0; 1940133359Sobrien } 194168349Sobrien} 194268349Sobrien 194368349Sobrien/* 194468349Sobrien * Convert a string containing C character escapes. Stop at an unescaped 194568349Sobrien * space or tab. 1946192350Sdelphij * Copy the converted version to "m->value.s", and the length in m->vallen. 1947192350Sdelphij * Return updated scan pointer as function result. Warn if set. 194868349Sobrien */ 1949159764Sobrienprivate const char * 1950192350Sdelphijgetstr(struct magic_set *ms, struct magic *m, const char *s, int warn) 195168349Sobrien{ 1952159764Sobrien const char *origs = s; 1953192350Sdelphij char *p = m->value.s; 1954192350Sdelphij size_t plen = sizeof(m->value.s); 1955159764Sobrien char *origp = p; 195668349Sobrien char *pmax = p + plen - 1; 195768349Sobrien int c; 195868349Sobrien int val; 195968349Sobrien 196068349Sobrien while ((c = *s++) != '\0') { 196168349Sobrien if (isspace((unsigned char) c)) 196268349Sobrien break; 196368349Sobrien if (p >= pmax) { 1964133359Sobrien file_error(ms, 0, "string too long: `%s'", origs); 1965133359Sobrien return NULL; 196668349Sobrien } 1967169962Sobrien if (c == '\\') { 196868349Sobrien switch(c = *s++) { 196968349Sobrien 197068349Sobrien case '\0': 1971192350Sdelphij if (warn) 1972169962Sobrien file_magwarn(ms, "incomplete escape"); 197368349Sobrien goto out; 197468349Sobrien 1975169962Sobrien case '\t': 1976192350Sdelphij if (warn) { 1977169962Sobrien file_magwarn(ms, 1978169962Sobrien "escaped tab found, use \\t instead"); 1979192350Sdelphij warn = 0; /* already did */ 1980169962Sobrien } 1981169962Sobrien /*FALLTHROUGH*/ 198268349Sobrien default: 1983192350Sdelphij if (warn) { 1984192350Sdelphij if (isprint((unsigned char)c)) { 1985192350Sdelphij /* Allow escaping of 1986192350Sdelphij * ``relations'' */ 1987234449Sobrien if (strchr("<>&^=!", c) == NULL 1988234449Sobrien && (m->type != FILE_REGEX || 1989234449Sobrien strchr("[]().*?^$|{}", c) 1990234449Sobrien == NULL)) { 1991192350Sdelphij file_magwarn(ms, "no " 1992192350Sdelphij "need to escape " 1993192350Sdelphij "`%c'", c); 1994192350Sdelphij } 1995192350Sdelphij } else { 1996192350Sdelphij file_magwarn(ms, 1997192350Sdelphij "unknown escape sequence: " 1998192350Sdelphij "\\%03o", c); 1999192350Sdelphij } 2000169962Sobrien } 2001169962Sobrien /*FALLTHROUGH*/ 2002169962Sobrien /* space, perhaps force people to use \040? */ 2003169962Sobrien case ' ': 2004169962Sobrien#if 0 2005169962Sobrien /* 2006169962Sobrien * Other things people escape, but shouldn't need to, 2007169962Sobrien * so we disallow them 2008169962Sobrien */ 2009169962Sobrien case '\'': 2010169962Sobrien case '"': 2011169962Sobrien case '?': 2012169962Sobrien#endif 2013169962Sobrien /* Relations */ 2014169962Sobrien case '>': 2015169962Sobrien case '<': 2016169962Sobrien case '&': 2017169962Sobrien case '^': 2018169962Sobrien case '=': 2019169962Sobrien case '!': 2020169962Sobrien /* and baskslash itself */ 2021169962Sobrien case '\\': 202268349Sobrien *p++ = (char) c; 202368349Sobrien break; 202468349Sobrien 2025169962Sobrien case 'a': 2026169962Sobrien *p++ = '\a'; 2027169962Sobrien break; 2028169962Sobrien 2029169962Sobrien case 'b': 2030169962Sobrien *p++ = '\b'; 2031169962Sobrien break; 2032169962Sobrien 2033169962Sobrien case 'f': 2034169962Sobrien *p++ = '\f'; 2035169962Sobrien break; 2036169962Sobrien 203768349Sobrien case 'n': 203868349Sobrien *p++ = '\n'; 203968349Sobrien break; 204068349Sobrien 204168349Sobrien case 'r': 204268349Sobrien *p++ = '\r'; 204368349Sobrien break; 204468349Sobrien 204568349Sobrien case 't': 204668349Sobrien *p++ = '\t'; 204768349Sobrien break; 204868349Sobrien 204968349Sobrien case 'v': 205068349Sobrien *p++ = '\v'; 205168349Sobrien break; 205268349Sobrien 205368349Sobrien /* \ and up to 3 octal digits */ 205468349Sobrien case '0': 205568349Sobrien case '1': 205668349Sobrien case '2': 205768349Sobrien case '3': 205868349Sobrien case '4': 205968349Sobrien case '5': 206068349Sobrien case '6': 206168349Sobrien case '7': 206268349Sobrien val = c - '0'; 206368349Sobrien c = *s++; /* try for 2 */ 2064169962Sobrien if (c >= '0' && c <= '7') { 2065169962Sobrien val = (val << 3) | (c - '0'); 206668349Sobrien c = *s++; /* try for 3 */ 2067169962Sobrien if (c >= '0' && c <= '7') 2068169962Sobrien val = (val << 3) | (c-'0'); 206968349Sobrien else 207068349Sobrien --s; 207168349Sobrien } 207268349Sobrien else 207368349Sobrien --s; 207468349Sobrien *p++ = (char)val; 207568349Sobrien break; 207668349Sobrien 207768349Sobrien /* \x and up to 2 hex digits */ 207868349Sobrien case 'x': 207968349Sobrien val = 'x'; /* Default if no digits */ 208068349Sobrien c = hextoint(*s++); /* Get next char */ 208168349Sobrien if (c >= 0) { 208268349Sobrien val = c; 208368349Sobrien c = hextoint(*s++); 208468349Sobrien if (c >= 0) 208568349Sobrien val = (val << 4) + c; 208668349Sobrien else 208768349Sobrien --s; 208868349Sobrien } else 208968349Sobrien --s; 209068349Sobrien *p++ = (char)val; 209168349Sobrien break; 209268349Sobrien } 209368349Sobrien } else 209468349Sobrien *p++ = (char)c; 209568349Sobrien } 209668349Sobrienout: 209768349Sobrien *p = '\0'; 2098234449Sobrien m->vallen = CAST(unsigned char, (p - origp)); 2099192350Sdelphij if (m->type == FILE_PSTRING) 2100234449Sobrien m->vallen += (unsigned char)file_pstring_length_size(m); 210168349Sobrien return s; 210268349Sobrien} 210368349Sobrien 210468349Sobrien 210568349Sobrien/* Single hex char to int; -1 if not a hex char. */ 2106133359Sobrienprivate int 2107103373Sobrienhextoint(int c) 210868349Sobrien{ 210968349Sobrien if (!isascii((unsigned char) c)) 211068349Sobrien return -1; 211168349Sobrien if (isdigit((unsigned char) c)) 211268349Sobrien return c - '0'; 2113169962Sobrien if ((c >= 'a') && (c <= 'f')) 211468349Sobrien return c + 10 - 'a'; 2115169962Sobrien if (( c>= 'A') && (c <= 'F')) 211668349Sobrien return c + 10 - 'A'; 211768349Sobrien return -1; 211868349Sobrien} 211968349Sobrien 212068349Sobrien 212168349Sobrien/* 212268349Sobrien * Print a string containing C character escapes. 212368349Sobrien */ 2124133359Sobrienprotected void 2125133359Sobrienfile_showstr(FILE *fp, const char *s, size_t len) 212668349Sobrien{ 212768349Sobrien char c; 212868349Sobrien 212968349Sobrien for (;;) { 2130133359Sobrien if (len == ~0U) { 2131234449Sobrien c = *s++; 213268349Sobrien if (c == '\0') 213368349Sobrien break; 213468349Sobrien } 213568349Sobrien else { 213668349Sobrien if (len-- == 0) 213768349Sobrien break; 2138234449Sobrien c = *s++; 213968349Sobrien } 2140169962Sobrien if (c >= 040 && c <= 0176) /* TODO isprint && !iscntrl */ 214168349Sobrien (void) fputc(c, fp); 214268349Sobrien else { 214368349Sobrien (void) fputc('\\', fp); 214468349Sobrien switch (c) { 2145169962Sobrien case '\a': 2146169962Sobrien (void) fputc('a', fp); 2147169962Sobrien break; 2148169962Sobrien 2149169962Sobrien case '\b': 2150169962Sobrien (void) fputc('b', fp); 2151169962Sobrien break; 2152169962Sobrien 2153169962Sobrien case '\f': 2154169962Sobrien (void) fputc('f', fp); 2155169962Sobrien break; 2156169962Sobrien 215768349Sobrien case '\n': 215868349Sobrien (void) fputc('n', fp); 215968349Sobrien break; 216068349Sobrien 216168349Sobrien case '\r': 216268349Sobrien (void) fputc('r', fp); 216368349Sobrien break; 216468349Sobrien 216568349Sobrien case '\t': 216668349Sobrien (void) fputc('t', fp); 216768349Sobrien break; 216868349Sobrien 216968349Sobrien case '\v': 217068349Sobrien (void) fputc('v', fp); 217168349Sobrien break; 217268349Sobrien 217368349Sobrien default: 217468349Sobrien (void) fprintf(fp, "%.3o", c & 0377); 217568349Sobrien break; 217668349Sobrien } 217768349Sobrien } 217868349Sobrien } 217968349Sobrien} 218068349Sobrien 218168349Sobrien/* 218268349Sobrien * eatsize(): Eat the size spec from a number [eg. 10UL] 218368349Sobrien */ 2184133359Sobrienprivate void 2185159764Sobrieneatsize(const char **p) 218668349Sobrien{ 2187159764Sobrien const char *l = *p; 218868349Sobrien 218968349Sobrien if (LOWCASE(*l) == 'u') 219068349Sobrien l++; 219168349Sobrien 219268349Sobrien switch (LOWCASE(*l)) { 219368349Sobrien case 'l': /* long */ 219468349Sobrien case 's': /* short */ 219568349Sobrien case 'h': /* short */ 219668349Sobrien case 'b': /* char/byte */ 219768349Sobrien case 'c': /* char/byte */ 219868349Sobrien l++; 219968349Sobrien /*FALLTHROUGH*/ 220068349Sobrien default: 220168349Sobrien break; 220268349Sobrien } 220368349Sobrien 220468349Sobrien *p = l; 220568349Sobrien} 220674784Sobrien 2207210761Srpaulo#ifndef COMPILE_ONLY 220874784Sobrien/* 2209103373Sobrien * handle a compiled file. 221074784Sobrien */ 2211133359Sobrienprivate int 2212133359Sobrienapprentice_map(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp, 2213133359Sobrien const char *fn) 221474784Sobrien{ 221574784Sobrien int fd; 221674784Sobrien struct stat st; 2217103373Sobrien uint32_t *ptr; 2218103373Sobrien uint32_t version; 221974784Sobrien int needsbyteswap; 2220186691Sobrien char *dbname = NULL; 2221133359Sobrien void *mm = NULL; 222274784Sobrien 2223191771Sobrien dbname = mkdbname(ms, fn, 0); 222480588Sobrien if (dbname == NULL) 2225186691Sobrien goto error2; 222680588Sobrien 2227159764Sobrien if ((fd = open(dbname, O_RDONLY|O_BINARY)) == -1) 2228186691Sobrien goto error2; 222974784Sobrien 223074784Sobrien if (fstat(fd, &st) == -1) { 2231133359Sobrien file_error(ms, errno, "cannot stat `%s'", dbname); 2232186691Sobrien goto error1; 223374784Sobrien } 2234186691Sobrien if (st.st_size < 8) { 2235133359Sobrien file_error(ms, 0, "file `%s' is too small", dbname); 2236186691Sobrien goto error1; 2237133359Sobrien } 223874784Sobrien 223980588Sobrien#ifdef QUICK 2240103373Sobrien if ((mm = mmap(0, (size_t)st.st_size, PROT_READ|PROT_WRITE, 224174784Sobrien MAP_PRIVATE|MAP_FILE, fd, (off_t)0)) == MAP_FAILED) { 2242133359Sobrien file_error(ms, errno, "cannot map `%s'", dbname); 2243186691Sobrien goto error1; 224474784Sobrien } 2245133359Sobrien#define RET 2 224680588Sobrien#else 2247186691Sobrien if ((mm = CAST(void *, malloc((size_t)st.st_size))) == NULL) { 2248169942Sobrien file_oomem(ms, (size_t)st.st_size); 2249186691Sobrien goto error1; 225080588Sobrien } 2251192350Sdelphij if (read(fd, mm, (size_t)st.st_size) != (ssize_t)st.st_size) { 2252133359Sobrien file_badread(ms); 2253186691Sobrien goto error1; 225480588Sobrien } 2255133359Sobrien#define RET 1 225680588Sobrien#endif 2257186691Sobrien *magicp = CAST(struct magic *, mm); 225874784Sobrien (void)close(fd); 225975937Sobrien fd = -1; 2260133359Sobrien ptr = (uint32_t *)(void *)*magicp; 226174784Sobrien if (*ptr != MAGICNO) { 226274784Sobrien if (swap4(*ptr) != MAGICNO) { 2263186691Sobrien file_error(ms, 0, "bad magic in `%s'", dbname); 2264186691Sobrien goto error1; 226574784Sobrien } 226674784Sobrien needsbyteswap = 1; 226774784Sobrien } else 226874784Sobrien needsbyteswap = 0; 226974784Sobrien if (needsbyteswap) 227074784Sobrien version = swap4(ptr[1]); 227174784Sobrien else 227274784Sobrien version = ptr[1]; 227374784Sobrien if (version != VERSIONNO) { 2274234449Sobrien file_error(ms, 0, "File %s supports only version %d magic " 2275234449Sobrien "files. `%s' is version %d", VERSION, 2276175296Sobrien VERSIONNO, dbname, version); 2277186691Sobrien goto error1; 227874784Sobrien } 2279186691Sobrien *nmagicp = (uint32_t)(st.st_size / sizeof(struct magic)); 2280186691Sobrien if (*nmagicp > 0) 2281186691Sobrien (*nmagicp)--; 228274784Sobrien (*magicp)++; 228374784Sobrien if (needsbyteswap) 228474784Sobrien byteswap(*magicp, *nmagicp); 2285186691Sobrien free(dbname); 2286133359Sobrien return RET; 228774784Sobrien 2288186691Sobrienerror1: 228974784Sobrien if (fd != -1) 229074784Sobrien (void)close(fd); 2291103373Sobrien if (mm) { 229280588Sobrien#ifdef QUICK 2293133359Sobrien (void)munmap((void *)mm, (size_t)st.st_size); 229480588Sobrien#else 2295103373Sobrien free(mm); 229680588Sobrien#endif 229780588Sobrien } else { 229874784Sobrien *magicp = NULL; 229974784Sobrien *nmagicp = 0; 230074784Sobrien } 2301186691Sobrienerror2: 2302186691Sobrien free(dbname); 230374784Sobrien return -1; 230474784Sobrien} 2305210761Srpaulo#endif /* COMPILE_ONLY */ 230674784Sobrien 2307133359Sobrienprivate const uint32_t ar[] = { 2308133359Sobrien MAGICNO, VERSIONNO 2309133359Sobrien}; 231074784Sobrien/* 231174784Sobrien * handle an mmaped file. 231274784Sobrien */ 2313133359Sobrienprivate int 2314133359Sobrienapprentice_compile(struct magic_set *ms, struct magic **magicp, 2315133359Sobrien uint32_t *nmagicp, const char *fn) 231674784Sobrien{ 2317234449Sobrien int fd = -1; 2318186691Sobrien char *dbname; 2319186691Sobrien int rv = -1; 232074784Sobrien 2321191771Sobrien dbname = mkdbname(ms, fn, 1); 2322186691Sobrien 232380588Sobrien if (dbname == NULL) 2324186691Sobrien goto out; 232580588Sobrien 2326159764Sobrien if ((fd = open(dbname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644)) == -1) { 2327133359Sobrien file_error(ms, errno, "cannot open `%s'", dbname); 2328186691Sobrien goto out; 232974784Sobrien } 233074784Sobrien 2331133359Sobrien if (write(fd, ar, sizeof(ar)) != (ssize_t)sizeof(ar)) { 2332133359Sobrien file_error(ms, errno, "error writing `%s'", dbname); 2333186691Sobrien goto out; 233474784Sobrien } 233574784Sobrien 2336133359Sobrien if (lseek(fd, (off_t)sizeof(struct magic), SEEK_SET) 2337133359Sobrien != sizeof(struct magic)) { 2338133359Sobrien file_error(ms, errno, "error seeking `%s'", dbname); 2339186691Sobrien goto out; 234074784Sobrien } 234174784Sobrien 2342133359Sobrien if (write(fd, *magicp, (sizeof(struct magic) * *nmagicp)) 2343133359Sobrien != (ssize_t)(sizeof(struct magic) * *nmagicp)) { 2344133359Sobrien file_error(ms, errno, "error writing `%s'", dbname); 2345186691Sobrien goto out; 234674784Sobrien } 234774784Sobrien 2348234449Sobrien if (fd != -1) 2349234449Sobrien (void)close(fd); 2350186691Sobrien rv = 0; 2351186691Sobrienout: 2352186691Sobrien free(dbname); 2353186691Sobrien return rv; 235474784Sobrien} 235574784Sobrien 2356133359Sobrienprivate const char ext[] = ".mgc"; 235774784Sobrien/* 235874784Sobrien * make a dbname 235974784Sobrien */ 2360191771Sobrienprivate char * 2361191771Sobrienmkdbname(struct magic_set *ms, const char *fn, int strip) 236274784Sobrien{ 2363191771Sobrien const char *p, *q; 2364191771Sobrien char *buf; 2365191771Sobrien 2366139368Sobrien if (strip) { 2367139368Sobrien if ((p = strrchr(fn, '/')) != NULL) 2368139368Sobrien fn = ++p; 2369139368Sobrien } 2370139368Sobrien 2371191771Sobrien for (q = fn; *q; q++) 2372191771Sobrien continue; 2373191771Sobrien /* Look for .mgc */ 2374191771Sobrien for (p = ext + sizeof(ext) - 1; p >= ext && q >= fn; p--, q--) 2375191771Sobrien if (*p != *q) 2376191771Sobrien break; 2377186691Sobrien 2378191771Sobrien /* Did not find .mgc, restore q */ 2379191771Sobrien if (p >= ext) 2380191771Sobrien while (*q) 2381191771Sobrien q++; 2382191771Sobrien 2383191771Sobrien q++; 2384191771Sobrien /* Compatibility with old code that looked in .mime */ 2385191771Sobrien if (ms->flags & MAGIC_MIME) { 2386191771Sobrien asprintf(&buf, "%.*s.mime%s", (int)(q - fn), fn, ext); 2387191771Sobrien if (access(buf, R_OK) != -1) { 2388191771Sobrien ms->flags &= MAGIC_MIME_TYPE; 2389191771Sobrien return buf; 2390191771Sobrien } 2391191771Sobrien free(buf); 2392186691Sobrien } 2393191771Sobrien asprintf(&buf, "%.*s%s", (int)(q - fn), fn, ext); 2394191771Sobrien 2395191771Sobrien /* Compatibility with old code that looked in .mime */ 2396191771Sobrien if (strstr(p, ".mime") != NULL) 2397191771Sobrien ms->flags &= MAGIC_MIME_TYPE; 2398191771Sobrien return buf; 239974784Sobrien} 240074784Sobrien 2401210761Srpaulo#ifndef COMPILE_ONLY 240274784Sobrien/* 240374784Sobrien * Byteswap an mmap'ed file if needed 240474784Sobrien */ 2405133359Sobrienprivate void 2406103373Sobrienbyteswap(struct magic *magic, uint32_t nmagic) 240774784Sobrien{ 2408103373Sobrien uint32_t i; 240974784Sobrien for (i = 0; i < nmagic; i++) 241074784Sobrien bs1(&magic[i]); 241174784Sobrien} 241274784Sobrien 241374784Sobrien/* 241474784Sobrien * swap a short 241574784Sobrien */ 2416133359Sobrienprivate uint16_t 2417103373Sobrienswap2(uint16_t sv) 241874784Sobrien{ 2419103373Sobrien uint16_t rv; 2420133359Sobrien uint8_t *s = (uint8_t *)(void *)&sv; 2421133359Sobrien uint8_t *d = (uint8_t *)(void *)&rv; 242274784Sobrien d[0] = s[1]; 242374784Sobrien d[1] = s[0]; 242474784Sobrien return rv; 242574784Sobrien} 242674784Sobrien 242774784Sobrien/* 242874784Sobrien * swap an int 242974784Sobrien */ 2430133359Sobrienprivate uint32_t 2431103373Sobrienswap4(uint32_t sv) 243274784Sobrien{ 2433103373Sobrien uint32_t rv; 2434133359Sobrien uint8_t *s = (uint8_t *)(void *)&sv; 2435133359Sobrien uint8_t *d = (uint8_t *)(void *)&rv; 243674784Sobrien d[0] = s[3]; 243774784Sobrien d[1] = s[2]; 243874784Sobrien d[2] = s[1]; 243974784Sobrien d[3] = s[0]; 244074784Sobrien return rv; 244174784Sobrien} 244274784Sobrien 244374784Sobrien/* 2444169942Sobrien * swap a quad 2445169942Sobrien */ 2446169942Sobrienprivate uint64_t 2447169942Sobrienswap8(uint64_t sv) 2448169942Sobrien{ 2449186691Sobrien uint64_t rv; 2450169942Sobrien uint8_t *s = (uint8_t *)(void *)&sv; 2451169942Sobrien uint8_t *d = (uint8_t *)(void *)&rv; 2452186691Sobrien#if 0 2453169942Sobrien d[0] = s[3]; 2454169942Sobrien d[1] = s[2]; 2455169942Sobrien d[2] = s[1]; 2456169942Sobrien d[3] = s[0]; 2457169942Sobrien d[4] = s[7]; 2458169942Sobrien d[5] = s[6]; 2459169942Sobrien d[6] = s[5]; 2460169942Sobrien d[7] = s[4]; 2461186691Sobrien#else 2462186691Sobrien d[0] = s[7]; 2463186691Sobrien d[1] = s[6]; 2464186691Sobrien d[2] = s[5]; 2465186691Sobrien d[3] = s[4]; 2466186691Sobrien d[4] = s[3]; 2467186691Sobrien d[5] = s[2]; 2468186691Sobrien d[6] = s[1]; 2469186691Sobrien d[7] = s[0]; 2470186691Sobrien#endif 2471169942Sobrien return rv; 2472169942Sobrien} 2473169942Sobrien 2474169942Sobrien/* 247574784Sobrien * byteswap a single magic entry 247674784Sobrien */ 2477133359Sobrienprivate void 2478133359Sobrienbs1(struct magic *m) 247974784Sobrien{ 248074784Sobrien m->cont_level = swap2(m->cont_level); 2481133359Sobrien m->offset = swap4((uint32_t)m->offset); 2482133359Sobrien m->in_offset = swap4((uint32_t)m->in_offset); 2483169962Sobrien m->lineno = swap4((uint32_t)m->lineno); 2484169962Sobrien if (IS_STRING(m->type)) { 2485186691Sobrien m->str_range = swap4(m->str_range); 2486169962Sobrien m->str_flags = swap4(m->str_flags); 2487169962Sobrien } 2488169962Sobrien else { 2489169942Sobrien m->value.q = swap8(m->value.q); 2490169962Sobrien m->num_mask = swap8(m->num_mask); 2491169962Sobrien } 249274784Sobrien} 2493210761Srpaulo#endif /* COMPILE_ONLY */ 2494234449Sobrien 2495234449Sobrienprotected size_t 2496234449Sobrienfile_pstring_length_size(const struct magic *m) 2497234449Sobrien{ 2498234449Sobrien switch (m->str_flags & PSTRING_LEN) { 2499234449Sobrien case PSTRING_1_LE: 2500234449Sobrien return 1; 2501234449Sobrien case PSTRING_2_LE: 2502234449Sobrien case PSTRING_2_BE: 2503234449Sobrien return 2; 2504234449Sobrien case PSTRING_4_LE: 2505234449Sobrien case PSTRING_4_BE: 2506234449Sobrien return 4; 2507234449Sobrien default: 2508234449Sobrien abort(); /* Impossible */ 2509234449Sobrien return 1; 2510234449Sobrien } 2511234449Sobrien} 2512234449Sobrienprotected size_t 2513234449Sobrienfile_pstring_get_length(const struct magic *m, const char *s) 2514234449Sobrien{ 2515234449Sobrien size_t len = 0; 2516234449Sobrien 2517234449Sobrien switch (m->str_flags & PSTRING_LEN) { 2518234449Sobrien case PSTRING_1_LE: 2519234449Sobrien len = *s; 2520234449Sobrien break; 2521234449Sobrien case PSTRING_2_LE: 2522234449Sobrien len = (s[1] << 8) | s[0]; 2523234449Sobrien break; 2524234449Sobrien case PSTRING_2_BE: 2525234449Sobrien len = (s[0] << 8) | s[1]; 2526234449Sobrien break; 2527234449Sobrien case PSTRING_4_LE: 2528234449Sobrien len = (s[3] << 24) | (s[2] << 16) | (s[1] << 8) | s[0]; 2529234449Sobrien break; 2530234449Sobrien case PSTRING_4_BE: 2531234449Sobrien len = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; 2532234449Sobrien break; 2533234449Sobrien default: 2534234449Sobrien abort(); /* Impossible */ 2535234449Sobrien } 2536234449Sobrien 2537234449Sobrien if (m->str_flags & PSTRING_LENGTH_INCLUDES_ITSELF) 2538234449Sobrien len -= file_pstring_length_size(m); 2539234449Sobrien 2540234449Sobrien return len; 2541234449Sobrien} 2542