• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ascii-boxes / boxes / 7076187031

03 Dec 2023 10:13AM UTC coverage: 86.336% (+4.7%) from 81.608%
7076187031

push

github

tsjensen
Updates based on @msaft's comments

2485 of 3057 branches covered (0.0%)

Branch coverage included in aggregate %.

3998 of 4452 relevant lines covered (89.8%)

12453.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.52
/src/tools.c
1
/*
2
 * boxes - Command line filter to draw/remove ASCII boxes around text
3
 * Copyright (c) 1999-2023 Thomas Jensen and the boxes contributors
4
 *
5
 * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
6
 * License, version 3, as published by the Free Software Foundation.
7
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
8
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
9
 * details.
10
 * You should have received a copy of the GNU General Public License along with this program.
11
 * If not, see <https://www.gnu.org/licenses/>.
12
 *
13
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14
 */
15

16
/*
17
 * Provide tool functions for error reporting and some string handling
18
 */
19

20
#include "config.h"
21

22
#include <errno.h>
23
#include <stdarg.h>
24
#include <stdio.h>
25
#include <stdlib.h>
26
#include <string.h>
27
#include <strings.h>
28
#include <uniconv.h>
29
#include <unictype.h>
30
#include <unistr.h>
31
#include <unitypes.h>
32
#include <uniwidth.h>
33

34
#include "boxes.h"
35
#include "regulex.h"
36
#include "shape.h"
37
#include "tools.h"
38
#include "unicode.h"
39

40

41

42
static pcre2_code *pattern_ascii_id = NULL;
43
static pcre2_code *pattern_ascii_id_strict = NULL;
44

45
/**
46
 * Initialize the `bx_fprintf` function pointer to point to the original
47
 * `bx_fprintf` function, now renamed `bx_fprintf_original`. During unit
48
 * tests, this will be replaced with `__wrap_bx_fprintf`, which stores
49
 * the result that would have been printed so the output can be validated.
50
 * This is necessary for unit testing and CI to work with MacOS.
51
 */
52
bx_fprintf_t bx_fprintf = bx_fprintf_original;
53

54

55
static pcre2_code *get_pattern_ascii_id(int strict)
1,214✔
56
{
57
    pcre2_code *result = NULL;
1,214✔
58
    if (strict) {
1,214✔
59
        if (pattern_ascii_id_strict == NULL) {
1,194✔
60
            pattern_ascii_id_strict = compile_pattern("^(?!.*?--|none)[a-z][a-z0-9-]*(?<!-)$");
252✔
61
        }
126✔
62
        result = pattern_ascii_id_strict;
1,194✔
63
    }
597✔
64
    else {
65
        if (pattern_ascii_id == NULL) {
20✔
66
            pattern_ascii_id = compile_pattern("^(?!.*?[-_]{2,}|none)[a-zA-Z][a-zA-Z0-9_-]*(?<![-_])$");
2✔
67
        }
1✔
68
        result = pattern_ascii_id;
20✔
69
    }
70
    return result;
1,214✔
71
}
72

73

74

75
int strisyes(const char *s)
50✔
76
/*
77
 *  Determine if the string s has a contents indicating "yes".
78
 *
79
 *     s   string to examine
80
 *
81
 *  RETURNS:  == 0  string does NOT indicate yes (including errors)
82
 *            != 0  string indicates yes
83
 *
84
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
85
 */
86
{
87
    if (s == NULL) {
50✔
88
        return 0;
2✔
89
    }
90

91
    if (!strncasecmp("on", s, 3)) {
48✔
92
        return 1;
4✔
93
    }
94
    else if (!strncasecmp("yes", s, 4)) {
44✔
95
        return 1;
4✔
96
    }
97
    else if (!strncasecmp("true", s, 5)) {
40✔
98
        return 1;
8✔
99
    }
100
    else if (!strncmp("1", s, 2)) {
32✔
101
        return 1;
2✔
102
    }
103
    else if (!strncasecmp("t", s, 2)) {
30✔
104
        return 1;
4✔
105
    }
106
    else {
107
        return 0;
26✔
108
    }
109
}
25✔
110

