/* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "louis.h"
#include "transcommon.ci"

static int translateString (void);
static int compbrlStart = 0;
static int compbrlEnd = 0;

int EXPORT_CALL
lou_translateString (const char *tableList, const widechar
		     * inbufx,
		     int *inlen, widechar * outbuf, int *outlen, char
		     *typeform, char *spacing, int mode)
{
  return
    lou_translate (tableList, inbufx, inlen, outbuf, outlen, typeform,
		   spacing, NULL, NULL, NULL, mode);
}

int EXPORT_CALL
lou_translate (const char *tableList, const widechar
	       * inbufx,
	       int *inlen, widechar * outbuf, int *outlen,
	       char *typeform, char *spacing, int *outputPos,
	       int *inputPos, int *cursorPos, int modex)
{
  return trace_translate (tableList, inbufx, inlen, outbuf, outlen,
			  typeform, spacing, outputPos, inputPos, cursorPos,
			  NULL, NULL, modex);
}

int
trace_translate (const char *tableList, const widechar * inbufx,
		 int *inlen, widechar * outbuf, int *outlen,
		 char *typeform, char *spacing, int *outputPos,
		 int *inputPos, int *cursorPos,
		 const TranslationTableRule ** rules, int *rulesLen, int modex)
{
  int k;
  int goodTrans = 1;
  if (tableList == NULL || inbufx == NULL || inlen == NULL || outbuf ==
      NULL || outlen == NULL)
    return 0;
  if ((modex & otherTrans))
    return other_translate (tableList, inbufx,
			    inlen, outbuf, outlen,
			    typeform, spacing, outputPos, inputPos, cursorPos,
			    modex);
  table = lou_getTable (tableList);
  if (table == NULL || *inlen < 0 || *outlen < 0)
    return 0;
  currentInput = (widechar *) inbufx;
  srcmax = 0;
  while (srcmax < *inlen && currentInput[srcmax])
    srcmax++;
  destmax = *outlen;
  haveEmphasis = 0;
  if (!(typebuf = liblouis_allocMem (alloc_typebuf, srcmax, destmax)))
    return 0;
  if (typeform != NULL)
    {
      for (k = 0; k < srcmax; k++)
	if ((typebuf[k] = typeform[k] & EMPHASIS))
	  haveEmphasis = 1;
    }
  else
    memset (typebuf, 0, srcmax * sizeof (unsigned short));
  if (!(spacing == NULL || *spacing == 'X'))
    srcSpacing = (unsigned char *) spacing;
  outputPositions = outputPos;
  if (outputPos != NULL)
    for (k = 0; k < srcmax; k++)
      outputPos[k] = -1;
  inputPositions = inputPos;
  mode = modex;
  if (cursorPos != NULL && *cursorPos >= 0)
    {
      cursorStatus = 0;
      cursorPosition = *cursorPos;
      if ((mode & (compbrlAtCursor | compbrlLeftCursor)))
	{
	  compbrlStart = cursorPosition;
	  if (checkAttr (currentInput[compbrlStart], CTC_Space, 0))
	    compbrlEnd = compbrlStart + 1;
	  else
	    {
	      while (compbrlStart >= 0 && !checkAttr
		     (currentInput[compbrlStart], CTC_Space, 0))
		compbrlStart--;
	      compbrlStart++;
	      compbrlEnd = cursorPosition;
	      if (!(mode & compbrlLeftCursor))
		while (compbrlEnd < srcmax && !checkAttr
		       (currentInput[compbrlEnd], CTC_Space, 0))
		  compbrlEnd++;
	    }
	}
    }
  else
    {
      cursorPosition = -1;
      cursorStatus = 1;		/*so it won't check cursor position */
    }
  if (!(passbuf1 = liblouis_allocMem (alloc_passbuf1, srcmax, destmax)))
    return 0;
  if (!(srcMapping = liblouis_allocMem (alloc_srcMapping, srcmax, destmax)))
    return 0;
  if (!
      (prevSrcMapping =
       liblouis_allocMem (alloc_prevSrcMapping, srcmax, destmax)))
    return 0;
  for (k = 0; k <= srcmax; k++)
    srcMapping[k] = k;
  srcMapping[srcmax] = srcmax;
  if ((!(mode & pass1Only)) && (table->numPasses > 1 || table->corrections))
    {
      if (!(passbuf2 = liblouis_allocMem (alloc_passbuf2, srcmax, destmax)))
	return 0;
    }
  if (srcSpacing != NULL)
    {
      if (!(destSpacing = liblouis_allocMem (alloc_destSpacing, srcmax,
					     destmax)))
	goodTrans = 0;
      else
	memset (destSpacing, '*', destmax);
    }
  appliedRulesCount = 0;
  if (rules != NULL && rulesLen != NULL)
    {
      appliedRules = rules;
      maxAppliedRules = *rulesLen;
    }
  else
    {
      appliedRules = NULL;
      maxAppliedRules = 0;
    }
  currentPass = 0;
  if ((mode & pass1Only))
    {
      currentOutput = passbuf1;
      memcpy (prevSrcMapping, srcMapping, destmax * sizeof (int));
      goodTrans = translateString ();
      currentPass = 5;		/*Certainly > table->numPasses */
    }
  while (currentPass <= table->numPasses && goodTrans)
    {
      memcpy (prevSrcMapping, srcMapping, destmax * sizeof (int));
      switch (currentPass)
	{
	case 0:
	  if (table->corrections)
	    {
	      currentOutput = passbuf2;
	      goodTrans = makeCorrections ();
	      currentInput = passbuf2;
	      srcmax = dest;
	    }
	  break;
	case 1:
	  currentOutput = passbuf1;
	  goodTrans = translateString ();
	  break;
	case 2:
	  srcmax = dest;
	  currentInput = passbuf1;
	  currentOutput = passbuf2;
	  goodTrans = translatePass ();
	  break;
	case 3:
	  srcmax = dest;
	  currentInput = passbuf2;
	  currentOutput = passbuf1;
	  goodTrans = translatePass ();
	  break;
	case 4:
	  srcmax = dest;
	  currentInput = passbuf1;
	  currentOutput = passbuf2;
	  goodTrans = translatePass ();
	  break;
	default:
	  break;
	}
      currentPass++;
    }
  if (goodTrans)
    {
      for (k = 0; k < dest; k++)
	{
	  if (typeform != NULL)
	    {
	      if ((currentOutput[k] & (B7 | B8)))
		typeform[k] = '8';
	      else
		typeform[k] = '0';
	    }
	  if ((mode & dotsIO))
	    {
	      if ((mode & ucBrl))
		outbuf[k] = ((currentOutput[k] & 0xff) | 0x2800);
	      else
		outbuf[k] = currentOutput[k];
	    }
	  else
	    outbuf[k] = getCharFromDots (currentOutput[k]);
	}
      *inlen = realInlen;
      *outlen = dest;
      if (inputPositions != NULL)
	memcpy (inputPositions, srcMapping, destmax * sizeof (int));
      if (outputPos != NULL)
	{
	  int lastpos = 0;
	  for (k = 0; k < *inlen; k++)
	    if (outputPos[k] == -1)
	      outputPos[k] = lastpos;
	    else
	      lastpos = outputPos[k];
	}
    }
  if (destSpacing != NULL)
    {
      memcpy (srcSpacing, destSpacing, srcmax);
      srcSpacing[srcmax] = 0;
    }
  if (cursorPos != NULL)
    *cursorPos = cursorPosition;
  if (rulesLen != NULL)
    *rulesLen = appliedRulesCount;
  return goodTrans;
}

