xref: /openbsd-src/usr.bin/file/file.c (revision db3296cf5c1dd9058ceecc3a29fe4aaa0bd26000)
1 /*	$OpenBSD: file.c,v 1.13 2003/07/02 21:04:09 deraadt Exp $	*/
2 
3 /*
4  * file - find type of a file or files - main program.
5  *
6  * Copyright (c) Ian F. Darwin 1986-1995.
7  * Software written by Ian F. Darwin and others;
8  * maintained 1995-present by Christos Zoulas and others.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice immediately at the beginning of the file, without modification,
15  *    this list of conditions, and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
24  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #ifndef	lint
34 static char *moduleid = "$OpenBSD: file.c,v 1.13 2003/07/02 21:04:09 deraadt Exp $";
35 #endif	/* lint */
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/types.h>
41 #include <sys/param.h>	/* for MAXPATHLEN */
42 #include <sys/stat.h>
43 #include <fcntl.h>	/* for open() */
44 #if (__COHERENT__ >= 0x420)
45 # include <sys/utime.h>
46 #else
47 # ifdef USE_UTIMES
48 #  include <sys/time.h>
49 # else
50 #  include <utime.h>
51 # endif
52 #endif
53 #include <unistd.h>	/* for read() */
54 #include <err.h>
55 
56 #include <netinet/in.h>		/* for byte swapping */
57 
58 #include "patchlevel.h"
59 #include "file.h"
60 
61 #ifdef S_IFLNK
62 # define USAGE  "Usage: %s [-vbczL] [-f namefile] [-m magicfiles] file...\n"
63 #else
64 # define USAGE  "Usage: %s [-vbcz] [-f namefile] [-m magicfiles] file...\n"
65 #endif
66 
67 #ifndef MAGIC
68 # define MAGIC "/etc/magic"
69 #endif
70 
71 int 			/* Global command-line options 		*/
72 	debug = 0, 	/* debugging 				*/
73 	bflag = 0,	/* Don't print filename			*/
74 	lflag = 0,	/* follow Symlinks (BSD only) 		*/
75 	zflag = 0;	/* follow (uncompress) compressed files */
76 
77 int			/* Misc globals				*/
78 	nmagic = 0;	/* number of valid magic[]s 		*/
79 
80 struct  magic *magic;	/* array of magic entries		*/
81 
82 char *magicfile;	/* where magic be found 		*/
83 
84 int lineno;		/* line number in the magic file	*/
85 
86 
87 static void	unwrap(char *fn);
88 #if 0
89 static int	byteconv4(int, int, int);
90 static short	byteconv2(int, int, int);
91 #endif
92 
93 /*
94  * main - parse arguments and handle options
95  */
96 int
97 main(int argc, char *argv[])
98 {
99 	int c;
100 	int check = 0, didsomefiles = 0, errflg = 0, ret = 0, app = 0;
101 	extern char *__progname;
102 
103 	if (!(magicfile = getenv("MAGIC")))
104 		magicfile = MAGIC;
105 
106 	while ((c = getopt(argc, argv, "bvcdf:Lm:z")) != -1)
107 		switch (c) {
108 		case 'v':
109 			(void) printf("%s-%d.%d\n", __progname,
110 				       FILE_VERSION_MAJOR, patchlevel);
111 			return 1;
112 		case 'b':
113 			++bflag;
114 			break;
115 		case 'c':
116 			++check;
117 			break;
118 		case 'd':
119 			++debug;
120 			break;
121 		case 'f':
122 			if (!app) {
123 				ret = apprentice(magicfile, check);
124 				if (check)
125 					exit(ret);
126 				app = 1;
127 			}
128 			unwrap(optarg);
129 			++didsomefiles;
130 			break;
131 #ifdef S_IFLNK
132 		case 'L':
133 			++lflag;
134 			break;
135 #endif
136 		case 'm':
137 			magicfile = optarg;
138 			break;
139 		case 'z':
140 			zflag++;
141 			break;
142 		case '?':
143 		default:
144 			errflg++;
145 			break;
146 		}
147 
148 	if (errflg) {
149 		(void) fprintf(stderr, USAGE, __progname);
150 		exit(2);
151 	}
152 
153 	if (!app) {
154 		ret = apprentice(magicfile, check);
155 		if (check)
156 			exit(ret);
157 		app = 1;
158 	}
159 
160 	if (optind == argc) {
161 		if (!didsomefiles) {
162 			fprintf(stderr, USAGE, __progname);
163 			exit(2);
164 		}
165 	} else {
166 		int i, wid, nw;
167 		for (wid = 0, i = optind; i < argc; i++) {
168 			nw = strlen(argv[i]);
169 			if (nw > wid)
170 				wid = nw;
171 		}
172 		for (; optind < argc; optind++)
173 			process(argv[optind], wid);
174 	}
175 
176 	return 0;
177 }
178 
179 
180 /*
181  * unwrap -- read a file of filenames, do each one.
182  */
183 static void
184 unwrap(fn)
185 char *fn;
186 {
187 	char buf[MAXPATHLEN];
188 	FILE *f;
189 	int wid = 0, cwid;
190 
191 	if (strcmp("-", fn) == 0) {
192 		f = stdin;
193 		wid = 1;
194 	} else {
195 		if ((f = fopen(fn, "r")) == NULL) {
196 			err(1, "Cannot open `%s'", fn);
197 			/*NOTREACHED*/
198 		}
199 
200 		while (fgets(buf, sizeof(buf), f) != NULL) {
201 			cwid = strlen(buf) - 1;
202 			if (cwid > wid)
203 				wid = cwid;
204 		}
205 
206 		rewind(f);
207 	}
208 
209 	while (fgets(buf, sizeof(buf), f) != NULL) {
210 		buf[strlen(buf)-1] = '\0';
211 		process(buf, wid);
212 	}
213 
214 	(void) fclose(f);
215 }
216 
217 
218 #if 0
219 /*
220  * byteconv4
221  * Input:
222  *	from		4 byte quantity to convert
223  *	same		whether to perform byte swapping
224  *	big_endian	whether we are a big endian host
225  */
226 static int
227 byteconv4(from, same, big_endian)
228     int from;
229     int same;
230     int big_endian;
231 {
232   if (same)
233     return from;
234   else if (big_endian)		/* lsb -> msb conversion on msb */
235   {
236     union {
237       int i;
238       char c[4];
239     } retval, tmpval;
240 
241     tmpval.i = from;
242     retval.c[0] = tmpval.c[3];
243     retval.c[1] = tmpval.c[2];
244     retval.c[2] = tmpval.c[1];
245     retval.c[3] = tmpval.c[0];
246 
247     return retval.i;
248   }
249   else
250     return ntohl(from);		/* msb -> lsb conversion on lsb */
251 }
252 
253 /*
254  * byteconv2
255  * Same as byteconv4, but for shorts
256  */
257 static short
258 byteconv2(from, same, big_endian)
259 	int from;
260 	int same;
261 	int big_endian;
262 {
263   if (same)
264     return from;
265   else if (big_endian)		/* lsb -> msb conversion on msb */
266   {
267     union {
268       short s;
269       char c[2];
270     } retval, tmpval;
271 
272     tmpval.s = (short) from;
273     retval.c[0] = tmpval.c[1];
274     retval.c[1] = tmpval.c[0];
275 
276     return retval.s;
277   }
278   else
279     return ntohs(from);		/* msb -> lsb conversion on lsb */
280 }
281 #endif
282 
283 /*
284  * process - process input file
285  */
286 void
287 process(inname, wid)
288 const char	*inname;
289 int wid;
290 {
291 	int	fd = 0;
292 	static  const char stdname[] = "standard input";
293 	unsigned char	buf[HOWMANY+1];	/* one extra for terminating '\0' */
294 	struct stat	sb;
295 	int nbytes = 0;	/* number of bytes read from a datafile */
296 	char match = '\0';
297 
298 	if (strcmp("-", inname) == 0) {
299 		if (fstat(0, &sb)<0) {
300 			err(1, "cannot fstat `%s'", stdname);
301 			/*NOTREACHED*/
302 		}
303 		inname = stdname;
304 	}
305 
306 	if (wid > 0 && !bflag)
307 	     (void) printf("%s:%*s ", inname,
308 			   (int) (wid - strlen(inname)), "");
309 
310 	if (inname != stdname) {
311 	    /*
312 	     * first try judging the file based on its filesystem status
313 	     */
314 	    if (fsmagic(inname, &sb) != 0) {
315 		    putchar('\n');
316 		    return;
317 	    }
318 
319 	    if ((fd = open(inname, O_RDONLY)) < 0) {
320 		    /* We can't open it, but we were able to stat it. */
321 		    if (sb.st_mode & 0002) ckfputs("writable, ", stdout);
322 		    if (sb.st_mode & 0111) ckfputs("executable, ", stdout);
323 		    ckfprintf(stdout, "can't read `%s' (%s).\n",
324 			inname, strerror(errno));
325 		    return;
326 	    }
327 	}
328 
329 
330 	/*
331 	 * try looking at the first HOWMANY bytes
332 	 */
333 	if ((nbytes = read(fd, (char *)buf, HOWMANY)) == -1) {
334 		err(1, "read failed");
335 		/*NOTREACHED*/
336 	}
337 
338 	if (nbytes == 0)
339 		ckfputs("empty", stdout);
340 	else {
341 		buf[nbytes++] = '\0';	/* null-terminate it */
342 		match = tryit(buf, nbytes, zflag);
343 	}
344 
345 #ifdef BUILTIN_ELF
346 	if (match == 's' && nbytes > 5)
347 		tryelf(fd, buf, nbytes);
348 #endif
349 
350 	if (inname != stdname) {
351 #ifdef RESTORE_TIME
352 		/*
353 		 * Try to restore access, modification times if read it.
354 		 */
355 # ifdef USE_UTIMES
356 		struct timeval  utsbuf[2];
357 		utsbuf[0].tv_sec = sb.st_atime;
358 		utsbuf[1].tv_sec = sb.st_mtime;
359 
360 		(void) utimes(inname, utsbuf); /* don't care if loses */
361 # else
362 		struct utimbuf  utbuf;
363 
364 		utbuf.actime = sb.st_atime;
365 		utbuf.modtime = sb.st_mtime;
366 		(void) utime(inname, &utbuf); /* don't care if loses */
367 # endif
368 #endif
369 		(void) close(fd);
370 	}
371 	(void) putchar('\n');
372 }
373 
374 
375 int
376 tryit(buf, nb, zflag)
377 unsigned char *buf;
378 int nb, zflag;
379 {
380 	/* try compression stuff */
381 	if (zflag && zmagic(buf, nb))
382 		return 'z';
383 
384 	/* try tests in /etc/magic (or surrogate magic file) */
385 	if (softmagic(buf, nb))
386 		return 's';
387 
388 	/* try known keywords, check whether it is ASCII */
389 	if (ascmagic(buf, nb))
390 		return 'a';
391 
392 	/* see if it's international language text */
393 	if (internatmagic(buf, nb))
394 		return 'i';
395 
396 	/* abandon hope, all ye who remain here */
397 	ckfputs("data", stdout);
398 		return '\0';
399 }
400