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

systemd / systemd / 27854786886

20 Jun 2026 12:28AM UTC coverage: 73.048% (+0.05%) from 72.995%
27854786886

push

github

bluca
report: disable json normalization

Two PRs got merged at the same time, which cause a test to fail,
as they work individually but fail when combined

TEST-74-AUX-UTILS.sh[1688]: + /usr/lib/systemd/systemd-report generate io.systemd.Manager.UnitsTotal
TEST-74-AUX-UTILS.sh[1805]: {"mediaType":"application/vnd.io.systemd.report","metrics":[{"name":"io.systemd.Manager.UnitsTotal","value":249}],"timestamp":"Fri 2026-06-19 19:50:48 UTC"}
TEST-74-AUX-UTILS.sh[1806]: + /usr/lib/systemd/systemd-report generate io.systemd.Manager.UnitsTotal
TEST-74-AUX-UTILS.sh[1807]: + jq .
TEST-74-AUX-UTILS.sh[1807]: {
TEST-74-AUX-UTILS.sh[1807]:   "mediaType": "application/vnd.io.systemd.report",
TEST-74-AUX-UTILS.sh[1807]:   "metrics": [
TEST-74-AUX-UTILS.sh[1807]: {
TEST-74-AUX-UTILS.sh[1807]:   "name": "io.systemd.Manager.UnitsTotal",
TEST-74-AUX-UTILS.sh[1807]:   "value": 249
TEST-74-AUX-UTILS.sh[1807]: }
TEST-74-AUX-UTILS.sh[1807]:   ],
TEST-74-AUX-UTILS.sh[1807]:   "timestamp": "Fri 2026-06-19 19:50:48 UTC"
TEST-74-AUX-UTILS.sh[1807]: }
TEST-74-AUX-UTILS.sh[1688]: + /usr/lib/systemd/systemd-report upload --url=http://localhost:8089/
TEST-74-AUX-UTILS.sh[1808]: Failed to normalize report JSON: Wrong medium type

https://github.com/systemd/systemd/pull/42594
https://github.com/systemd/systemd/pull/42595

Disable normalization for now, and track the issue at
https://github.com/systemd/systemd/issues/42669

Follow-up for 3c2f7c600

339084 of 464191 relevant lines covered (73.05%)

1311580.8 hits per line

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

95.24
/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) {
37,827,113✔
21
        assert(name);
37,827,113✔
22

23
        STRV_FOREACH(i, l)
349,736,253✔
24
                if (streq(*i, name))
314,123,638✔
25
                        return *i;
26

27
        return NULL;
28
}
29

30
char* strv_find_case(char * const *l, const char *name) {
2,256,251✔
31
        assert(name);
2,256,251✔
32

33
        STRV_FOREACH(i, l)
9,634,655✔
34
                if (strcaseeq(*i, name))
8,513,701✔
35
                        return *i;
36

37
        return NULL;
38
}
39

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

43
        STRV_FOREACH(i, l)
8,826✔
44
                if (startswith(*i, name))
8,435✔
45
                        return *i;
46

47
        return NULL;
48
}
49

