root/third_party/liblouis/overrides/liblouis/compileTranslationTable.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. noMemory
  2. reallocWrapper
  3. strdupWrapper
  4. lou_getProgramPath
  5. lou_setDataPath
  6. lou_getDataPath
  7. lou_logFile
  8. lou_logPrint
  9. lou_logEnd
  10. eqasc2uni
  11. showString
  12. showDots
  13. showAttributes
  14. getAChar
  15. getALine
  16. getToken
  17. compileError
  18. compileWarning
  19. allocateSpaceInTable
  20. reserveSpaceInTable
  21. allocateHeader
  22. stringHash
  23. charHash
  24. compile_findCharOrDots
  25. definedCharOrDots
  26. addCharOrDots
  27. getCharOrDots
  28. getDotsForChar
  29. getCharFromDots
  30. putCharAndDots
  31. unknownDots
  32. charactersDefined
  33. add_0_single
  34. add_0_multiple
  35. add_1_single
  36. add_1_multiple
  37. makeRuleChain
  38. addPassRule
  39. findCharacterClass
  40. addCharacterClass
  41. deallocateCharacterClasses
  42. allocateCharacterClasses
  43. getOpcode
  44. findOpcodeNumber
  45. findOpcodeName
  46. hexValue
  47. parseChars
  48. extParseChars
  49. parseDots
  50. extParseDots
  51. getCharacters
  52. getRuleCharsText
  53. getRuleDotsText
  54. getRuleDotsPattern
  55. getCharacterClass
  56. includeFile
  57. findRuleName
  58. addRuleName
  59. deallocateRuleNames
  60. compileSwapDots
  61. compileSwap
  62. getNumber
  63. passGetAttributes
  64. passGetEmphasis
  65. passGetDots
  66. passGetString
  67. passGetNumber
  68. passGetName
  69. passIsKeyword
  70. passFindName
  71. passAddName
  72. passGetScriptToken
  73. passIsLeftParen
  74. passIsName
  75. passIsComma
  76. passIsNumber
  77. passIsRightParen
  78. passGetRange
  79. passInsertAttributes
  80. compilePassOpcode
  81. compileBrailleIndicator
  82. compileNumber
  83. compileGrouping
  84. compileUplow
  85. hyphenStringHash
  86. hyphenHashNew
  87. hyphenHashFree
  88. hyphenHashInsert
  89. hyphenHashLookup
  90. hyphenGetNewState
  91. hyphenAddTrans
  92. compileHyphenation
  93. compileNoBreak
  94. compileCharDef
  95. compileRule
  96. lou_readCharFromFile
  97. findTable
  98. compileFile
  99. compileString
  100. makeDoubleRule
  101. setDefaults
  102. doLang2table
  103. compileTranslationTable
  104. getTable
  105. getLastTableList
  106. lou_getTable
  107. liblouis_allocMem
  108. lou_free
  109. lou_version
  110. lou_charSize
  111. lou_compileString
  112. debugHook

/* liblouis Braille Translation and Back-Translation 
Library

   Based on the Linux screenreader BRLTTY, copyright (C) 1999-2006 by
   The BRLTTY Team

   Copyright (C) 2004, 2005, 2006
   ViewPlus Technologies, Inc. www.viewplus.com
   and
   JJB Software, Inc. www.jjb-software.com
   All rights reserved

   This file is free software; you can redistribute it and/or modify it
   under the terms of the Lesser or Library GNU General Public License 
   as published by the
   Free Software Foundation; either version 3, or (at your option) any
   later version.

   This file is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
   Library GNU General Public License for more details.

   You should have received a copy of the Library GNU General Public 
   License along with this program; see the file COPYING.  If not, write to
   the Free Software Foundation, 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.

   Maintained by John J. Boyer john.boyer@jjb-software.com
   */

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
//#include <unistd.h>

#include "louis.h"
#include "config.h"

#define QUOTESUB 28             /*Stand-in for double quotes in strings */


/* Contributed by Michel Such <michel.such@free.fr */
#ifdef _WIN32

/* Adapted from BRLTTY code (see sys_progs_wihdows.h) */

#include <shlobj.h>

static void
noMemory (void)
{
  printf ("Insufficient memory: %s", strerror (errno), "\n");
  exit (3);
}

static void *
reallocWrapper (void *address, size_t size)
{
  if (!(address = realloc (address, size)) && size)
    noMemory ();
  return address;
}

static char *
strdupWrapper (const char *string)
{
  char *address = strdup (string);
  if (!address)
    noMemory ();
  return address;
}

char *EXPORT_CALL
lou_getProgramPath (void)
{
  char *path = NULL;
  HMODULE handle;

  if ((handle = GetModuleHandle (NULL)))
    {
      size_t size = 0X80;
      char *buffer = NULL;

      while (1)
        {
          buffer = reallocWrapper (buffer, size <<= 1);

          {
            DWORD length = GetModuleFileName (handle, buffer, size);

            if (!length)
              {
                printf ("GetModuleFileName\n");
                exit (3);
                3;
              }

            if (length < size)
              {
                buffer[length] = 0;
                path = strdupWrapper (buffer);

                while (length > 0)
                  if (path[--length] == '\\')
                    break;

                strncpy (path, path, length + 1);
                path[length + 1] = '\0';
                break;
              }
          }
        }

      free (buffer);
    }
  else
    {
      printf ("GetModuleHandle\n");
      exit (3);
    }

  return path;
}

#define PATH_SEP ';'
#define DIR_SEP '\\'
#else
#define PATH_SEP ':'
#define DIR_SEP '/'
#endif
/* End of MS contribution */

#ifdef ANDROID
#include "android/log.h"
#endif

/* The folowing variables and functions make it possible to specify the 
* path on which all tables for liblouis and all files for liblouisutdml, 
* in their proper directories, will be found.
*/

static char dataPath[MAXSTRING];
static char *dataPathPtr;

char *EXPORT_CALL
lou_setDataPath (char *path)
{
  dataPathPtr = NULL;
  if (path == NULL)
    return NULL;
  strcpy (dataPath, path);
  dataPathPtr = dataPath;
  return dataPathPtr;
}

char *EXPORT_CALL
lou_getDataPath ()
{
  return dataPathPtr;
}

/* End of dataPath code.*/

static char tablePath[MAXSTRING];
static FILE *logFile = NULL;
static char initialLogFileName[256];

void EXPORT_CALL
lou_logFile (const char *fileName)
{
  if (fileName == NULL || fileName[0] == 0)
    return;
  if (initialLogFileName[0] == 0)
    strcpy (initialLogFileName, fileName);
  logFile = fopen (fileName, "wb");
  if (logFile == NULL && initialLogFileName[0] != 0)
    logFile = fopen (initialLogFileName, "wb");
  if (logFile == NULL)
    {
      fprintf (stderr, "Cannot open log file %s\n", fileName);
      logFile = stderr;
    }
}

void EXPORT_CALL
lou_logPrint (char *format, ...)
{
#ifndef __SYMBIAN32__
  va_list argp;
  if (format == NULL)
    return;
  if (logFile == NULL && initialLogFileName[0] != 0)
    logFile = fopen (initialLogFileName, "wb");
  if (logFile == NULL)
  logFile = stderr;
  va_start (argp, format);
#ifndef ANDROID
  vfprintf (logFile, format, argp);
  fprintf (logFile, "\n");
#else
  __android_log_vprint(ANDROID_LOG_DEBUG, "liblouis", format, argp);
#endif
  va_end (argp);
#endif
}

void EXPORT_CALL
lou_logEnd (void)
{
  if (logFile != NULL)
    fclose (logFile);
  logFile = NULL;
}

static int
eqasc2uni (const unsigned char *a, const widechar * b, const int len)
{
  int k;
  for (k = 0; k < len; k++)
    if ((widechar) a[k] != b[k])
      return 0;
  return 1;
}

typedef struct
{
  widechar length;
  widechar chars[MAXSTRING];
}
CharsString;

static int errorCount;
static int warningCount;
static TranslationTableHeader *table;
static TranslationTableOffset tableSize;
static TranslationTableOffset tableUsed;

static const char *characterClassNames[] = {
  "space",
  "letter",
  "digit",
  "punctuation",
  "uppercase",
  "lowercase",
  "math",
  "sign",
  "litdigit",
  NULL
};

struct CharacterClass
{
  struct CharacterClass *next;
  TranslationTableCharacterAttributes attribute;
  widechar length;
  widechar name[1];
};
static struct CharacterClass *characterClasses;
static TranslationTableCharacterAttributes characterClassAttribute;

static const char *opcodeNames[CTO_None] = {
  "include",
  "locale",
  "undefined",
  "capsign",
  "begcaps",
  "lenbegcaps",
  "endcaps",
  "firstwordcaps",
  "lastwordbeforecaps",
  "lastwordaftercaps",
  "lencapsphrase",
  "letsign",
  "noletsignbefore",
  "noletsign",
  "noletsignafter",
  "numsign",
  "firstwordital",
  "italsign",
  "lastworditalbefore",
  "lastworditalafter",
  "begital",
  "firstletterital",
  "endital",
  "lastletterital",
  "singleletterital",
  "italword",
  "lenitalphrase",
  "firstwordbold",
  "boldsign",
  "lastwordboldbefore",
  "lastwordboldafter",
  "begbold",
  "firstletterbold",
  "endbold",
  "lastletterbold",
  "singleletterbold",
  "boldword",
  "lenboldphrase",
  "firstwordunder",
  "undersign",
  "lastwordunderbefore",
  "lastwordunderafter",
  "begunder",
  "firstletterunder",
  "endunder",
  "lastletterunder",
  "singleletterunder",
  "underword",
  "lenunderphrase",
  "begcomp",
  "compbegemph1",
  "compendemph1",
  "compbegemph2",
  "compendemph2",
  "compbegemph3",
  "compendemph3",
  "compcapsign",
  "compbegcaps",
  "compendcaps",
  "endcomp",
  "multind",
  "compdots",
  "comp6",
  "class",
  "after",
  "before",
  "noback",
  "nofor",
  "swapcc",
  "swapcd",
  "swapdd",
  "space",
  "digit",
  "punctuation",
  "math",
  "sign",
  "letter",
  "uppercase",
  "lowercase",
  "grouping",
  "uplow",
  "litdigit",
  "display",
  "replace",
  "context",
  "correct",
  "pass2",
  "pass3",
  "pass4",
  "repeated",
  "repword",
  "capsnocont",
  "always",
  "exactdots",
  "nocross",
  "syllable",
  "nocont",
  "compbrl",
  "literal",
  "largesign",
  "word",
  "partword",
  "joinnum",
  "joinword",
  "lowword",
  "contraction",
  "sufword",
  "prfword",
  "begword",
  "begmidword",
  "midword",
  "midendword",
  "endword",
  "prepunc",
  "postpunc",
  "begnum",
  "midnum",
  "endnum",
  "decpoint",
  "hyphen",
  "nobreak"
};
static short opcodeLengths[CTO_None] = { 0 };

typedef enum
{ noEncoding, bigEndian, littleEndian, ascii8 } EncodingType;


typedef struct
{
  const char *fileName;
  FILE *in;
  int lineNumber;
  EncodingType encoding;
  int status;
  int linelen;
  int linepos;
  int checkencoding[2];
  widechar line[MAXSTRING];
}
FileInfo;

static char scratchBuf[MAXSTRING];

char *
showString (widechar const *chars, int length)
{
/*Translate a string of characters to the encoding used in character 
* operands */
  int charPos;
  int bufPos = 0;
  scratchBuf[bufPos++] = '\'';
  for (charPos = 0; charPos < length; charPos++)
    {
      if (chars[charPos] >= 32 && chars[charPos] < 127)
        scratchBuf[bufPos++] = (char) chars[charPos];
      else
        {
          char hexbuf[20];
          int hexLength;
          char escapeLetter;

          int leadingZeros;
          int hexPos;
          hexLength = sprintf (hexbuf, "%x", chars[charPos]);
          switch (hexLength)
            {
            case 1:
            case 2:
            case 3:
            case 4:
              escapeLetter = 'x';
              leadingZeros = 4 - hexLength;
              break;
            case 5:
              escapeLetter = 'y';
              leadingZeros = 0;
              break;
            case 6:
            case 7:
            case 8:
              escapeLetter = 'z';
              leadingZeros = 8 - hexLength;
              break;
            default:
              escapeLetter = '?';
              leadingZeros = 0;
              break;
            }
          if ((bufPos + leadingZeros + hexLength + 4) >= sizeof (scratchBuf))
            break;
          scratchBuf[bufPos++] = '\\';
          scratchBuf[bufPos++] = escapeLetter;
          for (hexPos = 0; hexPos < leadingZeros; hexPos++)
            scratchBuf[bufPos++] = '0';
          for (hexPos = 0; hexPos < hexLength; hexPos++)
            scratchBuf[bufPos++] = hexbuf[hexPos];
        }
    }
  scratchBuf[bufPos++] = '\'';
  scratchBuf[bufPos] = 0;
  return scratchBuf;
}

char *
showDots (widechar const *dots, int length)
{
/* Translate a sequence of dots to the encoding used in dots operands. 
*/
  int bufPos = 0;
  int dotsPos;
  for (dotsPos = 0; bufPos < sizeof (scratchBuf) && dotsPos < length;
       dotsPos++)
    {
      if ((dots[dotsPos] & B1))
        scratchBuf[bufPos++] = '1';
      if ((dots[dotsPos] & B2))
        scratchBuf[bufPos++] = '2';
      if ((dots[dotsPos] & B3))
        scratchBuf[bufPos++] = '3';
      if ((dots[dotsPos] & B4))
        scratchBuf[bufPos++] = '4';
      if ((dots[dotsPos] & B5))
        scratchBuf[bufPos++] = '5';
      if ((dots[dotsPos] & B6))
        scratchBuf[bufPos++] = '6';
      if ((dots[dotsPos] & B7))
        scratchBuf[bufPos++] = '7';
      if ((dots[dotsPos] & B8))
        scratchBuf[bufPos++] = '8';
      if ((dots[dotsPos] & B9))
        scratchBuf[bufPos++] = '9';
      if ((dots[dotsPos] & B10))
        scratchBuf[bufPos++] = 'A';
      if ((dots[dotsPos] & B11))
        scratchBuf[bufPos++] = 'B';
      if ((dots[dotsPos] & B12))
        scratchBuf[bufPos++] = 'C';
      if ((dots[dotsPos] & B13))
        scratchBuf[bufPos++] = 'D';
      if ((dots[dotsPos] & B14))
        scratchBuf[bufPos++] = 'E';
      if ((dots[dotsPos] & B15))
        scratchBuf[bufPos++] = 'F';
      if ((dots[dotsPos] == B16))
        scratchBuf[bufPos++] = '0';
      if (dotsPos != length - 1)
        scratchBuf[bufPos++] = '-';
    }
  scratchBuf[bufPos] = 0;
  return &scratchBuf[0];
}

char *
showAttributes (TranslationTableCharacterAttributes a)
{
/* Show attributes using the letters used after the $ in multipass 
* opcodes. */
  int bufPos = 0;
  if ((a & CTC_Space))
    scratchBuf[bufPos++] = 's';
  if ((a & CTC_Letter))
    scratchBuf[bufPos++] = 'l';
  if ((a & CTC_Digit))
    scratchBuf[bufPos++] = 'd';
  if ((a & CTC_Punctuation))
    scratchBuf[bufPos++] = 'p';
  if ((a & CTC_UpperCase))
    scratchBuf[bufPos++] = 'U';
  if ((a & CTC_LowerCase))
    scratchBuf[bufPos++] = 'u';
  if ((a & CTC_Math))
    scratchBuf[bufPos++] = 'm';
  if ((a & CTC_Sign))
    scratchBuf[bufPos++] = 'S';
  if ((a & CTC_LitDigit))
    scratchBuf[bufPos++] = 'D';
  if ((a & CTC_Class1))
    scratchBuf[bufPos++] = 'w';
  if ((a & CTC_Class2))
    scratchBuf[bufPos++] = 'x';
  if ((a & CTC_Class3))
    scratchBuf[bufPos++] = 'y';
  if ((a & CTC_Class4))
    scratchBuf[bufPos++] = 'z';
  scratchBuf[bufPos] = 0;
  return scratchBuf;
}

static void compileError (FileInfo * nested, char *format, ...);

static int
getAChar (FileInfo * nested)
{
/*Read a big endian, little *ndian or ASCII 8 file and convert it to 
* 16- or 32-bit unsigned integers */
  int ch1 = 0, ch2 = 0;
  widechar character;
  if (nested->encoding == ascii8)
    if (nested->status == 2)
      {
        nested->status++;
        return nested->checkencoding[1];
      }
  while ((ch1 = fgetc (nested->in)) != EOF)
    {
      if (nested->status < 2)
        nested->checkencoding[nested->status] = ch1;
      nested->status++;
      if (nested->status == 2)
        {
          if (nested->checkencoding[0] == 0xfe
              && nested->checkencoding[1] == 0xff)
            nested->encoding = bigEndian;
          else if (nested->checkencoding[0] == 0xff
                   && nested->checkencoding[1] == 0xfe)
            nested->encoding = littleEndian;
          else if (nested->checkencoding[0] < 128
                   && nested->checkencoding[1] < 128)
            {
              nested->encoding = ascii8;
              return nested->checkencoding[0];
            }
          else
            {
              compileError (nested,
                            "encoding is neither big-endian, little-endian nor ASCII 8.");
              ch1 = EOF;
              break;;
            }
          continue;
        }
      switch (nested->encoding)
        {
        case noEncoding:
          break;
        case ascii8:
          return ch1;
          break;
        case bigEndian:
          ch2 = fgetc (nested->in);
          if (ch2 == EOF)
            break;
          character = (ch1 << 8) | ch2;
          return (int) character;
          break;
        case littleEndian:
          ch2 = fgetc (nested->in);
          if (ch2 == EOF)
            break;
          character = (ch2 << 8) | ch1;
          return (int) character;
          break;
        }
      if (ch1 == EOF || ch2 == EOF)
        break;
    }
  return EOF;
}

static int
getALine (FileInfo * nested)
{
/*Read a line of widechar's from an input file */
  int ch;
  int pch = 0;
  nested->linelen = 0;
  while ((ch = getAChar (nested)) != EOF)
    {
      if (ch == 13)
        continue;
      if (pch == '\\' && ch == 10)
        {
          nested->linelen--;
          continue;
        }
      if (ch == 10 || nested->linelen >= MAXSTRING)
        break;
      nested->line[nested->linelen++] = (widechar) ch;
      pch = ch;
    }
  nested->line[nested->linelen] = 0;
  nested->linepos = 0;
  if (ch == EOF)
    return 0;
  nested->lineNumber++;
  return 1;
}

