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