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