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

systemd / systemd / 14872145375

06 May 2025 09:07PM UTC coverage: 72.232% (+0.02%) from 72.214%
14872145375

push

github

DaanDeMeyer
string-table: annotate _to_string and _from_string with _const_ and _pure_, respectively

Follow-up for c94f6ab1b

297286 of 411572 relevant lines covered (72.23%)

695615.99 hits per line

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

93.08
/src/basic/string-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <errno.h>
4
#include <stdarg.h>
5
#include <stdint.h>
6
#include <stdio.h>
7
#include <stdlib.h>
8

9
#include "alloc-util.h"
10
#include "escape.h"
11
#include "extract-word.h"
12
#include "fd-util.h"
13
#include "fileio.h"
14
#include "glyph-util.h"
15
#include "gunicode.h"
16
#include "locale-util.h"
17
#include "log.h"
18
#include "macro.h"
19
#include "memory-util.h"
20
#include "memstream-util.h"
21
#include "path-util.h"
22
#include "string-util.h"
23
#include "strv.h"
24
#include "terminal-util.h"
25
#include "utf8.h"
26

27
char* first_word(const char *s, const char *word) {
3,050,953✔
28
        assert(s);
3,050,953✔
29
        assert(word);
3,050,953✔
30

31
        /* Checks if the string starts with the specified word, either followed by NUL or by whitespace.
32
         * Returns a pointer to the NUL or the first character after the whitespace. */
33

34
        if (isempty(word))
3,050,953✔
35
                return (char*) s;
36

37
        const char *p = startswith(s, word);
3,050,952✔
38
        if (!p)
3,050,952✔
39
                return NULL;
40
        if (*p == '\0')
103,811✔
41
                return (char*) p;
42

43
        const char *nw = skip_leading_chars(p, WHITESPACE);
103,810✔
44
        if (p == nw)
103,810✔
45
                return NULL;
1✔
46

47
        return (char*) nw;
48
}
49

50
char* strprepend(char **x, const char *s) {
5✔
51
        assert(x);
5✔
52

53
        if (isempty(s) && *x)
6✔
54
                return *x;
5✔
55

56
        char *p = strjoin(strempty(s), *x);
6✔
57
        if (!p)
5✔
58
                return NULL;
59

60
        free_and_replace(*x, p);
5✔
61
        return *x;
5✔
62
}
63

64
char* strextendn(char **x, const char *s, size_t l) {
27,389✔
65
        assert(x);
27,389✔
66
        assert(s || l == 0);
27,389✔
67

68
        if (l > 0)
27,389✔
69
                l = strnlen(s, l); /* ignore trailing noise */
26,749✔
70

71
        if (l > 0 || !*x) {
27,389✔
72
                size_t q;
27,076✔
73
                char *m;
27,076✔
74

75
                q = strlen_ptr(*x);
27,076✔
76
                m = realloc(*x, q + l + 1);
27,076✔
77
                if (!m)
27,076✔
78
                        return NULL;
79

80
                *mempcpy_typesafe(m + q, s, l) = 0;
27,076✔
81

82
                *x = m;
27,076✔
83
        }
84

85
        return *x;
27,389✔
86
}
87

88
char* strstrip(char *s) {
5,200,232✔
89
        if (!s)
5,200,232✔
90
                return NULL;
91

92
        /* Drops trailing whitespace. Modifies the string in place. Returns pointer to first non-space character */
93

94
        return delete_trailing_chars(skip_leading_chars(s, WHITESPACE), WHITESPACE);
5,200,232✔
95
}
96

97
char* delete_chars(char *s, const char *bad) {
9✔
98
        char *f, *t;
9✔
99

100
        /* Drops all specified bad characters, regardless where in the string */
101

102
        if (!s)
9✔
103
                return NULL;
104

105
        if (!bad)
9✔
106
                bad = WHITESPACE;
×
107

108
        for (f = s, t = s; *f; f++) {
131✔
109
                if (strchr(bad, *f))
122✔
110
                        continue;
67✔
111

112
                *(t++) = *f;
55✔
113
        }
114

115
        *t = 0;
9✔
116

117
        return s;
9✔
118
}
119

120
char* delete_trailing_chars(char *s, const char *bad) {
6,248,317✔
121
        char *c = s;
6,248,317✔
122

123
        /* Drops all specified bad characters, at the end of the string */
124

125
        if (!s)
6,248,317✔
126
                return NULL;
127

128
        if (!bad)
6,248,317✔
129
                bad = WHITESPACE;
63,952✔
130

131
        for (char *p = s; *p; p++)
252,909,401✔
132
                if (!strchr(bad, *p))
246,661,084✔
133
                        c = p + 1;
236,475,775✔
134

135
        *c = 0;
6,248,317✔
136

137
        return s;
6,248,317✔
138
}
139

140
char* truncate_nl_full(char *s, size_t *ret_len) {
2,875,009✔
141
        size_t n;
2,875,009✔
142

143
        assert(s);
2,875,009✔
144

145
        n = strcspn(s, NEWLINE);
2,875,009✔
146
        s[n] = '\0';
2,875,009✔
147
        if (ret_len)
2,875,009✔
148
                *ret_len = n;
6✔
149
        return s;
2,875,009✔
150
}
151

152
char ascii_tolower(char x) {
4,152,724✔
153

154
        if (x >= 'A' && x <= 'Z')
4,152,724✔
155
                return x - 'A' + 'a';
172,830✔
156

157
        return x;
158
}
159

160
char ascii_toupper(char x) {
289,609✔
161

162
        if (x >= 'a' && x <= 'z')
289,609✔
163
                return x - 'a' + 'A';
180,794✔
164

165
        return x;
166
}
167

168
char* ascii_strlower(char *t) {
22,482✔
169
        assert(t);
22,482✔
170

171
        for (char *p = t; *p; p++)
144,123✔
172
                *p = ascii_tolower(*p);
121,641✔
173

174
        return t;
22,482✔
175
}
176

177
char* ascii_strupper(char *t) {
21,041✔
178
        assert(t);
21,041✔
179

180
        for (char *p = t; *p; p++)
310,496✔
181
                *p = ascii_toupper(*p);
289,455✔
182

183
        return t;
21,041✔
184
}
185

186
char* ascii_strlower_n(char *t, size_t n) {
691,602✔
187
        if (n <= 0)
691,602✔
188
                return t;
189

190
        for (size_t i = 0; i < n; i++)
2,272,367✔
191
                t[i] = ascii_tolower(t[i]);
1,581,055✔
192

193
        return t;
194
}
195

196
int ascii_strcasecmp_n(const char *a, const char *b, size_t n) {
620,703✔
197

198
        for (; n > 0; a++, b++, n--) {
1,707,027✔
199
                int x, y;
1,220,896✔
200

201
                x = (int) (uint8_t) ascii_tolower(*a);
1,220,896✔
202
                y = (int) (uint8_t) ascii_tolower(*b);
1,220,896✔
203

204
                if (x != y)
1,220,896✔
205
                        return x - y;
134,572✔
206
        }
207

208
        return 0;
209
}
210

