--- /dev/null
+Written by Ian Darwin,
+Box 603, Station F, Toronto, CANADA M4Y 2L8.
+Uucp: {utzoo|ihnp4}!darwin!ian.
+Internet: ian@sq.com (proposed).
+
+Copyright (c) 1986, Ian Darwin. All rights reserved, except as noted below.
+
+This program may be used for its intended purpose on any computer system.
+
+ *** Unmodified copies ***
+of the source may be freely copied for this purpose
+provided this notice is maintained.
+ *** Redistribution of modified source copies is prohibited; ***
+distribute the original source with your changes as diff listings
+or as RCS archives (or SCCS if you can find a way to mail them).
+Please send me your changes; if I like 'em I'll incorporate them.
+
+Binary copies may also be distributed; if they are derived from
+modified source then you must inform every recipient of the
+origin of the program and that the source is available from the author.
+
+The module is_tar.c is by Fred Blonder at the University of Maryland
+and appeared as part of his fixes to the UNIX file(1) program
+in net.sources; it is not covered by the above restrictions.
+
+The module strtok.c is by Henry Spencer at the University of Toronto
+and is not covered by the above restrictions.
+
+The module strtol is a quick hack and is not covered by the above.
--- /dev/null
+** ALPHA TEST RELEASE **
+You are getting this ahead of the rest of the world because
+you need it or because I like you or something. Please do not
+forward it to anybody yet. The "official" version will appear
+in mod.sources at a theater or USENET site near you in time
+for the Christmas rush.
+
+** File(1) Command **
+This is Ian Darwin's (copyright but distributable unmodified)
+file(1) command. It follows the USG (Sys V) model of the file
+command, rather than the Research (V7) version. That is, there
+is a file (/etc/magic) that contains much of the ritual information
+that is the source of this program's power. It knows a little
+more magic (including tar archives) than System V; the /etc/magic
+parsing seems to be compatible with the (poorly documented)
+System V /etc/magic format.
+
+In addition, the /etc/magic file is built from a subdirectory
+for easier maintenance. At the instigation of John Gilmore,
+I will act as a clearinghouse for magic numbers assigned to
+all sorts of whacko data files that are in reasonable circulation.
+Send your whacko magic numbers, in file(5) format please(!), to the
+address above.
+
+LEGAL.NOTICE - read this first.
+README - read this second.
+Makefile - read this third.
+TODO - read this last (sigh).
+apprentice.c - parses /etc/magic to learn magic
+ascmagic.c - third & last set of tests, based on hardwired assumptions.
+core - not included in distribution
+debug.c - includes -c printout routine
+file.1 - man page
+file.c - main program
+file.h - fairly standard header file
+fsmagic.c - first set of tests the program runs, based on filesystem info
+is_tar.c - knows about tarchives (courtesy of Fred Blonder, U Maryland)
+magdir - directory of /etc/magic pieces
+names.h - header file for ascmagic.c
+softmagic.c - 2nd set of tests, based on /etc/magic
+strtol.c - in case you need it
+strtol.tst.c - in case you need to test it
+
--- /dev/null
+# Makefile for file(1) cmd. Copyright (c) Darwin 86/09/01 - see LEGAL.NOTICE.
+
+DEFS = # -Dvoid=int
+CFLAGS = -O $(DEFS)
+OTHERS = darwin!ian # people to whom we mail updates
+FILE=/usr/bin/file.sun # old version or system-provided command, for comparison
+
+# There are no system-dependant configuration options (except maybe CFLAGS);
+# you may delete some of LOCALSRCS and LOCALOBJS if they're in your C library.
+LOCALSRCS = strtol.c strtok.c
+SRCS = file.c apprentice.c fsmagic.c softmagic.c ascmagic.c is_tar.c \
+ print.c $(LOCALSRCS)
+LOCALOBJS = strtol.o strtok.o
+OBJS = file.o apprentice.o fsmagic.o softmagic.o ascmagic.o is_tar.o \
+ print.o $(LOCALOBJS)
+
+ALLSRC = LEGAL.NOTICE README PORTING TODO $(SRCS) *.h \
+ Makefile file.[15] magdir tst/Makefile
+
+run: file magic
+ time $(FILE) -m ./magic * tst/* >/tmp/t1
+ time ./file -m ./magic * tst/* >/tmp/t2
+ diff -b /tmp/t[12]
+
+file: $(OBJS)
+ cc $(CFLAGS) $(OBJS) -o $@
+lint: $(SRCS)
+ lint -ha $(DEFS) $(SRCS)
+magic: magdir
+ cat magdir/[a-z]* >$@ # exclude RCS or SCCS dirs
+
+ascmagic.o: names.h
+
+apprentice.o ascmagic.o file.o fsmagic.o print.o softmagic.o: file.h
+
+dist: $(ALLSRC)
+ (echo mkdir tst; bundle $(ALLSRC)) | \
+ mailx -s 'semi-public-domain file(1)' $(OTHERS)
+update: $(ALLSRC)
+ (echo mkdir tst; bundle $?; echo touch $@) | mail $(OTHERS)
+ touch $@
--- /dev/null
+Portability of the new file(1) command.
+
+Obviously if you are running the old Ritchie compiler or
+something else that doesn't know about void, you will have
+to un-comment-out the definition of `void=int' in the Makefile.
+
+I had hoped to be able to make a file command that
+didn't have any system-dependant #ifdefs, no special libraries,
+etc., etc. Well, System V shot me down. They moved the definition
+of major() and minor() out of <sys/types.h> into <sys/sysmacros.h>.
+So, if major isn't defined after including types.h, I automatically
+include sys/sysmacros.h. If you have a system in which neither
+types.h nor sysmacros.h defines `major', you will get a compilation
+error in trying to compile a warning message. Please do the following:
+ 1) change the appropriate (2nd) #include at the start of
+ fsmagic.c
+and 2) let me know the name of the system, the release number,
+ and the name of the header file that *does* include
+ this "standard" definition.
+
+Other than this, there should be no portability problems,
+but one never knows these days. Please let me know of any
+other problems you find porting to a UNIX system. I don't much
+care about non-UNIX systems but will collect magic numbers
+for them.
+
+Ian Darwin
+Toronto, Canada
--- /dev/null
+/*
+ * make one pass through /etc/magic, reading it into core
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "file.h"
+
+#define MAXSTR 500
+#define EATAB {while (isascii(*l) && *l == '\t') ++l;}
+
+extern char *progname;
+extern char *magicfile;
+extern int debug, check; /* options */
+extern int nmagic; /* number of valid magic[]s */
+extern long strtol();
+
+struct magic magic[MAXMAGIS];
+
+apprentice(fn)
+char *fn;
+{
+ FILE *f;
+ char line[MAXSTR+1];
+
+ f = fopen(fn, "r");
+ if (f==NULL) {
+ (void) fprintf(stderr, "%s: can't read magic file %s\n",
+ progname, fn);
+ exit(1);
+ }
+
+ /* parse it */
+ if (check) /* print silly verbose header for USG compat. */
+ (void) printf("cont\toffset\ttype\topcode\tvalue\tdesc\n");
+ while (fgets(line, MAXSTR, f) != NULL) {
+ if (line[0]=='#')
+ continue;
+ line[strlen(line)-1] = '\0'; /* nuke newline */
+ (void) parse(line, &nmagic);
+ }
+
+ (void) fclose(f);
+}
+
+/*
+ * parse one line from magic file, put into magic[index++] if valid
+ */
+
+
+parse(l, ndx)
+char *l;
+int *ndx;
+{
+ int i = 0, nd = *ndx;
+ static int warned = 0;
+ struct magic *m;
+
+ if (nd+1 >= MAXMAGIS){
+ if (warned++ == 0)
+ warning("magic tab overflow - increase MAXMAGIS beyond %d in file/apprentice.c\n", MAXMAGIS);
+ return 0;
+ }
+ m = &magic[*ndx];
+
+ if (*l == '>') {
+ ++l; /* step over */
+ m->contflag = 1;
+ } else
+ m->contflag = 0;
+
+ /* get offset, then skip over it */
+ m->offset = atoi(l);
+ while (isascii(*l) && isdigit(*l))
+ ++l;
+ EATAB;
+
+#define NBYTE 4
+#define NSHORT 5
+#define NLONG 4
+#define NSTRING 6
+ /* get type, skip it */
+ if (strncmp(l, "byte", NBYTE)==0) {
+ m->type = BYTE;
+ l += NBYTE;
+ } else if (strncmp(l, "short", NSHORT)==0) {
+ m->type = SHORT;
+ l += NSHORT;
+ } else if (strncmp(l, "long", NLONG)==0) {
+ m->type = LONG;
+ l += NLONG;
+ } else if (strncmp(l, "string", NSTRING)==0) {
+ m->type = STRING;
+ l += NSTRING;
+ } else {
+ warning("type %s invalid\n", l);
+ return 0;
+ }
+ EATAB;
+
+ if (*l == '>' || *l == '&' || *l == '=') {
+ m->reln = *l;
+ ++l;
+ } else
+ m->reln = '=';
+ EATAB;
+
+/* TODO finish this macro and start using it! */
+#define offsetcheck {if (offset > HOWMANY-1) warning("offset too big"); }
+ switch(m->type) {
+ case BYTE:
+ m->value.l = strtol(l,&l,0);
+ break;
+ case SHORT:
+ m->value.l = strtol(l,&l,0);
+ break;
+ case LONG:
+ m->value.l = strtol(l,&l,0);
+ break;
+ case STRING:
+ /* TODO check return from sscanf (system dependant?) */
+ (void) sscanf(l, "%[^\t]", m->value.s);
+ l += strlen(m->value.s);
+ break;
+ default:
+ warning("can't happen: m->type=%d\n", m->type);
+ }
+
+ /*
+ * now get last part - the description
+ */
+ EATAB;
+ while ((m->desc[i++] = *l++) != '\0' && i<MAXDESC)
+ /* NULLBODY */;
+
+ if (check) {
+ mdump(m);
+ }
+ ++(*ndx); /* make room for next */
+ return 1;
+}
+
--- /dev/null
+/*
+ * Ascii magic -- file types that we know based on keywords
+ * that can appear anywhere in the file.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "file.h"
+char ckfmsg[] = "write error on output";
+#include "names.h"
+
+ /* an optimisation over plain strcmp() */
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+ascmagic(buf)
+register char *buf;
+{
+ register int i;
+ char *s, *strtok(), *token;
+ register struct names *p;
+ extern int nbytes;
+ short has_escapes = 0;
+
+ /* these are easy, do them first */
+
+ /*
+ * for troff, look for . + letter + letter;
+ * this must be done to disambiguate tar archives' ./file
+ * and other trash from real troff input.
+ */
+ if (*buf == '.' &&
+ isascii(*(buf+1)) && isalnum(*(buf+1)) &&
+ isascii(*(buf+2)) && isalnum(*(buf+2))){
+ ckfputs("troff or preprocessor input text", stdout);
+ return 1;
+ }
+ if ((*buf == 'c' || *buf == 'C') &&
+ isascii(*(buf + 1)) && isspace(*(buf + 1))) {
+ ckfputs("fortran program text", stdout);
+ return 1;
+ }
+
+ /* look for tokens from names.h - this is expensive! */
+ s = buf;
+ while ((token = strtok(s, " \t\n\r\f")) != NULL) {
+ s = NULL; /* make strtok() keep on tokin' */
+ for (p = names; p < names + NNAMES; p++) {
+ if (STREQ(p->name, token)) {
+ ckfputs(types[p->type], stdout);
+ return 1;
+ }
+ }
+ }
+
+ if (is_tar(buf)) {
+ ckfputs("tar archive", stdout);
+ return 1;
+ }
+
+ for (i = 0; i < nbytes; i++) {
+ if (!isascii(*(buf+i)))
+ return 0; /* not all ascii */
+ if (*(buf+i) == '\033') /* ascii ESCAPE */
+ has_escapes ++;
+ }
+
+ /* all else fails, but it is ascii... */
+ if (has_escapes){
+ ckfputs("ascii text (with escape sequences)", stdout);
+ }
+ else {
+ ckfputs("ascii text", stdout);
+ }
+ return 1;
+}
+
+
--- /dev/null
+/*
+ * file - find type of a file or files
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "file.h"
+
+#define USAGE "usage: %s [-f file] [-m magicfile] file...\n"
+
+extern char *ckfmsg;
+int check = 0, /* check format of magic file */
+ debug = 0, /* huh? */
+ nbytes = 0, /* number of bytes read from a datafile */
+ nmagic = 0; /* number of valid magic[]s */
+FILE *efopen();
+char *magicfile = "/etc/magic"; /* where magic be found */
+char *progname;
+struct stat statbuf;
+struct utimbuf { /* for utime(2), belongs in a .h file */
+ time_t actime; /* access time */
+ time_t modtime; /* modification time */
+};
+
+/*
+ * main - parse arguments and handle options
+ */
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ extern int optind;
+ extern char *optarg;
+
+ progname = argv[0];
+
+ while ((c = getopt(argc, argv, "cdf:m:")) != EOF)
+ switch (c) {
+ case 'c':
+ ++check;
+ break;
+ case 'd':
+ ++debug;
+ break;
+ case 'f':
+ unwrap(optarg);
+ break;
+ case 'm':
+ magicfile = optarg;
+ break;
+ case '?':
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg) {
+ (void) fprintf(stderr, USAGE, progname);
+ exit(2);
+ }
+
+ apprentice(magicfile);
+
+ if (optind == argc)
+ (void)fprintf(stderr, USAGE, progname);
+ else
+ for (; optind < argc; optind++)
+ process(argv[optind]);
+
+ exit(0);
+}
+
+/*
+ * unwrap -- read a file of filenames, do each one.
+ */
+unwrap(fn)
+char *fn;
+{
+#define FILENAMELEN 128
+ char buf[FILENAMELEN];
+ FILE *f;
+
+ if ((f = fopen(fn, "r")) == NULL)
+ (void) fprintf(stderr, "%s: file %s unreadable\n",
+ progname, fn);
+ else {
+ while (fgets(buf, FILENAMELEN, f) != NULL) {
+ buf[strlen(buf)-1] = '\0';
+ process(buf);
+ }
+ (void) fclose(f);
+ }
+}
+
+/*
+ * process - process input file
+ */
+process(inname)
+char *inname;
+{
+ int fd;
+ char buf[HOWMANY];
+ struct utimbuf utbuf;
+
+ (void) printf("%s:\t", inname);
+
+ /*
+ * first try judging the file based on its filesystem status
+ * Side effect: fsmagic updates global data `statbuf'.
+ */
+ if (fsmagic(inname) != 0) {
+ /*NULLBODY*/;
+ } else if ((fd = open(inname, 0)) < 0) {
+ warning("can't open for reading");
+ } else {
+ /*
+ * try looking at the first HOWMANY bytes
+ */
+ if ((nbytes = read(fd, buf, HOWMANY)) == -1)
+ warning("read failed");
+ /*
+ * try tests in /etc/magic (or surrogate magic file
+ */
+ if (softmagic(buf) == 1)
+ /*NULLBODY*/;
+ else if (ascmagic(buf) == 1)
+ /*
+ * try known keywords, check for ascii-ness too.
+ */
+ /*NULLBODY*/;
+ else {
+ /*
+ * abandon hope, all ye who remain here
+ */
+ ckfputs("data", stdout);
+ }
+ /*
+ * Restore access, modification times if we read the file.
+ */
+ utbuf.actime = statbuf.st_atime;
+ utbuf.modtime = statbuf.st_mtime;
+ (void) utime(inname, &utbuf); /* don't care if we lack perms */
+ (void) close(fd);
+ }
+
+ (void) putchar('\n');
+}
+
+
--- /dev/null
+/*
+ * definitions for file(1) program:
+ */
+
+#define HOWMANY 1024 /* how much of the file to look at */
+#define MAXMAGIS 250 /* max entries in /etc/magic */
+#define MAXDESC 50 /* max leng of text description */
+#define MAXstring 32 /* max leng of "string" types */
+#define ckfputs(str,fil) {if (fputs(str,fil)==EOF) error(ckfmsg,"");}
+
+struct magic {
+ short contflag; /* 1 if '>0' appears */
+ long offset; /* offset to magic number */
+ char reln; /* relation (0=.eq, '>'=gt, etc) */
+ short type; /* int, short, long or string. */
+#define BYTE 1
+#define SHORT 2
+#define LONG 4
+#define STRING 5
+ union VALUETYPE {
+ char b;
+ short h;
+ long l;
+ char s[MAXstring];
+ } value; /* either number or string */
+ char desc[MAXDESC]; /* description */
+};
+
+extern void error(), exit();
--- /dev/null
+/*
+ * magic based on mode of file - directory, special files, etc.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#ifndef major /* if `major' not defined in types.h, */
+#include <sys/sysmacros.h> /* try this one. */
+#endif
+#ifndef major
+ /* If cc tries to compile this, read and act on it. */
+ /* On most systems cpp will discard it automatically */
+ Congratulations, you have found a portability bug.
+ Please grep /usr/include/sys and edit the above #include
+ to point at the file that defines the `major' macro.
+#endif /*major*/
+#include <sys/stat.h>
+#include "file.h"
+
+#define USAGE "usage: %s [-f file] [-m magicfile] file...\n"
+
+extern char *progname;
+extern char *ckfmsg, *magicfile;
+extern int debug;
+extern FILE *efopen();
+
+fsmagic(fn)
+char *fn;
+{
+ extern struct stat statbuf;
+
+ /*
+ * Fstat is cheaper but fails for files you don't have read perms on.
+ * On 4.2BSD and similar systems, use lstat() so identify symlinks.
+ */
+#ifdef S_IFLNK
+ if (lstat(fn, &statbuf) <0)
+#else
+ if (stat(fn, &statbuf) <0)
+#endif
+ {
+ warning("can't stat");
+ return -1;
+ }
+ switch (statbuf.st_mode & S_IFMT) {
+ case S_IFDIR:
+ ckfputs("directory", stdout);
+ return 1;
+ case S_IFCHR:
+ (void) printf("character special (%d/%d)",
+ major(statbuf.st_rdev), minor(statbuf.st_rdev));
+ return 1;
+ case S_IFBLK:
+ (void) printf("block special (%d/%d)",
+ major(statbuf.st_rdev), minor(statbuf.st_rdev));
+ return 1;
+ /* TODO add code to handle V7 MUX and Blit MUX files */
+#ifdef S_IFIFO
+ case S_IFIFO:
+ ckfputs("fifo (named pipe)", stdout);
+ return 1;
+#endif
+#ifdef S_IFLNK
+ case S_IFLNK:
+ ckfputs("symbolic link", stdout);
+ return 1;
+#endif
+#ifdef S_IFSOCK
+ case S_IFSOCK:
+ ckfputs("socket", stdout);
+ return 1;
+#endif
+ case S_IFREG:
+ break;
+ default:
+ warning("invalid st_mode %d in statbuf!", statbuf.st_mode);
+ }
+
+ /*
+ * regular file, check next possibility
+ */
+ if (statbuf.st_size == 0) {
+ ckfputs("empty", stdout);
+ return 1;
+ }
+ return 0;
+}
+
--- /dev/null
+/*
+ * istar() - see if it is a tar file. Found in code written by
+ * Fred Blonder (301) 454-7690
+ * harpo!seismo!umcp-cs!fred
+ * Fred@Maryland.ARPA
+ * Net.sources, <9@gyre.uucp>, October 1983
+ * I changed the calling sequence, added a cast or two, and ran through cb -sj.
+ */
+
+is_tar(buf) /* Is it a tar file? */
+char *buf;
+{
+ /* This stuff is copied from tar.c. */
+#define TBLOCK 512
+#define NAMSIZ 100
+ register struct header {
+ char name[NAMSIZ];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char size[12];
+ char mtime[12];
+ char chksum[8];
+ char linkflag;
+ char linkname[NAMSIZ];
+ };
+ register comp_chksum;
+ register char *cp;
+ int header_chksum;
+
+ /* Compute checksum */
+ comp_chksum = 0;
+ for (cp = buf; cp < &buf[TBLOCK]; cp++)
+ comp_chksum += (cp < ((struct header *)buf)->chksum
+ || cp >= &((struct header *)buf)->chksum[8]) ?
+ *cp : ' ';
+
+ /* Convert checksum field to integer */
+ (void) sscanf(((struct header *)buf)->chksum, "%o", &header_chksum);
+
+ return (comp_chksum == header_chksum); /* Is checksum correct? */
+}
+
+
--- /dev/null
+/*
+ * Names.h - names and types used by ascmagic in file(1).
+ * These tokens are here because they can appear anywhere in
+ * the first HOWMANY bytes, while tokens in /etc/magic must
+ * appear at fixed offsets into the file. Don't make HOWMANY
+ * too high unless you have a very fast CPU.
+ */
+
+/* these types are used to index the table 'types': keep em in sync! */
+#define L_C 0 /* first and foremost on UNIX */
+#define L_FORT 1 /* the oldest one */
+#define L_MAKE 2 /* Makefiles */
+#define L_PLI 3 /* PL/1 */
+#define L_MACH 4 /* some kinda assembler */
+#define L_ENG 5 /* English */
+#define L_PAS 6 /* Pascal */
+
+char *types[] = {
+ "c program text",
+ "fortran program text",
+ "makefile commands text" ,
+ "pl/1 (blech) program text",
+ "assembler (blech) program text",
+ "english text",
+ "pascal program text",
+ "can't happen error on names.h/types",
+ 0};
+
+struct names {
+ char *name;
+ short type;
+} names[] = {
+ /* These must be sorted by eye for optimal hit rate */
+ /* Add to this list only after substantial meditation */
+ {"/*", L_C},
+ {"#include", L_C},
+ {"char", L_C},
+ {"The", L_ENG},
+ {"double", L_C},
+ {"extern", L_C},
+ {"float", L_C},
+ {"real", L_C},
+ {"struct", L_C},
+ {"union", L_C},
+ {"CFLAGS", L_MAKE},
+ {"LDFLAGS", L_MAKE},
+ {"all:", L_MAKE},
+ {".PRECIOUS", L_MAKE},
+ {"subroutine", L_FORT},
+ {"function", L_FORT},
+ {"block", L_FORT},
+ {"common", L_FORT},
+ {"dimension", L_FORT},
+ {"integer", L_FORT},
+ {"data", L_FORT},
+ {".ascii", L_MACH},
+ {".asciiz", L_MACH},
+ {".byte", L_MACH},
+ {".even", L_MACH},
+ {".globl", L_MACH},
+ {"clr", L_MACH},
+ {"(input,", L_PAS},
+ {"dcl", L_PLI},
+ 0};
+#define NNAMES ((sizeof(names)/sizeof(struct names)) - 1)
--- /dev/null
+/*
+ * Debugging printout routines
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include "file.h"
+
+#define MAXSTR 500
+
+extern char *progname;
+extern char *magicfile;
+extern int debug, nmagic; /* number of valid magic[]s */
+
+mdump(m)
+struct magic *m;
+{
+ (void) printf("%d\t%d\t%d\t%c\t",
+ m->contflag,
+ m->offset,
+ m->type,
+ m->reln,
+ 0);
+ if (m->type == STRING)
+ (void) printf("%s",m->value.s);
+ else
+ (void) printf("%d",m->value.l);
+ (void) printf("\t%s", m->desc);
+ (void) putchar('\n');
+}
+
+/*
+ * error - print best error message possible and exit
+ */
+void
+error(s1, s2)
+char *s1, *s2;
+{
+ warning(s1, s2);
+ exit(1);
+}
+
+/*VARARGS*/
+warning(f, a)
+char *f, *a;
+{
+ extern int errno, sys_nerr;
+ extern char *sys_errlist[];
+ int myerrno;
+
+ myerrno = errno;
+ (void) printf(f, a);
+ if (myerrno > 0 && myerrno < sys_nerr)
+ (void) printf(" (%s)", sys_errlist[myerrno]);
+}
--- /dev/null
+/*
+ * variable magic from /etc/magic
+ */
+
+#include <stdio.h>
+#include "file.h"
+
+#define USAGE "usage: %s [-f file] [-m magicfile] file...\n"
+
+extern char *progname;
+extern char *magicfile; /* name of current /etc/magic or clone */
+extern int debug, nmagic;
+extern FILE *efopen();
+extern struct magic magic[];
+static int magindex;
+
+/*
+ * softmagic - lookup one file in /etc/magic database.
+ * Passed the name and FILE * of one file to be typed.
+ */
+softmagic(buf)
+char *buf;
+{
+ magindex = 0;
+ if (match(buf))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * go through the whole list, stopping if you find a match.
+ * Be sure to process every continuation of this match.
+ */
+match(s)
+char *s;
+{
+ while (magindex < nmagic) {
+ /* if main entry matches, print it... */
+ if (mcheck(s, &magic[magindex])) {
+ mprint(&magic[magindex],s);
+ /* and any continuations that match */
+ while (magic[magindex+1].contflag &&
+ magindex < nmagic) {
+ ++magindex;
+ if (mcheck(s, &magic[magindex])){
+ (void) putchar(' ');
+ mprint(&magic[magindex],s);
+ }
+ }
+ return 1; /* all through */
+ } else {
+ /* main entry didn't match, flush its continuations */
+ while (magic[magindex+1].contflag &&
+ magindex < nmagic) {
+ ++magindex;
+ }
+ }
+ ++magindex; /* on to the next */
+ }
+ return 0; /* no match at all */
+}
+
+
+mprint(m,s)
+struct magic *m;
+char *s;
+{
+ register union VALUETYPE *p = (union VALUETYPE *)(s+m->offset);
+
+ switch (m->type) {
+ case BYTE:
+ (void) printf(m->desc, p->b);
+ break;
+ case SHORT:
+ (void) printf(m->desc, p->h);
+ break;
+ case LONG:
+ (void) printf(m->desc, p->l);
+ break;
+ case STRING:
+ (void) printf(m->desc, p->s);
+ break;
+ default:
+ warning("invalid m->type (%d) in mprint()", m->type);
+ }
+}
+
+mcheck(s, m)
+char *s;
+struct magic *m;
+{
+ register char reln = m->reln;
+ register union VALUETYPE *p = (union VALUETYPE *)(s+m->offset);
+ register long l = m->value.l;
+
+ if (debug) {
+ (void) printf("mcheck: %10.10s ", s);
+ mdump(m);
+ }
+ switch (m->type) {
+ case BYTE:
+ if (reln == '>')
+ return p->b > l;
+ if (reln == '&')
+ return l & p->b;
+ if (reln == '=')
+ return l == p->b;
+ warning("mcheck: can't happen: invalid BYTE reln %d", reln);
+ return 0;
+ case SHORT:
+ if (reln == '=')
+ return l == p->h;
+ if (reln == '>')
+ return p->h > l;
+ if (reln == '&')
+ return l & p->h;
+ warning("mcheck: can't happen: invalid SHORT reln %d", reln);
+ return 0;
+ case LONG:
+ if (reln == '=')
+ return l == p->l;
+ if (reln == '>')
+ return p->l > l;
+ if (reln == '&')
+ return l & p->l;
+ warning("mcheck: can't happen: invalid LONG reln %d", reln);
+ return 0;
+ case STRING:
+ if (reln == '=')
+ return strncmp(m->value.s, p->s,
+ strlen(m->value.s)) == 0;
+ if (reln == '>')
+ if (strcmp(m->value.s, "0") == 0) /* special case! */
+ return *s > '\0';
+ else
+ return strncmp(m->value.s, p->s,
+ strlen(m->value.s)) > 0;
+ warning("mcheck: can't happen: invalid STRING reln %c(0%o)",
+ reln, reln);
+ return 0;
+ default:
+ warning("invalid type %d in mcheck()", m->type);
+ return 0;
+ }
+}
+
+