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