111

112

113
int strisno(const char *s)
46✔
114
/*
115
 *  Determine if the string s has a contents indicating "no".
116
 *
117
 *     s   string to examine
118
 *
119
 *  RETURNS:  == 0  string does NOT indicate no (including errors)
120
 *            != 0  string indicates no
121
 *
122
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
123
 */
124
{
125
    if (s == NULL) {
46✔
126
        return 0;
2✔
127
    }
128

129
    if (!strncasecmp("off", s, 4)) {
44✔
130
        return 1;
4✔
131
    }
132
    else if (!strncasecmp("no", s, 3)) {
40✔
133
        return 1;
4✔
134
    }
135
    else if (!strncasecmp("false", s, 6)) {
36✔
136
        return 1;
6✔
137
    }
138
    else if (!strncmp("0", s, 2)) {
30✔
139
        return 1;
2✔
140
    }
141
    else if (!strncasecmp("f", s, 2)) {
28✔
142
        return 1;
4✔
143
    }
144
    else {
145
        return 0;
24✔
146
    }
147
}
23✔
148

149

150

151
void concat_strings(char *dst, int max_len, int count, ...)
×
152
/*
153
 *  Concatenate a variable number of strings into a fixed-length buffer.
154
 *
155
 *      dst     Destination array
156
 *      max_len Maximum resulting string length (including terminating NULL).
157
 *      count   Number of source strings.
158
 *
159
 *  The concatenation process terminates when either the destination
160
 *  buffer is full or all 'count' strings are processed.  Null string
161
 *  pointers are treated as empty strings.
162
 *
163
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
164
 */
165
{
166
    va_list va;
167
    const char *src;
168

169
    va_start(va, count);
×
170

171
    /*
172
     *  Sanity check.
173
     */
174
    if (max_len < 1) {
×
175
        return;
×
176
    }
177

178
    if (max_len == 1 || count < 1) {
×
179
        *dst = '\0';
×
180
        return;
×
181
    }
182

183
    /*
184
     *  Loop over all input strings.
185
     */
186
    while (count-- > 0 && max_len > 1) {
×
187
        /*
188
         * Grab an input string pointer.  If it's NULL, skip it (eg. treat
189
         * it as empty.
190
         */
191
        src = va_arg(va, const char *);
×
192

193
        if (src == NULL) {
×
194
            continue;
×
195
        }
196

197
        /*
198
         * Concatenate 'src' onto 'dst', as long as we have room.
199
         */
200
        while (*src && max_len > 1) {
×
201
            *dst++ = *src++;
×
202
            max_len--;
×
203
        }
204
    }
205

206
    va_end(va);
×
207

208
    /*
209
     * Terminate the string with an ASCII NUL.
210
     */
211
    *dst = '\0';
×
212
}
213

214

215

216
char *concat_strings_alloc(size_t count, ...)
59✔
217
{
218
    if (count < 1) {
59!
219
        return strdup("");
×
220
    }
221

222
    size_t total_len = 0;
59✔
223
    const char *src;
224
    va_list va;
225

226
    va_start(va, count);
59✔
227
    for (size_t i = 0; i < count; i++) {
219✔
228
        src = va_arg(va, const char *);
160!
229
        if (src != NULL) {
160✔
230
            total_len += strlen(src);
160✔
231
        }
79✔
232
    }
79✔
233
    va_end(va);
59✔
234

235
    char *result = malloc(total_len + 1);
59✔
236
    char *p = result;
59✔
237

238
    va_start(va, count);
59✔
239
    for (size_t i = 0; i < count; i++) {
219✔
240
        src = va_arg(va, const char *);
160!
241
        if (src != NULL && src[0] != '\0') {
160!
242
            strcpy(p, src);
154✔
243
            p += strlen(src);
154✔
244
        }
76✔
245
    }
79✔
246
    va_end(va);
59✔
247

248
    *p = '\0';
59✔
249
    return result;
59✔
250
}
29✔
251