50
char* strv_find_startswith(char * const *l, const char *name) {
1,894,812✔
51
        assert(name);
1,894,812✔
52

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

56
        STRV_FOREACH(i, l) {
3,841,460✔
57
                char *e;
1,950,875✔
58

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

64
        return NULL;
65
}
66

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

71
        assert(name);
33✔
72

73
        STRV_FOREACH(s, l) {
231✔
74
                char *e = startswith(*s, name);
198✔
75
                if (!e)
198✔
76
                        continue;
134✔
77

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

85
        return best;
33✔
86
}
87

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

92
        assert(name);
18✔
93

94
        STRV_FOREACH(i, l) {
118✔
95
                ssize_t distance;
100✔
96

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

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

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

112
        return best;
113
}
114

115
char* strv_find_closest(char * const *l, const char *name) {
33✔
116
        assert(name);
33✔
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);
33✔
123
        if (found)
33✔
124
                return found;
125

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

129
char* strv_find_first_field(char * const *needles, char * const *haystack) {
8,545✔
130
        STRV_FOREACH(k, needles) {
22,364✔
131
                char *value = strv_env_pairs_get((char **)haystack, *k);
19,334✔
132
                if (value)
19,334✔
133
                        return value;
134
        }
135

136
        return NULL;
137
}
138

139
char** strv_free(char **l) {
9,624,111✔
140
        STRV_FOREACH(k, l)
28,209,365✔
141
                free(*k);
18,585,254✔
142

143
        return mfree(l);
9,624,111✔
144
}
145

146
char** strv_free_erase(char **l) {
99,649✔
147
        STRV_FOREACH(i, l)
113,963✔
148
                erase_and_freep(i);
14,314✔
149

150
        return mfree(l);
99,649✔
151
}
152

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

156
        FOREACH_ARRAY (i, strvs, n)
120✔
157
                strv_free(*i);
75✔
158

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

162
char** strv_copy_n(char * const *l, size_t n) {
156,107✔
163
        _cleanup_strv_free_ char **result = NULL;
156,107✔
164
        char **k;
156,107✔
165

166
        result = new(char*, MIN(strv_length(l), n) + 1);
156,107✔
167
        if (!result)
156,107✔
168
                return NULL;
169

170
        k = result;
171
        STRV_FOREACH(i, l) {
911,804✔
172
                if (n == 0)
755,703✔
173
                        break;
174

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

180
                if (n != SIZE_MAX)
755,697✔
181
                        n--;
81✔
182
        }
183

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

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

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

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

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

204
size_t strv_length(char * const *l) {
13,298,437✔
205
        size_t n = 0;
13,298,437✔
206

207
        STRV_FOREACH(i, l)
305,621,670✔
208
                n++;
292,323,233✔
209

210
        return n;
13,298,437✔
211
}
212

213
char** strv_new_ap(const char *x, va_list ap) {
77,149✔
214
        _cleanup_strv_free_ char **a = NULL;
77,149✔
215
        size_t n = 0, i = 0;
77,149✔
216
        va_list aq;
77,149✔
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);
77,149✔
224
        for (const char *s = x; s; s = va_arg(aq, const char*)) {
315,922✔
225
                if (s == STRV_IGNORE)
238,773✔
226
                        continue;
7,163✔
227

228
                n++;
231,610✔
229
        }
230
        va_end(aq);
77,149✔
231

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

236
        for (const char *s = x; s; s = va_arg(ap, const char*)) {
315,922✔
237
                if (s == STRV_IGNORE)
238,773✔
238
                        continue;
7,163✔
239

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

244
                i++;
231,610✔
245
        }
246

247
        a[i] = NULL;
77,149✔
248

249
        return TAKE_PTR(a);
77,149✔
250
}
251

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

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

260
        return r;
61,961✔
261
}
262

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

266
        assert(a);
24,211✔
267

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

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

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

280
        t[p] = NULL;
20,467✔
281
        *a = t;
20,467✔
282

283
        STRV_FOREACH(s, b) {
48,784✔
284
                if (filter_duplicates && strv_contains(t, *s))
28,317✔
285
                        continue;
44✔
286

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

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

295
        assert(i <= q);
20,467✔
296

297
        return (int) i;
20,467✔
298

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

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

309
        assert(a);
192,909✔
310

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

315
        p = strv_length(*a);
126,288✔
316
        if (p == 0) {
126,288✔
317
                strv_free_and_replace(*a, b_consume);
108,155✔
318

319
                if (filter_duplicates)
108,155✔
320
                        strv_uniq(*a);
30,341✔
321

322
                return strv_length(*a);
108,155✔
323
        }
324

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

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

332
        t[p] = NULL;
18,133✔
333
        *a = t;
18,133✔
334

335
        if (!filter_duplicates) {
18,133✔
336
                *mempcpy_typesafe(t + p, b, q) = NULL;
17,473✔
337
                i = q;
17,473✔
338
        } else {
339
                i = 0;
340

341
                STRV_FOREACH(s, b) {
1,899✔
342
                        if (strv_contains(t, *s)) {
1,239✔
343
                                free(*s);
198✔
344
                                continue;
198✔
345
                        }
346

347
                        t[p+i] = *s;
1,041✔
348

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

354
        assert(i <= q);
18,133✔
355

356
        b_consume = mfree(b_consume);
18,133✔
357

358
        return (int) i;
18,133✔
359
}
360

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

364
        assert(a);
70,646✔
365

366
        STRV_FOREACH(s, b) {
351,471✔
367
                char *v;
280,825✔
368

369
                v = strjoin(strempty(prefix), *s, suffix);
561,648✔
370
                if (!v)
280,825✔
371
                        return -ENOMEM;
372

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

378
        return 0;
379
}
380

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

386
        assert(s);
594,410✔
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);
594,410✔
392
        if (r < 0)
594,410✔
393
                return r;
394

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

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

405
char** strv_split_newlines(const char *s) {
63,534✔
406
        char **ret;
63,534✔
407

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

411
        return ret;
63,534✔
412
}
413

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

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

422
        for (;;) {
16,185,940✔
423
                _cleanup_free_ char *word = NULL;
7,647,580✔
424

425
                r = extract_first_word(&s, &word, separators, flags);
8,538,369✔
426
                if (r < 0)
8,538,369✔
427
                        return r;
428
                if (r == 0)
8,538,360✔
429
                        break;
430

431
                if (!GREEDY_REALLOC(l, n + 2))
7,647,571✔
432
                        return -ENOMEM;
433

434
                l[n++] = TAKE_PTR(word);
7,647,571✔
435
                l[n] = NULL;
7,647,571✔
436
        }
437

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

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

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

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

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

455
        return ret;
18,905✔
456
}
457

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

