/*
       reader.c - RTF file reader.  Distribution 1.06.

       ASCII 10 (\n) and 13 (\r) are ignored and silently discarded.
       (The read hook will still get a look at them.)

       "\:" is not a ":", it's a control symbol.  But some versions of
       Word seem to write "\:" for ":".  This reader treats "\:" as a
       plain text ":"

       This reader needs to catch \cf and \cb in the color table
       reader?  (Doesn't yet.)
*/

# include       <stdio.h>
# include       <ctype.h>
# include       "rtf.h"


/*
       Return pointer to new element of type t, or NULL
*/

# define        New(t)  ((t *) RTFAlloc (sizeof (t)))


extern char     *index ();
extern char     *malloc ();
extern char     *strcpy ();


/*
       Public variables (listed in rtf.h)
*/

char    rtfTextBuf[rtfBufSiz];
int     rtfTextLen;
int     rtfClass;
int     rtfMajor;
int     rtfMinor;
int     rtfParam;


/*
       Private stuff
*/


static RTFFont  *fontList = NULL;       /* these lists MUST be */
static RTFColor *colorList = NULL;      /* initialized to NULL */
static RTFStyle *styleList = NULL;


static FILE     *rtffp = stdin;

static int      pushback = EOF;


static void     _RTFGetToken ();
static int      GetChar ();
static int      HexVal ();
static void     ReadFontTbl ();
static void     ReadColorTbl ();
static void     ReadStyleSheet ();
static void     ReadInfoGroup ();
static void     ReadPictGroup ();
static void     LookupInit ();
static void     Lookup ();
static int      Hash ();


/*
       Initialize the reader.  This may be called multiple times.
       The only thing not reset is the input stream.
*/

void RTFInit ()
{
int     i;
RTFColor        *cp;
RTFFont         *fp;
RTFStyle        *sp;
RTFStyleElt     *eltList, *ep;

       /* initialize lookup table */
       LookupInit ();

       for (i = 0; i < rtfMaxClass; i++)
               RTFSetClassCallback (i, NULL);
       for (i = 0; i < rtfMaxDestination; i++)
               RTFSetDestinationCallback (i, NULL);

       /* install built-in destination readers */
       RTFSetDestinationCallback (rtfFontTbl, ReadFontTbl);
       RTFSetDestinationCallback (rtfColorTbl, ReadColorTbl);
       RTFSetDestinationCallback (rtfStyleSheet, ReadStyleSheet);
       RTFSetDestinationCallback (rtfInfo, ReadInfoGroup);
       RTFSetDestinationCallback (rtfPict, ReadPictGroup);

       RTFSetReadHook (NULL);

       /* dump old lists if necessary */

       while (fontList != NULL)
       {
               fp = fontList->rtfNextFont;
               RTFFree (fontList->rtfFName);
               RTFFree ((char *) fontList);
               fontList = fp;
       }
       while (colorList != NULL)
       {
               cp = colorList->rtfNextColor;
               RTFFree ((char *) colorList);
               colorList = cp;
       }
       while (styleList != NULL)
       {
               sp = styleList->rtfNextStyle;
               eltList = styleList->rtfSSEList;
               while (eltList != NULL)
               {
                       ep = eltList->rtfNextSE;
                       RTFFree (eltList->rtfSEText);
                       RTFFree ((char *) eltList);
                       eltList = ep;
               }
               RTFFree (styleList->rtfSName);
               RTFFree ((char *) styleList);
               styleList = sp;
       }

       pushback = EOF;
}


/*
       Set the reader's input stream to the given stream.  Can
       be used to redirect other than to the default (stdin).
*/

void RTFSetStream (stream)
FILE    *stream;
{
       rtffp = stream;
}


/* ---------------------------------------------------------------------- */

/*
       Callback table manipulation routines
*/


/*
       Install or return a writer callback for a token class
*/

static void     (*ccb[rtfMaxClass]) ();         /* class callbacks */

void RTFSetClassCallback (class, callback)
int     class;
void    (*callback) ();
{
       if (class >= 0 && class < rtfMaxClass)
               ccb[class] = callback;
}


void (*RTFGetClassCallback (class)) ()
int     class;
{
       if (class >= 0 && class < rtfMaxClass)
               return (ccb[class]);
       return (NULL);
}


/*
       Install or return a writer callback for a destination type
*/

static void     (*dcb[rtfMaxDestination]) ();   /* destination callbacks */

void RTFSetDestinationCallback (dest, callback)
int     dest;
void    (*callback) ();
{
       if (dest >= 0 && dest < rtfMaxDestination)
               dcb[dest] = callback;
}


void (*RTFGetDestinationCallback (dest)) ()
int     dest;
{
       if (dest >= 0 && dest < rtfMaxDestination)
               return (dcb[dest]);
       return (NULL);
}


/* ---------------------------------------------------------------------- */

/*
       Token reading routines
*/


/*
       Read the input stream, invoking the writer's callbacks
       where appropriate.
*/

void RTFRead ()
{
       while (RTFGetToken () != rtfEOF)
               RTFRouteToken ();
}


/*
       Route a token.  If it's a destination for which a reader is
       installed, process the destination internally, otherwise
       pass the token to the writer's class callback.
*/

void RTFRouteToken ()
{
void            (*p) ();

       if (rtfClass < 0 || rtfClass >= rtfMaxClass)    /* watchdog */
       {
               fprintf (stderr, "Unknown class %d: %s", rtfClass, rtfTextBuf);
               fprintf (stderr, " (reader malfunction)\n");
               return;
       }
       if (RTFCheckCM (rtfControl, rtfDestination))
       {
               /* invoke destination-specific callback if there is one */
               if ((p = RTFGetDestinationCallback (rtfMinor)) != NULL)
               {
                       (*p) ();
                       return;
               }
       }
       /* invoke class callback if there is one */
       if ((p = RTFGetClassCallback (rtfClass)) != NULL)
               (*p) ();
}


