summaryrefslogtreecommitdiff
path: root/src/lib/libast/misc/mime.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
committerIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
commit3950ffe2a485479f6561c27364d3d7df5a21d124 (patch)
tree468c6e14449d1b1e279222ec32f676b0311917d2 /src/lib/libast/misc/mime.c
downloadksh-upstream.tar.gz
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/lib/libast/misc/mime.c')
-rw-r--r--src/lib/libast/misc/mime.c839
1 files changed, 839 insertions, 0 deletions
diff --git a/src/lib/libast/misc/mime.c b/src/lib/libast/misc/mime.c
new file mode 100644
index 0000000..49cfaa1
--- /dev/null
+++ b/src/lib/libast/misc/mime.c
@@ -0,0 +1,839 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1985-2011 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* Phong Vo <kpv@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+
+/*
+ * Glenn Fowler
+ * AT&T Research
+ *
+ * mime/mailcap support library
+ */
+
+static const char id[] = "\n@(#)$Id: mime library (AT&T Research) 2002-10-29 $\0\n";
+
+static const char lib[] = "libast:mime";
+
+#include "mimelib.h"
+
+typedef struct Att_s
+{
+ struct Att_s* next;
+ char* name;
+ char* value;
+} Att_t;
+
+typedef struct Cap_s
+{
+ struct Cap_s* next;
+ unsigned long flags;
+ Att_t att;
+ char* test;
+ char data[1];
+} Cap_t;
+
+typedef struct
+{
+ Dtlink_t link;
+ Cap_t* cap;
+ Cap_t* pac;
+ char name[1];
+} Ent_t;
+
+typedef struct
+{
+ char* data;
+ int size;
+} String_t;
+
+typedef struct
+{
+ char* next;
+ String_t name;
+ String_t value;
+} Parse_t;
+
+typedef struct
+{
+ const char* pattern;
+ int prefix;
+ Sfio_t* fp;
+ int hit;
+} Walk_t;
+
+/*
+ * convert c to lower case
+ */
+
+static int
+lower(register int c)
+{
+ return isupper(c) ? tolower(c) : c;
+}
+
+/*
+ * Ent_t case insensitive comparf
+ */
+
+static int
+order(Dt_t* dt, void* a, void* b, Dtdisc_t* disc)
+{
+ return strcasecmp(a, b);
+}
+
+/*
+ * Cap_t free
+ */
+
+static void
+dropcap(register Cap_t* cap)
+{
+ register Att_t* att;
+
+ while (att = cap->att.next)
+ {
+ cap->att.next = att->next;
+ free(att);
+ }
+ free(cap);
+}
+
+/*
+ * Ent_t freef
+ */
+
+static void
+drop(Dt_t* dt, void* object, Dtdisc_t* disc)
+{
+ register Ent_t* ent = (Ent_t*)object;
+ register Cap_t* cap;
+
+ while (cap = ent->cap)
+ {
+ ent->cap = cap->next;
+ dropcap(cap);
+ }
+ free(ent);
+}
+
+/*
+ * add mime type entry in s to mp
+ */
+
+int
+mimeset(Mime_t* mp, register char* s, unsigned long flags)
+{
+ register Ent_t* ent;
+ register Cap_t* cap;
+ register Att_t* att;
+ register char* t;
+ register char* v;
+ register char* k;
+ char* x;
+ Att_t* tta;
+ int q;
+
+ for (; isspace(*s); s++);
+ if (*s && *s != '#')
+ {
+ cap = 0;
+ for (v = s; *v && *v != ';'; v++)
+ if (isspace(*v) || *v == '/' && *(v + 1) == '*')
+ *v = 0;
+ if (*v)
+ {
+ *v++ = 0;
+ do
+ {
+ for (; isspace(*v); v++);
+ if (cap)
+ {
+ for (t = v; *t && !isspace(*t) && *t != '='; t++);
+ for (k = t; isspace(*t); t++);
+ if (!*t || *t == '=' || *t == ';')
+ {
+ if (*t)
+ while (isspace(*++t));
+ *k = 0;
+ k = v;
+ v = t;
+ }
+ else
+ k = 0;
+ }
+ if (*v == '"')
+ q = *v++;
+ else
+ q = 0;
+ for (t = v; *t; t++)
+ if (*t == '\\')
+ {
+ switch (*(t + 1))
+ {
+ case 0:
+ case '\\':
+ case '%':
+ *t = *(t + 1);
+ break;
+ default:
+ *t = ' ';
+ break;
+ }
+ if (!*++t)
+ break;
+ }
+ else if (*t == q)
+ {
+ *t = ' ';
+ q = 0;
+ }
+ else if (*t == ';' && !q)
+ {
+ *t = ' ';
+ break;
+ }
+ for (; t > v && isspace(*(t - 1)); t--);
+ if (t <= v && (!cap || !k))
+ break;
+ if (!cap)
+ {
+ if (!(cap = newof(0, Cap_t, 1, strlen(v) + 1)))
+ return -1;
+ if (*t)
+ *t++ = 0;
+ tta = &cap->att;
+ tta->name = "default";
+ x = strcopy(tta->value = cap->data, v) + 1;
+ }
+ else if (k)
+ {
+ if (*t)
+ *t++ = 0;
+ if (!(att = newof(0, Att_t, 1, 0)))
+ return -1;
+ x = strcopy(att->name = x, k) + 1;
+ x = strcopy(att->value = x, v) + 1;
+ tta = tta->next = att;
+ if (!strcasecmp(k, "test"))
+ cap->test = att->value;
+ }
+ } while (*(v = t));
+ }
+ ent = (Ent_t*)dtmatch(mp->cap, s);
+ if (cap)
+ {
+ if (ent)
+ {
+ register Cap_t* dup;
+ register Cap_t* pud;
+
+ for (pud = 0, dup = ent->cap; dup; pud = dup, dup = dup->next)
+ if (!cap->test && !dup->test || cap->test && dup->test && streq(cap->test, dup->test))
+ {
+ if (flags & MIME_REPLACE)
+ {
+ if (pud)
+ pud->next = cap;
+ else
+ ent->cap = cap;
+ if (!(cap->next = dup->next))
+ ent->pac = cap;
+ cap = dup;
+ }
+ dropcap(cap);
+ return 0;
+ }
+ ent->pac = ent->pac->next = cap;
+ }
+ else if (!(ent = newof(0, Ent_t, 1, strlen(s) + 1)))
+ return -1;
+ else
+ {
+ strcpy(ent->name, s);
+ ent->cap = ent->pac = cap;
+ dtinsert(mp->cap, ent);
+ }
+ }
+ else if (ent && (flags & MIME_REPLACE))
+ dtdelete(mp->cap, ent);
+ }
+ return 0;
+}
+
+/*
+ * load mime type files into mp
+ */
+
+int
+mimeload(Mime_t* mp, const char* file, unsigned long flags)
+{
+ register char* s;
+ register char* t;
+ register char* e;
+ register int n;
+ Sfio_t* fp;
+
+ if (!(s = (char*)file))
+ {
+ flags |= MIME_LIST;
+ if (!(s = getenv(MIME_FILES_ENV)))
+ s = MIME_FILES;
+ }
+ for (;;)
+ {
+ if (!(flags & MIME_LIST))
+ e = 0;
+ else if (e = strchr(s, ':'))
+ {
+ /*
+ * ok, so ~ won't work for the last list element
+ * we do it for MIME_FILES_ENV anyway
+ */
+
+ if ((strneq(s, "~/", n = 2) || strneq(s, "$HOME/", n = 6) || strneq(s, "${HOME}/", n = 8)) && (t = getenv("HOME")))
+ {
+ sfputr(mp->buf, t, -1);
+ s += n - 1;
+ }
+ sfwrite(mp->buf, s, e - s);
+ if (!(s = sfstruse(mp->buf)))
+ return -1;
+ }
+ if (fp = tokline(s, SF_READ, NiL))
+ {
+ while (t = sfgetr(fp, '\n', 1))
+ if (mimeset(mp, t, flags))
+ break;
+ sfclose(fp);
+ }
+ else if (!(flags & MIME_LIST))
+ return -1;
+ if (!e)
+ break;
+ s = e + 1;
+ }
+ return 0;
+}
+
+/*
+ * mimelist walker
+ */
+
+static int
+list(Dt_t* dt, void* object, void* context)
+{
+ register Walk_t* wp = (Walk_t*)context;
+ register Ent_t* ent = (Ent_t*)object;
+ register Cap_t* cap;
+ register Att_t* att;
+
+ if (!wp->pattern || !strncasecmp(ent->name, wp->pattern, wp->prefix) && (!ent->name[wp->prefix] || ent->name[wp->prefix] == '/'))
+ {
+ wp->hit++;
+ for (cap = ent->cap; cap; cap = cap->next)
+ {
+ sfprintf(wp->fp, "%s", ent->name);
+ for (att = &cap->att; att; att = att->next)
+ {
+ sfprintf(wp->fp, "\n\t");
+ if (att != &cap->att)
+ {
+ sfprintf(wp->fp, "%s", att->name);
+ if (*att->value)
+ sfprintf(wp->fp, " = ");
+ }
+ sfputr(wp->fp, att->value, -1);
+ }
+ sfprintf(wp->fp, "\n");
+ }
+ }
+ return 0;
+}
+
+/*
+ * find entry matching type
+ * if exact match fails then left and right x- and right version number
+ * permutations are attempted
+ */
+
+static Ent_t*
+find(Mime_t* mp, const char* type)
+{
+ register char* lp;
+ register char* rp;
+ register char* rb;
+ register char* rv;
+ register int rc;
+ register int i;
+ char* s;
+ Ent_t* ent;
+ char buf[256];
+
+ static const char* prefix[] = { "", "", "x-", "x-", "" };
+
+ if ((ent = (Ent_t*)dtmatch(mp->cap, type)) ||
+ !(rp = strchr(lp = (char*)type, '/')) ||
+ strlen(lp) >= sizeof(buf))
+ return ent;
+ strcpy(buf, type);
+ rp = buf + (rp - lp);
+ *rp++ = 0;
+ if (*rp == 'x' && *(rp + 1) == '-')
+ rp += 2;
+ lp = buf;
+ if (*lp == 'x' && *(lp + 1) == '-')
+ lp += 2;
+ rb = rp;
+ for (rv = rp + strlen(rp); rv > rp && (isdigit(*(rv - 1)) || *(rv - 1) == '.'); rv--);
+ rc = *rv;
+ do
+ {
+ rp = rb;
+ do
+ {
+ for (i = 0; i < elementsof(prefix) - 1; i++)
+ {
+ sfprintf(mp->buf, "%s%s/%s%s", prefix[i], lp, prefix[i + 1], rp);
+ if (!(s = sfstruse(mp->buf)))
+ return 0;
+ if (ent = (Ent_t*)dtmatch(mp->cap, s))
+ return ent;
+ if (rc)
+ {
+ *rv = 0;
+ sfprintf(mp->buf, "%s%s/%s%s", prefix[i], lp, prefix[i + 1], rp);
+ if (!(s = sfstruse(mp->buf)))
+ return 0;
+ if (ent = (Ent_t*)dtmatch(mp->cap, s))
+ return ent;
+ *rv = rc;
+ }
+ }
+ while (*rp && *rp++ != '-');
+ } while (*rp);
+ while (*lp && *lp++ != '-');
+ } while (*lp);
+ return (Ent_t*)dtmatch(mp->cap, buf);
+}
+
+/*
+ * list mime <type,data> for pat on fp
+ */
+
+int
+mimelist(Mime_t* mp, Sfio_t* fp, register const char* pattern)
+{
+ Ent_t* ent;
+ Walk_t ws;
+
+ ws.fp = fp;
+ ws.hit = 0;
+ ws.prefix = 0;
+ if (ws.pattern = pattern)
+ {
+ while (*pattern && *pattern++ != '/');
+ if (!*pattern || *pattern == '*' && !*(pattern + 1))
+ ws.prefix = pattern - ws.pattern;
+ else if (ent = find(mp, ws.pattern))
+ {
+ ws.pattern = 0;
+ list(mp->cap, ent, &ws);
+ return ws.hit;
+ }
+ }
+ dtwalk(mp->cap, list, &ws);
+ return ws.hit;
+}
+
+/*
+ * get next arg in pp
+ * 0 returned if no more args
+ */
+
+static int
+arg(register Parse_t* pp, int first)
+{
+ register char* s;
+ register int c;
+ register int q;
+ int x;
+
+ for (s = pp->next; isspace(*s) && *s != '\n'; s++);
+ if (!*s || *s == '\n')
+ {
+ pp->next = s;
+ return 0;
+ }
+ pp->name.data = s;
+ pp->value.data = 0;
+ q = 0;
+ x = 0;
+ while ((c = *s++) && c != ';' && c != '\n')
+ {
+ if (c == '"')
+ {
+ q = 1;
+ if (pp->value.data)
+ {
+ pp->value.data = s;
+ if (x)
+ x = -1;
+ else
+ x = 1;
+ }
+ else if (!x && pp->name.data == (s - 1))
+ {
+ x = 1;
+ pp->name.data = s;
+ }
+ do
+ {
+ if (!(c = *s++) || c == '\n')
+ {
+ s--;
+ break;
+ }
+ } while (c != '"');
+ if (first < 0 || x > 0)
+ {
+ c = ';';
+ break;
+ }
+ }
+ else if (c == '=' && !first)
+ {
+ first = 1;
+ pp->name.size = s - pp->name.data - 1;
+ pp->value.data = s;
+ }
+ else if (first >= 0 && isspace(c))
+ break;
+ }
+ pp->next = s - (c != ';');
+ if (first >= 0 || !q)
+ for (s--; s > pp->name.data && isspace(*(s - 1)); s--);
+ if (pp->value.data)
+ pp->value.size = s - pp->value.data - (q && first < 0);
+ else
+ {
+ pp->value.size = 0;
+ pp->name.size = s - pp->name.data - (q && first < 0);
+ }
+ if (first >= 0 && pp->name.size > 0 && pp->name.data[pp->name.size - 1] == ':')
+ return 0;
+ return pp->name.size > 0;
+}
+
+/*
+ * low level for mimeview()
+ */
+
+static char*
+expand(Mime_t* mp, register char* s, const char* name, const char* type, const char* opts)
+{
+ register char* t;
+ register char* v;
+ register int c;
+ register int e;
+ register int n;
+ Parse_t pp;
+
+ mp->disc->flags |= MIME_PIPE;
+ for (;;)
+ {
+ switch (c = *s++)
+ {
+ case 0:
+ case '\n':
+ break;
+ case '%':
+ if ((c = *s++) == '{' && (e = '}') || c == '(' && (e = ')'))
+ {
+ for (t = s; *s && *s != e; s++);
+ n = s - t;
+ switch (*s)
+ {
+ case '}':
+ s++;
+ c = '{';
+ break;
+ case ')':
+ s++;
+ if (c = *s)
+ s++;
+ break;
+ }
+ }
+ else
+ t = 0;
+ switch (c)
+ {
+ case 's':
+ v = (char*)name;
+ mp->disc->flags &= ~MIME_PIPE;
+ break;
+ case 't':
+ v = (char*)type;
+ break;
+ case '{':
+ for (t = s; *s && *s != '}'; s++);
+ if (*s && (c = s++ - t) && (pp.next = (char*)opts))
+ while (arg(&pp, 0))
+ if (pp.name.size == c && !strncasecmp(pp.name.data, t, c))
+ {
+ if (pp.value.size)
+ sfwrite(mp->buf, pp.value.data, pp.value.size);
+ break;
+ }
+ continue;
+ default:
+ sfputc(mp->buf, c);
+ continue;
+ }
+ if (v && *v)
+ n = strlen(v);
+ else if (t)
+ v = t;
+ else
+ continue;
+ sfputr(mp->buf, fmtquote(v, 0, 0, n, FMT_SHELL), -1);
+ continue;
+ default:
+ sfputc(mp->buf, c);
+ continue;
+ }
+ break;
+ }
+ return sfstruse(mp->buf);
+}
+
+/*
+ * return expanded command/path/value for <view,name,type,opts>
+ * return value valid until next mime*() call
+ */
+
+char*
+mimeview(Mime_t* mp, const char* view, const char* name, const char* type, const char* opts)
+{
+ register Ent_t* ent;
+ register Cap_t* cap;
+ register Att_t* att;
+ register char* s;
+ int c;
+
+ if (ent = find(mp, type))
+ {
+ cap = ent->cap;
+ if (!view || strcasecmp(view, "test"))
+ while (s = cap->test)
+ {
+ if (s = expand(mp, s, name, type, opts))
+ {
+ Parse_t a1;
+ Parse_t a2;
+ Parse_t a3;
+ Parse_t a4;
+
+ /*
+ * try to do a few common cases here
+ * mailcap consistency is a winning
+ * strategy
+ */
+
+ a1.next = s;
+ if (arg(&a1, -1))
+ {
+ if ((c = *a1.name.data == '!') && --a1.name.size <= 0 && !arg(&a1, -1))
+ goto lose;
+ if (a1.name.size == 6 && strneq(a1.name.data, "strcmp", 6) || a1.name.size == 10 && strneq(a1.name.data, "strcasecmp", 10))
+ {
+ a2.next = a1.next;
+ if (!arg(&a2, -1))
+ goto lose;
+ a3.next = a2.next;
+ if (!arg(&a3, -1))
+ goto lose;
+ if (a2.name.size != a3.name.size)
+ c ^= 0;
+ else c ^= (a1.name.size == 6 ? strncmp : strncasecmp)(a2.name.data, a3.name.data, a2.name.size) == 0;
+ if (c)
+ break;
+ goto skip;
+ }
+ else if (a1.name.size == 4 && strneq(a1.name.data, "test", 4))
+ {
+ if (!arg(&a1, -1))
+ goto lose;
+ a2.next = a1.next;
+ 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)))
+ goto lose;
+ a3.next = a2.next;
+ if (!arg(&a3, -1))
+ goto lose;
+ if (*a3.name.data == '`' && *(a3.name.data + a3.name.size - 1) == '`')
+ {
+ a4 = a3;
+ a3 = a1;
+ a1 = a4;
+ }
+ if (*a1.name.data == '`' && *(a1.name.data + a1.name.size - 1) == '`')
+ {
+ a1.next = a1.name.data + 1;
+ if (!arg(&a1, -1) || a1.name.size != 4 || !strneq(a1.name.data, "echo", 4) || !arg(&a1, -1))
+ goto lose;
+ a4.next = a1.next;
+ if (!arg(&a4, 1) || a4.name.size < 21 || !strneq(a4.name.data, "| tr '[A-Z]' '[a-z]'`", 21))
+ goto lose;
+ }
+ else
+ a4.name.size = 0;
+ c = *a2.name.data == '!';
+ if (a1.name.size != a3.name.size)
+ c ^= 0;
+ else c ^= (a4.name.size ? strncasecmp : strncmp)(a1.name.data, a3.name.data, a1.name.size) == 0;
+ if (c)
+ break;
+ goto skip;
+ }
+ }
+ lose:
+ if (!system(s))
+ break;
+ }
+ skip:
+ if (!(cap = cap->next))
+ return 0;
+ }
+ att = &cap->att;
+ if (view && *view && !streq(view, "-"))
+ while (strcasecmp(view, att->name))
+ if (!(att = att->next))
+ return 0;
+ return expand(mp, att->value, name, type, opts);
+ }
+ return 0;
+}
+
+/*
+ * lower case identifier prefix strcmp
+ * if e!=0 then it will point to the next char after the match
+ */
+
+int
+mimecmp(register const char* s, register const char* v, char** e)
+{
+ register int n;
+
+ while (isalnum(*v) || *v == *s && (*v == '_' || *v == '-' || *v == '/'))
+ if (n = lower(*s++) - lower(*v++))
+ return n;
+ if (!isalnum(*s) && *s != '_' && *s != '-')
+ {
+ if (e)
+ *e = (char*)s;
+ return 0;
+ }
+ return lower(*s) - lower(*v);
+}
+
+/*
+ * parse mime headers in strsearch(tab,num,siz) from s
+ * return >0 if mime header consumed
+ */
+
+int
+mimehead(Mime_t* mp, void* tab, size_t num, size_t siz, register char* s)
+{
+ register void* p;
+ char* e;
+ Parse_t pp;
+ Mimevalue_f set;
+
+ set = mp->disc->valuef;
+ if (!strncasecmp(s, "original-", 9))
+ s += 9;
+ if (!strncasecmp(s, "content-", 8))
+ {
+ s += 8;
+ if ((p = strsearch(tab, num, siz, (Strcmp_f)mimecmp, s, &e)) && *e == ':')
+ {
+ pp.next = e + 1;
+ if (arg(&pp, 1))
+ {
+ if ((*set)(mp, p, pp.name.data, pp.name.size, mp->disc))
+ return 0;
+ while (arg(&pp, 0))
+ if (pp.value.size &&
+ (p = strsearch(tab, num, siz, (Strcmp_f)mimecmp, pp.name.data, &e)) &&
+ (*set)(mp, p, pp.value.data, pp.value.size, mp->disc))
+ return 0;
+ return 1;
+ }
+ }
+ else if (strchr(s, ':'))
+ return 1;
+ }
+ return !strncasecmp(s, "x-", 2);
+}
+
+/*
+ * open a mime library handle
+ */
+
+Mime_t*
+mimeopen(Mimedisc_t* disc)
+{
+ register Mime_t* mp;
+
+ if (!(mp = newof(0, Mime_t, 1, 0)))
+ return 0;
+ mp->id = lib;
+ mp->disc = disc;
+ mp->dict.key = offsetof(Ent_t, name);
+ mp->dict.comparf = order;
+ mp->dict.freef = drop;
+ if (!(mp->buf = sfstropen()) || !(mp->cap = dtopen(&mp->dict, Dtoset)))
+ {
+ mimeclose(mp);
+ return 0;
+ }
+ return mp;
+}
+
+/*
+ * close a mimeopen() handle
+ */
+
+int
+mimeclose(Mime_t* mp)
+{
+ if (mp)
+ {
+ if (mp->buf)
+ sfclose(mp->buf);
+ if (mp->cap)
+ dtclose(mp->cap);
+ if (mp->freef)
+ (*mp->freef)(mp);
+ free(mp);
+ }
+ return 0;
+}