diff options
Diffstat (limited to 'contrib/file/file.c')
-rw-r--r-- | contrib/file/file.c | 538 |
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 |