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

ascii-boxes / boxes / 5017901761

18 May 2023 08:22PM UTC coverage: 81.608% (-0.3%) from 81.947%
5017901761

push

github

Thomas Jensen
Enable terminal colors globally for tests

2190 of 2965 branches covered (73.86%)

Branch coverage included in aggregate %.

3414 of 3902 relevant lines covered (87.49%)

6909.89 hits per line

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

81.08
/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
static pcre2_code *get_pattern_ascii_id(int strict)
607✔
47
{
48
    pcre2_code *result = NULL;
607✔
49
    if (strict) {
607✔
50
        if (pattern_ascii_id_strict == NULL) {
597✔
51
            pattern_ascii_id_strict = compile_pattern("^(?!.*?--|none)[a-z][a-z0-9-]*(?<!-)$");
126✔
52
        }
53
        result = pattern_ascii_id_strict;
597✔
54
    }
55
    else {
56
        if (pattern_ascii_id == NULL) {
10✔
57
            pattern_ascii_id = compile_pattern("^(?!.*?[-_]{2,}|none)[a-zA-Z][a-zA-Z0-9_-]*(?<![-_])$");
1✔
58
        }
59
        result = pattern_ascii_id;
10✔
60
    }
61
    return result;
607✔
62
}
63

64

65

66
int strisyes(const char *s)
25✔
67
/*
68
 *  Determine if the string s has a contents indicating "yes".
69
 *
70
 *     s   string to examine
71
 *
72
 *  RETURNS:  == 0  string does NOT indicate yes (including errors)
73
 *            != 0  string indicates yes
74
 *
75
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
76
 */
77
{
78
    if (s == NULL) {
25✔
79
        return 0;
1✔
80
    }
81

82
    if (!strncasecmp("on", s, 3)) {
24✔
83
        return 1;
2✔
84
    }
85
    else if (!strncasecmp("yes", s, 4)) {
22✔
86
        return 1;
2✔
87
    }
88
    else if (!strncasecmp("true", s, 5)) {
20✔
89
        return 1;
4✔
90
    }
91
    else if (!strncmp("1", s, 2)) {
16✔
92
        return 1;
1✔
93
    }
94
    else if (!strncasecmp("t", s, 2)) {
15✔
95
        return 1;
2✔
96
    }
97
    else {
98
        return 0;
13✔
99
    }
100
}
101

102

103

104
int strisno(const char *s)
23✔
105
/*
106
 *  Determine if the string s has a contents indicating "no".
107
 *
108
 *     s   string to examine
109
 *
110
 *  RETURNS:  == 0  string does NOT indicate no (including errors)
111
 *            != 0  string indicates no
112
 *
113
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
114
 */
115
{
116
    if (s == NULL) {
23✔
117
        return 0;
1✔
118
    }
119

120
    if (!strncasecmp("off", s, 4)) {
22✔
121
        return 1;
2✔
122
    }
123
    else if (!strncasecmp("no", s, 3)) {
20✔
124
        return 1;
2✔
125
    }
126
    else if (!strncasecmp("false", s, 6)) {
18✔
127
        return 1;
3✔
128
    }
129
    else if (!strncmp("0", s, 2)) {
15✔
130
        return 1;
1✔
131
    }
132
    else if (!strncasecmp("f", s, 2)) {
14✔
133
        return 1;
2✔
134
    }
135
    else {
136
        return 0;
12✔
137
    }
138
}
139

140

141

142
void concat_strings(char *dst, int max_len, int count, ...)
×
143
/*
144
 *  Concatenate a variable number of strings into a fixed-length buffer.
145
 *
146
 *      dst     Destination array
147
 *      max_len Maximum resulting string length (including terminating NULL).
148
 *      count   Number of source strings.
149
 *
150
 *  The concatenation process terminates when either the destination
151
 *  buffer is full or all 'count' strings are processed.  Null string
152
 *  pointers are treated as empty strings.
153
 *
154
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
155
 */
