blob: 07d72ee561a7f0a57bbce285fff99570e9a592a2 [file] [log] [blame] [edit]
/*
* 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 <errno.h>
#include "program.h"
#include "cmdline.h"
#include "log.h"
#include "file.h"
#include "unicode.h"
#include "utf8.h"
#include "brl_dots.h"
#include "ttb.h"
static char *opt_tablesDirectory;
static char *opt_inputTable;
static char *opt_outputTable;
static int opt_sixDots;
static int opt_noBaseCharacters;
static const char tableName_autoselect[] = "auto";
static const char tableName_unicode[] = "unicode";
BEGIN_OPTION_TABLE(programOptions)
{ .word = "input-table",
.letter = 'i',
.argument = strtext("file"),
.setting.string = &opt_inputTable,
.internal.setting = tableName_autoselect,
.description = strtext("Path to input text table.")
},
{ .word = "output-table",
.letter = 'o',
.argument = strtext("file"),
.setting.string = &opt_outputTable,
.internal.setting = tableName_unicode,
.description = strtext("Path to output text table.")
},
{ .word = "six-dots",
.letter = '6',
.setting.flag = &opt_sixDots,
.description = strtext("Remove dots seven and eight.")
},
{ .word = "no-base-characters",
.letter = 'b',
.setting.flag = &opt_noBaseCharacters,
.description = strtext("Don't fall back to the Unicode base character.")
},
{ .word = "tables-directory",
.letter = 'T',
.argument = strtext("directory"),
.setting.string = &opt_tablesDirectory,
.internal.setting = TABLES_DIRECTORY,
.internal.adjust = fixInstallPath,
.description = strtext("Path to directory for text tables.")
},
END_OPTION_TABLE(programOptions)
static TextTable *inputTable;
static TextTable *outputTable;
static FILE *outputStream;
static const char *outputName;
static unsigned char (*toDots) (wchar_t character);
static wchar_t (*toCharacter) (unsigned char dots);
static unsigned char
toDots_mapped (wchar_t character) {
return convertCharacterToDots(inputTable, character);
}
static unsigned char
toDots_unicode (wchar_t character) {
return ((character & UNICODE_ROW_MASK) == UNICODE_BRAILLE_ROW)?
character & UNICODE_CELL_MASK:
(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 wchar_t
toCharacter_mapped (unsigned char dots) {
return convertDotsToCharacter(outputTable, dots);
}
static wchar_t
toCharacter_unicode (unsigned char dots) {
return UNICODE_BRAILLE_ROW | dots;
}
static int
writeCharacter (const wchar_t *character, mbstate_t *state) {
char bytes[0X1000];
size_t result = wcrtomb(bytes, (character? *character: WC_C('\0')), state);
if (result == (size_t)-1) return 0;
if (!character) result -= 1;
fwrite(bytes, 1, result, outputStream);
return !ferror(outputStream);
}
static int
processStream (FILE *inputStream, const char *inputName) {
mbstate_t inputState;
memset(&inputState, 0, sizeof(inputState));
mbstate_t outputState;
memset(&outputState, 0, sizeof(outputState));
while (!feof(inputStream)) {
char inputBuffer[0X1000];
size_t inputCount = fread(inputBuffer, 1, sizeof(inputBuffer)-1, inputStream);
if (ferror(inputStream)) goto inputError;
if (!inputCount) break;
inputBuffer[inputCount] = 0;
{
char *byte = inputBuffer;
while (inputCount) {
wchar_t character;
{
size_t result = mbrtowc(&character, byte, inputCount, &inputState);
if (result == (size_t)-2) break;
if (result == (size_t)-1) goto inputError;
if (!result) result = 1;
byte += result;
inputCount -= result;
}
if (!iswcntrl(character)) {
unsigned char dots = toDots(character);
if (dots || !iswspace(character)) {
if (opt_sixDots) dots &= ~(BRL_DOT_7 | BRL_DOT_8);
character = toCharacter(dots);
}
}
if (!writeCharacter(&character, &outputState)) goto outputError;
}
}
}
if (!writeCharacter(NULL, &outputState)) goto outputError;
fflush(outputStream);
if (ferror(outputStream)) goto outputError;
if (!mbsinit(&inputState)) {
#ifdef EILSEQ
errno = EILSEQ;
#else /* EILSEQ */
errno = EINVAL;
#endif /* EILSEQ */
goto inputError;
}
return 1;
inputError:
logMessage(LOG_ERR, "input error: %s: %s", inputName, strerror(errno));
return 0;
outputError:
logMessage(LOG_ERR, "output error: %s: %s", outputName, strerror(errno));
return 0;
}
static int
getTable (TextTable **table, const char *name) {
const char *directory = opt_tablesDirectory;
*table = NULL;
if (strcmp(name, tableName_unicode) != 0) {
char *allocated = NULL;
if (strcmp(name, tableName_autoselect) == 0) {
if (!(name = allocated = getTextTableForLocale(directory))) {
logMessage(LOG_ERR, "cannot find text table for current locale");
}
}
if (name) {
char *path = makeTextTablePath(directory, name);
if (path) {
*table = compileTextTable(path);
free(path);
}
}
if (allocated) free(allocated);
if (opt_noBaseCharacters) setTryBaseCharacter(*table, 0);
if (!*table) return 0;
}
return 1;
}
int
main (int argc, char *argv[]) {
ProgramExitStatus exitStatus = PROG_EXIT_FATAL;
{
const CommandLineDescriptor descriptor = {
.options = &programOptions,
.applicationName = "brltty-trtxt",
.usage = {
.purpose = strtext("Translate one binary braille representation to another."),
.parameters = "[{input-file | -} ...]",
}
};
PROCESS_OPTIONS(descriptor, argc, argv);
}
if (getTable(&inputTable, opt_inputTable)) {
if (getTable(&outputTable, opt_outputTable)) {
outputStream = stdout;
outputName = standardOutputName;
toDots = inputTable? toDots_mapped: toDots_unicode;
toCharacter = outputTable? toCharacter_mapped: toCharacter_unicode;
if (argc) {
do {
const char *file = argv[0];
FILE *stream;
if (strcmp(file, standardStreamArgument) == 0) {
if (!processStream(stdin, standardInputName)) break;
} else if ((stream = fopen(file, "r"))) {
int ok = processStream(stream, file);
fclose(stream);
if (!ok) break;
} else {
logMessage(LOG_ERR, "cannot open file: %s: %s", file, strerror(errno));
exitStatus = PROG_EXIT_SEMANTIC;
break;
}
argv += 1, argc -= 1;
} while (argc);
if (!argc) exitStatus = PROG_EXIT_SUCCESS;
} else if (processStream(stdin, standardInputName)) {
exitStatus = PROG_EXIT_SUCCESS;
}
if (outputTable) destroyTextTable(outputTable);
}
if (inputTable) destroyTextTable(inputTable);
}
return exitStatus;
}