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