279 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			279 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /* file - report on file type.		Author: Andy Tanenbaum */
 | |
| /* Magic number detection changed to look-up table 08-Jan-91 - ajm */
 | |
| 
 | |
| #include <sys/types.h>
 | |
| #include <sys/stat.h>
 | |
| #include <fcntl.h>
 | |
| #include <stdlib.h>
 | |
| #include <unistd.h>
 | |
| #include <stdio.h>
 | |
| 
 | |
| #define BLOCK_SIZE	1024
 | |
| 
 | |
| #define XBITS 00111		/* rwXrwXrwX (x bits in the mode) */
 | |
| #define ENGLISH 25		/* cutoff for determining if text is Eng. */
 | |
| unsigned char buf[BLOCK_SIZE];
 | |
| 
 | |
| struct info {
 | |
|   int execflag;			/* 1 == ack executable, 2 == gnu executable,
 | |
| 				 * 3 == core */
 | |
|   unsigned char magic[4];	/* First four bytes of the magic number */
 | |
|   unsigned char mask[4];	/* Mask to apply when matching */
 | |
|   char *description;		/* What it means */
 | |
| } table[] = {
 | |
|   0x00, 0x1f, 0x9d, 0x8d, 0x00,		0xff, 0xff, 0xff, 0x00,
 | |
| 	"13-bit compressed file",
 | |
|   0x00, 0x1f, 0x9d, 0x90, 0x00,		0xff, 0xff, 0xff, 0x00,
 | |
| 	"16-bit compressed file",
 | |
|   0x00, 0x65, 0xff, 0x00, 0x00,		0xff, 0xff, 0x00, 0x00,
 | |
| 	"MINIX-PC bcc archive",
 | |
|   0x00, 0x2c, 0xff, 0x00, 0x00,		0xff, 0xff, 0x00, 0x00,
 | |
| 	"ACK object archive",
 | |
|   0x00, 0x65, 0xff, 0x00, 0x00,		0xff, 0xff, 0x00, 0x00,
 | |
| 	"MINIX-PC ack archive",
 | |
|   0x00, 0x47, 0x6e, 0x75, 0x20,		0xff, 0xff, 0xff, 0xff,
 | |
| 	"MINIX-68k gnu archive",
 | |
|   0x00, 0x21, 0x3c, 0x61, 0x72,		0xff, 0xff, 0xff, 0xff,
 | |
| 	"MINIX-PC gnu archive",
 | |
|   0x00, 0x01, 0x02, 0x00, 0x00,		0xff, 0xff, 0x00, 0x00,
 | |
| 	"ACK object file",
 | |
|   0x00, 0xa3, 0x86, 0x00, 0x00, 	0xff, 0xff, 0x00, 0x00,
 | |
| 	"MINIX-PC bcc object file",
 | |
|   0x00, 0x00, 0x00, 0x01, 0x07, 	0xff, 0xff, 0xff, 0xff,
 | |
| 	"MINIX-68k gnu object file",
 | |
|   0x00, 0x07, 0x01, 0x00, 0x00, 	0xff, 0xff, 0xff, 0xff,
 | |
| 	"MINIX-PC gnu object file",
 | |
|   0x01, 0x01, 0x03, 0x00, 0x04, 	0xff, 0xff, 0x00, 0xff,
 | |
| 	"MINIX-PC 16-bit executable",
 | |
|   0x01, 0x01, 0x03, 0x00, 0x10, 	0xff, 0xff, 0x00, 0xff,
 | |
| 	"MINIX-PC 32-bit executable",
 | |
|   0x01, 0x04, 0x10, 0x03, 0x01, 	0xff, 0xff, 0xff, 0xff,
 | |
| 	"MINIX-68k old style executable",
 | |
|   0x01, 0x01, 0x03, 0x10, 0x0b, 	0xff, 0xff, 0xff, 0xff,
 | |
| 	"MINIX-68k new style executable",
 | |
|   0x02, 0x0b, 0x01, 0x00, 0x00, 	0xff, 0xff, 0xff, 0xff,
 | |
| 	"MINIX-PC 32-bit gnu executable combined I & D space",
 | |
|   0x02, 0x00, 0x00, 0x0b, 0x01, 	0xff, 0xff, 0xff, 0xff,
 | |
| 	"MINIX-68k gnu executable",
 | |
|   0x03, 0x82, 0x12, 0xC4, 0xC0,		0xff, 0xff, 0xff, 0xff,
 | |
| 	"core file",
 | |
| };
 | |
