xref: /onnv-gate/usr/src/lib/libast/common/misc/magic.c (revision 12068:08a39a083754)
14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*12068SRoger.Faulkner@Oracle.COM *          Copyright (c) 1985-2010 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
78462SApril.Chin@Sun.COM *                    by AT&T Intellectual Property                     *
84887Schin *                                                                      *
94887Schin *                A copy of the License is available at                 *
104887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
114887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
124887Schin *                                                                      *
134887Schin *              Information and Software Systems Research               *
144887Schin *                            AT&T Research                             *
154887Schin *                           Florham Park NJ                            *
164887Schin *                                                                      *
174887Schin *                 Glenn Fowler <gsf@research.att.com>                  *
184887Schin *                  David Korn <dgk@research.att.com>                   *
194887Schin *                   Phong Vo <kpv@research.att.com>                    *
204887Schin *                                                                      *
214887Schin ***********************************************************************/
224887Schin #pragma prototyped
234887Schin /*
244887Schin  * Glenn Fowler
254887Schin  * AT&T Research
264887Schin  *
274887Schin  * library interface to file
284887Schin  *
294887Schin  * the sum of the hacks {s5,v10,planix} is _____ than the parts
304887Schin  */
314887Schin 
328462SApril.Chin@Sun.COM static const char id[] = "\n@(#)$Id: magic library (AT&T Research) 2008-09-10 $\0\n";
334887Schin 
344887Schin static const char lib[] = "libast:magic";
354887Schin 
364887Schin #include <ast.h>
374887Schin #include <ctype.h>
384887Schin #include <ccode.h>
394887Schin #include <dt.h>
404887Schin #include <modex.h>
414887Schin #include <error.h>
424887Schin #include <regex.h>
434887Schin #include <swap.h>
444887Schin 
454887Schin #define T(m)		(*m?ERROR_translate(NiL,NiL,lib,m):m)
464887Schin 
474887Schin #define match(s,p)	strgrpmatch(s,p,NiL,0,STR_LEFT|STR_RIGHT|STR_ICASE)
484887Schin 
494887Schin #define MAXNEST		10		/* { ... } nesting limit	*/
504887Schin #define MINITEM		4		/* magic buffer rounding	*/
514887Schin 
524887Schin typedef struct				/* identifier dictionary entry	*/
534887Schin {
544887Schin 	const char	name[16];	/* identifier name		*/
554887Schin 	int		value;		/* identifier value		*/
564887Schin 	Dtlink_t	link;		/* dictionary link		*/
574887Schin } Info_t;
584887Schin 
594887Schin typedef struct Edit			/* edit substitution		*/
604887Schin {
614887Schin 	struct Edit*	next;		/* next in list			*/
624887Schin 	regex_t*	from;		/* from pattern			*/
634887Schin } Edit_t;
644887Schin 
654887Schin struct Entry;
664887Schin 
674887Schin typedef struct				/* loop info			*/
684887Schin {
694887Schin 	struct Entry*	lab;		/* call this function		*/
704887Schin 	int		start;		/* start here			*/
714887Schin 	int		size;		/* increment by this amount	*/
724887Schin 	int		count;		/* dynamic loop count		*/
734887Schin 	int		offset;		/* dynamic offset		*/
744887Schin } Loop_t;
754887Schin 
764887Schin typedef struct Entry			/* magic file entry		*/
774887Schin {
784887Schin 	struct Entry*	next;		/* next in list			*/
794887Schin 	char*		expr;		/* offset expression		*/
804887Schin 	union
814887Schin 	{
824887Schin 	unsigned long	num;
834887Schin 	char*		str;
844887Schin 	struct Entry*	lab;
854887Schin 	regex_t*	sub;
864887Schin 	Loop_t*		loop;
874887Schin 	}		value;		/* comparison value		*/
884887Schin 	char*		desc;		/* file description		*/
894887Schin 	char*		mime;		/* file mime type		*/
904887Schin 	unsigned long	offset;		/* offset in bytes		*/
914887Schin 	unsigned long	mask;		/* mask before compare		*/
924887Schin 	char		cont;		/* continuation operation	*/
934887Schin 	char		type;		/* datum type			*/
944887Schin 	char		op;		/* comparison operation		*/
954887Schin 	char		nest;		/* { or } nesting operation	*/
964887Schin 	char		swap;		/* forced swap order		*/
974887Schin } Entry_t;
984887Schin 
994887Schin #define CC_BIT		5
1004887Schin 
1014887Schin #if (CC_MAPS*CC_BIT) <= (CHAR_BIT*2)
1024887Schin typedef unsigned short Cctype_t;
1034887Schin #else
1044887Schin typedef unsigned long Cctype_t;
1054887Schin #endif
1064887Schin 
1074887Schin #define CC_text		0x01
1084887Schin #define CC_control	0x02
1094887Schin #define CC_latin	0x04
1104887Schin #define CC_binary	0x08
1114887Schin #define CC_utf_8	0x10
1124887Schin 
1134887Schin #define CC_notext	CC_text		/* CC_text is flipped before checking */
1144887Schin 
1154887Schin #define CC_MASK		(CC_binary|CC_latin|CC_control|CC_text)
1164887Schin 
1174887Schin #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)
1184887Schin 
1194887Schin #define ID_NONE		0
1204887Schin #define ID_ASM		1
1214887Schin #define ID_C		2
1224887Schin #define ID_COBOL	3
1234887Schin #define ID_COPYBOOK	4
1244887Schin #define ID_CPLUSPLUS	5
1254887Schin #define ID_FORTRAN	6
1264887Schin #define ID_HTML		7
1274887Schin #define ID_INCL1	8
1284887Schin #define ID_INCL2	9
1294887Schin #define ID_INCL3	10
1304887Schin #define ID_MAM1		11
1314887Schin #define ID_MAM2		12
1324887Schin #define ID_MAM3		13
1334887Schin #define ID_NOTEXT	14
1344887Schin #define ID_PL1		15
1354887Schin #define ID_YACC		16
1364887Schin 
1374887Schin #define ID_MAX		ID_YACC
1384887Schin 
1394887Schin #define INFO_atime	1
1404887Schin #define INFO_blocks	2
1414887Schin #define INFO_ctime	3
1424887Schin #define INFO_fstype	4
1434887Schin #define INFO_gid	5
1444887Schin #define INFO_mode	6
1454887Schin #define INFO_mtime	7
1464887Schin #define INFO_name	8
1474887Schin #define INFO_nlink	9
1484887Schin #define INFO_size	10
1494887Schin #define INFO_uid	11
1504887Schin 
1514887Schin #define _MAGIC_PRIVATE_ \
1524887Schin 	Magicdisc_t*	disc;			/* discipline		*/ \
1534887Schin 	Vmalloc_t*	vm;			/* vmalloc region	*/ \
1544887Schin 	Entry_t*	magic;			/* parsed magic table	*/ \
1554887Schin 	Entry_t*	magiclast;		/* last entry in magic	*/ \
1564887Schin 	char*		mime;			/* MIME type		*/ \
1574887Schin 	unsigned char*	x2n;			/* CC_ALIEN=>CC_NATIVE	*/ \
1584887Schin 	char		fbuf[SF_BUFSIZE + 1];	/* file data		*/ \
1594887Schin 	char		xbuf[SF_BUFSIZE + 1];	/* indirect file data	*/ \
1604887Schin 	char		nbuf[256];		/* !CC_NATIVE data	*/ \
1614887Schin 	char		mbuf[64];		/* mime string		*/ \
1624887Schin 	char		sbuf[64];		/* type suffix string	*/ \
1634887Schin 	char		tbuf[2 * PATH_MAX];	/* type string		*/ \
1644887Schin 	Cctype_t	cctype[UCHAR_MAX + 1];	/* char code types	*/ \
1654887Schin 	unsigned int	count[UCHAR_MAX + 1];	/* char frequency count	*/ \
1664887Schin 	unsigned int	multi[UCHAR_MAX + 1];	/* muti char count	*/ \
1674887Schin 	int		keep[MAXNEST];		/* ckmagic nest stack	*/ \
1684887Schin 	char*		cap[MAXNEST];		/* ckmagic mime stack	*/ \
1694887Schin 	char*		msg[MAXNEST];		/* ckmagic text stack	*/ \
1704887Schin 	Entry_t*	ret[MAXNEST];		/* ckmagic return stack	*/ \
1714887Schin 	int		fbsz;			/* fbuf size		*/ \
1724887Schin 	int		fbmx;			/* fbuf max size	*/ \
1734887Schin 	int		xbsz;			/* xbuf size		*/ \
1744887Schin 	int		swap;			/* swap() operation	*/ \
1754887Schin 	unsigned long	flags;			/* disc+open flags	*/ \
1764887Schin 	long		xoff;			/* xbuf offset		*/ \
1774887Schin 	int		identifier[ID_MAX + 1];	/* Info_t identifier	*/ \
1784887Schin 	Sfio_t*		fp;			/* fbuf fp		*/ \
1794887Schin 	Sfio_t*		tmp;			/* tmp string		*/ \
1804887Schin 	regdisc_t	redisc;			/* regex discipline	*/ \
1814887Schin 	Dtdisc_t	dtdisc;			/* dict discipline	*/ \
1824887Schin 	Dt_t*		idtab;			/* identifier dict	*/ \
1834887Schin 	Dt_t*		infotab;		/* info keyword dict	*/
1844887Schin 
1854887Schin #include <magic.h>
1864887Schin 
1874887Schin static Info_t		dict[] =		/* keyword dictionary	*/
1884887Schin {
1894887Schin 	{ 	"COMMON",	ID_FORTRAN	},
1904887Schin 	{ 	"COMPUTE",	ID_COBOL	},
1914887Schin 	{ 	"COMP",		ID_COPYBOOK	},
1924887Schin 	{ 	"COMPUTATIONAL",ID_COPYBOOK	},
1934887Schin 	{ 	"DCL",		ID_PL1		},
1944887Schin 	{ 	"DEFINED",	ID_PL1		},
1954887Schin 	{ 	"DIMENSION",	ID_FORTRAN	},
1964887Schin 	{ 	"DIVISION",	ID_COBOL	},
1974887Schin 	{ 	"FILLER",	ID_COPYBOOK	},
1984887Schin 	{ 	"FIXED",	ID_PL1		},
1994887Schin 	{ 	"FUNCTION",	ID_FORTRAN	},
2004887Schin 	{ 	"HTML",		ID_HTML		},
2014887Schin 	{ 	"INTEGER",	ID_FORTRAN	},
2024887Schin 	{ 	"MAIN",		ID_PL1		},
2034887Schin 	{ 	"OPTIONS",	ID_PL1		},
2044887Schin 	{ 	"PERFORM",	ID_COBOL	},
2054887Schin 	{ 	"PIC",		ID_COPYBOOK	},
2064887Schin 	{ 	"REAL",		ID_FORTRAN	},
2074887Schin 	{ 	"REDEFINES",	ID_COPYBOOK	},
2084887Schin 	{ 	"S9",		ID_COPYBOOK	},
2094887Schin 	{ 	"SECTION",	ID_COBOL	},
2104887Schin 	{ 	"SELECT",	ID_COBOL	},
2114887Schin 	{ 	"SUBROUTINE",	ID_FORTRAN	},
2124887Schin 	{ 	"TEXT",		ID_ASM		},
2134887Schin 	{ 	"VALUE",	ID_COPYBOOK	},
2144887Schin 	{ 	"attr",		ID_MAM3		},
2154887Schin 	{ 	"binary",	ID_YACC		},
2164887Schin 	{ 	"block",	ID_FORTRAN	},
2174887Schin 	{ 	"bss",		ID_ASM		},
2184887Schin 	{ 	"byte",		ID_ASM		},
2194887Schin 	{ 	"char",		ID_C		},
2204887Schin 	{ 	"class",	ID_CPLUSPLUS	},
2214887Schin 	{ 	"clr",		ID_NOTEXT	},
2224887Schin 	{ 	"comm",		ID_ASM		},
2234887Schin 	{ 	"common",	ID_FORTRAN	},
2244887Schin 	{ 	"data",		ID_ASM		},
2254887Schin 	{ 	"dimension",	ID_FORTRAN	},
2264887Schin 	{ 	"done",		ID_MAM2		},
2274887Schin 	{ 	"double",	ID_C		},
2284887Schin 	{ 	"even",		ID_ASM		},
2294887Schin 	{ 	"exec",		ID_MAM3		},
2304887Schin 	{ 	"extern",	ID_C		},
2314887Schin 	{ 	"float",	ID_C		},
2324887Schin 	{ 	"function",	ID_FORTRAN	},
2334887Schin 	{ 	"globl",	ID_ASM		},
2344887Schin 	{ 	"h",		ID_INCL3	},
2354887Schin 	{ 	"html",		ID_HTML		},
2364887Schin 	{ 	"include",	ID_INCL1	},
2374887Schin 	{ 	"int",		ID_C		},
2384887Schin 	{ 	"integer",	ID_FORTRAN	},
2394887Schin 	{ 	"jmp",		ID_NOTEXT	},
2404887Schin 	{ 	"left",		ID_YACC		},
2414887Schin 	{ 	"libc",		ID_INCL2	},
2424887Schin 	{ 	"long",		ID_C		},
2434887Schin 	{ 	"make",		ID_MAM1		},
2444887Schin 	{ 	"mov",		ID_NOTEXT	},
2454887Schin 	{ 	"private",	ID_CPLUSPLUS	},
2464887Schin 	{ 	"public",	ID_CPLUSPLUS	},
2474887Schin 	{ 	"real",		ID_FORTRAN	},
2484887Schin 	{ 	"register",	ID_C		},
2494887Schin 	{ 	"right",	ID_YACC		},
2504887Schin 	{ 	"sfio",		ID_INCL2	},
2514887Schin 	{ 	"static",	ID_C		},
2524887Schin 	{ 	"stdio",	ID_INCL2	},
2534887Schin 	{ 	"struct",	ID_C		},
2544887Schin 	{ 	"subroutine",	ID_FORTRAN	},
2554887Schin 	{ 	"sys",		ID_NOTEXT	},
2564887Schin 	{ 	"term",		ID_YACC		},
2574887Schin 	{ 	"text",		ID_ASM		},
2584887Schin 	{ 	"tst",		ID_NOTEXT	},
2594887Schin 	{ 	"type",		ID_YACC		},
2604887Schin 	{ 	"typedef",	ID_C		},
2614887Schin 	{ 	"u",		ID_INCL2	},
2624887Schin 	{ 	"union",	ID_YACC		},
2634887Schin 	{ 	"void",		ID_C		},
2644887Schin };
2654887Schin 
2664887Schin static Info_t		info[] =
2674887Schin {
2684887Schin 	{	"atime",	INFO_atime		},
2694887Schin 	{	"blocks",	INFO_blocks		},
2704887Schin 	{	"ctime",	INFO_ctime		},
2714887Schin 	{	"fstype",	INFO_fstype		},
2724887Schin 	{	"gid",		INFO_gid		},
2734887Schin 	{	"mode",		INFO_mode		},
2744887Schin 	{	"mtime",	INFO_mtime		},
2754887Schin 	{	"name",		INFO_name		},
2764887Schin 	{	"nlink",	INFO_nlink		},
2774887Schin 	{	"size",		INFO_size		},
2784887Schin 	{	"uid",		INFO_uid		},
2794887Schin };
2804887Schin 
2814887Schin /*
2824887Schin  * return pointer to data at offset off and size siz
2834887Schin  */
2844887Schin 
2854887Schin static char*
getdata(register Magic_t * mp,register long off,register int siz)2864887Schin getdata(register Magic_t* mp, register long off, register int siz)
2874887Schin {
2884887Schin 	register long	n;
2894887Schin 
2904887Schin 	if (off < 0)
2914887Schin 		return 0;
2924887Schin 	if (off + siz <= mp->fbsz)
2934887Schin 		return mp->fbuf + off;
2944887Schin 	if (off < mp->xoff || off + siz > mp->xoff + mp->xbsz)
2954887Schin 	{
2964887Schin 		if (off + siz > mp->fbmx)
2974887Schin 			return 0;
2984887Schin 		n = (off / (SF_BUFSIZE / 2)) * (SF_BUFSIZE / 2);
2994887Schin 		if (sfseek(mp->fp, n, SEEK_SET) != n)
3004887Schin 			return 0;
3014887Schin 		if ((mp->xbsz = sfread(mp->fp, mp->xbuf, sizeof(mp->xbuf) - 1)) < 0)
3024887Schin 		{
3034887Schin 			mp->xoff = 0;
3044887Schin 			mp->xbsz = 0;
3054887Schin 			return 0;
3064887Schin 		}
3074887Schin 		mp->xbuf[mp->xbsz] = 0;
3084887Schin 		mp->xoff = n;
3094887Schin 		if (off + siz > mp->xoff + mp->xbsz)
3104887Schin 			return 0;
3114887Schin 	}
3124887Schin 	return mp->xbuf + off - mp->xoff;
3134887Schin }
3144887Schin 
3154887Schin /*
3164887Schin  * @... evaluator for strexpr()
3174887Schin  */
3184887Schin 
3194887Schin static long
indirect(const char * cs,char ** e,void * handle)3204887Schin indirect(const char* cs, char** e, void* handle)
3214887Schin {
3224887Schin 	register char*		s = (char*)cs;
3234887Schin 	register Magic_t*	mp = (Magic_t*)handle;
3244887Schin 	register long		n = 0;
3254887Schin 	register char*		p;
3264887Schin 
3274887Schin 	if (s)
3284887Schin 	{
3294887Schin 		if (*s == '@')
3304887Schin 		{
3314887Schin 			n = *++s == '(' ? strexpr(s, e, indirect, mp) : strtol(s, e, 0);
3324887Schin 			switch (*(s = *e))
3334887Schin 			{
3344887Schin 			case 'b':
3354887Schin 			case 'B':
3364887Schin 				s++;
3374887Schin 				if (p = getdata(mp, n, 1))
3384887Schin 					n = *(unsigned char*)p;
3394887Schin 				else
3404887Schin 					s = (char*)cs;
3414887Schin 				break;
3424887Schin 			case 'h':
3434887Schin 			case 'H':
3444887Schin 				s++;
3454887Schin 				if (p = getdata(mp, n, 2))
3464887Schin 					n = swapget(mp->swap, p, 2);
3474887Schin 				else
3484887Schin 					s = (char*)cs;
3494887Schin 				break;
3504887Schin 			case 'q':
3514887Schin 			case 'Q':
3524887Schin 				s++;
3534887Schin 				if (p = getdata(mp, n, 8))
3544887Schin 					n = swapget(mp->swap, p, 8);
3554887Schin 				else
3564887Schin 					s = (char*)cs;
3574887Schin 				break;
3584887Schin 			default:
3594887Schin 				if (isalnum(*s))
3604887Schin 					s++;
3614887Schin 				if (p = getdata(mp, n, 4))
3624887Schin 					n = swapget(mp->swap, p, 4);
3634887Schin 				else
3644887Schin 					s = (char*)cs;
3654887Schin 				break;
3664887Schin 			}
3674887Schin 		}
3684887Schin 		*e = s;
3694887Schin 	}
3704887Schin 	else if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
3714887Schin 		(*mp->disc->errorf)(mp, mp->disc, 2, "%s in indirect expression", *e);
3724887Schin 	return n;
3734887Schin }
3744887Schin 
3754887Schin /*
3764887Schin  * emit regex error message
3774887Schin  */
3784887Schin 
3794887Schin static void
regmessage(Magic_t * mp,regex_t * re,int code)3804887Schin regmessage(Magic_t* mp, regex_t* re, int code)
3814887Schin {
3824887Schin 	char	buf[128];
3834887Schin 
3844887Schin 	if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
3854887Schin 	{
3864887Schin 		regerror(code, re, buf, sizeof(buf));
3874887Schin 		(*mp->disc->errorf)(mp, mp->disc, 3, "regex: %s", buf);
3884887Schin 	}
3894887Schin }
3904887Schin 
3914887Schin /*
3924887Schin  * decompose vcodex(3) method composition
3934887Schin  */
3944887Schin 
3954887Schin static char*
vcdecomp(char * b,char * e,unsigned char * m,unsigned char * x)3964887Schin vcdecomp(char* b, char* e, unsigned char* m, unsigned char* x)
3974887Schin {
3984887Schin 	unsigned char*	map;
3998462SApril.Chin@Sun.COM 	const char*	o;
4004887Schin 	int		c;
4014887Schin 	int		n;
4024887Schin 	int		i;
4038462SApril.Chin@Sun.COM 	int		a;
4044887Schin 
4054887Schin 	map = CCMAP(CC_ASCII, CC_NATIVE);
4068462SApril.Chin@Sun.COM 	a = 0;
4074887Schin 	i = 1;
4084887Schin 	for (;;)
4094887Schin 	{
4104887Schin 		if (i)
4114887Schin 			i = 0;
4124887Schin 		else
4134887Schin 			*b++ = '^';
4148462SApril.Chin@Sun.COM 		if (m < (x - 1) && !*(m + 1))
4154887Schin 		{
4168462SApril.Chin@Sun.COM 			/*
4178462SApril.Chin@Sun.COM 			 * obsolete indices
4188462SApril.Chin@Sun.COM 			 */
4198462SApril.Chin@Sun.COM 
4208462SApril.Chin@Sun.COM 			if (!a)
4218462SApril.Chin@Sun.COM 			{
4228462SApril.Chin@Sun.COM 				a = 1;
4238462SApril.Chin@Sun.COM 				o = "old, ";
4248462SApril.Chin@Sun.COM 				while (b < e && (c = *o++))
4258462SApril.Chin@Sun.COM 					*b++ = c;
4268462SApril.Chin@Sun.COM 			}
4278462SApril.Chin@Sun.COM 			switch (*m)
4288462SApril.Chin@Sun.COM 			{
4298462SApril.Chin@Sun.COM 			case 0:		o = "delta"; break;
4308462SApril.Chin@Sun.COM 			case 1:		o = "huffman"; break;
4318462SApril.Chin@Sun.COM 			case 2:		o = "huffgroup"; break;
4328462SApril.Chin@Sun.COM 			case 3:		o = "arith"; break;
4338462SApril.Chin@Sun.COM 			case 4:		o = "bwt"; break;
4348462SApril.Chin@Sun.COM 			case 5:		o = "rle"; break;
4358462SApril.Chin@Sun.COM 			case 6:		o = "mtf"; break;
4368462SApril.Chin@Sun.COM 			case 7:		o = "transpose"; break;
4378462SApril.Chin@Sun.COM 			case 8:		o = "table"; break;
4388462SApril.Chin@Sun.COM 			case 9:		o = "huffpart"; break;
4398462SApril.Chin@Sun.COM 			case 50:	o = "map"; break;
4408462SApril.Chin@Sun.COM 			case 100:	o = "recfm"; break;
4418462SApril.Chin@Sun.COM 			case 101:	o = "ss7"; break;
4428462SApril.Chin@Sun.COM 			default:	o = "UNKNOWN"; break;
4438462SApril.Chin@Sun.COM 			}
4448462SApril.Chin@Sun.COM 			m += 2;
4458462SApril.Chin@Sun.COM 			while (b < e && (c = *o++))
4468462SApril.Chin@Sun.COM 				*b++ = c;
4474887Schin 		}
4488462SApril.Chin@Sun.COM 		else
4498462SApril.Chin@Sun.COM 			while (b < e && m < x && (c = *m++))
4508462SApril.Chin@Sun.COM 			{
4518462SApril.Chin@Sun.COM 				if (map)
4528462SApril.Chin@Sun.COM 					c = map[c];
4538462SApril.Chin@Sun.COM 				*b++ = c;
4548462SApril.Chin@Sun.COM 			}
4554887Schin 		if (b >= e)
4564887Schin 			break;
4574887Schin 		n = 0;
4584887Schin 		while (m < x)
4594887Schin 		{
4604887Schin 			n = (n<<7) | (*m & 0x7f);
4614887Schin 			if (!(*m++ & 0x80))
4624887Schin 				break;
4634887Schin 		}
4644887Schin 		if (n >= (x - m))
4654887Schin 			break;
4664887Schin 		m += n;
4674887Schin 	}
4684887Schin 	return b;
4694887Schin }
4704887Schin 
4714887Schin /*
4724887Schin  * check for magic table match in buf
4734887Schin  */
4744887Schin 
4754887Schin static char*
ckmagic(register Magic_t * mp,const char * file,char * buf,struct stat * st,unsigned long off)4764887Schin ckmagic(register Magic_t* mp, const char* file, char* buf, struct stat* st, unsigned long off)
4774887Schin {
4784887Schin 	register Entry_t*	ep;
4794887Schin 	register char*		p;
4804887Schin 	register char*		b;
4814887Schin 	register int		level = 0;
4824887Schin 	int			call = -1;
4834887Schin 	int			c;
4844887Schin 	char*			q;
4854887Schin 	char*			t;
4864887Schin 	char*			base = 0;
4874887Schin 	unsigned long		num;
4884887Schin 	unsigned long		mask;
4894887Schin 	regmatch_t		matches[10];
4904887Schin 
4914887Schin 	mp->swap = 0;
4924887Schin 	b = mp->msg[0] = buf;
4934887Schin 	mp->mime = mp->cap[0] = 0;
4944887Schin 	mp->keep[0] = 0;
4954887Schin 	for (ep = mp->magic; ep; ep = ep->next)
4964887Schin 	{
4974887Schin 	fun:
4984887Schin 		if (ep->nest == '{')
4994887Schin 		{
5004887Schin 			if (++level >= MAXNEST)
5014887Schin 			{
5024887Schin 				call = -1;
5034887Schin 				level = 0;
5044887Schin 				mp->keep[0] = 0;
5054887Schin 				b = mp->msg[0];
5064887Schin 				mp->mime = mp->cap[0];
5074887Schin 				continue;
5084887Schin 			}
5094887Schin 			mp->keep[level] = mp->keep[level - 1] != 0;
5104887Schin 			mp->msg[level] = b;
5114887Schin 			mp->cap[level] = mp->mime;
5124887Schin 		}
5134887Schin 		switch (ep->cont)
5144887Schin 		{
5154887Schin 		case '#':
5164887Schin 			if (mp->keep[level] && b > buf)
5174887Schin 			{
5184887Schin 				*b = 0;
5194887Schin 				return buf;
5204887Schin 			}
5214887Schin 			mp->swap = 0;
5224887Schin 			b = mp->msg[0] = buf;
5234887Schin 			mp->mime = mp->cap[0] = 0;
5244887Schin 			if (ep->type == ' ')
5254887Schin 				continue;
5264887Schin 			break;
5274887Schin 		case '$':
5284887Schin 			if (mp->keep[level] && call < (MAXNEST - 1))
5294887Schin 			{
5304887Schin 				mp->ret[++call] = ep;
5314887Schin 				ep = ep->value.lab;
5324887Schin 				goto fun;
5334887Schin 			}
5344887Schin 			continue;
5354887Schin 		case ':':
5364887Schin 			ep = mp->ret[call--];
5374887Schin 			if (ep->op == 'l')
5384887Schin 				goto fun;
5394887Schin 			continue;
5404887Schin 		case '|':
5414887Schin 			if (mp->keep[level] > 1)
5424887Schin 				goto checknest;
5434887Schin 			/*FALLTHROUGH*/
5444887Schin 		default:
5454887Schin 			if (!mp->keep[level])
5464887Schin 			{
5474887Schin 				b = mp->msg[level];
5484887Schin 				mp->mime = mp->cap[level];
5494887Schin 				goto checknest;
5504887Schin 			}
5514887Schin 			break;
5524887Schin 		}
5538462SApril.Chin@Sun.COM 		p = "";
5548462SApril.Chin@Sun.COM 		num = 0;
5554887Schin 		if (!ep->expr)
5564887Schin 			num = ep->offset + off;
5574887Schin 		else
5584887Schin 			switch (ep->offset)
5594887Schin 			{
5604887Schin 			case 0:
5614887Schin 				num = strexpr(ep->expr, NiL, indirect, mp) + off;
5624887Schin 				break;
5634887Schin 			case INFO_atime:
5644887Schin 				num = st->st_atime;
5654887Schin 				ep->type = 'D';
5664887Schin 				break;
5674887Schin 			case INFO_blocks:
5684887Schin 				num = iblocks(st);
5694887Schin 				ep->type = 'N';
5704887Schin 				break;
5714887Schin 			case INFO_ctime:
5724887Schin 				num = st->st_ctime;
5734887Schin 				ep->type = 'D';
5744887Schin 				break;
5754887Schin 			case INFO_fstype:
5764887Schin 				p = fmtfs(st);
5774887Schin 				ep->type = toupper(ep->type);
5784887Schin 				break;
5794887Schin 			case INFO_gid:
5804887Schin 				if (ep->type == 'e' || ep->type == 'm' || ep->type == 's')
5814887Schin 				{
5824887Schin 					p = fmtgid(st->st_gid);
5834887Schin 					ep->type = toupper(ep->type);
5844887Schin 				}
5854887Schin 				else
5864887Schin 				{
5874887Schin 					num = st->st_gid;
5884887Schin 					ep->type = 'N';
5894887Schin 				}
5904887Schin 				break;
5914887Schin 			case INFO_mode:
5924887Schin 				if (ep->type == 'e' || ep->type == 'm' || ep->type == 's')
5934887Schin 				{
5944887Schin 					p = fmtmode(st->st_mode, 0);
5954887Schin 					ep->type = toupper(ep->type);
5964887Schin 				}
5974887Schin 				else
5984887Schin 				{
5994887Schin 					num = modex(st->st_mode);
6004887Schin 					ep->type = 'N';
6014887Schin 				}
6024887Schin 				break;
6034887Schin 			case INFO_mtime:
6044887Schin 				num = st->st_ctime;
6054887Schin 				ep->type = 'D';
6064887Schin 				break;
6074887Schin 			case INFO_name:
6084887Schin 				if (!base)
6094887Schin 				{
6104887Schin 					if (base = strrchr(file, '/'))
6114887Schin 						base++;
6124887Schin 					else
6134887Schin 						base = (char*)file;
6144887Schin 				}
6154887Schin 				p = base;
6164887Schin 				ep->type = toupper(ep->type);
6174887Schin 				break;
6184887Schin 			case INFO_nlink:
6194887Schin 				num = st->st_nlink;
6204887Schin 				ep->type = 'N';
6214887Schin 				break;
6224887Schin 			case INFO_size:
6234887Schin 				num = st->st_size;
6244887Schin 				ep->type = 'N';
6254887Schin 				break;
6264887Schin 			case INFO_uid:
6274887Schin 				if (ep->type == 'e' || ep->type == 'm' || ep->type == 's')
6284887Schin 				{
6294887Schin 					p = fmtuid(st->st_uid);
6304887Schin 					ep->type = toupper(ep->type);
6314887Schin 				}
6324887Schin 				else
6334887Schin 				{
6344887Schin 					num = st->st_uid;
6354887Schin 					ep->type = 'N';
6364887Schin 				}
6374887Schin 				break;
6384887Schin 			}
6394887Schin 		switch (ep->type)
6404887Schin 		{
6414887Schin 
6424887Schin 		case 'b':
6434887Schin 			if (!(p = getdata(mp, num, 1)))
6444887Schin 				goto next;
6454887Schin 			num = *(unsigned char*)p;
6464887Schin 			break;
6474887Schin 
6484887Schin 		case 'h':
6494887Schin 			if (!(p = getdata(mp, num, 2)))
6504887Schin 				goto next;
6514887Schin 			num = swapget(ep->swap ? (~ep->swap ^ mp->swap) : mp->swap, p, 2);
6524887Schin 			break;
6534887Schin 
6544887Schin 		case 'd':
6554887Schin 		case 'l':
6564887Schin 		case 'v':
6574887Schin 			if (!(p = getdata(mp, num, 4)))
6584887Schin 				goto next;
6594887Schin 			num = swapget(ep->swap ? (~ep->swap ^ mp->swap) : mp->swap, p, 4);
6604887Schin 			break;
6614887Schin 
6624887Schin 		case 'q':
6634887Schin 			if (!(p = getdata(mp, num, 8)))
6644887Schin 				goto next;
6654887Schin 			num = swapget(ep->swap ? (~ep->swap ^ mp->swap) : mp->swap, p, 8);
6664887Schin 			break;
6674887Schin 
6684887Schin 		case 'e':
6694887Schin 			if (!(p = getdata(mp, num, 0)))
6704887Schin 				goto next;
6714887Schin 			/*FALLTHROUGH*/
6724887Schin 		case 'E':
6734887Schin 			if (!ep->value.sub)
6744887Schin 				goto next;
6754887Schin 			if ((c = regexec(ep->value.sub, p, elementsof(matches), matches, 0)) || (c = regsubexec(ep->value.sub, p, elementsof(matches), matches)))
6764887Schin 			{
6774887Schin 				c = mp->fbsz;
6784887Schin 				if (c >= sizeof(mp->nbuf))
6794887Schin 					c = sizeof(mp->nbuf) - 1;
6804887Schin 				p = (char*)memcpy(mp->nbuf, p, c);
6814887Schin 				p[c] = 0;
6824887Schin 				ccmapstr(mp->x2n, p, c);
6834887Schin 				if ((c = regexec(ep->value.sub, p, elementsof(matches), matches, 0)) || (c = regsubexec(ep->value.sub, p, elementsof(matches), matches)))
6844887Schin 				{
6854887Schin 					if (c != REG_NOMATCH)
6864887Schin 						regmessage(mp, ep->value.sub, c);
6874887Schin 					goto next;
6884887Schin 				}
6894887Schin 			}
6904887Schin 			p = ep->value.sub->re_sub->re_buf;
6914887Schin 			q = T(ep->desc);
6924887Schin 			t = *q ? q : p;
6934887Schin 			if (mp->keep[level]++ && b > buf && *(b - 1) != ' ' && *t && *t != ',' && *t != '.' && *t != '\b')
6944887Schin 				*b++ = ' ';
6954887Schin 			b += sfsprintf(b, PATH_MAX - (b - buf), *q ? q : "%s", p + (*p == '\b'));
6964887Schin 			if (ep->mime)
6974887Schin 				mp->mime = ep->mime;
6984887Schin 			goto checknest;
6994887Schin 
7004887Schin 		case 's':
7014887Schin 			if (!(p = getdata(mp, num, ep->mask)))
7024887Schin 				goto next;
7034887Schin 			goto checkstr;
7044887Schin 		case 'm':
7054887Schin 			if (!(p = getdata(mp, num, 0)))
7064887Schin 				goto next;
7074887Schin 			/*FALLTHROUGH*/
7084887Schin 		case 'M':
7094887Schin 		case 'S':
7104887Schin 		checkstr:
7114887Schin 			for (;;)
7124887Schin 			{
7134887Schin 				if (*ep->value.str == '*' && !*(ep->value.str + 1) && isprint(*p))
7144887Schin 					break;
7154887Schin 				if ((ep->type == 'm' || ep->type == 'M') ? strmatch(p, ep->value.str) : !memcmp(p, ep->value.str, ep->mask))
7164887Schin 					break;
7174887Schin 				if (p == mp->nbuf || ep->mask >= sizeof(mp->nbuf))
7184887Schin 					goto next;
7194887Schin 				p = (char*)memcpy(mp->nbuf, p, ep->mask);
7204887Schin 				p[ep->mask] = 0;
7214887Schin 				ccmapstr(mp->x2n, p, ep->mask);
7224887Schin 			}
7234887Schin 			q = T(ep->desc);
7244887Schin 			if (mp->keep[level]++ && b > buf && *(b - 1) != ' ' && *q && *q != ',' && *q != '.' && *q != '\b')
7254887Schin 				*b++ = ' ';
7264887Schin 			for (t = p; (c = *t) >= 0 && c <= 0177 && isprint(c) && c != '\n'; t++);
7274887Schin 			*t = 0;
7284887Schin 			b += sfsprintf(b, PATH_MAX - (b - buf), q + (*q == '\b'), p);
7294887Schin 			*t = c;
7304887Schin 			if (ep->mime)
7314887Schin 				mp->mime = ep->mime;
7324887Schin 			goto checknest;
7334887Schin 
7344887Schin 		}
7354887Schin 		if (mask = ep->mask)
7364887Schin 			num &= mask;
7374887Schin 		switch (ep->op)
7384887Schin 		{
7394887Schin 
7404887Schin 		case '=':
7414887Schin 		case '@':
7424887Schin 			if (num == ep->value.num)
7434887Schin 				break;
7444887Schin 			if (ep->cont != '#')
7454887Schin 				goto next;
7464887Schin 			if (!mask)
7474887Schin 				mask = ~mask;
7484887Schin 			if (ep->type == 'h')
7494887Schin 			{
7504887Schin 				if ((num = swapget(mp->swap = 1, p, 2) & mask) == ep->value.num)
7514887Schin 				{
7524887Schin 					if (!(mp->swap & (mp->swap + 1)))
7534887Schin 						mp->swap = 7;
7544887Schin 					goto swapped;
7554887Schin 				}
7564887Schin 			}
7574887Schin 			else if (ep->type == 'l')
7584887Schin 			{
7594887Schin 				for (c = 1; c < 4; c++)
7604887Schin 					if ((num = swapget(mp->swap = c, p, 4) & mask) == ep->value.num)
7614887Schin 					{
7624887Schin 						if (!(mp->swap & (mp->swap + 1)))
7634887Schin 							mp->swap = 7;
7644887Schin 						goto swapped;
7654887Schin 					}
7664887Schin 			}
7674887Schin 			else if (ep->type == 'q')
7684887Schin 			{
7694887Schin 				for (c = 1; c < 8; c++)
7704887Schin 					if ((num = swapget(mp->swap = c, p, 8) & mask) == ep->value.num)
7714887Schin 						goto swapped;
7724887Schin 			}
7734887Schin 			goto next;
7744887Schin 
7754887Schin 		case '!':
7764887Schin 			if (num != ep->value.num)
7774887Schin 				break;
7784887Schin 			goto next;
7794887Schin 
7804887Schin 		case '^':
7814887Schin 			if (num ^ ep->value.num)
7824887Schin 				break;
7834887Schin 			goto next;
7844887Schin 
7854887Schin 		case '>':
7864887Schin 			if (num > ep->value.num)
7874887Schin 				break;
7884887Schin 			goto next;
7894887Schin 
7904887Schin 		case '<':
7914887Schin 			if (num < ep->value.num)
7924887Schin 				break;
7934887Schin 			goto next;
7944887Schin 
7954887Schin 		case 'l':
7964887Schin 			if (num > 0 && mp->keep[level] && call < (MAXNEST - 1))
7974887Schin 			{
7984887Schin 				if (!ep->value.loop->count)
7994887Schin 				{
8004887Schin 					ep->value.loop->count = num;
8014887Schin 					ep->value.loop->offset = off;
8024887Schin 					off = ep->value.loop->start;
8034887Schin 				}
8044887Schin 				else if (!--ep->value.loop->count)
8054887Schin 				{
8064887Schin 					off = ep->value.loop->offset;
8074887Schin 					goto next;
8084887Schin 				}
8094887Schin 				else
8104887Schin 					off += ep->value.loop->size;
8114887Schin 				mp->ret[++call] = ep;
8124887Schin 				ep = ep->value.loop->lab;
8134887Schin 				goto fun;
8144887Schin 			}
8154887Schin 			goto next;
8164887Schin 
8174887Schin 		case 'm':
8184887Schin 			c = mp->swap;
8194887Schin 			t = ckmagic(mp, file, b + (b > buf), st, num);
8204887Schin 			mp->swap = c;
8214887Schin 			if (!t)
8224887Schin 				goto next;
8234887Schin 			if (b > buf)
8244887Schin 				*b = ' ';
8254887Schin 			b += strlen(b);
8264887Schin 			break;
8274887Schin 
8284887Schin 		case 'r':
8294887Schin #if _UWIN
8304887Schin 		{
8314887Schin 			char*			e;
8324887Schin 			Sfio_t*			rp;
8334887Schin 			Sfio_t*			gp;
8344887Schin 
8354887Schin 			if (!(t = strrchr(file, '.')))
8364887Schin 				goto next;
8374887Schin 			sfprintf(mp->tmp, "/reg/classes_root/%s", t);
8384887Schin 			if (!(t = sfstruse(mp->tmp)) || !(rp = sfopen(NiL, t, "r")))
8394887Schin 				goto next;
8404887Schin 			*ep->desc = 0;
8414887Schin 			*ep->mime = 0;
8424887Schin 			gp = 0;
8434887Schin 			while (t = sfgetr(rp, '\n', 1))
8444887Schin 			{
8454887Schin 				if (strneq(t, "Content Type=", 13))
8464887Schin 				{
8474887Schin 					ep->mime = vmnewof(mp->vm, ep->mime, char, sfvalue(rp), 0);
8484887Schin 					strcpy(ep->mime, t + 13);
8494887Schin 					if (gp)
8504887Schin 						break;
8514887Schin 				}
8524887Schin 				else
8534887Schin 				{
8544887Schin 					sfprintf(mp->tmp, "/reg/classes_root/%s", t);
8554887Schin 					if ((e = sfstruse(mp->tmp)) && (gp = sfopen(NiL, e, "r")))
8564887Schin 					{
8574887Schin 						ep->desc = vmnewof(mp->vm, ep->desc, char, strlen(t), 1);
8584887Schin 						strcpy(ep->desc, t);
8594887Schin 						if (*ep->mime)
8604887Schin 							break;
8614887Schin 					}
8624887Schin 				}
8634887Schin 			}
8644887Schin 			sfclose(rp);
8654887Schin 			if (!gp)
8664887Schin 				goto next;
8674887Schin 			if (!*ep->mime)
8684887Schin 			{
8694887Schin 				t = T(ep->desc);
8704887Schin 				if (!strncasecmp(t, "microsoft", 9))
8714887Schin 					t += 9;
8724887Schin 				while (isspace(*t))
8734887Schin 					t++;
8744887Schin 				e = "application/x-ms-";
8754887Schin 				ep->mime = vmnewof(mp->vm, ep->mime, char, strlen(t), strlen(e));
8764887Schin 				e = strcopy(ep->mime, e);
8774887Schin 				while ((c = *t++) && c != '.' && c != ' ')
8784887Schin 					*e++ = isupper(c) ? tolower(c) : c;
8794887Schin 				*e = 0;
8804887Schin 			}
8814887Schin 			while (t = sfgetr(gp, '\n', 1))
8824887Schin 				if (*t && !streq(t, "\"\""))
8834887Schin 				{
8844887Schin 					ep->desc = vmnewof(mp->vm, ep->desc, char, sfvalue(gp), 0);
8854887Schin 					strcpy(ep->desc, t);
8864887Schin 					break;
8874887Schin 				}
8884887Schin 			sfclose(gp);
8894887Schin 			if (!*ep->desc)
8904887Schin 				goto next;
8914887Schin 			if (!t)
8924887Schin 				for (t = T(ep->desc); *t; t++)
8934887Schin 					if (*t == '.')
8944887Schin 						*t = ' ';
8954887Schin 			if (!mp->keep[level])
8964887Schin 				mp->keep[level] = 2;
8974887Schin 			mp->mime = ep->mime;
8984887Schin 			break;
8994887Schin 		}
9004887Schin #else
9014887Schin 			if (ep->cont == '#' && !mp->keep[level])
9024887Schin 				mp->keep[level] = 1;
9034887Schin 			goto next;
9044887Schin #endif
9054887Schin 
9064887Schin 		case 'v':
9074887Schin 			if (!(p = getdata(mp, num, 4)))
9084887Schin 				goto next;
9094887Schin 			c = 0;
9104887Schin 			do
9114887Schin 			{
9124887Schin 				num++;
9134887Schin 				c = (c<<7) | (*p & 0x7f);
9144887Schin 			} while (*p++ & 0x80);
9154887Schin 			if (!(p = getdata(mp, num, c)))
9164887Schin 				goto next;
9174887Schin 			if (mp->keep[level]++ && b > buf && *(b - 1) != ' ')
9184887Schin 			{
9194887Schin 				*b++ = ',';
9204887Schin 				*b++ = ' ';
9214887Schin 			}
9224887Schin 			b = vcdecomp(b, buf + PATH_MAX, (unsigned char*)p, (unsigned char*)p + c);
9234887Schin 			goto checknest;
9244887Schin 
9254887Schin 		}
9264887Schin 	swapped:
9274887Schin 		q = T(ep->desc);
9284887Schin 		if (mp->keep[level]++ && b > buf && *(b - 1) != ' ' && *q && *q != ',' && *q != '.' && *q != '\b')
9294887Schin 			*b++ = ' ';
9304887Schin 		if (ep->type == 'd' || ep->type == 'D')
9314887Schin 			b += sfsprintf(b, PATH_MAX - (b - buf), q + (*q == '\b'), fmttime("%?%l", (time_t)num));
9324887Schin 		else if (ep->type == 'v')
9334887Schin 			b += sfsprintf(b, PATH_MAX - (b - buf), q + (*q == '\b'), fmtversion(num));
9344887Schin 		else
9354887Schin 			b += sfsprintf(b, PATH_MAX - (b - buf), q + (*q == '\b'), num);
9364887Schin 		if (ep->mime && *ep->mime)
9374887Schin 			mp->mime = ep->mime;
9384887Schin 	checknest:
9394887Schin 		if (ep->nest == '}')
9404887Schin 		{
9414887Schin 			if (!mp->keep[level])
9424887Schin 			{
9434887Schin 				b = mp->msg[level];
9444887Schin 				mp->mime = mp->cap[level];
9454887Schin 			}
9464887Schin 			else if (level > 0)
9474887Schin 				mp->keep[level - 1] = mp->keep[level];
9484887Schin 			if (--level < 0)
9494887Schin 			{
9504887Schin 				level = 0;
9514887Schin 				mp->keep[0] = 0;
9524887Schin 			}
9534887Schin 		}
9544887Schin 		continue;
9554887Schin 	next:
9564887Schin 		if (ep->cont == '&')
9574887Schin 			mp->keep[level] = 0;
9584887Schin 		goto checknest;
9594887Schin 	}
9604887Schin 	if (mp->keep[level] && b > buf)
9614887Schin 	{
9624887Schin 		*b = 0;
9634887Schin 		return buf;
9644887Schin 	}
9654887Schin 	return 0;
9664887Schin }
9674887Schin 
9684887Schin /*
9694887Schin  * check english language stats
9704887Schin  */
9714887Schin 
9724887Schin static int
ckenglish(register Magic_t * mp,int pun,int badpun)9734887Schin ckenglish(register Magic_t* mp, int pun, int badpun)
9744887Schin {
9754887Schin 	register char*	s;
9764887Schin 	register int	vowl = 0;
9774887Schin 	register int	freq = 0;
9784887Schin 	register int	rare = 0;
9794887Schin 
9804887Schin 	if (5 * badpun > pun)
9814887Schin 		return 0;
9824887Schin 	if (2 * mp->count[';'] > mp->count['E'] + mp->count['e'])
9834887Schin 		return 0;
9844887Schin 	if ((mp->count['>'] + mp->count['<'] + mp->count['/']) > mp->count['E'] + mp->count['e'])
9854887Schin 		return 0;
9864887Schin 	for (s = "aeiou"; *s; s++)
9874887Schin 		vowl += mp->count[toupper(*s)] + mp->count[*s];
9884887Schin 	for (s = "etaion"; *s; s++)
9894887Schin 		freq += mp->count[toupper(*s)] + mp->count[*s];
9904887Schin 	for (s = "vjkqxz"; *s; s++)
9914887Schin 		rare += mp->count[toupper(*s)] + mp->count[*s];
9924887Schin 	return 5 * vowl >= mp->fbsz - mp->count[' '] && freq >= 10 * rare;
9934887Schin }
9944887Schin 
9954887Schin /*
9964887Schin  * check programming language stats
9974887Schin  */
9984887Schin 
9994887Schin static char*
cklang(register Magic_t * mp,const char * file,char * buf,struct stat * st)10004887Schin cklang(register Magic_t* mp, const char* file, char* buf, struct stat* st)
10014887Schin {
10024887Schin 	register int		c;
10034887Schin 	register unsigned char*	b;
10044887Schin 	register unsigned char*	e;
10054887Schin 	register int		q;
10064887Schin 	register char*		s;
10074887Schin 	char*			t;
10084887Schin 	char*			base;
10094887Schin 	char*			suff;
10104887Schin 	char*			t1;
10114887Schin 	char*			t2;
10124887Schin 	char*			t3;
10134887Schin 	int			n;
10144887Schin 	int			badpun;
10154887Schin 	int			code;
10164887Schin 	int			pun;
10174887Schin 	Cctype_t		flags;
10184887Schin 	Info_t*			ip;
10194887Schin 
10204887Schin 	b = (unsigned char*)mp->fbuf;
10214887Schin 	e = b + mp->fbsz;
10224887Schin 	memzero(mp->count, sizeof(mp->count));
10234887Schin 	memzero(mp->multi, sizeof(mp->multi));
10244887Schin 	memzero(mp->identifier, sizeof(mp->identifier));
10254887Schin 
10264887Schin 	/*
10274887Schin 	 * check character coding
10284887Schin 	 */
10294887Schin 
10304887Schin 	flags = 0;
10314887Schin 	while (b < e)
10324887Schin 		flags |= mp->cctype[*b++];
10334887Schin 	b = (unsigned char*)mp->fbuf;
10344887Schin 	code = 0;
10354887Schin 	q = CC_ASCII;
10364887Schin 	n = CC_MASK;
10374887Schin 	for (c = 0; c < CC_MAPS; c++)
10384887Schin 	{
10394887Schin 		flags ^= CC_text;
10404887Schin 		if ((flags & CC_MASK) < n)
10414887Schin 		{
10424887Schin 			n = flags & CC_MASK;
10434887Schin 			q = c;
10444887Schin 		}
10454887Schin 		flags >>= CC_BIT;
10464887Schin 	}
10474887Schin 	flags = n;
10484887Schin 	if (!(flags & (CC_binary|CC_notext)))
10494887Schin 	{
10504887Schin 		if (q != CC_NATIVE)
10514887Schin 		{
10524887Schin 			code = q;
10534887Schin 			ccmaps(mp->fbuf, mp->fbsz, q, CC_NATIVE);
10544887Schin 		}
10554887Schin 		if (b[0] == '#' && b[1] == '!')
10564887Schin 		{
10574887Schin 			for (b += 2; b < e && isspace(*b); b++);
10584887Schin 			for (s = (char*)b; b < e && isprint(*b); b++);
10594887Schin 			c = *b;
10604887Schin 			*b = 0;
10614887Schin 			if ((st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) || match(s, "/*bin*/*") || !access(s, F_OK))
10624887Schin 			{
10634887Schin 				if (t = strrchr(s, '/'))
10644887Schin 					s = t + 1;
10654887Schin 				for (t = s; *t; t++)
10664887Schin 					if (isspace(*t))
10674887Schin 					{
10684887Schin 						*t = 0;
10694887Schin 						break;
10704887Schin 					}
10714887Schin 				sfsprintf(mp->mbuf, sizeof(mp->mbuf), "application/x-%s", *s ? s : "sh");
10724887Schin 				mp->mime = mp->mbuf;
10734887Schin 				if (match(s, "*sh"))
10744887Schin 				{
10754887Schin 					t1 = T("command");
10764887Schin 					if (streq(s, "sh"))
10774887Schin 						*s = 0;
10784887Schin 					else
10794887Schin 					{
10804887Schin 						*b++ = ' ';
10814887Schin 						*b = 0;
10824887Schin 					}
10834887Schin 				}
10844887Schin 				else
10854887Schin 				{
10864887Schin 					t1 = T("interpreter");
10874887Schin 					*b++ = ' ';
10884887Schin 					*b = 0;
10894887Schin 				}
10904887Schin 				sfsprintf(mp->sbuf, sizeof(mp->sbuf), T("%s%s script"), s, t1);
10914887Schin 				s = mp->sbuf;
10924887Schin 				goto qualify;
10934887Schin 			}
10944887Schin 			*b = c;
10954887Schin 			b = (unsigned char*)mp->fbuf;
10964887Schin 		}
10974887Schin 		badpun = 0;
10984887Schin 		pun = 0;
10994887Schin 		q = 0;
11004887Schin 		s = 0;
11014887Schin 		t = 0;
11024887Schin 		while (b < e)
11034887Schin 		{
11044887Schin 			c = *b++;
11054887Schin 			mp->count[c]++;
11064887Schin 			if (c == q && (q != '*' || *b == '/' && b++))
11074887Schin 			{
11084887Schin 				mp->multi[q]++;
11094887Schin 				q = 0;
11104887Schin 			}
11114887Schin 			else if (c == '\\')
11124887Schin 			{
11134887Schin 				s = 0;
11144887Schin 				b++;
11154887Schin 			}
11164887Schin 			else if (!q)
11174887Schin 			{
11184887Schin 				if (isalpha(c) || c == '_')
11194887Schin 				{
11204887Schin 					if (!s)
11214887Schin 						s = (char*)b - 1;
11224887Schin 				}
11234887Schin 				else if (!isdigit(c))
11244887Schin 				{
11254887Schin 					if (s)
11264887Schin 					{
11274887Schin 						if (s > mp->fbuf)
11284887Schin 							switch (*(s - 1))
11294887Schin 							{
11304887Schin 							case ':':
11314887Schin 								if (*b == ':')
11324887Schin 									mp->multi[':']++;
11334887Schin 								break;
11344887Schin 							case '.':
11354887Schin 								if (((char*)b - s) == 3 && (s == (mp->fbuf + 1) || *(s - 2) == '\n'))
11364887Schin 									mp->multi['.']++;
11374887Schin 								break;
11384887Schin 							case '\n':
11394887Schin 							case '\\':
11404887Schin 								if (*b == '{')
11414887Schin 									t = (char*)b + 1;
11424887Schin 								break;
11434887Schin 							case '{':
11444887Schin 								if (s == t && *b == '}')
11454887Schin 									mp->multi['X']++;
11464887Schin 								break;
11474887Schin 							}
11484887Schin 							if (!mp->idtab)
11494887Schin 							{
11504887Schin 								if (mp->idtab = dtnew(mp->vm, &mp->dtdisc, Dthash))
11514887Schin 									for (q = 0; q < elementsof(dict); q++)
11524887Schin 										dtinsert(mp->idtab, &dict[q]);
11534887Schin 								else if (mp->disc->errorf)
11544887Schin 									(*mp->disc->errorf)(mp, mp->disc, 3, "out of space");
11554887Schin 								q = 0;
11564887Schin 							}
11574887Schin 							if (mp->idtab)
11584887Schin 							{
11594887Schin 								*(b - 1) = 0;
11604887Schin 								if (ip = (Info_t*)dtmatch(mp->idtab, s))
11614887Schin 									mp->identifier[ip->value]++;
11624887Schin 								*(b - 1) = c;
11634887Schin 							}
11644887Schin 							s = 0;
11654887Schin 						}
11664887Schin 					switch (c)
11674887Schin 					{
11684887Schin 					case '\t':
11694887Schin 						if (b == (unsigned char*)(mp->fbuf + 1) || *(b - 2) == '\n')
11704887Schin 							mp->multi['\t']++;
11714887Schin 						break;
11724887Schin 					case '"':
11734887Schin 					case '\'':
11744887Schin 						q = c;
11754887Schin 						break;
11764887Schin 					case '/':
11774887Schin 						if (*b == '*')
11784887Schin 							q = *b++;
11794887Schin 						else if (*b == '/')
11804887Schin 							q = '\n';
11814887Schin 						break;
11824887Schin 					case '$':
11834887Schin 						if (*b == '(' && *(b + 1) != ' ')
11844887Schin 							mp->multi['$']++;
11854887Schin 						break;
11864887Schin 					case '{':
11874887Schin 					case '}':
11884887Schin 					case '[':
11894887Schin 					case ']':
11904887Schin 					case '(':
11914887Schin 						mp->multi[c]++;
11924887Schin 						break;
11934887Schin 					case ')':
11944887Schin 						mp->multi[c]++;
11954887Schin 						goto punctuation;
11964887Schin 					case ':':
11974887Schin 						if (*b == ':' && isspace(*(b + 1)) && b > (unsigned char*)(mp->fbuf + 1) && isspace(*(b - 2)))
11984887Schin 							mp->multi[':']++;
11994887Schin 						goto punctuation;
12004887Schin 					case '.':
12014887Schin 					case ',':
12024887Schin 					case '%':
12034887Schin 					case ';':
12044887Schin 					case '?':
12054887Schin 					punctuation:
12064887Schin 						pun++;
12074887Schin 						if (*b != ' ' && *b != '\n')
12084887Schin 							badpun++;
12094887Schin 						break;
12104887Schin 					}
12114887Schin 				}
12124887Schin 			}
12134887Schin 		}
12144887Schin 	}
12154887Schin 	else
12164887Schin 		while (b < e)
12174887Schin 			mp->count[*b++]++;
12184887Schin 	base = (t1 = strrchr(file, '/')) ? t1 + 1 : (char*)file;
12194887Schin 	suff = (t1 = strrchr(base, '.')) ? t1 + 1 : "";
12204887Schin 	if (!flags)
12214887Schin 	{
12224887Schin 		if (match(suff, "*sh|bat|cmd"))
12234887Schin 			goto id_sh;
12244887Schin 		if (match(base, "*@(mkfile)"))
12254887Schin 			goto id_mk;
12264887Schin 		if (match(base, "*@(makefile|.mk)"))
12274887Schin 			goto id_make;
12284887Schin 		if (match(base, "*@(mamfile|.mam)"))
12294887Schin 			goto id_mam;
12304887Schin 		if (match(suff, "[cly]?(pp|xx|++)|cc|ll|yy"))
12314887Schin 			goto id_c;
12324887Schin 		if (match(suff, "f"))
12334887Schin 			goto id_fortran;
12344887Schin 		if (match(suff, "htm+(l)"))
12354887Schin 			goto id_html;
12364887Schin 		if (match(suff, "cpy"))
12374887Schin 			goto id_copybook;
12384887Schin 		if (match(suff, "cob|cbl|cb2"))
12394887Schin 			goto id_cobol;
12404887Schin 		if (match(suff, "pl[1i]"))
12414887Schin 			goto id_pl1;
12424887Schin 		if (match(suff, "tex"))
12434887Schin 			goto id_tex;
12444887Schin 		if (match(suff, "asm|s"))
12454887Schin 			goto id_asm;
12464887Schin 		if ((st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) && (!suff || suff != strchr(suff, '.')))
12474887Schin 		{
12484887Schin 		id_sh:
12494887Schin 			s = T("command script");
12504887Schin 			mp->mime = "application/sh";
12514887Schin 			goto qualify;
12524887Schin 		}
12534887Schin 		if (strmatch(mp->fbuf, "From * [0-9][0-9]:[0-9][0-9]:[0-9][0-9] *"))
12544887Schin 		{
12554887Schin 			s = T("mail message");
12564887Schin 			mp->mime = "message/rfc822";
12574887Schin 			goto qualify;
12584887Schin 		}
12594887Schin 		if (match(base, "*@(mkfile)"))
12604887Schin 		{
12614887Schin 		id_mk:
12624887Schin 			s = "mkfile";
12634887Schin 			mp->mime = "application/mk";
12644887Schin 			goto qualify;
12654887Schin 		}
12664887Schin 		if (match(base, "*@(makefile|.mk)") || mp->multi['\t'] >= mp->count[':'] && (mp->multi['$'] > 0 || mp->multi[':'] > 0))
12674887Schin 		{
12684887Schin 		id_make:
12694887Schin 			s = "makefile";
12704887Schin 			mp->mime = "application/make";
12714887Schin 			goto qualify;
12724887Schin 		}
12734887Schin 		if (mp->multi['.'] >= 3)
12744887Schin 		{
12754887Schin 			s = T("nroff input");
12764887Schin 			mp->mime = "application/x-troff";
12774887Schin 			goto qualify;
12784887Schin 		}
12794887Schin 		if (mp->multi['X'] >= 3)
12804887Schin 		{
12814887Schin 			s = T("TeX input");
12824887Schin 			mp->mime = "application/x-tex";
12834887Schin 			goto qualify;
12844887Schin 		}
12854887Schin 		if (mp->fbsz < SF_BUFSIZE &&
12864887Schin 		    (mp->multi['('] == mp->multi[')'] &&
12874887Schin 		     mp->multi['{'] == mp->multi['}'] &&
12884887Schin 		     mp->multi['['] == mp->multi[']']) ||
12894887Schin 		    mp->fbsz >= SF_BUFSIZE &&
12904887Schin 		    (mp->multi['('] >= mp->multi[')'] &&
12914887Schin 		     mp->multi['{'] >= mp->multi['}'] &&
12924887Schin 		     mp->multi['['] >= mp->multi[']']))
12934887Schin 		{
12944887Schin 			c = mp->identifier[ID_INCL1];
12954887Schin 			if (c >= 2 && mp->identifier[ID_INCL2] >= c && mp->identifier[ID_INCL3] >= c && mp->count['.'] >= c ||
12964887Schin 			    mp->identifier[ID_C] >= 5 && mp->count[';'] >= 5 ||
12974887Schin 			    mp->count['='] >= 20 && mp->count[';'] >= 20)
12984887Schin 			{
12994887Schin 			id_c:
13004887Schin 				t1 = "";
13014887Schin 				t2 = "c ";
13024887Schin 				t3 = T("program");
13034887Schin 				switch (*suff)
13044887Schin 				{
13054887Schin 				case 'c':
13064887Schin 				case 'C':
13074887Schin 					mp->mime = "application/x-cc";
13084887Schin 					break;
13094887Schin 				case 'l':
13104887Schin 				case 'L':
13114887Schin 					t1 = "lex ";
13124887Schin 					mp->mime = "application/x-lex";
13134887Schin 					break;
13144887Schin 				default:
13154887Schin 					t3 = T("header");
13164887Schin 					if (mp->identifier[ID_YACC] < 5 || mp->count['%'] < 5)
13174887Schin 					{
13184887Schin 						mp->mime = "application/x-cc";
13194887Schin 						break;
13204887Schin 					}
13214887Schin 					/*FALLTHROUGH*/
13224887Schin 				case 'y':
13234887Schin 				case 'Y':
13244887Schin 					t1 = "yacc ";
13254887Schin 					mp->mime = "application/x-yacc";
13264887Schin 					break;
13274887Schin 				}
13284887Schin 				if (mp->identifier[ID_CPLUSPLUS] >= 3)
13294887Schin 				{
13304887Schin 					t2 = "c++ ";
13314887Schin 					mp->mime = "application/x-c++";
13324887Schin 				}
13334887Schin 				sfsprintf(mp->sbuf, sizeof(mp->sbuf), "%s%s%s", t1, t2, t3);
13344887Schin 				s = mp->sbuf;
13354887Schin 				goto qualify;
13364887Schin 			}
13374887Schin 		}
13384887Schin 		if (mp->identifier[ID_MAM1] >= 2 && mp->identifier[ID_MAM3] >= 2 &&
13394887Schin 		    (mp->fbsz < SF_BUFSIZE && mp->identifier[ID_MAM1] == mp->identifier[ID_MAM2] ||
13404887Schin 		     mp->fbsz >= SF_BUFSIZE && mp->identifier[ID_MAM1] >= mp->identifier[ID_MAM2]))
13414887Schin 		{
13424887Schin 		id_mam:
13434887Schin 			s = T("mam program");
13444887Schin 			mp->mime = "application/x-mam";
13454887Schin 			goto qualify;
13464887Schin 		}
13474887Schin 		if (mp->identifier[ID_FORTRAN] >= 8)
13484887Schin 		{
13494887Schin 		id_fortran:
13504887Schin 			s = T("fortran program");
13514887Schin 			mp->mime = "application/x-fortran";
13524887Schin 			goto qualify;
13534887Schin 		}
13544887Schin 		if (mp->identifier[ID_HTML] > 0 && mp->count['<'] >= 8 && (c = mp->count['<'] - mp->count['>']) >= -2 && c <= 2)
13554887Schin 		{
13564887Schin 		id_html:
13574887Schin 			s = T("html input");
13584887Schin 			mp->mime = "text/html";
13594887Schin 			goto qualify;
13604887Schin 		}
13614887Schin 		if (mp->identifier[ID_COPYBOOK] > 0 && mp->identifier[ID_COBOL] == 0 && (c = mp->count['('] - mp->count[')']) >= -2 && c <= 2)
13624887Schin 		{
13634887Schin 		id_copybook:
13644887Schin 			s = T("cobol copybook");
13654887Schin 			mp->mime = "application/x-cobol";
13664887Schin 			goto qualify;
13674887Schin 		}
13684887Schin 		if (mp->identifier[ID_COBOL] > 0 && mp->identifier[ID_COPYBOOK] > 0 && (c = mp->count['('] - mp->count[')']) >= -2 && c <= 2)
13694887Schin 		{
13704887Schin 		id_cobol:
13714887Schin 			s = T("cobol program");
13724887Schin 			mp->mime = "application/x-cobol";
13734887Schin 			goto qualify;
13744887Schin 		}
13754887Schin 		if (mp->identifier[ID_PL1] > 0 && (c = mp->count['('] - mp->count[')']) >= -2 && c <= 2)
13764887Schin 		{
13774887Schin 		id_pl1:
13784887Schin 			s = T("pl1 program");
13794887Schin 			mp->mime = "application/x-pl1";
13804887Schin 			goto qualify;
13814887Schin 		}
13824887Schin 		if (mp->count['{'] >= 6 && (c = mp->count['{'] - mp->count['}']) >= -2 && c <= 2 && mp->count['\\'] >= mp->count['{'])
13834887Schin 		{
13844887Schin 		id_tex:
13854887Schin 			s = T("TeX input");
13864887Schin 			mp->mime = "text/tex";
13874887Schin 			goto qualify;
13884887Schin 		}
13894887Schin 		if (mp->identifier[ID_ASM] >= 4)
13904887Schin 		{
13914887Schin 		id_asm:
13924887Schin 			s = T("as program");
13934887Schin 			mp->mime = "application/x-as";
13944887Schin 			goto qualify;
13954887Schin 		}
13964887Schin 		if (ckenglish(mp, pun, badpun))
13974887Schin 		{
13984887Schin 			s = T("english text");
13994887Schin 			mp->mime = "text/plain";
14004887Schin 			goto qualify;
14014887Schin 		}
14024887Schin 	}
14034887Schin 	else if (streq(base, "core"))
14044887Schin 	{
14054887Schin 		mp->mime = "x-system/core";
14064887Schin 		return T("core dump");
14074887Schin 	}
14084887Schin 	if (flags & (CC_binary|CC_notext))
14094887Schin 	{
14104887Schin 		b = (unsigned char*)mp->fbuf;
14114887Schin 		e = b + mp->fbsz;
14124887Schin 		n = 0;
14134887Schin 		for (;;)
14144887Schin 		{
14154887Schin 			c = *b++;
14164887Schin 			q = 0;
14174887Schin 			while (c & 0x80)
14184887Schin 			{
14194887Schin 				c <<= 1;
14204887Schin 				q++;
14214887Schin 			}
14224887Schin 			switch (q)
14234887Schin 			{
14244887Schin 			case 4:
14254887Schin 				if (b < e && (*b++ & 0xc0) != 0x80)
14264887Schin 					break;
14274887Schin 			case 3:
14284887Schin 				if (b < e && (*b++ & 0xc0) != 0x80)
14294887Schin 					break;
14304887Schin 			case 2:
14314887Schin 				if (b < e && (*b++ & 0xc0) != 0x80)
14324887Schin 					break;
14334887Schin 				n = 1;
14344887Schin 			case 0:
14354887Schin 				if (b >= e)
14364887Schin 				{
14374887Schin 					if (n)
14384887Schin 					{
14394887Schin 						flags &= ~(CC_binary|CC_notext);
14404887Schin 						flags |= CC_utf_8;
14414887Schin 					}
14424887Schin 					break;
14434887Schin 				}
14444887Schin 				continue;
14454887Schin 			}
14464887Schin 			break;
14474887Schin 		}
14484887Schin 	}
14494887Schin 	if (flags & (CC_binary|CC_notext))
14504887Schin 	{
14514887Schin 		unsigned long	d = 0;
14524887Schin 
14534887Schin 		if ((q = mp->fbsz / UCHAR_MAX) >= 2)
14544887Schin 		{
14554887Schin 			/*
14564887Schin 			 * compression/encryption via standard deviation
14574887Schin 			 */
14584887Schin 
14594887Schin 
14604887Schin 			for (c = 0; c < UCHAR_MAX; c++)
14614887Schin 			{
14624887Schin 				pun = mp->count[c] - q;
14634887Schin 				d += pun * pun;
14644887Schin 			}
14654887Schin 			d /= mp->fbsz;
14664887Schin 		}
14674887Schin 		if (d <= 0)
14684887Schin 			s = T("binary");
14694887Schin 		else if (d < 4)
14704887Schin 			s = T("encrypted");
14714887Schin 		else if (d < 16)
14724887Schin 			s = T("packed");
14734887Schin 		else if (d < 64)
14744887Schin 			s = T("compressed");
14754887Schin 		else if (d < 256)
14764887Schin 			s = T("delta");
14774887Schin 		else
14784887Schin 			s = T("data");
14794887Schin 		mp->mime = "application/octet-stream";
14804887Schin 		return s;
14814887Schin 	}
14824887Schin 	mp->mime = "text/plain";
14834887Schin 	if (flags & CC_utf_8)
14844887Schin 		s = (flags & CC_control) ? T("utf-8 text with control characters") : T("utf-8 text");
14854887Schin 	else if (flags & CC_latin)
14864887Schin 		s = (flags & CC_control) ? T("latin text with control characters") : T("latin text");
14874887Schin 	else
14884887Schin 		s = (flags & CC_control) ? T("text with control characters") : T("text");
14894887Schin  qualify:
14904887Schin 	if (!flags && mp->count['\n'] >= mp->count['\r'] && mp->count['\n'] <= (mp->count['\r'] + 1) && mp->count['\r'])
14914887Schin 	{
14924887Schin 		t = "dos ";
14934887Schin 		mp->mime = "text/dos";
14944887Schin 	}
14954887Schin 	else
14964887Schin 		t = "";
14974887Schin 	if (code)
14984887Schin 	{
14994887Schin 		if (code == CC_ASCII)
15004887Schin 			sfsprintf(buf, PATH_MAX, "ascii %s%s", t, s);
15014887Schin 		else
15024887Schin 		{
15034887Schin 			sfsprintf(buf, PATH_MAX, "ebcdic%d %s%s", code - 1, t, s);
15044887Schin 			mp->mime = "text/ebcdic";
15054887Schin 		}
15064887Schin 		s = buf;
15074887Schin 	}
15084887Schin 	else if (*t)
15094887Schin 	{
15104887Schin 		sfsprintf(buf, PATH_MAX, "%s%s", t, s);
15114887Schin 		s = buf;
15124887Schin 	}
15134887Schin 	return s;
15144887Schin }
15154887Schin 
15164887Schin /*
15174887Schin  * return the basic magic string for file,st in buf,size
15184887Schin  */
15194887Schin 
15204887Schin static char*
type(register Magic_t * mp,const char * file,struct stat * st,char * buf,int size)15214887Schin type(register Magic_t* mp, const char* file, struct stat* st, char* buf, int size)
15224887Schin {
15234887Schin 	register char*	s;
15244887Schin 	register char*	t;
15254887Schin 
15264887Schin 	mp->mime = 0;
15274887Schin 	if (!S_ISREG(st->st_mode))
15284887Schin 	{
15294887Schin 		if (S_ISDIR(st->st_mode))
15304887Schin 		{
15314887Schin 			mp->mime = "x-system/dir";
15324887Schin 			return T("directory");
15334887Schin 		}
15344887Schin 		if (S_ISLNK(st->st_mode))
15354887Schin 		{
15364887Schin 			mp->mime = "x-system/lnk";
15374887Schin 			s = buf;
15384887Schin 			s += sfsprintf(s, PATH_MAX, T("symbolic link to "));
15394887Schin 			if (pathgetlink(file, s, size - (s - buf)) < 0)
15404887Schin 				return T("cannot read symbolic link text");
15414887Schin 			return buf;
15424887Schin 		}
15434887Schin 		if (S_ISBLK(st->st_mode))
15444887Schin 		{
15454887Schin 			mp->mime = "x-system/blk";
15464887Schin 			sfsprintf(buf, PATH_MAX, T("block special (%s)"), fmtdev(st));
15474887Schin 			return buf;
15484887Schin 		}
15494887Schin 		if (S_ISCHR(st->st_mode))
15504887Schin 		{
15514887Schin 			mp->mime = "x-system/chr";
15524887Schin 			sfsprintf(buf, PATH_MAX, T("character special (%s)"), fmtdev(st));
15534887Schin 			return buf;
15544887Schin 		}
15554887Schin 		if (S_ISFIFO(st->st_mode))
15564887Schin 		{
15574887Schin 			mp->mime = "x-system/fifo";
15584887Schin 			return "fifo";
15594887Schin 		}
15604887Schin #ifdef S_ISSOCK
15614887Schin 		if (S_ISSOCK(st->st_mode))
15624887Schin 		{
15634887Schin 			mp->mime = "x-system/sock";
15644887Schin 			return "socket";
15654887Schin 		}
15664887Schin #endif
15674887Schin 	}
15684887Schin 	if (!(mp->fbmx = st->st_size))
15694887Schin 		s = T("empty");
15704887Schin 	else if (!mp->fp)
15714887Schin 		s = T("cannot read");
15724887Schin 	else
15734887Schin 	{
15744887Schin 		mp->fbsz = sfread(mp->fp, mp->fbuf, sizeof(mp->fbuf) - 1);
15754887Schin 		if (mp->fbsz < 0)
15764887Schin 			s = fmterror(errno);
15774887Schin 		else if (mp->fbsz == 0)
15784887Schin 			s = T("empty");
15794887Schin 		else
15804887Schin 		{
15814887Schin 			mp->fbuf[mp->fbsz] = 0;
15824887Schin 			mp->xoff = 0;
15834887Schin 			mp->xbsz = 0;
15844887Schin 			if (!(s = ckmagic(mp, file, buf, st, 0)))
15854887Schin 				s = cklang(mp, file, buf, st);
15864887Schin 		}
15874887Schin 	}
15884887Schin 	if (!mp->mime)
15894887Schin 		mp->mime = "application/unknown";
15904887Schin 	else if ((t = strchr(mp->mime, '%')) && *(t + 1) == 's' && !*(t + 2))
15914887Schin 	{
15924887Schin 		register char*	b;
15934887Schin 		register char*	be;
15944887Schin 		register char*	m;
15954887Schin 		register char*	me;
15964887Schin 
15974887Schin 		b = mp->mime;
15984887Schin 		me = (m = mp->mime = mp->fbuf) + sizeof(mp->fbuf) - 1;
15994887Schin 		while (m < me && b < t)
16004887Schin 			*m++ = *b++;
16014887Schin 		b = t = s;
16024887Schin 		for (;;)
16034887Schin 		{
16044887Schin 			if (!(be = strchr(t, ' ')))
16054887Schin 			{
16064887Schin 				be = b + strlen(b);
16074887Schin 				break;
16084887Schin 			}
16094887Schin 			if (*(be - 1) == ',' || strneq(be + 1, "data", 4) || strneq(be + 1, "file", 4))
16104887Schin 				break;
16114887Schin 			b = t;
16124887Schin 			t = be + 1;
16134887Schin 		}
16144887Schin 		while (m < me && b < be)
16154887Schin 			if ((*m++ = *b++) == ' ')
16164887Schin 				*(m - 1) = '-';
16174887Schin 		*m = 0;
16184887Schin 	}
16194887Schin 	return s;
16204887Schin }
16214887Schin 
16224887Schin /*
16234887Schin  * low level for magicload()
16244887Schin  */
16254887Schin 
16264887Schin static int
load(register Magic_t * mp,char * file,register Sfio_t * fp)16274887Schin load(register Magic_t* mp, char* file, register Sfio_t* fp)
16284887Schin {
16294887Schin 	register Entry_t*	ep;
16304887Schin 	register char*		p;
16314887Schin 	register char*		p2;
16324887Schin 	char*			p3;
16334887Schin 	char*			next;
16344887Schin 	int			n;
16354887Schin 	int			lge;
16364887Schin 	int			lev;
16374887Schin 	int			ent;
16384887Schin 	int			old;
16394887Schin 	int			cont;
16404887Schin 	Info_t*			ip;
16414887Schin 	Entry_t*		ret;
16424887Schin 	Entry_t*		first;
16434887Schin 	Entry_t*		last = 0;
16444887Schin 	Entry_t*		fun['z' - 'a' + 1];
16454887Schin 
16464887Schin 	memzero(fun, sizeof(fun));
16474887Schin 	cont = '$';
16484887Schin 	ent = 0;
16494887Schin 	lev = 0;
16504887Schin 	old = 0;
16514887Schin 	ret = 0;
16524887Schin 	error_info.file = file;
16534887Schin 	error_info.line = 0;
16544887Schin 	first = ep = vmnewof(mp->vm, 0, Entry_t, 1, 0);
16554887Schin 	while (p = sfgetr(fp, '\n', 1))
16564887Schin 	{
16574887Schin 		error_info.line++;
16584887Schin 		for (; isspace(*p); p++);
16594887Schin 
16604887Schin 		/*
16614887Schin 		 * nesting
16624887Schin 		 */
16634887Schin 
16644887Schin 		switch (*p)
16654887Schin 		{
16664887Schin 		case 0:
16674887Schin 		case '#':
16684887Schin 			cont = '#';
16694887Schin 			continue;
16704887Schin 		case '{':
16714887Schin 			if (++lev < MAXNEST)
16724887Schin 				ep->nest = *p;
16734887Schin 			else if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
16744887Schin 				(*mp->disc->errorf)(mp, mp->disc, 1, "{ ... } operator nesting too deep -- %d max", MAXNEST);
16754887Schin 			continue;
16764887Schin 		case '}':
16774887Schin 			if (!last || lev <= 0)
16784887Schin 			{
16794887Schin 				if (mp->disc->errorf)
16804887Schin 					(*mp->disc->errorf)(mp, mp->disc, 2, "`%c': invalid nesting", *p);
16814887Schin 			}
16824887Schin 			else if (lev-- == ent)
16834887Schin 			{
16844887Schin 				ent = 0;
16854887Schin 				ep->cont = ':';
16864887Schin 				ep->offset = ret->offset;
16874887Schin 				ep->nest = ' ';
16884887Schin 				ep->type = ' ';
16894887Schin 				ep->op = ' ';
16904887Schin 				ep->desc = "[RETURN]";
16914887Schin 				last = ep;
16924887Schin 				ep = ret->next = vmnewof(mp->vm, 0, Entry_t, 1, 0);
16934887Schin 				ret = 0;
16944887Schin 			}
16954887Schin 			else
16964887Schin 				last->nest = *p;
16974887Schin 			continue;
16984887Schin 		default:
16994887Schin 			if (*(p + 1) == '{' || *(p + 1) == '(' && *p != '+' && *p != '>' && *p != '&' && *p != '|')
17004887Schin 			{
17014887Schin 				n = *p++;
17024887Schin 				if (n >= 'a' && n <= 'z')
17034887Schin 					n -= 'a';
17044887Schin 				else
17054887Schin 				{
17064887Schin 					if (mp->disc->errorf)
17074887Schin 						(*mp->disc->errorf)(mp, mp->disc, 2, "%c: invalid function name", n);
17084887Schin 					n = 0;
17094887Schin 				}
17104887Schin 				if (ret && mp->disc->errorf)
17114887Schin 					(*mp->disc->errorf)(mp, mp->disc, 2, "%c: function has no return", ret->offset + 'a');
17124887Schin 				if (*p == '{')
17134887Schin 				{
17144887Schin 					ent = ++lev;
17154887Schin 					ret = ep;
17164887Schin 					ep->desc = "[FUNCTION]";
17174887Schin 				}
17184887Schin 				else
17194887Schin 				{
17204887Schin 					if (*(p + 1) != ')' && mp->disc->errorf)
17214887Schin 						(*mp->disc->errorf)(mp, mp->disc, 2, "%c: invalid function call argument list", n + 'a');
17224887Schin 					ep->desc = "[CALL]";
17234887Schin 				}
17244887Schin 				ep->cont = cont;
17254887Schin 				ep->offset = n;
17264887Schin 				ep->nest = ' ';
17274887Schin 				ep->type = ' ';
17284887Schin 				ep->op = ' ';
17294887Schin 				last = ep;
17304887Schin 				ep = ep->next = vmnewof(mp->vm, 0, Entry_t, 1, 0);
17314887Schin 				if (ret)
17324887Schin 					fun[n] = last->value.lab = ep;
17334887Schin 				else if (!(last->value.lab = fun[n]) && mp->disc->errorf)
17344887Schin 					(*mp->disc->errorf)(mp, mp->disc, 2, "%c: function not defined", n + 'a');
17354887Schin 				continue;
17364887Schin 			}
17374887Schin 			if (!ep->nest)
17384887Schin 				ep->nest = (lev > 0 && lev != ent) ? ('0' + lev - !!ent) : ' ';
17394887Schin 			break;
17404887Schin 		}
17414887Schin 
17424887Schin 		/*
17434887Schin 		 * continuation
17444887Schin 		 */
17454887Schin 
17464887Schin 		cont = '$';
17474887Schin 		switch (*p)
17484887Schin 		{
17494887Schin 		case '>':
17504887Schin 			old = 1;
17514887Schin 			if (*(p + 1) == *p)
17524887Schin 			{
17534887Schin 				/*
17544887Schin 				 * old style nesting push
17554887Schin 				 */
17564887Schin 
17574887Schin 				p++;
17584887Schin 				old = 2;
17594887Schin 				if (!lev && last)
17604887Schin 				{
17614887Schin 					lev = 1;
17624887Schin 					last->nest = '{';
17634887Schin 					if (last->cont == '>')
17644887Schin 						last->cont = '&';
17654887Schin 					ep->nest = '1';
17664887Schin 				}
17674887Schin 			}
17684887Schin 			/*FALLTHROUGH*/
17694887Schin 		case '+':
17704887Schin 		case '&':
17714887Schin 		case '|':
17724887Schin 			ep->cont = *p++;
17734887Schin 			break;
17744887Schin 		default:
17754887Schin 			if ((mp->flags & MAGIC_VERBOSE) && !isalpha(*p) && mp->disc->errorf)
17764887Schin 				(*mp->disc->errorf)(mp, mp->disc, 1, "`%c': invalid line continuation operator", *p);
17774887Schin 			/*FALLTHROUGH*/
17784887Schin 		case '*':
17794887Schin 		case '0': case '1': case '2': case '3': case '4':
17804887Schin 		case '5': case '6': case '7': case '8': case '9':
17814887Schin 			ep->cont = (lev > 0) ? '&' : '#';
17824887Schin 			break;
17834887Schin 		}
17844887Schin 		switch (old)
17854887Schin 		{
17864887Schin 		case 1:
17874887Schin 			old = 0;
17884887Schin 			if (lev)
17894887Schin 			{
17904887Schin 				/*
17914887Schin 				 * old style nesting pop
17924887Schin 				 */
17934887Schin 
17944887Schin 				lev = 0;
17954887Schin 				if (last)
17964887Schin 					last->nest = '}';
17974887Schin 				ep->nest = ' ';
17984887Schin 				if (ep->cont == '&')
17994887Schin 					ep->cont = '#';
18004887Schin 			}
18014887Schin 			break;
18024887Schin 		case 2:
18034887Schin 			old = 1;
18044887Schin 			break;
18054887Schin 		}
18064887Schin 		if (isdigit(*p))
18074887Schin 		{
18084887Schin 			/*
18094887Schin 			 * absolute offset
18104887Schin 			 */
18114887Schin 
18124887Schin 			ep->offset = strton(p, &next, NiL, 0);
18134887Schin 			p2 = next;
18144887Schin 		}
18154887Schin 		else
18164887Schin 		{
18174887Schin 			for (p2 = p; *p2 && !isspace(*p2); p2++);
18184887Schin 			if (!*p2)
18194887Schin 			{
18204887Schin 				if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
18214887Schin 					(*mp->disc->errorf)(mp, mp->disc, 1, "not enough fields: `%s'", p);
18224887Schin 				continue;
18234887Schin 			}
18244887Schin 
18254887Schin 			/*
18264887Schin 			 * offset expression
18274887Schin 			 */
18284887Schin 
18294887Schin 			*p2++ = 0;
18304887Schin 			ep->expr = vmstrdup(mp->vm, p);
18314887Schin 			if (isalpha(*p))
18324887Schin 				ep->offset = (ip = (Info_t*)dtmatch(mp->infotab, p)) ? ip->value : 0;
18334887Schin 			else if (*p == '(' && ep->cont == '>')
18344887Schin 			{
18354887Schin 				/*
18364887Schin 				 * convert old style indirection to @
18374887Schin 				 */
18384887Schin 
18394887Schin 				p = ep->expr + 1;
18404887Schin 				for (;;)
18414887Schin 				{
18424887Schin 					switch (*p++)
18434887Schin 					{
18444887Schin 					case 0:
18454887Schin 					case '@':
18464887Schin 					case '(':
18474887Schin 						break;
18484887Schin 					case ')':
18494887Schin 						break;
18504887Schin 					default:
18514887Schin 						continue;
18524887Schin 					}
18534887Schin 					break;
18544887Schin 				}
18554887Schin 				if (*--p == ')')
18564887Schin 				{
18574887Schin 					*p = 0;
18584887Schin 					*ep->expr = '@';
18594887Schin 				}
18604887Schin 			}
18614887Schin 		}
18624887Schin 		for (; isspace(*p2); p2++);
18634887Schin 		for (p = p2; *p2 && !isspace(*p2); p2++);
18644887Schin 		if (!*p2)
18654887Schin 		{
18664887Schin 			if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
18674887Schin 				(*mp->disc->errorf)(mp, mp->disc, 1, "not enough fields: `%s'", p);
18684887Schin 			continue;
18694887Schin 		}
18704887Schin 		*p2++ = 0;
18714887Schin 
18724887Schin 		/*
18734887Schin 		 * type
18744887Schin 		 */
18754887Schin 
18764887Schin 		if ((*p == 'b' || *p == 'l') && *(p + 1) == 'e')
18774887Schin 		{
18784887Schin 			ep->swap = ~(*p == 'l' ? 7 : 0);
18794887Schin 			p += 2;
18804887Schin 		}
18814887Schin 		if (*p == 's')
18824887Schin 		{
18834887Schin 			if (*(p + 1) == 'h')
18844887Schin 				ep->type = 'h';
18854887Schin 			else
18864887Schin 				ep->type = 's';
18874887Schin 		}
18884887Schin 		else if (*p == 'a')
18894887Schin 			ep->type = 's';
18904887Schin 		else
18914887Schin 			ep->type = *p;
18924887Schin 		if (p = strchr(p, '&'))
18934887Schin 		{
18944887Schin 			/*
18954887Schin 			 * old style mask
18964887Schin 			 */
18974887Schin 
18984887Schin 			ep->mask = strton(++p, NiL, NiL, 0);
18994887Schin 		}
19004887Schin 		for (; isspace(*p2); p2++);
19014887Schin 		if (ep->mask)
19024887Schin 			*--p2 = '=';
19034887Schin 
19044887Schin 		/*
19054887Schin 		 * comparison operation
19064887Schin 		 */
19074887Schin 
19084887Schin 		p = p2;
19094887Schin 		if (p2 = strchr(p, '\t'))
19104887Schin 			*p2++ = 0;
19114887Schin 		else
19124887Schin 		{
19134887Schin 			int	qe = 0;
19144887Schin 			int	qn = 0;
19154887Schin 
19164887Schin 			/*
19174887Schin 			 * assume balanced {}[]()\\""'' field
19184887Schin 			 */
19194887Schin 
19204887Schin 			for (p2 = p;;)
19214887Schin 			{
19224887Schin 				switch (n = *p2++)
19234887Schin 				{
19244887Schin 				case 0:
19254887Schin 					break;
19264887Schin 				case '{':
19274887Schin 					if (!qe)
19284887Schin 						qe = '}';
19294887Schin 					if (qe == '}')
19304887Schin 						qn++;
19314887Schin 					continue;
19324887Schin 				case '(':
19334887Schin 					if (!qe)
19344887Schin 						qe = ')';
19354887Schin 					if (qe == ')')
19364887Schin 						qn++;
19374887Schin 					continue;
19384887Schin 				case '[':
19394887Schin 					if (!qe)
19404887Schin 						qe = ']';
19414887Schin 					if (qe == ']')
19424887Schin 						qn++;
19434887Schin 					continue;
19444887Schin 				case '}':
19454887Schin 				case ')':
19464887Schin 				case ']':
19474887Schin 					if (qe == n && qn > 0)
19484887Schin 						qn--;
19494887Schin 					continue;
19504887Schin 				case '"':
19514887Schin 				case '\'':
19524887Schin 					if (!qe)
19534887Schin 						qe = n;
19544887Schin 					else if (qe == n)
19554887Schin 						qe = 0;
19564887Schin 					continue;
19574887Schin 				case '\\':
19584887Schin 					if (*p2)
19594887Schin 						p2++;
19604887Schin 					continue;
19614887Schin 				default:
19624887Schin 					if (!qe && isspace(n))
19634887Schin 						break;
19644887Schin 					continue;
19654887Schin 				}
19664887Schin 				if (n)
19674887Schin 					*(p2 - 1) = 0;
19684887Schin 				else
19694887Schin 					p2--;
19704887Schin 				break;
19714887Schin 			}
19724887Schin 		}
19734887Schin 		lge = 0;
19744887Schin 		if (ep->type == 'e' || ep->type == 'm' || ep->type == 's')
19754887Schin 			ep->op = '=';
19764887Schin 		else
19774887Schin 		{
19784887Schin 			if (*p == '&')
19794887Schin 			{
19804887Schin 				ep->mask = strton(++p, &next, NiL, 0);
19814887Schin 				p = next;
19824887Schin 			}
19834887Schin 			switch (*p)
19844887Schin 			{
19854887Schin 			case '=':
19864887Schin 			case '>':
19874887Schin 			case '<':
19884887Schin 			case '*':
19894887Schin 				ep->op = *p++;
19904887Schin 				if (*p == '=')
19914887Schin 				{
19924887Schin 					p++;
19934887Schin 					switch (ep->op)
19944887Schin 					{
19954887Schin 					case '>':
19964887Schin 						lge = -1;
19974887Schin 						break;
19984887Schin 					case '<':
19994887Schin 						lge = 1;
20004887Schin 						break;
20014887Schin 					}
20024887Schin 				}
20034887Schin 				break;
20044887Schin 			case '!':
20054887Schin 			case '@':
20064887Schin 				ep->op = *p++;
20074887Schin 				if (*p == '=')
20084887Schin 					p++;
20094887Schin 				break;
20104887Schin 			case 'x':
20114887Schin 				p++;
20124887Schin 				ep->op = '*';
20134887Schin 				break;
20144887Schin 			default:
20154887Schin 				ep->op = '=';
20164887Schin 				if (ep->mask)
20174887Schin 					ep->value.num = ep->mask;
20184887Schin 				break;
20194887Schin 			}
20204887Schin 		}
20214887Schin 		if (ep->op != '*' && !ep->value.num)
20224887Schin 		{
20234887Schin 			if (ep->type == 'e')
20244887Schin 			{
20254887Schin 				if (ep->value.sub = vmnewof(mp->vm, 0, regex_t, 1, 0))
20264887Schin 				{
20274887Schin 					ep->value.sub->re_disc = &mp->redisc;
20284887Schin 					if (!(n = regcomp(ep->value.sub, p, REG_DELIMITED|REG_LENIENT|REG_NULL|REG_DISCIPLINE)))
20294887Schin 					{
20304887Schin 						p += ep->value.sub->re_npat;
20314887Schin 						if (!(n = regsubcomp(ep->value.sub, p, NiL, 0, 0)))
20324887Schin 							p += ep->value.sub->re_npat;
20334887Schin 					}
20344887Schin 					if (n)
20354887Schin 					{
20364887Schin 						regmessage(mp, ep->value.sub, n);
20374887Schin 						ep->value.sub = 0;
20384887Schin 					}
20394887Schin 					else if (*p && mp->disc->errorf)
20404887Schin 						(*mp->disc->errorf)(mp, mp->disc, 1, "invalid characters after substitution: %s", p);
20414887Schin 				}
20424887Schin 			}
20434887Schin 			else if (ep->type == 'm')
20444887Schin 			{
20454887Schin 				ep->mask = stresc(p) + 1;
20464887Schin 				ep->value.str = vmnewof(mp->vm, 0, char, ep->mask + 1, 0);
20474887Schin 				memcpy(ep->value.str, p, ep->mask);
20484887Schin 				if ((!ep->expr || !ep->offset) && !strmatch(ep->value.str, "\\!\\(*\\)"))
20494887Schin 					ep->value.str[ep->mask - 1] = '*';
20504887Schin 			}
20514887Schin 			else if (ep->type == 's')
20524887Schin 			{
20534887Schin 				ep->mask = stresc(p);
20544887Schin 				ep->value.str = vmnewof(mp->vm, 0, char, ep->mask, 0);
20554887Schin 				memcpy(ep->value.str, p, ep->mask);
20564887Schin 			}
20574887Schin 			else if (*p == '\'')
20584887Schin 			{
20594887Schin 				stresc(p);
20604887Schin 				ep->value.num = *(unsigned char*)(p + 1) + lge;
20614887Schin 			}
20624887Schin 			else if (strmatch(p, "+([a-z])\\(*\\)"))
20634887Schin 			{
20644887Schin 				char*	t;
20654887Schin 
20664887Schin 				t = p;
20674887Schin 				ep->type = 'V';
20684887Schin 				ep->op = *p;
20694887Schin 				while (*p && *p++ != '(');
20704887Schin 				switch (ep->op)
20714887Schin 				{
20724887Schin 				case 'l':
20734887Schin 					n = *p++;
20744887Schin 					if (n < 'a' || n > 'z')
20754887Schin 					{
20764887Schin 						if (mp->disc->errorf)
20774887Schin 							(*mp->disc->errorf)(mp, mp->disc, 2, "%c: invalid function name", n);
20784887Schin 					}
20794887Schin 					else if (!fun[n -= 'a'])
20804887Schin 					{
20814887Schin 						if (mp->disc->errorf)
20824887Schin 							(*mp->disc->errorf)(mp, mp->disc, 2, "%c: function not defined", n + 'a');
20834887Schin 					}
20844887Schin 					else
20854887Schin 					{
20864887Schin 						ep->value.loop = vmnewof(mp->vm, 0, Loop_t, 1, 0);
20874887Schin 						ep->value.loop->lab = fun[n];
20884887Schin 						while (*p && *p++ != ',');
20894887Schin 						ep->value.loop->start = strton(p, &t, NiL, 0);
20904887Schin 						while (*t && *t++ != ',');
20914887Schin 						ep->value.loop->size = strton(t, &t, NiL, 0);
20924887Schin 					}
20934887Schin 					break;
20944887Schin 				case 'm':
20954887Schin 				case 'r':
20964887Schin 					ep->desc = vmnewof(mp->vm, 0, char, 32, 0);
20974887Schin 					ep->mime = vmnewof(mp->vm, 0, char, 32, 0);
20984887Schin 					break;
20994887Schin 				case 'v':
21004887Schin 					break;
21014887Schin 				default:
21024887Schin 					if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
21034887Schin 						(*mp->disc->errorf)(mp, mp->disc, 1, "%-.*s: unknown function", p - t, t);
21044887Schin 					break;
21054887Schin 				}
21064887Schin 			}
21074887Schin 			else
21084887Schin 			{
21094887Schin 				ep->value.num = strton(p, NiL, NiL, 0) + lge;
21104887Schin 				if (ep->op == '@')
21114887Schin 					ep->value.num = swapget(0, (char*)&ep->value.num, sizeof(ep->value.num));
21124887Schin 			}
21134887Schin 		}
21144887Schin 
21154887Schin 		/*
21164887Schin 		 * file description
21174887Schin 		 */
21184887Schin 
21194887Schin 		if (p2)
21204887Schin 		{
21214887Schin 			for (; isspace(*p2); p2++);
21224887Schin 			if (p = strchr(p2, '\t'))
21234887Schin 			{
21244887Schin 				/*
21254887Schin 				 * check for message catalog index
21264887Schin 				 */
21274887Schin 
21284887Schin 				*p++ = 0;
21294887Schin 				if (isalpha(*p2))
21304887Schin 				{
21314887Schin 					for (p3 = p2; isalnum(*p3); p3++);
21324887Schin 					if (*p3++ == ':')
21334887Schin 					{
21344887Schin 						for (; isdigit(*p3); p3++);
21354887Schin 						if (!*p3)
21364887Schin 						{
21374887Schin 							for (p2 = p; isspace(*p2); p2++);
21384887Schin 							if (p = strchr(p2, '\t'))
21394887Schin 								*p++ = 0;
21404887Schin 						}
21414887Schin 					}
21424887Schin 				}
21434887Schin 			}
21444887Schin 			stresc(p2);
21454887Schin 			ep->desc = vmstrdup(mp->vm, p2);
21464887Schin 			if (p)
21474887Schin 			{
21484887Schin 				for (; isspace(*p); p++);
21494887Schin 				if (*p)
21504887Schin 					ep->mime = vmstrdup(mp->vm, p);
21514887Schin 			}
21524887Schin 		}
21534887Schin 		else
21544887Schin 			ep->desc = "";
21554887Schin 
21564887Schin 		/*
21574887Schin 		 * get next entry
21584887Schin 		 */
21594887Schin 
21604887Schin 		last = ep;
21614887Schin 		ep = ep->next = vmnewof(mp->vm, 0, Entry_t, 1, 0);
21624887Schin 	}
21634887Schin 	if (last)
21644887Schin 	{
21654887Schin 		last->next = 0;
21664887Schin 		if (mp->magiclast)
21674887Schin 			mp->magiclast->next = first;
21684887Schin 		else
21694887Schin 			mp->magic = first;
21704887Schin 		mp->magiclast = last;
21714887Schin 	}
21724887Schin 	vmfree(mp->vm, ep);
21734887Schin 	if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
21744887Schin 	{
21754887Schin 		if (lev < 0)
21764887Schin 			(*mp->disc->errorf)(mp, mp->disc, 1, "too many } operators");
21774887Schin 		else if (lev > 0)
21784887Schin 			(*mp->disc->errorf)(mp, mp->disc, 1, "not enough } operators");
21794887Schin 		if (ret)
21804887Schin 			(*mp->disc->errorf)(mp, mp->disc, 2, "%c: function has no return", ret->offset + 'a');
21814887Schin 	}
21824887Schin 	error_info.file = 0;
21834887Schin 	error_info.line = 0;
21844887Schin 	return 0;
21854887Schin }
21864887Schin 
21874887Schin /*
21884887Schin  * load a magic file into mp
21894887Schin  */
21904887Schin 
21914887Schin int
magicload(register Magic_t * mp,const char * file,unsigned long flags)21924887Schin magicload(register Magic_t* mp, const char* file, unsigned long flags)
21934887Schin {
21944887Schin 	register char*		s;
21954887Schin 	register char*		e;
21964887Schin 	register char*		t;
21974887Schin 	int			n;
21984887Schin 	int			found;
21994887Schin 	int			list;
22004887Schin 	Sfio_t*			fp;
22014887Schin 
22024887Schin 	mp->flags = mp->disc->flags | flags;
22034887Schin 	found = 0;
22044887Schin 	if (list = !(s = (char*)file) || !*s || (*s == '-' || *s == '.') && !*(s + 1))
22054887Schin 	{
22064887Schin 		if (!(s = getenv(MAGIC_FILE_ENV)) || !*s)
22074887Schin 			s = MAGIC_FILE;
22084887Schin 	}
22094887Schin 	for (;;)
22104887Schin 	{
22114887Schin 		if (!list)
22124887Schin 			e = 0;
22134887Schin 		else if (e = strchr(s, ':'))
22144887Schin 		{
22154887Schin 			/*
22164887Schin 			 * ok, so ~ won't work for the last list element
22174887Schin 			 * we do it for MAGIC_FILES_ENV anyway
22184887Schin 			 */
22194887Schin 
22204887Schin 			if ((strneq(s, "~/", n = 2) || strneq(s, "$HOME/", n = 6) || strneq(s, "${HOME}/", n = 8)) && (t = getenv("HOME")))
22214887Schin 			{
22224887Schin 				sfputr(mp->tmp, t, -1);
22234887Schin 				s += n - 1;
22244887Schin 			}
22254887Schin 			sfwrite(mp->tmp, s, e - s);
22264887Schin 			if (!(s = sfstruse(mp->tmp)))
22274887Schin 				goto nospace;
22284887Schin 		}
22294887Schin 		if (!*s || streq(s, "-"))
22304887Schin 			s = MAGIC_FILE;
22314887Schin 		if (!(fp = sfopen(NiL, s, "r")))
22324887Schin 		{
22334887Schin 			if (list)
22344887Schin 			{
22354887Schin 				if (!(t = pathpath(mp->fbuf, s, "", PATH_REGULAR|PATH_READ)) && !strchr(s, '/'))
22364887Schin 				{
22374887Schin 					strcpy(mp->fbuf, s);
22384887Schin 					sfprintf(mp->tmp, "%s/%s", MAGIC_DIR, mp->fbuf);
22394887Schin 					if (!(s = sfstruse(mp->tmp)))
22404887Schin 						goto nospace;
22414887Schin 					if (!(t = pathpath(mp->fbuf, s, "", PATH_REGULAR|PATH_READ)))
22424887Schin 						goto next;
22434887Schin 				}
22444887Schin 				if (!(fp = sfopen(NiL, t, "r")))
22454887Schin 					goto next;
22464887Schin 			}
22474887Schin 			else
22484887Schin 			{
22494887Schin 				if (mp->disc->errorf)
22504887Schin 					(*mp->disc->errorf)(mp, mp->disc, 3, "%s: cannot open magic file", s);
22514887Schin 				return -1;
22524887Schin 			}
22534887Schin 		}
22544887Schin 		found = 1;
22554887Schin 		n = load(mp, s, fp);
22564887Schin 		sfclose(fp);
22574887Schin 		if (n && !list)
22584887Schin 			return -1;
22594887Schin 	next:
22604887Schin 		if (!e)
22614887Schin 			break;
22624887Schin 		s = e + 1;
22634887Schin 	}
22644887Schin 	if (!found)
22654887Schin 	{
22664887Schin 		if (mp->flags & MAGIC_VERBOSE)
22674887Schin 		{
22684887Schin 			if (mp->disc->errorf)
22694887Schin 				(*mp->disc->errorf)(mp, mp->disc, 2, "cannot find magic file");
22704887Schin 		}
22714887Schin 		return -1;
22724887Schin 	}
22734887Schin 	return 0;
22744887Schin  nospace:
22754887Schin 	if (mp->disc->errorf)
22764887Schin 		(*mp->disc->errorf)(mp, mp->disc, 3, "out of space");
22774887Schin 	return -1;
22784887Schin }
22794887Schin 
22804887Schin /*
22814887Schin  * open a magic session
22824887Schin  */
22834887Schin 
22844887Schin Magic_t*
magicopen(Magicdisc_t * disc)22854887Schin magicopen(Magicdisc_t* disc)
22864887Schin {
22874887Schin 	register Magic_t*	mp;
22884887Schin 	register int		i;
22894887Schin 	register int		n;
22904887Schin 	register int		f;
22914887Schin 	register int		c;
22924887Schin 	register Vmalloc_t*	vm;
22934887Schin 	unsigned char*		map[CC_MAPS + 1];
22944887Schin 
22954887Schin 	if (!(vm = vmopen(Vmdcheap, Vmbest, 0)))
22964887Schin 		return 0;
22974887Schin 	if (!(mp = vmnewof(vm, 0, Magic_t, 1, 0)))
22984887Schin 	{
22994887Schin 		vmclose(vm);
23004887Schin 		return 0;
23014887Schin 	}
23024887Schin 	mp->id = lib;
23034887Schin 	mp->disc = disc;
23044887Schin 	mp->vm = vm;
23054887Schin 	mp->flags = disc->flags;
23064887Schin 	mp->redisc.re_version = REG_VERSION;
23074887Schin 	mp->redisc.re_flags = REG_NOFREE;
23084887Schin 	mp->redisc.re_errorf = (regerror_t)disc->errorf;
23094887Schin 	mp->redisc.re_resizef = (regresize_t)vmgetmem;
23104887Schin 	mp->redisc.re_resizehandle = (void*)mp->vm;
23114887Schin 	mp->dtdisc.key = offsetof(Info_t, name);
23124887Schin 	mp->dtdisc.link = offsetof(Info_t, link);
23134887Schin 	if (!(mp->tmp = sfstropen()) || !(mp->infotab = dtnew(mp->vm, &mp->dtdisc, Dthash)))
23144887Schin 		goto bad;
23154887Schin 	for (n = 0; n < elementsof(info); n++)
23164887Schin 		dtinsert(mp->infotab, &info[n]);
23174887Schin 	for (i = 0; i < CC_MAPS; i++)
23184887Schin 		map[i] = ccmap(i, CC_ASCII);
23194887Schin 	mp->x2n = ccmap(CC_ALIEN, CC_NATIVE);
23204887Schin 	for (n = 0; n <= UCHAR_MAX; n++)
23214887Schin 	{
23224887Schin 		f = 0;
23234887Schin 		i = CC_MAPS;
23244887Schin 		while (--i >= 0)
23254887Schin 		{
23264887Schin 			c = ccmapchr(map[i], n);
23274887Schin 			f = (f << CC_BIT) | CCTYPE(c);
23284887Schin 		}
23294887Schin 		mp->cctype[n] = f;
23304887Schin 	}
23314887Schin 	return mp;
23324887Schin  bad:
23334887Schin 	magicclose(mp);
23344887Schin 	return 0;
23354887Schin }
23364887Schin 
23374887Schin /*
23384887Schin  * close a magicopen() session
23394887Schin  */
23404887Schin 
23414887Schin int
magicclose(register Magic_t * mp)23424887Schin magicclose(register Magic_t* mp)
23434887Schin {
23444887Schin 	if (!mp)
23454887Schin 		return -1;
23464887Schin 	if (mp->tmp)
23474887Schin 		sfstrclose(mp->tmp);
23484887Schin 	if (mp->vm)
23494887Schin 		vmclose(mp->vm);
23504887Schin 	return 0;
23514887Schin }
23524887Schin 
23534887Schin /*
23544887Schin  * return the magic string for file with optional stat info st
23554887Schin  */
23564887Schin 
23574887Schin char*
magictype(register Magic_t * mp,Sfio_t * fp,const char * file,register struct stat * st)23584887Schin magictype(register Magic_t* mp, Sfio_t* fp, const char* file, register struct stat* st)
23594887Schin {
23604887Schin 	off_t	off;
23614887Schin 	char*	s;
23624887Schin 
23634887Schin 	mp->flags = mp->disc->flags;
23644887Schin 	mp->mime = 0;
23654887Schin 	if (!st)
23664887Schin 		s = T("cannot stat");
23674887Schin 	else
23684887Schin 	{
23694887Schin 		if (mp->fp = fp)
23704887Schin 			off = sfseek(mp->fp, (off_t)0, SEEK_CUR);
23714887Schin 		s = type(mp, file, st, mp->tbuf, sizeof(mp->tbuf));
23724887Schin 		if (mp->fp)
23734887Schin 			sfseek(mp->fp, off, SEEK_SET);
23744887Schin 		if (!(mp->flags & MAGIC_MIME))
23754887Schin 		{
23764887Schin 			if (S_ISREG(st->st_mode) && (st->st_size > 0) && (st->st_size < 128))
23774887Schin 				sfprintf(mp->tmp, "%s ", T("short"));
23784887Schin 			sfprintf(mp->tmp, "%s", s);
23794887Schin 			if (!mp->fp && (st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
23804887Schin 				sfprintf(mp->tmp, ", %s", S_ISDIR(st->st_mode) ? T("searchable") : T("executable"));
23814887Schin 			if (st->st_mode & S_ISUID)
23824887Schin 				sfprintf(mp->tmp, ", setuid=%s", fmtuid(st->st_uid));
23834887Schin 			if (st->st_mode & S_ISGID)
23844887Schin 				sfprintf(mp->tmp, ", setgid=%s", fmtgid(st->st_gid));
23854887Schin 			if (st->st_mode & S_ISVTX)
23864887Schin 				sfprintf(mp->tmp, ", sticky");
23874887Schin 			if (!(s = sfstruse(mp->tmp)))
23884887Schin 				s = T("out of space");
23894887Schin 		}
23904887Schin 	}
23914887Schin 	if (mp->flags & MAGIC_MIME)
23924887Schin 		s = mp->mime;
23934887Schin 	if (!s)
23944887Schin 		s = T("error");
23954887Schin 	return s;
23964887Schin }
23974887Schin 
23984887Schin /*
23994887Schin  * list the magic table in mp on sp
24004887Schin  */
24014887Schin 
24024887Schin int
magiclist(register Magic_t * mp,register Sfio_t * sp)24034887Schin magiclist(register Magic_t* mp, register Sfio_t* sp)
24044887Schin {
24054887Schin 	register Entry_t*	ep = mp->magic;
24064887Schin 	register Entry_t*	rp = 0;
24074887Schin 
24084887Schin 	mp->flags = mp->disc->flags;
24094887Schin 	sfprintf(sp, "cont\toffset\ttype\top\tmask\tvalue\tmime\tdesc\n");
24104887Schin 	while (ep)
24114887Schin 	{
24124887Schin 		sfprintf(sp, "%c %c\t", ep->cont, ep->nest);
24134887Schin 		if (ep->expr)
24144887Schin 			sfprintf(sp, "%s", ep->expr);
24154887Schin 		else
24164887Schin 			sfprintf(sp, "%ld", ep->offset);
24174887Schin 		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);
24184887Schin 		switch (ep->type)
24194887Schin 		{
24204887Schin 		case 'm':
24214887Schin 		case 's':
24224887Schin 			sfputr(sp, fmtesc(ep->value.str), -1);
24234887Schin 			break;
24244887Schin 		case 'V':
24254887Schin 			switch (ep->op)
24264887Schin 			{
24274887Schin 			case 'l':
24284887Schin 				sfprintf(sp, "loop(%d,%d,%d,%d)", ep->value.loop->start, ep->value.loop->size, ep->value.loop->count, ep->value.loop->offset);
24294887Schin 				break;
24304887Schin 			case 'v':
24314887Schin 				sfprintf(sp, "vcodex()");
24324887Schin 				break;
24334887Schin 			default:
24344887Schin 				sfprintf(sp, "%p", ep->value.str);
24354887Schin 				break;
24364887Schin 			}
24374887Schin 			break;
24384887Schin 		default:
24394887Schin 			sfprintf(sp, "%lo", ep->value.num);
24404887Schin 			break;
24414887Schin 		}
24424887Schin 		sfprintf(sp, "\t%s\t%s\n", ep->mime ? ep->mime : "", fmtesc(ep->desc));
24434887Schin 		if (ep->cont == '$' && !ep->value.lab->mask)
24444887Schin 		{
24454887Schin 			rp = ep;
24464887Schin 			ep = ep->value.lab;
24474887Schin 		}
24484887Schin 		else
24494887Schin 		{
24504887Schin 			if (ep->cont == ':')
24514887Schin 			{
24524887Schin 				ep = rp;
24534887Schin 				ep->value.lab->mask = 1;
24544887Schin 			}
24554887Schin 			ep = ep->next;
24564887Schin 		}
24574887Schin 	}
24584887Schin 	return 0;
24594887Schin }
2460