211
int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) {
468,816✔
212
        int r;
468,816✔
213

214
        r = ascii_strcasecmp_n(a, b, MIN(n, m));
468,816✔
215
        if (r != 0)
468,816✔
216
                return r;
217

218
        return CMP(n, m);
434,664✔
219
}
220

221
bool chars_intersect(const char *a, const char *b) {
502✔
222
        /* Returns true if any of the chars in a are in b. */
223
        for (const char *p = a; *p; p++)
5,372✔
224
                if (strchr(b, *p))
4,883✔
225
                        return true;
226

227
        return false;
228
}
229

230
bool string_has_cc(const char *p, const char *ok) {
181,579✔
231
        assert(p);
181,579✔
232

233
        /*
234
         * Check if a string contains control characters. If 'ok' is
235
         * non-NULL it may be a string containing additional CCs to be
236
         * considered OK.
237
         */
238

239
        for (const char *t = p; *t; t++) {
1,368,114✔
240
                if (ok && strchr(ok, *t))
1,186,551✔
241
                        continue;
8✔
242

243
                if (char_is_cc(*t))
1,186,543✔
244
                        return true;
245
        }
246

247
        return false;
248
}
249

250
static int write_ellipsis(char *buf, bool unicode) {
4,233✔
251
        const char *s = glyph_full(GLYPH_ELLIPSIS, unicode);
4,233✔
252
        assert(strlen(s) == 3);
4,233✔
253
        memcpy(buf, s, 3);
4,233✔
254
        return 3;
4,233✔
255
}
256

257
static size_t ansi_sequence_length(const char *s, size_t len) {
6,271✔
258
        assert(s);
6,271✔
259

260
        if (len < 2)
6,271✔
261
                return 0;
262

263
        if (s[0] != 0x1B)  /* ASCII 27, aka ESC, aka Ctrl-[ */
6,181✔
264
                return 0;  /* Not the start of a sequence */
265

266
        if (s[1] == 0x5B) { /* [, start of CSI sequence */
895✔
267
                size_t i = 2;
895✔
268

269
                if (i == len)
895✔
270
                        return 0;
271

272
                while (s[i] >= 0x30 && s[i] <= 0x3F) /* Parameter bytes */
9,184✔
273
                        if (++i == len)
8,289✔
274
                                return 0;
275
                while (s[i] >= 0x20 && s[i] <= 0x2F) /* Intermediate bytes */
895✔
276
                        if (++i == len)
×
277
                                return 0;
278
                if (s[i] >= 0x40 && s[i] <= 0x7E) /* Final byte */
895✔
279
                        return i + 1;
895✔
280
                return 0;  /* Bad sequence */
281

282
        } else if (s[1] >= 0x40 && s[1] <= 0x5F) /* other non-CSI Fe sequence */
×
283
                return 2;
×
284

285
        return 0;  /* Bad escape? */
286
}
287

288
static bool string_has_ansi_sequence(const char *s, size_t len) {
7,627✔
289
        const char *t = s;
7,627✔
290

291
        while ((t = memchr(s, 0x1B, len - (t - s))))
7,627✔
292
                if (ansi_sequence_length(t, len - (t - s)) > 0)
151✔
293
                        return true;
294
        return false;
295
}
296

297
static size_t previous_ansi_sequence(const char *s, size_t length, const char **ret_where) {
263✔
298
        /* Locate the previous ANSI sequence and save its start in *ret_where and return length. */
299

300
        for (size_t i = length - 2; i > 0; i--) {  /* -2 because at least two bytes are needed */
3,409✔
301
                size_t slen = ansi_sequence_length(s + (i - 1), length - (i - 1));
3,407✔
302
                if (slen == 0)
3,407✔
303
                        continue;
3,146✔
304

305
                *ret_where = s + (i - 1);
261✔
306
                return slen;
261✔
307
        }
308

309
        *ret_where = NULL;
2✔
310
        return 0;
2✔
311
}
312

313
static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
4,127✔
314
        size_t x, need_space, suffix_len;
4,127✔
315
        char *t;
4,127✔
316

317
        assert(s);
4,127✔
318
        assert(percent <= 100);
4,127✔
319
        assert(new_length != SIZE_MAX);
4,127✔
320

321
        if (old_length <= new_length)
4,127✔
322
                return strndup(s, old_length);
1,637✔
323

324
        /* Special case short ellipsations */
325
        switch (new_length) {
2,490✔
326

327
        case 0:
×
328
                return strdup("");
×
329

330
        case 1:
65✔
331
                if (is_locale_utf8())
65✔
332
                        return strdup("…");
65✔
333
                else
334
                        return strdup(".");
×
335

336
        case 2:
35✔
337
                if (!is_locale_utf8())
35✔
338
                        return strdup("..");
×
339
                break;
340
        }
341

342
        /* Calculate how much space the ellipsis will take up. If we are in UTF-8 mode we only need space for one
343
         * character ("…"), otherwise for three characters ("..."). Note that in both cases we need 3 bytes of storage,
344
         * either for the UTF-8 encoded character or for three ASCII characters. */
345
        need_space = is_locale_utf8() ? 1 : 3;
2,425✔
346

347
        t = new(char, new_length+3);
2,425✔
348
        if (!t)
2,425✔
349
                return NULL;
350

351
        assert(new_length >= need_space);
2,425✔
352

353
        x = ((new_length - need_space) * percent + 50) / 100;
2,425✔
354
        assert(x <= new_length - need_space);
2,425✔
355

356
        write_ellipsis(mempcpy(t, s, x), /* unicode = */ false);
2,425✔
357
        suffix_len = new_length - x - need_space;
2,425✔
358
        memcpy(t + x + 3, s + old_length - suffix_len, suffix_len);
2,425✔
359
        *(t + x + 3 + suffix_len) = '\0';
2,425✔
360

361
        return t;
2,425✔
362
}
363

364
char* ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
7,798✔
365
        size_t x, k, len, len2;
7,798✔
366
        const char *i, *j;
7,798✔
367
        int r;
7,798✔
368

369
        /* Note that 'old_length' refers to bytes in the string, while 'new_length' refers to character cells taken up
370
         * on screen. This distinction doesn't matter for ASCII strings, but it does matter for non-ASCII UTF-8
371
         * strings.
372
         *
373
         * Ellipsation is done in a locale-dependent way:
374
         * 1. If the string passed in is fully ASCII and the current locale is not UTF-8, three dots are used ("...")
375
         * 2. Otherwise, a unicode ellipsis is used ("…")
376
         *
377
         * In other words: you'll get a unicode ellipsis as soon as either the string contains non-ASCII characters or
378
         * the current locale is UTF-8.
379
         */
380

381
        assert(s);
7,798✔
382
        assert(percent <= 100);
7,798✔
383

384
        if (new_length == SIZE_MAX)
7,798✔
385
                return strndup(s, old_length);
×
386

387
        if (new_length == 0)
7,798✔
388
                return strdup("");
171✔
389

390
        bool has_ansi_seq = string_has_ansi_sequence(s, old_length);
7,627✔
391

392
        /* If no multibyte characters or ANSI sequences, use ascii_ellipsize_mem for speed */
393
        if (!has_ansi_seq && ascii_is_valid_n(s, old_length))
7,627✔
394
                return ascii_ellipsize_mem(s, old_length, new_length, percent);
