]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/file/file.c
This commit was generated by cvs2svn to compensate for changes in r165538,
[FreeBSD/FreeBSD.git] / contrib / file / file.c
1 /*
2  * Copyright (c) Ian F. Darwin 1986-1995.
3  * Software written by Ian F. Darwin and others;
4  * maintained 1995-present by Christos Zoulas and others.
5  * 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice immediately at the beginning of the file, without modification,
11  *    this list of conditions, and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *  
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 /*
29  * file - find type of a file or files - main program.
30  */
31
32 #include "file.h"
33 #include "magic.h"
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <string.h>
39 #include <sys/types.h>
40 #include <sys/param.h>  /* for MAXPATHLEN */
41 #include <sys/stat.h>
42 #ifdef RESTORE_TIME
43 # if (__COHERENT__ >= 0x420)
44 #  include <sys/utime.h>
45 # else
46 #  ifdef USE_UTIMES
47 #   include <sys/time.h>
48 #  else
49 #   include <utime.h>
50 #  endif
51 # endif
52 #endif
53 #ifdef HAVE_UNISTD_H
54 #include <unistd.h>     /* for read() */
55 #endif
56 #ifdef HAVE_LOCALE_H
57 #include <locale.h>
58 #endif
59 #ifdef HAVE_WCHAR_H
60 #include <wchar.h>
61 #endif
62
63 #ifdef HAVE_GETOPT_H
64 #include <getopt.h>     /* for long options (is this portable?)*/
65 #else
66 #undef HAVE_GETOPT_LONG
67 #endif
68
69 #include <netinet/in.h>         /* for byte swapping */
70
71 #include "patchlevel.h"
72
73 #ifndef lint
74 FILE_RCSID("@(#)$Id: file.c,v 1.100 2005/10/17 18:41:44 christos Exp $")
75 #endif  /* lint */
76
77
78 #ifdef S_IFLNK
79 #define SYMLINKFLAG "Lh"
80 #else
81 #define SYMLINKFLAG ""
82 #endif
83
84 # define USAGE  "Usage: %s [-bcik" SYMLINKFLAG "nNsvz] [-f namefile] [-F separator] [-m magicfiles] file...\n       %s -C -m magicfiles\n"
85
86 #ifndef MAXPATHLEN
87 #define MAXPATHLEN      512
88 #endif
89
90 private int             /* Global command-line options          */
91         bflag = 0,      /* brief output format                  */
92         nopad = 0,      /* Don't pad output                     */
93         nobuffer = 0;   /* Do not buffer stdout                 */
94
95 private const char *magicfile = 0;      /* where the magic is   */
96 private const char *default_magicfile = MAGIC;
97 private const char *separator = ":";    /* Default field separator      */
98
99 private char *progname;         /* used throughout              */
100
101 private struct magic_set *magic;
102
103 private void unwrap(char *);
104 private void usage(void);
105 #ifdef HAVE_GETOPT_LONG
106 private void help(void);
107 #endif
108 #if 0
109 private int byteconv4(int, int, int);
110 private short byteconv2(int, int, int);
111 #endif
112
113 int main(int, char *[]);
114 private void process(const char *, int);
115 private void load(const char *, int);
116
117
118 /*
119  * main - parse arguments and handle options
120  */
121 int
122 main(int argc, char *argv[])
123 {
124         int c;
125         int action = 0, didsomefiles = 0, errflg = 0;
126         int flags = 0;
127         char *home, *usermagic;
128         struct stat sb;
129 #define OPTSTRING       "bcCdf:F:hikLm:nNprsvz"
130 #ifdef HAVE_GETOPT_LONG
131         int longindex;
132         private struct option long_options[] =
133         {
134                 {"version", 0, 0, 'v'},
135                 {"help", 0, 0, 0},
136                 {"brief", 0, 0, 'b'},
137                 {"checking-printout", 0, 0, 'c'},
138                 {"debug", 0, 0, 'd'},
139                 {"files-from", 1, 0, 'f'},
140                 {"separator", 1, 0, 'F'},
141                 {"mime", 0, 0, 'i'},
142                 {"keep-going", 0, 0, 'k'},
143 #ifdef S_IFLNK
144                 {"dereference", 0, 0, 'L'},
145                 {"no-dereference", 0, 0, 'h'},
146 #endif
147                 {"magic-file", 1, 0, 'm'},
148 #if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
149                 {"preserve-date", 0, 0, 'p'},
150 #endif
151                 {"uncompress", 0, 0, 'z'},
152                 {"raw", 0, 0, 'r'},
153                 {"no-buffer", 0, 0, 'n'},
154                 {"no-pad", 0, 0, 'N'},
155                 {"special-files", 0, 0, 's'},
156                 {"compile", 0, 0, 'C'},
157                 {0, 0, 0, 0},
158         };
159 #endif
160
161 #ifdef LC_CTYPE
162         /* makes islower etc work for other langs */
163         (void)setlocale(LC_CTYPE, "");
164 #endif
165
166 #ifdef __EMX__
167         /* sh-like wildcard expansion! Shouldn't hurt at least ... */
168         _wildcard(&argc, &argv);
169 #endif
170
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                         if ((usermagic = malloc(strlen(home) + 8)) != NULL) {
182                                 (void)strcpy(usermagic, home);
183                                 (void)strcat(usermagic, "/.magic");
184                                 if (stat(usermagic, &sb)<0) 
185                                         free(usermagic);
186                                 else
187                                         magicfile = usermagic;
188                         }
189                 }
190
191 #ifdef S_IFLNK
192         flags |= getenv("POSIXLY_CORRECT") ? MAGIC_SYMLINK : 0;
193 #endif
194 #ifndef HAVE_GETOPT_LONG
195         while ((c = getopt(argc, argv, OPTSTRING)) != -1)
196 #else
197         while ((c = getopt_long(argc, argv, OPTSTRING, long_options,
198             &longindex)) != -1)
199 #endif
200                 switch (c) {
201 #ifdef HAVE_GETOPT_LONG
202                 case 0 :
203                         if (longindex == 1)
204                                 help();
205                         break;
206 #endif
207                 case 'b':
208                         ++bflag;
209                         break;
210                 case 'c':
211                         action = FILE_CHECK;
212                         break;
213                 case 'C':
214                         action = FILE_COMPILE;
215                         break;
216                 case 'd':
217                         flags |= MAGIC_DEBUG|MAGIC_CHECK;
218                         break;
219                 case 'f':
220                         if(action)
221                                 usage();
222                         load(magicfile, flags);
223                         unwrap(optarg);
224                         ++didsomefiles;
225                         break;
226                 case 'F':
227                         separator = optarg;
228                         break;
229                 case 'i':
230                         flags |= MAGIC_MIME;
231                         break;
232                 case 'k':
233                         flags |= MAGIC_CONTINUE;
234                         break;
235                 case 'm':
236                         magicfile = optarg;
237                         break;
238                 case 'n':
239                         ++nobuffer;
240                         break;
241                 case 'N':
242                         ++nopad;
243                         break;
244 #if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
245                 case 'p':
246                         flags |= MAGIC_PRESERVE_ATIME;
247                         break;
248 #endif
249                 case 'r':
250                         flags |= MAGIC_RAW;
251                         break;
252                 case 's':
253                         flags |= MAGIC_DEVICES;
254                         break;
255                 case 'v':
256                         (void)fprintf(stdout, "%s-%d.%.2d\n", progname,
257                                        FILE_VERSION_MAJOR, patchlevel);
258                         (void)fprintf(stdout, "magic file from %s\n",
259                                        magicfile);
260                         return 1;
261                 case 'z':
262                         flags |= MAGIC_COMPRESS;
263                         break;
264 #ifdef S_IFLNK
265                 case 'L':
266                         flags |= MAGIC_SYMLINK;
267                         break;
268                 case 'h':
269                         flags &= ~MAGIC_SYMLINK;
270                         break;
271 #endif
272                 case '?':
273                 default:
274                         errflg++;
275                         break;
276                 }
277
278         if (errflg) {
279                 usage();
280         }
281
282         switch(action) {
283         case FILE_CHECK:
284         case FILE_COMPILE:
285                 magic = magic_open(flags|MAGIC_CHECK);
286                 if (magic == NULL) {
287                         (void)fprintf(stderr, "%s: %s\n", progname,
288                             strerror(errno));
289                         return 1;
290                 }
291                 c = action == FILE_CHECK ? magic_check(magic, magicfile) :
292                     magic_compile(magic, magicfile);
293                 if (c == -1) {
294                         (void)fprintf(stderr, "%s: %s\n", progname,
295                             magic_error(magic));
296                         return -1;
297                 }
298                 return 0;
299         default:
300                 load(magicfile, flags);
301                 break;
302         }
303
304         if (optind == argc) {
305                 if (!didsomefiles) {
306                         usage();
307                 }
308         }
309         else {
310                 int i, wid, nw;
311                 for (wid = 0, i = optind; i < argc; i++) {
312                         nw = file_mbswidth(argv[i]);
313                         if (nw > wid)
314                                 wid = nw;
315                 }
316                 for (; optind < argc; optind++)
317                         process(argv[optind], wid);
318         }
319
320         magic_close(magic);
321         return 0;
322 }
323
324
325 private void
326 /*ARGSUSED*/
327 load(const char *m, int flags)
328 {
329         if (magic)
330                 return;
331         magic = magic_open(flags);
332         if (magic == NULL) {
333                 (void)fprintf(stderr, "%s: %s\n", progname, strerror(errno));
334                 exit(1);
335         }
336         if (magic_load(magic, magicfile) == -1) {
337                 (void)fprintf(stderr, "%s: %s\n",
338                     progname, magic_error(magic));
339                 exit(1);
340         }
341 }
342
343 /*
344  * unwrap -- read a file of filenames, do each one.
345  */
346 private void
347 unwrap(char *fn)
348 {
349         char buf[MAXPATHLEN];
350         FILE *f;
351         int wid = 0, cwid;
352         size_t len;
353
354         if (strcmp("-", fn) == 0) {
355                 f = stdin;
356                 wid = 1;
357         } else {
358                 if ((f = fopen(fn, "r")) == NULL) {
359                         (void)fprintf(stderr, "%s: Cannot open `%s' (%s).\n",
360                             progname, fn, strerror(errno));
361                         exit(1);
362                 }
363
364                 while (fgets(buf, MAXPATHLEN, f) != NULL) {
365                         len = strlen(buf);
366                         if (len > 0 && buf[len - 1] == '\n')
367                                 buf[len - 1] = '\0';
368                         cwid = file_mbswidth(buf);
369                         if (cwid > wid)
370                                 wid = cwid;
371                 }
372
373                 rewind(f);
374         }
375
376         while (fgets(buf, MAXPATHLEN, f) != NULL) {
377                 len = strlen(buf);
378                 if (len > 0 && buf[len - 1] == '\n')
379                         buf[len - 1] = '\0';
380                 process(buf, wid);
381                 if(nobuffer)
382                         (void)fflush(stdout);
383         }
384
385         (void)fclose(f);
386 }
387
388 private void
389 process(const char *inname, int wid)
390 {
391         const char *type;
392         int std_in = strcmp(inname, "-") == 0;
393
394         if (wid > 0 && !bflag)
395                 (void)printf("%s%s%*s ", std_in ? "/dev/stdin" : inname,
396                     separator, (int) (nopad ? 0 : (wid - file_mbswidth(inname))), "");
397
398         type = magic_file(magic, std_in ? NULL : inname);
399         if (type == NULL)
400                 (void)printf("ERROR: %s\n", magic_error(magic));
401         else
402                 (void)printf("%s\n", type);
403 }
404
405
406 #if 0
407 /*
408  * byteconv4
409  * Input:
410  *      from            4 byte quantity to convert
411  *      same            whether to perform byte swapping
412  *      big_endian      whether we are a big endian host
413  */
414 private int
415 byteconv4(int from, int same, int big_endian)
416 {
417         if (same)
418                 return from;
419         else if (big_endian) {          /* lsb -> msb conversion on msb */
420                 union {
421                         int i;
422                         char c[4];
423                 } retval, tmpval;
424
425                 tmpval.i = from;
426                 retval.c[0] = tmpval.c[3];
427                 retval.c[1] = tmpval.c[2];
428                 retval.c[2] = tmpval.c[1];
429                 retval.c[3] = tmpval.c[0];
430
431                 return retval.i;
432         }
433         else
434                 return ntohl(from);     /* msb -> lsb conversion on lsb */
435 }
436
437 /*
438  * byteconv2
439  * Same as byteconv4, but for shorts
440  */
441 private short
442 byteconv2(int from, int same, int big_endian)
443 {
444         if (same)
445                 return from;
446         else if (big_endian) {          /* lsb -> msb conversion on msb */
447                 union {
448                         short s;
449                         char c[2];
450                 } retval, tmpval;
451
452                 tmpval.s = (short) from;
453                 retval.c[0] = tmpval.c[1];
454                 retval.c[1] = tmpval.c[0];
455
456                 return retval.s;
457         }
458         else
459                 return ntohs(from);     /* msb -> lsb conversion on lsb */
460 }
461 #endif
462
463 size_t
464 file_mbswidth(const char *s)
465 {
466 #if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH)
467         size_t bytesconsumed, old_n, n, width = 0;
468         mbstate_t state;
469         wchar_t nextchar;
470         (void)memset(&state, 0, sizeof(mbstate_t));
471         old_n = n = strlen(s);
472
473         while (n > 0) {
474                 bytesconsumed = mbrtowc(&nextchar, s, n, &state);
475                 if (bytesconsumed == (size_t)(-1) ||
476                     bytesconsumed == (size_t)(-2)) {
477                         /* Something went wrong, return something reasonable */
478                         return old_n;
479                 }
480                 if (s[0] == '\n') {
481                         /*
482                          * do what strlen() would do, so that caller
483                          * is always right
484                          */
485                         width++;
486                 } else
487                         width += wcwidth(nextchar);
488
489                 s += bytesconsumed, n -= bytesconsumed;
490         }
491         return width;
492 #else
493         return strlen(s);
494 #endif
495 }
496
497 private void
498 usage(void)
499 {
500         (void)fprintf(stderr, USAGE, progname, progname);
501 #ifdef HAVE_GETOPT_LONG
502         (void)fputs("Try `file --help' for more information.\n", stderr);
503 #endif
504         exit(1);
505 }
506
507 #ifdef HAVE_GETOPT_LONG
508 private void
509 help(void)
510 {
511         (void)puts(
512 "Usage: file [OPTION]... [FILE]...\n"
513 "Determine file type of FILEs.\n"
514 "\n"
515 "  -m, --magic-file LIST      use LIST as a colon-separated list of magic\n"
516 "                               number files\n"
517 "  -z, --uncompress           try to look inside compressed files\n"
518 "  -b, --brief                do not prepend filenames to output lines\n"
519 "  -c, --checking-printout    print the parsed form of the magic file, use in\n"
520 "                               conjunction with -m to debug a new magic file\n"
521 "                               before installing it\n"
522 "  -f, --files-from FILE      read the filenames to be examined from FILE\n"
523 "  -F, --separator string     use string as separator instead of `:'\n"
524 "  -i, --mime                 output mime type strings\n"
525 "  -k, --keep-going           don't stop at the first match\n"
526 "  -L, --dereference          causes symlinks to be followed\n"
527 "  -n, --no-buffer            do not buffer output\n"
528 "  -N, --no-pad               do not pad output\n"
529 "  -p, --preserve-date        preserve access times on files\n"
530 "  -r, --raw                  don't translate unprintable chars to \\ooo\n"
531 "  -s, --special-files        treat special (block/char devices) files as\n"
532 "                             ordinary ones\n"
533 "      --help                 display this help and exit\n"
534 "      --version              output version information and exit\n"
535 );
536         exit(0);
537 }
538 #endif