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