2 * file - find type of a file or files - main program.
4 * Copyright (c) Ian F. Darwin, 1987.
5 * Written by Ian F. Darwin.
7 * This software is not subject to any license of the American Telephone
8 * and Telegraph Company or of the Regents of the University of California.
10 * Permission is granted to anyone to use this software for any purpose on
11 * any computer system, and to alter it and redistribute it freely, subject
12 * to the following restrictions:
14 * 1. The author is not responsible for the consequences of use of this
15 * software, no matter how awful, even if they arise from flaws in it.
17 * 2. The origin of this software must not be misrepresented, either by
18 * explicit claim or by omission. Since few users ever read sources,
19 * credits must appear in the documentation.
21 * 3. Altered versions must be plainly marked as such, and must not be
22 * misrepresented as being the original software. Since few users
23 * ever read sources, credits must appear in the documentation.
25 * 4. This notice may not be removed or altered.
31 #include <sys/types.h>
32 #include <sys/param.h> /* for MAXPATHLEN */
34 #include <fcntl.h> /* for open() */
36 # if (__COHERENT__ >= 0x420)
37 # include <sys/utime.h>
40 # include <sys/time.h>
47 #include <unistd.h> /* for read() */
53 #include <netinet/in.h> /* for byte swapping */
56 #include "patchlevel.h"
59 FILE_RCSID("@(#)$Id: file.c,v 1.59 2001/07/23 00:02:32 christos Exp $")
64 # define USAGE "Usage: %s [-bciknsvzL] [-f namefile] [-m magicfiles] file...\n"
66 # define USAGE "Usage: %s [-bciknsvz] [-f namefile] [-m magicfiles] file...\n"
70 # define MAGIC "/etc/magic"
74 #define MAXPATHLEN 512
77 int /* Global command-line options */
78 debug = 0, /* debugging */
79 lflag = 0, /* follow Symlinks (BSD only) */
80 bflag = 0, /* brief output format */
81 zflag = 0, /* follow (uncompress) compressed files */
82 sflag = 0, /* read block special files */
84 nobuffer = 0, /* Do not buffer stdout */
85 kflag = 0; /* Keep going after the first match */
87 int /* Misc globals */
88 nmagic = 0; /* number of valid magic[]s */
90 struct magic *magic; /* array of magic entries */
92 const char *magicfile = 0; /* where the magic is */
93 const char *default_magicfile = MAGIC;
95 char *progname; /* used throughout */
96 int lineno; /* line number in the magic file */
99 static void unwrap __P((char *fn));
100 static void usage __P((void));
102 static int byteconv4 __P((int, int, int));
103 static short byteconv2 __P((int, int, int));
106 int main __P((int, char *[]));
109 * main - parse arguments and handle options
117 int action = 0, didsomefiles = 0, errflg = 0, ret = 0, app = 0;
118 char *mime, *home, *usermagic;
122 setlocale(LC_CTYPE, ""); /* makes islower etc work for other langs */
125 if ((progname = strrchr(argv[0], '/')) != NULL)
130 magicfile = default_magicfile;
131 if ((usermagic = getenv("MAGIC")) != NULL)
132 magicfile = usermagic;
134 if ((home = getenv("HOME")) != NULL) {
135 if ((usermagic = malloc(strlen(home) + 8)) != NULL) {
136 (void)strcpy(usermagic, home);
137 (void)strcat(usermagic, "/.magic");
138 if (stat(usermagic, &sb)<0)
141 magicfile = usermagic;
145 while ((c = getopt(argc, argv, "bcdf:ikm:nsvzCL")) != EOF)
161 ret = apprentice(magicfile, action);
171 if ((mime = malloc(strlen(magicfile) + 6)) != NULL) {
172 (void)strcpy(mime, magicfile);
173 (void)strcat(mime, ".mime");
190 (void) fprintf(stdout, "%s-%d.%d\n", progname,
191 FILE_VERSION_MAJOR, patchlevel);
192 (void) fprintf(stdout, "magic file from %s\n",
214 ret = apprentice(magicfile, action);
220 if (optind == argc) {
227 for (wid = 0, i = optind; i < argc; i++) {
228 nw = strlen(argv[i]);
232 for (; optind < argc; optind++)
233 process(argv[optind], wid);
241 * unwrap -- read a file of filenames, do each one.
247 char buf[MAXPATHLEN];
251 if (strcmp("-", fn) == 0) {
255 if ((f = fopen(fn, "r")) == NULL) {
256 error("Cannot open `%s' (%s).\n", fn, strerror(errno));
260 while (fgets(buf, MAXPATHLEN, f) != NULL) {
261 cwid = strlen(buf) - 1;
269 while (fgets(buf, MAXPATHLEN, f) != NULL) {
270 buf[strlen(buf)-1] = '\0';
273 (void) fflush(stdout);
284 * from 4 byte quantity to convert
285 * same whether to perform byte swapping
286 * big_endian whether we are a big endian host
289 byteconv4(from, same, big_endian)
296 else if (big_endian) { /* lsb -> msb conversion on msb */
303 retval.c[0] = tmpval.c[3];
304 retval.c[1] = tmpval.c[2];
305 retval.c[2] = tmpval.c[1];
306 retval.c[3] = tmpval.c[0];
311 return ntohl(from); /* msb -> lsb conversion on lsb */
316 * Same as byteconv4, but for shorts
319 byteconv2(from, same, big_endian)
326 else if (big_endian) { /* lsb -> msb conversion on msb */
332 tmpval.s = (short) from;
333 retval.c[0] = tmpval.c[1];
334 retval.c[1] = tmpval.c[0];
339 return ntohs(from); /* msb -> lsb conversion on lsb */
344 * process - process input file
352 static const char stdname[] = "standard input";
353 unsigned char buf[HOWMANY+1]; /* one extra for terminating '\0' */
355 int nbytes = 0; /* number of bytes read from a datafile */
358 if (strcmp("-", inname) == 0) {
359 if (fstat(0, &sb)<0) {
360 error("cannot fstat `%s' (%s).\n", stdname,
367 if (wid > 0 && !bflag)
368 (void) printf("%s:%*s ", inname,
369 (int) (wid - strlen(inname)), "");
371 if (inname != stdname) {
373 * first try judging the file based on its filesystem status
375 if (fsmagic(inname, &sb) != 0) {
380 if ((fd = open(inname, O_RDONLY)) < 0) {
381 /* We can't open it, but we were able to stat it. */
382 if (sb.st_mode & 0002) ckfputs("writeable, ", stdout);
383 if (sb.st_mode & 0111) ckfputs("executable, ", stdout);
384 ckfprintf(stdout, "can't read `%s' (%s).\n",
385 inname, strerror(errno));
392 * try looking at the first HOWMANY bytes
394 if ((nbytes = read(fd, (char *)buf, HOWMANY)) == -1) {
395 error("read failed (%s).\n", strerror(errno));
400 ckfputs(iflag ? "application/x-empty" : "empty", stdout);
402 buf[nbytes++] = '\0'; /* null-terminate it */
403 match = tryit(buf, nbytes, zflag);
407 if (match == 's' && nbytes > 5) {
409 * We matched something in the file, so this *might*
410 * be an ELF file, and the file is at least 5 bytes long,
411 * so if it's an ELF file it has at least one byte
412 * past the ELF magic number - try extracting information
413 * from the ELF headers that can't easily be extracted
414 * with rules in the magic file.
416 tryelf(fd, buf, nbytes);
420 if (inname != stdname) {
423 * Try to restore access, modification times if read it.
424 * This is really *bad* because it will modify the status
425 * time of the file... And of course this will affect
429 struct timeval utsbuf[2];
430 utsbuf[0].tv_sec = sb.st_atime;
431 utsbuf[1].tv_sec = sb.st_mtime;
433 (void) utimes(inname, utsbuf); /* don't care if loses */
435 struct utimbuf utbuf;
437 utbuf.actime = sb.st_atime;
438 utbuf.modtime = sb.st_mtime;
439 (void) utime(inname, &utbuf); /* don't care if loses */
444 (void) putchar('\n');
449 tryit(buf, nb, zflag)
453 /* try compression stuff */
454 if (zflag && zmagic(buf, nb))
457 /* try tests in /etc/magic (or surrogate magic file) */
458 if (softmagic(buf, nb))
461 /* try known keywords, check whether it is ASCII */
462 if (ascmagic(buf, nb))
465 /* abandon hope, all ye who remain here */
466 ckfputs("data", stdout);
473 (void)fprintf(stderr, USAGE, progname);
474 (void)fprintf(stderr, "Usage: %s -C [-m magic]\n", progname);