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

systemd / systemd / 14554080340

19 Apr 2025 11:46AM UTC coverage: 72.101% (-0.03%) from 72.13%
14554080340

push

github

web-flow
Add two new paragraphs to coding style about header files (#37188)

296880 of 411754 relevant lines covered (72.1%)

687547.52 hits per line

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

93.31
/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,041,024✔
28
        assert(s);
3,041,024✔
29
        assert(word);
3,041,024✔
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,041,024✔
35
                return (char*) s;
36

37
        const char *p = startswith(s, word);
3,041,023✔
38
        if (!p)
3,041,023✔
39
                return NULL;
40
        if (*p == '\0')
102,592✔
41
                return (char*) p;
42

43
        const char *nw = skip_leading_chars(p, WHITESPACE);
102,591✔
44
        if (p == nw)
102,591✔
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,341✔
65
        assert(x);
27,341✔
66
        assert(s || l == 0);
27,341✔
67

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

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

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

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

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

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

88
char* strstrip(char *s) {
5,146,380✔
89
        if (!s)
5,146,380✔
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,146,380✔
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,164,779✔
121
        char *c = s;
6,164,779✔
122

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

125
        if (!s)
6,164,779✔
126
                return NULL;
127

128
        if (!bad)
6,164,779✔
129
                bad = WHITESPACE;
63,781✔
130

131
        for (char *p = s; *p; p++)
249,193,284✔
132
                if (!strchr(bad, *p))
243,028,505✔
133
                        c = p + 1;
233,029,879✔
134

135
        *c = 0;
6,164,779✔
136

137
        return s;
6,164,779✔
138
}
139

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

143
        assert(s);
2,616,438✔
144

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

152
char ascii_tolower(char x) {
3,855,206✔
153

154
        if (x >= 'A' && x <= 'Z')
3,855,206✔
155
                return x - 'A' + 'a';
165,829✔
156

157
        return x;
158
}
159

160
char ascii_toupper(char x) {
286,279✔
161

162
        if (x >= 'a' && x <= 'z')
286,279✔
163
                return x - 'a' + 'A';
177,967✔
164

165
        return x;
166
}
167

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

171
        for (char *p = t; *p; p++)
142,353✔
172
                *p = ascii_tolower(*p);
120,067✔
173

174
        return t;
22,286✔
175
}
176

177
char* ascii_strupper(char *t) {
20,739✔
178
        assert(t);
20,739✔
179

180
        for (char *p = t; *p; p++)
306,864✔
181
                *p = ascii_toupper(*p);
286,125✔
182

183
        return t;
20,739✔
184
}
185

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

190
        for (size_t i = 0; i < n; i++)
2,164,356✔
191
                t[i] = ascii_tolower(t[i]);
1,497,283✔
192

193
        return t;
194
}
195

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

198
        for (; n > 0; a++, b++, n--) {
1,575,135✔
199
                int x, y;
1,114,846✔
200

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

204
                if (x != y)
1,114,846✔
205
                        return x - y;
118,978✔
206
        }
207

208
        return 0;
209
}
210

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

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

218
        return CMP(n, m);
418,971✔
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) {
180,450✔
231
        assert(p);
180,450✔
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,356,137✔
240
                if (ok && strchr(ok, *t))
1,175,703✔
241
                        continue;
8✔
242

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

247
        return false;
248
}
249