462
        assert(t);
1,011✔
463
        assert(s);
1,011✔
464

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

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

473
        return (int) strv_length(*t);
1,011✔
474
}
475

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

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

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

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

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

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

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

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

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

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

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

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

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

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

536
        if (!separator)
202,191✔
537
                separator = " ";
1,382✔
538

539
        k = strlen(separator);
202,191✔
540
        m = strlen_ptr(prefix);
202,191✔
541

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

545
        n = 0;
546
        STRV_FOREACH(s, l) {
1,002,621✔
547
                if (s != l)
800,430✔
548
                        n += k;
600,983✔
549

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

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

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

559
        e = r;
560
        STRV_FOREACH(s, l) {
1,002,621✔
561
                if (s != l)
800,430✔
562
                        e = stpcpy(e, separator);
600,983✔
563

564
                if (prefix)
800,430✔
565
                        e = stpcpy(e, prefix);
17✔
566

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

569
                if (needs_escaping)
800,419✔
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);
800,419✔
577
        }
578

579
        *e = 0;
202,191✔
580

581
        return r;
202,191✔
582
}
583

584
int strv_push_with_size(char ***l, size_t *n, char *value) {
9,785,609✔
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
        assert(l);
9,785,609✔
591
        POINTER_MAY_BE_NULL(n);
9,785,609✔
592

593
        if (!value)
9,785,609✔
594
                return 0;
595

596
        size_t size = n ? *n : SIZE_MAX;
9,785,607✔
597
        if (size == SIZE_MAX)
318,827✔
598
                size = strv_length(*l);
9,466,800✔
599

600
        /* Check for overflow */
601
        if (size > SIZE_MAX-2)
9,785,607✔
602
                return -ENOMEM;
603

604
        char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(size + 2), sizeof(char*));
9,785,607✔
605
        if (!c)
9,785,607✔
606
                return -ENOMEM;
607

608
        c[size] = value;
9,785,607✔
609
        c[size+1] = NULL;
9,785,607✔
610

611
        *l = c;
9,785,607✔
612
        if (n)
9,785,607✔
613
                *n = size + 1;
318,827✔
614
        return 0;
615
}
616

617
int strv_push_pair(char ***l, char *a, char *b) {
370✔
618
        char **c;
370✔
619
        size_t n;
370✔
620

621
        assert(l);
370✔
622

623
        if (!a && !b)
370✔
624
                return 0;
625

626
        n = strv_length(*l);
370✔
627

628
        /* Check for overflow */
629
        if (n > SIZE_MAX-3)
370✔
630
                return -ENOMEM;
631

632
        /* increase and check for overflow */
633
        c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(n + !!a + !!b + 1), sizeof(char*));
370✔
634
        if (!c)
370✔
635
                return -ENOMEM;
636

637
        if (a)
370✔
638
                c[n++] = a;
370✔
639
        if (b)
370✔
640
                c[n++] = b;
370✔
641
        c[n] = NULL;
370✔
642

643
        *l = c;
370✔
644
        return 0;
370✔
645
}
646

647
int strv_insert(char ***l, size_t position, char *value) {
7,359✔
648
        char **c;
7,359✔
649
        size_t n, m;
7,359✔
650

651
        assert(l);
7,359✔
652

653
        if (!value)
7,359✔
654
                return 0;
655

656
        n = strv_length(*l);
7,358✔
657
        position = MIN(position, n);
7,358✔
658

659
        /* check for overflow and increase */
660
        if (n > SIZE_MAX - 2)
7,358✔
661
                return -ENOMEM;
662
        m = n + 2;
7,358✔
663

664
        c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m), sizeof(char*));
7,358✔
665
        if (!c)
7,358✔
666
                return -ENOMEM;
667

668
        if (n > position)
7,358✔
669
                memmove(c + position + 1, c + position, (n - position) * sizeof(char*));
3,054✔
670

671
        c[position] = value;
7,358✔
672
        c[n + 1] = NULL;
7,358✔
673

674
        *l = c;
7,358✔
675
        return 0;
7,358✔
676
}
677

