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