156
{
157
    va_list va;
158
    const char *src;
159

160
    va_start(va, count);
×
161

162
    /*
163
     *  Sanity check.
164
     */
165
    if (max_len < 1) {
×
166
        return;
×
167
    }
168

169
    if (max_len == 1 || count < 1) {
×
170
        *dst = '\0';
×
171
        return;
×
172
    }
173

174
    /*
175
     *  Loop over all input strings.
176
     */
177
    while (count-- > 0 && max_len > 1) {
×
178
        /*
179
         * Grab an input string pointer.  If it's NULL, skip it (eg. treat
180
         * it as empty.
181
         */
182
        src = va_arg(va, const char *);
×
183

184
        if (src == NULL) {
×
185
            continue;
×
186
        }
187

188
        /*
189
         * Concatenate 'src' onto 'dst', as long as we have room.
190
         */
191
        while (*src && max_len > 1) {
×
192
            *dst++ = *src++;
×
193
            max_len--;
×
194
        }
195
    }
196

197
    va_end(va);
×
198

199
    /*
200
     * Terminate the string with an ASCII NUL.
201
     */
202
    *dst = '\0';
×
203
}
204

205

206

207
char *concat_strings_alloc(size_t count, ...)
30✔
208
{
209
    if (count < 1) {
30!
210
        return strdup("");
×
211
    }
212

213
    size_t total_len = 0;
30✔
214
    const char *src;
215
    va_list va;
216

217
    va_start(va, count);
30✔
218
    for (size_t i = 0; i < count; i++) {
111✔
219
        src = va_arg(va, const char *);
81✔
220
        if (src != NULL) {
81!
221
            total_len += strlen(src);
81✔
222
        }
223
    }
224
    va_end(va);
30✔
225

226
    char *result = malloc(total_len + 1);
30✔
227
    char *p = result;
30✔
228

229
    va_start(va, count);
30✔
230
    for (size_t i = 0; i < count; i++) {
111✔
231
        src = va_arg(va, const char *);
81✔
232
        if (src != NULL && src[0] != '\0') {
81!
233
            strcpy(p, src);
78✔
234
            p += strlen(src);
78✔
235
        }
236
    }
237
    va_end(va);
30✔
238

239
    *p = '\0';
30✔
240
    return result;
30✔
241
}
242

243

244

245
char *repeat(char *s, size_t count)
1,049✔
246
{
247
    if (s == NULL) {
1,049✔
248
        return NULL;
1✔
249
    }
250

251
    size_t len = strlen(s);
1,048✔
252
    char *result = (char *) malloc(count * len + 1);
1,048✔
253
    if (result != NULL) {
1,048!
254
        char *dest = result;
1,048✔
255
        for (size_t i = 0; i < count; i++) {
7,655✔
256
            strcpy(dest, s);
6,607✔
257
            dest += len;
6,607✔
258
        }
259
        *dest = '\0';
1,048✔
260
    }
261
    return result;
1,048✔
262
}
263

264

265

266
int empty_line(const line_t *line)
2,281✔
267
/*
268
 *  Return true if line is empty.
269
 *
270
 *  Empty lines either consist entirely of whitespace or don't exist.
271
 *
272
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
273
 */
274
{
275
    char *p;
276
    size_t j;
277

278
    if (!line) {
2,281!
279
        return 1;
×
280
    }
281
    if (line->text == NULL || line->len <= 0) {
2,281!
282
        return 1;
20✔
283
    }
284

285
    for (p = line->text, j = 0; *p && j < line->len; ++j, ++p) {
4,692!
286
        if (*p != ' ' && *p != '\t' && *p != '\r') {
4,062!
287
            return 0;
1,631✔
288
        }
289
    }
290
    return 1;
630✔
291
}
292

293

294

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

320
    *text = NULL;
613✔
321
    *tabpos = NULL;
613✔
322
    *tabpos_len = 0;
613✔
323

324
    if (opt.tabexp == 'k') {
613✔
325
        /* We need to know the exact tab positions only if expansion type 'k' is requested (keep tabs as much as they
326
         * were as possible). Else we'll just convert spaces and tabs without having to know where exactly the tabs
327
         * were in the first place. */
328
        ucs4_t puc;
329
        const uint32_t *rest = input_buffer;
9✔
330
        while ((rest = u32_next(&puc, rest))) {
100✔
331
            if (puc == char_tab) {
91✔
332
                (*tabpos_len)++;
5✔
333
            }
334
        }
335
    }
336

337
    if (*tabpos_len > 0) {
613✔
338
        *tabpos = (size_t *) calloc((*tabpos_len) + 1, sizeof(size_t));
3✔
339
        if (*tabpos == NULL) {
3!
340
            return 0; /* out of memory */
×
341
        }
342
    }
343

344
    ucs4_t puc;
345
    const uint32_t *rest = input_buffer;
613✔
346
    io = 0;