static int lastToken;
static int
getToken (FileInfo * nested, CharsString * result, const char *description)
{
/*Find the next string of contiguous non-whitespace characters. If this 
 * is the last token on the line, return 2 instead of 1. */
  while (nested->line[nested->linepos] && nested->line[nested->linepos] <= 32)
    nested->linepos++;
  result->length = 0;
  while (nested->line[nested->linepos] && nested->line[nested->linepos] > 32)
    result->chars[result->length++] = nested->line[nested->linepos++];
  if (!result->length)
    {
      /* Not enough tokens */
      if (description)
        compileError (nested, "%s not specified.", description);
      return 0;
    }
  result->chars[result->length] = 0;
  while (nested->line[nested->linepos] && nested->line[nested->linepos] <= 32)
    nested->linepos++;
  if (nested->line[nested->linepos] == 0)
    {
      lastToken = 1;
      return 2;
    }
  else
    {
      lastToken = 0;
      return 1;
    }
}

static void
compileError (FileInfo * nested, char *format, ...)
{
#ifndef __SYMBIAN32__
  char buffer[MAXSTRING];
  va_list arguments;
  va_start (arguments, format);
#ifdef _WIN32
  (void) _vsnprintf (buffer, sizeof (buffer), format, arguments);
#else
  (void) vsnprintf (buffer, sizeof (buffer), format, arguments);
#endif
  va_end (arguments);
  if (nested)
    lou_logPrint ("%s:%d: error: %s", nested->fileName,
                  nested->lineNumber, buffer);
  else
    lou_logPrint ("error: %s", buffer);
  errorCount++;
#endif
}

static void
compileWarning (FileInfo * nested, char *format, ...)
{
#ifndef __SYMBIAN32__
  char buffer[MAXSTRING];
  va_list arguments;
  va_start (arguments, format);
#ifdef _WIN32
  (void) _vsnprintf (buffer, sizeof (buffer), format, arguments);
#else
  (void) vsnprintf (buffer, sizeof (buffer), format, arguments);
#endif
  va_end (arguments);
  if (nested)
    lou_logPrint ("%s:%d: warning: %s", nested->fileName,
                  nested->lineNumber, buffer);
  else
    lou_logPrint ("warning: %s", buffer);
  warningCount++;
#endif
}

static int
allocateSpaceInTable (FileInfo * nested, TranslationTableOffset * offset,
                      int count)
{
/* allocate memory for translation table and expand previously allocated 
* memory if necessary */
  int spaceNeeded = ((count + OFFSETSIZE - 1) / OFFSETSIZE) * OFFSETSIZE;
  TranslationTableOffset size = tableUsed + spaceNeeded;
  if (size > tableSize)
    {
      void *newTable;
      size += (size / OFFSETSIZE);
      newTable = realloc (table, size);
      if (!newTable)
        {
          compileError (nested, "Not enough memory for translation table.");
          return 0;
        }
      memset (((unsigned char *) newTable) + tableSize, 0, size - tableSize);
      table = (TranslationTableHeader *) newTable;
      tableSize = size;
    }
  if (offset != NULL)
    {
      *offset = (tableUsed - sizeof (*table)) / OFFSETSIZE;
      tableUsed += spaceNeeded;
    }
  return 1;
}

static int
reserveSpaceInTable (FileInfo * nested, int count)
{
  return (allocateSpaceInTable (nested, NULL, count));
}

static int
allocateHeader (FileInfo * nested)
{
/*Allocate memory for the table header and a guess on the number of 
* rules */
  const TranslationTableOffset startSize = 2 * sizeof (*table);
  if (table)
    return 1;
  tableUsed = sizeof (*table) + OFFSETSIZE;     /*So no offset is ever zero */
  if (!(table = malloc (startSize)))
    {
      compileError (nested, "Not enough memory");
      if (table != NULL)
        free (table);
      table = NULL;
      return 0;
    }
  memset (table, 0, startSize);
  tableSize = startSize;
  return 1;
}

int
stringHash (const widechar * c)
{
/*hash function for strings */
  unsigned long int makeHash = (((unsigned long int) c[0] << 8) +
                                (unsigned long int) c[1]) % HASHNUM;
  return (int) makeHash;
}

int
charHash (widechar c)
{
  unsigned long int makeHash = (unsigned long int) c % HASHNUM;
  return (int) makeHash;
}

static TranslationTableCharacter *
compile_findCharOrDots (widechar c, int m)
{
/*Look up a character or dot pattern. If m is 0 look up a character, 
* otherwise look up a dot pattern. Although the algorithms are almost 
* identical, different tables are needed for characters and dots because 
* of the possibility of conflicts.*/
  TranslationTableCharacter *character;
  TranslationTableOffset bucket;
  unsigned long int makeHash = (unsigned long int) c % HASHNUM;
  if (m == 0)
    bucket = table->characters[makeHash];
  else
    bucket = table->dots[makeHash];
  while (bucket)
    {
      character = (TranslationTableCharacter *) & table->ruleArea[bucket];
      if (character->realchar == c)
        return character;
      bucket = character->next;
    }
  return NULL;
}

static TranslationTableCharacter noChar = { 0, 0, 0, CTC_Space, 32, 32, 32 };
static TranslationTableCharacter noDots =
  { 0, 0, 0, CTC_Space, B16, B16, B16 };
static char *unknownDots (widechar dots);

static TranslationTableCharacter *
definedCharOrDots (FileInfo * nested, widechar c, int m)
{
  TranslationTableCharacter *notFound;
  TranslationTableCharacter *charOrDots = compile_findCharOrDots (c, m);
  if (charOrDots)
    return charOrDots;
  if (m == 0)
    {
      notFound = &noChar;
      compileError (nested,
                    "character %s should be defined at this point but is not",
                    showString (&c, 1));
    }
  else
    {
      notFound = &noDots;
      compileError (nested,
                    "cell %s should be defined at this point but is not",
                    unknownDots (c));
    }
  return notFound;
}

static TranslationTableCharacter *
addCharOrDots (FileInfo * nested, widechar c, int m)
{
/*See if a character or dot pattern is in the appropriate table. If not, 
* insert it. In either 
* case, return a pointer to it. */
  TranslationTableOffset bucket;
  TranslationTableCharacter *character;
  TranslationTableCharacter *oldchar;
  TranslationTableOffset offset;
  unsigned long int makeHash;
  if ((character = compile_findCharOrDots (c, m)))
    return character;
  if (!allocateSpaceInTable (nested, &offset, sizeof (*character)))
    return NULL;
  character = (TranslationTableCharacter *) & table->ruleArea[offset];
  memset (character, 0, sizeof (*character));
  character->realchar = c;
  makeHash = (unsigned long int) c % HASHNUM;
  if (m == 0)
    bucket = table->characters[makeHash];
  else
    bucket = table->dots[makeHash];
  if (!bucket)
    {
      if (m == 0)
        table->characters[makeHash] = offset;
      else
        table->dots[makeHash] = offset;
    }
  else
    {
      oldchar = (TranslationTableCharacter *) & table->ruleArea[bucket];
      while (oldchar->next)
        oldchar =
          (TranslationTableCharacter *) & table->ruleArea[oldchar->next];
      oldchar->next = offset;
    }
  return character;
}

static CharOrDots *
getCharOrDots (widechar c, int m)
{
  CharOrDots *cdPtr;
  TranslationTableOffset bucket;
  unsigned long int makeHash = (unsigned long int) c % HASHNUM;
  if (m == 0)
    bucket = table->charToDots[makeHash];
  else
    bucket = table->dotsToChar[makeHash];
  while (bucket)
    {
      cdPtr = (CharOrDots *) & table->ruleArea[bucket];
      if (cdPtr->lookFor == c)
        return cdPtr;
      bucket = cdPtr->next;
    }
  return NULL;
}

widechar
getDotsForChar (widechar c)
{
  CharOrDots *cdPtr = getCharOrDots (c, 0);
  if (cdPtr)
    return cdPtr->found;
  return B16;
}

widechar
getCharFromDots (widechar d)
{
  CharOrDots *cdPtr = getCharOrDots (d, 1);
  if (cdPtr)
    return cdPtr->found;
  return ' ';
}

static int
putCharAndDots (FileInfo * nested, widechar c, widechar d)
{
  TranslationTableOffset bucket;
  CharOrDots *cdPtr;
  CharOrDots *oldcdPtr = NULL;
  TranslationTableOffset offset;
  unsigned long int makeHash;
  if (!(cdPtr = getCharOrDots (c, 0)))
    {
      if (!allocateSpaceInTable (nested, &offset, sizeof (*cdPtr)))
        return 0;
      cdPtr = (CharOrDots *) & table->ruleArea[offset];
      cdPtr->next = 0;
      cdPtr->lookFor = c;
      cdPtr->found = d;
      makeHash = (unsigned long int) c % HASHNUM;
      bucket = table->charToDots[makeHash];
      if (!bucket)
        table->charToDots[makeHash] = offset;
      else
        {
          oldcdPtr = (CharOrDots *) & table->ruleArea[bucket];
          while (oldcdPtr->next)
            oldcdPtr = (CharOrDots *) & table->ruleArea[oldcdPtr->next];
          oldcdPtr->next = offset;
        }
    }
  if (!(cdPtr = getCharOrDots (d, 1)))
    {
      if (!allocateSpaceInTable (nested, &offset, sizeof (*cdPtr)))
        return 0;
      cdPtr = (CharOrDots *) & table->ruleArea[offset];
      cdPtr->next = 0;
      cdPtr->lookFor = d;
      cdPtr->found = c;
      makeHash = (unsigned long int) d % HASHNUM;
      bucket = table->dotsToChar[makeHash];
      if (!bucket)
        table->dotsToChar[makeHash] = offset;
      else
        {
          oldcdPtr = (CharOrDots *) & table->ruleArea[bucket];
          while (oldcdPtr->next)
            oldcdPtr = (CharOrDots *) & table->ruleArea[oldcdPtr->next];
          oldcdPtr->next = offset;
        }
    }
  return 1;
}

static char *
unknownDots (widechar dots)
{
/*Print out dot numbers */
  static char buffer[20];
  int k = 1;
  buffer[0] = '\\';
  if ((dots & B1))
    buffer[k++] = '1';
  if ((dots & B2))
    buffer[k++] = '2';
  if ((dots & B3))
    buffer[k++] = '3';
  if ((dots & B4))
    buffer[k++] = '4';
  if ((dots & B5))
    buffer[k++] = '5';
  if ((dots & B6))
    buffer[k++] = '6';
  if ((dots & B7))
    buffer[k++] = '7';
  if ((dots & B8))
    buffer[k++] = '8';
  if ((dots & B9))
    buffer[k++] = '9';
  if ((dots & B10))
    buffer[k++] = 'A';
  if ((dots & B11))
    buffer[k++] = 'B';
  if ((dots & B12))
    buffer[k++] = 'C';
  if ((dots & B13))
    buffer[k++] = 'D';
  if ((dots & B14))
    buffer[k++] = 'E';
  if ((dots & B15))
    buffer[k++] = 'F';
  buffer[k++] = '/';
  buffer[k] = 0;
  return buffer;
}

static TranslationTableOffset newRuleOffset = 0;
static TranslationTableRule *newRule = NULL;

static int
charactersDefined (FileInfo * nested)
{
/*Check that all characters are defined by character-definition 
* opcodes*/
  int noErrors = 1;
  int k;
  if ((newRule->opcode >= CTO_Space && newRule->opcode <= CTO_LitDigit)
      || newRule->opcode == CTO_SwapDd
      ||
      newRule->opcode == CTO_Replace || newRule->opcode == CTO_MultInd
      || newRule->opcode == CTO_Repeated ||
      ((newRule->opcode >= CTO_Context && newRule->opcode <=
        CTO_Pass4) && newRule->opcode != CTO_Correct))
    return 1;
  for (k = 0; k < newRule->charslen; k++)
    if (!compile_findCharOrDots (newRule->charsdots[k], 0))
      {
        compileError (nested, "Character %s is not defined", showString
                      (&newRule->charsdots[k], 1));
        noErrors = 0;
      }
  if (!(newRule->opcode == CTO_Correct || newRule->opcode ==
        CTO_NoBreak || newRule->opcode == CTO_SwapCc || newRule->opcode ==
        CTO_SwapCd))
    {
      for (k = newRule->charslen; k < newRule->charslen + newRule->dotslen;
           k++)
        if (!compile_findCharOrDots (newRule->charsdots[k], 1))
          {
            compileError (nested, "Dot pattern %s is not defined.",
                          unknownDots (newRule->charsdots[k]));
            noErrors = 0;
          }
    }
  return noErrors;
}

static int noback = 0;
static int nofor = 0;

/*The following functions are 
called by addRule to handle various 
* cases.*/

static void
add_0_single (FileInfo * nested)
{
/*direction = 0, newRule->charslen = 1*/
  TranslationTableRule *currentRule;
  TranslationTableOffset *currentOffsetPtr;
  TranslationTableCharacter *character;
  int m = 0;
  if (newRule->opcode == CTO_CompDots || newRule->opcode == CTO_Comp6)
    return;
  if (newRule->opcode >= CTO_Pass2 && newRule->opcode <= CTO_Pass4)
    m = 1;
  character = definedCharOrDots (nested, newRule->charsdots[0], m);
  if (m != 1 && character->attributes & CTC_Letter && (newRule->opcode
                                                       ==
                                                       CTO_WholeWord
                                                       || newRule->opcode ==
                                                       CTO_LargeSign))
    {
      if (table->noLetsignCount < LETSIGNSIZE)
        table->noLetsign[table->noLetsignCount++] = newRule->charsdots[0];
    }
  if (newRule->opcode >= CTO_Space && newRule->opcode < CTO_UpLow)
    character->definitionRule = newRuleOffset;
  currentOffsetPtr = &character->otherRules;
  while (*currentOffsetPtr)
    {
      currentRule = (TranslationTableRule *)
        & table->ruleArea[*currentOffsetPtr];
      if (currentRule->charslen == 0)
        break;
      if (currentRule->opcode >= CTO_Space && currentRule->opcode < CTO_UpLow)
        if (!(newRule->opcode >= CTO_Space && newRule->opcode < CTO_UpLow))
          break;
      currentOffsetPtr = &currentRule->charsnext;
    }
  newRule->charsnext = *currentOffsetPtr;
  *currentOffsetPtr = newRuleOffset;
}

static void
add_0_multiple (void)
{
/*direction = 0 newRule->charslen > 1*/
  TranslationTableRule *currentRule = NULL;
  TranslationTableOffset *currentOffsetPtr =
    &table->forRules[stringHash (&newRule->charsdots[0])];
  while (*currentOffsetPtr)
    {
      currentRule = (TranslationTableRule *)
        & table->ruleArea[*currentOffsetPtr];
      if (newRule->charslen > currentRule->charslen)
        break;
      if (newRule->charslen == currentRule->charslen)
        if ((currentRule->opcode == CTO_Always)
            && (newRule->opcode != CTO_Always))
          break;
      currentOffsetPtr = &currentRule->charsnext;
    }
  newRule->charsnext = *currentOffsetPtr;
  *currentOffsetPtr = newRuleOffset;
}

static void
add_1_single (FileInfo * nested)
{
/*direction = 1, newRule->dotslen = 1*/
  TranslationTableRule *currentRule;
  TranslationTableOffset *currentOffsetPtr;
  TranslationTableCharacter *dots;
  if (newRule->opcode == CTO_NoBreak || newRule->opcode == CTO_SwapCc ||
      (newRule->opcode >= CTO_Context
       &&
       newRule->opcode <= CTO_Pass4)
      || newRule->opcode == CTO_Repeated || (newRule->opcode == CTO_Always
                                             && newRule->charslen == 1))
    return;                     /*too ambiguous */
  dots = definedCharOrDots (nested, newRule->charsdots[newRule->charslen], 1);
  if (newRule->opcode >= CTO_Space && newRule->opcode < CTO_UpLow)
    dots->definitionRule = newRuleOffset;
  currentOffsetPtr = &dots->otherRules;
  while (*currentOffsetPtr)
    {
      currentRule = (TranslationTableRule *)
        & table->ruleArea[*currentOffsetPtr];
      if (newRule->charslen > currentRule->charslen ||
          currentRule->dotslen == 0)
        break;
      if (currentRule->opcode >= CTO_Space && currentRule->opcode < CTO_UpLow)
        if (!(newRule->opcode >= CTO_Space && newRule->opcode < CTO_UpLow))
          break;
      currentOffsetPtr = &currentRule->dotsnext;
    }
  newRule->dotsnext = *currentOffsetPtr;
  *currentOffsetPtr = newRuleOffset;
}

static void
add_1_multiple (void)
{
/*direction = 1, newRule->dotslen > 1*/
  TranslationTableRule *currentRule = NULL;
  TranslationTableOffset *currentOffsetPtr = &table->backRules[stringHash
                                                               (&newRule->
                                                                charsdots
                                                                [newRule->
                                                                 charslen])];
  if (newRule->opcode == CTO_NoBreak || newRule->opcode == CTO_SwapCc ||
      (newRule->opcode >= CTO_Context && newRule->opcode <= CTO_Pass4))
    return;
  while (*currentOffsetPtr)
    {
      int currentLength;
      int newLength;
      currentRule = (TranslationTableRule *)
        & table->ruleArea[*currentOffsetPtr];
      currentLength = currentRule->dotslen + currentRule->charslen;
      newLength = newRule->dotslen + newRule->charslen;
      if (newLength > currentLength)
        break;
      if (currentLength == newLength)
        if ((currentRule->opcode == CTO_Always)
            && (newRule->opcode != CTO_Always))
          break;
      currentOffsetPtr = &currentRule->dotsnext;
    }
  newRule->dotsnext = *currentOffsetPtr;
  *currentOffsetPtr = newRuleOffset;
}

static void
makeRuleChain (TranslationTableOffset * offsetPtr)
{
  TranslationTableRule *currentRule;
  while (*offsetPtr)
    {
      currentRule = (TranslationTableRule *) & table->ruleArea[*offsetPtr];
      offsetPtr = &currentRule->charsnext;
    }
  newRule->charsnext = *offsetPtr;
  *offsetPtr = newRuleOffset;
}

static int
addPassRule (FileInfo * nested)
{
  TranslationTableOffset *offsetPtr;
  switch (newRule->opcode)
    {
    case CTO_Correct:
      offsetPtr = &table->attribOrSwapRules[0];
      break;
    case CTO_Context:
      offsetPtr = &table->attribOrSwapRules[1];
      break;
    case CTO_Pass2:
      offsetPtr = &table->attribOrSwapRules[2];
      break;
    case CTO_Pass3:
      offsetPtr = &table->attribOrSwapRules[3];
      break;
    case CTO_Pass4:
      offsetPtr = &table->attribOrSwapRules[4];
      break;
    default:
      return 0;
    }
  makeRuleChain (offsetPtr);
  return 1;
}

