file/src/file.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) Ian F. Darwin 1986-1995.
00003  * Software written by Ian F. Darwin and others;
00004  * maintained 1995-present by Christos Zoulas and others.
00005  * 
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  * 1. Redistributions of source code must retain the above copyright
00010  *    notice immediately at the beginning of the file, without modification,
00011  *    this list of conditions, and the following disclaimer.
00012  * 2. Redistributions in binary form must reproduce the above copyright
00013  *    notice, this list of conditions and the following disclaimer in the
00014  *    documentation and/or other materials provided with the distribution.
00015  *  
00016  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
00017  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00018  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00019  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
00020  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00021  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00022  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00023  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00024  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00025  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00026  * SUCH DAMAGE.
00027  */
00028 /*
00029  * file - find type of a file or files - main program.
00030  */
00031 
00032 #include "file.h"
00033 #include "magic.h"
00034 
00035 #include <stdio.h>
00036 #include <stdlib.h>
00037 #include <unistd.h>
00038 #include <string.h>
00039 #include <sys/types.h>
00040 #include <sys/param.h>  /* for MAXPATHLEN */
00041 #include <sys/stat.h>
00042 #ifdef RESTORE_TIME
00043 # if (__COHERENT__ >= 0x420)
00044 #  include <sys/utime.h>
00045 # else
00046 #  ifdef USE_UTIMES
00047 #   include <sys/time.h>
00048 #  else
00049 #   include <utime.h>
00050 #  endif
00051 # endif
00052 #endif
00053 #ifdef HAVE_UNISTD_H
00054 #include <unistd.h>     /* for read() */
00055 #endif
00056 #ifdef HAVE_LOCALE_H
00057 #include <locale.h>
00058 #endif
00059 #ifdef HAVE_WCHAR_H
00060 #include <wchar.h>
00061 #endif
00062 
00063 #ifdef HAVE_GETOPT_H
00064 #include <getopt.h>     /* for long options (is this portable?)*/
00065 #else
00066 #undef HAVE_GETOPT_LONG
00067 #endif
00068 
00069 #include <netinet/in.h>         /* for byte swapping */
00070 
00071 #include "patchlevel.h"
00072 
00073 #ifndef lint
00074 FILE_RCSID("@(#)$File: file.c,v 1.111 2007/05/08 14:44:18 christos Exp $")
00075 #endif  /* lint */
00076 
00077 
00078 #ifdef S_IFLNK
00079 #define SYMLINKFLAG "Lh"
00080 #else
00081 #define SYMLINKFLAG ""
00082 #endif
00083 
00084 # define USAGE  "Usage: %s [-bcik" SYMLINKFLAG "nNrsvz0] [-e test] [-f namefile] [-F separator] [-m magicfiles] file...\n       %s -C -m magicfiles\n"
00085 
00086 #ifndef MAXPATHLEN
00087 #define MAXPATHLEN      512
00088 #endif
00089 
00090 /*@unchecked@*/
00091 private int             /* Global command-line options          */
00092         bflag = 0,      /* brief output format                  */
00093         nopad = 0,      /* Don't pad output                     */
00094         nobuffer = 0,   /* Do not buffer stdout                 */
00095         nulsep = 0;     /* Append '\0' to the separator         */
00096 
00097 /*@unchecked@*/ /*@observer@*/ /*@relnull@*/
00098 private const char *magicfile = 0;      /* where the magic is   */
00099 /*@unchecked@*/ /*@observer@*/
00100 private const char *default_magicfile = MAGIC;
00101 /*@unchecked@*/ /*@observer@*/
00102 private const char *separator = ":";    /* Default field separator      */
00103 
00104 /*@unchecked@*/ /*@null@*/
00105 private char *progname;         /* used throughout              */
00106 
00107 /*@unchecked@*/ /*@only@*/ /*@null@*/
00108 private struct magic_set *magic;
00109 
00110 private void unwrap(char *)
00111         /*@globals fileSystem, internalState @*/
00112         /*@modifies fileSystem, internalState @*/;
00113 /*@exits@*/
00114 private void usage(void)
00115         /*@globals fileSystem @*/
00116         /*@modifies fileSystem @*/;
00117 #ifdef HAVE_GETOPT_LONG
00118 /*@exits@*/
00119 private void help(void)
00120         /*@globals fileSystem @*/
00121         /*@modifies fileSystem @*/;
00122 #endif
00123 #if 0
00124 private int byteconv4(int, int, int);
00125 private short byteconv2(int, int, int);
00126 #endif
00127 
00128 int main(int argc, char *argv[])
00129         /*@globals bflag, default_magicfile, magic, magicfile, nobuffer,
00130                 nopad, nulsep, optind, progname, separator, fileSystem, internalState @*/
00131         /*@modifies argv, bflag, default_magicfile, magic, magicfile, nobuffer,
00132                 nopad, nulsep, optind, progname, separator, fileSystem, internalState @*/;
00133 private void process(const char *, int)
00134         /*@globals fileSystem, internalState @*/
00135         /*@modifies fileSystem, internalState @*/;
00136 private void load(const char *m, int flags)
00137         /*@globals magic, fileSystem, internalState @*/
00138         /*@modifies magic, fileSystem, internalState @*/;
00139 
00140 /*
00141  * main - parse arguments and handle options
00142  */
00143 int
00144 main(int argc, char *argv[])
00145 {
00146         int c, i;
00147         int action = 0, didsomefiles = 0, errflg = 0;
00148         int flags = 0;
00149         char *home, *usermagic;
00150         struct stat sb;
00151         /*@observer@*/
00152         static const char hmagic[] = "/.magic";
00153 #define OPTSTRING       "bcCde:f:F:hikLm:nNprsvz0"
00154 #ifdef HAVE_GETOPT_LONG
00155         int longindex;
00156 /*@-nullassign -readonlytrans @*/
00157         /*@observer@*/
00158         static const struct option long_options[] =
00159         {
00160                 {"version", 0, 0, 'v'},
00161                 {"help", 0, 0, 0},
00162                 {"brief", 0, 0, 'b'},
00163                 {"checking-printout", 0, 0, 'c'},
00164                 {"debug", 0, 0, 'd'},
00165                 {"exclude", 1, 0, 'e' },
00166                 {"files-from", 1, 0, 'f'},
00167                 {"separator", 1, 0, 'F'},
00168                 {"mime", 0, 0, 'i'},
00169                 {"keep-going", 0, 0, 'k'},
00170 #ifdef S_IFLNK
00171                 {"dereference", 0, 0, 'L'},
00172                 {"no-dereference", 0, 0, 'h'},
00173 #endif
00174                 {"magic-file", 1, 0, 'm'},
00175 #if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
00176                 {"preserve-date", 0, 0, 'p'},
00177 #endif
00178                 {"uncompress", 0, 0, 'z'},
00179                 {"raw", 0, 0, 'r'},
00180                 {"no-buffer", 0, 0, 'n'},
00181                 {"no-pad", 0, 0, 'N'},
00182                 {"special-files", 0, 0, 's'},
00183                 {"compile", 0, 0, 'C'},
00184                 {"print0", 0, 0, '0'},
00185                 {0, 0, 0, 0},
00186         };
00187 #endif
00188 /*@=nullassign =readonlytrans@*/
00189 
00190         /*@observer@*/
00191         static const struct {
00192         /*@observer@*/
00193                 const char *name;
00194                 int value;
00195         } nv[] = {
00196                 { "apptype",    MAGIC_NO_CHECK_APPTYPE },
00197                 { "ascii",      MAGIC_NO_CHECK_ASCII },
00198                 { "compress",   MAGIC_NO_CHECK_COMPRESS },
00199                 { "elf",        MAGIC_NO_CHECK_ELF },
00200                 { "fortran",    MAGIC_NO_CHECK_FORTRAN },
00201                 { "soft",       MAGIC_NO_CHECK_SOFT },
00202                 { "tar",        MAGIC_NO_CHECK_TAR },
00203                 { "tokens",     MAGIC_NO_CHECK_TOKENS },
00204                 { "troff",      MAGIC_NO_CHECK_TROFF },
00205         };
00206 
00207 #ifdef LC_CTYPE
00208         /* makes islower etc work for other langs */
00209         (void)setlocale(LC_CTYPE, "");
00210 #endif
00211 
00212 #ifdef __EMX__
00213         /* sh-like wildcard expansion! Shouldn't hurt at least ... */
00214         _wildcard(&argc, &argv);
00215 #endif
00216 
00217         if ((progname = strrchr(argv[0], '/')) != NULL)
00218                 progname++;
00219         else
00220                 progname = argv[0];
00221 
00222         magicfile = default_magicfile;
00223         if ((usermagic = getenv("MAGIC")) != NULL)
00224                 magicfile = usermagic;
00225         else
00226                 if ((home = getenv("HOME")) != NULL) {
00227                         if ((usermagic = malloc(strlen(home)
00228                             + sizeof(hmagic))) != NULL) {
00229                                 (void)strcpy(usermagic, home);
00230                                 (void)strcat(usermagic, hmagic);
00231                                 if (stat(usermagic, &sb)<0) 
00232                                         free(usermagic);
00233                                 else
00234                                         magicfile = usermagic;
00235                         }
00236                 }
00237 
00238 #ifdef S_IFLNK
00239         flags |= getenv("POSIXLY_CORRECT") ? MAGIC_SYMLINK : 0;
00240 #endif
00241 #ifndef HAVE_GETOPT_LONG
00242         while ((c = getopt(argc, argv, OPTSTRING)) != -1)
00243 #else
00244 /*@i@*/ while ((c = getopt_long(argc, argv, OPTSTRING, long_options,
00245             &longindex)) != -1)
00246 #endif
00247                 switch (c) {
00248 #ifdef HAVE_GETOPT_LONG
00249                 case 0 :
00250                         if (longindex == 1)
00251                                 help();
00252                         /*@switchbreak@*/ break;
00253 #endif
00254                 case '0':
00255                         nulsep = 1;
00256                         /*@switchbreak@*/ break;
00257                 case 'b':
00258                         ++bflag;
00259                         /*@switchbreak@*/ break;
00260                 case 'c':
00261                         action = FILE_CHECK;
00262                         /*@switchbreak@*/ break;
00263                 case 'C':
00264                         action = FILE_COMPILE;
00265                         /*@switchbreak@*/ break;
00266                 case 'd':
00267                         flags |= MAGIC_DEBUG|MAGIC_CHECK;
00268                         /*@switchbreak@*/ break;
00269                 case 'e':
00270                         for (i = 0; i < sizeof(nv) / sizeof(nv[0]); i++)
00271                                 if (strcmp(nv[i].name, optarg) == 0)
00272                                         /*@innerbreak@*/ break;
00273 
00274                         if (i == sizeof(nv) / sizeof(nv[0]))
00275                                 errflg++;
00276                         else
00277                                 flags |= nv[i].value;
00278                         /*@switchbreak@*/ break;
00279                         
00280                 case 'f':
00281                         if(action)
00282                                 usage();
00283                         load(magicfile, flags);
00284                         unwrap(optarg);
00285                         ++didsomefiles;
00286                         /*@switchbreak@*/ break;
00287                 case 'F':
00288                         separator = optarg;
00289                         /*@switchbreak@*/ break;
00290                 case 'i':
00291                         flags |= MAGIC_MIME;
00292                         /*@switchbreak@*/ break;
00293                 case 'k':
00294                         flags |= MAGIC_CONTINUE;
00295                         /*@switchbreak@*/ break;
00296                 case 'm':
00297                         magicfile = optarg;
00298                         /*@switchbreak@*/ break;
00299                 case 'n':
00300                         ++nobuffer;
00301                         /*@switchbreak@*/ break;
00302                 case 'N':
00303                         ++nopad;
00304                         /*@switchbreak@*/ break;
00305 #if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
00306                 case 'p':
00307                         flags |= MAGIC_PRESERVE_ATIME;
00308                         /*@switchbreak@*/ break;
00309 #endif
00310                 case 'r':
00311                         flags |= MAGIC_RAW;
00312                         /*@switchbreak@*/ break;
00313                 case 's':
00314                         flags |= MAGIC_DEVICES;
00315                         /*@switchbreak@*/ break;
00316                 case 'v':
00317                         (void)fprintf(stdout, "%s-%d.%.2d\n", progname,
00318                                        FILE_VERSION_MAJOR, patchlevel);
00319                         (void)fprintf(stdout, "magic file from %s\n",
00320                                        magicfile);
00321                         return 1;
00322                 case 'z':
00323                         flags |= MAGIC_COMPRESS;
00324                         /*@switchbreak@*/ break;
00325 #ifdef S_IFLNK
00326                 case 'L':
00327                         flags |= MAGIC_SYMLINK;
00328                         /*@switchbreak@*/ break;
00329                 case 'h':
00330                         flags &= ~MAGIC_SYMLINK;
00331                         /*@switchbreak@*/ break;
00332 #endif
00333                 case '?':
00334                 default:
00335                         errflg++;
00336                         /*@switchbreak@*/ break;
00337                 }
00338 
00339         if (errflg) {
00340                 usage();
00341         }
00342 
00343         switch(action) {
00344         case FILE_CHECK:
00345         case FILE_COMPILE:
00346                 magic = magic_open(flags|MAGIC_CHECK);
00347                 if (magic == NULL) {
00348                         (void)fprintf(stderr, "%s: %s\n", progname,
00349                             strerror(errno));
00350                         return 1;
00351                 }
00352                 c = action == FILE_CHECK ? magic_check(magic, magicfile) :
00353                     magic_compile(magic, magicfile);
00354                 if (c == -1) {
00355                         (void)fprintf(stderr, "%s: %s\n", progname,
00356                             magic_error(magic));
00357                         return -1;
00358                 }
00359                 return 0;
00360         default:
00361                 load(magicfile, flags);
00362                 break;
00363         }
00364 
00365         if (optind == argc) {
00366                 if (!didsomefiles) {
00367                         usage();
00368                 }
00369         }
00370         else {
00371                 int i, wid, nw;
00372                 for (wid = 0, i = optind; i < argc; i++) {
00373                         nw = file_mbswidth(argv[i]);
00374                         if (nw > wid)
00375                                 wid = nw;
00376                 }
00377                 for (; optind < argc; optind++)
00378                         process(argv[optind], wid);
00379         }
00380 
00381         magic_close(magic);
00382         return 0;
00383 }
00384 
00385 
00386 private void
00387 load(/*@unused@*/ const char *m f__unused, int flags)
00388 {
00389         if (magic)
00390                 return;
00391         magic = magic_open(flags);
00392         if (magic == NULL) {
00393                 (void)fprintf(stderr, "%s: %s\n", progname, strerror(errno));
00394                 exit(EXIT_FAILURE);
00395         }
00396         if (magic_load(magic, magicfile) == -1) {
00397                 (void)fprintf(stderr, "%s: %s\n",
00398                     progname, magic_error(magic));
00399                 exit(EXIT_FAILURE);
00400         }
00401 }
00402 
00403 /*
00404  * unwrap -- read a file of filenames, do each one.
00405  */
00406 private void
00407 unwrap(char *fn)
00408 {
00409         char buf[MAXPATHLEN];
00410         FILE *f;
00411         int wid = 0, cwid;
00412         size_t len;
00413 
00414         if (strcmp("-", fn) == 0) {
00415                 f = stdin;
00416                 wid = 1;
00417         } else {
00418                 if ((f = fopen(fn, "r")) == NULL) {
00419                         (void)fprintf(stderr, "%s: Cannot open `%s' (%s).\n",
00420                             progname, fn, strerror(errno));
00421                         exit(EXIT_FAILURE);
00422                 }
00423 
00424                 while (fgets(buf, MAXPATHLEN, f) != NULL) {
00425                         len = strlen(buf);
00426                         if (len > 0 && buf[len - 1] == '\n')
00427                                 buf[len - 1] = '\0';
00428                         cwid = file_mbswidth(buf);
00429                         if (cwid > wid)
00430                                 wid = cwid;
00431                 }
00432 
00433                 rewind(f);
00434         }
00435 
00436         while (fgets(buf, MAXPATHLEN, f) != NULL) {
00437                 len = strlen(buf);
00438                 if (len > 0 && buf[len - 1] == '\n')
00439                         buf[len - 1] = '\0';
00440                 process(buf, wid);
00441                 if(nobuffer)
00442                         (void)fflush(stdout);
00443         }
00444 
00445         (void)fclose(f);
00446 }
00447 
00448 /*
00449  * Called for each input file on the command line (or in a list of files)
00450  */
00451 private void
00452 process(const char *inname, int wid)
00453 {
00454         const char *type;
00455         int std_in = strcmp(inname, "-") == 0;
00456 
00457         if (wid > 0 && !bflag) {
00458                 (void)printf("%s", std_in ? "/dev/stdin" : inname);
00459                 if (nulsep)
00460                         (void)putc('\0', stdout);
00461                 else
00462                         (void)printf("%s", separator);
00463                 (void)printf("%*s ",
00464                     (int) (nopad ? 0 : (wid - file_mbswidth(inname))), "");
00465         }
00466 
00467         type = magic_file(magic, std_in ? NULL : inname);
00468         if (type == NULL)
00469                 (void)printf("ERROR: %s\n", magic_error(magic));
00470         else
00471                 (void)printf("%s\n", type);
00472 }
00473 
00474 
00475 #if 0
00476 /*
00477  * byteconv4
00478  * Input:
00479  *      from            4 byte quantity to convert
00480  *      same            whether to perform byte swapping
00481  *      big_endian      whether we are a big endian host
00482  */
00483 private int
00484 byteconv4(int from, int same, int big_endian)
00485 {
00486         if (same)
00487                 return from;
00488         else if (big_endian) {          /* lsb -> msb conversion on msb */
00489                 union {
00490                         int i;
00491                         char c[4];
00492                 } retval, tmpval;
00493 
00494                 tmpval.i = from;
00495                 retval.c[0] = tmpval.c[3];
00496                 retval.c[1] = tmpval.c[2];
00497                 retval.c[2] = tmpval.c[1];
00498                 retval.c[3] = tmpval.c[0];
00499 
00500                 return retval.i;
00501         }
00502         else
00503                 return ntohl(from);     /* msb -> lsb conversion on lsb */
00504 }
00505 
00506 /*
00507  * byteconv2
00508  * Same as byteconv4, but for shorts
00509  */
00510 private short
00511 byteconv2(int from, int same, int big_endian)
00512 {
00513         if (same)
00514                 return from;
00515         else if (big_endian) {          /* lsb -> msb conversion on msb */
00516                 union {
00517                         short s;
00518                         char c[2];
00519                 } retval, tmpval;
00520 
00521                 tmpval.s = (short) from;
00522                 retval.c[0] = tmpval.c[1];
00523                 retval.c[1] = tmpval.c[0];
00524 
00525                 return retval.s;
00526         }
00527         else
00528                 return ntohs(from);     /* msb -> lsb conversion on lsb */
00529 }
00530 #endif
00531 
00532 size_t
00533 file_mbswidth(const char *s)
00534 {
00535 #if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH)
00536         size_t bytesconsumed, old_n, n, width = 0;
00537         mbstate_t state;
00538         wchar_t nextchar;
00539         (void)memset(&state, 0, sizeof(mbstate_t));
00540         old_n = n = strlen(s);
00541 
00542         while (n > 0) {
00543                 bytesconsumed = mbrtowc(&nextchar, s, n, &state);
00544                 if (bytesconsumed == (size_t)(-1) ||
00545                     bytesconsumed == (size_t)(-2)) {
00546                         /* Something went wrong, return something reasonable */
00547                         return old_n;
00548                 }
00549                 if (s[0] == '\n') {
00550                         /*
00551                          * do what strlen() would do, so that caller
00552                          * is always right
00553                          */
00554                         width++;
00555                 } else
00556                         width += wcwidth(nextchar);
00557 
00558                 s += bytesconsumed, n -= bytesconsumed;
00559         }
00560         return width;
00561 #else
00562         return strlen(s);
00563 #endif
00564 }
00565 
00566 private void
00567 usage(void)
00568 {
00569         (void)fprintf(stderr, USAGE, progname, progname);
00570 #ifdef HAVE_GETOPT_LONG
00571         (void)fputs("Try `file --help' for more information.\n", stderr);
00572 #endif
00573         exit(EXIT_FAILURE);
00574 }
00575 
00576 #ifdef HAVE_GETOPT_LONG
00577 private void
00578 help(void)
00579 {
00580         (void)puts(
00581 "Usage: file [OPTION]... [FILE]...\n"
00582 "Determine file type of FILEs.\n"
00583 "\n"
00584 "  -m, --magic-file LIST      use LIST as a colon-separated list of magic\n"
00585 "                               number files\n"
00586 "  -z, --uncompress           try to look inside compressed files\n"
00587 "  -b, --brief                do not prepend filenames to output lines\n"
00588 "  -c, --checking-printout    print the parsed form of the magic file, use in\n"
00589 "                               conjunction with -m to debug a new magic file\n"
00590 "                               before installing it\n"
00591 "  -e, --exclude              exclude test from the list of test to be\n"
00592 "                               performed for file. Valid tests are:\n"
00593 "                               ascii, apptype, elf, compress, soft, tar\n"
00594 "  -f, --files-from FILE      read the filenames to be examined from FILE\n"
00595 "  -F, --separator string     use string as separator instead of `:'\n"
00596 "  -i, --mime                 output mime type strings\n"
00597 "  -k, --keep-going           don't stop at the first match\n"
00598 "  -L, --dereference          causes symlinks to be followed\n"
00599 "  -n, --no-buffer            do not buffer output\n"
00600 "  -N, --no-pad               do not pad output\n"
00601 "  -p, --preserve-date        preserve access times on files\n"
00602 "  -r, --raw                  don't translate unprintable chars to \\ooo\n"
00603 "  -s, --special-files        treat special (block/char devices) files as\n"
00604 "                             ordinary ones\n"
00605 "or\n"
00606 "      --help                 display this help and exit\n"
00607 "or\n"
00608 "      --version              output version information and exit\n"
00609 "or\n"
00610 "  -C, --compile              compile file specified by -m\n"
00611 );
00612         exit(EXIT_SUCCESS);
00613 }
00614 #endif

Generated on Fri Sep 7 01:07:22 2007 for rpm by  doxygen 1.5.1