Annotation of src/usr.bin/file/file.c, Revision 1.23
1.23 ! stsp 1: /* $OpenBSD: file.c,v 1.22 2009/10/27 23:59:37 deraadt 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.20 deraadt 33: #include <sys/types.h>
34: #include <sys/param.h> /* for MAXPATHLEN */
35: #include <sys/stat.h>
36:
1.14 tedu 37: #include "file.h"
38: #include "magic.h"
1.1 deraadt 39:
40: #include <stdio.h>
41: #include <stdlib.h>
1.14 tedu 42: #include <unistd.h>
1.1 deraadt 43: #include <string.h>
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:
1.19 chl 65: #include <getopt.h>
66: #ifndef HAVE_GETOPT_LONG
67: int getopt_long(int argc, char * const *argv, const char *optstring, const struct option *longopts, int *longindex);
1.14 tedu 68: #endif
1.1 deraadt 69:
1.5 millert 70: #include <netinet/in.h> /* for byte swapping */
1.1 deraadt 71:
72: #include "patchlevel.h"
1.14 tedu 73:
1.1 deraadt 74:
75: #ifdef S_IFLNK
1.18 chl 76: #define SYMLINKFLAG "Lh"
1.1 deraadt 77: #else
1.14 tedu 78: #define SYMLINKFLAG ""
1.1 deraadt 79: #endif
80:
1.21 ajacouto 81: # define USAGE "Usage: %s [-bcik" SYMLINKFLAG "nNprsvz0] [-e test] [-f namefile] [-F separator] [-m magicfiles] file...\n" \
82: " %s -C -m magicfiles\n"
1.14 tedu 83:
84: #ifndef MAXPATHLEN
85: #define MAXPATHLEN 512
1.1 deraadt 86: #endif
87:
1.14 tedu 88: private int /* Global command-line options */
89: bflag = 0, /* brief output format */
90: nopad = 0, /* Don't pad output */
1.18 chl 91: nobuffer = 0, /* Do not buffer stdout */
92: nulsep = 0; /* Append '\0' to the separator */
1.14 tedu 93:
94: private const char *magicfile = 0; /* where the magic is */
95: private const char *default_magicfile = MAGIC;
1.18 chl 96: private const char *separator = ":"; /* Default field separator */
1.14 tedu 97:
1.19 chl 98: extern char *__progname; /* used throughout */
99:
1.14 tedu 100: private struct magic_set *magic;
101:
102: private void unwrap(char *);
103: private void usage(void);
104: private void help(void);
1.1 deraadt 105:
1.14 tedu 106: int main(int, char *[]);
107: private void process(const char *, int);
108: private void load(const char *, int);
1.1 deraadt 109:
110:
111: /*
112: * main - parse arguments and handle options
113: */
114: int
1.13 deraadt 115: main(int argc, char *argv[])
1.1 deraadt 116: {
1.19 chl 117: int c;
118: size_t i;
1.14 tedu 119: int action = 0, didsomefiles = 0, errflg = 0;
120: int flags = 0;
121: char *home, *usermagic;
122: struct stat sb;
1.18 chl 123: static const char hmagic[] = "/.magic";
1.19 chl 124: #define OPTSTRING "bcCde:f:F:hikLm:nNprsvz0"
1.14 tedu 125: int longindex;
1.18 chl 126: static const struct option long_options[] =
1.14 tedu 127: {
1.19 chl 128: #define OPT(shortname, longname, opt, doc) \
129: {longname, opt, NULL, shortname},
130: #define OPT_LONGONLY(longname, opt, doc) \
131: {longname, opt, NULL, 0},
132: #include "file_opts.h"
133: #undef OPT
134: #undef OPT_LONGONLY
135: {0, 0, NULL, 0}
136: };
1.14 tedu 137:
1.18 chl 138: static const struct {
139: const char *name;
140: int value;
141: } nv[] = {
142: { "apptype", MAGIC_NO_CHECK_APPTYPE },
143: { "ascii", MAGIC_NO_CHECK_ASCII },
144: { "compress", MAGIC_NO_CHECK_COMPRESS },
145: { "elf", MAGIC_NO_CHECK_ELF },
146: { "soft", MAGIC_NO_CHECK_SOFT },
147: { "tar", MAGIC_NO_CHECK_TAR },
148: { "tokens", MAGIC_NO_CHECK_TOKENS },
149: };
150:
151: /* makes islower etc work for other langs */
152: (void)setlocale(LC_CTYPE, "");
1.1 deraadt 153:
1.14 tedu 154: #ifdef __EMX__
155: /* sh-like wildcard expansion! Shouldn't hurt at least ... */
156: _wildcard(&argc, &argv);
157: #endif
1.1 deraadt 158:
1.14 tedu 159: magicfile = default_magicfile;
160: if ((usermagic = getenv("MAGIC")) != NULL)
161: magicfile = usermagic;
162: else
163: if ((home = getenv("HOME")) != NULL) {
1.18 chl 164: size_t len = strlen(home) + sizeof(hmagic);
1.14 tedu 165: if ((usermagic = malloc(len)) != NULL) {
166: (void)strlcpy(usermagic, home, len);
1.18 chl 167: (void)strlcat(usermagic, hmagic, len);
1.14 tedu 168: if (stat(usermagic, &sb)<0)
169: free(usermagic);
170: else
171: magicfile = usermagic;
172: }
173: }
174:
1.18 chl 175: #ifdef S_IFLNK
176: flags |= getenv("POSIXLY_CORRECT") ? MAGIC_SYMLINK : 0;
177: #endif
1.14 tedu 178: while ((c = getopt_long(argc, argv, OPTSTRING, long_options,
179: &longindex)) != -1)
1.1 deraadt 180: switch (c) {
1.14 tedu 181: case 0 :
1.19 chl 182: switch (longindex) {
183: case 0:
1.14 tedu 184: help();
1.19 chl 185: break;
186: case 10:
187: flags |= MAGIC_MIME_TYPE;
188: break;
189: case 11:
190: flags |= MAGIC_MIME_ENCODING;
191: break;
192: }
1.14 tedu 193: break;
1.18 chl 194: case '0':
195: nulsep = 1;
196: break;
1.9 millert 197: case 'b':
1.19 chl 198: bflag++;
1.9 millert 199: break;
1.1 deraadt 200: case 'c':
1.14 tedu 201: action = FILE_CHECK;
202: break;
203: case 'C':
204: action = FILE_COMPILE;
1.1 deraadt 205: break;
206: case 'd':
1.14 tedu 207: flags |= MAGIC_DEBUG|MAGIC_CHECK;
1.1 deraadt 208: break;
1.18 chl 209: case 'e':
210: for (i = 0; i < sizeof(nv) / sizeof(nv[0]); i++)
211: if (strcmp(nv[i].name, optarg) == 0)
212: break;
213:
214: if (i == sizeof(nv) / sizeof(nv[0]))
215: errflg++;
216: else
217: flags |= nv[i].value;
218: break;
219:
1.1 deraadt 220: case 'f':
1.14 tedu 221: if(action)
222: usage();
223: load(magicfile, flags);
1.1 deraadt 224: unwrap(optarg);
225: ++didsomefiles;
226: break;
1.14 tedu 227: case 'F':
228: separator = optarg;
229: break;
1.19 chl 230: case 'i':
231: flags |= MAGIC_MIME;
232: break;
1.14 tedu 233: case 'k':
234: flags |= MAGIC_CONTINUE;
1.1 deraadt 235: break;
236: case 'm':
237: magicfile = optarg;
238: break;
1.14 tedu 239: case 'n':
240: ++nobuffer;
241: break;
242: case 'N':
243: ++nopad;
244: break;
245: #if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
246: case 'p':
247: flags |= MAGIC_PRESERVE_ATIME;
248: break;
249: #endif
250: case 'r':
251: flags |= MAGIC_RAW;
252: break;
253: case 's':
254: flags |= MAGIC_DEVICES;
255: break;
256: case 'v':
1.19 chl 257: (void)fprintf(stderr, "%s-%d.%.2d\n", __progname,
1.14 tedu 258: FILE_VERSION_MAJOR, patchlevel);
1.19 chl 259: (void)fprintf(stderr, "magic file from %s\n",
1.14 tedu 260: magicfile);
261: return 1;
1.1 deraadt 262: case 'z':
1.14 tedu 263: flags |= MAGIC_COMPRESS;
1.1 deraadt 264: break;
1.14 tedu 265: #ifdef S_IFLNK
266: case 'L':
267: flags |= MAGIC_SYMLINK;
268: break;
1.18 chl 269: case 'h':
270: flags &= ~MAGIC_SYMLINK;
271: break;
1.14 tedu 272: #endif
1.1 deraadt 273: case '?':
274: default:
275: errflg++;
276: break;
277: }
278:
279: if (errflg) {
1.14 tedu 280: usage();
1.1 deraadt 281: }
282:
1.14 tedu 283: switch(action) {
284: case FILE_CHECK:
285: case FILE_COMPILE:
286: magic = magic_open(flags|MAGIC_CHECK);
287: if (magic == NULL) {
1.18 chl 288: (void)fprintf(stderr, "%s: %s\n", __progname,
1.14 tedu 289: strerror(errno));
290: return 1;
291: }
292: c = action == FILE_CHECK ? magic_check(magic, magicfile) :
293: magic_compile(magic, magicfile);
294: if (c == -1) {
1.18 chl 295: (void)fprintf(stderr, "%s: %s\n", __progname,
1.14 tedu 296: magic_error(magic));
297: return -1;
298: }
299: return 0;
300: default:
301: load(magicfile, flags);
302: break;
1.1 deraadt 303: }
304:
305: if (optind == argc) {
1.7 deraadt 306: if (!didsomefiles) {
1.14 tedu 307: usage();
1.7 deraadt 308: }
1.14 tedu 309: }
310: else {
1.19 chl 311: size_t j, wid, nw;
312: for (wid = 0, j = (size_t)optind; j < (size_t)argc; j++) {
313: nw = file_mbswidth(argv[j]);
1.1 deraadt 314: if (nw > wid)
315: wid = nw;
316: }
1.19 chl 317: /*
318: * If bflag is only set twice, set it depending on
319: * number of files [this is undocumented, and subject to change]
320: */
321: if (bflag == 2) {
322: bflag = optind >= argc - 1;
323: }
1.1 deraadt 324: for (; optind < argc; optind++)
325: process(argv[optind], wid);
326: }
327:
1.19 chl 328: c = magic->haderr ? 1 : 0;
1.18 chl 329: magic_close(magic);
1.19 chl 330: return c;
1.1 deraadt 331: }
332:
333:
1.14 tedu 334: private void
1.18 chl 335: /*ARGSUSED*/
1.14 tedu 336: load(const char *m, int flags)
337: {
1.18 chl 338: if (magic || m == NULL)
1.14 tedu 339: return;
340: magic = magic_open(flags);
341: if (magic == NULL) {
1.18 chl 342: (void)fprintf(stderr, "%s: %s\n", __progname, strerror(errno));
1.14 tedu 343: exit(1);
344: }
345: if (magic_load(magic, magicfile) == -1) {
346: (void)fprintf(stderr, "%s: %s\n",
1.18 chl 347: __progname, magic_error(magic));
1.14 tedu 348: exit(1);
349: }
350: }
351:
1.1 deraadt 352: /*
353: * unwrap -- read a file of filenames, do each one.
354: */
1.14 tedu 355: private void
356: unwrap(char *fn)
1.1 deraadt 357: {
358: char buf[MAXPATHLEN];
359: FILE *f;
360: int wid = 0, cwid;
361:
1.5 millert 362: if (strcmp("-", fn) == 0) {
363: f = stdin;
364: wid = 1;
365: } else {
366: if ((f = fopen(fn, "r")) == NULL) {
1.14 tedu 367: (void)fprintf(stderr, "%s: Cannot open `%s' (%s).\n",
1.18 chl 368: __progname, fn, strerror(errno));
1.14 tedu 369: exit(1);
1.5 millert 370: }
371:
1.15 otto 372: while (fgets(buf, sizeof(buf), f) != NULL) {
1.18 chl 373: buf[strcspn(buf, "\n")] = '\0';
374: cwid = file_mbswidth(buf);
1.5 millert 375: if (cwid > wid)
376: wid = cwid;
377: }
1.1 deraadt 378:
1.5 millert 379: rewind(f);
1.1 deraadt 380: }
381:
1.15 otto 382: while (fgets(buf, sizeof(buf), f) != NULL) {
1.18 chl 383: buf[strcspn(buf, "\n")] = '\0';
1.1 deraadt 384: process(buf, wid);
1.14 tedu 385: if(nobuffer)
1.18 chl 386: (void)fflush(stdout);
1.1 deraadt 387: }
388:
1.18 chl 389: (void)fclose(f);
1.1 deraadt 390: }
391:
1.18 chl 392: /*
393: * Called for each input file on the command line (or in a list of files)
394: */
1.14 tedu 395: private void
396: process(const char *inname, int wid)
397: {
398: const char *type;
399: int std_in = strcmp(inname, "-") == 0;
400:
1.18 chl 401: if (wid > 0 && !bflag) {
402: (void)printf("%s", std_in ? "/dev/stdin" : inname);
403: if (nulsep)
404: (void)putc('\0', stdout);
405: else
406: (void)printf("%s", separator);
407: (void)printf("%*s ",
408: (int) (nopad ? 0 : (wid - file_mbswidth(inname))), "");
409: }
1.14 tedu 410:
411: type = magic_file(magic, std_in ? NULL : inname);
412: if (type == NULL)
1.18 chl 413: (void)printf("ERROR: %s\n", magic_error(magic));
1.14 tedu 414: else
1.18 chl 415: (void)printf("%s\n", type);
1.14 tedu 416: }
417:
418: size_t
419: file_mbswidth(const char *s)
1.1 deraadt 420: {
1.18 chl 421: #if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH)
1.14 tedu 422: size_t bytesconsumed, old_n, n, width = 0;
423: mbstate_t state;
424: wchar_t nextchar;
425: (void)memset(&state, 0, sizeof(mbstate_t));
426: old_n = n = strlen(s);
1.23 ! stsp 427: int w;
1.14 tedu 428:
429: while (n > 0) {
430: bytesconsumed = mbrtowc(&nextchar, s, n, &state);
431: if (bytesconsumed == (size_t)(-1) ||
432: bytesconsumed == (size_t)(-2)) {
433: /* Something went wrong, return something reasonable */
434: return old_n;
1.1 deraadt 435: }
1.14 tedu 436: if (s[0] == '\n') {
437: /*
438: * do what strlen() would do, so that caller
439: * is always right
440: */
441: width++;
1.23 ! stsp 442: } else {
! 443: w = wcwidth(nextchar);
! 444: if (w > 0)
! 445: width += w;
! 446: }
1.1 deraadt 447:
1.14 tedu 448: s += bytesconsumed, n -= bytesconsumed;
1.1 deraadt 449: }
1.14 tedu 450: return width;
451: #else
452: return strlen(s);
1.5 millert 453: #endif
1.14 tedu 454: }
1.1 deraadt 455:
1.14 tedu 456: private void
457: usage(void)
458: {
1.18 chl 459: (void)fprintf(stderr, USAGE, __progname, __progname);
1.14 tedu 460: (void)fputs("Try `file --help' for more information.\n", stderr);
461: exit(1);
1.1 deraadt 462: }
463:
1.14 tedu 464: private void
465: help(void)
1.1 deraadt 466: {
1.19 chl 467: (void)fputs(
468: "Usage: file [OPTION...] [FILE...]\n"
469: "Determine type of FILEs.\n"
470: "\n", stderr);
471: #define OPT(shortname, longname, opt, doc) \
472: fprintf(stderr, " -%c, --" longname doc, shortname);
473: #define OPT_LONGONLY(longname, opt, doc) \
474: fprintf(stderr, " --" longname doc);
475: #include "file_opts.h"
476: #undef OPT
477: #undef OPT_LONGONLY
1.14 tedu 478: exit(0);
1.1 deraadt 479: }