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

ascii-boxes / boxes / 25991660202

17 May 2026 01:04PM UTC coverage: 82.822%. Remained the same
25991660202

push

github

tsjensen
Fix a Heisenbug in u32_insert_space_at() in unicode.c

2776 of 3695 branches covered (75.13%)

Branch coverage included in aggregate %.

5 of 7 new or added lines in 1 file covered. (71.43%)

297 existing lines in 19 files now uncovered.

4345 of 4903 relevant lines covered (88.62%)

98937.49 hits per line

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

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

17
/*
18
 * The boxes-internal representation of strings.
19
 */
20

21
#include "config.h"
22

23
#include <stdarg.h>
24
#include <string.h>
25
#include <unictype.h>
26
#include <unistdio.h>
27
#include <unistr.h>
28
#include <uniwidth.h>
29

30
#include "bxstring.h"
31
#include "tools.h"
32
#include "unicode.h"
33

34

35

36
bxstr_t *bxs_from_ascii(char *pAscii)
4,576✔
37
{
38
    if (pAscii == NULL) {
4,576✔
39
        bx_fprintf(stderr, "%s: internal error: from_ascii() called with NULL\n", PROJECT);
1✔
40
        return NULL;
1✔
41
    }
42

43
    bxstr_t *result = (bxstr_t *) calloc(1, sizeof(bxstr_t));
4,575✔
44
    result->memory = u32_strconv_from_arg(pAscii, "ASCII");
4,575✔
45
    if (result->memory == NULL) {
4,575!
46
        BFREE(result);
×
UNCOV
47
        return NULL;
×
48
    }
49
    result->ascii = strdup(pAscii);
4,575✔
50

51
    size_t error_pos = 0;
4,575✔
52
    if (!bxs_valid_anywhere(result, &error_pos)) {
4,575✔
53
        ucs4_t c = result->memory[error_pos];
1✔
54
        bx_fprintf(stderr, "%s: illegal character '%lc' (%#010x) encountered in string\n", PROJECT, c, (int) c);
1✔
55
        bxs_free(result);
1✔
56
        return NULL;
1✔
57
    }
58

59
    size_t num_esc = 0;
4,574✔
60
    char *ascii_copy;
61
    size_t *map;
62
    result->num_chars_invisible = count_invisible_chars(result->memory, &num_esc, &ascii_copy, &(map));
4,574✔
63
    BFREE(ascii_copy);
4,574!
64

65
    result->num_chars = strlen(pAscii);
4,574✔
66
    result->num_columns = result->num_chars;
4,574✔
67
    result->num_chars_visible = result->num_chars - result->num_chars_invisible;
4,574✔
68

69
    result->indent = strspn(pAscii, " ");
4,574✔
70
    result->trailing = my_strrspn(pAscii, " ");
4,574✔
71

72
    result->first_char = calloc(result->num_chars_visible + 1, sizeof(size_t));
4,574✔
73
    result->visible_char = calloc(result->num_chars_visible + 1, sizeof(size_t));
4,574✔
74
    for (size_t i = 0; i <= result->num_chars_visible; i++) {
31,060✔
75
        result->first_char[i] = i;
26,486✔
76
        result->visible_char[i] = i;
26,486✔
77
    }
78

79
    return result;
4,574✔
80
}
81

82

83

