xref: /original-bsd/usr.bin/file/file.c (revision e58c8952)
1 /*-
2  * Copyright (c) 1991, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.proprietary.c%
6  */
7 
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1991, 1993, 1994\n\
11 	The Regents of the University of California.  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)file.c	8.2 (Berkeley) 03/31/94";
16 #endif /* not lint */
17 
18 /*
19  * file - determine type of file
20  */
21 
22 #include <sys/param.h>
23 #include <sys/stat.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <a.out.h>
28 
29 #if defined(hp300) || defined(hp800)
30 #include <hp/hpux/hpux_exec.h>
31 #endif
32 
33 extern  int errno;
34 int in;
35 int i  = 0;
36 
37 #define BUFSIZE 4096
38 
39 char buf[BUFSIZE];
40 char *troff[] = {	/* new troff intermediate lang */
41 	"x","T","res","init","font","202","V0","p1",0};
42 char *fort[] = {
43 	"function","subroutine","common","dimension","block","integer",
44 	"real","data","double",0};
45 char *asc[] = {
46 	"chmk","mov","tst","clr","jmp",0};
47 char *c[] = {
48 	"int","char","float","double","struct","extern", "static",0};
49 char *as[] = {
50 	"globl","byte","align","text","data","comm",0};
51 char *sh[] = {
52 	"fi", "elif", "esac", "done", "export",
53 	"readonly", "trap", "PATH", "HOME", 0 };
54 char *csh[] = {
55 	"alias", "breaksw", "endsw", "foreach", "limit",  "onintr",
56 	"repeat", "setenv", "source", "path", "home", 0 };
57 int	ifile;
58 
59 int (*statfcn) __P((const char *, struct stat *));
60 
61 main(argc, argv)
62 char **argv;
63 {
64 	FILE *fl;
65 	register char *p;
66 	char ap[MAXPATHLEN + 1];
67 
68 	if (argc < 2) {
69 		fprintf(stderr, "usage: %s file ...\n", argv[0]);
70 		exit(3);
71 	}
72 
73 	if (argc>1 && argv[1][0]=='-' && argv[1][1]=='h') {
74 		statfcn = lstat;
75 		--argc;
76 		++argv;
77 	} else
78 		statfcn = stat;
79 
80 	if (argc>1 && argv[1][0]=='-' && argv[1][1]=='f') {
81 		if ((fl = fopen(argv[2], "r")) == NULL) {
82 			perror(argv[2]);
83 			exit(2);
84 		}
85 		while ((p = fgets(ap, sizeof ap, fl)) != NULL) {
86 			int l = strlen(p);
87 			if (l>0)
88 				p[l-1] = '\0';
89 			type(p);
90 			if (ifile>=0)
91 				close(ifile);
92 		}
93 		exit(1);
94 	}
95 	while(argc > 1) {
96 		ifile = -1;
97 		type(argv[1]);
98 		fflush(stdout);
99 		argc--;
100 		argv++;
101 		if (ifile >= 0)
102 			close(ifile);
103 	}
104 	exit(0);
105 }
106 
107 type(file)
108 char *file;
109 {
110 	int j,nl;
111 	char ch;
112 	struct stat mbuf;
113 	char slink[MAXPATHLEN + 1];
114 	struct exec *hdr;
115 #if defined(hp300) || defined(hp800)
116 	int ishpux300 = 0;
117 	int ishpux800 = 0;
118 #endif
119 
120 	if (statfcn(file, &mbuf) < 0 &&
121 	    (statfcn == lstat || lstat(file, &mbuf))) {
122 		fprintf(stderr, "file: %s: %s\n", file, strerror(errno));
123 		return;
124 	}
125 	switch (mbuf.st_mode & S_IFMT) {
126 	case S_IFLNK:
127 		printf("%s:\tsymbolic link", file);
128 		j = readlink(file, slink, sizeof slink - 1);
129 		if (j >= 0) {
130 			slink[j] = '\0';
131 			printf(" to %s", slink);
132 		}
133 		printf("\n");
134 		return;
135 
136 	case S_IFDIR:
137 		printf("%s:\t", file);
138 		if (mbuf.st_mode & S_ISVTX)
139 			printf("append-only ");
140 		printf("directory\n");
141 		return;
142 
143 	case S_IFCHR:
144 	case S_IFBLK:
145 		printf("%s:\t%s special (%d/%d)\n", file,
146 		    (mbuf.st_mode&S_IFMT) == S_IFCHR ? "character" : "block",
147 		     major(mbuf.st_rdev), minor(mbuf.st_rdev));
148 		return;
149 
150 	case S_IFSOCK:
151 		printf("%s:\tsocket\n", file);
152 		return;
153 	}
154 
155 	ifile = open(file, 0);
156 	if (ifile < 0) {
157 		fprintf(stderr, "file: %s: %s\n", file, strerror(errno));
158 		return;
159 	}
160 	printf("%s:\t", file);
161 	in = read(ifile, buf, BUFSIZE);
162 	if (in == 0) {
163 		printf("empty\n");
164 		return;
165 	}
166 	hdr = (struct exec *) buf;
167 #ifdef MID_ZERO		/* if we have a_mid field */
168 	switch (hdr->a_mid) {
169 	case MID_SUN010:
170 		printf("SUN 68010/68020 ");
171 		break;
172 	case MID_SUN020:
173 		printf("SUN 68020 ");
174 		break;
175 	case MID_HP200:
176 		printf("HP200 ");
177 		break;
178 	case MID_HP300:
179 		printf("HP300 ");
180 		break;
181 #if defined(hp300) || defined(hp800)
182 	case MID_HPUX:
183 		printf("HP-UX series [234]00 ");
184 		ishpux300 = 1;
185 		if (hdr->a_magic == 0406) {
186 			printf("relocatable object\n");
187 			return;
188 		}
189 		break;
190 	case MID_HPUX800:
191 		printf("HP-UX series 800 ");
192 		ishpux800 = 1;
193 		if (hdr->a_magic == 0x106) {
194 			printf("relocatable object\n");
195 			return;
196 		}
197 		break;
198 #endif
199 #ifdef MID_MIPSI
200 	case MID_MIPSI:
201 		printf("MIPS R3000 ");
202 		break;
203 #endif
204 #ifdef MID_MIPSII
205 	case MID_MIPSII:
206 		printf("MIPS R4000 ");
207 		break;
208 #endif
209 #if BYTE_ORDER == BIG_ENDIAN
210 	case ((OMAGIC & 0xff) << 8) | (OMAGIC >> 8):
211 	case ((NMAGIC & 0xff) << 8) | (NMAGIC >> 8):
212 	case ((ZMAGIC & 0xff) << 8) | (ZMAGIC >> 8):
213 		printf("byte-swapped (VAX/386) ");
214 		hdr->a_magic = ((hdr->a_mid & 0xff) << 8) | (hdr->a_mid >> 8);
215 		break;
216 #endif
217 	}
218 #endif /* MID_ZERO, a_mid */
219 	switch (hdr->a_magic) {
220 
221 	case 0411:
222 		printf("jfr or pdp-11 unix 411 executable\n");
223 		return;
224 
225 	case ZMAGIC:
226 		printf("demand paged ");
227 		/* FALLTHROUGH */
228 
229 	case NMAGIC:
230 		printf("pure ");
231 		/* FALLTHROUGH */
232 
233 	case OMAGIC:
234 		if (mbuf.st_mode & S_ISUID)
235 			printf("set-uid ");
236 		if (mbuf.st_mode & S_ISGID)
237 			printf("set-gid ");
238 		if (mbuf.st_mode & S_ISVTX)
239 			printf("sticky ");
240 		if ((mbuf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0 &&
241 			(hdr->a_trsize || hdr->a_drsize)) {
242 			printf("relocatable object\n");
243 			return;
244 		}
245 #if defined(hp300)
246 		if (ishpux300) {
247 			if (((int *)buf)[2] & 0x40000000)
248 				printf("dynamically-linked ");
249 		}
250 #endif
251 		printf("executable");
252 #if defined(hp300) || defined(hp800)
253 		if (ishpux300) {
254 			if (((int *)buf)[9] != 0)
255 				printf(" not stripped");
256 		} else if (ishpux800) {
257 			if (((int *)buf)[24] != 0)
258 				printf(" not stripped");
259 		} else
260 #endif
261 		if (hdr->a_syms != 0)
262 			printf(" not stripped");
263 		printf("\n");
264 		return;
265 #if defined(hp300)
266 	case 0x10e:
267 		printf("shared library, version %d\n", ((short *)buf)[2]);
268 		return;
269 #endif
270 	}
271 
272 	switch (*(int *)buf) {
273 	case 0177555:
274 		printf("very old archive\n");
275 		return;
276 
277 	case 0177545:
278 		printf("old archive\n");
279 		return;
280 
281 	case 070707:
282 		printf("cpio data\n");
283 		return;
284 	}
285 
286 	if (buf[0] == '#' && buf[1] == '!' && shellscript(buf+2, &mbuf))
287 		return;
288 	if (buf[0] == '\037' && buf[1] == '\235') {
289 		if (buf[2]&0x80)
290 			printf("block ");
291 		printf("compressed %d bit code data\n", buf[2]&0x1f);
292 		return;
293 	}
294 	if (strncmp(buf, "!<arch>\n__.SYMDEF", 17) == 0 ) {
295 		printf("archive random library\n");
296 		return;
297 	}
298 	if (strncmp(buf, "!<arch>\n", 8)==0) {
299 		printf("archive\n");
300 		return;
301 	}
302 	if (mbuf.st_size % 512 == 0) {	/* it may be a PRESS file */
303 		lseek(ifile, -512L, 2);	/* last block */
304 		if (read(ifile, buf, BUFSIZE) > 0 && *(short *)buf == 12138) {
305 			printf("PRESS file\n");
306 			return;
307 		}
308 	}
309 	i = 0;
310 	if(ccom() == 0)goto notc;
311 	while(buf[i] == '#'){
312 		j = i;
313 		while(buf[i++] != '\n'){
314 			if(i - j > 255){
315 				printf("data\n");
316 				return;
317 			}
318 			if(i >= in)goto notc;
319 		}
320 		if(ccom() == 0)goto notc;
321 	}
322 check:
323 	if(lookup(c) == 1){
324 		while((ch = buf[i++]) != ';' && ch != '{')if(i >= in)goto notc;
325 		printf("c program text");
326 		goto outa;
327 	}
328 	nl = 0;
329 	while(buf[i] != '('){
330 		if(buf[i] <= 0)
331 			goto notas;
332 		if(buf[i] == ';'){
333 			i++;
334 			goto check;
335 		}
336 		if(buf[i++] == '\n')
337 			if(nl++ > 6)goto notc;
338 		if(i >= in)goto notc;
339 	}
340 	while(buf[i] != ')'){
341 		if(buf[i++] == '\n')
342 			if(nl++ > 6)goto notc;
343 		if(i >= in)goto notc;
344 	}
345 	while(buf[i] != '{'){
346 		if(buf[i++] == '\n')
347 			if(nl++ > 6)goto notc;
348 		if(i >= in)goto notc;
349 	}
350 	printf("c program text");
351 	goto outa;
352 notc:
353 	i = 0;
354 	while(buf[i] == 'c' || buf[i] == '#'){
355 		while(buf[i++] != '\n')if(i >= in)goto notfort;
356 	}
357 	if(lookup(fort) == 1){
358 		printf("fortran program text");
359 		goto outa;
360 	}
361 notfort:
362 	i=0;
363 	if(ascom() == 0)goto notas;
364 	j = i-1;
365 	if(buf[i] == '.'){
366 		i++;
367 		if(lookup(as) == 1){
368 			printf("assembler program text");
369 			goto outa;
370 		}
371 		else if(buf[j] == '\n' && isalpha(buf[j+2])){
372 			printf("roff, nroff, or eqn input text");
373 			goto outa;
374 		}
375 	}
376 	while(lookup(asc) == 0){
377 		if(ascom() == 0)goto notas;
378 		while(buf[i] != '\n' && buf[i++] != ':')
379 			if(i >= in)goto notas;
380 		while(buf[i] == '\n' || buf[i] == ' ' || buf[i] == '\t')if(i++ >= in)goto notas;
381 		j = i-1;
382 		if(buf[i] == '.'){
383 			i++;
384 			if(lookup(as) == 1){
385 				printf("assembler program text");
386 				goto outa;
387 			}
388 			else if(buf[j] == '\n' && isalpha(buf[j+2])){
389 				printf("roff, nroff, or eqn input text");
390 				goto outa;
391 			}
392 		}
393 	}
394 	printf("assembler program text");
395 	goto outa;
396 notas:
397 	for(i=0; i < in; i++)if(buf[i]&0200){
398 		if (buf[0]=='\100' && buf[1]=='\357')
399 			printf("troff (CAT) output\n");
400 		else
401 			printf("data\n");
402 		return;
403 	}
404 	if (mbuf.st_mode&((S_IEXEC)|(S_IEXEC>>3)|(S_IEXEC>>6))) {
405 		if (mbuf.st_mode & S_ISUID)
406 			printf("set-uid ");
407 		if (mbuf.st_mode & S_ISGID)
408 			printf("set-gid ");
409 		if (mbuf.st_mode & S_ISVTX)
410 			printf("sticky ");
411 		if (shell(buf, in, sh))
412 			printf("shell script");
413 		else if (shell(buf, in, csh))
414 			printf("c-shell script");
415 		else
416 			printf("commands text");
417 	} else if (troffint(buf, in))
418 		printf("troff intermediate output text");
419 	else if (shell(buf, in, sh))
420 		printf("shell commands");
421 	else if (shell(buf, in, csh))
422 		printf("c-shell commands");
423 	else if (english(buf, in))
424 		printf("English text");
425 	else
426 		printf("ascii text");
427 outa:
428 	while(i < in)
429 		if((buf[i++]&0377) > 127){
430 			printf(" with garbage\n");
431 			return;
432 		}
433 	/* if next few lines in then read whole file looking for nulls ...
434 		while((in = read(ifile,buf,BUFSIZE)) > 0)
435 			for(i = 0; i < in; i++)
436 				if((buf[i]&0377) > 127){
437 					printf(" with garbage\n");
438 					return;
439 				}
440 		/*.... */
441 	printf("\n");
442 }
443 
444 troffint(bp, n)
445 char *bp;
446 int n;
447 {
448 	int k;
449 
450 	i = 0;
451 	for (k = 0; k < 6; k++) {
452 		if (lookup(troff) == 0)
453 			return(0);
454 		if (lookup(troff) == 0)
455 			return(0);
456 		while (i < n && buf[i] != '\n')
457 			i++;
458 		if (i++ >= n)
459 			return(0);
460 	}
461 	return(1);
462 }
463 lookup(tab)
464 char *tab[];
465 {
466 	char r;
467 	int k,j,l;
468 	while(buf[i] == ' ' || buf[i] == '\t' || buf[i] == '\n')i++;
469 	for(j=0; tab[j] != 0; j++){
470 		l=0;
471 		for(k=i; ((r=tab[j][l++]) == buf[k] && r != '\0');k++);
472 		if(r == '\0')
473 			if(buf[k] == ' ' || buf[k] == '\n' || buf[k] == '\t'
474 			    || buf[k] == '{' || buf[k] == '/'){
475 				i=k;
476 				return(1);
477 			}
478 	}
479 	return(0);
480 }
481 ccom(){
482 	char cc;
483 	while((cc = buf[i]) == ' ' || cc == '\t' || cc == '\n')if(i++ >= in)return(0);
484 	if(buf[i] == '/' && buf[i+1] == '*'){
485 		i += 2;
486 		while(buf[i] != '*' || buf[i+1] != '/'){
487 			if(buf[i] == '\\')i += 2;
488 			else i++;
489 			if(i >= in)return(0);
490 		}
491 		if((i += 2) >= in)return(0);
492 	}
493 	if(buf[i] == '\n')if(ccom() == 0)return(0);
494 	return(1);
495 }
496 ascom(){
497 	while(buf[i] == '/'){
498 		i++;
499 		while(buf[i++] != '\n')if(i >= in)return(0);
500 		while(buf[i] == '\n')if(i++ >= in)return(0);
501 	}
502 	return(1);
503 }
504 
505 english (bp, n)
506 char *bp;
507 {
508 # define NASC 128
509 	int ct[NASC], j, vow, freq, rare;
510 	int badpun = 0, punct = 0;
511 	if (n<50) return(0); /* no point in statistics on squibs */
512 	for(j=0; j<NASC; j++)
513 		ct[j]=0;
514 	for(j=0; j<n; j++)
515 	{
516 		if ((u_char)bp[j]<NASC)
517 			ct[bp[j]|040]++;
518 		switch (bp[j])
519 		{
520 		case '.':
521 		case ',':
522 		case ')':
523 		case '%':
524 		case ';':
525 		case ':':
526 		case '?':
527 			punct++;
528 			if ( j < n-1 &&
529 			    bp[j+1] != ' ' &&
530 			    bp[j+1] != '\n')
531 				badpun++;
532 		}
533 	}
534 	if (badpun*5 > punct)
535 		return(0);
536 	vow = ct['a'] + ct['e'] + ct['i'] + ct['o'] + ct['u'];
537 	freq = ct['e'] + ct['t'] + ct['a'] + ct['i'] + ct['o'] + ct['n'];
538 	rare = ct['v'] + ct['j'] + ct['k'] + ct['q'] + ct['x'] + ct['z'];
539 	if (2*ct[';'] > ct['e']) return(0);
540 	if ( (ct['>']+ct['<']+ct['/'])>ct['e']) return(0); /* shell file test */
541 	return (vow*5 >= n-ct[' '] && freq >= 10*rare);
542 }
543 
544 shellscript(buf, sb)
545 	char buf[];
546 	struct stat *sb;
547 {
548 	register char *tp;
549 	char *cp, *xp, *index();
550 
551 	cp = index(buf, '\n');
552 	if (cp == 0 || cp - buf > in)
553 		return (0);
554 	for (tp = buf; tp != cp && isspace(*tp); tp++)
555 		if (!isascii(*tp))
556 			return (0);
557 	for (xp = tp; tp != cp && !isspace(*tp); tp++)
558 		if (!isascii(*tp))
559 			return (0);
560 	if (tp == xp)
561 		return (0);
562 	if (sb->st_mode & S_ISUID)
563 		printf("set-uid ");
564 	if (sb->st_mode & S_ISGID)
565 		printf("set-gid ");
566 	if (strncmp(xp, "/bin/sh", tp-xp) == 0)
567 		xp = "shell";
568 	else if (strncmp(xp, "/bin/csh", tp-xp) == 0)
569 		xp = "c-shell";
570 	else
571 		*tp = '\0';
572 	printf("executable %s script\n", xp);
573 	return (1);
574 }
575 
576 shell(bp, n, tab)
577 	char *bp;
578 	int n;
579 	char *tab[];
580 {
581 
582 	i = 0;
583 	do {
584 		if (buf[i] == '#' || buf[i] == ':')
585 			while (i < n && buf[i] != '\n')
586 				i++;
587 		if (++i >= n)
588 			break;
589 		if (lookup(tab) == 1)
590 			return (1);
591 	} while (i < n);
592 	return (0);
593 }
594