int EXPORT_CALL
lou_translatePrehyphenated (const char *tableList,
			    const widechar * inbufx, int *inlen,
			    widechar * outbuf, int *outlen,
			    char *typeform, char *spacing,
			    int *outputPos, int *inputPos, int *cursorPos,
			    char *inputHyphens, char *outputHyphens,
			    int modex)
{
  int rv = 1;
  int *alloc_inputPos = NULL;
  if (inputHyphens != NULL)
    {
      if (outputHyphens == NULL)
	return 0;
      if (inputPos == NULL)
	{
	  alloc_inputPos = malloc (*outlen * sizeof (int));
	  inputPos = alloc_inputPos;
	}
    }
  if (lou_translate (tableList, inbufx, inlen, outbuf, outlen, typeform,
		     spacing, outputPos, inputPos, cursorPos, modex))
    {
      if (inputHyphens != NULL)
	{
	  int inpos = 0;
	  int outpos;
	  for (outpos = 0; outpos < *outlen; outpos++)
	    {
	      int new_inpos = inputPos[outpos];
	      if (new_inpos < inpos)
		{
		  rv = 0;
		  break;
		}
	      if (new_inpos > inpos)
		outputHyphens[outpos] = inputHyphens[new_inpos];
	      else
		outputHyphens[outpos] = '0';
	      inpos = new_inpos;
	    }
	}
    }
  if (alloc_inputPos != NULL)
    free (alloc_inputPos);
  return rv;
}

static TranslationTableOpcode indicOpcode;
static const TranslationTableRule *indicRule;
static int dontContract = 0;

static int doCompbrl (void);

static int
hyphenate (const widechar * word, int wordSize, char *hyphens)
{
  widechar prepWord[MAXSTRING];
  int i, j, k;
  int stateNum;
  widechar ch;
  HyphenationState *statesArray = (HyphenationState *)
    & table->ruleArea[table->hyphenStatesArray];
  HyphenationState *currentState;
  HyphenationTrans *transitionsArray;
  char *hyphenPattern;
  int patternOffset;
  if (!table->hyphenStatesArray || (wordSize + 3) > MAXSTRING)
    return 0;
  j = 0;
  prepWord[j++] = '.';
  for (i = 0; i < wordSize; i++)
    prepWord[j++] = (findCharOrDots (word[i], 0))->lowercase;
  prepWord[j++] = '.';
  prepWord[j] = 0;
  for (i = 0; i < wordSize; i++)
    hyphens[i] = '0';
  /* now, run the finite state machine */
  stateNum = 0;
  for (i = 0; i < j; i++)
    {
      ch = prepWord[i];
      while (1)
	{
	  if (stateNum == 0xffff)
	    {
	      stateNum = 0;
	      goto nextLetter;
	    }
	  currentState = &statesArray[stateNum];
	  if (currentState->trans.offset)
	    {
	      transitionsArray = (HyphenationTrans *) &
		table->ruleArea[currentState->trans.offset];
	      for (k = 0; k < currentState->numTrans; k++)
		if (transitionsArray[k].ch == ch)
		  {
		    stateNum = transitionsArray[k].newState;
		    goto stateFound;
		  }
	    }
	  stateNum = currentState->fallbackState;
	}
    stateFound:
      currentState = &statesArray[stateNum];
      if (currentState->hyphenPattern)
	{
	  hyphenPattern =
	    (char *) &table->ruleArea[currentState->hyphenPattern];
	  patternOffset = i + 1 - strlen (hyphenPattern);
	  for (k = 0; hyphenPattern[k]; k++)
	    if (hyphens[patternOffset + k] < hyphenPattern[k])
	      hyphens[patternOffset + k] = hyphenPattern[k];
	}
    nextLetter:;
    }
  hyphens[wordSize] = 0;
  return 1;
}

static int destword;
static int srcword;
static int doCompTrans (int start, int end);

static int
for_updatePositions (const widechar * outChars, int inLength, int outLength)
{
  int k;
  if ((dest + outLength) > destmax || (src + inLength) > srcmax)
    return 0;
  memcpy (&currentOutput[dest], outChars, outLength * CHARSIZE);
  if (!cursorStatus)
    {
      if ((mode & (compbrlAtCursor | compbrlLeftCursor)))
	{
	  if (src >= compbrlStart)
	    {
	      cursorStatus = 2;
	      return (doCompTrans (compbrlStart, compbrlEnd));
	    }
	}
      else if (cursorPosition >= src && cursorPosition < (src + inLength))
	{
	  cursorPosition = dest;
	  cursorStatus = 1;
	}
      else if (currentInput[cursorPosition] == 0 &&
	       cursorPosition == (src + inLength))
	{
	  cursorPosition = dest + outLength / 2 + 1;
	  cursorStatus = 1;
	}
    }
  else if (cursorStatus == 2 && cursorPosition == src)
    cursorPosition = dest;
  if (inputPositions != NULL || outputPositions != NULL)
    {
      if (outLength <= inLength)
	{
	  for (k = 0; k < outLength; k++)
	    {
	      if (inputPositions != NULL)
		srcMapping[dest + k] = prevSrcMapping[src];
	      if (outputPositions != NULL)
		outputPositions[prevSrcMapping[src + k]] = dest;
	    }
	  for (k = outLength; k < inLength; k++)
	    if (outputPositions != NULL)
	      outputPositions[prevSrcMapping[src + k]] = dest;
	}
      else
	{
	  for (k = 0; k < inLength; k++)
	    {
	      if (inputPositions != NULL)
		srcMapping[dest + k] = prevSrcMapping[src];
	      if (outputPositions != NULL)
		outputPositions[prevSrcMapping[src + k]] = dest;
	    }
	  for (k = inLength; k < outLength; k++)
	    if (inputPositions != NULL)
	      srcMapping[dest + k] = prevSrcMapping[src];
	}
    }
  dest += outLength;
  return 1;
}

static int
syllableBreak (void)
{
  int wordStart;
  int wordEnd;
  int k;
  char hyphens[MAXSTRING];
  for (wordStart = src; wordStart >= 0; wordStart--)
    if (!((findCharOrDots (currentInput[wordStart], 0))->attributes &
	  CTC_Letter))
      {
	wordStart++;
	break;
      }
  if (wordStart < 0)
    wordStart = 0;
  for (wordEnd = src; wordEnd < srcmax; wordEnd++)
    if (!((findCharOrDots (currentInput[wordEnd], 0))->attributes &
	  CTC_Letter))
      {
	wordEnd--;
	break;
      }
  if (!hyphenate (&currentInput[wordStart], wordEnd - wordStart, hyphens))
    return 0;
/* If the number at the beginning of the syllable is odd or all 
* numbers are even there is no syllable break. Otherwise there is.*/
  k = src - wordStart;
  if (hyphens[k] & 1)
    return 0;
  k++;
  for (; k < (src - wordStart + transCharslen); k++)
    if (hyphens[k] & 1)
      return 1;
  return 0;
}

static TranslationTableCharacter *curCharDef;
static widechar before, after;
static TranslationTableCharacterAttributes beforeAttributes;
static TranslationTableCharacterAttributes afterAttributes;
static void
setBefore (void)
{
  if (src >= 2 && currentInput[src - 1] == ENDSEGMENT)
    before = currentInput[src - 2];
  else
    before = (src == 0) ? ' ' : currentInput[src - 1];
  beforeAttributes = (findCharOrDots (before, 0))->attributes;
}

static void
setAfter (int length)
{
  if ((src + length + 2) < srcmax && currentInput[src + 1] == ENDSEGMENT)
    after = currentInput[src + 2];
  else
    after = (src + length < srcmax) ? currentInput[src + length] : ' ';
  afterAttributes = (findCharOrDots (after, 0))->attributes;
}

static int prevTypeform = plain_text;
static int prevSrc = 0;
static TranslationTableRule pseudoRule = {
  0
};

static int
brailleIndicatorDefined (TranslationTableOffset offset)
{
  if (!offset)
    return 0;
  indicRule = (TranslationTableRule *) & table->ruleArea[offset];
  indicOpcode = indicRule->opcode;
  return 1;
}

static typeforms prevType = plain_text;
static typeforms curType = plain_text;

typedef enum
{
  firstWord,
  lastWordBefore,
  lastWordAfter,
  firstLetter,
  lastLetter,
  singleLetter,
  word,
  lenPhrase
} emphCodes;

static int wordsMarked = 0;
static int finishEmphasis = 0;
static int wordCount = 0;
static int lastWord = 0;
static int startType = -1;
static int endType = 0;