678
int strv_consume_with_size(char ***l, size_t *n, char *value) {
9,718,637✔
679
        int r;
9,718,637✔
680

681
        r = strv_push_with_size(l, n, value);
9,718,637✔
682
        if (r < 0)
9,718,637✔
683
                free(value);
×
684

685
        return r;
9,718,637✔
686
}
687

688
int strv_consume_pair(char ***l, char *a, char *b) {
337✔
689
        int r;
337✔
690

691
        r = strv_push_pair(l, a, b);
337✔
692
        if (r < 0) {
337✔
693
                free(a);
×
694
                free(b);
×
695
        }
696

697
        return r;
337✔
698
}
699

700
int strv_consume_prepend(char ***l, char *value) {
7,353✔
701
        int r;
7,353✔
702

703
        r = strv_push_prepend(l, value);
7,353✔
704
        if (r < 0)
7,353✔
705
                free(value);
×
706

707
        return r;
7,353✔
708
}
709

710
int strv_prepend(char ***l, const char *value) {
6,084✔
711
        char *v;
6,084✔
712

713
        if (!value)
6,084✔
714
                return 0;
715

716
        v = strdup(value);
5,952✔
717
        if (!v)
5,952✔
718
                return -ENOMEM;
719

720
        return strv_consume_prepend(l, v);
5,952✔
721
}
722

723
int strv_extend_with_size(char ***l, size_t *n, const char *value) {
1,782,997✔
724
        char *v;
1,782,997✔
725

726
        if (!value)
1,782,997✔
727
                return 0;
728

729
        v = strdup(value);
1,778,709✔
730
        if (!v)
1,778,709✔
731
                return -ENOMEM;
732

733
        return strv_consume_with_size(l, n, v);
1,778,709✔
734
}
735

736
int strv_extend_many_internal(char ***l, const char *value, ...) {
3,694✔
737
        va_list ap;
3,694✔
738
        size_t n, m;
3,694✔
739
        int r;
3,694✔
740

741
        assert(l);
3,694✔
742

743
        m = n = strv_length(*l);
3,694✔
744

745
        r = 0;
3,694✔
746
        va_start(ap, value);
3,694✔
747
        for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
11,854✔
748
                if (!s)
8,160✔
749
                        continue;
37✔
750

751
                if (m > SIZE_MAX-1) { /* overflow */
8,123✔
752
                        r = -ENOMEM;
753
                        break;
754
                }
755
                m++;
8,123✔
756
        }
757
        va_end(ap);
3,694✔
758

759
        if (r < 0)
3,694✔
760
                return r;
3,694✔
761
        if (m > SIZE_MAX-1)
3,694✔
762
                return -ENOMEM;
763

764
        char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m+1), sizeof(char*));
3,694✔
765
        if (!c)
3,694✔
766
                return -ENOMEM;
767
        *l = c;
3,694✔
768

769
        r = 0;
3,694✔
770
        size_t i = n;
3,694✔
771
        va_start(ap, value);
3,694✔
772
        for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
11,854✔
773
                if (!s)
8,160✔
774
                        continue;
37✔
775

776
                c[i] = strdup(s);
8,123✔
777
                if (!c[i]) {
8,123✔
778
                        r = -ENOMEM;
779
                        break;
780
                }
781
                i++;
8,123✔
782
        }
783
        va_end(ap);
3,694✔
784

785
        if (r < 0) {
3,694✔
786
                /* rollback on error */
787
                for (size_t j = n; j < i; j++)
×
788
                        c[j] = mfree(c[j]);
×
789
                return r;
790
        }
791

792
        c[i] = NULL;
3,694✔
793
        return 0;
3,694✔
794
}
795

796
char** strv_uniq(char **l) {
83,839✔
797
        /* Drops duplicate entries. The first identical string will be
798
         * kept, the others dropped */
799

800
        STRV_FOREACH(i, l)
413,626✔
801
                strv_remove(i+1, *i);
329,787✔
802

803
        return l;
83,839✔
804
}
805

806
bool strv_is_uniq(char * const *l) {
51,622✔
807
        STRV_FOREACH(i, l)
119,019✔
808
                if (strv_contains(i+1, *i))
67,398✔
809
                        return false;
810

811
        return true;
812
}
813

814
char** strv_remove(char **l, const char *s) {
329,892✔
815
        if (!l)
329,892✔
816
                return NULL;
817

818
        assert(s);
329,892✔
819

820
        /* Drops every occurrence of s in the string list, edits in-place. */
821

822
        char **f, **t;
823
        for (f = t = l; *f; f++)
1,762,898✔
824
                if (streq(*f, s))
1,433,006✔
825
                        free(*f);
42,891✔
826
                else
827
                        *(t++) = *f;
1,390,115✔
828

829
        *t = NULL;
329,892✔
830
        return l;
329,892✔
831
}
832