4,127✔
395

396
        x = (new_length - 1) * percent / 100;
3,500✔
397
        assert(x <= new_length - 1);
3,500✔
398

399
        k = 0;
400
        for (i = s; i < s + old_length; ) {
48,231✔
401
                size_t slen = has_ansi_seq ? ansi_sequence_length(i, old_length - (i - s)) : 0;
47,040✔
402
                if (slen > 0) {
984✔
403
                        i += slen;
221✔
404
                        continue;  /* ANSI sequences don't take up any space in output */
221✔
405
                }
406

407
                char32_t c;
46,819✔
408
                r = utf8_encoded_to_unichar(i, &c);
46,819✔
409
                if (r < 0)
46,819✔
410
                        return NULL;
×
411

412
                int w = unichar_iswide(c) ? 2 : 1;
46,819✔
413
                if (k + w > x)
46,819✔
414
                        break;
415

416
                k += w;
44,510✔
417
                i += r;
44,510✔
418
        }
419

420
        const char *ansi_start = s + old_length;
3,500✔
421
        size_t ansi_len = 0;
3,500✔
422

423
        for (const char *t = j = s + old_length; t > i && k < new_length; ) {
12,939✔
424
                char32_t c;
9,525✔
425
                int w;
9,525✔
426
                const char *tt;
9,525✔
427

428
                if (has_ansi_seq && ansi_start >= t)
9,525✔
429
                        /* Figure out the previous ANSI sequence, if any */
430
                        ansi_len = previous_ansi_sequence(s, t - s, &ansi_start);
263✔
431

432
                /* If the sequence extends all the way to the current position, skip it. */
433
                if (has_ansi_seq && ansi_len > 0 && ansi_start + ansi_len == t) {
9,525✔
434
                        t = ansi_start;
112✔
435
                        continue;
112✔
436
                }
437

438
                tt = utf8_prev_char(t);
9,413✔
439
                r = utf8_encoded_to_unichar(tt, &c);
9,413✔
440
                if (r < 0)
9,413✔
441
                        return NULL;
×
442

443
                w = unichar_iswide(c) ? 2 : 1;
9,413✔
444
                if (k + w > new_length)
9,413✔
445
                        break;
446

447
                k += w;
9,327✔
448
                j = t = tt;  /* j should always point to the first "real" character */
9,327✔
449
        }
450

451
        /* We don't actually need to ellipsize */
452
        if (i >= j)
3,500✔
453
                return memdup_suffix0(s, old_length);
1,716✔
454

455
        if (k >= new_length) {
1,784✔
456
                /* Make space for ellipsis, if required and possible. We know that the edge character is not
457
                 * part of an ANSI sequence (because then we'd skip it). If the last character we looked at
458
                 * was wide, we don't need to make space. */
459
                if (j < s + old_length)
1,698✔
460
                        j = utf8_next_char(j);
1,698✔
461
                else if (i > s)
×
462
                        i = utf8_prev_char(i);
×
463
        }
464

465
        len = i - s;
1,784✔
466
        len2 = s + old_length - j;
1,784✔
467

468
        /* If we have ANSI, allow the same length as the source string + ellipsis. It'd be too involved to
469
         * figure out what exact space is needed. Strings with ANSI sequences are most likely to be fairly
470
         * short anyway. */
471
        size_t alloc_len = has_ansi_seq ? old_length + 3 + 1 : len + 3 + len2 + 1;
1,784✔
472

473
        char *e = new(char, alloc_len);
1,784✔
474
        if (!e)
1,784✔
475
                return NULL;
476

477
        memcpy_safe(e, s, len);
1,784✔
478
        write_ellipsis(e + len, /* unicode = */ true);
1,784✔
479

480
        char *dst = e + len + 3;
1,784✔
481

482
        if (has_ansi_seq)
1,784✔
483
                /* Copy over any ANSI sequences in full */
484
                for (const char *p = s + len; p < j; ) {
1,864✔
485
                        size_t slen = ansi_sequence_length(p, j - p);
1,729✔
486
                        if (slen > 0) {
1,729✔
487
                                dst = mempcpy(dst, p, slen);
262✔
488
                                p += slen;
262✔
489
                        } else
490
                                p = utf8_next_char(p);
1,467✔
491
                }
492

493
        memcpy_safe(dst, j, len2);
1,784✔
494
        dst[len2] = '\0';
1,784✔
495

496
        return e;
1,784✔
497
}
498

499
char* cellescape(char *buf, size_t len, const char *s) {
43,478✔
500
        /* Escape and ellipsize s into buffer buf of size len. Only non-control ASCII
501
         * characters are copied as they are, everything else is escaped. The result
502
         * is different then if escaping and ellipsization was performed in two
503
         * separate steps, because each sequence is either stored in full or skipped.
504
         *
505
         * This function should be used for logging about strings which expected to
506
         * be plain ASCII in a safe way.
507
         *
508
         * An ellipsis will be used if s is too long. It was always placed at the
509
         * very end.
510
         */
511

512
        size_t i = 0, last_char_width[4] = {}, k = 0;
43,478✔
513

514
        assert(buf);
43,478✔
515
        assert(len > 0); /* at least a terminating NUL */
43,478✔
516
        assert(s);
43,478✔
517

518
        for (;;) {
959,224✔
519
                char four[4];
501,351✔
520
                int w;
501,351✔
521

522
                if (*s == 0) /* terminating NUL detected? then we are done! */
501,351✔
523
                        goto done;
43,442✔
524

525
                w = cescape_char(*s, four);
457,909✔
526
                if (i + w + 1 > len) /* This character doesn't fit into the buffer anymore? In that case let's
457,909✔
527
                                      * ellipsize at the previous location */
528
                        break;
529

530
                /* OK, there was space, let's add this escaped character to the buffer */
531
                memcpy(buf + i, four, w);
457,873✔
532
                i += w;
457,873✔
533

534
                /* And remember its width in the ring buffer */
535
                last_char_width[k] = w;
457,873✔
536
                k = (k + 1) % 4;
457,873✔
537

538
                s++;
457,873✔
539
        }
540

541
        /* Ellipsation is necessary. This means we might need to truncate the string again to make space for 4
542
         * characters ideally, but the buffer is shorter than that in the first place take what we can get */
543
        for (size_t j = 0; j < ELEMENTSOF(last_char_width); j++) {
81✔
544

545
                if (i + 4 <= len) /* nice, we reached our space goal */
81✔
546
                        break;
547

548
                k = k == 0 ? 3 : k - 1;
57✔
549
                if (last_char_width[k] == 0) /* bummer, we reached the beginning of the strings */
57✔
550
                        break;
551

552
                assert(i >= last_char_width[k]);
45✔
553
                i -= last_char_width[k];
45✔
554
        }
555

556
        if (i + 4 <= len) /* yay, enough space */
36✔
557
                i += write_ellipsis(buf + i, /* unicode = */ false);
24✔
558
        else if (i + 3 <= len) { /* only space for ".." */
12✔
559
                buf[i++] = '.';
4✔
560
                buf[i++] = '.';
4✔
561
        } else if (i + 2 <= len) /* only space for a single "." */
8✔
562
                buf[i++] = '.';
4✔
563
        else
564
                assert(i + 1 <= len);
4✔
565

566
done:
43,478✔
567
        buf[i] = '\0';
43,478✔
568
        return buf;
43,478✔
569
}
570

