1 /* $OpenBSD: file.c,v 1.9 2002/11/29 00:27:03 millert 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.9 2002/11/29 00:27:03 millert 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 [-vbczL] [-f namefile] [-m magicfiles] file...\n" 59 #else 60 # define USAGE "Usage: %s [-vbcz] [-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 bflag = 0, /* Don't print filename */ 70 lflag = 0, /* follow Symlinks (BSD only) */ 71 zflag = 0; /* follow (uncompress) compressed files */ 72 73 int /* Misc globals */ 74 nmagic = 0; /* number of valid magic[]s */ 75 76 struct magic *magic; /* array of magic entries */ 77 78 char *magicfile; /* where magic be found */ 79 80 int lineno; /* line number in the magic file */ 81 82 83 static void unwrap(char *fn); 84 #if 0 85 static int byteconv4(int, int, int); 86 static short byteconv2(int, int, int); 87 #endif 88 89 /* 90 * main - parse arguments and handle options 91 */ 92 int 93 main(argc, argv) 94 int argc; 95 char *argv[]; 96 { 97 int c; 98 int check = 0, didsomefiles = 0, errflg = 0, ret = 0, app = 0; 99 extern char *__progname; 100 101 if (!(magicfile = getenv("MAGIC"))) 102 magicfile = MAGIC; 103 104 while ((c = getopt(argc, argv, "bvcdf:Lm:z")) != -1) 105 switch (c) { 106 case 'v': 107 (void) printf("%s-%d.%d\n", __progname, 108 FILE_VERSION_MAJOR, patchlevel); 109 return 1; 110 case 'b': 111 ++bflag; 112 break; 113 case 'c': 114 ++check; 115 break; 116 case 'd': 117 ++debug; 118 break; 119 case 'f': 120 if (!app) { 121 ret = apprentice(magicfile, check); 122 if (check) 123 exit(ret); 124 app = 1; 125 } 126 unwrap(optarg); 127 ++didsomefiles; 128 break; 129 #ifdef S_IFLNK 130 case 'L': 131 ++lflag; 132 break; 133 #endif 134 case 'm': 135 magicfile = optarg; 136 break; 137 case 'z': 138 zflag++; 139 break; 140 case '?': 141 default: 142 errflg++; 143 break; 144 } 145 146 if (errflg) { 147 (void) fprintf(stderr, USAGE, __progname); 148 exit(2); 149 } 150 151 if (!app) { 152 ret = apprentice(magicfile, check); 153 if (check) 154 exit(ret); 155 app = 1; 156 } 157 158 if (optind == argc) { 159 if (!didsomefiles) { 160 fprintf(stderr, USAGE, __progname); 161 exit(2); 162 } 163 } else { 164 int i, wid, nw; 165 for (wid = 0, i = optind; i < argc; i++) { 166 nw = strlen(argv[i]); 167 if (nw > wid) 168 wid = nw; 169 } 170 for (; optind < argc; optind++) 171 process(argv[optind], wid); 172 } 173 174 return 0; 175 } 176 177 178 /* 179 * unwrap -- read a file of filenames, do each one. 180 */ 181 static void 182 unwrap(fn) 183 char *fn; 184 { 185 char buf[MAXPATHLEN]; 186 FILE *f; 187 int wid = 0, cwid; 188 189 if (strcmp("-", fn) == 0) { 190 f = stdin; 191 wid = 1; 192 } else { 193 if ((f = fopen(fn, "r")) == NULL) { 194 err(1, "Cannot open `%s'", fn); 195 /*NOTREACHED*/ 196 } 197 198 while (fgets(buf, sizeof(buf), f) != NULL) { 199 cwid = strlen(buf) - 1; 200 if (cwid > wid) 201 wid = cwid; 202 } 203 204 rewind(f); 205 } 206 207 while (fgets(buf, sizeof(buf), f) != NULL) { 208 buf[strlen(buf)-1] = '\0'; 209 process(buf, wid); 210 } 211 212 (void) fclose(f); 213 } 214 215 216 #if 0 217 /* 218 * byteconv4 219 * Input: 220 * from 4 byte quantity to convert 221 * same whether to perform byte swapping 222 * big_endian whether we are a big endian host 223 */ 224 static int 225 byteconv4(from, same, big_endian) 226 int from; 227 int same; 228 int big_endian; 229 { 230 if (same) 231 return from; 232 else if (big_endian) /* lsb -> msb conversion on msb */ 233 { 234 union { 235 int i; 236 char c[4]; 237 } retval, tmpval; 238 239 tmpval.i = from; 240 retval.c[0] = tmpval.c[3]; 241 retval.c[1] = tmpval.c[2]; 242 retval.c[2] = tmpval.c[1]; 243 retval.c[3] = tmpval.c[0]; 244 245 return retval.i; 246 } 247 else 248 return ntohl(from); /* msb -> lsb conversion on lsb */ 249 } 250 251 /* 252 * byteconv2 253 * Same as byteconv4, but for shorts 254 */ 255 static short 256 byteconv2(from, same, big_endian) 257 int from; 258 int same; 259 int big_endian; 260 { 261 if (same) 262 return from; 263 else if (big_endian) /* lsb -> msb conversion on msb */ 264 { 265 union { 266 short s; 267 char c[2]; 268 } retval, tmpval; 269 270 tmpval.s = (short) from; 271 retval.c[0] = tmpval.c[1]; 272 retval.c[1] = tmpval.c[0]; 273 274 return retval.s; 275 } 276 else 277 return ntohs(from); /* msb -> lsb conversion on lsb */ 278 } 279 #endif 280 281 /* 282 * process - process input file 283 */ 284 void 285 process(inname, wid) 286 const char *inname; 287 int wid; 288 { 289 int fd = 0; 290 static const char stdname[] = "standard input"; 291 unsigned char buf[HOWMANY+1]; /* one extra for terminating '\0' */ 292 struct stat sb; 293 int nbytes = 0; /* number of bytes read from a datafile */ 294 char match = '\0'; 295 296 if (strcmp("-", inname) == 0) { 297 if (fstat(0, &sb)<0) { 298 err(1, "cannot fstat `%s'", stdname); 299 /*NOTREACHED*/ 300 } 301 inname = stdname; 302 } 303 304 if (wid > 0 && !bflag) 305 (void) printf("%s:%*s ", inname, 306 (int) (wid - strlen(inname)), ""); 307 308 if (inname != stdname) { 309 /* 310 * first try judging the file based on its filesystem status 311 */ 312 if (fsmagic(inname, &sb) != 0) { 313 putchar('\n'); 314 return; 315 } 316 317 if ((fd = open(inname, O_RDONLY)) < 0) { 318 /* We can't open it, but we were able to stat it. */ 319 if (sb.st_mode & 0002) ckfputs("writeable, ", stdout); 320 if (sb.st_mode & 0111) ckfputs("executable, ", stdout); 321 ckfprintf(stdout, "can't read `%s' (%s).\n", 322 inname, strerror(errno)); 323 return; 324 } 325 } 326 327 328 /* 329 * try looking at the first HOWMANY bytes 330 */ 331 if ((nbytes = read(fd, (char *)buf, HOWMANY)) == -1) { 332 err(1, "read failed"); 333 /*NOTREACHED*/ 334 } 335 336 if (nbytes == 0) 337 ckfputs("empty", stdout); 338 else { 339 buf[nbytes++] = '\0'; /* null-terminate it */ 340 match = tryit(buf, nbytes, zflag); 341 } 342 343 #ifdef BUILTIN_ELF 344 if (match == 's' && nbytes > 5) 345 tryelf(fd, buf, nbytes); 346 #endif 347 348 if (inname != stdname) { 349 #ifdef RESTORE_TIME 350 /* 351 * Try to restore access, modification times if read it. 352 */ 353 # ifdef USE_UTIMES 354 struct timeval utsbuf[2]; 355 utsbuf[0].tv_sec = sb.st_atime; 356 utsbuf[1].tv_sec = sb.st_mtime; 357 358 (void) utimes(inname, utsbuf); /* don't care if loses */ 359 # else 360 struct utimbuf utbuf; 361 362 utbuf.actime = sb.st_atime; 363 utbuf.modtime = sb.st_mtime; 364 (void) utime(inname, &utbuf); /* don't care if loses */ 365 # endif 366 #endif 367 (void) close(fd); 368 } 369 (void) putchar('\n'); 370 } 371 372 373 int 374 tryit(buf, nb, zflag) 375 unsigned char *buf; 376 int nb, zflag; 377 { 378 /* try compression stuff */ 379 if (zflag && zmagic(buf, nb)) 380 return 'z'; 381 382 /* try tests in /etc/magic (or surrogate magic file) */ 383 if (softmagic(buf, nb)) 384 return 's'; 385 386 /* try known keywords, check whether it is ASCII */ 387 if (ascmagic(buf, nb)) 388 return 'a'; 389 390 /* see if it's international language text */ 391 if (internatmagic(buf, nb)) 392 return 'i'; 393 394 /* abandon hope, all ye who remain here */ 395 ckfputs("data", stdout); 396 return '\0'; 397 } 398