613✔
347
    while ((rest = u32_next(&puc, rest)) && io < (LINE_MAX_BYTES - 12)) {
30,894!
348
        if (puc == char_tab) {
30,281✔
349
            if (*tabpos_len > 0) {
525✔
350
                (*tabpos)[tabnum++] = io;
5✔
351
            }
352
            size_t num_spc = tabstop - (io % tabstop);
525✔
353
            u32_set(temp + io, char_space, num_spc);
525✔
354
            io += num_spc;
525✔
355
        }
356
        else {
357
            set_char_at(temp, io, puc);
29,756✔
358
            ++io;
29,756✔
359
        }
360
    }
361
    temp[io] = 0;
613✔
362

363
    *text = u32_strdup(temp);
613✔
364
    if (*text == NULL) {
613!
365
        return 0;
×
366
    }
367
    return io;
613✔
368
}
369

370

371

372
void btrim(char *text, size_t *len)
546✔
373
/*
374
 *  Remove trailing whitespace from line.
375
 *
376
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
377
 */
378
{
379
    long idx = (long) (*len) - 1;
546✔
380

381
    while (idx >= 0 && (text[idx] == '\n' || text[idx] == '\r' || text[idx] == '\t' || text[idx] == ' ')) {
1,462!
382
        text[idx--] = '\0';
916✔
383
    }
384

385
    *len = idx + 1;
546✔
386
}
546✔
387

388

389

390
void btrim32(uint32_t *text, size_t *len)
613✔
391
/*
392
 *  Remove trailing whitespace from line (unicode and escape sequence enabled version).
393
 *  CHECK replace by bxs_rtrim() from bxstring module
394
 *
395
 *      text     string to trim
396
 *      len      pointer to the length of the string in characters
397
 *
398
 *  Both the string and the length will be modified as trailing whitespace is removed.
399
 *
400
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
401
 */
402
{
403
    if (text == NULL || len == 0) {
613!
404
        return;
×
405
    }
406

407
    const uint32_t *rest = text;
613✔
408
    int last_char_pos = -1;
613✔
409
    size_t step_invis;
410

411
    for (ucs4_t c = text[0]; c != char_nul; c = rest[0]) {
18,701✔
412
        if (c != char_esc) {
18,088✔
413
            if (!uc_is_c_whitespace(c) && !uc_is_property_white_space(c) && !uc_is_property_bidi_whitespace(c)) {
16,150!
414
                last_char_pos = (int) (rest - text);
12,078✔
415
            }
416
        }
417
        rest = advance_next32(rest, &step_invis);
18,088✔
418
    }
419

420
    /* If the last character is followed by an escape sequence, keep it (but only one). */
421
    if (last_char_pos >= 0) {
613✔
422
        rest = text + last_char_pos + 1;
593✔
423
        if (rest[0] == char_esc) {
593✔
424
            advance_next32(rest, &step_invis);
105✔
425
            last_char_pos += step_invis;
105✔
426
        }
427
    }
428

429
    set_char_at(text, (size_t) (last_char_pos + 1), char_nul);
613✔
430
    *len = (size_t) (last_char_pos + 1);
613✔
431
}
432

433

434

435
char *my_strnrstr(const char *s1, const char *s2, const size_t s2_len, int skip)
242✔
436
/*
437
 *  Return pointer to last occurrence of string s2 in string s1.
438
 *
439
 *      s1       string to search
440
 *      s2       string to search for in s1
441
 *      s2_len   length in characters of s2
442
 *      skip     number of finds to ignore before returning anything
443
 *
444
 *  RETURNS: pointer to last occurrence of string s2 in string s1
445
 *           NULL if not found or error
446
 *
447
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
448
 */
449
{
450
    char *p;
451
    int comp;
452

453
    if (!s2 || *s2 == '\0') {
242!
454
        return (char *) s1;
×
455
    }
456
    if (!s1 || *s1 == '\0') {
242!
457
        return NULL;
×
458
    }
459
    if (skip < 0) {
242!
460
        skip = 0;
×
461
    }
462

463
    p = strrchr(s1, s2[0]);
242✔
464
    if (!p) {
242!
465
        return NULL;
×
466
    }
467

468
    while (p >= s1) {
10,244✔
469
        comp = strncmp(p, s2, s2_len);
10,080✔
470
        if (comp == 0) {
10,080✔
471
            if (skip--) {
234✔
472
                --p;
156✔
473
            }
474
            else {
475
                return p;
78✔
476
            }
477
        }
478
        else {
479
            --p;
9,846✔
480
        }
481
    }
482

483
    return NULL;
164✔
484
}
485

