1 /*
2 * Copyright (c) Ian F. Darwin 1986-1995.
3 * Software written by Ian F. Darwin and others;
4 * maintained 1995-present by Christos Zoulas and others.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice immediately at the beginning of the file, without modification,
11 * this list of conditions, and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28 /*
29 * file - find type of a file or files - main program.
30 */
31
32 #include "file.h"
33
34 #ifndef lint
35 FILE_RCSID("@(#)$File: file.c,v 1.215 2023/05/21 17:08:34 christos Exp $")
36 #endif /* lint */
37
38 #include "magic.h"
39
40 #include <stdlib.h>
41 #include <unistd.h>
42 #include <string.h>
43 #ifdef RESTORE_TIME
44 # if (__COHERENT__ >= 0x420)
45 # include <sys/utime.h>
46 # else
47 # ifdef USE_UTIMES
48 # include <sys/time.h>
49 # else
50 # include <utime.h>
51 # endif
52 # endif
53 #endif
54 #ifdef HAVE_UNISTD_H
55 #include <unistd.h> /* for read() */
56 #endif
57 #ifdef HAVE_WCHAR_H
58 #include <wchar.h>
59 #endif
60 #ifdef HAVE_WCTYPE_H
61 #include <wctype.h>
62 #endif
63 #if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH) && \
64 defined(HAVE_WCTYPE_H)
65 #define FILE_WIDE_SUPPORT
66 #else
67 #include <ctype.h>
68 #endif
69
70 #if defined(HAVE_GETOPT_H) && defined(HAVE_STRUCT_OPTION)
71 # include <getopt.h>
72 # ifndef HAVE_GETOPT_LONG
73 int getopt_long(int, char * const *, const char *,
74 const struct option *, int *);
75 # endif
76 # else
77 # include "mygetopt.h"
78 #endif
79
80 #ifdef S_IFLNK
81 # define IFLNK_h "h"
82 # define IFLNK_L "L"
83 #else
84 # define IFLNK_h ""
85 # define IFLNK_L ""
86 #endif
87
88 #define FILE_FLAGS "bcCdE" IFLNK_h "ik" IFLNK_L "lNnprsSvzZ0"
89 #define OPTSTRING "bcCde:Ef:F:hiklLm:nNpP:rsSvzZ0"
90
91 # define USAGE \
92 "Usage: %s [-" FILE_FLAGS "] [--apple] [--extension] [--mime-encoding]\n" \
93 " [--mime-type] [-e <testname>] [-F <separator>] " \
94 " [-f <namefile>]\n" \
95 " [-m <magicfiles>] [-P <parameter=value>] [--exclude-quiet]\n" \
96 " <file> ...\n" \
97 " %s -C [-m <magicfiles>]\n" \
98 " %s [--help]\n"
99
100 file_private int /* Global command-line options */
101 bflag = 0, /* brief output format */
102 nopad = 0, /* Don't pad output */
103 nobuffer = 0, /* Do not buffer stdout */
104 nulsep = 0; /* Append '\0' to the separator */
105
106 file_private const char *separator = ":"; /* Default field separator */
107 file_private const struct option long_options[] = {
108 #define OPT_HELP 1
109 #define OPT_APPLE 2
110 #define OPT_EXTENSIONS 3
111 #define OPT_MIME_TYPE 4
112 #define OPT_MIME_ENCODING 5
113 #define OPT_EXCLUDE_QUIET 6
114 #define OPT(shortname, longname, opt, def, doc) \
115 {longname, opt, NULL, shortname},
116 #define OPT_LONGONLY(longname, opt, def, doc, id) \
117 {longname, opt, NULL, id},
118 #include "file_opts.h"
119 #undef OPT
120 #undef OPT_LONGONLY
121 {0, 0, NULL, 0}
122 };
123
124 file_private const struct {
125 const char *name;
126 int value;
127 } nv[] = {
128 { "apptype", MAGIC_NO_CHECK_APPTYPE },
129 { "ascii", MAGIC_NO_CHECK_ASCII },
130 { "cdf", MAGIC_NO_CHECK_CDF },
131 { "compress", MAGIC_NO_CHECK_COMPRESS },
132 { "csv", MAGIC_NO_CHECK_CSV },
133 { "elf", MAGIC_NO_CHECK_ELF },
134 { "encoding", MAGIC_NO_CHECK_ENCODING },
135 { "soft", MAGIC_NO_CHECK_SOFT },
136 { "tar", MAGIC_NO_CHECK_TAR },
137 { "json", MAGIC_NO_CHECK_JSON },
138 { "simh", MAGIC_NO_CHECK_SIMH },
139 { "text", MAGIC_NO_CHECK_TEXT }, /* synonym for ascii */
140 { "tokens", MAGIC_NO_CHECK_TOKENS }, /* OBSOLETE: ignored for backwards compatibility */
141 };
142
143 file_private struct {
144 const char *name;
145 size_t value;
146 size_t def;
147 const char *desc;
148 int tag;
149 int set;
150 } pm[] = {
151 { "bytes", 0, FILE_BYTES_MAX, "max bytes to look inside file",
152 MAGIC_PARAM_BYTES_MAX, 0 },
153 { "elf_notes", 0, FILE_ELF_NOTES_MAX, "max ELF notes processed",
154 MAGIC_PARAM_ELF_NOTES_MAX, 0 },
155 { "elf_phnum", 0, FILE_ELF_PHNUM_MAX, "max ELF prog sections processed",
156 MAGIC_PARAM_ELF_PHNUM_MAX, 0 },
157 { "elf_shnum", 0, FILE_ELF_SHNUM_MAX, "max ELF sections processed",
158 MAGIC_PARAM_ELF_SHNUM_MAX, 0 },
159 { "elf_shsize", 0, FILE_ELF_SHSIZE_MAX, "max ELF section size",
160 MAGIC_PARAM_ELF_SHSIZE_MAX, 0 },
161 { "encoding", 0, FILE_ENCODING_MAX, "max bytes to scan for encoding",
162 MAGIC_PARAM_ENCODING_MAX, 0 },
163 { "indir", 0, FILE_INDIR_MAX, "recursion limit for indirection",
164 MAGIC_PARAM_INDIR_MAX, 0 },
165 { "name", 0, FILE_NAME_MAX, "use limit for name/use magic",
166 MAGIC_PARAM_NAME_MAX, 0 },
167 { "regex", 0, FILE_REGEX_MAX, "length limit for REGEX searches",
168 MAGIC_PARAM_REGEX_MAX, 0 },
169 };
170
171 file_private int posixly;
172
173 #ifdef __dead
174 __dead
175 #endif
176 file_private void usage(void);
177 file_private void docprint(const char *, int);
178 #ifdef __dead
179 __dead
180 #endif
181 file_private void help(void);
182
183 file_private int unwrap(struct magic_set *, const char *);
184 file_private int process(struct magic_set *ms, const char *, int);
185 file_private struct magic_set *load(const char *, int);
186 file_private void setparam(const char *);
187 file_private void applyparam(magic_t);
188
189
190 /*
191 * main - parse arguments and handle options
192 */
193 int
main(int argc,char * argv[])194 main(int argc, char *argv[])
195 {
196 int c;
197 size_t i, j, wid, nw;
198 int action = 0, didsomefiles = 0, errflg = 0;
199 int flags = 0, e = 0;
200 #ifdef HAVE_LIBSECCOMP
201 int sandbox = 1;
202 #endif
203 struct magic_set *magic = NULL;
204 int longindex;
205 const char *magicfile = NULL; /* where the magic is */
206 char *progname;
207
208 /* makes islower etc work for other langs */
209 (void)setlocale(LC_CTYPE, "");
210
211 #ifdef __EMX__
212 /* sh-like wildcard expansion! Shouldn't hurt at least ... */
213 _wildcard(&argc, &argv);
214 #endif
215
216 if ((progname = strrchr(argv[0], '/')) != NULL)
217 progname++;
218 else
219 progname = argv[0];
220
221 file_setprogname(progname);
222
223
224 #ifdef S_IFLNK
225 posixly = getenv("POSIXLY_CORRECT") != NULL;
226 flags |= posixly ? MAGIC_SYMLINK : 0;
227 #endif
228 while ((c = getopt_long(argc, argv, OPTSTRING, long_options,
229 &longindex)) != -1)
230 switch (c) {
231 case OPT_HELP:
232 help();
233 break;
234 case OPT_APPLE:
235 flags |= MAGIC_APPLE;
236 break;
237 case OPT_EXTENSIONS:
238 flags |= MAGIC_EXTENSION;
239 break;
240 case OPT_MIME_TYPE:
241 flags |= MAGIC_MIME_TYPE;
242 break;
243 case OPT_MIME_ENCODING:
244 flags |= MAGIC_MIME_ENCODING;
245 break;
246 case '0':
247 nulsep++;
248 break;
249 case 'b':
250 bflag++;
251 break;
252 case 'c':
253 action = FILE_CHECK;
254 break;
255 case 'C':
256 action = FILE_COMPILE;
257 break;
258 case 'd':
259 flags |= MAGIC_DEBUG|MAGIC_CHECK;
260 break;
261 case 'E':
262 flags |= MAGIC_ERROR;
263 break;
264 case 'e':
265 case OPT_EXCLUDE_QUIET:
266 for (i = 0; i < __arraycount(nv); i++)
267 if (strcmp(nv[i].name, optarg) == 0)
268 break;
269
270 if (i == __arraycount(nv)) {
271 if (c != OPT_EXCLUDE_QUIET)
272 errflg++;
273 } else
274 flags |= nv[i].value;
275 break;
276
277 case 'f':
278 if(action)
279 usage();
280 if (magic == NULL)
281 if ((magic = load(magicfile, flags)) == NULL)
282 return 1;
283 applyparam(magic);
284 e |= unwrap(magic, optarg);
285 ++didsomefiles;
286 break;
287 case 'F':
288 separator = optarg;
289 break;
290 case 'i':
291 flags |= MAGIC_MIME;
292 break;
293 case 'k':
294 flags |= MAGIC_CONTINUE;
295 break;
296 case 'l':
297 action = FILE_LIST;
298 break;
299 case 'm':
300 magicfile = optarg;
301 break;
302 case 'n':
303 ++nobuffer;
304 break;
305 case 'N':
306 ++nopad;
307 break;
308 #if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
309 case 'p':
310 flags |= MAGIC_PRESERVE_ATIME;
311 break;
312 #endif
313 case 'P':
314 setparam(optarg);
315 break;
316 case 'r':
317 flags |= MAGIC_RAW;
318 break;
319 case 's':
320 flags |= MAGIC_DEVICES;
321 break;
322 case 'S':
323 #ifdef HAVE_LIBSECCOMP
324 sandbox = 0;
325 #endif
326 break;
327 case 'v':
328 if (magicfile == NULL)
329 magicfile = magic_getpath(magicfile, action);
330 (void)fprintf(stdout, "%s-%s\n", file_getprogname(),
331 VERSION);
332 (void)fprintf(stdout, "magic file from %s\n",
333 magicfile);
334 #ifdef HAVE_LIBSECCOMP
335 (void)fprintf(stdout, "seccomp support included\n");
336 #endif
337 return 0;
338 case 'z':
339 flags |= MAGIC_COMPRESS;
340 break;
341
342 case 'Z':
343 flags |= MAGIC_COMPRESS|MAGIC_COMPRESS_TRANSP;
344 break;
345 #ifdef S_IFLNK
346 case 'L':
347 flags |= MAGIC_SYMLINK;
348 break;
349 case 'h':
350 flags &= ~MAGIC_SYMLINK;
351 break;
352 #endif
353 case '?':
354 default:
355 errflg++;
356 break;
357 }
358
359 if (errflg) {
360 usage();
361 }
362 if (e)
363 return e;
364
365 #ifdef HAVE_LIBSECCOMP
366 #if 0
367 if (sandbox && enable_sandbox_basic() == -1)
368 #else
369 if (sandbox && enable_sandbox_full() == -1)
370 #endif
371 file_err(EXIT_FAILURE, "SECCOMP initialisation failed");
372 if (sandbox)
373 flags |= MAGIC_NO_COMPRESS_FORK;
374 #endif /* HAVE_LIBSECCOMP */
375
376 if (MAGIC_VERSION != magic_version())
377 file_warnx("Compiled magic version [%d] "
378 "does not match with shared library magic version [%d]\n",
379 MAGIC_VERSION, magic_version());
380
381 switch(action) {
382 case FILE_CHECK:
383 case FILE_COMPILE:
384 case FILE_LIST:
385 /*
386 * Don't try to check/compile ~/.magic unless we explicitly
387 * ask for it.
388 */
389 magic = magic_open(flags|MAGIC_CHECK);
390 if (magic == NULL) {
391 file_warn("Can't create magic");
392 return 1;
393 }
394
395
396 switch(action) {
397 case FILE_CHECK:
398 c = magic_check(magic, magicfile);
399 break;
400 case FILE_COMPILE:
401 c = magic_compile(magic, magicfile);
402 break;
403 case FILE_LIST:
404 c = magic_list(magic, magicfile);
405 break;
406 default:
407 abort();
408 }
409 if (c == -1) {
410 file_warnx("%s", magic_error(magic));
411 e = 1;
412 goto out;
413 }
414 goto out;
415 default:
416 if (magic == NULL)
417 if ((magic = load(magicfile, flags)) == NULL)
418 return 1;
419 applyparam(magic);
420 }
421
422 if (optind == argc) {
423 if (!didsomefiles)
424 usage();
425 goto out;
426 }
427
428 for (wid = 0, j = CAST(size_t, optind); j < CAST(size_t, argc);
429 j++) {
430 nw = file_mbswidth(magic, argv[j]);
431 if (nw > wid)
432 wid = nw;
433 }
434
435 /*
436 * If bflag is only set twice, set it depending on
437 * number of files [this is undocumented, and subject to change]
438 */
439 if (bflag == 2) {
440 bflag = optind >= argc - 1;
441 }
442 for (; optind < argc; optind++)
443 e |= process(magic, argv[optind], wid);
444
445 out:
446 if (!nobuffer)
447 e |= fflush(stdout) != 0;
448
449 if (magic)
450 magic_close(magic);
451 return e;
452 }
453
454 file_private void
applyparam(magic_t magic)455 applyparam(magic_t magic)
456 {
457 size_t i;
458
459 for (i = 0; i < __arraycount(pm); i++) {
460 if (!pm[i].set)
461 continue;
462 if (magic_setparam(magic, pm[i].tag, &pm[i].value) == -1)
463 file_err(EXIT_FAILURE, "Can't set %s", pm[i].name);
464 }
465 }
466
467 file_private void
setparam(const char * p)468 setparam(const char *p)
469 {
470 size_t i;
471 char *s;
472
473 if ((s = CCAST(char *, strchr(p, '='))) == NULL)
474 goto badparm;
475
476 for (i = 0; i < __arraycount(pm); i++) {
477 if (strncmp(p, pm[i].name, s - p) != 0)
478 continue;
479 pm[i].value = atoi(s + 1);
480 pm[i].set = 1;
481 return;
482 }
483 badparm:
484 file_errx(EXIT_FAILURE, "Unknown param %s", p);
485 }
486
487 file_private struct magic_set *
488 /*ARGSUSED*/
load(const char * magicfile,int flags)489 load(const char *magicfile, int flags)
490 {
491 struct magic_set *magic = magic_open(flags);
492 const char *e;
493
494 if (magic == NULL) {
495 file_warn("Can't create magic");
496 return NULL;
497 }
498 if (magic_load(magic, magicfile) == -1) {
499 file_warn("%s", magic_error(magic));
500 magic_close(magic);
501 return NULL;
502 }
503 if ((e = magic_error(magic)) != NULL)
504 file_warn("%s", e);
505 return magic;
506 }
507
508 /*
509 * unwrap -- read a file of filenames, do each one.
510 */
511 file_private int
unwrap(struct magic_set * ms,const char * fn)512 unwrap(struct magic_set *ms, const char *fn)
513 {
514 FILE *f;
515 ssize_t len;
516 char *line = NULL;
517 size_t llen = 0;
518 int wid = 0, cwid;
519 int e = 0;
520 size_t fi = 0, fimax = 0;
521 char **flist = NULL;
522
523 if (strcmp("-", fn) == 0)
524 f = stdin;
525 else {
526 if ((f = fopen(fn, "r")) == NULL) {
527 file_warn("Cannot open `%s'", fn);
528 return 1;
529 }
530 }
531
532 while ((len = getline(&line, &llen, f)) > 0) {
533 if (line[len - 1] == '\n')
534 line[len - 1] = '\0';
535 cwid = file_mbswidth(ms, line);
536 if (nobuffer) {
537 e |= process(ms, line, cwid);
538 free(line);
539 line = NULL;
540 llen = 0;
541 continue;
542 }
543 if (cwid > wid)
544 wid = cwid;
545 if (fi >= fimax) {
546 fimax += 100;
547 char **nf = CAST(char **,
548 realloc(flist, fimax * sizeof(*flist)));
549 if (nf == NULL) {
550 file_err(EXIT_FAILURE,
551 "Cannot allocate memory for file list");
552 }
553 flist = nf;
554 }
555 flist[fi++] = line;
556 line = NULL;
557 llen = 0;
558 }
559
560 if (!nobuffer) {
561 fimax = fi;
562 for (fi = 0; fi < fimax; fi++) {
563 e |= process(ms, flist[fi], wid);
564 free(flist[fi]);
565 }
566 }
567 free(flist);
568
569 if (f != stdin)
570 (void)fclose(f);
571 return e;
572 }
573
574 file_private void
file_octal(unsigned char c)575 file_octal(unsigned char c)
576 {
577 (void)putc('\\', stdout);
578 (void)putc(((c >> 6) & 7) + '0', stdout);
579 (void)putc(((c >> 3) & 7) + '0', stdout);
580 (void)putc(((c >> 0) & 7) + '0', stdout);
581 }
582
583 file_private void
fname_print(const char * inname)584 fname_print(const char *inname)
585 {
586 size_t n = strlen(inname);
587 #ifdef FILE_WIDE_SUPPORT
588 mbstate_t state;
589 wchar_t nextchar;
590 size_t bytesconsumed;
591
592
593 (void)memset(&state, 0, sizeof(state));
594 while (n > 0) {
595 bytesconsumed = mbrtowc(&nextchar, inname, n, &state);
596 if (bytesconsumed == CAST(size_t, -1) ||
597 bytesconsumed == CAST(size_t, -2)) {
598 nextchar = *inname++;
599 n--;
600 (void)memset(&state, 0, sizeof(state));
601 file_octal(CAST(unsigned char, nextchar));
602 continue;
603 }
604 inname += bytesconsumed;
605 n -= bytesconsumed;
606 if (iswprint(nextchar)) {
607 printf("%lc", (wint_t)nextchar);
608 continue;
609 }
610 /* XXX: What if it is > 255? */
611 file_octal(CAST(unsigned char, nextchar));
612 }
613 #else
614 size_t i;
615 for (i = 0; i < n; i++) {
616 unsigned char c = CAST(unsigned char, inname[i]);
617 if (isprint(c)) {
618 (void)putc(c, stdout);
619 continue;
620 }
621 file_octal(c);
622 }
623 #endif
624 }
625
626 /*
627 * Called for each input file on the command line (or in a list of files)
628 */
629 file_private int
process(struct magic_set * ms,const char * inname,int wid)630 process(struct magic_set *ms, const char *inname, int wid)
631 {
632 const char *type, c = nulsep > 1 ? '\0' : '\n';
633 int std_in = strcmp(inname, "-") == 0;
634 int haderror = 0;
635
636 if (wid > 0 && !bflag) {
637 const char *pname = std_in ? "/dev/stdin" : inname;
638 if ((ms->flags & MAGIC_RAW) == 0)
639 fname_print(pname);
640 else
641 (void)printf("%s", pname);
642 if (nulsep)
643 (void)putc('\0', stdout);
644 if (nulsep < 2) {
645 (void)printf("%s", separator);
646 (void)printf("%*s ", CAST(int, nopad ? 0
647 : (wid - file_mbswidth(ms, inname))), "");
648 }
649 }
650
651 type = magic_file(ms, std_in ? NULL : inname);
652
653 if (type == NULL) {
654 haderror |= printf("ERROR: %s%c", magic_error(ms), c);
655 } else {
656 haderror |= printf("%s%c", type, c) < 0;
657 }
658 if (nobuffer)
659 haderror |= fflush(stdout) != 0;
660 return haderror || type == NULL;
661 }
662
663 file_protected size_t
file_mbswidth(struct magic_set * ms,const char * s)664 file_mbswidth(struct magic_set *ms, const char *s)
665 {
666 size_t width = 0;
667 #ifdef FILE_WIDE_SUPPORT
668 size_t bytesconsumed, n;
669 mbstate_t state;
670 wchar_t nextchar;
671
672 (void)memset(&state, 0, sizeof(state));
673 n = strlen(s);
674
675 while (n > 0) {
676 bytesconsumed = mbrtowc(&nextchar, s, n, &state);
677 if (bytesconsumed == CAST(size_t, -1) ||
678 bytesconsumed == CAST(size_t, -2)) {
679 nextchar = *s;
680 bytesconsumed = 1;
681 (void)memset(&state, 0, sizeof(state));
682 width += 4;
683 } else {
684 int w = wcwidth(nextchar);
685 width += ((ms->flags & MAGIC_RAW) != 0
686 || iswprint(nextchar)) ? (w > 0 ? w : 1) : 4;
687 }
688
689 s += bytesconsumed, n -= bytesconsumed;
690 }
691 #else
692 for (; *s; s++) {
693 width += (ms->flags & MAGIC_RAW) != 0
694 || isprint(CAST(unsigned char, *s)) ? 1 : 4;
695 }
696 #endif
697 return width;
698 }
699
700 file_private void
usage(void)701 usage(void)
702 {
703 const char *pn = file_getprogname();
704 (void)fprintf(stderr, USAGE, pn, pn, pn);
705 exit(EXIT_FAILURE);
706 }
707
708 file_private void
defprint(int def)709 defprint(int def)
710 {
711 if (!def)
712 return;
713 if (((def & 1) && posixly) || ((def & 2) && !posixly))
714 (void)fprintf(stdout, " (default)");
715 (void)putc('\n', stdout);
716 }
717
718 file_private void
docprint(const char * opts,int def)719 docprint(const char *opts, int def)
720 {
721 size_t i;
722 int comma, pad;
723 char *sp, *p;
724
725 p = CCAST(char *, strchr(opts, '%'));
726 if (p == NULL) {
727 (void)fprintf(stdout, "%s", opts);
728 defprint(def);
729 return;
730 }
731
732 for (sp = p - 1; sp > opts && *sp == ' '; sp--)
733 continue;
734
735 (void)printf("%.*s", CAST(int, p - opts), opts);
736 pad = (int)CAST(int, p - sp - 1);
737
738 switch (*++p) {
739 case 'e':
740 comma = 0;
741 for (i = 0; i < __arraycount(nv); i++) {
742 (void)printf("%s%s", comma++ ? ", " : "", nv[i].name);
743 if (i && i % 5 == 0 && i != __arraycount(nv) - 1) {
744 (void)printf(",\n%*s", pad, "");
745 comma = 0;
746 }
747 }
748 break;
749 case 'P':
750 for (i = 0; i < __arraycount(pm); i++) {
751 (void)printf("%9s %7zu %s", pm[i].name, pm[i].def,
752 pm[i].desc);
753 if (i != __arraycount(pm) - 1)
754 (void)printf("\n%*s", pad, "");
755 }
756 break;
757 default:
758 file_errx(EXIT_FAILURE, "Unknown escape `%c' in long options",
759 *p);
760 break;
761 }
762 (void)printf("%s", opts + (p - opts) + 1);
763
764 }
765
766 file_private void
help(void)767 help(void)
768 {
769 (void)fputs(
770 "Usage: file [OPTION...] [FILE...]\n"
771 "Determine type of FILEs.\n"
772 "\n", stdout);
773 #define OPT(shortname, longname, opt, def, doc) \
774 (void)printf(" -%c, --" longname, shortname), \
775 docprint(doc, def);
776 #define OPT_LONGONLY(longname, opt, def, doc, id) \
777 (void)printf(" --" longname), \
778 docprint(doc, def);
779 #include "file_opts.h"
780 #undef OPT
781 #undef OPT_LONGONLY
782 (void)printf("\nReport bugs to https://bugs.astron.com/\n");
783 exit(EXIT_SUCCESS);
784 }
785
786 file_private const char *file_progname;
787
788 file_protected void
file_setprogname(const char * progname)789 file_setprogname(const char *progname)
790 {
791 file_progname = progname;
792 }
793
794 file_protected const char *
file_getprogname(void)795 file_getprogname(void)
796 {
797 return file_progname;
798 }
799
800 file_protected void
file_err(int e,const char * fmt,...)801 file_err(int e, const char *fmt, ...)
802 {
803 va_list ap;
804 int se = errno;
805
806 va_start(ap, fmt);
807 (void)fprintf(stderr, "%s: ", file_progname);
808 (void)vfprintf(stderr, fmt, ap);
809 va_end(ap);
810 if (se)
811 (void)fprintf(stderr, " (%s)\n", strerror(se));
812 else
813 fputc('\n', stderr);
814 exit(e);
815 }
816
817 file_protected void
file_errx(int e,const char * fmt,...)818 file_errx(int e, const char *fmt, ...)
819 {
820 va_list ap;
821
822 va_start(ap, fmt);
823 (void)fprintf(stderr, "%s: ", file_progname);
824 (void)vfprintf(stderr, fmt, ap);
825 va_end(ap);
826 (void)fprintf(stderr, "\n");
827 exit(e);
828 }
829
830 file_protected void
file_warn(const char * fmt,...)831 file_warn(const char *fmt, ...)
832 {
833 va_list ap;
834 int se = errno;
835
836 va_start(ap, fmt);
837 (void)fprintf(stderr, "%s: ", file_progname);
838 (void)vfprintf(stderr, fmt, ap);
839 va_end(ap);
840 if (se)
841 (void)fprintf(stderr, " (%s)\n", strerror(se));
842 else
843 fputc('\n', stderr);
844 errno = se;
845 }
846
847 file_protected void
file_warnx(const char * fmt,...)848 file_warnx(const char *fmt, ...)
849 {
850 va_list ap;
851 int se = errno;
852
853 va_start(ap, fmt);
854 (void)fprintf(stderr, "%s: ", file_progname);
855 (void)vfprintf(stderr, fmt, ap);
856 va_end(ap);
857 (void)fprintf(stderr, "\n");
858 errno = se;
859 }
860