Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/file/src/file.c
39478 views
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.217 2024/09/29 16:49:25 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
{ "magwarn", 0, FILE_MAGWARN_MAX, "maximum number of magic warnings",
170
MAGIC_PARAM_MAGWARN_MAX, 0 },
171
};
172
173
file_private int posixly;
174
175
#ifdef __dead
176
__dead
177
#endif
178
file_private void usage(void);
179
file_private void docprint(const char *, int);
180
#ifdef __dead
181
__dead
182
#endif
183
file_private void help(void);
184
185
file_private int unwrap(struct magic_set *, const char *);
186
file_private int process(struct magic_set *ms, const char *, int);
187
file_private struct magic_set *load(const char *, int);
188
file_private void setparam(const char *);
189
file_private void applyparam(magic_t);
190
191
192
/*
193
* main - parse arguments and handle options
194
*/
195
int
196
main(int argc, char *argv[])
197
{
198
int c;
199
size_t i, j, wid, nw;
200
int action = 0, didsomefiles = 0, errflg = 0;
201
int flags = 0, e = 0;
202
#ifdef HAVE_LIBSECCOMP
203
int sandbox = 1;
204
#endif
205
struct magic_set *magic = NULL;
206
int longindex;
207
const char *magicfile = NULL; /* where the magic is */
208
char *progname;
209
210
/* makes islower etc work for other langs */
211
(void)setlocale(LC_CTYPE, "");
212
213
#ifdef __EMX__
214
/* sh-like wildcard expansion! Shouldn't hurt at least ... */
215
_wildcard(&argc, &argv);
216
#endif
217
218
if ((progname = strrchr(argv[0], '/')) != NULL)
219
progname++;
220
else
221
progname = argv[0];
222
223
file_setprogname(progname);
224
225
226
#ifdef S_IFLNK
227
posixly = getenv("POSIXLY_CORRECT") != NULL;
228
flags |= posixly ? MAGIC_SYMLINK : 0;
229
#endif
230
while ((c = getopt_long(argc, argv, OPTSTRING, long_options,
231
&longindex)) != -1)
232
switch (c) {
233
case OPT_HELP:
234
help();
235
break;
236
case OPT_APPLE:
237
flags |= MAGIC_APPLE;
238
break;
239
case OPT_EXTENSIONS:
240
flags |= MAGIC_EXTENSION;
241
break;
242
case OPT_MIME_TYPE:
243
flags |= MAGIC_MIME_TYPE;
244
break;
245
case OPT_MIME_ENCODING:
246
flags |= MAGIC_MIME_ENCODING;
247
break;
248
case '0':
249
nulsep++;
250
break;
251
case 'b':
252
bflag++;
253
break;
254
case 'c':
255
action = FILE_CHECK;
256
break;
257
case 'C':
258
action = FILE_COMPILE;
259
break;
260
case 'd':
261
flags |= MAGIC_DEBUG|MAGIC_CHECK;
262
break;
263
case 'E':
264
flags |= MAGIC_ERROR;
265
break;
266
case 'e':
267
case OPT_EXCLUDE_QUIET:
268
for (i = 0; i < __arraycount(nv); i++)
269
if (strcmp(nv[i].name, optarg) == 0)
270
break;
271
272
if (i == __arraycount(nv)) {
273
if (c != OPT_EXCLUDE_QUIET)
274
errflg++;
275
} else
276
flags |= nv[i].value;
277
break;
278
279
case 'f':
280
if(action)
281
usage();
282
if (magic == NULL)
283
if ((magic = load(magicfile, flags)) == NULL)
284
return 1;
285
applyparam(magic);
286
e |= unwrap(magic, optarg);
287
++didsomefiles;
288
break;
289
case 'F':
290
separator = optarg;
291
break;
292
case 'i':
293
flags |= MAGIC_MIME;
294
break;
295
case 'k':
296
flags |= MAGIC_CONTINUE;
297
break;
298
case 'l':
299
action = FILE_LIST;
300
break;
301
case 'm':
302
magicfile = optarg;
303
break;
304
case 'n':
305
++nobuffer;
306
break;
307
case 'N':
308
++nopad;
309
break;
310
#if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
311
case 'p':
312
flags |= MAGIC_PRESERVE_ATIME;
313
break;
314
#endif
315
case 'P':
316
setparam(optarg);
317
break;
318
case 'r':
319
flags |= MAGIC_RAW;
320
break;
321
case 's':
322
flags |= MAGIC_DEVICES;
323
break;
324
case 'S':
325
#ifdef HAVE_LIBSECCOMP
326
sandbox = 0;
327
#endif
328
break;
329
case 'v':
330
if (magicfile == NULL)
331
magicfile = magic_getpath(magicfile, action);
332
(void)fprintf(stdout, "%s-%s\n", file_getprogname(),
333
VERSION);
334
(void)fprintf(stdout, "magic file from %s\n",
335
magicfile);
336
#ifdef HAVE_LIBSECCOMP
337
(void)fprintf(stdout, "seccomp support included\n");
338
#endif
339
return 0;
340
case 'z':
341
flags |= MAGIC_COMPRESS;
342
break;
343
344
case 'Z':
345
flags |= MAGIC_COMPRESS|MAGIC_COMPRESS_TRANSP;
346
break;
347
#ifdef S_IFLNK
348
case 'L':
349
flags |= MAGIC_SYMLINK;
350
break;
351
case 'h':
352
flags &= ~MAGIC_SYMLINK;
353
break;
354
#endif
355
case '?':
356
default:
357
errflg++;
358
break;
359
}
360
361
if (errflg) {
362
usage();
363
}
364
if (e)
365
return e;
366
367
#ifdef HAVE_LIBSECCOMP
368
if (sandbox && enable_sandbox() == -1)
369
file_err(EXIT_FAILURE, "SECCOMP initialisation failed");
370
if (sandbox)
371
flags |= MAGIC_NO_COMPRESS_FORK;
372
#endif /* HAVE_LIBSECCOMP */
373
374
if (MAGIC_VERSION != magic_version())
375
file_warnx("Compiled magic version [%d] "
376
"does not match with shared library magic version [%d]\n",
377
MAGIC_VERSION, magic_version());
378
379
switch(action) {
380
case FILE_CHECK:
381
case FILE_COMPILE:
382
case FILE_LIST:
383
/*
384
* Don't try to check/compile ~/.magic unless we explicitly
385
* ask for it.
386
*/
387
magic = magic_open(flags|MAGIC_CHECK);
388
if (magic == NULL) {
389
file_warn("Can't create magic");
390
return 1;
391
}
392
393
394
switch(action) {
395
case FILE_CHECK:
396
c = magic_check(magic, magicfile);
397
break;
398
case FILE_COMPILE:
399
c = magic_compile(magic, magicfile);
400
break;
401
case FILE_LIST:
402
c = magic_list(magic, magicfile);
403
break;
404
default:
405
abort();
406
}
407
if (c == -1) {
408
file_warnx("%s", magic_error(magic));
409
e = 1;
410
goto out;
411
}
412
goto out;
413
default:
414
if (magic == NULL)
415
if ((magic = load(magicfile, flags)) == NULL)
416
return 1;
417
applyparam(magic);
418
}
419
420
if (optind == argc) {
421
if (!didsomefiles)
422
usage();
423
goto out;
424
}
425
426
for (wid = 0, j = CAST(size_t, optind); j < CAST(size_t, argc);
427
j++) {
428
nw = file_mbswidth(magic, argv[j]);
429
if (nw > wid)
430
wid = nw;
431
}
432
433
/*
434
* If bflag is only set twice, set it depending on
435
* number of files [this is undocumented, and subject to change]
436
*/
437
if (bflag == 2) {
438
bflag = optind >= argc - 1;
439
}
440
for (; optind < argc; optind++)
441
e |= process(magic, argv[optind], wid);
442
443
out:
444
if (!nobuffer)
445
e |= fflush(stdout) != 0;
446
447
if (magic)
448
magic_close(magic);
449
return e;
450
}
451
452
file_private void
453
applyparam(magic_t magic)
454
{
455
size_t i;
456
457
for (i = 0; i < __arraycount(pm); i++) {
458
if (!pm[i].set)
459
continue;
460
if (magic_setparam(magic, pm[i].tag, &pm[i].value) == -1)
461
file_err(EXIT_FAILURE, "Can't set %s", pm[i].name);
462
}
463
}
464
465
file_private void
466
setparam(const char *p)
467
{
468
size_t i;
469
char *s;
470
471
if ((s = CCAST(char *, strchr(p, '='))) == NULL)
472
goto badparm;
473
474
for (i = 0; i < __arraycount(pm); i++) {
475
if (strncmp(p, pm[i].name, s - p) != 0)
476
continue;
477
pm[i].value = atoi(s + 1);
478
pm[i].set = 1;
479
return;
480
}
481
badparm:
482
file_errx(EXIT_FAILURE, "Unknown param %s", p);
483
}
484
485
file_private struct magic_set *
486
/*ARGSUSED*/
487
load(const char *magicfile, int flags)
488
{
489
struct magic_set *magic = magic_open(flags);
490
const char *e;
491
492
if (magic == NULL) {
493
file_warn("Can't create magic");
494
return NULL;
495
}
496
if (magic_load(magic, magicfile) == -1) {
497
file_warn("%s", magic_error(magic));
498
magic_close(magic);
499
return NULL;
500
}
501
if ((e = magic_error(magic)) != NULL)
502
file_warn("%s", e);
503
return magic;
504
}
505
506
/*
507
* unwrap -- read a file of filenames, do each one.
508
*/
509
file_private int
510
unwrap(struct magic_set *ms, const char *fn)
511
{
512
FILE *f;
513
ssize_t len;
514
char *line = NULL;
515
size_t llen = 0;
516
int wid = 0, cwid;
517
int e = 0;
518
size_t fi = 0, fimax = 0;
519
char **flist = NULL;
520
521
if (strcmp("-", fn) == 0)
522
f = stdin;
523
else {
524
if ((f = fopen(fn, "r")) == NULL) {
525
file_warn("Cannot open `%s'", fn);
526
return 1;
527
}
528
}
529
530
while ((len = getline(&line, &llen, f)) > 0) {
531
if (line[len - 1] == '\n')
532
line[len - 1] = '\0';
533
cwid = file_mbswidth(ms, line);
534
if (nobuffer) {
535
e |= process(ms, line, cwid);
536
free(line);
537
line = NULL;
538
llen = 0;
539
continue;
540
}
541
if (cwid > wid)
542
wid = cwid;
543
if (fi >= fimax) {
544
fimax += 100;
545
char **nf = CAST(char **,
546
realloc(flist, fimax * sizeof(*flist)));
547
if (nf == NULL) {
548
file_err(EXIT_FAILURE,
549
"Cannot allocate memory for file list");
550
}
551
flist = nf;
552
}
553
flist[fi++] = line;
554
line = NULL;
555
llen = 0;
556
}
557
558
if (!nobuffer) {
559
fimax = fi;
560
for (fi = 0; fi < fimax; fi++) {
561
e |= process(ms, flist[fi], wid);
562
free(flist[fi]);
563
}
564
}
565
free(flist);
566
567
if (f != stdin)
568
(void)fclose(f);
569
return e;
570
}
571
572
file_private void
573
file_octal(unsigned char c)
574
{
575
(void)putc('\\', stdout);
576
(void)putc(((c >> 6) & 7) + '0', stdout);
577
(void)putc(((c >> 3) & 7) + '0', stdout);
578
(void)putc(((c >> 0) & 7) + '0', stdout);
579
}
580
581
file_private void
582
fname_print(const char *inname)
583
{
584
size_t n = strlen(inname);
585
#ifdef FILE_WIDE_SUPPORT
586
mbstate_t state;
587
wchar_t nextchar;
588
size_t bytesconsumed;
589
590
591
(void)memset(&state, 0, sizeof(state));
592
while (n > 0) {
593
bytesconsumed = mbrtowc(&nextchar, inname, n, &state);
594
if (bytesconsumed == CAST(size_t, -1) ||
595
bytesconsumed == CAST(size_t, -2)) {
596
nextchar = *inname++;
597
n--;
598
(void)memset(&state, 0, sizeof(state));
599
file_octal(CAST(unsigned char, nextchar));
600
continue;
601
}
602
inname += bytesconsumed;
603
n -= bytesconsumed;
604
if (iswprint(nextchar)) {
605
printf("%lc", (wint_t)nextchar);
606
continue;
607
}
608
/* XXX: What if it is > 255? */
609
file_octal(CAST(unsigned char, nextchar));
610
}
611
#else
612
size_t i;
613
for (i = 0; i < n; i++) {
614
unsigned char c = CAST(unsigned char, inname[i]);
615
if (isprint(c)) {
616
(void)putc(c, stdout);
617
continue;
618
}
619
file_octal(c);
620
}
621
#endif
622
}
623
624
/*
625
* Called for each input file on the command line (or in a list of files)
626
*/
627
file_private int
628
process(struct magic_set *ms, const char *inname, int wid)
629
{
630
const char *type, c = nulsep > 1 ? '\0' : '\n';
631
int std_in = strcmp(inname, "-") == 0;
632
int haderror = 0;
633
634
if (wid > 0 && !bflag) {
635
const char *pname = std_in ? "/dev/stdin" : inname;
636
if ((ms->flags & MAGIC_RAW) == 0)
637
fname_print(pname);
638
else
639
(void)printf("%s", pname);
640
if (nulsep)
641
(void)putc('\0', stdout);
642
if (nulsep < 2) {
643
(void)printf("%s", separator);
644
(void)printf("%*s ", CAST(int, nopad ? 0
645
: (wid - file_mbswidth(ms, inname))), "");
646
}
647
}
648
649
type = magic_file(ms, std_in ? NULL : inname);
650
651
if (type == NULL) {
652
haderror |= printf("ERROR: %s%c", magic_error(ms), c);
653
} else {
654
haderror |= printf("%s%c", type, c) < 0;
655
}
656
if (nobuffer)
657
haderror |= fflush(stdout) != 0;
658
return haderror || type == NULL;
659
}
660
661
file_protected size_t
662
file_mbswidth(struct magic_set *ms, const char *s)
663
{
664
size_t width = 0;
665
#ifdef FILE_WIDE_SUPPORT
666
size_t bytesconsumed, n;
667
mbstate_t state;
668
wchar_t nextchar;
669
670
(void)memset(&state, 0, sizeof(state));
671
n = strlen(s);
672
673
while (n > 0) {
674
bytesconsumed = mbrtowc(&nextchar, s, n, &state);
675
if (bytesconsumed == CAST(size_t, -1) ||
676
bytesconsumed == CAST(size_t, -2)) {
677
nextchar = *s;
678
bytesconsumed = 1;
679
(void)memset(&state, 0, sizeof(state));
680
width += 4;
681
} else {
682
int w = wcwidth(nextchar);
683
width += ((ms->flags & MAGIC_RAW) != 0
684
|| iswprint(nextchar)) ? (w > 0 ? w : 1) : 4;
685
}
686
687
s += bytesconsumed, n -= bytesconsumed;
688
}
689
#else
690
for (; *s; s++) {
691
width += (ms->flags & MAGIC_RAW) != 0
692
|| isprint(CAST(unsigned char, *s)) ? 1 : 4;
693
}
694
#endif
695
return width;
696
}
697
698
file_private void
699
usage(void)
700
{
701
const char *pn = file_getprogname();
702
(void)fprintf(stderr, USAGE, pn, pn, pn);
703
exit(EXIT_FAILURE);
704
}
705
706
file_private void
707
defprint(int def)
708
{
709
if (!def)
710
return;
711
if (((def & 1) && posixly) || ((def & 2) && !posixly))
712
(void)fprintf(stdout, " (default)");
713
(void)putc('\n', stdout);
714
}
715
716
file_private void
717
docprint(const char *opts, int def)
718
{
719
size_t i;
720
int comma, pad;
721
char *sp, *p;
722
723
p = CCAST(char *, strchr(opts, '%'));
724
if (p == NULL) {
725
(void)fprintf(stdout, "%s", opts);
726
defprint(def);
727
return;
728
}
729
730
for (sp = p - 1; sp > opts && *sp == ' '; sp--)
731
continue;
732
733
(void)printf("%.*s", CAST(int, p - opts), opts);
734
pad = (int)CAST(int, p - sp - 1);
735
736
switch (*++p) {
737
case 'e':
738
comma = 0;
739
for (i = 0; i < __arraycount(nv); i++) {
740
(void)printf("%s%s", comma++ ? ", " : "", nv[i].name);
741
if (i && i % 5 == 0 && i != __arraycount(nv) - 1) {
742
(void)printf(",\n%*s", pad, "");
743
comma = 0;
744
}
745
}
746
break;
747
case 'P':
748
for (i = 0; i < __arraycount(pm); i++) {
749
(void)printf("%9s %7zu %s", pm[i].name, pm[i].def,
750
pm[i].desc);
751
if (i != __arraycount(pm) - 1)
752
(void)printf("\n%*s", pad, "");
753
}
754
break;
755
default:
756
file_errx(EXIT_FAILURE, "Unknown escape `%c' in long options",
757
*p);
758
break;
759
}
760
(void)printf("%s", opts + (p - opts) + 1);
761
762
}
763
764
file_private void
765
help(void)
766
{
767
(void)fputs(
768
"Usage: file [OPTION...] [FILE...]\n"
769
"Determine type of FILEs.\n"
770
"\n", stdout);
771
#define OPT(shortname, longname, opt, def, doc) \
772
(void)printf(" -%c, --" longname, shortname), \
773
docprint(doc, def);
774
#define OPT_LONGONLY(longname, opt, def, doc, id) \
775
(void)printf(" --" longname), \
776
docprint(doc, def);
777
#include "file_opts.h"
778
#undef OPT
779
#undef OPT_LONGONLY
780
(void)printf("\nReport bugs to https://bugs.astron.com/\n");
781
exit(EXIT_SUCCESS);
782
}
783
784
file_private const char *file_progname;
785
786
file_protected void
787
file_setprogname(const char *progname)
788
{
789
file_progname = progname;
790
}
791
792
file_protected const char *
793
file_getprogname(void)
794
{
795
return file_progname;
796
}
797
798
file_protected void
799
file_err(int e, const char *fmt, ...)
800
{
801
va_list ap;
802
int se = errno;
803
804
va_start(ap, fmt);
805
(void)fprintf(stderr, "%s: ", file_progname);
806
(void)vfprintf(stderr, fmt, ap);
807
va_end(ap);
808
if (se)
809
(void)fprintf(stderr, " (%s)\n", strerror(se));
810
else
811
fputc('\n', stderr);
812
exit(e);
813
}
814
815
file_protected void
816
file_errx(int e, const char *fmt, ...)
817
{
818
va_list ap;
819
820
va_start(ap, fmt);
821
(void)fprintf(stderr, "%s: ", file_progname);
822
(void)vfprintf(stderr, fmt, ap);
823
va_end(ap);
824
(void)fprintf(stderr, "\n");
825
exit(e);
826
}
827
828
file_protected void
829
file_warn(const char *fmt, ...)
830
{
831
va_list ap;
832
int se = errno;
833
834
va_start(ap, fmt);
835
(void)fprintf(stderr, "%s: ", file_progname);
836
(void)vfprintf(stderr, fmt, ap);
837
va_end(ap);
838
if (se)
839
(void)fprintf(stderr, " (%s)\n", strerror(se));
840
else
841
fputc('\n', stderr);
842
errno = se;
843
}
844
845
file_protected void
846
file_warnx(const char *fmt, ...)
847
{
848
va_list ap;
849
int se = errno;
850
851
va_start(ap, fmt);
852
(void)fprintf(stderr, "%s: ", file_progname);
853
(void)vfprintf(stderr, fmt, ap);
854
va_end(ap);
855
(void)fprintf(stderr, "\n");
856
errno = se;
857
}
858
859