84
bxstr_t *bxs_from_unicode(uint32_t *pInput)
296,741✔
85
{
86
    if (pInput == NULL) {
296,741✔
87
        bx_fprintf(stderr, "%s: internal error: bxs_from_unicode() called with NULL\n", PROJECT);
1✔
88
        return NULL;
1✔
89
    }
90

91
    bxstr_t *result = (bxstr_t *) calloc(1, sizeof(bxstr_t));
296,740✔
92
    result->memory = u32_strdup(pInput);
296,740✔
93
    result->num_chars = u32_strlen(pInput);
296,740✔
94
    size_t ascii_len = ((size_t) u32_strwidth(pInput, encoding)) + 1;
296,740✔
95
    result->ascii = (char *) calloc(ascii_len, sizeof(char));
296,740✔
96
    size_t map_size = 5;
296,740✔
97
    result->first_char = (size_t *) calloc(map_size, sizeof(size_t));
296,740✔
98
    result->visible_char = (size_t *) calloc(map_size, sizeof(size_t));
296,740✔
99
    char *ascii_ptr = result->ascii;
296,740✔
100

101
    const uint32_t *rest = pInput;
296,740✔
102
    size_t step_invis = 0;
296,740✔
103
    int indent_active = 1;
296,740✔
104
    size_t blank_streak = 0;
296,740✔
105
    int first_candidate = -1;
296,740✔
106
    int non_blank_encountered = 0;
296,740✔
107
    size_t idx = 0;
296,740✔
108

109
    for (ucs4_t c = pInput[0]; c != char_nul; c = rest[0]) {
9,364,580✔
110
        if (result->num_chars_visible >= map_size - 2) {
9,067,841✔
111
            map_size = map_size * 2 + 1;
334,660✔
112
            result->first_char = (size_t *) realloc(result->first_char, map_size * sizeof(size_t));
334,660✔
113
            result->visible_char = (size_t *) realloc(result->visible_char, map_size * sizeof(size_t));
334,660✔
114
        }
115

116
        if (!is_allowed_anywhere(c)) { /* currently used for config only, reconsider when using on input data */
9,067,841✔
117
            bx_fprintf(stderr, "%s: illegal character '%lc' (%#010x) encountered in string\n", PROJECT, c, (int) c);
1✔
118
            bxs_free(result);
1✔
119
            return NULL;
1✔
120
        }
121
        else if (c == char_esc) {
9,067,840✔
122
            if (is_csi_reset(rest)) {
29,732✔
123
                first_candidate = -1;
14,335✔
124
            }
125
            else {
126
                first_candidate = idx;
15,397✔
127
            }
128
        }
129
        else {
130
            int cols = 1;
9,038,108✔
131
            if (is_ascii_printable(c)) {
9,038,108✔
132
                *ascii_ptr = c & 0xff;
8,822,712✔
133
                ++ascii_ptr;
8,822,712✔
134
            }
135
            else if (c == char_tab) {
215,396✔
136
                *ascii_ptr = ' ';
55✔
137
                ++ascii_ptr;
55✔
138
            }
139
            else {
140
                cols = BMAX(0, uc_width(c, encoding));
215,341✔
141
                if (cols > 0) {
215,341✔
142
                    memset(ascii_ptr, (int) (uc_is_blank(c) ? ' ' : 'x'), cols);
64,657✔
143
                    ascii_ptr += cols;
64,657✔
144
                }
145
            }
146
            if (is_blank(c)) {
9,038,108✔
147
                if (indent_active) {
4,481,673✔
148
                    result->indent += cols;
504,105✔
149
                }
150
                blank_streak++;
4,481,673✔
151
            }
152
            result->num_columns += BMAX(0, cols);
9,038,108✔
153
            result->visible_char[result->num_chars_visible] = idx;
9,038,108✔
154
            result->first_char[result->num_chars_visible] = first_candidate < 0 ? idx : (size_t) first_candidate;
9,038,108✔
155
            first_candidate = -1;
9,038,108✔
156
        }
157

158
        if (!is_blank(c) && c != char_esc) {
9,067,840✔
159
            indent_active = 0;
4,556,435✔
160
            blank_streak = 0;
4,556,435✔
161
            non_blank_encountered = 1;
4,556,435✔
162
        }
163

164
        rest = advance_next32(rest, &step_invis);
9,067,840✔
165

166
        if (step_invis == 0) {
9,067,840✔
167
            result->num_chars_visible++;
9,038,108✔
168
            idx++;
9,038,108✔
169
        }
170
        else {
171
            result->num_chars_invisible += step_invis;
29,732✔
172
            idx += step_invis;
29,732✔
173
        }
174
    }
175

176
    *ascii_ptr = '\0';
296,739✔
177
    result->visible_char[result->num_chars_visible] = idx;  // both point to the terminator
296,739✔
178
    result->first_char[result->num_chars_visible] = idx;
296,739✔
179
    result->trailing = non_blank_encountered ? blank_streak : 0;
296,739✔
180
    return result;
296,739✔
181
}
182

183

184

185
bxstr_t *bxs_new_empty_string()
55✔
186
{
187
    return bxs_from_ascii("");
55✔
188
}
189

190

191

