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.
35 FILE_RCSID("@(#)$File: file.c,v 1.204 2022/09/13 18:46:07 christos Exp $")
44 # if (__COHERENT__ >= 0x420)
45 # include <sys/utime.h>
48 # include <sys/time.h>
55 #include <unistd.h> /* for read() */
63 #if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH) && \
64 defined(HAVE_WCTYPE_H)
65 #define FILE_WIDE_SUPPORT
70 #if defined(HAVE_GETOPT_H) && defined(HAVE_STRUCT_OPTION)
72 # ifndef HAVE_GETOPT_LONG
73 int getopt_long(int, char * const *, const char *,
74 const struct option
*, int *);
77 # include "mygetopt.h"
88 #define FILE_FLAGS "bcCdE" IFLNK_h "ik" IFLNK_L "lNnprsSvzZ0"
89 #define OPTSTRING "bcCde:Ef:F:hiklLm:nNpP:rsSvzZ0"
92 "Usage: %s [-" FILE_FLAGS "] [--apple] [--extension] [--mime-encoding]\n" \
93 " [--mime-type] [-e <testname>] [-F <separator>] " \
94 " [-f <namefile>]\n" \
95 " [-m <magicfiles>] [-P <parameter=value>] [--exclude-quiet]\n" \
97 " %s -C [-m <magicfiles>]\n" \
100 private int /* Global command-line options */
101 bflag
= 0, /* brief output format */
102 nopad
= 0, /* Don't pad output */
103 nobuffer
= 0, /* Do not buffer stdout */
104 nulsep
= 0; /* Append '\0' to the separator */
106 private const char *separator
= ":"; /* Default field separator */
107 private const struct option long_options
[] = {
110 #define OPT_EXTENSIONS 3
111 #define OPT_MIME_TYPE 4
112 #define OPT_MIME_ENCODING 5
113 #define OPT_EXCLUDE_QUIET 6
114 #define OPT(shortname, longname, opt, def, doc) \
115 {longname, opt, NULL, shortname},
116 #define OPT_LONGONLY(longname, opt, def, doc, id) \
117 {longname, opt, NULL, id},
118 #include "file_opts.h"
124 private const struct {
128 { "apptype", MAGIC_NO_CHECK_APPTYPE
},
129 { "ascii", MAGIC_NO_CHECK_ASCII
},
130 { "cdf", MAGIC_NO_CHECK_CDF
},
131 { "compress", MAGIC_NO_CHECK_COMPRESS
},
132 { "csv", MAGIC_NO_CHECK_CSV
},
133 { "elf", MAGIC_NO_CHECK_ELF
},
134 { "encoding", MAGIC_NO_CHECK_ENCODING
},
135 { "soft", MAGIC_NO_CHECK_SOFT
},
136 { "tar", MAGIC_NO_CHECK_TAR
},
137 { "json", MAGIC_NO_CHECK_JSON
},
138 { "text", MAGIC_NO_CHECK_TEXT
}, /* synonym for ascii */
139 { "tokens", MAGIC_NO_CHECK_TOKENS
}, /* OBSOLETE: ignored for backwards compatibility */
150 { "bytes", 0, FILE_BYTES_MAX
, "max bytes to look inside file",
151 MAGIC_PARAM_BYTES_MAX
, 0 },
152 { "elf_notes", 0, FILE_ELF_NOTES_MAX
, "max ELF notes processed",
153 MAGIC_PARAM_ELF_NOTES_MAX
, 0 },
154 { "elf_phnum", 0, FILE_ELF_PHNUM_MAX
, "max ELF prog sections processed",
155 MAGIC_PARAM_ELF_PHNUM_MAX
, 0 },
156 { "elf_shnum", 0, FILE_ELF_SHNUM_MAX
, "max ELF sections processed",
157 MAGIC_PARAM_ELF_SHNUM_MAX
, 0 },
158 { "encoding", 0, FILE_ENCODING_MAX
, "max bytes to scan for encoding",
159 MAGIC_PARAM_ENCODING_MAX
, 0 },
160 { "indir", 0, FILE_INDIR_MAX
, "recursion limit for indirection",
161 MAGIC_PARAM_INDIR_MAX
, 0 },
162 { "name", 0, FILE_NAME_MAX
, "use limit for name/use magic",
163 MAGIC_PARAM_NAME_MAX
, 0 },
164 { "regex", 0, FILE_REGEX_MAX
, "length limit for REGEX searches",
165 MAGIC_PARAM_REGEX_MAX
, 0 },
173 private void usage(void);
174 private void docprint(const char *, int);
178 private void help(void);
180 private int unwrap(struct magic_set
*, const char *);
181 private int process(struct magic_set
*ms
, const char *, int);
182 private struct magic_set
*load(const char *, int);
183 private void setparam(const char *);
184 private void applyparam(magic_t
);
188 * main - parse arguments and handle options
191 main(int argc
, char *argv
[])
194 size_t i
, j
, wid
, nw
;
195 int action
= 0, didsomefiles
= 0, errflg
= 0;
196 int flags
= 0, e
= 0;
197 #ifdef HAVE_LIBSECCOMP
200 struct magic_set
*magic
= NULL
;
202 const char *magicfile
= NULL
; /* where the magic is */
205 /* makes islower etc work for other langs */
206 (void)setlocale(LC_CTYPE
, "");
209 /* sh-like wildcard expansion! Shouldn't hurt at least ... */
210 _wildcard(&argc
, &argv
);
213 if ((progname
= strrchr(argv
[0], '/')) != NULL
)
218 file_setprogname(progname
);
222 posixly
= getenv("POSIXLY_CORRECT") != NULL
;
223 flags
|= posixly
? MAGIC_SYMLINK
: 0;
225 while ((c
= getopt_long(argc
, argv
, OPTSTRING
, long_options
,
232 flags
|= MAGIC_APPLE
;
235 flags
|= MAGIC_EXTENSION
;
238 flags
|= MAGIC_MIME_TYPE
;
240 case OPT_MIME_ENCODING
:
241 flags
|= MAGIC_MIME_ENCODING
;
253 action
= FILE_COMPILE
;
256 flags
|= MAGIC_DEBUG
|MAGIC_CHECK
;
259 flags
|= MAGIC_ERROR
;
262 case OPT_EXCLUDE_QUIET
:
263 for (i
= 0; i
< __arraycount(nv
); i
++)
264 if (strcmp(nv
[i
].name
, optarg
) == 0)
267 if (i
== __arraycount(nv
)) {
268 if (c
!= OPT_EXCLUDE_QUIET
)
271 flags
|= nv
[i
].value
;
278 if ((magic
= load(magicfile
, flags
)) == NULL
)
281 e
|= unwrap(magic
, optarg
);
291 flags
|= MAGIC_CONTINUE
;
305 #if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
307 flags
|= MAGIC_PRESERVE_ATIME
;
317 flags
|= MAGIC_DEVICES
;
320 #ifdef HAVE_LIBSECCOMP
325 if (magicfile
== NULL
)
326 magicfile
= magic_getpath(magicfile
, action
);
327 (void)fprintf(stdout
, "%s-%s\n", file_getprogname(),
329 (void)fprintf(stdout
, "magic file from %s\n",
331 #ifdef HAVE_LIBSECCOMP
332 (void)fprintf(stdout
, "seccomp support included\n");
336 flags
|= MAGIC_COMPRESS
;
340 flags
|= MAGIC_COMPRESS
|MAGIC_COMPRESS_TRANSP
;
344 flags
|= MAGIC_SYMLINK
;
347 flags
&= ~MAGIC_SYMLINK
;
362 #ifdef HAVE_LIBSECCOMP
364 if (sandbox
&& enable_sandbox_basic() == -1)
366 if (sandbox
&& enable_sandbox_full() == -1)
368 file_err(EXIT_FAILURE
, "SECCOMP initialisation failed");
369 #endif /* HAVE_LIBSECCOMP */
371 if (MAGIC_VERSION
!= magic_version())
372 file_warnx("Compiled magic version [%d] "
373 "does not match with shared library magic version [%d]\n",
374 MAGIC_VERSION
, magic_version());
381 * Don't try to check/compile ~/.magic unless we explicitly
384 magic
= magic_open(flags
|MAGIC_CHECK
);
386 file_warn("Can't create magic");
393 c
= magic_check(magic
, magicfile
);
396 c
= magic_compile(magic
, magicfile
);
399 c
= magic_list(magic
, magicfile
);
405 file_warnx("%s", magic_error(magic
));
412 if ((magic
= load(magicfile
, flags
)) == NULL
)
417 if (optind
== argc
) {
423 for (wid
= 0, j
= CAST(size_t, optind
); j
< CAST(size_t, argc
);
425 nw
= file_mbswidth(magic
, argv
[j
]);
431 * If bflag is only set twice, set it depending on
432 * number of files [this is undocumented, and subject to change]
435 bflag
= optind
>= argc
- 1;
437 for (; optind
< argc
; optind
++)
438 e
|= process(magic
, argv
[optind
], wid
);
442 e
|= fflush(stdout
) != 0;
450 applyparam(magic_t magic
)
454 for (i
= 0; i
< __arraycount(pm
); i
++) {
457 if (magic_setparam(magic
, pm
[i
].tag
, &pm
[i
].value
) == -1)
458 file_err(EXIT_FAILURE
, "Can't set %s", pm
[i
].name
);
463 setparam(const char *p
)
468 if ((s
= CCAST(char *, strchr(p
, '='))) == NULL
)
471 for (i
= 0; i
< __arraycount(pm
); i
++) {
472 if (strncmp(p
, pm
[i
].name
, s
- p
) != 0)
474 pm
[i
].value
= atoi(s
+ 1);
479 file_errx(EXIT_FAILURE
, "Unknown param %s", p
);
482 private struct magic_set
*
484 load(const char *magicfile
, int flags
)
486 struct magic_set
*magic
= magic_open(flags
);
490 file_warn("Can't create magic");
493 if (magic_load(magic
, magicfile
) == -1) {
494 file_warn("%s", magic_error(magic
));
498 if ((e
= magic_error(magic
)) != NULL
)
504 * unwrap -- read a file of filenames, do each one.
507 unwrap(struct magic_set
*ms
, const char *fn
)
515 size_t fi
= 0, fimax
= 100;
516 char **flist
= CAST(char **, malloc(sizeof(*flist
) * fimax
));
519 out
: file_err(EXIT_FAILURE
, "Cannot allocate memory for file list");
521 if (strcmp("-", fn
) == 0)
524 if ((f
= fopen(fn
, "r")) == NULL
) {
525 file_warn("Cannot open `%s'", fn
);
530 while ((len
= getline(&line
, &llen
, f
)) > 0) {
531 if (line
[len
- 1] == '\n')
532 line
[len
- 1] = '\0';
535 char **nf
= CAST(char **,
536 realloc(flist
, fimax
* sizeof(*flist
)));
542 cwid
= file_mbswidth(ms
, line
);
550 for (fi
= 0; fi
< fimax
; fi
++) {
551 e
|= process(ms
, flist
[fi
], wid
);
562 file_octal(unsigned char c
)
565 putc(((c
>> 6) & 7) + '0', stdout
);
566 putc(((c
>> 3) & 7) + '0', stdout
);
567 putc(((c
>> 0) & 7) + '0', stdout
);
571 fname_print(const char *inname
)
573 size_t n
= strlen(inname
);
574 #ifdef FILE_WIDE_SUPPORT
577 size_t bytesconsumed
;
580 (void)memset(&state
, 0, sizeof(state
));
582 bytesconsumed
= mbrtowc(&nextchar
, inname
, n
, &state
);
583 if (bytesconsumed
== CAST(size_t, -1) ||
584 bytesconsumed
== CAST(size_t, -2)) {
585 nextchar
= *inname
++;
587 (void)memset(&state
, 0, sizeof(state
));
588 file_octal(CAST(unsigned char, nextchar
));
591 inname
+= bytesconsumed
;
593 if (iswprint(nextchar
)) {
594 printf("%lc", nextchar
);
597 /* XXX: What if it is > 255? */
598 file_octal(CAST(unsigned char, nextchar
));
602 for (i
= 0; i
< n
; i
++) {
603 unsigned char c
= CAST(unsigned char, inname
[i
]);
614 * Called for each input file on the command line (or in a list of files)
617 process(struct magic_set
*ms
, const char *inname
, int wid
)
619 const char *type
, c
= nulsep
> 1 ? '\0' : '\n';
620 int std_in
= strcmp(inname
, "-") == 0;
623 if (wid
> 0 && !bflag
) {
624 const char *pname
= std_in
? "/dev/stdin" : inname
;
625 if ((ms
->flags
& MAGIC_RAW
) == 0)
628 (void)printf("%s", pname
);
630 (void)putc('\0', stdout
);
632 (void)printf("%s", separator
);
633 (void)printf("%*s ", CAST(int, nopad
? 0
634 : (wid
- file_mbswidth(ms
, inname
))), "");
638 type
= magic_file(ms
, std_in
? NULL
: inname
);
641 haderror
|= printf("ERROR: %s%c", magic_error(ms
), c
);
643 haderror
|= printf("%s%c", type
, c
) < 0;
646 haderror
|= fflush(stdout
) != 0;
647 return haderror
|| type
== NULL
;
651 file_mbswidth(struct magic_set
*ms
, const char *s
)
654 #ifdef FILE_WIDE_SUPPORT
655 size_t bytesconsumed
, n
;
659 (void)memset(&state
, 0, sizeof(state
));
663 bytesconsumed
= mbrtowc(&nextchar
, s
, n
, &state
);
664 if (bytesconsumed
== CAST(size_t, -1) ||
665 bytesconsumed
== CAST(size_t, -2)) {
668 (void)memset(&state
, 0, sizeof(state
));
671 int w
= wcwidth(nextchar
);
672 width
+= ((ms
->flags
& MAGIC_RAW
) != 0
673 || iswprint(nextchar
)) ? (w
> 0 ? w
: 1) : 4;
676 s
+= bytesconsumed
, n
-= bytesconsumed
;
680 width
+= (ms
->flags
& MAGIC_RAW
) != 0
681 || isprint(CAST(unsigned char, *s
)) ? 1 : 4;
690 const char *pn
= file_getprogname();
691 (void)fprintf(stderr
, USAGE
, pn
, pn
, pn
);
700 if (((def
& 1) && posixly
) || ((def
& 2) && !posixly
))
701 fprintf(stdout
, " (default)");
706 docprint(const char *opts
, int def
)
712 p
= CCAST(char *, strchr(opts
, '%'));
714 fprintf(stdout
, "%s", opts
);
719 for (sp
= p
- 1; sp
> opts
&& *sp
== ' '; sp
--)
722 fprintf(stdout
, "%.*s", CAST(int, p
- opts
), opts
);
723 pad
= (int)CAST(int, p
- sp
- 1);
728 for (i
= 0; i
< __arraycount(nv
); i
++) {
729 fprintf(stdout
, "%s%s", comma
++ ? ", " : "", nv
[i
].name
);
730 if (i
&& i
% 5 == 0 && i
!= __arraycount(nv
) - 1) {
731 fprintf(stdout
, ",\n%*s", pad
, "");
737 for (i
= 0; i
< __arraycount(pm
); i
++) {
738 fprintf(stdout
, "%9s %7zu %s", pm
[i
].name
, pm
[i
].def
,
740 if (i
!= __arraycount(pm
) - 1)
741 fprintf(stdout
, "\n%*s", pad
, "");
745 file_errx(EXIT_FAILURE
, "Unknown escape `%c' in long options",
749 fprintf(stdout
, "%s", opts
+ (p
- opts
) + 1);
757 "Usage: file [OPTION...] [FILE...]\n"
758 "Determine type of FILEs.\n"
760 #define OPT(shortname, longname, opt, def, doc) \
761 fprintf(stdout, " -%c, --" longname, shortname), \
763 #define OPT_LONGONLY(longname, opt, def, doc, id) \
764 fprintf(stdout, " --" longname), \
766 #include "file_opts.h"
769 fprintf(stdout
, "\nReport bugs to https://bugs.astron.com/\n");
773 private const char *file_progname
;
776 file_setprogname(const char *progname
)
778 file_progname
= progname
;
781 protected const char *
782 file_getprogname(void)
784 return file_progname
;
788 file_err(int e
, const char *fmt
, ...)
794 fprintf(stderr
, "%s: ", file_progname
);
795 vfprintf(stderr
, fmt
, ap
);
798 fprintf(stderr
, " (%s)\n", strerror(se
));
805 file_errx(int e
, const char *fmt
, ...)
810 fprintf(stderr
, "%s: ", file_progname
);
811 vfprintf(stderr
, fmt
, ap
);
813 fprintf(stderr
, "\n");
818 file_warn(const char *fmt
, ...)
824 fprintf(stderr
, "%s: ", file_progname
);
825 vfprintf(stderr
, fmt
, ap
);
828 fprintf(stderr
, " (%s)\n", strerror(se
));
835 file_warnx(const char *fmt
, ...)
841 fprintf(stderr
, "%s: ", file_progname
);
842 vfprintf(stderr
, fmt
, ap
);
844 fprintf(stderr
, "\n");