• 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

95.57
/src/basic/strv.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fnmatch.h>
4
#include <stdio.h>
5

6
#include "alloc-util.h"
7
#include "env-util.h"
8
#include "escape.h"
9
#include "extract-word.h"
10
#include "fileio.h"
11
#include "gunicode.h"
12
#include "hashmap.h"
13
#include "log.h"
14
#include "memory-util.h"
15
#include "sort-util.h"
16
#include "string-util.h"
17
#include "strv.h"
18
#include "utf8.h"
19

20
char* strv_find(char * const *l, const char *name) {
23,521,454✔
21
        assert(name);
23,521,454✔
22

23
        STRV_FOREACH(i, l)
196,763,408✔
24
                if (streq(*i, name))
175,387,969✔
25
                        return *i;
26

27
        return NULL;
28
}
29

30
char* strv_find_case(char * const *l, const char *name) {
1,283,378✔
31
        assert(name);
1,283,378✔
32

33
        STRV_FOREACH(i, l)
5,349,022✔
34
                if (strcaseeq(*i, name))
4,682,452✔
35
                        return *i;
36

37
        return NULL;
38
}
39

40
char* strv_find_prefix(char * const *l, const char *name) {
322✔
41
        assert(name);
322✔
42

43
        STRV_FOREACH(i, l)
3,907✔
44
                if (startswith(*i, name))
3,669✔
45
                        return *i;
46

47
        return NULL;
48
}
49

50
char* strv_find_startswith(char * const *l, const char *name) {
981,030✔
51
        assert(name);
981,030✔
52

53
        /* Like strv_find_prefix, but actually returns only the
54
         * suffix, not the whole item */
55

56
        STRV_FOREACH(i, l) {
1,982,715✔
57
                char *e;
1,003,172✔
58

59
                e = startswith(*i, name);
1,003,172✔
60
                if (e)
1,003,172✔
61
                        return e;
62
        }
63

64
        return NULL;
65
}
66

67
static char* strv_find_closest_prefix(char * const *l, const char *name) {
29✔
68
        size_t best_distance = SIZE_MAX;
29✔
69
        char *best = NULL;
29✔
70

71
        assert(name);
29✔
72

73
        STRV_FOREACH(s, l) {
196✔
74
                char *e = startswith(*s, name);
167✔
75
                if (!e)
167✔
76
                        continue;
106✔
77

78
                size_t n = strlen(e);
61✔
79
                if (n < best_distance) {
61✔
80
                        best_distance = n;
16✔
81
                        best = *s;
16✔
82
                }
83
        }
84

85
        return best;
29✔
86
}
87

88
static char* strv_find_closest_by_levenshtein(char * const *l, const char *name) {
14✔
89
        ssize_t best_distance = SSIZE_MAX;
14✔
90
        char *best = NULL;
14✔
91

92
        assert(name);
14✔
93

94
        STRV_FOREACH(i, l) {
86✔
95
                ssize_t distance;
72✔
96

97
                distance = strlevenshtein(*i, name);
72✔
98
                if (distance < 0) {
72✔
UNCOV
99
                        log_debug_errno(distance, "Failed to determine Levenshtein distance between %s and %s: %m", *i, name);
×
UNCOV
100
                        return NULL;
×
101
                }
102

103
                if (distance > 5) /* If the distance is just too far off, don't make a bad suggestion */
72✔
104
                        continue;
39✔
105

106
                if (distance < best_distance) {
33✔
107
                        best_distance = distance;
15✔
108
                        best = *i;
15✔
109
                }
110
        }
111

112
        return best;
113
}
114

115
char* strv_find_closest(char * const *l, const char *name) {
29✔
116
        assert(name);
29✔
117

118
        /* Be more helpful to the user, and give a hint what the user might have wanted to type. We search
119
         * with two mechanisms: a simple prefix match and – if that didn't yield results –, a Levenshtein
120
         * word distance based match. */
121

122
        char *found = strv_find_closest_prefix(l, name);
29✔
123
        if (found)
29✔
124
                return found;
125

126
        return strv_find_closest_by_levenshtein(l, name);
14✔
127
}
128

129
char* strv_find_first_field(char * const *needles, char * const *haystack) {
6,583✔
130
        STRV_FOREACH(k, needles) {
17,094✔
131
                char *value = strv_env_pairs_get((char **)haystack, *k);
14,876✔
132
                if (value)
14,876✔
133
                        return value;
134
        }
135

136
        return NULL;
137
}
138

139
char** strv_free(char **l) {
5,910,692✔
140
        STRV_FOREACH(k, l)
22,404,786✔
141
                free(*k);
16,494,094✔
142

143
        return mfree(l);
5,910,692✔
144
}
145

146
char** strv_free_erase(char **l) {
63,001✔
147
        STRV_FOREACH(i, l)
70,009✔
148
                erase_and_freep(i);
7,008✔
149

150
        return mfree(l);
63,001✔
151
}
152

153
void strv_free_many(char ***strvs, size_t n) {
40✔
154
        assert(strvs || n == 0);
40✔
155

156
        FOREACH_ARRAY (i, strvs, n)
100✔
157
                strv_free(*i);
60✔
158

159
        free(strvs);
40✔
160
}
40✔
161

