1*4887Schin /***********************************************************************
2*4887Schin *                                                                      *
3*4887Schin *               This software is part of the ast package               *
4*4887Schin *           Copyright (c) 1985-2007 AT&T Knowledge Ventures            *
5*4887Schin *                      and is licensed under the                       *
6*4887Schin *                  Common Public License, Version 1.0                  *
7*4887Schin *                      by AT&T Knowledge Ventures                      *
8*4887Schin *                                                                      *
9*4887Schin *                A copy of the License is available at                 *
10*4887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
11*4887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*4887Schin *                                                                      *
13*4887Schin *              Information and Software Systems Research               *
14*4887Schin *                            AT&T Research                             *
15*4887Schin *                           Florham Park NJ                            *
16*4887Schin *                                                                      *
17*4887Schin *                 Glenn Fowler <gsf@research.att.com>                  *
18*4887Schin *                  David Korn <dgk@research.att.com>                   *
19*4887Schin *                   Phong Vo <kpv@research.att.com>                    *
20*4887Schin *                                                                      *
21*4887Schin ***********************************************************************/
22*4887Schin #pragma prototyped
23*4887Schin 
24*4887Schin /*
25*4887Schin  * Glenn Fowler
26*4887Schin  * AT&T Research
27*4887Schin  *
28*4887Schin  * mime/mailcap support library
29*4887Schin  */
30*4887Schin 
31*4887Schin static const char id[] = "\n@(#)$Id: mime library (AT&T Research) 2002-10-29 $\0\n";
32*4887Schin 
33*4887Schin static const char lib[] = "libast:mime";
34*4887Schin 
35*4887Schin #include "mimelib.h"
36*4887Schin 
37*4887Schin typedef struct Att_s
38*4887Schin {
39*4887Schin 	struct Att_s*	next;
40*4887Schin 	char*		name;
41*4887Schin 	char*		value;
42*4887Schin } Att_t;
43*4887Schin 
44*4887Schin typedef struct Cap_s
45*4887Schin {
46*4887Schin 	struct Cap_s*	next;
47*4887Schin 	unsigned long	flags;
48*4887Schin 	Att_t		att;
49*4887Schin 	char*		test;
50*4887Schin 	char		data[1];
51*4887Schin } Cap_t;
52*4887Schin 
53*4887Schin typedef struct
54*4887Schin {
55*4887Schin 	Dtlink_t	link;
56*4887Schin 	Cap_t*		cap;
57*4887Schin 	Cap_t*		pac;
58*4887Schin 	char		name[1];
59*4887Schin } Ent_t;
60*4887Schin 
61*4887Schin typedef struct
62*4887Schin {
63*4887Schin 	char*		data;
64*4887Schin 	int		size;
65*4887Schin } String_t;
66*4887Schin 
67*4887Schin typedef struct
68*4887Schin {
69*4887Schin 	char*		next;
70*4887Schin 	String_t	name;
71*4887Schin 	String_t	value;
72*4887Schin } Parse_t;
73*4887Schin 
74*4887Schin typedef struct
75*4887Schin {
76*4887Schin 	const char*	pattern;
77*4887Schin 	int		prefix;
78*4887Schin 	Sfio_t*		fp;
79*4887Schin 	int		hit;
80*4887Schin } Walk_t;
81*4887Schin 
82*4887Schin /*
83*4887Schin  * convert c to lower case
84*4887Schin  */
85*4887Schin 
86*4887Schin static int
87*4887Schin lower(register int c)
88*4887Schin {
89*4887Schin 	return isupper(c) ? tolower(c) : c;
90*4887Schin }
91*4887Schin 
92*4887Schin /*
93*4887Schin  * Ent_t case insensitive comparf
94*4887Schin  */
95*4887Schin 
96*4887Schin static int
97*4887Schin order(Dt_t* dt, void* a, void* b, Dtdisc_t* disc)
98*4887Schin {
99*4887Schin 	return strcasecmp(a, b);
100*4887Schin }
101*4887Schin 
102*4887Schin /*
103*4887Schin  * Cap_t free
104*4887Schin  */
105*4887Schin 
106*4887Schin static void
107*4887Schin dropcap(register Cap_t* cap)
108*4887Schin {
109*4887Schin 	register Att_t*	att;
110*4887Schin 
111*4887Schin 	while (att = cap->att.next)
112*4887Schin 	{
113*4887Schin 		cap->att.next = att->next;
114*4887Schin 		free(att);
115*4887Schin 	}
116*4887Schin 	free(cap);
117*4887Schin }
118*4887Schin 
119*4887Schin /*
120*4887Schin  * Ent_t freef
121*4887Schin  */
122*4887Schin 
123*4887Schin static void
124*4887Schin drop(Dt_t* dt, void* object, Dtdisc_t* disc)
125*4887Schin {
126*4887Schin 	register Ent_t*	ent = (Ent_t*)object;
127*4887Schin 	register Cap_t*	cap;
128*4887Schin 
129*4887Schin 	while (cap = ent->cap)
130*4887Schin 	{
131*4887Schin 		ent->cap = cap->next;
132*4887Schin 		dropcap(cap);
133*4887Schin 	}
134*4887Schin 	free(ent);
135*4887Schin }
136*4887Schin 
137*4887Schin /*
138*4887Schin  * add mime type entry in s to mp
139*4887Schin  */
140*4887Schin 
141*4887Schin int
142*4887Schin mimeset(Mime_t* mp, register char* s, unsigned long flags)
143*4887Schin {
144*4887Schin 	register Ent_t*	ent;
145*4887Schin 	register Cap_t*	cap;
146*4887Schin 	register Att_t*	att;
147*4887Schin 	register char*	t;
148*4887Schin 	register char*	v;
149*4887Schin 	register char*	k;
150*4887Schin 	char*		x;
151*4887Schin 	Att_t*		tta;
152*4887Schin 	int		q;
153*4887Schin 
154*4887Schin 	for (; isspace(*s); s++);
155*4887Schin 	if (*s && *s != '#')
156*4887Schin 	{
157*4887Schin 		cap = 0;
158*4887Schin 		for (v = s; *v && *v != ';'; v++)
159*4887Schin 			if (isspace(*v) || *v == '/' && *(v + 1) == '*')
160*4887Schin 				*v = 0;
161*4887Schin 		if (*v)
162*4887Schin 		{
163*4887Schin 			*v++ = 0;
164*4887Schin 			do
165*4887Schin 			{
166*4887Schin 				for (; isspace(*v); v++);
167*4887Schin 				if (cap)
168*4887Schin 				{
169*4887Schin 					for (t = v; *t && !isspace(*t) && *t != '='; t++);
170*4887Schin 					for (k = t; isspace(*t); t++);
171*4887Schin 					if (!*t || *t == '=' || *t == ';')
172*4887Schin 					{
173*4887Schin 						if (*t)
174*4887Schin 							while (isspace(*++t));
175*4887Schin 						*k = 0;
176*4887Schin 						k = v;
177*4887Schin 						v = t;
178*4887Schin 					}
179*4887Schin 					else
180*4887Schin 						k = 0;
181*4887Schin 				}
182*4887Schin 				if (*v == '"')
183*4887Schin 					q = *v++;
184*4887Schin 				else
185*4887Schin 					q = 0;
186*4887Schin 				for (t = v; *t; t++)
187*4887Schin 					if (*t == '\\')
188*4887Schin 					{
189*4887Schin 						switch (*(t + 1))
190*4887Schin 						{
191*4887Schin 						case 0:
192*4887Schin 						case '\\':
193*4887Schin 						case '%':
194*4887Schin 							*t = *(t + 1);
195*4887Schin 							break;
196*4887Schin 						default:
197*4887Schin 							*t = ' ';
198*4887Schin 							break;
199*4887Schin 						}
200*4887Schin 						if (!*++t)
201*4887Schin 							break;
202*4887Schin 					}
203*4887Schin 					else if (*t == q)
204*4887Schin 					{
205*4887Schin 						*t = ' ';
206*4887Schin 						q = 0;
207*4887Schin 					}
208*4887Schin 					else if (*t == ';' && !q)
209*4887Schin 					{
210*4887Schin 						*t = ' ';
211*4887Schin 						break;
212*4887Schin 					}
213*4887Schin 				for (; t > v && isspace(*(t - 1)); t--);
214*4887Schin 				if (t <= v && (!cap || !k))
215*4887Schin 					break;
216*4887Schin 				if (!cap)
217*4887Schin 				{
218*4887Schin 					if (!(cap = newof(0, Cap_t, 1, strlen(v) + 1)))
219*4887Schin 						return -1;
220*4887Schin 					if (*t)
221*4887Schin 						*t++ = 0;
222*4887Schin 					tta = &cap->att;
223*4887Schin 					tta->name = "default";
224*4887Schin 					x = strcopy(tta->value = cap->data, v) + 1;
225*4887Schin 				}
226*4887Schin 				else if (k)
227*4887Schin 				{
228*4887Schin 					if (*t)
229*4887Schin 						*t++ = 0;
230*4887Schin 					if (!(att = newof(0, Att_t, 1, 0)))
231*4887Schin 						return -1;
232*4887Schin 					x = strcopy(att->name = x, k) + 1;
233*4887Schin 					x = strcopy(att->value = x, v) + 1;
234*4887Schin 					tta = tta->next = att;
235*4887Schin 					if (!strcasecmp(k, "test"))
236*4887Schin 						cap->test = att->value;
237*4887Schin 				}
238*4887Schin 			} while (*(v = t));
239*4887Schin 		}
240*4887Schin 		ent = (Ent_t*)dtmatch(mp->cap, s);
241*4887Schin 		if (cap)
242*4887Schin 		{
243*4887Schin 			if (ent)
244*4887Schin 			{
245*4887Schin 				register Cap_t*	dup;
246*4887Schin 				register Cap_t*	pud;
247*4887Schin 
248*4887Schin 				for (pud = 0, dup = ent->cap; dup; pud = dup, dup = dup->next)
249*4887Schin 					if (!cap->test && !dup->test || cap->test && dup->test && streq(cap->test, dup->test))
250*4887Schin 					{
251*4887Schin 						if (flags & MIME_REPLACE)
252*4887Schin 						{
253*4887Schin 							if (pud)
254*4887Schin 								pud->next = cap;
255*4887Schin 							else
256*4887Schin 								ent->cap = cap;
257*4887Schin 							if (!(cap->next = dup->next))
258*4887Schin 								ent->pac = cap;
259*4887Schin 							cap = dup;
260*4887Schin 						}
261*4887Schin 						dropcap(cap);
262*4887Schin 						return 0;
263*4887Schin 					}
264*4887Schin 				ent->pac = ent->pac->next = cap;
265*4887Schin 			}
266*4887Schin 			else if (!(ent = newof(0, Ent_t, 1, strlen(s) + 1)))
267*4887Schin 				return -1;
268*4887Schin 			else
269*4887Schin 			{
270*4887Schin 				strcpy(ent->name, s);
271*4887Schin 				ent->cap = ent->pac = cap;
272*4887Schin 				dtinsert(mp->cap, ent);
273*4887Schin 			}
274*4887Schin 		}
275*4887Schin 		else if (ent && (flags & MIME_REPLACE))
276*4887Schin 			dtdelete(mp->cap, ent);
277*4887Schin 	}
278*4887Schin 	return 0;
279*4887Schin }
280*4887Schin 
281*4887Schin /*
282*4887Schin  * load mime type files into mp
283*4887Schin  */
284*4887Schin 
285*4887Schin int
286*4887Schin mimeload(Mime_t* mp, const char* file, unsigned long flags)
287*4887Schin {
288*4887Schin 	register char*	s;
289*4887Schin 	register char*	t;
290*4887Schin 	register char*	e;
291*4887Schin 	register int	n;
292*4887Schin 	Sfio_t*		fp;
293*4887Schin 
294*4887Schin 	if (!(s = (char*)file))
295*4887Schin 	{
296*4887Schin 		flags |= MIME_LIST;
297*4887Schin 		if (!(s = getenv(MIME_FILES_ENV)))
298*4887Schin 			s = MIME_FILES;
299*4887Schin 	}
300*4887Schin 	for (;;)
301*4887Schin 	{
302*4887Schin 		if (!(flags & MIME_LIST))
303*4887Schin 			e = 0;
304*4887Schin 		else if (e = strchr(s, ':'))
305*4887Schin 		{
306*4887Schin 			/*
307*4887Schin 			 * ok, so ~ won't work for the last list element
308*4887Schin 			 * we do it for MIME_FILES_ENV anyway
309*4887Schin 			 */
310*4887Schin 
311*4887Schin 			if ((strneq(s, "~/", n = 2) || strneq(s, "$HOME/", n = 6) || strneq(s, "${HOME}/", n = 8)) && (t = getenv("HOME")))
312*4887Schin 			{
313*4887Schin 				sfputr(mp->buf, t, -1);
314*4887Schin 				s += n - 1;
315*4887Schin 			}
316*4887Schin 			sfwrite(mp->buf, s, e - s);
317*4887Schin 			if (!(s = sfstruse(mp->buf)))
318*4887Schin 				return -1;
319*4887Schin 		}
320*4887Schin 		if (fp = tokline(s, SF_READ, NiL))
321*4887Schin 		{
322*4887Schin 			while (t = sfgetr(fp, '\n', 1))
323*4887Schin 				if (mimeset(mp, t, flags))
324*4887Schin 					break;
325*4887Schin 			sfclose(fp);
326*4887Schin 		}
327*4887Schin 		else if (!(flags & MIME_LIST))
328*4887Schin 			return -1;
329*4887Schin 		if (!e)
330*4887Schin 			break;
331*4887Schin 		s = e + 1;
332*4887Schin 	}
333*4887Schin 	return 0;
334*4887Schin }
335*4887Schin 
336*4887Schin /*
337*4887Schin  * mimelist walker
338*4887Schin  */
339*4887Schin 
340*4887Schin static int
341*4887Schin list(Dt_t* dt, void* object, void* context)
342*4887Schin {
343*4887Schin 	register Walk_t*	wp = (Walk_t*)context;
344*4887Schin 	register Ent_t*		ent = (Ent_t*)object;
345*4887Schin 	register Cap_t*		cap;
346*4887Schin 	register Att_t*		att;
347*4887Schin 
348*4887Schin 	if (!wp->pattern || !strncasecmp(ent->name, wp->pattern, wp->prefix) && (!ent->name[wp->prefix] || ent->name[wp->prefix] == '/'))
349*4887Schin 	{
350*4887Schin 		wp->hit++;
351*4887Schin 		for (cap = ent->cap; cap; cap = cap->next)
352*4887Schin 		{
353*4887Schin 			sfprintf(wp->fp, "%s", ent->name);
354*4887Schin 			for (att = &cap->att; att; att = att->next)
355*4887Schin 			{
356*4887Schin 				sfprintf(wp->fp, "\n\t");
357*4887Schin 				if (att != &cap->att)
358*4887Schin 				{
359*4887Schin 					sfprintf(wp->fp, "%s", att->name);
360*4887Schin 					if (*att->value)
361*4887Schin 						sfprintf(wp->fp, " = ");
362*4887Schin 				}
363*4887Schin 				sfputr(wp->fp, att->value, -1);
364*4887Schin 			}
365*4887Schin 			sfprintf(wp->fp, "\n");
366*4887Schin 		}
367*4887Schin 	}
368*4887Schin 	return 0;
369*4887Schin }
370*4887Schin 
371*4887Schin /*
372*4887Schin  * find entry matching type
373*4887Schin  * if exact match fails then left and right x- and right version number
374*4887Schin  * permutations are attempted
375*4887Schin  */
376*4887Schin 
377*4887Schin static Ent_t*
378*4887Schin find(Mime_t* mp, const char* type)
379*4887Schin {
380*4887Schin 	register char*	lp;
381*4887Schin 	register char*	rp;
382*4887Schin 	register char*	rb;
383*4887Schin 	register char*	rv;
384*4887Schin 	register int	rc;
385*4887Schin 	register int	i;
386*4887Schin 	char*		s;
387*4887Schin 	Ent_t*		ent;
388*4887Schin 	char		buf[256];
389*4887Schin 
390*4887Schin 	static const char*	prefix[] = { "", "", "x-", "x-", "" };
391*4887Schin 
392*4887Schin 	if ((ent = (Ent_t*)dtmatch(mp->cap, type)) ||
393*4887Schin 	    !(rp = strchr(lp = (char*)type, '/')) ||
394*4887Schin 	    strlen(lp) >= sizeof(buf))
395*4887Schin 		return ent;
396*4887Schin 	strcpy(buf, type);
397*4887Schin 	rp = buf + (rp - lp);
398*4887Schin 	*rp++ = 0;
399*4887Schin 	if (*rp == 'x' && *(rp + 1) == '-')
400*4887Schin 		rp += 2;
401*4887Schin 	lp = buf;
402*4887Schin 	if (*lp == 'x' && *(lp + 1) == '-')
403*4887Schin 		lp += 2;
404*4887Schin 	rb = rp;
405*4887Schin 	for (rv = rp + strlen(rp); rv > rp && (isdigit(*(rv - 1)) || *(rv - 1) == '.'); rv--);
406*4887Schin 	rc = *rv;
407*4887Schin 	do
408*4887Schin 	{
409*4887Schin 		rp = rb;
410*4887Schin 		do
411*4887Schin 		{
412*4887Schin 			for (i = 0; i < elementsof(prefix) - 1; i++)
413*4887Schin 			{
414*4887Schin 				sfprintf(mp->buf, "%s%s/%s%s", prefix[i], lp, prefix[i + 1], rp);
415*4887Schin 				if (!(s = sfstruse(mp->buf)))
416*4887Schin 					return 0;
417*4887Schin 				if (ent = (Ent_t*)dtmatch(mp->cap, s))
418*4887Schin 					return ent;
419*4887Schin 				if (rc)
420*4887Schin 				{
421*4887Schin 					*rv = 0;
422*4887Schin 					sfprintf(mp->buf, "%s%s/%s%s", prefix[i], lp, prefix[i + 1], rp);
423*4887Schin 					if (!(s = sfstruse(mp->buf)))
424*4887Schin 						return 0;
425*4887Schin 					if (ent = (Ent_t*)dtmatch(mp->cap, s))
426*4887Schin 						return ent;
427*4887Schin 					*rv = rc;
428*4887Schin 				}
429*4887Schin 			}
430*4887Schin 			while (*rp && *rp++ != '-');
431*4887Schin 		} while (*rp);
432*4887Schin 		while (*lp && *lp++ != '-');
433*4887Schin 	} while (*lp);
434*4887Schin 	return (Ent_t*)dtmatch(mp->cap, buf);
435*4887Schin }
436*4887Schin 
437*4887Schin /*
438*4887Schin  * list mime <type,data> for pat on fp
439*4887Schin  */
440*4887Schin 
441*4887Schin int
442*4887Schin mimelist(Mime_t* mp, Sfio_t* fp, register const char* pattern)
443*4887Schin {
444*4887Schin 	Ent_t*	ent;
445*4887Schin 	Walk_t	ws;
446*4887Schin 
447*4887Schin 	ws.fp = fp;
448*4887Schin 	ws.hit = 0;
449*4887Schin 	ws.prefix = 0;
450*4887Schin 	if (ws.pattern = pattern)
451*4887Schin 	{
452*4887Schin 		while (*pattern && *pattern++ != '/');
453*4887Schin 		if (!*pattern || *pattern == '*' && !*(pattern + 1))
454*4887Schin 			ws.prefix = pattern - ws.pattern;
455*4887Schin 		else if (ent = find(mp, ws.pattern))
456*4887Schin 		{
457*4887Schin 			ws.pattern = 0;
458*4887Schin 			list(mp->cap, ent, &ws);
459*4887Schin 			return ws.hit;
460*4887Schin 		}
461*4887Schin 	}
462*4887Schin 	dtwalk(mp->cap, list, &ws);
463*4887Schin 	return ws.hit;
464*4887Schin }
465*4887Schin 
466*4887Schin /*
467*4887Schin  * get next arg in pp
468*4887Schin  * 0 returned if no more args
469*4887Schin  */
470*4887Schin 
471*4887Schin static int
472*4887Schin arg(register Parse_t* pp, int first)
473*4887Schin {
474*4887Schin 	register char*	s;
475*4887Schin 	register int	c;
476*4887Schin 	register int	q;
477*4887Schin 	int		x;
478*4887Schin 
479*4887Schin 	for (s = pp->next; isspace(*s) && *s != '\n'; s++);
480*4887Schin 	if (!*s || *s == '\n')
481*4887Schin 	{
482*4887Schin 		pp->next = s;
483*4887Schin 		return 0;
484*4887Schin 	}
485*4887Schin 	pp->name.data = s;
486*4887Schin 	pp->value.data = 0;
487*4887Schin 	q = 0;
488*4887Schin 	x = 0;
489*4887Schin 	while ((c = *s++) && c != ';' && c != '\n')
490*4887Schin 	{
491*4887Schin 		if (c == '"')
492*4887Schin 		{
493*4887Schin 			q = 1;
494*4887Schin 			if (pp->value.data)
495*4887Schin 			{
496*4887Schin 				pp->value.data = s;
497*4887Schin 				if (x)
498*4887Schin 					x = -1;
499*4887Schin 				else
500*4887Schin 					x = 1;
501*4887Schin 			}
502*4887Schin 			else if (!x && pp->name.data == (s - 1))
503*4887Schin 			{
504*4887Schin 				x = 1;
505*4887Schin 				pp->name.data = s;
506*4887Schin 			}
507*4887Schin 			do
508*4887Schin 			{
509*4887Schin 				if (!(c = *s++) || c == '\n')
510*4887Schin 				{
511*4887Schin 					s--;
512*4887Schin 					break;
513*4887Schin 				}
514*4887Schin 			} while (c != '"');
515*4887Schin 			if (first < 0 || x > 0)
516*4887Schin 			{
517*4887Schin 				c = ';';
518*4887Schin 				break;
519*4887Schin 			}
520*4887Schin  		}
521*4887Schin 		else if (c == '=' && !first)
522*4887Schin 		{
523*4887Schin 			first = 1;
524*4887Schin 			pp->name.size = s - pp->name.data - 1;
525*4887Schin 			pp->value.data = s;
526*4887Schin 		}
527*4887Schin 		else if (first >= 0 && isspace(c))
528*4887Schin 			break;
529*4887Schin 	}
530*4887Schin 	pp->next = s - (c != ';');
531*4887Schin 	if (first >= 0 || !q)
532*4887Schin 		for (s--; s > pp->name.data && isspace(*(s - 1)); s--);
533*4887Schin 	if (pp->value.data)
534*4887Schin 		pp->value.size = s - pp->value.data - (q && first < 0);
535*4887Schin 	else
536*4887Schin 	{
537*4887Schin 		pp->value.size = 0;
538*4887Schin 		pp->name.size = s - pp->name.data - (q && first < 0);
539*4887Schin 	}
540*4887Schin 	if (first >= 0 && pp->name.size > 0 && pp->name.data[pp->name.size - 1] == ':')
541*4887Schin 		return 0;
542*4887Schin 	return pp->name.size > 0;
543*4887Schin }
544*4887Schin 
545*4887Schin /*
546*4887Schin  * low level for mimeview()
547*4887Schin  */
548*4887Schin 
549*4887Schin static char*
550*4887Schin expand(Mime_t* mp, register char* s, const char* name, const char* type, const char* opts)
551*4887Schin {
552*4887Schin 	register char*	t;
553*4887Schin 	register int	c;
554*4887Schin 	Parse_t		pp;
555*4887Schin 
556*4887Schin 	mp->disc->flags |= MIME_PIPE;
557*4887Schin 	for (;;)
558*4887Schin 	{
559*4887Schin 		switch (c = *s++)
560*4887Schin 		{
561*4887Schin 		case 0:
562*4887Schin 		case '\n':
563*4887Schin 			break;
564*4887Schin 		case '%':
565*4887Schin 			switch (c = *s++)
566*4887Schin 			{
567*4887Schin 			case 's':
568*4887Schin 				sfputr(mp->buf, (char*)name, -1);
569*4887Schin 				mp->disc->flags &= ~MIME_PIPE;
570*4887Schin 				continue;
571*4887Schin 			case 't':
572*4887Schin 				sfputr(mp->buf, (char*)type, -1);
573*4887Schin 				continue;
574*4887Schin 			case '{':
575*4887Schin 				for (t = s; *s && *s != '}'; s++);
576*4887Schin 				if (*s && (c = s++ - t) && (pp.next = (char*)opts))
577*4887Schin 					while (arg(&pp, 0))
578*4887Schin 						if (pp.name.size == c && !strncasecmp(pp.name.data, t, c))
579*4887Schin 						{
580*4887Schin 							if (pp.value.size)
581*4887Schin 								sfwrite(mp->buf, pp.value.data, pp.value.size);
582*4887Schin 							break;
583*4887Schin 						}
584*4887Schin 				continue;
585*4887Schin 			}
586*4887Schin 			/*FALLTHROUGH*/
587*4887Schin 		default:
588*4887Schin 			sfputc(mp->buf, c);
589*4887Schin 			continue;
590*4887Schin 		}
591*4887Schin 		break;
592*4887Schin 	}
593*4887Schin 	return sfstruse(mp->buf);
594*4887Schin }
595*4887Schin 
596*4887Schin /*
597*4887Schin  * return expanded command/path/value for <view,name,type,opts>
598*4887Schin  * return value valid until next mime*() call
599*4887Schin  */
600*4887Schin 
601*4887Schin char*
602*4887Schin mimeview(Mime_t* mp, const char* view, const char* name, const char* type, const char* opts)
603*4887Schin {
604*4887Schin 	register Ent_t*	ent;
605*4887Schin 	register Cap_t*	cap;
606*4887Schin 	register Att_t*	att;
607*4887Schin 	register char*	s;
608*4887Schin 	int		c;
609*4887Schin 
610*4887Schin 	if (ent = find(mp, type))
611*4887Schin 	{
612*4887Schin 		cap = ent->cap;
613*4887Schin 		if (!view || strcasecmp(view, "test"))
614*4887Schin 			while (s = cap->test)
615*4887Schin 			{
616*4887Schin 				if (s = expand(mp, s, name, type, opts))
617*4887Schin 				{
618*4887Schin 					Parse_t	a1;
619*4887Schin 					Parse_t	a2;
620*4887Schin 					Parse_t	a3;
621*4887Schin 					Parse_t	a4;
622*4887Schin 
623*4887Schin 					/*
624*4887Schin 					 * try to do a few common cases here
625*4887Schin 					 * mailcap consistency is a winning
626*4887Schin 					 * strategy
627*4887Schin 					 */
628*4887Schin 
629*4887Schin 					a1.next = s;
630*4887Schin 					if (arg(&a1, -1))
631*4887Schin 					{
632*4887Schin 						if ((c = *a1.name.data == '!') && --a1.name.size <= 0 && !arg(&a1, -1))
633*4887Schin 							goto lose;
634*4887Schin 						if (a1.name.size == 6 && strneq(a1.name.data, "strcmp", 6) || a1.name.size == 10 && strneq(a1.name.data, "strcasecmp", 10))
635*4887Schin 						{
636*4887Schin 							a2.next = a1.next;
637*4887Schin 							if (!arg(&a2, -1))
638*4887Schin 								goto lose;
639*4887Schin 							a3.next = a2.next;
640*4887Schin 							if (!arg(&a3, -1))
641*4887Schin 								goto lose;
642*4887Schin 							if (a2.name.size != a3.name.size)
643*4887Schin 								c ^= 0;
644*4887Schin 							else c ^= (a1.name.size == 6 ? strncmp : strncasecmp)(a2.name.data, a3.name.data, a2.name.size) == 0;
645*4887Schin 							if (c)
646*4887Schin 								break;
647*4887Schin 							goto skip;
648*4887Schin 						}
649*4887Schin 						else if (a1.name.size == 4 && strneq(a1.name.data, "test", 4))
650*4887Schin 						{
651*4887Schin 							if (!arg(&a1, -1))
652*4887Schin 								goto lose;
653*4887Schin 							a2.next = a1.next;
654*4887Schin 							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)))
655*4887Schin 								goto lose;
656*4887Schin 							a3.next = a2.next;
657*4887Schin 							if (!arg(&a3, -1))
658*4887Schin 								goto lose;
659*4887Schin 							if (*a3.name.data == '`' && *(a3.name.data + a3.name.size - 1) == '`')
660*4887Schin 							{
661*4887Schin 								a4 = a3;
662*4887Schin 								a3 = a1;
663*4887Schin 								a1 = a4;
664*4887Schin 							}
665*4887Schin 							if (*a1.name.data == '`' && *(a1.name.data + a1.name.size - 1) == '`')
666*4887Schin 							{
667*4887Schin 								a1.next = a1.name.data + 1;
668*4887Schin 								if (!arg(&a1, -1) || a1.name.size != 4 || !strneq(a1.name.data, "echo", 4) || !arg(&a1, -1))
669*4887Schin 									goto lose;
670*4887Schin 								a4.next = a1.next;
671*4887Schin 								if (!arg(&a4, 1) || a4.name.size < 21 || !strneq(a4.name.data, "| tr '[A-Z]' '[a-z]'`", 21))
672*4887Schin 									goto lose;
673*4887Schin 							}
674*4887Schin 							else
675*4887Schin 								a4.name.size = 0;
676*4887Schin 							c = *a2.name.data == '!';
677*4887Schin 							if (a1.name.size != a3.name.size)
678*4887Schin 								c ^= 0;
679*4887Schin 							else c ^= (a4.name.size ? strncasecmp : strncmp)(a1.name.data, a3.name.data, a1.name.size) == 0;
680*4887Schin 							if (c)
681*4887Schin 								break;
682*4887Schin 							goto skip;
683*4887Schin 						}
684*4887Schin 					}
685*4887Schin 				lose:
686*4887Schin 					if (!system(s))
687*4887Schin 						break;
688*4887Schin 				}
689*4887Schin 			skip:
690*4887Schin 				if (!(cap = cap->next))
691*4887Schin 					return 0;
692*4887Schin 			}
693*4887Schin 		att = &cap->att;
694*4887Schin 		if (view && *view && !streq(view, "-"))
695*4887Schin 			while (strcasecmp(view, att->name))
696*4887Schin 				if (!(att = att->next))
697*4887Schin 					return 0;
698*4887Schin 		return expand(mp, att->value, name, type, opts);
699*4887Schin 	}
700*4887Schin 	return 0;
701*4887Schin }
702*4887Schin 
703*4887Schin /*
704*4887Schin  * lower case identifier prefix strcmp
705*4887Schin  * if e!=0 then it will point to the next char after the match
706*4887Schin  */
707*4887Schin 
708*4887Schin int
709*4887Schin mimecmp(register const char* s, register const char* v, char** e)
710*4887Schin {
711*4887Schin 	register int	n;
712*4887Schin 
713*4887Schin 	while (isalnum(*v) || *v == *s && (*v == '_' || *v == '-' || *v == '/'))
714*4887Schin 		if (n = lower(*s++) - lower(*v++))
715*4887Schin 			return n;
716*4887Schin 	if (!isalnum(*s) && *s != '_' && *s != '-')
717*4887Schin 	{
718*4887Schin 		if (e)
719*4887Schin 			*e = (char*)s;
720*4887Schin 		return 0;
721*4887Schin 	}
722*4887Schin 	return lower(*s) - lower(*v);
723*4887Schin }
724*4887Schin 
725*4887Schin /*
726*4887Schin  * parse mime headers in strsearch(tab,num,siz) from s
727*4887Schin  * return >0 if mime header consumed
728*4887Schin  */
729*4887Schin 
730*4887Schin int
731*4887Schin mimehead(Mime_t* mp, void* tab, size_t num, size_t siz, register char* s)
732*4887Schin {
733*4887Schin 	register void*	p;
734*4887Schin 	char*		e;
735*4887Schin 	Parse_t		pp;
736*4887Schin 	Mimevalue_f	set;
737*4887Schin 
738*4887Schin 	set = mp->disc->valuef;
739*4887Schin 	if (!strncasecmp(s, "original-", 9))
740*4887Schin 		s += 9;
741*4887Schin 	if (!strncasecmp(s, "content-", 8))
742*4887Schin 	{
743*4887Schin 		s += 8;
744*4887Schin 		if ((p = strsearch(tab, num, siz, (Strcmp_f)mimecmp, s, &e)) && *e == ':')
745*4887Schin 		{
746*4887Schin 			pp.next = e + 1;
747*4887Schin 			if (arg(&pp, 1))
748*4887Schin 			{
749*4887Schin 				if ((*set)(mp, p, pp.name.data, pp.name.size, mp->disc))
750*4887Schin 					return 0;
751*4887Schin 				while (arg(&pp, 0))
752*4887Schin 					if (pp.value.size &&
753*4887Schin 					    (p = strsearch(tab, num, siz, (Strcmp_f)mimecmp, pp.name.data, &e)) &&
754*4887Schin 					    (*set)(mp, p, pp.value.data, pp.value.size, mp->disc))
755*4887Schin 						return 0;
756*4887Schin 				return 1;
757*4887Schin 			}
758*4887Schin 		}
759*4887Schin 		else if (strchr(s, ':'))
760*4887Schin 			return 1;
761*4887Schin 	}
762*4887Schin 	return !strncasecmp(s, "x-", 2);
763*4887Schin }
764*4887Schin 
765*4887Schin /*
766*4887Schin  * open a mime library handle
767*4887Schin  */
768*4887Schin 
769*4887Schin Mime_t*
770*4887Schin mimeopen(Mimedisc_t* disc)
771*4887Schin {
772*4887Schin 	register Mime_t*	mp;
773*4887Schin 
774*4887Schin 	if (!(mp = newof(0, Mime_t, 1, 0)))
775*4887Schin 		return 0;
776*4887Schin 	mp->id = lib;
777*4887Schin 	mp->disc = disc;
778*4887Schin 	mp->dict.key = offsetof(Ent_t, name);
779*4887Schin 	mp->dict.comparf = order;
780*4887Schin 	mp->dict.freef = drop;
781*4887Schin 	if (!(mp->buf = sfstropen()) || !(mp->cap = dtopen(&mp->dict, Dtorder)))
782*4887Schin 	{
783*4887Schin 		mimeclose(mp);
784*4887Schin 		return 0;
785*4887Schin 	}
786*4887Schin 	return mp;
787*4887Schin }
788*4887Schin 
789*4887Schin /*
790*4887Schin  * close a mimeopen() handle
791*4887Schin  */
792*4887Schin 
793*4887Schin int
794*4887Schin mimeclose(Mime_t* mp)
795*4887Schin {
796*4887Schin 	if (mp)
797*4887Schin 	{
798*4887Schin 		if (mp->buf)
799*4887Schin 			sfclose(mp->buf);
800*4887Schin 		if (mp->cap)
801*4887Schin 			dtclose(mp->cap);
802*4887Schin 		if (mp->freef)
803*4887Schin 			(*mp->freef)(mp);
804*4887Schin 		free(mp);
805*4887Schin 	}
806*4887Schin 	return 0;
807*4887Schin }
808