static int
  addRule
  (FileInfo * nested,
   TranslationTableOpcode opcode,
   CharsString * ruleChars,
   CharsString * ruleDots,
   TranslationTableCharacterAttributes after,
   TranslationTableCharacterAttributes before)
{
/*Add a rule to the table, using the hash function to find the start of 
* chains and chaining both the chars and dots strings */
  int ruleSize = sizeof (TranslationTableRule) - (DEFAULTRULESIZE * CHARSIZE);
  int direction = 0;            /*0 = forward translation; 1 = bacward */
  if (ruleChars)
    ruleSize += CHARSIZE * ruleChars->length;
  if (ruleDots)
    ruleSize += CHARSIZE * ruleDots->length;
  if (!allocateSpaceInTable (nested, &newRuleOffset, ruleSize))
    return 0;
  newRule = (TranslationTableRule *) & table->ruleArea[newRuleOffset];
  newRule->opcode = opcode;
  newRule->after = after;
  newRule->before = before;
  if (ruleChars)
    memcpy (&newRule->charsdots[0], &ruleChars->chars[0],
            CHARSIZE * (newRule->charslen = ruleChars->length));
  else
    newRule->charslen = 0;
  if (ruleDots)
    memcpy (&newRule->charsdots[newRule->charslen],
            &ruleDots->chars[0], CHARSIZE * (newRule->dotslen =
                                             ruleDots->length));
  else
    newRule->dotslen = 0;
  if (!charactersDefined (nested))
    return 0;

  /*link new rule into table. */
  if (opcode == CTO_SwapCc || opcode == CTO_SwapCd || opcode == CTO_SwapDd)
    return 1;
  if (opcode >= CTO_Context && opcode <= CTO_Pass4 && newRule->charslen == 0)
    return addPassRule (nested);
  if (newRule->charslen == 0 || nofor)
    direction = 1;
  while (direction < 2)
    {
      if (direction == 0 && newRule->charslen == 1)
        add_0_single (nested);
      else if (direction == 0 && newRule->charslen > 1)
        add_0_multiple ();
      else if (direction == 1 && newRule->dotslen == 1 && !noback)
        add_1_single (nested);
      else if (direction == 1 && newRule->dotslen > 1 && !noback)
        add_1_multiple ();
      else
        {
        }
      direction++;
      if (newRule->dotslen == 0)
        direction = 2;
    }
  return 1;
}

static const struct CharacterClass *
findCharacterClass (const CharsString * name)
{
/*Find a character class, whether predefined or user-defined */
  const struct CharacterClass *class = characterClasses;
  while (class)
    {
      if ((name->length == class->length) &&
          (memcmp (&name->chars[0], class->name, CHARSIZE *
                   name->length) == 0))
        return class;
      class = class->next;
    }
  return NULL;
}

static struct CharacterClass *
addCharacterClass (FileInfo * nested, const widechar * name, int length)
{
/*Define a character class, Whether predefined or user-defined */
  struct CharacterClass *class;
  if (characterClassAttribute)
    {
      if ((class = malloc (sizeof (*class) + CHARSIZE * (length - 1))))
        {
          memset (class, 0, sizeof (*class));
          memcpy (class->name, name, CHARSIZE * (class->length = length));
          class->attribute = characterClassAttribute;
          characterClassAttribute <<= 1;
          class->next = characterClasses;
          characterClasses = class;
          return class;
        }
    }
  compileError (nested, "character class table overflow.");
  return NULL;
}

static void
deallocateCharacterClasses (void)
{
  while (characterClasses)
    {
      struct CharacterClass *class = characterClasses;
      characterClasses = characterClasses->next;
      if (class)
        free (class);
    }
}

static int
allocateCharacterClasses (void)
{
/*Allocate memory for predifined character classes */
  int k = 0;
  characterClasses = NULL;
  characterClassAttribute = 1;
  while (characterClassNames[k])
    {
      widechar wname[MAXSTRING];
      int length = strlen (characterClassNames[k]);
      int kk;
      for (kk = 0; kk < length; kk++)
        wname[kk] = (widechar) characterClassNames[k][kk];
      if (!addCharacterClass (NULL, wname, length))
        {
          deallocateCharacterClasses ();
          return 0;
        }
      k++;
    }
  return 1;
}

static TranslationTableOpcode
getOpcode (FileInfo * nested, const CharsString * token)
{
  static TranslationTableOpcode lastOpcode = 0;
  TranslationTableOpcode opcode = lastOpcode;

  do
    {
      if (token->length == opcodeLengths[opcode])
        if (eqasc2uni ((unsigned char *) opcodeNames[opcode],
                       &token->chars[0], token->length))
          {
            lastOpcode = opcode;
            return opcode;
          }
      opcode++;
      if (opcode >= CTO_None)
        opcode = 0;
    }
  while (opcode != lastOpcode);
  compileError (nested, "opcode %s not defined.", showString
                (&token->chars[0], token->length));
  return CTO_None;
}

TranslationTableOpcode
findOpcodeNumber (const char *toFind)
{
/* Used by tools such as lou_debug */
  static TranslationTableOpcode lastOpcode = 0;
  TranslationTableOpcode opcode = lastOpcode;
  int length = strlen (toFind);
  do
    {
      if (length == opcodeLengths[opcode] && strcasecmp (toFind,
                                                         opcodeNames[opcode])
          == 0)
        {
          lastOpcode = opcode;
          return opcode;
        }
      opcode++;
      if (opcode >= CTO_None)
        opcode = 0;
    }
  while (opcode != lastOpcode);
  return CTO_None;
}

const char *
findOpcodeName (TranslationTableOpcode opcode)
{
/* Used by tools such as lou_debug */
  if (opcode < 0 || opcode >= CTO_None)
    {
      sprintf (scratchBuf, "%d", opcode);
      return scratchBuf;
    }
  return opcodeNames[opcode];
}

static widechar
hexValue (FileInfo * nested, const widechar * digits, int length)
{
  int k;
  unsigned int binaryValue = 0;
  for (k = 0; k < length; k++)
    {
      unsigned int hexDigit = 0;
      if (digits[k] >= '0' && digits[k] <= '9')
        hexDigit = digits[k] - '0';
      else if (digits[k] >= 'a' && digits[k] <= 'f')
        hexDigit = digits[k] - 'a' + 10;
      else if (digits[k] >= 'A' && digits[k] <= 'F')
        hexDigit = digits[k] - 'A' + 10;
      else
        {
          compileError (nested, "invalid %d-digit hexadecimal number",
                        length);
          return (widechar) 0xffffffff;
        }
      binaryValue |= hexDigit << (4 * (length - 1 - k));
    }
  return (widechar) binaryValue;
}

#define MAXBYTES 7
static int first0Bit[MAXBYTES] = { 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0XFE };

static int
parseChars (FileInfo * nested, CharsString * result, CharsString * token)
{
  int in = 0;
  int out = 0;
  int lastOutSize = 0;
  int lastIn;
  unsigned int ch = 0;
  int numBytes = 0;
  unsigned int utf32 = 0;
  int k;
  while (in < token->length)
    {
      ch = token->chars[in++] & 0xff;
      if (ch < 128)
        {
          if (ch == '\\')
            {                   /* escape sequence */
              switch (ch = token->chars[in])
                {
                case '\\':
                  break;
                case 'e':
                  ch = 0x1b;
                  break;
                case 'f':
                  ch = 12;
                  break;
                case 'n':
                  ch = 10;
                  break;
                case 'r':
                  ch = 13;
                  break;
                case 's':
                  ch = ' ';
                  break;
                case 't':
                  ch = 9;
                  break;
                case 'v':
                  ch = 22;
                  break;
                case 'w':
                  ch = ENDSEGMENT;
                  break;
                case 34:
                  ch = QUOTESUB;
                  break;
                case 'X':
                case 'x':
                  if (token->length - in > 4)
                    {
                      ch = hexValue (nested, &token->chars[in + 1], 4);
                      in += 4;
                    }
                  break;
                case 'y':
                case 'Y':
                  if (CHARSIZE == 2)
                    {
                    not32:
                      compileError (nested,
                                    "liblouis has not been compiled for 32-bit Unicode");
                      break;
                    }
                  if (token->length - in > 5)
                    {
                      ch = hexValue (nested, &token->chars[in + 1], 5);
                      in += 5;
                    }
                  break;
                case 'z':
                case 'Z':
                  if (CHARSIZE == 2)
                    goto not32;
                  if (token->length - in > 8)
                    {
                      ch = hexValue (nested, &token->chars[in + 1], 8);
                      in += 8;
                    }
                  break;
                default:
                  compileError (nested, "invalid escape sequence '\\%c'", ch);
                  break;
                }
              in++;
            }
          result->chars[out++] = (widechar) ch;
          if (out >= MAXSTRING)
            {
              result->length = out;
              return 1;
            }
          continue;
        }
      lastOutSize = out;
      lastIn = in;
      for (numBytes = MAXBYTES - 1; numBytes >= 0; numBytes--)
        if (ch >= first0Bit[numBytes])
          break;
      if (numBytes < 0)
        continue;
      utf32 = ch & (0XFF - first0Bit[numBytes]);
      for (k = 0; k < numBytes; k++)
        {
          if (in >= MAXSTRING)
            break;
          if (token->chars[in] < 128 || (token->chars[in] & 0x0040))
            {
              compileWarning (nested, "invalid UTF-8. Assuming Latin-1.");
              result->chars[out++] = token->chars[lastIn];
              in = lastIn + 1;
              continue;
            }
          utf32 = (utf32 << 6) + (token->chars[in++] & 0x3f);
        }
      if (CHARSIZE == 2 && utf32 > 0xffff)
        utf32 = 0xffff;
      result->chars[out++] = (widechar) utf32;
      if (out >= MAXSTRING)
        {
          result->length = lastOutSize;
          return 1;
        }
    }
  result->length = out;
  return 1;
}

int
extParseChars (const char *inString, widechar * outString)
{
/* Parse external character strings */
  CharsString wideIn;
  CharsString result;
  int k;
  for (k = 0; inString[k] && k < MAXSTRING; k++)
    wideIn.chars[k] = inString[k];
  wideIn.chars[k] = 0;
  wideIn.length = k;
  parseChars (NULL, &result, &wideIn);
  if (errorCount)
    {
      errorCount = 0;
      return 0;
    }
  for (k = 0; k < result.length; k++)
    outString[k] = result.chars[k];
  outString[k] = 0;
  return result.length;
}

static int
parseDots (FileInfo * nested, CharsString * cells, const CharsString * token)
{
/*get dot patterns */
  widechar cell = 0;            /*assembly place for dots */
  int cellCount = 0;
  int index;
  int start = 0;

  for (index = 0; index < token->length; index++)
    {
      int started = index != start;
      widechar character = token->chars[index];
      switch (character)
        {                       /*or dots to make up Braille cell */
          {
            int dot;
        case '1':
            dot = B1;
            goto haveDot;
        case '2':
            dot = B2;
            goto haveDot;
        case '3':
            dot = B3;
            goto haveDot;
        case '4':
            dot = B4;
            goto haveDot;
        case '5':
            dot = B5;
            goto haveDot;
        case '6':
            dot = B6;
            goto haveDot;
        case '7':
            dot = B7;
            goto haveDot;
        case '8':
            dot = B8;
            goto haveDot;
        case '9':
            dot = B9;
            goto haveDot;
        case 'a':
        case 'A':
            dot = B10;
            goto haveDot;
        case 'b':
        case 'B':
            dot = B11;
            goto haveDot;
        case 'c':
        case 'C':
            dot = B12;
            goto haveDot;
        case 'd':
        case 'D':
            dot = B13;
            goto haveDot;
        case 'e':
        case 'E':
            dot = B14;
            goto haveDot;
        case 'f':
        case 'F':
            dot = B15;
          haveDot:
            if (started && !cell)
              goto invalid;
            if (cell & dot)
              {
                compileError (nested, "dot specified more than once.");
                return 0;
              }
            cell |= dot;
            break;
          }
        case '0':               /*blank */
          if (started)
            goto invalid;
          break;
        case '-':               /*got all dots for this cell */
          if (!started)
            {
              compileError (nested, "missing cell specification.");
              return 0;
            }
          cells->chars[cellCount++] = cell | B16;
          cell = 0;
          start = index + 1;
          break;
        default:
        invalid:
          compileError (nested, "invalid dot number %s.", showString
                        (&character, 1));
          return 0;
        }
    }
  if (index == start)
    {
      compileError (nested, "missing cell specification.");
      return 0;
    }
  cells->chars[cellCount++] = cell | B16;       /*last cell */
  cells->length = cellCount;
  return 1;
}

int
extParseDots (const char *inString, widechar * outString)
{
/* Parse external dot patterns */
  CharsString wideIn;
  CharsString result;
  int k;
  for (k = 0; inString[k] && k < MAXSTRING; k++)
    wideIn.chars[k] = inString[k];
  wideIn.chars[k] = 0;
  wideIn.length = k;
  parseDots (NULL, &result, &wideIn);
  if (errorCount)
    {
      errorCount = 0;
      return 0;
    }
  for (k = 0; k < result.length; k++)
    outString[k] = result.chars[k];
  outString[k] = 0;
  return result.length;
}

static int
getCharacters (FileInfo * nested, CharsString * characters)
{
/*Get ruleChars string */
  CharsString token;
  if (getToken (nested, &token, "characters"))
    if (parseChars (nested, characters, &token))
      return 1;
  return 0;
}

static int
getRuleCharsText (FileInfo * nested, CharsString * ruleChars)
{
  CharsString token;
  if (getToken (nested, &token, "Characters operand"))
    if (parseChars (nested, ruleChars, &token))
      return 1;
  return 0;
}

static int
getRuleDotsText (FileInfo * nested, CharsString * ruleDots)
{
  CharsString token;
  if (getToken (nested, &token, "characters"))
    if (parseChars (nested, ruleDots, &token))
      return 1;
  return 0;
}

static int
getRuleDotsPattern (FileInfo * nested, CharsString * ruleDots)
{
/*Interpret the dets operand */
  CharsString token;
  if (getToken (nested, &token, "Dots operand"))
    {
      if (token.length == 1 && token.chars[0] == '=')
        {
          ruleDots->length = 0;
          return 1;
        }
      if (parseDots (nested, ruleDots, &token))
        return 1;
    }
  return 0;
}

static int
getCharacterClass (FileInfo * nested, const struct CharacterClass **class)
{
  CharsString token;
  if (getToken (nested, &token, "character class name"))
    {
      if ((*class = findCharacterClass (&token)))
        return 1;
      compileError (nested, "character class not defined.");
    }
  return 0;
}

static int compileFile (const char *fileName);

static int
includeFile (FileInfo * nested, CharsString * includedFile)
{
/*Implement include opcode*/
  int k;
  char includeThis[MAXSTRING];
  for (k = 0; k < includedFile->length; k++)
    includeThis[k] = (char) includedFile->chars[k];
  includeThis[k] = 0;
  return compileFile (includeThis);
}

struct RuleName
{
  struct RuleName *next;
  TranslationTableOffset ruleOffset;
  widechar length;
  widechar name[1];
};
static struct RuleName *ruleNames = NULL;
static TranslationTableOffset
findRuleName (const CharsString * name)
{
  const struct RuleName *nameRule = ruleNames;
  while (nameRule)
    {
      if ((name->length == nameRule->length) &&
          (memcmp (&name->chars[0], nameRule->name, CHARSIZE *
                   name->length) == 0))
        return nameRule->ruleOffset;
      nameRule = nameRule->next;
    }
  return 0;
}

static int
addRuleName (FileInfo * nested, CharsString * name)
{
  int k;
  struct RuleName *nameRule;
  if (!(nameRule = malloc (sizeof (*nameRule) + CHARSIZE *
                           (name->length - 1))))
    {
      compileError (nested, "not enough memory");
      return 0;
    }
  memset (nameRule, 0, sizeof (*nameRule));
  for (k = 0; k < name->length; k++)
    {
      TranslationTableCharacter *ch = definedCharOrDots
        (nested, name->chars[k],
         0);
      if (!(ch->attributes & CTC_Letter))
        {
          compileError (nested, "a name may contain only letters");
          return 0;
        }
      nameRule->name[k] = name->chars[k];
    }
  nameRule->length = name->length;
  nameRule->ruleOffset = newRuleOffset;
  nameRule->next = ruleNames;
  ruleNames = nameRule;
  return 1;
}

static void
deallocateRuleNames (void)
{
  while (ruleNames)
    {
      struct RuleName *nameRule = ruleNames;
      ruleNames = ruleNames->next;
      if (nameRule)
        free (nameRule);
    }
}

static int
compileSwapDots (FileInfo * nested, CharsString * source, CharsString * dest)
{
  int k = 0;
  int kk = 0;
  CharsString dotsSource;
  CharsString dotsDest;
  dest->length = 0;
  dotsSource.length = 0;
  while (k <= source->length)
    {
      if (source->chars[k] != ',' && k != source->length)
        dotsSource.chars[dotsSource.length++] = source->chars[k];
      else
        {
          if (!parseDots (nested, &dotsDest, &dotsSource))
            return 0;
          dest->chars[dest->length++] = dotsDest.length + 1;
          for (kk = 0; kk < dotsDest.length; kk++)
            dest->chars[dest->length++] = dotsDest.chars[kk];
          dotsSource.length = 0;
        }
      k++;
    }
  return 1;
}

static int
compileSwap (FileInfo * nested, TranslationTableOpcode opcode)
{
  CharsString ruleChars;
  CharsString ruleDots;
  CharsString name;
  CharsString matches;
  CharsString replacements;
  if (!getToken (nested, &name, "name operand"))
    return 0;
  if (!getToken (nested, &matches, "matches operand"))
    return 0;
  if (!getToken (nested, &replacements, "replacements operand"))
    return 0;
  if (opcode == CTO_SwapCc || opcode == CTO_SwapCd)
    {
      if (!parseChars (nested, &ruleChars, &matches))
        return 0;
    }
  else
    {
      if (!compileSwapDots (nested, &matches, &ruleChars))
        return 0;
    }
  if (opcode == CTO_SwapCc)
    {
      if (!parseChars (nested, &ruleDots, &replacements))
        return 0;
    }
  else
    {
      if (!compileSwapDots (nested, &replacements, &ruleDots))
        return 0;
    }
  if (!addRule (nested, opcode, &ruleChars, &ruleDots, 0, 0))
    return 0;
  if (!addRuleName (nested, &name))
    return 0;
  return 1;
}


static int
getNumber (widechar * source, widechar * dest)
{
/*Convert a string of wide character digits to an integer*/
  int k = 0;
  *dest = 0;
  while (source[k] >= '0' && source[k] <= '9')
    *dest = 10 * *dest + (source[k++] - '0');
  return k;
}

/* Start of multipass compiler*/
static CharsString passRuleChars;
static CharsString passRuleDots;
static CharsString passHoldString;
static CharsString passLine;
static int passLinepos;
static int passPrevLinepos;
static widechar passHoldNumber;
static widechar passEmphasis;
static TranslationTableCharacterAttributes passAttributes;
static FileInfo *passNested;
static TranslationTableOpcode passOpcode;
static widechar *passInstructions;
static int passIC;

