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