xref: /titanic_44/usr/src/lib/libast/common/misc/magic.c (revision 3e14f97f673e8a630f076077de35afdd43dc1587)
1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin *                                                                      *
3da2e3ebdSchin *               This software is part of the ast package               *
4*3e14f97fSRoger A. Faulkner *          Copyright (c) 1985-2010 AT&T Intellectual Property          *
5da2e3ebdSchin *                      and is licensed under the                       *
6da2e3ebdSchin *                  Common Public License, Version 1.0                  *
77c2fbfb3SApril Chin *                    by AT&T Intellectual Property                     *
8da2e3ebdSchin *                                                                      *
9da2e3ebdSchin *                A copy of the License is available at                 *
10da2e3ebdSchin *            http://www.opensource.org/licenses/cpl1.0.txt             *
11da2e3ebdSchin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12da2e3ebdSchin *                                                                      *
13da2e3ebdSchin *              Information and Software Systems Research               *
14da2e3ebdSchin *                            AT&T Research                             *
15da2e3ebdSchin *                           Florham Park NJ                            *
16da2e3ebdSchin *                                                                      *
17da2e3ebdSchin *                 Glenn Fowler <gsf@research.att.com>                  *
18da2e3ebdSchin *                  David Korn <dgk@research.att.com>                   *
19da2e3ebdSchin *                   Phong Vo <kpv@research.att.com>                    *
20da2e3ebdSchin *                                                                      *
21da2e3ebdSchin ***********************************************************************/
22da2e3ebdSchin #pragma prototyped
23da2e3ebdSchin /*
24da2e3ebdSchin  * Glenn Fowler
25da2e3ebdSchin  * AT&T Research
26da2e3ebdSchin  *
27da2e3ebdSchin  * library interface to file
28da2e3ebdSchin  *
29da2e3ebdSchin  * the sum of the hacks {s5,v10,planix} is _____ than the parts
30da2e3ebdSchin  */
31da2e3ebdSchin 
327c2fbfb3SApril Chin static const char id[] = "\n@(#)$Id: magic library (AT&T Research) 2008-09-10 $\0\n";
33da2e3ebdSchin 
34da2e3ebdSchin static const char lib[] = "libast:magic";
35da2e3ebdSchin 
36da2e3ebdSchin #include <ast.h>
37da2e3ebdSchin #include <ctype.h>
38da2e3ebdSchin #include <ccode.h>
39da2e3ebdSchin #include <dt.h>
40da2e3ebdSchin #include <modex.h>
41da2e3ebdSchin #include <error.h>
42da2e3ebdSchin #include <regex.h>
43da2e3ebdSchin #include <swap.h>
44da2e3ebdSchin 
45da2e3ebdSchin #define T(m)		(*m?ERROR_translate(NiL,NiL,lib,m):m)
46da2e3ebdSchin 
47da2e3ebdSchin #define match(s,p)	strgrpmatch(s,p,NiL,0,STR_LEFT|STR_RIGHT|STR_ICASE)
48da2e3ebdSchin 
49da2e3ebdSchin #define MAXNEST		10		/* { ... } nesting limit	*/
50da2e3ebdSchin #define MINITEM		4		/* magic buffer rounding	*/
51da2e3ebdSchin 
52da2e3ebdSchin typedef struct				/* identifier dictionary entry	*/
53da2e3ebdSchin {
54da2e3ebdSchin 	const char	name[16];	/* identifier name		*/
55da2e3ebdSchin 	int		value;		/* identifier value		*/
56da2e3ebdSchin 	Dtlink_t	link;		/* dictionary link		*/
57da2e3ebdSchin } Info_t;
58da2e3ebdSchin 
59da2e3ebdSchin typedef struct Edit			/* edit substitution		*/
60da2e3ebdSchin {
61da2e3ebdSchin 	struct Edit*	next;		/* next in list			*/
62da2e3ebdSchin 	regex_t*	from;		/* from pattern			*/
63da2e3ebdSchin } Edit_t;
64da2e3ebdSchin 
65da2e3ebdSchin struct Entry;
66da2e3ebdSchin 
67da2e3ebdSchin typedef struct				/* loop info			*/
68da2e3ebdSchin {
69da2e3ebdSchin 	struct Entry*	lab;		/* call this function		*/
70da2e3ebdSchin 	int		start;		/* start here			*/
71da2e3ebdSchin 	int		size;		/* increment by this amount	*/
72da2e3ebdSchin 	int		count;		/* dynamic loop count		*/
73da2e3ebdSchin 	int		offset;		/* dynamic offset		*/
74da2e3ebdSchin } Loop_t;
75da2e3ebdSchin 
76da2e3ebdSchin typedef struct Entry			/* magic file entry		*/
77da2e3ebdSchin {
78da2e3ebdSchin 	struct Entry*	next;		/* next in list			*/
79da2e3ebdSchin 	char*		expr;		/* offset expression		*/
80da2e3ebdSchin 	union
81da2e3ebdSchin 	{
82da2e3ebdSchin 	unsigned long	num;
83da2e3ebdSchin 	char*		str;
84da2e3ebdSchin 	struct Entry*	lab;
85da2e3ebdSchin 	regex_t*	sub;
86da2e3ebdSchin 	Loop_t*		loop;
87da2e3ebdSchin 	}		value;		/* comparison value		*/
88da2e3ebdSchin 	char*		desc;		/* file description		*/
89da2e3ebdSchin 	char*		mime;		/* file mime type		*/
90da2e3ebdSchin 	unsigned long	offset;		/* offset in bytes		*/
91da2e3ebdSchin 	unsigned long	mask;		/* mask before compare		*/
92da2e3ebdSchin 	char		cont;		/* continuation operation	*/
93da2e3ebdSchin 	char		type;		/* datum type			*/
94da2e3ebdSchin 	char		op;		/* comparison operation		*/
95da2e3ebdSchin 	char		nest;		/* { or } nesting operation	*/
96da2e3ebdSchin 	char		swap;		/* forced swap order		*/
97da2e3ebdSchin } Entry_t;
98da2e3ebdSchin 
99da2e3ebdSchin #define CC_BIT		5
100da2e3ebdSchin 
101da2e3ebdSchin #if (CC_MAPS*CC_BIT) <= (CHAR_BIT*2)
102da2e3ebdSchin typedef unsigned short Cctype_t;
103da2e3ebdSchin #else
104da2e3ebdSchin typedef unsigned long Cctype_t;
105da2e3ebdSchin #endif
106da2e3ebdSchin 
107da2e3ebdSchin #define CC_text		0x01
108da2e3ebdSchin #define CC_control	0x02
109da2e3ebdSchin #define CC_latin	0x04
110da2e3ebdSchin #define CC_binary	0x08
111da2e3ebdSchin #define CC_utf_8	0x10
112da2e3ebdSchin 
113da2e3ebdSchin #define CC_notext	CC_text		/* CC_text is flipped before checking */
114da2e3ebdSchin 
115da2e3ebdSchin #define CC_MASK		(CC_binary|CC_latin|CC_control|CC_text)
116da2e3ebdSchin 
117da2e3ebdSchin #define CCTYPE(c)	(((c)>0240)?CC_binary:((c)>=0200)?CC_latin:((c)<040&&(c)!=007&&(c)!=011&&(c)!=012&&(c)!=013&&(c)!=015)?CC_control:CC_text)
118da2e3ebdSchin 
119da2e3ebdSchin #define ID_NONE		0
120da2e3ebdSchin #define ID_ASM		1
121da2e3ebdSchin #define ID_C		2
122da2e3ebdSchin #define ID_COBOL	3
123da2e3ebdSchin #define ID_COPYBOOK	4
124da2e3ebdSchin #define ID_CPLUSPLUS	5
125da2e3ebdSchin #define ID_FORTRAN	6
126da2e3ebdSchin #define ID_HTML		7
127da2e3ebdSchin #define ID_INCL1	8
128da2e3ebdSchin #define ID_INCL2	9
129da2e3ebdSchin #define ID_INCL3	10
130da2e3ebdSchin #define ID_MAM1		11
131da2e3ebdSchin #define ID_MAM2		12
132da2e3ebdSchin #define ID_MAM3		13
133da2e3ebdSchin #define ID_NOTEXT	14
134da2e3ebdSchin #define ID_PL1		15
135da2e3ebdSchin #define ID_YACC		16
136da2e3ebdSchin 
137da2e3ebdSchin #define ID_MAX		ID_YACC
138da2e3ebdSchin 
139da2e3ebdSchin #define INFO_atime	1
140da2e3ebdSchin #define INFO_blocks	2
141da2e3ebdSchin #define INFO_ctime	3
142da2e3ebdSchin #define INFO_fstype	4
143da2e3ebdSchin #define INFO_gid	5
144da2e3ebdSchin #define INFO_mode	6
145da2e3ebdSchin #define INFO_mtime	7
146da2e3ebdSchin #define INFO_name	8
147da2e3ebdSchin #define INFO_nlink	9
148da2e3ebdSchin #define INFO_size	10
149da2e3ebdSchin #define INFO_uid	11
150da2e3ebdSchin 
151da2e3ebdSchin #define _MAGIC_PRIVATE_ \
152da2e3ebdSchin 	Magicdisc_t*	disc;			/* discipline		*/ \
153da2e3ebdSchin 	Vmalloc_t*	vm;			/* vmalloc region	*/ \
154da2e3ebdSchin 	Entry_t*	magic;			/* parsed magic table	*/ \
155da2e3ebdSchin 	Entry_t*	magiclast;		/* last entry in magic	*/ \
156da2e3ebdSchin 	char*		mime;			/* MIME type		*/ \
157da2e3ebdSchin 	unsigned char*	x2n;			/* CC_ALIEN=>CC_NATIVE	*/ \
158da2e3ebdSchin 	char		fbuf[SF_BUFSIZE + 1];	/* file data		*/ \
159da2e3ebdSchin 	char		xbuf[SF_BUFSIZE + 1];	/* indirect file data	*/ \
160da2e3ebdSchin 	char		nbuf[256];		/* !CC_NATIVE data	*/ \
161da2e3ebdSchin 	char		mbuf[64];		/* mime string		*/ \
162da2e3ebdSchin 	char		sbuf[64];		/* type suffix string	*/ \
163da2e3ebdSchin 	char		tbuf[2 * PATH_MAX];	/* type string		*/ \
164da2e3ebdSchin 	Cctype_t	cctype[UCHAR_MAX + 1];	/* char code types	*/ \
165da2e3ebdSchin 	unsigned int	count[UCHAR_MAX + 1];	/* char frequency count	*/ \
166da2e3ebdSchin 	unsigned int	multi[UCHAR_MAX + 1];	/* muti char count	*/ \
167da2e3ebdSchin 	int		keep[MAXNEST];		/* ckmagic nest stack	*/ \
168da2e3ebdSchin 	char*		cap[MAXNEST];		/* ckmagic mime stack	*/ \
169da2e3ebdSchin 	char*		msg[MAXNEST];		/* ckmagic text stack	*/ \
170da2e3ebdSchin 	Entry_t*	ret[MAXNEST];		/* ckmagic return stack	*/ \
171da2e3ebdSchin 	int		fbsz;			/* fbuf size		*/ \
172da2e3ebdSchin 	int		fbmx;			/* fbuf max size	*/ \
173da2e3ebdSchin 	int		xbsz;			/* xbuf size		*/ \
174da2e3ebdSchin 	int		swap;			/* swap() operation	*/ \
175da2e3ebdSchin 	unsigned long	flags;			/* disc+open flags	*/ \
176da2e3ebdSchin 	long		xoff;			/* xbuf offset		*/ \
177da2e3ebdSchin 	int		identifier[ID_MAX + 1];	/* Info_t identifier	*/ \
178da2e3ebdSchin 	Sfio_t*		fp;			/* fbuf fp		*/ \
179da2e3ebdSchin 	Sfio_t*		tmp;			/* tmp string		*/ \
180da2e3ebdSchin 	regdisc_t	redisc;			/* regex discipline	*/ \
181da2e3ebdSchin 	Dtdisc_t	dtdisc;			/* dict discipline	*/ \
182da2e3ebdSchin 	Dt_t*		idtab;			/* identifier dict	*/ \
183da2e3ebdSchin 	Dt_t*		infotab;		/* info keyword dict	*/
184da2e3ebdSchin 
185da2e3ebdSchin #include <magic.h>
186da2e3ebdSchin 
187da2e3ebdSchin static Info_t		dict[] =		/* keyword dictionary	*/
188da2e3ebdSchin {
189da2e3ebdSchin 	{ 	"COMMON",	ID_FORTRAN	},
190da2e3ebdSchin 	{ 	"COMPUTE",	ID_COBOL	},
191da2e3ebdSchin 	{ 	"COMP",		ID_COPYBOOK	},
192da2e3ebdSchin 	{ 	"COMPUTATIONAL",ID_COPYBOOK	},
193da2e3ebdSchin 	{ 	"DCL",		ID_PL1		},
194da2e3ebdSchin 	{ 	"DEFINED",	ID_PL1		},
195da2e3ebdSchin 	{ 	"DIMENSION",	ID_FORTRAN	},
196da2e3ebdSchin 	{ 	"DIVISION",	ID_COBOL	},
197da2e3ebdSchin 	{ 	"FILLER",	ID_COPYBOOK	},
198da2e3ebdSchin 	{ 	"FIXED",	ID_PL1		},
199da2e3ebdSchin 	{ 	"FUNCTION",	ID_FORTRAN	},
200da2e3ebdSchin 	{ 	"HTML",		ID_HTML		},
201da2e3ebdSchin 	{ 	"INTEGER",	ID_FORTRAN	},
202da2e3ebdSchin 	{ 	"MAIN",		ID_PL1		},
203da2e3ebdSchin 	{ 	"OPTIONS",	ID_PL1		},
204da2e3ebdSchin 	{ 	"PERFORM",	ID_COBOL	},
205da2e3ebdSchin 	{ 	"PIC",		ID_COPYBOOK	},
206da2e3ebdSchin 	{ 	"REAL",		ID_FORTRAN	},
207da2e3ebdSchin 	{ 	"REDEFINES",	ID_COPYBOOK	},
208da2e3ebdSchin 	{ 	"S9",		ID_COPYBOOK	},
209da2e3ebdSchin 	{ 	"SECTION",	ID_COBOL	},
210da2e3ebdSchin 	{ 	"SELECT",	ID_COBOL	},
211da2e3ebdSchin 	{ 	"SUBROUTINE",	ID_FORTRAN	},
212da2e3ebdSchin 	{ 	"TEXT",		ID_ASM		},
213da2e3ebdSchin 	{ 	"VALUE",	ID_COPYBOOK	},
214da2e3ebdSchin 	{ 	"attr",		ID_MAM3		},
215da2e3ebdSchin 	{ 	"binary",	ID_YACC		},
216da2e3ebdSchin 	{ 	"block",	ID_FORTRAN	},
217da2e3ebdSchin 	{ 	"bss",		ID_ASM		},
218da2e3ebdSchin 	{ 	"byte",		ID_ASM		},
219da2e3ebdSchin 	{ 	"char",		ID_C		},
220da2e3ebdSchin 	{ 	"class",	ID_CPLUSPLUS	},
221da2e3ebdSchin 	{ 	"clr",		ID_NOTEXT	},
222da2e3ebdSchin 	{ 	"comm",		ID_ASM		},
223da2e3ebdSchin 	{ 	"common",	ID_FORTRAN	},
224da2e3ebdSchin 	{ 	"data",		ID_ASM		},
225da2e3ebdSchin 	{ 	"dimension",	ID_FORTRAN	},
226da2e3ebdSchin 	{ 	"done",		ID_MAM2		},
227da2e3ebdSchin 	{ 	"double",	ID_C		},
228da2e3ebdSchin 	{ 	"even",		ID_ASM		},
229da2e3ebdSchin 	{ 	"exec",		ID_MAM3		},
230da2e3ebdSchin 	{ 	"extern",	ID_C		},
231da2e3ebdSchin 	{ 	"float",	ID_C		},
232da2e3ebdSchin 	{ 	"function",	ID_FORTRAN	},
233da2e3ebdSchin 	{ 	"globl",	ID_ASM		},
234da2e3ebdSchin 	{ 	"h",		ID_INCL3	},
235da2e3ebdSchin 	{ 	"html",		ID_HTML		},
236da2e3ebdSchin 	{ 	"include",	ID_INCL1	},
237da2e3ebdSchin 	{ 	"int",		ID_C		},
238da2e3ebdSchin 	{ 	"integer",	ID_FORTRAN	},
239da2e3ebdSchin 	{ 	"jmp",		ID_NOTEXT	},
240da2e3ebdSchin 	{ 	"left",		ID_YACC		},
241da2e3ebdSchin 	{ 	"libc",		ID_INCL2	},
242da2e3ebdSchin 	{ 	"long",		ID_C		},
243da2e3ebdSchin 	{ 	"make",		ID_MAM1		},
244da2e3ebdSchin 	{ 	"mov",		ID_NOTEXT	},
245da2e3ebdSchin 	{ 	"private",	ID_CPLUSPLUS	},
246da2e3ebdSchin 	{ 	"public",	ID_CPLUSPLUS	},
247da2e3ebdSchin 	{ 	"real",		ID_FORTRAN	},
248da2e3ebdSchin 	{ 	"register",	ID_C		},
249da2e3ebdSchin 	{ 	"right",	ID_YACC		},
250da2e3ebdSchin 	{ 	"sfio",		ID_INCL2	},
251da2e3ebdSchin 	{ 	"static",	ID_C		},
252da2e3ebdSchin 	{ 	"stdio",	ID_INCL2	},
253da2e3ebdSchin 	{ 	"struct",	ID_C		},
254da2e3ebdSchin 	{ 	"subroutine",	ID_FORTRAN	},
255da2e3ebdSchin 	{ 	"sys",		ID_NOTEXT	},
256da2e3ebdSchin 	{ 	"term",		ID_YACC		},
257da2e3ebdSchin 	{ 	"text",		ID_ASM		},
258da2e3ebdSchin 	{ 	"tst",		ID_NOTEXT	},
259da2e3ebdSchin 	{ 	"type",		ID_YACC		},
260da2e3ebdSchin 	{ 	"typedef",	ID_C		},
261da2e3ebdSchin 	{ 	"u",		ID_INCL2	},
262da2e3ebdSchin 	{ 	"union",	ID_YACC		},
263da2e3ebdSchin 	{ 	"void",		ID_C		},
264da2e3ebdSchin };
265da2e3ebdSchin 
266da2e3ebdSchin static Info_t		info[] =
267da2e3ebdSchin {
268da2e3ebdSchin 	{	"atime",	INFO_atime		},
269da2e3ebdSchin 	{	"blocks",	INFO_blocks		},
270da2e3ebdSchin 	{	"ctime",	INFO_ctime		},
271da2e3ebdSchin 	{	"fstype",	INFO_fstype		},
272da2e3ebdSchin 	{	"gid",		INFO_gid		},
273da2e3ebdSchin 	{	"mode",		INFO_mode		},
274da2e3ebdSchin 	{	"mtime",	INFO_mtime		},
275da2e3ebdSchin 	{	"name",		INFO_name		},
276da2e3ebdSchin 	{	"nlink",	INFO_nlink		},
277da2e3ebdSchin 	{	"size",		INFO_size		},
278da2e3ebdSchin 	{	"uid",		INFO_uid		},
279da2e3ebdSchin };
280da2e3ebdSchin 
281da2e3ebdSchin /*
282da2e3ebdSchin  * return pointer to data at offset off and size siz
283da2e3ebdSchin  */
284da2e3ebdSchin 
285da2e3ebdSchin static char*
getdata(register Magic_t * mp,register long off,register int siz)286da2e3ebdSchin getdata(register Magic_t* mp, register long off, register int siz)
287da2e3ebdSchin {
288da2e3ebdSchin 	register long	n;
289da2e3ebdSchin 
290da2e3ebdSchin 	if (off < 0)
291da2e3ebdSchin 		return 0;
292da2e3ebdSchin 	if (off + siz <= mp->fbsz)
293da2e3ebdSchin 		return mp->fbuf + off;
294da2e3ebdSchin 	if (off < mp->xoff || off + siz > mp->xoff + mp->xbsz)
295da2e3ebdSchin 	{
296da2e3ebdSchin 		if (off + siz > mp->fbmx)
297da2e3ebdSchin 			return 0;
298da2e3ebdSchin 		n = (off / (SF_BUFSIZE / 2)) * (SF_BUFSIZE / 2);
299da2e3ebdSchin 		if (sfseek(mp->fp, n, SEEK_SET) != n)
300da2e3ebdSchin 			return 0;
301da2e3ebdSchin 		if ((mp->xbsz = sfread(mp->fp, mp->xbuf, sizeof(mp->xbuf) - 1)) < 0)
302da2e3ebdSchin 		{
303da2e3ebdSchin 			mp->xoff = 0;
304da2e3ebdSchin 			mp->xbsz = 0;
305da2e3ebdSchin 			return 0;
306da2e3ebdSchin 		}
307da2e3ebdSchin 		mp->xbuf[mp->xbsz] = 0;
308da2e3ebdSchin 		mp->xoff = n;
309da2e3ebdSchin 		if (off + siz > mp->xoff + mp->xbsz)
310da2e3ebdSchin 			return 0;
311da2e3ebdSchin 	}
312da2e3ebdSchin 	return mp->xbuf + off - mp->xoff;
313da2e3ebdSchin }
314da2e3ebdSchin 
315da2e3ebdSchin /*
316da2e3ebdSchin  * @... evaluator for strexpr()
317da2e3ebdSchin  */
318da2e3ebdSchin 
319da2e3ebdSchin static long
indirect(const char * cs,char ** e,void * handle)320da2e3ebdSchin indirect(const char* cs, char** e, void* handle)
321da2e3ebdSchin {
322da2e3ebdSchin 	register char*		s = (char*)cs;
323da2e3ebdSchin 	register Magic_t*	mp = (Magic_t*)handle;
324da2e3ebdSchin 	register long		n = 0;
325da2e3ebdSchin 	register char*		p;
326da2e3ebdSchin 
327da2e3ebdSchin 	if (s)
328da2e3ebdSchin 	{
329da2e3ebdSchin 		if (*s == '@')
330da2e3ebdSchin 		{
331da2e3ebdSchin 			n = *++s == '(' ? strexpr(s, e, indirect, mp) : strtol(s, e, 0);
332da2e3ebdSchin 			switch (*(s = *e))
333da2e3ebdSchin 			{
334da2e3ebdSchin 			case 'b':
335da2e3ebdSchin 			case 'B':
336da2e3ebdSchin 				s++;
337da2e3ebdSchin 				if (p = getdata(mp, n, 1))
338da2e3ebdSchin 					n = *(unsigned char*)p;
339da2e3ebdSchin 				else
340da2e3ebdSchin 					s = (char*)cs;
341da2e3ebdSchin 				break;
342da2e3ebdSchin 			case 'h':
343da2e3ebdSchin 			case 'H':
344da2e3ebdSchin 				s++;
345da2e3ebdSchin 				if (p = getdata(mp, n, 2))
346da2e3ebdSchin 					n = swapget(mp->swap, p, 2);
347da2e3ebdSchin 				else
348da2e3ebdSchin 					s = (char*)cs;
349da2e3ebdSchin 				break;
350da2e3ebdSchin 			case 'q':
351da2e3ebdSchin 			case 'Q':
352da2e3ebdSchin 				s++;
353da2e3ebdSchin 				if (p = getdata(mp, n, 8))
354da2e3ebdSchin 					n = swapget(mp->swap, p, 8);
355da2e3ebdSchin 				else
356da2e3ebdSchin 					s = (char*)cs;
357da2e3ebdSchin 				break;
358da2e3ebdSchin 			default:
359da2e3ebdSchin 				if (isalnum(*s))
360da2e3ebdSchin 					s++;
361da2e3ebdSchin 				if (p = getdata(mp, n, 4))
362da2e3ebdSchin 					n = swapget(mp->swap, p, 4);
363da2e3ebdSchin 				else
364da2e3ebdSchin 					s = (char*)cs;
365da2e3ebdSchin 				break;
366da2e3ebdSchin 			}
367da2e3ebdSchin 		}
368da2e3ebdSchin 		*e = s;
369da2e3ebdSchin 	}
370da2e3ebdSchin 	else if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
371da2e3ebdSchin 		(*mp->disc->errorf)(mp, mp->disc, 2, "%s in indirect expression", *e);
372da2e3ebdSchin 	return n;
373da2e3ebdSchin }
374da2e3ebdSchin 
375da2e3ebdSchin /*
376da2e3ebdSchin  * emit regex error message
377da2e3ebdSchin  */
378da2e3ebdSchin 
379da2e3ebdSchin static void
regmessage(Magic_t * mp,regex_t * re,int code)380da2e3ebdSchin regmessage(Magic_t* mp, regex_t* re, int code)
381da2e3ebdSchin {
382da2e3ebdSchin 	char	buf[128];
383da2e3ebdSchin 
384da2e3ebdSchin 	if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
385da2e3ebdSchin 	{
386da2e3ebdSchin 		regerror(code, re, buf, sizeof(buf));
387da2e3ebdSchin 		(*mp->disc->errorf)(mp, mp->disc, 3, "regex: %s", buf);
388da2e3ebdSchin 	}
389da2e3ebdSchin }
390da2e3ebdSchin 
391da2e3ebdSchin /*
392da2e3ebdSchin  * decompose vcodex(3) method composition
393da2e3ebdSchin  */
394da2e3ebdSchin 
395da2e3ebdSchin static char*
vcdecomp(char * b,char * e,unsigned char * m,unsigned char * x)396da2e3ebdSchin vcdecomp(char* b, char* e, unsigned char* m, unsigned char* x)
397da2e3ebdSchin {
398da2e3ebdSchin 	unsigned char*	map;
3997c2fbfb3SApril Chin 	const char*	o;
400da2e3ebdSchin 	int		c;
401da2e3ebdSchin 	int		n;
402da2e3ebdSchin 	int		i;
4037c2fbfb3SApril Chin 	int		a;
404da2e3ebdSchin 
405da2e3ebdSchin 	map = CCMAP(CC_ASCII, CC_NATIVE);
4067c2fbfb3SApril Chin 	a = 0;
407da2e3ebdSchin 	i = 1;
408da2e3ebdSchin 	for (;;)
409da2e3ebdSchin 	{
410da2e3ebdSchin 		if (i)
411da2e3ebdSchin 			i = 0;
412da2e3ebdSchin 		else
413da2e3ebdSchin 			*b++ = '^';
4147c2fbfb3SApril Chin 		if (m < (x - 1) && !*(m + 1))
4157c2fbfb3SApril Chin 		{
4167c2fbfb3SApril Chin 			/*
4177c2fbfb3SApril Chin 			 * obsolete indices
4187c2fbfb3SApril Chin 			 */
4197c2fbfb3SApril Chin 
4207c2fbfb3SApril Chin 			if (!a)
4217c2fbfb3SApril Chin 			{
4227c2fbfb3SApril Chin 				a = 1;
4237c2fbfb3SApril Chin 				o = "old, ";
4247c2fbfb3SApril Chin 				while (b < e && (c = *o++))
4257c2fbfb3SApril Chin 					*b++ = c;
4267c2fbfb3SApril Chin 			}
4277c2fbfb3SApril Chin 			switch (*m)
4287c2fbfb3SApril Chin 			{
4297c2fbfb3SApril Chin 			case 0:		o = "delta"; break;
4307c2fbfb3SApril Chin 			case 1:		o = "huffman"; break;
4317c2fbfb3SApril Chin 			case 2:		o = "huffgroup"; break;
4327c2fbfb3SApril Chin 			case 3:		o = "arith"; break;
4337c2fbfb3SApril Chin 			case 4:		o = "bwt"; break;
4347c2fbfb3SApril Chin 			case 5:		o = "rle"; break;
4357c2fbfb3SApril Chin 			case 6:		o = "mtf"; break;
4367c2fbfb3SApril Chin 			case 7:		o = "transpose"; break;
4377c2fbfb3SApril Chin 			case 8:		o = "table"; break;
4387c2fbfb3SApril Chin 			case 9:		o = "huffpart"; break;
4397c2fbfb3SApril Chin 			case 50:	o = "map"; break;
4407c2fbfb3SApril Chin 			case 100:	o = "recfm"; break;
4417c2fbfb3SApril Chin 			case 101:	o = "ss7"; break;
4427c2fbfb3SApril Chin 			default:	o = "UNKNOWN"; break;
4437c2fbfb3SApril Chin 			}
4447c2fbfb3SApril Chin 			m += 2;
4457c2fbfb3SApril Chin 			while (b < e && (c = *o++))
4467c2fbfb3SApril Chin 				*b++ = c;
4477c2fbfb3SApril Chin 		}
4487c2fbfb3SApril Chin 		else
449da2e3ebdSchin 			while (b < e && m < x && (c = *m++))
450da2e3ebdSchin 			{
451da2e3ebdSchin 				if (map)
452da2e3ebdSchin 					c = map[c];
453da2e3ebdSchin 				*b++ = c;
454da2e3ebdSchin 			}
455da2e3ebdSchin 		if (b >= e)
456da2e3ebdSchin 			break;
457da2e3ebdSchin 		n = 0;
458da2e3ebdSchin 		while (m < x)
459da2e3ebdSchin 		{
460da2e3ebdSchin 			n = (n<<7) | (*m & 0x7f);
461da2e3ebdSchin 			if (!(*m++ & 0x80))
462da2e3ebdSchin 				break;
463da2e3ebdSchin 		}
464da2e3ebdSchin 		if (n >= (x - m))
465da2e3ebdSchin 			break;
466da2e3ebdSchin 		m += n;
467da2e3ebdSchin 	}
468da2e3ebdSchin 	return b;
469da2e3ebdSchin }
470da2e3ebdSchin 
471da2e3ebdSchin /*
472da2e3ebdSchin  * check for magic table match in buf
473da2e3ebdSchin  */
474da2e3ebdSchin 
475da2e3ebdSchin static char*
ckmagic(register Magic_t * mp,const char * file,char * buf,struct stat * st,unsigned long off)476da2e3ebdSchin ckmagic(register Magic_t* mp, const char* file, char* buf, struct stat* st, unsigned long off)
477da2e3ebdSchin {
478da2e3ebdSchin 	register Entry_t*	ep;
479da2e3ebdSchin 	register char*		p;
480da2e3ebdSchin 	register char*		b;
481da2e3ebdSchin 	register int		level = 0;
482da2e3ebdSchin 	int			call = -1;
483da2e3ebdSchin 	int			c;
484da2e3ebdSchin 	char*			q;
485da2e3ebdSchin 	char*			t;
486da2e3ebdSchin 	char*			base = 0;
487da2e3ebdSchin 	unsigned long		num;
488da2e3ebdSchin 	unsigned long		mask;
489da2e3ebdSchin 	regmatch_t		matches[10];
490da2e3ebdSchin 
491da2e3ebdSchin 	mp->swap = 0;
492da2e3ebdSchin 	b = mp->msg[0] = buf;
493da2e3ebdSchin 	mp->mime = mp->cap[0] = 0;
494da2e3ebdSchin 	mp->keep[0] = 0;
495da2e3ebdSchin 	for (ep = mp->magic; ep; ep = ep->next)
496da2e3ebdSchin 	{
497da2e3ebdSchin 	fun:
498da2e3ebdSchin 		if (ep->nest == '{')
499da2e3ebdSchin 		{
500da2e3ebdSchin 			if (++level >= MAXNEST)
501da2e3ebdSchin 			{
502da2e3ebdSchin 				call = -1;
503da2e3ebdSchin 				level = 0;
504da2e3ebdSchin 				mp->keep[0] = 0;
505da2e3ebdSchin 				b = mp->msg[0];
506da2e3ebdSchin 				mp->mime = mp->cap[0];
507da2e3ebdSchin 				continue;
508da2e3ebdSchin 			}
509da2e3ebdSchin 			mp->keep[level] = mp->keep[level - 1] != 0;
510da2e3ebdSchin 			mp->msg[level] = b;
511da2e3ebdSchin 			mp->cap[level] = mp->mime;
512da2e3ebdSchin 		}
513da2e3ebdSchin 		switch (ep->cont)
514da2e3ebdSchin 		{
515da2e3ebdSchin 		case '#':
516da2e3ebdSchin 			if (mp->keep[level] && b > buf)
517da2e3ebdSchin 			{
518da2e3ebdSchin 				*b = 0;
519da2e3ebdSchin 				return buf;
520da2e3ebdSchin 			}
521da2e3ebdSchin 			mp->swap = 0;
522da2e3ebdSchin 			b = mp->msg[0] = buf;
523da2e3ebdSchin 			mp->mime = mp->cap[0] = 0;
524da2e3ebdSchin 			if (ep->type == ' ')
525da2e3ebdSchin 				continue;
526da2e3ebdSchin 			break;
527da2e3ebdSchin 		case '$':
528da2e3ebdSchin 			if (mp->keep[level] && call < (MAXNEST - 1))
529da2e3ebdSchin 			{
530da2e3ebdSchin 				mp->ret[++call] = ep;
531da2e3ebdSchin 				ep = ep->value.lab;
532da2e3ebdSchin 				goto fun;
533da2e3ebdSchin 			}
534da2e3ebdSchin 			continue;
535da2e3ebdSchin 		case ':':
536da2e3ebdSchin 			ep = mp->ret[call--];
537da2e3ebdSchin 			if (ep->op == 'l')
538da2e3ebdSchin 				goto fun;
539da2e3ebdSchin 			continue;
540da2e3ebdSchin 		case '|':
541da2e3ebdSchin 			if (mp->keep[level] > 1)
542da2e3ebdSchin 				goto checknest;
543da2e3ebdSchin 			/*FALLTHROUGH*/
544da2e3ebdSchin 		default:
545da2e3ebdSchin 			if (!mp->keep[level])
546da2e3ebdSchin 			{
547da2e3ebdSchin 				b = mp->msg[level];
548da2e3ebdSchin 				mp->mime = mp->cap[level];
549da2e3ebdSchin 				goto checknest;
550da2e3ebdSchin 			}
551da2e3ebdSchin 			break;
552da2e3ebdSchin 		}
5537c2fbfb3SApril Chin 		p = "";
5547c2fbfb3SApril Chin 		num = 0;
555da2e3ebdSchin 		if (!ep->expr)
556da2e3ebdSchin 			num = ep->offset + off;
557da2e3ebdSchin 		else
558da2e3ebdSchin 			switch (ep->offset)
559da2e3ebdSchin 			{
560da2e3ebdSchin 			case 0:
561da2e3ebdSchin 				num = strexpr(ep->expr, NiL, indirect, mp) + off;
562da2e3ebdSchin 				break;
563da2e3ebdSchin 			case INFO_atime:
564da2e3ebdSchin 				num = st->st_atime;
565da2e3ebdSchin 				ep->type = 'D';
566da2e3ebdSchin 				break;
567da2e3ebdSchin 			case INFO_blocks:
568da2e3ebdSchin 				num = iblocks(st);
569da2e3ebdSchin 				ep->type = 'N';
570da2e3ebdSchin 				break;
571da2e3ebdSchin 			case INFO_ctime:
572da2e3ebdSchin 				num = st->st_ctime;
573da2e3ebdSchin 				ep->type = 'D';
574da2e3ebdSchin 				break;
575da2e3ebdSchin 			case INFO_fstype:
576da2e3ebdSchin 				p = fmtfs(st);
577da2e3ebdSchin 				ep->type = toupper(ep->type);
578da2e3ebdSchin 				break;
579da2e3ebdSchin 			case INFO_gid:
580da2e3ebdSchin 				if (ep->type == 'e' || ep->type == 'm' || ep->type == 's')
581da2e3ebdSchin 				{
582da2e3ebdSchin 					p = fmtgid(st->st_gid);
583da2e3ebdSchin 					ep->type = toupper(ep->type);
584da2e3ebdSchin 				}
585da2e3ebdSchin 				else
586da2e3ebdSchin 				{
587da2e3ebdSchin 					num = st->st_gid;
588da2e3ebdSchin 					ep->type = 'N';
589da2e3ebdSchin 				}
590da2e3ebdSchin 				break;
591da2e3ebdSchin 			case INFO_mode:
592da2e3ebdSchin 				if (ep->type == 'e' || ep->type == 'm' || ep->type == 's')
593da2e3ebdSchin 				{
594da2e3ebdSchin 					p = fmtmode(st->st_mode, 0);
595da2e3ebdSchin 					ep->type = toupper(ep->type);
596da2e3ebdSchin 				}
597da2e3ebdSchin 				else
598da2e3ebdSchin 				{
599da2e3ebdSchin 					num = modex(st->st_mode);
600da2e3ebdSchin 					ep->type = 'N';
601da2e3ebdSchin 				}
602da2e3ebdSchin 				break;
603da2e3ebdSchin 			case INFO_mtime:
604da2e3ebdSchin 				num = st->st_ctime;
605da2e3ebdSchin 				ep->type = 'D';
606da2e3ebdSchin 				break;
607da2e3ebdSchin 			case INFO_name:
608da2e3ebdSchin 				if (!base)
609da2e3ebdSchin 				{
610da2e3ebdSchin 					if (base = strrchr(file, '/'))
611da2e3ebdSchin 						base++;
612da2e3ebdSchin 					else
613da2e3ebdSchin 						base = (char*)file;
614da2e3ebdSchin 				}
615da2e3ebdSchin 				p = base;
616da2e3ebdSchin 				ep->type = toupper(ep->type);
617da2e3ebdSchin 				break;
618da2e3ebdSchin 			case INFO_nlink:
619da2e3ebdSchin 				num = st->st_nlink;
620da2e3ebdSchin 				ep->type = 'N';
621da2e3ebdSchin 				break;
622da2e3ebdSchin 			case INFO_size:
623da2e3ebdSchin 				num = st->st_size;
624da2e3ebdSchin 				ep->type = 'N';
625da2e3ebdSchin 				break;
626da2e3ebdSchin 			case INFO_uid:
627da2e3ebdSchin 				if (ep->type == 'e' || ep->type == 'm' || ep->type == 's')
628da2e3ebdSchin 				{
629da2e3ebdSchin 					p = fmtuid(st->st_uid);
630da2e3ebdSchin 					ep->type = toupper(ep->type);
631da2e3ebdSchin 				}
632da2e3ebdSchin 				else
633da2e3ebdSchin 				{
634da2e3ebdSchin 					num = st->st_uid;
635da2e3ebdSchin 					ep->type = 'N';
636da2e3ebdSchin 				}
637da2e3ebdSchin 				break;
638da2e3ebdSchin 			}
639da2e3ebdSchin 		switch (ep->type)
640da2e3ebdSchin 		{
641da2e3ebdSchin 
642da2e3ebdSchin 		case 'b':
643da2e3ebdSchin 			if (!(p = getdata(mp, num, 1)))
644da2e3ebdSchin 				goto next;
645da2e3ebdSchin 			num = *(unsigned char*)p;
646da2e3ebdSchin 			break;
647da2e3ebdSchin 
648da2e3ebdSchin 		case 'h':
649da2e3ebdSchin 			if (!(p = getdata(mp, num, 2)))
650da2e3ebdSchin 				goto next;
651da2e3ebdSchin 			num = swapget(ep->swap ? (~ep->swap ^ mp->swap) : mp->swap, p, 2);
652da2e3ebdSchin 			break;
653da2e3ebdSchin 
654da2e3ebdSchin 		case 'd':
655da2e3ebdSchin 		case 'l':
656da2e3ebdSchin 		case 'v':
657da2e3ebdSchin 			if (!(p = getdata(mp, num, 4)))
658da2e3ebdSchin 				goto next;
659da2e3ebdSchin 			num = swapget(ep->swap ? (~ep->swap ^ mp->swap) : mp->swap, p, 4);
660da2e3ebdSchin 			break;
661da2e3ebdSchin 
662da2e3ebdSchin 		case 'q':
663da2e3ebdSchin 			if (!(p = getdata(mp, num, 8)))
664da2e3ebdSchin 				goto next;
665da2e3ebdSchin 			num = swapget(ep->swap ? (~ep->swap ^ mp->swap) : mp->swap, p, 8);
666da2e3ebdSchin 			break;
667da2e3ebdSchin 
668da2e3ebdSchin 		case 'e':
669da2e3ebdSchin 			if (!(p = getdata(mp, num, 0)))
670da2e3ebdSchin 				goto next;
671da2e3ebdSchin 			/*FALLTHROUGH*/
672da2e3ebdSchin 		case 'E':
673da2e3ebdSchin 			if (!ep->value.sub)
674da2e3ebdSchin 				goto next;
675da2e3ebdSchin 			if ((c = regexec(ep->value.sub, p, elementsof(matches), matches, 0)) || (c = regsubexec(ep->value.sub, p, elementsof(matches), matches)))
676da2e3ebdSchin 			{
677da2e3ebdSchin 				c = mp->fbsz;
678da2e3ebdSchin 				if (c >= sizeof(mp->nbuf))
679da2e3ebdSchin 					c = sizeof(mp->nbuf) - 1;
680da2e3ebdSchin 				p = (char*)memcpy(mp->nbuf, p, c);
681da2e3ebdSchin 				p[c] = 0;
682da2e3ebdSchin 				ccmapstr(mp->x2n, p, c);
683da2e3ebdSchin 				if ((c = regexec(ep->value.sub, p, elementsof(matches), matches, 0)) || (c = regsubexec(ep->value.sub, p, elementsof(matches), matches)))
684da2e3ebdSchin 				{
685da2e3ebdSchin 					if (c != REG_NOMATCH)
686da2e3ebdSchin 						regmessage(mp, ep->value.sub, c);
687da2e3ebdSchin 					goto next;
688da2e3ebdSchin 				}
689da2e3ebdSchin 			}
690da2e3ebdSchin 			p = ep->value.sub->re_sub->re_buf;
691da2e3ebdSchin 			q = T(ep->desc);
692da2e3ebdSchin 			t = *q ? q : p;
693da2e3ebdSchin 			if (mp->keep[level]++ && b > buf && *(b - 1) != ' ' && *t && *t != ',' && *t != '.' && *t != '\b')
694da2e3ebdSchin 				*b++ = ' ';
695da2e3ebdSchin 			b += sfsprintf(b, PATH_MAX - (b - buf), *q ? q : "%s", p + (*p == '\b'));
696da2e3ebdSchin 			if (ep->mime)
697da2e3ebdSchin 				mp->mime = ep->mime;
698da2e3ebdSchin 			goto checknest;
699da2e3ebdSchin 
700da2e3ebdSchin 		case 's':
701da2e3ebdSchin 			if (!(p = getdata(mp, num, ep->mask)))
702da2e3ebdSchin 				goto next;
703da2e3ebdSchin 			goto checkstr;
704da2e3ebdSchin 		case 'm':
705da2e3ebdSchin 			if (!(p = getdata(mp, num, 0)))
706da2e3ebdSchin 				goto next;
707da2e3ebdSchin 			/*FALLTHROUGH*/
708da2e3ebdSchin 		case 'M':
709da2e3ebdSchin 		case 'S':
710da2e3ebdSchin 		checkstr:
711da2e3ebdSchin 			for (;;)
712da2e3ebdSchin 			{
713da2e3ebdSchin 				if (*ep->value.str == '*' && !*(ep->value.str + 1) && isprint(*p))
714da2e3ebdSchin 					break;
715da2e3ebdSchin 				if ((ep->type == 'm' || ep->type == 'M') ? strmatch(p, ep->value.str) : !memcmp(p, ep->value.str, ep->mask))
716da2e3ebdSchin 					break;
717da2e3ebdSchin 				if (p == mp->nbuf || ep->mask >= sizeof(mp->nbuf))
718da2e3ebdSchin 					goto next;
719da2e3ebdSchin 				p = (char*)memcpy(mp->nbuf, p, ep->mask);
720da2e3ebdSchin 				p[ep->mask] = 0;
721da2e3ebdSchin 				ccmapstr(mp->x2n, p, ep->mask);
722da2e3ebdSchin 			}
723da2e3ebdSchin 			q = T(ep->desc);
724da2e3ebdSchin 			if (mp->keep[level]++ && b > buf && *(b - 1) != ' ' && *q && *q != ',' && *q != '.' && *q != '\b')
725da2e3ebdSchin 				*b++ = ' ';
726da2e3ebdSchin 			for (t = p; (c = *t) >= 0 && c <= 0177 && isprint(c) && c != '\n'; t++);
727da2e3ebdSchin 			*t = 0;
728da2e3ebdSchin 			b += sfsprintf(b, PATH_MAX - (b - buf), q + (*q == '\b'), p);
729da2e3ebdSchin 			*t = c;
730da2e3ebdSchin 			if (ep->mime)
731da2e3ebdSchin 				mp->mime = ep->mime;
732da2e3ebdSchin 			goto checknest;
733da2e3ebdSchin 
734da2e3ebdSchin 		}
735da2e3ebdSchin 		if (mask = ep->mask)
736da2e3ebdSchin 			num &= mask;
737da2e3ebdSchin 		switch (ep->op)
738da2e3ebdSchin 		{
739da2e3ebdSchin 
740da2e3ebdSchin 		case '=':
741da2e3ebdSchin 		case '@':
742da2e3ebdSchin 			if (num == ep->value.num)
743da2e3ebdSchin 				break;
744da2e3ebdSchin 			if (ep->cont != '#')
745da2e3ebdSchin 				goto next;
746da2e3ebdSchin 			if (!mask)
747da2e3ebdSchin 				mask = ~mask;
748da2e3ebdSchin 			if (ep->type == 'h')
749da2e3ebdSchin 			{
750da2e3ebdSchin 				if ((num = swapget(mp->swap = 1, p, 2) & mask) == ep->value.num)
751da2e3ebdSchin 				{
752da2e3ebdSchin 					if (!(mp->swap & (mp->swap + 1)))
753da2e3ebdSchin 						mp->swap = 7;
754da2e3ebdSchin 					goto swapped;
755da2e3ebdSchin 				}
756da2e3ebdSchin 			}
757da2e3ebdSchin 			else if (ep->type == 'l')
758da2e3ebdSchin 			{
759da2e3ebdSchin 				for (c = 1; c < 4; c++)
760da2e3ebdSchin 					if ((num = swapget(mp->swap = c, p, 4) & mask) == ep->value.num)
761da2e3ebdSchin 					{
762da2e3ebdSchin 						if (!(mp->swap & (mp->swap + 1)))
763da2e3ebdSchin 							mp->swap = 7;
764da2e3ebdSchin 						goto swapped;
765da2e3ebdSchin 					}
766da2e3ebdSchin 			}
767da2e3ebdSchin 			else if (ep->type == 'q')
768da2e3ebdSchin 			{
769da2e3ebdSchin 				for (c = 1; c < 8; c++)
770da2e3ebdSchin 					if ((num = swapget(mp->swap = c, p, 8) & mask) == ep->value.num)
771da2e3ebdSchin 						goto swapped;
772da2e3ebdSchin 			}
773da2e3ebdSchin 			goto next;
774da2e3ebdSchin 
775da2e3ebdSchin 		case '!':
776da2e3ebdSchin 			if (num != ep->value.num)
777da2e3ebdSchin 				break;
778da2e3ebdSchin 			goto next;
779da2e3ebdSchin 
780da2e3ebdSchin 		case '^':
781da2e3ebdSchin 			if (num ^ ep->value.num)
782da2e3ebdSchin 				break;
783da2e3ebdSchin 			goto next;
784da2e3ebdSchin 
785da2e3ebdSchin 		case '>':
786da2e3ebdSchin 			if (num > ep->value.num)
787da2e3ebdSchin 				break;
788da2e3ebdSchin 			goto next;
789da2e3ebdSchin 
790da2e3ebdSchin 		case '<':
791da2e3ebdSchin 			if (num < ep->value.num)
792da2e3ebdSchin 				break;
793da2e3ebdSchin 			goto next;
794da2e3ebdSchin 
795da2e3ebdSchin 		case 'l':
796da2e3ebdSchin 			if (num > 0 && mp->keep[level] && call < (MAXNEST - 1))
797da2e3ebdSchin 			{
798da2e3ebdSchin 				if (!ep->value.loop->count)
799da2e3ebdSchin 				{
800da2e3ebdSchin 					ep->value.loop->count = num;
801da2e3ebdSchin 					ep->value.loop->offset = off;
802da2e3ebdSchin 					off = ep->value.loop->start;
803da2e3ebdSchin 				}
804da2e3ebdSchin 				else if (!--ep->value.loop->count)
805da2e3ebdSchin 				{
806da2e3ebdSchin 					off = ep->value.loop->offset;
807da2e3ebdSchin 					goto next;
808da2e3ebdSchin 				}
809da2e3ebdSchin 				else
810da2e3ebdSchin 					off += ep->value.loop->size;
811da2e3ebdSchin 				mp->ret[++call] = ep;
812da2e3ebdSchin 				ep = ep->value.loop->lab;
813da2e3ebdSchin 				goto fun;
814da2e3ebdSchin 			}
815da2e3ebdSchin 			goto next;
816da2e3ebdSchin 
817da2e3ebdSchin 		case 'm':
818da2e3ebdSchin 			c = mp->swap;
819da2e3ebdSchin 			t = ckmagic(mp, file, b + (b > buf), st, num);
820da2e3ebdSchin 			mp->swap = c;
821da2e3ebdSchin 			if (!t)
822da2e3ebdSchin 				goto next;
823da2e3ebdSchin 			if (b > buf)
824da2e3ebdSchin 				*b = ' ';
825da2e3ebdSchin 			b += strlen(b);
826da2e3ebdSchin 			break;
827da2e3ebdSchin 
828da2e3ebdSchin 		case 'r':
829da2e3ebdSchin #if _UWIN
830da2e3ebdSchin 		{
831da2e3ebdSchin 			char*			e;
832da2e3ebdSchin 			Sfio_t*			rp;
833da2e3ebdSchin 			Sfio_t*			gp;
834da2e3ebdSchin 
835da2e3ebdSchin 			if (!(t = strrchr(file, '.')))
836da2e3ebdSchin 				goto next;
837da2e3ebdSchin 			sfprintf(mp->tmp, "/reg/classes_root/%s", t);
838da2e3ebdSchin 			if (!(t = sfstruse(mp->tmp)) || !(rp = sfopen(NiL, t, "r")))
839da2e3ebdSchin 				goto next;
840da2e3ebdSchin 			*ep->desc = 0;
841da2e3ebdSchin 			*ep->mime = 0;
842da2e3ebdSchin 			gp = 0;
843da2e3ebdSchin 			while (t = sfgetr(rp, '\n', 1))
844da2e3ebdSchin 			{
845da2e3ebdSchin 				if (strneq(t, "Content Type=", 13))
846da2e3ebdSchin 				{
847da2e3ebdSchin 					ep->mime = vmnewof(mp->vm, ep->mime, char, sfvalue(rp), 0);
848da2e3ebdSchin 					strcpy(ep->mime, t + 13);
849da2e3ebdSchin 					if (gp)
850da2e3ebdSchin 						break;
851da2e3ebdSchin 				}
852da2e3ebdSchin 				else
853da2e3ebdSchin 				{
854da2e3ebdSchin 					sfprintf(mp->tmp, "/reg/classes_root/%s", t);
855da2e3ebdSchin 					if ((e = sfstruse(mp->tmp)) && (gp = sfopen(NiL, e, "r")))
856da2e3ebdSchin 					{
857da2e3ebdSchin 						ep->desc = vmnewof(mp->vm, ep->desc, char, strlen(t), 1);
858da2e3ebdSchin 						strcpy(ep->desc, t);
859da2e3ebdSchin 						if (*ep->mime)
860da2e3ebdSchin 							break;
861da2e3ebdSchin 					}
862da2e3ebdSchin 				}
863da2e3ebdSchin 			}
864da2e3ebdSchin 			sfclose(rp);
865da2e3ebdSchin 			if (!gp)
866da2e3ebdSchin 				goto next;
867da2e3ebdSchin 			if (!*ep->mime)
868da2e3ebdSchin 			{
869da2e3ebdSchin 				t = T(ep->desc);
870da2e3ebdSchin 				if (!strncasecmp(t, "microsoft", 9))
871da2e3ebdSchin 					t += 9;
872da2e3ebdSchin 				while (isspace(*t))
873da2e3ebdSchin 					t++;
874da2e3ebdSchin 				e = "application/x-ms-";
875da2e3ebdSchin 				ep->mime = vmnewof(mp->vm, ep->mime, char, strlen(t), strlen(e));
876da2e3ebdSchin 				e = strcopy(ep->mime, e);
877da2e3ebdSchin 				while ((c = *t++) && c != '.' && c != ' ')
878da2e3ebdSchin 					*e++ = isupper(c) ? tolower(c) : c;
879da2e3ebdSchin 				*e = 0;
880da2e3ebdSchin 			}
881da2e3ebdSchin 			while (t = sfgetr(gp, '\n', 1))
882da2e3ebdSchin 				if (*t && !streq(t, "\"\""))
883da2e3ebdSchin 				{
884da2e3ebdSchin 					ep->desc = vmnewof(mp->vm, ep->desc, char, sfvalue(gp), 0);
885da2e3ebdSchin 					strcpy(ep->desc, t);
886da2e3ebdSchin 					break;
887da2e3ebdSchin 				}
888da2e3ebdSchin 			sfclose(gp);
889da2e3ebdSchin 			if (!*ep->desc)
890da2e3ebdSchin 				goto next;
891da2e3ebdSchin 			if (!t)
892da2e3ebdSchin 				for (t = T(ep->desc); *t; t++)
893da2e3ebdSchin 					if (*t == '.')
894da2e3ebdSchin 						*t = ' ';
895da2e3ebdSchin 			if (!mp->keep[level])
896da2e3ebdSchin 				mp->keep[level] = 2;
897da2e3ebdSchin 			mp->mime = ep->mime;
898da2e3ebdSchin 			break;
899da2e3ebdSchin 		}
900da2e3ebdSchin #else
901da2e3ebdSchin 			if (ep->cont == '#' && !mp->keep[level])
902da2e3ebdSchin 				mp->keep[level] = 1;
903da2e3ebdSchin 			goto next;
904da2e3ebdSchin #endif
905da2e3ebdSchin 
906da2e3ebdSchin 		case 'v':
907da2e3ebdSchin 			if (!(p = getdata(mp, num, 4)))
908da2e3ebdSchin 				goto next;
909da2e3ebdSchin 			c = 0;
910da2e3ebdSchin 			do
911da2e3ebdSchin 			{
912da2e3ebdSchin 				num++;
913da2e3ebdSchin 				c = (c<<7) | (*p & 0x7f);
914da2e3ebdSchin 			} while (*p++ & 0x80);
915da2e3ebdSchin 			if (!(p = getdata(mp, num, c)))
916da2e3ebdSchin 				goto next;
917da2e3ebdSchin 			if (mp->keep[level]++ && b > buf && *(b - 1) != ' ')
918da2e3ebdSchin 			{
919da2e3ebdSchin 				*b++ = ',';
920da2e3ebdSchin 				*b++ = ' ';
921da2e3ebdSchin 			}
922da2e3ebdSchin 			b = vcdecomp(b, buf + PATH_MAX, (unsigned char*)p, (unsigned char*)p + c);
923da2e3ebdSchin 			goto checknest;
924da2e3ebdSchin 
925da2e3ebdSchin 		}
926da2e3ebdSchin 	swapped:
927da2e3ebdSchin 		q = T(ep->desc);
928da2e3ebdSchin 		if (mp->keep[level]++ && b > buf && *(b - 1) != ' ' && *q && *q != ',' && *q != '.' && *q != '\b')
929da2e3ebdSchin 			*b++ = ' ';
930da2e3ebdSchin 		if (ep->type == 'd' || ep->type == 'D')
931da2e3ebdSchin 			b += sfsprintf(b, PATH_MAX - (b - buf), q + (*q == '\b'), fmttime("%?%l", (time_t)num));
932da2e3ebdSchin 		else if (ep->type == 'v')
933da2e3ebdSchin 			b += sfsprintf(b, PATH_MAX - (b - buf), q + (*q == '\b'), fmtversion(num));
934da2e3ebdSchin 		else
935da2e3ebdSchin 			b += sfsprintf(b, PATH_MAX - (b - buf), q + (*q == '\b'), num);
936da2e3ebdSchin 		if (ep->mime && *ep->mime)
937da2e3ebdSchin 			mp->mime = ep->mime;
938da2e3ebdSchin 	checknest:
939da2e3ebdSchin 		if (ep->nest == '}')
940da2e3ebdSchin 		{
941da2e3ebdSchin 			if (!mp->keep[level])
942da2e3ebdSchin 			{
943da2e3ebdSchin 				b = mp->msg[level];
944da2e3ebdSchin 				mp->mime = mp->cap[level];
945da2e3ebdSchin 			}
946da2e3ebdSchin 			else if (level > 0)
947da2e3ebdSchin 				mp->keep[level - 1] = mp->keep[level];
948da2e3ebdSchin 			if (--level < 0)
949da2e3ebdSchin 			{
950da2e3ebdSchin 				level = 0;
951da2e3ebdSchin 				mp->keep[0] = 0;
952da2e3ebdSchin 			}
953da2e3ebdSchin 		}
954da2e3ebdSchin 		continue;
955da2e3ebdSchin 	next:
956da2e3ebdSchin 		if (ep->cont == '&')
957da2e3ebdSchin 			mp->keep[level] = 0;
958da2e3ebdSchin 		goto checknest;
959da2e3ebdSchin 	}
960da2e3ebdSchin 	if (mp->keep[level] && b > buf)
961da2e3ebdSchin 	{
962da2e3ebdSchin 		*b = 0;
963da2e3ebdSchin 		return buf;
964da2e3ebdSchin 	}
965da2e3ebdSchin 	return 0;
966da2e3ebdSchin }
967da2e3ebdSchin 
968da2e3ebdSchin /*
969da2e3ebdSchin  * check english language stats
970da2e3ebdSchin  */
971da2e3ebdSchin 
972da2e3ebdSchin static int
ckenglish(register Magic_t * mp,int pun,int badpun)973da2e3ebdSchin ckenglish(register Magic_t* mp, int pun, int badpun)
974da2e3ebdSchin {
975da2e3ebdSchin 	register char*	s;
976da2e3ebdSchin 	register int	vowl = 0;
977da2e3ebdSchin 	register int	freq = 0;
978da2e3ebdSchin 	register int	rare = 0;
979da2e3ebdSchin 
980da2e3ebdSchin 	if (5 * badpun > pun)
981da2e3ebdSchin 		return 0;
982da2e3ebdSchin 	if (2 * mp->count[';'] > mp->count['E'] + mp->count['e'])
983da2e3ebdSchin 		return 0;
984da2e3ebdSchin 	if ((mp->count['>'] + mp->count['<'] + mp->count['/']) > mp->count['E'] + mp->count['e'])
985da2e3ebdSchin 		return 0;
986da2e3ebdSchin 	for (s = "aeiou"; *s; s++)
987da2e3ebdSchin 		vowl += mp->count[toupper(*s)] + mp->count[*s];
988da2e3ebdSchin 	for (s = "etaion"; *s; s++)
989da2e3ebdSchin 		freq += mp->count[toupper(*s)] + mp->count[*s];
990da2e3ebdSchin 	for (s = "vjkqxz"; *s; s++)
991da2e3ebdSchin 		rare += mp->count[toupper(*s)] + mp->count[*s];
992da2e3ebdSchin 	return 5 * vowl >= mp->fbsz - mp->count[' '] && freq >= 10 * rare;
993da2e3ebdSchin }
994da2e3ebdSchin 
995da2e3ebdSchin /*
996da2e3ebdSchin  * check programming language stats
997da2e3ebdSchin  */
998da2e3ebdSchin 
999da2e3ebdSchin static char*
cklang(register Magic_t * mp,const char * file,char * buf,struct stat * st)1000da2e3ebdSchin cklang(register Magic_t* mp, const char* file, char* buf, struct stat* st)
1001da2e3ebdSchin {
1002da2e3ebdSchin 	register int		c;
1003da2e3ebdSchin 	register unsigned char*	b;
1004da2e3ebdSchin 	register unsigned char*	e;
1005da2e3ebdSchin 	register int		q;
1006da2e3ebdSchin 	register char*		s;
1007da2e3ebdSchin 	char*			t;
1008da2e3ebdSchin 	char*			base;
1009da2e3ebdSchin 	char*			suff;
1010da2e3ebdSchin 	char*			t1;
1011da2e3ebdSchin 	char*			t2;
1012da2e3ebdSchin 	char*			t3;
1013da2e3ebdSchin 	int			n;
1014da2e3ebdSchin 	int			badpun;
1015da2e3ebdSchin 	int			code;
1016da2e3ebdSchin 	int			pun;
1017da2e3ebdSchin 	Cctype_t		flags;
1018da2e3ebdSchin 	Info_t*			ip;
1019da2e3ebdSchin 
1020da2e3ebdSchin 	b = (unsigned char*)mp->fbuf;
1021da2e3ebdSchin 	e = b + mp->fbsz;
1022da2e3ebdSchin 	memzero(mp->count, sizeof(mp->count));
1023da2e3ebdSchin 	memzero(mp->multi, sizeof(mp->multi));
1024da2e3ebdSchin 	memzero(mp->identifier, sizeof(mp->identifier));
1025da2e3ebdSchin 
1026da2e3ebdSchin 	/*
1027da2e3ebdSchin 	 * check character coding
1028da2e3ebdSchin 	 */
1029da2e3ebdSchin 
1030da2e3ebdSchin 	flags = 0;
1031da2e3ebdSchin 	while (b < e)
1032da2e3ebdSchin 		flags |= mp->cctype[*b++];
1033da2e3ebdSchin 	b = (unsigned char*)mp->fbuf;
1034da2e3ebdSchin 	code = 0;
1035da2e3ebdSchin 	q = CC_ASCII;
1036da2e3ebdSchin 	n = CC_MASK;
1037da2e3ebdSchin 	for (c = 0; c < CC_MAPS; c++)
1038da2e3ebdSchin 	{
1039da2e3ebdSchin 		flags ^= CC_text;
1040da2e3ebdSchin 		if ((flags & CC_MASK) < n)
1041da2e3ebdSchin 		{
1042da2e3ebdSchin 			n = flags & CC_MASK;
1043da2e3ebdSchin 			q = c;
1044da2e3ebdSchin 		}
1045da2e3ebdSchin 		flags >>= CC_BIT;
1046da2e3ebdSchin 	}
1047da2e3ebdSchin 	flags = n;
1048da2e3ebdSchin 	if (!(flags & (CC_binary|CC_notext)))
1049da2e3ebdSchin 	{
1050da2e3ebdSchin 		if (q != CC_NATIVE)
1051da2e3ebdSchin 		{
1052da2e3ebdSchin 			code = q;
1053da2e3ebdSchin 			ccmaps(mp->fbuf, mp->fbsz, q, CC_NATIVE);
1054da2e3ebdSchin 		}
1055da2e3ebdSchin 		if (b[0] == '#' && b[1] == '!')
1056da2e3ebdSchin 		{
1057da2e3ebdSchin 			for (b += 2; b < e && isspace(*b); b++);
1058da2e3ebdSchin 			for (s = (char*)b; b < e && isprint(*b); b++);
1059da2e3ebdSchin 			c = *b;
1060da2e3ebdSchin 			*b = 0;
1061da2e3ebdSchin 			if ((st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) || match(s, "/*bin*/*") || !access(s, F_OK))
1062da2e3ebdSchin 			{
1063da2e3ebdSchin 				if (t = strrchr(s, '/'))
1064da2e3ebdSchin 					s = t + 1;
1065da2e3ebdSchin 				for (t = s; *t; t++)
1066da2e3ebdSchin 					if (isspace(*t))
1067da2e3ebdSchin 					{
1068da2e3ebdSchin 						*t = 0;
1069da2e3ebdSchin 						break;
1070da2e3ebdSchin 					}
1071da2e3ebdSchin 				sfsprintf(mp->mbuf, sizeof(mp->mbuf), "application/x-%s", *s ? s : "sh");
1072da2e3ebdSchin 				mp->mime = mp->mbuf;
1073da2e3ebdSchin 				if (match(s, "*sh"))
1074da2e3ebdSchin 				{
1075da2e3ebdSchin 					t1 = T("command");
1076da2e3ebdSchin 					if (streq(s, "sh"))
1077da2e3ebdSchin 						*s = 0;
1078da2e3ebdSchin 					else
1079da2e3ebdSchin 					{
1080da2e3ebdSchin 						*b++ = ' ';
1081da2e3ebdSchin 						*b = 0;
1082da2e3ebdSchin 					}
1083da2e3ebdSchin 				}
1084da2e3ebdSchin 				else
1085da2e3ebdSchin 				{
1086da2e3ebdSchin 					t1 = T("interpreter");
1087da2e3ebdSchin 					*b++ = ' ';
1088da2e3ebdSchin 					*b = 0;
1089da2e3ebdSchin 				}
1090da2e3ebdSchin 				sfsprintf(mp->sbuf, sizeof(mp->sbuf), T("%s%s script"), s, t1);
1091da2e3ebdSchin 				s = mp->sbuf;
1092da2e3ebdSchin 				goto qualify;
1093da2e3ebdSchin 			}
1094da2e3ebdSchin 			*b = c;
1095da2e3ebdSchin 			b = (unsigned char*)mp->fbuf;
1096da2e3ebdSchin 		}
1097da2e3ebdSchin 		badpun = 0;
1098da2e3ebdSchin 		pun = 0;
1099da2e3ebdSchin 		q = 0;
1100da2e3ebdSchin 		s = 0;
1101da2e3ebdSchin 		t = 0;
1102da2e3ebdSchin 		while (b < e)
1103da2e3ebdSchin 		{
1104da2e3ebdSchin 			c = *b++;
1105da2e3ebdSchin 			mp->count[c]++;
1106da2e3ebdSchin 			if (c == q && (q != '*' || *b == '/' && b++))
1107da2e3ebdSchin 			{
1108da2e3ebdSchin 				mp->multi[q]++;
1109da2e3ebdSchin 				q = 0;
1110da2e3ebdSchin 			}
1111da2e3ebdSchin 			else if (c == '\\')
1112da2e3ebdSchin 			{
1113da2e3ebdSchin 				s = 0;
1114da2e3ebdSchin 				b++;
1115da2e3ebdSchin 			}
1116da2e3ebdSchin 			else if (!q)
1117da2e3ebdSchin 			{
1118da2e3ebdSchin 				if (isalpha(c) || c == '_')
1119da2e3ebdSchin 				{
1120da2e3ebdSchin 					if (!s)
1121da2e3ebdSchin 						s = (char*)b - 1;
1122da2e3ebdSchin 				}
1123da2e3ebdSchin 				else if (!isdigit(c))
1124da2e3ebdSchin 				{
1125da2e3ebdSchin 					if (s)
1126da2e3ebdSchin 					{
1127da2e3ebdSchin 						if (s > mp->fbuf)
1128da2e3ebdSchin 							switch (*(s - 1))
1129da2e3ebdSchin 							{
1130da2e3ebdSchin 							case ':':
1131da2e3ebdSchin 								if (*b == ':')
1132da2e3ebdSchin 									mp->multi[':']++;
1133da2e3ebdSchin 								break;
1134da2e3ebdSchin 							case '.':
1135da2e3ebdSchin 								if (((char*)b - s) == 3 && (s == (mp->fbuf + 1) || *(s - 2) == '\n'))
1136da2e3ebdSchin 									mp->multi['.']++;
1137da2e3ebdSchin 								break;
1138da2e3ebdSchin 							case '\n':
1139da2e3ebdSchin 							case '\\':
1140da2e3ebdSchin 								if (*b == '{')
1141da2e3ebdSchin 									t = (char*)b + 1;
1142da2e3ebdSchin 								break;
1143da2e3ebdSchin 							case '{':
1144da2e3ebdSchin 								if (s == t && *b == '}')
1145da2e3ebdSchin 									mp->multi['X']++;
1146da2e3ebdSchin 								break;
1147da2e3ebdSchin 							}
1148da2e3ebdSchin 							if (!mp->idtab)
1149da2e3ebdSchin 							{
1150da2e3ebdSchin 								if (mp->idtab = dtnew(mp->vm, &mp->dtdisc, Dthash))
1151da2e3ebdSchin 									for (q = 0; q < elementsof(dict); q++)
1152da2e3ebdSchin 										dtinsert(mp->idtab, &dict[q]);
1153da2e3ebdSchin 								else if (mp->disc->errorf)
1154da2e3ebdSchin 									(*mp->disc->errorf)(mp, mp->disc, 3, "out of space");
1155da2e3ebdSchin 								q = 0;
1156da2e3ebdSchin 							}
1157da2e3ebdSchin 							if (mp->idtab)
1158da2e3ebdSchin 							{
1159da2e3ebdSchin 								*(b - 1) = 0;
1160da2e3ebdSchin 								if (ip = (Info_t*)dtmatch(mp->idtab, s))
1161da2e3ebdSchin 									mp->identifier[ip->value]++;
1162da2e3ebdSchin 								*(b - 1) = c;
1163da2e3ebdSchin 							}
1164da2e3ebdSchin 							s = 0;
1165da2e3ebdSchin 						}
1166da2e3ebdSchin 					switch (c)
1167da2e3ebdSchin 					{
1168da2e3ebdSchin 					case '\t':
1169da2e3ebdSchin 						if (b == (unsigned char*)(mp->fbuf + 1) || *(b - 2) == '\n')
1170da2e3ebdSchin 							mp->multi['\t']++;
1171da2e3ebdSchin 						break;
1172da2e3ebdSchin 					case '"':
1173da2e3ebdSchin 					case '\'':
1174da2e3ebdSchin 						q = c;
1175da2e3ebdSchin 						break;
1176da2e3ebdSchin 					case '/':
1177da2e3ebdSchin 						if (*b == '*')
1178da2e3ebdSchin 							q = *b++;
1179da2e3ebdSchin 						else if (*b == '/')
1180da2e3ebdSchin 							q = '\n';
1181da2e3ebdSchin 						break;
1182da2e3ebdSchin 					case '$':
1183da2e3ebdSchin 						if (*b == '(' && *(b + 1) != ' ')
1184da2e3ebdSchin 							mp->multi['$']++;
1185da2e3ebdSchin 						break;
1186da2e3ebdSchin 					case '{':
1187da2e3ebdSchin 					case '}':
1188da2e3ebdSchin 					case '[':
1189da2e3ebdSchin 					case ']':
1190da2e3ebdSchin 					case '(':
1191da2e3ebdSchin 						mp->multi[c]++;
1192da2e3ebdSchin 						break;
1193da2e3ebdSchin 					case ')':
1194da2e3ebdSchin 						mp->multi[c]++;
1195da2e3ebdSchin 						goto punctuation;
1196da2e3ebdSchin 					case ':':
1197da2e3ebdSchin 						if (*b == ':' && isspace(*(b + 1)) && b > (unsigned char*)(mp->fbuf + 1) && isspace(*(b - 2)))
1198da2e3ebdSchin 							mp->multi[':']++;
1199da2e3ebdSchin 						goto punctuation;
1200da2e3ebdSchin 					case '.':
1201da2e3ebdSchin 					case ',':
1202da2e3ebdSchin 					case '%':
1203da2e3ebdSchin 					case ';':
1204da2e3ebdSchin 					case '?':
1205da2e3ebdSchin 					punctuation:
1206da2e3ebdSchin 						pun++;
1207da2e3ebdSchin 						if (*b != ' ' && *b != '\n')
1208da2e3ebdSchin 							badpun++;
1209da2e3ebdSchin 						break;
1210da2e3ebdSchin 					}
1211da2e3ebdSchin 				}
1212da2e3ebdSchin 			}
1213da2e3ebdSchin 		}
1214da2e3ebdSchin 	}
1215da2e3ebdSchin 	else
1216da2e3ebdSchin 		while (b < e)
1217da2e3ebdSchin 			mp->count[*b++]++;
1218da2e3ebdSchin 	base = (t1 = strrchr(file, '/')) ? t1 + 1 : (char*)file;
1219da2e3ebdSchin 	suff = (t1 = strrchr(base, '.')) ? t1 + 1 : "";
1220da2e3ebdSchin 	if (!flags)
1221da2e3ebdSchin 	{
1222da2e3ebdSchin 		if (match(suff, "*sh|bat|cmd"))
1223da2e3ebdSchin 			goto id_sh;
1224da2e3ebdSchin 		if (match(base, "*@(mkfile)"))
1225da2e3ebdSchin 			goto id_mk;
1226da2e3ebdSchin 		if (match(base, "*@(makefile|.mk)"))
1227da2e3ebdSchin 			goto id_make;
1228da2e3ebdSchin 		if (match(base, "*@(mamfile|.mam)"))
1229da2e3ebdSchin 			goto id_mam;
1230da2e3ebdSchin 		if (match(suff, "[cly]?(pp|xx|++)|cc|ll|yy"))
1231da2e3ebdSchin 			goto id_c;
1232da2e3ebdSchin 		if (match(suff, "f"))
1233da2e3ebdSchin 			goto id_fortran;
1234da2e3ebdSchin 		if (match(suff, "htm+(l)"))
1235da2e3ebdSchin 			goto id_html;
1236da2e3ebdSchin 		if (match(suff, "cpy"))
1237da2e3ebdSchin 			goto id_copybook;
1238da2e3ebdSchin 		if (match(suff, "cob|cbl|cb2"))
1239da2e3ebdSchin 			goto id_cobol;
1240da2e3ebdSchin 		if (match(suff, "pl[1i]"))
1241da2e3ebdSchin 			goto id_pl1;
1242da2e3ebdSchin 		if (match(suff, "tex"))
1243da2e3ebdSchin 			goto id_tex;
1244da2e3ebdSchin 		if (match(suff, "asm|s"))
1245da2e3ebdSchin 			goto id_asm;
1246da2e3ebdSchin 		if ((st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) && (!suff || suff != strchr(suff, '.')))
1247da2e3ebdSchin 		{
1248da2e3ebdSchin 		id_sh:
1249da2e3ebdSchin 			s = T("command script");
1250da2e3ebdSchin 			mp->mime = "application/sh";
1251da2e3ebdSchin 			goto qualify;
1252da2e3ebdSchin 		}
1253da2e3ebdSchin 		if (strmatch(mp->fbuf, "From * [0-9][0-9]:[0-9][0-9]:[0-9][0-9] *"))
1254da2e3ebdSchin 		{
1255da2e3ebdSchin 			s = T("mail message");
1256da2e3ebdSchin 			mp->mime = "message/rfc822";
1257da2e3ebdSchin 			goto qualify;
1258da2e3ebdSchin 		}
1259da2e3ebdSchin 		if (match(base, "*@(mkfile)"))
1260da2e3ebdSchin 		{
1261da2e3ebdSchin 		id_mk:
1262da2e3ebdSchin 			s = "mkfile";
1263da2e3ebdSchin 			mp->mime = "application/mk";
1264da2e3ebdSchin 			goto qualify;
1265da2e3ebdSchin 		}
1266da2e3ebdSchin 		if (match(base, "*@(makefile|.mk)") || mp->multi['\t'] >= mp->count[':'] && (mp->multi['$'] > 0 || mp->multi[':'] > 0))
1267da2e3ebdSchin 		{
1268da2e3ebdSchin 		id_make:
1269da2e3ebdSchin 			s = "makefile";
1270da2e3ebdSchin 			mp->mime = "application/make";
1271da2e3ebdSchin 			goto qualify;
1272da2e3ebdSchin 		}
1273da2e3ebdSchin 		if (mp->multi['.'] >= 3)
1274da2e3ebdSchin 		{
1275da2e3ebdSchin 			s = T("nroff input");
1276da2e3ebdSchin 			mp->mime = "application/x-troff";
1277da2e3ebdSchin 			goto qualify;
1278da2e3ebdSchin 		}
1279da2e3ebdSchin 		if (mp->multi['X'] >= 3)
1280da2e3ebdSchin 		{
1281da2e3ebdSchin 			s = T("TeX input");
1282da2e3ebdSchin 			mp->mime = "application/x-tex";
1283da2e3ebdSchin 			goto qualify;
1284da2e3ebdSchin 		}
1285da2e3ebdSchin 		if (mp->fbsz < SF_BUFSIZE &&
1286da2e3ebdSchin 		    (mp->multi['('] == mp->multi[')'] &&
1287da2e3ebdSchin 		     mp->multi['{'] == mp->multi['}'] &&
1288da2e3ebdSchin 		     mp->multi['['] == mp->multi[']']) ||
1289da2e3ebdSchin 		    mp->fbsz >= SF_BUFSIZE &&
1290da2e3ebdSchin 		    (mp->multi['('] >= mp->multi[')'] &&
1291da2e3ebdSchin 		     mp->multi['{'] >= mp->multi['}'] &&
1292da2e3ebdSchin 		     mp->multi['['] >= mp->multi[']']))
1293da2e3ebdSchin 		{
1294da2e3ebdSchin 			c = mp->identifier[ID_INCL1];
1295da2e3ebdSchin 			if (c >= 2 && mp->identifier[ID_INCL2] >= c && mp->identifier[ID_INCL3] >= c && mp->count['.'] >= c ||
1296da2e3ebdSchin 			    mp->identifier[ID_C] >= 5 && mp->count[';'] >= 5 ||
1297da2e3ebdSchin 			    mp->count['='] >= 20 && mp->count[';'] >= 20)
1298da2e3ebdSchin 			{
1299da2e3ebdSchin 			id_c:
1300da2e3ebdSchin 				t1 = "";
1301da2e3ebdSchin 				t2 = "c ";
1302da2e3ebdSchin 				t3 = T("program");
1303da2e3ebdSchin 				switch (*suff)
1304da2e3ebdSchin 				{
1305da2e3ebdSchin 				case 'c':
1306da2e3ebdSchin 				case 'C':
1307da2e3ebdSchin 					mp->mime = "application/x-cc";
1308da2e3ebdSchin 					break;
1309da2e3ebdSchin 				case 'l':
1310da2e3ebdSchin 				case 'L':
1311da2e3ebdSchin 					t1 = "lex ";
1312da2e3ebdSchin 					mp->mime = "application/x-lex";
1313da2e3ebdSchin 					break;
1314da2e3ebdSchin 				default:
1315da2e3ebdSchin 					t3 = T("header");
1316da2e3ebdSchin 					if (mp->identifier[ID_YACC] < 5 || mp->count['%'] < 5)
1317da2e3ebdSchin 					{
1318da2e3ebdSchin 						mp->mime = "application/x-cc";
1319da2e3ebdSchin 						break;
1320da2e3ebdSchin 					}
1321da2e3ebdSchin 					/*FALLTHROUGH*/
1322da2e3ebdSchin 				case 'y':
1323da2e3ebdSchin 				case 'Y':
1324da2e3ebdSchin 					t1 = "yacc ";
1325da2e3ebdSchin 					mp->mime = "application/x-yacc";
1326da2e3ebdSchin 					break;
1327da2e3ebdSchin 				}
1328da2e3ebdSchin 				if (mp->identifier[ID_CPLUSPLUS] >= 3)
1329da2e3ebdSchin 				{
1330da2e3ebdSchin 					t2 = "c++ ";
1331da2e3ebdSchin 					mp->mime = "application/x-c++";
1332da2e3ebdSchin 				}
1333da2e3ebdSchin 				sfsprintf(mp->sbuf, sizeof(mp->sbuf), "%s%s%s", t1, t2, t3);
1334da2e3ebdSchin 				s = mp->sbuf;
1335da2e3ebdSchin 				goto qualify;
1336da2e3ebdSchin 			}
1337da2e3ebdSchin 		}
1338da2e3ebdSchin 		if (mp->identifier[ID_MAM1] >= 2 && mp->identifier[ID_MAM3] >= 2 &&
1339da2e3ebdSchin 		    (mp->fbsz < SF_BUFSIZE && mp->identifier[ID_MAM1] == mp->identifier[ID_MAM2] ||
1340da2e3ebdSchin 		     mp->fbsz >= SF_BUFSIZE && mp->identifier[ID_MAM1] >= mp->identifier[ID_MAM2]))
1341da2e3ebdSchin 		{
1342da2e3ebdSchin 		id_mam:
1343da2e3ebdSchin 			s = T("mam program");
1344da2e3ebdSchin 			mp->mime = "application/x-mam";
1345da2e3ebdSchin 			goto qualify;
1346da2e3ebdSchin 		}
1347da2e3ebdSchin 		if (mp->identifier[ID_FORTRAN] >= 8)
1348da2e3ebdSchin 		{
1349da2e3ebdSchin 		id_fortran:
1350da2e3ebdSchin 			s = T("fortran program");
1351da2e3ebdSchin 			mp->mime = "application/x-fortran";
1352da2e3ebdSchin 			goto qualify;
1353da2e3ebdSchin 		}
1354da2e3ebdSchin 		if (mp->identifier[ID_HTML] > 0 && mp->count['<'] >= 8 && (c = mp->count['<'] - mp->count['>']) >= -2 && c <= 2)
1355da2e3ebdSchin 		{
1356da2e3ebdSchin 		id_html:
1357da2e3ebdSchin 			s = T("html input");
1358da2e3ebdSchin 			mp->mime = "text/html";
1359da2e3ebdSchin 			goto qualify;
1360da2e3ebdSchin 		}
1361da2e3ebdSchin 		if (mp->identifier[ID_COPYBOOK] > 0 && mp->identifier[ID_COBOL] == 0 && (c = mp->count['('] - mp->count[')']) >= -2 && c <= 2)
1362da2e3ebdSchin 		{
1363da2e3ebdSchin 		id_copybook:
1364da2e3ebdSchin 			s = T("cobol copybook");
1365da2e3ebdSchin 			mp->mime = "application/x-cobol";
1366da2e3ebdSchin 			goto qualify;
1367da2e3ebdSchin 		}
1368da2e3ebdSchin 		if (mp->identifier[ID_COBOL] > 0 && mp->identifier[ID_COPYBOOK] > 0 && (c = mp->count['('] - mp->count[')']) >= -2 && c <= 2)
1369da2e3ebdSchin 		{
1370da2e3ebdSchin 		id_cobol:
1371da2e3ebdSchin 			s = T("cobol program");
1372da2e3ebdSchin 			mp->mime = "application/x-cobol";
1373da2e3ebdSchin 			goto qualify;
1374da2e3ebdSchin 		}
1375da2e3ebdSchin 		if (mp->identifier[ID_PL1] > 0 && (c = mp->count['('] - mp->count[')']) >= -2 && c <= 2)
1376da2e3ebdSchin 		{
1377da2e3ebdSchin 		id_pl1:
1378da2e3ebdSchin 			s = T("pl1 program");
1379da2e3ebdSchin 			mp->mime = "application/x-pl1";
1380da2e3ebdSchin 			goto qualify;
1381da2e3ebdSchin 		}
1382da2e3ebdSchin 		if (mp->count['{'] >= 6 && (c = mp->count['{'] - mp->count['}']) >= -2 && c <= 2 && mp->count['\\'] >= mp->count['{'])
1383da2e3ebdSchin 		{
1384da2e3ebdSchin 		id_tex:
1385da2e3ebdSchin 			s = T("TeX input");
1386da2e3ebdSchin 			mp->mime = "text/tex";
1387da2e3ebdSchin 			goto qualify;
1388da2e3ebdSchin 		}
1389da2e3ebdSchin 		if (mp->identifier[ID_ASM] >= 4)
1390da2e3ebdSchin 		{
1391da2e3ebdSchin 		id_asm:
1392da2e3ebdSchin 			s = T("as program");
1393da2e3ebdSchin 			mp->mime = "application/x-as";
1394da2e3ebdSchin 			goto qualify;
1395da2e3ebdSchin 		}
1396da2e3ebdSchin 		if (ckenglish(mp, pun, badpun))
1397da2e3ebdSchin 		{
1398da2e3ebdSchin 			s = T("english text");
1399da2e3ebdSchin 			mp->mime = "text/plain";
1400da2e3ebdSchin 			goto qualify;
1401da2e3ebdSchin 		}
1402da2e3ebdSchin 	}
1403da2e3ebdSchin 	else if (streq(base, "core"))
1404da2e3ebdSchin 	{
1405da2e3ebdSchin 		mp->mime = "x-system/core";
1406da2e3ebdSchin 		return T("core dump");
1407da2e3ebdSchin 	}
1408da2e3ebdSchin 	if (flags & (CC_binary|CC_notext))
1409da2e3ebdSchin 	{
1410da2e3ebdSchin 		b = (unsigned char*)mp->fbuf;
1411da2e3ebdSchin 		e = b + mp->fbsz;
1412da2e3ebdSchin 		n = 0;
1413da2e3ebdSchin 		for (;;)
1414da2e3ebdSchin 		{
1415da2e3ebdSchin 			c = *b++;
1416da2e3ebdSchin 			q = 0;
1417da2e3ebdSchin 			while (c & 0x80)
1418da2e3ebdSchin 			{
1419da2e3ebdSchin 				c <<= 1;
1420da2e3ebdSchin 				q++;
1421da2e3ebdSchin 			}
1422da2e3ebdSchin 			switch (q)
1423da2e3ebdSchin 			{
1424da2e3ebdSchin 			case 4:
1425da2e3ebdSchin 				if (b < e && (*b++ & 0xc0) != 0x80)
1426da2e3ebdSchin 					break;
1427da2e3ebdSchin 			case 3:
1428da2e3ebdSchin 				if (b < e && (*b++ & 0xc0) != 0x80)
1429da2e3ebdSchin 					break;
1430da2e3ebdSchin 			case 2:
1431da2e3ebdSchin 				if (b < e && (*b++ & 0xc0) != 0x80)
1432da2e3ebdSchin 					break;
1433da2e3ebdSchin 				n = 1;
1434da2e3ebdSchin 			case 0:
1435da2e3ebdSchin 				if (b >= e)
1436da2e3ebdSchin 				{
1437da2e3ebdSchin 					if (n)
1438da2e3ebdSchin 					{
1439da2e3ebdSchin 						flags &= ~(CC_binary|CC_notext);
1440da2e3ebdSchin 						flags |= CC_utf_8;
1441da2e3ebdSchin 					}
1442da2e3ebdSchin 					break;
1443da2e3ebdSchin 				}
1444da2e3ebdSchin 				continue;
1445da2e3ebdSchin 			}
1446da2e3ebdSchin 			break;
1447da2e3ebdSchin 		}
1448da2e3ebdSchin 	}
1449da2e3ebdSchin 	if (flags & (CC_binary|CC_notext))
1450da2e3ebdSchin 	{
1451da2e3ebdSchin 		unsigned long	d = 0;
1452da2e3ebdSchin 
1453da2e3ebdSchin 		if ((q = mp->fbsz / UCHAR_MAX) >= 2)
1454da2e3ebdSchin 		{
1455da2e3ebdSchin 			/*
1456da2e3ebdSchin 			 * compression/encryption via standard deviation
1457da2e3ebdSchin 			 */
1458da2e3ebdSchin 
1459da2e3ebdSchin 
1460da2e3ebdSchin 			for (c = 0; c < UCHAR_MAX; c++)
1461da2e3ebdSchin 			{
1462da2e3ebdSchin 				pun = mp->count[c] - q;
1463da2e3ebdSchin 				d += pun * pun;
1464da2e3ebdSchin 			}
1465da2e3ebdSchin 			d /= mp->fbsz;
1466da2e3ebdSchin 		}
1467da2e3ebdSchin 		if (d <= 0)
1468da2e3ebdSchin 			s = T("binary");
1469da2e3ebdSchin 		else if (d < 4)
1470da2e3ebdSchin 			s = T("encrypted");
1471da2e3ebdSchin 		else if (d < 16)
1472da2e3ebdSchin 			s = T("packed");
1473da2e3ebdSchin 		else if (d < 64)
1474da2e3ebdSchin 			s = T("compressed");
1475da2e3ebdSchin 		else if (d < 256)
1476da2e3ebdSchin 			s = T("delta");
1477da2e3ebdSchin 		else
1478da2e3ebdSchin 			s = T("data");
1479da2e3ebdSchin 		mp->mime = "application/octet-stream";
1480da2e3ebdSchin 		return s;
1481da2e3ebdSchin 	}
1482da2e3ebdSchin 	mp->mime = "text/plain";
1483da2e3ebdSchin 	if (flags & CC_utf_8)
1484da2e3ebdSchin 		s = (flags & CC_control) ? T("utf-8 text with control characters") : T("utf-8 text");
1485da2e3ebdSchin 	else if (flags & CC_latin)
1486da2e3ebdSchin 		s = (flags & CC_control) ? T("latin text with control characters") : T("latin text");
1487da2e3ebdSchin 	else
1488da2e3ebdSchin 		s = (flags & CC_control) ? T("text with control characters") : T("text");
1489da2e3ebdSchin  qualify:
1490da2e3ebdSchin 	if (!flags && mp->count['\n'] >= mp->count['\r'] && mp->count['\n'] <= (mp->count['\r'] + 1) && mp->count['\r'])
1491da2e3ebdSchin 	{
1492da2e3ebdSchin 		t = "dos ";
1493da2e3ebdSchin 		mp->mime = "text/dos";
1494da2e3ebdSchin 	}
1495da2e3ebdSchin 	else
1496da2e3ebdSchin 		t = "";
1497da2e3ebdSchin 	if (code)
1498da2e3ebdSchin 	{
1499da2e3ebdSchin 		if (code == CC_ASCII)
1500da2e3ebdSchin 			sfsprintf(buf, PATH_MAX, "ascii %s%s", t, s);
1501da2e3ebdSchin 		else
1502da2e3ebdSchin 		{
1503da2e3ebdSchin 			sfsprintf(buf, PATH_MAX, "ebcdic%d %s%s", code - 1, t, s);
1504da2e3ebdSchin 			mp->mime = "text/ebcdic";
1505da2e3ebdSchin 		}
1506da2e3ebdSchin 		s = buf;
1507da2e3ebdSchin 	}
1508da2e3ebdSchin 	else if (*t)
1509da2e3ebdSchin 	{
1510da2e3ebdSchin 		sfsprintf(buf, PATH_MAX, "%s%s", t, s);
1511da2e3ebdSchin 		s = buf;
1512da2e3ebdSchin 	}
1513da2e3ebdSchin 	return s;
1514da2e3ebdSchin }
1515da2e3ebdSchin 
1516da2e3ebdSchin /*
1517da2e3ebdSchin  * return the basic magic string for file,st in buf,size
1518da2e3ebdSchin  */
1519da2e3ebdSchin 
1520da2e3ebdSchin static char*
type(register Magic_t * mp,const char * file,struct stat * st,char * buf,int size)1521da2e3ebdSchin type(register Magic_t* mp, const char* file, struct stat* st, char* buf, int size)
1522da2e3ebdSchin {
1523da2e3ebdSchin 	register char*	s;
1524da2e3ebdSchin 	register char*	t;
1525da2e3ebdSchin 
1526da2e3ebdSchin 	mp->mime = 0;
1527da2e3ebdSchin 	if (!S_ISREG(st->st_mode))
1528da2e3ebdSchin 	{
1529da2e3ebdSchin 		if (S_ISDIR(st->st_mode))
1530da2e3ebdSchin 		{
1531da2e3ebdSchin 			mp->mime = "x-system/dir";
1532da2e3ebdSchin 			return T("directory");
1533da2e3ebdSchin 		}
1534da2e3ebdSchin 		if (S_ISLNK(st->st_mode))
1535da2e3ebdSchin 		{
1536da2e3ebdSchin 			mp->mime = "x-system/lnk";
1537da2e3ebdSchin 			s = buf;
1538da2e3ebdSchin 			s += sfsprintf(s, PATH_MAX, T("symbolic link to "));
1539da2e3ebdSchin 			if (pathgetlink(file, s, size - (s - buf)) < 0)
1540da2e3ebdSchin 				return T("cannot read symbolic link text");
1541da2e3ebdSchin 			return buf;
1542da2e3ebdSchin 		}
1543da2e3ebdSchin 		if (S_ISBLK(st->st_mode))
1544da2e3ebdSchin 		{
1545da2e3ebdSchin 			mp->mime = "x-system/blk";
1546da2e3ebdSchin 			sfsprintf(buf, PATH_MAX, T("block special (%s)"), fmtdev(st));
1547da2e3ebdSchin 			return buf;
1548da2e3ebdSchin 		}
1549da2e3ebdSchin 		if (S_ISCHR(st->st_mode))
1550da2e3ebdSchin 		{
1551da2e3ebdSchin 			mp->mime = "x-system/chr";
1552da2e3ebdSchin 			sfsprintf(buf, PATH_MAX, T("character special (%s)"), fmtdev(st));
1553da2e3ebdSchin 			return buf;
1554da2e3ebdSchin 		}
1555da2e3ebdSchin 		if (S_ISFIFO(st->st_mode))
1556da2e3ebdSchin 		{
1557da2e3ebdSchin 			mp->mime = "x-system/fifo";
1558da2e3ebdSchin 			return "fifo";
1559da2e3ebdSchin 		}
1560da2e3ebdSchin #ifdef S_ISSOCK
1561da2e3ebdSchin 		if (S_ISSOCK(st->st_mode))
1562da2e3ebdSchin 		{
1563da2e3ebdSchin 			mp->mime = "x-system/sock";
1564da2e3ebdSchin 			return "socket";
1565da2e3ebdSchin 		}
1566da2e3ebdSchin #endif
1567da2e3ebdSchin 	}
1568da2e3ebdSchin 	if (!(mp->fbmx = st->st_size))
1569da2e3ebdSchin 		s = T("empty");
1570da2e3ebdSchin 	else if (!mp->fp)
1571da2e3ebdSchin 		s = T("cannot read");
1572da2e3ebdSchin 	else
1573da2e3ebdSchin 	{
1574da2e3ebdSchin 		mp->fbsz = sfread(mp->fp, mp->fbuf, sizeof(mp->fbuf) - 1);
1575da2e3ebdSchin 		if (mp->fbsz < 0)
1576da2e3ebdSchin 			s = fmterror(errno);
1577da2e3ebdSchin 		else if (mp->fbsz == 0)
1578da2e3ebdSchin 			s = T("empty");
1579da2e3ebdSchin 		else
1580da2e3ebdSchin 		{
1581da2e3ebdSchin 			mp->fbuf[mp->fbsz] = 0;
1582da2e3ebdSchin 			mp->xoff = 0;
1583da2e3ebdSchin 			mp->xbsz = 0;
1584da2e3ebdSchin 			if (!(s = ckmagic(mp, file, buf, st, 0)))
1585da2e3ebdSchin 				s = cklang(mp, file, buf, st);
1586da2e3ebdSchin 		}
1587da2e3ebdSchin 	}
1588da2e3ebdSchin 	if (!mp->mime)
1589da2e3ebdSchin 		mp->mime = "application/unknown";
1590da2e3ebdSchin 	else if ((t = strchr(mp->mime, '%')) && *(t + 1) == 's' && !*(t + 2))
1591da2e3ebdSchin 	{
1592da2e3ebdSchin 		register char*	b;
1593da2e3ebdSchin 		register char*	be;
1594da2e3ebdSchin 		register char*	m;
1595da2e3ebdSchin 		register char*	me;
1596da2e3ebdSchin 
1597da2e3ebdSchin 		b = mp->mime;
1598da2e3ebdSchin 		me = (m = mp->mime = mp->fbuf) + sizeof(mp->fbuf) - 1;
1599da2e3ebdSchin 		while (m < me && b < t)
1600da2e3ebdSchin 			*m++ = *b++;
1601da2e3ebdSchin 		b = t = s;
1602da2e3ebdSchin 		for (;;)
1603da2e3ebdSchin 		{
1604da2e3ebdSchin 			if (!(be = strchr(t, ' ')))
1605da2e3ebdSchin 			{
1606da2e3ebdSchin 				be = b + strlen(b);
1607da2e3ebdSchin 				break;
1608da2e3ebdSchin 			}
1609da2e3ebdSchin 			if (*(be - 1) == ',' || strneq(be + 1, "data", 4) || strneq(be + 1, "file", 4))
1610da2e3ebdSchin 				break;
1611da2e3ebdSchin 			b = t;
1612da2e3ebdSchin 			t = be + 1;
1613da2e3ebdSchin 		}
1614da2e3ebdSchin 		while (m < me && b < be)
1615da2e3ebdSchin 			if ((*m++ = *b++) == ' ')
1616da2e3ebdSchin 				*(m - 1) = '-';
1617da2e3ebdSchin 		*m = 0;
1618da2e3ebdSchin 	}
1619da2e3ebdSchin 	return s;
1620da2e3ebdSchin }
1621da2e3ebdSchin 
1622da2e3ebdSchin /*
1623da2e3ebdSchin  * low level for magicload()
1624da2e3ebdSchin  */
1625da2e3ebdSchin 
1626da2e3ebdSchin static int
load(register Magic_t * mp,char * file,register Sfio_t * fp)1627da2e3ebdSchin load(register Magic_t* mp, char* file, register Sfio_t* fp)
1628da2e3ebdSchin {
1629da2e3ebdSchin 	register Entry_t*	ep;
1630da2e3ebdSchin 	register char*		p;
1631da2e3ebdSchin 	register char*		p2;
1632da2e3ebdSchin 	char*			p3;
1633da2e3ebdSchin 	char*			next;
1634da2e3ebdSchin 	int			n;
1635da2e3ebdSchin 	int			lge;
1636da2e3ebdSchin 	int			lev;
1637da2e3ebdSchin 	int			ent;
1638da2e3ebdSchin 	int			old;
1639da2e3ebdSchin 	int			cont;
1640da2e3ebdSchin 	Info_t*			ip;
1641da2e3ebdSchin 	Entry_t*		ret;
1642da2e3ebdSchin 	Entry_t*		first;
1643da2e3ebdSchin 	Entry_t*		last = 0;
1644da2e3ebdSchin 	Entry_t*		fun['z' - 'a' + 1];
1645da2e3ebdSchin 
1646da2e3ebdSchin 	memzero(fun, sizeof(fun));
1647da2e3ebdSchin 	cont = '$';
1648da2e3ebdSchin 	ent = 0;
1649da2e3ebdSchin 	lev = 0;
1650da2e3ebdSchin 	old = 0;
1651da2e3ebdSchin 	ret = 0;
1652da2e3ebdSchin 	error_info.file = file;
1653da2e3ebdSchin 	error_info.line = 0;
1654da2e3ebdSchin 	first = ep = vmnewof(mp->vm, 0, Entry_t, 1, 0);
1655da2e3ebdSchin 	while (p = sfgetr(fp, '\n', 1))
1656da2e3ebdSchin 	{
1657da2e3ebdSchin 		error_info.line++;
1658da2e3ebdSchin 		for (; isspace(*p); p++);
1659da2e3ebdSchin 
1660da2e3ebdSchin 		/*
1661da2e3ebdSchin 		 * nesting
1662da2e3ebdSchin 		 */
1663da2e3ebdSchin 
1664da2e3ebdSchin 		switch (*p)
1665da2e3ebdSchin 		{
1666da2e3ebdSchin 		case 0:
1667da2e3ebdSchin 		case '#':
1668da2e3ebdSchin 			cont = '#';
1669da2e3ebdSchin 			continue;
1670da2e3ebdSchin 		case '{':
1671da2e3ebdSchin 			if (++lev < MAXNEST)
1672da2e3ebdSchin 				ep->nest = *p;
1673da2e3ebdSchin 			else if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
1674da2e3ebdSchin 				(*mp->disc->errorf)(mp, mp->disc, 1, "{ ... } operator nesting too deep -- %d max", MAXNEST);
1675da2e3ebdSchin 			continue;
1676da2e3ebdSchin 		case '}':
1677da2e3ebdSchin 			if (!last || lev <= 0)
1678da2e3ebdSchin 			{
1679da2e3ebdSchin 				if (mp->disc->errorf)
1680da2e3ebdSchin 					(*mp->disc->errorf)(mp, mp->disc, 2, "`%c': invalid nesting", *p);
1681da2e3ebdSchin 			}
1682da2e3ebdSchin 			else if (lev-- == ent)
1683da2e3ebdSchin 			{
1684da2e3ebdSchin 				ent = 0;
1685da2e3ebdSchin 				ep->cont = ':';
1686da2e3ebdSchin 				ep->offset = ret->offset;
1687da2e3ebdSchin 				ep->nest = ' ';
1688da2e3ebdSchin 				ep->type = ' ';
1689da2e3ebdSchin 				ep->op = ' ';
1690da2e3ebdSchin 				ep->desc = "[RETURN]";
1691da2e3ebdSchin 				last = ep;
1692da2e3ebdSchin 				ep = ret->next = vmnewof(mp->vm, 0, Entry_t, 1, 0);
1693da2e3ebdSchin 				ret = 0;
1694da2e3ebdSchin 			}
1695da2e3ebdSchin 			else
1696da2e3ebdSchin 				last->nest = *p;
1697da2e3ebdSchin 			continue;
1698da2e3ebdSchin 		default:
1699da2e3ebdSchin 			if (*(p + 1) == '{' || *(p + 1) == '(' && *p != '+' && *p != '>' && *p != '&' && *p != '|')
1700da2e3ebdSchin 			{
1701da2e3ebdSchin 				n = *p++;
1702da2e3ebdSchin 				if (n >= 'a' && n <= 'z')
1703da2e3ebdSchin 					n -= 'a';
1704da2e3ebdSchin 				else
1705da2e3ebdSchin 				{
1706da2e3ebdSchin 					if (mp->disc->errorf)
1707da2e3ebdSchin 						(*mp->disc->errorf)(mp, mp->disc, 2, "%c: invalid function name", n);
1708da2e3ebdSchin 					n = 0;
1709da2e3ebdSchin 				}
1710da2e3ebdSchin 				if (ret && mp->disc->errorf)
1711da2e3ebdSchin 					(*mp->disc->errorf)(mp, mp->disc, 2, "%c: function has no return", ret->offset + 'a');
1712da2e3ebdSchin 				if (*p == '{')
1713da2e3ebdSchin 				{
1714da2e3ebdSchin 					ent = ++lev;
1715da2e3ebdSchin 					ret = ep;
1716da2e3ebdSchin 					ep->desc = "[FUNCTION]";
1717da2e3ebdSchin 				}
1718da2e3ebdSchin 				else
1719da2e3ebdSchin 				{
1720da2e3ebdSchin 					if (*(p + 1) != ')' && mp->disc->errorf)
1721da2e3ebdSchin 						(*mp->disc->errorf)(mp, mp->disc, 2, "%c: invalid function call argument list", n + 'a');
1722da2e3ebdSchin 					ep->desc = "[CALL]";
1723da2e3ebdSchin 				}
1724da2e3ebdSchin 				ep->cont = cont;
1725da2e3ebdSchin 				ep->offset = n;
1726da2e3ebdSchin 				ep->nest = ' ';
1727da2e3ebdSchin 				ep->type = ' ';
1728da2e3ebdSchin 				ep->op = ' ';
1729da2e3ebdSchin 				last = ep;
1730da2e3ebdSchin 				ep = ep->next = vmnewof(mp->vm, 0, Entry_t, 1, 0);
1731da2e3ebdSchin 				if (ret)
1732da2e3ebdSchin 					fun[n] = last->value.lab = ep;
1733da2e3ebdSchin 				else if (!(last->value.lab = fun[n]) && mp->disc->errorf)
1734da2e3ebdSchin 					(*mp->disc->errorf)(mp, mp->disc, 2, "%c: function not defined", n + 'a');
1735da2e3ebdSchin 				continue;
1736da2e3ebdSchin 			}
1737da2e3ebdSchin 			if (!ep->nest)
1738da2e3ebdSchin 				ep->nest = (lev > 0 && lev != ent) ? ('0' + lev - !!ent) : ' ';
1739da2e3ebdSchin 			break;
1740da2e3ebdSchin 		}
1741da2e3ebdSchin 
1742da2e3ebdSchin 		/*
1743da2e3ebdSchin 		 * continuation
1744da2e3ebdSchin 		 */
1745da2e3ebdSchin 
1746da2e3ebdSchin 		cont = '$';
1747da2e3ebdSchin 		switch (*p)
1748da2e3ebdSchin 		{
1749da2e3ebdSchin 		case '>':
1750da2e3ebdSchin 			old = 1;
1751da2e3ebdSchin 			if (*(p + 1) == *p)
1752da2e3ebdSchin 			{
1753da2e3ebdSchin 				/*
1754da2e3ebdSchin 				 * old style nesting push
1755da2e3ebdSchin 				 */
1756da2e3ebdSchin 
1757da2e3ebdSchin 				p++;
1758da2e3ebdSchin 				old = 2;
1759da2e3ebdSchin 				if (!lev && last)
1760da2e3ebdSchin 				{
1761da2e3ebdSchin 					lev = 1;
1762da2e3ebdSchin 					last->nest = '{';
1763da2e3ebdSchin 					if (last->cont == '>')
1764da2e3ebdSchin 						last->cont = '&';
1765da2e3ebdSchin 					ep->nest = '1';
1766da2e3ebdSchin 				}
1767da2e3ebdSchin 			}
1768da2e3ebdSchin 			/*FALLTHROUGH*/
1769da2e3ebdSchin 		case '+':
1770da2e3ebdSchin 		case '&':
1771da2e3ebdSchin 		case '|':
1772da2e3ebdSchin 			ep->cont = *p++;
1773da2e3ebdSchin 			break;
1774da2e3ebdSchin 		default:
1775da2e3ebdSchin 			if ((mp->flags & MAGIC_VERBOSE) && !isalpha(*p) && mp->disc->errorf)
1776da2e3ebdSchin 				(*mp->disc->errorf)(mp, mp->disc, 1, "`%c': invalid line continuation operator", *p);
1777da2e3ebdSchin 			/*FALLTHROUGH*/
1778da2e3ebdSchin 		case '*':
1779da2e3ebdSchin 		case '0': case '1': case '2': case '3': case '4':
1780da2e3ebdSchin 		case '5': case '6': case '7': case '8': case '9':
1781da2e3ebdSchin 			ep->cont = (lev > 0) ? '&' : '#';
1782da2e3ebdSchin 			break;
1783da2e3ebdSchin 		}
1784da2e3ebdSchin 		switch (old)
1785da2e3ebdSchin 		{
1786da2e3ebdSchin 		case 1:
1787da2e3ebdSchin 			old = 0;
1788da2e3ebdSchin 			if (lev)
1789da2e3ebdSchin 			{
1790da2e3ebdSchin 				/*
1791da2e3ebdSchin 				 * old style nesting pop
1792da2e3ebdSchin 				 */
1793da2e3ebdSchin 
1794da2e3ebdSchin 				lev = 0;
1795da2e3ebdSchin 				if (last)
1796da2e3ebdSchin 					last->nest = '}';
1797da2e3ebdSchin 				ep->nest = ' ';
1798da2e3ebdSchin 				if (ep->cont == '&')
1799da2e3ebdSchin 					ep->cont = '#';
1800da2e3ebdSchin 			}
1801da2e3ebdSchin 			break;
1802da2e3ebdSchin 		case 2:
1803da2e3ebdSchin 			old = 1;
1804da2e3ebdSchin 			break;
1805da2e3ebdSchin 		}
1806da2e3ebdSchin 		if (isdigit(*p))
1807da2e3ebdSchin 		{
1808da2e3ebdSchin 			/*
1809da2e3ebdSchin 			 * absolute offset
1810da2e3ebdSchin 			 */
1811da2e3ebdSchin 
1812da2e3ebdSchin 			ep->offset = strton(p, &next, NiL, 0);
1813da2e3ebdSchin 			p2 = next;
1814da2e3ebdSchin 		}
1815da2e3ebdSchin 		else
1816da2e3ebdSchin 		{
1817da2e3ebdSchin 			for (p2 = p; *p2 && !isspace(*p2); p2++);
1818da2e3ebdSchin 			if (!*p2)
1819da2e3ebdSchin 			{
1820da2e3ebdSchin 				if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
1821da2e3ebdSchin 					(*mp->disc->errorf)(mp, mp->disc, 1, "not enough fields: `%s'", p);
1822da2e3ebdSchin 				continue;
1823da2e3ebdSchin 			}
1824da2e3ebdSchin 
1825da2e3ebdSchin 			/*
1826da2e3ebdSchin 			 * offset expression
1827da2e3ebdSchin 			 */
1828da2e3ebdSchin 
1829da2e3ebdSchin 			*p2++ = 0;
1830da2e3ebdSchin 			ep->expr = vmstrdup(mp->vm, p);
1831da2e3ebdSchin 			if (isalpha(*p))
1832da2e3ebdSchin 				ep->offset = (ip = (Info_t*)dtmatch(mp->infotab, p)) ? ip->value : 0;
1833da2e3ebdSchin 			else if (*p == '(' && ep->cont == '>')
1834da2e3ebdSchin 			{
1835da2e3ebdSchin 				/*
1836da2e3ebdSchin 				 * convert old style indirection to @
1837da2e3ebdSchin 				 */
1838da2e3ebdSchin 
1839da2e3ebdSchin 				p = ep->expr + 1;
1840da2e3ebdSchin 				for (;;)
1841da2e3ebdSchin 				{
1842da2e3ebdSchin 					switch (*p++)
1843da2e3ebdSchin 					{
1844da2e3ebdSchin 					case 0:
1845da2e3ebdSchin 					case '@':
1846da2e3ebdSchin 					case '(':
1847da2e3ebdSchin 						break;
1848da2e3ebdSchin 					case ')':
1849da2e3ebdSchin 						break;
1850da2e3ebdSchin 					default:
1851da2e3ebdSchin 						continue;
1852da2e3ebdSchin 					}
1853da2e3ebdSchin 					break;
1854da2e3ebdSchin 				}
1855da2e3ebdSchin 				if (*--p == ')')
1856da2e3ebdSchin 				{
1857da2e3ebdSchin 					*p = 0;
1858da2e3ebdSchin 					*ep->expr = '@';
1859da2e3ebdSchin 				}
1860da2e3ebdSchin 			}
1861da2e3ebdSchin 		}
1862da2e3ebdSchin 		for (; isspace(*p2); p2++);
1863da2e3ebdSchin 		for (p = p2; *p2 && !isspace(*p2); p2++);
1864da2e3ebdSchin 		if (!*p2)
1865da2e3ebdSchin 		{
1866da2e3ebdSchin 			if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
1867da2e3ebdSchin 				(*mp->disc->errorf)(mp, mp->disc, 1, "not enough fields: `%s'", p);
1868da2e3ebdSchin 			continue;
1869da2e3ebdSchin 		}
1870da2e3ebdSchin 		*p2++ = 0;
1871da2e3ebdSchin 
1872da2e3ebdSchin 		/*
1873da2e3ebdSchin 		 * type
1874da2e3ebdSchin 		 */
1875da2e3ebdSchin 
1876da2e3ebdSchin 		if ((*p == 'b' || *p == 'l') && *(p + 1) == 'e')
1877da2e3ebdSchin 		{
1878da2e3ebdSchin 			ep->swap = ~(*p == 'l' ? 7 : 0);
1879da2e3ebdSchin 			p += 2;
1880da2e3ebdSchin 		}
1881da2e3ebdSchin 		if (*p == 's')
1882da2e3ebdSchin 		{
1883da2e3ebdSchin 			if (*(p + 1) == 'h')
1884da2e3ebdSchin 				ep->type = 'h';
1885da2e3ebdSchin 			else
1886da2e3ebdSchin 				ep->type = 's';
1887da2e3ebdSchin 		}
1888da2e3ebdSchin 		else if (*p == 'a')
1889da2e3ebdSchin 			ep->type = 's';
1890da2e3ebdSchin 		else
1891da2e3ebdSchin 			ep->type = *p;
1892da2e3ebdSchin 		if (p = strchr(p, '&'))
1893da2e3ebdSchin 		{
1894da2e3ebdSchin 			/*
1895da2e3ebdSchin 			 * old style mask
1896da2e3ebdSchin 			 */
1897da2e3ebdSchin 
1898da2e3ebdSchin 			ep->mask = strton(++p, NiL, NiL, 0);
1899da2e3ebdSchin 		}
1900da2e3ebdSchin 		for (; isspace(*p2); p2++);
1901da2e3ebdSchin 		if (ep->mask)
1902da2e3ebdSchin 			*--p2 = '=';
1903da2e3ebdSchin 
1904da2e3ebdSchin 		/*
1905da2e3ebdSchin 		 * comparison operation
1906da2e3ebdSchin 		 */
1907da2e3ebdSchin 
1908da2e3ebdSchin 		p = p2;
1909da2e3ebdSchin 		if (p2 = strchr(p, '\t'))
1910da2e3ebdSchin 			*p2++ = 0;
1911da2e3ebdSchin 		else
1912da2e3ebdSchin 		{
1913da2e3ebdSchin 			int	qe = 0;
1914da2e3ebdSchin 			int	qn = 0;
1915da2e3ebdSchin 
1916da2e3ebdSchin 			/*
1917da2e3ebdSchin 			 * assume balanced {}[]()\\""'' field
1918da2e3ebdSchin 			 */
1919da2e3ebdSchin 
1920da2e3ebdSchin 			for (p2 = p;;)
1921da2e3ebdSchin 			{
1922da2e3ebdSchin 				switch (n = *p2++)
1923da2e3ebdSchin 				{
1924da2e3ebdSchin 				case 0:
1925da2e3ebdSchin 					break;
1926da2e3ebdSchin 				case '{':
1927da2e3ebdSchin 					if (!qe)
1928da2e3ebdSchin 						qe = '}';
1929da2e3ebdSchin 					if (qe == '}')
1930da2e3ebdSchin 						qn++;
1931da2e3ebdSchin 					continue;
1932da2e3ebdSchin 				case '(':
1933da2e3ebdSchin 					if (!qe)
1934da2e3ebdSchin 						qe = ')';
1935da2e3ebdSchin 					if (qe == ')')
1936da2e3ebdSchin 						qn++;
1937da2e3ebdSchin 					continue;
1938da2e3ebdSchin 				case '[':
1939da2e3ebdSchin 					if (!qe)
1940da2e3ebdSchin 						qe = ']';
1941da2e3ebdSchin 					if (qe == ']')
1942da2e3ebdSchin 						qn++;
1943da2e3ebdSchin 					continue;
1944da2e3ebdSchin 				case '}':
1945da2e3ebdSchin 				case ')':
1946da2e3ebdSchin 				case ']':
1947da2e3ebdSchin 					if (qe == n && qn > 0)
1948da2e3ebdSchin 						qn--;
1949da2e3ebdSchin 					continue;
1950da2e3ebdSchin 				case '"':
1951da2e3ebdSchin 				case '\'':
1952da2e3ebdSchin 					if (!qe)
1953da2e3ebdSchin 						qe = n;
1954da2e3ebdSchin 					else if (qe == n)
1955da2e3ebdSchin 						qe = 0;
1956da2e3ebdSchin 					continue;
1957da2e3ebdSchin 				case '\\':
1958da2e3ebdSchin 					if (*p2)
1959da2e3ebdSchin 						p2++;
1960da2e3ebdSchin 					continue;
1961da2e3ebdSchin 				default:
1962da2e3ebdSchin 					if (!qe && isspace(n))
1963da2e3ebdSchin 						break;
1964da2e3ebdSchin 					continue;
1965da2e3ebdSchin 				}
1966da2e3ebdSchin 				if (n)
1967da2e3ebdSchin 					*(p2 - 1) = 0;
1968da2e3ebdSchin 				else
1969da2e3ebdSchin 					p2--;
1970da2e3ebdSchin 				break;
1971da2e3ebdSchin 			}
1972da2e3ebdSchin 		}
1973da2e3ebdSchin 		lge = 0;
1974da2e3ebdSchin 		if (ep->type == 'e' || ep->type == 'm' || ep->type == 's')
1975da2e3ebdSchin 			ep->op = '=';
1976da2e3ebdSchin 		else
1977da2e3ebdSchin 		{
1978da2e3ebdSchin 			if (*p == '&')
1979da2e3ebdSchin 			{
1980da2e3ebdSchin 				ep->mask = strton(++p, &next, NiL, 0);
1981da2e3ebdSchin 				p = next;
1982da2e3ebdSchin 			}
1983da2e3ebdSchin 			switch (*p)
1984da2e3ebdSchin 			{
1985da2e3ebdSchin 			case '=':
1986da2e3ebdSchin 			case '>':
1987da2e3ebdSchin 			case '<':
1988da2e3ebdSchin 			case '*':
1989da2e3ebdSchin 				ep->op = *p++;
1990da2e3ebdSchin 				if (*p == '=')
1991da2e3ebdSchin 				{
1992da2e3ebdSchin 					p++;
1993da2e3ebdSchin 					switch (ep->op)
1994da2e3ebdSchin 					{
1995da2e3ebdSchin 					case '>':
1996da2e3ebdSchin 						lge = -1;
1997da2e3ebdSchin 						break;
1998da2e3ebdSchin 					case '<':
1999da2e3ebdSchin 						lge = 1;
2000da2e3ebdSchin 						break;
2001da2e3ebdSchin 					}
2002da2e3ebdSchin 				}
2003da2e3ebdSchin 				break;
2004da2e3ebdSchin 			case '!':
2005da2e3ebdSchin 			case '@':
2006da2e3ebdSchin 				ep->op = *p++;
2007da2e3ebdSchin 				if (*p == '=')
2008da2e3ebdSchin 					p++;
2009da2e3ebdSchin 				break;
2010da2e3ebdSchin 			case 'x':
2011da2e3ebdSchin 				p++;
2012da2e3ebdSchin 				ep->op = '*';
2013da2e3ebdSchin 				break;
2014da2e3ebdSchin 			default:
2015da2e3ebdSchin 				ep->op = '=';
2016da2e3ebdSchin 				if (ep->mask)
2017da2e3ebdSchin 					ep->value.num = ep->mask;
2018da2e3ebdSchin 				break;
2019da2e3ebdSchin 			}
2020da2e3ebdSchin 		}
2021da2e3ebdSchin 		if (ep->op != '*' && !ep->value.num)
2022da2e3ebdSchin 		{
2023da2e3ebdSchin 			if (ep->type == 'e')
2024da2e3ebdSchin 			{
2025da2e3ebdSchin 				if (ep->value.sub = vmnewof(mp->vm, 0, regex_t, 1, 0))
2026da2e3ebdSchin 				{
2027da2e3ebdSchin 					ep->value.sub->re_disc = &mp->redisc;
2028da2e3ebdSchin 					if (!(n = regcomp(ep->value.sub, p, REG_DELIMITED|REG_LENIENT|REG_NULL|REG_DISCIPLINE)))
2029da2e3ebdSchin 					{
2030da2e3ebdSchin 						p += ep->value.sub->re_npat;
2031da2e3ebdSchin 						if (!(n = regsubcomp(ep->value.sub, p, NiL, 0, 0)))
2032da2e3ebdSchin 							p += ep->value.sub->re_npat;
2033da2e3ebdSchin 					}
2034da2e3ebdSchin 					if (n)
2035da2e3ebdSchin 					{
2036da2e3ebdSchin 						regmessage(mp, ep->value.sub, n);
2037da2e3ebdSchin 						ep->value.sub = 0;
2038da2e3ebdSchin 					}
2039da2e3ebdSchin 					else if (*p && mp->disc->errorf)
2040da2e3ebdSchin 						(*mp->disc->errorf)(mp, mp->disc, 1, "invalid characters after substitution: %s", p);
2041da2e3ebdSchin 				}
2042da2e3ebdSchin 			}
2043da2e3ebdSchin 			else if (ep->type == 'm')
2044da2e3ebdSchin 			{
2045da2e3ebdSchin 				ep->mask = stresc(p) + 1;
2046da2e3ebdSchin 				ep->value.str = vmnewof(mp->vm, 0, char, ep->mask + 1, 0);
2047da2e3ebdSchin 				memcpy(ep->value.str, p, ep->mask);
2048da2e3ebdSchin 				if ((!ep->expr || !ep->offset) && !strmatch(ep->value.str, "\\!\\(*\\)"))
2049da2e3ebdSchin 					ep->value.str[ep->mask - 1] = '*';
2050da2e3ebdSchin 			}
2051da2e3ebdSchin 			else if (ep->type == 's')
2052da2e3ebdSchin 			{
2053da2e3ebdSchin 				ep->mask = stresc(p);
2054da2e3ebdSchin 				ep->value.str = vmnewof(mp->vm, 0, char, ep->mask, 0);
2055da2e3ebdSchin 				memcpy(ep->value.str, p, ep->mask);
2056da2e3ebdSchin 			}
2057da2e3ebdSchin 			else if (*p == '\'')
2058da2e3ebdSchin 			{
2059da2e3ebdSchin 				stresc(p);
2060da2e3ebdSchin 				ep->value.num = *(unsigned char*)(p + 1) + lge;
2061da2e3ebdSchin 			}
2062da2e3ebdSchin 			else if (strmatch(p, "+([a-z])\\(*\\)"))
2063da2e3ebdSchin 			{
2064da2e3ebdSchin 				char*	t;
2065da2e3ebdSchin 
2066da2e3ebdSchin 				t = p;
2067da2e3ebdSchin 				ep->type = 'V';
2068da2e3ebdSchin 				ep->op = *p;
2069da2e3ebdSchin 				while (*p && *p++ != '(');
2070da2e3ebdSchin 				switch (ep->op)
2071da2e3ebdSchin 				{
2072da2e3ebdSchin 				case 'l':
2073da2e3ebdSchin 					n = *p++;
2074da2e3ebdSchin 					if (n < 'a' || n > 'z')
2075da2e3ebdSchin 					{
2076da2e3ebdSchin 						if (mp->disc->errorf)
2077da2e3ebdSchin 							(*mp->disc->errorf)(mp, mp->disc, 2, "%c: invalid function name", n);
2078da2e3ebdSchin 					}
2079da2e3ebdSchin 					else if (!fun[n -= 'a'])
2080da2e3ebdSchin 					{
2081da2e3ebdSchin 						if (mp->disc->errorf)
2082da2e3ebdSchin 							(*mp->disc->errorf)(mp, mp->disc, 2, "%c: function not defined", n + 'a');
2083da2e3ebdSchin 					}
2084da2e3ebdSchin 					else
2085da2e3ebdSchin 					{
2086da2e3ebdSchin 						ep->value.loop = vmnewof(mp->vm, 0, Loop_t, 1, 0);
2087da2e3ebdSchin 						ep->value.loop->lab = fun[n];
2088da2e3ebdSchin 						while (*p && *p++ != ',');
2089da2e3ebdSchin 						ep->value.loop->start = strton(p, &t, NiL, 0);
2090da2e3ebdSchin 						while (*t && *t++ != ',');
2091da2e3ebdSchin 						ep->value.loop->size = strton(t, &t, NiL, 0);
2092da2e3ebdSchin 					}
2093da2e3ebdSchin 					break;
2094da2e3ebdSchin 				case 'm':
2095da2e3ebdSchin 				case 'r':
2096da2e3ebdSchin 					ep->desc = vmnewof(mp->vm, 0, char, 32, 0);
2097da2e3ebdSchin 					ep->mime = vmnewof(mp->vm, 0, char, 32, 0);
2098da2e3ebdSchin 					break;
2099da2e3ebdSchin 				case 'v':
2100da2e3ebdSchin 					break;
2101da2e3ebdSchin 				default:
2102da2e3ebdSchin 					if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
2103da2e3ebdSchin 						(*mp->disc->errorf)(mp, mp->disc, 1, "%-.*s: unknown function", p - t, t);
2104da2e3ebdSchin 					break;
2105da2e3ebdSchin 				}
2106da2e3ebdSchin 			}
2107da2e3ebdSchin 			else
2108da2e3ebdSchin 			{
2109da2e3ebdSchin 				ep->value.num = strton(p, NiL, NiL, 0) + lge;
2110da2e3ebdSchin 				if (ep->op == '@')
2111da2e3ebdSchin 					ep->value.num = swapget(0, (char*)&ep->value.num, sizeof(ep->value.num));
2112da2e3ebdSchin 			}
2113da2e3ebdSchin 		}
2114da2e3ebdSchin 
2115da2e3ebdSchin 		/*
2116da2e3ebdSchin 		 * file description
2117da2e3ebdSchin 		 */
2118da2e3ebdSchin 
2119da2e3ebdSchin 		if (p2)
2120da2e3ebdSchin 		{
2121da2e3ebdSchin 			for (; isspace(*p2); p2++);
2122da2e3ebdSchin 			if (p = strchr(p2, '\t'))
2123da2e3ebdSchin 			{
2124da2e3ebdSchin 				/*
2125da2e3ebdSchin 				 * check for message catalog index
2126da2e3ebdSchin 				 */
2127da2e3ebdSchin 
2128da2e3ebdSchin 				*p++ = 0;
2129da2e3ebdSchin 				if (isalpha(*p2))
2130da2e3ebdSchin 				{
2131da2e3ebdSchin 					for (p3 = p2; isalnum(*p3); p3++);
2132da2e3ebdSchin 					if (*p3++ == ':')
2133da2e3ebdSchin 					{
2134da2e3ebdSchin 						for (; isdigit(*p3); p3++);
2135da2e3ebdSchin 						if (!*p3)
2136da2e3ebdSchin 						{
2137da2e3ebdSchin 							for (p2 = p; isspace(*p2); p2++);
2138da2e3ebdSchin 							if (p = strchr(p2, '\t'))
2139da2e3ebdSchin 								*p++ = 0;
2140da2e3ebdSchin 						}
2141da2e3ebdSchin 					}
2142da2e3ebdSchin 				}
2143da2e3ebdSchin 			}
2144da2e3ebdSchin 			stresc(p2);
2145da2e3ebdSchin 			ep->desc = vmstrdup(mp->vm, p2);
2146da2e3ebdSchin 			if (p)
2147da2e3ebdSchin 			{
2148da2e3ebdSchin 				for (; isspace(*p); p++);
2149da2e3ebdSchin 				if (*p)
2150da2e3ebdSchin 					ep->mime = vmstrdup(mp->vm, p);
2151da2e3ebdSchin 			}
2152da2e3ebdSchin 		}
2153da2e3ebdSchin 		else
2154da2e3ebdSchin 			ep->desc = "";
2155da2e3ebdSchin 
2156da2e3ebdSchin 		/*
2157da2e3ebdSchin 		 * get next entry
2158da2e3ebdSchin 		 */
2159da2e3ebdSchin 
2160da2e3ebdSchin 		last = ep;
2161da2e3ebdSchin 		ep = ep->next = vmnewof(mp->vm, 0, Entry_t, 1, 0);
2162da2e3ebdSchin 	}
2163da2e3ebdSchin 	if (last)
2164da2e3ebdSchin 	{
2165da2e3ebdSchin 		last->next = 0;
2166da2e3ebdSchin 		if (mp->magiclast)
2167da2e3ebdSchin 			mp->magiclast->next = first;
2168da2e3ebdSchin 		else
2169da2e3ebdSchin 			mp->magic = first;
2170da2e3ebdSchin 		mp->magiclast = last;
2171da2e3ebdSchin 	}
2172da2e3ebdSchin 	vmfree(mp->vm, ep);
2173da2e3ebdSchin 	if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
2174da2e3ebdSchin 	{
2175da2e3ebdSchin 		if (lev < 0)
2176da2e3ebdSchin 			(*mp->disc->errorf)(mp, mp->disc, 1, "too many } operators");
2177da2e3ebdSchin 		else if (lev > 0)
2178da2e3ebdSchin 			(*mp->disc->errorf)(mp, mp->disc, 1, "not enough } operators");
2179da2e3ebdSchin 		if (ret)
2180da2e3ebdSchin 			(*mp->disc->errorf)(mp, mp->disc, 2, "%c: function has no return", ret->offset + 'a');
2181da2e3ebdSchin 	}
2182da2e3ebdSchin 	error_info.file = 0;
2183da2e3ebdSchin 	error_info.line = 0;
2184da2e3ebdSchin 	return 0;
2185da2e3ebdSchin }
2186da2e3ebdSchin 
2187da2e3ebdSchin /*
2188da2e3ebdSchin  * load a magic file into mp
2189da2e3ebdSchin  */
2190da2e3ebdSchin 
2191da2e3ebdSchin int
magicload(register Magic_t * mp,const char * file,unsigned long flags)2192da2e3ebdSchin magicload(register Magic_t* mp, const char* file, unsigned long flags)
2193da2e3ebdSchin {
2194da2e3ebdSchin 	register char*		s;
2195da2e3ebdSchin 	register char*		e;
2196da2e3ebdSchin 	register char*		t;
2197da2e3ebdSchin 	int			n;
2198da2e3ebdSchin 	int			found;
2199da2e3ebdSchin 	int			list;
2200da2e3ebdSchin 	Sfio_t*			fp;
2201da2e3ebdSchin 
2202da2e3ebdSchin 	mp->flags = mp->disc->flags | flags;
2203da2e3ebdSchin 	found = 0;
2204da2e3ebdSchin 	if (list = !(s = (char*)file) || !*s || (*s == '-' || *s == '.') && !*(s + 1))
2205da2e3ebdSchin 	{
2206da2e3ebdSchin 		if (!(s = getenv(MAGIC_FILE_ENV)) || !*s)
2207da2e3ebdSchin 			s = MAGIC_FILE;
2208da2e3ebdSchin 	}
2209da2e3ebdSchin 	for (;;)
2210da2e3ebdSchin 	{
2211da2e3ebdSchin 		if (!list)
2212da2e3ebdSchin 			e = 0;
2213da2e3ebdSchin 		else if (e = strchr(s, ':'))
2214da2e3ebdSchin 		{
2215da2e3ebdSchin 			/*
2216da2e3ebdSchin 			 * ok, so ~ won't work for the last list element
2217da2e3ebdSchin 			 * we do it for MAGIC_FILES_ENV anyway
2218da2e3ebdSchin 			 */
2219da2e3ebdSchin 
2220da2e3ebdSchin 			if ((strneq(s, "~/", n = 2) || strneq(s, "$HOME/", n = 6) || strneq(s, "${HOME}/", n = 8)) && (t = getenv("HOME")))
2221da2e3ebdSchin 			{
2222da2e3ebdSchin 				sfputr(mp->tmp, t, -1);
2223da2e3ebdSchin 				s += n - 1;
2224da2e3ebdSchin 			}
2225da2e3ebdSchin 			sfwrite(mp->tmp, s, e - s);
2226da2e3ebdSchin 			if (!(s = sfstruse(mp->tmp)))
2227da2e3ebdSchin 				goto nospace;
2228da2e3ebdSchin 		}
2229da2e3ebdSchin 		if (!*s || streq(s, "-"))
2230da2e3ebdSchin 			s = MAGIC_FILE;
2231da2e3ebdSchin 		if (!(fp = sfopen(NiL, s, "r")))
2232da2e3ebdSchin 		{
2233da2e3ebdSchin 			if (list)
2234da2e3ebdSchin 			{
2235da2e3ebdSchin 				if (!(t = pathpath(mp->fbuf, s, "", PATH_REGULAR|PATH_READ)) && !strchr(s, '/'))
2236da2e3ebdSchin 				{
2237da2e3ebdSchin 					strcpy(mp->fbuf, s);
2238da2e3ebdSchin 					sfprintf(mp->tmp, "%s/%s", MAGIC_DIR, mp->fbuf);
2239da2e3ebdSchin 					if (!(s = sfstruse(mp->tmp)))
2240da2e3ebdSchin 						goto nospace;
2241da2e3ebdSchin 					if (!(t = pathpath(mp->fbuf, s, "", PATH_REGULAR|PATH_READ)))
2242da2e3ebdSchin 						goto next;
2243da2e3ebdSchin 				}
2244da2e3ebdSchin 				if (!(fp = sfopen(NiL, t, "r")))
2245da2e3ebdSchin 					goto next;
2246da2e3ebdSchin 			}
2247da2e3ebdSchin 			else
2248da2e3ebdSchin 			{
2249da2e3ebdSchin 				if (mp->disc->errorf)
2250da2e3ebdSchin 					(*mp->disc->errorf)(mp, mp->disc, 3, "%s: cannot open magic file", s);
2251da2e3ebdSchin 				return -1;
2252da2e3ebdSchin 			}
2253da2e3ebdSchin 		}
2254da2e3ebdSchin 		found = 1;
2255da2e3ebdSchin 		n = load(mp, s, fp);
2256da2e3ebdSchin 		sfclose(fp);
2257da2e3ebdSchin 		if (n && !list)
2258da2e3ebdSchin 			return -1;
2259da2e3ebdSchin 	next:
2260da2e3ebdSchin 		if (!e)
2261da2e3ebdSchin 			break;
2262da2e3ebdSchin 		s = e + 1;
2263da2e3ebdSchin 	}
2264da2e3ebdSchin 	if (!found)
2265da2e3ebdSchin 	{
2266da2e3ebdSchin 		if (mp->flags & MAGIC_VERBOSE)
2267da2e3ebdSchin 		{
2268da2e3ebdSchin 			if (mp->disc->errorf)
2269da2e3ebdSchin 				(*mp->disc->errorf)(mp, mp->disc, 2, "cannot find magic file");
2270da2e3ebdSchin 		}
2271da2e3ebdSchin 		return -1;
2272da2e3ebdSchin 	}
2273da2e3ebdSchin 	return 0;
2274da2e3ebdSchin  nospace:
2275da2e3ebdSchin 	if (mp->disc->errorf)
2276da2e3ebdSchin 		(*mp->disc->errorf)(mp, mp->disc, 3, "out of space");
2277da2e3ebdSchin 	return -1;
2278da2e3ebdSchin }
2279da2e3ebdSchin 
2280da2e3ebdSchin /*
2281da2e3ebdSchin  * open a magic session
2282da2e3ebdSchin  */
2283da2e3ebdSchin 
2284da2e3ebdSchin Magic_t*
magicopen(Magicdisc_t * disc)2285da2e3ebdSchin magicopen(Magicdisc_t* disc)
2286da2e3ebdSchin {
2287da2e3ebdSchin 	register Magic_t*	mp;
2288da2e3ebdSchin 	register int		i;
2289da2e3ebdSchin 	register int		n;
2290da2e3ebdSchin 	register int		f;
2291da2e3ebdSchin 	register int		c;
2292da2e3ebdSchin 	register Vmalloc_t*	vm;
2293da2e3ebdSchin 	unsigned char*		map[CC_MAPS + 1];
2294da2e3ebdSchin 
2295da2e3ebdSchin 	if (!(vm = vmopen(Vmdcheap, Vmbest, 0)))
2296da2e3ebdSchin 		return 0;
2297da2e3ebdSchin 	if (!(mp = vmnewof(vm, 0, Magic_t, 1, 0)))
2298da2e3ebdSchin 	{
2299da2e3ebdSchin 		vmclose(vm);
2300da2e3ebdSchin 		return 0;
2301da2e3ebdSchin 	}
2302da2e3ebdSchin 	mp->id = lib;
2303da2e3ebdSchin 	mp->disc = disc;
2304da2e3ebdSchin 	mp->vm = vm;
2305da2e3ebdSchin 	mp->flags = disc->flags;
2306da2e3ebdSchin 	mp->redisc.re_version = REG_VERSION;
2307da2e3ebdSchin 	mp->redisc.re_flags = REG_NOFREE;
2308da2e3ebdSchin 	mp->redisc.re_errorf = (regerror_t)disc->errorf;
2309da2e3ebdSchin 	mp->redisc.re_resizef = (regresize_t)vmgetmem;
2310da2e3ebdSchin 	mp->redisc.re_resizehandle = (void*)mp->vm;
2311da2e3ebdSchin 	mp->dtdisc.key = offsetof(Info_t, name);
2312da2e3ebdSchin 	mp->dtdisc.link = offsetof(Info_t, link);
2313da2e3ebdSchin 	if (!(mp->tmp = sfstropen()) || !(mp->infotab = dtnew(mp->vm, &mp->dtdisc, Dthash)))
2314da2e3ebdSchin 		goto bad;
2315da2e3ebdSchin 	for (n = 0; n < elementsof(info); n++)
2316da2e3ebdSchin 		dtinsert(mp->infotab, &info[n]);
2317da2e3ebdSchin 	for (i = 0; i < CC_MAPS; i++)
2318da2e3ebdSchin 		map[i] = ccmap(i, CC_ASCII);
2319da2e3ebdSchin 	mp->x2n = ccmap(CC_ALIEN, CC_NATIVE);
2320da2e3ebdSchin 	for (n = 0; n <= UCHAR_MAX; n++)
2321da2e3ebdSchin 	{
2322da2e3ebdSchin 		f = 0;
2323da2e3ebdSchin 		i = CC_MAPS;
2324da2e3ebdSchin 		while (--i >= 0)
2325da2e3ebdSchin 		{
2326da2e3ebdSchin 			c = ccmapchr(map[i], n);
2327da2e3ebdSchin 			f = (f << CC_BIT) | CCTYPE(c);
2328da2e3ebdSchin 		}
2329da2e3ebdSchin 		mp->cctype[n] = f;
2330da2e3ebdSchin 	}
2331da2e3ebdSchin 	return mp;
2332da2e3ebdSchin  bad:
2333da2e3ebdSchin 	magicclose(mp);
2334da2e3ebdSchin 	return 0;
2335da2e3ebdSchin }
2336da2e3ebdSchin 
2337da2e3ebdSchin /*
2338da2e3ebdSchin  * close a magicopen() session
2339da2e3ebdSchin  */
2340da2e3ebdSchin 
2341da2e3ebdSchin int
magicclose(register Magic_t * mp)2342da2e3ebdSchin magicclose(register Magic_t* mp)
2343da2e3ebdSchin {
2344da2e3ebdSchin 	if (!mp)
2345da2e3ebdSchin 		return -1;
2346da2e3ebdSchin 	if (mp->tmp)
2347da2e3ebdSchin 		sfstrclose(mp->tmp);
2348da2e3ebdSchin 	if (mp->vm)
2349da2e3ebdSchin 		vmclose(mp->vm);
2350da2e3ebdSchin 	return 0;
2351da2e3ebdSchin }
2352da2e3ebdSchin 
2353da2e3ebdSchin /*
2354da2e3ebdSchin  * return the magic string for file with optional stat info st
2355da2e3ebdSchin  */
2356da2e3ebdSchin 
2357da2e3ebdSchin char*
magictype(register Magic_t * mp,Sfio_t * fp,const char * file,register struct stat * st)2358da2e3ebdSchin magictype(register Magic_t* mp, Sfio_t* fp, const char* file, register struct stat* st)
2359da2e3ebdSchin {
2360da2e3ebdSchin 	off_t	off;
2361da2e3ebdSchin 	char*	s;
2362da2e3ebdSchin 
2363da2e3ebdSchin 	mp->flags = mp->disc->flags;
2364da2e3ebdSchin 	mp->mime = 0;
2365da2e3ebdSchin 	if (!st)
2366da2e3ebdSchin 		s = T("cannot stat");
2367da2e3ebdSchin 	else
2368da2e3ebdSchin 	{
2369da2e3ebdSchin 		if (mp->fp = fp)
2370da2e3ebdSchin 			off = sfseek(mp->fp, (off_t)0, SEEK_CUR);
2371da2e3ebdSchin 		s = type(mp, file, st, mp->tbuf, sizeof(mp->tbuf));
2372da2e3ebdSchin 		if (mp->fp)
2373da2e3ebdSchin 			sfseek(mp->fp, off, SEEK_SET);
2374da2e3ebdSchin 		if (!(mp->flags & MAGIC_MIME))
2375da2e3ebdSchin 		{
2376da2e3ebdSchin 			if (S_ISREG(st->st_mode) && (st->st_size > 0) && (st->st_size < 128))
2377da2e3ebdSchin 				sfprintf(mp->tmp, "%s ", T("short"));
2378da2e3ebdSchin 			sfprintf(mp->tmp, "%s", s);
2379da2e3ebdSchin 			if (!mp->fp && (st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
2380da2e3ebdSchin 				sfprintf(mp->tmp, ", %s", S_ISDIR(st->st_mode) ? T("searchable") : T("executable"));
2381da2e3ebdSchin 			if (st->st_mode & S_ISUID)
2382da2e3ebdSchin 				sfprintf(mp->tmp, ", setuid=%s", fmtuid(st->st_uid));
2383da2e3ebdSchin 			if (st->st_mode & S_ISGID)
2384da2e3ebdSchin 				sfprintf(mp->tmp, ", setgid=%s", fmtgid(st->st_gid));
2385da2e3ebdSchin 			if (st->st_mode & S_ISVTX)
2386da2e3ebdSchin 				sfprintf(mp->tmp, ", sticky");
2387da2e3ebdSchin 			if (!(s = sfstruse(mp->tmp)))
2388da2e3ebdSchin 				s = T("out of space");
2389da2e3ebdSchin 		}
2390da2e3ebdSchin 	}
2391da2e3ebdSchin 	if (mp->flags & MAGIC_MIME)
2392da2e3ebdSchin 		s = mp->mime;
2393da2e3ebdSchin 	if (!s)
2394da2e3ebdSchin 		s = T("error");
2395da2e3ebdSchin 	return s;
2396da2e3ebdSchin }
2397da2e3ebdSchin 
2398da2e3ebdSchin /*
2399da2e3ebdSchin  * list the magic table in mp on sp
2400da2e3ebdSchin  */
2401da2e3ebdSchin 
2402da2e3ebdSchin int
magiclist(register Magic_t * mp,register Sfio_t * sp)2403da2e3ebdSchin magiclist(register Magic_t* mp, register Sfio_t* sp)
2404da2e3ebdSchin {
2405da2e3ebdSchin 	register Entry_t*	ep = mp->magic;
2406da2e3ebdSchin 	register Entry_t*	rp = 0;
2407da2e3ebdSchin 
2408da2e3ebdSchin 	mp->flags = mp->disc->flags;
2409da2e3ebdSchin 	sfprintf(sp, "cont\toffset\ttype\top\tmask\tvalue\tmime\tdesc\n");
2410da2e3ebdSchin 	while (ep)
2411da2e3ebdSchin 	{
2412da2e3ebdSchin 		sfprintf(sp, "%c %c\t", ep->cont, ep->nest);
2413da2e3ebdSchin 		if (ep->expr)
2414da2e3ebdSchin 			sfprintf(sp, "%s", ep->expr);
2415da2e3ebdSchin 		else
2416da2e3ebdSchin 			sfprintf(sp, "%ld", ep->offset);
2417da2e3ebdSchin 		sfprintf(sp, "\t%s%c\t%c\t%lo\t", ep->swap == (char)~3 ? "L" : ep->swap == (char)~0 ? "B" : "", ep->type, ep->op, ep->mask);
2418da2e3ebdSchin 		switch (ep->type)
2419da2e3ebdSchin 		{
2420da2e3ebdSchin 		case 'm':
2421da2e3ebdSchin 		case 's':
2422da2e3ebdSchin 			sfputr(sp, fmtesc(ep->value.str), -1);
2423da2e3ebdSchin 			break;
2424da2e3ebdSchin 		case 'V':
2425da2e3ebdSchin 			switch (ep->op)
2426da2e3ebdSchin 			{
2427da2e3ebdSchin 			case 'l':
2428da2e3ebdSchin 				sfprintf(sp, "loop(%d,%d,%d,%d)", ep->value.loop->start, ep->value.loop->size, ep->value.loop->count, ep->value.loop->offset);
2429da2e3ebdSchin 				break;
2430da2e3ebdSchin 			case 'v':
2431da2e3ebdSchin 				sfprintf(sp, "vcodex()");
2432da2e3ebdSchin 				break;
2433da2e3ebdSchin 			default:
2434da2e3ebdSchin 				sfprintf(sp, "%p", ep->value.str);
2435da2e3ebdSchin 				break;
2436da2e3ebdSchin 			}
2437da2e3ebdSchin 			break;
2438da2e3ebdSchin 		default:
2439da2e3ebdSchin 			sfprintf(sp, "%lo", ep->value.num);
2440da2e3ebdSchin 			break;
2441da2e3ebdSchin 		}
2442da2e3ebdSchin 		sfprintf(sp, "\t%s\t%s\n", ep->mime ? ep->mime : "", fmtesc(ep->desc));
2443da2e3ebdSchin 		if (ep->cont == '$' && !ep->value.lab->mask)
2444da2e3ebdSchin 		{
2445da2e3ebdSchin 			rp = ep;
2446da2e3ebdSchin 			ep = ep->value.lab;
2447da2e3ebdSchin 		}
2448da2e3ebdSchin 		else
2449da2e3ebdSchin 		{
2450da2e3ebdSchin 			if (ep->cont == ':')
2451da2e3ebdSchin 			{
2452da2e3ebdSchin 				ep = rp;
2453da2e3ebdSchin 				ep->value.lab->mask = 1;
2454da2e3ebdSchin 			}
2455da2e3ebdSchin 			ep = ep->next;
2456da2e3ebdSchin 		}
2457da2e3ebdSchin 	}
2458da2e3ebdSchin 	return 0;
2459da2e3ebdSchin }
2460