static int
passGetAttributes ()
{
  int more = 1;
  passAttributes = 0;
  while (more)
    {
      switch (passLine.chars[passLinepos])
        {
        case pass_any:
          passAttributes = 0xffffffff;
          break;
        case pass_digit:
          passAttributes |= CTC_Digit;
          break;
        case pass_litDigit:
          passAttributes |= CTC_LitDigit;
          break;
        case pass_letter:
          passAttributes |= CTC_Letter;
          break;
        case pass_math:
          passAttributes |= CTC_Math;
          break;
        case pass_punctuation:
          passAttributes |= CTC_Punctuation;
          break;
        case pass_sign:
          passAttributes |= CTC_Sign;
          break;
        case pass_space:
          passAttributes |= CTC_Space;
          break;
        case pass_uppercase:
          passAttributes |= CTC_UpperCase;
          break;
        case pass_lowercase:
          passAttributes |= CTC_LowerCase;
          break;
        case pass_class1:
          passAttributes |= CTC_Class1;
          break;
        case pass_class2:
          passAttributes |= CTC_Class2;
          break;
        case pass_class3:
          passAttributes |= CTC_Class3;
          break;
        case pass_class4:
          passAttributes |= CTC_Class4;
          break;
        default:
          more = 0;
          break;
        }
      if (more)
        passLinepos++;
    }
  if (!passAttributes)
    {
      compileError (passNested, "Missing attribute");
      passLinepos--;
      return 0;
    }
  return 1;
}

static int
passGetEmphasis ()
{
  int more = 1;
  passLinepos++;
  passEmphasis = 0;
  while (more)
    {
      switch (passLine.chars[passLinepos])
        {
        case 'i':
          passEmphasis |= italic;
          break;
        case 'b':
          passEmphasis |= bold;
          break;
        case 'u':
          passEmphasis |= underline;
          break;
        case 'c':
          passEmphasis |= computer_braille;
          break;
        default:
          more = 0;
          break;
        }
      if (more)
        passLinepos++;
    }
  if (!passEmphasis)
    {
      compileError (passNested, "emphasis indicators expected");
      passLinepos--;
      return 0;
    }
  return 1;
}

static int
passGetDots ()
{
  CharsString collectDots;
  collectDots.length = 0;
  while (passLinepos < passLine.length && (passLine.chars[passLinepos]
                                           == '-'
                                           || (passLine.chars[passLinepos] >=
                                               '0'
                                               && passLine.
                                               chars[passLinepos] <= '9')
                                           ||
                                           ((passLine.
                                             chars[passLinepos] | 32) >= 'a'
                                            && (passLine.
                                                chars[passLinepos] | 32) <=
                                            'f')))
    collectDots.chars[collectDots.length++] = passLine.chars[passLinepos++];
  if (!parseDots (passNested, &passHoldString, &collectDots))
    return 0;
  return 1;
}

static int
passGetString ()
{
  passHoldString.length = 0;
  while (1)
    {
      if (!passLine.chars[passLinepos])
        {
          compileError (passNested, "unterminated string");
          return 0;
        }
      if (passLine.chars[passLinepos] == 34)
        break;
      if (passLine.chars[passLinepos] == QUOTESUB)
        passHoldString.chars[passHoldString.length++] = 34;
      else
        passHoldString.chars[passHoldString.length++] =
          passLine.chars[passLinepos];
      passLinepos++;
    }
  passHoldString.chars[passHoldString.length] = 0;
  passLinepos++;
  return 1;
}

static int
passGetNumber ()
{
  /*Convert a string of wide character digits to an integer */
  passHoldNumber = 0;
  while (passLine.chars[passLinepos] >= '0'
         && passLine.chars[passLinepos] <= '9')
    passHoldNumber =
      10 * passHoldNumber + (passLine.chars[passLinepos++] - '0');
  return 1;
}

static int
passGetName ()
{
  TranslationTableCharacterAttributes attr;
  passHoldString.length = 0;
  do
    {
      attr = definedCharOrDots (passNested, passLine.chars[passLinepos],
                                0)->attributes;
      if (passHoldString.length == 0)
        {
          if (!(attr & CTC_Letter))
            {
              passLinepos++;
              continue;
            }
        }
      if (!(attr & CTC_Letter))
        break;
      passHoldString.chars[passHoldString.length++] =
        passLine.chars[passLinepos];
      passLinepos++;
    }
  while (passLinepos < passLine.length);
  return 1;
}

static int
passIsKeyword (const char *token)
{
  int k;
  int length = strlen (token);
  int ch = passLine.chars[passLinepos + length + 1];
  if (((ch | 32) >= 'a' && (ch | 32) <= 'z') || (ch >= '0' && ch <= '9'))
    return 0;
  for (k = 0; k < length && passLine.chars[passLinepos + k + 1]
       == (widechar) token[k]; k++);
  if (k == length)
    {
      passLinepos += length + 1;
      return 1;
    }
  return 0;
}

struct PassName
{
  struct PassName *next;
  int varnum;
  widechar length;
  widechar name[1];
};
static struct PassName *passNames = NULL;

static int
passFindName (const CharsString * name)
{
  const struct PassName *curname = passNames;
  CharsString augmentedName;
  for (augmentedName.length = 0; augmentedName.length < name->length;
       augmentedName.length++)
    augmentedName.chars[augmentedName.length] =
      name->chars[augmentedName.length];
  augmentedName.chars[augmentedName.length++] = passOpcode;
  while (curname)
    {
      if ((augmentedName.length == curname->length) &&
          (memcmp
           (&augmentedName.chars[0], curname->name,
            CHARSIZE * name->length) == 0))
        return curname->varnum;
      curname = curname->next;
    }
  compileError (passNested, "name not found");
  return 0;
}

static int
passAddName (CharsString * name, int var)
{
  int k;
  struct PassName *curname;
  CharsString augmentedName;
  for (augmentedName.length = 0;
       augmentedName.length < name->length; augmentedName.length++)
    augmentedName.
      chars[augmentedName.length] = name->chars[augmentedName.length];
  augmentedName.chars[augmentedName.length++] = passOpcode;
  if (!
      (curname =
       malloc (sizeof (*curname) + CHARSIZE * (augmentedName.length - 1))))
    {
      compileError (passNested, "not enough memory");
      return 0;
    }
  memset (curname, 0, sizeof (*curname));
  for (k = 0; k < augmentedName.length; k++)
    {
      curname->name[k] = augmentedName.chars[k];
    }
  curname->length = augmentedName.length;
  curname->varnum = var;
  curname->next = passNames;
  passNames = curname;
  return 1;
}

static pass_Codes
passGetScriptToken ()
{
  while (passLinepos < passLine.length)
    {
      passPrevLinepos = passLinepos;
      switch (passLine.chars[passLinepos])
        {
        case '\"':
          passLinepos++;
          if (passGetString ())
            return pass_string;
          return pass_invalidToken;
        case '@':
          passLinepos++;
          if (passGetDots ())
            return pass_dots;
          return pass_invalidToken;
        case '#':               /*comment */
          passLinepos = passLine.length + 1;
          return pass_noMoreTokens;
        case '!':
          if (passLine.chars[passLinepos + 1] == '=')
            {
              passLinepos += 2;
              return pass_noteq;
            }
          passLinepos++;
          return pass_not;
        case '-':
          passLinepos++;
          return pass_hyphen;
        case '=':
          passLinepos++;
          return pass_eq;
        case '<':
          passLinepos++;
          if (passLine.chars[passLinepos] == '=')
            {
              passLinepos++;
              return pass_lteq;
            }
          return pass_lt;
        case '>':
          passLinepos++;
          if (passLine.chars[passLinepos] == '=')
            {
              passLinepos++;
              return pass_gteq;
            }
          return pass_gt;
        case '+':
          passLinepos++;
          return pass_plus;
        case '(':
          passLinepos++;
          return pass_leftParen;
        case ')':
          passLinepos++;
          return pass_rightParen;
        case ',':
          passLinepos++;
          return pass_comma;
        case '&':
          if (passLine.chars[passLinepos = 1] == '&')
            {
              passLinepos += 2;
              return pass_and;
            }
          return pass_invalidToken;
        case '|':
          if (passLine.chars[passLinepos + 1] == '|')
            {
              passLinepos += 2;
              return pass_or;
            }
          return pass_invalidToken;
        case 'a':
          if (passIsKeyword ("ttr"))
            return pass_attributes;
          passGetName ();
          return pass_nameFound;
        case 'b':
          if (passIsKeyword ("ack"))
            return pass_lookback;
          if (passIsKeyword ("ool"))
            return pass_boolean;
          passGetName ();
          return pass_nameFound;
        case 'c':
          if (passIsKeyword ("lass"))
            return pass_class;
          passGetName ();
          return pass_nameFound;
        case 'd':
          if (passIsKeyword ("ef"))
            return pass_define;
          passGetName ();
          return pass_nameFound;
        case 'e':
          if (passIsKeyword ("mph"))
            return pass_emphasis;
          passGetName ();
          return pass_nameFound;
        case 'f':
          if (passIsKeyword ("ind"))
            return pass_search;
          if (passIsKeyword ("irst"))
            return pass_first;
          passGetName ();
          return pass_nameFound;
        case 'g':
          if (passIsKeyword ("roup"))
            return pass_group;
          passGetName ();
          return pass_nameFound;
        case 'i':
          if (passIsKeyword ("f"))
            return pass_if;
          passGetName ();
          return pass_nameFound;
        case 'l':
          if (passIsKeyword ("ast"))
            return pass_last;
          passGetName ();
          return pass_nameFound;
        case 'm':
          if (passIsKeyword ("ark"))
            return pass_mark;
          passGetName ();
          return pass_nameFound;
        case 'r':
          if (passIsKeyword ("epgroup"))
            return pass_repGroup;
          if (passIsKeyword ("epcopy"))
            return pass_copy;
          if (passIsKeyword ("epomit"))
            return pass_omit;
          if (passIsKeyword ("ep"))
            return pass_replace;
          passGetName ();
          return pass_nameFound;
        case 's':
          if (passIsKeyword ("cript"))
            return pass_script;
          if (passIsKeyword ("wap"))
            return pass_swap;
          passGetName ();
          return pass_nameFound;
        case 't':
          if (passIsKeyword ("hen"))
            return pass_then;
          passGetName ();
          return pass_nameFound;
        default:
          if (passLine.chars[passLinepos] <= 32)
            {
              passLinepos++;
              break;
            }
          if (passLine.chars[passLinepos] >= '0'
              && passLine.chars[passLinepos] <= '9')
            {
              passGetNumber ();
              return pass_numberFound;
            }
          else
            {
              if (!passGetName ())
                return pass_invalidToken;
              else
                return pass_nameFound;
            }
        }
    }
  return pass_noMoreTokens;
}

static int
passIsLeftParen ()
{
  pass_Codes passCode = passGetScriptToken ();
  if (passCode != pass_leftParen)
    {
      compileError (passNested, "'(' expected");
      return 0;
    }
  return 1;
}

static int
passIsName ()
{
  pass_Codes passCode = passGetScriptToken ();
  if (passCode != pass_nameFound)
    {
      compileError (passNested, "a name expected");
      return 0;
    }
  return 1;
}

static int
passIsComma ()
{
  pass_Codes passCode = passGetScriptToken ();
  if (passCode != pass_comma)
    {
      compileError (passNested, "',' expected");
      return 0;
    }
  return 1;
}

static int
passIsNumber ()
{
  pass_Codes passCode = passGetScriptToken ();
  if (passCode != pass_numberFound)
    {
      compileError (passNested, "a number expected");
      return 0;
    }
  return 1;
}

static int
passIsRightParen ()
{
  pass_Codes passCode = passGetScriptToken ();
  if (passCode != pass_rightParen)
    {
      compileError (passNested, "')' expected");
      return 0;
    }
  return 1;
}

static int
passGetRange ()
{
  pass_Codes passCode = passGetScriptToken ();
  if (!(passCode == pass_comma || passCode == pass_rightParen))
    {
      compileError (passNested, "invalid range");
      return 0;
    }
  if (passCode == pass_rightParen)
    {
      passInstructions[passIC++] = 1;
      passInstructions[passIC++] = 1;
      return 1;
    }
  if (!passIsNumber ())
    return 0;
  passInstructions[passIC++] = passHoldNumber;
  passCode = passGetScriptToken ();
  if (!(passCode == pass_comma || passCode == pass_rightParen))
    {
      compileError (passNested, "invalid range");
      return 0;
    }
  if (passCode == pass_rightParen)
    {
      passInstructions[passIC++] = passHoldNumber;
      return 1;
    }
  if (!passIsNumber ())
    return 0;
  passInstructions[passIC++] = passHoldNumber;
  if (!passIsRightParen ())
    return 0;
  return 1;
}

static int
passInsertAttributes ()
{
  passInstructions[passIC++] = pass_attributes;
  passInstructions[passIC++] = passAttributes >> 16;
  passInstructions[passIC++] = passAttributes & 0xffff;
  if (!passGetRange ())
    return 0;
  return 1;
}

