blob: da04dbd8471f6948a7693d0615e9408d9838dda9 [file] [log] [blame]
/*
* 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-2023 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://brltty.app/
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
#include "prologue.h"
#include "ttb.h"
#include "ttb_internal.h"
#include "ttb_compile.h"
static int inUcsBlock;
static int
getUnicodeCharacter (DataFile *file, wchar_t *character, const char *description) {
DataOperand string;
if (getDataOperand(file, &string, description)) {
if (string.length > 2) {
if ((string.characters[0] == WC_C('U')) &&
(string.characters[1] == WC_C('+'))) {
const wchar_t *digit = &string.characters[2];
int length = string.length - 2;
*character = 0;
while (length) {
int value;
int shift;
if (!isHexadecimalDigit(*digit++, &value, &shift)) break;
*character <<= shift;
*character |= value;
length -= 1;
}
if (!length) return 1;
}
}
reportDataError(file, "invalid Unicode character: %.*" PRIws,
string.length, string.characters);
}
return 0;
}
static int
testBrailleRepresentation (DataFile *file, wchar_t representation, unsigned char *dots) {
if ((representation & ~UNICODE_CELL_MASK) == UNICODE_BRAILLE_ROW) {
*dots = representation & UNICODE_CELL_MASK;
return 1;
} else {
reportDataError(file, "invalid braille representation");
}
return 0;
}
static DATA_OPERANDS_PROCESSOR(processEncodingOperands) {
DataOperand encoding;
if (getDataOperand(file, &encoding, "character encoding name")) {
if (!isKeyword(WS_C("UTF-8"), encoding.characters, encoding.length)) {
reportDataError(file, "unsupported character encoding: %.*" PRIws,
encoding.length, encoding.characters);
}
}
return 1;
}
static DATA_OPERANDS_PROCESSOR(processDelegateOperands) {
DataOperand type;
if (getDataOperand(file, &type, "delegate type")) {
if (isKeyword(WS_C("FILE"), type.characters, type.length)) {
DataOperand name;
if (getDataOperand(file, &name, "file name")) {
return includeDataFile(file, name.characters, name.length);
}
} else {
return includeDataFile(file, type.characters, type.length);
}
}
return 1;
}
static DATA_OPERANDS_PROCESSOR(processUcsBlockOperands) {
DataOperand action;
if (getDataOperand(file, &action, "UCS block action")) {
const wchar_t *expected = inUcsBlock? WS_C("END"): WS_C("START");
if (isKeyword(expected, action.characters, action.length)) {
inUcsBlock = !inUcsBlock;
} else {
reportDataError(file, "unexpected UCS block action: %.*" PRIws " (expecting %" PRIws ")",
action.length, action.characters, expected);
}
}
return 1;
}
static DATA_OPERANDS_PROCESSOR(processUcsCharOperands) {
TextTableData *ttd = data;
DataOperand string;
if (getDataOperand(file, &string, "character string")) {
if (string.length == 1) {
DataOperand representation;
if (getDataOperand(file, &representation, "braille representation")) {
if (representation.length == 1) {
unsigned char dots;
if (testBrailleRepresentation(file, representation.characters[0], &dots)) {
if (!setTextTableCharacter(ttd, string.characters[0], dots)) return 0;
}
} else {
reportDataError(file, "multi-cell braille representation not supported");
}
}
} else {
reportDataError(file, "multi-character string not supported");
}
}
return 1;
}
static DATA_OPERANDS_PROCESSOR(processUnicodeCharOperands) {
TextTableData *ttd = data;
wchar_t character;
if (getUnicodeCharacter(file, &character, "character")) {
wchar_t representation;
if (getUnicodeCharacter(file, &representation, "braille representation")) {
unsigned char dots;
if (testBrailleRepresentation(file, representation, &dots)) {
if (!setTextTableCharacter(ttd, character, dots)) return 0;
}
}
}
return 1;
}
static DATA_OPERANDS_PROCESSOR(processGnomeBrailleOperands) {
if (inUcsBlock) {
BEGIN_DATA_DIRECTIVE_TABLE
{.name=WS_C("UCS-BLOCK"), .processor=processUcsBlockOperands},
{.name=NULL, .processor=processUcsCharOperands},
END_DATA_DIRECTIVE_TABLE
return processDirectiveOperand(file, &directives, "gnome braille UCS block directive", data);
} else {
BEGIN_DATA_DIRECTIVE_TABLE
{.name=WS_C("ENCODING"), .processor=processEncodingOperands},
// {.name=WS_C("NAME"), .processor=processNameOperands},
// {.name=WS_C("LOCALES"), .processor=processLocalesOperands},
// {.name=WS_C("UCS-SUFFIX"), .processor=processUcsSuffixOperands},
{.name=WS_C("DELEGATE"), .processor=processDelegateOperands},
// {.name=WS_C("UTF8-STRING"), .processor=processUtf8StringOperands},
{.name=WS_C("UCS-BLOCK"), .processor=processUcsBlockOperands},
{.name=WS_C("UCS-CHAR"), .processor=processUcsCharOperands},
{.name=WS_C("UNICODE-CHAR"), .processor=processUnicodeCharOperands},
// {.name=WS_C("UNKNOWN-CHAR"), .processor=processUnknownCharOperands},
END_DATA_DIRECTIVE_TABLE
return processDirectiveOperand(file, &directives, "gnome braille main directive", data);
}
}
TextTableData *
processGnomeBrailleStream (FILE *stream, const char *name) {
TextTableData *ttd;
inUcsBlock = 0;
if ((ttd = processTextTableLines(stream, name, processGnomeBrailleOperands))) {
if (inUcsBlock) {
reportDataError(NULL, "unterminated UCS block");
}
};
return ttd;
}