/*
       Skip to the end of the current group.  When this returns,
       writers that maintain a state stack may want to call their
       state unstacker; global vars will still be set to the group's
       closing brace.
*/

void RTFSkipGroup ()
{
int     level = 1;

       while (RTFGetToken () != rtfEOF)
       {
               if (rtfClass == rtfGroup)
               {
                       if (rtfMajor == rtfBeginGroup)
                               ++level;
                       else if (rtfMajor == rtfEndGroup)
                       {
                               if (--level < 1)
                                       break;  /* end of initial group */
                       }
               }
       }
}


/*
       Read one token.  Call the read hook if there is one.  The
       token class is the return value.  Returns rtfEOF when there
       are no more tokens.
*/

int RTFGetToken ()
{
void    (*p) ();

       for (;;)
       {
               _RTFGetToken ();
               if ((p = RTFGetReadHook ()) != NULL)
                       (*p) ();        /* give read hook a look at token */

               /* Silently discard newlines and carriage returns.  */
               if (!(rtfClass == rtfText
                       && (rtfMajor == '\n' || rtfMajor == '\r')))
                       break;
       }
       return (rtfClass);
}


/*
       Install or return a token reader hook.
*/

static void     (*readHook) ();

void RTFSetReadHook (f)
void    (*f) ();
{
       readHook = f;
}


void (*RTFGetReadHook ()) ()
{
       return (readHook);
}


static void _RTFGetToken ()
{
int     sign;
int     c;

       /* initialize token vars */

       rtfClass = rtfUnknown;
       rtfParam = 0;
       rtfTextBuf[rtfTextLen = 0] = '\0';

       /* get first character, which may be a pushback from previous token */

       if (pushback != EOF)
       {
               c = pushback;
               rtfTextBuf[rtfTextLen] = c;
               rtfTextBuf[++rtfTextLen] = '\0';
               pushback = EOF;
       }
       else if ((c = GetChar ()) == EOF)
       {
               rtfClass = rtfEOF;
               return;
       }

       if (c == '{')
       {
               rtfClass = rtfGroup;
               rtfMajor = rtfBeginGroup;
               return;
       }
       if (c == '}')
       {
               rtfClass = rtfGroup;
               rtfMajor = rtfEndGroup;
               return;
       }
       if (c != '\\')
       {
               /*
                       Two possibilities here:
                       1) ASCII 9, effectively like \tab control symbol
                       2) literal text char
               */
               if (c == '\t')                  /* ASCII 9 */
               {
                       rtfClass = rtfControl;
                       rtfMajor = rtfTab;
               }
               else
               {
                       rtfClass = rtfText;
                       rtfMajor = c;
               }
               return;
       }
       if ((c = GetChar ()) == EOF)
       {
               /* early eof, whoops (class is rtfUnknown) */
               return;
       }
       if (!isalpha (c))
       {
               /*
                       Three possibilities here:
                       1) hex encoded text char, e.g., \'d5, \'d3
                       2) special escaped text char, e.g., \{, \}
                       3) control symbol, e.g., \_, \-, \|, \<10>
               */
               if (c == '\'')                          /* hex char */
               {
               int     c2;

                       if ((c = GetChar ()) != EOF && (c2 = GetChar ()) != EOF)
                       {
                               /* should do isxdigit check! */
                               rtfClass = rtfText;
                               rtfMajor = HexVal (c) * 16 + HexVal (c2);
                               return;
                       }
                       /* early eof, whoops (class is rtfUnknown) */
                       return;
               }

               if (index (":{}\\", c) != NULL)         /* escaped char */
               {
                       rtfClass = rtfText;
                       rtfMajor = c;
                       return;
               }

               /* control symbol */
               Lookup (rtfTextBuf);    /* sets class, major, minor */
               return;
       }
       /* control word */
       while (isalpha (c))
       {
               if ((c = GetChar ()) == EOF)
                       break;
       }

       /*
               At this point, the control word is all collected, so the
               major/minor numbers are determined before the parameter
               (if any) is scanned.  There will be one too many characters
               in the buffer, though, so fix up before and restore after
               looking up.
       */

       if (c != EOF)
               rtfTextBuf[rtfTextLen-1] = '\0';
       Lookup (rtfTextBuf);    /* sets class, major, minor */
       if (c != EOF)
               rtfTextBuf[rtfTextLen-1] = c;

       /*
               Should be looking at first digit of parameter if there
               is one, unless it's negative.  In that case, next char
               is '-', so need to gobble next char, and remember sign.
       */

       sign = 1;
       if (c == '-')
       {
               sign = -1;
               c = GetChar ();
       }
       if (c != EOF)
       {
               while (isdigit (c))     /* gobble parameter */
               {
                       rtfParam = rtfParam * 10 + c - '0';
                       if ((c = GetChar ()) == EOF)
                               break;
               }
               rtfParam *= sign;
       }
       /*
               If control symbol delimiter was a blank, gobble it.
               Otherwise the character is first char of next token, so
               push it back for next call.  In either case, delete the
               delimiter from the token buffer.
       */
       if (c != EOF)
       {
               if (c != ' ')
                       pushback = c;
               rtfTextBuf[--rtfTextLen] = '\0';
       }
       return;
}


/*
       Distributions up through 1.04 assumed high bit could be set in
       RTF file characters.  Beginning with 1.05, that's not true, but
       still check and die if such a character is found.
*/

static int GetChar ()
{
int     c;

       if ((c = getc (rtffp)) != EOF)
       {
               if (c & 0x80)
               {
                       fprintf (stderr, "Char with high bit set (%#x) found\n", c);
                       exit (1);
               }
               rtfTextBuf[rtfTextLen] = c;
               rtfTextBuf[++rtfTextLen] = '\0';
       }
       return (c);
}


static int HexVal (c)
char    c;
{
       if (isupper (c))
               c = tolower (c);
       if (isdigit (c))
               return (c - '0');       /* '0'..'9' */
       return (c - 'a' + 10);          /* 'a'..'f' */
}


