summaryrefslogtreecommitdiffstats
path: root/src/file.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/file.c856
1 files changed, 856 insertions, 0 deletions
diff --git a/src/file.c b/src/file.c
new file mode 100644
index 0000000..c1c3fb9
--- /dev/null
+++ b/src/file.c
@@ -0,0 +1,856 @@
+/*
+ * Copyright (c) Ian F. Darwin 1986-1995.
+ * Software written by Ian F. Darwin and others;
+ * maintained 1995-present by Christos Zoulas and others.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * file - find type of a file or files - main program.
+ */
+
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: file.c,v 1.213 2022/12/26 17:31:14 christos Exp $")
+#endif /* lint */
+
+#include "magic.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#ifdef RESTORE_TIME
+# if (__COHERENT__ >= 0x420)
+# include <sys/utime.h>
+# else
+# ifdef USE_UTIMES
+# include <sys/time.h>
+# else
+# include <utime.h>
+# endif
+# endif
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for read() */
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+#ifdef HAVE_WCTYPE_H
+#include <wctype.h>
+#endif
+#if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH) && \
+ defined(HAVE_WCTYPE_H)
+#define FILE_WIDE_SUPPORT
+#else
+#include <ctype.h>
+#endif
+
+#if defined(HAVE_GETOPT_H) && defined(HAVE_STRUCT_OPTION)
+# include <getopt.h>
+# ifndef HAVE_GETOPT_LONG
+int getopt_long(int, char * const *, const char *,
+ const struct option *, int *);
+# endif
+# else
+# include "mygetopt.h"
+#endif
+
+#ifdef S_IFLNK
+# define IFLNK_h "h"
+# define IFLNK_L "L"
+#else
+# define IFLNK_h ""
+# define IFLNK_L ""
+#endif
+
+#define FILE_FLAGS "bcCdE" IFLNK_h "ik" IFLNK_L "lNnprsSvzZ0"
+#define OPTSTRING "bcCde:Ef:F:hiklLm:nNpP:rsSvzZ0"
+
+# define USAGE \
+ "Usage: %s [-" FILE_FLAGS "] [--apple] [--extension] [--mime-encoding]\n" \
+ " [--mime-type] [-e <testname>] [-F <separator>] " \
+ " [-f <namefile>]\n" \
+ " [-m <magicfiles>] [-P <parameter=value>] [--exclude-quiet]\n" \
+ " <file> ...\n" \
+ " %s -C [-m <magicfiles>]\n" \
+ " %s [--help]\n"
+
+file_private int /* Global command-line options */
+ bflag = 0, /* brief output format */
+ nopad = 0, /* Don't pad output */
+ nobuffer = 0, /* Do not buffer stdout */
+ nulsep = 0; /* Append '\0' to the separator */
+
+file_private const char *separator = ":"; /* Default field separator */
+file_private const struct option long_options[] = {
+#define OPT_HELP 1
+#define OPT_APPLE 2
+#define OPT_EXTENSIONS 3
+#define OPT_MIME_TYPE 4
+#define OPT_MIME_ENCODING 5
+#define OPT_EXCLUDE_QUIET 6
+#define OPT(shortname, longname, opt, def, doc) \
+ {longname, opt, NULL, shortname},
+#define OPT_LONGONLY(longname, opt, def, doc, id) \
+ {longname, opt, NULL, id},
+#include "file_opts.h"
+#undef OPT
+#undef OPT_LONGONLY
+ {0, 0, NULL, 0}
+ };
+
+file_private const struct {
+ const char *name;
+ int value;
+} nv[] = {
+ { "apptype", MAGIC_NO_CHECK_APPTYPE },
+ { "ascii", MAGIC_NO_CHECK_ASCII },
+ { "cdf", MAGIC_NO_CHECK_CDF },
+ { "compress", MAGIC_NO_CHECK_COMPRESS },
+ { "csv", MAGIC_NO_CHECK_CSV },
+ { "elf", MAGIC_NO_CHECK_ELF },
+ { "encoding", MAGIC_NO_CHECK_ENCODING },
+ { "soft", MAGIC_NO_CHECK_SOFT },
+ { "tar", MAGIC_NO_CHECK_TAR },
+ { "json", MAGIC_NO_CHECK_JSON },
+ { "text", MAGIC_NO_CHECK_TEXT }, /* synonym for ascii */
+ { "tokens", MAGIC_NO_CHECK_TOKENS }, /* OBSOLETE: ignored for backwards compatibility */
+};
+
+file_private struct {
+ const char *name;
+ size_t value;
+ size_t def;
+ const char *desc;
+ int tag;
+ int set;
+} pm[] = {
+ { "bytes", 0, FILE_BYTES_MAX, "max bytes to look inside file",
+ MAGIC_PARAM_BYTES_MAX, 0 },
+ { "elf_notes", 0, FILE_ELF_NOTES_MAX, "max ELF notes processed",
+ MAGIC_PARAM_ELF_NOTES_MAX, 0 },
+ { "elf_phnum", 0, FILE_ELF_PHNUM_MAX, "max ELF prog sections processed",
+ MAGIC_PARAM_ELF_PHNUM_MAX, 0 },
+ { "elf_shnum", 0, FILE_ELF_SHNUM_MAX, "max ELF sections processed",
+ MAGIC_PARAM_ELF_SHNUM_MAX, 0 },
+ { "encoding", 0, FILE_ENCODING_MAX, "max bytes to scan for encoding",
+ MAGIC_PARAM_ENCODING_MAX, 0 },
+ { "indir", 0, FILE_INDIR_MAX, "recursion limit for indirection",
+ MAGIC_PARAM_INDIR_MAX, 0 },
+ { "name", 0, FILE_NAME_MAX, "use limit for name/use magic",
+ MAGIC_PARAM_NAME_MAX, 0 },
+ { "regex", 0, FILE_REGEX_MAX, "length limit for REGEX searches",
+ MAGIC_PARAM_REGEX_MAX, 0 },
+};
+
+file_private int posixly;
+
+#ifdef __dead
+__dead
+#endif
+file_private void usage(void);
+file_private void docprint(const char *, int);
+#ifdef __dead
+__dead
+#endif
+file_private void help(void);
+
+file_private int unwrap(struct magic_set *, const char *);
+file_private int process(struct magic_set *ms, const char *, int);
+file_private struct magic_set *load(const char *, int);
+file_private void setparam(const char *);
+file_private void applyparam(magic_t);
+
+
+/*
+ * main - parse arguments and handle options
+ */
+int
+main(int argc, char *argv[])
+{
+ int c;
+ size_t i, j, wid, nw;
+ int action = 0, didsomefiles = 0, errflg = 0;
+ int flags = 0, e = 0;
+#ifdef HAVE_LIBSECCOMP
+ int sandbox = 1;
+#endif
+ struct magic_set *magic = NULL;
+ int longindex;
+ const char *magicfile = NULL; /* where the magic is */
+ char *progname;
+
+ /* makes islower etc work for other langs */
+ (void)setlocale(LC_CTYPE, "");
+
+#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];
+
+ file_setprogname(progname);
+
+
+#ifdef S_IFLNK
+ posixly = getenv("POSIXLY_CORRECT") != NULL;
+ flags |= posixly ? MAGIC_SYMLINK : 0;
+#endif
+ while ((c = getopt_long(argc, argv, OPTSTRING, long_options,
+ &longindex)) != -1)
+ switch (c) {
+ case OPT_HELP:
+ help();
+ break;
+ case OPT_APPLE:
+ flags |= MAGIC_APPLE;
+ break;
+ case OPT_EXTENSIONS:
+ flags |= MAGIC_EXTENSION;
+ break;
+ case OPT_MIME_TYPE:
+ flags |= MAGIC_MIME_TYPE;
+ break;
+ case OPT_MIME_ENCODING:
+ flags |= MAGIC_MIME_ENCODING;
+ break;
+ case '0':
+ nulsep++;
+ break;
+ case 'b':
+ bflag++;
+ break;
+ case 'c':
+ action = FILE_CHECK;
+ break;
+ case 'C':
+ action = FILE_COMPILE;
+ break;
+ case 'd':
+ flags |= MAGIC_DEBUG|MAGIC_CHECK;
+ break;
+ case 'E':
+ flags |= MAGIC_ERROR;
+ break;
+ case 'e':
+ case OPT_EXCLUDE_QUIET:
+ for (i = 0; i < __arraycount(nv); i++)
+ if (strcmp(nv[i].name, optarg) == 0)
+ break;
+
+ if (i == __arraycount(nv)) {
+ if (c != OPT_EXCLUDE_QUIET)
+ errflg++;
+ } else
+ flags |= nv[i].value;
+ break;
+
+ case 'f':
+ if(action)
+ usage();
+ if (magic == NULL)
+ if ((magic = load(magicfile, flags)) == NULL)
+ return 1;
+ applyparam(magic);
+ e |= unwrap(magic, optarg);
+ ++didsomefiles;
+ break;
+ case 'F':
+ separator = optarg;
+ break;
+ case 'i':
+ flags |= MAGIC_MIME;
+ break;
+ case 'k':
+ flags |= MAGIC_CONTINUE;
+ break;
+ case 'l':
+ action = FILE_LIST;
+ break;
+ case 'm':
+ magicfile = optarg;
+ 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 'P':
+ setparam(optarg);
+ break;
+ case 'r':
+ flags |= MAGIC_RAW;
+ break;
+ case 's':
+ flags |= MAGIC_DEVICES;
+ break;
+ case 'S':
+#ifdef HAVE_LIBSECCOMP
+ sandbox = 0;
+#endif
+ break;
+ case 'v':
+ if (magicfile == NULL)
+ magicfile = magic_getpath(magicfile, action);
+ (void)fprintf(stdout, "%s-%s\n", file_getprogname(),
+ VERSION);
+ (void)fprintf(stdout, "magic file from %s\n",
+ magicfile);
+#ifdef HAVE_LIBSECCOMP
+ (void)fprintf(stdout, "seccomp support included\n");
+#endif
+ return 0;
+ case 'z':
+ flags |= MAGIC_COMPRESS;
+ break;
+
+ case 'Z':
+ flags |= MAGIC_COMPRESS|MAGIC_COMPRESS_TRANSP;
+ break;
+#ifdef S_IFLNK
+ case 'L':
+ flags |= MAGIC_SYMLINK;
+ break;
+ case 'h':
+ flags &= ~MAGIC_SYMLINK;
+ break;
+#endif
+ case '?':
+ default:
+ errflg++;
+ break;
+ }
+
+ if (errflg) {
+ usage();
+ }
+ if (e)
+ return e;
+
+#ifdef HAVE_LIBSECCOMP
+#if 0
+ if (sandbox && enable_sandbox_basic() == -1)
+#else
+ if (sandbox && enable_sandbox_full() == -1)
+#endif
+ file_err(EXIT_FAILURE, "SECCOMP initialisation failed");
+ if (sandbox)
+ flags |= MAGIC_NO_COMPRESS_FORK;
+#endif /* HAVE_LIBSECCOMP */
+
+ if (MAGIC_VERSION != magic_version())
+ file_warnx("Compiled magic version [%d] "
+ "does not match with shared library magic version [%d]\n",
+ MAGIC_VERSION, magic_version());
+
+ switch(action) {
+ case FILE_CHECK:
+ case FILE_COMPILE:
+ case FILE_LIST:
+ /*
+ * Don't try to check/compile ~/.magic unless we explicitly
+ * ask for it.
+ */
+ magic = magic_open(flags|MAGIC_CHECK);
+ if (magic == NULL) {
+ file_warn("Can't create magic");
+ return 1;
+ }
+
+
+ switch(action) {
+ case FILE_CHECK:
+ c = magic_check(magic, magicfile);
+ break;
+ case FILE_COMPILE:
+ c = magic_compile(magic, magicfile);
+ break;
+ case FILE_LIST:
+ c = magic_list(magic, magicfile);
+ break;
+ default:
+ abort();
+ }
+ if (c == -1) {
+ file_warnx("%s", magic_error(magic));
+ e = 1;
+ goto out;
+ }
+ goto out;
+ default:
+ if (magic == NULL)
+ if ((magic = load(magicfile, flags)) == NULL)
+ return 1;
+ applyparam(magic);
+ }
+
+ if (optind == argc) {
+ if (!didsomefiles)
+ usage();
+ goto out;
+ }
+
+ for (wid = 0, j = CAST(size_t, optind); j < CAST(size_t, argc);
+ j++) {
+ nw = file_mbswidth(magic, argv[j]);
+ if (nw > wid)
+ wid = nw;
+ }
+
+ /*
+ * If bflag is only set twice, set it depending on
+ * number of files [this is undocumented, and subject to change]
+ */
+ if (bflag == 2) {
+ bflag = optind >= argc - 1;
+ }
+ for (; optind < argc; optind++)
+ e |= process(magic, argv[optind], wid);
+
+out:
+ if (!nobuffer)
+ e |= fflush(stdout) != 0;
+
+ if (magic)
+ magic_close(magic);
+ return e;
+}
+
+file_private void
+applyparam(magic_t magic)
+{
+ size_t i;
+
+ for (i = 0; i < __arraycount(pm); i++) {
+ if (!pm[i].set)
+ continue;
+ if (magic_setparam(magic, pm[i].tag, &pm[i].value) == -1)
+ file_err(EXIT_FAILURE, "Can't set %s", pm[i].name);
+ }
+}
+
+file_private void
+setparam(const char *p)
+{
+ size_t i;
+ char *s;
+
+ if ((s = CCAST(char *, strchr(p, '='))) == NULL)
+ goto badparm;
+
+ for (i = 0; i < __arraycount(pm); i++) {
+ if (strncmp(p, pm[i].name, s - p) != 0)
+ continue;
+ pm[i].value = atoi(s + 1);
+ pm[i].set = 1;
+ return;
+ }
+badparm:
+ file_errx(EXIT_FAILURE, "Unknown param %s", p);
+}
+
+file_private struct magic_set *
+/*ARGSUSED*/
+load(const char *magicfile, int flags)
+{
+ struct magic_set *magic = magic_open(flags);
+ const char *e;
+
+ if (magic == NULL) {
+ file_warn("Can't create magic");
+ return NULL;
+ }
+ if (magic_load(magic, magicfile) == -1) {
+ file_warn("%s", magic_error(magic));
+ magic_close(magic);
+ return NULL;
+ }
+ if ((e = magic_error(magic)) != NULL)
+ file_warn("%s", e);
+ return magic;
+}
+
+/*
+ * unwrap -- read a file of filenames, do each one.
+ */
+file_private int
+unwrap(struct magic_set *ms, const char *fn)
+{
+ FILE *f;
+ ssize_t len;
+ char *line = NULL;
+ size_t llen = 0;
+ int wid = 0, cwid;
+ int e = 0;
+ size_t fi = 0, fimax = 0;
+ char **flist = NULL;
+
+ if (strcmp("-", fn) == 0)
+ f = stdin;
+ else {
+ if ((f = fopen(fn, "r")) == NULL) {
+ file_warn("Cannot open `%s'", fn);
+ return 1;
+ }
+ }
+
+ while ((len = getline(&line, &llen, f)) > 0) {
+ if (line[len - 1] == '\n')
+ line[len - 1] = '\0';
+ cwid = file_mbswidth(ms, line);
+ if (nobuffer) {
+ e |= process(ms, line, cwid);
+ free(line);
+ line = NULL;
+ llen = 0;
+ continue;
+ }
+ if (cwid > wid)
+ wid = cwid;
+ if (fi >= fimax) {
+ fimax += 100;
+ char **nf = CAST(char **,
+ realloc(flist, fimax * sizeof(*flist)));
+ if (nf == NULL) {
+ file_err(EXIT_FAILURE,
+ "Cannot allocate memory for file list");
+ }
+ flist = nf;
+ }
+ flist[fi++] = line;
+ line = NULL;
+ llen = 0;
+ }
+
+ if (!nobuffer) {
+ fimax = fi;
+ for (fi = 0; fi < fimax; fi++) {
+ e |= process(ms, flist[fi], wid);
+ free(flist[fi]);
+ }
+ }
+ free(flist);
+
+ if (f != stdin)
+ (void)fclose(f);
+ return e;
+}
+
+file_private void
+file_octal(unsigned char c)
+{
+ (void)putc('\\', stdout);
+ (void)putc(((c >> 6) & 7) + '0', stdout);
+ (void)putc(((c >> 3) & 7) + '0', stdout);
+ (void)putc(((c >> 0) & 7) + '0', stdout);
+}
+
+file_private void
+fname_print(const char *inname)
+{
+ size_t n = strlen(inname);
+#ifdef FILE_WIDE_SUPPORT
+ mbstate_t state;
+ wchar_t nextchar;
+ size_t bytesconsumed;
+
+
+ (void)memset(&state, 0, sizeof(state));
+ while (n > 0) {
+ bytesconsumed = mbrtowc(&nextchar, inname, n, &state);
+ if (bytesconsumed == CAST(size_t, -1) ||
+ bytesconsumed == CAST(size_t, -2)) {
+ nextchar = *inname++;
+ n--;
+ (void)memset(&state, 0, sizeof(state));
+ file_octal(CAST(unsigned char, nextchar));
+ continue;
+ }
+ inname += bytesconsumed;
+ n -= bytesconsumed;
+ if (iswprint(nextchar)) {
+ printf("%lc", (wint_t)nextchar);
+ continue;
+ }
+ /* XXX: What if it is > 255? */
+ file_octal(CAST(unsigned char, nextchar));
+ }
+#else
+ size_t i;
+ for (i = 0; i < n; i++) {
+ unsigned char c = CAST(unsigned char, inname[i]);
+ if (isprint(c)) {
+ (void)putc(c, stdout);
+ continue;
+ }
+ file_octal(c);
+ }
+#endif
+}
+
+/*
+ * Called for each input file on the command line (or in a list of files)
+ */
+file_private int
+process(struct magic_set *ms, const char *inname, int wid)
+{
+ const char *type, c = nulsep > 1 ? '\0' : '\n';
+ int std_in = strcmp(inname, "-") == 0;
+ int haderror = 0;
+
+ if (wid > 0 && !bflag) {
+ const char *pname = std_in ? "/dev/stdin" : inname;
+ if ((ms->flags & MAGIC_RAW) == 0)
+ fname_print(pname);
+ else
+ (void)printf("%s", pname);
+ if (nulsep)
+ (void)putc('\0', stdout);
+ if (nulsep < 2) {
+ (void)printf("%s", separator);
+ (void)printf("%*s ", CAST(int, nopad ? 0
+ : (wid - file_mbswidth(ms, inname))), "");
+ }
+ }
+
+ type = magic_file(ms, std_in ? NULL : inname);
+
+ if (type == NULL) {
+ haderror |= printf("ERROR: %s%c", magic_error(ms), c);
+ } else {
+ haderror |= printf("%s%c", type, c) < 0;
+ }
+ if (nobuffer)
+ haderror |= fflush(stdout) != 0;
+ return haderror || type == NULL;
+}
+
+file_protected size_t
+file_mbswidth(struct magic_set *ms, const char *s)
+{
+ size_t width = 0;
+#ifdef FILE_WIDE_SUPPORT
+ size_t bytesconsumed, n;
+ mbstate_t state;
+ wchar_t nextchar;
+
+ (void)memset(&state, 0, sizeof(state));
+ n = strlen(s);
+
+ while (n > 0) {
+ bytesconsumed = mbrtowc(&nextchar, s, n, &state);
+ if (bytesconsumed == CAST(size_t, -1) ||
+ bytesconsumed == CAST(size_t, -2)) {
+ nextchar = *s;
+ bytesconsumed = 1;
+ (void)memset(&state, 0, sizeof(state));
+ width += 4;
+ } else {
+ int w = wcwidth(nextchar);
+ width += ((ms->flags & MAGIC_RAW) != 0
+ || iswprint(nextchar)) ? (w > 0 ? w : 1) : 4;
+ }
+
+ s += bytesconsumed, n -= bytesconsumed;
+ }
+#else
+ for (; *s; s++) {
+ width += (ms->flags & MAGIC_RAW) != 0
+ || isprint(CAST(unsigned char, *s)) ? 1 : 4;
+ }
+#endif
+ return width;
+}
+
+file_private void
+usage(void)
+{
+ const char *pn = file_getprogname();
+ (void)fprintf(stderr, USAGE, pn, pn, pn);
+ exit(EXIT_FAILURE);
+}
+
+file_private void
+defprint(int def)
+{
+ if (!def)
+ return;
+ if (((def & 1) && posixly) || ((def & 2) && !posixly))
+ (void)fprintf(stdout, " (default)");
+ (void)putc('\n', stdout);
+}
+
+file_private void
+docprint(const char *opts, int def)
+{
+ size_t i;
+ int comma, pad;
+ char *sp, *p;
+
+ p = CCAST(char *, strchr(opts, '%'));
+ if (p == NULL) {
+ (void)fprintf(stdout, "%s", opts);
+ defprint(def);
+ return;
+ }
+
+ for (sp = p - 1; sp > opts && *sp == ' '; sp--)
+ continue;
+
+ (void)printf("%.*s", CAST(int, p - opts), opts);
+ pad = (int)CAST(int, p - sp - 1);
+
+ switch (*++p) {
+ case 'e':
+ comma = 0;
+ for (i = 0; i < __arraycount(nv); i++) {
+ (void)printf("%s%s", comma++ ? ", " : "", nv[i].name);
+ if (i && i % 5 == 0 && i != __arraycount(nv) - 1) {
+ (void)printf(",\n%*s", pad, "");
+ comma = 0;
+ }
+ }
+ break;
+ case 'P':
+ for (i = 0; i < __arraycount(pm); i++) {
+ (void)printf("%9s %7zu %s", pm[i].name, pm[i].def,
+ pm[i].desc);
+ if (i != __arraycount(pm) - 1)
+ (void)printf("\n%*s", pad, "");
+ }
+ break;
+ default:
+ file_errx(EXIT_FAILURE, "Unknown escape `%c' in long options",
+ *p);
+ break;
+ }
+ (void)printf("%s", opts + (p - opts) + 1);
+
+}
+
+file_private void
+help(void)
+{
+ (void)fputs(
+"Usage: file [OPTION...] [FILE...]\n"
+"Determine type of FILEs.\n"
+"\n", stdout);
+#define OPT(shortname, longname, opt, def, doc) \
+ (void)printf(" -%c, --" longname, shortname), \
+ docprint(doc, def);
+#define OPT_LONGONLY(longname, opt, def, doc, id) \
+ (void)printf(" --" longname), \
+ docprint(doc, def);
+#include "file_opts.h"
+#undef OPT
+#undef OPT_LONGONLY
+ (void)printf("\nReport bugs to https://bugs.astron.com/\n");
+ exit(EXIT_SUCCESS);
+}
+
+file_private const char *file_progname;
+
+file_protected void
+file_setprogname(const char *progname)
+{
+ file_progname = progname;
+}
+
+file_protected const char *
+file_getprogname(void)
+{
+ return file_progname;
+}
+
+file_protected void
+file_err(int e, const char *fmt, ...)
+{
+ va_list ap;
+ int se = errno;
+
+ va_start(ap, fmt);
+ (void)fprintf(stderr, "%s: ", file_progname);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ if (se)
+ (void)fprintf(stderr, " (%s)\n", strerror(se));
+ else
+ fputc('\n', stderr);
+ exit(e);
+}
+
+file_protected void
+file_errx(int e, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ (void)fprintf(stderr, "%s: ", file_progname);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(e);
+}
+
+file_protected void
+file_warn(const char *fmt, ...)
+{
+ va_list ap;
+ int se = errno;
+
+ va_start(ap, fmt);
+ (void)fprintf(stderr, "%s: ", file_progname);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ if (se)
+ (void)fprintf(stderr, " (%s)\n", strerror(se));
+ else
+ fputc('\n', stderr);
+ errno = se;
+}
+
+file_protected void
+file_warnx(const char *fmt, ...)
+{
+ va_list ap;
+ int se = errno;
+
+ va_start(ap, fmt);
+ (void)fprintf(stderr, "%s: ", file_progname);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ errno = se;
+}