Upstream version 10.38.222.0
[platform/framework/web/crosswalk.git] / src / third_party / liblouis / overrides / liblouis / compileTranslationTable.c
1 /* liblouis Braille Translation and Back-Translation 
2 Library
3
4    Based on the Linux screenreader BRLTTY, copyright (C) 1999-2006 by
5    The BRLTTY Team
6
7    Copyright (C) 2004, 2005, 2006
8    ViewPlus Technologies, Inc. www.viewplus.com
9    and
10    JJB Software, Inc. www.jjb-software.com
11    All rights reserved
12
13    This file is free software; you can redistribute it and/or modify it
14    under the terms of the Lesser or Library GNU General Public License 
15    as published by the
16    Free Software Foundation; either version 3, or (at your option) any
17    later version.
18
19    This file is distributed in the hope that it will be useful, but
20    WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
22    Library GNU General Public License for more details.
23
24    You should have received a copy of the Library GNU General Public 
25    License along with this program; see the file COPYING.  If not, write to
26    the Free Software Foundation, 51 Franklin Street, Fifth Floor,
27    Boston, MA 02110-1301, USA.
28
29    Maintained by John J. Boyer john.boyer@jjb-software.com
30    */
31
32 #include <stddef.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <stdarg.h>
36 #include <string.h>
37 #include <ctype.h>
38 //#include <unistd.h>
39
40 #include "louis.h"
41 #include "config.h"
42
43 #define QUOTESUB 28             /*Stand-in for double quotes in strings */
44
45
46 /* Contributed by Michel Such <michel.such@free.fr */
47 #ifdef _WIN32
48
49 /* Adapted from BRLTTY code (see sys_progs_wihdows.h) */
50
51 #include <shlobj.h>
52
53 static void
54 noMemory (void)
55 {
56   printf ("Insufficient memory: %s", strerror (errno), "\n");
57   exit (3);
58 }
59
60 static void *
61 reallocWrapper (void *address, size_t size)
62 {
63   if (!(address = realloc (address, size)) && size)
64     noMemory ();
65   return address;
66 }
67
68 static char *
69 strdupWrapper (const char *string)
70 {
71   char *address = strdup (string);
72   if (!address)
73     noMemory ();
74   return address;
75 }
76
77 char *EXPORT_CALL
78 lou_getProgramPath (void)
79 {
80   char *path = NULL;
81   HMODULE handle;
82
83   if ((handle = GetModuleHandle (NULL)))
84     {
85       size_t size = 0X80;
86       char *buffer = NULL;
87
88       while (1)
89         {
90           buffer = reallocWrapper (buffer, size <<= 1);
91
92           {
93             DWORD length = GetModuleFileName (handle, buffer, size);
94
95             if (!length)
96               {
97                 printf ("GetModuleFileName\n");
98                 exit (3);
99                 3;
100               }
101
102             if (length < size)
103               {
104                 buffer[length] = 0;
105                 path = strdupWrapper (buffer);
106
107                 while (length > 0)
108                   if (path[--length] == '\\')
109                     break;
110
111                 strncpy (path, path, length + 1);
112                 path[length + 1] = '\0';
113                 break;
114               }
115           }
116         }
117
118       free (buffer);
119     }
120   else
121     {
122       printf ("GetModuleHandle\n");
123       exit (3);
124     }
125
126   return path;
127 }
128
129 #define PATH_SEP ';'
130 #define DIR_SEP '\\'
131 #else
132 #define PATH_SEP ':'
133 #define DIR_SEP '/'
134 #endif
135 /* End of MS contribution */
136
137 #ifdef ANDROID
138 #include "android/log.h"
139 #endif
140
141 /* The folowing variables and functions make it possible to specify the 
142 * path on which all tables for liblouis and all files for liblouisutdml, 
143 * in their proper directories, will be found.
144 */
145
146 static char dataPath[MAXSTRING];
147 static char *dataPathPtr;
148
149 char *EXPORT_CALL
150 lou_setDataPath (char *path)
151 {
152   dataPathPtr = NULL;
153   if (path == NULL)
154     return NULL;
155   strcpy (dataPath, path);
156   dataPathPtr = dataPath;
157   return dataPathPtr;
158 }
159
160 char *EXPORT_CALL
161 lou_getDataPath ()
162 {
163   return dataPathPtr;
164 }
165
166 /* End of dataPath code.*/
167
168 static char tablePath[MAXSTRING];
169 static FILE *logFile = NULL;
170 static char initialLogFileName[256];
171
172 void EXPORT_CALL
173 lou_logFile (const char *fileName)
174 {
175   if (fileName == NULL || fileName[0] == 0)
176     return;
177   if (initialLogFileName[0] == 0)
178     strcpy (initialLogFileName, fileName);
179   logFile = fopen (fileName, "wb");
180   if (logFile == NULL && initialLogFileName[0] != 0)
181     logFile = fopen (initialLogFileName, "wb");
182   if (logFile == NULL)
183     {
184       fprintf (stderr, "Cannot open log file %s\n", fileName);
185       logFile = stderr;
186     }
187 }
188
189 void EXPORT_CALL
190 lou_logPrint (char *format, ...)
191 {
192 #ifndef __SYMBIAN32__
193   va_list argp;
194   if (format == NULL)
195     return;
196   if (logFile == NULL && initialLogFileName[0] != 0)
197     logFile = fopen (initialLogFileName, "wb");
198   if (logFile == NULL)
199   logFile = stderr;
200   va_start (argp, format);
201 #ifndef ANDROID
202   vfprintf (logFile, format, argp);
203   fprintf (logFile, "\n");
204 #else
205   __android_log_vprint(ANDROID_LOG_DEBUG, "liblouis", format, argp);
206 #endif
207   va_end (argp);
208 #endif
209 }
210
211 void EXPORT_CALL
212 lou_logEnd (void)
213 {
214   if (logFile != NULL)
215     fclose (logFile);
216   logFile = NULL;
217 }
218
219 static int
220 eqasc2uni (const unsigned char *a, const widechar * b, const int len)
221 {
222   int k;
223   for (k = 0; k < len; k++)
224     if ((widechar) a[k] != b[k])
225       return 0;
226   return 1;
227 }
228
229 typedef struct
230 {
231   widechar length;
232   widechar chars[MAXSTRING];
233 }
234 CharsString;
235
236 static int errorCount;
237 static int warningCount;
238 static TranslationTableHeader *table;
239 static TranslationTableOffset tableSize;
240 static TranslationTableOffset tableUsed;
241
242 static const char *characterClassNames[] = {
243   "space",
244   "letter",
245   "digit",
246   "punctuation",
247   "uppercase",
248   "lowercase",
249   "math",
250   "sign",
251   "litdigit",
252   NULL
253 };
254
255 struct CharacterClass
256 {
257   struct CharacterClass *next;
258   TranslationTableCharacterAttributes attribute;
259   widechar length;
260   widechar name[1];
261 };
262 static struct CharacterClass *characterClasses;
263 static TranslationTableCharacterAttributes characterClassAttribute;
264
265 static const char *opcodeNames[CTO_None] = {
266   "include",
267   "locale",
268   "undefined",
269   "capsign",
270   "begcaps",
271   "lenbegcaps",
272   "endcaps",
273   "firstwordcaps",
274   "lastwordbeforecaps",
275   "lastwordaftercaps",
276   "lencapsphrase",
277   "letsign",
278   "noletsignbefore",
279   "noletsign",
280   "noletsignafter",
281   "numsign",
282   "firstwordital",
283   "italsign",
284   "lastworditalbefore",
285   "lastworditalafter",
286   "begital",
287   "firstletterital",
288   "endital",
289   "lastletterital",
290   "singleletterital",
291   "italword",
292   "lenitalphrase",
293   "firstwordbold",
294   "boldsign",
295   "lastwordboldbefore",
296   "lastwordboldafter",
297   "begbold",
298   "firstletterbold",
299   "endbold",
300   "lastletterbold",
301   "singleletterbold",
302   "boldword",
303   "lenboldphrase",
304   "firstwordunder",
305   "undersign",
306   "lastwordunderbefore",
307   "lastwordunderafter",
308   "begunder",
309   "firstletterunder",
310   "endunder",
311   "lastletterunder",
312   "singleletterunder",
313   "underword",
314   "lenunderphrase",
315   "begcomp",
316   "compbegemph1",
317   "compendemph1",
318   "compbegemph2",
319   "compendemph2",
320   "compbegemph3",
321   "compendemph3",
322   "compcapsign",
323   "compbegcaps",
324   "compendcaps",
325   "endcomp",
326   "multind",
327   "compdots",
328   "comp6",
329   "class",
330   "after",
331   "before",
332   "noback",
333   "nofor",
334   "swapcc",
335   "swapcd",
336   "swapdd",
337   "space",
338   "digit",
339   "punctuation",
340   "math",
341   "sign",
342   "letter",
343   "uppercase",
344   "lowercase",
345   "grouping",
346   "uplow",
347   "litdigit",
348   "display",
349   "replace",
350   "context",
351   "correct",
352   "pass2",
353   "pass3",
354   "pass4",
355   "repeated",
356   "repword",
357   "capsnocont",
358   "always",
359   "exactdots",
360   "nocross",
361   "syllable",
362   "nocont",
363   "compbrl",
364   "literal",
365   "largesign",
366   "word",
367   "partword",
368   "joinnum",
369   "joinword",
370   "lowword",
371   "contraction",
372   "sufword",
373   "prfword",
374   "begword",
375   "begmidword",
376   "midword",
377   "midendword",
378   "endword",
379   "prepunc",
380   "postpunc",
381   "begnum",
382   "midnum",
383   "endnum",
384   "decpoint",
385   "hyphen",
386   "nobreak"
387 };
388 static short opcodeLengths[CTO_None] = { 0 };
389
390 typedef enum
391 { noEncoding, bigEndian, littleEndian, ascii8 } EncodingType;
392
393
394 typedef struct
395 {
396   const char *fileName;
397   FILE *in;
398   int lineNumber;
399   EncodingType encoding;
400   int status;
401   int linelen;
402   int linepos;
403   int checkencoding[2];
404   widechar line[MAXSTRING];
405 }
406 FileInfo;
407
408 static char scratchBuf[MAXSTRING];
409
410 char *
411 showString (widechar const *chars, int length)
412 {
413 /*Translate a string of characters to the encoding used in character 
414 * operands */
415   int charPos;
416   int bufPos = 0;
417   scratchBuf[bufPos++] = '\'';
418   for (charPos = 0; charPos < length; charPos++)
419     {
420       if (chars[charPos] >= 32 && chars[charPos] < 127)
421         scratchBuf[bufPos++] = (char) chars[charPos];
422       else
423         {
424           char hexbuf[20];
425           int hexLength;
426           char escapeLetter;
427
428           int leadingZeros;
429           int hexPos;
430           hexLength = sprintf (hexbuf, "%x", chars[charPos]);
431           switch (hexLength)
432             {
433             case 1:
434             case 2:
435             case 3:
436             case 4:
437               escapeLetter = 'x';
438               leadingZeros = 4 - hexLength;
439               break;
440             case 5:
441               escapeLetter = 'y';
442               leadingZeros = 0;
443               break;
444             case 6:
445             case 7:
446             case 8:
447               escapeLetter = 'z';
448               leadingZeros = 8 - hexLength;
449               break;
450             default:
451               escapeLetter = '?';
452               leadingZeros = 0;
453               break;
454             }
455           if ((bufPos + leadingZeros + hexLength + 4) >= sizeof (scratchBuf))
456             break;
457           scratchBuf[bufPos++] = '\\';
458           scratchBuf[bufPos++] = escapeLetter;
459           for (hexPos = 0; hexPos < leadingZeros; hexPos++)
460             scratchBuf[bufPos++] = '0';
461           for (hexPos = 0; hexPos < hexLength; hexPos++)
462             scratchBuf[bufPos++] = hexbuf[hexPos];
463         }
464     }
465   scratchBuf[bufPos++] = '\'';
466   scratchBuf[bufPos] = 0;
467   return scratchBuf;
468 }
469
470 char *
471 showDots (widechar const *dots, int length)
472 {
473 /* Translate a sequence of dots to the encoding used in dots operands. 
474 */
475   int bufPos = 0;
476   int dotsPos;
477   for (dotsPos = 0; bufPos < sizeof (scratchBuf) && dotsPos < length;
478        dotsPos++)
479     {
480       if ((dots[dotsPos] & B1))
481         scratchBuf[bufPos++] = '1';
482       if ((dots[dotsPos] & B2))
483         scratchBuf[bufPos++] = '2';
484       if ((dots[dotsPos] & B3))
485         scratchBuf[bufPos++] = '3';
486       if ((dots[dotsPos] & B4))
487         scratchBuf[bufPos++] = '4';
488       if ((dots[dotsPos] & B5))
489         scratchBuf[bufPos++] = '5';
490       if ((dots[dotsPos] & B6))
491         scratchBuf[bufPos++] = '6';
492       if ((dots[dotsPos] & B7))
493         scratchBuf[bufPos++] = '7';
494       if ((dots[dotsPos] & B8))
495         scratchBuf[bufPos++] = '8';
496       if ((dots[dotsPos] & B9))
497         scratchBuf[bufPos++] = '9';
498       if ((dots[dotsPos] & B10))
499         scratchBuf[bufPos++] = 'A';
500       if ((dots[dotsPos] & B11))
501         scratchBuf[bufPos++] = 'B';
502       if ((dots[dotsPos] & B12))
503         scratchBuf[bufPos++] = 'C';
504       if ((dots[dotsPos] & B13))
505         scratchBuf[bufPos++] = 'D';
506       if ((dots[dotsPos] & B14))
507         scratchBuf[bufPos++] = 'E';
508       if ((dots[dotsPos] & B15))
509         scratchBuf[bufPos++] = 'F';
510       if ((dots[dotsPos] == B16))
511         scratchBuf[bufPos++] = '0';
512       if (dotsPos != length - 1)
513         scratchBuf[bufPos++] = '-';
514     }
515   scratchBuf[bufPos] = 0;
516   return &scratchBuf[0];
517 }
518
519 char *
520 showAttributes (TranslationTableCharacterAttributes a)
521 {
522 /* Show attributes using the letters used after the $ in multipass 
523 * opcodes. */
524   int bufPos = 0;
525   if ((a & CTC_Space))
526     scratchBuf[bufPos++] = 's';
527   if ((a & CTC_Letter))
528     scratchBuf[bufPos++] = 'l';
529   if ((a & CTC_Digit))
530     scratchBuf[bufPos++] = 'd';
531   if ((a & CTC_Punctuation))
532     scratchBuf[bufPos++] = 'p';
533   if ((a & CTC_UpperCase))
534     scratchBuf[bufPos++] = 'U';
535   if ((a & CTC_LowerCase))
536     scratchBuf[bufPos++] = 'u';
537   if ((a & CTC_Math))
538     scratchBuf[bufPos++] = 'm';
539   if ((a & CTC_Sign))
540     scratchBuf[bufPos++] = 'S';
541   if ((a & CTC_LitDigit))
542     scratchBuf[bufPos++] = 'D';
543   if ((a & CTC_Class1))
544     scratchBuf[bufPos++] = 'w';
545   if ((a & CTC_Class2))
546     scratchBuf[bufPos++] = 'x';
547   if ((a & CTC_Class3))
548     scratchBuf[bufPos++] = 'y';
549   if ((a & CTC_Class4))
550     scratchBuf[bufPos++] = 'z';
551   scratchBuf[bufPos] = 0;
552   return scratchBuf;
553 }
554
555 static void compileError (FileInfo * nested, char *format, ...);
556
557 static int
558 getAChar (FileInfo * nested)
559 {
560 /*Read a big endian, little *ndian or ASCII 8 file and convert it to 
561 * 16- or 32-bit unsigned integers */
562   int ch1 = 0, ch2 = 0;
563   widechar character;
564   if (nested->encoding == ascii8)
565     if (nested->status == 2)
566       {
567         nested->status++;
568         return nested->checkencoding[1];
569       }
570   while ((ch1 = fgetc (nested->in)) != EOF)
571     {
572       if (nested->status < 2)
573         nested->checkencoding[nested->status] = ch1;
574       nested->status++;
575       if (nested->status == 2)
576         {
577           if (nested->checkencoding[0] == 0xfe
578               && nested->checkencoding[1] == 0xff)
579             nested->encoding = bigEndian;
580           else if (nested->checkencoding[0] == 0xff
581                    && nested->checkencoding[1] == 0xfe)
582             nested->encoding = littleEndian;
583           else if (nested->checkencoding[0] < 128
584                    && nested->checkencoding[1] < 128)
585             {
586               nested->encoding = ascii8;
587               return nested->checkencoding[0];
588             }
589           else
590             {
591               compileError (nested,
592                             "encoding is neither big-endian, little-endian nor ASCII 8.");
593               ch1 = EOF;
594               break;;
595             }
596           continue;
597         }
598       switch (nested->encoding)
599         {
600         case noEncoding:
601           break;
602         case ascii8:
603           return ch1;
604           break;
605         case bigEndian:
606           ch2 = fgetc (nested->in);
607           if (ch2 == EOF)
608             break;
609           character = (ch1 << 8) | ch2;
610           return (int) character;
611           break;
612         case littleEndian:
613           ch2 = fgetc (nested->in);
614           if (ch2 == EOF)
615             break;
616           character = (ch2 << 8) | ch1;
617           return (int) character;
618           break;
619         }
620       if (ch1 == EOF || ch2 == EOF)
621         break;
622     }
623   return EOF;
624 }
625
626 static int
627 getALine (FileInfo * nested)
628 {
629 /*Read a line of widechar's from an input file */
630   int ch;
631   int pch = 0;
632   nested->linelen = 0;
633   while ((ch = getAChar (nested)) != EOF)
634     {
635       if (ch == 13)
636         continue;
637       if (pch == '\\' && ch == 10)
638         {
639           nested->linelen--;
640           continue;
641         }
642       if (ch == 10 || nested->linelen >= MAXSTRING)
643         break;
644       nested->line[nested->linelen++] = (widechar) ch;
645       pch = ch;
646     }
647   nested->line[nested->linelen] = 0;
648   nested->linepos = 0;
649   if (ch == EOF)
650     return 0;
651   nested->lineNumber++;
652   return 1;
653 }
654
655 static int lastToken;
656 static int
657 getToken (FileInfo * nested, CharsString * result, const char *description)
658 {
659 /*Find the next string of contiguous non-whitespace characters. If this 
660  * is the last token on the line, return 2 instead of 1. */
661   while (nested->line[nested->linepos] && nested->line[nested->linepos] <= 32)
662     nested->linepos++;
663   result->length = 0;
664   while (nested->line[nested->linepos] && nested->line[nested->linepos] > 32)
665     result->chars[result->length++] = nested->line[nested->linepos++];
666   if (!result->length)
667     {
668       /* Not enough tokens */
669       if (description)
670         compileError (nested, "%s not specified.", description);
671       return 0;
672     }
673   result->chars[result->length] = 0;
674   while (nested->line[nested->linepos] && nested->line[nested->linepos] <= 32)
675     nested->linepos++;
676   if (nested->line[nested->linepos] == 0)
677     {
678       lastToken = 1;
679       return 2;
680     }
681   else
682     {
683       lastToken = 0;
684       return 1;
685     }
686 }
687
688 static void
689 compileError (FileInfo * nested, char *format, ...)
690 {
691 #ifndef __SYMBIAN32__
692   char buffer[MAXSTRING];
693   va_list arguments;
694   va_start (arguments, format);
695 #ifdef _WIN32
696   (void) _vsnprintf (buffer, sizeof (buffer), format, arguments);
697 #else
698   (void) vsnprintf (buffer, sizeof (buffer), format, arguments);
699 #endif
700   va_end (arguments);
701   if (nested)
702     lou_logPrint ("%s:%d: error: %s", nested->fileName,
703                   nested->lineNumber, buffer);
704   else
705     lou_logPrint ("error: %s", buffer);
706   errorCount++;
707 #endif
708 }
709
710 static void
711 compileWarning (FileInfo * nested, char *format, ...)
712 {
713 #ifndef __SYMBIAN32__
714   char buffer[MAXSTRING];
715   va_list arguments;
716   va_start (arguments, format);
717 #ifdef _WIN32
718   (void) _vsnprintf (buffer, sizeof (buffer), format, arguments);
719 #else
720   (void) vsnprintf (buffer, sizeof (buffer), format, arguments);
721 #endif
722   va_end (arguments);
723   if (nested)
724     lou_logPrint ("%s:%d: warning: %s", nested->fileName,
725                   nested->lineNumber, buffer);
726   else
727     lou_logPrint ("warning: %s", buffer);
728   warningCount++;
729 #endif
730 }
731
732 static int
733 allocateSpaceInTable (FileInfo * nested, TranslationTableOffset * offset,
734                       int count)
735 {
736 /* allocate memory for translation table and expand previously allocated 
737 * memory if necessary */
738   int spaceNeeded = ((count + OFFSETSIZE - 1) / OFFSETSIZE) * OFFSETSIZE;
739   TranslationTableOffset size = tableUsed + spaceNeeded;
740   if (size > tableSize)
741     {
742       void *newTable;
743       size += (size / OFFSETSIZE);
744       newTable = realloc (table, size);
745       if (!newTable)
746         {
747           compileError (nested, "Not enough memory for translation table.");
748           return 0;
749         }
750       memset (((unsigned char *) newTable) + tableSize, 0, size - tableSize);
751       table = (TranslationTableHeader *) newTable;
752       tableSize = size;
753     }
754   if (offset != NULL)
755     {
756       *offset = (tableUsed - sizeof (*table)) / OFFSETSIZE;
757       tableUsed += spaceNeeded;
758     }
759   return 1;
760 }
761
762 static int
763 reserveSpaceInTable (FileInfo * nested, int count)
764 {
765   return (allocateSpaceInTable (nested, NULL, count));
766 }
767
768 static int
769 allocateHeader (FileInfo * nested)
770 {
771 /*Allocate memory for the table header and a guess on the number of 
772 * rules */
773   const TranslationTableOffset startSize = 2 * sizeof (*table);
774   if (table)
775     return 1;
776   tableUsed = sizeof (*table) + OFFSETSIZE;     /*So no offset is ever zero */
777   if (!(table = malloc (startSize)))
778     {
779       compileError (nested, "Not enough memory");
780       if (table != NULL)
781         free (table);
782       table = NULL;
783       return 0;
784     }
785   memset (table, 0, startSize);
786   tableSize = startSize;
787   return 1;
788 }
789
790 int
791 stringHash (const widechar * c)
792 {
793 /*hash function for strings */
794   unsigned long int makeHash = (((unsigned long int) c[0] << 8) +
795                                 (unsigned long int) c[1]) % HASHNUM;
796   return (int) makeHash;
797 }
798
799 int
800 charHash (widechar c)
801 {
802   unsigned long int makeHash = (unsigned long int) c % HASHNUM;
803   return (int) makeHash;
804 }
805
806 static TranslationTableCharacter *
807 compile_findCharOrDots (widechar c, int m)
808 {
809 /*Look up a character or dot pattern. If m is 0 look up a character, 
810 * otherwise look up a dot pattern. Although the algorithms are almost 
811 * identical, different tables are needed for characters and dots because 
812 * of the possibility of conflicts.*/
813   TranslationTableCharacter *character;
814   TranslationTableOffset bucket;
815   unsigned long int makeHash = (unsigned long int) c % HASHNUM;
816   if (m == 0)
817     bucket = table->characters[makeHash];
818   else
819     bucket = table->dots[makeHash];
820   while (bucket)
821     {
822       character = (TranslationTableCharacter *) & table->ruleArea[bucket];
823       if (character->realchar == c)
824         return character;
825       bucket = character->next;
826     }
827   return NULL;
828 }
829
830 static TranslationTableCharacter noChar = { 0, 0, 0, CTC_Space, 32, 32, 32 };
831 static TranslationTableCharacter noDots =
832   { 0, 0, 0, CTC_Space, B16, B16, B16 };
833 static char *unknownDots (widechar dots);
834
835 static TranslationTableCharacter *
836 definedCharOrDots (FileInfo * nested, widechar c, int m)
837 {
838   TranslationTableCharacter *notFound;
839   TranslationTableCharacter *charOrDots = compile_findCharOrDots (c, m);
840   if (charOrDots)
841     return charOrDots;
842   if (m == 0)
843     {
844       notFound = &noChar;
845       compileError (nested,
846                     "character %s should be defined at this point but is not",
847                     showString (&c, 1));
848     }
849   else
850     {
851       notFound = &noDots;
852       compileError (nested,
853                     "cell %s should be defined at this point but is not",
854                     unknownDots (c));
855     }
856   return notFound;
857 }
858
859 static TranslationTableCharacter *
860 addCharOrDots (FileInfo * nested, widechar c, int m)
861 {
862 /*See if a character or dot pattern is in the appropriate table. If not, 
863 * insert it. In either 
864 * case, return a pointer to it. */
865   TranslationTableOffset bucket;
866   TranslationTableCharacter *character;
867   TranslationTableCharacter *oldchar;
868   TranslationTableOffset offset;
869   unsigned long int makeHash;
870   if ((character = compile_findCharOrDots (c, m)))
871     return character;
872   if (!allocateSpaceInTable (nested, &offset, sizeof (*character)))
873     return NULL;
874   character = (TranslationTableCharacter *) & table->ruleArea[offset];
875   memset (character, 0, sizeof (*character));
876   character->realchar = c;
877   makeHash = (unsigned long int) c % HASHNUM;
878   if (m == 0)
879     bucket = table->characters[makeHash];
880   else
881     bucket = table->dots[makeHash];
882   if (!bucket)
883     {
884       if (m == 0)
885         table->characters[makeHash] = offset;
886       else
887         table->dots[makeHash] = offset;
888     }
889   else
890     {
891       oldchar = (TranslationTableCharacter *) & table->ruleArea[bucket];
892       while (oldchar->next)
893         oldchar =
894           (TranslationTableCharacter *) & table->ruleArea[oldchar->next];
895       oldchar->next = offset;
896     }
897   return character;
898 }
899
900 static CharOrDots *
901 getCharOrDots (widechar c, int m)
902 {
903   CharOrDots *cdPtr;
904   TranslationTableOffset bucket;
905   unsigned long int makeHash = (unsigned long int) c % HASHNUM;
906   if (m == 0)
907     bucket = table->charToDots[makeHash];
908   else
909     bucket = table->dotsToChar[makeHash];
910   while (bucket)
911     {
912       cdPtr = (CharOrDots *) & table->ruleArea[bucket];
913       if (cdPtr->lookFor == c)
914         return cdPtr;
915       bucket = cdPtr->next;
916     }
917   return NULL;
918 }
919
920 widechar
921 getDotsForChar (widechar c)
922 {
923   CharOrDots *cdPtr = getCharOrDots (c, 0);
924   if (cdPtr)
925     return cdPtr->found;
926   return B16;
927 }
928
929 widechar
930 getCharFromDots (widechar d)
931 {
932   CharOrDots *cdPtr = getCharOrDots (d, 1);
933   if (cdPtr)
934     return cdPtr->found;
935   return ' ';
936 }
937
938 static int
939 putCharAndDots (FileInfo * nested, widechar c, widechar d)
940 {
941   TranslationTableOffset bucket;
942   CharOrDots *cdPtr;
943   CharOrDots *oldcdPtr = NULL;
944   TranslationTableOffset offset;
945   unsigned long int makeHash;
946   if (!(cdPtr = getCharOrDots (c, 0)))
947     {
948       if (!allocateSpaceInTable (nested, &offset, sizeof (*cdPtr)))
949         return 0;
950       cdPtr = (CharOrDots *) & table->ruleArea[offset];
951       cdPtr->next = 0;
952       cdPtr->lookFor = c;
953       cdPtr->found = d;
954       makeHash = (unsigned long int) c % HASHNUM;
955       bucket = table->charToDots[makeHash];
956       if (!bucket)
957         table->charToDots[makeHash] = offset;
958       else
959         {
960           oldcdPtr = (CharOrDots *) & table->ruleArea[bucket];
961           while (oldcdPtr->next)
962             oldcdPtr = (CharOrDots *) & table->ruleArea[oldcdPtr->next];
963           oldcdPtr->next = offset;
964         }
965     }
966   if (!(cdPtr = getCharOrDots (d, 1)))
967     {
968       if (!allocateSpaceInTable (nested, &offset, sizeof (*cdPtr)))
969         return 0;
970       cdPtr = (CharOrDots *) & table->ruleArea[offset];
971       cdPtr->next = 0;
972       cdPtr->lookFor = d;
973       cdPtr->found = c;
974       makeHash = (unsigned long int) d % HASHNUM;
975       bucket = table->dotsToChar[makeHash];
976       if (!bucket)
977         table->dotsToChar[makeHash] = offset;
978       else
979         {
980           oldcdPtr = (CharOrDots *) & table->ruleArea[bucket];
981           while (oldcdPtr->next)
982             oldcdPtr = (CharOrDots *) & table->ruleArea[oldcdPtr->next];
983           oldcdPtr->next = offset;
984         }
985     }
986   return 1;
987 }
988
989 static char *
990 unknownDots (widechar dots)
991 {
992 /*Print out dot numbers */
993   static char buffer[20];
994   int k = 1;
995   buffer[0] = '\\';
996   if ((dots & B1))
997     buffer[k++] = '1';
998   if ((dots & B2))
999     buffer[k++] = '2';
1000   if ((dots & B3))
1001     buffer[k++] = '3';
1002   if ((dots & B4))
1003     buffer[k++] = '4';
1004   if ((dots & B5))
1005     buffer[k++] = '5';
1006   if ((dots & B6))
1007     buffer[k++] = '6';
1008   if ((dots & B7))
1009     buffer[k++] = '7';
1010   if ((dots & B8))
1011     buffer[k++] = '8';
1012   if ((dots & B9))
1013     buffer[k++] = '9';
1014   if ((dots & B10))
1015     buffer[k++] = 'A';
1016   if ((dots & B11))
1017     buffer[k++] = 'B';
1018   if ((dots & B12))
1019     buffer[k++] = 'C';
1020   if ((dots & B13))
1021     buffer[k++] = 'D';
1022   if ((dots & B14))
1023     buffer[k++] = 'E';
1024   if ((dots & B15))
1025     buffer[k++] = 'F';
1026   buffer[k++] = '/';
1027   buffer[k] = 0;
1028   return buffer;
1029 }
1030
1031 static TranslationTableOffset newRuleOffset = 0;
1032 static TranslationTableRule *newRule = NULL;
1033
1034 static int
1035 charactersDefined (FileInfo * nested)
1036 {
1037 /*Check that all characters are defined by character-definition 
1038 * opcodes*/
1039   int noErrors = 1;
1040   int k;
1041   if ((newRule->opcode >= CTO_Space && newRule->opcode <= CTO_LitDigit)
1042       || newRule->opcode == CTO_SwapDd
1043       ||
1044       newRule->opcode == CTO_Replace || newRule->opcode == CTO_MultInd
1045       || newRule->opcode == CTO_Repeated ||
1046       ((newRule->opcode >= CTO_Context && newRule->opcode <=
1047         CTO_Pass4) && newRule->opcode != CTO_Correct))
1048     return 1;
1049   for (k = 0; k < newRule->charslen; k++)
1050     if (!compile_findCharOrDots (newRule->charsdots[k], 0))
1051       {
1052         compileError (nested, "Character %s is not defined", showString
1053                       (&newRule->charsdots[k], 1));
1054         noErrors = 0;
1055       }
1056   if (!(newRule->opcode == CTO_Correct || newRule->opcode ==
1057         CTO_NoBreak || newRule->opcode == CTO_SwapCc || newRule->opcode ==
1058         CTO_SwapCd))
1059     {
1060       for (k = newRule->charslen; k < newRule->charslen + newRule->dotslen;
1061            k++)
1062         if (!compile_findCharOrDots (newRule->charsdots[k], 1))
1063           {
1064             compileError (nested, "Dot pattern %s is not defined.",
1065                           unknownDots (newRule->charsdots[k]));
1066             noErrors = 0;
1067           }
1068     }
1069   return noErrors;
1070 }
1071
1072 static int noback = 0;
1073 static int nofor = 0;
1074
1075 /*The following functions are 
1076 called by addRule to handle various 
1077 * cases.*/
1078
1079 static void
1080 add_0_single (FileInfo * nested)
1081 {
1082 /*direction = 0, newRule->charslen = 1*/
1083   TranslationTableRule *currentRule;
1084   TranslationTableOffset *currentOffsetPtr;
1085   TranslationTableCharacter *character;
1086   int m = 0;
1087   if (newRule->opcode == CTO_CompDots || newRule->opcode == CTO_Comp6)
1088     return;
1089   if (newRule->opcode >= CTO_Pass2 && newRule->opcode <= CTO_Pass4)
1090     m = 1;
1091   character = definedCharOrDots (nested, newRule->charsdots[0], m);
1092   if (m != 1 && character->attributes & CTC_Letter && (newRule->opcode
1093                                                        ==
1094                                                        CTO_WholeWord
1095                                                        || newRule->opcode ==
1096                                                        CTO_LargeSign))
1097     {
1098       if (table->noLetsignCount < LETSIGNSIZE)
1099         table->noLetsign[table->noLetsignCount++] = newRule->charsdots[0];
1100     }
1101   if (newRule->opcode >= CTO_Space && newRule->opcode < CTO_UpLow)
1102     character->definitionRule = newRuleOffset;
1103   currentOffsetPtr = &character->otherRules;
1104   while (*currentOffsetPtr)
1105     {
1106       currentRule = (TranslationTableRule *)
1107         & table->ruleArea[*currentOffsetPtr];
1108       if (currentRule->charslen == 0)
1109         break;
1110       if (currentRule->opcode >= CTO_Space && currentRule->opcode < CTO_UpLow)
1111         if (!(newRule->opcode >= CTO_Space && newRule->opcode < CTO_UpLow))
1112           break;
1113       currentOffsetPtr = &currentRule->charsnext;
1114     }
1115   newRule->charsnext = *currentOffsetPtr;
1116   *currentOffsetPtr = newRuleOffset;
1117 }
1118
1119 static void
1120 add_0_multiple (void)
1121 {
1122 /*direction = 0 newRule->charslen > 1*/
1123   TranslationTableRule *currentRule = NULL;
1124   TranslationTableOffset *currentOffsetPtr =
1125     &table->forRules[stringHash (&newRule->charsdots[0])];
1126   while (*currentOffsetPtr)
1127     {
1128       currentRule = (TranslationTableRule *)
1129         & table->ruleArea[*currentOffsetPtr];
1130       if (newRule->charslen > currentRule->charslen)
1131         break;
1132       if (newRule->charslen == currentRule->charslen)
1133         if ((currentRule->opcode == CTO_Always)
1134             && (newRule->opcode != CTO_Always))
1135           break;
1136       currentOffsetPtr = &currentRule->charsnext;
1137     }
1138   newRule->charsnext = *currentOffsetPtr;
1139   *currentOffsetPtr = newRuleOffset;
1140 }
1141
1142 static void
1143 add_1_single (FileInfo * nested)
1144 {
1145 /*direction = 1, newRule->dotslen = 1*/
1146   TranslationTableRule *currentRule;
1147   TranslationTableOffset *currentOffsetPtr;
1148   TranslationTableCharacter *dots;
1149   if (newRule->opcode == CTO_NoBreak || newRule->opcode == CTO_SwapCc ||
1150       (newRule->opcode >= CTO_Context
1151        &&
1152        newRule->opcode <= CTO_Pass4)
1153       || newRule->opcode == CTO_Repeated || (newRule->opcode == CTO_Always
1154                                              && newRule->charslen == 1))
1155     return;                     /*too ambiguous */
1156   dots = definedCharOrDots (nested, newRule->charsdots[newRule->charslen], 1);
1157   if (newRule->opcode >= CTO_Space && newRule->opcode < CTO_UpLow)
1158     dots->definitionRule = newRuleOffset;
1159   currentOffsetPtr = &dots->otherRules;
1160   while (*currentOffsetPtr)
1161     {
1162       currentRule = (TranslationTableRule *)
1163         & table->ruleArea[*currentOffsetPtr];
1164       if (newRule->charslen > currentRule->charslen ||
1165           currentRule->dotslen == 0)
1166         break;
1167       if (currentRule->opcode >= CTO_Space && currentRule->opcode < CTO_UpLow)
1168         if (!(newRule->opcode >= CTO_Space && newRule->opcode < CTO_UpLow))
1169           break;
1170       currentOffsetPtr = &currentRule->dotsnext;
1171     }
1172   newRule->dotsnext = *currentOffsetPtr;
1173   *currentOffsetPtr = newRuleOffset;
1174 }
1175
1176 static void
1177 add_1_multiple (void)
1178 {
1179 /*direction = 1, newRule->dotslen > 1*/
1180   TranslationTableRule *currentRule = NULL;
1181   TranslationTableOffset *currentOffsetPtr = &table->backRules[stringHash
1182                                                                (&newRule->
1183                                                                 charsdots
1184                                                                 [newRule->
1185                                                                  charslen])];
1186   if (newRule->opcode == CTO_NoBreak || newRule->opcode == CTO_SwapCc ||
1187       (newRule->opcode >= CTO_Context && newRule->opcode <= CTO_Pass4))
1188     return;
1189   while (*currentOffsetPtr)
1190     {
1191       int currentLength;
1192       int newLength;
1193       currentRule = (TranslationTableRule *)
1194         & table->ruleArea[*currentOffsetPtr];
1195       currentLength = currentRule->dotslen + currentRule->charslen;
1196       newLength = newRule->dotslen + newRule->charslen;
1197       if (newLength > currentLength)
1198         break;
1199       if (currentLength == newLength)
1200         if ((currentRule->opcode == CTO_Always)
1201             && (newRule->opcode != CTO_Always))
1202           break;
1203       currentOffsetPtr = &currentRule->dotsnext;
1204     }
1205   newRule->dotsnext = *currentOffsetPtr;
1206   *currentOffsetPtr = newRuleOffset;
1207 }
1208
1209 static void
1210 makeRuleChain (TranslationTableOffset * offsetPtr)
1211 {
1212   TranslationTableRule *currentRule;
1213   while (*offsetPtr)
1214     {
1215       currentRule = (TranslationTableRule *) & table->ruleArea[*offsetPtr];
1216       offsetPtr = &currentRule->charsnext;
1217     }
1218   newRule->charsnext = *offsetPtr;
1219   *offsetPtr = newRuleOffset;
1220 }
1221
1222 static int
1223 addPassRule (FileInfo * nested)
1224 {
1225   TranslationTableOffset *offsetPtr;
1226   switch (newRule->opcode)
1227     {
1228     case CTO_Correct:
1229       offsetPtr = &table->attribOrSwapRules[0];
1230       break;
1231     case CTO_Context:
1232       offsetPtr = &table->attribOrSwapRules[1];
1233       break;
1234     case CTO_Pass2:
1235       offsetPtr = &table->attribOrSwapRules[2];
1236       break;
1237     case CTO_Pass3:
1238       offsetPtr = &table->attribOrSwapRules[3];
1239       break;
1240     case CTO_Pass4:
1241       offsetPtr = &table->attribOrSwapRules[4];
1242       break;
1243     default:
1244       return 0;
1245     }
1246   makeRuleChain (offsetPtr);
1247   return 1;
1248 }
1249
1250 static int
1251   addRule
1252   (FileInfo * nested,
1253    TranslationTableOpcode opcode,
1254    CharsString * ruleChars,
1255    CharsString * ruleDots,
1256    TranslationTableCharacterAttributes after,
1257    TranslationTableCharacterAttributes before)
1258 {
1259 /*Add a rule to the table, using the hash function to find the start of 
1260 * chains and chaining both the chars and dots strings */
1261   int ruleSize = sizeof (TranslationTableRule) - (DEFAULTRULESIZE * CHARSIZE);
1262   int direction = 0;            /*0 = forward translation; 1 = bacward */
1263   if (ruleChars)
1264     ruleSize += CHARSIZE * ruleChars->length;
1265   if (ruleDots)
1266     ruleSize += CHARSIZE * ruleDots->length;
1267   if (!allocateSpaceInTable (nested, &newRuleOffset, ruleSize))
1268     return 0;
1269   newRule = (TranslationTableRule *) & table->ruleArea[newRuleOffset];
1270   newRule->opcode = opcode;
1271   newRule->after = after;
1272   newRule->before = before;
1273   if (ruleChars)
1274     memcpy (&newRule->charsdots[0], &ruleChars->chars[0],
1275             CHARSIZE * (newRule->charslen = ruleChars->length));
1276   else
1277     newRule->charslen = 0;
1278   if (ruleDots)
1279     memcpy (&newRule->charsdots[newRule->charslen],
1280             &ruleDots->chars[0], CHARSIZE * (newRule->dotslen =
1281                                              ruleDots->length));
1282   else
1283     newRule->dotslen = 0;
1284   if (!charactersDefined (nested))
1285     return 0;
1286
1287   /*link new rule into table. */
1288   if (opcode == CTO_SwapCc || opcode == CTO_SwapCd || opcode == CTO_SwapDd)
1289     return 1;
1290   if (opcode >= CTO_Context && opcode <= CTO_Pass4 && newRule->charslen == 0)
1291     return addPassRule (nested);
1292   if (newRule->charslen == 0 || nofor)
1293     direction = 1;
1294   while (direction < 2)
1295     {
1296       if (direction == 0 && newRule->charslen == 1)
1297         add_0_single (nested);
1298       else if (direction == 0 && newRule->charslen > 1)
1299         add_0_multiple ();
1300       else if (direction == 1 && newRule->dotslen == 1 && !noback)
1301         add_1_single (nested);
1302       else if (direction == 1 && newRule->dotslen > 1 && !noback)
1303         add_1_multiple ();
1304       else
1305         {
1306         }
1307       direction++;
1308       if (newRule->dotslen == 0)
1309         direction = 2;
1310     }
1311   return 1;
1312 }
1313
1314 static const struct CharacterClass *
1315 findCharacterClass (const CharsString * name)
1316 {
1317 /*Find a character class, whether predefined or user-defined */
1318   const struct CharacterClass *class = characterClasses;
1319   while (class)
1320     {
1321       if ((name->length == class->length) &&
1322           (memcmp (&name->chars[0], class->name, CHARSIZE *
1323                    name->length) == 0))
1324         return class;
1325       class = class->next;
1326     }
1327   return NULL;
1328 }
1329
1330 static struct CharacterClass *
1331 addCharacterClass (FileInfo * nested, const widechar * name, int length)
1332 {
1333 /*Define a character class, Whether predefined or user-defined */
1334   struct CharacterClass *class;
1335   if (characterClassAttribute)
1336     {
1337       if ((class = malloc (sizeof (*class) + CHARSIZE * (length - 1))))
1338         {
1339           memset (class, 0, sizeof (*class));
1340           memcpy (class->name, name, CHARSIZE * (class->length = length));
1341           class->attribute = characterClassAttribute;
1342           characterClassAttribute <<= 1;
1343           class->next = characterClasses;
1344           characterClasses = class;
1345           return class;
1346         }
1347     }
1348   compileError (nested, "character class table overflow.");
1349   return NULL;
1350 }
1351
1352 static void
1353 deallocateCharacterClasses (void)
1354 {
1355   while (characterClasses)
1356     {
1357       struct CharacterClass *class = characterClasses;
1358       characterClasses = characterClasses->next;
1359       if (class)
1360         free (class);
1361     }
1362 }
1363
1364 static int
1365 allocateCharacterClasses (void)
1366 {
1367 /*Allocate memory for predifined character classes */
1368   int k = 0;
1369   characterClasses = NULL;
1370   characterClassAttribute = 1;
1371   while (characterClassNames[k])
1372     {
1373       widechar wname[MAXSTRING];
1374       int length = strlen (characterClassNames[k]);
1375       int kk;
1376       for (kk = 0; kk < length; kk++)
1377         wname[kk] = (widechar) characterClassNames[k][kk];
1378       if (!addCharacterClass (NULL, wname, length))
1379         {
1380           deallocateCharacterClasses ();
1381           return 0;
1382         }
1383       k++;
1384     }
1385   return 1;
1386 }
1387
1388 static TranslationTableOpcode
1389 getOpcode (FileInfo * nested, const CharsString * token)
1390 {
1391   static TranslationTableOpcode lastOpcode = 0;
1392   TranslationTableOpcode opcode = lastOpcode;
1393
1394   do
1395     {
1396       if (token->length == opcodeLengths[opcode])
1397         if (eqasc2uni ((unsigned char *) opcodeNames[opcode],
1398                        &token->chars[0], token->length))
1399           {
1400             lastOpcode = opcode;
1401             return opcode;
1402           }
1403       opcode++;
1404       if (opcode >= CTO_None)
1405         opcode = 0;
1406     }
1407   while (opcode != lastOpcode);
1408   compileError (nested, "opcode %s not defined.", showString
1409                 (&token->chars[0], token->length));
1410   return CTO_None;
1411 }
1412
1413 TranslationTableOpcode
1414 findOpcodeNumber (const char *toFind)
1415 {
1416 /* Used by tools such as lou_debug */
1417   static TranslationTableOpcode lastOpcode = 0;
1418   TranslationTableOpcode opcode = lastOpcode;
1419   int length = strlen (toFind);
1420   do
1421     {
1422       if (length == opcodeLengths[opcode] && strcasecmp (toFind,
1423                                                          opcodeNames[opcode])
1424           == 0)
1425         {
1426           lastOpcode = opcode;
1427           return opcode;
1428         }
1429       opcode++;
1430       if (opcode >= CTO_None)
1431         opcode = 0;
1432     }
1433   while (opcode != lastOpcode);
1434   return CTO_None;
1435 }
1436
1437 const char *
1438 findOpcodeName (TranslationTableOpcode opcode)
1439 {
1440 /* Used by tools such as lou_debug */
1441   if (opcode < 0 || opcode >= CTO_None)
1442     {
1443       sprintf (scratchBuf, "%d", opcode);
1444       return scratchBuf;
1445     }
1446   return opcodeNames[opcode];
1447 }
1448
1449 static widechar
1450 hexValue (FileInfo * nested, const widechar * digits, int length)
1451 {
1452   int k;
1453   unsigned int binaryValue = 0;
1454   for (k = 0; k < length; k++)
1455     {
1456       unsigned int hexDigit = 0;
1457       if (digits[k] >= '0' && digits[k] <= '9')
1458         hexDigit = digits[k] - '0';
1459       else if (digits[k] >= 'a' && digits[k] <= 'f')
1460         hexDigit = digits[k] - 'a' + 10;
1461       else if (digits[k] >= 'A' && digits[k] <= 'F')
1462         hexDigit = digits[k] - 'A' + 10;
1463       else
1464         {
1465           compileError (nested, "invalid %d-digit hexadecimal number",
1466                         length);
1467           return (widechar) 0xffffffff;
1468         }
1469       binaryValue |= hexDigit << (4 * (length - 1 - k));
1470     }
1471   return (widechar) binaryValue;
1472 }
1473
1474 #define MAXBYTES 7
1475 static int first0Bit[MAXBYTES] = { 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0XFE };
1476
1477 static int
1478 parseChars (FileInfo * nested, CharsString * result, CharsString * token)
1479 {
1480   int in = 0;
1481   int out = 0;
1482   int lastOutSize = 0;
1483   int lastIn;
1484   unsigned int ch = 0;
1485   int numBytes = 0;
1486   unsigned int utf32 = 0;
1487   int k;
1488   while (in < token->length)
1489     {
1490       ch = token->chars[in++] & 0xff;
1491       if (ch < 128)
1492         {
1493           if (ch == '\\')
1494             {                   /* escape sequence */
1495               switch (ch = token->chars[in])
1496                 {
1497                 case '\\':
1498                   break;
1499                 case 'e':
1500                   ch = 0x1b;
1501                   break;
1502                 case 'f':
1503                   ch = 12;
1504                   break;
1505                 case 'n':
1506                   ch = 10;
1507                   break;
1508                 case 'r':
1509                   ch = 13;
1510                   break;
1511                 case 's':
1512                   ch = ' ';
1513                   break;
1514                 case 't':
1515                   ch = 9;
1516                   break;
1517                 case 'v':
1518                   ch = 22;
1519                   break;
1520                 case 'w':
1521                   ch = ENDSEGMENT;
1522                   break;
1523                 case 34:
1524                   ch = QUOTESUB;
1525                   break;
1526                 case 'X':
1527                 case 'x':
1528                   if (token->length - in > 4)
1529                     {
1530                       ch = hexValue (nested, &token->chars[in + 1], 4);
1531                       in += 4;
1532                     }
1533                   break;
1534                 case 'y':
1535                 case 'Y':
1536                   if (CHARSIZE == 2)
1537                     {
1538                     not32:
1539                       compileError (nested,
1540                                     "liblouis has not been compiled for 32-bit Unicode");
1541                       break;
1542                     }
1543                   if (token->length - in > 5)
1544                     {
1545                       ch = hexValue (nested, &token->chars[in + 1], 5);
1546                       in += 5;
1547                     }
1548                   break;
1549                 case 'z':
1550                 case 'Z':
1551                   if (CHARSIZE == 2)
1552                     goto not32;
1553                   if (token->length - in > 8)
1554                     {
1555                       ch = hexValue (nested, &token->chars[in + 1], 8);
1556                       in += 8;
1557                     }
1558                   break;
1559                 default:
1560                   compileError (nested, "invalid escape sequence '\\%c'", ch);
1561                   break;
1562                 }
1563               in++;
1564             }
1565           result->chars[out++] = (widechar) ch;
1566           if (out >= MAXSTRING)
1567             {
1568               result->length = out;
1569               return 1;
1570             }
1571           continue;
1572         }
1573       lastOutSize = out;
1574       lastIn = in;
1575       for (numBytes = MAXBYTES - 1; numBytes >= 0; numBytes--)
1576         if (ch >= first0Bit[numBytes])
1577           break;
1578       if (numBytes < 0)
1579         continue;
1580       utf32 = ch & (0XFF - first0Bit[numBytes]);
1581       for (k = 0; k < numBytes; k++)
1582         {
1583           if (in >= MAXSTRING)
1584             break;
1585           if (token->chars[in] < 128 || (token->chars[in] & 0x0040))
1586             {
1587               compileWarning (nested, "invalid UTF-8. Assuming Latin-1.");
1588               result->chars[out++] = token->chars[lastIn];
1589               in = lastIn + 1;
1590               continue;
1591             }
1592           utf32 = (utf32 << 6) + (token->chars[in++] & 0x3f);
1593         }
1594       if (CHARSIZE == 2 && utf32 > 0xffff)
1595         utf32 = 0xffff;
1596       result->chars[out++] = (widechar) utf32;
1597       if (out >= MAXSTRING)
1598         {
1599           result->length = lastOutSize;
1600           return 1;
1601         }
1602     }
1603   result->length = out;
1604   return 1;
1605 }
1606
1607 int
1608 extParseChars (const char *inString, widechar * outString)
1609 {
1610 /* Parse external character strings */
1611   CharsString wideIn;
1612   CharsString result;
1613   int k;
1614   for (k = 0; inString[k] && k < MAXSTRING; k++)
1615     wideIn.chars[k] = inString[k];
1616   wideIn.chars[k] = 0;
1617   wideIn.length = k;
1618   parseChars (NULL, &result, &wideIn);
1619   if (errorCount)
1620     {
1621       errorCount = 0;
1622       return 0;
1623     }
1624   for (k = 0; k < result.length; k++)
1625     outString[k] = result.chars[k];
1626   outString[k] = 0;
1627   return result.length;
1628 }
1629
1630 static int
1631 parseDots (FileInfo * nested, CharsString * cells, const CharsString * token)
1632 {
1633 /*get dot patterns */
1634   widechar cell = 0;            /*assembly place for dots */
1635   int cellCount = 0;
1636   int index;
1637   int start = 0;
1638
1639   for (index = 0; index < token->length; index++)
1640     {
1641       int started = index != start;
1642       widechar character = token->chars[index];
1643       switch (character)
1644         {                       /*or dots to make up Braille cell */
1645           {
1646             int dot;
1647         case '1':
1648             dot = B1;
1649             goto haveDot;
1650         case '2':
1651             dot = B2;
1652             goto haveDot;
1653         case '3':
1654             dot = B3;
1655             goto haveDot;
1656         case '4':
1657             dot = B4;
1658             goto haveDot;
1659         case '5':
1660             dot = B5;
1661             goto haveDot;
1662         case '6':
1663             dot = B6;
1664             goto haveDot;
1665         case '7':
1666             dot = B7;
1667             goto haveDot;
1668         case '8':
1669             dot = B8;
1670             goto haveDot;
1671         case '9':
1672             dot = B9;
1673             goto haveDot;
1674         case 'a':
1675         case 'A':
1676             dot = B10;
1677             goto haveDot;
1678         case 'b':
1679         case 'B':
1680             dot = B11;
1681             goto haveDot;
1682         case 'c':
1683         case 'C':
1684             dot = B12;
1685             goto haveDot;
1686         case 'd':
1687         case 'D':
1688             dot = B13;
1689             goto haveDot;
1690         case 'e':
1691         case 'E':
1692             dot = B14;
1693             goto haveDot;
1694         case 'f':
1695         case 'F':
1696             dot = B15;
1697           haveDot:
1698             if (started && !cell)
1699               goto invalid;
1700             if (cell & dot)
1701               {
1702                 compileError (nested, "dot specified more than once.");
1703                 return 0;
1704               }
1705             cell |= dot;
1706             break;
1707           }
1708         case '0':               /*blank */
1709           if (started)
1710             goto invalid;
1711           break;
1712         case '-':               /*got all dots for this cell */
1713           if (!started)
1714             {
1715               compileError (nested, "missing cell specification.");
1716               return 0;
1717             }
1718           cells->chars[cellCount++] = cell | B16;
1719           cell = 0;
1720           start = index + 1;
1721           break;
1722         default:
1723         invalid:
1724           compileError (nested, "invalid dot number %s.", showString
1725                         (&character, 1));
1726           return 0;
1727         }
1728     }
1729   if (index == start)
1730     {
1731       compileError (nested, "missing cell specification.");
1732       return 0;
1733     }
1734   cells->chars[cellCount++] = cell | B16;       /*last cell */
1735   cells->length = cellCount;
1736   return 1;
1737 }
1738
1739 int
1740 extParseDots (const char *inString, widechar * outString)
1741 {
1742 /* Parse external dot patterns */
1743   CharsString wideIn;
1744   CharsString result;
1745   int k;
1746   for (k = 0; inString[k] && k < MAXSTRING; k++)
1747     wideIn.chars[k] = inString[k];
1748   wideIn.chars[k] = 0;
1749   wideIn.length = k;
1750   parseDots (NULL, &result, &wideIn);
1751   if (errorCount)
1752     {
1753       errorCount = 0;
1754       return 0;
1755     }
1756   for (k = 0; k < result.length; k++)
1757     outString[k] = result.chars[k];
1758   outString[k] = 0;
1759   return result.length;
1760 }
1761
1762 static int
1763 getCharacters (FileInfo * nested, CharsString * characters)
1764 {
1765 /*Get ruleChars string */
1766   CharsString token;
1767   if (getToken (nested, &token, "characters"))
1768     if (parseChars (nested, characters, &token))
1769       return 1;
1770   return 0;
1771 }
1772
1773 static int
1774 getRuleCharsText (FileInfo * nested, CharsString * ruleChars)
1775 {
1776   CharsString token;
1777   if (getToken (nested, &token, "Characters operand"))
1778     if (parseChars (nested, ruleChars, &token))
1779       return 1;
1780   return 0;
1781 }
1782
1783 static int
1784 getRuleDotsText (FileInfo * nested, CharsString * ruleDots)
1785 {
1786   CharsString token;
1787   if (getToken (nested, &token, "characters"))
1788     if (parseChars (nested, ruleDots, &token))
1789       return 1;
1790   return 0;
1791 }
1792
1793 static int
1794 getRuleDotsPattern (FileInfo * nested, CharsString * ruleDots)
1795 {
1796 /*Interpret the dets operand */
1797   CharsString token;
1798   if (getToken (nested, &token, "Dots operand"))
1799     {
1800       if (token.length == 1 && token.chars[0] == '=')
1801         {
1802           ruleDots->length = 0;
1803           return 1;
1804         }
1805       if (parseDots (nested, ruleDots, &token))
1806         return 1;
1807     }
1808   return 0;
1809 }
1810
1811 static int
1812 getCharacterClass (FileInfo * nested, const struct CharacterClass **class)
1813 {
1814   CharsString token;
1815   if (getToken (nested, &token, "character class name"))
1816     {
1817       if ((*class = findCharacterClass (&token)))
1818         return 1;
1819       compileError (nested, "character class not defined.");
1820     }
1821   return 0;
1822 }
1823
1824 static int compileFile (const char *fileName);
1825
1826 static int
1827 includeFile (FileInfo * nested, CharsString * includedFile)
1828 {
1829 /*Implement include opcode*/
1830   int k;
1831   char includeThis[MAXSTRING];
1832   for (k = 0; k < includedFile->length; k++)
1833     includeThis[k] = (char) includedFile->chars[k];
1834   includeThis[k] = 0;
1835   return compileFile (includeThis);
1836 }
1837
1838 struct RuleName
1839 {
1840   struct RuleName *next;
1841   TranslationTableOffset ruleOffset;
1842   widechar length;
1843   widechar name[1];
1844 };
1845 static struct RuleName *ruleNames = NULL;
1846 static TranslationTableOffset
1847 findRuleName (const CharsString * name)
1848 {
1849   const struct RuleName *nameRule = ruleNames;
1850   while (nameRule)
1851     {
1852       if ((name->length == nameRule->length) &&
1853           (memcmp (&name->chars[0], nameRule->name, CHARSIZE *
1854                    name->length) == 0))
1855         return nameRule->ruleOffset;
1856       nameRule = nameRule->next;
1857     }
1858   return 0;
1859 }
1860
1861 static int
1862 addRuleName (FileInfo * nested, CharsString * name)
1863 {
1864   int k;
1865   struct RuleName *nameRule;
1866   if (!(nameRule = malloc (sizeof (*nameRule) + CHARSIZE *
1867                            (name->length - 1))))
1868     {
1869       compileError (nested, "not enough memory");
1870       return 0;
1871     }
1872   memset (nameRule, 0, sizeof (*nameRule));
1873   for (k = 0; k < name->length; k++)
1874     {
1875       TranslationTableCharacter *ch = definedCharOrDots
1876         (nested, name->chars[k],
1877          0);
1878       if (!(ch->attributes & CTC_Letter))
1879         {
1880           compileError (nested, "a name may contain only letters");
1881           return 0;
1882         }
1883       nameRule->name[k] = name->chars[k];
1884     }
1885   nameRule->length = name->length;
1886   nameRule->ruleOffset = newRuleOffset;
1887   nameRule->next = ruleNames;
1888   ruleNames = nameRule;
1889   return 1;
1890 }
1891
1892 static void
1893 deallocateRuleNames (void)
1894 {
1895   while (ruleNames)
1896     {
1897       struct RuleName *nameRule = ruleNames;
1898       ruleNames = ruleNames->next;
1899       if (nameRule)
1900         free (nameRule);
1901     }
1902 }
1903
1904 static int
1905 compileSwapDots (FileInfo * nested, CharsString * source, CharsString * dest)
1906 {
1907   int k = 0;
1908   int kk = 0;
1909   CharsString dotsSource;
1910   CharsString dotsDest;
1911   dest->length = 0;
1912   dotsSource.length = 0;
1913   while (k <= source->length)
1914     {
1915       if (source->chars[k] != ',' && k != source->length)
1916         dotsSource.chars[dotsSource.length++] = source->chars[k];
1917       else
1918         {
1919           if (!parseDots (nested, &dotsDest, &dotsSource))
1920             return 0;
1921           dest->chars[dest->length++] = dotsDest.length + 1;
1922           for (kk = 0; kk < dotsDest.length; kk++)
1923             dest->chars[dest->length++] = dotsDest.chars[kk];
1924           dotsSource.length = 0;
1925         }
1926       k++;
1927     }
1928   return 1;
1929 }
1930
1931 static int
1932 compileSwap (FileInfo * nested, TranslationTableOpcode opcode)
1933 {
1934   CharsString ruleChars;
1935   CharsString ruleDots;
1936   CharsString name;
1937   CharsString matches;
1938   CharsString replacements;
1939   if (!getToken (nested, &name, "name operand"))
1940     return 0;
1941   if (!getToken (nested, &matches, "matches operand"))
1942     return 0;
1943   if (!getToken (nested, &replacements, "replacements operand"))
1944     return 0;
1945   if (opcode == CTO_SwapCc || opcode == CTO_SwapCd)
1946     {
1947       if (!parseChars (nested, &ruleChars, &matches))
1948         return 0;
1949     }
1950   else
1951     {
1952       if (!compileSwapDots (nested, &matches, &ruleChars))
1953         return 0;
1954     }
1955   if (opcode == CTO_SwapCc)
1956     {
1957       if (!parseChars (nested, &ruleDots, &replacements))
1958         return 0;
1959     }
1960   else
1961     {
1962       if (!compileSwapDots (nested, &replacements, &ruleDots))
1963         return 0;
1964     }
1965   if (!addRule (nested, opcode, &ruleChars, &ruleDots, 0, 0))
1966     return 0;
1967   if (!addRuleName (nested, &name))
1968     return 0;
1969   return 1;
1970 }
1971
1972
1973 static int
1974 getNumber (widechar * source, widechar * dest)
1975 {
1976 /*Convert a string of wide character digits to an integer*/
1977   int k = 0;
1978   *dest = 0;
1979   while (source[k] >= '0' && source[k] <= '9')
1980     *dest = 10 * *dest + (source[k++] - '0');
1981   return k;
1982 }
1983
1984 /* Start of multipass compiler*/
1985 static CharsString passRuleChars;
1986 static CharsString passRuleDots;
1987 static CharsString passHoldString;
1988 static CharsString passLine;
1989 static int passLinepos;
1990 static int passPrevLinepos;
1991 static widechar passHoldNumber;
1992 static widechar passEmphasis;
1993 static TranslationTableCharacterAttributes passAttributes;
1994 static FileInfo *passNested;
1995 static TranslationTableOpcode passOpcode;
1996 static widechar *passInstructions;
1997 static int passIC;
1998
1999 static int
2000 passGetAttributes ()
2001 {
2002   int more = 1;
2003   passAttributes = 0;
2004   while (more)
2005     {
2006       switch (passLine.chars[passLinepos])
2007         {
2008         case pass_any:
2009           passAttributes = 0xffffffff;
2010           break;
2011         case pass_digit:
2012           passAttributes |= CTC_Digit;
2013           break;
2014         case pass_litDigit:
2015           passAttributes |= CTC_LitDigit;
2016           break;
2017         case pass_letter:
2018           passAttributes |= CTC_Letter;
2019           break;
2020         case pass_math:
2021           passAttributes |= CTC_Math;
2022           break;
2023         case pass_punctuation:
2024           passAttributes |= CTC_Punctuation;
2025           break;
2026         case pass_sign:
2027           passAttributes |= CTC_Sign;
2028           break;
2029         case pass_space:
2030           passAttributes |= CTC_Space;
2031           break;
2032         case pass_uppercase:
2033           passAttributes |= CTC_UpperCase;
2034           break;
2035         case pass_lowercase:
2036           passAttributes |= CTC_LowerCase;
2037           break;
2038         case pass_class1:
2039           passAttributes |= CTC_Class1;
2040           break;
2041         case pass_class2:
2042           passAttributes |= CTC_Class2;
2043           break;
2044         case pass_class3:
2045           passAttributes |= CTC_Class3;
2046           break;
2047         case pass_class4:
2048           passAttributes |= CTC_Class4;
2049           break;
2050         default:
2051           more = 0;
2052           break;
2053         }
2054       if (more)
2055         passLinepos++;
2056     }
2057   if (!passAttributes)
2058     {
2059       compileError (passNested, "Missing attribute");
2060       passLinepos--;
2061       return 0;
2062     }
2063   return 1;
2064 }
2065
2066 static int
2067 passGetEmphasis ()
2068 {
2069   int more = 1;
2070   passLinepos++;
2071   passEmphasis = 0;
2072   while (more)
2073     {
2074       switch (passLine.chars[passLinepos])
2075         {
2076         case 'i':
2077           passEmphasis |= italic;
2078           break;
2079         case 'b':
2080           passEmphasis |= bold;
2081           break;
2082         case 'u':
2083           passEmphasis |= underline;
2084           break;
2085         case 'c':
2086           passEmphasis |= computer_braille;
2087           break;
2088         default:
2089           more = 0;
2090           break;
2091         }
2092       if (more)
2093         passLinepos++;
2094     }
2095   if (!passEmphasis)
2096     {
2097       compileError (passNested, "emphasis indicators expected");
2098       passLinepos--;
2099       return 0;
2100     }
2101   return 1;
2102 }
2103
2104 static int
2105 passGetDots ()
2106 {
2107   CharsString collectDots;
2108   collectDots.length = 0;
2109   while (passLinepos < passLine.length && (passLine.chars[passLinepos]
2110                                            == '-'
2111                                            || (passLine.chars[passLinepos] >=
2112                                                '0'
2113                                                && passLine.
2114                                                chars[passLinepos] <= '9')
2115                                            ||
2116                                            ((passLine.
2117                                              chars[passLinepos] | 32) >= 'a'
2118                                             && (passLine.
2119                                                 chars[passLinepos] | 32) <=
2120                                             'f')))
2121     collectDots.chars[collectDots.length++] = passLine.chars[passLinepos++];
2122   if (!parseDots (passNested, &passHoldString, &collectDots))
2123     return 0;
2124   return 1;
2125 }
2126
2127 static int
2128 passGetString ()
2129 {
2130   passHoldString.length = 0;
2131   while (1)
2132     {
2133       if (!passLine.chars[passLinepos])
2134         {
2135           compileError (passNested, "unterminated string");
2136           return 0;
2137         }
2138       if (passLine.chars[passLinepos] == 34)
2139         break;
2140       if (passLine.chars[passLinepos] == QUOTESUB)
2141         passHoldString.chars[passHoldString.length++] = 34;
2142       else
2143         passHoldString.chars[passHoldString.length++] =
2144           passLine.chars[passLinepos];
2145       passLinepos++;
2146     }
2147   passHoldString.chars[passHoldString.length] = 0;
2148   passLinepos++;
2149   return 1;
2150 }
2151
2152 static int
2153 passGetNumber ()
2154 {
2155   /*Convert a string of wide character digits to an integer */
2156   passHoldNumber = 0;
2157   while (passLine.chars[passLinepos] >= '0'
2158          && passLine.chars[passLinepos] <= '9')
2159     passHoldNumber =
2160       10 * passHoldNumber + (passLine.chars[passLinepos++] - '0');
2161   return 1;
2162 }
2163
2164 static int
2165 passGetName ()
2166 {
2167   TranslationTableCharacterAttributes attr;
2168   passHoldString.length = 0;
2169   do
2170     {
2171       attr = definedCharOrDots (passNested, passLine.chars[passLinepos],
2172                                 0)->attributes;
2173       if (passHoldString.length == 0)
2174         {
2175           if (!(attr & CTC_Letter))
2176             {
2177               passLinepos++;
2178               continue;
2179             }
2180         }
2181       if (!(attr & CTC_Letter))
2182         break;
2183       passHoldString.chars[passHoldString.length++] =
2184         passLine.chars[passLinepos];
2185       passLinepos++;
2186     }
2187   while (passLinepos < passLine.length);
2188   return 1;
2189 }
2190
2191 static int
2192 passIsKeyword (const char *token)
2193 {
2194   int k;
2195   int length = strlen (token);
2196   int ch = passLine.chars[passLinepos + length + 1];
2197   if (((ch | 32) >= 'a' && (ch | 32) <= 'z') || (ch >= '0' && ch <= '9'))
2198     return 0;
2199   for (k = 0; k < length && passLine.chars[passLinepos + k + 1]
2200        == (widechar) token[k]; k++);
2201   if (k == length)
2202     {
2203       passLinepos += length + 1;
2204       return 1;
2205     }
2206   return 0;
2207 }
2208
2209 struct PassName
2210 {
2211   struct PassName *next;
2212   int varnum;
2213   widechar length;
2214   widechar name[1];
2215 };
2216 static struct PassName *passNames = NULL;
2217
2218 static int
2219 passFindName (const CharsString * name)
2220 {
2221   const struct PassName *curname = passNames;
2222   CharsString augmentedName;
2223   for (augmentedName.length = 0; augmentedName.length < name->length;
2224        augmentedName.length++)
2225     augmentedName.chars[augmentedName.length] =
2226       name->chars[augmentedName.length];
2227   augmentedName.chars[augmentedName.length++] = passOpcode;
2228   while (curname)
2229     {
2230       if ((augmentedName.length == curname->length) &&
2231           (memcmp
2232            (&augmentedName.chars[0], curname->name,
2233             CHARSIZE * name->length) == 0))
2234         return curname->varnum;
2235       curname = curname->next;
2236     }
2237   compileError (passNested, "name not found");
2238   return 0;
2239 }
2240
2241 static int
2242 passAddName (CharsString * name, int var)
2243 {
2244   int k;
2245   struct PassName *curname;
2246   CharsString augmentedName;
2247   for (augmentedName.length = 0;
2248        augmentedName.length < name->length; augmentedName.length++)
2249     augmentedName.
2250       chars[augmentedName.length] = name->chars[augmentedName.length];
2251   augmentedName.chars[augmentedName.length++] = passOpcode;
2252   if (!
2253       (curname =
2254        malloc (sizeof (*curname) + CHARSIZE * (augmentedName.length - 1))))
2255     {
2256       compileError (passNested, "not enough memory");
2257       return 0;
2258     }
2259   memset (curname, 0, sizeof (*curname));
2260   for (k = 0; k < augmentedName.length; k++)
2261     {
2262       curname->name[k] = augmentedName.chars[k];
2263     }
2264   curname->length = augmentedName.length;
2265   curname->varnum = var;
2266   curname->next = passNames;
2267   passNames = curname;
2268   return 1;
2269 }
2270
2271 static pass_Codes
2272 passGetScriptToken ()
2273 {
2274   while (passLinepos < passLine.length)
2275     {
2276       passPrevLinepos = passLinepos;
2277       switch (passLine.chars[passLinepos])
2278         {
2279         case '\"':
2280           passLinepos++;
2281           if (passGetString ())
2282             return pass_string;
2283           return pass_invalidToken;
2284         case '@':
2285           passLinepos++;
2286           if (passGetDots ())
2287             return pass_dots;
2288           return pass_invalidToken;
2289         case '#':               /*comment */
2290           passLinepos = passLine.length + 1;
2291           return pass_noMoreTokens;
2292         case '!':
2293           if (passLine.chars[passLinepos + 1] == '=')
2294             {
2295               passLinepos += 2;
2296               return pass_noteq;
2297             }
2298           passLinepos++;
2299           return pass_not;
2300         case '-':
2301           passLinepos++;
2302           return pass_hyphen;
2303         case '=':
2304           passLinepos++;
2305           return pass_eq;
2306         case '<':
2307           passLinepos++;
2308           if (passLine.chars[passLinepos] == '=')
2309             {
2310               passLinepos++;
2311               return pass_lteq;
2312             }
2313           return pass_lt;
2314         case '>':
2315           passLinepos++;
2316           if (passLine.chars[passLinepos] == '=')
2317             {
2318               passLinepos++;
2319               return pass_gteq;
2320             }
2321           return pass_gt;
2322         case '+':
2323           passLinepos++;
2324           return pass_plus;
2325         case '(':
2326           passLinepos++;
2327           return pass_leftParen;
2328         case ')':
2329           passLinepos++;
2330           return pass_rightParen;
2331         case ',':
2332           passLinepos++;
2333           return pass_comma;
2334         case '&':
2335           if (passLine.chars[passLinepos = 1] == '&')
2336             {
2337               passLinepos += 2;
2338               return pass_and;
2339             }
2340           return pass_invalidToken;
2341         case '|':
2342           if (passLine.chars[passLinepos + 1] == '|')
2343             {
2344               passLinepos += 2;
2345               return pass_or;
2346             }
2347           return pass_invalidToken;
2348         case 'a':
2349           if (passIsKeyword ("ttr"))
2350             return pass_attributes;
2351           passGetName ();
2352           return pass_nameFound;
2353         case 'b':
2354           if (passIsKeyword ("ack"))
2355             return pass_lookback;
2356           if (passIsKeyword ("ool"))
2357             return pass_boolean;
2358           passGetName ();
2359           return pass_nameFound;
2360         case 'c':
2361           if (passIsKeyword ("lass"))
2362             return pass_class;
2363           passGetName ();
2364           return pass_nameFound;
2365         case 'd':
2366           if (passIsKeyword ("ef"))
2367             return pass_define;
2368           passGetName ();
2369           return pass_nameFound;
2370         case 'e':
2371           if (passIsKeyword ("mph"))
2372             return pass_emphasis;
2373           passGetName ();
2374           return pass_nameFound;
2375         case 'f':
2376           if (passIsKeyword ("ind"))
2377             return pass_search;
2378           if (passIsKeyword ("irst"))
2379             return pass_first;
2380           passGetName ();
2381           return pass_nameFound;
2382         case 'g':
2383           if (passIsKeyword ("roup"))
2384             return pass_group;
2385           passGetName ();
2386           return pass_nameFound;
2387         case 'i':
2388           if (passIsKeyword ("f"))
2389             return pass_if;
2390           passGetName ();
2391           return pass_nameFound;
2392         case 'l':
2393           if (passIsKeyword ("ast"))
2394             return pass_last;
2395           passGetName ();
2396           return pass_nameFound;
2397         case 'm':
2398           if (passIsKeyword ("ark"))
2399             return pass_mark;
2400           passGetName ();
2401           return pass_nameFound;
2402         case 'r':
2403           if (passIsKeyword ("epgroup"))
2404             return pass_repGroup;
2405           if (passIsKeyword ("epcopy"))
2406             return pass_copy;
2407           if (passIsKeyword ("epomit"))
2408             return pass_omit;
2409           if (passIsKeyword ("ep"))
2410             return pass_replace;
2411           passGetName ();
2412           return pass_nameFound;
2413         case 's':
2414           if (passIsKeyword ("cript"))
2415             return pass_script;
2416           if (passIsKeyword ("wap"))
2417             return pass_swap;
2418           passGetName ();
2419           return pass_nameFound;
2420         case 't':
2421           if (passIsKeyword ("hen"))
2422             return pass_then;
2423           passGetName ();
2424           return pass_nameFound;
2425         default:
2426           if (passLine.chars[passLinepos] <= 32)
2427             {
2428               passLinepos++;
2429               break;
2430             }
2431           if (passLine.chars[passLinepos] >= '0'
2432               && passLine.chars[passLinepos] <= '9')
2433             {
2434               passGetNumber ();
2435               return pass_numberFound;
2436             }
2437           else
2438             {
2439               if (!passGetName ())
2440                 return pass_invalidToken;
2441               else
2442                 return pass_nameFound;
2443             }
2444         }
2445     }
2446   return pass_noMoreTokens;
2447 }
2448
2449 static int
2450 passIsLeftParen ()
2451 {
2452   pass_Codes passCode = passGetScriptToken ();
2453   if (passCode != pass_leftParen)
2454     {
2455       compileError (passNested, "'(' expected");
2456       return 0;
2457     }
2458   return 1;
2459 }
2460
2461 static int
2462 passIsName ()
2463 {
2464   pass_Codes passCode = passGetScriptToken ();
2465   if (passCode != pass_nameFound)
2466     {
2467       compileError (passNested, "a name expected");
2468       return 0;
2469     }
2470   return 1;
2471 }
2472
2473 static int
2474 passIsComma ()
2475 {
2476   pass_Codes passCode = passGetScriptToken ();
2477   if (passCode != pass_comma)
2478     {
2479       compileError (passNested, "',' expected");
2480       return 0;
2481     }
2482   return 1;
2483 }
2484
2485 static int
2486 passIsNumber ()
2487 {
2488   pass_Codes passCode = passGetScriptToken ();
2489   if (passCode != pass_numberFound)
2490     {
2491       compileError (passNested, "a number expected");
2492       return 0;
2493     }
2494   return 1;
2495 }
2496
2497 static int
2498 passIsRightParen ()
2499 {
2500   pass_Codes passCode = passGetScriptToken ();
2501   if (passCode != pass_rightParen)
2502     {
2503       compileError (passNested, "')' expected");
2504       return 0;
2505     }
2506   return 1;
2507 }
2508
2509 static int
2510 passGetRange ()
2511 {
2512   pass_Codes passCode = passGetScriptToken ();
2513   if (!(passCode == pass_comma || passCode == pass_rightParen))
2514     {
2515       compileError (passNested, "invalid range");
2516       return 0;
2517     }
2518   if (passCode == pass_rightParen)
2519     {
2520       passInstructions[passIC++] = 1;
2521       passInstructions[passIC++] = 1;
2522       return 1;
2523     }
2524   if (!passIsNumber ())
2525     return 0;
2526   passInstructions[passIC++] = passHoldNumber;
2527   passCode = passGetScriptToken ();
2528   if (!(passCode == pass_comma || passCode == pass_rightParen))
2529     {
2530       compileError (passNested, "invalid range");
2531       return 0;
2532     }
2533   if (passCode == pass_rightParen)
2534     {
2535       passInstructions[passIC++] = passHoldNumber;
2536       return 1;
2537     }
2538   if (!passIsNumber ())
2539     return 0;
2540   passInstructions[passIC++] = passHoldNumber;
2541   if (!passIsRightParen ())
2542     return 0;
2543   return 1;
2544 }
2545
2546 static int
2547 passInsertAttributes ()
2548 {
2549   passInstructions[passIC++] = pass_attributes;
2550   passInstructions[passIC++] = passAttributes >> 16;
2551   passInstructions[passIC++] = passAttributes & 0xffff;
2552   if (!passGetRange ())
2553     return 0;
2554   return 1;
2555 }
2556
2557 static int
2558 compilePassOpcode (FileInfo * nested, TranslationTableOpcode opcode)
2559 {
2560 /*Compile the operands of a pass opcode */
2561   TranslationTableCharacterAttributes after = 0;
2562   TranslationTableCharacterAttributes before = 0;
2563   widechar passSubOp;
2564   const struct CharacterClass *class;
2565   TranslationTableOffset ruleOffset = 0;
2566   TranslationTableRule *rule = NULL;
2567   int k;
2568   int kk = 0;
2569   pass_Codes passCode;
2570   int endTest = 0;
2571   int isScript = 1;
2572   passInstructions = passRuleDots.chars;
2573   passIC = 0;                   /*Instruction counter */
2574   passRuleChars.length = 0;
2575   passNested = nested;
2576   passOpcode = opcode;
2577 /* passHoldString and passLine are static variables declared 
2578  * previously.*/
2579   passLinepos = 0;
2580   passHoldString.length = 0;
2581   for (k = nested->linepos; k < nested->linelen; k++)
2582     passHoldString.chars[passHoldString.length++] = nested->line[k];
2583   if (!eqasc2uni ((unsigned char *) "script", passHoldString.chars, 6))
2584     {
2585       isScript = 0;
2586 #define SEPCHAR 0x0001
2587       for (k = 0; k < passHoldString.length && passHoldString.chars[k] > 32;
2588            k++);
2589       if (k < passHoldString.length)
2590         passHoldString.chars[k] = SEPCHAR;
2591       else
2592         {
2593           compileError (passNested, "Invalid multipass operands");
2594           return 0;
2595         }
2596     }
2597   parseChars (passNested, &passLine, &passHoldString);
2598   if (isScript)
2599     {
2600       int more = 1;
2601       passCode = passGetScriptToken ();
2602       if (passCode != pass_script)
2603         {
2604           compileError (passNested, "Invalid multipass statement");
2605           return 0;
2606         }
2607       /* Declaratives */
2608       while (more)
2609         {
2610           passCode = passGetScriptToken ();
2611           switch (passCode)
2612             {
2613             case pass_define:
2614               if (!passIsLeftParen ())
2615                 return 0;
2616               if (!passIsName ())
2617                 return 0;
2618               if (!passIsComma ())
2619                 return 0;
2620               if (!passIsNumber ())
2621                 return 0;
2622               if (!passIsRightParen ())
2623                 return 0;
2624               passAddName (&passHoldString, passHoldNumber);
2625               break;
2626             case pass_if:
2627               more = 0;
2628               break;
2629             default:
2630               compileError (passNested,
2631                             "invalid definition in declarative part");
2632               return 0;
2633             }
2634         }
2635       /* if part */
2636       more = 1;
2637       while (more)
2638         {
2639           passCode = passGetScriptToken ();
2640           passSubOp = passCode;
2641           switch (passCode)
2642             {
2643             case pass_not:
2644               passInstructions[passIC++] = pass_not;
2645               break;
2646             case pass_first:
2647               passInstructions[passIC++] = pass_first;
2648               break;
2649             case pass_last:
2650               passInstructions[passIC++] = pass_last;
2651               break;
2652             case pass_search:
2653               passInstructions[passIC++] = pass_search;
2654               break;
2655             case pass_string:
2656               if (opcode != CTO_Context && opcode != CTO_Correct)
2657                 {
2658                   compileError (passNested,
2659                                 "Character strings can only be used with the context and correct opcodes.");
2660                   return 0;
2661                 }
2662               passInstructions[passIC++] = pass_string;
2663               goto ifDoCharsDots;
2664             case pass_dots:
2665               if (passOpcode == CTO_Correct || passOpcode == CTO_Context)
2666                 {
2667                   compileError (passNested,
2668                                 "dot patterns cannot be specified in the if part\
2669  of the correct or context opcodes");
2670                   return 0;
2671                 }
2672               passInstructions[passIC++] = pass_dots;
2673             ifDoCharsDots:
2674               passInstructions[passIC++] = passHoldString.length;
2675               for (kk = 0; kk < passHoldString.length; kk++)
2676                 passInstructions[passIC++] = passHoldString.chars[kk];
2677               break;
2678             case pass_attributes:
2679               if (!passIsLeftParen ())
2680                 return 0;
2681               if (!passGetAttributes ())
2682                 return 0;
2683               if (!passInsertAttributes ())
2684                 return 0;
2685               break;
2686             case pass_emphasis:
2687               if (!passIsLeftParen ())
2688                 return 0;
2689               if (!passGetEmphasis ())
2690                 return 0;
2691               /*Right parenthis handled by subfunctiion */
2692               break;
2693             case pass_lookback:
2694               passInstructions[passIC++] = pass_lookback;
2695               passCode = passGetScriptToken ();
2696               if (passCode != pass_leftParen)
2697                 {
2698                   passInstructions[passIC++] = 1;
2699                   passLinepos = passPrevLinepos;
2700                   break;
2701                 }
2702               if (!passIsNumber ())
2703                 return 0;
2704               if (!passIsRightParen ())
2705                 return 0;
2706               passInstructions[passIC] = passHoldNumber;
2707               break;
2708             case pass_group:
2709               if (!passIsLeftParen ())
2710                 return 0;
2711               break;
2712             case pass_mark:
2713               passInstructions[passIC++] = pass_startReplace;
2714               passInstructions[passIC++] = pass_endReplace;
2715               break;
2716             case pass_replace:
2717               passInstructions[passIC++] = pass_startReplace;
2718               if (!passIsLeftParen ())
2719                 return 0;
2720               break;
2721             case pass_rightParen:
2722               passInstructions[passIC++] = pass_endReplace;
2723               break;
2724             case pass_groupstart:
2725             case pass_groupend:
2726               if (!passIsLeftParen ())
2727                 return 0;
2728               if (!passGetName ())
2729                 return 0;
2730               if (!passIsRightParen ())
2731                 return 0;
2732               ruleOffset = findRuleName (&passHoldString);
2733               if (ruleOffset)
2734                 rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
2735               if (rule && rule->opcode == CTO_Grouping)
2736                 {
2737                   passInstructions[passIC++] = passSubOp;
2738                   passInstructions[passIC++] = ruleOffset >> 16;
2739                   passInstructions[passIC++] = ruleOffset & 0xffff;
2740                   break;
2741                 }
2742               else
2743                 {
2744                   compileError (passNested, "%s is not a grouping name",
2745                                 showString (&passHoldString.chars[0],
2746                                             passHoldString.length));
2747                   return 0;
2748                 }
2749               break;
2750             case pass_class:
2751               if (!passIsLeftParen ())
2752                 return 0;
2753               if (!passGetName ())
2754                 return 0;
2755               if (!passIsRightParen ())
2756                 return 0;
2757               if (!(class = findCharacterClass (&passHoldString)))
2758                 return 0;
2759               passAttributes = class->attribute;
2760               passInsertAttributes ();
2761               break;
2762             case pass_swap:
2763               ruleOffset = findRuleName (&passHoldString);
2764               if (!passIsLeftParen ())
2765                 return 0;
2766               if (!passGetName ())
2767                 return 0;
2768               if (!passIsRightParen ())
2769                 return 0;
2770               ruleOffset = findRuleName (&passHoldString);
2771               if (ruleOffset)
2772                 rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
2773               if (rule
2774                   && (rule->opcode == CTO_SwapCc || rule->opcode == CTO_SwapCd
2775                       || rule->opcode == CTO_SwapDd))
2776                 {
2777                   passInstructions[passIC++] = pass_swap;
2778                   passInstructions[passIC++] = ruleOffset >> 16;
2779                   passInstructions[passIC++] = ruleOffset & 0xffff;
2780                   if (!passGetRange ())
2781                     return 0;
2782                   break;
2783                 }
2784               compileError (passNested,
2785                             "%s is not a swap name.",
2786                             showString (&passHoldString.chars[0],
2787                                         passHoldString.length));
2788               return 0;
2789             case pass_nameFound:
2790               passHoldNumber = passFindName (&passHoldString);
2791               passCode = passGetScriptToken ();
2792               if (!(passCode == pass_eq || passCode == pass_lt || passCode
2793                     == pass_gt || passCode == pass_noteq || passCode ==
2794                     pass_lteq || passCode == pass_gteq))
2795                 {
2796                   compileError (nested,
2797                                 "invalid comparison operator in if part");
2798                   return 0;
2799                 }
2800               passInstructions[passIC++] = passCode;
2801               passInstructions[passIC++] = passHoldNumber;
2802               if (!passIsNumber ())
2803                 return 0;
2804               passInstructions[passIC++] = passHoldNumber;
2805               break;
2806             case pass_then:
2807               passInstructions[passIC++] = pass_endTest;
2808               more = 0;
2809               break;
2810             default:
2811               compileError (passNested, "invalid choice in if part");
2812               return 0;
2813             }
2814         }
2815
2816       /* then part */
2817       more = 1;
2818       while (more)
2819         {
2820           passCode = passGetScriptToken ();
2821           passSubOp = passCode;
2822           switch (passCode)
2823             {
2824             case pass_string:
2825               if (opcode != CTO_Correct)
2826                 {
2827                   compileError (passNested,
2828                                 "Character strings can only be used in the then part with the correct opcode.");
2829                   return 0;
2830                 }
2831               passInstructions[passIC++] = pass_string;
2832               goto thenDoCharsDots;
2833             case pass_dots:
2834               if (opcode == CTO_Correct)
2835                 {
2836                   compileError (passNested,
2837                                 "Dot patterns cannot be used with the correct opcode.");
2838                   return 0;
2839                 }
2840               passInstructions[passIC++] = pass_dots;
2841             thenDoCharsDots:
2842               passInstructions[passIC++] = passHoldString.length;
2843               for (kk = 0; kk < passHoldString.length; kk++)
2844                 passInstructions[passIC++] = passHoldString.chars[kk];
2845               break;
2846             case pass_nameFound:
2847               passHoldNumber = passFindName (&passHoldString);
2848               passCode = passGetScriptToken ();
2849               if (!(passCode == pass_plus || passCode == pass_hyphen
2850                     || passCode == pass_eq))
2851                 {
2852                   compileError (nested,
2853                                 "Invalid variable operator in then part");
2854                   return 0;
2855                 }
2856               passInstructions[passIC++] = passCode;
2857               passInstructions[passIC++] = passHoldNumber;
2858               if (!passIsNumber ())
2859                 return 0;
2860               passInstructions[passIC++] = passHoldNumber;
2861               break;
2862             case pass_copy:
2863               passInstructions[passIC++] = pass_copy;
2864               break;
2865             case pass_omit:
2866               passInstructions[passIC++] = pass_omit;
2867               break;
2868             case pass_swap:
2869               ruleOffset = findRuleName (&passHoldString);
2870               if (!passIsLeftParen ())
2871                 return 0;
2872               if (!passGetName ())
2873                 return 0;
2874               if (!passIsRightParen ())
2875                 return 0;
2876               ruleOffset = findRuleName (&passHoldString);
2877               if (ruleOffset)
2878                 rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
2879               if (rule
2880                   && (rule->opcode == CTO_SwapCc || rule->opcode == CTO_SwapCd
2881                       || rule->opcode == CTO_SwapDd))
2882                 {
2883                   passInstructions[passIC++] = pass_swap;
2884                   passInstructions[passIC++] = ruleOffset >> 16;
2885                   passInstructions[passIC++] = ruleOffset & 0xffff;
2886                   if (!passGetRange ())
2887                     return 0;
2888                   break;
2889                 }
2890               compileError (passNested,
2891                             "%s is not a swap name.",
2892                             showString (&passHoldString.chars[0],
2893                                         passHoldString.length));
2894               return 0;
2895             case pass_noMoreTokens:
2896               more = 0;
2897               break;
2898             default:
2899               compileError (passNested, "invalid action in then part");
2900               return 0;
2901             }
2902         }
2903     }
2904   else
2905     {
2906       /* Older machine-language-like "assembler". */
2907
2908       /*Compile test part */
2909       for (k = 0; k < passLine.length && passLine.chars[k] != SEPCHAR; k++);
2910       endTest = k;
2911       passLine.chars[endTest] = pass_endTest;
2912       passLinepos = 0;
2913       while (passLinepos <= endTest)
2914         {
2915           switch ((passSubOp = passLine.chars[passLinepos]))
2916             {
2917             case pass_lookback:
2918               passInstructions[passIC++] = pass_lookback;
2919               passLinepos++;
2920               passGetNumber ();
2921               if (passHoldNumber == 0)
2922                 passHoldNumber = 1;
2923               passInstructions[passIC++] = passHoldNumber;
2924               break;
2925             case pass_not:
2926               passInstructions[passIC++] = pass_not;
2927               passLinepos++;
2928               break;
2929             case pass_first:
2930               passInstructions[passIC++] = pass_first;
2931               passLinepos++;
2932               break;
2933             case pass_last:
2934               passInstructions[passIC++] = pass_last;
2935               passLinepos++;
2936               break;
2937             case pass_search:
2938               passInstructions[passIC++] = pass_search;
2939               passLinepos++;
2940               break;
2941             case pass_string:
2942               if (opcode != CTO_Context && opcode != CTO_Correct)
2943                 {
2944                   compileError (passNested,
2945                                 "Character strings can only be used with the context and correct opcodes.");
2946                   return 0;
2947                 }
2948               passLinepos++;
2949               passInstructions[passIC++] = pass_string;
2950               passGetString ();
2951               goto testDoCharsDots;
2952             case pass_dots:
2953               passLinepos++;
2954               passInstructions[passIC++] = pass_dots;
2955               passGetDots ();
2956             testDoCharsDots:
2957               if (passHoldString.length == 0)
2958                 return 0;
2959               passInstructions[passIC++] = passHoldString.length;
2960               for (kk = 0; kk < passHoldString.length; kk++)
2961                 passInstructions[passIC++] = passHoldString.chars[kk];
2962               break;
2963             case pass_startReplace:
2964               passInstructions[passIC++] = pass_startReplace;
2965               passLinepos++;
2966               break;
2967             case pass_endReplace:
2968               passInstructions[passIC++] = pass_endReplace;
2969               passLinepos++;
2970               break;
2971             case pass_variable:
2972               passLinepos++;
2973               passGetNumber ();
2974               switch (passLine.chars[passLinepos])
2975                 {
2976                 case pass_eq:
2977                   passInstructions[passIC++] = pass_eq;
2978                   goto doComp;
2979                 case pass_lt:
2980                   if (passLine.chars[passLinepos + 1] == pass_eq)
2981                     {
2982                       passLinepos++;
2983                       passInstructions[passIC++] = pass_lteq;
2984                     }
2985                   else
2986                     passInstructions[passIC++] = pass_lt;
2987                   goto doComp;
2988                 case pass_gt:
2989                   if (passLine.chars[passLinepos + 1] == pass_eq)
2990                     {
2991                       passLinepos++;
2992                       passInstructions[passIC++] = pass_gteq;
2993                     }
2994                   else
2995                     passInstructions[passIC++] = pass_gt;
2996                 doComp:
2997                   passInstructions[passIC++] = passHoldNumber;
2998                   passLinepos++;
2999                   passGetNumber ();
3000                   passInstructions[passIC++] = passHoldNumber;
3001                   break;
3002                 default:
3003                   compileError (passNested, "incorrect comparison operator");
3004                   return 0;
3005                 }
3006               break;
3007             case pass_attributes:
3008               passLinepos++;
3009               passGetAttributes ();
3010             insertAttributes:
3011               passInstructions[passIC++] = pass_attributes;
3012               passInstructions[passIC++] = passAttributes >> 16;
3013               passInstructions[passIC++] = passAttributes & 0xffff;
3014             getRange:
3015               if (passLine.chars[passLinepos] == pass_until)
3016                 {
3017                   passLinepos++;
3018                   passInstructions[passIC++] = 1;
3019                   passInstructions[passIC++] = 0xffff;
3020                   break;
3021                 }
3022               passGetNumber ();
3023               if (passHoldNumber == 0)
3024                 {
3025                   passHoldNumber = passInstructions[passIC++] = 1;
3026                   passInstructions[passIC++] = 1;       /*This is not an error */
3027                   break;
3028                 }
3029               passInstructions[passIC++] = passHoldNumber;
3030               if (passLine.chars[passLinepos] != pass_hyphen)
3031                 {
3032                   passInstructions[passIC++] = passHoldNumber;
3033                   break;
3034                 }
3035               passLinepos++;
3036               passGetNumber ();
3037               if (passHoldNumber == 0)
3038                 {
3039                   compileError (passNested, "invalid range");
3040                   return 0;
3041                 }
3042               passInstructions[passIC++] = passHoldNumber;
3043               break;
3044             case pass_groupstart:
3045             case pass_groupend:
3046               passLinepos++;
3047               passGetName ();
3048               ruleOffset = findRuleName (&passHoldString);
3049               if (ruleOffset)
3050                 rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
3051               if (rule && rule->opcode == CTO_Grouping)
3052                 {
3053                   passInstructions[passIC++] = passSubOp;
3054                   passInstructions[passIC++] = ruleOffset >> 16;
3055                   passInstructions[passIC++] = ruleOffset & 0xffff;
3056                   break;
3057                 }
3058               else
3059                 {
3060                   compileError (passNested, "%s is not a grouping name",
3061                                 showString (&passHoldString.chars[0],
3062                                             passHoldString.length));
3063                   return 0;
3064                 }
3065               break;
3066             case pass_swap:
3067               passGetName ();
3068               if ((class = findCharacterClass (&passHoldString)))
3069                 {
3070                   passAttributes = class->attribute;
3071                   goto insertAttributes;
3072                 }
3073               ruleOffset = findRuleName (&passHoldString);
3074               if (ruleOffset)
3075                 rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
3076               if (rule
3077                   && (rule->opcode == CTO_SwapCc || rule->opcode == CTO_SwapCd
3078                       || rule->opcode == CTO_SwapDd))
3079                 {
3080                   passInstructions[passIC++] = pass_swap;
3081                   passInstructions[passIC++] = ruleOffset >> 16;
3082                   passInstructions[passIC++] = ruleOffset & 0xffff;
3083                   goto getRange;
3084                 }
3085               compileError (passNested,
3086                             "%s is neither a class name nor a swap name.",
3087                             showString (&passHoldString.chars[0],
3088                                         passHoldString.length));
3089               return 0;
3090             case pass_endTest:
3091               passInstructions[passIC++] = pass_endTest;
3092               passLinepos++;
3093               break;
3094             default:
3095               compileError (passNested,
3096                             "incorrect operator '%c ' in test part",
3097                             passLine.chars[passLinepos]);
3098               return 0;
3099             }
3100
3101         }                       /*Compile action part */
3102
3103       /* Compile action part */
3104       while (passLinepos < passLine.length &&
3105              passLine.chars[passLinepos] <= 32)
3106         passLinepos++;
3107       while (passLinepos < passLine.length &&
3108              passLine.chars[passLinepos] > 32)
3109         {
3110           switch ((passSubOp = passLine.chars[passLinepos]))
3111             {
3112             case pass_string:
3113               if (opcode != CTO_Correct)
3114                 {
3115                   compileError (passNested,
3116                                 "Character strings can only be used with the ccorrect opcode.");
3117                   return 0;
3118                 }
3119               passLinepos++;
3120               passInstructions[passIC++] = pass_string;
3121               passGetString ();
3122               goto actionDoCharsDots;
3123             case pass_dots:
3124               if (opcode == CTO_Correct)
3125                 {
3126                   compileError (passNested,
3127                                 "Dot patterns cannot be used with the correct opcode.");
3128                   return 0;
3129                 }
3130               passLinepos++;
3131               passGetDots ();
3132               passInstructions[passIC++] = pass_dots;
3133             actionDoCharsDots:
3134               if (passHoldString.length == 0)
3135                 return 0;
3136               passInstructions[passIC++] = passHoldString.length;
3137               for (kk = 0; kk < passHoldString.length; kk++)
3138                 passInstructions[passIC++] = passHoldString.chars[kk];
3139               break;
3140             case pass_variable:
3141               passLinepos++;
3142               passGetNumber ();
3143               switch (passLine.chars[passLinepos])
3144                 {
3145                 case pass_eq:
3146                   passInstructions[passIC++] = pass_eq;
3147                   passInstructions[passIC++] = passHoldNumber;
3148                   passLinepos++;
3149                   passGetNumber ();
3150                   passInstructions[passIC++] = passHoldNumber;
3151                   break;
3152                 case pass_plus:
3153                 case pass_hyphen:
3154                   passInstructions[passIC++] = passLine.chars[passLinepos];
3155                   passInstructions[passIC++] = passHoldNumber;
3156                   break;
3157                 default:
3158                   compileError (passNested,
3159                                 "incorrect variable operator in action part");
3160                   return 0;
3161                 }
3162               break;
3163             case pass_copy:
3164               passInstructions[passIC++] = pass_copy;
3165               passLinepos++;
3166               break;
3167             case pass_omit:
3168               passInstructions[passIC++] = pass_omit;
3169               passLinepos++;
3170               break;
3171             case pass_groupreplace:
3172             case pass_groupstart:
3173             case pass_groupend:
3174               passLinepos++;
3175               passGetName ();
3176               ruleOffset = findRuleName (&passHoldString);
3177               if (ruleOffset)
3178                 rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
3179               if (rule && rule->opcode == CTO_Grouping)
3180                 {
3181                   passInstructions[passIC++] = passSubOp;
3182                   passInstructions[passIC++] = ruleOffset >> 16;
3183                   passInstructions[passIC++] = ruleOffset & 0xffff;
3184                   break;
3185                 }
3186               compileError (passNested, "%s is not a grouping name",
3187                             showString (&passHoldString.chars[0],
3188                                         passHoldString.length));
3189               return 0;
3190             case pass_swap:
3191               passLinepos++;
3192               passGetName ();
3193               ruleOffset = findRuleName (&passHoldString);
3194               if (ruleOffset)
3195                 rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
3196               if (rule
3197                   && (rule->opcode == CTO_SwapCc || rule->opcode == CTO_SwapCd
3198                       || rule->opcode == CTO_SwapDd))
3199                 {
3200                   passInstructions[passIC++] = pass_swap;
3201                   passInstructions[passIC++] = ruleOffset >> 16;
3202                   passInstructions[passIC++] = ruleOffset & 0xffff;
3203                   break;
3204                 }
3205               compileError (passNested, "%s is not a swap name.",
3206                             showString (&passHoldString.chars[0],
3207                                         passHoldString.length));
3208               return 0;
3209               break;
3210             default:
3211               compileError (passNested, "incorrect operator in action part");
3212               return 0;
3213             }
3214         }
3215     }
3216
3217   /*Analyze and add rule */
3218   passRuleDots.length = passIC;
3219   passIC = 0;
3220   while (passIC < passRuleDots.length)
3221     {
3222       int start = 0;
3223       switch (passInstructions[passIC])
3224         {
3225         case pass_string:
3226         case pass_dots:
3227         case pass_attributes:
3228         case pass_swap:
3229           start = 1;
3230           break;
3231         case pass_groupstart:
3232         case pass_groupend:
3233           start = 1;
3234           break;
3235         case pass_eq:
3236         case pass_lt:
3237         case pass_gt:
3238         case pass_lteq:
3239         case pass_gteq:
3240           passIC += 3;
3241           break;
3242         case pass_lookback:
3243           passIC += 2;
3244           break;
3245         case pass_not:
3246         case pass_startReplace:
3247         case pass_endReplace:
3248         case pass_first:
3249           passIC++;
3250           break;
3251         default:
3252           compileError (passNested,
3253                         "Test/if part must contain characters, dots, attributes or class \
3254 swap.");
3255           return 0;
3256         }
3257       if (start)
3258         break;
3259     }
3260
3261   switch (passInstructions[passIC])
3262     {
3263     case pass_string:
3264     case pass_dots:
3265       for (k = 0; k < passInstructions[passIC + 1]; k++)
3266         passRuleChars.chars[k] = passInstructions[passIC + 2 + k];
3267       passRuleChars.length = k;
3268       after = before = 0;
3269       break;
3270     case pass_attributes:
3271     case pass_groupstart:
3272     case pass_groupend:
3273     case pass_swap:
3274       after = passRuleDots.length;
3275       before = 0;
3276       break;
3277     default:
3278       break;
3279     }
3280   if (!addRule (passNested, opcode, &passRuleChars, &passRuleDots,
3281                 after, before))
3282     return 0;
3283   return 1;
3284 }
3285
3286 /* End of multipass compiler */
3287
3288 static int
3289 compileBrailleIndicator (FileInfo * nested, char *ermsg,
3290                          TranslationTableOpcode opcode,
3291                          TranslationTableOffset * rule)
3292 {
3293   CharsString token;
3294   CharsString cells;
3295   if (getToken (nested, &token, ermsg))
3296     if (parseDots (nested, &cells, &token))
3297       if (!addRule (nested, opcode, NULL, &cells, 0, 0))
3298         return 0;
3299   *rule = newRuleOffset;
3300   return 1;
3301 }
3302
3303 static int
3304 compileNumber (FileInfo * nested)
3305 {
3306   CharsString token;
3307   widechar dest;
3308   if (!getToken (nested, &token, "number"))
3309     return 0;
3310   getNumber (&token.chars[0], &dest);
3311   if (!(dest > 0))
3312     {
3313       compileError (nested, "a nonzero positive number is required");
3314       return 0;
3315     }
3316   return dest;
3317 }
3318
3319 static int
3320 compileGrouping (FileInfo * nested)
3321 {
3322   int k;
3323   CharsString name;
3324   CharsString groupChars;
3325   CharsString groupDots;
3326   CharsString dotsParsed;
3327   TranslationTableCharacter *charsDotsPtr;
3328   widechar endChar;
3329   widechar endDots;
3330   if (!getToken (nested, &name, "name operand"))
3331     return 0;
3332   if (!getRuleCharsText (nested, &groupChars))
3333     return 0;
3334   if (!getToken (nested, &groupDots, "dots operand"))
3335     return 0;
3336   for (k = 0; k < groupDots.length && groupDots.chars[k] != ','; k++);
3337   if (k == groupDots.length)
3338     {
3339       compileError (nested,
3340                     "Dots operand must consist of two cells separated by a comma");
3341       return 0;
3342     }
3343   groupDots.chars[k] = '-';
3344   if (!parseDots (nested, &dotsParsed, &groupDots))
3345     return 0;
3346   if (groupChars.length != 2 || dotsParsed.length != 2)
3347     {
3348       compileError (nested,
3349                     "two Unicode characters and two cells separated by a comma are needed.");
3350       return 0;
3351     }
3352   charsDotsPtr = addCharOrDots (nested, groupChars.chars[0], 0);
3353   charsDotsPtr->attributes |= CTC_Math;
3354   charsDotsPtr->uppercase = charsDotsPtr->realchar;
3355   charsDotsPtr->lowercase = charsDotsPtr->realchar;
3356   charsDotsPtr = addCharOrDots (nested, groupChars.chars[1], 0);
3357   charsDotsPtr->attributes |= CTC_Math;
3358   charsDotsPtr->uppercase = charsDotsPtr->realchar;
3359   charsDotsPtr->lowercase = charsDotsPtr->realchar;
3360   charsDotsPtr = addCharOrDots (nested, dotsParsed.chars[0], 1);
3361   charsDotsPtr->attributes |= CTC_Math;
3362   charsDotsPtr->uppercase = charsDotsPtr->realchar;
3363   charsDotsPtr->lowercase = charsDotsPtr->realchar;
3364   charsDotsPtr = addCharOrDots (nested, dotsParsed.chars[1], 1);
3365   charsDotsPtr->attributes |= CTC_Math;
3366   charsDotsPtr->uppercase = charsDotsPtr->realchar;
3367   charsDotsPtr->lowercase = charsDotsPtr->realchar;
3368   if (!addRule (nested, CTO_Grouping, &groupChars, &dotsParsed, 0, 0))
3369     return 0;
3370   if (!addRuleName (nested, &name))
3371     return 0;
3372   putCharAndDots (nested, groupChars.chars[0], dotsParsed.chars[0]);
3373   putCharAndDots (nested, groupChars.chars[1], dotsParsed.chars[1]);
3374   endChar = groupChars.chars[1];
3375   endDots = dotsParsed.chars[1];
3376   groupChars.length = dotsParsed.length = 1;
3377   if (!addRule (nested, CTO_Math, &groupChars, &dotsParsed, 0, 0))
3378     return 0;
3379   groupChars.chars[0] = endChar;
3380   dotsParsed.chars[0] = endDots;
3381   if (!addRule (nested, CTO_Math, &groupChars, &dotsParsed, 0, 0))
3382     return 0;
3383   return 1;
3384 }
3385
3386 static int
3387 compileUplow (FileInfo * nested)
3388 {
3389   int k;
3390   TranslationTableCharacter *upperChar;
3391   TranslationTableCharacter *lowerChar;
3392   TranslationTableCharacter *upperCell = NULL;
3393   TranslationTableCharacter *lowerCell = NULL;
3394   CharsString ruleChars;
3395   CharsString ruleDots;
3396   CharsString upperDots;
3397   CharsString lowerDots;
3398   int haveLowerDots = 0;
3399   TranslationTableCharacterAttributes attr;
3400   if (!getRuleCharsText (nested, &ruleChars))
3401     return 0;
3402   if (!getToken (nested, &ruleDots, "dots operand"))
3403     return 0;
3404   for (k = 0; k < ruleDots.length && ruleDots.chars[k] != ','; k++);
3405   if (k == ruleDots.length)
3406     {
3407       if (!parseDots (nested, &upperDots, &ruleDots))
3408         return 0;
3409       lowerDots.length = upperDots.length;
3410       for (k = 0; k < upperDots.length; k++)
3411         lowerDots.chars[k] = upperDots.chars[k];
3412       lowerDots.chars[k] = 0;
3413     }
3414   else
3415     {
3416       haveLowerDots = ruleDots.length;
3417       ruleDots.length = k;
3418       if (!parseDots (nested, &upperDots, &ruleDots))
3419         return 0;
3420       ruleDots.length = 0;
3421       k++;
3422       for (; k < haveLowerDots; k++)
3423         ruleDots.chars[ruleDots.length++] = ruleDots.chars[k];
3424       if (!parseDots (nested, &lowerDots, &ruleDots))
3425         return 0;
3426     }
3427   if (ruleChars.length != 2 || upperDots.length < 1)
3428     {
3429       compileError (nested,
3430                     "Exactly two Unicode characters and at least one cell are required.");
3431       return 0;
3432     }
3433   if (haveLowerDots && lowerDots.length < 1)
3434     {
3435       compileError (nested, "at least one cell is required after the comma.");
3436       return 0;
3437     }
3438   upperChar = addCharOrDots (nested, ruleChars.chars[0], 0);
3439   upperChar->attributes |= CTC_Letter | CTC_UpperCase;
3440   upperChar->uppercase = ruleChars.chars[0];
3441   upperChar->lowercase = ruleChars.chars[1];
3442   lowerChar = addCharOrDots (nested, ruleChars.chars[1], 0);
3443   lowerChar->attributes |= CTC_Letter | CTC_LowerCase;
3444   lowerChar->uppercase = ruleChars.chars[0];
3445   lowerChar->lowercase = ruleChars.chars[1];
3446   for (k = 0; k < upperDots.length; k++)
3447     if (!compile_findCharOrDots (upperDots.chars[k], 1))
3448       {
3449         attr = CTC_Letter | CTC_UpperCase;
3450         upperCell = addCharOrDots (nested, upperDots.chars[k], 1);
3451         if (upperDots.length != 1)
3452           attr = CTC_Space;
3453         upperCell->attributes |= attr;
3454         upperCell->uppercase = upperCell->realchar;
3455       }
3456   if (haveLowerDots)
3457     {
3458       for (k = 0; k < lowerDots.length; k++)
3459         if (!compile_findCharOrDots (lowerDots.chars[k], 1))
3460           {
3461             attr = CTC_Letter | CTC_LowerCase;
3462             lowerCell = addCharOrDots (nested, lowerDots.chars[k], 1);
3463             if (lowerDots.length != 1)
3464               attr = CTC_Space;
3465             lowerCell->attributes |= attr;
3466             lowerCell->lowercase = lowerCell->realchar;
3467           }
3468     }
3469   else if (upperCell != NULL && upperDots.length == 1)
3470     upperCell->attributes |= CTC_LowerCase;
3471   if (lowerDots.length == 1)
3472     putCharAndDots (nested, ruleChars.chars[1], lowerDots.chars[0]);
3473   if (upperCell != NULL)
3474     upperCell->lowercase = lowerDots.chars[0];
3475   if (lowerCell != NULL)
3476     lowerCell->uppercase = upperDots.chars[0];
3477   if (upperDots.length == 1)
3478     putCharAndDots (nested, ruleChars.chars[0], upperDots.chars[0]);
3479   ruleChars.length = 1;
3480   ruleChars.chars[2] = ruleChars.chars[0];
3481   ruleChars.chars[0] = ruleChars.chars[1];
3482   if (!addRule (nested, CTO_LowerCase, &ruleChars, &lowerDots, 0, 0))
3483     return 0;
3484   ruleChars.chars[0] = ruleChars.chars[2];
3485   if (!addRule (nested, CTO_UpperCase, &ruleChars, &upperDots, 0, 0))
3486     return 0;
3487   return 1;
3488 }
3489
3490 /*Functions for compiling hyphenation tables*/
3491
3492 typedef struct                  /*hyphenation dictionary: finite state machine */
3493 {
3494   int numStates;
3495   HyphenationState *states;
3496 } HyphenDict;
3497
3498 #define DEFAULTSTATE 0xffff
3499 #define HYPHENHASHSIZE 8191
3500
3501 typedef struct
3502 {
3503   void *next;
3504   CharsString *key;
3505   int val;
3506 } HyphenHashEntry;
3507
3508 typedef struct
3509 {
3510   HyphenHashEntry *entries[HYPHENHASHSIZE];
3511 } HyphenHashTab;
3512
3513 /* a hash function from ASU - adapted from Gtk+ */
3514 static unsigned int
3515 hyphenStringHash (const CharsString * s)
3516 {
3517   int k;
3518   unsigned int h = 0, g;
3519   for (k = 0; k < s->length; k++)
3520     {
3521       h = (h << 4) + s->chars[k];
3522       if ((g = h & 0xf0000000))
3523         {
3524           h = h ^ (g >> 24);
3525           h = h ^ g;
3526         }
3527     }
3528   return h;
3529 }
3530
3531 static HyphenHashTab *
3532 hyphenHashNew (void)
3533 {
3534   HyphenHashTab *hashTab;
3535   hashTab = malloc (sizeof (HyphenHashTab));
3536   memset (hashTab, 0, sizeof (HyphenHashTab));
3537   return hashTab;
3538 }
3539
3540 static void
3541 hyphenHashFree (HyphenHashTab * hashTab)
3542 {
3543   int i;
3544   HyphenHashEntry *e, *next;
3545   for (i = 0; i < HYPHENHASHSIZE; i++)
3546     for (e = hashTab->entries[i]; e; e = next)
3547       {
3548         next = e->next;
3549         free (e->key);
3550         free (e);
3551       }
3552   free (hashTab);
3553 }
3554
3555 /* assumes that key is not already present! */
3556 static void
3557 hyphenHashInsert (HyphenHashTab * hashTab, const CharsString * key, int val)
3558 {
3559   int i, j;
3560   HyphenHashEntry *e;
3561   i = hyphenStringHash (key) % HYPHENHASHSIZE;
3562   e = malloc (sizeof (HyphenHashEntry));
3563   e->next = hashTab->entries[i];
3564   e->key = malloc ((key->length + 1) * CHARSIZE);
3565   e->key->length = key->length;
3566   for (j = 0; j < key->length; j++)
3567     e->key->chars[j] = key->chars[j];
3568   e->val = val;
3569   hashTab->entries[i] = e;
3570 }
3571
3572 /* return val if found, otherwise DEFAULTSTATE */
3573 static int
3574 hyphenHashLookup (HyphenHashTab * hashTab, const CharsString * key)
3575 {
3576   int i, j;
3577   HyphenHashEntry *e;
3578   if (key->length == 0)
3579     return 0;
3580   i = hyphenStringHash (key) % HYPHENHASHSIZE;
3581   for (e = hashTab->entries[i]; e; e = e->next)
3582     {
3583       if (key->length != e->key->length)
3584         continue;
3585       for (j = 0; j < key->length; j++)
3586         if (key->chars[j] != e->key->chars[j])
3587           break;
3588       if (j == key->length)
3589         return e->val;
3590     }
3591   return DEFAULTSTATE;
3592 }
3593
3594 static int
3595 hyphenGetNewState (HyphenDict * dict, HyphenHashTab * hashTab, const
3596                    CharsString * string)
3597 {
3598   hyphenHashInsert (hashTab, string, dict->numStates);
3599   /* predicate is true if dict->numStates is a power of two */
3600   if (!(dict->numStates & (dict->numStates - 1)))
3601     dict->states = realloc (dict->states,
3602                             (dict->numStates << 1) *
3603                             sizeof (HyphenationState));
3604   dict->states[dict->numStates].hyphenPattern = 0;
3605   dict->states[dict->numStates].fallbackState = DEFAULTSTATE;
3606   dict->states[dict->numStates].numTrans = 0;
3607   dict->states[dict->numStates].trans.pointer = NULL;
3608   return dict->numStates++;
3609 }
3610
3611 /* add a transition from state1 to state2 through ch - assumes that the
3612    transition does not already exist */
3613 static void
3614 hyphenAddTrans (HyphenDict * dict, int state1, int state2, widechar ch)
3615 {
3616   int numTrans;
3617   numTrans = dict->states[state1].numTrans;
3618   if (numTrans == 0)
3619     dict->states[state1].trans.pointer = malloc (sizeof (HyphenationTrans));
3620   else if (!(numTrans & (numTrans - 1)))
3621     dict->states[state1].trans.pointer = realloc
3622       (dict->states[state1].trans.pointer,
3623        (numTrans << 1) * sizeof (HyphenationTrans));
3624   dict->states[state1].trans.pointer[numTrans].ch = ch;
3625   dict->states[state1].trans.pointer[numTrans].newState = state2;
3626   dict->states[state1].numTrans++;
3627 }
3628
3629 static int
3630 compileHyphenation (FileInfo * nested, CharsString * encoding)
3631 {
3632   CharsString hyph;
3633   HyphenationTrans *holdPointer;
3634   HyphenHashTab *hashTab;
3635   CharsString word;
3636   char pattern[MAXSTRING];
3637   unsigned int stateNum = 0, lastState = 0;
3638   int i, j, k = encoding->length;
3639   widechar ch;
3640   int found;
3641   HyphenHashEntry *e;
3642   HyphenDict dict;
3643   TranslationTableOffset holdOffset;
3644   /*Set aside enough space for hyphenation states and transitions in 
3645    * translation table. Must be done before anything else*/
3646   reserveSpaceInTable (nested, 250000);
3647   hashTab = hyphenHashNew ();
3648   dict.numStates = 1;
3649   dict.states = malloc (sizeof (HyphenationState));
3650   dict.states[0].hyphenPattern = 0;
3651   dict.states[0].fallbackState = DEFAULTSTATE;
3652   dict.states[0].numTrans = 0;
3653   dict.states[0].trans.pointer = NULL;
3654   do
3655     {
3656       if (encoding->chars[0] == 'I')
3657         {
3658           if (!getToken (nested, &hyph, NULL))
3659             continue;
3660         }
3661       else
3662         {
3663           /*UTF-8 */
3664           if (!getToken (nested, &word, NULL))
3665             continue;
3666           parseChars (nested, &hyph, &word);
3667         }
3668       if (hyph.length == 0 || hyph.chars[0] == '#' || hyph.chars[0] ==
3669           '%' || hyph.chars[0] == '<')
3670         continue;               /*comment */
3671       for (i = 0; i < hyph.length; i++)
3672         definedCharOrDots (nested, hyph.chars[i], 0);
3673       j = 0;
3674       pattern[j] = '0';
3675       for (i = 0; i < hyph.length; i++)
3676         {
3677           if (hyph.chars[i] >= '0' && hyph.chars[i] <= '9')
3678             pattern[j] = (char) hyph.chars[i];
3679           else
3680             {
3681               word.chars[j] = hyph.chars[i];
3682               pattern[++j] = '0';
3683             }
3684         }
3685       word.chars[j] = 0;
3686       word.length = j;
3687       pattern[j + 1] = 0;
3688       for (i = 0; pattern[i] == '0'; i++);
3689       found = hyphenHashLookup (hashTab, &word);
3690       if (found != DEFAULTSTATE)
3691         stateNum = found;
3692       else
3693         stateNum = hyphenGetNewState (&dict, hashTab, &word);
3694       k = j + 2 - i;
3695       if (k > 0)
3696         {
3697           allocateSpaceInTable (nested,
3698                                 &dict.states[stateNum].hyphenPattern, k);
3699           memcpy (&table->ruleArea[dict.states[stateNum].hyphenPattern],
3700                   &pattern[i], k);
3701         }
3702       /* now, put in the prefix transitions */
3703       while (found == DEFAULTSTATE)
3704         {
3705           lastState = stateNum;
3706           ch = word.chars[word.length-- - 1];
3707           found = hyphenHashLookup (hashTab, &word);
3708           if (found != DEFAULTSTATE)
3709             stateNum = found;
3710           else
3711             stateNum = hyphenGetNewState (&dict, hashTab, &word);
3712           hyphenAddTrans (&dict, stateNum, lastState, ch);
3713         }
3714     }
3715   while (getALine (nested));
3716   /* put in the fallback states */
3717   for (i = 0; i < HYPHENHASHSIZE; i++)
3718     {
3719       for (e = hashTab->entries[i]; e; e = e->next)
3720         {
3721           for (j = 1; j <= e->key->length; j++)
3722             {
3723               word.length = 0;
3724               for (k = j; k < e->key->length; k++)
3725                 word.chars[word.length++] = e->key->chars[k];
3726               stateNum = hyphenHashLookup (hashTab, &word);
3727               if (stateNum != DEFAULTSTATE)
3728                 break;
3729             }
3730           if (e->val)
3731             dict.states[e->val].fallbackState = stateNum;
3732         }
3733     }
3734   hyphenHashFree (hashTab);
3735 /*Transfer hyphenation information to table*/
3736   for (i = 0; i < dict.numStates; i++)
3737     {
3738       if (dict.states[i].numTrans == 0)
3739         dict.states[i].trans.offset = 0;
3740       else
3741         {
3742           holdPointer = dict.states[i].trans.pointer;
3743           allocateSpaceInTable (nested,
3744                                 &dict.states[i].trans.offset,
3745                                 dict.states[i].numTrans *
3746                                 sizeof (HyphenationTrans));
3747           memcpy (&table->ruleArea[dict.states[i].trans.offset],
3748                   holdPointer,
3749                   dict.states[i].numTrans * sizeof (HyphenationTrans));
3750           free (holdPointer);
3751         }
3752     }
3753   allocateSpaceInTable (nested,
3754                         &holdOffset, dict.numStates *
3755                         sizeof (HyphenationState));
3756   table->hyphenStatesArray = holdOffset;
3757   /* Prevents segmentajion fault if table is reallocated */
3758   memcpy (&table->ruleArea[table->hyphenStatesArray], &dict.states[0],
3759           dict.numStates * sizeof (HyphenationState));
3760   free (dict.states);
3761   return 1;
3762 }
3763
3764 static int
3765 compileNoBreak (FileInfo * nested)
3766 {
3767   int k;
3768   CharsString ruleDots;
3769   CharsString otherDots;
3770   CharsString dotsBefore;
3771   CharsString dotsAfter;
3772   int haveDotsAfter = 0;
3773   if (!getToken (nested, &ruleDots, "dots operand"))
3774     return 0;
3775   for (k = 0; k < ruleDots.length && ruleDots.chars[k] != ','; k++);
3776   if (k == ruleDots.length)
3777     {
3778       if (!parseDots (nested, &dotsBefore, &ruleDots))
3779         return 0;
3780       dotsAfter.length = dotsBefore.length;
3781       for (k = 0; k < dotsBefore.length; k++)
3782         dotsAfter.chars[k] = dotsBefore.chars[k];
3783       dotsAfter.chars[k] = 0;
3784     }
3785   else
3786     {
3787       haveDotsAfter = ruleDots.length;
3788       ruleDots.length = k;
3789       if (!parseDots (nested, &dotsBefore, &ruleDots))
3790         return 0;
3791       otherDots.length = 0;
3792       k++;
3793       for (; k < haveDotsAfter; k++)
3794         otherDots.chars[otherDots.length++] = ruleDots.chars[k];
3795       if (!parseDots (nested, &dotsAfter, &otherDots))
3796         return 0;
3797     }
3798   for (k = 0; k < dotsBefore.length; k++)
3799     dotsBefore.chars[k] = getCharFromDots (dotsBefore.chars[k]);
3800   for (k = 0; k < dotsAfter.length; k++)
3801     dotsAfter.chars[k] = getCharFromDots (dotsAfter.chars[k]);
3802   if (!addRule (nested, CTO_NoBreak, &dotsBefore, &dotsAfter, 0, 0))
3803     return 0;
3804   table->noBreak = newRuleOffset;
3805   return 1;
3806 }
3807
3808 static int
3809 compileCharDef (FileInfo * nested,
3810                 TranslationTableOpcode opcode,
3811                 TranslationTableCharacterAttributes attributes)
3812 {
3813   CharsString ruleChars;
3814   CharsString ruleDots;
3815   TranslationTableCharacter *character;
3816   TranslationTableCharacter *cell;
3817   TranslationTableCharacter *otherCell;
3818   TranslationTableCharacterAttributes attr;
3819   int k;
3820   if (!getRuleCharsText (nested, &ruleChars))
3821     return 0;
3822   if (attributes & (CTC_UpperCase | CTC_LowerCase))
3823     attributes |= CTC_Letter;
3824   if (!getRuleDotsPattern (nested, &ruleDots))
3825     return 0;
3826   if (ruleChars.length != 1 || ruleDots.length < 1)
3827     {
3828       compileError (nested,
3829                     "Exactly one Unicode character and at least one cell are required.");
3830       return 0;
3831     }
3832   character = addCharOrDots (nested, ruleChars.chars[0], 0);
3833   character->attributes |= attributes;
3834   character->uppercase = character->lowercase = character->realchar;
3835   cell = compile_findCharOrDots (ruleDots.chars[0], 1);
3836   if (ruleDots.length == 1 && cell)
3837     cell->attributes |= attributes;
3838   else
3839     {
3840       for (k = 0; k < ruleDots.length; k++)
3841         {
3842           if (!compile_findCharOrDots (ruleDots.chars[k], 1))
3843             {
3844               attr = attributes;
3845               otherCell = addCharOrDots (nested, ruleDots.chars[k], 1);
3846               if (ruleDots.length != 1)
3847                 attr = CTC_Space;
3848               otherCell->attributes |= attr;
3849               otherCell->uppercase = otherCell->lowercase =
3850                 otherCell->realchar;
3851             }
3852         }
3853     }
3854   if (!addRule (nested, opcode, &ruleChars, &ruleDots, 0, 0))
3855     return 0;
3856   if (ruleDots.length == 1)
3857     putCharAndDots (nested, ruleChars.chars[0], ruleDots.chars[0]);
3858   return 1;
3859 }
3860
3861 static int
3862 compileRule (FileInfo * nested)
3863 {
3864   int ok = 1;
3865   CharsString token;
3866   TranslationTableOpcode opcode;
3867   CharsString ruleChars;
3868   CharsString ruleDots;
3869   CharsString cells;
3870   CharsString scratchPad;
3871   TranslationTableCharacterAttributes after = 0;
3872   TranslationTableCharacterAttributes before = 0;
3873   int k;
3874
3875   noback = nofor = 0;
3876 doOpcode:
3877   if (!getToken (nested, &token, NULL))
3878     return 1;                   /*blank line */
3879   if (token.chars[0] == '#' || token.chars[0] == '<')
3880     return 1;                   /*comment */
3881   if (nested->lineNumber == 1 && (eqasc2uni ((unsigned char *) "ISO",
3882                                              token.chars, 3) ||
3883                                   eqasc2uni ((unsigned char *) "UTF-8",
3884                                              token.chars, 5)))
3885     {
3886       compileHyphenation (nested, &token);
3887       return 1;
3888     }
3889   opcode = getOpcode (nested, &token);
3890   switch (opcode)
3891     {                           /*Carry out operations */
3892     case CTO_None:
3893       break;
3894     case CTO_IncludeFile:
3895       {
3896         CharsString includedFile;
3897         if (getToken (nested, &token, "include file name"))
3898           if (parseChars (nested, &includedFile, &token))
3899             if (!includeFile (nested, &includedFile))
3900               ok = 0;
3901         break;
3902       }
3903     case CTO_Locale:
3904       break;
3905     case CTO_Undefined:
3906       ok =
3907         compileBrailleIndicator (nested, "undefined character opcode",
3908                                  CTO_Undefined, &table->undefined);
3909       break;
3910     case CTO_CapitalSign:
3911       ok =
3912         compileBrailleIndicator (nested, "capital sign", CTO_CapitalRule,
3913                                  &table->capitalSign);
3914       break;
3915     case CTO_BeginCapitalSign:
3916       ok =
3917         compileBrailleIndicator (nested, "begin capital sign",
3918                                  CTO_BeginCapitalRule,
3919                                  &table->beginCapitalSign);
3920       break;
3921     case CTO_LenBegcaps:
3922       ok = table->lenBeginCaps = compileNumber (nested);
3923       break;
3924     case CTO_EndCapitalSign:
3925       ok =
3926         compileBrailleIndicator (nested, "end capitals sign",
3927                                  CTO_EndCapitalRule, &table->endCapitalSign);
3928       break;
3929     case CTO_FirstWordCaps:
3930       ok =
3931         compileBrailleIndicator (nested, "first word capital sign",
3932                                  CTO_FirstWordCapsRule,
3933                                  &table->firstWordCaps);
3934       break;
3935     case CTO_LastWordCapsBefore:
3936       ok =
3937         compileBrailleIndicator (nested, "capital sign before last word",
3938                                  CTO_LastWordCapsBeforeRule,
3939                                  &table->lastWordCapsBefore);
3940       break;
3941     case CTO_LastWordCapsAfter:
3942       ok =
3943         compileBrailleIndicator (nested, "capital sign after last word",
3944                                  CTO_LastWordCapsAfterRule,
3945                                  &table->lastWordCapsAfter);
3946       break;
3947     case CTO_LenCapsPhrase:
3948       ok = table->lenCapsPhrase = compileNumber (nested);
3949       break;
3950     case CTO_LetterSign:
3951       ok =
3952         compileBrailleIndicator (nested, "letter sign", CTO_LetterRule,
3953                                  &table->letterSign);
3954       break;
3955     case CTO_NoLetsignBefore:
3956       if (getRuleCharsText (nested, &ruleChars))
3957         {
3958           if ((table->noLetsignBeforeCount + ruleChars.length) > LETSIGNSIZE)
3959             {
3960               compileError (nested, "More than %d characters", LETSIGNSIZE);
3961               ok = 0;
3962               break;
3963             }
3964           for (k = 0; k < ruleChars.length; k++)
3965             table->noLetsignBefore[table->noLetsignBeforeCount++] =
3966               ruleChars.chars[k];
3967         }
3968       break;
3969     case CTO_NoLetsign:
3970       if (getRuleCharsText (nested, &ruleChars))
3971         {
3972           if ((table->noLetsignCount + ruleChars.length) > LETSIGNSIZE)
3973             {
3974               compileError (nested, "More than %d characters", LETSIGNSIZE);
3975               ok = 0;
3976               break;
3977             }
3978           for (k = 0; k < ruleChars.length; k++)
3979             table->noLetsign[table->noLetsignCount++] = ruleChars.chars[k];
3980         }
3981       break;
3982     case CTO_NoLetsignAfter:
3983       if (getRuleCharsText (nested, &ruleChars))
3984         {
3985           if ((table->noLetsignAfterCount + ruleChars.length) > LETSIGNSIZE)
3986             {
3987               compileError (nested, "More than %d characters", LETSIGNSIZE);
3988               ok = 0;
3989               break;
3990             }
3991           for (k = 0; k < ruleChars.length; k++)
3992             table->noLetsignAfter[table->noLetsignAfterCount++] =
3993               ruleChars.chars[k];
3994         }
3995       break;
3996     case CTO_NumberSign:
3997       ok =
3998         compileBrailleIndicator (nested, "number sign", CTO_NumberRule,
3999                                  &table->numberSign);
4000       break;
4001     case CTO_FirstWordItal:
4002       ok =
4003         compileBrailleIndicator (nested, "first word italic",
4004                                  CTO_FirstWordItalRule,
4005                                  &table->firstWordItal);
4006       break;
4007     case CTO_ItalSign:
4008     case CTO_LastWordItalBefore:
4009       ok =
4010         compileBrailleIndicator (nested, "first word italic before",
4011                                  CTO_LastWordItalBeforeRule,
4012                                  &table->lastWordItalBefore);
4013       break;
4014     case CTO_LastWordItalAfter:
4015       ok =
4016         compileBrailleIndicator (nested, "last word italic after",
4017                                  CTO_LastWordItalAfterRule,
4018                                  &table->lastWordItalAfter);
4019       break;
4020     case CTO_BegItal:
4021     case CTO_FirstLetterItal:
4022       ok =
4023         compileBrailleIndicator (nested, "first letter italic",
4024                                  CTO_FirstLetterItalRule,
4025                                  &table->firstLetterItal);
4026       break;
4027     case CTO_EndItal:
4028     case CTO_LastLetterItal:
4029       ok =
4030         compileBrailleIndicator (nested, "last letter italic",
4031                                  CTO_LastLetterItalRule,
4032                                  &table->lastLetterItal);
4033       break;
4034     case CTO_SingleLetterItal:
4035       ok =
4036         compileBrailleIndicator (nested, "single letter italic",
4037                                  CTO_SingleLetterItalRule,
4038                                  &table->singleLetterItal);
4039       break;
4040     case CTO_ItalWord:
4041       ok =
4042         compileBrailleIndicator (nested, "italic word", CTO_ItalWordRule,
4043                                  &table->italWord);
4044       break;
4045     case CTO_LenItalPhrase:
4046       ok = table->lenItalPhrase = compileNumber (nested);
4047       break;
4048     case CTO_FirstWordBold:
4049       ok =
4050         compileBrailleIndicator (nested, "first word bold",
4051                                  CTO_FirstWordBoldRule,
4052                                  &table->firstWordBold);
4053       break;
4054     case CTO_BoldSign:
4055     case CTO_LastWordBoldBefore:
4056       ok =
4057         compileBrailleIndicator (nested, "last word bold before",
4058                                  CTO_LastWordBoldBeforeRule,
4059                                  &table->lastWordBoldBefore);
4060       break;
4061     case CTO_LastWordBoldAfter:
4062       ok =
4063         compileBrailleIndicator (nested, "last word bold after",
4064                                  CTO_LastWordBoldAfterRule,
4065                                  &table->lastWordBoldAfter);
4066       break;
4067     case CTO_BegBold:
4068     case CTO_FirstLetterBold:
4069       ok =
4070         compileBrailleIndicator (nested, "first  letter bold",
4071                                  CTO_FirstLetterBoldRule,
4072                                  &table->firstLetterBold);
4073       break;
4074     case CTO_EndBold:
4075     case CTO_LastLetterBold:
4076       ok =
4077         compileBrailleIndicator (nested, "last letter bold",
4078                                  CTO_LastLetterBoldRule,
4079                                  &table->lastLetterBold);
4080       break;
4081     case CTO_SingleLetterBold:
4082       ok =
4083         compileBrailleIndicator (nested, "single  letter bold",
4084                                  CTO_SingleLetterBoldRule,
4085                                  &table->singleLetterBold);
4086       break;
4087     case CTO_BoldWord:
4088       ok =
4089         compileBrailleIndicator (nested, "bold word", CTO_BoldWordRule,
4090                                  &table->boldWord);
4091       break;
4092     case CTO_LenBoldPhrase:
4093       ok = table->lenBoldPhrase = compileNumber (nested);
4094       break;
4095     case CTO_FirstWordUnder:
4096       ok =
4097         compileBrailleIndicator (nested, "first word  underline",
4098                                  CTO_FirstWordUnderRule,
4099                                  &table->firstWordUnder);
4100       break;
4101     case CTO_UnderSign:
4102     case CTO_LastWordUnderBefore:
4103       ok =
4104         compileBrailleIndicator (nested, "last word underline before",
4105                                  CTO_LastWordUnderBeforeRule,
4106                                  &table->lastWordUnderBefore);
4107       break;
4108     case CTO_LastWordUnderAfter:
4109       ok =
4110         compileBrailleIndicator (nested, "last  word underline after",
4111                                  CTO_LastWordUnderAfterRule,
4112                                  &table->lastWordUnderAfter);
4113       break;
4114     case CTO_BegUnder:
4115     case CTO_FirstLetterUnder:
4116       ok =
4117         compileBrailleIndicator (nested, "first letter underline",
4118                                  CTO_FirstLetterUnderRule,
4119                                  &table->firstLetterUnder);
4120       break;
4121     case CTO_EndUnder:
4122     case CTO_LastLetterUnder:
4123       ok =
4124         compileBrailleIndicator (nested, "last letter underline",
4125                                  CTO_LastLetterUnderRule,
4126                                  &table->lastLetterUnder);
4127       break;
4128     case CTO_SingleLetterUnder:
4129       ok =
4130         compileBrailleIndicator (nested, "single letter underline",
4131                                  CTO_SingleLetterUnderRule,
4132                                  &table->singleLetterUnder);
4133       break;
4134     case CTO_UnderWord:
4135       ok =
4136         compileBrailleIndicator (nested, "underlined word", CTO_UnderWordRule,
4137                                  &table->underWord);
4138       break;
4139     case CTO_LenUnderPhrase:
4140       ok = table->lenUnderPhrase = compileNumber (nested);
4141       break;
4142     case CTO_BegComp:
4143       ok =
4144         compileBrailleIndicator (nested, "begin computer braille",
4145                                  CTO_BegCompRule, &table->begComp);
4146       break;
4147     case CTO_EndComp:
4148       ok =
4149         compileBrailleIndicator (nested, "end computer braslle",
4150                                  CTO_EndCompRule, &table->endComp);
4151       break;
4152     case CTO_Syllable:
4153       table->syllables = 1;
4154     case CTO_Always:
4155     case CTO_NoCross:
4156     case CTO_LargeSign:
4157     case CTO_WholeWord:
4158     case CTO_PartWord:
4159     case CTO_JoinNum:
4160     case CTO_JoinableWord:
4161     case CTO_LowWord:
4162     case CTO_SuffixableWord:
4163     case CTO_PrefixableWord:
4164     case CTO_BegWord:
4165     case CTO_BegMidWord:
4166     case CTO_MidWord:
4167     case CTO_MidEndWord:
4168     case CTO_EndWord:
4169     case CTO_PrePunc:
4170     case CTO_PostPunc:
4171     case CTO_BegNum:
4172     case CTO_MidNum:
4173     case CTO_EndNum:
4174     case CTO_Repeated:
4175     case CTO_RepWord:
4176       if (getRuleCharsText (nested, &ruleChars))
4177         if (getRuleDotsPattern (nested, &ruleDots))
4178           if (!addRule (nested, opcode, &ruleChars, &ruleDots, after, before))
4179             ok = 0;
4180       break;
4181     case CTO_CompDots:
4182     case CTO_Comp6:
4183       if (!getRuleCharsText (nested, &ruleChars))
4184         return 0;
4185       if (ruleChars.length != 1 || ruleChars.chars[0] > 255)
4186         {
4187           compileError (nested,
4188                         "first operand must be 1 character and < 256");
4189           return 0;
4190         }
4191       if (!getRuleDotsPattern (nested, &ruleDots))
4192         return 0;
4193       if (!addRule (nested, opcode, &ruleChars, &ruleDots, after, before))
4194         ok = 0;
4195       table->compdotsPattern[ruleChars.chars[0]] = newRuleOffset;
4196       break;
4197     case CTO_ExactDots:
4198       if (!getRuleCharsText (nested, &ruleChars))
4199         return 0;
4200       if (ruleChars.chars[0] != '@')
4201         {
4202           compileError (nested, "The operand must begin with an at sign (@)");
4203           return 0;
4204         }
4205       for (k = 1; k < ruleChars.length; k++)
4206         scratchPad.chars[k - 1] = ruleChars.chars[k];
4207       scratchPad.length = ruleChars.length - 1;
4208       if (!parseDots (nested, &ruleDots, &scratchPad))
4209         return 0;
4210       if (!addRule (nested, opcode, &ruleChars, &ruleDots, before, after))
4211         ok = 0;
4212       break;
4213     case CTO_CapsNoCont:
4214       ruleChars.length = 1;
4215       ruleChars.chars[0] = 'a';
4216       if (!addRule
4217           (nested, CTO_CapsNoContRule, &ruleChars, NULL, after, before))
4218         ok = 0;
4219       table->capsNoCont = newRuleOffset;
4220       break;
4221     case CTO_Replace:
4222       if (getRuleCharsText (nested, &ruleChars))
4223         {
4224           if (lastToken)
4225             ruleDots.length = ruleDots.chars[0] = 0;
4226           else
4227             {
4228               getRuleDotsText (nested, &ruleDots);
4229               if (ruleDots.chars[0] == '#')
4230                 ruleDots.length = ruleDots.chars[0] = 0;
4231               else if (ruleDots.chars[0] == '\\' && ruleDots.chars[1] == '#')
4232                 memcpy (&ruleDots.chars[0], &ruleDots.chars[1],
4233                         ruleDots.length-- * CHARSIZE);
4234             }
4235         }
4236       for (k = 0; k < ruleChars.length; k++)
4237         addCharOrDots (nested, ruleChars.chars[k], 0);
4238       for (k = 0; k < ruleDots.length; k++)
4239         addCharOrDots (nested, ruleDots.chars[k], 0);
4240       if (!addRule (nested, opcode, &ruleChars, &ruleDots, after, before))
4241         ok = 0;
4242       break;
4243     case CTO_Pass2:
4244       if (table->numPasses < 2)
4245         table->numPasses = 2;
4246       goto doPass;
4247     case CTO_Pass3:
4248       if (table->numPasses < 3)
4249         table->numPasses = 3;
4250       goto doPass;
4251     case CTO_Pass4:
4252       if (table->numPasses < 4)
4253         table->numPasses = 4;
4254     doPass:
4255     case CTO_Context:
4256       if (!compilePassOpcode (nested, opcode))
4257         ok = 0;
4258       break;
4259     case CTO_Correct:
4260       if (!compilePassOpcode (nested, opcode))
4261         ok = 0;
4262       table->corrections = 1;
4263       break;
4264     case CTO_Contraction:
4265     case CTO_NoCont:
4266     case CTO_CompBrl:
4267     case CTO_Literal:
4268       if (getRuleCharsText (nested, &ruleChars))
4269         if (!addRule (nested, opcode, &ruleChars, NULL, after, before))
4270           ok = 0;
4271       break;
4272     case CTO_MultInd:
4273       {
4274         int lastToken;
4275         ruleChars.length = 0;
4276         if (getToken (nested, &token, "multiple braille indicators") &&
4277             parseDots (nested, &cells, &token))
4278           {
4279             while ((lastToken = getToken (nested, &token, "multind opcodes")))
4280               {
4281                 opcode = getOpcode (nested, &token);
4282                 if (opcode >= CTO_CapitalSign && opcode < CTO_MultInd)
4283                   ruleChars.chars[ruleChars.length++] = (widechar) opcode;
4284                 else
4285                   {
4286                     compileError (nested, "Not a braille indicator opcode.");
4287                     ok = 0;
4288                   }
4289                 if (lastToken == 2)
4290                   break;
4291               }
4292           }
4293         else
4294           ok = 0;
4295         if (!addRule (nested, CTO_MultInd, &ruleChars, &cells, after, before))
4296           ok = 0;
4297         break;
4298       }
4299
4300     case CTO_Class:
4301       {
4302         CharsString characters;
4303         const struct CharacterClass *class;
4304         if (!characterClasses)
4305           {
4306             if (!allocateCharacterClasses ())
4307               ok = 0;
4308           }
4309         if (getToken (nested, &token, "character class name"))
4310           {
4311             if ((class = findCharacterClass (&token)))
4312               {
4313                 compileError (nested, "character class already defined.");
4314               }
4315             else
4316               if ((class =
4317                    addCharacterClass (nested, &token.chars[0], token.length)))
4318               {
4319                 if (getCharacters (nested, &characters))
4320                   {
4321                     int index;
4322                     for (index = 0; index < characters.length; ++index)
4323                       {
4324                         TranslationTableRule *defRule;
4325                         TranslationTableCharacter *character =
4326                           definedCharOrDots
4327                           (nested, characters.chars[index], 0);
4328                         character->attributes |= class->attribute;
4329                         defRule = (TranslationTableRule *)
4330                           & table->ruleArea[character->definitionRule];
4331                         if (defRule->dotslen == 1)
4332                           {
4333                             character = definedCharOrDots
4334                               (nested,
4335                                defRule->charsdots[defRule->charslen], 1);
4336                             character->attributes |= class->attribute;
4337                           }
4338                       }
4339                   }
4340               }
4341           }
4342         break;
4343       }
4344
4345       {
4346         TranslationTableCharacterAttributes *attributes;
4347         const struct CharacterClass *class;
4348     case CTO_After:
4349         attributes = &after;
4350         goto doClass;
4351     case CTO_Before:
4352         attributes = &before;
4353       doClass:
4354
4355         if (!characterClasses)
4356           {
4357             if (!allocateCharacterClasses ())
4358               ok = 0;
4359           }
4360         if (getCharacterClass (nested, &class))
4361           {
4362             *attributes |= class->attribute;
4363             goto doOpcode;
4364           }
4365         break;
4366       }
4367     case CTO_NoBack:
4368       noback = 1;
4369       goto doOpcode;
4370     case CTO_NoFor:
4371       nofor = 1;
4372       goto doOpcode;
4373     case CTO_SwapCc:
4374     case CTO_SwapCd:
4375     case CTO_SwapDd:
4376       if (!compileSwap (nested, opcode))
4377         ok = 0;
4378       break;
4379     case CTO_Hyphen:
4380     case CTO_DecPoint:
4381       if (getRuleCharsText (nested, &ruleChars))
4382         if (getRuleDotsPattern (nested, &ruleDots))
4383           {
4384             if (ruleChars.length != 1 || ruleDots.length < 1)
4385               {
4386                 compileError (nested,
4387                               "One Unicode character and at least one cell are required.");
4388                 ok = 0;
4389               }
4390             if (!addRule
4391                 (nested, opcode, &ruleChars, &ruleDots, after, before))
4392               ok = 0;
4393           }
4394       break;
4395     case CTO_Space:
4396       compileCharDef (nested, opcode, CTC_Space);
4397       break;
4398     case CTO_Digit:
4399       compileCharDef (nested, opcode, CTC_Digit);
4400       break;
4401     case CTO_LitDigit:
4402       compileCharDef (nested, opcode, CTC_LitDigit);
4403       break;
4404     case CTO_Punctuation:
4405       compileCharDef (nested, opcode, CTC_Punctuation);
4406       break;
4407     case CTO_Math:
4408       compileCharDef (nested, opcode, CTC_Math);
4409       break;
4410     case CTO_Sign:
4411       compileCharDef (nested, opcode, CTC_Sign);
4412       break;
4413     case CTO_Letter:
4414       compileCharDef (nested, opcode, CTC_Letter);
4415       break;
4416     case CTO_UpperCase:
4417       compileCharDef (nested, opcode, CTC_UpperCase);
4418       break;
4419     case CTO_LowerCase:
4420       compileCharDef (nested, opcode, CTC_LowerCase);
4421       break;
4422     case CTO_NoBreak:
4423       ok = compileNoBreak (nested);
4424       break;
4425     case CTO_Grouping:
4426       ok = compileGrouping (nested);
4427       break;
4428     case CTO_UpLow:
4429       ok = compileUplow (nested);
4430       break;
4431     case CTO_Display:
4432       if (getRuleCharsText (nested, &ruleChars))
4433         if (getRuleDotsPattern (nested, &ruleDots))
4434           {
4435             if (ruleChars.length != 1 || ruleDots.length != 1)
4436               {
4437                 compileError (nested,
4438                               "Exactly one character and one cell are required.");
4439                 ok = 0;
4440               }
4441             putCharAndDots (nested, ruleChars.chars[0], ruleDots.chars[0]);
4442           }
4443       break;
4444     default:
4445       compileError (nested, "unimplemented opcode.");
4446       break;
4447     }
4448   return ok;
4449 }
4450
4451 int EXPORT_CALL
4452 lou_readCharFromFile (const char *fileName, int *mode)
4453 {
4454 /*Read a character from a file, whether big-endian, little-endian or 
4455 * ASCII8*/
4456   int ch;
4457   static FileInfo nested;
4458   if (fileName == NULL)
4459     return 0;
4460   if (*mode == 1)
4461     {
4462       *mode = 0;
4463       nested.fileName = fileName;
4464       nested.encoding = noEncoding;
4465       nested.status = 0;
4466       nested.lineNumber = 0;
4467       if (!(nested.in = fopen (nested.fileName, "r")))
4468         {
4469           lou_logPrint ("Cannot open file '%s'", nested.fileName);
4470           *mode = 1;
4471           return EOF;
4472         }
4473     }
4474   if (nested.in == NULL)
4475     {
4476       *mode = 1;
4477       return EOF;
4478     }
4479   ch = getAChar (&nested);
4480   if (ch == EOF)
4481     {
4482       fclose (nested.in);
4483       nested.in = NULL;
4484       *mode = 1;
4485     }
4486   return ch;
4487 }
4488
4489 static int fileCount = 0;
4490 static FILE *
4491 findTable (const char *tableName)
4492 {
4493 /* Search paths for tables */
4494   FILE *tableFile;
4495   char *pathList;
4496   char pathEnd[2];
4497   char trialPath[MAXSTRING];
4498   if (tableName == NULL || tableName[0] == 0)
4499     return NULL;
4500   strcpy (trialPath, tablePath);
4501   strcat (trialPath, tableName);
4502   if ((tableFile = fopen (trialPath, "rb")))
4503     return tableFile;
4504   pathEnd[0] = DIR_SEP;
4505   pathEnd[1] = 0;
4506   /* See if table is on environment path LOUIS_TABLEPATH */
4507   pathList = getenv ("LOUIS_TABLEPATH");
4508   if (pathList)
4509     while (1)
4510       {
4511         int k;
4512         int listLength;
4513         int currentListPos = 0;
4514         listLength = strlen (pathList);
4515         for (k = 0; k < listLength; k++)
4516           if (pathList[k] == ',')
4517             break;
4518         if (k == listLength || k == 0)
4519           {                     /* Only one file */
4520             strcpy (trialPath, pathList);
4521             strcat (trialPath, pathEnd);
4522             strcat (trialPath, tableName);
4523             if ((tableFile = fopen (trialPath, "rb")))
4524               break;
4525           }
4526         else
4527           {                     /* Compile a list of files */
4528             strncpy (trialPath, pathList, k);
4529             trialPath[k] = 0;
4530             strcat (trialPath, pathEnd);
4531             strcat (trialPath, tableName);
4532             currentListPos = k + 1;
4533             if ((tableFile = fopen (trialPath, "rb")))
4534               break;
4535             while (currentListPos < listLength)
4536               {
4537                 for (k = currentListPos; k < listLength; k++)
4538                   if (pathList[k] == ',')
4539                     break;
4540                 strncpy (trialPath,
4541                          &pathList[currentListPos], k - currentListPos);
4542                 trialPath[k - currentListPos] = 0;
4543                 strcat (trialPath, pathEnd);
4544                 strcat (trialPath, tableName);
4545                 if ((tableFile = fopen (trialPath, "rb")))
4546                   currentListPos = k + 1;
4547                 break;
4548               }
4549           }
4550         break;
4551       }
4552   if (tableFile)
4553     return tableFile;
4554   /* See if table in current directory or on a path in 
4555    * the table name*/
4556   if ((tableFile = fopen (tableName, "rb")))
4557     return tableFile;
4558 /* See if table on dataPath. */
4559   pathList = lou_getDataPath ();
4560   if (pathList)
4561     {
4562       strcpy (trialPath, pathList);
4563       strcat (trialPath, pathEnd);
4564 #ifdef _WIN32
4565       strcat (trialPath, "liblouis\\tables\\");
4566 #else
4567       strcat (trialPath, "liblouis/tables/");
4568 #endif
4569       strcat (trialPath, tableName);
4570       if ((tableFile = fopen (trialPath, "rb")))
4571         return tableFile;
4572     }
4573   /* See if table on installed or program path. */
4574 #ifdef _WIN32
4575   strcpy (trialPath, lou_getProgramPath ());
4576   strcat (trialPath, "\\share\\liblouss\\tables\\");
4577 #else
4578   strcpy (trialPath, TABLESDIR);
4579   strcat (trialPath, pathEnd);
4580 #endif
4581   strcat (trialPath, tableName);
4582   if ((tableFile = fopen (trialPath, "rb")))
4583     return tableFile;
4584   return NULL;
4585 }
4586
4587 static int
4588 compileFile (const char *fileName)
4589 {
4590 /*Compile a table file */
4591   FileInfo nested;
4592   fileCount++;
4593   nested.fileName = fileName;
4594   nested.encoding = noEncoding;
4595   nested.status = 0;
4596   nested.lineNumber = 0;
4597   if ((nested.in = findTable (fileName)))
4598     {
4599       while (getALine (&nested))
4600         compileRule (&nested);
4601       fclose (nested.in);
4602     }
4603   else
4604     {
4605       if (fileCount > 1)
4606         lou_logPrint ("Cannot open table '%s'", nested.fileName);
4607       errorCount++;
4608       return 0;
4609     }
4610   return 1;
4611 }
4612
4613 static int
4614 compileString (const char *inString)
4615 {
4616 /* This function can be used to make changes to tables on the fly. */
4617   int k;
4618   FileInfo nested;
4619   if (inString == NULL)
4620     return 0;
4621   nested.fileName = inString;
4622   nested.encoding = noEncoding;
4623   nested.lineNumber = 1;
4624   nested.status = 0;
4625   nested.linepos = 0;
4626   for (k = 0; inString[k]; k++)
4627     nested.line[k] = inString[k];
4628   nested.line[k] = 0;
4629   return compileRule (&nested);
4630 }
4631
4632 static int
4633 makeDoubleRule (TranslationTableOpcode opcode, TranslationTableOffset
4634                 * singleRule, TranslationTableOffset * doubleRule)
4635 {
4636   CharsString dots;
4637   TranslationTableRule *rule;
4638   if (!*singleRule || *doubleRule)
4639     return 1;
4640   rule = (TranslationTableRule *) & table->ruleArea[*singleRule];
4641   memcpy (dots.chars, &rule->charsdots[0], rule->dotslen * CHARSIZE);
4642   memcpy (&dots.chars[rule->dotslen], &rule->charsdots[0],
4643           rule->dotslen * CHARSIZE);
4644   dots.length = 2 * rule->dotslen;
4645   if (!addRule (NULL, opcode, NULL, &dots, 0, 0))
4646     return 0;
4647   *doubleRule = newRuleOffset;
4648   return 1;
4649 }
4650
4651 static int
4652 setDefaults (void)
4653 {
4654   if (!table->lenBeginCaps)
4655     table->lenBeginCaps = 2;
4656   makeDoubleRule (CTO_FirstWordItal, &table->lastWordItalBefore,
4657                   &table->firstWordItal);
4658   if (!table->lenItalPhrase)
4659     table->lenItalPhrase = 4;
4660   makeDoubleRule (CTO_FirstWordBold, &table->lastWordBoldBefore,
4661                   &table->firstWordBold);
4662   if (!table->lenBoldPhrase)
4663     table->lenBoldPhrase = 4;
4664   makeDoubleRule (CTO_FirstWordUnder, &table->lastWordUnderBefore,
4665                   &table->firstWordUnder);
4666   if (!table->lenUnderPhrase)
4667     table->lenUnderPhrase = 4;
4668   if (table->numPasses == 0)
4669     table->numPasses = 1;
4670   return 1;
4671 }
4672
4673 static char *
4674 doLang2table (const char *tableList)
4675 {
4676   static char newList[MAXSTRING];
4677   int k;
4678   char buffer[MAXSTRING];
4679   FILE *l2t;
4680   char *langCode;
4681   int langCodeLen;
4682   if (tableList == NULL || *tableList == 0)
4683     return NULL;
4684   strcpy (newList, tableList);
4685   for (k = strlen (newList) - 1; k >= 0 && newList[k] != '='; k--);
4686   if (k < 0)
4687     return newList;
4688   fileCount = 1;
4689   errorCount = 1;
4690   newList[k] = 0;
4691   strcpy (buffer, newList);
4692   langCode = &newList[k + 1];
4693   langCodeLen = strlen (langCode);
4694   strcat (buffer, "lang2table");
4695   l2t = fopen (buffer, "r");
4696   if (l2t == NULL)
4697     return NULL;
4698   while ((fgets (buffer, sizeof (buffer) - 2, l2t)))
4699     {
4700       char *codeInFile;
4701       int codeInFileLen;
4702       char *tableInFile;
4703       for (k = 0; buffer[k] < 32; k++);
4704       if (buffer[k] == '#' || buffer[k] < 32)
4705         continue;
4706       codeInFile = &buffer[k];
4707       codeInFileLen = k;
4708       while (buffer[k] > 32)
4709         k++;
4710       codeInFileLen = k - codeInFileLen;
4711       codeInFile[codeInFileLen] = 0;
4712       if (!
4713           (codeInFileLen == langCodeLen
4714            && strcasecmp (langCode, codeInFile) == 0))
4715         continue;
4716       while (buffer[k] < 32)
4717         k++;
4718       tableInFile = &buffer[k];
4719       while (buffer[k] > 32)
4720         k++;
4721       buffer[k] = 0;
4722       strcat (newList, tableInFile);
4723       fclose (l2t);
4724       fileCount = 0;
4725       errorCount = 0;
4726       return newList;
4727     }
4728   fclose (l2t);
4729   return NULL;
4730 }
4731
4732 static void *
4733 compileTranslationTable (const char *tl)
4734 {
4735 /*compile source tables into a table in memory */
4736   const char *tableList;
4737   int k;
4738   char mainTable[MAXSTRING];
4739   char subTable[MAXSTRING];
4740   int listLength;
4741   int currentListPos = 0;
4742   errorCount = 0;
4743   warningCount = 0;
4744   fileCount = 0;
4745   table = NULL;
4746   characterClasses = NULL;
4747   ruleNames = NULL;
4748   tableList = doLang2table (tl);
4749   if (tableList == NULL)
4750     return NULL;
4751   if (!opcodeLengths[0])
4752     {
4753       TranslationTableOpcode opcode;
4754       for (opcode = 0; opcode < CTO_None; opcode++)
4755         opcodeLengths[opcode] = strlen (opcodeNames[opcode]);
4756     }
4757   allocateHeader (NULL);
4758   /*Compile things that are necesary for the proper operation of 
4759      liblouis or liblouisxml or liblouisutdml */
4760   compileString ("space \\s 0");
4761   compileString ("noback sign \\x0000 0");
4762   compileString ("space \\x00a0 a unbreakable space");
4763   compileString ("space \\x001b 1b escape");
4764   compileString ("space \\xffff 123456789abcdef ENDSEGMENT");
4765   listLength = strlen (tableList);
4766   for (k = currentListPos; k < listLength; k++)
4767     if (tableList[k] == ',')
4768       break;
4769   if (k == listLength)
4770     {                           /* Only one file */
4771       strcpy (tablePath, tableList);
4772       for (k = strlen (tablePath); k >= 0; k--)
4773         if (tablePath[k] == '\\' || tablePath[k] == '/')
4774           break;
4775       strcpy (mainTable, &tablePath[k + 1]);
4776       tablePath[++k] = 0;
4777       if (!compileFile (mainTable))
4778         goto cleanup;
4779     }
4780   else
4781     {                           /* Compile a list of files */
4782       currentListPos = k + 1;
4783       strncpy (tablePath, tableList, k);
4784       tablePath[k] = 0;
4785       for (k = strlen (tablePath); k >= 0; k--)
4786         if (tablePath[k] == '\\' || tablePath[k] == '/')
4787           break;
4788       strcpy (mainTable, &tablePath[k + 1]);
4789       tablePath[++k] = 0;
4790       if (!compileFile (mainTable))
4791         goto cleanup;
4792       while (currentListPos < listLength)
4793         {
4794           for (k = currentListPos; k < listLength; k++)
4795             if (tableList[k] == ',')
4796               break;
4797           strncpy (subTable, &tableList[currentListPos], k - currentListPos);
4798           subTable[k - currentListPos] = 0;
4799           if (!compileFile (subTable))
4800             goto cleanup;
4801           currentListPos = k + 1;
4802         }
4803     }
4804 /*Clean up after compiling files*/
4805 cleanup:
4806   if (characterClasses)
4807     deallocateCharacterClasses ();
4808   if (ruleNames)
4809     deallocateRuleNames ();
4810   if (warningCount)
4811     lou_logPrint ("%d warnings issued", warningCount);
4812   if (!errorCount)
4813     {
4814       setDefaults ();
4815       table->tableSize = tableSize;
4816       table->bytesUsed = tableUsed;
4817     }
4818   else
4819     {
4820       if (!(errorCount == 1 && fileCount == 1))
4821         lou_logPrint ("%d errors found.", errorCount);
4822       if (table)
4823         free (table);
4824       table = NULL;
4825     }
4826   return (void *) table;
4827 }
4828
4829 typedef struct
4830 {
4831   void *next;
4832   void *table;
4833   int tableListLength;
4834   char tableList[1];
4835 } ChainEntry;
4836 static ChainEntry *tableChain = NULL;
4837 static ChainEntry *lastTrans = NULL;
4838 static void *
4839 getTable (const char *tableList)
4840 {
4841 /*Keep track of which tables have already been compiled */
4842   int tableListLen;
4843   ChainEntry *currentEntry = NULL;
4844   ChainEntry *lastEntry = NULL;
4845   void *newTable;
4846   if (tableList == NULL || *tableList == 0)
4847     return NULL;
4848   errorCount = fileCount = 0;
4849   tableListLen = strlen (tableList);
4850   /*See if this is the last table used. */
4851   if (lastTrans != NULL)
4852     if (tableListLen == lastTrans->tableListLength && (memcmp
4853                                                        (&lastTrans->
4854                                                         tableList
4855                                                         [0],
4856                                                         tableList,
4857                                                         tableListLen)) == 0)
4858       return (table = lastTrans->table);
4859 /*See if Table has already been compiled*/
4860   currentEntry = tableChain;
4861   while (currentEntry != NULL)
4862     {
4863       if (tableListLen == currentEntry->tableListLength && (memcmp
4864                                                             (&currentEntry->
4865                                                              tableList
4866                                                              [0],
4867                                                              tableList,
4868                                                              tableListLen))
4869           == 0)
4870         {
4871           lastTrans = currentEntry;
4872           return (table = currentEntry->table);
4873         }
4874       lastEntry = currentEntry;
4875       currentEntry = currentEntry->next;
4876     }
4877   if ((newTable = compileTranslationTable (tableList)))
4878     {
4879       /*Add a new entry to the table chain. */
4880       int entrySize = sizeof (ChainEntry) + tableListLen;
4881       ChainEntry *newEntry = malloc (entrySize);
4882       if (tableChain == NULL)
4883         tableChain = newEntry;
4884       else
4885         lastEntry->next = newEntry;
4886       newEntry->next = NULL;
4887       newEntry->table = newTable;
4888       newEntry->tableListLength = tableListLen;
4889       memcpy (&newEntry->tableList[0], tableList, tableListLen);
4890       lastTrans = newEntry;
4891       return newEntry->table;
4892     }
4893   return NULL;
4894 }
4895
4896 char *
4897 getLastTableList ()
4898 {
4899   if (lastTrans == NULL)
4900     return NULL;
4901   strncpy (scratchBuf, lastTrans->tableList, lastTrans->tableListLength);
4902   scratchBuf[lastTrans->tableListLength] = 0;
4903   return scratchBuf;
4904 }
4905
4906 void *EXPORT_CALL
4907 lou_getTable (const char *tableList)
4908 {
4909 /* Search paths for tables and keep track of compiled tables. */
4910   void *table = NULL;
4911   char *pathList;
4912   char pathEnd[2];
4913   char trialPath[MAXSTRING];
4914   if (tableList == NULL || tableList[0] == 0)
4915     return NULL;
4916   errorCount = fileCount = 0;
4917   pathEnd[0] = DIR_SEP;
4918   pathEnd[1] = 0;
4919   /* See if table is on environment path LOUIS_TABLEPATH */
4920   pathList = getenv ("LOUIS_TABLEPATH");
4921   if (pathList)
4922     while (1)
4923       {
4924         int k;
4925         int listLength;
4926         int currentListPos = 0;
4927         listLength = strlen (pathList);
4928         for (k = 0; k < listLength; k++)
4929           if (pathList[k] == ',')
4930             break;
4931         if (k == listLength || k == 0)
4932           {                     /* Only one file */
4933             strcpy (trialPath, pathList);
4934             strcat (trialPath, pathEnd);
4935             strcat (trialPath, tableList);
4936             table = getTable (trialPath);
4937             if (table)
4938               break;
4939           }
4940         else
4941           {                     /* Compile a list of files */
4942             strncpy (trialPath, pathList, k);
4943             trialPath[k] = 0;
4944             strcat (trialPath, pathEnd);
4945             strcat (trialPath, tableList);
4946             currentListPos = k + 1;
4947             table = getTable (trialPath);
4948             if (table)
4949               break;
4950             while (currentListPos < listLength)
4951               {
4952                 for (k = currentListPos; k < listLength; k++)
4953                   if (pathList[k] == ',')
4954                     break;
4955                 strncpy (trialPath,
4956                          &pathList[currentListPos], k - currentListPos);
4957                 trialPath[k - currentListPos] = 0;
4958                 strcat (trialPath, pathEnd);
4959                 strcat (trialPath, tableList);
4960                 table = getTable (trialPath);
4961                 currentListPos = k + 1;
4962                 if (table)
4963                   break;
4964               }
4965           }
4966         break;
4967       }
4968   if (!table)
4969     {
4970       /* See if table in current directory or on a path in 
4971        * the table name*/
4972       if (errorCount > 0 && (!(errorCount == 1 && fileCount == 1)))
4973         return NULL;
4974       table = getTable (tableList);
4975     }
4976   if (!table)
4977     {
4978 /* See if table on dataPath. */
4979       if (errorCount > 0 && (!(errorCount == 1 && fileCount == 1)))
4980         return NULL;
4981       pathList = lou_getDataPath ();
4982       if (pathList)
4983         {
4984           strcpy (trialPath, pathList);
4985           strcat (trialPath, pathEnd);
4986 #ifdef _WIN32
4987           strcat (trialPath, "liblouis\\tables\\");
4988 #else
4989           strcat (trialPath, "liblouis/tables/");
4990 #endif
4991           strcat (trialPath, tableList);
4992           table = getTable (trialPath);
4993         }
4994     }
4995   if (!table)
4996     {
4997       /* See if table on installed or program path. */
4998       if (errorCount > 0 && (!(errorCount == 1 && fileCount == 1)))
4999         return NULL;
5000 #ifdef _WIN32
5001       strcpy (trialPath, lou_getProgramPath ());
5002       strcat (trialPath, "\\share\\liblouss\\tables\\");
5003 #else
5004       strcpy (trialPath, TABLESDIR);
5005       strcat (trialPath, pathEnd);
5006 #endif
5007       strcat (trialPath, tableList);
5008       table = getTable (trialPath);
5009     }
5010   if (!table)
5011     lou_logPrint ("%s could not be found", tableList);
5012   return table;
5013 }
5014
5015 static unsigned char *destSpacing = NULL;
5016 static int sizeDestSpacing = 0;
5017 static unsigned short *typebuf = NULL;
5018 static int sizeTypebuf = 0;
5019 static widechar *passbuf1 = NULL;
5020 static int sizePassbuf1 = 0;
5021 static widechar *passbuf2 = NULL;
5022 static int sizePassbuf2 = 0;
5023 static int *srcMapping = NULL;
5024 static int *prevSrcMapping = NULL;
5025 static int sizeSrcMapping = 0;
5026 static int sizePrevSrcMapping = 0;
5027 void *
5028 liblouis_allocMem (AllocBuf buffer, int srcmax, int destmax)
5029 {
5030   if (srcmax < 1024)
5031     srcmax = 1024;
5032   if (destmax < 1024)
5033     destmax = 1024;
5034   switch (buffer)
5035     {
5036     case alloc_typebuf:
5037       if (destmax > sizeTypebuf)
5038         {
5039           if (typebuf != NULL)
5040             free (typebuf);
5041           typebuf = malloc ((destmax + 4) * sizeof (unsigned short));
5042           sizeTypebuf = destmax;
5043         }
5044       return typebuf;
5045     case alloc_destSpacing:
5046       if (destmax > sizeDestSpacing)
5047         {
5048           if (destSpacing != NULL)
5049             free (destSpacing);
5050           destSpacing = malloc (destmax + 4);
5051           sizeDestSpacing = destmax;
5052         }
5053       return destSpacing;
5054     case alloc_passbuf1:
5055       if (destmax > sizePassbuf1)
5056         {
5057           if (passbuf1 != NULL)
5058             free (passbuf1);
5059           passbuf1 = malloc ((destmax + 4) * CHARSIZE);
5060           sizePassbuf1 = destmax;
5061         }
5062       return passbuf1;
5063     case alloc_passbuf2:
5064       if (destmax > sizePassbuf2)
5065         {
5066           if (passbuf2 != NULL)
5067             free (passbuf2);
5068           passbuf2 = malloc ((destmax + 4) * CHARSIZE);
5069           sizePassbuf2 = destmax;
5070         }
5071       return passbuf2;
5072     case alloc_srcMapping:
5073       {
5074         int mapSize;
5075         if (srcmax >= destmax)
5076           mapSize = srcmax;
5077         else
5078           mapSize = destmax;
5079         if (mapSize > sizeSrcMapping)
5080           {
5081             if (srcMapping != NULL)
5082               free (srcMapping);
5083             srcMapping = malloc ((mapSize + 4) * sizeof (int));
5084             sizeSrcMapping = mapSize;
5085           }
5086       }
5087       return srcMapping;
5088     case alloc_prevSrcMapping:
5089       {
5090         int mapSize;
5091         if (srcmax >= destmax)
5092           mapSize = srcmax;
5093         else
5094           mapSize = destmax;
5095         if (mapSize > sizePrevSrcMapping)
5096           {
5097             if (prevSrcMapping != NULL)
5098               free (prevSrcMapping);
5099             prevSrcMapping = malloc ((mapSize + 4) * sizeof (int));
5100             sizePrevSrcMapping = mapSize;
5101           }
5102       }
5103       return prevSrcMapping;
5104     default:
5105       return NULL;
5106     }
5107 }
5108
5109 void EXPORT_CALL
5110 lou_free (void)
5111 {
5112   ChainEntry *currentEntry;
5113   ChainEntry *previousEntry;
5114   if (logFile != NULL)
5115     fclose (logFile);
5116   if (tableChain != NULL)
5117     {
5118       currentEntry = tableChain;
5119       while (currentEntry)
5120         {
5121           free (currentEntry->table);
5122           previousEntry = currentEntry;
5123           currentEntry = currentEntry->next;
5124           free (previousEntry);
5125         }
5126       tableChain = NULL;
5127       lastTrans = NULL;
5128     }
5129   if (typebuf != NULL)
5130     free (typebuf);
5131   typebuf = NULL;
5132   sizeTypebuf = 0;
5133   if (destSpacing != NULL)
5134     free (destSpacing);
5135   destSpacing = NULL;
5136   sizeDestSpacing = 0;
5137   if (passbuf1 != NULL)
5138     free (passbuf1);
5139   passbuf1 = NULL;
5140   sizePassbuf1 = 0;
5141   if (passbuf2 != NULL)
5142     free (passbuf2);
5143   passbuf2 = NULL;
5144   sizePassbuf2 = 0;
5145   if (srcMapping != NULL)
5146     free (srcMapping);
5147   srcMapping = NULL;
5148   sizeSrcMapping = 0;
5149   if (prevSrcMapping != NULL)
5150     free (prevSrcMapping);
5151   prevSrcMapping = NULL;
5152   sizePrevSrcMapping = 0;
5153   opcodeLengths[0] = 0;
5154 }
5155
5156 char *EXPORT_CALL
5157 lou_version ()
5158 {
5159   static char *version = PACKAGE_VERSION;
5160   return version;
5161 }
5162
5163 int EXPORT_CALL
5164 lou_charSize (void)
5165 {
5166   return CHARSIZE;
5167 }
5168
5169 int EXPORT_CALL
5170 lou_compileString (const char *tableList, const char *inString)
5171 {
5172   if (!lou_getTable (tableList))
5173     return 0;
5174   return compileString (inString);
5175 }
5176
5177 /**
5178  * This procedure provides a target for cals that serve as breakpoints 
5179  * for gdb.
5180  */
5181 /*
5182 char *EXPORT_CALL
5183 lou_getTablePaths ()
5184 {
5185   static char paths[MAXSTRING];
5186   char *pathList;
5187   strcpy (paths, tablePath);
5188   strcat (paths, ",");
5189   pathList = getenv ("LOUIS_TABLEPATH");
5190   if (pathList)
5191     {
5192       strcat (paths, pathList);
5193       strcat (paths, ",");
5194     }
5195   pathList = getcwd (scratchBuf, MAXSTRING);
5196   if (pathList)
5197     {
5198       strcat (paths, pathList);
5199       strcat (paths, ",");
5200     }
5201   pathList = lou_getDataPath ();
5202   if (pathList)
5203     {
5204       strcat (paths, pathList);
5205       strcat (paths, ",");
5206     }
5207 #ifdef _WIN32
5208   strcpy (paths, lou_getProgramPath ());
5209   strcat (paths, "\\share\\liblouss\\tables\\");
5210 #else
5211   strcpy (paths, TABLESDIR);
5212 #endif
5213   return paths;
5214 }
5215 */
5216
5217 void debugHook ()
5218 {
5219   char *hook = "debug hook";
5220   printf ("%s\n", hook);
5221 }