Annotation of src/usr.bin/file/file.c, Revision 1.14
1.14 ! tedu 1: /* $OpenBSD$ */
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
! 76: FILE_RCSID("@(#)$Id: file.c,v 1.92 2004/03/22 21:34:39 christos Exp $")
! 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.14 ! tedu 356: while (fgets(buf, MAXPATHLEN, f) != NULL) {
! 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.14 ! tedu 365: while (fgets(buf, MAXPATHLEN, f) != NULL) {
! 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