162
char** strv_copy_n(char * const *l, size_t m) {
153,199✔
163
        _cleanup_strv_free_ char **result = NULL;
153,199✔
164
        char **k;
153,199✔
165

166
        result = new(char*, MIN(strv_length(l), m) + 1);
153,199✔
167
        if (!result)
153,199✔
168
                return NULL;
169

170
        k = result;
171
        STRV_FOREACH(i, l) {
447,311✔
172
                if (m == 0)
294,118✔
173
                        break;
174

175
                *k = strdup(*i);
294,112✔
176
                if (!*k)
294,112✔
177
                        return NULL;
178
                k++;
294,112✔
179

180
                if (m != SIZE_MAX)
294,112✔
181
                        m--;
84✔
182
        }
183

184
        *k = NULL;
153,199✔
185
        return TAKE_PTR(result);
153,199✔
186
}
187

188
int strv_copy_unless_empty(char * const *l, char ***ret) {
6,362✔
189
        assert(ret);
6,362✔
190

191
        if (strv_isempty(l)) {
6,362✔
192
                *ret = NULL;
6,321✔
193
                return 0;
6,321✔
194
        }
195

196
        char **copy = strv_copy(l);
41✔
197
        if (!copy)
41✔
198
                return -ENOMEM;
199

200
        *ret = TAKE_PTR(copy);
41✔
201
        return 1;
41✔
202
}
203

204
size_t strv_length(char * const *l) {
9,710,591✔
205
        size_t n = 0;
9,710,591✔
206

207
        STRV_FOREACH(i, l)
291,216,523✔
208
                n++;
281,505,932✔
209

210
        return n;
9,710,591✔
211
}
212

213
char** strv_new_ap(const char *x, va_list ap) {
32,399✔
214
        _cleanup_strv_free_ char **a = NULL;
32,399✔
215
        size_t n = 0, i = 0;
32,399✔
216
        va_list aq;
32,399✔
217

218
        /* As a special trick we ignore all listed strings that equal
219
         * STRV_IGNORE. This is supposed to be used with the
220
         * STRV_IFNOTNULL() macro to include possibly NULL strings in
221
         * the string list. */
222

223
        va_copy(aq, ap);
32,399✔
224
        for (const char *s = x; s; s = va_arg(aq, const char*)) {
182,848✔
225
                if (s == STRV_IGNORE)
150,449✔
226
                        continue;
5,004✔
227

228
                n++;
145,445✔
229
        }
230
        va_end(aq);
32,399✔
231

232
        a = new(char*, n+1);
32,399✔
233
        if (!a)
32,399✔
234
                return NULL;
235

236
        for (const char *s = x; s; s = va_arg(ap, const char*)) {
182,848✔
237
                if (s == STRV_IGNORE)
150,449✔
238
                        continue;
5,004✔
239

240
                a[i] = strdup(s);
145,445✔
241
                if (!a[i])
145,445✔
242
                        return NULL;
243

244
                i++;
145,445✔
245
        }
246

247
        a[i] = NULL;
32,399✔
248

249
        return TAKE_PTR(a);
32,399✔
250
}
251

252
char** strv_new_internal(const char *x, ...) {
25,199✔
253
        char **r;
25,199✔
254
        va_list ap;
25,199✔
255

256
        va_start(ap, x);
25,199✔
257
        r = strv_new_ap(x, ap);
25,199✔
258
        va_end(ap);
25,199✔
259

260
        return r;
25,199✔
261
}
262

263
int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
37,136✔
264
        size_t p, q, i = 0;
37,136✔
265

266
        assert(a);
37,136✔
267

268
        q = strv_length(b);
37,136✔
269
        if (q == 0)
37,136✔
270
                return 0;
271

272
        p = strv_length(*a);
34,410✔
273
        if (p >= SIZE_MAX - q)
34,410✔
274
                return -ENOMEM;
275

276
        char **t = reallocarray(*a, GREEDY_ALLOC_ROUND_UP(p + q + 1), sizeof(char *));
34,410✔
277
        if (!t)
34,410✔
278
                return -ENOMEM;
279

280
        t[p] = NULL;
34,410✔
281
        *a = t;
34,410✔
282

283
        STRV_FOREACH(s, b) {
74,813✔
284
                if (filter_duplicates && strv_contains(t, *s))
40,403✔
285
                        continue;
73✔
286

287
                t[p+i] = strdup(*s);
40,330✔
288
                if (!t[p+i])
40,330✔
UNCOV
289
                        goto rollback;
×
290

291
                i++;
40,330✔
292
                t[p+i] = NULL;
40,330✔
293
        }
294

295
        assert(i <= q);
34,410✔
296

297
        return (int) i;
34,410✔
298

UNCOV
299
rollback:
×
UNCOV
300
        free_many_charp(t + p, i);
×
UNCOV
301
        t[p] = NULL;
×
302
        return -ENOMEM;
×
303
}
304

305
int strv_extend_strv_consume(char ***a, char **b, bool filter_duplicates) {
117,525✔
306
        _cleanup_strv_free_ char **b_consume = b;
117,525✔
307
        size_t p, q, i;
117,525✔
308

309
        assert(a);
117,525✔
310

311
        q = strv_length(b);
117,525✔
312
        if (q == 0)
117,525✔
313
                return 0;
314

315
        p = strv_length(*a);
63,681✔
316
        if (p == 0) {
63,681✔
317
                strv_free_and_replace(*a, b_consume);
53,516✔
318

319
                if (filter_duplicates)
53,516✔
320
                        strv_uniq(*a);
16,946✔
321

322
                return strv_length(*a);
53,516✔
323
        }
324

325
        if (p >= SIZE_MAX - q)
10,165✔
326
                return -ENOMEM;
327

328
        char **t = reallocarray(*a, GREEDY_ALLOC_ROUND_UP(p + q + 1), sizeof(char *));
10,165✔
329
        if (!t)
10,165✔
330
                return -ENOMEM;
331

332
        t[p] = NULL;
10,165✔
333
        *a = t;
10,165✔
334

335
        if (!filter_duplicates) {
10,165✔
336
                *mempcpy_typesafe(t + p, b, q) = NULL;
9,636✔
337
                i = q;
9,636✔
338
        } else {
339
                i = 0;
340

341
                STRV_FOREACH(s, b) {
1,540✔
342
                        if (strv_contains(t, *s)) {
1,011✔
343
                                free(*s);
173✔
344
                                continue;
173✔
345
                        }
346

347
                        t[p+i] = *s;
838✔
348

349
                        i++;
838✔
350
                        t[p+i] = NULL;
838✔
351
                }
352
        }
353

354
        assert(i <= q);
10,165✔
355

356
        b_consume = mfree(b_consume);
10,165✔
357

358
        return (int) i;
10,165✔
359
}
360

