| /* |
| * 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 <ctype.h> |
| #include <errno.h> |
| |
| #include "log.h" |
| #include "strfmt.h" |
| #include "program.h" |
| #include "cmdline.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 <fcntl.h> |
| #include <sys/stat.h> |
| #include <sys/ioctl.h> |
| #include <linux/kd.h> |
| |
| 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 = "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 */ |
| |
| { .word = "tables-directory", |
| .letter = 'T', |
| .argument = "directory", |
| .setting.string = &opt_tablesDirectory, |
| .internal.setting = TABLES_DIRECTORY, |
| .internal.adjust = fixInstallPath, |
| .description = strtext("Path to directory containing text tables.") |
| }, |
| END_OPTION_TABLE(programOptions) |
| |
| 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; |
| { |
| for (int dot=0; dot<BRL_DOT_COUNT; dot+=1) { |
| if (input & from[dot]) output |= to[dot]; |
| } |
| } |
| return output; |
| } |
| |
| typedef TextTableData *TableReader (const char *path, FILE *file, const void *data); |
| typedef int TableWriter (const char *path, FILE *file, TextTableData *ttd, const void *data); |
| typedef int CharacterWriter (FILE *file, const char *directive, wchar_t character, unsigned char dots); |
| typedef int AliasWriter (FILE *file, const TextTableAliasEntry *alias, const void *data); |
| |
| static int |
| getDots (TextTableData *ttd, wchar_t character, unsigned char *dots) { |
| const unsigned char *cell = getUnicodeCell(ttd, character); |
| if (!cell) return 0; |
| *dots = *cell; |
| return 1; |
| } |
| |
| static int |
| writeString (FILE *file, const char *string) { |
| if (fputs(string, file) != EOF) return 1; |
| logSystemError("output"); |
| return 0; |
| } |
| |
| static int |
| endLine (FILE *file) { |
| return writeString(file, "\n"); |
| } |
| |
| typedef int CommentWriter (FILE *file, const char *text); |
| |
| static int |
| writeHashComment (FILE *file, const char *text) { |
| if (!writeString(file, "# ")) return 0; |
| if (!writeString(file, text)) return 0; |
| if (!endLine(file)) return 0; |
| return 1; |
| } |
| |
| static int |
| writeSemicolonComment (FILE *file, const char *text) { |
| if (!writeString(file, "; ")) return 0; |
| if (!writeString(file, text)) return 0; |
| if (!endLine(file)) return 0; |
| return 1; |
| } |
| |
| static int |
| writeCComment (FILE *file, const char *text) { |
| if (!writeString(file, "/* ")) return 0; |
| if (!writeString(file, text)) return 0; |
| if (!writeString(file, " */")) return 0; |
| if (!endLine(file)) return 0; |
| return 1; |
| } |
| |
| static int |
| defineCMacro (FILE *file, const char *name, const char *args) { |
| if (!writeString(file, "#ifndef ")) return 0; |
| if (!writeString(file, name)) return 0; |
| if (!endLine(file)) return 0; |
| |
| if (!writeString(file, "#define ")) return 0; |
| if (!writeString(file, name)) return 0; |
| if (!writeString(file, args)) return 0; |
| if (!endLine(file)) return 0; |
| |
| if (!writeString(file, "#endif")) return 0; |
| if (!endLine(file)) return 0; |
| |
| if (!endLine(file)) return 0; |
| return 1; |
| } |
| |
| static int |
| beginCMacro (FILE *file, const char *name) { |
| if (!writeString(file, name)) return 0; |
| if (!writeString(file, "(")) return 0; |
| return 1; |
| } |
| |
| static int |
| endCMacro (FILE *file) { |
| if (!writeString(file, ")")) return 0; |
| return 1; |
| } |
| |
| static int |
| nextCArgument (FILE *file) { |
| return fprintf(file, ", ") != EOF; |
| } |
| |
| static int |
| writeHeaderComment (FILE *file, CommentWriter *writeComment) { |
| char buffer[0X80]; |
| |
| STR_BEGIN(buffer, sizeof(buffer)); |
| STR_PRINTF("generated by %s", programName); |
| if (*opt_charset) STR_PRINTF(": charset=%s", opt_charset); |
| STR_END; |
| |
| return writeComment(file, buffer); |
| } |
| |
| 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; |
| } |
| |
| typedef struct { |
| wchar_t character; |
| unsigned char dots; |
| } InputCharacterEntry; |
| |
| static int |
| sortInputCharacterEntries (const void *element1, const void *element2) { |
| const InputCharacterEntry *entry1 = element1; |
| const InputCharacterEntry *entry2 = element2; |
| |
| if (entry1->character < entry2->character) return -1; |
| if (entry1->character > entry2->character) return 1; |
| return 0; |
| } |
| |
| typedef struct { |
| const char *in; |
| const char *out; |
| const char *inOut; |
| } WriteCharacterDirectives; |
| |
| #define WRITE_CHARACTER_DIRECTIVES(...) &(const WriteCharacterDirectives){__VA_ARGS__} |
| #define WRITE_CHARACTERS_IN_ONLY WRITE_CHARACTER_DIRECTIVES(.in="") |
| #define WRITE_CHARACTERS_OUT_ONLY WRITE_CHARACTER_DIRECTIVES(.out="") |
| #define WRITE_CHARACTERS_NATIVE WRITE_CHARACTER_DIRECTIVES(.in="input", .out="glyph", .inOut="char") |
| #define WRITE_CHARACTERS_LIBLOUIS WRITE_CHARACTER_DIRECTIVES(.in="nofor", .out="noback", .inOut="") |
| |
| static int |
| writeInputCharacters ( |
| FILE *file, const wchar_t *character, |
| const InputCharacterEntry **entry, |
| const InputCharacterEntry *const *end, |
| CharacterWriter *writer, const char *directive |
| ) { |
| while (*entry < *end) { |
| if (character) { |
| if ((*entry)->character >= *character) { |
| break; |
| } |
| } |
| |
| if (directive) { |
| if (!writer(file, directive, (*entry)->character, (*entry)->dots)) { |
| return 0; |
| } |
| } |
| |
| *entry += 1; |
| } |
| |
| return 1; |
| } |
| |
| static int |
| writeCharacters (FILE *file, TextTableData *ttd, CharacterWriter writer, const void *data) { |
| const WriteCharacterDirectives *directives = data; |
| const TextTableHeader *header = getTextTableHeader(ttd); |
| |
| InputCharacterEntry inputCharactersBuffer[0X100]; |
| unsigned int inputCharacterCount = 0; |
| |
| for (unsigned int dots=0; dots<0X100; dots+=1) { |
| if (BITMASK_TEST(header->inputCharacterDefined, dots)) { |
| InputCharacterEntry *entry = &inputCharactersBuffer[inputCharacterCount++]; |
| entry->character = header->inputCharacters[dots]; |
| entry->dots = dots; |
| } |
| } |
| |
| qsort( |
| inputCharactersBuffer, inputCharacterCount, |
| sizeof(inputCharactersBuffer[0]), |
| sortInputCharacterEntries |
| ); |
| |
| const InputCharacterEntry *inputCharacterEntry = inputCharactersBuffer; |
| const InputCharacterEntry *const inputCharacterEnd = inputCharacterEntry + inputCharacterCount; |
| |
| for (unsigned int groupNumber=0; groupNumber<UNICODE_GROUP_COUNT; groupNumber+=1) { |
| TextTableOffset groupOffset = header->unicodeGroups[groupNumber]; |
| |
| if (groupOffset) { |
| const UnicodeGroupEntry *group = getTextTableItem(ttd, groupOffset); |
| |
| for (unsigned int planeNumber=0; planeNumber<UNICODE_PLANES_PER_GROUP; planeNumber+=1) { |
| TextTableOffset planeOffset = group->planes[planeNumber]; |
| |
| if (planeOffset) { |
| const UnicodePlaneEntry *plane = getTextTableItem(ttd, planeOffset); |
| |
| for (unsigned int rowNumber=0; rowNumber<UNICODE_ROWS_PER_PLANE; rowNumber+=1) { |
| TextTableOffset rowOffset = plane->rows[rowNumber]; |
| |
| if (rowOffset) { |
| const UnicodeRowEntry *row = getTextTableItem(ttd, rowOffset); |
| |
| for (unsigned int cellNumber=0; cellNumber<UNICODE_CELLS_PER_ROW; cellNumber+=1) { |
| if (BITMASK_TEST(row->cellDefined, cellNumber)) { |
| wchar_t character = UNICODE_CHARACTER(groupNumber, planeNumber, rowNumber, cellNumber); |
| unsigned char dots = row->cells[cellNumber]; |
| |
| { |
| int ok = writeInputCharacters( |
| file, &character, |
| &inputCharacterEntry, &inputCharacterEnd, |
| writer, directives->in |
| ); |
| |
| if (!ok) return 0; |
| } |
| |
| if (inputCharacterEntry < inputCharacterEnd) { |
| if (character == inputCharacterEntry->character) { |
| if (dots == inputCharacterEntry->dots) { |
| const char *directive = directives->inOut; |
| |
| if (directive) { |
| if (!writer(file, directive, character, dots)) { |
| return 0; |
| } |
| |
| inputCharacterEntry += 1; |
| continue; |
| } |
| } |
| } |
| } |
| |
| { |
| const char *directive = directives->out; |
| |
| if (directive) { |
| if (!writer(file, directive, character, dots)) { |
| return 0; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| return writeInputCharacters( |
| file, NULL, |
| &inputCharacterEntry, &inputCharacterEnd, |
| writer, directives->in |
| ); |
| } |
| |
| 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) { |
| if (fprintf(file, "(") == EOF) return 0; |
| |
| for (unsigned char 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, const char *directive, |
| wchar_t character, unsigned char dots |
| ) { |
| if (fprintf(file, "%s\t", directive) == 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 (fprintf(file, " ") == EOF) return 0; |
| if (!writeUtf8Cell(file, dots)) return 0; |
| |
| if (!writeCharacterDescription(file, character)) return 0; |
| return endLine(file); |
| } |
| |
| 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; |
| return endLine(file); |
| } |
| |
| 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<count; byte+=1) { |
| int dots = fgetc(file); |
| |
| if (dots == EOF) { |
| if (ferror(file)) { |
| logMessage(LOG_ERR, "input error: %s: %s", path, strerror(errno)); |
| } else { |
| logMessage(LOG_ERR, "table too short: %s", path); |
| } |
| |
| break; |
| } |
| |
| if (data) dots = mapDots(dots, data, dotsInternal); |
| if (!setTextTableByte(ttd, byte, dots)) break; |
| } |
| |
| if (byte == count) return ttd; |
| destroyTextTableData(ttd); |
| } |
| |
| return NULL; |
| } |
| |
| static int |
| writeTable_binary ( |
| const char *path, FILE *file, TextTableData *ttd, const void *data |
| ) { |
| for (unsigned int byte=0; byte<0X100; byte+=1) { |
| unsigned char dots; |
| |
| { |
| wint_t character = convertCharToWchar(byte); |
| if (character == WEOF) character = UNICODE_REPLACEMENT_CHARACTER; |
| |
| if (!getDots(ttd, character, &dots)) { |
| dots = 0; |
| } |
| } |
| |
| if (data) dots = mapDots(dots, dotsInternal, data); |
| if (fputc(dots, file) == EOF) { |
| logMessage(LOG_ERR, "output error: %s: %s", path, strerror(errno)); |
| return 0; |
| } |
| } |
| |
| return 1; |
| } |
| |
| static TextTableData * |
| readTable_LibLouis (const char *path, FILE *file, const void *data) { |
| return processLibLouisStream(file, path); |
| } |
| |
| static const char * |
| getCharacterType_LibLouis (wchar_t character) { |
| if (iswspace(character)) return "space"; |
| |
| if (iswlower(character)) return "lowercase"; |
| if (iswupper(character)) return "uppercase"; |
| if (iswalpha(character)) return "letter"; |
| |
| if (iswdigit(character)) return "digit"; |
| if (iswxdigit(character)) return "digit"; |
| |
| if (iswpunct(character)) return "punctuation"; |
| return NULL; |
| } |
| |
| static int |
| writeCharacter_LibLouis ( |
| FILE *file, const char *directive, |
| wchar_t character, unsigned char dots |
| ) { |
| const char *type = getCharacterType_LibLouis(character); |
| if (!type) return 1; |
| |
| if (*directive) { |
| if (fprintf(file, "%s ", directive) == EOF) { |
| return 0; |
| } |
| } |
| |
| if (fprintf(file, "%s\t", type) == EOF) return 0; |
| |
| if (character == WC_C('\\')) { |
| if (!writeString(file, "\\\\")) return 0; |
| } else if (character == WC_C('\f')) { |
| if (!writeString(file, "\\f")) return 0; |
| } else if (character == WC_C('\n')) { |
| if (!writeString(file, "\\n")) return 0; |
| } else if (character == WC_C('\r')) { |
| if (!writeString(file, "\\r")) return 0; |
| } else if (character == WC_C(' ')) { |
| if (!writeString(file, "\\s")) return 0; |
| } else if (character == WC_C('\t')) { |
| if (!writeString(file, "\\t")) return 0; |
| } else if (character == WC_C('\v')) { |
| if (!writeString(file, "\\v")) return 0; |
| } else if ((character > 0X20) && (character < 0X7F) && (character != '#')) { |
| wint_t value = character; |
| if (fprintf(file, "%" PRIwc, value) == EOF) return 0; |
| } else { |
| unsigned long int value = character; |
| int digits; |
| char format; |
| |
| if (value < (1 << 16)) { |
| digits = 4; |
| format = 'x'; |
| } else if (value < (1 << 20)) { |
| digits = 5; |
| format = 'y'; |
| } else { |
| digits = 8; |
| format = '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; |
| } |
| } |
| |
| return endLine(file); |
| } |
| |
| static int |
| writeTable_LibLouis ( |
| const char *path, FILE *file, TextTableData *ttd, const void *data |
| ) { |
| if (!writeHeaderComment(file, writeHashComment)) return 0; |
| if (!writeCharacters(file, ttd, writeCharacter_LibLouis, data)) return 0; |
| return 1; |
| } |
| |
| #ifdef HAVE_ICONV_H |
| static TextTableData * |
| readTable_gnome (const char *path, FILE *file, const void *data) { |
| return processGnomeBrailleStream(file, path); |
| } |
| |
| static int |
| writeCharacter_gnome ( |
| FILE *file, const char *directive, |
| wchar_t character, unsigned char dots |
| ) { |
| wchar_t pattern = UNICODE_BRAILLE_ROW | dots; |
| |
| if (iswprint(character) && !iswspace(character)) { |
| Utf8Buffer utf8Character; |
| if (!convertWcharToUtf8(character, utf8Character)) return 0; |
| |
| Utf8Buffer utf8Pattern; |
| if (!convertWcharToUtf8(pattern, utf8Pattern)) return 0; |
| |
| if (fprintf(file, "UCS-CHAR %s %s", utf8Character, utf8Pattern) == EOF) return 0; |
| } else { |
| uint32_t c = character; |
| uint32_t p = pattern; |
| if (fprintf(file, "UNICODE-CHAR U+%02" PRIx32 " U+%04" PRIx32, c, p) == EOF) return 0; |
| } |
| |
| { |
| char name[0X40]; |
| |
| if (getCharacterName(character, name, sizeof(name))) { |
| if (fprintf(file, " # %s", name) == EOF) return 0; |
| } |
| } |
| |
| return endLine(file); |
| } |
| |
| static int |
| writeTable_gnome ( |
| const char *path, FILE *file, TextTableData *ttd, const void *data |
| ) { |
| /* TODO UNKNOWN-CHAR %wc all */ |
| if (fprintf(file, "ENCODING UTF-8\n") == EOF) return 0; |
| if (!writeHeaderComment(file, writeHashComment)) return 0; |
| if (!writeCharacters(file, ttd, writeCharacter_gnome, data)) return 0; |
| return 1; |
| } |
| #endif /* HAVE_ICONV_H */ |
| |
| static int |
| writeCharacterDots_XCompose (FILE *file, unsigned char dots) { |
| if (fprintf(file, "<braille_") == EOF) return 0; |
| |
| if (!dots) { |
| if (fprintf(file, "blank") == EOF) return 0; |
| } else { |
| if (fprintf(file, "dots_") == EOF) return 0; |
| if (!writeDots(file, dots)) return 0; |
| } |
| |
| if (fprintf(file, ">") == EOF) return 0; |
| |
| return 1; |
| } |
| |
| static int |
| writeCharacterOutput_XCompose (FILE *file, wchar_t character) { |
| if (fprintf(file, " : \"") == EOF) return 0; |
| |
| 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; |
| } |
| |
| if (fprintf(file, "\"") == EOF) return 0; |
| |
| { |
| char name[0X40]; |
| |
| if (getCharacterName(character, name, sizeof(name))) { |
| if (fprintf(file, " # %s", name) == EOF) return 0; |
| } |
| } |
| |
| return endLine(file); |
| } |
| |
| static int |
| writeCharacter_XCompose ( |
| FILE *file, const char *directive, |
| wchar_t character, unsigned char dots |
| ) { |
| if (!writeCharacterDots_XCompose (file, dots)) return 0; |
| return writeCharacterOutput_XCompose(file, character); |
| } |
| |
| static int |
| writeTable_XCompose ( |
| const char *path, FILE *file, TextTableData *ttd, const void *data |
| ) { |
| if (!writeHeaderComment(file, writeHashComment)) return 0; |
| if (!writeCharacters(file, ttd, writeCharacter_XCompose, data)) return 0; |
| return 1; |
| } |
| |
| 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; |
| return writeCharacterOutput_XCompose(file, character); |
| } |
| |
| static int |
| writeCharacter_leftrighthalf_XCompose ( |
| FILE *file, const char *directive, |
| wchar_t character, unsigned char dots |
| ) { |
| unsigned char leftDots = brlGetLeftDots(dots); |
| unsigned char rightDots = brlGetRightDots(dots); |
| |
| if (!writeCharacter_half_XCompose(file, character, leftDots, rightDots)) { |
| return 0; |
| } |
| |
| if (!leftDots && rightDots) { |
| /* Also add shortcut without blank pattern for left part. */ |
| if (!writeCharacterDots_XCompose (file, rightDots)) return 0; |
| if (!writeCharacterOutput_XCompose(file, character)) return 0; |
| } |
| |
| return 1; |
| } |
| |
| static int |
| writeTable_leftrighthalf_XCompose ( |
| const char *path, FILE *file, TextTableData *ttd, const void *data |
| ) { |
| if (!writeHeaderComment(file, writeHashComment)) return 0; |
| if (!writeCharacters(file, ttd, writeCharacter_leftrighthalf_XCompose, data)) return 0; |
| return 1; |
| } |
| |
| static int |
| writeCharacter_lefthalf_XCompose ( |
| FILE *file, const char *directive, |
| wchar_t character, unsigned char dots |
| ) { |
| return writeCharacter_half_XCompose( |
| file, character, |
| brlGetLeftDots(dots), |
| brlGetRightDotsToLeftDots(dots) |
| ); |
| } |
| |
| static int |
| writeTable_lefthalf_XCompose ( |
| const char *path, FILE *file, TextTableData *ttd, const void *data |
| ) { |
| if (!writeHeaderComment(file, writeHashComment)) return 0; |
| if (!writeCharacters(file, ttd, writeCharacter_lefthalf_XCompose, data)) return 0; |
| return 1; |
| } |
| |
| static int |
| writeCharacter_lefthalfalt_XCompose ( |
| FILE *file, const char *directive, |
| wchar_t character, unsigned char dots |
| ) { |
| return writeCharacter_half_XCompose( |
| file, character, |
| brlGetLeftDots(dots), |
| brlGetRightDotsToLeftDotsAlt(dots) |
| ); |
| } |
| |
| static int |
| writeTable_lefthalfalt_XCompose ( |
| const char *path, FILE *file, TextTableData *ttd, const void *data |
| ) { |
| if (!writeHeaderComment(file, writeHashComment)) return 0; |
| if (!writeCharacters(file, ttd, writeCharacter_lefthalfalt_XCompose, data)) return 0; |
| return 1; |
| } |
| |
| static int |
| writeCharacter_righthalf_XCompose ( |
| FILE *file, const char *directive, |
| wchar_t character, unsigned char dots |
| ) { |
| return writeCharacter_half_XCompose( |
| file, character, |
| brlGetLeftDotsToRightDots(dots), |
| brlGetRightDots(dots) |
| ); |
| } |
| |
| static int |
| writeTable_righthalf_XCompose ( |
| const char *path, FILE *file, TextTableData *ttd, const void *data |
| ) { |
| if (!writeHeaderComment(file, writeHashComment)) return 0; |
| if (!writeCharacters(file, ttd, writeCharacter_righthalf_XCompose, data)) return 0; |
| return 1; |
| } |
| |
| static int |
| writeCharacter_righthalfalt_XCompose ( |
| FILE *file, const char *directive, |
| wchar_t character, unsigned char dots |
| ) { |
| return writeCharacter_half_XCompose( |
| file, character, |
| brlGetLeftDotsToRightDotsAlt(dots), |
| brlGetRightDots(dots) |
| ); |
| } |
| |
| static int |
| writeTable_righthalfalt_XCompose ( |
| const char *path, FILE *file, TextTableData *ttd, const void *data |
| ) { |
| if (!writeHeaderComment(file, writeHashComment)) return 0; |
| if (!writeCharacters(file, ttd, writeCharacter_righthalfalt_XCompose, data)) return 0; |
| return 1; |
| } |
| |
| static int |
| writeCharacter_JAWS ( |
| FILE *file, const char *directive, |
| wchar_t character, unsigned char dots |
| ) { |
| uint32_t value = character; |
| |
| if (fprintf(file, "U+%04" PRIX32 "=", value) == EOF) return 0; |
| if (!writeDots(file, dots)) return 0; |
| return endLine(file); |
| } |
| |
| 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, data)) 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, const char *directive, |
| wchar_t character, unsigned char dots |
| ) { |
| 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, "%s", directive) == EOF) return 0; |
| |
| if (!nextCArgument(file)) return 0; |
| if (!writeCharacterName_CPreprocessor(file, character)) return 0; |
| |
| if (!endCMacro(file)) return 0; |
| return endLine(file); |
| } |
| |
| 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, properties, 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, data)) 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, |
| .data = WRITE_CHARACTERS_NATIVE, |
| }, |
| |
| { .name = "a2b", |
| .read = readTable_binary, |
| .write = writeTable_binary, |
| .data = &dots12345678, |
| }, |
| |
| { .name = "sbl", |
| .read = readTable_binary, |
| .write = writeTable_binary, |
| .data = &dots14253678, |
| }, |
| |
| { .name = "ctb", |
| .read = readTable_LibLouis, |
| .write = writeTable_LibLouis, |
| .data = WRITE_CHARACTERS_LIBLOUIS, |
| }, |
| |
| { .name = "utb", |
| .read = readTable_LibLouis, |
| .write = writeTable_LibLouis, |
| .data = WRITE_CHARACTERS_LIBLOUIS, |
| }, |
| |
| #ifdef HAVE_ICONV_H |
| { .name = "gnb", |
| .read = readTable_gnome, |
| .write = writeTable_gnome, |
| .data = WRITE_CHARACTERS_IN_ONLY, |
| }, |
| #endif /* HAVE_ICONV_H */ |
| |
| { .name = "XCompose", |
| .write = writeTable_XCompose, |
| .data = WRITE_CHARACTERS_IN_ONLY, |
| }, |
| |
| { .name = "half-XCompose", |
| .write = writeTable_leftrighthalf_XCompose, |
| .data = WRITE_CHARACTERS_IN_ONLY, |
| }, |
| |
| { .name = "lefthalf-XCompose", |
| .write = writeTable_lefthalf_XCompose, |
| .data = WRITE_CHARACTERS_IN_ONLY, |
| }, |
| |
| { .name = "lefthalfalt-XCompose", |
| .write = writeTable_lefthalfalt_XCompose, |
| .data = WRITE_CHARACTERS_IN_ONLY, |
| }, |
| |
| { .name = "righthalf-XCompose", |
| .write = writeTable_righthalf_XCompose, |
| .data = WRITE_CHARACTERS_IN_ONLY, |
| }, |
| |
| { .name = "righthalfalt-XCompose", |
| .write = writeTable_righthalfalt_XCompose, |
| .data = WRITE_CHARACTERS_IN_ONLY, |
| }, |
| |
| { .name = "jbt", |
| .write = writeTable_JAWS, |
| .data = WRITE_CHARACTERS_OUT_ONLY, |
| }, |
| |
| { .name = "cpp", |
| .write = writeTable_CPreprocessor, |
| .data = WRITE_CHARACTER_DIRECTIVES(.in="1", .out="2", .inOut="3"), |
| }, |
| |
| { .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 <termios.h> |
| 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) { |
| for (unsigned int i=0; i<descriptionLength; i+=1) { |
| wint_t wc = convertCharToWchar(buffer[i]); |
| if (wc == WEOF) wc = WC_C(' '); |
| description[i] = wc; |
| } |
| |
| description[characterIndex] = printableCharacter; |
| description[brailleIndex] = gotDots? (UNICODE_BRAILLE_ROW | dots): WC_C(' '); |
| description[descriptionLength] = 0; |
| |
| if (length) *length = descriptionLength; |
| if (defined) *defined = gotDots; |
| if (braille) *braille = dots; |
| return description; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static void |
| printCharacterString (const wchar_t *wcs) { |
| while (*wcs) { |
| wint_t wc = *wcs++; |
| printw("%" PRIwc, wc); |
| } |
| } |
| |
| static void |
| printNavigationPair ( |
| const char *key1, const char *preposition1, |
| const char *key2, const char *preposition2, |
| const char *adjective, const char *noun, |
| int width |
| ) { |
| int length = 0; |
| |
| if (*key1 || *key2) { |
| const char *separator = (*key1 && *key2)? "/": ""; |
| |
| printw("%s%s%s: %s%s%s %s %s%n", |
| key1, separator, key2, |
| (*key1? preposition1: ""), separator, (*key2? preposition2: ""), |
| adjective, noun, &length); |
| } |
| |
| if (width > 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_WCH |
| #define IS_UNICODE_CHARACTER |
| wint_t ch; |
| int ret = get_wch(&ch); |
| |
| if (ret == KEY_CODE_YES) |
| #else /* GOT_CURSES_WCH */ |
| int ch = getch(); |
| |
| if (ch >= 0X100) |
| #endif /* GOT_CURSES_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_WCH |
| if (ret == OK) |
| #endif /* GOT_CURSES_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; |
| |
| { |
| for (int i=0; i<NCCS; i+=1) newAttributes.c_cc[i] = _POSIX_VDISABLE; |
| } |
| |
| newAttributes.c_cc[VTIME] = 0; |
| newAttributes.c_cc[VMIN] = 1; |
| |
| if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &newAttributes) != -1) { |
| inputAttributesChanged = 1; |
| }; |
| } |
| #endif /* input terminal initialization */ |
| #endif /* initialize keyboard and screen */ |
| |
| etd.charset = *opt_charset? opt_charset: NULL; |
| setFirstDefinedCharacter(&etd); |
| |
| while (updateCharacterDescription(&etd)) { |
| fd_set set; |
| FD_ZERO(&set); |
| |
| #ifndef __MINGW32__ |
| { |
| int maximumFileDescriptor = STDIN_FILENO; |
| FD_SET(STDIN_FILENO, &set); |
| |
| #ifdef ENABLE_API |
| if (haveBrailleDisplay(&etd)) { |
| FD_SET(etd.brlapiFileDescriptor, &set); |
| |
| if (etd.brlapiFileDescriptor > 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; |
| |
| { |
| const CommandLineDescriptor descriptor = { |
| .options = &programOptions, |
| .applicationName = "brltty-ttb", |
| |
| .usage = { |
| .purpose = strtext("Check/edit a text (computer braille) table, or convert it from one format to another."), |
| .parameters = "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; |
| } |