Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/std/file.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1989-2012 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* Glenn Fowler <[email protected]> *
18
* *
19
***********************************************************************/
20
#pragma prototyped
21
/*
22
* Glenn Fowler
23
* AT&T Research
24
*
25
* file -- determine file type
26
*
27
* the sum of the hacks {s5,v10,planix} is _____ than the parts
28
*/
29
30
static const char usage[] =
31
"[-?\n@(#)$Id: file (AT&T Research) 2011-08-01 $\n]"
32
USAGE_LICENSE
33
"[+NAME?file - determine file type]"
34
"[+DESCRIPTION?\bfile\b tests and attempts to classify each \afile\a argument."
35
" Non-regular files are classified by their \bstat\b(2) types. Empty and"
36
" non-readable regular files are classified as such. Otherwise a data"
37
" block is read from \afile\a and this is used to match against the"
38
" \amagic\a file(s) (see \bMAGIC FILE\b below). Files with less than 1024"
39
" bytes of data are labelled \bsmall\b to note that the sample may"
40
" be too small for an accurate classification. Failing a content match,"
41
" the file name extension may be used to classify. As a last resort"
42
" statistical sampling is done for a small range of languages and"
43
" applications. Failed matches usually result in the less informative"
44
" \bascii text\b or \bbinary data\b.]"
45
46
"[a:all?List all magic table matches.]"
47
"[b:brief|no-filename?Suppress the output line file name prefix.]"
48
"[c:mime?List the \bmime\b(1) classification for each \afile\a. Although the"
49
" default descriptions are fairly consistent, use \b--mime\b for"
50
" precise classification matching.]"
51
"[d:default-magic?Equivalent to \b--magic=-\b.]"
52
"[f:files|file-list?\afile\a contains list of file names, one per line, that"
53
" are classified.]:[file]"
54
"[i:ignore-magic?Equivalent to \b--magic=/dev/null\b.]"
55
"[l:list?The loaded \amagic\a files are listed and then \bfile\b exits.]"
56
"[M:magic?\afile\a is loaded as a \amagic\a file. More than one \b--magic\b"
57
" option may be specified; the precedence is from left to right. The"
58
" first \b--magic\b option causes the default system \amagic\a file to"
59
" be ignored; the file \b-\b may then be specified to explicitly"
60
" load the default system \amagic\a file. To ignore all magic files"
61
" specify the file \b/dev/null\b and no others.]:[file]"
62
"[m:append-magic?\afile\a is loaded as a \amagic\a file. Equivalent to the"
63
" \b--magic\b option, except that the default system \amagic\a file is"
64
" still loaded last. If \b--magic\b is also specified then the default"
65
" system \amagic\a is only loaded if explicity specified.]:[file]"
66
"[p:pattern|match?Only files with descriptions matching the \bsh\b(1)"
67
" match \apattern\a are listed. \bfile\b exits with status 0 if any"
68
" files match, 0 otherwise.]:[pattern]"
69
"[q:quiet|silent?Do not list matching \b--pattern\b files.]"
70
"[L:logical|dereference?Follow symbolic links.]"
71
"[P|h:physical?Don't follow symbolic links.]"
72
"[w:warn?Enable magic file parse warning messages.]"
73
74
"\n"
75
"\n[ file ... ]\n"
76
"\n"
77
78
"[+MAGIC FILE?A \amagic\a file specifies file content and name match"
79
" expressions, descriptions, and \bmime\b(1) classifications. Each line"
80
" in the file consists of five \btab\b separated fields:]{"
81
" [+[op]]offset?\aoffset\a determines tha data location for the content"
82
" test. \b(@\b\aexpression\a\b)\b specifies an indirect offset,"
83
" i.e., the offset is the numeric contents of the data"
84
" location at \aexpression\a. The default indirect numeric size"
85
" is 4 bytes; a \bB\b suffix denotes 1 byte, \bH\b denotes 2"
86
" bytes, and \bQ\b denotes 8 bytes. \aoffset\a may also be one"
87
" of { \batime\b \bblocks\b \bctime\b \bfstype\b \bgid\b"
88
" \bmode\b \bmtime\b \bname\b \bnlink\b \bsize\b \buid\b } to"
89
" access \bstat\b(2) information for the current file. The"
90
" optional \aop\a specifies relationships with surrounding"
91
" \amagic\a lines:]{"
92
" [++?previous fields in block match, current optional]"
93
" [+&?previous and current fields in block match]"
94
" [+|?previous fields in block do not match, subsequent skipped]"
95
" [+{?start nesting block]"
96
" [+}?end nesting block]"
97
" [+c{?function declaration and call (1 char names)]"
98
" [+}?function return]"
99
" [+c()?function call]"
100
" }"
101
" [+type?The content data type:]{"
102
" [+byte?1 byte integer]"
103
" [+short?2 byte integer]"
104
" [+long?4 byte integer]"
105
" [+quad?8 byte integer]"
106
" [+date?4 byte time_t]"
107
" [+version?4 byte unsigned integer of the form \aYYYYMMDD\a"
108
" for \aYYYY-MM-DD\a, 0x\aYYZZ\a for \aYY.ZZ\a, or"
109
" 0x\aWWXXYYZZ\a for \aWW.XX.YY.ZZ\a]"
110
" [+edit?substitute operator for string data:"
111
" %\aold\a%\anew\a%[glu]], where \b%\b is any delimiter]"
112
" [+match?case insensitive \bsh\b(1) match pattern operator"
113
" for string data]"
114
" }"
115
" [+[mask]]operator?\amask\a is an optional \b&\b\anumber\a that is"
116
" masked (bit \band\b) with the content data before"
117
" comparison. \aoperator\a is one of { \b< <= > >= != ==\b }."
118
" Numeric values may be decimal, octal or hex.]"
119
" [+description?The description text. Care was taken to maintain"
120
" consistency between all descriptions, i.e., character case,"
121
" grammatical parts placement, and punctuation, making"
122
" description pattern matches feasible. \adescription\a may"
123
" contain one \bprintf\b(3) format specification for the"
124
" current data value at \aoffset\a.]"
125
" [+mime?The \bmime\b(1) type/subtype. This provides a standard"
126
" and consistent matching key space.]"
127
"}"
128
129
"[+FILES]{"
130
" [+lib/file/magic?Default magic file on \b$PATH\b.]"
131
"}"
132
133
"[+SEE ALSO?\bfind\b(1), \bls\b(1), \bmime\b(1), \btw\b(1)]"
134
;
135
136
#include <ast.h>
137
#include <magic.h>
138
#include <ctype.h>
139
#include <error.h>
140
141
#define MAGIC_BRIEF (MAGIC_USER<<0)
142
#define MAGIC_LIST (MAGIC_USER<<1)
143
#define MAGIC_LOAD (MAGIC_USER<<2)
144
#define MAGIC_PHYSICAL (MAGIC_USER<<3)
145
#define MAGIC_SILENT (MAGIC_USER<<4)
146
147
static int
148
type(Magic_t* mp, char* file, const char* pattern, register Magicdisc_t* disc)
149
{
150
char* s;
151
char* e;
152
Sfio_t* fp;
153
struct stat* sp;
154
struct stat st;
155
156
sp = ((disc->flags & MAGIC_PHYSICAL) ? lstat(file, &st) : stat(file, &st)) ? (struct stat*)0 : &st;
157
fp = (sp && S_ISREG(sp->st_mode)) ? sfopen(NiL, file, "r") : (Sfio_t*)0;
158
s = magictype(mp, fp, file, sp);
159
if (fp)
160
sfclose(fp);
161
e = pathcanon(file, 0, 0);
162
if (!pattern)
163
{
164
if (!(disc->flags & MAGIC_BRIEF))
165
sfprintf(sfstdout, "%s:\t%s", file, e - file > 6 ? "" : "\t");
166
sfprintf(sfstdout, "%s\n", s);
167
return 1;
168
}
169
else if (strmatch(s, pattern))
170
{
171
if (!(disc->flags & MAGIC_SILENT))
172
sfprintf(sfstdout, "%s\n", file);
173
return 1;
174
}
175
return 0;
176
}
177
178
int
179
main(int argc, register char** argv)
180
{
181
register Magic_t* mp;
182
register char* p;
183
char* pattern = 0;
184
Sfio_t* list = 0;
185
int hit;
186
Magicdisc_t disc;
187
188
NoP(argc);
189
error_info.id = "file";
190
disc.version = MAGIC_VERSION;
191
disc.flags = 0;
192
disc.errorf = errorf;
193
if (!(mp = magicopen(&disc)))
194
error(3, "out of space");
195
for (;;)
196
{
197
switch (optget(argv, usage))
198
{
199
case 'a':
200
disc.flags |= MAGIC_ALL;
201
continue;
202
case 'b':
203
disc.flags |= MAGIC_BRIEF;
204
continue;
205
case 'c':
206
disc.flags |= MAGIC_MIME;
207
continue;
208
case 'd':
209
if (magicload(mp, NiL, 0))
210
error(3, "cannot load default magic file");
211
disc.flags |= MAGIC_LOAD;
212
continue;
213
case 'f':
214
if (streq(opt_info.arg, "-") || streq(opt_info.arg, "/dev/stdin") || streq(opt_info.arg, "/dev/fd/0"))
215
list = sfstdin;
216
else if (!(list = sfopen(NiL, opt_info.arg, "r")))
217
error(3, "cannot open %s", opt_info.arg);
218
continue;
219
case 'i':
220
disc.flags |= MAGIC_LOAD;
221
continue;
222
case 'l':
223
disc.flags |= MAGIC_LIST|MAGIC_VERBOSE;
224
continue;
225
case 'L':
226
disc.flags &= ~MAGIC_PHYSICAL;
227
continue;
228
case 'm':
229
if (magicload(mp, opt_info.arg, 0))
230
error(3, "%s: cannot load magic file", opt_info.arg);
231
continue;
232
case 'M':
233
if (magicload(mp, opt_info.arg, 0))
234
error(3, "%s: cannot load magic file", opt_info.arg);
235
disc.flags |= MAGIC_LOAD;
236
continue;
237
case 'p':
238
pattern = opt_info.arg;
239
continue;
240
case 'P':
241
case 'h':
242
disc.flags |= MAGIC_PHYSICAL;
243
continue;
244
case 'q':
245
disc.flags |= MAGIC_SILENT;
246
continue;
247
case 'w':
248
disc.flags |= MAGIC_VERBOSE;
249
continue;
250
case '?':
251
error(ERROR_USAGE|4, "%s", opt_info.arg);
252
continue;
253
case ':':
254
error(2, "%s", opt_info.arg);
255
continue;
256
}
257
break;
258
}
259
if (error_info.errors)
260
error(ERROR_USAGE|4, "%s", optusage(NiL));
261
argv += opt_info.index;
262
if (!(disc.flags & MAGIC_LOAD) && magicload(mp, NiL, 0))
263
error(3, "$%s,%s: cannot load default magic file", MAGIC_FILE_ENV, MAGIC_FILE);
264
if (disc.flags & MAGIC_LIST)
265
{
266
magiclist(mp, sfstdout);
267
hit = 1;
268
}
269
else
270
{
271
hit = 0;
272
if (!list && !*argv)
273
list = sfstdin;
274
if (list)
275
while (p = sfgetr(list, '\n', 1))
276
if (*p)
277
hit |= type(mp, p, pattern, &disc);
278
while (p = *argv++)
279
if (*p)
280
hit |= type(mp, p, pattern, &disc);
281
}
282
return !hit;
283
}
284
285