252

253

254
char *repeat(char *s, size_t count)
2,098✔
255
{
256
    if (s == NULL) {
2,098✔
257
        return NULL;
2✔
258
    }
259

260
    size_t len = strlen(s);
2,096✔
261
    char *result = (char *) malloc(count * len + 1);
2,096✔
262
    if (result != NULL) {
2,096✔
263
        char *dest = result;
2,096✔
264
        for (size_t i = 0; i < count; i++) {
15,310✔
265
            strcpy(dest, s);
13,214✔
266
            dest += len;
13,214✔
267
        }
6,607✔
268
        *dest = '\0';
2,096✔
269
    }
1,048✔
270
    return result;
2,096✔
271
}
1,049✔
272

273

274

275
int empty_line(const line_t *line)
4,562✔
276
/*
277
 *  Return true if line is empty.
278
 *
279
 *  Empty lines either consist entirely of whitespace or don't exist.
280
 *
281
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
282
 */
283
{
284
    char *p;
285
    size_t j;
286

287
    if (!line) {
4,562✔
288
        return 1;
×
289
    }
290
    if (line->text == NULL || line->len <= 0) {
4,562!
291
        return 1;
40✔
292
    }
293

294
    for (p = line->text, j = 0; *p && j < line->len; ++j, ++p) {
9,384✔
295
        if (*p != ' ' && *p != '\t' && *p != '\r') {
8,124!
296
            return 0;
3,262✔
297
        }
298
    }
2,431✔
299
    return 1;
1,260✔
300
}
2,281✔
301

302

303

304
size_t expand_tabs_into(const uint32_t *input_buffer, const int tabstop, uint32_t **text, size_t **tabpos,
1,226✔
305
        size_t *tabpos_len)
306
/*
307
 *  Expand tab chars in input_buffer and store result in text.
308
 *
309
 *  input_buffer   Line of text with tab chars
310
 *  tabstop        tab stop distance
311
 *  text           address of the pointer that will take the result
312
 *  tabpos         array of ints giving the positions of the first
313
 *                 space of an expanded tab in the text result buffer
314
 *  tabpos_len     number of tabs recorded in tabpos
315
 *
316
 *  Memory will be allocated for text and tabpos.
317
 *  Should only be called for lines of length > 0;
318
 *
319
 *  RETURNS:  Success: Length of the result line in characters (> 0)
320
 *            Error:   0       (e.g. out of memory)
321
 *
322
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
323
 */
324
{
325
    static uint32_t temp[LINE_MAX_BYTES + 100]; /* work string */
326
    size_t io;                                  /* character position in work string */
327
    size_t tabnum = 0;                          /* index of the current tab */
1,226✔
328

329
    *text = NULL;
1,226✔
330
    *tabpos = NULL;
1,226✔
331
    *tabpos_len = 0;
1,226✔
332

333
    if (opt.tabexp == 'k') {
1,226✔
334
        /* We need to know the exact tab positions only if expansion type 'k' is requested (keep tabs as much as they
335
         * were as possible). Else we'll just convert spaces and tabs without having to know where exactly the tabs
336
         * were in the first place. */
337
        ucs4_t puc;
338
        const uint32_t *rest = input_buffer;
18✔
339
        while ((rest = u32_next(&puc, rest))) {
200✔
340
            if (puc == char_tab) {
182✔
341
                (*tabpos_len)++;
10✔
342
            }
5✔
343
        }
344
    }
9✔
345

346
    if (*tabpos_len > 0) {
1,226✔
347
        *tabpos = (size_t *) calloc((*tabpos_len) + 1, sizeof(size_t));
6✔
348
        if (*tabpos == NULL) {
6!
349
            return 0; /* out of memory */
×
350
        }
351
    }
3✔
352

353
    ucs4_t puc;
354
    const uint32_t *rest = input_buffer;
1,226✔
355
    io = 0;
1,226✔
356
    while ((rest = u32_next(&puc, rest)) && io < (LINE_MAX_BYTES - 12)) {
61,788✔
357
        if (puc == char_tab) {
60,562✔
358
            if (*tabpos_len > 0) {
1,050✔
359
                (*tabpos)[tabnum++] = io;
10✔
360
            }
5✔
361
            size_t num_spc = tabstop - (io % tabstop);
1,050✔
362
            u32_set(temp + io, char_space, num_spc);
1,050✔
363
            io += num_spc;
1,050✔
364
        }
525✔
365
        else {
366
            set_char_at(temp, io, puc);
59,512✔
367
            ++io;
59,512✔
368
        }
369
    }
370
    temp[io] = 0;
1,226✔
371

372
    *text = u32_strdup(temp);
1,226✔
373
    if (*text == NULL) {
1,226✔
374
        return 0;
×
375
    }
376
    return io;
1,226✔
377
}
613✔
378

