root/ext/fileinfo/libmagic/apprentice.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. get_type
  2. get_standard_integer_type
  3. init_file_tables
  4. add_mlist
  5. apprentice_1
  6. file_ms_free
  7. file_ms_alloc
  8. apprentice_unmap
  9. mlist_alloc
  10. mlist_free
  11. file_apprentice
  12. apprentice_magic_strength
  13. apprentice_sort
  14. apprentice_list
  15. set_test_type
  16. addentry
  17. load_1
  18. cmpstrp
  19. set_text_binary
  20. set_last_default
  21. coalesce_entries
  22. magic_entry_free
  23. apprentice_load
  24. file_signextend
  25. string_modifier_check
  26. get_op
  27. get_cond
  28. check_cond
  29. parse
  30. parse_strength
  31. parse_apple
  32. parse_mime
  33. check_format_type
  34. check_format
  35. getvalue
  36. getstr
  37. hextoint
  38. file_showstr
  39. eatsize
  40. apprentice_map
  41. apprentice_compile
  42. mkdbname
  43. byteswap
  44. swap2
  45. swap4
  46. swap8
  47. bs1
  48. file_pstring_length_size
  49. file_pstring_get_length
  50. file_magicfind

   1 /*
   2  * Copyright (c) Ian F. Darwin 1986-1995.
   3  * Software written by Ian F. Darwin and others;
   4  * maintained 1995-present by Christos Zoulas and others.
   5  * 
   6  * Redistribution and use in source and binary forms, with or without
   7  * modification, are permitted provided that the following conditions
   8  * are met:
   9  * 1. Redistributions of source code must retain the above copyright
  10  *    notice immediately at the beginning of the file, without modification,
  11  *    this list of conditions, and the following disclaimer.
  12  * 2. Redistributions in binary form must reproduce the above copyright
  13  *    notice, this list of conditions and the following disclaimer in the
  14  *    documentation and/or other materials provided with the distribution.
  15  *  
  16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
  20  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  26  * SUCH DAMAGE.
  27  */
  28 /*
  29  * apprentice - make one pass through /etc/magic, learning its secrets.
  30  */
  31 
  32 #include "php.h"
  33 
  34 #include "file.h"
  35 
  36 #ifndef lint
  37 FILE_RCSID("@(#)$File: apprentice.c,v 1.196 2013/11/19 21:01:12 christos Exp $")
  38 #endif  /* lint */
  39 
  40 #include "magic.h"
  41 #include "patchlevel.h"
  42 #include <stdlib.h>
  43 
  44 #if defined(__hpux) && !defined(HAVE_STRTOULL)
  45 #if SIZEOF_LONG == 8
  46 # define strtoull strtoul
  47 #else
  48 # define strtoull __strtoull
  49 #endif
  50 #endif
  51 
  52 #ifdef PHP_WIN32
  53 #include "win32/unistd.h"
  54 #if _MSC_VER <= 1300
  55 # include "win32/php_strtoi64.h"
  56 #endif
  57 #define strtoull _strtoui64
  58 #else
  59 #include <unistd.h>
  60 #endif
  61 #include <string.h>
  62 #include <assert.h>
  63 #include <ctype.h>
  64 #include <fcntl.h>
  65 
  66 #define EATAB {while (isascii((unsigned char) *l) && \
  67                       isspace((unsigned char) *l))  ++l;}
  68 #define LOWCASE(l) (isupper((unsigned char) (l)) ? \
  69                         tolower((unsigned char) (l)) : (l))
  70 /*
  71  * Work around a bug in headers on Digital Unix.
  72  * At least confirmed for: OSF1 V4.0 878
  73  */
  74 #if defined(__osf__) && defined(__DECC)
  75 #ifdef MAP_FAILED
  76 #undef MAP_FAILED
  77 #endif
  78 #endif
  79 
  80 #ifndef MAP_FAILED
  81 #define MAP_FAILED (void *) -1
  82 #endif
  83 
  84 #ifndef MAP_FILE
  85 #define MAP_FILE 0
  86 #endif
  87 
  88 #define ALLOC_CHUNK     (size_t)10
  89 #define ALLOC_INCR      (size_t)200
  90 
  91 struct magic_entry {
  92         struct magic *mp;       
  93         uint32_t cont_count;
  94         uint32_t max_count;
  95 };
  96 
  97 struct magic_entry_set {
  98         struct magic_entry *me;
  99         uint32_t count;
 100         uint32_t max;
 101 };
 102 
 103 struct magic_map {
 104         void *p;
 105         size_t len;
 106         struct magic *magic[MAGIC_SETS];
 107         uint32_t nmagic[MAGIC_SETS];
 108 };
 109 
 110 int file_formats[FILE_NAMES_SIZE];
 111 const size_t file_nformats = FILE_NAMES_SIZE;
 112 const char *file_names[FILE_NAMES_SIZE];
 113 const size_t file_nnames = FILE_NAMES_SIZE;
 114 
 115 private int getvalue(struct magic_set *ms, struct magic *, const char **, int);
 116 private int hextoint(int);
 117 private const char *getstr(struct magic_set *, struct magic *, const char *,
 118     int);
 119 private int parse(struct magic_set *, struct magic_entry *, const char *,
 120     size_t, int);
 121 private void eatsize(const char **);
 122 private int apprentice_1(struct magic_set *, const char *, int);
 123 private size_t apprentice_magic_strength(const struct magic *);
 124 private int apprentice_sort(const void *, const void *);
 125 private void apprentice_list(struct mlist *, int );
 126 private struct magic_map *apprentice_load(struct magic_set *, 
 127     const char *, int);
 128 private struct mlist *mlist_alloc(void);
 129 private void mlist_free(struct mlist *);
 130 private void byteswap(struct magic *, uint32_t);
 131 private void bs1(struct magic *);
 132 private uint16_t swap2(uint16_t);
 133 private uint32_t swap4(uint32_t);
 134 private uint64_t swap8(uint64_t);
 135 private char *mkdbname(struct magic_set *, const char *, int);
 136 private struct magic_map *apprentice_map(struct magic_set *, const char *);
 137 private void apprentice_unmap(struct magic_map *);
 138 private int apprentice_compile(struct magic_set *, struct magic_map *,
 139     const char *);
 140 private int check_format_type(const char *, int);
 141 private int check_format(struct magic_set *, struct magic *);
 142 private int get_op(char);
 143 private int parse_mime(struct magic_set *, struct magic_entry *, const char *);
 144 private int parse_strength(struct magic_set *, struct magic_entry *, const char *);
 145 private int parse_apple(struct magic_set *, struct magic_entry *, const char *);
 146 
 147 
 148 private size_t magicsize = sizeof(struct magic);
 149 
 150 private const char usg_hdr[] = "cont\toffset\ttype\topcode\tmask\tvalue\tdesc";
 151 
 152 private struct {
 153         const char *name;
 154         size_t len;
 155         int (*fun)(struct magic_set *, struct magic_entry *, const char *);
 156 } bang[] = {
 157 #define DECLARE_FIELD(name) { # name, sizeof(# name) - 1, parse_ ## name }
 158         DECLARE_FIELD(mime),
 159         DECLARE_FIELD(apple),
 160         DECLARE_FIELD(strength),
 161 #undef  DECLARE_FIELD
 162         { NULL, 0, NULL }
 163 };
 164 
 165 #include "../data_file.c"
 166 
 167 struct type_tbl_s {
 168         const char name[16];
 169         const size_t len;
 170         const int type;
 171         const int format;
 172 };
 173 
 174 /*
 175  * XXX - the actual Single UNIX Specification says that "long" means "long",
 176  * as in the C data type, but we treat it as meaning "4-byte integer".
 177  * Given that the OS X version of file 5.04 did the same, I guess that passes
 178  * the actual test; having "long" be dependent on how big a "long" is on
 179  * the machine running "file" is silly.
 180  */
 181 static const struct type_tbl_s type_tbl[] = {
 182 # define XX(s)          s, (sizeof(s) - 1)
 183 # define XX_NULL        "", 0
 184         { XX("invalid"),        FILE_INVALID,           FILE_FMT_NONE },
 185         { XX("byte"),           FILE_BYTE,              FILE_FMT_NUM },
 186         { XX("short"),          FILE_SHORT,             FILE_FMT_NUM },
 187         { XX("default"),        FILE_DEFAULT,           FILE_FMT_NONE },
 188         { XX("long"),           FILE_LONG,              FILE_FMT_NUM },
 189         { XX("string"),         FILE_STRING,            FILE_FMT_STR },
 190         { XX("date"),           FILE_DATE,              FILE_FMT_STR },
 191         { XX("beshort"),        FILE_BESHORT,           FILE_FMT_NUM },
 192         { XX("belong"),         FILE_BELONG,            FILE_FMT_NUM },
 193         { XX("bedate"),         FILE_BEDATE,            FILE_FMT_STR },
 194         { XX("leshort"),        FILE_LESHORT,           FILE_FMT_NUM },
 195         { XX("lelong"),         FILE_LELONG,            FILE_FMT_NUM },
 196         { XX("ledate"),         FILE_LEDATE,            FILE_FMT_STR },
 197         { XX("pstring"),        FILE_PSTRING,           FILE_FMT_STR },
 198         { XX("ldate"),          FILE_LDATE,             FILE_FMT_STR },
 199         { XX("beldate"),        FILE_BELDATE,           FILE_FMT_STR },
 200         { XX("leldate"),        FILE_LELDATE,           FILE_FMT_STR },
 201         { XX("regex"),          FILE_REGEX,             FILE_FMT_STR },
 202         { XX("bestring16"),     FILE_BESTRING16,        FILE_FMT_STR },
 203         { XX("lestring16"),     FILE_LESTRING16,        FILE_FMT_STR },
 204         { XX("search"),         FILE_SEARCH,            FILE_FMT_STR },
 205         { XX("medate"),         FILE_MEDATE,            FILE_FMT_STR },
 206         { XX("meldate"),        FILE_MELDATE,           FILE_FMT_STR },
 207         { XX("melong"),         FILE_MELONG,            FILE_FMT_NUM },
 208         { XX("quad"),           FILE_QUAD,              FILE_FMT_QUAD },
 209         { XX("lequad"),         FILE_LEQUAD,            FILE_FMT_QUAD },
 210         { XX("bequad"),         FILE_BEQUAD,            FILE_FMT_QUAD },
 211         { XX("qdate"),          FILE_QDATE,             FILE_FMT_STR },
 212         { XX("leqdate"),        FILE_LEQDATE,           FILE_FMT_STR },
 213         { XX("beqdate"),        FILE_BEQDATE,           FILE_FMT_STR },
 214         { XX("qldate"),         FILE_QLDATE,            FILE_FMT_STR },
 215         { XX("leqldate"),       FILE_LEQLDATE,          FILE_FMT_STR },
 216         { XX("beqldate"),       FILE_BEQLDATE,          FILE_FMT_STR },
 217         { XX("float"),          FILE_FLOAT,             FILE_FMT_FLOAT },
 218         { XX("befloat"),        FILE_BEFLOAT,           FILE_FMT_FLOAT },
 219         { XX("lefloat"),        FILE_LEFLOAT,           FILE_FMT_FLOAT },
 220         { XX("double"),         FILE_DOUBLE,            FILE_FMT_DOUBLE },
 221         { XX("bedouble"),       FILE_BEDOUBLE,          FILE_FMT_DOUBLE },
 222         { XX("ledouble"),       FILE_LEDOUBLE,          FILE_FMT_DOUBLE },
 223         { XX("leid3"),          FILE_LEID3,             FILE_FMT_NUM },
 224         { XX("beid3"),          FILE_BEID3,             FILE_FMT_NUM },
 225         { XX("indirect"),       FILE_INDIRECT,          FILE_FMT_NUM },
 226         { XX("qwdate"),         FILE_QWDATE,            FILE_FMT_STR },
 227         { XX("leqwdate"),       FILE_LEQWDATE,          FILE_FMT_STR },
 228         { XX("beqwdate"),       FILE_BEQWDATE,          FILE_FMT_STR },
 229         { XX("name"),           FILE_NAME,              FILE_FMT_NONE },
 230         { XX("use"),            FILE_USE,               FILE_FMT_NONE },
 231         { XX("clear"),          FILE_CLEAR,             FILE_FMT_NONE },
 232         { XX_NULL,              FILE_INVALID,           FILE_FMT_NONE },
 233 };
 234 
 235 /*
 236  * These are not types, and cannot be preceded by "u" to make them
 237  * unsigned.
 238  */
 239 static const struct type_tbl_s special_tbl[] = {
 240         { XX("name"),           FILE_NAME,              FILE_FMT_STR },
 241         { XX("use"),            FILE_USE,               FILE_FMT_STR },
 242         { XX_NULL,              FILE_INVALID,           FILE_FMT_NONE },
 243 };
 244 # undef XX
 245 # undef XX_NULL
 246 
 247 #ifndef S_ISDIR
 248 #define S_ISDIR(mode) ((mode) & _S_IFDIR)
 249 #endif
 250 
 251 private int
 252 get_type(const struct type_tbl_s *tbl, const char *l, const char **t)
 253 {
 254         const struct type_tbl_s *p;
 255 
 256         for (p = tbl; p->len; p++) {
 257                 if (strncmp(l, p->name, p->len) == 0) {
 258                         if (t)
 259                                 *t = l + p->len;
 260                         break;
 261                 }
 262         }
 263         return p->type;
 264 }
 265 
 266 private int
 267 get_standard_integer_type(const char *l, const char **t)
 268 {
 269         int type;
 270 
 271         if (isalpha((unsigned char)l[1])) {
 272                 switch (l[1]) {
 273                 case 'C':
 274                         /* "dC" and "uC" */
 275                         type = FILE_BYTE;
 276                         break;
 277                 case 'S':
 278                         /* "dS" and "uS" */
 279                         type = FILE_SHORT;
 280                         break;
 281                 case 'I':
 282                 case 'L':
 283                         /*
 284                          * "dI", "dL", "uI", and "uL".
 285                          *
 286                          * XXX - the actual Single UNIX Specification says
 287                          * that "L" means "long", as in the C data type,
 288                          * but we treat it as meaning "4-byte integer".
 289                          * Given that the OS X version of file 5.04 did
 290                          * the same, I guess that passes the actual SUS
 291                          * validation suite; having "dL" be dependent on
 292                          * how big a "long" is on the machine running
 293                          * "file" is silly.
 294                          */
 295                         type = FILE_LONG;
 296                         break;
 297                 case 'Q':
 298                         /* "dQ" and "uQ" */
 299                         type = FILE_QUAD;
 300                         break;
 301                 default:
 302                         /* "d{anything else}", "u{anything else}" */
 303                         return FILE_INVALID;
 304                 }
 305                 l += 2;
 306         } else if (isdigit((unsigned char)l[1])) {
 307                 /*
 308                  * "d{num}" and "u{num}"; we only support {num} values
 309                  * of 1, 2, 4, and 8 - the Single UNIX Specification
 310                  * doesn't say anything about whether arbitrary
 311                  * values should be supported, but both the Solaris 10
 312                  * and OS X Mountain Lion versions of file passed the
 313                  * Single UNIX Specification validation suite, and
 314                  * neither of them support values bigger than 8 or
 315                  * non-power-of-2 values.
 316                  */
 317                 if (isdigit((unsigned char)l[2])) {
 318                         /* Multi-digit, so > 9 */
 319                         return FILE_INVALID;
 320                 }
 321                 switch (l[1]) {
 322                 case '1':
 323                         type = FILE_BYTE;
 324                         break;
 325                 case '2':
 326                         type = FILE_SHORT;
 327                         break;
 328                 case '4':
 329                         type = FILE_LONG;
 330                         break;
 331                 case '8':
 332                         type = FILE_QUAD;
 333                         break;
 334                 default:
 335                         /* XXX - what about 3, 5, 6, or 7? */
 336                         return FILE_INVALID;
 337                 }
 338                 l += 2;
 339         } else {
 340                 /*
 341                  * "d" or "u" by itself.
 342                  */
 343                 type = FILE_LONG;
 344                 ++l;
 345         }
 346         if (t)
 347                 *t = l;
 348         return type;
 349 }
 350 
 351 private void
 352 init_file_tables(void)
 353 {
 354         static int done = 0;
 355         const struct type_tbl_s *p;
 356 
 357         if (done)
 358                 return;
 359         done++;
 360 
 361         for (p = type_tbl; p->len; p++) {
 362                 assert(p->type < FILE_NAMES_SIZE);
 363                 file_names[p->type] = p->name;
 364                 file_formats[p->type] = p->format;
 365         }
 366         assert(p - type_tbl == FILE_NAMES_SIZE);
 367 }
 368 
 369 private int
 370 add_mlist(struct mlist *mlp, struct magic_map *map, size_t idx)
 371 {
 372         struct mlist *ml;
 373 
 374         if ((ml = CAST(struct mlist *, emalloc(sizeof(*ml)))) == NULL)
 375                 return -1;
 376 
 377         ml->map = idx == 0 ? map : NULL;
 378         ml->magic = map->magic[idx];
 379         ml->nmagic = map->nmagic[idx];
 380 
 381         mlp->prev->next = ml;
 382         ml->prev = mlp->prev;
 383         ml->next = mlp;
 384         mlp->prev = ml;
 385         return 0;
 386 }
 387 
 388 /*
 389  * Handle one file or directory.
 390  */
 391 private int
 392 apprentice_1(struct magic_set *ms, const char *fn, int action)
 393 {
 394         struct mlist *ml;
 395         struct magic_map *map;
 396         size_t i;
 397 
 398         if (magicsize != FILE_MAGICSIZE) {
 399                 file_error(ms, 0, "magic element size %lu != %lu",
 400                     (unsigned long)sizeof(*map->magic[0]),
 401                     (unsigned long)FILE_MAGICSIZE);
 402                 return -1;
 403         }
 404 
 405         if (action == FILE_COMPILE) {
 406                 map = apprentice_load(ms, fn, action);
 407                 if (map == NULL)
 408                         return -1;
 409                 return apprentice_compile(ms, map, fn);
 410         }
 411 
 412         map = apprentice_map(ms, fn);
 413         if (map == NULL) {
 414                 if (fn) {
 415                         if (ms->flags & MAGIC_CHECK)
 416                                 file_magwarn(ms, "using regular magic file `%s'", fn);
 417                         map = apprentice_load(ms, fn, action);
 418                 }
 419                 if (map == NULL)
 420                         return -1;
 421         }
 422 
 423         for (i = 0; i < MAGIC_SETS; i++) {
 424                 if (add_mlist(ms->mlist[i], map, i) == -1) {
 425                         file_oomem(ms, sizeof(*ml));
 426                         apprentice_unmap(map);
 427                         return -1;
 428                 }
 429         }
 430 
 431         if (action == FILE_LIST) {
 432                 for (i = 0; i < MAGIC_SETS; i++) {
 433                         printf("Set %zu:\nBinary patterns:\n", i);
 434                         apprentice_list(ms->mlist[i], BINTEST);
 435                         printf("Text patterns:\n");
 436                         apprentice_list(ms->mlist[i], TEXTTEST);
 437                 }
 438         }
 439         
 440         return 0;
 441 }
 442 
 443 protected void
 444 file_ms_free(struct magic_set *ms)
 445 {
 446         size_t i;
 447         if (ms == NULL)
 448                 return;
 449         for (i = 0; i < MAGIC_SETS; i++)
 450                 mlist_free(ms->mlist[i]);
 451         if (ms->o.pbuf) {
 452                 efree(ms->o.pbuf);
 453         }
 454         if (ms->o.buf) {
 455                 efree(ms->o.buf);
 456         }
 457         if (ms->c.li) {
 458                 efree(ms->c.li);
 459         }
 460         efree(ms);
 461 }
 462 
 463 protected struct magic_set *
 464 file_ms_alloc(int flags)
 465 {
 466         struct magic_set *ms;
 467         size_t i, len;
 468 
 469         if ((ms = CAST(struct magic_set *, ecalloc((size_t)1,
 470             sizeof(struct magic_set)))) == NULL)
 471                 return NULL;
 472 
 473         if (magic_setflags(ms, flags) == -1) {
 474                 errno = EINVAL;
 475                 goto free;
 476         }
 477 
 478         ms->o.buf = ms->o.pbuf = NULL;
 479         len = (ms->c.len = 10) * sizeof(*ms->c.li);
 480 
 481         if ((ms->c.li = CAST(struct level_info *, emalloc(len))) == NULL)
 482                 goto free;
 483 
 484         ms->event_flags = 0;
 485         ms->error = -1;
 486         for (i = 0; i < MAGIC_SETS; i++)
 487                 ms->mlist[i] = NULL;
 488         ms->file = "unknown";
 489         ms->line = 0;
 490         return ms;
 491 free:
 492         efree(ms);
 493         return NULL;
 494 }
 495 
 496 private void
 497 apprentice_unmap(struct magic_map *map)
 498 {
 499         if (map == NULL)
 500                 return;
 501         if (map->p != php_magic_database) {
 502                 if (map->p == NULL) {
 503                         int j;
 504                         for (j = 0; j < MAGIC_SETS; j++) {
 505                                 if (map->magic[j]) {
 506                                         efree(map->magic[j]);
 507                                 }
 508                         }
 509                 } else {
 510                         efree(map->p);
 511                 }
 512         }
 513         efree(map);
 514 }
 515 
 516 private struct mlist *
 517 mlist_alloc(void)
 518 {
 519         struct mlist *mlist;
 520         if ((mlist = CAST(struct mlist *, ecalloc(1, sizeof(*mlist)))) == NULL) {
 521                 return NULL;
 522         }
 523         mlist->next = mlist->prev = mlist;
 524         return mlist;
 525 }
 526 
 527 private void
 528 mlist_free(struct mlist *mlist)
 529 {
 530         struct mlist *ml;
 531 
 532         if (mlist == NULL)
 533                 return;
 534 
 535         for (ml = mlist->next; ml != mlist;) {
 536                 struct mlist *next = ml->next;
 537                 if (ml->map)
 538                         apprentice_unmap(ml->map);
 539                 efree(ml);
 540                 ml = next;
 541         }
 542         efree(ml);
 543 }
 544 
 545 /* const char *fn: list of magic files and directories */
 546 protected int
 547 file_apprentice(struct magic_set *ms, const char *fn, int action)
 548 {
 549         char *p, *mfn;
 550         int file_err, errs = -1;
 551         size_t i;
 552 
 553         file_reset(ms);
 554 
 555 /* XXX disabling default magic loading so the compiled in data is used */
 556 #if 0
 557         if ((fn = magic_getpath(fn, action)) == NULL)
 558                 return -1;
 559 #endif
 560 
 561         init_file_tables();
 562 
 563         if (fn == NULL)
 564                 fn = getenv("MAGIC");
 565         if (fn == NULL) {
 566                 for (i = 0; i < MAGIC_SETS; i++) {
 567                         mlist_free(ms->mlist[i]);
 568                         if ((ms->mlist[i] = mlist_alloc()) == NULL) {
 569                                 file_oomem(ms, sizeof(*ms->mlist[i]));
 570                                 return -1;
 571                         }
 572                 }
 573                 return apprentice_1(ms, fn, action);
 574         }
 575 
 576         if ((mfn = estrdup(fn)) == NULL) {
 577                 file_oomem(ms, strlen(fn));
 578                 return -1;
 579         }
 580 
 581         for (i = 0; i < MAGIC_SETS; i++) {
 582                 mlist_free(ms->mlist[i]);
 583                 if ((ms->mlist[i] = mlist_alloc()) == NULL) {
 584                         file_oomem(ms, sizeof(*ms->mlist[i]));
 585                         if (i != 0) {
 586                                 --i;
 587                                 do
 588                                         mlist_free(ms->mlist[i]);
 589                                 while (i != 0);
 590                         }
 591                         efree(mfn);
 592                         return -1;
 593                 }
 594         }
 595         fn = mfn;
 596 
 597         while (fn) {
 598                 p = strchr(fn, PATHSEP);
 599                 if (p)
 600                         *p++ = '\0';
 601                 if (*fn == '\0')
 602                         break;
 603                 file_err = apprentice_1(ms, fn, action);
 604                 errs = MAX(errs, file_err);
 605                 fn = p;
 606         }
 607 
 608         efree(mfn);
 609 
 610         if (errs == -1) {
 611                 for (i = 0; i < MAGIC_SETS; i++) {
 612                         mlist_free(ms->mlist[i]);
 613                         ms->mlist[i] = NULL;
 614                 }
 615                 file_error(ms, 0, "could not find any valid magic files!");
 616                 return -1;
 617         }
 618 
 619 #if 0
 620         /*
 621          * Always leave the database loaded
 622          */
 623         if (action == FILE_LOAD)
 624                 return 0;
 625 
 626         for (i = 0; i < MAGIC_SETS; i++) {
 627                 mlist_free(ms->mlist[i]);
 628                 ms->mlist[i] = NULL;
 629         }
 630 #endif
 631 
 632         switch (action) {
 633         case FILE_LOAD:
 634         case FILE_COMPILE:
 635         case FILE_CHECK:
 636         case FILE_LIST:
 637                 return 0;
 638         default:
 639                 file_error(ms, 0, "Invalid action %d", action);
 640                 return -1;
 641         }
 642 }
 643 
 644 /*
 645  * Get weight of this magic entry, for sorting purposes.
 646  */
 647 private size_t
 648 apprentice_magic_strength(const struct magic *m)
 649 {
 650 #define MULT 10
 651         size_t val = 2 * MULT;  /* baseline strength */
 652 
 653         switch (m->type) {
 654         case FILE_DEFAULT:      /* make sure this sorts last */
 655                 if (m->factor_op != FILE_FACTOR_OP_NONE)
 656                         abort();
 657                 return 0;
 658 
 659         case FILE_BYTE:
 660                 val += 1 * MULT;
 661                 break;
 662 
 663         case FILE_SHORT:
 664         case FILE_LESHORT:
 665         case FILE_BESHORT:
 666                 val += 2 * MULT;
 667                 break;
 668 
 669         case FILE_LONG:
 670         case FILE_LELONG:
 671         case FILE_BELONG:
 672         case FILE_MELONG:
 673                 val += 4 * MULT;
 674                 break;
 675 
 676         case FILE_PSTRING:
 677         case FILE_STRING:
 678                 val += m->vallen * MULT;
 679                 break;
 680 
 681         case FILE_BESTRING16:
 682         case FILE_LESTRING16:
 683                 val += m->vallen * MULT / 2;
 684                 break;
 685 
 686         case FILE_SEARCH:
 687         case FILE_REGEX:
 688                 val += m->vallen * MAX(MULT / m->vallen, 1);
 689                 break;
 690 
 691         case FILE_DATE:
 692         case FILE_LEDATE:
 693         case FILE_BEDATE:
 694         case FILE_MEDATE:
 695         case FILE_LDATE:
 696         case FILE_LELDATE:
 697         case FILE_BELDATE:
 698         case FILE_MELDATE:
 699         case FILE_FLOAT:
 700         case FILE_BEFLOAT:
 701         case FILE_LEFLOAT:
 702                 val += 4 * MULT;
 703                 break;
 704 
 705         case FILE_QUAD:
 706         case FILE_BEQUAD:
 707         case FILE_LEQUAD:
 708         case FILE_QDATE:
 709         case FILE_LEQDATE:
 710         case FILE_BEQDATE:
 711         case FILE_QLDATE:
 712         case FILE_LEQLDATE:
 713         case FILE_BEQLDATE:
 714         case FILE_QWDATE:
 715         case FILE_LEQWDATE:
 716         case FILE_BEQWDATE:
 717         case FILE_DOUBLE:
 718         case FILE_BEDOUBLE:
 719         case FILE_LEDOUBLE:
 720                 val += 8 * MULT;
 721                 break;
 722 
 723         case FILE_INDIRECT:
 724         case FILE_NAME:
 725         case FILE_USE:
 726                 break;
 727 
 728         default:
 729                 val = 0;
 730                 (void)fprintf(stderr, "Bad type %d\n", m->type);
 731                 abort();
 732         }
 733 
 734         switch (m->reln) {
 735         case 'x':       /* matches anything penalize */
 736         case '!':       /* matches almost anything penalize */
 737                 val = 0;
 738                 break;
 739 
 740         case '=':       /* Exact match, prefer */
 741                 val += MULT;
 742                 break;
 743 
 744         case '>':
 745         case '<':       /* comparison match reduce strength */
 746                 val -= 2 * MULT;
 747                 break;
 748 
 749         case '^':
 750         case '&':       /* masking bits, we could count them too */
 751                 val -= MULT;
 752                 break;
 753 
 754         default:
 755                 (void)fprintf(stderr, "Bad relation %c\n", m->reln);
 756                 abort();
 757         }
 758 
 759         if (val == 0)   /* ensure we only return 0 for FILE_DEFAULT */
 760                 val = 1;
 761 
 762         switch (m->factor_op) {
 763         case FILE_FACTOR_OP_NONE:
 764                 break;
 765         case FILE_FACTOR_OP_PLUS:
 766                 val += m->factor;
 767                 break;
 768         case FILE_FACTOR_OP_MINUS:
 769                 val -= m->factor;
 770                 break;
 771         case FILE_FACTOR_OP_TIMES:
 772                 val *= m->factor;
 773                 break;
 774         case FILE_FACTOR_OP_DIV:
 775                 val /= m->factor;
 776                 break;
 777         default:
 778                 abort();
 779         }
 780 
 781         /*
 782          * Magic entries with no description get a bonus because they depend
 783          * on subsequent magic entries to print something.
 784          */
 785         if (m->desc[0] == '\0')
 786                 val++;
 787         return val;
 788 }
 789 
 790 /*  
 791  * Sort callback for sorting entries by "strength" (basically length)
 792  */
 793 private int
 794 apprentice_sort(const void *a, const void *b)
 795 {
 796         const struct magic_entry *ma = CAST(const struct magic_entry *, a);
 797         const struct magic_entry *mb = CAST(const struct magic_entry *, b);
 798         size_t sa = apprentice_magic_strength(ma->mp);
 799         size_t sb = apprentice_magic_strength(mb->mp);
 800         if (sa == sb)
 801                 return 0;
 802         else if (sa > sb)
 803                 return -1;
 804         else
 805                 return 1;
 806 }
 807 
 808 /*  
 809  * Shows sorted patterns list in the order which is used for the matching
 810  */
 811 private void
 812 apprentice_list(struct mlist *mlist, int mode)
 813 {
 814         uint32_t magindex = 0;
 815         struct mlist *ml;
 816         for (ml = mlist->next; ml != mlist; ml = ml->next) {
 817                 for (magindex = 0; magindex < ml->nmagic; magindex++) {
 818                         struct magic *m = &ml->magic[magindex];
 819                         if ((m->flag & mode) != mode) {
 820                                 /* Skip sub-tests */
 821                                 while (magindex + 1 < ml->nmagic &&
 822                                        ml->magic[magindex + 1].cont_level != 0)
 823                                         ++magindex;
 824                                 continue; /* Skip to next top-level test*/
 825                         }
 826 
 827                         /*
 828                          * Try to iterate over the tree until we find item with
 829                          * description/mimetype.
 830                          */
 831                         while (magindex + 1 < ml->nmagic &&
 832                                ml->magic[magindex + 1].cont_level != 0 &&
 833                                *ml->magic[magindex].desc == '\0' &&
 834                                *ml->magic[magindex].mimetype == '\0')
 835                                 magindex++;
 836 
 837                         printf("Strength = %3" SIZE_T_FORMAT "u : %s [%s]\n",
 838                             apprentice_magic_strength(m),
 839                             ml->magic[magindex].desc,
 840                             ml->magic[magindex].mimetype);
 841                 }
 842         }
 843 }
 844 
 845 private void
 846 set_test_type(struct magic *mstart, struct magic *m)
 847 {
 848         switch (m->type) {
 849         case FILE_BYTE:
 850         case FILE_SHORT:
 851         case FILE_LONG:
 852         case FILE_DATE:
 853         case FILE_BESHORT:
 854         case FILE_BELONG:
 855         case FILE_BEDATE:
 856         case FILE_LESHORT:
 857         case FILE_LELONG:
 858         case FILE_LEDATE:
 859         case FILE_LDATE:
 860         case FILE_BELDATE:
 861         case FILE_LELDATE:
 862         case FILE_MEDATE:
 863         case FILE_MELDATE:
 864         case FILE_MELONG:
 865         case FILE_QUAD:
 866         case FILE_LEQUAD:
 867         case FILE_BEQUAD:
 868         case FILE_QDATE:
 869         case FILE_LEQDATE:
 870         case FILE_BEQDATE:
 871         case FILE_QLDATE:
 872         case FILE_LEQLDATE:
 873         case FILE_BEQLDATE:
 874         case FILE_QWDATE:
 875         case FILE_LEQWDATE:
 876         case FILE_BEQWDATE:
 877         case FILE_FLOAT:
 878         case FILE_BEFLOAT:
 879         case FILE_LEFLOAT:
 880         case FILE_DOUBLE:
 881         case FILE_BEDOUBLE:
 882         case FILE_LEDOUBLE:
 883                 mstart->flag |= BINTEST;
 884                 break;
 885         case FILE_STRING:
 886         case FILE_PSTRING:
 887         case FILE_BESTRING16:
 888         case FILE_LESTRING16:
 889                 /* Allow text overrides */
 890                 if (mstart->str_flags & STRING_TEXTTEST)
 891                         mstart->flag |= TEXTTEST;
 892                 else
 893                         mstart->flag |= BINTEST;
 894                 break;
 895         case FILE_REGEX:
 896         case FILE_SEARCH:
 897                 /* Check for override */
 898                 if (mstart->str_flags & STRING_BINTEST)
 899                         mstart->flag |= BINTEST;
 900                 if (mstart->str_flags & STRING_TEXTTEST)
 901                         mstart->flag |= TEXTTEST;
 902                     
 903                 if (mstart->flag & (TEXTTEST|BINTEST))
 904                         break;
 905 
 906                 /* binary test if pattern is not text */
 907                 if (file_looks_utf8(m->value.us, (size_t)m->vallen, NULL,
 908                     NULL) <= 0)
 909                         mstart->flag |= BINTEST;
 910                 else
 911                         mstart->flag |= TEXTTEST;
 912                 break;
 913         case FILE_DEFAULT:
 914                 /* can't deduce anything; we shouldn't see this at the
 915                    top level anyway */
 916                 break;
 917         case FILE_INVALID:
 918         default:
 919                 /* invalid search type, but no need to complain here */
 920                 break;
 921         }
 922 }
 923 
 924 private int
 925 addentry(struct magic_set *ms, struct magic_entry *me,
 926    struct magic_entry_set *mset)
 927 {
 928         size_t i = me->mp->type == FILE_NAME ? 1 : 0;
 929         if (mset[i].count == mset[i].max) {
 930                 struct magic_entry *mp;
 931 
 932                 mset[i].max += ALLOC_INCR;
 933                 if ((mp = CAST(struct magic_entry *,
 934                     erealloc(mset[i].me, sizeof(*mp) * mset[i].max))) ==
 935                     NULL) {
 936                         file_oomem(ms, sizeof(*mp) * mset[i].max);
 937                         return -1;
 938                 }
 939                 (void)memset(&mp[mset[i].count], 0, sizeof(*mp) *
 940                     ALLOC_INCR);
 941                 mset[i].me = mp;
 942         }
 943         mset[i].me[mset[i].count++] = *me;
 944         memset(me, 0, sizeof(*me));
 945         return 0;
 946 }
 947 
 948 /*
 949  * Load and parse one file.
 950  */
 951 private void
 952 load_1(struct magic_set *ms, int action, const char *fn, int *errs,
 953    struct magic_entry_set *mset)
 954 {
 955         char buffer[BUFSIZ + 1];
 956         char *line = NULL;
 957         size_t len;
 958         size_t lineno = 0;
 959         struct magic_entry me;
 960 
 961         php_stream *stream;
 962 
 963         TSRMLS_FETCH();
 964 
 965         ms->file = fn;
 966         stream = php_stream_open_wrapper((char *)fn, "rb", REPORT_ERRORS, NULL);
 967 
 968         if (stream == NULL) {
 969                 if (errno != ENOENT)
 970                         file_error(ms, errno, "cannot read magic file `%s'",
 971                                    fn);
 972                 (*errs)++;
 973                 return;
 974         }
 975 
 976         memset(&me, 0, sizeof(me));
 977         /* read and parse this file */
 978         for (ms->line = 1; (line = php_stream_get_line(stream, buffer , BUFSIZ, &len)) != NULL; ms->line++) {
 979                 if (len == 0) /* null line, garbage, etc */
 980                         continue;
 981                 if (line[len - 1] == '\n') {
 982                         lineno++;
 983                         line[len - 1] = '\0'; /* delete newline */
 984                 }
 985                 switch (line[0]) {
 986                 case '\0':      /* empty, do not parse */
 987                 case '#':       /* comment, do not parse */
 988                         continue;
 989                 case '!':
 990                         if (line[1] == ':') {
 991                                 size_t i;
 992 
 993                                 for (i = 0; bang[i].name != NULL; i++) {
 994                                         if ((size_t)(len - 2) > bang[i].len &&
 995                                             memcmp(bang[i].name, line + 2,
 996                                             bang[i].len) == 0)
 997                                                 break;
 998                                 }
 999                                 if (bang[i].name == NULL) {
1000                                         file_error(ms, 0,
1001                                             "Unknown !: entry `%s'", line);
1002                                         (*errs)++;
1003                                         continue;
1004                                 }
1005                                 if (me.mp == NULL) {
1006                                         file_error(ms, 0,
1007                                             "No current entry for :!%s type",
1008                                                 bang[i].name);
1009                                         (*errs)++;
1010                                         continue;
1011                                 }
1012                                 if ((*bang[i].fun)(ms, &me,
1013                                     line + bang[i].len + 2) != 0) {
1014                                         (*errs)++;
1015                                         continue;
1016                                 }
1017                                 continue;
1018                         }
1019                         /*FALLTHROUGH*/
1020                 default:
1021                 again:
1022                         switch (parse(ms, &me, line, lineno, action)) {
1023                         case 0:
1024                                 continue;
1025                         case 1:
1026                                 (void)addentry(ms, &me, mset);
1027                                 goto again;
1028                         default:
1029                                 (*errs)++;
1030                                 break;
1031                         }
1032                 }
1033         }
1034         if (me.mp)
1035                 (void)addentry(ms, &me, mset);
1036         php_stream_close(stream);
1037 }
1038 
1039 /*
1040  * parse a file or directory of files
1041  * const char *fn: name of magic file or directory
1042  */
1043 private int
1044 cmpstrp(const void *p1, const void *p2)
1045 {
1046         return strcmp(*(char *const *)p1, *(char *const *)p2);
1047 }
1048 
1049 
1050 private uint32_t
1051 set_text_binary(struct magic_set *ms, struct magic_entry *me, uint32_t nme,
1052     uint32_t starttest)
1053 {
1054         static const char text[] = "text";
1055         static const char binary[] = "binary";
1056         static const size_t len = sizeof(text);
1057 
1058         uint32_t i = starttest;
1059 
1060         do {
1061                 set_test_type(me[starttest].mp, me[i].mp);
1062                 if ((ms->flags & MAGIC_DEBUG) == 0)
1063                         continue;
1064                 (void)fprintf(stderr, "%s%s%s: %s\n",
1065                     me[i].mp->mimetype,
1066                     me[i].mp->mimetype[0] == '\0' ? "" : "; ",
1067                     me[i].mp->desc[0] ? me[i].mp->desc : "(no description)",
1068                     me[i].mp->flag & BINTEST ? binary : text);
1069                 if (me[i].mp->flag & BINTEST) {
1070                         char *p = strstr(me[i].mp->desc, text);
1071                         if (p && (p == me[i].mp->desc ||
1072                             isspace((unsigned char)p[-1])) &&
1073                             (p + len - me[i].mp->desc == MAXstring
1074                             || (p[len] == '\0' ||
1075                             isspace((unsigned char)p[len]))))
1076                                 (void)fprintf(stderr, "*** Possible "
1077                                     "binary test for text type\n");
1078                 }
1079         } while (++i < nme && me[i].mp->cont_level != 0);
1080         return i;
1081 }
1082 
1083 private void
1084 set_last_default(struct magic_set *ms, struct magic_entry *me, uint32_t nme)
1085 {
1086         uint32_t i;
1087         for (i = 0; i < nme; i++) {
1088                 if (me[i].mp->cont_level == 0 &&
1089                     me[i].mp->type == FILE_DEFAULT) {
1090                         while (++i < nme)
1091                                 if (me[i].mp->cont_level == 0)
1092                                         break;
1093                         if (i != nme) {
1094                                 /* XXX - Ugh! */
1095                                 ms->line = me[i].mp->lineno;
1096                                 file_magwarn(ms,
1097                                     "level 0 \"default\" did not sort last");
1098                         }
1099                         return;                                     
1100                 }
1101         }
1102 }
1103 
1104 private int
1105 coalesce_entries(struct magic_set *ms, struct magic_entry *me, uint32_t nme,
1106     struct magic **ma, uint32_t *nma)
1107 {
1108         uint32_t i, mentrycount = 0;
1109         size_t slen;
1110 
1111         for (i = 0; i < nme; i++)
1112                 mentrycount += me[i].cont_count;
1113 
1114         slen = sizeof(**ma) * mentrycount;
1115         if ((*ma = CAST(struct magic *, emalloc(slen))) == NULL) {
1116                 file_oomem(ms, slen);
1117                 return -1;
1118         }
1119 
1120         mentrycount = 0;
1121         for (i = 0; i < nme; i++) {
1122                 (void)memcpy(*ma + mentrycount, me[i].mp,
1123                     me[i].cont_count * sizeof(**ma));
1124                 mentrycount += me[i].cont_count;
1125         }
1126         *nma = mentrycount;
1127         return 0;
1128 }
1129 
1130 private void
1131 magic_entry_free(struct magic_entry *me, uint32_t nme)
1132 {
1133         uint32_t i;
1134         if (me == NULL)
1135                 return;
1136         for (i = 0; i < nme; i++)
1137                 efree(me[i].mp);
1138         efree(me);
1139 }
1140 
1141 private struct magic_map *
1142 apprentice_load(struct magic_set *ms, const char *fn, int action)
1143 {
1144         int errs = 0;
1145         uint32_t i, j;
1146         size_t files = 0, maxfiles = 0;
1147         char **filearr = NULL;
1148         struct stat st;
1149         struct magic_map *map;
1150         struct magic_entry_set mset[MAGIC_SETS];
1151         php_stream *dir;
1152         php_stream_dirent d;
1153  
1154         TSRMLS_FETCH();
1155 
1156         memset(mset, 0, sizeof(mset));
1157         ms->flags |= MAGIC_CHECK;       /* Enable checks for parsed files */
1158 
1159 
1160         if ((map = CAST(struct magic_map *, ecalloc(1, sizeof(*map)))) == NULL)
1161         {
1162                 file_oomem(ms, sizeof(*map));
1163                 return NULL;
1164         }
1165 
1166         /* print silly verbose header for USG compat. */
1167         if (action == FILE_CHECK)
1168                 (void)fprintf(stderr, "%s\n", usg_hdr);
1169 
1170         /* load directory or file */
1171         /* FIXME: Read file names and sort them to prevent
1172            non-determinism. See Debian bug #488562. */
1173         if (php_sys_stat(fn, &st) == 0 && S_ISDIR(st.st_mode)) {
1174                 int mflen;
1175                 char mfn[MAXPATHLEN];
1176 
1177                 dir = php_stream_opendir((char *)fn, REPORT_ERRORS, NULL);
1178                 if (!dir) {
1179                         errs++;
1180                         goto out;
1181                 }
1182                 while (php_stream_readdir(dir, &d)) {
1183                         if ((mflen = snprintf(mfn, sizeof(mfn), "%s/%s", fn, d.d_name)) < 0) {
1184                                 file_oomem(ms,
1185                                 strlen(fn) + strlen(d.d_name) + 2);
1186                                 errs++;
1187                                 php_stream_closedir(dir);
1188                                 goto out;
1189                         }
1190                         if (stat(mfn, &st) == -1 || !S_ISREG(st.st_mode)) {
1191                                 continue;
1192                         }
1193                         if (files >= maxfiles) {
1194                                 size_t mlen;
1195                                 maxfiles = (maxfiles + 1) * 2;
1196                                 mlen = maxfiles * sizeof(*filearr);
1197                                 if ((filearr = CAST(char **,
1198                                     erealloc(filearr, mlen))) == NULL) {
1199                                         file_oomem(ms, mlen);
1200                                         php_stream_closedir(dir);
1201                                         errs++;
1202                                         goto out;
1203                                 }
1204                         }
1205                         filearr[files++] = estrndup(mfn, (mflen > sizeof(mfn) - 1)? sizeof(mfn) - 1: mflen);
1206                 }
1207                 php_stream_closedir(dir);
1208                 qsort(filearr, files, sizeof(*filearr), cmpstrp);
1209                 for (i = 0; i < files; i++) {
1210                         load_1(ms, action, filearr[i], &errs, mset);
1211                         efree(filearr[i]);
1212                 }
1213                 efree(filearr);
1214         } else
1215                 load_1(ms, action, fn, &errs, mset);
1216         if (errs)
1217                 goto out;
1218 
1219         for (j = 0; j < MAGIC_SETS; j++) {
1220                 /* Set types of tests */
1221                 for (i = 0; i < mset[j].count; ) {
1222                         if (mset[j].me[i].mp->cont_level != 0) {
1223                                 i++;
1224                                 continue;
1225                         }
1226                         i = set_text_binary(ms, mset[j].me, mset[j].count, i);
1227                 }
1228                 qsort(mset[j].me, mset[j].count, sizeof(*mset[j].me),
1229                     apprentice_sort);
1230 
1231                 /*
1232                  * Make sure that any level 0 "default" line is last
1233                  * (if one exists).
1234                  */
1235                 set_last_default(ms, mset[j].me, mset[j].count);
1236 
1237                 /* coalesce per file arrays into a single one */
1238                 if (coalesce_entries(ms, mset[j].me, mset[j].count,
1239                     &map->magic[j], &map->nmagic[j]) == -1) {
1240                         errs++;
1241                         goto out;
1242                 }
1243         }
1244 
1245 out:
1246         for (j = 0; j < MAGIC_SETS; j++)
1247                 magic_entry_free(mset[j].me, mset[j].count);
1248 
1249         if (errs) {
1250                 for (j = 0; j < MAGIC_SETS; j++) {
1251                         if (map->magic[j])
1252                                 efree(map->magic[j]);
1253                 }
1254                 efree(map);
1255                 return NULL;
1256         }
1257         return map;
1258 }
1259 
1260 /*
1261  * extend the sign bit if the comparison is to be signed
1262  */
1263 protected uint64_t
1264 file_signextend(struct magic_set *ms, struct magic *m, uint64_t v)
1265 {
1266         if (!(m->flag & UNSIGNED)) {
1267                 switch(m->type) {
1268                 /*
1269                  * Do not remove the casts below.  They are
1270                  * vital.  When later compared with the data,
1271                  * the sign extension must have happened.
1272                  */
1273                 case FILE_BYTE:
1274                         v = (signed char) v;
1275                         break;
1276                 case FILE_SHORT:
1277                 case FILE_BESHORT:
1278                 case FILE_LESHORT:
1279                         v = (short) v;
1280                         break;
1281                 case FILE_DATE:
1282                 case FILE_BEDATE:
1283                 case FILE_LEDATE:
1284                 case FILE_MEDATE:
1285                 case FILE_LDATE:
1286                 case FILE_BELDATE:
1287                 case FILE_LELDATE:
1288                 case FILE_MELDATE:
1289                 case FILE_LONG:
1290                 case FILE_BELONG:
1291                 case FILE_LELONG:
1292                 case FILE_MELONG:
1293                 case FILE_FLOAT:
1294                 case FILE_BEFLOAT:
1295                 case FILE_LEFLOAT:
1296                         v = (int32_t) v;
1297                         break;
1298                 case FILE_QUAD:
1299                 case FILE_BEQUAD:
1300                 case FILE_LEQUAD:
1301                 case FILE_QDATE:
1302                 case FILE_QLDATE:
1303                 case FILE_QWDATE:
1304                 case FILE_BEQDATE:
1305                 case FILE_BEQLDATE:
1306                 case FILE_BEQWDATE:
1307                 case FILE_LEQDATE:
1308                 case FILE_LEQLDATE:
1309                 case FILE_LEQWDATE:
1310                 case FILE_DOUBLE:
1311                 case FILE_BEDOUBLE:
1312                 case FILE_LEDOUBLE:
1313                         v = (int64_t) v;
1314                         break;
1315                 case FILE_STRING:
1316                 case FILE_PSTRING:
1317                 case FILE_BESTRING16:
1318                 case FILE_LESTRING16:
1319                 case FILE_REGEX:
1320                 case FILE_SEARCH:
1321                 case FILE_DEFAULT:
1322                 case FILE_INDIRECT:
1323                 case FILE_NAME:
1324                 case FILE_USE:
1325                 case FILE_CLEAR:
1326                         break;
1327                 default:
1328                         if (ms->flags & MAGIC_CHECK)
1329                             file_magwarn(ms, "cannot happen: m->type=%d\n",
1330                                     m->type);
1331                         return ~0U;
1332                 }
1333         }
1334         return v;
1335 }
1336 
1337 private int
1338 string_modifier_check(struct magic_set *ms, struct magic *m)
1339 {
1340         if ((ms->flags & MAGIC_CHECK) == 0)
1341                 return 0;
1342 
1343         if (m->type != FILE_PSTRING && (m->str_flags & PSTRING_LEN) != 0) {
1344                 file_magwarn(ms,
1345                     "'/BHhLl' modifiers are only allowed for pascal strings\n");
1346                 return -1;
1347         }
1348         switch (m->type) {
1349         case FILE_BESTRING16:
1350         case FILE_LESTRING16:
1351                 if (m->str_flags != 0) {
1352                         file_magwarn(ms,
1353                             "no modifiers allowed for 16-bit strings\n");
1354                         return -1;
1355                 }
1356                 break;
1357         case FILE_STRING:
1358         case FILE_PSTRING:
1359                 if ((m->str_flags & REGEX_OFFSET_START) != 0) {
1360                         file_magwarn(ms,
1361                             "'/%c' only allowed on regex and search\n",
1362                             CHAR_REGEX_OFFSET_START);
1363                         return -1;
1364                 }
1365                 break;
1366         case FILE_SEARCH:
1367                 if (m->str_range == 0) {
1368                         file_magwarn(ms,
1369                             "missing range; defaulting to %d\n",
1370                             STRING_DEFAULT_RANGE);
1371                         m->str_range = STRING_DEFAULT_RANGE;
1372                         return -1;
1373                 }
1374                 break;
1375         case FILE_REGEX:
1376                 if ((m->str_flags & STRING_COMPACT_WHITESPACE) != 0) {
1377                         file_magwarn(ms, "'/%c' not allowed on regex\n",
1378                             CHAR_COMPACT_WHITESPACE);
1379                         return -1;
1380                 }
1381                 if ((m->str_flags & STRING_COMPACT_OPTIONAL_WHITESPACE) != 0) {
1382                         file_magwarn(ms, "'/%c' not allowed on regex\n",
1383                             CHAR_COMPACT_OPTIONAL_WHITESPACE);
1384                         return -1;
1385                 }
1386                 break;
1387         default:
1388                 file_magwarn(ms, "coding error: m->type=%d\n",
1389                     m->type);
1390                 return -1;
1391         }
1392         return 0;
1393 }
1394 
1395 private int
1396 get_op(char c)
1397 {
1398         switch (c) {
1399         case '&':
1400                 return FILE_OPAND;
1401         case '|':
1402                 return FILE_OPOR;
1403         case '^':
1404                 return FILE_OPXOR;
1405         case '+':
1406                 return FILE_OPADD;
1407         case '-':
1408                 return FILE_OPMINUS;
1409         case '*':
1410                 return FILE_OPMULTIPLY;
1411         case '/':
1412                 return FILE_OPDIVIDE;
1413         case '%':
1414                 return FILE_OPMODULO;
1415         default:
1416                 return -1;
1417         }
1418 }
1419 
1420 #ifdef ENABLE_CONDITIONALS
1421 private int
1422 get_cond(const char *l, const char **t)
1423 {
1424         static const struct cond_tbl_s {
1425                 char name[8];
1426                 size_t len;
1427                 int cond;
1428         } cond_tbl[] = {
1429                 { "if",         2,      COND_IF },
1430                 { "elif",       4,      COND_ELIF },
1431                 { "else",       4,      COND_ELSE },
1432                 { "",           0,      COND_NONE },
1433         };
1434         const struct cond_tbl_s *p;
1435 
1436         for (p = cond_tbl; p->len; p++) {
1437                 if (strncmp(l, p->name, p->len) == 0 &&
1438                     isspace((unsigned char)l[p->len])) {
1439                         if (t)
1440                                 *t = l + p->len;
1441                         break;
1442                 }
1443         }
1444         return p->cond;
1445 }
1446 
1447 private int
1448 check_cond(struct magic_set *ms, int cond, uint32_t cont_level)
1449 {
1450         int last_cond;
1451         last_cond = ms->c.li[cont_level].last_cond;
1452 
1453         switch (cond) {
1454         case COND_IF:
1455                 if (last_cond != COND_NONE && last_cond != COND_ELIF) {
1456                         if (ms->flags & MAGIC_CHECK)
1457                                 file_magwarn(ms, "syntax error: `if'");
1458                         return -1;
1459                 }
1460                 last_cond = COND_IF;
1461                 break;
1462 
1463         case COND_ELIF:
1464                 if (last_cond != COND_IF && last_cond != COND_ELIF) {
1465                         if (ms->flags & MAGIC_CHECK)
1466                                 file_magwarn(ms, "syntax error: `elif'");
1467                         return -1;
1468                 }
1469                 last_cond = COND_ELIF;
1470                 break;
1471 
1472         case COND_ELSE:
1473                 if (last_cond != COND_IF && last_cond != COND_ELIF) {
1474                         if (ms->flags & MAGIC_CHECK)
1475                                 file_magwarn(ms, "syntax error: `else'");
1476                         return -1;
1477                 }
1478                 last_cond = COND_NONE;
1479                 break;
1480 
1481         case COND_NONE:
1482                 last_cond = COND_NONE;
1483                 break;
1484         }
1485 
1486         ms->c.li[cont_level].last_cond = last_cond;
1487         return 0;
1488 }
1489 #endif /* ENABLE_CONDITIONALS */
1490 
1491 /*
1492  * parse one line from magic file, put into magic[index++] if valid
1493  */
1494 private int
1495 parse(struct magic_set *ms, struct magic_entry *me, const char *line,
1496     size_t lineno, int action)
1497 {
1498 #ifdef ENABLE_CONDITIONALS
1499         static uint32_t last_cont_level = 0;
1500 #endif
1501         size_t i;
1502         struct magic *m;
1503         const char *l = line;
1504         char *t;
1505         int op;
1506         uint32_t cont_level;
1507         int32_t diff;
1508 
1509         cont_level = 0;
1510 
1511         /*
1512          * Parse the offset.
1513          */
1514         while (*l == '>') {
1515                 ++l;            /* step over */
1516                 cont_level++; 
1517         }
1518 #ifdef ENABLE_CONDITIONALS
1519         if (cont_level == 0 || cont_level > last_cont_level)
1520                 if (file_check_mem(ms, cont_level) == -1)
1521                         return -1;
1522         last_cont_level = cont_level;
1523 #endif
1524         if (cont_level != 0) {
1525                 if (me->mp == NULL) {
1526                         file_magerror(ms, "No current entry for continuation");
1527                         return -1;
1528                 }
1529                 if (me->cont_count == 0) {
1530                         file_magerror(ms, "Continuations present with 0 count");
1531                         return -1;
1532                 }
1533                 m = &me->mp[me->cont_count - 1];
1534                 diff = (int32_t)cont_level - (int32_t)m->cont_level;
1535                 if (diff > 1)
1536                         file_magwarn(ms, "New continuation level %u is more "
1537                             "than one larger than current level %u", cont_level,
1538                             m->cont_level);
1539                 if (me->cont_count == me->max_count) {
1540                         struct magic *nm;
1541                         size_t cnt = me->max_count + ALLOC_CHUNK;
1542                         if ((nm = CAST(struct magic *, erealloc(me->mp,
1543                             sizeof(*nm) * cnt))) == NULL) {
1544                                 file_oomem(ms, sizeof(*nm) * cnt);
1545                                 return -1;
1546                         }
1547                         me->mp = m = nm;
1548                         me->max_count = CAST(uint32_t, cnt);
1549                 }
1550                 m = &me->mp[me->cont_count++];
1551                 (void)memset(m, 0, sizeof(*m));
1552                 m->cont_level = cont_level;
1553         } else {
1554                 static const size_t len = sizeof(*m) * ALLOC_CHUNK;
1555                 if (me->mp != NULL)
1556                         return 1;
1557                 if ((m = CAST(struct magic *, emalloc(len))) == NULL) {
1558                         file_oomem(ms, len);
1559                         return -1;
1560                 }
1561                 me->mp = m;
1562                 me->max_count = ALLOC_CHUNK;
1563                 (void)memset(m, 0, sizeof(*m));
1564                 m->factor_op = FILE_FACTOR_OP_NONE;
1565                 m->cont_level = 0;
1566                 me->cont_count = 1;
1567         }
1568         m->lineno = CAST(uint32_t, lineno);
1569 
1570         if (*l == '&') {  /* m->cont_level == 0 checked below. */
1571                 ++l;            /* step over */
1572                 m->flag |= OFFADD;
1573         }
1574         if (*l == '(') {
1575                 ++l;            /* step over */
1576                 m->flag |= INDIR;
1577                 if (m->flag & OFFADD)
1578                         m->flag = (m->flag & ~OFFADD) | INDIROFFADD;
1579 
1580                 if (*l == '&') {  /* m->cont_level == 0 checked below */
1581                         ++l;            /* step over */
1582                         m->flag |= OFFADD;
1583                 }
1584         }
1585         /* Indirect offsets are not valid at level 0. */
1586         if (m->cont_level == 0 && (m->flag & (OFFADD | INDIROFFADD)))
1587                 if (ms->flags & MAGIC_CHECK)
1588                         file_magwarn(ms, "relative offset at level 0");
1589 
1590         /* get offset, then skip over it */
1591         m->offset = (uint32_t)strtoul(l, &t, 0);
1592         if (l == t)
1593                 if (ms->flags & MAGIC_CHECK)
1594                         file_magwarn(ms, "offset `%s' invalid", l);
1595         l = t;
1596 
1597         if (m->flag & INDIR) {
1598                 m->in_type = FILE_LONG;
1599                 m->in_offset = 0;
1600                 /*
1601                  * read [.lbs][+-]nnnnn)
1602                  */
1603                 if (*l == '.') {
1604                         l++;
1605                         switch (*l) {
1606                         case 'l':
1607                                 m->in_type = FILE_LELONG;
1608                                 break;
1609                         case 'L':
1610                                 m->in_type = FILE_BELONG;
1611                                 break;
1612                         case 'm':
1613                                 m->in_type = FILE_MELONG;
1614                                 break;
1615                         case 'h':
1616                         case 's':
1617                                 m->in_type = FILE_LESHORT;
1618                                 break;
1619                         case 'H':
1620                         case 'S':
1621                                 m->in_type = FILE_BESHORT;
1622                                 break;
1623                         case 'c':
1624                         case 'b':
1625                         case 'C':
1626                         case 'B':
1627                                 m->in_type = FILE_BYTE;
1628                                 break;
1629                         case 'e':
1630                         case 'f':
1631                         case 'g':
1632                                 m->in_type = FILE_LEDOUBLE;
1633                                 break;
1634                         case 'E':
1635                         case 'F':
1636                         case 'G':
1637                                 m->in_type = FILE_BEDOUBLE;
1638                                 break;
1639                         case 'i':
1640                                 m->in_type = FILE_LEID3;
1641                                 break;
1642                         case 'I':
1643                                 m->in_type = FILE_BEID3;
1644                                 break;
1645                         default:
1646                                 if (ms->flags & MAGIC_CHECK)
1647                                         file_magwarn(ms,
1648                                             "indirect offset type `%c' invalid",
1649                                             *l);
1650                                 break;
1651                         }
1652                         l++;
1653                 }
1654 
1655                 m->in_op = 0;
1656                 if (*l == '~') {
1657                         m->in_op |= FILE_OPINVERSE;
1658                         l++;
1659                 }
1660                 if ((op = get_op(*l)) != -1) {
1661                         m->in_op |= op;
1662                         l++;
1663                 }
1664                 if (*l == '(') {
1665                         m->in_op |= FILE_OPINDIRECT;
1666                         l++;
1667                 }
1668                 if (isdigit((unsigned char)*l) || *l == '-') {
1669                         m->in_offset = (int32_t)strtol(l, &t, 0);
1670                         if (l == t)
1671                                 if (ms->flags & MAGIC_CHECK)
1672                                         file_magwarn(ms,
1673                                             "in_offset `%s' invalid", l);
1674                         l = t;
1675                 }
1676                 if (*l++ != ')' || 
1677                     ((m->in_op & FILE_OPINDIRECT) && *l++ != ')'))
1678                         if (ms->flags & MAGIC_CHECK)
1679                                 file_magwarn(ms,
1680                                     "missing ')' in indirect offset");
1681         }
1682         EATAB;
1683 
1684 #ifdef ENABLE_CONDITIONALS
1685         m->cond = get_cond(l, &l);
1686         if (check_cond(ms, m->cond, cont_level) == -1)
1687                 return -1;
1688 
1689         EATAB;
1690 #endif
1691 
1692         /*
1693          * Parse the type.
1694          */
1695         if (*l == 'u') {
1696                 /*
1697                  * Try it as a keyword type prefixed by "u"; match what
1698                  * follows the "u".  If that fails, try it as an SUS
1699                  * integer type. 
1700                  */
1701                 m->type = get_type(type_tbl, l + 1, &l);
1702                 if (m->type == FILE_INVALID) {
1703                         /*
1704                          * Not a keyword type; parse it as an SUS type,
1705                          * 'u' possibly followed by a number or C/S/L.
1706                          */
1707                         m->type = get_standard_integer_type(l, &l);
1708                 }
1709                 // It's unsigned.
1710                 if (m->type != FILE_INVALID)
1711                         m->flag |= UNSIGNED;
1712         } else {
1713                 /*
1714                  * Try it as a keyword type.  If that fails, try it as
1715                  * an SUS integer type if it begins with "d" or as an
1716                  * SUS string type if it begins with "s".  In any case,
1717                  * it's not unsigned.
1718                  */
1719                 m->type = get_type(type_tbl, l, &l);
1720                 if (m->type == FILE_INVALID) {
1721                         /*
1722                          * Not a keyword type; parse it as an SUS type,
1723                          * either 'd' possibly followed by a number or
1724                          * C/S/L, or just 's'.
1725                          */
1726                         if (*l == 'd')
1727                                 m->type = get_standard_integer_type(l, &l);
1728                         else if (*l == 's' && !isalpha((unsigned char)l[1])) {
1729                                 m->type = FILE_STRING;
1730                 ++l;
1731                         }
1732                 }
1733         }
1734 
1735         if (m->type == FILE_INVALID) {
1736                 /* Not found - try it as a special keyword. */
1737                 m->type = get_type(special_tbl, l, &l);
1738         }
1739                         
1740         if (m->type == FILE_INVALID) {
1741                 if (ms->flags & MAGIC_CHECK)
1742                         file_magwarn(ms, "type `%s' invalid", l);
1743                 /*if (me->mp) {
1744                         efree(me->mp);
1745                         me->mp = NULL;
1746                 }*/
1747                 return -1;
1748         }
1749 
1750         /* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */
1751         /* New and improved: ~ & | ^ + - * / % -- exciting, isn't it? */
1752 
1753         m->mask_op = 0;
1754         if (*l == '~') {
1755                 if (!IS_LIBMAGIC_STRING(m->type))
1756                         m->mask_op |= FILE_OPINVERSE;
1757                 else if (ms->flags & MAGIC_CHECK)
1758                         file_magwarn(ms, "'~' invalid for string types");
1759                 ++l;
1760         }
1761         m->str_range = 0;
1762         m->str_flags = m->type == FILE_PSTRING ? PSTRING_1_LE : 0;
1763         if ((op = get_op(*l)) != -1) {
1764                 if (!IS_LIBMAGIC_STRING(m->type)) {
1765                         uint64_t val;
1766                         ++l;
1767                         m->mask_op |= op;
1768                         val = (uint64_t)strtoull(l, &t, 0);
1769                         l = t;
1770                         m->num_mask = file_signextend(ms, m, val);
1771                         eatsize(&l);
1772                 }
1773                 else if (op == FILE_OPDIVIDE) {
1774                         int have_range = 0;
1775                         while (!isspace((unsigned char)*++l)) {
1776                                 switch (*l) {
1777                                 case '0':  case '1':  case '2':
1778                                 case '3':  case '4':  case '5':
1779                                 case '6':  case '7':  case '8':
1780                                 case '9':
1781                                         if (have_range &&
1782                                             (ms->flags & MAGIC_CHECK))
1783                                                 file_magwarn(ms,
1784                                                     "multiple ranges");
1785                                         have_range = 1;
1786                                         m->str_range = CAST(uint32_t,
1787                                             strtoul(l, &t, 0));
1788                                         if (m->str_range == 0)
1789                                                 file_magwarn(ms,
1790                                                     "zero range");
1791                                         l = t - 1;
1792                                         break;
1793                                 case CHAR_COMPACT_WHITESPACE:
1794                                         m->str_flags |=
1795                                             STRING_COMPACT_WHITESPACE;
1796                                         break;
1797                                 case CHAR_COMPACT_OPTIONAL_WHITESPACE:
1798                                         m->str_flags |=
1799                                             STRING_COMPACT_OPTIONAL_WHITESPACE;
1800                                         break;
1801                                 case CHAR_IGNORE_LOWERCASE:
1802                                         m->str_flags |= STRING_IGNORE_LOWERCASE;
1803                                         break;
1804                                 case CHAR_IGNORE_UPPERCASE:
1805                                         m->str_flags |= STRING_IGNORE_UPPERCASE;
1806                                         break;
1807                                 case CHAR_REGEX_OFFSET_START:
1808                                         m->str_flags |= REGEX_OFFSET_START;
1809                                         break;
1810                                 case CHAR_BINTEST:
1811                                         m->str_flags |= STRING_BINTEST;
1812                                         break;
1813                                 case CHAR_TEXTTEST:
1814                                         m->str_flags |= STRING_TEXTTEST;
1815                                         break;
1816                                 case CHAR_TRIM:
1817                                         m->str_flags |= STRING_TRIM;
1818                                         break;
1819                                 case CHAR_PSTRING_1_LE:
1820                                         if (m->type != FILE_PSTRING)
1821                                                 goto bad;
1822                                         m->str_flags = (m->str_flags & ~PSTRING_LEN) | PSTRING_1_LE;
1823                                         break;
1824                                 case CHAR_PSTRING_2_BE:
1825                                         if (m->type != FILE_PSTRING)
1826                                                 goto bad;
1827                                         m->str_flags = (m->str_flags & ~PSTRING_LEN) | PSTRING_2_BE;
1828                                         break;
1829                                 case CHAR_PSTRING_2_LE:
1830                                         if (m->type != FILE_PSTRING)
1831                                                 goto bad;
1832                                         m->str_flags = (m->str_flags & ~PSTRING_LEN) | PSTRING_2_LE;
1833                                         break;
1834                                 case CHAR_PSTRING_4_BE:
1835                                         if (m->type != FILE_PSTRING)
1836                                                 goto bad;
1837                                         m->str_flags = (m->str_flags & ~PSTRING_LEN) | PSTRING_4_BE;
1838                                         break;
1839                                 case CHAR_PSTRING_4_LE:
1840                                         if (m->type != FILE_PSTRING)
1841                                                 goto bad;
1842                                         m->str_flags = (m->str_flags & ~PSTRING_LEN) | PSTRING_4_LE;
1843                                         break;
1844                                 case CHAR_PSTRING_LENGTH_INCLUDES_ITSELF:
1845                                         if (m->type != FILE_PSTRING)
1846                                                 goto bad;
1847                                         m->str_flags |= PSTRING_LENGTH_INCLUDES_ITSELF;
1848                                         break;
1849                                 default:
1850                                 bad:
1851                                         if (ms->flags & MAGIC_CHECK)
1852                                                 file_magwarn(ms,
1853                                                     "string extension `%c' "
1854                                                     "invalid", *l);
1855                                         return -1;
1856                                 }
1857                                 /* allow multiple '/' for readability */
1858                                 if (l[1] == '/' &&
1859                                     !isspace((unsigned char)l[2]))
1860                                         l++;
1861                         }
1862                         if (string_modifier_check(ms, m) == -1)
1863                                 return -1;
1864                 }
1865                 else {
1866                         if (ms->flags & MAGIC_CHECK)
1867                                 file_magwarn(ms, "invalid string op: %c", *t);
1868                         return -1;
1869                 }
1870         }
1871         /*
1872          * We used to set mask to all 1's here, instead let's just not do
1873          * anything if mask = 0 (unless you have a better idea)
1874          */
1875         EATAB;
1876   
1877         switch (*l) {
1878         case '>':
1879         case '<':
1880                 m->reln = *l;
1881                 ++l;
1882                 if (*l == '=') {
1883                         if (ms->flags & MAGIC_CHECK) {
1884                                 file_magwarn(ms, "%c= not supported",
1885                                     m->reln);
1886                                 return -1;
1887                         }
1888                    ++l;
1889                 }
1890                 break;
1891         /* Old-style anding: "0 byte &0x80 dynamically linked" */
1892         case '&':
1893         case '^':
1894         case '=':
1895                 m->reln = *l;
1896                 ++l;
1897                 if (*l == '=') {
1898                    /* HP compat: ignore &= etc. */
1899                    ++l;
1900                 }
1901                 break;
1902         case '!':
1903                 m->reln = *l;
1904                 ++l;
1905                 break;
1906         default:
1907                 m->reln = '=';  /* the default relation */
1908                 if (*l == 'x' && ((isascii((unsigned char)l[1]) && 
1909                     isspace((unsigned char)l[1])) || !l[1])) {
1910                         m->reln = *l;
1911                         ++l;
1912                 }
1913                 break;
1914         }
1915         /*
1916          * Grab the value part, except for an 'x' reln.
1917          */
1918         if (m->reln != 'x' && getvalue(ms, m, &l, action))
1919                 return -1;
1920 
1921         /*
1922          * TODO finish this macro and start using it!
1923          * #define offsetcheck {if (offset > HOWMANY-1) 
1924          *      magwarn("offset too big"); }
1925          */
1926 
1927         /*
1928          * Now get last part - the description
1929          */
1930         EATAB;
1931         if (l[0] == '\b') {
1932                 ++l;
1933                 m->flag |= NOSPACE;
1934         } else if ((l[0] == '\\') && (l[1] == 'b')) {
1935                 ++l;
1936                 ++l;
1937                 m->flag |= NOSPACE;
1938         }
1939         for (i = 0; (m->desc[i++] = *l++) != '\0' && i < sizeof(m->desc); )
1940                 continue;
1941         if (i == sizeof(m->desc)) {
1942                 m->desc[sizeof(m->desc) - 1] = '\0';
1943                 if (ms->flags & MAGIC_CHECK)
1944                         file_magwarn(ms, "description `%s' truncated", m->desc);
1945         }
1946 
1947         /*
1948          * We only do this check while compiling, or if any of the magic
1949          * files were not compiled.
1950          */
1951         if (ms->flags & MAGIC_CHECK) {
1952                 if (check_format(ms, m) == -1)
1953                         return -1;
1954         }
1955         m->mimetype[0] = '\0';          /* initialise MIME type to none */
1956         return 0;
1957 }
1958 
1959 /*
1960  * parse a STRENGTH annotation line from magic file, put into magic[index - 1]
1961  * if valid
1962  */
1963 private int
1964 parse_strength(struct magic_set *ms, struct magic_entry *me, const char *line)
1965 {
1966         const char *l = line;
1967         char *el;
1968         unsigned long factor;
1969         struct magic *m = &me->mp[0];
1970 
1971         if (m->factor_op != FILE_FACTOR_OP_NONE) {
1972                 file_magwarn(ms,
1973                     "Current entry already has a strength type: %c %d",
1974                     m->factor_op, m->factor);
1975                 return -1;
1976         }
1977         if (m->type == FILE_NAME) {
1978                 file_magwarn(ms, "%s: Strength setting is not supported in "
1979                     "\"name\" magic entries", m->value.s);
1980                 return -1;
1981         }
1982         EATAB;
1983         switch (*l) {
1984         case FILE_FACTOR_OP_NONE:
1985         case FILE_FACTOR_OP_PLUS:
1986         case FILE_FACTOR_OP_MINUS:
1987         case FILE_FACTOR_OP_TIMES:
1988         case FILE_FACTOR_OP_DIV:
1989                 m->factor_op = *l++;
1990                 break;
1991         default:
1992                 file_magwarn(ms, "Unknown factor op `%c'", *l);
1993                 return -1;
1994         }
1995         EATAB;
1996         factor = strtoul(l, &el, 0);
1997         if (factor > 255) {
1998                 file_magwarn(ms, "Too large factor `%lu'", factor);
1999                 goto out;
2000         }
2001         if (*el && !isspace((unsigned char)*el)) {
2002                 file_magwarn(ms, "Bad factor `%s'", l);
2003                 goto out;
2004         }
2005         m->factor = (uint8_t)factor;
2006         if (m->factor == 0 && m->factor_op == FILE_FACTOR_OP_DIV) {
2007                 file_magwarn(ms, "Cannot have factor op `%c' and factor %u",
2008                     m->factor_op, m->factor);
2009                 goto out;
2010         }
2011         return 0;
2012 out:
2013         m->factor_op = FILE_FACTOR_OP_NONE;
2014         m->factor = 0;
2015         return -1;
2016 }
2017 
2018 /*
2019  * Parse an Apple CREATOR/TYPE annotation from magic file and put it into
2020  * magic[index - 1]
2021  */
2022 private int
2023 parse_apple(struct magic_set *ms, struct magic_entry *me, const char *line)
2024 {
2025         size_t i;
2026         const char *l = line;
2027         struct magic *m = &me->mp[me->cont_count == 0 ? 0 : me->cont_count - 1];
2028 
2029         if (m->apple[0] != '\0') {
2030                 file_magwarn(ms, "Current entry already has a APPLE type "
2031                     "`%.8s', new type `%s'", m->mimetype, l);
2032                 return -1;
2033         }       
2034 
2035         EATAB;
2036         for (i = 0; *l && ((isascii((unsigned char)*l) &&
2037             isalnum((unsigned char)*l)) || strchr("-+/.", *l)) &&
2038             i < sizeof(m->apple); m->apple[i++] = *l++)
2039                 continue;
2040         if (i == sizeof(m->apple) && *l) {
2041                 /* We don't need to NUL terminate here, printing handles it */
2042                 if (ms->flags & MAGIC_CHECK)
2043                         file_magwarn(ms, "APPLE type `%s' truncated %"
2044                             SIZE_T_FORMAT "u", line, i);
2045         }
2046 
2047         if (i > 0)
2048                 return 0;
2049         else
2050                 return -1;
2051 }
2052 
2053 /*
2054  * parse a MIME annotation line from magic file, put into magic[index - 1]
2055  * if valid
2056  */
2057 private int
2058 parse_mime(struct magic_set *ms, struct magic_entry *me, const char *line)
2059 {
2060         size_t i;
2061         const char *l = line;
2062         struct magic *m = &me->mp[me->cont_count == 0 ? 0 : me->cont_count - 1];
2063 
2064         if (m->mimetype[0] != '\0') {
2065                 file_magwarn(ms, "Current entry already has a MIME type `%s',"
2066                     " new type `%s'", m->mimetype, l);
2067                 return -1;
2068         }       
2069 
2070         EATAB;
2071         for (i = 0; *l && ((isascii((unsigned char)*l) &&
2072             isalnum((unsigned char)*l)) || strchr("-+/.", *l)) &&
2073             i < sizeof(m->mimetype); m->mimetype[i++] = *l++)
2074                 continue;
2075         if (i == sizeof(m->mimetype)) {
2076                 m->mimetype[sizeof(m->mimetype) - 1] = '\0';
2077                 if (ms->flags & MAGIC_CHECK)
2078                         file_magwarn(ms, "MIME type `%s' truncated %"
2079                             SIZE_T_FORMAT "u", m->mimetype, i);
2080         } else
2081                 m->mimetype[i] = '\0';
2082 
2083         if (i > 0)
2084                 return 0;
2085         else
2086                 return -1;
2087 }
2088 
2089 private int
2090 check_format_type(const char *ptr, int type)
2091 {
2092         int quad = 0;
2093         if (*ptr == '\0') {
2094                 /* Missing format string; bad */
2095                 return -1;
2096         }
2097 
2098         switch (type) {
2099         case FILE_FMT_QUAD:
2100                 quad = 1;
2101                 /*FALLTHROUGH*/
2102         case FILE_FMT_NUM:
2103                 if (*ptr == '-')
2104                         ptr++;
2105                 if (*ptr == '.')
2106                         ptr++;
2107                 while (isdigit((unsigned char)*ptr)) ptr++;
2108                 if (*ptr == '.')
2109                         ptr++;
2110                 while (isdigit((unsigned char)*ptr)) ptr++;
2111                 if (quad) {
2112                         if (*ptr++ != 'l')
2113                                 return -1;
2114                         if (*ptr++ != 'l')
2115                                 return -1;
2116                 }
2117         
2118                 switch (*ptr++) {
2119                 case 'l':
2120                         switch (*ptr++) {
2121                         case 'i':
2122                         case 'd':
2123                         case 'u':
2124                         case 'o':
2125                         case 'x':
2126                         case 'X':
2127                                 return 0;
2128                         default:
2129                                 return -1;
2130                         }
2131                 
2132                 case 'h':
2133                         switch (*ptr++) {
2134                         case 'h':
2135                                 switch (*ptr++) {
2136                                 case 'i':
2137                                 case 'd':
2138                                 case 'u':
2139                                 case 'o':
2140                                 case 'x':
2141                                 case 'X':
2142                                         return 0;
2143                                 default:
2144                                         return -1;
2145                                 }
2146                         case 'd':
2147                                 return 0;
2148                         default:
2149                                 return -1;
2150                         }
2151 
2152                 case 'i':
2153                 case 'c':
2154                 case 'd':
2155                 case 'u':
2156                 case 'o':
2157                 case 'x':
2158                 case 'X':
2159                         return 0;
2160                         
2161                 default:
2162                         return -1;
2163                 }
2164                 
2165         case FILE_FMT_FLOAT:
2166         case FILE_FMT_DOUBLE:
2167                 if (*ptr == '-')
2168                         ptr++;
2169                 if (*ptr == '.')
2170                         ptr++;
2171                 while (isdigit((unsigned char)*ptr)) ptr++;
2172                 if (*ptr == '.')
2173                         ptr++;
2174                 while (isdigit((unsigned char)*ptr)) ptr++;
2175         
2176                 switch (*ptr++) {
2177                 case 'e':
2178                 case 'E':
2179                 case 'f':
2180                 case 'F':
2181                 case 'g':
2182                 case 'G':
2183                         return 0;
2184                         
2185                 default:
2186                         return -1;
2187                 }
2188                 
2189 
2190         case FILE_FMT_STR:
2191                 if (*ptr == '-')
2192                         ptr++;
2193                 while (isdigit((unsigned char )*ptr))
2194                         ptr++;
2195                 if (*ptr == '.') {
2196                         ptr++;
2197                         while (isdigit((unsigned char )*ptr))
2198                                 ptr++;
2199                 }
2200                 
2201                 switch (*ptr++) {
2202                 case 's':
2203                         return 0;
2204                 default:
2205                         return -1;
2206                 }
2207                 
2208         default:
2209                 /* internal error */
2210                 abort();
2211         }
2212         /*NOTREACHED*/
2213         return -1;
2214 }
2215         
2216 /*
2217  * Check that the optional printf format in description matches
2218  * the type of the magic.
2219  */
2220 private int
2221 check_format(struct magic_set *ms, struct magic *m)
2222 {
2223         char *ptr;
2224 
2225         for (ptr = m->desc; *ptr; ptr++)
2226                 if (*ptr == '%')
2227                         break;
2228         if (*ptr == '\0') {
2229                 /* No format string; ok */
2230                 return 1;
2231         }
2232 
2233         assert(file_nformats == file_nnames);
2234 
2235         if (m->type >= file_nformats) {
2236                 file_magwarn(ms, "Internal error inconsistency between "
2237                     "m->type and format strings");              
2238                 return -1;
2239         }
2240         if (file_formats[m->type] == FILE_FMT_NONE) {
2241                 file_magwarn(ms, "No format string for `%s' with description "
2242                     "`%s'", m->desc, file_names[m->type]);
2243                 return -1;
2244         }
2245 
2246         ptr++;
2247         if (check_format_type(ptr, file_formats[m->type]) == -1) {
2248                 /*
2249                  * TODO: this error message is unhelpful if the format
2250                  * string is not one character long
2251                  */
2252                 file_magwarn(ms, "Printf format `%c' is not valid for type "
2253                     "`%s' in description `%s'", *ptr ? *ptr : '?',
2254                     file_names[m->type], m->desc);
2255                 return -1;
2256         }
2257         
2258         for (; *ptr; ptr++) {
2259                 if (*ptr == '%') {
2260                         file_magwarn(ms,
2261                             "Too many format strings (should have at most one) "
2262                             "for `%s' with description `%s'",
2263                             file_names[m->type], m->desc);
2264                         return -1;
2265                 }
2266         }
2267         return 0;
2268 }
2269 
2270 /* 
2271  * Read a numeric value from a pointer, into the value union of a magic 
2272  * pointer, according to the magic type.  Update the string pointer to point 
2273  * just after the number read.  Return 0 for success, non-zero for failure.
2274  */
2275 private int
2276 getvalue(struct magic_set *ms, struct magic *m, const char **p, int action)
2277 {
2278         switch (m->type) {
2279         case FILE_BESTRING16:
2280         case FILE_LESTRING16:
2281         case FILE_STRING:
2282         case FILE_PSTRING:
2283         case FILE_REGEX:
2284         case FILE_SEARCH:
2285         case FILE_NAME:
2286         case FILE_USE:
2287                 *p = getstr(ms, m, *p, action == FILE_COMPILE);
2288                 if (*p == NULL) {
2289                         if (ms->flags & MAGIC_CHECK)
2290                                 file_magwarn(ms, "cannot get string from `%s'",
2291                                     m->value.s);
2292                         return -1;
2293                 }
2294                 return 0;
2295         case FILE_FLOAT:
2296         case FILE_BEFLOAT:
2297         case FILE_LEFLOAT:
2298                 if (m->reln != 'x') {
2299                         char *ep;
2300 #ifdef HAVE_STRTOF
2301                         m->value.f = strtof(*p, &ep);
2302 #else
2303                         m->value.f = (float)strtod(*p, &ep);
2304 #endif
2305                         *p = ep;
2306                 }
2307                 return 0;
2308         case FILE_DOUBLE:
2309         case FILE_BEDOUBLE:
2310         case FILE_LEDOUBLE:
2311                 if (m->reln != 'x') {
2312                         char *ep;
2313                         m->value.d = strtod(*p, &ep);
2314                         *p = ep;
2315                 }
2316                 return 0;
2317         default:
2318                 if (m->reln != 'x') {
2319                         char *ep;
2320                         m->value.q = file_signextend(ms, m,
2321                             (uint64_t)strtoull(*p, &ep, 0));
2322                         *p = ep;
2323                         eatsize(p);
2324                 }
2325                 return 0;
2326         }
2327 }
2328 
2329 /*
2330  * Convert a string containing C character escapes.  Stop at an unescaped
2331  * space or tab.
2332  * Copy the converted version to "m->value.s", and the length in m->vallen.
2333  * Return updated scan pointer as function result. Warn if set.
2334  */
2335 private const char *
2336 getstr(struct magic_set *ms, struct magic *m, const char *s, int warn)
2337 {
2338         const char *origs = s;
2339         char    *p = m->value.s;
2340         size_t  plen = sizeof(m->value.s);
2341         char    *origp = p;
2342         char    *pmax = p + plen - 1;
2343         int     c;
2344         int     val;
2345 
2346         while ((c = *s++) != '\0') {
2347                 if (isspace((unsigned char) c))
2348                         break;
2349                 if (p >= pmax) {
2350                         file_error(ms, 0, "string too long: `%s'", origs);
2351                         return NULL;
2352                 }
2353                 if (c == '\\') {
2354                         switch(c = *s++) {
2355 
2356                         case '\0':
2357                                 if (warn)
2358                                         file_magwarn(ms, "incomplete escape");
2359                                 goto out;
2360 
2361                         case '\t':
2362                                 if (warn) {
2363                                         file_magwarn(ms,
2364                                             "escaped tab found, use \\t instead");
2365                                         warn = 0;       /* already did */
2366                                 }
2367                                 /*FALLTHROUGH*/
2368                         default:
2369                                 if (warn) {
2370                                         if (isprint((unsigned char)c)) {
2371                                                 /* Allow escaping of 
2372                                                  * ``relations'' */
2373                                                 if (strchr("<>&^=!", c) == NULL
2374                                                     && (m->type != FILE_REGEX ||
2375                                                     strchr("[]().*?^$|{}", c)
2376                                                     == NULL)) {
2377                                                         file_magwarn(ms, "no "
2378                                                             "need to escape "
2379                                                             "`%c'", c);
2380                                                 }
2381                                         } else {
2382                                                 file_magwarn(ms,
2383                                                     "unknown escape sequence: "
2384                                                     "\\%03o", c);
2385                                         }
2386                                 }
2387                                 /*FALLTHROUGH*/
2388                         /* space, perhaps force people to use \040? */
2389                         case ' ':
2390 #if 0
2391                         /*
2392                          * Other things people escape, but shouldn't need to,
2393                          * so we disallow them
2394                          */
2395                         case '\'':
2396                         case '"':
2397                         case '?':
2398 #endif
2399                         /* Relations */
2400                         case '>':
2401                         case '<':
2402                         case '&':
2403                         case '^':
2404                         case '=':
2405                         case '!':
2406                         /* and baskslash itself */
2407                         case '\\':
2408                                 *p++ = (char) c;
2409                                 break;
2410 
2411                         case 'a':
2412                                 *p++ = '\a';
2413                                 break;
2414 
2415                         case 'b':
2416                                 *p++ = '\b';
2417                                 break;
2418 
2419                         case 'f':
2420                                 *p++ = '\f';
2421                                 break;
2422 
2423                         case 'n':
2424                                 *p++ = '\n';
2425                                 break;
2426 
2427                         case 'r':
2428                                 *p++ = '\r';
2429                                 break;
2430 
2431                         case 't':
2432                                 *p++ = '\t';
2433                                 break;
2434 
2435                         case 'v':
2436                                 *p++ = '\v';
2437                                 break;
2438 
2439                         /* \ and up to 3 octal digits */
2440                         case '0':
2441                         case '1':
2442                         case '2':
2443                         case '3':
2444                         case '4':
2445                         case '5':
2446                         case '6':
2447                         case '7':
2448                                 val = c - '0';
2449                                 c = *s++;  /* try for 2 */
2450                                 if (c >= '0' && c <= '7') {
2451                                         val = (val << 3) | (c - '0');
2452                                         c = *s++;  /* try for 3 */
2453                                         if (c >= '0' && c <= '7')
2454                                                 val = (val << 3) | (c-'0');
2455                                         else
2456                                                 --s;
2457                                 }
2458                                 else
2459                                         --s;
2460                                 *p++ = (char)val;
2461                                 break;
2462 
2463                         /* \x and up to 2 hex digits */
2464                         case 'x':
2465                                 val = 'x';      /* Default if no digits */
2466                                 c = hextoint(*s++);     /* Get next char */
2467                                 if (c >= 0) {
2468                                         val = c;
2469                                         c = hextoint(*s++);
2470                                         if (c >= 0)
2471                                                 val = (val << 4) + c;
2472                                         else
2473                                                 --s;
2474                                 } else
2475                                         --s;
2476                                 *p++ = (char)val;
2477                                 break;
2478                         }
2479                 } else
2480                         *p++ = (char)c;
2481         }
2482 out:
2483         *p = '\0';
2484         m->vallen = CAST(unsigned char, (p - origp));
2485         if (m->type == FILE_PSTRING)
2486                 m->vallen += (unsigned char)file_pstring_length_size(m);
2487         return s;
2488 }
2489 
2490 
2491 /* Single hex char to int; -1 if not a hex char. */
2492 private int
2493 hextoint(int c)
2494 {
2495         if (!isascii((unsigned char) c))
2496                 return -1;
2497         if (isdigit((unsigned char) c))
2498                 return c - '0';
2499         if ((c >= 'a') && (c <= 'f'))
2500                 return c + 10 - 'a';
2501         if (( c>= 'A') && (c <= 'F'))
2502                 return c + 10 - 'A';
2503         return -1;
2504 }
2505 
2506 
2507 /*
2508  * Print a string containing C character escapes.
2509  */
2510 protected void
2511 file_showstr(FILE *fp, const char *s, size_t len)
2512 {
2513         char    c;
2514 
2515         for (;;) {
2516                 if (len == ~0U) {
2517                         c = *s++;
2518                         if (c == '\0')
2519                                 break;
2520                 }
2521                 else  {
2522                         if (len-- == 0)
2523                                 break;
2524                         c = *s++;
2525                 }
2526                 if (c >= 040 && c <= 0176)      /* TODO isprint && !iscntrl */
2527                         (void) fputc(c, fp);
2528                 else {
2529                         (void) fputc('\\', fp);
2530                         switch (c) {
2531                         case '\a':
2532                                 (void) fputc('a', fp);
2533                                 break;
2534 
2535                         case '\b':
2536                                 (void) fputc('b', fp);
2537                                 break;
2538 
2539                         case '\f':
2540                                 (void) fputc('f', fp);
2541                                 break;
2542 
2543                         case '\n':
2544                                 (void) fputc('n', fp);
2545                                 break;
2546 
2547                         case '\r':
2548                                 (void) fputc('r', fp);
2549                                 break;
2550 
2551                         case '\t':
2552                                 (void) fputc('t', fp);
2553                                 break;
2554 
2555                         case '\v':
2556                                 (void) fputc('v', fp);
2557                                 break;
2558 
2559                         default:
2560                                 (void) fprintf(fp, "%.3o", c & 0377);
2561                                 break;
2562                         }
2563                 }
2564         }
2565 }
2566 
2567 /*
2568  * eatsize(): Eat the size spec from a number [eg. 10UL]
2569  */
2570 private void
2571 eatsize(const char **p)
2572 {
2573         const char *l = *p;
2574 
2575         if (LOWCASE(*l) == 'u') 
2576                 l++;
2577 
2578         switch (LOWCASE(*l)) {
2579         case 'l':    /* long */
2580         case 's':    /* short */
2581         case 'h':    /* short */
2582         case 'b':    /* char/byte */
2583         case 'c':    /* char/byte */
2584                 l++;
2585                 /*FALLTHROUGH*/
2586         default:
2587                 break;
2588         }
2589 
2590         *p = l;
2591 }
2592 
2593 /*
2594  * handle a compiled file.
2595  */
2596 
2597 private struct magic_map *
2598 apprentice_map(struct magic_set *ms, const char *fn)
2599 {
2600         uint32_t *ptr;
2601         uint32_t version, entries, nentries;
2602         int needsbyteswap;
2603         char *dbname = NULL;
2604         struct magic_map *map;
2605         size_t i;
2606         php_stream *stream = NULL;
2607         php_stream_statbuf st;
2608 
2609 
2610         TSRMLS_FETCH();
2611 
2612         if ((map = CAST(struct magic_map *, ecalloc(1, sizeof(*map)))) == NULL) {
2613                 file_oomem(ms, sizeof(*map));
2614                 return NULL;
2615         }
2616 
2617         if (fn == NULL) {
2618                 map->p = (void *)&php_magic_database;
2619                 goto internal_loaded;
2620         }
2621 
2622 #ifdef PHP_WIN32
2623         /* Don't bother on windows with php_stream_open_wrapper,
2624         return to give apprentice_load() a chance. */
2625         if (php_stream_stat_path_ex((char *)fn, 0, &st, NULL) == SUCCESS) {
2626                if (st.sb.st_mode & S_IFDIR) {
2627                        return NULL;
2628                }
2629        }
2630 #endif
2631 
2632         dbname = mkdbname(ms, fn, 0);
2633         if (dbname == NULL)
2634                 goto error;
2635 
2636                 stream = php_stream_open_wrapper((char *)fn, "rb", REPORT_ERRORS, NULL);
2637 
2638         if (!stream) {
2639                 goto error;
2640         }
2641 
2642         if (php_stream_stat(stream, &st) < 0) {
2643                 file_error(ms, errno, "cannot stat `%s'", dbname);
2644                 goto error;
2645         }
2646 
2647         if (st.sb.st_size < 8) {
2648                 file_error(ms, 0, "file `%s' is too small", dbname);
2649                 goto error;
2650         }
2651 
2652         map->len = (size_t)st.sb.st_size;
2653         if ((map->p = CAST(void *, emalloc(map->len))) == NULL) {
2654                 file_oomem(ms, map->len);
2655                 goto error;
2656         }
2657         if (php_stream_read(stream, map->p, (size_t)st.sb.st_size) != (size_t)st.sb.st_size) {
2658                 file_badread(ms);
2659                 goto error;
2660         }
2661         map->len = 0;
2662 #define RET     1
2663 
2664         php_stream_close(stream);
2665         stream = NULL;
2666 
2667 internal_loaded:
2668         ptr = (uint32_t *)(void *)map->p;
2669         if (*ptr != MAGICNO) {
2670                 if (swap4(*ptr) != MAGICNO) {
2671                         file_error(ms, 0, "bad magic in `%s'", dbname);
2672                         goto error;
2673                 }
2674                 needsbyteswap = 1;
2675         } else
2676                 needsbyteswap = 0;
2677         if (needsbyteswap)
2678                 version = swap4(ptr[1]);
2679         else
2680                 version = ptr[1];
2681         if (version != VERSIONNO) {
2682                 file_error(ms, 0, "File %d.%d supports only version %d magic "
2683                     "files. `%s' is version %d", FILE_VERSION_MAJOR, patchlevel,
2684                     VERSIONNO, dbname, version);
2685                 goto error;
2686         }
2687 
2688         /* php_magic_database is a const, performing writes will segfault. This is for big-endian
2689         machines only, PPC and Sparc specifically. Consider static variable or MINIT in
2690         future. */
2691         if (needsbyteswap && fn == NULL) {
2692                 map->p = emalloc(sizeof(php_magic_database));
2693                 map->p = memcpy(map->p, php_magic_database, sizeof(php_magic_database));
2694         }
2695 
2696         if (NULL != fn) {
2697                 nentries = (uint32_t)(st.sb.st_size / sizeof(struct magic));
2698                 entries = (uint32_t)(st.sb.st_size / sizeof(struct magic));
2699                 if ((off_t)(entries * sizeof(struct magic)) != st.sb.st_size) {
2700                         file_error(ms, 0, "Size of `%s' %llu is not a multiple of %zu",
2701                                 dbname, (unsigned long long)st.sb.st_size,
2702                                 sizeof(struct magic));
2703                         goto error;
2704                 }
2705         }
2706         map->magic[0] = CAST(struct magic *, map->p) + 1;
2707         nentries = 0;
2708         for (i = 0; i < MAGIC_SETS; i++) {
2709                 if (needsbyteswap)
2710                         map->nmagic[i] = swap4(ptr[i + 2]);
2711                 else
2712                         map->nmagic[i] = ptr[i + 2];
2713                 if (i != MAGIC_SETS - 1)
2714                         map->magic[i + 1] = map->magic[i] + map->nmagic[i];
2715                 nentries += map->nmagic[i];
2716         }
2717         if (NULL != fn && entries != nentries + 1) {
2718                 file_error(ms, 0, "Inconsistent entries in `%s' %u != %u",
2719                     dbname, entries, nentries + 1);
2720                 goto error;
2721         }
2722 
2723         if (needsbyteswap)
2724                 for (i = 0; i < MAGIC_SETS; i++)
2725                         byteswap(map->magic[i], map->nmagic[i]);
2726 
2727         if (dbname) {
2728                 efree(dbname);
2729         }
2730         return map;
2731 
2732 error:
2733         if (stream) {
2734                 php_stream_close(stream);
2735         }
2736         apprentice_unmap(map);
2737         if (dbname) {
2738                 efree(dbname);
2739         }
2740         return NULL;
2741 }
2742 
2743 private const uint32_t ar[] = {
2744     MAGICNO, VERSIONNO
2745 };
2746 
2747 /*
2748  * handle an mmaped file.
2749  */
2750 private int
2751 apprentice_compile(struct magic_set *ms, struct magic_map *map, const char *fn)
2752 {
2753         static const size_t nm = sizeof(*map->nmagic) * MAGIC_SETS;
2754         static const size_t m = sizeof(**map->magic);
2755         int fd = -1;
2756         size_t len;
2757         char *dbname;
2758         int rv = -1;
2759         uint32_t i;
2760         php_stream *stream;
2761 
2762         TSRMLS_FETCH();
2763 
2764         dbname = mkdbname(ms, fn, 0);
2765 
2766         if (dbname == NULL) 
2767                 goto out;
2768 
2769         /* wb+ == O_WRONLY|O_CREAT|O_TRUNC|O_BINARY */
2770         stream = php_stream_open_wrapper((char *)fn, "wb+", REPORT_ERRORS, NULL);
2771 
2772         if (!stream) {
2773                 file_error(ms, errno, "cannot open `%s'", dbname);
2774                 goto out;
2775         }
2776 
2777         if (write(fd, ar, sizeof(ar)) != (ssize_t)sizeof(ar)) {
2778                 file_error(ms, errno, "error writing `%s'", dbname);
2779                 goto out;
2780         }
2781 
2782         if (php_stream_write(stream, (const char *)map->nmagic, nm) != (ssize_t)nm) {
2783                 file_error(ms, errno, "error writing `%s'", dbname);
2784                 goto out;
2785         }
2786 
2787         assert(nm + sizeof(ar) < m);
2788 
2789         if (php_stream_seek(stream,(off_t)sizeof(struct magic), SEEK_SET) != sizeof(struct magic)) {
2790                 file_error(ms, errno, "error seeking `%s'", dbname);
2791                 goto out;
2792         }
2793 
2794         for (i = 0; i < MAGIC_SETS; i++) {
2795                 len = m * map->nmagic[i];
2796                 if (php_stream_write(stream, (const char *)map->magic[i], len) != (ssize_t)len) {
2797                         file_error(ms, errno, "error writing `%s'", dbname);
2798                         goto out;
2799                 }
2800         }
2801 
2802         if (stream) {
2803                 php_stream_close(stream);
2804         }
2805 
2806         rv = 0;
2807 out:
2808         efree(dbname);
2809         return rv;
2810 }
2811 
2812 private const char ext[] = ".mgc";
2813 /*
2814  * make a dbname
2815  */
2816 private char *
2817 mkdbname(struct magic_set *ms, const char *fn, int strip)
2818 {
2819         const char *p, *q;
2820         char *buf;
2821         TSRMLS_FETCH();
2822 
2823         if (strip) {
2824                 if ((p = strrchr(fn, '/')) != NULL)
2825                         fn = ++p;
2826         }
2827 
2828         for (q = fn; *q; q++)
2829                 continue;
2830         /* Look for .mgc */
2831         for (p = ext + sizeof(ext) - 1; p >= ext && q >= fn; p--, q--)
2832                 if (*p != *q)
2833                         break;
2834 
2835         /* Did not find .mgc, restore q */
2836         if (p >= ext)
2837                 while (*q)
2838                         q++;
2839 
2840         q++;
2841         /* Compatibility with old code that looked in .mime */
2842         if (ms->flags & MAGIC_MIME) {
2843                 spprintf(&buf, MAXPATHLEN, "%.*s.mime%s", (int)(q - fn), fn, ext);
2844 #ifdef PHP_WIN32
2845                 if (VCWD_ACCESS(buf, R_OK) == 0) {
2846 #else
2847                 if (VCWD_ACCESS(buf, R_OK) != -1) {
2848 #endif
2849                         ms->flags &= MAGIC_MIME_TYPE;
2850                         return buf;
2851                 }
2852                 efree(buf);
2853         }
2854         spprintf(&buf, MAXPATHLEN, "%.*s%s", (int)(q - fn), fn, ext);
2855 
2856         /* Compatibility with old code that looked in .mime */
2857         if (strstr(p, ".mime") != NULL)
2858                 ms->flags &= MAGIC_MIME_TYPE;
2859         return buf;
2860 }
2861 
2862 /*
2863  * Byteswap an mmap'ed file if needed
2864  */
2865 private void
2866 byteswap(struct magic *magic, uint32_t nmagic)
2867 {
2868         uint32_t i;
2869         for (i = 0; i < nmagic; i++)
2870                 bs1(&magic[i]);
2871 }
2872 
2873 /*
2874  * swap a short
2875  */
2876 private uint16_t
2877 swap2(uint16_t sv)
2878 {
2879         uint16_t rv;
2880         uint8_t *s = (uint8_t *)(void *)&sv; 
2881         uint8_t *d = (uint8_t *)(void *)&rv; 
2882         d[0] = s[1];
2883         d[1] = s[0];
2884         return rv;
2885 }
2886 
2887 /*
2888  * swap an int
2889  */
2890 private uint32_t
2891 swap4(uint32_t sv)
2892 {
2893         uint32_t rv;
2894         uint8_t *s = (uint8_t *)(void *)&sv; 
2895         uint8_t *d = (uint8_t *)(void *)&rv; 
2896         d[0] = s[3];
2897         d[1] = s[2];
2898         d[2] = s[1];
2899         d[3] = s[0];
2900         return rv;
2901 }
2902 
2903 /*
2904  * swap a quad
2905  */
2906 private uint64_t
2907 swap8(uint64_t sv)
2908 {
2909         uint64_t rv;
2910         uint8_t *s = (uint8_t *)(void *)&sv; 
2911         uint8_t *d = (uint8_t *)(void *)&rv; 
2912 #if 0
2913         d[0] = s[3];
2914         d[1] = s[2];
2915         d[2] = s[1];
2916         d[3] = s[0];
2917         d[4] = s[7];
2918         d[5] = s[6];
2919         d[6] = s[5];
2920         d[7] = s[4];
2921 #else
2922         d[0] = s[7];
2923         d[1] = s[6];
2924         d[2] = s[5];
2925         d[3] = s[4];
2926         d[4] = s[3];
2927         d[5] = s[2];
2928         d[6] = s[1];
2929         d[7] = s[0];
2930 #endif
2931         return rv;
2932 }
2933 
2934 /*
2935  * byteswap a single magic entry
2936  */
2937 private void
2938 bs1(struct magic *m)
2939 {
2940         m->cont_level = swap2(m->cont_level);
2941         m->offset = swap4((uint32_t)m->offset);
2942         m->in_offset = swap4((uint32_t)m->in_offset);
2943         m->lineno = swap4((uint32_t)m->lineno);
2944         if (IS_LIBMAGIC_STRING(m->type)) {
2945                 m->str_range = swap4(m->str_range);
2946                 m->str_flags = swap4(m->str_flags);
2947         }
2948         else {
2949                 m->value.q = swap8(m->value.q);
2950                 m->num_mask = swap8(m->num_mask);
2951         }
2952 }
2953 
2954 protected size_t 
2955 file_pstring_length_size(const struct magic *m)
2956 {
2957         switch (m->str_flags & PSTRING_LEN) {
2958         case PSTRING_1_LE:
2959                 return 1;
2960         case PSTRING_2_LE:
2961         case PSTRING_2_BE:
2962                 return 2;
2963         case PSTRING_4_LE:
2964         case PSTRING_4_BE:
2965                 return 4;
2966         default:
2967                 abort();        /* Impossible */
2968                 return 1;
2969         }
2970 }
2971 protected size_t
2972 file_pstring_get_length(const struct magic *m, const char *s)
2973 {
2974         size_t len = 0;
2975 
2976         switch (m->str_flags & PSTRING_LEN) {
2977         case PSTRING_1_LE:
2978                 len = *s;
2979                 break;
2980         case PSTRING_2_LE:
2981                 len = (s[1] << 8) | s[0];
2982                 break;
2983         case PSTRING_2_BE:
2984                 len = (s[0] << 8) | s[1];
2985                 break;
2986         case PSTRING_4_LE:
2987                 len = (s[3] << 24) | (s[2] << 16) | (s[1] << 8) | s[0];
2988                 break;
2989         case PSTRING_4_BE:
2990                 len = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
2991                 break;
2992         default:
2993                 abort();        /* Impossible */
2994         }
2995 
2996         if (m->str_flags & PSTRING_LENGTH_INCLUDES_ITSELF)
2997                 len -= file_pstring_length_size(m);
2998 
2999         return len;
3000 }
3001 
3002 protected int
3003 file_magicfind(struct magic_set *ms, const char *name, struct mlist *v)
3004 {
3005         uint32_t i, j;
3006         struct mlist *mlist, *ml;
3007 
3008         mlist = ms->mlist[1];
3009 
3010         for (ml = mlist->next; ml != mlist; ml = ml->next) {
3011                 struct magic *ma = ml->magic;
3012                 uint32_t nma = ml->nmagic;
3013                 for (i = 0; i < nma; i++) {
3014                         if (ma[i].type != FILE_NAME)
3015                                 continue;
3016                         if (strcmp(ma[i].value.s, name) == 0) {
3017                                 v->magic = &ma[i];
3018                                 for (j = i + 1; j < nma; j++)
3019                                     if (ma[j].cont_level == 0)
3020                                             break;
3021                                 v->nmagic = j - i;
3022                                 return 0;
3023                         }
3024                 }
3025         }
3026         return -1;
3027 }

/* [<][>][^][v][top][bottom][index][help] */