diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2012-06-24 22:28:35 +0000 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2012-06-24 22:28:35 +0000 |
commit | 3950ffe2a485479f6561c27364d3d7df5a21d124 (patch) | |
tree | 468c6e14449d1b1e279222ec32f676b0311917d2 /src/lib/libast/misc/mime.c | |
download | ksh-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.c | 839 |
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; +} |