571
char* strshorten(char *s, size_t l) {
25,118✔
572
        assert(s);
25,118✔
573

574
        if (l >= SIZE_MAX-1) /* Would not change anything */
25,118✔
575
                return s;
576

577
        if (strnlen(s, l+1) > l)
25,116✔
578
                s[l] = 0;
21✔
579

580
        return s;
581
}
582

583
int strgrowpad0(char **s, size_t l) {
16✔
584
        size_t sz;
16✔
585

586
        assert(s);
16✔
587

588
        if (*s) {
16✔
589
                sz = strlen(*s) + 1;
16✔
590
                if (sz >= l) /* never shrink */
16✔
591
                        return 0;
592
        } else
593
                sz = 0;
594

595
        char *q = realloc(*s, l);
16✔
596
        if (!q)
16✔
597
                return -ENOMEM;
598

599
        *s = q;
16✔
600

601
        memzero(*s + sz, l - sz);
16✔
602
        return 0;
603
}
604

605
char* strreplace(const char *text, const char *old_string, const char *new_string) {
3,932✔
606
        size_t l, old_len, new_len;
3,932✔
607
        char *t, *ret = NULL;
3,932✔
608
        const char *f;
3,932✔
609

610
        assert(old_string);
3,932✔
611
        assert(new_string);
3,932✔
612

613
        if (!text)
3,932✔
614
                return NULL;
3,932✔
615

616
        old_len = strlen(old_string);
3,931✔
617
        new_len = strlen(new_string);
3,931✔
618

619
        l = strlen(text);
3,931✔
620
        if (!GREEDY_REALLOC(ret, l+1))
3,931✔
621
                return NULL;
622

623
        f = text;
3,931✔
624
        t = ret;
3,931✔
625
        while (*f) {
111,015✔
626
                size_t d, nl;
107,084✔
627

628
                if (!startswith(f, old_string)) {
107,084✔
629
                        *(t++) = *(f++);
104,490✔
630
                        continue;
104,490✔
631
                }
632

633
                d = t - ret;
2,594✔
634
                nl = l - old_len + new_len;
2,594✔
635

636
                if (!GREEDY_REALLOC(ret, nl + 1))
2,594✔
637
                        return mfree(ret);
×
638

639
                l = nl;
2,594✔
640
                t = ret + d;
2,594✔
641

642
                t = stpcpy(t, new_string);
2,594✔
643
                f += old_len;
2,594✔
644
        }
645

646
        *t = 0;
3,931✔
647
        return ret;
3,931✔
648
}
649

650
static void advance_offsets(
386✔
651
                ssize_t diff,
652
                size_t offsets[2], /* note: we can't use [static 2] here, since this may be NULL */
653
                size_t shift[static 2],
654
                size_t size) {
655

656
        if (!offsets)
386✔
657
                return;
658

659
        assert(shift);
376✔
660

661
        if ((size_t) diff < offsets[0])
376✔
662
                shift[0] += size;
×
663
        if ((size_t) diff < offsets[1])
376✔
664
                shift[1] += size;
×
665
}
666

667
char* strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
96,968✔
668
        const char *begin = NULL;
96,968✔
669
        enum {
96,968✔
670
                STATE_OTHER,
671
                STATE_ESCAPE,
672
                STATE_CSI,
673
                STATE_OSC,
674
                STATE_OSC_CLOSING,
675
        } state = STATE_OTHER;
96,968✔
676
        _cleanup_(memstream_done) MemStream m = {};
96,968✔
677
        size_t isz, shift[2] = {}, n_carriage_returns = 0;
96,968✔
678
        FILE *f;
96,968✔
679

680
        assert(ibuf);
96,968✔
681
        assert(*ibuf);
96,968✔
682

683
        /* This does three things:
684
         *
685
         * 1. Replaces TABs by 8 spaces
686
         * 2. Strips ANSI color sequences (a subset of CSI), i.e. ESC '[' … 'm' sequences
687
         * 3. Strips ANSI operating system sequences (OSC), i.e. ESC ']' … ST sequences
688
         * 4. Strip trailing \r characters (since they would "move the cursor", but have no
689
         *    other effect).
690
         *
691
         * Everything else will be left as it is. In particular other ANSI sequences are left as they are, as
692
         * are any other special characters. Truncated ANSI sequences are left-as is too. This call is
693
         * supposed to suppress the most basic formatting noise, but nothing else.
694
         *
695
         * Why care for OSC sequences? Well, to undo what terminal_urlify() and friends generate. */
696

697
        isz = _isz ? *_isz : strlen(*ibuf);
96,968✔
698

699
        /* Note we turn off internal locking on f for performance reasons. It's safe to do so since we
700
         * created f here and it doesn't leave our scope. */
701
        f = memstream_init(&m);
96,968✔
702
        if (!f)
96,968✔
703
                return NULL;
704

705
        for (const char *i = *ibuf; i < *ibuf + isz + 1; i++) {
10,985,027✔
706

707
                bool eot = i >= *ibuf + isz;
10,888,059✔
708

709
                switch (state) {
10,888,059✔
710

711
                case STATE_OTHER:
10,887,976✔
712
                        if (eot)
10,887,976✔
713
                                break;
714

715
                        if (*i == '\r') {
10,791,008✔
716
                                n_carriage_returns++;
7✔
717
                                break;
7✔
718
                        } else if (*i == '\n')
10,791,001✔
719
                                /* Ignore carriage returns before new line */
720
                                n_carriage_returns = 0;
37✔
721
                        for (; n_carriage_returns > 0; n_carriage_returns--)
10,791,003✔
722
                                fputc('\r', f);
2✔
723

724
                        if (*i == '\x1B')
10,791,001✔
725
                                state = STATE_ESCAPE;
726
                        else if (*i == '\t') {
10,790,985✔
727
                                fputs("        ", f);
381✔
728
                                advance_offsets(i - *ibuf, highlight, shift, 7);
381✔
729
                        } else
730
                                fputc(*i, f);
10,790,604✔
731

732
                        break;
733

734
                case STATE_ESCAPE:
16✔
735
                        assert(n_carriage_returns == 0);
16✔
736

737
                        if (eot) {
16✔
738
                                fputc('\x1B', f);
×
739
                                advance_offsets(i - *ibuf, highlight, shift, 1);
×
740
                                break;
741
                        } else if (*i == '[') { /* ANSI CSI */
16✔
742
                                state = STATE_CSI;
13✔
743
                                begin = i + 1;
13✔
744
                        } else if (*i == ']') { /* ANSI OSC */
3✔
745
                                state = STATE_OSC;
3✔
746
                                begin = i + 1;
3✔
747
                        } else {
748
                                fputc('\x1B', f);
×
749
                                fputc(*i, f);
×
750
                                advance_offsets(i - *ibuf, highlight, shift, 1);
×
751
                                state = STATE_OTHER;
×
752
                        }
753

754
                        break;
755

756
                case STATE_CSI:
41✔
757
                        assert(n_carriage_returns == 0);
41✔
758

759
                        if (eot || !strchr("01234567890;m", *i)) { /* EOT or invalid chars in sequence */
41✔
760
                                fputc('\x1B', f);
5✔
761
                                fputc('[', f);
5✔
762
                                advance_offsets(i - *ibuf, highlight, shift, 2);
5✔
763
                                state = STATE_OTHER;
5✔
764
                                i = begin-1;
5✔
765
                        } else if (*i == 'm')
36✔
766
                                state = STATE_OTHER;
8✔
767

768
                        break;
769

770
                case STATE_OSC:
24✔
771
                        assert(n_carriage_returns == 0);
24✔
772

773
                        /* There are three kinds of OSC terminators: \x07, \x1b\x5c or \x9c. We only support
774
                         * the first two, because the last one is a valid UTF-8 codepoint and hence creates
775
                         * an ambiguity (many Terminal emulators refuse to support it as well). */
776
                        if (eot || (!IN_SET(*i, '\x07', '\x1b') && !osc_char_is_valid(*i))) { /* EOT or invalid chars in sequence */
24✔
777
                                fputc('\x1B', f);
×
778
                                fputc(']', f);
×
779
                                advance_offsets(i - *ibuf, highlight, shift, 2);
×
780
                                state = STATE_OTHER;
×
781
                                i = begin-1;
×
782
                        } else if (*i == '\x07') /* Single character ST */
24✔
783
                                state = STATE_OTHER;
784
                        else if (*i == '\x1B')
23✔
785
                                state = STATE_OSC_CLOSING;
2✔
786

787
                        break;
788

789
                case STATE_OSC_CLOSING:
2✔
790
                        if (eot || *i != '\x5c') { /* EOT or incomplete two-byte ST in sequence */
2✔
791
                                fputc('\x1B', f);
×
792
                                fputc(']', f);
×
793
                                advance_offsets(i - *ibuf, highlight, shift, 2);
×
794
                                state = STATE_OTHER;
×
795
                                i = begin-1;
×
796
                        } else if (*i == '\x5c')
797
                                state = STATE_OTHER;
798

799
                        break;
800
                }
801
        }
