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