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