361
int strv_extend_strv_biconcat(char ***a, const char *prefix, const char* const *b, const char *suffix) {
83,114✔
362
        int r;
83,114✔
363

364
        assert(a);
83,114✔
365

366
        STRV_FOREACH(s, b) {
414,273✔
367
                char *v;
331,159✔
368

369
                v = strjoin(strempty(prefix), *s, suffix);
662,228✔
370
                if (!v)
331,159✔
371
                        return -ENOMEM;
372

373
                r = strv_consume(a, v);
331,159✔
374
                if (r < 0)
331,159✔
375
                        return r;
376
        }
377

378
        return 0;
379
}
380

381
int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags) {
626,744✔
382
        _cleanup_strv_free_ char **l = NULL;
626,744✔
383
        size_t n;
626,744✔
384
        int r;
626,744✔
385

386
        assert(s);
626,744✔
387

388
        /* Special version of strv_split_full() that splits on newlines and
389
         * suppresses an empty string at the end. */
390

391
        r = strv_split_full(&l, s, NEWLINE, flags);
626,744✔
392
        if (r < 0)
626,744✔
393
                return r;
394

395
        n = strv_length(l);
626,744✔
396
        if (n > 0 && isempty(l[n - 1])) {
626,744✔
UNCOV
397
                l[n - 1] = mfree(l[n - 1]);
×
UNCOV
398
                n--;
×
399
        }
400

401
        *ret = TAKE_PTR(l);
626,744✔
402
        return n;
626,744✔
403
}
404

405
char** strv_split_newlines(const char *s) {
52,922✔
406
        char **ret;
52,922✔
407

408
        if (strv_split_newlines_full(&ret, s, 0) < 0)
52,922✔
409
                return NULL;
52,922✔
410

411
        return ret;
52,922✔
412
}
413

414
int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags) {
890,158✔
415
        _cleanup_strv_free_ char **l = NULL;
890,158✔
416
        size_t n = 0;
890,158✔
417
        int r;
890,158✔
418

419
        assert(t);
890,158✔
420
        assert(s);
890,158✔
421

422
        for (;;) {
18,564,146✔
423
                _cleanup_free_ char *word = NULL;
8,837,003✔
424

425
                r = extract_first_word(&s, &word, separators, flags);
9,727,152✔
426
                if (r < 0)
9,727,152✔
427
                        return r;
428
                if (r == 0)
9,727,143✔
429
                        break;
430

431
                if (!GREEDY_REALLOC(l, n + 2))
8,836,994✔
432
                        return -ENOMEM;
433

434
                l[n++] = TAKE_PTR(word);
8,836,994✔
435
                l[n] = NULL;
8,836,994✔
436
        }
437

438
        if (!l) {
890,149✔
439
                l = new0(char*, 1);
75,710✔
440
                if (!l)
75,710✔
441
                        return -ENOMEM;
442
        }
443

444
        *t = TAKE_PTR(l);
890,149✔
445

446
        return (int) n;
890,149✔
447
}
448

449
char** strv_split(const char *s, const char *separators) {
14,928✔
450
        char **ret;
14,928✔
451

452
        if (strv_split_full(&ret, s, separators, EXTRACT_RETAIN_ESCAPE) < 0)
14,928✔
453
                return NULL;
14,928✔
454

455
        return ret;
14,928✔
456
}
457

458
int strv_split_and_extend_full(char ***t, const char *s, const char *separators, bool filter_duplicates, ExtractFlags flags) {
827✔
459
        char **l;
827✔
460
        int r;
827✔
461

462
        assert(t);
827✔
463
        assert(s);
827✔
464

465
        r = strv_split_full(&l, s, separators, flags);
827✔
466
        if (r < 0)
827✔
467
                return r;
827✔
468

469
        r = strv_extend_strv_consume(t, l, filter_duplicates);
827✔
470
        if (r < 0)
827✔
471
                return r;
472

473
        return (int) strv_length(*t);
827✔
474
}
475

476
int strv_split_and_extend(char ***t, const char *s, const char *separators, bool filter_duplicates) {
825✔
477
        return strv_split_and_extend_full(t, s, separators, filter_duplicates, 0);
825✔
478
}
479