| 
 | |
| int tabsize = sizeof(table) / sizeof(struct info);
 | |
| int L_flag;
 | |
| 
 | |
| _PROTOTYPE(int main, (int argc, char **argv));
 | |
| _PROTOTYPE(void file, (char *name));
 | |
| _PROTOTYPE(void do_strip, (int type));
 | |
| _PROTOTYPE(void usage, (void));
 | |
| 
 | |
| int main(argc, argv)
 | |
| int argc;
 | |
| char *argv[];
 | |
| {
 | |
| /* This program uses some heuristics to try to guess about a file type by
 | |
|  * looking at its contents.
 | |
|  */
 | |
|   int c, i;
 | |
| 
 | |
|   L_flag= 0;
 | |
|   while ((c= getopt(argc, argv, "L?")) != -1)
 | |
|   {
 | |
| 	switch(c)
 | |
| 	{
 | |
| 	case 'L':
 | |
| 		L_flag= 1;
 | |
| 		break;
 | |
| 	case '?':
 | |
| 		usage();
 | |
| 	default:
 | |
| 		fprintf(stderr, "file: panic getopt failed\n");
 | |
| 		exit(1);
 | |
| 	}
 | |
|   }
 | |
|   if (optind >= argc) usage();
 | |
|   for (i = optind; i < argc; i++) file(argv[i]);
 | |
|   return(0);
 | |
| }
 | |
| 
 | |
| void file(name)
 | |
| char *name;
 | |
