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

systemd / systemd / 15150396955

20 May 2025 10:32PM UTC coverage: 72.047% (-0.2%) from 72.25%
15150396955

push

github

web-flow
resolved: add new "DNS Delegate" concepts (#34368)

Various long standing issues (at least: #5573 #14159 #20485 #21260
#24532 #32022 #18056) have been asking for a way to delegate DNS
resolution of specific domains to very specific DNS servers.

This PR goes a major step towards that goal by adding a new concept "DNS
Delegate" which allows to configure just that. Basically, this adds a
third kind of DNS scope to resolved's logic: besides the per-link and
global DNS scopes there are now also "delegate" scopes, which can be
created by dropping in a new file /etc/systemd/dns-delegate/*.conf. They
carry DNS= and Domains= lines just like the global setting or what the
per-link configuration can carry.

And they are consulted the same way as link DNS scopes are considered,
following the same routing rules.

This allows to configure these DNS delegates statically via drop-in
files as mentioned, and only adds the most basic functionality. Later on
we might want to extend this:

1. Allow dynamic creation of DNS delegates via IPC with lifecycle bound
to IPC client (usecase: installing a DNS delegate that routes traffic to
some DNS-over-TLS server once basic setup is complete).
2. Allow configuration of protocol details per delegate the same way
this is currently allowed per-link.
3. Instead of strictly using DNS as delegation protocol, support an
alternative varlink based protocol (without retransmission problems and
so on) that systemd-machined and similar can implement.

This PR is not complete yet. Lacks docs and tests. Seems to work fine in
my local tests however.

Fixes: #5573
Fixes: #18056
Fixes: #20485

470 of 586 new or added lines in 14 files covered. (80.2%)

3358 existing lines in 54 files now uncovered.

299091 of 415134 relevant lines covered (72.05%)

703065.7 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 <errno.h>
4
#include <stdarg.h>
5
#include <stdint.h>
6
#include <stdio.h>
7
#include <stdlib.h>
8

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

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

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

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

37
        const char *p = startswith(s, word);
3,133,651✔
38
        if (!p)
3,133,651✔
39
                return NULL;
40
        if (*p == '\0')
105,855✔
41
                return (char*) p;
42

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

47
        return (char*) nw;
48
}
49

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

115
        *t = 0;
9✔
116

117
        return s;
9✔
118
}
119

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

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

125
        if (!s)
6,679,120✔
126
                return NULL;
127

128
        if (!bad)
6,679,120✔
129
                bad = WHITESPACE;
66,154✔
130

131
        for (char *p = s; *p; p++)
257,004,141✔
132
                if (!strchr(bad, *p))
250,325,021✔
133
                        c = p + 1;
240,028,343✔
134

135
        *c = 0;
6,679,120✔
136

137
        return s;
6,679,120✔
138
}
139

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

143
        assert(s);
2,958,381✔
144

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

152
char ascii_tolower(char x) {
4,087,338✔
153

154
        if (x >= 'A' && x <= 'Z')
4,087,338✔
155
                return x - 'A' + 'a';
172,252✔
156

157
        return x;
158
}
159

160
char ascii_toupper(char x) {
296,285✔
161

162
        if (x >= 'a' && x <= 'z')
296,285✔
163
                return x - 'a' + 'A';
185,631✔
164

165
        return x;
166
}
167

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

171
        for (char *p = t; *p; p++)
145,602✔
172
                *p = ascii_tolower(*p);
122,891✔
173

174
        return t;
22,711✔
175
}
176

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

180
        for (char *p = t; *p; p++)
317,491✔
181
                *p = ascii_toupper(*p);
296,131✔
182

183
        return t;
21,360✔
184
}
185

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

190
        for (size_t i = 0; i < n; i++)
2,268,951✔
191
                t[i] = ascii_tolower(t[i]);
1,588,125✔
192

193
        return t;
194
}
195

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

198
        for (; n > 0; a++, b++, n--) {
1,652,012✔
199
                int x, y;
1,183,973✔
200

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

204
                if (x != y)
1,183,973✔
205
                        return x - y;
127,921✔
206
        }
207

208
        return 0;
209
}
210

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

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

218
        return CMP(n, m);
417,286✔
219
}
220

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

227
        return false;
228
}
229

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

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

239
        for (const char *t = p; *t; t++) {
1,600,269✔
240
                if (ok && strchr(ok, *t))
1,403,434✔
241
                        continue;
8✔
242

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

247
        return false;
248
}
249

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

361
        return t;
2,413✔
362
}
363

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

496
        return e;
1,769✔
497
}
498

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

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

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

518
        for (;;) {
961,875✔
519
                char four[4];
502,784✔
520
                int w;
502,784✔
521

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

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

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

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

538
                s++;
459,091✔
539
        }
540

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

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

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

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

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

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

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

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

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

580
        return s;
581
}
582

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

586
        assert(s);
16✔
587

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

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

599
        *s = q;
16✔
600

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

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

610
        assert(old_string);
4,002✔
611
        assert(new_string);
4,002✔
612

613
        if (!text)
4,002✔
614
                return NULL;
4,002✔
615

616
        old_len = strlen(old_string);
4,001✔
617
        new_len = strlen(new_string);
4,001✔
618

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

623
        f = text;
4,001✔
624
        t = ret;
4,001✔
625
        while (*f) {
112,016✔
626
                size_t d, nl;
108,015✔
627

628
                if (!startswith(f, old_string)) {
108,015✔
629
                        *(t++) = *(f++);
105,389✔
630
                        continue;
105,389✔
631
                }
632

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

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

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

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

646
        *t = 0;
4,001✔
647
        return ret;
4,001✔
648
}
649

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

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

659
        assert(shift);
382✔
660

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

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

680
        assert(ibuf);
94,621✔
681
        assert(*ibuf);
94,621✔
682

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

697
        isz = _isz ? *_isz : strlen(*ibuf);
94,621✔
698

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

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

707
                bool eot = i >= *ibuf + isz;
10,426,337✔
708

709
                switch (state) {
10,426,337✔
710

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

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

724
                        if (*i == '\x1B')
10,331,626✔
725
                                state = STATE_ESCAPE;
726
                        else if (*i == '\t') {
10,331,610✔
727
                                fputs("        ", f);
387✔
728
                                advance_offsets(i - *ibuf, highlight, shift, 7);
387✔
729
                        } else
730
                                fputc(*i, f);
10,331,223✔
731

732
                        break;
733

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

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

754
                        break;
755

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

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

768
                        break;
769

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

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

787
                        break;
788

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

799
                        break;
800
                }
801
        }
802

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

807
        free_and_replace(*ibuf, obuf);
94,621✔
808

809
        if (highlight) {
94,621✔
810
                highlight[0] += shift[0];
94,612✔
811
                highlight[1] += shift[1];
94,612✔
812
        }
813

814
        return *ibuf;
94,621✔
815
}
816

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

824
        if (!x)
15,886,200✔
825
                x = &buffer;
14,948,620✔
826

827
        l = f = strlen_ptr(*x);
15,886,200✔
828

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

832
        va_start(ap, separator);
15,886,200✔
833
        for (const char *t;;) {
59,794,891✔
834
                size_t n;
59,794,891✔
835

836
                t = va_arg(ap, const char *);
59,794,891✔
837
                if (!t)
59,794,891✔
838
                        break;
839
                if (t == POINTER_MAX)
43,908,691✔
840
                        continue;
3✔
841

842
                n = strlen(t);
43,908,688✔
843

844
                if (need_separator)
43,908,688✔
845
                        n += l_separator;
28,552,684✔
846

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

852
                l += n;
43,908,688✔
853
                need_separator = true;
43,908,688✔
854
        }
855
        va_end(ap);
15,886,200✔
856

857
        need_separator = !isempty(*x);
15,886,200✔
858

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

863
        *x = nr;
15,886,200✔
864
        p = nr + f;
15,886,200✔
865

866
        va_start(ap, separator);
15,886,200✔
867
        for (;;) {
59,794,891✔
868
                const char *t;
59,794,891✔
869

870
                t = va_arg(ap, const char *);
59,794,891✔
871
                if (!t)
59,794,891✔
872
                        break;
873
                if (t == POINTER_MAX)
43,908,691✔
874
                        continue;
3✔
875

876
                if (need_separator && separator)
43,908,688✔
877
                        p = stpcpy(p, separator);
146,891✔
878

879
                p = stpcpy(p, t);
43,908,688✔
880

881
                need_separator = true;
43,908,688✔
882
        }
883
        va_end(ap);
15,886,200✔
884

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

888
        /* If no buffer to extend was passed in return the start of the buffer */
889
        if (buffer)
15,886,200✔
890
                return TAKE_PTR(buffer);
14,948,620✔
891

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

974
        return 0;
975

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

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

986
        assert(s);
11✔
987

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

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

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

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

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

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

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

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

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

1026
        assert(p);
1,875,858✔
1027

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

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

1034
        if (s) {
1,529,703✔
1035
                t = strdup(s);
1,523,484✔
1036
                if (!t)
1,523,484✔
1037
                        return -ENOMEM;
1038
        } else
1039
                t = NULL;
1040

1041
        free_and_replace(*p, t);
1,529,703✔
1042

1043
        return 1;
1,529,703✔
1044
}
1045

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

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

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

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

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

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

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

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

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

1081
int strdup_to_full(char **ret, const char *src) {
665,163✔
1082
        if (!src) {
665,163✔
1083
                if (ret)
201,788✔
1084
                        *ret = NULL;
201,787✔
1085

1086
                return 0;
201,788✔
1087
        } else {
1088
                if (ret) {
463,375✔
1089
                        char *t = strdup(src);
463,373✔
1090
                        if (!t)
463,373✔
1091
                                return -ENOMEM;
1092
                        *ret = t;
463,373✔
1093
                }
1094

1095
                return 1;
463,375✔
1096
        }
1097
};
1098

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

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

1105
        for (const char *t = p; *t; t++) {
1,657,672✔
1106
                if (*t > 0 && *t < ' ') /* no control characters */
1,511,901✔
1107
                        return false;
1108

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

1113
        return true;
1114
}
1115

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

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

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

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

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

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

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

1145
        assert(s);
210✔
1146

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1206
        for (;;) {
324,333✔
1207
                const char *q;
231,658✔
1208

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

1213
                        if (q) {
136,035✔
1214
                                char *m;
3,083✔
1215

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1283
        for (char *p = strchr(str, old_char); p; p = strchr(p + 1, old_char))
406,034✔
1284
                *p = new_char;
4,594✔
1285

1286
        return str;
401,440✔
1287
}
1288

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

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

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

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

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

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

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

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

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

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

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

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

1335
        if (isempty(str))
370,185✔
1336
                return 0;
1337

1338
        if (isempty(accept))
370,182✔
1339
                return 0;
1340

1341
        for (const char *p = str + strlen(str); p > str && strchr(accept, p[-1]); p--)
736,261✔
1342
                n++;
366,080✔
1343

1344
        return n;
1345
}
1346

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

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

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

1360
        return strndup(a, strcspn(a, reject));
44,537✔
1361
}
1362

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

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

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

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

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

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