static int
compilePassOpcode (FileInfo * nested, TranslationTableOpcode opcode)
{
/*Compile the operands of a pass opcode */
  TranslationTableCharacterAttributes after = 0;
  TranslationTableCharacterAttributes before = 0;
  widechar passSubOp;
  const struct CharacterClass *class;
  TranslationTableOffset ruleOffset = 0;
  TranslationTableRule *rule = NULL;
  int k;
  int kk = 0;
  pass_Codes passCode;
  int endTest = 0;
  int isScript = 1;
  passInstructions = passRuleDots.chars;
  passIC = 0;                   /*Instruction counter */
  passRuleChars.length = 0;
  passNested = nested;
  passOpcode = opcode;
/* passHoldString and passLine are static variables declared 
 * previously.*/
  passLinepos = 0;
  passHoldString.length = 0;
  for (k = nested->linepos; k < nested->linelen; k++)
    passHoldString.chars[passHoldString.length++] = nested->line[k];
  if (!eqasc2uni ((unsigned char *) "script", passHoldString.chars, 6))
    {
      isScript = 0;
#define SEPCHAR 0x0001
      for (k = 0; k < passHoldString.length && passHoldString.chars[k] > 32;
           k++);
      if (k < passHoldString.length)
        passHoldString.chars[k] = SEPCHAR;
      else
        {
          compileError (passNested, "Invalid multipass operands");
          return 0;
        }
    }
  parseChars (passNested, &passLine, &passHoldString);
  if (isScript)
    {
      int more = 1;
      passCode = passGetScriptToken ();
      if (passCode != pass_script)
        {
          compileError (passNested, "Invalid multipass statement");
          return 0;
        }
      /* Declaratives */
      while (more)
        {
          passCode = passGetScriptToken ();
          switch (passCode)
            {
            case pass_define:
              if (!passIsLeftParen ())
                return 0;
              if (!passIsName ())
                return 0;
              if (!passIsComma ())
                return 0;
              if (!passIsNumber ())
                return 0;
              if (!passIsRightParen ())
                return 0;
              passAddName (&passHoldString, passHoldNumber);
              break;
            case pass_if:
              more = 0;
              break;
            default:
              compileError (passNested,
                            "invalid definition in declarative part");
              return 0;
            }
        }
      /* if part */
      more = 1;
      while (more)
        {
          passCode = passGetScriptToken ();
          passSubOp = passCode;
          switch (passCode)
            {
            case pass_not:
              passInstructions[passIC++] = pass_not;
              break;
            case pass_first:
              passInstructions[passIC++] = pass_first;
              break;
            case pass_last:
              passInstructions[passIC++] = pass_last;
              break;
            case pass_search:
              passInstructions[passIC++] = pass_search;
              break;
            case pass_string:
              if (opcode != CTO_Context && opcode != CTO_Correct)
                {
                  compileError (passNested,
                                "Character strings can only be used with the context and correct opcodes.");
                  return 0;
                }
              passInstructions[passIC++] = pass_string;
              goto ifDoCharsDots;
            case pass_dots:
              if (passOpcode == CTO_Correct || passOpcode == CTO_Context)
                {
                  compileError (passNested,
                                "dot patterns cannot be specified in the if part\
 of the correct or context opcodes");
                  return 0;
                }
              passInstructions[passIC++] = pass_dots;
            ifDoCharsDots:
              passInstructions[passIC++] = passHoldString.length;
              for (kk = 0; kk < passHoldString.length; kk++)
                passInstructions[passIC++] = passHoldString.chars[kk];
              break;
            case pass_attributes:
              if (!passIsLeftParen ())
                return 0;
              if (!passGetAttributes ())
                return 0;
              if (!passInsertAttributes ())
                return 0;
              break;
            case pass_emphasis:
              if (!passIsLeftParen ())
                return 0;
              if (!passGetEmphasis ())
                return 0;
              /*Right parenthis handled by subfunctiion */
              break;
            case pass_lookback:
              passInstructions[passIC++] = pass_lookback;
              passCode = passGetScriptToken ();
              if (passCode != pass_leftParen)
                {
                  passInstructions[passIC++] = 1;
                  passLinepos = passPrevLinepos;
                  break;
                }
              if (!passIsNumber ())
                return 0;
              if (!passIsRightParen ())
                return 0;
              passInstructions[passIC] = passHoldNumber;
              break;
            case pass_group:
              if (!passIsLeftParen ())
                return 0;
              break;
            case pass_mark:
              passInstructions[passIC++] = pass_startReplace;
              passInstructions[passIC++] = pass_endReplace;
              break;
            case pass_replace:
              passInstructions[passIC++] = pass_startReplace;
              if (!passIsLeftParen ())
                return 0;
              break;
            case pass_rightParen:
              passInstructions[passIC++] = pass_endReplace;
              break;
            case pass_groupstart:
            case pass_groupend:
              if (!passIsLeftParen ())
                return 0;
              if (!passGetName ())
                return 0;
              if (!passIsRightParen ())
                return 0;
              ruleOffset = findRuleName (&passHoldString);
              if (ruleOffset)
                rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
              if (rule && rule->opcode == CTO_Grouping)
                {
                  passInstructions[passIC++] = passSubOp;
                  passInstructions[passIC++] = ruleOffset >> 16;
                  passInstructions[passIC++] = ruleOffset & 0xffff;
                  break;
                }
              else
                {
                  compileError (passNested, "%s is not a grouping name",
                                showString (&passHoldString.chars[0],
                                            passHoldString.length));
                  return 0;
                }
              break;
            case pass_class:
              if (!passIsLeftParen ())
                return 0;
              if (!passGetName ())
                return 0;
              if (!passIsRightParen ())
                return 0;
              if (!(class = findCharacterClass (&passHoldString)))
                return 0;
              passAttributes = class->attribute;
              passInsertAttributes ();
              break;
            case pass_swap:
              ruleOffset = findRuleName (&passHoldString);
              if (!passIsLeftParen ())
                return 0;
              if (!passGetName ())
                return 0;
              if (!passIsRightParen ())
                return 0;
              ruleOffset = findRuleName (&passHoldString);
              if (ruleOffset)
                rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
              if (rule
                  && (rule->opcode == CTO_SwapCc || rule->opcode == CTO_SwapCd
                      || rule->opcode == CTO_SwapDd))
                {
                  passInstructions[passIC++] = pass_swap;
                  passInstructions[passIC++] = ruleOffset >> 16;
                  passInstructions[passIC++] = ruleOffset & 0xffff;
                  if (!passGetRange ())
                    return 0;
                  break;
                }
              compileError (passNested,
                            "%s is not a swap name.",
                            showString (&passHoldString.chars[0],
                                        passHoldString.length));
              return 0;
            case pass_nameFound:
              passHoldNumber = passFindName (&passHoldString);
              passCode = passGetScriptToken ();
              if (!(passCode == pass_eq || passCode == pass_lt || passCode
                    == pass_gt || passCode == pass_noteq || passCode ==
                    pass_lteq || passCode == pass_gteq))
                {
                  compileError (nested,
                                "invalid comparison operator in if part");
                  return 0;
                }
              passInstructions[passIC++] = passCode;
              passInstructions[passIC++] = passHoldNumber;
              if (!passIsNumber ())
                return 0;
              passInstructions[passIC++] = passHoldNumber;
              break;
            case pass_then:
              passInstructions[passIC++] = pass_endTest;
              more = 0;
              break;
            default:
              compileError (passNested, "invalid choice in if part");
              return 0;
            }
        }

      /* then part */
      more = 1;
      while (more)
        {
          passCode = passGetScriptToken ();
          passSubOp = passCode;
          switch (passCode)
            {
            case pass_string:
              if (opcode != CTO_Correct)
                {
                  compileError (passNested,
                                "Character strings can only be used in the then part with the correct opcode.");
                  return 0;
                }
              passInstructions[passIC++] = pass_string;
              goto thenDoCharsDots;
            case pass_dots:
              if (opcode == CTO_Correct)
                {
                  compileError (passNested,
                                "Dot patterns cannot be used with the correct opcode.");
                  return 0;
                }
              passInstructions[passIC++] = pass_dots;
            thenDoCharsDots:
              passInstructions[passIC++] = passHoldString.length;
              for (kk = 0; kk < passHoldString.length; kk++)
                passInstructions[passIC++] = passHoldString.chars[kk];
              break;
            case pass_nameFound:
              passHoldNumber = passFindName (&passHoldString);
              passCode = passGetScriptToken ();
              if (!(passCode == pass_plus || passCode == pass_hyphen
                    || passCode == pass_eq))
                {
                  compileError (nested,
                                "Invalid variable operator in then part");
                  return 0;
                }
              passInstructions[passIC++] = passCode;
              passInstructions[passIC++] = passHoldNumber;
              if (!passIsNumber ())
                return 0;
              passInstructions[passIC++] = passHoldNumber;
              break;
            case pass_copy:
              passInstructions[passIC++] = pass_copy;
              break;
            case pass_omit:
              passInstructions[passIC++] = pass_omit;
              break;
            case pass_swap:
              ruleOffset = findRuleName (&passHoldString);
              if (!passIsLeftParen ())
                return 0;
              if (!passGetName ())
                return 0;
              if (!passIsRightParen ())
                return 0;
              ruleOffset = findRuleName (&passHoldString);
              if (ruleOffset)
                rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
              if (rule
                  && (rule->opcode == CTO_SwapCc || rule->opcode == CTO_SwapCd
                      || rule->opcode == CTO_SwapDd))
                {
                  passInstructions[passIC++] = pass_swap;
                  passInstructions[passIC++] = ruleOffset >> 16;
                  passInstructions[passIC++] = ruleOffset & 0xffff;
                  if (!passGetRange ())
                    return 0;
                  break;
                }
              compileError (passNested,
                            "%s is not a swap name.",
                            showString (&passHoldString.chars[0],
                                        passHoldString.length));
              return 0;
            case pass_noMoreTokens:
              more = 0;
              break;
            default:
              compileError (passNested, "invalid action in then part");
              return 0;
            }
        }
    }
  else
    {
      /* Older machine-language-like "assembler". */

      /*Compile test part */
      for (k = 0; k < passLine.length && passLine.chars[k] != SEPCHAR; k++);
      endTest = k;
      passLine.chars[endTest] = pass_endTest;
      passLinepos = 0;
      while (passLinepos <= endTest)
        {
          switch ((passSubOp = passLine.chars[passLinepos]))
            {
            case pass_lookback:
              passInstructions[passIC++] = pass_lookback;
              passLinepos++;
              passGetNumber ();
              if (passHoldNumber == 0)
                passHoldNumber = 1;
              passInstructions[passIC++] = passHoldNumber;
              break;
            case pass_not:
              passInstructions[passIC++] = pass_not;
              passLinepos++;
              break;
            case pass_first:
              passInstructions[passIC++] = pass_first;
              passLinepos++;
              break;
            case pass_last:
              passInstructions[passIC++] = pass_last;
              passLinepos++;
              break;
            case pass_search:
              passInstructions[passIC++] = pass_search;
              passLinepos++;
              break;
            case pass_string:
              if (opcode != CTO_Context && opcode != CTO_Correct)
                {
                  compileError (passNested,
                                "Character strings can only be used with the context and correct opcodes.");
                  return 0;
                }
              passLinepos++;
              passInstructions[passIC++] = pass_string;
              passGetString ();
              goto testDoCharsDots;
            case pass_dots:
              passLinepos++;
              passInstructions[passIC++] = pass_dots;
              passGetDots ();
            testDoCharsDots:
              if (passHoldString.length == 0)
                return 0;
              passInstructions[passIC++] = passHoldString.length;
              for (kk = 0; kk < passHoldString.length; kk++)
                passInstructions[passIC++] = passHoldString.chars[kk];
              break;
            case pass_startReplace:
              passInstructions[passIC++] = pass_startReplace;
              passLinepos++;
              break;
            case pass_endReplace:
              passInstructions[passIC++] = pass_endReplace;
              passLinepos++;
              break;
            case pass_variable:
              passLinepos++;
              passGetNumber ();
              switch (passLine.chars[passLinepos])
                {
                case pass_eq:
                  passInstructions[passIC++] = pass_eq;
                  goto doComp;
                case pass_lt:
                  if (passLine.chars[passLinepos + 1] == pass_eq)
                    {
                      passLinepos++;
                      passInstructions[passIC++] = pass_lteq;
                    }
                  else
                    passInstructions[passIC++] = pass_lt;
                  goto doComp;
                case pass_gt:
                  if (passLine.chars[passLinepos + 1] == pass_eq)
                    {
                      passLinepos++;
                      passInstructions[passIC++] = pass_gteq;
                    }
                  else
                    passInstructions[passIC++] = pass_gt;
                doComp:
                  passInstructions[passIC++] = passHoldNumber;
                  passLinepos++;
                  passGetNumber ();
                  passInstructions[passIC++] = passHoldNumber;
                  break;
                default:
                  compileError (passNested, "incorrect comparison operator");
                  return 0;
                }
              break;
            case pass_attributes:
              passLinepos++;
              passGetAttributes ();
            insertAttributes:
              passInstructions[passIC++] = pass_attributes;
              passInstructions[passIC++] = passAttributes >> 16;
              passInstructions[passIC++] = passAttributes & 0xffff;
            getRange:
              if (passLine.chars[passLinepos] == pass_until)
                {
                  passLinepos++;
                  passInstructions[passIC++] = 1;
                  passInstructions[passIC++] = 0xffff;
                  break;
                }
              passGetNumber ();
              if (passHoldNumber == 0)
                {
                  passHoldNumber = passInstructions[passIC++] = 1;
                  passInstructions[passIC++] = 1;       /*This is not an error */
                  break;
                }
              passInstructions[passIC++] = passHoldNumber;
              if (passLine.chars[passLinepos] != pass_hyphen)
                {
                  passInstructions[passIC++] = passHoldNumber;
                  break;
                }
              passLinepos++;
              passGetNumber ();
              if (passHoldNumber == 0)
                {
                  compileError (passNested, "invalid range");
                  return 0;
                }
              passInstructions[passIC++] = passHoldNumber;
              break;
            case pass_groupstart:
            case pass_groupend:
              passLinepos++;
              passGetName ();
              ruleOffset = findRuleName (&passHoldString);
              if (ruleOffset)
                rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
              if (rule && rule->opcode == CTO_Grouping)
                {
                  passInstructions[passIC++] = passSubOp;
                  passInstructions[passIC++] = ruleOffset >> 16;
                  passInstructions[passIC++] = ruleOffset & 0xffff;
                  break;
                }
              else
                {
                  compileError (passNested, "%s is not a grouping name",
                                showString (&passHoldString.chars[0],
                                            passHoldString.length));
                  return 0;
                }
              break;
            case pass_swap:
              passGetName ();
              if ((class = findCharacterClass (&passHoldString)))
                {
                  passAttributes = class->attribute;
                  goto insertAttributes;
                }
              ruleOffset = findRuleName (&passHoldString);
              if (ruleOffset)
                rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
              if (rule
                  && (rule->opcode == CTO_SwapCc || rule->opcode == CTO_SwapCd
                      || rule->opcode == CTO_SwapDd))
                {
                  passInstructions[passIC++] = pass_swap;
                  passInstructions[passIC++] = ruleOffset >> 16;
                  passInstructions[passIC++] = ruleOffset & 0xffff;
                  goto getRange;
                }
              compileError (passNested,
                            "%s is neither a class name nor a swap name.",
                            showString (&passHoldString.chars[0],
                                        passHoldString.length));
              return 0;
            case pass_endTest:
              passInstructions[passIC++] = pass_endTest;
              passLinepos++;
              break;
            default:
              compileError (passNested,
                            "incorrect operator '%c ' in test part",
                            passLine.chars[passLinepos]);
              return 0;
            }

        }                       /*Compile action part */

      /* Compile action part */
      while (passLinepos < passLine.length &&
             passLine.chars[passLinepos] <= 32)
        passLinepos++;
      while (passLinepos < passLine.length &&
             passLine.chars[passLinepos] > 32)
        {
          switch ((passSubOp = passLine.chars[passLinepos]))
            {
            case pass_string:
              if (opcode != CTO_Correct)
                {
                  compileError (passNested,
                                "Character strings can only be used with the ccorrect opcode.");
                  return 0;
                }
              passLinepos++;
              passInstructions[passIC++] = pass_string;
              passGetString ();
              goto actionDoCharsDots;
            case pass_dots:
              if (opcode == CTO_Correct)
                {
                  compileError (passNested,
                                "Dot patterns cannot be used with the correct opcode.");
                  return 0;
                }
              passLinepos++;
              passGetDots ();
              passInstructions[passIC++] = pass_dots;
            actionDoCharsDots:
              if (passHoldString.length == 0)
                return 0;
              passInstructions[passIC++] = passHoldString.length;
              for (kk = 0; kk < passHoldString.length; kk++)
                passInstructions[passIC++] = passHoldString.chars[kk];
              break;
            case pass_variable:
              passLinepos++;
              passGetNumber ();
              switch (passLine.chars[passLinepos])
                {
                case pass_eq:
                  passInstructions[passIC++] = pass_eq;
                  passInstructions[passIC++] = passHoldNumber;
                  passLinepos++;
                  passGetNumber ();
                  passInstructions[passIC++] = passHoldNumber;
                  break;
                case pass_plus:
                case pass_hyphen:
                  passInstructions[passIC++] = passLine.chars[passLinepos];
                  passInstructions[passIC++] = passHoldNumber;
                  break;
                default:
                  compileError (passNested,
                                "incorrect variable operator in action part");
                  return 0;
                }
              break;
            case pass_copy:
              passInstructions[passIC++] = pass_copy;
              passLinepos++;
              break;
            case pass_omit:
              passInstructions[passIC++] = pass_omit;
              passLinepos++;
              break;
            case pass_groupreplace:
            case pass_groupstart:
            case pass_groupend:
              passLinepos++;
              passGetName ();
              ruleOffset = findRuleName (&passHoldString);
              if (ruleOffset)
                rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
              if (rule && rule->opcode == CTO_Grouping)
                {
                  passInstructions[passIC++] = passSubOp;
                  passInstructions[passIC++] = ruleOffset >> 16;
                  passInstructions[passIC++] = ruleOffset & 0xffff;
                  break;
                }
              compileError (passNested, "%s is not a grouping name",
                            showString (&passHoldString.chars[0],
                                        passHoldString.length));
              return 0;
            case pass_swap:
              passLinepos++;
              passGetName ();
              ruleOffset = findRuleName (&passHoldString);
              if (ruleOffset)
                rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
              if (rule
                  && (rule->opcode == CTO_SwapCc || rule->opcode == CTO_SwapCd
                      || rule->opcode == CTO_SwapDd))
                {
                  passInstructions[passIC++] = pass_swap;
                  passInstructions[passIC++] = ruleOffset >> 16;
                  passInstructions[passIC++] = ruleOffset & 0xffff;
                  break;
                }
              compileError (passNested, "%s is not a swap name.",
                            showString (&passHoldString.chars[0],
                                        passHoldString.length));
              return 0;
              break;
            default:
              compileError (passNested, "incorrect operator in action part");
              return 0;
            }
        }
    }

  /*Analyze and add rule */
  passRuleDots.length = passIC;
  passIC = 0;
  while (passIC < passRuleDots.length)
    {
      int start = 0;
      switch (passInstructions[passIC])
        {
        case pass_string:
        case pass_dots:
        case pass_attributes:
        case pass_swap:
          start = 1;
          break;
        case pass_groupstart:
        case pass_groupend:
          start = 1;
          break;
        case pass_eq:
        case pass_lt:
        case pass_gt:
        case pass_lteq:
        case pass_gteq:
          passIC += 3;
          break;
        case pass_lookback:
          passIC += 2;
          break;
        case pass_not:
        case pass_startReplace:
        case pass_endReplace:
        case pass_first:
          passIC++;
          break;
        default:
          compileError (passNested,
                        "Test/if part must contain characters, dots, attributes or class \
swap.");
          return 0;
        }
      if (start)
        break;
    }

  switch (passInstructions[passIC])
    {
    case pass_string:
    case pass_dots:
      for (k = 0; k < passInstructions[passIC + 1]; k++)
        passRuleChars.chars[k] = passInstructions[passIC + 2 + k];
      passRuleChars.length = k;
      after = before = 0;
      break;
    case pass_attributes:
    case pass_groupstart:
    case pass_groupend:
    case pass_swap:
      after = passRuleDots.length;
      before = 0;
      break;
    default:
      break;
    }
  if (!addRule (passNested, opcode, &passRuleChars, &passRuleDots,
                after, before))
    return 0;
  return 1;
}

/* End of multipass compiler */

static int
compileBrailleIndicator (FileInfo * nested, char *ermsg,
                         TranslationTableOpcode opcode,
                         TranslationTableOffset * rule)
{
  CharsString token;
  CharsString cells;
  if (getToken (nested, &token, ermsg))
    if (parseDots (nested, &cells, &token))
      if (!addRule (nested, opcode, NULL, &cells, 0, 0))
        return 0;
  *rule = newRuleOffset;
  return 1;
}

static int
compileNumber (FileInfo * nested)
{
  CharsString token;
  widechar dest;
  if (!getToken (nested, &token, "number"))
    return 0;
  getNumber (&token.chars[0], &dest);
  if (!(dest > 0))
    {
      compileError (nested, "a nonzero positive number is required");
      return 0;
    }
  return dest;
}

static int
compileGrouping (FileInfo * nested)
{
  int k;
  CharsString name;
  CharsString groupChars;
  CharsString groupDots;
  CharsString dotsParsed;
  TranslationTableCharacter *charsDotsPtr;
  widechar endChar;
  widechar endDots;
  if (!getToken (nested, &name, "name operand"))
    return 0;
  if (!getRuleCharsText (nested, &groupChars))
    return 0;
  if (!getToken (nested, &groupDots, "dots operand"))
    return 0;
  for (k = 0; k < groupDots.length && groupDots.chars[k] != ','; k++);
  if (k == groupDots.length)
    {
      compileError (nested,
                    "Dots operand must consist of two cells separated by a comma");
      return 0;
    }
  groupDots.chars[k] = '-';
  if (!parseDots (nested, &dotsParsed, &groupDots))
    return 0;
  if (groupChars.length != 2 || dotsParsed.length != 2)
    {
      compileError (nested,
                    "two Unicode characters and two cells separated by a comma are needed.");
      return 0;
    }
  charsDotsPtr = addCharOrDots (nested, groupChars.chars[0], 0);
  charsDotsPtr->attributes |= CTC_Math;
  charsDotsPtr->uppercase = charsDotsPtr->realchar;
  charsDotsPtr->lowercase = charsDotsPtr->realchar;
  charsDotsPtr = addCharOrDots (nested, groupChars.chars[1], 0);
  charsDotsPtr->attributes |= CTC_Math;
  charsDotsPtr->uppercase = charsDotsPtr->realchar;
  charsDotsPtr->lowercase = charsDotsPtr->realchar;
  charsDotsPtr = addCharOrDots (nested, dotsParsed.chars[0], 1);
  charsDotsPtr->attributes |= CTC_Math;
  charsDotsPtr->uppercase = charsDotsPtr->realchar;
  charsDotsPtr->lowercase = charsDotsPtr->realchar;
  charsDotsPtr = addCharOrDots (nested, dotsParsed.chars[1], 1);
  charsDotsPtr->attributes |= CTC_Math;
  charsDotsPtr->uppercase = charsDotsPtr->realchar;
  charsDotsPtr->lowercase = charsDotsPtr->realchar;
  if (!addRule (nested, CTO_Grouping, &groupChars, &dotsParsed, 0, 0))
    return 0;
  if (!addRuleName (nested, &name))
    return 0;
  putCharAndDots (nested, groupChars.chars[0], dotsParsed.chars[0]);
  putCharAndDots (nested, groupChars.chars[1], dotsParsed.chars[1]);
  endChar = groupChars.chars[1];
  endDots = dotsParsed.chars[1];
  groupChars.length = dotsParsed.length = 1;
  if (!addRule (nested, CTO_Math, &groupChars, &dotsParsed, 0, 0))
    return 0;
  groupChars.chars[0] = endChar;
  dotsParsed.chars[0] = endDots;
  if (!addRule (nested, CTO_Math, &groupChars, &dotsParsed, 0, 0))
    return 0;
  return 1;
}

