blob: 18d2aa112dd33057c375c90e49621b41769bb30c [file] [log] [blame]
/* 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
Copyright (C) 2004, 2005, 2006 JJB Software, Inc. www.jjb-software.com
This file is part of liblouis.
liblouis is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation, either version 2.1 of the License, or
(at your option) any later version.
liblouis 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 GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with liblouis. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Translate from braille
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "internal.h"
typedef struct {
int size;
widechar **buffers;
int *inUse;
widechar *(*alloc)(int index, int length);
void (*free)(widechar *);
} StringBufferPool;
static widechar *
allocStringBuffer(int index, int length) {
return _lou_allocMem(alloc_passbuf, index, 0, length);
}
static const StringBufferPool *stringBufferPool = NULL;
static void
initStringBufferPool() {
static widechar *stringBuffers[MAXPASSBUF] = { NULL };
static int stringBuffersInUse[MAXPASSBUF] = { 0 };
StringBufferPool *pool = malloc(sizeof(StringBufferPool));
pool->size = MAXPASSBUF;
pool->buffers = stringBuffers;
pool->inUse = stringBuffersInUse;
pool->alloc = &allocStringBuffer;
pool->free = NULL;
stringBufferPool = pool;
}
static int
getStringBuffer(int length) {
int i;
for (i = 0; i < stringBufferPool->size; i++) {
if (!stringBufferPool->inUse[i]) {
stringBufferPool->buffers[i] = stringBufferPool->alloc(i, length);
stringBufferPool->inUse[i] = 1;
return i;
}
}
_lou_outOfMemory();
return -1;
}
static int
releaseStringBuffer(int idx) {
if (idx >= 0 && idx < stringBufferPool->size) {
int inUse = stringBufferPool->inUse[idx];
if (inUse && stringBufferPool->free)
stringBufferPool->free(stringBufferPool->buffers[idx]);
stringBufferPool->inUse[idx] = 0;
return inUse;
}
return 0;
}
typedef struct {
int bufferIndex;
const widechar *chars;
int length;
} InString;
typedef struct {
int bufferIndex;
widechar *chars;
int maxlength;
int length;
} OutString;
typedef struct {
int startMatch;
int startReplace;
int endReplace;
int endMatch;
} PassRuleMatch;
static int
backTranslateString(const TranslationTableHeader *table,
const DisplayTableHeader *displayTable, int mode, int currentPass,
const InString *input, OutString *output, char *spacebuf, int *posMapping,
int *realInlen, int *cursorPosition, int *cursorStatus,
const TranslationTableRule **appliedRules, int *appliedRulesCount,
int maxAppliedRules);
static int
makeCorrections(const TranslationTableHeader *table,
const DisplayTableHeader *displayTable, int mode, int currentPass,
const InString *input, OutString *output, int *posMapping, int *realInlen,
int *cursorPosition, int *cursorStatus, const TranslationTableRule **appliedRules,
int *appliedRulesCount, int maxAppliedRules);
static int
translatePass(const TranslationTableHeader *table, const DisplayTableHeader *displayTable,
int mode, int currentPass, const InString *input, OutString *output,
int *posMapping, int *realInlen, int *cursorPosition, int *cursorStatus,
const TranslationTableRule **appliedRules, int *appliedRulesCount,
int maxAppliedRules);
static void
passSelectRule(const TranslationTableHeader *table, int pos, int currentPass,
const InString *input, TranslationTableOpcode *currentOpcode,
const TranslationTableRule **currentRule, const widechar **passInstructions,
int *passIC, PassRuleMatch *match);
int EXPORT_CALL
lou_backTranslateString(const char *tableList, const widechar *inbuf, int *inlen,
widechar *outbuf, int *outlen, formtype *typeform, char *spacing, int modex) {
return lou_backTranslate(tableList, inbuf, inlen, outbuf, outlen, typeform, spacing,
NULL, NULL, NULL, modex);
}
int EXPORT_CALL
lou_backTranslate(const char *tableList, const widechar *inbuf, int *inlen,
widechar *outbuf, int *outlen, formtype *typeform, char *spacing, int *outputPos,
int *inputPos, int *cursorPos, int modex) {
return _lou_backTranslate(tableList, tableList, inbuf, inlen, outbuf, outlen,
typeform, spacing, outputPos, inputPos, cursorPos, modex, NULL, NULL);
}
int EXPORT_CALL
_lou_backTranslate(const char *tableList, const char *displayTableList,
const widechar *inbuf, int *inlen, widechar *outbuf, int *outlen,
formtype *typeform, char *spacing, int *outputPos, int *inputPos, int *cursorPos,
int mode, const TranslationTableRule **rules, int *rulesLen) {
const TranslationTableHeader *table;
const DisplayTableHeader *displayTable;
InString input;
OutString output;
unsigned char *typebuf = NULL;
char *spacebuf;
// posMapping contains position mapping info between the output of the current pass
// and the initial input. It is 1 longer than the input. The values are monotonically
// increasing and can range between -1 and the output length. At the end the position
// info is passed to the user as an inputPos and outputPos array. inputPos has the
// length of the final output and has values ranging from 0 to inlen-1. outputPos has
// the length of the initial input and has values ranging from 0 to outlen-1.
int *posMapping = NULL;
int *posMapping1;
int *posMapping2;
int *posMapping3;
int cursorPosition;
int cursorStatus;
const TranslationTableRule **appliedRules;
int maxAppliedRules;
int appliedRulesCount;
int k;
int goodTrans = 1;
int idx;
if (tableList == NULL || inbuf == NULL || inlen == NULL || outbuf == NULL ||
outlen == NULL)
return 0;
if (displayTableList == NULL) displayTableList = tableList;
_lou_getTable(tableList, displayTableList, &table, &displayTable);
if (table == NULL) return 0;
if (!_lou_isValidMode(mode))
_lou_logMessage(LOU_LOG_ERROR, "Invalid mode parameter: %d", mode);
if (!stringBufferPool) initStringBufferPool();
for (idx = 0; idx < stringBufferPool->size; idx++) releaseStringBuffer(idx);
{
widechar *passbuf1;
int srcmax;
k = 0;
while (k < *inlen && inbuf[k]) k++;
srcmax = k;
idx = getStringBuffer(srcmax);
passbuf1 = stringBufferPool->buffers[idx];
for (k = 0; k < srcmax; k++)
if ((mode & dotsIO))
passbuf1[k] = inbuf[k] | LOU_DOTS;
else
passbuf1[k] = _lou_getDotsForChar(inbuf[k], displayTable);
passbuf1[srcmax] = _lou_getDotsForChar(' ', displayTable);
input = (InString){ .chars = passbuf1, .length = srcmax, .bufferIndex = idx };
}
idx = getStringBuffer(*outlen);
output = (OutString){ .chars = stringBufferPool->buffers[idx],
.maxlength = *outlen,
.length = 0,
.bufferIndex = idx };
typebuf = (unsigned char *)typeform;
spacebuf = spacing;
if (outputPos != NULL)
for (k = 0; k < input.length; k++) outputPos[k] = -1;
if (cursorPos != NULL)
cursorPosition = *cursorPos;
else
cursorPosition = -1;
cursorStatus = 0;
if (typebuf != NULL) memset(typebuf, '0', *outlen);
if (spacebuf != NULL) memset(spacebuf, '*', *outlen);
if (!(posMapping1 = _lou_allocMem(alloc_posMapping1, 0, input.length, *outlen)))
return 0;
if (table->numPasses > 1 || table->corrections) {
if (!(posMapping2 = _lou_allocMem(alloc_posMapping2, 0, input.length, *outlen)))
return 0;
if (!(posMapping3 = _lou_allocMem(alloc_posMapping3, 0, input.length, *outlen)))
return 0;
}
appliedRulesCount = 0;
if (rules != NULL && rulesLen != NULL) {
appliedRules = rules;
maxAppliedRules = *rulesLen;
} else {
appliedRules = NULL;
maxAppliedRules = 0;
}
posMapping = posMapping1;
int currentPass = table->numPasses;
int lastPass = table->corrections ? 0 : 1;
int *passPosMapping = posMapping;
while (1) {
int realInlen;
switch (currentPass) {
case 1:
goodTrans = backTranslateString(table, displayTable, mode, currentPass,
&input, &output, spacebuf, passPosMapping, &realInlen,
&cursorPosition, &cursorStatus, appliedRules, &appliedRulesCount,
maxAppliedRules);
break;
case 0:
goodTrans = makeCorrections(table, displayTable, mode, currentPass, &input,
&output, passPosMapping, &realInlen, &cursorPosition, &cursorStatus,
appliedRules, &appliedRulesCount, maxAppliedRules);
break;
default:
goodTrans = translatePass(table, displayTable, mode, currentPass, &input,
&output, passPosMapping, &realInlen, &cursorPosition, &cursorStatus,
appliedRules, &appliedRulesCount, maxAppliedRules);
break;
}
passPosMapping[realInlen] = output.length;
if (passPosMapping == posMapping) {
passPosMapping = posMapping2;
} else {
int *prevPosMapping = posMapping3;
memcpy((int *)prevPosMapping, posMapping, (*inlen + 1) * sizeof(int));
for (k = 0; k <= *inlen; k++) {
if (prevPosMapping[k] > realInlen) {
*inlen = k;
posMapping[k] = output.length;
break;
} else if (prevPosMapping[k] < 0)
posMapping[k] = passPosMapping[0];
else
posMapping[k] = passPosMapping[prevPosMapping[k]];
}
}
currentPass--;
if (currentPass >= lastPass && goodTrans) {
releaseStringBuffer(input.bufferIndex);
input = (InString){ .chars = output.chars,
.length = output.length,
.bufferIndex = output.bufferIndex };
idx = getStringBuffer(*outlen);
output = (OutString){ .chars = stringBufferPool->buffers[idx],
.maxlength = *outlen,
.length = 0,
.bufferIndex = idx };
continue;
}
break;
}
if (goodTrans) {
for (k = 0; k < output.length; k++) outbuf[k] = output.chars[k];
*outlen = output.length;
if (inputPos != NULL) {
int inpos = -1;
int outpos = -1;
for (k = 0; k < *inlen; k++)
if (posMapping[k] > outpos) {
while (outpos < posMapping[k]) {
if (outpos >= 0 && outpos < *outlen)
inputPos[outpos] = inpos < 0 ? 0 : inpos;
outpos++;
}
inpos = k;
}
if (outpos < 0) outpos = 0;
while (outpos < *outlen) inputPos[outpos++] = inpos;
}
if (outputPos != NULL) {
for (k = 0; k < *inlen; k++)
if (posMapping[k] < 0)
outputPos[k] = 0;
else if (posMapping[k] > *outlen - 1)
outputPos[k] = *outlen - 1;
else
outputPos[k] = posMapping[k];
}
}
if (cursorPos != NULL && *cursorPos != -1) {
if (outputPos != NULL)
*cursorPos = outputPos[*cursorPos];
else
*cursorPos = cursorPosition;
}
if (rulesLen != NULL) *rulesLen = appliedRulesCount;
return goodTrans;
}
static TranslationTableCharacter *
back_findCharOrDots(widechar c, int m, const TranslationTableHeader *table) {
/* Look up character or dot pattern in the appropriate
* table. */
static TranslationTableCharacter noChar = { 0, 0, 0, CTC_Space, 32, 32, 32 };
static TranslationTableCharacter noDots = { 0, 0, 0, CTC_Space, LOU_DOTS, LOU_DOTS,
LOU_DOTS };
TranslationTableCharacter *notFound;
TranslationTableCharacter *character;
TranslationTableOffset bucket;
unsigned long int makeHash = _lou_charHash(c);
if (m == 0) {
bucket = table->characters[makeHash];
notFound = &noChar;
} else {
bucket = table->dots[makeHash];
notFound = &noDots;
}
while (bucket) {
character = (TranslationTableCharacter *)&table->ruleArea[bucket];
if (character->realchar == c) return character;
bucket = character->next;
}
notFound->realchar = notFound->uppercase = notFound->lowercase = c;
return notFound;
}
static int
checkAttr(const widechar c, const TranslationTableCharacterAttributes a, int m,
const TranslationTableHeader *table) {
static widechar prevc = 0;
static TranslationTableCharacterAttributes preva = 0;
if (c != prevc) {
preva = (back_findCharOrDots(c, m, table))->attributes;
prevc = c;
}
return ((preva & a) ? 1 : 0);
}
static int
compareDots(const widechar *address1, const widechar *address2, int count) {
int k;
if (!count) return 0;
for (k = 0; k < count; k++)
if (address1[k] != address2[k]) return 0;
return 1;
}
static void
back_setBefore(const TranslationTableHeader *table, OutString *output,
TranslationTableCharacterAttributes *beforeAttributes) {
widechar before = (output->length == 0) ? ' ' : output->chars[output->length - 1];
*beforeAttributes = (back_findCharOrDots(before, 0, table))->attributes;
}
static void
back_setAfter(int length, const TranslationTableHeader *table, int pos,
const InString *input, TranslationTableCharacterAttributes *afterAttributes) {
widechar after = (pos + length < input->length) ? input->chars[pos + length] : ' ';
*afterAttributes = (back_findCharOrDots(after, 1, table))->attributes;
}
static int
isBegWord(const TranslationTableHeader *table, OutString *output) {
/* See if this is really the beginning of a word. Look at what has
* already been translated. */
int k;
if (output->length == 0) return 1;
for (k = output->length - 1; k >= 0; k--) {
const TranslationTableCharacter *ch =
back_findCharOrDots(output->chars[k], 0, table);
if (ch->attributes & CTC_Space) break;
if (ch->attributes & (CTC_Letter | CTC_Digit | CTC_Math | CTC_Sign)) return 0;
}
return 1;
}
static int
isEndWord(const TranslationTableHeader *table, int pos, int mode, const InString *input,
int currentDotslen) {
if (mode & partialTrans) return 0;
/* See if this is really the end of a word. */
int k;
const TranslationTableCharacter *dots;
TranslationTableOffset testRuleOffset;
TranslationTableRule *testRule;
for (k = pos + currentDotslen; k < input->length; k++) {
int postpuncFound = 0;
int TranslationFound = 0;
dots = back_findCharOrDots(input->chars[k], 1, table);
testRuleOffset = dots->otherRules;
if (dots->attributes & CTC_Space) break;
if (dots->attributes & CTC_Letter) return 0;
while (testRuleOffset) {
testRule = (TranslationTableRule *)&table->ruleArea[testRuleOffset];
/* #360: Don't treat begword/midword as definite translations here
* because we don't know whether they apply yet. Subsequent
* input will allow us to determine whether the word continues.
*/
if (testRule->charslen > 1 && testRule->opcode != CTO_BegWord &&
testRule->opcode != CTO_MidWord)
TranslationFound = 1;
if (testRule->opcode == CTO_PostPunc) postpuncFound = 1;
if (testRule->opcode == CTO_Hyphen) return 1;
testRuleOffset = testRule->dotsnext;
}
if (TranslationFound && !postpuncFound) return 0;
}
return 1;
}
static int
findBrailleIndicatorRule(TranslationTableOffset offset,
const TranslationTableHeader *table, int *currentDotslen,
TranslationTableOpcode *currentOpcode, const TranslationTableRule **currentRule) {
if (!offset) return 0;
*currentRule = (TranslationTableRule *)&table->ruleArea[offset];
*currentOpcode = (*currentRule)->opcode;
*currentDotslen = (*currentRule)->dotslen;
return 1;
}
static int
handleMultind(const TranslationTableHeader *table, int *currentDotslen,
TranslationTableOpcode *currentOpcode, const TranslationTableRule **currentRule,
int *doingMultind, const TranslationTableRule *multindRule) {
/* Handle multille braille indicators */
int found = 0;
if (!*doingMultind) return 0;
switch (multindRule->charsdots[multindRule->charslen - *doingMultind]) {
case CTO_CapsLetterRule: // FIXME: make sure this works
found = findBrailleIndicatorRule(table->emphRules[capsRule][letterOffset], table,
currentDotslen, currentOpcode, currentRule);
break;
// NOTE: following fixme is based on the names at the time of
// commit f22f91eb510cb4eef33dfb4950a297235dd2f9f1.
// FIXME: the next two opcodes were begcaps/endcaps,
// and they were aliased to opcodes capsword/capswordstop.
// However, the table attributes they use are
// table->beginCapitalSign and table->endCapitalSign.
// These are actually compiled with firstlettercaps/lastlettercaps.
// Which to use here?
case CTO_BegCapsWordRule:
found = findBrailleIndicatorRule(table->emphRules[capsRule][begWordOffset], table,
currentDotslen, currentOpcode, currentRule);
break;
case CTO_EndCapsWordRule:
found = findBrailleIndicatorRule(table->emphRules[capsRule][endWordOffset], table,
currentDotslen, currentOpcode, currentRule);
break;
case CTO_LetterSign:
found = findBrailleIndicatorRule(
table->letterSign, table, currentDotslen, currentOpcode, currentRule);
break;
case CTO_NoContractSign:
found = findBrailleIndicatorRule(
table->noContractSign, table, currentDotslen, currentOpcode, currentRule);
break;
case CTO_NumberSign:
found = findBrailleIndicatorRule(
table->numberSign, table, currentDotslen, currentOpcode, currentRule);
break;
case CTO_EndEmph1PhraseBeforeRule:
found = findBrailleIndicatorRule(
table->emphRules[emph1Rule][endPhraseBeforeOffset], table, currentDotslen,
currentOpcode, currentRule);
break;
case CTO_BegEmph1Rule:
found = findBrailleIndicatorRule(table->emphRules[emph1Rule][begOffset], table,
currentDotslen, currentOpcode, currentRule);
break;
case CTO_EndEmph1Rule:
found = findBrailleIndicatorRule(table->emphRules[emph1Rule][endOffset], table,
currentDotslen, currentOpcode, currentRule);
break;
case CTO_EndEmph2PhraseBeforeRule:
found = findBrailleIndicatorRule(
table->emphRules[emph2Rule][endPhraseBeforeOffset], table, currentDotslen,
currentOpcode, currentRule);
break;
case CTO_BegEmph2Rule:
found = findBrailleIndicatorRule(table->emphRules[emph2Rule][begOffset], table,
currentDotslen, currentOpcode, currentRule);
break;
case CTO_EndEmph2Rule:
found = findBrailleIndicatorRule(table->emphRules[emph2Rule][endOffset], table,
currentDotslen, currentOpcode, currentRule);
break;
case CTO_EndEmph3PhraseBeforeRule:
found = findBrailleIndicatorRule(
table->emphRules[emph3Rule][endPhraseBeforeOffset], table, currentDotslen,
currentOpcode, currentRule);
break;
case CTO_BegEmph3Rule:
found = findBrailleIndicatorRule(table->emphRules[emph3Rule][begOffset], table,
currentDotslen, currentOpcode, currentRule);
break;
case CTO_EndEmph3Rule:
found = findBrailleIndicatorRule(table->emphRules[emph3Rule][endOffset], table,
currentDotslen, currentOpcode, currentRule);
break;
case CTO_BegComp:
found = findBrailleIndicatorRule(
table->begComp, table, currentDotslen, currentOpcode, currentRule);
break;
case CTO_EndComp:
found = findBrailleIndicatorRule(
table->endComp, table, currentDotslen, currentOpcode, currentRule);
break;
default:
found = 0;
break;
}
(*doingMultind)--;
return found;
}
static int
back_passDoTest(const TranslationTableHeader *table, int pos, const InString *input,
TranslationTableOpcode currentOpcode, const TranslationTableRule *currentRule,
const widechar **passInstructions, int *passIC, PassRuleMatch *match);
static int
back_passDoAction(const TranslationTableHeader *table,
const DisplayTableHeader *displayTable, int *pos, int mode, const InString *input,
OutString *output, int *posMapping, int *cursorPosition, int *cursorStatus,
int *nextUpper, int allUpper, int allUpperPhrase,
TranslationTableOpcode currentOpcode, const TranslationTableRule *currentRule,
const widechar *passInstructions, int passIC, PassRuleMatch match);
static int
findBackPassRule(const TranslationTableHeader *table, int pos, int currentPass,
const InString *input, TranslationTableOpcode *currentOpcode,
const TranslationTableRule **currentRule, const widechar **passInstructions,
int *passIC, PassRuleMatch *match) {
TranslationTableOffset ruleOffset;
ruleOffset = table->backPassRules[currentPass];
while (ruleOffset) {
*currentRule = (TranslationTableRule *)&table->ruleArea[ruleOffset];
*currentOpcode = (*currentRule)->opcode;
switch (*currentOpcode) {
case CTO_Correct:
if (currentPass != 0) goto NEXT_RULE;
break;
case CTO_Context:
if (currentPass != 1) goto NEXT_RULE;
break;
case CTO_Pass2:
if (currentPass != 2) goto NEXT_RULE;
break;
case CTO_Pass3:
if (currentPass != 3) goto NEXT_RULE;
break;
case CTO_Pass4:
if (currentPass != 4) goto NEXT_RULE;
break;
default:
goto NEXT_RULE;
}
if (back_passDoTest(table, pos, input, *currentOpcode, *currentRule,
passInstructions, passIC, match))
return 1;
NEXT_RULE:
ruleOffset = (*currentRule)->dotsnext;
}
return 0;
}
static void
back_selectRule(const TranslationTableHeader *table, int pos, int mode,
const InString *input, OutString *output, int itsANumber, int itsALetter,
int *currentDotslen, TranslationTableOpcode *currentOpcode,
const TranslationTableRule **currentRule, TranslationTableOpcode previousOpcode,
int *doingMultind, const TranslationTableRule **multindRule,
TranslationTableCharacterAttributes beforeAttributes,
const widechar **passInstructions, int *passIC, PassRuleMatch *patternMatch) {
/* check for valid back-translations */
int length = input->length - pos;
TranslationTableOffset ruleOffset = 0;
static TranslationTableRule pseudoRule = { 0 };
unsigned long int makeHash = 0;
const TranslationTableCharacter *dots =
back_findCharOrDots(input->chars[pos], 1, table);
int tryThis;
if (handleMultind(table, currentDotslen, currentOpcode, currentRule, doingMultind,
*multindRule))
return;
for (tryThis = 0; tryThis < 3; tryThis++) {
switch (tryThis) {
case 0:
if (length < 2 || (itsANumber && (dots->attributes & CTC_LitDigit))) break;
/* Hash function optimized for backward translation */
makeHash = (unsigned long int)dots->realchar << 8;
makeHash += (unsigned long int)(back_findCharOrDots(
input->chars[pos + 1], 1, table))
->realchar;
makeHash %= HASHNUM;
ruleOffset = table->backRules[makeHash];
break;
case 1:
if (!(length >= 1)) break;
length = 1;
ruleOffset = dots->otherRules;
break;
case 2: /* No rule found */
*currentRule = &pseudoRule;
*currentOpcode = pseudoRule.opcode = CTO_None;
*currentDotslen = pseudoRule.dotslen = 1;
pseudoRule.charsdots[0] = input->chars[pos];
pseudoRule.charslen = 0;
return;
break;
}
while (ruleOffset) {
const widechar *currentDots;
*currentRule = (TranslationTableRule *)&table->ruleArea[ruleOffset];
*currentOpcode = (*currentRule)->opcode;
if (*currentOpcode == CTO_Context) {
currentDots = &(*currentRule)->charsdots[0];
*currentDotslen = (*currentRule)->charslen;
} else {
currentDots = &(*currentRule)->charsdots[(*currentRule)->charslen];
*currentDotslen = (*currentRule)->dotslen;
}
if (((*currentDotslen <= length) &&
compareDots(&input->chars[pos], currentDots, *currentDotslen))) {
TranslationTableCharacterAttributes afterAttributes;
/* check this rule */
back_setAfter(*currentDotslen, table, pos, input, &afterAttributes);
if ((!((*currentRule)->after & ~CTC_EmpMatch) ||
(beforeAttributes & (*currentRule)->after)) &&
(!((*currentRule)->before & ~CTC_EmpMatch) ||
(afterAttributes & (*currentRule)->before))) {
switch (*currentOpcode) { /* check validity of this Translation */
case CTO_Context:
if (back_passDoTest(table, pos, input, *currentOpcode,
*currentRule, passInstructions, passIC, patternMatch))
return;
break;
case CTO_Space:
case CTO_Digit:
case CTO_Letter:
case CTO_UpperCase:
case CTO_LowerCase:
case CTO_Punctuation:
case CTO_Math:
case CTO_Sign:
case CTO_ExactDots:
case CTO_NoCross:
case CTO_Repeated:
case CTO_Replace:
case CTO_Hyphen:
return;
case CTO_LitDigit:
if (itsANumber) return;
break;
case CTO_CapsLetterRule:
case CTO_BegCapsRule:
case CTO_EndCapsRule:
case CTO_BegCapsWordRule:
case CTO_EndCapsWordRule:
case CTO_BegEmph1Rule:
case CTO_EndEmph1Rule:
case CTO_BegEmph2Rule:
case CTO_EndEmph2Rule:
case CTO_BegEmph3Rule:
case CTO_EndEmph3Rule:
case CTO_NumberRule:
case CTO_BegCompRule:
case CTO_EndCompRule:
return;
case CTO_LetterRule:
case CTO_NoContractRule:
// BF: This is just a heuristic test. During forward translation,
// the
// nocontractsign is inserted either when in numeric mode and the
// next
// character is not numeric (CTC_Digit | CTC_LitDigit |
// CTC_NumericMode | CTC_MidEndNumericMode),
// or when a "contraction" rule is matched and the characters are
// preceded and followed by space or punctuation (CTC_Space |
// CTC_Punctuation).
if (!(beforeAttributes & CTC_Letter) &&
(afterAttributes & (CTC_Letter | CTC_Sign)))
return;
break;
case CTO_MultInd:
*doingMultind = *currentDotslen;
*multindRule = *currentRule;
if (handleMultind(table, currentDotslen, currentOpcode,
currentRule, doingMultind, *multindRule))
return;
break;
case CTO_LargeSign:
return;
case CTO_WholeWord:
if (mode & partialTrans) break;
if (itsALetter || itsANumber) break;
case CTO_Contraction:
if ((beforeAttributes & (CTC_Space | CTC_Punctuation)) &&
((afterAttributes & CTC_Space) ||
isEndWord(table, pos, mode, input,
*currentDotslen)))
return;
break;
case CTO_LowWord:
if (mode & partialTrans) break;
if ((beforeAttributes & CTC_Space) &&
(afterAttributes & CTC_Space) &&
(previousOpcode != CTO_JoinableWord))
return;
break;
case CTO_JoinNum:
case CTO_JoinableWord:
if ((beforeAttributes & (CTC_Space | CTC_Punctuation)) &&
(!(afterAttributes & CTC_Space) || mode & partialTrans))
return;
break;
case CTO_SuffixableWord:
if (beforeAttributes & (CTC_Space | CTC_Punctuation)) return;
break;
case CTO_PrefixableWord:
if ((beforeAttributes &
(CTC_Space | CTC_Letter | CTC_Punctuation)) &&
isEndWord(table, pos, mode, input, *currentDotslen))
return;
break;
case CTO_BegWord:
if ((beforeAttributes & (CTC_Space | CTC_Punctuation)) &&
(!isEndWord(table, pos, mode, input, *currentDotslen)))
return;
break;
case CTO_BegMidWord:
if ((beforeAttributes &
(CTC_Letter | CTC_Space | CTC_Punctuation)) &&
(!isEndWord(table, pos, mode, input, *currentDotslen)))
return;
break;
case CTO_PartWord:
if (!(beforeAttributes & CTC_LitDigit) &&
(beforeAttributes & CTC_Letter ||
!isEndWord(table, pos, mode, input,
*currentDotslen)))
return;
break;
case CTO_MidWord:
if (beforeAttributes & CTC_Letter &&
!isEndWord(table, pos, mode, input, *currentDotslen))
return;
break;
case CTO_MidEndWord:
if ((beforeAttributes & CTC_Letter)) return;
break;
case CTO_EndWord:
if ((beforeAttributes & CTC_Letter) &&
isEndWord(table, pos, mode, input, *currentDotslen))
return;
break;
case CTO_BegNum:
if (beforeAttributes & (CTC_Space | CTC_Punctuation) &&
(afterAttributes & (CTC_LitDigit | CTC_Sign)))
return;
break;
case CTO_MidNum:
if (beforeAttributes & CTC_Digit &&
afterAttributes & CTC_LitDigit)
return;
break;
case CTO_EndNum:
if (itsANumber && !(afterAttributes & CTC_LitDigit)) return;
break;
case CTO_DecPoint:
if (afterAttributes & (CTC_Digit | CTC_LitDigit)) return;
break;
case CTO_PrePunc:
if (isBegWord(table, output)) return;
break;
case CTO_PostPunc:
if (isEndWord(table, pos, mode, input, *currentDotslen)) return;
break;
case CTO_Always:
if ((beforeAttributes & CTC_LitDigit) &&
(afterAttributes & CTC_LitDigit) &&
(*currentRule)->charslen > 1)
break;
return;
case CTO_BackMatch: {
widechar *patterns, *pattern;
// if(dontContract || (mode & noContractions))
// break;
// if(checkEmphasisChange(0))
// break;
patterns = (widechar *)&table->ruleArea[(*currentRule)->patterns];
/* check before pattern */
pattern = &patterns[1];
if (!_lou_pattern_check(
input->chars, pos - 1, -1, -1, pattern, table))
break;
/* check after pattern */
pattern = &patterns[patterns[0]];
if (!_lou_pattern_check(input->chars,
pos + (*currentRule)->dotslen, input->length, 1,
pattern, table))
break;
return;
}
default:
break;
}
}
} /* Done with checking this rule */
ruleOffset = (*currentRule)->dotsnext;
}
}
}
static int
putchars(const widechar *chars, int count, const TranslationTableHeader *table,
OutString *output, int *nextUpper, int allUpper, int allUpperPhrase) {
int k = 0;
if (!count || (output->length + count) > output->maxlength) return 0;
if (*nextUpper) {
output->chars[(output->length)++] =
(back_findCharOrDots(chars[k++], 0, table))->uppercase;
*nextUpper = 0;
}
if (!allUpper && !allUpperPhrase) {
memcpy(&output->chars[output->length], &chars[k], CHARSIZE * (count - k));
output->length += count - k;
} else
for (; k < count; k++)
output->chars[(output->length)++] =
(back_findCharOrDots(chars[k], 0, table))->uppercase;
return 1;
}
static int
back_updatePositions(const widechar *outChars, int inLength, int outLength,
const TranslationTableHeader *table, int pos, const InString *input,
OutString *output, int *posMapping, int *cursorPosition, int *cursorStatus,
int *nextUpper, int allUpper, int allUpperPhrase) {
int k;
if ((output->length + outLength) > output->maxlength ||
(pos + inLength) > input->length)
return 0;
if (!*cursorStatus && *cursorPosition >= pos && *cursorPosition < (pos + inLength)) {
*cursorPosition = output->length + outLength / 2;
*cursorStatus = 1;
}
for (k = 0; k < inLength; k++) posMapping[pos + k] = output->length;
return putchars(
outChars, outLength, table, output, nextUpper, allUpper, allUpperPhrase);
}
static int
undefinedDots(widechar dots, int mode, OutString *output, int pos, int *posMapping) {
posMapping[pos] = output->length;
if (mode & noUndefined) return 1;
/* Print out dot numbers */
const char *buffer = _lou_unknownDots(dots);
size_t buflen = strlen(buffer);
if ((output->length + buflen) > output->maxlength) return 0;
for (unsigned int k = 0; k < buflen; k += 1) {
output->chars[output->length++] = buffer[k];
}
return 1;
}
static int
putCharacter(widechar dots, const TranslationTableHeader *table,
const DisplayTableHeader *displayTable, int pos, int mode, const InString *input,
OutString *output, int *posMapping, int *cursorPosition, int *cursorStatus,
int *nextUpper, int allUpper, int allUpperPhrase) {
/* Output character(s) corresponding to a Unicode braille Character */
TranslationTableOffset offset = (back_findCharOrDots(dots, 1, table))->definitionRule;
if (offset) {
widechar c;
const TranslationTableRule *rule =
(TranslationTableRule *)&table->ruleArea[offset];
if (rule->charslen)
return back_updatePositions(&rule->charsdots[0], rule->dotslen,
rule->charslen, table, pos, input, output, posMapping, cursorPosition,
cursorStatus, nextUpper, allUpper, allUpperPhrase);
c = _lou_getCharFromDots(dots, displayTable);
if (c == '\0') c = ' ';
return back_updatePositions(&c, 1, 1, table, pos, input, output, posMapping,
cursorPosition, cursorStatus, nextUpper, allUpper, allUpperPhrase);
}
return undefinedDots(dots, mode, output, pos, posMapping);
}
static int
putCharacters(const widechar *characters, int count, const TranslationTableHeader *table,
const DisplayTableHeader *displayTable, int pos, int mode, const InString *input,
OutString *output, int *posMapping, int *cursorPosition, int *cursorStatus,
int *nextUpper, int allUpper, int allUpperPhrase) {
int k;
for (k = 0; k < count; k++)
if (!putCharacter(characters[k], table, displayTable, pos, mode, input, output,
posMapping, cursorPosition, cursorStatus, nextUpper, allUpper,
allUpperPhrase))
return 0;
return 1;
}
static int
insertSpace(const TranslationTableHeader *table, int pos, const InString *input,
OutString *output, char *spacebuf, int *posMapping, int *cursorPosition,
int *cursorStatus, int *nextUpper, int allUpper, int allUpperPhrase) {
widechar c = ' ';
if (!back_updatePositions(&c, 1, 1, table, pos, input, output, posMapping,
cursorPosition, cursorStatus, nextUpper, allUpper, allUpperPhrase))
return 0;
if (spacebuf) spacebuf[output->length - 1] = '1';
return 1;
}
static int
compareChars(const widechar *address1, const widechar *address2, int count, int m,
const TranslationTableHeader *table) {
int k;
if (!count) return 0;
for (k = 0; k < count; k++)
if ((back_findCharOrDots(address1[k], m, table))->lowercase !=
(back_findCharOrDots(address2[k], m, table))->lowercase)
return 0;
return 1;
}
static int
makeCorrections(const TranslationTableHeader *table,
const DisplayTableHeader *displayTable, int mode, int currentPass,
const InString *input, OutString *output, int *posMapping, int *realInlen,
int *cursorPosition, int *cursorStatus, const TranslationTableRule **appliedRules,
int *appliedRulesCount, int maxAppliedRules) {
int pos;
int nextUpper = 0;
int allUpper = 0;
int allUpperPhrase = 0;
if (!table->corrections) return 1;
pos = 0;
output->length = 0;
_lou_resetPassVariables();
while (pos < input->length) {
TranslationTableOpcode currentOpcode;
const TranslationTableRule *currentRule; /* pointer to current rule in table */
const widechar *passInstructions;
int passIC; /* Instruction counter */
PassRuleMatch patternMatch;
int length = input->length - pos;
const TranslationTableCharacter *character =
back_findCharOrDots(input->chars[pos], 0, table);
const TranslationTableCharacter *character2;
int tryThis = 0;
if (!findBackPassRule(table, pos, currentPass, input, &currentOpcode,
&currentRule, &passInstructions, &passIC, &patternMatch))
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 = back_findCharOrDots(input->chars[pos + 1], 0, table);
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 */
currentOpcode = CTO_Always;
ruleOffset = 0;
break;
}
while (ruleOffset) {
currentRule = (TranslationTableRule *)&table->ruleArea[ruleOffset];
currentOpcode = currentRule->opcode;
int currentCharslen = currentRule->charslen;
if (tryThis == 1 ||
(currentCharslen <= length &&
compareChars(&currentRule->charsdots[0],
&input->chars[pos], currentCharslen, 0,
table))) {
if (currentOpcode == CTO_Correct &&
back_passDoTest(table, pos, input, currentOpcode,
currentRule, &passInstructions, &passIC,
&patternMatch)) {
tryThis = 4;
break;
}
}
ruleOffset = currentRule->dotsnext;
}
tryThis++;
}
switch (currentOpcode) {
case CTO_Always:
if (output->length >= output->maxlength) goto failure;
posMapping[pos] = output->length;
output->chars[(output->length)++] = input->chars[pos++];
break;
case CTO_Correct:
if (appliedRules != NULL && *appliedRulesCount < maxAppliedRules)
appliedRules[(*appliedRulesCount)++] = currentRule;
if (!back_passDoAction(table, displayTable, &pos, mode, input, output,
posMapping, cursorPosition, cursorStatus, &nextUpper, allUpper,
allUpperPhrase, currentOpcode, currentRule, passInstructions,
passIC, patternMatch))
goto failure;
break;
default:
break;
}
}
failure:
*realInlen = pos;
return 1;
}
static int
backTranslateString(const TranslationTableHeader *table,
const DisplayTableHeader *displayTable, int mode, int currentPass,
const InString *input, OutString *output, char *spacebuf, int *posMapping,
int *realInlen, int *cursorPosition, int *cursorStatus,
const TranslationTableRule **appliedRules, int *appliedRulesCount,
int maxAppliedRules) {
int pos;
int nextUpper;
int allUpper;
int allUpperPhrase;
int itsANumber;
int itsALetter;
/* Back translation */
int srcword = 0;
int destword = 0; /* last word translated */
TranslationTableOpcode previousOpcode;
int doingMultind = 0;
const TranslationTableRule *multindRule;
_lou_resetPassVariables();
translation_direction = 0;
nextUpper = allUpper = allUpperPhrase = itsANumber = itsALetter = 0;
previousOpcode = CTO_None;
pos = output->length = 0;
while (pos < input->length) {
/* the main translation loop */
int currentDotslen; /* length of current find string */
TranslationTableOpcode currentOpcode;
const TranslationTableRule *currentRule; /* pointer to current rule in table */
TranslationTableCharacterAttributes beforeAttributes;
const widechar *passInstructions;
int passIC; /* Instruction counter */
PassRuleMatch patternMatch;
back_setBefore(table, output, &beforeAttributes);
if ((allUpper == 1) && (beforeAttributes & CTC_UpperCase))
// Capsword in progress
allUpper = 2;
else if ((allUpper == 2) && !(beforeAttributes & CTC_UpperCase) &&
!(beforeAttributes & CTC_CapsMode))
// terminate capsword
allUpper = 0;
if ((itsANumber == 2) && output->length > 0 &&
!(beforeAttributes & CTC_LitDigit) &&
!(beforeAttributes & CTC_NumericMode) &&
!(beforeAttributes & CTC_MidEndNumericMode))
itsANumber = 0;
back_selectRule(table, pos, mode, input, output, itsANumber, itsALetter,
&currentDotslen, &currentOpcode, &currentRule, previousOpcode,
&doingMultind, &multindRule, beforeAttributes, &passInstructions, &passIC,
&patternMatch);
if (appliedRules != NULL && *appliedRulesCount < maxAppliedRules)
appliedRules[(*appliedRulesCount)++] = currentRule;
/* processing before replacement */
switch (currentOpcode) {
case CTO_LargeSign:
if (previousOpcode == CTO_LargeSign)
if (!insertSpace(table, pos, input, output, spacebuf, posMapping,
cursorPosition, cursorStatus, &nextUpper, allUpper,
allUpperPhrase))
goto failure;
break;
case CTO_CapsLetterRule:
nextUpper = 1;
allUpper = 0;
itsANumber = 0;
while (currentDotslen-- > 0) posMapping[pos++] = output->length;
continue;
break;
case CTO_BegCapsWordRule:
allUpper = 1;
itsANumber = 0;
while (currentDotslen-- > 0) posMapping[pos++] = output->length;
continue;
break;
case CTO_BegCapsRule:
allUpperPhrase = 1;
itsANumber = 0;
while (currentDotslen-- > 0) posMapping[pos++] = output->length;
continue;
break;
case CTO_EndCapsWordRule:
allUpper = 0;
itsANumber = 0;
while (currentDotslen-- > 0) posMapping[pos++] = output->length;
continue;
break;
case CTO_EndCapsRule:
allUpperPhrase = 0;
itsANumber = 0;
while (currentDotslen-- > 0) posMapping[pos++] = output->length;
continue;
break;
case CTO_LetterRule:
case CTO_NoContractRule:
itsALetter = 1;
itsANumber = 0;
while (currentDotslen-- > 0) posMapping[pos++] = output->length;
continue;
break;
case CTO_NumberRule:
itsANumber = 1; // Starting number
allUpper = 0;
while (currentDotslen-- > 0) posMapping[pos++] = output->length;
continue;
break;
case CTO_LitDigit:
itsANumber = 2; // In the middle of a number
break;
case CTO_BegCompRule:
itsANumber = 0;
case CTO_BegEmph1Rule:
case CTO_BegEmph2Rule:
case CTO_BegEmph3Rule:
case CTO_EndEmph1Rule:
case CTO_EndEmph2Rule:
case CTO_EndEmph3Rule:
case CTO_EndCompRule:
while (currentDotslen-- > 0) posMapping[pos++] = output->length;
continue;
break;
default:
break;
}
/* replacement processing */
switch (currentOpcode) {
case CTO_Context:
if (!back_passDoAction(table, displayTable, &pos, mode, input, output,
posMapping, cursorPosition, cursorStatus, &nextUpper, allUpper,
allUpperPhrase, currentOpcode, currentRule, passInstructions,
passIC, patternMatch))
return 0;
break;
case CTO_Replace:
while (currentDotslen-- > 0) posMapping[pos++] = output->length;
if (!putCharacters(&currentRule->charsdots[0], currentRule->charslen, table,
displayTable, pos, mode, input, output, posMapping,
cursorPosition, cursorStatus, &nextUpper, allUpper,
allUpperPhrase))
goto failure;
break;
case CTO_None:
if (!undefinedDots(input->chars[pos], mode, output, pos, posMapping))
goto failure;
pos++;
break;
case CTO_BegNum:
itsANumber = 1;
goto insertChars;
case CTO_EndNum:
itsANumber = 0;
goto insertChars;
case CTO_Space:
itsALetter = itsANumber = allUpper = nextUpper = 0;
goto insertChars;
default:
insertChars:
if (currentRule->charslen) {
if (!back_updatePositions(&currentRule->charsdots[0],
currentRule->dotslen, currentRule->charslen, table, pos,
input, output, posMapping, cursorPosition, cursorStatus,
&nextUpper, allUpper, allUpperPhrase))
goto failure;
pos += currentDotslen;
} else {
int srclim = pos + currentDotslen;
while (1) {
if (!putCharacter(input->chars[pos], table, displayTable, pos, mode,
input, output, posMapping, cursorPosition, cursorStatus,
&nextUpper, allUpper, allUpperPhrase))
goto failure;
if (++pos == srclim) break;
}
}
}
/* processing after replacement */
switch (currentOpcode) {
case CTO_JoinNum:
case CTO_JoinableWord:
if (!insertSpace(table, pos, input, output, spacebuf, posMapping,
cursorPosition, cursorStatus, &nextUpper, allUpper,
allUpperPhrase))
goto failure;
break;
default:
passSelectRule(table, pos, currentPass, input, &currentOpcode, &currentRule,
&passInstructions, &passIC, &patternMatch);
if (currentOpcode == CTO_Context) {
back_passDoAction(table, displayTable, &pos, mode, input, output,
posMapping, cursorPosition, cursorStatus, &nextUpper, allUpper,
allUpperPhrase, currentOpcode, currentRule, passInstructions,
passIC, patternMatch);
}
break;
}
if (((pos > 0) && checkAttr(input->chars[pos - 1], CTC_Space, 1, table) &&
(currentOpcode != CTO_JoinableWord))) {
srcword = pos;
destword = output->length;
}
if ((currentOpcode >= CTO_Always && currentOpcode <= CTO_None) ||
(currentOpcode >= CTO_Digit && currentOpcode <= CTO_LitDigit))
previousOpcode = currentOpcode;
} /* end of translation loop */
failure:
if (destword != 0 && pos < input->length &&
!checkAttr(input->chars[pos], CTC_Space, 1, table)) {
pos = srcword;
output->length = destword;
}
if (pos < input->length) {
while (checkAttr(input->chars[pos], CTC_Space, 1, table))
if (++pos == input->length) break;
}
*realInlen = pos;
return 1;
} /* translation completed */
/* Multipass translation */
static int
matchCurrentInput(
const InString *input, int pos, const widechar *passInstructions, int passIC) {
int k;
int kk = pos;
for (k = passIC + 2; k < passIC + 2 + passInstructions[passIC + 1]; k++)
if (passInstructions[k] != input->chars[kk++]) return 0;
return 1;
}
static int
back_swapTest(const TranslationTableHeader *table, const InString *input, int *pos,
const widechar *passInstructions, int passIC) {
int curLen;
int curTest;
int curSrc = *pos;
TranslationTableOffset swapRuleOffset;
TranslationTableRule *swapRule;
swapRuleOffset = (passInstructions[passIC + 1] << 16) | passInstructions[passIC + 2];
swapRule = (TranslationTableRule *)&table->ruleArea[swapRuleOffset];
for (curLen = 0; curLen < passInstructions[passIC] + 3; curLen++) {
for (curTest = 0; curTest < swapRule->charslen; curTest++) {
if (input->chars[curSrc] == swapRule->charsdots[curTest]) break;
}
if (curTest == swapRule->charslen) return 0;
curSrc++;
}
if (passInstructions[passIC + 2] == passInstructions[passIC + 3]) {
*pos = curSrc;
return 1;
}
while (curLen < passInstructions[passIC + 4]) {
for (curTest = 0; curTest < swapRule->charslen; curTest++) {
if (input->chars[curSrc] != swapRule->charsdots[curTest]) break;
}
if (curTest < swapRule->charslen)
if (curTest < swapRule->charslen) {
*pos = curSrc;
return 1;
}
curSrc++;
curLen++;
}
*pos = curSrc;
return 1;
}
static int
back_swapReplace(int start, int end, const TranslationTableHeader *table,
const InString *input, OutString *output, int *posMapping,
const widechar *passInstructions, int passIC) {
TranslationTableOffset swapRuleOffset;
TranslationTableRule *swapRule;
widechar *replacements;
int p;
int lastPos = 0;
int lastRep = 0;
swapRuleOffset = (passInstructions[passIC + 1] << 16) | passInstructions[passIC + 2];
swapRule = (TranslationTableRule *)&table->ruleArea[swapRuleOffset];
replacements = &swapRule->charsdots[swapRule->charslen];
for (p = start; p < end; p++) {
int rep;
int test;
int k;
for (test = 0; test < swapRule->charslen; test++)
if (input->chars[p] == swapRule->charsdots[test]) break;
if (test == swapRule->charslen) return p;
if (test >= lastRep) {
k = lastPos;
rep = lastRep;
} else {
k = 0;
rep = 0;
}
while (k < swapRule->dotslen) {
if (rep == test) {
int l = replacements[k] - 1;
if (output->length + l >= output->maxlength) return 0;
posMapping[p] = output->length;
memcpy(&output->chars[output->length], &replacements[k + 1],
l * CHARSIZE);
output->length += l;
lastPos = k;
lastRep = rep;
break;
}
rep++;
k += replacements[k];
}
}
return p;
}
static int
back_passDoTest(const TranslationTableHeader *table, int pos, const InString *input,
TranslationTableOpcode currentOpcode, const TranslationTableRule *currentRule,
const widechar **passInstructions, int *passIC, PassRuleMatch *match) {
int k;
int m;
int not = 0;
TranslationTableCharacterAttributes attributes;
*passInstructions = &currentRule->charsdots[currentRule->charslen];
*passIC = 0;
match->startMatch = match->endMatch = pos;
match->startReplace = -1;
if (currentOpcode == CTO_Correct)
m = 0;
else
m = 1;
while (*passIC < currentRule->dotslen) {
int itsTrue = 1;
if (pos > input->length) return 0;
switch ((*passInstructions)[*passIC]) {
case pass_first:
if (pos != 0) itsTrue = 0;
(*passIC)++;
break;
case pass_last:
if (pos != input->length) itsTrue = 0;
(*passIC)++;
break;
case pass_lookback:
pos -= (*passInstructions)[*passIC + 1];
if (pos < 0) {
pos = 0;
itsTrue = 0;
}
*passIC += 2;
break;
case pass_not:
not = !not;
(*passIC)++;
continue;
case pass_string:
case pass_dots:
itsTrue = matchCurrentInput(input, pos, *passInstructions, *passIC);
pos += (*passInstructions)[*passIC + 1];
*passIC += (*passInstructions)[*passIC + 1] + 2;
break;
case pass_startReplace:
match->startReplace = pos;
(*passIC)++;
break;
case pass_endReplace:
match->endReplace = pos;
(*passIC)++;
break;
case pass_attributes:
attributes = ((*passInstructions)[*passIC + 1] << 16) |
(*passInstructions)[*passIC + 2];
for (k = 0; k < (*passInstructions)[*passIC + 3]; k++) {
if (pos >= input->length) {
itsTrue = 0;
break;
}
if (!(back_findCharOrDots(input->chars[pos], m, table)->attributes &
attributes)) {
itsTrue = 0;
break;
}
pos++;
}
if (itsTrue) {
for (k = (*passInstructions)[*passIC + 3];
k < (*passInstructions)[*passIC + 4] && pos < input->length;
k++) {
if (!(back_findCharOrDots(input->chars[pos], m, table)->attributes &
attributes))
break;
pos++;
}
}
*passIC += 5;
break;
case pass_swap:
itsTrue = back_swapTest(table, input, &pos, *passInstructions, *passIC);
*passIC += 5;
break;
case pass_endTest: {
(*passIC)++;
match->endMatch = pos;
if (match->startReplace == -1) {
match->startReplace = match->startMatch;
match->endReplace = match->endMatch;
}
return 1;
break;
}
default:
if (_lou_handlePassVariableTest(*passInstructions, passIC, &itsTrue)) break;
return 0;
}
if ((!not&&!itsTrue) || (not&&itsTrue)) return 0;
not = 0;
}
return 1;
}
static int
copyCharacters(int from, int to, const TranslationTableHeader *table,
const DisplayTableHeader *displayTable, int mode, const InString *input,
OutString *output, int *posMapping, int *cursorPosition, int *cursorStatus,
int *nextUpper, int allUpper, int allUpperPhrase,
TranslationTableOpcode currentOpcode) {
if (currentOpcode == CTO_Context) {
while (from < to) {
if (!putCharacter(input->chars[from], table, displayTable, from, mode, input,
output, posMapping, cursorPosition, cursorStatus, nextUpper,
allUpper, allUpperPhrase))
return 0;
from++;
}
} else {
if (to > from) {
if ((output->length + to - from) > output->maxlength) return 0;
while (to > from) {
posMapping[from] = output->length;
output->chars[output->length] = input->chars[from];
output->length++;
from++;
}
}
}
return 1;
}
static int
back_passDoAction(const TranslationTableHeader *table,
const DisplayTableHeader *displayTable, int *pos, int mode, const InString *input,
OutString *output, int *posMapping, int *cursorPosition, int *cursorStatus,
int *nextUpper, int allUpper, int allUpperPhrase,
TranslationTableOpcode currentOpcode, const TranslationTableRule *currentRule,
const widechar *passInstructions, int passIC, PassRuleMatch match) {
int k;
int destStartMatch = output->length;
int destStartReplace;
int newPos = match.endReplace;
if (!copyCharacters(match.startMatch, match.startReplace, table, displayTable, mode,
input, output, posMapping, cursorPosition, cursorStatus, nextUpper,
allUpper, allUpperPhrase, currentOpcode))
return 0;
destStartReplace = output->length;
for (k = match.startReplace; k < match.endReplace; k++)
posMapping[k] = output->length;
while (passIC < currentRule->dotslen) switch (passInstructions[passIC]) {
case pass_string:
case pass_dots:
if ((output->length + passInstructions[passIC + 1]) > output->maxlength)
return 0;
memcpy(&output->chars[output->length], &passInstructions[passIC + 2],
passInstructions[passIC + 1] * sizeof(*output->chars));
output->length += passInstructions[passIC + 1];
passIC += passInstructions[passIC + 1] + 2;
break;
case pass_swap:
if (!back_swapReplace(match.startReplace, match.endReplace, table, input,
output, posMapping, passInstructions, passIC))
return 0;
passIC += 3;
break;
case pass_omit:
passIC++;
break;
case pass_copy: {
int count = destStartReplace - destStartMatch;
if (count > 0) {
memmove(&output->chars[destStartMatch], &output->chars[destStartReplace],
count * sizeof(*output->chars));
output->length -= count;
destStartReplace = destStartMatch;
}
}
if (!copyCharacters(match.startReplace, match.endReplace, table, displayTable,
mode, input, output, posMapping, cursorPosition, cursorStatus,
nextUpper, allUpper, allUpperPhrase, currentOpcode))
return 0;
newPos = match.endMatch;
passIC++;
break;
default:
if (_lou_handlePassVariableAction(passInstructions, &passIC)) break;
return 0;
}
*pos = newPos;
return 1;
}
static void
passSelectRule(const TranslationTableHeader *table, int pos, int currentPass,
const InString *input, TranslationTableOpcode *currentOpcode,
const TranslationTableRule **currentRule, const widechar **passInstructions,
int *passIC, PassRuleMatch *match) {
if (!findBackPassRule(table, pos, currentPass, input, currentOpcode, currentRule,
passInstructions, passIC, match)) {
*currentOpcode = CTO_Always;
}
}
static int
translatePass(const TranslationTableHeader *table, const DisplayTableHeader *displayTable,
int mode, int currentPass, const InString *input, OutString *output,
int *posMapping, int *realInlen, int *cursorPosition, int *cursorStatus,
const TranslationTableRule **appliedRules, int *appliedRulesCount,
int maxAppliedRules) {
int pos;
int nextUpper = 0;
int allUpper = 0;
int allUpperPhrase = 0;
pos = output->length = 0;
_lou_resetPassVariables();
while (pos < input->length) { /* the main multipass translation loop */
TranslationTableOpcode currentOpcode;
const TranslationTableRule *currentRule; /* pointer to current rule in table */
const widechar *passInstructions;
int passIC; /* Instruction counter */
PassRuleMatch patternMatch;
passSelectRule(table, pos, currentPass, input, &currentOpcode, &currentRule,
&passInstructions, &passIC, &patternMatch);
switch (currentOpcode) {
case CTO_Pass2:
case CTO_Pass3:
case CTO_Pass4:
if (appliedRules != NULL && *appliedRulesCount < maxAppliedRules)
appliedRules[(*appliedRulesCount)++] = currentRule;
if (!back_passDoAction(table, displayTable, &pos, mode, input, output,
posMapping, cursorPosition, cursorStatus, &nextUpper, allUpper,
allUpperPhrase, currentOpcode, currentRule, passInstructions,
passIC, patternMatch))
goto failure;
break;
case CTO_Always:
if ((output->length + 1) > output->maxlength) goto failure;
posMapping[pos] = output->length;
output->chars[(output->length)++] = input->chars[pos++];
break;
default:
goto failure;
}
}
failure:
if (pos < input->length) {
while (checkAttr(input->chars[pos], CTC_Space, 1, table))
if (++pos == input->length) break;
}
*realInlen = pos;
return 1;
}