Commit message: Fix yet another buffer overflow in the braille table parser

Reported by Henri Salo

Fixes #593
CVE: CVE-2018-11685
CWE: CWE-787
Vulnerability description: Liblouis 3.5.0 has a stack-based Buffer Overflow in the function compileHyphenation in compileTranslationTable.c.

Project: liblouis
File: compileTranslationTable.c

compileRule(FileInfo *nested, CharacterClass **characterClasses,
		TranslationTableCharacterAttributes *characterClassAttribute,
		short opcodeLengths[], TranslationTableOffset *newRuleOffset,
		TranslationTableRule **newRule, RuleName **ruleNames,
		TranslationTableHeader **table) {
	int lastToken = 0;
	int ok = 1;
	CharsString token;
	TranslationTableOpcode opcode;
	CharsString ruleChars;
	CharsString ruleDots;
	CharsString cells;
	CharsString scratchPad;
	CharsString emphClass;
	TranslationTableCharacterAttributes after = 0;
	TranslationTableCharacterAttributes before = 0;
	TranslationTableCharacter *c = NULL;
	widechar *patterns = NULL;
	int k, i;
	int noback, nofor;
	noback = nofor = 0;
	TranslationTableOffset tmp_offset;
doOpcode:
	if (!getToken(nested, &token, NULL, &lastToken)) return 1;	/* blank line */
	if (token.chars[0] == '#' || token.chars[0] == '<') return 1; /* comment */
	if (nested->lineNumber == 1 &&
			(eqasc2uni((unsigned char *)"ISO", token.chars, 3) ||
					eqasc2uni((unsigned char *)"UTF-8", token.chars, 5))) {
		compileHyphenation(nested, &token, &lastToken, table);
		return 1;
	}
	opcode = getOpcode(nested, &token, opcodeLengths);

	switch (opcode) { /* Carry out operations */
	case CTO_None:
		break;
	case CTO_IncludeFile: {
		CharsString includedFile;
		if (getToken(nested, &token, "include file name", &lastToken))
			if (parseChars(nested, &includedFile, &token))
				if (!includeFile(nested, &includedFile, characterClasses,
							characterClassAttribute, opcodeLengths, newRuleOffset,
							newRule, ruleNames, table))
					ok = 0;
		break;
	}
	case CTO_Locale:
		break;
	case CTO_Undefined:
		tmp_offset = (*table)->undefined;
		ok = compileBrailleIndicator(nested, "undefined character opcode", CTO_Undefined,
				&tmp_offset, &lastToken, newRuleOffset, newRule, noback, nofor, table);
		(*table)->undefined = tmp_offset;
		break;

	case CTO_Match: {
		CharsString ptn_before, ptn_after;
		TranslationTableOffset offset;
		int len, mrk;

		size_t patternsByteSize = sizeof(*patterns) * 27720;
		patterns = (widechar *)malloc(patternsByteSize);
		if (!patterns) _lou_outOfMemory();
		memset(patterns, 0xffff, patternsByteSize);

		noback = 1;
		getCharacters(nested, &ptn_before, &lastToken);
		getRuleCharsText(nested, &ruleChars, &lastToken);
		getCharacters(nested, &ptn_after, &lastToken);
		getRuleDotsPattern(nested, &ruleDots, &lastToken);

		if (!addRule(nested, opcode, &ruleChars, &ruleDots, after, before, newRuleOffset,
					newRule, noback, nofor, table))
			ok = 0;

		if (ptn_before.chars[0] == '-' && ptn_before.length == 1)
			len = _lou_pattern_compile(
					&ptn_before.chars[0], 0, &patterns[1], 13841, *table);
		else
			len = _lou_pattern_compile(
					&ptn_before.chars[0], ptn_before.length, &patterns[1], 13841, *table);
		if (!len) {
			ok = 0;
			break;
		}
		mrk = patterns[0] = len + 1;
		_lou_pattern_reverse(&patterns[1]);

		if (ptn_after.chars[0] == '-' && ptn_after.length == 1)
			len = _lou_pattern_compile(
					&ptn_after.chars[0], 0, &patterns[mrk], 13841, *table);
		else
			len = _lou_pattern_compile(
					&ptn_after.chars[0], ptn_after.length, &patterns[mrk], 13841, *table);
		if (!len) {
			ok = 0;
			break;
		}
		len += mrk;

		if (!allocateSpaceInTable(nested, &offset, len * sizeof(widechar), table)) {
			ok = 0;
			break;
		}

		/* realloc may have moved table, so make sure newRule is still valid */
		*newRule = (TranslationTableRule *)&(*table)->ruleArea[*newRuleOffset];

		memcpy(&(*table)->ruleArea[offset], patterns, len * sizeof(widechar));
		(*newRule)->patterns = offset;

		break;
	}

	case CTO_BackMatch: {
		CharsString ptn_before, ptn_after;
		TranslationTableOffset offset;
		int len, mrk;

		size_t patternsByteSize = sizeof(*patterns) * 27720;
		patterns = (widechar *)malloc(patternsByteSize);
		if (!patterns) _lou_outOfMemory();
		memset(patterns, 0xffff, patternsByteSize);

		nofor = 1;
		getCharacters(nested, &ptn_before, &lastToken);
		getRuleCharsText(nested, &ruleChars, &lastToken);
		getCharacters(nested, &ptn_after, &lastToken);
		getRuleDotsPattern(nested, &ruleDots, &lastToken);

		if (!addRule(nested, opcode, &ruleChars, &ruleDots, 0, 0, newRuleOffset, newRule,
					noback, nofor, table))
			ok = 0;

		if (ptn_before.chars[0] == '-' && ptn_before.length == 1)
			len = _lou_pattern_compile(
					&ptn_before.chars[0], 0, &patterns[1], 13841, *table);
		else
			len = _lou_pattern_compile(
					&ptn_before.chars[0], ptn_before.length, &patterns[1], 13841, *table);
		if (!len) {
			ok = 0;
			break;
		}
		mrk = patterns[0] = len + 1;
		_lou_pattern_reverse(&patterns[1]);

		if (ptn_after.chars[0] == '-' && ptn_after.length == 1)
			len = _lou_pattern_compile(
					&ptn_after.chars[0], 0, &patterns[mrk], 13841, *table);
		else
			len = _lou_pattern_compile(
					&ptn_after.chars[0], ptn_after.length, &patterns[mrk], 13841, *table);
		if (!len) {
			ok = 0;
			break;
		}
		len += mrk;

		if (!allocateSpaceInTable(nested, &offset, len * sizeof(widechar), table)) {
			ok = 0;
			break;
		}

		/* realloc may have moved table, so make sure newRule is still valid */
		*newRule = (TranslationTableRule *)&(*table)->ruleArea[*newRuleOffset];

		memcpy(&(*table)->ruleArea[offset], patterns, len * sizeof(widechar));
		(*newRule)->patterns = offset;

		break;
	}

	case CTO_BegCapsPhrase:
		tmp_offset = (*table)->emphRules[capsRule][begPhraseOffset];
		ok = compileBrailleIndicator(nested, "first word capital sign",
				CTO_BegCapsPhraseRule, &tmp_offset, &lastToken, newRuleOffset, newRule,
				noback, nofor, table);
		(*table)->emphRules[capsRule][begPhraseOffset] = tmp_offset;
		break;
	case CTO_EndCapsPhrase:
		switch (compileBeforeAfter(nested, &lastToken)) {
		case 1:  // before
			if ((*table)->emphRules[capsRule][endPhraseAfterOffset]) {
				compileError(nested, "Capital sign after last word already defined.");
				ok = 0;
				break;
			}
			tmp_offset = (*table)->emphRules[capsRule][endPhraseBeforeOffset];
			ok = compileBrailleIndicator(nested, "capital sign before last word",
					CTO_EndCapsPhraseBeforeRule, &tmp_offset, &lastToken, newRuleOffset,
					newRule, noback, nofor, table);
			(*table)->emphRules[capsRule][endPhraseBeforeOffset] = tmp_offset;
			break;
		case 2:  // after
			if ((*table)->emphRules[capsRule][endPhraseBeforeOffset]) {
				compileError(nested, "Capital sign before last word already defined.");
				ok = 0;
				break;
			}
			tmp_offset = (*table)->emphRules[capsRule][endPhraseAfterOffset];
			ok = compileBrailleIndicator(nested, "capital sign after last word",
					CTO_EndCapsPhraseAfterRule, &tmp_offset, &lastToken, newRuleOffset,
					newRule, noback, nofor, table);
			(*table)->emphRules[capsRule][endPhraseAfterOffset] = tmp_offset;
			break;
		default:  // error
			compileError(nested, "Invalid lastword indicator location.");
			ok = 0;
			break;
		}
		break;
	case CTO_BegCaps:
		tmp_offset = (*table)->emphRules[capsRule][begOffset];
		ok = compileBrailleIndicator(nested, "first letter capital sign", CTO_BegCapsRule,
				&tmp_offset, &lastToken, newRuleOffset, newRule, noback, nofor, table);
		(*table)->emphRules[capsRule][begOffset] = tmp_offset;
		break;
	case CTO_EndCaps:
		tmp_offset = (*table)->emphRules[capsRule][endOffset];
		ok = compileBrailleIndicator(nested, "last letter capital sign", CTO_EndCapsRule,
				&tmp_offset, &lastToken, newRuleOffset, newRule, noback, nofor, table);
		(*table)->emphRules[capsRule][endOffset] = tmp_offset;
		break;
	case CTO_CapsLetter:
		tmp_offset = (*table)->emphRules[capsRule][letterOffset];
		ok = compileBrailleIndicator(nested, "single letter capital sign",
				CTO_CapsLetterRule, &tmp_offset, &lastToken, newRuleOffset, newRule,
				noback, nofor, table);
		(*table)->emphRules[capsRule][letterOffset] = tmp_offset;
		break;
	case CTO_BegCapsWord:
		tmp_offset = (*table)->emphRules[capsRule][begWordOffset];
		ok = compileBrailleIndicator(nested, "capital word", CTO_BegCapsWordRule,
				&tmp_offset, &lastToken, newRuleOffset, newRule, noback, nofor, table);
		(*table)->emphRules[capsRule][begWordOffset] = tmp_offset;
		break;
	case CTO_EndCapsWord:
		tmp_offset = (*table)->emphRules[capsRule][endWordOffset];
		ok = compileBrailleIndicator(nested, "capital word stop", CTO_EndCapsWordRule,
				&tmp_offset, &lastToken, newRuleOffset, newRule, noback, nofor, table);
		(*table)->emphRules[capsRule][endWordOffset] = tmp_offset;
		break;
	case CTO_LenCapsPhrase:
		ok = (*table)->emphRules[capsRule][lenPhraseOffset] =
				compileNumber(nested, &lastToken);
		break;

	/* these 9 general purpose emphasis opcodes are compiled further down to more specific
	 * internal opcodes:
	 * - emphletter
	 * - begemphword
	 * - endemphword
	 * - begemph
	 * - endemph
	 * - begemphphrase
	 * - endemphphrase
	 * - lenemphphrase
	 */
	case CTO_EmphClass:
		if (getToken(nested, &token, "emphasis class", &lastToken))
			if (parseChars(nested, &emphClass, &token)) {
				char *s = malloc(sizeof(char) * (emphClass.length + 1));
				for (k = 0; k < emphClass.length; k++) s[k] = (char)emphClass.chars[k];
				s[k++] = '\0';
				for (i = 0; (*table)->emphClasses[i]; i++)
					if (strcmp(s, (*table)->emphClasses[i]) == 0) {
						_lou_logMessage(LOG_WARN, "Duplicate emphasis class: %s", s);
						warningCount++;
						free(s);
						return 1;
					}
				if (i < MAX_EMPH_CLASSES) {
					switch (i) {
					/* For backwards compatibility (i.e. because programs will assume the
					 * first 3
					 * typeform bits are `italic', `underline' and `bold') we require that
					 * the first
					 * 3 emphclass definitions are (in that order):
					 *
					 *   emphclass italic
					 *   emphclass underline
					 *   emphclass bold
					 *
					 * While it would be possible to use the emphclass opcode only for
					 * defining
					 * _additional_ classes (not allowing for them to be called italic,
					 * underline or
					 * bold), thereby reducing the amount of boilerplate, we deliberately
					 * choose not
					 * to do that in order to not give italic, underline and bold any
					 * special
					 * status. The hope is that eventually all programs will use liblouis
					 * for
					 * emphasis the recommended way (i.e. by looking up the supported
					 * typeforms in
					 * the documentation or API) so that we can drop this restriction.
					 */
					case 0:
						if (strcmp(s, "italic") != 0) {
							_lou_logMessage(LOG_ERROR,
									"First emphasis class must be \"italic\" but got %s",
									s);
							errorCount++;
							free(s);
							return 0;
						}
						break;
					case 1:
						if (strcmp(s, "underline") != 0) {
							_lou_logMessage(LOG_ERROR,
									"Second emphasis class must be \"underline\" but got "
									"%s",
									s);
							errorCount++;
							free(s);
							return 0;
						}
						break;
					case 2:
						if (strcmp(s, "bold") != 0) {
							_lou_logMessage(LOG_ERROR,
									"Third emphasis class must be \"bold\" but got %s",
									s);
							errorCount++;
							free(s);
							return 0;
						}
						break;
					}
					(*table)->emphClasses[i] = s;
					(*table)->emphClasses[i + 1] = NULL;
					ok = 1;
					break;
				} else {
					_lou_logMessage(LOG_ERROR,
							"Max number of emphasis classes (%i) reached",
							MAX_EMPH_CLASSES);
					errorCount++;
					free(s);
					ok = 0;
					break;
				}
			}
		compileError(nested, "emphclass must be followed by a valid class name.");
		ok = 0;
		break;
	case CTO_EmphLetter:
	case CTO_BegEmphWord:
	case CTO_EndEmphWord:
	case CTO_BegEmph:
	case CTO_EndEmph:
	case CTO_BegEmphPhrase:
	case CTO_EndEmphPhrase:
	case CTO_LenEmphPhrase:
		ok = 0;
		if (getToken(nested, &token, "emphasis class", &lastToken))
			if (parseChars(nested, &emphClass, &token)) {
				char *s = malloc(sizeof(char) * (emphClass.length + 1));
				for (k = 0; k < emphClass.length; k++) s[k] = (char)emphClass.chars[k];
				s[k++] = '\0';
				for (i = 0; (*table)->emphClasses[i]; i++)
					if (strcmp(s, (*table)->emphClasses[i]) == 0) break;
				if (!(*table)->emphClasses[i]) {
					_lou_logMessage(LOG_ERROR, "Emphasis class %s not declared", s);
					errorCount++;
					free(s);
					break;
				}
				i++;  // in table->emphRules the first index is used for caps
				if (opcode == CTO_EmphLetter) {
					tmp_offset = (*table)->emphRules[i][letterOffset];
					ok = compileBrailleIndicator(nested, "single letter",
							CTO_Emph1LetterRule + letterOffset + (8 * i), &tmp_offset,
							&lastToken, newRuleOffset, newRule, noback, nofor, table);
					(*table)->emphRules[i][letterOffset] = tmp_offset;
				} else if (opcode == CTO_BegEmphWord) {
					tmp_offset = (*table)->emphRules[i][begWordOffset];
					ok = compileBrailleIndicator(nested, "word",
							CTO_Emph1LetterRule + begWordOffset + (8 * i), &tmp_offset,
							&lastToken, newRuleOffset, newRule, noback, nofor, table);
					(*table)->emphRules[i][begWordOffset] = tmp_offset;
				} else if (opcode == CTO_EndEmphWord) {
					tmp_offset = (*table)->emphRules[i][endWordOffset];
					ok = compileBrailleIndicator(nested, "word stop",
							CTO_Emph1LetterRule + endWordOffset + (8 * i), &tmp_offset,
							&lastToken, newRuleOffset, newRule, noback, nofor, table);
					(*table)->emphRules[i][endWordOffset] = tmp_offset;
				} else if (opcode == CTO_BegEmph) {
					/* fail if both begemph and any of begemphphrase or begemphword are
					 * defined */
					if ((*table)->emphRules[i][begWordOffset] ||
							(*table)->emphRules[i][begPhraseOffset]) {
						compileError(nested,
								"Cannot define emphasis for both no context and word or "
								"phrase context, i.e. cannot have both begemph and "
								"begemphword or begemphphrase.");
						ok = 0;
						break;
					}
					tmp_offset = (*table)->emphRules[i][begOffset];
					ok = compileBrailleIndicator(nested, "first letter",
							CTO_Emph1LetterRule + begOffset + (8 * i), &tmp_offset,
							&lastToken, newRuleOffset, newRule, noback, nofor, table);
					(*table)->emphRules[i][begOffset] = tmp_offset;
				} else if (opcode == CTO_EndEmph) {
					if ((*table)->emphRules[i][endWordOffset] ||
							(*table)->emphRules[i][endPhraseBeforeOffset] ||
							(*table)->emphRules[i][endPhraseAfterOffset]) {
						compileError(nested,
								"Cannot define emphasis for both no context and word or "
								"phrase context, i.e. cannot have both endemph and "
								"endemphword or endemphphrase.");
						ok = 0;
						break;
					}
					tmp_offset = (*table)->emphRules[i][endOffset];
					ok = compileBrailleIndicator(nested, "last letter",
							CTO_Emph1LetterRule + endOffset + (8 * i), &tmp_offset,
							&lastToken, newRuleOffset, newRule, noback, nofor, table);
					(*table)->emphRules[i][endOffset] = tmp_offset;
				} else if (opcode == CTO_BegEmphPhrase) {
					tmp_offset = (*table)->emphRules[i][begPhraseOffset];
					ok = compileBrailleIndicator(nested, "first word",
							CTO_Emph1LetterRule + begPhraseOffset + (8 * i), &tmp_offset,
							&lastToken, newRuleOffset, newRule, noback, nofor, table);
					(*table)->emphRules[i][begPhraseOffset] = tmp_offset;
				} else if (opcode == CTO_EndEmphPhrase)
					switch (compileBeforeAfter(nested, &lastToken)) {
					case 1:  // before
						if ((*table)->emphRules[i][endPhraseAfterOffset]) {
							compileError(nested, "last word after already defined.");
							ok = 0;
							break;
						}
						tmp_offset = (*table)->emphRules[i][endPhraseBeforeOffset];
						ok = compileBrailleIndicator(nested, "last word before",
								CTO_Emph1LetterRule + endPhraseBeforeOffset + (8 * i),
								&tmp_offset, &lastToken, newRuleOffset, newRule, noback,
								nofor, table);
						(*table)->emphRules[i][endPhraseBeforeOffset] = tmp_offset;
						break;
					case 2:  // after
						if ((*table)->emphRules[i][endPhraseBeforeOffset]) {
							compileError(nested, "last word before already defined.");
							ok = 0;
							break;
						}
						tmp_offset = (*table)->emphRules[i][endPhraseAfterOffset];
						ok = compileBrailleIndicator(nested, "last word after",
								CTO_Emph1LetterRule + endPhraseAfterOffset + (8 * i),
								&tmp_offset, &lastToken, newRuleOffset, newRule, noback,
								nofor, table);
						(*table)->emphRules[i][endPhraseAfterOffset] = tmp_offset;
						break;
					default:  // error
						compileError(nested, "Invalid lastword indicator location.");
						ok = 0;
						break;
					}
				else if (opcode == CTO_LenEmphPhrase)
					ok = (*table)->emphRules[i][lenPhraseOffset] =
							compileNumber(nested, &lastToken);
				free(s);
			}
		break;

	case CTO_LetterSign:
		tmp_offset = (*table)->letterSign;
		ok = compileBrailleIndicator(nested, "letter sign", CTO_LetterRule, &tmp_offset,
				&lastToken, newRuleOffset, newRule, noback, nofor, table);
		(*table)->letterSign = tmp_offset;
		break;
	case CTO_NoLetsignBefore:
		if (getRuleCharsText(nested, &ruleChars, &lastToken)) {
			if (((*table)->noLetsignBeforeCount + ruleChars.length) > LETSIGNSIZE) {
				compileError(nested, "More than %d characters", LETSIGNSIZE);
				ok = 0;
				break;
			}
			for (k = 0; k < ruleChars.length; k++)
				(*table)->noLetsignBefore[(*table)->noLetsignBeforeCount++] =
						ruleChars.chars[k];
		}
		break;
	case CTO_NoLetsign:
		if (getRuleCharsText(nested, &ruleChars, &lastToken)) {
			if (((*table)->noLetsignCount + ruleChars.length) > LETSIGNSIZE) {
				compileError(nested, "More than %d characters", LETSIGNSIZE);
				ok = 0;
				break;
			}
			for (k = 0; k < ruleChars.length; k++)
				(*table)->noLetsign[(*table)->noLetsignCount++] = ruleChars.chars[k];
		}
		break;
	case CTO_NoLetsignAfter:
		if (getRuleCharsText(nested, &ruleChars, &lastToken)) {
			if (((*table)->noLetsignAfterCount + ruleChars.length) > LETSIGNSIZE) {
				compileError(nested, "More than %d characters", LETSIGNSIZE);
				ok = 0;
				break;
			}
			for (k = 0; k < ruleChars.length; k++)
				(*table)->noLetsignAfter[(*table)->noLetsignAfterCount++] =
						ruleChars.chars[k];
		}
		break;
	case CTO_NumberSign:
		tmp_offset = (*table)->numberSign;
		ok = compileBrailleIndicator(nested, "number sign", CTO_NumberRule, &tmp_offset,
				&lastToken, newRuleOffset, newRule, noback, nofor, table);
		(*table)->numberSign = tmp_offset;
		break;

	case CTO_Attribute:

		c = NULL;
		ok = 1;
		if (!getToken(nested, &ruleChars, "attribute number", &lastToken)) {
			compileError(nested, "Expected attribute number.");
			ok = 0;
			break;
		}

		k = -1;
		switch (ruleChars.chars[0]) {
		case '0':
			k = 0;
			break;
		case '1':
			k = 1;
			break;
		case '2':
			k = 2;
			break;
		case '3':
			k = 3;
			break;
		case '4':
			k = 4;
			break;
		case '5':
			k = 5;
			break;
		case '6':
			k = 6;
			break;
		case '7':
			k = 7;
			break;
		}
		if (k == -1) {
			compileError(nested, "Invalid attribute number.");
			ok = 0;
			break;
		}

		if (getRuleCharsText(nested, &ruleChars, &lastToken)) {
			for (i = 0; i < ruleChars.length; i++) {
				c = compile_findCharOrDots(ruleChars.chars[i], 0, *table);
				if (c)
					c->attributes |= (CTC_UserDefined0 << k);
				else {
					compileError(nested, "Attribute character undefined");
					ok = 0;
					break;
				}
			}
		}
		break;

	case CTO_NumericModeChars:

		c = NULL;
		ok = 1;
		if (getRuleCharsText(nested, &ruleChars, &lastToken)) {
			for (k = 0; k < ruleChars.length; k++) {
				c = compile_findCharOrDots(ruleChars.chars[k], 0, *table);
				if (c)
					c->attributes |= CTC_NumericMode;
				else {
					compileError(nested, "Numeric mode character undefined");
					ok = 0;
					break;
				}
			}
			(*table)->usesNumericMode = 1;
		}
		break;

	case CTO_NumericNoContractChars:

		c = NULL;
		ok = 1;
		if (getRuleCharsText(nested, &ruleChars, &lastToken)) {
			for (k = 0; k < ruleChars.length; k++) {
				c = compile_findCharOrDots(ruleChars.chars[k], 0, *table);
				if (c)
					c->attributes |= CTC_NumericNoContract;
				else {
					compileError(nested, "Numeric no contraction character undefined");
					ok = 0;
					break;
				}
			}
			(*table)->usesNumericMode = 1;
		}
		break;

	case CTO_NoContractSign:

		tmp_offset = (*table)->noContractSign;
		ok = compileBrailleIndicator(nested, "no contractions sign", CTO_NoContractRule,
				&tmp_offset, &lastToken, newRuleOffset, newRule, noback, nofor, table);
		(*table)->noContractSign = tmp_offset;
		break;

	case CTO_SeqDelimiter:

		c = NULL;
		ok = 1;
		if (getRuleCharsText(nested, &ruleChars, &lastToken)) {
			for (k = 0; k < ruleChars.length; k++) {
				c = compile_findCharOrDots(ruleChars.chars[k], 0, *table);
				if (c)
					c->attributes |= CTC_SeqDelimiter;
				else {
					compileError(nested, "Sequence delimiter character undefined");
					ok = 0;
					break;
				}
			}
			(*table)->usesSequences = 1;
		}
		break;

	case CTO_SeqBeforeChars:

		c = NULL;
		ok = 1;
		if (getRuleCharsText(nested, &ruleChars, &lastToken)) {
			for (k = 0; k < ruleChars.length; k++) {
				c = compile_findCharOrDots(ruleChars.chars[k], 0, *table);
				if (c)
					c->attributes |= CTC_SeqBefore;
				else {
					compileError(nested, "Sequence before character undefined");
					ok = 0;
					break;
				}
			}
		}
		break;

	case CTO_SeqAfterChars:

		c = NULL;
		ok = 1;
		if (getRuleCharsText(nested, &ruleChars, &lastToken)) {
			for (k = 0; k < ruleChars.length; k++) {
				c = compile_findCharOrDots(ruleChars.chars[k], 0, *table);
				if (c)
					c->attributes |= CTC_SeqAfter;
				else {
					compileError(nested, "Sequence after character undefined");
					ok = 0;
					break;
				}
			}
		}
		break;

	case CTO_SeqAfterPattern:

		if (getRuleCharsText(nested, &ruleChars, &lastToken)) {
			if (((*table)->seqPatternsCount + ruleChars.length + 1) > SEQPATTERNSIZE) {
				compileError(nested, "More than %d characters", SEQPATTERNSIZE);
				ok = 0;
				break;
			}
			for (k = 0; k < ruleChars.length; k++)
				(*table)->seqPatterns[(*table)->seqPatternsCount++] = ruleChars.chars[k];
			(*table)->seqPatterns[(*table)->seqPatternsCount++] = 0;
		}
		break;
	case CTO_SeqAfterExpression:

		if (getRuleCharsText(nested, &ruleChars, &lastToken)) {
			for ((*table)->seqAfterExpressionLength = 0;
					(*table)->seqAfterExpressionLength < ruleChars.length;
					(*table)->seqAfterExpressionLength++)
				(*table)->seqAfterExpression[(*table)->seqAfterExpressionLength] =
						ruleChars.chars[(*table)->seqAfterExpressionLength];
			(*table)->seqAfterExpression[(*table)->seqAfterExpressionLength] = 0;
		}
		break;

	case CTO_CapsModeChars:

		c = NULL;
		ok = 1;
		if (getRuleCharsText(nested, &ruleChars, &lastToken)) {
			for (k = 0; k < ruleChars.length; k++) {
				c = compile_findCharOrDots(ruleChars.chars[k], 0, *table);
				if (c)
					c->attributes |= CTC_CapsMode;
				else {
					compileError(nested, "Capital mode character undefined");
					ok = 0;
					break;
				}
			}
		}
		break;

	case CTO_BegComp:
		tmp_offset = (*table)->begComp;
		ok = compileBrailleIndicator(nested, "begin computer braille", CTO_BegCompRule,
				&tmp_offset, &lastToken, newRuleOffset, newRule, noback, nofor, table);
		(*table)->begComp = tmp_offset;
		break;
	case CTO_EndComp:
		tmp_offset = (*table)->endComp;
		ok = compileBrailleIndicator(nested, "end computer braslle", CTO_EndCompRule,
				&tmp_offset, &lastToken, newRuleOffset, newRule, noback, nofor, table);
		(*table)->endComp = tmp_offset;
		break;
	case CTO_Syllable:
		(*table)->syllables = 1;
	case CTO_Always:
	case CTO_NoCross:
	case CTO_LargeSign:
	case CTO_WholeWord:
	case CTO_PartWord:
	case CTO_JoinNum:
	case CTO_JoinableWord:
	case CTO_LowWord:
	case CTO_SuffixableWord:
	case CTO_PrefixableWord:
	case CTO_BegWord:
	case CTO_BegMidWord:
	case CTO_MidWord:
	case CTO_MidEndWord:
	case CTO_EndWord:
	case CTO_PrePunc:
	case CTO_PostPunc:
	case CTO_BegNum:
	case CTO_MidNum:
	case CTO_EndNum:
	case CTO_Repeated:
	case CTO_RepWord:
		if (getRuleCharsText(nested, &ruleChars, &lastToken))
			if (getRuleDotsPattern(nested, &ruleDots, &lastToken))
				if (!addRule(nested, opcode, &ruleChars, &ruleDots, after, before,
							newRuleOffset, newRule, noback, nofor, table))
					ok = 0;
		// if (opcode == CTO_MidNum)
		// {
		//   TranslationTableCharacter *c = compile_findCharOrDots(ruleChars.chars[0], 0);
		//   if(c)
		//     c->attributes |= CTC_NumericMode;
		// }
		break;
	case CTO_CompDots:
	case CTO_Comp6:
		if (!getRuleCharsText(nested, &ruleChars, &lastToken)) return 0;
		if (ruleChars.length != 1 || ruleChars.chars[0] > 255) {
			compileError(nested, "first operand must be 1 character and < 256");
			return 0;
		}
		if (!getRuleDotsPattern(nested, &ruleDots, &lastToken)) return 0;
		if (!addRule(nested, opcode, &ruleChars, &ruleDots, after, before, newRuleOffset,
					newRule, noback, nofor, table))
			ok = 0;
		(*table)->compdotsPattern[ruleChars.chars[0]] = *newRuleOffset;
		break;
	case CTO_ExactDots:
		if (!getRuleCharsText(nested, &ruleChars, &lastToken)) return 0;
		if (ruleChars.chars[0] != '@') {
			compileError(nested, "The operand must begin with an at sign (@)");
			return 0;
		}
		for (k = 1; k < ruleChars.length; k++)
			scratchPad.chars[k - 1] = ruleChars.chars[k];
		scratchPad.length = ruleChars.length - 1;
		if (!parseDots(nested, &ruleDots, &scratchPad)) return 0;
		if (!addRule(nested, opcode, &ruleChars, &ruleDots, before, after, newRuleOffset,
					newRule, noback, nofor, table))
			ok = 0;
		break;
	case CTO_CapsNoCont:
		ruleChars.length = 1;
		ruleChars.chars[0] = 'a';
		if (!addRule(nested, CTO_CapsNoContRule, &ruleChars, NULL, after, before,
					newRuleOffset, newRule, noback, nofor, table))
			ok = 0;
		(*table)->capsNoCont = *newRuleOffset;
		break;
	case CTO_Replace:
		if (getRuleCharsText(nested, &ruleChars, &lastToken)) {
			if (lastToken)
				ruleDots.length = ruleDots.chars[0] = 0;
			else {
				getRuleDotsText(nested, &ruleDots, &lastToken);
				if (ruleDots.chars[0] == '#')
					ruleDots.length = ruleDots.chars[0] = 0;
				else if (ruleDots.chars[0] == '\\' && ruleDots.chars[1] == '#')
					memcpy(&ruleDots.chars[0], &ruleDots.chars[1],
							ruleDots.length-- * CHARSIZE);
			}
		}
		for (k = 0; k < ruleChars.length; k++)
			addCharOrDots(nested, ruleChars.chars[k], 0, table);
		for (k = 0; k < ruleDots.length; k++)
			addCharOrDots(nested, ruleDots.chars[k], 0, table);
		if (!addRule(nested, opcode, &ruleChars, &ruleDots, after, before, newRuleOffset,
					newRule, noback, nofor, table))
			ok = 0;
		break;
	case CTO_Correct:
		(*table)->corrections = 1;
		goto doPass;
	case CTO_Pass2:
		if ((*table)->numPasses < 2) (*table)->numPasses = 2;
		goto doPass;
	case CTO_Pass3:
		if ((*table)->numPasses < 3) (*table)->numPasses = 3;
		goto doPass;
	case CTO_Pass4:
		if ((*table)->numPasses < 4) (*table)->numPasses = 4;
	doPass:
	case CTO_Context:
		if (!(nofor || noback)) {
			compileError(nested, "%s or %s must be specified.",
					_lou_findOpcodeName(CTO_NoFor), _lou_findOpcodeName(CTO_NoBack));
			ok = 0;
			break;
		}
		if (!compilePassOpcode(nested, opcode, *characterClasses, newRuleOffset, newRule,
					noback, nofor, *ruleNames, table))
			ok = 0;
		break;
	case CTO_Contraction:
	case CTO_NoCont:
	case CTO_CompBrl:
	case CTO_Literal:
		if (getRuleCharsText(nested, &ruleChars, &lastToken))
			if (!addRule(nested, opcode, &ruleChars, NULL, after, before, newRuleOffset,
						newRule, noback, nofor, table))
				ok = 0;
		break;
	case CTO_MultInd: {
		int t;
		ruleChars.length = 0;
		if (getToken(nested, &token, "multiple braille indicators", &lastToken) &&
				parseDots(nested, &cells, &token)) {
			while ((t = getToken(nested, &token, "multind opcodes", &lastToken))) {
				opcode = getOpcode(nested, &token, opcodeLengths);
				if (opcode >= CTO_CapsLetter && opcode < CTO_MultInd)
					ruleChars.chars[ruleChars.length++] = (widechar)opcode;
				else {
					compileError(nested, "Not a braille indicator opcode.");
					ok = 0;
				}
				if (t == 2) break;
			}
		} else
			ok = 0;
		if (!addRule(nested, CTO_MultInd, &ruleChars, &cells, after, before,
					newRuleOffset, newRule, noback, nofor, table))
			ok = 0;
		break;
	}

	case CTO_Class: {
		CharsString characters;
		const CharacterClass *class;
		if (!*characterClasses) {
			if (!allocateCharacterClasses(characterClasses, characterClassAttribute))
				ok = 0;
		}
		if (getToken(nested, &token, "character class name", &lastToken)) {
			class = findCharacterClass(&token, *characterClasses);
			if (!class)
				// no class with that name: create one
				class = addCharacterClass(nested, &token.chars[0], token.length,
						characterClasses, characterClassAttribute);
			if (class) {
				// there is a class with that name or a new class was successfully created
				if (getCharacters(nested, &characters, &lastToken)) {
					int index;
					for (index = 0; index < characters.length; ++index) {
						TranslationTableRule *defRule;
						TranslationTableCharacter *character = definedCharOrDots(
								nested, characters.chars[index], 0, *table);
						character->attributes |= class->attribute;
						defRule = (TranslationTableRule *)&(
								*table)->ruleArea[character->definitionRule];
						if (defRule->dotslen == 1) {
							character = definedCharOrDots(nested,
									defRule->charsdots[defRule->charslen], 1, *table);
							character->attributes |= class->attribute;
						}
					}
				}
			}
		}
		break;
	}

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

			if (!*characterClasses) {
				if (!allocateCharacterClasses(characterClasses, characterClassAttribute))
					ok = 0;
			}
			if (getCharacterClass(nested, &class, *characterClasses, &lastToken)) {
				*attributes |= class->attribute;
				goto doOpcode;
			}
			break;
		}

	case CTO_NoBack:
		if (nofor) {
			compileError(nested, "%s already specified.", _lou_findOpcodeName(CTO_NoFor));
			ok = 0;
			break;
		}
		noback = 1;
		goto doOpcode;
	case CTO_NoFor:
		if (noback) {
			compileError(
					nested, "%s already specified.", _lou_findOpcodeName(CTO_NoBack));
			ok = 0;
			break;
		}
		nofor = 1;
		goto doOpcode;

	case CTO_EmpMatchBefore:
		before |= CTC_EmpMatch;
		goto doOpcode;
	case CTO_EmpMatchAfter:
		after |= CTC_EmpMatch;
		goto doOpcode;

	case CTO_SwapCc:
	case CTO_SwapCd:
	case CTO_SwapDd:
		if (!compileSwap(nested, opcode, &lastToken, newRuleOffset, newRule, noback,
					nofor, ruleNames, table))
			ok = 0;
		break;
	case CTO_Hyphen:
	case CTO_DecPoint:
		//	case CTO_Apostrophe:
		//	case CTO_Initial:
		if (getRuleCharsText(nested, &ruleChars, &lastToken))
			if (getRuleDotsPattern(nested, &ruleDots, &lastToken)) {
				if (ruleChars.length != 1 || ruleDots.length < 1) {
					compileError(nested,
							"One Unicode character and at least one cell are required.");
					ok = 0;
				}
				if (!addRule(nested, opcode, &ruleChars, &ruleDots, after, before,
							newRuleOffset, newRule, noback, nofor, table))
					ok = 0;
				// if (opcode == CTO_DecPoint)
				// {
				//   TranslationTableCharacter *c =
				//   compile_findCharOrDots(ruleChars.chars[0], 0);
				//   if(c)
				//     c->attributes |= CTC_NumericMode;
				// }
			}
		break;
	case CTO_Space:
		compileCharDef(nested, opcode, CTC_Space, &lastToken, newRuleOffset, newRule,
				noback, nofor, table);
		break;
	case CTO_Digit:
		compileCharDef(nested, opcode, CTC_Digit, &lastToken, newRuleOffset, newRule,
				noback, nofor, table);
		break;
	case CTO_LitDigit:
		compileCharDef(nested, opcode, CTC_LitDigit, &lastToken, newRuleOffset, newRule,
				noback, nofor, table);
		break;
	case CTO_Punctuation:
		compileCharDef(nested, opcode, CTC_Punctuation, &lastToken, newRuleOffset,
				newRule, noback, nofor, table);
		break;
	case CTO_Math:
		compileCharDef(nested, opcode, CTC_Math, &lastToken, newRuleOffset, newRule,
				noback, nofor, table);
		break;
	case CTO_Sign:
		compileCharDef(nested, opcode, CTC_Sign, &lastToken, newRuleOffset, newRule,
				noback, nofor, table);
		break;
	case CTO_Letter:
		compileCharDef(nested, opcode, CTC_Letter, &lastToken, newRuleOffset, newRule,
				noback, nofor, table);
		break;
	case CTO_UpperCase:
		compileCharDef(nested, opcode, CTC_UpperCase, &lastToken, newRuleOffset, newRule,
				noback, nofor, table);
		break;
	case CTO_LowerCase:
		compileCharDef(nested, opcode, CTC_LowerCase, &lastToken, newRuleOffset, newRule,
				noback, nofor, table);
		break;
	case CTO_Grouping:
		ok = compileGrouping(nested, &lastToken, newRuleOffset, newRule, noback, nofor,
				ruleNames, table);
		break;
	case CTO_UpLow:
		ok = compileUplow(
				nested, &lastToken, newRuleOffset, newRule, noback, nofor, table);
		break;
	case CTO_Display:
		if (getRuleCharsText(nested, &ruleChars, &lastToken))
			if (getRuleDotsPattern(nested, &ruleDots, &lastToken)) {
				if (ruleChars.length != 1 || ruleDots.length != 1) {
					compileError(
							nested, "Exactly one character and one cell are required.");
					ok = 0;
				}
				putCharAndDots(nested, ruleChars.chars[0], ruleDots.chars[0], table);
			}
		break;
	default:
		compileError(nested, "unimplemented opcode.");
		ok = 0;
		break;
	}

	if (patterns != NULL) free(patterns);

	return ok;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