802

803
        char *obuf;
96,968✔
804
        if (memstream_finalize(&m, &obuf, _isz) < 0)
96,968✔
805
                return NULL;
806

807
        free_and_replace(*ibuf, obuf);
96,968✔
808

809
        if (highlight) {
96,968✔
810
                highlight[0] += shift[0];
96,959✔
811
                highlight[1] += shift[1];
96,959✔
812
        }
813

814
        return *ibuf;
96,968✔
815
}
816

817
char* strextend_with_separator_internal(char **x, const char *separator, ...) {
15,695,060✔
818
        _cleanup_free_ char *buffer = NULL;
31,390,120✔
819
        size_t f, l, l_separator;
15,695,060✔
820
        bool need_separator;
15,695,060✔
821
        char *nr, *p;
15,695,060✔
822
        va_list ap;
15,695,060✔
823

824
        if (!x)
15,695,060✔
825
                x = &buffer;
14,764,466✔
826

827
        l = f = strlen_ptr(*x);
15,695,060✔
828

829
        need_separator = !isempty(*x);
15,695,060✔
830
        l_separator = strlen_ptr(separator);
15,695,060✔
831

832
        va_start(ap, separator);
15,695,060✔
833
        for (const char *t;;) {
58,975,681✔
834
                size_t n;
58,975,681✔
835

836
                t = va_arg(ap, const char *);
58,975,681✔
837
                if (!t)
58,975,681✔
838
                        break;
839
                if (t == POINTER_MAX)
43,280,621✔
840
                        continue;
3✔
841

842
                n = strlen(t);
43,280,618✔
843

844
                if (need_separator)
43,280,618✔
845
                        n += l_separator;
28,111,749✔
846

847
                if (n >= SIZE_MAX - l) {
43,280,618✔
848
                        va_end(ap);
×
849
                        return NULL;
×
850
                }
851

852
                l += n;
43,280,618✔
853
                need_separator = true;
43,280,618✔
854
        }
855
        va_end(ap);
15,695,060✔
856

857
        need_separator = !isempty(*x);
15,695,060✔
858

859
        nr = realloc(*x, GREEDY_ALLOC_ROUND_UP(l+1));
15,695,060✔
860
        if (!nr)
15,695,060✔
861
                return NULL;
862

863
        *x = nr;
15,695,060✔
864
        p = nr + f;
15,695,060✔
865

866
        va_start(ap, separator);
15,695,060✔
867
        for (;;) {
58,975,681✔
868
                const char *t;
58,975,681✔
869

870
                t = va_arg(ap, const char *);
58,975,681✔
871
                if (!t)
58,975,681✔
872
                        break;
873
                if (t == POINTER_MAX)
43,280,621✔
874
                        continue;
3✔
875

876
                if (need_separator && separator)
43,280,618✔
877
                        p = stpcpy(p, separator);
144,052✔
878

879
                p = stpcpy(p, t);
43,280,618✔
880

881
                need_separator = true;
43,280,618✔
882
        }
883
        va_end(ap);
15,695,060✔
884

885
        assert(p == nr + l);
15,695,060✔
886
        *p = 0;
15,695,060✔
887

888
        /* If no buffer to extend was passed in return the start of the buffer */
889
        if (buffer)
15,695,060✔
890
                return TAKE_PTR(buffer);
14,764,466✔
891

892
        /* Otherwise we extended the buffer: return the end */
893
        return p;
894
}
895