379

380

381
void btrim(char *text, size_t *len)
1,092✔
382
/*
383
 *  Remove trailing whitespace from line.
384
 *
385
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
386
 */
387
{
388
    long idx = (long) (*len) - 1;
1,092✔
389

390
    while (idx >= 0 && (text[idx] == '\n' || text[idx] == '\r' || text[idx] == '\t' || text[idx] == ' ')) {
3,470!
391
        text[idx--] = '\0';
1,832✔
392
    }
393

394
    *len = idx + 1;
1,092✔
395
}
1,092✔
396

397

398

399
void btrim32(uint32_t *text, size_t *len)
1,226✔
400
/*
401
 *  Remove trailing whitespace from line (unicode and escape sequence enabled version).
402
 *  CHECK replace by bxs_rtrim() from bxstring module
403
 *
404
 *      text     string to trim
405
 *      len      pointer to the length of the string in characters
406
 *
407
 *  Both the string and the length will be modified as trailing whitespace is removed.
408
 *
409
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
410
 */
411
{
412
    if (text == NULL || len == 0) {
1,226!
413
        return;
×
414
    }
415

416
    const uint32_t *rest = text;
1,226✔
417
    int last_char_pos = -1;
1,226✔
418
    size_t step_invis;
419

420
    for (ucs4_t c = text[0]; c != char_nul; c = rest[0]) {
37,402✔
421
        if (c != char_esc) {
36,176✔
422
            if (!uc_is_c_whitespace(c) && !uc_is_property_white_space(c) && !uc_is_property_bidi_whitespace(c)) {
32,300!
423
                last_char_pos = (int) (rest - text);
24,156✔
424
            }
12,078✔
425
        }
16,150✔
426
        rest = advance_next32(rest, &step_invis);
36,176✔
427
    }
18,088✔
428

429
    /* If the last character is followed by an escape sequence, keep it (but only one). */
430
    if (last_char_pos >= 0) {
1,226✔
431
        rest = text + last_char_pos + 1;
1,186✔
432
        if (rest[0] == char_esc) {
1,186✔
433
            advance_next32(rest, &step_invis);
210✔
434
            last_char_pos += step_invis;
210✔
435
        }
105✔
436
    }
593✔
437

438
    set_char_at(text, (size_t) (last_char_pos + 1), char_nul);
1,226✔
439
    *len = (size_t) (last_char_pos + 1);
1,226✔
440
}
613✔
441

442

443

444
char *my_strnrstr(const char *s1, const char *s2, const size_t s2_len, int skip)
484✔
445
/*
446
 *  Return pointer to last occurrence of string s2 in string s1.
447
 *
448
 *      s1       string to search
449
 *      s2       string to search for in s1
450
 *      s2_len   length in characters of s2
451
 *      skip     number of finds to ignore before returning anything
452
 *
453
 *  RETURNS: pointer to last occurrence of string s2 in string s1
454
 *           NULL if not found or error
455
 *
456
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
457
 */
