1284194Sdelphij/*
2284194Sdelphij * Copyright (c) Ian F. Darwin 1986-1995.
3284194Sdelphij * Software written by Ian F. Darwin and others;
4284194Sdelphij * maintained 1995-present by Christos Zoulas and others.
5284194Sdelphij *
6284194Sdelphij * Redistribution and use in source and binary forms, with or without
7284194Sdelphij * modification, are permitted provided that the following conditions
8284194Sdelphij * are met:
9284194Sdelphij * 1. Redistributions of source code must retain the above copyright
10284194Sdelphij *    notice immediately at the beginning of the file, without modification,
11284194Sdelphij *    this list of conditions, and the following disclaimer.
12284194Sdelphij * 2. Redistributions in binary form must reproduce the above copyright
13284194Sdelphij *    notice, this list of conditions and the following disclaimer in the
14284194Sdelphij *    documentation and/or other materials provided with the distribution.
15284194Sdelphij *
16284194Sdelphij * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17284194Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18284194Sdelphij * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19284194Sdelphij * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20284194Sdelphij * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21284194Sdelphij * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22284194Sdelphij * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23284194Sdelphij * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24284194Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25284194Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26284194Sdelphij * SUCH DAMAGE.
27284194Sdelphij */
28284194Sdelphij/*
29284194Sdelphij * file - find type of a file or files - main program.
30284194Sdelphij */
31284194Sdelphij
32284194Sdelphij#include "file.h"
33284194Sdelphij
34284194Sdelphij#ifndef	lint
35284194SdelphijFILE_RCSID("@(#)$File: file.c,v 1.160 2014/12/16 23:18:40 christos Exp $")
36284194Sdelphij#endif	/* lint */
37284194Sdelphij
38284194Sdelphij#include "magic.h"
39284194Sdelphij
40284194Sdelphij#include <stdlib.h>
41284194Sdelphij#include <unistd.h>
42284194Sdelphij#include <string.h>
43284194Sdelphij#ifdef RESTORE_TIME
44284194Sdelphij# if (__COHERENT__ >= 0x420)
45284194Sdelphij#  include <sys/utime.h>
46284194Sdelphij# else
47284194Sdelphij#  ifdef USE_UTIMES
48284194Sdelphij#   include <sys/time.h>
49284194Sdelphij#  else
50284194Sdelphij#   include <utime.h>
51284194Sdelphij#  endif
52284194Sdelphij# endif
53284194Sdelphij#endif
54284194Sdelphij#ifdef HAVE_UNISTD_H
55284194Sdelphij#include <unistd.h>	/* for read() */
56284194Sdelphij#endif
57284194Sdelphij#ifdef HAVE_WCHAR_H
58284194Sdelphij#include <wchar.h>
59284194Sdelphij#endif
60284194Sdelphij
61284194Sdelphij#if defined(HAVE_GETOPT_H) && defined(HAVE_STRUCT_OPTION)
62284194Sdelphij#include <getopt.h>
63284194Sdelphij#ifndef HAVE_GETOPT_LONG
64284194Sdelphijint getopt_long(int argc, char * const *argv, const char *optstring, const struct option *longopts, int *longindex);
65284194Sdelphij#endif
66284194Sdelphij#else
67284194Sdelphij#include "mygetopt.h"
68284194Sdelphij#endif
69284194Sdelphij
70284194Sdelphij#ifdef S_IFLNK
71284194Sdelphij#define FILE_FLAGS "-bcEhikLlNnprsvz0"
72284194Sdelphij#else
73284194Sdelphij#define FILE_FLAGS "-bcEiklNnprsvz0"
74284194Sdelphij#endif
75284194Sdelphij
76284194Sdelphij# define USAGE  \
77284194Sdelphij    "Usage: %s [" FILE_FLAGS \
78284194Sdelphij	"] [--apple] [--mime-encoding] [--mime-type]\n" \
79284194Sdelphij    "            [-e testname] [-F separator] [-f namefile] [-m magicfiles] " \
80284194Sdelphij    "file ...\n" \
81284194Sdelphij    "       %s -C [-m magicfiles]\n" \
82284194Sdelphij    "       %s [--help]\n"
83284194Sdelphij
84284194Sdelphijprivate int 		/* Global command-line options 		*/
85284194Sdelphij	bflag = 0,	/* brief output format	 		*/
86284194Sdelphij	nopad = 0,	/* Don't pad output			*/
87284194Sdelphij	nobuffer = 0,   /* Do not buffer stdout 		*/
88284194Sdelphij	nulsep = 0;	/* Append '\0' to the separator		*/
89284194Sdelphij
90284194Sdelphijprivate const char *separator = ":";	/* Default field separator	*/
91284194Sdelphijprivate const struct option long_options[] = {
92284194Sdelphij#define OPT(shortname, longname, opt, doc)      \
93284194Sdelphij    {longname, opt, NULL, shortname},
94284194Sdelphij#define OPT_LONGONLY(longname, opt, doc)        \
95284194Sdelphij    {longname, opt, NULL, 0},
96284194Sdelphij#include "file_opts.h"
97284194Sdelphij#undef OPT
98284194Sdelphij#undef OPT_LONGONLY
99284194Sdelphij    {0, 0, NULL, 0}
100284194Sdelphij};
101284194Sdelphij#define OPTSTRING	"bcCde:Ef:F:hiklLm:nNpP:rsvz0"
102284194Sdelphij
103284194Sdelphijprivate const struct {
104284194Sdelphij	const char *name;
105284194Sdelphij	int value;
106284194Sdelphij} nv[] = {
107284194Sdelphij	{ "apptype",	MAGIC_NO_CHECK_APPTYPE },
108284194Sdelphij	{ "ascii",	MAGIC_NO_CHECK_ASCII },
109284194Sdelphij	{ "cdf",	MAGIC_NO_CHECK_CDF },
110284194Sdelphij	{ "compress",	MAGIC_NO_CHECK_COMPRESS },
111284194Sdelphij	{ "elf",	MAGIC_NO_CHECK_ELF },
112284194Sdelphij	{ "encoding",	MAGIC_NO_CHECK_ENCODING },
113284194Sdelphij	{ "soft",	MAGIC_NO_CHECK_SOFT },
114284194Sdelphij	{ "tar",	MAGIC_NO_CHECK_TAR },
115284194Sdelphij	{ "text",	MAGIC_NO_CHECK_TEXT },	/* synonym for ascii */
116284194Sdelphij	{ "tokens",	MAGIC_NO_CHECK_TOKENS }, /* OBSOLETE: ignored for backwards compatibility */
117284194Sdelphij};
118284194Sdelphij
119284194Sdelphijprivate struct {
120284194Sdelphij	const char *name;
121284194Sdelphij	int tag;
122284194Sdelphij	size_t value;
123284194Sdelphij} pm[] = {
124284194Sdelphij	{ "indir",	MAGIC_PARAM_INDIR_MAX, 0 },
125284194Sdelphij	{ "name",	MAGIC_PARAM_NAME_MAX, 0 },
126284194Sdelphij	{ "elf_phnum",	MAGIC_PARAM_ELF_PHNUM_MAX, 0 },
127284194Sdelphij	{ "elf_shnum",	MAGIC_PARAM_ELF_SHNUM_MAX, 0 },
128284194Sdelphij	{ "elf_notes",	MAGIC_PARAM_ELF_NOTES_MAX, 0 },
129284194Sdelphij};
130284194Sdelphij
131284194Sdelphijprivate char *progname;		/* used throughout 		*/
132284194Sdelphij
133284194Sdelphijprivate void usage(void);
134284194Sdelphijprivate void docprint(const char *);
135284194Sdelphijprivate void help(void);
136284194Sdelphij
137284194Sdelphijprivate int unwrap(struct magic_set *, const char *);
138284194Sdelphijprivate int process(struct magic_set *ms, const char *, int);
139284194Sdelphijprivate struct magic_set *load(const char *, int);
140284194Sdelphijprivate void setparam(const char *);
141284194Sdelphijprivate void applyparam(magic_t);
142284194Sdelphij
143284194Sdelphij
144284194Sdelphij/*
145284194Sdelphij * main - parse arguments and handle options
146284194Sdelphij */
147284194Sdelphijint
148284194Sdelphijmain(int argc, char *argv[])
149284194Sdelphij{
150284194Sdelphij	int c;
151284194Sdelphij	size_t i;
152284194Sdelphij	int action = 0, didsomefiles = 0, errflg = 0;
153284194Sdelphij	int flags = 0, e = 0;
154284194Sdelphij	struct magic_set *magic = NULL;
155284194Sdelphij	int longindex;
156284194Sdelphij	const char *magicfile = NULL;		/* where the magic is	*/
157284194Sdelphij
158284194Sdelphij	/* makes islower etc work for other langs */
159284194Sdelphij#ifdef HAVE_SETLOCALE
160284194Sdelphij	(void)setlocale(LC_CTYPE, "");
161284194Sdelphij#endif
162284194Sdelphij
163284194Sdelphij#ifdef __EMX__
164284194Sdelphij	/* sh-like wildcard expansion! Shouldn't hurt at least ... */
165284194Sdelphij	_wildcard(&argc, &argv);
166284194Sdelphij#endif
167284194Sdelphij
168284194Sdelphij	if ((progname = strrchr(argv[0], '/')) != NULL)
169284194Sdelphij		progname++;
170284194Sdelphij	else
171284194Sdelphij		progname = argv[0];
172284194Sdelphij
173284194Sdelphij#ifdef S_IFLNK
174284194Sdelphij	flags |= getenv("POSIXLY_CORRECT") ? MAGIC_SYMLINK : 0;
175284194Sdelphij#endif
176284194Sdelphij	while ((c = getopt_long(argc, argv, OPTSTRING, long_options,
177284194Sdelphij	    &longindex)) != -1)
178284194Sdelphij		switch (c) {
179284194Sdelphij		case 0 :
180284194Sdelphij			switch (longindex) {
181284194Sdelphij			case 0:
182284194Sdelphij				help();
183284194Sdelphij				break;
184284194Sdelphij			case 10:
185284194Sdelphij				flags |= MAGIC_APPLE;
186284194Sdelphij				break;
187284194Sdelphij			case 11:
188284194Sdelphij				flags |= MAGIC_MIME_TYPE;
189284194Sdelphij				break;
190284194Sdelphij			case 12:
191284194Sdelphij				flags |= MAGIC_MIME_ENCODING;
192284194Sdelphij				break;
193284194Sdelphij			}
194284194Sdelphij			break;
195284194Sdelphij		case '0':
196284194Sdelphij			nulsep = 1;
197284194Sdelphij			break;
198284194Sdelphij		case 'b':
199284194Sdelphij			bflag++;
200284194Sdelphij			break;
201284194Sdelphij		case 'c':
202284194Sdelphij			action = FILE_CHECK;
203284194Sdelphij			break;
204284194Sdelphij		case 'C':
205284194Sdelphij			action = FILE_COMPILE;
206284194Sdelphij			break;
207284194Sdelphij		case 'd':
208284194Sdelphij			flags |= MAGIC_DEBUG|MAGIC_CHECK;
209284194Sdelphij			break;
210284194Sdelphij		case 'E':
211284194Sdelphij			flags |= MAGIC_ERROR;
212284194Sdelphij			break;
213284194Sdelphij		case 'e':
214284194Sdelphij			for (i = 0; i < sizeof(nv) / sizeof(nv[0]); i++)
215284194Sdelphij				if (strcmp(nv[i].name, optarg) == 0)
216284194Sdelphij					break;
217284194Sdelphij
218284194Sdelphij			if (i == sizeof(nv) / sizeof(nv[0]))
219284194Sdelphij				errflg++;
220284194Sdelphij			else
221284194Sdelphij				flags |= nv[i].value;
222284194Sdelphij			break;
223284194Sdelphij
224284194Sdelphij		case 'f':
225284194Sdelphij			if(action)
226284194Sdelphij				usage();
227284194Sdelphij			if (magic == NULL)
228284194Sdelphij				if ((magic = load(magicfile, flags)) == NULL)
229284194Sdelphij					return 1;
230284194Sdelphij			e |= unwrap(magic, optarg);
231284194Sdelphij			++didsomefiles;
232284194Sdelphij			break;
233284194Sdelphij		case 'F':
234284194Sdelphij			separator = optarg;
235284194Sdelphij			break;
236284194Sdelphij		case 'i':
237284194Sdelphij			flags |= MAGIC_MIME;
238284194Sdelphij			break;
239284194Sdelphij		case 'k':
240284194Sdelphij			flags |= MAGIC_CONTINUE;
241284194Sdelphij			break;
242284194Sdelphij		case 'l':
243284194Sdelphij			action = FILE_LIST;
244284194Sdelphij			break;
245284194Sdelphij		case 'm':
246284194Sdelphij			magicfile = optarg;
247284194Sdelphij			break;
248284194Sdelphij		case 'n':
249284194Sdelphij			++nobuffer;
250284194Sdelphij			break;
251284194Sdelphij		case 'N':
252284194Sdelphij			++nopad;
253284194Sdelphij			break;
254284194Sdelphij#if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
255284194Sdelphij		case 'p':
256284194Sdelphij			flags |= MAGIC_PRESERVE_ATIME;
257284194Sdelphij			break;
258284194Sdelphij#endif
259284194Sdelphij		case 'P':
260284194Sdelphij			setparam(optarg);
261284194Sdelphij			break;
262284194Sdelphij		case 'r':
263284194Sdelphij			flags |= MAGIC_RAW;
264284194Sdelphij			break;
265284194Sdelphij			break;
266284194Sdelphij		case 's':
267284194Sdelphij			flags |= MAGIC_DEVICES;
268284194Sdelphij			break;
269284194Sdelphij		case 'v':
270284194Sdelphij			if (magicfile == NULL)
271284194Sdelphij				magicfile = magic_getpath(magicfile, action);
272284194Sdelphij			(void)fprintf(stdout, "%s-%s\n", progname, VERSION);
273284194Sdelphij			(void)fprintf(stdout, "magic file from %s\n",
274284194Sdelphij				       magicfile);
275284194Sdelphij			return 0;
276284194Sdelphij		case 'z':
277284194Sdelphij			flags |= MAGIC_COMPRESS;
278284194Sdelphij			break;
279284194Sdelphij#ifdef S_IFLNK
280284194Sdelphij		case 'L':
281284194Sdelphij			flags |= MAGIC_SYMLINK;
282284194Sdelphij			break;
283284194Sdelphij		case 'h':
284284194Sdelphij			flags &= ~MAGIC_SYMLINK;
285284194Sdelphij			break;
286284194Sdelphij#endif
287284194Sdelphij		case '?':
288284194Sdelphij		default:
289284194Sdelphij			errflg++;
290284194Sdelphij			break;
291284194Sdelphij		}
292284194Sdelphij
293284194Sdelphij	if (errflg) {
294284194Sdelphij		usage();
295284194Sdelphij	}
296284194Sdelphij	if (e)
297284194Sdelphij		return e;
298284194Sdelphij
299284194Sdelphij	if (MAGIC_VERSION != magic_version())
300284194Sdelphij		(void)fprintf(stderr, "%s: compiled magic version [%d] "
301284194Sdelphij		    "does not match with shared library magic version [%d]\n",
302284194Sdelphij		    progname, MAGIC_VERSION, magic_version());
303284194Sdelphij
304284194Sdelphij	switch(action) {
305284194Sdelphij	case FILE_CHECK:
306284194Sdelphij	case FILE_COMPILE:
307284194Sdelphij	case FILE_LIST:
308284194Sdelphij		/*
309284194Sdelphij		 * Don't try to check/compile ~/.magic unless we explicitly
310284194Sdelphij		 * ask for it.
311284194Sdelphij		 */
312284194Sdelphij		magic = magic_open(flags|MAGIC_CHECK);
313284194Sdelphij		if (magic == NULL) {
314284194Sdelphij			(void)fprintf(stderr, "%s: %s\n", progname,
315284194Sdelphij			    strerror(errno));
316284194Sdelphij			return 1;
317284194Sdelphij		}
318284194Sdelphij
319284194Sdelphij
320284194Sdelphij		switch(action) {
321284194Sdelphij		case FILE_CHECK:
322284194Sdelphij			c = magic_check(magic, magicfile);
323284194Sdelphij			break;
324284194Sdelphij		case FILE_COMPILE:
325284194Sdelphij			c = magic_compile(magic, magicfile);
326284194Sdelphij			break;
327284194Sdelphij		case FILE_LIST:
328284194Sdelphij			c = magic_list(magic, magicfile);
329284194Sdelphij			break;
330284194Sdelphij		default:
331284194Sdelphij			abort();
332284194Sdelphij		}
333284194Sdelphij		if (c == -1) {
334284194Sdelphij			(void)fprintf(stderr, "%s: %s\n", progname,
335284194Sdelphij			    magic_error(magic));
336284194Sdelphij			return 1;
337284194Sdelphij		}
338284194Sdelphij		return 0;
339284194Sdelphij	default:
340284194Sdelphij		if (magic == NULL)
341284194Sdelphij			if ((magic = load(magicfile, flags)) == NULL)
342284194Sdelphij				return 1;
343284194Sdelphij		applyparam(magic);
344284194Sdelphij	}
345284194Sdelphij
346284194Sdelphij	if (optind == argc) {
347284194Sdelphij		if (!didsomefiles)
348284194Sdelphij			usage();
349284194Sdelphij	}
350284194Sdelphij	else {
351284194Sdelphij		size_t j, wid, nw;
352284194Sdelphij		for (wid = 0, j = (size_t)optind; j < (size_t)argc; j++) {
353284194Sdelphij			nw = file_mbswidth(argv[j]);
354284194Sdelphij			if (nw > wid)
355284194Sdelphij				wid = nw;
356284194Sdelphij		}
357284194Sdelphij		/*
358284194Sdelphij		 * If bflag is only set twice, set it depending on
359284194Sdelphij		 * number of files [this is undocumented, and subject to change]
360284194Sdelphij		 */
361284194Sdelphij		if (bflag == 2) {
362284194Sdelphij			bflag = optind >= argc - 1;
363284194Sdelphij		}
364284194Sdelphij		for (; optind < argc; optind++)
365284194Sdelphij			e |= process(magic, argv[optind], wid);
366284194Sdelphij	}
367284194Sdelphij
368284194Sdelphij	if (magic)
369284194Sdelphij		magic_close(magic);
370284194Sdelphij	return e;
371284194Sdelphij}
372284194Sdelphij
373284194Sdelphijprivate void
374284194Sdelphijapplyparam(magic_t magic)
375284194Sdelphij{
376284194Sdelphij	size_t i;
377284194Sdelphij
378284194Sdelphij	for (i = 0; i < __arraycount(pm); i++) {
379284194Sdelphij		if (pm[i].value == 0)
380284194Sdelphij			continue;
381284194Sdelphij		if (magic_setparam(magic, pm[i].tag, &pm[i].value) == -1) {
382284194Sdelphij			(void)fprintf(stderr, "%s: Can't set %s %s\n", progname,
383284194Sdelphij				pm[i].name, strerror(errno));
384284194Sdelphij			exit(1);
385284194Sdelphij		}
386284194Sdelphij	}
387284194Sdelphij}
388284194Sdelphij
389284194Sdelphijprivate void
390284194Sdelphijsetparam(const char *p)
391284194Sdelphij{
392284194Sdelphij	size_t i;
393284194Sdelphij	char *s;
394284194Sdelphij
395284194Sdelphij	if ((s = strchr(p, '=')) == NULL)
396284194Sdelphij		goto badparm;
397284194Sdelphij
398284194Sdelphij	for (i = 0; i < __arraycount(pm); i++) {
399284194Sdelphij		if (strncmp(p, pm[i].name, s - p) != 0)
400284194Sdelphij			continue;
401284194Sdelphij		pm[i].value = atoi(s + 1);
402284194Sdelphij		return;
403284194Sdelphij	}
404284194Sdelphijbadparm:
405284194Sdelphij	(void)fprintf(stderr, "%s: Unknown param %s\n", progname, p);
406284194Sdelphij	exit(1);
407284194Sdelphij}
408284194Sdelphij
409284194Sdelphijprivate struct magic_set *
410284194Sdelphij/*ARGSUSED*/
411284194Sdelphijload(const char *magicfile, int flags)
412284194Sdelphij{
413284194Sdelphij	struct magic_set *magic = magic_open(flags);
414284194Sdelphij	if (magic == NULL) {
415284194Sdelphij		(void)fprintf(stderr, "%s: %s\n", progname, strerror(errno));
416284194Sdelphij		return NULL;
417284194Sdelphij	}
418284194Sdelphij	if (magic_load(magic, magicfile) == -1) {
419284194Sdelphij		(void)fprintf(stderr, "%s: %s\n",
420284194Sdelphij		    progname, magic_error(magic));
421284194Sdelphij		magic_close(magic);
422284194Sdelphij		return NULL;
423284194Sdelphij	}
424284194Sdelphij	return magic;
425284194Sdelphij}
426284194Sdelphij
427284194Sdelphij/*
428284194Sdelphij * unwrap -- read a file of filenames, do each one.
429284194Sdelphij */
430284194Sdelphijprivate int
431284194Sdelphijunwrap(struct magic_set *ms, const char *fn)
432284194Sdelphij{
433284194Sdelphij	FILE *f;
434284194Sdelphij	ssize_t len;
435284194Sdelphij	char *line = NULL;
436284194Sdelphij	size_t llen = 0;
437284194Sdelphij	int wid = 0, cwid;
438284194Sdelphij	int e = 0;
439284194Sdelphij
440284194Sdelphij	if (strcmp("-", fn) == 0) {
441284194Sdelphij		f = stdin;
442284194Sdelphij		wid = 1;
443284194Sdelphij	} else {
444284194Sdelphij		if ((f = fopen(fn, "r")) == NULL) {
445284194Sdelphij			(void)fprintf(stderr, "%s: Cannot open `%s' (%s).\n",
446284194Sdelphij			    progname, fn, strerror(errno));
447284194Sdelphij			return 1;
448284194Sdelphij		}
449284194Sdelphij
450284194Sdelphij		while ((len = getline(&line, &llen, f)) > 0) {
451284194Sdelphij			if (line[len - 1] == '\n')
452284194Sdelphij				line[len - 1] = '\0';
453284194Sdelphij			cwid = file_mbswidth(line);
454284194Sdelphij			if (cwid > wid)
455284194Sdelphij				wid = cwid;
456284194Sdelphij		}
457284194Sdelphij
458284194Sdelphij		rewind(f);
459284194Sdelphij	}
460284194Sdelphij
461284194Sdelphij	while ((len = getline(&line, &llen, f)) > 0) {
462284194Sdelphij		if (line[len - 1] == '\n')
463284194Sdelphij			line[len - 1] = '\0';
464284194Sdelphij		e |= process(ms, line, wid);
465284194Sdelphij		if(nobuffer)
466284194Sdelphij			(void)fflush(stdout);
467284194Sdelphij	}
468284194Sdelphij
469284194Sdelphij	free(line);
470284194Sdelphij	(void)fclose(f);
471284194Sdelphij	return e;
472284194Sdelphij}
473284194Sdelphij
474284194Sdelphij/*
475284194Sdelphij * Called for each input file on the command line (or in a list of files)
476284194Sdelphij */
477284194Sdelphijprivate int
478284194Sdelphijprocess(struct magic_set *ms, const char *inname, int wid)
479284194Sdelphij{
480284194Sdelphij	const char *type;
481284194Sdelphij	int std_in = strcmp(inname, "-") == 0;
482284194Sdelphij
483284194Sdelphij	if (wid > 0 && !bflag) {
484284194Sdelphij		(void)printf("%s", std_in ? "/dev/stdin" : inname);
485284194Sdelphij		if (nulsep)
486284194Sdelphij			(void)putc('\0', stdout);
487284194Sdelphij		(void)printf("%s", separator);
488284194Sdelphij		(void)printf("%*s ",
489284194Sdelphij		    (int) (nopad ? 0 : (wid - file_mbswidth(inname))), "");
490284194Sdelphij	}
491284194Sdelphij
492284194Sdelphij	type = magic_file(ms, std_in ? NULL : inname);
493284194Sdelphij	if (type == NULL) {
494284194Sdelphij		(void)printf("ERROR: %s\n", magic_error(ms));
495284194Sdelphij		return 1;
496284194Sdelphij	} else {
497284194Sdelphij		(void)printf("%s\n", type);
498284194Sdelphij		return 0;
499284194Sdelphij	}
500284194Sdelphij}
501284194Sdelphij
502284194Sdelphijprotected size_t
503284194Sdelphijfile_mbswidth(const char *s)
504284194Sdelphij{
505284194Sdelphij#if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH)
506284194Sdelphij	size_t bytesconsumed, old_n, n, width = 0;
507284194Sdelphij	mbstate_t state;
508284194Sdelphij	wchar_t nextchar;
509284194Sdelphij	(void)memset(&state, 0, sizeof(mbstate_t));
510284194Sdelphij	old_n = n = strlen(s);
511284194Sdelphij
512284194Sdelphij	while (n > 0) {
513284194Sdelphij		bytesconsumed = mbrtowc(&nextchar, s, n, &state);
514284194Sdelphij		if (bytesconsumed == (size_t)(-1) ||
515284194Sdelphij		    bytesconsumed == (size_t)(-2)) {
516284194Sdelphij			/* Something went wrong, return something reasonable */
517284194Sdelphij			return old_n;
518284194Sdelphij		}
519284194Sdelphij		if (s[0] == '\n') {
520284194Sdelphij			/*
521284194Sdelphij			 * do what strlen() would do, so that caller
522284194Sdelphij			 * is always right
523284194Sdelphij			 */
524284194Sdelphij			width++;
525284194Sdelphij		} else {
526284194Sdelphij			int w = wcwidth(nextchar);
527284194Sdelphij			if (w > 0)
528284194Sdelphij				width += w;
529284194Sdelphij		}
530284194Sdelphij
531284194Sdelphij		s += bytesconsumed, n -= bytesconsumed;
532284194Sdelphij	}
533284194Sdelphij	return width;
534284194Sdelphij#else
535284194Sdelphij	return strlen(s);
536284194Sdelphij#endif
537284194Sdelphij}
538284194Sdelphij
539284194Sdelphijprivate void
540284194Sdelphijusage(void)
541284194Sdelphij{
542284194Sdelphij	(void)fprintf(stderr, USAGE, progname, progname, progname);
543284194Sdelphij	exit(1);
544284194Sdelphij}
545284194Sdelphij
546284194Sdelphijprivate void
547284194Sdelphijdocprint(const char *opts)
548284194Sdelphij{
549284194Sdelphij	size_t i;
550284194Sdelphij	int comma;
551284194Sdelphij	char *sp, *p;
552284194Sdelphij
553284194Sdelphij	p = strstr(opts, "%o");
554284194Sdelphij	if (p == NULL) {
555284194Sdelphij		fprintf(stdout, "%s", opts);
556284194Sdelphij		return;
557284194Sdelphij	}
558284194Sdelphij
559284194Sdelphij	for (sp = p - 1; sp > opts && *sp == ' '; sp--)
560284194Sdelphij		continue;
561284194Sdelphij
562284194Sdelphij	fprintf(stdout, "%.*s", (int)(p - opts), opts);
563284194Sdelphij
564284194Sdelphij	comma = 0;
565284194Sdelphij	for (i = 0; i < __arraycount(nv); i++) {
566284194Sdelphij		fprintf(stdout, "%s%s", comma++ ? ", " : "", nv[i].name);
567284194Sdelphij		if (i && i % 5 == 0) {
568284194Sdelphij			fprintf(stdout, ",\n%*s", (int)(p - sp - 1), "");
569284194Sdelphij			comma = 0;
570284194Sdelphij		}
571284194Sdelphij	}
572284194Sdelphij
573284194Sdelphij	fprintf(stdout, "%s", opts + (p - opts) + 2);
574284194Sdelphij}
575284194Sdelphij
576284194Sdelphijprivate void
577284194Sdelphijhelp(void)
578284194Sdelphij{
579284194Sdelphij	(void)fputs(
580284194Sdelphij"Usage: file [OPTION...] [FILE...]\n"
581284194Sdelphij"Determine type of FILEs.\n"
582284194Sdelphij"\n", stdout);
583284194Sdelphij#define OPT(shortname, longname, opt, doc)      \
584284194Sdelphij	fprintf(stdout, "  -%c, --" longname, shortname), \
585284194Sdelphij	docprint(doc);
586284194Sdelphij#define OPT_LONGONLY(longname, opt, doc)        \
587284194Sdelphij	fprintf(stdout, "      --" longname),	\
588284194Sdelphij	docprint(doc);
589284194Sdelphij#include "file_opts.h"
590284194Sdelphij#undef OPT
591284194Sdelphij#undef OPT_LONGONLY
592284194Sdelphij	fprintf(stdout, "\nReport bugs to http://bugs.gw.com/\n");
593284194Sdelphij	exit(0);
594284194Sdelphij}
595