833
char** strv_remove_strv(char **l, char *const*ll) {
15✔
834

835
        if (strv_isempty(l))
15✔
836
                return l;
837

838
        STRV_FOREACH(i, ll)
23✔
839
                strv_remove(l, *i);
10✔
840

841
        return l;
842
}
843

844
bool strv_overlap(char * const *a, char * const *b) {
971✔
845
        POINTER_MAY_BE_NULL(a);
971✔
846
        POINTER_MAY_BE_NULL(b);
971✔
847

848
        STRV_FOREACH(i, a)
1,724✔
849
                if (strv_contains(b, *i))
974✔
850
                        return true;
851

852
        return false;
853
}
854

855
static int str_compare(char * const *a, char * const *b) {
1,291,416✔
856
        /* This is called from qsort()s inner loops. Correctly implemented qsort will never pass NULL so we
857
           just suppress the check via POINTER_MAY_BE_NULL instead of assert() to avoid the runtime cost. */
858
        POINTER_MAY_BE_NULL(a);
1,291,416✔
859
        POINTER_MAY_BE_NULL(b);
1,291,416✔
860

861
        return strcmp(*a, *b);
1,291,416✔
862
}
863

864
char** strv_sort(char **l) {
48,341✔
865
        typesafe_qsort(l, strv_length(l), str_compare);
48,341✔
866
        return l;
48,341✔
867
}
868

869
char** strv_sort_uniq(char **l) {
41,212✔
870
        if (strv_isempty(l))
41,212✔
871
                return l;
872

873
        char **tail = strv_sort(l), *prev = NULL;
436✔
874
        STRV_FOREACH(i, l)
4,874✔
875
                if (streq_ptr(*i, prev))
4,002✔
876
                        free(*i);
33✔
877
                else
878
                        *(tail++) = prev = *i;
3,969✔
879

880
        *tail = NULL;
436✔
881
        return l;
436✔
882
}
883

884
int strv_compare(char * const *a, char * const *b) {
13,591✔
885
        int r;
13,591✔
886

887
        POINTER_MAY_BE_NULL(a);
13,591✔
888
        POINTER_MAY_BE_NULL(b);
13,591✔
889

890
        if (strv_isempty(a)) {
13,591✔
891
                if (strv_isempty(b))
3,240✔
892
                        return 0;
3,160✔
893
                else
894
                        return -1;
895
        }
896

897
        if (strv_isempty(b))
10,351✔
898
                return 1;
899

900
        for ( ; *a || *b; ++a, ++b) {
23,775✔
901
                r = strcmp_ptr(*a, *b);
13,606✔
902
                if (r != 0)
13,606✔
903
                        return r;
904
        }
905

906
        return 0;
907
}
908

909
bool strv_equal_ignore_order(char * const *a, char * const *b) {
495✔
910

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

915
        if (a == b)
495✔
916
                return true;
917

918
        STRV_FOREACH(i, a)
941✔
919
                if (!strv_contains(b, *i))
488✔
920
                        return false;
921

922
        STRV_FOREACH(i, b)
937✔
923
                if (!strv_contains(a, *i))
488✔
924
                        return false;
925

926
        return true;
927
}
928

929
void strv_print_full(char * const *l, const char *prefix) {
112✔
930
        STRV_FOREACH(s, l)
3,124✔
931
                printf("%s%s\n", strempty(prefix), *s);
6,024✔
932
}
112✔
933

934
int strv_extendf_with_size(char ***l, size_t *n, const char *format, ...) {
1,280,579✔
935
        va_list ap;
1,280,579✔
936
        char *x;
1,280,579✔
937
        int r;
1,280,579✔
938

939
        va_start(ap, format);
1,280,579✔
940
        r = vasprintf(&x, format, ap);
1,280,579✔
941
        va_end(ap);
1,280,579✔
942

943
        if (r < 0)
1,280,579✔
944
                return -ENOMEM;
1,280,579✔
945

946
        return strv_consume_with_size(l, n, x);
1,280,579✔
947
}
948

949
int strv_extend_joined_with_size_sentinel(char ***l, size_t *n, ...) {
60,495✔
950
        va_list ap;
60,495✔
951

952
        va_start(ap, n);
60,495✔
953
        char *x = strextendv_with_separator(/* x= */ NULL, /* separator= */ NULL, ap);
60,495✔
954
        va_end(ap);
60,495✔
955
        if (!x)
60,495✔
956
                return -ENOMEM;
60,495✔
957

958
        return strv_consume_with_size(l, n, x);
60,495✔
959
}
960

