]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/file/file.c
This commit was generated by cvs2svn to compensate for changes in r80486,
[FreeBSD/FreeBSD.git] / contrib / file / file.c
1 /*
2  * file - find type of a file or files - main program.
3  *
4  * Copyright (c) Ian F. Darwin, 1987.
5  * Written by Ian F. Darwin.
6  *
7  * This software is not subject to any license of the American Telephone
8  * and Telegraph Company or of the Regents of the University of California.
9  *
10  * Permission is granted to anyone to use this software for any purpose on
11  * any computer system, and to alter it and redistribute it freely, subject
12  * to the following restrictions:
13  *
14  * 1. The author is not responsible for the consequences of use of this
15  *    software, no matter how awful, even if they arise from flaws in it.
16  *
17  * 2. The origin of this software must not be misrepresented, either by
18  *    explicit claim or by omission.  Since few users ever read sources,
19  *    credits must appear in the documentation.
20  *
21  * 3. Altered versions must be plainly marked as such, and must not be
22  *    misrepresented as being the original software.  Since few users
23  *    ever read sources, credits must appear in the documentation.
24  *
25  * 4. This notice may not be removed or altered.
26  */
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <sys/param.h>  /* for MAXPATHLEN */
33 #include <sys/stat.h>
34 #include <fcntl.h>      /* for open() */
35 #ifdef RESTORE_TIME
36 # if (__COHERENT__ >= 0x420)
37 #  include <sys/utime.h>
38 # else
39 #  ifdef USE_UTIMES
40 #   include <sys/time.h>
41 #  else
42 #   include <utime.h>
43 #  endif
44 # endif
45 #endif
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>     /* for read() */
48 #endif
49 #ifdef HAVE_LOCALE_H
50 #include <locale.h>
51 #endif
52
53 #include <netinet/in.h>         /* for byte swapping */
54
55 #include "file.h"
56 #include "patchlevel.h"
57
58 #ifndef lint
59 FILE_RCSID("@(#)$Id: file.c,v 1.56 2001/03/11 20:29:16 christos Exp $")
60 #endif  /* lint */
61
62
63 #ifdef S_IFLNK
64 # define USAGE  "Usage: %s [-bciknvzL] [-f namefile] [-m magicfiles] file...\n"
65 #else
66 # define USAGE  "Usage: %s [-bciknvz] [-f namefile] [-m magicfiles] file...\n"
67 #endif
68
69 #ifndef MAGIC
70 # define MAGIC "/etc/magic"
71 #endif
72
73 #ifndef MAXPATHLEN
74 #define MAXPATHLEN      512
75 #endif
76
77 int                     /* Global command-line options          */
78         debug = 0,      /* debugging                            */
79         lflag = 0,      /* follow Symlinks (BSD only)           */
80         bflag = 0,      /* brief output format                  */
81         zflag = 0,      /* follow (uncompress) compressed files */
82         sflag = 0,      /* read block special files             */
83         iflag = 0,
84         nobuffer = 0,   /* Do not buffer stdout */
85         kflag = 0;      /* Keep going after the first match     */
86
87 int                     /* Misc globals                         */
88         nmagic = 0;     /* number of valid magic[]s             */
89
90 struct  magic *magic;   /* array of magic entries               */
91
92 const char *magicfile;  /* where magic be found                 */
93 const char *default_magicfile = MAGIC;
94
95 char *progname;         /* used throughout                      */
96 int lineno;             /* line number in the magic file        */
97
98
99 static void     unwrap          __P((char *fn));
100 static void     usage           __P((void));
101 #if 0
102 static int      byteconv4       __P((int, int, int));
103 static short    byteconv2       __P((int, int, int));
104 #endif
105
106 int main __P((int, char *[]));
107
108 /*
109  * main - parse arguments and handle options
110  */
111 int
112 main(argc, argv)
113         int argc;
114         char *argv[];
115 {
116         int c;
117         int action = 0, didsomefiles = 0, errflg = 0, ret = 0, app = 0;
118         char *mime;
119
120 #ifdef LC_CTYPE
121         setlocale(LC_CTYPE, ""); /* makes islower etc work for other langs */
122 #endif
123
124         if ((progname = strrchr(argv[0], '/')) != NULL)
125                 progname++;
126         else
127                 progname = argv[0];
128
129         if (!(magicfile = getenv("MAGIC")))
130                 magicfile = default_magicfile;
131
132         while ((c = getopt(argc, argv, "bcdf:ikm:nsvzCL")) != EOF)
133                 switch (c) {
134                 case 'b':
135                         ++bflag;
136                         break;
137                 case 'c':
138                         action = CHECK;
139                         break;
140                 case 'C':
141                         action = COMPILE;
142                         break;
143                 case 'd':
144                         ++debug;
145                         break;
146                 case 'f':
147                         if (!app) {
148                                 ret = apprentice(magicfile, action);
149                                 if (action)
150                                         exit(ret);
151                                 app = 1;
152                         }
153                         unwrap(optarg);
154                         ++didsomefiles;
155                         break;
156                 case 'i':
157                         iflag++;
158                         if ((mime = malloc(strlen(magicfile) + 5)) != NULL) {
159                                 (void)strcpy(mime, magicfile);
160                                 (void)strcat(mime, ".mime");
161                                 magicfile = mime;
162                         }
163                         break;
164                 case 'k':
165                         kflag = 1;
166                         break;
167                 case 'm':
168                         magicfile = optarg;
169                         break;
170                 case 'n':
171                         ++nobuffer;
172                         break;
173                 case 's':
174                         sflag++;
175                         break;
176                 case 'v':
177                         (void) fprintf(stdout, "%s-%d.%d\n", progname,
178                                        FILE_VERSION_MAJOR, patchlevel);
179                         (void) fprintf(stdout, "magic file from %s\n",
180                                        magicfile);
181                         return 1;
182                 case 'z':
183                         zflag++;
184                         break;
185 #ifdef S_IFLNK
186                 case 'L':
187                         ++lflag;
188                         break;
189 #endif
190                 case '?':
191                 default:
192                         errflg++;
193                         break;
194                 }
195
196         if (errflg) {
197                 usage();
198         }
199
200         if (!app) {
201                 ret = apprentice(magicfile, action);
202                 if (action)
203                         exit(ret);
204                 app = 1;
205         }
206
207         if (optind == argc) {
208                 if (!didsomefiles) {
209                         usage();
210                 }
211         }
212         else {
213                 int i, wid, nw;
214                 for (wid = 0, i = optind; i < argc; i++) {
215                         nw = strlen(argv[i]);
216                         if (nw > wid)
217                                 wid = nw;
218                 }
219                 for (; optind < argc; optind++)
220                         process(argv[optind], wid);
221         }
222
223         return 0;
224 }
225
226
227 /*
228  * unwrap -- read a file of filenames, do each one.
229  */
230 static void
231 unwrap(fn)
232         char *fn;
233 {
234         char buf[MAXPATHLEN];
235         FILE *f;
236         int wid = 0, cwid;
237
238         if (strcmp("-", fn) == 0) {
239                 f = stdin;
240                 wid = 1;
241         } else {
242                 if ((f = fopen(fn, "r")) == NULL) {
243                         error("Cannot open `%s' (%s).\n", fn, strerror(errno));
244                         /*NOTREACHED*/
245                 }
246
247                 while (fgets(buf, MAXPATHLEN, f) != NULL) {
248                         cwid = strlen(buf) - 1;
249                         if (cwid > wid)
250                                 wid = cwid;
251                 }
252
253                 rewind(f);
254         }
255
256         while (fgets(buf, MAXPATHLEN, f) != NULL) {
257                 buf[strlen(buf)-1] = '\0';
258                 process(buf, wid);
259                 if(nobuffer)
260                         (void) fflush(stdout);
261         }
262
263         (void) fclose(f);
264 }
265
266
267 #if 0
268 /*
269  * byteconv4
270  * Input:
271  *      from            4 byte quantity to convert
272  *      same            whether to perform byte swapping
273  *      big_endian      whether we are a big endian host
274  */
275 static int
276 byteconv4(from, same, big_endian)
277         int from;
278         int same;
279         int big_endian;
280 {
281         if (same)
282                 return from;
283         else if (big_endian) {          /* lsb -> msb conversion on msb */
284                 union {
285                         int i;
286                         char c[4];
287                 } retval, tmpval;
288
289                 tmpval.i = from;
290                 retval.c[0] = tmpval.c[3];
291                 retval.c[1] = tmpval.c[2];
292                 retval.c[2] = tmpval.c[1];
293                 retval.c[3] = tmpval.c[0];
294
295                 return retval.i;
296         }
297         else
298                 return ntohl(from);     /* msb -> lsb conversion on lsb */
299 }
300
301 /*
302  * byteconv2
303  * Same as byteconv4, but for shorts
304  */
305 static short
306 byteconv2(from, same, big_endian)
307         int from;
308         int same;
309         int big_endian;
310 {
311         if (same)
312                 return from;
313         else if (big_endian) {          /* lsb -> msb conversion on msb */
314                 union {
315                         short s;
316                         char c[2];
317                 } retval, tmpval;
318
319                 tmpval.s = (short) from;
320                 retval.c[0] = tmpval.c[1];
321                 retval.c[1] = tmpval.c[0];
322
323                 return retval.s;
324         }
325         else
326                 return ntohs(from);     /* msb -> lsb conversion on lsb */
327 }
328 #endif
329
330 /*
331  * process - process input file
332  */
333 void
334 process(inname, wid)
335         const char      *inname;
336         int wid;
337 {
338         int     fd = 0;
339         static  const char stdname[] = "standard input";
340         unsigned char   buf[HOWMANY+1]; /* one extra for terminating '\0' */
341         struct stat     sb;
342         int nbytes = 0; /* number of bytes read from a datafile */
343         char match = '\0';
344
345         if (strcmp("-", inname) == 0) {
346                 if (fstat(0, &sb)<0) {
347                         error("cannot fstat `%s' (%s).\n", stdname,
348                               strerror(errno));
349                         /*NOTREACHED*/
350                 }
351                 inname = stdname;
352         }
353
354         if (wid > 0 && !bflag)
355              (void) printf("%s:%*s ", inname, 
356                            (int) (wid - strlen(inname)), "");
357
358         if (inname != stdname) {
359                 /*
360                  * first try judging the file based on its filesystem status
361                  */
362                 if (fsmagic(inname, &sb) != 0) {
363                         putchar('\n');
364                         return;
365                 }
366
367                 if ((fd = open(inname, O_RDONLY)) < 0) {
368                         /* We can't open it, but we were able to stat it. */
369                         if (sb.st_mode & 0002) ckfputs("writeable, ", stdout);
370                         if (sb.st_mode & 0111) ckfputs("executable, ", stdout);
371                         ckfprintf(stdout, "can't read `%s' (%s).\n",
372                             inname, strerror(errno));
373                         return;
374                 }
375         }
376
377
378         /*
379          * try looking at the first HOWMANY bytes
380          */
381         if ((nbytes = read(fd, (char *)buf, HOWMANY)) == -1) {
382                 error("read failed (%s).\n", strerror(errno));
383                 /*NOTREACHED*/
384         }
385
386         if (nbytes == 0)
387                 ckfputs(iflag ? "application/x-empty" : "empty", stdout);
388         else {
389                 buf[nbytes++] = '\0';   /* null-terminate it */
390                 match = tryit(buf, nbytes, zflag);
391         }
392
393 #ifdef BUILTIN_ELF
394         if (match == 's' && nbytes > 5) {
395                 /*
396                  * We matched something in the file, so this *might*
397                  * be an ELF file, and the file is at least 5 bytes long,
398                  * so if it's an ELF file it has at least one byte
399                  * past the ELF magic number - try extracting information
400                  * from the ELF headers that can't easily be extracted
401                  * with rules in the magic file.
402                  */
403                 tryelf(fd, buf, nbytes);
404         }
405 #endif
406
407         if (inname != stdname) {
408 #ifdef RESTORE_TIME
409                 /*
410                  * Try to restore access, modification times if read it.
411                  * This is really *bad* because it will modify the status
412                  * time of the file... And of course this will affect
413                  * backup programs
414                  */
415 # ifdef USE_UTIMES
416                 struct timeval  utsbuf[2];
417                 utsbuf[0].tv_sec = sb.st_atime;
418                 utsbuf[1].tv_sec = sb.st_mtime;
419
420                 (void) utimes(inname, utsbuf); /* don't care if loses */
421 # else
422                 struct utimbuf  utbuf;
423
424                 utbuf.actime = sb.st_atime;
425                 utbuf.modtime = sb.st_mtime;
426                 (void) utime(inname, &utbuf); /* don't care if loses */
427 # endif
428 #endif
429                 (void) close(fd);
430         }
431         (void) putchar('\n');
432 }
433
434
435 int
436 tryit(buf, nb, zflag)
437         unsigned char *buf;
438         int nb, zflag;
439 {
440         /* try compression stuff */
441         if (zflag && zmagic(buf, nb))
442                 return 'z';
443
444         /* try tests in /etc/magic (or surrogate magic file) */
445         if (softmagic(buf, nb))
446                 return 's';
447
448         /* try known keywords, check whether it is ASCII */
449         if (ascmagic(buf, nb))
450                 return 'a';
451
452         /* abandon hope, all ye who remain here */
453         ckfputs("data", stdout);
454                 return '\0';
455 }
456
457 static void
458 usage()
459 {
460         (void)fprintf(stderr, USAGE, progname);
461 #ifdef QUICK
462         (void)fprintf(stderr, "Usage: %s -C [-m magic]\n", progname);
463 #endif
464         exit(1);
465 }