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