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