UNCOV
1386
char* find_line(const char *haystack, const char *needle) {
×
UNCOV
1387
        char *p;
×
1388

UNCOV
1389
        assert(haystack);
×
UNCOV
1390
        assert(needle);
×
1391

1392
        /* Finds the first line in 'haystack' that match the specified string. Returns a pointer to the
1393
         * beginning of the line */
1394

1395
        p = find_line_startswith(haystack, needle);
×
UNCOV
1396
        if (!p)
×
1397
                return NULL;
1398

UNCOV
1399
        if (*p == 0 || strchr(NEWLINE, *p))
×
UNCOV
1400
                return p - strlen(needle);
×
1401

1402
        return NULL;
1403
}
1404

1405
char* find_line_after(const char *haystack, const char *needle) {
×
UNCOV
1406
        char *p;
×
1407

UNCOV
1408
        assert(haystack);
×
UNCOV
1409
        assert(needle);
×
1410

1411
        /* Finds the first line in 'haystack' that match the specified string. Returns a pointer to the
1412
         * next line after it */
1413

UNCOV
1414
        p = find_line_startswith(haystack, needle);
×
UNCOV
1415
        if (!p)
×
1416
                return NULL;
1417

UNCOV
1418
        if (*p == 0)
×
1419
                return p;
UNCOV
1420
        if (strchr(NEWLINE, *p))
×
UNCOV
1421
                return p + 1;
×
1422

1423
        return NULL;
1424
}
1425

