1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1985-2010 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.opensource.org/licenses/cpl1.0.txt * 11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * Glenn Fowler <gsf@research.att.com> * 18 * David Korn <dgk@research.att.com> * 19 * Phong Vo <kpv@research.att.com> * 20 * * 21 ***********************************************************************/ 22 #pragma prototyped 23 /* 24 * Glenn Fowler 25 * AT&T Research 26 * 27 * library interface to file 28 * 29 * the sum of the hacks {s5,v10,planix} is _____ than the parts 30 */ 31 32 static const char id[] = "\n@(#)$Id: magic library (AT&T Research) 2008-09-10 $\0\n"; 33 34 static const char lib[] = "libast:magic"; 35 36 #include <ast.h> 37 #include <ctype.h> 38 #include <ccode.h> 39 #include <dt.h> 40 #include <modex.h> 41 #include <error.h> 42 #include <regex.h> 43 #include <swap.h> 44 45 #define T(m) (*m?ERROR_translate(NiL,NiL,lib,m):m) 46 47 #define match(s,p) strgrpmatch(s,p,NiL,0,STR_LEFT|STR_RIGHT|STR_ICASE) 48 49 #define MAXNEST 10 /* { ... } nesting limit */ 50 #define MINITEM 4 /* magic buffer rounding */ 51 52 typedef struct /* identifier dictionary entry */ 53 { 54 const char name[16]; /* identifier name */ 55 int value; /* identifier value */ 56 Dtlink_t link; /* dictionary link */ 57 } Info_t; 58 59 typedef struct Edit /* edit substitution */ 60 { 61 struct Edit* next; /* next in list */ 62 regex_t* from; /* from pattern */ 63 } Edit_t; 64 65 struct Entry; 66 67 typedef struct /* loop info */ 68 { 69 struct Entry* lab; /* call this function */ 70 int start; /* start here */ 71 int size; /* increment by this amount */ 72 int count; /* dynamic loop count */ 73 int offset; /* dynamic offset */ 74 } Loop_t; 75 76 typedef struct Entry /* magic file entry */ 77 { 78 struct Entry* next; /* next in list */ 79 char* expr; /* offset expression */ 80 union 81 { 82 unsigned long num; 83 char* str; 84 struct Entry* lab; 85 regex_t* sub; 86 Loop_t* loop; 87 } value; /* comparison value */ 88 char* desc; /* file description */ 89 char* mime; /* file mime type */ 90 unsigned long offset; /* offset in bytes */ 91 unsigned long mask; /* mask before compare */ 92 char cont; /* continuation operation */ 93 char type; /* datum type */ 94 char op; /* comparison operation */ 95 char nest; /* { or } nesting operation */ 96 char swap; /* forced swap order */ 97 } Entry_t; 98 99 #define CC_BIT 5 100 101 #if (CC_MAPS*CC_BIT) <= (CHAR_BIT*2) 102 typedef unsigned short Cctype_t; 103 #else 104 typedef unsigned long Cctype_t; 105 #endif 106 107 #define CC_text 0x01 108 #define CC_control 0x02 109 #define CC_latin 0x04 110 #define CC_binary 0x08 111 #define CC_utf_8 0x10 112 113 #define CC_notext CC_text /* CC_text is flipped before checking */ 114 115 #define CC_MASK (CC_binary|CC_latin|CC_control|CC_text) 116 117 #define CCTYPE(c) (((c)>0240)?CC_binary:((c)>=0200)?CC_latin:((c)<040&&(c)!=007&&(c)!=011&&(c)!=012&&(c)!=013&&(c)!=015)?CC_control:CC_text) 118 119 #define ID_NONE 0 120 #define ID_ASM 1 121 #define ID_C 2 122 #define ID_COBOL 3 123 #define ID_COPYBOOK 4 124 #define ID_CPLUSPLUS 5 125 #define ID_FORTRAN 6 126 #define ID_HTML 7 127 #define ID_INCL1 8 128 #define ID_INCL2 9 129 #define ID_INCL3 10 130 #define ID_MAM1 11 131 #define ID_MAM2 12 132 #define ID_MAM3 13 133 #define ID_NOTEXT 14 134 #define ID_PL1 15 135 #define ID_YACC 16 136 137 #define ID_MAX ID_YACC 138 139 #define INFO_atime 1 140 #define INFO_blocks 2 141 #define INFO_ctime 3 142 #define INFO_fstype 4 143 #define INFO_gid 5 144 #define INFO_mode 6 145 #define INFO_mtime 7 146 #define INFO_name 8 147 #define INFO_nlink 9 148 #define INFO_size 10 149 #define INFO_uid 11 150 151 #define _MAGIC_PRIVATE_ \ 152 Magicdisc_t* disc; /* discipline */ \ 153 Vmalloc_t* vm; /* vmalloc region */ \ 154 Entry_t* magic; /* parsed magic table */ \ 155 Entry_t* magiclast; /* last entry in magic */ \ 156 char* mime; /* MIME type */ \ 157 unsigned char* x2n; /* CC_ALIEN=>CC_NATIVE */ \ 158 char fbuf[SF_BUFSIZE + 1]; /* file data */ \ 159 char xbuf[SF_BUFSIZE + 1]; /* indirect file data */ \ 160 char nbuf[256]; /* !CC_NATIVE data */ \ 161 char mbuf[64]; /* mime string */ \ 162 char sbuf[64]; /* type suffix string */ \ 163 char tbuf[2 * PATH_MAX]; /* type string */ \ 164 Cctype_t cctype[UCHAR_MAX + 1]; /* char code types */ \ 165 unsigned int count[UCHAR_MAX + 1]; /* char frequency count */ \ 166 unsigned int multi[UCHAR_MAX + 1]; /* muti char count */ \ 167 int keep[MAXNEST]; /* ckmagic nest stack */ \ 168 char* cap[MAXNEST]; /* ckmagic mime stack */ \ 169 char* msg[MAXNEST]; /* ckmagic text stack */ \ 170 Entry_t* ret[MAXNEST]; /* ckmagic return stack */ \ 171 int fbsz; /* fbuf size */ \ 172 int fbmx; /* fbuf max size */ \ 173 int xbsz; /* xbuf size */ \ 174 int swap; /* swap() operation */ \ 175 unsigned long flags; /* disc+open flags */ \ 176 long xoff; /* xbuf offset */ \ 177 int identifier[ID_MAX + 1]; /* Info_t identifier */ \ 178 Sfio_t* fp; /* fbuf fp */ \ 179 Sfio_t* tmp; /* tmp string */ \ 180 regdisc_t redisc; /* regex discipline */ \ 181 Dtdisc_t dtdisc; /* dict discipline */ \ 182 Dt_t* idtab; /* identifier dict */ \ 183 Dt_t* infotab; /* info keyword dict */ 184 185 #include <magic.h> 186 187 static Info_t dict[] = /* keyword dictionary */ 188 { 189 { "COMMON", ID_FORTRAN }, 190 { "COMPUTE", ID_COBOL }, 191 { "COMP", ID_COPYBOOK }, 192 { "COMPUTATIONAL",ID_COPYBOOK }, 193 { "DCL", ID_PL1 }, 194 { "DEFINED", ID_PL1 }, 195 { "DIMENSION", ID_FORTRAN }, 196 { "DIVISION", ID_COBOL }, 197 { "FILLER", ID_COPYBOOK }, 198 { "FIXED", ID_PL1 }, 199 { "FUNCTION", ID_FORTRAN }, 200 { "HTML", ID_HTML }, 201 { "INTEGER", ID_FORTRAN }, 202 { "MAIN", ID_PL1 }, 203 { "OPTIONS", ID_PL1 }, 204 { "PERFORM", ID_COBOL }, 205 { "PIC", ID_COPYBOOK }, 206 { "REAL", ID_FORTRAN }, 207 { "REDEFINES", ID_COPYBOOK }, 208 { "S9", ID_COPYBOOK }, 209 { "SECTION", ID_COBOL }, 210 { "SELECT", ID_COBOL }, 211 { "SUBROUTINE", ID_FORTRAN }, 212 { "TEXT", ID_ASM }, 213 { "VALUE", ID_COPYBOOK }, 214 { "attr", ID_MAM3 }, 215 { "binary", ID_YACC }, 216 { "block", ID_FORTRAN }, 217 { "bss", ID_ASM }, 218 { "byte", ID_ASM }, 219 { "char", ID_C }, 220 { "class", ID_CPLUSPLUS }, 221 { "clr", ID_NOTEXT }, 222 { "comm", ID_ASM }, 223 { "common", ID_FORTRAN }, 224 { "data", ID_ASM }, 225 { "dimension", ID_FORTRAN }, 226 { "done", ID_MAM2 }, 227 { "double", ID_C }, 228 { "even", ID_ASM }, 229 { "exec", ID_MAM3 }, 230 { "extern", ID_C }, 231 { "float", ID_C }, 232 { "function", ID_FORTRAN }, 233 { "globl", ID_ASM }, 234 { "h", ID_INCL3 }, 235 { "html", ID_HTML }, 236 { "include", ID_INCL1 }, 237 { "int", ID_C }, 238 { "integer", ID_FORTRAN }, 239 { "jmp", ID_NOTEXT }, 240 { "left", ID_YACC }, 241 { "libc", ID_INCL2 }, 242 { "long", ID_C }, 243 { "make", ID_MAM1 }, 244 { "mov", ID_NOTEXT }, 245 { "private", ID_CPLUSPLUS }, 246 { "public", ID_CPLUSPLUS }, 247 { "real", ID_FORTRAN }, 248 { "register", ID_C }, 249 { "right", ID_YACC }, 250 { "sfio", ID_INCL2 }, 251 { "static", ID_C }, 252 { "stdio", ID_INCL2 }, 253 { "struct", ID_C }, 254 { "subroutine", ID_FORTRAN }, 255 { "sys", ID_NOTEXT }, 256 { "term", ID_YACC }, 257 { "text", ID_ASM }, 258 { "tst", ID_NOTEXT }, 259 { "type", ID_YACC }, 260 { "typedef", ID_C }, 261 { "u", ID_INCL2 }, 262 { "union", ID_YACC }, 263 { "void", ID_C }, 264 }; 265 266 static Info_t info[] = 267 { 268 { "atime", INFO_atime }, 269 { "blocks", INFO_blocks }, 270 { "ctime", INFO_ctime }, 271 { "fstype", INFO_fstype }, 272 { "gid", INFO_gid }, 273 { "mode", INFO_mode }, 274 { "mtime", INFO_mtime }, 275 { "name", INFO_name }, 276 { "nlink", INFO_nlink }, 277 { "size", INFO_size }, 278 { "uid", INFO_uid }, 279 }; 280 281 /* 282 * return pointer to data at offset off and size siz 283 */ 284 285 static char* 286 getdata(register Magic_t* mp, register long off, register int siz) 287 { 288 register long n; 289 290 if (off < 0) 291 return 0; 292 if (off + siz <= mp->fbsz) 293 return mp->fbuf + off; 294 if (off < mp->xoff || off + siz > mp->xoff + mp->xbsz) 295 { 296 if (off + siz > mp->fbmx) 297 return 0; 298 n = (off / (SF_BUFSIZE / 2)) * (SF_BUFSIZE / 2); 299 if (sfseek(mp->fp, n, SEEK_SET) != n) 300 return 0; 301 if ((mp->xbsz = sfread(mp->fp, mp->xbuf, sizeof(mp->xbuf) - 1)) < 0) 302 { 303 mp->xoff = 0; 304 mp->xbsz = 0; 305 return 0; 306 } 307 mp->xbuf[mp->xbsz] = 0; 308 mp->xoff = n; 309 if (off + siz > mp->xoff + mp->xbsz) 310 return 0; 311 } 312 return mp->xbuf + off - mp->xoff; 313 } 314 315 /* 316 * @... evaluator for strexpr() 317 */ 318 319 static long 320 indirect(const char* cs, char** e, void* handle) 321 { 322 register char* s = (char*)cs; 323 register Magic_t* mp = (Magic_t*)handle; 324 register long n = 0; 325 register char* p; 326 327 if (s) 328 { 329 if (*s == '@') 330 { 331 n = *++s == '(' ? strexpr(s, e, indirect, mp) : strtol(s, e, 0); 332 switch (*(s = *e)) 333 { 334 case 'b': 335 case 'B': 336 s++; 337 if (p = getdata(mp, n, 1)) 338 n = *(unsigned char*)p; 339 else 340 s = (char*)cs; 341 break; 342 case 'h': 343 case 'H': 344 s++; 345 if (p = getdata(mp, n, 2)) 346 n = swapget(mp->swap, p, 2); 347 else 348 s = (char*)cs; 349 break; 350 case 'q': 351 case 'Q': 352 s++; 353 if (p = getdata(mp, n, 8)) 354 n = swapget(mp->swap, p, 8); 355 else 356 s = (char*)cs; 357 break; 358 default: 359 if (isalnum(*s)) 360 s++; 361 if (p = getdata(mp, n, 4)) 362 n = swapget(mp->swap, p, 4); 363 else 364 s = (char*)cs; 365 break; 366 } 367 } 368 *e = s; 369 } 370 else if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf) 371 (*mp->disc->errorf)(mp, mp->disc, 2, "%s in indirect expression", *e); 372 return n; 373 } 374 375 /* 376 * emit regex error message 377 */ 378 379 static void 380 regmessage(Magic_t* mp, regex_t* re, int code) 381 { 382 char buf[128]; 383 384 if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf) 385 { 386 regerror(code, re, buf, sizeof(buf)); 387 (*mp->disc->errorf)(mp, mp->disc, 3, "regex: %s", buf); 388 } 389 } 390 391 /* 392 * decompose vcodex(3) method composition 393 */ 394 395 static char* 396 vcdecomp(char* b, char* e, unsigned char* m, unsigned char* x) 397 { 398 unsigned char* map; 399 const char* o; 400 int c; 401 int n; 402 int i; 403 int a; 404 405 map = CCMAP(CC_ASCII, CC_NATIVE); 406 a = 0; 407 i = 1; 408 for (;;) 409 { 410 if (i) 411 i = 0; 412 else 413 *b++ = '^'; 414 if (m < (x - 1) && !*(m + 1)) 415 { 416 /* 417 * obsolete indices 418 */ 419 420 if (!a) 421 { 422 a = 1; 423 o = "old, "; 424 while (b < e && (c = *o++)) 425 *b++ = c; 426 } 427 switch (*m) 428 { 429 case 0: o = "delta"; break; 430 case 1: o = "huffman"; break; 431 case 2: o = "huffgroup"; break; 432 case 3: o = "arith"; break; 433 case 4: o = "bwt"; break; 434 case 5: o = "rle"; break; 435 case 6: o = "mtf"; break; 436 case 7: o = "transpose"; break; 437 case 8: o = "table"; break; 438 case 9: o = "huffpart"; break; 439 case 50: o = "map"; break; 440 case 100: o = "recfm"; break; 441 case 101: o = "ss7"; break; 442 default: o = "UNKNOWN"; break; 443 } 444 m += 2; 445 while (b < e && (c = *o++)) 446 *b++ = c; 447 } 448 else 449 while (b < e && m < x && (c = *m++)) 450 { 451 if (map) 452 c = map[c]; 453 *b++ = c; 454 } 455 if (b >= e) 456 break; 457 n = 0; 458 while (m < x) 459 { 460 n = (n<<7) | (*m & 0x7f); 461 if (!(*m++ & 0x80)) 462 break; 463 } 464 if (n >= (x - m)) 465 break; 466 m += n; 467 } 468 return b; 469 } 470 471 /* 472 * check for magic table match in buf 473 */ 474 475 static char* 476 ckmagic(register Magic_t* mp, const char* file, char* buf, struct stat* st, unsigned long off) 477 { 478 register Entry_t* ep; 479 register char* p; 480 register char* b; 481 register int level = 0; 482 int call = -1; 483 int c; 484 char* q; 485 char* t; 486 char* base = 0; 487 unsigned long num; 488 unsigned long mask; 489 regmatch_t matches[10]; 490 491 mp->swap = 0; 492 b = mp->msg[0] = buf; 493 mp->mime = mp->cap[0] = 0; 494 mp->keep[0] = 0; 495 for (ep = mp->magic; ep; ep = ep->next) 496 { 497 fun: 498 if (ep->nest == '{') 499 { 500 if (++level >= MAXNEST) 501 { 502 call = -1; 503 level = 0; 504 mp->keep[0] = 0; 505 b = mp->msg[0]; 506 mp->mime = mp->cap[0]; 507 continue; 508 } 509 mp->keep[level] = mp->keep[level - 1] != 0; 510 mp->msg[level] = b; 511 mp->cap[level] = mp->mime; 512 } 513 switch (ep->cont) 514 { 515 case '#': 516 if (mp->keep[level] && b > buf) 517 { 518 *b = 0; 519 return buf; 520 } 521 mp->swap = 0; 522 b = mp->msg[0] = buf; 523 mp->mime = mp->cap[0] = 0; 524 if (ep->type == ' ') 525 continue; 526 break; 527 case '$': 528 if (mp->keep[level] && call < (MAXNEST - 1)) 529 { 530 mp->ret[++call] = ep; 531 ep = ep->value.lab; 532 goto fun; 533 } 534 continue; 535 case ':': 536 ep = mp->ret[call--]; 537 if (ep->op == 'l') 538 goto fun; 539 continue; 540 case '|': 541 if (mp->keep[level] > 1) 542 goto checknest; 543 /*FALLTHROUGH*/ 544 default: 545 if (!mp->keep[level]) 546 { 547 b = mp->msg[level]; 548 mp->mime = mp->cap[level]; 549 goto checknest; 550 } 551 break; 552 } 553 p = ""; 554 num = 0; 555 if (!ep->expr) 556 num = ep->offset + off; 557 else 558 switch (ep->offset) 559 { 560 case 0: 561 num = strexpr(ep->expr, NiL, indirect, mp) + off; 562 break; 563 case INFO_atime: 564 num = st->st_atime; 565 ep->type = 'D'; 566 break; 567 case INFO_blocks: 568 num = iblocks(st); 569 ep->type = 'N'; 570 break; 571 case INFO_ctime: 572 num = st->st_ctime; 573 ep->type = 'D'; 574 break; 575 case INFO_fstype: 576 p = fmtfs(st); 577 ep->type = toupper(ep->type); 578 break; 579 case INFO_gid: 580 if (ep->type == 'e' || ep->type == 'm' || ep->type == 's') 581 { 582 p = fmtgid(st->st_gid); 583 ep->type = toupper(ep->type); 584 } 585 else 586 { 587 num = st->st_gid; 588 ep->type = 'N'; 589 } 590 break; 591 case INFO_mode: 592 if (ep->type == 'e' || ep->type == 'm' || ep->type == 's') 593 { 594 p = fmtmode(st->st_mode, 0); 595 ep->type = toupper(ep->type); 596 } 597 else 598 { 599 num = modex(st->st_mode); 600 ep->type = 'N'; 601 } 602 break; 603 case INFO_mtime: 604 num = st->st_ctime; 605 ep->type = 'D'; 606 break; 607 case INFO_name: 608 if (!base) 609 { 610 if (base = strrchr(file, '/')) 611 base++; 612 else 613 base = (char*)file; 614 } 615 p = base; 616 ep->type = toupper(ep->type); 617 break; 618 case INFO_nlink: 619 num = st->st_nlink; 620 ep->type = 'N'; 621 break; 622 case INFO_size: 623 num = st->st_size; 624 ep->type = 'N'; 625 break; 626 case INFO_uid: 627 if (ep->type == 'e' || ep->type == 'm' || ep->type == 's') 628 { 629 p = fmtuid(st->st_uid); 630 ep->type = toupper(ep->type); 631 } 632 else 633 { 634 num = st->st_uid; 635 ep->type = 'N'; 636 } 637 break; 638 } 639 switch (ep->type) 640 { 641 642 case 'b': 643 if (!(p = getdata(mp, num, 1))) 644 goto next; 645 num = *(unsigned char*)p; 646 break; 647 648 case 'h': 649 if (!(p = getdata(mp, num, 2))) 650 goto next; 651 num = swapget(ep->swap ? (~ep->swap ^ mp->swap) : mp->swap, p, 2); 652 break; 653 654 case 'd': 655 case 'l': 656 case 'v': 657 if (!(p = getdata(mp, num, 4))) 658 goto next; 659 num = swapget(ep->swap ? (~ep->swap ^ mp->swap) : mp->swap, p, 4); 660 break; 661 662 case 'q': 663 if (!(p = getdata(mp, num, 8))) 664 goto next; 665 num = swapget(ep->swap ? (~ep->swap ^ mp->swap) : mp->swap, p, 8); 666 break; 667 668 case 'e': 669 if (!(p = getdata(mp, num, 0))) 670 goto next; 671 /*FALLTHROUGH*/ 672 case 'E': 673 if (!ep->value.sub) 674 goto next; 675 if ((c = regexec(ep->value.sub, p, elementsof(matches), matches, 0)) || (c = regsubexec(ep->value.sub, p, elementsof(matches), matches))) 676 { 677 c = mp->fbsz; 678 if (c >= sizeof(mp->nbuf)) 679 c = sizeof(mp->nbuf) - 1; 680 p = (char*)memcpy(mp->nbuf, p, c); 681 p[c] = 0; 682 ccmapstr(mp->x2n, p, c); 683 if ((c = regexec(ep->value.sub, p, elementsof(matches), matches, 0)) || (c = regsubexec(ep->value.sub, p, elementsof(matches), matches))) 684 { 685 if (c != REG_NOMATCH) 686 regmessage(mp, ep->value.sub, c); 687 goto next; 688 } 689 } 690 p = ep->value.sub->re_sub->re_buf; 691 q = T(ep->desc); 692 t = *q ? q : p; 693 if (mp->keep[level]++ && b > buf && *(b - 1) != ' ' && *t && *t != ',' && *t != '.' && *t != '\b') 694 *b++ = ' '; 695 b += sfsprintf(b, PATH_MAX - (b - buf), *q ? q : "%s", p + (*p == '\b')); 696 if (ep->mime) 697 mp->mime = ep->mime; 698 goto checknest; 699 700 case 's': 701 if (!(p = getdata(mp, num, ep->mask))) 702 goto next; 703 goto checkstr; 704 case 'm': 705 if (!(p = getdata(mp, num, 0))) 706 goto next; 707 /*FALLTHROUGH*/ 708 case 'M': 709 case 'S': 710 checkstr: 711 for (;;) 712 { 713 if (*ep->value.str == '*' && !*(ep->value.str + 1) && isprint(*p)) 714 break; 715 if ((ep->type == 'm' || ep->type == 'M') ? strmatch(p, ep->value.str) : !memcmp(p, ep->value.str, ep->mask)) 716 break; 717 if (p == mp->nbuf || ep->mask >= sizeof(mp->nbuf)) 718 goto next; 719 p = (char*)memcpy(mp->nbuf, p, ep->mask); 720 p[ep->mask] = 0; 721 ccmapstr(mp->x2n, p, ep->mask); 722 } 723 q = T(ep->desc); 724 if (mp->keep[level]++ && b > buf && *(b - 1) != ' ' && *q && *q != ',' && *q != '.' && *q != '\b') 725 *b++ = ' '; 726 for (t = p; (c = *t) >= 0 && c <= 0177 && isprint(c) && c != '\n'; t++); 727 *t = 0; 728 b += sfsprintf(b, PATH_MAX - (b - buf), q + (*q == '\b'), p); 729 *t = c; 730 if (ep->mime) 731 mp->mime = ep->mime; 732 goto checknest; 733 734 } 735 if (mask = ep->mask) 736 num &= mask; 737 switch (ep->op) 738 { 739 740 case '=': 741 case '@': 742 if (num == ep->value.num) 743 break; 744 if (ep->cont != '#') 745 goto next; 746 if (!mask) 747 mask = ~mask; 748 if (ep->type == 'h') 749 { 750 if ((num = swapget(mp->swap = 1, p, 2) & mask) == ep->value.num) 751 { 752 if (!(mp->swap & (mp->swap + 1))) 753 mp->swap = 7; 754 goto swapped; 755 } 756 } 757 else if (ep->type == 'l') 758 { 759 for (c = 1; c < 4; c++) 760 if ((num = swapget(mp->swap = c, p, 4) & mask) == ep->value.num) 761 { 762 if (!(mp->swap & (mp->swap + 1))) 763 mp->swap = 7; 764 goto swapped; 765 } 766 } 767 else if (ep->type == 'q') 768 { 769 for (c = 1; c < 8; c++) 770 if ((num = swapget(mp->swap = c, p, 8) & mask) == ep->value.num) 771 goto swapped; 772 } 773 goto next; 774 775 case '!': 776 if (num != ep->value.num) 777 break; 778 goto next; 779 780 case '^': 781 if (num ^ ep->value.num) 782 break; 783 goto next; 784 785 case '>': 786 if (num > ep->value.num) 787 break; 788 goto next; 789 790 case '<': 791 if (num < ep->value.num) 792 break; 793 goto next; 794 795 case 'l': 796 if (num > 0 && mp->keep[level] && call < (MAXNEST - 1)) 797 { 798 if (!ep->value.loop->count) 799 { 800 ep->value.loop->count = num; 801 ep->value.loop->offset = off; 802 off = ep->value.loop->start; 803 } 804 else if (!--ep->value.loop->count) 805 { 806 off = ep->value.loop->offset; 807 goto next; 808 } 809 else 810 off += ep->value.loop->size; 811 mp->ret[++call] = ep; 812 ep = ep->value.loop->lab; 813 goto fun; 814 } 815 goto next; 816 817 case 'm': 818 c = mp->swap; 819 t = ckmagic(mp, file, b + (b > buf), st, num); 820 mp->swap = c; 821 if (!t) 822 goto next; 823 if (b > buf) 824 *b = ' '; 825 b += strlen(b); 826 break; 827 828 case 'r': 829 #if _UWIN 830 { 831 char* e; 832 Sfio_t* rp; 833 Sfio_t* gp; 834 835 if (!(t = strrchr(file, '.'))) 836 goto next; 837 sfprintf(mp->tmp, "/reg/classes_root/%s", t); 838 if (!(t = sfstruse(mp->tmp)) || !(rp = sfopen(NiL, t, "r"))) 839 goto next; 840 *ep->desc = 0; 841 *ep->mime = 0; 842 gp = 0; 843 while (t = sfgetr(rp, '\n', 1)) 844 { 845 if (strneq(t, "Content Type=", 13)) 846 { 847 ep->mime = vmnewof(mp->vm, ep->mime, char, sfvalue(rp), 0); 848 strcpy(ep->mime, t + 13); 849 if (gp) 850 break; 851 } 852 else 853 { 854 sfprintf(mp->tmp, "/reg/classes_root/%s", t); 855 if ((e = sfstruse(mp->tmp)) && (gp = sfopen(NiL, e, "r"))) 856 { 857 ep->desc = vmnewof(mp->vm, ep->desc, char, strlen(t), 1); 858 strcpy(ep->desc, t); 859 if (*ep->mime) 860 break; 861 } 862 } 863 } 864 sfclose(rp); 865 if (!gp) 866 goto next; 867 if (!*ep->mime) 868 { 869 t = T(ep->desc); 870 if (!strncasecmp(t, "microsoft", 9)) 871 t += 9; 872 while (isspace(*t)) 873 t++; 874 e = "application/x-ms-"; 875 ep->mime = vmnewof(mp->vm, ep->mime, char, strlen(t), strlen(e)); 876 e = strcopy(ep->mime, e); 877 while ((c = *t++) && c != '.' && c != ' ') 878 *e++ = isupper(c) ? tolower(c) : c; 879 *e = 0; 880 } 881 while (t = sfgetr(gp, '\n', 1)) 882 if (*t && !streq(t, "\"\"")) 883 { 884 ep->desc = vmnewof(mp->vm, ep->desc, char, sfvalue(gp), 0); 885 strcpy(ep->desc, t); 886 break; 887 } 888 sfclose(gp); 889 if (!*ep->desc) 890 goto next; 891 if (!t) 892 for (t = T(ep->desc); *t; t++) 893 if (*t == '.') 894 *t = ' '; 895 if (!mp->keep[level]) 896 mp->keep[level] = 2; 897 mp->mime = ep->mime; 898 break; 899 } 900 #else 901 if (ep->cont == '#' && !mp->keep[level]) 902 mp->keep[level] = 1; 903 goto next; 904 #endif 905 906 case 'v': 907 if (!(p = getdata(mp, num, 4))) 908 goto next; 909 c = 0; 910 do 911 { 912 num++; 913 c = (c<<7) | (*p & 0x7f); 914 } while (*p++ & 0x80); 915 if (!(p = getdata(mp, num, c))) 916 goto next; 917 if (mp->keep[level]++ && b > buf && *(b - 1) != ' ') 918 { 919 *b++ = ','; 920 *b++ = ' '; 921 } 922 b = vcdecomp(b, buf + PATH_MAX, (unsigned char*)p, (unsigned char*)p + c); 923 goto checknest; 924 925 } 926 swapped: 927 q = T(ep->desc); 928 if (mp->keep[level]++ && b > buf && *(b - 1) != ' ' && *q && *q != ',' && *q != '.' && *q != '\b') 929 *b++ = ' '; 930 if (ep->type == 'd' || ep->type == 'D') 931 b += sfsprintf(b, PATH_MAX - (b - buf), q + (*q == '\b'), fmttime("%?%l", (time_t)num)); 932 else if (ep->type == 'v') 933 b += sfsprintf(b, PATH_MAX - (b - buf), q + (*q == '\b'), fmtversion(num)); 934 else 935 b += sfsprintf(b, PATH_MAX - (b - buf), q + (*q == '\b'), num); 936 if (ep->mime && *ep->mime) 937 mp->mime = ep->mime; 938 checknest: 939 if (ep->nest == '}') 940 { 941 if (!mp->keep[level]) 942 { 943 b = mp->msg[level]; 944 mp->mime = mp->cap[level]; 945 } 946 else if (level > 0) 947 mp->keep[level - 1] = mp->keep[level]; 948 if (--level < 0) 949 { 950 level = 0; 951 mp->keep[0] = 0; 952 } 953 } 954 continue; 955 next: 956 if (ep->cont == '&') 957 mp->keep[level] = 0; 958 goto checknest; 959 } 960 if (mp->keep[level] && b > buf) 961 { 962 *b = 0; 963 return buf; 964 } 965 return 0; 966 } 967 968 /* 969 * check english language stats 970 */ 971 972 static int 973 ckenglish(register Magic_t* mp, int pun, int badpun) 974 { 975 register char* s; 976 register int vowl = 0; 977 register int freq = 0; 978 register int rare = 0; 979 980 if (5 * badpun > pun) 981 return 0; 982 if (2 * mp->count[';'] > mp->count['E'] + mp->count['e']) 983 return 0; 984 if ((mp->count['>'] + mp->count['<'] + mp->count['/']) > mp->count['E'] + mp->count['e']) 985 return 0; 986 for (s = "aeiou"; *s; s++) 987 vowl += mp->count[toupper(*s)] + mp->count[*s]; 988 for (s = "etaion"; *s; s++) 989 freq += mp->count[toupper(*s)] + mp->count[*s]; 990 for (s = "vjkqxz"; *s; s++) 991 rare += mp->count[toupper(*s)] + mp->count[*s]; 992 return 5 * vowl >= mp->fbsz - mp->count[' '] && freq >= 10 * rare; 993 } 994 995 /* 996 * check programming language stats 997 */ 998 999 static char* 1000 cklang(register Magic_t* mp, const char* file, char* buf, struct stat* st) 1001 { 1002 register int c; 1003 register unsigned char* b; 1004 register unsigned char* e; 1005 register int q; 1006 register char* s; 1007 char* t; 1008 char* base; 1009 char* suff; 1010 char* t1; 1011 char* t2; 1012 char* t3; 1013 int n; 1014 int badpun; 1015 int code; 1016 int pun; 1017 Cctype_t flags; 1018 Info_t* ip; 1019 1020 b = (unsigned char*)mp->fbuf; 1021 e = b + mp->fbsz; 1022 memzero(mp->count, sizeof(mp->count)); 1023 memzero(mp->multi, sizeof(mp->multi)); 1024 memzero(mp->identifier, sizeof(mp->identifier)); 1025 1026 /* 1027 * check character coding 1028 */ 1029 1030 flags = 0; 1031 while (b < e) 1032 flags |= mp->cctype[*b++]; 1033 b = (unsigned char*)mp->fbuf; 1034 code = 0; 1035 q = CC_ASCII; 1036 n = CC_MASK; 1037 for (c = 0; c < CC_MAPS; c++) 1038 { 1039 flags ^= CC_text; 1040 if ((flags & CC_MASK) < n) 1041 { 1042 n = flags & CC_MASK; 1043 q = c; 1044 } 1045 flags >>= CC_BIT; 1046 } 1047 flags = n; 1048 if (!(flags & (CC_binary|CC_notext))) 1049 { 1050 if (q != CC_NATIVE) 1051 { 1052 code = q; 1053 ccmaps(mp->fbuf, mp->fbsz, q, CC_NATIVE); 1054 } 1055 if (b[0] == '#' && b[1] == '!') 1056 { 1057 for (b += 2; b < e && isspace(*b); b++); 1058 for (s = (char*)b; b < e && isprint(*b); b++); 1059 c = *b; 1060 *b = 0; 1061 if ((st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) || match(s, "/*bin*/*") || !access(s, F_OK)) 1062 { 1063 if (t = strrchr(s, '/')) 1064 s = t + 1; 1065 for (t = s; *t; t++) 1066 if (isspace(*t)) 1067 { 1068 *t = 0; 1069 break; 1070 } 1071 sfsprintf(mp->mbuf, sizeof(mp->mbuf), "application/x-%s", *s ? s : "sh"); 1072 mp->mime = mp->mbuf; 1073 if (match(s, "*sh")) 1074 { 1075 t1 = T("command"); 1076 if (streq(s, "sh")) 1077 *s = 0; 1078 else 1079 { 1080 *b++ = ' '; 1081 *b = 0; 1082 } 1083 } 1084 else 1085 { 1086 t1 = T("interpreter"); 1087 *b++ = ' '; 1088 *b = 0; 1089 } 1090 sfsprintf(mp->sbuf, sizeof(mp->sbuf), T("%s%s script"), s, t1); 1091 s = mp->sbuf; 1092 goto qualify; 1093 } 1094 *b = c; 1095 b = (unsigned char*)mp->fbuf; 1096 } 1097 badpun = 0; 1098 pun = 0; 1099 q = 0; 1100 s = 0; 1101 t = 0; 1102 while (b < e) 1103 { 1104 c = *b++; 1105 mp->count[c]++; 1106 if (c == q && (q != '*' || *b == '/' && b++)) 1107 { 1108 mp->multi[q]++; 1109 q = 0; 1110 } 1111 else if (c == '\\') 1112 { 1113 s = 0; 1114 b++; 1115 } 1116 else if (!q) 1117 { 1118 if (isalpha(c) || c == '_') 1119 { 1120 if (!s) 1121 s = (char*)b - 1; 1122 } 1123 else if (!isdigit(c)) 1124 { 1125 if (s) 1126 { 1127 if (s > mp->fbuf) 1128 switch (*(s - 1)) 1129 { 1130 case ':': 1131 if (*b == ':') 1132 mp->multi[':']++; 1133 break; 1134 case '.': 1135 if (((char*)b - s) == 3 && (s == (mp->fbuf + 1) || *(s - 2) == '\n')) 1136 mp->multi['.']++; 1137 break; 1138 case '\n': 1139 case '\\': 1140 if (*b == '{') 1141 t = (char*)b + 1; 1142 break; 1143 case '{': 1144 if (s == t && *b == '}') 1145 mp->multi['X']++; 1146 break; 1147 } 1148 if (!mp->idtab) 1149 { 1150 if (mp->idtab = dtnew(mp->vm, &mp->dtdisc, Dthash)) 1151 for (q = 0; q < elementsof(dict); q++) 1152 dtinsert(mp->idtab, &dict[q]); 1153 else if (mp->disc->errorf) 1154 (*mp->disc->errorf)(mp, mp->disc, 3, "out of space"); 1155 q = 0; 1156 } 1157 if (mp->idtab) 1158 { 1159 *(b - 1) = 0; 1160 if (ip = (Info_t*)dtmatch(mp->idtab, s)) 1161 mp->identifier[ip->value]++; 1162 *(b - 1) = c; 1163 } 1164 s = 0; 1165 } 1166 switch (c) 1167 { 1168 case '\t': 1169 if (b == (unsigned char*)(mp->fbuf + 1) || *(b - 2) == '\n') 1170 mp->multi['\t']++; 1171 break; 1172 case '"': 1173 case '\'': 1174 q = c; 1175 break; 1176 case '/': 1177 if (*b == '*') 1178 q = *b++; 1179 else if (*b == '/') 1180 q = '\n'; 1181 break; 1182 case '$': 1183 if (*b == '(' && *(b + 1) != ' ') 1184 mp->multi['$']++; 1185 break; 1186 case '{': 1187 case '}': 1188 case '[': 1189 case ']': 1190 case '(': 1191 mp->multi[c]++; 1192 break; 1193 case ')': 1194 mp->multi[c]++; 1195 goto punctuation; 1196 case ':': 1197 if (*b == ':' && isspace(*(b + 1)) && b > (unsigned char*)(mp->fbuf + 1) && isspace(*(b - 2))) 1198 mp->multi[':']++; 1199 goto punctuation; 1200 case '.': 1201 case ',': 1202 case '%': 1203 case ';': 1204 case '?': 1205 punctuation: 1206 pun++; 1207 if (*b != ' ' && *b != '\n') 1208 badpun++; 1209 break; 1210 } 1211 } 1212 } 1213 } 1214 } 1215 else 1216 while (b < e) 1217 mp->count[*b++]++; 1218 base = (t1 = strrchr(file, '/')) ? t1 + 1 : (char*)file; 1219 suff = (t1 = strrchr(base, '.')) ? t1 + 1 : ""; 1220 if (!flags) 1221 { 1222 if (match(suff, "*sh|bat|cmd")) 1223 goto id_sh; 1224 if (match(base, "*@(mkfile)")) 1225 goto id_mk; 1226 if (match(base, "*@(makefile|.mk)")) 1227 goto id_make; 1228 if (match(base, "*@(mamfile|.mam)")) 1229 goto id_mam; 1230 if (match(suff, "[cly]?(pp|xx|++)|cc|ll|yy")) 1231 goto id_c; 1232 if (match(suff, "f")) 1233 goto id_fortran; 1234 if (match(suff, "htm+(l)")) 1235 goto id_html; 1236 if (match(suff, "cpy")) 1237 goto id_copybook; 1238 if (match(suff, "cob|cbl|cb2")) 1239 goto id_cobol; 1240 if (match(suff, "pl[1i]")) 1241 goto id_pl1; 1242 if (match(suff, "tex")) 1243 goto id_tex; 1244 if (match(suff, "asm|s")) 1245 goto id_asm; 1246 if ((st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) && (!suff || suff != strchr(suff, '.'))) 1247 { 1248 id_sh: 1249 s = T("command script"); 1250 mp->mime = "application/sh"; 1251 goto qualify; 1252 } 1253 if (strmatch(mp->fbuf, "From * [0-9][0-9]:[0-9][0-9]:[0-9][0-9] *")) 1254 { 1255 s = T("mail message"); 1256 mp->mime = "message/rfc822"; 1257 goto qualify; 1258 } 1259 if (match(base, "*@(mkfile)")) 1260 { 1261 id_mk: 1262 s = "mkfile"; 1263 mp->mime = "application/mk"; 1264 goto qualify; 1265 } 1266 if (match(base, "*@(makefile|.mk)") || mp->multi['\t'] >= mp->count[':'] && (mp->multi['$'] > 0 || mp->multi[':'] > 0)) 1267 { 1268 id_make: 1269 s = "makefile"; 1270 mp->mime = "application/make"; 1271 goto qualify; 1272 } 1273 if (mp->multi['.'] >= 3) 1274 { 1275 s = T("nroff input"); 1276 mp->mime = "application/x-troff"; 1277 goto qualify; 1278 } 1279 if (mp->multi['X'] >= 3) 1280 { 1281 s = T("TeX input"); 1282 mp->mime = "application/x-tex"; 1283 goto qualify; 1284 } 1285 if (mp->fbsz < SF_BUFSIZE && 1286 (mp->multi['('] == mp->multi[')'] && 1287 mp->multi['{'] == mp->multi['}'] && 1288 mp->multi['['] == mp->multi[']']) || 1289 mp->fbsz >= SF_BUFSIZE && 1290 (mp->multi['('] >= mp->multi[')'] && 1291 mp->multi['{'] >= mp->multi['}'] && 1292 mp->multi['['] >= mp->multi[']'])) 1293 { 1294 c = mp->identifier[ID_INCL1]; 1295 if (c >= 2 && mp->identifier[ID_INCL2] >= c && mp->identifier[ID_INCL3] >= c && mp->count['.'] >= c || 1296 mp->identifier[ID_C] >= 5 && mp->count[';'] >= 5 || 1297 mp->count['='] >= 20 && mp->count[';'] >= 20) 1298 { 1299 id_c: 1300 t1 = ""; 1301 t2 = "c "; 1302 t3 = T("program"); 1303 switch (*suff) 1304 { 1305 case 'c': 1306 case 'C': 1307 mp->mime = "application/x-cc"; 1308 break; 1309 case 'l': 1310 case 'L': 1311 t1 = "lex "; 1312 mp->mime = "application/x-lex"; 1313 break; 1314 default: 1315 t3 = T("header"); 1316 if (mp->identifier[ID_YACC] < 5 || mp->count['%'] < 5) 1317 { 1318 mp->mime = "application/x-cc"; 1319 break; 1320 } 1321 /*FALLTHROUGH*/ 1322 case 'y': 1323 case 'Y': 1324 t1 = "yacc "; 1325 mp->mime = "application/x-yacc"; 1326 break; 1327 } 1328 if (mp->identifier[ID_CPLUSPLUS] >= 3) 1329 { 1330 t2 = "c++ "; 1331 mp->mime = "application/x-c++"; 1332 } 1333 sfsprintf(mp->sbuf, sizeof(mp->sbuf), "%s%s%s", t1, t2, t3); 1334 s = mp->sbuf; 1335 goto qualify; 1336 } 1337 } 1338 if (mp->identifier[ID_MAM1] >= 2 && mp->identifier[ID_MAM3] >= 2 && 1339 (mp->fbsz < SF_BUFSIZE && mp->identifier[ID_MAM1] == mp->identifier[ID_MAM2] || 1340 mp->fbsz >= SF_BUFSIZE && mp->identifier[ID_MAM1] >= mp->identifier[ID_MAM2])) 1341 { 1342 id_mam: 1343 s = T("mam program"); 1344 mp->mime = "application/x-mam"; 1345 goto qualify; 1346 } 1347 if (mp->identifier[ID_FORTRAN] >= 8) 1348 { 1349 id_fortran: 1350 s = T("fortran program"); 1351 mp->mime = "application/x-fortran"; 1352 goto qualify; 1353 } 1354 if (mp->identifier[ID_HTML] > 0 && mp->count['<'] >= 8 && (c = mp->count['<'] - mp->count['>']) >= -2 && c <= 2) 1355 { 1356 id_html: 1357 s = T("html input"); 1358 mp->mime = "text/html"; 1359 goto qualify; 1360 } 1361 if (mp->identifier[ID_COPYBOOK] > 0 && mp->identifier[ID_COBOL] == 0 && (c = mp->count['('] - mp->count[')']) >= -2 && c <= 2) 1362 { 1363 id_copybook: 1364 s = T("cobol copybook"); 1365 mp->mime = "application/x-cobol"; 1366 goto qualify; 1367 } 1368 if (mp->identifier[ID_COBOL] > 0 && mp->identifier[ID_COPYBOOK] > 0 && (c = mp->count['('] - mp->count[')']) >= -2 && c <= 2) 1369 { 1370 id_cobol: 1371 s = T("cobol program"); 1372 mp->mime = "application/x-cobol"; 1373 goto qualify; 1374 } 1375 if (mp->identifier[ID_PL1] > 0 && (c = mp->count['('] - mp->count[')']) >= -2 && c <= 2) 1376 { 1377 id_pl1: 1378 s = T("pl1 program"); 1379 mp->mime = "application/x-pl1"; 1380 goto qualify; 1381 } 1382 if (mp->count['{'] >= 6 && (c = mp->count['{'] - mp->count['}']) >= -2 && c <= 2 && mp->count['\\'] >= mp->count['{']) 1383 { 1384 id_tex: 1385 s = T("TeX input"); 1386 mp->mime = "text/tex"; 1387 goto qualify; 1388 } 1389 if (mp->identifier[ID_ASM] >= 4) 1390 { 1391 id_asm: 1392 s = T("as program"); 1393 mp->mime = "application/x-as"; 1394 goto qualify; 1395 } 1396 if (ckenglish(mp, pun, badpun)) 1397 { 1398 s = T("english text"); 1399 mp->mime = "text/plain"; 1400 goto qualify; 1401 } 1402 } 1403 else if (streq(base, "core")) 1404 { 1405 mp->mime = "x-system/core"; 1406 return T("core dump"); 1407 } 1408 if (flags & (CC_binary|CC_notext)) 1409 { 1410 b = (unsigned char*)mp->fbuf; 1411 e = b + mp->fbsz; 1412 n = 0; 1413 for (;;) 1414 { 1415 c = *b++; 1416 q = 0; 1417 while (c & 0x80) 1418 { 1419 c <<= 1; 1420 q++; 1421 } 1422 switch (q) 1423 { 1424 case 4: 1425 if (b < e && (*b++ & 0xc0) != 0x80) 1426 break; 1427 case 3: 1428 if (b < e && (*b++ & 0xc0) != 0x80) 1429 break; 1430 case 2: 1431 if (b < e && (*b++ & 0xc0) != 0x80) 1432 break; 1433 n = 1; 1434 case 0: 1435 if (b >= e) 1436 { 1437 if (n) 1438 { 1439 flags &= ~(CC_binary|CC_notext); 1440 flags |= CC_utf_8; 1441 } 1442 break; 1443 } 1444 continue; 1445 } 1446 break; 1447 } 1448 } 1449 if (flags & (CC_binary|CC_notext)) 1450 { 1451 unsigned long d = 0; 1452 1453 if ((q = mp->fbsz / UCHAR_MAX) >= 2) 1454 { 1455 /* 1456 * compression/encryption via standard deviation 1457 */ 1458 1459 1460 for (c = 0; c < UCHAR_MAX; c++) 1461 { 1462 pun = mp->count[c] - q; 1463 d += pun * pun; 1464 } 1465 d /= mp->fbsz; 1466 } 1467 if (d <= 0) 1468 s = T("binary"); 1469 else if (d < 4) 1470 s = T("encrypted"); 1471 else if (d < 16) 1472 s = T("packed"); 1473 else if (d < 64) 1474 s = T("compressed"); 1475 else if (d < 256) 1476 s = T("delta"); 1477 else 1478 s = T("data"); 1479 mp->mime = "application/octet-stream"; 1480 return s; 1481 } 1482 mp->mime = "text/plain"; 1483 if (flags & CC_utf_8) 1484 s = (flags & CC_control) ? T("utf-8 text with control characters") : T("utf-8 text"); 1485 else if (flags & CC_latin) 1486 s = (flags & CC_control) ? T("latin text with control characters") : T("latin text"); 1487 else 1488 s = (flags & CC_control) ? T("text with control characters") : T("text"); 1489 qualify: 1490 if (!flags && mp->count['\n'] >= mp->count['\r'] && mp->count['\n'] <= (mp->count['\r'] + 1) && mp->count['\r']) 1491 { 1492 t = "dos "; 1493 mp->mime = "text/dos"; 1494 } 1495 else 1496 t = ""; 1497 if (code) 1498 { 1499 if (code == CC_ASCII) 1500 sfsprintf(buf, PATH_MAX, "ascii %s%s", t, s); 1501 else 1502 { 1503 sfsprintf(buf, PATH_MAX, "ebcdic%d %s%s", code - 1, t, s); 1504 mp->mime = "text/ebcdic"; 1505 } 1506 s = buf; 1507 } 1508 else if (*t) 1509 { 1510 sfsprintf(buf, PATH_MAX, "%s%s", t, s); 1511 s = buf; 1512 } 1513 return s; 1514 } 1515 1516 /* 1517 * return the basic magic string for file,st in buf,size 1518 */ 1519 1520 static char* 1521 type(register Magic_t* mp, const char* file, struct stat* st, char* buf, int size) 1522 { 1523 register char* s; 1524 register char* t; 1525 1526 mp->mime = 0; 1527 if (!S_ISREG(st->st_mode)) 1528 { 1529 if (S_ISDIR(st->st_mode)) 1530 { 1531 mp->mime = "x-system/dir"; 1532 return T("directory"); 1533 } 1534 if (S_ISLNK(st->st_mode)) 1535 { 1536 mp->mime = "x-system/lnk"; 1537 s = buf; 1538 s += sfsprintf(s, PATH_MAX, T("symbolic link to ")); 1539 if (pathgetlink(file, s, size - (s - buf)) < 0) 1540 return T("cannot read symbolic link text"); 1541 return buf; 1542 } 1543 if (S_ISBLK(st->st_mode)) 1544 { 1545 mp->mime = "x-system/blk"; 1546 sfsprintf(buf, PATH_MAX, T("block special (%s)"), fmtdev(st)); 1547 return buf; 1548 } 1549 if (S_ISCHR(st->st_mode)) 1550 { 1551 mp->mime = "x-system/chr"; 1552 sfsprintf(buf, PATH_MAX, T("character special (%s)"), fmtdev(st)); 1553 return buf; 1554 } 1555 if (S_ISFIFO(st->st_mode)) 1556 { 1557 mp->mime = "x-system/fifo"; 1558 return "fifo"; 1559 } 1560 #ifdef S_ISSOCK 1561 if (S_ISSOCK(st->st_mode)) 1562 { 1563 mp->mime = "x-system/sock"; 1564 return "socket"; 1565 } 1566 #endif 1567 } 1568 if (!(mp->fbmx = st->st_size)) 1569 s = T("empty"); 1570 else if (!mp->fp) 1571 s = T("cannot read"); 1572 else 1573 { 1574 mp->fbsz = sfread(mp->fp, mp->fbuf, sizeof(mp->fbuf) - 1); 1575 if (mp->fbsz < 0) 1576 s = fmterror(errno); 1577 else if (mp->fbsz == 0) 1578 s = T("empty"); 1579 else 1580 { 1581 mp->fbuf[mp->fbsz] = 0; 1582 mp->xoff = 0; 1583 mp->xbsz = 0; 1584 if (!(s = ckmagic(mp, file, buf, st, 0))) 1585 s = cklang(mp, file, buf, st); 1586 } 1587 } 1588 if (!mp->mime) 1589 mp->mime = "application/unknown"; 1590 else if ((t = strchr(mp->mime, '%')) && *(t + 1) == 's' && !*(t + 2)) 1591 { 1592 register char* b; 1593 register char* be; 1594 register char* m; 1595 register char* me; 1596 1597 b = mp->mime; 1598 me = (m = mp->mime = mp->fbuf) + sizeof(mp->fbuf) - 1; 1599 while (m < me && b < t) 1600 *m++ = *b++; 1601 b = t = s; 1602 for (;;) 1603 { 1604 if (!(be = strchr(t, ' '))) 1605 { 1606 be = b + strlen(b); 1607 break; 1608 } 1609 if (*(be - 1) == ',' || strneq(be + 1, "data", 4) || strneq(be + 1, "file", 4)) 1610 break; 1611 b = t; 1612 t = be + 1; 1613 } 1614 while (m < me && b < be) 1615 if ((*m++ = *b++) == ' ') 1616 *(m - 1) = '-'; 1617 *m = 0; 1618 } 1619 return s; 1620 } 1621 1622 /* 1623 * low level for magicload() 1624 */ 1625 1626 static int 1627 load(register Magic_t* mp, char* file, register Sfio_t* fp) 1628 { 1629 register Entry_t* ep; 1630 register char* p; 1631 register char* p2; 1632 char* p3; 1633 char* next; 1634 int n; 1635 int lge; 1636 int lev; 1637 int ent; 1638 int old; 1639 int cont; 1640 Info_t* ip; 1641 Entry_t* ret; 1642 Entry_t* first; 1643 Entry_t* last = 0; 1644 Entry_t* fun['z' - 'a' + 1]; 1645 1646 memzero(fun, sizeof(fun)); 1647 cont = '$'; 1648 ent = 0; 1649 lev = 0; 1650 old = 0; 1651 ret = 0; 1652 error_info.file = file; 1653 error_info.line = 0; 1654 first = ep = vmnewof(mp->vm, 0, Entry_t, 1, 0); 1655 while (p = sfgetr(fp, '\n', 1)) 1656 { 1657 error_info.line++; 1658 for (; isspace(*p); p++); 1659 1660 /* 1661 * nesting 1662 */ 1663 1664 switch (*p) 1665 { 1666 case 0: 1667 case '#': 1668 cont = '#'; 1669 continue; 1670 case '{': 1671 if (++lev < MAXNEST) 1672 ep->nest = *p; 1673 else if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf) 1674 (*mp->disc->errorf)(mp, mp->disc, 1, "{ ... } operator nesting too deep -- %d max", MAXNEST); 1675 continue; 1676 case '}': 1677 if (!last || lev <= 0) 1678 { 1679 if (mp->disc->errorf) 1680 (*mp->disc->errorf)(mp, mp->disc, 2, "`%c': invalid nesting", *p); 1681 } 1682 else if (lev-- == ent) 1683 { 1684 ent = 0; 1685 ep->cont = ':'; 1686 ep->offset = ret->offset; 1687 ep->nest = ' '; 1688 ep->type = ' '; 1689 ep->op = ' '; 1690 ep->desc = "[RETURN]"; 1691 last = ep; 1692 ep = ret->next = vmnewof(mp->vm, 0, Entry_t, 1, 0); 1693 ret = 0; 1694 } 1695 else 1696 last->nest = *p; 1697 continue; 1698 default: 1699 if (*(p + 1) == '{' || *(p + 1) == '(' && *p != '+' && *p != '>' && *p != '&' && *p != '|') 1700 { 1701 n = *p++; 1702 if (n >= 'a' && n <= 'z') 1703 n -= 'a'; 1704 else 1705 { 1706 if (mp->disc->errorf) 1707 (*mp->disc->errorf)(mp, mp->disc, 2, "%c: invalid function name", n); 1708 n = 0; 1709 } 1710 if (ret && mp->disc->errorf) 1711 (*mp->disc->errorf)(mp, mp->disc, 2, "%c: function has no return", ret->offset + 'a'); 1712 if (*p == '{') 1713 { 1714 ent = ++lev; 1715 ret = ep; 1716 ep->desc = "[FUNCTION]"; 1717 } 1718 else 1719 { 1720 if (*(p + 1) != ')' && mp->disc->errorf) 1721 (*mp->disc->errorf)(mp, mp->disc, 2, "%c: invalid function call argument list", n + 'a'); 1722 ep->desc = "[CALL]"; 1723 } 1724 ep->cont = cont; 1725 ep->offset = n; 1726 ep->nest = ' '; 1727 ep->type = ' '; 1728 ep->op = ' '; 1729 last = ep; 1730 ep = ep->next = vmnewof(mp->vm, 0, Entry_t, 1, 0); 1731 if (ret) 1732 fun[n] = last->value.lab = ep; 1733 else if (!(last->value.lab = fun[n]) && mp->disc->errorf) 1734 (*mp->disc->errorf)(mp, mp->disc, 2, "%c: function not defined", n + 'a'); 1735 continue; 1736 } 1737 if (!ep->nest) 1738 ep->nest = (lev > 0 && lev != ent) ? ('0' + lev - !!ent) : ' '; 1739 break; 1740 } 1741 1742 /* 1743 * continuation 1744 */ 1745 1746 cont = '$'; 1747 switch (*p) 1748 { 1749 case '>': 1750 old = 1; 1751 if (*(p + 1) == *p) 1752 { 1753 /* 1754 * old style nesting push 1755 */ 1756 1757 p++; 1758 old = 2; 1759 if (!lev && last) 1760 { 1761 lev = 1; 1762 last->nest = '{'; 1763 if (last->cont == '>') 1764 last->cont = '&'; 1765 ep->nest = '1'; 1766 } 1767 } 1768 /*FALLTHROUGH*/ 1769 case '+': 1770 case '&': 1771 case '|': 1772 ep->cont = *p++; 1773 break; 1774 default: 1775 if ((mp->flags & MAGIC_VERBOSE) && !isalpha(*p) && mp->disc->errorf) 1776 (*mp->disc->errorf)(mp, mp->disc, 1, "`%c': invalid line continuation operator", *p); 1777 /*FALLTHROUGH*/ 1778 case '*': 1779 case '0': case '1': case '2': case '3': case '4': 1780 case '5': case '6': case '7': case '8': case '9': 1781 ep->cont = (lev > 0) ? '&' : '#'; 1782 break; 1783 } 1784 switch (old) 1785 { 1786 case 1: 1787 old = 0; 1788 if (lev) 1789 { 1790 /* 1791 * old style nesting pop 1792 */ 1793 1794 lev = 0; 1795 if (last) 1796 last->nest = '}'; 1797 ep->nest = ' '; 1798 if (ep->cont == '&') 1799 ep->cont = '#'; 1800 } 1801 break; 1802 case 2: 1803 old = 1; 1804 break; 1805 } 1806 if (isdigit(*p)) 1807 { 1808 /* 1809 * absolute offset 1810 */ 1811 1812 ep->offset = strton(p, &next, NiL, 0); 1813 p2 = next; 1814 } 1815 else 1816 { 1817 for (p2 = p; *p2 && !isspace(*p2); p2++); 1818 if (!*p2) 1819 { 1820 if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf) 1821 (*mp->disc->errorf)(mp, mp->disc, 1, "not enough fields: `%s'", p); 1822 continue; 1823 } 1824 1825 /* 1826 * offset expression 1827 */ 1828 1829 *p2++ = 0; 1830 ep->expr = vmstrdup(mp->vm, p); 1831 if (isalpha(*p)) 1832 ep->offset = (ip = (Info_t*)dtmatch(mp->infotab, p)) ? ip->value : 0; 1833 else if (*p == '(' && ep->cont == '>') 1834 { 1835 /* 1836 * convert old style indirection to @ 1837 */ 1838 1839 p = ep->expr + 1; 1840 for (;;) 1841 { 1842 switch (*p++) 1843 { 1844 case 0: 1845 case '@': 1846 case '(': 1847 break; 1848 case ')': 1849 break; 1850 default: 1851 continue; 1852 } 1853 break; 1854 } 1855 if (*--p == ')') 1856 { 1857 *p = 0; 1858 *ep->expr = '@'; 1859 } 1860 } 1861 } 1862 for (; isspace(*p2); p2++); 1863 for (p = p2; *p2 && !isspace(*p2); p2++); 1864 if (!*p2) 1865 { 1866 if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf) 1867 (*mp->disc->errorf)(mp, mp->disc, 1, "not enough fields: `%s'", p); 1868 continue; 1869 } 1870 *p2++ = 0; 1871 1872 /* 1873 * type 1874 */ 1875 1876 if ((*p == 'b' || *p == 'l') && *(p + 1) == 'e') 1877 { 1878 ep->swap = ~(*p == 'l' ? 7 : 0); 1879 p += 2; 1880 } 1881 if (*p == 's') 1882 { 1883 if (*(p + 1) == 'h') 1884 ep->type = 'h'; 1885 else 1886 ep->type = 's'; 1887 } 1888 else if (*p == 'a') 1889 ep->type = 's'; 1890 else 1891 ep->type = *p; 1892 if (p = strchr(p, '&')) 1893 { 1894 /* 1895 * old style mask 1896 */ 1897 1898 ep->mask = strton(++p, NiL, NiL, 0); 1899 } 1900 for (; isspace(*p2); p2++); 1901 if (ep->mask) 1902 *--p2 = '='; 1903 1904 /* 1905 * comparison operation 1906 */ 1907 1908 p = p2; 1909 if (p2 = strchr(p, '\t')) 1910 *p2++ = 0; 1911 else 1912 { 1913 int qe = 0; 1914 int qn = 0; 1915 1916 /* 1917 * assume balanced {}[]()\\""'' field 1918 */ 1919 1920 for (p2 = p;;) 1921 { 1922 switch (n = *p2++) 1923 { 1924 case 0: 1925 break; 1926 case '{': 1927 if (!qe) 1928 qe = '}'; 1929 if (qe == '}') 1930 qn++; 1931 continue; 1932 case '(': 1933 if (!qe) 1934 qe = ')'; 1935 if (qe == ')') 1936 qn++; 1937 continue; 1938 case '[': 1939 if (!qe) 1940 qe = ']'; 1941 if (qe == ']') 1942 qn++; 1943 continue; 1944 case '}': 1945 case ')': 1946 case ']': 1947 if (qe == n && qn > 0) 1948 qn--; 1949 continue; 1950 case '"': 1951 case '\'': 1952 if (!qe) 1953 qe = n; 1954 else if (qe == n) 1955 qe = 0; 1956 continue; 1957 case '\\': 1958 if (*p2) 1959 p2++; 1960 continue; 1961 default: 1962 if (!qe && isspace(n)) 1963 break; 1964 continue; 1965 } 1966 if (n) 1967 *(p2 - 1) = 0; 1968 else 1969 p2--; 1970 break; 1971 } 1972 } 1973 lge = 0; 1974 if (ep->type == 'e' || ep->type == 'm' || ep->type == 's') 1975 ep->op = '='; 1976 else 1977 { 1978 if (*p == '&') 1979 { 1980 ep->mask = strton(++p, &next, NiL, 0); 1981 p = next; 1982 } 1983 switch (*p) 1984 { 1985 case '=': 1986 case '>': 1987 case '<': 1988 case '*': 1989 ep->op = *p++; 1990 if (*p == '=') 1991 { 1992 p++; 1993 switch (ep->op) 1994 { 1995 case '>': 1996 lge = -1; 1997 break; 1998 case '<': 1999 lge = 1; 2000 break; 2001 } 2002 } 2003 break; 2004 case '!': 2005 case '@': 2006 ep->op = *p++; 2007 if (*p == '=') 2008 p++; 2009 break; 2010 case 'x': 2011 p++; 2012 ep->op = '*'; 2013 break; 2014 default: 2015 ep->op = '='; 2016 if (ep->mask) 2017 ep->value.num = ep->mask; 2018 break; 2019 } 2020 } 2021 if (ep->op != '*' && !ep->value.num) 2022 { 2023 if (ep->type == 'e') 2024 { 2025 if (ep->value.sub = vmnewof(mp->vm, 0, regex_t, 1, 0)) 2026 { 2027 ep->value.sub->re_disc = &mp->redisc; 2028 if (!(n = regcomp(ep->value.sub, p, REG_DELIMITED|REG_LENIENT|REG_NULL|REG_DISCIPLINE))) 2029 { 2030 p += ep->value.sub->re_npat; 2031 if (!(n = regsubcomp(ep->value.sub, p, NiL, 0, 0))) 2032 p += ep->value.sub->re_npat; 2033 } 2034 if (n) 2035 { 2036 regmessage(mp, ep->value.sub, n); 2037 ep->value.sub = 0; 2038 } 2039 else if (*p && mp->disc->errorf) 2040 (*mp->disc->errorf)(mp, mp->disc, 1, "invalid characters after substitution: %s", p); 2041 } 2042 } 2043 else if (ep->type == 'm') 2044 { 2045 ep->mask = stresc(p) + 1; 2046 ep->value.str = vmnewof(mp->vm, 0, char, ep->mask + 1, 0); 2047 memcpy(ep->value.str, p, ep->mask); 2048 if ((!ep->expr || !ep->offset) && !strmatch(ep->value.str, "\\!\\(*\\)")) 2049 ep->value.str[ep->mask - 1] = '*'; 2050 } 2051 else if (ep->type == 's') 2052 { 2053 ep->mask = stresc(p); 2054 ep->value.str = vmnewof(mp->vm, 0, char, ep->mask, 0); 2055 memcpy(ep->value.str, p, ep->mask); 2056 } 2057 else if (*p == '\'') 2058 { 2059 stresc(p); 2060 ep->value.num = *(unsigned char*)(p + 1) + lge; 2061 } 2062 else if (strmatch(p, "+([a-z])\\(*\\)")) 2063 { 2064 char* t; 2065 2066 t = p; 2067 ep->type = 'V'; 2068 ep->op = *p; 2069 while (*p && *p++ != '('); 2070 switch (ep->op) 2071 { 2072 case 'l': 2073 n = *p++; 2074 if (n < 'a' || n > 'z') 2075 { 2076 if (mp->disc->errorf) 2077 (*mp->disc->errorf)(mp, mp->disc, 2, "%c: invalid function name", n); 2078 } 2079 else if (!fun[n -= 'a']) 2080 { 2081 if (mp->disc->errorf) 2082 (*mp->disc->errorf)(mp, mp->disc, 2, "%c: function not defined", n + 'a'); 2083 } 2084 else 2085 { 2086 ep->value.loop = vmnewof(mp->vm, 0, Loop_t, 1, 0); 2087 ep->value.loop->lab = fun[n]; 2088 while (*p && *p++ != ','); 2089 ep->value.loop->start = strton(p, &t, NiL, 0); 2090 while (*t && *t++ != ','); 2091 ep->value.loop->size = strton(t, &t, NiL, 0); 2092 } 2093 break; 2094 case 'm': 2095 case 'r': 2096 ep->desc = vmnewof(mp->vm, 0, char, 32, 0); 2097 ep->mime = vmnewof(mp->vm, 0, char, 32, 0); 2098 break; 2099 case 'v': 2100 break; 2101 default: 2102 if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf) 2103 (*mp->disc->errorf)(mp, mp->disc, 1, "%-.*s: unknown function", p - t, t); 2104 break; 2105 } 2106 } 2107 else 2108 { 2109 ep->value.num = strton(p, NiL, NiL, 0) + lge; 2110 if (ep->op == '@') 2111 ep->value.num = swapget(0, (char*)&ep->value.num, sizeof(ep->value.num)); 2112 } 2113 } 2114 2115 /* 2116 * file description 2117 */ 2118 2119 if (p2) 2120 { 2121 for (; isspace(*p2); p2++); 2122 if (p = strchr(p2, '\t')) 2123 { 2124 /* 2125 * check for message catalog index 2126 */ 2127 2128 *p++ = 0; 2129 if (isalpha(*p2)) 2130 { 2131 for (p3 = p2; isalnum(*p3); p3++); 2132 if (*p3++ == ':') 2133 { 2134 for (; isdigit(*p3); p3++); 2135 if (!*p3) 2136 { 2137 for (p2 = p; isspace(*p2); p2++); 2138 if (p = strchr(p2, '\t')) 2139 *p++ = 0; 2140 } 2141 } 2142 } 2143 } 2144 stresc(p2); 2145 ep->desc = vmstrdup(mp->vm, p2); 2146 if (p) 2147 { 2148 for (; isspace(*p); p++); 2149 if (*p) 2150 ep->mime = vmstrdup(mp->vm, p); 2151 } 2152 } 2153 else 2154 ep->desc = ""; 2155 2156 /* 2157 * get next entry 2158 */ 2159 2160 last = ep; 2161 ep = ep->next = vmnewof(mp->vm, 0, Entry_t, 1, 0); 2162 } 2163 if (last) 2164 { 2165 last->next = 0; 2166 if (mp->magiclast) 2167 mp->magiclast->next = first; 2168 else 2169 mp->magic = first; 2170 mp->magiclast = last; 2171 } 2172 vmfree(mp->vm, ep); 2173 if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf) 2174 { 2175 if (lev < 0) 2176 (*mp->disc->errorf)(mp, mp->disc, 1, "too many } operators"); 2177 else if (lev > 0) 2178 (*mp->disc->errorf)(mp, mp->disc, 1, "not enough } operators"); 2179 if (ret) 2180 (*mp->disc->errorf)(mp, mp->disc, 2, "%c: function has no return", ret->offset + 'a'); 2181 } 2182 error_info.file = 0; 2183 error_info.line = 0; 2184 return 0; 2185 } 2186 2187 /* 2188 * load a magic file into mp 2189 */ 2190 2191 int 2192 magicload(register Magic_t* mp, const char* file, unsigned long flags) 2193 { 2194 register char* s; 2195 register char* e; 2196 register char* t; 2197 int n; 2198 int found; 2199 int list; 2200 Sfio_t* fp; 2201 2202 mp->flags = mp->disc->flags | flags; 2203 found = 0; 2204 if (list = !(s = (char*)file) || !*s || (*s == '-' || *s == '.') && !*(s + 1)) 2205 { 2206 if (!(s = getenv(MAGIC_FILE_ENV)) || !*s) 2207 s = MAGIC_FILE; 2208 } 2209 for (;;) 2210 { 2211 if (!list) 2212 e = 0; 2213 else if (e = strchr(s, ':')) 2214 { 2215 /* 2216 * ok, so ~ won't work for the last list element 2217 * we do it for MAGIC_FILES_ENV anyway 2218 */ 2219 2220 if ((strneq(s, "~/", n = 2) || strneq(s, "$HOME/", n = 6) || strneq(s, "${HOME}/", n = 8)) && (t = getenv("HOME"))) 2221 { 2222 sfputr(mp->tmp, t, -1); 2223 s += n - 1; 2224 } 2225 sfwrite(mp->tmp, s, e - s); 2226 if (!(s = sfstruse(mp->tmp))) 2227 goto nospace; 2228 } 2229 if (!*s || streq(s, "-")) 2230 s = MAGIC_FILE; 2231 if (!(fp = sfopen(NiL, s, "r"))) 2232 { 2233 if (list) 2234 { 2235 if (!(t = pathpath(mp->fbuf, s, "", PATH_REGULAR|PATH_READ)) && !strchr(s, '/')) 2236 { 2237 strcpy(mp->fbuf, s); 2238 sfprintf(mp->tmp, "%s/%s", MAGIC_DIR, mp->fbuf); 2239 if (!(s = sfstruse(mp->tmp))) 2240 goto nospace; 2241 if (!(t = pathpath(mp->fbuf, s, "", PATH_REGULAR|PATH_READ))) 2242 goto next; 2243 } 2244 if (!(fp = sfopen(NiL, t, "r"))) 2245 goto next; 2246 } 2247 else 2248 { 2249 if (mp->disc->errorf) 2250 (*mp->disc->errorf)(mp, mp->disc, 3, "%s: cannot open magic file", s); 2251 return -1; 2252 } 2253 } 2254 found = 1; 2255 n = load(mp, s, fp); 2256 sfclose(fp); 2257 if (n && !list) 2258 return -1; 2259 next: 2260 if (!e) 2261 break; 2262 s = e + 1; 2263 } 2264 if (!found) 2265 { 2266 if (mp->flags & MAGIC_VERBOSE) 2267 { 2268 if (mp->disc->errorf) 2269 (*mp->disc->errorf)(mp, mp->disc, 2, "cannot find magic file"); 2270 } 2271 return -1; 2272 } 2273 return 0; 2274 nospace: 2275 if (mp->disc->errorf) 2276 (*mp->disc->errorf)(mp, mp->disc, 3, "out of space"); 2277 return -1; 2278 } 2279 2280 /* 2281 * open a magic session 2282 */ 2283 2284 Magic_t* 2285 magicopen(Magicdisc_t* disc) 2286 { 2287 register Magic_t* mp; 2288 register int i; 2289 register int n; 2290 register int f; 2291 register int c; 2292 register Vmalloc_t* vm; 2293 unsigned char* map[CC_MAPS + 1]; 2294 2295 if (!(vm = vmopen(Vmdcheap, Vmbest, 0))) 2296 return 0; 2297 if (!(mp = vmnewof(vm, 0, Magic_t, 1, 0))) 2298 { 2299 vmclose(vm); 2300 return 0; 2301 } 2302 mp->id = lib; 2303 mp->disc = disc; 2304 mp->vm = vm; 2305 mp->flags = disc->flags; 2306 mp->redisc.re_version = REG_VERSION; 2307 mp->redisc.re_flags = REG_NOFREE; 2308 mp->redisc.re_errorf = (regerror_t)disc->errorf; 2309 mp->redisc.re_resizef = (regresize_t)vmgetmem; 2310 mp->redisc.re_resizehandle = (void*)mp->vm; 2311 mp->dtdisc.key = offsetof(Info_t, name); 2312 mp->dtdisc.link = offsetof(Info_t, link); 2313 if (!(mp->tmp = sfstropen()) || !(mp->infotab = dtnew(mp->vm, &mp->dtdisc, Dthash))) 2314 goto bad; 2315 for (n = 0; n < elementsof(info); n++) 2316 dtinsert(mp->infotab, &info[n]); 2317 for (i = 0; i < CC_MAPS; i++) 2318 map[i] = ccmap(i, CC_ASCII); 2319 mp->x2n = ccmap(CC_ALIEN, CC_NATIVE); 2320 for (n = 0; n <= UCHAR_MAX; n++) 2321 { 2322 f = 0; 2323 i = CC_MAPS; 2324 while (--i >= 0) 2325 { 2326 c = ccmapchr(map[i], n); 2327 f = (f << CC_BIT) | CCTYPE(c); 2328 } 2329 mp->cctype[n] = f; 2330 } 2331 return mp; 2332 bad: 2333 magicclose(mp); 2334 return 0; 2335 } 2336 2337 /* 2338 * close a magicopen() session 2339 */ 2340 2341 int 2342 magicclose(register Magic_t* mp) 2343 { 2344 if (!mp) 2345 return -1; 2346 if (mp->tmp) 2347 sfstrclose(mp->tmp); 2348 if (mp->vm) 2349 vmclose(mp->vm); 2350 return 0; 2351 } 2352 2353 /* 2354 * return the magic string for file with optional stat info st 2355 */ 2356 2357 char* 2358 magictype(register Magic_t* mp, Sfio_t* fp, const char* file, register struct stat* st) 2359 { 2360 off_t off; 2361 char* s; 2362 2363 mp->flags = mp->disc->flags; 2364 mp->mime = 0; 2365 if (!st) 2366 s = T("cannot stat"); 2367 else 2368 { 2369 if (mp->fp = fp) 2370 off = sfseek(mp->fp, (off_t)0, SEEK_CUR); 2371 s = type(mp, file, st, mp->tbuf, sizeof(mp->tbuf)); 2372 if (mp->fp) 2373 sfseek(mp->fp, off, SEEK_SET); 2374 if (!(mp->flags & MAGIC_MIME)) 2375 { 2376 if (S_ISREG(st->st_mode) && (st->st_size > 0) && (st->st_size < 128)) 2377 sfprintf(mp->tmp, "%s ", T("short")); 2378 sfprintf(mp->tmp, "%s", s); 2379 if (!mp->fp && (st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) 2380 sfprintf(mp->tmp, ", %s", S_ISDIR(st->st_mode) ? T("searchable") : T("executable")); 2381 if (st->st_mode & S_ISUID) 2382 sfprintf(mp->tmp, ", setuid=%s", fmtuid(st->st_uid)); 2383 if (st->st_mode & S_ISGID) 2384 sfprintf(mp->tmp, ", setgid=%s", fmtgid(st->st_gid)); 2385 if (st->st_mode & S_ISVTX) 2386 sfprintf(mp->tmp, ", sticky"); 2387 if (!(s = sfstruse(mp->tmp))) 2388 s = T("out of space"); 2389 } 2390 } 2391 if (mp->flags & MAGIC_MIME) 2392 s = mp->mime; 2393 if (!s) 2394 s = T("error"); 2395 return s; 2396 } 2397 2398 /* 2399 * list the magic table in mp on sp 2400 */ 2401 2402 int 2403 magiclist(register Magic_t* mp, register Sfio_t* sp) 2404 { 2405 register Entry_t* ep = mp->magic; 2406 register Entry_t* rp = 0; 2407 2408 mp->flags = mp->disc->flags; 2409 sfprintf(sp, "cont\toffset\ttype\top\tmask\tvalue\tmime\tdesc\n"); 2410 while (ep) 2411 { 2412 sfprintf(sp, "%c %c\t", ep->cont, ep->nest); 2413 if (ep->expr) 2414 sfprintf(sp, "%s", ep->expr); 2415 else 2416 sfprintf(sp, "%ld", ep->offset); 2417 sfprintf(sp, "\t%s%c\t%c\t%lo\t", ep->swap == (char)~3 ? "L" : ep->swap == (char)~0 ? "B" : "", ep->type, ep->op, ep->mask); 2418 switch (ep->type) 2419 { 2420 case 'm': 2421 case 's': 2422 sfputr(sp, fmtesc(ep->value.str), -1); 2423 break; 2424 case 'V': 2425 switch (ep->op) 2426 { 2427 case 'l': 2428 sfprintf(sp, "loop(%d,%d,%d,%d)", ep->value.loop->start, ep->value.loop->size, ep->value.loop->count, ep->value.loop->offset); 2429 break; 2430 case 'v': 2431 sfprintf(sp, "vcodex()"); 2432 break; 2433 default: 2434 sfprintf(sp, "%p", ep->value.str); 2435 break; 2436 } 2437 break; 2438 default: 2439 sfprintf(sp, "%lo", ep->value.num); 2440 break; 2441 } 2442 sfprintf(sp, "\t%s\t%s\n", ep->mime ? ep->mime : "", fmtesc(ep->desc)); 2443 if (ep->cont == '$' && !ep->value.lab->mask) 2444 { 2445 rp = ep; 2446 ep = ep->value.lab; 2447 } 2448 else 2449 { 2450 if (ep->cont == ':') 2451 { 2452 ep = rp; 2453 ep->value.lab->mask = 1; 2454 } 2455 ep = ep->next; 2456 } 2457 } 2458 return 0; 2459 } 2460