static void
markWords (const TranslationTableOffset * offset)
{
/*Mark the beginnings of words*/
  int numWords = 0;
  int k;
  wordsMarked = 1;
  numWords = offset[lenPhrase];
  if (!numWords)
    numWords = 4;
  if (wordCount < numWords)
    {
      for (k = src; k < endType; k++)
	if (!checkAttr (currentInput[k - 1], CTC_Letter | CTC_Digit, 0) &&
	    checkAttr (currentInput[k], CTC_Digit | CTC_Letter, 0))
	  typebuf[k] |= STARTWORD;
    }
  else
    {
      int firstWord = 1;
      int lastWord = src;
      for (k = src; k < endType; k++)
	{
	  if (!checkAttr (currentInput[k - 1], CTC_Letter | CTC_Digit, 0)
	      && checkAttr (currentInput[k], CTC_Digit | CTC_Letter, 0))
	    {
	      if (firstWord)
		{
		  typebuf[k] |= FIRSTWORD;
		  firstWord = 0;
		}
	      else
		lastWord = k;
	    }
	}
      typebuf[lastWord] |= STARTWORD;
    }
}

static int
insertIndicators (void)
{
/*Insert italic, bold, etc. indicators before words*/
  int typeMark;
  int ruleFound = 0;
  if (!wordsMarked || !haveEmphasis)
    return 1;
  typeMark = typebuf[src] & (STARTWORD | FIRSTWORD);
  if (!typeMark)
    return 1;
  switch (typebuf[src] & EMPHASIS)
    {
    case italic:
      if ((typeMark & FIRSTWORD))
	ruleFound = brailleIndicatorDefined (table->firstWordItal);
      else
	ruleFound = brailleIndicatorDefined (table->lastWordItalBefore);
      break;
    case bold:
      if ((typeMark & FIRSTWORD))
	ruleFound = brailleIndicatorDefined (table->firstWordBold);
      else
	ruleFound = brailleIndicatorDefined (table->lastWordBoldBefore);
      break;
    case underline:
      if ((typeMark & FIRSTWORD))
	ruleFound = brailleIndicatorDefined (table->firstWordUnder);
      else
	ruleFound = brailleIndicatorDefined (table->lastWordUnderBefore);
      break;
    default:
      ruleFound = 0;
      break;
    }
  if (ruleFound)
    {
      if (!for_updatePositions
	  (&indicRule->charsdots[0], 0, indicRule->dotslen))
	return 0;
    }
  return 1;
}

static int
validMatch ()
{
/*Analyze the typeform parameter and also check for capitalization*/
  TranslationTableCharacter *currentInputChar;
  TranslationTableCharacter *ruleChar;
  TranslationTableCharacterAttributes prevAttr = 0;
  int k;
  int kk = 0;
  unsigned short mask = 0;
  if (!transCharslen)
    return 0;
  switch (transOpcode)
    {
    case CTO_WholeWord:
    case CTO_PrefixableWord:
    case CTO_SuffixableWord:
    case CTO_JoinableWord:
    case CTO_LowWord:
      mask = EMPHASIS | capsemph;
      break;
    default:
      mask = EMPHASIS | SYLLABLEMARKS | INTERNALMARKS | capsemph;
      break;
    }
  for (k = src; k < src + transCharslen; k++)
    {
      if (currentInput[k] == ENDSEGMENT)
	{
	  if (k == src && transCharslen == 1)
	    return 1;
	  else
	    return 0;
	}
      currentInputChar = findCharOrDots (currentInput[k], 0);
      if (k == src)
	prevAttr = currentInputChar->attributes;
      ruleChar = findCharOrDots (transRule->charsdots[kk++], 0);
      if ((currentInputChar->lowercase != ruleChar->lowercase))
	return 0;
      if (typebuf != NULL && (typebuf[src] & capsemph) == 0 &&
	  (typebuf[k] & mask) != (typebuf[src] & mask))
	return 0;
      if (currentInputChar->attributes != CTC_Letter)
	{
	  if (k != (src + 1) && (prevAttr &
				 CTC_Letter)
	      && (currentInputChar->attributes & CTC_Letter)
	      &&
	      ((currentInputChar->
		attributes & (CTC_LowerCase | CTC_UpperCase |
			      CTC_Letter)) !=
	       (prevAttr & (CTC_LowerCase | CTC_UpperCase | CTC_Letter))))
	    return 0;
	}
      prevAttr = currentInputChar->attributes;
    }
  return 1;
}

static int
checkMultCaps (void)
{
  int k;
  for (k = 0; k < table->lenBeginCaps; k++)
    if (k >= srcmax - src || 
	!checkAttr (currentInput[src + k], CTC_UpperCase, 0))
      return 0;
  return 1;
}

static int prevPrevType = 0;
static int nextType = 0;
static TranslationTableCharacterAttributes prevPrevAttr = 0;

static int
beginEmphasis (const TranslationTableOffset * offset)
{
  if (src != startType)
    {
      wordCount = finishEmphasis = wordsMarked = 0;
      startType = lastWord = src;
      for (endType = src; endType < srcmax; endType++)
	{
	  if ((typebuf[endType] & EMPHASIS) != curType)
	    break;
	  if (checkAttr (currentInput[endType - 1], CTC_Space, 0)
	      && !checkAttr (currentInput[endType], CTC_Space, 0))
	    {
	      lastWord = endType;
	      wordCount++;
	    }
	}
    }
  if ((beforeAttributes & CTC_Letter) && (endType - startType) ==
      1 && brailleIndicatorDefined (offset[singleLetter]))
    return 1;
  else
    if ((beforeAttributes & CTC_Letter) && brailleIndicatorDefined
	(offset[firstLetter]))
    return 1;
  else if (brailleIndicatorDefined (offset[lastWordBefore]))
    {
      markWords (offset);
      return 0;
    }
  else
    return (brailleIndicatorDefined (offset[firstWord]));
  return 0;
}

static int
endEmphasis (const TranslationTableOffset * offset)
{
  if (wordsMarked)
    return 0;
  if (prevPrevType != prevType && nextType != prevType &&
      brailleIndicatorDefined (offset[singleLetter]))
    return 0;
  else
    if ((finishEmphasis || (src < srcmax && ((findCharOrDots
					      (currentInput[src + 1],
					       0))->attributes &
					     CTC_Letter)))
	&& brailleIndicatorDefined (offset[lastLetter]))
    return 1;
  else
    return (brailleIndicatorDefined (offset[lastWordAfter]));
  return 0;
}

static int
doCompEmph (void)
{
  int endEmph;
  for (endEmph = src; (typebuf[endEmph] & computer_braille) && endEmph
       <= srcmax; endEmph++);
  return doCompTrans (src, endEmph);
}

