Coverage Report

Created: 2026-08-13 07:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/liblouis/liblouis/utils.c
Line
Count
Source
1
/* liblouis Braille Translation and Back-Translation Library
2
3
   Based on the Linux screenreader BRLTTY, copyright (C) 1999-2006 by The
4
   BRLTTY Team
5
6
   Copyright (C) 2004, 2005, 2006 ViewPlus Technologies, Inc. www.viewplus.com
7
   Copyright (C) 2004, 2005, 2006 JJB Software, Inc. www.jjb-software.com
8
   Copyright (C) 2016 Mike Gray, American Printing House for the Blind
9
   Copyright (C) 2016 Davy Kager, Dedicon
10
11
   This file is part of liblouis.
12
13
   liblouis is free software: you can redistribute it and/or modify it
14
   under the terms of the GNU Lesser General Public License as published
15
   by the Free Software Foundation, either version 2.1 of the License, or
16
   (at your option) any later version.
17
18
   liblouis is distributed in the hope that it will be useful, but
19
   WITHOUT ANY WARRANTY; without even the implied warranty of
20
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21
   Lesser General Public License for more details.
22
23
   You should have received a copy of the GNU Lesser General Public
24
   License along with liblouis. If not, see <http://www.gnu.org/licenses/>.
25
*/
26
27
/**
28
 * @file
29
 * @brief Common utility functions
30
 */
