1 /* $OpenBSD: file.c,v 1.16 2004/12/04 19:55:12 jaredy 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 "file.h" 34 #include "magic.h" 35 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <unistd.h> 39 #include <string.h> 40 #include <sys/types.h> 41 #include <sys/param.h> /* for MAXPATHLEN */ 42 #include <sys/stat.h> 43 #include <fcntl.h> /* for open() */ 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 #ifdef HAVE_GETOPT_H 66 #include <getopt.h> /* for long options (is this portable?)*/ 67 #else 68 #undef HAVE_GETOPT_LONG 69 #endif 70 71 #include <netinet/in.h> /* for byte swapping */ 72 73 #include "patchlevel.h" 74 75 #ifndef lint 76 FILE_RCSID("@(#)$Id: file.c,v 1.16 2004/12/04 19:55:12 jaredy Exp $") 77 #endif /* lint */ 78 79 80 #ifdef S_IFLNK 81 #define SYMLINKFLAG "L" 82 #else 83 #define SYMLINKFLAG "" 84 #endif 85 86 #define USAGE "Usage: %s [-bck" SYMLINKFLAG "Nnrsvz] [-F separator] [-f namefile] [-m magicfiles] file ...\n" \ 87 " %s [-m magicfiles] -C\n" 88 89 #ifndef MAXPATHLEN 90 #define MAXPATHLEN 512 91 #endif 92 93 private int /* Global command-line options */ 94 bflag = 0, /* brief output format */ 95 nopad = 0, /* Don't pad output */ 96 nobuffer = 0; /* Do not buffer stdout */ 97 98 private const char *magicfile = 0; /* where the magic is */ 99 private const char *default_magicfile = MAGIC; 100 private char *separator = ":"; /* Default field separator */ 101 102 private char *progname; /* used throughout */ 103 104 private struct magic_set *magic; 105 106 private void unwrap(char *); 107 private void usage(void); 108 #ifdef HAVE_GETOPT_LONG 109 private void help(void); 110 #endif 111 #if 0 112 private int byteconv4(int, int, int); 113 private short byteconv2(int, int, int); 114 #endif 115 116 int main(int, char *[]); 117 private void process(const char *, int); 118 private void load(const char *, int); 119 120 121 /* 122 * main - parse arguments and handle options 123 */ 124 int 125 main(int argc, char *argv[]) 126 { 127 int c; 128 int action = 0, didsomefiles = 0, errflg = 0; 129 int flags = 0; 130 char *home, *usermagic; 131 struct stat sb; 132 #define OPTSTRING "bcCdf:F:ikLm:nNprsvz" 133 #ifdef HAVE_GETOPT_LONG 134 int longindex; 135 private struct option long_options[] = 136 { 137 {"version", 0, 0, 'v'}, 138 {"help", 0, 0, 0}, 139 {"brief", 0, 0, 'b'}, 140 {"checking-printout", 0, 0, 'c'}, 141 {"debug", 0, 0, 'd'}, 142 {"files-from", 1, 0, 'f'}, 143 {"separator", 1, 0, 'F'}, 144 {"mime", 0, 0, 'i'}, 145 {"keep-going", 0, 0, 'k'}, 146 #ifdef S_IFLNK 147 {"dereference", 0, 0, 'L'}, 148 #endif 149 {"magic-file", 1, 0, 'm'}, 150 #if defined(HAVE_UTIME) || defined(HAVE_UTIMES) 151 {"preserve-date", 0, 0, 'p'}, 152 #endif 153 {"uncompress", 0, 0, 'z'}, 154 {"raw", 0, 0, 'r'}, 155 {"no-buffer", 0, 0, 'n'}, 156 {"no-pad", 0, 0, 'N'}, 157 {"special-files", 0, 0, 's'}, 158 {"compile", 0, 0, 'C'}, 159 {0, 0, 0, 0}, 160 }; 161 #endif 162 163 #ifdef LC_CTYPE 164 setlocale(LC_CTYPE, ""); /* makes islower etc work for other langs */ 165 #endif 166 167 #ifdef __EMX__ 168 /* sh-like wildcard expansion! Shouldn't hurt at least ... */ 169 _wildcard(&argc, &argv); 170 #endif 171 172 if ((progname = strrchr(argv[0], '/')) != NULL) 173 progname++; 174 else 175 progname = argv[0]; 176 177 magicfile = default_magicfile; 178 if ((usermagic = getenv("MAGIC")) != NULL) 179 magicfile = usermagic; 180 else 181 if ((home = getenv("HOME")) != NULL) { 182 size_t len = strlen(home) + 8; 183 if ((usermagic = malloc(len)) != NULL) { 184 (void)strlcpy(usermagic, home, len); 185 (void)strlcat(usermagic, "/.magic", len); 186 if (stat(usermagic, &sb)<0) 187 free(usermagic); 188 else 189 magicfile = usermagic; 190 } 191 } 192 193 #ifndef HAVE_GETOPT_LONG 194 while ((c = getopt(argc, argv, OPTSTRING)) != -1) 195 #else 196 while ((c = getopt_long(argc, argv, OPTSTRING, long_options, 197 &longindex)) != -1) 198 #endif 199 switch (c) { 200 #ifdef HAVE_GETOPT_LONG 201 case 0 : 202 if (longindex == 1) 203 help(); 204 break; 205 #endif 206 case 'b': 207 ++bflag; 208 break; 209 case 'c': 210 action = FILE_CHECK; 211 break; 212 case 'C': 213 action = FILE_COMPILE; 214 break; 215 case 'd': 216 flags |= MAGIC_DEBUG|MAGIC_CHECK; 217 break; 218 case 'f': 219 if(action) 220 usage(); 221 load(magicfile, flags); 222 unwrap(optarg); 223 ++didsomefiles; 224 break; 225 case 'F': 226 separator = optarg; 227 break; 228 case 'i': 229 flags |= MAGIC_MIME; 230 break; 231 case 'k': 232 flags |= MAGIC_CONTINUE; 233 break; 234 case 'm': 235 magicfile = optarg; 236 break; 237 case 'n': 238 ++nobuffer; 239 break; 240 case 'N': 241 ++nopad; 242 break; 243 #if defined(HAVE_UTIME) || defined(HAVE_UTIMES) 244 case 'p': 245 flags |= MAGIC_PRESERVE_ATIME; 246 break; 247 #endif 248 case 'r': 249 flags |= MAGIC_RAW; 250 break; 251 case 's': 252 flags |= MAGIC_DEVICES; 253 break; 254 case 'v': 255 (void) fprintf(stdout, "%s-%d.%.2d\n", progname, 256 FILE_VERSION_MAJOR, patchlevel); 257 (void) fprintf(stdout, "magic file from %s\n", 258 magicfile); 259 return 1; 260 case 'z': 261 flags |= MAGIC_COMPRESS; 262 break; 263 #ifdef S_IFLNK 264 case 'L': 265 flags |= MAGIC_SYMLINK; 266 break; 267 #endif 268 case '?': 269 default: 270 errflg++; 271 break; 272 } 273 274 if (errflg) { 275 usage(); 276 } 277 278 switch(action) { 279 case FILE_CHECK: 280 case FILE_COMPILE: 281 magic = magic_open(flags|MAGIC_CHECK); 282 if (magic == NULL) { 283 (void)fprintf(stderr, "%s: %s\n", progname, 284 strerror(errno)); 285 return 1; 286 } 287 c = action == FILE_CHECK ? magic_check(magic, magicfile) : 288 magic_compile(magic, magicfile); 289 if (c == -1) { 290 (void)fprintf(stderr, "%s: %s\n", progname, 291 magic_error(magic)); 292 return -1; 293 } 294 return 0; 295 default: 296 load(magicfile, flags); 297 break; 298 } 299 300 if (optind == argc) { 301 if (!didsomefiles) { 302 usage(); 303 } 304 } 305 else { 306 int i, wid, nw; 307 for (wid = 0, i = optind; i < argc; i++) { 308 nw = file_mbswidth(argv[i]); 309 if (nw > wid) 310 wid = nw; 311 } 312 for (; optind < argc; optind++) 313 process(argv[optind], wid); 314 } 315 316 return 0; 317 } 318 319 320 private void 321 load(const char *m, int flags) 322 { 323 if (magic) 324 return; 325 magic = magic_open(flags); 326 if (magic == NULL) { 327 (void)fprintf(stderr, "%s: %s\n", progname, strerror(errno)); 328 exit(1); 329 } 330 if (magic_load(magic, magicfile) == -1) { 331 (void)fprintf(stderr, "%s: %s\n", 332 progname, magic_error(magic)); 333 exit(1); 334 } 335 } 336 337 /* 338 * unwrap -- read a file of filenames, do each one. 339 */ 340 private void 341 unwrap(char *fn) 342 { 343 char buf[MAXPATHLEN]; 344 FILE *f; 345 int wid = 0, cwid; 346 347 if (strcmp("-", fn) == 0) { 348 f = stdin; 349 wid = 1; 350 } else { 351 if ((f = fopen(fn, "r")) == NULL) { 352 (void)fprintf(stderr, "%s: Cannot open `%s' (%s).\n", 353 progname, fn, strerror(errno)); 354 exit(1); 355 } 356 357 while (fgets(buf, sizeof(buf), f) != NULL) { 358 cwid = file_mbswidth(buf) - 1; 359 if (cwid > wid) 360 wid = cwid; 361 } 362 363 rewind(f); 364 } 365 366 while (fgets(buf, sizeof(buf), f) != NULL) { 367 buf[file_mbswidth(buf)-1] = '\0'; 368 process(buf, wid); 369 if(nobuffer) 370 (void) fflush(stdout); 371 } 372 373 (void) fclose(f); 374 } 375 376 private void 377 process(const char *inname, int wid) 378 { 379 const char *type; 380 int std_in = strcmp(inname, "-") == 0; 381 382 if (wid > 0 && !bflag) 383 (void) printf("%s%s%*s ", std_in ? "/dev/stdin" : inname, 384 separator, (int) (nopad ? 0 : (wid - file_mbswidth(inname))), ""); 385 386 type = magic_file(magic, std_in ? NULL : inname); 387 if (type == NULL) 388 printf("ERROR: %s\n", magic_error(magic)); 389 else 390 printf("%s\n", type); 391 } 392 393 394 #if 0 395 /* 396 * byteconv4 397 * Input: 398 * from 4 byte quantity to convert 399 * same whether to perform byte swapping 400 * big_endian whether we are a big endian host 401 */ 402 private int 403 byteconv4(int from, int same, int big_endian) 404 { 405 if (same) 406 return from; 407 else if (big_endian) { /* lsb -> msb conversion on msb */ 408 union { 409 int i; 410 char c[4]; 411 } retval, tmpval; 412 413 tmpval.i = from; 414 retval.c[0] = tmpval.c[3]; 415 retval.c[1] = tmpval.c[2]; 416 retval.c[2] = tmpval.c[1]; 417 retval.c[3] = tmpval.c[0]; 418 419 return retval.i; 420 } 421 else 422 return ntohl(from); /* msb -> lsb conversion on lsb */ 423 } 424 425 /* 426 * byteconv2 427 * Same as byteconv4, but for shorts 428 */ 429 private short 430 byteconv2(int from, int same, int big_endian) 431 { 432 if (same) 433 return from; 434 else if (big_endian) { /* lsb -> msb conversion on msb */ 435 union { 436 short s; 437 char c[2]; 438 } retval, tmpval; 439 440 tmpval.s = (short) from; 441 retval.c[0] = tmpval.c[1]; 442 retval.c[1] = tmpval.c[0]; 443 444 return retval.s; 445 } 446 else 447 return ntohs(from); /* msb -> lsb conversion on lsb */ 448 } 449 #endif 450 451 size_t 452 file_mbswidth(const char *s) 453 { 454 #ifdef HAVE_WCHAR_H 455 size_t bytesconsumed, old_n, n, width = 0; 456 mbstate_t state; 457 wchar_t nextchar; 458 (void)memset(&state, 0, sizeof(mbstate_t)); 459 old_n = n = strlen(s); 460 461 while (n > 0) { 462 bytesconsumed = mbrtowc(&nextchar, s, n, &state); 463 if (bytesconsumed == (size_t)(-1) || 464 bytesconsumed == (size_t)(-2)) { 465 /* Something went wrong, return something reasonable */ 466 return old_n; 467 } 468 if (s[0] == '\n') { 469 /* 470 * do what strlen() would do, so that caller 471 * is always right 472 */ 473 width++; 474 } else 475 width += wcwidth(nextchar); 476 477 s += bytesconsumed, n -= bytesconsumed; 478 } 479 return width; 480 #else 481 return strlen(s); 482 #endif 483 } 484 485 private void 486 usage(void) 487 { 488 (void)fprintf(stderr, USAGE, progname, progname); 489 #ifdef HAVE_GETOPT_LONG 490 (void)fputs("Try `file --help' for more information.\n", stderr); 491 #endif 492 exit(1); 493 } 494 495 #ifdef HAVE_GETOPT_LONG 496 private void 497 help(void) 498 { 499 puts( 500 "Usage: file [OPTION]... [FILE]...\n" 501 "Determine file type of FILEs.\n" 502 "\n" 503 " -m, --magic-file LIST use LIST as a colon-separated list of magic\n" 504 " number files\n" 505 " -z, --uncompress try to look inside compressed files\n" 506 " -b, --brief do not prepend filenames to output lines\n" 507 " -c, --checking-printout print the parsed form of the magic file, use in\n" 508 " conjunction with -m to debug a new magic file\n" 509 " before installing it\n" 510 " -f, --files-from FILE read the filenames to be examined from FILE\n" 511 " -F, --separator string use string as separator instead of `:'\n" 512 " -i, --mime output mime type strings\n" 513 " -k, --keep-going don't stop at the first match\n" 514 " -L, --dereference causes symlinks to be followed\n" 515 " -n, --no-buffer do not buffer output\n" 516 " -N, --no-pad do not pad output\n" 517 " -p, --preserve-date preserve access times on files\n" 518 " -r, --raw don't translate unprintable chars to \\ooo\n" 519 " -s, --special-files treat special (block/char devices) files as\n" 520 " ordinary ones\n" 521 " --help display this help and exit\n" 522 " --version output version information and exit\n" 523 ); 524 exit(0); 525 } 526 #endif 527