/* * 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 #include #include "log.h" #include "strfmt.h" #include "program.h" #include "options.h" #include "file.h" #include "get_select.h" #include "brl_dots.h" #include "charset.h" #include "ttb.h" #include "ttb_internal.h" #include "ttb_compile.h" #undef HAVE_UNDEFINED_CHARACTERS_SUPPORT #if defined(__linux__) #define HAVE_UNDEFINED_CHARACTERS_SUPPORT #include #include #include #include static void showUndefinedCharacters (TextTableData *ttd) { char path[0X40]; int console; { const char *vt = getenv("XDG_VTNR"); if (!(vt && *vt)) vt = "0"; snprintf(path, sizeof(path), "/dev/tty%s", vt); } if ((console = open(path, O_RDONLY)) != -1) { struct unimapdesc sfm; unsigned short size = 0X100; while (1) { sfm.entry_ct = size; if (!(sfm.entries = malloc(ARRAY_SIZE(sfm.entries, sfm.entry_ct)))) { logMallocError(); break; } if (ioctl(console, GIO_UNIMAP, &sfm) != -1) break; free(sfm.entries); sfm.entries = NULL; if (errno != ENOMEM) { logSystemError("ioctl[GIO_UNIMAP]"); break; } if (!(size <<= 1)) { logMessage(LOG_ERR, "screen font map too big"); break; } } if (sfm.entries) { const struct unipair *entry = sfm.entries; const struct unipair *end = entry + sfm.entry_ct; while (entry < end) { wchar_t character = entry++->unicode; if (character == UNICODE_REPLACEMENT_CHARACTER) continue; if ((character & ~UNICODE_CELL_MASK) == UNICODE_BRAILLE_ROW) continue; if (!getUnicodeCell(ttd, character)) { const TextTableHeader *header = getTextTableHeader(ttd); const TextTableAliasEntry *alias = locateTextTableAlias( character, getTextTableItem(ttd, header->aliasArray), header->aliasCount ); if (!alias) { char buffer[0X80]; STR_BEGIN(buffer, sizeof(buffer)); STR_PRINTF("undefined character: U+%04"PRIX32, (uint32_t)character); { char name[0X40]; if (getCharacterName(character, name, sizeof(name))) { STR_PRINTF(" [%s]", name); } } STR_END; logMessage(LOG_WARNING, "%s", buffer); } } } free(sfm.entries); } close(console); } else { logMessage(LOG_ERR, "cannot open console: %s: %s", path, strerror(errno)); } } #endif /* undefined characters support */ static char *opt_charset; static char *opt_inputFormat; static char *opt_outputFormat; static char *opt_tablesDirectory; static int opt_edit; #ifdef HAVE_UNDEFINED_CHARACTERS_SUPPORT static int opt_undefined; #endif /* HAVE_UNDEFINED_CHARACTERS_SUPPORT */ BEGIN_OPTION_TABLE(programOptions) { .word = "tables-directory", .letter = 'T', .flags = OPT_Hidden, .argument = "directory", .setting.string = &opt_tablesDirectory, .internal.setting = TABLES_DIRECTORY, .internal.adjust = fixInstallPath, .description = strtext("Path to directory containing text tables.") }, { .word = "edit", .letter = 'e', .setting.flag = &opt_edit, .description = strtext("Edit table.") }, { .word = "input-format", .letter = 'i', .argument = "format", .setting.string = &opt_inputFormat, .description = strtext("Format of input file.") }, { .word = "output-format", .letter = 'o', .argument = "format", .setting.string = &opt_outputFormat, .description = strtext("Format of output file.") }, { .word = "charset", .letter = 'c', .argument = "charset", .setting.string = &opt_charset, .description = strtext("8-bit character set to use.") }, #ifdef HAVE_UNDEFINED_CHARACTERS_SUPPORT { .word = "undefined", .letter = 'u', .setting.flag = &opt_undefined, .description = strtext("Report the characters within the current screen font that aren't defined within the text table.") }, #endif /* HAVE_UNDEFINED_CHARACTERS_SUPPORT */ END_OPTION_TABLE static const BrlDotTable dotsInternal = { BRL_DOT_1, BRL_DOT_2, BRL_DOT_3, BRL_DOT_4, BRL_DOT_5, BRL_DOT_6, BRL_DOT_7, BRL_DOT_8 }; static const BrlDotTable dots12345678 = { 0X01, 0X02, 0X04, 0X08, 0X10, 0X20, 0X40, 0X80 }; static const BrlDotTable dots14253678 = { 0X01, 0X04, 0X10, 0X02, 0X08, 0X20, 0X40, 0X80 }; static unsigned char mapDots (unsigned char input, const BrlDotTable from, const BrlDotTable to) { unsigned char output = 0; { int dot; for (dot=0; dotdotsCharacterDefined, dots)) if (header->dotsToCharacter[dots] == character) return 1; return 0; } static int writeCharacterDescription (FILE *file, wchar_t character) { if (!writeString(file, " ")) return 0; if (!writeUtf8Character(file, ((iswprint(character) && !iswspace(character))? character: WC_C(' ')))) return 0; { char name[0X40]; if (getCharacterName(character, name, sizeof(name))) { if (!writeString(file, " [")) return 0; if (!writeString(file, name)) return 0; if (!writeString(file, "]")) return 0; } } return 1; } static int writeCharacters (FILE *file, TextTableData *ttd, CharacterWriter writer, const void *data) { if (*opt_charset) { unsigned char byte = 0; do { wint_t character = convertCharToWchar(byte); if (character != WEOF) { const unsigned char *cell = getUnicodeCell(ttd, character); if (cell) { int isPrimary = isPrimaryCharacter(ttd, character, *cell); if (!writer(file, character, *cell, &byte, isPrimary, data)) return 0; } } } while ((byte += 1)); } { const TextTableHeader *header = getTextTableHeader(ttd); unsigned int groupNumber; for (groupNumber=0; groupNumberunicodeGroups[groupNumber]; if (groupOffset) { const UnicodeGroupEntry *group = getTextTableItem(ttd, groupOffset); unsigned int planeNumber; for (planeNumber=0; planeNumberplanes[planeNumber]; if (planeOffset) { const UnicodePlaneEntry *plane = getTextTableItem(ttd, planeOffset); unsigned int rowNumber; for (rowNumber=0; rowNumberrows[rowNumber]; if (rowOffset) { const UnicodeRowEntry *row = getTextTableItem(ttd, rowOffset); unsigned int cellNumber; for (cellNumber=0; cellNumbercellDefined, cellNumber)) { wchar_t character = UNICODE_CHARACTER(groupNumber, planeNumber, rowNumber, cellNumber); if (*opt_charset) { if (convertWcharToChar(character) != EOF) { continue; } } { const unsigned char *cell = &row->cells[cellNumber]; int isPrimary = isPrimaryCharacter(ttd, character, *cell); if (!writer(file, character, *cell, NULL, isPrimary, data)) return 0; } } } } } } } } } } return 1; } static int writeAliases (FILE *file, TextTableData *ttd, AliasWriter writer, const void *data) { const TextTableHeader *header = getTextTableHeader(ttd); const TextTableAliasEntry *alias = getTextTableItem(ttd, header->aliasArray); const TextTableAliasEntry *end = alias + header->aliasCount; while (alias < end) { if (!writer(file, alias, data)) return 0; alias += 1; } return 1; } static TextTableData * readTable_native (const char *path, FILE *file, const void *data) { return processTextTableStream(file, path); } static int writeDots_native (FILE *file, unsigned char dots) { unsigned char dot; if (fprintf(file, "(") == EOF) return 0; for (dot=0X01; dot; dot<<=1) { char number = (dots & dot)? brlDotToNumber(dot): ' '; if (fprintf(file, "%c", number) == EOF) return 0; } if (fprintf(file, ")") == EOF) return 0; return 1; } static int writeCharacter_native ( FILE *file, wchar_t character, unsigned char dots, const unsigned char *byte, int isPrimary, const void *data ) { if (fprintf(file, "%s\t", (isPrimary? "char": "glyph")) == EOF) return 0; if (!writeHexadecimalCharacter(file, character)) return 0; if (fprintf(file, "\t") == EOF) return 0; if (!writeDots_native(file, dots)) return 0; if (fprintf(file, "\t#") == EOF) return 0; if (*opt_charset) { const int width = 2; char buffer[width + 1]; if (byte) { snprintf(buffer, sizeof(buffer), "%0*X", width, *byte); } else { snprintf(buffer, sizeof(buffer), "%*s", width, ""); } if (fprintf(file, " %s", buffer) == EOF) return 0; } if (fprintf(file, " ") == EOF) return 0; if (!writeUtf8Cell(file, dots)) return 0; if (!writeCharacterDescription(file, character)) return 0; if (fprintf(file, "\n") == EOF) return 0; return 1; } static int writeAlias_native (FILE *file, const TextTableAliasEntry *alias, const void *data) { if (fprintf(file, "alias\t") == EOF) return 0; if (!writeHexadecimalCharacter(file, alias->from)) return 0; if (fprintf(file, "\t") == EOF) return 0; if (!writeHexadecimalCharacter(file, alias->to)) return 0; if (fprintf(file, "\t#") == EOF) return 0; if (!writeCharacterDescription(file, alias->from)) return 0; if (fprintf(file, "\n") == EOF) return 0; return 1; } static int writeTable_native ( const char *path, FILE *file, TextTableData *ttd, const void *data ) { if (!writeHeaderComment(file, writeHashComment)) return 0; if (!writeCharacters(file, ttd, writeCharacter_native, data)) return 0; if (!writeAliases(file, ttd, writeAlias_native, data)) return 0; return 1; } static TextTableData * readTable_binary (const char *path, FILE *file, const void *data) { TextTableData *ttd; if ((ttd = newTextTableData())) { int count = 0X100; int byte; for (byte=0; byte 0X20) && (character < 0X7F) && (character != '#')) { wint_t value = character; if (fprintf(file, "%" PRIwc, value) == EOF) return 0; } else { unsigned long int value = character; int digits = (value < (1 << 16))? 4: (value < (1 << 20))? 5: 8; int format = (value < (1 << 16))? 'x': (value < (1 << 20))? 'y': 'z'; if (fprintf(file, "\\%c%0*lx", format, digits, value) == EOF) return 0; } if (fprintf(file, "\t") == EOF) return 0; if (!dots) { if (fprintf(file, "0") == EOF) return 0; } else { if (!writeDots(file, dots)) return 0; } { char name[0X40]; if (getCharacterName(character, name, sizeof(name))) { if (fprintf(file, "\t%s", name) == EOF) return 0; } } if (fprintf(file, "\n") == EOF) return 0; return 1; } static int writeTable_libLouis ( const char *path, FILE *file, TextTableData *ttd, const void *data ) { if (!writeHeaderComment(file, writeHashComment)) goto error; if (!writeCharacters(file, ttd, writeCharacter_libLouis, data)) goto error; return 1; error: return 0; } static int writeCharacterDots_XCompose (FILE *file, unsigned char dots) { if (fprintf(file, "") == EOF) return 0; return 1; } static int writeCharacterOutput_XCompose (FILE *file, wchar_t character) { switch (character) { case WC_C('\n'): if (fprintf(file, "\\n") == EOF) return 0; break; case WC_C('\r'): if (fprintf(file, "\\r") == EOF) return 0; break; case WC_C('"'): if (fprintf(file, "\\\"") == EOF) return 0; break; case WC_C('\\'): if (fprintf(file, "\\\\") == EOF) return 0; break; default: if (fprintf(file, "%lc", (wint_t)character) == EOF) return 0; break; } return 1; } static int writeCharacter_XCompose ( FILE *file, wchar_t character, unsigned char dots, const unsigned char *byte, int isPrimary, const void *_data ) { if (isPrimary) { if (!writeCharacterDots_XCompose (file, dots)) return 0; if (fprintf(file, " : \"") == EOF) return 0; if (!writeCharacterOutput_XCompose(file, character)) return 0; if (fprintf(file, "\"\n") == EOF) return 0; } return 1; } static int writeTable_XCompose ( const char *path, FILE *file, TextTableData *ttd, const void *data ) { if (!writeHeaderComment(file, writeHashComment)) goto error; if (!writeCharacters(file, ttd, writeCharacter_XCompose, NULL)) goto error; return 1; error: return 0; } static int writeCharacter_half_XCompose ( FILE *file, wchar_t character, unsigned char leftDots, unsigned char rightDots ) { if (!writeCharacterDots_XCompose (file, leftDots)) return 0; if (fprintf(file, " ") == EOF) return 0; if (!writeCharacterDots_XCompose (file, rightDots)) return 0; if (fprintf(file, " : \"") == EOF) return 0; if (!writeCharacterOutput_XCompose(file, character)) return 0; if (fprintf(file, "\"\n") == EOF) return 0; return 1; } static int writeCharacter_leftrighthalf_XCompose ( FILE *file, wchar_t character, unsigned char dots, const unsigned char *byte, int isPrimary, const void *_data ) { if (isPrimary) { unsigned char leftDots = brlGetLeftDots(dots); unsigned char rightDots = brlGetRightDots(dots); if (!writeCharacter_half_XCompose(file, character, leftDots, rightDots)) return 0; if (!leftDots) { /* Also add shortcut without blank pattern for left part. */ if (!writeCharacterDots_XCompose (file, rightDots)) return 0; if (fprintf(file, " : \"") == EOF) return 0; if (!writeCharacterOutput_XCompose(file, character)) return 0; if (fprintf(file, "\"\n") == EOF) return 0; } } return 1; } static int writeTable_leftrighthalf_XCompose ( const char *path, FILE *file, TextTableData *ttd, const void *data ) { if (!writeHeaderComment(file, writeHashComment)) goto error; if (!writeCharacters(file, ttd, writeCharacter_leftrighthalf_XCompose, NULL)) goto error; return 1; error: return 0; } static int writeCharacter_lefthalf_XCompose ( FILE *file, wchar_t character, unsigned char dots, const unsigned char *byte, int isPrimary, const void *_data ) { if (isPrimary) { unsigned char leftDots = brlGetLeftDots(dots); unsigned char rightDots = brlGetRightDotsToLeftDots(dots); if (!writeCharacter_half_XCompose(file, character, leftDots, rightDots)) return 0; } return 1; } static int writeTable_lefthalf_XCompose ( const char *path, FILE *file, TextTableData *ttd, const void *data ) { if (!writeHeaderComment(file, writeHashComment)) goto error; if (!writeCharacters(file, ttd, writeCharacter_lefthalf_XCompose, NULL)) goto error; return 1; error: return 0; } static int writeCharacter_lefthalfalt_XCompose ( FILE *file, wchar_t character, unsigned char dots, const unsigned char *byte, int isPrimary, const void *_data ) { if (isPrimary) { unsigned char leftDots = brlGetLeftDots(dots); unsigned char rightDots = brlGetRightDotsToLeftDotsAlt(dots); if (!writeCharacter_half_XCompose(file, character, leftDots, rightDots)) return 0; } return 1; } static int writeTable_lefthalfalt_XCompose ( const char *path, FILE *file, TextTableData *ttd, const void *data ) { if (!writeHeaderComment(file, writeHashComment)) goto error; if (!writeCharacters(file, ttd, writeCharacter_lefthalfalt_XCompose, NULL)) goto error; return 1; error: return 0; } static int writeCharacter_righthalf_XCompose ( FILE *file, wchar_t character, unsigned char dots, const unsigned char *byte, int isPrimary, const void *_data ) { if (isPrimary) { unsigned char leftDots = brlGetLeftDotsToRightDots(dots); unsigned char rightDots = brlGetRightDots(dots); if (!writeCharacter_half_XCompose(file, character, leftDots, rightDots)) return 0; } return 1; } static int writeTable_righthalf_XCompose ( const char *path, FILE *file, TextTableData *ttd, const void *data ) { if (!writeHeaderComment(file, writeHashComment)) goto error; if (!writeCharacters(file, ttd, writeCharacter_righthalf_XCompose, NULL)) goto error; return 1; error: return 0; } static int writeCharacter_righthalfalt_XCompose ( FILE *file, wchar_t character, unsigned char dots, const unsigned char *byte, int isPrimary, const void *_data ) { if (isPrimary) { unsigned char leftDots = brlGetLeftDotsToRightDotsAlt(dots); unsigned char rightDots = brlGetRightDots(dots); if (!writeCharacter_half_XCompose(file, character, leftDots, rightDots)) return 0; } return 1; } static int writeTable_righthalfalt_XCompose ( const char *path, FILE *file, TextTableData *ttd, const void *data ) { if (!writeHeaderComment(file, writeHashComment)) goto error; if (!writeCharacters(file, ttd, writeCharacter_righthalfalt_XCompose, NULL)) goto error; return 1; error: return 0; } static int writeCharacter_JAWS ( FILE *file, wchar_t character, unsigned char dots, const unsigned char *byte, int isPrimary, const void *data ) { uint32_t value = character; if (fprintf(file, "U+%04" PRIX32 "=", value) == EOF) return 0; if (!writeDots(file, dots)) return 0; if (fprintf(file, "\n") == EOF) return 0; return 1; } static int writeTable_JAWS ( const char *path, FILE *file, TextTableData *ttd, const void *data ) { if (!writeHeaderComment(file, writeSemicolonComment)) return 0; if (!writeCharacters(file, ttd, writeCharacter_JAWS, NULL)) return 0; return 1; } static int writeCharacterValue_CPreprocessor (FILE *file, wchar_t character) { return fprintf(file, "0X%08" PRIX32, (uint32_t)character) != EOF; } static int writeCharacterName_CPreprocessor (FILE *file, wchar_t character) { char name[0X40]; if (getCharacterName(character, name, sizeof(name))) { if (!writeString(file, "\"")) return 0; if (!writeString(file, name)) return 0; if (!writeString(file, "\"")) return 0; } else { if (!beginCMacro(file, "BRLTTY_TEXT_TABLE_NO_NAME")) return 0; if (!writeCharacterValue_CPreprocessor(file, character)) return 0; if (!endCMacro(file)) return 0; } return 1; } static int writeCharacter_CPreprocessor ( FILE *file, wchar_t character, unsigned char dots, const unsigned char *byte, int isPrimary, const void *data ) { if (!beginCMacro(file, "BRLTTY_TEXT_TABLE_CHARACTER")) return 0; if (!writeCharacterValue_CPreprocessor(file, character)) return 0; if (!nextCArgument(file)) return 0; if (fprintf(file, "0X%02" PRIX8, dots) == EOF) return 0; if (!nextCArgument(file)) return 0; if (fprintf(file, "%d", isPrimary) == EOF) return 0; if (!nextCArgument(file)) return 0; if (!writeCharacterName_CPreprocessor(file, character)) return 0; if (!endCMacro(file)) return 0; if (!endLine(file)) return 0; return 1; } static int writeAlias_CPreprocessor (FILE *file, const TextTableAliasEntry *alias, const void *data) { if (!beginCMacro(file, "BRLTTY_TEXT_TABLE_ALIAS")) return 0; if (!writeCharacterValue_CPreprocessor(file, alias->from)) return 0; if (!nextCArgument(file)) return 0; if (!writeCharacterValue_CPreprocessor(file, alias->to)) return 0; if (!nextCArgument(file)) return 0; if (!writeCharacterName_CPreprocessor(file, alias->from)) return 0; if (!endCMacro(file)) return 0; if (!endLine(file)) return 0; return 1; } static int writeTable_CPreprocessor ( const char *path, FILE *file, TextTableData *ttd, const void *data ) { if (!writeHeaderComment(file, writeCComment)) return 0; if (!endLine(file)) return 0; if (!defineCMacro(file, "BRLTTY_TEXT_TABLE_BEGIN_CHARACTERS", "")) return 0; if (!defineCMacro(file, "BRLTTY_TEXT_TABLE_CHARACTER", "(unicode, braille, isPrimary, name)")) return 0; if (!defineCMacro(file, "BRLTTY_TEXT_TABLE_END_CHARACTERS", "")) return 0; if (!defineCMacro(file, "BRLTTY_TEXT_TABLE_BEGIN_ALIASES", "")) return 0; if (!defineCMacro(file, "BRLTTY_TEXT_TABLE_ALIAS", "(from, to, name)")) return 0; if (!defineCMacro(file, "BRLTTY_TEXT_TABLE_END_ALIASES", "")) return 0; if (!defineCMacro(file, "BRLTTY_TEXT_TABLE_NO_NAME", "(character)")) return 0; if (fprintf(file, "BRLTTY_TEXT_TABLE_BEGIN_CHARACTERS\n") == EOF) return 0; if (!writeCharacters(file, ttd, writeCharacter_CPreprocessor, NULL)) return 0; if (fprintf(file, "BRLTTY_TEXT_TABLE_END_CHARACTERS\n") == EOF) return 0; if (!endLine(file)) return 0; if (fprintf(file, "BRLTTY_TEXT_TABLE_BEGIN_ALIASES\n") == EOF) return 0; if (!writeAliases(file, ttd, writeAlias_CPreprocessor, data)) return 0; if (fprintf(file, "BRLTTY_TEXT_TABLE_END_ALIASES\n") == EOF) return 0; if (!endLine(file)) return 0; return 1; } typedef struct { const char *name; TableReader *read; TableWriter *write; const void *data; } FormatEntry; static const FormatEntry formatEntries[] = { { .name = "ttb", .read = readTable_native, .write = writeTable_native, }, { .name = "a2b", .read = readTable_binary, .write = writeTable_binary, .data = &dots12345678, }, { .name = "sbl", .read = readTable_binary, .write = writeTable_binary, .data = &dots14253678, }, #ifdef HAVE_ICONV_H { .name = "gnb", .read = readTable_gnome, .write = writeTable_gnome, }, #endif /* HAVE_ICONV_H */ { .name = "ctb", .read = readTable_libLouis, .write = writeTable_libLouis, }, { .name = "utb", .read = readTable_libLouis, .write = writeTable_libLouis, }, { .name = "XCompose", .write = writeTable_XCompose, }, { .name = "half-XCompose", .write = writeTable_leftrighthalf_XCompose, }, { .name = "lefthalf-XCompose", .write = writeTable_lefthalf_XCompose, }, { .name = "lefthalfalt-XCompose", .write = writeTable_lefthalfalt_XCompose, }, { .name = "righthalf-XCompose", .write = writeTable_righthalf_XCompose, }, { .name = "righthalfalt-XCompose", .write = writeTable_righthalfalt_XCompose, }, { .name = "jbt", .write = writeTable_JAWS, }, { .name = "cpp", .write = writeTable_CPreprocessor, }, { .name = NULL } }; static const FormatEntry * findFormatEntry (const char *name) { const FormatEntry *format = formatEntries; while (format->name) { if (strcmp(name, format->name) == 0) return format; format += 1; } return NULL; } static const FormatEntry * getFormatEntry (const char *name, const char *path, const char *description) { if (!(name && *name)) { name = locatePathExtension(path); if (!(name && *++name)) { logMessage(LOG_ERR, "unspecified %s format.", description); exit(PROG_EXIT_SYNTAX); } } { const FormatEntry *format = findFormatEntry(name); if (format) return format; } logMessage(LOG_ERR, "unknown %s format: %s", description, name); exit(PROG_EXIT_SYNTAX); } static const char *inputPath; static const char *outputPath; static const FormatEntry *inputFormat; static const FormatEntry *outputFormat; static FILE * openTable (const char **file, const char *mode, const char *directory, FILE *stdStream, const char *stdName) { if (stdStream) { if (strcmp(*file, standardStreamArgument) == 0) { *file = stdName; return stdStream; } } if (directory) { const char *path = makeTextTablePath(directory, *file); if (!path) return NULL; *file = path; } { FILE *stream = fopen(*file, mode); if (!stream) logMessage(LOG_ERR, "table open error: %s: %s", *file, strerror(errno)); return stream; } } static FILE * openInputTable (const char **path, int allowStandardInput) { FILE *stdStream; const char *stdName; if (allowStandardInput) { stdStream = stdin; stdName = standardInputName; } else { stdStream = NULL; stdName = NULL; } return openTable(path, "r", opt_tablesDirectory, stdStream, stdName); } static FILE * openOutputTable (const char **path) { return openTable(path, "w", NULL, stdout, standardOutputName); } static TextTableData * readTable (const char *path, FILE *file, const FormatEntry *fmt) { if (fmt->read) return fmt->read(path, file, fmt->data); logMessage(LOG_ERR, "reading not supported: %s", fmt->name); return NULL; } static ProgramExitStatus convertTable (void) { ProgramExitStatus exitStatus; FILE *inputFile = openInputTable(&inputPath, 1); if (inputFile) { TextTableData *ttd; if ((ttd = readTable(inputPath, inputFile, inputFormat))) { #ifdef HAVE_UNDEFINED_CHARACTERS_SUPPORT if (opt_undefined) { showUndefinedCharacters(ttd); } #endif /* HAVE_UNDEFINED_CHARACTERS_SUPPORT */ if (outputPath) { FILE *outputFile = openOutputTable(&outputPath); if (outputFile) { if (outputFormat->write(outputPath, outputFile, ttd, outputFormat->data)) { exitStatus = PROG_EXIT_SUCCESS; } else { exitStatus = PROG_EXIT_FATAL; } fclose(outputFile); } else { exitStatus = PROG_EXIT_FATAL; } } else { exitStatus = PROG_EXIT_SUCCESS; } destroyTextTableData(ttd); } else { exitStatus = PROG_EXIT_FATAL; } fclose(inputFile); } else { exitStatus = PROG_EXIT_FATAL; } return exitStatus; } #include "get_curses.h" #ifndef GOT_CURSES #define refresh() fflush(stdout) #define printw printf #define erase() printf("\r\n\v") #define beep() printf("\a") static int inputAttributesChanged; #if defined(__MINGW32__) #define STDIN_HANDLE ((HANDLE)_get_osfhandle(STDIN_FILENO)) static DWORD inputConsoleMode; #undef beep #define beep() MessageBeep(MB_ICONWARNING) #else /* termios */ #include static struct termios inputTerminalAttributes; #ifndef _POSIX_VDISABLE #define _POSIX_VDISABLE 0 #endif /* _POSIX_VDISABLE */ #endif /* input terminal definitions */ #endif /* GOT_CURSES */ #ifdef ENABLE_API #define BRLAPI_NO_DEPRECATED #include "brlapi.h" #endif /* ENABLE_API */ typedef struct { TextTableData *ttd; unsigned updated:1; const char *charset; union { wchar_t unicode; unsigned char byte; } character; #ifdef ENABLE_API brlapi_fileDescriptor brlapiFileDescriptor; const char *brlapiErrorFunction; const char *brlapiErrorMessage; #endif /* ENABLE_API */ unsigned int displayWidth; unsigned int displayHeight; } EditTableData; #ifdef ENABLE_API static int haveBrailleDisplay (EditTableData *etd) { return etd->brlapiFileDescriptor != (brlapi_fileDescriptor)-1; } static void setBrlapiError (EditTableData *etd, const char *function) { if ((etd->brlapiErrorFunction = function)) { etd->brlapiErrorMessage = brlapi_strerror(&brlapi_error); } else { etd->brlapiErrorMessage = NULL; } } static void releaseBrailleDisplay (EditTableData *etd) { brlapi_closeConnection(); etd->brlapiFileDescriptor = (brlapi_fileDescriptor)-1; } static int claimBrailleDisplay (EditTableData *etd) { etd->brlapiFileDescriptor = brlapi_openConnection(NULL, NULL); if (haveBrailleDisplay(etd)) { if (brlapi_getDisplaySize(&etd->displayWidth, &etd->displayHeight) != -1) { if (brlapi_enterTtyMode(BRLAPI_TTY_DEFAULT, NULL) != -1) { setBrlapiError(etd, NULL); return 1; } else { setBrlapiError(etd, "brlapi_enterTtyMode"); } } else { setBrlapiError(etd, "brlapi_getDisplaySize"); } releaseBrailleDisplay(etd); } else { setBrlapiError(etd, "brlapi_openConnection"); } return 0; } #endif /* ENABLE_API */ static int getCharacter (EditTableData *etd, wchar_t *character) { if (etd->charset) { wint_t wc = convertCharToWchar(etd->character.byte); if (wc == WEOF) return 0; *character = wc; } else { *character = etd->character.unicode; } return 1; } static wchar_t * makeCharacterDescription (TextTableData *ttd, wchar_t character, size_t *length, int *defined, unsigned char *braille) { char buffer[0X100]; size_t characterIndex; size_t brailleIndex; size_t descriptionLength; unsigned char dots = 0; int gotDots = getDots(ttd, character, &dots); wchar_t printableCharacter; char printablePrefix; printableCharacter = character; if (iswLatin1(printableCharacter)) { printablePrefix = '^'; if (!(printableCharacter & 0X60)) { printableCharacter |= 0X40; if (printableCharacter & 0X80) printablePrefix = '~'; } else if (printableCharacter == 0X7F) { printableCharacter ^= 0X40; } else if (printableCharacter != printablePrefix) { printablePrefix = ' '; } } else { printablePrefix = ' '; } if (!gotDots) dots = 0; #define DOT(n) ((dots & BRL_DOT_##n)? ((n) + '0'): ' ') { uint32_t value = character; STR_BEGIN(buffer, sizeof(buffer)); STR_PRINTF("%04" PRIX32 " %c", value, printablePrefix); characterIndex = STR_LENGTH; STR_PRINTF("x "); brailleIndex = STR_LENGTH; STR_PRINTF("x %c%c%c%c%c%c%c%c%c%c", (gotDots? '[': ' '), DOT(1), DOT(2), DOT(3), DOT(4), DOT(5), DOT(6), DOT(7), DOT(8), (gotDots? ']': ' ')); descriptionLength = STR_LENGTH; STR_END; } #undef DOT { char *name = buffer + descriptionLength + 1; int size = buffer + sizeof(buffer) - name; if (getCharacterName(character, name, size)) { descriptionLength += strlen(name) + 1; *--name = ' '; } } { wchar_t *description = calloc(descriptionLength+1, sizeof(*description)); if (description) { unsigned int i; for (i=0; i length) printw("%*s", width-length, ""); } static int updateCharacterDescription (EditTableData *etd) { int ok = 0; wchar_t character = UNICODE_REPLACEMENT_CHARACTER; int gotCharacter = getCharacter(etd, &character); size_t length; int gotDots; unsigned char dots; wchar_t *description = makeCharacterDescription(etd->ttd, character, &length, &gotDots, &dots); if (description) { ok = 1; erase(); #if defined(GOT_CURSES) #define KEY_FIRST_ACTUAL_CHARACTER "" #define KEY_LAST_ACTUAL_CHARACTER "" #define KEY_PREVIOUS_ACTUAL_CHARACTER "Left" #define KEY_NEXT_ACTUAL_CHARACTER "Right" #define KEY_FIRST_DEFINED_CHARACTER "Home" #define KEY_LAST_DEFINED_CHARACTER "End" #define KEY_PREVIOUS_DEFINED_CHARACTER "Up" #define KEY_NEXT_DEFINED_CHARACTER "Down" #define KEY_TOGGLE_DOT1 "F4" #define KEY_TOGGLE_DOT2 "F3" #define KEY_TOGGLE_DOT3 "F2" #define KEY_TOGGLE_DOT4 "F5" #define KEY_TOGGLE_DOT5 "F6" #define KEY_TOGGLE_DOT6 "F7" #define KEY_TOGGLE_DOT7 "F1" #define KEY_TOGGLE_DOT8 "F8" #define KEY_TOGGLE_CHARACTER "F9" #define KEY_ALTERNATE_CHARACTER "F10" #define KEY_SAVE_TABLE "F11" #define KEY_EXIT_EDITOR "F12" #else /* standard input/output */ #define KEY_FIRST_ACTUAL_CHARACTER "^S" #define KEY_LAST_ACTUAL_CHARACTER "^G" #define KEY_PREVIOUS_ACTUAL_CHARACTER "^D" #define KEY_NEXT_ACTUAL_CHARACTER "^F" #define KEY_FIRST_DEFINED_CHARACTER "^H" #define KEY_LAST_DEFINED_CHARACTER "^L" #define KEY_PREVIOUS_DEFINED_CHARACTER "^J" #define KEY_NEXT_DEFINED_CHARACTER "^K" #define KEY_TOGGLE_DOT1 "^R" #define KEY_TOGGLE_DOT2 "^E" #define KEY_TOGGLE_DOT3 "^W" #define KEY_TOGGLE_DOT4 "^U" #define KEY_TOGGLE_DOT5 "^I" #define KEY_TOGGLE_DOT6 "^O" #define KEY_TOGGLE_DOT7 "^Q" #define KEY_TOGGLE_DOT8 "^P" #define KEY_TOGGLE_CHARACTER "^T" #define KEY_ALTERNATE_CHARACTER "^Y" #define KEY_SAVE_TABLE "^A" #define KEY_EXIT_EDITOR "^Z" #endif /* key definitions */ { const char *first = "first"; const char *last = "last"; const char *previous = "prev"; const char *next = "next"; const char *actual = etd->charset? etd->charset: "unicode"; const char *defined = "defined"; const char *character = "char"; const int width = 38; printNavigationPair(KEY_PREVIOUS_ACTUAL_CHARACTER, previous, KEY_NEXT_ACTUAL_CHARACTER, next, actual, character, width); printNavigationPair(KEY_PREVIOUS_DEFINED_CHARACTER, previous, KEY_NEXT_DEFINED_CHARACTER, next, defined, character, 0); printw("\n"); printNavigationPair(KEY_FIRST_ACTUAL_CHARACTER, first, KEY_LAST_ACTUAL_CHARACTER, last, actual, character, width); printNavigationPair(KEY_FIRST_DEFINED_CHARACTER, first, KEY_LAST_DEFINED_CHARACTER, last, defined, character, 0); printw("\n"); } printw("\n"); #define DOT(n) printw("%s: %s dot %u ", KEY_TOGGLE_DOT##n, ((dots & BRL_DOT_##n)? "lower": "raise"), n) DOT(1); DOT(4); printw("%s: %s", KEY_TOGGLE_CHARACTER, !gotCharacter? "": !gotDots? "define character (empty cell)": dots? "clear all dots": "undefine character"); printw("\n"); DOT(2); DOT(5); printw("%s:", KEY_ALTERNATE_CHARACTER); { const char *lower = "lowercase"; const char *upper = "uppercase"; const char *alternate = NULL; if (etd->charset) { if (isupper(etd->character.byte)) { alternate = lower; } else if (islower(etd->character.byte)) { alternate = upper; } } else { if (iswupper(etd->character.unicode)) { alternate = lower; } else if (iswlower(etd->character.unicode)) { alternate = upper; } } if (alternate) printw(" switch to %s equivalent", alternate); } printw("\n"); DOT(3); DOT(6); printw("%s: %s", KEY_SAVE_TABLE, etd->updated? "save table": ""); printw("\n"); DOT(7); DOT(8); printw("%s: exit table editor", KEY_EXIT_EDITOR); if (etd->updated) printw(" (unsaved changes)"); printw("\n"); #undef DOT printw("\n"); if (etd->charset) { printw("%02X: %s\n", etd->character.byte, etd->charset); } printCharacterString(description); printw("\n"); #define DOT(n) ((dots & BRL_DOT_##n)? '#': ' ') printw(" +---+ \n"); printw("1|%c %c|4\n", DOT(1), DOT(4)); printw("2|%c %c|5\n", DOT(2), DOT(5)); printw("3|%c %c|6\n", DOT(3), DOT(6)); printw("7|%c %c|8\n", DOT(7), DOT(8)); printw(" +---+ \n"); #undef DOT #ifdef ENABLE_API if (etd->brlapiErrorFunction) { printw("BrlAPI error: %s: %s\n", etd->brlapiErrorFunction, etd->brlapiErrorMessage); setBrlapiError(etd, NULL); } #endif /* ENABLE_API */ refresh(); #ifdef ENABLE_API if (haveBrailleDisplay(etd)) { brlapi_writeArguments_t args = BRLAPI_WRITEARGUMENTS_INITIALIZER; wchar_t text[etd->displayWidth]; { size_t count = MIN(etd->displayWidth, length); wmemcpy(text, description, count); wmemset(&text[count], WC_C(' '), etd->displayWidth-count); } args.regionBegin = 1; args.regionSize = etd->displayWidth; args.text = (char *)text; args.textSize = etd->displayWidth * sizeof(text[0]); args.charset = "WCHAR_T"; if (brlapi_write(&args) == -1) { setBrlapiError(etd, "brlapi_write"); releaseBrailleDisplay(etd); } } #endif /* ENABLE_API */ free(description); } return ok; } static void setPreviousActualCharacter (EditTableData *etd) { if (etd->charset) { etd->character.byte = (etd->character.byte - 1) & CHARSET_BYTE_MAXIMUM; } else { etd->character.unicode = (etd->character.unicode - 1) & WCHAR_MAX; } } static void setNextActualCharacter (EditTableData *etd) { if (etd->charset) { etd->character.byte = (etd->character.byte + 1) & CHARSET_BYTE_MAXIMUM; } else { etd->character.unicode = (etd->character.unicode + 1) & WCHAR_MAX; } } static void setFirstActualCharacter (EditTableData *etd) { if (etd->charset) { etd->character.byte = 0; } else { etd->character.unicode = 0; } } static void setLastActualCharacter (EditTableData *etd) { if (etd->charset) { etd->character.byte = CHARSET_BYTE_MAXIMUM; } else { etd->character.unicode = WCHAR_MAX; } } static int findCharacter (EditTableData *etd, int backward) { const int increment = backward? -1: 1; if (etd->charset) { const int byteLimit = backward? 0: CHARSET_BYTE_MAXIMUM; const int byteReset = CHARSET_BYTE_MAXIMUM - byteLimit - increment; unsigned char byte = etd->character.byte; int counter = CHARSET_BYTE_COUNT; do { if (byte == byteLimit) byte = byteReset; byte += increment; { wint_t wc = convertCharToWchar(byte); if (wc != WEOF) { if (getUnicodeCell(etd->ttd, wc)) { etd->character.byte = byte; return 1; } } } } while ((counter -= 1) >= 0); } else { const int groupLimit = backward? 0: UNICODE_GROUP_MAXIMUM; const int planeLimit = backward? 0: UNICODE_PLANE_MAXIMUM; const int rowLimit = backward? 0: UNICODE_ROW_MAXIMUM; const int cellLimit = backward? 0: UNICODE_CELL_MAXIMUM; const int groupReset = UNICODE_GROUP_MAXIMUM - groupLimit; const int planeReset = UNICODE_PLANE_MAXIMUM - planeLimit; const int rowReset = UNICODE_ROW_MAXIMUM - rowLimit; const int cellReset = UNICODE_CELL_MAXIMUM - cellLimit - increment; int groupNumber = UNICODE_GROUP_NUMBER(etd->character.unicode); int planeNumber = UNICODE_PLANE_NUMBER(etd->character.unicode); int rowNumber = UNICODE_ROW_NUMBER(etd->character.unicode); int cellNumber = UNICODE_CELL_NUMBER(etd->character.unicode); const TextTableHeader *header = getTextTableHeader(etd->ttd); int groupCounter = UNICODE_GROUP_COUNT; do { TextTableOffset groupOffset = header->unicodeGroups[groupNumber]; if (groupOffset) { const UnicodeGroupEntry *group = getTextTableItem(etd->ttd, groupOffset); while (1) { TextTableOffset planeOffset = group->planes[planeNumber]; if (planeOffset) { const UnicodePlaneEntry *plane = getTextTableItem(etd->ttd, planeOffset); while (1) { TextTableOffset rowOffset = plane->rows[rowNumber]; if (rowOffset) { const UnicodeRowEntry *row = getTextTableItem(etd->ttd, rowOffset); while (cellNumber != cellLimit) { cellNumber += increment; if (BITMASK_TEST(row->cellDefined, cellNumber)) { etd->character.unicode = UNICODE_CHARACTER(groupNumber, planeNumber, rowNumber, cellNumber); return 1; } } } cellNumber = cellReset; if (rowNumber == rowLimit) break; rowNumber += increment; } } rowNumber = rowReset; cellNumber = cellReset; if (planeNumber == planeLimit) break; planeNumber += increment; } } planeNumber = planeReset; rowNumber = rowReset; cellNumber = cellReset; if (groupNumber == groupLimit) { groupNumber = groupReset; } else { groupNumber += increment; } } while ((groupCounter -= 1) >= 0); } return 0; } static int setPreviousDefinedCharacter (EditTableData *etd) { return findCharacter(etd, 1); } static int setNextDefinedCharacter (EditTableData *etd) { return findCharacter(etd, 0); } static int setFirstDefinedCharacter (EditTableData *etd) { setLastActualCharacter(etd); if (setNextDefinedCharacter(etd)) return 1; setFirstActualCharacter(etd); return 0; } static int setLastDefinedCharacter (EditTableData *etd) { setFirstActualCharacter(etd); if (setPreviousDefinedCharacter(etd)) return 1; setLastActualCharacter(etd); return 0; } static int setAlternateCharacter (EditTableData *etd) { if (etd->charset) { if (isalpha(etd->character.byte)) { if (islower(etd->character.byte)) { etd->character.byte = toupper(etd->character.byte); return 1; } if (isupper(etd->character.byte)) { etd->character.byte = tolower(etd->character.byte); return 1; } } } else { if (iswalpha(etd->character.unicode)) { if (iswlower(etd->character.unicode)) { etd->character.unicode = towupper(etd->character.unicode); return 1; } if (iswupper(etd->character.unicode)) { etd->character.unicode = towlower(etd->character.unicode); return 1; } } } return 0; } static int toggleCharacter (EditTableData *etd) { wchar_t character; if (!getCharacter(etd, &character)) return 0; { const unsigned char *cell = getUnicodeCell(etd->ttd, character); if (cell && !*cell) { unsetTextTableCharacter(etd->ttd, character); } else if (!setTextTableCharacter(etd->ttd, character, 0)) { return 0; } } etd->updated = 1; return 1; } static int toggleDot (EditTableData *etd, unsigned char dot) { wchar_t character; if (getCharacter(etd, &character)) { const unsigned char *cell = getUnicodeCell(etd->ttd, character); unsigned char dots = cell? *cell: 0; if (setTextTableCharacter(etd->ttd, character, dots^dot)) { etd->updated = 1; return 1; } } return 0; } static int setDots (EditTableData *etd, unsigned char dots) { wchar_t character; if (getCharacter(etd, &character)) { if (setTextTableCharacter(etd->ttd, character, dots)) { etd->updated = 1; return 1; } } return 0; } static int saveTable (EditTableData *etd) { int ok = 0; FILE *outputFile; if (!outputPath) outputPath = inputPath; if (!outputFormat) outputFormat = inputFormat; if ((outputFile = openOutputTable(&outputPath))) { if (outputFormat->write(outputPath, outputFile, etd->ttd, outputFormat->data)) { ok = 1; etd->updated = 0; } fclose(outputFile); } return ok; } static int doKeyboardCommand (EditTableData *etd) { #undef IS_UNICODE_CHARACTER #if defined(GOT_CURSES) #ifdef GOT_CURSES_GET_WCH #define IS_UNICODE_CHARACTER wint_t ch; int ret = get_wch(&ch); if (ret == KEY_CODE_YES) #else /* GOT_CURSES_GET_WCH */ int ch = getch(); if (ch >= 0X100) #endif /* GOT_CURSES_GET_WCH */ { switch (ch) { case KEY_LEFT: setPreviousActualCharacter(etd); break; case KEY_RIGHT: setNextActualCharacter(etd); break; case KEY_UP: if (!setPreviousDefinedCharacter(etd)) beep(); break; case KEY_DOWN: if (!setNextDefinedCharacter(etd)) beep(); break; case KEY_HOME: if (!setFirstDefinedCharacter(etd)) beep(); break; case KEY_END: if (!setLastDefinedCharacter(etd)) beep(); break; case KEY_F(1): if (!toggleDot(etd, BRL_DOT_7)) beep(); break; case KEY_F(2): if (!toggleDot(etd, BRL_DOT_3)) beep(); break; case KEY_F(3): if (!toggleDot(etd, BRL_DOT_2)) beep(); break; case KEY_F(4): if (!toggleDot(etd, BRL_DOT_1)) beep(); break; case KEY_F(5): if (!toggleDot(etd, BRL_DOT_4)) beep(); break; case KEY_F(6): if (!toggleDot(etd, BRL_DOT_5)) beep(); break; case KEY_F(7): if (!toggleDot(etd, BRL_DOT_6)) beep(); break; case KEY_F(8): if (!toggleDot(etd, BRL_DOT_8)) beep(); break; case KEY_F(9): if (!toggleCharacter(etd)) beep(); break; case KEY_F(10): if (!setAlternateCharacter(etd)) beep(); break; case KEY_F(11): if (!(etd->updated && saveTable(etd))) beep(); break; case KEY_F(12): return 0; default: beep(); break; } } else #ifdef GOT_CURSES_GET_WCH if (ret == OK) #endif /* GOT_CURSES_GET_WCH */ #else /* standard input/output */ int handled = 1; #ifdef __MSDOS__ int ch = fgetc(stdin); if (ch == EOF) return 0; #else /* __MSDOS__ */ #define IS_UNICODE_CHARACTER wint_t ch = fgetwc(stdin); if (ch == WEOF) return 0; #endif /* __MSDOS__ */ switch (ch) { case 0X1B: /* escape */ return 0; case 0X11: /* CTRL-Q */ if (!toggleDot(etd, BRL_DOT_7)) beep(); break; case 0X17: /* CTRL-W */ if (!toggleDot(etd, BRL_DOT_3)) beep(); break; case 0X05: /* CTRL-E */ if (!toggleDot(etd, BRL_DOT_2)) beep(); break; case 0X12: /* CTRL-R */ if (!toggleDot(etd, BRL_DOT_1)) beep(); break; case 0X14: /* CTRL-T */ if (!toggleCharacter(etd)) beep(); break; case 0X19: /* CTRL-Y */ if (!setAlternateCharacter(etd)) beep(); break; case 0X15: /* CTRL-U */ if (!toggleDot(etd, BRL_DOT_4)) beep(); break; case 0X09: /* CTRL-I */ if (!toggleDot(etd, BRL_DOT_5)) beep(); break; case 0X0F: /* CTRL-O */ if (!toggleDot(etd, BRL_DOT_6)) beep(); break; case 0X10: /* CTRL-P */ if (!toggleDot(etd, BRL_DOT_8)) beep(); break; case 0X01: /* CTRL-A */ if (!(etd->updated && saveTable(etd))) beep(); break; case 0X13: /* CTRL-S */ setFirstActualCharacter(etd); break; case 0X04: /* CTRL-D */ setPreviousActualCharacter(etd); break; case 0X06: /* CTRL-F */ setNextActualCharacter(etd); break; case 0X07: /* CTRL-G */ setLastActualCharacter(etd); break; case 0X08: /* CTRL-H */ if (!setFirstDefinedCharacter(etd)) beep(); break; case 0X0A: /* CTRL-J */ if (!setPreviousDefinedCharacter(etd)) beep(); break; case 0X0B: /* CTRL-K */ if (!setNextDefinedCharacter(etd)) beep(); break; case 0X0C: /* CTRL-L */ if (!setLastDefinedCharacter(etd)) beep(); break; case 0X1A: /* CTRL-Z */ return 0; case 0X18: /* CTRL-X */ beep(); break; case 0X03: /* CTRL-C */ beep(); break; case 0X16: /* CTRL-V */ beep(); break; case 0X02: /* CTRL-B */ beep(); break; case 0X0E: /* CTRL-N */ beep(); break; case 0X0D: /* CTRL-M */ beep(); break; default: handled = 0; break; } if (!handled) #endif /* read character */ { wint_t character; #ifdef IS_UNICODE_CHARACTER character = ch; #else /* IS_UNICODE_CHARACTER */ character = convertCharToWchar(ch); #endif /* IS_UNICODE_CHARACTER */ if ((character >= UNICODE_BRAILLE_ROW) && (character <= (UNICODE_BRAILLE_ROW | UNICODE_CELL_MASK))) { if (!setDots(etd, character & UNICODE_CELL_MASK)) beep(); } else { if (etd->charset) { int c; #ifdef IS_UNICODE_CHARACTER c = convertWcharToChar(ch); #else /* IS_UNICODE_CHARACTER */ c = ch; #endif /* IS_UNICODE_CHARACTER */ if (c != EOF) { etd->character.byte = c; } else { beep(); } } else if (character != WEOF) { etd->character.unicode = character; } else { beep(); } } } return 1; } #ifndef __MINGW32__ #ifdef ENABLE_API static int doBrailleCommand (EditTableData *etd) { if (haveBrailleDisplay(etd)) { brlapi_keyCode_t key; int ret = brlapi_readKey(0, &key); if (ret == 1) { unsigned long code = key & BRLAPI_KEY_CODE_MASK; switch (key & BRLAPI_KEY_TYPE_MASK) { case BRLAPI_KEY_TYPE_CMD: switch (code & BRLAPI_KEY_CMD_BLK_MASK) { case 0: switch (code) { case BRLAPI_KEY_CMD_FWINLT: setPreviousActualCharacter(etd); break; case BRLAPI_KEY_CMD_FWINRT: setNextActualCharacter(etd); break; case BRLAPI_KEY_CMD_LNUP: if (!setPreviousDefinedCharacter(etd)) beep(); break; case BRLAPI_KEY_CMD_LNDN: if (!setNextDefinedCharacter(etd)) beep(); break; case BRLAPI_KEY_CMD_TOP_LEFT: case BRLAPI_KEY_CMD_TOP: if (!setFirstDefinedCharacter(etd)) beep(); break; case BRLAPI_KEY_CMD_BOT_LEFT: case BRLAPI_KEY_CMD_BOT: if (!setLastDefinedCharacter(etd)) beep(); break; default: beep(); break; } break; case BRLAPI_KEY_CMD_PASSDOTS: if (!setDots(etd, code & BRLAPI_KEY_CMD_ARG_MASK)) beep(); break; default: beep(); break; } break; case BRLAPI_KEY_TYPE_SYM: { /* latin1 */ if (code < 0X100) code |= BRLAPI_KEY_SYM_UNICODE; if ((code & 0X1f000000) == BRLAPI_KEY_SYM_UNICODE) { /* unicode */ if ((code & 0Xffff00) == UNICODE_BRAILLE_ROW) { /* Set braille pattern */ if (!setDots(etd, code & UNICODE_CELL_MASK)) beep(); } else { wchar_t character = code & 0XFFFFFF; if (etd->charset) { int c = convertWcharToChar(character); if (c != EOF) { etd->character.byte = c; } else { beep(); } } else { etd->character.unicode = character; } } } else { switch (code) { case BRLAPI_KEY_SYM_LEFT: setPreviousActualCharacter(etd); break; case BRLAPI_KEY_SYM_RIGHT: setNextActualCharacter(etd); break; case BRLAPI_KEY_SYM_UP: if (!setPreviousDefinedCharacter(etd)) beep(); break; case BRLAPI_KEY_SYM_DOWN: if (!setNextDefinedCharacter(etd)) beep(); break; case BRLAPI_KEY_SYM_HOME: if (!setFirstDefinedCharacter(etd)) beep(); break; case BRLAPI_KEY_SYM_END: if (!setLastDefinedCharacter(etd)) beep(); break; default: beep(); break; } } break; } default: beep(); break; } } else if (ret == -1) { setBrlapiError(etd, "brlapi_readKey"); releaseBrailleDisplay(etd); } } return 1; } #endif /* ENABLE_API */ #endif /* __MINGW32__ */ static ProgramExitStatus editTable (void) { ProgramExitStatus exitStatus; EditTableData etd; etd.ttd = NULL; etd.updated = 0; { FILE *inputFile = openInputTable(&inputPath, 0); if (inputFile) { if ((etd.ttd = readTable(inputPath, inputFile, inputFormat))) { exitStatus = PROG_EXIT_SUCCESS; } else { exitStatus = PROG_EXIT_FATAL; } fclose(inputFile); } else { exitStatus = PROG_EXIT_FATAL; } } if (exitStatus == PROG_EXIT_SUCCESS) { #ifdef ENABLE_API claimBrailleDisplay(&etd); #endif /* ENABLE_API */ #if defined(GOT_CURSES) initscr(); cbreak(); keypad(stdscr, TRUE); noecho(); nonl(); intrflush(stdscr, FALSE); #else /* standard input/output */ setvbuf(stdin, NULL, _IONBF, 0); inputAttributesChanged = 0; #if defined(__MINGW32__) if (GetConsoleMode(STDIN_HANDLE, &inputConsoleMode)) { DWORD newConsoleMode = inputConsoleMode; newConsoleMode &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT); if (SetConsoleMode(STDIN_HANDLE, newConsoleMode)) { inputAttributesChanged = 1; } } #else /* termios */ if (tcgetattr(STDIN_FILENO, &inputTerminalAttributes) != -1) { struct termios newAttributes = inputTerminalAttributes; newAttributes.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); newAttributes.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); newAttributes.c_cflag &= ~(CSIZE | PARENB); newAttributes.c_cflag |= CS8; { int i; for (i=0; i maximumFileDescriptor) { maximumFileDescriptor = etd.brlapiFileDescriptor; } } #endif /* ENABLE_API */ select(maximumFileDescriptor+1, &set, NULL, NULL, NULL); } #ifdef ENABLE_API if (haveBrailleDisplay(&etd) && FD_ISSET(etd.brlapiFileDescriptor, &set)) { if (!doBrailleCommand(&etd)) break; } #endif /* ENABLE_API */ if (FD_ISSET(STDIN_FILENO, &set)) #endif /* __MINGW32__ */ { if (!doKeyboardCommand(&etd)) break; } } erase(); refresh(); #if defined(GOT_CURSES) endwin(); #else /* standard input/output */ if (inputAttributesChanged) { #if defined(__MINGW32__) SetConsoleMode(STDIN_HANDLE, inputConsoleMode); #else /* termios */ tcsetattr(STDIN_FILENO, TCSAFLUSH, &inputTerminalAttributes); #endif /* input terminal restoration */ } #endif /* restore keyboard and screen */ #ifdef ENABLE_API if (haveBrailleDisplay(&etd)) releaseBrailleDisplay(&etd); #endif /* ENABLE_API */ if (etd.ttd) destroyTextTableData(etd.ttd); } return exitStatus; } int main (int argc, char *argv[]) { ProgramExitStatus exitStatus; { static const OptionsDescriptor descriptor = { OPTION_TABLE(programOptions), .applicationName = "brltty-ttb", .argumentsSummary = "input-table [output-table]" }; PROCESS_OPTIONS(descriptor, argc, argv); } if (argc == 0) { logMessage(LOG_ERR, "missing input table"); return PROG_EXIT_SYNTAX; } inputPath = *argv++, argc--; if (argc > 0) { outputPath = *argv++, argc--; } else if (opt_outputFormat && *opt_outputFormat) { const char *extension = locatePathExtension(inputPath); int prefix = extension? (extension - inputPath): strlen(inputPath); char buffer[prefix + 1 + strlen(opt_outputFormat) + 1]; snprintf(buffer, sizeof(buffer), "%.*s.%s", prefix, inputPath, opt_outputFormat); if (!(outputPath = strdup(buffer))) { logMallocError(); return PROG_EXIT_FATAL; } } else { outputPath = NULL; } if (argc > 0) { logMessage(LOG_ERR, "too many parameters"); return PROG_EXIT_SYNTAX; } inputFormat = getFormatEntry(opt_inputFormat, inputPath, "input"); if (outputPath) { outputFormat = getFormatEntry(opt_outputFormat, outputPath, "output"); } else { outputFormat = NULL; } if (*opt_charset && !setCharset(opt_charset)) { logMessage(LOG_ERR, "cannot establish character set: %s", opt_charset); return PROG_EXIT_SEMANTIC; } if (opt_edit) { exitStatus = editTable(); } else { exitStatus = convertTable(); } return exitStatus; }