Annotation of src/usr.bin/file/file.c, Revision 1.15
1.15 ! otto 1: /* $OpenBSD: file.c,v 1.14 2004/05/19 02:32:35 tedu 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.15 ! otto 76: FILE_RCSID("@(#)$Id: file.c,v 1.14 2004/05/19 02:32:35 tedu 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.14 tedu 86: # define USAGE "Usage: %s [-bcik" SYMLINKFLAG "nNsvz] [-f namefile] [-F separator] [-m magicfiles] file...\n %s -C -m magicfiles\n"
87:
88: #ifndef MAXPATHLEN
89: #define MAXPATHLEN 512
1.1 deraadt 90: #endif
91:
1.14 tedu 92: private int /* Global command-line options */
93: bflag = 0, /* brief output format */
94: nopad = 0, /* Don't pad output */
95: nobuffer = 0; /* Do not buffer stdout */
96:
97: private const char *magicfile = 0; /* where the magic is */
98: private const char *default_magicfile = MAGIC;
99: private char *separator = ":"; /* Default field separator */
100:
101: private char *progname; /* used throughout */
102:
103: private struct magic_set *magic;
104:
105: private void unwrap(char *);
106: private void usage(void);
107: #ifdef HAVE_GETOPT_LONG
108: private void help(void);
109: #endif
110: #if 0
111: private int byteconv4(int, int, int);
112: private short byteconv2(int, int, int);
113: #endif
1.1 deraadt 114:
1.14 tedu 115: int main(int, char *[]);
116: private void process(const char *, int);
117: private void load(const char *, int);
1.1 deraadt 118:
119:
120: /*
121: * main - parse arguments and handle options
122: */
123: int
1.13 deraadt 124: main(int argc, char *argv[])
1.1 deraadt 125: {
126: int c;
1.14 tedu 127: int action = 0, didsomefiles = 0, errflg = 0;
128: int flags = 0;
129: char *home, *usermagic;
130: struct stat sb;
131: #define OPTSTRING "bcCdf:F:ikLm:nNprsvz"
132: #ifdef HAVE_GETOPT_LONG
133: int longindex;
134: private struct option long_options[] =
135: {
136: {"version", 0, 0, 'v'},
137: {"help", 0, 0, 0},
138: {"brief", 0, 0, 'b'},
139: {"checking-printout", 0, 0, 'c'},
140: {"debug", 0, 0, 'd'},
141: {"files-from", 1, 0, 'f'},
142: {"separator", 1, 0, 'F'},
143: {"mime", 0, 0, 'i'},
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 'i':
228: flags |= MAGIC_MIME;
229: break;
230: case 'k':
231: flags |= MAGIC_CONTINUE;
1.1 deraadt 232: break;
233: case 'm':
234: magicfile = optarg;
235: break;
1.14 tedu 236: case 'n':
237: ++nobuffer;
238: break;
239: case 'N':
240: ++nopad;
241: break;
242: #if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
243: case 'p':
244: flags |= MAGIC_PRESERVE_ATIME;
245: break;
246: #endif
247: case 'r':
248: flags |= MAGIC_RAW;
249: break;
250: case 's':
251: flags |= MAGIC_DEVICES;
252: break;
253: case 'v':
254: (void) fprintf(stdout, "%s-%d.%.2d\n", progname,
255: FILE_VERSION_MAJOR, patchlevel);
256: (void) fprintf(stdout, "magic file from %s\n",
257: magicfile);
258: return 1;
1.1 deraadt 259: case 'z':
1.14 tedu 260: flags |= MAGIC_COMPRESS;
1.1 deraadt 261: break;
1.14 tedu 262: #ifdef S_IFLNK
263: case 'L':
264: flags |= MAGIC_SYMLINK;
265: break;
266: #endif
1.1 deraadt 267: case '?':
268: default:
269: errflg++;
270: break;
271: }
272:
273: if (errflg) {
1.14 tedu 274: usage();
1.1 deraadt 275: }
276:
1.14 tedu 277: switch(action) {
278: case FILE_CHECK:
279: case FILE_COMPILE:
280: magic = magic_open(flags|MAGIC_CHECK);
281: if (magic == NULL) {
282: (void)fprintf(stderr, "%s: %s\n", progname,
283: strerror(errno));
284: return 1;
285: }
286: c = action == FILE_CHECK ? magic_check(magic, magicfile) :
287: magic_compile(magic, magicfile);
288: if (c == -1) {
289: (void)fprintf(stderr, "%s: %s\n", progname,
290: magic_error(magic));
291: return -1;
292: }
293: return 0;
294: default:
295: load(magicfile, flags);
296: break;
1.1 deraadt 297: }
298:
299: if (optind == argc) {
1.7 deraadt 300: if (!didsomefiles) {
1.14 tedu 301: usage();
1.7 deraadt 302: }
1.14 tedu 303: }
304: else {
1.1 deraadt 305: int i, wid, nw;
306: for (wid = 0, i = optind; i < argc; i++) {
1.14 tedu 307: nw = file_mbswidth(argv[i]);
1.1 deraadt 308: if (nw > wid)
309: wid = nw;
310: }
311: for (; optind < argc; optind++)
312: process(argv[optind], wid);
313: }
314:
315: return 0;
316: }
317:
318:
1.14 tedu 319: private void
320: load(const char *m, int flags)
321: {
322: if (magic)
323: return;
324: magic = magic_open(flags);
325: if (magic == NULL) {
326: (void)fprintf(stderr, "%s: %s\n", progname, strerror(errno));
327: exit(1);
328: }
329: if (magic_load(magic, magicfile) == -1) {
330: (void)fprintf(stderr, "%s: %s\n",
331: progname, magic_error(magic));
332: exit(1);
333: }
334: }
335:
1.1 deraadt 336: /*
337: * unwrap -- read a file of filenames, do each one.
338: */
1.14 tedu 339: private void
340: unwrap(char *fn)
1.1 deraadt 341: {
342: char buf[MAXPATHLEN];
343: FILE *f;
344: int wid = 0, cwid;
345:
1.5 millert 346: if (strcmp("-", fn) == 0) {
347: f = stdin;
348: wid = 1;
349: } else {
350: if ((f = fopen(fn, "r")) == NULL) {
1.14 tedu 351: (void)fprintf(stderr, "%s: Cannot open `%s' (%s).\n",
352: progname, fn, strerror(errno));
353: exit(1);
1.5 millert 354: }
355:
1.15 ! otto 356: while (fgets(buf, sizeof(buf), f) != NULL) {
1.14 tedu 357: cwid = file_mbswidth(buf) - 1;
1.5 millert 358: if (cwid > wid)
359: wid = cwid;
360: }
1.1 deraadt 361:
1.5 millert 362: rewind(f);
1.1 deraadt 363: }
364:
1.15 ! otto 365: while (fgets(buf, sizeof(buf), f) != NULL) {
1.14 tedu 366: buf[file_mbswidth(buf)-1] = '\0';
1.1 deraadt 367: process(buf, wid);
1.14 tedu 368: if(nobuffer)
369: (void) fflush(stdout);
1.1 deraadt 370: }
371:
372: (void) fclose(f);
373: }
374:
1.14 tedu 375: private void
376: process(const char *inname, int wid)
377: {
378: const char *type;
379: int std_in = strcmp(inname, "-") == 0;
380:
381: if (wid > 0 && !bflag)
382: (void) printf("%s%s%*s ", std_in ? "/dev/stdin" : inname,
383: separator, (int) (nopad ? 0 : (wid - file_mbswidth(inname))), "");
384:
385: type = magic_file(magic, std_in ? NULL : inname);
386: if (type == NULL)
387: printf("ERROR: %s\n", magic_error(magic));
388: else
389: printf("%s\n", type);
390: }
391:
1.1 deraadt 392:
1.5 millert 393: #if 0
394: /*
395: * byteconv4
396: * Input:
397: * from 4 byte quantity to convert
398: * same whether to perform byte swapping
399: * big_endian whether we are a big endian host
400: */
1.14 tedu 401: private int
402: byteconv4(int from, int same, int big_endian)
1.5 millert 403: {
1.14 tedu 404: if (same)
405: return from;
406: else if (big_endian) { /* lsb -> msb conversion on msb */
407: union {
408: int i;
409: char c[4];
410: } retval, tmpval;
411:
412: tmpval.i = from;
413: retval.c[0] = tmpval.c[3];
414: retval.c[1] = tmpval.c[2];
415: retval.c[2] = tmpval.c[1];
416: retval.c[3] = tmpval.c[0];
417:
418: return retval.i;
419: }
420: else
421: return ntohl(from); /* msb -> lsb conversion on lsb */
1.5 millert 422: }
423:
424: /*
425: * byteconv2
426: * Same as byteconv4, but for shorts
427: */
1.14 tedu 428: private short
429: byteconv2(int from, int same, int big_endian)
1.5 millert 430: {
1.14 tedu 431: if (same)
432: return from;
433: else if (big_endian) { /* lsb -> msb conversion on msb */
434: union {
435: short s;
436: char c[2];
437: } retval, tmpval;
438:
439: tmpval.s = (short) from;
440: retval.c[0] = tmpval.c[1];
441: retval.c[1] = tmpval.c[0];
442:
443: return retval.s;
444: }
445: else
446: return ntohs(from); /* msb -> lsb conversion on lsb */
1.5 millert 447: }
448: #endif
449:
1.14 tedu 450: size_t
451: file_mbswidth(const char *s)
1.1 deraadt 452: {
1.14 tedu 453: #ifdef HAVE_WCHAR_H
454: size_t bytesconsumed, old_n, n, width = 0;
455: mbstate_t state;
456: wchar_t nextchar;
457: (void)memset(&state, 0, sizeof(mbstate_t));
458: old_n = n = strlen(s);
459:
460: while (n > 0) {
461: bytesconsumed = mbrtowc(&nextchar, s, n, &state);
462: if (bytesconsumed == (size_t)(-1) ||
463: bytesconsumed == (size_t)(-2)) {
464: /* Something went wrong, return something reasonable */
465: return old_n;
1.1 deraadt 466: }
1.14 tedu 467: if (s[0] == '\n') {
468: /*
469: * do what strlen() would do, so that caller
470: * is always right
471: */
472: width++;
473: } else
474: width += wcwidth(nextchar);
1.1 deraadt 475:
1.14 tedu 476: s += bytesconsumed, n -= bytesconsumed;
1.1 deraadt 477: }
1.14 tedu 478: return width;
479: #else
480: return strlen(s);
1.5 millert 481: #endif
1.14 tedu 482: }
1.1 deraadt 483:
1.14 tedu 484: private void
485: usage(void)
486: {
487: (void)fprintf(stderr, USAGE, progname, progname);
488: #ifdef HAVE_GETOPT_LONG
489: (void)fputs("Try `file --help' for more information.\n", stderr);
1.1 deraadt 490: #endif
1.14 tedu 491: exit(1);
1.1 deraadt 492: }
493:
1.14 tedu 494: #ifdef HAVE_GETOPT_LONG
495: private void
496: help(void)
1.1 deraadt 497: {
1.14 tedu 498: puts(
499: "Usage: file [OPTION]... [FILE]...\n"
500: "Determine file type of FILEs.\n"
501: "\n"
502: " -m, --magic-file LIST use LIST as a colon-separated list of magic\n"
503: " number files\n"
504: " -z, --uncompress try to look inside compressed files\n"
505: " -b, --brief do not prepend filenames to output lines\n"
506: " -c, --checking-printout print the parsed form of the magic file, use in\n"
507: " conjunction with -m to debug a new magic file\n"
508: " before installing it\n"
509: " -f, --files-from FILE read the filenames to be examined from FILE\n"
510: " -F, --separator string use string as separator instead of `:'\n"
511: " -i, --mime output mime type strings\n"
512: " -k, --keep-going don't stop at the first match\n"
513: " -L, --dereference causes symlinks to be followed\n"
514: " -n, --no-buffer do not buffer output\n"
515: " -N, --no-pad do not pad output\n"
516: " -p, --preserve-date preserve access times on files\n"
517: " -r, --raw don't translate unprintable chars to \\ooo\n"
518: " -s, --special-files treat special (block/char devices) files as\n"
519: " ordinary ones\n"
520: " --help display this help and exit\n"
521: " --version output version information and exit\n"
522: );
523: exit(0);
1.1 deraadt 524: }
1.14 tedu 525: #endif