458
{
459
    char *p;
460
    int comp;
461

462
    if (!s2 || *s2 == '\0') {
484!
463
        return (char *) s1;
×
464
    }
465
    if (!s1 || *s1 == '\0') {
484!
466
        return NULL;
×
467
    }
468
    if (skip < 0) {
484✔
469
        skip = 0;
×
470
    }
471

472
    p = strrchr(s1, s2[0]);
484✔
473
    if (!p) {
484✔
474
        return NULL;
×
475
    }
476

477
    while (p >= s1) {
20,488✔
478
        comp = strncmp(p, s2, s2_len);
20,160✔
479
        if (comp == 0) {
20,160✔
480
            if (skip--) {
468✔
481
                --p;
312✔
482
            }
156✔
483
            else {
484
                return p;
156✔
485
            }
486
        }
156✔
487
        else {
488
            --p;
19,692✔
489
        }
490
    }
491

492
    return NULL;
328✔
493
}
242✔
494

495

496

497
size_t my_strrspn(const char *s, const char *accept)
1,938✔
498
{
499
    if (!s || *s == '\0') {
1,938✔
500
        return 0;
14✔
501
    }
502
    if (!accept || *accept == '\0') {
1,924✔
503
        return 0;
4✔
504
    }
505

506
    for (int i = strlen(s) - 1; i >= 0; i--) {
4,166✔
507
        size_t idx = (size_t) i;
2,746✔
508
        if (strchr(accept, s[idx]) == NULL) {
2,746✔
509
            return strlen(s) - (idx + 1);
500✔
510
        }
511
    }
1,123✔
512
    return strlen(s);
1,420✔
513
}
969✔
514

515

516

517
uint32_t *tabbify_indent(const size_t lineno, uint32_t *indentspc, const size_t indentspc_len)
2,086✔
518
{
519
    size_t i;
520
    uint32_t *result;
521
    size_t result_len;
522

523
    if (opt.tabexp != 'k') {
2,086✔
524
        return indentspc;
2,056✔
525
    }
526
    if (lineno >= input.num_lines) {
30!
527
        return NULL;
×
528
    }
529
    if (indentspc_len == 0) {
30✔
530
        return new_empty_string32();
×
531
    }
532

533
    result = (uint32_t *) malloc((indentspc_len + 1) * sizeof(uint32_t));
30✔
534
    if (result == NULL) {
30!
535
        perror(PROJECT);
×
536
        return NULL;
×
537
    }
538
    u32_set(result, char_space, indentspc_len);
30✔
539
    set_char_at(result, indentspc_len, char_nul);
30✔
540
    result_len = indentspc_len;
30✔
541

542
    for (i = 0; i < input.lines[lineno].tabpos_len && input.lines[lineno].tabpos[i] < indentspc_len; ++i) {
36✔
543
        size_t tpos = input.lines[lineno].tabpos[i];
6✔
544
        size_t nspc = opt.tabstop - (tpos % opt.tabstop); /* no of spcs covered by tab */
6✔
545
        if (tpos + nspc > input.indent) {
6✔
546
            break;
×
547
        }
548
        set_char_at(result, tpos, char_tab);
6✔
549
        result_len -= nspc - 1;
6✔
550
        set_char_at(result, result_len, char_nul);
6✔
551
    }
3✔
552

553
    return result;
30✔
554
}
1,043✔
555

556

557

558
char *nspaces(const size_t n)
1,416✔
559
{
560
    char *spaces = (char *) memset(malloc(n + 1), (int) ' ', n);
1,416✔
561
    spaces[n] = '\0';
1,416✔
562
    return spaces;
1,416✔
563
}
564

565

566

567
#if defined(DEBUG) || 0
568

569
/**
570
 *  Debugging Code: Display contents of input structure
571
 */