480
int strv_split_colon_pairs(char ***t, const char *s) {
4✔
481
        _cleanup_strv_free_ char **l = NULL;
4✔
482
        size_t n = 0;
4✔
483
        int r;
4✔
484

485
        assert(t);
4✔
486
        assert(s);
4✔
487

488
        for (;;) {
16✔
489
                _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL, *second_or_empty = NULL;
16✔
490

491
                r = extract_first_word(&s, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
16✔
492
                if (r < 0)
16✔
493
                        return r;
494
                if (r == 0)
16✔
495
                        break;
496

497
                const char *p = tuple;
13✔
498
                r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS,
13✔
499
                                       &first, &second);
500
                if (r < 0)
13✔
501
                        return r;
502
                if (r == 0)
13✔
UNCOV
503
                        continue;
×
504
                /* Enforce that at most 2 colon-separated words are contained in each group */
505
                if (!isempty(p))
14✔
506
                        return -EINVAL;
507

508
                second_or_empty = strdup(strempty(second));
12✔
509
                if (!second_or_empty)
12✔
510
                        return -ENOMEM;
511

512
                if (!GREEDY_REALLOC(l, n + 3))
12✔
513
                        return -ENOMEM;
514

515
                l[n++] = TAKE_PTR(first);
12✔
516
                l[n++] = TAKE_PTR(second_or_empty);
12✔
517

518
                l[n] = NULL;
12✔
519
        }
520

521
        if (!l) {
3✔
UNCOV
522
                l = new0(char*, 1);
×
UNCOV
523
                if (!l)
×
524
                        return -ENOMEM;
525
        }
526

527
        *t = TAKE_PTR(l);
3✔
528

529
        return (int) n;
3✔
530
}
531

532
char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separator) {
62,760✔
533
        char *r, *e;
62,760✔
534
        size_t n, k, m;
62,760✔
535

536
        if (!separator)
62,760✔
537
                separator = " ";
2✔
538

539
        k = strlen(separator);
62,760✔
540
        m = strlen_ptr(prefix);
62,760✔
541

542
        if (escape_separator) /* If the separator was multi-char, we wouldn't know how to escape it. */
62,760✔
543
                assert(k == 1);
362✔
544

545
        n = 0;
546
        STRV_FOREACH(s, l) {
259,355✔
547
                if (s != l)
196,595✔
548
                        n += k;
134,623✔
549

550
                bool needs_escaping = escape_separator && strchr(*s, *separator);
196,595✔
551

552
                n += m + strlen(*s) * (1 + needs_escaping);
196,595✔
553
        }
554

555
        r = new(char, n+1);
62,760✔
556
        if (!r)
62,760✔
557
                return NULL;
558

559
        e = r;
560
        STRV_FOREACH(s, l) {
259,355✔
561
                if (s != l)
196,595✔
562
                        e = stpcpy(e, separator);
134,623✔
563

564
                if (prefix)
196,595✔
565
                        e = stpcpy(e, prefix);
17✔
566

567
                bool needs_escaping = escape_separator && strchr(*s, *separator);
196,595✔
568

569
                if (needs_escaping)
196,584✔
570
                        for (size_t i = 0; (*s)[i]; i++) {
71✔
571
                                if ((*s)[i] == *separator)
60✔
572
                                        *(e++) = '\\';
14✔
573
                                *(e++) = (*s)[i];
60✔
574
                        }
575
                else
576
                        e = stpcpy(e, *s);
196,584✔
577
        }
578

579
        *e = 0;
62,760✔
580

581
        return r;
62,760✔
582
}
583

584
int strv_push_with_size(char ***l, size_t *n, char *value) {
7,012,051✔
585
        /* n is a pointer to a variable to store the size of l.
586
         * If not given (i.e. n is NULL or *n is SIZE_MAX), size will be calculated using strv_length().
587
         * If n is not NULL, the size after the push will be returned.
588
         * If value is empty, no action is taken and *n is not set. */
589

590
        if (!value)
7,012,051✔
591
                return 0;
592

593
        size_t size = n ? *n : SIZE_MAX;
7,012,049✔
594
        if (size == SIZE_MAX)
459✔
595
                size = strv_length(*l);
7,011,603✔
596

597
        /* Check for overflow */
598
        if (size > SIZE_MAX-2)
7,012,049✔
599
                return -ENOMEM;
600

601
        char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(size + 2), sizeof(char*));
7,012,049✔
602
        if (!c)
7,012,049✔
603
                return -ENOMEM;
604

605
        c[size] = value;
7,012,049✔
606
        c[size+1] = NULL;
7,012,049✔
607

608
        *l = c;
7,012,049✔
609
        if (n)
7,012,049✔
610
                *n = size + 1;
459✔
611
        return 0;
612
}
613

614
int strv_push_pair(char ***l, char *a, char *b) {
457✔
615
        char **c;
457✔
616
        size_t n;
457✔
617

618
        if (!a && !b)
457✔
619
                return 0;
620

621
        n = strv_length(*l);
457✔
622

623
        /* Check for overflow */
624
        if (n > SIZE_MAX-3)
457✔
625
                return -ENOMEM;
626

627
        /* increase and check for overflow */
628
        c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(n + !!a + !!b + 1), sizeof(char*));
457✔
629
        if (!c)
457✔
630
                return -ENOMEM;
631

632
        if (a)
457✔
633
                c[n++] = a;
457✔
634
        if (b)
457✔
635
                c[n++] = b;
457✔
636
        c[n] = NULL;
457✔
637

638
        *l = c;
457✔
639
        return 0;
457✔
640
}
641

642
int strv_insert(char ***l, size_t position, char *value) {
4,424✔
643
        char **c;
4,424✔
644
        size_t n, m;
4,424✔
645

646
        assert(l);
4,424✔
647

648
        if (!value)
4,424✔
649
                return 0;
650

651
        n = strv_length(*l);
4,423✔
652
        position = MIN(position, n);
4,423✔
653

654
        /* check for overflow and increase */
655
        if (n > SIZE_MAX - 2)
4,423✔
656
                return -ENOMEM;
657
        m = n + 2;
4,423✔
658

659
        c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m), sizeof(char*));
