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);
|
||
|
}
|