static int
insertBrailleIndicators (int finish)
{
/*Insert braille indicators such as italic, bold, capital, 
* letter, number, etc.*/
  typedef enum
  {
    checkNothing,
    checkBeginTypeform,
    checkEndTypeform,
    checkNumber,
    checkLetter,
    checkBeginMultCaps,
    checkEndMultCaps,
    checkSingleCap,
    checkAll
  } checkThis;
  checkThis checkWhat = checkNothing;
  int ok = 0;
  int k;
  if (finish == 2)
    {
      while (dest > 0 && (currentOutput[dest - 1] == 0 ||
			  currentOutput[dest - 1] == B16))
	dest--;
      finishEmphasis = 1;
      prevType = prevPrevType;
      curType = plain_text;
      checkWhat = checkEndTypeform;
    }
  else
    {
      if (src == prevSrc && !finish)
	return 1;
      if (src != prevSrc)
	{
	  if (haveEmphasis && src < srcmax)
	    nextType = typebuf[src + 1] & EMPHASIS;
	  else
	    nextType = plain_text;
	  if (src > 2)
	    {
	      if (haveEmphasis)
		prevPrevType = typebuf[src - 2] & EMPHASIS;
	      else
		prevPrevType = plain_text;
	      prevPrevAttr =
		(findCharOrDots (currentInput[src - 2], 0))->attributes;
	    }
	  else
	    {
	      prevPrevType = plain_text;
	      prevPrevAttr = CTC_Space;
	    }
	  if (haveEmphasis && (typebuf[src] & EMPHASIS) != prevTypeform)
	    {
	      prevType = prevTypeform & EMPHASIS;
	      curType = typebuf[src] & EMPHASIS;
	      checkWhat = checkEndTypeform;
	    }
	  else if (!finish)
	    checkWhat = checkNothing;
	  else
	    checkWhat = checkNumber;
	}
      if (finish == 1)
	checkWhat = checkNumber;
    }
  do
    {
      ok = 0;
      switch (checkWhat)
	{
	case checkNothing:
	  ok = 0;
	  break;
	case checkBeginTypeform:
	  if (haveEmphasis)
	    switch (curType)
	      {
	      case plain_text:
		ok = 0;
		break;
	      case italic:
		ok = beginEmphasis (&table->firstWordItal);
		curType = 0;
		break;
	      case bold:
		ok = beginEmphasis (&table->firstWordBold);
		curType = 0;
		break;
	      case underline:
		ok = beginEmphasis (&table->firstWordUnder);
		curType = 0;
		break;
	      case computer_braille:
		ok = 0;
		doCompEmph ();
		curType = 0;
		break;
	      case italic + underline:
		ok = beginEmphasis (&table->firstWordUnder);
		curType -= underline;
		break;
	      case italic + bold:
		ok = beginEmphasis (&table->firstWordBold);
		curType -= bold;
		break;
	      case italic + computer_braille:
		ok = 0;
		doCompEmph ();
		curType -= computer_braille;
		break;
	      case underline + bold:
		beginEmphasis (&table->firstWordBold);
		curType -= bold;
		break;
	      case underline + computer_braille:
		ok = 0;
		doCompEmph ();
		curType -= computer_braille;
		break;
	      case bold + computer_braille:
		ok = 0;
		doCompEmph ();
		curType -= computer_braille;
		break;
	      default:
		ok = 0;
		curType = 0;
		break;
	      }
	  if (!curType)
	    {
	      if (!finish)
		checkWhat = checkNothing;
	      else
		checkWhat = checkNumber;
	    }
	  break;
	case checkEndTypeform:
	  if (haveEmphasis)
	    switch (prevType)
	      {
	      case plain_text:
		ok = 0;
		break;
	      case italic:
		ok = endEmphasis (&table->firstWordItal);
		prevType = 0;
		break;
	      case bold:
		ok = endEmphasis (&table->firstWordBold);
		prevType = 0;
		break;
	      case underline:
		ok = endEmphasis (&table->firstWordUnder);
		prevType = 0;
		break;
	      case computer_braille:
		ok = 0;
		prevType = 0;
		break;
	      case italic + underline:
		ok = endEmphasis (&table->firstWordUnder);
		prevType -= underline;
		break;
	      case italic + bold:
		ok = endEmphasis (&table->firstWordBold);
		prevType -= bold;
		break;
	      case italic + computer_braille:
		ok = 1;
		prevType -= computer_braille;
		break;
	      case underline + bold:
		ok = endEmphasis (&table->firstWordBold);
		prevType -= bold;
		break;
	      case underline + computer_braille:
		ok = 0;
		prevType -= computer_braille;
		break;
	      case bold + computer_braille:
		ok = endEmphasis (&table->firstWordBold);
		prevType -= bold;
		break;
	      default:
		ok = 0;
		prevType = 0;
		break;
	      }
	  if (!prevType)
	    {
	      checkWhat = checkBeginTypeform;
	      prevTypeform = typebuf[src] & EMPHASIS;
	    }
	  break;
	case checkNumber:
	  if (brailleIndicatorDefined
	      (table->numberSign) &&
	      checkAttr (currentInput[src], CTC_Digit, 0) &&
	      (prevTransOpcode == CTO_ExactDots
	       || !(beforeAttributes & CTC_Digit))
	      && prevTransOpcode != CTO_MidNum)
	    {
	      ok = 1;
	      checkWhat = checkNothing;
	    }
	  else
	    checkWhat = checkLetter;
	  break;
	case checkLetter:
	  if (!brailleIndicatorDefined (table->letterSign))
	    {
	      ok = 0;
	      checkWhat = checkBeginMultCaps;
	      break;
	    }
	  if (transOpcode == CTO_Contraction)
	    {
	      ok = 1;
	      checkWhat = checkBeginMultCaps;
	      break;
	    }
	  if ((checkAttr (currentInput[src], CTC_Letter, 0)
	       && !(beforeAttributes & CTC_Letter))
	      && (!checkAttr (currentInput[src + 1], CTC_Letter, 0)
		  || (beforeAttributes & CTC_Digit)))
	    {
	      ok = 1;
	      if (src > 0)
		for (k = 0; k < table->noLetsignBeforeCount; k++)
		  if (currentInput[src - 1] == table->noLetsignBefore[k])
		    {
		      ok = 0;
		      break;
		    }
	      for (k = 0; k < table->noLetsignCount; k++)
		if (currentInput[src] == table->noLetsign[k])
		  {
		    ok = 0;
		    break;
		  }
	      if ((src + 1) < srcmax)
		for (k = 0; k < table->noLetsignAfterCount; k++)
		  if (currentInput[src + 1] == table->noLetsignAfter[k])
		    {
		      ok = 0;
		      break;
		    }
	    }
	  checkWhat = checkBeginMultCaps;
	  break;
	case checkBeginMultCaps:
	  if (brailleIndicatorDefined (table->beginCapitalSign) &&
	      !(beforeAttributes & CTC_UpperCase) && checkMultCaps ())
	    {
	      ok = 1;
	      if (table->capsNoCont)
		dontContract = 1;
	      checkWhat = checkNothing;
	    }
	  else
	    checkWhat = checkSingleCap;
	  break;
	case checkEndMultCaps:
	  if (brailleIndicatorDefined (table->endCapitalSign) &&
	      (prevPrevAttr & CTC_UpperCase)
	      && (beforeAttributes & CTC_UpperCase)
	      && checkAttr (currentInput[src], CTC_LowerCase, 0))
	    {
	      ok = 1;
	      if (table->capsNoCont)
		dontContract = 0;
	    }
	  checkWhat = checkNothing;
	  break;
	case checkSingleCap:
	  if (brailleIndicatorDefined (table->capitalSign) && src < srcmax
	      && checkAttr (currentInput[src], CTC_UpperCase, 0) &&
	      (!(beforeAttributes & CTC_UpperCase) ||
	       table->beginCapitalSign == 0))
	    {
	      ok = 1;
	      checkWhat = checkNothing;
	    }
	  checkWhat = checkEndMultCaps;
	  break;
	default:
	  ok = 0;
	  checkWhat = checkNothing;
	  break;
	}
      if (ok && indicRule != NULL)
	{
	  if (!for_updatePositions
	      (&indicRule->charsdots[0], 0, indicRule->dotslen))
	    return 0;
	  if (cursorStatus == 2)
	    checkWhat = checkNothing;
	}
    }
  while (checkWhat != checkNothing);
  finishEmphasis = 0;
  return 1;
}

static int
onlyLettersBehind (void)
{
  /* Actually, spaces, then letters */
  int k;
  if (!(beforeAttributes & CTC_Space))
    return 0;
  for (k = src - 2; k >= 0; k--)
    {
      TranslationTableCharacterAttributes attr = (findCharOrDots
						  (currentInput[k],
						   0))->attributes;
      if ((attr & CTC_Space))
	continue;
      if ((attr & CTC_Letter))
	return 1;
      else
	return 0;
    }
  return 1;
}

static int
onlyLettersAhead (void)
{
  /* Actullly, spaces, then letters */
  int k;
  if (!(afterAttributes & CTC_Space))
    return 0;
  for (k = src + transCharslen + 1; k < srcmax; k++)
    {
      TranslationTableCharacterAttributes attr = (findCharOrDots
						  (currentInput[k],
						   0))->attributes;
      if ((attr & CTC_Space))
	continue;
      if ((attr & (CTC_Letter | CTC_LitDigit)))
	return 1;
      else
	return 0;
    }
  return 0;
}