572
void print_input_lines(const char *heading)
573
{
574
    fprintf(stderr, "Input Lines%s:\n", heading != NULL ? heading : "");
575
    fprintf(stderr, "     [num_chars] \"real text\" [num_cols] \"ascii_text\"\n");
576
    for (size_t i = 0; i < input.num_lines; ++i) {
577
        fprintf(stderr, "%4d [%02d] \"%s\"  [%02d] \"%s\"", (int) i,
578
                (int) input.lines[i].num_chars, u32_strconv_to_output(input.lines[i].mbtext),
579
                (int) input.lines[i].len, input.lines[i].text);
580
        fprintf(stderr, "\tTabs: [");
581
        if (input.lines[i].tabpos != NULL) {
582
            for (size_t j = 0; j < input.lines[i].tabpos_len; ++j) {
583
                fprintf(stderr, "%d", (int) input.lines[i].tabpos[j]);
584
                if (j < input.lines[i].tabpos_len - 1) {
585
                    fprintf(stderr, ", ");
586
                }
587
            }
588
        }
589
        fprintf(stderr, "] (%d)", (int) input.lines[i].tabpos_len);
590
        fprintf(stderr, "\tinvisible=%d\n", (int) input.lines[i].invis);
591

592
        fprintf(stderr, "          posmap=");
593
        if (input.lines[i].posmap != NULL) {
594
            fprintf(stderr, "[");
595
            for (size_t j = 0; j < input.lines[i].len; j++) {
596
                fprintf(stderr, "%d%s", (int) input.lines[i].posmap[j], j == (input.lines[i].len - 1) ? "" : ", ");
597
            }
598
            fprintf(stderr, "]\n");
599
        }
600
        else {
601
            fprintf(stderr, "null\n");
602
        }
603
    }
604
    fprintf(stderr, " Longest line: %d columns\n", (int) input.maxline);
605
    fprintf(stderr, "  Indentation: %2d spaces\n", (int) input.indent);
606
    fprintf(stderr, "Final newline: %s\n", input.final_newline ? "yes" : "no");
607
}
608

609
#endif
610

611

612

613
/**
614
 * Analyze the multi-byte string in order to determine its metrics:
615
 * - number of visible columns it occupies
616
 * - number of escape characters (== number of escape sequences)
617
 * - the ASCII equivalent of the string
618
 * - the number of invisible characters in the string
619
 *
620
 * @param <s> the multi-byte string to analyze
621
 * @param <num_esc> pointer to where the number of escape sequences should be stored
622
 * @param <ascii> pointer to where the ASCII equivalent of the string should be stored
623
 * @param <posmap> pointer to the position map, which maps each position in <ascii> to a position in <s>
624
 * @returns the number of invisible characters in <s>
625
 */
626
size_t count_invisible_chars(const uint32_t *s, size_t *num_esc, char **ascii, size_t **posmap)
4,400✔
627
{
628
    size_t invis = 0; /* counts invisible column positions */
4,400✔
629
    *num_esc = 0;     /* counts the number of escape sequences found */
4,400✔
630

631
    if (is_empty(s)) {
4,400✔
632
        (*ascii) = (char *) strdup("");
74✔
633
        (*posmap) = NULL;
74✔
634
        return 0;
74✔
635
    }
636

637
    size_t buflen = (size_t) u32_strwidth(s, encoding) + 1;
4,326✔
638
    size_t map_size = BMAX((size_t) 5, buflen);
4,326✔
639
    size_t map_idx = 0;
4,326✔
640
    size_t *map = (size_t *) calloc(map_size, sizeof(size_t)); /* might not be enough if many double-wide chars */
4,326✔
641
    (*ascii) = (char *) calloc(buflen, sizeof(char));          /* maybe a little too much, but certainly enough */
4,326✔
642
    char *p = *ascii;
4,326✔
643

644
    size_t mb_idx = 0;
4,326✔
645
    size_t step_invis;
646
    const uint32_t *rest = s;
4,326✔
647

648
    for (ucs4_t c = s[0]; c != char_nul; c = rest[0]) {
94,586✔
649
        if (map_idx >= map_size - 4) {
90,260✔
650
            map_size = map_size * 2 + 1;
2,708✔
651
            map = (size_t *) realloc(map, map_size * sizeof(size_t));
2,708✔
652
        }
1,354✔
653

654
        if (c == char_esc) {
90,260✔
655
            (*num_esc)++;
6,876✔
656
        }
3,438✔
657
        else if (is_ascii_printable(c)) {
83,384✔
658
            *p = c & 0xff;
82,364✔
659
            map[map_idx++] = mb_idx;
82,364✔
660
            ++p;
82,364✔
661
        }
41,182✔
662
        else {
663
            int cols = uc_width(c, encoding);
1,020✔
664
            if (cols > 0) {
1,020✔
665
                memset(p, (int) 'x', cols);
1,020✔
666
                for (int i = 0; i < cols; i++) {
2,604✔
667
                    map[map_idx++] = mb_idx;
1,584✔
668
                }
792✔
669
                p += cols;
1,020✔
670
            }
510✔
671
        }
672

673
        rest = advance_next32(rest, &step_invis);
90,260✔
674

675
        mb_idx += BMAX((size_t) 1, step_invis);
90,260✔
676
        invis += step_invis;
90,260✔
677
    }
45,130✔
678

679
    *p = '\0';
4,326✔
680
    (*posmap) = map;
4,326✔
681
    return invis;
4,326✔
682
}
2,200✔
683

