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

ascii-boxes / boxes / 7314827362

24 Dec 2023 01:12PM UTC coverage: 88.826% (+0.6%) from 88.199%
7314827362

push

github

tsjensen
Use -ggdb3 option for more detailed debug info

2869 of 3408 branches covered (0.0%)

Branch coverage included in aggregate %.

4619 of 5022 relevant lines covered (91.98%)

202175.9 hits per line

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

86.72
/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)
4,466✔
56
{
57
    pcre2_code *result = NULL;
4,466✔
58
    if (strict) {
4,466✔
59
        if (pattern_ascii_id_strict == NULL) {
4,446✔
60
            pattern_ascii_id_strict = compile_pattern("^(?!.*?--|none)[a-z][a-z0-9-]*(?<!-)$");
704✔
61
        }
352✔
62
        result = pattern_ascii_id_strict;
4,446✔
63
    }
2,223✔
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;
4,466✔
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
char *concat_strings_alloc(size_t count, ...)
59✔
152
{
153
    if (count < 1) {
59!
154
        return strdup("");
×
155
    }
156

157
    size_t total_len = 0;
59✔
158
    const char *src;
159
    va_list va;
160

161
    va_start(va, count);
59✔
162
    for (size_t i = 0; i < count; i++) {
219✔
163
        src = va_arg(va, const char *);
160!
164
        if (src != NULL) {
160✔
165
            total_len += strlen(src);
160✔
166
        }
79✔
167
    }
79✔
168
    va_end(va);
59✔
169

170
    char *result = malloc(total_len + 1);
59✔
171
    char *p = result;
59✔
172

173
    va_start(va, count);
59✔
174
    for (size_t i = 0; i < count; i++) {
219✔
175
        src = va_arg(va, const char *);
160!
176
        if (src != NULL && src[0] != '\0') {
160!
177
            strcpy(p, src);
154✔
178
            p += strlen(src);
154✔
179
        }
76✔
180
    }
79✔
181
    va_end(va);
59✔
182

183
    *p = '\0';
59✔
184
    return result;
59✔
185
}
29✔
186

187

188

189
char *repeat(char *s, size_t count)
5,132✔
190
{
191
    if (s == NULL) {
5,132✔
192
        return NULL;
2✔
193
    }
194

195
    size_t len = strlen(s);
5,130✔
196
    char *result = (char *) malloc(count * len + 1);
5,130✔
197
    if (result != NULL) {
5,130✔
198
        char *dest = result;
5,130✔
199
        for (size_t i = 0; i < count; i++) {
36,408✔
200
            strcpy(dest, s);
31,278✔
201
            dest += len;
31,278✔
202
        }
15,639✔
203
        *dest = '\0';
5,130✔
204
    }
2,565✔
205
    return result;
5,130✔
206
}
2,566✔
207

208

209

210
int empty_line(const line_t *line)
3,198✔
211
{
212
    if (!line) {
3,198!
213
        return 1;
×
214
    }
215
    return bxs_is_blank(line->text);
3,198✔
216
}
1,599✔
217

218

219

220
size_t expand_tabs_into(const uint32_t *input_buffer, const int tabstop, uint32_t **text, size_t **tabpos,
5,224✔
221
        size_t *tabpos_len)
222
{
223
    static uint32_t temp[LINE_MAX_BYTES + 100]; /* work string */
224
    size_t io;                                  /* character position in work string */
225
    size_t tabnum = 0;                          /* index of the current tab */
5,224✔
226

227
    *text = NULL;
5,224✔
228
    *tabpos = NULL;
5,224✔
229
    *tabpos_len = 0;
5,224✔
230

231
    if (opt.tabexp == 'k') {
5,224✔
232
        /* We need to know the exact tab positions only if expansion type 'k' is requested (keep tabs as much as they
233
         * were as possible). Else we'll just convert spaces and tabs without having to know where exactly the tabs
234
         * were in the first place. */
235
        ucs4_t puc;
236
        const uint32_t *rest = input_buffer;
18✔
237
        while ((rest = u32_next(&puc, rest))) {
200✔
238
            if (puc == char_tab) {
182✔
239
                (*tabpos_len)++;
10✔
240
            }
5✔
241
        }
242
    }
9✔
243

244
    if (*tabpos_len > 0) {
5,224✔
245
        *tabpos = (size_t *) calloc((*tabpos_len) + 1, sizeof(size_t));
6✔
246
        if (*tabpos == NULL) {
6!
247
            return 0; /* out of memory */
×
248
        }
249
    }
3✔
250

251
    ucs4_t puc;
252
    const uint32_t *rest = input_buffer;
5,224✔
253
    io = 0;
5,224✔
254
    while ((rest = u32_next(&puc, rest)) && io < (LINE_MAX_BYTES - 12)) {
332,590✔
255
        if (puc == char_tab) {
327,366✔
256
            if (*tabpos_len > 0) {
1,050✔
257
                (*tabpos)[tabnum++] = io;
10✔
258
            }
5✔
259
            size_t num_spc = tabstop - (io % tabstop);
1,050✔
260
            u32_set(temp + io, char_space, num_spc);
1,050✔
261
            io += num_spc;
1,050✔
262
        }
525✔
263
        else {
264
            set_char_at(temp, io, puc);
326,316✔
265
            ++io;
326,316✔
266
        }
267
    }
268
    temp[io] = 0;
5,224✔
269

270
    *text = u32_strdup(temp);
5,224✔
271
    if (*text == NULL) {
5,224✔
272
        return 0;
×
273
    }
274
    return io;
5,224✔
275
}
2,612✔
276

277

278

279
void btrim(char *text, size_t *len)
17,990✔
280
/*
281
 *  Remove trailing whitespace from line.
282
 *
283
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
284
 */
285
{
286
    long idx = (long) (*len) - 1;
17,990✔
287

288
    while (idx >= 0 && (text[idx] == '\n' || text[idx] == '\r' || text[idx] == '\t' || text[idx] == ' ')) {
45,147!
289
        text[idx--] = '\0';
18,162✔
290
    }
291

292
    *len = idx + 1;
17,990✔
293
}
17,990✔
294

295

296

297
void btrim32(uint32_t *text, size_t *len)
1,916✔
298
/*
299
 *  Remove trailing whitespace from line (unicode and escape sequence enabled version).
300
 *  CHECK replace by bxs_rtrim() from bxstring module
301
 *
302
 *      text     string to trim
303
 *      len      pointer to the length of the string in characters
304
 *
305
 *  Both the string and the length will be modified as trailing whitespace is removed.
306
 *
307
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
308
 */
309
{
310
    if (text == NULL || len == 0) {
1,916!
311
        return;
×
312
    }
313

314
    const uint32_t *rest = text;
1,916✔
315
    int last_char_pos = -1;
1,916✔
316
    size_t step_invis;
317

318
    for (ucs4_t c = text[0]; c != char_nul; c = rest[0]) {
72,668✔
319
        if (c != char_esc) {
70,752✔
320
            if (!uc_is_c_whitespace(c) && !uc_is_property_white_space(c) && !uc_is_property_bidi_whitespace(c)) {
67,016!
321
                last_char_pos = (int) (rest - text);
53,938✔
322
            }
26,969✔
323
        }
33,508✔
324
        rest = advance_next32(rest, &step_invis);
70,752✔
325
    }
35,376✔
326

327
    /* If the last character is followed by an escape sequence, keep it (but only one). */
328
    if (last_char_pos >= 0) {
1,916✔
329
        rest = text + last_char_pos + 1;
1,884✔
330
        if (rest[0] == char_esc) {
1,884✔
331
            advance_next32(rest, &step_invis);
204✔
332
            last_char_pos += step_invis;
204✔
333
        }
102✔
334
    }
942✔
335

336
    set_char_at(text, (size_t) (last_char_pos + 1), char_nul);
1,916✔
337
    *len = (size_t) (last_char_pos + 1);
1,916✔
338
}
958✔
339

340

341

342
size_t my_strrspn(const char *s, const char *accept)
9,114✔
343
{
344
    if (!s || *s == '\0') {
9,114✔
345
        return 0;
122✔
346
    }
347
    if (!accept || *accept == '\0') {
8,992✔
348
        return 0;
4✔
349
    }
350

351
    for (int i = strlen(s) - 1; i >= 0; i--) {
14,158✔
352
        size_t idx = (size_t) i;
10,702✔
353
        if (strchr(accept, s[idx]) == NULL) {
10,702✔
354
            return strlen(s) - (idx + 1);
5,532✔
355
        }
356
    }
2,585✔
357
    return strlen(s);
3,456✔
358
}
4,557✔
359

360

361

362
uint32_t *tabbify_indent(const size_t lineno, uint32_t *indentspc, const size_t indentspc_len)
5,120✔
363
{
364
    size_t i;
365
    uint32_t *result;
366
    size_t result_len;
367

368
    if (opt.tabexp != 'k') {
5,120✔
369
        return indentspc;
5,090✔
370
    }
371
    if (lineno >= input.num_lines) {
30!
372
        return NULL;
×
373
    }
374
    if (indentspc_len == 0) {
30✔
375
        return new_empty_string32();
×
376
    }
377

378
    result = (uint32_t *) malloc((indentspc_len + 1) * sizeof(uint32_t));
30✔
379
    if (result == NULL) {
30!
380
        perror(PROJECT);
×
381
        return NULL;
×
382
    }
383
    u32_set(result, char_space, indentspc_len);
30✔
384
    set_char_at(result, indentspc_len, char_nul);
30✔
385
    result_len = indentspc_len;
30✔
386

387
    for (i = 0; i < input.lines[lineno].tabpos_len && input.lines[lineno].tabpos[i] < indentspc_len; ++i) {
36✔
388
        size_t tpos = input.lines[lineno].tabpos[i];
6✔
389
        size_t nspc = opt.tabstop - (tpos % opt.tabstop); /* no of spcs covered by tab */
6✔
390
        if (tpos + nspc > input.indent) {
6✔
391
            break;
×
392
        }
393
        set_char_at(result, tpos, char_tab);
6✔
394
        result_len -= nspc - 1;
6✔
395
        set_char_at(result, result_len, char_nul);
6✔
396
    }
3✔
397

398
    return result;
30✔
399
}
2,560✔
400

401

402

403
char *nspaces(const size_t n)
3,416✔
404
{
405
    char *spaces = (char *) memset(malloc(n + 1), (int) ' ', n);
3,416✔
406
    spaces[n] = '\0';
3,416✔
407
    return spaces;
3,416✔
408
}
409

410

411

412
#if defined(DEBUG) || 0
413

414
/**
415
 * Debugging Code: Display contents of input structure
416
 * @param heading a heading to show for identification of the printed lines
417
 */
418
void print_input_lines(const char *heading)
419
{
420
    fprintf(stderr, "%d Input Lines%s:\n", (int) input.num_lines, heading != NULL ? heading : "");
421
    fprintf(stderr, "     [num_chars] \"real text\" [num_cols] \"ascii_text\"\n");
422
    for (size_t i = 0; i < input.num_lines; ++i) {
423
        char *outtext = bxs_to_output(input.lines[i].text);
424
        fprintf(stderr, "%4d [%02d] \"%s\"  [%02d] \"%s\"", (int) i,
425
                (int) input.lines[i].text->num_chars, outtext,
426
                (int) input.lines[i].text->num_columns, input.lines[i].text->ascii);
427
        BFREE(outtext);
428
        fprintf(stderr, "\tTabs: [");
429
        if (input.lines[i].tabpos != NULL) {
430
            for (size_t j = 0; j < input.lines[i].tabpos_len; ++j) {
431
                fprintf(stderr, "%d", (int) input.lines[i].tabpos[j]);
432
                if (j < input.lines[i].tabpos_len - 1) {
433
                    fprintf(stderr, ", ");
434
                }
435
            }
436
        }
437
        fprintf(stderr, "] (%d)", (int) input.lines[i].tabpos_len);
438
        fprintf(stderr, "\tinvisible=%d\n", (int) input.lines[i].text->num_chars_invisible);
439

440
        fprintf(stderr, "    visible_char=");
441
        if (input.lines[i].text->visible_char != NULL) {
442
            fprintf(stderr, "[");
443
            for (size_t j = 0; j < input.lines[i].text->num_chars_visible; j++) {
444
                fprintf(stderr, "%d%s", (int) input.lines[i].text->visible_char[j],
445
                    j == (input.lines[i].text->num_chars_visible - 1) ? "" : ", ");
446
            }
447
            fprintf(stderr, "]\n");
448
        }
449
        else {
450
            fprintf(stderr, "null\n");
451
        }
452

453
        fprintf(stderr, "      first_char=");
454
        if (input.lines[i].text->first_char != NULL) {
455
            fprintf(stderr, "[");
456
            for (size_t j = 0; j < input.lines[i].text->num_chars_visible; j++) {
457
                fprintf(stderr, "%d%s", (int) input.lines[i].text->first_char[j],
458
                    j == (input.lines[i].text->num_chars_visible - 1) ? "" : ", ");
459
            }
460
            fprintf(stderr, "]\n");
461
        }
462
        else {
463
            fprintf(stderr, "null\n");
464
        }
465
    }
466
    fprintf(stderr, " Longest line: %d columns\n", (int) input.maxline);
467
    fprintf(stderr, "  Indentation: %2d spaces\n", (int) input.indent);
468
    fprintf(stderr, "Final newline: %s\n", input.final_newline ? "yes" : "no");
469
}
470

471
#endif
472

473

474

475
/**
476
 * Analyze the multi-byte string in order to determine its metrics:
477
 * - number of visible columns it occupies
478
 * - number of escape characters (== number of escape sequences)
479
 * - the ASCII equivalent of the string
480
 * - the number of invisible characters in the string
481
 *
482
 * @param <s> the multi-byte string to analyze
483
 * @param <num_esc> pointer to where the number of escape sequences should be stored
484
 * @param <ascii> pointer to where the ASCII equivalent of the string should be stored
485
 * @param <posmap> pointer to the position map, which maps each position in <ascii> to a position in <s>
486
 * @returns the number of invisible characters in <s>
487
 */
488
size_t count_invisible_chars(const uint32_t *s, size_t *num_esc, char **ascii, size_t **posmap)
9,090✔
489
{
490
    size_t invis = 0; /* counts invisible column positions */
9,090✔
491
    *num_esc = 0;     /* counts the number of escape sequences found */
9,090✔
492

493
    if (is_empty(s)) {
9,090✔
494
        (*ascii) = (char *) strdup("");
116✔
495
        (*posmap) = NULL;
116✔
496
        return 0;
116✔
497
    }
498

499
    size_t buflen = (size_t) u32_strwidth(s, encoding) + 1;
8,974✔
500
    size_t map_size = BMAX((size_t) 5, buflen);
8,974✔
501
    size_t map_idx = 0;
8,974✔
502
    size_t *map = (size_t *) calloc(map_size, sizeof(size_t)); /* might not be enough if many double-wide chars */
8,974✔
503
    (*ascii) = (char *) calloc(buflen, sizeof(char));          /* maybe a little too much, but certainly enough */
8,974✔
504
    char *p = *ascii;
8,974✔
505

506
    size_t mb_idx = 0;
8,974✔
507
    size_t step_invis;
508
    const uint32_t *rest = s;
8,974✔
509

510
    for (ucs4_t c = s[0]; c != char_nul; c = rest[0]) {
52,512✔
511
        if (map_idx >= map_size - 4) {
43,538✔
512
            map_size = map_size * 2 + 1;
6,144✔
513
            map = (size_t *) realloc(map, map_size * sizeof(size_t));
6,144✔
514
        }
3,072✔
515

516
        if (c == char_esc) {
43,538!
517
            (*num_esc)++;
×
518
        }
519
        else if (is_ascii_printable(c)) {
43,538!
520
            *p = c & 0xff;
43,538✔
521
            map[map_idx++] = mb_idx;
43,538✔
522
            ++p;
43,538✔
523
        }
21,769✔
524
        else {
525
            int cols = uc_width(c, encoding);
×
526
            if (cols > 0) {
×
527
                memset(p, (int) 'x', cols);
×
528
                for (int i = 0; i < cols; i++) {
×
529
                    map[map_idx++] = mb_idx;
×
530
                }
531
                p += cols;
×
532
            }
533
        }
534

535
        rest = advance_next32(rest, &step_invis);
43,538✔
536

537
        mb_idx += BMAX((size_t) 1, step_invis);
43,538!
538
        invis += step_invis;
43,538✔
539
    }
21,769✔
540

541
    *p = '\0';
8,974✔
542
    (*posmap) = map;
8,974✔
543
    return invis;
8,974✔
544
}
4,545✔
545

546

547

548
int is_csi_reset(const uint32_t *csi)
59,530✔
549
{
550
    ucs4_t puc = '\0';
59,530✔
551
    const uint32_t *rest = csi;
59,530✔
552
    size_t csi_pos = 0;
59,530✔
553
    while ((rest = u32_next(&puc, rest))) {
207,784✔
554
        switch(csi_pos) {
207,778✔
555
            case 0:
29,742✔
556
                if (puc != char_esc) {
59,484✔
557
                    return 0;
4✔
558
                }
559
                break;
59,480✔
560
            case 1:
29,740✔
561
                if (puc != '[' && puc != '(') {
59,480✔
562
                    return 0;
2✔
563
                }
564
                break;
59,478✔
565
            case 2:
29,737✔
566
                if (puc != '0') {
59,518✔
567
                    if (puc >= 0x40 && puc <= 0x7e) {
38,950!
568
                        return 1;
36✔
569
                    }
570
                    else if ((puc == '1' && *rest == '0')
38,914!
571
                          || (puc == '3' && *rest == '9')
38,914✔
572
                          || (puc == '4' && *rest == '9')
49,518!
573
                          || (puc == '5' && *rest == '9')
30,142!
574
                          || (puc == '7' && *rest == '5')) {
30,142!
575
                        rest = u32_next(&puc, rest);
38,752✔
576
                        break;
38,752✔
577
                    }
578
                    return 0;
30,142✔
579
                }
580
                break;
20,568✔
581
            default:
14,648✔
582
                return (puc >= 0x40 && puc <= 0x7e) ? 1 : 0;
29,296!
583
        }
584
        csi_pos++;
148,254✔
585
    }
586
    return 0;
6✔
587
}
29,743✔
588

589

590

591
void analyze_line_ascii(input_t *input_ptr, line_t *line)
7,756✔
592
{
593
    if (line->text->num_columns > input_ptr->maxline) {
7,756✔
594
        input_ptr->maxline = line->text->num_columns;
2,294✔
595
    }
1,147✔
596
}
7,756✔
597

598

599

600
int array_contains(char **array, const size_t array_len, const char *s)
×
601
{
602
    int result = 0;
×
603
    if (array != NULL && array_len > 0) {
×
604
        for (size_t i = 0; i < array_len; ++i) {
×
605
            if (strcmp(array[i], s) == 0) {
×
606
                result = 1;
×
607
                break;
×
608
            }
609
        }
610
    }
611
    return result;
×
612
}
613

614

615

616
int array_contains0(char **array, const char *s)
4,826✔
617
{
618
    int result = 0;
4,826✔
619
    if (array != NULL) {
4,826✔
620
        for (size_t i = 0; array[i] != NULL; ++i) {
9,518✔
621
            if (strcasecmp(array[i], s) == 0) {
4,900✔
622
                result = 1;
208✔
623
                break;
208✔
624
            }
625
        }
2,346✔
626
    }
2,413✔
627
    return result;
4,826✔
628
}
629

630

631

632
int array_contains_bxs(bxstr_t **array, const size_t array_len, bxstr_t *s)
52✔
633
{
634
    int result = 0;
52✔
635
    if (array != NULL && array_len > 0) {
52✔
636
        for (size_t i = 0; i < array_len; ++i) {
30✔
637
            if (bxs_strcmp(array[i], s) == 0) {
18✔
638
                result = 1;
2✔
639
                break;
2✔
640
            }
641
        }
8✔
642
    }
7✔
643
    return result;
52✔
644
}
645

646

647

648
size_t array_count0(char **array)
4,464✔
649
{
650
    size_t num_elems = 0;
4,464✔
651
    if (array != NULL) {
4,464!
652
        while (array[num_elems] != NULL) {
8,594✔
653
            ++num_elems;
4,130✔
654
        }
655
    }
2,232✔
656
    return num_elems;
4,464✔
657
}
658

659

660

661
char *trimdup(char *s, char *e)
38✔
662
{
663
    if (s > e || (s == e && *s == '\0')) {
38!
664
        return strdup("");
×
665
    }
666
    while (s <= e && (*s == ' ' || *s == '\t')) {
57!
667
        ++s;
×
668
    }
669
    while (e > s && (*e == ' ' || *e == '\t')) {
57!
670
        --e;
×
671
    }
672
    return bx_strndup(s, e - s + 1);
38✔
673
}
19✔
674

675

676

677
int tag_is_valid(char *tag)
34✔
678
{
679
    pcre2_code *pattern = get_pattern_ascii_id(1);
34✔
680
    return regex_match(pattern, tag);
34✔
681
}
682

683

684

685
int is_ascii_id(bxstr_t *s, int strict)
4,440✔
686
{
687
    if (s == NULL || s->num_chars == 0) {
4,440✔
688
        return 0;
8✔
689
    }
690
    pcre2_code *pattern = get_pattern_ascii_id(strict);
4,432✔
691
    return u32_regex_match(pattern, s->memory);
4,432✔
692
}
2,220✔
693

694

695

696
char *bx_strndup(const char *s, size_t n)
38✔
697
{
698
    if (s == NULL) {
38✔
699
        return NULL;
×
700
    }
701

702
    size_t len = strlen(s);
38✔
703
    if (n < len) {
38✔
704
        len = n;
×
705
    }
706

707
    char *result = (char *) malloc(len + 1);
38✔
708
    if (result == NULL) {
38!
709
        return NULL;
×
710
    }
711

712
    result[len] = '\0';
38✔
713
    return (char *) memcpy(result, s, len);
38✔
714
}
19✔
715

716

717

718
void bx_fprintf_original(FILE *stream, const char *format, ...)
16✔
719
{
720
    va_list va;
721
    va_start(va, format);
16✔
722
    vfprintf(stream, format, va);
16✔
723
    va_end(va);
16✔
724
}
16✔
725

726

727
void set_bx_fprintf(bx_fprintf_t bx_fprintf_function) {
2✔
728
    bx_fprintf = bx_fprintf_function;
2✔
729
}
2✔
730

731

732
FILE *bx_fopens(bxstr_t *pathname, char *mode)
808✔
733
{
734
    return bx_fopen(to_utf8(pathname->memory), mode);
808✔
735
}
736

737

738

739
FILE *bx_fopen(char *pathname, char *mode)
1,618✔
740
{
741
    /*
742
     * On Linux/UNIX and OS X (Mac), one can access files with non-ASCII file names by passing them to fopen() as UTF-8.
743
     * On Windows, a different function must be called. (Info: https://stackoverflow.com/a/35065142/1005481)
744
     */
745
    FILE *f = fopen(pathname, mode);
1,618✔
746
    // TODO Windows
747
    return f;
1,618✔
748
}
749

750

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