static int
noCompbrlAhead (void)
{
  int start = src + transCharslen;
  int end;
  int curSrc;
  if (start >= srcmax)
    return 1;
  while (start < srcmax && checkAttr (currentInput[start], CTC_Space, 0))
    start++;
  if (start == srcmax || (transOpcode == CTO_JoinableWord && (!checkAttr
							      (currentInput
							       [start],
							       CTC_Letter |
							       CTC_Digit, 0)
							      ||
							      !checkAttr
							      (currentInput
							       [start - 1],
							       CTC_Space,
							       0))))
    return 1;
  end = start;
  while (end < srcmax && !checkAttr (currentInput[end], CTC_Space, 0))
    end++;
  if ((mode & (compbrlAtCursor | compbrlLeftCursor)) && cursorPosition
      >= start && cursorPosition < end)
    return 0;
  /* Look ahead for rules with CTO_CompBrl */
  for (curSrc = start; curSrc < end; curSrc++)
    {
      int length = srcmax - curSrc;
      int tryThis;
      const TranslationTableCharacter *character1;
      const TranslationTableCharacter *character2;
      int k;
      character1 = findCharOrDots (currentInput[curSrc], 0);
      for (tryThis = 0; tryThis < 2; tryThis++)
	{
	  TranslationTableOffset ruleOffset = 0;
	  TranslationTableRule *testRule;
	  unsigned long int makeHash = 0;
	  switch (tryThis)
	    {
	    case 0:
	      if (!(length >= 2))
		break;
	      /*Hash function optimized for forward translation */
	      makeHash = (unsigned long int) character1->lowercase << 8;
	      character2 = findCharOrDots (currentInput[curSrc + 1], 0);
	      makeHash += (unsigned long int) character2->lowercase;
	      makeHash %= HASHNUM;
	      ruleOffset = table->forRules[makeHash];
	      break;
	    case 1:
	      if (!(length >= 1))
		break;
	      length = 1;
	      ruleOffset = character1->otherRules;
	      break;
	    }
	  while (ruleOffset)
	    {
	      testRule =
		(TranslationTableRule *) & table->ruleArea[ruleOffset];
	      for (k = 0; k < testRule->charslen; k++)
		{
		  character1 = findCharOrDots (testRule->charsdots[k], 0);
		  character2 = findCharOrDots (currentInput[curSrc + k], 0);
		  if (character1->lowercase != character2->lowercase)
		    break;
		}
	      if (tryThis == 1 || k == testRule->charslen)
		{
		  if (testRule->opcode == CTO_CompBrl
		      || testRule->opcode == CTO_Literal)
		    return 0;
		}
	      ruleOffset = testRule->charsnext;
	    }
	}
    }
  return 1;
}

static widechar const *repwordStart;
static int repwordLength;
static int
isRepeatedWord (void)
{
  int start;
  if (src == 0 || !checkAttr (currentInput[src - 1], CTC_Letter, 0))
    return 0;
  if ((src + transCharslen) >= srcmax || !checkAttr (currentInput[src +
								  transCharslen],
						     CTC_Letter, 0))
    return 0;
  for (start = src - 2;
       start >= 0 && checkAttr (currentInput[start], CTC_Letter, 0); start--);
  start++;
  repwordStart = &currentInput[start];
  repwordLength = src - start;
  if (compareChars (repwordStart, &currentInput[src
						+ transCharslen],
		    repwordLength, 0))
    return 1;
  return 0;
}

static void
for_selectRule (void)
{
/*check for valid Translations. Return value is in transRule. */
  int length = srcmax - src;
  int tryThis;
  const TranslationTableCharacter *character2;
  int k;
  curCharDef = findCharOrDots (currentInput[src], 0);
  for (tryThis = 0; tryThis < 3; tryThis++)
    {
      TranslationTableOffset ruleOffset = 0;
      unsigned long int makeHash = 0;
      switch (tryThis)
	{
	case 0:
	  if (!(length >= 2))
	    break;
	  /*Hash function optimized for forward translation */
	  makeHash = (unsigned long int) curCharDef->lowercase << 8;
	  character2 = findCharOrDots (currentInput[src + 1], 0);
	  makeHash += (unsigned long int) character2->lowercase;
	  makeHash %= HASHNUM;
	  ruleOffset = table->forRules[makeHash];
	  break;
	case 1:
	  if (!(length >= 1))
	    break;
	  length = 1;
	  ruleOffset = curCharDef->otherRules;
	  break;
	case 2:		/*No rule found */
	  transRule = &pseudoRule;
	  transOpcode = pseudoRule.opcode = CTO_None;
	  transCharslen = pseudoRule.charslen = 1;
	  pseudoRule.charsdots[0] = currentInput[src];
	  pseudoRule.dotslen = 0;
	  return;
	  break;
	}
      while (ruleOffset)
	{
	  transRule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
	  transOpcode = transRule->opcode;
	  transCharslen = transRule->charslen;
	  if (tryThis == 1 || ((transCharslen <= length) && validMatch ()))
	    {
	      /* check this rule */
	      setAfter (transCharslen);
	      if ((!transRule->after || (beforeAttributes
					 & transRule->after)) &&
		  (!transRule->before || (afterAttributes
					  & transRule->before)))
		switch (transOpcode)
		  {		/*check validity of this Translation */
		  case CTO_Space:
		  case CTO_Letter:
		  case CTO_UpperCase:
		  case CTO_LowerCase:
		  case CTO_Digit:
		  case CTO_LitDigit:
		  case CTO_Punctuation:
		  case CTO_Math:
		  case CTO_Sign:
		  case CTO_Hyphen:
		  case CTO_Replace:
		  case CTO_CompBrl:
		  case CTO_Literal:
		    return;
		  case CTO_Repeated:
		    if ((mode & (compbrlAtCursor | compbrlLeftCursor))
			&& src >= compbrlStart && src <= compbrlEnd)
		      break;
		    return;
		  case CTO_RepWord:
		    if (dontContract || (mode & noContractions))
		      break;
		    if (isRepeatedWord ())
		      return;
		    break;
		  case CTO_NoCont:
		    if (dontContract || (mode & noContractions))
		      break;
		    return;
		  case CTO_Syllable:
		    transOpcode = CTO_Always;
		  case CTO_Always:
		    if (dontContract || (mode & noContractions))
		      break;
		    return;
		  case CTO_ExactDots:
		    return;
		  case CTO_NoCross:
		    if (syllableBreak ())
		      break;
		    return;
		  case CTO_Context:
		    if (!srcIncremented || !passDoTest ())
		      break;
		    return;
		  case CTO_LargeSign:
		    if (dontContract || (mode & noContractions))
		      break;
		    if (!((beforeAttributes & (CTC_Space
					       | CTC_Punctuation))
			  || onlyLettersBehind ())
			|| !((afterAttributes & CTC_Space)
			     || prevTransOpcode == CTO_LargeSign)
			|| (afterAttributes & CTC_Letter)
			|| !noCompbrlAhead ())
		      transOpcode = CTO_Always;
		    return;
		  case CTO_WholeWord:
		    if (dontContract || (mode & noContractions))
		      break;
		  case CTO_Contraction:
		    if ((beforeAttributes & (CTC_Space | CTC_Punctuation))
			&& (afterAttributes & (CTC_Space | CTC_Punctuation)))
		      return;
		    break;
		  case CTO_PartWord:
		    if (dontContract || (mode & noContractions))
		      break;
		    if ((beforeAttributes & CTC_Letter)
			|| (afterAttributes & CTC_Letter))
		      return;
		    break;
		  case CTO_JoinNum:
		    if (dontContract || (mode & noContractions))
		      break;
		    if ((beforeAttributes & (CTC_Space | CTC_Punctuation))
			&&
			(afterAttributes & CTC_Space) &&
			(dest + transRule->dotslen < destmax))
		      {
			int cursrc = src + transCharslen + 1;
			while (cursrc < srcmax)
			  {
			    if (!checkAttr
				(currentInput[cursrc], CTC_Space, 0))
			      {
				if (checkAttr
				    (currentInput[cursrc], CTC_Digit, 0))
				  return;
				break;
			      }
			    cursrc++;
			  }
		      }
		    break;
		  case CTO_LowWord:
		    if (dontContract || (mode & noContractions))
		      break;
		    if ((beforeAttributes & CTC_Space)
			&& (afterAttributes & CTC_Space)
			&& (prevTransOpcode != CTO_JoinableWord))
		      return;
		    break;
		  case CTO_JoinableWord:
		    if (dontContract || (mode & noContractions))
		      break;
		    if (beforeAttributes & (CTC_Space | CTC_Punctuation)
			&& onlyLettersAhead () && noCompbrlAhead ())
		      return;
		    break;
		  case CTO_SuffixableWord:
		    if (dontContract || (mode & noContractions))
		      break;
		    if ((beforeAttributes & (CTC_Space | CTC_Punctuation))
			&& (afterAttributes &
			    (CTC_Space | CTC_Letter | CTC_Punctuation)))
		      return;
		    break;
		  case CTO_PrefixableWord:
		    if (dontContract || (mode & noContractions))
		      break;
		    if ((beforeAttributes &
			 (CTC_Space | CTC_Letter | CTC_Punctuation))
			&& (afterAttributes & (CTC_Space | CTC_Punctuation)))
		      return;
		    break;
		  case CTO_BegWord:
		    if (dontContract || (mode & noContractions))
		      break;
		    if ((beforeAttributes & (CTC_Space | CTC_Punctuation))
			&& (afterAttributes & CTC_Letter))
		      return;
		    break;
		  case CTO_BegMidWord:
		    if (dontContract || (mode & noContractions))
		      break;
		    if ((beforeAttributes &
			 (CTC_Letter | CTC_Space | CTC_Punctuation))
			&& (afterAttributes & CTC_Letter))
		      return;
		    break;
		  case CTO_MidWord:
		    if (dontContract || (mode & noContractions))
		      break;
		    if (beforeAttributes & CTC_Letter
			&& afterAttributes & CTC_Letter)
		      return;
		    break;
		  case CTO_MidEndWord:
		    if (dontContract || (mode & noContractions))
		      break;
		    if (beforeAttributes & CTC_Letter
			&& afterAttributes & (CTC_Letter | CTC_Space |
					      CTC_Punctuation))
		      return;
		    break;
		  case CTO_EndWord:
		    if (dontContract || (mode & noContractions))
		      break;
		    if (beforeAttributes & CTC_Letter
			&& afterAttributes & (CTC_Space | CTC_Punctuation))
		      return;
		    break;
		  case CTO_BegNum:
		    if (beforeAttributes & (CTC_Space | CTC_Punctuation)
			&& afterAttributes & CTC_Digit)
		      return;
		    break;
		  case CTO_MidNum:
		    if (prevTransOpcode != CTO_ExactDots
			&& beforeAttributes & CTC_Digit
			&& afterAttributes & CTC_Digit)
		      return;
		    break;
		  case CTO_EndNum:
		    if (beforeAttributes & CTC_Digit &&
			prevTransOpcode != CTO_ExactDots)
		      return;
		    break;
		  case CTO_DecPoint:
		    if (!(afterAttributes & CTC_Digit))
		      break;
		    if (beforeAttributes & CTC_Digit)
		      transOpcode = CTO_MidNum;
		    return;
		  case CTO_PrePunc:
		    if (!checkAttr (currentInput[src], CTC_Punctuation, 0)
			|| (src > 0
			    && checkAttr (currentInput[src - 1], CTC_Letter,
					  0)))
		      break;
		    for (k = src + transCharslen; k < srcmax; k++)
		      {
			if (checkAttr
			    (currentInput[k], (CTC_Letter | CTC_Digit), 0))
			  return;
			if (checkAttr (currentInput[k], CTC_Space, 0))
			  break;
		      }
		    break;
		  case CTO_PostPunc:
		    if (!checkAttr (currentInput[src], CTC_Punctuation, 0)
			|| (src < (srcmax - 1)
			    && checkAttr (currentInput[src + 1], CTC_Letter,
					  0)))
		      break;
		    for (k = src; k >= 0; k--)
		      {
			if (checkAttr
			    (currentInput[k], (CTC_Letter | CTC_Digit), 0))
			  return;
			if (checkAttr (currentInput[k], CTC_Space, 0))
			  break;
		      }
		    break;
		  default:
		    break;
		  }
	    }
/*Done with checking this rule */
	  ruleOffset = transRule->charsnext;
	}
    }
}