684

685

686
int is_csi_reset(const uint32_t *csi)
3,146✔
687
{
688
    ucs4_t puc = '\0';
3,146✔
689
    const uint32_t *rest = csi;
3,146✔
690
    size_t csi_pos = 0;
3,146✔
691
    while ((rest = u32_next(&puc, rest))) {
10,966✔
692
        switch(csi_pos) {
10,960✔
693
            case 0:
1,572✔
694
                if (puc != char_esc) {
3,144✔
695
                    return 0;
4✔
696
                }
697
                break;
3,140✔
698
            case 1:
1,570✔
699
                if (puc != '[' && puc != '(') {
3,140✔
700
                    return 0;
2✔
701
                }
702
                break;
3,138✔
703
            case 2:
1,567✔
704
                if (puc != '0') {
3,134✔
705
                    if (puc >= 0x40 && puc <= 0x7e) {
1,592!
706
                        return 1;
14✔
707
                    }
708
                    return 0;
1,578✔
709
                }
710
                break;
1,542✔
711
            default:
771✔
712
                return (puc >= 0x40 && puc <= 0x7e) ? 1 : 0;
1,542!
713
        }
714
        csi_pos++;
7,820✔
715
    }
716
    return 0;
6✔
717
}
1,573✔
718

719

720

721
void analyze_line_ascii(input_t *input_ptr, line_t *line)
2,486✔
722
{
723
    size_t num_esc = 0;
2,486✔
724
    char *ascii;
725
    size_t *map;
726
    size_t invis = count_invisible_chars(line->mbtext, &num_esc, &ascii, &(map));
2,486✔
727
    line->invis = invis;
2,486✔
728
    /* u32_strwidth() does not count control characters, i.e. ESC characters, for which we must correct */
729
    line->len = u32_strwidth(line->mbtext, encoding) - invis + num_esc;
2,486✔
730
    line->num_chars = u32_strlen(line->mbtext);
2,486✔
731
    BFREE(line->text);
2,486✔
732
    line->text = ascii;
2,486✔
733
    BFREE(line->posmap);
2,486✔
734
    line->posmap = map;
2,486✔
735

736
    if (line->len > input_ptr->maxline) {
2,486✔
737
        input_ptr->maxline = line->len;
518✔
738
    }
259✔
739
}
2,486✔
740

741

742

743
int array_contains(char **array, const size_t array_len, const char *s)
×
744
{
745
    int result = 0;
×
746
    if (array != NULL && array_len > 0) {
×
747
        for (size_t i = 0; i < array_len; ++i) {
×
748
            if (strcmp(array[i], s) == 0) {
×
749
                result = 1;
×
750
                break;
×
751
            }
752
        }
753
    }
754
    return result;
×
755
}
756

757

758

759
int array_contains0(char **array, const char *s)
1,286✔
760
{
761
    int result = 0;
1,286✔
762
    if (array != NULL) {
1,286✔
763
        for (size_t i = 0; array[i] != NULL; ++i) {
2,510✔
764
            if (strcasecmp(array[i], s) == 0) {
1,288✔
765
                result = 1;
64✔
766
                break;
64✔
767
            }
768
        }
612✔
769
    }
643✔
770
    return result;
1,286✔
771
}
772