896
int strextendf_with_separator(char **x, const char *separator, const char *format, ...) {
12,464✔
897
        size_t m, a, l_separator;
12,464✔
898
        va_list ap;
12,464✔
899
        int l;
12,464✔
900

901
        /* Appends a formatted string to the specified string. Don't use this in inner loops, since then
902
         * we'll spend a tonload of time in determining the length of the string passed in, over and over
903
         * again. */
904

905
        assert(x);
12,464✔
906
        assert(format);
12,464✔
907

908
        l_separator = isempty(*x) ? 0 : strlen_ptr(separator);
21,591✔
909

910
        /* Let's try to use the allocated buffer, if there's room at the end still. Otherwise let's extend by 64 chars. */
911
        if (*x) {
12,464✔
912
                m = strlen(*x);
9,127✔
913
                a = MALLOC_SIZEOF_SAFE(*x);
9,127✔
914
                assert(a >= m + 1);
9,127✔
915
        } else
916
                m = a = 0;
917

918
        if (a - m < 17 + l_separator) { /* if there's less than 16 chars space, then enlarge the buffer first */
12,464✔
919
                char *n;
8,596✔
920

921
                if (_unlikely_(l_separator > SIZE_MAX - 64)) /* overflow check #1 */
8,596✔
922
                        return -ENOMEM;
12,464✔
923
                if (_unlikely_(m > SIZE_MAX - 64 - l_separator)) /* overflow check #2 */
8,596✔
924
                        return -ENOMEM;
925

926
                n = realloc(*x, m + 64 + l_separator);
8,596✔
927
                if (!n)
8,596✔
928
                        return -ENOMEM;
929

930
                *x = n;
8,596✔
931
                a = MALLOC_SIZEOF_SAFE(*x);
8,596✔
932
        }
933

934
        /* Now, let's try to format the string into it */
935
        memcpy_safe(*x + m, separator, l_separator);
12,464✔
936
        va_start(ap, format);
12,464✔
937
        l = vsnprintf(*x + m + l_separator, a - m - l_separator, format, ap);
12,464✔
938
        va_end(ap);
12,464✔
939

940
        assert(l >= 0);
12,464✔
941

942
        if ((size_t) l < a - m - l_separator) {
12,464✔
943
                char *n;
12,162✔
944

945
                /* Nice! This worked. We are done. But first, let's return the extra space we don't
946
                 * need. This should be a cheap operation, since we only lower the allocation size here,
947
                 * never increase. */
948
                n = realloc(*x, m + (size_t) l + l_separator + 1);
12,162✔
949
                if (n)
12,162✔
950
                        *x = n;
12,162✔
951
        } else {
952
                char *n;
302✔
953

954
                /* Wasn't enough. Then let's allocate exactly what we need. */
955

956
                if (_unlikely_((size_t) l > SIZE_MAX - (l_separator + 1))) /* overflow check #1 */
302✔
957
                        goto oom;
×
958
                if (_unlikely_(m > SIZE_MAX - ((size_t) l + l_separator + 1))) /* overflow check #2 */
302✔
959
                        goto oom;
×
960

961
                a = m + (size_t) l + l_separator + 1;
302✔
962
                n = realloc(*x, a);
302✔
963
                if (!n)
302✔
964
                        goto oom;
×
965
                *x = n;
302✔
966

967
                va_start(ap, format);
302✔
968
                l = vsnprintf(*x + m + l_separator, a - m - l_separator, format, ap);
302✔
969
                va_end(ap);
302✔
970

971
                assert((size_t) l < a - m - l_separator);
302✔
972
        }
973

974
        return 0;
975

976
oom:
×
977
        /* truncate the bytes added after memcpy_safe() again */
978
        (*x)[m] = 0;
×
979
        return -ENOMEM;
×
980
}
981

982
char* strrep(const char *s, unsigned n) {
11✔
983
        char *r, *p;
11✔
984
        size_t l;
11✔
985

986
        assert(s);
11✔
987

988
        l = strlen(s);
11✔
989
        p = r = malloc(l * n + 1);
11✔
990
        if (!r)
11✔
991
                return NULL;
992

993
        for (unsigned i = 0; i < n; i++)
40,337✔
994
                p = stpcpy(p, s);
40,326✔
995

996
        *p = 0;
11✔
997
        return r;
11✔
998
}
999

1000
int split_pair(const char *s, const char *sep, char **ret_first, char **ret_second) {
1,572✔
1001
        assert(s);
1,572✔
1002
        assert(!isempty(sep));
1,572✔
1003
        assert(ret_first);
1,572✔
1004
        assert(ret_second);
1,572✔
1005

1006
        const char *x = strstr(s, sep);
1,572✔
1007
        if (!x)
1,572✔
1008
                return -EINVAL;
1,572✔
1009

1010
        _cleanup_free_ char *a = strndup(s, x - s);
258✔
1011
        if (!a)
258✔
1012
                return -ENOMEM;
1013

1014
        _cleanup_free_ char *b = strdup(x + strlen(sep));
258✔
1015
        if (!b)
258✔
1016
                return -ENOMEM;
1017

1018
        *ret_first = TAKE_PTR(a);
258✔
1019
        *ret_second = TAKE_PTR(b);
258✔
1020
        return 0;
258✔
1021
}
1022

1023
int free_and_strdup(char **p, const char *s) {
1,548,446✔
1024
        char *t;
1,548,446✔
1025

1026
        assert(p);
1,548,446✔
1027

1028
        /* Replaces a string pointer with a strdup()ed new string,
1029
         * possibly freeing the old one. */
1030

1031
        if (streq_ptr(*p, s))
1,548,446✔
1032
                return 0;
1,548,446✔
1033

1034
        if (s) {
1,284,616✔
1035
                t = strdup(s);
1,278,487✔
1036
                if (!t)
1,278,487✔
1037
                        return -ENOMEM;
1038
        } else
1039
                t = NULL;
1040

1041
        free_and_replace(*p, t);
1,284,616✔
1042

1043
        return 1;
1,284,616✔
1044
}
1045

1046
int free_and_strdup_warn(char **p, const char *s) {
45,437✔
1047
        int r;
45,437✔
1048

1049
        r = free_and_strdup(p, s);
45,437✔
1050
        if (r < 0)
45,437✔
1051
                return log_oom();
×
1052
        return r;
1053
}
1054

1055
int free_and_strndup(char **p, const char *s, size_t l) {
66,108✔
1056
        char *t;
66,108✔
1057

1058
        assert(p);
66,108✔
1059
        assert(s || l == 0);
66,108✔
1060

1061
        /* Replaces a string pointer with a strndup()ed new string,
1062
         * freeing the old one. */
1063

1064
        if (!*p && !s)
66,108✔
1065
                return 0;
1066

1067
        if (*p && s && strneq(*p, s, l) && (l > strlen(*p) || (*p)[l] == '\0'))
66,108✔
1068
                return 0;
1069

1070
        if (s) {
31,295✔
1071
                t = strndup(s, l);
31,354✔
1072
                if (!t)
31,354✔
1073
                        return -ENOMEM;
1074
        } else
1075
                t = NULL;
1076

1077
        free_and_replace(*p, t);
31,355✔
1078
        return 1;
31,355✔
1079
}
1080

1081
int strdup_to_full(char **ret, const char *src) {
661,542✔
1082
        if (!src) {
661,542✔
1083
                if (ret)
197,000✔
1084
                        *ret = NULL;
196,999✔
1085

1086
                return 0;
197,000✔
1087
        } else {
1088
                if (ret) {
464,542✔
1089
                        char *t = strdup(src);
464,540✔
1090
                        if (!t)
464,540✔
1091
                                return -ENOMEM;
1092
                        *ret = t;
464,540✔
1093
                }
1094

1095
                return 1;
464,542✔
1096
        }
1097
};
1098

1099
bool string_is_safe(const char *p) {
142,326✔
1100
        if (!p)
142,326✔
1101
                return false;
1102

1103
        /* Checks if the specified string contains no quotes or control characters */
1104

1105
        for (const char *t = p; *t; t++) {
1,581,369✔
1106
                if (*t > 0 && *t < ' ') /* no control characters */
1,439,056✔
1107
                        return false;
1108

1109
                if (strchr(QUOTES "\\\x7f", *t))
1,439,048✔
1110
                        return false;
1111
        }
1112

1113
        return true;
1114
}
1115

1116
bool string_is_safe_ascii(const char *p) {
×
1117
        return ascii_is_valid(p) && string_is_safe(p);
×
1118
}
1119

1120
char* str_realloc(char *p) {
28,136✔
1121
        /* Reallocate *p to actual size. Ignore failure, and return the original string on error. */
1122

1123
        if (!p)
28,136✔
1124
                return NULL;
1125

1126
        return realloc(p, strlen(p) + 1) ?: p;
28,136✔
1127
}
1128