/*
       Synthesize a token by setting the global variables to the
       values supplied.  Typically this is followed with a call
       to RTFRouteToken().

       If param is non-negative, it becomes part of the token text.
*/

void RTFSetToken (class, major, minor, param, text)
int     class, major, minor, param;
char    *text;
{
       rtfClass = class;
       rtfMajor = major;
       rtfMinor = minor;
       rtfParam = param;
       if (param >= 0)
               sprintf (rtfTextBuf, "%s%d", text, param);
       else
               strcpy (rtfTextBuf, text);
       rtfTextLen = strlen (rtfTextBuf);
}


/* ---------------------------------------------------------------------- */

/*
       Special destination readers.  They gobble the destination so the
       writer doesn't have to deal with them.  That's wrong for any
       translator that wants to process any of these itself.  In that
       case, these readers should be overridden by installing a different
       destination callback.

       The last token read by each of these reader will be the destination's
       terminating '}', which will then be the current token.  That token
       is passed to RTFRouteToken() - the writer had already seen the '{'
       that began the destination grou, and may have pushed a state; it also
       needs to know at the end of the group that a state should be popped.

       It's important that rtf.h and the control token lookup table list
       as many symbols as possible, because these readers unfortunately
       make strict assumptions about the input they expect, and a token
       of class rtfUnknown will throw them off easily.
*/


/*
       Read { \fonttbl ... } destination.  Old font tables don't have
       braces around each table entry; try to adjust for that.
*/

static void ReadFontTbl ()
{
RTFFont *fp;
char    buf[rtfBufSiz], *bp;
int     old = -1;

       for (;;)
       {
               (void) RTFGetToken ();
               if (RTFCheckCM (rtfGroup, rtfEndGroup))
                       break;
               if (old < 0)            /* first entry - determine tbl type */
               {
                       if (RTFCheckCMM (rtfControl, rtfCharAttr, rtfFontNum))
                               old = 1;        /* no brace */
                       else if (RTFCheckCM (rtfGroup, rtfBeginGroup))
                               old = 0;        /* brace */
                       else
                       {                       /* can't tell! */
                               fprintf (stderr, "Bad font table-1\n");
                               exit (1);
                       }
               }
               if (old == 0)           /* need to find "{" here */
               {
                       if (!RTFCheckCM (rtfGroup, rtfBeginGroup))
                       {
                               fprintf (stderr, "Bad font table-2\n");
                               exit (1);
                       }
                       (void) RTFGetToken ();  /* yes, skip to next token */
               }
               if ((fp = New (RTFFont)) == NULL)
               {
                       fprintf (stderr, "Can't allocate font entry\n");
                       exit (1);
               }
               fp->rtfNextFont = fontList;
               fontList = fp;
               if (!RTFCheckCMM (rtfControl, rtfCharAttr, rtfFontNum))
               {
                       fprintf (stderr, "Bad font table-3\n");
                       exit (1);
               }
               fp->rtfFNum = rtfParam;
               (void) RTFGetToken ();
               if (!RTFCheckCM (rtfControl, rtfFontFamily))
               {
                       fprintf (stderr, "Bad font table-4\n");
                       exit (1);
               }
               fp->rtfFFamily = rtfMinor;
               bp = buf;
               while (RTFGetToken () == rtfText)
               {
                       if (rtfMajor == ';')
                               break;
                       *bp++ = rtfMajor;
               }
               *bp = '\0';
               if ((fp->rtfFName = RTFStrSave (buf)) == NULL)
               {
                       fprintf (stderr, "Can't allocate font name\n");
                       exit (1);
               }
               if (old == 0)   /* need to see "}" here */
               {
                       (void) RTFGetToken ();
                       if (!RTFCheckCM (rtfGroup, rtfEndGroup))
                       {
                               fprintf (stderr, "Bad font table-5\n");
                               exit (1);
                       }
               }
       }
       RTFRouteToken ();       /* feed "}" back to router */
}


/*
       The color table entries have color values of -1 if
       the default color should be used for the entry (only
       a semi-colon is given in the definition, no color values).
       There will be a problem if a partial entry (1 or 2 but
       not 3 color values) is given.  The possibility is ignored
       here.
*/

static void ReadColorTbl ()
{
RTFColor        *cp;
int             cnum = 0;

       for (;;)
       {
               (void) RTFGetToken ();
               if (RTFCheckCM (rtfGroup, rtfEndGroup))
                       break;
               if ((cp = New (RTFColor)) == NULL)
               {
                       fprintf (stderr, "Can't allocate color entry\n");
                       exit (1);
               }
               cp->rtfCNum = cnum++;
               cp->rtfCRed = cp->rtfCGreen = cp->rtfCBlue = -1;
               cp->rtfNextColor = colorList;
               colorList = cp;
               for (;;)
               {
                       if (!RTFCheckCM (rtfControl, rtfColorName))
                               break;
                       switch (rtfMinor)
                       {
                       case rtfRed:    cp->rtfCRed = rtfParam; break;
                       case rtfGreen:  cp->rtfCGreen = rtfParam; break;
                       case rtfBlue:   cp->rtfCBlue = rtfParam; break;
                       }
                       RTFGetToken ();
               }
               if (!RTFCheckCM (rtfText, (int) ';'))
               {
                       fprintf (stderr, "Bad color table\n");
                       exit (1);
               }
       }
       RTFRouteToken ();       /* feed "}" back to router */
}


/*
       The "Normal" style definition doesn't contain any style number
       (why?), all others do.  Normal style is given style 0.
*/

