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