file.c revision 103373
1/* 2 * file - find type of a file or files - main program. 3 * 4 * Copyright (c) Ian F. Darwin, 1987. 5 * Written by Ian F. Darwin. 6 * 7 * This software is not subject to any license of the American Telephone 8 * and Telegraph Company or of the Regents of the University of California. 9 * 10 * Permission is granted to anyone to use this software for any purpose on 11 * any computer system, and to alter it and redistribute it freely, subject 12 * to the following restrictions: 13 * 14 * 1. The author is not responsible for the consequences of use of this 15 * software, no matter how awful, even if they arise from flaws in it. 16 * 17 * 2. The origin of this software must not be misrepresented, either by 18 * explicit claim or by omission. Since few users ever read sources, 19 * credits must appear in the documentation. 20 * 21 * 3. Altered versions must be plainly marked as such, and must not be 22 * misrepresented as being the original software. Since few users 23 * ever read sources, credits must appear in the documentation. 24 * 25 * 4. This notice may not be removed or altered. 26 */ 27 28#include "file.h" 29#include <stdlib.h> 30#include <unistd.h> 31#include <string.h> 32#include <sys/param.h> /* for MAXPATHLEN */ 33#include <fcntl.h> /* for open() */ 34#ifdef RESTORE_TIME 35# if (__COHERENT__ >= 0x420) 36# include <sys/utime.h> 37# else 38# ifdef USE_UTIMES 39# include <sys/time.h> 40# else 41# include <utime.h> 42# endif 43# endif 44#endif 45#ifdef HAVE_UNISTD_H 46#include <unistd.h> /* for read() */ 47#endif 48#ifdef HAVE_LOCALE_H 49#include <locale.h> 50#endif 51 52#ifdef HAVE_GETOPT_H 53#include <getopt.h> /* for long options (is this portable?)*/ 54#endif 55 56#include <netinet/in.h> /* for byte swapping */ 57 58#include "patchlevel.h" 59 60#ifndef lint 61FILE_RCSID("@(#)$Id: file.c,v 1.66 2002/07/03 19:00:41 christos Exp $") 62#endif /* lint */ 63 64 65#ifdef S_IFLNK 66# define USAGE "Usage: %s [-bciknsvzL] [-f namefile] [-m magicfiles] file...\n" 67#else 68# define USAGE "Usage: %s [-bciknsvz] [-f namefile] [-m magicfiles] file...\n" 69#endif 70 71#ifdef __EMX__ 72static char *apptypeName = NULL; 73int os2_apptype (const char *fn, char *buf, int nb); 74#endif /* __EMX__ */ 75 76#ifndef MAGIC 77# define MAGIC "/etc/magic" 78#endif 79 80#ifndef MAXPATHLEN 81#define MAXPATHLEN 512 82#endif 83 84int /* Global command-line options */ 85 debug = 0, /* debugging */ 86 lflag = 0, /* follow Symlinks (BSD only) */ 87 bflag = 0, /* brief output format */ 88 zflag = 0, /* follow (uncompress) compressed files */ 89 sflag = 0, /* read block special files */ 90 iflag = 0, 91 nobuffer = 0, /* Do not buffer stdout */ 92 kflag = 0; /* Keep going after the first match */ 93 94int /* Misc globals */ 95 nmagic = 0; /* number of valid magic[]s */ 96 97struct magic *magic; /* array of magic entries */ 98 99const char *magicfile = 0; /* where the magic is */ 100const char *default_magicfile = MAGIC; 101 102char *progname; /* used throughout */ 103int lineno; /* line number in the magic file */ 104 105 106static void unwrap(char *fn); 107static void usage(void); 108#ifdef HAVE_GETOPT_H 109static void help(void); 110#endif 111#if 0 112static int byteconv4(int, int, int); 113static short byteconv2(int, int, int); 114#endif 115 116int main(int, char *[]); 117 118/* 119 * main - parse arguments and handle options 120 */ 121int 122main(int argc, char **argv) 123{ 124 int c; 125 int action = 0, didsomefiles = 0, errflg = 0, ret = 0, app = 0; 126 char *mime, *home, *usermagic; 127 struct stat sb; 128#define OPTSTRING "bcdf:ikm:nsvzCL" 129#ifdef HAVE_GETOPT_H 130 int longindex; 131 static struct option long_options[] = 132 { 133 {"version", 0, 0, 'v'}, 134 {"help", 0, 0, 0}, 135 {"brief", 0, 0, 'b'}, 136 {"checking-printout", 0, 0, 'c'}, 137 {"debug", 0, 0, 'd'}, 138 {"files-from", 1, 0, 'f'}, 139 {"mime", 0, 0, 'i'}, 140 {"keep-going", 0, 0, 'k'}, 141#ifdef S_IFLNK 142 {"dereference", 0, 0, 'L'}, 143#endif 144 {"magic-file", 1, 0, 'm'}, 145 {"uncompress", 0, 0, 'z'}, 146 {"no-buffer", 0, 0, 'n'}, 147 {"special-files", 0, 0, 's'}, 148 {"compile", 0, 0, 'C'}, 149 {0, 0, 0, 0}, 150 }; 151#endif 152 153#ifdef LC_CTYPE 154 setlocale(LC_CTYPE, ""); /* makes islower etc work for other langs */ 155#endif 156 157#ifdef __EMX__ 158 /* sh-like wildcard expansion! Shouldn't hurt at least ... */ 159 _wildcard(&argc, &argv); 160#endif 161 162 if ((progname = strrchr(argv[0], '/')) != NULL) 163 progname++; 164 else 165 progname = argv[0]; 166 167 magicfile = default_magicfile; 168 if ((usermagic = getenv("MAGIC")) != NULL) 169 magicfile = usermagic; 170 else 171 if ((home = getenv("HOME")) != NULL) { 172 if ((usermagic = malloc(strlen(home) + 8)) != NULL) { 173 (void)strcpy(usermagic, home); 174 (void)strcat(usermagic, "/.magic"); 175 if (stat(usermagic, &sb)<0) 176 free(usermagic); 177 else 178 magicfile = usermagic; 179 } 180 } 181 182#ifndef HAVE_GETOPT_H 183 while ((c = getopt(argc, argv, OPTSTRING)) != -1) 184#else 185 while ((c = getopt_long(argc, argv, OPTSTRING, long_options, 186 &longindex)) != -1) 187#endif 188 switch (c) { 189#ifdef HAVE_GETOPT_H 190 case 0 : 191 if (longindex == 1) 192 help(); 193 break; 194#endif 195 case 'b': 196 ++bflag; 197 break; 198 case 'c': 199 action = CHECK; 200 break; 201 case 'C': 202 action = COMPILE; 203 break; 204 case 'd': 205 ++debug; 206 break; 207 case 'f': 208 if (!app) { 209 ret = apprentice(magicfile, action); 210 if (action) 211 exit(ret); 212 app = 1; 213 } 214 unwrap(optarg); 215 ++didsomefiles; 216 break; 217 case 'i': 218 iflag++; 219 if ((mime = malloc(strlen(magicfile) + 6)) != NULL) { 220 (void)strcpy(mime, magicfile); 221 (void)strcat(mime, ".mime"); 222 magicfile = mime; 223 } 224 break; 225 case 'k': 226 kflag = 1; 227 break; 228 case 'm': 229 magicfile = optarg; 230 break; 231 case 'n': 232 ++nobuffer; 233 break; 234 case 's': 235 sflag++; 236 break; 237 case 'v': 238 (void) fprintf(stdout, "%s-%d.%d\n", progname, 239 FILE_VERSION_MAJOR, patchlevel); 240 (void) fprintf(stdout, "magic file from %s\n", 241 magicfile); 242 return 1; 243 case 'z': 244 zflag++; 245 break; 246#ifdef S_IFLNK 247 case 'L': 248 ++lflag; 249 break; 250#endif 251 case '?': 252 default: 253 errflg++; 254 break; 255 } 256 257 if (errflg) { 258 usage(); 259 } 260 261 if (!app) { 262 ret = apprentice(magicfile, action); 263 if (action) 264 exit(ret); 265 app = 1; 266 } 267 268 if (optind == argc) { 269 if (!didsomefiles) { 270 usage(); 271 } 272 } 273 else { 274 int i, wid, nw; 275 for (wid = 0, i = optind; i < argc; i++) { 276 nw = strlen(argv[i]); 277 if (nw > wid) 278 wid = nw; 279 } 280 for (; optind < argc; optind++) 281 process(argv[optind], wid); 282 } 283 284 return 0; 285} 286 287 288/* 289 * unwrap -- read a file of filenames, do each one. 290 */ 291static void 292unwrap(char *fn) 293{ 294 char buf[MAXPATHLEN]; 295 FILE *f; 296 int wid = 0, cwid; 297 298 if (strcmp("-", fn) == 0) { 299 f = stdin; 300 wid = 1; 301 } else { 302 if ((f = fopen(fn, "r")) == NULL) { 303 error("Cannot open `%s' (%s).\n", fn, strerror(errno)); 304 /*NOTREACHED*/ 305 } 306 307 while (fgets(buf, MAXPATHLEN, f) != NULL) { 308 cwid = strlen(buf) - 1; 309 if (cwid > wid) 310 wid = cwid; 311 } 312 313 rewind(f); 314 } 315 316 while (fgets(buf, MAXPATHLEN, f) != NULL) { 317 buf[strlen(buf)-1] = '\0'; 318 process(buf, wid); 319 if(nobuffer) 320 (void) fflush(stdout); 321 } 322 323 (void) fclose(f); 324} 325 326 327#if 0 328/* 329 * byteconv4 330 * Input: 331 * from 4 byte quantity to convert 332 * same whether to perform byte swapping 333 * big_endian whether we are a big endian host 334 */ 335static int 336byteconv4(int from, int same, int big_endian) 337{ 338 if (same) 339 return from; 340 else if (big_endian) { /* lsb -> msb conversion on msb */ 341 union { 342 int i; 343 char c[4]; 344 } retval, tmpval; 345 346 tmpval.i = from; 347 retval.c[0] = tmpval.c[3]; 348 retval.c[1] = tmpval.c[2]; 349 retval.c[2] = tmpval.c[1]; 350 retval.c[3] = tmpval.c[0]; 351 352 return retval.i; 353 } 354 else 355 return ntohl(from); /* msb -> lsb conversion on lsb */ 356} 357 358/* 359 * byteconv2 360 * Same as byteconv4, but for shorts 361 */ 362static short 363byteconv2(int from, int same, int big_endian) 364{ 365 if (same) 366 return from; 367 else if (big_endian) { /* lsb -> msb conversion on msb */ 368 union { 369 short s; 370 char c[2]; 371 } retval, tmpval; 372 373 tmpval.s = (short) from; 374 retval.c[0] = tmpval.c[1]; 375 retval.c[1] = tmpval.c[0]; 376 377 return retval.s; 378 } 379 else 380 return ntohs(from); /* msb -> lsb conversion on lsb */ 381} 382#endif 383 384/* 385 * process - process input file 386 */ 387void 388process(const char *inname, int wid) 389{ 390 int fd = 0; 391 static const char stdname[] = "standard input"; 392 unsigned char buf[HOWMANY+1]; /* one extra for terminating '\0' */ 393 struct stat sb; 394 int nbytes = 0; /* number of bytes read from a datafile */ 395 char match = '\0'; 396 397 if (strcmp("-", inname) == 0) { 398 if (fstat(0, &sb)<0) { 399 error("cannot fstat `%s' (%s).\n", stdname, 400 strerror(errno)); 401 /*NOTREACHED*/ 402 } 403 inname = stdname; 404 } 405 406 if (wid > 0 && !bflag) 407 (void) printf("%s:%*s ", inname, 408 (int) (wid - strlen(inname)), ""); 409 410 if (inname != stdname) { 411 /* 412 * first try judging the file based on its filesystem status 413 */ 414 if (fsmagic(inname, &sb) != 0) { 415 putchar('\n'); 416 return; 417 } 418 419 if ((fd = open(inname, O_RDONLY)) < 0) { 420 /* We can't open it, but we were able to stat it. */ 421 if (sb.st_mode & 0002) ckfputs("writeable, ", stdout); 422 if (sb.st_mode & 0111) ckfputs("executable, ", stdout); 423 ckfprintf(stdout, "can't read `%s' (%s).\n", 424 inname, strerror(errno)); 425 return; 426 } 427 } 428 429 430 /* 431 * try looking at the first HOWMANY bytes 432 */ 433 if ((nbytes = read(fd, (char *)buf, HOWMANY)) == -1) { 434 error("read failed (%s).\n", strerror(errno)); 435 /*NOTREACHED*/ 436 } 437 438 if (nbytes == 0) 439 ckfputs(iflag ? "application/x-empty" : "empty", stdout); 440 else { 441 buf[nbytes++] = '\0'; /* null-terminate it */ 442 match = tryit(inname, buf, nbytes, zflag); 443 } 444 445#ifdef BUILTIN_ELF 446 if (match == 's' && nbytes > 5) { 447 /* 448 * We matched something in the file, so this *might* 449 * be an ELF file, and the file is at least 5 bytes long, 450 * so if it's an ELF file it has at least one byte 451 * past the ELF magic number - try extracting information 452 * from the ELF headers that can't easily be extracted 453 * with rules in the magic file. 454 */ 455 tryelf(fd, buf, nbytes); 456 } 457#endif 458 459 if (inname != stdname) { 460#ifdef RESTORE_TIME 461 /* 462 * Try to restore access, modification times if read it. 463 * This is really *bad* because it will modify the status 464 * time of the file... And of course this will affect 465 * backup programs 466 */ 467# ifdef USE_UTIMES 468 struct timeval utsbuf[2]; 469 utsbuf[0].tv_sec = sb.st_atime; 470 utsbuf[1].tv_sec = sb.st_mtime; 471 472 (void) utimes(inname, utsbuf); /* don't care if loses */ 473# else 474 struct utimbuf utbuf; 475 476 utbuf.actime = sb.st_atime; 477 utbuf.modtime = sb.st_mtime; 478 (void) utime(inname, &utbuf); /* don't care if loses */ 479# endif 480#endif 481 (void) close(fd); 482 } 483 (void) putchar('\n'); 484} 485 486 487int 488tryit(const char *fn, unsigned char *buf, int nb, int zfl) 489{ 490 491 /* 492 * The main work is done here! 493 * We have the file name and/or the data buffer to be identified. 494 */ 495 496#ifdef __EMX__ 497 /* 498 * Ok, here's the right place to add a call to some os-specific 499 * routine, e.g. 500 */ 501 if (os2_apptype(fn, buf, nb) == 1) 502 return 'o'; 503#endif 504 /* try compression stuff */ 505 if (zfl && zmagic(fn, buf, nb)) 506 return 'z'; 507 508 /* try tests in /etc/magic (or surrogate magic file) */ 509 if (softmagic(buf, nb)) 510 return 's'; 511 512 /* try known keywords, check whether it is ASCII */ 513 if (ascmagic(buf, nb)) 514 return 'a'; 515 516 /* abandon hope, all ye who remain here */ 517 ckfputs(iflag ? "application/octet-stream" : "data", stdout); 518 return '\0'; 519} 520 521static void 522usage(void) 523{ 524 (void)fprintf(stderr, USAGE, progname); 525 (void)fprintf(stderr, "Usage: %s -C [-m magic]\n", progname); 526#ifdef HAVE_GETOPT_H 527 (void)fputs("Try `file --help' for more information.\n", stderr); 528#endif 529 exit(1); 530} 531 532#ifdef HAVE_GETOPT_H 533static void 534help(void) 535{ 536 puts( 537"Usage: file [OPTION]... [FILE]...\n" 538"Determine file type of FILEs.\n" 539"\n" 540" -m, --magic-file LIST use LIST as a colon-separated list of magic\n" 541" number files\n" 542" -z, --uncompress try to look inside compressed files\n" 543" -b, --brief do not prepend filenames to output lines\n" 544" -c, --checking-printout print the parsed form of the magic file, use in\n" 545" conjunction with -m to debug a new magic file\n" 546" before installing it\n" 547" -f, --files-from FILE read the filenames to be examined from FILE\n" 548" -i, --mime output mime type strings\n" 549" -k, --keep-going don't stop at the first match\n" 550" -L, --dereference causes symlinks to be followed\n" 551" -n, --no-buffer do not buffer output\n" 552" -s, --special-files treat special (block/char devices) files as\n" 553" ordinary ones\n" 554" --help display this help and exit\n" 555" --version output version information and exit\n" 556); 557 exit(0); 558} 559#endif 560