Fix Xlib locking in create_desktop.
[wine] / dlls / richedit / reader.c
1 /*
2  * WINE RTF file reader
3  *
4  * Portions Copyright 2004 Mike McCormack for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 /*
22  * - Need to document error code meanings.
23  * - Need to do something with \* on destinations.
24  * - Make the parameter a long?
25  *
26  * reader.c - RTF file reader.  Release 1.10.
27  *
28  * ASCII 10 (\n) and 13 (\r) are ignored and silently discarded.
29  * Nulls are also discarded.
30  * (although the read hook will still get a look at them.)
31  *
32  * "\:" is not a ":", it's a control symbol.  But some versions of
33  * Word seem to write "\:" for ":".  This reader treats "\:" as a
34  * plain text ":"
35  *
36  * 19 Mar 93
37  * - Add hack to skip "{\*\keycode ... }" group in stylesheet.
38  * This is probably the wrong thing to do, but it's simple.
39  * 13 Jul 93
40  * - Add THINK C awareness to malloc() declaration.  Necessary so
41  * compiler knows the malloc argument is 4 bytes.  Ugh.
42  * 07 Sep 93
43  * - Text characters are mapped onto standard codes, which are placed
44  * in rtfMinor.
45  * - Eliminated use of index() function.
46  * 05 Mar 94
47  * - Added zillions of new symbols (those defined in RTF spec 1.2).
48  * 14 Mar 94
49  * - Public functions RTFMsg() and RTFPanic() now take variable arguments.
50  * This means RTFPanic() now is used in place of what was formerly the
51  * internal function Error().
52  * - 8-bit characters are now legal, so they're not converted to \'xx
53  * hex char representation now.
54  * 01 Apr 94
55  * - Added public variables rtfLineNum and rtfLinePos.
56  * - #include string.h or strings.h, avoiding strncmp() problem where
57  * last argument is treated as zero when prototype isn't available.
58  * 04 Apr 94
59  * - Treat style numbers 222 and 0 properly as "no style" and "normal".
60  *
61  * Author: Paul DuBois  dubois@primate.wisc.edu
62  *
63  * This software may be redistributed without restriction and used for
64  * any purpose whatsoever.
65  */
66
67 # include       <stdio.h>
68 # include       <ctype.h>
69 # include       <string.h>
70 # include       <stdarg.h>
71
72 #include "rtf.h"
73 /*
74  *  include hard coded charsets
75  */
76
77 #include "ansi_gen.h"
78 #include "ansi_sym.h"
79 #include "text_map.h"
80
81 #include <stdlib.h>
82
83 #include "windef.h"
84 #include "winbase.h"
85 #include "wine/debug.h"
86
87 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
88
89 extern HANDLE RICHED32_hHeap;
90
91 static int      _RTFGetChar(RTF_Info *);
92 static void     _RTFGetToken (RTF_Info *);
93 static void     _RTFGetToken2 (RTF_Info *);
94 static int      GetChar (RTF_Info *);
95 static void     ReadFontTbl (RTF_Info *);
96 static void     ReadColorTbl (RTF_Info *);
97 static void     ReadStyleSheet (RTF_Info *);
98 static void     ReadInfoGroup (RTF_Info *);
99 static void     ReadPictGroup (RTF_Info *);
100 static void     ReadObjGroup (RTF_Info *);
101 static void     LookupInit (void);
102 static void     Lookup (RTF_Info *, char *);
103 static int      Hash (char*);
104
105 static void     CharSetInit (RTF_Info *);
106 static void     ReadCharSetMaps (RTF_Info *);
107
108
109 /*
110  * This array is used to map standard character names onto their numeric codes.
111  * The position of the name within the array is the code.
112  * stdcharnames.h is generated in the ../h directory.
113  */
114
115 #include "stdcharnames.h"
116
117 int _RTFGetChar(RTF_Info *info)
118 {
119     int ch;
120
121     TRACE("\n");
122
123     if( info->dwInputSize <= info->dwInputUsed )
124     {
125         long count = 0;
126         info->editstream.pfnCallback(info->editstream.dwCookie, 
127                    info->InputBuffer, sizeof(info->InputBuffer), &count);
128         if(count == 0)
129             return EOF;
130         info->dwInputSize = count;
131         info->dwInputUsed = 0;
132     }
133     ch = info->InputBuffer[info->dwInputUsed++];
134     if( !ch ) return EOF;
135     return ch;
136 }
137
138 void RTFSetEditStream(RTF_Info *info, EDITSTREAM *es)
139 {
140     TRACE("\n");
141
142     info->editstream.dwCookie = es->dwCookie;
143     info->editstream.dwError  = es->dwError;
144     info->editstream.pfnCallback = es->pfnCallback;
145 }
146
147 /*
148  * Initialize the reader.  This may be called multiple times,
149  * to read multiple files.  The only thing not reset is the input
150  * stream; that must be done with RTFSetStream().
151  */
152
153 void RTFInit(RTF_Info *info)
154 {
155 int     i;
156 RTFColor        *cp;
157 RTFFont         *fp;
158 RTFStyle        *sp;
159 RTFStyleElt     *eltList, *ep;
160
161     TRACE("\n");
162
163         if (info->rtfTextBuf == (char *) NULL)  /* initialize the text buffers */
164         {
165                 info->rtfTextBuf = RTFAlloc (rtfBufSiz);
166                 info->pushedTextBuf = RTFAlloc (rtfBufSiz);
167                 if (info->rtfTextBuf == (char *) NULL
168                         || info->pushedTextBuf == (char *) NULL)
169                         RTFPanic (info,"Cannot allocate text buffers.");
170                 info->rtfTextBuf[0] = info->pushedTextBuf[0] = '\0';
171         }
172
173         RTFFree (info->inputName);
174         RTFFree (info->outputName);
175         info->inputName = info->outputName = (char *) NULL;
176
177         /* initialize lookup table */
178         LookupInit ();
179
180         for (i = 0; i < rtfMaxClass; i++)
181                 RTFSetClassCallback (info, i, (RTFFuncPtr) NULL);
182         for (i = 0; i < rtfMaxDestination; i++)
183                 RTFSetDestinationCallback (info, i, (RTFFuncPtr) NULL);
184
185         /* install built-in destination readers */
186         RTFSetDestinationCallback (info, rtfFontTbl, ReadFontTbl);
187         RTFSetDestinationCallback (info, rtfColorTbl, ReadColorTbl);
188         RTFSetDestinationCallback (info, rtfStyleSheet, ReadStyleSheet);
189         RTFSetDestinationCallback (info, rtfInfo, ReadInfoGroup);
190         RTFSetDestinationCallback (info, rtfPict, ReadPictGroup);
191         RTFSetDestinationCallback (info, rtfObject, ReadObjGroup);
192
193
194         RTFSetReadHook (info, (RTFFuncPtr) NULL);
195
196         /* dump old lists if necessary */
197
198         while (info->fontList != (RTFFont *) NULL)
199         {
200                 fp = info->fontList->rtfNextFont;
201                 RTFFree (info->fontList->rtfFName);
202                 RTFFree ((char *) info->fontList);
203                 info->fontList = fp;
204         }
205         while (info->colorList != (RTFColor *) NULL)
206         {
207                 cp = info->colorList->rtfNextColor;
208                 RTFFree ((char *) info->colorList);
209                 info->colorList = cp;
210         }
211         while (info->styleList != (RTFStyle *) NULL)
212         {
213                 sp = info->styleList->rtfNextStyle;
214                 eltList = info->styleList->rtfSSEList;
215                 while (eltList != (RTFStyleElt *) NULL)
216                 {
217                         ep = eltList->rtfNextSE;
218                         RTFFree (eltList->rtfSEText);
219                         RTFFree ((char *) eltList);
220                         eltList = ep;
221                 }
222                 RTFFree (info->styleList->rtfSName);
223                 RTFFree ((char *) info->styleList);
224                 info->styleList = sp;
225         }
226
227         info->rtfClass = -1;
228         info->pushedClass = -1;
229         info->pushedChar = EOF;
230
231         info->rtfLineNum = 0;
232         info->rtfLinePos = 0;
233         info->prevChar = EOF;
234         info->bumpLine = 0;
235
236         CharSetInit (info);
237         info->csTop = 0;
238 }
239
240 /*
241  * Set or get the input or output file name.  These are never guaranteed
242  * to be accurate, only insofar as the calling program makes them so.
243  */
244
245 void RTFSetInputName(RTF_Info *info, char *name)
246 {
247     TRACE("\n");
248
249         if ((info->inputName = RTFStrSave (name)) == (char *) NULL)
250                 RTFPanic (info,"RTFSetInputName: out of memory");
251 }
252
253
254 char *RTFGetInputName(RTF_Info *info)
255 {
256         return (info->inputName);
257 }
258
259
260 void RTFSetOutputName(RTF_Info *info, char *name)
261 {
262     TRACE("\n");
263
264         if ((info->outputName = RTFStrSave (name)) == (char *) NULL)
265                 RTFPanic (info, "RTFSetOutputName: out of memory");
266 }
267
268
269 char *RTFGetOutputName(RTF_Info *info)
270 {
271         return (info->outputName);
272 }
273
274
275
276 /* ---------------------------------------------------------------------- */
277
278 /*
279  * Callback table manipulation routines
280  */
281
282
283 /*
284  * Install or return a writer callback for a token class
285  */
286
287 void RTFSetClassCallback(RTF_Info *info, int class, RTFFuncPtr callback)
288 {
289         if (class >= 0 && class < rtfMaxClass)
290                 info->ccb[class] = callback;
291 }
292
293
294 RTFFuncPtr RTFGetClassCallback(RTF_Info *info, int class)
295 {
296         if (class >= 0 && class < rtfMaxClass)
297                 return (info->ccb[class]);
298         return ((RTFFuncPtr) NULL);
299 }
300
301
302 /*
303  * Install or return a writer callback for a destination type
304  */
305
306 void RTFSetDestinationCallback(RTF_Info *info, int dest, RTFFuncPtr callback)
307 {
308         if (dest >= 0 && dest < rtfMaxDestination)
309                 info->dcb[dest] = callback;
310 }
311
312
313 RTFFuncPtr RTFGetDestinationCallback(RTF_Info *info, int dest)
314 {
315         if (dest >= 0 && dest < rtfMaxDestination)
316                 return (info->dcb[dest]);
317         return ((RTFFuncPtr) NULL);
318 }
319
320
321 /* ---------------------------------------------------------------------- */
322
323 /*
324  * Token reading routines
325  */
326
327
328 /*
329  * Read the input stream, invoking the writer's callbacks
330  * where appropriate.
331  */
332
333 void RTFRead(RTF_Info *info)
334 {
335         while (RTFGetToken (info) != rtfEOF)
336                 RTFRouteToken (info);
337 }
338
339
340 /*
341  * Route a token.  If it's a destination for which a reader is
342  * installed, process the destination internally, otherwise
343  * pass the token to the writer's class callback.
344  */
345
346 void RTFRouteToken(RTF_Info *info)
347 {
348 RTFFuncPtr      p;
349
350     TRACE("\n");
351
352         if (info->rtfClass < 0 || info->rtfClass >= rtfMaxClass)        /* watchdog */
353         {
354                 RTFPanic (info,"Unknown class %d: %s (reader malfunction)",
355                                                         info->rtfClass, info->rtfTextBuf);
356         }
357         if (RTFCheckCM (info, rtfControl, rtfDestination))
358         {
359                 /* invoke destination-specific callback if there is one */
360                 if ((p = RTFGetDestinationCallback (info, info->rtfMinor))
361                                                         != (RTFFuncPtr) NULL)
362                 {
363                         (*p) (info);
364                         return;
365                 }
366         }
367         /* invoke class callback if there is one */
368         if ((p = RTFGetClassCallback (info, info->rtfClass)) != (RTFFuncPtr) NULL)
369                 (*p) (info);
370 }
371
372
373 /*
374  * Skip to the end of the current group.  When this returns,
375  * writers that maintain a state stack may want to call their
376  * state unstacker; global vars will still be set to the group's
377  * closing brace.
378  */
379
380 void RTFSkipGroup(RTF_Info *info)
381 {
382 int     level = 1;
383     TRACE("\n");
384
385         while (RTFGetToken (info) != rtfEOF)
386         {
387                 if (info->rtfClass == rtfGroup)
388                 {
389                         if (info->rtfMajor == rtfBeginGroup)
390                                 ++level;
391                         else if (info->rtfMajor == rtfEndGroup)
392                         {
393                                 if (--level < 1)
394                                         break;  /* end of initial group */
395                         }
396                 }
397         }
398 }
399
400
401 /*
402  * Read one token.  Call the read hook if there is one.  The
403  * token class is the return value.  Returns rtfEOF when there
404  * are no more tokens.
405  */
406
407 int RTFGetToken(RTF_Info *info)
408 {
409 RTFFuncPtr      p;
410     TRACE("\n");
411
412         for (;;)
413         {
414                 _RTFGetToken (info);
415                 if ((p = RTFGetReadHook (info)) != (RTFFuncPtr) NULL)
416                         (*p) (info);    /* give read hook a look at token */
417
418                 /* Silently discard newlines, carriage returns, nulls.  */
419                 if (!(info->rtfClass == rtfText && info->rtfFormat != SF_TEXT
420                         && (info->rtfMajor == '\r' || info->rtfMajor == '\n' || info->rtfMajor == '\0')))
421                         break;
422         }
423         return (info->rtfClass);
424 }
425
426
427 /*
428  * Install or return a token reader hook.
429  */
430
431 void RTFSetReadHook(RTF_Info *info, RTFFuncPtr f)
432 {
433         info->readHook = f;
434 }
435
436
437 RTFFuncPtr RTFGetReadHook(RTF_Info *info)
438 {
439         return (info->readHook);
440 }
441
442
443 void RTFUngetToken(RTF_Info *info)
444 {
445     TRACE("\n");
446
447         if (info->pushedClass >= 0)     /* there's already an ungotten token */
448                 RTFPanic (info,"cannot unget two tokens");
449         if (info->rtfClass < 0)
450                 RTFPanic (info,"no token to unget");
451         info->pushedClass = info->rtfClass;
452         info->pushedMajor = info->rtfMajor;
453         info->pushedMinor = info->rtfMinor;
454         info->pushedParam = info->rtfParam;
455         (void) strcpy (info->pushedTextBuf, info->rtfTextBuf);
456 }
457
458
459 int RTFPeekToken(RTF_Info *info)
460 {
461         _RTFGetToken (info);
462         RTFUngetToken (info);
463         return (info->rtfClass);
464 }
465
466
467 static void _RTFGetToken(RTF_Info *info)
468 {
469 RTFFont *fp;
470
471     TRACE("\n");
472
473         if (info->rtfFormat == SF_TEXT) {
474             info->rtfMajor = GetChar (info);
475             info->rtfMinor = rtfSC_nothing;
476             info->rtfParam = rtfNoParam;
477             info->rtfTextBuf[info->rtfTextLen = 0] = '\0';
478             if (info->rtfMajor == EOF)
479                 info->rtfClass = rtfEOF;
480             else
481                 info->rtfClass = rtfText;
482             return;
483         }
484
485         /* first check for pushed token from RTFUngetToken() */
486
487         if (info->pushedClass >= 0)
488         {
489                 info->rtfClass = info->pushedClass;
490                 info->rtfMajor = info->pushedMajor;
491                 info->rtfMinor = info->pushedMinor;
492                 info->rtfParam = info->pushedParam;
493                 (void) strcpy (info->rtfTextBuf, info->pushedTextBuf);
494                 info->rtfTextLen = strlen (info->rtfTextBuf);
495                 info->pushedClass = -1;
496                 return;
497         }
498
499         /*
500          * Beyond this point, no token is ever seen twice, which is
501          * important, e.g., for making sure no "}" pops the font stack twice.
502          */
503
504         _RTFGetToken2 (info);
505         if (info->rtfClass == rtfText)  /* map RTF char to standard code */
506                 info->rtfMinor = RTFMapChar (info, info->rtfMajor);
507
508         /*
509          * If auto-charset stuff is activated, see if anything needs doing,
510          * like reading the charset maps or switching between them.
511          */
512
513         if (info->autoCharSetFlags == 0)
514                 return;
515
516         if ((info->autoCharSetFlags & rtfReadCharSet)
517                 && RTFCheckCM (info, rtfControl, rtfCharSet))
518         {
519                 ReadCharSetMaps (info);
520         }
521         else if ((info->autoCharSetFlags & rtfSwitchCharSet)
522                 && RTFCheckCMM (info, rtfControl, rtfCharAttr, rtfFontNum))
523         {
524                 if ((fp = RTFGetFont (info, info->rtfParam)) != (RTFFont *) NULL)
525                 {
526                         if (strncmp (fp->rtfFName, "Symbol", 6) == 0)
527                                 info->curCharSet = rtfCSSymbol;
528                         else
529                                 info->curCharSet = rtfCSGeneral;
530                         RTFSetCharSet (info, info->curCharSet);
531                 }
532         }
533         else if ((info->autoCharSetFlags & rtfSwitchCharSet) && info->rtfClass == rtfGroup)
534         {
535                 switch (info->rtfMajor)
536                 {
537                 case rtfBeginGroup:
538                         if (info->csTop >= maxCSStack)
539                                 RTFPanic (info, "_RTFGetToken: stack overflow");
540                         info->csStack[info->csTop++] = info->curCharSet;
541                         break;
542                 case rtfEndGroup:
543                         /*
544                          * If stack top is 1 at this point, we are ending the
545                          * group started by the initial {, which ends the
546                          * RTF stream
547                          */
548                         if (info->csTop <= 0)
549                                 RTFPanic (info,"_RTFGetToken: stack underflow");
550                         else if (info->csTop == 1)
551                                 info->rtfClass = rtfEOF;
552                         else
553                         {
554                                 info->curCharSet = info->csStack[--info->csTop];
555                                 RTFSetCharSet (info, info->curCharSet);
556                         }
557                         break;
558                 }
559         }
560 }
561
562
563 /* this shouldn't be called anywhere but from _RTFGetToken() */
564
565 static void _RTFGetToken2(RTF_Info *info)
566 {
567 int     sign;
568 int     c;
569
570     TRACE("\n");
571
572         /* initialize token vars */
573
574         info->rtfClass = rtfUnknown;
575         info->rtfParam = rtfNoParam;
576         info->rtfTextBuf[info->rtfTextLen = 0] = '\0';
577
578         /* get first character, which may be a pushback from previous token */
579
580         if (info->pushedChar != EOF)
581         {
582                 c = info->pushedChar;
583                 info->rtfTextBuf[info->rtfTextLen++] = c;
584                 info->rtfTextBuf[info->rtfTextLen] = '\0';
585                 info->pushedChar = EOF;
586         }
587         else if ((c = GetChar (info)) == EOF)
588         {
589                 info->rtfClass = rtfEOF;
590                 return;
591         }
592
593         if (c == '{')
594         {
595                 info->rtfClass = rtfGroup;
596                 info->rtfMajor = rtfBeginGroup;
597                 return;
598         }
599         if (c == '}')
600         {
601                 info->rtfClass = rtfGroup;
602                 info->rtfMajor = rtfEndGroup;
603                 return;
604         }
605         if (c != '\\')
606         {
607                 /*
608                  * Two possibilities here:
609                  * 1) ASCII 9, effectively like \tab control symbol
610                  * 2) literal text char
611                  */
612                 if (c == '\t')                  /* ASCII 9 */
613                 {
614                         info->rtfClass = rtfControl;
615                         info->rtfMajor = rtfSpecialChar;
616                         info->rtfMinor = rtfTab;
617                 }
618                 else
619                 {
620                         info->rtfClass = rtfText;
621                         info->rtfMajor = c;
622                 }
623                 return;
624         }
625         if ((c = GetChar (info)) == EOF)
626         {
627                 /* early eof, whoops (class is rtfUnknown) */
628                 return;
629         }
630         if (!isalpha (c))
631         {
632                 /*
633                  * Three possibilities here:
634                  * 1) hex encoded text char, e.g., \'d5, \'d3
635                  * 2) special escaped text char, e.g., \{, \}
636                  * 3) control symbol, e.g., \_, \-, \|, \<10>
637                  */
638                 if (c == '\'')                          /* hex char */
639                 {
640                 int     c2;
641
642                         if ((c = GetChar (info)) != EOF && (c2 = GetChar (info)) != EOF)
643                         {
644                                 /* should do isxdigit check! */
645                                 info->rtfClass = rtfText;
646                                 info->rtfMajor = RTFCharToHex (c) * 16
647                                                 + RTFCharToHex (c2);
648                                 return;
649                         }
650                         /* early eof, whoops (class is rtfUnknown) */
651                         return;
652                 }
653
654                 /* escaped char */
655                 /*if (index (":{}\\", c) != (char *) NULL)*/ /* escaped char */
656                 if (c == ':' || c == '{' || c == '}' || c == '\\')
657                 {
658                         info->rtfClass = rtfText;
659                         info->rtfMajor = c;
660                         return;
661                 }
662
663                 /* control symbol */
664                 Lookup (info, info->rtfTextBuf);        /* sets class, major, minor */
665                 return;
666         }
667         /* control word */
668         while (isalpha (c))
669         {
670                 if ((c = GetChar (info)) == EOF)
671                         break;
672         }
673
674         /*
675          * At this point, the control word is all collected, so the
676          * major/minor numbers are determined before the parameter
677          * (if any) is scanned.  There will be one too many characters
678          * in the buffer, though, so fix up before and restore after
679          * looking up.
680          */
681
682         if (c != EOF)
683                 info->rtfTextBuf[info->rtfTextLen-1] = '\0';
684         Lookup (info, info->rtfTextBuf);        /* sets class, major, minor */
685         if (c != EOF)
686                 info->rtfTextBuf[info->rtfTextLen-1] = c;
687
688         /*
689          * Should be looking at first digit of parameter if there
690          * is one, unless it's negative.  In that case, next char
691          * is '-', so need to gobble next char, and remember sign.
692          */
693
694         sign = 1;
695         if (c == '-')
696         {
697                 sign = -1;
698                 c = GetChar (info);
699         }
700         if (c != EOF && isdigit (c))
701         {
702                 info->rtfParam = 0;
703                 while (isdigit (c))     /* gobble parameter */
704                 {
705                         info->rtfParam = info->rtfParam * 10 + c - '0';
706                         if ((c = GetChar (info)) == EOF)
707                                 break;
708                 }
709                 info->rtfParam *= sign;
710         }
711         /*
712          * If control symbol delimiter was a blank, gobble it.
713          * Otherwise the character is first char of next token, so
714          * push it back for next call.  In either case, delete the
715          * delimiter from the token buffer.
716          */
717         if (c != EOF)
718         {
719                 if (c != ' ')
720                         info->pushedChar = c;
721                 info->rtfTextBuf[--info->rtfTextLen] = '\0';
722         }
723 }
724
725
726 /*
727  * Read the next character from the input.  This handles setting the
728  * current line and position-within-line variables.  Those variable are
729  * set correctly whether lines end with CR, LF, or CRLF (the last being
730  * the tricky case).
731  *
732  * bumpLine indicates whether the line number should be incremented on
733  * the *next* input character.
734  */
735
736
737 static int GetChar(RTF_Info *info)
738 {
739 int     c;
740 int     oldBumpLine;
741
742     TRACE("\n");
743
744         if ((c = _RTFGetChar(info)) != EOF)
745         {
746                 info->rtfTextBuf[info->rtfTextLen++] = c;
747                 info->rtfTextBuf[info->rtfTextLen] = '\0';
748         }
749         if (info->prevChar == EOF)
750                 info->bumpLine = 1;
751         oldBumpLine = info->bumpLine;   /* non-zero if prev char was line ending */
752         info->bumpLine = 0;
753         if (c == '\r')
754                 info->bumpLine = 1;
755         else if (c == '\n')
756         {
757                 info->bumpLine = 1;
758                 if (info->prevChar == '\r')             /* oops, previous \r wasn't */
759                         oldBumpLine = 0;        /* really a line ending */
760         }
761         ++info->rtfLinePos;
762         if (oldBumpLine)        /* were we supposed to increment the */
763         {                       /* line count on this char? */
764                 ++info->rtfLineNum;
765                 info->rtfLinePos = 1;
766         }
767         info->prevChar = c;
768         return (c);
769 }
770
771
772 /*
773  * Synthesize a token by setting the global variables to the
774  * values supplied.  Typically this is followed with a call
775  * to RTFRouteToken().
776  *
777  * If a param value other than rtfNoParam is passed, it becomes
778  * part of the token text.
779  */
780
781 void RTFSetToken(RTF_Info *info, int class, int major, int minor, int param, char *text)
782 {
783     TRACE("\n");
784
785         info->rtfClass = class;
786         info->rtfMajor = major;
787         info->rtfMinor = minor;
788         info->rtfParam = param;
789         if (param == rtfNoParam)
790                 (void) strcpy (info->rtfTextBuf, text);
791         else
792                 sprintf (info->rtfTextBuf, "%s%d", text, param);
793         info->rtfTextLen = strlen (info->rtfTextBuf);
794 }
795
796
797 /* ---------------------------------------------------------------------- */
798
799 /*
800  * Routines to handle mapping of RTF character sets
801  * onto standard characters.
802  *
803  * RTFStdCharCode(name) given char name, produce numeric code
804  * RTFStdCharName(code) given char code, return name
805  * RTFMapChar(c)        map input (RTF) char code to std code
806  * RTFSetCharSet(id)    select given charset map
807  * RTFGetCharSet()      get current charset map
808  *
809  * See ../h/README for more information about charset names and codes.
810  */
811
812
813 /*
814  * Initialize charset stuff.
815  */
816
817 static void CharSetInit(RTF_Info *info)
818 {
819     TRACE("\n");
820
821         info->autoCharSetFlags = (rtfReadCharSet | rtfSwitchCharSet);
822         RTFFree (info->genCharSetFile);
823         info->genCharSetFile = (char *) NULL;
824         info->haveGenCharSet = 0;
825         RTFFree (info->symCharSetFile);
826         info->symCharSetFile = (char *) NULL;
827         info->haveSymCharSet = 0;
828         info->curCharSet = rtfCSGeneral;
829         info->curCharCode = info->genCharCode;
830 }
831
832
833 /*
834  * Specify the name of a file to be read when auto-charset-file reading is
835  * done.
836  */
837
838 void RTFSetCharSetMap (RTF_Info *info, char *name, int csId)
839 {
840     TRACE("\n");
841
842         if ((name = RTFStrSave (name)) == (char *) NULL)        /* make copy */
843                 RTFPanic (info,"RTFSetCharSetMap: out of memory");
844         switch (csId)
845         {
846         case rtfCSGeneral:
847                 RTFFree (info->genCharSetFile); /* free any previous value */
848                 info->genCharSetFile = name;
849                 break;
850         case rtfCSSymbol:
851                 RTFFree (info->symCharSetFile); /* free any previous value */
852                 info->symCharSetFile = name;
853                 break;
854         }
855 }
856
857
858 /*
859  * Do auto-charset-file reading.
860  * will always use the ansi charset no mater what the value
861  * of the rtfTextBuf is.
862  *
863  * TODO: add support for other charset in the future.
864  *
865  */
866
867 static void ReadCharSetMaps(RTF_Info *info)
868 {
869 char    buf[rtfBufSiz];
870
871     TRACE("\n");
872
873         if (info->genCharSetFile != (char *) NULL)
874                 (void) strcpy (buf, info->genCharSetFile);
875         else
876                 sprintf (buf, "%s-gen", &info->rtfTextBuf[1]);
877         if (RTFReadCharSetMap (info, rtfCSGeneral) == 0)
878                 RTFPanic (info,"ReadCharSetMaps: Cannot read charset map %s", buf);
879         if (info->symCharSetFile != (char *) NULL)
880             (void) strcpy (buf, info->symCharSetFile);
881         else
882                 sprintf (buf, "%s-sym", &info->rtfTextBuf[1]);
883         if (RTFReadCharSetMap (info, rtfCSSymbol) == 0)
884                 RTFPanic (info,"ReadCharSetMaps: Cannot read charset map %s", buf);
885 }
886
887
888
889 /*
890  * Convert a CaracterSetMap (caracter_name, caracter) into
891  * this form : array[caracter_ident] = caracter;
892  */
893
894 int RTFReadCharSetMap(RTF_Info *info, int csId)
895 {
896         int     *stdCodeArray;
897         unsigned int i;
898
899     TRACE("\n");
900
901         switch (csId)
902         {
903         default:
904                 return (0);     /* illegal charset id */
905         case rtfCSGeneral:
906
907                 info->haveGenCharSet = 1;
908                 stdCodeArray = info->genCharCode;
909                 for (i = 0; i < charSetSize; i++)
910                 {
911                     stdCodeArray[i] = rtfSC_nothing;
912                 }
913
914                 for ( i = 0 ; i< sizeof(ansi_gen)/(sizeof(int));i+=2)
915                 {
916                     stdCodeArray[ ansi_gen[i+1] ] = ansi_gen[i];
917                 }
918                 break;
919
920         case rtfCSSymbol:
921
922                 info->haveSymCharSet = 1;
923                 stdCodeArray = info->symCharCode;
924                 for (i = 0; i < charSetSize; i++)
925                 {
926                     stdCodeArray[i] = rtfSC_nothing;
927                 }
928
929                 for ( i = 0 ; i< sizeof(ansi_sym)/(sizeof(int));i+=2)
930                 {
931                     stdCodeArray[ ansi_sym[i+1] ] = ansi_sym[i];
932                 }
933                 break;
934         }
935
936         return (1);
937 }
938
939
940 /*
941  * Given a standard character name (a string), find its code (a number).
942  * Return -1 if name is unknown.
943  */
944
945 int RTFStdCharCode(RTF_Info *info, char *name)
946 {
947 int     i;
948
949     TRACE("\n");
950
951         for (i = 0; i < rtfSC_MaxChar; i++)
952         {
953                 if (strcmp (name, stdCharName[i]) == 0)
954                         return (i);
955         }
956         return (-1);
957 }
958
959
960 /*
961  * Given a standard character code (a number), find its name (a string).
962  * Return NULL if code is unknown.
963  */
964
965 char *RTFStdCharName(RTF_Info *info, int code)
966 {
967         if (code < 0 || code >= rtfSC_MaxChar)
968                 return ((char *) NULL);
969         return (stdCharName[code]);
970 }
971
972
973 /*
974  * Given an RTF input character code, find standard character code.
975  * The translator should read the appropriate charset maps when it finds a
976  * charset control.  However, the file might not contain one.  In this
977  * case, no map will be available.  When the first attempt is made to
978  * map a character under these circumstances, RTFMapChar() assumes ANSI
979  * and reads the map as necessary.
980  */
981
982 int RTFMapChar(RTF_Info *info, int c)
983 {
984     TRACE("\n");
985
986         switch (info->curCharSet)
987         {
988         case rtfCSGeneral:
989                 if (!info->haveGenCharSet)
990                 {
991                         if (RTFReadCharSetMap (info, rtfCSGeneral) == 0)
992                                 RTFPanic (info,"RTFMapChar: cannot read ansi-gen");
993                 }
994                 break;
995         case rtfCSSymbol:
996                 if (!info->haveSymCharSet)
997                 {
998                         if (RTFReadCharSetMap (info, rtfCSSymbol) == 0)
999                                 RTFPanic (info,"RTFMapChar: cannot read ansi-sym");
1000                 }
1001                 break;
1002         }
1003         if (c < 0 || c >= charSetSize)
1004                 return (rtfSC_nothing);
1005         return (info->curCharCode[c]);
1006 }
1007
1008
1009 /*
1010  * Set the current character set.  If csId is illegal, uses general charset.
1011  */
1012
1013 void RTFSetCharSet(RTF_Info *info, int csId)
1014 {
1015     TRACE("\n");
1016
1017         switch (csId)
1018         {
1019         default:                /* use general if csId unknown */
1020         case rtfCSGeneral:
1021                 info->curCharCode = info->genCharCode;
1022                 info->curCharSet = csId;
1023                 break;
1024         case rtfCSSymbol:
1025                 info->curCharCode = info->symCharCode;
1026                 info->curCharSet = csId;
1027                 break;
1028         }
1029 }
1030
1031
1032 int RTFGetCharSet(RTF_Info *info)
1033 {
1034         return (info->curCharSet);
1035 }
1036
1037
1038 /* ---------------------------------------------------------------------- */
1039
1040 /*
1041  * Special destination readers.  They gobble the destination so the
1042  * writer doesn't have to deal with them.  That's wrong for any
1043  * translator that wants to process any of these itself.  In that
1044  * case, these readers should be overridden by installing a different
1045  * destination callback.
1046  *
1047  * NOTE: The last token read by each of these reader will be the
1048  * destination's terminating '}', which will then be the current token.
1049  * That '}' token is passed to RTFRouteToken() - the writer has already
1050  * seen the '{' that began the destination group, and may have pushed a
1051  * state; it also needs to know at the end of the group that a state
1052  * should be popped.
1053  *
1054  * It's important that rtf.h and the control token lookup table list
1055  * as many symbols as possible, because these destination readers
1056  * unfortunately make strict assumptions about the input they expect,
1057  * and a token of class rtfUnknown will throw them off easily.
1058  */
1059
1060
1061 /*
1062  * Read { \fonttbl ... } destination.  Old font tables don't have
1063  * braces around each table entry; try to adjust for that.
1064  */
1065
1066 static void ReadFontTbl(RTF_Info *info)
1067 {
1068 RTFFont *fp = NULL;
1069 char    buf[rtfBufSiz], *bp;
1070 int     old = -1;
1071 char    *fn = "ReadFontTbl";
1072
1073     TRACE("\n");
1074
1075         for (;;)
1076         {
1077                 (void) RTFGetToken (info);
1078                 if (RTFCheckCM (info, rtfGroup, rtfEndGroup))
1079                         break;
1080                 if (old < 0)            /* first entry - determine tbl type */
1081                 {
1082                         if (RTFCheckCMM (info, rtfControl, rtfCharAttr, rtfFontNum))
1083                                 old = 1;        /* no brace */
1084                         else if (RTFCheckCM (info, rtfGroup, rtfBeginGroup))
1085                                 old = 0;        /* brace */
1086                         else                    /* can't tell! */
1087                                 RTFPanic (info, "%s: Cannot determine format", fn);
1088                 }
1089                 if (old == 0)           /* need to find "{" here */
1090                 {
1091                         if (!RTFCheckCM (info, rtfGroup, rtfBeginGroup))
1092                                 RTFPanic (info, "%s: missing \"{\"", fn);
1093                         (void) RTFGetToken (info);      /* yes, skip to next token */
1094                 }
1095                 if ((fp = New (RTFFont)) == (RTFFont *) NULL)
1096                         RTFPanic (info, "%s: cannot allocate font entry", fn);
1097
1098                 fp->rtfNextFont = info->fontList;
1099                 info->fontList = fp;
1100
1101                 fp->rtfFName = (char *) NULL;
1102                 fp->rtfFAltName = (char *) NULL;
1103                 fp->rtfFNum = -1;
1104                 fp->rtfFFamily = 0;
1105                 fp->rtfFCharSet = 0;
1106                 fp->rtfFPitch = 0;
1107                 fp->rtfFType = 0;
1108                 fp->rtfFCodePage = 0;
1109
1110                 while (info->rtfClass != rtfEOF
1111                        && !RTFCheckCM (info, rtfText, ';')
1112                        && !RTFCheckCM (info, rtfGroup, rtfEndGroup))
1113                 {
1114                         if (info->rtfClass == rtfControl)
1115                         {
1116                                 switch (info->rtfMajor)
1117                                 {
1118                                 default:
1119                                         /* ignore token but announce it */
1120                                         RTFMsg (info,"%s: unknown token \"%s\"\n",
1121                                                         fn, info->rtfTextBuf);
1122                                         break;
1123                                 case rtfFontFamily:
1124                                         fp->rtfFFamily = info->rtfMinor;
1125                                         break;
1126                                 case rtfCharAttr:
1127                                         switch (info->rtfMinor)
1128                                         {
1129                                         default:
1130                                                 break;  /* ignore unknown? */
1131                                         case rtfFontNum:
1132                                                 fp->rtfFNum = info->rtfParam;
1133                                                 break;
1134                                         }
1135                                         break;
1136                                 case rtfFontAttr:
1137                                         switch (info->rtfMinor)
1138                                         {
1139                                         default:
1140                                                 break;  /* ignore unknown? */
1141                                         case rtfFontCharSet:
1142                                                 fp->rtfFCharSet = info->rtfParam;
1143                                                 break;
1144                                         case rtfFontPitch:
1145                                                 fp->rtfFPitch = info->rtfParam;
1146                                                 break;
1147                                         case rtfFontCodePage:
1148                                                 fp->rtfFCodePage = info->rtfParam;
1149                                                 break;
1150                                         case rtfFTypeNil:
1151                                         case rtfFTypeTrueType:
1152                                                 fp->rtfFType = info->rtfParam;
1153                                                 break;
1154                                         }
1155                                         break;
1156                                 }
1157                         }
1158                         else if (RTFCheckCM (info, rtfGroup, rtfBeginGroup))    /* dest */
1159                         {
1160                                 RTFSkipGroup (info);    /* ignore for now */
1161                         }
1162                         else if (info->rtfClass == rtfText)     /* font name */
1163                         {
1164                                 bp = buf;
1165                                 while (info->rtfClass == rtfText
1166                                         && !RTFCheckCM (info, rtfText, ';'))
1167                                 {
1168                                         *bp++ = info->rtfMajor;
1169                                         (void) RTFGetToken (info);
1170                                 }
1171
1172                                 /* FIX: in some cases the <fontinfo> isn't finished with a semi-column */
1173                                 if(RTFCheckCM (info, rtfGroup, rtfEndGroup))
1174                                 {
1175                                   RTFUngetToken (info);
1176                                 }
1177                                 *bp = '\0';
1178                                 fp->rtfFName = RTFStrSave (buf);
1179                                 if (fp->rtfFName == (char *) NULL)
1180                                         RTFPanic (info, "%s: cannot allocate font name", fn);
1181                                 /* already have next token; don't read one */
1182                                 /* at bottom of loop */
1183                                 continue;
1184                         }
1185                         else
1186                         {
1187                                 /* ignore token but announce it */
1188                                 RTFMsg (info, "%s: unknown token \"%s\"\n",
1189                                                         fn,info->rtfTextBuf);
1190                         }
1191                         (void) RTFGetToken (info);
1192                 }
1193                 if (old == 0)   /* need to see "}" here */
1194                 {
1195                         (void) RTFGetToken (info);
1196                         if (!RTFCheckCM (info, rtfGroup, rtfEndGroup))
1197                                 RTFPanic (info, "%s: missing \"}\"", fn);
1198                 }
1199         }
1200         if (fp->rtfFNum == -1)
1201                 RTFPanic (info,"%s: missing font number", fn);
1202 /*
1203  * Could check other pieces of structure here, too, I suppose.
1204  */
1205         RTFRouteToken (info);   /* feed "}" back to router */
1206 }
1207
1208
1209 /*
1210  * The color table entries have color values of -1 if
1211  * the default color should be used for the entry (only
1212  * a semi-colon is given in the definition, no color values).
1213  * There will be a problem if a partial entry (1 or 2 but
1214  * not 3 color values) is given.  The possibility is ignored
1215  * here.
1216  */
1217
1218 static void ReadColorTbl(RTF_Info *info)
1219 {
1220 RTFColor        *cp;
1221 int             cnum = 0;
1222 char            *fn = "ReadColorTbl";
1223
1224     TRACE("\n");
1225
1226         for (;;)
1227         {
1228                 (void) RTFGetToken (info);
1229                 if (RTFCheckCM (info, rtfGroup, rtfEndGroup))
1230                         break;
1231                 if ((cp = New (RTFColor)) == (RTFColor *) NULL)
1232                         RTFPanic (info,"%s: cannot allocate color entry", fn);
1233                 cp->rtfCNum = cnum++;
1234                 cp->rtfCRed = cp->rtfCGreen = cp->rtfCBlue = -1;
1235                 cp->rtfNextColor = info->colorList;
1236                 info->colorList = cp;
1237                 while (RTFCheckCM (info, rtfControl, rtfColorName))
1238                 {
1239                         switch (info->rtfMinor)
1240                         {
1241                         case rtfRed:    cp->rtfCRed = info->rtfParam; break;
1242                         case rtfGreen:  cp->rtfCGreen = info->rtfParam; break;
1243                         case rtfBlue:   cp->rtfCBlue = info->rtfParam; break;
1244                         }
1245                         RTFGetToken (info);
1246                 }
1247                 if (!RTFCheckCM (info, rtfText, (int) ';'))
1248                         RTFPanic (info,"%s: malformed entry", fn);
1249         }
1250         RTFRouteToken (info);   /* feed "}" back to router */
1251 }
1252
1253
1254 /*
1255  * The "Normal" style definition doesn't contain any style number,
1256  * all others do.  Normal style is given style rtfNormalStyleNum.
1257  */
1258
1259 static void ReadStyleSheet(RTF_Info *info)
1260 {
1261 RTFStyle        *sp;
1262 RTFStyleElt     *sep, *sepLast;
1263 char            buf[rtfBufSiz], *bp;
1264 char            *fn = "ReadStyleSheet";
1265
1266     TRACE("\n");
1267
1268         for (;;)
1269         {
1270                 (void) RTFGetToken (info);
1271                 if (RTFCheckCM (info, rtfGroup, rtfEndGroup))
1272                         break;
1273                 if ((sp = New (RTFStyle)) == (RTFStyle *) NULL)
1274                         RTFPanic (info,"%s: cannot allocate stylesheet entry", fn);
1275                 sp->rtfSName = (char *) NULL;
1276                 sp->rtfSNum = -1;
1277                 sp->rtfSType = rtfParStyle;
1278                 sp->rtfSAdditive = 0;
1279                 sp->rtfSBasedOn = rtfNoStyleNum;
1280                 sp->rtfSNextPar = -1;
1281                 sp->rtfSSEList = sepLast = (RTFStyleElt *) NULL;
1282                 sp->rtfNextStyle = info->styleList;
1283                 sp->rtfExpanding = 0;
1284                 info->styleList = sp;
1285                 if (!RTFCheckCM (info, rtfGroup, rtfBeginGroup))
1286                         RTFPanic (info,"%s: missing \"{\"", fn);
1287                 for (;;)
1288                 {
1289                         (void) RTFGetToken (info);
1290                         if (info->rtfClass == rtfEOF
1291                                 || RTFCheckCM (info, rtfText, ';'))
1292                                 break;
1293                         if (info->rtfClass == rtfControl)
1294                         {
1295                                 if (RTFCheckMM (info, rtfSpecialChar, rtfOptDest))
1296                                         continue;       /* ignore "\*" */
1297                                 if (RTFCheckMM (info, rtfParAttr, rtfStyleNum))
1298                                 {
1299                                         sp->rtfSNum = info->rtfParam;
1300                                         sp->rtfSType = rtfParStyle;
1301                                         continue;
1302                                 }
1303                                 if (RTFCheckMM (info, rtfCharAttr, rtfCharStyleNum))
1304                                 {
1305                                         sp->rtfSNum = info->rtfParam;
1306                                         sp->rtfSType = rtfCharStyle;
1307                                         continue;
1308                                 }
1309                                 if (RTFCheckMM (info, rtfSectAttr, rtfSectStyleNum))
1310                                 {
1311                                         sp->rtfSNum = info->rtfParam;
1312                                         sp->rtfSType = rtfSectStyle;
1313                                         continue;
1314                                 }
1315                                 if (RTFCheckMM (info, rtfStyleAttr, rtfBasedOn))
1316                                 {
1317                                         sp->rtfSBasedOn = info->rtfParam;
1318                                         continue;
1319                                 }
1320                                 if (RTFCheckMM (info, rtfStyleAttr, rtfAdditive))
1321                                 {
1322                                         sp->rtfSAdditive = 1;
1323                                         continue;
1324                                 }
1325                                 if (RTFCheckMM (info, rtfStyleAttr, rtfNext))
1326                                 {
1327                                         sp->rtfSNextPar = info->rtfParam;
1328                                         continue;
1329                                 }
1330                                 if ((sep = New (RTFStyleElt)) == (RTFStyleElt *) NULL)
1331                                         RTFPanic (info,"%s: cannot allocate style element", fn);
1332                                 sep->rtfSEClass = info->rtfClass;
1333                                 sep->rtfSEMajor = info->rtfMajor;
1334                                 sep->rtfSEMinor = info->rtfMinor;
1335                                 sep->rtfSEParam = info->rtfParam;
1336                                 if ((sep->rtfSEText = RTFStrSave (info->rtfTextBuf))
1337                                                                 == (char *) NULL)
1338                                         RTFPanic (info,"%s: cannot allocate style element text", fn);
1339                                 if (sepLast == (RTFStyleElt *) NULL)
1340                                         sp->rtfSSEList = sep;   /* first element */
1341                                 else                            /* add to end */
1342                                         sepLast->rtfNextSE = sep;
1343                                 sep->rtfNextSE = (RTFStyleElt *) NULL;
1344                                 sepLast = sep;
1345                         }
1346                         else if (RTFCheckCM (info, rtfGroup, rtfBeginGroup))
1347                         {
1348                                 /*
1349                                  * This passes over "{\*\keycode ... }, among
1350                                  * other things. A temporary (perhaps) hack.
1351                                  */
1352                                 RTFSkipGroup (info);
1353                                 continue;
1354                         }
1355                         else if (info->rtfClass == rtfText)     /* style name */
1356                         {
1357                                 bp = buf;
1358                                 while (info->rtfClass == rtfText)
1359                                 {
1360                                         if (info->rtfMajor == ';')
1361                                         {
1362                                                 /* put back for "for" loop */
1363                                                 (void) RTFUngetToken (info);
1364                                                 break;
1365                                         }
1366                                         *bp++ = info->rtfMajor;
1367                                         (void) RTFGetToken (info);
1368                                 }
1369                                 *bp = '\0';
1370                                 if ((sp->rtfSName = RTFStrSave (buf)) == (char *) NULL)
1371                                         RTFPanic (info, "%s: cannot allocate style name", fn);
1372                         }
1373                         else            /* unrecognized */
1374                         {
1375                                 /* ignore token but announce it */
1376                                 RTFMsg (info, "%s: unknown token \"%s\"\n",
1377                                                         fn, info->rtfTextBuf);
1378                         }
1379                 }
1380                 (void) RTFGetToken (info);
1381                 if (!RTFCheckCM (info, rtfGroup, rtfEndGroup))
1382                         RTFPanic (info, "%s: missing \"}\"", fn);
1383
1384                 /*
1385                  * Check over the style structure.  A name is a must.
1386                  * If no style number was specified, check whether it's the
1387                  * Normal style (in which case it's given style number
1388                  * rtfNormalStyleNum).  Note that some "normal" style names
1389                  * just begin with "Normal" and can have other stuff following,
1390                  * e.g., "Normal,Times 10 point".  Ugh.
1391                  *
1392                  * Some German RTF writers use "Standard" instead of "Normal".
1393                  */
1394                 if (sp->rtfSName == (char *) NULL)
1395                         RTFPanic (info,"%s: missing style name", fn);
1396                 if (sp->rtfSNum < 0)
1397                 {
1398                         if (strncmp (buf, "Normal", 6) != 0
1399                                 && strncmp (buf, "Standard", 8) != 0)
1400                                 RTFPanic (info,"%s: missing style number", fn);
1401                         sp->rtfSNum = rtfNormalStyleNum;
1402                 }
1403                 if (sp->rtfSNextPar == -1)      /* if \snext not given, */
1404                         sp->rtfSNextPar = sp->rtfSNum;  /* next is itself */
1405         }
1406         RTFRouteToken (info);   /* feed "}" back to router */
1407 }
1408
1409
1410 static void ReadInfoGroup(RTF_Info *info)
1411 {
1412         RTFSkipGroup (info);
1413         RTFRouteToken (info);   /* feed "}" back to router */
1414 }
1415
1416
1417 static void ReadPictGroup(RTF_Info *info)
1418 {
1419         RTFSkipGroup (info);
1420         RTFRouteToken (info);   /* feed "}" back to router */
1421 }
1422
1423
1424 static void ReadObjGroup(RTF_Info *info)
1425 {
1426         RTFSkipGroup (info);
1427         RTFRouteToken (info);   /* feed "}" back to router */
1428 }
1429
1430
1431 /* ---------------------------------------------------------------------- */
1432
1433 /*
1434  * Routines to return pieces of stylesheet, or font or color tables.
1435  * References to style 0 are mapped onto the Normal style.
1436  */
1437
1438
1439 RTFStyle *RTFGetStyle(RTF_Info *info, int num)
1440 {
1441 RTFStyle        *s;
1442
1443         if (num == -1)
1444                 return (info->styleList);
1445         for (s = info->styleList; s != (RTFStyle *) NULL; s = s->rtfNextStyle)
1446         {
1447                 if (s->rtfSNum == num)
1448                         break;
1449         }
1450         return (s);             /* NULL if not found */
1451 }
1452
1453
1454 RTFFont *RTFGetFont(RTF_Info *info, int num)
1455 {
1456 RTFFont *f;
1457
1458         if (num == -1)
1459                 return (info->fontList);
1460         for (f = info->fontList; f != (RTFFont *) NULL; f = f->rtfNextFont)
1461         {
1462                 if (f->rtfFNum == num)
1463                         break;
1464         }
1465         return (f);             /* NULL if not found */
1466 }
1467
1468
1469 RTFColor *RTFGetColor(RTF_Info *info, int num)
1470 {
1471 RTFColor        *c;
1472
1473         if (num == -1)
1474                 return (info->colorList);
1475         for (c = info->colorList; c != (RTFColor *) NULL; c = c->rtfNextColor)
1476         {
1477                 if (c->rtfCNum == num)
1478                         break;
1479         }
1480         return (c);             /* NULL if not found */
1481 }
1482
1483
1484 /* ---------------------------------------------------------------------- */
1485
1486
1487 /*
1488  * Expand style n, if there is such a style.
1489  */
1490
1491 void RTFExpandStyle(RTF_Info *info, int n)
1492 {
1493 RTFStyle        *s;
1494 RTFStyleElt     *se;
1495
1496     TRACE("\n");
1497
1498         if (n == -1 || (s = RTFGetStyle (info, n)) == (RTFStyle *) NULL)
1499                 return;
1500         if (s->rtfExpanding != 0)
1501                 RTFPanic (info,"Style expansion loop, style %d", n);
1502         s->rtfExpanding = 1;    /* set expansion flag for loop detection */
1503         /*
1504          * Expand "based-on" style (unless it's the same as the current
1505          * style -- Normal style usually gives itself as its own based-on
1506          * style).  Based-on style expansion is done by synthesizing
1507          * the token that the writer needs to see in order to trigger
1508          * another style expansion, and feeding to token back through
1509          * the router so the writer sees it.
1510          */
1511         if (n != s->rtfSBasedOn)
1512         {
1513                 RTFSetToken (info, rtfControl, rtfParAttr, rtfStyleNum,
1514                                                         s->rtfSBasedOn, "\\s");
1515                 RTFRouteToken (info);
1516         }
1517         /*
1518          * Now route the tokens unique to this style.  RTFSetToken()
1519          * isn't used because it would add the param value to the end
1520          * of the token text, which already has it in.
1521          */
1522         for (se = s->rtfSSEList; se != (RTFStyleElt *) NULL; se = se->rtfNextSE)
1523         {
1524                 info->rtfClass = se->rtfSEClass;
1525                 info->rtfMajor = se->rtfSEMajor;
1526                 info->rtfMinor = se->rtfSEMinor;
1527                 info->rtfParam = se->rtfSEParam;
1528                 (void) strcpy (info->rtfTextBuf, se->rtfSEText);
1529                 info->rtfTextLen = strlen (info->rtfTextBuf);
1530                 RTFRouteToken (info);
1531         }
1532         s->rtfExpanding = 0;    /* done - clear expansion flag */
1533 }
1534
1535
1536 /* ---------------------------------------------------------------------- */
1537
1538 /*
1539  * Control symbol lookup routines
1540  */
1541
1542
1543 typedef struct RTFKey   RTFKey;
1544
1545 struct RTFKey
1546 {
1547         int     rtfKMajor;      /* major number */
1548         int     rtfKMinor;      /* minor number */
1549         char    *rtfKStr;       /* symbol name */
1550         int     rtfKHash;       /* symbol name hash value */
1551 };
1552
1553 /*
1554  * A minor number of -1 means the token has no minor number
1555  * (all valid minor numbers are >= 0).
1556  */
1557
1558 static RTFKey   rtfKey[] =
1559 {
1560         /*
1561          * Special characters
1562          */
1563
1564         { rtfSpecialChar,       rtfIIntVersion,         "vern",         0 },
1565         { rtfSpecialChar,       rtfICreateTime,         "creatim",      0 },
1566         { rtfSpecialChar,       rtfIRevisionTime,       "revtim",       0 },
1567         { rtfSpecialChar,       rtfIPrintTime,          "printim",      0 },
1568         { rtfSpecialChar,       rtfIBackupTime,         "buptim",       0 },
1569         { rtfSpecialChar,       rtfIEditTime,           "edmins",       0 },
1570         { rtfSpecialChar,       rtfIYear,               "yr",           0 },
1571         { rtfSpecialChar,       rtfIMonth,              "mo",           0 },
1572         { rtfSpecialChar,       rtfIDay,                "dy",           0 },
1573         { rtfSpecialChar,       rtfIHour,               "hr",           0 },
1574         { rtfSpecialChar,       rtfIMinute,             "min",          0 },
1575         { rtfSpecialChar,       rtfISecond,             "sec",          0 },
1576         { rtfSpecialChar,       rtfINPages,             "nofpages",     0 },
1577         { rtfSpecialChar,       rtfINWords,             "nofwords",     0 },
1578         { rtfSpecialChar,       rtfINChars,             "nofchars",     0 },
1579         { rtfSpecialChar,       rtfIIntID,              "id",           0 },
1580
1581         { rtfSpecialChar,       rtfCurHeadDate,         "chdate",       0 },
1582         { rtfSpecialChar,       rtfCurHeadDateLong,     "chdpl",        0 },
1583         { rtfSpecialChar,       rtfCurHeadDateAbbrev,   "chdpa",        0 },
1584         { rtfSpecialChar,       rtfCurHeadTime,         "chtime",       0 },
1585         { rtfSpecialChar,       rtfCurHeadPage,         "chpgn",        0 },
1586         { rtfSpecialChar,       rtfSectNum,             "sectnum",      0 },
1587         { rtfSpecialChar,       rtfCurFNote,            "chftn",        0 },
1588         { rtfSpecialChar,       rtfCurAnnotRef,         "chatn",        0 },
1589         { rtfSpecialChar,       rtfFNoteSep,            "chftnsep",     0 },
1590         { rtfSpecialChar,       rtfFNoteCont,           "chftnsepc",    0 },
1591         { rtfSpecialChar,       rtfCell,                "cell",         0 },
1592         { rtfSpecialChar,       rtfRow,                 "row",          0 },
1593         { rtfSpecialChar,       rtfPar,                 "par",          0 },
1594         /* newline and carriage return are synonyms for */
1595         /* \par when they are preceded by a \ character */
1596         { rtfSpecialChar,       rtfPar,                 "\n",           0 },
1597         { rtfSpecialChar,       rtfPar,                 "\r",           0 },
1598         { rtfSpecialChar,       rtfSect,                "sect",         0 },
1599         { rtfSpecialChar,       rtfPage,                "page",         0 },
1600         { rtfSpecialChar,       rtfColumn,              "column",       0 },
1601         { rtfSpecialChar,       rtfLine,                "line",         0 },
1602         { rtfSpecialChar,       rtfSoftPage,            "softpage",     0 },
1603         { rtfSpecialChar,       rtfSoftColumn,          "softcol",      0 },
1604         { rtfSpecialChar,       rtfSoftLine,            "softline",     0 },
1605         { rtfSpecialChar,       rtfSoftLineHt,          "softlheight",  0 },
1606         { rtfSpecialChar,       rtfTab,                 "tab",          0 },
1607         { rtfSpecialChar,       rtfEmDash,              "emdash",       0 },
1608         { rtfSpecialChar,       rtfEnDash,              "endash",       0 },
1609         { rtfSpecialChar,       rtfEmSpace,             "emspace",      0 },
1610         { rtfSpecialChar,       rtfEnSpace,             "enspace",      0 },
1611         { rtfSpecialChar,       rtfBullet,              "bullet",       0 },
1612         { rtfSpecialChar,       rtfLQuote,              "lquote",       0 },
1613         { rtfSpecialChar,       rtfRQuote,              "rquote",       0 },
1614         { rtfSpecialChar,       rtfLDblQuote,           "ldblquote",    0 },
1615         { rtfSpecialChar,       rtfRDblQuote,           "rdblquote",    0 },
1616         { rtfSpecialChar,       rtfFormula,             "|",            0 },
1617         { rtfSpecialChar,       rtfNoBrkSpace,          "~",            0 },
1618         { rtfSpecialChar,       rtfNoReqHyphen,         "-",            0 },
1619         { rtfSpecialChar,       rtfNoBrkHyphen,         "_",            0 },
1620         { rtfSpecialChar,       rtfOptDest,             "*",            0 },
1621         { rtfSpecialChar,       rtfLTRMark,             "ltrmark",      0 },
1622         { rtfSpecialChar,       rtfRTLMark,             "rtlmark",      0 },
1623         { rtfSpecialChar,       rtfNoWidthJoiner,       "zwj",          0 },
1624         { rtfSpecialChar,       rtfNoWidthNonJoiner,    "zwnj",         0 },
1625         /* is this valid? */
1626         { rtfSpecialChar,       rtfCurHeadPict,         "chpict",       0 },
1627
1628         /*
1629          * Character formatting attributes
1630          */
1631
1632         { rtfCharAttr,  rtfPlain,               "plain",        0 },
1633         { rtfCharAttr,  rtfBold,                "b",            0 },
1634         { rtfCharAttr,  rtfAllCaps,             "caps",         0 },
1635         { rtfCharAttr,  rtfDeleted,             "deleted",      0 },
1636         { rtfCharAttr,  rtfSubScript,           "dn",           0 },
1637         { rtfCharAttr,  rtfSubScrShrink,        "sub",          0 },
1638         { rtfCharAttr,  rtfNoSuperSub,          "nosupersub",   0 },
1639         { rtfCharAttr,  rtfExpand,              "expnd",        0 },
1640         { rtfCharAttr,  rtfExpandTwips,         "expndtw",      0 },
1641         { rtfCharAttr,  rtfKerning,             "kerning",      0 },
1642         { rtfCharAttr,  rtfFontNum,             "f",            0 },
1643         { rtfCharAttr,  rtfFontSize,            "fs",           0 },
1644         { rtfCharAttr,  rtfItalic,              "i",            0 },
1645         { rtfCharAttr,  rtfOutline,             "outl",         0 },
1646         { rtfCharAttr,  rtfRevised,             "revised",      0 },
1647         { rtfCharAttr,  rtfRevAuthor,           "revauth",      0 },
1648         { rtfCharAttr,  rtfRevDTTM,             "revdttm",      0 },
1649         { rtfCharAttr,  rtfSmallCaps,           "scaps",        0 },
1650         { rtfCharAttr,  rtfShadow,              "shad",         0 },
1651         { rtfCharAttr,  rtfStrikeThru,          "strike",       0 },
1652         { rtfCharAttr,  rtfUnderline,           "ul",           0 },
1653         { rtfCharAttr,  rtfDotUnderline,        "uld",          0 },
1654         { rtfCharAttr,  rtfDbUnderline,         "uldb",         0 },
1655         { rtfCharAttr,  rtfNoUnderline,         "ulnone",       0 },
1656         { rtfCharAttr,  rtfWordUnderline,       "ulw",          0 },
1657         { rtfCharAttr,  rtfSuperScript,         "up",           0 },
1658         { rtfCharAttr,  rtfSuperScrShrink,      "super",        0 },
1659         { rtfCharAttr,  rtfInvisible,           "v",            0 },
1660         { rtfCharAttr,  rtfForeColor,           "cf",           0 },
1661         { rtfCharAttr,  rtfBackColor,           "cb",           0 },
1662         { rtfCharAttr,  rtfRTLChar,             "rtlch",        0 },
1663         { rtfCharAttr,  rtfLTRChar,             "ltrch",        0 },
1664         { rtfCharAttr,  rtfCharStyleNum,        "cs",           0 },
1665         { rtfCharAttr,  rtfCharCharSet,         "cchs",         0 },
1666         { rtfCharAttr,  rtfLanguage,            "lang",         0 },
1667         /* this has disappeared from spec 1.2 */
1668         { rtfCharAttr,  rtfGray,                "gray",         0 },
1669
1670         /*
1671          * Paragraph formatting attributes
1672          */
1673
1674         { rtfParAttr,   rtfParDef,              "pard",         0 },
1675         { rtfParAttr,   rtfStyleNum,            "s",            0 },
1676         { rtfParAttr,   rtfHyphenate,           "hyphpar",      0 },
1677         { rtfParAttr,   rtfInTable,             "intbl",        0 },
1678         { rtfParAttr,   rtfKeep,                "keep",         0 },
1679         { rtfParAttr,   rtfNoWidowControl,      "nowidctlpar",  0 },
1680         { rtfParAttr,   rtfKeepNext,            "keepn",        0 },
1681         { rtfParAttr,   rtfOutlineLevel,        "level",        0 },
1682         { rtfParAttr,   rtfNoLineNum,           "noline",       0 },
1683         { rtfParAttr,   rtfPBBefore,            "pagebb",       0 },
1684         { rtfParAttr,   rtfSideBySide,          "sbys",         0 },
1685         { rtfParAttr,   rtfQuadLeft,            "ql",           0 },
1686         { rtfParAttr,   rtfQuadRight,           "qr",           0 },
1687         { rtfParAttr,   rtfQuadJust,            "qj",           0 },
1688         { rtfParAttr,   rtfQuadCenter,          "qc",           0 },
1689         { rtfParAttr,   rtfFirstIndent,         "fi",           0 },
1690         { rtfParAttr,   rtfLeftIndent,          "li",           0 },
1691         { rtfParAttr,   rtfRightIndent,         "ri",           0 },
1692         { rtfParAttr,   rtfSpaceBefore,         "sb",           0 },
1693         { rtfParAttr,   rtfSpaceAfter,          "sa",           0 },
1694         { rtfParAttr,   rtfSpaceBetween,        "sl",           0 },
1695         { rtfParAttr,   rtfSpaceMultiply,       "slmult",       0 },
1696
1697         { rtfParAttr,   rtfSubDocument,         "subdocument",  0 },
1698
1699         { rtfParAttr,   rtfRTLPar,              "rtlpar",       0 },
1700         { rtfParAttr,   rtfLTRPar,              "ltrpar",       0 },
1701
1702         { rtfParAttr,   rtfTabPos,              "tx",           0 },
1703         /*
1704          * FrameMaker writes \tql (to mean left-justified tab, apparently)
1705          * although it's not in the spec.  It's also redundant, since lj
1706          * tabs are the default.
1707          */
1708         { rtfParAttr,   rtfTabLeft,             "tql",          0 },
1709         { rtfParAttr,   rtfTabRight,            "tqr",          0 },
1710         { rtfParAttr,   rtfTabCenter,           "tqc",          0 },
1711         { rtfParAttr,   rtfTabDecimal,          "tqdec",        0 },
1712         { rtfParAttr,   rtfTabBar,              "tb",           0 },
1713         { rtfParAttr,   rtfLeaderDot,           "tldot",        0 },
1714         { rtfParAttr,   rtfLeaderHyphen,        "tlhyph",       0 },
1715         { rtfParAttr,   rtfLeaderUnder,         "tlul",         0 },
1716         { rtfParAttr,   rtfLeaderThick,         "tlth",         0 },
1717         { rtfParAttr,   rtfLeaderEqual,         "tleq",         0 },
1718
1719         { rtfParAttr,   rtfParLevel,            "pnlvl",        0 },
1720         { rtfParAttr,   rtfParBullet,           "pnlvlblt",     0 },
1721         { rtfParAttr,   rtfParSimple,           "pnlvlbody",    0 },
1722         { rtfParAttr,   rtfParNumCont,          "pnlvlcont",    0 },
1723         { rtfParAttr,   rtfParNumOnce,          "pnnumonce",    0 },
1724         { rtfParAttr,   rtfParNumAcross,        "pnacross",     0 },
1725         { rtfParAttr,   rtfParHangIndent,       "pnhang",       0 },
1726         { rtfParAttr,   rtfParNumRestart,       "pnrestart",    0 },
1727         { rtfParAttr,   rtfParNumCardinal,      "pncard",       0 },
1728         { rtfParAttr,   rtfParNumDecimal,       "pndec",        0 },
1729         { rtfParAttr,   rtfParNumULetter,       "pnucltr",      0 },
1730         { rtfParAttr,   rtfParNumURoman,        "pnucrm",       0 },
1731         { rtfParAttr,   rtfParNumLLetter,       "pnlcltr",      0 },
1732         { rtfParAttr,   rtfParNumLRoman,        "pnlcrm",       0 },
1733         { rtfParAttr,   rtfParNumOrdinal,       "pnord",        0 },
1734         { rtfParAttr,   rtfParNumOrdinalText,   "pnordt",       0 },
1735         { rtfParAttr,   rtfParNumBold,          "pnb",          0 },
1736         { rtfParAttr,   rtfParNumItalic,        "pni",          0 },
1737         { rtfParAttr,   rtfParNumAllCaps,       "pncaps",       0 },
1738         { rtfParAttr,   rtfParNumSmallCaps,     "pnscaps",      0 },
1739         { rtfParAttr,   rtfParNumUnder,         "pnul",         0 },
1740         { rtfParAttr,   rtfParNumDotUnder,      "pnuld",        0 },
1741         { rtfParAttr,   rtfParNumDbUnder,       "pnuldb",       0 },
1742         { rtfParAttr,   rtfParNumNoUnder,       "pnulnone",     0 },
1743         { rtfParAttr,   rtfParNumWordUnder,     "pnulw",        0 },
1744         { rtfParAttr,   rtfParNumStrikethru,    "pnstrike",     0 },
1745         { rtfParAttr,   rtfParNumForeColor,     "pncf",         0 },
1746         { rtfParAttr,   rtfParNumFont,          "pnf",          0 },
1747         { rtfParAttr,   rtfParNumFontSize,      "pnfs",         0 },
1748         { rtfParAttr,   rtfParNumIndent,        "pnindent",     0 },
1749         { rtfParAttr,   rtfParNumSpacing,       "pnsp",         0 },
1750         { rtfParAttr,   rtfParNumInclPrev,      "pnprev",       0 },
1751         { rtfParAttr,   rtfParNumCenter,        "pnqc",         0 },
1752         { rtfParAttr,   rtfParNumLeft,          "pnql",         0 },
1753         { rtfParAttr,   rtfParNumRight,         "pnqr",         0 },
1754         { rtfParAttr,   rtfParNumStartAt,       "pnstart",      0 },
1755
1756         { rtfParAttr,   rtfBorderTop,           "brdrt",        0 },
1757         { rtfParAttr,   rtfBorderBottom,        "brdrb",        0 },
1758         { rtfParAttr,   rtfBorderLeft,          "brdrl",        0 },
1759         { rtfParAttr,   rtfBorderRight,         "brdrr",        0 },
1760         { rtfParAttr,   rtfBorderBetween,       "brdrbtw",      0 },
1761         { rtfParAttr,   rtfBorderBar,           "brdrbar",      0 },
1762         { rtfParAttr,   rtfBorderBox,           "box",          0 },
1763         { rtfParAttr,   rtfBorderSingle,        "brdrs",        0 },
1764         { rtfParAttr,   rtfBorderThick,         "brdrth",       0 },
1765         { rtfParAttr,   rtfBorderShadow,        "brdrsh",       0 },
1766         { rtfParAttr,   rtfBorderDouble,        "brdrdb",       0 },
1767         { rtfParAttr,   rtfBorderDot,           "brdrdot",      0 },
1768         { rtfParAttr,   rtfBorderDot,           "brdrdash",     0 },
1769         { rtfParAttr,   rtfBorderHair,          "brdrhair",     0 },
1770         { rtfParAttr,   rtfBorderWidth,         "brdrw",        0 },
1771         { rtfParAttr,   rtfBorderColor,         "brdrcf",       0 },
1772         { rtfParAttr,   rtfBorderSpace,         "brsp",         0 },
1773
1774         { rtfParAttr,   rtfShading,             "shading",      0 },
1775         { rtfParAttr,   rtfBgPatH,              "bghoriz",      0 },
1776         { rtfParAttr,   rtfBgPatV,              "bgvert",       0 },
1777         { rtfParAttr,   rtfFwdDiagBgPat,        "bgfdiag",      0 },
1778         { rtfParAttr,   rtfBwdDiagBgPat,        "bgbdiag",      0 },
1779         { rtfParAttr,   rtfHatchBgPat,          "bgcross",      0 },
1780         { rtfParAttr,   rtfDiagHatchBgPat,      "bgdcross",     0 },
1781         { rtfParAttr,   rtfDarkBgPatH,          "bgdkhoriz",    0 },
1782         { rtfParAttr,   rtfDarkBgPatV,          "bgdkvert",     0 },
1783         { rtfParAttr,   rtfFwdDarkBgPat,        "bgdkfdiag",    0 },
1784         { rtfParAttr,   rtfBwdDarkBgPat,        "bgdkbdiag",    0 },
1785         { rtfParAttr,   rtfDarkHatchBgPat,      "bgdkcross",    0 },
1786         { rtfParAttr,   rtfDarkDiagHatchBgPat,  "bgdkdcross",   0 },
1787         { rtfParAttr,   rtfBgPatLineColor,      "cfpat",        0 },
1788         { rtfParAttr,   rtfBgPatColor,          "cbpat",        0 },
1789
1790         /*
1791          * Section formatting attributes
1792          */
1793
1794         { rtfSectAttr,  rtfSectDef,             "sectd",        0 },
1795         { rtfSectAttr,  rtfENoteHere,           "endnhere",     0 },
1796         { rtfSectAttr,  rtfPrtBinFirst,         "binfsxn",      0 },
1797         { rtfSectAttr,  rtfPrtBin,              "binsxn",       0 },
1798         { rtfSectAttr,  rtfSectStyleNum,        "ds",           0 },
1799
1800         { rtfSectAttr,  rtfNoBreak,             "sbknone",      0 },
1801         { rtfSectAttr,  rtfColBreak,            "sbkcol",       0 },
1802         { rtfSectAttr,  rtfPageBreak,           "sbkpage",      0 },
1803         { rtfSectAttr,  rtfEvenBreak,           "sbkeven",      0 },
1804         { rtfSectAttr,  rtfOddBreak,            "sbkodd",       0 },
1805
1806         { rtfSectAttr,  rtfColumns,             "cols",         0 },
1807         { rtfSectAttr,  rtfColumnSpace,         "colsx",        0 },
1808         { rtfSectAttr,  rtfColumnNumber,        "colno",        0 },
1809         { rtfSectAttr,  rtfColumnSpRight,       "colsr",        0 },
1810         { rtfSectAttr,  rtfColumnWidth,         "colw",         0 },
1811         { rtfSectAttr,  rtfColumnLine,          "linebetcol",   0 },
1812
1813         { rtfSectAttr,  rtfLineModulus,         "linemod",      0 },
1814         { rtfSectAttr,  rtfLineDist,            "linex",        0 },
1815         { rtfSectAttr,  rtfLineStarts,          "linestarts",   0 },
1816         { rtfSectAttr,  rtfLineRestart,         "linerestart",  0 },
1817         { rtfSectAttr,  rtfLineRestartPg,       "lineppage",    0 },
1818         { rtfSectAttr,  rtfLineCont,            "linecont",     0 },
1819
1820         { rtfSectAttr,  rtfSectPageWid,         "pgwsxn",       0 },
1821         { rtfSectAttr,  rtfSectPageHt,          "pghsxn",       0 },
1822         { rtfSectAttr,  rtfSectMarginLeft,      "marglsxn",     0 },
1823         { rtfSectAttr,  rtfSectMarginRight,     "margrsxn",     0 },
1824         { rtfSectAttr,  rtfSectMarginTop,       "margtsxn",     0 },
1825         { rtfSectAttr,  rtfSectMarginBottom,    "margbsxn",     0 },
1826         { rtfSectAttr,  rtfSectMarginGutter,    "guttersxn",    0 },
1827         { rtfSectAttr,  rtfSectLandscape,       "lndscpsxn",    0 },
1828         { rtfSectAttr,  rtfTitleSpecial,        "titlepg",      0 },
1829         { rtfSectAttr,  rtfHeaderY,             "headery",      0 },
1830         { rtfSectAttr,  rtfFooterY,             "footery",      0 },
1831
1832         { rtfSectAttr,  rtfPageStarts,          "pgnstarts",    0 },
1833         { rtfSectAttr,  rtfPageCont,            "pgncont",      0 },
1834         { rtfSectAttr,  rtfPageRestart,         "pgnrestart",   0 },
1835         { rtfSectAttr,  rtfPageNumRight,        "pgnx",         0 },
1836         { rtfSectAttr,  rtfPageNumTop,          "pgny",         0 },
1837         { rtfSectAttr,  rtfPageDecimal,         "pgndec",       0 },
1838         { rtfSectAttr,  rtfPageURoman,          "pgnucrm",      0 },
1839         { rtfSectAttr,  rtfPageLRoman,          "pgnlcrm",      0 },
1840         { rtfSectAttr,  rtfPageULetter,         "pgnucltr",     0 },
1841         { rtfSectAttr,  rtfPageLLetter,         "pgnlcltr",     0 },
1842         { rtfSectAttr,  rtfPageNumHyphSep,      "pgnhnsh",      0 },
1843         { rtfSectAttr,  rtfPageNumSpaceSep,     "pgnhnsp",      0 },
1844         { rtfSectAttr,  rtfPageNumColonSep,     "pgnhnsc",      0 },
1845         { rtfSectAttr,  rtfPageNumEmdashSep,    "pgnhnsm",      0 },
1846         { rtfSectAttr,  rtfPageNumEndashSep,    "pgnhnsn",      0 },
1847
1848         { rtfSectAttr,  rtfTopVAlign,           "vertalt",      0 },
1849         /* misspelled as "vertal" in specification 1.0 */
1850         { rtfSectAttr,  rtfBottomVAlign,        "vertalb",      0 },
1851         { rtfSectAttr,  rtfCenterVAlign,        "vertalc",      0 },
1852         { rtfSectAttr,  rtfJustVAlign,          "vertalj",      0 },
1853
1854         { rtfSectAttr,  rtfRTLSect,             "rtlsect",      0 },
1855         { rtfSectAttr,  rtfLTRSect,             "ltrsect",      0 },
1856
1857         /* I've seen these in an old spec, but not in real files... */
1858         /*rtfSectAttr,  rtfNoBreak,             "nobreak",      0,*/
1859         /*rtfSectAttr,  rtfColBreak,            "colbreak",     0,*/
1860         /*rtfSectAttr,  rtfPageBreak,           "pagebreak",    0,*/
1861         /*rtfSectAttr,  rtfEvenBreak,           "evenbreak",    0,*/
1862         /*rtfSectAttr,  rtfOddBreak,            "oddbreak",     0,*/
1863
1864         /*
1865          * Document formatting attributes
1866          */
1867
1868         { rtfDocAttr,   rtfDefTab,              "deftab",       0 },
1869         { rtfDocAttr,   rtfHyphHotZone,         "hyphhotz",     0 },
1870         { rtfDocAttr,   rtfHyphConsecLines,     "hyphconsec",   0 },
1871         { rtfDocAttr,   rtfHyphCaps,            "hyphcaps",     0 },
1872         { rtfDocAttr,   rtfHyphAuto,            "hyphauto",     0 },
1873         { rtfDocAttr,   rtfLineStart,           "linestart",    0 },
1874         { rtfDocAttr,   rtfFracWidth,           "fracwidth",    0 },
1875         /* \makeback was given in old version of spec, it's now */
1876         /* listed as \makebackup */
1877         { rtfDocAttr,   rtfMakeBackup,          "makeback",     0 },
1878         { rtfDocAttr,   rtfMakeBackup,          "makebackup",   0 },
1879         { rtfDocAttr,   rtfRTFDefault,          "defformat",    0 },
1880         { rtfDocAttr,   rtfPSOverlay,           "psover",       0 },
1881         { rtfDocAttr,   rtfDocTemplate,         "doctemp",      0 },
1882         { rtfDocAttr,   rtfDefLanguage,         "deflang",      0 },
1883
1884         { rtfDocAttr,   rtfFENoteType,          "fet",          0 },
1885         { rtfDocAttr,   rtfFNoteEndSect,        "endnotes",     0 },
1886         { rtfDocAttr,   rtfFNoteEndDoc,         "enddoc",       0 },
1887         { rtfDocAttr,   rtfFNoteText,           "ftntj",        0 },
1888         { rtfDocAttr,   rtfFNoteBottom,         "ftnbj",        0 },
1889         { rtfDocAttr,   rtfENoteEndSect,        "aendnotes",    0 },
1890         { rtfDocAttr,   rtfENoteEndDoc,         "aenddoc",      0 },
1891         { rtfDocAttr,   rtfENoteText,           "aftntj",       0 },
1892         { rtfDocAttr,   rtfENoteBottom,         "aftnbj",       0 },
1893         { rtfDocAttr,   rtfFNoteStart,          "ftnstart",     0 },
1894         { rtfDocAttr,   rtfENoteStart,          "aftnstart",    0 },
1895         { rtfDocAttr,   rtfFNoteRestartPage,    "ftnrstpg",     0 },
1896         { rtfDocAttr,   rtfFNoteRestart,        "ftnrestart",   0 },
1897         { rtfDocAttr,   rtfFNoteRestartCont,    "ftnrstcont",   0 },
1898         { rtfDocAttr,   rtfENoteRestart,        "aftnrestart",  0 },
1899         { rtfDocAttr,   rtfENoteRestartCont,    "aftnrstcont",  0 },
1900         { rtfDocAttr,   rtfFNoteNumArabic,      "ftnnar",       0 },
1901         { rtfDocAttr,   rtfFNoteNumLLetter,     "ftnnalc",      0 },
1902         { rtfDocAttr,   rtfFNoteNumULetter,     "ftnnauc",      0 },
1903         { rtfDocAttr,   rtfFNoteNumLRoman,      "ftnnrlc",      0 },
1904         { rtfDocAttr,   rtfFNoteNumURoman,      "ftnnruc",      0 },
1905         { rtfDocAttr,   rtfFNoteNumChicago,     "ftnnchi",      0 },
1906         { rtfDocAttr,   rtfENoteNumArabic,      "aftnnar",      0 },
1907         { rtfDocAttr,   rtfENoteNumLLetter,     "aftnnalc",     0 },
1908         { rtfDocAttr,   rtfENoteNumULetter,     "aftnnauc",     0 },
1909         { rtfDocAttr,   rtfENoteNumLRoman,      "aftnnrlc",     0 },
1910         { rtfDocAttr,   rtfENoteNumURoman,      "aftnnruc",     0 },
1911         { rtfDocAttr,   rtfENoteNumChicago,     "aftnnchi",     0 },
1912
1913         { rtfDocAttr,   rtfPaperWidth,          "paperw",       0 },
1914         { rtfDocAttr,   rtfPaperHeight,         "paperh",       0 },
1915         { rtfDocAttr,   rtfPaperSize,           "psz",          0 },
1916         { rtfDocAttr,   rtfLeftMargin,          "margl",        0 },
1917         { rtfDocAttr,   rtfRightMargin,         "margr",        0 },
1918         { rtfDocAttr,   rtfTopMargin,           "margt",        0 },
1919         { rtfDocAttr,   rtfBottomMargin,        "margb",        0 },
1920         { rtfDocAttr,   rtfFacingPage,          "facingp",      0 },
1921         { rtfDocAttr,   rtfGutterWid,           "gutter",       0 },
1922         { rtfDocAttr,   rtfMirrorMargin,        "margmirror",   0 },
1923         { rtfDocAttr,   rtfLandscape,           "landscape",    0 },
1924         { rtfDocAttr,   rtfPageStart,           "pgnstart",     0 },
1925         { rtfDocAttr,   rtfWidowCtrl,           "widowctrl",    0 },
1926
1927         { rtfDocAttr,   rtfLinkStyles,          "linkstyles",   0 },
1928
1929         { rtfDocAttr,   rtfNoAutoTabIndent,     "notabind",     0 },
1930         { rtfDocAttr,   rtfWrapSpaces,          "wraptrsp",     0 },
1931         { rtfDocAttr,   rtfPrintColorsBlack,    "prcolbl",      0 },
1932         { rtfDocAttr,   rtfNoExtraSpaceRL,      "noextrasprl",  0 },
1933         { rtfDocAttr,   rtfNoColumnBalance,     "nocolbal",     0 },
1934         { rtfDocAttr,   rtfCvtMailMergeQuote,   "cvmme",        0 },
1935         { rtfDocAttr,   rtfSuppressTopSpace,    "sprstsp",      0 },
1936         { rtfDocAttr,   rtfSuppressPreParSpace, "sprsspbf",     0 },
1937         { rtfDocAttr,   rtfCombineTblBorders,   "otblrul",      0 },
1938         { rtfDocAttr,   rtfTranspMetafiles,     "transmf",      0 },
1939         { rtfDocAttr,   rtfSwapBorders,         "swpbdr",       0 },
1940         { rtfDocAttr,   rtfShowHardBreaks,      "brkfrm",       0 },
1941
1942         { rtfDocAttr,   rtfFormProtected,       "formprot",     0 },
1943         { rtfDocAttr,   rtfAllProtected,        "allprot",      0 },
1944         { rtfDocAttr,   rtfFormShading,         "formshade",    0 },
1945         { rtfDocAttr,   rtfFormDisplay,         "formdisp",     0 },
1946         { rtfDocAttr,   rtfPrintData,           "printdata",    0 },
1947
1948         { rtfDocAttr,   rtfRevProtected,        "revprot",      0 },
1949         { rtfDocAttr,   rtfRevisions,           "revisions",    0 },
1950         { rtfDocAttr,   rtfRevDisplay,          "revprop",      0 },
1951         { rtfDocAttr,   rtfRevBar,              "revbar",       0 },
1952
1953         { rtfDocAttr,   rtfAnnotProtected,      "annotprot",    0 },
1954
1955         { rtfDocAttr,   rtfRTLDoc,              "rtldoc",       0 },
1956         { rtfDocAttr,   rtfLTRDoc,              "ltrdoc",       0 },
1957
1958         /*
1959          * Style attributes
1960          */
1961
1962         { rtfStyleAttr, rtfAdditive,            "additive",     0 },
1963         { rtfStyleAttr, rtfBasedOn,             "sbasedon",     0 },
1964         { rtfStyleAttr, rtfNext,                "snext",        0 },
1965
1966         /*
1967          * Picture attributes
1968          */
1969
1970         { rtfPictAttr,  rtfMacQD,               "macpict",      0 },
1971         { rtfPictAttr,  rtfPMMetafile,          "pmmetafile",   0 },
1972         { rtfPictAttr,  rtfWinMetafile,         "wmetafile",    0 },
1973         { rtfPictAttr,  rtfDevIndBitmap,        "dibitmap",     0 },
1974         { rtfPictAttr,  rtfWinBitmap,           "wbitmap",      0 },
1975         { rtfPictAttr,  rtfPixelBits,           "wbmbitspixel", 0 },
1976         { rtfPictAttr,  rtfBitmapPlanes,        "wbmplanes",    0 },
1977         { rtfPictAttr,  rtfBitmapWid,           "wbmwidthbytes", 0 },
1978
1979         { rtfPictAttr,  rtfPicWid,              "picw",         0 },
1980         { rtfPictAttr,  rtfPicHt,               "pich",         0 },
1981         { rtfPictAttr,  rtfPicGoalWid,          "picwgoal",     0 },
1982         { rtfPictAttr,  rtfPicGoalHt,           "pichgoal",     0 },
1983         /* these two aren't in the spec, but some writers emit them */
1984         { rtfPictAttr,  rtfPicGoalWid,          "picwGoal",     0 },
1985         { rtfPictAttr,  rtfPicGoalHt,           "pichGoal",     0 },
1986         { rtfPictAttr,  rtfPicScaleX,           "picscalex",    0 },
1987         { rtfPictAttr,  rtfPicScaleY,           "picscaley",    0 },
1988         { rtfPictAttr,  rtfPicScaled,           "picscaled",    0 },
1989         { rtfPictAttr,  rtfPicCropTop,          "piccropt",     0 },
1990         { rtfPictAttr,  rtfPicCropBottom,       "piccropb",     0 },
1991         { rtfPictAttr,  rtfPicCropLeft,         "piccropl",     0 },
1992         { rtfPictAttr,  rtfPicCropRight,        "piccropr",     0 },
1993
1994         { rtfPictAttr,  rtfPicMFHasBitmap,      "picbmp",       0 },
1995         { rtfPictAttr,  rtfPicMFBitsPerPixel,   "picbpp",       0 },
1996
1997         { rtfPictAttr,  rtfPicBinary,           "bin",          0 },
1998
1999         /*
2000          * NeXT graphic attributes
2001          */
2002
2003         { rtfNeXTGrAttr,        rtfNeXTGWidth,          "width",        0 },
2004         { rtfNeXTGrAttr,        rtfNeXTGHeight,         "height",       0 },
2005
2006         /*
2007          * Destinations
2008          */
2009
2010         { rtfDestination,       rtfFontTbl,             "fonttbl",      0 },
2011         { rtfDestination,       rtfFontAltName,         "falt",         0 },
2012         { rtfDestination,       rtfEmbeddedFont,        "fonteb",       0 },
2013         { rtfDestination,       rtfFontFile,            "fontfile",     0 },
2014         { rtfDestination,       rtfFileTbl,             "filetbl",      0 },
2015         { rtfDestination,       rtfFileInfo,            "file",         0 },
2016         { rtfDestination,       rtfColorTbl,            "colortbl",     0 },
2017         { rtfDestination,       rtfStyleSheet,          "stylesheet",   0 },
2018         { rtfDestination,       rtfKeyCode,             "keycode",      0 },
2019         { rtfDestination,       rtfRevisionTbl,         "revtbl",       0 },
2020         { rtfDestination,       rtfInfo,                "info",         0 },
2021         { rtfDestination,       rtfITitle,              "title",        0 },
2022         { rtfDestination,       rtfISubject,            "subject",      0 },
2023         { rtfDestination,       rtfIAuthor,             "author",       0 },
2024         { rtfDestination,       rtfIOperator,           "operator",     0 },
2025         { rtfDestination,       rtfIKeywords,           "keywords",     0 },
2026         { rtfDestination,       rtfIComment,            "comment",      0 },
2027         { rtfDestination,       rtfIVersion,            "version",      0 },
2028         { rtfDestination,       rtfIDoccomm,            "doccomm",      0 },
2029         /* \verscomm may not exist -- was seen in earlier spec version */
2030         { rtfDestination,       rtfIVerscomm,           "verscomm",     0 },
2031         { rtfDestination,       rtfNextFile,            "nextfile",     0 },
2032         { rtfDestination,       rtfTemplate,            "template",     0 },
2033         { rtfDestination,       rtfFNSep,               "ftnsep",       0 },
2034         { rtfDestination,       rtfFNContSep,           "ftnsepc",      0 },
2035         { rtfDestination,       rtfFNContNotice,        "ftncn",        0 },
2036         { rtfDestination,       rtfENSep,               "aftnsep",      0 },
2037         { rtfDestination,       rtfENContSep,           "aftnsepc",     0 },
2038         { rtfDestination,       rtfENContNotice,        "aftncn",       0 },
2039         { rtfDestination,       rtfPageNumLevel,        "pgnhn",        0 },
2040         { rtfDestination,       rtfParNumLevelStyle,    "pnseclvl",     0 },
2041         { rtfDestination,       rtfHeader,              "header",       0 },
2042         { rtfDestination,       rtfFooter,              "footer",       0 },
2043         { rtfDestination,       rtfHeaderLeft,          "headerl",      0 },
2044         { rtfDestination,       rtfHeaderRight,         "headerr",      0 },
2045         { rtfDestination,       rtfHeaderFirst,         "headerf",      0 },
2046         { rtfDestination,       rtfFooterLeft,          "footerl",      0 },
2047         { rtfDestination,       rtfFooterRight,         "footerr",      0 },
2048         { rtfDestination,       rtfFooterFirst,         "footerf",      0 },
2049         { rtfDestination,       rtfParNumText,          "pntext",       0 },
2050         { rtfDestination,       rtfParNumbering,        "pn",           0 },
2051         { rtfDestination,       rtfParNumTextAfter,     "pntexta",      0 },
2052         { rtfDestination,       rtfParNumTextBefore,    "pntextb",      0 },
2053         { rtfDestination,       rtfBookmarkStart,       "bkmkstart",    0 },
2054         { rtfDestination,       rtfBookmarkEnd,         "bkmkend",      0 },
2055         { rtfDestination,       rtfPict,                "pict",         0 },
2056         { rtfDestination,       rtfObject,              "object",       0 },
2057         { rtfDestination,       rtfObjClass,            "objclass",     0 },
2058         { rtfDestination,       rtfObjName,             "objname",      0 },
2059         { rtfObjAttr,   rtfObjTime,             "objtime",      0 },
2060         { rtfDestination,       rtfObjData,             "objdata",      0 },
2061         { rtfDestination,       rtfObjAlias,            "objalias",     0 },
2062         { rtfDestination,       rtfObjSection,          "objsect",      0 },
2063         /* objitem and objtopic aren't documented in the spec! */
2064         { rtfDestination,       rtfObjItem,             "objitem",      0 },
2065         { rtfDestination,       rtfObjTopic,            "objtopic",     0 },
2066         { rtfDestination,       rtfObjResult,           "result",       0 },
2067         { rtfDestination,       rtfDrawObject,          "do",           0 },
2068         { rtfDestination,       rtfFootnote,            "footnote",     0 },
2069         { rtfDestination,       rtfAnnotRefStart,       "atrfstart",    0 },
2070         { rtfDestination,       rtfAnnotRefEnd,         "atrfend",      0 },
2071         { rtfDestination,       rtfAnnotID,             "atnid",        0 },
2072         { rtfDestination,       rtfAnnotAuthor,         "atnauthor",    0 },
2073         { rtfDestination,       rtfAnnotation,          "annotation",   0 },
2074         { rtfDestination,       rtfAnnotRef,            "atnref",       0 },
2075         { rtfDestination,       rtfAnnotTime,           "atntime",      0 },
2076         { rtfDestination,       rtfAnnotIcon,           "atnicn",       0 },
2077         { rtfDestination,       rtfField,               "field",        0 },
2078         { rtfDestination,       rtfFieldInst,           "fldinst",      0 },
2079         { rtfDestination,       rtfFieldResult,         "fldrslt",      0 },
2080         { rtfDestination,       rtfDataField,           "datafield",    0 },
2081         { rtfDestination,       rtfIndex,               "xe",           0 },
2082         { rtfDestination,       rtfIndexText,           "txe",          0 },
2083         { rtfDestination,       rtfIndexRange,          "rxe",          0 },
2084         { rtfDestination,       rtfTOC,                 "tc",           0 },
2085         { rtfDestination,       rtfNeXTGraphic,         "NeXTGraphic",  0 },
2086
2087         /*
2088          * Font families
2089          */
2090
2091         { rtfFontFamily,        rtfFFNil,               "fnil",         0 },
2092         { rtfFontFamily,        rtfFFRoman,             "froman",       0 },
2093         { rtfFontFamily,        rtfFFSwiss,             "fswiss",       0 },
2094         { rtfFontFamily,        rtfFFModern,            "fmodern",      0 },
2095         { rtfFontFamily,        rtfFFScript,            "fscript",      0 },
2096         { rtfFontFamily,        rtfFFDecor,             "fdecor",       0 },
2097         { rtfFontFamily,        rtfFFTech,              "ftech",        0 },
2098         { rtfFontFamily,        rtfFFBidirectional,     "fbidi",        0 },
2099
2100         /*
2101          * Font attributes
2102          */
2103
2104         { rtfFontAttr,  rtfFontCharSet,         "fcharset",     0 },
2105         { rtfFontAttr,  rtfFontPitch,           "fprq",         0 },
2106         { rtfFontAttr,  rtfFontCodePage,        "cpg",          0 },
2107         { rtfFontAttr,  rtfFTypeNil,            "ftnil",        0 },
2108         { rtfFontAttr,  rtfFTypeTrueType,       "fttruetype",   0 },
2109
2110         /*
2111          * File table attributes
2112          */
2113
2114         { rtfFileAttr,  rtfFileNum,             "fid",          0 },
2115         { rtfFileAttr,  rtfFileRelPath,         "frelative",    0 },
2116         { rtfFileAttr,  rtfFileOSNum,           "fosnum",       0 },
2117
2118         /*
2119          * File sources
2120          */
2121
2122         { rtfFileSource,        rtfSrcMacintosh,        "fvalidmac",    0 },
2123         { rtfFileSource,        rtfSrcDOS,              "fvaliddos",    0 },
2124         { rtfFileSource,        rtfSrcNTFS,             "fvalidntfs",   0 },
2125         { rtfFileSource,        rtfSrcHPFS,             "fvalidhpfs",   0 },
2126         { rtfFileSource,        rtfSrcNetwork,          "fnetwork",     0 },
2127
2128         /*
2129          * Color names
2130          */
2131
2132         { rtfColorName, rtfRed,                 "red",          0 },
2133         { rtfColorName, rtfGreen,               "green",        0 },
2134         { rtfColorName, rtfBlue,                "blue",         0 },
2135
2136         /*
2137          * Charset names
2138          */
2139
2140         { rtfCharSet,   rtfMacCharSet,          "mac",          0 },
2141         { rtfCharSet,   rtfAnsiCharSet,         "ansi",         0 },
2142         { rtfCharSet,   rtfPcCharSet,           "pc",           0 },
2143         { rtfCharSet,   rtfPcaCharSet,          "pca",          0 },
2144
2145         /*
2146          * Table attributes
2147          */
2148
2149         { rtfTblAttr,   rtfRowDef,              "trowd",        0 },
2150         { rtfTblAttr,   rtfRowGapH,             "trgaph",       0 },
2151         { rtfTblAttr,   rtfCellPos,             "cellx",        0 },
2152         { rtfTblAttr,   rtfMergeRngFirst,       "clmgf",        0 },
2153         { rtfTblAttr,   rtfMergePrevious,       "clmrg",        0 },
2154
2155         { rtfTblAttr,   rtfRowLeft,             "trql",         0 },
2156         { rtfTblAttr,   rtfRowRight,            "trqr",         0 },
2157         { rtfTblAttr,   rtfRowCenter,           "trqc",         0 },
2158         { rtfTblAttr,   rtfRowLeftEdge,         "trleft",       0 },
2159         { rtfTblAttr,   rtfRowHt,               "trrh",         0 },
2160         { rtfTblAttr,   rtfRowHeader,           "trhdr",        0 },
2161         { rtfTblAttr,   rtfRowKeep,             "trkeep",       0 },
2162
2163         { rtfTblAttr,   rtfRTLRow,              "rtlrow",       0 },
2164         { rtfTblAttr,   rtfLTRRow,              "ltrrow",       0 },
2165
2166         { rtfTblAttr,   rtfRowBordTop,          "trbrdrt",      0 },
2167         { rtfTblAttr,   rtfRowBordLeft,         "trbrdrl",      0 },
2168         { rtfTblAttr,   rtfRowBordBottom,       "trbrdrb",      0 },
2169         { rtfTblAttr,   rtfRowBordRight,        "trbrdrr",      0 },
2170         { rtfTblAttr,   rtfRowBordHoriz,        "trbrdrh",      0 },
2171         { rtfTblAttr,   rtfRowBordVert,         "trbrdrv",      0 },
2172
2173         { rtfTblAttr,   rtfCellBordBottom,      "clbrdrb",      0 },
2174         { rtfTblAttr,   rtfCellBordTop,         "clbrdrt",      0 },
2175         { rtfTblAttr,   rtfCellBordLeft,        "clbrdrl",      0 },
2176         { rtfTblAttr,   rtfCellBordRight,       "clbrdrr",      0 },
2177
2178         { rtfTblAttr,   rtfCellShading,         "clshdng",      0 },
2179         { rtfTblAttr,   rtfCellBgPatH,          "clbghoriz",    0 },
2180         { rtfTblAttr,   rtfCellBgPatV,          "clbgvert",     0 },
2181         { rtfTblAttr,   rtfCellFwdDiagBgPat,    "clbgfdiag",    0 },
2182         { rtfTblAttr,   rtfCellBwdDiagBgPat,    "clbgbdiag",    0 },
2183         { rtfTblAttr,   rtfCellHatchBgPat,      "clbgcross",    0 },
2184         { rtfTblAttr,   rtfCellDiagHatchBgPat,  "clbgdcross",   0 },
2185         /*
2186          * The spec lists "clbgdkhor", but the corresponding non-cell
2187          * control is "bgdkhoriz".  At any rate Macintosh Word seems
2188          * to accept both "clbgdkhor" and "clbgdkhoriz".
2189          */
2190         { rtfTblAttr,   rtfCellDarkBgPatH,      "clbgdkhoriz",  0 },
2191         { rtfTblAttr,   rtfCellDarkBgPatH,      "clbgdkhor",    0 },
2192         { rtfTblAttr,   rtfCellDarkBgPatV,      "clbgdkvert",   0 },
2193         { rtfTblAttr,   rtfCellFwdDarkBgPat,    "clbgdkfdiag",  0 },
2194         { rtfTblAttr,   rtfCellBwdDarkBgPat,    "clbgdkbdiag",  0 },
2195         { rtfTblAttr,   rtfCellDarkHatchBgPat,  "clbgdkcross",  0 },
2196         { rtfTblAttr,   rtfCellDarkDiagHatchBgPat, "clbgdkdcross",      0 },
2197         { rtfTblAttr,   rtfCellBgPatLineColor, "clcfpat",       0 },
2198         { rtfTblAttr,   rtfCellBgPatColor,      "clcbpat",      0 },
2199
2200         /*
2201          * Field attributes
2202          */
2203
2204         { rtfFieldAttr, rtfFieldDirty,          "flddirty",     0 },
2205         { rtfFieldAttr, rtfFieldEdited,         "fldedit",      0 },
2206         { rtfFieldAttr, rtfFieldLocked,         "fldlock",      0 },
2207         { rtfFieldAttr, rtfFieldPrivate,        "fldpriv",      0 },
2208         { rtfFieldAttr, rtfFieldAlt,            "fldalt",       0 },
2209
2210         /*
2211          * Positioning attributes
2212          */
2213
2214         { rtfPosAttr,   rtfAbsWid,              "absw",         0 },
2215         { rtfPosAttr,   rtfAbsHt,               "absh",         0 },
2216
2217         { rtfPosAttr,   rtfRPosMargH,           "phmrg",        0 },
2218         { rtfPosAttr,   rtfRPosPageH,           "phpg",         0 },
2219         { rtfPosAttr,   rtfRPosColH,            "phcol",        0 },
2220         { rtfPosAttr,   rtfPosX,                "posx",         0 },
2221         { rtfPosAttr,   rtfPosNegX,             "posnegx",      0 },
2222         { rtfPosAttr,   rtfPosXCenter,          "posxc",        0 },
2223         { rtfPosAttr,   rtfPosXInside,          "posxi",        0 },
2224         { rtfPosAttr,   rtfPosXOutSide,         "posxo",        0 },
2225         { rtfPosAttr,   rtfPosXRight,           "posxr",        0 },
2226         { rtfPosAttr,   rtfPosXLeft,            "posxl",        0 },
2227
2228         { rtfPosAttr,   rtfRPosMargV,           "pvmrg",        0 },
2229         { rtfPosAttr,   rtfRPosPageV,           "pvpg",         0 },
2230         { rtfPosAttr,   rtfRPosParaV,           "pvpara",       0 },
2231         { rtfPosAttr,   rtfPosY,                "posy",         0 },
2232         { rtfPosAttr,   rtfPosNegY,             "posnegy",      0 },
2233         { rtfPosAttr,   rtfPosYInline,          "posyil",       0 },
2234         { rtfPosAttr,   rtfPosYTop,             "posyt",        0 },
2235         { rtfPosAttr,   rtfPosYCenter,          "posyc",        0 },
2236         { rtfPosAttr,   rtfPosYBottom,          "posyb",        0 },
2237
2238         { rtfPosAttr,   rtfNoWrap,              "nowrap",       0 },
2239         { rtfPosAttr,   rtfDistFromTextAll,     "dxfrtext",     0 },
2240         { rtfPosAttr,   rtfDistFromTextX,       "dfrmtxtx",     0 },
2241         { rtfPosAttr,   rtfDistFromTextY,       "dfrmtxty",     0 },
2242         /* \dyfrtext no longer exists in spec 1.2, apparently */
2243         /* replaced by \dfrmtextx and \dfrmtexty. */
2244         { rtfPosAttr,   rtfTextDistY,           "dyfrtext",     0 },
2245
2246         { rtfPosAttr,   rtfDropCapLines,        "dropcapli",    0 },
2247         { rtfPosAttr,   rtfDropCapType,         "dropcapt",     0 },
2248
2249         /*
2250          * Object controls
2251          */
2252
2253         { rtfObjAttr,   rtfObjEmb,              "objemb",       0 },
2254         { rtfObjAttr,   rtfObjLink,             "objlink",      0 },
2255         { rtfObjAttr,   rtfObjAutoLink,         "objautlink",   0 },
2256         { rtfObjAttr,   rtfObjSubscriber,       "objsub",       0 },
2257         { rtfObjAttr,   rtfObjPublisher,        "objpub",       0 },
2258         { rtfObjAttr,   rtfObjICEmb,            "objicemb",     0 },
2259
2260         { rtfObjAttr,   rtfObjLinkSelf,         "linkself",     0 },
2261         { rtfObjAttr,   rtfObjLock,             "objupdate",    0 },
2262         { rtfObjAttr,   rtfObjUpdate,           "objlock",      0 },
2263
2264         { rtfObjAttr,   rtfObjHt,               "objh",         0 },
2265         { rtfObjAttr,   rtfObjWid,              "objw",         0 },
2266         { rtfObjAttr,   rtfObjSetSize,          "objsetsize",   0 },
2267         { rtfObjAttr,   rtfObjAlign,            "objalign",     0 },
2268         { rtfObjAttr,   rtfObjTransposeY,       "objtransy",    0 },
2269         { rtfObjAttr,   rtfObjCropTop,          "objcropt",     0 },
2270         { rtfObjAttr,   rtfObjCropBottom,       "objcropb",     0 },
2271         { rtfObjAttr,   rtfObjCropLeft,         "objcropl",     0 },
2272         { rtfObjAttr,   rtfObjCropRight,        "objcropr",     0 },
2273         { rtfObjAttr,   rtfObjScaleX,           "objscalex",    0 },
2274         { rtfObjAttr,   rtfObjScaleY,           "objscaley",    0 },
2275
2276         { rtfObjAttr,   rtfObjResRTF,           "rsltrtf",      0 },
2277         { rtfObjAttr,   rtfObjResPict,          "rsltpict",     0 },
2278         { rtfObjAttr,   rtfObjResBitmap,        "rsltbmp",      0 },
2279         { rtfObjAttr,   rtfObjResText,          "rslttxt",      0 },
2280         { rtfObjAttr,   rtfObjResMerge,         "rsltmerge",    0 },
2281
2282         { rtfObjAttr,   rtfObjBookmarkPubObj,   "bkmkpub",      0 },
2283         { rtfObjAttr,   rtfObjPubAutoUpdate,    "pubauto",      0 },
2284
2285         /*
2286          * Associated character formatting attributes
2287          */
2288
2289         { rtfACharAttr, rtfACBold,              "ab",           0 },
2290         { rtfACharAttr, rtfACAllCaps,           "caps",         0 },
2291         { rtfACharAttr, rtfACForeColor,         "acf",          0 },
2292         { rtfACharAttr, rtfACSubScript,         "adn",          0 },
2293         { rtfACharAttr, rtfACExpand,            "aexpnd",       0 },
2294         { rtfACharAttr, rtfACFontNum,           "af",           0 },
2295         { rtfACharAttr, rtfACFontSize,          "afs",          0 },
2296         { rtfACharAttr, rtfACItalic,            "ai",           0 },
2297         { rtfACharAttr, rtfACLanguage,          "alang",        0 },
2298         { rtfACharAttr, rtfACOutline,           "aoutl",        0 },
2299         { rtfACharAttr, rtfACSmallCaps,         "ascaps",       0 },
2300         { rtfACharAttr, rtfACShadow,            "ashad",        0 },
2301         { rtfACharAttr, rtfACStrikeThru,        "astrike",      0 },
2302         { rtfACharAttr, rtfACUnderline,         "aul",          0 },
2303         { rtfACharAttr, rtfACDotUnderline,      "auld",         0 },
2304         { rtfACharAttr, rtfACDbUnderline,       "auldb",        0 },
2305         { rtfACharAttr, rtfACNoUnderline,       "aulnone",      0 },
2306         { rtfACharAttr, rtfACWordUnderline,     "aulw",         0 },
2307         { rtfACharAttr, rtfACSuperScript,       "aup",          0 },
2308
2309         /*
2310          * Footnote attributes
2311          */
2312
2313         { rtfFNoteAttr, rtfFNAlt,               "ftnalt",       0 },
2314
2315         /*
2316          * Key code attributes
2317          */
2318
2319         { rtfKeyCodeAttr,       rtfAltKey,              "alt",          0 },
2320         { rtfKeyCodeAttr,       rtfShiftKey,            "shift",        0 },
2321         { rtfKeyCodeAttr,       rtfControlKey,          "ctrl",         0 },
2322         { rtfKeyCodeAttr,       rtfFunctionKey,         "fn",           0 },
2323
2324         /*
2325          * Bookmark attributes
2326          */
2327
2328         { rtfBookmarkAttr, rtfBookmarkFirstCol, "bkmkcolf",     0 },
2329         { rtfBookmarkAttr, rtfBookmarkLastCol,  "bkmkcoll",     0 },
2330
2331         /*
2332          * Index entry attributes
2333          */
2334
2335         { rtfIndexAttr, rtfIndexNumber,         "xef",          0 },
2336         { rtfIndexAttr, rtfIndexBold,           "bxe",          0 },
2337         { rtfIndexAttr, rtfIndexItalic,         "ixe",          0 },
2338
2339         /*
2340          * Table of contents attributes
2341          */
2342
2343         { rtfTOCAttr,   rtfTOCType,             "tcf",          0 },
2344         { rtfTOCAttr,   rtfTOCLevel,            "tcl",          0 },
2345
2346         /*
2347          * Drawing object attributes
2348          */
2349
2350         { rtfDrawAttr,  rtfDrawLock,            "dolock",       0 },
2351         { rtfDrawAttr,  rtfDrawPageRelX,        "doxpage",      0 },
2352         { rtfDrawAttr,  rtfDrawColumnRelX,      "dobxcolumn",   0 },
2353         { rtfDrawAttr,  rtfDrawMarginRelX,      "dobxmargin",   0 },
2354         { rtfDrawAttr,  rtfDrawPageRelY,        "dobypage",     0 },
2355         { rtfDrawAttr,  rtfDrawColumnRelY,      "dobycolumn",   0 },
2356         { rtfDrawAttr,  rtfDrawMarginRelY,      "dobymargin",   0 },
2357         { rtfDrawAttr,  rtfDrawHeight,          "dobhgt",       0 },
2358
2359         { rtfDrawAttr,  rtfDrawBeginGroup,      "dpgroup",      0 },
2360         { rtfDrawAttr,  rtfDrawGroupCount,      "dpcount",      0 },
2361         { rtfDrawAttr,  rtfDrawEndGroup,        "dpendgroup",   0 },
2362         { rtfDrawAttr,  rtfDrawArc,             "dparc",        0 },
2363         { rtfDrawAttr,  rtfDrawCallout,         "dpcallout",    0 },
2364         { rtfDrawAttr,  rtfDrawEllipse,         "dpellipse",    0 },
2365         { rtfDrawAttr,  rtfDrawLine,            "dpline",       0 },
2366         { rtfDrawAttr,  rtfDrawPolygon,         "dppolygon",    0 },
2367         { rtfDrawAttr,  rtfDrawPolyLine,        "dppolyline",   0 },
2368         { rtfDrawAttr,  rtfDrawRect,            "dprect",       0 },
2369         { rtfDrawAttr,  rtfDrawTextBox,         "dptxbx",       0 },
2370
2371         { rtfDrawAttr,  rtfDrawOffsetX,         "dpx",          0 },
2372         { rtfDrawAttr,  rtfDrawSizeX,           "dpxsize",      0 },
2373         { rtfDrawAttr,  rtfDrawOffsetY,         "dpy",          0 },
2374         { rtfDrawAttr,  rtfDrawSizeY,           "dpysize",      0 },
2375
2376         { rtfDrawAttr,  rtfCOAngle,             "dpcoa",        0 },
2377         { rtfDrawAttr,  rtfCOAccentBar,         "dpcoaccent",   0 },
2378         { rtfDrawAttr,  rtfCOBestFit,           "dpcobestfit",  0 },
2379         { rtfDrawAttr,  rtfCOBorder,            "dpcoborder",   0 },
2380         { rtfDrawAttr,  rtfCOAttachAbsDist,     "dpcodabs",     0 },
2381         { rtfDrawAttr,  rtfCOAttachBottom,      "dpcodbottom",  0 },
2382         { rtfDrawAttr,  rtfCOAttachCenter,      "dpcodcenter",  0 },
2383         { rtfDrawAttr,  rtfCOAttachTop,         "dpcodtop",     0 },
2384         { rtfDrawAttr,  rtfCOLength,            "dpcolength",   0 },
2385         { rtfDrawAttr,  rtfCONegXQuadrant,      "dpcominusx",   0 },
2386         { rtfDrawAttr,  rtfCONegYQuadrant,      "dpcominusy",   0 },
2387         { rtfDrawAttr,  rtfCOOffset,            "dpcooffset",   0 },
2388         { rtfDrawAttr,  rtfCOAttachSmart,       "dpcosmarta",   0 },
2389         { rtfDrawAttr,  rtfCODoubleLine,        "dpcotdouble",  0 },
2390         { rtfDrawAttr,  rtfCORightAngle,        "dpcotright",   0 },
2391         { rtfDrawAttr,  rtfCOSingleLine,        "dpcotsingle",  0 },
2392         { rtfDrawAttr,  rtfCOTripleLine,        "dpcottriple",  0 },
2393
2394         { rtfDrawAttr,  rtfDrawTextBoxMargin,   "dptxbxmar",    0 },
2395         { rtfDrawAttr,  rtfDrawTextBoxText,     "dptxbxtext",   0 },
2396         { rtfDrawAttr,  rtfDrawRoundRect,       "dproundr",     0 },
2397
2398         { rtfDrawAttr,  rtfDrawPointX,          "dpptx",        0 },
2399         { rtfDrawAttr,  rtfDrawPointY,          "dppty",        0 },
2400         { rtfDrawAttr,  rtfDrawPolyCount,       "dppolycount",  0 },
2401
2402         { rtfDrawAttr,  rtfDrawArcFlipX,        "dparcflipx",   0 },
2403         { rtfDrawAttr,  rtfDrawArcFlipY,        "dparcflipy",   0 },
2404
2405         { rtfDrawAttr,  rtfDrawLineBlue,        "dplinecob",    0 },
2406         { rtfDrawAttr,  rtfDrawLineGreen,       "dplinecog",    0 },
2407         { rtfDrawAttr,  rtfDrawLineRed,         "dplinecor",    0 },
2408         { rtfDrawAttr,  rtfDrawLinePalette,     "dplinepal",    0 },
2409         { rtfDrawAttr,  rtfDrawLineDashDot,     "dplinedado",   0 },
2410         { rtfDrawAttr,  rtfDrawLineDashDotDot,  "dplinedadodo", 0 },
2411         { rtfDrawAttr,  rtfDrawLineDash,        "dplinedash",   0 },
2412         { rtfDrawAttr,  rtfDrawLineDot,         "dplinedot",    0 },
2413         { rtfDrawAttr,  rtfDrawLineGray,        "dplinegray",   0 },
2414         { rtfDrawAttr,  rtfDrawLineHollow,      "dplinehollow", 0 },
2415         { rtfDrawAttr,  rtfDrawLineSolid,       "dplinesolid",  0 },
2416         { rtfDrawAttr,  rtfDrawLineWidth,       "dplinew",      0 },
2417
2418         { rtfDrawAttr,  rtfDrawHollowEndArrow,  "dpaendhol",    0 },
2419         { rtfDrawAttr,  rtfDrawEndArrowLength,  "dpaendl",      0 },
2420         { rtfDrawAttr,  rtfDrawSolidEndArrow,   "dpaendsol",    0 },
2421         { rtfDrawAttr,  rtfDrawEndArrowWidth,   "dpaendw",      0 },
2422         { rtfDrawAttr,  rtfDrawHollowStartArrow,"dpastarthol",  0 },
2423         { rtfDrawAttr,  rtfDrawStartArrowLength,"dpastartl",    0 },
2424         { rtfDrawAttr,  rtfDrawSolidStartArrow, "dpastartsol",  0 },
2425         { rtfDrawAttr,  rtfDrawStartArrowWidth, "dpastartw",    0 },
2426
2427         { rtfDrawAttr,  rtfDrawBgFillBlue,      "dpfillbgcb",   0 },
2428         { rtfDrawAttr,  rtfDrawBgFillGreen,     "dpfillbgcg",   0 },
2429         { rtfDrawAttr,  rtfDrawBgFillRed,       "dpfillbgcr",   0 },
2430         { rtfDrawAttr,  rtfDrawBgFillPalette,   "dpfillbgpal",  0 },
2431         { rtfDrawAttr,  rtfDrawBgFillGray,      "dpfillbggray", 0 },
2432         { rtfDrawAttr,  rtfDrawFgFillBlue,      "dpfillfgcb",   0 },
2433         { rtfDrawAttr,  rtfDrawFgFillGreen,     "dpfillfgcg",   0 },
2434         { rtfDrawAttr,  rtfDrawFgFillRed,       "dpfillfgcr",   0 },
2435         { rtfDrawAttr,  rtfDrawFgFillPalette,   "dpfillfgpal",  0 },
2436         { rtfDrawAttr,  rtfDrawFgFillGray,      "dpfillfggray", 0 },
2437         { rtfDrawAttr,  rtfDrawFillPatIndex,    "dpfillpat",    0 },
2438
2439         { rtfDrawAttr,  rtfDrawShadow,          "dpshadow",     0 },
2440         { rtfDrawAttr,  rtfDrawShadowXOffset,   "dpshadx",      0 },
2441         { rtfDrawAttr,  rtfDrawShadowYOffset,   "dpshady",      0 },
2442
2443         { rtfVersion,   -1,                     "rtf",          0 },
2444         { rtfDefFont,   -1,                     "deff",         0 },
2445
2446         { 0,            -1,                     (char *) NULL,  0 }
2447 };
2448
2449
2450 /*
2451  * Initialize lookup table hash values.  Only need to do this once.
2452  */
2453
2454 static void LookupInit(void)
2455 {
2456 static int      inited = 0;
2457 RTFKey  *rp;
2458
2459         if (inited == 0)
2460         {
2461                 for (rp = rtfKey; rp->rtfKStr != (char *) NULL; rp++)
2462                         rp->rtfKHash = Hash (rp->rtfKStr);
2463                 ++inited;
2464         }
2465 }
2466
2467
2468 /*
2469  * Determine major and minor number of control token.  If it's
2470  * not found, the class turns into rtfUnknown.
2471  */
2472
2473 static void Lookup(RTF_Info *info, char *s)
2474 {
2475 RTFKey  *rp;
2476 int     hash;
2477
2478         TRACE("\n");
2479         ++s;                    /* skip over the leading \ character */
2480         hash = Hash (s);
2481         for (rp = rtfKey; rp->rtfKStr != (char *) NULL; rp++)
2482         {
2483                 if (hash == rp->rtfKHash && strcmp (s, rp->rtfKStr) == 0)
2484                 {
2485                         info->rtfClass = rtfControl;
2486                         info->rtfMajor = rp->rtfKMajor;
2487                         info->rtfMinor = rp->rtfKMinor;
2488                         return;
2489                 }
2490         }
2491         info->rtfClass = rtfUnknown;
2492 }
2493
2494
2495 /*
2496  * Compute hash value of symbol
2497  */
2498
2499 static int Hash(char *s)
2500 {
2501 char    c;
2502 int     val = 0;
2503
2504         while ((c = *s++) != '\0')
2505                 val += (int) c;
2506         return (val);
2507 }
2508
2509
2510 /* ---------------------------------------------------------------------- */
2511
2512 /*
2513  * Memory allocation routines
2514  */
2515
2516
2517 /*
2518  * Return pointer to block of size bytes, or NULL if there's
2519  * not enough memory available.
2520  *
2521  * This is called through RTFAlloc(), a define which coerces the
2522  * argument to int.  This avoids the persistent problem of allocation
2523  * failing under THINK C when a long is passed.
2524  */
2525
2526 char *_RTFAlloc(int size)
2527 {
2528         return HeapAlloc(RICHED32_hHeap, 0, size);
2529 }
2530
2531
2532 /*
2533  * Saves a string on the heap and returns a pointer to it.
2534  */
2535
2536
2537 char *RTFStrSave(char *s)
2538 {
2539 char    *p;
2540
2541         if ((p = RTFAlloc ((int) (strlen (s) + 1))) == (char *) NULL)
2542                 return ((char *) NULL);
2543         return (strcpy (p, s));
2544 }
2545
2546
2547 void RTFFree(char *p)
2548 {
2549         HeapFree(RICHED32_hHeap, 0, p);
2550 }
2551
2552
2553 /* ---------------------------------------------------------------------- */
2554
2555
2556 /*
2557  * Token comparison routines
2558  */
2559
2560 int RTFCheckCM(RTF_Info *info, int class, int major)
2561 {
2562         return (info->rtfClass == class && info->rtfMajor == major);
2563 }
2564
2565
2566 int RTFCheckCMM(RTF_Info *info, int class, int major, int minor)
2567 {
2568         return (info->rtfClass == class && info->rtfMajor == major && info->rtfMinor == minor);
2569 }
2570
2571
2572 int RTFCheckMM(RTF_Info *info, int major, int minor)
2573 {
2574         return (info->rtfMajor == major && info->rtfMinor == minor);
2575 }
2576
2577
2578 /* ---------------------------------------------------------------------- */
2579
2580
2581 int RTFCharToHex(char c)
2582 {
2583         if (isupper (c))
2584                 c = tolower (c);
2585         if (isdigit (c))
2586                 return (c - '0');       /* '0'..'9' */
2587         return (c - 'a' + 10);          /* 'a'..'f' */
2588 }
2589
2590
2591 int RTFHexToChar(int i)
2592 {
2593         if (i < 10)
2594                 return (i + '0');
2595         return (i - 10 + 'a');
2596 }
2597
2598
2599 /* ---------------------------------------------------------------------- */
2600
2601 /*
2602  * RTFReadOutputMap() -- Read output translation map
2603  */
2604
2605 /*
2606  * Read in an array describing the relation between the standard character set
2607  * and an RTF translator's corresponding output sequences.  Each line consists
2608  * of a standard character name and the output sequence for that character.
2609  *
2610  * outMap is an array of strings into which the sequences should be placed.
2611  * It should be declared like this in the calling program:
2612  *
2613  *      char *outMap[rtfSC_MaxChar];
2614  *
2615  * reinit should be non-zero if outMap should be initialized
2616  * zero otherwise.
2617  *
2618  */
2619
2620 int RTFReadOutputMap(RTF_Info *info, char *outMap[], int reinit)
2621 {
2622     unsigned int  i;
2623     int  stdCode;
2624     char *name, *seq;
2625
2626     if (reinit)
2627     {
2628         for (i = 0; i < rtfSC_MaxChar; i++)
2629         {
2630             outMap[i] = (char *) NULL;
2631         }
2632     }
2633
2634     for (i=0 ;i< sizeof(text_map)/sizeof(char*); i+=2)
2635     {
2636         name = text_map[i];
2637         seq  = text_map[i+1];
2638         stdCode = RTFStdCharCode( info, name );
2639         outMap[stdCode] = seq;
2640     }
2641
2642     return (1);
2643 }
2644
2645 /* ---------------------------------------------------------------------- */
2646
2647 /*
2648  * Open a library file.
2649  */
2650
2651
2652 void RTFSetOpenLibFileProc(RTF_Info *info, FILE *(*proc)())
2653 {
2654     info->libFileOpen = proc;
2655 }
2656
2657
2658 FILE *RTFOpenLibFile (RTF_Info *info, char *file, char *mode)
2659 {
2660         if (info->libFileOpen == NULL)
2661                 return ((FILE *) NULL);
2662         return ((*info->libFileOpen) (file, mode));
2663 }
2664
2665
2666 /* ---------------------------------------------------------------------- */
2667
2668 /*
2669  * Print message.  Default is to send message to stderr
2670  * but this may be overridden with RTFSetMsgProc().
2671  *
2672  * Message should include linefeeds as necessary.  If the default
2673  * function is overridden, the overriding function may want to
2674  * map linefeeds to another line ending character or sequence if
2675  * the host system doesn't use linefeeds.
2676  */
2677
2678
2679 void RTFMsg (RTF_Info *info, char *fmt, ...)
2680 {
2681 char    buf[rtfBufSiz];
2682
2683         va_list args;
2684         va_start (args,fmt);
2685         vsprintf (buf, fmt, args);
2686         va_end (args);
2687         MESSAGE( "%s", buf);
2688 }
2689
2690
2691 /* ---------------------------------------------------------------------- */
2692
2693
2694 /*
2695  * Process termination.  Print error message and exit.  Also prints
2696  * current token, and current input line number and position within
2697  * line if any input has been read from the current file.  (No input
2698  * has been read if prevChar is EOF).
2699  */
2700
2701 static void DefaultPanicProc(RTF_Info *info, char *s)
2702 {
2703     MESSAGE( "%s", s);
2704         /*exit (1);*/
2705 }
2706
2707
2708
2709 void RTFPanic(RTF_Info *info, char *fmt, ...)
2710 {
2711 char    buf[rtfBufSiz];
2712
2713         va_list args;
2714         va_start (args,fmt);
2715         vsprintf (buf, fmt, args);
2716         va_end (args);
2717         (void) strcat (buf, "\n");
2718         if (info->prevChar != EOF && info->rtfTextBuf != (char *) NULL)
2719         {
2720                 sprintf (buf + strlen (buf),
2721                         "Last token read was \"%s\" near line %ld, position %d.\n",
2722                         info->rtfTextBuf, info->rtfLineNum, info->rtfLinePos);
2723         }
2724         DefaultPanicProc(info, buf);
2725 }