1 /* $OpenBSD: file.c,v 1.7 1999/02/19 00:28:37 deraadt Exp $ */ 2 3 /* 4 * file - find type of a file or files - main program. 5 * 6 * Copyright (c) Ian F. Darwin, 1987. 7 * Written by Ian F. Darwin. 8 * 9 * This software is not subject to any license of the American Telephone 10 * and Telegraph Company or of the Regents of the University of California. 11 * 12 * Permission is granted to anyone to use this software for any purpose on 13 * any computer system, and to alter it and redistribute it freely, subject 14 * to the following restrictions: 15 * 16 * 1. The author is not responsible for the consequences of use of this 17 * software, no matter how awful, even if they arise from flaws in it. 18 * 19 * 2. The origin of this software must not be misrepresented, either by 20 * explicit claim or by omission. Since few users ever read sources, 21 * credits must appear in the documentation. 22 * 23 * 3. Altered versions must be plainly marked as such, and must not be 24 * misrepresented as being the original software. Since few users 25 * ever read sources, credits must appear in the documentation. 26 * 27 * 4. This notice may not be removed or altered. 28 */ 29 #ifndef lint 30 static char *moduleid = "$OpenBSD: file.c,v 1.7 1999/02/19 00:28:37 deraadt Exp $"; 31 #endif /* lint */ 32 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <sys/types.h> 37 #include <sys/param.h> /* for MAXPATHLEN */ 38 #include <sys/stat.h> 39 #include <fcntl.h> /* for open() */ 40 #if (__COHERENT__ >= 0x420) 41 # include <sys/utime.h> 42 #else 43 # ifdef USE_UTIMES 44 # include <sys/time.h> 45 # else 46 # include <utime.h> 47 # endif 48 #endif 49 #include <unistd.h> /* for read() */ 50 #include <err.h> 51 52 #include <netinet/in.h> /* for byte swapping */ 53 54 #include "patchlevel.h" 55 #include "file.h" 56 57 #ifdef S_IFLNK 58 # define USAGE "Usage: %s [-vczL] [-f namefile] [-m magicfiles] file...\n" 59 #else 60 # define USAGE "Usage: %s [-vcz] [-f namefile] [-m magicfiles] file...\n" 61 #endif 62 63 #ifndef MAGIC 64 # define MAGIC "/etc/magic" 65 #endif 66 67 int /* Global command-line options */ 68 debug = 0, /* debugging */ 69 lflag = 0, /* follow Symlinks (BSD only) */ 70 zflag = 0; /* follow (uncompress) compressed files */ 71 72 int /* Misc globals */ 73 nmagic = 0; /* number of valid magic[]s */ 74 75 struct magic *magic; /* array of magic entries */ 76 77 char *magicfile; /* where magic be found */ 78 79 int lineno; /* line number in the magic file */ 80 81 82 static void unwrap __P((char *fn)); 83 #if 0 84 static int byteconv4 __P((int, int, int)); 85 static short byteconv2 __P((int, int, int)); 86 #endif 87 88 /* 89 * main - parse arguments and handle options 90 */ 91 int 92 main(argc, argv) 93 int argc; 94 char *argv[]; 95 { 96 int c; 97 int check = 0, didsomefiles = 0, errflg = 0, ret = 0, app = 0; 98 extern char *__progname; 99 100 if (!(magicfile = getenv("MAGIC"))) 101 magicfile = MAGIC; 102 103 while ((c = getopt(argc, argv, "vcdf:Lm:z")) != -1) 104 switch (c) { 105 case 'v': 106 (void) printf("%s-%d.%d\n", __progname, 107 FILE_VERSION_MAJOR, patchlevel); 108 return 1; 109 case 'c': 110 ++check; 111 break; 112 case 'd': 113 ++debug; 114 break; 115 case 'f': 116 if (!app) { 117 ret = apprentice(magicfile, check); 118 if (check) 119 exit(ret); 120 app = 1; 121 } 122 unwrap(optarg); 123 ++didsomefiles; 124 break; 125 #ifdef S_IFLNK 126 case 'L': 127 ++lflag; 128 break; 129 #endif 130 case 'm': 131 magicfile = optarg; 132 break; 133 case 'z': 134 zflag++; 135 break; 136 case '?': 137 default: 138 errflg++; 139 break; 140 } 141 142 if (errflg) { 143 (void) fprintf(stderr, USAGE, __progname); 144 exit(2); 145 } 146 147 if (!app) { 148 ret = apprentice(magicfile, check); 149 if (check) 150 exit(ret); 151 app = 1; 152 } 153 154 if (optind == argc) { 155 if (!didsomefiles) { 156 fprintf(stderr, USAGE, __progname); 157 exit(2); 158 } 159 } else { 160 int i, wid, nw; 161 for (wid = 0, i = optind; i < argc; i++) { 162 nw = strlen(argv[i]); 163 if (nw > wid) 164 wid = nw; 165 } 166 for (; optind < argc; optind++) 167 process(argv[optind], wid); 168 } 169 170 return 0; 171 } 172 173 174 /* 175 * unwrap -- read a file of filenames, do each one. 176 */ 177 static void 178 unwrap(fn) 179 char *fn; 180 { 181 char buf[MAXPATHLEN]; 182 FILE *f; 183 int wid = 0, cwid; 184 185 if (strcmp("-", fn) == 0) { 186 f = stdin; 187 wid = 1; 188 } else { 189 if ((f = fopen(fn, "r")) == NULL) { 190 err(1, "Cannot open `%s'", fn); 191 /*NOTREACHED*/ 192 } 193 194 while (fgets(buf, sizeof(buf), f) != NULL) { 195 cwid = strlen(buf) - 1; 196 if (cwid > wid) 197 wid = cwid; 198 } 199 200 rewind(f); 201 } 202 203 while (fgets(buf, sizeof(buf), f) != NULL) { 204 buf[strlen(buf)-1] = '\0'; 205 process(buf, wid); 206 } 207 208 (void) fclose(f); 209 } 210 211 212 #if 0 213 /* 214 * byteconv4 215 * Input: 216 * from 4 byte quantity to convert 217 * same whether to perform byte swapping 218 * big_endian whether we are a big endian host 219 */ 220 static int 221 byteconv4(from, same, big_endian) 222 int from; 223 int same; 224 int big_endian; 225 { 226 if (same) 227 return from; 228 else if (big_endian) /* lsb -> msb conversion on msb */ 229 { 230 union { 231 int i; 232 char c[4]; 233 } retval, tmpval; 234 235 tmpval.i = from; 236 retval.c[0] = tmpval.c[3]; 237 retval.c[1] = tmpval.c[2]; 238 retval.c[2] = tmpval.c[1]; 239 retval.c[3] = tmpval.c[0]; 240 241 return retval.i; 242 } 243 else 244 return ntohl(from); /* msb -> lsb conversion on lsb */ 245 } 246 247 /* 248 * byteconv2 249 * Same as byteconv4, but for shorts 250 */ 251 static short 252 byteconv2(from, same, big_endian) 253 int from; 254 int same; 255 int big_endian; 256 { 257 if (same) 258 return from; 259 else if (big_endian) /* lsb -> msb conversion on msb */ 260 { 261 union { 262 short s; 263 char c[2]; 264 } retval, tmpval; 265 266 tmpval.s = (short) from; 267 retval.c[0] = tmpval.c[1]; 268 retval.c[1] = tmpval.c[0]; 269 270 return retval.s; 271 } 272 else 273 return ntohs(from); /* msb -> lsb conversion on lsb */ 274 } 275 #endif 276 277 /* 278 * process - process input file 279 */ 280 void 281 process(inname, wid) 282 const char *inname; 283 int wid; 284 { 285 int fd = 0; 286 static const char stdname[] = "standard input"; 287 unsigned char buf[HOWMANY+1]; /* one extra for terminating '\0' */ 288 struct stat sb; 289 int nbytes = 0; /* number of bytes read from a datafile */ 290 char match = '\0'; 291 292 if (strcmp("-", inname) == 0) { 293 if (fstat(0, &sb)<0) { 294 err(1, "cannot fstat `%s'", stdname); 295 /*NOTREACHED*/ 296 } 297 inname = stdname; 298 } 299 300 if (wid > 0) 301 (void) printf("%s:%*s ", inname, 302 (int) (wid - strlen(inname)), ""); 303 304 if (inname != stdname) { 305 /* 306 * first try judging the file based on its filesystem status 307 */ 308 if (fsmagic(inname, &sb) != 0) { 309 putchar('\n'); 310 return; 311 } 312 313 if ((fd = open(inname, O_RDONLY)) < 0) { 314 /* We can't open it, but we were able to stat it. */ 315 if (sb.st_mode & 0002) ckfputs("writeable, ", stdout); 316 if (sb.st_mode & 0111) ckfputs("executable, ", stdout); 317 ckfprintf(stdout, "can't read `%s' (%s).\n", 318 inname, strerror(errno)); 319 return; 320 } 321 } 322 323 324 /* 325 * try looking at the first HOWMANY bytes 326 */ 327 if ((nbytes = read(fd, (char *)buf, HOWMANY)) == -1) { 328 err(1, "read failed"); 329 /*NOTREACHED*/ 330 } 331 332 if (nbytes == 0) 333 ckfputs("empty", stdout); 334 else { 335 buf[nbytes++] = '\0'; /* null-terminate it */ 336 match = tryit(buf, nbytes, zflag); 337 } 338 339 #ifdef BUILTIN_ELF 340 if (match == 's' && nbytes > 5) 341 tryelf(fd, buf, nbytes); 342 #endif 343 344 if (inname != stdname) { 345 #ifdef RESTORE_TIME 346 /* 347 * Try to restore access, modification times if read it. 348 */ 349 # ifdef USE_UTIMES 350 struct timeval utsbuf[2]; 351 utsbuf[0].tv_sec = sb.st_atime; 352 utsbuf[1].tv_sec = sb.st_mtime; 353 354 (void) utimes(inname, utsbuf); /* don't care if loses */ 355 # else 356 struct utimbuf utbuf; 357 358 utbuf.actime = sb.st_atime; 359 utbuf.modtime = sb.st_mtime; 360 (void) utime(inname, &utbuf); /* don't care if loses */ 361 # endif 362 #endif 363 (void) close(fd); 364 } 365 (void) putchar('\n'); 366 } 367 368 369 int 370 tryit(buf, nb, zflag) 371 unsigned char *buf; 372 int nb, zflag; 373 { 374 /* try compression stuff */ 375 if (zflag && zmagic(buf, nb)) 376 return 'z'; 377 378 /* try tests in /etc/magic (or surrogate magic file) */ 379 if (softmagic(buf, nb)) 380 return 's'; 381 382 /* try known keywords, check whether it is ASCII */ 383 if (ascmagic(buf, nb)) 384 return 'a'; 385 386 /* see if it's international language text */ 387 if (internatmagic(buf, nb)) 388 return 'i'; 389 390 /* abandon hope, all ye who remain here */ 391 ckfputs("data", stdout); 392 return '\0'; 393 } 394