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