250
static int write_ellipsis(char *buf, bool unicode) {
4,238✔
251
        const char *s = glyph_full(GLYPH_ELLIPSIS, unicode);
4,238✔
252
        assert(strlen(s) == 3);
4,238✔
253
        memcpy(buf, s, 3);
4,238✔
254
        return 3;
4,238✔
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,611✔
289
        const char *t = s;
7,611✔
290

291
        while ((t = memchr(s, 0x1B, len - (t - s))))
7,611✔
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,111✔
314
        size_t x, need_space, suffix_len;
4,111✔
315
        char *t;
4,111✔
316

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

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

324
        /* Special case short ellipsations */
325
        switch (new_length) {
2,495✔
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,430✔
346

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

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

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

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

361
        return t;
2,430✔
362
}
363

364
char* ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
7,782✔
365
        size_t x, k, len, len2;
7,782✔
366
        const char *i, *j;
7,782✔
367
        int r;
7,782✔
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,782✔
382
        assert(percent <= 100);
7,782✔
383

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

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

390
        bool has_ansi_seq = string_has_ansi_sequence(s, old_length);
7,611✔
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,611✔
394
                return ascii_ellipsize_mem(s, old_length, new_length, percent);
4,111✔
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) {
44,053✔
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;
44,053✔
513

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

518
        for (;;) {
979,067✔
519
                char four[4];
511,560✔
520
                int w;
511,560✔
521

522
                if (*s == 0) /* terminating NUL detected? then we are done! */
511,560✔
523
                        goto done;
44,017✔
524

525
                w = cescape_char(*s, four);
467,543✔
526
                if (i + w + 1 > len) /* This character doesn't fit into the buffer anymore? In that case let's
467,543✔
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);
467,507✔
532
                i += w;
467,507✔
533

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

538
                s++;
467,507✔
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:
4✔
567
        buf[i] = '\0';
44,053✔
568
        return buf;
44,053✔
569
}
570

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

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

577
        if (strnlen(s, l+1) > l)
25,786✔
578
                s[l] = 0;
18✔
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,890✔
606
        size_t l, old_len, new_len;
3,890✔
607
        char *t, *ret = NULL;
3,890✔
608
        const char *f;
3,890✔
609

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

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

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

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

623
        f = text;
3,889✔
624
        t = ret;
3,889✔
625
        while (*f) {
110,805✔
626
                size_t d, nl;
106,916✔
627

628
                if (!startswith(f, old_string)) {
106,916✔
629
                        *(t++) = *(f++);
104,325✔
630
                        continue;
104,325✔
631
                }
632

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

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

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

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

646
        *t = 0;
3,889✔
647
        return ret;
3,889✔
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]) {
97,246✔
668
        const char *begin = NULL;
97,246✔
669
        enum {
97,246✔
670
                STATE_OTHER,
671
                STATE_ESCAPE,
672
                STATE_CSI,
673
                STATE_OSC,
674
                STATE_OSC_CLOSING,
675
        } state = STATE_OTHER;
97,246✔
676
        _cleanup_(memstream_done) MemStream m = {};
97,246✔
677
        size_t isz, shift[2] = {}, n_carriage_returns = 0;
97,246✔
678
        FILE *f;
97,246✔
679

680
        assert(ibuf);
97,246✔
681
        assert(*ibuf);
97,246✔
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);
97,246✔
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);
97,246✔
702
        if (!f)
97,246✔
703
                return NULL;
704

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

707
                bool eot = i >= *ibuf + isz;
10,885,644✔
708

709
                switch (state) {
10,885,644✔
710

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

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

724
                        if (*i == '\x1B')
10,788,308✔
725
                                state = STATE_ESCAPE;
726
                        else if (*i == '\t') {
10,788,292✔
727
                                fputs("        ", f);
381✔
728
                                advance_offsets(i - *ibuf, highlight, shift, 7);
381✔
729
                        } else
730
                                fputc(*i, f);
10,787,911✔
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;
97,246✔
804
        if (memstream_finalize(&m, &obuf, _isz) < 0)
97,246✔
805
                return NULL;
806

807
        free_and_replace(*ibuf, obuf);
97,246✔
808

809
        if (highlight) {
97,246✔
810
                highlight[0] += shift[0];
97,237✔
811
                highlight[1] += shift[1];
97,237✔
812
        }
813

814
        return *ibuf;
97,246✔
815
}
816

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

824
        if (!x)
15,430,475✔
825
                x = &buffer;
14,512,011✔
826

827
        l = f = strlen_ptr(*x);
15,430,475✔
828

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

832
        va_start(ap, separator);
15,430,475✔
833
        for (const char *t;;) {
57,958,291✔
834
                size_t n;
57,958,291✔
835

836
                t = va_arg(ap, const char *);
57,958,291✔
837
                if (!t)
57,958,291✔
838
                        break;
839
                if (t == POINTER_MAX)
42,527,816✔
840
                        continue;
3✔
841

842
                n = strlen(t);
42,527,813✔
843

844
                if (need_separator)
42,527,813✔
845
                        n += l_separator;
27,616,565✔
846

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

852
                l += n;
42,527,813✔
853
                need_separator = true;
42,527,813✔
854
        }
855
        va_end(ap);
15,430,475✔
856

857
        need_separator = !isempty(*x);
15,430,475✔
858

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

863
        *x = nr;
15,430,475✔
864
        p = nr + f;
15,430,475✔
865

866
        va_start(ap, separator);
15,430,475✔
867
        for (;;) {
57,958,291✔
868
                const char *t;
57,958,291✔
869

870
                t = va_arg(ap, const char *);
57,958,291✔
871
                if (!t)
57,958,291✔
872
                        break;
873
                if (t == POINTER_MAX)
42,527,816✔
874
                        continue;
3✔
875

876
                if (need_separator && separator)
42,527,813✔
877
                        p = stpcpy(p, separator);
144,157✔
878

879
                p = stpcpy(p, t);
42,527,813✔
880

881
                need_separator = true;
42,527,813✔
882
        }
883
        va_end(ap);
15,430,475✔
884

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

888
        /* If no buffer to extend was passed in return the start of the buffer */
889
        if (buffer)
15,430,475✔
890
                return TAKE_PTR(buffer);
14,512,011✔
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,274✔
897
        size_t m, a, l_separator;
12,274✔
898
        va_list ap;
12,274✔
899
        int l;
12,274✔
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,274✔
906
        assert(format);
12,274✔
907

908
        l_separator = isempty(*x) ? 0 : strlen_ptr(separator);
21,264✔
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,274✔
912
                m = strlen(*x);
8,990✔
913
                a = MALLOC_SIZEOF_SAFE(*x);
8,990✔
914
                assert(a >= m + 1);
8,990✔
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,274✔
919
                char *n;
8,451✔
920

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

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

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

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

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

942
        if ((size_t) l < a - m - l_separator) {
12,274✔
943
                char *n;
12,030✔
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,030✔
949
                if (n)
12,030✔
950
                        *x = n;
12,030✔
951
        } else {
952
                char *n;
244✔
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 */
244✔
957
                        goto oom;
×
958
                if (_unlikely_(m > SIZE_MAX - ((size_t) l + l_separator + 1))) /* overflow check #2 */
244✔
959
                        goto oom;
×
960

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

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

971
                assert((size_t) l < a - m - l_separator);
244✔
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,439,148✔
1024
        char *t;
1,439,148✔
1025

1026
        assert(p);
1,439,148✔
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,439,148✔
1032
                return 0;
1,439,148✔
1033

1034
        if (s) {
1,184,383✔
1035
                t = strdup(s);
1,178,352✔
1036
                if (!t)
1,178,352✔
1037
                        return -ENOMEM;
1038
        } else
1039
                t = NULL;
1040

1041
        free_and_replace(*p, t);
1,184,383✔
1042

1043
        return 1;
1,184,383✔
1044
}
1045

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

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

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

1058
        assert(p);
65,569✔
1059
        assert(s || l == 0);
65,569✔
1060

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

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

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

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

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

1081
int strdup_to_full(char **ret, const char *src) {
634,244✔
1082
        if (!src) {
634,244✔
1083
                if (ret)
196,953✔
1084
                        *ret = NULL;
196,952✔
1085

1086
                return 0;
196,953✔
1087
        } else {
1088
                if (ret) {
437,291✔
1089
                        char *t = strdup(src);
437,289✔
1090
                        if (!t)
437,289✔
1091
                                return -ENOMEM;
1092
                        *ret = t;
437,289✔
1093
                }
1094

1095
                return 1;
437,291✔
1096
        }
1097
};
1098

1099
bool string_is_safe(const char *p) {
138,787✔
1100
        if (!p)
138,787✔
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,529,448✔
1106
                if (*t > 0 && *t < ' ') /* no control characters */
1,390,674✔
1107
                        return false;
1108

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

1113
        return true;
1114
}
1115

1116
char* string_erase(char *x) {
110✔
1117
        if (!x)
110✔
1118
                return NULL;
1119

1120
        /* A delicious drop of snake-oil! To be called on memory where we stored passphrases or so, after we
1121
         * used them. */
1122
        explicit_bzero_safe(x, strlen(x));
110✔
1123
        return x;
110✔
1124
}
1125

1126
int string_truncate_lines(const char *s, size_t n_lines, char **ret) {
210✔
1127
        const char *p = s, *e = s;
210✔
1128
        bool truncation_applied = false;
210✔
1129
        char *copy;
210✔
1130
        size_t n = 0;
210✔
1131

1132
        assert(s);
210✔
1133

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

1138
        for (;;) {
436✔
1139
                size_t k;
323✔
1140

1141
                k = strcspn(p, "\n");
323✔
1142

1143
                if (p[k] == 0) {
323✔
1144
                        if (k == 0) /* final empty line */
178✔
1145
                                break;
1146

1147
                        if (n >= n_lines) /* above threshold */
153✔
1148
                                break;
1149

1150
                        e = p + k; /* last line to include */
138✔
1151
                        break;
138✔
1152
                }
1153

1154
                assert(p[k] == '\n');
145✔
1155

1156
                if (n >= n_lines)
145✔
1157
                        break;
1158

1159
                if (k > 0)
113✔
1160
                        e = p + k;
90✔
1161

1162
                p += k + 1;
113✔
1163
                n++;
113✔
1164
        }
1165

1166
        /* e points after the last character we want to keep */
1167
        if (isempty(e))
210✔
1168
                copy = strdup(s);
142✔
1169
        else {
1170
                if (!in_charset(e, "\n")) /* We only consider things truncated if we remove something that
68✔
1171
                                           * isn't a new-line or a series of them */
1172
                        truncation_applied = true;
40✔
1173

1174
                copy = strndup(s, e - s);
68✔
1175
        }
1176
        if (!copy)
210✔
1177
                return -ENOMEM;
1178

1179
        *ret = copy;
210✔
1180
        return truncation_applied;
210✔
1181
}
1182

1183
int string_extract_line(const char *s, size_t i, char **ret) {
134,731✔
1184
        const char *p = s;
134,731✔
1185
        size_t c = 0;
134,731✔
1186

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

1193
        for (;;) {
319,809✔
1194
                const char *q;
227,270✔
1195

1196
                q = strchr(p, '\n');
227,270✔
1197
                if (i == c) {
227,270✔
1198
                        /* The line we are looking for! */
1199

1200
                        if (q) {
131,827✔
1201
                                char *m;
3,039✔
1202

1203
                                m = strndup(p, q - p);
3,039✔
1204
                                if (!m)
3,039✔
1205
                                        return -ENOMEM;
1206

1207
                                *ret = m;
3,039✔
1208
                                return !isempty(q + 1); /* More coming? */
6,078✔
1209
                        } else
1210
                                /* Tell the caller to use the input string if equal */
1211
                                return strdup_to(ret, p != s ? p : NULL);
256,314✔
1212
                }
1213

1214
                if (!q)
95,443✔
1215
                        /* No more lines, return empty line */
1216
                        return strdup_to(ret, "");
2,904✔
1217

1218
                p = q + 1;
92,539✔
1219
                c++;
92,539✔
1220
        }
1221
}
1222

1223
int string_contains_word_strv(const char *string, const char *separators, char * const *words, const char **ret_word) {
71✔
1224
        /* In the default mode with no separators specified, we split on whitespace and coalesce separators. */
1225
        const ExtractFlags flags = separators ? EXTRACT_DONT_COALESCE_SEPARATORS : 0;
71✔
1226
        const char *found = NULL;
71✔
1227
        int r;
196✔
1228

1229
        for (;;) {
321✔
1230
                _cleanup_free_ char *w = NULL;
125✔
1231

1232
                r = extract_first_word(&string, &w, separators, flags);
196✔
1233
                if (r < 0)
196✔
1234
                        return r;
×
1235
                if (r == 0)
196✔
1236
                        break;
1237

1238
                found = strv_find(words, w);
146✔
1239
                if (found)
146✔
1240
                        break;
1241
        }
1242

1243
        if (ret_word)
71✔
1244
                *ret_word = found;
7✔
1245
        return !!found;
71✔
1246
}
1247

1248
bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok) {
8,076✔
1249
        if (!s1 && !s2)
8,076✔
1250
                return true;
1251
        if (!s1 || !s2)
8,075✔
1252
                return false;
1253

1254
        if (!ok)
8,073✔
1255
                ok = WHITESPACE;
13✔
1256

1257
        for (; *s1 && *s2; s1++, s2++)
15,140✔
1258
                if (*s1 != *s2)
9,236✔
1259
                        break;
1260

1261
        return in_charset(s1, ok) && in_charset(s2, ok);
10,248✔
1262
}
1263

1264
char* string_replace_char(char *str, char old_char, char new_char) {
392,658✔
1265
        assert(str);
392,658✔
1266
        assert(old_char != '\0');
392,658✔
1267
        assert(new_char != '\0');
392,658✔
1268
        assert(old_char != new_char);
392,658✔
1269

1270
        for (char *p = strchr(str, old_char); p; p = strchr(p + 1, old_char))
397,028✔
1271
                *p = new_char;
4,370✔
1272

1273
        return str;
392,658✔
1274
}
1275

1276
int make_cstring(const char *s, size_t n, MakeCStringMode mode, char **ret) {
1,445✔
1277
        char *b;
1,445✔
1278

1279
        assert(s || n == 0);
1,445✔
1280
        assert(mode >= 0);
1,445✔
1281
        assert(mode < _MAKE_CSTRING_MODE_MAX);
1,445✔
1282

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

1286
        if (n == 0) {
1,445✔
1287
                if (mode == MAKE_CSTRING_REQUIRE_TRAILING_NUL)
20✔
1288
                        return -EINVAL;
1289

1290
                if (!ret)
19✔
1291
                        return 0;
1292

1293
                b = new0(char, 1);
19✔
1294
        } else {
1295
                const char *nul;
1,425✔
1296

1297
                nul = memchr(s, 0, n);
1,425✔
1298
                if (nul) {
1,425✔
1299
                        if (nul < s + n - 1 || /* embedded NUL? */
19✔
1300
                            mode == MAKE_CSTRING_REFUSE_TRAILING_NUL)
1301
                                return -EINVAL;
1302

1303
                        n--;
1304
                } else if (mode == MAKE_CSTRING_REQUIRE_TRAILING_NUL)
1,406✔
1305
                        return -EINVAL;
1306

1307
                if (!ret)
1,410✔
1308
                        return 0;
1309

1310
                b = memdup_suffix0(s, n);
1,410✔
1311
        }
1312
        if (!b)
1,429✔
1313
                return -ENOMEM;
1314

1315
        *ret = b;
1,429✔
1316
        return 0;
1,429✔
1317
}
1318

1319
size_t strspn_from_end(const char *str, const char *accept) {
362,109✔
1320
        size_t n = 0;
362,109✔
1321

1322
        if (isempty(str))
362,109✔
1323
                return 0;
1324

1325
        if (isempty(accept))
362,106✔
1326
                return 0;
1327

1328
        for (const char *p = str + strlen(str); p > str && strchr(accept, p[-1]); p--)
721,257✔
1329
                n++;
359,152✔
1330

1331
        return n;
1332
}
1333

1334
char* strdupspn(const char *a, const char *accept) {
×
1335
        if (isempty(a) || isempty(accept))
×
1336
                return strdup("");
×
1337

1338
        return strndup(a, strspn(a, accept));
×
1339
}
1340

1341
char* strdupcspn(const char *a, const char *reject) {
27,703✔
1342
        if (isempty(a))
27,703✔
1343
                return strdup("");
×
1344
        if (isempty(reject))
27,703✔
1345
                return strdup(a);
×
1346

1347
        return strndup(a, strcspn(a, reject));
27,703✔
1348
}
1349

1350
char* find_line_startswith(const char *haystack, const char *needle) {
17,241✔
1351
        char *p;
17,241✔
1352

1353
        assert(haystack);
17,241✔
1354
        assert(needle);
17,241✔
1355

1356
        /* Finds the first line in 'haystack' that starts with the specified string. Returns a pointer to the
1357
         * first character after it */
1358

1359
        p = strstr(haystack, needle);
17,241✔
1360
        if (!p)
17,241✔
1361
                return NULL;
1362

1363
        if (p > haystack)
16,873✔
1364
                while (p[-1] != '\n') {
16,638✔
1365
                        p = strstr(p + 1, needle);
9✔
1366
                        if (!p)
9✔
1367
                                return NULL;
1368
                }
1369

1370
        return p + strlen(needle);
16,872✔
1371
}
1372

1373
bool version_is_valid(const char *s) {
3,975✔
1374
        if (isempty(s))
3,975✔
1375
                return false;
1376

1377
        if (!filename_part_is_valid(s))
3,973✔
1378
                return false;
1379

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

1384
        return true;
1385
}
1386

1387
bool version_is_valid_versionspec(const char *s) {
62✔
1388
        if (!filename_part_is_valid(s))
62✔
1389
                return false;
1390

1391
        if (!in_charset(s, ALPHANUMERICAL "-.~^"))
62✔
1392
                return false;
×
1393

1394
        return true;
1395
}
1396

1397
ssize_t strlevenshtein(const char *x, const char *y) {
90✔
1398
        _cleanup_free_ size_t *t0 = NULL, *t1 = NULL, *t2 = NULL;
180✔
1399
        size_t xl, yl;
90✔
1400

1401
        /* This is inspired from the Linux kernel's Levenshtein implementation */
1402

1403
        if (streq_ptr(x, y))
90✔
1404
                return 0;
1405

1406
        xl = strlen_ptr(x);
86✔
1407
        if (xl > SSIZE_MAX)
85✔
1408
                return -E2BIG;
1409

1410
        yl = strlen_ptr(y);
86✔
1411
        if (yl > SSIZE_MAX)
85✔
1412
                return -E2BIG;
1413

1414
        if (isempty(x))
86✔
1415
                return yl;
3✔
1416
        if (isempty(y))
83✔
1417
                return xl;
1✔
1418

1419
        t0 = new0(size_t, yl + 1);
82✔
1420
        if (!t0)
82✔
1421
                return -ENOMEM;
1422
        t1 = new0(size_t, yl + 1);
82✔
1423
        if (!t1)
82✔
1424
                return -ENOMEM;
1425
        t2 = new0(size_t, yl + 1);
82✔
1426
        if (!t2)
82✔
1427
                return -ENOMEM;
1428

1429
        for (size_t i = 0; i <= yl; i++)
905✔
1430
                t1[i] = i;
823✔
1431

1432
        for (size_t i = 0; i < xl; i++) {
453✔
1433
                t2[0] = i + 1;
371✔
1434

1435
                for (size_t j = 0; j < yl; j++) {
4,228✔
1436
                        /* Substitution */
1437
                        t2[j+1] = t1[j] + (x[i] != y[j]);
3,857✔
1438

1439
                        /* Swap */
1440
                        if (i > 0 && j > 0 && x[i-1] == y[j] && x[i] == y[j-1] && t2[j+1] > t0[j-1] + 1)
3,857✔
1441
                                t2[j+1] = t0[j-1] + 1;
10✔
1442

1443
                        /* Deletion */
1444
                        if (t2[j+1] > t1[j+1] + 1)
3,857✔
1445
                                t2[j+1] = t1[j+1] + 1;
141✔
1446

1447
                        /* Insertion */
1448
                        if (t2[j+1] > t2[j] + 1)
3,857✔
1449
                                t2[j+1] = t2[j] + 1;
638✔
1450
                }
1451

1452
                size_t *dummy = t0;
1453
                t0 = t1;
1454
                t1 = t2;
1455
                t2 = dummy;
1456
        }
1457

1458
        return t1[yl];
82✔
1459
}
1460

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

1464
        if (!haystack || !needle)
7,180✔
1465
                return NULL;
1466

1467
        /* Special case: for the empty string we return the very last possible occurrence, i.e. *after* the
1468
         * last char, not before. */
1469
        if (*needle == 0)
7,177✔
1470
                return strchr(haystack, 0);
2✔
1471

1472
        for (const char *p = strstr(haystack, needle), *q; p; p = q) {
7,185✔
1473
                q = strstr(p + 1, needle);
21✔
1474
                if (!q)
21✔
1475
                        return (char *) p;
1476
        }
1477
        return NULL;
1478
}
1479

1480
size_t str_common_prefix(const char *a, const char *b) {
16✔
1481
        assert(a);
16✔
1482
        assert(b);
16✔
1483

1484
        /* Returns the length of the common prefix of the two specified strings, or SIZE_MAX in case the
1485
         * strings are fully identical. */
1486

1487
        for (size_t n = 0;; n++) {
39✔
1488
                char c = a[n];
55✔
1489
                if (c != b[n])
55✔
1490
                        return n;
1491
                if (c == 0)
42✔
1492
                        return SIZE_MAX;
1493
        }
1494
}
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