192
bxstr_t *bxs_strdup(bxstr_t *pString)
34,473✔
193
{
194
    if (pString == NULL) {
34,473✔
195
        return NULL;
1✔
196
    }
197
    bxstr_t *result = (bxstr_t *) calloc(1, sizeof(bxstr_t));
34,472✔
198
    if (result != NULL) {
34,472!
199
        result->memory = u32_strdup(pString->memory);
34,472✔
200
        result->ascii = strdup(pString->ascii);
34,472✔
201
        result->indent = pString->indent;
34,472✔
202
        result->num_columns = pString->num_columns;
34,472✔
203
        result->num_chars = pString->num_chars;
34,472✔
204
        result->num_chars_visible = pString->num_chars_visible;
34,472✔
205
        result->num_chars_invisible = pString->num_chars_invisible;
34,472✔
206
        result->trailing = pString->trailing;
34,472✔
207
        result->first_char = malloc((pString->num_chars_visible + 1) * sizeof(size_t));
34,472✔
208
        memcpy(result->first_char, pString->first_char, (pString->num_chars_visible + 1) * sizeof(size_t));
34,472✔
209
        result->visible_char = malloc((pString->num_chars_visible + 1) * sizeof(size_t));
34,472✔
210
        memcpy(result->visible_char, pString->visible_char, (pString->num_chars_visible + 1) * sizeof(size_t));
34,472✔
211
    }
212
    return result;
34,472✔
213
}
214

215

216

217
bxstr_t *bxs_trimdup(bxstr_t *pString, size_t start_idx, size_t end_idx)
2,210✔
218
{
219
    if (pString == NULL) {
2,210✔
220
        return NULL;
1✔
221
    }
222
    if (start_idx > pString->num_chars_visible) {
2,209✔
223
        /* a start_idx on the terminating NUL is a valid input */
224
        bx_fprintf(stderr, "%s: internal error: start_idx out of bounds in bxs_trimdup()\n", PROJECT);
1✔
225
        return NULL;
1✔
226
    }
227
    if (end_idx > pString->num_chars_visible) {
2,208✔
228
        bx_fprintf(stderr, "%s: internal error: end_idx out of bounds in bxs_trimdup()\n", PROJECT);
1✔
229
        return NULL;
1✔
230
    }
231
    if (end_idx < start_idx) {
2,207✔
232
        bx_fprintf(stderr, "%s: internal error: end_idx before start_idx in bxs_trimdup()\n", PROJECT);
1✔
233
        return NULL;
1✔
234
    }
235

236
    while (start_idx < end_idx && uc_is_blank(pString->memory[pString->visible_char[start_idx]])) {
2,372✔
237
        start_idx++;
166✔
238
    }
239
    while (start_idx < end_idx && uc_is_blank(pString->memory[pString->visible_char[end_idx - 1]])) {
2,365✔
240
        end_idx--;
159✔
241
    }
242

243
    ucs4_t save = char_nul;
2,206✔
244
    if (end_idx < pString->num_chars_visible) {
2,206✔
245
        save = pString->memory[pString->first_char[end_idx]];
35✔
246
        set_char_at(pString->memory, pString->first_char[end_idx], char_nul);
35✔
247
    }
248

249
    bxstr_t *result = bxs_from_unicode(pString->memory + pString->first_char[start_idx]);
2,206✔
250
    if (end_idx < pString->num_chars_visible) {
2,206✔
251
        set_char_at(pString->memory, pString->first_char[end_idx], save);
35✔
252
    }
253
    return result;
2,206✔
254
}
255

256

257

258
bxstr_t *bxs_substr(bxstr_t *pString, size_t start_idx, size_t end_idx)
892✔
259
{
260
    if (pString == NULL) {
892✔
261
        return NULL;
1✔
262
    }
263
    if (start_idx > pString->num_chars) {
891✔
264
        start_idx = pString->num_chars;
1✔
265
    }
266
    if (end_idx > pString->num_chars) {
891✔
267
        end_idx = pString->num_chars;
1✔
268
    }
269
    if (end_idx < start_idx) {
891✔
270
        bx_fprintf(stderr, "%s: internal error: end_idx before start_idx in bxs_substr()\n", PROJECT);
1✔
271
        return NULL;
1✔
272
    }
273

274
    ucs4_t save = pString->memory[end_idx];
890✔
275
    set_char_at(pString->memory, end_idx, char_nul);
890✔
276
    bxstr_t *result = bxs_from_unicode(pString->memory + start_idx);
890✔
277
    set_char_at(pString->memory, end_idx, save);
890✔
278
    return result;
890✔
279
}
280

281

282

