1 /* $OpenBSD: file.c,v 1.23 2011/04/15 16:05:34 stsp 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 75 #ifdef S_IFLNK 76 #define SYMLINKFLAG "Lh" 77 #else 78 #define SYMLINKFLAG "" 79 #endif 80 81 # define USAGE "Usage: %s [-bcik" SYMLINKFLAG "nNprsvz0] [-e test] [-f namefile] [-F separator] [-m magicfiles] file...\n" \ 82 " %s -C -m magicfiles\n" 83 84 #ifndef MAXPATHLEN 85 #define MAXPATHLEN 512 86 #endif 87 88 private int /* Global command-line options */ 89 bflag = 0, /* brief output format */ 90 nopad = 0, /* Don't pad output */ 91 nobuffer = 0, /* Do not buffer stdout */ 92 nulsep = 0; /* Append '\0' to the separator */ 93 94 private const char *magicfile = 0; /* where the magic is */ 95 private const char *default_magicfile = MAGIC; 96 private const char *separator = ":"; /* Default field separator */ 97 98 extern char *__progname; /* used throughout */ 99 100 private struct magic_set *magic; 101 102 private void unwrap(char *); 103 private void usage(void); 104 private void help(void); 105 106 int main(int, char *[]); 107 private void process(const char *, int); 108 private void load(const char *, int); 109 110 111 /* 112 * main - parse arguments and handle options 113 */ 114 int 115 main(int argc, char *argv[]) 116 { 117 int c; 118 size_t i; 119 int action = 0, didsomefiles = 0, errflg = 0; 120 int flags = 0; 121 char *home, *usermagic; 122 struct stat sb; 123 static const char hmagic[] = "/.magic"; 124 #define OPTSTRING "bcCde:f:F:hikLm:nNprsvz0" 125 int longindex; 126 static const struct option long_options[] = 127 { 128 #define OPT(shortname, longname, opt, doc) \ 129 {longname, opt, NULL, shortname}, 130 #define OPT_LONGONLY(longname, opt, doc) \ 131 {longname, opt, NULL, 0}, 132 #include "file_opts.h" 133 #undef OPT 134 #undef OPT_LONGONLY 135 {0, 0, NULL, 0} 136 }; 137 138 static const struct { 139 const char *name; 140 int value; 141 } nv[] = { 142 { "apptype", MAGIC_NO_CHECK_APPTYPE }, 143 { "ascii", MAGIC_NO_CHECK_ASCII }, 144 { "compress", MAGIC_NO_CHECK_COMPRESS }, 145 { "elf", MAGIC_NO_CHECK_ELF }, 146 { "soft", MAGIC_NO_CHECK_SOFT }, 147 { "tar", MAGIC_NO_CHECK_TAR }, 148 { "tokens", MAGIC_NO_CHECK_TOKENS }, 149 }; 150 151 /* makes islower etc work for other langs */ 152 (void)setlocale(LC_CTYPE, ""); 153 154 #ifdef __EMX__ 155 /* sh-like wildcard expansion! Shouldn't hurt at least ... */ 156 _wildcard(&argc, &argv); 157 #endif 158 159 magicfile = default_magicfile; 160 if ((usermagic = getenv("MAGIC")) != NULL) 161 magicfile = usermagic; 162 else 163 if ((home = getenv("HOME")) != NULL) { 164 size_t len = strlen(home) + sizeof(hmagic); 165 if ((usermagic = malloc(len)) != NULL) { 166 (void)strlcpy(usermagic, home, len); 167 (void)strlcat(usermagic, hmagic, len); 168 if (stat(usermagic, &sb)<0) 169 free(usermagic); 170 else 171 magicfile = usermagic; 172 } 173 } 174 175 #ifdef S_IFLNK 176 flags |= getenv("POSIXLY_CORRECT") ? MAGIC_SYMLINK : 0; 177 #endif 178 while ((c = getopt_long(argc, argv, OPTSTRING, long_options, 179 &longindex)) != -1) 180 switch (c) { 181 case 0 : 182 switch (longindex) { 183 case 0: 184 help(); 185 break; 186 case 10: 187 flags |= MAGIC_MIME_TYPE; 188 break; 189 case 11: 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 for (i = 0; i < sizeof(nv) / sizeof(nv[0]); i++) 211 if (strcmp(nv[i].name, optarg) == 0) 212 break; 213 214 if (i == sizeof(nv) / sizeof(nv[0])) 215 errflg++; 216 else 217 flags |= nv[i].value; 218 break; 219 220 case 'f': 221 if(action) 222 usage(); 223 load(magicfile, flags); 224 unwrap(optarg); 225 ++didsomefiles; 226 break; 227 case 'F': 228 separator = optarg; 229 break; 230 case 'i': 231 flags |= MAGIC_MIME; 232 break; 233 case 'k': 234 flags |= MAGIC_CONTINUE; 235 break; 236 case 'm': 237 magicfile = optarg; 238 break; 239 case 'n': 240 ++nobuffer; 241 break; 242 case 'N': 243 ++nopad; 244 break; 245 #if defined(HAVE_UTIME) || defined(HAVE_UTIMES) 246 case 'p': 247 flags |= MAGIC_PRESERVE_ATIME; 248 break; 249 #endif 250 case 'r': 251 flags |= MAGIC_RAW; 252 break; 253 case 's': 254 flags |= MAGIC_DEVICES; 255 break; 256 case 'v': 257 (void)fprintf(stderr, "%s-%d.%.2d\n", __progname, 258 FILE_VERSION_MAJOR, patchlevel); 259 (void)fprintf(stderr, "magic file from %s\n", 260 magicfile); 261 return 1; 262 case 'z': 263 flags |= MAGIC_COMPRESS; 264 break; 265 #ifdef S_IFLNK 266 case 'L': 267 flags |= MAGIC_SYMLINK; 268 break; 269 case 'h': 270 flags &= ~MAGIC_SYMLINK; 271 break; 272 #endif 273 case '?': 274 default: 275 errflg++; 276 break; 277 } 278 279 if (errflg) { 280 usage(); 281 } 282 283 switch(action) { 284 case FILE_CHECK: 285 case FILE_COMPILE: 286 magic = magic_open(flags|MAGIC_CHECK); 287 if (magic == NULL) { 288 (void)fprintf(stderr, "%s: %s\n", __progname, 289 strerror(errno)); 290 return 1; 291 } 292 c = action == FILE_CHECK ? magic_check(magic, magicfile) : 293 magic_compile(magic, magicfile); 294 if (c == -1) { 295 (void)fprintf(stderr, "%s: %s\n", __progname, 296 magic_error(magic)); 297 return -1; 298 } 299 return 0; 300 default: 301 load(magicfile, flags); 302 break; 303 } 304 305 if (optind == argc) { 306 if (!didsomefiles) { 307 usage(); 308 } 309 } 310 else { 311 size_t j, wid, nw; 312 for (wid = 0, j = (size_t)optind; j < (size_t)argc; j++) { 313 nw = file_mbswidth(argv[j]); 314 if (nw > wid) 315 wid = nw; 316 } 317 /* 318 * If bflag is only set twice, set it depending on 319 * number of files [this is undocumented, and subject to change] 320 */ 321 if (bflag == 2) { 322 bflag = optind >= argc - 1; 323 } 324 for (; optind < argc; optind++) 325 process(argv[optind], wid); 326 } 327 328 c = magic->haderr ? 1 : 0; 329 magic_close(magic); 330 return c; 331 } 332 333 334 private void 335 /*ARGSUSED*/ 336 load(const char *m, int flags) 337 { 338 if (magic || m == NULL) 339 return; 340 magic = magic_open(flags); 341 if (magic == NULL) { 342 (void)fprintf(stderr, "%s: %s\n", __progname, strerror(errno)); 343 exit(1); 344 } 345 if (magic_load(magic, magicfile) == -1) { 346 (void)fprintf(stderr, "%s: %s\n", 347 __progname, magic_error(magic)); 348 exit(1); 349 } 350 } 351 352 /* 353 * unwrap -- read a file of filenames, do each one. 354 */ 355 private void 356 unwrap(char *fn) 357 { 358 char buf[MAXPATHLEN]; 359 FILE *f; 360 int wid = 0, cwid; 361 362 if (strcmp("-", fn) == 0) { 363 f = stdin; 364 wid = 1; 365 } else { 366 if ((f = fopen(fn, "r")) == NULL) { 367 (void)fprintf(stderr, "%s: Cannot open `%s' (%s).\n", 368 __progname, fn, strerror(errno)); 369 exit(1); 370 } 371 372 while (fgets(buf, sizeof(buf), f) != NULL) { 373 buf[strcspn(buf, "\n")] = '\0'; 374 cwid = file_mbswidth(buf); 375 if (cwid > wid) 376 wid = cwid; 377 } 378 379 rewind(f); 380 } 381 382 while (fgets(buf, sizeof(buf), f) != NULL) { 383 buf[strcspn(buf, "\n")] = '\0'; 384 process(buf, wid); 385 if(nobuffer) 386 (void)fflush(stdout); 387 } 388 389 (void)fclose(f); 390 } 391 392 /* 393 * Called for each input file on the command line (or in a list of files) 394 */ 395 private void 396 process(const char *inname, int wid) 397 { 398 const char *type; 399 int std_in = strcmp(inname, "-") == 0; 400 401 if (wid > 0 && !bflag) { 402 (void)printf("%s", std_in ? "/dev/stdin" : inname); 403 if (nulsep) 404 (void)putc('\0', stdout); 405 else 406 (void)printf("%s", separator); 407 (void)printf("%*s ", 408 (int) (nopad ? 0 : (wid - file_mbswidth(inname))), ""); 409 } 410 411 type = magic_file(magic, std_in ? NULL : inname); 412 if (type == NULL) 413 (void)printf("ERROR: %s\n", magic_error(magic)); 414 else 415 (void)printf("%s\n", type); 416 } 417 418 size_t 419 file_mbswidth(const char *s) 420 { 421 #if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH) 422 size_t bytesconsumed, old_n, n, width = 0; 423 mbstate_t state; 424 wchar_t nextchar; 425 (void)memset(&state, 0, sizeof(mbstate_t)); 426 old_n = n = strlen(s); 427 int w; 428 429 while (n > 0) { 430 bytesconsumed = mbrtowc(&nextchar, s, n, &state); 431 if (bytesconsumed == (size_t)(-1) || 432 bytesconsumed == (size_t)(-2)) { 433 /* Something went wrong, return something reasonable */ 434 return old_n; 435 } 436 if (s[0] == '\n') { 437 /* 438 * do what strlen() would do, so that caller 439 * is always right 440 */ 441 width++; 442 } else { 443 w = wcwidth(nextchar); 444 if (w > 0) 445 width += w; 446 } 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