xref: /onnv-gate/usr/src/lib/libast/common/misc/mime.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 /*
254887Schin  * Glenn Fowler
264887Schin  * AT&T Research
274887Schin  *
284887Schin  * mime/mailcap support library
294887Schin  */
304887Schin 
314887Schin static const char id[] = "\n@(#)$Id: mime library (AT&T Research) 2002-10-29 $\0\n";
324887Schin 
334887Schin static const char lib[] = "libast:mime";
344887Schin 
354887Schin #include "mimelib.h"
364887Schin 
374887Schin typedef struct Att_s
384887Schin {
394887Schin 	struct Att_s*	next;
404887Schin 	char*		name;
414887Schin 	char*		value;
424887Schin } Att_t;
434887Schin 
444887Schin typedef struct Cap_s
454887Schin {
464887Schin 	struct Cap_s*	next;
474887Schin 	unsigned long	flags;
484887Schin 	Att_t		att;
494887Schin 	char*		test;
504887Schin 	char		data[1];
514887Schin } Cap_t;
524887Schin 
534887Schin typedef struct
544887Schin {
554887Schin 	Dtlink_t	link;
564887Schin 	Cap_t*		cap;
574887Schin 	Cap_t*		pac;
584887Schin 	char		name[1];
594887Schin } Ent_t;
604887Schin 
614887Schin typedef struct
624887Schin {
634887Schin 	char*		data;
644887Schin 	int		size;
654887Schin } String_t;
664887Schin 
674887Schin typedef struct
684887Schin {
694887Schin 	char*		next;
704887Schin 	String_t	name;
714887Schin 	String_t	value;
724887Schin } Parse_t;
734887Schin 
744887Schin typedef struct
754887Schin {
764887Schin 	const char*	pattern;
774887Schin 	int		prefix;
784887Schin 	Sfio_t*		fp;
794887Schin 	int		hit;
804887Schin } Walk_t;
814887Schin 
824887Schin /*
834887Schin  * convert c to lower case
844887Schin  */
854887Schin 
864887Schin static int
lower(register int c)874887Schin lower(register int c)
884887Schin {
894887Schin 	return isupper(c) ? tolower(c) : c;
904887Schin }
914887Schin 
924887Schin /*
934887Schin  * Ent_t case insensitive comparf
944887Schin  */
954887Schin 
964887Schin static int
order(Dt_t * dt,void * a,void * b,Dtdisc_t * disc)974887Schin order(Dt_t* dt, void* a, void* b, Dtdisc_t* disc)
984887Schin {
994887Schin 	return strcasecmp(a, b);
1004887Schin }
1014887Schin 
1024887Schin /*
1034887Schin  * Cap_t free
1044887Schin  */
1054887Schin 
1064887Schin static void
dropcap(register Cap_t * cap)1074887Schin dropcap(register Cap_t* cap)
1084887Schin {
1094887Schin 	register Att_t*	att;
1104887Schin 
1114887Schin 	while (att = cap->att.next)
1124887Schin 	{
1134887Schin 		cap->att.next = att->next;
1144887Schin 		free(att);
1154887Schin 	}
1164887Schin 	free(cap);
1174887Schin }
1184887Schin 
1194887Schin /*
1204887Schin  * Ent_t freef
1214887Schin  */
1224887Schin 
1234887Schin static void
drop(Dt_t * dt,void * object,Dtdisc_t * disc)1244887Schin drop(Dt_t* dt, void* object, Dtdisc_t* disc)
1254887Schin {
1264887Schin 	register Ent_t*	ent = (Ent_t*)object;
1274887Schin 	register Cap_t*	cap;
1284887Schin 
1294887Schin 	while (cap = ent->cap)
1304887Schin 	{
1314887Schin 		ent->cap = cap->next;
1324887Schin 		dropcap(cap);
1334887Schin 	}
1344887Schin 	free(ent);
1354887Schin }
1364887Schin 
1374887Schin /*
1384887Schin  * add mime type entry in s to mp
1394887Schin  */
1404887Schin 
1414887Schin int
mimeset(Mime_t * mp,register char * s,unsigned long flags)1424887Schin mimeset(Mime_t* mp, register char* s, unsigned long flags)
1434887Schin {
1444887Schin 	register Ent_t*	ent;
1454887Schin 	register Cap_t*	cap;
1464887Schin 	register Att_t*	att;
1474887Schin 	register char*	t;
1484887Schin 	register char*	v;
1494887Schin 	register char*	k;
1504887Schin 	char*		x;
1514887Schin 	Att_t*		tta;
1524887Schin 	int		q;
1534887Schin 
1544887Schin 	for (; isspace(*s); s++);
1554887Schin 	if (*s && *s != '#')
1564887Schin 	{
1574887Schin 		cap = 0;
1584887Schin 		for (v = s; *v && *v != ';'; v++)
1594887Schin 			if (isspace(*v) || *v == '/' && *(v + 1) == '*')
1604887Schin 				*v = 0;
1614887Schin 		if (*v)
1624887Schin 		{
1634887Schin 			*v++ = 0;
1644887Schin 			do
1654887Schin 			{
1664887Schin 				for (; isspace(*v); v++);
1674887Schin 				if (cap)
1684887Schin 				{
1694887Schin 					for (t = v; *t && !isspace(*t) && *t != '='; t++);
1704887Schin 					for (k = t; isspace(*t); t++);
1714887Schin 					if (!*t || *t == '=' || *t == ';')
1724887Schin 					{
1734887Schin 						if (*t)
1744887Schin 							while (isspace(*++t));
1754887Schin 						*k = 0;
1764887Schin 						k = v;
1774887Schin 						v = t;
1784887Schin 					}
1794887Schin 					else
1804887Schin 						k = 0;
1814887Schin 				}
1824887Schin 				if (*v == '"')
1834887Schin 					q = *v++;
1844887Schin 				else
1854887Schin 					q = 0;
1864887Schin 				for (t = v; *t; t++)
1874887Schin 					if (*t == '\\')
1884887Schin 					{
1894887Schin 						switch (*(t + 1))
1904887Schin 						{
1914887Schin 						case 0:
1924887Schin 						case '\\':
1934887Schin 						case '%':
1944887Schin 							*t = *(t + 1);
1954887Schin 							break;
1964887Schin 						default:
1974887Schin 							*t = ' ';
1984887Schin 							break;
1994887Schin 						}
2004887Schin 						if (!*++t)
2014887Schin 							break;
2024887Schin 					}
2034887Schin 					else if (*t == q)
2044887Schin 					{
2054887Schin 						*t = ' ';
2064887Schin 						q = 0;
2074887Schin 					}
2084887Schin 					else if (*t == ';' && !q)
2094887Schin 					{
2104887Schin 						*t = ' ';
2114887Schin 						break;
2124887Schin 					}
2134887Schin 				for (; t > v && isspace(*(t - 1)); t--);
2144887Schin 				if (t <= v && (!cap || !k))
2154887Schin 					break;
2164887Schin 				if (!cap)
2174887Schin 				{
2184887Schin 					if (!(cap = newof(0, Cap_t, 1, strlen(v) + 1)))
2194887Schin 						return -1;
2204887Schin 					if (*t)
2214887Schin 						*t++ = 0;
2224887Schin 					tta = &cap->att;
2234887Schin 					tta->name = "default";
2244887Schin 					x = strcopy(tta->value = cap->data, v) + 1;
2254887Schin 				}
2264887Schin 				else if (k)
2274887Schin 				{
2284887Schin 					if (*t)
2294887Schin 						*t++ = 0;
2304887Schin 					if (!(att = newof(0, Att_t, 1, 0)))
2314887Schin 						return -1;
2324887Schin 					x = strcopy(att->name = x, k) + 1;
2334887Schin 					x = strcopy(att->value = x, v) + 1;
2344887Schin 					tta = tta->next = att;
2354887Schin 					if (!strcasecmp(k, "test"))
2364887Schin 						cap->test = att->value;
2374887Schin 				}
2384887Schin 			} while (*(v = t));
2394887Schin 		}
2404887Schin 		ent = (Ent_t*)dtmatch(mp->cap, s);
2414887Schin 		if (cap)
2424887Schin 		{
2434887Schin 			if (ent)
2444887Schin 			{
2454887Schin 				register Cap_t*	dup;
2464887Schin 				register Cap_t*	pud;
2474887Schin 
2484887Schin 				for (pud = 0, dup = ent->cap; dup; pud = dup, dup = dup->next)
2494887Schin 					if (!cap->test && !dup->test || cap->test && dup->test && streq(cap->test, dup->test))
2504887Schin 					{
2514887Schin 						if (flags & MIME_REPLACE)
2524887Schin 						{
2534887Schin 							if (pud)
2544887Schin 								pud->next = cap;
2554887Schin 							else
2564887Schin 								ent->cap = cap;
2574887Schin 							if (!(cap->next = dup->next))
2584887Schin 								ent->pac = cap;
2594887Schin 							cap = dup;
2604887Schin 						}
2614887Schin 						dropcap(cap);
2624887Schin 						return 0;
2634887Schin 					}
2644887Schin 				ent->pac = ent->pac->next = cap;
2654887Schin 			}
2664887Schin 			else if (!(ent = newof(0, Ent_t, 1, strlen(s) + 1)))
2674887Schin 				return -1;
2684887Schin 			else
2694887Schin 			{
2704887Schin 				strcpy(ent->name, s);
2714887Schin 				ent->cap = ent->pac = cap;
2724887Schin 				dtinsert(mp->cap, ent);
2734887Schin 			}
2744887Schin 		}
2754887Schin 		else if (ent && (flags & MIME_REPLACE))
2764887Schin 			dtdelete(mp->cap, ent);
2774887Schin 	}
2784887Schin 	return 0;
2794887Schin }
2804887Schin 
2814887Schin /*
2824887Schin  * load mime type files into mp
2834887Schin  */
2844887Schin 
2854887Schin int
mimeload(Mime_t * mp,const char * file,unsigned long flags)2864887Schin mimeload(Mime_t* mp, const char* file, unsigned long flags)
2874887Schin {
2884887Schin 	register char*	s;
2894887Schin 	register char*	t;
2904887Schin 	register char*	e;
2914887Schin 	register int	n;
2924887Schin 	Sfio_t*		fp;
2934887Schin 
2944887Schin 	if (!(s = (char*)file))
2954887Schin 	{
2964887Schin 		flags |= MIME_LIST;
2974887Schin 		if (!(s = getenv(MIME_FILES_ENV)))
2984887Schin 			s = MIME_FILES;
2994887Schin 	}
3004887Schin 	for (;;)
3014887Schin 	{
3024887Schin 		if (!(flags & MIME_LIST))
3034887Schin 			e = 0;
3044887Schin 		else if (e = strchr(s, ':'))
3054887Schin 		{
3064887Schin 			/*
3074887Schin 			 * ok, so ~ won't work for the last list element
3084887Schin 			 * we do it for MIME_FILES_ENV anyway
3094887Schin 			 */
3104887Schin 
3114887Schin 			if ((strneq(s, "~/", n = 2) || strneq(s, "$HOME/", n = 6) || strneq(s, "${HOME}/", n = 8)) && (t = getenv("HOME")))
3124887Schin 			{
3134887Schin 				sfputr(mp->buf, t, -1);
3144887Schin 				s += n - 1;
3154887Schin 			}
3164887Schin 			sfwrite(mp->buf, s, e - s);
3174887Schin 			if (!(s = sfstruse(mp->buf)))
3184887Schin 				return -1;
3194887Schin 		}
3204887Schin 		if (fp = tokline(s, SF_READ, NiL))
3214887Schin 		{
3224887Schin 			while (t = sfgetr(fp, '\n', 1))
3234887Schin 				if (mimeset(mp, t, flags))
3244887Schin 					break;
3254887Schin 			sfclose(fp);
3264887Schin 		}
3274887Schin 		else if (!(flags & MIME_LIST))
3284887Schin 			return -1;
3294887Schin 		if (!e)
3304887Schin 			break;
3314887Schin 		s = e + 1;
3324887Schin 	}
3334887Schin 	return 0;
3344887Schin }
3354887Schin 
3364887Schin /*
3374887Schin  * mimelist walker
3384887Schin  */
3394887Schin 
3404887Schin static int
list(Dt_t * dt,void * object,void * context)3414887Schin list(Dt_t* dt, void* object, void* context)
3424887Schin {
3434887Schin 	register Walk_t*	wp = (Walk_t*)context;
3444887Schin 	register Ent_t*		ent = (Ent_t*)object;
3454887Schin 	register Cap_t*		cap;
3464887Schin 	register Att_t*		att;
3474887Schin 
3484887Schin 	if (!wp->pattern || !strncasecmp(ent->name, wp->pattern, wp->prefix) && (!ent->name[wp->prefix] || ent->name[wp->prefix] == '/'))
3494887Schin 	{
3504887Schin 		wp->hit++;
3514887Schin 		for (cap = ent->cap; cap; cap = cap->next)
3524887Schin 		{
3534887Schin 			sfprintf(wp->fp, "%s", ent->name);
3544887Schin 			for (att = &cap->att; att; att = att->next)
3554887Schin 			{
3564887Schin 				sfprintf(wp->fp, "\n\t");
3574887Schin 				if (att != &cap->att)
3584887Schin 				{
3594887Schin 					sfprintf(wp->fp, "%s", att->name);
3604887Schin 					if (*att->value)
3614887Schin 						sfprintf(wp->fp, " = ");
3624887Schin 				}
3634887Schin 				sfputr(wp->fp, att->value, -1);
3644887Schin 			}
3654887Schin 			sfprintf(wp->fp, "\n");
3664887Schin 		}
3674887Schin 	}
3684887Schin 	return 0;
3694887Schin }
3704887Schin 
3714887Schin /*
3724887Schin  * find entry matching type
3734887Schin  * if exact match fails then left and right x- and right version number
3744887Schin  * permutations are attempted
3754887Schin  */
3764887Schin 
3774887Schin static Ent_t*
find(Mime_t * mp,const char * type)3784887Schin find(Mime_t* mp, const char* type)
3794887Schin {
3804887Schin 	register char*	lp;
3814887Schin 	register char*	rp;
3824887Schin 	register char*	rb;
3834887Schin 	register char*	rv;
3844887Schin 	register int	rc;
3854887Schin 	register int	i;
3864887Schin 	char*		s;
3874887Schin 	Ent_t*		ent;
3884887Schin 	char		buf[256];
3894887Schin 
3904887Schin 	static const char*	prefix[] = { "", "", "x-", "x-", "" };
3914887Schin 
3924887Schin 	if ((ent = (Ent_t*)dtmatch(mp->cap, type)) ||
3934887Schin 	    !(rp = strchr(lp = (char*)type, '/')) ||
3944887Schin 	    strlen(lp) >= sizeof(buf))
3954887Schin 		return ent;
3964887Schin 	strcpy(buf, type);
3974887Schin 	rp = buf + (rp - lp);
3984887Schin 	*rp++ = 0;
3994887Schin 	if (*rp == 'x' && *(rp + 1) == '-')
4004887Schin 		rp += 2;
4014887Schin 	lp = buf;
4024887Schin 	if (*lp == 'x' && *(lp + 1) == '-')
4034887Schin 		lp += 2;
4044887Schin 	rb = rp;
4054887Schin 	for (rv = rp + strlen(rp); rv > rp && (isdigit(*(rv - 1)) || *(rv - 1) == '.'); rv--);
4064887Schin 	rc = *rv;
4074887Schin 	do
4084887Schin 	{
4094887Schin 		rp = rb;
4104887Schin 		do
4114887Schin 		{
4124887Schin 			for (i = 0; i < elementsof(prefix) - 1; i++)
4134887Schin 			{
4144887Schin 				sfprintf(mp->buf, "%s%s/%s%s", prefix[i], lp, prefix[i + 1], rp);
4154887Schin 				if (!(s = sfstruse(mp->buf)))
4164887Schin 					return 0;
4174887Schin 				if (ent = (Ent_t*)dtmatch(mp->cap, s))
4184887Schin 					return ent;
4194887Schin 				if (rc)
4204887Schin 				{
4214887Schin 					*rv = 0;
4224887Schin 					sfprintf(mp->buf, "%s%s/%s%s", prefix[i], lp, prefix[i + 1], rp);
4234887Schin 					if (!(s = sfstruse(mp->buf)))
4244887Schin 						return 0;
4254887Schin 					if (ent = (Ent_t*)dtmatch(mp->cap, s))
4264887Schin 						return ent;
4274887Schin 					*rv = rc;
4284887Schin 				}
4294887Schin 			}
4304887Schin 			while (*rp && *rp++ != '-');
4314887Schin 		} while (*rp);
4324887Schin 		while (*lp && *lp++ != '-');
4334887Schin 	} while (*lp);
4344887Schin 	return (Ent_t*)dtmatch(mp->cap, buf);
4354887Schin }
4364887Schin 
4374887Schin /*
4384887Schin  * list mime <type,data> for pat on fp
4394887Schin  */
4404887Schin 
4414887Schin int
mimelist(Mime_t * mp,Sfio_t * fp,register const char * pattern)4424887Schin mimelist(Mime_t* mp, Sfio_t* fp, register const char* pattern)
4434887Schin {
4444887Schin 	Ent_t*	ent;
4454887Schin 	Walk_t	ws;
4464887Schin 
4474887Schin 	ws.fp = fp;
4484887Schin 	ws.hit = 0;
4494887Schin 	ws.prefix = 0;
4504887Schin 	if (ws.pattern = pattern)
4514887Schin 	{
4524887Schin 		while (*pattern && *pattern++ != '/');
4534887Schin 		if (!*pattern || *pattern == '*' && !*(pattern + 1))
4544887Schin 			ws.prefix = pattern - ws.pattern;
4554887Schin 		else if (ent = find(mp, ws.pattern))
4564887Schin 		{
4574887Schin 			ws.pattern = 0;
4584887Schin 			list(mp->cap, ent, &ws);
4594887Schin 			return ws.hit;
4604887Schin 		}
4614887Schin 	}
4624887Schin 	dtwalk(mp->cap, list, &ws);
4634887Schin 	return ws.hit;
4644887Schin }
4654887Schin 
4664887Schin /*
4674887Schin  * get next arg in pp
4684887Schin  * 0 returned if no more args
4694887Schin  */
4704887Schin 
4714887Schin static int
arg(register Parse_t * pp,int first)4724887Schin arg(register Parse_t* pp, int first)
4734887Schin {
4744887Schin 	register char*	s;
4754887Schin 	register int	c;
4764887Schin 	register int	q;
4774887Schin 	int		x;
4784887Schin 
4794887Schin 	for (s = pp->next; isspace(*s) && *s != '\n'; s++);
4804887Schin 	if (!*s || *s == '\n')
4814887Schin 	{
4824887Schin 		pp->next = s;
4834887Schin 		return 0;
4844887Schin 	}
4854887Schin 	pp->name.data = s;
4864887Schin 	pp->value.data = 0;
4874887Schin 	q = 0;
4884887Schin 	x = 0;
4894887Schin 	while ((c = *s++) && c != ';' && c != '\n')
4904887Schin 	{
4914887Schin 		if (c == '"')
4924887Schin 		{
4934887Schin 			q = 1;
4944887Schin 			if (pp->value.data)
4954887Schin 			{
4964887Schin 				pp->value.data = s;
4974887Schin 				if (x)
4984887Schin 					x = -1;
4994887Schin 				else
5004887Schin 					x = 1;
5014887Schin 			}
5024887Schin 			else if (!x && pp->name.data == (s - 1))
5034887Schin 			{
5044887Schin 				x = 1;
5054887Schin 				pp->name.data = s;
5064887Schin 			}
5074887Schin 			do
5084887Schin 			{
5094887Schin 				if (!(c = *s++) || c == '\n')
5104887Schin 				{
5114887Schin 					s--;
5124887Schin 					break;
5134887Schin 				}
5144887Schin 			} while (c != '"');
5154887Schin 			if (first < 0 || x > 0)
5164887Schin 			{
5174887Schin 				c = ';';
5184887Schin 				break;
5194887Schin 			}
5204887Schin  		}
5214887Schin 		else if (c == '=' && !first)
5224887Schin 		{
5234887Schin 			first = 1;
5244887Schin 			pp->name.size = s - pp->name.data - 1;
5254887Schin 			pp->value.data = s;
5264887Schin 		}
5274887Schin 		else if (first >= 0 && isspace(c))
5284887Schin 			break;
5294887Schin 	}
5304887Schin 	pp->next = s - (c != ';');
5314887Schin 	if (first >= 0 || !q)
5324887Schin 		for (s--; s > pp->name.data && isspace(*(s - 1)); s--);
5334887Schin 	if (pp->value.data)
5344887Schin 		pp->value.size = s - pp->value.data - (q && first < 0);
5354887Schin 	else
5364887Schin 	{
5374887Schin 		pp->value.size = 0;
5384887Schin 		pp->name.size = s - pp->name.data - (q && first < 0);
5394887Schin 	}
5404887Schin 	if (first >= 0 && pp->name.size > 0 && pp->name.data[pp->name.size - 1] == ':')
5414887Schin 		return 0;
5424887Schin 	return pp->name.size > 0;
5434887Schin }
5444887Schin 
5454887Schin /*
5464887Schin  * low level for mimeview()
5474887Schin  */
5484887Schin 
5494887Schin static char*
expand(Mime_t * mp,register char * s,const char * name,const char * type,const char * opts)5504887Schin expand(Mime_t* mp, register char* s, const char* name, const char* type, const char* opts)
5514887Schin {
5524887Schin 	register char*	t;
5534887Schin 	register int	c;
5544887Schin 	Parse_t		pp;
5554887Schin 
5564887Schin 	mp->disc->flags |= MIME_PIPE;
5574887Schin 	for (;;)
5584887Schin 	{
5594887Schin 		switch (c = *s++)
5604887Schin 		{
5614887Schin 		case 0:
5624887Schin 		case '\n':
5634887Schin 			break;
5644887Schin 		case '%':
5654887Schin 			switch (c = *s++)
5664887Schin 			{
5674887Schin 			case 's':
5684887Schin 				sfputr(mp->buf, (char*)name, -1);
5694887Schin 				mp->disc->flags &= ~MIME_PIPE;
5704887Schin 				continue;
5714887Schin 			case 't':
5724887Schin 				sfputr(mp->buf, (char*)type, -1);
5734887Schin 				continue;
5744887Schin 			case '{':
5754887Schin 				for (t = s; *s && *s != '}'; s++);
5764887Schin 				if (*s && (c = s++ - t) && (pp.next = (char*)opts))
5774887Schin 					while (arg(&pp, 0))
5784887Schin 						if (pp.name.size == c && !strncasecmp(pp.name.data, t, c))
5794887Schin 						{
5804887Schin 							if (pp.value.size)
5814887Schin 								sfwrite(mp->buf, pp.value.data, pp.value.size);
5824887Schin 							break;
5834887Schin 						}
5844887Schin 				continue;
5854887Schin 			}
5864887Schin 			/*FALLTHROUGH*/
5874887Schin 		default:
5884887Schin 			sfputc(mp->buf, c);
5894887Schin 			continue;
5904887Schin 		}
5914887Schin 		break;
5924887Schin 	}
5934887Schin 	return sfstruse(mp->buf);
5944887Schin }
5954887Schin 
5964887Schin /*
5974887Schin  * return expanded command/path/value for <view,name,type,opts>
5984887Schin  * return value valid until next mime*() call
5994887Schin  */
6004887Schin 
6014887Schin char*
mimeview(Mime_t * mp,const char * view,const char * name,const char * type,const char * opts)6024887Schin mimeview(Mime_t* mp, const char* view, const char* name, const char* type, const char* opts)
6034887Schin {
6044887Schin 	register Ent_t*	ent;
6054887Schin 	register Cap_t*	cap;
6064887Schin 	register Att_t*	att;
6074887Schin 	register char*	s;
6084887Schin 	int		c;
6094887Schin 
6104887Schin 	if (ent = find(mp, type))
6114887Schin 	{
6124887Schin 		cap = ent->cap;
6134887Schin 		if (!view || strcasecmp(view, "test"))
6144887Schin 			while (s = cap->test)
6154887Schin 			{
6164887Schin 				if (s = expand(mp, s, name, type, opts))
6174887Schin 				{
6184887Schin 					Parse_t	a1;
6194887Schin 					Parse_t	a2;
6204887Schin 					Parse_t	a3;
6214887Schin 					Parse_t	a4;
6224887Schin 
6234887Schin 					/*
6244887Schin 					 * try to do a few common cases here
6254887Schin 					 * mailcap consistency is a winning
6264887Schin 					 * strategy
6274887Schin 					 */
6284887Schin 
6294887Schin 					a1.next = s;
6304887Schin 					if (arg(&a1, -1))
6314887Schin 					{
6324887Schin 						if ((c = *a1.name.data == '!') && --a1.name.size <= 0 && !arg(&a1, -1))
6334887Schin 							goto lose;
6344887Schin 						if (a1.name.size == 6 && strneq(a1.name.data, "strcmp", 6) || a1.name.size == 10 && strneq(a1.name.data, "strcasecmp", 10))
6354887Schin 						{
6364887Schin 							a2.next = a1.next;
6374887Schin 							if (!arg(&a2, -1))
6384887Schin 								goto lose;
6394887Schin 							a3.next = a2.next;
6404887Schin 							if (!arg(&a3, -1))
6414887Schin 								goto lose;
6424887Schin 							if (a2.name.size != a3.name.size)
6434887Schin 								c ^= 0;
6444887Schin 							else c ^= (a1.name.size == 6 ? strncmp : strncasecmp)(a2.name.data, a3.name.data, a2.name.size) == 0;
6454887Schin 							if (c)
6464887Schin 								break;
6474887Schin 							goto skip;
6484887Schin 						}
6494887Schin 						else if (a1.name.size == 4 && strneq(a1.name.data, "test", 4))
6504887Schin 						{
6514887Schin 							if (!arg(&a1, -1))
6524887Schin 								goto lose;
6534887Schin 							a2.next = a1.next;
6544887Schin 							if (!arg(&a2, -1) || a2.name.size > 2 || a2.name.size == 1 && *a2.name.data != '=' || a2.name.size == 2 && (!strneq(a1.name.data, "!=", 2) || !strneq(a2.name.data, "==", 2)))
6554887Schin 								goto lose;
6564887Schin 							a3.next = a2.next;
6574887Schin 							if (!arg(&a3, -1))
6584887Schin 								goto lose;
6594887Schin 							if (*a3.name.data == '`' && *(a3.name.data + a3.name.size - 1) == '`')
6604887Schin 							{
6614887Schin 								a4 = a3;
6624887Schin 								a3 = a1;
6634887Schin 								a1 = a4;
6644887Schin 							}
6654887Schin 							if (*a1.name.data == '`' && *(a1.name.data + a1.name.size - 1) == '`')
6664887Schin 							{
6674887Schin 								a1.next = a1.name.data + 1;
6684887Schin 								if (!arg(&a1, -1) || a1.name.size != 4 || !strneq(a1.name.data, "echo", 4) || !arg(&a1, -1))
6694887Schin 									goto lose;
6704887Schin 								a4.next = a1.next;
6714887Schin 								if (!arg(&a4, 1) || a4.name.size < 21 || !strneq(a4.name.data, "| tr '[A-Z]' '[a-z]'`", 21))
6724887Schin 									goto lose;
6734887Schin 							}
6744887Schin 							else
6754887Schin 								a4.name.size = 0;
6764887Schin 							c = *a2.name.data == '!';
6774887Schin 							if (a1.name.size != a3.name.size)
6784887Schin 								c ^= 0;
6794887Schin 							else c ^= (a4.name.size ? strncasecmp : strncmp)(a1.name.data, a3.name.data, a1.name.size) == 0;
6804887Schin 							if (c)
6814887Schin 								break;
6824887Schin 							goto skip;
6834887Schin 						}
6844887Schin 					}
6854887Schin 				lose:
6864887Schin 					if (!system(s))
6874887Schin 						break;
6884887Schin 				}
6894887Schin 			skip:
6904887Schin 				if (!(cap = cap->next))
6914887Schin 					return 0;
6924887Schin 			}
6934887Schin 		att = &cap->att;
6944887Schin 		if (view && *view && !streq(view, "-"))
6954887Schin 			while (strcasecmp(view, att->name))
6964887Schin 				if (!(att = att->next))
6974887Schin 					return 0;
6984887Schin 		return expand(mp, att->value, name, type, opts);
6994887Schin 	}
7004887Schin 	return 0;
7014887Schin }
7024887Schin 
7034887Schin /*
7044887Schin  * lower case identifier prefix strcmp
7054887Schin  * if e!=0 then it will point to the next char after the match
7064887Schin  */
7074887Schin 
7084887Schin int
mimecmp(register const char * s,register const char * v,char ** e)7094887Schin mimecmp(register const char* s, register const char* v, char** e)
7104887Schin {
7114887Schin 	register int	n;
7124887Schin 
7134887Schin 	while (isalnum(*v) || *v == *s && (*v == '_' || *v == '-' || *v == '/'))
7144887Schin 		if (n = lower(*s++) - lower(*v++))
7154887Schin 			return n;
7164887Schin 	if (!isalnum(*s) && *s != '_' && *s != '-')
7174887Schin 	{
7184887Schin 		if (e)
7194887Schin 			*e = (char*)s;
7204887Schin 		return 0;
7214887Schin 	}
7224887Schin 	return lower(*s) - lower(*v);
7234887Schin }
7244887Schin 
7254887Schin /*
7264887Schin  * parse mime headers in strsearch(tab,num,siz) from s
7274887Schin  * return >0 if mime header consumed
7284887Schin  */
7294887Schin 
7304887Schin int
mimehead(Mime_t * mp,void * tab,size_t num,size_t siz,register char * s)7314887Schin mimehead(Mime_t* mp, void* tab, size_t num, size_t siz, register char* s)
7324887Schin {
7334887Schin 	register void*	p;
7344887Schin 	char*		e;
7354887Schin 	Parse_t		pp;
7364887Schin 	Mimevalue_f	set;
7374887Schin 
7384887Schin 	set = mp->disc->valuef;
7394887Schin 	if (!strncasecmp(s, "original-", 9))
7404887Schin 		s += 9;
7414887Schin 	if (!strncasecmp(s, "content-", 8))
7424887Schin 	{
7434887Schin 		s += 8;
7444887Schin 		if ((p = strsearch(tab, num, siz, (Strcmp_f)mimecmp, s, &e)) && *e == ':')
7454887Schin 		{
7464887Schin 			pp.next = e + 1;
7474887Schin 			if (arg(&pp, 1))
7484887Schin 			{
7494887Schin 				if ((*set)(mp, p, pp.name.data, pp.name.size, mp->disc))
7504887Schin 					return 0;
7514887Schin 				while (arg(&pp, 0))
7524887Schin 					if (pp.value.size &&
7534887Schin 					    (p = strsearch(tab, num, siz, (Strcmp_f)mimecmp, pp.name.data, &e)) &&
7544887Schin 					    (*set)(mp, p, pp.value.data, pp.value.size, mp->disc))
7554887Schin 						return 0;
7564887Schin 				return 1;
7574887Schin 			}
7584887Schin 		}
7594887Schin 		else if (strchr(s, ':'))
7604887Schin 			return 1;
7614887Schin 	}
7624887Schin 	return !strncasecmp(s, "x-", 2);
7634887Schin }
7644887Schin 
7654887Schin /*
7664887Schin  * open a mime library handle
7674887Schin  */
7684887Schin 
7694887Schin Mime_t*
mimeopen(Mimedisc_t * disc)7704887Schin mimeopen(Mimedisc_t* disc)
7714887Schin {
7724887Schin 	register Mime_t*	mp;
7734887Schin 
7744887Schin 	if (!(mp = newof(0, Mime_t, 1, 0)))
7754887Schin 		return 0;
7764887Schin 	mp->id = lib;
7774887Schin 	mp->disc = disc;
7784887Schin 	mp->dict.key = offsetof(Ent_t, name);
7794887Schin 	mp->dict.comparf = order;
7804887Schin 	mp->dict.freef = drop;
7814887Schin 	if (!(mp->buf = sfstropen()) || !(mp->cap = dtopen(&mp->dict, Dtorder)))
7824887Schin 	{
7834887Schin 		mimeclose(mp);
7844887Schin 		return 0;
7854887Schin 	}
7864887Schin 	return mp;
7874887Schin }
7884887Schin 
7894887Schin /*
7904887Schin  * close a mimeopen() handle
7914887Schin  */
7924887Schin 
7934887Schin int
mimeclose(Mime_t * mp)7944887Schin mimeclose(Mime_t* mp)
7954887Schin {
7964887Schin 	if (mp)
7974887Schin 	{
7984887Schin 		if (mp->buf)
7994887Schin 			sfclose(mp->buf);
8004887Schin 		if (mp->cap)
8014887Schin 			dtclose(mp->cap);
8024887Schin 		if (mp->freef)
8034887Schin 			(*mp->freef)(mp);
8044887Schin 		free(mp);
8054887Schin 	}
8064887Schin 	return 0;
8074887Schin }
808