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