Logo Search packages:      
Sourcecode: af version File versions  Download package

mailcap.c

/* Mailcap.c - MIME configuration file reading and support for af.
   Copyright (C) 1997 - 2002 Malc Arnold.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */


#include <stdio.h>
#include <errno.h>
#include "af.h"
#include "atom.h"
#include "keyseq.h"
#include "functions.h"
#include "variable.h"
#include "mailcap.h"
#include "caplist.h"
#include "mime.h"
#include "complete.h"
#include STRING_HDR

/****************************************************************************/
/* RCS info */

#ifndef lint
static char *RcsId = "$Id: mailcap.c,v 1.3 2002/09/08 21:26:22 malc Exp $";
static char *MailcapId = MAILCAPID;
#endif /* ! lint */

/****************************************************************************/
/* External function declarations */

extern char *xmalloc(), *xrealloc(), *xstrdup();
extern char *vstrcat(), *getenv(), *get_home();
extern char *get_cline(), *atext(), *avalue();
extern char *c_contype(), *mc_contype();
extern char *typeonly(), *charset();
extern char *get_param(), *utos();
extern int strcasecmp(), strncasecmp();
extern int link(), unlink(), shellout();
extern int get_vval(), runcmd(), write_text();
extern int match_contype(), is_wildcard();
extern int compare_contypes(), close_pipe();
extern unsigned count_messages();
extern void free(), afree(), msgl();
extern void cmsg(), emsgl(), typeout();
extern void column(), free_messages();
extern ATOM *mtokenise(), *ttokenise();
extern ATOM *atoken(), *asearch();
extern ATOM *adiscard(), *adelete();
extern ATOM *acut(), *acopy();
extern MESSAGE *get_body_parts();
extern FILE *open_pipe();
extern CLIST *add_clist();

/* Local function declarations */

void remove_mime_tempfiles();
static char *mailcap_field();
static char *mailcap_value();
static char *get_mcap_cmd();
static char *expand_mcap_cmd();
static char *mcap_escape();
static char **mcap_tempfiles();
static char *temp_file_name();
static char *mcap_param();
static char *mcap_no_parts();
static char *mcap_body_parts();
static int parse_path_files();
static int parse_file();
static int parse_mailcap();
static int add_mailcap_field();
static int parse_mime_type();
static int parse_mime_charset();
static int cmd_uses_tempfile();
static int mailcap_test();
static void trim_mailcaps();
static void free_mailcap();
static void clean_up_tempfiles();
static void add_mime_tempfiles();
static MAILCAP *add_mailcap();
static MIME_TYPE *add_mime_type();
static MIME_SUFFIX *add_mime_suffix();
static MIME_CHARSET *add_mime_charset();

/****************************************************************************/
/* Import the system error number */

extern int errno;

/****************************************************************************/
/* The lists of mailcaps and mime.types */

static MAILCAP *mailcaps = NULL;
static MIME_TYPE *mime_types = NULL;
static MIME_SUFFIX *mime_suffixes = NULL;
static MIME_CHARSET *mime_charsets = NULL;

/****************************************************************************/
/* The list of outstanding temporary files */

static MIME_TEMPFILES *mime_tempfiles = NULL;