283
bxstr_t *bxs_strcat(bxstr_t *pString, uint32_t *pToAppend)
9,063✔
284
{
285
    if (pToAppend == NULL) {
9,063✔
286
        return bxs_strdup(pString);
1✔
287
    }
288
    size_t appened_num_chars = u32_strlen(pToAppend);
9,062✔
289
    if (appened_num_chars == 0) {
9,062✔
290
        return bxs_strdup(pString);
1✔
291
    }
292
    if (pString == NULL || pString->num_chars == 0) {
9,061✔
293
        return bxs_from_unicode(pToAppend);
2✔
294
    }
295

296
    size_t combined_num_chars = pString->num_chars + appened_num_chars;
9,059✔
297
    uint32_t *s = (uint32_t *) malloc((combined_num_chars + 1) * sizeof(uint32_t));
9,059✔
298
    memcpy(s, pString->memory, pString->num_chars * sizeof(uint32_t));
9,059✔
299
    memcpy(s + pString->num_chars, pToAppend, appened_num_chars * sizeof(uint32_t));
9,059✔
300
    set_char_at(s, combined_num_chars, char_nul);
9,059✔
301

302
    bxstr_t *result = bxs_from_unicode(s);
9,059✔
303
    BFREE(s);
9,059!
304
    return result;
9,059✔
305
}
306

307

308

309
bxstr_t *bxs_concat(size_t count, ...)
2,584✔
310
{
311
    if (count < 1) {
2,584✔
312
        return bxs_from_ascii("");
1✔
313
    }
314

315
    size_t total_len = 0;
2,583✔
316
    uint32_t *src;
317
    va_list va;
318

319
    va_start(va, count);
2,583✔
320
    for (size_t i = 0; i < count; i++) {
18,316✔
321
        src = va_arg(va, uint32_t *);
15,733✔
322
        if (src != NULL) {
15,733✔
323
            total_len += u32_strlen(src);
15,732✔
324
        } else {
325
            total_len += 6;  /* strlen("(NULL)") == 6 */
1✔
326
        }
327
    }
328
    va_end(va);
2,583✔
329

330
    uint32_t *utf32 = (uint32_t *) malloc((total_len + 1) * sizeof(uint32_t));
2,583✔
331
    char *format = repeat("%llU", count);   /* "%llU" stands for a UTF-32 string */
2,583✔
332

333
    va_start(va, count);
2,583✔
334
    u32_vsnprintf(utf32, total_len + 1, format, va);
2,583✔
335
    va_end(va);
2,583✔
336

337
    bxstr_t *result = bxs_from_unicode(utf32);
2,583✔
338
    BFREE(format);
2,583!
339
    BFREE(utf32);
2,583!
340
    return result;
2,583✔
341
}
342

343

344

345
uint32_t *bxs_strchr(bxstr_t *pString, ucs4_t c, int *cursor)
2,211✔
346
{
347
    uint32_t *result = NULL;
2,211✔
348
    if (pString != NULL && pString->num_chars_visible > 0) {
2,211✔
349
        size_t start_idx = cursor != NULL ? *cursor + 1 : 0;
2,209✔
350
        for (size_t i = start_idx; i < pString->num_chars_visible; i++) {
15,803✔
351
            if (pString->memory[pString->visible_char[i]] == c) {
13,604✔
352
                result = pString->memory + pString->visible_char[i];
10✔
353
                if (cursor != NULL) {
10✔
354
                    *cursor = (int) i;
7✔
355
                }
356
                break;
10✔
357
            }
358
        }
359
    }
360
    return result;
2,211✔
361
}
362

363

364

365
bxstr_t *bxs_cut_front(bxstr_t *pString, size_t n)
2,714✔
366
{
367
    if (pString == NULL) {
2,714✔
368
        return NULL;
1✔
369
    }
370
    if (n >= pString->num_chars_visible) {
2,713✔
371
        return bxs_new_empty_string();
33✔
372
    }
373
    if (n == 0) {
2,680✔
374
        return bxs_strdup(pString);
2,467✔
375
    }
376
    uint32_t *s = pString->memory + pString->first_char[n];
213✔
377
    return bxs_from_unicode(s);
213✔
378
}
379

380

381

382
uint32_t *bxs_first_char_ptr(bxstr_t *pString, size_t n)
410✔
383
{
384
    if (pString == NULL) {
410✔
385
        return NULL;
1✔
386
    }
387
    if (n >= pString->num_chars_visible) {
409✔
388
        return pString->memory + pString->first_char[pString->num_chars_visible];  /* pointer to NUL terminator */
10✔
389
    }
390
    return pString->memory + pString->first_char[n];
399✔
391
}
392

