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 #include <fcntl.h>      /* for open() */
00043 #ifdef RESTORE_TIME
00044 # if (__COHERENT__ >= 0x420)
00045 #  include <sys/utime.h>
00046 # else
00047 #  ifdef USE_UTIMES
00048 #   include <sys/time.h>
00049 #  else
00050 #   include <utime.h>
00051 #  endif
00052 # endif
00053 #endif
00054 #ifdef HAVE_UNISTD_H
00055 #include <unistd.h>     /* for read() */
00056 #endif
00057 #ifdef HAVE_LOCALE_H
00058 #include <locale.h>
00059 #endif
00060 #ifdef HAVE_WCHAR_H
00061 #include <wchar.h>
00062 #endif
00063 
00064 #ifdef HAVE_GETOPT_H
00065 #include <getopt.h>     /* for long options (is this portable?)*/
00066 #else
00067 #undef HAVE_GETOPT_LONG
00068 #endif
00069 
00070 #include <netinet/in.h>         /* for byte swapping */
00071 
00072 #include "patchlevel.h"
00073 
00074 #ifndef lint
00075 FILE_RCSID("@(#)$Id: file.c,v 1.96 2005/03/06 05:58:22 christos Exp $")
00076 #endif  /* lint */
00077 
00078 
00079 #ifdef S_IFLNK
00080 #define SYMLINKFLAG "L"
00081 #else
00082 #define SYMLINKFLAG ""
00083 #endif
00084 
00085 # define USAGE  "Usage: %s [-bcik" SYMLINKFLAG "nNsvz] [-f namefile] [-F separator] [-m magicfiles] file...\n       %s -C -m magicfiles\n"
00086 
00087 #ifndef MAXPATHLEN
00088 #define MAXPATHLEN      512
00089 #endif
00090 
00091 /*@unchecked@*/
00092 private int             /* Global command-line options          */
00093         bflag = 0,      /* brief output format                  */
00094         nopad = 0,      /* Don't pad output                     */
00095         nobuffer = 0;   /* Do not buffer stdout                 */
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 *fn)
00111         /*@globals fileSystem, internalState @*/
00112         /*@modifies fileSystem, internalState @*/;
00113 private void usage(void)
00114         /*@globals fileSystem @*/
00115         /*@modifies fileSystem @*/;
00116 #ifdef HAVE_GETOPT_LONG
00117 private void help(void)
00118         /*@globals fileSystem @*/
00119         /*@modifies fileSystem @*/;
00120 #endif
00121 #if 0
00122 private int byteconv4(int, int, int)
00123         /*@*/;
00124 private short byteconv2(int, int, int)
00125         /*@*/;
00126 #endif
00127 
00128 private void process(const char *inname, int wid)
00129         /*@globals magic, fileSystem, internalState @*/
00130         /*@modifies magic, fileSystem, internalState @*/;
00131 private void load(const char *m, int flags)
00132         /*@globals magic, fileSystem, internalState @*/
00133         /*@modifies magic, fileSystem, internalState @*/;
00134 
00135 
00136 /*
00137  * main - parse arguments and handle options
00138  */
00139 int
00140 main(int argc, char *argv[])
00141         /*@globals bflag, default_magicfile, magic, magicfile,
00142                 nobuffer, nopad, optind, progname, separator,
00143                 fileSystem, internalState @*/
00144         /*@modifies argv, bflag, default_magicfile, magic, magicfile,
00145                 nobuffer, nopad, optind, progname, separator,
00146                 fileSystem, internalState @*/
00147 {
00148         int c;
00149         int action = 0, didsomefiles = 0, errflg = 0;
00150         int flags = 0;
00151         char *home, *usermagic;
00152         struct stat sb;
00153 #define OPTSTRING       "bcCdf:F:ikLm:nNprsvz"
00154 #ifdef HAVE_GETOPT_LONG
00155         int longindex;
00156 /*@observer@*/
00157 /*@-nullassign -readonlytrans@*/
00158         private 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                 {"files-from", 1, 0, 'f'},
00166                 {"separator", 1, 0, 'F'},
00167                 {"mime", 0, 0, 'i'},
00168                 {"keep-going", 0, 0, 'k'},
00169 #ifdef S_IFLNK
00170                 {"dereference", 0, 0, 'L'},
00171 #endif
00172                 {"magic-file", 1, 0, 'm'},
00173 #if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
00174                 {"preserve-date", 0, 0, 'p'},
00175 #endif
00176                 {"uncompress", 0, 0, 'z'},
00177                 {"raw", 0, 0, 'r'},
00178                 {"no-buffer", 0, 0, 'n'},
00179                 {"no-pad", 0, 0, 'N'},
00180                 {"special-files", 0, 0, 's'},
00181                 {"compile", 0, 0, 'C'},
00182                 {0, 0, 0, 0},
00183         };
00184 /*@=nullassign =readonlytrans@*/
00185 #endif
00186 
00187 #ifdef LC_CTYPE
00188         setlocale(LC_CTYPE, ""); /* makes islower etc work for other langs */
00189 #endif
00190 
00191 #ifdef __EMX__
00192         /* sh-like wildcard expansion! Shouldn't hurt at least ... */
00193         _wildcard(&argc, &argv);
00194 #endif
00195 
00196         if ((progname = strrchr(argv[0], '/')) != NULL)
00197                 progname++;
00198         else
00199                 progname = argv[0];
00200 
00201         magicfile = default_magicfile;
00202         if ((usermagic = getenv("MAGIC")) != NULL)
00203                 magicfile = usermagic;
00204         else
00205                 if ((home = getenv("HOME")) != NULL) {
00206                         if ((usermagic = malloc(strlen(home) + 8)) != NULL) {
00207                                 (void)strcpy(usermagic, home);
00208                                 (void)strcat(usermagic, "/.magic");
00209                                 if (stat(usermagic, &sb)<0) 
00210                                         free(usermagic);
00211                                 else
00212                                         magicfile = usermagic;
00213                         }
00214                 }
00215 
00216 /*@-moduncon@*/
00217 #ifndef HAVE_GETOPT_LONG
00218         while ((c = getopt(argc, argv, OPTSTRING)) != -1)
00219 #else
00220         while ((c = getopt_long(argc, argv, OPTSTRING, long_options,
00221             &longindex)) != -1)
00222 #endif
00223 /*@=moduncon@*/
00224                 switch (c) {
00225 #ifdef HAVE_GETOPT_LONG
00226                 case 0 :
00227                         if (longindex == 1)
00228                                 help();
00229                         /*@switchbreak@*/ break;
00230 #endif
00231                 case 'b':
00232                         ++bflag;
00233                         /*@switchbreak@*/ break;
00234                 case 'c':
00235                         action = FILE_CHECK;
00236                         /*@switchbreak@*/ break;
00237                 case 'C':
00238                         action = FILE_COMPILE;
00239                         /*@switchbreak@*/ break;
00240                 case 'd':
00241                         flags |= MAGIC_DEBUG|MAGIC_CHECK;
00242                         /*@switchbreak@*/ break;
00243                 case 'f':
00244                         if(action)
00245                                 usage();
00246                         load(magicfile, flags);
00247                         unwrap(optarg);
00248                         ++didsomefiles;
00249                         /*@switchbreak@*/ break;
00250                 case 'F':
00251                         separator = optarg;
00252                         /*@switchbreak@*/ break;
00253                 case 'i':
00254                         flags |= MAGIC_MIME;
00255                         /*@switchbreak@*/ break;
00256                 case 'k':
00257                         flags |= MAGIC_CONTINUE;
00258                         /*@switchbreak@*/ break;
00259                 case 'm':
00260                         magicfile = optarg;
00261                         /*@switchbreak@*/ break;
00262                 case 'n':
00263                         ++nobuffer;
00264                         /*@switchbreak@*/ break;
00265                 case 'N':
00266                         ++nopad;
00267                         /*@switchbreak@*/ break;
00268 #if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
00269                 case 'p':
00270                         flags |= MAGIC_PRESERVE_ATIME;
00271                         /*@switchbreak@*/ break;
00272 #endif
00273                 case 'r':
00274                         flags |= MAGIC_RAW;
00275                         /*@switchbreak@*/ break;
00276                 case 's':
00277                         flags |= MAGIC_DEVICES;
00278                         /*@switchbreak@*/ break;
00279                 case 'v':
00280                         (void) fprintf(stdout, "%s-%d.%.2d\n", progname,
00281                                        FILE_VERSION_MAJOR, patchlevel);
00282                         (void) fprintf(stdout, "magic file from %s\n",
00283                                        magicfile);
00284                         return 1;
00285                 case 'z':
00286                         flags |= MAGIC_COMPRESS;
00287                         /*@switchbreak@*/ break;
00288 #ifdef S_IFLNK
00289                 case 'L':
00290                         flags |= MAGIC_SYMLINK;
00291                         /*@switchbreak@*/ break;
00292 #endif
00293                 case '?':
00294                 default:
00295                         errflg++;
00296                         /*@switchbreak@*/ break;
00297                 }
00298 
00299         if (errflg) {
00300                 usage();
00301         }
00302 
00303         switch(action) {
00304         case FILE_CHECK:
00305         case FILE_COMPILE:
00306                 magic = magic_open(flags|MAGIC_CHECK);
00307                 if (magic == NULL) {
00308                         (void)fprintf(stderr, "%s: %s\n", progname,
00309                             strerror(errno));
00310                         return 1;
00311                 }
00312                 c = action == FILE_CHECK ? magic_check(magic, magicfile) :
00313                     magic_compile(magic, magicfile);
00314                 if (c == -1) {
00315                         (void)fprintf(stderr, "%s: %s\n", progname,
00316                             magic_error(magic));
00317                         return -1;
00318                 }
00319                 return 0;
00320         default:
00321                 load(magicfile, flags);
00322                 break;
00323         }
00324 
00325         if (optind == argc) {
00326                 if (!didsomefiles) {
00327                         usage();
00328                 }
00329         }
00330         else {
00331                 int i, wid, nw;
00332                 for (wid = 0, i = optind; i < argc; i++) {
00333                         nw = file_mbswidth(argv[i]);
00334                         if (nw > wid)
00335                                 wid = nw;
00336                 }
00337                 for (; optind < argc; optind++)
00338                         process(argv[optind], wid);
00339         }
00340 
00341         magic_close(magic);
00342         magic = NULL;
00343         return 0;
00344 }
00345 
00346 
00347 private void
00348 load(/*@unused@*/ const char *m, int flags)
00349 {
00350         if (magic)
00351                 return;
00352         magic = magic_open(flags);
00353         if (magic == NULL) {
00354                 (void)fprintf(stderr, "%s: %s\n", progname, strerror(errno));
00355                 exit(EXIT_FAILURE);
00356         }
00357         if (magic_load(magic, magicfile) == -1) {
00358                 (void)fprintf(stderr, "%s: %s\n",
00359                     progname, magic_error(magic));
00360                 exit(EXIT_FAILURE);
00361         }
00362 }
00363 
00364 /*
00365  * unwrap -- read a file of filenames, do each one.
00366  */
00367 private void
00368 unwrap(char *fn)
00369 {
00370         char buf[MAXPATHLEN];
00371         FILE *f;
00372         int wid = 0, cwid;
00373 
00374         if (strcmp("-", fn) == 0) {
00375                 f = stdin;
00376                 wid = 1;
00377         } else {
00378                 if ((f = fopen(fn, "r")) == NULL) {
00379                         (void)fprintf(stderr, "%s: Cannot open `%s' (%s).\n",
00380                             progname, fn, strerror(errno));
00381                         exit(EXIT_FAILURE);
00382                 }
00383 
00384                 while (fgets(buf, MAXPATHLEN, f) != NULL) {
00385                         cwid = file_mbswidth(buf) - 1;
00386                         if (cwid > wid)
00387                                 wid = cwid;
00388                 }
00389 
00390                 rewind(f);
00391         }
00392 
00393         while (fgets(buf, MAXPATHLEN, f) != NULL) {
00394                 // cut off CR, we _need_ bytes here, not characters
00395                 buf[strlen(buf)-1] = '\0';
00396                 process(buf, wid);
00397                 if(nobuffer)
00398                         (void) fflush(stdout);
00399         }
00400 
00401         (void) fclose(f);
00402 }
00403 
00404 private void
00405 process(const char *inname, int wid)
00406 {
00407         const char *type;
00408         int std_in = strcmp(inname, "-") == 0;
00409 
00410         if (wid > 0 && !bflag)
00411                 (void) printf("%s%s%*s ", std_in ? "/dev/stdin" : inname,
00412                     separator, (int) (nopad ? 0 : (wid - file_mbswidth(inname))), "");
00413 
00414         type = magic_file(magic, std_in ? NULL : inname);
00415         if (type == NULL)
00416                 printf("ERROR: %s\n", magic_error(magic));
00417         else
00418                 printf("%s\n", type);
00419 }
00420 
00421 
00422 #if 0
00423 /*
00424  * byteconv4
00425  * Input:
00426  *      from            4 byte quantity to convert
00427  *      same            whether to perform byte swapping
00428  *      big_endian      whether we are a big endian host
00429  */
00430 private int
00431 byteconv4(int from, int same, int big_endian)
00432 {
00433         if (same)
00434                 return from;
00435         else if (big_endian) {          /* lsb -> msb conversion on msb */
00436                 union {
00437                         int i;
00438                         char c[4];
00439                 } retval, tmpval;
00440 
00441                 tmpval.i = from;
00442                 retval.c[0] = tmpval.c[3];
00443                 retval.c[1] = tmpval.c[2];
00444                 retval.c[2] = tmpval.c[1];
00445                 retval.c[3] = tmpval.c[0];
00446 
00447                 return retval.i;
00448         }
00449         else
00450                 return ntohl(from);     /* msb -> lsb conversion on lsb */
00451 }
00452 
00453 /*
00454  * byteconv2
00455  * Same as byteconv4, but for shorts
00456  */
00457 private short
00458 byteconv2(int from, int same, int big_endian)
00459 {
00460         if (same)
00461                 return from;
00462         else if (big_endian) {          /* lsb -> msb conversion on msb */
00463                 union {
00464                         short s;
00465                         char c[2];
00466                 } retval, tmpval;
00467 
00468                 tmpval.s = (short) from;
00469                 retval.c[0] = tmpval.c[1];
00470                 retval.c[1] = tmpval.c[0];
00471 
00472                 return retval.s;
00473         }
00474         else
00475                 return ntohs(from);     /* msb -> lsb conversion on lsb */
00476 }
00477 #endif
00478 
00479 size_t
00480 file_mbswidth(const char *s)
00481 {
00482 #if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH)
00483         size_t bytesconsumed, old_n, n, width = 0;
00484         mbstate_t state;
00485         wchar_t nextchar;
00486         (void)memset(&state, 0, sizeof(mbstate_t));
00487         old_n = n = strlen(s);
00488 
00489         while (n > 0) {
00490                 bytesconsumed = mbrtowc(&nextchar, s, n, &state);
00491                 if (bytesconsumed == (size_t)(-1) ||
00492                     bytesconsumed == (size_t)(-2)) {
00493                         /* Something went wrong, return something reasonable */
00494                         return old_n;
00495                 }
00496                 if (s[0] == '\n') {
00497                         /*
00498                          * do what strlen() would do, so that caller
00499                          * is always right
00500                          */
00501                         width++;
00502                 } else
00503                         width += wcwidth(nextchar);
00504 
00505                 s += bytesconsumed, n -= bytesconsumed;
00506         }
00507         return width;
00508 #else
00509         return strlen(s);
00510 #endif
00511 }
00512 
00513 private void
00514 usage(void)
00515 {
00516         (void)fprintf(stderr, USAGE, progname, progname);
00517 #ifdef HAVE_GETOPT_LONG
00518         (void)fputs("Try `file --help' for more information.\n", stderr);
00519 #endif
00520         exit(EXIT_FAILURE);
00521 }
00522 
00523 #ifdef HAVE_GETOPT_LONG
00524 private void
00525 help(void)
00526 {
00527         puts(
00528 "Usage: file [OPTION]... [FILE]...\n"
00529 "Determine file type of FILEs.\n"
00530 "\n"
00531 "  -m, --magic-file LIST      use LIST as a colon-separated list of magic\n"
00532 "                               number files\n"
00533 "  -z, --uncompress           try to look inside compressed files\n"
00534 "  -b, --brief                do not prepend filenames to output lines\n"
00535 "  -c, --checking-printout    print the parsed form of the magic file, use in\n"
00536 "                               conjunction with -m to debug a new magic file\n"
00537 "                               before installing it\n"
00538 "  -f, --files-from FILE      read the filenames to be examined from FILE\n"
00539 "  -F, --separator string     use string as separator instead of `:'\n"
00540 "  -i, --mime                 output mime type strings\n"
00541 "  -k, --keep-going           don't stop at the first match\n"
00542 "  -L, --dereference          causes symlinks to be followed\n"
00543 "  -n, --no-buffer            do not buffer output\n"
00544 "  -N, --no-pad               do not pad output\n"
00545 "  -p, --preserve-date        preserve access times on files\n"
00546 "  -r, --raw                  don't translate unprintable chars to \\ooo\n"
00547 "  -s, --special-files        treat special (block/char devices) files as\n"
00548 "                             ordinary ones\n"
00549 "      --help                 display this help and exit\n"
00550 "      --version              output version information and exit\n"
00551 );
00552         exit(EXIT_SUCCESS);
00553 }
00554 #endif

Generated on Fri Oct 12 08:44:53 2007 for rpm by  doxygen 1.5.2