486

487

488
size_t my_strrspn(const char *s, const char *accept)
969✔
489
{
490
    if (!s || *s == '\0') {
969✔
491
        return 0;
7✔
492
    }
493
    if (!accept || *accept == '\0') {
962✔
494
        return 0;
2✔
495
    }
496

497
    for (int i = strlen(s) - 1; i >= 0; i--) {
2,083✔
498
        size_t idx = (size_t) i;
1,373✔
499
        if (strchr(accept, s[idx]) == NULL) {
1,373✔
500
            return strlen(s) - (idx + 1);
250✔
501
        }
502
    }
503
    return strlen(s);
710✔
504
}
505

506

507

508
uint32_t *tabbify_indent(const size_t lineno, uint32_t *indentspc, const size_t indentspc_len)
1,043✔
509
{
510
    size_t i;
511
    uint32_t *result;
512
    size_t result_len;
513

514
    if (opt.tabexp != 'k') {
1,043✔
515
        return indentspc;
1,028✔
516
    }
517
    if (lineno >= input.num_lines) {
15!
518
        return NULL;
×
519
    }
520
    if (indentspc_len == 0) {
15!
521
        return new_empty_string32();
×
522
    }
523

524
    result = (uint32_t *) malloc((indentspc_len + 1) * sizeof(uint32_t));
15✔
525
    if (result == NULL) {
15!
526
        perror(PROJECT);
×
527
        return NULL;
×
528
    }
529
    u32_set(result, char_space, indentspc_len);
15✔
530
    set_char_at(result, indentspc_len, char_nul);
15✔
531
    result_len = indentspc_len;
15✔
532

533
    for (i = 0; i < input.lines[lineno].tabpos_len && input.lines[lineno].tabpos[i] < indentspc_len; ++i) {
18✔
534
        size_t tpos = input.lines[lineno].tabpos[i];
3✔
535
        size_t nspc = opt.tabstop - (tpos % opt.tabstop); /* no of spcs covered by tab */
3✔
536
        if (tpos + nspc > input.indent) {
3!
537
            break;
×
538
        }
539
        set_char_at(result, tpos, char_tab);
3✔
540
        result_len -= nspc - 1;
3✔
541
        set_char_at(result, result_len, char_nul);
3✔
542
    }
543

544
    return result;
15✔
545
}
546

547

548

549
char *nspaces(const size_t n)
708✔
550
{
551
    char *spaces = (char *) memset(malloc(n + 1), (int) ' ', n);
708✔
552
    spaces[n] = '\0';
708✔
553
    return spaces;
708✔
554
}
555

556

557

558
#if defined(DEBUG) || 0
559

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

583
        fprintf(stderr, "          posmap=");
584
        if (input.lines[i].posmap != NULL) {
585
            fprintf(stderr, "[");
586
            for (size_t j = 0; j < input.lines[i].len; j++) {
587
                fprintf(stderr, "%d%s", (int) input.lines[i].posmap[j], j == (input.lines[i].len - 1) ? "" : ", ");
588
            }
589
            fprintf(stderr, "]\n");
590
        }
591
        else {
592
            fprintf(stderr, "null\n");
593
        }
594
    }
595
    fprintf(stderr, " Longest line: %d columns\n", (int) input.maxline);
596
    fprintf(stderr, "  Indentation: %2d spaces\n", (int) input.indent);
597
    fprintf(stderr, "Final newline: %s\n", input.final_newline ? "yes" : "no");
598
}
599

600
#endif
601

602

603

604
/**
605
 * Analyze the multi-byte string in order to determine its metrics:
606
 * - number of visible columns it occupies
607
 * - number of escape characters (== number of escape sequences)
608
 * - the ASCII equivalent of the string
609
 * - the number of invisible characters in the string
610
 *
611
 * @param <s> the multi-byte string to analyze
612
 * @param <num_esc> pointer to where the number of escape sequences should be stored
613
 * @param <ascii> pointer to where the ASCII equivalent of the string should be stored
614
 * @param <posmap> pointer to the position map, which maps each position in <ascii> to a position in <s>
615
 * @returns the number of invisible characters in <s>
616
 */