393

394

395
uint32_t *bxs_last_char_ptr(bxstr_t *pString)
1,126✔
396
{
397
    if (pString == NULL) {
1,126✔
398
        return NULL;
1✔
399
    }
400
    return pString->memory + pString->first_char[pString->num_chars_visible];
1,125✔
401
}
402

403

404

405
uint32_t *bxs_unindent_ptr(bxstr_t *pString)
14,074✔
406
{
407
    if (pString == NULL) {
14,074✔
408
        return NULL;
1✔
409
    }
410
    return pString->memory + pString->first_char[pString->indent];
14,073✔
411
}
412

413

414

415
bxstr_t *bxs_trim(bxstr_t *pString)
19✔
416
{
417
    if (pString == NULL) {
19✔
418
        return NULL;
1✔
419
    }
420
    if (pString->indent == 0 && pString->trailing == 0) {
18!
421
        return bxs_strdup(pString);
1✔
422
    }
423
    if (pString->indent + pString->trailing == pString->num_chars_visible) {
17✔
424
        return bxs_new_empty_string();
1✔
425
    }
426

427
    uint32_t *e = u32_strdup(pString->memory);
16✔
428
    set_char_at(e, pString->first_char[pString->num_chars_visible - pString->trailing], char_nul);
16✔
429
    uint32_t *s = e + pString->first_char[pString->indent];
16✔
430
    bxstr_t *result = bxs_from_unicode(s);
16✔
431
    BFREE(e);
16!
432
    return result;
16✔
433
}
434

435

436

437
uint32_t *bxs_ltrim(bxstr_t *pString, size_t max)
5✔
438
{
439
    if (pString == NULL) {
5✔
440
        return NULL;
1✔
441
    }
442

443
    size_t num_trimmed = BMIN(max, pString->num_chars_visible);
4✔
444
    for (size_t i = 0; i < max; i++) {
14✔
445
        if (!is_blank(pString->memory[pString->visible_char[i]])) {
12✔
446
            num_trimmed = i;
2✔
447
            break;
2✔
448
        }
449
    }
450
    return u32_strdup(pString->memory + pString->first_char[num_trimmed]);
4✔
451
}
452

453

454

455
bxstr_t *bxs_rtrim(bxstr_t *pString)
13,228✔
456
{
457
    if (pString == NULL) {
13,228✔
458
        return NULL;
1✔
459
    }
460
    if (pString->trailing == 0) {
13,227✔
461
        return bxs_strdup(pString);
11,740✔
462
    }
463

464
    uint32_t *s = u32_strdup(pString->memory);
1,487✔
465
    set_char_at(s, pString->first_char[pString->num_chars_visible - pString->trailing], char_nul);
1,487✔
466
    bxstr_t *result = bxs_from_unicode(s);
1,487✔
467
    BFREE(s);
1,487!
468
    return result;
1,487✔
469
}
470

471

472

473
bxstr_t *bxs_prepend_spaces(bxstr_t *pString, size_t n)
888✔
474
{
475
    bxstr_t *result = NULL;
888✔
476
    if (n == 0) {
888✔
477
        result = bxs_strdup(pString);
876✔
478
    }
479
    else if (pString == NULL) {
12✔
480
        uint32_t *s = u32_nspaces(n);
1✔
481
        result = bxs_from_unicode(s);
1✔
482
        BFREE(s);
1!
483
    }
484
    else {
485
        result = (bxstr_t *) calloc(1, sizeof(bxstr_t));
11✔
486

487
        result->memory = (uint32_t *) malloc((pString->num_chars + n + 1) * sizeof(uint32_t));
11✔
488
        uint32_t *s = u32_nspaces(n);
11✔
489
        u32_cpy(result->memory, s, n);
11✔
490
        BFREE(s);
11!
491
        u32_cpy(result->memory + n, pString->memory, pString->num_chars);
11✔
492
        set_char_at(result->memory, pString->num_chars + n, char_nul);
11✔
493

494
        result->ascii = (char *) malloc((pString->num_columns + n + 1) * sizeof(char));
11✔
495
        memset(result->ascii, ' ', n);
11✔
496
        strcpy(result->ascii + n, pString->ascii);
11✔
497

498
        result->indent = pString->indent + n;
11✔
499
        result->num_columns = pString->num_columns + n;
11✔
500
        result->num_chars = pString->num_chars + n;
11✔
501
        result->num_chars_visible = pString->num_chars_visible + n;
11✔
502
        result->num_chars_invisible = pString->num_chars_invisible;
11✔
503
        result->trailing = pString->trailing;
11✔
504

505
        result->first_char = (size_t *) malloc((pString->num_chars_visible + n + 1) * sizeof(size_t));
11✔
506
        result->visible_char = (size_t *) malloc((pString->num_chars_visible + n + 1) * sizeof(size_t));
11✔
507
        for (size_t i=0; i < n; i++) {
51✔
508
            result->first_char[i] = i;
40✔
509
            result->visible_char[i] = i;
40✔
510
        }
511
        for (size_t i=0; i <= pString->num_chars_visible; i++) {
244✔
512
            result->first_char[i + n] = pString->first_char[i] + n;
233✔
513
            result->visible_char[i + n] = pString->visible_char[i] + n;
233✔
514
        }
515
    }
516
    return result;
888✔
517
}
518

