/* * 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 "program.h" #include "options.h" #include "prefs.h" #include "log.h" #include "file.h" #include "datafile.h" #include "parse.h" #include "utf8.h" #include "unicode.h" #include "ascii.h" #include "ttb.h" #include "ctb.h" static char *opt_tablesDirectory; static char *opt_contractionTable; static char *opt_textTable; static char *opt_verificationTable; static int opt_reformatText; static char *opt_outputWidth; static int opt_forceOutput; BEGIN_OPTION_TABLE(programOptions) { .word = "tables-directory", .letter = 'T', .flags = OPT_Hidden, .argument = strtext("directory"), .setting.string = &opt_tablesDirectory, .internal.setting = TABLES_DIRECTORY, .internal.adjust = fixInstallPath, .description = strtext("Path to directory containing tables.") }, { .word = "contraction-table", .letter = 'c', .argument = "file", .setting.string = &opt_contractionTable, .internal.setting = "en-us-g2", .description = strtext("Contraction table.") }, { .word = "text-table", .letter = 't', .argument = "file", .setting.string = &opt_textTable, .description = strtext("Text table.") }, { .word = "verification-table", .letter = 'v', .argument = "file", .setting.string = &opt_verificationTable, .description = strtext("Contraction verification table.") }, { .word = "reformat-text", .letter = 'r', .setting.flag = &opt_reformatText, .description = strtext("Reformat input.") }, { .word = "output-width", .letter = 'w', .argument = "columns", .setting.string = &opt_outputWidth, .internal.setting = "", .description = strtext("Maximum length of an output line.") }, { .word = "force-output", .letter = 'f', .setting.flag = &opt_forceOutput, .description = strtext("Force immediate output.") }, END_OPTION_TABLE static wchar_t *inputBuffer; static size_t inputSize; static size_t inputLength; static FILE *outputStream; static unsigned char *outputBuffer; static int outputWidth; static int outputExtend; #define VERIFICATION_TABLE_EXTENSION ".cvb" #define VERIFICATION_SUBTABLE_EXTENSION ".cvi" static char *verificationTablePath; static FILE *verificationTableStream; static int (*processInputCharacters) (const wchar_t *characters, size_t length, void *data); static int (*putCell) (unsigned char cell, void *data); typedef struct { ProgramExitStatus exitStatus; } LineProcessingData; static void noMemory (void *data) { LineProcessingData *lpd = data; logMallocError(); lpd->exitStatus = PROG_EXIT_FATAL; } static int checkOutputStream (void *data) { LineProcessingData *lpd = data; if (ferror(outputStream)) { logSystemError("output"); lpd->exitStatus = PROG_EXIT_FATAL; return 0; } return 1; } static int flushOutputStream (void *data) { fflush(outputStream); return checkOutputStream(data); } static int putCharacter (unsigned char character, void *data) { fputc(character, outputStream); return checkOutputStream(data); } static int putCellCharacter (wchar_t character, void *data) { Utf8Buffer utf8; size_t utfs = convertWcharToUtf8(character, utf8); fprintf(outputStream, "%.*s", (int)utfs, utf8); return checkOutputStream(data); } static int putTextCell (unsigned char cell, void *data) { return putCellCharacter(convertDotsToCharacter(textTable, cell), data); } static int putBrailleCell (unsigned char cell, void *data) { return putCellCharacter((UNICODE_BRAILLE_ROW | cell), data); } static int writeCharacters (const wchar_t *inputLine, size_t inputLength, void *data) { const wchar_t *inputBuffer = inputLine; while (inputLength) { int inputCount = inputLength; int outputCount = outputWidth; if (!outputBuffer) { if (!(outputBuffer = malloc(outputWidth))) { noMemory(data); return 0; } } contractText(contractionTable, inputBuffer, &inputCount, outputBuffer, &outputCount, NULL, CTB_NO_CURSOR); if ((inputCount < inputLength) && outputExtend) { free(outputBuffer); outputBuffer = NULL; outputWidth <<= 1; } else { { int index; for (index=0; index inputSize) { size_t newSize = newLength | 0XFF; wchar_t *newBuffer = calloc(newSize, sizeof(*newBuffer)); if (!newBuffer) { noMemory(data); return 0; } wmemcpy(newBuffer, inputBuffer, inputLength); free(inputBuffer); inputBuffer = newBuffer; inputSize = newSize; } while (spaces) { inputBuffer[inputLength++] = WC_C(' '); spaces -= 1; } wmemcpy(&inputBuffer[inputLength], characters, count); inputLength += count; } if (end != '\n') { if (!flushCharacters(0, data)) return 0; if (!putCharacter(end, data)) return 0; } } else { if (!flushCharacters('\n', data)) return 0; if (!writeCharacters(characters, count, data)) return 0; if (!putCharacter(end, data)) return 0; } return 1; } static int writeContractedBraille (const wchar_t *characters, size_t length, void *data) { const wchar_t *character = characters; while (1) { const wchar_t *end = wmemchr(character, FF, length); size_t count; if (!end) break; count = end - character; if (!processCharacters(character, count, *end, data)) return 0; count += 1; character += count; length -= count; } if (!processCharacters(character, length, '\n', data)) return 0; if (opt_forceOutput) if (!flushOutputStream(data)) return 0; return 1; } static char * makeUtf8FromCells (unsigned char *cells, size_t count) { char *text = malloc((count * UTF8_LEN_MAX) + 1); if (text) { char *ch = text; size_t i; for (i=0; i 0)? "w": "r"; if ((verificationTableStream = openDataFile(verificationTablePath, verificationTableMode, 0))) { processInputCharacters = writeVerificationTableLine; } else { exitStatus = PROG_EXIT_FATAL; } } else { exitStatus = PROG_EXIT_FATAL; } } } if (exitStatus == PROG_EXIT_SUCCESS) { if (verificationTableStream && !argc) { exitStatus = processVerificationTable(); } else { LineProcessingData lpd = { .exitStatus = PROG_EXIT_SUCCESS }; const InputFilesProcessingParameters parameters = { .dataFileParameters = { .options = DFO_NO_COMMENTS, .processOperands = processInputLine, .data = &lpd } }; if ((exitStatus = processInputFiles(argv, argc, ¶meters)) == PROG_EXIT_SUCCESS) { if (!(flushCharacters('\n', &lpd) && flushOutputStream(&lpd))) { exitStatus = lpd.exitStatus; } } } if (textTable) destroyTextTable(textTable); } destroyContractionTable(contractionTable); } else { exitStatus = PROG_EXIT_FATAL; } free(contractionTablePath); } } if (verificationTableStream) { if (fclose(verificationTableStream) == EOF) { exitStatus = PROG_EXIT_FATAL; } verificationTableStream = NULL; } if (verificationTablePath) { free(verificationTablePath); verificationTablePath = NULL; } if (outputBuffer) free(outputBuffer); if (inputBuffer) free(inputBuffer); return exitStatus; }