[BACK]Return to file.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / file

Diff for /src/usr.bin/file/file.c between version 1.13 and 1.14

version 1.13, 2003/07/02 21:04:09 version 1.14, 2004/05/19 02:32:35
Line 1 
Line 1 
 /*      $OpenBSD$       */  /*      $OpenBSD$ */
   
 /*  /*
  * file - find type of a file or files - main program.  
  *  
  * Copyright (c) Ian F. Darwin 1986-1995.   * Copyright (c) Ian F. Darwin 1986-1995.
  * Software written by Ian F. Darwin and others;   * Software written by Ian F. Darwin and others;
  * maintained 1995-present by Christos Zoulas and others.   * maintained 1995-present by Christos Zoulas and others.
Line 29 
Line 26 
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.   * SUCH DAMAGE.
  */   */
   /*
    * file - find type of a file or files - main program.
    */
   
 #ifndef lint  #include "file.h"
 static char *moduleid = "$OpenBSD$";  #include "magic.h"
 #endif  /* lint */  
   
 #include <stdio.h>  #include <stdio.h>
 #include <stdlib.h>  #include <stdlib.h>
   #include <unistd.h>
 #include <string.h>  #include <string.h>
 #include <sys/types.h>  #include <sys/types.h>
 #include <sys/param.h>  /* for MAXPATHLEN */  #include <sys/param.h>  /* for MAXPATHLEN */
 #include <sys/stat.h>  #include <sys/stat.h>
 #include <fcntl.h>      /* for open() */  #include <fcntl.h>      /* for open() */
 #if (__COHERENT__ >= 0x420)  #ifdef RESTORE_TIME
 # include <sys/utime.h>  # if (__COHERENT__ >= 0x420)
 #else  #  include <sys/utime.h>
 # ifdef USE_UTIMES  
 #  include <sys/time.h>  
 # else  # else
 #  include <utime.h>  #  ifdef USE_UTIMES
   #   include <sys/time.h>
   #  else
   #   include <utime.h>
   #  endif
 # endif  # endif
 #endif  #endif
   #ifdef HAVE_UNISTD_H
 #include <unistd.h>     /* for read() */  #include <unistd.h>     /* for read() */
 #include <err.h>  #endif
   #ifdef HAVE_LOCALE_H
   #include <locale.h>
   #endif
   #ifdef HAVE_WCHAR_H
   #include <wchar.h>
   #endif
   
   #ifdef HAVE_GETOPT_H
   #include <getopt.h>     /* for long options (is this portable?)*/
   #else
   #undef HAVE_GETOPT_LONG
   #endif
   
 #include <netinet/in.h>         /* for byte swapping */  #include <netinet/in.h>         /* for byte swapping */
   
 #include "patchlevel.h"  #include "patchlevel.h"
 #include "file.h"  
   
   #ifndef lint
   FILE_RCSID("@(#)$Id$")
   #endif  /* lint */
   
   
 #ifdef S_IFLNK  #ifdef S_IFLNK
 # define USAGE  "Usage: %s [-vbczL] [-f namefile] [-m magicfiles] file...\n"  #define SYMLINKFLAG "L"
 #else  #else
 # define USAGE  "Usage: %s [-vbcz] [-f namefile] [-m magicfiles] file...\n"  #define SYMLINKFLAG ""
 #endif  #endif
   
 #ifndef MAGIC  # define USAGE  "Usage: %s [-bcik" SYMLINKFLAG "nNsvz] [-f namefile] [-F separator] [-m magicfiles] file...\n       %s -C -m magicfiles\n"
 # define MAGIC "/etc/magic"  
   #ifndef MAXPATHLEN
   #define MAXPATHLEN      512
 #endif  #endif
   
 int                     /* Global command-line options          */  private int             /* Global command-line options          */
         debug = 0,      /* debugging                            */          bflag = 0,      /* brief output format                  */
         bflag = 0,      /* Don't print filename                 */          nopad = 0,      /* Don't pad output                     */
         lflag = 0,      /* follow Symlinks (BSD only)           */          nobuffer = 0;   /* Do not buffer stdout                 */
         zflag = 0;      /* follow (uncompress) compressed files */  
   
 int                     /* Misc globals                         */  private const char *magicfile = 0;      /* where the magic is   */
         nmagic = 0;     /* number of valid magic[]s             */  private const char *default_magicfile = MAGIC;
   private char *separator = ":";  /* Default field separator      */
   
 struct  magic *magic;   /* array of magic entries               */  private char *progname;         /* used throughout              */
   
 char *magicfile;        /* where magic be found                 */  private struct magic_set *magic;
   
 int lineno;             /* line number in the magic file        */  private void unwrap(char *);
   private void usage(void);
   #ifdef HAVE_GETOPT_LONG
 static void     unwrap(char *fn);  private void help(void);
   #endif
 #if 0  #if 0
 static int      byteconv4(int, int, int);  private int byteconv4(int, int, int);
 static short    byteconv2(int, int, int);  private short byteconv2(int, int, int);
 #endif  #endif
   
   int main(int, char *[]);
   private void process(const char *, int);
   private void load(const char *, int);
   
   
 /*  /*
  * main - parse arguments and handle options   * main - parse arguments and handle options
  */   */
