xref: /openbsd-src/usr.bin/file/file.c (revision 47911bd667ac77dc523b8a13ef40b012dbffa741)
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