static int
compileUplow (FileInfo * nested)
{
  int k;
  TranslationTableCharacter *upperChar;
  TranslationTableCharacter *lowerChar;
  TranslationTableCharacter *upperCell = NULL;
  TranslationTableCharacter *lowerCell = NULL;
  CharsString ruleChars;
  CharsString ruleDots;
  CharsString upperDots;
  CharsString lowerDots;
  int haveLowerDots = 0;
  TranslationTableCharacterAttributes attr;
  if (!getRuleCharsText (nested, &ruleChars))
    return 0;
  if (!getToken (nested, &ruleDots, "dots operand"))
    return 0;
  for (k = 0; k < ruleDots.length && ruleDots.chars[k] != ','; k++);
  if (k == ruleDots.length)
    {
      if (!parseDots (nested, &upperDots, &ruleDots))
        return 0;
      lowerDots.length = upperDots.length;
      for (k = 0; k < upperDots.length; k++)
        lowerDots.chars[k] = upperDots.chars[k];
      lowerDots.chars[k] = 0;
    }
  else
    {
      haveLowerDots = ruleDots.length;
      ruleDots.length = k;
      if (!parseDots (nested, &upperDots, &ruleDots))
        return 0;
      ruleDots.length = 0;
      k++;
      for (; k < haveLowerDots; k++)
        ruleDots.chars[ruleDots.length++] = ruleDots.chars[k];
      if (!parseDots (nested, &lowerDots, &ruleDots))
        return 0;
    }
  if (ruleChars.length != 2 || upperDots.length < 1)
    {
      compileError (nested,
                    "Exactly two Unicode characters and at least one cell are required.");
      return 0;
    }
  if (haveLowerDots && lowerDots.length < 1)
    {
      compileError (nested, "at least one cell is required after the comma.");
      return 0;
    }
  upperChar = addCharOrDots (nested, ruleChars.chars[0], 0);
  upperChar->attributes |= CTC_Letter | CTC_UpperCase;
  upperChar->uppercase = ruleChars.chars[0];
  upperChar->lowercase = ruleChars.chars[1];
  lowerChar = addCharOrDots (nested, ruleChars.chars[1], 0);
  lowerChar->attributes |= CTC_Letter | CTC_LowerCase;
  lowerChar->uppercase = ruleChars.chars[0];
  lowerChar->lowercase = ruleChars.chars[1];
  for (k = 0; k < upperDots.length; k++)
    if (!compile_findCharOrDots (upperDots.chars[k], 1))
      {
        attr = CTC_Letter | CTC_UpperCase;
        upperCell = addCharOrDots (nested, upperDots.chars[k], 1);
        if (upperDots.length != 1)
          attr = CTC_Space;
        upperCell->attributes |= attr;
        upperCell->uppercase = upperCell->realchar;
      }
  if (haveLowerDots)
    {
      for (k = 0; k < lowerDots.length; k++)
        if (!compile_findCharOrDots (lowerDots.chars[k], 1))
          {
            attr = CTC_Letter | CTC_LowerCase;
            lowerCell = addCharOrDots (nested, lowerDots.chars[k], 1);
            if (lowerDots.length != 1)
              attr = CTC_Space;
            lowerCell->attributes |= attr;
            lowerCell->lowercase = lowerCell->realchar;
          }
    }
  else if (upperCell != NULL && upperDots.length == 1)
    upperCell->attributes |= CTC_LowerCase;
  if (lowerDots.length == 1)
    putCharAndDots (nested, ruleChars.chars[1], lowerDots.chars[0]);
  if (upperCell != NULL)
    upperCell->lowercase = lowerDots.chars[0];
  if (lowerCell != NULL)
    lowerCell->uppercase = upperDots.chars[0];
  if (upperDots.length == 1)
    putCharAndDots (nested, ruleChars.chars[0], upperDots.chars[0]);
  ruleChars.length = 1;
  ruleChars.chars[2] = ruleChars.chars[0];
  ruleChars.chars[0] = ruleChars.chars[1];
  if (!addRule (nested, CTO_LowerCase, &ruleChars, &lowerDots, 0, 0))
    return 0;
  ruleChars.chars[0] = ruleChars.chars[2];
  if (!addRule (nested, CTO_UpperCase, &ruleChars, &upperDots, 0, 0))
    return 0;
  return 1;
}

/*Functions for compiling hyphenation tables*/

typedef struct                  /*hyphenation dictionary: finite state machine */
{
  int numStates;
  HyphenationState *states;
} HyphenDict;

#define DEFAULTSTATE 0xffff
#define HYPHENHASHSIZE 8191

typedef struct
{
  void *next;
  CharsString *key;
  int val;
} HyphenHashEntry;

typedef struct
{
  HyphenHashEntry *entries[HYPHENHASHSIZE];
} HyphenHashTab;

/* a hash function from ASU - adapted from Gtk+ */
static unsigned int
hyphenStringHash (const CharsString * s)
{
  int k;
  unsigned int h = 0, g;
  for (k = 0; k < s->length; k++)
    {
      h = (h << 4) + s->chars[k];
      if ((g = h & 0xf0000000))
        {
          h = h ^ (g >> 24);
          h = h ^ g;
        }
    }
  return h;
}

static HyphenHashTab *
hyphenHashNew (void)
{
  HyphenHashTab *hashTab;
  hashTab = malloc (sizeof (HyphenHashTab));
  memset (hashTab, 0, sizeof (HyphenHashTab));
  return hashTab;
}

static void
hyphenHashFree (HyphenHashTab * hashTab)
{
  int i;
  HyphenHashEntry *e, *next;
  for (i = 0; i < HYPHENHASHSIZE; i++)
    for (e = hashTab->entries[i]; e; e = next)
      {
        next = e->next;
        free (e->key);
        free (e);
      }
  free (hashTab);
}

/* assumes that key is not already present! */
static void
hyphenHashInsert (HyphenHashTab * hashTab, const CharsString * key, int val)
{
  int i, j;
  HyphenHashEntry *e;
  i = hyphenStringHash (key) % HYPHENHASHSIZE;
  e = malloc (sizeof (HyphenHashEntry));
  e->next = hashTab->entries[i];
  e->key = malloc ((key->length + 1) * CHARSIZE);
  e->key->length = key->length;
  for (j = 0; j < key->length; j++)
    e->key->chars[j] = key->chars[j];
  e->val = val;
  hashTab->entries[i] = e;
}

/* return val if found, otherwise DEFAULTSTATE */
static int
hyphenHashLookup (HyphenHashTab * hashTab, const CharsString * key)
{
  int i, j;
  HyphenHashEntry *e;
  if (key->length == 0)
    return 0;
  i = hyphenStringHash (key) % HYPHENHASHSIZE;
  for (e = hashTab->entries[i]; e; e = e->next)
    {
      if (key->length != e->key->length)
        continue;
      for (j = 0; j < key->length; j++)
        if (key->chars[j] != e->key->chars[j])
          break;
      if (j == key->length)
        return e->val;
    }
  return DEFAULTSTATE;
}

static int
hyphenGetNewState (HyphenDict * dict, HyphenHashTab * hashTab, const
                   CharsString * string)
{
  hyphenHashInsert (hashTab, string, dict->numStates);
  /* predicate is true if dict->numStates is a power of two */
  if (!(dict->numStates & (dict->numStates - 1)))
    dict->states = realloc (dict->states,
                            (dict->numStates << 1) *
                            sizeof (HyphenationState));
  dict->states[dict->numStates].hyphenPattern = 0;
  dict->states[dict->numStates].fallbackState = DEFAULTSTATE;
  dict->states[dict->numStates].numTrans = 0;
  dict->states[dict->numStates].trans.pointer = NULL;
  return dict->numStates++;
}

/* add a transition from state1 to state2 through ch - assumes that the
   transition does not already exist */
static void
hyphenAddTrans (HyphenDict * dict, int state1, int state2, widechar ch)
{
  int numTrans;
  numTrans = dict->states[state1].numTrans;
  if (numTrans == 0)
    dict->states[state1].trans.pointer = malloc (sizeof (HyphenationTrans));
  else if (!(numTrans & (numTrans - 1)))
    dict->states[state1].trans.pointer = realloc
      (dict->states[state1].trans.pointer,
       (numTrans << 1) * sizeof (HyphenationTrans));
  dict->states[state1].trans.pointer[numTrans].ch = ch;
  dict->states[state1].trans.pointer[numTrans].newState = state2;
  dict->states[state1].numTrans++;
}

static int
compileHyphenation (FileInfo * nested, CharsString * encoding)
{
  CharsString hyph;
  HyphenationTrans *holdPointer;
  HyphenHashTab *hashTab;
  CharsString word;
  char pattern[MAXSTRING];
  unsigned int stateNum = 0, lastState = 0;
  int i, j, k = encoding->length;
  widechar ch;
  int found;
  HyphenHashEntry *e;
  HyphenDict dict;
  TranslationTableOffset holdOffset;
  /*Set aside enough space for hyphenation states and transitions in 
   * translation table. Must be done before anything else*/
  reserveSpaceInTable (nested, 250000);
  hashTab = hyphenHashNew ();
  dict.numStates = 1;
  dict.states = malloc (sizeof (HyphenationState));
  dict.states[0].hyphenPattern = 0;
  dict.states[0].fallbackState = DEFAULTSTATE;
  dict.states[0].numTrans = 0;
  dict.states[0].trans.pointer = NULL;
  do
    {
      if (encoding->chars[0] == 'I')
        {
          if (!getToken (nested, &hyph, NULL))
            continue;
        }
      else
        {
          /*UTF-8 */
          if (!getToken (nested, &word, NULL))
            continue;
          parseChars (nested, &hyph, &word);
        }
      if (hyph.length == 0 || hyph.chars[0] == '#' || hyph.chars[0] ==
          '%' || hyph.chars[0] == '<')
        continue;               /*comment */
      for (i = 0; i < hyph.length; i++)
        definedCharOrDots (nested, hyph.chars[i], 0);
      j = 0;
      pattern[j] = '0';
      for (i = 0; i < hyph.length; i++)
        {
          if (hyph.chars[i] >= '0' && hyph.chars[i] <= '9')
            pattern[j] = (char) hyph.chars[i];
          else
            {
              word.chars[j] = hyph.chars[i];
              pattern[++j] = '0';
            }
        }
      word.chars[j] = 0;
      word.length = j;
      pattern[j + 1] = 0;
      for (i = 0; pattern[i] == '0'; i++);
      found = hyphenHashLookup (hashTab, &word);
      if (found != DEFAULTSTATE)
        stateNum = found;
      else
        stateNum = hyphenGetNewState (&dict, hashTab, &word);
      k = j + 2 - i;
      if (k > 0)
        {
          allocateSpaceInTable (nested,
                                &dict.states[stateNum].hyphenPattern, k);
          memcpy (&table->ruleArea[dict.states[stateNum].hyphenPattern],
                  &pattern[i], k);
        }
      /* now, put in the prefix transitions */
      while (found == DEFAULTSTATE)
        {
          lastState = stateNum;
          ch = word.chars[word.length-- - 1];
          found = hyphenHashLookup (hashTab, &word);
          if (found != DEFAULTSTATE)
            stateNum = found;
          else
            stateNum = hyphenGetNewState (&dict, hashTab, &word);
          hyphenAddTrans (&dict, stateNum, lastState, ch);
        }
    }
  while (getALine (nested));
  /* put in the fallback states */
  for (i = 0; i < HYPHENHASHSIZE; i++)
    {
      for (e = hashTab->entries[i]; e; e = e->next)
        {
          for (j = 1; j <= e->key->length; j++)
            {
              word.length = 0;
              for (k = j; k < e->key->length; k++)
                word.chars[word.length++] = e->key->chars[k];
              stateNum = hyphenHashLookup (hashTab, &word);
              if (stateNum != DEFAULTSTATE)
                break;
            }
          if (e->val)
            dict.states[e->val].fallbackState = stateNum;
        }
    }
  hyphenHashFree (hashTab);
/*Transfer hyphenation information to table*/
  for (i = 0; i < dict.numStates; i++)
    {
      if (dict.states[i].numTrans == 0)
        dict.states[i].trans.offset = 0;
      else
        {
          holdPointer = dict.states[i].trans.pointer;
          allocateSpaceInTable (nested,
                                &dict.states[i].trans.offset,
                                dict.states[i].numTrans *
                                sizeof (HyphenationTrans));
          memcpy (&table->ruleArea[dict.states[i].trans.offset],
                  holdPointer,
                  dict.states[i].numTrans * sizeof (HyphenationTrans));
          free (holdPointer);
        }
    }
  allocateSpaceInTable (nested,
                        &holdOffset, dict.numStates *
                        sizeof (HyphenationState));
  table->hyphenStatesArray = holdOffset;
  /* Prevents segmentajion fault if table is reallocated */
  memcpy (&table->ruleArea[table->hyphenStatesArray], &dict.states[0],
          dict.numStates * sizeof (HyphenationState));
  free (dict.states);
  return 1;
}

static int
compileNoBreak (FileInfo * nested)
{
  int k;
  CharsString ruleDots;
  CharsString otherDots;
  CharsString dotsBefore;
  CharsString dotsAfter;
  int haveDotsAfter = 0;
  if (!getToken (nested, &ruleDots, "dots operand"))
    return 0;
  for (k = 0; k < ruleDots.length && ruleDots.chars[k] != ','; k++);
  if (k == ruleDots.length)
    {
      if (!parseDots (nested, &dotsBefore, &ruleDots))
        return 0;
      dotsAfter.length = dotsBefore.length;
      for (k = 0; k < dotsBefore.length; k++)
        dotsAfter.chars[k] = dotsBefore.chars[k];
      dotsAfter.chars[k] = 0;
    }
  else
    {
      haveDotsAfter = ruleDots.length;
      ruleDots.length = k;
      if (!parseDots (nested, &dotsBefore, &ruleDots))
        return 0;
      otherDots.length = 0;
      k++;
      for (; k < haveDotsAfter; k++)
        otherDots.chars[otherDots.length++] = ruleDots.chars[k];
      if (!parseDots (nested, &dotsAfter, &otherDots))
        return 0;
    }
  for (k = 0; k < dotsBefore.length; k++)
    dotsBefore.chars[k] = getCharFromDots (dotsBefore.chars[k]);
  for (k = 0; k < dotsAfter.length; k++)
    dotsAfter.chars[k] = getCharFromDots (dotsAfter.chars[k]);
  if (!addRule (nested, CTO_NoBreak, &dotsBefore, &dotsAfter, 0, 0))
    return 0;
  table->noBreak = newRuleOffset;
  return 1;
}

static int
compileCharDef (FileInfo * nested,
                TranslationTableOpcode opcode,
                TranslationTableCharacterAttributes attributes)
{
  CharsString ruleChars;
  CharsString ruleDots;
  TranslationTableCharacter *character;
  TranslationTableCharacter *cell;
  TranslationTableCharacter *otherCell;
  TranslationTableCharacterAttributes attr;
  int k;
  if (!getRuleCharsText (nested, &ruleChars))
    return 0;
  if (attributes & (CTC_UpperCase | CTC_LowerCase))
    attributes |= CTC_Letter;
  if (!getRuleDotsPattern (nested, &ruleDots))
    return 0;
  if (ruleChars.length != 1 || ruleDots.length < 1)
    {
      compileError (nested,
                    "Exactly one Unicode character and at least one cell are required.");
      return 0;
    }
  character = addCharOrDots (nested, ruleChars.chars[0], 0);
  character->attributes |= attributes;
  character->uppercase = character->lowercase = character->realchar;
  cell = compile_findCharOrDots (ruleDots.chars[0], 1);
  if (ruleDots.length == 1 && cell)
    cell->attributes |= attributes;
  else
    {
      for (k = 0; k < ruleDots.length; k++)
        {
          if (!compile_findCharOrDots (ruleDots.chars[k], 1))
            {
              attr = attributes;
              otherCell = addCharOrDots (nested, ruleDots.chars[k], 1);
              if (ruleDots.length != 1)
                attr = CTC_Space;
              otherCell->attributes |= attr;
              otherCell->uppercase = otherCell->lowercase =
                otherCell->realchar;
            }
        }
    }
  if (!addRule (nested, opcode, &ruleChars, &ruleDots, 0, 0))
    return 0;
  if (ruleDots.length == 1)
    putCharAndDots (nested, ruleChars.chars[0], ruleDots.chars[0]);
  return 1;
}