519

520

521
void bxs_append_spaces(bxstr_t *pString, size_t n)
798✔
522
{
523
    if (pString == NULL || n == 0) {
798✔
524
        return;
2✔
525
    }
526

527
    pString->memory = (uint32_t *) realloc(pString->memory, (pString->num_chars + n + 1) * sizeof(uint32_t));
796✔
528
    u32_set(pString->memory + pString->num_chars, char_space, n);
796✔
529
    set_char_at(pString->memory, pString->num_chars + n, char_nul);
796✔
530

531
    pString->ascii = (char *) realloc(pString->ascii, (pString->num_columns + n + 1) * sizeof(char));
796✔
532
    memset(pString->ascii + pString->num_columns, ' ', n);
796✔
533
    pString->ascii[pString->num_columns + n] = '\0';
796✔
534

535
    pString->first_char =
796✔
536
            (size_t *) realloc(pString->first_char, (pString->num_chars_visible + n + 1) * sizeof(size_t));
796✔
537
    pString->visible_char =
796✔
538
            (size_t *) realloc(pString->visible_char, (pString->num_chars_visible + n + 1) * sizeof(size_t));
796✔
539
    for (size_t i = 0; i <= n; i++) {
7,455✔
540
        pString->first_char[pString->num_chars_visible + i] = pString->num_chars + i;
6,659✔
541
        pString->visible_char[pString->num_chars_visible + i] = pString->num_chars + i;
6,659✔
542
    }
543

544
    pString->num_chars += n;
796✔
545
    pString->num_chars_visible += n;
796✔
546
    pString->num_columns += n;
796✔
547
    pString->trailing += n;
796✔
548
}
549

550

551

552
char *bxs_to_output(bxstr_t *pString)
2,949✔
553
{
554
    if (pString == NULL) {
2,949✔
555
        return strdup("NULL");
1✔
556
    }
557

558
    if (color_output_enabled) {
2,948✔
559
        return u32_strconv_to_output(pString->memory);
2,947✔
560
    }
561

562
    uint32_t *vis = bxs_filter_visible(pString);
1✔
563
    char *result = u32_strconv_to_output(vis);
1✔
564
    BFREE(vis);
1!
565
    return result;
1✔
566
}
567

568

569

570
int bxs_is_empty(bxstr_t *pString)
20,074✔
571
{
572
    if (pString == NULL) {
20,074✔
573
        return 1;
2✔
574
    }
575
    return pString->num_chars > 0 ? 0 : 1;
20,072✔
576
}
577

578

579

580
int bxs_is_blank(bxstr_t *pString)
19,192✔
581
{
582
    if (bxs_is_empty(pString)) {
19,192✔
583
        return 1;
27✔
584
    }
585
    for (size_t i = 0; i < pString->num_chars_visible; i++) {
50,282✔
586
        ucs4_t c = pString->memory[pString->visible_char[i]];
43,587✔
587
        if (c != char_tab && c != char_cr && !uc_is_blank(c)) {
43,587!
588
            return 0;
12,470✔
589
        }
590
    }
591
    return 1;
6,695✔
592
}
593

594

595