/****************************************************************************/
int read_mime_config()
{
      /* Read the mime configuration files and return status */

      static char *def_mailcaps = DEFAULT_MAILCAPS;
      char *mailcaps;
      int user_mailcap = FALSE;
      int errors = FALSE;

      /* Get the path to search for mailcap files */

      if ((mailcaps = getenv(MAILCAPS)) == NULL) {
            /* Use the user and default mailcaps */

            mailcaps = def_mailcaps;
            user_mailcap = TRUE;
      }

      /* Now read all of the mailcap files into a list */

      errors = parse_path_files((user_mailcap) ? MAILCAPFILE : NULL,
                          mailcaps, mtokenise, parse_mailcap);
      trim_mailcaps();

      /* Read all the available mime.types files into a list */

      errors += parse_path_files(MIMETYPESFILE, DEFAULT_MIME_TYPES,
                           ttokenise, parse_mime_type);

      /* And read all the available mime.charsets files into a list */

      errors += parse_path_files(MIMECHARSETSFILE, DEFAULT_MIME_CHARSETS,
                           ttokenise, parse_mime_charset);

      /* Now return the number of errors we found */

      return(errors);
}
/****************************************************************************/
char *content_type(filnam)
char *filnam;
{
      /* Return a content-type derived from any suffix on filnam */

      char *suffix;
      MIME_SUFFIX *s;

      /* Find any suffix on the filename */

      if ((suffix = strrchr(filnam, '.')) != NULL && strlen(suffix)) {
            /* Move to the start of the suffix proper */

            suffix++;

            /* Do we have a mime.types entry for the suffix? */

            for (s = mime_suffixes; s != NULL &&
                 *(s->suffix) <= *suffix; s = s->next) {
                  /* Is this the suffix we want? */

                  if (!strcasecmp(s->suffix, suffix)) {
                        /* Return the associated content-type */

                        return(s->type->contype);
                  }
            }
      }

      /* No content-type known for this file */

      return(NULL);
}
/****************************************************************************/
int known_charset(cset)
char *cset;
{
      /* Return TRUE if the character set is known */

      MIME_CHARSET *c;
      
      /* Loop over all the available mime charsets */

      for (c = mime_charsets; c != NULL; c = c->next) {
            /* Is this the charset we're looking for? */

            if (!strcasecmp(cset, c->charset)) {
                  /* This is a known charset */

                  return(TRUE);
            }
      }

      /* This is an unknown charset */

      return(FALSE);
}
/****************************************************************************/
MAILCAP *find_mailcap(ctype, message, what)
MESSAGE *message;
int what;
{
      /* Find the mailcap entry to run for what on message */

      MAILCAP *m;

      /* Find the appropriate entry in the list */

      for (m = mailcaps; m != NULL; m = m->next) {
            /* Have we found the correct mailcap? */

            if (match_contype(m->contype, ctype) &&
                get_mcap_cmd(m, what) != NULL &&
                (message == NULL || !mailcap_test(message, m))) {
                  /* This is the mailcap we need */

                  return(m);
            }
      }

      /* No matching mailcap for the message */

      return(NULL);
}
/****************************************************************************/
int mailcap_view(message, mcap, pager, fmt, hdrlist, parallel)
MESSAGE *message;
MAILCAP *mcap;
char *pager, *hdrlist;
int fmt, parallel;
{
      /* View the message via the mailcap command */

      char *cmd, *ecmd, **tfiles;
      int status;
      FILE *ifp = NULL, *ofp = NULL;

      /* Get the text of the command */

      if ((cmd = get_mcap_cmd(mcap, MCAP_VIEW)) == NULL) {
            /* We can always do nothing successfully */

            return(0);
      }

      /* Generate a temporary file for the text */

      if ((tfiles = mcap_tempfiles(mcap, message, MCAP_VIEW,
                             fmt, hdrlist)) == NULL) {
            /* Error creating the temporary file */

            return(-1);
      }

      /* Expand the text of the command */

      ecmd = expand_mcap_cmd(message, mcap, cmd, tfiles);

      /* Open the temporary file for reading? */

      if (!cmd_uses_tempfile(message, cmd) &&
          (ifp = fopen(tfiles[0], "r")) == NULL) {
            /* Error opening the file */

            emsgl("Can't open ", tfiles[0], ": ",
                  strerror(errno), NULL);
            clean_up_tempfiles(tfiles);
            return(-1);
      }

      /* Or direct standard input from /dev/null if required */

      if (cmd_uses_tempfile(message, cmd) && !(mcap->term)
          && (ifp = fopen(BITBUCKET, "r")) == NULL) {
            /* Error opening the bit bucket */

            emsgl("Error opening ", BITBUCKET, ": ",
                  strerror(errno), NULL);
            clean_up_tempfiles(tfiles);
            return(-1);
      }
            
      /* Handle connecting stdout to a pager */

      if (!(mcap->term) && mcap->page && pager != NULL
          && strcmp(pager, V_USE_TYPEOUT) &&
          (ofp = open_pipe(pager, "w", TRUE, NULL)) == NULL) {
            /* Error executing the pager */

            emsgl("Can't execute ", pager, ": ",
                  strerror(errno), NULL);
            (void) fclose(ifp);
            clean_up_tempfiles(tfiles);
            return(-1);
      }

      /* Or direct output to /dev/null if required */

      if (!(mcap->term) && !(mcap->page) &&
          (ofp = fopen(BITBUCKET, "r")) == NULL) {
            /* Error opening the bit bucket */

            emsgl("Error opening ", BITBUCKET, ": ",
                  strerror(errno), NULL);
            (void) fclose(ifp);
            clean_up_tempfiles(tfiles);
            return(-1);
      }

      /* Now execute the command in the right environment */

      status = (mcap->page &&
              (pager == NULL || !strcmp(pager, V_USE_TYPEOUT)))
            ? runcmd(ecmd, FALSE, ifp)
            : shellout(ecmd, FALSE, parallel && !mcap->term, ifp, ofp);

      /* Close any redirected input */

      if (!cmd_uses_tempfile(message, cmd) || !(mcap->term)) {
            (void) fclose(ifp);
      }

      /* Close any pipe to the pager */

      if (!(mcap->term) && mcap->page && pager != NULL
          && strcmp(pager, V_USE_TYPEOUT)) {
            (void) close_pipe(ofp, TRUE, get_vval(V_PAUSE));
      }

      /* Now clean up and return status */

      if (parallel && !mcap->term && !mcap->page) {
            /* Save the temp files for later use */

            add_mime_tempfiles(tfiles);
      } else {
            /* Remove the temp files */

            clean_up_tempfiles(tfiles);
      }
      return(status);
}
/****************************************************************************/
int mailcap_print(spool_fp, message, mcap, fmt, hdrlist)
FILE *spool_fp;
MESSAGE *message;
MAILCAP *mcap;
int fmt;
char *hdrlist;
{
      /* Print the message via the mailcap command */

      char *cmd, *ecmd, **tfiles;
      int status;
      FILE *ifp = NULL;

      /* Get the text of the command */

      if ((cmd = get_mcap_cmd(mcap, MCAP_PRINT)) == NULL) {
            /* We can always do nothing successfully */

            return(0);
      }

      /* Generate a temporary file if required */

      if ((tfiles = mcap_tempfiles(mcap, message, MCAP_PRINT,
                             fmt, hdrlist)) == NULL) {
            /* Error creating the temporary file */

            return(1);
      }

      /* Expand the text of the command */

      ecmd = expand_mcap_cmd(message, mcap, cmd, tfiles);

      /* Open the temporary file for reading? */

      if (!cmd_uses_tempfile(message, cmd) &&
          (ifp = fopen(tfiles[0], "r")) == NULL) {
            /* Error opening the file */

            emsgl("Can't open ", tfiles[0], ": ",
                  strerror(errno), NULL);
            clean_up_tempfiles(tfiles);
            return(-1);
      }

      /* Or direct standard input from /dev/null if required */

      if (cmd_uses_tempfile(message, cmd) &&
          (ifp = fopen(BITBUCKET, "r")) == NULL) {
            /* Error opening the bit bucket */

            emsgl("Error opening ", BITBUCKET, ": ",
                  strerror(errno), NULL);
            clean_up_tempfiles(tfiles);
            return(-1);
      }

      /* Now execute the command in the right environment */

      status = shellout(ecmd, FALSE, FALSE, ifp, spool_fp);

      /* Close the redirected input */

      (void) fclose(ifp);

      /* Now clean up and return status */

      clean_up_tempfiles(tfiles);
      return(status);
}
/****************************************************************************/
int mailcap_edit(message, mcap, etfile, fmt)
MESSAGE *message;
MAILCAP *mcap;
char *etfile;
int fmt;
{
      /* Edit the body of the message via the mailcap command */

      char *cmd, *ecmd, **tfiles;
      int status;
      FILE *ifp = NULL, *ofp = stdout;

      /* Get the text of the command */

      if ((cmd = get_mcap_cmd(mcap, MCAP_EDIT)) == NULL) {
            /* We can always do nothing successfully */

            return(0);
      }

      /* Generate a temporary file if required */

      if ((tfiles = mcap_tempfiles(mcap, message, MCAP_EDIT,
                             fmt, NULL)) == NULL) {
            /* Error creating the temporary file */

            return(1);
      }

      /* Expand the text of the command */

      ecmd = expand_mcap_cmd(message, mcap, cmd, tfiles);

      /* Open the temporary file for reading? */

      if (!cmd_uses_tempfile(message, cmd) &&
          (ifp = fopen(tfiles[0], "r")) == NULL) {
            /* Error opening the file */

            emsgl("Can't open ", tfiles[0], ": ",
                  strerror(errno), NULL);
            clean_up_tempfiles(tfiles);
            return(-1);
      }

      /* Handle connecting stdout to a file */

      if (!cmd_uses_tempfile(message, cmd) &&
          (ofp = fopen(etfile, "w")) == NULL) {
            /* Error opening the file */

            emsgl("Can't open ", etfile, ": ", strerror(errno), NULL);
            (void) fclose(ifp);
            clean_up_tempfiles(tfiles);
            return(-1);
      }

      /* Now execute the command in the right environment */

      status = shellout(ecmd, FALSE, FALSE, ifp, ofp);

      /* Close any redirected input and output */

      if (!cmd_uses_tempfile(message, cmd)) {
            (void) fclose(ifp);
            (void) fclose(ofp);
      }

      /* Move the tempfile to the edit file */

      (void) unlink(etfile);
      if (link(tfiles[0], etfile) < 0) {
            /* Error linking file */

            emsgl("Error linking ", tfiles[0], " to ",
                  etfile, ": ", strerror(errno), NULL);
            clean_up_tempfiles(tfiles);
            return(-1);
      }

      /* Now clean up and return status */

      clean_up_tempfiles(tfiles);
      return(status);
}
/****************************************************************************/
int mailcap_compose(mcap, etfile, fmt, typed)
MAILCAP *mcap;
char *etfile;
int fmt, typed;
{
      /* Compose the body of a message via the mailcap command */

      char *cmd, *ecmd, **tfiles;
      int status, operation;
      FILE *ifp = NULL, *ofp = NULL;

      /* Which mailcap operation are we using? */

      operation = (typed) ? MCAP_TYPED : MCAP_COMPOSE;

      /* Get the text of the compose command */

      if ((cmd = get_mcap_cmd(mcap, operation)) == NULL) {
            /* We can always do nothing successfully */

            return(0);
      }

      /* Generate a temporary file if required */

      if ((tfiles = mcap_tempfiles(mcap, NULL, operation,
                             fmt, NULL)) == NULL) {
            /* Error creating the temporary file */

            return(1);
      }

      /* Expand the text of the command */

      ecmd = expand_mcap_cmd(NULL, mcap, cmd, tfiles);

      /* Direct standard input from /dev/null if required */

      if (cmd_uses_tempfile(NULL, cmd) && !mcap->term
          && (ifp = fopen(BITBUCKET, "r")) == NULL) {
            /* Error opening the bit bucket */

            emsgl("Error opening ", BITBUCKET, ": ",
                  strerror(errno), NULL);
            clean_up_tempfiles(tfiles);
            return(-1);
      }

      /* Handle connecting stdout to a file */

      if (!cmd_uses_tempfile(NULL, cmd) &&
          (ofp = fopen(etfile, "w")) == NULL) {
            /* Error opening the file */

            emsgl("Can't open ", etfile, ": ", strerror(errno), NULL);
            (void) fclose(ifp);
            clean_up_tempfiles(tfiles);
            return(-1);
      }

      /* Now execute the command in the right environment */

      status = shellout(ecmd, FALSE, FALSE, ifp, ofp);

      /* Close any redirected input */

      if (!cmd_uses_tempfile(NULL, cmd) || !(mcap->term)) {
            (void) fclose(ifp);
      }

      /* And close any output file */

      if (!cmd_uses_tempfile(NULL, cmd)) {
            (void) fclose(ofp);
      }

      /* Move the tempfile to the edit file */

      if (cmd_uses_tempfile(NULL, cmd)) {
            /* Replace the edit file with the new file */

            (void) unlink(etfile);
            if (link(tfiles[0], etfile) < 0) {
                  /* Error linking the file */

                  emsgl("Error linking ", tfiles[0], " to ",
                        etfile, ": ", strerror(errno), NULL);
                  clean_up_tempfiles(tfiles);
                  return(-1);
            }
      }

      /* Now clean up and return status */

      clean_up_tempfiles(tfiles);
      return(status);
}
/****************************************************************************/
static int parse_path_files(user_file, path, tokeniser, parser)
char *user_file, *path;
ATOM *(*tokeniser)();
int (*parser)();
{
      /* Parse all the files listed in user_file or path with parser */

      char *filename, *start, *colon;
      int errors = 0;
      FILE *fp;

      /* Read the user's personal file, if any */

      if (user_file != NULL) {
            /* Generate the full name of the user file */

            filename = vstrcat(get_home(NULL), "/", user_file, NULL);

            /* Now see if we can open the file */

            if ((fp = fopen(filename, "r")) != NULL) {
                  /* Let the user know what's happening */

                  msgl("Reading ", filename, "... ", NULL);

                  /* Parse the file and then close it */

                  errors += parse_file(fp, tokeniser, parser);
                  (void) fclose(fp);

                  /* And confirm the read */

                  cmsg("Done");
            }

            /* And free the file name */

            free(filename);
      }

      /* Now read any global files */

      start = path;
      while (start != NULL && *start != '\0') {
            /* Get the end of this file name */

            if ((colon = strchr(start, ':')) == NULL) {
                  /* Last element in the list */

                  colon = start + strlen(start);
            }

            /* Copy the path element out of the list */

            filename = xmalloc(colon - start + 1);
            (void) strncpy(filename, start, colon - start);
            filename[colon - start] = '\0';

            /* Open the file and check status */
            if ((fp = fopen(filename, "r")) != NULL) {
                  /* Let the user know what's happening */

                  msgl("Reading ", filename, "... ", NULL);

                  /* Parse the file and then close it */

                  errors += parse_file(fp, tokeniser, parser);
                  (void) fclose(fp);

                  /* And confirm the read */

                  cmsg("Done");
            }

            /* Free the file name and move on */

            free(filename);
            start = (*colon != '\0') ? colon + 1 : NULL;
      }

      /* Return the number of errors found */

      return(errors);
}
/****************************************************************************/
static int parse_file(fp, tokeniser, parser)
FILE *fp;
ATOM *(*tokeniser)();
int (*parser)();
{
      /* Read non-comment lines from fp, tokenise, and parse them */

      char *line;
      int errors = 0;
      ATOM *alist, *atom;

      /* Loop over the lines from the file */

      while ((line = get_cline(fp)) != NULL) {
            /* Tokenise the line */

            alist = tokeniser(line);

            /* Now check if the line is blank */

            if (alist != NULL && (atom = atoken(alist)) != NULL) {
                  /* Pass the line to the parser */

                  errors += (parser(atom)) ? 0 : 1;
            }

            /* Free the atom list and line */

            afree(alist);
            free(line);
      }
      
      /* And return */

      return(errors);
}
/****************************************************************************/
static int parse_mailcap(atom)
ATOM *atom;
{
      char *ctype, *view, *text;
      char *field, *value;
      MAILCAP *node;
      ATOM *alist;

      /* Generate a copy of the atom list */

      alist = acopy(atom);

      /* Find the content-type and subtype */

      if ((ctype = mc_contype(mailcap_value(&alist))) == NULL
          || (view = mailcap_value(&alist)) == NULL) {
            /* Error in the mailcap; output a message */

            text = atext(NULL, alist, AC_TRIM);
            emsgl("Invalid mailcap entry: ", text, NULL);

            /* Then clean up and fail */

            free(text);
            afree(alist);
            if (ctype != NULL) {
                  free(ctype);
            }
            return(FALSE);
      }

      /* Add a new node to the list */

      node = add_mailcap(ctype, view);

      /* Now read the other fields in the entry */

      while (alist != NULL) {
            /* Is the next field in the entry valid? */

            if ((field = mailcap_field(&alist)) != NULL) {
                  /* Get the value and add the field */

                  value = mailcap_value(&alist);
                  add_mailcap_field(node, field, value);
            }
      }

      /* Now return success */

      return(TRUE);
}
/****************************************************************************/
static char *mailcap_field(alist)
ATOM **alist;
{
      /*
       * Strip the next field name from a mailcap entry and return
       * it in a static buffer.  The name is checked for validity,
       * and if it isn't then everything up to the next semicolon
       * is discarded.   As a side-effect, update alist to point
       * to the first atom after the field name.
       */

      static char *buf = NULL;
      ATOM *field, *next;
      ATOM *equals, *semi;
      
      /* Find the start of the field */

      if ((field = atoken(*alist)) == NULL) {
            /* No more fields in the entry */

            afree(*alist);
            *alist = NULL;
            return(NULL);
      }

      /* Find the possible field terminators */

      next = atoken(field->next);
      equals = asearch(next, AT_EQUALS);
      semi = asearch(next, AT_SEMI);

      /* Check the field name is valid */

      if (field->type != AT_ATOM || next != equals && next != semi) {
            /* The field name is invalid, skip the entry */

            *alist = adelete(*alist, *alist, (semi != NULL)
                         ? semi->next : NULL);
            return(FALSE);
      }

      /* Strip out the field name */

      *alist = adelete(*alist, *alist, field);
      *alist = acut(*alist, field, next);

      /* Discard any equals in the list */

      if (next != NULL && next == equals) {
            *alist = adiscard(*alist, next);
      }

      /* Set the return buffer */

      if (buf != NULL) {
            free(buf);
      }
      buf = atext(NULL, field, AC_TRIM);
      afree(field);

      /* Now return the field name */

      return(buf);
}
/****************************************************************************/
static char *mailcap_value(alist)
ATOM **alist;
{
      /*
       * Strip the next field value from a mailcap entry and return
       * it in a static buffer, or NULL of there is no value.  As a
       * side-effect, update alist to point to the first atom after
       * the field value.
       */

      static char *buf = NULL;
      ATOM *value, *semi;
      
      /* Find the start of the value */

      if ((value = atoken(*alist)) == NULL) {
            /* No more fields in the entry */

            afree(*alist);
            *alist = NULL;
            return(NULL);
      }

      /* Find the end of the value */

      if ((semi = asearch(value, AT_SEMI)) == value) {
            /* Blank value for this entry */

            *alist = adiscard(*alist, semi);
            return(NULL);
      }

      /* Strip out the value */

      *alist = adelete(*alist, *alist, value);
      *alist = acut(*alist, value, semi);

      /* Discard any semicolon */

      if (semi != NULL) {
            *alist = adiscard(*alist, semi);
      }

      /* Set the return buffer */

      if (buf != NULL) {
            free(buf);
      }
      buf = atext(NULL, value, AC_TRIM);
      afree(value);

      /* Now return the value */

      return(buf);
}
/****************************************************************************/
static int add_mailcap_field(node, field, value)
MAILCAP *node;
char *field, *value;
{
      /* Handle a MIME field by updating node */

      FIELD *f;

      /* Search for the field in the list of relevant fields */

      for (f = mailcap_fields; f->name != NULL; f++) {
            /* Is this the field we're looking for? */

            if (!strcasecmp(field, f->name)) {
                  /* This is the field; handle it */

                  return(f->handler(node, value));
            }
      }
      
      /* Nothing to do for this field */

      return(TRUE);
}
/****************************************************************************/
static int mcap_test(node, value)
MAILCAP *node;
char *value;
{
      /* Set the test field of node to value */

      if (node->test == NULL && value != NULL) {
            node->test = xstrdup(value);
      }
      return(value != NULL);
}
/****************************************************************************/
static int mcap_print(node, value)
MAILCAP *node;
char *value;
{
      /* Set the print field of node to value */

      if (node->print == NULL && value != NULL) {
            node->print = xstrdup(value);
      }
      return(value != NULL);
}
/****************************************************************************/
static int mcap_edit(node, value)
MAILCAP *node;
char *value;
{
      /* Set the edit field of node to value */

      if (node->edit == NULL && value != NULL) {
            node->edit = xstrdup(value);
      }
      return(value != NULL);
}
/****************************************************************************/
static int mcap_compose(node, value)
MAILCAP *node;
char *value;
{
      /* Set the compose field of node to value */

      if (node->compose == NULL && value != NULL) {
            node->compose = xstrdup(value);
      }
      return(value != NULL);
}
/****************************************************************************/
static int mcap_typed(node, value)
MAILCAP *node;
char *value;
{
      /* Set the composetyped field of node to value */

      if (node->typed == NULL && value != NULL) {
            node->typed = xstrdup(value);
      }
      return(value != NULL);
}
/****************************************************************************/
static int mcap_desc(node, value)
MAILCAP *node;
char *value;
{
      /* Set the description field of node to value */

      if (node->desc == NULL && value != NULL) {
            node->desc = xstrdup(value);
      }
      return(value != NULL);
}
/****************************************************************************/
static int mcap_file(node, value)
MAILCAP *node;
char *value;
{
      /* Set the nametemplate field of node to value */

      if (node->file == NULL && value != NULL) {
            node->file = xstrdup(value);
      }
      return(value != NULL);
}
/****************************************************************************/
static int mcap_term(node, value)
MAILCAP *node;
char *value;
{
      /* Set the needsterminal field of node */

      node->term = TRUE;
      return(value == NULL);
}
/****************************************************************************/
static int mcap_page(node, value)
MAILCAP *node;
char *value;
{
      /* Set the page field of node */

      node->page = TRUE;
      return(value == NULL);
}
/****************************************************************************/
static int mcap_textual(node, value)
MAILCAP *node;
char *value;
{
      /* Set the textual field of node to value */

      if (value != NULL) {
            node->textual = (strlen(value) > 0) ? TRUE : FALSE;
      }
      return(value != NULL);
}
/****************************************************************************/
static void trim_mailcaps()
{
      /* Remove any redundant mailcap entries */

      MAILCAP *m, *next;

      /* Loop over the available mailcap entries */

      m = mailcaps;
      while (m != NULL && m->next != NULL) {
            /* Get the next entry in the list */

            next = m->next;

            /* Can we delete the next mailcap? */

            if (m->test == NULL &&
                (!is_wildcard(m->contype) || is_wildcard(next->contype))
                && match_contype(m->contype, next->contype)
                && (m->view != NULL || next->view == NULL)
                && (m->print != NULL || next->print == NULL)
                && (m->edit != NULL || next->edit == NULL)
                && (m->compose != NULL || next->compose == NULL)
                && (m->typed != NULL || next->typed == NULL)) {
                  /* Copy the description and file if required */

                  m->desc = (m->desc == NULL && next->desc != NULL)
                        ? xstrdup(next->desc) : m->desc;
                  m->file = (m->file == NULL && next->file != NULL)
                        ? xstrdup(next->file) : m->file;

                  /* And remove the next entry */

                  m->next = next->next;
                  next->next = NULL;
                  free_mailcap(next);
            } else {
                  /* Try the next entry in the list */

                  m = next;
            }
      }
      return;
}
/****************************************************************************/
static void free_mailcap(mcap)
MAILCAP *mcap;
{
      /* Free the single mailcap entry mcap */

      free(mcap->contype);
      free(mcap->view);
      if (mcap->test != NULL) {
            free(mcap->test);
      }
      if (mcap->print != NULL) {
            free(mcap->print);
      }
      if (mcap->edit != NULL) {
            free(mcap->edit);
      }
      if (mcap->compose != NULL) {
            free(mcap->compose);
      }
      if (mcap->typed != NULL) {
            free(mcap->typed);
      }
      if (mcap->desc != NULL) {
            free(mcap->desc);
      }
      if (mcap->file != NULL) {
            free(mcap->file);
      }
      free(mcap);

      return;
}
/****************************************************************************/
static int parse_mime_type(atom)
ATOM *atom;
{
      /* Canonicalise, check, and possibly add a mime.types entry */

      char *ctype;
      MIME_TYPE *node;
      MIME_SUFFIX *suffix;

      /* Canonicalise the content-type */

      if ((ctype = c_contype(atom->text)) == NULL) {
            /* Invalid entry in mime.types file */

            emsgl("Invalid content-type ", atom->text,
                  " listed in mime.types", NULL);
            return(FALSE);
      }

      /* Add this content-type to the list */

      node = add_mime_type(ctype);
      free(ctype);

      /* Now add the file suffixes */

      while ((atom = atoken(atom->next)) != NULL) {
            /* Add this suffix to the type */

            suffix = add_mime_suffix(node, atom->text);
      }

      /* Return success */

      return(TRUE);
}
/****************************************************************************/
static int parse_mime_charset(atom)
ATOM *atom;
{
      /* Canonicalise, check, and possibly add a mime.charsets entry */

      char *cset;
      MIME_CHARSET *node;

      /* Canonicalise the character set */

      if ((cset = charset(atom->text)) == NULL) {
            /* Invalid entry in mime.charset file */

            emsgl("Invalid character set ", cset,
                  " listed in mime.charsets", NULL);
            return(FALSE);
      }

      /* Add this content-type to the list */

      node = add_mime_charset(cset);
      free(cset);

      /* Return success */

      return(TRUE);
}
/****************************************************************************/
static MAILCAP *add_mailcap(ctype, view)
char *ctype, *view;
{
      /* Add a new mailcap entry for ctype */

      MAILCAP *node, *m;
      MAILCAP *last = NULL;

      /* Find the appropriate entry in the list */

      for (m = mailcaps; m != NULL; m = m->next) {
            /* Have we found the node or it's insert point? */

            if (compare_contypes(ctype, m->contype) < 0) {
                  break;
            }

            /* Set the last node found */

            last = m;
      }

      /* Create a new node */

      node = (MAILCAP *) xmalloc(sizeof(MAILCAP));
      node->contype = xstrdup(ctype);
      node->view = xstrdup(view);
      node->test = node->print = node->edit = NULL;
      node->compose = node->typed = NULL;
      node->file = node->desc = NULL;
      node->term = node->page = node->textual = FALSE;

      /* Add the new node and return it */

      if (last != NULL) {
            node->next = last->next;
            last->next = node;
      } else {
            node->next = mailcaps;
            mailcaps = node;
      }           
      return(node);
}
/****************************************************************************/
static MIME_TYPE *add_mime_type(ctype)
char *ctype;
{
      /* Add a new mime_type entry for ctype or return an existing one */

      int compare;
      MIME_TYPE *node, *t;
      MIME_TYPE *last = NULL;

      /* Find the appropriate entry in the list */

      for (t = mime_types; t != NULL; t = t->next) {
            /* Have we found the node or it's insert point? */

            if (!(compare = strcasecmp(ctype, t->contype))) {
                  return(t);
            } else if (compare < 0) {
                  break;
            }

            /* Set the last node found */

            last = t;
      }

      /* Create a new node */

      node = (MIME_TYPE *) xmalloc(sizeof(MIME_TYPE));
      node->contype = xstrdup(ctype);

      /* Add the new node and return it */

      if (last != NULL) {
            node->next = last->next;
            last->next = node;
      } else {
            node->next = mime_types;
            mime_types = node;
      }           
      return(node);
}
/****************************************************************************/
static MIME_SUFFIX *add_mime_suffix(mimetype, suffix)
MIME_TYPE *mimetype;
char *suffix;
{
      /* Add a new mime filename suffix or return an existing one */

      int compare;
      MIME_SUFFIX *node, *s;
      MIME_SUFFIX *last = NULL;

      /* Find the appropriate entry in the list */

      for (s = mime_suffixes; s != NULL; s = s->next) {
            /* Have we found the node or it's insert point? */

            if (!(compare = strcasecmp(suffix, s->suffix))) {
                  return(s);
            } else if (compare < 0) {
                  break;
            }

            /* Set the last node found */

            last = s;
      }

      /* Create a new node */

      node = (MIME_SUFFIX *) xmalloc(sizeof(MIME_SUFFIX));
      node->suffix = xstrdup(suffix);
      node->type = mimetype;

      /* Add the new node and return it */

      if (last != NULL) {
            node->next = last->next;
            last->next = node;
      } else {
            node->next = mime_suffixes;
            mime_suffixes = node;
      }
      return(node);
}
/****************************************************************************/
static MIME_CHARSET *add_mime_charset(cset)
char *cset;
{
      /* Add a new mime_charset entry or return an existing one */

      int compare;
      MIME_CHARSET *node, *c;
      MIME_CHARSET *last = NULL;

      /* Find the appropriate entry in the list */

      for (c = mime_charsets; c != NULL; c = c->next) {
            /* Have we found the node or it's insert point? */

            if (!(compare = strcasecmp(cset, c->charset))) {
                  return(c);
            } else if (compare < 0) {
                  break;
            }

            /* Set the last node found */

            last = c;
      }

      /* Create a new node */

      node = (MIME_CHARSET *) xmalloc(sizeof(MIME_CHARSET));
      node->charset = xstrdup(cset);

      /* Add the new node and return it */

      if (last != NULL) {
            node->next = last->next;
            last->next = node;
      } else {
            node->next = mime_charsets;
            mime_charsets = node;
      }           
      return(node);
}
/****************************************************************************/
static int mailcap_test(message, mcap)
MESSAGE *message;
MAILCAP *mcap;
{
      /* Format and run the test command from a mailcap entry */

      char *cmd, *ecmd;
      char **tfiles = NULL;
      int status;
      FILE *ifp, *ofp;

      /* Get the text of the command */

      if ((cmd = get_mcap_cmd(mcap, MCAP_TEST)) == NULL) {
            /* We can always do nothing successfully */

            return(0);
      }

      /* Generate a temporary file name if required */

      if (cmd_uses_tempfile(NULL, cmd) &&
          (tfiles = mcap_tempfiles(mcap, message, MCAP_TEST,
                             0, NULL)) == NULL) {
            /* Error creating the temporary file name */

            return(1);
      }

      /* Expand the text of the command */

      ecmd = expand_mcap_cmd(message, mcap, cmd, tfiles);

      /* Set up the null stdin and stdout for the test */

      if ((ifp = fopen(BITBUCKET, "r")) == NULL ||
          (ofp = fopen(BITBUCKET, "w")) == NULL) {
            /* Error opening the bit bucket */

            emsgl("Error opening ", BITBUCKET,
                  ": ", strerror(errno), NULL);
            if (ifp != NULL) {
                  (void) fclose(ifp);
            }
            clean_up_tempfiles(tfiles);
            return(-1);
      }

      /* Now execute the test command */

      status = shellout(ecmd, FALSE, FALSE, ifp, ofp);

      /* Clean up and return status */

      clean_up_tempfiles(tfiles);
      (void) fclose(ifp);
      (void) fclose(ofp);

      return(status);
}
/****************************************************************************/
static char *get_mcap_cmd(mcap, what)
MAILCAP *mcap;
int what;
{
      /* Return the command relevant to what from the mailcap entry */

      switch (what) {
      case MCAP_TEST:
            return(mcap->test);
      case MCAP_VIEW:
            return(mcap->view);
      case MCAP_PRINT:
            return(mcap->print);
      case MCAP_EDIT:
            return(mcap->edit);
      case MCAP_COMPOSE:
            return(mcap->compose);
      case MCAP_TYPED:
            return(mcap->typed);
      }

      /* We shouldn't ever reach here */

      return(NULL);
}
/****************************************************************************/
static int cmd_uses_tempfile(message, cmd)
MESSAGE *message;
char *cmd;
{
      /*
       * Check if the command uses one or more temporary files.
       * Returns 0 if the command does not include a temporary
       * file, or one or both of the flags TFILE_MESSAGE and
       * TFILE_BODY_PARTS if the %s and/or %F escapes are used
       * in the command's text.  The %F escape is only valid if
       * the message has body parts, and is ignored otherwise.
       */

      char *c;
      int escaped = FALSE;
      int status = 0;
      MESSAGE *body_parts;

      /* Loop over the command looking for escapes */

      for (c = cmd; *c != '\0'; c++) {
            /* Is this an unescaped temporary file escape? */

            if (!escaped && *c == '%' && *(c + 1) == 'F'
                && !(status & TFILE_BODY_PARTS) && message != NULL
                && (body_parts = get_body_parts(message)) != NULL) {
                  /* This is a valid body part temporary file escape */

                  status = (status | TFILE_BODY_PARTS);
                  free_messages(body_parts);
            } else if (!escaped && *c == '%' && *(c + 1) == 's'
                     && !(status & TFILE_MESSAGE)) {
                  /* This is a message temporary file escape */

                  status = (status | TFILE_MESSAGE);
            }

            /* Update the escaped flag */

            escaped = (!escaped && *c == '\\');
      }

      /* Return whether a temporary file was used */

      return(status);
}
/****************************************************************************/
static char *expand_mcap_cmd(message, mcap, cmd, tfiles)
MESSAGE *message;
MAILCAP *mcap;
char *cmd, **tfiles;
{
      /* Return the expanded text of cmd in a static buffer */

      static char *buf = NULL;
      char *p, *q, *escape;
      int escaped = FALSE;
      size_t len = 0;

      /* Calculate the expanded length of the command */

      p = cmd;
      while (*p != '\0') {
            /* Handle backslashes and percent escapes */

            if (!escaped && *p == '%') {
                  /* Add the escape to the length */

                  escape = mcap_escape(message, mcap, cmd, tfiles, &p);
                  len += strlen(escape);
            } else {
                  /* Update escaped and the length */

                  escaped = (!escaped && *p == '\\');
                  len++;
                  p++;
            }
      }

      /* (Re)allocate space for the command */

      q = buf = (buf == NULL) ? xmalloc(len + 1) : xrealloc(buf, len + 1);

      /* Now generate the expanded command */

      p = cmd;
      while (*p != '\0') {
            /* Check if this is an escape sequence */

            if (!escaped && *p == '%') {
                  /* Add the escape to the command */

                  escape = mcap_escape(message, mcap, cmd, tfiles, &p);
                  (void) strcpy(q, escape);
                  q += strlen(q);
            } else {
                  /* Update escaped and copy the character */

                  escaped = (!escaped && *p == '\\');
                  *q++ = *p++;
            }
      }
      *q = '\0';

      /* Now return the buffer */

      return(buf);
}
/****************************************************************************/
static char *mcap_escape(message, mcap, cmd, tfiles, pos)
MESSAGE *message;
MAILCAP *mcap;
char *cmd, **tfiles, **pos;
{
      /* Return the expansion of a mailcap escape */

      static char *buf = NULL;

      /* Free any old return value */

      if (buf != NULL) {
            free(buf);
      }

      /* Now handle the valid escapes */

      switch (*(*pos + 1)) {
      case 's':
            buf = xstrdup(tfiles[0]);
            *pos += 2;
            return(buf);
      case 't':
            buf = typeonly(message->contype);
            *pos += 2;
            return(buf);
      case '{':
            buf = mcap_param(message, pos);
            return(buf);

      case 'n':
            buf = mcap_no_parts(message);
            *pos += 2;
            return(buf);
      case 'F':
            buf = mcap_body_parts(message, cmd, tfiles);
            *pos += 2;
            return(buf);
      }

      /* Invalid escape; ignore it */

      buf = xstrdup("");
      *pos += 2;

      /* And return the expansion */

      return(buf);
}
/****************************************************************************/
static char **mcap_tempfiles(mcap, message, what, fmt, hdrlist)
MAILCAP *mcap;
MESSAGE *message;
int what, fmt;
char *hdrlist;
{
      /*
       * Return an array of the temporary files required for message.
       * If a %s escape was used in the mailcap command, then the
       * first entry in the array is the file for the %s escape.
       * If a %F escape was used, then the files for the %F escape
       * are included after that for the %s escape, if any.  The
       * array is NULL terminated.
       */

      char **tfiles = NULL;
      char *cmd;
      int what_tfiles;
      int no_tfiles, t = 0;
      MAILCAP *body_mcap;
      MESSAGE *body_parts, *b;

      /* Extract the command from the mailcap entry */

      cmd = get_mcap_cmd(mcap, what);

      /* First check what types of temporary files are needed */

      what_tfiles = cmd_uses_tempfile(message, cmd);

      /* Extract the body parts of the message if required */

      body_parts =  (what_tfiles & TFILE_BODY_PARTS)
            ? get_body_parts(message) : NULL;

      /* Count the number of temporary files required */

      no_tfiles = (body_parts != NULL) ? (what_tfiles & TFILE_MESSAGE)
            ? count_messages(body_parts, TRUE)  + 1
            : count_messages(body_parts, TRUE) : 1;

      /* Allocate the array for the temporary files */

      tfiles = (char **) xmalloc(sizeof(char *) * (no_tfiles + 1));

      /* Create the message temporary file if required */

      if ((what_tfiles & TFILE_MESSAGE) || body_parts == NULL) {
            /* Add the single temporary file to the array */

            if ((tfiles[t++] = temp_file_name(mcap, message, fmt,
                                      hdrlist)) == NULL) {
                  /* Error creating the temporary file */

                  clean_up_tempfiles(tfiles);
                  return(NULL);
            }
      }

      /* Create the body-part temporary files if required */

      for (b = body_parts; b != NULL && b->text != NULL; b = b->next) {
            /* Find the mailcap entry for the body part */

            body_mcap = (!b->viewable && b->decodable)
                  ? find_mailcap(b->contype, b, what) : NULL;

            /* And add the temporary file to the array */

            if ((tfiles[t++] = temp_file_name(body_mcap, b, fmt,
                                      hdrlist)) == NULL) {
                  /* Error creating the temporary file */

                  clean_up_tempfiles(tfiles);
                  return(NULL);
            }
      }

      /* Terminate and return the array of temporary files */

      tfiles[t] = NULL;
      return(tfiles);
}
/****************************************************************************/
static char *temp_file_name(mcap, body_part, fmt, hdrlist)
MAILCAP *mcap;
MESSAGE *body_part;
int fmt;
char *hdrlist;
{
      /* Return a temporary file name as defined by any template */

      char prefix[TPFX_LEN + 1];
      char *suffix = NULL;
      char *template, *pct;
      char *slash, *tfile;
      int status;
      size_t len;
      FILE *fp;

      /* Default the prefix to af's usual one */

      (void) strcpy(prefix, TFILEPFX);

      /* Extract the mailcap's file name template without path */

      slash = (mcap != NULL && mcap->file != NULL)
            ? strrchr(mcap->file, '/') : NULL;
      template = (mcap != NULL && mcap->file != NULL)
            ? (slash != NULL) ? slash + 1 : mcap->file : NULL;

      /* Find the place-holder in the name */

      pct = (template != NULL) ? strchr(template, '%') : NULL;

      /* Find the file prefix and suffix */

      while (pct != NULL) {
            /* Have we found the %s in the template? */

            if (*(pct + 1) == 's') {
                  /* Generate the prefix and suffix */

                  if ((len = (pct - template > TPFX_LEN)
                       ? TPFX_LEN : pct - template) > 0) {
                        (void) strncpy(prefix, template, len);
                        prefix[len] = '\0';
                  }
                  suffix = pct + 2;
                  break;
            }

            /* Try the next percent character */

            pct = strchr(pct + 1, '%');
      }

      /* Now generate a temporary file name */

      if ((tfile = tempnam(TFILEDIR, prefix)) == NULL) {
            /* Error creating the temporary file */

            emsgl("Can't create temporary file: ",
                  strerror(errno), NULL);
            return(NULL);
      }

      /* Append any suffix to the temporary file */

      if (suffix != NULL) {
            tfile = xrealloc(tfile, strlen(tfile) + strlen(suffix) + 1);
            (void) strcat(tfile, suffix);
      }

      /* Now create the temporary file */

      if ((fp = fopen(tfile, "w")) == NULL) {
            /* Error creating the temporary file */

            emsgl("Can't open ", tfile, ": ",
                  strerror(errno), NULL);
            (void) unlink(tfile);
            free(tfile);
            return(NULL);
      }

      /* Now write the text into the temporary file if required */

      if (body_part != NULL &&
          (status = write_text(fp, body_part, fmt, hdrlist))) {
            /* Error writing the text to the file */

            emsgl("Error writing temporary file: ",
                  strerror(status), NULL);
            (void) fclose(fp);
            (void) unlink(tfile);
            free(tfile);
            return(NULL);
      }

      /* Close and return the temporary file */

      (void) fclose(fp);
      return(tfile);
}
/****************************************************************************/
static char *mcap_param(message, pos)
MESSAGE *message;
char **pos;
{
      /*
       * Extract the parameter name from pos and return its value
       * as an allocated string.  As a side effect, update pos to
       * point to the first character after the parameter name.
       */

      char *param, *value, *end;

      /* Get the end of the parameter name */

      if ((end = strchr(*pos + 2, '}')) == NULL) {
            /* Invalid escape; ignore it */

            *pos += 2;
            return(xstrdup(""));
      }

      /* Copy the parameter name */

      param = xmalloc(end - *pos - 1);
      (void) strncpy(param, *pos + 2, end - *pos - 2);
      param[end - *pos - 2] = '\0';

      /* Now get the parameter's value */

      value = get_param(message->contype, param);
      free(param);

      /* Move on the the end of the escape */

      *pos = (end + 1);

      /* And return the value or the null string */

      return((value != NULL) ? value : xstrdup(""));
}
/****************************************************************************/
static char *mcap_no_parts(message)
MESSAGE *message;
{
      /*
       * Return the number of body parts in message as
       * an allocated string.
       */
      
      char *buf;
      MESSAGE *body_parts;

      /* Get the body parts and count them */

      body_parts = get_body_parts(message);
      buf = xstrdup(utos(count_messages(body_parts)));
      free_messages(body_parts);

      /* And return the buffer */

      return(buf);
}
/****************************************************************************/
static char *mcap_body_parts(message, cmd, tfiles)
MESSAGE *message;
char *cmd, **tfiles;
{
      /*
       * Return a list of one content-type and temporary file
       * pair for each body part in message.  The list is
       * returned as an allocated string.
       *
       * We "know" that tfiles must have one entry for each body
       * part, since it will have been set up that way by the
       * mcap_tempfiles() routine.
       */
      
      char *deftype, *ctype;
      char **tfile, *buf;
      MESSAGE *body_parts, *b;

      /* Get the default content-type for the body-parts */

      deftype = vstrcat(TEXT_TYPE, "/", PLAIN_SUBTYPE, NULL);

      /* Extract the body parts for the message */

      b = body_parts = get_body_parts(message);
      tfile = tfiles;

      /* Is there a message temporary file to skip? */

      if (cmd_uses_tempfile(message, cmd) & TFILE_MESSAGE) {
            /* Skip the message temporary file */

            tfile++;
      }

      /* Initialise the buffer */

      buf = xstrdup("");

      /* Now loop over the body parts, adding each to the buffer */

      while (b != NULL && b->text != NULL) {
            /* Add the content-type and temporary file pair */

            ctype = (b->contype == NULL) ? deftype : typeonly(b->contype);
            buf = xrealloc(buf, strlen(buf) + strlen(ctype)
                         + strlen(*tfile) + 2);
            (void) strcat(buf, ctype);
            (void) strcat(buf, " ");
            (void) strcat(buf, *tfile++);

            /* Move on to the next pair */

            if ((b = b->next) != NULL && b->text != NULL) {
                  /* Append a space to the buffer */
                  
                  buf = xrealloc(buf, strlen(buf) + 2);
                  (void) strcat(buf, " ");
            }
      }

      /* Clean up and return the buffer */

      free(deftype);
      free_messages(body_parts);
      return(buf);
}
/****************************************************************************/
static void clean_up_tempfiles(tfiles)
char **tfiles;
{
      /* Unlink and free the temporary files listed in tfiles */

      char **tfile;

      /* Unlink and free each temporary file name in tfiles */

      for (tfile = tfiles; tfile != NULL && *tfile != NULL; tfile++) {
            (void) unlink(*tfile);
            free(*tfile);
      }

      /* Now free the list of temporary files itself */

      if (tfiles != NULL) {
            free(tfiles);
      }
      return;
}
/****************************************************************************/
static void add_mime_tempfiles(tfiles)
char **tfiles;
{
      /* Add the temporary files to the list of outstanding ones */

      MIME_TEMPFILES *node;

      /* Initialise a new mime tempfiles node */

      node = (MIME_TEMPFILES *) xmalloc(sizeof(MIME_TEMPFILES));
      node->tfiles = tfiles;
      node->next = mime_tempfiles;

      /* And update the list to point at the new node */

      mime_tempfiles = node;
      return;
}
/****************************************************************************/
void remove_mime_tempfiles()
{
      /* Remove any outstanding temporary files */

      MIME_TEMPFILES *node, *next;

      /* Loop over the tempfiles, removing them */

      node = mime_tempfiles;
      while (node != NULL) {
            /* Clean up the temporary files */

            clean_up_tempfiles(node->tfiles);
            
            /* Now free the node and move on to the next */

            next = node->next;
            free(node);
            node = next;
      }

      /* Finally, clear the list pointer */

      mime_tempfiles = NULL;
      return;
}
/****************************************************************************/
void list_mailcaps()
{
      /* List the defined mailcap entries to typeout */

      MAILCAP *m;
      
      /* Loop over all the available mail capabilities */

      for (m = mailcaps; m != NULL; m = m->next) {
            /* Print the specified content-type */

            typeout(m->contype);
            typeout("; ");
            typeout(m->view);

            /* Now print any optional fields */

            if (m->test != NULL) {
                  typeout("; test=");
                  typeout(m->test);
            }
            if (m->print != NULL) {
                  typeout("; print=");
                  typeout(m->print);
            }
            if (m->edit != NULL) {
                  typeout("; edit=");
                  typeout(m->edit);
            }
            if (m->compose != NULL) {
                  typeout("; compose=");
                  typeout(m->compose);
            }
            if (m->typed != NULL) {
                  typeout("; composetyped=");
                  typeout(m->typed);
            }
            if (m->desc != NULL) {
                  typeout("; description=");
                  typeout(m->desc);
            }
            if (m->file != NULL) {
                  typeout("; nametemplate=");
                  typeout(m->file);
            }
            if (m->term) {
                  typeout("; needsterminal");
            }
            if (m->page) {
                  typeout("; copiousoutput");
            }
            if (m->textual) {
                  typeout("; textualnewlines=");
            }

            /* And terminate the line */

            typeout("\n\n");
      }

      /* All done */

      return;
}
/****************************************************************************/
void list_mime_types()
{
      /* List the defined mime types to typeout */

      MIME_TYPE *t;
      MIME_SUFFIX *s;
      
      /* Loop over all the available mime types */

      for (t = mime_types; t != NULL; t = t->next) {
            /* Print the specified content-type */

            column(t->contype);

            /* Now loop over the available suffixes */

            for (s = mime_suffixes; s != NULL; s = s->next) {
                  /* Print the suffix if it matches */

                  if (s->type == t) {
                        typeout(s->suffix);
                        typeout(" ");
                  }
            }

            /* And terminate the line */

            typeout("\n");
      }

      /* All done */

      return;
}
/****************************************************************************/
void list_mime_charsets()
{
      /* List the defined mime charsets to typeout */

      MIME_CHARSET *c;
      
      /* Loop over all the available mime charsets */

      for (c = mime_charsets; c != NULL; c = c->next) {
            /* Print the specified charset */

            typeout(c->charset);
            typeout("\n");
      }

      /* All done */

      return;
}
/****************************************************************************/
CLIST *contype_complete(list, base)
CLIST *list;
char *base;
{
      /* Return a list of content-types completing base */

      MIME_TYPE *t;

      /* Add the known MIME content-types to the list */

      for (t = mime_types; t != NULL; t = t->next) {
            if (!strncasecmp(base, t->contype, strlen(base))) {
                  list = add_clist(list, t->contype, FALSE);
            }
      }

      /* And return the updated list */

      return(list);
}
/****************************************************************************/
CLIST *charset_complete(list, base)
CLIST *list;
char *base;
{
      /* Return a list of charsets completing base */

      MIME_CHARSET *c;

      /* Add the known MIME charsets to the list */

      for (c = mime_charsets; c != NULL; c = c->next) {
            if (!strncasecmp(base, c->charset, strlen(base))) {
                  list = add_clist(list, c->charset, FALSE);
            }
      }

      /* And return the updated list */

      return(list);
}
/****************************************************************************/
CLIST *encoding_complete(list, base)
CLIST *list;
char *base;
{
      /* Return a list of encodings completing base */

      static char *encodings[] = ENCODINGS;
      char **enc;

      /* Add the known MIME encodings to the list */

      for (enc = encodings; *enc != NULL; enc++) {
            if (!strncasecmp(base, *enc, strlen(base))) {
                  list = add_clist(list, *enc, FALSE);
            }
      }

      /* And return the updated list */

      return(list);
}
/****************************************************************************/

Generated by  Doxygen 1.6.0   Back to index