961
char* startswith_strv_internal(const char *s, char * const *l) {
12,494,001✔
962
        STRV_FOREACH(i, l) {
38,072,104✔
963
                char *found = (char*) startswith(s, *i);
25,715,859✔
964
                if (found)
25,715,859✔
965
                        return found;
966
        }
967

968
        return NULL;
969
}
970

971
char* endswith_strv_internal(const char *s, char * const *l) {
5,974,088✔
972
        STRV_FOREACH(i, l) {
17,928,459✔
973
                char *found = (char*) endswith(s, *i);
11,991,137✔
974
                if (found)
11,991,137✔
975
                        return found;
976
        }
977

978
        return NULL;
979
}
980

981
char** strv_reverse(char **l) {
856✔
982
        size_t n;
856✔
983

984
        n = strv_length(l);
856✔
985
        if (n <= 1)
856✔
986
                return l;
987

988
        for (size_t i = 0; i < n / 2; i++)
318✔
989
                SWAP_TWO(l[i], l[n-1-i]);
163✔
990

991
        return l;
992
}
993

994
char** strv_shell_escape(char **l, const char *bad) {
3✔
995
        /* Escapes every character in every string in l that is in bad,
996
         * edits in-place, does not roll-back on error. */
997

998
        STRV_FOREACH(s, l) {
8✔
999
                char *v;
5✔
1000

1001
                v = shell_escape(*s, bad);
5✔
1002
                if (!v)
5✔
1003
                        return NULL;
3✔
1004

1005
                free_and_replace(*s, v);
5✔
1006
        }
1007

1008
        return l;
1009
}
1010

1011
bool strv_fnmatch_full(
91,645✔
1012
                char* const* patterns,
1013
                const char *s,
1014
                int flags,
1015
                size_t *ret_matched_pos) {
1016

1017
        assert(s);
91,645✔
1018

1019
        if (patterns)
91,645✔
1020
                for (size_t i = 0; patterns[i]; i++)
162,772✔
1021
                        /* NB: We treat all fnmatch() errors as equivalent to FNM_NOMATCH, i.e. if fnmatch() fails to
1022
                         * process the pattern for some reason we'll consider this equivalent to non-matching. */
1023
                        if (fnmatch(patterns[i], s, flags) == 0) {
95,251✔
1024
                                if (ret_matched_pos)
20,791✔
1025
                                        *ret_matched_pos = i;
1,748✔
1026
                                return true;
1027
                        }
1028

1029
        if (ret_matched_pos)
70,854✔
1030
                *ret_matched_pos = SIZE_MAX;
20,577✔
1031

1032
        return false;
1033
}
1034

1035
char** strv_skip(char **l, size_t n) {
27,070✔
1036
        while (n > 0) {
58,123✔
1037
                if (strv_isempty(l))
32,366✔
1038
                        return NULL;
1039

1040
                l++, n--;
31,053✔
1041
        }
1042

1043
        /* To simplify callers, always return NULL instead of a zero-item array. */
1044
        if (strv_isempty(l))
25,757✔
1045
                return NULL;
5,020✔
1046
        return l;
1047
}
1048

1049
int strv_extend_n(char ***l, const char *value, size_t n) {
839✔
1050
        size_t i, k;
839✔
1051
        char **nl;
839✔
1052

1053
        assert(l);
839✔
1054

1055
        if (!value)
839✔
1056
                return 0;
1057
        if (n == 0)
839✔
1058
                return 0;
1059

1060
        /* Adds the value n times to l */
1061

1062
        k = strv_length(*l);
838✔
1063
        if (n >= SIZE_MAX - k)
838✔
1064
                return -ENOMEM;
1065

1066
        nl = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(k + n + 1), sizeof(char *));
838✔
1067
        if (!nl)
838✔
1068
                return -ENOMEM;
1069

1070
        *l = nl;
838✔
1071

1072
        for (i = k; i < k + n; i++) {
1,724✔
1073
                nl[i] = strdup(value);
886✔
1074
                if (!nl[i])
886✔
1075
                        goto rollback;
×
1076
        }
1077
        nl[i] = NULL;
838✔
1078

1079
        return 0;
838✔
1080

1081
rollback:
×
1082
        for (size_t j = k; j < i; j++)
×
1083
                free(nl[j]);
×
1084
        nl[k] = NULL;
×
1085

1086
        return -ENOMEM;
×
1087
}
1088