static int
compileRule (FileInfo * nested)
{
  int ok = 1;
  CharsString token;
  TranslationTableOpcode opcode;
  CharsString ruleChars;
  CharsString ruleDots;
  CharsString cells;
  CharsString scratchPad;
  TranslationTableCharacterAttributes after = 0;
  TranslationTableCharacterAttributes before = 0;
  int k;

  noback = nofor = 0;
doOpcode:
  if (!getToken (nested, &token, NULL))
    return 1;                   /*blank line */
  if (token.chars[0] == '#' || token.chars[0] == '<')
    return 1;                   /*comment */
  if (nested->lineNumber == 1 && (eqasc2uni ((unsigned char *) "ISO",
                                             token.chars, 3) ||
                                  eqasc2uni ((unsigned char *) "UTF-8",
                                             token.chars, 5)))
    {
      compileHyphenation (nested, &token);
      return 1;
    }
  opcode = getOpcode (nested, &token);
  switch (opcode)
    {                           /*Carry out operations */
    case CTO_None:
      break;
    case CTO_IncludeFile:
      {
        CharsString includedFile;
        if (getToken (nested, &token, "include file name"))
          if (parseChars (nested, &includedFile, &token))
            if (!includeFile (nested, &includedFile))
              ok = 0;
        break;
      }
    case CTO_Locale:
      break;
    case CTO_Undefined:
      ok =
        compileBrailleIndicator (nested, "undefined character opcode",
                                 CTO_Undefined, &table->undefined);
      break;
    case CTO_CapitalSign:
      ok =
        compileBrailleIndicator (nested, "capital sign", CTO_CapitalRule,
                                 &table->capitalSign);
      break;
    case CTO_BeginCapitalSign:
      ok =
        compileBrailleIndicator (nested, "begin capital sign",
                                 CTO_BeginCapitalRule,
                                 &table->beginCapitalSign);
      break;
    case CTO_LenBegcaps:
      ok = table->lenBeginCaps = compileNumber (nested);
      break;
    case CTO_EndCapitalSign:
      ok =
        compileBrailleIndicator (nested, "end capitals sign",
                                 CTO_EndCapitalRule, &table->endCapitalSign);
      break;
    case CTO_FirstWordCaps:
      ok =
        compileBrailleIndicator (nested, "first word capital sign",
                                 CTO_FirstWordCapsRule,
                                 &table->firstWordCaps);
      break;
    case CTO_LastWordCapsBefore:
      ok =
        compileBrailleIndicator (nested, "capital sign before last word",
                                 CTO_LastWordCapsBeforeRule,
                                 &table->lastWordCapsBefore);
      break;
    case CTO_LastWordCapsAfter:
      ok =
        compileBrailleIndicator (nested, "capital sign after last word",
                                 CTO_LastWordCapsAfterRule,
                                 &table->lastWordCapsAfter);
      break;
    case CTO_LenCapsPhrase:
      ok = table->lenCapsPhrase = compileNumber (nested);
      break;
    case CTO_LetterSign:
      ok =
        compileBrailleIndicator (nested, "letter sign", CTO_LetterRule,
                                 &table->letterSign);
      break;
    case CTO_NoLetsignBefore:
      if (getRuleCharsText (nested, &ruleChars))
        {
          if ((table->noLetsignBeforeCount + ruleChars.length) > LETSIGNSIZE)
            {
              compileError (nested, "More than %d characters", LETSIGNSIZE);
              ok = 0;
              break;
            }
          for (k = 0; k < ruleChars.length; k++)
            table->noLetsignBefore[table->noLetsignBeforeCount++] =
              ruleChars.chars[k];
        }
      break;
    case CTO_NoLetsign:
      if (getRuleCharsText (nested, &ruleChars))
        {
          if ((table->noLetsignCount + ruleChars.length) > LETSIGNSIZE)
            {
              compileError (nested, "More than %d characters", LETSIGNSIZE);
              ok = 0;
              break;
            }
          for (k = 0; k < ruleChars.length; k++)
            table->noLetsign[table->noLetsignCount++] = ruleChars.chars[k];
        }
      break;
    case CTO_NoLetsignAfter:
      if (getRuleCharsText (nested, &ruleChars))
        {
          if ((table->noLetsignAfterCount + ruleChars.length) > LETSIGNSIZE)
            {
              compileError (nested, "More than %d characters", LETSIGNSIZE);
              ok = 0;
              break;
            }
          for (k = 0; k < ruleChars.length; k++)
            table->noLetsignAfter[table->noLetsignAfterCount++] =
              ruleChars.chars[k];
        }
      break;
    case CTO_NumberSign:
      ok =
        compileBrailleIndicator (nested, "number sign", CTO_NumberRule,
                                 &table->numberSign);
      break;
    case CTO_FirstWordItal:
      ok =
        compileBrailleIndicator (nested, "first word italic",
                                 CTO_FirstWordItalRule,
                                 &table->firstWordItal);
      break;
    case CTO_ItalSign:
    case CTO_LastWordItalBefore:
      ok =
        compileBrailleIndicator (nested, "first word italic before",
                                 CTO_LastWordItalBeforeRule,
                                 &table->lastWordItalBefore);
      break;
    case CTO_LastWordItalAfter:
      ok =
        compileBrailleIndicator (nested, "last word italic after",
                                 CTO_LastWordItalAfterRule,
                                 &table->lastWordItalAfter);
      break;
    case CTO_BegItal:
    case CTO_FirstLetterItal:
      ok =
        compileBrailleIndicator (nested, "first letter italic",
                                 CTO_FirstLetterItalRule,
                                 &table->firstLetterItal);
      break;
    case CTO_EndItal:
    case CTO_LastLetterItal:
      ok =
        compileBrailleIndicator (nested, "last letter italic",
                                 CTO_LastLetterItalRule,
                                 &table->lastLetterItal);
      break;
    case CTO_SingleLetterItal:
      ok =
        compileBrailleIndicator (nested, "single letter italic",
                                 CTO_SingleLetterItalRule,
                                 &table->singleLetterItal);
      break;
    case CTO_ItalWord:
      ok =
        compileBrailleIndicator (nested, "italic word", CTO_ItalWordRule,
                                 &table->italWord);
      break;
    case CTO_LenItalPhrase:
      ok = table->lenItalPhrase = compileNumber (nested);
      break;
    case CTO_FirstWordBold:
      ok =
        compileBrailleIndicator (nested, "first word bold",
                                 CTO_FirstWordBoldRule,
                                 &table->firstWordBold);
      break;
    case CTO_BoldSign:
    case CTO_LastWordBoldBefore:
      ok =
        compileBrailleIndicator (nested, "last word bold before",
                                 CTO_LastWordBoldBeforeRule,
                                 &table->lastWordBoldBefore);
      break;
    case CTO_LastWordBoldAfter:
      ok =
        compileBrailleIndicator (nested, "last word bold after",
                                 CTO_LastWordBoldAfterRule,
                                 &table->lastWordBoldAfter);
      break;
    case CTO_BegBold:
    case CTO_FirstLetterBold:
      ok =
        compileBrailleIndicator (nested, "first  letter bold",
                                 CTO_FirstLetterBoldRule,
                                 &table->firstLetterBold);
      break;
    case CTO_EndBold:
    case CTO_LastLetterBold:
      ok =
        compileBrailleIndicator (nested, "last letter bold",
                                 CTO_LastLetterBoldRule,
                                 &table->lastLetterBold);
      break;
    case CTO_SingleLetterBold:
      ok =
        compileBrailleIndicator (nested, "single  letter bold",
                                 CTO_SingleLetterBoldRule,
                                 &table->singleLetterBold);
      break;
    case CTO_BoldWord:
      ok =
        compileBrailleIndicator (nested, "bold word", CTO_BoldWordRule,
                                 &table->boldWord);
      break;
    case CTO_LenBoldPhrase:
      ok = table->lenBoldPhrase = compileNumber (nested);
      break;
    case CTO_FirstWordUnder:
      ok =
        compileBrailleIndicator (nested, "first word  underline",
                                 CTO_FirstWordUnderRule,
                                 &table->firstWordUnder);
      break;
    case CTO_UnderSign:
    case CTO_LastWordUnderBefore:
      ok =
        compileBrailleIndicator (nested, "last word underline before",
                                 CTO_LastWordUnderBeforeRule,
                                 &table->lastWordUnderBefore);
      break;
    case CTO_LastWordUnderAfter:
      ok =
        compileBrailleIndicator (nested, "last  word underline after",
                                 CTO_LastWordUnderAfterRule,
                                 &table->lastWordUnderAfter);
      break;
    case CTO_BegUnder:
    case CTO_FirstLetterUnder:
      ok =
        compileBrailleIndicator (nested, "first letter underline",
                                 CTO_FirstLetterUnderRule,
                                 &table->firstLetterUnder);
      break;
    case CTO_EndUnder:
    case CTO_LastLetterUnder:
      ok =
        compileBrailleIndicator (nested, "last letter underline",
                                 CTO_LastLetterUnderRule,
                                 &table->lastLetterUnder);
      break;
    case CTO_SingleLetterUnder:
      ok =
        compileBrailleIndicator (nested, "single letter underline",
                                 CTO_SingleLetterUnderRule,
                                 &table->singleLetterUnder);
      break;
    case CTO_UnderWord:
      ok =
        compileBrailleIndicator (nested, "underlined word", CTO_UnderWordRule,
                                 &table->underWord);
      break;
    case CTO_LenUnderPhrase:
      ok = table->lenUnderPhrase = compileNumber (nested);
      break;
    case CTO_BegComp:
      ok =
        compileBrailleIndicator (nested, "begin computer braille",
                                 CTO_BegCompRule, &table->begComp);
      break;
    case CTO_EndComp:
      ok =
        compileBrailleIndicator (nested, "end computer braslle",
                                 CTO_EndCompRule, &table->endComp);
      break;
    case CTO_Syllable:
      table->syllables = 1;
    case CTO_Always:
    case CTO_NoCross:
    case CTO_LargeSign:
    case CTO_WholeWord:
    case CTO_PartWord:
    case CTO_JoinNum:
    case CTO_JoinableWord:
    case CTO_LowWord:
    case CTO_SuffixableWord:
    case CTO_PrefixableWord:
    case CTO_BegWord:
    case CTO_BegMidWord:
    case CTO_MidWord:
    case CTO_MidEndWord:
    case CTO_EndWord:
    case CTO_PrePunc:
    case CTO_PostPunc:
    case CTO_BegNum:
    case CTO_MidNum:
    case CTO_EndNum:
    case CTO_Repeated:
    case CTO_RepWord:
      if (getRuleCharsText (nested, &ruleChars))
        if (getRuleDotsPattern (nested, &ruleDots))
          if (!addRule (nested, opcode, &ruleChars, &ruleDots, after, before))
            ok = 0;
      break;
    case CTO_CompDots:
    case CTO_Comp6:
      if (!getRuleCharsText (nested, &ruleChars))
        return 0;
      if (ruleChars.length != 1 || ruleChars.chars[0] > 255)
        {
          compileError (nested,
                        "first operand must be 1 character and < 256");
          return 0;
        }
      if (!getRuleDotsPattern (nested, &ruleDots))
        return 0;
      if (!addRule (nested, opcode, &ruleChars, &ruleDots, after, before))
        ok = 0;
      table->compdotsPattern[ruleChars.chars[0]] = newRuleOffset;
      break;
    case CTO_ExactDots:
      if (!getRuleCharsText (nested, &ruleChars))
        return 0;
      if (ruleChars.chars[0] != '@')
        {
          compileError (nested, "The operand must begin with an at sign (@)");
          return 0;
        }
      for (k = 1; k < ruleChars.length; k++)
        scratchPad.chars[k - 1] = ruleChars.chars[k];
      scratchPad.length = ruleChars.length - 1;
      if (!parseDots (nested, &ruleDots, &scratchPad))
        return 0;
      if (!addRule (nested, opcode, &ruleChars, &ruleDots, before, after))
        ok = 0;
      break;
    case CTO_CapsNoCont:
      ruleChars.length = 1;
      ruleChars.chars[0] = 'a';
      if (!addRule
          (nested, CTO_CapsNoContRule, &ruleChars, NULL, after, before))
        ok = 0;
      table->capsNoCont = newRuleOffset;
      break;
    case CTO_Replace:
      if (getRuleCharsText (nested, &ruleChars))
        {
          if (lastToken)
            ruleDots.length = ruleDots.chars[0] = 0;
          else
            {
              getRuleDotsText (nested, &ruleDots);
              if (ruleDots.chars[0] == '#')
                ruleDots.length = ruleDots.chars[0] = 0;
              else if (ruleDots.chars[0] == '\\' && ruleDots.chars[1] == '#')
                memcpy (&ruleDots.chars[0], &ruleDots.chars[1],
                        ruleDots.length-- * CHARSIZE);
            }
        }
      for (k = 0; k < ruleChars.length; k++)
        addCharOrDots (nested, ruleChars.chars[k], 0);
      for (k = 0; k < ruleDots.length; k++)
        addCharOrDots (nested, ruleDots.chars[k], 0);
      if (!addRule (nested, opcode, &ruleChars, &ruleDots, after, before))
        ok = 0;
      break;
    case CTO_Pass2:
      if (table->numPasses < 2)
        table->numPasses = 2;
      goto doPass;
    case CTO_Pass3:
      if (table->numPasses < 3)
        table->numPasses = 3;
      goto doPass;
    case CTO_Pass4:
      if (table->numPasses < 4)
        table->numPasses = 4;
    doPass:
    case CTO_Context:
      if (!compilePassOpcode (nested, opcode))
        ok = 0;
      break;
    case CTO_Correct:
      if (!compilePassOpcode (nested, opcode))
        ok = 0;
      table->corrections = 1;
      break;
    case CTO_Contraction:
    case CTO_NoCont:
    case CTO_CompBrl:
    case CTO_Literal:
      if (getRuleCharsText (nested, &ruleChars))
        if (!addRule (nested, opcode, &ruleChars, NULL, after, before))
          ok = 0;
      break;
    case CTO_MultInd:
      {
        int lastToken;
        ruleChars.length = 0;
        if (getToken (nested, &token, "multiple braille indicators") &&
            parseDots (nested, &cells, &token))
          {
            while ((lastToken = getToken (nested, &token, "multind opcodes")))
              {
                opcode = getOpcode (nested, &token);
                if (opcode >= CTO_CapitalSign && opcode < CTO_MultInd)
                  ruleChars.chars[ruleChars.length++] = (widechar) opcode;
                else
                  {
                    compileError (nested, "Not a braille indicator opcode.");
                    ok = 0;
                  }
                if (lastToken == 2)
                  break;
              }
          }
        else
          ok = 0;
        if (!addRule (nested, CTO_MultInd, &ruleChars, &cells, after, before))
          ok = 0;
        break;
      }

    case CTO_Class:
      {
        CharsString characters;
        const struct CharacterClass *class;
        if (!characterClasses)
          {
            if (!allocateCharacterClasses ())
              ok = 0;
          }
        if (getToken (nested, &token, "character class name"))
          {
            if ((class = findCharacterClass (&token)))
              {
                compileError (nested, "character class already defined.");
              }
            else
              if ((class =
                   addCharacterClass (nested, &token.chars[0], token.length)))
              {
                if (getCharacters (nested, &characters))
                  {
                    int index;
                    for (index = 0; index < characters.length; ++index)
                      {
                        TranslationTableRule *defRule;
                        TranslationTableCharacter *character =
                          definedCharOrDots
                          (nested, characters.chars[index], 0);
                        character->attributes |= class->attribute;
                        defRule = (TranslationTableRule *)
                          & table->ruleArea[character->definitionRule];
                        if (defRule->dotslen == 1)
                          {
                            character = definedCharOrDots
                              (nested,
                               defRule->charsdots[defRule->charslen], 1);
                            character->attributes |= class->attribute;
                          }
                      }
                  }
              }
          }
        break;
      }

      {
        TranslationTableCharacterAttributes *attributes;
        const struct CharacterClass *class;
    case CTO_After:
        attributes = &after;
        goto doClass;
    case CTO_Before:
        attributes = &before;
      doClass:

        if (!characterClasses)
          {
            if (!allocateCharacterClasses ())
              ok = 0;
          }
        if (getCharacterClass (nested, &class))
          {
            *attributes |= class->attribute;
            goto doOpcode;
          }
        break;
      }
    case CTO_NoBack:
      noback = 1;
      goto doOpcode;
    case CTO_NoFor:
      nofor = 1;
      goto doOpcode;
    case CTO_SwapCc:
    case CTO_SwapCd:
    case CTO_SwapDd:
      if (!compileSwap (nested, opcode))
        ok = 0;
      break;
    case CTO_Hyphen:
    case CTO_DecPoint:
      if (getRuleCharsText (nested, &ruleChars))
        if (getRuleDotsPattern (nested, &ruleDots))
          {
            if (ruleChars.length != 1 || ruleDots.length < 1)
              {
                compileError (nested,
                              "One Unicode character and at least one cell are required.");
                ok = 0;
              }
            if (!addRule
                (nested, opcode, &ruleChars, &ruleDots, after, before))
              ok = 0;
          }
      break;
    case CTO_Space:
      compileCharDef (nested, opcode, CTC_Space);
      break;
    case CTO_Digit:
      compileCharDef (nested, opcode, CTC_Digit);
      break;
    case CTO_LitDigit:
      compileCharDef (nested, opcode, CTC_LitDigit);
      break;
    case CTO_Punctuation:
      compileCharDef (nested, opcode, CTC_Punctuation);
      break;
    case CTO_Math:
      compileCharDef (nested, opcode, CTC_Math);
      break;
    case CTO_Sign:
      compileCharDef (nested, opcode, CTC_Sign);
      break;
    case CTO_Letter:
      compileCharDef (nested, opcode, CTC_Letter);
      break;
    case CTO_UpperCase:
      compileCharDef (nested, opcode, CTC_UpperCase);
      break;
    case CTO_LowerCase:
      compileCharDef (nested, opcode, CTC_LowerCase);
      break;
    case CTO_NoBreak:
      ok = compileNoBreak (nested);
      break;
    case CTO_Grouping:
      ok = compileGrouping (nested);
      break;
    case CTO_UpLow:
      ok = compileUplow (nested);
      break;
    case CTO_Display:
      if (getRuleCharsText (nested, &ruleChars))
        if (getRuleDotsPattern (nested, &ruleDots))
          {
            if (ruleChars.length != 1 || ruleDots.length != 1)
              {
                compileError (nested,
                              "Exactly one character and one cell are required.");
                ok = 0;
              }
            putCharAndDots (nested, ruleChars.chars[0], ruleDots.chars[0]);
          }
      break;
    default:
      compileError (nested, "unimplemented opcode.");
      break;
    }
  return ok;
}

int EXPORT_CALL
lou_readCharFromFile (const char *fileName, int *mode)
{
/*Read a character from a file, whether big-endian, little-endian or 
* ASCII8*/
  int ch;
  static FileInfo nested;
  if (fileName == NULL)
    return 0;
  if (*mode == 1)
    {
      *mode = 0;
      nested.fileName = fileName;
      nested.encoding = noEncoding;
      nested.status = 0;
      nested.lineNumber = 0;
      if (!(nested.in = fopen (nested.fileName, "r")))
        {
          lou_logPrint ("Cannot open file '%s'", nested.fileName);
          *mode = 1;
          return EOF;
        }
    }
  if (nested.in == NULL)
    {
      *mode = 1;
      return EOF;
    }
  ch = getAChar (&nested);
  if (ch == EOF)
    {
      fclose (nested.in);
      nested.in = NULL;
      *mode = 1;
    }
  return ch;
}

static int fileCount = 0;
static FILE *
findTable (const char *tableName)
{
/* Search paths for tables */
  FILE *tableFile;
  char *pathList;
  char pathEnd[2];
  char trialPath[MAXSTRING];
  if (tableName == NULL || tableName[0] == 0)
    return NULL;
  strcpy (trialPath, tablePath);
  strcat (trialPath, tableName);
  if ((tableFile = fopen (trialPath, "rb")))
    return tableFile;
  pathEnd[0] = DIR_SEP;
  pathEnd[1] = 0;
  /* See if table is on environment path LOUIS_TABLEPATH */
  pathList = getenv ("LOUIS_TABLEPATH");
  if (pathList)
    while (1)
      {
        int k;
        int listLength;
        int currentListPos = 0;
        listLength = strlen (pathList);
        for (k = 0; k < listLength; k++)
          if (pathList[k] == ',')
            break;
        if (k == listLength || k == 0)
          {                     /* Only one file */
            strcpy (trialPath, pathList);
            strcat (trialPath, pathEnd);
            strcat (trialPath, tableName);
            if ((tableFile = fopen (trialPath, "rb")))
              break;
          }
        else
          {                     /* Compile a list of files */
            strncpy (trialPath, pathList, k);
            trialPath[k] = 0;
            strcat (trialPath, pathEnd);
            strcat (trialPath, tableName);
            currentListPos = k + 1;
            if ((tableFile = fopen (trialPath, "rb")))
              break;
            while (currentListPos < listLength)
              {
                for (k = currentListPos; k < listLength; k++)
                  if (pathList[k] == ',')
                    break;
                strncpy (trialPath,
                         &pathList[currentListPos], k - currentListPos);
                trialPath[k - currentListPos] = 0;
                strcat (trialPath, pathEnd);
                strcat (trialPath, tableName);
                if ((tableFile = fopen (trialPath, "rb")))
                  currentListPos = k + 1;
                break;
              }
          }
        break;
      }
  if (tableFile)
    return tableFile;
  /* See if table in current directory or on a path in 
   * the table name*/
  if ((tableFile = fopen (tableName, "rb")))
    return tableFile;
/* See if table on dataPath. */
  pathList = lou_getDataPath ();
  if (pathList)
    {
      strcpy (trialPath, pathList);
      strcat (trialPath, pathEnd);
#ifdef _WIN32
      strcat (trialPath, "liblouis\\tables\\");
#else
      strcat (trialPath, "liblouis/tables/");
#endif
      strcat (trialPath, tableName);
      if ((tableFile = fopen (trialPath, "rb")))
        return tableFile;
    }
  /* See if table on installed or program path. */
#ifdef _WIN32
  strcpy (trialPath, lou_getProgramPath ());
  strcat (trialPath, "\\share\\liblouss\\tables\\");
#else
  strcpy (trialPath, TABLESDIR);
  strcat (trialPath, pathEnd);
#endif
  strcat (trialPath, tableName);
  if ((tableFile = fopen (trialPath, "rb")))
    return tableFile;
  return NULL;
}

