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