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

atom.c

/* Atom.c - Code to handle atoms of RFC 822 headers.
   Copyright (C) 1992 - 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 <ctype.h>
#include "af.h"
#include "atom.h"
#include STRING_HDR

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

#ifndef lint
static char *RcsId = "$Id: atom.c,v 2.4 2002/08/21 23:54:48 malc Exp $";
static char *AtomId = ATOMID;
#endif /* ! lint */

/****************************************************************************/
/* Global function declarations */

extern char *xmalloc(), *xrealloc();
extern char *xstrdup(), *vstrcat();
extern void free();

/* Local function declarations */

char *atext(), *avalue();
void afree();
ATOM *aappend(), *ainsert(), *aadd();
ATOM *aprev(), *acut();
static char *get_atom(), *get_catom();
static char *get_ctatom(), *get_matom();
static char *get_tatom(), *get_watom();
static char *set_char(), *set_str();
static char *set_ws(), *escape_chars();
static char *unescape_chars();
static char *unfold_atom();
static ATOM *atomise();

/****************************************************************************/
/* The error flag and text for tokenising and parsing */

int a_errno = AERR_NONE;
char *a_errtext = NULL;

/****************************************************************************/
ATOM *tokenise(text)
char *text;
{
      /* Break down some text into its component parts */

      return(atomise(text, get_atom));
}
/****************************************************************************/
ATOM *ctokenise(text)
char *text;
{
      /* Break down a content type into its component parts */

      return(atomise(text, get_catom));
}
/****************************************************************************/
ATOM *cttokenise(text)
char *text;
{
      /* Break down the text of a comment into its component parts */

      return(atomise(text, get_ctatom));
}
/****************************************************************************/
ATOM *mtokenise(text)
char *text;
{
      /* Break down a mailcap entry into its component parts */

      return(atomise(text, get_matom));
}
/****************************************************************************/
ATOM *ttokenise(text)
char *text;
{
      /* Break down a mime.types entry into its component parts */

      return(atomise(text, get_tatom));
}
/****************************************************************************/
ATOM *wtokenise(text)
char *text;
{
      /* Break down some text into its component words */

      return(atomise(text, get_watom));
}
/****************************************************************************/
static ATOM *atomise(text, get_func)
char *text, *(*get_func)();
{
      /* Break down some text into its component parts */

      char *start, *end, *value;
      int type;
      ATOM *alist = NULL;
      ATOM *last = NULL;

      /* Haven't had an error yet */

      a_errno = AERR_NONE;
      if (a_errtext != NULL) {
            free(a_errtext);
            a_errtext = NULL;
      }

      /* Initialise the pointers */

      if ((start = end = text) == NULL) {
            /* No text to tokenise */

            return(NULL);
      }

      /* Loop until all atoms are exhausted */

      while (*start != '\0') {
            /* Get the next atomic item from the text */

            if ((end = get_func(start, &type)) == NULL) {
                  afree(alist);
                  return(NULL);
            }

            /* Extract the atom from the text */

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

            /* Add the atom to the list */

            alist = aadd(alist, last, value, type);
            last = (last != NULL) ? last->next : alist;
            
            /* Free space and advance start */

            free(value);
            start = end;
      }

      return(alist);
}
/****************************************************************************/
static char *get_atom(start, type)
char *start;
int *type;
{
      /* Find the end of the current atom and set its type */

      /* We don't know what type this atom is yet */

      *type = AT_WS;

      /* Generate the atom */

      switch (*start) {
      case ' ':                     /* Whitespace */
      case '\t':
      case '\n':
      case '\r':
            return(set_ws(start, type));
      case '(':                     /* Comment */
            return(set_str(start, SC_COMMENT, TC_COMMENT,
                         TRUE, AT_COMMENT, type));
      case '"':                     /* Quoted string */
            return(set_str(start, SC_QSTRING, TC_QSTRING, 
                         TRUE, AT_QSTRING, type));
      case '[':                     /* Domain literal */
            return(set_str(start, SC_DLITERAL, TC_DLITERAL,
                         TRUE, AT_DLITERAL, type));
      case '.':
            return(set_char(start, AT_DOT, type));
      case '@':
            return(set_char(start, AT_AT, type));
      case '%':
            return(set_char(start, AT_PERCENT, type));
      case ',':
            return(set_char(start, AT_COMMA, type));
      case '<':
            return(set_char(start, AT_LANGLEB, type));
      case '>':
            return(set_char(start, AT_RANGLEB, type));
      case ':':
            return(set_char(start, AT_COLON, type));
      case ';':
            return(set_char(start, AT_SEMI, type));
      case ')':
      case ']':
      case '\\':
            a_errno = AERR_ADDRESS;
            a_errtext = xstrdup(start);
            return(NULL);
      default:
            return(set_str(start, SC_ATOM, TC_ATOM,
                         FALSE, AT_ATOM, type));
      }
      /*NOTREACHED*/
}
/****************************************************************************/
static char *get_catom(start, type)
char *start;
int *type;
{
      /* Find the end of the current content-type atom and set its type */

      /* We don't know what type this atom is yet */

      *type = AT_WS;

      /* Generate the atom */

      switch (*start) {
      case ' ':                     /* Whitespace */
      case '\t':
      case '\n':
      case '\r':
            return(set_ws(start, type));
      case '(':                     /* Comment */
            return(set_str(start, SC_COMMENT, TC_COMMENT,
                         TRUE, AT_COMMENT, type));
      case '"':                     /* Quoted string */
            return(set_str(start, SC_QSTRING, TC_QSTRING,
                         TRUE, AT_QSTRING, type));
      case '[':                     /* Domain literal */
            return(set_str(start, SC_DLITERAL, TC_DLITERAL,
                         TRUE, AT_DLITERAL, type));
      case '@':
            return(set_char(start, AT_AT, type));
      case '%':
            return(set_char(start, AT_PERCENT, type));
      case ',':
            return(set_char(start, AT_COMMA, type));
      case '<':
            return(set_char(start, AT_LANGLEB, type));
      case '>':
            return(set_char(start, AT_RANGLEB, type));
      case ':':
            return(set_char(start, AT_COLON, type));
      case ';':
            return(set_char(start, AT_SEMI, type));
      case ')':
      case ']':
      case '\\':
            a_errno = AERR_ADDRESS;
            a_errtext = xstrdup(start);
            return(NULL);
      case '/':
            return(set_char(start, AT_SLASH, type));
      case '?':
            return(set_char(start, AT_QUERY, type));
      case '=':
            return(set_char(start, AT_EQUALS, type));
      default:
            return(set_str(start, SC_ATOM, TC_CATOM,
                         FALSE, AT_ATOM, type));
      }
      /*NOTREACHED*/
}
/****************************************************************************/
static char *get_ctatom(start, type)
char *start;
int *type;
{
      /* Find the end of the current comment atom and set its type */

      /* We don't know what type this atom is yet */

      *type = AT_WS;

      /* Generate the atom */

      switch (*start) {
      case ' ':                     /* Whitespace */
      case '\t':
      case '\n':
      case '\r':
            return(set_ws(start, type));
      case '(':                     /* Comment */
            return(set_str(start, SC_COMMENT, TC_COMMENT,
                         TRUE, AT_COMMENT, type));
      default:
            return(set_str(start, SC_ATOM, TC_CTATOM,
                         TRUE, AT_ATOM, type));
      }
      /*NOTREACHED*/
}
/****************************************************************************/
static char *get_matom(start, type)
char *start;
int *type;
{
      /* Find the end of the current mailcap atom and set its type */

      /* We don't know what type this atom is yet */

      *type = AT_WS;

      /* Generate the atom */

      switch (*start) {
      case ' ':                     /* Whitespace */
      case '\t':
      case '\n':
      case '\r':
            return(set_ws(start, type));
      case '#':
            return(set_str(start, SC_MCOMMENT, TC_MCOMMENT,
                         TRUE, AT_COMMENT, type));
      case '=':
            return(set_char(start, AT_EQUALS, type));
      case ';':
            return(set_char(start, AT_SEMI, type));
      default:
            return(set_str(start, SC_ATOM, TC_MATOM,
                         TRUE, AT_ATOM, type));
      }
      /*NOTREACHED*/
}
/****************************************************************************/
static char *get_tatom(start, type)
char *start;
int *type;
{
      /* Find the end of the current mime.types atom and set its type */

      /* We don't know what type this atom is yet */

      *type = AT_WS;

      /* Generate the atom */

      switch (*start) {
      case ' ':                     /* Whitespace */
      case '\t':
      case '\n':
      case '\r':
            return(set_ws(start, type));
      case '#':
            return(set_str(start, SC_TCOMMENT, TC_TCOMMENT,
                         TRUE, AT_COMMENT, type));
      default:
            return(set_str(start, SC_ATOM, TC_TATOM,
                         TRUE, AT_ATOM, type));
      }
      /*NOTREACHED*/
}
/****************************************************************************/
static char *get_watom(start, type)
char *start;
int *type;
{
      /* Find the end of the current word atom and set its type */

      /* We don't know what type this atom is yet */

      *type = AT_WS;

      /* Generate the atom */

      switch (*start) {
      case ' ':                     /* Whitespace */
      case '\t':
      case '\n':
      case '\r':
            return(set_ws(start, type));
      default:
            return(set_str(start, SC_WORD, TC_WORD,
                         FALSE, AT_ATOM, type));
      }
      /*NOTREACHED*/
}
/****************************************************************************/
static char *set_char(start, atype, type)
char *start;
int atype, *type;
{
      /* Set the type for a single-character atom of type atype */

      *type = atype;

      /* And return the first character after the atom */

      return(start + 1);
}
/****************************************************************************/
static char *set_str(atom, start, term, quoting, atype, type)
char *atom, *start, *term;
int quoting, atype, *type;
{
      /* Handle multi-character atoms */

      char *p;
      int nested = 0;

      /* Set the type of the atom */

      *type = atype;

      /* Find the end of the atom */

      for (p = atom; *p != '\0'; p++) {
            /* Handle escaped characters */

            if (quoting && *p == '\\' && *(p + 1) != '\0') {
                  /* Quoted special character; skip */

                  p++;
            } else if (p != atom && strchr(term, *p) != NULL) {
                  /* Termination character; check nesting */

                  if (nested-- <= 0) {
                        return((atype == AT_ATOM) ? p : p + 1);
                  }
            } else if (p != atom && strchr(start, *p) != NULL) {
                  /* Start character; increment nesting level */

                  nested++;
            }
      }

      /* No termination character; error anything but an atom */

      switch (atype) {
      case AT_QSTRING:
            a_errno = AERR_QSTRING;
            break;
      case AT_DLITERAL:
            a_errno = AERR_DLITERAL;
            break;
      case AT_COMMENT:
            a_errno = AERR_COMMENT;
            break;
      default:
            return(p);
      }

      /* If we reached here we need to set the error text */

      a_errtext = xstrdup(atom);
      return(NULL);
}
/****************************************************************************/
static char *set_ws(start, type)
char *start;
int *type;
{
      /* Handle whitespace */

      char *p;

      /* Set the type of the atom */

      *type = AT_WS;

      /* Find the end of the atom */

      for (p = start; p != '\0' && isspace(*p); p++) {
            /* NULL LOOP */
      }
      return(p);
}
/****************************************************************************/
ATOM *aappend(alist, text, type)
ATOM *alist;
char *text;
int type;
{
      /* Append an atom to the list, by inserting at the end */

      return(ainsert(alist, NULL, text, type));
}
/****************************************************************************/
ATOM *ainsert(alist, where, text, type)
ATOM *alist, *where;
char *text;
int type;
{
      /* Insert an atom before where in alist */

      ATOM *node, *prev;

      /* Create and fill the node for the new atom */

      node = (ATOM *) xmalloc(sizeof(ATOM));
      node->text = xstrdup(text);
      node->type = type;
      node->next = NULL;

      /* Find the atom before where in the list */

      if ((prev = aprev(alist, where)) == NULL) {
            /* No previous atom, prepend the node */

            node->next = alist;
            return(node);
      }

      /* Insert the node at the position we found */

      node->next = prev->next;
      prev->next = node;

      /* And return the updated atom list */

      return(alist);
}
/****************************************************************************/
ATOM *aadd(alist, where, text, type)
ATOM *alist, *where;
char *text;
int type;
{
      /* Append an atom after where in alist */

      ATOM *node;

      /* Create and fill the node for the new atom */

      node = (ATOM *) xmalloc(sizeof(ATOM));
      node->text = xstrdup(text);
      node->type = type;
      node->next = NULL;

      /* Append the node after where if possible */

      if (alist != NULL && where != NULL) {
            node->next = where->next;
            where->next = node;
      }

      /* And return the updated atom list */

      return((alist != NULL) ? alist : node);
}
/****************************************************************************/
ATOM *amerge(alist, where, mlist)
ATOM *alist, *where, *mlist;
{
      /* Insert mlist into alist before where, and return the new list */

      ATOM *last, *prev;

      /* First find the last atom in mlist */

      if ((last = aprev(mlist, NULL)) == NULL) {
            /* Nothing to insert, just return */

            return(alist);
      }

      /* Find the atom before where in the list */

      if ((prev = aprev(alist, where)) == NULL) {
            /* No previous atom, prepend the list */

            last->next = alist;
            return(mlist);
      }

      /* Now merge the lists together */

      last->next = prev->next;
      prev->next = mlist;

      /* And return the updated atom list */

      return(alist);
}
/****************************************************************************/
ATOM *asplit(alist, atom, pos)
ATOM *alist, *atom;
char *pos;
{
      /*
       * Split an atom list into two at some point within a single
       * atom.  The atom may be split into two atoms, one at the end
       * of the old list, and one at the start of the new.
       * specified by pos, discarding the character at pos.
       * return the newly created atom as the head of a
       * new list, separate from the old one.
       */

      char *text;
      ATOM *prev, *new_list = NULL;

      /* Check for pos lying at the start of the atom */

      if (pos == atom->text) {
            /* Pos is at start; atom starts the new list */

            if ((prev = aprev(alist, atom)) == NULL) {
                  /* Can't split at the head of the list */

                  return(NULL);
            }

            /* Split the list before the atom */

            prev->next = NULL;

            /* Update the atom's text */

            text = xstrdup(atom->text + 1);
            free(atom->text);
            atom->text = text;

            /* And return the atom */

            return(atom);
      }

      /* Check for pos lying at the end of the atom */

      if (*(pos + 1) == '\0') {
            /* Pos is at end; atom ends the old list */

            /* Update the atom's text */
            *pos = '\0';

            /* And split the list after atom */

            new_list = atom->next;
            atom->next = NULL;

            /* Now return the new atom list */

            return(new_list);
      }

      /* We need to split the atom; update the old atom's text */

      *pos++ = '\0';

      /* Now create the new atom list */

      new_list = aappend(NULL, pos, atom->type);
      new_list->next = atom->next;

      /* Terminate the original list and return the new one */

      atom->next = NULL;
      return(new_list);
}
/****************************************************************************/
ATOM *acomment(alist)
ATOM *alist;
{
      /* Return an atom containing alist as a comment */

      char *buf, *qbuf, *ebuf;
      ATOM *new_list;

      /* Allocate a new string and set it to the comment text */

      buf = atext(NULL, alist, AC_UNQUOTE);
      qbuf = vstrcat("(", buf, ")", NULL);

      /* Check the text is a valid comment */

      if ((new_list = tokenise(qbuf)) == NULL
          || new_list->next != NULL) {
            /* We need to fix parens in the comment */

            free(qbuf);
            afree(new_list);
            ebuf = escape_chars(buf, EC_COMMENT);
            qbuf = vstrcat("(", ebuf, ")", NULL);
            new_list = aappend(NULL, qbuf, AT_QSTRING);
      }

      /* Clean up and return the new list */

      free(buf);
      free(qbuf);
      return(new_list);
}
/****************************************************************************/
ATOM *aquote(alist)
ATOM *alist;
{
      /* Return an atom containing alist as a quoted string */

      char *buf, *ebuf, *qbuf;
      ATOM *new_list;

      /* Allocate a new string and set it to the quoted string */

      buf = atext(NULL, alist, AC_UNQUOTE);
      ebuf = escape_chars(buf, EC_QSTRING);
      qbuf = vstrcat("\"", ebuf, "\"", NULL);

      /* Make an atom list containing the string */

      new_list = aappend(NULL, qbuf, AT_QSTRING);

      /* Clean up and return the new list */

      free(buf);
      free(qbuf);
      return(new_list);
}
/****************************************************************************/
ATOM *acopy(alist)
ATOM *alist;
{
      /* Return an allocated copy of alist */

      ATOM *atom, *new_list = NULL;

      /* Simply append each token to the new list */

      for (atom = alist; atom != NULL; atom = atom->next) {
            new_list = aappend(new_list, atom->text, atom->type);
      }

      /* And return the new atom list */

      return(new_list);
}
/****************************************************************************/
ATOM *adiscard(alist, atom)
ATOM *alist, *atom;
{
      /* Discard atom from alist and return the updated alist */

      ATOM *prev;

      /* Remove the atom from the list */

      if ((prev = aprev(alist, atom)) != NULL) {
            /* Update prev to point after atom */

            prev->next = atom->next;
      } else if (atom == alist) {
            /* Update alist to point after atom */

            alist = alist->next;
      }

      /* Now free the discarded atom */

      free(atom->text);
      free(atom);

      /* And return the updated atom list */

      return(alist);
}
/****************************************************************************/
ATOM *acut(alist, start, end)
ATOM *alist, *start, *end;
{
      /*
       * Strip atoms from start up to (but not including) end
       * out of alist. Returns alist as modified by the cut.
       */

      ATOM *prev;

      /* First find the atom before start */

      if ((prev = aprev(alist, start)) != NULL) {
            /* Cut from start up to end out of the list */

            prev->next = end;
      } else if (alist == start) {
            /* Cutting at the start of the list */

            alist = end;
      }

      /* Find the atom before end */

      if (end != NULL && (prev = aprev(start, end)) != NULL) {
            /* Update the end of the cut atoms */

            prev->next = NULL;
      }

      /* Now return the updated atom list */

      return(alist);
}
/****************************************************************************/
ATOM *adelete(alist, start, end)
ATOM *alist, *start, *end;
{
      /*
       * Delete atoms from start up to (but not including) end
       * from alist. Returns alist as modified by the cut.
       */

      /* Cut the atoms and free any cut section */

      if ((alist = acut(alist, start, end)) != start) {
            afree(start);
      }

      /* Return the updated atom list */

      return(alist);
}
/****************************************************************************/
ATOM *aprev(alist, atom)
ATOM *alist, *atom;
{
      /* Return the atom before atom in alist, or NULL if none */

      ATOM *a;

      /* Loop over the list looking for atom */

      for (a = alist; a != NULL && a != atom; a = a->next) {
            /* Is this the atom's previous atom? */

            if (a->next == atom) {
                  return(a);
            }
      }

      /* No previous atom found */

      return(NULL);
}
/****************************************************************************/
ATOM *atoken(alist)
ATOM *alist;
{
      /*
       * Return the first atom in alist that is a "token"; ie that
       * isn't whitespace or a comment.  Returns NULL if none found.
       */

      ATOM *atom;

      /* Loop over all the whitespace or comment tokens */

      for (atom = alist; atom != NULL && IS_WS(atom); atom = atom->next) {
            /* NULL LOOP */
      }

      /* Return the atom we found */

      return(atom);
}
/****************************************************************************/
ATOM *afind(alist)
ATOM *alist;
{
      /*
       * Return the first atom in alist that isn't whitespace.
       * Returns NULL if none found.
       */

      ATOM *atom;

      /* Loop over all the whitespace atoms */

      for (atom = alist; atom != NULL && atom->type == AT_WS;
           atom = atom->next) {
            /* NULL LOOP */
      }

      /* Return the atom we found */

      return(atom);
}
/****************************************************************************/
ATOM *asearch(alist, type)
ATOM *alist;
int type;
{
      /* Return the first atom of type 'type' in alist */

      ATOM *atom;

      /* Loop until we find an atom of the right type */

      for (atom = alist; atom != NULL && atom->type != type;
           atom = atom->next) {
            /* NULL LOOP */
      }

      /* Return the atom we found */

      return(atom);
}
/****************************************************************************/
char *avalue(atom)
ATOM *atom;
{
      /*
       * Return the "value" of the atom in a static buffer.  The
       * "value" is the text without any quoting characters.
       */

      static char *buf = NULL;
      char *ubuf;

      /* Free any old return buffer */

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

      /* Now extract the unquoted text of the atom */

      if (atom->type == AT_QSTRING || atom->type == AT_COMMENT) {
            /* Allocate and fill the return buffer */

            buf = xmalloc(strlen(atom->text) - 1);
            (void) strncpy(buf, atom->text + 1, strlen(atom->text) - 2);
            buf[strlen(atom->text) - 2] = '\0';
      } else {
            /* Just copy the atom's text into the buffer */

            buf = xstrdup(atom->text);
      }

      /* Now unescape any quotes in quoted strings */

      if (atom->type == AT_QSTRING) {
            /* Copy and unescape the buffer */

            ubuf = unescape_chars(buf, EC_QSTRING);
            free(buf);
            buf = xstrdup(ubuf);
      }

      /* And return the buffer */

      return(buf);
}
/****************************************************************************/
char *atext(buf, alist, canon)
char *buf;
ATOM *alist;
int canon;
{
      /*
       * Return an allocated string containing the concatenation of the
       * string held in buf (if non-null) and the atoms in alist.
       * Canon specifies the canonicalisation required: none, trim
       * leading and trailing white space, trim white space and unfold,
       * canonicalise white space, remove the parens around comments,
       * unquote quoted strings, unquote quoted strings and quote double
       * quotes, or fullly canonical RFC 822 form.
       */

      size_t len = 0;
      ATOM *atom, *start, *end;

      /* Set the start and end positions in the list */

      start = end = NULL;
      for (atom = alist; canon != AC_NONE && atom != NULL;
           atom = atom->next) {
            /* Is this white space we might trim? */

            if (atom->type != AT_WS) {
                  /* Update the start and end atoms */

                  start = (start == NULL) ? atom : start;
                  end = atom->next;
            }
      }

      /* Default the start atom if required */

      start = (start == NULL && canon == AC_NONE) ? alist : start;

      /* If we don't have any atoms, there's no text either */

      if (start == NULL) {
            /* Just return a null string */

            return((buf == NULL) ? xstrdup("") : buf);
      }

      /* Find out how long the text will be */

      for (atom = start; atom != end; atom = atom->next) {
            if (atom->type == AT_QSTRING && canon == AC_UNQUOTE ||
                atom->type == AT_COMMENT && canon == AC_UNCOMMENT) {
                  /* We will strip the start and end of this atom */

                  len += strlen(atom->text) - 2;
            } else if ((atom->type == AT_QSTRING
                      || atom->type == AT_COMMENT)
                     && (canon == AC_WS || canon == AC_UNFOLD
                         || canon == AC_FULL)) {
                  /* We'll add the unfolded text of the atom */

                  len += strlen(unfold_atom(atom));
            } else if (atom->type == AT_WS &&
                     (canon == AC_WS || canon == AC_UNQUOTE
                      || canon == AC_UNFOLD &&
                      strchr(atom->text, '\n') != NULL)) {
                  /* We'll add a single whitespace character */

                  len++;
            } else if (atom->type == AT_SEMI && canon == AC_FULL) {
                  /* We'll be adding a space after the semi */

                  len += strlen(atom->text) + 1;
            } else if (canon != AC_FULL || atom->type != AT_WS
                     && atom->type != AT_COMMENT) {
                  /* Add the length of this atom */

                  len += strlen(atom->text);
            }
      }

      /* Allocate or reallocate the string */

      if (buf == NULL) {
            buf = xmalloc(len + 1);
            *buf = '\0';
      } else {
            buf = xrealloc(buf, strlen(buf) + len + 1);
      }

      /* Concatenate the list to the string */

      for (atom = start; atom != end; atom = atom->next) {
            if (atom->type == AT_QSTRING && canon == AC_UNQUOTE ||
                atom->type == AT_COMMENT && canon == AC_UNCOMMENT) {
                  /* Add the value of the atom */

                  (void) strcat(buf, avalue(atom));
            } else if ((atom->type == AT_QSTRING
                      || atom->type == AT_COMMENT)
                     && (canon == AC_WS || canon == AC_UNFOLD
                         || canon == AC_FULL)) {
                  /* We'll add the unfolded text of the atom */

                  (void) strcat(buf, unfold_atom(atom));
            } else if (atom->type == AT_WS &&
                     (canon == AC_WS || canon == AC_UNQUOTE
                      || canon == AC_UNFOLD &&
                      strchr(atom->text, '\n') != NULL)) {
                  /* Add a single (canonical) space */

                  (void) strcat(buf, " ");
            } else if (atom->type == AT_SEMI && canon == AC_FULL) {
                  /* Add the semi followed by a space */

                  (void) strcat(buf, atom->text);
                  (void) strcat(buf, " ");
            } else if (canon != AC_FULL || atom->type != AT_WS
                     && atom->type != AT_COMMENT) {
                  /* Add the atom to the buffer */

                  (void) strcat(buf, atom->text);
            }
      }

      /* Return the string buffer */

      return(buf);
}
/****************************************************************************/
void afree(alist)
ATOM *alist;
{
      /* Free the atom list alist */

      if (alist != NULL) {
            afree(alist->next);
            free(alist->text);
            free(alist);
      }
      return;
}
/****************************************************************************/
char *a_strerror()
{
      /* Return the text associated with an error */

      /* The list of base messages */

      static char *aerr_base[] = {
            AERR_BASE_TEXT, AERR_BASE_TEXT, AERR_BASE_TEXT,
            AERR_BASE_TEXT, AERR_BASE_TEXT, AERR_BASE_TEXT,
            AERR_BASE_TEXT, AERR_BASE_TEXT, AERR_BASE_TEXT,
            AERR_BASE_TEXT, AERR_BASE_TEXT, CERR_BASE_TEXT,
            CERR_BASE_TEXT, CERR_BASE_TEXT, EERR_BASE_TEXT,
            EERR_BASE_TEXT, DERR_BASE_TEXT, DERR_BASE_TEXT,
            DERR_BASE_TEXT, SERR_BASE_TEXT, RERR_BASE_TEXT,
            RERR_BASE_TEXT, RERR_BASE_TEXT
      };

      /* The list of error messages */

      static char *aerr_text[] = {
            ATEXT_NONE, ATEXT_NULL, ATEXT_ADDRESS, ATEXT_BRACKET, 
            ATEXT_ROUTE, ATEXT_LOCAL, ATEXT_DOMAIN, ATEXT_QSTRING,
            ATEXT_DLITERAL, ATEXT_COMMENT, ATEXT_GROUP, CTEXT_NULL,
            CTEXT_TYPE, CTEXT_PARAM, ETEXT_NULL, ETEXT_ENCODING,
            DTEXT_NULL, DTEXT_DISP, DTEXT_PARAM, STEXT_CHARSET,
            RTEXT_TOKEN, RTEXT_BRACKET, RTEXT_REFERENCE
      };

      /* A static buffer for the error message */

      static char *err_buf = NULL;

      /* Free the buffer if previously allocated */

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

      /* Allocate the buffer */

      if (a_errtext != NULL) {
            err_buf = xmalloc(strlen(aerr_base[a_errno]) +
                          strlen(aerr_text[a_errno]) +
                          strlen(a_errtext) + 2);
      } else {
            err_buf = xmalloc(strlen(aerr_base[a_errno]) +
                          strlen(aerr_text[a_errno]) + 1);
      }

      /* Fill the buffer */

      (void) strcpy(err_buf, aerr_base[a_errno]);
      (void) strcat(err_buf, aerr_text[a_errno]);

      /* Append the text of the error if any */

      if (a_errtext != NULL) {
            (void) strcat(err_buf, " ");
            (void) strcat(err_buf, a_errtext);
      }

      /* Return the error string */

      return(err_buf);
}
/****************************************************************************/
static char *escape_chars(text, echars)
char *text, *echars;
{
      /*
       * Return a static buffer containing text with any character
       * listed in echars escaped with a backslash.
       */

      static char *buf = NULL;
      char *p, *q;
      int escaped = FALSE;

      /* (Re)allocate the return buffer */

      q = buf = (buf == NULL) ? xmalloc(strlen(text) * 2 + 1)
            : xrealloc(buf, strlen(text) * 2 + 1);

      /* Now copy the text, escaping as required */

      p = text;
      while (*p != '\0') {
            /* Escape this character if required */

            if (!escaped && strchr(echars, *p) != NULL) {
                  /* Insert a backslash before the character */

                  *q++ = '\\';
            }

            /* Check if the next character is escaped */

            escaped = (!escaped && *p == '\\');

            /* And copy the character itself */

            *q++ = *p++;
      }
      *q = '\0';

      /* Resize and return the static buffer */

      buf = xrealloc(buf, strlen(buf) + 1);
      return(buf);
}
/****************************************************************************/
static char *unescape_chars(text, echars)
char *text, *echars;
{
      /*
       * Return a static buffer containing text with any backslash
       * escapes of characters listed in echars removed.
       */

      static char *buf = NULL;
      char *p, *q;
      int escaped = FALSE;

      /* (Re)allocate the return buffer */

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

      /* Now copy the text, escaping as required */

      p = text;
      while (*p != '\0') {
            /* Unescape this character if required */

            if (!escaped && *(p + 1) != '\0' &&
                strchr(echars, *(p + 1)) != NULL) {
                  /* Skip the backslash character */

                  p++;
            }

            /* Check if the next character is escaped */

            escaped = (!escaped && *p == '\\');

            /* And copy the character itself */

            *q++ = *p++;
      }
      *q = '\0';

      /* Resize and return the static buffer */

      buf = xrealloc(buf, strlen(buf) + 1);
      return(buf);
}
/****************************************************************************/
static char *unfold_atom(atom)
ATOM *atom;
{
      static char *buf = NULL;
      ATOM *alist;

      /* Free any old return buffer */

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

      /* Atomise the atom's text and check status */

      if ((alist = atomise(atom->text, get_watom)) != NULL) {
            /* Extract the unfolded text of the atom */

            buf = atext(NULL, alist, AC_UNFOLD);
            afree(alist);
      }

      /* Now return the unfolded text */

      return(buf);
}
/****************************************************************************/


Generated by  Doxygen 1.6.0   Back to index