1426
bool version_is_valid(const char *s) {
4,023✔
1427
        if (isempty(s))
4,023✔
1428
                return false;
1429

1430
        if (!filename_part_is_valid(s))
4,021✔
1431
                return false;
1432

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

1437
        return true;
1438
}
1439

1440
bool version_is_valid_versionspec(const char *s) {
62✔
1441
        if (!filename_part_is_valid(s))
62✔
1442
                return false;
1443

1444
        if (!in_charset(s, ALPHANUMERICAL "-.~^"))
62✔
UNCOV
1445
                return false;
×
1446

1447
        return true;
1448
}
1449

1450
ssize_t strlevenshtein(const char *x, const char *y) {
90✔
1451
        _cleanup_free_ size_t *t0 = NULL, *t1 = NULL, *t2 = NULL;
180✔
1452
        size_t xl, yl;
90✔
1453

1454
        /* This is inspired from the Linux kernel's Levenshtein implementation */
1455

1456
        if (streq_ptr(x, y))
90✔
1457
                return 0;
1458

1459
        xl = strlen_ptr(x);
86✔
1460
        if (xl > SSIZE_MAX)
85✔
1461
                return -E2BIG;
1462

1463
        yl = strlen_ptr(y);
86✔
1464
        if (yl > SSIZE_MAX)
85✔
1465
                return -E2BIG;
1466

1467
        if (isempty(x))
86✔
1468
                return yl;
3✔
1469
        if (isempty(y))
83✔
1470
                return xl;
1✔
1471

1472
        t0 = new0(size_t, yl + 1);
82✔
1473
        if (!t0)
82✔
1474
                return -ENOMEM;
1475
        t1 = new0(size_t, yl + 1);
82✔
1476
        if (!t1)
82✔
1477
                return -ENOMEM;
1478
        t2 = new0(size_t, yl + 1);
82✔
1479
        if (!t2)
82✔
1480
                return -ENOMEM;
1481

1482
        for (size_t i = 0; i <= yl; i++)
905✔
1483
                t1[i] = i;
823✔
1484

1485
        for (size_t i = 0; i < xl; i++) {
453✔
1486
                t2[0] = i + 1;
371✔
1487

1488
                for (size_t j = 0; j < yl; j++) {
4,228✔
1489
                        /* Substitution */
1490
                        t2[j+1] = t1[j] + (x[i] != y[j]);
3,857✔
1491

1492
                        /* Swap */
1493
                        if (i > 0 && j > 0 && x[i-1] == y[j] && x[i] == y[j-1] && t2[j+1] > t0[j-1] + 1)
3,857✔
1494
                                t2[j+1] = t0[j-1] + 1;
10✔
1495

1496
                        /* Deletion */
1497
                        if (t2[j+1] > t1[j+1] + 1)
3,857✔
1498
                                t2[j+1] = t1[j+1] + 1;
141✔
1499

1500
                        /* Insertion */
1501
                        if (t2[j+1] > t2[j] + 1)
3,857✔
1502
                                t2[j+1] = t2[j] + 1;
638✔
1503
                }
1504

1505
                size_t *dummy = t0;
1506
                t0 = t1;
1507
                t1 = t2;
1508
                t2 = dummy;
1509
        }
1510

1511
        return t1[yl];
82✔
1512
}
1513

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

1517
        if (!haystack || !needle)
7,197✔
1518
                return NULL;
1519

1520
        /* Special case: for the empty string we return the very last possible occurrence, i.e. *after* the
1521
         * last char, not before. */
1522
        if (*needle == 0)
7,194✔
1523
                return strchr(haystack, 0);
2✔
1524

1525
        for (const char *p = strstr(haystack, needle), *q; p; p = q) {
7,202✔
1526
                q = strstr(p + 1, needle);
25✔
1527
                if (!q)
25✔
1528
                        return (char *) p;
1529
        }
1530
        return NULL;
1531
}
1532

1533
size_t str_common_prefix(const char *a, const char *b) {
16✔
1534
        assert(a);
16✔
1535
        assert(b);
16✔
1536

1537
        /* Returns the length of the common prefix of the two specified strings, or SIZE_MAX in case the
1538
         * strings are fully identical. */
1539

1540
        for (size_t n = 0;; n++) {
39✔
1541
                char c = a[n];
55✔
1542
                if (c != b[n])
55✔
1543
                        return n;
1544
                if (c == 0)
42✔
1545
                        return SIZE_MAX;
1546
        }
1547
}
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