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