4,423✔
660
        if (!c)
4,423✔
661
                return -ENOMEM;
662

663
        if (n > position)
4,423✔
664
                memmove(c + position + 1, c + position, (n - position) * sizeof(char*));
1,159✔
665

666
        c[position] = value;
4,423✔
667
        c[n + 1] = NULL;
4,423✔
668

669
        *l = c;
4,423✔
670
        return 0;
4,423✔
671
}
672

673
int strv_consume_with_size(char ***l, size_t *n, char *value) {
6,961,753✔
674
        int r;
6,961,753✔
675

676
        r = strv_push_with_size(l, n, value);
6,961,753✔
677
        if (r < 0)
6,961,753✔
UNCOV
678
                free(value);
×
679

680
        return r;
6,961,753✔
681
}
682

683
int strv_consume_pair(char ***l, char *a, char *b) {
418✔
684
        int r;
418✔
685

686
        r = strv_push_pair(l, a, b);
418✔
687
        if (r < 0) {
418✔
UNCOV
688
                free(a);
×
UNCOV
689
                free(b);
×
690
        }
691

692
        return r;
418✔
693
}
694

695
int strv_consume_prepend(char ***l, char *value) {
4,406✔
696
        int r;
4,406✔
697

698
        r = strv_push_prepend(l, value);
4,406✔
699
        if (r < 0)
4,406✔
UNCOV
700
                free(value);
×
701

702
        return r;
4,406✔
703
}
704

705
int strv_prepend(char ***l, const char *value) {
3,616✔
706
        char *v;
3,616✔
707

708
        if (!value)
3,616✔
709
                return 0;
710

711
        v = strdup(value);
3,517✔
712
        if (!v)
3,517✔
713
                return -ENOMEM;
714

715
        return strv_consume_prepend(l, v);
3,517✔
716
}
717

718
int strv_extend_with_size(char ***l, size_t *n, const char *value) {
1,208,465✔
719
        char *v;
1,208,465✔
720

721
        if (!value)
1,208,465✔
722
                return 0;
723

724
        v = strdup(value);
1,204,849✔
725
        if (!v)
1,204,849✔
726
                return -ENOMEM;
727

728
        return strv_consume_with_size(l, n, v);
1,204,849✔
729
}
730

731
int strv_extend_many_internal(char ***l, const char *value, ...) {
346✔
732
        va_list ap;
346✔
733
        size_t n, m;
346✔
734
        int r;
346✔
735

736
        assert(l);
346✔
737

738
        m = n = strv_length(*l);
346✔
739

740
        r = 0;
346✔
741
        va_start(ap, value);
346✔
742
        for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
1,250✔
743
                if (!s)
904✔
744
                        continue;
27✔
745

746
                if (m > SIZE_MAX-1) { /* overflow */
877✔
747
                        r = -ENOMEM;
748
                        break;
749
                }
750
                m++;
877✔
751
        }
752
        va_end(ap);
346✔
753

754
        if (r < 0)
346✔
755
                return r;
346✔
756
        if (m > SIZE_MAX-1)
346✔
757
                return -ENOMEM;
758

759
        char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m+1), sizeof(char*));
346✔
760
        if (!c)
346✔
761
                return -ENOMEM;
762
        *l = c;
346✔
763

764
        r = 0;
346✔
765
        size_t i = n;
346✔
766
        va_start(ap, value);
346✔
767
        for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
1,250✔
768
                if (!s)
904✔
769
                        continue;
27✔
770

771
                c[i] = strdup(s);
877✔
772
                if (!c[i]) {
877✔
773
                        r = -ENOMEM;
774
                        break;
775
                }
776
                i++;
877✔
777
        }
778
        va_end(ap);
346✔
779

780
        if (r < 0) {
346✔
781
                /* rollback on error */
UNCOV
782
                for (size_t j = n; j < i; j++)
×
UNCOV
783
                        c[j] = mfree(c[j]);
×
784
                return r;
785
        }
786

787
        c[i] = NULL;
346✔
788
        return 0;
346✔
789
}
790

791
char** strv_uniq(char **l) {
51,001✔
792
        /* Drops duplicate entries. The first identical string will be
793
         * kept, the others dropped */
794

795
        STRV_FOREACH(i, l)
284,442✔
796
                strv_remove(i+1, *i);
233,441✔
797

798
        return l;
51,001✔
799
}
800

801
bool strv_is_uniq(char * const *l) {
20,246✔
802
        STRV_FOREACH(i, l)
47,469✔
803
                if (strv_contains(i+1, *i))
27,224✔
804
                        return false;
805

806
        return true;
807
}
808

809
char** strv_remove(char **l, const char *s) {
233,507✔
810
        char **f, **t;
233,507✔
811

812
        if (!l)
233,507✔
813
                return NULL;
814

815
        assert(s);
233,507✔
816

817
        /* Drops every occurrence of s in the string list, edits
818
         * in-place. */
819

820
        for (f = t = l; *f; f++)
1,236,671✔
821
                if (streq(*f, s))
1,003,164✔
822
                        free(*f);
30,343✔
823
                else
824
                        *(t++) = *f;
972,821✔
825

826
        *t = NULL;
233,507✔
827
        return l;
233,507✔
828
}
829

830
bool strv_overlap(char * const *a, char * const *b) {
405✔
831
        STRV_FOREACH(i, a)
739✔
832
                if (strv_contains(b, *i))
408✔
833
                        return true;
834

835
        return false;
836
}
837

