]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/file/src/file.c
Import DTS files for riscv from Linux 5.4
[FreeBSD/FreeBSD.git] / contrib / file / src / file.c
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.181 2019/03/28 20:54:03 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
61 #if defined(HAVE_GETOPT_H) && defined(HAVE_STRUCT_OPTION)
62 # include <getopt.h>
63 # ifndef HAVE_GETOPT_LONG
64 int getopt_long(int, char * const *, const char *,
65     const struct option *, int *);
66 # endif
67 # else
68 #  include "mygetopt.h"
69 #endif
70
71 #ifdef S_IFLNK
72 # define IFLNK_h "h"
73 # define IFLNK_L "L"
74 #else
75 # define IFLNK_h ""
76 # define IFLNK_L ""
77 #endif
78
79 #ifdef HAVE_LIBSECCOMP
80 # define SECCOMP_S "S"
81 #else
82 # define SECCOMP_S ""
83 #endif
84
85 #define FILE_FLAGS      "bcCdE" IFLNK_h "ik" IFLNK_L "lNnprs" SECCOMP_S "vzZ0"
86 #define OPTSTRING       "bcCde:Ef:F:hiklLm:nNpP:rsSvzZ0"
87
88 # define USAGE  \
89     "Usage: %s [-" FILE_FLAGS "] [--apple] [--extension] [--mime-encoding]\n" \
90     "            [--mime-type] [-e <testname>] [-F <separator>] " \
91     " [-f <namefile>]\n" \
92     "            [-m <magicfiles>] [-P <parameter=value>] <file> ...\n" \
93     "       %s -C [-m <magicfiles>]\n" \
94     "       %s [--help]\n"
95
96 private int             /* Global command-line options          */
97         bflag = 0,      /* brief output format                  */
98         nopad = 0,      /* Don't pad output                     */
99         nobuffer = 0,   /* Do not buffer stdout                 */
100         nulsep = 0;     /* Append '\0' to the separator         */
101
102 private const char *separator = ":";    /* Default field separator      */
103 private const struct option long_options[] = {
104 #define OPT_HELP                1
105 #define OPT_APPLE               2
106 #define OPT_EXTENSIONS          3
107 #define OPT_MIME_TYPE           4
108 #define OPT_MIME_ENCODING       5
109 #define OPT(shortname, longname, opt, def, doc)      \
110     {longname, opt, NULL, shortname},
111 #define OPT_LONGONLY(longname, opt, def, doc, id)        \
112     {longname, opt, NULL, id},
113 #include "file_opts.h"
114 #undef OPT
115 #undef OPT_LONGONLY
116     {0, 0, NULL, 0}
117     };
118
119 private const struct {
120         const char *name;
121         int value;
122 } nv[] = {
123         { "apptype",    MAGIC_NO_CHECK_APPTYPE },
124         { "ascii",      MAGIC_NO_CHECK_ASCII },
125         { "cdf",        MAGIC_NO_CHECK_CDF },
126         { "compress",   MAGIC_NO_CHECK_COMPRESS },
127         { "elf",        MAGIC_NO_CHECK_ELF },
128         { "encoding",   MAGIC_NO_CHECK_ENCODING },
129         { "soft",       MAGIC_NO_CHECK_SOFT },
130         { "tar",        MAGIC_NO_CHECK_TAR },
131         { "json",       MAGIC_NO_CHECK_JSON },
132         { "text",       MAGIC_NO_CHECK_TEXT },  /* synonym for ascii */
133         { "tokens",     MAGIC_NO_CHECK_TOKENS }, /* OBSOLETE: ignored for backwards compatibility */
134 };
135
136 private struct {
137         const char *name;
138         int tag;
139         size_t value;
140         int set;
141 } pm[] = {
142         { "indir",      MAGIC_PARAM_INDIR_MAX, 0, 0 },
143         { "name",       MAGIC_PARAM_NAME_MAX, 0, 0 },
144         { "elf_phnum",  MAGIC_PARAM_ELF_PHNUM_MAX, 0, 0 },
145         { "elf_shnum",  MAGIC_PARAM_ELF_SHNUM_MAX, 0, 0 },
146         { "elf_notes",  MAGIC_PARAM_ELF_NOTES_MAX, 0, 0 },
147         { "regex",      MAGIC_PARAM_REGEX_MAX, 0, 0 },
148         { "bytes",      MAGIC_PARAM_BYTES_MAX, 0, 0 },
149 };
150
151 private int posixly;
152
153 #ifdef __dead
154 __dead
155 #endif
156 private void usage(void);
157 private void docprint(const char *, int);
158 #ifdef __dead
159 __dead
160 #endif
161 private void help(void);
162
163 private int unwrap(struct magic_set *, const char *);
164 private int process(struct magic_set *ms, const char *, int);
165 private struct magic_set *load(const char *, int);
166 private void setparam(const char *);
167 private void applyparam(magic_t);
168
169
170 /*
171  * main - parse arguments and handle options
172  */
173 int
174 main(int argc, char *argv[])
175 {
176         int c;
177         size_t i;
178         int action = 0, didsomefiles = 0, errflg = 0;
179         int flags = 0, e = 0;
180 #ifdef HAVE_LIBSECCOMP
181         int sandbox = 1;
182 #endif
183         struct magic_set *magic = NULL;
184         int longindex;
185         const char *magicfile = NULL;           /* where the magic is   */
186         char *progname;
187
188         /* makes islower etc work for other langs */
189         (void)setlocale(LC_CTYPE, "");
190
191 #ifdef __EMX__
192         /* sh-like wildcard expansion! Shouldn't hurt at least ... */
193         _wildcard(&argc, &argv);
194 #endif
195
196         if ((progname = strrchr(argv[0], '/')) != NULL)
197                 progname++;
198         else
199                 progname = argv[0];
200
201         file_setprogname(progname);
202
203
204 #ifdef S_IFLNK
205         posixly = getenv("POSIXLY_CORRECT") != NULL;
206         flags |=  posixly ? MAGIC_SYMLINK : 0;
207 #endif
208         while ((c = getopt_long(argc, argv, OPTSTRING, long_options,
209             &longindex)) != -1)
210                 switch (c) {
211                 case OPT_HELP:
212                         help();
213                         break;
214                 case OPT_APPLE:
215                         flags |= MAGIC_APPLE;
216                         break;
217                 case OPT_EXTENSIONS:
218                         flags |= MAGIC_EXTENSION;
219                         break;
220                 case OPT_MIME_TYPE:
221                         flags |= MAGIC_MIME_TYPE;
222                         break;
223                 case OPT_MIME_ENCODING:
224                         flags |= MAGIC_MIME_ENCODING;
225                         break;
226                 case '0':
227                         nulsep++;
228                         break;
229                 case 'b':
230                         bflag++;
231                         break;
232                 case 'c':
233                         action = FILE_CHECK;
234                         break;
235                 case 'C':
236                         action = FILE_COMPILE;
237                         break;
238                 case 'd':
239                         flags |= MAGIC_DEBUG|MAGIC_CHECK;
240                         break;
241                 case 'E':
242                         flags |= MAGIC_ERROR;
243                         break;
244                 case 'e':
245                         for (i = 0; i < __arraycount(nv); i++)
246                                 if (strcmp(nv[i].name, optarg) == 0)
247                                         break;
248
249                         if (i == __arraycount(nv))
250                                 errflg++;
251                         else
252                                 flags |= nv[i].value;
253                         break;
254
255                 case 'f':
256                         if(action)
257                                 usage();
258                         if (magic == NULL)
259                                 if ((magic = load(magicfile, flags)) == NULL)
260                                         return 1;
261                         applyparam(magic);
262                         e |= unwrap(magic, optarg);
263                         ++didsomefiles;
264                         break;
265                 case 'F':
266                         separator = optarg;
267                         break;
268                 case 'i':
269                         flags |= MAGIC_MIME;
270                         break;
271                 case 'k':
272                         flags |= MAGIC_CONTINUE;
273                         break;
274                 case 'l':
275                         action = FILE_LIST;
276                         break;
277                 case 'm':
278                         magicfile = optarg;
279                         break;
280                 case 'n':
281                         ++nobuffer;
282                         break;
283                 case 'N':
284                         ++nopad;
285                         break;
286 #if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
287                 case 'p':
288                         flags |= MAGIC_PRESERVE_ATIME;
289                         break;
290 #endif
291                 case 'P':
292                         setparam(optarg);
293                         break;
294                 case 'r':
295                         flags |= MAGIC_RAW;
296                         break;
297                 case 's':
298                         flags |= MAGIC_DEVICES;
299                         break;
300 #ifdef HAVE_LIBSECCOMP
301                 case 'S':
302                         sandbox = 0;
303                         break;
304 #endif
305                 case 'v':
306                         if (magicfile == NULL)
307                                 magicfile = magic_getpath(magicfile, action);
308                         (void)fprintf(stdout, "%s-%s\n", file_getprogname(),
309                             VERSION);
310                         (void)fprintf(stdout, "magic file from %s\n",
311                             magicfile);
312                         return 0;
313                 case 'z':
314                         flags |= MAGIC_COMPRESS;
315                         break;
316
317                 case 'Z':
318                         flags |= MAGIC_COMPRESS|MAGIC_COMPRESS_TRANSP;
319                         break;
320 #ifdef S_IFLNK
321                 case 'L':
322                         flags |= MAGIC_SYMLINK;
323                         break;
324                 case 'h':
325                         flags &= ~MAGIC_SYMLINK;
326                         break;
327 #endif
328                 case '?':
329                 default:
330                         errflg++;
331                         break;
332                 }
333
334         if (errflg) {
335                 usage();
336         }
337         if (e)
338                 return e;
339
340 #ifdef HAVE_LIBSECCOMP
341 #if 0
342         if (sandbox && enable_sandbox_basic() == -1)
343 #else
344         if (sandbox && enable_sandbox_full() == -1)
345 #endif
346                 file_err(EXIT_FAILURE, "SECCOMP initialisation failed");
347 #endif /* HAVE_LIBSECCOMP */
348
349         if (MAGIC_VERSION != magic_version())
350                 file_warnx("Compiled magic version [%d] "
351                     "does not match with shared library magic version [%d]\n",
352                     MAGIC_VERSION, magic_version());
353
354         switch(action) {
355         case FILE_CHECK:
356         case FILE_COMPILE:
357         case FILE_LIST:
358                 /*
359                  * Don't try to check/compile ~/.magic unless we explicitly
360                  * ask for it.
361                  */
362                 magic = magic_open(flags|MAGIC_CHECK);
363                 if (magic == NULL) {
364                         file_warn("Can't create magic");
365                         return 1;
366                 }
367
368
369                 switch(action) {
370                 case FILE_CHECK:
371                         c = magic_check(magic, magicfile);
372                         break;
373                 case FILE_COMPILE:
374                         c = magic_compile(magic, magicfile);
375                         break;
376                 case FILE_LIST:
377                         c = magic_list(magic, magicfile);
378                         break;
379                 default:
380                         abort();
381                 }
382                 if (c == -1) {
383                         file_warnx("%s", magic_error(magic));
384                         e = 1;
385                         goto out;
386                 }
387                 goto out;
388         default:
389                 if (magic == NULL)
390                         if ((magic = load(magicfile, flags)) == NULL)
391                                 return 1;
392                 applyparam(magic);
393         }
394
395         if (optind == argc) {
396                 if (!didsomefiles)
397                         usage();
398         }
399         else {
400                 size_t j, wid, nw;
401                 for (wid = 0, j = CAST(size_t, optind); j < CAST(size_t, argc);
402                     j++) {
403                         nw = file_mbswidth(argv[j]);
404                         if (nw > wid)
405                                 wid = nw;
406                 }
407                 /*
408                  * If bflag is only set twice, set it depending on
409                  * number of files [this is undocumented, and subject to change]
410                  */
411                 if (bflag == 2) {
412                         bflag = optind >= argc - 1;
413                 }
414                 for (; optind < argc; optind++)
415                         e |= process(magic, argv[optind], wid);
416         }
417
418 out:
419         if (magic)
420                 magic_close(magic);
421         return e;
422 }
423
424 private void
425 applyparam(magic_t magic)
426 {
427         size_t i;
428
429         for (i = 0; i < __arraycount(pm); i++) {
430                 if (!pm[i].set)
431                         continue;
432                 if (magic_setparam(magic, pm[i].tag, &pm[i].value) == -1)
433                         file_err(EXIT_FAILURE, "Can't set %s", pm[i].name);
434         }
435 }
436
437 private void
438 setparam(const char *p)
439 {
440         size_t i;
441         char *s;
442
443         if ((s = strchr(p, '=')) == NULL)
444                 goto badparm;
445
446         for (i = 0; i < __arraycount(pm); i++) {
447                 if (strncmp(p, pm[i].name, s - p) != 0)
448                         continue;
449                 pm[i].value = atoi(s + 1);
450                 pm[i].set = 1;
451                 return;
452         }
453 badparm:
454         file_errx(EXIT_FAILURE, "Unknown param %s", p);
455 }
456
457 private struct magic_set *
458 /*ARGSUSED*/
459 load(const char *magicfile, int flags)
460 {
461         struct magic_set *magic = magic_open(flags);
462         const char *e;
463
464         if (magic == NULL) {
465                 file_warn("Can't create magic");
466                 return NULL;
467         }
468         if (magic_load(magic, magicfile) == -1) {
469                 file_warn("%s", magic_error(magic));
470                 magic_close(magic);
471                 return NULL;
472         }
473         if ((e = magic_error(magic)) != NULL)
474                 file_warn("%s", e);
475         return magic;
476 }
477
478 /*
479  * unwrap -- read a file of filenames, do each one.
480  */
481 private int
482 unwrap(struct magic_set *ms, const char *fn)
483 {
484         FILE *f;
485         ssize_t len;
486         char *line = NULL;
487         size_t llen = 0;
488         int wid = 0, cwid;
489         int e = 0;
490
491         if (strcmp("-", fn) == 0) {
492                 f = stdin;
493                 wid = 1;
494         } else {
495                 if ((f = fopen(fn, "r")) == NULL) {
496                         file_warn("Cannot open `%s'", fn);
497                         return 1;
498                 }
499
500                 while ((len = getline(&line, &llen, f)) > 0) {
501                         if (line[len - 1] == '\n')
502                                 line[len - 1] = '\0';
503                         cwid = file_mbswidth(line);
504                         if (cwid > wid)
505                                 wid = cwid;
506                 }
507
508                 rewind(f);
509         }
510
511         while ((len = getline(&line, &llen, f)) > 0) {
512                 if (line[len - 1] == '\n')
513                         line[len - 1] = '\0';
514                 e |= process(ms, line, wid);
515                 if(nobuffer)
516                         (void)fflush(stdout);
517         }
518
519         free(line);
520         (void)fclose(f);
521         return e;
522 }
523
524 /*
525  * Called for each input file on the command line (or in a list of files)
526  */
527 private int
528 process(struct magic_set *ms, const char *inname, int wid)
529 {
530         const char *type, c = nulsep > 1 ? '\0' : '\n';
531         int std_in = strcmp(inname, "-") == 0;
532
533         if (wid > 0 && !bflag) {
534                 (void)printf("%s", std_in ? "/dev/stdin" : inname);
535                 if (nulsep)
536                         (void)putc('\0', stdout);
537                 if (nulsep < 2) {
538                         (void)printf("%s", separator);
539                         (void)printf("%*s ", CAST(int, nopad ? 0
540                             : (wid - file_mbswidth(inname))), "");
541                 }
542         }
543
544         type = magic_file(ms, std_in ? NULL : inname);
545
546         if (type == NULL) {
547                 (void)printf("ERROR: %s%c", magic_error(ms), c);
548                 return 1;
549         } else {
550                 (void)printf("%s%c", type, c);
551                 return 0;
552         }
553 }
554
555 protected size_t
556 file_mbswidth(const char *s)
557 {
558 #if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH)
559         size_t bytesconsumed, old_n, n, width = 0;
560         mbstate_t state;
561         wchar_t nextchar;
562         (void)memset(&state, 0, sizeof(mbstate_t));
563         old_n = n = strlen(s);
564
565         while (n > 0) {
566                 bytesconsumed = mbrtowc(&nextchar, s, n, &state);
567                 if (bytesconsumed == CAST(size_t, -1) ||
568                     bytesconsumed == CAST(size_t, -2)) {
569                         /* Something went wrong, return something reasonable */
570                         return old_n;
571                 }
572                 if (s[0] == '\n') {
573                         /*
574                          * do what strlen() would do, so that caller
575                          * is always right
576                          */
577                         width++;
578                 } else {
579                         int w = wcwidth(nextchar);
580                         if (w > 0)
581                                 width += w;
582                 }
583
584                 s += bytesconsumed, n -= bytesconsumed;
585         }
586         return width;
587 #else
588         return strlen(s);
589 #endif
590 }
591
592 private void
593 usage(void)
594 {
595         const char *pn = file_getprogname();
596         (void)fprintf(stderr, USAGE, pn, pn, pn);
597         exit(EXIT_FAILURE);
598 }
599
600 private void
601 defprint(int def)
602 {
603         if (!def)
604                 return;
605         if (((def & 1) && posixly) || ((def & 2) && !posixly))
606                 fprintf(stdout, " (default)");
607         fputc('\n', stdout);
608 }
609
610 private void
611 docprint(const char *opts, int def)
612 {
613         size_t i;
614         int comma;
615         char *sp, *p;
616
617         p = strstr(opts, "%o");
618         if (p == NULL) {
619                 fprintf(stdout, "%s", opts);
620                 defprint(def);
621                 return;
622         }
623
624         for (sp = p - 1; sp > opts && *sp == ' '; sp--)
625                 continue;
626
627         fprintf(stdout, "%.*s", CAST(int, p - opts), opts);
628
629         comma = 0;
630         for (i = 0; i < __arraycount(nv); i++) {
631                 fprintf(stdout, "%s%s", comma++ ? ", " : "", nv[i].name);
632                 if (i && i % 5 == 0 && i != __arraycount(nv) - 1) {
633                         fprintf(stdout, ",\n%*s", CAST(int, p - sp - 1), "");
634                         comma = 0;
635                 }
636         }
637
638         fprintf(stdout, "%s", opts + (p - opts) + 2);
639 }
640
641 private void
642 help(void)
643 {
644         (void)fputs(
645 "Usage: file [OPTION...] [FILE...]\n"
646 "Determine type of FILEs.\n"
647 "\n", stdout);
648 #define OPT(shortname, longname, opt, def, doc)      \
649         fprintf(stdout, "  -%c, --" longname, shortname), \
650         docprint(doc, def);
651 #define OPT_LONGONLY(longname, opt, def, doc, id)        \
652         fprintf(stdout, "      --" longname),   \
653         docprint(doc, def);
654 #include "file_opts.h"
655 #undef OPT
656 #undef OPT_LONGONLY
657         fprintf(stdout, "\nReport bugs to https://bugs.astron.com/\n");
658         exit(EXIT_SUCCESS);
659 }
660
661 private const char *file_progname;
662
663 protected void
664 file_setprogname(const char *progname)
665 {
666         file_progname = progname;
667 }
668
669 protected const char *
670 file_getprogname(void)
671 {
672         return file_progname;
673 }
674
675 protected void
676 file_err(int e, const char *fmt, ...)
677 {
678         va_list ap;
679         int se = errno;
680
681         va_start(ap, fmt);
682         fprintf(stderr, "%s: ", file_progname);
683         vfprintf(stderr, fmt, ap);
684         va_end(ap);
685         fprintf(stderr, " (%s)\n", strerror(se));
686         exit(e);
687 }
688
689 protected void
690 file_errx(int e, const char *fmt, ...)
691 {
692         va_list ap;
693
694         va_start(ap, fmt);
695         fprintf(stderr, "%s: ", file_progname);
696         vfprintf(stderr, fmt, ap);
697         va_end(ap);
698         fprintf(stderr, "\n");
699         exit(e);
700 }
701
702 protected void
703 file_warn(const char *fmt, ...)
704 {
705         va_list ap;
706         int se = errno;
707
708         va_start(ap, fmt);
709         fprintf(stderr, "%s: ", file_progname);
710         vfprintf(stderr, fmt, ap);
711         va_end(ap);
712         fprintf(stderr, " (%s)\n", strerror(se));
713         errno = se;
714 }
715
716 protected void
717 file_warnx(const char *fmt, ...)
718 {
719         va_list ap;
720         int se = errno;
721
722         va_start(ap, fmt);
723         fprintf(stderr, "%s: ", file_progname);
724         vfprintf(stderr, fmt, ap);
725         va_end(ap);
726         fprintf(stderr, "\n");
727         errno = se;
728 }