summaryrefslogtreecommitdiffstats
path: root/third_party/liblouis
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/liblouis')
-rw-r--r--third_party/liblouis/README.chromium11
-rw-r--r--third_party/liblouis/liblouis_list_tables.py88
-rw-r--r--third_party/liblouis/liblouis_untrusted.gyp183
-rw-r--r--third_party/liblouis/nacl_wrapper/liblouis_instance.cc323
-rw-r--r--third_party/liblouis/nacl_wrapper/liblouis_instance.h92
-rw-r--r--third_party/liblouis/nacl_wrapper/liblouis_module.cc49
-rw-r--r--third_party/liblouis/nacl_wrapper/liblouis_module.h39
-rw-r--r--third_party/liblouis/nacl_wrapper/liblouis_wrapper.cc204
-rw-r--r--third_party/liblouis/nacl_wrapper/liblouis_wrapper.h58
-rw-r--r--third_party/liblouis/nacl_wrapper/liblouis_wrapper_browsertest.cc18
-rw-r--r--third_party/liblouis/nacl_wrapper/translation_params.h32
-rw-r--r--third_party/liblouis/nacl_wrapper/translation_result.h33
-rw-r--r--third_party/liblouis/overrides/liblouis/compileTranslationTable.c5219
-rw-r--r--third_party/liblouis/overrides/liblouis/config.h18
-rw-r--r--third_party/liblouis/overrides/liblouis/liblouis.h146
-rw-r--r--third_party/liblouis/overrides/tables/braille-patterns.cti287
-rw-r--r--third_party/liblouis/overrides/tables/da.ctb280
-rw-r--r--third_party/liblouis/tables.json398
18 files changed, 7477 insertions, 1 deletions
diff --git a/third_party/liblouis/README.chromium b/third_party/liblouis/README.chromium
index 866732a..3178a46 100644
--- a/third_party/liblouis/README.chromium
+++ b/third_party/liblouis/README.chromium
@@ -15,4 +15,13 @@ libloius is used in a native client binary in ChromeVox and not linked into
Chrome itself.
Local Modifications:
-...
+
+* Add manually created liblouis.h.
+* On Android: log to logcat.
+* Fix backtranslation to not output unicode braille patterns
+ (svn r838)
+* Fix 3 letters in Danish 8 dot braille table, reverting part of svn r238.
+* Fix out-of-bounds array access (code removed by upstream).
+* Fix compiler warnings (part of svn r856).
+* Add tables.json a list of tables with metadata.
+* Add a wrapper to expose the library in native client.
diff --git a/third_party/liblouis/liblouis_list_tables.py b/third_party/liblouis/liblouis_list_tables.py
new file mode 100644
index 0000000..79b0c10
--- /dev/null
+++ b/third_party/liblouis/liblouis_list_tables.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import re
+import sys
+
+import json
+import optparse
+
+# Matches the include statement in the braille table files.
+INCLUDE_RE = re.compile(r"^\s*include\s+([^#\s]+)")
+
+
+def Error(msg):
+ print >> sys.stderr, 'liblouis_list_tables: %s' % msg
+ sys.exit(1)
+
+
+def ToNativePath(pathname):
+ return os.path.sep.join(pathname.split('/'))
+
+
+def LoadTablesFile(filename):
+ with open(ToNativePath(filename), 'r') as fh:
+ return json.load(fh)
+
+
+def FindFile(filename, directories):
+ for d in directories:
+ fullname = '/'.join([d, filename])
+ if os.path.isfile(ToNativePath(fullname)):
+ return fullname
+ Error('File not found: %s' % filename)
+
+
+def GetIncludeFiles(filename):
+ result = []
+ with open(ToNativePath(filename), 'r') as fh:
+ for line in fh.xreadlines():
+ match = INCLUDE_RE.match(line)
+ if match:
+ result.append(match.group(1))
+ return result
+
+
+def ProcessFile(output_set, filename, directories):
+ fullname = FindFile(filename, directories)
+ if fullname in output_set:
+ return
+ output_set.add(fullname)
+ for include_file in GetIncludeFiles(fullname):
+ ProcessFile(output_set, include_file, directories)
+
+
+def DoMain(argv):
+ "Entry point for gyp's pymod_do_main command."
+ parser = optparse.OptionParser()
+ # Give a clearer error message when this is used as a module.
+ parser.prog = 'liblouis_list_tables'
+ parser.set_usage('usage: %prog [options] listfile')
+ parser.add_option('-D', '--directory', dest='directories',
+ action='append', help='Where to search for table files')
+ (options, args) = parser.parse_args(argv)
+
+ if len(args) != 1:
+ parser.error('Expecting exactly one argument')
+ if not options.directories:
+ parser.error('At least one --directory option must be specified')
+
+ tables = LoadTablesFile(args[0])
+ output_set = set()
+ for table in tables:
+ ProcessFile(output_set, table['fileName'], options.directories)
+ return '\n'.join(output_set)
+
+
+def main(argv):
+ print DoMain(argv[1:])
+
+
+if __name__ == '__main__':
+ try:
+ sys.exit(main(sys.argv))
+ except KeyboardInterrupt:
+ Error('interrupted')
diff --git a/third_party/liblouis/liblouis_untrusted.gyp b/third_party/liblouis/liblouis_untrusted.gyp
new file mode 100644
index 0000000..530425f
--- /dev/null
+++ b/third_party/liblouis/liblouis_untrusted.gyp
@@ -0,0 +1,183 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ 'braille_test_data_dir': '<(PRODUCT_DIR)/chromevox_test_data/braille',
+ 'braille_chromevox_dir': '<(PRODUCT_DIR)/resources/chromeos/chromevox/chromevox/background/braille',
+ 'table_files': [
+ '>!@pymod_do_main(liblouis_list_tables -D overrides/tables -D src/tables tables.json)',
+ ],
+ },
+ # x86 targets build both 32 and 64 bit binaries by default. We only need
+ # the one that matches our target architecture.
+ 'target_defaults': {
+ 'conditions': [
+ ['target_arch=="ia32"', {
+ 'variables': {
+ 'enable_x86_64': 0,
+ },
+ }],
+ ['target_arch=="x64"', {
+ 'variables': {
+ 'enable_x86_32': 0,
+ },
+ }],
+ ],
+ },
+ 'includes': [
+ '../../build/common_untrusted.gypi',
+ ],
+ 'conditions': [
+ ['disable_nacl==0 and disable_nacl_untrusted==0', {
+ 'targets': [
+ {
+ 'target_name': 'liblouis_untrusted',
+ 'type': 'none',
+ 'variables': {
+ 'nacl_untrusted_build': 1,
+ 'nlib_target': 'liblouis_untrusted.a',
+ 'build_newlib': 1,
+ },
+ 'include_dirs': [
+ 'overrides/liblouis',
+ 'src/liblouis',
+ '.',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ 'overrides',
+ ],
+ },
+ 'sources': [
+ 'overrides/liblouis/config.h',
+ 'overrides/liblouis/liblouis.h',
+ 'overrides/liblouis/compileTranslationTable.c',
+ 'src/liblouis/lou_backTranslateString.c',
+ 'src/liblouis/lou_translateString.c',
+ 'src/liblouis/transcommon.ci',
+ 'src/liblouis/wrappers.c',
+ ],
+ 'dependencies': [
+ '../../native_client/tools.gyp:prep_toolchain',
+ ],
+ },
+
+ {
+ 'target_name': 'liblouis_nacl_wrapper_untrusted',
+ 'type': 'none',
+ 'variables': {
+ 'nacl_untrusted_build': 1,
+ 'nexe_target': 'liblouis_nacl',
+ 'out_newlib64': '<(braille_test_data_dir)/>(nexe_target)_x86_64.nexe',
+ 'out_newlib32': '<(braille_test_data_dir)/>(nexe_target)_x86_32.nexe',
+ 'out_newlib_arm': '<(braille_test_data_dir)/>(nexe_target)_arm.nexe',
+ 'build_newlib': 1,
+ 'extra_args': [
+ '--strip-debug',
+ ],
+ 'nmf': '<(braille_test_data_dir)/>(nexe_target).nmf',
+ 'target_conditions': [
+ ['enable_x86_64==1', {
+ 'nexe_files': ['>(out_newlib64)'],
+ }],
+ ['enable_x86_32==1', {
+ 'nexe_files': ['>(out_newlib32)'],
+ }],
+ ['enable_arm==1', {
+ 'nexe_files': ['>(out_newlib_arm)'],
+ }],
+ ],
+ },
+ 'sources': [
+ 'nacl_wrapper/liblouis_instance.h',
+ 'nacl_wrapper/liblouis_instance.cc',
+ 'nacl_wrapper/liblouis_module.h',
+ 'nacl_wrapper/liblouis_module.cc',
+ 'nacl_wrapper/liblouis_wrapper.h',
+ 'nacl_wrapper/liblouis_wrapper.cc',
+ 'nacl_wrapper/translation_params.h',
+ 'nacl_wrapper/translation_result.h',
+ ],
+ 'link_flags': [
+ '-lppapi',
+ '-lppapi_cpp',
+ '-llouis_untrusted',
+ '-ljsoncpp_untrusted',
+ '-lpthread',
+ '-lnacl_io',
+ ],
+ 'dependencies': [
+ '../../native_client/src/untrusted/nacl/nacl.gyp:nacl_lib',
+ '../../native_client/tools.gyp:prep_toolchain',
+ '../../native_client_sdk/native_client_sdk_untrusted.gyp:nacl_io_untrusted',
+ '../../ppapi/native_client/native_client.gyp:ppapi_lib',
+ '../../ppapi/ppapi_untrusted.gyp:ppapi_cpp_lib',
+ '../jsoncpp/jsoncpp_untrusted.gyp:jsoncpp_untrusted',
+ 'liblouis_untrusted',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'Generate NEWLIB NMF',
+ 'inputs': [
+ '>@(nexe_files)',
+ ],
+ 'outputs': ['>(nmf)'],
+ 'action': [
+ 'python',
+ '<(DEPTH)/native_client_sdk/src/tools/create_nmf.py',
+ '>@(_inputs)',
+ '--output=>(nmf)',
+ ],
+ },
+ ],
+ # Copy specific files into the product directory to avoid
+ # copying over the unstripped binary file.
+ 'copies': [
+ {
+ 'destination': '<(braille_chromevox_dir)',
+ 'files': [
+ '<(nmf)',
+ '>@(nexe_files)',
+ 'tables.json',
+ ],
+ },
+ {
+ 'destination': '<(braille_chromevox_dir)/tables',
+ 'files': [
+ '<@(table_files)',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'liblouis_test_data',
+ 'type': 'none',
+ 'variables': {
+ 'test_extension_dir': '<(DEPTH)/chrome/test/data/chromeos/liblouis_nacl',
+ },
+ 'dependencies': [
+ 'liblouis_nacl_wrapper_untrusted',
+ ],
+ 'copies': [
+ {
+ 'destination': '<(braille_test_data_dir)',
+ 'files': [
+ 'tables.json',
+ '<(test_extension_dir)/manifest.json',
+ '<(test_extension_dir)/test.js',
+ ],
+ },
+ {
+ 'destination': '<(braille_test_data_dir)/tables',
+ 'files': [
+ '<@(table_files)',
+ ],
+ },
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/third_party/liblouis/nacl_wrapper/liblouis_instance.cc b/third_party/liblouis/nacl_wrapper/liblouis_instance.cc
new file mode 100644
index 0000000..e92b9a0
--- /dev/null
+++ b/third_party/liblouis/nacl_wrapper/liblouis_instance.cc
@@ -0,0 +1,323 @@
+// Copyright 2013 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "liblouis_instance.h"
+
+#include <cstdio>
+#include <cstring>
+#include <sys/mount.h>
+#include <vector>
+
+#include "nacl_io/nacl_io.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/cpp/module.h"
+
+#include "translation_result.h"
+
+namespace {
+
+static const char kHexadecimalChars[] = "0123456789abcdef";
+
+// Converts a vector of bytes into a (lowercase) hexadecimal string.
+static void BytesToHexString(const std::vector<unsigned char>& bytes,
+ std::string* out) {
+ std::string hex;
+ hex.reserve(bytes.size() * 2);
+ for (size_t i = 0; i < bytes.size(); ++i) {
+ unsigned char byte = bytes[i];
+ hex.push_back(kHexadecimalChars[byte >> 4]);
+ hex.push_back(kHexadecimalChars[byte & 0x0f]);
+ }
+ out->swap(hex);
+}
+
+// Converts a hexadecimal string to a vector of bytes.
+// Returns false on failure.
+static bool HexStringToBytes(const std::string& hex,
+ std::vector<unsigned char>* out) {
+ if (hex.size() % 2 != 0) {
+ return false;
+ }
+
+ std::vector<unsigned char> bytes;
+ bytes.reserve(hex.size() / 2);
+ for (size_t i = 0; i < hex.size(); i += 2) {
+ unsigned char byte;
+ char ch = hex[i];
+ if ('0' <= ch && ch <= '9') {
+ byte = (ch - '0') << 4;
+ } else if ('a' <= ch && ch <= 'f') {
+ byte = (ch - 'a' + 10) << 4;
+ } else if ('A' <= ch && ch <= 'F') {
+ byte = (ch - 'A' + 10) << 4;
+ } else {
+ return false;
+ }
+ ch = hex[i+1];
+ if ('0' <= ch && ch <= '9') {
+ byte |= ch - '0';
+ } else if ('a' <= ch && ch <= 'f') {
+ byte |= ch - 'a' + 10;
+ } else if ('A' <= ch && ch <= 'F') {
+ byte |= ch - 'A' + 10;
+ } else {
+ return false;
+ }
+ bytes.push_back(byte);
+ }
+ out->swap(bytes);
+ return true;
+}
+
+template <typename T>
+static void CopyVectorToJson(const std::vector<T>& vec, Json::Value* out) {
+ Json::Value result(Json::arrayValue);
+ result.resize(vec.size());
+ for (size_t i = 0; i < vec.size(); ++i) {
+ result[i] = vec[i];
+ }
+ out->swap(result);
+}
+
+} // namespace
+
+
+namespace liblouis_nacl {
+
+// Well-known strings used for configuration.
+static const char kTablesDirKey[] = "tablesdir";
+static const char kTablesDirDefault[] = "tables";
+
+// Well-known strings used in JSON messages.
+static const char kCommandKey[] = "command";
+static const char kMessageIdKey[] = "message_id";
+static const char kInReplyToKey[] = "in_reply_to";
+static const char kErrorKey[] = "error";
+static const char kTableNameKey[] = "table_name";
+static const char kSuccessKey[] = "success";
+static const char kTextKey[] = "text";
+static const char kCellsKey[] = "cells";
+static const char kCursorPositionKey[] = "cursor_position";
+static const char kTextToBrailleKey[] = "text_to_braille";
+static const char kBrailleToTextKey[] = "braille_to_text";
+static const char kCheckTableCommand[] = "CheckTable";
+static const char kTranslateCommand[] = "Translate";
+static const char kBackTranslateCommand[] = "BackTranslate";
+
+LibLouisInstance::LibLouisInstance(PP_Instance instance)
+ : pp::Instance(instance), liblouis_thread_(this), cc_factory_(this) {}
+
+LibLouisInstance::~LibLouisInstance() {}
+
+bool LibLouisInstance::Init(uint32_t argc, const char* argn[],
+ const char* argv[]) {
+ const char* tables_dir = kTablesDirDefault;
+ for (size_t i = 0; i < argc; ++i) {
+ if (strcmp(argn[i], kTablesDirKey) == 0) {
+ tables_dir = argv[i];
+ }
+ }
+
+ nacl_io_init_ppapi(pp_instance(),
+ pp::Module::Get()->get_browser_interface());
+ if (mount(tables_dir, liblouis_.tables_dir(), "httpfs", 0, "") != 0) {
+ // TODO(jbroman): report this error.
+ return false;
+ }
+
+ return liblouis_thread_.Start();
+}
+
+void LibLouisInstance::HandleMessage(const pp::Var& var_message) {
+ if (!var_message.is_string()) {
+ PostError("expected message to be a JSON string");
+ return;
+ }
+
+ Json::Value message;
+ Json::Reader reader;
+ bool parsed = reader.parse(var_message.AsString(),
+ message, false /* collectComments */);
+ if (!parsed) {
+ PostError("expected message to be a JSON string");
+ return;
+ }
+
+ Json::Value message_id = message[kMessageIdKey];
+ if (!message_id.isString()) {
+ PostError("expected message_id string");
+ return;
+ }
+ std::string message_id_str = message_id.asString();
+
+ Json::Value command = message[kCommandKey];
+ if (!command.isString()) {
+ PostError("expected command string", message_id_str);
+ return;
+ }
+
+ std::string command_str = command.asString();
+ if (command_str == kCheckTableCommand) {
+ HandleCheckTable(message, message_id_str);
+ } else if (command_str == kTranslateCommand) {
+ HandleTranslate(message, message_id_str);
+ } else if (command_str == kBackTranslateCommand) {
+ HandleBackTranslate(message, message_id_str);
+ } else {
+ PostError("unknown command", message_id_str);
+ }
+}
+
+void LibLouisInstance::PostReply(Json::Value reply,
+ const std::string& in_reply_to) {
+ Json::FastWriter writer;
+ reply[kInReplyToKey] = in_reply_to;
+ pp::Var var_reply(writer.write(reply));
+ PostMessage(var_reply);
+}
+
+void LibLouisInstance::PostError(const std::string& error_message) {
+ Json::FastWriter writer;
+ Json::Value reply(Json::objectValue);
+ reply[kErrorKey] = error_message;
+ pp::Var var_reply(writer.write(reply));
+ PostMessage(var_reply);
+}
+
+void LibLouisInstance::PostError(const std::string& error_message,
+ const std::string& in_reply_to) {
+ Json::FastWriter writer;
+ Json::Value reply(Json::objectValue);
+ reply[kErrorKey] = error_message;
+ reply[kInReplyToKey] = in_reply_to;
+ reply[kSuccessKey] = false;
+ pp::Var var_reply(writer.write(reply));
+ PostMessage(var_reply);
+}
+
+void LibLouisInstance::HandleCheckTable(const Json::Value& message,
+ const std::string& message_id) {
+ Json::Value table_name = message[kTableNameKey];
+ if (!table_name.isString()) {
+ PostError("expected table_name to be a string", message_id);
+ return;
+ }
+ PostWorkToBackground(cc_factory_.NewCallback(
+ &LibLouisInstance::CheckTableInBackground,
+ table_name.asString(), message_id));
+}
+
+void LibLouisInstance::CheckTableInBackground(int32_t result,
+ const std::string& table_name, const std::string& message_id) {
+ if (result != PP_OK) {
+ PostError("failed to transfer call to background thread", message_id);
+ return;
+ }
+ bool success = liblouis_.CheckTable(table_name);
+ Json::Value reply(Json::objectValue);
+ reply[kSuccessKey] = success;
+ PostReply(reply, message_id);
+}
+
+void LibLouisInstance::HandleTranslate(const Json::Value& message,
+ const std::string& message_id) {
+ Json::Value table_name = message[kTableNameKey];
+ Json::Value text = message[kTextKey];
+ Json::Value cursor_position = message[kCursorPositionKey];
+ if (!table_name.isString()) {
+ PostError("expected table_name to be a string", message_id);
+ return;
+ } else if (!text.isString()) {
+ PostError("expected text to be a string", message_id);
+ return;
+ } else if (!cursor_position.isNull() && !cursor_position.isIntegral()) {
+ PostError("expected cursor_position to be null or integral", message_id);
+ return;
+ }
+ TranslationParams params;
+ params.table_name = table_name.asString();
+ params.text = text.asString();
+ params.cursor_position = cursor_position.isIntegral() ?
+ cursor_position.asInt() : -1;
+ PostWorkToBackground(cc_factory_.NewCallback(
+ &LibLouisInstance::TranslateInBackground,
+ params, message_id));
+}
+
+void LibLouisInstance::TranslateInBackground(int32_t result,
+ const TranslationParams& params, const std::string& message_id) {
+ if (result != PP_OK) {
+ PostError("failed to transfer call to background thread", message_id);
+ return;
+ }
+ TranslationResult translation_result;
+ bool success = liblouis_.Translate(params, &translation_result);
+ Json::Value reply(Json::objectValue);
+ reply[kSuccessKey] = success;
+ if (success) {
+ std::string hex_cells;
+ Json::Value text_to_braille;
+ Json::Value braille_to_text;
+ BytesToHexString(translation_result.cells, &hex_cells);
+ CopyVectorToJson(translation_result.text_to_braille, &text_to_braille);
+ CopyVectorToJson(translation_result.braille_to_text, &braille_to_text);
+ reply[kCellsKey] = hex_cells;
+ reply[kTextToBrailleKey] = text_to_braille;
+ reply[kBrailleToTextKey] = braille_to_text;
+ if (translation_result.cursor_position >= 0) {
+ reply[kCursorPositionKey] = translation_result.cursor_position;
+ }
+ }
+ PostReply(reply, message_id);
+}
+
+void LibLouisInstance::HandleBackTranslate(const Json::Value& message,
+ const std::string& message_id) {
+ Json::Value table_name = message[kTableNameKey];
+ Json::Value cells = message[kCellsKey];
+ if (!table_name.isString()) {
+ PostError("expected table_name to be a string", message_id);
+ return;
+ } else if (!cells.isString()) {
+ PostError("expected cells to be a string", message_id);
+ return;
+ }
+ std::vector<unsigned char> cells_vector;
+ if (!HexStringToBytes(cells.asString(), &cells_vector)) {
+ PostError("expected cells to be a valid hexadecimal string", message_id);
+ return;
+ }
+ PostWorkToBackground(cc_factory_.NewCallback(
+ &LibLouisInstance::BackTranslateInBackground,
+ table_name.asString(), cells_vector, message_id));
+}
+
+void LibLouisInstance::BackTranslateInBackground(int32_t result,
+ const std::string& table_name, const std::vector<unsigned char>& cells,
+ const std::string& message_id) {
+ if (result != PP_OK) {
+ PostError("failed to transfer call to background thread", message_id);
+ return;
+ }
+ std::string text;
+ bool success = liblouis_.BackTranslate(table_name, cells, &text);
+ Json::Value reply(Json::objectValue);
+ reply[kSuccessKey] = success;
+ if (success) {
+ reply[kTextKey] = text;
+ }
+ PostReply(reply, message_id);
+}
+
+} // namespace liblouis_nacl
diff --git a/third_party/liblouis/nacl_wrapper/liblouis_instance.h b/third_party/liblouis/nacl_wrapper/liblouis_instance.h
new file mode 100644
index 0000000..d202a76
--- /dev/null
+++ b/third_party/liblouis/nacl_wrapper/liblouis_instance.h
@@ -0,0 +1,92 @@
+// Copyright 2013 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#ifndef LIBLOUIS_NACL_LIBLOUIS_INSTANCE_H_
+#define LIBLOUIS_NACL_LIBLOUIS_INSTANCE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "json/json.h"
+#include "liblouis_wrapper.h"
+#include "ppapi/c/pp_instance.h"
+#include "ppapi/cpp/completion_callback.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/message_loop.h"
+#include "ppapi/cpp/var.h"
+#include "ppapi/utility/completion_callback_factory.h"
+#include "ppapi/utility/threading/simple_thread.h"
+#include "translation_params.h"
+
+namespace liblouis_nacl {
+
+class LibLouisInstance : public pp::Instance {
+ public:
+ explicit LibLouisInstance(PP_Instance instance);
+ virtual ~LibLouisInstance();
+
+ virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]);
+ virtual void HandleMessage(const pp::Var& var_message);
+
+ private:
+ // Post work to the background (liblouis) thread.
+ int32_t PostWorkToBackground(const pp::CompletionCallback& callback) {
+ return liblouis_thread_.message_loop().PostWork(callback);
+ }
+
+ // Encodes a message as JSON and sends it over to JavaScript.
+ void PostReply(Json::Value reply, const std::string& in_reply_to);
+
+ // Posts an error response to JavaScript.
+ void PostError(const std::string& error);
+
+ // Posts an error response to JavaScript, with the message ID of the call
+ // which caused it.
+ void PostError(const std::string& error, const std::string& in_reply_to);
+
+ // Parses and executes a table check command.
+ void HandleCheckTable(const Json::Value& message,
+ const std::string& message_id);
+
+ // Called to check a table on a background thread.
+ void CheckTableInBackground(int32_t result, const std::string& table_name,
+ const std::string& message_id);
+
+ // Parses and executes a translation command.
+ void HandleTranslate(const Json::Value& message,
+ const std::string& message_id);
+
+ // Called to translate text on a background thread.
+ void TranslateInBackground(int32_t result, const TranslationParams& params,
+ const std::string& message_id);
+
+ // Parses and executes a back translation command.
+ void HandleBackTranslate(const Json::Value& message,
+ const std::string& message_id);
+
+ // Called to back-translate text on a background thread.
+ void BackTranslateInBackground(int32_t result,
+ const std::string& table_name, const std::vector<unsigned char>& cells,
+ const std::string& message_id);
+
+ LibLouisWrapper liblouis_;
+ pp::SimpleThread liblouis_thread_;
+ pp::CompletionCallbackFactory<LibLouisInstance> cc_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(LibLouisInstance);
+};
+
+} // namespace liblouis_nacl
+
+#endif // LIBLOUIS_NACL_LIBLOUIS_INSTANCE_H_
diff --git a/third_party/liblouis/nacl_wrapper/liblouis_module.cc b/third_party/liblouis/nacl_wrapper/liblouis_module.cc
new file mode 100644
index 0000000..1c0170d2
--- /dev/null
+++ b/third_party/liblouis/nacl_wrapper/liblouis_module.cc
@@ -0,0 +1,49 @@
+// Copyright 2013 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "liblouis_module.h"
+
+#include <cstddef>
+
+#include "liblouis_instance.h"
+
+namespace liblouis_nacl {
+
+LibLouisModule::LibLouisModule() : pp::Module() {}
+
+LibLouisModule::~LibLouisModule() {}
+
+pp::Instance* LibLouisModule::CreateInstance(PP_Instance instance) {
+ static bool created = false;
+ if (!created) {
+ created = true;
+ return new LibLouisInstance(instance);
+ }
+ return NULL;
+}
+
+} // namespace liblouis_nacl
+
+namespace pp {
+
+Module* CreateModule() {
+ static bool created = false;
+ if (!created) {
+ created = true;
+ return new liblouis_nacl::LibLouisModule();
+ }
+ return NULL;
+}
+
+} // namespace pp
diff --git a/third_party/liblouis/nacl_wrapper/liblouis_module.h b/third_party/liblouis/nacl_wrapper/liblouis_module.h
new file mode 100644
index 0000000..09d2493
--- /dev/null
+++ b/third_party/liblouis/nacl_wrapper/liblouis_module.h
@@ -0,0 +1,39 @@
+// Copyright 2013 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#ifndef LIBLOUIS_NACL_LIBLOUIS_MODULE_H_
+#define LIBLOUIS_NACL_LIBLOUIS_MODULE_H_
+
+#include "base/basictypes.h"
+#include "ppapi/c/pp_instance.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/module.h"
+
+namespace liblouis_nacl {
+
+// Native Client module which contains liblouis.
+class LibLouisModule : public pp::Module {
+ public:
+ LibLouisModule();
+ virtual ~LibLouisModule();
+
+ virtual pp::Instance* CreateInstance(PP_Instance instance);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LibLouisModule);
+};
+
+} // namespace liblouis_nacl
+
+#endif // LIBLOUIS_NACL_LIBLOUIS_MODULE_H_
diff --git a/third_party/liblouis/nacl_wrapper/liblouis_wrapper.cc b/third_party/liblouis/nacl_wrapper/liblouis_wrapper.cc
new file mode 100644
index 0000000..390e192
--- /dev/null
+++ b/third_party/liblouis/nacl_wrapper/liblouis_wrapper.cc
@@ -0,0 +1,204 @@
+// Copyright 2013 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "liblouis_wrapper.h"
+
+#include <cstddef>
+
+#include "liblouis/liblouis.h"
+
+namespace {
+
+// Decodes UTF-8 into 16-bit wide characters.
+// This implementation is very permissive and may miss encoding errors.
+// It ignores charaters which are not in the Unicode Basic Multilingual Plane.
+// TODO(jbroman): Handle more than BMP if liblouis changes to accept UTF-16.
+static bool DecodeUtf8(const std::string& in, std::vector<widechar>* out) {
+ int len = in.length();
+ std::vector<widechar> result;
+ result.reserve(len);
+ int i = 0;
+ while (i < len) {
+ int ch = static_cast<unsigned char>(in[i++]);
+ widechar cp;
+ if ((ch & 0x80) == 0x00) { // U+0000 - U+007F
+ cp = ch;
+ } else if ((ch & 0xe0) == 0xc0 && i < len) { // U+0080 - U+07FF
+ cp = (ch & 0x1f) << 6;
+ ch = static_cast<unsigned char>(in[i++]);
+ cp |= (ch & 0x3f);
+ } else if ((ch & 0xf0) == 0xe0 && i+1 < len) { // U+0800 - U+FFFF
+ cp = (ch & 0x0f) << 12;
+ ch = static_cast<unsigned char>(in[i++]);
+ cp |= (ch & 0x3f) << 6;
+ ch = static_cast<unsigned char>(in[i++]);
+ cp |= (ch & 0x3f);
+ } else if ((ch & 0xf8) == 0xf0 && i+2 < len) { // U+10000 - U+1FFFFF
+ i += 3;
+ continue;
+ } else if ((ch & 0xfc) == 0xf8 && i+3 < len) { // U+200000 - U+3FFFFFF
+ i += 4;
+ continue;
+ } else if ((ch & 0xfe) == 0xfc && i+4 < len) { // U+4000000 - U+7FFFFFFF
+ i += 5;
+ continue;
+ } else {
+ // Invalid first code point.
+ return false;
+ }
+ result.push_back(cp);
+ }
+ out->swap(result);
+ return true;
+}
+
+// Encodes 16-bit wide characters into UTF-8.
+// This implementation is very permissive and may miss invalid code points in
+// its input.
+// TODO(jbroman): Handle more than BMP if widechar ever becomes larger.
+static bool EncodeUtf8(const std::vector<widechar>& in, std::string* out) {
+ std::string result;
+ result.reserve(in.size() * 2);
+ for (std::vector<widechar>::const_iterator it = in.begin(); it != in.end();
+ ++it) {
+ unsigned int cp = *it;
+ if (cp <= 0x007f) { // U+0000 - U+007F
+ result.push_back(static_cast<char>(cp));
+ } else if (cp <= 0x07ff) { // U+0080 - U+07FF
+ result.push_back(static_cast<char>(0xc0 | ((cp >> 6) & 0x1f)));
+ result.push_back(static_cast<char>(0x80 | (cp & 0x3f)));
+ } else if (cp <= 0xffff) { // U+0800 - U+FFFF
+ result.push_back(static_cast<char>(0xe0 | ((cp >> 12) & 0x0f)));
+ result.push_back(static_cast<char>(0x80 | ((cp >> 6) & 0x3f)));
+ result.push_back(static_cast<char>(0x80 | (cp & 0x3f)));
+ } else {
+ // This can't happen if widechar is 16 bits wide.
+ // TODO(jbroman): assert this
+ }
+ }
+ out->swap(result);
+ return true;
+}
+
+} // namespace
+
+
+namespace liblouis_nacl {
+
+LibLouisWrapper::LibLouisWrapper() {
+ char data_path[] = "/"; // Needed because lou_setDataPath takes a char*.
+ lou_setDataPath(data_path);
+}
+
+LibLouisWrapper::~LibLouisWrapper() {
+ lou_free();
+}
+
+const char* LibLouisWrapper::tables_dir() const {
+ return "/liblouis/tables";
+}
+
+bool LibLouisWrapper::CheckTable(const std::string& table_name) {
+ return lou_getTable(table_name.c_str()) != NULL;
+}
+
+bool LibLouisWrapper::Translate(const TranslationParams& params,
+ TranslationResult* out) {
+ // Convert the character set of the input text.
+ std::vector<widechar> inbuf;
+ if (!DecodeUtf8(params.text, &inbuf)) {
+ // TODO(jbroman): log this
+ return false;
+ }
+
+ int inlen = inbuf.size();
+ int outlen = inlen * 2; // TODO(jbroman): choose this size more accurately.
+ std::vector<widechar> outbuf(outlen);
+ std::vector<int> text_to_braille(inlen);
+ std::vector<int> braille_to_text(outlen);
+
+ // Compute the cursor position pointer to pass to liblouis.
+ int out_cursor_position;
+ int* out_cursor_position_ptr;
+ if (params.cursor_position < 0) {
+ out_cursor_position = -1;
+ out_cursor_position_ptr = NULL;
+ } else {
+ out_cursor_position = params.cursor_position;
+ out_cursor_position_ptr = &out_cursor_position;
+ }
+
+ // Invoke liblouis.
+ int result = lou_translate(params.table_name.c_str(),
+ &inbuf[0], &inlen, &outbuf[0], &outlen,
+ NULL /* typeform */, NULL /* spacing */,
+ &text_to_braille[0], &braille_to_text[0],
+ out_cursor_position_ptr, dotsIO /* mode */);
+ if (result == 0) {
+ // TODO(jbroman): log this
+ return false;
+ }
+
+ // Massage the result.
+ std::vector<unsigned char> cells;
+ cells.reserve(outlen);
+ for (int i = 0; i < outlen; i++) {
+ cells.push_back(outbuf[i]);
+ }
+ braille_to_text.resize(outlen);
+
+ // Return the translation result.
+ out->cells.swap(cells);
+ out->text_to_braille.swap(text_to_braille);
+ out->braille_to_text.swap(braille_to_text);
+ out->cursor_position = out_cursor_position;
+ return true;
+}
+
+bool LibLouisWrapper::BackTranslate(const std::string& table_name,
+ const std::vector<unsigned char>& cells, std::string* out) {
+ std::vector<widechar> inbuf;
+ inbuf.reserve(cells.size());
+ for (std::vector<unsigned char>::const_iterator it = cells.begin();
+ it != cells.end(); ++it) {
+ // Set the high-order bit to prevent liblouis from dropping empty cells.
+ inbuf.push_back(*it | 0x8000);
+ }
+ int inlen = inbuf.size();
+ int outlen = inlen * 2; // TODO(jbroman): choose this size more accurately.
+ std::vector<widechar> outbuf(outlen);
+
+ // Invoke liblouis.
+ int result = lou_backTranslateString(table_name.c_str(),
+ &inbuf[0], &inlen, &outbuf[0], &outlen,
+ NULL /* typeform */, NULL /* spacing */, dotsIO /* mode */);
+ if (result == 0) {
+ // TODO(njbroman): log this
+ return false;
+ }
+
+ // Massage the result.
+ outbuf.resize(outlen);
+ std::string text;
+ if (!EncodeUtf8(outbuf, &text)) {
+ // TODO(jbroman): log this
+ return false;
+ }
+
+ // Return the back translation result.
+ out->swap(text);
+ return true;
+}
+
+} // namespace liblouis_nacl
diff --git a/third_party/liblouis/nacl_wrapper/liblouis_wrapper.h b/third_party/liblouis/nacl_wrapper/liblouis_wrapper.h
new file mode 100644
index 0000000..dac2b4a
--- /dev/null
+++ b/third_party/liblouis/nacl_wrapper/liblouis_wrapper.h
@@ -0,0 +1,58 @@
+// Copyright 2013 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#ifndef LIBLOUIS_NACL_LIBLOUIS_WRAPPER_H_
+#define LIBLOUIS_NACL_LIBLOUIS_WRAPPER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "translation_params.h"
+#include "translation_result.h"
+
+namespace liblouis_nacl {
+
+// Encapsulates logic for interacting (synchronously) with liblouis.
+//
+// This class is *not* thread-safe; it should be used only from one thread.
+// Since the underlying library is not reentrant, only one instance should be
+// in use at a time.
+//
+// All input strings should be represented in UTF-8.
+class LibLouisWrapper {
+ public:
+ LibLouisWrapper();
+ ~LibLouisWrapper();
+
+ // Returns one of the paths where tables may be searched for.
+ const char* tables_dir() const;
+
+ // Loads, checks, and compiles the requested table.
+ // Returns true on success.
+ bool CheckTable(const std::string& table_name);
+
+ // Translates the given text and cursor position into braille.
+ bool Translate(const TranslationParams& params, TranslationResult* out);
+
+ // Translates the given braille cells into text.
+ bool BackTranslate(const std::string& table_name,
+ const std::vector<unsigned char>& cells, std::string* out);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LibLouisWrapper);
+};
+
+} // namespace liblouis_nacl
+
+#endif // LIBLOUIS_NACL_LIBLOUIS_WRAPPER_H_
diff --git a/third_party/liblouis/nacl_wrapper/liblouis_wrapper_browsertest.cc b/third_party/liblouis/nacl_wrapper/liblouis_wrapper_browsertest.cc
new file mode 100644
index 0000000..bc2fda2
--- /dev/null
+++ b/third_party/liblouis/nacl_wrapper/liblouis_wrapper_browsertest.cc
@@ -0,0 +1,18 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/base_paths.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "chrome/browser/extensions/extension_apitest.h"
+
+class LibLouisWrapperTest : public ExtensionApiTest {
+};
+
+IN_PROC_BROWSER_TEST_F(LibLouisWrapperTest, LibLouisLoad) {
+ ASSERT_TRUE(PathService::Get(base::DIR_EXE, &test_data_dir_));
+ test_data_dir_ = test_data_dir_.AppendASCII("chromevox_test_data");
+ LOG(ERROR) << "Test data dir: " << test_data_dir_.MaybeAsASCII();
+ ASSERT_TRUE(RunExtensionTest("braille")) << message_;
+}
diff --git a/third_party/liblouis/nacl_wrapper/translation_params.h b/third_party/liblouis/nacl_wrapper/translation_params.h
new file mode 100644
index 0000000..3505520
--- /dev/null
+++ b/third_party/liblouis/nacl_wrapper/translation_params.h
@@ -0,0 +1,32 @@
+// Copyright 2013 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#ifndef LIBLOUIS_NACL_TRANSLATION_PARAMS_H_
+#define LIBLOUIS_NACL_TRANSLATION_PARAMS_H_
+
+#include <string>
+
+namespace liblouis_nacl {
+
+// Struct containing the parameters of translation.
+struct TranslationParams {
+ public:
+ std::string table_name;
+ std::string text;
+ int cursor_position;
+};
+
+}
+
+#endif // LIBLOUIS_NACL_TRANSLATION_PARAMS_H_
diff --git a/third_party/liblouis/nacl_wrapper/translation_result.h b/third_party/liblouis/nacl_wrapper/translation_result.h
new file mode 100644
index 0000000..34a7beb
--- /dev/null
+++ b/third_party/liblouis/nacl_wrapper/translation_result.h
@@ -0,0 +1,33 @@
+// Copyright 2013 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#ifndef LIBLOUIS_NACL_TRANSLATION_RESULT_H_
+#define LIBLOUIS_NACL_TRANSLATION_RESULT_H_
+
+#include <vector>
+
+namespace liblouis_nacl {
+
+// Struct containing the result of translation.
+struct TranslationResult {
+ public:
+ std::vector<unsigned char> cells;
+ std::vector<int> text_to_braille;
+ std::vector<int> braille_to_text;
+ int cursor_position;
+};
+
+}
+
+#endif // LIBLOUIS_NACL_TRANSLATION_RESULT_H_
diff --git a/third_party/liblouis/overrides/liblouis/compileTranslationTable.c b/third_party/liblouis/overrides/liblouis/compileTranslationTable.c
new file mode 100644
index 0000000..8af65f2
--- /dev/null
+++ b/third_party/liblouis/overrides/liblouis/compileTranslationTable.c
@@ -0,0 +1,5219 @@
+/* liblouis Braille Translation and Back-Translation
+Library
+
+ Based on the Linux screenreader BRLTTY, copyright (C) 1999-2006 by
+ The BRLTTY Team
+
+ Copyright (C) 2004, 2005, 2006
+ ViewPlus Technologies, Inc. www.viewplus.com
+ and
+ JJB Software, Inc. www.jjb-software.com
+ All rights reserved
+
+ This file is free software; you can redistribute it and/or modify it
+ under the terms of the Lesser or Library GNU General Public License
+ as published by the
+ Free Software Foundation; either version 3, or (at your option) any
+ later version.
+
+ This file is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ Library GNU General Public License for more details.
+
+ You should have received a copy of the Library GNU General Public
+ License along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ Maintained by John J. Boyer john.boyer@jjb-software.com
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+//#include <unistd.h>
+
+#include "louis.h"
+#include "config.h"
+
+#define QUOTESUB 28 /*Stand-in for double quotes in strings */
+
+
+/* Contributed by Michel Such <michel.such@free.fr */
+#ifdef _WIN32
+
+/* Adapted from BRLTTY code (see sys_progs_wihdows.h) */
+
+#include <shlobj.h>
+
+static void
+noMemory (void)
+{
+ printf ("Insufficient memory: %s", strerror (errno), "\n");
+ exit (3);
+}
+
+static void *
+reallocWrapper (void *address, size_t size)
+{
+ if (!(address = realloc (address, size)) && size)
+ noMemory ();
+ return address;
+}
+
+static char *
+strdupWrapper (const char *string)
+{
+ char *address = strdup (string);
+ if (!address)
+ noMemory ();
+ return address;
+}
+
+char *EXPORT_CALL
+lou_getProgramPath (void)
+{
+ char *path = NULL;
+ HMODULE handle;
+
+ if ((handle = GetModuleHandle (NULL)))
+ {
+ size_t size = 0X80;
+ char *buffer = NULL;
+
+ while (1)
+ {
+ buffer = reallocWrapper (buffer, size <<= 1);
+
+ {
+ DWORD length = GetModuleFileName (handle, buffer, size);
+
+ if (!length)
+ {
+ printf ("GetModuleFileName\n");
+ exit (3);
+ 3;
+ }
+
+ if (length < size)
+ {
+ buffer[length] = 0;
+ path = strdupWrapper (buffer);
+
+ while (length > 0)
+ if (path[--length] == '\\')
+ break;
+
+ strncpy (path, path, length + 1);
+ path[length + 1] = '\0';
+ break;
+ }
+ }
+ }
+
+ free (buffer);
+ }
+ else
+ {
+ printf ("GetModuleHandle\n");
+ exit (3);
+ }
+
+ return path;
+}
+
+#define PATH_SEP ';'
+#define DIR_SEP '\\'
+#else
+#define PATH_SEP ':'
+#define DIR_SEP '/'
+#endif
+/* End of MS contribution */
+
+#ifdef ANDROID
+#include "android/log.h"
+#endif
+
+/* The folowing variables and functions make it possible to specify the
+* path on which all tables for liblouis and all files for liblouisutdml,
+* in their proper directories, will be found.
+*/
+
+static char dataPath[MAXSTRING];
+static char *dataPathPtr;
+
+char *EXPORT_CALL
+lou_setDataPath (char *path)
+{
+ dataPathPtr = NULL;
+ if (path == NULL)
+ return NULL;
+ strcpy (dataPath, path);
+ dataPathPtr = dataPath;
+ return dataPathPtr;
+}
+
+char *EXPORT_CALL
+lou_getDataPath ()
+{
+ return dataPathPtr;
+}
+
+/* End of dataPath code.*/
+
+static char tablePath[MAXSTRING];
+static FILE *logFile = NULL;
+static char initialLogFileName[256];
+
+void EXPORT_CALL
+lou_logFile (const char *fileName)
+{
+ if (fileName == NULL || fileName[0] == 0)
+ return;
+ if (initialLogFileName[0] == 0)
+ strcpy (initialLogFileName, fileName);
+ logFile = fopen (fileName, "wb");
+ if (logFile == NULL && initialLogFileName[0] != 0)
+ logFile = fopen (initialLogFileName, "wb");
+ if (logFile == NULL)
+ {
+ fprintf (stderr, "Cannot open log file %s\n", fileName);
+ logFile = stderr;
+ }
+}
+
+void EXPORT_CALL
+lou_logPrint (char *format, ...)
+{
+#ifndef __SYMBIAN32__
+ va_list argp;
+ if (format == NULL)
+ return;
+ if (logFile == NULL && initialLogFileName[0] != 0)
+ logFile = fopen (initialLogFileName, "wb");
+ if (logFile == NULL)
+ logFile = stderr;
+ va_start (argp, format);
+#ifndef ANDROID
+ vfprintf (logFile, format, argp);
+ fprintf (logFile, "\n");
+#else
+ __android_log_vprint(ANDROID_LOG_DEBUG, "liblouis", format, argp);
+#endif
+ va_end (argp);
+#endif
+}
+
+void EXPORT_CALL
+lou_logEnd (void)
+{
+ if (logFile != NULL)
+ fclose (logFile);
+ logFile = NULL;
+}
+
+static int
+eqasc2uni (const unsigned char *a, const widechar * b, const int len)
+{
+ int k;
+ for (k = 0; k < len; k++)
+ if ((widechar) a[k] != b[k])
+ return 0;
+ return 1;
+}
+
+typedef struct
+{
+ widechar length;
+ widechar chars[MAXSTRING];
+}
+CharsString;
+
+static int errorCount;
+static int warningCount;
+static TranslationTableHeader *table;
+static TranslationTableOffset tableSize;
+static TranslationTableOffset tableUsed;
+
+static const char *characterClassNames[] = {
+ "space",
+ "letter",
+ "digit",
+ "punctuation",
+ "uppercase",
+ "lowercase",
+ "math",
+ "sign",
+ "litdigit",
+ NULL
+};
+
+struct CharacterClass
+{
+ struct CharacterClass *next;
+ TranslationTableCharacterAttributes attribute;
+ widechar length;
+ widechar name[1];
+};
+static struct CharacterClass *characterClasses;
+static TranslationTableCharacterAttributes characterClassAttribute;
+
+static const char *opcodeNames[CTO_None] = {
+ "include",
+ "locale",
+ "undefined",
+ "capsign",
+ "begcaps",
+ "lenbegcaps",
+ "endcaps",
+ "firstwordcaps",
+ "lastwordbeforecaps",
+ "lastwordaftercaps",
+ "lencapsphrase",
+ "letsign",
+ "noletsignbefore",
+ "noletsign",
+ "noletsignafter",
+ "numsign",
+ "firstwordital",
+ "italsign",
+ "lastworditalbefore",
+ "lastworditalafter",
+ "begital",
+ "firstletterital",
+ "endital",
+ "lastletterital",
+ "singleletterital",
+ "italword",
+ "lenitalphrase",
+ "firstwordbold",
+ "boldsign",
+ "lastwordboldbefore",
+ "lastwordboldafter",
+ "begbold",
+ "firstletterbold",
+ "endbold",
+ "lastletterbold",
+ "singleletterbold",
+ "boldword",
+ "lenboldphrase",
+ "firstwordunder",
+ "undersign",
+ "lastwordunderbefore",
+ "lastwordunderafter",
+ "begunder",
+ "firstletterunder",
+ "endunder",
+ "lastletterunder",
+ "singleletterunder",
+ "underword",
+ "lenunderphrase",
+ "begcomp",
+ "compbegemph1",
+ "compendemph1",
+ "compbegemph2",
+ "compendemph2",
+ "compbegemph3",
+ "compendemph3",
+ "compcapsign",
+ "compbegcaps",
+ "compendcaps",
+ "endcomp",
+ "multind",
+ "compdots",
+ "comp6",
+ "class",
+ "after",
+ "before",
+ "noback",
+ "nofor",
+ "swapcc",
+ "swapcd",
+ "swapdd",
+ "space",
+ "digit",
+ "punctuation",
+ "math",
+ "sign",
+ "letter",
+ "uppercase",
+ "lowercase",
+ "grouping",
+ "uplow",
+ "litdigit",
+ "display",
+ "replace",
+ "context",
+ "correct",
+ "pass2",
+ "pass3",
+ "pass4",
+ "repeated",
+ "repword",
+ "capsnocont",
+ "always",
+ "exactdots",
+ "nocross",
+ "syllable",
+ "nocont",
+ "compbrl",
+ "literal",
+ "largesign",
+ "word",
+ "partword",
+ "joinnum",
+ "joinword",
+ "lowword",
+ "contraction",
+ "sufword",
+ "prfword",
+ "begword",
+ "begmidword",
+ "midword",
+ "midendword",
+ "endword",
+ "prepunc",
+ "postpunc",
+ "begnum",
+ "midnum",
+ "endnum",
+ "decpoint",
+ "hyphen",
+ "nobreak"
+};
+static short opcodeLengths[CTO_None] = { 0 };
+
+typedef enum
+{ noEncoding, bigEndian, littleEndian, ascii8 } EncodingType;
+
+
+typedef struct
+{
+ const char *fileName;
+ FILE *in;
+ int lineNumber;
+ EncodingType encoding;
+ int status;
+ int linelen;
+ int linepos;
+ int checkencoding[2];
+ widechar line[MAXSTRING];
+}
+FileInfo;
+
+static char scratchBuf[MAXSTRING];
+
+char *
+showString (widechar const *chars, int length)
+{
+/*Translate a string of characters to the encoding used in character
+* operands */
+ int charPos;
+ int bufPos = 0;
+ scratchBuf[bufPos++] = '\'';
+ for (charPos = 0; charPos < length; charPos++)
+ {
+ if (chars[charPos] >= 32 && chars[charPos] < 127)
+ scratchBuf[bufPos++] = (char) chars[charPos];
+ else
+ {
+ char hexbuf[20];
+ int hexLength;
+ char escapeLetter;
+
+ int leadingZeros;
+ int hexPos;
+ hexLength = sprintf (hexbuf, "%x", chars[charPos]);
+ switch (hexLength)
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ escapeLetter = 'x';
+ leadingZeros = 4 - hexLength;
+ break;
+ case 5:
+ escapeLetter = 'y';
+ leadingZeros = 0;
+ break;
+ case 6:
+ case 7:
+ case 8:
+ escapeLetter = 'z';
+ leadingZeros = 8 - hexLength;
+ break;
+ default:
+ escapeLetter = '?';
+ leadingZeros = 0;
+ break;
+ }
+ if ((bufPos + leadingZeros + hexLength + 4) >= sizeof (scratchBuf))
+ break;
+ scratchBuf[bufPos++] = '\\';
+ scratchBuf[bufPos++] = escapeLetter;
+ for (hexPos = 0; hexPos < leadingZeros; hexPos++)
+ scratchBuf[bufPos++] = '0';
+ for (hexPos = 0; hexPos < hexLength; hexPos++)
+ scratchBuf[bufPos++] = hexbuf[hexPos];
+ }
+ }
+ scratchBuf[bufPos++] = '\'';
+ scratchBuf[bufPos] = 0;
+ return scratchBuf;
+}
+
+char *
+showDots (widechar const *dots, int length)
+{
+/* Translate a sequence of dots to the encoding used in dots operands.
+*/
+ int bufPos = 0;
+ int dotsPos;
+ for (dotsPos = 0; bufPos < sizeof (scratchBuf) && dotsPos < length;
+ dotsPos++)
+ {
+ if ((dots[dotsPos] & B1))
+ scratchBuf[bufPos++] = '1';
+ if ((dots[dotsPos] & B2))
+ scratchBuf[bufPos++] = '2';
+ if ((dots[dotsPos] & B3))
+ scratchBuf[bufPos++] = '3';
+ if ((dots[dotsPos] & B4))
+ scratchBuf[bufPos++] = '4';
+ if ((dots[dotsPos] & B5))
+ scratchBuf[bufPos++] = '5';
+ if ((dots[dotsPos] & B6))
+ scratchBuf[bufPos++] = '6';
+ if ((dots[dotsPos] & B7))
+ scratchBuf[bufPos++] = '7';
+ if ((dots[dotsPos] & B8))
+ scratchBuf[bufPos++] = '8';
+ if ((dots[dotsPos] & B9))
+ scratchBuf[bufPos++] = '9';
+ if ((dots[dotsPos] & B10))
+ scratchBuf[bufPos++] = 'A';
+ if ((dots[dotsPos] & B11))
+ scratchBuf[bufPos++] = 'B';
+ if ((dots[dotsPos] & B12))
+ scratchBuf[bufPos++] = 'C';
+ if ((dots[dotsPos] & B13))
+ scratchBuf[bufPos++] = 'D';
+ if ((dots[dotsPos] & B14))
+ scratchBuf[bufPos++] = 'E';
+ if ((dots[dotsPos] & B15))
+ scratchBuf[bufPos++] = 'F';
+ if ((dots[dotsPos] == B16))
+ scratchBuf[bufPos++] = '0';
+ if (dotsPos != length - 1)
+ scratchBuf[bufPos++] = '-';
+ }
+ scratchBuf[bufPos] = 0;
+ return &scratchBuf[0];
+}
+
+char *
+showAttributes (TranslationTableCharacterAttributes a)
+{
+/* Show attributes using the letters used after the $ in multipass
+* opcodes. */
+ int bufPos = 0;
+ if ((a & CTC_Space))
+ scratchBuf[bufPos++] = 's';
+ if ((a & CTC_Letter))
+ scratchBuf[bufPos++] = 'l';
+ if ((a & CTC_Digit))
+ scratchBuf[bufPos++] = 'd';
+ if ((a & CTC_Punctuation))
+ scratchBuf[bufPos++] = 'p';
+ if ((a & CTC_UpperCase))
+ scratchBuf[bufPos++] = 'U';
+ if ((a & CTC_LowerCase))
+ scratchBuf[bufPos++] = 'u';
+ if ((a & CTC_Math))
+ scratchBuf[bufPos++] = 'm';
+ if ((a & CTC_Sign))
+ scratchBuf[bufPos++] = 'S';
+ if ((a & CTC_LitDigit))
+ scratchBuf[bufPos++] = 'D';
+ if ((a & CTC_Class1))
+ scratchBuf[bufPos++] = 'w';
+ if ((a & CTC_Class2))
+ scratchBuf[bufPos++] = 'x';
+ if ((a & CTC_Class3))
+ scratchBuf[bufPos++] = 'y';
+ if ((a & CTC_Class4))
+ scratchBuf[bufPos++] = 'z';
+ scratchBuf[bufPos] = 0;
+ return scratchBuf;
+}
+
+static void compileError (FileInfo * nested, char *format, ...);
+
+static int
+getAChar (FileInfo * nested)
+{
+/*Read a big endian, little *ndian or ASCII 8 file and convert it to
+* 16- or 32-bit unsigned integers */
+ int ch1 = 0, ch2 = 0;
+ widechar character;
+ if (nested->encoding == ascii8)
+ if (nested->status == 2)
+ {
+ nested->status++;
+ return nested->checkencoding[1];
+ }
+ while ((ch1 = fgetc (nested->in)) != EOF)
+ {
+ if (nested->status < 2)
+ nested->checkencoding[nested->status] = ch1;
+ nested->status++;
+ if (nested->status == 2)
+ {
+ if (nested->checkencoding[0] == 0xfe
+ && nested->checkencoding[1] == 0xff)
+ nested->encoding = bigEndian;
+ else if (nested->checkencoding[0] == 0xff
+ && nested->checkencoding[1] == 0xfe)
+ nested->encoding = littleEndian;
+ else if (nested->checkencoding[0] < 128
+ && nested->checkencoding[1] < 128)
+ {
+ nested->encoding = ascii8;
+ return nested->checkencoding[0];
+ }
+ else
+ {
+ compileError (nested,
+ "encoding is neither big-endian, little-endian nor ASCII 8.");
+ ch1 = EOF;
+ break;;
+ }
+ continue;
+ }
+ switch (nested->encoding)
+ {
+ case noEncoding:
+ break;
+ case ascii8:
+ return ch1;
+ break;
+ case bigEndian:
+ ch2 = fgetc (nested->in);
+ if (ch2 == EOF)
+ break;
+ character = (ch1 << 8) | ch2;
+ return (int) character;
+ break;
+ case littleEndian:
+ ch2 = fgetc (nested->in);
+ if (ch2 == EOF)
+ break;
+ character = (ch2 << 8) | ch1;
+ return (int) character;
+ break;
+ }
+ if (ch1 == EOF || ch2 == EOF)
+ break;
+ }
+ return EOF;
+}
+
+static int
+getALine (FileInfo * nested)
+{
+/*Read a line of widechar's from an input file */
+ int ch;
+ int pch = 0;
+ nested->linelen = 0;
+ while ((ch = getAChar (nested)) != EOF)
+ {
+ if (ch == 13)
+ continue;
+ if (pch == '\\' && ch == 10)
+ {
+ nested->linelen--;
+ continue;
+ }
+ if (ch == 10 || nested->linelen >= MAXSTRING)
+ break;
+ nested->line[nested->linelen++] = (widechar) ch;
+ pch = ch;
+ }
+ nested->line[nested->linelen] = 0;
+ nested->linepos = 0;
+ if (ch == EOF)
+ return 0;
+ nested->lineNumber++;
+ return 1;
+}
+
+static int lastToken;
+static int
+getToken (FileInfo * nested, CharsString * result, const char *description)
+{
+/*Find the next string of contiguous non-whitespace characters. If this
+ * is the last token on the line, return 2 instead of 1. */
+ while (nested->line[nested->linepos] && nested->line[nested->linepos] <= 32)
+ nested->linepos++;
+ result->length = 0;
+ while (nested->line[nested->linepos] && nested->line[nested->linepos] > 32)
+ result->chars[result->length++] = nested->line[nested->linepos++];
+ if (!result->length)
+ {
+ /* Not enough tokens */
+ if (description)
+ compileError (nested, "%s not specified.", description);
+ return 0;
+ }
+ result->chars[result->length] = 0;
+ while (nested->line[nested->linepos] && nested->line[nested->linepos] <= 32)
+ nested->linepos++;
+ if (nested->line[nested->linepos] == 0)
+ {
+ lastToken = 1;
+ return 2;
+ }
+ else
+ {
+ lastToken = 0;
+ return 1;
+ }
+}
+
+static void
+compileError (FileInfo * nested, char *format, ...)
+{
+#ifndef __SYMBIAN32__
+ char buffer[MAXSTRING];
+ va_list arguments;
+ va_start (arguments, format);
+#ifdef _WIN32
+ (void) _vsnprintf (buffer, sizeof (buffer), format, arguments);
+#else
+ (void) vsnprintf (buffer, sizeof (buffer), format, arguments);
+#endif
+ va_end (arguments);
+ if (nested)
+ lou_logPrint ("%s:%d: error: %s", nested->fileName,
+ nested->lineNumber, buffer);
+ else
+ lou_logPrint ("error: %s", buffer);
+ errorCount++;
+#endif
+}
+
+static void
+compileWarning (FileInfo * nested, char *format, ...)
+{
+#ifndef __SYMBIAN32__
+ char buffer[MAXSTRING];
+ va_list arguments;
+ va_start (arguments, format);
+#ifdef _WIN32
+ (void) _vsnprintf (buffer, sizeof (buffer), format, arguments);
+#else
+ (void) vsnprintf (buffer, sizeof (buffer), format, arguments);
+#endif
+ va_end (arguments);
+ if (nested)
+ lou_logPrint ("%s:%d: warning: %s", nested->fileName,
+ nested->lineNumber, buffer);
+ else
+ lou_logPrint ("warning: %s", buffer);
+ warningCount++;
+#endif
+}
+
+static int
+allocateSpaceInTable (FileInfo * nested, TranslationTableOffset * offset,
+ int count)
+{
+/* allocate memory for translation table and expand previously allocated
+* memory if necessary */
+ int spaceNeeded = ((count + OFFSETSIZE - 1) / OFFSETSIZE) * OFFSETSIZE;
+ TranslationTableOffset size = tableUsed + spaceNeeded;
+ if (size > tableSize)
+ {
+ void *newTable;
+ size += (size / OFFSETSIZE);
+ newTable = realloc (table, size);
+ if (!newTable)
+ {
+ compileError (nested, "Not enough memory for translation table.");
+ return 0;
+ }
+ memset (((unsigned char *) newTable) + tableSize, 0, size - tableSize);
+ table = (TranslationTableHeader *) newTable;
+ tableSize = size;
+ }
+ if (offset != NULL)
+ {
+ *offset = (tableUsed - sizeof (*table)) / OFFSETSIZE;
+ tableUsed += spaceNeeded;
+ }
+ return 1;
+}
+
+static int
+reserveSpaceInTable (FileInfo * nested, int count)
+{
+ return (allocateSpaceInTable (nested, NULL, count));
+}
+
+static int
+allocateHeader (FileInfo * nested)
+{
+/*Allocate memory for the table header and a guess on the number of
+* rules */
+ const TranslationTableOffset startSize = 2 * sizeof (*table);
+ if (table)
+ return 1;
+ tableUsed = sizeof (*table) + OFFSETSIZE; /*So no offset is ever zero */
+ if (!(table = malloc (startSize)))
+ {
+ compileError (nested, "Not enough memory");
+ if (table != NULL)
+ free (table);
+ table = NULL;
+ return 0;
+ }
+ memset (table, 0, startSize);
+ tableSize = startSize;
+ return 1;
+}
+
+int
+stringHash (const widechar * c)
+{
+/*hash function for strings */
+ unsigned long int makeHash = (((unsigned long int) c[0] << 8) +
+ (unsigned long int) c[1]) % HASHNUM;
+ return (int) makeHash;
+}
+
+int
+charHash (widechar c)
+{
+ unsigned long int makeHash = (unsigned long int) c % HASHNUM;
+ return (int) makeHash;
+}
+
+static TranslationTableCharacter *
+compile_findCharOrDots (widechar c, int m)
+{
+/*Look up a character or dot pattern. If m is 0 look up a character,
+* otherwise look up a dot pattern. Although the algorithms are almost
+* identical, different tables are needed for characters and dots because
+* of the possibility of conflicts.*/
+ TranslationTableCharacter *character;
+ TranslationTableOffset bucket;
+ unsigned long int makeHash = (unsigned long int) c % HASHNUM;
+ if (m == 0)
+ bucket = table->characters[makeHash];
+ else
+ bucket = table->dots[makeHash];
+ while (bucket)
+ {
+ character = (TranslationTableCharacter *) & table->ruleArea[bucket];
+ if (character->realchar == c)
+ return character;
+ bucket = character->next;
+ }
+ return NULL;
+}
+
+static TranslationTableCharacter noChar = { 0, 0, 0, CTC_Space, 32, 32, 32 };
+static TranslationTableCharacter noDots =
+ { 0, 0, 0, CTC_Space, B16, B16, B16 };
+static char *unknownDots (widechar dots);
+
+static TranslationTableCharacter *
+definedCharOrDots (FileInfo * nested, widechar c, int m)
+{
+ TranslationTableCharacter *notFound;
+ TranslationTableCharacter *charOrDots = compile_findCharOrDots (c, m);
+ if (charOrDots)
+ return charOrDots;
+ if (m == 0)
+ {
+ notFound = &noChar;
+ compileError (nested,
+ "character %s should be defined at this point but is not",
+ showString (&c, 1));
+ }
+ else
+ {
+ notFound = &noDots;
+ compileError (nested,
+ "cell %s should be defined at this point but is not",
+ unknownDots (c));
+ }
+ return notFound;
+}
+
+static TranslationTableCharacter *
+addCharOrDots (FileInfo * nested, widechar c, int m)
+{
+/*See if a character or dot pattern is in the appropriate table. If not,
+* insert it. In either
+* case, return a pointer to it. */
+ TranslationTableOffset bucket;
+ TranslationTableCharacter *character;
+ TranslationTableCharacter *oldchar;
+ TranslationTableOffset offset;
+ unsigned long int makeHash;
+ if ((character = compile_findCharOrDots (c, m)))
+ return character;
+ if (!allocateSpaceInTable (nested, &offset, sizeof (*character)))
+ return NULL;
+ character = (TranslationTableCharacter *) & table->ruleArea[offset];
+ memset (character, 0, sizeof (*character));
+ character->realchar = c;
+ makeHash = (unsigned long int) c % HASHNUM;
+ if (m == 0)
+ bucket = table->characters[makeHash];
+ else
+ bucket = table->dots[makeHash];
+ if (!bucket)
+ {
+ if (m == 0)
+ table->characters[makeHash] = offset;
+ else
+ table->dots[makeHash] = offset;
+ }
+ else
+ {
+ oldchar = (TranslationTableCharacter *) & table->ruleArea[bucket];
+ while (oldchar->next)
+ oldchar =
+ (TranslationTableCharacter *) & table->ruleArea[oldchar->next];
+ oldchar->next = offset;
+ }
+ return character;
+}
+
+static CharOrDots *
+getCharOrDots (widechar c, int m)
+{
+ CharOrDots *cdPtr;
+ TranslationTableOffset bucket;
+ unsigned long int makeHash = (unsigned long int) c % HASHNUM;
+ if (m == 0)
+ bucket = table->charToDots[makeHash];
+ else
+ bucket = table->dotsToChar[makeHash];
+ while (bucket)
+ {
+ cdPtr = (CharOrDots *) & table->ruleArea[bucket];
+ if (cdPtr->lookFor == c)
+ return cdPtr;
+ bucket = cdPtr->next;
+ }
+ return NULL;
+}
+
+widechar
+getDotsForChar (widechar c)
+{
+ CharOrDots *cdPtr = getCharOrDots (c, 0);
+ if (cdPtr)
+ return cdPtr->found;
+ return B16;
+}
+
+widechar
+getCharFromDots (widechar d)
+{
+ CharOrDots *cdPtr = getCharOrDots (d, 1);
+ if (cdPtr)
+ return cdPtr->found;
+ return ' ';
+}
+
+static int
+putCharAndDots (FileInfo * nested, widechar c, widechar d)
+{
+ TranslationTableOffset bucket;
+ CharOrDots *cdPtr;
+ CharOrDots *oldcdPtr = NULL;
+ TranslationTableOffset offset;
+ unsigned long int makeHash;
+ if (!(cdPtr = getCharOrDots (c, 0)))
+ {
+ if (!allocateSpaceInTable (nested, &offset, sizeof (*cdPtr)))
+ return 0;
+ cdPtr = (CharOrDots *) & table->ruleArea[offset];
+ cdPtr->next = 0;
+ cdPtr->lookFor = c;
+ cdPtr->found = d;
+ makeHash = (unsigned long int) c % HASHNUM;
+ bucket = table->charToDots[makeHash];
+ if (!bucket)
+ table->charToDots[makeHash] = offset;
+ else
+ {
+ oldcdPtr = (CharOrDots *) & table->ruleArea[bucket];
+ while (oldcdPtr->next)
+ oldcdPtr = (CharOrDots *) & table->ruleArea[oldcdPtr->next];
+ oldcdPtr->next = offset;
+ }
+ }
+ if (!(cdPtr = getCharOrDots (d, 1)))
+ {
+ if (!allocateSpaceInTable (nested, &offset, sizeof (*cdPtr)))
+ return 0;
+ cdPtr = (CharOrDots *) & table->ruleArea[offset];
+ cdPtr->next = 0;
+ cdPtr->lookFor = d;
+ cdPtr->found = c;
+ makeHash = (unsigned long int) d % HASHNUM;
+ bucket = table->dotsToChar[makeHash];
+ if (!bucket)
+ table->dotsToChar[makeHash] = offset;
+ else
+ {
+ oldcdPtr = (CharOrDots *) & table->ruleArea[bucket];
+ while (oldcdPtr->next)
+ oldcdPtr = (CharOrDots *) & table->ruleArea[oldcdPtr->next];
+ oldcdPtr->next = offset;
+ }
+ }
+ return 1;
+}
+
+static char *
+unknownDots (widechar dots)
+{
+/*Print out dot numbers */
+ static char buffer[20];
+ int k = 1;
+ buffer[0] = '\\';
+ if ((dots & B1))
+ buffer[k++] = '1';
+ if ((dots & B2))
+ buffer[k++] = '2';
+ if ((dots & B3))
+ buffer[k++] = '3';
+ if ((dots & B4))
+ buffer[k++] = '4';
+ if ((dots & B5))
+ buffer[k++] = '5';
+ if ((dots & B6))
+ buffer[k++] = '6';
+ if ((dots & B7))
+ buffer[k++] = '7';
+ if ((dots & B8))
+ buffer[k++] = '8';
+ if ((dots & B9))
+ buffer[k++] = '9';
+ if ((dots & B10))
+ buffer[k++] = 'A';
+ if ((dots & B11))
+ buffer[k++] = 'B';
+ if ((dots & B12))
+ buffer[k++] = 'C';
+ if ((dots & B13))
+ buffer[k++] = 'D';
+ if ((dots & B14))
+ buffer[k++] = 'E';
+ if ((dots & B15))
+ buffer[k++] = 'F';
+ buffer[k++] = '/';
+ buffer[k] = 0;
+ return buffer;
+}
+
+static TranslationTableOffset newRuleOffset = 0;
+static TranslationTableRule *newRule = NULL;
+
+static int
+charactersDefined (FileInfo * nested)
+{
+/*Check that all characters are defined by character-definition
+* opcodes*/
+ int noErrors = 1;
+ int k;
+ if ((newRule->opcode >= CTO_Space && newRule->opcode <= CTO_LitDigit)
+ || newRule->opcode == CTO_SwapDd
+ ||
+ newRule->opcode == CTO_Replace || newRule->opcode == CTO_MultInd
+ || newRule->opcode == CTO_Repeated ||
+ ((newRule->opcode >= CTO_Context && newRule->opcode <=
+ CTO_Pass4) && newRule->opcode != CTO_Correct))
+ return 1;
+ for (k = 0; k < newRule->charslen; k++)
+ if (!compile_findCharOrDots (newRule->charsdots[k], 0))
+ {
+ compileError (nested, "Character %s is not defined", showString
+ (&newRule->charsdots[k], 1));
+ noErrors = 0;
+ }
+ if (!(newRule->opcode == CTO_Correct || newRule->opcode ==
+ CTO_NoBreak || newRule->opcode == CTO_SwapCc || newRule->opcode ==
+ CTO_SwapCd))
+ {
+ for (k = newRule->charslen; k < newRule->charslen + newRule->dotslen;
+ k++)
+ if (!compile_findCharOrDots (newRule->charsdots[k], 1))
+ {
+ compileError (nested, "Dot pattern %s is not defined.",
+ unknownDots (newRule->charsdots[k]));
+ noErrors = 0;
+ }
+ }
+ return noErrors;
+}
+
+static int noback = 0;
+static int nofor = 0;
+
+/*The following functions are
+called by addRule to handle various
+* cases.*/
+
+static void
+add_0_single (FileInfo * nested)
+{
+/*direction = 0, newRule->charslen = 1*/
+ TranslationTableRule *currentRule;
+ TranslationTableOffset *currentOffsetPtr;
+ TranslationTableCharacter *character;
+ int m = 0;
+ if (newRule->opcode == CTO_CompDots || newRule->opcode == CTO_Comp6)
+ return;
+ if (newRule->opcode >= CTO_Pass2 && newRule->opcode <= CTO_Pass4)
+ m = 1;
+ character = definedCharOrDots (nested, newRule->charsdots[0], m);
+ if (m != 1 && character->attributes & CTC_Letter && (newRule->opcode
+ ==
+ CTO_WholeWord
+ || newRule->opcode ==
+ CTO_LargeSign))
+ {
+ if (table->noLetsignCount < LETSIGNSIZE)
+ table->noLetsign[table->noLetsignCount++] = newRule->charsdots[0];
+ }
+ if (newRule->opcode >= CTO_Space && newRule->opcode < CTO_UpLow)
+ character->definitionRule = newRuleOffset;
+ currentOffsetPtr = &character->otherRules;
+ while (*currentOffsetPtr)
+ {
+ currentRule = (TranslationTableRule *)
+ & table->ruleArea[*currentOffsetPtr];
+ if (currentRule->charslen == 0)
+ break;
+ if (currentRule->opcode >= CTO_Space && currentRule->opcode < CTO_UpLow)
+ if (!(newRule->opcode >= CTO_Space && newRule->opcode < CTO_UpLow))
+ break;
+ currentOffsetPtr = &currentRule->charsnext;
+ }
+ newRule->charsnext = *currentOffsetPtr;
+ *currentOffsetPtr = newRuleOffset;
+}
+
+static void
+add_0_multiple (void)
+{
+/*direction = 0 newRule->charslen > 1*/
+ TranslationTableRule *currentRule = NULL;
+ TranslationTableOffset *currentOffsetPtr =
+ &table->forRules[stringHash (&newRule->charsdots[0])];
+ while (*currentOffsetPtr)
+ {
+ currentRule = (TranslationTableRule *)
+ & table->ruleArea[*currentOffsetPtr];
+ if (newRule->charslen > currentRule->charslen)
+ break;
+ if (newRule->charslen == currentRule->charslen)
+ if ((currentRule->opcode == CTO_Always)
+ && (newRule->opcode != CTO_Always))
+ break;
+ currentOffsetPtr = &currentRule->charsnext;
+ }
+ newRule->charsnext = *currentOffsetPtr;
+ *currentOffsetPtr = newRuleOffset;
+}
+
+static void
+add_1_single (FileInfo * nested)
+{
+/*direction = 1, newRule->dotslen = 1*/
+ TranslationTableRule *currentRule;
+ TranslationTableOffset *currentOffsetPtr;
+ TranslationTableCharacter *dots;
+ if (newRule->opcode == CTO_NoBreak || newRule->opcode == CTO_SwapCc ||
+ (newRule->opcode >= CTO_Context
+ &&
+ newRule->opcode <= CTO_Pass4)
+ || newRule->opcode == CTO_Repeated || (newRule->opcode == CTO_Always
+ && newRule->charslen == 1))
+ return; /*too ambiguous */
+ dots = definedCharOrDots (nested, newRule->charsdots[newRule->charslen], 1);
+ if (newRule->opcode >= CTO_Space && newRule->opcode < CTO_UpLow)
+ dots->definitionRule = newRuleOffset;
+ currentOffsetPtr = &dots->otherRules;
+ while (*currentOffsetPtr)
+ {
+ currentRule = (TranslationTableRule *)
+ & table->ruleArea[*currentOffsetPtr];
+ if (newRule->charslen > currentRule->charslen ||
+ currentRule->dotslen == 0)
+ break;
+ if (currentRule->opcode >= CTO_Space && currentRule->opcode < CTO_UpLow)
+ if (!(newRule->opcode >= CTO_Space && newRule->opcode < CTO_UpLow))
+ break;
+ currentOffsetPtr = &currentRule->dotsnext;
+ }
+ newRule->dotsnext = *currentOffsetPtr;
+ *currentOffsetPtr = newRuleOffset;
+}
+
+static void
+add_1_multiple (void)
+{
+/*direction = 1, newRule->dotslen > 1*/
+ TranslationTableRule *currentRule = NULL;
+ TranslationTableOffset *currentOffsetPtr = &table->backRules[stringHash
+ (&newRule->
+ charsdots
+ [newRule->
+ charslen])];
+ if (newRule->opcode == CTO_NoBreak || newRule->opcode == CTO_SwapCc ||
+ (newRule->opcode >= CTO_Context && newRule->opcode <= CTO_Pass4))
+ return;
+ while (*currentOffsetPtr)
+ {
+ int currentLength;
+ int newLength;
+ currentRule = (TranslationTableRule *)
+ & table->ruleArea[*currentOffsetPtr];
+ currentLength = currentRule->dotslen + currentRule->charslen;
+ newLength = newRule->dotslen + newRule->charslen;
+ if (newLength > currentLength)
+ break;
+ if (currentLength == newLength)
+ if ((currentRule->opcode == CTO_Always)
+ && (newRule->opcode != CTO_Always))
+ break;
+ currentOffsetPtr = &currentRule->dotsnext;
+ }
+ newRule->dotsnext = *currentOffsetPtr;
+ *currentOffsetPtr = newRuleOffset;
+}
+
+static void
+makeRuleChain (TranslationTableOffset * offsetPtr)
+{
+ TranslationTableRule *currentRule;
+ while (*offsetPtr)
+ {
+ currentRule = (TranslationTableRule *) & table->ruleArea[*offsetPtr];
+ offsetPtr = &currentRule->charsnext;
+ }
+ newRule->charsnext = *offsetPtr;
+ *offsetPtr = newRuleOffset;
+}
+
+static int
+addPassRule (FileInfo * nested)
+{
+ TranslationTableOffset *offsetPtr;
+ switch (newRule->opcode)
+ {
+ case CTO_Correct:
+ offsetPtr = &table->attribOrSwapRules[0];
+ break;
+ case CTO_Context:
+ offsetPtr = &table->attribOrSwapRules[1];
+ break;
+ case CTO_Pass2:
+ offsetPtr = &table->attribOrSwapRules[2];
+ break;
+ case CTO_Pass3:
+ offsetPtr = &table->attribOrSwapRules[3];
+ break;
+ case CTO_Pass4:
+ offsetPtr = &table->attribOrSwapRules[4];
+ break;
+ default:
+ return 0;
+ }
+ makeRuleChain (offsetPtr);
+ return 1;
+}
+
+static int
+ addRule
+ (FileInfo * nested,
+ TranslationTableOpcode opcode,
+ CharsString * ruleChars,
+ CharsString * ruleDots,
+ TranslationTableCharacterAttributes after,
+ TranslationTableCharacterAttributes before)
+{
+/*Add a rule to the table, using the hash function to find the start of
+* chains and chaining both the chars and dots strings */
+ int ruleSize = sizeof (TranslationTableRule) - (DEFAULTRULESIZE * CHARSIZE);
+ int direction = 0; /*0 = forward translation; 1 = bacward */
+ if (ruleChars)
+ ruleSize += CHARSIZE * ruleChars->length;
+ if (ruleDots)
+ ruleSize += CHARSIZE * ruleDots->length;
+ if (!allocateSpaceInTable (nested, &newRuleOffset, ruleSize))
+ return 0;
+ newRule = (TranslationTableRule *) & table->ruleArea[newRuleOffset];
+ newRule->opcode = opcode;
+ newRule->after = after;
+ newRule->before = before;
+ if (ruleChars)
+ memcpy (&newRule->charsdots[0], &ruleChars->chars[0],
+ CHARSIZE * (newRule->charslen = ruleChars->length));
+ else
+ newRule->charslen = 0;
+ if (ruleDots)
+ memcpy (&newRule->charsdots[newRule->charslen],
+ &ruleDots->chars[0], CHARSIZE * (newRule->dotslen =
+ ruleDots->length));
+ else
+ newRule->dotslen = 0;
+ if (!charactersDefined (nested))
+ return 0;
+
+ /*link new rule into table. */
+ if (opcode == CTO_SwapCc || opcode == CTO_SwapCd || opcode == CTO_SwapDd)
+ return 1;
+ if (opcode >= CTO_Context && opcode <= CTO_Pass4 && newRule->charslen == 0)
+ return addPassRule (nested);
+ if (newRule->charslen == 0 || nofor)
+ direction = 1;
+ while (direction < 2)
+ {
+ if (direction == 0 && newRule->charslen == 1)
+ add_0_single (nested);
+ else if (direction == 0 && newRule->charslen > 1)
+ add_0_multiple ();
+ else if (direction == 1 && newRule->dotslen == 1 && !noback)
+ add_1_single (nested);
+ else if (direction == 1 && newRule->dotslen > 1 && !noback)
+ add_1_multiple ();
+ else
+ {
+ }
+ direction++;
+ if (newRule->dotslen == 0)
+ direction = 2;
+ }
+ return 1;
+}
+
+static const struct CharacterClass *
+findCharacterClass (const CharsString * name)
+{
+/*Find a character class, whether predefined or user-defined */
+ const struct CharacterClass *class = characterClasses;
+ while (class)
+ {
+ if ((name->length == class->length) &&
+ (memcmp (&name->chars[0], class->name, CHARSIZE *
+ name->length) == 0))
+ return class;
+ class = class->next;
+ }
+ return NULL;
+}
+
+static struct CharacterClass *
+addCharacterClass (FileInfo * nested, const widechar * name, int length)
+{
+/*Define a character class, Whether predefined or user-defined */
+ struct CharacterClass *class;
+ if (characterClassAttribute)
+ {
+ if ((class = malloc (sizeof (*class) + CHARSIZE * (length - 1))))
+ {
+ memset (class, 0, sizeof (*class));
+ memcpy (class->name, name, CHARSIZE * (class->length = length));
+ class->attribute = characterClassAttribute;
+ characterClassAttribute <<= 1;
+ class->next = characterClasses;
+ characterClasses = class;
+ return class;
+ }
+ }
+ compileError (nested, "character class table overflow.");
+ return NULL;
+}
+
+static void
+deallocateCharacterClasses (void)
+{
+ while (characterClasses)
+ {
+ struct CharacterClass *class = characterClasses;
+ characterClasses = characterClasses->next;
+ if (class)
+ free (class);
+ }
+}
+
+static int
+allocateCharacterClasses (void)
+{
+/*Allocate memory for predifined character classes */
+ int k = 0;
+ characterClasses = NULL;
+ characterClassAttribute = 1;
+ while (characterClassNames[k])
+ {
+ widechar wname[MAXSTRING];
+ int length = strlen (characterClassNames[k]);
+ int kk;
+ for (kk = 0; kk < length; kk++)
+ wname[kk] = (widechar) characterClassNames[k][kk];
+ if (!addCharacterClass (NULL, wname, length))
+ {
+ deallocateCharacterClasses ();
+ return 0;
+ }
+ k++;
+ }
+ return 1;
+}
+
+static TranslationTableOpcode
+getOpcode (FileInfo * nested, const CharsString * token)
+{
+ static TranslationTableOpcode lastOpcode = 0;
+ TranslationTableOpcode opcode = lastOpcode;
+
+ do
+ {
+ if (token->length == opcodeLengths[opcode])
+ if (eqasc2uni ((unsigned char *) opcodeNames[opcode],
+ &token->chars[0], token->length))
+ {
+ lastOpcode = opcode;
+ return opcode;
+ }
+ opcode++;
+ if (opcode >= CTO_None)
+ opcode = 0;
+ }
+ while (opcode != lastOpcode);
+ compileError (nested, "opcode %s not defined.", showString
+ (&token->chars[0], token->length));
+ return CTO_None;
+}
+
+TranslationTableOpcode
+findOpcodeNumber (const char *toFind)
+{
+/* Used by tools such as lou_debug */
+ static TranslationTableOpcode lastOpcode = 0;
+ TranslationTableOpcode opcode = lastOpcode;
+ int length = strlen (toFind);
+ do
+ {
+ if (length == opcodeLengths[opcode] && strcasecmp (toFind,
+ opcodeNames[opcode])
+ == 0)
+ {
+ lastOpcode = opcode;
+ return opcode;
+ }
+ opcode++;
+ if (opcode >= CTO_None)
+ opcode = 0;
+ }
+ while (opcode != lastOpcode);
+ return CTO_None;
+}
+
+const char *
+findOpcodeName (TranslationTableOpcode opcode)
+{
+/* Used by tools such as lou_debug */
+ if (opcode < 0 || opcode >= CTO_None)
+ {
+ sprintf (scratchBuf, "%d", opcode);
+ return scratchBuf;
+ }
+ return opcodeNames[opcode];
+}
+
+static widechar
+hexValue (FileInfo * nested, const widechar * digits, int length)
+{
+ int k;
+ unsigned int binaryValue = 0;
+ for (k = 0; k < length; k++)
+ {
+ unsigned int hexDigit = 0;
+ if (digits[k] >= '0' && digits[k] <= '9')
+ hexDigit = digits[k] - '0';
+ else if (digits[k] >= 'a' && digits[k] <= 'f')
+ hexDigit = digits[k] - 'a' + 10;
+ else if (digits[k] >= 'A' && digits[k] <= 'F')
+ hexDigit = digits[k] - 'A' + 10;
+ else
+ {
+ compileError (nested, "invalid %d-digit hexadecimal number",
+ length);
+ return (widechar) 0xffffffff;
+ }
+ binaryValue |= hexDigit << (4 * (length - 1 - k));
+ }
+ return (widechar) binaryValue;
+}
+
+#define MAXBYTES 7
+static int first0Bit[MAXBYTES] = { 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0XFE };
+
+static int
+parseChars (FileInfo * nested, CharsString * result, CharsString * token)
+{
+ int in = 0;
+ int out = 0;
+ int lastOutSize = 0;
+ int lastIn;
+ unsigned int ch = 0;
+ int numBytes = 0;
+ unsigned int utf32 = 0;
+ int k;
+ while (in < token->length)
+ {
+ ch = token->chars[in++] & 0xff;
+ if (ch < 128)
+ {
+ if (ch == '\\')
+ { /* escape sequence */
+ switch (ch = token->chars[in])
+ {
+ case '\\':
+ break;
+ case 'e':
+ ch = 0x1b;
+ break;
+ case 'f':
+ ch = 12;
+ break;
+ case 'n':
+ ch = 10;
+ break;
+ case 'r':
+ ch = 13;
+ break;
+ case 's':
+ ch = ' ';
+ break;
+ case 't':
+ ch = 9;
+ break;
+ case 'v':
+ ch = 22;
+ break;
+ case 'w':
+ ch = ENDSEGMENT;
+ break;
+ case 34:
+ ch = QUOTESUB;
+ break;
+ case 'X':
+ case 'x':
+ if (token->length - in > 4)
+ {
+ ch = hexValue (nested, &token->chars[in + 1], 4);
+ in += 4;
+ }
+ break;
+ case 'y':
+ case 'Y':
+ if (CHARSIZE == 2)
+ {
+ not32:
+ compileError (nested,
+ "liblouis has not been compiled for 32-bit Unicode");
+ break;
+ }
+ if (token->length - in > 5)
+ {
+ ch = hexValue (nested, &token->chars[in + 1], 5);
+ in += 5;
+ }
+ break;
+ case 'z':
+ case 'Z':
+ if (CHARSIZE == 2)
+ goto not32;
+ if (token->length - in > 8)
+ {
+ ch = hexValue (nested, &token->chars[in + 1], 8);
+ in += 8;
+ }
+ break;
+ default:
+ compileError (nested, "invalid escape sequence '\\%c'", ch);
+ break;
+ }
+ in++;
+ }
+ result->chars[out++] = (widechar) ch;
+ if (out >= MAXSTRING)
+ {
+ result->length = out;
+ return 1;
+ }
+ continue;
+ }
+ lastOutSize = out;
+ lastIn = in;
+ for (numBytes = MAXBYTES - 1; numBytes >= 0; numBytes--)
+ if (ch >= first0Bit[numBytes])
+ break;
+ utf32 = ch & (0XFF - first0Bit[numBytes]);
+ for (k = 0; k < numBytes; k++)
+ {
+ if (in >= MAXSTRING)
+ break;
+ if (token->chars[in] < 128 || (token->chars[in] & 0x0040))
+ {
+ compileWarning (nested, "invalid UTF-8. Assuming Latin-1.");
+ result->chars[out++] = token->chars[lastIn];
+ in = lastIn + 1;
+ continue;
+ }
+ utf32 = (utf32 << 6) + (token->chars[in++] & 0x3f);
+ }
+ if (CHARSIZE == 2 && utf32 > 0xffff)
+ utf32 = 0xffff;
+ result->chars[out++] = (widechar) utf32;
+ if (out >= MAXSTRING)
+ {
+ result->length = lastOutSize;
+ return 1;
+ }
+ }
+ result->length = out;
+ return 1;
+}
+
+int
+extParseChars (const char *inString, widechar * outString)
+{
+/* Parse external character strings */
+ CharsString wideIn;
+ CharsString result;
+ int k;
+ for (k = 0; inString[k] && k < MAXSTRING; k++)
+ wideIn.chars[k] = inString[k];
+ wideIn.chars[k] = 0;
+ wideIn.length = k;
+ parseChars (NULL, &result, &wideIn);
+ if (errorCount)
+ {
+ errorCount = 0;
+ return 0;
+ }
+ for (k = 0; k < result.length; k++)
+ outString[k] = result.chars[k];
+ outString[k] = 0;
+ return result.length;
+}
+
+static int
+parseDots (FileInfo * nested, CharsString * cells, const CharsString * token)
+{
+/*get dot patterns */
+ widechar cell = 0; /*assembly place for dots */
+ int cellCount = 0;
+ int index;
+ int start = 0;
+
+ for (index = 0; index < token->length; index++)
+ {
+ int started = index != start;
+ widechar character = token->chars[index];
+ switch (character)
+ { /*or dots to make up Braille cell */
+ {
+ int dot;
+ case '1':
+ dot = B1;
+ goto haveDot;
+ case '2':
+ dot = B2;
+ goto haveDot;
+ case '3':
+ dot = B3;
+ goto haveDot;
+ case '4':
+ dot = B4;
+ goto haveDot;
+ case '5':
+ dot = B5;
+ goto haveDot;
+ case '6':
+ dot = B6;
+ goto haveDot;
+ case '7':
+ dot = B7;
+ goto haveDot;
+ case '8':
+ dot = B8;
+ goto haveDot;
+ case '9':
+ dot = B9;
+ goto haveDot;
+ case 'a':
+ case 'A':
+ dot = B10;
+ goto haveDot;
+ case 'b':
+ case 'B':
+ dot = B11;
+ goto haveDot;
+ case 'c':
+ case 'C':
+ dot = B12;
+ goto haveDot;
+ case 'd':
+ case 'D':
+ dot = B13;
+ goto haveDot;
+ case 'e':
+ case 'E':
+ dot = B14;
+ goto haveDot;
+ case 'f':
+ case 'F':
+ dot = B15;
+ haveDot:
+ if (started && !cell)
+ goto invalid;
+ if (cell & dot)
+ {
+ compileError (nested, "dot specified more than once.");
+ return 0;
+ }
+ cell |= dot;
+ break;
+ }
+ case '0': /*blank */
+ if (started)
+ goto invalid;
+ break;
+ case '-': /*got all dots for this cell */
+ if (!started)
+ {
+ compileError (nested, "missing cell specification.");
+ return 0;
+ }
+ cells->chars[cellCount++] = cell | B16;
+ cell = 0;
+ start = index + 1;
+ break;
+ default:
+ invalid:
+ compileError (nested, "invalid dot number %s.", showString
+ (&character, 1));
+ return 0;
+ }
+ }
+ if (index == start)
+ {
+ compileError (nested, "missing cell specification.");
+ return 0;
+ }
+ cells->chars[cellCount++] = cell | B16; /*last cell */
+ cells->length = cellCount;
+ return 1;
+}
+
+int
+extParseDots (const char *inString, widechar * outString)
+{
+/* Parse external dot patterns */
+ CharsString wideIn;
+ CharsString result;
+ int k;
+ for (k = 0; inString[k] && k < MAXSTRING; k++)
+ wideIn.chars[k] = inString[k];
+ wideIn.chars[k] = 0;
+ wideIn.length = k;
+ parseDots (NULL, &result, &wideIn);
+ if (errorCount)
+ {
+ errorCount = 0;
+ return 0;
+ }
+ for (k = 0; k < result.length; k++)
+ outString[k] = result.chars[k];
+ outString[k] = 0;
+ return result.length;
+}
+
+static int
+getCharacters (FileInfo * nested, CharsString * characters)
+{
+/*Get ruleChars string */
+ CharsString token;
+ if (getToken (nested, &token, "characters"))
+ if (parseChars (nested, characters, &token))
+ return 1;
+ return 0;
+}
+
+static int
+getRuleCharsText (FileInfo * nested, CharsString * ruleChars)
+{
+ CharsString token;
+ if (getToken (nested, &token, "Characters operand"))
+ if (parseChars (nested, ruleChars, &token))
+ return 1;
+ return 0;
+}
+
+static int
+getRuleDotsText (FileInfo * nested, CharsString * ruleDots)
+{
+ CharsString token;
+ if (getToken (nested, &token, "characters"))
+ if (parseChars (nested, ruleDots, &token))
+ return 1;
+ return 0;
+}
+
+static int
+getRuleDotsPattern (FileInfo * nested, CharsString * ruleDots)
+{
+/*Interpret the dets operand */
+ CharsString token;
+ if (getToken (nested, &token, "Dots operand"))
+ {
+ if (token.length == 1 && token.chars[0] == '=')
+ {
+ ruleDots->length = 0;
+ return 1;
+ }
+ if (parseDots (nested, ruleDots, &token))
+ return 1;
+ }
+ return 0;
+}
+
+static int
+getCharacterClass (FileInfo * nested, const struct CharacterClass **class)
+{
+ CharsString token;
+ if (getToken (nested, &token, "character class name"))
+ {
+ if ((*class = findCharacterClass (&token)))
+ return 1;
+ compileError (nested, "character class not defined.");
+ }
+ return 0;
+}
+
+static int compileFile (const char *fileName);
+
+static int
+includeFile (FileInfo * nested, CharsString * includedFile)
+{
+/*Implement include opcode*/
+ int k;
+ char includeThis[MAXSTRING];
+ for (k = 0; k < includedFile->length; k++)
+ includeThis[k] = (char) includedFile->chars[k];
+ includeThis[k] = 0;
+ return compileFile (includeThis);
+}
+
+struct RuleName
+{
+ struct RuleName *next;
+ TranslationTableOffset ruleOffset;
+ widechar length;
+ widechar name[1];
+};
+static struct RuleName *ruleNames = NULL;
+static TranslationTableOffset
+findRuleName (const CharsString * name)
+{
+ const struct RuleName *nameRule = ruleNames;
+ while (nameRule)
+ {
+ if ((name->length == nameRule->length) &&
+ (memcmp (&name->chars[0], nameRule->name, CHARSIZE *
+ name->length) == 0))
+ return nameRule->ruleOffset;
+ nameRule = nameRule->next;
+ }
+ return 0;
+}
+
+static int
+addRuleName (FileInfo * nested, CharsString * name)
+{
+ int k;
+ struct RuleName *nameRule;
+ if (!(nameRule = malloc (sizeof (*nameRule) + CHARSIZE *
+ (name->length - 1))))
+ {
+ compileError (nested, "not enough memory");
+ return 0;
+ }
+ memset (nameRule, 0, sizeof (*nameRule));
+ for (k = 0; k < name->length; k++)
+ {
+ TranslationTableCharacter *ch = definedCharOrDots
+ (nested, name->chars[k],
+ 0);
+ if (!(ch->attributes & CTC_Letter))
+ {
+ compileError (nested, "a name may contain only letters");
+ return 0;
+ }
+ nameRule->name[k] = name->chars[k];
+ }
+ nameRule->length = name->length;
+ nameRule->ruleOffset = newRuleOffset;
+ nameRule->next = ruleNames;
+ ruleNames = nameRule;
+ return 1;
+}
+
+static void
+deallocateRuleNames (void)
+{
+ while (ruleNames)
+ {
+ struct RuleName *nameRule = ruleNames;
+ ruleNames = ruleNames->next;
+ if (nameRule)
+ free (nameRule);
+ }
+}
+
+static int
+compileSwapDots (FileInfo * nested, CharsString * source, CharsString * dest)
+{
+ int k = 0;
+ int kk = 0;
+ CharsString dotsSource;
+ CharsString dotsDest;
+ dest->length = 0;
+ dotsSource.length = 0;
+ while (k <= source->length)
+ {
+ if (source->chars[k] != ',' && k != source->length)
+ dotsSource.chars[dotsSource.length++] = source->chars[k];
+ else
+ {
+ if (!parseDots (nested, &dotsDest, &dotsSource))
+ return 0;
+ dest->chars[dest->length++] = dotsDest.length + 1;
+ for (kk = 0; kk < dotsDest.length; kk++)
+ dest->chars[dest->length++] = dotsDest.chars[kk];
+ dotsSource.length = 0;
+ }
+ k++;
+ }
+ return 1;
+}
+
+static int
+compileSwap (FileInfo * nested, TranslationTableOpcode opcode)
+{
+ CharsString ruleChars;
+ CharsString ruleDots;
+ CharsString name;
+ CharsString matches;
+ CharsString replacements;
+ if (!getToken (nested, &name, "name operand"))
+ return 0;
+ if (!getToken (nested, &matches, "matches operand"))
+ return 0;
+ if (!getToken (nested, &replacements, "replacements operand"))
+ return 0;
+ if (opcode == CTO_SwapCc || opcode == CTO_SwapCd)
+ {
+ if (!parseChars (nested, &ruleChars, &matches))
+ return 0;
+ }
+ else
+ {
+ if (!compileSwapDots (nested, &matches, &ruleChars))
+ return 0;
+ }
+ if (opcode == CTO_SwapCc)
+ {
+ if (!parseChars (nested, &ruleDots, &replacements))
+ return 0;
+ }
+ else
+ {
+ if (!compileSwapDots (nested, &replacements, &ruleDots))
+ return 0;
+ }
+ if (!addRule (nested, opcode, &ruleChars, &ruleDots, 0, 0))
+ return 0;
+ if (!addRuleName (nested, &name))
+ return 0;
+ return 1;
+}
+
+
+static int
+getNumber (widechar * source, widechar * dest)
+{
+/*Convert a string of wide character digits to an integer*/
+ int k = 0;
+ *dest = 0;
+ while (source[k] >= '0' && source[k] <= '9')
+ *dest = 10 * *dest + (source[k++] - '0');
+ return k;
+}
+
+/* Start of multipass compiler*/
+static CharsString passRuleChars;
+static CharsString passRuleDots;
+static CharsString passHoldString;
+static CharsString passLine;
+static int passLinepos;
+static int passPrevLinepos;
+static widechar passHoldNumber;
+static widechar passEmphasis;
+static TranslationTableCharacterAttributes passAttributes;
+static FileInfo *passNested;
+static TranslationTableOpcode passOpcode;
+static widechar *passInstructions;
+static int passIC;
+
+static int
+passGetAttributes ()
+{
+ int more = 1;
+ passAttributes = 0;
+ while (more)
+ {
+ switch (passLine.chars[passLinepos])
+ {
+ case pass_any:
+ passAttributes = 0xffffffff;
+ break;
+ case pass_digit:
+ passAttributes |= CTC_Digit;
+ break;
+ case pass_litDigit:
+ passAttributes |= CTC_LitDigit;
+ break;
+ case pass_letter:
+ passAttributes |= CTC_Letter;
+ break;
+ case pass_math:
+ passAttributes |= CTC_Math;
+ break;
+ case pass_punctuation:
+ passAttributes |= CTC_Punctuation;
+ break;
+ case pass_sign:
+ passAttributes |= CTC_Sign;
+ break;
+ case pass_space:
+ passAttributes |= CTC_Space;
+ break;
+ case pass_uppercase:
+ passAttributes |= CTC_UpperCase;
+ break;
+ case pass_lowercase:
+ passAttributes |= CTC_LowerCase;
+ break;
+ case pass_class1:
+ passAttributes |= CTC_Class1;
+ break;
+ case pass_class2:
+ passAttributes |= CTC_Class2;
+ break;
+ case pass_class3:
+ passAttributes |= CTC_Class3;
+ break;
+ case pass_class4:
+ passAttributes |= CTC_Class4;
+ break;
+ default:
+ more = 0;
+ break;
+ }
+ if (more)
+ passLinepos++;
+ }
+ if (!passAttributes)
+ {
+ compileError (passNested, "Missing attribute");
+ passLinepos--;
+ return 0;
+ }
+ return 1;
+}
+
+static int
+passGetEmphasis ()
+{
+ int more = 1;
+ passLinepos++;
+ passEmphasis = 0;
+ while (more)
+ {
+ switch (passLine.chars[passLinepos])
+ {
+ case 'i':
+ passEmphasis |= italic;
+ break;
+ case 'b':
+ passEmphasis |= bold;
+ break;
+ case 'u':
+ passEmphasis |= underline;
+ break;
+ case 'c':
+ passEmphasis |= computer_braille;
+ break;
+ default:
+ more = 0;
+ break;
+ }
+ if (more)
+ passLinepos++;
+ }
+ if (!passEmphasis)
+ {
+ compileError (passNested, "emphasis indicators expected");
+ passLinepos--;
+ return 0;
+ }
+ return 1;
+}
+
+static int
+passGetDots ()
+{
+ CharsString collectDots;
+ collectDots.length = 0;
+ while (passLinepos < passLine.length && (passLine.chars[passLinepos]
+ == '-'
+ || (passLine.chars[passLinepos] >=
+ '0'
+ && passLine.
+ chars[passLinepos] <= '9')
+ ||
+ ((passLine.
+ chars[passLinepos] | 32) >= 'a'
+ && (passLine.
+ chars[passLinepos] | 32) <=
+ 'f')))
+ collectDots.chars[collectDots.length++] = passLine.chars[passLinepos++];
+ if (!parseDots (passNested, &passHoldString, &collectDots))
+ return 0;
+ return 1;
+}
+
+static int
+passGetString ()
+{
+ passHoldString.length = 0;
+ while (1)
+ {
+ if (!passLine.chars[passLinepos])
+ {
+ compileError (passNested, "unterminated string");
+ return 0;
+ }
+ if (passLine.chars[passLinepos] == 34)
+ break;
+ if (passLine.chars[passLinepos] == QUOTESUB)
+ passHoldString.chars[passHoldString.length++] = 34;
+ else
+ passHoldString.chars[passHoldString.length++] =
+ passLine.chars[passLinepos];
+ passLinepos++;
+ }
+ passHoldString.chars[passHoldString.length] = 0;
+ passLinepos++;
+ return 1;
+}
+
+static int
+passGetNumber ()
+{
+ /*Convert a string of wide character digits to an integer */
+ passHoldNumber = 0;
+ while (passLine.chars[passLinepos] >= '0'
+ && passLine.chars[passLinepos] <= '9')
+ passHoldNumber =
+ 10 * passHoldNumber + (passLine.chars[passLinepos++] - '0');
+ return 1;
+}
+
+static int
+passGetName ()
+{
+ TranslationTableCharacterAttributes attr;
+ passHoldString.length = 0;
+ do
+ {
+ attr = definedCharOrDots (passNested, passLine.chars[passLinepos],
+ 0)->attributes;
+ if (passHoldString.length == 0)
+ {
+ if (!(attr & CTC_Letter))
+ {
+ passLinepos++;
+ continue;
+ }
+ }
+ if (!(attr & CTC_Letter))
+ break;
+ passHoldString.chars[passHoldString.length++] =
+ passLine.chars[passLinepos];
+ passLinepos++;
+ }
+ while (passLinepos < passLine.length);
+ return 1;
+}
+
+static int
+passIsKeyword (const char *token)
+{
+ int k;
+ int length = strlen (token);
+ int ch = passLine.chars[passLinepos + length + 1];
+ if (((ch | 32) >= 'a' && (ch | 32) <= 'z') || (ch >= '0' && ch <= '9'))
+ return 0;
+ for (k = 0; k < length && passLine.chars[passLinepos + k + 1]
+ == (widechar) token[k]; k++);
+ if (k == length)
+ {
+ passLinepos += length + 1;
+ return 1;
+ }
+ return 0;
+}
+
+struct PassName
+{
+ struct PassName *next;
+ int varnum;
+ widechar length;
+ widechar name[1];
+};
+static struct PassName *passNames = NULL;
+
+static int
+passFindName (const CharsString * name)
+{
+ const struct PassName *curname = passNames;
+ CharsString augmentedName;
+ for (augmentedName.length = 0; augmentedName.length < name->length;
+ augmentedName.length++)
+ augmentedName.chars[augmentedName.length] =
+ name->chars[augmentedName.length];
+ augmentedName.chars[augmentedName.length++] = passOpcode;
+ while (curname)
+ {
+ if ((augmentedName.length == curname->length) &&
+ (memcmp
+ (&augmentedName.chars[0], curname->name,
+ CHARSIZE * name->length) == 0))
+ return curname->varnum;
+ curname = curname->next;
+ }
+ compileError (passNested, "name not found");
+ return 0;
+}
+
+static int
+passAddName (CharsString * name, int var)
+{
+ int k;
+ struct PassName *curname;
+ CharsString augmentedName;
+ for (augmentedName.length = 0;
+ augmentedName.length < name->length; augmentedName.length++)
+ augmentedName.
+ chars[augmentedName.length] = name->chars[augmentedName.length];
+ augmentedName.chars[augmentedName.length++] = passOpcode;
+ if (!
+ (curname =
+ malloc (sizeof (*curname) + CHARSIZE * (augmentedName.length - 1))))
+ {
+ compileError (passNested, "not enough memory");
+ return 0;
+ }
+ memset (curname, 0, sizeof (*curname));
+ for (k = 0; k < augmentedName.length; k++)
+ {
+ curname->name[k] = augmentedName.chars[k];
+ }
+ curname->length = augmentedName.length;
+ curname->varnum = var;
+ curname->next = passNames;
+ passNames = curname;
+ return 1;
+}
+
+static pass_Codes
+passGetScriptToken ()
+{
+ while (passLinepos < passLine.length)
+ {
+ passPrevLinepos = passLinepos;
+ switch (passLine.chars[passLinepos])
+ {
+ case '\"':
+ passLinepos++;
+ if (passGetString ())
+ return pass_string;
+ return pass_invalidToken;
+ case '@':
+ passLinepos++;
+ if (passGetDots ())
+ return pass_dots;
+ return pass_invalidToken;
+ case '#': /*comment */
+ passLinepos = passLine.length + 1;
+ return pass_noMoreTokens;
+ case '!':
+ if (passLine.chars[passLinepos + 1] == '=')
+ {
+ passLinepos += 2;
+ return pass_noteq;
+ }
+ passLinepos++;
+ return pass_not;
+ case '-':
+ passLinepos++;
+ return pass_hyphen;
+ case '=':
+ passLinepos++;
+ return pass_eq;
+ case '<':
+ passLinepos++;
+ if (passLine.chars[passLinepos] == '=')
+ {
+ passLinepos++;
+ return pass_lteq;
+ }
+ return pass_lt;
+ case '>':
+ passLinepos++;
+ if (passLine.chars[passLinepos] == '=')
+ {
+ passLinepos++;
+ return pass_gteq;
+ }
+ return pass_gt;
+ case '+':
+ passLinepos++;
+ return pass_plus;
+ case '(':
+ passLinepos++;
+ return pass_leftParen;
+ case ')':
+ passLinepos++;
+ return pass_rightParen;
+ case ',':
+ passLinepos++;
+ return pass_comma;
+ case '&':
+ if (passLine.chars[passLinepos = 1] == '&')
+ {
+ passLinepos += 2;
+ return pass_and;
+ }
+ return pass_invalidToken;
+ case '|':
+ if (passLine.chars[passLinepos + 1] == '|')
+ {
+ passLinepos += 2;
+ return pass_or;
+ }
+ return pass_invalidToken;
+ case 'a':
+ if (passIsKeyword ("ttr"))
+ return pass_attributes;
+ passGetName ();
+ return pass_nameFound;
+ case 'b':
+ if (passIsKeyword ("ack"))
+ return pass_lookback;
+ if (passIsKeyword ("ool"))
+ return pass_boolean;
+ passGetName ();
+ return pass_nameFound;
+ case 'c':
+ if (passIsKeyword ("lass"))
+ return pass_class;
+ passGetName ();
+ return pass_nameFound;
+ case 'd':
+ if (passIsKeyword ("ef"))
+ return pass_define;
+ passGetName ();
+ return pass_nameFound;
+ case 'e':
+ if (passIsKeyword ("mph"))
+ return pass_emphasis;
+ passGetName ();
+ return pass_nameFound;
+ case 'f':
+ if (passIsKeyword ("ind"))
+ return pass_search;
+ if (passIsKeyword ("irst"))
+ return pass_first;
+ passGetName ();
+ return pass_nameFound;
+ case 'g':
+ if (passIsKeyword ("roup"))
+ return pass_group;
+ passGetName ();
+ return pass_nameFound;
+ case 'i':
+ if (passIsKeyword ("f"))
+ return pass_if;
+ passGetName ();
+ return pass_nameFound;
+ case 'l':
+ if (passIsKeyword ("ast"))
+ return pass_last;
+ passGetName ();
+ return pass_nameFound;
+ case 'm':
+ if (passIsKeyword ("ark"))
+ return pass_mark;
+ passGetName ();
+ return pass_nameFound;
+ case 'r':
+ if (passIsKeyword ("epgroup"))
+ return pass_repGroup;
+ if (passIsKeyword ("epcopy"))
+ return pass_copy;
+ if (passIsKeyword ("epomit"))
+ return pass_omit;
+ if (passIsKeyword ("ep"))
+ return pass_replace;
+ passGetName ();
+ return pass_nameFound;
+ case 's':
+ if (passIsKeyword ("cript"))
+ return pass_script;
+ if (passIsKeyword ("wap"))
+ return pass_swap;
+ passGetName ();
+ return pass_nameFound;
+ case 't':
+ if (passIsKeyword ("hen"))
+ return pass_then;
+ passGetName ();
+ return pass_nameFound;
+ default:
+ if (passLine.chars[passLinepos] <= 32)
+ {
+ passLinepos++;
+ break;
+ }
+ if (passLine.chars[passLinepos] >= '0'
+ && passLine.chars[passLinepos] <= '9')
+ {
+ passGetNumber ();
+ return pass_numberFound;
+ }
+ else
+ {
+ if (!passGetName ())
+ return pass_invalidToken;
+ else
+ return pass_nameFound;
+ }
+ }
+ }
+ return pass_noMoreTokens;
+}
+
+static int
+passIsLeftParen ()
+{
+ pass_Codes passCode = passGetScriptToken ();
+ if (passCode != pass_leftParen)
+ {
+ compileError (passNested, "'(' expected");
+ return 0;
+ }
+ return 1;
+}
+
+static int
+passIsName ()
+{
+ pass_Codes passCode = passGetScriptToken ();
+ if (passCode != pass_nameFound)
+ {
+ compileError (passNested, "a name expected");
+ return 0;
+ }
+ return 1;
+}
+
+static int
+passIsComma ()
+{
+ pass_Codes passCode = passGetScriptToken ();
+ if (passCode != pass_comma)
+ {
+ compileError (passNested, "',' expected");
+ return 0;
+ }
+ return 1;
+}
+
+static int
+passIsNumber ()
+{
+ pass_Codes passCode = passGetScriptToken ();
+ if (passCode != pass_numberFound)
+ {
+ compileError (passNested, "a number expected");
+ return 0;
+ }
+ return 1;
+}
+
+static int
+passIsRightParen ()
+{
+ pass_Codes passCode = passGetScriptToken ();
+ if (passCode != pass_rightParen)
+ {
+ compileError (passNested, "')' expected");
+ return 0;
+ }
+ return 1;
+}
+
+static int
+passGetRange ()
+{
+ pass_Codes passCode = passGetScriptToken ();
+ if (!(passCode == pass_comma || passCode == pass_rightParen))
+ {
+ compileError (passNested, "invalid range");
+ return 0;
+ }
+ if (passCode == pass_rightParen)
+ {
+ passInstructions[passIC++] = 1;
+ passInstructions[passIC++] = 1;
+ return 1;
+ }
+ if (!passIsNumber ())
+ return 0;
+ passInstructions[passIC++] = passHoldNumber;
+ passCode = passGetScriptToken ();
+ if (!(passCode == pass_comma || passCode == pass_rightParen))
+ {
+ compileError (passNested, "invalid range");
+ return 0;
+ }
+ if (passCode == pass_rightParen)
+ {
+ passInstructions[passIC++] = passHoldNumber;
+ return 1;
+ }
+ if (!passIsNumber ())
+ return 0;
+ passInstructions[passIC++] = passHoldNumber;
+ if (!passIsRightParen ())
+ return 0;
+ return 1;
+}
+
+static int
+passInsertAttributes ()
+{
+ passInstructions[passIC++] = pass_attributes;
+ passInstructions[passIC++] = passAttributes >> 16;
+ passInstructions[passIC++] = passAttributes & 0xffff;
+ if (!passGetRange ())
+ return 0;
+ return 1;
+}
+
+static int
+compilePassOpcode (FileInfo * nested, TranslationTableOpcode opcode)
+{
+/*Compile the operands of a pass opcode */
+ TranslationTableCharacterAttributes after = 0;
+ TranslationTableCharacterAttributes before = 0;
+ widechar passSubOp;
+ const struct CharacterClass *class;
+ TranslationTableOffset ruleOffset = 0;
+ TranslationTableRule *rule = NULL;
+ int k;
+ int kk = 0;
+ pass_Codes passCode;
+ int endTest = 0;
+ int isScript = 1;
+ passInstructions = passRuleDots.chars;
+ passIC = 0; /*Instruction counter */
+ passRuleChars.length = 0;
+ passNested = nested;
+ passOpcode = opcode;
+/* passHoldString and passLine are static variables declared
+ * previously.*/
+ passLinepos = 0;
+ passHoldString.length = 0;
+ for (k = nested->linepos; k < nested->linelen; k++)
+ passHoldString.chars[passHoldString.length++] = nested->line[k];
+ if (!eqasc2uni ((unsigned char *) "script", passHoldString.chars, 6))
+ {
+ isScript = 0;
+#define SEPCHAR 0x0001
+ for (k = 0; k < passHoldString.length && passHoldString.chars[k] > 32;
+ k++);
+ if (k < passHoldString.length)
+ passHoldString.chars[k] = SEPCHAR;
+ else
+ {
+ compileError (passNested, "Invalid multipass operands");
+ return 0;
+ }
+ }
+ parseChars (passNested, &passLine, &passHoldString);
+ if (isScript)
+ {
+ int more = 1;
+ passCode = passGetScriptToken ();
+ if (passCode != pass_script)
+ {
+ compileError (passNested, "Invalid multipass statement");
+ return 0;
+ }
+ /* Declaratives */
+ while (more)
+ {
+ passCode = passGetScriptToken ();
+ switch (passCode)
+ {
+ case pass_define:
+ if (!passIsLeftParen ())
+ return 0;
+ if (!passIsName ())
+ return 0;
+ if (!passIsComma ())
+ return 0;
+ if (!passIsNumber ())
+ return 0;
+ if (!passIsRightParen ())
+ return 0;
+ passAddName (&passHoldString, passHoldNumber);
+ break;
+ case pass_if:
+ more = 0;
+ break;
+ default:
+ compileError (passNested,
+ "invalid definition in declarative part");
+ return 0;
+ }
+ }
+ /* if part */
+ more = 1;
+ while (more)
+ {
+ passCode = passGetScriptToken ();
+ passSubOp = passCode;
+ switch (passCode)
+ {
+ case pass_not:
+ passInstructions[passIC++] = pass_not;
+ break;
+ case pass_first:
+ passInstructions[passIC++] = pass_first;
+ break;
+ case pass_last:
+ passInstructions[passIC++] = pass_last;
+ break;
+ case pass_search:
+ passInstructions[passIC++] = pass_search;
+ break;
+ case pass_string:
+ if (opcode != CTO_Context && opcode != CTO_Correct)
+ {
+ compileError (passNested,
+ "Character strings can only be used with the context and correct opcodes.");
+ return 0;
+ }
+ passInstructions[passIC++] = pass_string;
+ goto ifDoCharsDots;
+ case pass_dots:
+ if (passOpcode == CTO_Correct || passOpcode == CTO_Context)
+ {
+ compileError (passNested,
+ "dot patterns cannot be specified in the if part\
+ of the correct or context opcodes");
+ return 0;
+ }
+ passInstructions[passIC++] = pass_dots;
+ ifDoCharsDots:
+ passInstructions[passIC++] = passHoldString.length;
+ for (kk = 0; kk < passHoldString.length; kk++)
+ passInstructions[passIC++] = passHoldString.chars[kk];
+ break;
+ case pass_attributes:
+ if (!passIsLeftParen ())
+ return 0;
+ if (!passGetAttributes ())
+ return 0;
+ if (!passInsertAttributes ())
+ return 0;
+ break;
+ case pass_emphasis:
+ if (!passIsLeftParen ())
+ return 0;
+ if (!passGetEmphasis ())
+ return 0;
+ /*Right parenthis handled by subfunctiion */
+ break;
+ case pass_lookback:
+ passInstructions[passIC++] = pass_lookback;
+ passCode = passGetScriptToken ();
+ if (passCode != pass_leftParen)
+ {
+ passInstructions[passIC++] = 1;
+ passLinepos = passPrevLinepos;
+ break;
+ }
+ if (!passIsNumber ())
+ return 0;
+ if (!passIsRightParen ())
+ return 0;
+ passInstructions[passIC] = passHoldNumber;
+ break;
+ case pass_group:
+ if (!passIsLeftParen ())
+ return 0;
+ break;
+ case pass_mark:
+ passInstructions[passIC++] = pass_startReplace;
+ passInstructions[passIC++] = pass_endReplace;
+ break;
+ case pass_replace:
+ passInstructions[passIC++] = pass_startReplace;
+ if (!passIsLeftParen ())
+ return 0;
+ break;
+ case pass_rightParen:
+ passInstructions[passIC++] = pass_endReplace;
+ break;
+ case pass_groupstart:
+ case pass_groupend:
+ if (!passIsLeftParen ())
+ return 0;
+ if (!passGetName ())
+ return 0;
+ if (!passIsRightParen ())
+ return 0;
+ ruleOffset = findRuleName (&passHoldString);
+ if (ruleOffset)
+ rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
+ if (rule && rule->opcode == CTO_Grouping)
+ {
+ passInstructions[passIC++] = passSubOp;
+ passInstructions[passIC++] = ruleOffset >> 16;
+ passInstructions[passIC++] = ruleOffset & 0xffff;
+ break;
+ }
+ else
+ {
+ compileError (passNested, "%s is not a grouping name",
+ showString (&passHoldString.chars[0],
+ passHoldString.length));
+ return 0;
+ }
+ break;
+ case pass_class:
+ if (!passIsLeftParen ())
+ return 0;
+ if (!passGetName ())
+ return 0;
+ if (!passIsRightParen ())
+ return 0;
+ if (!(class = findCharacterClass (&passHoldString)))
+ return 0;
+ passAttributes = class->attribute;
+ passInsertAttributes ();
+ break;
+ case pass_swap:
+ ruleOffset = findRuleName (&passHoldString);
+ if (!passIsLeftParen ())
+ return 0;
+ if (!passGetName ())
+ return 0;
+ if (!passIsRightParen ())
+ return 0;
+ ruleOffset = findRuleName (&passHoldString);
+ if (ruleOffset)
+ rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
+ if (rule
+ && (rule->opcode == CTO_SwapCc || rule->opcode == CTO_SwapCd
+ || rule->opcode == CTO_SwapDd))
+ {
+ passInstructions[passIC++] = pass_swap;
+ passInstructions[passIC++] = ruleOffset >> 16;
+ passInstructions[passIC++] = ruleOffset & 0xffff;
+ if (!passGetRange ())
+ return 0;
+ break;
+ }
+ compileError (passNested,
+ "%s is not a swap name.",
+ showString (&passHoldString.chars[0],
+ passHoldString.length));
+ return 0;
+ case pass_nameFound:
+ passHoldNumber = passFindName (&passHoldString);
+ passCode = passGetScriptToken ();
+ if (!(passCode == pass_eq || passCode == pass_lt || passCode
+ == pass_gt || passCode == pass_noteq || passCode ==
+ pass_lteq || passCode == pass_gteq))
+ {
+ compileError (nested,
+ "invalid comparison operator in if part");
+ return 0;
+ }
+ passInstructions[passIC++] = passCode;
+ passInstructions[passIC++] = passHoldNumber;
+ if (!passIsNumber ())
+ return 0;
+ passInstructions[passIC++] = passHoldNumber;
+ break;
+ case pass_then:
+ passInstructions[passIC++] = pass_endTest;
+ more = 0;
+ break;
+ default:
+ compileError (passNested, "invalid choice in if part");
+ return 0;
+ }
+ }
+
+ /* then part */
+ more = 1;
+ while (more)
+ {
+ passCode = passGetScriptToken ();
+ passSubOp = passCode;
+ switch (passCode)
+ {
+ case pass_string:
+ if (opcode != CTO_Correct)
+ {
+ compileError (passNested,
+ "Character strings can only be used in the then part with the correct opcode.");
+ return 0;
+ }
+ passInstructions[passIC++] = pass_string;
+ goto thenDoCharsDots;
+ case pass_dots:
+ if (opcode == CTO_Correct)
+ {
+ compileError (passNested,
+ "Dot patterns cannot be used with the correct opcode.");
+ return 0;
+ }
+ passInstructions[passIC++] = pass_dots;
+ thenDoCharsDots:
+ passInstructions[passIC++] = passHoldString.length;
+ for (kk = 0; kk < passHoldString.length; kk++)
+ passInstructions[passIC++] = passHoldString.chars[kk];
+ break;
+ case pass_nameFound:
+ passHoldNumber = passFindName (&passHoldString);
+ passCode = passGetScriptToken ();
+ if (!(passCode == pass_plus || passCode == pass_hyphen
+ || passCode == pass_eq))
+ {
+ compileError (nested,
+ "Invalid variable operator in then part");
+ return 0;
+ }
+ passInstructions[passIC++] = passCode;
+ passInstructions[passIC++] = passHoldNumber;
+ if (!passIsNumber ())
+ return 0;
+ passInstructions[passIC++] = passHoldNumber;
+ break;
+ case pass_copy:
+ passInstructions[passIC++] = pass_copy;
+ break;
+ case pass_omit:
+ passInstructions[passIC++] = pass_omit;
+ break;
+ case pass_swap:
+ ruleOffset = findRuleName (&passHoldString);
+ if (!passIsLeftParen ())
+ return 0;
+ if (!passGetName ())
+ return 0;
+ if (!passIsRightParen ())
+ return 0;
+ ruleOffset = findRuleName (&passHoldString);
+ if (ruleOffset)
+ rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
+ if (rule
+ && (rule->opcode == CTO_SwapCc || rule->opcode == CTO_SwapCd
+ || rule->opcode == CTO_SwapDd))
+ {
+ passInstructions[passIC++] = pass_swap;
+ passInstructions[passIC++] = ruleOffset >> 16;
+ passInstructions[passIC++] = ruleOffset & 0xffff;
+ if (!passGetRange ())
+ return 0;
+ break;
+ }
+ compileError (passNested,
+ "%s is not a swap name.",
+ showString (&passHoldString.chars[0],
+ passHoldString.length));
+ return 0;
+ case pass_noMoreTokens:
+ more = 0;
+ break;
+ default:
+ compileError (passNested, "invalid action in then part");
+ return 0;
+ }
+ }
+ }
+ else
+ {
+ /* Older machine-language-like "assembler". */
+
+ /*Compile test part */
+ for (k = 0; k < passLine.length && passLine.chars[k] != SEPCHAR; k++);
+ endTest = k;
+ passLine.chars[endTest] = pass_endTest;
+ passLinepos = 0;
+ while (passLinepos <= endTest)
+ {
+ switch ((passSubOp = passLine.chars[passLinepos]))
+ {
+ case pass_lookback:
+ passInstructions[passIC++] = pass_lookback;
+ passLinepos++;
+ passGetNumber ();
+ if (passHoldNumber == 0)
+ passHoldNumber = 1;
+ passInstructions[passIC++] = passHoldNumber;
+ break;
+ case pass_not:
+ passInstructions[passIC++] = pass_not;
+ passLinepos++;
+ break;
+ case pass_first:
+ passInstructions[passIC++] = pass_first;
+ passLinepos++;
+ break;
+ case pass_last:
+ passInstructions[passIC++] = pass_last;
+ passLinepos++;
+ break;
+ case pass_search:
+ passInstructions[passIC++] = pass_search;
+ passLinepos++;
+ break;
+ case pass_string:
+ if (opcode != CTO_Context && opcode != CTO_Correct)
+ {
+ compileError (passNested,
+ "Character strings can only be used with the context and correct opcodes.");
+ return 0;
+ }
+ passLinepos++;
+ passInstructions[passIC++] = pass_string;
+ passGetString ();
+ goto testDoCharsDots;
+ case pass_dots:
+ passLinepos++;
+ passInstructions[passIC++] = pass_dots;
+ passGetDots ();
+ testDoCharsDots:
+ if (passHoldString.length == 0)
+ return 0;
+ passInstructions[passIC++] = passHoldString.length;
+ for (kk = 0; kk < passHoldString.length; kk++)
+ passInstructions[passIC++] = passHoldString.chars[kk];
+ break;
+ case pass_startReplace:
+ passInstructions[passIC++] = pass_startReplace;
+ passLinepos++;
+ break;
+ case pass_endReplace:
+ passInstructions[passIC++] = pass_endReplace;
+ passLinepos++;
+ break;
+ case pass_variable:
+ passLinepos++;
+ passGetNumber ();
+ switch (passLine.chars[passLinepos])
+ {
+ case pass_eq:
+ passInstructions[passIC++] = pass_eq;
+ goto doComp;
+ case pass_lt:
+ if (passLine.chars[passLinepos + 1] == pass_eq)
+ {
+ passLinepos++;
+ passInstructions[passIC++] = pass_lteq;
+ }
+ else
+ passInstructions[passIC++] = pass_lt;
+ goto doComp;
+ case pass_gt:
+ if (passLine.chars[passLinepos + 1] == pass_eq)
+ {
+ passLinepos++;
+ passInstructions[passIC++] = pass_gteq;
+ }
+ else
+ passInstructions[passIC++] = pass_gt;
+ doComp:
+ passInstructions[passIC++] = passHoldNumber;
+ passLinepos++;
+ passGetNumber ();
+ passInstructions[passIC++] = passHoldNumber;
+ break;
+ default:
+ compileError (passNested, "incorrect comparison operator");
+ return 0;
+ }
+ break;
+ case pass_attributes:
+ passLinepos++;
+ passGetAttributes ();
+ insertAttributes:
+ passInstructions[passIC++] = pass_attributes;
+ passInstructions[passIC++] = passAttributes >> 16;
+ passInstructions[passIC++] = passAttributes & 0xffff;
+ getRange:
+ if (passLine.chars[passLinepos] == pass_until)
+ {
+ passLinepos++;
+ passInstructions[passIC++] = 1;
+ passInstructions[passIC++] = 0xffff;
+ break;
+ }
+ passGetNumber ();
+ if (passHoldNumber == 0)
+ {
+ passHoldNumber = passInstructions[passIC++] = 1;
+ passInstructions[passIC++] = 1; /*This is not an error */
+ break;
+ }
+ passInstructions[passIC++] = passHoldNumber;
+ if (passLine.chars[passLinepos] != pass_hyphen)
+ {
+ passInstructions[passIC++] = passHoldNumber;
+ break;
+ }
+ passLinepos++;
+ passGetNumber ();
+ if (passHoldNumber == 0)
+ {
+ compileError (passNested, "invalid range");
+ return 0;
+ }
+ passInstructions[passIC++] = passHoldNumber;
+ break;
+ case pass_groupstart:
+ case pass_groupend:
+ passLinepos++;
+ passGetName ();
+ ruleOffset = findRuleName (&passHoldString);
+ if (ruleOffset)
+ rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
+ if (rule && rule->opcode == CTO_Grouping)
+ {
+ passInstructions[passIC++] = passSubOp;
+ passInstructions[passIC++] = ruleOffset >> 16;
+ passInstructions[passIC++] = ruleOffset & 0xffff;
+ break;
+ }
+ else
+ {
+ compileError (passNested, "%s is not a grouping name",
+ showString (&passHoldString.chars[0],
+ passHoldString.length));
+ return 0;
+ }
+ break;
+ case pass_swap:
+ passGetName ();
+ if ((class = findCharacterClass (&passHoldString)))
+ {
+ passAttributes = class->attribute;
+ goto insertAttributes;
+ }
+ ruleOffset = findRuleName (&passHoldString);
+ if (ruleOffset)
+ rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
+ if (rule
+ && (rule->opcode == CTO_SwapCc || rule->opcode == CTO_SwapCd
+ || rule->opcode == CTO_SwapDd))
+ {
+ passInstructions[passIC++] = pass_swap;
+ passInstructions[passIC++] = ruleOffset >> 16;
+ passInstructions[passIC++] = ruleOffset & 0xffff;
+ goto getRange;
+ }
+ compileError (passNested,
+ "%s is neither a class name nor a swap name.",
+ showString (&passHoldString.chars[0],
+ passHoldString.length));
+ return 0;
+ case pass_endTest:
+ passInstructions[passIC++] = pass_endTest;
+ passLinepos++;
+ break;
+ default:
+ compileError (passNested,
+ "incorrect operator '%c ' in test part",
+ passLine.chars[passLinepos]);
+ return 0;
+ }
+
+ } /*Compile action part */
+
+ /* Compile action part */
+ while (passLinepos < passLine.length &&
+ passLine.chars[passLinepos] <= 32)
+ passLinepos++;
+ while (passLinepos < passLine.length &&
+ passLine.chars[passLinepos] > 32)
+ {
+ switch ((passSubOp = passLine.chars[passLinepos]))
+ {
+ case pass_string:
+ if (opcode != CTO_Correct)
+ {
+ compileError (passNested,
+ "Character strings can only be used with the ccorrect opcode.");
+ return 0;
+ }
+ passLinepos++;
+ passInstructions[passIC++] = pass_string;
+ passGetString ();
+ goto actionDoCharsDots;
+ case pass_dots:
+ if (opcode == CTO_Correct)
+ {
+ compileError (passNested,
+ "Dot patterns cannot be used with the correct opcode.");
+ return 0;
+ }
+ passLinepos++;
+ passGetDots ();
+ passInstructions[passIC++] = pass_dots;
+ actionDoCharsDots:
+ if (passHoldString.length == 0)
+ return 0;
+ passInstructions[passIC++] = passHoldString.length;
+ for (kk = 0; kk < passHoldString.length; kk++)
+ passInstructions[passIC++] = passHoldString.chars[kk];
+ break;
+ case pass_variable:
+ passLinepos++;
+ passGetNumber ();
+ switch (passLine.chars[passLinepos])
+ {
+ case pass_eq:
+ passInstructions[passIC++] = pass_eq;
+ passInstructions[passIC++] = passHoldNumber;
+ passLinepos++;
+ passGetNumber ();
+ passInstructions[passIC++] = passHoldNumber;
+ break;
+ case pass_plus:
+ case pass_hyphen:
+ passInstructions[passIC++] = passLine.chars[passLinepos];
+ passInstructions[passIC++] = passHoldNumber;
+ break;
+ default:
+ compileError (passNested,
+ "incorrect variable operator in action part");
+ return 0;
+ }
+ break;
+ case pass_copy:
+ passInstructions[passIC++] = pass_copy;
+ passLinepos++;
+ break;
+ case pass_omit:
+ passInstructions[passIC++] = pass_omit;
+ passLinepos++;
+ break;
+ case pass_groupreplace:
+ case pass_groupstart:
+ case pass_groupend:
+ passLinepos++;
+ passGetName ();
+ ruleOffset = findRuleName (&passHoldString);
+ if (ruleOffset)
+ rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
+ if (rule && rule->opcode == CTO_Grouping)
+ {
+ passInstructions[passIC++] = passSubOp;
+ passInstructions[passIC++] = ruleOffset >> 16;
+ passInstructions[passIC++] = ruleOffset & 0xffff;
+ break;
+ }
+ compileError (passNested, "%s is not a grouping name",
+ showString (&passHoldString.chars[0],
+ passHoldString.length));
+ return 0;
+ case pass_swap:
+ passLinepos++;
+ passGetName ();
+ ruleOffset = findRuleName (&passHoldString);
+ if (ruleOffset)
+ rule = (TranslationTableRule *) & table->ruleArea[ruleOffset];
+ if (rule
+ && (rule->opcode == CTO_SwapCc || rule->opcode == CTO_SwapCd
+ || rule->opcode == CTO_SwapDd))
+ {
+ passInstructions[passIC++] = pass_swap;
+ passInstructions[passIC++] = ruleOffset >> 16;
+ passInstructions[passIC++] = ruleOffset & 0xffff;
+ break;
+ }
+ compileError (passNested, "%s is not a swap name.",
+ showString (&passHoldString.chars[0],
+ passHoldString.length));
+ return 0;
+ break;
+ default:
+ compileError (passNested, "incorrect operator in action part");
+ return 0;
+ }
+ }
+ }
+
+ /*Analyze and add rule */
+ passRuleDots.length = passIC;
+ passIC = 0;
+ while (passIC < passRuleDots.length)
+ {
+ int start = 0;
+ switch (passInstructions[passIC])
+ {
+ case pass_string:
+ case pass_dots:
+ case pass_attributes:
+ case pass_swap:
+ start = 1;
+ break;
+ case pass_groupstart:
+ case pass_groupend:
+ start = 1;
+ break;
+ case pass_eq:
+ case pass_lt:
+ case pass_gt:
+ case pass_lteq:
+ case pass_gteq:
+ passIC += 3;
+ break;
+ case pass_lookback:
+ passIC += 2;
+ break;
+ case pass_not:
+ case pass_startReplace:
+ case pass_endReplace:
+ case pass_first:
+ passIC++;
+ break;
+ default:
+ compileError (passNested,
+ "Test/if part must contain characters, dots, attributes or class \
+swap.");
+ return 0;
+ }
+ if (start)
+ break;
+ }
+
+ switch (passInstructions[passIC])
+ {
+ case pass_string:
+ case pass_dots:
+ for (k = 0; k < passInstructions[passIC + 1]; k++)
+ passRuleChars.chars[k] = passInstructions[passIC + 2 + k];
+ passRuleChars.length = k;
+ after = before = 0;
+ break;
+ case pass_attributes:
+ case pass_groupstart:
+ case pass_groupend:
+ case pass_swap:
+ after = passRuleDots.length;
+ before = 0;
+ break;
+ default:
+ break;
+ }
+ if (!addRule (passNested, opcode, &passRuleChars, &passRuleDots,
+ after, before))
+ return 0;
+ return 1;
+}
+
+/* End of multipass compiler */
+
+static int
+compileBrailleIndicator (FileInfo * nested, char *ermsg,
+ TranslationTableOpcode opcode,
+ TranslationTableOffset * rule)
+{
+ CharsString token;
+ CharsString cells;
+ if (getToken (nested, &token, ermsg))
+ if (parseDots (nested, &cells, &token))
+ if (!addRule (nested, opcode, NULL, &cells, 0, 0))
+ return 0;
+ *rule = newRuleOffset;
+ return 1;
+}
+
+static int
+compileNumber (FileInfo * nested)
+{
+ CharsString token;
+ widechar dest;
+ if (!getToken (nested, &token, "number"))
+ return 0;
+ getNumber (&token.chars[0], &dest);
+ if (!(dest > 0))
+ {
+ compileError (nested, "a nonzero positive number is required");
+ return 0;
+ }
+ return dest;
+}
+
+static int
+compileGrouping (FileInfo * nested)
+{
+ int k;
+ CharsString name;
+ CharsString groupChars;
+ CharsString groupDots;
+ CharsString dotsParsed;
+ TranslationTableCharacter *charsDotsPtr;
+ widechar endChar;
+ widechar endDots;
+ if (!getToken (nested, &name, "name operand"))
+ return 0;
+ if (!getRuleCharsText (nested, &groupChars))
+ return 0;
+ if (!getToken (nested, &groupDots, "dots operand"))
+ return 0;
+ for (k = 0; k < groupDots.length && groupDots.chars[k] != ','; k++);
+ if (k == groupDots.length)
+ {
+ compileError (nested,
+ "Dots operand must consist of two cells separated by a comma");
+ return 0;
+ }
+ groupDots.chars[k] = '-';
+ if (!parseDots (nested, &dotsParsed, &groupDots))
+ return 0;
+ if (groupChars.length != 2 || dotsParsed.length != 2)
+ {
+ compileError (nested,
+ "two Unicode characters and two cells separated by a comma are needed.");
+ return 0;
+ }
+ charsDotsPtr = addCharOrDots (nested, groupChars.chars[0], 0);
+ charsDotsPtr->attributes |= CTC_Math;
+ charsDotsPtr->uppercase = charsDotsPtr->realchar;
+ charsDotsPtr->lowercase = charsDotsPtr->realchar;
+ charsDotsPtr = addCharOrDots (nested, groupChars.chars[1], 0);
+ charsDotsPtr->attributes |= CTC_Math;
+ charsDotsPtr->uppercase = charsDotsPtr->realchar;
+ charsDotsPtr->lowercase = charsDotsPtr->realchar;
+ charsDotsPtr = addCharOrDots (nested, dotsParsed.chars[0], 1);
+ charsDotsPtr->attributes |= CTC_Math;
+ charsDotsPtr->uppercase = charsDotsPtr->realchar;
+ charsDotsPtr->lowercase = charsDotsPtr->realchar;
+ charsDotsPtr = addCharOrDots (nested, dotsParsed.chars[1], 1);
+ charsDotsPtr->attributes |= CTC_Math;
+ charsDotsPtr->uppercase = charsDotsPtr->realchar;
+ charsDotsPtr->lowercase = charsDotsPtr->realchar;
+ if (!addRule (nested, CTO_Grouping, &groupChars, &dotsParsed, 0, 0))
+ return 0;
+ if (!addRuleName (nested, &name))
+ return 0;
+ putCharAndDots (nested, groupChars.chars[0], dotsParsed.chars[0]);
+ putCharAndDots (nested, groupChars.chars[1], dotsParsed.chars[1]);
+ endChar = groupChars.chars[1];
+ endDots = dotsParsed.chars[1];
+ groupChars.length = dotsParsed.length = 1;
+ if (!addRule (nested, CTO_Math, &groupChars, &dotsParsed, 0, 0))
+ return 0;
+ groupChars.chars[0] = endChar;
+ dotsParsed.chars[0] = endDots;
+ if (!addRule (nested, CTO_Math, &groupChars, &dotsParsed, 0, 0))
+ return 0;
+ return 1;
+}
+
+static int
+compileUplow (FileInfo * nested)
+{
+ int k;
+ TranslationTableCharacter *upperChar;
+ TranslationTableCharacter *lowerChar;
+ TranslationTableCharacter *upperCell = NULL;
+ TranslationTableCharacter *lowerCell = NULL;
+ CharsString ruleChars;
+ CharsString ruleDots;
+ CharsString upperDots;
+ CharsString lowerDots;
+ int haveLowerDots = 0;
+ TranslationTableCharacterAttributes attr;
+ if (!getRuleCharsText (nested, &ruleChars))
+ return 0;
+ if (!getToken (nested, &ruleDots, "dots operand"))
+ return 0;
+ for (k = 0; k < ruleDots.length && ruleDots.chars[k] != ','; k++);
+ if (k == ruleDots.length)
+ {
+ if (!parseDots (nested, &upperDots, &ruleDots))
+ return 0;
+ lowerDots.length = upperDots.length;
+ for (k = 0; k < upperDots.length; k++)
+ lowerDots.chars[k] = upperDots.chars[k];
+ lowerDots.chars[k] = 0;
+ }
+ else
+ {
+ haveLowerDots = ruleDots.length;
+ ruleDots.length = k;
+ if (!parseDots (nested, &upperDots, &ruleDots))
+ return 0;
+ ruleDots.length = 0;
+ k++;
+ for (; k < haveLowerDots; k++)
+ ruleDots.chars[ruleDots.length++] = ruleDots.chars[k];
+ if (!parseDots (nested, &lowerDots, &ruleDots))
+ return 0;
+ }
+ if (ruleChars.length != 2 || upperDots.length < 1)
+ {
+ compileError (nested,
+ "Exactly two Unicode characters and at least one cell are required.");
+ return 0;
+ }
+ if (haveLowerDots && lowerDots.length < 1)
+ {
+ compileError (nested, "at least one cell is required after the comma.");
+ return 0;
+ }
+ upperChar = addCharOrDots (nested, ruleChars.chars[0], 0);
+ upperChar->attributes |= CTC_Letter | CTC_UpperCase;
+ upperChar->uppercase = ruleChars.chars[0];
+ upperChar->lowercase = ruleChars.chars[1];
+ lowerChar = addCharOrDots (nested, ruleChars.chars[1], 0);
+ lowerChar->attributes |= CTC_Letter | CTC_LowerCase;
+ lowerChar->uppercase = ruleChars.chars[0];
+ lowerChar->lowercase = ruleChars.chars[1];
+ for (k = 0; k < upperDots.length; k++)
+ if (!compile_findCharOrDots (upperDots.chars[k], 1))
+ {
+ attr = CTC_Letter | CTC_UpperCase;
+ upperCell = addCharOrDots (nested, upperDots.chars[k], 1);
+ if (upperDots.length != 1)
+ attr = CTC_Space;
+ upperCell->attributes |= attr;
+ upperCell->uppercase = upperCell->realchar;
+ }
+ if (haveLowerDots)
+ {
+ for (k = 0; k < lowerDots.length; k++)
+ if (!compile_findCharOrDots (lowerDots.chars[k], 1))
+ {
+ attr = CTC_Letter | CTC_LowerCase;
+ lowerCell = addCharOrDots (nested, lowerDots.chars[k], 1);
+ if (lowerDots.length != 1)
+ attr = CTC_Space;
+ lowerCell->attributes |= attr;
+ lowerCell->lowercase = lowerCell->realchar;
+ }
+ }
+ else if (upperCell != NULL && upperDots.length == 1)
+ upperCell->attributes |= CTC_LowerCase;
+ if (lowerDots.length == 1)
+ putCharAndDots (nested, ruleChars.chars[1], lowerDots.chars[0]);
+ if (upperCell != NULL)
+ upperCell->lowercase = lowerDots.chars[0];
+ if (lowerCell != NULL)
+ lowerCell->uppercase = upperDots.chars[0];
+ if (upperDots.length == 1)
+ putCharAndDots (nested, ruleChars.chars[0], upperDots.chars[0]);
+ ruleChars.length = 1;
+ ruleChars.chars[2] = ruleChars.chars[0];
+ ruleChars.chars[0] = ruleChars.chars[1];
+ if (!addRule (nested, CTO_LowerCase, &ruleChars, &lowerDots, 0, 0))
+ return 0;
+ ruleChars.chars[0] = ruleChars.chars[2];
+ if (!addRule (nested, CTO_UpperCase, &ruleChars, &upperDots, 0, 0))
+ return 0;
+ return 1;
+}
+
+/*Functions for compiling hyphenation tables*/
+
+typedef struct /*hyphenation dictionary: finite state machine */
+{
+ int numStates;
+ HyphenationState *states;
+} HyphenDict;
+
+#define DEFAULTSTATE 0xffff
+#define HYPHENHASHSIZE 8191
+
+typedef struct
+{
+ void *next;
+ CharsString *key;
+ int val;
+} HyphenHashEntry;
+
+typedef struct
+{
+ HyphenHashEntry *entries[HYPHENHASHSIZE];
+} HyphenHashTab;
+
+/* a hash function from ASU - adapted from Gtk+ */
+static unsigned int
+hyphenStringHash (const CharsString * s)
+{
+ int k;
+ unsigned int h = 0, g;
+ for (k = 0; k < s->length; k++)
+ {
+ h = (h << 4) + s->chars[k];
+ if ((g = h & 0xf0000000))
+ {
+ h = h ^ (g >> 24);
+ h = h ^ g;
+ }
+ }
+ return h;
+}
+
+static HyphenHashTab *
+hyphenHashNew (void)
+{
+ HyphenHashTab *hashTab;
+ hashTab = malloc (sizeof (HyphenHashTab));
+ memset (hashTab, 0, sizeof (HyphenHashTab));
+ return hashTab;
+}
+
+static void
+hyphenHashFree (HyphenHashTab * hashTab)
+{
+ int i;
+ HyphenHashEntry *e, *next;
+ for (i = 0; i < HYPHENHASHSIZE; i++)
+ for (e = hashTab->entries[i]; e; e = next)
+ {
+ next = e->next;
+ free (e->key);
+ free (e);
+ }
+ free (hashTab);
+}
+
+/* assumes that key is not already present! */
+static void
+hyphenHashInsert (HyphenHashTab * hashTab, const CharsString * key, int val)
+{
+ int i, j;
+ HyphenHashEntry *e;
+ i = hyphenStringHash (key) % HYPHENHASHSIZE;
+ e = malloc (sizeof (HyphenHashEntry));
+ e->next = hashTab->entries[i];
+ e->key = malloc ((key->length + 1) * CHARSIZE);
+ e->key->length = key->length;
+ for (j = 0; j < key->length; j++)
+ e->key->chars[j] = key->chars[j];
+ e->val = val;
+ hashTab->entries[i] = e;
+}
+
+/* return val if found, otherwise DEFAULTSTATE */
+static int
+hyphenHashLookup (HyphenHashTab * hashTab, const CharsString * key)
+{
+ int i, j;
+ HyphenHashEntry *e;
+ if (key->length == 0)
+ return 0;
+ i = hyphenStringHash (key) % HYPHENHASHSIZE;
+ for (e = hashTab->entries[i]; e; e = e->next)
+ {
+ if (key->length != e->key->length)
+ continue;
+ for (j = 0; j < key->length; j++)
+ if (key->chars[j] != e->key->chars[j])
+ break;
+ if (j == key->length)
+ return e->val;
+ }
+ return DEFAULTSTATE;
+}
+
+static int
+hyphenGetNewState (HyphenDict * dict, HyphenHashTab * hashTab, const
+ CharsString * string)
+{
+ hyphenHashInsert (hashTab, string, dict->numStates);
+ /* predicate is true if dict->numStates is a power of two */
+ if (!(dict->numStates & (dict->numStates - 1)))
+ dict->states = realloc (dict->states,
+ (dict->numStates << 1) *
+ sizeof (HyphenationState));
+ dict->states[dict->numStates].hyphenPattern = 0;
+ dict->states[dict->numStates].fallbackState = DEFAULTSTATE;
+ dict->states[dict->numStates].numTrans = 0;
+ dict->states[dict->numStates].trans.pointer = NULL;
+ return dict->numStates++;
+}
+
+/* add a transition from state1 to state2 through ch - assumes that the
+ transition does not already exist */
+static void
+hyphenAddTrans (HyphenDict * dict, int state1, int state2, widechar ch)
+{
+ int numTrans;
+ numTrans = dict->states[state1].numTrans;
+ if (numTrans == 0)
+ dict->states[state1].trans.pointer = malloc (sizeof (HyphenationTrans));
+ else if (!(numTrans & (numTrans - 1)))
+ dict->states[state1].trans.pointer = realloc
+ (dict->states[state1].trans.pointer,
+ (numTrans << 1) * sizeof (HyphenationTrans));
+ dict->states[state1].trans.pointer[numTrans].ch = ch;
+ dict->states[state1].trans.pointer[numTrans].newState = state2;
+ dict->states[state1].numTrans++;
+}
+
+static int
+compileHyphenation (FileInfo * nested, CharsString * encoding)
+{
+ CharsString hyph;
+ HyphenationTrans *holdPointer;
+ HyphenHashTab *hashTab;
+ CharsString word;
+ char pattern[MAXSTRING];
+ unsigned int stateNum = 0, lastState = 0;
+ int i, j, k = encoding->length;
+ widechar ch;
+ int found;
+ HyphenHashEntry *e;
+ HyphenDict dict;
+ TranslationTableOffset holdOffset;
+ /*Set aside enough space for hyphenation states and transitions in
+ * translation table. Must be done before anything else*/
+ reserveSpaceInTable (nested, 250000);
+ hashTab = hyphenHashNew ();
+ dict.numStates = 1;
+ dict.states = malloc (sizeof (HyphenationState));
+ dict.states[0].hyphenPattern = 0;
+ dict.states[0].fallbackState = DEFAULTSTATE;
+ dict.states[0].numTrans = 0;
+ dict.states[0].trans.pointer = NULL;
+ do
+ {
+ if (encoding->chars[0] == 'I')
+ {
+ if (!getToken (nested, &hyph, NULL))
+ continue;
+ }
+ else
+ {
+ /*UTF-8 */
+ if (!getToken (nested, &word, NULL))
+ continue;
+ parseChars (nested, &hyph, &word);
+ }
+ if (hyph.length == 0 || hyph.chars[0] == '#' || hyph.chars[0] ==
+ '%' || hyph.chars[0] == '<')
+ continue; /*comment */
+ for (i = 0; i < hyph.length; i++)
+ definedCharOrDots (nested, hyph.chars[i], 0);
+ j = 0;
+ pattern[j] = '0';
+ for (i = 0; i < hyph.length; i++)
+ {
+ if (hyph.chars[i] >= '0' && hyph.chars[i] <= '9')
+ pattern[j] = (char) hyph.chars[i];
+ else
+ {
+ word.chars[j] = hyph.chars[i];
+ pattern[++j] = '0';
+ }
+ }
+ word.chars[j] = 0;
+ word.length = j;
+ pattern[j + 1] = 0;
+ for (i = 0; pattern[i] == '0'; i++);
+ found = hyphenHashLookup (hashTab, &word);
+ if (found != DEFAULTSTATE)
+ stateNum = found;
+ else
+ stateNum = hyphenGetNewState (&dict, hashTab, &word);
+ k = j + 2 - i;
+ if (k > 0)
+ {
+ allocateSpaceInTable (nested,
+ &dict.states[stateNum].hyphenPattern, k);
+ memcpy (&table->ruleArea[dict.states[stateNum].hyphenPattern],
+ &pattern[i], k);
+ }
+ /* now, put in the prefix transitions */
+ while (found == DEFAULTSTATE)
+ {
+ lastState = stateNum;
+ ch = word.chars[word.length-- - 1];
+ found = hyphenHashLookup (hashTab, &word);
+ if (found != DEFAULTSTATE)
+ stateNum = found;
+ else
+ stateNum = hyphenGetNewState (&dict, hashTab, &word);
+ hyphenAddTrans (&dict, stateNum, lastState, ch);
+ }
+ }
+ while (getALine (nested));
+ /* put in the fallback states */
+ for (i = 0; i < HYPHENHASHSIZE; i++)
+ {
+ for (e = hashTab->entries[i]; e; e = e->next)
+ {
+ for (j = 1; j <= e->key->length; j++)
+ {
+ word.length = 0;
+ for (k = j; k < e->key->length; k++)
+ word.chars[word.length++] = e->key->chars[k];
+ stateNum = hyphenHashLookup (hashTab, &word);
+ if (stateNum != DEFAULTSTATE)
+ break;
+ }
+ if (e->val)
+ dict.states[e->val].fallbackState = stateNum;
+ }
+ }
+ hyphenHashFree (hashTab);
+/*Transfer hyphenation information to table*/
+ for (i = 0; i < dict.numStates; i++)
+ {
+ if (dict.states[i].numTrans == 0)
+ dict.states[i].trans.offset = 0;
+ else
+ {
+ holdPointer = dict.states[i].trans.pointer;
+ allocateSpaceInTable (nested,
+ &dict.states[i].trans.offset,
+ dict.states[i].numTrans *
+ sizeof (HyphenationTrans));
+ memcpy (&table->ruleArea[dict.states[i].trans.offset],
+ holdPointer,
+ dict.states[i].numTrans * sizeof (HyphenationTrans));
+ free (holdPointer);
+ }
+ }
+ allocateSpaceInTable (nested,
+ &holdOffset, dict.numStates *
+ sizeof (HyphenationState));
+ table->hyphenStatesArray = holdOffset;
+ /* Prevents segmentajion fault if table is reallocated */
+ memcpy (&table->ruleArea[table->hyphenStatesArray], &dict.states[0],
+ dict.numStates * sizeof (HyphenationState));
+ free (dict.states);
+ return 1;
+}
+
+static int
+compileNoBreak (FileInfo * nested)
+{
+ int k;
+ CharsString ruleDots;
+ CharsString otherDots;
+ CharsString dotsBefore;
+ CharsString dotsAfter;
+ int haveDotsAfter = 0;
+ if (!getToken (nested, &ruleDots, "dots operand"))
+ return 0;
+ for (k = 0; k < ruleDots.length && ruleDots.chars[k] != ','; k++);
+ if (k == ruleDots.length)
+ {
+ if (!parseDots (nested, &dotsBefore, &ruleDots))
+ return 0;
+ dotsAfter.length = dotsBefore.length;
+ for (k = 0; k < dotsBefore.length; k++)
+ dotsAfter.chars[k] = dotsBefore.chars[k];
+ dotsAfter.chars[k] = 0;
+ }
+ else
+ {
+ haveDotsAfter = ruleDots.length;
+ ruleDots.length = k;
+ if (!parseDots (nested, &dotsBefore, &ruleDots))
+ return 0;
+ otherDots.length = 0;
+ k++;
+ for (; k < haveDotsAfter; k++)
+ otherDots.chars[otherDots.length++] = ruleDots.chars[k];
+ if (!parseDots (nested, &dotsAfter, &otherDots))
+ return 0;
+ }
+ for (k = 0; k < dotsBefore.length; k++)
+ dotsBefore.chars[k] = getCharFromDots (dotsBefore.chars[k]);
+ for (k = 0; k < dotsAfter.length; k++)
+ dotsAfter.chars[k] = getCharFromDots (dotsAfter.chars[k]);
+ if (!addRule (nested, CTO_NoBreak, &dotsBefore, &dotsAfter, 0, 0))
+ return 0;
+ table->noBreak = newRuleOffset;
+ return 1;
+}
+
+static int
+compileCharDef (FileInfo * nested,
+ TranslationTableOpcode opcode,
+ TranslationTableCharacterAttributes attributes)
+{
+ CharsString ruleChars;
+ CharsString ruleDots;
+ TranslationTableCharacter *character;
+ TranslationTableCharacter *cell;
+ TranslationTableCharacter *otherCell;
+ TranslationTableCharacterAttributes attr;
+ int k;
+ if (!getRuleCharsText (nested, &ruleChars))
+ return 0;
+ if (attributes & (CTC_UpperCase | CTC_LowerCase))
+ attributes |= CTC_Letter;
+ if (!getRuleDotsPattern (nested, &ruleDots))
+ return 0;
+ if (ruleChars.length != 1 || ruleDots.length < 1)
+ {
+ compileError (nested,
+ "Exactly one Unicode character and at least one cell are required.");
+ return 0;
+ }
+ character = addCharOrDots (nested, ruleChars.chars[0], 0);
+ character->attributes |= attributes;
+ character->uppercase = character->lowercase = character->realchar;
+ cell = compile_findCharOrDots (ruleDots.chars[0], 1);
+ if (ruleDots.length == 1 && cell)
+ cell->attributes |= attributes;
+ else
+ {
+ for (k = 0; k < ruleDots.length; k++)
+ {
+ if (!compile_findCharOrDots (ruleDots.chars[k], 1))
+ {
+ attr = attributes;
+ otherCell = addCharOrDots (nested, ruleDots.chars[k], 1);
+ if (ruleDots.length != 1)
+ attr = CTC_Space;
+ otherCell->attributes |= attr;
+ otherCell->uppercase = otherCell->lowercase =
+ otherCell->realchar;
+ }
+ }
+ }
+ if (!addRule (nested, opcode, &ruleChars, &ruleDots, 0, 0))
+ return 0;
+ if (ruleDots.length == 1)
+ putCharAndDots (nested, ruleChars.chars[0], ruleDots.chars[0]);
+ return 1;
+}
+
+static int
+compileRule (FileInfo * nested)
+{
+ int ok = 1;
+ CharsString token;
+ TranslationTableOpcode opcode;
+ CharsString ruleChars;
+ CharsString ruleDots;
+ CharsString cells;
+ CharsString scratchPad;
+ TranslationTableCharacterAttributes after = 0;
+ TranslationTableCharacterAttributes before = 0;
+ int k;
+
+ noback = nofor = 0;
+doOpcode:
+ if (!getToken (nested, &token, NULL))
+ return 1; /*blank line */
+ if (token.chars[0] == '#' || token.chars[0] == '<')
+ return 1; /*comment */
+ if (nested->lineNumber == 1 && (eqasc2uni ((unsigned char *) "ISO",
+ token.chars, 3) ||
+ eqasc2uni ((unsigned char *) "UTF-8",
+ token.chars, 5)))
+ {
+ compileHyphenation (nested, &token);
+ return 1;
+ }
+ opcode = getOpcode (nested, &token);
+ switch (opcode)
+ { /*Carry out operations */
+ case CTO_None:
+ break;
+ case CTO_IncludeFile:
+ {
+ CharsString includedFile;
+ if (getToken (nested, &token, "include file name"))
+ if (parseChars (nested, &includedFile, &token))
+ if (!includeFile (nested, &includedFile))
+ ok = 0;
+ break;
+ }
+ case CTO_Locale:
+ break;
+ case CTO_Undefined:
+ ok =
+ compileBrailleIndicator (nested, "undefined character opcode",
+ CTO_Undefined, &table->undefined);
+ break;
+ case CTO_CapitalSign:
+ ok =
+ compileBrailleIndicator (nested, "capital sign", CTO_CapitalRule,
+ &table->capitalSign);
+ break;
+ case CTO_BeginCapitalSign:
+ ok =
+ compileBrailleIndicator (nested, "begin capital sign",
+ CTO_BeginCapitalRule,
+ &table->beginCapitalSign);
+ break;
+ case CTO_LenBegcaps:
+ ok = table->lenBeginCaps = compileNumber (nested);
+ break;
+ case CTO_EndCapitalSign:
+ ok =
+ compileBrailleIndicator (nested, "end capitals sign",
+ CTO_EndCapitalRule, &table->endCapitalSign);
+ break;
+ case CTO_FirstWordCaps:
+ ok =
+ compileBrailleIndicator (nested, "first word capital sign",
+ CTO_FirstWordCapsRule,
+ &table->firstWordCaps);
+ break;
+ case CTO_LastWordCapsBefore:
+ ok =
+ compileBrailleIndicator (nested, "capital sign before last word",
+ CTO_LastWordCapsBeforeRule,
+ &table->lastWordCapsBefore);
+ break;
+ case CTO_LastWordCapsAfter:
+ ok =
+ compileBrailleIndicator (nested, "capital sign after last word",
+ CTO_LastWordCapsAfterRule,
+ &table->lastWordCapsAfter);
+ break;
+ case CTO_LenCapsPhrase:
+ ok = table->lenCapsPhrase = compileNumber (nested);
+ break;
+ case CTO_LetterSign:
+ ok =
+ compileBrailleIndicator (nested, "letter sign", CTO_LetterRule,
+ &table->letterSign);
+ break;
+ case CTO_NoLetsignBefore:
+ if (getRuleCharsText (nested, &ruleChars))
+ {
+ if ((table->noLetsignBeforeCount + ruleChars.length) > LETSIGNSIZE)
+ {
+ compileError (nested, "More than %d characters", LETSIGNSIZE);
+ ok = 0;
+ break;
+ }
+ for (k = 0; k < ruleChars.length; k++)
+ table->noLetsignBefore[table->noLetsignBeforeCount++] =
+ ruleChars.chars[k];
+ }
+ break;
+ case CTO_NoLetsign:
+ if (getRuleCharsText (nested, &ruleChars))
+ {
+ if ((table->noLetsignCount + ruleChars.length) > LETSIGNSIZE)
+ {
+ compileError (nested, "More than %d characters", LETSIGNSIZE);
+ ok = 0;
+ break;
+ }
+ for (k = 0; k < ruleChars.length; k++)
+ table->noLetsign[table->noLetsignCount++] = ruleChars.chars[k];
+ }
+ break;
+ case CTO_NoLetsignAfter:
+ if (getRuleCharsText (nested, &ruleChars))
+ {
+ if ((table->noLetsignAfterCount + ruleChars.length) > LETSIGNSIZE)
+ {
+ compileError (nested, "More than %d characters", LETSIGNSIZE);
+ ok = 0;
+ break;
+ }
+ for (k = 0; k < ruleChars.length; k++)
+ table->noLetsignAfter[table->noLetsignAfterCount++] =
+ ruleChars.chars[k];
+ }
+ break;
+ case CTO_NumberSign:
+ ok =
+ compileBrailleIndicator (nested, "number sign", CTO_NumberRule,
+ &table->numberSign);
+ break;
+ case CTO_FirstWordItal:
+ ok =
+ compileBrailleIndicator (nested, "first word italic",
+ CTO_FirstWordItalRule,
+ &table->firstWordItal);
+ break;
+ case CTO_ItalSign:
+ case CTO_LastWordItalBefore:
+ ok =
+ compileBrailleIndicator (nested, "first word italic before",
+ CTO_LastWordItalBeforeRule,
+ &table->lastWordItalBefore);
+ break;
+ case CTO_LastWordItalAfter:
+ ok =
+ compileBrailleIndicator (nested, "last word italic after",
+ CTO_LastWordItalAfterRule,
+ &table->lastWordItalAfter);
+ break;
+ case CTO_BegItal:
+ case CTO_FirstLetterItal:
+ ok =
+ compileBrailleIndicator (nested, "first letter italic",
+ CTO_FirstLetterItalRule,
+ &table->firstLetterItal);
+ break;
+ case CTO_EndItal:
+ case CTO_LastLetterItal:
+ ok =
+ compileBrailleIndicator (nested, "last letter italic",
+ CTO_LastLetterItalRule,
+ &table->lastLetterItal);
+ break;
+ case CTO_SingleLetterItal:
+ ok =
+ compileBrailleIndicator (nested, "single letter italic",
+ CTO_SingleLetterItalRule,
+ &table->singleLetterItal);
+ break;
+ case CTO_ItalWord:
+ ok =
+ compileBrailleIndicator (nested, "italic word", CTO_ItalWordRule,
+ &table->italWord);
+ break;
+ case CTO_LenItalPhrase:
+ ok = table->lenItalPhrase = compileNumber (nested);
+ break;
+ case CTO_FirstWordBold:
+ ok =
+ compileBrailleIndicator (nested, "first word bold",
+ CTO_FirstWordBoldRule,
+ &table->firstWordBold);
+ break;
+ case CTO_BoldSign:
+ case CTO_LastWordBoldBefore:
+ ok =
+ compileBrailleIndicator (nested, "last word bold before",
+ CTO_LastWordBoldBeforeRule,
+ &table->lastWordBoldBefore);
+ break;
+ case CTO_LastWordBoldAfter:
+ ok =
+ compileBrailleIndicator (nested, "last word bold after",
+ CTO_LastWordBoldAfterRule,
+ &table->lastWordBoldAfter);
+ break;
+ case CTO_BegBold:
+ case CTO_FirstLetterBold:
+ ok =
+ compileBrailleIndicator (nested, "first letter bold",
+ CTO_FirstLetterBoldRule,
+ &table->firstLetterBold);
+ break;
+ case CTO_EndBold:
+ case CTO_LastLetterBold:
+ ok =
+ compileBrailleIndicator (nested, "last letter bold",
+ CTO_LastLetterBoldRule,
+ &table->lastLetterBold);
+ break;
+ case CTO_SingleLetterBold:
+ ok =
+ compileBrailleIndicator (nested, "single letter bold",
+ CTO_SingleLetterBoldRule,
+ &table->singleLetterBold);
+ break;
+ case CTO_BoldWord:
+ ok =
+ compileBrailleIndicator (nested, "bold word", CTO_BoldWordRule,
+ &table->boldWord);
+ break;
+ case CTO_LenBoldPhrase:
+ ok = table->lenBoldPhrase = compileNumber (nested);
+ break;
+ case CTO_FirstWordUnder:
+ ok =
+ compileBrailleIndicator (nested, "first word underline",
+ CTO_FirstWordUnderRule,
+ &table->firstWordUnder);
+ break;
+ case CTO_UnderSign:
+ case CTO_LastWordUnderBefore:
+ ok =
+ compileBrailleIndicator (nested, "last word underline before",
+ CTO_LastWordUnderBeforeRule,
+ &table->lastWordUnderBefore);
+ break;
+ case CTO_LastWordUnderAfter:
+ ok =
+ compileBrailleIndicator (nested, "last word underline after",
+ CTO_LastWordUnderAfterRule,
+ &table->lastWordUnderAfter);
+ break;
+ case CTO_BegUnder:
+ case CTO_FirstLetterUnder:
+ ok =
+ compileBrailleIndicator (nested, "first letter underline",
+ CTO_FirstLetterUnderRule,
+ &table->firstLetterUnder);
+ break;
+ case CTO_EndUnder:
+ case CTO_LastLetterUnder:
+ ok =
+ compileBrailleIndicator (nested, "last letter underline",
+ CTO_LastLetterUnderRule,
+ &table->lastLetterUnder);
+ break;
+ case CTO_SingleLetterUnder:
+ ok =
+ compileBrailleIndicator (nested, "single letter underline",
+ CTO_SingleLetterUnderRule,
+ &table->singleLetterUnder);
+ break;
+ case CTO_UnderWord:
+ ok =
+ compileBrailleIndicator (nested, "underlined word", CTO_UnderWordRule,
+ &table->underWord);
+ break;
+ case CTO_LenUnderPhrase:
+ ok = table->lenUnderPhrase = compileNumber (nested);
+ break;
+ case CTO_BegComp:
+ ok =
+ compileBrailleIndicator (nested, "begin computer braille",
+ CTO_BegCompRule, &table->begComp);
+ break;
+ case CTO_EndComp:
+ ok =
+ compileBrailleIndicator (nested, "end computer braslle",
+ CTO_EndCompRule, &table->endComp);
+ break;
+ case CTO_Syllable:
+ table->syllables = 1;
+ case CTO_Always:
+ case CTO_NoCross:
+ case CTO_LargeSign:
+ case CTO_WholeWord:
+ case CTO_PartWord:
+ case CTO_JoinNum:
+ case CTO_JoinableWord:
+ case CTO_LowWord:
+ case CTO_SuffixableWord:
+ case CTO_PrefixableWord:
+ case CTO_BegWord:
+ case CTO_BegMidWord:
+ case CTO_MidWord:
+ case CTO_MidEndWord:
+ case CTO_EndWord:
+ case CTO_PrePunc:
+ case CTO_PostPunc:
+ case CTO_BegNum:
+ case CTO_MidNum:
+ case CTO_EndNum:
+ case CTO_Repeated:
+ case CTO_RepWord:
+ if (getRuleCharsText (nested, &ruleChars))
+ if (getRuleDotsPattern (nested, &ruleDots))
+ if (!addRule (nested, opcode, &ruleChars, &ruleDots, after, before))
+ ok = 0;
+ break;
+ case CTO_CompDots:
+ case CTO_Comp6:
+ if (!getRuleCharsText (nested, &ruleChars))
+ return 0;
+ if (ruleChars.length != 1 || ruleChars.chars[0] > 255)
+ {
+ compileError (nested,
+ "first operand must be 1 character and < 256");
+ return 0;
+ }
+ if (!getRuleDotsPattern (nested, &ruleDots))
+ return 0;
+ if (!addRule (nested, opcode, &ruleChars, &ruleDots, after, before))
+ ok = 0;
+ table->compdotsPattern[ruleChars.chars[0]] = newRuleOffset;
+ break;
+ case CTO_ExactDots:
+ if (!getRuleCharsText (nested, &ruleChars))
+ return 0;
+ if (ruleChars.chars[0] != '@')
+ {
+ compileError (nested, "The operand must begin with an at sign (@)");
+ return 0;
+ }
+ for (k = 1; k < ruleChars.length; k++)
+ scratchPad.chars[k - 1] = ruleChars.chars[k];
+ scratchPad.length = ruleChars.length - 1;
+ if (!parseDots (nested, &ruleDots, &scratchPad))
+ return 0;
+ if (!addRule (nested, opcode, &ruleChars, &ruleDots, before, after))
+ ok = 0;
+ break;
+ case CTO_CapsNoCont:
+ ruleChars.length = 1;
+ ruleChars.chars[0] = 'a';
+ if (!addRule
+ (nested, CTO_CapsNoContRule, &ruleChars, NULL, after, before))
+ ok = 0;
+ table->capsNoCont = newRuleOffset;
+ break;
+ case CTO_Replace:
+ if (getRuleCharsText (nested, &ruleChars))
+ {
+ if (lastToken)
+ ruleDots.length = ruleDots.chars[0] = 0;
+ else
+ {
+ getRuleDotsText (nested, &ruleDots);
+ if (ruleDots.chars[0] == '#')
+ ruleDots.length = ruleDots.chars[0] = 0;
+ else if (ruleDots.chars[0] == '\\' && ruleDots.chars[1] == '#')
+ memcpy (&ruleDots.chars[0], &ruleDots.chars[1],
+ ruleDots.length-- * CHARSIZE);
+ }
+ }
+ for (k = 0; k < ruleChars.length; k++)
+ addCharOrDots (nested, ruleChars.chars[k], 0);
+ for (k = 0; k < ruleDots.length; k++)
+ addCharOrDots (nested, ruleDots.chars[k], 0);
+ if (!addRule (nested, opcode, &ruleChars, &ruleDots, after, before))
+ ok = 0;
+ break;
+ case CTO_Pass2:
+ if (table->numPasses < 2)
+ table->numPasses = 2;
+ goto doPass;
+ case CTO_Pass3:
+ if (table->numPasses < 3)
+ table->numPasses = 3;
+ goto doPass;
+ case CTO_Pass4:
+ if (table->numPasses < 4)
+ table->numPasses = 4;
+ doPass:
+ case CTO_Context:
+ if (!compilePassOpcode (nested, opcode))
+ ok = 0;
+ break;
+ case CTO_Correct:
+ if (!compilePassOpcode (nested, opcode))
+ ok = 0;
+ table->corrections = 1;
+ break;
+ case CTO_Contraction:
+ case CTO_NoCont:
+ case CTO_CompBrl:
+ case CTO_Literal:
+ if (getRuleCharsText (nested, &ruleChars))
+ if (!addRule (nested, opcode, &ruleChars, NULL, after, before))
+ ok = 0;
+ break;
+ case CTO_MultInd:
+ {
+ int lastToken;
+ ruleChars.length = 0;
+ if (getToken (nested, &token, "multiple braille indicators") &&
+ parseDots (nested, &cells, &token))
+ {
+ while ((lastToken = getToken (nested, &token, "multind opcodes")))
+ {
+ opcode = getOpcode (nested, &token);
+ if (opcode >= CTO_CapitalSign && opcode < CTO_MultInd)
+ ruleChars.chars[ruleChars.length++] = (widechar) opcode;
+ else
+ {
+ compileError (nested, "Not a braille indicator opcode.");
+ ok = 0;
+ }
+ if (lastToken == 2)
+ break;
+ }
+ }
+ else
+ ok = 0;
+ if (!addRule (nested, CTO_MultInd, &ruleChars, &cells, after, before))
+ ok = 0;
+ break;
+ }
+
+ case CTO_Class:
+ {
+ CharsString characters;
+ const struct CharacterClass *class;
+ if (!characterClasses)
+ {
+ if (!allocateCharacterClasses ())
+ ok = 0;
+ }
+ if (getToken (nested, &token, "character class name"))
+ {
+ if ((class = findCharacterClass (&token)))
+ {
+ compileError (nested, "character class already defined.");
+ }
+ else
+ if ((class =
+ addCharacterClass (nested, &token.chars[0], token.length)))
+ {
+ if (getCharacters (nested, &characters))
+ {
+ int index;
+ for (index = 0; index < characters.length; ++index)
+ {
+ TranslationTableRule *defRule;
+ TranslationTableCharacter *character =
+ definedCharOrDots
+ (nested, characters.chars[index], 0);
+ character->attributes |= class->attribute;
+ defRule = (TranslationTableRule *)
+ & table->ruleArea[character->definitionRule];
+ if (defRule->dotslen == 1)
+ {
+ character = definedCharOrDots
+ (nested,
+ defRule->charsdots[defRule->charslen], 1);
+ character->attributes |= class->attribute;
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ {
+ TranslationTableCharacterAttributes *attributes;
+ const struct CharacterClass *class;
+ case CTO_After:
+ attributes = &after;
+ goto doClass;
+ case CTO_Before:
+ attributes = &before;
+ doClass:
+
+ if (!characterClasses)
+ {
+ if (!allocateCharacterClasses ())
+ ok = 0;
+ }
+ if (getCharacterClass (nested, &class))
+ {
+ *attributes |= class->attribute;
+ goto doOpcode;
+ }
+ break;
+ }
+ case CTO_NoBack:
+ noback = 1;
+ goto doOpcode;
+ case CTO_NoFor:
+ nofor = 1;
+ goto doOpcode;
+ case CTO_SwapCc:
+ case CTO_SwapCd:
+ case CTO_SwapDd:
+ if (!compileSwap (nested, opcode))
+ ok = 0;
+ break;
+ case CTO_Hyphen:
+ case CTO_DecPoint:
+ if (getRuleCharsText (nested, &ruleChars))
+ if (getRuleDotsPattern (nested, &ruleDots))
+ {
+ if (ruleChars.length != 1 || ruleDots.length < 1)
+ {
+ compileError (nested,
+ "One Unicode character and at least one cell are required.");
+ ok = 0;
+ }
+ if (!addRule
+ (nested, opcode, &ruleChars, &ruleDots, after, before))
+ ok = 0;
+ }
+ break;
+ case CTO_Space:
+ compileCharDef (nested, opcode, CTC_Space);
+ break;
+ case CTO_Digit:
+ compileCharDef (nested, opcode, CTC_Digit);
+ break;
+ case CTO_LitDigit:
+ compileCharDef (nested, opcode, CTC_LitDigit);
+ break;
+ case CTO_Punctuation:
+ compileCharDef (nested, opcode, CTC_Punctuation);
+ break;
+ case CTO_Math:
+ compileCharDef (nested, opcode, CTC_Math);
+ break;
+ case CTO_Sign:
+ compileCharDef (nested, opcode, CTC_Sign);
+ break;
+ case CTO_Letter:
+ compileCharDef (nested, opcode, CTC_Letter);
+ break;
+ case CTO_UpperCase:
+ compileCharDef (nested, opcode, CTC_UpperCase);
+ break;
+ case CTO_LowerCase:
+ compileCharDef (nested, opcode, CTC_LowerCase);
+ break;
+ case CTO_NoBreak:
+ ok = compileNoBreak (nested);
+ break;
+ case CTO_Grouping:
+ ok = compileGrouping (nested);
+ break;
+ case CTO_UpLow:
+ ok = compileUplow (nested);
+ break;
+ case CTO_Display:
+ if (getRuleCharsText (nested, &ruleChars))
+ if (getRuleDotsPattern (nested, &ruleDots))
+ {
+ if (ruleChars.length != 1 || ruleDots.length != 1)
+ {
+ compileError (nested,
+ "Exactly one character and one cell are required.");
+ ok = 0;
+ }
+ putCharAndDots (nested, ruleChars.chars[0], ruleDots.chars[0]);
+ }
+ break;
+ default:
+ compileError (nested, "unimplemented opcode.");
+ break;
+ }
+ return ok;
+}
+
+int EXPORT_CALL
+lou_readCharFromFile (const char *fileName, int *mode)
+{
+/*Read a character from a file, whether big-endian, little-endian or
+* ASCII8*/
+ int ch;
+ static FileInfo nested;
+ if (fileName == NULL)
+ return 0;
+ if (*mode == 1)
+ {
+ *mode = 0;
+ nested.fileName = fileName;
+ nested.encoding = noEncoding;
+ nested.status = 0;
+ nested.lineNumber = 0;
+ if (!(nested.in = fopen (nested.fileName, "r")))
+ {
+ lou_logPrint ("Cannot open file '%s'", nested.fileName);
+ *mode = 1;
+ return EOF;
+ }
+ }
+ if (nested.in == NULL)
+ {
+ *mode = 1;
+ return EOF;
+ }
+ ch = getAChar (&nested);
+ if (ch == EOF)
+ {
+ fclose (nested.in);
+ nested.in = NULL;
+ *mode = 1;
+ }
+ return ch;
+}
+
+static int fileCount = 0;
+static FILE *
+findTable (const char *tableName)
+{
+/* Search paths for tables */
+ FILE *tableFile;
+ char *pathList;
+ char pathEnd[2];
+ char trialPath[MAXSTRING];
+ if (tableName == NULL || tableName[0] == 0)
+ return NULL;
+ strcpy (trialPath, tablePath);
+ strcat (trialPath, tableName);
+ if ((tableFile = fopen (trialPath, "rb")))
+ return tableFile;
+ pathEnd[0] = DIR_SEP;
+ pathEnd[1] = 0;
+ /* See if table is on environment path LOUIS_TABLEPATH */
+ pathList = getenv ("LOUIS_TABLEPATH");
+ if (pathList)
+ while (1)
+ {
+ int k;
+ int listLength;
+ int currentListPos = 0;
+ listLength = strlen (pathList);
+ for (k = 0; k < listLength; k++)
+ if (pathList[k] == ',')
+ break;
+ if (k == listLength || k == 0)
+ { /* Only one file */
+ strcpy (trialPath, pathList);
+ strcat (trialPath, pathEnd);
+ strcat (trialPath, tableName);
+ if ((tableFile = fopen (trialPath, "rb")))
+ break;
+ }
+ else
+ { /* Compile a list of files */
+ strncpy (trialPath, pathList, k);
+ trialPath[k] = 0;
+ strcat (trialPath, pathEnd);
+ strcat (trialPath, tableName);
+ currentListPos = k + 1;
+ if ((tableFile = fopen (trialPath, "rb")))
+ break;
+ while (currentListPos < listLength)
+ {
+ for (k = currentListPos; k < listLength; k++)
+ if (pathList[k] == ',')
+ break;
+ strncpy (trialPath,
+ &pathList[currentListPos], k - currentListPos);
+ trialPath[k - currentListPos] = 0;
+ strcat (trialPath, pathEnd);
+ strcat (trialPath, tableName);
+ if ((tableFile = fopen (trialPath, "rb")))
+ currentListPos = k + 1;
+ break;
+ }
+ }
+ break;
+ }
+ if (tableFile)
+ return tableFile;
+ /* See if table in current directory or on a path in
+ * the table name*/
+ if ((tableFile = fopen (tableName, "rb")))
+ return tableFile;
+/* See if table on dataPath. */
+ pathList = lou_getDataPath ();
+ if (pathList)
+ {
+ strcpy (trialPath, pathList);
+ strcat (trialPath, pathEnd);
+#ifdef _WIN32
+ strcat (trialPath, "liblouis\\tables\\");
+#else
+ strcat (trialPath, "liblouis/tables/");
+#endif
+ strcat (trialPath, tableName);
+ if ((tableFile = fopen (trialPath, "rb")))
+ return tableFile;
+ }
+ /* See if table on installed or program path. */
+#ifdef _WIN32
+ strcpy (trialPath, lou_getProgramPath ());
+ strcat (trialPath, "\\share\\liblouss\\tables\\");
+#else
+ strcpy (trialPath, TABLESDIR);
+ strcat (trialPath, pathEnd);
+#endif
+ strcat (trialPath, tableName);
+ if ((tableFile = fopen (trialPath, "rb")))
+ return tableFile;
+ return NULL;
+}
+
+static int
+compileFile (const char *fileName)
+{
+/*Compile a table file */
+ FileInfo nested;
+ fileCount++;
+ nested.fileName = fileName;
+ nested.encoding = noEncoding;
+ nested.status = 0;
+ nested.lineNumber = 0;
+ if ((nested.in = findTable (fileName)))
+ {
+ while (getALine (&nested))
+ compileRule (&nested);
+ fclose (nested.in);
+ }
+ else
+ {
+ if (fileCount > 1)
+ lou_logPrint ("Cannot open table '%s'", nested.fileName);
+ errorCount++;
+ return 0;
+ }
+ return 1;
+}
+
+static int
+compileString (const char *inString)
+{
+/* This function can be used to make changes to tables on the fly. */
+ int k;
+ FileInfo nested;
+ if (inString == NULL)
+ return 0;
+ nested.fileName = inString;
+ nested.encoding = noEncoding;
+ nested.lineNumber = 1;
+ nested.status = 0;
+ nested.linepos = 0;
+ for (k = 0; inString[k]; k++)
+ nested.line[k] = inString[k];
+ nested.line[k] = 0;
+ return compileRule (&nested);
+}
+
+static int
+makeDoubleRule (TranslationTableOpcode opcode, TranslationTableOffset
+ * singleRule, TranslationTableOffset * doubleRule)
+{
+ CharsString dots;
+ TranslationTableRule *rule;
+ if (!*singleRule || *doubleRule)
+ return 1;
+ rule = (TranslationTableRule *) & table->ruleArea[*singleRule];
+ memcpy (dots.chars, &rule->charsdots[0], rule->dotslen * CHARSIZE);
+ memcpy (&dots.chars[rule->dotslen], &rule->charsdots[0],
+ rule->dotslen * CHARSIZE);
+ dots.length = 2 * rule->dotslen;
+ if (!addRule (NULL, opcode, NULL, &dots, 0, 0))
+ return 0;
+ *doubleRule = newRuleOffset;
+ return 1;
+}
+
+static int
+setDefaults (void)
+{
+ if (!table->lenBeginCaps)
+ table->lenBeginCaps = 2;
+ makeDoubleRule (CTO_FirstWordItal, &table->lastWordItalBefore,
+ &table->firstWordItal);
+ if (!table->lenItalPhrase)
+ table->lenItalPhrase = 4;
+ makeDoubleRule (CTO_FirstWordBold, &table->lastWordBoldBefore,
+ &table->firstWordBold);
+ if (!table->lenBoldPhrase)
+ table->lenBoldPhrase = 4;
+ makeDoubleRule (CTO_FirstWordUnder, &table->lastWordUnderBefore,
+ &table->firstWordUnder);
+ if (!table->lenUnderPhrase)
+ table->lenUnderPhrase = 4;
+ if (table->numPasses == 0)
+ table->numPasses = 1;
+ return 1;
+}
+
+static char *
+doLang2table (const char *tableList)
+{
+ static char newList[MAXSTRING];
+ int k;
+ char buffer[MAXSTRING];
+ FILE *l2t;
+ char *langCode;
+ int langCodeLen;
+ if (tableList == NULL || *tableList == 0)
+ return NULL;
+ strcpy (newList, tableList);
+ for (k = strlen (newList) - 1; k >= 0 && newList[k] != '='; k--);
+ if (k < 0)
+ return newList;
+ fileCount = 1;
+ errorCount = 1;
+ newList[k] = 0;
+ strcpy (buffer, newList);
+ langCode = &newList[k + 1];
+ langCodeLen = strlen (langCode);
+ strcat (buffer, "lang2table");
+ l2t = fopen (buffer, "r");
+ if (l2t == NULL)
+ return NULL;
+ while ((fgets (buffer, sizeof (buffer) - 2, l2t)))
+ {
+ char *codeInFile;
+ int codeInFileLen;
+ char *tableInFile;
+ for (k = 0; buffer[k] < 32; k++);
+ if (buffer[k] == '#' || buffer[k] < 32)
+ continue;
+ codeInFile = &buffer[k];
+ codeInFileLen = k;
+ while (buffer[k] > 32)
+ k++;
+ codeInFileLen = k - codeInFileLen;
+ codeInFile[codeInFileLen] = 0;
+ if (!
+ (codeInFileLen == langCodeLen
+ && strcasecmp (langCode, codeInFile) == 0))
+ continue;
+ while (buffer[k] < 32)
+ k++;
+ tableInFile = &buffer[k];
+ while (buffer[k] > 32)
+ k++;
+ buffer[k] = 0;
+ strcat (newList, tableInFile);
+ fclose (l2t);
+ fileCount = 0;
+ errorCount = 0;
+ return newList;
+ }
+ fclose (l2t);
+ return NULL;
+}
+
+static void *
+compileTranslationTable (const char *tl)
+{
+/*compile source tables into a table in memory */
+ const char *tableList;
+ int k;
+ char mainTable[MAXSTRING];
+ char subTable[MAXSTRING];
+ int listLength;
+ int currentListPos = 0;
+ errorCount = 0;
+ warningCount = 0;
+ fileCount = 0;
+ table = NULL;
+ characterClasses = NULL;
+ ruleNames = NULL;
+ tableList = doLang2table (tl);
+ if (tableList == NULL)
+ return NULL;
+ if (!opcodeLengths[0])
+ {
+ TranslationTableOpcode opcode;
+ for (opcode = 0; opcode < CTO_None; opcode++)
+ opcodeLengths[opcode] = strlen (opcodeNames[opcode]);
+ }
+ allocateHeader (NULL);
+ /*Compile things that are necesary for the proper operation of
+ liblouis or liblouisxml or liblouisutdml */
+ compileString ("space \\s 0");
+ compileString ("noback sign \\x0000 0");
+ compileString ("space \\x00a0 a unbreakable space");
+ compileString ("space \\x001b 1b escape");
+ compileString ("space \\xffff 123456789abcdef ENDSEGMENT");
+ listLength = strlen (tableList);
+ for (k = currentListPos; k < listLength; k++)
+ if (tableList[k] == ',')
+ break;
+ if (k == listLength)
+ { /* Only one file */
+ strcpy (tablePath, tableList);
+ for (k = strlen (tablePath); k >= 0; k--)
+ if (tablePath[k] == '\\' || tablePath[k] == '/')
+ break;
+ strcpy (mainTable, &tablePath[k + 1]);
+ tablePath[++k] = 0;
+ if (!compileFile (mainTable))
+ goto cleanup;
+ }
+ else
+ { /* Compile a list of files */
+ currentListPos = k + 1;
+ strncpy (tablePath, tableList, k);
+ tablePath[k] = 0;
+ for (k = strlen (tablePath); k >= 0; k--)
+ if (tablePath[k] == '\\' || tablePath[k] == '/')
+ break;
+ strcpy (mainTable, &tablePath[k + 1]);
+ tablePath[++k] = 0;
+ if (!compileFile (mainTable))
+ goto cleanup;
+ while (currentListPos < listLength)
+ {
+ for (k = currentListPos; k < listLength; k++)
+ if (tableList[k] == ',')
+ break;
+ strncpy (subTable, &tableList[currentListPos], k - currentListPos);
+ subTable[k - currentListPos] = 0;
+ if (!compileFile (subTable))
+ goto cleanup;
+ currentListPos = k + 1;
+ }
+ }
+/*Clean up after compiling files*/
+cleanup:
+ if (characterClasses)
+ deallocateCharacterClasses ();
+ if (ruleNames)
+ deallocateRuleNames ();
+ if (warningCount)
+ lou_logPrint ("%d warnings issued", warningCount);
+ if (!errorCount)
+ {
+ setDefaults ();
+ table->tableSize = tableSize;
+ table->bytesUsed = tableUsed;
+ }
+ else
+ {
+ if (!(errorCount == 1 && fileCount == 1))
+ lou_logPrint ("%d errors found.", errorCount);
+ if (table)
+ free (table);
+ table = NULL;
+ }
+ return (void *) table;
+}
+
+typedef struct
+{
+ void *next;
+ void *table;
+ int tableListLength;
+ char tableList[1];
+} ChainEntry;
+static ChainEntry *tableChain = NULL;
+static ChainEntry *lastTrans = NULL;
+static void *
+getTable (const char *tableList)
+{
+/*Keep track of which tables have already been compiled */
+ int tableListLen;
+ ChainEntry *currentEntry = NULL;
+ ChainEntry *lastEntry = NULL;
+ void *newTable;
+ if (tableList == NULL || *tableList == 0)
+ return NULL;
+ errorCount = fileCount = 0;
+ tableListLen = strlen (tableList);
+ /*See if this is the last table used. */
+ if (lastTrans != NULL)
+ if (tableListLen == lastTrans->tableListLength && (memcmp
+ (&lastTrans->
+ tableList
+ [0],
+ tableList,
+ tableListLen)) == 0)
+ return (table = lastTrans->table);
+/*See if Table has already been compiled*/
+ currentEntry = tableChain;
+ while (currentEntry != NULL)
+ {
+ if (tableListLen == currentEntry->tableListLength && (memcmp
+ (&currentEntry->
+ tableList
+ [0],
+ tableList,
+ tableListLen))
+ == 0)
+ {
+ lastTrans = currentEntry;
+ return (table = currentEntry->table);
+ }
+ lastEntry = currentEntry;
+ currentEntry = currentEntry->next;
+ }
+ if ((newTable = compileTranslationTable (tableList)))
+ {
+ /*Add a new entry to the table chain. */
+ int entrySize = sizeof (ChainEntry) + tableListLen;
+ ChainEntry *newEntry = malloc (entrySize);
+ if (tableChain == NULL)
+ tableChain = newEntry;
+ else
+ lastEntry->next = newEntry;
+ newEntry->next = NULL;
+ newEntry->table = newTable;
+ newEntry->tableListLength = tableListLen;
+ memcpy (&newEntry->tableList[0], tableList, tableListLen);
+ lastTrans = newEntry;
+ return newEntry->table;
+ }
+ return NULL;
+}
+
+char *
+getLastTableList ()
+{
+ if (lastTrans == NULL)
+ return NULL;
+ strncpy (scratchBuf, lastTrans->tableList, lastTrans->tableListLength);
+ scratchBuf[lastTrans->tableListLength] = 0;
+ return scratchBuf;
+}
+
+void *EXPORT_CALL
+lou_getTable (const char *tableList)
+{
+/* Search paths for tables and keep track of compiled tables. */
+ void *table = NULL;
+ char *pathList;
+ char pathEnd[2];
+ char trialPath[MAXSTRING];
+ if (tableList == NULL || tableList[0] == 0)
+ return NULL;
+ errorCount = fileCount = 0;
+ pathEnd[0] = DIR_SEP;
+ pathEnd[1] = 0;
+ /* See if table is on environment path LOUIS_TABLEPATH */
+ pathList = getenv ("LOUIS_TABLEPATH");
+ if (pathList)
+ while (1)
+ {
+ int k;
+ int listLength;
+ int currentListPos = 0;
+ listLength = strlen (pathList);
+ for (k = 0; k < listLength; k++)
+ if (pathList[k] == ',')
+ break;
+ if (k == listLength || k == 0)
+ { /* Only one file */
+ strcpy (trialPath, pathList);
+ strcat (trialPath, pathEnd);
+ strcat (trialPath, tableList);
+ table = getTable (trialPath);
+ if (table)
+ break;
+ }
+ else
+ { /* Compile a list of files */
+ strncpy (trialPath, pathList, k);
+ trialPath[k] = 0;
+ strcat (trialPath, pathEnd);
+ strcat (trialPath, tableList);
+ currentListPos = k + 1;
+ table = getTable (trialPath);
+ if (table)
+ break;
+ while (currentListPos < listLength)
+ {
+ for (k = currentListPos; k < listLength; k++)
+ if (pathList[k] == ',')
+ break;
+ strncpy (trialPath,
+ &pathList[currentListPos], k - currentListPos);
+ trialPath[k - currentListPos] = 0;
+ strcat (trialPath, pathEnd);
+ strcat (trialPath, tableList);
+ table = getTable (trialPath);
+ currentListPos = k + 1;
+ if (table)
+ break;
+ }
+ }
+ break;
+ }
+ if (!table)
+ {
+ /* See if table in current directory or on a path in
+ * the table name*/
+ if (errorCount > 0 && (!(errorCount == 1 && fileCount == 1)))
+ return NULL;
+ table = getTable (tableList);
+ }
+ if (!table)
+ {
+/* See if table on dataPath. */
+ if (errorCount > 0 && (!(errorCount == 1 && fileCount == 1)))
+ return NULL;
+ pathList = lou_getDataPath ();
+ if (pathList)
+ {
+ strcpy (trialPath, pathList);
+ strcat (trialPath, pathEnd);
+#ifdef _WIN32
+ strcat (trialPath, "liblouis\\tables\\");
+#else
+ strcat (trialPath, "liblouis/tables/");
+#endif
+ strcat (trialPath, tableList);
+ table = getTable (trialPath);
+ }
+ }
+ if (!table)
+ {
+ /* See if table on installed or program path. */
+ if (errorCount > 0 && (!(errorCount == 1 && fileCount == 1)))
+ return NULL;
+#ifdef _WIN32
+ strcpy (trialPath, lou_getProgramPath ());
+ strcat (trialPath, "\\share\\liblouss\\tables\\");
+#else
+ strcpy (trialPath, TABLESDIR);
+ strcat (trialPath, pathEnd);
+#endif
+ strcat (trialPath, tableList);
+ table = getTable (trialPath);
+ }
+ if (!table)
+ lou_logPrint ("%s could not be found", tableList);
+ return table;
+}
+
+static unsigned char *destSpacing = NULL;
+static int sizeDestSpacing = 0;
+static unsigned short *typebuf = NULL;
+static int sizeTypebuf = 0;
+static widechar *passbuf1 = NULL;
+static int sizePassbuf1 = 0;
+static widechar *passbuf2 = NULL;
+static int sizePassbuf2 = 0;
+static int *srcMapping = NULL;
+static int *prevSrcMapping = NULL;
+static int sizeSrcMapping = 0;
+static int sizePrevSrcMapping = 0;
+void *
+liblouis_allocMem (AllocBuf buffer, int srcmax, int destmax)
+{
+ if (srcmax < 1024)
+ srcmax = 1024;
+ if (destmax < 1024)
+ destmax = 1024;
+ switch (buffer)
+ {
+ case alloc_typebuf:
+ if (destmax > sizeTypebuf)
+ {
+ if (typebuf != NULL)
+ free (typebuf);
+ typebuf = malloc ((destmax + 4) * sizeof (unsigned short));
+ sizeTypebuf = destmax;
+ }
+ return typebuf;
+ case alloc_destSpacing:
+ if (destmax > sizeDestSpacing)
+ {
+ if (destSpacing != NULL)
+ free (destSpacing);
+ destSpacing = malloc (destmax + 4);
+ sizeDestSpacing = destmax;
+ }
+ return destSpacing;
+ case alloc_passbuf1:
+ if (destmax > sizePassbuf1)
+ {
+ if (passbuf1 != NULL)
+ free (passbuf1);
+ passbuf1 = malloc ((destmax + 4) * CHARSIZE);
+ sizePassbuf1 = destmax;
+ }
+ return passbuf1;
+ case alloc_passbuf2:
+ if (destmax > sizePassbuf2)
+ {
+ if (passbuf2 != NULL)
+ free (passbuf2);
+ passbuf2 = malloc ((destmax + 4) * CHARSIZE);
+ sizePassbuf2 = destmax;
+ }
+ return passbuf2;
+ case alloc_srcMapping:
+ {
+ int mapSize;
+ if (srcmax >= destmax)
+ mapSize = srcmax;
+ else
+ mapSize = destmax;
+ if (mapSize > sizeSrcMapping)
+ {
+ if (srcMapping != NULL)
+ free (srcMapping);
+ srcMapping = malloc ((mapSize + 4) * sizeof (int));
+ sizeSrcMapping = mapSize;
+ }
+ }
+ return srcMapping;
+ case alloc_prevSrcMapping:
+ {
+ int mapSize;
+ if (srcmax >= destmax)
+ mapSize = srcmax;
+ else
+ mapSize = destmax;
+ if (mapSize > sizePrevSrcMapping)
+ {
+ if (prevSrcMapping != NULL)
+ free (prevSrcMapping);
+ prevSrcMapping = malloc ((mapSize + 4) * sizeof (int));
+ sizePrevSrcMapping = mapSize;
+ }
+ }
+ return prevSrcMapping;
+ default:
+ return NULL;
+ }
+}
+
+void EXPORT_CALL
+lou_free (void)
+{
+ ChainEntry *currentEntry;
+ ChainEntry *previousEntry;
+ if (logFile != NULL)
+ fclose (logFile);
+ if (tableChain != NULL)
+ {
+ currentEntry = tableChain;
+ while (currentEntry)
+ {
+ free (currentEntry->table);
+ previousEntry = currentEntry;
+ currentEntry = currentEntry->next;
+ free (previousEntry);
+ }
+ tableChain = NULL;
+ lastTrans = NULL;
+ }
+ if (typebuf != NULL)
+ free (typebuf);
+ typebuf = NULL;
+ sizeTypebuf = 0;
+ if (destSpacing != NULL)
+ free (destSpacing);
+ destSpacing = NULL;
+ sizeDestSpacing = 0;
+ if (passbuf1 != NULL)
+ free (passbuf1);
+ passbuf1 = NULL;
+ sizePassbuf1 = 0;
+ if (passbuf2 != NULL)
+ free (passbuf2);
+ passbuf2 = NULL;
+ sizePassbuf2 = 0;
+ if (srcMapping != NULL)
+ free (srcMapping);
+ srcMapping = NULL;
+ sizeSrcMapping = 0;
+ if (prevSrcMapping != NULL)
+ free (prevSrcMapping);
+ prevSrcMapping = NULL;
+ sizePrevSrcMapping = 0;
+ opcodeLengths[0] = 0;
+}
+
+char *EXPORT_CALL
+lou_version ()
+{
+ static char *version = PACKAGE_VERSION;
+ return version;
+}
+
+int EXPORT_CALL
+lou_charSize (void)
+{
+ return CHARSIZE;
+}
+
+int EXPORT_CALL
+lou_compileString (const char *tableList, const char *inString)
+{
+ if (!lou_getTable (tableList))
+ return 0;
+ return compileString (inString);
+}
+
+/**
+ * This procedure provides a target for cals that serve as breakpoints
+ * for gdb.
+ */
+/*
+char *EXPORT_CALL
+lou_getTablePaths ()
+{
+ static char paths[MAXSTRING];
+ char *pathList;
+ strcpy (paths, tablePath);
+ strcat (paths, ",");
+ pathList = getenv ("LOUIS_TABLEPATH");
+ if (pathList)
+ {
+ strcat (paths, pathList);
+ strcat (paths, ",");
+ }
+ pathList = getcwd (scratchBuf, MAXSTRING);
+ if (pathList)
+ {
+ strcat (paths, pathList);
+ strcat (paths, ",");
+ }
+ pathList = lou_getDataPath ();
+ if (pathList)
+ {
+ strcat (paths, pathList);
+ strcat (paths, ",");
+ }
+#ifdef _WIN32
+ strcpy (paths, lou_getProgramPath ());
+ strcat (paths, "\\share\\liblouss\\tables\\");
+#else
+ strcpy (paths, TABLESDIR);
+#endif
+ return paths;
+}
+*/
+
+void debugHook ()
+{
+ char *hook = "debug hook";
+ printf ("%s\n", hook);
+}
diff --git a/third_party/liblouis/overrides/liblouis/config.h b/third_party/liblouis/overrides/liblouis/config.h
new file mode 100644
index 0000000..6ae8012
--- /dev/null
+++ b/third_party/liblouis/overrides/liblouis/config.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#define TABLESDIR ""
+#define PACKAGE_VERSION "2.5.1-google"
diff --git a/third_party/liblouis/overrides/liblouis/liblouis.h b/third_party/liblouis/overrides/liblouis/liblouis.h
new file mode 100644
index 0000000..140296b
--- /dev/null
+++ b/third_party/liblouis/overrides/liblouis/liblouis.h
@@ -0,0 +1,146 @@
+/* liblouis Braille Translation and Back-Translation Library
+
+ Based on the Linux screenreader BRLTTY, copyright (C) 1999-2006 by
+ The BRLTTY Team
+
+ Copyright (C) 2004, 2005, 2006, 2009 ViewPlus Technologies, Inc.
+ www.viewplus.com and JJB Software, Inc. www.jjb-software.com
+
+ liblouis is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ liblouis is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+
+ Maintained by John J. Boyer john.boyer@abilitiessoft.com
+ */
+
+#ifndef __LIBLOUIS_H_
+#define __LIBLOUIS_H_
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+#define widechar unsigned short int
+
+#ifdef _WIN32
+#define EXPORT_CALL __stdcall
+char * EXPORT_CALL lou_getProgramPath ();
+#else
+#define EXPORT_CALL
+#endif
+
+ typedef enum
+ {
+ plain_text = 0,
+ italic = 1,
+ underline = 2,
+ bold = 4,
+ computer_braille = 8
+ } typeforms;
+#define comp_emph_1 italic
+#define comp_emph_2 underline
+#define comp_emph_3 bold
+
+ typedef enum
+ {
+ noContractions = 1,
+ compbrlAtCursor = 2,
+ dotsIO = 4,
+ comp8Dots = 8,
+ pass1Only = 16,
+ compbrlLeftCursor = 32,
+ otherTrans = 64,
+ ucBrl = 128
+ } translationModes;
+
+char * EXPORT_CALL lou_version ();
+
+int EXPORT_CALL lou_charSize ();
+
+/* Return the size of widechar */
+
+ int EXPORT_CALL lou_translateString
+ (const char *tableList,
+ const widechar *inbuf,
+ int *inlen,
+ widechar * outbuf,
+ int *outlen, char *typeform, char *spacing, int mode);
+
+ int EXPORT_CALL lou_translate (const char *tableList, const widechar
+ *inbuf,
+ int *inlen, widechar * outbuf, int *outlen,
+ char *typeform, char *spacing, int *outputPos, int
+*inputPos, int *cursorPos, int mode);
+int EXPORT_CALL lou_hyphenate (const char *tableList, const widechar
+ *inbuf,
+ int inlen, char *hyphens, int mode);
+int EXPORT_CALL lou_dotsToChar (const char *tableList, widechar *inbuf,
+ widechar *outbuf, int length, int mode);
+int EXPORT_CALL lou_charToDots (const char *tableList, const widechar
+*inbuf,
+ widechar *outbuf, int length, int mode);
+ int EXPORT_CALL lou_backTranslateString (const char *tableList,
+ const widechar *inbuf,
+ int *inlen,
+ widechar * outbuf,
+ int *outlen, char *typeform, char
+ *spacing, int mode);
+
+ int EXPORT_CALL lou_backTranslate (const char *tableList, const widechar
+ *inbuf,
+ int *inlen, widechar * outbuf, int *outlen,
+char *typeform, char *spacing, int
+ *outputPos, int *inputPos, int *cursorPos, int
+ mode);
+ void EXPORT_CALL lou_logPrint (char *format, ...);
+/* prints error messages to a file */
+
+ void EXPORT_CALL lou_logFile (const char *filename);
+/* Specifies the name of the file to be used by lou_logPrint. If it is
+* not used, this file is stderr*/
+
+ int EXPORT_CALL lou_readCharFromFile (const char *fileName, int *mode);
+/*Read a character from a file, whether big-encian, little-endian or
+* ASCII8, and return it as an integer. EOF at end of file. Mode = 1 on
+* first call, any other value thereafter*/
+
+ void EXPORT_CALL lou_logEnd ();
+ /* Closes the log file so it can be read by other functions. */
+
+ void * EXPORT_CALL lou_getTable (const char *tableList);
+/* This function checks a table for errors. If none are found it loads
+* the table into memory and returns a pointer to it. if errors are found
+* it returns a null pointer. It is called by lou_translateString and
+* lou_backTranslateString and also by functions in liblouisxml
+*/
+
+int EXPORT_CALL lou_compileString (const char *tableList, const char
+ *inString);
+ char * EXPORT_CALL lou_setDataPath (char *path);
+ /* Set the path used for searching for tables and liblouisutdml files.
+ * Overrides the installation path. */
+
+ char * EXPORT_CALL lou_getDataPath ();
+ /* Get the path set in the previous function. */
+
+// char EXPORT_CALL * lou_getTablePaths ();
+ /* Get a list of paths actually used in seraching for tables*/
+
+ void EXPORT_CALL lou_free ();
+/* This function should be called at the end of
+* the application to free all memory allocated by liblouis. */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /*LibLOUIS_H_ */
diff --git a/third_party/liblouis/overrides/tables/braille-patterns.cti b/third_party/liblouis/overrides/tables/braille-patterns.cti
new file mode 100644
index 0000000..eafd6c0
--- /dev/null
+++ b/third_party/liblouis/overrides/tables/braille-patterns.cti
@@ -0,0 +1,287 @@
+#
+# Copyright (C) 2010, 2011 DocArch <http://www.docarch.be>.
+#
+# This file is part of liblouis.
+#
+# liblouis is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# liblouis is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with liblouis. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+# ----------------------------------------------------------------------------------------------
+# odt2braille - Braille authoring in OpenOffice.org.
+# ----------------------------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------------------------
+# Unicode 2800..28FF Braille Patterns
+# ----------------------------------------------------------------------------------------------
+
+noback sign \x2800 0 # ⠀ BRAILLE PATTERN DOTS-0
+noback sign \x2801 1 # ⠁ BRAILLE PATTERN DOTS-1
+noback sign \x2802 2 # ⠂ BRAILLE PATTERN DOTS-2
+noback sign \x2803 12 # ⠃ BRAILLE PATTERN DOTS-12
+noback sign \x2804 3 # ⠄ BRAILLE PATTERN DOTS-3
+noback sign \x2805 13 # ⠅ BRAILLE PATTERN DOTS-13
+noback sign \x2806 23 # ⠆ BRAILLE PATTERN DOTS-23
+noback sign \x2807 123 # ⠇ BRAILLE PATTERN DOTS-123
+noback sign \x2808 4 # ⠈ BRAILLE PATTERN DOTS-4
+noback sign \x2809 14 # ⠉ BRAILLE PATTERN DOTS-14
+noback sign \x280A 24 # ⠊ BRAILLE PATTERN DOTS-24
+noback sign \x280B 124 # ⠋ BRAILLE PATTERN DOTS-124
+noback sign \x280C 34 # ⠌ BRAILLE PATTERN DOTS-34
+noback sign \x280D 134 # ⠍ BRAILLE PATTERN DOTS-134
+noback sign \x280E 234 # ⠎ BRAILLE PATTERN DOTS-234
+noback sign \x280F 1234 # ⠏ BRAILLE PATTERN DOTS-1234
+noback sign \x2810 5 # ⠐ BRAILLE PATTERN DOTS-5
+noback sign \x2811 15 # ⠑ BRAILLE PATTERN DOTS-15
+noback sign \x2812 25 # ⠒ BRAILLE PATTERN DOTS-25
+noback sign \x2813 125 # ⠓ BRAILLE PATTERN DOTS-125
+noback sign \x2814 35 # ⠔ BRAILLE PATTERN DOTS-35
+noback sign \x2815 135 # ⠕ BRAILLE PATTERN DOTS-135
+noback sign \x2816 235 # ⠖ BRAILLE PATTERN DOTS-235
+noback sign \x2817 1235 # ⠗ BRAILLE PATTERN DOTS-1235
+noback sign \x2818 45 # ⠘ BRAILLE PATTERN DOTS-45
+noback sign \x2819 145 # ⠙ BRAILLE PATTERN DOTS-145
+noback sign \x281A 245 # ⠚ BRAILLE PATTERN DOTS-245
+noback sign \x281B 1245 # ⠛ BRAILLE PATTERN DOTS-1245
+noback sign \x281C 345 # ⠜ BRAILLE PATTERN DOTS-345
+noback sign \x281D 1345 # ⠝ BRAILLE PATTERN DOTS-1345
+noback sign \x281E 2345 # ⠞ BRAILLE PATTERN DOTS-2345
+noback sign \x281F 12345 # ⠟ BRAILLE PATTERN DOTS-12345
+noback sign \x2820 6 # ⠠ BRAILLE PATTERN DOTS-6
+noback sign \x2821 16 # ⠡ BRAILLE PATTERN DOTS-16
+noback sign \x2822 26 # ⠢ BRAILLE PATTERN DOTS-26
+noback sign \x2823 126 # ⠣ BRAILLE PATTERN DOTS-126
+noback sign \x2824 36 # ⠤ BRAILLE PATTERN DOTS-36
+noback sign \x2825 136 # ⠥ BRAILLE PATTERN DOTS-136
+noback sign \x2826 236 # ⠦ BRAILLE PATTERN DOTS-236
+noback sign \x2827 1236 # ⠧ BRAILLE PATTERN DOTS-1236
+noback sign \x2828 46 # ⠨ BRAILLE PATTERN DOTS-46
+noback sign \x2829 146 # ⠩ BRAILLE PATTERN DOTS-146
+noback sign \x282A 246 # ⠪ BRAILLE PATTERN DOTS-246
+noback sign \x282B 1246 # ⠫ BRAILLE PATTERN DOTS-1246
+noback sign \x282C 346 # ⠬ BRAILLE PATTERN DOTS-346
+noback sign \x282D 1346 # ⠭ BRAILLE PATTERN DOTS-1346
+noback sign \x282E 2346 # ⠮ BRAILLE PATTERN DOTS-2346
+noback sign \x282F 12346 # ⠯ BRAILLE PATTERN DOTS-12346
+noback sign \x2830 56 # ⠰ BRAILLE PATTERN DOTS-56
+noback sign \x2831 156 # ⠱ BRAILLE PATTERN DOTS-156
+noback sign \x2832 256 # ⠲ BRAILLE PATTERN DOTS-256
+noback sign \x2833 1256 # ⠳ BRAILLE PATTERN DOTS-1256
+noback sign \x2834 356 # ⠴ BRAILLE PATTERN DOTS-356
+noback sign \x2835 1356 # ⠵ BRAILLE PATTERN DOTS-1356
+noback sign \x2836 2356 # ⠶ BRAILLE PATTERN DOTS-2356
+noback sign \x2837 12356 # ⠷ BRAILLE PATTERN DOTS-12356
+noback sign \x2838 456 # ⠸ BRAILLE PATTERN DOTS-456
+noback sign \x2839 1456 # ⠹ BRAILLE PATTERN DOTS-1456
+noback sign \x283A 2456 # ⠺ BRAILLE PATTERN DOTS-2456
+noback sign \x283B 12456 # ⠻ BRAILLE PATTERN DOTS-12456
+noback sign \x283C 3456 # ⠼ BRAILLE PATTERN DOTS-3456
+noback sign \x283D 13456 # ⠽ BRAILLE PATTERN DOTS-13456
+noback sign \x283E 23456 # ⠾ BRAILLE PATTERN DOTS-23456
+noback sign \x283F 123456 # ⠿ BRAILLE PATTERN DOTS-123456
+noback sign \x2840 7 # ⡀ BRAILLE PATTERN DOTS-7
+noback sign \x2841 17 # ⡁ BRAILLE PATTERN DOTS-17
+noback sign \x2842 27 # ⡂ BRAILLE PATTERN DOTS-27
+noback sign \x2843 127 # ⡃ BRAILLE PATTERN DOTS-127
+noback sign \x2844 37 # ⡄ BRAILLE PATTERN DOTS-37
+noback sign \x2845 137 # ⡅ BRAILLE PATTERN DOTS-137
+noback sign \x2846 237 # ⡆ BRAILLE PATTERN DOTS-237
+noback sign \x2847 1237 # ⡇ BRAILLE PATTERN DOTS-1237
+noback sign \x2848 47 # ⡈ BRAILLE PATTERN DOTS-47
+noback sign \x2849 147 # ⡉ BRAILLE PATTERN DOTS-147
+noback sign \x284A 247 # ⡊ BRAILLE PATTERN DOTS-247
+noback sign \x284B 1247 # ⡋ BRAILLE PATTERN DOTS-1247
+noback sign \x284C 347 # ⡌ BRAILLE PATTERN DOTS-347
+noback sign \x284D 1347 # ⡍ BRAILLE PATTERN DOTS-1347
+noback sign \x284E 2347 # ⡎ BRAILLE PATTERN DOTS-2347
+noback sign \x284F 12347 # ⡏ BRAILLE PATTERN DOTS-12347
+noback sign \x2850 57 # ⡐ BRAILLE PATTERN DOTS-57
+noback sign \x2851 157 # ⡑ BRAILLE PATTERN DOTS-157
+noback sign \x2852 257 # ⡒ BRAILLE PATTERN DOTS-257
+noback sign \x2853 1257 # ⡓ BRAILLE PATTERN DOTS-1257
+noback sign \x2854 357 # ⡔ BRAILLE PATTERN DOTS-357
+noback sign \x2855 1357 # ⡕ BRAILLE PATTERN DOTS-1357
+noback sign \x2856 2357 # ⡖ BRAILLE PATTERN DOTS-2357
+noback sign \x2857 12357 # ⡗ BRAILLE PATTERN DOTS-12357
+noback sign \x2858 457 # ⡘ BRAILLE PATTERN DOTS-457
+noback sign \x2859 1457 # ⡙ BRAILLE PATTERN DOTS-1457
+noback sign \x285A 2457 # ⡚ BRAILLE PATTERN DOTS-2457
+noback sign \x285B 12457 # ⡛ BRAILLE PATTERN DOTS-12457
+noback sign \x285C 3457 # ⡜ BRAILLE PATTERN DOTS-3457
+noback sign \x285D 13457 # ⡝ BRAILLE PATTERN DOTS-13457
+noback sign \x285E 23457 # ⡞ BRAILLE PATTERN DOTS-23457
+noback sign \x285F 123457 # ⡟ BRAILLE PATTERN DOTS-123457
+noback sign \x2860 67 # ⡠ BRAILLE PATTERN DOTS-67
+noback sign \x2861 167 # ⡡ BRAILLE PATTERN DOTS-167
+noback sign \x2862 267 # ⡢ BRAILLE PATTERN DOTS-267
+noback sign \x2863 1267 # ⡣ BRAILLE PATTERN DOTS-1267
+noback sign \x2864 367 # ⡤ BRAILLE PATTERN DOTS-367
+noback sign \x2865 1367 # ⡥ BRAILLE PATTERN DOTS-1367
+noback sign \x2866 2367 # ⡦ BRAILLE PATTERN DOTS-2367
+noback sign \x2867 12367 # ⡧ BRAILLE PATTERN DOTS-12367
+noback sign \x2868 467 # ⡨ BRAILLE PATTERN DOTS-467
+noback sign \x2869 1467 # ⡩ BRAILLE PATTERN DOTS-1467
+noback sign \x286A 2467 # ⡪ BRAILLE PATTERN DOTS-2467
+noback sign \x286B 12467 # ⡫ BRAILLE PATTERN DOTS-12467
+noback sign \x286C 3467 # ⡬ BRAILLE PATTERN DOTS-3467
+noback sign \x286D 13467 # ⡭ BRAILLE PATTERN DOTS-13467
+noback sign \x286E 23467 # ⡮ BRAILLE PATTERN DOTS-23467
+noback sign \x286F 123467 # ⡯ BRAILLE PATTERN DOTS-123467
+noback sign \x2870 567 # ⡰ BRAILLE PATTERN DOTS-567
+noback sign \x2871 1567 # ⡱ BRAILLE PATTERN DOTS-1567
+noback sign \x2872 2567 # ⡲ BRAILLE PATTERN DOTS-2567
+noback sign \x2873 12567 # ⡳ BRAILLE PATTERN DOTS-12567
+noback sign \x2874 3567 # ⡴ BRAILLE PATTERN DOTS-3567
+noback sign \x2875 13567 # ⡵ BRAILLE PATTERN DOTS-13567
+noback sign \x2876 23567 # ⡶ BRAILLE PATTERN DOTS-23567
+noback sign \x2877 123567 # ⡷ BRAILLE PATTERN DOTS-123567
+noback sign \x2878 4567 # ⡸ BRAILLE PATTERN DOTS-4567
+noback sign \x2879 14567 # ⡹ BRAILLE PATTERN DOTS-14567
+noback sign \x287A 24567 # ⡺ BRAILLE PATTERN DOTS-24567
+noback sign \x287B 124567 # ⡻ BRAILLE PATTERN DOTS-124567
+noback sign \x287C 34567 # ⡼ BRAILLE PATTERN DOTS-34567
+noback sign \x287D 134567 # ⡽ BRAILLE PATTERN DOTS-134567
+noback sign \x287E 234567 # ⡾ BRAILLE PATTERN DOTS-234567
+noback sign \x287F 1234567 # ⡿ BRAILLE PATTERN DOTS-1234567
+noback sign \x2880 8 # ⢀ BRAILLE PATTERN DOTS-8
+noback sign \x2881 18 # ⢁ BRAILLE PATTERN DOTS-18
+noback sign \x2882 28 # ⢂ BRAILLE PATTERN DOTS-28
+noback sign \x2883 128 # ⢃ BRAILLE PATTERN DOTS-128
+noback sign \x2884 38 # ⢄ BRAILLE PATTERN DOTS-38
+noback sign \x2885 138 # ⢅ BRAILLE PATTERN DOTS-138
+noback sign \x2886 238 # ⢆ BRAILLE PATTERN DOTS-238
+noback sign \x2887 1238 # ⢇ BRAILLE PATTERN DOTS-1238
+noback sign \x2888 48 # ⢈ BRAILLE PATTERN DOTS-48
+noback sign \x2889 148 # ⢉ BRAILLE PATTERN DOTS-148
+noback sign \x288A 248 # ⢊ BRAILLE PATTERN DOTS-248
+noback sign \x288B 1248 # ⢋ BRAILLE PATTERN DOTS-1248
+noback sign \x288C 348 # ⢌ BRAILLE PATTERN DOTS-348
+noback sign \x288D 1348 # ⢍ BRAILLE PATTERN DOTS-1348
+noback sign \x288E 2348 # ⢎ BRAILLE PATTERN DOTS-2348
+noback sign \x288F 12348 # ⢏ BRAILLE PATTERN DOTS-12348
+noback sign \x2890 58 # ⢐ BRAILLE PATTERN DOTS-58
+noback sign \x2891 158 # ⢑ BRAILLE PATTERN DOTS-158
+noback sign \x2892 258 # ⢒ BRAILLE PATTERN DOTS-258
+noback sign \x2893 1258 # ⢓ BRAILLE PATTERN DOTS-1258
+noback sign \x2894 358 # ⢔ BRAILLE PATTERN DOTS-358
+noback sign \x2895 1358 # ⢕ BRAILLE PATTERN DOTS-1358
+noback sign \x2896 2358 # ⢖ BRAILLE PATTERN DOTS-2358
+noback sign \x2897 12358 # ⢗ BRAILLE PATTERN DOTS-12358
+noback sign \x2898 458 # ⢘ BRAILLE PATTERN DOTS-458
+noback sign \x2899 1458 # ⢙ BRAILLE PATTERN DOTS-1458
+noback sign \x289A 2458 # ⢚ BRAILLE PATTERN DOTS-2458
+noback sign \x289B 12458 # ⢛ BRAILLE PATTERN DOTS-12458
+noback sign \x289C 3458 # ⢜ BRAILLE PATTERN DOTS-3458
+noback sign \x289D 13458 # ⢝ BRAILLE PATTERN DOTS-13458
+noback sign \x289E 23458 # ⢞ BRAILLE PATTERN DOTS-23458
+noback sign \x289F 123458 # ⢟ BRAILLE PATTERN DOTS-123458
+noback sign \x28A0 68 # ⢠ BRAILLE PATTERN DOTS-68
+noback sign \x28A1 168 # ⢡ BRAILLE PATTERN DOTS-168
+noback sign \x28A2 268 # ⢢ BRAILLE PATTERN DOTS-268
+noback sign \x28A3 1268 # ⢣ BRAILLE PATTERN DOTS-1268
+noback sign \x28A4 368 # ⢤ BRAILLE PATTERN DOTS-368
+noback sign \x28A5 1368 # ⢥ BRAILLE PATTERN DOTS-1368
+noback sign \x28A6 2368 # ⢦ BRAILLE PATTERN DOTS-2368
+noback sign \x28A7 12368 # ⢧ BRAILLE PATTERN DOTS-12368
+noback sign \x28A8 468 # ⢨ BRAILLE PATTERN DOTS-468
+noback sign \x28A9 1468 # ⢩ BRAILLE PATTERN DOTS-1468
+noback sign \x28AA 2468 # ⢪ BRAILLE PATTERN DOTS-2468
+noback sign \x28AB 12468 # ⢫ BRAILLE PATTERN DOTS-12468
+noback sign \x28AC 3468 # ⢬ BRAILLE PATTERN DOTS-3468
+noback sign \x28AD 13468 # ⢭ BRAILLE PATTERN DOTS-13468
+noback sign \x28AE 23468 # ⢮ BRAILLE PATTERN DOTS-23468
+noback sign \x28AF 123468 # ⢯ BRAILLE PATTERN DOTS-123468
+noback sign \x28B0 568 # ⢰ BRAILLE PATTERN DOTS-568
+noback sign \x28B1 1568 # ⢱ BRAILLE PATTERN DOTS-1568
+noback sign \x28B2 2568 # ⢲ BRAILLE PATTERN DOTS-2568
+noback sign \x28B3 12568 # ⢳ BRAILLE PATTERN DOTS-12568
+noback sign \x28B4 3568 # ⢴ BRAILLE PATTERN DOTS-3568
+noback sign \x28B5 13568 # ⢵ BRAILLE PATTERN DOTS-13568
+noback sign \x28B6 23568 # ⢶ BRAILLE PATTERN DOTS-23568
+noback sign \x28B7 123568 # ⢷ BRAILLE PATTERN DOTS-123568
+noback sign \x28B8 4568 # ⢸ BRAILLE PATTERN DOTS-4568
+noback sign \x28B9 14568 # ⢹ BRAILLE PATTERN DOTS-14568
+noback sign \x28BA 24568 # ⢺ BRAILLE PATTERN DOTS-24568
+noback sign \x28BB 124568 # ⢻ BRAILLE PATTERN DOTS-124568
+noback sign \x28BC 34568 # ⢼ BRAILLE PATTERN DOTS-34568
+noback sign \x28BD 134568 # ⢽ BRAILLE PATTERN DOTS-134568
+noback sign \x28BE 234568 # ⢾ BRAILLE PATTERN DOTS-234568
+noback sign \x28BF 1234568 # ⢿ BRAILLE PATTERN DOTS-1234568
+noback sign \x28C0 78 # ⣀ BRAILLE PATTERN DOTS-78
+noback sign \x28C1 178 # ⣁ BRAILLE PATTERN DOTS-178
+noback sign \x28C2 278 # ⣂ BRAILLE PATTERN DOTS-278
+noback sign \x28C3 1278 # ⣃ BRAILLE PATTERN DOTS-1278
+noback sign \x28C4 378 # ⣄ BRAILLE PATTERN DOTS-378
+noback sign \x28C5 1378 # ⣅ BRAILLE PATTERN DOTS-1378
+noback sign \x28C6 2378 # ⣆ BRAILLE PATTERN DOTS-2378
+noback sign \x28C7 12378 # ⣇ BRAILLE PATTERN DOTS-12378
+noback sign \x28C8 478 # ⣈ BRAILLE PATTERN DOTS-478
+noback sign \x28C9 1478 # ⣉ BRAILLE PATTERN DOTS-1478
+noback sign \x28CA 2478 # ⣊ BRAILLE PATTERN DOTS-2478
+noback sign \x28CB 12478 # ⣋ BRAILLE PATTERN DOTS-12478
+noback sign \x28CC 3478 # ⣌ BRAILLE PATTERN DOTS-3478
+noback sign \x28CD 13478 # ⣍ BRAILLE PATTERN DOTS-13478
+noback sign \x28CE 23478 # ⣎ BRAILLE PATTERN DOTS-23478
+noback sign \x28CF 123478 # ⣏ BRAILLE PATTERN DOTS-123478
+noback sign \x28D0 578 # ⣐ BRAILLE PATTERN DOTS-578
+noback sign \x28D1 1578 # ⣑ BRAILLE PATTERN DOTS-1578
+noback sign \x28D2 2578 # ⣒ BRAILLE PATTERN DOTS-2578
+noback sign \x28D3 12578 # ⣓ BRAILLE PATTERN DOTS-12578
+noback sign \x28D4 3578 # ⣔ BRAILLE PATTERN DOTS-3578
+noback sign \x28D5 13578 # ⣕ BRAILLE PATTERN DOTS-13578
+noback sign \x28D6 23578 # ⣖ BRAILLE PATTERN DOTS-23578
+noback sign \x28D7 123578 # ⣗ BRAILLE PATTERN DOTS-123578
+noback sign \x28D8 4578 # ⣘ BRAILLE PATTERN DOTS-4578
+noback sign \x28D9 14578 # ⣙ BRAILLE PATTERN DOTS-14578
+noback sign \x28DA 24578 # ⣚ BRAILLE PATTERN DOTS-24578
+noback sign \x28DB 124578 # ⣛ BRAILLE PATTERN DOTS-124578
+noback sign \x28DC 34578 # ⣜ BRAILLE PATTERN DOTS-34578
+noback sign \x28DD 134578 # ⣝ BRAILLE PATTERN DOTS-134578
+noback sign \x28DE 234578 # ⣞ BRAILLE PATTERN DOTS-234578
+noback sign \x28DF 1234578 # ⣟ BRAILLE PATTERN DOTS-1234578
+noback sign \x28E0 678 # ⣠ BRAILLE PATTERN DOTS-678
+noback sign \x28E1 1678 # ⣡ BRAILLE PATTERN DOTS-1678
+noback sign \x28E2 2678 # ⣢ BRAILLE PATTERN DOTS-2678
+noback sign \x28E3 12678 # ⣣ BRAILLE PATTERN DOTS-12678
+noback sign \x28E4 3678 # ⣤ BRAILLE PATTERN DOTS-3678
+noback sign \x28E5 13678 # ⣥ BRAILLE PATTERN DOTS-13678
+noback sign \x28E6 23678 # ⣦ BRAILLE PATTERN DOTS-23678
+noback sign \x28E7 123678 # ⣧ BRAILLE PATTERN DOTS-123678
+noback sign \x28E8 4678 # ⣨ BRAILLE PATTERN DOTS-4678
+noback sign \x28E9 14678 # ⣩ BRAILLE PATTERN DOTS-14678
+noback sign \x28EA 24678 # ⣪ BRAILLE PATTERN DOTS-24678
+noback sign \x28EB 124678 # ⣫ BRAILLE PATTERN DOTS-124678
+noback sign \x28EC 34678 # ⣬ BRAILLE PATTERN DOTS-34678
+noback sign \x28ED 134678 # ⣭ BRAILLE PATTERN DOTS-134678
+noback sign \x28EE 234678 # ⣮ BRAILLE PATTERN DOTS-234678
+noback sign \x28EF 1234678 # ⣯ BRAILLE PATTERN DOTS-1234678
+noback sign \x28F0 5678 # ⣰ BRAILLE PATTERN DOTS-5678
+noback sign \x28F1 15678 # ⣱ BRAILLE PATTERN DOTS-15678
+noback sign \x28F2 25678 # ⣲ BRAILLE PATTERN DOTS-25678
+noback sign \x28F3 125678 # ⣳ BRAILLE PATTERN DOTS-125678
+noback sign \x28F4 35678 # ⣴ BRAILLE PATTERN DOTS-35678
+noback sign \x28F5 135678 # ⣵ BRAILLE PATTERN DOTS-135678
+noback sign \x28F6 235678 # ⣶ BRAILLE PATTERN DOTS-235678
+noback sign \x28F7 1235678 # ⣷ BRAILLE PATTERN DOTS-1235678
+noback sign \x28F8 45678 # ⣸ BRAILLE PATTERN DOTS-45678
+noback sign \x28F9 145678 # ⣹ BRAILLE PATTERN DOTS-145678
+noback sign \x28FA 245678 # ⣺ BRAILLE PATTERN DOTS-245678
+noback sign \x28FB 1245678 # ⣻ BRAILLE PATTERN DOTS-1245678
+noback sign \x28FC 345678 # ⣼ BRAILLE PATTERN DOTS-345678
+noback sign \x28FD 1345678 # ⣽ BRAILLE PATTERN DOTS-1345678
+noback sign \x28FE 2345678 # ⣾ BRAILLE PATTERN DOTS-2345678
+noback sign \x28FF 12345678 # ⣿ BRAILLE PATTERN DOTS-12345678
+
+
+# ----------------------------------------------------------------------------------------------
+
diff --git a/third_party/liblouis/overrides/tables/da.ctb b/third_party/liblouis/overrides/tables/da.ctb
new file mode 100644
index 0000000..49baad3
--- /dev/null
+++ b/third_party/liblouis/overrides/tables/da.ctb
@@ -0,0 +1,280 @@
+###############################################################################
+# BRLTTY - A background process providing access to the console screen (when in
+# text mode) for a blind person using a refreshable braille display.
+#
+# Copyright (C) 1995-2008 by The BRLTTY Developers.
+#
+# BRLTTY comes with ABSOLUTELY NO WARRANTY.
+#
+# This is free software, placed under the terms of the
+# GNU Lesser General Public License, as published by the Free Software
+# Foundation; either version 2.1 of the License, or (at your option) any
+# later version. Please see the file LICENSE-LGPL for details.
+#
+# Web Page: http://mielke.cc/brltty/
+#
+# This software is maintained by Dave Mielke <dave@mielke.cc>.
+###############################################################################
+
+# BRLTTY Text Table - Danish (iso-8859-1)
+
+# This is the table which comes closest to the Danish standard 1252 table. All
+# control characters are mapped as their corresponding capital letters with
+# dot-8 added. Most Danish braille users should use this table.
+
+# generated by ttbtest
+letter \x0000 8 NULL
+letter \x0001 178 START OF HEADING
+letter \x0002 1278 START OF TEXT
+letter \x0003 1478 END OF TEXT
+letter \x0004 14578 END OF TRANSMISSION
+letter \x0005 24568 ENQUIRY
+letter \x0006 12478 ACKNOWLEDGE
+letter \x0007 124578 BELL
+letter \x0008 12578 BACKSPACE
+space \t 2478 CHARACTER TABULATION
+space \n 678 LINE FEED (LF)
+space \v 1368 LINE TABULATION
+space \f 12378 FORM FEED (FF)
+space \r 257 CARRIAGE RETURN (CR)
+letter \x000e 134578 SHIFT OUT
+letter \x000f 12358 SHIFT IN
+letter \x0010 123478 DATA LINK ESCAPE
+letter \x0011 1234578 DEVICE CONTROL ONE
+letter \x0012 13568 DEVICE CONTROL TWO
+letter \x0013 4578 DEVICE CONTROL THREE
+letter \x0014 268 DEVICE CONTROL FOUR
+letter \x0015 13678 NEGATIVE ACKNOWLEDGE
+letter \x0016 278 SYNCHRONOUS IDLE
+letter \x0017 3578 END OF TRANSMISSION BLOCK
+letter \x0018 78 CANCEL
+letter \x0019 68 END OF MEDIUM
+letter \x001a 135678 SUBSTITUTE
+letter \x001b 2678 ESCAPE
+letter \x001c 45678 INFORMATION SEPARATOR FOUR
+letter \x001d 12368 INFORMATION SEPARATOR THREE
+letter \x001e 1234678 INFORMATION SEPARATOR TWO
+letter \x001f 235678 INFORMATION SEPARATOR ONE
+space \s 0 SPACE
+punctuation ! 235 EXCLAMATION MARK
+punctuation " 2356 QUOTATION MARK
+punctuation # 34568 NUMBER SIGN
+punctuation $ 25678 DOLLAR SIGN
+punctuation % 24578 PERCENT SIGN
+punctuation & 123468 AMPERSAND
+punctuation ' 4 APOSTROPHE
+punctuation ( 2368 LEFT PARENTHESIS
+punctuation ) 3568 RIGHT PARENTHESIS
+punctuation * 35 ASTERISK
+punctuation + 2358 PLUS SIGN
+punctuation , 2 COMMA
+punctuation - 368 HYPHEN-MINUS
+punctuation . 3 FULL STOP
+punctuation / 34 SOLIDUS
+include digits8Dots.uti
+punctuation : 25 COLON
+punctuation ; 23 SEMICOLON
+punctuation < 358 LESS-THAN SIGN
+punctuation = 23568 EQUALS SIGN
+punctuation > 267 GREATER-THAN SIGN
+punctuation ? 26 QUESTION MARK
+punctuation @ 478 COMMERCIAL AT
+uppercase A 17 LATIN CAPITAL LETTER A
+uppercase B 127 LATIN CAPITAL LETTER B
+uppercase C 147 LATIN CAPITAL LETTER C
+uppercase D 1457 LATIN CAPITAL LETTER D
+uppercase E 157 LATIN CAPITAL LETTER E
+uppercase F 1247 LATIN CAPITAL LETTER F
+uppercase G 12457 LATIN CAPITAL LETTER G
+uppercase H 1257 LATIN CAPITAL LETTER H
+uppercase I 247 LATIN CAPITAL LETTER I
+uppercase J 2457 LATIN CAPITAL LETTER J
+uppercase K 137 LATIN CAPITAL LETTER K
+uppercase L 1237 LATIN CAPITAL LETTER L
+uppercase M 1347 LATIN CAPITAL LETTER M
+uppercase N 13457 LATIN CAPITAL LETTER N
+uppercase O 1357 LATIN CAPITAL LETTER O
+uppercase P 12347 LATIN CAPITAL LETTER P
+uppercase Q 123457 LATIN CAPITAL LETTER Q
+uppercase R 12357 LATIN CAPITAL LETTER R
+uppercase S 2347 LATIN CAPITAL LETTER S
+uppercase T 23457 LATIN CAPITAL LETTER T
+uppercase U 1367 LATIN CAPITAL LETTER U
+uppercase V 12367 LATIN CAPITAL LETTER V
+uppercase W 24567 LATIN CAPITAL LETTER W
+uppercase X 13467 LATIN CAPITAL LETTER X
+uppercase Y 134567 LATIN CAPITAL LETTER Y
+uppercase Z 13567 LATIN CAPITAL LETTER Z
+punctuation [ 23678 LEFT SQUARE BRACKET
+punctuation \\ 347 REVERSE SOLIDUS
+punctuation ] 35678 RIGHT SQUARE BRACKET
+punctuation ^ 12348 CIRCUMFLEX ACCENT
+punctuation _ 3678 LOW LINE
+punctuation ` 5 GRAVE ACCENT
+lowercase a 1 LATIN SMALL LETTER A
+lowercase b 12 LATIN SMALL LETTER B
+lowercase c 14 LATIN SMALL LETTER C
+lowercase d 145 LATIN SMALL LETTER D
+lowercase e 15 LATIN SMALL LETTER E
+lowercase f 124 LATIN SMALL LETTER F
+lowercase g 1245 LATIN SMALL LETTER G
+lowercase h 125 LATIN SMALL LETTER H
+lowercase i 24 LATIN SMALL LETTER I
+lowercase j 245 LATIN SMALL LETTER J
+lowercase k 13 LATIN SMALL LETTER K
+lowercase l 123 LATIN SMALL LETTER L
+lowercase m 134 LATIN SMALL LETTER M
+lowercase n 1345 LATIN SMALL LETTER N
+lowercase o 135 LATIN SMALL LETTER O
+lowercase p 1234 LATIN SMALL LETTER P
+lowercase q 12345 LATIN SMALL LETTER Q
+lowercase r 1235 LATIN SMALL LETTER R
+lowercase s 234 LATIN SMALL LETTER S
+lowercase t 2345 LATIN SMALL LETTER T
+lowercase u 136 LATIN SMALL LETTER U
+lowercase v 1236 LATIN SMALL LETTER V
+lowercase w 2456 LATIN SMALL LETTER W
+lowercase x 1346 LATIN SMALL LETTER X
+lowercase y 13456 LATIN SMALL LETTER Y
+lowercase z 1356 LATIN SMALL LETTER Z
+punctuation { 123678 LEFT CURLY BRACKET
+punctuation | 4568 VERTICAL LINE
+punctuation } 345678 RIGHT CURLY BRACKET
+punctuation ~ 467 TILDE
+letter \x007f 7 DELETE
+letter \x20AC 1578 EURO SIGN
+letter \x201A 457
+letter \x0192 58
+letter \x201E 2378
+letter \x2022 37
+letter \x2026 6
+letter \x0080 24568 <control-0080>
+letter \x0081 45 <control-0081>
+letter \x0082 457 BREAK PERMITTED HERE
+letter \x0083 5 NO BREAK HERE
+letter \x0084 2378 <control-0084>
+letter \x0085 6 NEXT LINE (NEL)
+letter \x0086 2357 START OF SELECTED AREA
+letter \x0087 23578 END OF SELECTED AREA
+letter \x0088 5678 CHARACTER TABULATION SET
+letter \x0089 3578 CHARACTER TABULATION WITH JUSTIFICATION
+letter \x008a 4578 LINE TABULATION SET
+letter \x008b 456 PARTIAL LINE FORWARD
+letter \x008c 12358 PARTIAL LINE BACKWARD
+letter \x008d 3567 REVERSE LINE FEED
+letter \x008e 3467 SINGLE SHIFT TWO
+letter \x008f 27 SINGLE SHIFT THREE
+letter \x0090 357 DEVICE CONTROL STRING
+letter \x0091 47 PRIVATE USE ONE
+letter \x0092 48 PRIVATE USE TWO
+letter \x0093 237 SET TRANSMIT STATE
+letter \x0094 568 CANCEL CHARACTER
+letter \x0095 37 MESSAGE WAITING
+letter \x0096 36 START OF GUARDED AREA
+letter \x0097 367 END OF GUARDED AREA
+letter \x0098 46 START OF STRING
+letter \x0099 268 <control-0099>
+letter \x009a 2348 SINGLE CHARACTER INTRODUCER
+letter \x009b 4567 CONTROL SEQUENCE INTRODUCER
+letter \x009c 1358 STRING TERMINATOR
+letter \x009d 23458 OPERATING SYSTEM COMMAND
+letter \x009e 346 PRIVACY MESSAGE
+letter \x009f 2345678 APPLICATION PROGRAM COMMAND
+punctuation \x00a0 0 NO-BREAK SPACE
+punctuation \x00a1 256 INVERTED EXCLAMATION MARK
+punctuation \x00a2 2578 CENT SIGN
+punctuation \x00a3 1238 POUND SIGN
+punctuation \x00a4 2367 CURRENCY SIGN
+punctuation \x00a5 67 YEN SIGN
+punctuation \x00a6 3478 BROKEN BAR
+punctuation \x00a7 578 SECTION SIGN
+punctuation \x00a8 56 DIAERESIS
+punctuation \x00a9 78 COPYRIGHT SIGN
+letter \x00aa 234678 FEMININE ORDINAL INDICATOR
+punctuation \x00ab 57 LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+punctuation \x00ac 34567 NOT SIGN
+punctuation \x00ad 378 SOFT HYPHEN
+punctuation \x00ae 13568 REGISTERED SIGN
+punctuation \x00af 23567 MACRON
+punctuation \x00b0 356 DEGREE SIGN
+punctuation \x00b1 123458 PLUS-MINUS SIGN
+punctuation \x00b2 238 SUPERSCRIPT TWO
+punctuation \x00b3 258 SUPERSCRIPT THREE
+punctuation \x00b4 468 ACUTE ACCENT
+lowercase \x00b5 236 MICRO SIGN
+punctuation \x00b6 1234568 PILCROW SIGN
+punctuation \x00b7 38 MIDDLE DOT
+punctuation \x00b8 4678 CEDILLA
+punctuation \x00b9 28 SUPERSCRIPT ONE
+letter \x00ba 7 MASCULINE ORDINAL INDICATOR
+punctuation \x00bb 567 RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+punctuation \x00bc 13458 VULGAR FRACTION ONE QUARTER
+punctuation \x00bd 458 VULGAR FRACTION ONE HALF
+punctuation \x00be 3456 VULGAR FRACTION THREE QUARTERS
+punctuation \x00bf 348 INVERTED QUESTION MARK
+uppercase \x00c0 123567 LATIN CAPITAL LETTER A WITH GRAVE
+uppercase \x00c1 1235678 LATIN CAPITAL LETTER A WITH ACUTE
+uppercase \x00c2 1678 LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+uppercase \x00c3 14678 LATIN CAPITAL LETTER A WITH TILDE
+uppercase \x00c4 34578 LATIN CAPITAL LETTER A WITH DIAERESIS
+uppercase \x00c5 167 LATIN CAPITAL LETTER A WITH RING ABOVE
+uppercase \x00c6 3457 LATIN CAPITAL LETTER AE
+uppercase \x00c7 123467 LATIN CAPITAL LETTER C WITH CEDILLA
+uppercase \x00c8 23467 LATIN CAPITAL LETTER E WITH GRAVE
+uppercase \x00c9 1234567 LATIN CAPITAL LETTER E WITH ACUTE
+uppercase \x00ca 1267 LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+uppercase \x00cb 12467 LATIN CAPITAL LETTER E WITH DIAERESIS
+uppercase \x00cc 15678 LATIN CAPITAL LETTER I WITH GRAVE
+uppercase \x00cd 12678 LATIN CAPITAL LETTER I WITH ACUTE
+uppercase \x00ce 1467 LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+uppercase \x00cf 124567 LATIN CAPITAL LETTER I WITH DIAERESIS
+uppercase \x00d0 68 LATIN CAPITAL LETTER ETH
+uppercase \x00d1 1245678 LATIN CAPITAL LETTER N WITH TILDE
+uppercase \x00d2 124678 LATIN CAPITAL LETTER O WITH GRAVE
+uppercase \x00d3 34678 LATIN CAPITAL LETTER O WITH ACUTE
+uppercase \x00d4 14567 LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+uppercase \x00d5 145678 LATIN CAPITAL LETTER O WITH TILDE
+uppercase \x00d6 24678 LATIN CAPITAL LETTER O WITH DIAERESIS
+punctuation \x00d7 13468 MULTIPLICATION SIGN
+uppercase \x00d8 2467 LATIN CAPITAL LETTER O WITH STROKE
+uppercase \x00d9 234567 LATIN CAPITAL LETTER U WITH GRAVE
+uppercase \x00da 125678 LATIN CAPITAL LETTER U WITH ACUTE
+uppercase \x00db 1567 LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+uppercase \x00dc 12567 LATIN CAPITAL LETTER U WITH DIAERESIS
+uppercase \x00dd 257 LATIN CAPITAL LETTER Y WITH ACUTE
+uppercase \x00de 1368 LATIN CAPITAL LETTER THORN
+lowercase \x00df 23468 LATIN SMALL LETTER SHARP S
+lowercase \x00e0 12356 LATIN SMALL LETTER A WITH GRAVE
+lowercase \x00e1 123568 LATIN SMALL LETTER A WITH ACUTE
+lowercase \x00e2 168 LATIN SMALL LETTER A WITH CIRCUMFLEX
+lowercase \x00e3 1468 LATIN SMALL LETTER A WITH TILDE
+lowercase \x00e4 3458 LATIN SMALL LETTER A WITH DIAERESIS
+lowercase \x00e5 16 LATIN SMALL LETTER A WITH RING ABOVE
+lowercase \x00e6 345 LATIN SMALL LETTER AE
+lowercase \x00e7 12346 LATIN SMALL LETTER C WITH CEDILLA
+lowercase \x00e8 2346 LATIN SMALL LETTER E WITH GRAVE
+lowercase \x00e9 123456 LATIN SMALL LETTER E WITH ACUTE
+lowercase \x00ea 126 LATIN SMALL LETTER E WITH CIRCUMFLEX
+lowercase \x00eb 1246 LATIN SMALL LETTER E WITH DIAERESIS
+lowercase \x00ec 1568 LATIN SMALL LETTER I WITH GRAVE
+lowercase \x00ed 1268 LATIN SMALL LETTER I WITH ACUTE
+lowercase \x00ee 146 LATIN SMALL LETTER I WITH CIRCUMFLEX
+lowercase \x00ef 12456 LATIN SMALL LETTER I WITH DIAERESIS
+lowercase \x00f0 134568 LATIN SMALL LETTER ETH
+lowercase \x00f1 124568 LATIN SMALL LETTER N WITH TILDE
+lowercase \x00f2 12468 LATIN SMALL LETTER O WITH GRAVE
+lowercase \x00f3 3468 LATIN SMALL LETTER O WITH ACUTE
+lowercase \x00f4 1456 LATIN SMALL LETTER O WITH CIRCUMFLEX
+lowercase \x00f5 14568 LATIN SMALL LETTER O WITH TILDE
+lowercase \x00f6 2468 LATIN SMALL LETTER O WITH DIAERESIS
+punctuation \x00f7 2568 DIVISION SIGN
+lowercase \x00f8 246 LATIN SMALL LETTER O WITH STROKE
+lowercase \x00f9 23456 LATIN SMALL LETTER U WITH GRAVE
+lowercase \x00fa 12568 LATIN SMALL LETTER U WITH ACUTE
+lowercase \x00fb 156 LATIN SMALL LETTER U WITH CIRCUMFLEX
+lowercase \x00fc 1256 LATIN SMALL LETTER U WITH DIAERESIS
+lowercase \x00fd 1348 LATIN SMALL LETTER Y WITH ACUTE
+lowercase \x00fe 138 LATIN SMALL LETTER THORN
+lowercase \x00ff 234568 LATIN SMALL LETTER Y WITH DIAERESIS
+punctuation \x0192 58
+
diff --git a/third_party/liblouis/tables.json b/third_party/liblouis/tables.json
new file mode 100644
index 0000000..54e089d
--- /dev/null
+++ b/third_party/liblouis/tables.json
@@ -0,0 +1,398 @@
+[
+ {
+ "locale": "ar",
+ "dots": "6",
+ "id": "ar-g1",
+ "grade": "1",
+ "fileName": "ar-ar-g1.utb"
+ },
+ {
+ "locale": "bg",
+ "dots": "8",
+ "id": "bg-comp8",
+ "fileName": "bg.ctb"
+ },
+ {
+ "locale": "ca",
+ "dots": "6",
+ "id": "ca-g1",
+ "grade": "1",
+ "fileName": "ca-g1.ctb"
+ },
+ {
+ "locale": "hr",
+ "dots": "8",
+ "id": "hr-comp8",
+ "fileName": "hr.ctb"
+ },
+ {
+ "locale": "cs",
+ "dots": "6",
+ "id": "cs-g1",
+ "grade": "1",
+ "fileName": "cs-g1.ctb"
+ },
+ {
+ "locale": "da",
+ "dots": "8",
+ "id": "da-comp8",
+ "fileName": "da.ctb"
+ },
+ {
+ "locale": "nl",
+ "dots": "6",
+ "id": "nl-g1",
+ "grade": "1",
+ "fileName": "Nl-Nl-g1.utb"
+ },
+ {
+ "locale": "en_CA",
+ "dots": "8",
+ "id": "en-CA-comp8",
+ "fileName": "en_CA.ctb"
+ },
+ {
+ "locale": "en_GB",
+ "dots": "6",
+ "id": "en-GB-g1",
+ "grade": "1",
+ "fileName": "en-gb-g1.utb"
+ },
+ {
+ "locale": "en_GB",
+ "dots": "6",
+ "id": "en-GB-g2",
+ "grade": "2",
+ "fileName": "en-GB-g2.ctb"
+ },
+ {
+ "locale": "en_US",
+ "dots": "8",
+ "id": "en-US-comp8",
+ "fileName": "en-us-comp8.ctb"
+ },
+ {
+ "locale": "en_US",
+ "dots": "6",
+ "id": "en-US-g1",
+ "grade": "1",
+ "fileName": "en-us-g1.ctb"
+ },
+ {
+ "locale": "en_US",
+ "dots": "6",
+ "id": "en-US-g2",
+ "grade": "2",
+ "fileName": "en-us-g2.ctb"
+ },
+ {
+ "locale": "et",
+ "dots": "8",
+ "id": "et-comp8",
+ "fileName": "et-g0.utb"
+ },
+ {
+ "locale": "fr",
+ "dots": "8",
+ "id": "fr-comp8",
+ "fileName": "fr-2007.ctb"
+ },
+ {
+ "locale": "fr_CA",
+ "dots": "6",
+ "id": "fr-CA-g1",
+ "grade": "1",
+ "fileName": "fr-ca-g1.utb"
+ },
+ {
+ "locale": "fr_CA",
+ "dots": "6",
+ "id": "fr-CA-g2",
+ "grade": "2",
+ "fileName": "Fr-Ca-g2.ctb"
+ },
+ {
+ "locale": "fr_FR",
+ "dots": "6",
+ "id": "fr-FR-g1",
+ "grade": "1",
+ "fileName": "fr-fr-g1.utb"
+ },
+ {
+ "locale": "fr_FR",
+ "dots": "6",
+ "id": "fr-FR-g2",
+ "grade": "2",
+ "fileName": "Fr-Fr-g2.ctb"
+ },
+ {
+ "locale": "fi",
+ "dots": "8",
+ "id": "fi-comp8",
+ "fileName": "fi-fi-8dot.ctb"
+ },
+ {
+ "locale": "de",
+ "dots": "8",
+ "id": "de-comp8",
+ "fileName": "de-de-comp8.ctb"
+ },
+ {
+ "locale": "de_CH",
+ "dots": "6",
+ "id": "de-CH-g0",
+ "grade": "0",
+ "fileName": "de-ch-g0.utb"
+ },
+ {
+ "locale": "de_CH",
+ "dots": "6",
+ "id": "de-CH-g1",
+ "grade": "1",
+ "fileName": "de-ch-g1.ctb"
+ },
+ {
+ "locale": "de_CH",
+ "dots": "6",
+ "id": "de-CH-g2",
+ "grade": "2",
+ "fileName": "de-ch-g2.ctb"
+ },
+ {
+ "locale": "de_DE",
+ "dots": "6",
+ "id": "de-DE-g0",
+ "grade": "0",
+ "fileName": "de-de-g0.utb"
+ },
+ {
+ "locale": "de_DE",
+ "dots": "6",
+ "id": "de-DE-g1",
+ "grade": "1",
+ "fileName": "de-de-g1.ctb"
+ },
+ {
+ "locale": "de_DE",
+ "dots": "6",
+ "id": "de-DE-g2",
+ "grade": "2",
+ "fileName": "de-de-g2.ctb"
+ },
+ {
+ "locale": "el",
+ "dots": "6",
+ "id": "el-g1",
+ "grade": "1",
+ "fileName": "gr-gr-g1.utb"
+ },
+ {
+ "locale": "hi",
+ "dots": "8",
+ "id": "hi-comp8",
+ "fileName": "hi.ctb"
+ },
+ {
+ "locale": "hi",
+ "dots": "6",
+ "id": "hi-g1",
+ "grade": "1",
+ "fileName": "hi-in-g1.utb"
+ },
+ {
+ "locale": "hu",
+ "dots": "8",
+ "id": "hu-comp8",
+ "fileName": "hu-hu-comp8.ctb"
+ },
+ {
+ "locale": "hu",
+ "dots": "6",
+ "id": "hu-g1",
+ "grade": "1",
+ "fileName": "hu-hu-g1.ctb"
+ },
+ {
+ "locale": "is",
+ "dots": "8",
+ "id": "is-comp8",
+ "fileName": "is.ctb"
+ },
+ {
+ "locale": "it",
+ "dots": "8",
+ "id": "it-comp8",
+ "fileName": "it-it-comp8.utb"
+ },
+ {
+ "locale": "it",
+ "dots": "6",
+ "id": "it-g1",
+ "grade": "1",
+ "fileName": "it-it-comp6.utb"
+ },
+ {
+ "locale": "lv",
+ "dots": "6",
+ "id": "lv-g1",
+ "grade": "1",
+ "fileName": "Lv-Lv-g1.utb"
+ },
+ {
+ "locale": "lt",
+ "dots": "8",
+ "id": "lt-comp8",
+ "fileName": "lt.ctb"
+ },
+ {
+ "locale": "nb",
+ "dots": "8",
+ "id": "nb-comp8",
+ "fileName": "no-no.ctb"
+ },
+ {
+ "locale": "nb",
+ "dots": "6",
+ "id": "nb-g0",
+ "grade": "0",
+ "fileName": "no-no-g0.utb"
+ },
+ {
+ "locale": "nb",
+ "dots": "6",
+ "id": "nb-g1",
+ "grade": "1",
+ "fileName": "no-no-g1.ctb"
+ },
+ {
+ "locale": "nb",
+ "dots": "6",
+ "id": "nb-g2",
+ "grade": "2",
+ "fileName": "no-no-g2.ctb"
+ },
+ {
+ "locale": "nb",
+ "dots": "6",
+ "id": "nb-g3",
+ "grade": "3",
+ "fileName": "no-no-g3.ctb"
+ },
+ {
+ "locale": "pl",
+ "dots": "6",
+ "id": "pl-g1",
+ "grade": "1",
+ "fileName": "Pl-Pl-g1.utb"
+ },
+ {
+ "locale": "pt",
+ "dots": "8",
+ "id": "pt-comp8",
+ "fileName": "pt-pt-comp8.ctb"
+ },
+ {
+ "locale": "pt",
+ "dots": "6",
+ "id": "pt-g1",
+ "grade": "1",
+ "fileName": "pt-pt-g1.utb"
+ },
+ {
+ "locale": "pt",
+ "dots": "6",
+ "id": "pt-g2",
+ "grade": "2",
+ "fileName": "pt-pt-g2.ctb"
+ },
+ {
+ "locale": "ro",
+ "dots": "8",
+ "id": "ro-comp8",
+ "fileName": "ro.ctb"
+ },
+ {
+ "locale": "ru",
+ "dots": "8",
+ "id": "ru-comp8",
+ "fileName": "ru.ctb"
+ },
+ {
+ "locale": "ru",
+ "dots": "6",
+ "id": "ru-g1",
+ "grade": "1",
+ "fileName": "ru-ru-g1.utb"
+ },
+ {
+ "locale": "sr",
+ "dots": "6",
+ "id": "sr-g1",
+ "grade": "1",
+ "fileName": "sr-g1.ctb"
+ },
+ {
+ "locale": "sk",
+ "dots": "6",
+ "id": "sk-g1",
+ "grade": "1",
+ "fileName": "sk-sk-g1.utb"
+ },
+ {
+ "locale": "sl",
+ "dots": "6",
+ "id": "sl-g1",
+ "grade": "1",
+ "fileName": "sl-si-g1.utb"
+ },
+ {
+ "locale": "es",
+ "dots": "8",
+ "id": "es-comp8",
+ "fileName": "Es-Es-G0.utb"
+ },
+ {
+ "locale": "es",
+ "dots": "6",
+ "id": "es-g1",
+ "grade": "1",
+ "fileName": "es-g1.ctb"
+ },
+ {
+ "locale": "sv",
+ "dots": "8",
+ "id": "sv-comp8",
+ "fileName": "sv-1996.ctb"
+ },
+ {
+ "locale": "sv",
+ "dots": "6",
+ "id": "sv-g1",
+ "grade": "1",
+ "fileName": "Se-Se-g1.utb"
+ },
+ {
+ "locale": "tr",
+ "dots": "8",
+ "id": "tr-comp8",
+ "fileName": "tr.ctb"
+ },
+ {
+ "locale": "vi",
+ "dots": "8",
+ "id": "vi-comp8",
+ "fileName": "vi.ctb"
+ },
+ {
+ "locale": "zh",
+ "dots": "8",
+ "id": "zh-comp8",
+ "fileName": "zh-hk.ctb"
+ },
+ {
+ "locale": "zh_TW",
+ "dots": "8",
+ "id": "zh-TW-comp8",
+ "fileName": "zh-tw.ctb"
+ }
+]