617
size_t count_invisible_chars(const uint32_t *s, size_t *num_esc, char **ascii, size_t **posmap)
2,200✔
618
{
619
    size_t invis = 0; /* counts invisible column positions */
2,200✔
620
    *num_esc = 0;     /* counts the number of escape sequences found */
2,200✔
621

622
    if (is_empty(s)) {
2,200✔
623
        (*ascii) = (char *) strdup("");
37✔
624
        (*posmap) = NULL;
37✔
625
        return 0;
37✔
626
    }
627

628
    size_t buflen = (size_t) u32_strwidth(s, encoding) + 1;
2,163✔
629
    size_t map_size = BMAX((size_t) 5, buflen);
2,163✔
630
    size_t map_idx = 0;
2,163✔
631
    size_t *map = (size_t *) calloc(map_size, sizeof(size_t)); /* might not be enough if many double-wide chars */
2,163✔
632
    (*ascii) = (char *) calloc(buflen, sizeof(char));          /* maybe a little too much, but certainly enough */
2,163✔
633
    char *p = *ascii;
2,163✔
634

635
    size_t mb_idx = 0;
2,163✔
636
    size_t step_invis;
637
    const uint32_t *rest = s;
2,163✔
638

639
    for (ucs4_t c = s[0]; c != char_nul; c = rest[0]) {
47,293✔
640
        if (map_idx >= map_size - 4) {
45,130✔
641
            map_size = map_size * 2 + 1;
1,354✔
642
            map = (size_t *) realloc(map, map_size * sizeof(size_t));
1,354✔
643
        }
644

645
        if (c == char_esc) {
45,130✔
646
            (*num_esc)++;
3,438✔
647
        }
648
        else if (is_ascii_printable(c)) {
41,692✔
649
            *p = c & 0xff;
41,182✔
650
            map[map_idx++] = mb_idx;
41,182✔
651
            ++p;
41,182✔
652
        }
653
        else {
654
            int cols = uc_width(c, encoding);
510✔
655
            if (cols > 0) {
510!
656
                memset(p, (int) 'x', cols);
510✔
657
                for (int i = 0; i < cols; i++) {
1,302✔
658
                    map[map_idx++] = mb_idx;
792✔
659
                }
660
                p += cols;
510✔
661
            }
662
        }
663

664
        rest = advance_next32(rest, &step_invis);
45,130✔
665

666
        mb_idx += BMAX((size_t) 1, step_invis);
45,130✔
667
        invis += step_invis;
45,130✔
668
    }
669

670
    *p = '\0';
2,163✔
671
    (*posmap) = map;
2,163✔
672
    return invis;
2,163✔
673
}
674

675

676

677
int is_csi_reset(const uint32_t *csi)
1,573✔
678
{
679
    ucs4_t puc = '\0';
1,573✔
680
    const uint32_t *rest = csi;
1,573✔
681
    size_t csi_pos = 0;
1,573✔
682
    while ((rest = u32_next(&puc, rest))) {
5,483✔
683
        switch(csi_pos) {
5,480✔
684
            case 0:
1,572✔
685
                if (puc != char_esc) {
1,572✔
686
                    return 0;
2✔
687
                }
688
                break;
1,570✔
689
            case 1:
1,570✔
690
                if (puc != '[' && puc != '(') {
1,570✔
691
                    return 0;
1✔
692
                }
693
                break;
1,569✔
694
            case 2:
1,567✔
695
                if (puc != '0') {
1,567✔
696
                    if (puc >= 0x40 && puc <= 0x7e) {
796!
697
                        return 1;
7✔
698
                    }
699
                    return 0;
789✔
700
                }
701
                break;
771✔
702
            default:
771✔
703
                return (puc >= 0x40 && puc <= 0x7e) ? 1 : 0;
771!
704
        }
705
        csi_pos++;
3,910✔
706
    }
707
    return 0;
3✔
708
}
709

710

711

712
void analyze_line_ascii(input_t *input_ptr, line_t *line)
1,243✔
713
{
714
    size_t num_esc = 0;
1,243✔
715
    char *ascii;
716
    size_t *map;
717
    size_t invis = count_invisible_chars(line->mbtext, &num_esc, &ascii, &(map));
1,243✔
718
    line->invis = invis;
1,243✔
719
    /* u32_strwidth() does not count control characters, i.e. ESC characters, for which we must correct */
720
    line->len = u32_strwidth(line->mbtext, encoding) - invis + num_esc;
1,243✔
721
    line->num_chars = u32_strlen(line->mbtext);
1,243✔
722
    BFREE(line->text);
1,243✔
723
    line->text = ascii;
1,243✔
724
    BFREE(line->posmap);
1,243✔
725
    line->posmap = map;
1,243✔
726

727
    if (line->len > input_ptr->maxline) {
1,243✔
728
        input_ptr->maxline = line->len;
259✔
729
    }
730
}
1,243✔
731