1129
char* string_erase(char *x) {
110✔
1130
        if (!x)
110✔
1131
                return NULL;
1132

1133
        /* A delicious drop of snake-oil! To be called on memory where we stored passphrases or so, after we
1134
         * used them. */
1135
        explicit_bzero_safe(x, strlen(x));
110✔
1136
        return x;
110✔
1137
}
1138

1139
int string_truncate_lines(const char *s, size_t n_lines, char **ret) {
210✔
1140
        const char *p = s, *e = s;
210✔
1141
        bool truncation_applied = false;
210✔
1142
        char *copy;
210✔
1143
        size_t n = 0;
210✔
1144

1145
        assert(s);
210✔
1146

1147
        /* Truncate after the specified number of lines. Returns > 0 if a truncation was applied or == 0 if
1148
         * there were fewer lines in the string anyway. Trailing newlines on input are ignored, and not
1149
         * generated either. */
1150

1151
        for (;;) {
436✔
1152
                size_t k;
323✔
1153

1154
                k = strcspn(p, "\n");
323✔
1155

1156
                if (p[k] == 0) {
323✔
1157
                        if (k == 0) /* final empty line */
178✔
1158
                                break;
1159

1160
                        if (n >= n_lines) /* above threshold */
153✔
1161
                                break;
1162

1163
                        e = p + k; /* last line to include */
138✔
1164
                        break;
138✔
1165
                }
1166

1167
                assert(p[k] == '\n');
145✔
1168

1169
                if (n >= n_lines)
145✔
1170
                        break;
1171

1172
                if (k > 0)
113✔
1173
                        e = p + k;
90✔
1174

1175
                p += k + 1;
113✔
1176
                n++;
113✔
1177
        }
1178

1179
        /* e points after the last character we want to keep */
1180
        if (isempty(e))
210✔
1181
                copy = strdup(s);
142✔
1182
        else {
1183
                if (!in_charset(e, "\n")) /* We only consider things truncated if we remove something that
68✔
1184
                                           * isn't a new-line or a series of them */
1185
                        truncation_applied = true;
40✔
1186

1187
                copy = strndup(s, e - s);
68✔
1188
        }
1189
        if (!copy)
210✔
1190
                return -ENOMEM;
1191

1192
        *ret = copy;
210✔
1193
        return truncation_applied;
210✔
1194
}
1195

1196
int string_extract_line(const char *s, size_t i, char **ret) {
134,396✔
1197
        const char *p = s;
134,396✔
1198
        size_t c = 0;
134,396✔
1199

1200
        /* Extract the i'nth line from the specified string. Returns > 0 if there are more lines after that,
1201
         * and == 0 if we are looking at the last line or already beyond the last line. As special
1202
         * optimization, if the first line is requested and the string only consists of one line we return
1203
         * NULL, indicating the input string should be used as is, and avoid a memory allocation for a very
1204
         * common case. */
1205

1206
        for (;;) {
319,698✔
1207
                const char *q;
227,047✔
1208

1209
                q = strchr(p, '\n');
227,047✔
1210
                if (i == c) {
227,047✔
1211
                        /* The line we are looking for! */
1212

1213
                        if (q) {
131,459✔
1214
                                char *m;
3,072✔
1215

1216
                                m = strndup(p, q - p);
3,072✔
1217
                                if (!m)
3,072✔
1218
                                        return -ENOMEM;
1219

1220
                                *ret = m;
3,072✔
1221
                                return !isempty(q + 1); /* More coming? */
6,144✔
1222
                        } else
1223
                                /* Tell the caller to use the input string if equal */
1224
                                return strdup_to(ret, p != s ? p : NULL);
255,522✔
1225
                }
1226

1227
                if (!q)
95,588✔
1228
                        /* No more lines, return empty line */
1229
                        return strdup_to(ret, "");
2,937✔
1230

1231
                p = q + 1;
92,651✔
1232
                c++;
92,651✔
1233
        }
1234
}
1235

1236
int string_contains_word_strv(const char *string, const char *separators, char * const *words, const char **ret_word) {
71✔
1237
        /* In the default mode with no separators specified, we split on whitespace and coalesce separators. */
1238
        const ExtractFlags flags = separators ? EXTRACT_DONT_COALESCE_SEPARATORS : 0;
71✔
1239
        const char *found = NULL;
71✔
1240
        int r;
196✔
1241

1242
        for (;;) {
321✔
1243
                _cleanup_free_ char *w = NULL;
125✔
1244

1245
                r = extract_first_word(&string, &w, separators, flags);
196✔
1246
                if (r < 0)
196✔
1247
                        return r;
×
1248
                if (r == 0)
196✔
1249
                        break;
1250

1251
                found = strv_find(words, w);
146✔
1252
                if (found)
146✔
1253
                        break;
1254
        }
1255

1256
        if (ret_word)
71✔
1257
                *ret_word = found;
7✔
1258
        return !!found;
71✔
1259
}
1260

1261
bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok) {
8,134✔
1262
        if (!s1 && !s2)
8,134✔
1263
                return true;
1264
        if (!s1 || !s2)
8,133✔
1265
                return false;
1266

1267
        if (!ok)
8,131✔
1268
                ok = WHITESPACE;
13✔
1269

1270
        for (; *s1 && *s2; s1++, s2++)
15,269✔
1271
                if (*s1 != *s2)
9,308✔
1272
                        break;
1273

1274
        return in_charset(s1, ok) && in_charset(s2, ok);
10,307✔
1275
}
1276

1277
char* string_replace_char(char *str, char old_char, char new_char) {
393,056✔
1278
        assert(str);
393,056✔
1279
        assert(old_char != '\0');
393,056✔
1280
        assert(new_char != '\0');
393,056✔
1281
        assert(old_char != new_char);
393,056✔
1282

1283
        for (char *p = strchr(str, old_char); p; p = strchr(p + 1, old_char))
397,475✔
1284
                *p = new_char;
4,419✔
1285

1286
        return str;
393,056✔
1287
}
1288

1289
int make_cstring(const char *s, size_t n, MakeCStringMode mode, char **ret) {
1,499✔
1290
        char *b;
1,499✔
1291

1292
        assert(s || n == 0);
1,499✔
1293
        assert(mode >= 0);
1,499✔
1294
        assert(mode < _MAKE_CSTRING_MODE_MAX);
1,499✔
1295

1296
        /* Converts a sized character buffer into a NUL-terminated NUL string, refusing if there are embedded
1297
         * NUL bytes. Whether to expect a trailing NUL byte can be specified via 'mode' */
1298

1299
        if (n == 0) {
1,499✔
1300
                if (mode == MAKE_CSTRING_REQUIRE_TRAILING_NUL)
20✔
1301
                        return -EINVAL;
1302

1303
                if (!ret)
19✔
1304
                        return 0;
1305

1306
                b = new0(char, 1);
19✔
1307
        } else {
1308
                const char *nul;
1,479✔
1309

1310
                nul = memchr(s, 0, n);
1,479✔
1311
                if (nul) {
1,479✔
1312
                        if (nul < s + n - 1 || /* embedded NUL? */
19✔
1313
                            mode == MAKE_CSTRING_REFUSE_TRAILING_NUL)
1314
                                return -EINVAL;
1315

1316
                        n--;
1317
                } else if (mode == MAKE_CSTRING_REQUIRE_TRAILING_NUL)
1,460✔
1318
                        return -EINVAL;
1319

1320
                if (!ret)
1,464✔
1321
                        return 0;
1322

1323
                b = memdup_suffix0(s, n);
1,464✔
1324
        }
1325
        if (!b)
1,483✔
1326
                return -ENOMEM;
1327

1328
        *ret = b;
1,483✔
1329
        return 0;
1,483✔
1330
}
1331

