summaryrefslogtreecommitdiff
path: root/contrib/file/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/file/file.c')
-rw-r--r--contrib/file/file.c538
1 files changed, 226 insertions, 312 deletions
diff --git a/contrib/file/file.c b/contrib/file/file.c
index a482f63711def..6af9ad5299500 100644
--- a/contrib/file/file.c
+++ b/contrib/file/file.c
@@ -1,44 +1,36 @@
/*
- * Copyright (c) Ian F. Darwin 1986-1995.
- * Software written by Ian F. Darwin and others;
- * maintained 1995-present by Christos Zoulas and others.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice immediately at the beginning of the file, without modification,
- * this list of conditions, and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-/*
* file - find type of a file or files - main program.
+ *
+ * Copyright (c) Ian F. Darwin, 1987.
+ * Written by Ian F. Darwin.
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or of the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Since few users ever read sources,
+ * credits must appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software. Since few users
+ * ever read sources, credits must appear in the documentation.
+ *
+ * 4. This notice may not be removed or altered.
*/
-
-#include "file.h"
-#include "magic.h"
-
#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/param.h> /* for MAXPATHLEN */
#include <sys/stat.h>
+#include <fcntl.h> /* for open() */
#ifdef RESTORE_TIME
# if (__COHERENT__ >= 0x420)
# include <sys/utime.h>
@@ -56,116 +48,75 @@
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
-#ifdef HAVE_WCHAR_H
-#include <wchar.h>
-#endif
-
-#ifdef HAVE_GETOPT_H
-#include <getopt.h> /* for long options (is this portable?)*/
-#else
-#undef HAVE_GETOPT_LONG
-#endif
#include <netinet/in.h> /* for byte swapping */
+#include "file.h"
#include "patchlevel.h"
#ifndef lint
-FILE_RCSID("@(#)$Id: file.c,v 1.100 2005/10/17 18:41:44 christos Exp $")
+FILE_RCSID("@(#)$Id: file.c,v 1.54 2000/08/05 18:30:26 christos Exp $")
#endif /* lint */
#ifdef S_IFLNK
-#define SYMLINKFLAG "Lh"
+# define USAGE "Usage: %s [-bciknvzL] [-f namefile] [-m magicfiles] file...\n"
#else
-#define SYMLINKFLAG ""
+# define USAGE "Usage: %s [-bciknvz] [-f namefile] [-m magicfiles] file...\n"
#endif
-# define USAGE "Usage: %s [-bcik" SYMLINKFLAG "nNsvz] [-f namefile] [-F separator] [-m magicfiles] file...\n %s -C -m magicfiles\n"
+#ifndef MAGIC
+# define MAGIC "/etc/magic"
+#endif
#ifndef MAXPATHLEN
#define MAXPATHLEN 512
#endif
-private int /* Global command-line options */
+int /* Global command-line options */
+ debug = 0, /* debugging */
+ lflag = 0, /* follow Symlinks (BSD only) */
bflag = 0, /* brief output format */
- nopad = 0, /* Don't pad output */
- nobuffer = 0; /* Do not buffer stdout */
+ zflag = 0, /* follow (uncompress) compressed files */
+ sflag = 0, /* read block special files */
+ iflag = 0,
+ nobuffer = 0, /* Do not buffer stdout */
+ kflag = 0; /* Keep going after the first match */
-private const char *magicfile = 0; /* where the magic is */
-private const char *default_magicfile = MAGIC;
-private const char *separator = ":"; /* Default field separator */
+int /* Misc globals */
+ nmagic = 0; /* number of valid magic[]s */
-private char *progname; /* used throughout */
+struct magic *magic; /* array of magic entries */
-private struct magic_set *magic;
+const char *magicfile; /* where magic be found */
+const char *default_magicfile = MAGIC;
-private void unwrap(char *);
-private void usage(void);
-#ifdef HAVE_GETOPT_LONG
-private void help(void);
-#endif
+char *progname; /* used throughout */
+int lineno; /* line number in the magic file */
+
+
+static void unwrap __P((char *fn));
#if 0
-private int byteconv4(int, int, int);
-private short byteconv2(int, int, int);
+static int byteconv4 __P((int, int, int));
+static short byteconv2 __P((int, int, int));
#endif
-int main(int, char *[]);
-private void process(const char *, int);
-private void load(const char *, int);
-
+int main __P((int, char *[]));
/*
* main - parse arguments and handle options
*/
int
-main(int argc, char *argv[])
+main(argc, argv)
+ int argc;
+ char *argv[];
{
int c;
- int action = 0, didsomefiles = 0, errflg = 0;
- int flags = 0;
- char *home, *usermagic;
- struct stat sb;
-#define OPTSTRING "bcCdf:F:hikLm:nNprsvz"
-#ifdef HAVE_GETOPT_LONG
- int longindex;
- private struct option long_options[] =
- {
- {"version", 0, 0, 'v'},
- {"help", 0, 0, 0},
- {"brief", 0, 0, 'b'},
- {"checking-printout", 0, 0, 'c'},
- {"debug", 0, 0, 'd'},
- {"files-from", 1, 0, 'f'},
- {"separator", 1, 0, 'F'},
- {"mime", 0, 0, 'i'},
- {"keep-going", 0, 0, 'k'},
-#ifdef S_IFLNK
- {"dereference", 0, 0, 'L'},
- {"no-dereference", 0, 0, 'h'},
-#endif
- {"magic-file", 1, 0, 'm'},
-#if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
- {"preserve-date", 0, 0, 'p'},
-#endif
- {"uncompress", 0, 0, 'z'},
- {"raw", 0, 0, 'r'},
- {"no-buffer", 0, 0, 'n'},
- {"no-pad", 0, 0, 'N'},
- {"special-files", 0, 0, 's'},
- {"compile", 0, 0, 'C'},
- {0, 0, 0, 0},
- };
-#endif
+ int check = 0, didsomefiles = 0, errflg = 0, ret = 0, app = 0;
+ char *mime;
#ifdef LC_CTYPE
- /* makes islower etc work for other langs */
- (void)setlocale(LC_CTYPE, "");
-#endif
-
-#ifdef __EMX__
- /* sh-like wildcard expansion! Shouldn't hurt at least ... */
- _wildcard(&argc, &argv);
+ setlocale(LC_CTYPE, ""); /* makes islower etc work for other langs */
#endif
if ((progname = strrchr(argv[0], '/')) != NULL)
@@ -173,64 +124,40 @@ main(int argc, char *argv[])
else
progname = argv[0];
- magicfile = default_magicfile;
- if ((usermagic = getenv("MAGIC")) != NULL)
- magicfile = usermagic;
- else
- if ((home = getenv("HOME")) != NULL) {
- if ((usermagic = malloc(strlen(home) + 8)) != NULL) {
- (void)strcpy(usermagic, home);
- (void)strcat(usermagic, "/.magic");
- if (stat(usermagic, &sb)<0)
- free(usermagic);
- else
- magicfile = usermagic;
- }
- }
+ if (!(magicfile = getenv("MAGIC")))
+ magicfile = default_magicfile;
-#ifdef S_IFLNK
- flags |= getenv("POSIXLY_CORRECT") ? MAGIC_SYMLINK : 0;
-#endif
-#ifndef HAVE_GETOPT_LONG
- while ((c = getopt(argc, argv, OPTSTRING)) != -1)
-#else
- while ((c = getopt_long(argc, argv, OPTSTRING, long_options,
- &longindex)) != -1)
-#endif
+ while ((c = getopt(argc, argv, "bcdf:ikm:nsvzL")) != EOF)
switch (c) {
-#ifdef HAVE_GETOPT_LONG
- case 0 :
- if (longindex == 1)
- help();
- break;
-#endif
case 'b':
++bflag;
break;
case 'c':
- action = FILE_CHECK;
- break;
- case 'C':
- action = FILE_COMPILE;
+ ++check;
break;
case 'd':
- flags |= MAGIC_DEBUG|MAGIC_CHECK;
+ ++debug;
break;
case 'f':
- if(action)
- usage();
- load(magicfile, flags);
+ if (!app) {
+ ret = apprentice(magicfile, check);
+ if (check)
+ exit(ret);
+ app = 1;
+ }
unwrap(optarg);
++didsomefiles;
break;
- case 'F':
- separator = optarg;
- break;
case 'i':
- flags |= MAGIC_MIME;
+ iflag++;
+ if ((mime = malloc(strlen(magicfile) + 5)) != NULL) {
+ (void)strcpy(mime, magicfile);
+ (void)strcat(mime, ".mime");
+ magicfile = mime;
+ }
break;
case 'k':
- flags |= MAGIC_CONTINUE;
+ kflag = 1;
break;
case 'm':
magicfile = optarg;
@@ -238,35 +165,21 @@ main(int argc, char *argv[])
case 'n':
++nobuffer;
break;
- case 'N':
- ++nopad;
- break;
-#if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
- case 'p':
- flags |= MAGIC_PRESERVE_ATIME;
- break;
-#endif
- case 'r':
- flags |= MAGIC_RAW;
- break;
case 's':
- flags |= MAGIC_DEVICES;
+ sflag++;
break;
case 'v':
- (void)fprintf(stdout, "%s-%d.%.2d\n", progname,
+ (void) fprintf(stdout, "%s-%d.%d\n", progname,
FILE_VERSION_MAJOR, patchlevel);
- (void)fprintf(stdout, "magic file from %s\n",
+ (void) fprintf(stdout, "magic file from %s\n",
magicfile);
return 1;
case 'z':
- flags |= MAGIC_COMPRESS;
+ zflag++;
break;
#ifdef S_IFLNK
case 'L':
- flags |= MAGIC_SYMLINK;
- break;
- case 'h':
- flags &= ~MAGIC_SYMLINK;
+ ++lflag;
break;
#endif
case '?':
@@ -276,40 +189,27 @@ main(int argc, char *argv[])
}
if (errflg) {
- usage();
+ (void) fprintf(stderr, USAGE, progname);
+ exit(2);
}
- switch(action) {
- case FILE_CHECK:
- case FILE_COMPILE:
- magic = magic_open(flags|MAGIC_CHECK);
- if (magic == NULL) {
- (void)fprintf(stderr, "%s: %s\n", progname,
- strerror(errno));
- return 1;
- }
- c = action == FILE_CHECK ? magic_check(magic, magicfile) :
- magic_compile(magic, magicfile);
- if (c == -1) {
- (void)fprintf(stderr, "%s: %s\n", progname,
- magic_error(magic));
- return -1;
- }
- return 0;
- default:
- load(magicfile, flags);
- break;
+ if (!app) {
+ ret = apprentice(magicfile, check);
+ if (check)
+ exit(ret);
+ app = 1;
}
if (optind == argc) {
if (!didsomefiles) {
- usage();
+ (void)fprintf(stderr, USAGE, progname);
+ exit(2);
}
}
else {
int i, wid, nw;
for (wid = 0, i = optind; i < argc; i++) {
- nw = file_mbswidth(argv[i]);
+ nw = strlen(argv[i]);
if (nw > wid)
wid = nw;
}
@@ -317,55 +217,32 @@ main(int argc, char *argv[])
process(argv[optind], wid);
}
- magic_close(magic);
return 0;
}
-private void
-/*ARGSUSED*/
-load(const char *m, int flags)
-{
- if (magic)
- return;
- magic = magic_open(flags);
- if (magic == NULL) {
- (void)fprintf(stderr, "%s: %s\n", progname, strerror(errno));
- exit(1);
- }
- if (magic_load(magic, magicfile) == -1) {
- (void)fprintf(stderr, "%s: %s\n",
- progname, magic_error(magic));
- exit(1);
- }
-}
-
/*
* unwrap -- read a file of filenames, do each one.
*/
-private void
-unwrap(char *fn)
+static void
+unwrap(fn)
+ char *fn;
{
char buf[MAXPATHLEN];
FILE *f;
int wid = 0, cwid;
- size_t len;
if (strcmp("-", fn) == 0) {
f = stdin;
wid = 1;
} else {
if ((f = fopen(fn, "r")) == NULL) {
- (void)fprintf(stderr, "%s: Cannot open `%s' (%s).\n",
- progname, fn, strerror(errno));
- exit(1);
+ error("Cannot open `%s' (%s).\n", fn, strerror(errno));
+ /*NOTREACHED*/
}
while (fgets(buf, MAXPATHLEN, f) != NULL) {
- len = strlen(buf);
- if (len > 0 && buf[len - 1] == '\n')
- buf[len - 1] = '\0';
- cwid = file_mbswidth(buf);
+ cwid = strlen(buf) - 1;
if (cwid > wid)
wid = cwid;
}
@@ -374,32 +251,13 @@ unwrap(char *fn)
}
while (fgets(buf, MAXPATHLEN, f) != NULL) {
- len = strlen(buf);
- if (len > 0 && buf[len - 1] == '\n')
- buf[len - 1] = '\0';
+ buf[strlen(buf)-1] = '\0';
process(buf, wid);
if(nobuffer)
- (void)fflush(stdout);
+ (void) fflush(stdout);
}
- (void)fclose(f);
-}
-
-private void
-process(const char *inname, int wid)
-{
- const char *type;
- int std_in = strcmp(inname, "-") == 0;
-
- if (wid > 0 && !bflag)
- (void)printf("%s%s%*s ", std_in ? "/dev/stdin" : inname,
- separator, (int) (nopad ? 0 : (wid - file_mbswidth(inname))), "");
-
- type = magic_file(magic, std_in ? NULL : inname);
- if (type == NULL)
- (void)printf("ERROR: %s\n", magic_error(magic));
- else
- (void)printf("%s\n", type);
+ (void) fclose(f);
}
@@ -411,8 +269,11 @@ process(const char *inname, int wid)
* same whether to perform byte swapping
* big_endian whether we are a big endian host
*/
-private int
-byteconv4(int from, int same, int big_endian)
+static int
+byteconv4(from, same, big_endian)
+ int from;
+ int same;
+ int big_endian;
{
if (same)
return from;
@@ -438,8 +299,11 @@ byteconv4(int from, int same, int big_endian)
* byteconv2
* Same as byteconv4, but for shorts
*/
-private short
-byteconv2(int from, int same, int big_endian)
+static short
+byteconv2(from, same, big_endian)
+ int from;
+ int same;
+ int big_endian;
{
if (same)
return from;
@@ -460,79 +324,129 @@ byteconv2(int from, int same, int big_endian)
}
#endif
-size_t
-file_mbswidth(const char *s)
+/*
+ * process - process input file
+ */
+void
+process(inname, wid)
+ const char *inname;
+ int wid;
{
-#if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH)
- size_t bytesconsumed, old_n, n, width = 0;
- mbstate_t state;
- wchar_t nextchar;
- (void)memset(&state, 0, sizeof(mbstate_t));
- old_n = n = strlen(s);
-
- while (n > 0) {
- bytesconsumed = mbrtowc(&nextchar, s, n, &state);
- if (bytesconsumed == (size_t)(-1) ||
- bytesconsumed == (size_t)(-2)) {
- /* Something went wrong, return something reasonable */
- return old_n;
+ int fd = 0;
+ static const char stdname[] = "standard input";
+ unsigned char buf[HOWMANY+1]; /* one extra for terminating '\0' */
+ struct stat sb;
+ int nbytes = 0; /* number of bytes read from a datafile */
+ char match = '\0';
+
+ if (strcmp("-", inname) == 0) {
+ if (fstat(0, &sb)<0) {
+ error("cannot fstat `%s' (%s).\n", stdname,
+ strerror(errno));
+ /*NOTREACHED*/
}
- if (s[0] == '\n') {
- /*
- * do what strlen() would do, so that caller
- * is always right
- */
- width++;
- } else
- width += wcwidth(nextchar);
-
- s += bytesconsumed, n -= bytesconsumed;
+ inname = stdname;
+ }
+
+ if (wid > 0 && !bflag)
+ (void) printf("%s:%*s ", inname,
+ (int) (wid - strlen(inname)), "");
+
+ if (inname != stdname) {
+ /*
+ * first try judging the file based on its filesystem status
+ */
+ if (fsmagic(inname, &sb) != 0) {
+ putchar('\n');
+ return;
+ }
+
+ if ((fd = open(inname, O_RDONLY)) < 0) {
+ /* We can't open it, but we were able to stat it. */
+ if (sb.st_mode & 0002) ckfputs("writeable, ", stdout);
+ if (sb.st_mode & 0111) ckfputs("executable, ", stdout);
+ ckfprintf(stdout, "can't read `%s' (%s).\n",
+ inname, strerror(errno));
+ return;
+ }
+ }
+
+
+ /*
+ * try looking at the first HOWMANY bytes
+ */
+ if ((nbytes = read(fd, (char *)buf, HOWMANY)) == -1) {
+ error("read failed (%s).\n", strerror(errno));
+ /*NOTREACHED*/
+ }
+
+ if (nbytes == 0)
+ ckfputs(iflag ? "application/x-empty" : "empty", stdout);
+ else {
+ buf[nbytes++] = '\0'; /* null-terminate it */
+ match = tryit(buf, nbytes, zflag);
+ }
+
+#ifdef BUILTIN_ELF
+ if (match == 's' && nbytes > 5) {
+ /*
+ * We matched something in the file, so this *might*
+ * be an ELF file, and the file is at least 5 bytes long,
+ * so if it's an ELF file it has at least one byte
+ * past the ELF magic number - try extracting information
+ * from the ELF headers that can't easily be extracted
+ * with rules in the magic file.
+ */
+ tryelf(fd, buf, nbytes);
}
- return width;
-#else
- return strlen(s);
#endif
-}
-private void
-usage(void)
-{
- (void)fprintf(stderr, USAGE, progname, progname);
-#ifdef HAVE_GETOPT_LONG
- (void)fputs("Try `file --help' for more information.\n", stderr);
+ if (inname != stdname) {
+#ifdef RESTORE_TIME
+ /*
+ * Try to restore access, modification times if read it.
+ * This is really *bad* because it will modify the status
+ * time of the file... And of course this will affect
+ * backup programs
+ */
+# ifdef USE_UTIMES
+ struct timeval utsbuf[2];
+ utsbuf[0].tv_sec = sb.st_atime;
+ utsbuf[1].tv_sec = sb.st_mtime;
+
+ (void) utimes(inname, utsbuf); /* don't care if loses */
+# else
+ struct utimbuf utbuf;
+
+ utbuf.actime = sb.st_atime;
+ utbuf.modtime = sb.st_mtime;
+ (void) utime(inname, &utbuf); /* don't care if loses */
+# endif
#endif
- exit(1);
+ (void) close(fd);
+ }
+ (void) putchar('\n');
}
-#ifdef HAVE_GETOPT_LONG
-private void
-help(void)
+
+int
+tryit(buf, nb, zflag)
+ unsigned char *buf;
+ int nb, zflag;
{
- (void)puts(
-"Usage: file [OPTION]... [FILE]...\n"
-"Determine file type of FILEs.\n"
-"\n"
-" -m, --magic-file LIST use LIST as a colon-separated list of magic\n"
-" number files\n"
-" -z, --uncompress try to look inside compressed files\n"
-" -b, --brief do not prepend filenames to output lines\n"
-" -c, --checking-printout print the parsed form of the magic file, use in\n"
-" conjunction with -m to debug a new magic file\n"
-" before installing it\n"
-" -f, --files-from FILE read the filenames to be examined from FILE\n"
-" -F, --separator string use string as separator instead of `:'\n"
-" -i, --mime output mime type strings\n"
-" -k, --keep-going don't stop at the first match\n"
-" -L, --dereference causes symlinks to be followed\n"
-" -n, --no-buffer do not buffer output\n"
-" -N, --no-pad do not pad output\n"
-" -p, --preserve-date preserve access times on files\n"
-" -r, --raw don't translate unprintable chars to \\ooo\n"
-" -s, --special-files treat special (block/char devices) files as\n"
-" ordinary ones\n"
-" --help display this help and exit\n"
-" --version output version information and exit\n"
-);
- exit(0);
+ /* try compression stuff */
+ if (zflag && zmagic(buf, nb))
+ return 'z';
+
+ /* try tests in /etc/magic (or surrogate magic file) */
+ if (softmagic(buf, nb))
+ return 's';
+
+ /* try known keywords, check whether it is ASCII */
+ if (ascmagic(buf, nb))
+ return 'a';
+
+ /* abandon hope, all ye who remain here */
+ ckfputs("data", stdout);
+ return '\0';
}
-#endif