static int
compileFile (const char *fileName)
{
/*Compile a table file */
  FileInfo nested;
  fileCount++;
  nested.fileName = fileName;
  nested.encoding = noEncoding;
  nested.status = 0;
  nested.lineNumber = 0;
  if ((nested.in = findTable (fileName)))
    {
      while (getALine (&nested))
        compileRule (&nested);
      fclose (nested.in);
    }
  else
    {
      if (fileCount > 1)
        lou_logPrint ("Cannot open table '%s'", nested.fileName);
      errorCount++;
      return 0;
    }
  return 1;
}

static int
compileString (const char *inString)
{
/* This function can be used to make changes to tables on the fly. */
  int k;
  FileInfo nested;
  if (inString == NULL)
    return 0;
  nested.fileName = inString;
  nested.encoding = noEncoding;
  nested.lineNumber = 1;
  nested.status = 0;
  nested.linepos = 0;
  for (k = 0; inString[k]; k++)
    nested.line[k] = inString[k];
  nested.line[k] = 0;
  return compileRule (&nested);
}

static int
makeDoubleRule (TranslationTableOpcode opcode, TranslationTableOffset
                * singleRule, TranslationTableOffset * doubleRule)
{
  CharsString dots;
  TranslationTableRule *rule;
  if (!*singleRule || *doubleRule)
    return 1;
  rule = (TranslationTableRule *) & table->ruleArea[*singleRule];
  memcpy (dots.chars, &rule->charsdots[0], rule->dotslen * CHARSIZE);
  memcpy (&dots.chars[rule->dotslen], &rule->charsdots[0],
          rule->dotslen * CHARSIZE);
  dots.length = 2 * rule->dotslen;
  if (!addRule (NULL, opcode, NULL, &dots, 0, 0))
    return 0;
  *doubleRule = newRuleOffset;
  return 1;
}

static int
setDefaults (void)
{
  if (!table->lenBeginCaps)
    table->lenBeginCaps = 2;
  makeDoubleRule (CTO_FirstWordItal, &table->lastWordItalBefore,
                  &table->firstWordItal);
  if (!table->lenItalPhrase)
    table->lenItalPhrase = 4;
  makeDoubleRule (CTO_FirstWordBold, &table->lastWordBoldBefore,
                  &table->firstWordBold);
  if (!table->lenBoldPhrase)
    table->lenBoldPhrase = 4;
  makeDoubleRule (CTO_FirstWordUnder, &table->lastWordUnderBefore,
                  &table->firstWordUnder);
  if (!table->lenUnderPhrase)
    table->lenUnderPhrase = 4;
  if (table->numPasses == 0)
    table->numPasses = 1;
  return 1;
}

static char *
doLang2table (const char *tableList)
{
  static char newList[MAXSTRING];
  int k;
  char buffer[MAXSTRING];
  FILE *l2t;
  char *langCode;
  int langCodeLen;
  if (tableList == NULL || *tableList == 0)
    return NULL;
  strcpy (newList, tableList);
  for (k = strlen (newList) - 1; k >= 0 && newList[k] != '='; k--);
  if (k < 0)
    return newList;
  fileCount = 1;
  errorCount = 1;
  newList[k] = 0;
  strcpy (buffer, newList);
  langCode = &newList[k + 1];
  langCodeLen = strlen (langCode);
  strcat (buffer, "lang2table");
  l2t = fopen (buffer, "r");
  if (l2t == NULL)
    return NULL;
  while ((fgets (buffer, sizeof (buffer) - 2, l2t)))
    {
      char *codeInFile;
      int codeInFileLen;
      char *tableInFile;
      for (k = 0; buffer[k] < 32; k++);
      if (buffer[k] == '#' || buffer[k] < 32)
        continue;
      codeInFile = &buffer[k];
      codeInFileLen = k;
      while (buffer[k] > 32)
        k++;
      codeInFileLen = k - codeInFileLen;
      codeInFile[codeInFileLen] = 0;
      if (!
          (codeInFileLen == langCodeLen
           && strcasecmp (langCode, codeInFile) == 0))
        continue;
      while (buffer[k] < 32)
        k++;
      tableInFile = &buffer[k];
      while (buffer[k] > 32)
        k++;
      buffer[k] = 0;
      strcat (newList, tableInFile);
      fclose (l2t);
      fileCount = 0;
      errorCount = 0;
      return newList;
    }
  fclose (l2t);
  return NULL;
}

static void *
compileTranslationTable (const char *tl)
{
/*compile source tables into a table in memory */
  const char *tableList;
  int k;
  char mainTable[MAXSTRING];
  char subTable[MAXSTRING];
  int listLength;
  int currentListPos = 0;
  errorCount = 0;
  warningCount = 0;
  fileCount = 0;
  table = NULL;
  characterClasses = NULL;
  ruleNames = NULL;
  tableList = doLang2table (tl);
  if (tableList == NULL)
    return NULL;
  if (!opcodeLengths[0])
    {
      TranslationTableOpcode opcode;
      for (opcode = 0; opcode < CTO_None; opcode++)
        opcodeLengths[opcode] = strlen (opcodeNames[opcode]);
    }
  allocateHeader (NULL);
  /*Compile things that are necesary for the proper operation of 
     liblouis or liblouisxml or liblouisutdml */
  compileString ("space \\s 0");
  compileString ("noback sign \\x0000 0");
  compileString ("space \\x00a0 a unbreakable space");
  compileString ("space \\x001b 1b escape");
  compileString ("space \\xffff 123456789abcdef ENDSEGMENT");
  listLength = strlen (tableList);
  for (k = currentListPos; k < listLength; k++)
    if (tableList[k] == ',')
      break;
  if (k == listLength)
    {                           /* Only one file */
      strcpy (tablePath, tableList);
      for (k = strlen (tablePath); k >= 0; k--)
        if (tablePath[k] == '\\' || tablePath[k] == '/')
          break;
      strcpy (mainTable, &tablePath[k + 1]);
      tablePath[++k] = 0;
      if (!compileFile (mainTable))
        goto cleanup;
    }
  else
    {                           /* Compile a list of files */
      currentListPos = k + 1;
      strncpy (tablePath, tableList, k);
      tablePath[k] = 0;
      for (k = strlen (tablePath); k >= 0; k--)
        if (tablePath[k] == '\\' || tablePath[k] == '/')
          break;
      strcpy (mainTable, &tablePath[k + 1]);
      tablePath[++k] = 0;
      if (!compileFile (mainTable))
        goto cleanup;
      while (currentListPos < listLength)
        {
          for (k = currentListPos; k < listLength; k++)
            if (tableList[k] == ',')
              break;
          strncpy (subTable, &tableList[currentListPos], k - currentListPos);
          subTable[k - currentListPos] = 0;
          if (!compileFile (subTable))
            goto cleanup;
          currentListPos = k + 1;
        }
    }
/*Clean up after compiling files*/
cleanup:
  if (characterClasses)
    deallocateCharacterClasses ();
  if (ruleNames)
    deallocateRuleNames ();
  if (warningCount)
    lou_logPrint ("%d warnings issued", warningCount);
  if (!errorCount)
    {
      setDefaults ();
      table->tableSize = tableSize;
      table->bytesUsed = tableUsed;
    }
  else
    {
      if (!(errorCount == 1 && fileCount == 1))
        lou_logPrint ("%d errors found.", errorCount);
      if (table)
        free (table);
      table = NULL;
    }
  return (void *) table;
}

typedef struct
{
  void *next;
  void *table;
  int tableListLength;
  char tableList[1];
} ChainEntry;
static ChainEntry *tableChain = NULL;
static ChainEntry *lastTrans = NULL;
static void *
getTable (const char *tableList)
{
/*Keep track of which tables have already been compiled */
  int tableListLen;
  ChainEntry *currentEntry = NULL;
  ChainEntry *lastEntry = NULL;
  void *newTable;
  if (tableList == NULL || *tableList == 0)
    return NULL;
  errorCount = fileCount = 0;
  tableListLen = strlen (tableList);
  /*See if this is the last table used. */
  if (lastTrans != NULL)
    if (tableListLen == lastTrans->tableListLength && (memcmp
                                                       (&lastTrans->
                                                        tableList
                                                        [0],
                                                        tableList,
                                                        tableListLen)) == 0)
      return (table = lastTrans->table);
/*See if Table has already been compiled*/
  currentEntry = tableChain;
  while (currentEntry != NULL)
    {
      if (tableListLen == currentEntry->tableListLength && (memcmp
                                                            (&currentEntry->
                                                             tableList
                                                             [0],
                                                             tableList,
                                                             tableListLen))
          == 0)
        {
          lastTrans = currentEntry;
          return (table = currentEntry->table);
        }
      lastEntry = currentEntry;
      currentEntry = currentEntry->next;
    }
  if ((newTable = compileTranslationTable (tableList)))
    {
      /*Add a new entry to the table chain. */
      int entrySize = sizeof (ChainEntry) + tableListLen;
      ChainEntry *newEntry = malloc (entrySize);
      if (tableChain == NULL)
        tableChain = newEntry;
      else
        lastEntry->next = newEntry;
      newEntry->next = NULL;
      newEntry->table = newTable;
      newEntry->tableListLength = tableListLen;
      memcpy (&newEntry->tableList[0], tableList, tableListLen);
      lastTrans = newEntry;
      return newEntry->table;
    }
  return NULL;
}

char *
getLastTableList ()
{
  if (lastTrans == NULL)
    return NULL;
  strncpy (scratchBuf, lastTrans->tableList, lastTrans->tableListLength);
  scratchBuf[lastTrans->tableListLength] = 0;
  return scratchBuf;
}

void *EXPORT_CALL
lou_getTable (const char *tableList)
{
/* Search paths for tables and keep track of compiled tables. */
  void *table = NULL;
  char *pathList;
  char pathEnd[2];
  char trialPath[MAXSTRING];
  if (tableList == NULL || tableList[0] == 0)
    return NULL;
  errorCount = fileCount = 0;
  pathEnd[0] = DIR_SEP;
  pathEnd[1] = 0;
  /* See if table is on environment path LOUIS_TABLEPATH */
  pathList = getenv ("LOUIS_TABLEPATH");
  if (pathList)
    while (1)
      {
        int k;
        int listLength;
        int currentListPos = 0;
        listLength = strlen (pathList);
        for (k = 0; k < listLength; k++)
          if (pathList[k] == ',')
            break;
        if (k == listLength || k == 0)
          {                     /* Only one file */
            strcpy (trialPath, pathList);
            strcat (trialPath, pathEnd);
            strcat (trialPath, tableList);
            table = getTable (trialPath);
            if (table)
              break;
          }
        else
          {                     /* Compile a list of files */
            strncpy (trialPath, pathList, k);
            trialPath[k] = 0;
            strcat (trialPath, pathEnd);
            strcat (trialPath, tableList);
            currentListPos = k + 1;
            table = getTable (trialPath);
            if (table)
              break;
            while (currentListPos < listLength)
              {
                for (k = currentListPos; k < listLength; k++)
                  if (pathList[k] == ',')
                    break;
                strncpy (trialPath,
                         &pathList[currentListPos], k - currentListPos);
                trialPath[k - currentListPos] = 0;
                strcat (trialPath, pathEnd);
                strcat (trialPath, tableList);
                table = getTable (trialPath);
                currentListPos = k + 1;
                if (table)
                  break;
              }
          }
        break;
      }
  if (!table)
    {
      /* See if table in current directory or on a path in 
       * the table name*/
      if (errorCount > 0 && (!(errorCount == 1 && fileCount == 1)))
        return NULL;
      table = getTable (tableList);
    }
  if (!table)
    {
/* See if table on dataPath. */
      if (errorCount > 0 && (!(errorCount == 1 && fileCount == 1)))
        return NULL;
      pathList = lou_getDataPath ();
      if (pathList)
        {
          strcpy (trialPath, pathList);
          strcat (trialPath, pathEnd);
#ifdef _WIN32
          strcat (trialPath, "liblouis\\tables\\");
#else
          strcat (trialPath, "liblouis/tables/");
#endif
          strcat (trialPath, tableList);
          table = getTable (trialPath);
        }
    }
  if (!table)
    {
      /* See if table on installed or program path. */
      if (errorCount > 0 && (!(errorCount == 1 && fileCount == 1)))
        return NULL;
#ifdef _WIN32
      strcpy (trialPath, lou_getProgramPath ());
      strcat (trialPath, "\\share\\liblouss\\tables\\");
#else
      strcpy (trialPath, TABLESDIR);
      strcat (trialPath, pathEnd);
#endif
      strcat (trialPath, tableList);
      table = getTable (trialPath);
    }
  if (!table)
    lou_logPrint ("%s could not be found", tableList);
  return table;
}

static unsigned char *destSpacing = NULL;
static int sizeDestSpacing = 0;
static unsigned short *typebuf = NULL;
static int sizeTypebuf = 0;
static widechar *passbuf1 = NULL;
static int sizePassbuf1 = 0;
static widechar *passbuf2 = NULL;
static int sizePassbuf2 = 0;
static int *srcMapping = NULL;
static int *prevSrcMapping = NULL;
static int sizeSrcMapping = 0;
static int sizePrevSrcMapping = 0;
void *
liblouis_allocMem (AllocBuf buffer, int srcmax, int destmax)
{
  if (srcmax < 1024)
    srcmax = 1024;
  if (destmax < 1024)
    destmax = 1024;
  switch (buffer)
    {
    case alloc_typebuf:
      if (destmax > sizeTypebuf)
        {
          if (typebuf != NULL)
            free (typebuf);
          typebuf = malloc ((destmax + 4) * sizeof (unsigned short));
          sizeTypebuf = destmax;
        }
      return typebuf;
    case alloc_destSpacing:
      if (destmax > sizeDestSpacing)
        {
          if (destSpacing != NULL)
            free (destSpacing);
          destSpacing = malloc (destmax + 4);
          sizeDestSpacing = destmax;
        }
      return destSpacing;
    case alloc_passbuf1:
      if (destmax > sizePassbuf1)
        {
          if (passbuf1 != NULL)
            free (passbuf1);
          passbuf1 = malloc ((destmax + 4) * CHARSIZE);
          sizePassbuf1 = destmax;
        }
      return passbuf1;
    case alloc_passbuf2:
      if (destmax > sizePassbuf2)
        {
          if (passbuf2 != NULL)
            free (passbuf2);
          passbuf2 = malloc ((destmax + 4) * CHARSIZE);
          sizePassbuf2 = destmax;
        }
      return passbuf2;
    case alloc_srcMapping:
      {
        int mapSize;
        if (srcmax >= destmax)
          mapSize = srcmax;
        else
          mapSize = destmax;
        if (mapSize > sizeSrcMapping)
          {
            if (srcMapping != NULL)
              free (srcMapping);
            srcMapping = malloc ((mapSize + 4) * sizeof (int));
            sizeSrcMapping = mapSize;
          }
      }
      return srcMapping;
    case alloc_prevSrcMapping:
      {
        int mapSize;
        if (srcmax >= destmax)
          mapSize = srcmax;
        else
          mapSize = destmax;
        if (mapSize > sizePrevSrcMapping)
          {
            if (prevSrcMapping != NULL)
              free (prevSrcMapping);
            prevSrcMapping = malloc ((mapSize + 4) * sizeof (int));
            sizePrevSrcMapping = mapSize;
          }
      }
      return prevSrcMapping;
    default:
      return NULL;
    }
}

void EXPORT_CALL
lou_free (void)
{
  ChainEntry *currentEntry;
  ChainEntry *previousEntry;
  if (logFile != NULL)
    fclose (logFile);
  if (tableChain != NULL)
    {
      currentEntry = tableChain;
      while (currentEntry)
        {
          free (currentEntry->table);
          previousEntry = currentEntry;
          currentEntry = currentEntry->next;
          free (previousEntry);
        }
      tableChain = NULL;
      lastTrans = NULL;
    }
  if (typebuf != NULL)
    free (typebuf);
  typebuf = NULL;
  sizeTypebuf = 0;
  if (destSpacing != NULL)
    free (destSpacing);
  destSpacing = NULL;
  sizeDestSpacing = 0;
  if (passbuf1 != NULL)
    free (passbuf1);
  passbuf1 = NULL;
  sizePassbuf1 = 0;
  if (passbuf2 != NULL)
    free (passbuf2);
  passbuf2 = NULL;
  sizePassbuf2 = 0;
  if (srcMapping != NULL)
    free (srcMapping);
  srcMapping = NULL;
  sizeSrcMapping = 0;
  if (prevSrcMapping != NULL)
    free (prevSrcMapping);
  prevSrcMapping = NULL;
  sizePrevSrcMapping = 0;
  opcodeLengths[0] = 0;
}

char *EXPORT_CALL
lou_version ()
{
  static char *version = PACKAGE_VERSION;
  return version;
}

int EXPORT_CALL
lou_charSize (void)
{
  return CHARSIZE;
}

int EXPORT_CALL
lou_compileString (const char *tableList, const char *inString)
{
  if (!lou_getTable (tableList))
    return 0;
  return compileString (inString);
}

/**
 * This procedure provides a target for cals that serve as breakpoints 
 * for gdb.
 */
/*
char *EXPORT_CALL
lou_getTablePaths ()
{
  static char paths[MAXSTRING];
  char *pathList;
  strcpy (paths, tablePath);
  strcat (paths, ",");
  pathList = getenv ("LOUIS_TABLEPATH");
  if (pathList)
    {
      strcat (paths, pathList);
      strcat (paths, ",");
    }
  pathList = getcwd (scratchBuf, MAXSTRING);
  if (pathList)
    {
      strcat (paths, pathList);
      strcat (paths, ",");
    }
  pathList = lou_getDataPath ();
  if (pathList)
    {
      strcat (paths, pathList);
      strcat (paths, ",");
    }
#ifdef _WIN32
  strcpy (paths, lou_getProgramPath ());
  strcat (paths, "\\share\\liblouss\\tables\\");
#else
  strcpy (paths, TABLESDIR);
#endif
  return paths;
}
*/

void debugHook ()
{
  char *hook = "debug hook";
  printf ("%s\n", hook);
}

/* [<][>][^][v][top][bottom][index][help] */