Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
file/src/file.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
859 lines (778 sloc)
18.7 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * 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.215 2023/05/21 17:08:34 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 }, | |
| { "simh", MAGIC_NO_CHECK_SIMH }, | |
| { "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 }, | |
| { "elf_shsize", 0, FILE_ELF_SHSIZE_MAX, "max ELF section size", | |
| MAGIC_PARAM_ELF_SHSIZE_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; | |
| } |