static int
undefinedCharacter (widechar c)
{
/*Display an undefined character in the output buffer*/
  int k;
  char *display;
  if (table->undefined)
    {
      TranslationTableRule *transRule = (TranslationTableRule *)
	& table->ruleArea[table->undefined];
      if (!for_updatePositions
	  (&transRule->charsdots[transRule->charslen],
	   transRule->charslen, transRule->dotslen))
	return 0;
      return 1;
    }
  display = showString (&c, 1);
  if ((dest + strlen (display)) > destmax)
    return 0;
  if (outputPositions != NULL)
    outputPositions[prevSrcMapping[src]] = dest;
  for (k = 0; k < strlen (display); k++)
    {
      if (inputPositions != NULL)
	srcMapping[dest] = prevSrcMapping[src];
      currentOutput[dest++] = getDotsForChar (display[k]);
    }
  return 1;
}

static int
putCharacter (widechar character)
{
/*Insert the dots equivalent of a character into the output buffer */
  TranslationTableCharacter *chardef;
  TranslationTableOffset offset;
  if (cursorStatus == 2)
    return 1;
  chardef = (findCharOrDots (character, 0));
  if ((chardef->attributes & CTC_Letter) && (chardef->attributes &
					     CTC_UpperCase))
    chardef = findCharOrDots (chardef->lowercase, 0);
  offset = chardef->definitionRule;
  if (offset)
    {
      const TranslationTableRule *rule = (TranslationTableRule *)
	& table->ruleArea[offset];
      if (rule->dotslen)
	return for_updatePositions (&rule->charsdots[1], 1, rule->dotslen);
      {
	widechar d = getDotsForChar (character);
	return for_updatePositions (&d, 1, 1);
      }
    }
  return undefinedCharacter (character);
}

static int
putCharacters (const widechar * characters, int count)
{
/*Insert the dot equivalents of a series of characters in the output 
* buffer */
  int k;
  for (k = 0; k < count; k++)
    if (!putCharacter (characters[k]))
      return 0;
  return 1;
}

static int
doCompbrl (void)
{
/*Handle strings containing substrings defined by the compbrl opcode*/
  int stringEnd;
  if (checkAttr (currentInput[src], CTC_Space, 0))
    return 1;
  if (destword)
    {
      src = srcword;
      dest = destword;
    }
  else
    {
      src = 0;
      dest = 0;
    }
  for (stringEnd = src; stringEnd < srcmax; stringEnd++)
    if (checkAttr (currentInput[stringEnd], CTC_Space, 0))
      break;
  return (doCompTrans (src, stringEnd));
}

static int
putCompChar (widechar character)
{
/*Insert the dots equivalent of a character into the output buffer */
  TranslationTableOffset offset = (findCharOrDots
				   (character, 0))->definitionRule;
  if (offset)
    {
      const TranslationTableRule *rule = (TranslationTableRule *)
	& table->ruleArea[offset];
      if (rule->dotslen)
	return for_updatePositions (&rule->charsdots[1], 1, rule->dotslen);
      {
	widechar d = getDotsForChar (character);
	return for_updatePositions (&d, 1, 1);
      }
    }
  return undefinedCharacter (character);
}

static int
doCompTrans (int start, int end)
{
  int k;
  if (cursorStatus != 2 && brailleIndicatorDefined (table->begComp))
    if (!for_updatePositions
	(&indicRule->charsdots[0], 0, indicRule->dotslen))
      return 0;
  for (k = start; k < end; k++)
    {
      TranslationTableOffset compdots = 0;
      src = k;
      if (currentInput[k] < 256)
	compdots = table->compdotsPattern[currentInput[k]];
      if (compdots != 0)
	{
	  transRule = (TranslationTableRule *) & table->ruleArea[compdots];
	  if (!for_updatePositions
	      (&transRule->charsdots[transRule->charslen],
	       transRule->charslen, transRule->dotslen))
	    return 0;
	}
      else if (!putCompChar (currentInput[k]))
	return 0;
    }
  if (cursorStatus != 2 && brailleIndicatorDefined (table->endComp))
    if (!for_updatePositions
	(&indicRule->charsdots[0], 0, indicRule->dotslen))
      return 0;
  src = end;
  return 1;
}