596
int bxs_is_visible_char(bxstr_t *pString, size_t idx)
10✔
597
{
598
    int result = 0;
10✔
599
    if (pString != NULL) {
10✔
600
        for (size_t i = 0; i <= idx && i < pString->num_chars_visible; i++) {
24!
601
            if (pString->visible_char[i] == idx) {
20✔
602
                result = 1;
3✔
603
                break;
3✔
604
            }
605
            else if (pString->visible_char[i] > idx) {
17✔
606
                break;
2✔
607
            }
608
        }
609
    }
610
    return result;
10✔
611
}
612

613

614

615
uint32_t *bxs_filter_visible(bxstr_t *pString)
154✔
616
{
617
    uint32_t *result = NULL;
154✔
618
    if (pString != NULL) {
154✔
619
        if (pString->num_chars_invisible == 0) {
153✔
620
            result = u32_strdup(pString->memory);
18✔
621
        }
622
        else {
623
            result = (uint32_t *) calloc(pString->num_chars_visible + 1, sizeof(uint32_t));
135✔
624
            for (size_t i = 0; i < pString->num_chars_visible; i++) {
1,855✔
625
                set_char_at(result, i, pString->memory[pString->visible_char[i]]);
1,720✔
626
            }
627
            set_char_at(result, pString->num_chars_visible, char_nul);
135✔
628
        }
629
    }
630
    return result;
154✔
631
}
632

633

634

635
int bxs_strcmp(bxstr_t *s1, bxstr_t *s2)
19✔
636
{
637
    if (s1 == NULL) {
19✔
638
        if (s2 == NULL) {
2✔
639
            return 0;
1✔
640
        }
641
        else {
642
            return 1;
1✔
643
        }
644
    }
645
    if (s2 == NULL) {
17✔
646
        return -1;
1✔
647
    }
648
    return u32_strcmp(s1->memory, s2->memory);
16✔
649
}
650

651

652

653
static int bxs_valid_in_context(bxstr_t *pString, size_t *error_pos, int (*predicate)(const ucs4_t))
23,887✔
654
{
655
    if (pString == NULL) {
23,887✔
656
        if (error_pos != NULL) {
2✔
657
            *error_pos = 0;
1✔
658
        }
659
        return 0;   /* invalid */
2✔
660
    }
661

662
    for (size_t i = 0; pString->memory[i] != char_nul; i++) {
478,615✔
663
        if ((*predicate)(pString->memory[i]) == 0) {
454,732✔
664
            if (error_pos != NULL) {
2✔
665
                *error_pos = i;
1✔
666
            }
667
            return 0;   /* invalid */
2✔
668
        }
669
    }
670

671
    return 1;   /* valid */
23,883✔
672
}
673

674

675

676
int bxs_valid_anywhere(bxstr_t *pString, size_t *error_pos)
4,578✔
677
{
678
    return bxs_valid_in_context(pString, error_pos, &is_allowed_anywhere);
4,578✔
679
}
680

681

682

683
int bxs_valid_in_shape(bxstr_t *pString, size_t *error_pos)
16,952✔
684
{
685
    return pString->num_chars_visible > 0 && bxs_valid_in_context(pString, error_pos, &is_allowed_in_shape);
16,952!
686
}
687

688

689

690
int bxs_valid_in_sample(bxstr_t *pString, size_t *error_pos)
865✔
691
{
692
    return pString->num_chars_visible > 0 && bxs_valid_in_context(pString, error_pos, &is_allowed_in_sample);
865!
693
}
694

695

696

697
int bxs_valid_in_filename(bxstr_t *pString, size_t *error_pos)
17✔
698
{
699
    return pString->num_chars_visible > 0 && pString->num_chars_invisible == 0
16✔
700
            && bxs_valid_in_context(pString, error_pos, &is_allowed_in_filename);
33!
701
}
702

703

704

705
int bxs_valid_in_kv_string(bxstr_t *pString, size_t *error_pos)
1,477✔
706
{
707
    return bxs_valid_in_context(pString, error_pos, &is_allowed_in_kv_string);
1,477✔
708
}
709

710

711

712
void bxs_free(bxstr_t *pString)
38,044✔
713
{
714
    if (pString != NULL) {
38,044✔
715
        BFREE(pString->memory);
37,527✔
716
        BFREE(pString->ascii);
37,527✔
717
        BFREE(pString->first_char);
37,527✔
718
        BFREE(pString->visible_char);
37,527✔
719
        BFREE(pString);
37,527!
720
    }
721
}
38,044✔
722

723

724
/* vim: set cindent 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