838
static int str_compare(char * const *a, char * const *b) {
464,854✔
839
        return strcmp(*a, *b);
464,854✔
840
}
841

842
char** strv_sort(char **l) {
13,443✔
843
        typesafe_qsort(l, strv_length(l), str_compare);
13,443✔
844
        return l;
13,443✔
845
}
846

847
char** strv_sort_uniq(char **l) {
34,073✔
848
        if (strv_isempty(l))
34,073✔
849
                return l;
850

851
        char **tail = strv_sort(l), *prev = NULL;
205✔
852
        STRV_FOREACH(i, l)
3,520✔
853
                if (streq_ptr(*i, prev))
3,315✔
854
                        free(*i);
27✔
855
                else
856
                        *(tail++) = prev = *i;
3,288✔
857

858
        *tail = NULL;
205✔
859
        return l;
205✔
860
}
861

862
int strv_compare(char * const *a, char * const *b) {
3,390✔
863
        int r;
3,390✔
864

865
        if (strv_isempty(a)) {
3,390✔
866
                if (strv_isempty(b))
1,199✔
867
                        return 0;
1,136✔
868
                else
869
                        return -1;
870
        }
871

872
        if (strv_isempty(b))
2,191✔
873
                return 1;
874

875
        for ( ; *a || *b; ++a, ++b) {
5,316✔
876
                r = strcmp_ptr(*a, *b);
3,255✔
877
                if (r != 0)
3,255✔
878
                        return r;
879
        }
880

881
        return 0;
882
}
883

884
bool strv_equal_ignore_order(char * const *a, char * const *b) {
449✔
885

886
        /* Just like strv_equal(), but doesn't care about the order of elements or about redundant entries
887
         * (i.e. it's even ok if the number of entries in the array differ, as long as the difference just
888
         * consists of repetitions). */
889

890
        if (a == b)
449✔
891
                return true;
892

893
        STRV_FOREACH(i, a)
853✔
894
                if (!strv_contains(b, *i))
441✔
895
                        return false;
896

897
        STRV_FOREACH(i, b)
849✔
898
                if (!strv_contains(a, *i))
441✔
899
                        return false;
900

901
        return true;
902
}
903

904
void strv_print_full(char * const *l, const char *prefix) {
61✔
905
        STRV_FOREACH(s, l)
2,528✔
906
                printf("%s%s\n", strempty(prefix), *s);
4,934✔
907
}
61✔
908

909
int strv_extendf(char ***l, const char *format, ...) {
977,498✔
910
        va_list ap;
977,498✔
911
        char *x;
977,498✔
912
        int r;
977,498✔
913

914
        va_start(ap, format);
977,498✔
915
        r = vasprintf(&x, format, ap);
977,498✔
916
        va_end(ap);
977,498✔
917

918
        if (r < 0)
977,498✔
919
                return -ENOMEM;
977,498✔
920

921
        return strv_consume(l, x);
977,498✔
922
}
923

924
char* startswith_strv(const char *s, char * const *l) {
11,803,566✔
925
        STRV_FOREACH(i, l) {
36,009,935✔
926
                char *found = startswith(s, *i);
24,282,140✔
927
                if (found)
24,282,140✔
928
                        return found;
929
        }
930

931
        return NULL;
932
}
933

934
char* endswith_strv(const char *s, char * const *l) {
3,175,968✔
935
        STRV_FOREACH(i, l) {
9,507,163✔
936
                char *found = endswith(s, *i);
6,358,642✔
937
                if (found)
6,358,642✔
938
                        return found;
939
        }
940

941
        return NULL;
942
}
943

944
char** strv_reverse(char **l) {
844✔
945
        size_t n;
844✔
946

947
        n = strv_length(l);
844✔
948
        if (n <= 1)
844✔
949
                return l;
950

951
        for (size_t i = 0; i < n / 2; i++)
294✔
952
                SWAP_TWO(l[i], l[n-1-i]);
151✔
953

954
        return l;
955
}
956

957
char** strv_shell_escape(char **l, const char *bad) {
3✔
958
        /* Escapes every character in every string in l that is in bad,
959
         * edits in-place, does not roll-back on error. */
960

961
        STRV_FOREACH(s, l) {
8✔
962
                char *v;
5✔
963

964
                v = shell_escape(*s, bad);
5✔
965
                if (!v)
5✔
966
                        return NULL;
3✔
967

968
                free_and_replace(*s, v);
5✔
969
        }
970

971
        return l;
972
}
973

974
bool strv_fnmatch_full(
72,813✔
975
                char* const* patterns,
976
                const char *s,
977
                int flags,
978
                size_t *ret_matched_pos) {
979

980
        assert(s);
72,813✔
981

982
        if (patterns)
72,813✔
983
                for (size_t i = 0; patterns[i]; i++)
129,482✔
984
                        /* NB: We treat all fnmatch() errors as equivalent to FNM_NOMATCH, i.e. if fnmatch() fails to
985
                         * process the pattern for some reason we'll consider this equivalent to non-matching. */
986
                        if (fnmatch(patterns[i], s, flags) == 0) {
75,975✔
987
                                if (ret_matched_pos)
16,289✔
988
                                        *ret_matched_pos = i;
1,383✔
989
                                return true;
16,289✔
990
                        }
991

992
        if (ret_matched_pos)
56,524✔
993
                *ret_matched_pos = SIZE_MAX;
19,360✔
994

995
        return false;
996
}
997