static void ReadStyleSheet ()
{
RTFStyle        *sp;
RTFStyleElt     *sep, *sepLast;
char            buf[rtfBufSiz], *bp;

       for (;;)
       {
               (void) RTFGetToken ();
               if (RTFCheckCM (rtfGroup, rtfEndGroup))
                       break;
               if ((sp = New (RTFStyle)) == NULL)
               {
                       fprintf (stderr, "Can't allocate style entry\n");
                       exit (1);
               }
               sp->rtfSNum = -1;
               sp->rtfSBasedOn = rtfBasedOnNone;
               sp->rtfSNextPar = -1;
               sp->rtfSSEList = sepLast = NULL;
               sp->rtfNextStyle = styleList;
               sp->rtfExpanding = 0;
               styleList = sp;
               if (!RTFCheckCM (rtfGroup, rtfBeginGroup))
               {
                       fprintf (stderr, "Bad stylesheet-1\n");
                       exit (1);
               }
               while (RTFGetToken () == rtfControl)
               {
                       if (RTFCheckMM (rtfParAttr, rtfStyleNum))
                       {
                               sp->rtfSNum = rtfParam;
                               continue;
                       }
                       if (RTFCheckMM (rtfStyleAttr, rtfBasedOn))
                       {
                               sp->rtfSBasedOn = rtfParam;
                               continue;
                       }
                       if (RTFCheckMM (rtfStyleAttr, rtfNext))
                       {
                               sp->rtfSNextPar = rtfParam;
                               continue;
                       }
                       if ((sep = New (RTFStyleElt)) == NULL)
                       {
                               fprintf (stderr, "Can't allocate style element\n");
                               exit (1);
                       }
                       sep->rtfSEClass = rtfClass;
                       sep->rtfSEMajor = rtfMajor;
                       sep->rtfSEMinor = rtfMinor;
                       sep->rtfSEParam = rtfParam;
                       if ((sep->rtfSEText = RTFStrSave (rtfTextBuf)) == NULL)
                       {
                               fprintf (stderr, "Can't allocate style element\n");
                               exit (1);
                       }
                       if (sepLast == NULL)    /* first style element */
                               sp->rtfSSEList = sep;
                       else                    /* add to end */
                               sepLast->rtfNextSE = sep;
                       sep->rtfNextSE = NULL;
                       sepLast = sep;
               }
               if (sp->rtfSNextPar == -1)              /* \snext not given */
                       sp->rtfSNextPar = sp->rtfSNum;  /* next is itself */
               if (rtfClass != rtfText)
               {
                       fprintf (stderr, "Bad stylesheet-2\n");
                       exit (1);
               }
               bp = buf;
               while (rtfClass == rtfText)
               {
                       if (rtfMajor == ';')
                       {
                               (void) RTFGetToken ();
                               break;
                       }
                       *bp++ = rtfMajor;
                       (void) RTFGetToken ();
               }
               *bp = '\0';
               if (sp->rtfSNum < 0)    /* no style number was specified */
               {                       /* (only legal for Normal style) */
                       if (strcmp (buf, "Normal") != 0)
                       {
                               fprintf (stderr, "Bad stylesheet-3\n");
                               exit (1);
                       }
                       sp->rtfSNum = 0;
               }
               if ((sp->rtfSName = RTFStrSave (buf)) == NULL)
               {
                       fprintf (stderr, "Can't allocate style name\n");
                       exit (1);
               }
               if (!RTFCheckCM (rtfGroup, rtfEndGroup))
               {
                       fprintf (stderr, "Bad stylesheet-4\n");
                       exit (1);
               }
       }
       RTFRouteToken ();       /* feed "}" back to router */
}


static void ReadInfoGroup ()
{
       RTFSkipGroup ();
       RTFRouteToken ();       /* feed "}" back to router */
}


static void ReadPictGroup ()
{
       RTFSkipGroup ();
       RTFRouteToken ();       /* feed "}" back to router */
}


/* ---------------------------------------------------------------------- */

/*
       Routines to return pieces of stylesheet, or font or color tables
*/


RTFStyle *RTFGetStyle (num)
int     num;
{
RTFStyle        *s;

       if (num == -1)
               return (styleList);
       for (s = styleList; s != NULL; s = s->rtfNextStyle)
       {
               if (s->rtfSNum == num)
                       break;
       }
       return (s);             /* NULL if not found */
}


RTFFont *RTFGetFont (num)
int     num;
{
RTFFont *f;

       if (num == -1)
               return (fontList);
       for (f = fontList; f != NULL; f = f->rtfNextFont)
       {
               if (f->rtfFNum == num)
                       break;
       }
       return (f);             /* NULL if not found */
}


RTFColor *RTFGetColor (num)
int     num;
{
RTFColor        *c;

       if (num == -1)
               return (colorList);
       for (c = colorList; c != NULL; c = c->rtfNextColor)
       {
               if (c->rtfCNum == num)
                       break;
       }
       return (c);             /* NULL if not found */
}


/* ---------------------------------------------------------------------- */


/*
       Expand style n, if there is such a style.
*/

void RTFExpandStyle (n)
int     n;
{
RTFStyle        *s;
RTFStyleElt     *se;

       if (n == -1 || (s = RTFGetStyle (n)) == NULL)
               return;
       if (s->rtfExpanding != 0)
       {
               fprintf (stderr, "Style expansion loop, style %d\n", n);
               exit (1);
       }
       s->rtfExpanding = 1;    /* set expansion flag for loop detection */
       /*
               Expand "based-on" style.  This is done by synthesizing
               the token that the writer needs to see in order to trigger
               another style expansion, and feeding to token back through
               the router so the writer sees it.
       */
       RTFSetToken (rtfControl, rtfParAttr, rtfStyleNum, s->rtfSBasedOn, "\\s");
       RTFRouteToken ();
       /*
               Now route the tokens unique to this style
       */
       for (se = s->rtfSSEList; se != NULL; se = se->rtfNextSE)
       {
               rtfClass = se->rtfSEClass;
               rtfMajor = se->rtfSEMajor;
               rtfMinor = se->rtfSEMinor;
               rtfParam = se->rtfSEParam;
               strcpy (rtfTextBuf, se->rtfSEText);
               rtfTextLen = strlen (rtfTextBuf);
               RTFRouteToken ();
       }
       s->rtfExpanding = 0;    /* done - clear expansion flag */
}