Line 97 
Line 124 
 main(int argc, char *argv[])  main(int argc, char *argv[])
 {  {
         int c;          int c;
         int check = 0, didsomefiles = 0, errflg = 0, ret = 0, app = 0;          int action = 0, didsomefiles = 0, errflg = 0;
         extern char *__progname;          int flags = 0;
           char *home, *usermagic;
           struct stat sb;
   #define OPTSTRING       "bcCdf:F:ikLm:nNprsvz"
   #ifdef HAVE_GETOPT_LONG
           int longindex;
           private struct option long_options[] =
           {
                   {"version", 0, 0, 'v'},
                   {"help", 0, 0, 0},
                   {"brief", 0, 0, 'b'},
                   {"checking-printout", 0, 0, 'c'},
                   {"debug", 0, 0, 'd'},
                   {"files-from", 1, 0, 'f'},
                   {"separator", 1, 0, 'F'},
                   {"mime", 0, 0, 'i'},
                   {"keep-going", 0, 0, 'k'},
   #ifdef S_IFLNK
                   {"dereference", 0, 0, 'L'},
   #endif
                   {"magic-file", 1, 0, 'm'},
   #if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
                   {"preserve-date", 0, 0, 'p'},
   #endif
                   {"uncompress", 0, 0, 'z'},
                   {"raw", 0, 0, 'r'},
                   {"no-buffer", 0, 0, 'n'},
                   {"no-pad", 0, 0, 'N'},
                   {"special-files", 0, 0, 's'},
                   {"compile", 0, 0, 'C'},
                   {0, 0, 0, 0},
           };
   #endif
   
         if (!(magicfile = getenv("MAGIC")))  #ifdef LC_CTYPE
                 magicfile = MAGIC;          setlocale(LC_CTYPE, ""); /* makes islower etc work for other langs */
   #endif
   
         while ((c = getopt(argc, argv, "bvcdf:Lm:z")) != -1)  #ifdef __EMX__
           /* sh-like wildcard expansion! Shouldn't hurt at least ... */
           _wildcard(&argc, &argv);
   #endif
   
           if ((progname = strrchr(argv[0], '/')) != NULL)
                   progname++;
           else
                   progname = argv[0];
   
           magicfile = default_magicfile;
           if ((usermagic = getenv("MAGIC")) != NULL)
                   magicfile = usermagic;
           else
                   if ((home = getenv("HOME")) != NULL) {
                           size_t len = strlen(home) + 8;
                           if ((usermagic = malloc(len)) != NULL) {
                                   (void)strlcpy(usermagic, home, len);
                                   (void)strlcat(usermagic, "/.magic", len);
                                   if (stat(usermagic, &sb)<0)
                                           free(usermagic);
                                   else
                                           magicfile = usermagic;
                           }
                   }
   
   #ifndef HAVE_GETOPT_LONG
           while ((c = getopt(argc, argv, OPTSTRING)) != -1)
   #else
           while ((c = getopt_long(argc, argv, OPTSTRING, long_options,
               &longindex)) != -1)
   #endif
                 switch (c) {                  switch (c) {
                 case 'v':  #ifdef HAVE_GETOPT_LONG
                         (void) printf("%s-%d.%d\n", __progname,                  case 0 :
                                        FILE_VERSION_MAJOR, patchlevel);                          if (longindex == 1)
                         return 1;                                  help();
                           break;
   #endif
                 case 'b':                  case 'b':
                         ++bflag;                          ++bflag;
                         break;                          break;
                 case 'c':                  case 'c':
                         ++check;                          action = FILE_CHECK;
                         break;                          break;
                   case 'C':
                           action = FILE_COMPILE;
                           break;
                 case 'd':                  case 'd':
                         ++debug;                          flags |= MAGIC_DEBUG|MAGIC_CHECK;
                         break;                          break;
                 case 'f':                  case 'f':
                         if (!app) {                          if(action)
                                 ret = apprentice(magicfile, check);                                  usage();
                                 if (check)                          load(magicfile, flags);
                                         exit(ret);  
                                 app = 1;  
                         }  
                         unwrap(optarg);                          unwrap(optarg);
                         ++didsomefiles;                          ++didsomefiles;
                         break;                          break;
 #ifdef S_IFLNK                  case 'F':
                 case 'L':                          separator = optarg;
                         ++lflag;  
                         break;                          break;
 #endif                  case 'i':
                           flags |= MAGIC_MIME;
                           break;
                   case 'k':
                           flags |= MAGIC_CONTINUE;
                           break;
                 case 'm':                  case 'm':
                         magicfile = optarg;                          magicfile = optarg;
                         break;                          break;
                   case 'n':
                           ++nobuffer;
                           break;
                   case 'N':
                           ++nopad;
                           break;
   #if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
                   case 'p':
                           flags |= MAGIC_PRESERVE_ATIME;
                           break;
   #endif
                   case 'r':
                           flags |= MAGIC_RAW;
                           break;
                   case 's':
                           flags |= MAGIC_DEVICES;
                           break;
                   case 'v':
                           (void) fprintf(stdout, "%s-%d.%.2d\n", progname,
                                          FILE_VERSION_MAJOR, patchlevel);
                           (void) fprintf(stdout, "magic file from %s\n",
                                          magicfile);
                           return 1;
                 case 'z':                  case 'z':
                         zflag++;                          flags |= MAGIC_COMPRESS;
                         break;                          break;
   #ifdef S_IFLNK
                   case 'L':
                           flags |= MAGIC_SYMLINK;
                           break;
   #endif
                 case '?':                  case '?':
                 default:                  default:
                         errflg++;                          errflg++;
Line 146 
Line 271 
                 }                  }
   
         if (errflg) {          if (errflg) {
                 (void) fprintf(stderr, USAGE, __progname);                  usage();
                 exit(2);  
         }          }
   
         if (!app) {          switch(action) {
                 ret = apprentice(magicfile, check);          case FILE_CHECK:
                 if (check)          case FILE_COMPILE:
                         exit(ret);                  magic = magic_open(flags|MAGIC_CHECK);
                 app = 1;                  if (magic == NULL) {
                           (void)fprintf(stderr, "%s: %s\n", progname,
                               strerror(errno));
                           return 1;
                   }
                   c = action == FILE_CHECK ? magic_check(magic, magicfile) :
                       magic_compile(magic, magicfile);
                   if (c == -1) {
                           (void)fprintf(stderr, "%s: %s\n", progname,
                               magic_error(magic));
                           return -1;
                   }
                   return 0;
           default:
                   load(magicfile, flags);
                   break;
         }          }
   
         if (optind == argc) {          if (optind == argc) {
                 if (!didsomefiles) {                  if (!didsomefiles) {
                         fprintf(stderr, USAGE, __progname);                          usage();
                         exit(2);  
                 }                  }
         } else {          }
           else {
                 int i, wid, nw;                  int i, wid, nw;
                 for (wid = 0, i = optind; i < argc; i++) {                  for (wid = 0, i = optind; i < argc; i++) {
                         nw = strlen(argv[i]);                          nw = file_mbswidth(argv[i]);
                         if (nw > wid)                          if (nw > wid)
                                 wid = nw;                                  wid = nw;
                 }                  }
Line 177 
Line 316 
 }  }
   
   
   private void
   load(const char *m, int flags)
   {
           if (magic)
                   return;
           magic = magic_open(flags);
           if (magic == NULL) {
                   (void)fprintf(stderr, "%s: %s\n", progname, strerror(errno));
                   exit(1);
           }
           if (magic_load(magic, magicfile) == -1) {
                   (void)fprintf(stderr, "%s: %s\n",
                       progname, magic_error(magic));
                   exit(1);
           }
   }
   
 /*  /*
  * unwrap -- read a file of filenames, do each one.   * unwrap -- read a file of filenames, do each one.
  */   */
 static void  private void
 unwrap(fn)  unwrap(char *fn)
 char *fn;  
 {  {
         char buf[MAXPATHLEN];          char buf[MAXPATHLEN];
         FILE *f;          FILE *f;
Line 193 
Line 348 
                 wid = 1;                  wid = 1;
         } else {          } else {
                 if ((f = fopen(fn, "r")) == NULL) {                  if ((f = fopen(fn, "r")) == NULL) {
                         err(1, "Cannot open `%s'", fn);                          (void)fprintf(stderr, "%s: Cannot open `%s' (%s).\n",
                         /*NOTREACHED*/                              progname, fn, strerror(errno));
                           exit(1);
                 }                  }
   
                 while (fgets(buf, sizeof(buf), f) != NULL) {                  while (fgets(buf, MAXPATHLEN, f) != NULL) {
                         cwid = strlen(buf) - 1;                          cwid = file_mbswidth(buf) - 1;
                         if (cwid > wid)                          if (cwid > wid)
                                 wid = cwid;                                  wid = cwid;
                 }                  }
Line 206 
Line 362 
                 rewind(f);                  rewind(f);
         }          }
   
         while (fgets(buf, sizeof(buf), f) != NULL) {          while (fgets(buf, MAXPATHLEN, f) != NULL) {
                 buf[strlen(buf)-1] = '\0';                  buf[file_mbswidth(buf)-1] = '\0';
                 process(buf, wid);                  process(buf, wid);
                   if(nobuffer)
                           (void) fflush(stdout);
         }          }
   
         (void) fclose(f);          (void) fclose(f);
 }  }
   
   private void
   process(const char *inname, int wid)
   {
           const char *type;
           int std_in = strcmp(inname, "-") == 0;
   
           if (wid > 0 && !bflag)
                   (void) printf("%s%s%*s ", std_in ? "/dev/stdin" : inname,
                       separator, (int) (nopad ? 0 : (wid - file_mbswidth(inname))), "");
   
           type = magic_file(magic, std_in ? NULL : inname);
           if (type == NULL)
                   printf("ERROR: %s\n", magic_error(magic));
           else
                   printf("%s\n", type);
   }
   
   
 #if 0  #if 0
 /*  /*
  * byteconv4   * byteconv4
Line 223 
Line 398 
  *      same            whether to perform byte swapping   *      same            whether to perform byte swapping
  *      big_endian      whether we are a big endian host   *      big_endian      whether we are a big endian host
  */   */
 static int  private int
 byteconv4(from, same, big_endian)  byteconv4(int from, int same, int big_endian)
     int from;  
     int same;  
     int big_endian;  
 {  {
   if (same)          if (same)
     return from;                  return from;
   else if (big_endian)          /* lsb -> msb conversion on msb */          else if (big_endian) {          /* lsb -> msb conversion on msb */
   {                  union {
     union {                          int i;
       int i;                          char c[4];
       char c[4];                  } retval, tmpval;
     } retval, tmpval;  
   
     tmpval.i = from;                  tmpval.i = from;
     retval.c[0] = tmpval.c[3];                  retval.c[0] = tmpval.c[3];
     retval.c[1] = tmpval.c[2];                  retval.c[1] = tmpval.c[2];
     retval.c[2] = tmpval.c[1];                  retval.c[2] = tmpval.c[1];
     retval.c[3] = tmpval.c[0];                  retval.c[3] = tmpval.c[0];
   
     return retval.i;                  return retval.i;
   }          }
   else          else
     return ntohl(from);         /* msb -> lsb conversion on lsb */                  return ntohl(from);     /* msb -> lsb conversion on lsb */
 }  }
   
 /*  /*
  * byteconv2   * byteconv2
  * Same as byteconv4, but for shorts   * Same as byteconv4, but for shorts
  */   */
 static short  private short
 byteconv2(from, same, big_endian)  byteconv2(int from, int same, int big_endian)
         int from;  
         int same;  
         int big_endian;  
 {  {
   if (same)          if (same)
     return from;                  return from;
   else if (big_endian)          /* lsb -> msb conversion on msb */          else if (big_endian) {          /* lsb -> msb conversion on msb */
   {                  union {
     union {                          short s;
       short s;                          char c[2];
       char c[2];                  } retval, tmpval;
     } retval, tmpval;  
   
     tmpval.s = (short) from;                  tmpval.s = (short) from;
     retval.c[0] = tmpval.c[1];                  retval.c[0] = tmpval.c[1];
     retval.c[1] = tmpval.c[0];                  retval.c[1] = tmpval.c[0];
   
     return retval.s;                  return retval.s;
   }          }
   else          else
     return ntohs(from);         /* msb -> lsb conversion on lsb */                  return ntohs(from);     /* msb -> lsb conversion on lsb */
 }  }
 #endif  #endif
   
 /*  size_t
  * process - process input file  file_mbswidth(const char *s)
  */  
 void  
 process(inname, wid)  
 const char      *inname;  
 int wid;  
 {  {
         int     fd = 0;  #ifdef HAVE_WCHAR_H
         static  const char stdname[] = "standard input";          size_t bytesconsumed, old_n, n, width = 0;
         unsigned char   buf[HOWMANY+1]; /* one extra for terminating '\0' */          mbstate_t state;
         struct stat     sb;          wchar_t nextchar;
         int nbytes = 0; /* number of bytes read from a datafile */          (void)memset(&state, 0, sizeof(mbstate_t));
         char match = '\0';          old_n = n = strlen(s);
   
         if (strcmp("-", inname) == 0) {          while (n > 0) {
                 if (fstat(0, &sb)<0) {                  bytesconsumed = mbrtowc(&nextchar, s, n, &state);
                         err(1, "cannot fstat `%s'", stdname);                  if (bytesconsumed == (size_t)(-1) ||
                         /*NOTREACHED*/                      bytesconsumed == (size_t)(-2)) {
                           /* Something went wrong, return something reasonable */
                           return old_n;
                 }                  }
                 inname = stdname;                  if (s[0] == '\n') {
         }                          /*
                            * do what strlen() would do, so that caller
                            * is always right
                            */
                           width++;
                   } else
                           width += wcwidth(nextchar);
   
         if (wid > 0 && !bflag)                  s += bytesconsumed, n -= bytesconsumed;
              (void) printf("%s:%*s ", inname,  
                            (int) (wid - strlen(inname)), "");  
   
         if (inname != stdname) {  
             /*  
              * first try judging the file based on its filesystem status  
              */  
             if (fsmagic(inname, &sb) != 0) {  
                     putchar('\n');  
                     return;  
             }  
   
             if ((fd = open(inname, O_RDONLY)) < 0) {  
                     /* We can't open it, but we were able to stat it. */  
                     if (sb.st_mode & 0002) ckfputs("writable, ", stdout);  
                     if (sb.st_mode & 0111) ckfputs("executable, ", stdout);  
                     ckfprintf(stdout, "can't read `%s' (%s).\n",  
                         inname, strerror(errno));  
                     return;  
             }  
         }          }
           return width;
   #else
         /*          return strlen(s);
          * try looking at the first HOWMANY bytes  
          */  
         if ((nbytes = read(fd, (char *)buf, HOWMANY)) == -1) {  
                 err(1, "read failed");  
                 /*NOTREACHED*/  
         }  
   
         if (nbytes == 0)  
                 ckfputs("empty", stdout);  
         else {  
                 buf[nbytes++] = '\0';   /* null-terminate it */  
                 match = tryit(buf, nbytes, zflag);  
         }  
   
 #ifdef BUILTIN_ELF  
         if (match == 's' && nbytes > 5)  
                 tryelf(fd, buf, nbytes);  
 #endif  #endif
   }
   
         if (inname != stdname) {  private void
 #ifdef RESTORE_TIME  usage(void)
                 /*  {
                  * Try to restore access, modification times if read it.          (void)fprintf(stderr, USAGE, progname, progname);
                  */  #ifdef HAVE_GETOPT_LONG
 # ifdef USE_UTIMES          (void)fputs("Try `file --help' for more information.\n", stderr);
                 struct timeval  utsbuf[2];  
                 utsbuf[0].tv_sec = sb.st_atime;  
                 utsbuf[1].tv_sec = sb.st_mtime;  
   
                 (void) utimes(inname, utsbuf); /* don't care if loses */  
 # else  
                 struct utimbuf  utbuf;  
   
                 utbuf.actime = sb.st_atime;  
                 utbuf.modtime = sb.st_mtime;  
                 (void) utime(inname, &utbuf); /* don't care if loses */  
 # endif  
 #endif  #endif
                 (void) close(fd);          exit(1);
         }  
         (void) putchar('\n');  
 }  }
   
   #ifdef HAVE_GETOPT_LONG
 int  private void
 tryit(buf, nb, zflag)  help(void)
 unsigned char *buf;  
 int nb, zflag;  
 {  {
         /* try compression stuff */          puts(
         if (zflag && zmagic(buf, nb))  "Usage: file [OPTION]... [FILE]...\n"
                 return 'z';  "Determine file type of FILEs.\n"
   "\n"
         /* try tests in /etc/magic (or surrogate magic file) */  "  -m, --magic-file LIST      use LIST as a colon-separated list of magic\n"
         if (softmagic(buf, nb))  "                               number files\n"
                 return 's';  "  -z, --uncompress           try to look inside compressed files\n"
   "  -b, --brief                do not prepend filenames to output lines\n"
         /* try known keywords, check whether it is ASCII */  "  -c, --checking-printout    print the parsed form of the magic file, use in\n"
         if (ascmagic(buf, nb))  "                               conjunction with -m to debug a new magic file\n"
                 return 'a';  "                               before installing it\n"
   "  -f, --files-from FILE      read the filenames to be examined from FILE\n"
         /* see if it's international language text */  "  -F, --separator string     use string as separator instead of `:'\n"
         if (internatmagic(buf, nb))  "  -i, --mime                 output mime type strings\n"
                 return 'i';  "  -k, --keep-going           don't stop at the first match\n"
   "  -L, --dereference          causes symlinks to be followed\n"
         /* abandon hope, all ye who remain here */  "  -n, --no-buffer            do not buffer output\n"
         ckfputs("data", stdout);  "  -N, --no-pad               do not pad output\n"
                 return '\0';  "  -p, --preserve-date        preserve access times on files\n"
   "  -r, --raw                  don't translate unprintable chars to \\ooo\n"
   "  -s, --special-files        treat special (block/char devices) files as\n"
   "                             ordinary ones\n"
   "      --help                 display this help and exit\n"
   "      --version              output version information and exit\n"
   );
           exit(0);
 }  }
   #endif

Legend:
Removed from v.1.13  
changed lines
  Added in v.1.14