998
char** strv_skip(char **l, size_t n) {
28,449✔
999
        while (n > 0) {
77,441✔
1000
                if (strv_isempty(l))
50,011✔
1001
                        return NULL;
1002

1003
                l++, n--;
48,992✔
1004
        }
1005

1006
        /* To simplify callers, always return NULL instead of a zero-item array. */
1007
        if (strv_isempty(l))
27,430✔
1008
                return NULL;
11,294✔
1009
        return l;
1010
}
1011

1012
int strv_extend_n(char ***l, const char *value, size_t n) {
512✔
1013
        size_t i, k;
512✔
1014
        char **nl;
512✔
1015

1016
        assert(l);
512✔
1017

1018
        if (!value)
512✔
1019
                return 0;
1020
        if (n == 0)
512✔
1021
                return 0;
1022

1023
        /* Adds the value n times to l */
1024

1025
        k = strv_length(*l);
511✔
1026
        if (n >= SIZE_MAX - k)
511✔
1027
                return -ENOMEM;
1028

1029
        nl = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(k + n + 1), sizeof(char *));
511✔
1030
        if (!nl)
511✔
1031
                return -ENOMEM;
1032

1033
        *l = nl;
511✔
1034

1035
        for (i = k; i < k + n; i++) {
1,059✔
1036
                nl[i] = strdup(value);
548✔
1037
                if (!nl[i])
548✔
UNCOV
1038
                        goto rollback;
×
1039
        }
1040
        nl[i] = NULL;
511✔
1041

1042
        return 0;
511✔
1043

UNCOV
1044
rollback:
×
UNCOV
1045
        for (size_t j = k; j < i; j++)
×
UNCOV
1046
                free(nl[j]);
×
1047
        nl[k] = NULL;
×
1048

1049
        return -ENOMEM;
×
1050
}
1051

1052
int strv_extend_assignment(char ***l, const char *lhs, const char *rhs) {
5,298,553✔
1053
        char *j;
5,298,553✔
1054

1055
        assert(l);
5,298,553✔
1056
        assert(lhs);
5,298,553✔
1057

1058
        if (!rhs) /* value is optional, in which case we suppress the field */
5,298,553✔
1059
                return 0;
1060

1061
        j = strjoin(lhs, "=", rhs);
3,839,660✔
1062
        if (!j)
3,839,660✔
1063
                return -ENOMEM;
1064

1065
        return strv_consume(l, j);
3,839,660✔
1066
}
1067

1068
int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) {
14,283✔
1069
        bool b = false;
14,283✔
1070
        int r;
14,283✔
1071

1072
        assert(f);
14,283✔
1073

1074
        /* Like fputs(), but for strv, and with a less stupid argument order */
1075

1076
        if (!space)
14,283✔
1077
                space = &b;
15✔
1078

1079
        STRV_FOREACH(s, l) {
14,713✔
1080
                r = fputs_with_separator(f, *s, separator, space);
430✔
1081
                if (r < 0)
430✔
1082
                        return r;
14,283✔
1083
        }
1084

1085
        return 0;
1086
}
1087

1088
void string_strv_hashmap_remove(Hashmap *h, const char *key, const char *value) {
135✔
1089
        assert(key);
135✔
1090

1091
        if (value) {
135✔
1092
                char **l = hashmap_get(h, key);
135✔
1093
                if (!l)
135✔
1094
                        return;
129✔
1095

1096
                strv_remove(l, value);
10✔
1097
                if (!strv_isempty(l))
135✔
1098
                        return;
1099
        }
1100

1101
        _unused_ _cleanup_free_ char *key_free = NULL;
6✔
1102
        strv_free(hashmap_remove2(h, key, (void**) &key_free));
6✔
1103
}
1104

1105
void string_strv_ordered_hashmap_remove(OrderedHashmap *h, const char *key, const char *value) {
4✔
1106
        string_strv_hashmap_remove(PLAIN_HASHMAP(h), key, value);
4✔
1107
}
4✔
1108

1109
static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const char *value) {
267,708✔
1110
        char **l;
267,708✔
1111
        int r;
267,708✔
1112

1113
        assert(h);
267,708✔
1114
        assert(key);
267,708✔
1115
        assert(value);
267,708✔
1116

1117
        l = hashmap_get(h, key);
267,708✔
1118
        if (l) {
267,708✔
1119
                /* A list for this key already exists, let's append to it if it is not listed yet */
1120
                if (strv_contains(l, value))
8,843✔
1121
                        return 0;
267,708✔
1122

1123
                r = strv_extend(&l, value);
8,830✔
1124
                if (r < 0)
8,830✔
1125
                        return r;
1126

1127
                assert_se(hashmap_update(h, key, l) >= 0);
8,830✔
1128
        } else {
1129
                /* No list for this key exists yet, create one */
UNCOV
1130
                _cleanup_strv_free_ char **l2 = NULL;
×
1131
                _cleanup_free_ char *t = NULL;
258,865✔
1132

1133
                t = strdup(key);
258,865✔
1134
                if (!t)
258,865✔
1135
                        return -ENOMEM;
1136

1137
                r = strv_extend(&l2, value);
258,865✔
1138
                if (r < 0)
258,865✔
1139
                        return r;
1140

1141
                r = hashmap_put(h, t, l2);
258,865✔
1142
                if (r < 0)
258,865✔
1143
                        return r;
1144

1145
                TAKE_PTR(t);
258,865✔
1146
                TAKE_PTR(l2);
258,865✔
1147
        }
1148

1149
        return 1;
1150
}
1151

1152
int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value) {
267,682✔
1153
        int r;
267,682✔
1154

1155
        assert(h);
267,682✔
1156
        assert(key);
267,682✔
1157
        assert(value);
267,682✔
1158

1159
        r = hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free);
