168349Sobrien/*
2133359Sobrien * Copyright (c) Ian F. Darwin 1986-1995.
3133359Sobrien * Software written by Ian F. Darwin and others;
4133359Sobrien * maintained 1995-present by Christos Zoulas and others.
5191736Sobrien *
6133359Sobrien * Redistribution and use in source and binary forms, with or without
7133359Sobrien * modification, are permitted provided that the following conditions
8133359Sobrien * are met:
9133359Sobrien * 1. Redistributions of source code must retain the above copyright
10133359Sobrien *    notice immediately at the beginning of the file, without modification,
11133359Sobrien *    this list of conditions, and the following disclaimer.
12133359Sobrien * 2. Redistributions in binary form must reproduce the above copyright
13133359Sobrien *    notice, this list of conditions and the following disclaimer in the
14133359Sobrien *    documentation and/or other materials provided with the distribution.
15191736Sobrien *
16133359Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17133359Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18133359Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19133359Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20133359Sobrien * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21133359Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22133359Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23133359Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24133359Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25133359Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26133359Sobrien * SUCH DAMAGE.
27133359Sobrien */
28133359Sobrien/*
2968349Sobrien * file - find type of a file or files - main program.
3068349Sobrien */
31103373Sobrien
32103373Sobrien#include "file.h"
33191736Sobrien
34191736Sobrien#ifndef	lint
35309848SdelphijFILE_RCSID("@(#)$File: file.c,v 1.172 2016/10/24 15:21:07 christos Exp $")
36191736Sobrien#endif	/* lint */
37191736Sobrien
38133359Sobrien#include "magic.h"
39133359Sobrien
4068349Sobrien#include <stdlib.h>
4169216Sobrien#include <unistd.h>
4268349Sobrien#include <string.h>
4368349Sobrien#ifdef RESTORE_TIME
4468349Sobrien# if (__COHERENT__ >= 0x420)
4568349Sobrien#  include <sys/utime.h>
4668349Sobrien# else
4768349Sobrien#  ifdef USE_UTIMES
4868349Sobrien#   include <sys/time.h>
4968349Sobrien#  else
5068349Sobrien#   include <utime.h>
5168349Sobrien#  endif
5268349Sobrien# endif
5368349Sobrien#endif
5468349Sobrien#ifdef HAVE_UNISTD_H
5568349Sobrien#include <unistd.h>	/* for read() */
5668349Sobrien#endif
57133359Sobrien#ifdef HAVE_WCHAR_H
58133359Sobrien#include <wchar.h>
59133359Sobrien#endif
6068349Sobrien
61192348Sdelphij#if defined(HAVE_GETOPT_H) && defined(HAVE_STRUCT_OPTION)
62186690Sobrien#include <getopt.h>
63226048Sobrien#ifndef HAVE_GETOPT_LONG
64226048Sobrienint getopt_long(int argc, char * const *argv, const char *optstring, const struct option *longopts, int *longindex);
65226048Sobrien#endif
66133359Sobrien#else
67186690Sobrien#include "mygetopt.h"
68103373Sobrien#endif
69103373Sobrien
7068349Sobrien#ifdef S_IFLNK
71284778Sdelphij#define FILE_FLAGS "-bcEhikLlNnprsvzZ0"
7268349Sobrien#else
73284778Sdelphij#define FILE_FLAGS "-bcEiklNnprsvzZ0"
7468349Sobrien#endif
7568349Sobrien
76226048Sobrien# define USAGE  \
77226048Sobrien    "Usage: %s [" FILE_FLAGS \
78284778Sdelphij	"] [--apple] [--extension] [--mime-encoding] [--mime-type]\n" \
79226048Sobrien    "            [-e testname] [-F separator] [-f namefile] [-m magicfiles] " \
80226048Sobrien    "file ...\n" \
81226048Sobrien    "       %s -C [-m magicfiles]\n" \
82226048Sobrien    "       %s [--help]\n"
83103373Sobrien
84133359Sobrienprivate int 		/* Global command-line options 		*/
8568349Sobrien	bflag = 0,	/* brief output format	 		*/
86110949Sobrien	nopad = 0,	/* Don't pad output			*/
87169942Sobrien	nobuffer = 0,   /* Do not buffer stdout 		*/
88169942Sobrien	nulsep = 0;	/* Append '\0' to the separator		*/
8968349Sobrien
90159764Sobrienprivate const char *separator = ":";	/* Default field separator	*/
91191736Sobrienprivate const struct option long_options[] = {
92284778Sdelphij#define OPT_HELP		1
93284778Sdelphij#define OPT_APPLE		2
94284778Sdelphij#define OPT_EXTENSIONS		3
95284778Sdelphij#define OPT_MIME_TYPE		4
96284778Sdelphij#define OPT_MIME_ENCODING	5
97300899Sdelphij#define OPT(shortname, longname, opt, def, doc)      \
98191736Sobrien    {longname, opt, NULL, shortname},
99300899Sdelphij#define OPT_LONGONLY(longname, opt, def, doc, id)        \
100284778Sdelphij    {longname, opt, NULL, id},
101191736Sobrien#include "file_opts.h"
102191736Sobrien#undef OPT
103191736Sobrien#undef OPT_LONGONLY
104191736Sobrien    {0, 0, NULL, 0}
105191736Sobrien};
106284778Sdelphij#define OPTSTRING	"bcCde:Ef:F:hiklLm:nNpP:rsvzZ0"
10768349Sobrien
108191736Sobrienprivate const struct {
109191736Sobrien	const char *name;
110191736Sobrien	int value;
111191736Sobrien} nv[] = {
112191736Sobrien	{ "apptype",	MAGIC_NO_CHECK_APPTYPE },
113191736Sobrien	{ "ascii",	MAGIC_NO_CHECK_ASCII },
114191736Sobrien	{ "cdf",	MAGIC_NO_CHECK_CDF },
115191736Sobrien	{ "compress",	MAGIC_NO_CHECK_COMPRESS },
116191736Sobrien	{ "elf",	MAGIC_NO_CHECK_ELF },
117191736Sobrien	{ "encoding",	MAGIC_NO_CHECK_ENCODING },
118191736Sobrien	{ "soft",	MAGIC_NO_CHECK_SOFT },
119191736Sobrien	{ "tar",	MAGIC_NO_CHECK_TAR },
120226048Sobrien	{ "text",	MAGIC_NO_CHECK_TEXT },	/* synonym for ascii */
121234250Sobrien	{ "tokens",	MAGIC_NO_CHECK_TOKENS }, /* OBSOLETE: ignored for backwards compatibility */
122191736Sobrien};
123191736Sobrien
124276415Sdelphijprivate struct {
125276415Sdelphij	const char *name;
126276415Sdelphij	int tag;
127276415Sdelphij	size_t value;
128276415Sdelphij} pm[] = {
129276415Sdelphij	{ "indir",	MAGIC_PARAM_INDIR_MAX, 0 },
130276415Sdelphij	{ "name",	MAGIC_PARAM_NAME_MAX, 0 },
131276415Sdelphij	{ "elf_phnum",	MAGIC_PARAM_ELF_PHNUM_MAX, 0 },
132276415Sdelphij	{ "elf_shnum",	MAGIC_PARAM_ELF_SHNUM_MAX, 0 },
133277592Sdelphij	{ "elf_notes",	MAGIC_PARAM_ELF_NOTES_MAX, 0 },
134290152Sdelphij	{ "regex",	MAGIC_PARAM_REGEX_MAX, 0 },
135300899Sdelphij	{ "bytes",	MAGIC_PARAM_BYTES_MAX, 0 },
136276415Sdelphij};
137276415Sdelphij
138133359Sobrienprivate char *progname;		/* used throughout 		*/
139300899Sdelphijprivate int posixly;
14068349Sobrien
141284778Sdelphij#ifdef __dead
142284778Sdelphij__dead
143284778Sdelphij#endif
144133359Sobrienprivate void usage(void);
145300899Sdelphijprivate void docprint(const char *, int);
146284778Sdelphij#ifdef __dead
147284778Sdelphij__dead
148284778Sdelphij#endif
149133359Sobrienprivate void help(void);
15068349Sobrien
151191736Sobrienprivate int unwrap(struct magic_set *, const char *);
152191736Sobrienprivate int process(struct magic_set *ms, const char *, int);
153191736Sobrienprivate struct magic_set *load(const char *, int);
154276415Sdelphijprivate void setparam(const char *);
155276415Sdelphijprivate void applyparam(magic_t);
156133359Sobrien
157191736Sobrien
15868349Sobrien/*
15968349Sobrien * main - parse arguments and handle options
16068349Sobrien */
16168349Sobrienint
162133359Sobrienmain(int argc, char *argv[])
16368349Sobrien{
164175296Sobrien	int c;
165175296Sobrien	size_t i;
166133359Sobrien	int action = 0, didsomefiles = 0, errflg = 0;
167191736Sobrien	int flags = 0, e = 0;
168191736Sobrien	struct magic_set *magic = NULL;
169103373Sobrien	int longindex;
170226048Sobrien	const char *magicfile = NULL;		/* where the magic is	*/
17168349Sobrien
172159764Sobrien	/* makes islower etc work for other langs */
173276415Sdelphij#ifdef HAVE_SETLOCALE
174159764Sobrien	(void)setlocale(LC_CTYPE, "");
175276415Sdelphij#endif
17668349Sobrien
177103373Sobrien#ifdef __EMX__
178103373Sobrien	/* sh-like wildcard expansion! Shouldn't hurt at least ... */
179103373Sobrien	_wildcard(&argc, &argv);
180103373Sobrien#endif
181103373Sobrien
18268349Sobrien	if ((progname = strrchr(argv[0], '/')) != NULL)
18368349Sobrien		progname++;
18468349Sobrien	else
18568349Sobrien		progname = argv[0];
18668349Sobrien
187159764Sobrien#ifdef S_IFLNK
188300899Sdelphij	posixly = getenv("POSIXLY_CORRECT") != NULL;
189300899Sdelphij	flags |=  posixly ? MAGIC_SYMLINK : 0;
190159764Sobrien#endif
191103373Sobrien	while ((c = getopt_long(argc, argv, OPTSTRING, long_options,
192103373Sobrien	    &longindex)) != -1)
19368349Sobrien		switch (c) {
194284778Sdelphij		case OPT_HELP:
195284778Sdelphij			help();
196103373Sobrien			break;
197284778Sdelphij		case OPT_APPLE:
198284778Sdelphij			flags |= MAGIC_APPLE;
199284778Sdelphij			break;
200284778Sdelphij		case OPT_EXTENSIONS:
201284778Sdelphij			flags |= MAGIC_EXTENSION;
202284778Sdelphij			break;
203284778Sdelphij		case OPT_MIME_TYPE:
204284778Sdelphij			flags |= MAGIC_MIME_TYPE;
205284778Sdelphij			break;
206284778Sdelphij		case OPT_MIME_ENCODING:
207284778Sdelphij			flags |= MAGIC_MIME_ENCODING;
208284778Sdelphij			break;
209169942Sobrien		case '0':
210300899Sdelphij			nulsep++;
211169942Sobrien			break;
21268349Sobrien		case 'b':
213175296Sobrien			bflag++;
21468349Sobrien			break;
21568349Sobrien		case 'c':
216133359Sobrien			action = FILE_CHECK;
21768349Sobrien			break;
21874784Sobrien		case 'C':
219133359Sobrien			action = FILE_COMPILE;
22074784Sobrien			break;
22168349Sobrien		case 'd':
222133359Sobrien			flags |= MAGIC_DEBUG|MAGIC_CHECK;
22368349Sobrien			break;
224267843Sdelphij		case 'E':
225267843Sdelphij			flags |= MAGIC_ERROR;
226267843Sdelphij			break;
227169962Sobrien		case 'e':
228169962Sobrien			for (i = 0; i < sizeof(nv) / sizeof(nv[0]); i++)
229169962Sobrien				if (strcmp(nv[i].name, optarg) == 0)
230169962Sobrien					break;
231169962Sobrien
232169962Sobrien			if (i == sizeof(nv) / sizeof(nv[0]))
233169962Sobrien				errflg++;
234169962Sobrien			else
235169962Sobrien				flags |= nv[i].value;
236169962Sobrien			break;
237191736Sobrien
23868349Sobrien		case 'f':
239133359Sobrien			if(action)
240133359Sobrien				usage();
241191736Sobrien			if (magic == NULL)
242191736Sobrien				if ((magic = load(magicfile, flags)) == NULL)
243191736Sobrien					return 1;
244290152Sdelphij			applyparam(magic);
245191736Sobrien			e |= unwrap(magic, optarg);
24668349Sobrien			++didsomefiles;
24768349Sobrien			break;
248110949Sobrien		case 'F':
249133359Sobrien			separator = optarg;
250110949Sobrien			break;
25168349Sobrien		case 'i':
252133359Sobrien			flags |= MAGIC_MIME;
25368349Sobrien			break;
25468349Sobrien		case 'k':
255133359Sobrien			flags |= MAGIC_CONTINUE;
25668349Sobrien			break;
257226048Sobrien		case 'l':
258226048Sobrien			action = FILE_LIST;
259226048Sobrien			break;
26068349Sobrien		case 'm':
26168349Sobrien			magicfile = optarg;
26268349Sobrien			break;
26368349Sobrien		case 'n':
26468349Sobrien			++nobuffer;
26568349Sobrien			break;
266110949Sobrien		case 'N':
267110949Sobrien			++nopad;
268110949Sobrien			break;
269133359Sobrien#if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
270133359Sobrien		case 'p':
271133359Sobrien			flags |= MAGIC_PRESERVE_ATIME;
272133359Sobrien			break;
273133359Sobrien#endif
274276415Sdelphij		case 'P':
275276415Sdelphij			setparam(optarg);
276276415Sdelphij			break;
277133359Sobrien		case 'r':
278133359Sobrien			flags |= MAGIC_RAW;
279133359Sobrien			break;
28068349Sobrien		case 's':
281133359Sobrien			flags |= MAGIC_DEVICES;
28268349Sobrien			break;
28368349Sobrien		case 'v':
284226048Sobrien			if (magicfile == NULL)
285226048Sobrien				magicfile = magic_getpath(magicfile, action);
286226048Sobrien			(void)fprintf(stdout, "%s-%s\n", progname, VERSION);
287226048Sobrien			(void)fprintf(stdout, "magic file from %s\n",
28868349Sobrien				       magicfile);
289267843Sdelphij			return 0;
29068349Sobrien		case 'z':
291133359Sobrien			flags |= MAGIC_COMPRESS;
29268349Sobrien			break;
293284778Sdelphij
294284778Sdelphij		case 'Z':
295284778Sdelphij			flags |= MAGIC_COMPRESS|MAGIC_COMPRESS_TRANSP;
296284778Sdelphij			break;
29768349Sobrien#ifdef S_IFLNK
29868349Sobrien		case 'L':
299133359Sobrien			flags |= MAGIC_SYMLINK;
30068349Sobrien			break;
301159764Sobrien		case 'h':
302159764Sobrien			flags &= ~MAGIC_SYMLINK;
303159764Sobrien			break;
30468349Sobrien#endif
30568349Sobrien		case '?':
30668349Sobrien		default:
30768349Sobrien			errflg++;
30868349Sobrien			break;
30968349Sobrien		}
31068349Sobrien
31168349Sobrien	if (errflg) {
31274784Sobrien		usage();
31368349Sobrien	}
314191736Sobrien	if (e)
315191736Sobrien		return e;
31668349Sobrien
317267843Sdelphij	if (MAGIC_VERSION != magic_version())
318267843Sdelphij		(void)fprintf(stderr, "%s: compiled magic version [%d] "
319267843Sdelphij		    "does not match with shared library magic version [%d]\n",
320267843Sdelphij		    progname, MAGIC_VERSION, magic_version());
321267843Sdelphij
322133359Sobrien	switch(action) {
323133359Sobrien	case FILE_CHECK:
324133359Sobrien	case FILE_COMPILE:
325226048Sobrien	case FILE_LIST:
326191736Sobrien		/*
327191736Sobrien		 * Don't try to check/compile ~/.magic unless we explicitly
328191736Sobrien		 * ask for it.
329191736Sobrien		 */
330133359Sobrien		magic = magic_open(flags|MAGIC_CHECK);
331133359Sobrien		if (magic == NULL) {
332133359Sobrien			(void)fprintf(stderr, "%s: %s\n", progname,
333133359Sobrien			    strerror(errno));
334133359Sobrien			return 1;
335133359Sobrien		}
336276415Sdelphij
337276415Sdelphij
338226048Sobrien		switch(action) {
339226048Sobrien		case FILE_CHECK:
340226048Sobrien			c = magic_check(magic, magicfile);
341226048Sobrien			break;
342226048Sobrien		case FILE_COMPILE:
343226048Sobrien			c = magic_compile(magic, magicfile);
344226048Sobrien			break;
345226048Sobrien		case FILE_LIST:
346226048Sobrien			c = magic_list(magic, magicfile);
347226048Sobrien			break;
348226048Sobrien		default:
349226048Sobrien			abort();
350226048Sobrien		}
351133359Sobrien		if (c == -1) {
352133359Sobrien			(void)fprintf(stderr, "%s: %s\n", progname,
353133359Sobrien			    magic_error(magic));
354302555Sdelphij			e = 1;
355302555Sdelphij			goto out;
356133359Sobrien		}
357302555Sdelphij		goto out;
358133359Sobrien	default:
359191736Sobrien		if (magic == NULL)
360191736Sobrien			if ((magic = load(magicfile, flags)) == NULL)
361191736Sobrien				return 1;
362276415Sdelphij		applyparam(magic);
36368349Sobrien	}
36468349Sobrien
36568349Sobrien	if (optind == argc) {
366191736Sobrien		if (!didsomefiles)
36774784Sobrien			usage();
36868349Sobrien	}
36968349Sobrien	else {
370175296Sobrien		size_t j, wid, nw;
371175296Sobrien		for (wid = 0, j = (size_t)optind; j < (size_t)argc; j++) {
372175296Sobrien			nw = file_mbswidth(argv[j]);
37368349Sobrien			if (nw > wid)
37468349Sobrien				wid = nw;
37568349Sobrien		}
376175296Sobrien		/*
377175296Sobrien		 * If bflag is only set twice, set it depending on
378175296Sobrien		 * number of files [this is undocumented, and subject to change]
379175296Sobrien		 */
380175296Sobrien		if (bflag == 2) {
381175296Sobrien			bflag = optind >= argc - 1;
382175296Sobrien		}
38368349Sobrien		for (; optind < argc; optind++)
384191736Sobrien			e |= process(magic, argv[optind], wid);
38568349Sobrien	}
38668349Sobrien
387302555Sdelphijout:
388191736Sobrien	if (magic)
389191736Sobrien		magic_close(magic);
390191736Sobrien	return e;
39168349Sobrien}
39268349Sobrien
393276415Sdelphijprivate void
394276415Sdelphijapplyparam(magic_t magic)
395276415Sdelphij{
396276415Sdelphij	size_t i;
39768349Sobrien
398276415Sdelphij	for (i = 0; i < __arraycount(pm); i++) {
399276415Sdelphij		if (pm[i].value == 0)
400276415Sdelphij			continue;
401276415Sdelphij		if (magic_setparam(magic, pm[i].tag, &pm[i].value) == -1) {
402276415Sdelphij			(void)fprintf(stderr, "%s: Can't set %s %s\n", progname,
403276415Sdelphij				pm[i].name, strerror(errno));
404276415Sdelphij			exit(1);
405276415Sdelphij		}
406276415Sdelphij	}
407276415Sdelphij}
408276415Sdelphij
409276415Sdelphijprivate void
410276415Sdelphijsetparam(const char *p)
411276415Sdelphij{
412276415Sdelphij	size_t i;
413276415Sdelphij	char *s;
414276415Sdelphij
415276415Sdelphij	if ((s = strchr(p, '=')) == NULL)
416276415Sdelphij		goto badparm;
417276415Sdelphij
418276415Sdelphij	for (i = 0; i < __arraycount(pm); i++) {
419276415Sdelphij		if (strncmp(p, pm[i].name, s - p) != 0)
420276415Sdelphij			continue;
421276415Sdelphij		pm[i].value = atoi(s + 1);
422276415Sdelphij		return;
423276415Sdelphij	}
424276415Sdelphijbadparm:
425276415Sdelphij	(void)fprintf(stderr, "%s: Unknown param %s\n", progname, p);
426276415Sdelphij	exit(1);
427276415Sdelphij}
428276415Sdelphij
429191736Sobrienprivate struct magic_set *
430159764Sobrien/*ARGSUSED*/
431191736Sobrienload(const char *magicfile, int flags)
432133359Sobrien{
433191736Sobrien	struct magic_set *magic = magic_open(flags);
434309848Sdelphij	const char *e;
435309848Sdelphij
436133359Sobrien	if (magic == NULL) {
437133359Sobrien		(void)fprintf(stderr, "%s: %s\n", progname, strerror(errno));
438191736Sobrien		return NULL;
439133359Sobrien	}
440133359Sobrien	if (magic_load(magic, magicfile) == -1) {
441133359Sobrien		(void)fprintf(stderr, "%s: %s\n",
442133359Sobrien		    progname, magic_error(magic));
443191736Sobrien		magic_close(magic);
444191736Sobrien		return NULL;
445133359Sobrien	}
446309848Sdelphij	if ((e = magic_error(magic)) != NULL)
447309848Sdelphij		(void)fprintf(stderr, "%s: Warning: %s\n", progname, e);
448191736Sobrien	return magic;
449133359Sobrien}
450133359Sobrien
45168349Sobrien/*
45268349Sobrien * unwrap -- read a file of filenames, do each one.
45368349Sobrien */
454191736Sobrienprivate int
455191736Sobrienunwrap(struct magic_set *ms, const char *fn)
45668349Sobrien{
45768349Sobrien	FILE *f;
458226048Sobrien	ssize_t len;
459226048Sobrien	char *line = NULL;
460226048Sobrien	size_t llen = 0;
46168349Sobrien	int wid = 0, cwid;
462191736Sobrien	int e = 0;
46368349Sobrien
46468349Sobrien	if (strcmp("-", fn) == 0) {
46568349Sobrien		f = stdin;
46668349Sobrien		wid = 1;
46768349Sobrien	} else {
46868349Sobrien		if ((f = fopen(fn, "r")) == NULL) {
469133359Sobrien			(void)fprintf(stderr, "%s: Cannot open `%s' (%s).\n",
470133359Sobrien			    progname, fn, strerror(errno));
471191736Sobrien			return 1;
47268349Sobrien		}
47368349Sobrien
474226048Sobrien		while ((len = getline(&line, &llen, f)) > 0) {
475226048Sobrien			if (line[len - 1] == '\n')
476226048Sobrien				line[len - 1] = '\0';
477226048Sobrien			cwid = file_mbswidth(line);
47868349Sobrien			if (cwid > wid)
47968349Sobrien				wid = cwid;
48068349Sobrien		}
48168349Sobrien
48268349Sobrien		rewind(f);
48368349Sobrien	}
48468349Sobrien
485226048Sobrien	while ((len = getline(&line, &llen, f)) > 0) {
486226048Sobrien		if (line[len - 1] == '\n')
487226048Sobrien			line[len - 1] = '\0';
488226048Sobrien		e |= process(ms, line, wid);
48968349Sobrien		if(nobuffer)
490159764Sobrien			(void)fflush(stdout);
49168349Sobrien	}
49268349Sobrien
493226048Sobrien	free(line);
494159764Sobrien	(void)fclose(f);
495191736Sobrien	return e;
49668349Sobrien}
49768349Sobrien
498169942Sobrien/*
499169942Sobrien * Called for each input file on the command line (or in a list of files)
500169942Sobrien */
501191736Sobrienprivate int
502191736Sobrienprocess(struct magic_set *ms, const char *inname, int wid)
503133359Sobrien{
504300899Sdelphij	const char *type, c = nulsep > 1 ? '\0' : '\n';
505133359Sobrien	int std_in = strcmp(inname, "-") == 0;
50668349Sobrien
507169942Sobrien	if (wid > 0 && !bflag) {
508169942Sobrien		(void)printf("%s", std_in ? "/dev/stdin" : inname);
509169942Sobrien		if (nulsep)
510169962Sobrien			(void)putc('\0', stdout);
511300899Sdelphij		if (nulsep < 2) {
512300899Sdelphij			(void)printf("%s", separator);
513300899Sdelphij			(void)printf("%*s ",
514300899Sdelphij			    (int) (nopad ? 0 : (wid - file_mbswidth(inname))),
515300899Sdelphij			    "");
516300899Sdelphij		}
517169942Sobrien	}
518133359Sobrien
519191736Sobrien	type = magic_file(ms, std_in ? NULL : inname);
520300899Sdelphij
521191736Sobrien	if (type == NULL) {
522300899Sdelphij		(void)printf("ERROR: %s%c", magic_error(ms), c);
523191736Sobrien		return 1;
524191736Sobrien	} else {
525300899Sdelphij		(void)printf("%s%c", type, c);
526191736Sobrien		return 0;
527191736Sobrien	}
528133359Sobrien}
529133359Sobrien
530267843Sdelphijprotected size_t
531133359Sobrienfile_mbswidth(const char *s)
53268349Sobrien{
533133359Sobrien#if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH)
534133359Sobrien	size_t bytesconsumed, old_n, n, width = 0;
535133359Sobrien	mbstate_t state;
536133359Sobrien	wchar_t nextchar;
537133359Sobrien	(void)memset(&state, 0, sizeof(mbstate_t));
538133359Sobrien	old_n = n = strlen(s);
53968349Sobrien
540133359Sobrien	while (n > 0) {
541133359Sobrien		bytesconsumed = mbrtowc(&nextchar, s, n, &state);
542133359Sobrien		if (bytesconsumed == (size_t)(-1) ||
543133359Sobrien		    bytesconsumed == (size_t)(-2)) {
544133359Sobrien			/* Something went wrong, return something reasonable */
545133359Sobrien			return old_n;
54668349Sobrien		}
547133359Sobrien		if (s[0] == '\n') {
548133359Sobrien			/*
549133359Sobrien			 * do what strlen() would do, so that caller
550133359Sobrien			 * is always right
551133359Sobrien			 */
552133359Sobrien			width++;
553267843Sdelphij		} else {
554267843Sdelphij			int w = wcwidth(nextchar);
555267843Sdelphij			if (w > 0)
556267843Sdelphij				width += w;
557267843Sdelphij		}
55868349Sobrien
559133359Sobrien		s += bytesconsumed, n -= bytesconsumed;
56068349Sobrien	}
561133359Sobrien	return width;
562133359Sobrien#else
563133359Sobrien	return strlen(s);
56468349Sobrien#endif
56568349Sobrien}
56668349Sobrien
567133359Sobrienprivate void
568103373Sobrienusage(void)
56974784Sobrien{
570226048Sobrien	(void)fprintf(stderr, USAGE, progname, progname, progname);
57174784Sobrien	exit(1);
57274784Sobrien}
573103373Sobrien
574133359Sobrienprivate void
575300899Sdelphijdefprint(int def)
576267843Sdelphij{
577300899Sdelphij	if (!def)
578300899Sdelphij		return;
579300899Sdelphij	if (((def & 1) && posixly) || ((def & 2) && !posixly))
580300899Sdelphij		fprintf(stdout, " (default)");
581300899Sdelphij	fputc('\n', stdout);
582300899Sdelphij}
583300899Sdelphij
584300899Sdelphijprivate void
585300899Sdelphijdocprint(const char *opts, int def)
586300899Sdelphij{
587267843Sdelphij	size_t i;
588267843Sdelphij	int comma;
589267843Sdelphij	char *sp, *p;
590267843Sdelphij
591267843Sdelphij	p = strstr(opts, "%o");
592267843Sdelphij	if (p == NULL) {
593267843Sdelphij		fprintf(stdout, "%s", opts);
594300899Sdelphij		defprint(def);
595267843Sdelphij		return;
596267843Sdelphij	}
597267843Sdelphij
598267843Sdelphij	for (sp = p - 1; sp > opts && *sp == ' '; sp--)
599267843Sdelphij		continue;
600267843Sdelphij
601267843Sdelphij	fprintf(stdout, "%.*s", (int)(p - opts), opts);
602267843Sdelphij
603267843Sdelphij	comma = 0;
604267843Sdelphij	for (i = 0; i < __arraycount(nv); i++) {
605267843Sdelphij		fprintf(stdout, "%s%s", comma++ ? ", " : "", nv[i].name);
606267843Sdelphij		if (i && i % 5 == 0) {
607267843Sdelphij			fprintf(stdout, ",\n%*s", (int)(p - sp - 1), "");
608267843Sdelphij			comma = 0;
609267843Sdelphij		}
610267843Sdelphij	}
611267843Sdelphij
612267843Sdelphij	fprintf(stdout, "%s", opts + (p - opts) + 2);
613267843Sdelphij}
614267843Sdelphij
615267843Sdelphijprivate void
616103373Sobrienhelp(void)
617103373Sobrien{
618175296Sobrien	(void)fputs(
619175296Sobrien"Usage: file [OPTION...] [FILE...]\n"
620175296Sobrien"Determine type of FILEs.\n"
621226048Sobrien"\n", stdout);
622300899Sdelphij#define OPT(shortname, longname, opt, def, doc)      \
623267843Sdelphij	fprintf(stdout, "  -%c, --" longname, shortname), \
624300899Sdelphij	docprint(doc, def);
625300899Sdelphij#define OPT_LONGONLY(longname, opt, def, doc, id)        \
626267843Sdelphij	fprintf(stdout, "      --" longname),	\
627300899Sdelphij	docprint(doc, def);
628175296Sobrien#include "file_opts.h"
629175296Sobrien#undef OPT
630175296Sobrien#undef OPT_LONGONLY
631226048Sobrien	fprintf(stdout, "\nReport bugs to http://bugs.gw.com/\n");
632103373Sobrien	exit(0);
633103373Sobrien}
634