/* * 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-2021 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 . */ #include "prologue.h" #include #include "log.h" #include "file.h" #include "datafile.h" #include "dataarea.h" #include "charset.h" #include "ttb.h" #include "ttb_internal.h" #include "ttb_compile.h" struct TextTableDataStruct { DataArea *area; struct { TextTableAliasEntry *array; size_t size; size_t count; } alias; }; void * getTextTableItem (TextTableData *ttd, TextTableOffset offset) { return getDataItem(ttd->area, offset); } TextTableHeader * getTextTableHeader (TextTableData *ttd) { return getTextTableItem(ttd, 0); } static DataOffset getUnicodeGroupOffset (TextTableData *ttd, wchar_t character, int allocate) { unsigned int groupNumber = UNICODE_GROUP_NUMBER(character); DataOffset groupOffset = getTextTableHeader(ttd)->unicodeGroups[groupNumber]; if (!groupOffset && allocate) { if (!allocateDataItem(ttd->area, &groupOffset, sizeof(UnicodeGroupEntry), __alignof__(UnicodeGroupEntry))) { return 0; } getTextTableHeader(ttd)->unicodeGroups[groupNumber] = groupOffset; } return groupOffset; } static DataOffset getUnicodePlaneOffset (TextTableData *ttd, wchar_t character, int allocate) { DataOffset groupOffset = getUnicodeGroupOffset(ttd, character, allocate); if (!groupOffset) return 0; { UnicodeGroupEntry *group = getDataItem(ttd->area, groupOffset); unsigned int planeNumber = UNICODE_PLANE_NUMBER(character); DataOffset planeOffset = group->planes[planeNumber]; if (!planeOffset && allocate) { if (!allocateDataItem(ttd->area, &planeOffset, sizeof(UnicodePlaneEntry), __alignof__(UnicodePlaneEntry))) { return 0; } group = getDataItem(ttd->area, groupOffset); group->planes[planeNumber] = planeOffset; } return planeOffset; } } static DataOffset getUnicodeRowOffset (TextTableData *ttd, wchar_t character, int allocate) { DataOffset planeOffset = getUnicodePlaneOffset(ttd, character, allocate); if (!planeOffset) return 0; { UnicodePlaneEntry *plane = getDataItem(ttd->area, planeOffset); unsigned int rowNumber = UNICODE_ROW_NUMBER(character); DataOffset rowOffset = plane->rows[rowNumber]; if (!rowOffset && allocate) { if (!allocateDataItem(ttd->area, &rowOffset, sizeof(UnicodeRowEntry), __alignof__(UnicodeRowEntry))) { return 0; } plane = getDataItem(ttd->area, planeOffset); plane->rows[rowNumber] = rowOffset; } return rowOffset; } } UnicodeRowEntry * getUnicodeRowEntry (TextTableData *ttd, wchar_t character, int allocate) { DataOffset rowOffset = getUnicodeRowOffset(ttd, character, allocate); if (!rowOffset) return NULL; return getDataItem(ttd->area, rowOffset); } const unsigned char * getUnicodeCell (TextTableData *ttd, wchar_t character) { const UnicodeRowEntry *row = getUnicodeRowEntry(ttd, character, 0); if (row) { unsigned int cellNumber = UNICODE_CELL_NUMBER(character); if (BITMASK_TEST(row->cellDefined, cellNumber)) return &row->cells[cellNumber]; } return NULL; } static void resetTextTableDots (TextTableData *ttd, unsigned char dots, wchar_t character) { TextTableHeader *header = getTextTableHeader(ttd); if (BITMASK_TEST(header->dotsCharacterDefined, dots)) { if (header->dotsToCharacter[dots] == character) { header->dotsToCharacter[dots] = 0; BITMASK_CLEAR(header->dotsCharacterDefined, dots); } } } static void setTextTableDots (TextTableData *ttd, wchar_t character, unsigned char dots) { TextTableHeader *header = getTextTableHeader(ttd); if (!BITMASK_TEST(header->dotsCharacterDefined, dots)) { header->dotsToCharacter[dots] = character; BITMASK_SET(header->dotsCharacterDefined, dots); } } int setTextTableGlyph (TextTableData *ttd, wchar_t character, unsigned char dots) { UnicodeRowEntry *row = getUnicodeRowEntry(ttd, character, 1); if (row) { unsigned int cellNumber = UNICODE_CELL_NUMBER(character); unsigned char *cell = &row->cells[cellNumber]; if (!BITMASK_TEST(row->cellDefined, cellNumber)) { BITMASK_SET(row->cellDefined, cellNumber); } else if (*cell != dots) { resetTextTableDots(ttd, *cell, character); } *cell = dots; return 1; } return 0; } int setTextTableCharacter (TextTableData *ttd, wchar_t character, unsigned char dots) { if (!setTextTableGlyph(ttd, character, dots)) return 0; setTextTableDots(ttd, character, dots); return 1; } void unsetTextTableCharacter (TextTableData *ttd, wchar_t character) { UnicodeRowEntry *row = getUnicodeRowEntry(ttd, character, 0); if (row) { unsigned int cellNumber = UNICODE_CELL_NUMBER(character); if (BITMASK_TEST(row->cellDefined, cellNumber)) { unsigned char *cell = &row->cells[cellNumber]; resetTextTableDots(ttd, *cell, character); *cell = 0; BITMASK_CLEAR(row->cellDefined, cellNumber); } } } int setTextTableByte (TextTableData *ttd, unsigned char byte, unsigned char dots) { wint_t character = convertCharToWchar(byte); if (character != WEOF) if (!setTextTableCharacter(ttd, character, dots)) return 0; return 1; } int addTextTableAlias (TextTableData *ttd, wchar_t from, wchar_t to) { if (ttd->alias.count == ttd->alias.size) { size_t newSize = ttd->alias.size? (ttd->alias.size << 1): 0X10; TextTableAliasEntry *newArray; if (!(newArray = realloc(ttd->alias.array, ARRAY_SIZE(newArray, newSize)))) { logMallocError(); return 0; } ttd->alias.array = newArray; ttd->alias.size = newSize; } { unsigned int cellNumber = UNICODE_CELL_NUMBER(from); UnicodeRowEntry *row = getUnicodeRowEntry(ttd, from, 1); if (!row) return 0; BITMASK_SET(row->cellAliased, cellNumber); } { TextTableAliasEntry *alias = &ttd->alias.array[ttd->alias.count++]; memset(alias, 0, sizeof(*alias)); alias->from = from; alias->to = to; } return 1; } TextTableData * newTextTableData (void) { TextTableData *ttd; if ((ttd = malloc(sizeof(*ttd)))) { memset(ttd, 0, sizeof(*ttd)); ttd->alias.array = NULL; ttd->alias.size = 0; ttd->alias.count = 0; if ((ttd->area = newDataArea())) { if (allocateDataItem(ttd->area, NULL, sizeof(TextTableHeader), __alignof__(TextTableHeader))) { return ttd; } destroyDataArea(ttd->area); } free(ttd); } return NULL; } void destroyTextTableData (TextTableData *ttd) { if (ttd->alias.array) free(ttd->alias.array); destroyDataArea(ttd->area); free(ttd); } static int sortTextTableAliasArray (const void *element1, const void *element2) { const TextTableAliasEntry *alias1 = element1; const TextTableAliasEntry *alias2 = element2; wchar_t wc1 = alias1->from; wchar_t wc2 = alias2->from; if (wc1 < wc2) return -1; if (wc1 > wc2) return 1; return 0; } static int finishTextTableData (TextTableData *ttd) { qsort(ttd->alias.array, ttd->alias.count, sizeof(*ttd->alias.array), sortTextTableAliasArray); { DataOffset offset; if (!saveDataItem(ttd->area, &offset, ttd->alias.array, ARRAY_SIZE(ttd->alias.array, ttd->alias.count), __alignof__(*ttd->alias.array))) { return 0; } { TextTableHeader *header = getTextTableHeader(ttd); header->aliasArray = offset; header->aliasCount = ttd->alias.count; } } return 1; } TextTableData * processTextTableLines (FILE *stream, const char *name, DataOperandsProcessor *processOperands) { if (setTableDataVariables(TEXT_TABLE_EXTENSION, TEXT_SUBTABLE_EXTENSION)) { TextTableData *ttd; if ((ttd = newTextTableData())) { const DataFileParameters parameters = { .processOperands = processOperands, .data = ttd }; if (processDataStream(NULL, stream, name, ¶meters)) { if (finishTextTableData(ttd)) { return ttd; } } destroyTextTableData(ttd); } } return NULL; } TextTable * makeTextTable (TextTableData *ttd) { TextTable *table = malloc(sizeof(*table)); if (table) { memset(table, 0, sizeof(*table)); table->header.fields = getTextTableHeader(ttd); table->size = getDataSize(ttd->area); table->options.tryBaseCharacter = 1; { const unsigned char **cell = &table->cells.replacementCharacter; *cell = getUnicodeCell(ttd, UNICODE_REPLACEMENT_CHARACTER); if (!*cell) *cell = getUnicodeCell(ttd, WC_C('?')); } resetDataArea(ttd->area); } return table; } void destroyTextTable (TextTable *table) { if (table->size) { free(table->header.fields); free(table); } } char * ensureTextTableExtension (const char *path) { return ensureFileExtension(path, TEXT_TABLE_EXTENSION); } char * makeTextTablePath (const char *directory, const char *name) { char *subdirectory = makePath(directory, TEXT_TABLES_SUBDIRECTORY); if (subdirectory) { char *file = makeFilePath(subdirectory, name, TEXT_TABLE_EXTENSION); free(subdirectory); if (file) return file; } return NULL; } static int testTextTable (const char *directory, char *name) { int exists = 0; char *path; if ((path = makeTextTablePath(directory, name))) { logMessage(LOG_DEBUG, "checking for text table: %s", path); if (testFilePath(path)) exists = 1; free(path); } return exists; } char * selectTextTable (const char *directory) { char *locale = getLocaleName(); if (locale) { char name[strlen(locale) + 1]; { size_t length = strcspn(locale, ".@"); strncpy(name, locale, length); name[length] = 0; } free(locale); locale = NULL; if (isPosixLocale(name)) { name[0] = 0; } else if (!testTextTable(directory, name)) { char *delimiter = strchr(name, '_'); if (delimiter) { *delimiter = 0; if (!testTextTable(directory, name)) name[0] = 0; } } if (name[0]) { char *textTableName = strdup(name); if (textTableName) return textTableName; logMallocError(); } } return NULL; }