Back to index

php5  5.3.10
apprentice.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) Ian F. Darwin 1986-1995.
00003  * Software written by Ian F. Darwin and others;
00004  * maintained 1995-present by Christos Zoulas and others.
00005  * 
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  * 1. Redistributions of source code must retain the above copyright
00010  *    notice immediately at the beginning of the file, without modification,
00011  *    this list of conditions, and the following disclaimer.
00012  * 2. Redistributions in binary form must reproduce the above copyright
00013  *    notice, this list of conditions and the following disclaimer in the
00014  *    documentation and/or other materials provided with the distribution.
00015  *  
00016  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
00017  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00018  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00019  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
00020  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00021  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00022  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00023  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00024  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00025  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00026  * SUCH DAMAGE.
00027  */
00028 /*
00029  * apprentice - make one pass through /etc/magic, learning its secrets.
00030  */
00031 
00032 #include "php.h"
00033 
00034 #include "file.h"
00035 
00036 #ifndef       lint
00037 FILE_RCSID("@(#)$File: apprentice.c,v 1.151 2009/03/18 15:19:23 christos Exp $")
00038 #endif /* lint */
00039 
00040 #include "magic.h"
00041 #include "patchlevel.h"
00042 #include <stdlib.h>
00043 
00044 #if defined(__hpux) && !defined(HAVE_STRTOULL)
00045 #if SIZEOF_LONG == 8
00046 # define strtoull strtoul
00047 #else
00048 # define strtoull __strtoull
00049 #endif
00050 #endif
00051 
00052 #ifdef PHP_WIN32
00053 #include "win32/unistd.h"
00054 #if _MSC_VER <= 1300
00055 # include "win32/php_strtoi64.h"
00056 #endif
00057 #define strtoull _strtoui64
00058 #else
00059 #include <unistd.h>
00060 #endif
00061 
00062 #include <string.h>
00063 #include <assert.h>
00064 #include <ctype.h>
00065 #include <fcntl.h>
00066 #ifndef PHP_WIN32
00067 #include <dirent.h>
00068 #endif
00069 
00070 #define       EATAB {while (isascii((unsigned char) *l) && \
00071                     isspace((unsigned char) *l))  ++l;}
00072 #define LOWCASE(l) (isupper((unsigned char) (l)) ? \
00073                      tolower((unsigned char) (l)) : (l))
00074 /*
00075  * Work around a bug in headers on Digital Unix.
00076  * At least confirmed for: OSF1 V4.0 878
00077  */
00078 #if defined(__osf__) && defined(__DECC)
00079 #ifdef MAP_FAILED
00080 #undef MAP_FAILED
00081 #endif
00082 #endif
00083 
00084 #ifndef MAP_FAILED
00085 #define MAP_FAILED (void *) -1
00086 #endif
00087 
00088 #ifndef MAP_FILE
00089 #define MAP_FILE 0
00090 #endif
00091 
00092 #ifndef MAXPATHLEN
00093 #define MAXPATHLEN   1024
00094 #endif
00095 
00096 struct magic_entry {
00097        struct magic *mp;    
00098        uint32_t cont_count;
00099        uint32_t max_count;
00100 };
00101 
00102 int file_formats[FILE_NAMES_SIZE];
00103 const size_t file_nformats = FILE_NAMES_SIZE;
00104 const char *file_names[FILE_NAMES_SIZE];
00105 const size_t file_nnames = FILE_NAMES_SIZE;
00106 
00107 private int getvalue(struct magic_set *ms, struct magic *, const char **, int);
00108 private int hextoint(int);
00109 private const char *getstr(struct magic_set *, struct magic *, const char *,
00110     int);
00111 private int parse(struct magic_set *, struct magic_entry **, uint32_t *,
00112     const char *, size_t, int);
00113 private void eatsize(const char **);
00114 private int apprentice_1(struct magic_set *, const char *, int, struct mlist *);
00115 private size_t apprentice_magic_strength(const struct magic *);
00116 private int apprentice_sort(const void *, const void *);
00117 private int apprentice_load(struct magic_set *, struct magic **, uint32_t *,
00118     const char *, int);
00119 private void byteswap(struct magic *, uint32_t);
00120 private void bs1(struct magic *);
00121 private uint16_t swap2(uint16_t);
00122 private uint32_t swap4(uint32_t);
00123 private uint64_t swap8(uint64_t);
00124 private char *mkdbname(struct magic_set *, const char *, int);
00125 private int apprentice_map(struct magic_set *, struct magic **, uint32_t *,
00126     const char *);
00127 private int apprentice_compile(struct magic_set *, struct magic **, uint32_t *,
00128     const char *);
00129 private int check_format_type(const char *, int);
00130 private int check_format(struct magic_set *, struct magic *);
00131 private int get_op(char);
00132 private int parse_mime(struct magic_set *, struct magic_entry *, const char *);
00133 private int parse_strength(struct magic_set *, struct magic_entry *, const char *);
00134 private int parse_apple(struct magic_set *, struct magic_entry *, const char *);
00135 
00136 private size_t maxmagic = 0;
00137 private size_t magicsize = sizeof(struct magic);
00138 
00139 private const char usg_hdr[] = "cont\toffset\ttype\topcode\tmask\tvalue\tdesc";
00140 private struct {
00141        const char *name;
00142        size_t len;
00143        int (*fun)(struct magic_set *, struct magic_entry *, const char *);
00144 } bang[] = {
00145 #define       DECLARE_FIELD(name) { # name, sizeof(# name) - 1, parse_ ## name }
00146        DECLARE_FIELD(mime),
00147        DECLARE_FIELD(apple),
00148        DECLARE_FIELD(strength),
00149 #undef DECLARE_FIELD
00150        { NULL, 0, NULL }
00151 };
00152 
00153 #include "../data_file.c"
00154 
00155 static const struct type_tbl_s {
00156        const char name[16];
00157        const size_t len;
00158        const int type;
00159        const int format;
00160 } type_tbl[] = {
00161 # define XX(s)              s, (sizeof(s) - 1)
00162 # define XX_NULL     "", 0
00163        { XX("byte"),        FILE_BYTE,           FILE_FMT_NUM },
00164        { XX("short"),              FILE_SHORT,          FILE_FMT_NUM },
00165        { XX("default"),     FILE_DEFAULT,        FILE_FMT_STR },
00166        { XX("long"),        FILE_LONG,           FILE_FMT_NUM },
00167        { XX("string"),             FILE_STRING,         FILE_FMT_STR },
00168        { XX("date"),        FILE_DATE,           FILE_FMT_STR },
00169        { XX("beshort"),     FILE_BESHORT,        FILE_FMT_NUM },
00170        { XX("belong"),             FILE_BELONG,         FILE_FMT_NUM },
00171        { XX("bedate"),             FILE_BEDATE,         FILE_FMT_STR },
00172        { XX("leshort"),     FILE_LESHORT,        FILE_FMT_NUM },
00173        { XX("lelong"),             FILE_LELONG,         FILE_FMT_NUM },
00174        { XX("ledate"),             FILE_LEDATE,         FILE_FMT_STR },
00175        { XX("pstring"),     FILE_PSTRING,        FILE_FMT_STR },
00176        { XX("ldate"),              FILE_LDATE,          FILE_FMT_STR },
00177        { XX("beldate"),     FILE_BELDATE,        FILE_FMT_STR },
00178        { XX("leldate"),     FILE_LELDATE,        FILE_FMT_STR },
00179        { XX("regex"),              FILE_REGEX,          FILE_FMT_STR },
00180        { XX("bestring16"),  FILE_BESTRING16,     FILE_FMT_STR },
00181        { XX("lestring16"),  FILE_LESTRING16,     FILE_FMT_STR },
00182        { XX("search"),             FILE_SEARCH,         FILE_FMT_STR },
00183        { XX("medate"),             FILE_MEDATE,         FILE_FMT_STR },
00184        { XX("meldate"),     FILE_MELDATE,        FILE_FMT_STR },
00185        { XX("melong"),             FILE_MELONG,         FILE_FMT_NUM },
00186        { XX("quad"),        FILE_QUAD,           FILE_FMT_QUAD },
00187        { XX("lequad"),             FILE_LEQUAD,         FILE_FMT_QUAD },
00188        { XX("bequad"),             FILE_BEQUAD,         FILE_FMT_QUAD },
00189        { XX("qdate"),              FILE_QDATE,          FILE_FMT_STR },
00190        { XX("leqdate"),     FILE_LEQDATE,        FILE_FMT_STR },
00191        { XX("beqdate"),     FILE_BEQDATE,        FILE_FMT_STR },
00192        { XX("qldate"),             FILE_QLDATE,         FILE_FMT_STR },
00193        { XX("leqldate"),    FILE_LEQLDATE,              FILE_FMT_STR },
00194        { XX("beqldate"),    FILE_BEQLDATE,              FILE_FMT_STR },
00195        { XX("float"),              FILE_FLOAT,          FILE_FMT_FLOAT },
00196        { XX("befloat"),     FILE_BEFLOAT,        FILE_FMT_FLOAT },
00197        { XX("lefloat"),     FILE_LEFLOAT,        FILE_FMT_FLOAT },
00198        { XX("double"),             FILE_DOUBLE,         FILE_FMT_DOUBLE },
00199        { XX("bedouble"),    FILE_BEDOUBLE,              FILE_FMT_DOUBLE },
00200        { XX("ledouble"),    FILE_LEDOUBLE,              FILE_FMT_DOUBLE },
00201        { XX("leid3"),              FILE_LEID3,          FILE_FMT_NUM },
00202        { XX("beid3"),              FILE_BEID3,          FILE_FMT_NUM },
00203        { XX("indirect"),    FILE_INDIRECT,              FILE_FMT_NONE },
00204        { XX_NULL,           FILE_INVALID,        FILE_FMT_NONE },
00205 # undef XX
00206 # undef XX_NULL
00207 };
00208 
00209 #ifndef S_ISDIR
00210 #define S_ISDIR(mode) ((mode) & _S_IFDIR)
00211 #endif
00212 
00213 private int
00214 get_type(const char *l, const char **t)
00215 {
00216        const struct type_tbl_s *p;
00217 
00218        for (p = type_tbl; p->len; p++) {
00219               if (strncmp(l, p->name, p->len) == 0) {
00220                      if (t)
00221                             *t = l + p->len;
00222                      break;
00223               }
00224        }
00225        return p->type;
00226 }
00227 
00228 private void
00229 init_file_tables(void)
00230 {
00231        static int done = 0;
00232        const struct type_tbl_s *p;
00233 
00234        if (done)
00235               return;
00236        done++;
00237 
00238        for (p = type_tbl; p->len; p++) {
00239               assert(p->type < FILE_NAMES_SIZE);
00240               file_names[p->type] = p->name;
00241               file_formats[p->type] = p->format;
00242        }
00243 }
00244 
00245 /*
00246  * Handle one file or directory.
00247  */
00248 private int
00249 apprentice_1(struct magic_set *ms, const char *fn, int action,
00250     struct mlist *mlist)
00251 {
00252        struct magic *magic = NULL;
00253        uint32_t nmagic = 0;
00254        struct mlist *ml;
00255        int rv = -1;
00256        int mapped;
00257 
00258        if (magicsize != FILE_MAGICSIZE) {
00259               file_error(ms, 0, "magic element size %lu != %lu",
00260                   (unsigned long)sizeof(*magic),
00261                   (unsigned long)FILE_MAGICSIZE);
00262               return -1;
00263        }
00264 
00265        if (action == FILE_COMPILE) {
00266               rv = apprentice_load(ms, &magic, &nmagic, fn, action);
00267               if (rv != 0)
00268                      return -1;
00269               rv = apprentice_compile(ms, &magic, &nmagic, fn);
00270               efree(magic);
00271               return rv;
00272        }
00273 
00274        if ((rv = apprentice_map(ms, &magic, &nmagic, fn)) == -1) {
00275               if (fn) {
00276                      if (ms->flags & MAGIC_CHECK)
00277                             file_magwarn(ms, "using regular magic file `%s'", fn);
00278                      rv = apprentice_load(ms, &magic, &nmagic, fn, action);
00279               }
00280 
00281               if (rv != 0)
00282                      return -1;
00283        }
00284 
00285        mapped = rv;
00286             
00287        if (magic == NULL) {
00288               file_delmagic(magic, mapped, nmagic);
00289               return -1;
00290        }
00291 
00292        ml = emalloc(sizeof(*ml));
00293 
00294        ml->magic = magic;
00295        ml->nmagic = nmagic;
00296        ml->mapped = mapped;
00297 
00298        mlist->prev->next = ml;
00299        ml->prev = mlist->prev;
00300        ml->next = mlist;
00301        mlist->prev = ml;
00302 
00303        return 0;
00304 }
00305 
00306 protected void
00307 file_delmagic(struct magic *p, int type, size_t entries)
00308 {
00309        if (p == NULL)
00310               return;
00311        switch (type) {
00312        case 3:
00313               /* Do nothing, it's part of the code segment */
00314               break;
00315 
00316        case 1:
00317               p--;
00318               /*FALLTHROUGH*/
00319 
00320        case 0:
00321               efree(p);
00322               break;
00323 
00324        default:
00325               abort();
00326        }
00327 }
00328 
00329 /* const char *fn: list of magic files and directories */
00330 protected struct mlist *
00331 file_apprentice(struct magic_set *ms, const char *fn, int action)
00332 {
00333        char *p, *mfn;
00334        int file_err, errs = -1;
00335        struct mlist *mlist;
00336 
00337        init_file_tables();
00338 
00339        if (fn == NULL)
00340               fn = getenv("MAGIC");
00341        if (fn == NULL) {
00342               mlist = emalloc(sizeof(*mlist));
00343               mlist->next = mlist->prev = mlist;
00344               apprentice_1(ms, fn, action, mlist);
00345               return mlist;
00346        }
00347 
00348        mfn = estrdup(fn);
00349        fn = mfn;
00350 
00351        mlist = emalloc(sizeof(*mlist));
00352        mlist->next = mlist->prev = mlist;
00353 
00354        while (fn) {
00355               p = strchr(fn, PATHSEP);
00356               if (p)
00357                      *p++ = '\0';
00358               if (*fn == '\0')
00359                      break;
00360               file_err = apprentice_1(ms, fn, action, mlist);
00361               errs = MAX(errs, file_err);
00362               fn = p;
00363        }
00364        if (errs == -1) {
00365               efree(mfn);
00366               efree(mlist);
00367               mlist = NULL;
00368               file_error(ms, 0, "could not find any magic files!");
00369               return NULL;
00370        }
00371        efree(mfn);
00372        return mlist;
00373 }
00374 
00375 /*
00376  * Get weight of this magic entry, for sorting purposes.
00377  */
00378 private size_t
00379 apprentice_magic_strength(const struct magic *m)
00380 {
00381 #define MULT 10
00382        size_t val = 2 * MULT;      /* baseline strength */
00383 
00384        switch (m->type) {
00385        case FILE_DEFAULT:   /* make sure this sorts last */
00386               if (m->factor_op != FILE_FACTOR_OP_NONE)
00387                      abort();
00388               return 0;
00389 
00390        case FILE_BYTE:
00391               val += 1 * MULT;
00392               break;
00393 
00394        case FILE_SHORT:
00395        case FILE_LESHORT:
00396        case FILE_BESHORT:
00397               val += 2 * MULT;
00398               break;
00399 
00400        case FILE_LONG:
00401        case FILE_LELONG:
00402        case FILE_BELONG:
00403        case FILE_MELONG:
00404               val += 4 * MULT;
00405               break;
00406 
00407        case FILE_PSTRING:
00408        case FILE_STRING:
00409               val += m->vallen * MULT;
00410               break;
00411 
00412        case FILE_BESTRING16:
00413        case FILE_LESTRING16:
00414               val += m->vallen * MULT / 2;
00415               break;
00416 
00417        case FILE_SEARCH:
00418        case FILE_REGEX:
00419               val += m->vallen * MAX(MULT / m->vallen, 1);
00420               break;
00421 
00422        case FILE_DATE:
00423        case FILE_LEDATE:
00424        case FILE_BEDATE:
00425        case FILE_MEDATE:
00426        case FILE_LDATE:
00427        case FILE_LELDATE:
00428        case FILE_BELDATE:
00429        case FILE_MELDATE:
00430        case FILE_FLOAT:
00431        case FILE_BEFLOAT:
00432        case FILE_LEFLOAT:
00433               val += 4 * MULT;
00434               break;
00435 
00436        case FILE_QUAD:
00437        case FILE_BEQUAD:
00438        case FILE_LEQUAD:
00439        case FILE_QDATE:
00440        case FILE_LEQDATE:
00441        case FILE_BEQDATE:
00442        case FILE_QLDATE:
00443        case FILE_LEQLDATE:
00444        case FILE_BEQLDATE:
00445        case FILE_DOUBLE:
00446        case FILE_BEDOUBLE:
00447        case FILE_LEDOUBLE:
00448               val += 8 * MULT;
00449               break;
00450 
00451        default:
00452               val = 0;
00453               (void)fprintf(stderr, "Bad type %d\n", m->type);
00454               abort();
00455        }
00456 
00457        switch (m->reln) {
00458        case 'x':     /* matches anything penalize */
00459        case '!':       /* matches almost anything penalize */
00460               val = 0;
00461               break;
00462 
00463        case '=':     /* Exact match, prefer */
00464               val += MULT;
00465               break;
00466 
00467        case '>':
00468        case '<':     /* comparison match reduce strength */
00469               val -= 2 * MULT;
00470               break;
00471 
00472        case '^':
00473        case '&':     /* masking bits, we could count them too */
00474               val -= MULT;
00475               break;
00476 
00477        default:
00478               (void)fprintf(stderr, "Bad relation %c\n", m->reln);
00479               abort();
00480        }
00481 
00482        if (val == 0) /* ensure we only return 0 for FILE_DEFAULT */
00483               val = 1;
00484 
00485        switch (m->factor_op) {
00486        case FILE_FACTOR_OP_NONE:
00487               break;
00488        case FILE_FACTOR_OP_PLUS:
00489               val += m->factor;
00490               break;
00491        case FILE_FACTOR_OP_MINUS:
00492               val -= m->factor;
00493               break;
00494        case FILE_FACTOR_OP_TIMES:
00495               val *= m->factor;
00496               break;
00497        case FILE_FACTOR_OP_DIV:
00498               val /= m->factor;
00499               break;
00500        default:
00501               abort();
00502        }
00503 
00504 
00505        /*
00506         * Magic entries with no description get a bonus because they depend
00507         * on subsequent magic entries to print something.
00508         */
00509        if (m->desc[0] == '\0')
00510               val++;
00511        return val;
00512 }
00513 
00514 /*  
00515  * Sort callback for sorting entries by "strength" (basically length)
00516  */
00517 private int
00518 apprentice_sort(const void *a, const void *b)
00519 {
00520        const struct magic_entry *ma = a;
00521        const struct magic_entry *mb = b;
00522        size_t sa = apprentice_magic_strength(ma->mp);
00523        size_t sb = apprentice_magic_strength(mb->mp);
00524        if (sa == sb)
00525               return 0;
00526        else if (sa > sb)
00527               return -1;
00528        else
00529               return 1;
00530 }
00531 
00532 private void
00533 set_test_type(struct magic *mstart, struct magic *m)
00534 {
00535        switch (m->type) {
00536        case FILE_BYTE:
00537        case FILE_SHORT:
00538        case FILE_LONG:
00539        case FILE_DATE:
00540        case FILE_BESHORT:
00541        case FILE_BELONG:
00542        case FILE_BEDATE:
00543        case FILE_LESHORT:
00544        case FILE_LELONG:
00545        case FILE_LEDATE:
00546        case FILE_LDATE:
00547        case FILE_BELDATE:
00548        case FILE_LELDATE:
00549        case FILE_MEDATE:
00550        case FILE_MELDATE:
00551        case FILE_MELONG:
00552        case FILE_QUAD:
00553        case FILE_LEQUAD:
00554        case FILE_BEQUAD:
00555        case FILE_QDATE:
00556        case FILE_LEQDATE:
00557        case FILE_BEQDATE:
00558        case FILE_QLDATE:
00559        case FILE_LEQLDATE:
00560        case FILE_BEQLDATE:
00561        case FILE_FLOAT:
00562        case FILE_BEFLOAT:
00563        case FILE_LEFLOAT:
00564        case FILE_DOUBLE:
00565        case FILE_BEDOUBLE:
00566        case FILE_LEDOUBLE:
00567        case FILE_STRING:
00568        case FILE_PSTRING:
00569        case FILE_BESTRING16:
00570        case FILE_LESTRING16:
00571               /* binary test, set flag */
00572               mstart->flag |= BINTEST;
00573               break;
00574        case FILE_REGEX:
00575        case FILE_SEARCH:
00576               /* binary test if pattern is not text */
00577               if (file_looks_utf8(m->value.us, (size_t)m->vallen, NULL,
00578                   NULL) <= 0)
00579                      mstart->flag |= BINTEST;
00580               break;
00581        case FILE_DEFAULT:
00582               /* can't deduce anything; we shouldn't see this at the
00583                  top level anyway */
00584               break;
00585        case FILE_INVALID:
00586        default:
00587               /* invalid search type, but no need to complain here */
00588               break;
00589        }
00590 }
00591 
00592 /*
00593  * Load and parse one file.
00594  */
00595 private void
00596 load_1(struct magic_set *ms, int action, const char *fn, int *errs,
00597    struct magic_entry **marray, uint32_t *marraycount)
00598 {
00599        char buffer[BUFSIZ + 1];
00600        char *line;
00601        size_t line_len;
00602        size_t lineno = 0;
00603 
00604        php_stream *stream;
00605 
00606        TSRMLS_FETCH();
00607 
00608 #if PHP_API_VERSION < 20100412
00609        stream = php_stream_open_wrapper((char *)fn, "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL);
00610 #else
00611        stream = php_stream_open_wrapper((char *)fn, "rb", REPORT_ERRORS, NULL);
00612 #endif
00613 
00614        if (stream == NULL) {
00615               if (errno != ENOENT)
00616                      file_error(ms, errno, "cannot read magic file `%s'",
00617                                fn);
00618               (*errs)++;
00619        } else {
00620 
00621               /* read and parse this file */
00622 #if (PHP_MAJOR_VERSION < 6)
00623               for (ms->line = 1; (line = php_stream_get_line(stream, buffer , BUFSIZ, &line_len)) != NULL; ms->line++) {
00624 #else         
00625               for (ms->line = 1; (line = php_stream_get_line(stream, ZSTR(buffer), BUFSIZ, &line_len)) != NULL; ms->line++) {
00626 #endif
00627                      if (line_len == 0) /* null line, garbage, etc */
00628                             continue;
00629 
00630                      if (line[line_len - 1] == '\n') {
00631                             lineno++;
00632                             line[line_len - 1] = '\0'; /* delete newline */
00633                      }
00634                      if (line[0] == '\0') /* empty, do not parse */
00635                             continue;
00636                      if (line[0] == '#')  /* comment, do not parse */
00637                             continue;
00638 
00639                      if (line[0] == '!' && line[1] == ':') {
00640                             size_t i;
00641 
00642                             for (i = 0; bang[i].name != NULL; i++) {
00643                                    if (line_len - 2 > bang[i].len &&
00644                                        memcmp(bang[i].name, line + 2,
00645                                        bang[i].len) == 0)
00646                                           break;
00647                             }
00648                             if (bang[i].name == NULL) {
00649                                    file_error(ms, 0,
00650                                        "Unknown !: entry `%s'", line);
00651                                    (*errs)++;
00652                                    continue;
00653                             }
00654                             if (*marraycount == 0) {
00655                                    file_error(ms, 0,
00656                                        "No current entry for :!%s type",
00657                                           bang[i].name);
00658                                    (*errs)++;
00659                                    continue;
00660                             }
00661                             if ((*bang[i].fun)(ms, 
00662                                 &(*marray)[*marraycount - 1],
00663                                 line + bang[i].len + 2) != 0) {
00664                                    (*errs)++;
00665                                    continue;
00666                             }
00667                             continue;
00668                      }
00669                      if (parse(ms, marray, marraycount, line, lineno, action) != 0)
00670                             (*errs)++;
00671               }
00672 
00673               php_stream_close(stream);
00674        }
00675 }
00676 
00677 /*
00678  * parse a file or directory of files
00679  * const char *fn: name of magic file or directory
00680  */
00681 private int
00682 apprentice_load(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp,
00683     const char *fn, int action)
00684 {
00685        int errs = 0;
00686        struct magic_entry *marray;
00687        uint32_t marraycount, i, mentrycount = 0, starttest;
00688        char subfn[MAXPATHLEN];
00689        struct stat st;
00690        DIR *dir;
00691        struct dirent *d;
00692 
00693        ms->flags |= MAGIC_CHECK;   /* Enable checks for parsed files */
00694 
00695        maxmagic = MAXMAGIS;
00696        marray = ecalloc(maxmagic, sizeof(*marray));
00697        marraycount = 0;
00698 
00699        /* print silly verbose header for USG compat. */
00700        if (action == FILE_CHECK)
00701               (void)fprintf(stderr, "%s\n", usg_hdr);
00702 
00703        /* load directory or file */
00704         /* FIXME: Read file names and sort them to prevent
00705            non-determinism. See Debian bug #488562. */
00706        if (php_sys_stat(fn, &st) == 0 && S_ISDIR(st.st_mode)) {
00707               dir = opendir(fn);
00708               if (dir) {
00709                      while ((d = readdir(dir)) != NULL) {
00710                             snprintf(subfn, sizeof(subfn), "%s/%s",
00711                                 fn, d->d_name);
00712                             if (stat(subfn, &st) == 0 &&
00713                                    S_ISREG(st.st_mode)) {
00714                                    load_1(ms, action, subfn, &errs,
00715                                        &marray, &marraycount);
00716                             }
00717                      }
00718                      closedir(dir);
00719               } else
00720                      errs++;
00721        } else
00722               load_1(ms, action, fn, &errs, &marray, &marraycount);
00723        if (errs)
00724               goto out;
00725 
00726        /* Set types of tests */
00727        for (i = 0; i < marraycount; ) {
00728               if (marray[i].mp->cont_level != 0) {
00729                      i++;
00730                      continue;
00731               }
00732 
00733               starttest = i;
00734               do {
00735                      static const char text[] = "text";
00736                      static const char binary[] = "binary";
00737                      static const size_t len = sizeof(text);
00738                      set_test_type(marray[starttest].mp, marray[i].mp);
00739                      if ((ms->flags & MAGIC_DEBUG) == 0)
00740                             continue;
00741                      (void)fprintf(stderr, "%s%s%s: %s\n",
00742                          marray[i].mp->mimetype,
00743                          marray[i].mp->mimetype[0] == '\0' ? "" : "; ",
00744                          marray[i].mp->desc[0] ? marray[i].mp->desc :
00745                          "(no description)",
00746                          marray[i].mp->flag & BINTEST ? binary : text);
00747                      if (marray[i].mp->flag & BINTEST) {
00748                             char *p = strstr(marray[i].mp->desc, text);
00749                             if (p && (p == marray[i].mp->desc ||
00750                                 isspace((unsigned char)p[-1])) &&
00751                                 (p + len - marray[i].mp->desc == 
00752                                 MAXstring || (p[len] == '\0' ||
00753                                 isspace((unsigned char)p[len]))))
00754                                    (void)fprintf(stderr, "*** Possible "
00755                                        "binary test for text type\n");
00756                      }
00757               } while (++i < marraycount && marray[i].mp->cont_level != 0);
00758        }
00759 
00760        qsort(marray, marraycount, sizeof(*marray), apprentice_sort);
00761 
00762        /*
00763         * Make sure that any level 0 "default" line is last (if one exists).
00764         */
00765        for (i = 0; i < marraycount; i++) {
00766               if (marray[i].mp->cont_level == 0 &&
00767                   marray[i].mp->type == FILE_DEFAULT) {
00768                      while (++i < marraycount)
00769                             if (marray[i].mp->cont_level == 0)
00770                                    break;
00771                      if (i != marraycount) {
00772                             ms->line = marray[i].mp->lineno; /* XXX - Ugh! */
00773                             file_magwarn(ms,
00774                                 "level 0 \"default\" did not sort last");
00775                      }
00776                      break;                                 
00777               }
00778        }
00779 
00780        for (i = 0; i < marraycount; i++)
00781               mentrycount += marray[i].cont_count;
00782 
00783        *magicp = emalloc(sizeof(**magicp) * mentrycount);
00784 
00785        mentrycount = 0;
00786        for (i = 0; i < marraycount; i++) {
00787               (void)memcpy(*magicp + mentrycount, marray[i].mp,
00788                   marray[i].cont_count * sizeof(**magicp));
00789               mentrycount += marray[i].cont_count;
00790        }
00791 out:
00792        for (i = 0; i < marraycount; i++)
00793               efree(marray[i].mp);
00794        efree(marray);
00795        if (errs) {
00796               *magicp = NULL;
00797               *nmagicp = 0;
00798               return errs;
00799        } else {
00800               *nmagicp = mentrycount;
00801               return 0;
00802        }
00803 
00804 }
00805 
00806 /*
00807  * extend the sign bit if the comparison is to be signed
00808  */
00809 protected uint64_t
00810 file_signextend(struct magic_set *ms, struct magic *m, uint64_t v)
00811 {
00812        if (!(m->flag & UNSIGNED)) {
00813               switch(m->type) {
00814               /*
00815                * Do not remove the casts below.  They are
00816                * vital.  When later compared with the data,
00817                * the sign extension must have happened.
00818                */
00819               case FILE_BYTE:
00820                      v = (char) v;
00821                      break;
00822               case FILE_SHORT:
00823               case FILE_BESHORT:
00824               case FILE_LESHORT:
00825                      v = (short) v;
00826                      break;
00827               case FILE_DATE:
00828               case FILE_BEDATE:
00829               case FILE_LEDATE:
00830               case FILE_MEDATE:
00831               case FILE_LDATE:
00832               case FILE_BELDATE:
00833               case FILE_LELDATE:
00834               case FILE_MELDATE:
00835               case FILE_LONG:
00836               case FILE_BELONG:
00837               case FILE_LELONG:
00838               case FILE_MELONG:
00839               case FILE_FLOAT:
00840               case FILE_BEFLOAT:
00841               case FILE_LEFLOAT:
00842                      v = (int32_t) v;
00843                      break;
00844               case FILE_QUAD:
00845               case FILE_BEQUAD:
00846               case FILE_LEQUAD:
00847               case FILE_QDATE:
00848               case FILE_QLDATE:
00849               case FILE_BEQDATE:
00850               case FILE_BEQLDATE:
00851               case FILE_LEQDATE:
00852               case FILE_LEQLDATE:
00853               case FILE_DOUBLE:
00854               case FILE_BEDOUBLE:
00855               case FILE_LEDOUBLE:
00856                      v = (int64_t) v;
00857                      break;
00858               case FILE_STRING:
00859               case FILE_PSTRING:
00860               case FILE_BESTRING16:
00861               case FILE_LESTRING16:
00862               case FILE_REGEX:
00863               case FILE_SEARCH:
00864               case FILE_DEFAULT:
00865               case FILE_INDIRECT:
00866                      break;
00867               default:
00868                      if (ms->flags & MAGIC_CHECK)
00869                          file_magwarn(ms, "cannot happen: m->type=%d\n",
00870                                 m->type);
00871                      return ~0U;
00872               }
00873        }
00874        return v;
00875 }
00876 
00877 private int
00878 string_modifier_check(struct magic_set *ms, struct magic *m)
00879 {
00880        if ((ms->flags & MAGIC_CHECK) == 0)
00881               return 0;
00882 
00883        switch (m->type) {
00884        case FILE_BESTRING16:
00885        case FILE_LESTRING16:
00886               if (m->str_flags != 0) {
00887                      file_magwarn(ms,
00888                          "no modifiers allowed for 16-bit strings\n");
00889                      return -1;
00890               }
00891               break;
00892        case FILE_STRING:
00893        case FILE_PSTRING:
00894               if ((m->str_flags & REGEX_OFFSET_START) != 0) {
00895                      file_magwarn(ms,
00896                          "'/%c' only allowed on regex and search\n",
00897                          CHAR_REGEX_OFFSET_START);
00898                      return -1;
00899               }
00900               break;
00901        case FILE_SEARCH:
00902               if (m->str_range == 0) {
00903                      file_magwarn(ms,
00904                          "missing range; defaulting to %d\n",
00905                             STRING_DEFAULT_RANGE);
00906                      m->str_range = STRING_DEFAULT_RANGE;
00907                      return -1;
00908               }
00909               break;
00910        case FILE_REGEX:
00911               if ((m->str_flags & STRING_COMPACT_BLANK) != 0) {
00912                      file_magwarn(ms, "'/%c' not allowed on regex\n",
00913                          CHAR_COMPACT_BLANK);
00914                      return -1;
00915               }
00916               if ((m->str_flags & STRING_COMPACT_OPTIONAL_BLANK) != 0) {
00917                      file_magwarn(ms, "'/%c' not allowed on regex\n",
00918                          CHAR_COMPACT_OPTIONAL_BLANK);
00919                      return -1;
00920               }
00921               break;
00922        default:
00923               file_magwarn(ms, "coding error: m->type=%d\n",
00924                   m->type);
00925               return -1;
00926        }
00927        return 0;
00928 }
00929 
00930 private int
00931 get_op(char c)
00932 {
00933        switch (c) {
00934        case '&':
00935               return FILE_OPAND;
00936        case '|':
00937               return FILE_OPOR;
00938        case '^':
00939               return FILE_OPXOR;
00940        case '+':
00941               return FILE_OPADD;
00942        case '-':
00943               return FILE_OPMINUS;
00944        case '*':
00945               return FILE_OPMULTIPLY;
00946        case '/':
00947               return FILE_OPDIVIDE;
00948        case '%':
00949               return FILE_OPMODULO;
00950        default:
00951               return -1;
00952        }
00953 }
00954 
00955 #ifdef ENABLE_CONDITIONALS
00956 private int
00957 get_cond(const char *l, const char **t)
00958 {
00959        static const struct cond_tbl_s {
00960               char name[8];
00961               size_t len;
00962               int cond;
00963        } cond_tbl[] = {
00964               { "if",              2,     COND_IF },
00965               { "elif",     4,     COND_ELIF },
00966               { "else",     4,     COND_ELSE },
00967               { "",         0,     COND_NONE },
00968        };
00969        const struct cond_tbl_s *p;
00970 
00971        for (p = cond_tbl; p->len; p++) {
00972               if (strncmp(l, p->name, p->len) == 0 &&
00973                   isspace((unsigned char)l[p->len])) {
00974                      if (t)
00975                             *t = l + p->len;
00976                      break;
00977               }
00978        }
00979        return p->cond;
00980 }
00981 
00982 private int
00983 check_cond(struct magic_set *ms, int cond, uint32_t cont_level)
00984 {
00985        int last_cond;
00986        last_cond = ms->c.li[cont_level].last_cond;
00987 
00988        switch (cond) {
00989        case COND_IF:
00990               if (last_cond != COND_NONE && last_cond != COND_ELIF) {
00991                      if (ms->flags & MAGIC_CHECK)
00992                             file_magwarn(ms, "syntax error: `if'");
00993                      return -1;
00994               }
00995               last_cond = COND_IF;
00996               break;
00997 
00998        case COND_ELIF:
00999               if (last_cond != COND_IF && last_cond != COND_ELIF) {
01000                      if (ms->flags & MAGIC_CHECK)
01001                             file_magwarn(ms, "syntax error: `elif'");
01002                      return -1;
01003               }
01004               last_cond = COND_ELIF;
01005               break;
01006 
01007        case COND_ELSE:
01008               if (last_cond != COND_IF && last_cond != COND_ELIF) {
01009                      if (ms->flags & MAGIC_CHECK)
01010                             file_magwarn(ms, "syntax error: `else'");
01011                      return -1;
01012               }
01013               last_cond = COND_NONE;
01014               break;
01015 
01016        case COND_NONE:
01017               last_cond = COND_NONE;
01018               break;
01019        }
01020 
01021        ms->c.li[cont_level].last_cond = last_cond;
01022        return 0;
01023 }
01024 #endif /* ENABLE_CONDITIONALS */
01025 
01026 /*
01027  * parse one line from magic file, put into magic[index++] if valid
01028  */
01029 private int
01030 parse(struct magic_set *ms, struct magic_entry **mentryp, uint32_t *nmentryp, 
01031     const char *line, size_t lineno, int action)
01032 {
01033 #ifdef ENABLE_CONDITIONALS
01034        static uint32_t last_cont_level = 0;
01035 #endif
01036        size_t i;
01037        struct magic_entry *me;
01038        struct magic *m;
01039        const char *l = line;
01040        char *t;
01041        int op;
01042        uint32_t cont_level;
01043 
01044        cont_level = 0;
01045 
01046        while (*l == '>') {
01047               ++l;          /* step over */
01048               cont_level++; 
01049        }
01050 #ifdef ENABLE_CONDITIONALS
01051        if (cont_level == 0 || cont_level > last_cont_level)
01052               if (file_check_mem(ms, cont_level) == -1)
01053                      return -1;
01054        last_cont_level = cont_level;
01055 #endif
01056 
01057 #define ALLOC_CHUNK  (size_t)10
01058 #define ALLOC_INCR   (size_t)200
01059 
01060        if (cont_level != 0) {
01061               if (*nmentryp == 0) {
01062                      file_error(ms, 0, "No current entry for continuation");
01063                      return -1;
01064               }
01065               me = &(*mentryp)[*nmentryp - 1];
01066               if (me->cont_count == me->max_count) {
01067                      struct magic *nm;
01068                      size_t cnt = me->max_count + ALLOC_CHUNK;
01069                      nm = erealloc(me->mp, sizeof(*nm) * cnt);
01070                      me->mp = m = nm;
01071                      me->max_count = cnt;
01072               }
01073               m = &me->mp[me->cont_count++];
01074               (void)memset(m, 0, sizeof(*m));
01075               m->cont_level = cont_level;
01076        } else {
01077               if (*nmentryp == maxmagic) {
01078                      struct magic_entry *mp;
01079 
01080                      maxmagic += ALLOC_INCR;
01081                      mp = erealloc(*mentryp, sizeof(*mp) * maxmagic);
01082                      (void)memset(&mp[*nmentryp], 0, sizeof(*mp) * ALLOC_INCR);
01083                      *mentryp = mp;
01084               }
01085               me = &(*mentryp)[*nmentryp];
01086               if (me->mp == NULL) {
01087                      m = safe_emalloc(sizeof(*m), ALLOC_CHUNK, 0);
01088                      me->mp = m;
01089                      me->max_count = ALLOC_CHUNK;
01090               } else
01091                      m = me->mp;
01092               (void)memset(m, 0, sizeof(*m));
01093               m->factor_op = FILE_FACTOR_OP_NONE;
01094               m->cont_level = 0;
01095               me->cont_count = 1;
01096        }
01097        m->lineno = lineno;
01098 
01099        if (*l == '&') {  /* m->cont_level == 0 checked below. */
01100                 ++l;            /* step over */
01101                 m->flag |= OFFADD;
01102         }
01103        if (*l == '(') {
01104               ++l;          /* step over */
01105               m->flag |= INDIR;
01106               if (m->flag & OFFADD)
01107                      m->flag = (m->flag & ~OFFADD) | INDIROFFADD;
01108 
01109               if (*l == '&') {  /* m->cont_level == 0 checked below */
01110                      ++l;            /* step over */
01111                      m->flag |= OFFADD;
01112               }
01113        }
01114        /* Indirect offsets are not valid at level 0. */
01115        if (m->cont_level == 0 && (m->flag & (OFFADD | INDIROFFADD)))
01116               if (ms->flags & MAGIC_CHECK)
01117                      file_magwarn(ms, "relative offset at level 0");
01118 
01119        /* get offset, then skip over it */
01120        m->offset = (uint32_t)strtoul(l, &t, 0);
01121         if (l == t)
01122               if (ms->flags & MAGIC_CHECK)
01123                      file_magwarn(ms, "offset `%s' invalid", l);
01124         l = t;
01125 
01126        if (m->flag & INDIR) {
01127               m->in_type = FILE_LONG;
01128               m->in_offset = 0;
01129               /*
01130                * read [.lbs][+-]nnnnn)
01131                */
01132               if (*l == '.') {
01133                      l++;
01134                      switch (*l) {
01135                      case 'l':
01136                             m->in_type = FILE_LELONG;
01137                             break;
01138                      case 'L':
01139                             m->in_type = FILE_BELONG;
01140                             break;
01141                      case 'm':
01142                             m->in_type = FILE_MELONG;
01143                             break;
01144                      case 'h':
01145                      case 's':
01146                             m->in_type = FILE_LESHORT;
01147                             break;
01148                      case 'H':
01149                      case 'S':
01150                             m->in_type = FILE_BESHORT;
01151                             break;
01152                      case 'c':
01153                      case 'b':
01154                      case 'C':
01155                      case 'B':
01156                             m->in_type = FILE_BYTE;
01157                             break;
01158                      case 'e':
01159                      case 'f':
01160                      case 'g':
01161                             m->in_type = FILE_LEDOUBLE;
01162                             break;
01163                      case 'E':
01164                      case 'F':
01165                      case 'G':
01166                             m->in_type = FILE_BEDOUBLE;
01167                             break;
01168                      case 'i':
01169                             m->in_type = FILE_LEID3;
01170                             break;
01171                      case 'I':
01172                             m->in_type = FILE_BEID3;
01173                             break;
01174                      default:
01175                             if (ms->flags & MAGIC_CHECK)
01176                                    file_magwarn(ms,
01177                                        "indirect offset type `%c' invalid",
01178                                        *l);
01179                             break;
01180                      }
01181                      l++;
01182               }
01183 
01184               m->in_op = 0;
01185               if (*l == '~') {
01186                      m->in_op |= FILE_OPINVERSE;
01187                      l++;
01188               }
01189               if ((op = get_op(*l)) != -1) {
01190                      m->in_op |= op;
01191                      l++;
01192               }
01193               if (*l == '(') {
01194                      m->in_op |= FILE_OPINDIRECT;
01195                      l++;
01196               }
01197               if (isdigit((unsigned char)*l) || *l == '-') {
01198                      m->in_offset = (int32_t)strtol(l, &t, 0);
01199                      if (l == t)
01200                             if (ms->flags & MAGIC_CHECK)
01201                                    file_magwarn(ms,
01202                                        "in_offset `%s' invalid", l);
01203                      l = t;
01204               }
01205               if (*l++ != ')' || 
01206                   ((m->in_op & FILE_OPINDIRECT) && *l++ != ')'))
01207                      if (ms->flags & MAGIC_CHECK)
01208                             file_magwarn(ms,
01209                                 "missing ')' in indirect offset");
01210        }
01211        EATAB;
01212 
01213 #ifdef ENABLE_CONDITIONALS
01214        m->cond = get_cond(l, &l);
01215        if (check_cond(ms, m->cond, cont_level) == -1)
01216               return -1;
01217 
01218        EATAB;
01219 #endif
01220 
01221        if (*l == 'u') {
01222               ++l;
01223               m->flag |= UNSIGNED;
01224        }
01225 
01226        m->type = get_type(l, &l);
01227        if (m->type == FILE_INVALID) {
01228               if (ms->flags & MAGIC_CHECK)
01229                      file_magwarn(ms, "type `%s' invalid", l);
01230               return -1;
01231        }
01232 
01233        /* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */
01234        /* New and improved: ~ & | ^ + - * / % -- exciting, isn't it? */
01235 
01236        m->mask_op = 0;
01237        if (*l == '~') {
01238               if (!IS_LIBMAGIC_STRING(m->type))
01239                      m->mask_op |= FILE_OPINVERSE;
01240               else if (ms->flags & MAGIC_CHECK)
01241                      file_magwarn(ms, "'~' invalid for string types");
01242               ++l;
01243        }
01244        m->str_range = 0;
01245        m->str_flags = 0;
01246        m->num_mask = 0;
01247        if ((op = get_op(*l)) != -1) {
01248               if (!IS_LIBMAGIC_STRING(m->type)) {
01249                      uint64_t val;
01250                      ++l;
01251                      m->mask_op |= op;
01252                      val = (uint64_t)strtoull(l, &t, 0);
01253                      l = t;
01254                      m->num_mask = file_signextend(ms, m, val);
01255                      eatsize(&l);
01256               }
01257               else if (op == FILE_OPDIVIDE) {
01258                      int have_range = 0;
01259                      while (!isspace((unsigned char)*++l)) {
01260                             switch (*l) {
01261                             case '0':  case '1':  case '2':
01262                             case '3':  case '4':  case '5':
01263                             case '6':  case '7':  case '8':
01264                             case '9':
01265                                    if (have_range &&
01266                                        (ms->flags & MAGIC_CHECK))
01267                                           file_magwarn(ms,
01268                                               "multiple ranges");
01269                                    have_range = 1;
01270                                    m->str_range = strtoul(l, &t, 0);
01271                                    if (m->str_range == 0)
01272                                           file_magwarn(ms,
01273                                               "zero range");
01274                                    l = t - 1;
01275                                    break;
01276                             case CHAR_COMPACT_BLANK:
01277                                    m->str_flags |= STRING_COMPACT_BLANK;
01278                                    break;
01279                             case CHAR_COMPACT_OPTIONAL_BLANK:
01280                                    m->str_flags |=
01281                                        STRING_COMPACT_OPTIONAL_BLANK;
01282                                    break;
01283                             case CHAR_IGNORE_LOWERCASE:
01284                                    m->str_flags |= STRING_IGNORE_LOWERCASE;
01285                                    break;
01286                             case CHAR_IGNORE_UPPERCASE:
01287                                    m->str_flags |= STRING_IGNORE_UPPERCASE;
01288                                    break;
01289                             case CHAR_REGEX_OFFSET_START:
01290                                    m->str_flags |= REGEX_OFFSET_START;
01291                                    break;
01292                             default:
01293                                    if (ms->flags & MAGIC_CHECK)
01294                                           file_magwarn(ms,
01295                                           "string extension `%c' invalid",
01296                                           *l);
01297                                    return -1;
01298                             }
01299                             /* allow multiple '/' for readability */
01300                             if (l[1] == '/' &&
01301                                 !isspace((unsigned char)l[2]))
01302                                    l++;
01303                      }
01304                      if (string_modifier_check(ms, m) == -1)
01305                             return -1;
01306               }
01307               else {
01308                      if (ms->flags & MAGIC_CHECK)
01309                             file_magwarn(ms, "invalid string op: %c", *t);
01310                      return -1;
01311               }
01312        }
01313        /*
01314         * We used to set mask to all 1's here, instead let's just not do
01315         * anything if mask = 0 (unless you have a better idea)
01316         */
01317        EATAB;
01318   
01319        switch (*l) {
01320        case '>':
01321        case '<':
01322               m->reln = *l;
01323               ++l;
01324               if (*l == '=') {
01325                      if (ms->flags & MAGIC_CHECK) {
01326                             file_magwarn(ms, "%c= not supported",
01327                                 m->reln);
01328                             return -1;
01329                      }
01330                  ++l;
01331               }
01332               break;
01333        /* Old-style anding: "0 byte &0x80 dynamically linked" */
01334        case '&':
01335        case '^':
01336        case '=':
01337               m->reln = *l;
01338               ++l;
01339               if (*l == '=') {
01340                  /* HP compat: ignore &= etc. */
01341                  ++l;
01342               }
01343               break;
01344        case '!':
01345               m->reln = *l;
01346               ++l;
01347               break;
01348        default:
01349               m->reln = '=';       /* the default relation */
01350               if (*l == 'x' && ((isascii((unsigned char)l[1]) && 
01351                   isspace((unsigned char)l[1])) || !l[1])) {
01352                      m->reln = *l;
01353                      ++l;
01354               }
01355               break;
01356        }
01357        /*
01358         * Grab the value part, except for an 'x' reln.
01359         */
01360        if (m->reln != 'x' && getvalue(ms, m, &l, action))
01361               return -1;
01362 
01363        /*
01364         * TODO finish this macro and start using it!
01365         * #define offsetcheck {if (offset > HOWMANY-1) 
01366         *     magwarn("offset too big"); }
01367         */
01368 
01369        /*
01370         * Now get last part - the description
01371         */
01372        EATAB;
01373        if (l[0] == '\b') {
01374               ++l;
01375               m->flag |= NOSPACE;
01376        } else if ((l[0] == '\\') && (l[1] == 'b')) {
01377               ++l;
01378               ++l;
01379               m->flag |= NOSPACE;
01380        }
01381        for (i = 0; (m->desc[i++] = *l++) != '\0' && i < sizeof(m->desc); )
01382               continue;
01383        if (i == sizeof(m->desc)) {
01384               m->desc[sizeof(m->desc) - 1] = '\0';
01385               if (ms->flags & MAGIC_CHECK)
01386                      file_magwarn(ms, "description `%s' truncated", m->desc);
01387        }
01388 
01389         /*
01390         * We only do this check while compiling, or if any of the magic
01391         * files were not compiled.
01392          */
01393         if (ms->flags & MAGIC_CHECK) {
01394               if (check_format(ms, m) == -1)
01395                      return -1;
01396        }
01397        m->mimetype[0] = '\0';             /* initialise MIME type to none */
01398        if (m->cont_level == 0)
01399               ++(*nmentryp);              /* make room for next */
01400        return 0;
01401 }
01402 
01403 /*
01404  * parse a STRENGTH annotation line from magic file, put into magic[index - 1]
01405  * if valid
01406  */
01407 private int
01408 parse_strength(struct magic_set *ms, struct magic_entry *me, const char *line)
01409 {
01410        const char *l = line;
01411        char *el;
01412        unsigned long factor;
01413        struct magic *m = &me->mp[0];
01414 
01415        if (m->factor_op != FILE_FACTOR_OP_NONE) {
01416               file_magwarn(ms,
01417                   "Current entry already has a strength type: %c %d",
01418                   m->factor_op, m->factor);
01419               return -1;
01420        }
01421        EATAB;
01422        switch (*l) {
01423        case FILE_FACTOR_OP_NONE:
01424        case FILE_FACTOR_OP_PLUS:
01425        case FILE_FACTOR_OP_MINUS:
01426        case FILE_FACTOR_OP_TIMES:
01427        case FILE_FACTOR_OP_DIV:
01428               m->factor_op = *l++;
01429               break;
01430        default:
01431               file_magwarn(ms, "Unknown factor op `%c'", *l);
01432               return -1;
01433        }
01434        EATAB;
01435        factor = strtoul(l, &el, 0);
01436        if (factor > 255) {
01437               file_magwarn(ms, "Too large factor `%lu'", factor);
01438               goto out;
01439        }
01440        if (*el && !isspace((unsigned char)*el)) {
01441               file_magwarn(ms, "Bad factor `%s'", l);
01442               goto out;
01443        }
01444        m->factor = (uint8_t)factor;
01445        if (m->factor == 0 && m->factor_op == FILE_FACTOR_OP_DIV) {
01446               file_magwarn(ms, "Cannot have factor op `%c' and factor %u",
01447                   m->factor_op, m->factor);
01448               goto out;
01449        }
01450        return 0;
01451 out:
01452        m->factor_op = FILE_FACTOR_OP_NONE;
01453        m->factor = 0;
01454        return -1;
01455 }
01456 
01457 /*
01458  * Parse an Apple CREATOR/TYPE annotation from magic file and put it into magic[index - 1]
01459  */
01460 private int
01461 parse_apple(struct magic_set *ms, struct magic_entry *me, const char *line)
01462 {
01463        size_t i;
01464        const char *l = line;
01465        struct magic *m = &me->mp[me->cont_count == 0 ? 0 : me->cont_count - 1];
01466 
01467        if (m->apple[0] != '\0') {
01468               file_magwarn(ms, "Current entry already has a APPLE type `%.8s',"
01469                   " new type `%s'", m->mimetype, l);
01470               return -1;
01471        }      
01472 
01473        EATAB;
01474        for (i = 0; *l && ((isascii((unsigned char)*l) && isalnum((unsigned char)*l))
01475             || strchr("-+/.", *l)) && i < sizeof(m->apple); m->apple[i++] = *l++)
01476               continue;
01477        if (i == sizeof(m->apple) && *l) {
01478               if (ms->flags & MAGIC_CHECK)
01479                      file_magwarn(ms, "APPLE type `%s' truncated %zu",
01480                          line, i);
01481        }
01482 
01483        if (i > 0)
01484               return 0;
01485        else
01486               return -1;
01487 }
01488 
01489 /*
01490  * parse a MIME annotation line from magic file, put into magic[index - 1]
01491  * if valid
01492  */
01493 private int
01494 parse_mime(struct magic_set *ms, struct magic_entry *me, const char *line)
01495 {
01496        size_t i;
01497        const char *l = line;
01498        struct magic *m = &me->mp[me->cont_count == 0 ? 0 : me->cont_count - 1];
01499 
01500        if (m->mimetype[0] != '\0') {
01501               file_magwarn(ms, "Current entry already has a MIME type `%s',"
01502                   " new type `%s'", m->mimetype, l);
01503               return -1;
01504        }      
01505 
01506        EATAB;
01507        for (i = 0; *l && ((isascii((unsigned char)*l) && isalnum((unsigned char)*l))
01508             || strchr("-+/.", *l)) && i < sizeof(m->mimetype); m->mimetype[i++] = *l++)
01509               continue;
01510        if (i == sizeof(m->mimetype)) {
01511               m->desc[sizeof(m->mimetype) - 1] = '\0';
01512               if (ms->flags & MAGIC_CHECK)
01513                      file_magwarn(ms, "MIME type `%s' truncated %zu",
01514                          m->mimetype, i);
01515        } else
01516               m->mimetype[i] = '\0';
01517 
01518        if (i > 0)
01519               return 0;
01520        else
01521               return -1;
01522 }
01523 
01524 private int
01525 check_format_type(const char *ptr, int type)
01526 {
01527        int quad = 0;
01528        if (*ptr == '\0') {
01529               /* Missing format string; bad */
01530               return -1;
01531        }
01532 
01533        switch (type) {
01534        case FILE_FMT_QUAD:
01535               quad = 1;
01536               /*FALLTHROUGH*/
01537        case FILE_FMT_NUM:
01538               if (*ptr == '-')
01539                      ptr++;
01540               if (*ptr == '.')
01541                      ptr++;
01542               while (isdigit((unsigned char)*ptr)) ptr++;
01543               if (*ptr == '.')
01544                      ptr++;
01545               while (isdigit((unsigned char)*ptr)) ptr++;
01546               if (quad) {
01547                      if (*ptr++ != 'l')
01548                             return -1;
01549                      if (*ptr++ != 'l')
01550                             return -1;
01551               }
01552        
01553               switch (*ptr++) {
01554               case 'l':
01555                      switch (*ptr++) {
01556                      case 'i':
01557                      case 'd':
01558                      case 'u':
01559                      case 'x':
01560                      case 'X':
01561                             return 0;
01562                      default:
01563                             return -1;
01564                      }
01565               
01566               case 'h':
01567                      switch (*ptr++) {
01568                      case 'h':
01569                             switch (*ptr++) {
01570                             case 'i':
01571                             case 'd':
01572                             case 'u':
01573                             case 'x':
01574                             case 'X':
01575                                    return 0;
01576                             default:
01577                                    return -1;
01578                             }
01579                      case 'd':
01580                             return 0;
01581                      default:
01582                             return -1;
01583                      }
01584 
01585               case 'i':
01586               case 'c':
01587               case 'd':
01588               case 'u':
01589               case 'x':
01590               case 'X':
01591                      return 0;
01592                      
01593               default:
01594                      return -1;
01595               }
01596               
01597        case FILE_FMT_FLOAT:
01598        case FILE_FMT_DOUBLE:
01599               if (*ptr == '-')
01600                      ptr++;
01601               if (*ptr == '.')
01602                      ptr++;
01603               while (isdigit((unsigned char)*ptr)) ptr++;
01604               if (*ptr == '.')
01605                      ptr++;
01606               while (isdigit((unsigned char)*ptr)) ptr++;
01607        
01608               switch (*ptr++) {
01609               case 'e':
01610               case 'E':
01611               case 'f':
01612               case 'F':
01613               case 'g':
01614               case 'G':
01615                      return 0;
01616                      
01617               default:
01618                      return -1;
01619               }
01620               
01621 
01622        case FILE_FMT_STR:
01623               if (*ptr == '-')
01624                      ptr++;
01625               while (isdigit((unsigned char )*ptr))
01626                      ptr++;
01627               if (*ptr == '.') {
01628                      ptr++;
01629                      while (isdigit((unsigned char )*ptr))
01630                             ptr++;
01631               }
01632               
01633               switch (*ptr++) {
01634               case 's':
01635                      return 0;
01636               default:
01637                      return -1;
01638               }
01639               
01640        default:
01641               /* internal error */
01642               abort();
01643        }
01644        /*NOTREACHED*/
01645        return -1;
01646 }
01647        
01648 /*
01649  * Check that the optional printf format in description matches
01650  * the type of the magic.
01651  */
01652 private int
01653 check_format(struct magic_set *ms, struct magic *m)
01654 {
01655        char *ptr;
01656 
01657        for (ptr = m->desc; *ptr; ptr++)
01658               if (*ptr == '%')
01659                      break;
01660        if (*ptr == '\0') {
01661               /* No format string; ok */
01662               return 1;
01663        }
01664 
01665        assert(file_nformats == file_nnames);
01666 
01667        if (m->type >= file_nformats) {
01668               file_magwarn(ms, "Internal error inconsistency between "
01669                   "m->type and format strings");        
01670               return -1;
01671        }
01672        if (file_formats[m->type] == FILE_FMT_NONE) {
01673               file_magwarn(ms, "No format string for `%s' with description "
01674                   "`%s'", m->desc, file_names[m->type]);
01675               return -1;
01676        }
01677 
01678        ptr++;
01679        if (check_format_type(ptr, file_formats[m->type]) == -1) {
01680               /*
01681                * TODO: this error message is unhelpful if the format
01682                * string is not one character long
01683                */
01684               file_magwarn(ms, "Printf format `%c' is not valid for type "
01685                   "`%s' in description `%s'", *ptr ? *ptr : '?',
01686                   file_names[m->type], m->desc);
01687               return -1;
01688        }
01689        
01690        for (; *ptr; ptr++) {
01691               if (*ptr == '%') {
01692                      file_magwarn(ms,
01693                          "Too many format strings (should have at most one) "
01694                          "for `%s' with description `%s'",
01695                          file_names[m->type], m->desc);
01696                      return -1;
01697               }
01698        }
01699        return 0;
01700 }
01701 
01702 /* 
01703  * Read a numeric value from a pointer, into the value union of a magic 
01704  * pointer, according to the magic type.  Update the string pointer to point 
01705  * just after the number read.  Return 0 for success, non-zero for failure.
01706  */
01707 private int
01708 getvalue(struct magic_set *ms, struct magic *m, const char **p, int action)
01709 {
01710        switch (m->type) {
01711        case FILE_BESTRING16:
01712        case FILE_LESTRING16:
01713        case FILE_STRING:
01714        case FILE_PSTRING:
01715        case FILE_REGEX:
01716        case FILE_SEARCH:
01717               *p = getstr(ms, m, *p, action == FILE_COMPILE);
01718               if (*p == NULL) {
01719                      if (ms->flags & MAGIC_CHECK)
01720                             file_magwarn(ms, "cannot get string from `%s'",
01721                                 m->value.s);
01722                      return -1;
01723               }
01724               return 0;
01725        case FILE_FLOAT:
01726        case FILE_BEFLOAT:
01727        case FILE_LEFLOAT:
01728               if (m->reln != 'x') {
01729                      char *ep;
01730 #ifdef HAVE_STRTOF
01731                      m->value.f = strtof(*p, &ep);
01732 #else
01733                      m->value.f = (float)strtod(*p, &ep);
01734 #endif
01735                      *p = ep;
01736               }
01737               return 0;
01738        case FILE_DOUBLE:
01739        case FILE_BEDOUBLE:
01740        case FILE_LEDOUBLE:
01741               if (m->reln != 'x') {
01742                      char *ep;
01743                      m->value.d = strtod(*p, &ep);
01744                      *p = ep;
01745               }
01746               return 0;
01747        default:
01748               if (m->reln != 'x') {
01749                      char *ep;
01750                      m->value.q = file_signextend(ms, m,
01751                          (uint64_t)strtoull(*p, &ep, 0));
01752                      *p = ep;
01753                      eatsize(p);
01754               }
01755               return 0;
01756        }
01757 }
01758 
01759 /*
01760  * Convert a string containing C character escapes.  Stop at an unescaped
01761  * space or tab.
01762  * Copy the converted version to "m->value.s", and the length in m->vallen.
01763  * Return updated scan pointer as function result. Warn if set.
01764  */
01765 private const char *
01766 getstr(struct magic_set *ms, struct magic *m, const char *s, int warn)
01767 {
01768        const char *origs = s;
01769        char   *p = m->value.s;
01770        size_t  plen = sizeof(m->value.s);
01771        char   *origp = p;
01772        char   *pmax = p + plen - 1;
01773        int    c;
01774        int    val;
01775 
01776        while ((c = *s++) != '\0') {
01777               if (isspace((unsigned char) c))
01778                      break;
01779               if (p >= pmax) {
01780                      file_error(ms, 0, "string too long: `%s'", origs);
01781                      return NULL;
01782               }
01783               if (c == '\\') {
01784                      switch(c = *s++) {
01785 
01786                      case '\0':
01787                             if (warn)
01788                                    file_magwarn(ms, "incomplete escape");
01789                             goto out;
01790 
01791                      case '\t':
01792                             if (warn) {
01793                                    file_magwarn(ms,
01794                                        "escaped tab found, use \\t instead");
01795                                    warn = 0;     /* already did */
01796                             }
01797                             /*FALLTHROUGH*/
01798                      default:
01799                             if (warn) {
01800                                    if (isprint((unsigned char)c)) {
01801                                           /* Allow escaping of 
01802                                            * ``relations'' */
01803                                           if (strchr("<>&^=!", c)
01804                                               == NULL) {
01805                                                  file_magwarn(ms, "no "
01806                                                      "need to escape "
01807                                                      "`%c'", c);
01808                                           }
01809                                    } else {
01810                                           file_magwarn(ms,
01811                                               "unknown escape sequence: "
01812                                               "\\%03o", c);
01813                                    }
01814                             }
01815                             /*FALLTHROUGH*/
01816                      /* space, perhaps force people to use \040? */
01817                      case ' ':
01818 #if 0
01819                      /*
01820                       * Other things people escape, but shouldn't need to,
01821                       * so we disallow them
01822                       */
01823                      case '\'':
01824                      case '"':
01825                      case '?':
01826 #endif
01827                      /* Relations */
01828                      case '>':
01829                      case '<':
01830                      case '&':
01831                      case '^':
01832                      case '=':
01833                      case '!':
01834                      /* and baskslash itself */
01835                      case '\\':
01836                             *p++ = (char) c;
01837                             break;
01838 
01839                      case 'a':
01840                             *p++ = '\a';
01841                             break;
01842 
01843                      case 'b':
01844                             *p++ = '\b';
01845                             break;
01846 
01847                      case 'f':
01848                             *p++ = '\f';
01849                             break;
01850 
01851                      case 'n':
01852                             *p++ = '\n';
01853                             break;
01854 
01855                      case 'r':
01856                             *p++ = '\r';
01857                             break;
01858 
01859                      case 't':
01860                             *p++ = '\t';
01861                             break;
01862 
01863                      case 'v':
01864                             *p++ = '\v';
01865                             break;
01866 
01867                      /* \ and up to 3 octal digits */
01868                      case '0':
01869                      case '1':
01870                      case '2':
01871                      case '3':
01872                      case '4':
01873                      case '5':
01874                      case '6':
01875                      case '7':
01876                             val = c - '0';
01877                             c = *s++;  /* try for 2 */
01878                             if (c >= '0' && c <= '7') {
01879                                    val = (val << 3) | (c - '0');
01880                                    c = *s++;  /* try for 3 */
01881                                    if (c >= '0' && c <= '7')
01882                                           val = (val << 3) | (c-'0');
01883                                    else
01884                                           --s;
01885                             }
01886                             else
01887                                    --s;
01888                             *p++ = (char)val;
01889                             break;
01890 
01891                      /* \x and up to 2 hex digits */
01892                      case 'x':
01893                             val = 'x';    /* Default if no digits */
01894                             c = hextoint(*s++);  /* Get next char */
01895                             if (c >= 0) {
01896                                    val = c;
01897                                    c = hextoint(*s++);
01898                                    if (c >= 0)
01899                                           val = (val << 4) + c;
01900                                    else
01901                                           --s;
01902                             } else
01903                                    --s;
01904                             *p++ = (char)val;
01905                             break;
01906                      }
01907               } else
01908                      *p++ = (char)c;
01909        }
01910 out:
01911        *p = '\0';
01912        m->vallen = p - origp;
01913        if (m->type == FILE_PSTRING)
01914               m->vallen++;
01915        return s;
01916 }
01917 
01918 
01919 /* Single hex char to int; -1 if not a hex char. */
01920 private int
01921 hextoint(int c)
01922 {
01923        if (!isascii((unsigned char) c))
01924               return -1;
01925        if (isdigit((unsigned char) c))
01926               return c - '0';
01927        if ((c >= 'a') && (c <= 'f'))
01928               return c + 10 - 'a';
01929        if (( c>= 'A') && (c <= 'F'))
01930               return c + 10 - 'A';
01931        return -1;
01932 }
01933 
01934 
01935 /*
01936  * Print a string containing C character escapes.
01937  */
01938 protected void
01939 file_showstr(FILE *fp, const char *s, size_t len)
01940 {
01941        char   c;
01942 
01943        for (;;) {
01944               c = *s++;
01945               if (len == ~0U) {
01946                      if (c == '\0')
01947                             break;
01948               }
01949               else  {
01950                      if (len-- == 0)
01951                             break;
01952               }
01953               if (c >= 040 && c <= 0176)  /* TODO isprint && !iscntrl */
01954                      (void) fputc(c, fp);
01955               else {
01956                      (void) fputc('\\', fp);
01957                      switch (c) {
01958                      case '\a':
01959                             (void) fputc('a', fp);
01960                             break;
01961 
01962                      case '\b':
01963                             (void) fputc('b', fp);
01964                             break;
01965 
01966                      case '\f':
01967                             (void) fputc('f', fp);
01968                             break;
01969 
01970                      case '\n':
01971                             (void) fputc('n', fp);
01972                             break;
01973 
01974                      case '\r':
01975                             (void) fputc('r', fp);
01976                             break;
01977 
01978                      case '\t':
01979                             (void) fputc('t', fp);
01980                             break;
01981 
01982                      case '\v':
01983                             (void) fputc('v', fp);
01984                             break;
01985 
01986                      default:
01987                             (void) fprintf(fp, "%.3o", c & 0377);
01988                             break;
01989                      }
01990               }
01991        }
01992 }
01993 
01994 /*
01995  * eatsize(): Eat the size spec from a number [eg. 10UL]
01996  */
01997 private void
01998 eatsize(const char **p)
01999 {
02000        const char *l = *p;
02001 
02002        if (LOWCASE(*l) == 'u') 
02003               l++;
02004 
02005        switch (LOWCASE(*l)) {
02006        case 'l':    /* long */
02007        case 's':    /* short */
02008        case 'h':    /* short */
02009        case 'b':    /* char/byte */
02010        case 'c':    /* char/byte */
02011               l++;
02012               /*FALLTHROUGH*/
02013        default:
02014               break;
02015        }
02016 
02017        *p = l;
02018 }
02019 
02020 /*
02021  * handle a compiled file.
02022  * return -1 = error
02023  * return 1  = memory structure you can free
02024  * return 3  = bundled library from PHP
02025  */
02026 private int
02027 apprentice_map(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp,
02028     const char *fn)
02029 {
02030        uint32_t *ptr;
02031        uint32_t version;
02032        int needsbyteswap;
02033        char *dbname = NULL;
02034        void *mm = NULL;
02035        int   ret = 0;
02036        php_stream *stream = NULL;
02037        php_stream_statbuf st;
02038 
02039 
02040        TSRMLS_FETCH();
02041 
02042        if (fn == NULL) {
02043               mm = (void *)&php_magic_database;
02044               ret = 3;
02045               goto internal_loaded;
02046        }
02047 
02048        dbname = mkdbname(ms, fn, 0);
02049        if (dbname == NULL)
02050               goto error2;
02051 
02052 #if PHP_API_VERSION < 20100412
02053               stream = php_stream_open_wrapper((char *)fn, "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL);
02054 #else
02055               stream = php_stream_open_wrapper((char *)fn, "rb", REPORT_ERRORS, NULL);
02056 #endif
02057 
02058        if (!stream) {
02059               goto error2;
02060        }
02061 
02062        if (php_stream_stat(stream, &st) < 0) {
02063               file_error(ms, errno, "cannot stat `%s'", dbname);
02064               goto error1;
02065        }
02066 
02067        if (st.sb.st_size < 8) {
02068               file_error(ms, 0, "file `%s' is too small", dbname);
02069               goto error1;
02070        }
02071 
02072        mm = emalloc((size_t)st.sb.st_size);
02073        ret = 1;
02074        if (php_stream_read(stream, mm, (size_t)st.sb.st_size) != (size_t)st.sb.st_size) {
02075               file_badread(ms);
02076               goto error1;
02077        }
02078 
02079        php_stream_close(stream);
02080        stream = NULL;
02081 
02082 internal_loaded:
02083        *magicp = mm;
02084        ptr = (uint32_t *)(void *)*magicp;
02085        if (*ptr != MAGICNO) {
02086               if (swap4(*ptr) != MAGICNO) {
02087                      file_error(ms, 0, "bad magic in `%s'", dbname);
02088                      goto error1;
02089               }
02090               needsbyteswap = 1;
02091        } else {
02092               needsbyteswap = 0;
02093        }
02094 
02095        if (needsbyteswap)
02096               version = swap4(ptr[1]);
02097        else
02098               version = ptr[1];
02099 
02100        if (version != VERSIONNO) {
02101               file_error(ms, 0, "File %d.%d supports only version %d magic "
02102                   "files. `%s' is version %d", FILE_VERSION_MAJOR, patchlevel,
02103                   VERSIONNO, dbname, version);
02104               goto error1;
02105        }
02106 
02107        /* php_magic_database is a const, performing writes will segfault. This is for big-endian
02108        machines only, PPC and Sparc specifically. Consider static variable or MINIT in
02109        future. */
02110        if (needsbyteswap && fn == NULL) {
02111               mm = emalloc(sizeof(php_magic_database));
02112               mm = memcpy(mm, php_magic_database, sizeof(php_magic_database));
02113               *magicp = mm;
02114               ret = 1;
02115        }
02116 
02117        if (fn == NULL) {
02118               *nmagicp = (sizeof(php_magic_database) / sizeof(struct magic));
02119        } else {
02120               *nmagicp = (uint32_t)(st.sb.st_size / sizeof(struct magic));
02121        }
02122        if (*nmagicp > 0) {
02123               (*nmagicp)--;
02124        }
02125        (*magicp)++;
02126        if (needsbyteswap) {
02127               byteswap(*magicp, *nmagicp);
02128        }
02129 
02130        if (dbname) {
02131               efree(dbname);
02132        }
02133        return ret;
02134 
02135 error1:
02136        if (stream) {
02137               php_stream_close(stream);
02138        }
02139 
02140        if (mm && ret == 1) {
02141               efree(mm);
02142        } else {
02143               *magicp = NULL;
02144               *nmagicp = 0;
02145        }
02146 error2:
02147        if (dbname) {
02148               efree(dbname);
02149        }
02150        return -1;
02151 }
02152 
02153 private const uint32_t ar[] = {
02154     MAGICNO, VERSIONNO
02155 };
02156 /*
02157  * handle an mmaped file.
02158  */
02159 private int
02160 apprentice_compile(struct magic_set *ms, struct magic **magicp,
02161     uint32_t *nmagicp, const char *fn)
02162 {
02163        char *dbname;
02164        int rv = -1;
02165        php_stream *stream;
02166 
02167        TSRMLS_FETCH();
02168 
02169        dbname = mkdbname(ms, fn, 0);
02170 
02171        if (dbname == NULL) {
02172               goto out;
02173        }
02174 
02175 /* wb+ == O_WRONLY|O_CREAT|O_TRUNC|O_BINARY */
02176 #if PHP_API_VERSION < 20100412
02177        stream = php_stream_open_wrapper((char *)fn, "wb+", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL);
02178 #else
02179        stream = php_stream_open_wrapper((char *)fn, "wb+", REPORT_ERRORS, NULL);
02180 #endif
02181 
02182        if (!stream) {
02183               file_error(ms, errno, "cannot open `%s'", dbname);
02184               goto out;
02185        }
02186 
02187        if (php_stream_write(stream, (char *)ar, sizeof(ar)) != (ssize_t)sizeof(ar)) {
02188               file_error(ms, errno, "error writing `%s'", dbname);
02189               goto out;
02190        }
02191 
02192        if (php_stream_seek(stream,(off_t)sizeof(struct magic), SEEK_SET) != sizeof(struct magic)) {
02193               file_error(ms, errno, "error seeking `%s'", dbname);
02194               goto out;
02195        }
02196 
02197        if (php_stream_write(stream, (char *)*magicp, (sizeof(struct magic) * *nmagicp) != (ssize_t)(sizeof(struct magic) * *nmagicp))) {
02198               file_error(ms, errno, "error writing `%s'", dbname);
02199               goto out;
02200        }
02201 
02202        php_stream_close(stream);
02203 
02204        rv = 0;
02205 out:
02206        efree(dbname);
02207        return rv;
02208 }
02209 
02210 private const char ext[] = ".mgc";
02211 /*
02212  * make a dbname
02213  */
02214 private char *
02215 mkdbname(struct magic_set *ms, const char *fn, int strip)
02216 {
02217        const char *p, *q;
02218        char *buf;
02219        TSRMLS_FETCH();
02220 
02221        if (strip) {
02222               if ((p = strrchr(fn, '/')) != NULL)
02223                      fn = ++p;
02224        }
02225 
02226        for (q = fn; *q; q++)
02227               continue;
02228        /* Look for .mgc */
02229        for (p = ext + sizeof(ext) - 1; p >= ext && q >= fn; p--, q--)
02230               if (*p != *q)
02231                      break;
02232 
02233        /* Did not find .mgc, restore q */
02234        if (p >= ext)
02235               while (*q)
02236                      q++;
02237 
02238        q++;
02239        /* Compatibility with old code that looked in .mime */
02240        if (ms->flags & MAGIC_MIME) {
02241               spprintf(&buf, MAXPATHLEN, "%.*s.mime%s", (int)(q - fn), fn, ext);
02242               if (VCWD_ACCESS(buf, R_OK) != -1) {
02243                      ms->flags &= MAGIC_MIME_TYPE;
02244                      return buf;
02245               }
02246               efree(buf);
02247        }
02248        spprintf(&buf, MAXPATHLEN, "%.*s%s", (int)(q - fn), fn, ext);
02249 
02250        /* Compatibility with old code that looked in .mime */
02251        if (strstr(p, ".mime") != NULL)
02252               ms->flags &= MAGIC_MIME_TYPE;
02253        return buf;
02254 }
02255 
02256 /*
02257  * Byteswap an mmap'ed file if needed
02258  */
02259 private void
02260 byteswap(struct magic *magic, uint32_t nmagic)
02261 {
02262        uint32_t i;
02263        for (i = 0; i < nmagic; i++)
02264               bs1(&magic[i]);
02265 }
02266 
02267 /*
02268  * swap a short
02269  */
02270 private uint16_t
02271 swap2(uint16_t sv)
02272 {
02273        uint16_t rv;
02274        uint8_t *s = (uint8_t *)(void *)&sv; 
02275        uint8_t *d = (uint8_t *)(void *)&rv; 
02276        d[0] = s[1];
02277        d[1] = s[0];
02278        return rv;
02279 }
02280 
02281 /*
02282  * swap an int
02283  */
02284 private uint32_t
02285 swap4(uint32_t sv)
02286 {
02287        uint32_t rv;
02288        uint8_t *s = (uint8_t *)(void *)&sv; 
02289        uint8_t *d = (uint8_t *)(void *)&rv; 
02290        d[0] = s[3];
02291        d[1] = s[2];
02292        d[2] = s[1];
02293        d[3] = s[0];
02294        return rv;
02295 }
02296 
02297 /*
02298  * swap a quad
02299  */
02300 private uint64_t
02301 swap8(uint64_t sv)
02302 {
02303        uint64_t rv;
02304        uint8_t *s = (uint8_t *)(void *)&sv; 
02305        uint8_t *d = (uint8_t *)(void *)&rv; 
02306 #if 0
02307        d[0] = s[3];
02308        d[1] = s[2];
02309        d[2] = s[1];
02310        d[3] = s[0];
02311        d[4] = s[7];
02312        d[5] = s[6];
02313        d[6] = s[5];
02314        d[7] = s[4];
02315 #else
02316        d[0] = s[7];
02317        d[1] = s[6];
02318        d[2] = s[5];
02319        d[3] = s[4];
02320        d[4] = s[3];
02321        d[5] = s[2];
02322        d[6] = s[1];
02323        d[7] = s[0];
02324 #endif
02325        return rv;
02326 }
02327 
02328 /*
02329  * byteswap a single magic entry
02330  */
02331 private void
02332 bs1(struct magic *m)
02333 {
02334        m->cont_level = swap2(m->cont_level);
02335        m->offset = swap4((uint32_t)m->offset);
02336        m->in_offset = swap4((uint32_t)m->in_offset);
02337        m->lineno = swap4((uint32_t)m->lineno);
02338        if (IS_LIBMAGIC_STRING(m->type)) {
02339               m->str_range = swap4(m->str_range);
02340               m->str_flags = swap4(m->str_flags);
02341        }
02342        else {
02343               m->value.q = swap8(m->value.q);
02344               m->num_mask = swap8(m->num_mask);
02345        }
02346 }