static int
doNocont (void)
{
/*Handle strings containing substrings defined by the nocont opcode*/
  if (checkAttr (currentInput[src], CTC_Space, 0) || dontContract
      || (mode & noContractions))
    return 1;
  if (destword)
    {
      src = srcword;
      dest = destword;
    }
  else
    {
      src = 0;
      dest = 0;
    }
  dontContract = 1;
  return 1;
}

static int
markSyllables (void)
{
  int k;
  int syllableMarker = 0;
  int currentMark = 0;
  if (typebuf == NULL || !table->syllables)
    return 1;
  src = 0;
  while (src < srcmax)
    {				/*the main multipass translation loop */
      int length = srcmax - src;
      const TranslationTableCharacter *character = findCharOrDots
	(currentInput[src], 0);
      const TranslationTableCharacter *character2;
      int tryThis = 0;
      while (tryThis < 3)
	{
	  TranslationTableOffset ruleOffset = 0;
	  unsigned long int makeHash = 0;
	  switch (tryThis)
	    {
	    case 0:
	      if (!(length >= 2))
		break;
	      makeHash = (unsigned long int) character->lowercase << 8;
	      character2 = findCharOrDots (currentInput[src + 1], 0);
	      makeHash += (unsigned long int) character2->lowercase;
	      makeHash %= HASHNUM;
	      ruleOffset = table->forRules[makeHash];
	      break;
	    case 1:
	      if (!(length >= 1))
		break;
	      length = 1;
	      ruleOffset = character->otherRules;
	      break;
	    case 2:		/*No rule found */
	      transOpcode = CTO_Always;
	      ruleOffset = 0;
	      break;
	    }
	  while (ruleOffset)
	    {
	      transRule =
		(TranslationTableRule *) & table->ruleArea[ruleOffset];
	      transOpcode = transRule->opcode;
	      transCharslen = transRule->charslen;
	      if (tryThis == 1 || (transCharslen <= length &&
				   compareChars (&transRule->
						 charsdots[0],
						 &currentInput[src],
						 transCharslen, 0)))
		{
		  if (transOpcode == CTO_Syllable)
		    {
		      tryThis = 4;
		      break;
		    }
		}
	      ruleOffset = transRule->charsnext;
	    }
	  tryThis++;
	}
      switch (transOpcode)
	{
	case CTO_Always:
	  if (src >= srcmax)
	    return 0;
	  if (typebuf != NULL)
	    typebuf[src++] |= currentMark;
	  break;
	case CTO_Syllable:
	  syllableMarker++;
	  if (syllableMarker > 3)
	    syllableMarker = 1;
	  currentMark = syllableMarker << 6;
	  /*The syllable marker is bita 6 and 7 of typebuf. */
	  if ((src + transCharslen) > srcmax)
	    return 0;
	  for (k = 0; k < transCharslen; k++)
	    typebuf[src++] |= currentMark;
	  break;
	default:
	  break;
	}
    }
  return 1;
}

static int
translateString (void)
{
/*Main translation routine */
  int k;
  markSyllables ();
  srcword = 0;
  destword = 0;			/* last word translated */
  dontContract = 0;
  prevTransOpcode = CTO_None;
  wordsMarked = 0;
  prevType = prevPrevType = curType = nextType = prevTypeform = plain_text;
  startType = prevSrc = -1;
  src = dest = 0;
  srcIncremented = 1;
  for (k = 0; k < NUMVAR; k++)
    passVariables[k] = 0;
  if (typebuf && table->capitalSign)
    for (k = 0; k < srcmax; k++)
      if (checkAttr (currentInput[k], CTC_UpperCase, 0))
	typebuf[k] |= capsemph;
  while (src < srcmax)
    {				/*the main translation loop */
      setBefore ();
      if (!insertBrailleIndicators (0))
	goto failure;
      if (src >= srcmax)
	break;
      if (!insertIndicators ())
	goto failure;
      for_selectRule ();
      if (appliedRules != NULL && appliedRulesCount < maxAppliedRules)
	appliedRules[appliedRulesCount++] = transRule;
      srcIncremented = 1;
      prevSrc = src;
      switch (transOpcode)	/*Rules that pre-empt context and swap */
	{
	case CTO_CompBrl:
	case CTO_Literal:
	  if (!doCompbrl ())
	    goto failure;
	  continue;
	default:
	  break;
	}
      if (!insertBrailleIndicators (1))
	goto failure;
      if (transOpcode == CTO_Context || findAttribOrSwapRules ())
	switch (transOpcode)
	  {
	  case CTO_Context:
	    if (appliedRules != NULL && appliedRulesCount < maxAppliedRules)
	      appliedRules[appliedRulesCount++] = transRule;
	    if (!passDoAction ())
	      goto failure;
	    if (endReplace == src)
	      srcIncremented = 0;
	    src = endReplace;
	    continue;
	  default:
	    break;
	  }

/*Processing before replacement*/
      switch (transOpcode)
	{
	case CTO_EndNum:
	  if (table->letterSign && checkAttr (currentInput[src],
					      CTC_Letter, 0))
	    dest--;
	  break;
	case CTO_Repeated:
	case CTO_Space:
	  dontContract = 0;
	  break;
	case CTO_LargeSign:
	  if (prevTransOpcode == CTO_LargeSign)
	    if (dest > 0 && checkAttr (currentOutput[dest - 1], CTC_Space, 1))
	      dest--;
	  break;
	case CTO_DecPoint:
	  if (table->numberSign)
	    {
	      TranslationTableRule *numRule = (TranslationTableRule *)
		& table->ruleArea[table->numberSign];
	      if (!for_updatePositions
		  (&numRule->charsdots[numRule->charslen],
		   numRule->charslen, numRule->dotslen))
		goto failure;
	    }
	  transOpcode = CTO_MidNum;
	  break;
	case CTO_NoCont:
	  if (!dontContract)
	    doNocont ();
	  continue;
	default:
	  break;
	}			/*end of action */

      /* replacement processing */
      switch (transOpcode)
	{
	case CTO_Replace:
	  src += transCharslen;
	  if (!putCharacters
	      (&transRule->charsdots[transCharslen], transRule->dotslen))
	    goto failure;
	  break;
	case CTO_None:
	  if (!undefinedCharacter (currentInput[src]))
	    goto failure;
	  src++;
	  break;
	case CTO_UpperCase:
	  /* Only needs special handling if not within compbrl and
	   *the table defines a capital sign. */
	  if (!
	      (mode & (compbrlAtCursor | compbrlLeftCursor) && src >=
	       compbrlStart
	       && src <= compbrlEnd) && (transRule->dotslen == 1
					 && table->capitalSign))
	    {
	      putCharacter (curCharDef->lowercase);
	      src++;
	      break;
	    }
	default:
	  if (cursorStatus == 2)
	    cursorStatus = 1;
	  else
	    {
	      if (transRule->dotslen)
		{
		  if (!for_updatePositions
		      (&transRule->charsdots[transCharslen],
		       transCharslen, transRule->dotslen))
		    goto failure;
		}
	      else
		{
		  for (k = src; k < (src + transCharslen); k++)
		    {
		      if (!putCharacter (currentInput[k]))
			goto failure;
		    }
		}
	      if (cursorStatus == 2)
		cursorStatus = 1;
	      else
		src += transCharslen;
	    }
	  break;
	}

      /* processing after replacement */
      switch (transOpcode)
	{
	case CTO_Repeated:
	  {
	    /* Skip repeated characters. */
	    int srclim = srcmax - transCharslen;
	    if (mode & (compbrlAtCursor | compbrlLeftCursor) &&
		compbrlStart < srclim)
	      /* Don't skip characters from compbrlStart onwards. */
	      srclim = compbrlStart - 1;
	    while ((src <= srclim)
		   && compareChars (&transRule->charsdots[0],
				    &currentInput[src], transCharslen, 0))
	      {
		/* Map skipped input positions to the previous output position. */
		if (outputPositions != NULL)
		  {
		    int tcc;
		    for (tcc = 0; tcc < transCharslen; tcc++)
		      outputPositions[prevSrcMapping[src + tcc]] = dest - 1;
		  }
		if (!cursorStatus && src <= cursorPosition
		    && cursorPosition < src + transCharslen)
		  {
		    cursorStatus = 1;
		    cursorPosition = dest - 1;
		  }
		src += transCharslen;
	      }
	    break;
	  }
	case CTO_RepWord:
	  {
	    /* Skip repeated characters. */
	    int srclim = srcmax - transCharslen;
	    if (mode & (compbrlAtCursor | compbrlLeftCursor) &&
		compbrlStart < srclim)
	      /* Don't skip characters from compbrlStart onwards. */
	      srclim = compbrlStart - 1;
	    while ((src <= srclim)
		   && compareChars (repwordStart,
				    &currentInput[src], repwordLength, 0))
	      {
		/* Map skipped input positions to the previous output position. */
		if (outputPositions != NULL)
		  {
		    int tcc;
		    for (tcc = 0; tcc < transCharslen; tcc++)
		      outputPositions[prevSrcMapping[src + tcc]] = dest - 1;
		  }
		if (!cursorStatus && src <= cursorPosition
		    && cursorPosition < src + transCharslen)
		  {
		    cursorStatus = 1;
		    cursorPosition = dest - 1;
		  }
		src += repwordLength + transCharslen;
	      }
	    src -= transCharslen;
	    break;
	  }
	case CTO_JoinNum:
	case CTO_JoinableWord:
	  while ((src < srcmax)
		 && checkAttr (currentInput[src], CTC_Space, 0))
	    src++;
	  break;
	default:
	  break;
	}
      if (((src > 0) && checkAttr (currentInput[src - 1], CTC_Space, 0)
	   && (transOpcode != CTO_JoinableWord)))
	{
	  srcword = src;
	  destword = dest;
	}
      if (srcSpacing != NULL && srcSpacing[src] >= '0' && srcSpacing[src] <=
	  '9')
	destSpacing[dest] = srcSpacing[src];
      if ((transOpcode >= CTO_Always && transOpcode <= CTO_None) ||
	  (transOpcode >= CTO_Digit && transOpcode <= CTO_LitDigit))
	prevTransOpcode = transOpcode;
    }				/*end of translation loop */
  if (haveEmphasis && !wordsMarked && prevPrevType != plain_text)
    insertBrailleIndicators (2);
failure:
  if (destword != 0 && src < srcmax
      && !checkAttr (currentInput[src], CTC_Space, 0))
    {
      src = srcword;
      dest = destword;
    }
  if (src < srcmax)
    {
      while (checkAttr (currentInput[src], CTC_Space, 0))
	if (++src == srcmax)
	  break;
    }
  realInlen = src;
  return 1;
}				/*first pass translation completed */

