/* * 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 #include "program.h" #include "options.h" #include "log.h" #include "file.h" #include "parse.h" #include "dynld.h" #include "ktb.h" #include "ktb_keyboard.h" #include "brl.h" static int opt_audit; static int opt_listKeyNames; static int opt_listHelpScreen; static int opt_listRestructuredText; static char *opt_tablesDirectory; char *opt_driversDirectory; BEGIN_OPTION_TABLE(programOptions) { .word = "audit", .letter = 'a', .setting.flag = &opt_audit, .description = strtext("Report problems with the key table.") }, { .word = "keys", .letter = 'k', .setting.flag = &opt_listKeyNames, .description = strtext("List key names.") }, { .word = "list", .letter = 'l', .setting.flag = &opt_listHelpScreen, .description = strtext("List key table in help screen format.") }, { .word = "reStructuredText", .letter = 'r', .setting.flag = &opt_listRestructuredText, .description = strtext("List key table in reStructuredText format.") }, { .word = "tables-directory", .letter = 'T', .flags = OPT_Hidden, .argument = strtext("directory"), .setting.string = &opt_tablesDirectory, .internal.setting = TABLES_DIRECTORY, .internal.adjust = fixInstallPath, .description = strtext("Path to directory containing tables.") }, { .word = "drivers-directory", .letter = 'D', .flags = OPT_Hidden, .argument = strtext("directory"), .setting.string = &opt_driversDirectory, .internal.setting = DRIVERS_DIRECTORY, .internal.adjust = fixInstallPath, .description = strtext("Path to directory for loading drivers.") }, END_OPTION_TABLE static void *driverObject; typedef struct { KEY_NAME_TABLES_REFERENCE names; char *path; } KeyTableDescriptor; static int getKeyTableDescriptor (KeyTableDescriptor *ktd, const char *name) { int ok = 0; int componentsLeft; char **nameComponents = splitString(name, '-', &componentsLeft); memset(ktd, 0, sizeof(*ktd)); ktd->names = NULL; ktd->path = NULL; if (nameComponents) { char **currentComponent = nameComponents; if (componentsLeft) { const char *tableType = (componentsLeft--, *currentComponent++); if (strcmp(tableType, "kbd") == 0) { if (componentsLeft) { const char *keyboardType = (componentsLeft--, *currentComponent++); ktd->names = KEY_NAME_TABLES(keyboard); if ((ktd->path = makeKeyboardTablePath(opt_tablesDirectory, keyboardType))) ok = 1; } else { logMessage(LOG_ERR, "missing keyboard type"); } } else if (strcmp(tableType, "brl") == 0) { if (componentsLeft) { const char *driverCode = (componentsLeft--, *currentComponent++); if (loadBrailleDriver(driverCode, &driverObject, opt_driversDirectory)) { char *keyTablesSymbol; { const char *strings[] = {"brl_ktb_", driverCode}; keyTablesSymbol = joinStrings(strings, ARRAY_COUNT(strings)); } if (keyTablesSymbol) { const KeyTableDefinition *const *keyTableDefinitions; if (findSharedSymbol(driverObject, keyTablesSymbol, &keyTableDefinitions)) { const KeyTableDefinition *const *currentDefinition = keyTableDefinitions; if (componentsLeft) { const char *deviceType = (componentsLeft--, *currentComponent++); while (*currentDefinition) { if (strcmp(deviceType, (*currentDefinition)->bindings) == 0) { ktd->names = (*currentDefinition)->names; if ((ktd->path = makeInputTablePath(opt_tablesDirectory, driverCode, deviceType))) ok = 1; break; } currentDefinition += 1; } if (!ktd->names) { logMessage(LOG_ERR, "unknown braille device type: %s-%s", driverCode, deviceType); } } else { logMessage(LOG_ERR, "missing braille device type"); } } free(keyTablesSymbol); } else { logMallocError(); } } } else { logMessage(LOG_ERR, "missing braille driver code"); } } else { logMessage(LOG_ERR, "unknown key table type: %s", tableType); } } else { logMessage(LOG_ERR, "missing key table type"); } deallocateStrings(nameComponents); } if (ok) { if (componentsLeft) { logMessage(LOG_ERR, "too many key table name components"); ok = 0; } } if (ok) return 1; if (ktd->path) free(ktd->path); memset(ktd, 0, sizeof(*ktd)); return 0; } static int writeLine (const wchar_t *line) { FILE *stream = stdout; fprintf(stream, "%" PRIws "\n", line); return !ferror(stream); } static int hlpWriteLine (const wchar_t *line, void *data) { return writeLine(line); } typedef struct { unsigned int headerLevel; unsigned int elementLevel; wchar_t elementBullet; unsigned blankLine:1; } RestructuredTextData; static int rstAddLine (const wchar_t *line, RestructuredTextData *rst) { if (*line) { rst->blankLine = 0; } else if (rst->blankLine) { return 1; } else { rst->blankLine = 1; } return writeLine(line); } static int rstAddBlankLine (RestructuredTextData *rst) { return rstAddLine(WS_C(""), rst); } static int rstWriteLine (const wchar_t *line, void *data) { RestructuredTextData *rst = data; const unsigned int indent = 2; const unsigned int count = indent * rst->elementLevel; size_t length = wcslen(line); wchar_t buffer[count + length + 1]; if (rst->elementLevel > 0) { wmemset(buffer, WC_C(' '), count); buffer[count - indent] = rst->elementBullet; rst->elementBullet = WC_C(' '); wmemcpy(&buffer[count], line, (length + 1)); line = buffer; } return rstAddLine(line, rst); } static int rstAddHeader (const wchar_t *text, RestructuredTextData *rst) { static const wchar_t characters[] = { WC_C('='), WC_C('-'), WC_C('~') }; int isTitle = rst->headerLevel == 0; size_t length = wcslen(text); wchar_t underline[length + 1]; wmemset(underline, characters[rst->headerLevel], length); underline[length] = 0; if (!rstAddLine(text, rst)) return 0; if (!rstAddLine(underline, rst)) return 0; if (!rstAddBlankLine(rst)) return 0; if (isTitle) { if (!rstAddLine(WS_C(".. contents::"), rst)) return 0; if (!rstAddBlankLine(rst)) return 0; } return 1; } static int rstWriteHeader (const wchar_t *text, unsigned int level, void *data) { RestructuredTextData *rst = data; if (rst->headerLevel < level) { while (++rst->headerLevel < level) { if (!rstAddHeader(WS_C("\\ "), rst)) return 0; } } else if (rst->headerLevel > level) { rst->headerLevel = level; } return rstAddHeader(text, rst); } static int rstBeginElement (unsigned int level, void *data) { RestructuredTextData *rst = data; static const wchar_t bullets[] = { WC_C('*'), WC_C('+'), WC_C('o') }; rst->elementLevel = level; rst->elementBullet = bullets[level - 1]; if (!rstAddBlankLine(rst)) return 0; return 1; } static int rstEndList (void *data) { RestructuredTextData *rst = data; rst->elementLevel = 0; if (!rstAddBlankLine(rst)) return 0; return 1; } static const KeyTableListMethods rstMethods = { .writeHeader = rstWriteHeader, .beginElement = rstBeginElement, .endList = rstEndList }; int main (int argc, char *argv[]) { ProgramExitStatus exitStatus = PROG_EXIT_SUCCESS; { static const OptionsDescriptor descriptor = { OPTION_TABLE(programOptions), .applicationName = "brltty-ktb", .argumentsSummary = "key-table" }; PROCESS_OPTIONS(descriptor, argc, argv); } driverObject = NULL; if (argc) { const char *tableName = (argc--, *argv++); KeyTableDescriptor ktd; int gotKeyTableDescriptor; { const char *file = locatePathName(tableName); const char *delimiter = strrchr(file, '.'); size_t length = delimiter? (delimiter - file): strlen(file); char name[length + 1]; memcpy(name, file, length); name[length] = 0; gotKeyTableDescriptor = getKeyTableDescriptor(&ktd, name); } if (gotKeyTableDescriptor) { if (opt_listKeyNames) { if (!listKeyNames(ktd.names, hlpWriteLine, NULL)) { exitStatus = PROG_EXIT_FATAL; } } if (exitStatus == PROG_EXIT_SUCCESS) { KeyTable *keyTable = compileKeyTable(ktd.path, ktd.names); if (keyTable) { if (opt_audit) { if (!auditKeyTable(keyTable, ktd.path)) { exitStatus = PROG_EXIT_FATAL; } } if (opt_listHelpScreen) { if (!listKeyTable(keyTable, NULL, hlpWriteLine, NULL)) { exitStatus = PROG_EXIT_FATAL; } } if (opt_listRestructuredText) { RestructuredTextData rst = { .headerLevel = 0, .elementLevel = 0, .elementBullet = WC_C(' '), .blankLine = 0 }; if (!listKeyTable(keyTable, &rstMethods, rstWriteLine, &rst)) { exitStatus = PROG_EXIT_FATAL; } } destroyKeyTable(keyTable); } else { exitStatus = PROG_EXIT_FATAL; } } free(ktd.path); } else { exitStatus = PROG_EXIT_FATAL; } } else { logMessage(LOG_ERR, "missing key table name"); exitStatus = PROG_EXIT_SYNTAX; } if (driverObject) unloadSharedObject(driverObject); return exitStatus; } #include "scr.h" KeyTableCommandContext getScreenCommandContext (void) { return KTB_CTX_DEFAULT; } int currentVirtualTerminal (void) { return 0; } #include "alert.h" void alert (AlertIdentifier identifier) { } #include "api_control.h" const ApiMethods api; #include "message.h" int message (const char *mode, const char *text, MessageOptions options) { return 1; }