|
|
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.