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

systemd / systemd / 13297401319

12 Feb 2025 09:02PM UTC coverage: 71.784% (-0.06%) from 71.84%
13297401319

push

github

poettering
update TODO

293477 of 408836 relevant lines covered (71.78%)

714381.64 hits per line

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

93.33
/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 "macro.h"
18
#include "memory-util.h"
19
#include "memstream-util.h"
20
#include "path-util.h"
21
#include "string-util.h"
22
#include "strv.h"
23
#include "terminal-util.h"
24
#include "utf8.h"
25

26
char* first_word(const char *s, const char *word) {
3,268,785✔
27
        assert(s);
3,268,785✔
28
        assert(word);
3,268,785✔
29

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

33
        if (isempty(word))
3,268,785✔
34
                return (char*) s;
35

36
        const char *p = startswith(s, word);
3,268,784✔
37
        if (!p)
3,268,784✔
38
                return NULL;
39
        if (*p == '\0')
114,865✔
40
                return (char*) p;
41

42
        const char *nw = skip_leading_chars(p, WHITESPACE);
114,864✔
43
        if (p == nw)
114,864✔
44
                return NULL;
1✔
45

46
        return (char*) nw;
47
}
48

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

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

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

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

63
char* strextendn(char **x, const char *s, size_t l) {
30,565✔
64
        assert(x);
30,565✔
65
        assert(s || l == 0);
30,565✔
66

67
        if (l > 0)
30,565✔
68
                l = strnlen(s, l); /* ignore trailing noise */
30,004✔
69

70
        if (l > 0 || !*x) {
30,565✔
71
                size_t q;
30,291✔
72
                char *m;
30,291✔
73

74
                q = strlen_ptr(*x);
30,291✔
75
                m = realloc(*x, q + l + 1);
30,291✔
76
                if (!m)
30,291✔
77
                        return NULL;
78

79
                *mempcpy_typesafe(m + q, s, l) = 0;
30,291✔
80

81
                *x = m;
30,291✔
82
        }
83

84
        return *x;
30,565✔
85
}
86

87
char* strstrip(char *s) {
5,990,598✔
88
        if (!s)
5,990,598✔
89
                return NULL;
90

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

93
        return delete_trailing_chars(skip_leading_chars(s, WHITESPACE), WHITESPACE);
5,990,598✔
94
}
95

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

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

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

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

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

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

114
        *t = 0;
9✔
115

116
        return s;
9✔
117
}
118

119
char* delete_trailing_chars(char *s, const char *bad) {
7,092,591✔
120
        char *c = s;
7,092,591✔
121

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

124
        if (!s)
7,092,591✔
125
                return NULL;
126

127
        if (!bad)
7,092,591✔
128
                bad = WHITESPACE;
66,772✔
129

130
        for (char *p = s; *p; p++)
272,985,914✔
131
                if (!strchr(bad, *p))
265,893,323✔
132
                        c = p + 1;
255,117,236✔
133

134
        *c = 0;
7,092,591✔
135

136
        return s;
7,092,591✔
137
}
138

139
char* truncate_nl_full(char *s, size_t *ret_len) {
3,176,847✔
140
        size_t n;
3,176,847✔
141

142
        assert(s);
3,176,847✔
143

144
        n = strcspn(s, NEWLINE);
3,176,847✔
145
        s[n] = '\0';
3,176,847✔
146
        if (ret_len)
3,176,847✔
147
                *ret_len = n;
6✔
148
        return s;
3,176,847✔
149
}
150

151
char ascii_tolower(char x) {
4,382,189✔
152

153
        if (x >= 'A' && x <= 'Z')
4,382,189✔
154
                return x - 'A' + 'a';
167,844✔
155

156
        return x;
157
}
158

159
char ascii_toupper(char x) {
207,151✔
160

161
        if (x >= 'a' && x <= 'z')
207,151✔
162
                return x - 'a' + 'A';
101,061✔
163

164
        return x;
165
}
166

167
char* ascii_strlower(char *t) {
21,587✔
168
        assert(t);
21,587✔
169

170
        for (char *p = t; *p; p++)
138,011✔
171
                *p = ascii_tolower(*p);
116,424✔
172

173
        return t;
21,587✔
174
}
175

176
char* ascii_strupper(char *t) {
9,993✔
177
        assert(t);
9,993✔
178

179
        for (char *p = t; *p; p++)
216,990✔
180
                *p = ascii_toupper(*p);
206,997✔
181

182
        return t;
9,993✔
183
}
184

185
char* ascii_strlower_n(char *t, size_t n) {
734,255✔
186
        if (n <= 0)
734,255✔
187
                return t;
188

189
        for (size_t i = 0; i < n; i++)
2,442,722✔
190
                t[i] = ascii_tolower(t[i]);
1,708,696✔
191

192
        return t;
193
}
194

195
int ascii_strcasecmp_n(const char *a, const char *b, size_t n) {
651,101✔
196

197
        for (; n > 0; a++, b++, n--) {
1,776,330✔
198
                int x, y;
1,274,677✔
199

200
                x = (int) (uint8_t) ascii_tolower(*a);
1,274,677✔
201
                y = (int) (uint8_t) ascii_tolower(*b);
1,274,677✔
202

203
                if (x != y)
1,274,677✔
204
                        return x - y;
149,448✔
205
        }
206

207
        return 0;
208
}
209

210
int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) {
492,247✔
211
        int r;
492,247✔
212

213
        r = ascii_strcasecmp_n(a, b, MIN(n, m));
492,247✔
214
        if (r != 0)
492,247✔
215
                return r;
216

217
        return CMP(n, m);
452,020✔
218
}
219

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

226
        return false;
227
}
228

229
bool string_has_cc(const char *p, const char *ok) {
201,837✔
230
        assert(p);
201,837✔
231

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

238
        for (const char *t = p; *t; t++) {
1,429,499✔
239
                if (ok && strchr(ok, *t))
1,227,678✔
240
                        continue;
8✔
241

242
                if (char_is_cc(*t))
1,227,670✔
243
                        return true;
244
        }
245

246
        return false;
247
}
248

249
static int write_ellipsis(char *buf, bool unicode) {
3,092✔
250
        const char *s = special_glyph_full(SPECIAL_GLYPH_ELLIPSIS, unicode);
3,092✔
251
        assert(strlen(s) == 3);
3,092✔
252
        memcpy(buf, s, 3);
3,092✔
253
        return 3;
3,092✔
254
}
255

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

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

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

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

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

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

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

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

287
static bool string_has_ansi_sequence(const char *s, size_t len) {
6,746✔
288
        const char *t = s;
6,746✔
289

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

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

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

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

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

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

316
        assert(s);
3,966✔
317
        assert(percent <= 100);
3,966✔
318
        assert(new_length != SIZE_MAX);
3,966✔
319

320
        if (old_length <= new_length)
3,966✔
321
                return strndup(s, old_length);
1,897✔
322

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

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

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

335
        case 2:
35✔
336
                if (!is_locale_utf8())
35✔
337
                        return strdup("..");
×
338

339
                break;
340

341
        default:
342
                break;
343
        }
344

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

350
        t = new(char, new_length+3);
2,004✔
351
        if (!t)
2,004✔
352
                return NULL;
353

354
        assert(new_length >= need_space);
2,004✔
355

356
        x = ((new_length - need_space) * percent + 50) / 100;
2,004✔
357
        assert(x <= new_length - need_space);
2,004✔
358

359
        write_ellipsis(mempcpy(t, s, x), /* unicode = */ false);
2,004✔
360
        suffix_len = new_length - x - need_space;
2,004✔
361
        memcpy(t + x + 3, s + old_length - suffix_len, suffix_len);
2,004✔
362
        *(t + x + 3 + suffix_len) = '\0';
2,004✔
363

364
        return t;
2,004✔
365
}
366

367
char* ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
6,917✔
368
        size_t x, k, len, len2;
6,917✔
369
        const char *i, *j;
6,917✔
370
        int r;
6,917✔
371

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

384
        assert(s);
6,917✔
385
        assert(percent <= 100);
6,917✔
386

387
        if (new_length == SIZE_MAX)
6,917✔
388
                return strndup(s, old_length);
×
389

390
        if (new_length == 0)
6,917✔
391
                return strdup("");
171✔
392

393
        bool has_ansi_seq = string_has_ansi_sequence(s, old_length);
6,746✔
394

395
        /* If no multibyte characters or ANSI sequences, use ascii_ellipsize_mem for speed */
396
        if (!has_ansi_seq && ascii_is_valid_n(s, old_length))
6,746✔
397
                return ascii_ellipsize_mem(s, old_length, new_length, percent);
3,966✔
398

399
        x = (new_length - 1) * percent / 100;
2,780✔
400
        assert(x <= new_length - 1);
2,780✔
401

402
        k = 0;
403
        for (i = s; i < s + old_length; ) {
24,291✔
404
                size_t slen = has_ansi_seq ? ansi_sequence_length(i, old_length - (i - s)) : 0;
23,100✔
405
                if (slen > 0) {
984✔
406
                        i += slen;
221✔
407
                        continue;  /* ANSI sequences don't take up any space in output */
221✔
408
                }
409

410
                char32_t c;
22,879✔
411
                r = utf8_encoded_to_unichar(i, &c);
22,879✔
412
                if (r < 0)
22,879✔
413
                        return NULL;
×
414

415
                int w = unichar_iswide(c) ? 2 : 1;
22,879✔
416
                if (k + w > x)
22,879✔
417
                        break;
418

419
                k += w;
21,290✔
420
                i += r;
21,290✔
421
        }
422

423
        const char *ansi_start = s + old_length;
2,780✔
424
        size_t ansi_len = 0;
2,780✔
425

426
        for (const char *t = j = s + old_length; t > i && k < new_length; ) {
8,619✔
427
                char32_t c;
5,925✔
428
                int w;
5,925✔
429
                const char *tt;
5,925✔
430

431
                if (has_ansi_seq && ansi_start >= t)
5,925✔
432
                        /* Figure out the previous ANSI sequence, if any */
433
                        ansi_len = previous_ansi_sequence(s, t - s, &ansi_start);
263✔
434

435
                /* If the sequence extends all the way to the current position, skip it. */
436
                if (has_ansi_seq && ansi_len > 0 && ansi_start + ansi_len == t) {
5,925✔
437
                        t = ansi_start;
112✔
438
                        continue;
112✔
439
                }
440

441
                tt = utf8_prev_char(t);
5,813✔
442
                r = utf8_encoded_to_unichar(tt, &c);
5,813✔
443
                if (r < 0)
5,813✔
444
                        return NULL;
×
445

446
                w = unichar_iswide(c) ? 2 : 1;
5,813✔
447
                if (k + w > new_length)
5,813✔
448
                        break;
449

450
                k += w;
5,727✔
451
                j = t = tt;  /* j should always point to the first "real" character */
5,727✔
452
        }
453

454
        /* We don't actually need to ellipsize */
455
        if (i >= j)
2,780✔
456
                return memdup_suffix0(s, old_length);
1,716✔
457

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

468
        len = i - s;
1,064✔
469
        len2 = s + old_length - j;
1,064✔
470

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

476
        char *e = new(char, alloc_len);
1,064✔
477
        if (!e)
1,064✔
478
                return NULL;
479

480
        memcpy_safe(e, s, len);
1,064✔
481
        write_ellipsis(e + len, /* unicode = */ true);
1,064✔
482

483
        char *dst = e + len + 3;
1,064✔
484

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

496
        memcpy_safe(dst, j, len2);
1,064✔
497
        dst[len2] = '\0';
1,064✔
498

499
        return e;
1,064✔
500
}
501

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

515
        size_t i = 0, last_char_width[4] = {}, k = 0;
50,819✔
516

517
        assert(buf);
50,819✔
518
        assert(len > 0); /* at least a terminating NUL */
50,819✔
519
        assert(s);
50,819✔
520

521
        for (;;) {
1,143,727✔
522
                char four[4];
597,273✔
523
                int w;
597,273✔
524

525
                if (*s == 0) /* terminating NUL detected? then we are done! */
597,273✔
526
                        goto done;
50,783✔
527

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

533
                /* OK, there was space, let's add this escaped character to the buffer */
534
                memcpy(buf + i, four, w);
546,454✔
535
                i += w;
546,454✔
536

537
                /* And remember its width in the ring buffer */
538
                last_char_width[k] = w;
546,454✔
539
                k = (k + 1) % 4;
546,454✔
540

541
                s++;
546,454✔
542
        }
543

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

548
                if (i + 4 <= len) /* nice, we reached our space goal */
81✔
549
                        break;
550

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

555
                assert(i >= last_char_width[k]);
45✔
556
                i -= last_char_width[k];
45✔
557
        }
558

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

569
done:
4✔
570
        buf[i] = '\0';
50,819✔
571
        return buf;
50,819✔
572
}
573

574
char* strshorten(char *s, size_t l) {
26,808✔
575
        assert(s);
26,808✔
576

577
        if (l >= SIZE_MAX-1) /* Would not change anything */
26,808✔
578
                return s;
579

580
        if (strnlen(s, l+1) > l)
26,806✔
581
                s[l] = 0;
18✔
582

583
        return s;
584
}
585

586
int strgrowpad0(char **s, size_t l) {
14✔
587
        size_t sz;
14✔
588

589
        assert(s);
14✔
590

591
        if (*s) {
14✔
592
                sz = strlen(*s) + 1;
14✔
593
                if (sz >= l) /* never shrink */
14✔
594
                        return 0;
595
        } else
596
                sz = 0;
597

598
        char *q = realloc(*s, l);
14✔
599
        if (!q)
14✔
600
                return -ENOMEM;
601

602
        *s = q;
14✔
603

604
        memzero(*s + sz, l - sz);
14✔
605
        return 0;
606
}
607

608
char* strreplace(const char *text, const char *old_string, const char *new_string) {
2,929✔
609
        size_t l, old_len, new_len;
2,929✔
610
        char *t, *ret = NULL;
2,929✔
611
        const char *f;
2,929✔
612

613
        assert(old_string);
2,929✔
614
        assert(new_string);
2,929✔
615

616
        if (!text)
2,929✔
617
                return NULL;
2,929✔
618

619
        old_len = strlen(old_string);
2,928✔
620
        new_len = strlen(new_string);
2,928✔
621

622
        l = strlen(text);
2,928✔
623
        if (!GREEDY_REALLOC(ret, l+1))
2,928✔
624
                return NULL;
625

626
        f = text;
2,928✔
627
        t = ret;
2,928✔
628
        while (*f) {
95,768✔
629
                size_t d, nl;
92,840✔
630

631
                if (!startswith(f, old_string)) {
92,840✔
632
                        *(t++) = *(f++);
90,663✔
633
                        continue;
90,663✔
634
                }
635

636
                d = t - ret;
2,177✔
637
                nl = l - old_len + new_len;
2,177✔
638

639
                if (!GREEDY_REALLOC(ret, nl + 1))
2,177✔
640
                        return mfree(ret);
×
641

642
                l = nl;
2,177✔
643
                t = ret + d;
2,177✔
644

645
                t = stpcpy(t, new_string);
2,177✔
646
                f += old_len;
2,177✔
647
        }
648

649
        *t = 0;
2,928✔
650
        return ret;
2,928✔
651
}
652

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

659
        if (!offsets)
392✔
660
                return;
661

662
        assert(shift);
382✔
663

664
        if ((size_t) diff < offsets[0])
382✔
665
                shift[0] += size;
×
666
        if ((size_t) diff < offsets[1])
382✔
667
                shift[1] += size;
×
668
}
669

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

683
        assert(ibuf);
80,029✔
684
        assert(*ibuf);
80,029✔
685

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

700
        isz = _isz ? *_isz : strlen(*ibuf);
80,029✔
701

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

708
        for (const char *i = *ibuf; i < *ibuf + isz + 1; i++) {
9,413,390✔
709

710
                bool eot = i >= *ibuf + isz;
9,333,361✔
711

712
                switch (state) {
9,333,361✔
713

714
                case STATE_OTHER:
9,333,278✔
715
                        if (eot)
9,333,278✔
716
                                break;
717

718
                        if (*i == '\r') {
9,253,249✔
719
                                n_carriage_returns++;
7✔
720
                                break;
7✔
721
                        } else if (*i == '\n')
9,253,242✔
722
                                /* Ignore carriage returns before new line */
723
                                n_carriage_returns = 0;
2✔
724
                        for (; n_carriage_returns > 0; n_carriage_returns--)
9,253,244✔
725
                                fputc('\r', f);
2✔
726

727
                        if (*i == '\x1B')
9,253,242✔
728
                                state = STATE_ESCAPE;
729
                        else if (*i == '\t') {
9,253,226✔
730
                                fputs("        ", f);
387✔
731
                                advance_offsets(i - *ibuf, highlight, shift, 7);
387✔
732
                        } else
733
                                fputc(*i, f);
9,252,839✔
734

735
                        break;
736

737
                case STATE_ESCAPE:
16✔
738
                        assert(n_carriage_returns == 0);
16✔
739

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

757
                        break;
758

759
                case STATE_CSI:
41✔
760
                        assert(n_carriage_returns == 0);
41✔
761

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

771
                        break;
772

773
                case STATE_OSC:
24✔
774
                        assert(n_carriage_returns == 0);
24✔
775

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

790
                        break;
791

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

802
                        break;
803
                }
804
        }
805

806
        char *obuf;
80,029✔
807
        if (memstream_finalize(&m, &obuf, _isz) < 0)
80,029✔
808
                return NULL;
809

810
        free_and_replace(*ibuf, obuf);
80,029✔
811

812
        if (highlight) {
80,029✔
813
                highlight[0] += shift[0];
80,020✔
814
                highlight[1] += shift[1];
80,020✔
815
        }
816

817
        return *ibuf;
80,029✔
818
}
819

820
char* strextend_with_separator_internal(char **x, const char *separator, ...) {
15,663,487✔
821
        _cleanup_free_ char *buffer = NULL;
15,663,487✔
822
        size_t f, l, l_separator;
15,663,487✔
823
        bool need_separator;
15,663,487✔
824
        char *nr, *p;
15,663,487✔
825
        va_list ap;
15,663,487✔
826

827
        if (!x)
15,663,487✔
828
                x = &buffer;
14,490,036✔
829

830
        l = f = strlen_ptr(*x);
15,663,487✔
831

832
        need_separator = !isempty(*x);
15,663,487✔
833
        l_separator = strlen_ptr(separator);
15,663,487✔
834

835
        va_start(ap, separator);
15,663,487✔
836
        for (const char *t;;) {
58,538,053✔
837
                size_t n;
58,538,053✔
838

839
                t = va_arg(ap, const char *);
58,538,053✔
840
                if (!t)
58,538,053✔
841
                        break;
842
                if (t == POINTER_MAX)
42,874,566✔
843
                        continue;
3✔
844

845
                n = strlen(t);
42,874,563✔
846

847
                if (need_separator)
42,874,563✔
848
                        n += l_separator;
27,878,935✔
849

850
                if (n >= SIZE_MAX - l) {
42,874,563✔
851
                        va_end(ap);
×
852
                        return NULL;
×
853
                }
854

855
                l += n;
42,874,563✔
856
                need_separator = true;
42,874,563✔
857
        }
858
        va_end(ap);
15,663,487✔
859

860
        need_separator = !isempty(*x);
15,663,487✔
861

862
        nr = realloc(*x, GREEDY_ALLOC_ROUND_UP(l+1));
15,663,487✔
863
        if (!nr)
15,663,487✔
864
                return NULL;
865

866
        *x = nr;
15,663,487✔
867
        p = nr + f;
15,663,487✔
868

869
        va_start(ap, separator);
15,663,487✔
870
        for (;;) {
58,538,053✔
871
                const char *t;
58,538,053✔
872

873
                t = va_arg(ap, const char *);
58,538,053✔
874
                if (!t)
58,538,053✔
875
                        break;
876
                if (t == POINTER_MAX)
42,874,566✔
877
                        continue;
3✔
878

879
                if (need_separator && separator)
42,874,563✔
880
                        p = stpcpy(p, separator);
179,687✔
881

882
                p = stpcpy(p, t);
42,874,563✔
883

884
                need_separator = true;
42,874,563✔
885
        }
886
        va_end(ap);
15,663,487✔
887

888
        assert(p == nr + l);
15,663,487✔
889
        *p = 0;
15,663,487✔
890

891
        /* If no buffer to extend was passed in return the start of the buffer */
892
        if (buffer)
15,663,487✔
893
                return TAKE_PTR(buffer);
14,490,036✔
894

895
        /* Otherwise we extended the buffer: return the end */
896
        return p;
897
}
898

899
int strextendf_with_separator(char **x, const char *separator, const char *format, ...) {
23,189✔
900
        size_t m, a, l_separator;
23,189✔
901
        va_list ap;
23,189✔
902
        int l;
23,189✔
903

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

908
        assert(x);
23,189✔
909
        assert(format);
23,189✔
910

911
        l_separator = isempty(*x) ? 0 : strlen_ptr(separator);
33,986✔
912

913
        /* Let's try to use the allocated buffer, if there's room at the end still. Otherwise let's extend by 64 chars. */
914
        if (*x) {
23,189✔
915
                m = strlen(*x);
10,797✔
916
                a = MALLOC_SIZEOF_SAFE(*x);
10,797✔
917
                assert(a >= m + 1);
10,797✔
918
        } else
919
                m = a = 0;
920

921
        if (a - m < 17 + l_separator) { /* if there's less than 16 chars space, then enlarge the buffer first */
23,189✔
922
                char *n;
17,995✔
923

924
                if (_unlikely_(l_separator > SIZE_MAX - 64)) /* overflow check #1 */
17,995✔
925
                        return -ENOMEM;
23,189✔
926
                if (_unlikely_(m > SIZE_MAX - 64 - l_separator)) /* overflow check #2 */
17,995✔
927
                        return -ENOMEM;
928

929
                n = realloc(*x, m + 64 + l_separator);
17,995✔
930
                if (!n)
17,995✔
931
                        return -ENOMEM;
932

933
                *x = n;
17,995✔
934
                a = MALLOC_SIZEOF_SAFE(*x);
17,995✔
935
        }
936

937
        /* Now, let's try to format the string into it */
938
        memcpy_safe(*x + m, separator, l_separator);
23,189✔
939
        va_start(ap, format);
23,189✔
940
        l = vsnprintf(*x + m + l_separator, a - m - l_separator, format, ap);
23,189✔
941
        va_end(ap);
23,189✔
942

943
        assert(l >= 0);
23,189✔
944

945
        if ((size_t) l < a - m - l_separator) {
23,189✔
946
                char *n;
23,164✔
947

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

957
                /* Wasn't enough. Then let's allocate exactly what we need. */
958

959
                if (_unlikely_((size_t) l > SIZE_MAX - (l_separator + 1))) /* overflow check #1 */
25✔
960
                        goto oom;
×
961
                if (_unlikely_(m > SIZE_MAX - ((size_t) l + l_separator + 1))) /* overflow check #2 */
25✔
962
                        goto oom;
×
963

964
                a = m + (size_t) l + l_separator + 1;
25✔
965
                n = realloc(*x, a);
25✔
966
                if (!n)
25✔
967
                        goto oom;
×
968
                *x = n;
25✔
969

970
                va_start(ap, format);
25✔
971
                l = vsnprintf(*x + m + l_separator, a - m - l_separator, format, ap);
25✔
972
                va_end(ap);
25✔
973

974
                assert((size_t) l < a - m - l_separator);
25✔
975
        }
976

977
        return 0;
978

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

985
char* strrep(const char *s, unsigned n) {
11✔
986
        char *r, *p;
11✔
987
        size_t l;
11✔
988

989
        assert(s);
11✔
990

991
        l = strlen(s);
11✔
992
        p = r = malloc(l * n + 1);
11✔
993
        if (!r)
11✔
994
                return NULL;
995

996
        for (unsigned i = 0; i < n; i++)
40,337✔
997
                p = stpcpy(p, s);
40,326✔
998

999
        *p = 0;
11✔
1000
        return r;
11✔
1001
}
1002

1003
int split_pair(const char *s, const char *sep, char **ret_first, char **ret_second) {
3,195✔
1004
        assert(s);
3,195✔
1005
        assert(!isempty(sep));
3,195✔
1006
        assert(ret_first);
3,195✔
1007
        assert(ret_second);
3,195✔
1008

1009
        const char *x = strstr(s, sep);
3,195✔
1010
        if (!x)
3,195✔
1011
                return -EINVAL;
3,195✔
1012

1013
        _cleanup_free_ char *a = strndup(s, x - s);
542✔
1014
        if (!a)
542✔
1015
                return -ENOMEM;
1016

1017
        _cleanup_free_ char *b = strdup(x + strlen(sep));
542✔
1018
        if (!b)
542✔
1019
                return -ENOMEM;
1020

1021
        *ret_first = TAKE_PTR(a);
542✔
1022
        *ret_second = TAKE_PTR(b);
542✔
1023
        return 0;
542✔
1024
}
1025

1026
int free_and_strdup(char **p, const char *s) {
1,487,687✔
1027
        char *t;
1,487,687✔
1028

1029
        assert(p);
1,487,687✔
1030

1031
        /* Replaces a string pointer with a strdup()ed new string,
1032
         * possibly freeing the old one. */
1033

1034
        if (streq_ptr(*p, s))
1,487,687✔
1035
                return 0;
1,487,687✔
1036

1037
        if (s) {
1,289,864✔
1038
                t = strdup(s);
1,286,073✔
1039
                if (!t)
1,286,073✔
1040
                        return -ENOMEM;
1041
        } else
1042
                t = NULL;
1043

1044
        free_and_replace(*p, t);
1,289,864✔
1045

1046
        return 1;
1,289,864✔
1047
}
1048

1049
int free_and_strndup(char **p, const char *s, size_t l) {
64,708✔
1050
        char *t;
64,708✔
1051

1052
        assert(p);
64,708✔
1053
        assert(s || l == 0);
64,708✔
1054

1055
        /* Replaces a string pointer with a strndup()ed new string,
1056
         * freeing the old one. */
1057

1058
        if (!*p && !s)
64,708✔
1059
                return 0;
64,708✔
1060

1061
        if (*p && s && strneq(*p, s, l) && (l > strlen(*p) || (*p)[l] == '\0'))
64,708✔
1062
                return 0;
1063

1064
        if (s) {
30,655✔
1065
                t = strndup(s, l);
30,714✔
1066
                if (!t)
30,714✔
1067
                        return -ENOMEM;
1068
        } else
1069
                t = NULL;
1070

1071
        free_and_replace(*p, t);
30,715✔
1072
        return 1;
30,715✔
1073
}
1074

1075
int strdup_to_full(char **ret, const char *src) {
541,868✔
1076
        if (!src) {
541,868✔
1077
                if (ret)
181,300✔
1078
                        *ret = NULL;
181,299✔
1079

1080
                return 0;
181,300✔
1081
        } else {
1082
                if (ret) {
360,568✔
1083
                        char *t = strdup(src);
360,566✔
1084
                        if (!t)
360,566✔
1085
                                return -ENOMEM;
1086
                        *ret = t;
360,566✔
1087
                }
1088

1089
                return 1;
360,568✔
1090
        }
1091
};
1092

1093
bool string_is_safe(const char *p) {
154,265✔
1094
        if (!p)
154,265✔
1095
                return false;
1096

1097
        /* Checks if the specified string contains no quotes or control characters */
1098

1099
        for (const char *t = p; *t; t++) {
1,556,045✔
1100
                if (*t > 0 && *t < ' ') /* no control characters */
1,401,793✔
1101
                        return false;
1102

1103
                if (strchr(QUOTES "\\\x7f", *t))
1,401,785✔
1104
                        return false;
1105
        }
1106

1107
        return true;
1108
}
1109

1110
char* string_erase(char *x) {
100✔
1111
        if (!x)
100✔
1112
                return NULL;
1113

1114
        /* A delicious drop of snake-oil! To be called on memory where we stored passphrases or so, after we
1115
         * used them. */
1116
        explicit_bzero_safe(x, strlen(x));
100✔
1117
        return x;
100✔
1118
}
1119

1120
int string_truncate_lines(const char *s, size_t n_lines, char **ret) {
210✔
1121
        const char *p = s, *e = s;
210✔
1122
        bool truncation_applied = false;
210✔
1123
        char *copy;
210✔
1124
        size_t n = 0;
210✔
1125

1126
        assert(s);
210✔
1127

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

1132
        for (;;) {
436✔
1133
                size_t k;
323✔
1134

1135
                k = strcspn(p, "\n");
323✔
1136

1137
                if (p[k] == 0) {
323✔
1138
                        if (k == 0) /* final empty line */
178✔
1139
                                break;
1140

1141
                        if (n >= n_lines) /* above threshold */
153✔
1142
                                break;
1143

1144
                        e = p + k; /* last line to include */
138✔
1145
                        break;
138✔
1146
                }
1147

1148
                assert(p[k] == '\n');
145✔
1149

1150
                if (n >= n_lines)
145✔
1151
                        break;
1152

1153
                if (k > 0)
113✔
1154
                        e = p + k;
90✔
1155

1156
                p += k + 1;
113✔
1157
                n++;
113✔
1158
        }
1159

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

1168
                copy = strndup(s, e - s);
68✔
1169
        }
1170
        if (!copy)
210✔
1171
                return -ENOMEM;
1172

1173
        *ret = copy;
210✔
1174
        return truncation_applied;
210✔
1175
}
1176

1177
int string_extract_line(const char *s, size_t i, char **ret) {
127,141✔
1178
        const char *p = s;
127,141✔
1179
        size_t c = 0;
127,141✔
1180

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

1187
        for (;;) {
311,789✔
1188
                const char *q;
219,465✔
1189

1190
                q = strchr(p, '\n');
219,465✔
1191
                if (i == c) {
219,465✔
1192
                        /* The line we are looking for! */
1193

1194
                        if (q) {
124,357✔
1195
                                char *m;
2,919✔
1196

1197
                                m = strndup(p, q - p);
2,919✔
1198
                                if (!m)
2,919✔
1199
                                        return -ENOMEM;
1200

1201
                                *ret = m;
2,919✔
1202
                                return !isempty(q + 1); /* More coming? */
5,838✔
1203
                        } else
1204
                                /* Tell the caller to use the input string if equal */
1205
                                return strdup_to(ret, p != s ? p : NULL);
241,669✔
1206
                }
1207

1208
                if (!q)
95,108✔
1209
                        /* No more lines, return empty line */
1210
                        return strdup_to(ret, "");
2,784✔
1211

1212
                p = q + 1;
92,324✔
1213
                c++;
92,324✔
1214
        }
1215
}
1216

1217
int string_contains_word_strv(const char *string, const char *separators, char * const *words, const char **ret_word) {
71✔
1218
        /* In the default mode with no separators specified, we split on whitespace and coalesce separators. */
1219
        const ExtractFlags flags = separators ? EXTRACT_DONT_COALESCE_SEPARATORS : 0;
71✔
1220
        const char *found = NULL;
71✔
1221
        int r;
196✔
1222

1223
        for (;;) {
321✔
1224
                _cleanup_free_ char *w = NULL;
125✔
1225

1226
                r = extract_first_word(&string, &w, separators, flags);
196✔
1227
                if (r < 0)
196✔
1228
                        return r;
×
1229
                if (r == 0)
196✔
1230
                        break;
1231

1232
                found = strv_find(words, w);
146✔
1233
                if (found)
146✔
1234
                        break;
1235
        }
1236

1237
        if (ret_word)
71✔
1238
                *ret_word = found;
7✔
1239
        return !!found;
71✔
1240
}
1241

1242
bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok) {
9,361✔
1243
        if (!s1 && !s2)
9,361✔
1244
                return true;
1245
        if (!s1 || !s2)
9,360✔
1246
                return false;
1247

1248
        if (!ok)
9,358✔
1249
                ok = WHITESPACE;
13✔
1250

1251
        for (; *s1 && *s2; s1++, s2++)
19,167✔
1252
                if (*s1 != *s2)
12,310✔
1253
                        break;
1254

1255
        return in_charset(s1, ok) && in_charset(s2, ok);
11,865✔
1256
}
1257

1258
char* string_replace_char(char *str, char old_char, char new_char) {
517,399✔
1259
        assert(str);
517,399✔
1260
        assert(old_char != '\0');
517,399✔
1261
        assert(new_char != '\0');
517,399✔
1262
        assert(old_char != new_char);
517,399✔
1263

1264
        for (char *p = strchr(str, old_char); p; p = strchr(p + 1, old_char))
521,903✔
1265
                *p = new_char;
4,504✔
1266

1267
        return str;
517,399✔
1268
}
1269

1270
int make_cstring(const char *s, size_t n, MakeCStringMode mode, char **ret) {
1,732✔
1271
        char *b;
1,732✔
1272

1273
        assert(s || n == 0);
1,732✔
1274
        assert(mode >= 0);
1,732✔
1275
        assert(mode < _MAKE_CSTRING_MODE_MAX);
1,732✔
1276

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

1280
        if (n == 0) {
1,732✔
1281
                if (mode == MAKE_CSTRING_REQUIRE_TRAILING_NUL)
20✔
1282
                        return -EINVAL;
1283

1284
                if (!ret)
19✔
1285
                        return 0;
1286

1287
                b = new0(char, 1);
19✔
1288
        } else {
1289
                const char *nul;
1,712✔
1290

1291
                nul = memchr(s, 0, n);
1,712✔
1292
                if (nul) {
1,712✔
1293
                        if (nul < s + n - 1 || /* embedded NUL? */
20✔
1294
                            mode == MAKE_CSTRING_REFUSE_TRAILING_NUL)
1295
                                return -EINVAL;
1296

1297
                        n--;
1298
                } else if (mode == MAKE_CSTRING_REQUIRE_TRAILING_NUL)
1,692✔
1299
                        return -EINVAL;
1300

1301
                if (!ret)
1,696✔
1302
                        return 0;
1303

1304
                b = memdup_suffix0(s, n);
1,696✔
1305
        }
1306
        if (!b)
1,715✔
1307
                return -ENOMEM;
1308

1309
        *ret = b;
1,715✔
1310
        return 0;
1,715✔
1311
}
1312

1313
size_t strspn_from_end(const char *str, const char *accept) {
494,067✔
1314
        size_t n = 0;
494,067✔
1315

1316
        if (isempty(str))
494,067✔
1317
                return 0;
1318

1319
        if (isempty(accept))
494,064✔
1320
                return 0;
1321

1322
        for (const char *p = str + strlen(str); p > str && strchr(accept, p[-1]); p--)
963,273✔
1323
                n++;
469,210✔
1324

1325
        return n;
1326
}
1327

1328
char* strdupspn(const char *a, const char *accept) {
×
1329
        if (isempty(a) || isempty(accept))
×
1330
                return strdup("");
×
1331

1332
        return strndup(a, strspn(a, accept));
×
1333
}
1334

1335
char* strdupcspn(const char *a, const char *reject) {
23,762✔
1336
        if (isempty(a))
23,762✔
1337
                return strdup("");
×
1338
        if (isempty(reject))
23,762✔
1339
                return strdup(a);
×
1340

1341
        return strndup(a, strcspn(a, reject));
23,762✔
1342
}
1343

1344
char* find_line_startswith(const char *haystack, const char *needle) {
17,421✔
1345
        char *p;
17,421✔
1346

1347
        assert(haystack);
17,421✔
1348
        assert(needle);
17,421✔
1349

1350
        /* Finds the first line in 'haystack' that starts with the specified string. Returns a pointer to the
1351
         * first character after it */
1352

1353
        p = strstr(haystack, needle);
17,421✔
1354
        if (!p)
17,421✔
1355
                return NULL;
1356

1357
        if (p > haystack)
17,055✔
1358
                while (p[-1] != '\n') {
16,813✔
1359
                        p = strstr(p + 1, needle);
9✔
1360
                        if (!p)
9✔
1361
                                return NULL;
1362
                }
1363

1364
        return p + strlen(needle);
17,054✔
1365
}
1366

1367
bool version_is_valid(const char *s) {
3,975✔
1368
        if (isempty(s))
3,975✔
1369
                return false;
1370

1371
        if (!filename_part_is_valid(s))
3,973✔
1372
                return false;
1373

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

1378
        return true;
1379
}
1380

1381
bool version_is_valid_versionspec(const char *s) {
60✔
1382
        if (!filename_part_is_valid(s))
60✔
1383
                return false;
1384

1385
        if (!in_charset(s, ALPHANUMERICAL "-.~^"))
60✔
1386
                return false;
×
1387

1388
        return true;
1389
}
1390

1391
ssize_t strlevenshtein(const char *x, const char *y) {
76✔
1392
        _cleanup_free_ size_t *t0 = NULL, *t1 = NULL, *t2 = NULL;
152✔
1393
        size_t xl, yl;
76✔
1394

1395
        /* This is inspired from the Linux kernel's Levenshtein implementation */
1396

1397
        if (streq_ptr(x, y))
76✔
1398
                return 0;
1399

1400
        xl = strlen_ptr(x);
72✔
1401
        if (xl > SSIZE_MAX)
71✔
1402
                return -E2BIG;
1403

1404
        yl = strlen_ptr(y);
72✔
1405
        if (yl > SSIZE_MAX)
71✔
1406
                return -E2BIG;
1407

1408
        if (isempty(x))
72✔
1409
                return yl;
3✔
1410
        if (isempty(y))
69✔
1411
                return xl;
1✔
1412

1413
        t0 = new0(size_t, yl + 1);
68✔
1414
        if (!t0)
68✔
1415
                return -ENOMEM;
1416
        t1 = new0(size_t, yl + 1);
68✔
1417
        if (!t1)
68✔
1418
                return -ENOMEM;
1419
        t2 = new0(size_t, yl + 1);
68✔
1420
        if (!t2)
68✔
1421
                return -ENOMEM;
1422

1423
        for (size_t i = 0; i <= yl; i++)
681✔
1424
                t1[i] = i;
613✔
1425

1426
        for (size_t i = 0; i < xl; i++) {
360✔
1427
                t2[0] = i + 1;
292✔
1428

1429
                for (size_t j = 0; j < yl; j++) {
3,043✔
1430
                        /* Substitution */
1431
                        t2[j+1] = t1[j] + (x[i] != y[j]);
2,751✔
1432

1433
                        /* Swap */
1434
                        if (i > 0 && j > 0 && x[i-1] == y[j] && x[i] == y[j-1] && t2[j+1] > t0[j-1] + 1)
2,751✔
1435
                                t2[j+1] = t0[j-1] + 1;
4✔
1436

1437
                        /* Deletion */
1438
                        if (t2[j+1] > t1[j+1] + 1)
2,751✔
1439
                                t2[j+1] = t1[j+1] + 1;
88✔
1440

1441
                        /* Insertion */
1442
                        if (t2[j+1] > t2[j] + 1)
2,751✔
1443
                                t2[j+1] = t2[j] + 1;
446✔
1444
                }
1445

1446
                size_t *dummy = t0;
1447
                t0 = t1;
1448
                t1 = t2;
1449
                t2 = dummy;
1450
        }
1451

1452
        return t1[yl];
68✔
1453
}
1454

1455
char* strrstr(const char *haystack, const char *needle) {
7,310✔
1456
        /* Like strstr() but returns the last rather than the first occurrence of "needle" in "haystack". */
1457

1458
        if (!haystack || !needle)
7,310✔
1459
                return NULL;
1460

1461
        /* Special case: for the empty string we return the very last possible occurrence, i.e. *after* the
1462
         * last char, not before. */
1463
        if (*needle == 0)
7,307✔
1464
                return strchr(haystack, 0);
2✔
1465

1466
        for (const char *p = strstr(haystack, needle), *q; p; p = q) {
7,315✔
1467
                q = strstr(p + 1, needle);
21✔
1468
                if (!q)
21✔
1469
                        return (char *) p;
1470
        }
1471
        return NULL;
1472
}
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