int EXPORT_CALL
lou_hyphenate (const char *tableList, const widechar
	       * inbuf, int inlen, char *hyphens, int mode)
{
#define HYPHSTRING 100
  widechar workingBuffer[HYPHSTRING];
  int k, kk;
  int wordStart;
  int wordEnd;
  table = lou_getTable (tableList);
  if (table == NULL || inbuf == NULL || hyphens
      == NULL || table->hyphenStatesArray == 0 || inlen >= HYPHSTRING)
    return 0;
  if (mode != 0)
    {
      k = inlen;
      kk = HYPHSTRING;
      if (!lou_backTranslate (tableList, inbuf, &k,
			      &workingBuffer[0],
			      &kk, NULL, NULL, NULL, NULL, NULL, 0))
	return 0;
    }
  else
    {
      memcpy (&workingBuffer[0], inbuf, CHARSIZE * inlen);
      kk = inlen;
    }
  for (wordStart = 0; wordStart < kk; wordStart++)
    if (((findCharOrDots (workingBuffer[wordStart], 0))->attributes &
	 CTC_Letter))
      break;
  if (wordStart == kk)
    return 0;
  for (wordEnd = kk - 1; wordEnd >= 0; wordEnd--)
    if (((findCharOrDots (workingBuffer[wordEnd], 0))->attributes &
	 CTC_Letter))
      break;
  for (k = wordStart; k <= wordEnd; k++)
    {
      TranslationTableCharacter *c = findCharOrDots (workingBuffer[k], 0);
      if (!(c->attributes & CTC_Letter))
	return 0;
    }
  if (!hyphenate
      (&workingBuffer[wordStart], wordEnd - wordStart + 1,
       &hyphens[wordStart]))
    return 0;
  for (k = 0; k <= wordStart; k++)
    hyphens[k] = '0';
  if (mode != 0)
    {
      widechar workingBuffer2[HYPHSTRING];
      int outputPos[HYPHSTRING];
      char hyphens2[HYPHSTRING];
      kk = wordEnd - wordStart + 1;
      k = HYPHSTRING;
      if (!lou_translate (tableList, &workingBuffer[wordStart], &kk,
			  &workingBuffer2[0], &k, NULL,
			  NULL, &outputPos[0], NULL, NULL, 0))
	return 0;
      for (kk = 0; kk < k; kk++)
	{
	  int hyphPos = outputPos[kk];
	  if (hyphPos > k || hyphPos < 0)
	    break;
	  if (hyphens[wordStart + kk] & 1)
	    hyphens2[hyphPos] = '1';
	  else
	    hyphens2[hyphPos] = '0';
	}
      for (kk = wordStart; kk < wordStart + k; kk++)
	if (!table->noBreak || hyphens2[kk] == '0')
	  hyphens[kk] = hyphens2[kk];
	else
	  {
	    TranslationTableRule *noBreakRule = (TranslationTableRule *)
	      & table->ruleArea[table->noBreak];
	    int kkk;
	    if (kk > 0)
	      for (kkk = 0; kkk < noBreakRule->charslen; kkk++)
		if (workingBuffer2[kk - 1] == noBreakRule->charsdots[kkk])
		  {
		    hyphens[kk] = '0';
		    break;
		  }
	    for (kkk = 0; kkk < noBreakRule->dotslen; kkk++);
	    if (workingBuffer2[kk] ==
		noBreakRule->charsdots[noBreakRule->charslen + kkk])
	      {
		hyphens[kk] = '0';
		break;
	      }
	  }
    }
  for (k = 0; k < inlen; k++)
    if (hyphens[k] & 1)
      hyphens[k] = '1';
    else
      hyphens[k] = '0';
  hyphens[inlen] = 0;
  return 1;
}

int EXPORT_CALL
lou_dotsToChar (const char *tableList, widechar * inbuf, widechar * outbuf,
		int length, int mode)
{
  int k;
  widechar dots;
  if (tableList == NULL || inbuf == NULL || outbuf == NULL)
    return 0;
  if ((mode & otherTrans))
    return other_dotsToChar (tableList, inbuf, outbuf, length, mode);
  table = lou_getTable (tableList);
  if (table == NULL || length <= 0)
    return 0;
  for (k = 0; k < length; k++)
    {
      dots = inbuf[k];
      if (!(dots & B16) && (dots & 0xff00) == 0x2800)	/*Unicode braille */
	dots = (dots & 0x00ff) | B16;
      outbuf[k] = getCharFromDots (dots);
    }
  return 1;
}

int EXPORT_CALL
lou_charToDots (const char *tableList, const widechar * inbuf, widechar *
		outbuf, int length, int mode)
{
  int k;
  if (tableList == NULL || inbuf == NULL || outbuf == NULL)
    return 0;
  if ((mode & otherTrans))
    return other_charToDots (tableList, inbuf, outbuf, length, mode);

  table = lou_getTable (tableList);
  if (table == NULL || length <= 0)
    return 0;
  for (k = 0; k < length; k++)
    if ((mode & ucBrl))
      outbuf[k] = ((getDotsForChar (inbuf[k]) & 0xff) | 0x2800);
    else
      outbuf[k] = getDotsForChar (inbuf[k]);
  return 1;
}