1332
size_t strspn_from_end(const char *str, const char *accept) {
362,123✔
1333
        size_t n = 0;
362,123✔
1334

1335
        if (isempty(str))
362,123✔
1336
                return 0;
1337

1338
        if (isempty(accept))
362,120✔
1339
                return 0;
1340

1341
        for (const char *p = str + strlen(str); p > str && strchr(accept, p[-1]); p--)
721,964✔
1342
                n++;
359,845✔
1343

1344
        return n;
1345
}
1346

1347
char* strdupspn(const char *a, const char *accept) {
×
1348
        if (isempty(a) || isempty(accept))
×
1349
                return strdup("");
×
1350

1351
        return strndup(a, strspn(a, accept));
×
1352
}
1353

1354
char* strdupcspn(const char *a, const char *reject) {
43,710✔
1355
        if (isempty(a))
43,710✔
1356
                return strdup("");
×
1357
        if (isempty(reject))
43,710✔
1358
                return strdup(a);
×
1359

1360
        return strndup(a, strcspn(a, reject));
43,710✔
1361
}
1362

1363
char* find_line_startswith(const char *haystack, const char *needle) {
606✔
1364
        char *p;
606✔
1365

1366
        assert(haystack);
606✔
1367
        assert(needle);
606✔
1368

1369
        /* Finds the first line in 'haystack' that starts with the specified string. Returns a pointer to the
1370
         * first character after it */
1371

1372
        p = strstr(haystack, needle);
606✔
1373
        if (!p)
606✔
1374
                return NULL;
1375

1376
        if (p > haystack)
234✔
1377
                while (p[-1] != '\n') {
18✔
1378
                        p = strstr(p + 1, needle);
9✔
1379
                        if (!p)
9✔
1380
                                return NULL;
1381
                }
1382

1383
        return p + strlen(needle);
233✔
1384
}
1385

1386
bool version_is_valid(const char *s) {
3,975✔
1387
        if (isempty(s))
3,975✔
1388
                return false;
1389

1390
        if (!filename_part_is_valid(s))
3,973✔
1391
                return false;
1392

1393
        /* This is a superset of the characters used by semver. We additionally allow "," and "_". */
1394
        if (!in_charset(s, ALPHANUMERICAL ".,_-+"))
3,973✔
1395
                return false;
×
1396

1397
        return true;
1398
}
1399

1400
bool version_is_valid_versionspec(const char *s) {
62✔
1401
        if (!filename_part_is_valid(s))
62✔
1402
                return false;
1403

1404
        if (!in_charset(s, ALPHANUMERICAL "-.~^"))
62✔
1405
                return false;
×
1406

1407
        return true;
1408
}
1409

1410
ssize_t strlevenshtein(const char *x, const char *y) {
90✔
1411
        _cleanup_free_ size_t *t0 = NULL, *t1 = NULL, *t2 = NULL;
180✔
1412
        size_t xl, yl;
90✔
1413

1414
        /* This is inspired from the Linux kernel's Levenshtein implementation */
1415

1416
        if (streq_ptr(x, y))
90✔
1417
                return 0;
1418

1419
        xl = strlen_ptr(x);
86✔
1420
        if (xl > SSIZE_MAX)
85✔
1421
                return -E2BIG;
1422

1423
        yl = strlen_ptr(y);
86✔
1424
        if (yl > SSIZE_MAX)
85✔
1425
                return -E2BIG;
1426

1427
        if (isempty(x))
86✔
1428
                return yl;
3✔
1429
        if (isempty(y))
83✔
1430
                return xl;
1✔
1431

1432
        t0 = new0(size_t, yl + 1);
82✔
1433
        if (!t0)
82✔
1434
                return -ENOMEM;
1435
        t1 = new0(size_t, yl + 1);
82✔
1436
        if (!t1)
82✔
1437
                return -ENOMEM;
1438
        t2 = new0(size_t, yl + 1);
82✔
1439
        if (!t2)
82✔
1440
                return -ENOMEM;
1441

1442
        for (size_t i = 0; i <= yl; i++)
905✔
1443
                t1[i] = i;
823✔
1444

1445
        for (size_t i = 0; i < xl; i++) {
453✔
1446
                t2[0] = i + 1;
371✔
1447

1448
                for (size_t j = 0; j < yl; j++) {
4,228✔
1449
                        /* Substitution */
1450
                        t2[j+1] = t1[j] + (x[i] != y[j]);
3,857✔
1451

1452
                        /* Swap */
1453
                        if (i > 0 && j > 0 && x[i-1] == y[j] && x[i] == y[j-1] && t2[j+1] > t0[j-1] + 1)
3,857✔
1454
                                t2[j+1] = t0[j-1] + 1;
10✔
1455

1456
                        /* Deletion */
1457
                        if (t2[j+1] > t1[j+1] + 1)
3,857✔
1458
                                t2[j+1] = t1[j+1] + 1;
141✔
1459

1460
                        /* Insertion */
1461
                        if (t2[j+1] > t2[j] + 1)
3,857✔
1462
                                t2[j+1] = t2[j] + 1;
638✔
1463
                }
1464

1465
                size_t *dummy = t0;
1466
                t0 = t1;
1467
                t1 = t2;
1468
                t2 = dummy;
1469
        }
1470

1471
        return t1[yl];
82✔
1472
}
1473

1474
char* strrstr(const char *haystack, const char *needle) {
7,180✔
1475
        /* Like strstr() but returns the last rather than the first occurrence of "needle" in "haystack". */
1476

1477
        if (!haystack || !needle)
7,180✔
1478
                return NULL;
1479

1480
        /* Special case: for the empty string we return the very last possible occurrence, i.e. *after* the
1481
         * last char, not before. */
1482
        if (*needle == 0)
7,177✔
1483
                return strchr(haystack, 0);
2✔
1484

1485
        for (const char *p = strstr(haystack, needle), *q; p; p = q) {
7,185✔
1486
                q = strstr(p + 1, needle);
21✔
1487
                if (!q)
21✔
1488
                        return (char *) p;
1489
        }
1490
        return NULL;
1491
}
1492

1493
size_t str_common_prefix(const char *a, const char *b) {
16✔
1494
        assert(a);
16✔
1495
        assert(b);
16✔
1496

1497
        /* Returns the length of the common prefix of the two specified strings, or SIZE_MAX in case the
1498
         * strings are fully identical. */
1499

1500
        for (size_t n = 0;; n++) {
39✔
1501
                char c = a[n];
55✔
1502
                if (c != b[n])
55✔
1503
                        return n;
1504
                if (c == 0)
42✔
1505
                        return SIZE_MAX;
1506
        }
1507
}
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