|
|
1.1 ! root 1: char _version[] = "Version 1.1"; ! 2: /* ! 3: * Look at a file and try to ! 4: * figure out its type. Knows about the various ! 5: * flavours of filesystem entries, object files of various ! 6: * types, C programs, input to one of the various flavours ! 7: * of text formatter, etc. ! 8: */ ! 9: #include <stdio.h> ! 10: #include <ctype.h> ! 11: #include <sys/stat.h> ! 12: #include <sys/uproc.h> ! 13: #include <n.out.h> ! 14: #include <filehdr.h> ! 15: #include <canon.h> ! 16: #include <ar.h> ! 17: ! 18: /* UNIX archive magic numbers */ ! 19: #define COFFARMAG "!<arch>\n" ! 20: #define COFFARMAG_RAN "!<arch>\n/" ! 21: #define UARMAG 0177545 /* UNIX v7 archives */ ! 22: #define OUARMAG 0177555 /* UNIX v6 and previous archives */ ! 23: ! 24: #define COMPRESSED 0x9D1F /* Output of "compress(1)" command. */ ! 25: ! 26: #define EXEC (S_IEXEC|(S_IEXEC<<3)|(S_IEXEC<<6)) ! 27: ! 28: /* ! 29: * This is a tar header. ! 30: */ ! 31: struct th_info { ! 32: char th_name[100], ! 33: th_mode[8], ! 34: th_uid[8], ! 35: th_gid[8], ! 36: th_size[12], ! 37: th_mtime[12], ! 38: th_check[8], ! 39: th_islink, ! 40: th_link[100], ! 41: th_pad[255]; ! 42: }; ! 43: ! 44: /* ! 45: * The first BUFSIZ bytes of the ! 46: * file in question are read into this ! 47: * union. It contains members for accessing ! 48: * the possible file structures. ! 49: */ ! 50: ! 51: union iobuf ! 52: { ! 53: char u_buf[BUFSIZ]; /* General data */ ! 54: struct ldheader u_lout; /* L.out object file header */ ! 55: struct filehdr u_coff; /* COFF object file header */ ! 56: UPROC u_u; /* core file header */ ! 57: int u_armag; /* Archive number */ ! 58: struct th_info u_tar; /* Tar header */ ! 59: }; ! 60: ! 61: char *type; ! 62: ! 63: union iobuf iobuf; ! 64: char *file(); ! 65: char *textclass(); ! 66: char *objclass(); ! 67: char *coffclass(); ! 68: char *dirtype(); ! 69: char *mtype(); ! 70: char *coffmtype(); ! 71: char *strcat(); ! 72: ! 73: main(argc, argv) ! 74: char *argv[]; ! 75: { ! 76: struct stat sb; ! 77: register char *p; ! 78: register int i; ! 79: register int estat; ! 80: ! 81: if (argc < 2) { ! 82: fprintf(stderr, "Usage: file name ...\n"); ! 83: exit(1); ! 84: } ! 85: estat = 0; ! 86: for (i=1; i<argc; i++) { ! 87: p = argv[i]; ! 88: if (stat(p, &sb) < 0) { ! 89: fprintf(stderr, "file: %s: not accessible\n", p); ! 90: estat = 1; ! 91: continue; ! 92: } ! 93: printf("%s: %s\n", p, file(&sb, p)); ! 94: } ! 95: exit(estat); ! 96: } ! 97: ! 98: /* ! 99: * Routine to guess filetype ! 100: */ ! 101: char * ! 102: file(sbp, fn) ! 103: struct stat *sbp; ! 104: char *fn; ! 105: { ! 106: static char buf[50]; ! 107: int magic; ! 108: register int nb; ! 109: register int fd = -1; ! 110: ! 111: if ((sbp->st_mode&S_IFMT) != S_IFREG) { ! 112: switch (sbp->st_mode & S_IFMT) { ! 113: case S_IFDIR: ! 114: return (dirtype(fn)); ! 115: ! 116: case S_IFBLK: ! 117: sprintf(buf, "block special file %d/%d", ! 118: major(sbp->st_rdev), minor(sbp->st_rdev)); ! 119: return (buf); ! 120: ! 121: case S_IFCHR: ! 122: sprintf(buf, "character special file %d/%d", ! 123: major(sbp->st_rdev), minor(sbp->st_rdev)); ! 124: return (buf); ! 125: ! 126: case S_IFMPB: ! 127: return ("block multiplexor file"); ! 128: ! 129: case S_IFMPC: ! 130: return ("character multiplexor file"); ! 131: ! 132: case S_IFPIP: ! 133: return ("named pipe"); ! 134: ! 135: default: ! 136: return ("invalid filetype"); ! 137: } ! 138: } ! 139: if ((fd = open(fn, 0)) < 0) ! 140: return ("unreadable"); ! 141: if ((nb = read(fd, (char *) &iobuf, sizeof(iobuf))) < 0) { ! 142: close(fd); ! 143: return ("read error"); ! 144: } ! 145: close(fd); ! 146: if (nb == 0) ! 147: return ("empty"); ! 148: /* l.out executable? */ ! 149: if (nb >= sizeof(struct ldheader)) { ! 150: magic = iobuf.u_lout.l_magic; ! 151: canint(magic); ! 152: if (magic == L_MAGIC) { ! 153: canint(iobuf.u_lout.l_flag); ! 154: canint(iobuf.u_lout.l_machine); ! 155: return (objclass(fd, &iobuf.u_lout)); ! 156: } ! 157: } ! 158: /* COFF executable? */ ! 159: if (nb >= sizeof(struct filehdr)) { ! 160: magic = iobuf.u_coff.f_magic; ! 161: if (ISCOFF(magic)) { ! 162: return (coffclass(&iobuf.u_coff)); ! 163: } ! 164: } ! 165: ! 166: /* COFF archive? */ ! 167: if (nb >= strlen(COFFARMAG_RAN)) { ! 168: if ( strncmp(COFFARMAG_RAN, ! 169: iobuf.u_buf, ! 170: strlen(COFFARMAG_RAN)) == 0) { ! 171: return("COFF archive (ranlib)"); ! 172: } ! 173: if ( strncmp(COFFARMAG, iobuf.u_buf, strlen(COFFARMAG)) == 0) { ! 174: return("COFF archive"); ! 175: } ! 176: } ! 177: ! 178: if (nb >= sizeof(struct th_info)) { ! 179: if ( tar_path(iobuf.u_tar.th_name, 100) && ! 180: tar_oct(iobuf.u_tar.th_mode, 8) && ! 181: tar_dec(iobuf.u_tar.th_uid, 8) && ! 182: tar_dec(iobuf.u_tar.th_gid, 8) && ! 183: tar_dec(iobuf.u_tar.th_size, 12) && ! 184: tar_dec(iobuf.u_tar.th_mtime, 12) && ! 185: strlen(iobuf.u_tar.th_pad) < 8 ) { ! 186: ! 187: type = malloc(64); ! 188: ! 189: type[0] ='\0'; ! 190: if ( '\0' != iobuf.u_tar.th_pad[0]) { ! 191: strcpy(type, iobuf.u_tar.th_pad); ! 192: } else { ! 193: strcpy(type, "v7 tar"); ! 194: } ! 195: ! 196: strcat(type, " archive"); ! 197: return(type); ! 198: } /* if looks like a tar header. */ ! 199: } ! 200: ! 201: /* core file? ! 202: * This is pure heuristic. If the last signal number was legal ! 203: * (a 13/256 random chance) and if the command string is NUL ! 204: * terminated (about 1/5 random chance) and at least one long, ! 205: * and if all of the ! 206: * characters are printable (~ (96/256)^4), we assume it really ! 207: * is a core file. (Pardon the crude statistics.) ! 208: * That all adds up to a less than 0.1% chance of incorrectly ! 209: * classifying a random binary file. ! 210: */ ! 211: if (nb > sizeof(UPROC)) { ! 212: if ((iobuf.u_u.u_signo < NSIG) && /* Legal signal? */ ! 213: (iobuf.u_u.u_comm[9] == '\0') ) { /* Terminated command? */ ! 214: char *comm; ! 215: int i; ! 216: int len; ! 217: ! 218: /* Check to see if the command is printable. */ ! 219: comm = iobuf.u_u.u_comm; ! 220: len = strlen(comm); ! 221: ! 222: for (i=0; isprint(comm[i]) && (i < len); i++) { ! 223: /* Do nothing. */ ! 224: } ! 225: /* If we go to the end, probably a core file. */ ! 226: if (len == i && len > 0) { ! 227: type = malloc(64); ! 228: sprintf(type, "core file from \"%s\"", comm); ! 229: return(type); ! 230: } ! 231: ! 232: } ! 233: } ! 234: ! 235: /* Archive or compress'ed file? */ ! 236: if (nb >= sizeof (int)) { ! 237: magic = iobuf.u_armag; ! 238: canint(magic); ! 239: if (magic == ARMAG) ! 240: return ("l.out archive"); ! 241: if (magic == UARMAG) ! 242: return ("seventh edition archive"); ! 243: if (magic == OUARMAG) ! 244: return ("sixth edition archive"); ! 245: if (magic == COMPRESSED){ ! 246: type = malloc(64); ! 247: sprintf(type, "%d bit compressed file", ! 248: (int) (0x7f & iobuf.u_buf[2])); ! 249: return(type); ! 250: } ! 251: } ! 252: ! 253: if (hasnonascii((unsigned char *) &iobuf, nb)) ! 254: return ("binary data"); ! 255: if ((sbp->st_mode&EXEC) != 0) { ! 256: if (strncmp(iobuf.u_buf, "#!", 2) == 0) { ! 257: type = malloc(64); ! 258: sprintf(type, "%s script", ! 259: strtok(&(iobuf.u_buf[2]), " \t\n")); ! 260: return(type); ! 261: } else { ! 262: return ("commands"); ! 263: } ! 264: } ! 265: return (textclass((unsigned char *) &iobuf, nb)); ! 266: } ! 267: ! 268: /* ! 269: * Return the type of the directory. ! 270: * Currently, only "s.*" is recognised as SCCS directory. ! 271: */ ! 272: char * ! 273: dirtype(dn) ! 274: register char *dn; ! 275: { ! 276: register char *cp; ! 277: ! 278: for (cp=dn; *cp!='\0'; cp++) ! 279: ; ! 280: while (cp > dn) ! 281: if (*--cp == '/') { ! 282: cp++; ! 283: break; ! 284: } ! 285: if (cp[0]=='s' && cp[1]=='.') ! 286: return ("SCCS directory"); ! 287: ! 288: if (strcmp(cp, "RCS") == 0) ! 289: return ("RCS directory"); ! 290: ! 291: return ("directory"); ! 292: } ! 293: ! 294: /* ! 295: * Return true if there ar characters ! 296: * in the buffer that do not look like good ! 297: * ascii characters. ! 298: * This routine knows that ascii is a seven ! 299: * bit code. ! 300: */ ! 301: hasnonascii(bp, nb) ! 302: register unsigned char *bp; ! 303: register nb; ! 304: { ! 305: while (nb--) { ! 306: if (!isascii(*bp)) ! 307: return (1); ! 308: if (!(isprint(*bp) || isspace(*bp) || *bp=='\b' || *bp=='\a')) ! 309: return (1); ! 310: bp++; ! 311: } ! 312: return (0); ! 313: } ! 314: ! 315: /* ! 316: * Classify the first `nb' ! 317: * bytes of a text file. ! 318: */ ! 319: char * ! 320: textclass(bp, nb) ! 321: register unsigned char *bp; ! 322: register nb; ! 323: { ! 324: register int c; ! 325: int nlf = 1; ! 326: int nlbrace; ! 327: int nrbrace; ! 328: int nsharps; ! 329: int nsemi; ! 330: int xmail; ! 331: char *sbp = bp; ! 332: ! 333: nlbrace = 0; ! 334: nrbrace = 0; ! 335: nsharps = 0; ! 336: nsemi = 0; ! 337: xmail = 0; ! 338: while (nb--) { ! 339: c = *bp++; ! 340: if (c=='.' && strncmp(bp, "globl", 5)==0) ! 341: return ("assembler"); ! 342: if (nlf) { ! 343: if (c == '.') ! 344: return ("nroff, tbl or eqn input"); ! 345: else if (c == ':') ! 346: return ("commands"); ! 347: else if (c == '#') ! 348: ++nsharps; ! 349: else if (c=='%') ! 350: if (*bp=='%' || *bp=='{' || *bp=='}') ! 351: return ("yacc or lex input"); ! 352: } ! 353: if (c == '{') ! 354: ++nlbrace; ! 355: else if (c == '}') ! 356: ++nrbrace; ! 357: if (c == '\n') { ! 358: nlf = 1; ! 359: if (bp[-2] == ';') ! 360: nsemi++; ! 361: if (((bp-sbp) % 22) == 0) { ! 362: if (bp[-2]==' ' || bp[-2]=='!') ! 363: xmail++; ! 364: } else ! 365: xmail = 0; ! 366: } else ! 367: nlf = 0; ! 368: } ! 369: if (xmail) ! 370: return ("xmail encoded text"); ! 371: if ((nsharps || nsemi) && (nlbrace || nrbrace)) ! 372: return ("C program"); ! 373: return ("probably text"); ! 374: } ! 375: ! 376: /* ! 377: * Figure out the type of an l.out ! 378: * object file. Tag it with the machine ! 379: * id if not for the machine upon which the ! 380: * command is running. ! 381: */ ! 382: char * ! 383: objclass(fd, lhp) ! 384: register struct ldheader *lhp; ! 385: { ! 386: static char type[64]; ! 387: register char *mch; ! 388: struct ldsym lds; ! 389: register fsize_t stbase; ! 390: register i; ! 391: ! 392: type[0] = '\0'; ! 393: if ((lhp->l_flag&LF_32) != 0) ! 394: strcat(type, "32 bit "); ! 395: if ((lhp->l_flag&LF_SLIB) != 0) ! 396: strcat(type, "shared library "); ! 397: if ((lhp->l_flag&LF_SLREF) != 0) ! 398: strcat(type, "libref "); ! 399: if ((lhp->l_flag&LF_SHR) != 0) ! 400: strcat(type, "shared "); ! 401: if ((lhp->l_flag&LF_SEP) != 0) ! 402: strcat(type, "separate "); ! 403: if ((lhp->l_flag&LF_KER) != 0) { ! 404: register unsigned ssize; ! 405: ! 406: ssize = sizeof (lds); ! 407: if ((lhp->l_flag & LF_32) == 0) { ! 408: stbase = sizeof(*lhp) - 2*sizeof(int); ! 409: ssize -= sizeof (int); ! 410: } else { ! 411: canshort(lhp->l_tbase); ! 412: stbase = lhp->l_tbase; ! 413: } ! 414: for (i=L_SHRI; i<L_SYM; ++i) ! 415: if (i!=L_BSSI && i!=L_BSSD) { ! 416: cansize(lhp->l_ssize[i]); ! 417: stbase += lhp->l_ssize[i]; ! 418: } ! 419: lseek(fd, stbase, 0); ! 420: cansize(lhp->l_ssize[L_SYM]); ! 421: while (lhp->l_ssize[L_SYM] != 0) { ! 422: if (read(fd, &lds, ssize) != ssize) ! 423: break; ! 424: canshort(lds.ls_type); ! 425: if (strncmp(lds.ls_id, "conftab_", NCPLN) == 0 ! 426: && (lds.ls_type&LR_SEG) != L_REF) ! 427: break; ! 428: lhp->l_ssize[L_SYM] -= ssize; ! 429: } ! 430: strcat(type, lhp->l_ssize[L_SYM]==0?"kernel ":"driver "); ! 431: } ! 432: strcat(type, "executable"); ! 433: if ((lhp->l_flag&LF_NRB) == 0) ! 434: strcat(type, " with relocation"); ! 435: if ((mch = mtype(lhp->l_machine)) == NULL) ! 436: mch = "Unknown machine type"; ! 437: sprintf(type, "%s (%s)", type, mch); ! 438: return (type); ! 439: } ! 440: ! 441: /* ! 442: * Figure out the type of a coff ! 443: * object file. Tag it with the machine ! 444: * id if not for the machine upon which the ! 445: * command is running. ! 446: */ ! 447: char * ! 448: coffclass(chp) ! 449: register struct filehdr *chp; ! 450: { ! 451: static char type[64]; ! 452: register char *mch; ! 453: ! 454: sprintf(type, "COFF "); ! 455: if ((chp->f_flags&F_MINMAL) != 0) ! 456: strcat(type, "minimal "); ! 457: if ((chp->f_flags&F_UPDATE) != 0) ! 458: strcat(type, "update "); ! 459: if ((chp->f_flags&F_SWABD) != 0) ! 460: strcat(type, "swapped bytes "); ! 461: if ((chp->f_flags&F_PATCH) != 0) ! 462: strcat(type, "patch "); ! 463: if ((chp->f_flags&F_NODF) != 0) ! 464: strcat(type, "no decision "); ! 465: if ((chp->f_flags&F_EXEC) != 0){ ! 466: if ((chp->f_flags&F_LSYMS) != 0) ! 467: strcat(type, "stripped "); ! 468: strcat(type, "executable "); ! 469: } else { ! 470: strcat(type, "object "); ! 471: if ((chp->f_flags&F_RELFLG) != 0) ! 472: strcat(type, "stripped relocation "); ! 473: ! 474: if ((chp->f_flags&F_LSYMS) != 0) ! 475: strcat(type, "stripped symbols "); ! 476: } ! 477: ! 478: if ((mch = coffmtype(chp->f_magic)) == NULL) ! 479: mch = "Unknown machine type"; ! 480: sprintf(type, "%s(%s) ", type, mch); ! 481: ! 482: if (chp->f_nsyms > 0) { ! 483: strcat(type, "not stripped "); ! 484: } ! 485: return (type); ! 486: } ! 487: ! 488: ! 489: /* ! 490: * Identify a COFF executable. ! 491: */ ! 492: char * ! 493: coffmtype(magic) ! 494: unsigned short magic; ! 495: { ! 496: switch ((unsigned) magic) { ! 497: ! 498: case IAPX16: ! 499: case IAPX16TV: ! 500: case IAPX20: ! 501: case IAPX20TV: ! 502: return("iAPX"); ! 503: ! 504: case B16MAGIC: ! 505: case BTVMAGIC: ! 506: return("Intel Basic-16"); ! 507: ! 508: case X86MAGIC: ! 509: case XTVMAGIC: ! 510: return("Intel x86"); ! 511: ! 512: case I286SMAGIC: ! 513: return("Intel 286"); ! 514: ! 515: case I386MAGIC: ! 516: return("Intel 386"); ! 517: ! 518: case N3BMAGIC: ! 519: case NTVMAGIC: ! 520: return("New 3B"); ! 521: ! 522: case WE32MAGIC: ! 523: case RBOMAGIC: ! 524: case MTVMAGIC: ! 525: return("MAC-32, 3515, 3B5"); ! 526: ! 527: case VAXWRMAGIC: ! 528: case VAXROMAGIC: ! 529: return("VAX 11/780 or /750"); ! 530: ! 531: case 0401: ! 532: case 0405: ! 533: case 0407: ! 534: case 0410: ! 535: case 0411: ! 536: case 0437: ! 537: return("pdp11"); ! 538: ! 539: case MC68MAGIC: ! 540: case MC68TVMAGIC: ! 541: case M68MAGIC: ! 542: case M68TVMAGIC: ! 543: return("Motorola 680xx"); ! 544: ! 545: /* case I286LMAGIC: */ ! 546: case MC68KPGMAGIC: ! 547: return("UNIX PC or iAPX 286"); ! 548: ! 549: case U370WRMAGIC: ! 550: case U370ROMAGIC: ! 551: return("IBM 370"); ! 552: ! 553: case AMDWRMAGIC: ! 554: case AMDROMAGIC: ! 555: return("Amdahl 470/580"); ! 556: default: ! 557: return(NULL); ! 558: ! 559: } ! 560: } /* coffmtype() */ ! 561: ! 562: /* ! 563: * Decide if character array 'str' of length 'len' is an acceptable tar ! 564: * octal field. Returns TRUE if it is acceptable, FALSE otherwise. ! 565: */ ! 566: int ! 567: tar_oct(str, len) ! 568: char *str; ! 569: int len; ! 570: { ! 571: for (; len > 0; --len, ++str) { ! 572: if (*str != '\0' && *str != ' ' && ! 573: !('0' <= *str && *str <= '7')) { ! 574: break; ! 575: } ! 576: } ! 577: ! 578: return(0 == len); ! 579: } /* tar_oct() */ ! 580: ! 581: /* ! 582: * Decide if character array 'str' of length 'len' is an acceptable tar ! 583: * decimal field. Returns TRUE if it is acceptable, FALSE otherwise. ! 584: */ ! 585: int ! 586: tar_dec(str, len) ! 587: char *str; ! 588: int len; ! 589: { ! 590: for (; len > 0; --len, ++str) { ! 591: if (*str != '\0' && *str != ' ' && ! 592: !isdigit(*str)) { ! 593: break; ! 594: } ! 595: } ! 596: ! 597: return(0 == len); ! 598: } /* tar_dec() */ ! 599: ! 600: /* ! 601: * Decide if character array 'str' of length 'len' is an acceptable tar ! 602: * path type field. Returns TRUE if it is acceptable, FALSE otherwise. ! 603: */ ! 604: int ! 605: tar_path(str, len) ! 606: char *str; ! 607: int len; ! 608: { ! 609: for (; len > 0; --len, ++str) { ! 610: if (*str != '\0' && *str != ' ' && ! 611: !isprint(*str)) { ! 612: break; ! 613: } ! 614: } ! 615: ! 616: return(0 == len); ! 617: } /* tar_path() */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.