425 lines
8.7 KiB
C
425 lines
8.7 KiB
C
/* $Id: mailcap.c,v 1.3 2001/11/15 00:32:13 a-ito Exp $ */
|
|
#include "fm.h"
|
|
#include "myctype.h"
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include "parsetag.h"
|
|
#include "local.h"
|
|
#ifdef __EMX__
|
|
#include <strings.h> /* for bzero() */
|
|
#endif /* __EMX__ */
|
|
|
|
static struct mailcap DefaultMailcap[] =
|
|
{
|
|
{"image/*", "xv %s", 0, NULL, NULL, NULL}, /* */
|
|
{"audio/basic", "showaudio %s", 0, NULL, NULL, NULL},
|
|
{NULL, NULL, 0, NULL, NULL, NULL}
|
|
};
|
|
|
|
void
|
|
initMailcap()
|
|
{
|
|
int i;
|
|
TextListItem *tl;
|
|
|
|
if (non_null(mailcap_files))
|
|
mailcap_list = make_domain_list(mailcap_files);
|
|
else
|
|
mailcap_list = NULL;
|
|
if (mailcap_list == NULL)
|
|
return;
|
|
UserMailcap = New_N(struct mailcap *, mailcap_list->nitem);
|
|
for (i = 0, tl = mailcap_list->first; tl; i++, tl = tl->next)
|
|
UserMailcap[i] = loadMailcap(tl->ptr);
|
|
}
|
|
|
|
int
|
|
mailcapMatch(struct mailcap *mcap, char *type)
|
|
{
|
|
char *cap = mcap->type, *p;
|
|
int level;
|
|
for (p = cap; *p != '/'; p++) {
|
|
if (tolower(*p) != tolower(*type))
|
|
return 0;
|
|
type++;
|
|
}
|
|
if (*type != '/')
|
|
return 0;
|
|
p++;
|
|
type++;
|
|
if (mcap->flags & MAILCAP_HTMLOUTPUT)
|
|
level = 1;
|
|
else
|
|
level = 0;
|
|
if (*p == '*')
|
|
return 10 + level;
|
|
while (*p) {
|
|
if (tolower(*p) != tolower(*type))
|
|
return 0;
|
|
p++;
|
|
type++;
|
|
}
|
|
if (*type != '\0')
|
|
return 0;
|
|
return 20 + level;
|
|
}
|
|
|
|
struct mailcap *
|
|
searchMailcap(struct mailcap *table, char *type)
|
|
{
|
|
int level = 0;
|
|
struct mailcap *mcap = NULL;
|
|
int i;
|
|
|
|
if (table == NULL)
|
|
return NULL;
|
|
for (; table->type; table++) {
|
|
i = mailcapMatch(table, type);
|
|
if (i > level) {
|
|
if (table->test) {
|
|
Str command = unquote_mailcap(table->test, type, NULL, NULL, NULL);
|
|
if (system(command->ptr) != 0)
|
|
continue;
|
|
}
|
|
level = i;
|
|
mcap = table;
|
|
}
|
|
}
|
|
return mcap;
|
|
}
|
|
|
|
static int
|
|
matchMailcapAttr(char *p, char *attr, int len, Str * value)
|
|
{
|
|
int quoted;
|
|
char *q = NULL;
|
|
|
|
if (strncasecmp(p, attr, len) == 0) {
|
|
p += len;
|
|
SKIP_BLANKS(p);
|
|
if (value) {
|
|
*value = Strnew();
|
|
if (*p == '=') {
|
|
p++;
|
|
SKIP_BLANKS(p);
|
|
quoted = 0;
|
|
while (*p && (quoted || *p != ';')) {
|
|
if (quoted || !IS_SPACE(*p))
|
|
q = p;
|
|
if (quoted)
|
|
quoted = 0;
|
|
else if (*p == '\\')
|
|
quoted = 1;
|
|
Strcat_char(*value, *p);
|
|
p++;
|
|
}
|
|
if (q)
|
|
Strshrink(*value, p - q - 1);
|
|
}
|
|
return 1;
|
|
}
|
|
else {
|
|
if (*p == '\0' || *p == ';') {
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
extractMailcapEntry(char *mcap_entry, struct mailcap *mcap)
|
|
{
|
|
int j, k;
|
|
char *p;
|
|
int quoted;
|
|
Str tmp;
|
|
|
|
bzero(mcap, sizeof(struct mailcap));
|
|
p = mcap_entry;
|
|
SKIP_BLANKS(p);
|
|
k = -1;
|
|
for (j = 0; p[j] && p[j] != ';'; j++) {
|
|
if (!IS_SPACE(p[j]))
|
|
k = j;
|
|
}
|
|
mcap->type = allocStr(p, (k >= 0)? k + 1 : j);
|
|
if (!p[j])
|
|
return 0;
|
|
p += j + 1;
|
|
|
|
SKIP_BLANKS(p);
|
|
k = -1;
|
|
quoted = 0;
|
|
for (j = 0; p[j] && (quoted || p[j] != ';'); j++) {
|
|
if (quoted || !IS_SPACE(p[j]))
|
|
k = j;
|
|
if (quoted)
|
|
quoted = 0;
|
|
else if (p[j] == '\\')
|
|
quoted = 1;
|
|
}
|
|
mcap->viewer = allocStr(p, (k >= 0)? k + 1 : j);
|
|
p += j;
|
|
|
|
while (*p == ';') {
|
|
p++;
|
|
SKIP_BLANKS(p);
|
|
if (matchMailcapAttr(p, "needsterminal", 13, NULL)) {
|
|
mcap->flags |= MAILCAP_NEEDSTERMINAL;
|
|
}
|
|
else if (matchMailcapAttr(p, "copiousoutput", 13, NULL)) {
|
|
mcap->flags |= MAILCAP_COPIOUSOUTPUT;
|
|
}
|
|
else if (matchMailcapAttr(p, "x-htmloutput", 12, NULL) ||
|
|
matchMailcapAttr(p, "htmloutput", 10, NULL)) {
|
|
mcap->flags |= MAILCAP_HTMLOUTPUT;
|
|
}
|
|
else if (matchMailcapAttr(p, "test", 4, &tmp)) {
|
|
mcap->test = allocStr(tmp->ptr, tmp->length);
|
|
}
|
|
else if (matchMailcapAttr(p, "nametemplate", 12, &tmp)) {
|
|
mcap->nametemplate = allocStr(tmp->ptr, tmp->length);
|
|
}
|
|
else if (matchMailcapAttr(p, "edit", 4, &tmp)) {
|
|
mcap->edit = allocStr(tmp->ptr, tmp->length);
|
|
}
|
|
quoted = 0;
|
|
while (*p && (quoted || *p != ';')) {
|
|
if (quoted)
|
|
quoted = 0;
|
|
else if (*p == '\\')
|
|
quoted = 1;
|
|
p++;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
struct mailcap *
|
|
loadMailcap(char *filename)
|
|
{
|
|
FILE *f;
|
|
int i, n;
|
|
Str tmp;
|
|
struct mailcap *mcap;
|
|
|
|
f = fopen(expandName(filename), "r");
|
|
if (f == NULL)
|
|
return NULL;
|
|
i = 0;
|
|
while (tmp = Strfgets(f), tmp->length > 0) {
|
|
if (tmp->ptr[0] != '#')
|
|
i++;
|
|
}
|
|
fseek(f, 0, 0);
|
|
n = i;
|
|
mcap = New_N(struct mailcap, n + 1);
|
|
i = 0;
|
|
while (tmp = Strfgets(f), tmp->length > 0) {
|
|
if (tmp->ptr[0] == '#')
|
|
continue;
|
|
redo:
|
|
while (IS_SPACE(Strlastchar(tmp)))
|
|
Strshrink(tmp, 1);
|
|
if (Strlastchar(tmp) == '\\') {
|
|
/* continuation */
|
|
Strshrink(tmp, 1);
|
|
Strcat(tmp, Strfgets(f));
|
|
goto redo;
|
|
}
|
|
if (extractMailcapEntry(tmp->ptr, &mcap[i]))
|
|
i++;
|
|
}
|
|
bzero(&mcap[i], sizeof(struct mailcap));
|
|
fclose(f);
|
|
return mcap;
|
|
}
|
|
|
|
struct mailcap *
|
|
searchExtViewer(char *type)
|
|
{
|
|
struct mailcap *p;
|
|
int i;
|
|
|
|
if (mailcap_list == NULL)
|
|
goto no_user_mailcap;
|
|
|
|
for (i = 0; i < mailcap_list->nitem; i++) {
|
|
if ((p = searchMailcap(UserMailcap[i], type)) != NULL)
|
|
return p;
|
|
}
|
|
|
|
no_user_mailcap:
|
|
return searchMailcap(DefaultMailcap, type);
|
|
}
|
|
|
|
#define MC_NORMAL 0
|
|
#define MC_PREC 1
|
|
#define MC_PREC2 2
|
|
#define MC_QUOTED 3
|
|
|
|
#define MCF_SQUOTED (1 << 0)
|
|
#define MCF_DQUOTED (1 << 1)
|
|
|
|
Str
|
|
quote_mailcap(char *s, int flag)
|
|
{
|
|
Str d;
|
|
|
|
d = Strnew();
|
|
|
|
for (;; ++s)
|
|
switch (*s) {
|
|
case '\0':
|
|
goto end;
|
|
case '$':
|
|
case '`':
|
|
case '"':
|
|
case '\\':
|
|
if (!(flag & MCF_SQUOTED))
|
|
Strcat_char(d, '\\');
|
|
|
|
Strcat_char(d, *s);
|
|
break;
|
|
case '\'':
|
|
if (flag & MCF_SQUOTED) {
|
|
Strcat_charp(d, "'\\''");
|
|
break;
|
|
}
|
|
default:
|
|
if (!flag && !IS_ALNUM(*s))
|
|
Strcat_char(d, '\\');
|
|
case '_':
|
|
case '.':
|
|
case ':':
|
|
case '/':
|
|
Strcat_char(d, *s);
|
|
break;
|
|
}
|
|
end:
|
|
return d;
|
|
}
|
|
|
|
|
|
static Str
|
|
unquote_mailcap_loop(char *qstr, char *type, char *name, char *attr, int *stat, int flag0)
|
|
{
|
|
Str str, tmp, test, then;
|
|
char *p, *q;
|
|
int status = MC_NORMAL, prev_status = MC_NORMAL, sp = 0, flag;
|
|
|
|
if (stat)
|
|
*stat = 0;
|
|
|
|
if (qstr == NULL)
|
|
return NULL;
|
|
|
|
str = Strnew();
|
|
tmp = test = then = NULL;
|
|
|
|
for (flag = flag0, p = qstr; *p; p++) {
|
|
if (status == MC_QUOTED) {
|
|
if (prev_status == MC_PREC2)
|
|
Strcat_char(tmp, *p);
|
|
else
|
|
Strcat_char(str, *p);
|
|
status = prev_status;
|
|
continue;
|
|
}
|
|
else if (*p == '\\') {
|
|
prev_status = status;
|
|
status = MC_QUOTED;
|
|
continue;
|
|
}
|
|
switch (status) {
|
|
case MC_NORMAL:
|
|
if (*p == '%') {
|
|
status = MC_PREC;
|
|
}
|
|
else {
|
|
if (*p == '\'') {
|
|
if (!flag0 && flag & MCF_SQUOTED)
|
|
flag &= ~MCF_SQUOTED;
|
|
else if (!flag)
|
|
flag |= MCF_SQUOTED;
|
|
}
|
|
else if (*p == '"') {
|
|
if (!flag0 && flag & MCF_DQUOTED)
|
|
flag &= ~MCF_DQUOTED;
|
|
else if (!flag)
|
|
flag |= MCF_DQUOTED;
|
|
}
|
|
Strcat_char(str, *p);
|
|
}
|
|
break;
|
|
case MC_PREC:
|
|
if (IS_ALPHA(*p)) {
|
|
switch (*p) {
|
|
case 's':
|
|
if (name) {
|
|
Strcat_charp(str, quote_mailcap(name, flag)->ptr);
|
|
if (stat)
|
|
*stat |= MCSTAT_REPNAME;
|
|
}
|
|
break;
|
|
case 't':
|
|
if (type) {
|
|
Strcat_charp(str, quote_mailcap(type, flag)->ptr);
|
|
if (stat)
|
|
*stat |= MCSTAT_REPTYPE;
|
|
}
|
|
break;
|
|
}
|
|
status = MC_NORMAL;
|
|
}
|
|
else if (*p == '{') {
|
|
status = MC_PREC2;
|
|
test = then = NULL;
|
|
tmp = Strnew();
|
|
}
|
|
else if (*p == '%') {
|
|
Strcat_char(str, *p);
|
|
}
|
|
break;
|
|
case MC_PREC2:
|
|
if (sp > 0 || *p == '{') {
|
|
Strcat_char(tmp, *p);
|
|
|
|
switch (*p) {
|
|
case '{':
|
|
++sp;
|
|
break;
|
|
case '}':
|
|
--sp;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (*p == '}') {
|
|
char *q;
|
|
if (attr && (q = strcasestr(attr, tmp->ptr)) != NULL &&
|
|
(q == attr || IS_SPACE(*(q-1)) || *(q-1) == ';') &&
|
|
matchattr(q, tmp->ptr, tmp->length, &tmp)) {
|
|
Strcat_charp(str, quote_mailcap(tmp->ptr, flag)->ptr);
|
|
if (stat)
|
|
*stat |= MCSTAT_REPPARAM;
|
|
}
|
|
status = MC_NORMAL;
|
|
}
|
|
else {
|
|
Strcat_char(tmp, *p);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return str;
|
|
}
|
|
|
|
Str
|
|
unquote_mailcap(char *qstr, char *type, char *name, char *attr, int *stat)
|
|
{
|
|
return unquote_mailcap_loop(qstr, type, name, attr, stat, 0);
|
|
}
|