Newer
Older
/*
* 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-2018 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.
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
#include "prologue.h"
#include <string.h>
#include "log.h"
#include "ctb_translate.h"
#include <unicode/uchar.h>
#include <unicode/unorm.h>
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
static int
nextBaseCharacter (const UChar **current, const UChar *end) {
do {
if (*current == end) return 0;
} while (u_getCombiningClass(*(*current)++));
return 1;
}
static int
normalizeText (
BrailleContractionData *bcd,
const wchar_t *begin, const wchar_t *end,
wchar_t *buffer, size_t *length,
unsigned int *map
) {
const size_t size = end - begin;
UChar source[size];
UChar target[size];
int32_t count;
{
const wchar_t *wc = begin;
UChar *uc = source;
while (wc < end) {
*uc++ = *wc++;
}
}
{
UErrorCode error = U_ZERO_ERROR;
count = unorm_normalize(source, ARRAY_COUNT(source),
UNORM_NFC, 0,
target, ARRAY_COUNT(target),
&error);
if (!U_SUCCESS(error)) return 0;
}
if (count == size) {
if (memcmp(source, target, (count * sizeof(source[0]))) == 0) {
return 0;
}
}
{
const UChar *src = source;
const UChar *srcEnd = src + ARRAY_COUNT(source);
const UChar *trg = target;
const UChar *trgEnd = target + count;
wchar_t *out = buffer;
while (trg < trgEnd) {
if (!nextBaseCharacter(&src, srcEnd)) return 0;
*map++ = src - source - 1;
*out++ = *trg++;
}
if (nextBaseCharacter(&src, srcEnd)) return 0;
*map = src - source;
}
*length = count;
return 1;
}
static int
normalizeText (
BrailleContractionData *bcd,
const wchar_t *begin, const wchar_t *end,
wchar_t *buffer, size_t *length,
unsigned int *map
) {
return 0;
}
CharacterEntry *
getCharacterEntry (BrailleContractionData *bcd, wchar_t character) {
int first = 0;
int last = bcd->table->characters.count - 1;
while (first <= last) {
int current = (first + last) / 2;
CharacterEntry *entry = &bcd->table->characters.array[current];
if (entry->value < character) {
first = current + 1;
} else if (entry->value > character) {
last = current - 1;
if (bcd->table->characters.count == bcd->table->characters.size) {
int newSize = bcd->table->characters.size;
newSize = newSize? newSize<<1: 0X80;
{
CharacterEntry *newArray = realloc(bcd->table->characters.array, (newSize * sizeof(*newArray)));
if (!newArray) {
logMallocError();
return NULL;
bcd->table->characters.array = newArray;
bcd->table->characters.size = newSize;
memmove(&bcd->table->characters.array[first+1],
&bcd->table->characters.array[first],
(bcd->table->characters.count - first) * sizeof(*bcd->table->characters.array));
bcd->table->characters.count += 1;
{
CharacterEntry *entry = &bcd->table->characters.array[first];
memset(entry, 0, sizeof(*entry));
entry->value = entry->uppercase = entry->lowercase = character;
if (iswspace(character)) {
entry->attributes |= CTC_Space;
} else if (iswalpha(character)) {
entry->attributes |= CTC_Letter;
if (iswupper(character)) {
entry->attributes |= CTC_UpperCase;
entry->lowercase = towlower(character);
if (iswlower(character)) {
entry->attributes |= CTC_LowerCase;
entry->uppercase = towupper(character);
} else if (iswdigit(character)) {
entry->attributes |= CTC_Digit;
} else if (iswpunct(character)) {
entry->attributes |= CTC_Punctuation;
bcd->table->translationMethods->finishCharacterEntry(bcd, entry);
return entry;
makeCachedCursorOffset (BrailleContractionData *bcd) {
return bcd->input.cursor? (bcd->input.cursor - bcd->input.begin): CTB_NO_CURSOR;
checkCache (BrailleContractionData *bcd) {
if (!bcd->table->cache.input.characters) return 0;
if (!bcd->table->cache.output.cells) return 0;
if (bcd->input.offsets && !bcd->table->cache.offsets.count) return 0;
if (bcd->table->cache.output.maximum != getOutputCount(bcd)) return 0;
if (bcd->table->cache.cursorOffset != makeCachedCursorOffset(bcd)) return 0;
if (bcd->table->cache.expandCurrentWord != prefs.expandCurrentWord) return 0;
if (bcd->table->cache.capitalizationMode != prefs.capitalizationMode) return 0;
unsigned int count = getInputCount(bcd);
if (bcd->table->cache.input.count != count) return 0;
if (wmemcmp(bcd->input.begin, bcd->table->cache.input.characters, count) != 0) return 0;
}
return 1;
}
static void
updateCache (BrailleContractionData *bcd) {
unsigned int count = getInputCount(bcd);
if (count > bcd->table->cache.input.size) {
unsigned int newSize = count | 0X7F;
wchar_t *newCharacters = malloc(ARRAY_SIZE(newCharacters, newSize));
if (!newCharacters) {
logMallocError();
bcd->table->cache.input.count = 0;
if (bcd->table->cache.input.characters) free(bcd->table->cache.input.characters);
bcd->table->cache.input.characters = newCharacters;
bcd->table->cache.input.size = newSize;
wmemcpy(bcd->table->cache.input.characters, bcd->input.begin, count);
bcd->table->cache.input.count = count;
bcd->table->cache.input.consumed = getInputConsumed(bcd);
unsigned int count = getOutputConsumed(bcd);
if (count > bcd->table->cache.output.size) {
unsigned int newSize = count | 0X7F;
unsigned char *newCells = malloc(ARRAY_SIZE(newCells, newSize));
if (!newCells) {
logMallocError();
bcd->table->cache.output.count = 0;
if (bcd->table->cache.output.cells) free(bcd->table->cache.output.cells);
bcd->table->cache.output.cells = newCells;
bcd->table->cache.output.size = newSize;
memcpy(bcd->table->cache.output.cells, bcd->output.begin, count);
bcd->table->cache.output.count = count;
bcd->table->cache.output.maximum = getOutputCount(bcd);
if (bcd->input.offsets) {
unsigned int count = getInputCount(bcd);
if (count > bcd->table->cache.offsets.size) {
unsigned int newSize = count | 0X7F;
int *newArray = malloc(ARRAY_SIZE(newArray, newSize));
if (!newArray) {
logMallocError();
bcd->table->cache.offsets.count = 0;
if (bcd->table->cache.offsets.array) free(bcd->table->cache.offsets.array);
bcd->table->cache.offsets.array = newArray;
bcd->table->cache.offsets.size = newSize;
memcpy(bcd->table->cache.offsets.array, bcd->input.offsets, ARRAY_SIZE(bcd->input.offsets, count));
bcd->table->cache.offsets.count = count;
bcd->table->cache.offsets.count = 0;
bcd->table->cache.cursorOffset = makeCachedCursorOffset(bcd);
bcd->table->cache.expandCurrentWord = prefs.expandCurrentWord;
bcd->table->cache.capitalizationMode = prefs.capitalizationMode;
}
void
contractText (
ContractionTable *contractionTable,
const wchar_t *inputBuffer, int *inputLength,
BYTE *outputBuffer, int *outputLength,
int *offsetsMap, const int cursorOffset
) {
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
BrailleContractionData bcd = {
.table = contractionTable,
.input = {
.begin = inputBuffer,
.current = inputBuffer,
.end = inputBuffer + *inputLength,
.cursor = (cursorOffset == CTB_NO_CURSOR)? NULL: &inputBuffer[cursorOffset],
.offsets = offsetsMap
},
.output = {
.begin = outputBuffer,
.end = outputBuffer + *outputLength,
.current = outputBuffer
}
};
if (checkCache(&bcd)) {
bcd.input.current = bcd.input.begin + bcd.table->cache.input.consumed;
if (bcd.input.offsets) {
memcpy(bcd.input.offsets, bcd.table->cache.offsets.array,
ARRAY_SIZE(bcd.input.offsets, bcd.table->cache.offsets.count));
}
bcd.output.current = bcd.output.begin + bcd.table->cache.output.count;
memcpy(bcd.output.begin, bcd.table->cache.output.cells,
ARRAY_SIZE(bcd.output.begin, bcd.table->cache.output.count));
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
int contracted;
{
const size_t size = getInputCount(&bcd);
wchar_t buffer[size];
unsigned int map[size + 1];
size_t length;
if (normalizeText(&bcd, bcd.input.begin, bcd.input.end, buffer, &length, map)) {
const wchar_t *oldBegin = bcd.input.begin;
const wchar_t *oldEnd = bcd.input.end;
bcd.input.begin = buffer;
bcd.input.current = bcd.input.begin + (bcd.input.current - oldBegin);
bcd.input.end = bcd.input.begin + length;
if (bcd.input.cursor) {
ptrdiff_t offset = bcd.input.cursor - oldBegin;
unsigned int mapIndex;
bcd.input.cursor = NULL;
for (mapIndex=0; mapIndex<=length; mapIndex+=1) {
unsigned int mappedIndex = map[mapIndex];
if (mappedIndex > offset) break;
bcd.input.cursor = &bcd.input.begin[mappedIndex];
}
}
contracted = contractionTable->translationMethods->contractText(&bcd);
if (bcd.input.offsets) {
size_t mapIndex = length;
size_t offsetsIndex = oldEnd - oldBegin;
while (mapIndex > 0) {
unsigned int mappedIndex = map[--mapIndex];
int offset = bcd.input.offsets[mapIndex];
if (offset != CTB_NO_OFFSET) {
while (--offsetsIndex > mappedIndex) bcd.input.offsets[offsetsIndex] = CTB_NO_OFFSET;
bcd.input.offsets[offsetsIndex] = offset;
}
}
while (offsetsIndex > 0) bcd.input.offsets[--offsetsIndex] = CTB_NO_OFFSET;
}
bcd.input.begin = oldBegin;
bcd.input.current = bcd.input.begin + map[bcd.input.current - buffer];
bcd.input.end = oldEnd;
} else {
contracted = contractionTable->translationMethods->contractText(&bcd);
}
}
if (!contracted) {
bcd.input.current = bcd.input.begin;
bcd.output.current = bcd.output.begin;
while ((bcd.input.current < bcd.input.end) && (bcd.output.current < bcd.output.end)) {
setOffset(&bcd);
*bcd.output.current++ = convertCharacterToDots(textTable, *bcd.input.current++);
if (bcd.input.current < bcd.input.end) {
const wchar_t *srcorig = bcd.input.current;
if (done && !testCurrent(&bcd, CTC_Space)) {
if (!bcd.input.cursor || (bcd.input.cursor < srcorig) || (bcd.input.cursor >= bcd.input.current)) {
setOffset(&bcd);
srcorig = bcd.input.current;
if (++bcd.input.current == bcd.input.end) break;
clearOffset(&bcd);
if (!done) bcd.input.current = srcorig;
*inputLength = getInputConsumed(&bcd);
*outputLength = getOutputConsumed(&bcd);