/*
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 */
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.
*/
/*
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.
*/
/*
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 */
}
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 */
}
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).
*/