version 1.26, 2015/01/16 18:08:15 |
version 1.27, 2015/04/24 16:24:11 |
|
|
/* $OpenBSD$ */ |
/* $OpenBSD$ */ |
|
|
/* |
/* |
* Copyright (c) Ian F. Darwin 1986-1995. |
* Copyright (c) 2015 Nicholas Marriott <nicm@openbsd.org> |
* Software written by Ian F. Darwin and others; |
* |
* maintained 1995-present by Christos Zoulas and others. |
* Permission to use, copy, modify, and distribute this software for any |
* |
* purpose with or without fee is hereby granted, provided that the above |
* Redistribution and use in source and binary forms, with or without |
* copyright notice and this permission notice appear in all copies. |
* modification, are permitted provided that the following conditions |
* |
* are met: |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
* 1. Redistributions of source code must retain the above copyright |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
* notice immediately at the beginning of the file, without modification, |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
* this list of conditions, and the following disclaimer. |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
* 2. Redistributions in binary form must reproduce the above copyright |
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER |
* notice, this list of conditions and the following disclaimer in the |
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
* documentation and/or other materials provided with the distribution. |
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
* |
|
* 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 <sys/types.h> |
#include <sys/types.h> |
#include <sys/stat.h> |
#include <sys/ioctl.h> |
|
#include <sys/mman.h> |
|
|
#include "file.h" |
#include <errno.h> |
#include "magic.h" |
#include <libgen.h> |
|
#include <getopt.h> |
#include <stdio.h> |
#include <fcntl.h> |
|
#include <pwd.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <unistd.h> |
#include <unistd.h> |
#include <limits.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_LOCALE_H |
|
#include <locale.h> |
|
#endif |
|
#ifdef HAVE_WCHAR_H |
|
#include <wchar.h> |
|
#endif |
|
|
|
#include <getopt.h> |
#include "file.h" |
#ifndef HAVE_GETOPT_LONG |
#include "magic.h" |
int getopt_long(int argc, char * const *argv, const char *optstring, const struct option *longopts, int *longindex); |
#include "xmalloc.h" |
#endif |
|
|
|
#include <netinet/in.h> /* for byte swapping */ |
struct input_file |
|
{ |
|
struct magic *m; |
|
|
#include "patchlevel.h" |
const char *path; |
|
const char *label; |
|
|
|
int fd; |
|
struct stat sb; |
|
const char *error; |
|
|
#ifdef S_IFLNK |
void *base; |
#define SYMLINKFLAG "Lh" |
size_t size; |
#else |
int mapped; |
#define SYMLINKFLAG "" |
char *result; |
#endif |
|
|
|
# define USAGE "Usage: %s [-bcik" SYMLINKFLAG "nNprsvz0] [-e test] [-f namefile] [-F separator] [-m magicfiles] file...\n" \ |
char link_path[PATH_MAX]; |
" %s -C -m magicfiles\n" |
const char *link_error; |
|
int link_target; |
|
}; |
|
|
private int /* Global command-line options */ |
extern char *__progname; |
bflag = 0, /* brief output format */ |
|
nopad = 0, /* Don't pad output */ |
|
nobuffer = 0, /* Do not buffer stdout */ |
|
nulsep = 0; /* Append '\0' to the separator */ |
|
|
|
private const char *magicfile = 0; /* where the magic is */ |
__dead void usage(void); |
private const char *default_magicfile = MAGIC; |
|
private const char *separator = ":"; /* Default field separator */ |
|
|
|
extern char *__progname; /* used throughout */ |
static void open_file(struct input_file *, const char *, int *); |
|
static void read_link(struct input_file *); |
|
static void test_file(struct magic *, struct input_file *, int); |
|
|
private struct magic_set *magic; |
static int try_stat(struct input_file *); |
|
static int try_empty(struct input_file *); |
|
static int try_access(struct input_file *); |
|
static int try_text(struct input_file *); |
|
static int try_magic(struct input_file *); |
|
static int try_unknown(struct input_file *); |
|
|
private void unwrap(char *); |
static int bflag; |
private void usage(void); |
static int cflag; |
private void help(void); |
static int iflag; |
|
static int Lflag; |
|
static int sflag; |
|
static int Wflag; |
|
|
int main(int, char *[]); |
static struct option longopts[] = { |
private void process(const char *, int); |
{ "mime", no_argument, NULL, 'i' }, |
private void load(const char *, int); |
{ "mime-type", no_argument, NULL, 'i' }, |
|
{ NULL, 0, NULL, 0 } |
|
}; |
|
|
|
__dead void |
|
usage(void) |
|
{ |
|
fprintf(stderr, "usage: %s [-bchiLsW] [file ...]\n", __progname); |
|
exit(1); |
|
} |
|
|
/* |
|
* main - parse arguments and handle options |
|
*/ |
|
int |
int |
main(int argc, char *argv[]) |
main(int argc, char **argv) |
{ |
{ |
int c; |
struct input_file *files = NULL; |
size_t i; |
int nfiles, opt, i, width = 0; |
int action = 0, didsomefiles = 0, errflg = 0; |
FILE *f; |
int flags = 0; |
struct magic *m; |
char *home, *usermagic; |
char *home, *path; |
struct stat sb; |
struct passwd *pw; |
static const char hmagic[] = "/.magic"; |
|
#define OPTSTRING "bcCde:f:F:hikLm:nNprsvz0" |
|
int longindex; |
|
static const struct option long_options[] = |
|
{ |
|
#define OPT(shortname, longname, opt, doc) \ |
|
{longname, opt, NULL, shortname}, |
|
#define OPT_LONGONLY(longname, opt, doc) \ |
|
{longname, opt, NULL, 0}, |
|
#include "file_opts.h" |
|
#undef OPT |
|
#undef OPT_LONGONLY |
|
{0, 0, NULL, 0} |
|
}; |
|
|
|
static const struct { |
for (;;) { |
const char *name; |
opt = getopt_long(argc, argv, "bchiLsW", longopts, NULL); |
int value; |
if (opt == -1) |
} nv[] = { |
|
{ "apptype", MAGIC_NO_CHECK_APPTYPE }, |
|
{ "ascii", MAGIC_NO_CHECK_ASCII }, |
|
{ "compress", MAGIC_NO_CHECK_COMPRESS }, |
|
{ "elf", MAGIC_NO_CHECK_ELF }, |
|
{ "soft", MAGIC_NO_CHECK_SOFT }, |
|
{ "tar", MAGIC_NO_CHECK_TAR }, |
|
{ "tokens", MAGIC_NO_CHECK_TOKENS }, |
|
}; |
|
|
|
/* 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 |
|
|
|
magicfile = default_magicfile; |
|
if ((usermagic = getenv("MAGIC")) != NULL) |
|
magicfile = usermagic; |
|
else |
|
if ((home = getenv("HOME")) != NULL) { |
|
size_t len = strlen(home) + sizeof(hmagic); |
|
if ((usermagic = malloc(len)) != NULL) { |
|
(void)strlcpy(usermagic, home, len); |
|
(void)strlcat(usermagic, hmagic, len); |
|
if (stat(usermagic, &sb)<0) |
|
free(usermagic); |
|
else |
|
magicfile = usermagic; |
|
} |
|
} |
|
|
|
#ifdef S_IFLNK |
|
flags |= getenv("POSIXLY_CORRECT") ? MAGIC_SYMLINK : 0; |
|
#endif |
|
while ((c = getopt_long(argc, argv, OPTSTRING, long_options, |
|
&longindex)) != -1) |
|
switch (c) { |
|
case 0 : |
|
switch (longindex) { |
|
case 0: |
|
help(); |
|
break; |
|
case 10: |
|
flags |= MAGIC_MIME_TYPE; |
|
break; |
|
case 11: |
|
flags |= MAGIC_MIME_ENCODING; |
|
break; |
|
} |
|
break; |
break; |
case '0': |
switch (opt) { |
nulsep = 1; |
|
break; |
|
case 'b': |
case 'b': |
bflag++; |
bflag = 1; |
break; |
break; |
case 'c': |
case 'c': |
action = FILE_CHECK; |
cflag = 1; |
break; |
break; |
case 'C': |
case 'h': |
action = FILE_COMPILE; |
Lflag = 0; |
break; |
break; |
case 'd': |
|
flags |= MAGIC_DEBUG|MAGIC_CHECK; |
|
break; |
|
case 'e': |
|
for (i = 0; i < sizeof(nv) / sizeof(nv[0]); i++) |
|
if (strcmp(nv[i].name, optarg) == 0) |
|
break; |
|
|
|
if (i == sizeof(nv) / sizeof(nv[0])) |
|
errflg++; |
|
else |
|
flags |= nv[i].value; |
|
break; |
|
|
|
case 'f': |
|
if(action) |
|
usage(); |
|
load(magicfile, flags); |
|
unwrap(optarg); |
|
++didsomefiles; |
|
break; |
|
case 'F': |
|
separator = optarg; |
|
break; |
|
case 'i': |
case 'i': |
flags |= MAGIC_MIME; |
iflag = 1; |
break; |
break; |
case 'k': |
case 'L': |
flags |= MAGIC_CONTINUE; |
Lflag = 1; |
break; |
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 'r': |
|
flags |= MAGIC_RAW; |
|
break; |
|
case 's': |
case 's': |
flags |= MAGIC_DEVICES; |
sflag = 1; |
break; |
break; |
case 'v': |
case 'W': |
(void)fprintf(stderr, "%s-%d.%.2d\n", __progname, |
Wflag = 1; |
FILE_VERSION_MAJOR, patchlevel); |
|
(void)fprintf(stderr, "magic file from %s\n", |
|
magicfile); |
|
return 1; |
|
case 'z': |
|
flags |= MAGIC_COMPRESS; |
|
break; |
break; |
#ifdef S_IFLNK |
|
case 'L': |
|
flags |= MAGIC_SYMLINK; |
|
break; |
|
case 'h': |
|
flags &= ~MAGIC_SYMLINK; |
|
break; |
|
#endif |
|
case '?': |
|
default: |
default: |
errflg++; |
usage(); |
break; |
|
} |
} |
|
} |
if (errflg) { |
argc -= optind; |
|
argv += optind; |
|
if (cflag) { |
|
if (argc != 0) |
|
usage(); |
|
} else if (argc == 0) |
usage(); |
usage(); |
|
|
|
nfiles = argc; |
|
if (nfiles != 0) { |
|
files = xcalloc(nfiles, sizeof *files); |
|
for (i = 0; i < argc; i++) |
|
open_file(&files[i], argv[i], &width); |
} |
} |
|
|
switch(action) { |
home = getenv("HOME"); |
case FILE_CHECK: |
if (home == NULL || *home == '\0') { |
case FILE_COMPILE: |
pw = getpwuid(getuid()); |
magic = magic_open(flags|MAGIC_CHECK); |
if (pw != NULL) |
if (magic == NULL) { |
home = pw->pw_dir; |
(void)fprintf(stderr, "%s: %s\n", __progname, |
else |
strerror(errno)); |
home = NULL; |
return 1; |
|
} |
|
c = action == FILE_CHECK ? magic_check(magic, magicfile) : |
|
magic_compile(magic, magicfile); |
|
if (c == -1) { |
|
(void)fprintf(stderr, "%s: %s\n", __progname, |
|
magic_error(magic)); |
|
return -1; |
|
} |
|
return 0; |
|
default: |
|
load(magicfile, flags); |
|
break; |
|
} |
} |
|
if (home != NULL) { |
|
xasprintf(&path, "%s/.magic", home); |
|
f = fopen(path, "r"); |
|
} else |
|
f = NULL; |
|
if (f == NULL) { |
|
path = xstrdup("/etc/magic"); |
|
f = fopen(path, "r"); |
|
} |
|
if (f == NULL) |
|
err(1, "%s", path); |
|
|
if (optind == argc) { |
if (geteuid() == 0) { |
if (!didsomefiles) { |
pw = getpwnam(FILE_USER); |
usage(); |
if (pw == NULL) |
} |
errx(1, "unknown user %s", FILE_USER); |
|
if (setgroups(1, &pw->pw_gid) != 0) |
|
err(1, "setgroups"); |
|
if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) |
|
err(1, "setresgid"); |
|
if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) |
|
err(1, "setresuid"); |
} |
} |
else { |
|
size_t j, wid, nw; |
m = magic_load(f, path, cflag || Wflag); |
for (wid = 0, j = (size_t)optind; j < (size_t)argc; j++) { |
if (cflag) { |
nw = file_mbswidth(argv[j]); |
magic_dump(m); |
if (nw > wid) |
exit(0); |
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++) |
|
process(argv[optind], wid); |
|
} |
} |
|
|
c = magic->haderr ? 1 : 0; |
for (i = 0; i < nfiles; i++) |
magic_close(magic); |
test_file(m, &files[i], width); |
return c; |
exit(0); |
} |
} |
|
|
|
static void |
|
open_file(struct input_file *inf, const char *path, int *width) |
|
{ |
|
char *label; |
|
int n, retval; |
|
|
private void |
inf->path = xstrdup(path); |
/*ARGSUSED*/ |
|
load(const char *m, int flags) |
n = xasprintf(&label, "%s:", inf->path); |
|
if (n > *width) |
|
*width = n; |
|
inf->label = label; |
|
|
|
retval = lstat(inf->path, &inf->sb); |
|
if (retval == -1) { |
|
inf->error = strerror(errno); |
|
return; |
|
} |
|
|
|
if (S_ISLNK(inf->sb.st_mode)) |
|
read_link(inf); |
|
inf->fd = open(inf->path, O_RDONLY|O_NONBLOCK); |
|
} |
|
|
|
static void |
|
read_link(struct input_file *inf) |
{ |
{ |
if (magic || m == NULL) |
struct stat sb; |
|
char path[PATH_MAX]; |
|
char *copy, *root; |
|
int used; |
|
ssize_t size; |
|
|
|
size = readlink(inf->path, path, sizeof path); |
|
if (size == -1) { |
|
inf->link_error = strerror(errno); |
return; |
return; |
magic = magic_open(flags); |
|
if (magic == NULL) { |
|
(void)fprintf(stderr, "%s: %s\n", __progname, strerror(errno)); |
|
exit(1); |
|
} |
} |
if (magic_load(magic, magicfile) == -1) { |
path[size] = '\0'; |
(void)fprintf(stderr, "%s: %s\n", |
|
__progname, magic_error(magic)); |
if (*path == '/') |
exit(1); |
strlcpy(inf->link_path, path, sizeof inf->link_path); |
|
else { |
|
copy = xstrdup(inf->path); |
|
|
|
root = dirname(copy); |
|
if (*root == '\0' || strcmp(root, ".") == 0 || |
|
strcmp (root, "/") == 0) |
|
strlcpy(inf->link_path, path, sizeof inf->link_path); |
|
else { |
|
used = snprintf(inf->link_path, sizeof inf->link_path, |
|
"%s/%s", root, path); |
|
if (used < 0 || (size_t)used >= sizeof inf->link_path) { |
|
inf->link_error = strerror(ENAMETOOLONG); |
|
return; |
|
} |
|
} |
|
|
|
free(copy); |
} |
} |
|
|
|
if (Lflag) { |
|
if (stat(inf->path, &inf->sb) == -1) |
|
inf->error = strerror(errno); |
|
} else { |
|
if (stat(inf->link_path, &sb) == -1) |
|
inf->link_target = errno; |
|
} |
} |
} |
|
|
/* |
static void * |
* unwrap -- read a file of filenames, do each one. |
fill_buffer(struct input_file *inf) |
*/ |
|
private void |
|
unwrap(char *fn) |
|
{ |
{ |
char buf[PATH_MAX]; |
static void *buffer; |
FILE *f; |
ssize_t got; |
int wid = 0, cwid; |
size_t left; |
|
void *next; |
|
|
if (strcmp("-", fn) == 0) { |
if (buffer == NULL) |
f = stdin; |
buffer = xmalloc(FILE_READ_SIZE); |
wid = 1; |
|
} else { |
next = buffer; |
if ((f = fopen(fn, "r")) == NULL) { |
left = inf->size; |
(void)fprintf(stderr, "%s: Cannot open `%s' (%s).\n", |
while (left != 0) { |
__progname, fn, strerror(errno)); |
got = read(inf->fd, next, left); |
exit(1); |
if (got == -1) { |
|
if (errno == EINTR) |
|
continue; |
|
return NULL; |
} |
} |
|
if (got == 0) |
|
break; |
|
next = (char*)next + got; |
|
left -= got; |
|
} |
|
|
while (fgets(buf, sizeof(buf), f) != NULL) { |
return buffer; |
buf[strcspn(buf, "\n")] = '\0'; |
} |
cwid = file_mbswidth(buf); |
|
if (cwid > wid) |
static int |
wid = cwid; |
load_file(struct input_file *inf) |
|
{ |
|
int available; |
|
|
|
inf->size = inf->sb.st_size; |
|
if (inf->size > FILE_READ_SIZE) |
|
inf->size = FILE_READ_SIZE; |
|
if (S_ISFIFO(inf->sb.st_mode)) { |
|
if (ioctl(inf->fd, FIONREAD, &available) == -1) { |
|
xasprintf(&inf->result, "cannot read '%s' (%s)", |
|
inf->path, strerror(errno)); |
|
return (1); |
} |
} |
|
inf->size = available; |
|
} else if (!S_ISREG(inf->sb.st_mode) && inf->size == 0) |
|
inf->size = FILE_READ_SIZE; |
|
if (inf->size == 0) |
|
return (0); |
|
|
rewind(f); |
inf->base = mmap(NULL, inf->size, PROT_READ, MAP_PRIVATE, inf->fd, 0); |
|
if (inf->base == MAP_FAILED) { |
|
inf->base = fill_buffer(inf); |
|
if (inf->base == NULL) { |
|
xasprintf(&inf->result, "cannot read '%s' (%s)", |
|
inf->path, strerror(errno)); |
|
return (1); |
|
} |
|
} else |
|
inf->mapped = 1; |
|
return (0); |
|
} |
|
|
|
static int |
|
try_stat(struct input_file *inf) |
|
{ |
|
if (inf->error != NULL) { |
|
xasprintf(&inf->result, "cannot stat '%s' (%s)", inf->path, |
|
inf->error); |
|
return (1); |
} |
} |
|
if (sflag) { |
|
switch (inf->sb.st_mode & S_IFMT) { |
|
case S_IFBLK: |
|
case S_IFCHR: |
|
case S_IFIFO: |
|
case S_IFREG: |
|
return (0); |
|
} |
|
} |
|
|
while (fgets(buf, sizeof(buf), f) != NULL) { |
if (iflag && (inf->sb.st_mode & S_IFMT) != S_IFREG) { |
buf[strcspn(buf, "\n")] = '\0'; |
xasprintf(&inf->result, "application/x-not-regular-file"); |
process(buf, wid); |
return (1); |
if(nobuffer) |
|
(void)fflush(stdout); |
|
} |
} |
|
|
(void)fclose(f); |
|
|
switch (inf->sb.st_mode & S_IFMT) { |
|
case S_IFDIR: |
|
xasprintf(&inf->result, "directory"); |
|
return (1); |
|
case S_IFLNK: |
|
if (inf->link_error != NULL) { |
|
xasprintf(&inf->result, "unreadable symlink '%s' (%s)", |
|
inf->path, inf->link_error); |
|
return (1); |
|
} |
|
if (inf->link_target == ELOOP) |
|
xasprintf(&inf->result, "symbolic link in a loop"); |
|
else if (inf->link_target != 0) { |
|
xasprintf(&inf->result, "broken symbolic link to '%s'", |
|
inf->link_path); |
|
} else { |
|
xasprintf(&inf->result, "symbolic link to '%s'", |
|
inf->link_path); |
|
} |
|
return (1); |
|
case S_IFSOCK: |
|
xasprintf(&inf->result, "socket"); |
|
return (1); |
|
case S_IFBLK: |
|
xasprintf(&inf->result, "block special (%ld/%ld)", |
|
(long)major(inf->sb.st_rdev), (long)minor(inf->sb.st_rdev)); |
|
return (1); |
|
case S_IFCHR: |
|
xasprintf(&inf->result, "character special (%ld/%ld)", |
|
(long)major(inf->sb.st_rdev), (long)minor(inf->sb.st_rdev)); |
|
return (1); |
|
case S_IFIFO: |
|
xasprintf(&inf->result, "fifo (named pipe)"); |
|
return (1); |
|
} |
|
return (0); |
} |
} |
|
|
/* |
static int |
* Called for each input file on the command line (or in a list of files) |
try_empty(struct input_file *inf) |
*/ |
|
private void |
|
process(const char *inname, int wid) |
|
{ |
{ |
const char *type; |
if (inf->size != 0) |
int std_in = strcmp(inname, "-") == 0; |
return (0); |
|
|
if (wid > 0 && !bflag) { |
if (iflag) |
(void)printf("%s", std_in ? "/dev/stdin" : inname); |
xasprintf(&inf->result, "application/x-empty"); |
if (nulsep) |
else |
(void)putc('\0', stdout); |
xasprintf(&inf->result, "empty"); |
|
return (1); |
|
} |
|
|
|
static int |
|
try_access(struct input_file *inf) |
|
{ |
|
char tmp[256] = ""; |
|
|
|
if (inf->fd != -1) |
|
return (0); |
|
|
|
if (inf->sb.st_mode & 0222) |
|
strlcat(tmp, "writable, ", sizeof tmp); |
|
if (inf->sb.st_mode & 0111) |
|
strlcat(tmp, "executable, ", sizeof tmp); |
|
if (S_ISREG(inf->sb.st_mode)) |
|
strlcat(tmp, "regular file, ", sizeof tmp); |
|
strlcat(tmp, "no read permission", sizeof tmp); |
|
|
|
inf->result = xstrdup(tmp); |
|
return (1); |
|
} |
|
|
|
static int |
|
try_text(struct input_file *inf) |
|
{ |
|
const char *type, *s; |
|
int flags; |
|
|
|
flags = MAGIC_TEST_TEXT; |
|
if (iflag) |
|
flags |= MAGIC_TEST_MIME; |
|
|
|
type = text_get_type(inf->base, inf->size); |
|
if (type == NULL) |
|
return (0); |
|
|
|
s = magic_test(inf->m, inf->base, inf->size, flags); |
|
if (s != NULL) { |
|
inf->result = xstrdup(s); |
|
return (1); |
|
} |
|
|
|
s = text_try_words(inf->base, inf->size, flags); |
|
if (s != NULL) { |
|
if (iflag) |
|
inf->result = xstrdup(s); |
else |
else |
(void)printf("%s", separator); |
xasprintf(&inf->result, "%s %s text", type, s); |
(void)printf("%*s ", |
return (1); |
(int) (nopad ? 0 : (wid - file_mbswidth(inname))), ""); |
|
} |
} |
|
|
type = magic_file(magic, std_in ? NULL : inname); |
if (iflag) |
if (type == NULL) |
inf->result = xstrdup("text/plain"); |
(void)printf("ERROR: %s\n", magic_error(magic)); |
|
else |
else |
(void)printf("%s\n", type); |
xasprintf(&inf->result, "%s text", type); |
|
return (1); |
} |
} |
|
|
size_t |
static int |
file_mbswidth(const char *s) |
try_magic(struct input_file *inf) |
{ |
{ |
#if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH) |
const char *s; |
size_t bytesconsumed, old_n, n, width = 0; |
int flags; |
mbstate_t state; |
|
wchar_t nextchar; |
|
(void)memset(&state, 0, sizeof(mbstate_t)); |
|
old_n = n = strlen(s); |
|
int w; |
|
|
|
while (n > 0) { |
flags = 0; |
bytesconsumed = mbrtowc(&nextchar, s, n, &state); |
if (iflag) |
if (bytesconsumed == (size_t)(-1) || |
flags |= MAGIC_TEST_MIME; |
bytesconsumed == (size_t)(-2)) { |
|
/* Something went wrong, return something reasonable */ |
|
return old_n; |
|
} |
|
if (s[0] == '\n') { |
|
/* |
|
* do what strlen() would do, so that caller |
|
* is always right |
|
*/ |
|
width++; |
|
} else { |
|
w = wcwidth(nextchar); |
|
if (w > 0) |
|
width += w; |
|
} |
|
|
|
s += bytesconsumed, n -= bytesconsumed; |
s = magic_test(inf->m, inf->base, inf->size, flags); |
|
if (s != NULL) { |
|
inf->result = xstrdup(s); |
|
return (1); |
} |
} |
return width; |
return (0); |
#else |
|
return strlen(s); |
|
#endif |
|
} |
} |
|
|
private void |
static int |
usage(void) |
try_unknown(struct input_file *inf) |
{ |
{ |
(void)fprintf(stderr, USAGE, __progname, __progname); |
if (iflag) |
(void)fputs("Try `file --help' for more information.\n", stderr); |
xasprintf(&inf->result, "application/x-not-regular-file"); |
exit(1); |
else |
|
xasprintf(&inf->result, "data"); |
|
return (1); |
} |
} |
|
|
private void |
static void |
help(void) |
test_file(struct magic *m, struct input_file *inf, int width) |
{ |
{ |
(void)fputs( |
int stop; |
"Usage: file [OPTION...] [FILE...]\n" |
|
"Determine type of FILEs.\n" |
inf->m = m; |
"\n", stderr); |
|
#define OPT(shortname, longname, opt, doc) \ |
stop = 0; |
fprintf(stderr, " -%c, --" longname doc, shortname); |
if (!stop) |
#define OPT_LONGONLY(longname, opt, doc) \ |
stop = try_stat(inf); |
fprintf(stderr, " --" longname doc); |
if (!stop) |
#include "file_opts.h" |
stop = try_access(inf); |
#undef OPT |
if (!stop) |
#undef OPT_LONGONLY |
stop = load_file(inf); |
exit(0); |
if (!stop) |
|
stop = try_empty(inf); |
|
if (!stop) |
|
stop = try_magic(inf); |
|
if (!stop) |
|
stop = try_text(inf); |
|
if (!stop) |
|
stop = try_unknown(inf); |
|
|
|
if (bflag) |
|
printf("%s\n", inf->result); |
|
else |
|
printf("%-*s %s\n", width, inf->label, inf->result); |
|
|
|
if (inf->mapped && inf->base != NULL) |
|
munmap(inf->base, inf->size); |
|
inf->base = NULL; |
|
|
|
free(inf->result); |
} |
} |