1089
int strv_extend_assignment(char ***l, const char *lhs, const char *rhs) {
7,441,894✔
1090
        char *j;
7,441,894✔
1091

1092
        assert(l);
7,441,894✔
1093
        assert(lhs);
7,441,894✔
1094

1095
        if (!rhs) /* value is optional, in which case we suppress the field */
7,441,894✔
1096
                return 0;
1097

1098
        j = strjoin(lhs, "=", rhs);
5,274,333✔
1099
        if (!j)
5,274,333✔
1100
                return -ENOMEM;
1101

1102
        return strv_consume(l, j);
5,274,333✔
1103
}
1104

1105
int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) {
17,597✔
1106
        bool b = false;
17,597✔
1107
        int r;
17,597✔
1108

1109
        assert(f);
17,597✔
1110

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

1113
        if (!space)
17,597✔
1114
                space = &b;
5✔
1115

1116
        STRV_FOREACH(s, l) {
17,998✔
1117
                r = fputs_with_separator(f, *s, separator, space);
401✔
1118
                if (r < 0)
401✔
1119
                        return r;
17,597✔
1120
        }
1121

1122
        return 0;
1123
}
1124

1125
void string_strv_hashmap_remove(Hashmap *h, const char *key, const char *value) {
558✔
1126
        assert(key);
558✔
1127

1128
        if (value) {
558✔
1129
                char **l = hashmap_get(h, key);
558✔
1130
                if (!l)
558✔
1131
                        return;
529✔
1132

1133
                strv_remove(l, value);
33✔
1134
                if (!strv_isempty(l))
558✔
1135
                        return;
1136
        }
1137

1138
        _unused_ _cleanup_free_ char *key_free = NULL;
29✔
1139
        strv_free(hashmap_remove2(h, key, (void**) &key_free));
29✔
1140
}
1141

1142
void string_strv_ordered_hashmap_remove(OrderedHashmap *h, const char *key, const char *value) {
4✔
1143
        string_strv_hashmap_remove(PLAIN_HASHMAP(h), key, value);
4✔
1144
}
4✔
1145

1146
static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const char *value) {
558,379✔
1147
        char **l;
558,379✔
1148
        int r;
558,379✔
1149

1150
        assert(h);
558,379✔
1151
        assert(key);
558,379✔
1152
        assert(value);
558,379✔
1153

1154
        l = hashmap_get(h, key);
558,379✔
1155
        if (l) {
558,379✔
1156
                /* A list for this key already exists, let's append to it if it is not listed yet */
1157
                if (strv_contains(l, value))
34,371✔
1158
                        return 0;
558,379✔
1159

1160
                r = strv_extend(&l, value);
34,358✔
1161
                if (r < 0)
34,358✔
1162
                        return r;
1163

1164
                assert_se(hashmap_update(h, key, l) >= 0);
34,358✔
1165
        } else {
1166
                /* No list for this key exists yet, create one */
1167
                _cleanup_strv_free_ char **l2 = NULL;
×
1168
                _cleanup_free_ char *t = NULL;
524,008✔
1169

1170
                t = strdup(key);
524,008✔
1171
                if (!t)
524,008✔
1172
                        return -ENOMEM;
1173

1174
                r = strv_extend(&l2, value);
524,008✔
1175
                if (r < 0)
524,008✔
1176
                        return r;
1177

1178
                r = hashmap_put(h, t, l2);
524,008✔
1179
                if (r < 0)
524,008✔
1180
                        return r;
1181

1182
                TAKE_PTR(t);
524,008✔
1183
                TAKE_PTR(l2);
524,008✔
1184
        }
1185

1186
        return 1;
1187
}
1188

1189
int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value) {
558,353✔
1190
        int r;
558,353✔
1191

1192
        assert(h);
558,353✔
1193
        assert(key);
558,353✔
1194
        assert(value);
558,353✔
1195

1196
        r = hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free);
558,353✔
1197
        if (r < 0)
558,353✔
1198
                return r;
1199

1200
        return string_strv_hashmap_put_internal(*h, key, value);
558,353✔
1201
}
1202

1203
int string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value) {
26✔
1204
        int r;
26✔
1205

1206
        assert(h);
26✔
1207
        assert(key);
26✔
1208
        assert(value);
26✔
1209

1210
        r = ordered_hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free);
26✔
1211
        if (r < 0)
26✔
1212
                return r;
1213

1214
        return string_strv_hashmap_put_internal(PLAIN_HASHMAP(*h), key, value);
26✔
1215
}
1216