31
32
#include "config.h"
33
34
#include <stddef.h>
35
#include <stdlib.h>
36
#include <stdio.h>
37
#include <stdarg.h>
38
#include <string.h>
39
#include <ctype.h>
40
#include <sys/stat.h>
41
42
#include "internal.h"
43
44
/* Contributed by Michel Such <michel.such@free.fr> */
45
#ifdef _WIN32
46
47
/* Adapted from BRLTTY code (see sys_progs_wihdows.h) */
48
49
#include <shlobj.h>
50
51
static void *
52
reallocWrapper(void *address, size_t size) {
53
  if (!(address = realloc(address, size)) && size) _lou_outOfMemory();
54
  return address;
55
}
56
57
static char *
58
strdupWrapper(const char *string) {
59
  char *address = strdup(string);
60
  if (!address) _lou_outOfMemory();
61
  return address;
62
}
63
64
char *EXPORT_CALL
65
lou_getProgramPath(void) {
66
  char *path = NULL;
67
  HMODULE handle;
68
69
  if ((handle = GetModuleHandle(NULL))) {
70
    DWORD size = 0X80;
71
    char *buffer = NULL;
72
73
    while (1) {
74
      buffer = reallocWrapper(buffer, size <<= 1);
75
76
      {
77
        // As the "UNICODE" Windows define may have been set at compilation,
78
        // This call must be specifically GetModuleFilenameA as further code
79
        // expects it to be single byte chars.
80
        DWORD length = GetModuleFileNameA(handle, buffer, size);
81
82
        if (!length) {
83
          printf("GetModuleFileName\n");
84
          exit(3);
85
        }
86
87
        if (length < size) {
88
          buffer[length] = 0;
89
          path = strdupWrapper(buffer);
90
91
          while (length > 0)
92
            if (path[--length] == '\\') break;
93
94
          strncpy(path, path, length + 1);
95
          path[length + 1] = '\0';
96
          break;
97
        }
98
      }
99
    }
100
101
    free(buffer);
102
  } else {
103
    printf("GetModuleHandle\n");
104
    exit(3);
105
  }
106
107
  return path;
108
}
109
#endif
110
/* End of MS contribution */
111
112
static widechar
113
4.05M
toLowercase(widechar c, const TranslationTableHeader *table) {
114
4.05M
  static TranslationTableOffset offset;
115
4.05M
  static TranslationTableCharacter *character;
116
4.05M
  offset = table->characters[_lou_charHash(c)];
117
4.13M
  while (offset) {
118
1.98M
    character = (TranslationTableCharacter *)&table->ruleArea[offset];
119
1.98M
    if (character->value == c) {
120
1.90M
      if (character->mode & CTC_UpperCase) {
121
851
        const TranslationTableCharacter *c = character;
122
851
        if (c->basechar)
123
851
          c = (TranslationTableCharacter *)&table->ruleArea[c->basechar];
124
937
        while (1) {
125
937
          if ((c->mode & (character->mode & ~CTC_UpperCase)) ==
126
937
              (character->mode & ~CTC_UpperCase))
127
851
            return c->value;
128
86
          if (!c->linked) break;
129
86
          c = (TranslationTableCharacter *)&table->ruleArea[c->linked];
130
86
        }
131
851
      }
132
1.90M
      return character->value;
133
1.90M
    }
134
83.8k
    offset = character->next;
135
83.8k
  }
136
2.15M
  return c;
137
4.05M
}
138
139
unsigned long int EXPORT_CALL
140
2.02M
_lou_stringHash(const widechar *c, int lowercase, const TranslationTableHeader *table) {
141
2.02M
  if (!lowercase)
142
465
    return (((unsigned long int)c[0] << 8) + (unsigned long int)c[1]) % HASHNUM;
143
2.02M
  else
144
2.02M
    return (((unsigned long int)toLowercase(c[0], table) << 8) +
145
2.02M
             (unsigned long int)toLowercase(c[1], table)) %
146
2.02M
        HASHNUM;
147
2.02M
}
148
149
unsigned long int EXPORT_CALL
150
452M
_lou_charHash(widechar c) {
151
452M
  return (unsigned long int)c % HASHNUM;
152
452M
}
153
154
const char *EXPORT_CALL
155
381k
_lou_showString(widechar const *chars, int length, int forceHex) {
156
  /* Translate a string of characters to the encoding used in character
157
   * operands */
158
381k
  static char scratchBuf[MAXSTRING];
159
381k
  int bufPos = 0;
160
381k
  scratchBuf[bufPos++] = '\'';
161
162
764k
  for (int charPos = 0; (charPos < length) && (bufPos < (MAXSTRING - 2));
163
382k
      charPos += 1) {
164
382k
    widechar c = chars[charPos];
165
166
382k
    if (!forceHex && isASCII(c)) {
167
635
      scratchBuf[bufPos++] = (char)c;
168
381k
    } else {
169
381k
      char hexbuf[20];
170
381k
      int hexLength;
171
381k
      char escapeLetter;
172
173
381k
      int leadingZeros;
174
381k
      int hexPos;
175
381k
      hexLength = sprintf(hexbuf, "%x", c);
176
381k
      switch (hexLength) {
177
38.7k
      case 1:
178
371k
      case 2:
179
371k
      case 3:
180
381k
      case 4:
181
381k
        escapeLetter = 'x';
182
381k
        leadingZeros = 4 - hexLength;
183
381k
        break;
184
0
      case 5:
185
0
        escapeLetter = 'y';
186
0
        leadingZeros = 0;
187
0
        break;
188
0
      case 6:
189
0
      case 7:
190
0
      case 8:
191
0
        escapeLetter = 'z';
192
0
        leadingZeros = 8 - hexLength;
193
0
        break;
194
0
      default:
195
0
        escapeLetter = '?';
196
0
        leadingZeros = 0;
197
0
        break;
198
381k
      }
199
381k
      if ((bufPos + leadingZeros + hexLength + 4) >= (MAXSTRING - 2)) break;
200
381k
      scratchBuf[bufPos++] = '\\';
201
381k
      scratchBuf[bufPos++] = escapeLetter;
202
1.16M
      for (hexPos = 0; hexPos < leadingZeros; hexPos++) scratchBuf[bufPos++] = '0';
203
1.12M
      for (hexPos = 0; hexPos < hexLength; hexPos++)
204
745k
        scratchBuf[bufPos++] = hexbuf[hexPos];
205
381k
    }
206
382k
  }
207
381k
  scratchBuf[bufPos++] = '\'';
208
381k
  scratchBuf[bufPos] = 0;
209
381k
  return scratchBuf;
210
381k
}
211
212
/**
213
 * Mapping between braille dot and textual representation as used in dots operands
214
 */