267,682✔
1160
        if (r < 0)
267,682✔
1161
                return r;
1162

1163
        return string_strv_hashmap_put_internal(*h, key, value);
267,682✔
1164
}
1165

1166
int string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value) {
26✔
1167
        int r;
26✔
1168

1169
        assert(h);
26✔
1170
        assert(key);
26✔
1171
        assert(value);
26✔
1172

1173
        r = ordered_hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free);
26✔
1174
        if (r < 0)
26✔
1175
                return r;
1176

1177
        return string_strv_hashmap_put_internal(PLAIN_HASHMAP(*h), key, value);
26✔
1178
}
1179

1180
int strv_rebreak_lines(char **l, size_t width, char ***ret) {
14,450✔
1181
        _cleanup_strv_free_ char **broken = NULL;
14,450✔
1182
        int r;
14,450✔
1183

1184
        assert(ret);
14,450✔
1185

1186
        /* Implements a simple UTF-8 line breaking algorithm
1187
         *
1188
         * Goes through all entries in *l, and line-breaks each line that is longer than the specified
1189
         * character width. Breaks at the end of words/beginning of whitespace. Lines that do not contain whitespace are not
1190
         * broken. Retains whitespace at beginning of lines, removes it at end of lines. */
1191

1192
        if (width == SIZE_MAX) { /* NOP? */
14,450✔
1193
                broken = strv_copy(l);
2,938✔
1194
                if (!broken)
2,938✔
1195
                        return -ENOMEM;
1196

1197
                *ret = TAKE_PTR(broken);
2,938✔
1198
                return 0;
2,938✔
1199
        }
1200

1201
        STRV_FOREACH(i, l) {
23,150✔
1202
                const char *start = *i, *whitespace_begin = NULL, *whitespace_end = NULL;
1203
                bool in_prefix = true; /* still in the whitespace in the beginning of the line? */
1204
                size_t w = 0;
1205

1206
                for (const char *p = start; *p != 0; p = utf8_next_char(p)) {
639,606✔
1207
                        if (strchr(NEWLINE, *p)) {
627,968✔
1208
                                in_prefix = true;
1209
                                whitespace_begin = whitespace_end = NULL;
1210
                                w = 0;
1211
                        } else if (strchr(WHITESPACE, *p)) {
627,968✔
1212
                                if (!in_prefix && (!whitespace_begin || whitespace_end)) {
84,929✔
1213
                                        whitespace_begin = p;
84,332✔
1214
                                        whitespace_end = NULL;
84,332✔
1215
                                }
1216
                        } else {
1217
                                if (whitespace_begin && !whitespace_end)
543,039✔
1218
                                        whitespace_end = p;
84,323✔
1219

1220
                                in_prefix = false;
1221
                        }
1222

1223
                        int cw = utf8_char_console_width(p);
627,968✔
1224
                        if (cw < 0) {
627,968✔
UNCOV
1225
                                log_debug_errno(cw, "Comment to line break contains invalid UTF-8, ignoring.");
×
1226
                                cw = 1;
1227
                        }
1228

1229
                        w += cw;
627,968✔
1230

1231
                        if (w > width && whitespace_begin && whitespace_end) {
627,968✔
UNCOV
1232
                                _cleanup_free_ char *truncated = NULL;
×
1233

1234
                                truncated = strndup(start, whitespace_begin - start);
4,989✔
1235
                                if (!truncated)
4,989✔
1236
                                        return -ENOMEM;
1237

1238
                                r = strv_consume(&broken, TAKE_PTR(truncated));
4,989✔
1239
                                if (r < 0)
4,989✔
1240
                                        return r;
1241

1242
                                p = start = whitespace_end;
1243
                                whitespace_begin = whitespace_end = NULL;
1244
                                w = cw;
1245
                        }
1246
                }
1247

1248
                /* Process rest of the line */
1249
                assert(start);
11,638✔
1250
                if (in_prefix) /* Never seen anything non-whitespace? Generate empty line! */
11,638✔
1251
                        r = strv_extend(&broken, "");
1✔
1252
                else if (whitespace_begin && !whitespace_end) { /* Ends in whitespace? Chop it off! */
11,637✔
UNCOV
1253
                        _cleanup_free_ char *truncated = strndup(start, whitespace_begin - start);
×
1254
                        if (!truncated)
9✔
UNCOV
1255
                                return -ENOMEM;
×
1256

1257
                        r = strv_consume(&broken, TAKE_PTR(truncated));
9✔
1258
                } else /* Otherwise use line as is */
1259
                        r = strv_extend(&broken, start);
11,628✔
1260
                if (r < 0)
11,638✔
1261
                        return r;
1262
        }
1263

1264
        *ret = TAKE_PTR(broken);
11,512✔
1265
        return 0;
11,512✔
1266
}
1267

1268
char** strv_filter_prefix(char * const *l, const char *prefix) {
4✔
1269

1270
        /* Allocates a copy of 'l', but only copies over entries starting with 'prefix' */
1271

1272
        if (isempty(prefix))
4✔
1273
                return strv_copy(l);
5✔
1274

1275
        _cleanup_strv_free_ char **f = NULL;
3✔
1276
        size_t sz = 0;
3✔
1277

1278
        STRV_FOREACH(i, l) {
24✔
1279
                if (!startswith(*i, prefix))
21✔
1280
                        continue;
16✔
1281

1282
                if (strv_extend_with_size(&f, &sz, *i) < 0)
5✔
1283
                        return NULL;
1284
        }
1285

1286
        return TAKE_PTR(f);
3✔
1287
}
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