~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/riched20/reader.c

Version: ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.