1217
int strv_rebreak_lines(char **l, size_t width, char ***ret) {
65,508✔
1218
        _cleanup_strv_free_ char **broken = NULL;
65,508✔
1219
        int r;
65,508✔
1220

1221
        assert(ret);
65,508✔
1222

1223
        /* Implements a simple UTF-8 line breaking algorithm
1224
         *
1225
         * Goes through all entries in *l, and line-breaks each line that is longer than the specified
1226
         * character width. Breaks at the end of words/beginning of whitespace. Lines that do not contain whitespace are not
1227
         * broken. Retains whitespace at beginning of lines, removes it at end of lines. */
1228

1229
        if (width == SIZE_MAX) { /* NOP? */
65,508✔
1230
                broken = strv_copy(l);
8,231✔
1231
                if (!broken)
8,231✔
1232
                        return -ENOMEM;
1233

1234
                *ret = TAKE_PTR(broken);
8,231✔
1235
                return 0;
8,231✔
1236
        }
1237

1238
        STRV_FOREACH(i, l) {
114,680✔
1239
                const char *start = *i, *whitespace_begin = NULL, *whitespace_end = NULL;
1240
                bool in_prefix = true; /* still in the whitespace in the beginning of the line? */
1241
                size_t w = 0;
1242

1243
                for (const char *p = start; *p != 0; p = utf8_next_char(p)) {
3,250,841✔
1244
                        if (strchr(NEWLINE, *p)) {
3,193,438✔
1245
                                in_prefix = true;
1246
                                whitespace_begin = whitespace_end = NULL;
1247
                                w = 0;
1248
                        } else if (strchr(WHITESPACE, *p)) {
3,193,438✔
1249
                                if (!in_prefix && (!whitespace_begin || whitespace_end)) {
275,118✔
1250
                                        whitespace_begin = p;
274,521✔
1251
                                        whitespace_end = NULL;
274,521✔
1252
                                }
1253
                        } else {
1254
                                if (whitespace_begin && !whitespace_end)
2,918,320✔
1255
                                        whitespace_end = p;
274,512✔
1256

1257
                                in_prefix = false;
1258
                        }
1259

1260
                        int cw = utf8_char_console_width(p);
3,193,438✔
1261
                        if (cw < 0) {
3,193,438✔
1262
                                log_debug_errno(cw, "Comment to line break contains invalid UTF-8, ignoring.");
×
1263
                                cw = 1;
1264
                        }
1265

1266
                        w += cw;
3,193,438✔
1267

1268
                        if (w > width && whitespace_begin && whitespace_end) {
3,193,438✔
1269
                                _cleanup_free_ char *truncated = NULL;
×
1270

1271
                                truncated = strndup(start, whitespace_begin - start);
14,561✔
1272
                                if (!truncated)
14,561✔
1273
                                        return -ENOMEM;
1274

1275
                                r = strv_consume(&broken, TAKE_PTR(truncated));
14,561✔
1276
                                if (r < 0)
14,561✔
1277
                                        return r;
1278

1279
                                p = start = whitespace_end;
1280
                                whitespace_begin = whitespace_end = NULL;
1281
                                w = cw;
1282
                        }
1283
                }
1284

1285
                /* Process rest of the line */
1286
                assert(start);
57,403✔
1287
                if (in_prefix) /* Never seen anything non-whitespace? Generate empty line! */
57,403✔
1288
                        r = strv_extend(&broken, "");
1✔
1289
                else if (whitespace_begin && !whitespace_end) { /* Ends in whitespace? Chop it off! */
57,402✔
1290
                        _cleanup_free_ char *truncated = strndup(start, whitespace_begin - start);
×
1291
                        if (!truncated)
9✔
1292
                                return -ENOMEM;
×
1293

1294
                        r = strv_consume(&broken, TAKE_PTR(truncated));
9✔
1295
                } else /* Otherwise use line as is */
1296
                        r = strv_extend(&broken, start);
57,393✔
1297
                if (r < 0)
57,403✔
1298
                        return r;
1299
        }
1300

1301
        *ret = TAKE_PTR(broken);
57,277✔
1302
        return 0;
57,277✔
1303
}
1304

1305
char** strv_filter_prefix(char * const *l, const char *prefix) {
4✔
1306

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

1309
        if (isempty(prefix))
4✔
1310
                return strv_copy(l);
5✔
1311

1312
        _cleanup_strv_free_ char **f = NULL;
3✔
1313
        size_t sz = 0;
3✔
1314

1315
        STRV_FOREACH(i, l) {
24✔
1316
                if (!startswith(*i, prefix))
21✔
1317
                        continue;
16✔
1318

1319
                if (strv_extend_with_size(&f, &sz, *i) < 0)
5✔
1320
                        return NULL;
1321
        }
1322

1323
        return TAKE_PTR(f);
3✔
1324
}
1325

1326
const char* const strv_empty[] = { NULL };
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