| {
 | |
|   int i, fd, n, mode, nonascii, special, funnypct, etaoins;
 | |
|   int j;
 | |
|   long engpct;
 | |
|   int c;
 | |
|   struct stat st_buf;
 | |
| 
 | |
|   printf("%s: ", name);
 | |
| 
 | |
| #ifdef S_IFLNK
 | |
|   if (!L_flag)
 | |
| 	n = lstat(name, &st_buf);
 | |
|   else
 | |
| 	n = stat(name, &st_buf);
 | |
| #else
 | |
|   n = stat(name, &st_buf);
 | |
| #endif
 | |
|   if (n < 0) {
 | |
| 	printf("cannot stat\n");
 | |
| 	return;
 | |
|   }
 | |
|   mode = st_buf.st_mode;
 | |
| 
 | |
|   /* Check for directories and special files. */
 | |
|   if (S_ISDIR(mode)) {
 | |
| 	printf("directory\n");
 | |
| 	return;
 | |
|   }
 | |
|   if (S_ISCHR(mode)) {
 | |
| 	printf("character special file\n");
 | |
| 	return;
 | |
|   }
 | |
|   if (S_ISBLK(mode)) {
 | |
| 	printf("block special file\n");
 | |
| 	return;
 | |
|   }
 | |
|   if (S_ISFIFO(mode)) {
 | |
| 	printf("named pipe\n");
 | |
| 	return;
 | |
|   }
 | |
| #ifdef S_IFLNK
 | |
|   if (S_ISLNK(mode)) {
 | |
| 	n= readlink(name, (char *)buf, BLOCK_SIZE);
 | |
| 	if (n == -1)
 | |
| 		printf("cannot readlink\n");
 | |
| 	else
 | |
| 		printf("symbolic link to %.*s\n", n, buf);
 | |
| 	return;
 | |
|   }
 | |
| #endif
 | |
|   if (!S_ISREG(mode)) {
 | |
| 	printf("strange file type %5o\n", mode);
 | |
| 	return;
 | |
|   }
 | |
| 
 | |
|   /* Open the file, stat it, and read in 1 block. */
 | |
|   fd = open(name, O_RDONLY);
 | |
|   if (fd < 0) {
 | |
| 	printf("cannot open\n");
 | |
| 	return;
 | |
|   }
 | |
|   n = read(fd, (char *)buf, BLOCK_SIZE);
 | |
|   if (n < 0) {
 | |
| 	printf("cannot read\n");
 | |
| 	close(fd);
 | |
| 	return;
 | |
|   }
 | |
|   if (n == 0) {       /* must check this, for loop will fail otherwise !! */
 | |
|       printf("empty file\n");
 | |
|       close(fd);
 | |
|       return;
 | |
|   }
 | |
| 
 | |
|   for (i = 0; i < tabsize; i++) {
 | |
| 	for (j = 0; j < 4; j++)
 | |
| 		if ((buf[j] & table[i].mask[j]) != table[i].magic[j])
 | |
| 			break;
 | |
| 	if (j == 4) {
 | |
| 		printf("%s", table[i].description);
 | |
| 		do_strip(table[i].execflag);
 | |
| 		close(fd);
 | |
| 		return;
 | |
| 	}
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /* Check to see if file is a shell script. */
 | |
|   if (mode & XBITS) {
 | |
| 	/* Not a binary, but executable.  Probably a shell script. */
 | |
| 	printf("shell script\n");
 | |
| 	close(fd);
 | |
| 	return;
 | |
|   }
 | |
| 
 | |
|   /* Check for ASCII data and certain punctuation. */
 | |
|   nonascii = 0;
 | |
|   special = 0;
 | |
|   etaoins = 0;
 | |
|   for (i = 0; i < n; i++) {
 | |
| 	c = buf[i];
 | |
| 	if (c & 0200) nonascii++;
 | |
| 	if (c == ';' || c == '{' || c == '}' || c == '#') special++;
 | |
| 	if (c == '*' || c == '<' || c == '>' || c == '/') special++;
 | |
| 	if (c >= 'A' && c <= 'Z') c = c - 'A' + 'a';
 | |
| 	if (c == 'e' || c == 't' || c == 'a' || c == 'o') etaoins++;
 | |
| 	if (c == 'i' || c == 'n' || c == 's') etaoins++;
 | |
|   }
 | |
| 
 | |
|   if (nonascii == 0) {
 | |
| 	/* File only contains ASCII characters.  Continue processing. */
 | |
| 	funnypct = 100 * special / n;
 | |
| 	engpct = 100L * (long) etaoins / n;
 | |
| 	if (funnypct > 1) {
 | |
| 		printf("C program\n");
 | |
| 	} else {
 | |
| 		if (engpct > (long) ENGLISH)
 | |
| 			printf("English text\n");
 | |
| 		else
 | |
| 			printf("ASCII text\n");
 | |
| 	}
 | |
| 	close(fd);
 | |
| 	return;
 | |
|   }
 | |
| 
 | |
|   /* Give up.  Call it data. */
 | |
|   printf("data\n");
 | |
|   close(fd);
 | |
|   return;
 | |
| }
 | |
| 
 | |
| void do_strip(type)
 | |
| int type;
 | |
| {
 | |
|   if (type == 1) {	/* Non-GNU executable */
 | |
| 	if (buf[2] & 1)
 | |
| 		printf(", UZP");
 | |
| 	if (buf[2] & 2)
 | |
| 		printf(", PAL");
 | |
| 	if (buf[2] & 4)
 | |
| 		printf(", NSYM");
 | |
| 	if (buf[2] & 0x20)
 | |
| 		printf(", sep I&D");
 | |
| 	else
 | |
| 		printf(", comm I&D");
 | |
| 	if (( buf[28] | buf[29] | buf[30] | buf[31]) != 0)
 | |
| 		printf(" not stripped\n");
 | |
| 	else
 | |
| 		printf(" stripped\n");
 | |
| 	return;
 | |
|   }
 | |
| 
 | |
|   if (type == 2) {	/* GNU format executable */
 | |
|      if ((buf[16] | buf[17] | buf[18] | buf[19]) != 0)
 | |
| 	 printf(" not stripped\n");
 | |
|      else
 | |
| 	 printf(" stripped\n");
 | |
|      return;
 | |
|   }
 | |
| 
 | |
|   if (type == 3) {	/* Core file in <sys/core.h> format */
 | |
| 	switch(buf[36] & 0xff)
 | |
| 	{
 | |
| 		case 1:	printf(" of i86 executable"); break;
 | |
| 		case 2:	printf(" of i386 executable"); break;
 | |
| 		default:printf(" of unknown executable"); break;
 | |
| 	}
 | |
| 	printf(" '%.32s'\n", buf+4);
 | |
| 	return;
 | |
|   }
 | |
| 
 | |
|   printf("\n");		/* Not an executable file */
 | |
|  }
 | |
| 
 | |
| void usage()
 | |
| {
 | |
|   printf("Usage: file [-L] name ...\n");
 | |
|   exit(1);
 | |
| }
 | 
