| /* |
| * 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 <stdio.h> |
| #include <string.h> |
| |
| #include "program.h" |
| #include "cmdline.h" |
| #include "log.h" |
| #include "file.h" |
| #include "parse.h" |
| #include "dynld.h" |
| #include "ktb.h" |
| #include "ktb_keyboard.h" |
| #include "brl.h" |
| |
| static char *opt_brailleDriver; |
| 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 = "braille-driver", |
| .letter = 'b', |
| .argument = strtext("driver"), |
| .setting.string = &opt_brailleDriver, |
| .description = strtext("Braille driver code."), |
| }, |
| |
| { .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', |
| .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', |
| .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(programOptions) |
| |
| static void *driverObject; |
| |
| typedef struct { |
| KEY_NAME_TABLES_REFERENCE names; |
| char *path; |
| } KeyTableDescriptor; |
| |
| static int |
| getKeyTableDescriptor (KeyTableDescriptor *ktd, const char *tableName) { |
| int ok = 0; |
| |
| memset(ktd, 0, sizeof(*ktd)); |
| ktd->names = NULL; |
| ktd->path = NULL; |
| |
| if (*opt_brailleDriver) { |
| if (loadBrailleDriver(opt_brailleDriver, &driverObject, opt_driversDirectory)) { |
| char *keyTablesSymbol; |
| |
| { |
| const char *strings[] = {"brl_ktb_", opt_brailleDriver}; |
| keyTablesSymbol = joinStrings(strings, ARRAY_COUNT(strings)); |
| } |
| |
| if (keyTablesSymbol) { |
| const KeyTableDefinition *const *keyTableDefinitions; |
| |
| if (findSharedSymbol(driverObject, keyTablesSymbol, &keyTableDefinitions)) { |
| const KeyTableDefinition *const *currentDefinition = keyTableDefinitions; |
| |
| while (*currentDefinition) { |
| if (strcmp(tableName, (*currentDefinition)->bindings) == 0) { |
| ktd->names = (*currentDefinition)->names; |
| if ((ktd->path = makeInputTablePath(opt_tablesDirectory, opt_brailleDriver, tableName))) ok = 1; |
| break; |
| } |
| |
| currentDefinition += 1; |
| } |
| |
| if (!ktd->names) { |
| logMessage(LOG_ERR, |
| "unknown braille device model: %s-%s", |
| opt_brailleDriver, tableName |
| ); |
| } |
| } |
| |
| free(keyTablesSymbol); |
| } else { |
| logMallocError(); |
| } |
| } |
| } else { |
| ktd->names = KEY_NAME_TABLES(keyboard); |
| if ((ktd->path = makeKeyboardTablePath(opt_tablesDirectory, tableName))) ok = 1; |
| } |
| |
| if (!ok) { |
| if (ktd->path) free(ktd->path); |
| memset(ktd, 0, sizeof(*ktd)); |
| } |
| |
| return ok; |
| } |
| |
| 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 |
| rstWriteHeader (const wchar_t *text, unsigned int level, void *data) { |
| RestructuredTextData *rst = data; |
| |
| if (level > (rst->headerLevel + 1)) { |
| level = rst->headerLevel + 1; |
| } else { |
| rst->headerLevel = level; |
| } |
| |
| static const wchar_t characters[] = { |
| WC_C('='), WC_C('-'), WC_C('~') |
| }; |
| |
| size_t length = wcslen(text); |
| wchar_t underline[length + 1]; |
| |
| wmemset(underline, characters[level], length); |
| underline[length] = 0; |
| |
| if (!rstAddLine(text, rst)) return 0; |
| if (!rstAddLine(underline, rst)) return 0; |
| if (!rstAddBlankLine(rst)) return 0; |
| |
| if (level == 0) { |
| if (!rstAddLine(WS_C(".. contents::"), rst)) return 0; |
| if (!rstAddBlankLine(rst)) return 0; |
| } |
| |
| return 1; |
| } |
| |
| 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; |
| |
| { |
| const CommandLineDescriptor descriptor = { |
| .options = &programOptions, |
| .applicationName = "brltty-ktb", |
| |
| .usage = { |
| .purpose = strtext("check a key table, list the key naems it can use, or write the key bindings it defines in useful formats."), |
| .parameters = "table-name", |
| } |
| }; |
| |
| 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) { |
| } |
| |
| void |
| speakAlertText (const wchar_t *text) { |
| } |
| |
| #include "api_control.h" |
| |
| const ApiMethods api; |
| |
| #include "message.h" |
| |
| int |
| message (const char *mode, const char *text, MessageOptions options) { |
| return 1; |
| } |