773

774

775
int array_contains_bxs(bxstr_t **array, const size_t array_len, bxstr_t *s)
52✔
776
{
777
    int result = 0;
52✔
778
    if (array != NULL && array_len > 0) {
52✔
779
        for (size_t i = 0; i < array_len; ++i) {
30✔
780
            if (bxs_strcmp(array[i], s) == 0) {
18✔
781
                result = 1;
2✔
782
                break;
2✔
783
            }
784
        }
8✔
785
    }
7✔
786
    return result;
52✔
787
}
788

789

790

791
size_t array_count0(char **array)
1,212✔
792
{
793
    size_t num_elems = 0;
1,212✔
794
    if (array != NULL) {
1,212!
795
        while (array[num_elems] != NULL) {
2,268✔
796
            ++num_elems;
1,056✔
797
        }
798
    }
606✔
799
    return num_elems;
1,212✔
800
}
801

802

803

804
char *trimdup(char *s, char *e)
36✔
805
{
806
    if (s > e || (s == e && *s == '\0')) {
36!
807
        return strdup("");
×
808
    }
809
    while (s <= e && (*s == ' ' || *s == '\t')) {
54!
810
        ++s;
×
811
    }
812
    while (e > s && (*e == ' ' || *e == '\t')) {
54!
813
        --e;
×
814
    }
815
    return bx_strndup(s, e - s + 1);
36✔
816
}
18✔
817

818

819

820
int tag_is_valid(char *tag)
34✔
821
{
822
    pcre2_code *pattern = get_pattern_ascii_id(1);
34✔
823
    return regex_match(pattern, tag);
34✔
824
}
825

826

827

828
int is_ascii_id(bxstr_t *s, int strict)
1,188✔
829
{
830
    if (s == NULL || s->num_chars == 0) {
1,188✔
831
        return 0;
8✔
832
    }
833
    pcre2_code *pattern = get_pattern_ascii_id(strict);
1,180✔
834
    return u32_regex_match(pattern, s->memory);
1,180✔
835
}
594✔
836

837

838

839
char *bx_strndup(const char *s, size_t n)
36✔
840
{
841
    if (s == NULL) {
36✔
842
        return NULL;
×
843
    }
844

845
    size_t len = strlen(s);
36✔
846
    if (n < len) {
36✔
847
        len = n;
×
848
    }
849

850
    char *result = (char *) malloc(len + 1);
36✔
851
    if (result == NULL) {
36!
852
        return NULL;
×
853
    }
854

855
    result[len] = '\0';
36✔
856
    return (char *) memcpy(result, s, len);
36✔
857
}
18✔
858

859

860

861
void bx_fprintf_original(FILE *stream, const char *format, ...)
16✔
862
{
863
    va_list va;
864
    va_start(va, format);
16✔
865
    vfprintf(stream, format, va);
16✔
866
    va_end(va);
16✔
867
}
16✔
868

869

870
void set_bx_fprintf(bx_fprintf_t bx_fprintf_function) {
2✔
871
    bx_fprintf = bx_fprintf_function;
2✔
872
}
2✔
873

874

875
FILE *bx_fopens(bxstr_t *pathname, char *mode)
348✔
876
{
877
    return bx_fopen(to_utf8(pathname->memory), mode);
348✔
878
}
879

880

881

882
FILE *bx_fopen(char *pathname, char *mode)
698✔
883
{
884
    /*
885
     * On Linux/UNIX and OS X (Mac), one can access files with non-ASCII file names by passing them to fopen() as UTF-8.
886
     * On Windows, a different function must be called. (Info: https://stackoverflow.com/a/35065142/1005481)
887
     */
888
    FILE *f = fopen(pathname, mode);
698✔
889
    // TODO Windows
890
    return f;
698✔
891
}
892

893

894
/* vim: set sw=4: */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc