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

systemd / systemd / 15263807472

26 May 2025 08:53PM UTC coverage: 72.046% (-0.002%) from 72.048%
15263807472

push

github

yuwata
src/core/manager.c: log preset activity on first boot

This gives us a little more information about what units were enabled
or disabled on that first boot and will be useful for OS developers
tracking down the source of unit state.

An example with this enabled looks like:

```
NET: Registered PF_VSOCK protocol family
systemd[1]: Applying preset policy.
systemd[1]: Unit /etc/systemd/system/dnsmasq.service is masked, ignoring.
systemd[1]: Unit /etc/systemd/system/systemd-repart.service is masked, ignoring.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket'.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir.mount' → '/etc/systemd/system/var-mnt-workdir.mount'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir\x2dtmp.mount' → '/etc/systemd/system/var-mnt-workdir\x2dtmp.mount'.
systemd[1]: Created symlink '/etc/systemd/system/afterburn-sshkeys.target.requires/afterburn-sshkeys@core.service' → '/usr/lib/systemd/system/afterburn-sshkeys@.service'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket' → '/usr/lib/systemd/system/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket' → '/usr/lib/systemd/system/systemd-resolved-monitor.socket'.
systemd[1]: Populated /etc with preset unit settings.
```

Considering it only happens on first boot and not on every boot I think
the extra information is worth the extra verbosity in the logs just for
that boot.

5 of 6 new or added lines in 1 file covered. (83.33%)

5463 existing lines in 165 files now uncovered.

299151 of 415222 relevant lines covered (72.05%)

702386.45 hits per line

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

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

3
#include <stdio.h>
4

5
#include "alloc-util.h"
6
#include "escape.h"
7
#include "extract-word.h"
8
#include "glyph-util.h"
9
#include "gunicode.h"
10
#include "locale-util.h"
11
#include "log.h"
12
#include "memory-util.h"
13
#include "memstream-util.h"
14
#include "path-util.h"
15
#include "string-util.h"
16
#include "strv.h"
17
#include "terminal-util.h"
18
#include "utf8.h"
19

20
char* first_word(const char *s, const char *word) {
3,101,669✔
21
        assert(s);
3,101,669✔
22
        assert(word);
3,101,669✔
23

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

27
        if (isempty(word))
3,101,669✔
28
                return (char*) s;
29

30
        const char *p = startswith(s, word);
3,101,668✔
31
        if (!p)
3,101,668✔
32
                return NULL;
33
        if (*p == '\0')
105,421✔
34
                return (char*) p;
35

36
        const char *nw = skip_leading_chars(p, WHITESPACE);
105,420✔
37
        if (p == nw)
105,420✔
38
                return NULL;
1✔
39

40
        return (char*) nw;
41
}
42

43
char* strprepend(char **x, const char *s) {
5✔
44
        assert(x);
5✔
45

46
        if (isempty(s) && *x)
6✔
47
                return *x;
5✔
48

49
        char *p = strjoin(strempty(s), *x);
6✔
50
        if (!p)
5✔
51
                return NULL;
52

53
        free_and_replace(*x, p);
5✔
54
        return *x;
5✔
55
}
56

57
char* strextendn(char **x, const char *s, size_t l) {
27,488✔
58
        assert(x);
27,488✔
59
        assert(s || l == 0);
27,488✔
60

61
        if (l > 0)
27,488✔
62
                l = strnlen(s, l); /* ignore trailing noise */
26,855✔
63

64
        if (l > 0 || !*x) {
27,488✔
65
                size_t q;
27,178✔
66
                char *m;
27,178✔
67

68
                q = strlen_ptr(*x);
27,178✔
69
                m = realloc(*x, q + l + 1);
27,178✔
70
                if (!m)
27,178✔
71
                        return NULL;
72

73
                *mempcpy_typesafe(m + q, s, l) = 0;
27,178✔
74

75
                *x = m;
27,178✔
76
        }
77

78
        return *x;
27,488✔
79
}
80

81
char* strstrip(char *s) {
5,228,159✔
82
        if (!s)
5,228,159✔
83
                return NULL;
84

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

87
        return delete_trailing_chars(skip_leading_chars(s, WHITESPACE), WHITESPACE);
5,228,159✔
88
}
89

90
char* delete_chars(char *s, const char *bad) {
9✔
91
        char *f, *t;
9✔
92

93
        /* Drops all specified bad characters, regardless where in the string */
94

95
        if (!s)
9✔
96
                return NULL;
97

98
        if (!bad)
9✔
UNCOV
99
                bad = WHITESPACE;
×
100

101
        for (f = s, t = s; *f; f++) {
131✔
102
                if (strchr(bad, *f))
122✔
103
                        continue;
67✔
104

105
                *(t++) = *f;
55✔
106
        }
107

108
        *t = 0;
9✔
109

110
        return s;
9✔
111
}
112

113
char* delete_trailing_chars(char *s, const char *bad) {
6,677,045✔
114
        char *c = s;
6,677,045✔
115

116
        /* Drops all specified bad characters, at the end of the string */
117

118
        if (!s)
6,677,045✔
119
                return NULL;
120

121
        if (!bad)
6,677,045✔
122
                bad = WHITESPACE;
66,621✔
123

124
        for (char *p = s; *p; p++)
256,950,591✔
125
                if (!strchr(bad, *p))
250,273,546✔
126
                        c = p + 1;
239,973,887✔
127

128
        *c = 0;
6,677,045✔
129

130
        return s;
6,677,045✔
131
}
132

133
char* truncate_nl_full(char *s, size_t *ret_len) {
2,951,367✔
134
        size_t n;
2,951,367✔
135

136
        assert(s);
2,951,367✔
137

138
        n = strcspn(s, NEWLINE);
2,951,367✔
139
        s[n] = '\0';
2,951,367✔
140
        if (ret_len)
2,951,367✔
141
                *ret_len = n;
6✔
142
        return s;
2,951,367✔
143
}
144

145
char ascii_tolower(char x) {
4,202,836✔
146

147
        if (x >= 'A' && x <= 'Z')
4,202,836✔
148
                return x - 'A' + 'a';
174,626✔
149

150
        return x;
151
}
152

153
char ascii_toupper(char x) {
295,730✔
154

155
        if (x >= 'a' && x <= 'z')
295,730✔
156
                return x - 'a' + 'A';
185,931✔
157

158
        return x;
159
}
160

161
char* ascii_strlower(char *t) {
22,741✔
162
        assert(t);
22,741✔
163

164
        for (char *p = t; *p; p++)
145,914✔
165
                *p = ascii_tolower(*p);
123,173✔
166

167
        return t;
22,741✔
168
}
169

170
char* ascii_strupper(char *t) {
21,420✔
171
        assert(t);
21,420✔
172

173
        for (char *p = t; *p; p++)
316,996✔
174
                *p = ascii_toupper(*p);
295,576✔
175

176
        return t;
21,420✔
177
}
178

179
char* ascii_strlower_n(char *t, size_t n) {
713,484✔
180
        if (n <= 0)
713,484✔
181
                return t;
182

183
        for (size_t i = 0; i < n; i++)
2,357,043✔
184
                t[i] = ascii_tolower(t[i]);
1,643,879✔
185

186
        return t;
187
}
188

189
int ascii_strcasecmp_n(const char *a, const char *b, size_t n) {
620,398✔
190

191
        for (; n > 0; a++, b++, n--) {
1,701,082✔
192
                int x, y;
1,213,668✔
193

194
                x = (int) (uint8_t) ascii_tolower(*a);
1,213,668✔
195
                y = (int) (uint8_t) ascii_tolower(*b);
1,213,668✔
196

197
                if (x != y)
1,213,668✔
198
                        return x - y;
132,984✔
199
        }
200

201
        return 0;
202
}
203

204
int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) {
468,744✔
205
        int r;
468,744✔
206

207
        r = ascii_strcasecmp_n(a, b, MIN(n, m));
468,744✔
208
        if (r != 0)
468,744✔
209
                return r;
210

211
        return CMP(n, m);
435,349✔
212
}
213

214
bool chars_intersect(const char *a, const char *b) {
8,562✔
215
        /* Returns true if any of the chars in a are in b. */
216
        for (const char *p = a; *p; p++)
117,846✔
217
                if (strchr(b, *p))
109,297✔
218
                        return true;
219

220
        return false;
221
}
222

223
bool string_has_cc(const char *p, const char *ok) {
204,740✔
224
        assert(p);
204,740✔
225

226
        /*
227
         * Check if a string contains control characters. If 'ok' is
228
         * non-NULL it may be a string containing additional CCs to be
229
         * considered OK.
230
         */
231

232
        for (const char *t = p; *t; t++) {
1,710,297✔
233
                if (ok && strchr(ok, *t))
1,505,573✔
234
                        continue;
8✔
235

236
                if (char_is_cc(*t))
1,505,565✔
237
                        return true;
238
        }
239

240
        return false;
241
}
242

243
static int write_ellipsis(char *buf, bool unicode) {
4,225✔
244
        const char *s = glyph_full(GLYPH_ELLIPSIS, unicode);
4,225✔
245
        assert(strlen(s) == 3);
4,225✔
246
        memcpy(buf, s, 3);
4,225✔
247
        return 3;
4,225✔
248
}
249

250
static size_t ansi_sequence_length(const char *s, size_t len) {
6,271✔
251
        assert(s);
6,271✔
252

253
        if (len < 2)
6,271✔
254
                return 0;
255

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

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

262
                if (i == len)
895✔
263
                        return 0;
264

265
                while (s[i] >= 0x30 && s[i] <= 0x3F) /* Parameter bytes */
9,184✔
266
                        if (++i == len)
8,289✔
267
                                return 0;
268
                while (s[i] >= 0x20 && s[i] <= 0x2F) /* Intermediate bytes */
895✔
UNCOV
269
                        if (++i == len)
×
270
                                return 0;
271
                if (s[i] >= 0x40 && s[i] <= 0x7E) /* Final byte */
895✔
272
                        return i + 1;
895✔
273
                return 0;  /* Bad sequence */
274

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

278
        return 0;  /* Bad escape? */
279
}
280

281
static bool string_has_ansi_sequence(const char *s, size_t len) {
7,624✔
282
        const char *t = s;
7,624✔
283

284
        while ((t = memchr(s, 0x1B, len - (t - s))))
7,624✔
285
                if (ansi_sequence_length(t, len - (t - s)) > 0)
151✔
286
                        return true;
287
        return false;
288
}
289

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

293
        for (size_t i = length - 2; i > 0; i--) {  /* -2 because at least two bytes are needed */
3,409✔
294
                size_t slen = ansi_sequence_length(s + (i - 1), length - (i - 1));
3,407✔
295
                if (slen == 0)
3,407✔
296
                        continue;
3,146✔
297

298
                *ret_where = s + (i - 1);
261✔
299
                return slen;
261✔
300
        }
301

302
        *ret_where = NULL;
2✔
303
        return 0;
2✔
304
}
305

306
static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
4,133✔
307
        size_t x, need_space, suffix_len;
4,133✔
308
        char *t;
4,133✔
309

310
        assert(s);
4,133✔
311
        assert(percent <= 100);
4,133✔
312
        assert(new_length != SIZE_MAX);
4,133✔
313

314
        if (old_length <= new_length)
4,133✔
315
                return strndup(s, old_length);
1,642✔
316

317
        /* Special case short ellipsations */
318
        switch (new_length) {
2,491✔
319

UNCOV
320
        case 0:
×
UNCOV
321
                return strdup("");
×
322

323
        case 1:
65✔
324
                if (is_locale_utf8())
65✔
325
                        return strdup("…");
65✔
326
                else
327
                        return strdup(".");
×
328

329
        case 2:
35✔
330
                if (!is_locale_utf8())
35✔
UNCOV
331
                        return strdup("..");
×
332
                break;
333
        }
334

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

340
        t = new(char, new_length+3);
2,426✔
341
        if (!t)
2,426✔
342
                return NULL;
343

344
        assert(new_length >= need_space);
2,426✔
345

346
        x = ((new_length - need_space) * percent + 50) / 100;
2,426✔
347
        assert(x <= new_length - need_space);
2,426✔
348

349
        write_ellipsis(mempcpy(t, s, x), /* unicode = */ false);
2,426✔
350
        suffix_len = new_length - x - need_space;
2,426✔
351
        memcpy(t + x + 3, s + old_length - suffix_len, suffix_len);
2,426✔
352
        *(t + x + 3 + suffix_len) = '\0';
2,426✔
353

354
        return t;
2,426✔
355
}
356

357
char* ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
7,795✔
358
        size_t x, k, len, len2;
7,795✔
359
        const char *i, *j;
7,795✔
360
        int r;
7,795✔
361

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

374
        assert(s);
7,795✔
375
        assert(percent <= 100);
7,795✔
376

377
        if (new_length == SIZE_MAX)
7,795✔
UNCOV
378
                return strndup(s, old_length);
×
379

380
        if (new_length == 0)
7,795✔
381
                return strdup("");
171✔
382

383
        bool has_ansi_seq = string_has_ansi_sequence(s, old_length);
7,624✔
384

385
        /* If no multibyte characters or ANSI sequences, use ascii_ellipsize_mem for speed */
386
        if (!has_ansi_seq && ascii_is_valid_n(s, old_length))
7,624✔
387
                return ascii_ellipsize_mem(s, old_length, new_length, percent);
4,133✔
388

389
        x = (new_length - 1) * percent / 100;
3,491✔
390
        assert(x <= new_length - 1);
3,491✔
391

392
        k = 0;
393
        for (i = s; i < s + old_length; ) {
47,979✔
394
                size_t slen = has_ansi_seq ? ansi_sequence_length(i, old_length - (i - s)) : 0;
46,788✔
395
                if (slen > 0) {
984✔
396
                        i += slen;
221✔
397
                        continue;  /* ANSI sequences don't take up any space in output */
221✔
398
                }
399

400
                char32_t c;
46,567✔
401
                r = utf8_encoded_to_unichar(i, &c);
46,567✔
402
                if (r < 0)
46,567✔
UNCOV
403
                        return NULL;
×
404

405
                int w = unichar_iswide(c) ? 2 : 1;
46,567✔
406
                if (k + w > x)
46,567✔
407
                        break;
408

409
                k += w;
44,267✔
410
                i += r;
44,267✔
411
        }
412

413
        const char *ansi_start = s + old_length;
3,491✔
414
        size_t ansi_len = 0;
3,491✔
415

416
        for (const char *t = j = s + old_length; t > i && k < new_length; ) {
12,894✔
417
                char32_t c;
9,489✔
418
                int w;
9,489✔
419
                const char *tt;
9,489✔
420

421
                if (has_ansi_seq && ansi_start >= t)
9,489✔
422
                        /* Figure out the previous ANSI sequence, if any */
423
                        ansi_len = previous_ansi_sequence(s, t - s, &ansi_start);
263✔
424

425
                /* If the sequence extends all the way to the current position, skip it. */
426
                if (has_ansi_seq && ansi_len > 0 && ansi_start + ansi_len == t) {
9,489✔
427
                        t = ansi_start;
112✔
428
                        continue;
112✔
429
                }
430

431
                tt = utf8_prev_char(t);
9,377✔
432
                r = utf8_encoded_to_unichar(tt, &c);
9,377✔
433
                if (r < 0)
9,377✔
UNCOV
434
                        return NULL;
×
435

436
                w = unichar_iswide(c) ? 2 : 1;
9,377✔
437
                if (k + w > new_length)
9,377✔
438
                        break;
439

440
                k += w;
9,291✔
441
                j = t = tt;  /* j should always point to the first "real" character */
9,291✔
442
        }
443

444
        /* We don't actually need to ellipsize */
445
        if (i >= j)
3,491✔
446
                return memdup_suffix0(s, old_length);
1,716✔
447

448
        if (k >= new_length) {
1,775✔
449
                /* Make space for ellipsis, if required and possible. We know that the edge character is not
450
                 * part of an ANSI sequence (because then we'd skip it). If the last character we looked at
451
                 * was wide, we don't need to make space. */
452
                if (j < s + old_length)
1,689✔
453
                        j = utf8_next_char(j);
1,689✔
UNCOV
454
                else if (i > s)
×
UNCOV
455
                        i = utf8_prev_char(i);
×
456
        }
457

458
        len = i - s;
1,775✔
459
        len2 = s + old_length - j;
1,775✔
460

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

466
        char *e = new(char, alloc_len);
1,775✔
467
        if (!e)
1,775✔
468
                return NULL;
469

470
        memcpy_safe(e, s, len);
1,775✔
471
        write_ellipsis(e + len, /* unicode = */ true);
1,775✔
472

473
        char *dst = e + len + 3;
1,775✔
474

475
        if (has_ansi_seq)
1,775✔
476
                /* Copy over any ANSI sequences in full */
477
                for (const char *p = s + len; p < j; ) {
1,864✔
478
                        size_t slen = ansi_sequence_length(p, j - p);
1,729✔
479
                        if (slen > 0) {
1,729✔
480
                                dst = mempcpy(dst, p, slen);
262✔
481
                                p += slen;
262✔
482
                        } else
483
                                p = utf8_next_char(p);
1,467✔
484
                }
485

486
        memcpy_safe(dst, j, len2);
1,775✔
487
        dst[len2] = '\0';
1,775✔
488

489
        return e;
1,775✔
490
}
491

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

505
        size_t i = 0, last_char_width[4] = {}, k = 0;
43,433✔
506

507
        assert(buf);
43,433✔
508
        assert(len > 0); /* at least a terminating NUL */
43,433✔
509
        assert(s);
43,433✔
510

511
        for (;;) {
955,733✔
512
                char four[4];
499,583✔
513
                int w;
499,583✔
514

515
                if (*s == 0) /* terminating NUL detected? then we are done! */
499,583✔
516
                        goto done;
43,397✔
517

518
                w = cescape_char(*s, four);
456,186✔
519
                if (i + w + 1 > len) /* This character doesn't fit into the buffer anymore? In that case let's
456,186✔
520
                                      * ellipsize at the previous location */
521
                        break;
522

523
                /* OK, there was space, let's add this escaped character to the buffer */
524
                memcpy(buf + i, four, w);
456,150✔
525
                i += w;
456,150✔
526

527
                /* And remember its width in the ring buffer */
528
                last_char_width[k] = w;
456,150✔
529
                k = (k + 1) % 4;
456,150✔
530

531
                s++;
456,150✔
532
        }
533

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

538
                if (i + 4 <= len) /* nice, we reached our space goal */
81✔
539
                        break;
540

541
                k = k == 0 ? 3 : k - 1;
57✔
542
                if (last_char_width[k] == 0) /* bummer, we reached the beginning of the strings */
57✔
543
                        break;
544

545
                assert(i >= last_char_width[k]);
45✔
546
                i -= last_char_width[k];
45✔
547
        }
548

549
        if (i + 4 <= len) /* yay, enough space */
36✔
550
                i += write_ellipsis(buf + i, /* unicode = */ false);
24✔
551
        else if (i + 3 <= len) { /* only space for ".." */
12✔
552
                buf[i++] = '.';
4✔
553
                buf[i++] = '.';
4✔
554
        } else if (i + 2 <= len) /* only space for a single "." */
8✔
555
                buf[i++] = '.';
4✔
556
        else
557
                assert(i + 1 <= len);
4✔
558

559
done:
4✔
560
        buf[i] = '\0';
43,433✔
561
        return buf;
43,433✔
562
}
563

564
char* strshorten(char *s, size_t l) {
24,968✔
565
        assert(s);
24,968✔
566

567
        if (l >= SIZE_MAX-1) /* Would not change anything */
24,968✔
568
                return s;
569

570
        if (strnlen(s, l+1) > l)
24,966✔
571
                s[l] = 0;
27✔
572

573
        return s;
574
}
575

576
int strgrowpad0(char **s, size_t l) {
16✔
577
        size_t sz;
16✔
578

579
        assert(s);
16✔
580

581
        if (*s) {
16✔
582
                sz = strlen(*s) + 1;
16✔
583
                if (sz >= l) /* never shrink */
16✔
584
                        return 0;
585
        } else
586
                sz = 0;
587

588
        char *q = realloc(*s, l);
16✔
589
        if (!q)
16✔
590
                return -ENOMEM;
591

592
        *s = q;
16✔
593

594
        memzero(*s + sz, l - sz);
16✔
595
        return 0;
596
}
597

598
char* strreplace(const char *text, const char *old_string, const char *new_string) {
4,024✔
599
        size_t l, old_len, new_len;
4,024✔
600
        char *t, *ret = NULL;
4,024✔
601
        const char *f;
4,024✔
602

603
        assert(old_string);
4,024✔
604
        assert(new_string);
4,024✔
605

606
        if (!text)
4,024✔
607
                return NULL;
4,024✔
608

609
        old_len = strlen(old_string);
4,023✔
610
        new_len = strlen(new_string);
4,023✔
611

612
        l = strlen(text);
4,023✔
613
        if (!GREEDY_REALLOC(ret, l+1))
4,023✔
614
                return NULL;
615

616
        f = text;
4,023✔
617
        t = ret;
4,023✔
618
        while (*f) {
112,725✔
619
                size_t d, nl;
108,702✔
620

621
                if (!startswith(f, old_string)) {
108,702✔
622
                        *(t++) = *(f++);
106,088✔
623
                        continue;
106,088✔
624
                }
625

626
                d = t - ret;
2,614✔
627
                nl = l - old_len + new_len;
2,614✔
628

629
                if (!GREEDY_REALLOC(ret, nl + 1))
2,614✔
UNCOV
630
                        return mfree(ret);
×
631

632
                l = nl;
2,614✔
633
                t = ret + d;
2,614✔
634

635
                t = stpcpy(t, new_string);
2,614✔
636
                f += old_len;
2,614✔
637
        }
638

639
        *t = 0;
4,023✔
640
        return ret;
4,023✔
641
}
642

643
static void advance_offsets(
386✔
644
                ssize_t diff,
645
                size_t offsets[2], /* note: we can't use [static 2] here, since this may be NULL */
646
                size_t shift[static 2],
647
                size_t size) {
648

649
        if (!offsets)
386✔
650
                return;
651

652
        assert(shift);
376✔
653

654
        if ((size_t) diff < offsets[0])
376✔
UNCOV
655
                shift[0] += size;
×
656
        if ((size_t) diff < offsets[1])
376✔
UNCOV
657
                shift[1] += size;
×
658
}
659

660
char* strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
98,892✔
661
        const char *begin = NULL;
98,892✔
662
        enum {
98,892✔
663
                STATE_OTHER,
664
                STATE_ESCAPE,
665
                STATE_CSI,
666
                STATE_OSC,
667
                STATE_OSC_CLOSING,
668
        } state = STATE_OTHER;
98,892✔
669
        _cleanup_(memstream_done) MemStream m = {};
98,892✔
670
        size_t isz, shift[2] = {}, n_carriage_returns = 0;
98,892✔
671
        FILE *f;
98,892✔
672

673
        assert(ibuf);
98,892✔
674
        assert(*ibuf);
98,892✔
675

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

690
        isz = _isz ? *_isz : strlen(*ibuf);
98,892✔
691

692
        /* Note we turn off internal locking on f for performance reasons. It's safe to do so since we
693
         * created f here and it doesn't leave our scope. */
694
        f = memstream_init(&m);
98,892✔
695
        if (!f)
98,892✔
696
                return NULL;
697

698
        for (const char *i = *ibuf; i < *ibuf + isz + 1; i++) {
11,209,227✔
699

700
                bool eot = i >= *ibuf + isz;
11,110,335✔
701

702
                switch (state) {
11,110,335✔
703

704
                case STATE_OTHER:
11,110,252✔
705
                        if (eot)
11,110,252✔
706
                                break;
707

708
                        if (*i == '\r') {
11,011,360✔
709
                                n_carriage_returns++;
7✔
710
                                break;
7✔
711
                        } else if (*i == '\n')
11,011,353✔
712
                                /* Ignore carriage returns before new line */
713
                                n_carriage_returns = 0;
41✔
714
                        for (; n_carriage_returns > 0; n_carriage_returns--)
11,011,355✔
715
                                fputc('\r', f);
2✔
716

717
                        if (*i == '\x1B')
11,011,353✔
718
                                state = STATE_ESCAPE;
719
                        else if (*i == '\t') {
11,011,337✔
720
                                fputs("        ", f);
381✔
721
                                advance_offsets(i - *ibuf, highlight, shift, 7);
381✔
722
                        } else
723
                                fputc(*i, f);
11,010,956✔
724

725
                        break;
726

727
                case STATE_ESCAPE:
16✔
728
                        assert(n_carriage_returns == 0);
16✔
729

730
                        if (eot) {
16✔
UNCOV
731
                                fputc('\x1B', f);
×
UNCOV
732
                                advance_offsets(i - *ibuf, highlight, shift, 1);
×
733
                                break;
734
                        } else if (*i == '[') { /* ANSI CSI */
16✔
735
                                state = STATE_CSI;
13✔
736
                                begin = i + 1;
13✔
737
                        } else if (*i == ']') { /* ANSI OSC */
3✔
738
                                state = STATE_OSC;
3✔
739
                                begin = i + 1;
3✔
740
                        } else {
UNCOV
741
                                fputc('\x1B', f);
×
UNCOV
742
                                fputc(*i, f);
×
UNCOV
743
                                advance_offsets(i - *ibuf, highlight, shift, 1);
×
UNCOV
744
                                state = STATE_OTHER;
×
745
                        }
746

747
                        break;
748

749
                case STATE_CSI:
41✔
750
                        assert(n_carriage_returns == 0);
41✔
751

752
                        if (eot || !strchr("01234567890;m", *i)) { /* EOT or invalid chars in sequence */
41✔
753
                                fputc('\x1B', f);
5✔
754
                                fputc('[', f);
5✔
755
                                advance_offsets(i - *ibuf, highlight, shift, 2);
5✔
756
                                state = STATE_OTHER;
5✔
757
                                i = begin-1;
5✔
758
                        } else if (*i == 'm')
36✔
759
                                state = STATE_OTHER;
8✔
760

761
                        break;
762

763
                case STATE_OSC:
24✔
764
                        assert(n_carriage_returns == 0);
24✔
765

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

780
                        break;
781

782
                case STATE_OSC_CLOSING:
2✔
783
                        if (eot || *i != '\x5c') { /* EOT or incomplete two-byte ST in sequence */
2✔
UNCOV
784
                                fputc('\x1B', f);
×
UNCOV
785
                                fputc(']', f);
×
UNCOV
786
                                advance_offsets(i - *ibuf, highlight, shift, 2);
×
UNCOV
787
                                state = STATE_OTHER;
×
UNCOV
788
                                i = begin-1;
×
789
                        } else if (*i == '\x5c')
790
                                state = STATE_OTHER;
791

792
                        break;
793
                }
794
        }
795

796
        char *obuf;
98,892✔
797
        if (memstream_finalize(&m, &obuf, _isz) < 0)
98,892✔
798
                return NULL;
799

800
        free_and_replace(*ibuf, obuf);
98,892✔
801

802
        if (highlight) {
98,892✔
803
                highlight[0] += shift[0];
98,883✔
804
                highlight[1] += shift[1];
98,883✔
805
        }
806

807
        return *ibuf;
98,892✔
808
}
809

810
char* strextend_with_separator_internal(char **x, const char *separator, ...) {
15,833,529✔
811
        _cleanup_free_ char *buffer = NULL;
15,833,529✔
812
        size_t f, l, l_separator;
15,833,529✔
813
        bool need_separator;
15,833,529✔
814
        char *nr, *p;
15,833,529✔
815
        va_list ap;
15,833,529✔
816

817
        if (!x)
15,833,529✔
818
                x = &buffer;
14,887,411✔
819

820
        l = f = strlen_ptr(*x);
15,833,529✔
821

822
        need_separator = !isempty(*x);
15,833,529✔
823
        l_separator = strlen_ptr(separator);
15,833,529✔
824

825
        va_start(ap, separator);
15,833,529✔
826
        for (const char *t;;) {
59,575,994✔
827
                size_t n;
59,575,994✔
828

829
                t = va_arg(ap, const char *);
59,575,994✔
830
                if (!t)
59,575,994✔
831
                        break;
832
                if (t == POINTER_MAX)
43,742,465✔
833
                        continue;
3✔
834

835
                n = strlen(t);
43,742,462✔
836

837
                if (need_separator)
43,742,462✔
838
                        n += l_separator;
28,445,402✔
839

840
                if (n >= SIZE_MAX - l) {
43,742,462✔
UNCOV
841
                        va_end(ap);
×
UNCOV
842
                        return NULL;
×
843
                }
844

845
                l += n;
43,742,462✔
846
                need_separator = true;
43,742,462✔
847
        }
848
        va_end(ap);
15,833,529✔
849

850
        need_separator = !isempty(*x);
15,833,529✔
851

852
        nr = realloc(*x, GREEDY_ALLOC_ROUND_UP(l+1));
15,833,529✔
853
        if (!nr)
15,833,529✔
854
                return NULL;
855

856
        *x = nr;
15,833,529✔
857
        p = nr + f;
15,833,529✔
858

859
        va_start(ap, separator);
15,833,529✔
860
        for (;;) {
59,575,994✔
861
                const char *t;
59,575,994✔
862

863
                t = va_arg(ap, const char *);
59,575,994✔
864
                if (!t)
59,575,994✔
865
                        break;
866
                if (t == POINTER_MAX)
43,742,465✔
867
                        continue;
3✔
868

869
                if (need_separator && separator)
43,742,462✔
870
                        p = stpcpy(p, separator);
147,057✔
871

872
                p = stpcpy(p, t);
43,742,462✔
873

874
                need_separator = true;
43,742,462✔
875
        }
876
        va_end(ap);
15,833,529✔
877

878
        assert(p == nr + l);
15,833,529✔
879
        *p = 0;
15,833,529✔
880

881
        /* If no buffer to extend was passed in return the start of the buffer */
882
        if (buffer)
15,833,529✔
883
                return TAKE_PTR(buffer);
14,887,411✔
884

885
        /* Otherwise we extended the buffer: return the end */
886
        return p;
887
}
888

889
int strextendf_with_separator(char **x, const char *separator, const char *format, ...) {
12,474✔
890
        size_t m, a, l_separator;
12,474✔
891
        va_list ap;
12,474✔
892
        int l;
12,474✔
893

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

898
        assert(x);
12,474✔
899
        assert(format);
12,474✔
900

901
        l_separator = isempty(*x) ? 0 : strlen_ptr(separator);
21,608✔
902

903
        /* Let's try to use the allocated buffer, if there's room at the end still. Otherwise let's extend by 64 chars. */
904
        if (*x) {
12,474✔
905
                m = strlen(*x);
9,134✔
906
                a = MALLOC_SIZEOF_SAFE(*x);
9,134✔
907
                assert(a >= m + 1);
9,134✔
908
        } else
909
                m = a = 0;
910

911
        if (a - m < 17 + l_separator) { /* if there's less than 16 chars space, then enlarge the buffer first */
12,474✔
912
                char *n;
8,594✔
913

914
                if (_unlikely_(l_separator > SIZE_MAX - 64)) /* overflow check #1 */
8,594✔
915
                        return -ENOMEM;
12,474✔
916
                if (_unlikely_(m > SIZE_MAX - 64 - l_separator)) /* overflow check #2 */
8,594✔
917
                        return -ENOMEM;
918

919
                n = realloc(*x, m + 64 + l_separator);
8,594✔
920
                if (!n)
8,594✔
921
                        return -ENOMEM;
922

923
                *x = n;
8,594✔
924
                a = MALLOC_SIZEOF_SAFE(*x);
8,594✔
925
        }
926

927
        /* Now, let's try to format the string into it */
928
        memcpy_safe(*x + m, separator, l_separator);
12,474✔
929
        va_start(ap, format);
12,474✔
930
        l = vsnprintf(*x + m + l_separator, a - m - l_separator, format, ap);
12,474✔
931
        va_end(ap);
12,474✔
932

933
        assert(l >= 0);
12,474✔
934

935
        if ((size_t) l < a - m - l_separator) {
12,474✔
936
                char *n;
12,167✔
937

938
                /* Nice! This worked. We are done. But first, let's return the extra space we don't
939
                 * need. This should be a cheap operation, since we only lower the allocation size here,
940
                 * never increase. */
941
                n = realloc(*x, m + (size_t) l + l_separator + 1);
12,167✔
942
                if (n)
12,167✔
943
                        *x = n;
12,167✔
944
        } else {
945
                char *n;
307✔
946

947
                /* Wasn't enough. Then let's allocate exactly what we need. */
948

949
                if (_unlikely_((size_t) l > SIZE_MAX - (l_separator + 1))) /* overflow check #1 */
307✔
UNCOV
950
                        goto oom;
×
951
                if (_unlikely_(m > SIZE_MAX - ((size_t) l + l_separator + 1))) /* overflow check #2 */
307✔
UNCOV
952
                        goto oom;
×
953

954
                a = m + (size_t) l + l_separator + 1;
307✔
955
                n = realloc(*x, a);
307✔
956
                if (!n)
307✔
957
                        goto oom;
×
958
                *x = n;
307✔
959

960
                va_start(ap, format);
307✔
961
                l = vsnprintf(*x + m + l_separator, a - m - l_separator, format, ap);
307✔
962
                va_end(ap);
307✔
963

964
                assert((size_t) l < a - m - l_separator);
307✔
965
        }
966

967
        return 0;
968

UNCOV
969
oom:
×
970
        /* truncate the bytes added after memcpy_safe() again */
UNCOV
971
        (*x)[m] = 0;
×
UNCOV
972
        return -ENOMEM;
×
973
}
974

975
char* strrep(const char *s, unsigned n) {
11✔
976
        char *r, *p;
11✔
977
        size_t l;
11✔
978

979
        assert(s);
11✔
980

981
        l = strlen(s);
11✔
982
        p = r = malloc(l * n + 1);
11✔
983
        if (!r)
11✔
984
                return NULL;
985

986
        for (unsigned i = 0; i < n; i++)
40,337✔
987
                p = stpcpy(p, s);
40,326✔
988

989
        *p = 0;
11✔
990
        return r;
11✔
991
}
992

993
int split_pair(const char *s, const char *sep, char **ret_first, char **ret_second) {
1,572✔
994
        assert(s);
1,572✔
995
        assert(!isempty(sep));
1,572✔
996
        assert(ret_first);
1,572✔
997
        assert(ret_second);
1,572✔
998

999
        const char *x = strstr(s, sep);
1,572✔
1000
        if (!x)
1,572✔
1001
                return -EINVAL;
1,572✔
1002

1003
        _cleanup_free_ char *a = strndup(s, x - s);
258✔
1004
        if (!a)
258✔
1005
                return -ENOMEM;
1006

1007
        _cleanup_free_ char *b = strdup(x + strlen(sep));
258✔
1008
        if (!b)
258✔
1009
                return -ENOMEM;
1010

1011
        *ret_first = TAKE_PTR(a);
258✔
1012
        *ret_second = TAKE_PTR(b);
258✔
1013
        return 0;
258✔
1014
}
1015

1016
int free_and_strdup(char **p, const char *s) {
1,881,381✔
1017
        char *t;
1,881,381✔
1018

1019
        assert(p);
1,881,381✔
1020

1021
        /* Replaces a string pointer with a strdup()ed new string,
1022
         * possibly freeing the old one. */
1023

1024
        if (streq_ptr(*p, s))
1,881,381✔
1025
                return 0;
1,881,381✔
1026

1027
        if (s) {
1,537,517✔
1028
                t = strdup(s);
1,531,353✔
1029
                if (!t)
1,531,353✔
1030
                        return -ENOMEM;
1031
        } else
1032
                t = NULL;
1033

1034
        free_and_replace(*p, t);
1,537,517✔
1035

1036
        return 1;
1,537,517✔
1037
}
1038

1039
int free_and_strdup_warn(char **p, const char *s) {
45,740✔
1040
        int r;
45,740✔
1041

1042
        r = free_and_strdup(p, s);
45,740✔
1043
        if (r < 0)
45,740✔
UNCOV
1044
                return log_oom();
×
1045
        return r;
1046
}
1047

1048
int free_and_strndup(char **p, const char *s, size_t l) {
66,336✔
1049
        char *t;
66,336✔
1050

1051
        assert(p);
66,336✔
1052
        assert(s || l == 0);
66,336✔
1053

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

1057
        if (!*p && !s)
66,336✔
1058
                return 0;
66,336✔
1059

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

1063
        if (s) {
31,405✔
1064
                t = strndup(s, l);
31,464✔
1065
                if (!t)
31,464✔
1066
                        return -ENOMEM;
1067
        } else
1068
                t = NULL;
1069

1070
        free_and_replace(*p, t);
31,465✔
1071
        return 1;
31,465✔
1072
}
1073

1074
int strdup_to_full(char **ret, const char *src) {
663,656✔
1075
        if (!src) {
663,656✔
1076
                if (ret)
202,323✔
1077
                        *ret = NULL;
202,322✔
1078

1079
                return 0;
202,323✔
1080
        } else {
1081
                if (ret) {
461,333✔
1082
                        char *t = strdup(src);
461,331✔
1083
                        if (!t)
461,331✔
1084
                                return -ENOMEM;
1085
                        *ret = t;
461,331✔
1086
                }
1087

1088
                return 1;
461,333✔
1089
        }
1090
};
1091

1092
bool string_is_safe(const char *p) {
145,357✔
1093
        if (!p)
145,357✔
1094
                return false;
1095

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

1098
        for (const char *t = p; *t; t++) {
1,651,244✔
1099
                if (*t > 0 && *t < ' ') /* no control characters */
1,505,900✔
1100
                        return false;
1101

1102
                if (strchr(QUOTES "\\\x7f", *t))
1,505,892✔
1103
                        return false;
1104
        }
1105

1106
        return true;
1107
}
1108

UNCOV
1109
bool string_is_safe_ascii(const char *p) {
×
UNCOV
1110
        return ascii_is_valid(p) && string_is_safe(p);
×
1111
}
1112

1113
char* str_realloc(char *p) {
28,672✔
1114
        /* Reallocate *p to actual size. Ignore failure, and return the original string on error. */
1115

1116
        if (!p)
28,672✔
1117
                return NULL;
1118

1119
        return realloc(p, strlen(p) + 1) ?: p;
28,672✔
1120
}
1121

1122
char* string_erase(char *x) {
110✔
1123
        if (!x)
110✔
1124
                return NULL;
1125

1126
        /* A delicious drop of snake-oil! To be called on memory where we stored passphrases or so, after we
1127
         * used them. */
1128
        explicit_bzero_safe(x, strlen(x));
110✔
1129
        return x;
110✔
1130
}
1131

1132
int string_truncate_lines(const char *s, size_t n_lines, char **ret) {
210✔
1133
        const char *p = s, *e = s;
210✔
1134
        bool truncation_applied = false;
210✔
1135
        char *copy;
210✔
1136
        size_t n = 0;
210✔
1137

1138
        assert(s);
210✔
1139

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

1144
        for (;;) {
436✔
1145
                size_t k;
323✔
1146

1147
                k = strcspn(p, "\n");
323✔
1148

1149
                if (p[k] == 0) {
323✔
1150
                        if (k == 0) /* final empty line */
178✔
1151
                                break;
1152

1153
                        if (n >= n_lines) /* above threshold */
153✔
1154
                                break;
1155

1156
                        e = p + k; /* last line to include */
138✔
1157
                        break;
138✔
1158
                }
1159

1160
                assert(p[k] == '\n');
145✔
1161

1162
                if (n >= n_lines)
145✔
1163
                        break;
1164

1165
                if (k > 0)
113✔
1166
                        e = p + k;
90✔
1167

1168
                p += k + 1;
113✔
1169
                n++;
113✔
1170
        }
1171

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

1180
                copy = strndup(s, e - s);
68✔
1181
        }
1182
        if (!copy)
210✔
1183
                return -ENOMEM;
1184

1185
        *ret = copy;
210✔
1186
        return truncation_applied;
210✔
1187
}
1188

1189
int string_extract_line(const char *s, size_t i, char **ret) {
139,477✔
1190
        const char *p = s;
139,477✔
1191
        size_t c = 0;
139,477✔
1192

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

1199
        for (;;) {
324,907✔
1200
                const char *q;
232,192✔
1201

1202
                q = strchr(p, '\n');
232,192✔
1203
                if (i == c) {
232,192✔
1204
                        /* The line we are looking for! */
1205

1206
                        if (q) {
136,507✔
1207
                                char *m;
3,105✔
1208

1209
                                m = strndup(p, q - p);
3,105✔
1210
                                if (!m)
3,105✔
1211
                                        return -ENOMEM;
1212

1213
                                *ret = m;
3,105✔
1214
                                return !isempty(q + 1); /* More coming? */
6,210✔
1215
                        } else
1216
                                /* Tell the caller to use the input string if equal */
1217
                                return strdup_to(ret, p != s ? p : NULL);
265,534✔
1218
                }
1219

1220
                if (!q)
95,685✔
1221
                        /* No more lines, return empty line */
1222
                        return strdup_to(ret, "");
2,970✔
1223

1224
                p = q + 1;
92,715✔
1225
                c++;
92,715✔
1226
        }
1227
}
1228

1229
int string_contains_word_strv(const char *string, const char *separators, char * const *words, const char **ret_word) {
71✔
1230
        /* In the default mode with no separators specified, we split on whitespace and coalesce separators. */
1231
        const ExtractFlags flags = separators ? EXTRACT_DONT_COALESCE_SEPARATORS : 0;
71✔
1232
        const char *found = NULL;
71✔
1233
        int r;
196✔
1234

1235
        for (;;) {
321✔
1236
                _cleanup_free_ char *w = NULL;
125✔
1237

1238
                r = extract_first_word(&string, &w, separators, flags);
196✔
1239
                if (r < 0)
196✔
UNCOV
1240
                        return r;
×
1241
                if (r == 0)
196✔
1242
                        break;
1243

1244
                found = strv_find(words, w);
146✔
1245
                if (found)
146✔
1246
                        break;
1247
        }
1248

1249
        if (ret_word)
71✔
1250
                *ret_word = found;
7✔
1251
        return !!found;
71✔
1252
}
1253

1254
bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok) {
8,149✔
1255
        if (!s1 && !s2)
8,149✔
1256
                return true;
1257
        if (!s1 || !s2)
8,148✔
1258
                return false;
1259

1260
        if (!ok)
8,146✔
1261
                ok = WHITESPACE;
13✔
1262

1263
        for (; *s1 && *s2; s1++, s2++)
15,322✔
1264
                if (*s1 != *s2)
9,347✔
1265
                        break;
1266

1267
        return in_charset(s1, ok) && in_charset(s2, ok);
10,323✔
1268
}
1269

1270
char* string_replace_char(char *str, char old_char, char new_char) {
401,383✔
1271
        assert(str);
401,383✔
1272
        assert(old_char != '\0');
401,383✔
1273
        assert(new_char != '\0');
401,383✔
1274
        assert(old_char != new_char);
401,383✔
1275

1276
        for (char *p = strchr(str, old_char); p; p = strchr(p + 1, old_char))
405,956✔
1277
                *p = new_char;
4,573✔
1278

1279
        return str;
401,383✔
1280
}
1281

1282
int make_cstring(const char *s, size_t n, MakeCStringMode mode, char **ret) {
1,521✔
1283
        char *b;
1,521✔
1284

1285
        assert(s || n == 0);
1,521✔
1286
        assert(mode >= 0);
1,521✔
1287
        assert(mode < _MAKE_CSTRING_MODE_MAX);
1,521✔
1288

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

1292
        if (n == 0) {
1,521✔
1293
                if (mode == MAKE_CSTRING_REQUIRE_TRAILING_NUL)
20✔
1294
                        return -EINVAL;
1295

1296
                if (!ret)
19✔
1297
                        return 0;
1298

1299
                b = new0(char, 1);
19✔
1300
        } else {
1301
                const char *nul;
1,501✔
1302

1303
                nul = memchr(s, 0, n);
1,501✔
1304
                if (nul) {
1,501✔
1305
                        if (nul < s + n - 1 || /* embedded NUL? */
19✔
1306
                            mode == MAKE_CSTRING_REFUSE_TRAILING_NUL)
1307
                                return -EINVAL;
1308

1309
                        n--;
1310
                } else if (mode == MAKE_CSTRING_REQUIRE_TRAILING_NUL)
1,482✔
1311
                        return -EINVAL;
1312

1313
                if (!ret)
1,486✔
1314
                        return 0;
1315

1316
                b = memdup_suffix0(s, n);
1,486✔
1317
        }
1318
        if (!b)
1,505✔
1319
                return -ENOMEM;
1320

1321
        *ret = b;
1,505✔
1322
        return 0;
1,505✔
1323
}
1324

1325
size_t strspn_from_end(const char *str, const char *accept) {
369,963✔
1326
        size_t n = 0;
369,963✔
1327

1328
        if (isempty(str))
369,963✔
1329
                return 0;
1330

1331
        if (isempty(accept))
369,960✔
1332
                return 0;
1333

1334
        for (const char *p = str + strlen(str); p > str && strchr(accept, p[-1]); p--)
735,713✔
1335
                n++;
365,754✔
1336

1337
        return n;
1338
}
1339

UNCOV
1340
char* strdupspn(const char *a, const char *accept) {
×
UNCOV
1341
        if (isempty(a) || isempty(accept))
×
UNCOV
1342
                return strdup("");
×
1343

UNCOV
1344
        return strndup(a, strspn(a, accept));
×
1345
}
1346

1347
char* strdupcspn(const char *a, const char *reject) {
44,274✔
1348
        if (isempty(a))
44,274✔
1349
                return strdup("");
×
1350
        if (isempty(reject))
44,274✔
1351
                return strdup(a);
×
1352

1353
        return strndup(a, strcspn(a, reject));
44,274✔
1354
}
1355

1356
char* find_line_startswith(const char *haystack, const char *needle) {
610✔
1357
        char *p;
610✔
1358

1359
        assert(haystack);
610✔
1360
        assert(needle);
610✔
1361

1362
        /* Finds the first line in 'haystack' that starts with the specified string. Returns a pointer to the
1363
         * first character after it */
1364

1365
        p = strstr(haystack, needle);
610✔
1366
        if (!p)
610✔
1367
                return NULL;
1368

1369
        if (p > haystack)
242✔
1370
                while (p[-1] != '\n') {
18✔
1371
                        p = strstr(p + 1, needle);
9✔
1372
                        if (!p)
9✔
1373
                                return NULL;
1374
                }
1375

1376
        return p + strlen(needle);
241✔
1377
}
1378

UNCOV
1379
char* find_line(const char *haystack, const char *needle) {
×
UNCOV
1380
        char *p;
×
1381

UNCOV
1382
        assert(haystack);
×
UNCOV
1383
        assert(needle);
×
1384

1385
        /* Finds the first line in 'haystack' that match the specified string. Returns a pointer to the
1386
         * beginning of the line */
1387

UNCOV
1388
        p = find_line_startswith(haystack, needle);
×
1389
        if (!p)
×
1390
                return NULL;
1391

UNCOV
1392
        if (*p == 0 || strchr(NEWLINE, *p))
×
UNCOV
1393
                return p - strlen(needle);
×
1394

1395
        return NULL;
1396
}
1397

UNCOV
1398
char* find_line_after(const char *haystack, const char *needle) {
×
1399
        char *p;
×
1400

UNCOV
1401
        assert(haystack);
×
UNCOV
1402
        assert(needle);
×
1403

1404
        /* Finds the first line in 'haystack' that match the specified string. Returns a pointer to the
1405
         * next line after it */
1406

UNCOV
1407
        p = find_line_startswith(haystack, needle);
×
1408
        if (!p)
×
1409
                return NULL;
1410

UNCOV
1411
        if (*p == 0)
×
1412
                return p;
UNCOV
1413
        if (strchr(NEWLINE, *p))
×
1414
                return p + 1;
×
1415

1416
        return NULL;
1417
}
1418

1419
bool version_is_valid(const char *s) {
4,023✔
1420
        if (isempty(s))
4,023✔
1421
                return false;
1422

1423
        if (!filename_part_is_valid(s))
4,021✔
1424
                return false;
1425

1426
        /* This is a superset of the characters used by semver. We additionally allow "," and "_". */
1427
        if (!in_charset(s, ALPHANUMERICAL ".,_-+"))
4,021✔
UNCOV
1428
                return false;
×
1429

1430
        return true;
1431
}
1432

1433
bool version_is_valid_versionspec(const char *s) {
62✔
1434
        if (!filename_part_is_valid(s))
62✔
1435
                return false;
1436

1437
        if (!in_charset(s, ALPHANUMERICAL "-.~^"))
62✔
UNCOV
1438
                return false;
×
1439

1440
        return true;
1441
}
1442

1443
ssize_t strlevenshtein(const char *x, const char *y) {
90✔
1444
        _cleanup_free_ size_t *t0 = NULL, *t1 = NULL, *t2 = NULL;
180✔
1445
        size_t xl, yl;
90✔
1446

1447
        /* This is inspired from the Linux kernel's Levenshtein implementation */
1448

1449
        if (streq_ptr(x, y))
90✔
1450
                return 0;
1451

1452
        xl = strlen_ptr(x);
86✔
1453
        if (xl > SSIZE_MAX)
85✔
1454
                return -E2BIG;
1455

1456
        yl = strlen_ptr(y);
86✔
1457
        if (yl > SSIZE_MAX)
85✔
1458
                return -E2BIG;
1459

1460
        if (isempty(x))
86✔
1461
                return yl;
3✔
1462
        if (isempty(y))
83✔
1463
                return xl;
1✔
1464

1465
        t0 = new0(size_t, yl + 1);
82✔
1466
        if (!t0)
82✔
1467
                return -ENOMEM;
1468
        t1 = new0(size_t, yl + 1);
82✔
1469
        if (!t1)
82✔
1470
                return -ENOMEM;
1471
        t2 = new0(size_t, yl + 1);
82✔
1472
        if (!t2)
82✔
1473
                return -ENOMEM;
1474

1475
        for (size_t i = 0; i <= yl; i++)
905✔
1476
                t1[i] = i;
823✔
1477

1478
        for (size_t i = 0; i < xl; i++) {
453✔
1479
                t2[0] = i + 1;
371✔
1480

1481
                for (size_t j = 0; j < yl; j++) {
4,228✔
1482
                        /* Substitution */
1483
                        t2[j+1] = t1[j] + (x[i] != y[j]);
3,857✔
1484

1485
                        /* Swap */
1486
                        if (i > 0 && j > 0 && x[i-1] == y[j] && x[i] == y[j-1] && t2[j+1] > t0[j-1] + 1)
3,857✔
1487
                                t2[j+1] = t0[j-1] + 1;
10✔
1488

1489
                        /* Deletion */
1490
                        if (t2[j+1] > t1[j+1] + 1)
3,857✔
1491
                                t2[j+1] = t1[j+1] + 1;
141✔
1492

1493
                        /* Insertion */
1494
                        if (t2[j+1] > t2[j] + 1)
3,857✔
1495
                                t2[j+1] = t2[j] + 1;
638✔
1496
                }
1497

1498
                size_t *dummy = t0;
1499
                t0 = t1;
1500
                t1 = t2;
1501
                t2 = dummy;
1502
        }
1503

1504
        return t1[yl];
82✔
1505
}
1506

1507
char* strrstr(const char *haystack, const char *needle) {
7,197✔
1508
        /* Like strstr() but returns the last rather than the first occurrence of "needle" in "haystack". */
1509

1510
        if (!haystack || !needle)
7,197✔
1511
                return NULL;
1512

1513
        /* Special case: for the empty string we return the very last possible occurrence, i.e. *after* the
1514
         * last char, not before. */
1515
        if (*needle == 0)
7,194✔
1516
                return strchr(haystack, 0);
2✔
1517

1518
        for (const char *p = strstr(haystack, needle), *q; p; p = q) {
7,202✔
1519
                q = strstr(p + 1, needle);
25✔
1520
                if (!q)
25✔
1521
                        return (char *) p;
1522
        }
1523
        return NULL;
1524
}
1525

1526
size_t str_common_prefix(const char *a, const char *b) {
16✔
1527
        assert(a);
16✔
1528
        assert(b);
16✔
1529

1530
        /* Returns the length of the common prefix of the two specified strings, or SIZE_MAX in case the
1531
         * strings are fully identical. */
1532

1533
        for (size_t n = 0;; n++) {
39✔
1534
                char c = a[n];
55✔
1535
                if (c != b[n])
55✔
1536
                        return n;
1537
                if (c == 0)
42✔
1538
                        return SIZE_MAX;
1539
        }
1540
}
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