215
static const intCharTupple dotMapping[] = {
216
  { LOU_DOT_1, '1' },
217
  { LOU_DOT_2, '2' },
218
  { LOU_DOT_3, '3' },
219
  { LOU_DOT_4, '4' },
220
  { LOU_DOT_5, '5' },
221
  { LOU_DOT_6, '6' },
222
  { LOU_DOT_7, '7' },
223
  { LOU_DOT_8, '8' },
224
  { LOU_DOT_9, '9' },
225
  { LOU_DOT_10, 'A' },
226
  { LOU_DOT_11, 'B' },
227
  { LOU_DOT_12, 'C' },
228
  { LOU_DOT_13, 'D' },
229
  { LOU_DOT_14, 'E' },
230
  { LOU_DOT_15, 'F' },
231
  { 0, 0 },
232
};
233
234
/**
235
 * Print out dot numbers
236
 *
237
 * @return a string containing the dot numbers. The longest possible
238
 * output is "\123456789ABCDEF0/"
239
 */
240
const char *EXPORT_CALL
241
0
_lou_unknownDots(widechar dots) {
242
0
  static char buffer[20];
243
244
0
  int k = 0;
245
0
  buffer[k++] = '\\';
246
247
0
  for (int mappingPos = 0; dotMapping[mappingPos].key; mappingPos++) {
248
0
    if (dots & dotMapping[mappingPos].key) buffer[k++] = dotMapping[mappingPos].value;
249
0
  }
250
251
0
  if (k == 1) buffer[k++] = '0';
252
0
  buffer[k++] = '/';
253
0
  buffer[k] = 0;
254
0
  return buffer;
255
0
}
256
257
/**
258
 * Translate a sequence of dots to the encoding used in dots operands.
259
 */
260
const char *EXPORT_CALL
261
472
_lou_showDots(widechar const *dots, int length) {
262
472
  int bufPos = 0;
263
472
  static char scratchBuf[MAXSTRING];
264
944
  for (int dotsPos = 0; dotsPos < length && bufPos < (MAXSTRING - 1); dotsPos++) {
265
7.55k
    for (int mappingPos = 0; dotMapping[mappingPos].key; mappingPos++) {
266
7.08k
      if ((dots[dotsPos] & dotMapping[mappingPos].key) &&
267
509
          (bufPos < (MAXSTRING - 1)))
268
509
        scratchBuf[bufPos++] = dotMapping[mappingPos].value;
269
7.08k
    }
270
472
    if ((dots[dotsPos] == LOU_DOTS) && (bufPos < (MAXSTRING - 1)))
271
11
      scratchBuf[bufPos++] = '0';
272
472
    if ((dotsPos != length - 1) && (bufPos < (MAXSTRING - 1)))
273
0
      scratchBuf[bufPos++] = '-';
274
472
  }
275
472
  scratchBuf[bufPos] = 0;
276
472
  return scratchBuf;
277
472
}
278
279
/**
280
 * Mapping between character attribute and textual representation
281
 */
282
static const intCharTupple attributeMapping[] = {
283
  { CTC_Space, 's' },
284
  { CTC_Letter, 'l' },
285
  { CTC_Digit, 'd' },
286
  { CTC_Punctuation, 'p' },
287
  { CTC_UpperCase, 'U' },
288
  { CTC_LowerCase, 'u' },
289
  { CTC_Math, 'm' },
290
  { CTC_Sign, 'S' },
291
  { CTC_LitDigit, 'D' },
292
  { CTC_UserDefined9, 'w' },
293
  { CTC_UserDefined10, 'x' },
294
  { CTC_UserDefined11, 'y' },
295
  { CTC_UserDefined12, 'z' },
296
  { 0, 0 },
297
};
298
299
/**
300
 * Show attributes using the letters used after the $ in multipass
301
 * opcodes.
302
 */
