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