279 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			279 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* 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);
 | 
						|
}
 |