732

733

734
int array_contains(char **array, const size_t array_len, const char *s)
×
735
{
736
    int result = 0;
×
737
    if (array != NULL && array_len > 0) {
×
738
        for (size_t i = 0; i < array_len; ++i) {
×
739
            if (strcmp(array[i], s) == 0) {
×
740
                result = 1;
×
741
                break;
×
742
            }
743
        }
744
    }
745
    return result;
×
746
}
747

748

749

750
int array_contains0(char **array, const char *s)
643✔
751
{
752
    int result = 0;
643✔
753
    if (array != NULL) {
643!
754
        for (size_t i = 0; array[i] != NULL; ++i) {
1,255✔
755
            if (strcasecmp(array[i], s) == 0) {
644✔
756
                result = 1;
32✔
757
                break;
32✔
758
            }
759
        }
760
    }
761
    return result;
643✔
762
}
763

764

765

766
int array_contains_bxs(bxstr_t **array, const size_t array_len, bxstr_t *s)
26✔
767
{
768
    int result = 0;
26✔
769
    if (array != NULL && array_len > 0) {
26!
770
        for (size_t i = 0; i < array_len; ++i) {
15✔
771
            if (bxs_strcmp(array[i], s) == 0) {
9✔
772
                result = 1;
1✔
773
                break;
1✔
774
            }
775
        }
776
    }
777
    return result;
26✔
778
}
779

780

781

782
size_t array_count0(char **array)
606✔
783
{
784
    size_t num_elems = 0;
606✔
785
    if (array != NULL) {
606!
786
        while (array[num_elems] != NULL) {
1,134✔
787
            ++num_elems;
528✔
788
        }
789
    }
790
    return num_elems;
606✔
791
}
792

793

794

795
char *trimdup(char *s, char *e)
18✔
796
{
797
    if (s > e || (s == e && *s == '\0')) {
18!
798
        return strdup("");
×
799
    }
800
    while (s <= e && (*s == ' ' || *s == '\t')) {
18!
801
        ++s;
×
802
    }
803
    while (e > s && (*e == ' ' || *e == '\t')) {
18!
804
        --e;
×
805
    }
806
    return bx_strndup(s, e - s + 1);
18✔
807
}
808

809

810

811
int tag_is_valid(char *tag)
17✔
812
{
813
    pcre2_code *pattern = get_pattern_ascii_id(1);
17✔
814
    return regex_match(pattern, tag);
17✔
815
}
816

817

818

819
int is_ascii_id(bxstr_t *s, int strict)
594✔
820
{
821
    if (s == NULL || s->num_chars == 0) {
594✔
822
        return 0;
4✔
823
    }
824
    pcre2_code *pattern = get_pattern_ascii_id(strict);
590✔
825
    return u32_regex_match(pattern, s->memory);
590✔
826
}
827

828

829

830
char *bx_strndup(const char *s, size_t n)
18✔
831
{
832
    if (s == NULL) {
18!
833
        return NULL;
×
834
    }
835

836
    size_t len = strlen(s);
18✔
837
    if (n < len) {
18!
838
        len = n;
×
839
    }
840

841
    char *result = (char *) malloc(len + 1);
18✔
842
    if (result == NULL) {
18!
843
        return NULL;
×
844
    }
845

846
    result[len] = '\0';
18✔
847
    return (char *) memcpy(result, s, len);
18✔
848
}
849

850

851

852
void bx_fprintf(FILE *stream, const char *format, ...)
8✔
853
{
854
    va_list va;
855
    va_start(va, format);
8✔
856
    vfprintf(stream, format, va);
8✔
857
    va_end(va);
8✔
858
}
8✔
859

860

861

862
FILE *bx_fopens(bxstr_t *pathname, char *mode)
174✔
863
{
864
    return bx_fopen(to_utf8(pathname->memory), mode);
174✔
865
}
866

867

868

869
FILE *bx_fopen(char *pathname, char *mode)
349✔
870
{
871
    /*
872
     * On Linux/UNIX and OS X (Mac), one can access files with non-ASCII file names by passing them to fopen() as UTF-8.
873
     * On Windows, a different function must be called. (Info: https://stackoverflow.com/a/35065142/1005481)
874
     */
875
    FILE *f = fopen(pathname, mode);
349✔
876
    // TODO Windows
877
    return f;
349✔
878
}
879

880

881
/* 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