1 /* $OpenBSD: file.c,v 1.20 2009/08/27 16:26:43 deraadt Exp $ */ 2 /* 3 * Copyright (c) Ian F. Darwin 1986-1995. 4 * Software written by Ian F. Darwin and others; 5 * maintained 1995-present by Christos Zoulas and others. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice immediately at the beginning of the file, without modification, 12 * this list of conditions, and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 21 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 /* 30 * file - find type of a file or files - main program. 31 */ 32 33 #include <sys/types.h> 34 #include <sys/param.h> /* for MAXPATHLEN */ 35 #include <sys/stat.h> 36 37 #include "file.h" 38 #include "magic.h" 39 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <unistd.h> 43 #include <string.h> 44 #ifdef RESTORE_TIME 45 # if (__COHERENT__ >= 0x420) 46 # include <sys/utime.h> 47 # else 48 # ifdef USE_UTIMES 49 # include <sys/time.h> 50 # else 51 # include <utime.h> 52 # endif 53 # endif 54 #endif 55 #ifdef HAVE_UNISTD_H 56 #include <unistd.h> /* for read() */ 57 #endif 58 #ifdef HAVE_LOCALE_H 59 #include <locale.h> 60 #endif 61 #ifdef HAVE_WCHAR_H 62 #include <wchar.h> 63 #endif 64 65 #include <getopt.h> 66 #ifndef HAVE_GETOPT_LONG 67 int getopt_long(int argc, char * const *argv, const char *optstring, const struct option *longopts, int *longindex); 68 #endif 69 70 #include <netinet/in.h> /* for byte swapping */ 71 72 #include "patchlevel.h" 73 74 #ifndef lint 75 FILE_RCSID("@(#)$Id: file.c,v 1.20 2009/08/27 16:26:43 deraadt Exp $") 76 #endif /* lint */ 77 78 79 #ifdef S_IFLNK 80 #define SYMLINKFLAG "Lh" 81 #else 82 #define SYMLINKFLAG "" 83 #endif 84 85 # define USAGE "Usage: %s [-bcik" SYMLINKFLAG "nNrsvz0] [-e test] [-f namefile] [-F separator] [-m magicfiles] file...\n" \ 86 " %s [-m magicfiles] -C\n" 87 88 #ifndef MAXPATHLEN 89 #define MAXPATHLEN 512 90 #endif 91 92 private int /* Global command-line options */ 93 bflag = 0, /* brief output format */ 94 nopad = 0, /* Don't pad output */ 95 nobuffer = 0, /* Do not buffer stdout */ 96 nulsep = 0; /* Append '\0' to the separator */ 97 98 private const char *magicfile = 0; /* where the magic is */ 99 private const char *default_magicfile = MAGIC; 100 private const char *separator = ":"; /* Default field separator */ 101 102 extern char *__progname; /* used throughout */ 103 104 private struct magic_set *magic; 105 106 private void unwrap(char *); 107 private void usage(void); 108 private void help(void); 109 110 int main(int, char *[]); 111 private void process(const char *, int); 112 private void load(const char *, int); 113 114 115 /* 116 * main - parse arguments and handle options 117 */ 118 int 119 main(int argc, char *argv[]) 120 { 121 int c; 122 size_t i; 123 int action = 0, didsomefiles = 0, errflg = 0; 124 int flags = 0; 125 char *home, *usermagic; 126 struct stat sb; 127 static const char hmagic[] = "/.magic"; 128 #define OPTSTRING "bcCde:f:F:hikLm:nNprsvz0" 129 int longindex; 130 static const struct option long_options[] = 131 { 132 #define OPT(shortname, longname, opt, doc) \ 133 {longname, opt, NULL, shortname}, 134 #define OPT_LONGONLY(longname, opt, doc) \ 135 {longname, opt, NULL, 0}, 136 #include "file_opts.h" 137 #undef OPT 138 #undef OPT_LONGONLY 139 {0, 0, NULL, 0} 140 }; 141 142 static const struct { 143 const char *name; 144 int value; 145 } nv[] = { 146 { "apptype", MAGIC_NO_CHECK_APPTYPE }, 147 { "ascii", MAGIC_NO_CHECK_ASCII }, 148 { "compress", MAGIC_NO_CHECK_COMPRESS }, 149 { "elf", MAGIC_NO_CHECK_ELF }, 150 { "soft", MAGIC_NO_CHECK_SOFT }, 151 { "tar", MAGIC_NO_CHECK_TAR }, 152 { "tokens", MAGIC_NO_CHECK_TOKENS }, 153 }; 154 155 /* makes islower etc work for other langs */ 156 (void)setlocale(LC_CTYPE, ""); 157 158 #ifdef __EMX__ 159 /* sh-like wildcard expansion! Shouldn't hurt at least ... */ 160 _wildcard(&argc, &argv); 161 #endif 162 163 magicfile = default_magicfile; 164 if ((usermagic = getenv("MAGIC")) != NULL) 165 magicfile = usermagic; 166 else 167 if ((home = getenv("HOME")) != NULL) { 168 size_t len = strlen(home) + sizeof(hmagic); 169 if ((usermagic = malloc(len)) != NULL) { 170 (void)strlcpy(usermagic, home, len); 171 (void)strlcat(usermagic, hmagic, len); 172 if (stat(usermagic, &sb)<0) 173 free(usermagic); 174 else 175 magicfile = usermagic; 176 } 177 } 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_MIME_TYPE; 192 break; 193 case 11: 194 flags |= MAGIC_MIME_ENCODING; 195 break; 196 } 197 break; 198 case '0': 199 nulsep = 1; 200 break; 201 case 'b': 202 bflag++; 203 break; 204 case 'c': 205 action = FILE_CHECK; 206 break; 207 case 'C': 208 action = FILE_COMPILE; 209 break; 210 case 'd': 211 flags |= MAGIC_DEBUG|MAGIC_CHECK; 212 break; 213 case 'e': 214 for (i = 0; i < sizeof(nv) / sizeof(nv[0]); i++) 215 if (strcmp(nv[i].name, optarg) == 0) 216 break; 217 218 if (i == sizeof(nv) / sizeof(nv[0])) 219 errflg++; 220 else 221 flags |= nv[i].value; 222 break; 223 224 case 'f': 225 if(action) 226 usage(); 227 load(magicfile, flags); 228 unwrap(optarg); 229 ++didsomefiles; 230 break; 231 case 'F': 232 separator = optarg; 233 break; 234 case 'i': 235 flags |= MAGIC_MIME; 236 break; 237 case 'k': 238 flags |= MAGIC_CONTINUE; 239 break; 240 case 'm': 241 magicfile = optarg; 242 break; 243 case 'n': 244 ++nobuffer; 245 break; 246 case 'N': 247 ++nopad; 248 break; 249 #if defined(HAVE_UTIME) || defined(HAVE_UTIMES) 250 case 'p': 251 flags |= MAGIC_PRESERVE_ATIME; 252 break; 253 #endif 254 case 'r': 255 flags |= MAGIC_RAW; 256 break; 257 case 's': 258 flags |= MAGIC_DEVICES; 259 break; 260 case 'v': 261 (void)fprintf(stderr, "%s-%d.%.2d\n", __progname, 262 FILE_VERSION_MAJOR, patchlevel); 263 (void)fprintf(stderr, "magic file from %s\n", 264 magicfile); 265 return 1; 266 case 'z': 267 flags |= MAGIC_COMPRESS; 268 break; 269 #ifdef S_IFLNK 270 case 'L': 271 flags |= MAGIC_SYMLINK; 272 break; 273 case 'h': 274 flags &= ~MAGIC_SYMLINK; 275 break; 276 #endif 277 case '?': 278 default: 279 errflg++; 280 break; 281 } 282 283 if (errflg) { 284 usage(); 285 } 286 287 switch(action) { 288 case FILE_CHECK: 289 case FILE_COMPILE: 290 magic = magic_open(flags|MAGIC_CHECK); 291 if (magic == NULL) { 292 (void)fprintf(stderr, "%s: %s\n", __progname, 293 strerror(errno)); 294 return 1; 295 } 296 c = action == FILE_CHECK ? magic_check(magic, magicfile) : 297 magic_compile(magic, magicfile); 298 if (c == -1) { 299 (void)fprintf(stderr, "%s: %s\n", __progname, 300 magic_error(magic)); 301 return -1; 302 } 303 return 0; 304 default: 305 load(magicfile, flags); 306 break; 307 } 308 309 if (optind == argc) { 310 if (!didsomefiles) { 311 usage(); 312 } 313 } 314 else { 315 size_t j, wid, nw; 316 for (wid = 0, j = (size_t)optind; j < (size_t)argc; j++) { 317 nw = file_mbswidth(argv[j]); 318 if (nw > wid) 319 wid = nw; 320 } 321 /* 322 * If bflag is only set twice, set it depending on 323 * number of files [this is undocumented, and subject to change] 324 */ 325 if (bflag == 2) { 326 bflag = optind >= argc - 1; 327 } 328 for (; optind < argc; optind++) 329 process(argv[optind], wid); 330 } 331 332 c = magic->haderr ? 1 : 0; 333 magic_close(magic); 334 return c; 335 } 336 337 338 private void 339 /*ARGSUSED*/ 340 load(const char *m, int flags) 341 { 342 if (magic || m == NULL) 343 return; 344 magic = magic_open(flags); 345 if (magic == NULL) { 346 (void)fprintf(stderr, "%s: %s\n", __progname, strerror(errno)); 347 exit(1); 348 } 349 if (magic_load(magic, magicfile) == -1) { 350 (void)fprintf(stderr, "%s: %s\n", 351 __progname, magic_error(magic)); 352 exit(1); 353 } 354 } 355 356 /* 357 * unwrap -- read a file of filenames, do each one. 358 */ 359 private void 360 unwrap(char *fn) 361 { 362 char buf[MAXPATHLEN]; 363 FILE *f; 364 int wid = 0, cwid; 365 366 if (strcmp("-", fn) == 0) { 367 f = stdin; 368 wid = 1; 369 } else { 370 if ((f = fopen(fn, "r")) == NULL) { 371 (void)fprintf(stderr, "%s: Cannot open `%s' (%s).\n", 372 __progname, fn, strerror(errno)); 373 exit(1); 374 } 375 376 while (fgets(buf, sizeof(buf), f) != NULL) { 377 buf[strcspn(buf, "\n")] = '\0'; 378 cwid = file_mbswidth(buf); 379 if (cwid > wid) 380 wid = cwid; 381 } 382 383 rewind(f); 384 } 385 386 while (fgets(buf, sizeof(buf), f) != NULL) { 387 buf[strcspn(buf, "\n")] = '\0'; 388 process(buf, wid); 389 if(nobuffer) 390 (void)fflush(stdout); 391 } 392 393 (void)fclose(f); 394 } 395 396 /* 397 * Called for each input file on the command line (or in a list of files) 398 */ 399 private void 400 process(const char *inname, int wid) 401 { 402 const char *type; 403 int std_in = strcmp(inname, "-") == 0; 404 405 if (wid > 0 && !bflag) { 406 (void)printf("%s", std_in ? "/dev/stdin" : inname); 407 if (nulsep) 408 (void)putc('\0', stdout); 409 else 410 (void)printf("%s", separator); 411 (void)printf("%*s ", 412 (int) (nopad ? 0 : (wid - file_mbswidth(inname))), ""); 413 } 414 415 type = magic_file(magic, std_in ? NULL : inname); 416 if (type == NULL) 417 (void)printf("ERROR: %s\n", magic_error(magic)); 418 else 419 (void)printf("%s\n", type); 420 } 421 422 size_t 423 file_mbswidth(const char *s) 424 { 425 #if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH) 426 size_t bytesconsumed, old_n, n, width = 0; 427 mbstate_t state; 428 wchar_t nextchar; 429 (void)memset(&state, 0, sizeof(mbstate_t)); 430 old_n = n = strlen(s); 431 432 while (n > 0) { 433 bytesconsumed = mbrtowc(&nextchar, s, n, &state); 434 if (bytesconsumed == (size_t)(-1) || 435 bytesconsumed == (size_t)(-2)) { 436 /* Something went wrong, return something reasonable */ 437 return old_n; 438 } 439 if (s[0] == '\n') { 440 /* 441 * do what strlen() would do, so that caller 442 * is always right 443 */ 444 width++; 445 } else 446 width += wcwidth(nextchar); 447 448 s += bytesconsumed, n -= bytesconsumed; 449 } 450 return width; 451 #else 452 return strlen(s); 453 #endif 454 } 455 456 private void 457 usage(void) 458 { 459 (void)fprintf(stderr, USAGE, __progname, __progname); 460 (void)fputs("Try `file --help' for more information.\n", stderr); 461 exit(1); 462 } 463 464 private void 465 help(void) 466 { 467 (void)fputs( 468 "Usage: file [OPTION...] [FILE...]\n" 469 "Determine type of FILEs.\n" 470 "\n", stderr); 471 #define OPT(shortname, longname, opt, doc) \ 472 fprintf(stderr, " -%c, --" longname doc, shortname); 473 #define OPT_LONGONLY(longname, opt, doc) \ 474 fprintf(stderr, " --" longname doc); 475 #include "file_opts.h" 476 #undef OPT 477 #undef OPT_LONGONLY 478 exit(0); 479 } 480