file.c revision 68349
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#include <stdio.h> 28#include <stdlib.h> 29#include <string.h> 30#include <sys/types.h> 31#include <sys/param.h> /* for MAXPATHLEN */ 32#include <sys/stat.h> 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#include <netinet/in.h> /* for byte swapping */ 53 54#include "file.h" 55#include "patchlevel.h" 56 57#ifndef lint 58FILE_RCSID("@(#)$Id: file.c,v 1.54 2000/08/05 18:30:26 christos Exp $") 59#endif /* lint */ 60 61 62#ifdef S_IFLNK 63# define USAGE "Usage: %s [-bciknvzL] [-f namefile] [-m magicfiles] file...\n" 64#else 65# define USAGE "Usage: %s [-bciknvz] [-f namefile] [-m magicfiles] file...\n" 66#endif 67 68#ifndef MAGIC 69# define MAGIC "/etc/magic" 70#endif 71 72#ifndef MAXPATHLEN 73#define MAXPATHLEN 512 74#endif 75 76int /* Global command-line options */ 77 debug = 0, /* debugging */ 78 lflag = 0, /* follow Symlinks (BSD only) */ 79 bflag = 0, /* brief output format */ 80 zflag = 0, /* follow (uncompress) compressed files */ 81 sflag = 0, /* read block special files */ 82 iflag = 0, 83 nobuffer = 0, /* Do not buffer stdout */ 84 kflag = 0; /* Keep going after the first match */ 85 86int /* Misc globals */ 87 nmagic = 0; /* number of valid magic[]s */ 88 89struct magic *magic; /* array of magic entries */ 90 91const char *magicfile; /* where magic be found */ 92const char *default_magicfile = MAGIC; 93 94char *progname; /* used throughout */ 95int lineno; /* line number in the magic file */ 96 97 98static void unwrap __P((char *fn)); 99#if 0 100static int byteconv4 __P((int, int, int)); 101static short byteconv2 __P((int, int, int)); 102#endif 103 104int main __P((int, char *[])); 105 106/* 107 * main - parse arguments and handle options 108 */ 109int 110main(argc, argv) 111 int argc; 112 char *argv[]; 113{ 114 int c; 115 int check = 0, didsomefiles = 0, errflg = 0, ret = 0, app = 0; 116 char *mime; 117 118#ifdef LC_CTYPE 119 setlocale(LC_CTYPE, ""); /* makes islower etc work for other langs */ 120#endif 121 122 if ((progname = strrchr(argv[0], '/')) != NULL) 123 progname++; 124 else 125 progname = argv[0]; 126 127 if (!(magicfile = getenv("MAGIC"))) 128 magicfile = default_magicfile; 129 130 while ((c = getopt(argc, argv, "bcdf:ikm:nsvzL")) != EOF) 131 switch (c) { 132 case 'b': 133 ++bflag; 134 break; 135 case 'c': 136 ++check; 137 break; 138 case 'd': 139 ++debug; 140 break; 141 case 'f': 142 if (!app) { 143 ret = apprentice(magicfile, check); 144 if (check) 145 exit(ret); 146 app = 1; 147 } 148 unwrap(optarg); 149 ++didsomefiles; 150 break; 151 case 'i': 152 iflag++; 153 if ((mime = malloc(strlen(magicfile) + 5)) != NULL) { 154 (void)strcpy(mime, magicfile); 155 (void)strcat(mime, ".mime"); 156 magicfile = mime; 157 } 158 break; 159 case 'k': 160 kflag = 1; 161 break; 162 case 'm': 163 magicfile = optarg; 164 break; 165 case 'n': 166 ++nobuffer; 167 break; 168 case 's': 169 sflag++; 170 break; 171 case 'v': 172 (void) fprintf(stdout, "%s-%d.%d\n", progname, 173 FILE_VERSION_MAJOR, patchlevel); 174 (void) fprintf(stdout, "magic file from %s\n", 175 magicfile); 176 return 1; 177 case 'z': 178 zflag++; 179 break; 180#ifdef S_IFLNK 181 case 'L': 182 ++lflag; 183 break; 184#endif 185 case '?': 186 default: 187 errflg++; 188 break; 189 } 190 191 if (errflg) { 192 (void) fprintf(stderr, USAGE, progname); 193 exit(2); 194 } 195 196 if (!app) { 197 ret = apprentice(magicfile, check); 198 if (check) 199 exit(ret); 200 app = 1; 201 } 202 203 if (optind == argc) { 204 if (!didsomefiles) { 205 (void)fprintf(stderr, USAGE, progname); 206 exit(2); 207 } 208 } 209 else { 210 int i, wid, nw; 211 for (wid = 0, i = optind; i < argc; i++) { 212 nw = strlen(argv[i]); 213 if (nw > wid) 214 wid = nw; 215 } 216 for (; optind < argc; optind++) 217 process(argv[optind], wid); 218 } 219 220 return 0; 221} 222 223 224/* 225 * unwrap -- read a file of filenames, do each one. 226 */ 227static void 228unwrap(fn) 229 char *fn; 230{ 231 char buf[MAXPATHLEN]; 232 FILE *f; 233 int wid = 0, cwid; 234 235 if (strcmp("-", fn) == 0) { 236 f = stdin; 237 wid = 1; 238 } else { 239 if ((f = fopen(fn, "r")) == NULL) { 240 error("Cannot open `%s' (%s).\n", fn, strerror(errno)); 241 /*NOTREACHED*/ 242 } 243 244 while (fgets(buf, MAXPATHLEN, f) != NULL) { 245 cwid = strlen(buf) - 1; 246 if (cwid > wid) 247 wid = cwid; 248 } 249 250 rewind(f); 251 } 252 253 while (fgets(buf, MAXPATHLEN, f) != NULL) { 254 buf[strlen(buf)-1] = '\0'; 255 process(buf, wid); 256 if(nobuffer) 257 (void) fflush(stdout); 258 } 259 260 (void) fclose(f); 261} 262 263 264#if 0 265/* 266 * byteconv4 267 * Input: 268 * from 4 byte quantity to convert 269 * same whether to perform byte swapping 270 * big_endian whether we are a big endian host 271 */ 272static int 273byteconv4(from, same, big_endian) 274 int from; 275 int same; 276 int big_endian; 277{ 278 if (same) 279 return from; 280 else if (big_endian) { /* lsb -> msb conversion on msb */ 281 union { 282 int i; 283 char c[4]; 284 } retval, tmpval; 285 286 tmpval.i = from; 287 retval.c[0] = tmpval.c[3]; 288 retval.c[1] = tmpval.c[2]; 289 retval.c[2] = tmpval.c[1]; 290 retval.c[3] = tmpval.c[0]; 291 292 return retval.i; 293 } 294 else 295 return ntohl(from); /* msb -> lsb conversion on lsb */ 296} 297 298/* 299 * byteconv2 300 * Same as byteconv4, but for shorts 301 */ 302static short 303byteconv2(from, same, big_endian) 304 int from; 305 int same; 306 int big_endian; 307{ 308 if (same) 309 return from; 310 else if (big_endian) { /* lsb -> msb conversion on msb */ 311 union { 312 short s; 313 char c[2]; 314 } retval, tmpval; 315 316 tmpval.s = (short) from; 317 retval.c[0] = tmpval.c[1]; 318 retval.c[1] = tmpval.c[0]; 319 320 return retval.s; 321 } 322 else 323 return ntohs(from); /* msb -> lsb conversion on lsb */ 324} 325#endif 326 327/* 328 * process - process input file 329 */ 330void 331process(inname, wid) 332 const char *inname; 333 int wid; 334{ 335 int fd = 0; 336 static const char stdname[] = "standard input"; 337 unsigned char buf[HOWMANY+1]; /* one extra for terminating '\0' */ 338 struct stat sb; 339 int nbytes = 0; /* number of bytes read from a datafile */ 340 char match = '\0'; 341 342 if (strcmp("-", inname) == 0) { 343 if (fstat(0, &sb)<0) { 344 error("cannot fstat `%s' (%s).\n", stdname, 345 strerror(errno)); 346 /*NOTREACHED*/ 347 } 348 inname = stdname; 349 } 350 351 if (wid > 0 && !bflag) 352 (void) printf("%s:%*s ", inname, 353 (int) (wid - strlen(inname)), ""); 354 355 if (inname != stdname) { 356 /* 357 * first try judging the file based on its filesystem status 358 */ 359 if (fsmagic(inname, &sb) != 0) { 360 putchar('\n'); 361 return; 362 } 363 364 if ((fd = open(inname, O_RDONLY)) < 0) { 365 /* We can't open it, but we were able to stat it. */ 366 if (sb.st_mode & 0002) ckfputs("writeable, ", stdout); 367 if (sb.st_mode & 0111) ckfputs("executable, ", stdout); 368 ckfprintf(stdout, "can't read `%s' (%s).\n", 369 inname, strerror(errno)); 370 return; 371 } 372 } 373 374 375 /* 376 * try looking at the first HOWMANY bytes 377 */ 378 if ((nbytes = read(fd, (char *)buf, HOWMANY)) == -1) { 379 error("read failed (%s).\n", strerror(errno)); 380 /*NOTREACHED*/ 381 } 382 383 if (nbytes == 0) 384 ckfputs(iflag ? "application/x-empty" : "empty", stdout); 385 else { 386 buf[nbytes++] = '\0'; /* null-terminate it */ 387 match = tryit(buf, nbytes, zflag); 388 } 389 390#ifdef BUILTIN_ELF 391 if (match == 's' && nbytes > 5) { 392 /* 393 * We matched something in the file, so this *might* 394 * be an ELF file, and the file is at least 5 bytes long, 395 * so if it's an ELF file it has at least one byte 396 * past the ELF magic number - try extracting information 397 * from the ELF headers that can't easily be extracted 398 * with rules in the magic file. 399 */ 400 tryelf(fd, buf, nbytes); 401 } 402#endif 403 404 if (inname != stdname) { 405#ifdef RESTORE_TIME 406 /* 407 * Try to restore access, modification times if read it. 408 * This is really *bad* because it will modify the status 409 * time of the file... And of course this will affect 410 * backup programs 411 */ 412# ifdef USE_UTIMES 413 struct timeval utsbuf[2]; 414 utsbuf[0].tv_sec = sb.st_atime; 415 utsbuf[1].tv_sec = sb.st_mtime; 416 417 (void) utimes(inname, utsbuf); /* don't care if loses */ 418# else 419 struct utimbuf utbuf; 420 421 utbuf.actime = sb.st_atime; 422 utbuf.modtime = sb.st_mtime; 423 (void) utime(inname, &utbuf); /* don't care if loses */ 424# endif 425#endif 426 (void) close(fd); 427 } 428 (void) putchar('\n'); 429} 430 431 432int 433tryit(buf, nb, zflag) 434 unsigned char *buf; 435 int nb, zflag; 436{ 437 /* try compression stuff */ 438 if (zflag && zmagic(buf, nb)) 439 return 'z'; 440 441 /* try tests in /etc/magic (or surrogate magic file) */ 442 if (softmagic(buf, nb)) 443 return 's'; 444 445 /* try known keywords, check whether it is ASCII */ 446 if (ascmagic(buf, nb)) 447 return 'a'; 448 449 /* abandon hope, all ye who remain here */ 450 ckfputs("data", stdout); 451 return '\0'; 452} 453