/* ---------------------------------------------------------------------- */

/*
       Control symbol lookup routines
*/


typedef struct RTFKey   RTFKey;

struct RTFKey
{
       int     rtfKMajor;      /* major number */
       int     rtfKMinor;      /* minor number */
       char    *rtfKStr;       /* symbol name */
       int     rtfKHash;       /* symbol name hash value */
};

/*
       A minor number of -1 means the token has no minor number
       (all valid minor numbers are >= 0).
*/

static RTFKey   rtfKey[] =
{
       rtfSpecialChar, rtfCurHeadPict,         "chpict",       0,      /* ?? */

       rtfSpecialChar, rtfCurHeadDate,         "chdate",       0,
       rtfSpecialChar, rtfCurHeadTime,         "chtime",       0,
       rtfSpecialChar, rtfCurHeadPage,         "chpgn",        0,
       rtfSpecialChar, rtfCurFNote,            "chftn",        0,
       rtfSpecialChar, rtfCurAnnotRef,         "chatn",        0,
       rtfSpecialChar, rtfFNoteSep,            "chftnsep",     0,
       rtfSpecialChar, rtfFNoteCont,           "chftnsepc",    0,
       rtfSpecialChar, rtfFormula,             "|",            0,
       rtfSpecialChar, rtfNoBrkSpace,          "~",            0,
       rtfSpecialChar, rtfNoReqHyphen,         "-",            0,
       rtfSpecialChar, rtfNoBrkHyphen,         "_",            0,
       rtfSpecialChar, rtfCell,                "cell",         0,
       rtfSpecialChar, rtfRow,                 "row",          0,
       rtfSpecialChar, rtfPar,                 "par",          0,
       rtfSpecialChar, rtfPar,                 "\n",           0,
       rtfSpecialChar, rtfPar,                 "\r",           0,
       rtfSpecialChar, rtfSect,                "sect",         0,
       rtfSpecialChar, rtfPage,                "page",         0,
       rtfSpecialChar, rtfColumn,              "column",       0,
       rtfSpecialChar, rtfLine,                "line",         0,
       rtfSpecialChar, rtfTab,                 "tab",          0,
       rtfSpecialChar, rtfOptDest,             "*",            0,
       rtfSpecialChar, rtfIIntVersion,         "vern",         0,
       rtfSpecialChar, rtfICreateTime,         "creatim",      0,
       rtfSpecialChar, rtfIRevisionTime,       "revtim",       0,
       rtfSpecialChar, rtfIPrintTime,          "printim",      0,
       rtfSpecialChar, rtfIBackupTime,         "buptim",       0,
       rtfSpecialChar, rtfIEditTime,           "edmins",       0,
       rtfSpecialChar, rtfIYear,               "yr",           0,
       rtfSpecialChar, rtfIMonth,              "mo",           0,
       rtfSpecialChar, rtfIDay,                "dy",           0,
       rtfSpecialChar, rtfIHour,               "hr",           0,
       rtfSpecialChar, rtfIMinute,             "min",          0,
       rtfSpecialChar, rtfINPages,             "nofpages",     0,
       rtfSpecialChar, rtfINWords,             "nofwords",     0,
       rtfSpecialChar, rtfINChars,             "nofchars",     0,
       rtfSpecialChar, rtfIIntID,              "id",           0,

       rtfCharAttr,    rtfPlain,               "plain",        0,
       rtfCharAttr,    rtfBold,                "b",            0,
       rtfCharAttr,    rtfItalic,              "i",            0,
       rtfCharAttr,    rtfStrikeThru,          "strike",       0,
       rtfCharAttr,    rtfOutline,             "outl",         0,
       rtfCharAttr,    rtfShadow,              "shad",         0,
       rtfCharAttr,    rtfSmallCaps,           "scaps",        0,
       rtfCharAttr,    rtfAllCaps,             "caps",         0,
       rtfCharAttr,    rtfInvisible,           "v",            0,
       rtfCharAttr,    rtfFontNum,             "f",            0,
       rtfCharAttr,    rtfFontSize,            "fs",           0,
       rtfCharAttr,    rtfExpand,              "expnd",        0,
       rtfCharAttr,    rtfUnderline,           "ul",           0,
       rtfCharAttr,    rtfWUnderline,          "ulw",          0,
       rtfCharAttr,    rtfDUnderline,          "uld",          0,
       rtfCharAttr,    rtfDbUnderline,         "uldb",         0,
       rtfCharAttr,    rtfNoUnderline,         "ulnone",       0,
       rtfCharAttr,    rtfSuperScript,         "up",           0,
       rtfCharAttr,    rtfSubScript,           "dn",           0,
       rtfCharAttr,    rtfRevised,             "revised",      0,
       rtfCharAttr,    rtfForeColor,           "cf",           0,
       rtfCharAttr,    rtfBackColor,           "cb",           0,

       rtfParAttr,     rtfParDef,              "pard",         0,
       rtfParAttr,     rtfStyleNum,            "s",            0,
       rtfParAttr,     rtfQuadLeft,            "ql",           0,
       rtfParAttr,     rtfQuadRight,           "qr",           0,
       rtfParAttr,     rtfQuadJust,            "qj",           0,
       rtfParAttr,     rtfQuadCenter,          "qc",           0,
       rtfParAttr,     rtfFirstIndent,         "fi",           0,
       rtfParAttr,     rtfLeftIndent,          "li",           0,
       rtfParAttr,     rtfRightIndent,         "ri",           0,
       rtfParAttr,     rtfSpaceBefore,         "sb",           0,
       rtfParAttr,     rtfSpaceAfter,          "sa",           0,
       rtfParAttr,     rtfSpaceBetween,        "sl",           0,
       rtfParAttr,     rtfInTable,             "intbl",        0,
       rtfParAttr,     rtfKeep,                "keep",         0,
       rtfParAttr,     rtfKeepNext,            "keepn",        0,
       rtfParAttr,     rtfSideBySide,          "sbys",         0,
       rtfParAttr,     rtfPBBefore,            "pagebb",       0,
       rtfParAttr,     rtfNoLineNum,           "noline",       0,
       rtfParAttr,     rtfTabPos,              "tx",           0,
       rtfParAttr,     rtfTabRight,            "tqr",          0,
       rtfParAttr,     rtfTabCenter,           "tqc",          0,
       rtfParAttr,     rtfTabDecimal,          "tqdec",        0,
       rtfParAttr,     rtfTabBar,              "tb",           0,
       rtfParAttr,     rtfBorderTop,           "brdrt",        0,
       rtfParAttr,     rtfBorderBottom,        "brdrb",        0,
       rtfParAttr,     rtfBorderLeft,          "brdrl",        0,
       rtfParAttr,     rtfBorderRight,         "brdrr",        0,
       rtfParAttr,     rtfBorderBar,           "bar",          0,
       rtfParAttr,     rtfBorderBox,           "box",          0,
       rtfParAttr,     rtfBorderBetween,       "brdrbtw",      0,
       rtfParAttr,     rtfBorderSingle,        "brdrs",        0,
       rtfParAttr,     rtfBorderThick,         "brdrth",       0,
       rtfParAttr,     rtfBorderShadow,        "brdrsh",       0,
       rtfParAttr,     rtfBorderDouble,        "brdrdb",       0,
       rtfParAttr,     rtfBorderDot,           "brdrdot",      0,
       rtfParAttr,     rtfBorderHair,          "brdrhair",     0,
       rtfParAttr,     rtfLeaderDot,           "tldot",        0,
       rtfParAttr,     rtfLeaderHyphen,        "tlhyph",       0,
       rtfParAttr,     rtfLeaderUnder,         "tlul",         0,
       rtfParAttr,     rtfLeaderThick,         "tlth",         0,
       rtfParAttr,     rtfBorderSpace,         "brsp",         0,

       rtfSectAttr,    rtfSectDef,             "sectd",        0,
       /*rtfSectAttr,  rtfNoBreak,             "nobreak",      0,
       rtfSectAttr,    rtfColBreak,            "colbreak",     0,
       rtfSectAttr,    rtfPageBreak,           "pagebreak",    0,
       rtfSectAttr,    rtfEvenBreak,           "evenbreak",    0,
       rtfSectAttr,    rtfOddBreak,            "oddbreak",     0,*/
       rtfSectAttr,    rtfNoBreak,             "sbknone",      0,
       rtfSectAttr,    rtfColBreak,            "sbkcol",       0,
       rtfSectAttr,    rtfPageBreak,           "sbkpage",      0,
       rtfSectAttr,    rtfEvenBreak,           "sbkeven",      0,
       rtfSectAttr,    rtfOddBreak,            "sbkodd",       0,
       rtfSectAttr,    rtfPageCont,            "pgncont",      0,
       rtfSectAttr,    rtfPageStarts,          "pgnstarts",    0,
       rtfSectAttr,    rtfPageRestart,         "pgnrestart",   0,
       rtfSectAttr,    rtfPageDecimal,         "pgndec",       0,
       rtfSectAttr,    rtfPageURoman,          "pgnucrm",      0,
       rtfSectAttr,    rtfPageLRoman,          "pgnlcrm",      0,
       rtfSectAttr,    rtfPageULetter,         "pgnucltr",     0,
       rtfSectAttr,    rtfPageLLetter,         "pgnlcltr",     0,
       rtfSectAttr,    rtfPageNumLeft,         "pgnx",         0,
       rtfSectAttr,    rtfPageNumTop,          "pgny",         0,
       rtfSectAttr,    rtfHeaderY,             "headery",      0,
       rtfSectAttr,    rtfFooterY,             "footery",      0,
       rtfSectAttr,    rtfLineModulus,         "linemod",      0,
       rtfSectAttr,    rtfLineDist,            "linex",        0,
       rtfSectAttr,    rtfLineStarts,          "linestarts",   0,
       rtfSectAttr,    rtfLineRestart,         "linerestart",  0,
       rtfSectAttr,    rtfLineRestartPg,       "lineppage",    0,
       rtfSectAttr,    rtfLineCont,            "linecont",     0,
       rtfSectAttr,    rtfTopVAlign,           "vertalt",      0,
       rtfSectAttr,    rtfBottomVAlign,        "vertal",       0,
       rtfSectAttr,    rtfCenterVAlign,        "vertalc",      0,
       rtfSectAttr,    rtfJustVAlign,          "vertalj",      0,
       rtfSectAttr,    rtfColumns,             "cols",         0,
       rtfSectAttr,    rtfColumnSpace,         "colsx",        0,
       rtfSectAttr,    rtfColumnLine,          "linebetcol",   0,
       rtfSectAttr,    rtfENoteHere,           "endnhere",     0,
       rtfSectAttr,    rtfTitleSpecial,        "titlepg",      0,

       rtfDocAttr,     rtfPaperWidth,          "paperw",       0,
       rtfDocAttr,     rtfPaperHeight,         "paperh",       0,
       rtfDocAttr,     rtfLeftMargin,          "margl",        0,
       rtfDocAttr,     rtfRightMargin,         "margr",        0,
       rtfDocAttr,     rtfTopMargin,           "margt",        0,
       rtfDocAttr,     rtfBottomMargin,        "margb",        0,
       rtfDocAttr,     rtfFacingPage,          "facingp",      0,
       rtfDocAttr,     rtfGutterWid,           "gutter",       0,
       rtfDocAttr,     rtfDefTab,              "deftab",       0,
       rtfDocAttr,     rtfWidowCtrl,           "widowctrl",    0,
       rtfDocAttr,     rtfHyphHotZone,         "hyphhotz",     0,
       rtfDocAttr,     rtfFNoteEndSect,        "endnotes",     0,
       rtfDocAttr,     rtfFNoteEndDoc,         "enddoc",       0,
       rtfDocAttr,     rtfFNoteBottom,         "ftnbj",        0,
       rtfDocAttr,     rtfFNoteText,           "ftntj",        0,
       rtfDocAttr,     rtfFNoteStart,          "ftnstart",     0,
       rtfDocAttr,     rtfFNoteRestart,        "ftnrestart",   0,
       rtfDocAttr,     rtfPageStart,           "pgnstart",     0,
       rtfDocAttr,     rtfLineStart,           "linestart",    0,
       rtfDocAttr,     rtfLandscape,           "landscape",    0,
       rtfDocAttr,     rtfFracWidth,           "fracwidth",    0,
       rtfDocAttr,     rtfNextFile,            "nextfile",     0,
       rtfDocAttr,     rtfTemplate,            "template",     0,
       rtfDocAttr,     rtfMakeBackup,          "makeback",     0,
       rtfDocAttr,     rtfRTFDefault,          "defformat",    0,
       rtfDocAttr,     rtfRevisions,           "revisions",    0,
       rtfDocAttr,     rtfMirrorMargin,        "margmirror",   0,
       rtfDocAttr,     rtfRevDisplay,          "revprop",      0,
       rtfDocAttr,     rtfRevBar,              "revbar",       0,

       rtfStyleAttr,   rtfBasedOn,             "sbasedon",     0,
       rtfStyleAttr,   rtfNext,                "snext",        0,

       rtfPictAttr,    rtfMacQD,               "macpict",      0,
       rtfPictAttr,    rtfWinMetafile,         "wmetafile",    0,
       rtfPictAttr,    rtfWinBitmap,           "wbitmap",      0,
       rtfPictAttr,    rtfPicWid,              "picw",         0,
       rtfPictAttr,    rtfPicHt,               "pich",         0,
       rtfPictAttr,    rtfPicGoalWid,          "picwgoal",     0,
       rtfPictAttr,    rtfPicGoalWid,          "picwGoal",     0,
       rtfPictAttr,    rtfPicGoalHt,           "pichgoal",     0,
       rtfPictAttr,    rtfPicGoalHt,           "pichGoal",     0,
       rtfPictAttr,    rtfPicScaleX,           "picscalex",    0,
       rtfPictAttr,    rtfPicScaleY,           "picscaley",    0,
       rtfPictAttr,    rtfPicScaled,           "picscaled",    0,
       rtfPictAttr,    rtfPicCropTop,          "piccropt",     0,
       rtfPictAttr,    rtfPicCropBottom,       "piccropb",     0,
       rtfPictAttr,    rtfPicCropLeft,         "piccropl",     0,
       rtfPictAttr,    rtfPicCropRight,        "piccropr",     0,
       rtfPictAttr,    rtfPixelBits,           "wbmbitspixel", 0,
       rtfPictAttr,    rtfBitmapPlanes,        "wbmplanes",    0,
       rtfPictAttr,    rtfBitmapWid,           "wbmwidthbytes", 0,
       rtfPictAttr,    rtfPicBinary,           "bin",          0,

       rtfDestination, rtfPict,                "pict",         0,
       rtfDestination, rtfFootnote,            "footnote",     0,
       rtfDestination, rtfHeader,              "header",       0,
       rtfDestination, rtfHeaderLeft,          "headerl",      0,
       rtfDestination, rtfHeaderRight,         "headerr",      0,
       rtfDestination, rtfHeaderFirst,         "headerf",      0,
       rtfDestination, rtfFooter,              "footer",       0,
       rtfDestination, rtfFooterLeft,          "footerl",      0,
       rtfDestination, rtfFooterRight,         "footerr",      0,
       rtfDestination, rtfFooterFirst,         "footerf",      0,
       rtfDestination, rtfFNSep,               "ftnsep",       0,
       rtfDestination, rtfFNContSep,           "ftnsepc",      0,
       rtfDestination, rtfFNContNotice,        "ftncn",        0,
       rtfDestination, rtfInfo,                "info",         0,
       rtfDestination, rtfStyleSheet,          "stylesheet",   0,
       rtfDestination, rtfFontTbl,             "fonttbl",      0,
       rtfDestination, rtfColorTbl,            "colortbl",     0,
       rtfDestination, rtfAnnotation,          "annotation",   0,
       rtfDestination, rtfAnnotID,             "atnid",        0,
       rtfDestination, rtfField,               "field",        0,
       rtfDestination, rtfFieldInst,           "fldinst",      0,
       rtfDestination, rtfFieldResult,         "fldrslt",      0,
       rtfDestination, rtfIndex,               "xe",           0,
       rtfDestination, rtfIndexBold,           "bxe",          0,
       rtfDestination, rtfIndexItalic,         "ixe",          0,
       rtfDestination, rtfIndexText,           "txe",          0,
       rtfDestination, rtfIndexRange,          "rxe",          0,
       rtfDestination, rtfTOC,                 "tc",           0,
       rtfDestination, rtfBookmarkStart,       "bkmkstart",    0,
       rtfDestination, rtfBookmarkEnd,         "bkmkend",      0,
       rtfDestination, rtfITitle,              "title",        0,
       rtfDestination, rtfISubject,            "subject",      0,
       rtfDestination, rtfIAuthor,             "author",       0,
       rtfDestination, rtfIOperator,           "operator",     0,
       rtfDestination, rtfIKeywords,           "keywords",     0,
       rtfDestination, rtfIComment,            "comment",      0,
       rtfDestination, rtfIVersion,            "version",      0,
       rtfDestination, rtfIDoccomm,            "doccomm",      0,

       rtfTOCAttr,     rtfTOCType,             "tcf",          0,
       rtfTOCAttr,     rtfTOCLevel,            "tcl",          0,

       rtfFontFamily,  rtfFFNil,               "fnil",         0,
       rtfFontFamily,  rtfFFRoman,             "froman",       0,
       rtfFontFamily,  rtfFFSwiss,             "fswiss",       0,
       rtfFontFamily,  rtfFFModern,            "fmodern",      0,
       rtfFontFamily,  rtfFFScript,            "fscript",      0,
       rtfFontFamily,  rtfFFDecor,             "fdecor",       0,
       rtfFontFamily,  rtfFFTech,              "ftech",        0,

       rtfColorName,   rtfRed,                 "red",          0,
       rtfColorName,   rtfGreen,               "green",        0,
       rtfColorName,   rtfBlue,                "blue",         0,

       rtfCharSet,     rtfMacCharSet,          "mac",          0,
       rtfCharSet,     rtfAnsiCharSet,         "ansi",         0,
       rtfCharSet,     rtfPcCharSet,           "pc",           0,
       rtfCharSet,     rtfPcaCharSet,          "pca",          0,

       rtfTblAttr,     rtfCellBordBottom,      "clbrdrb",      0,
       rtfTblAttr,     rtfCellBordTop,         "clbrdrt",      0,
       rtfTblAttr,     rtfCellBordLeft,        "clbrdrl",      0,
       rtfTblAttr,     rtfCellBordRight,       "clbrdrr",      0,
       rtfTblAttr,     rtfRowDef,              "trowd",        0,
       rtfTblAttr,     rtfRowLeft,             "trql",         0,
       rtfTblAttr,     rtfRowRight,            "trqr",         0,
       rtfTblAttr,     rtfRowCenter,           "trqc",         0,
       rtfTblAttr,     rtfRowGapH,             "trgaph",       0,
       rtfTblAttr,     rtfRowHt,               "trrh",         0,
       rtfTblAttr,     rtfRowLeftEdge,         "trleft",       0,
       rtfTblAttr,     rtfCellPos,             "cellx",        0,
       rtfTblAttr,     rtfMergeRngFirst,       "clmgf",        0,
       rtfTblAttr,     rtfMergePrevious,       "clmrg",        0,

       rtfFieldAttr,   rtfFieldDirty,          "flddirty",     0,
       rtfFieldAttr,   rtfFieldEdited,         "fldedit",      0,
       rtfFieldAttr,   rtfFieldLocked,         "fldlock",      0,
       rtfFieldAttr,   rtfFieldPrivate,        "fldpriv",      0,

       rtfPosAttr,     rtfPosX,                "posx",         0,
       rtfPosAttr,     rtfPosXCenter,          "posxc",        0,
       rtfPosAttr,     rtfPosXInside,          "posxi",        0,
       rtfPosAttr,     rtfPosXLeft,            "posxl",        0,
       rtfPosAttr,     rtfPosXOutSide,         "posxo",        0,
       rtfPosAttr,     rtfPosXRight,           "posxr",        0,
       rtfPosAttr,     rtfPosY,                "posy",         0,
       rtfPosAttr,     rtfPosYInline,          "posyil",       0,
       rtfPosAttr,     rtfPosYTop,             "posyt",        0,
       rtfPosAttr,     rtfPosYCenter,          "posyc",        0,
       rtfPosAttr,     rtfPosYBottom,          "posyb",        0,
       rtfPosAttr,     rtfAbsWid,              "absw",         0,
       rtfPosAttr,     rtfTextDist,            "dxfrtext",     0,
       rtfPosAttr,     rtfRPosMargV,           "pvmrg",        0,
       rtfPosAttr,     rtfRPosPageV,           "pvpg",         0,
       rtfPosAttr,     rtfRPosMargH,           "phmrg",        0,
       rtfPosAttr,     rtfRPosPageH,           "phpg",         0,
       rtfPosAttr,     rtfRPosColH,            "phcol",        0,

       rtfVersion,     -1,                     "rtf",          0,
       rtfDefFont,     -1,                     "deff",         0,

       0,              -1,                     NULL,           0
};


