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.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
29 * file - find type of a file or files - main program.
39 #include <sys/types.h>
40 #include <sys/param.h> /* for MAXPATHLEN */
42 #include <fcntl.h> /* for open() */
44 # if (__COHERENT__ >= 0x420)
45 # include <sys/utime.h>
48 # include <sys/time.h>
55 #include <unistd.h> /* for read() */
65 #include <getopt.h> /* for long options (is this portable?)*/
67 #undef HAVE_GETOPT_LONG
70 #include <netinet/in.h> /* for byte swapping */
72 #include "patchlevel.h"
75 FILE_RCSID("@(#)$Id: file.c,v 1.95 2004/09/27 15:28:37 christos Exp $")
80 #define SYMLINKFLAG "L"
82 #define SYMLINKFLAG ""
85 # define USAGE "Usage: %s [-bcik" SYMLINKFLAG "nNsvz] [-f namefile] [-F separator] [-m magicfiles] file...\n %s -C -m magicfiles\n"
88 #define MAXPATHLEN 512
91 private int /* Global command-line options */
92 bflag
= 0, /* brief output format */
93 nopad
= 0, /* Don't pad output */
94 nobuffer
= 0; /* Do not buffer stdout */
96 private const char *magicfile
= 0; /* where the magic is */
97 private const char *default_magicfile
= MAGIC
;
98 private char *separator
= ":"; /* Default field separator */
100 private char *progname
; /* used throughout */
102 private struct magic_set
*magic
;
104 private void unwrap(char *);
105 private void usage(void);
106 #ifdef HAVE_GETOPT_LONG
107 private void help(void);
110 private int byteconv4(int, int, int);
111 private short byteconv2(int, int, int);
114 int main(int, char *[]);
115 private void process(const char *, int);
116 private void load(const char *, int);
120 * main - parse arguments and handle options
123 main(int argc
, char *argv
[])
126 int action
= 0, didsomefiles
= 0, errflg
= 0;
128 char *home
, *usermagic
;
130 #define OPTSTRING "bcCdf:F:ikLm:nNprsvz"
131 #ifdef HAVE_GETOPT_LONG
133 private struct option long_options
[] =
135 {"version", 0, 0, 'v'},
137 {"brief", 0, 0, 'b'},
138 {"checking-printout", 0, 0, 'c'},
139 {"debug", 0, 0, 'd'},
140 {"files-from", 1, 0, 'f'},
141 {"separator", 1, 0, 'F'},
143 {"keep-going", 0, 0, 'k'},
145 {"dereference", 0, 0, 'L'},
147 {"magic-file", 1, 0, 'm'},
148 #if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
149 {"preserve-date", 0, 0, 'p'},
151 {"uncompress", 0, 0, 'z'},
153 {"no-buffer", 0, 0, 'n'},
154 {"no-pad", 0, 0, 'N'},
155 {"special-files", 0, 0, 's'},
156 {"compile", 0, 0, 'C'},
162 setlocale(LC_CTYPE
, ""); /* makes islower etc work for other langs */
166 /* sh-like wildcard expansion! Shouldn't hurt at least ... */
167 _wildcard(&argc
, &argv
);
170 if ((progname
= strrchr(argv
[0], '/')) != NULL
)
175 magicfile
= default_magicfile
;
176 if ((usermagic
= getenv("MAGIC")) != NULL
)
177 magicfile
= usermagic
;
179 if ((home
= getenv("HOME")) != NULL
) {
180 if ((usermagic
= malloc(strlen(home
) + 8)) != NULL
) {
181 (void)strcpy(usermagic
, home
);
182 (void)strcat(usermagic
, "/.magic");
183 if (stat(usermagic
, &sb
)<0)
186 magicfile
= usermagic
;
190 #ifndef HAVE_GETOPT_LONG
191 while ((c
= getopt(argc
, argv
, OPTSTRING
)) != -1)
193 while ((c
= getopt_long(argc
, argv
, OPTSTRING
, long_options
,
197 #ifdef HAVE_GETOPT_LONG
210 action
= FILE_COMPILE
;
213 flags
|= MAGIC_DEBUG
|MAGIC_CHECK
;
218 load(magicfile
, flags
);
229 flags
|= MAGIC_CONTINUE
;
240 #if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
242 flags
|= MAGIC_PRESERVE_ATIME
;
249 flags
|= MAGIC_DEVICES
;
252 (void) fprintf(stdout
, "%s-%d.%.2d\n", progname
,
253 FILE_VERSION_MAJOR
, patchlevel
);
254 (void) fprintf(stdout
, "magic file from %s\n",
258 flags
|= MAGIC_COMPRESS
;
262 flags
|= MAGIC_SYMLINK
;
278 magic
= magic_open(flags
|MAGIC_CHECK
);
280 (void)fprintf(stderr
, "%s: %s\n", progname
,
284 c
= action
== FILE_CHECK
? magic_check(magic
, magicfile
) :
285 magic_compile(magic
, magicfile
);
287 (void)fprintf(stderr
, "%s: %s\n", progname
,
293 load(magicfile
, flags
);
297 if (optind
== argc
) {
304 for (wid
= 0, i
= optind
; i
< argc
; i
++) {
305 nw
= file_mbswidth(argv
[i
]);
309 for (; optind
< argc
; optind
++)
310 process(argv
[optind
], wid
);
319 load(const char *m
, int flags
)
323 magic
= magic_open(flags
);
325 (void)fprintf(stderr
, "%s: %s\n", progname
, strerror(errno
));
328 if (magic_load(magic
, magicfile
) == -1) {
329 (void)fprintf(stderr
, "%s: %s\n",
330 progname
, magic_error(magic
));
336 * unwrap -- read a file of filenames, do each one.
341 char buf
[MAXPATHLEN
];
345 if (strcmp("-", fn
) == 0) {
349 if ((f
= fopen(fn
, "r")) == NULL
) {
350 (void)fprintf(stderr
, "%s: Cannot open `%s' (%s).\n",
351 progname
, fn
, strerror(errno
));
355 while (fgets(buf
, MAXPATHLEN
, f
) != NULL
) {
356 cwid
= file_mbswidth(buf
) - 1;
364 while (fgets(buf
, MAXPATHLEN
, f
) != NULL
) {
365 buf
[file_mbswidth(buf
)-1] = '\0';
368 (void) fflush(stdout
);
375 process(const char *inname
, int wid
)
378 int std_in
= strcmp(inname
, "-") == 0;
380 if (wid
> 0 && !bflag
)
381 (void) printf("%s%s%*s ", std_in
? "/dev/stdin" : inname
,
382 separator
, (int) (nopad
? 0 : (wid
- file_mbswidth(inname
))), "");
384 type
= magic_file(magic
, std_in
? NULL
: inname
);
386 printf("ERROR: %s\n", magic_error(magic
));
388 printf("%s\n", type
);
396 * from 4 byte quantity to convert
397 * same whether to perform byte swapping
398 * big_endian whether we are a big endian host
401 byteconv4(int from
, int same
, int big_endian
)
405 else if (big_endian
) { /* lsb -> msb conversion on msb */
412 retval
.c
[0] = tmpval
.c
[3];
413 retval
.c
[1] = tmpval
.c
[2];
414 retval
.c
[2] = tmpval
.c
[1];
415 retval
.c
[3] = tmpval
.c
[0];
420 return ntohl(from
); /* msb -> lsb conversion on lsb */
425 * Same as byteconv4, but for shorts
428 byteconv2(int from
, int same
, int big_endian
)
432 else if (big_endian
) { /* lsb -> msb conversion on msb */
438 tmpval
.s
= (short) from
;
439 retval
.c
[0] = tmpval
.c
[1];
440 retval
.c
[1] = tmpval
.c
[0];
445 return ntohs(from
); /* msb -> lsb conversion on lsb */
450 file_mbswidth(const char *s
)
452 #if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH)
453 size_t bytesconsumed
, old_n
, n
, width
= 0;
456 (void)memset(&state
, 0, sizeof(mbstate_t));
457 old_n
= n
= strlen(s
);
460 bytesconsumed
= mbrtowc(&nextchar
, s
, n
, &state
);
461 if (bytesconsumed
== (size_t)(-1) ||
462 bytesconsumed
== (size_t)(-2)) {
463 /* Something went wrong, return something reasonable */
468 * do what strlen() would do, so that caller
473 width
+= wcwidth(nextchar
);
475 s
+= bytesconsumed
, n
-= bytesconsumed
;
486 (void)fprintf(stderr
, USAGE
, progname
, progname
);
487 #ifdef HAVE_GETOPT_LONG
488 (void)fputs("Try `file --help' for more information.\n", stderr
);
493 #ifdef HAVE_GETOPT_LONG
498 "Usage: file [OPTION]... [FILE]...\n"
499 "Determine file type of FILEs.\n"
501 " -m, --magic-file LIST use LIST as a colon-separated list of magic\n"
503 " -z, --uncompress try to look inside compressed files\n"
504 " -b, --brief do not prepend filenames to output lines\n"
505 " -c, --checking-printout print the parsed form of the magic file, use in\n"
506 " conjunction with -m to debug a new magic file\n"
507 " before installing it\n"
508 " -f, --files-from FILE read the filenames to be examined from FILE\n"
509 " -F, --separator string use string as separator instead of `:'\n"
510 " -i, --mime output mime type strings\n"
511 " -k, --keep-going don't stop at the first match\n"
512 " -L, --dereference causes symlinks to be followed\n"
513 " -n, --no-buffer do not buffer output\n"
514 " -N, --no-pad do not pad output\n"
515 " -p, --preserve-date preserve access times on files\n"
516 " -r, --raw don't translate unprintable chars to \\ooo\n"
517 " -s, --special-files treat special (block/char devices) files as\n"
519 " --help display this help and exit\n"
520 " --version output version information and exit\n"