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