303
char *EXPORT_CALL
304
0
_lou_showAttributes(TranslationTableCharacterAttributes a) {
305
0
  int bufPos = 0;
306
0
  static char scratchBuf[MAXSTRING];
307
0
  for (int mappingPos = 0; attributeMapping[mappingPos].key; mappingPos++) {
308
0
    if ((a & attributeMapping[mappingPos].key) && bufPos < (MAXSTRING - 1))
309
0
      scratchBuf[bufPos++] = attributeMapping[mappingPos].value;
310
0
  }
311
0
  scratchBuf[bufPos] = 0;
312
0
  return scratchBuf;
313
0
}
314
315
void EXPORT_CALL
316
0
_lou_outOfMemory(void) {
317
0
  _lou_logMessage(LOU_LOG_FATAL, "liblouis: Insufficient memory\n");
318
0
  exit(3);
319
0
}
320
321
#ifdef DEBUG
322
void EXPORT_CALL
323
_lou_debugHook(void) {
324
  char *hook = "debug hook";
325
  printf("%s\n", hook);
326
}
327
#endif
328
329
static const int validTranslationModes[] = { noContractions, compbrlAtCursor, dotsIO,
330
  compbrlLeftCursor, ucBrl, noUndefined, partialTrans };
331
332
int EXPORT_CALL
333
487
_lou_isValidMode(int mode) {
334
  // mask out all valid mode bits. If you end up with some bits set
335
  // then the input isn't valid. See
336
  // https://en.wikipedia.org/wiki/Material_nonimplication
337
487
  for (size_t i = 0;
338
3.89k
      i < (sizeof(validTranslationModes) / sizeof(*validTranslationModes)); i++)
339
3.40k
    mode &= ~validTranslationModes[i];
340
487
  return !mode;
341
487
}
342
343
/* Map char to dots according to North American Braille Computer Code (NABCC) */
344
widechar EXPORT_CALL
345
3.04M
_lou_charToFallbackDots(widechar c) {
346
3.04M
  static const unsigned char charToDots[] = {
347
    /* ASCII characters 0X00-0X1F - control characters.
348
     * These won't be referenced so we have room for data.
349
     * These groups must be in descending order.
350
     * Each group contains the following four bytes:
351
     * 1) The first character to which this block applies.
352
     * 2) The bits to remove from the character.
353
     * 3) The bits to add to the character.
354
     * 4) The dots to add to the braille pattern.
355
     */
356
    // clang-format off
357
3.04M
    0X7F, 0X20, 0X00, LOU_DOT_7,
358
3.04M
    0X60, 0X20, 0X00, 0,
359
3.04M
    0X5F, 0X00, 0X00, 0,
360
3.04M
    0X40, 0X00, 0X00, LOU_DOT_7,
361
3.04M
    0X20, 0X00, 0X00, 0,
362
3.04M
    0X00, 0X00, 0X40, LOU_DOT_7 | LOU_DOT_8,
363
364
    // ASCII characters 0X20-0X3F - digits and common symbols.
365
3.04M
    [' '] = 0,
366
3.04M
    ['!'] = LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_6,
367
3.04M
    ['"'] = LOU_DOT_5,
368
3.04M
    ['#'] = LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_5 | LOU_DOT_6,
369
3.04M
    ['$'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_4 | LOU_DOT_6,
370
3.04M
    ['%'] = LOU_DOT_1 | LOU_DOT_4 | LOU_DOT_6,
371
3.04M
    ['&'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_6,
372
3.04M
    ['\''] = LOU_DOT_3,
373
3.04M
    ['('] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_5 | LOU_DOT_6,
374
3.04M
    [')'] = LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_5 | LOU_DOT_6,
375
3.04M
    ['*'] = LOU_DOT_1 | LOU_DOT_6,
376
3.04M
    ['+'] = LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_6,
377
3.04M
    [','] = LOU_DOT_6,
378
3.04M
    ['-'] = LOU_DOT_3 | LOU_DOT_6,
379
3.04M
    ['.'] = LOU_DOT_4 | LOU_DOT_6,
380
3.04M
    ['/'] = LOU_DOT_3 | LOU_DOT_4,
381
3.04M
    ['0'] = LOU_DOT_3 | LOU_DOT_5 | LOU_DOT_6,
382
3.04M
    ['1'] = LOU_DOT_2,
383
3.04M
    ['2'] = LOU_DOT_2 | LOU_DOT_3,
384
3.04M
    ['3'] = LOU_DOT_2 | LOU_DOT_5,
385
3.04M
    ['4'] = LOU_DOT_2 | LOU_DOT_5 | LOU_DOT_6,
386
3.04M
    ['5'] = LOU_DOT_2 | LOU_DOT_6,
387
3.04M
    ['6'] = LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_5,
388
3.04M
    ['7'] = LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_5 | LOU_DOT_6,
389
3.04M
    ['8'] = LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_6,
390
3.04M
    ['9'] = LOU_DOT_3 | LOU_DOT_5,
391
3.04M
    [':'] = LOU_DOT_1 | LOU_DOT_5 | LOU_DOT_6,
392
3.04M
    [';'] = LOU_DOT_5 | LOU_DOT_6,
393
3.04M
    ['<'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_6,
394
3.04M
    ['='] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_5 | LOU_DOT_6,
395
3.04M
    ['>'] = LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_5,
396
3.04M
    ['?'] = LOU_DOT_1 | LOU_DOT_4 | LOU_DOT_5 | LOU_DOT_6,
397
398
    // ASCII characters 0X40-0X5F - letters and other symbols.
399
3.04M
    ['@'] = LOU_DOT_4,
400
3.04M
    ['A'] = LOU_DOT_1,
401
3.04M
    ['B'] = LOU_DOT_1 | LOU_DOT_2,
402
3.04M
    ['C'] = LOU_DOT_1 | LOU_DOT_4,
403
3.04M
    ['D'] = LOU_DOT_1 | LOU_DOT_4 | LOU_DOT_5,
404
3.04M
    ['E'] = LOU_DOT_1 | LOU_DOT_5,
405
3.04M
    ['F'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_4,
406
3.04M
    ['G'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_4 | LOU_DOT_5,
407
3.04M
    ['H'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_5,
408
3.04M
    ['I'] = LOU_DOT_2 | LOU_DOT_4,
409
3.04M
    ['J'] = LOU_DOT_2 | LOU_DOT_4 | LOU_DOT_5,
410
3.04M
    ['K'] = LOU_DOT_1 | LOU_DOT_3,
411
3.04M
    ['L'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_3,
412
3.04M
    ['M'] = LOU_DOT_1 | LOU_DOT_3 | LOU_DOT_4,
413
3.04M
    ['N'] = LOU_DOT_1 | LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_5,
414
3.04M
    ['O'] = LOU_DOT_1 | LOU_DOT_3 | LOU_DOT_5,
415
3.04M
    ['P'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_4,
416
3.04M
    ['Q'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_5,
417
3.04M
    ['R'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_5,
418
3.04M
    ['S'] = LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_4,
419
3.04M
    ['T'] = LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_5,
420
3.04M
    ['U'] = LOU_DOT_1 | LOU_DOT_3 | LOU_DOT_6,
421
3.04M
    ['V'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_3 | LOU_DOT_6,
422
3.04M
    ['W'] = LOU_DOT_2 | LOU_DOT_4 | LOU_DOT_5 | LOU_DOT_6,
423
3.04M
    ['X'] = LOU_DOT_1 | LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_6,
424
3.04M
    ['Y'] = LOU_DOT_1 | LOU_DOT_3 | LOU_DOT_4 | LOU_DOT_5 | LOU_DOT_6,
425
3.04M
    ['Z'] = LOU_DOT_1 | LOU_DOT_3 | LOU_DOT_5 | LOU_DOT_6,
426
3.04M
    ['['] = LOU_DOT_2 | LOU_DOT_4 | LOU_DOT_6,
427
3.04M
    ['\\'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_5 | LOU_DOT_6,
428
3.04M
    [']'] = LOU_DOT_1 | LOU_DOT_2 | LOU_DOT_4 | LOU_DOT_5 | LOU_DOT_6,
429
3.04M
    ['^'] = LOU_DOT_4 | LOU_DOT_5,
430
3.04M
    ['_'] = LOU_DOT_4 | LOU_DOT_5 | LOU_DOT_6
431
    // clang-format on
432
3.04M
  };
433
434
3.04M
  if (c >= 0X80) c = '?';
435
3.04M
  widechar dots = LOU_DOTS;
436
437
3.04M
  {
438
3.04M
    const unsigned char *p = charToDots;
439
13.3M
    while (*p > c) p += 4;
440
441
3.04M
    c &= ~*++p;
442
3.04M
    c |= *++p;
443
3.04M
    dots |= *++p;
444
3.04M
  }
445
446
3.04M
  dots |= charToDots[c];
447
3.04M
  return dots;
448
3.04M
}