/*
       Initialize lookup table hash values
*/

static void LookupInit ()
{
static int      inited = 0;
RTFKey  *rp;

       if (inited == 0)
       {
               for (rp = rtfKey; rp->rtfKStr != NULL; rp++)
                       rp->rtfKHash = Hash (rp->rtfKStr);
               ++inited;
       }
}


/*
       Determine major and minor number of control token.  If it's
       not found, the class turns into rtfUnknown.
*/

static void Lookup (s)
char    *s;
{
RTFKey  *rp;
int     hash;

       ++s;                    /* skip over the leading \ character */
       hash = Hash (s);
       for (rp = rtfKey; rp->rtfKStr != NULL; rp++)
       {
               if (hash == rp->rtfKHash && strcmp (s, rp->rtfKStr) == 0)
               {
                       rtfClass = rtfControl;
                       rtfMajor = rp->rtfKMajor;
                       rtfMinor = rp->rtfKMinor;
                       return;
               }
       }
       rtfClass = rtfUnknown;
}


/*
       Compute hash value of symbol
*/

static int Hash (s)
char    *s;
{
char    c;
int     val = 0;

       while ((c = *s++) != '\0')
               val += (int) c;
       return (val);
}


/* ---------------------------------------------------------------------- */

/*
       Memory allocation routines
*/


/*
       Return pointer to block of size bytes, or NULL if there's
       not enough memory available.
*/

char *RTFAlloc (size)
int     size;
{
       return (malloc (size));
}


/*
       Saves a string on the heap and returns a pointer to it.
*/


char *RTFStrSave (s)
char    *s;
{
char    *p;

       if ((p = RTFAlloc (strlen (s) + 1)) == NULL)
               return (NULL);
       return (strcpy (p, s));
}


void RTFFree (p)
char    *p;
{
       if (p != NULL)
               free (p);
}


/* ---------------------------------------------------------------------- */


/*
       Token comparison routines
*/

int RTFCheckCM (class, major)
int     class, major;
{
       return (rtfClass == class && rtfMajor == major);
}


int RTFCheckCMM (class, major, minor)
int     class, major, minor;
{
       return (rtfClass == class && rtfMajor == major && rtfMinor == minor);
}


int RTFCheckMM (major, minor)
int     major, minor;
{
       return (rtfMajor == major && rtfMinor == minor);
}