Annotation of src/usr.bin/file/file.c, Revision 1.17
1.17 ! tom 1: /* $OpenBSD: file.c,v 1.16 2004/12/04 19:55:12 jaredy 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.17 ! tom 76: FILE_RCSID("@(#)$Id: file.c,v 1.16 2004/12/04 19:55:12 jaredy 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;
1.17 ! tom 132: #define OPTSTRING "bcCdf:F:kLm:nNprsvz"
1.14 tedu 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: {"keep-going", 0, 0, 'k'},
145: #ifdef S_IFLNK
146: {"dereference", 0, 0, 'L'},
147: #endif
148: {"magic-file", 1, 0, 'm'},
149: #if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
150: {"preserve-date", 0, 0, 'p'},
151: #endif
152: {"uncompress", 0, 0, 'z'},
153: {"raw", 0, 0, 'r'},
154: {"no-buffer", 0, 0, 'n'},
155: {"no-pad", 0, 0, 'N'},
156: {"special-files", 0, 0, 's'},
157: {"compile", 0, 0, 'C'},
158: {0, 0, 0, 0},
159: };
160: #endif
161:
162: #ifdef LC_CTYPE
163: setlocale(LC_CTYPE, ""); /* makes islower etc work for other langs */
164: #endif
1.1 deraadt 165:
1.14 tedu 166: #ifdef __EMX__
167: /* sh-like wildcard expansion! Shouldn't hurt at least ... */
168: _wildcard(&argc, &argv);
169: #endif
1.1 deraadt 170:
1.14 tedu 171: if ((progname = strrchr(argv[0], '/')) != NULL)
172: progname++;
173: else
174: progname = argv[0];
175:
176: magicfile = default_magicfile;
177: if ((usermagic = getenv("MAGIC")) != NULL)
178: magicfile = usermagic;
179: else
180: if ((home = getenv("HOME")) != NULL) {
181: size_t len = strlen(home) + 8;
182: if ((usermagic = malloc(len)) != NULL) {
183: (void)strlcpy(usermagic, home, len);
184: (void)strlcat(usermagic, "/.magic", len);
185: if (stat(usermagic, &sb)<0)
186: free(usermagic);
187: else
188: magicfile = usermagic;
189: }
190: }
191:
192: #ifndef HAVE_GETOPT_LONG
193: while ((c = getopt(argc, argv, OPTSTRING)) != -1)
194: #else
195: while ((c = getopt_long(argc, argv, OPTSTRING, long_options,
196: &longindex)) != -1)
197: #endif
1.1 deraadt 198: switch (c) {
1.14 tedu 199: #ifdef HAVE_GETOPT_LONG
200: case 0 :
201: if (longindex == 1)
202: help();
203: break;
204: #endif
1.9 millert 205: case 'b':
206: ++bflag;
207: break;
1.1 deraadt 208: case 'c':
1.14 tedu 209: action = FILE_CHECK;
210: break;
211: case 'C':
212: action = FILE_COMPILE;
1.1 deraadt 213: break;
214: case 'd':
1.14 tedu 215: flags |= MAGIC_DEBUG|MAGIC_CHECK;
1.1 deraadt 216: break;
217: case 'f':
1.14 tedu 218: if(action)
219: usage();
220: load(magicfile, flags);
1.1 deraadt 221: unwrap(optarg);
222: ++didsomefiles;
223: break;
1.14 tedu 224: case 'F':
225: separator = optarg;
226: break;
227: case 'k':
228: flags |= MAGIC_CONTINUE;
1.1 deraadt 229: break;
230: case 'm':
231: magicfile = optarg;
232: break;
1.14 tedu 233: case 'n':
234: ++nobuffer;
235: break;
236: case 'N':
237: ++nopad;
238: break;
239: #if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
240: case 'p':
241: flags |= MAGIC_PRESERVE_ATIME;
242: break;
243: #endif
244: case 'r':
245: flags |= MAGIC_RAW;
246: break;
247: case 's':
248: flags |= MAGIC_DEVICES;
249: break;
250: case 'v':
251: (void) fprintf(stdout, "%s-%d.%.2d\n", progname,
252: FILE_VERSION_MAJOR, patchlevel);
253: (void) fprintf(stdout, "magic file from %s\n",
254: magicfile);
255: return 1;
1.1 deraadt 256: case 'z':
1.14 tedu 257: flags |= MAGIC_COMPRESS;
1.1 deraadt 258: break;
1.14 tedu 259: #ifdef S_IFLNK
260: case 'L':
261: flags |= MAGIC_SYMLINK;
262: break;
263: #endif
1.1 deraadt 264: case '?':
265: default:
266: errflg++;
267: break;
268: }
269:
270: if (errflg) {
1.14 tedu 271: usage();
1.1 deraadt 272: }
273:
1.14 tedu 274: switch(action) {
275: case FILE_CHECK:
276: case FILE_COMPILE:
277: magic = magic_open(flags|MAGIC_CHECK);
278: if (magic == NULL) {
279: (void)fprintf(stderr, "%s: %s\n", progname,
280: strerror(errno));
281: return 1;
282: }
283: c = action == FILE_CHECK ? magic_check(magic, magicfile) :
284: magic_compile(magic, magicfile);
285: if (c == -1) {
286: (void)fprintf(stderr, "%s: %s\n", progname,
287: magic_error(magic));
288: return -1;
289: }
290: return 0;
291: default:
292: load(magicfile, flags);
293: break;
1.1 deraadt 294: }
295:
296: if (optind == argc) {
1.7 deraadt 297: if (!didsomefiles) {
1.14 tedu 298: usage();
1.7 deraadt 299: }
1.14 tedu 300: }
301: else {
1.1 deraadt 302: int i, wid, nw;
303: for (wid = 0, i = optind; i < argc; i++) {
1.14 tedu 304: nw = file_mbswidth(argv[i]);
1.1 deraadt 305: if (nw > wid)
306: wid = nw;
307: }
308: for (; optind < argc; optind++)
309: process(argv[optind], wid);
310: }
311:
312: return 0;
313: }
314:
315:
1.14 tedu 316: private void
317: load(const char *m, int flags)
318: {
319: if (magic)
320: return;
321: magic = magic_open(flags);
322: if (magic == NULL) {
323: (void)fprintf(stderr, "%s: %s\n", progname, strerror(errno));
324: exit(1);
325: }
326: if (magic_load(magic, magicfile) == -1) {
327: (void)fprintf(stderr, "%s: %s\n",
328: progname, magic_error(magic));
329: exit(1);
330: }
331: }
332:
1.1 deraadt 333: /*
334: * unwrap -- read a file of filenames, do each one.
335: */
1.14 tedu 336: private void
337: unwrap(char *fn)
1.1 deraadt 338: {
339: char buf[MAXPATHLEN];
340: FILE *f;
341: int wid = 0, cwid;
342:
1.5 millert 343: if (strcmp("-", fn) == 0) {
344: f = stdin;
345: wid = 1;
346: } else {
347: if ((f = fopen(fn, "r")) == NULL) {
1.14 tedu 348: (void)fprintf(stderr, "%s: Cannot open `%s' (%s).\n",
349: progname, fn, strerror(errno));
350: exit(1);
1.5 millert 351: }
352:
1.15 otto 353: while (fgets(buf, sizeof(buf), f) != NULL) {
1.14 tedu 354: cwid = file_mbswidth(buf) - 1;
1.5 millert 355: if (cwid > wid)
356: wid = cwid;
357: }
1.1 deraadt 358:
1.5 millert 359: rewind(f);
1.1 deraadt 360: }
361:
1.15 otto 362: while (fgets(buf, sizeof(buf), f) != NULL) {
1.14 tedu 363: buf[file_mbswidth(buf)-1] = '\0';
1.1 deraadt 364: process(buf, wid);
1.14 tedu 365: if(nobuffer)
366: (void) fflush(stdout);
1.1 deraadt 367: }
368:
369: (void) fclose(f);
370: }
371:
1.14 tedu 372: private void
373: process(const char *inname, int wid)
374: {
375: const char *type;
376: int std_in = strcmp(inname, "-") == 0;
377:
378: if (wid > 0 && !bflag)
379: (void) printf("%s%s%*s ", std_in ? "/dev/stdin" : inname,
380: separator, (int) (nopad ? 0 : (wid - file_mbswidth(inname))), "");
381:
382: type = magic_file(magic, std_in ? NULL : inname);
383: if (type == NULL)
384: printf("ERROR: %s\n", magic_error(magic));
385: else
386: printf("%s\n", type);
387: }
388:
1.1 deraadt 389:
1.5 millert 390: #if 0
391: /*
392: * byteconv4
393: * Input:
394: * from 4 byte quantity to convert
395: * same whether to perform byte swapping
396: * big_endian whether we are a big endian host
397: */
1.14 tedu 398: private int
399: byteconv4(int from, int same, int big_endian)
1.5 millert 400: {
1.14 tedu 401: if (same)
402: return from;
403: else if (big_endian) { /* lsb -> msb conversion on msb */
404: union {
405: int i;
406: char c[4];
407: } retval, tmpval;
408:
409: tmpval.i = from;
410: retval.c[0] = tmpval.c[3];
411: retval.c[1] = tmpval.c[2];
412: retval.c[2] = tmpval.c[1];
413: retval.c[3] = tmpval.c[0];
414:
415: return retval.i;
416: }
417: else
418: return ntohl(from); /* msb -> lsb conversion on lsb */
1.5 millert 419: }
420:
421: /*
422: * byteconv2
423: * Same as byteconv4, but for shorts
424: */
1.14 tedu 425: private short
426: byteconv2(int from, int same, int big_endian)
1.5 millert 427: {
1.14 tedu 428: if (same)
429: return from;
430: else if (big_endian) { /* lsb -> msb conversion on msb */
431: union {
432: short s;
433: char c[2];
434: } retval, tmpval;
435:
436: tmpval.s = (short) from;
437: retval.c[0] = tmpval.c[1];
438: retval.c[1] = tmpval.c[0];
439:
440: return retval.s;
441: }
442: else
443: return ntohs(from); /* msb -> lsb conversion on lsb */
1.5 millert 444: }
445: #endif
446:
1.14 tedu 447: size_t
448: file_mbswidth(const char *s)
1.1 deraadt 449: {
1.14 tedu 450: #ifdef HAVE_WCHAR_H
451: size_t bytesconsumed, old_n, n, width = 0;
452: mbstate_t state;
453: wchar_t nextchar;
454: (void)memset(&state, 0, sizeof(mbstate_t));
455: old_n = n = strlen(s);
456:
457: while (n > 0) {
458: bytesconsumed = mbrtowc(&nextchar, s, n, &state);
459: if (bytesconsumed == (size_t)(-1) ||
460: bytesconsumed == (size_t)(-2)) {
461: /* Something went wrong, return something reasonable */
462: return old_n;
1.1 deraadt 463: }
1.14 tedu 464: if (s[0] == '\n') {
465: /*
466: * do what strlen() would do, so that caller
467: * is always right
468: */
469: width++;
470: } else
471: width += wcwidth(nextchar);
1.1 deraadt 472:
1.14 tedu 473: s += bytesconsumed, n -= bytesconsumed;
1.1 deraadt 474: }
1.14 tedu 475: return width;
476: #else
477: return strlen(s);
1.5 millert 478: #endif
1.14 tedu 479: }
1.1 deraadt 480:
1.14 tedu 481: private void
482: usage(void)
483: {
484: (void)fprintf(stderr, USAGE, progname, progname);
485: #ifdef HAVE_GETOPT_LONG
486: (void)fputs("Try `file --help' for more information.\n", stderr);
1.1 deraadt 487: #endif
1.14 tedu 488: exit(1);
1.1 deraadt 489: }
490:
1.14 tedu 491: #ifdef HAVE_GETOPT_LONG
492: private void
493: help(void)
1.1 deraadt 494: {
1.14 tedu 495: puts(
496: "Usage: file [OPTION]... [FILE]...\n"
497: "Determine file type of FILEs.\n"
498: "\n"
499: " -m, --magic-file LIST use LIST as a colon-separated list of magic\n"
500: " number files\n"
501: " -z, --uncompress try to look inside compressed files\n"
502: " -b, --brief do not prepend filenames to output lines\n"
503: " -c, --checking-printout print the parsed form of the magic file, use in\n"
504: " conjunction with -m to debug a new magic file\n"
505: " before installing it\n"
506: " -f, --files-from FILE read the filenames to be examined from FILE\n"
507: " -F, --separator string use string as separator instead of `:'\n"
508: " -k, --keep-going don't stop at the first match\n"
509: " -L, --dereference causes symlinks to be followed\n"
510: " -n, --no-buffer do not buffer output\n"
511: " -N, --no-pad do not pad output\n"
512: " -p, --preserve-date preserve access times on files\n"
513: " -r, --raw don't translate unprintable chars to \\ooo\n"
514: " -s, --special-files treat special (block/char devices) files as\n"
515: " ordinary ones\n"
516: " --help display this help and exit\n"
517: " --version output version information and exit\n"
518: );
519: exit(0);
1.1 deraadt 520: }
1.14 tedu 521: #endif