_lou_defaultTableResolver(const char *tableList, const char *base) {
	char *searchPath;
	char **tableFiles;
	char *subTable;
	char *tableList_copy;
	char *cp;
	int last;
	int k;

	/* Set up search path */
	searchPath = _lou_getTablePath();

	/* Count number of subtables in table list */
	k = 0;
	for (cp = (char *)tableList; *cp != '\0'; cp++)
		if (*cp == ',') k++;
	tableFiles = (char **)malloc((k + 2) * sizeof(char *));

	/* Resolve subtables */
	k = 0;
	tableList_copy = strdup(tableList);
	for (subTable = tableList_copy;; subTable = cp + 1) {
		for (cp = subTable; *cp != '\0' && *cp != ','; cp++)
			;
		last = (*cp == '\0');
		*cp = '\0';
		if (!(tableFiles[k++] = resolveSubtable(subTable, base, searchPath))) {
			char *path;
			_lou_logMessage(LOG_ERROR, "Cannot resolve table '%s'", subTable);
			path = getenv("LOUIS_TABLEPATH");
			if (path != NULL && path[0] != '\0')
				_lou_logMessage(LOG_ERROR, "LOUIS_TABLEPATH=%s", path);
			free(searchPath);
			free(tableList_copy);
			free(tableFiles);
			return NULL;
		}
		if (k == 1) base = subTable;
		if (last) break;
	}
	free(searchPath);
	free(tableList_copy);
	tableFiles[k] = NULL;
	return tableFiles;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

getPartName(int actionPart) {
	return actionPart ? "action" : "test";
}

Project: liblouis
File: compileTranslationTable.c

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

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

compileFile(const char *fileName, CharacterClass **characterClasses,
		TranslationTableCharacterAttributes *characterClassAttribute,
		short opcodeLengths[], TranslationTableOffset *newRuleOffset,
		TranslationTableRule **newRule, RuleName **ruleNames,
		TranslationTableHeader **table) {
	FileInfo nested;
	fileCount++;
	nested.fileName = fileName;
	nested.encoding = noEncoding;
	nested.status = 0;
	nested.lineNumber = 0;
	if ((nested.in = fopen(nested.fileName, "rb"))) {
		while (_lou_getALine(&nested))
			compileRule(&nested, characterClasses, characterClassAttribute, opcodeLengths,
					newRuleOffset, newRule, ruleNames, table);
		fclose(nested.in);
		return 1;
	} else
		_lou_logMessage(LOG_ERROR, "Cannot open table '%s'", nested.fileName);
	errorCount++;
	return 0;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

lou_setDataPath(const char *path) {
	static char dataPath[MAXSTRING];
	dataPathPtr = NULL;
	if (path == NULL) return NULL;
	strcpy(dataPath, path);
	dataPathPtr = dataPath;
	return dataPathPtr;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

lou_getEmphClasses(const char *tableList) {
	const char *names[MAX_EMPH_CLASSES + 1];
	unsigned int count = 0;
	const TranslationTableHeader *table = lou_getTable(tableList);
	if (!table) return NULL;

	while (count < MAX_EMPH_CLASSES) {
		char const *name = table->emphClasses[count];
		if (!name) break;
		names[count++] = name;
	}
	names[count++] = NULL;

	{
		unsigned int size = count * sizeof(names[0]);
		char const **result = malloc(size);
		if (!result) return NULL;
		/* The void* cast is necessary to stop MSVC from warning about
		 * different 'const' qualifiers (C4090). */
		memcpy((void *)result, names, size);
		return result;
	}
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

lou_registerTableResolver(
		char **(EXPORT_CALL *resolver)(const char *tableList, const char *base)) {
	tableResolver = resolver;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

lou_getTable(const char *tableList) {
	/* Keep track of which tables have already been compiled */
	int tableListLen;
	ChainEntry *currentEntry = NULL;
	ChainEntry *lastEntry = NULL;
	TranslationTableHeader *newTable;
	if (tableList == NULL || *tableList == 0) return NULL;
	errorCount = fileCount = 0;
	tableListLen = (int)strlen(tableList);
	/* See if this is the last table used. */
	if (lastTrans != NULL)
		if (tableListLen == lastTrans->tableListLength &&
				(memcmp(&lastTrans->tableList[0], tableList, tableListLen)) == 0)
			return (gTable = lastTrans->table);
	/* See if Table has already been compiled */
	currentEntry = tableChain;
	while (currentEntry != NULL) {
		if (tableListLen == currentEntry->tableListLength &&
				(memcmp(&currentEntry->tableList[0], tableList, tableListLen)) == 0) {
			lastTrans = currentEntry;
			return (gTable = currentEntry->table);
		}
		lastEntry = currentEntry;
		currentEntry = currentEntry->next;
	}
	if ((newTable = compileTranslationTable(tableList, &gCharacterClasses,
				 &gCharacterClassAttribute, gOpcodeLengths, &gNewRuleOffset, &gNewRule,
				 &gRuleNames))) {
		/* Add a new entry to the table chain. */
		int entrySize = sizeof(ChainEntry) + tableListLen;
		ChainEntry *newEntry = malloc(entrySize);
		if (!newEntry) _lou_outOfMemory();
		if (tableChain == NULL)
			tableChain = newEntry;
		else
			lastEntry->next = newEntry;
		newEntry->next = NULL;
		newEntry->table = newTable;
		newEntry->tableListLength = tableListLen;
		memcpy(&newEntry->tableList[0], tableList, tableListLen);
		lastTrans = newEntry;
		return (gTable = newEntry->table);
	}
	_lou_logMessage(LOG_ERROR, "%s could not be compiled", tableList);
	return NULL;
}

Project: liblouis
File: compileTranslationTable.c

passIsLeftParen(CharsString *passLine, int *passLinepos, int *passPrevLinepos,
		CharsString *passHoldString, FileInfo *passNested,
		TranslationTableHeader *table) {
	widechar passHoldNumber;
	pass_Codes passCode = passGetScriptToken(passLine, passLinepos, passPrevLinepos,
			passHoldString, &passHoldNumber, passNested, table);
	if (passCode != pass_leftParen) {
		compileError(passNested, "'(' expected");
		return 0;
	}
	return 1;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

addBackwardRuleWithMultipleCells(widechar *cells, int count,
		TranslationTableOffset *newRuleOffset, TranslationTableRule *newRule,
		TranslationTableHeader *table) {
	/* direction = 1, newRule->dotslen > 1 */
	TranslationTableRule *currentRule = NULL;
	TranslationTableOffset *currentOffsetPtr = &table->backRules[_lou_stringHash(cells)];
	if (newRule->opcode == CTO_SwapCc) return;
	while (*currentOffsetPtr) {
		int currentLength;
		int newLength;
		currentRule = (TranslationTableRule *)&table->ruleArea[*currentOffsetPtr];
		currentLength = currentRule->dotslen + currentRule->charslen;
		newLength = count + newRule->charslen;
		if (newLength > currentLength) break;
		if (currentLength == newLength)
			if ((currentRule->opcode == CTO_Always) && (newRule->opcode != CTO_Always))
				break;
		currentOffsetPtr = &currentRule->dotsnext;
	}
	newRule->dotsnext = *currentOffsetPtr;
	*currentOffsetPtr = *newRuleOffset;
}

Project: liblouis
File: compileTranslationTable.c

passInsertAttributes(CharsString *passLine, int *passLinepos, int *passPrevLinepos,
		CharsString *passHoldString, TranslationTableCharacterAttributes *passAttributes,
		FileInfo *passNested, widechar *passInstructions, int *passIC,
		TranslationTableHeader *table) {
	passInstructions[(*passIC)++] = pass_attributes;
	passInstructions[(*passIC)++] = *passAttributes >> 16;
	passInstructions[(*passIC)++] = *passAttributes & 0xffff;
	if (!passGetRange(passLine, passLinepos, passPrevLinepos, passHoldString, passNested,
				passInstructions, passIC, table))
		return 0;
	return 1;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

lou_charSize(void) {
	return CHARSIZE;
}

Project: liblouis
File: compileTranslationTable.c

_lou_allocMem(AllocBuf buffer, int index, int srcmax, int destmax) {
	if (srcmax < 1024) srcmax = 1024;
	if (destmax < 1024) destmax = 1024;
	switch (buffer) {
	case alloc_typebuf:
		if (destmax > sizeTypebuf) {
			if (typebuf != NULL) free(typebuf);
			// TODO: should this be srcmax?
			typebuf = malloc((destmax + 4) * sizeof(formtype));
			if (!typebuf) _lou_outOfMemory();
			sizeTypebuf = destmax;
		}
		return typebuf;

	case alloc_wordBuffer:

		if (wordBuffer != NULL) free(wordBuffer);
		wordBuffer = malloc((srcmax + 4) * sizeof(unsigned int));
		if (!wordBuffer) _lou_outOfMemory();
		return wordBuffer;

	case alloc_emphasisBuffer:

		if (emphasisBuffer != NULL) free(emphasisBuffer);
		emphasisBuffer = malloc((srcmax + 4) * sizeof(EmphasisInfo));
		if (!emphasisBuffer) _lou_outOfMemory();
		return emphasisBuffer;

	case alloc_destSpacing:
		if (destmax > sizeDestSpacing) {
			if (destSpacing != NULL) free(destSpacing);
			destSpacing = malloc(destmax + 4);
			if (!destSpacing) _lou_outOfMemory();
			sizeDestSpacing = destmax;
		}
		return destSpacing;
	case alloc_passbuf:
		if (index < 0 || index >= MAXPASSBUF) {
			_lou_logMessage(LOG_FATAL, "Index out of bounds: %d\n", index);
			exit(3);
		}
		if (destmax > sizePassbuf[index]) {
			if (passbuf[index] != NULL) free(passbuf[index]);
			passbuf[index] = malloc((destmax + 4) * CHARSIZE);
			if (!passbuf[index]) _lou_outOfMemory();
			sizePassbuf[index] = destmax;
		}
		return passbuf[index];
	case alloc_posMapping1: {
		int mapSize;
		if (srcmax >= destmax)
			mapSize = srcmax;
		else
			mapSize = destmax;
		if (mapSize > sizePosMapping1) {
			if (posMapping1 != NULL) free(posMapping1);
			posMapping1 = malloc((mapSize + 4) * sizeof(int));
			if (!posMapping1) _lou_outOfMemory();
			sizePosMapping1 = mapSize;
		}
	}
		return posMapping1;
	case alloc_posMapping2: {
		int mapSize;
		if (srcmax >= destmax)
			mapSize = srcmax;
		else
			mapSize = destmax;
		if (mapSize > sizePosMapping2) {
			if (posMapping2 != NULL) free(posMapping2);
			posMapping2 = malloc((mapSize + 4) * sizeof(int));
			if (!posMapping2) _lou_outOfMemory();
			sizePosMapping2 = mapSize;
		}
	}
		return posMapping2;
	case alloc_posMapping3: {
		int mapSize;
		if (srcmax >= destmax)
			mapSize = srcmax;
		else
			mapSize = destmax;
		if (mapSize > sizePosMapping3) {
			if (posMapping3 != NULL) free(posMapping3);
			posMapping3 = malloc((mapSize + 4) * sizeof(int));
			if (!posMapping3) _lou_outOfMemory();
			sizePosMapping3 = mapSize;
		}
	}
		return posMapping3;
	default:
		return NULL;
	}
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

atEndOfLine(FileInfo *nested) {
	return nested->linepos >= nested->linelen;
}

Project: liblouis
File: compileTranslationTable.c

_lou_getTablePath(void) {
	char searchPath[MAXSTRING];
	char *path;
	char *cp;
	cp = searchPath;
	path = getenv("LOUIS_TABLEPATH");
	if (path != NULL && path[0] != '\0') cp += sprintf(cp, ",%s", path);
	path = lou_getDataPath();
	if (path != NULL && path[0] != '\0')
		cp += sprintf(cp, ",%s%c%s%c%s", path, DIR_SEP, "liblouis", DIR_SEP, "tables");
#ifdef _WIN32
	path = lou_getProgramPath();
	if (path != NULL) {
		if (path[0] != '\0')
			cp += sprintf(cp, ",%s%s", path, "\\share\\liblouis\\tables");
		free(path);
	}
#else
	cp += sprintf(cp, ",%s", TABLESDIR);
#endif
	if (searchPath[0] != '\0')
		return strdup(&searchPath[1]);
	else
		return strdup(".");
}

Project: liblouis
File: compileTranslationTable.c

lou_getTypeformForEmphClass(const char *tableList, const char *emphClass) {
	int i;
	TranslationTableHeader *table = lou_getTable(tableList);
	if (!table) return 0;
	for (i = 0; table->emphClasses[i]; i++)
		if (strcmp(emphClass, table->emphClasses[i]) == 0) return italic << i;
	return 0;
}

Project: liblouis
File: compileTranslationTable.c

compileBeforeAfter(FileInfo *nested, int *lastToken) {
	/* 1=before, 2=after, 0=error */
	CharsString token;
	CharsString tmp;
	if (getToken(nested, &token, "last word before or after", lastToken))
		if (parseChars(nested, &tmp, &token)) {
			if (eqasc2uni((unsigned char *)"before", tmp.chars, 6)) return 1;
			if (eqasc2uni((unsigned char *)"after", tmp.chars, 5)) return 2;
		}
	return 0;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

resolveSubtable(const char *table, const char *base, const char *searchPath) {
	char *tableFile;
	static struct stat info;

	if (table == NULL || table[0] == '\0') return NULL;
	tableFile = (char *)malloc(MAXSTRING * sizeof(char) * 2);

	//
	// First try to resolve against base
	//
	if (base) {
		int k;
		strcpy(tableFile, base);
		k = (int)strlen(tableFile);
		while (k >= 0 && tableFile[k] != '/' && tableFile[k] != '\\') k--;
		tableFile[++k] = '\0';
		strcat(tableFile, table);
		if (stat(tableFile, &info) == 0 && !(info.st_mode & S_IFDIR)) {
			_lou_logMessage(LOG_DEBUG, "found table %s", tableFile);
			return tableFile;
		}
	}

	//
	// It could be an absolute path, or a path relative to the current working
	// directory
	//
	strcpy(tableFile, table);
	if (stat(tableFile, &info) == 0 && !(info.st_mode & S_IFDIR)) {
		_lou_logMessage(LOG_DEBUG, "found table %s", tableFile);
		return tableFile;
	}

	//
	// Then search `LOUIS_TABLEPATH`, `dataPath` and `programPath`
	//
	if (searchPath[0] != '\0') {
		char *dir;
		int last;
		char *cp;
		char *searchPath_copy = strdup(searchPath);
		for (dir = searchPath_copy;; dir = cp + 1) {
			for (cp = dir; *cp != '\0' && *cp != ','; cp++)
				;
			last = (*cp == '\0');
			*cp = '\0';
			if (dir == cp) dir = ".";
			sprintf(tableFile, "%s%c%s", dir, DIR_SEP, table);
			if (stat(tableFile, &info) == 0 && !(info.st_mode & S_IFDIR)) {
				_lou_logMessage(LOG_DEBUG, "found table %s", tableFile);
				free(searchPath_copy);
				return tableFile;
			}
			if (last) break;
			sprintf(tableFile, "%s%c%s%c%s%c%s", dir, DIR_SEP, "liblouis", DIR_SEP,
					"tables", DIR_SEP, table);
			if (stat(tableFile, &info) == 0 && !(info.st_mode & S_IFDIR)) {
				_lou_logMessage(LOG_DEBUG, "found table %s", tableFile);
				free(searchPath_copy);
				return tableFile;
			}
			if (last) break;
		}
		free(searchPath_copy);
	}
	free(tableFile);
	return NULL;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

copyStringArray(char **array) {
	int len;
	char **copy;
	if (!array) return NULL;
	len = 0;
	while (array[len]) len++;
	copy = malloc((len + 1) * sizeof(char *));
	copy[len] = NULL;
	while (len) {
		len--;
		copy[len] = strdup(array[len]);
	}
	return copy;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

addForwardPassRule(TranslationTableOffset *newRuleOffset, TranslationTableRule *newRule,
		TranslationTableHeader *table) {
	TranslationTableOffset *currentOffsetPtr;
	TranslationTableRule *currentRule;
	switch (newRule->opcode) {
	case CTO_Correct:
		currentOffsetPtr = &table->forPassRules[0];
		break;
	case CTO_Context:
		currentOffsetPtr = &table->forPassRules[1];
		break;
	case CTO_Pass2:
		currentOffsetPtr = &table->forPassRules[2];
		break;
	case CTO_Pass3:
		currentOffsetPtr = &table->forPassRules[3];
		break;
	case CTO_Pass4:
		currentOffsetPtr = &table->forPassRules[4];
		break;
	default:
		return 0;
	}
	while (*currentOffsetPtr) {
		currentRule = (TranslationTableRule *)&table->ruleArea[*currentOffsetPtr];
		if (newRule->charslen > currentRule->charslen) break;
		currentOffsetPtr = &currentRule->charsnext;
	}
	newRule->charsnext = *currentOffsetPtr;
	*currentOffsetPtr = *newRuleOffset;
	return 1;
}

Project: liblouis
File: compileTranslationTable.c

_lou_getLastTableList(void) {
	static char scratchBuf[MAXSTRING];
	if (lastTrans == NULL) return NULL;
	strncpy(scratchBuf, lastTrans->tableList, lastTrans->tableListLength);
	scratchBuf[lastTrans->tableListLength] = 0;
	return scratchBuf;
}

Project: liblouis
File: compileTranslationTable.c

wantsString(TranslationTableOpcode opcode, int actionPart, int nofor) {
	if (opcode == CTO_Correct) return 1;
	if (opcode != CTO_Context) return 0;
	return !nofor == !actionPart;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

verifyStringOrDots(FileInfo *nested, TranslationTableOpcode opcode, int isString,
		int actionPart, int nofor) {
	if (!wantsString(opcode, actionPart, nofor) == !isString) return 1;

	compileError(nested, "%s are not allowed in the %s part of a %s translation %s rule.",
			isString ? "strings" : "dots", getPartName(actionPart),
			nofor ? "backward" : "forward", _lou_findOpcodeName(opcode));

	return 0;
}

Project: liblouis
File: compileTranslationTable.c

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

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

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

		} /* Compile action part */

		/* Compile action part */
		while (passLinepos < passLine.length && passLine.chars[passLinepos] <= 32)
			passLinepos++;
		while (passLinepos < passLine.length && passLine.chars[passLinepos] > 32) {
			if (passIC >= MAXSTRING) {
				compileError(passNested, "Action part in multipass operand too long");
				return 0;
			}
			switch ((passSubOp = passLine.chars[passLinepos])) {
			case pass_string:
				if (!verifyStringOrDots(nested, opcode, 1, 1, nofor)) {
					return 0;
				}
				passLinepos++;
				passInstructions[passIC++] = pass_string;
				passGetString(&passLine, &passLinepos, &passHoldString, passNested);
				goto actionDoCharsDots;
			case pass_dots:
				if (!verifyStringOrDots(nested, opcode, 0, 1, nofor)) {
					return 0;
				}
				passLinepos++;
				passGetDots(&passLine, &passLinepos, &passHoldString, passNested);
				passInstructions[passIC++] = pass_dots;
			actionDoCharsDots:
				if (passHoldString.length == 0) return 0;
				passInstructions[passIC++] = passHoldString.length;
				for (kk = 0; kk < passHoldString.length; kk++) {
					if (passIC >= MAXSTRING) {
						compileError(passNested,
								"@ operand in action part of multipass operand too long");
						return 0;
					}
					passInstructions[passIC++] = passHoldString.chars[kk];
				}
				break;
			case pass_variable:
				passLinepos++;
				if (!passGetVariableNumber(
							nested, &passLine, &passLinepos, &passHoldNumber))
					return 0;
				switch (passLine.chars[passLinepos]) {
				case pass_eq:
					passInstructions[passIC++] = pass_eq;
					passInstructions[passIC++] = passHoldNumber;
					passLinepos++;
					passGetNumber(&passLine, &passLinepos, &passHoldNumber);
					passInstructions[passIC++] = passHoldNumber;
					break;
				case pass_plus:
				case pass_hyphen:
					passInstructions[passIC++] = passLine.chars[passLinepos++];
					passInstructions[passIC++] = passHoldNumber;
					break;
				default:
					compileError(
							passNested, "incorrect variable operator in action part");
					return 0;
				}
				break;
			case pass_copy:
				passInstructions[passIC++] = pass_copy;
				passLinepos++;
				break;
			case pass_omit:
				passInstructions[passIC++] = pass_omit;
				passLinepos++;
				break;
			case pass_groupreplace:
			case pass_groupstart:
			case pass_groupend:
				passLinepos++;
				passGetName(&passLine, &passLinepos, &passHoldString, passNested, *table);
				ruleOffset = findRuleName(&passHoldString, ruleNames);
				if (ruleOffset)
					rule = (TranslationTableRule *)&(*table)->ruleArea[ruleOffset];
				if (rule && rule->opcode == CTO_Grouping) {
					passInstructions[passIC++] = passSubOp;
					passInstructions[passIC++] = ruleOffset >> 16;
					passInstructions[passIC++] = ruleOffset & 0xffff;
					break;
				}
				compileError(passNested, "%s is not a grouping name",
						_lou_showString(&passHoldString.chars[0], passHoldString.length));
				return 0;
			case pass_swap:
				passLinepos++;
				passGetName(&passLine, &passLinepos, &passHoldString, passNested, *table);
				ruleOffset = findRuleName(&passHoldString, ruleNames);
				if (ruleOffset)
					rule = (TranslationTableRule *)&(*table)->ruleArea[ruleOffset];
				if (rule && (rule->opcode == CTO_SwapCc || rule->opcode == CTO_SwapCd ||
									rule->opcode == CTO_SwapDd)) {
					passInstructions[passIC++] = pass_swap;
					passInstructions[passIC++] = ruleOffset >> 16;
					passInstructions[passIC++] = ruleOffset & 0xffff;
					break;
				}
				compileError(passNested, "%s is not a swap name.",
						_lou_showString(&passHoldString.chars[0], passHoldString.length));
				return 0;
				break;
			default:
				compileError(passNested, "incorrect operator in action part");
				return 0;
			}
		}
	}

	/* Analyze and add rule */
	passRuleDots.length = passIC;

	{
		widechar *characters;
		int length;
		int found = passFindCharacters(
				passNested, passInstructions, passRuleDots.length, &characters, &length);

		if (!found) return 0;

		if (characters) {
			for (k = 0; k < length; k += 1) passRuleChars.chars[k] = characters[k];
			passRuleChars.length = k;
		}
	}

	if (!addRule(passNested, opcode, &passRuleChars, &passRuleDots, 0, 0, newRuleOffset,
				newRule, noback, nofor, table))
		return 0;
	return 1;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

hyphenHashInsert(HyphenHashTab *hashTab, const CharsString *key, int val) {
	int i, j;
	HyphenHashEntry *e;
	i = hyphenStringHash(key) % HYPHENHASHSIZE;
	if (!(e = malloc(sizeof(HyphenHashEntry)))) _lou_outOfMemory();
	e->next = hashTab->entries[i];
	e->key = malloc((key->length + 1) * CHARSIZE);
	if (!e->key) _lou_outOfMemory();
	e->key->length = key->length;
	for (j = 0; j < key->length; j++) e->key->chars[j] = key->chars[j];
	e->val = val;
	hashTab->entries[i] = e;
}

Project: liblouis
File: compileTranslationTable.c

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

	/* link new rule into table. */
	if (opcode == CTO_SwapCc || opcode == CTO_SwapCd || opcode == CTO_SwapDd) return 1;
	if (opcode >= CTO_Context && opcode <= CTO_Pass4)
		if (!(opcode == CTO_Context && rule->charslen > 0)) {
			if (!nofor)
				if (!addForwardPassRule(newRuleOffset, rule, *table)) return 0;
			if (!noback)
				if (!addBackwardPassRule(newRuleOffset, rule, *table)) return 0;
			return 1;
		}
	if (!nofor) {
		if (rule->charslen == 1)
			addForwardRuleWithSingleChar(nested, newRuleOffset, rule, *table);
		else if (rule->charslen > 1)
			addForwardRuleWithMultipleChars(newRuleOffset, rule, *table);
	}
	if (!noback) {
		widechar *cells;
		int count;

		if (rule->opcode == CTO_Context) {
			cells = &rule->charsdots[0];
			count = rule->charslen;
		} else {
			cells = &rule->charsdots[rule->charslen];
			count = rule->dotslen;
		}

		if (count == 1)
			addBackwardRuleWithSingleCell(nested, *cells, newRuleOffset, rule, *table);
		else if (count > 1)
			addBackwardRuleWithMultipleCells(cells, count, newRuleOffset, rule, *table);
	}
	return 1;
}

Project: liblouis
File: compileTranslationTable.c

compileBrailleIndicator(FileInfo *nested, const char *ermsg,
		TranslationTableOpcode opcode, TranslationTableOffset *rule, int *lastToken,
		TranslationTableOffset *newRuleOffset, TranslationTableRule **newRule, int noback,
		int nofor, TranslationTableHeader **table) {
	CharsString token;
	CharsString cells;
	if (getToken(nested, &token, ermsg, lastToken))
		if (parseDots(nested, &cells, &token))
			if (!addRule(nested, opcode, NULL, &cells, 0, 0, newRuleOffset, newRule,
						noback, nofor, table))
				return 0;
	*rule = *newRuleOffset;
	return 1;
}

Project: liblouis
File: compileTranslationTable.c

includeFile(FileInfo *nested, CharsString *includedFile,
		CharacterClass **characterClasses,
		TranslationTableCharacterAttributes *characterClassAttribute,
		short opcodeLengths[], TranslationTableOffset *newRuleOffset,
		TranslationTableRule **newRule, RuleName **ruleNames,
		TranslationTableHeader **table) {
	int k;
	char includeThis[MAXSTRING];
	char **tableFiles;
	int rv;
	for (k = 0; k < includedFile->length; k++)
		includeThis[k] = (char)includedFile->chars[k];
	if (k >= MAXSTRING) {
		compileError(nested, "Include statement too long: 'include %s'", includeThis);
		return 0;
	}
	includeThis[k] = 0;
	tableFiles = _lou_resolveTable(includeThis, nested->fileName);
	if (tableFiles == NULL) {
		errorCount++;
		return 0;
	}
	if (tableFiles[1] != NULL) {
		free_tablefiles(tableFiles);
		compileError(nested,
				"Table list not supported in include statement: 'include %s'",
				includeThis);
		return 0;
	}
	rv = compileFile(*tableFiles, characterClasses, characterClassAttribute,
			opcodeLengths, newRuleOffset, newRule, ruleNames, table);
	free_tablefiles(tableFiles);
	return rv;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

lou_checkTable(const char *tableList) {
	if (lou_getTable(tableList)) return 1;
	return 0;
}

Project: liblouis
File: compileTranslationTable.c

lou_compileString(const char *tableList, const char *inString) {
	int r;
	TranslationTableHeader *table = lou_getTable(tableList);
	if (!table) return 0;
	r = compileString(inString, &gCharacterClasses, &gCharacterClassAttribute,
			gOpcodeLengths, &gNewRuleOffset, &gNewRule, &gRuleNames, &table);
	gTable = table;
	return r;
}

Project: liblouis
File: compileTranslationTable.c

passIsRightParen(CharsString *passLine, int *passLinepos, int *passPrevLinepos,
		CharsString *passHoldString, FileInfo *passNested,
		TranslationTableHeader *table) {
	widechar passHoldNumber;
	pass_Codes passCode = passGetScriptToken(passLine, passLinepos, passPrevLinepos,
			passHoldString, &passHoldNumber, passNested, table);
	if (passCode != pass_rightParen) {
		compileError(passNested, "')' expected");
		return 0;
	}
	return 1;
}

Project: liblouis
File: compileTranslationTable.c

compileSwap(FileInfo *nested, TranslationTableOpcode opcode, int *lastToken,
		TranslationTableOffset *newRuleOffset, TranslationTableRule **newRule, int noback,
		int nofor, RuleName **ruleNames, TranslationTableHeader **table) {
	CharsString ruleChars;
	CharsString ruleDots;
	CharsString name;
	CharsString matches;
	CharsString replacements;
	if (!getToken(nested, &name, "name operand", lastToken)) return 0;
	if (!getToken(nested, &matches, "matches operand", lastToken)) return 0;
	if (!getToken(nested, &replacements, "replacements operand", lastToken)) return 0;
	if (opcode == CTO_SwapCc || opcode == CTO_SwapCd) {
		if (!parseChars(nested, &ruleChars, &matches)) return 0;
	} else {
		if (!compileSwapDots(nested, &matches, &ruleChars)) return 0;
	}
	if (opcode == CTO_SwapCc) {
		if (!parseChars(nested, &ruleDots, &replacements)) return 0;
	} else {
		if (!compileSwapDots(nested, &replacements, &ruleDots)) return 0;
	}
	if (!addRule(nested, opcode, &ruleChars, &ruleDots, 0, 0, newRuleOffset, newRule,
				noback, nofor, table))
		return 0;
	if (!addRuleName(nested, &name, newRuleOffset, ruleNames, *table)) return 0;
	return 1;
}

Project: liblouis
File: compileTranslationTable.c

hyphenStringHash(const CharsString *s) {
	int k;
	unsigned int h = 0, g;
	for (k = 0; k < s->length; k++) {
		h = (h << 4) + s->chars[k];
		if ((g = h & 0xf0000000)) {
			h = h ^ (g >> 24);
			h = h ^ g;
		}
	}
	return h;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

passIsName(CharsString *passLine, int *passLinepos, int *passPrevLinepos,
		CharsString *passHoldString, FileInfo *passNested,
		TranslationTableHeader *table) {
	widechar passHoldNumber;
	pass_Codes passCode = passGetScriptToken(passLine, passLinepos, passPrevLinepos,
			passHoldString, &passHoldNumber, passNested, table);
	if (passCode != pass_nameFound) {
		compileError(passNested, "a name expected");
		return 0;
	}
	return 1;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

lou_free(void) {
	ChainEntry *currentEntry;
	ChainEntry *previousEntry;
	lou_logEnd();
	if (tableChain != NULL) {
		currentEntry = tableChain;
		while (currentEntry) {
			int i;
			TranslationTableHeader *t = (TranslationTableHeader *)currentEntry->table;
			for (i = 0; t->emphClasses[i]; i++) free(t->emphClasses[i]);
			free(t);
			previousEntry = currentEntry;
			currentEntry = currentEntry->next;
			free(previousEntry);
		}
		tableChain = NULL;
		lastTrans = NULL;
	}
	if (typebuf != NULL) free(typebuf);
	typebuf = NULL;
	if (wordBuffer != NULL) free(wordBuffer);
	wordBuffer = NULL;
	if (emphasisBuffer != NULL) free(emphasisBuffer);
	emphasisBuffer = NULL;
	sizeTypebuf = 0;
	if (destSpacing != NULL) free(destSpacing);
	destSpacing = NULL;
	sizeDestSpacing = 0;
	{
		int k;
		for (k = 0; k < MAXPASSBUF; k++) {
			if (passbuf[k] != NULL) free(passbuf[k]);
			passbuf[k] = NULL;
			sizePassbuf[k] = 0;
		}
	}
	if (posMapping1 != NULL) free(posMapping1);
	posMapping1 = NULL;
	sizePosMapping1 = 0;
	if (posMapping2 != NULL) free(posMapping2);
	posMapping2 = NULL;
	sizePosMapping2 = 0;
	if (posMapping3 != NULL) free(posMapping3);
	posMapping3 = NULL;
	sizePosMapping3 = 0;
	gOpcodeLengths[0] = 0;
}

Project: liblouis
File: compileTranslationTable.c

free_tablefiles(char **tables) {
	char **table;
	if (!tables) return;
	for (table = tables; *table; table++) free(*table);
	free(tables);
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

compileString(const char *inString, CharacterClass **characterClasses,
		TranslationTableCharacterAttributes *characterClassAttribute,
		short opcodeLengths[], TranslationTableOffset *newRuleOffset,
		TranslationTableRule **newRule, RuleName **ruleNames,
		TranslationTableHeader **table) {
	/* This function can be used to make changes to tables on the fly. */
	int k;
	FileInfo nested;
	if (inString == NULL) return 0;
	memset(&nested, 0, sizeof(nested));
	nested.fileName = inString;
	nested.encoding = noEncoding;
	nested.lineNumber = 1;
	nested.status = 0;
	nested.linepos = 0;
	for (k = 0; inString[k]; k++) nested.line[k] = inString[k];
	nested.line[k] = 0;
	nested.linelen = k;
	return compileRule(&nested, characterClasses, characterClassAttribute, opcodeLengths,
			newRuleOffset, newRule, ruleNames, table);
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

passIsNumber(CharsString *passLine, int *passLinepos, int *passPrevLinepos,
		CharsString *passHoldString, widechar *passHoldNumber, FileInfo *passNested,
		TranslationTableHeader *table) {
	pass_Codes passCode = passGetScriptToken(passLine, passLinepos, passPrevLinepos,
			passHoldString, passHoldNumber, passNested, table);
	if (passCode != pass_numberFound) {
		compileError(passNested, "a number expected");
		return 0;
	}
	return 1;
}

Project: liblouis
File: compileTranslationTable.c

unknownDots(widechar dots) {
	static char buffer[20];
	int k = 1;
	buffer[0] = '\\';
	for (int mappingPos = 0; dotMapping[mappingPos].key; mappingPos++) {
		if (dots & dotMapping[mappingPos].key) buffer[k++] = dotMapping[mappingPos].value;
	}
	buffer[k++] = '/';
	buffer[k] = 0;
	return buffer;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

passFindCharacters(FileInfo *nested, widechar *instructions, int end,
		widechar **characters, int *length) {
	int IC = 0;
	int lookback = 0;

	*characters = NULL;
	*length = 0;

	while (IC < end) {
		widechar instruction = instructions[IC];

		switch (instruction) {
		case pass_string:
		case pass_dots: {
			int count = instructions[IC + 1];
			IC += 2;
			if (count > lookback) {
				*characters = &instructions[IC + lookback];
				*length = count - lookback;
				return 1;
			} else {
				lookback -= count;
			}
			IC += count;
			continue;
		}

		case pass_attributes:
			IC += 5;
			if (instructions[IC - 2] == instructions[IC - 1] &&
					instructions[IC - 1] <= lookback) {
				lookback -= instructions[IC - 1];
				continue;
			}
			goto NO_CHARACTERS;

		case pass_swap:
			IC += 2;
		/* fall through */

		case pass_groupstart:
		case pass_groupend:
		case pass_groupreplace:
			IC += 3;

		NO_CHARACTERS : { return 1; }

		case pass_eq:
		case pass_lt:
		case pass_gt:
		case pass_lteq:
		case pass_gteq:
			IC += 3;
			continue;

		case pass_lookback:
			lookback = instructions[IC + 1];
			IC += 2;
			continue;

		case pass_not:
		case pass_startReplace:
		case pass_endReplace:
		case pass_first:
		case pass_last:
		case pass_copy:
		case pass_omit:
		case pass_plus:
		case pass_hyphen:
			IC += 1;
			continue;

		case pass_endTest:
			goto NOT_FOUND;

		default:
			compileError(nested, "unhandled test suboperand: \\x%02x", instruction);
			return 0;
		}
	}

NOT_FOUND:
	compileError(
			nested, "characters, dots, attributes, or class swap not found in test part");

	return 0;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

compileTranslationTable(const char *tableList, CharacterClass **characterClasses,
		TranslationTableCharacterAttributes *characterClassAttribute,
		short opcodeLengths[], TranslationTableOffset *newRuleOffset,
		TranslationTableRule **newRule, RuleName **ruleNames) {
	TranslationTableHeader *table = NULL;
	char **tableFiles;
	char **subTable;
	errorCount = warningCount = fileCount = 0;
	*characterClasses = NULL;
	*ruleNames = NULL;
	if (tableList == NULL) return NULL;
	if (!opcodeLengths[0]) {
		TranslationTableOpcode opcode;
		for (opcode = 0; opcode < CTO_None; opcode++)
			opcodeLengths[opcode] = (short)strlen(opcodeNames[opcode]);
	}
	allocateHeader(NULL, &table);

	/* Initialize emphClasses array */
	table->emphClasses[0] = NULL;

	/* Compile things that are necesary for the proper operation of
	 * liblouis or liblouisxml or liblouisutdml */
	compileString("space \\s 0", characterClasses, characterClassAttribute, opcodeLengths,
			newRuleOffset, newRule, ruleNames, &table);
	compileString("noback sign \\x0000 0", characterClasses, characterClassAttribute,
			opcodeLengths, newRuleOffset, newRule, ruleNames, &table);
	compileString("space \\x00a0 a unbreakable space", characterClasses,
			characterClassAttribute, opcodeLengths, newRuleOffset, newRule, ruleNames,
			&table);
	compileString("space \\x001b 1b escape", characterClasses, characterClassAttribute,
			opcodeLengths, newRuleOffset, newRule, ruleNames, &table);
	compileString("space \\xffff 123456789abcdef ENDSEGMENT", characterClasses,
			characterClassAttribute, opcodeLengths, newRuleOffset, newRule, ruleNames,
			&table);

	/* Compile all subtables in the list */
	if (!(tableFiles = _lou_resolveTable(tableList, NULL))) {
		errorCount++;
		goto cleanup;
	}
	for (subTable = tableFiles; *subTable; subTable++)
		if (!compileFile(*subTable, characterClasses, characterClassAttribute,
					opcodeLengths, newRuleOffset, newRule, ruleNames, &table))
			goto cleanup;

/* Clean up after compiling files */
cleanup:
	free_tablefiles(tableFiles);
	if (*characterClasses) deallocateCharacterClasses(characterClasses);
	if (*ruleNames) deallocateRuleNames(ruleNames);
	if (warningCount) _lou_logMessage(LOG_WARN, "%d warnings issued", warningCount);
	if (!errorCount) {
		setDefaults(table);
		table->tableSize = tableSize;
		table->bytesUsed = tableUsed;
	} else {
		_lou_logMessage(LOG_ERROR, "%d errors found.", errorCount);
		if (table) free(table);
		table = NULL;
	}
	return table;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

passIsComma(CharsString *passLine, int *passLinepos, int *passPrevLinepos,
		CharsString *passHoldString, FileInfo *passNested,
		TranslationTableHeader *table) {
	widechar passHoldNumber;
	pass_Codes passCode = passGetScriptToken(passLine, passLinepos, passPrevLinepos,
			passHoldString, &passHoldNumber, passNested, table);
	if (passCode != pass_comma) {
		compileError(passNested, "',' expected");
		return 0;
	}
	return 1;
}

Project: liblouis
File: compileTranslationTable.c

passGetVariableNumber(FileInfo *nested, CharsString *passLine, int *passLinepos,
		widechar *passHoldNumber) {
	if (!passGetNumber(passLine, passLinepos, passHoldNumber)) return 0;
	if ((*passHoldNumber >= 0) && (*passHoldNumber < NUMVAR)) return 1;
	compileError(nested, "variable number out of range");
	return 0;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

_lou_resolveTable(const char *tableList, const char *base) {
	char **tableFiles = (*tableResolver)(tableList, base);
	char **result = copyStringArray(tableFiles);
	if (tableResolver == &_lou_defaultTableResolver) free_tablefiles(tableFiles);
	return result;
}

Project: liblouis
File: compileTranslationTable.c

hyphenAddTrans(HyphenDict *dict, int state1, int state2, widechar ch) {
	int numTrans;
	numTrans = dict->states[state1].numTrans;
	if (numTrans == 0)
		dict->states[state1].trans.pointer = malloc(sizeof(HyphenationTrans));
	else if (!(numTrans & (numTrans - 1)))
		dict->states[state1].trans.pointer = realloc(dict->states[state1].trans.pointer,
				(numTrans << 1) * sizeof(HyphenationTrans));
	dict->states[state1].trans.pointer[numTrans].ch = ch;
	dict->states[state1].trans.pointer[numTrans].newState = state2;
	dict->states[state1].numTrans++;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

addBackwardPassRule(TranslationTableOffset *newRuleOffset, TranslationTableRule *newRule,
		TranslationTableHeader *table) {
	TranslationTableOffset *currentOffsetPtr;
	TranslationTableRule *currentRule;
	switch (newRule->opcode) {
	case CTO_Correct:
		currentOffsetPtr = &table->backPassRules[0];
		break;
	case CTO_Context:
		currentOffsetPtr = &table->backPassRules[1];
		break;
	case CTO_Pass2:
		currentOffsetPtr = &table->backPassRules[2];
		break;
	case CTO_Pass3:
		currentOffsetPtr = &table->backPassRules[3];
		break;
	case CTO_Pass4:
		currentOffsetPtr = &table->backPassRules[4];
		break;
	default:
		return 0;
	}
	while (*currentOffsetPtr) {
		currentRule = (TranslationTableRule *)&table->ruleArea[*currentOffsetPtr];
		if (newRule->charslen > currentRule->charslen) break;
		currentOffsetPtr = &currentRule->dotsnext;
	}
	newRule->dotsnext = *currentOffsetPtr;
	*currentOffsetPtr = *newRuleOffset;
	return 1;
}

Project: liblouis
File: compileTranslationTable.c

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

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

Project: liblouis
File: compileTranslationTable.c

passGetRange(CharsString *passLine, int *passLinepos, int *passPrevLinepos,
		CharsString *passHoldString, FileInfo *passNested, widechar *passInstructions,
		int *passIC, TranslationTableHeader *table) {
	widechar passHoldNumber;
	pass_Codes passCode = passGetScriptToken(passLine, passLinepos, passPrevLinepos,
			passHoldString, &passHoldNumber, passNested, table);
	if (!(passCode == pass_comma || passCode == pass_rightParen)) {
		compileError(passNested, "invalid range");
		return 0;
	}
	if (passCode == pass_rightParen) {
		passInstructions[(*passIC)++] = 1;
		passInstructions[(*passIC)++] = 1;
		return 1;
	}
	if (!passIsNumber(passLine, passLinepos, passPrevLinepos, passHoldString,
				&passHoldNumber, passNested, table))
		return 0;
	passInstructions[(*passIC)++] = passHoldNumber;
	passCode = passGetScriptToken(passLine, passLinepos, passPrevLinepos, passHoldString,
			&passHoldNumber, passNested, table);
	if (!(passCode == pass_comma || passCode == pass_rightParen)) {
		compileError(passNested, "invalid range");
		return 0;
	}
	if (passCode == pass_rightParen) {
		passInstructions[(*passIC)++] = passHoldNumber;
		return 1;
	}
	if (!passIsNumber(passLine, passLinepos, passPrevLinepos, passHoldString,
				&passHoldNumber, passNested, table))
		return 0;
	passInstructions[(*passIC)++] = passHoldNumber;
	if (!passIsRightParen(passLine, passLinepos, passPrevLinepos, passHoldString,
				passNested, table))
		return 0;
	return 1;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

definedCharOrDots(FileInfo *nested, widechar c, int m, TranslationTableHeader *table) {
	static TranslationTableCharacter noChar = {
		.next = 0,
		.definitionRule = 0,
		.otherRules = 0,
		.attributes = CTC_Space,
		.realchar = 32,
		.uppercase = 32,
		.lowercase = 32,
	};
	static TranslationTableCharacter noDots = {
		.next = 0,
		.definitionRule = 0,
		.otherRules = 0,
		.attributes = CTC_Space,
		.realchar = B16,
		.uppercase = B16,
		.lowercase = B16,
	};
	TranslationTableCharacter *notFound;
	TranslationTableCharacter *charOrDots = compile_findCharOrDots(c, m, table);
	if (charOrDots) return charOrDots;
	if (m == 0) {
		notFound = &noChar;
		compileError(nested, "character %s should be defined at this point but is not",
				_lou_showString(&c, 1));
	} else {
		notFound = &noDots;
		compileError(nested, "cell %s should be defined at this point but is not",
				unknownDots(c));
	}
	return notFound;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

charactersDefined(
		FileInfo *nested, TranslationTableRule *newRule, TranslationTableHeader *table) {
	/* Check that all characters are defined by character-definition
	 * opcodes */
	int noErrors = 1;
	int k;
	if ((newRule->opcode >= CTO_Space && newRule->opcode <= CTO_LitDigit) ||
			newRule->opcode == CTO_SwapDd || newRule->opcode == CTO_Replace ||
			newRule->opcode == CTO_MultInd || newRule->opcode == CTO_Repeated ||
			((newRule->opcode >= CTO_Context && newRule->opcode <= CTO_Pass4) &&
					newRule->opcode != CTO_Correct) ||
			newRule->opcode == CTO_Match)
		return 1;
	for (k = 0; k < newRule->charslen; k++)
		if (!compile_findCharOrDots(newRule->charsdots[k], 0, table)) {
			compileError(nested, "Character %s is not defined",
					_lou_showString(&newRule->charsdots[k], 1));
			noErrors = 0;
		}
	if (!(newRule->opcode == CTO_Correct || newRule->opcode == CTO_SwapCc ||
				newRule->opcode == CTO_SwapCd)
			// TODO: these just need to know there is a way to get from dots to a char
			&&
			!(newRule->opcode >= CTO_CapsLetterRule &&
					newRule->opcode <= CTO_EndEmph10PhraseAfterRule)) {
		for (k = newRule->charslen; k < newRule->charslen + newRule->dotslen; k++)
			if (!compile_findCharOrDots(newRule->charsdots[k], 1, table)) {
				compileError(nested, "Dot pattern %s is not defined.",
						unknownDots(newRule->charsdots[k]));
				noErrors = 0;
			}
	}
	return noErrors;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

compileCharDef(FileInfo *nested, TranslationTableOpcode opcode,
		TranslationTableCharacterAttributes attributes, int *lastToken,
		TranslationTableOffset *newRuleOffset, TranslationTableRule **newRule, int noback,
		int nofor, TranslationTableHeader **table) {
	CharsString ruleChars;
	CharsString ruleDots;
	TranslationTableCharacter *character;
	TranslationTableCharacter *cell = NULL;
	int k;
	if (!getRuleCharsText(nested, &ruleChars, lastToken)) return 0;
	if (!getRuleDotsPattern(nested, &ruleDots, lastToken)) return 0;
	if (ruleChars.length != 1) {
		compileError(nested, "Exactly one character is required.");
		return 0;
	}
	if (ruleDots.length < 1) {
		compileError(nested, "At least one cell is required.");
		return 0;
	}
	if (attributes & (CTC_UpperCase | CTC_LowerCase)) attributes |= CTC_Letter;
	character = addCharOrDots(nested, ruleChars.chars[0], 0, table);
	character->attributes |= attributes;
	character->uppercase = character->lowercase = character->realchar;
	for (k = ruleDots.length - 1; k >= 0; k -= 1) {
		cell = compile_findCharOrDots(ruleDots.chars[k], 1, *table);
		if (!cell) {
			cell = addCharOrDots(nested, ruleDots.chars[k], 1, table);
			cell->uppercase = cell->lowercase = cell->realchar;
		}
	}
	if (ruleDots.length == 1) {
		cell->attributes |= attributes;
		putCharAndDots(nested, ruleChars.chars[0], ruleDots.chars[0], table);
	}
	if (!addRule(nested, opcode, &ruleChars, &ruleDots, 0, 0, newRuleOffset, newRule,
				noback, nofor, table))
		return 0;
	return 1;
}

Project: liblouis
File: compileTranslationTable.c

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

Project: liblouis
File: compileTranslationTable.c

atTokenDelimiter(FileInfo *nested) {
	return nested->line[nested->linepos] <= 32;
}

Project: liblouis
File: compileTranslationTable.c

setDefaults(TranslationTableHeader *table) {
	if (!table->emphRules[emph1Rule][lenPhraseOffset])
		table->emphRules[emph1Rule][lenPhraseOffset] = 4;
	if (!table->emphRules[emph2Rule][lenPhraseOffset])
		table->emphRules[emph2Rule][lenPhraseOffset] = 4;
	if (!table->emphRules[emph3Rule][lenPhraseOffset])
		table->emphRules[emph3Rule][lenPhraseOffset] = 4;
	if (table->numPasses == 0) table->numPasses = 1;
	return 1;
}

Project: liblouis
File: compileTranslationTable.c

lou_getDataPath(void) {
	return dataPathPtr;
}

Project: liblouis
File: compileTranslationTable.c

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