/* liblouis Braille Translation and Back-Translation Library Based on the Linux screenreader BRLTTY, copyright (C) 1999-2006 by The BRLTTY Team Copyright (C) 2004, 2005, 2006 ViewPlus Technologies, Inc. www.viewplus.com Copyright (C) 2004, 2005, 2006 JJB Software, Inc. www.jjb-software.com Copyright (C) 2016 Mike Gray, American Printing House for the Blind Copyright (C) 2016 Davy Kager, Dedicon This file is part of liblouis. liblouis is free software: you can redistribute it and/or modify it 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. liblouis is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with liblouis. If not, see . */ /** * @file * @brief Common utility functions */ #include #include #include #include #include #include #include #include "internal.h" #include "config.h" /* Contributed by Michel Such */ #ifdef _WIN32 /* Adapted from BRLTTY code (see sys_progs_wihdows.h) */ #include static void * reallocWrapper(void *address, size_t size) { if (!(address = realloc(address, size)) && size) _lou_outOfMemory(); return address; } static char * strdupWrapper(const char *string) { char *address = strdup(string); if (!address) _lou_outOfMemory(); return address; } char *EXPORT_CALL lou_getProgramPath(void) { char *path = NULL; HMODULE handle; if ((handle = GetModuleHandle(NULL))) { DWORD size = 0X80; char *buffer = NULL; while (1) { buffer = reallocWrapper(buffer, size <<= 1); { // As the "UNICODE" Windows define may have been set at compilation, // This call must be specifically GetModuleFilenameA as further code // expects it to be single byte chars. DWORD length = GetModuleFileNameA(handle, buffer, size); if (!length) { printf("GetModuleFileName\n"); exit(3); } if (length < size) { buffer[length] = 0; path = strdupWrapper(buffer); while (length > 0) if (path[--length] == '\\') break; strncpy(path, path, length + 1); path[length + 1] = '\0'; break; } } } free(buffer); } else { printf("GetModuleHandle\n"); exit(3); } return path; } #endif /* End of MS contribution */ static widechar toLowercase(widechar c, const TranslationTableHeader *table) { static TranslationTableOffset offset; static TranslationTableCharacter *character; offset = table->characters[_lou_charHash(c)]; while (offset) { character = (TranslationTableCharacter *)&table->ruleArea[offset]; if (character->realchar == c) return character->lowercase; offset = character->next; } return c; } unsigned long int EXPORT_CALL _lou_stringHash(const widechar *c, int lowercase, const TranslationTableHeader *table) { if (!lowercase) return (((unsigned long int)c[0] << 8) + (unsigned long int)c[1]) % HASHNUM; else return (((unsigned long int)toLowercase(c[0], table) << 8) + (unsigned long int)toLowercase(c[1], table)) % HASHNUM; } unsigned long int EXPORT_CALL _lou_charHash(widechar c) { return (unsigned long int)c % HASHNUM; } const char *EXPORT_CALL _lou_showString(widechar const *chars, int length, int forceHex) { /* Translate a string of characters to the encoding used in character * operands */ static char scratchBuf[MAXSTRING]; int bufPos = 0; scratchBuf[bufPos++] = '\''; for (int charPos = 0; (charPos < length) && (bufPos < (MAXSTRING - 2)); charPos += 1) { widechar c = chars[charPos]; if (!forceHex && isASCII(c)) { scratchBuf[bufPos++] = (char)c; } else { char hexbuf[20]; int hexLength; char escapeLetter; int leadingZeros; int hexPos; hexLength = sprintf(hexbuf, "%x", c); switch (hexLength) { case 1: case 2: case 3: case 4: escapeLetter = 'x'; leadingZeros = 4 - hexLength; break; case 5: escapeLetter = 'y'; leadingZeros = 0; break; case 6: case 7: case 8: escapeLetter = 'z'; leadingZeros = 8 - hexLength; break; default: escapeLetter = '?'; leadingZeros = 0; break; } if ((bufPos + leadingZeros + hexLength + 4) >= (MAXSTRING - 2)) break; scratchBuf[bufPos++] = '\\'; scratchBuf[bufPos++] = escapeLetter; for (hexPos = 0; hexPos < leadingZeros; hexPos++) scratchBuf[bufPos++] = '0'; for (hexPos = 0; hexPos < hexLength; hexPos++) scratchBuf[bufPos++] = hexbuf[hexPos]; } } scratchBuf[bufPos++] = '\''; scratchBuf[bufPos] = 0; return scratchBuf; } /** * Mapping between braille dot and textual representation as used in dots operands */ static const intCharTupple dotMapping[] = { { LOU_DOT_1, '1' }, { LOU_DOT_2, '2' }, { LOU_DOT_3, '3' }, { LOU_DOT_4, '4' }, { LOU_DOT_5, '5' }, { LOU_DOT_6, '6' }, { LOU_DOT_7, '7' }, { LOU_DOT_8, '8' }, { LOU_DOT_9, '9' }, { LOU_DOT_10, 'A' }, { LOU_DOT_11, 'B' }, { LOU_DOT_12, 'C' }, { LOU_DOT_13, 'D' }, { LOU_DOT_14, 'E' }, { LOU_DOT_15, 'F' }, { 0, 0 }, }; /** * Print out dot numbers * * @return a string containing the dot numbers. The longest possible * output is "\123456789ABCDEF0/" */ const char *EXPORT_CALL _lou_unknownDots(widechar dots) { static char buffer[20]; int k = 0; buffer[k++] = '\\'; for (int mappingPos = 0; dotMapping[mappingPos].key; mappingPos++) { if (dots & dotMapping[mappingPos].key) buffer[k++] = dotMapping[mappingPos].value; } if (k == 1) buffer[k++] = '0'; buffer[k++] = '/'; buffer[k] = 0; return buffer; } /** * Translate a sequence of dots to the encoding used in dots operands. */ const char *EXPORT_CALL _lou_showDots(widechar const *dots, int length) { int bufPos = 0; static char scratchBuf[MAXSTRING]; for (int dotsPos = 0; dotsPos < length && bufPos < (MAXSTRING - 1); dotsPos++) { for (int mappingPos = 0; dotMapping[mappingPos].key; mappingPos++) { if ((dots[dotsPos] & dotMapping[mappingPos].key) && (bufPos < (MAXSTRING - 1))) scratchBuf[bufPos++] = dotMapping[mappingPos].value; } if ((dots[dotsPos] == LOU_DOTS) && (bufPos < (MAXSTRING - 1))) scratchBuf[bufPos++] = '0'; if ((dotsPos != length - 1) && (bufPos < (MAXSTRING - 1))) scratchBuf[bufPos++] = '-'; } scratchBuf[bufPos] = 0; return scratchBuf; } /** * Mapping between character attribute and textual representation */ static const intCharTupple attributeMapping[] = { { CTC_Space, 's' }, { CTC_Letter, 'l' }, { CTC_Digit, 'd' }, { CTC_Punctuation, 'p' }, { CTC_UpperCase, 'U' }, { CTC_LowerCase, 'u' }, { CTC_Math, 'm' }, { CTC_Sign, 'S' }, { CTC_LitDigit, 'D' }, { CTC_Class1, 'w' }, { CTC_Class2, 'x' }, { CTC_Class3, 'y' }, { CTC_Class4, 'z' }, { 0, 0 }, }; /** * Show attributes using the letters used after the $ in multipass * opcodes. */ char *EXPORT_CALL _lou_showAttributes(TranslationTableCharacterAttributes a) { int bufPos = 0; static char scratchBuf[MAXSTRING]; for (int mappingPos = 0; attributeMapping[mappingPos].key; mappingPos++) { if ((a & attributeMapping[mappingPos].key) && bufPos < (MAXSTRING - 1)) scratchBuf[bufPos++] = attributeMapping[mappingPos].value; } scratchBuf[bufPos] = 0; return scratchBuf; } void EXPORT_CALL _lou_outOfMemory(void) { _lou_logMessage(LOU_LOG_FATAL, "liblouis: Insufficient memory\n"); exit(3); } #ifdef DEBUG void EXPORT_CALL _lou_debugHook(void) { char *hook = "debug hook"; printf("%s\n", hook); } #endif static const int validTranslationModes[] = { noContractions, compbrlAtCursor, dotsIO, compbrlLeftCursor, ucBrl, noUndefined, partialTrans }; int EXPORT_CALL _lou_isValidMode(int mode) { // mask out all valid mode bits. If you end up with some bits set // then the input isn't valid. See // https://en.wikipedia.org/wiki/Material_nonimplication for (int i = 0; i < (sizeof(validTranslationModes) / sizeof(*validTranslationModes)); i++) mode &= ~validTranslationModes[i]; return !mode; } /* Map char to dots according to North American Braille Computer Code (NABCC) */ widechar EXPORT_CALL _lou_charToFallbackDots(widechar c) { static const unsigned char charToDots[] = { /* ASCII characters 0X00-0X1F - control characters. * These won't be referenced so we have room for data. * These groups must be in descending order. * Each group contains the following four bytes: * 1) The first character to which this block applies. * 2) The bits to remove from the character. * 3) The bits to add to the character. * 4) The dots to add to the braille pattern. */ // clang-format off 0X7F, 0X20, 0X00, LOU_DOT_7, 0X60, 0X20, 0X00, 0, 0X5F, 0X00, 0X00, 0, 0X40, 0X00, 0X00, LOU_DOT_7, 0X20, 0X00, 0X00, 0, 0X00, 0X00, 0X40, LOU_DOT_7 | LOU_DOT_8, // ASCII characters 0X20-0X3F - digits and common symbols. [' '] = 0, ['!'] = LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_6, ['"'] = LOU_DOT_5, ['#'] = LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_5 | LOU_DOT_6, ['$'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_4 | LOU_DOT_6, ['%'] = LOU_DOT_1 | LOU_DOT_4 | LOU_DOT_6, ['&'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_6, ['\''] = LOU_DOT_3, ['('] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_5 | LOU_DOT_6, [')'] = LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_5 | LOU_DOT_6, ['*'] = LOU_DOT_1 | LOU_DOT_6, ['+'] = LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_6, [','] = LOU_DOT_6, ['-'] = LOU_DOT_3 | LOU_DOT_6, ['.'] = LOU_DOT_4 | LOU_DOT_6, ['/'] = LOU_DOT_3 | LOU_DOT_4, ['0'] = LOU_DOT_3 | LOU_DOT_5 | LOU_DOT_6, ['1'] = LOU_DOT_2, ['2'] = LOU_DOT_2 | LOU_DOT_3, ['3'] = LOU_DOT_2 | LOU_DOT_5, ['4'] = LOU_DOT_2 | LOU_DOT_5 | LOU_DOT_6, ['5'] = LOU_DOT_2 | LOU_DOT_6, ['6'] = LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_5, ['7'] = LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_5 | LOU_DOT_6, ['8'] = LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_6, ['9'] = LOU_DOT_3 | LOU_DOT_5, [':'] = LOU_DOT_1 | LOU_DOT_5 | LOU_DOT_6, [';'] = LOU_DOT_5 | LOU_DOT_6, ['<'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_6, ['='] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_5 | LOU_DOT_6, ['>'] = LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_5, ['?'] = LOU_DOT_1 | LOU_DOT_4 | LOU_DOT_5 | LOU_DOT_6, // ASCII characters 0X40-0X5F - letters and other symbols. ['@'] = LOU_DOT_4, ['A'] = LOU_DOT_1, ['B'] = LOU_DOT_1 | LOU_DOT_2, ['C'] = LOU_DOT_1 | LOU_DOT_4, ['D'] = LOU_DOT_1 | LOU_DOT_4 | LOU_DOT_5, ['E'] = LOU_DOT_1 | LOU_DOT_5, ['F'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_4, ['G'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_4 | LOU_DOT_5, ['H'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_5, ['I'] = LOU_DOT_2 | LOU_DOT_4, ['J'] = LOU_DOT_2 | LOU_DOT_4 | LOU_DOT_5, ['K'] = LOU_DOT_1 | LOU_DOT_3, ['L'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_3, ['M'] = LOU_DOT_1 | LOU_DOT_3 | LOU_DOT_4, ['N'] = LOU_DOT_1 | LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_5, ['O'] = LOU_DOT_1 | LOU_DOT_3 | LOU_DOT_5, ['P'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_4, ['Q'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_5, ['R'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_5, ['S'] = LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_4, ['T'] = LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_5, ['U'] = LOU_DOT_1 | LOU_DOT_3 | LOU_DOT_6, ['V'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_6, ['W'] = LOU_DOT_2 | LOU_DOT_4 | LOU_DOT_5 | LOU_DOT_6, ['X'] = LOU_DOT_1 | LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_6, ['Y'] = LOU_DOT_1 | LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_5 | LOU_DOT_6, ['Z'] = LOU_DOT_1 | LOU_DOT_3 | LOU_DOT_5 | LOU_DOT_6, ['['] = LOU_DOT_2 | LOU_DOT_4 | LOU_DOT_6, ['\\'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_5 | LOU_DOT_6, [']'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_4 | LOU_DOT_5 | LOU_DOT_6, ['^'] = LOU_DOT_4 | LOU_DOT_5, ['_'] = LOU_DOT_4 | LOU_DOT_5 | LOU_DOT_6 // clang-format on }; if (c >= 0X80) c = '?'; widechar dots = LOU_DOTS; { const unsigned char *p = charToDots; while (*p > c) p += 4; c &= ~*++p; c |= *++p; dots |= *++p; } dots |= charToDots[c]; return dots; }