Annotation of src/usr.bin/file/file.c, Revision 1.16
1.16 ! jaredy 1: /* $OpenBSD: file.c,v 1.15 2004/09/25 09:19:35 otto Exp $ */
1.1 deraadt 2: /*
1.11 ian 3: * Copyright (c) Ian F. Darwin 1986-1995.
4: * Software written by Ian F. Darwin and others;
5: * maintained 1995-present by Christos Zoulas and others.
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice immediately at the beginning of the file, without modification,
12: * this list of conditions, and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
16: *
17: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21: * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27: * SUCH DAMAGE.
1.1 deraadt 28: */
1.14 tedu 29: /*
30: * file - find type of a file or files - main program.
31: */
1.11 ian 32:
1.14 tedu 33: #include "file.h"
34: #include "magic.h"
1.1 deraadt 35:
36: #include <stdio.h>
37: #include <stdlib.h>
1.14 tedu 38: #include <unistd.h>
1.1 deraadt 39: #include <string.h>
40: #include <sys/types.h>
41: #include <sys/param.h> /* for MAXPATHLEN */
42: #include <sys/stat.h>
43: #include <fcntl.h> /* for open() */
1.14 tedu 44: #ifdef RESTORE_TIME
45: # if (__COHERENT__ >= 0x420)
46: # include <sys/utime.h>
1.5 millert 47: # else
1.14 tedu 48: # ifdef USE_UTIMES
49: # include <sys/time.h>
50: # else
51: # include <utime.h>
52: # endif
1.5 millert 53: # endif
1.1 deraadt 54: #endif
1.14 tedu 55: #ifdef HAVE_UNISTD_H
1.1 deraadt 56: #include <unistd.h> /* for read() */
1.14 tedu 57: #endif
58: #ifdef HAVE_LOCALE_H
59: #include <locale.h>
60: #endif
61: #ifdef HAVE_WCHAR_H
62: #include <wchar.h>
63: #endif
64:
65: #ifdef HAVE_GETOPT_H
66: #include <getopt.h> /* for long options (is this portable?)*/
67: #else
68: #undef HAVE_GETOPT_LONG
69: #endif
1.1 deraadt 70:
1.5 millert 71: #include <netinet/in.h> /* for byte swapping */
1.1 deraadt 72:
73: #include "patchlevel.h"
1.14 tedu 74:
75: #ifndef lint
1.16 ! jaredy 76: FILE_RCSID("@(#)$Id: file.c,v 1.15 2004/09/25 09:19:35 otto Exp $")
1.14 tedu 77: #endif /* lint */
78:
1.1 deraadt 79:
80: #ifdef S_IFLNK
1.14 tedu 81: #define SYMLINKFLAG "L"
1.1 deraadt 82: #else
1.14 tedu 83: #define SYMLINKFLAG ""
1.1 deraadt 84: #endif
85:
1.16 ! jaredy 86: #define USAGE "Usage: %s [-bck" SYMLINKFLAG "Nnrsvz] [-F separator] [-f namefile] [-m magicfiles] file ...\n" \
! 87: " %s [-m magicfiles] -C\n"
1.14 tedu 88:
89: #ifndef MAXPATHLEN
90: #define MAXPATHLEN 512
1.1 deraadt 91: #endif
92:
1.14 tedu 93: private int /* Global command-line options */
94: bflag = 0, /* brief output format */
95: nopad = 0, /* Don't pad output */
96: nobuffer = 0; /* Do not buffer stdout */
97:
98: private const char *magicfile = 0; /* where the magic is */
99: private const char *default_magicfile = MAGIC;
100: private char *separator = ":"; /* Default field separator */
101:
102: private char *progname; /* used throughout */
103:
104: private struct magic_set *magic;
105:
106: private void unwrap(char *);
107: private void usage(void);
108: #ifdef HAVE_GETOPT_LONG
109: private void help(void);
110: #endif
111: #if 0
112: private int byteconv4(int, int, int);
113: private short byteconv2(int, int, int);
114: #endif
1.1 deraadt 115:
1.14 tedu 116: int main(int, char *[]);
117: private void process(const char *, int);
118: private void load(const char *, int);
1.1 deraadt 119:
120:
121: /*
122: * main - parse arguments and handle options
123: */
124: int
1.13 deraadt 125: main(int argc, char *argv[])
1.1 deraadt 126: {
127: int c;
1.14 tedu 128: int action = 0, didsomefiles = 0, errflg = 0;
129: int flags = 0;
130: char *home, *usermagic;
131: struct stat sb;
132: #define OPTSTRING "bcCdf:F:ikLm:nNprsvz"
133: #ifdef HAVE_GETOPT_LONG
134: int longindex;
135: private struct option long_options[] =
136: {
137: {"version", 0, 0, 'v'},
138: {"help", 0, 0, 0},
139: {"brief", 0, 0, 'b'},
140: {"checking-printout", 0, 0, 'c'},
141: {"debug", 0, 0, 'd'},
142: {"files-from", 1, 0, 'f'},
143: {"separator", 1, 0, 'F'},
144: {"mime", 0, 0, 'i'},
145: {"keep-going", 0, 0, 'k'},
146: #ifdef S_IFLNK
147: {"dereference", 0, 0, 'L'},
148: #endif
149: {"magic-file", 1, 0, 'm'},
150: #if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
151: {"preserve-date", 0, 0, 'p'},
152: #endif
153: {"uncompress", 0, 0, 'z'},
154: {"raw", 0, 0, 'r'},
155: {"no-buffer", 0, 0, 'n'},
156: {"no-pad", 0, 0, 'N'},
157: {"special-files", 0, 0, 's'},
158: {"compile", 0, 0, 'C'},
159: {0, 0, 0, 0},
160: };
161: #endif
162:
163: #ifdef LC_CTYPE
164: setlocale(LC_CTYPE, ""); /* makes islower etc work for other langs */
165: #endif
1.1 deraadt 166:
1.14 tedu 167: #ifdef __EMX__
168: /* sh-like wildcard expansion! Shouldn't hurt at least ... */
169: _wildcard(&argc, &argv);
170: #endif
1.1 deraadt 171:
1.14 tedu 172: if ((progname = strrchr(argv[0], '/')) != NULL)
173: progname++;
174: else
175: progname = argv[0];
176:
177: magicfile = default_magicfile;
178: if ((usermagic = getenv("MAGIC")) != NULL)
179: magicfile = usermagic;
180: else
181: if ((home = getenv("HOME")) != NULL) {
182: size_t len = strlen(home) + 8;
183: if ((usermagic = malloc(len)) != NULL) {
184: (void)strlcpy(usermagic, home, len);
185: (void)strlcat(usermagic, "/.magic", len);
186: if (stat(usermagic, &sb)<0)
187: free(usermagic);
188: else
189: magicfile = usermagic;
190: }
191: }
192:
193: #ifndef HAVE_GETOPT_LONG
194: while ((c = getopt(argc, argv, OPTSTRING)) != -1)
195: #else
196: while ((c = getopt_long(argc, argv, OPTSTRING, long_options,
197: &longindex)) != -1)
198: #endif
1.1 deraadt 199: switch (c) {
1.14 tedu 200: #ifdef HAVE_GETOPT_LONG
201: case 0 :
202: if (longindex == 1)
203: help();
204: break;
205: #endif
1.9 millert 206: case 'b':
207: ++bflag;
208: break;
1.1 deraadt 209: case 'c':
1.14 tedu 210: action = FILE_CHECK;
211: break;
212: case 'C':
213: action = FILE_COMPILE;
1.1 deraadt 214: break;
215: case 'd':
1.14 tedu 216: flags |= MAGIC_DEBUG|MAGIC_CHECK;
1.1 deraadt 217: break;
218: case 'f':
1.14 tedu 219: if(action)
220: usage();
221: load(magicfile, flags);
1.1 deraadt 222: unwrap(optarg);
223: ++didsomefiles;
224: break;
1.14 tedu 225: case 'F':
226: separator = optarg;
227: break;
228: case 'i':
229: flags |= MAGIC_MIME;
230: break;
231: case 'k':
232: flags |= MAGIC_CONTINUE;
1.1 deraadt 233: break;
234: case 'm':
235: magicfile = optarg;
236: break;
1.14 tedu 237: case 'n':
238: ++nobuffer;
239: break;
240: case 'N':
241: ++nopad;
242: break;
243: #if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
244: case 'p':
245: flags |= MAGIC_PRESERVE_ATIME;
246: break;
247: #endif
248: case 'r':
249: flags |= MAGIC_RAW;
250: break;
251: case 's':
252: flags |= MAGIC_DEVICES;
253: break;
254: case 'v':
255: (void) fprintf(stdout, "%s-%d.%.2d\n", progname,
256: FILE_VERSION_MAJOR, patchlevel);
257: (void) fprintf(stdout, "magic file from %s\n",
258: magicfile);
259: return 1;
1.1 deraadt 260: case 'z':
1.14 tedu 261: flags |= MAGIC_COMPRESS;
1.1 deraadt 262: break;
1.14 tedu 263: #ifdef S_IFLNK
264: case 'L':
265: flags |= MAGIC_SYMLINK;
266: break;
267: #endif
1.1 deraadt 268: case '?':
269: default:
270: errflg++;
271: break;
272: }
273:
274: if (errflg) {
1.14 tedu 275: usage();
1.1 deraadt 276: }
277:
1.14 tedu 278: switch(action) {
279: case FILE_CHECK:
280: case FILE_COMPILE:
281: magic = magic_open(flags|MAGIC_CHECK);
282: if (magic == NULL) {
283: (void)fprintf(stderr, "%s: %s\n", progname,
284: strerror(errno));
285: return 1;
286: }
287: c = action == FILE_CHECK ? magic_check(magic, magicfile) :
288: magic_compile(magic, magicfile);
289: if (c == -1) {
290: (void)fprintf(stderr, "%s: %s\n", progname,
291: magic_error(magic));
292: return -1;
293: }
294: return 0;
295: default:
296: load(magicfile, flags);
297: break;
1.1 deraadt 298: }
299:
300: if (optind == argc) {
1.7 deraadt 301: if (!didsomefiles) {
1.14 tedu 302: usage();
1.7 deraadt 303: }
1.14 tedu 304: }
305: else {
1.1 deraadt 306: int i, wid, nw;
307: for (wid = 0, i = optind; i < argc; i++) {
1.14 tedu 308: nw = file_mbswidth(argv[i]);
1.1 deraadt 309: if (nw > wid)
310: wid = nw;
311: }
312: for (; optind < argc; optind++)
313: process(argv[optind], wid);
314: }
315:
316: return 0;
317: }
318:
319:
1.14 tedu 320: private void
321: load(const char *m, int flags)
322: {
323: if (magic)
324: return;
325: magic = magic_open(flags);
326: if (magic == NULL) {
327: (void)fprintf(stderr, "%s: %s\n", progname, strerror(errno));
328: exit(1);
329: }
330: if (magic_load(magic, magicfile) == -1) {
331: (void)fprintf(stderr, "%s: %s\n",
332: progname, magic_error(magic));
333: exit(1);
334: }
335: }
336:
1.1 deraadt 337: /*
338: * unwrap -- read a file of filenames, do each one.
339: */
1.14 tedu 340: private void
341: unwrap(char *fn)
1.1 deraadt 342: {
343: char buf[MAXPATHLEN];
344: FILE *f;
345: int wid = 0, cwid;
346:
1.5 millert 347: if (strcmp("-", fn) == 0) {
348: f = stdin;
349: wid = 1;
350: } else {
351: if ((f = fopen(fn, "r")) == NULL) {
1.14 tedu 352: (void)fprintf(stderr, "%s: Cannot open `%s' (%s).\n",
353: progname, fn, strerror(errno));
354: exit(1);
1.5 millert 355: }
356:
1.15 otto 357: while (fgets(buf, sizeof(buf), f) != NULL) {
1.14 tedu 358: cwid = file_mbswidth(buf) - 1;
1.5 millert 359: if (cwid > wid)
360: wid = cwid;
361: }
1.1 deraadt 362:
1.5 millert 363: rewind(f);
1.1 deraadt 364: }
365:
1.15 otto 366: while (fgets(buf, sizeof(buf), f) != NULL) {
1.14 tedu 367: buf[file_mbswidth(buf)-1] = '\0';
1.1 deraadt 368: process(buf, wid);
1.14 tedu 369: if(nobuffer)
370: (void) fflush(stdout);
1.1 deraadt 371: }
372:
373: (void) fclose(f);
374: }
375:
1.14 tedu 376: private void
377: process(const char *inname, int wid)
378: {
379: const char *type;
380: int std_in = strcmp(inname, "-") == 0;
381:
382: if (wid > 0 && !bflag)
383: (void) printf("%s%s%*s ", std_in ? "/dev/stdin" : inname,
384: separator, (int) (nopad ? 0 : (wid - file_mbswidth(inname))), "");
385:
386: type = magic_file(magic, std_in ? NULL : inname);
387: if (type == NULL)
388: printf("ERROR: %s\n", magic_error(magic));
389: else
390: printf("%s\n", type);
391: }
392:
1.1 deraadt 393:
1.5 millert 394: #if 0
395: /*
396: * byteconv4
397: * Input:
398: * from 4 byte quantity to convert
399: * same whether to perform byte swapping
400: * big_endian whether we are a big endian host
401: */
1.14 tedu 402: private int
403: byteconv4(int from, int same, int big_endian)
1.5 millert 404: {
1.14 tedu 405: if (same)
406: return from;
407: else if (big_endian) { /* lsb -> msb conversion on msb */
408: union {
409: int i;
410: char c[4];
411: } retval, tmpval;
412:
413: tmpval.i = from;
414: retval.c[0] = tmpval.c[3];
415: retval.c[1] = tmpval.c[2];
416: retval.c[2] = tmpval.c[1];
417: retval.c[3] = tmpval.c[0];
418:
419: return retval.i;
420: }
421: else
422: return ntohl(from); /* msb -> lsb conversion on lsb */
1.5 millert 423: }
424:
425: /*
426: * byteconv2
427: * Same as byteconv4, but for shorts
428: */
1.14 tedu 429: private short
430: byteconv2(int from, int same, int big_endian)
1.5 millert 431: {
1.14 tedu 432: if (same)
433: return from;
434: else if (big_endian) { /* lsb -> msb conversion on msb */
435: union {
436: short s;
437: char c[2];
438: } retval, tmpval;
439:
440: tmpval.s = (short) from;
441: retval.c[0] = tmpval.c[1];
442: retval.c[1] = tmpval.c[0];
443:
444: return retval.s;
445: }
446: else
447: return ntohs(from); /* msb -> lsb conversion on lsb */
1.5 millert 448: }
449: #endif
450:
1.14 tedu 451: size_t
452: file_mbswidth(const char *s)
1.1 deraadt 453: {
1.14 tedu 454: #ifdef HAVE_WCHAR_H
455: size_t bytesconsumed, old_n, n, width = 0;
456: mbstate_t state;
457: wchar_t nextchar;
458: (void)memset(&state, 0, sizeof(mbstate_t));
459: old_n = n = strlen(s);
460:
461: while (n > 0) {
462: bytesconsumed = mbrtowc(&nextchar, s, n, &state);
463: if (bytesconsumed == (size_t)(-1) ||
464: bytesconsumed == (size_t)(-2)) {
465: /* Something went wrong, return something reasonable */
466: return old_n;
1.1 deraadt 467: }
1.14 tedu 468: if (s[0] == '\n') {
469: /*
470: * do what strlen() would do, so that caller
471: * is always right
472: */
473: width++;
474: } else
475: width += wcwidth(nextchar);
1.1 deraadt 476:
1.14 tedu 477: s += bytesconsumed, n -= bytesconsumed;
1.1 deraadt 478: }
1.14 tedu 479: return width;
480: #else
481: return strlen(s);
1.5 millert 482: #endif
1.14 tedu 483: }
1.1 deraadt 484:
1.14 tedu 485: private void
486: usage(void)
487: {
488: (void)fprintf(stderr, USAGE, progname, progname);
489: #ifdef HAVE_GETOPT_LONG
490: (void)fputs("Try `file --help' for more information.\n", stderr);
1.1 deraadt 491: #endif
1.14 tedu 492: exit(1);
1.1 deraadt 493: }
494:
1.14 tedu 495: #ifdef HAVE_GETOPT_LONG
496: private void
497: help(void)
1.1 deraadt 498: {
1.14 tedu 499: puts(
500: "Usage: file [OPTION]... [FILE]...\n"
501: "Determine file type of FILEs.\n"
502: "\n"
503: " -m, --magic-file LIST use LIST as a colon-separated list of magic\n"
504: " number files\n"
505: " -z, --uncompress try to look inside compressed files\n"
506: " -b, --brief do not prepend filenames to output lines\n"
507: " -c, --checking-printout print the parsed form of the magic file, use in\n"
508: " conjunction with -m to debug a new magic file\n"
509: " before installing it\n"
510: " -f, --files-from FILE read the filenames to be examined from FILE\n"
511: " -F, --separator string use string as separator instead of `:'\n"
512: " -i, --mime output mime type strings\n"
513: " -k, --keep-going don't stop at the first match\n"
514: " -L, --dereference causes symlinks to be followed\n"
515: " -n, --no-buffer do not buffer output\n"
516: " -N, --no-pad do not pad output\n"
517: " -p, --preserve-date preserve access times on files\n"
518: " -r, --raw don't translate unprintable chars to \\ooo\n"
519: " -s, --special-files treat special (block/char devices) files as\n"
520: " ordinary ones\n"
521: " --help display this help and exit\n"
522: " --version output version information and exit\n"
523: );
524: exit(0);
1.1 deraadt 525: }
1.14 tedu 526: #endif