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

systemd / systemd / 25770446501

12 May 2026 11:15PM UTC coverage: 72.513% (-0.1%) from 72.65%
25770446501

push

github

web-flow
dhcp: random trivial cleanups (#42061)

8 of 25 new or added lines in 4 files covered. (32.0%)

4141 existing lines in 87 files now uncovered.

327776 of 452023 relevant lines covered (72.51%)

1437215.93 hits per line

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

95.22
/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) {
35,957,866✔
21
        assert(name);
35,957,866✔
22

23
        STRV_FOREACH(i, l)
326,921,706✔
24
                if (streq(*i, name))
293,243,795✔
25
                        return *i;
26

27
        return NULL;
28
}
29

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

33
        STRV_FOREACH(i, l)
9,098,780✔
34
                if (strcaseeq(*i, name))
8,029,874✔
35
                        return *i;
36

37
        return NULL;
38
}
39

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

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

47
        return NULL;
48
}
49

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

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

56
        STRV_FOREACH(i, l) {
2,950,840✔
57
                char *e;
1,503,163✔
58

59
                e = startswith(*i, name);
1,503,163✔
60
                if (e)
1,503,163✔
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,163,951✔
140
        STRV_FOREACH(k, l)
28,907,687✔
141
                free(*k);
19,743,736✔
142

143
        return mfree(l);
9,163,951✔
144
}
145

146
char** strv_free_erase(char **l) {
96,128✔
147
        STRV_FOREACH(i, l)
109,437✔
148
                erase_and_freep(i);
13,309✔
149

150
        return mfree(l);
96,128✔
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) {
143,985✔
163
        _cleanup_strv_free_ char **result = NULL;
143,985✔
164
        char **k;
143,985✔
165

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

170
        k = result;
171
        STRV_FOREACH(i, l) {
823,086✔
172
                if (n == 0)
679,107✔
173
                        break;
174

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

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

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

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

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

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

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

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

207
        STRV_FOREACH(i, l)
285,548,101✔
208
                n++;
272,100,179✔
209

210
        return n;
13,447,922✔
211
}
212

213
char** strv_new_ap(const char *x, va_list ap) {
64,705✔
214
        _cleanup_strv_free_ char **a = NULL;
64,705✔
215
        size_t n = 0, i = 0;
64,705✔
216
        va_list aq;
64,705✔
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);
64,705✔
224
        for (const char *s = x; s; s = va_arg(aq, const char*)) {
295,445✔
225
                if (s == STRV_IGNORE)
230,740✔
226
                        continue;
6,889✔
227

228
                n++;
223,851✔
229
        }
230
        va_end(aq);
64,705✔
231

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

236
        for (const char *s = x; s; s = va_arg(ap, const char*)) {
295,445✔
237
                if (s == STRV_IGNORE)
230,740✔
238
                        continue;
6,889✔
239

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

244
                i++;
223,851✔
245
        }
246

247
        a[i] = NULL;
64,705✔
248

249
        return TAKE_PTR(a);
64,705✔
250
}
251

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

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

260
        return r;
51,579✔
261
}
262

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

266
        assert(a);
23,779✔
267

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

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

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

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

283
        STRV_FOREACH(s, b) {
47,696✔
284
                if (filter_duplicates && strv_contains(t, *s))
27,544✔
285
                        continue;
47✔
286

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

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

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

297
        return (int) i;
20,152✔
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) {
177,117✔
306
        _cleanup_strv_free_ char **b_consume = b;
177,117✔
307
        size_t p, q, i;
177,117✔
308

309
        assert(a);
177,117✔
310

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

315
        p = strv_length(*a);
114,110✔
316
        if (p == 0) {
114,110✔
317
                strv_free_and_replace(*a, b_consume);
97,603✔
318

319
                if (filter_duplicates)
97,603✔
320
                        strv_uniq(*a);
27,061✔
321

322
                return strv_length(*a);
97,603✔
323
        }
324

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

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

332
        t[p] = NULL;
16,507✔
333
        *a = t;
16,507✔
334

335
        if (!filter_duplicates) {
16,507✔
336
                *mempcpy_typesafe(t + p, b, q) = NULL;
15,860✔
337
                i = q;
15,860✔
338
        } else {
339
                i = 0;
340

341
                STRV_FOREACH(s, b) {
1,852✔
342
                        if (strv_contains(t, *s)) {
1,205✔
343
                                free(*s);
197✔
344
                                continue;
197✔
345
                        }
346

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

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

354
        assert(i <= q);
16,507✔
355

356
        b_consume = mfree(b_consume);
16,507✔
357

358
        return (int) i;
16,507✔
359
}
360

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

364
        assert(a);
68,662✔
365

366
        STRV_FOREACH(s, b) {
341,579✔
367
                char *v;
272,917✔
368

369
                v = strjoin(strempty(prefix), *s, suffix);
545,832✔
370
                if (!v)
272,917✔
371
                        return -ENOMEM;
372

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

378
        return 0;
379
}
380

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

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

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

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

405
char** strv_split_newlines(const char *s) {
59,334✔
406
        char **ret;
59,334✔
407

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

411
        return ret;
59,334✔
412
}
413

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

419
        assert(t);
933,729✔
420
        assert(s);
933,729✔
421

422
        for (;;) {
18,502,783✔
423
                _cleanup_free_ char *word = NULL;
8,784,536✔
424

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

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

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

438
        if (!l) {
933,720✔
439
                l = new0(char*, 1);
73,264✔
440
                if (!l)
73,264✔
441
                        return -ENOMEM;
442
        }
443

444
        *t = TAKE_PTR(l);
933,720✔
445

446
        return (int) n;
933,720✔
447
}
448

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

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

455
        return ret;
18,266✔
456
}
457

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

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

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

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

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

476
int strv_split_and_extend(char ***t, const char *s, const char *separators, bool filter_duplicates) {
981✔
477
        return strv_split_and_extend_full(t, s, separators, filter_duplicates, 0);
981✔
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) {
151,446✔
533
        char *r, *e;
151,446✔
534
        size_t n, k, m;
151,446✔
535

536
        if (!separator)
151,446✔
537
                separator = " ";
1,306✔
538

539
        k = strlen(separator);
151,446✔
540
        m = strlen_ptr(prefix);
151,446✔
541

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

545
        n = 0;
546
        STRV_FOREACH(s, l) {
729,541✔
547
                if (s != l)
578,095✔
548
                        n += k;
429,197✔
549

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

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

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

559
        e = r;
560
        STRV_FOREACH(s, l) {
729,541✔
561
                if (s != l)
578,095✔
562
                        e = stpcpy(e, separator);
429,197✔
563

564
                if (prefix)
578,095✔
565
                        e = stpcpy(e, prefix);
17✔
566

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

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

579
        *e = 0;
151,446✔
580

581
        return r;
151,446✔
582
}
583

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

593
        if (!value)
9,915,993✔
594
                return 0;
595

596
        size_t size = n ? *n : SIZE_MAX;
9,915,991✔
597
        if (size == SIZE_MAX)
309,867✔
598
                size = strv_length(*l);
9,606,143✔
599

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

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

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

611
        *l = c;
9,915,991✔
612
        if (n)
9,915,991✔
613
                *n = size + 1;
309,867✔
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) {
6,974✔
648
        char **c;
6,974✔
649
        size_t n, m;
6,974✔
650

651
        assert(l);
6,974✔
652

653
        if (!value)
6,974✔
654
                return 0;
655

656
        n = strv_length(*l);
6,973✔
657
        position = MIN(position, n);
6,973✔
658

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

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

668
        if (n > position)
6,973✔
669
                memmove(c + position + 1, c + position, (n - position) * sizeof(char*));
2,960✔
670

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

674
        *l = c;
6,973✔
675
        return 0;
6,973✔
676
}
677

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

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

685
        return r;
9,852,340✔
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) {
6,968✔
701
        int r;
6,968✔
702

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

707
        return r;
6,968✔
708
}
709

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

713
        if (!value)
5,755✔
714
                return 0;
715

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

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

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

726
        if (!value)
1,611,345✔
727
                return 0;
728

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

733
        return strv_consume_with_size(l, n, v);
1,607,301✔
734
}
735

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

741
        assert(l);
3,627✔
742

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

745
        r = 0;
3,627✔
746
        va_start(ap, value);
3,627✔
747
        for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
11,617✔
748
                if (!s)
7,990✔
749
                        continue;
37✔
750

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

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

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

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

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

785
        if (r < 0) {
3,627✔
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,627✔
793
        return 0;
3,627✔
794
}
795

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

800
        STRV_FOREACH(i, l)
390,330✔
801
                strv_remove(i+1, *i);
314,075✔
802

803
        return l;
76,255✔
804
}
805

806
bool strv_is_uniq(char * const *l) {
84,602✔
807
        STRV_FOREACH(i, l)
181,356✔
808
                if (strv_contains(i+1, *i))
96,755✔
809
                        return false;
810

811
        return true;
812
}
813

814
char** strv_remove(char **l, const char *s) {
314,167✔
815
        char **f, **t;
314,167✔
816

817
        if (!l)
314,167✔
818
                return NULL;
819

820
        assert(s);
314,167✔
821

822
        /* Drops every occurrence of s in the string list, edits
823
         * in-place. */
824

825
        for (f = t = l; *f; f++)
1,696,735✔
826
                if (streq(*f, s))
1,382,568✔
827
                        free(*f);
41,650✔
828
                else
829
                        *(t++) = *f;
1,340,918✔
830

831
        *t = NULL;
314,167✔
832
        return l;
314,167✔
833
}
834

835
bool strv_overlap(char * const *a, char * const *b) {
947✔
836
        POINTER_MAY_BE_NULL(a);
947✔
837
        POINTER_MAY_BE_NULL(b);
947✔
838

839
        STRV_FOREACH(i, a)
1,684✔
840
                if (strv_contains(b, *i))
950✔
841
                        return true;
842

843
        return false;
844
}
845

846
static int str_compare(char * const *a, char * const *b) {
859,106✔
847
        /* This is called from qsort()s inner loops. Correctly implemented qsort will never pass NULL so we
848
           just suppress the check via POINTER_MAY_BE_NULL instead of assert() to avoid the runtime cost. */
849
        POINTER_MAY_BE_NULL(a);
859,106✔
850
        POINTER_MAY_BE_NULL(b);
859,106✔
851

852
        return strcmp(*a, *b);
859,106✔
853
}
854

855
char** strv_sort(char **l) {
35,332✔
856
        typesafe_qsort(l, strv_length(l), str_compare);
35,332✔
857
        return l;
35,332✔
858
}
859

860
char** strv_sort_uniq(char **l) {
39,077✔
861
        if (strv_isempty(l))
39,077✔
862
                return l;
863

864
        char **tail = strv_sort(l), *prev = NULL;
340✔
865
        STRV_FOREACH(i, l)
4,399✔
866
                if (streq_ptr(*i, prev))
3,719✔
867
                        free(*i);
29✔
868
                else
869
                        *(tail++) = prev = *i;
3,690✔
870

871
        *tail = NULL;
340✔
872
        return l;
340✔
873
}
874

875
int strv_compare(char * const *a, char * const *b) {
10,666✔
876
        int r;
10,666✔
877

878
        POINTER_MAY_BE_NULL(a);
10,666✔
879
        POINTER_MAY_BE_NULL(b);
10,666✔
880

881
        if (strv_isempty(a)) {
10,666✔
882
                if (strv_isempty(b))
1,680✔
883
                        return 0;
1,605✔
884
                else
885
                        return -1;
886
        }
887

888
        if (strv_isempty(b))
8,986✔
889
                return 1;
890

891
        for ( ; *a || *b; ++a, ++b) {
20,658✔
892
                r = strcmp_ptr(*a, *b);
11,847✔
893
                if (r != 0)
11,847✔
894
                        return r;
895
        }
896

897
        return 0;
898
}
899

900
bool strv_equal_ignore_order(char * const *a, char * const *b) {
487✔
901

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

906
        if (a == b)
487✔
907
                return true;
908

909
        STRV_FOREACH(i, a)
925✔
910
                if (!strv_contains(b, *i))
480✔
911
                        return false;
912

913
        STRV_FOREACH(i, b)
921✔
914
                if (!strv_contains(a, *i))
480✔
915
                        return false;
916

917
        return true;
918
}
919

920
void strv_print_full(char * const *l, const char *prefix) {
110✔
921
        STRV_FOREACH(s, l)
3,087✔
922
                printf("%s%s\n", strempty(prefix), *s);
5,954✔
923
}
110✔
924

925
int strv_extendf_with_size(char ***l, size_t *n, const char *format, ...) {
1,361,244✔
926
        va_list ap;
1,361,244✔
927
        char *x;
1,361,244✔
928
        int r;
1,361,244✔
929

930
        va_start(ap, format);
1,361,244✔
931
        r = vasprintf(&x, format, ap);
1,361,244✔
932
        va_end(ap);
1,361,244✔
933

934
        if (r < 0)
1,361,244✔
935
                return -ENOMEM;
1,361,244✔
936

937
        return strv_consume_with_size(l, n, x);
1,361,244✔
938
}
939

940
int strv_extend_joined_with_size_sentinel(char ***l, size_t *n, ...) {
57,051✔
941
        va_list ap;
57,051✔
942

943
        va_start(ap, n);
57,051✔
944
        char *x = strextendv_with_separator(/* x= */ NULL, /* separator= */ NULL, ap);
57,051✔
945
        va_end(ap);
57,051✔
946
        if (!x)
57,051✔
947
                return -ENOMEM;
57,051✔
948

949
        return strv_consume_with_size(l, n, x);
57,051✔
950
}
951

952
char* startswith_strv_internal(const char *s, char * const *l) {
13,114,799✔
953
        STRV_FOREACH(i, l) {
39,917,996✔
954
                char *found = (char*) startswith(s, *i);
26,927,718✔
955
                if (found)
26,927,718✔
956
                        return found;
957
        }
958

959
        return NULL;
960
}
961

962
char* endswith_strv_internal(const char *s, char * const *l) {
5,900,099✔
963
        STRV_FOREACH(i, l) {
17,703,682✔
964
                char *found = (char*) endswith(s, *i);
11,839,523✔
965
                if (found)
11,839,523✔
966
                        return found;
967
        }
968

969
        return NULL;
970
}
971

972
char** strv_reverse(char **l) {
844✔
973
        size_t n;
844✔
974

975
        n = strv_length(l);
844✔
976
        if (n <= 1)
844✔
977
                return l;
978

979
        for (size_t i = 0; i < n / 2; i++)
294✔
980
                SWAP_TWO(l[i], l[n-1-i]);
151✔
981

982
        return l;
983
}
984

985
char** strv_shell_escape(char **l, const char *bad) {
3✔
986
        /* Escapes every character in every string in l that is in bad,
987
         * edits in-place, does not roll-back on error. */
988

989
        STRV_FOREACH(s, l) {
8✔
990
                char *v;
5✔
991

992
                v = shell_escape(*s, bad);
5✔
993
                if (!v)
5✔
994
                        return NULL;
3✔
995

996
                free_and_replace(*s, v);
5✔
997
        }
998

999
        return l;
1000
}
1001

1002
bool strv_fnmatch_full(
86,735✔
1003
                char* const* patterns,
1004
                const char *s,
1005
                int flags,
1006
                size_t *ret_matched_pos) {
1007

1008
        assert(s);
86,735✔
1009

1010
        if (patterns)
86,735✔
1011
                for (size_t i = 0; patterns[i]; i++)
153,983✔
1012
                        /* NB: We treat all fnmatch() errors as equivalent to FNM_NOMATCH, i.e. if fnmatch() fails to
1013
                         * process the pattern for some reason we'll consider this equivalent to non-matching. */
1014
                        if (fnmatch(patterns[i], s, flags) == 0) {
90,242✔
1015
                                if (ret_matched_pos)
19,738✔
1016
                                        *ret_matched_pos = i;
1,637✔
1017
                                return true;
1018
                        }
1019

1020
        if (ret_matched_pos)
66,997✔
1021
                *ret_matched_pos = SIZE_MAX;
24,551✔
1022

1023
        return false;
1024
}
1025

1026
char** strv_skip(char **l, size_t n) {
40,970✔
1027
        while (n > 0) {
96,894✔
1028
                if (strv_isempty(l))
57,456✔
1029
                        return NULL;
1030

1031
                l++, n--;
55,924✔
1032
        }
1033

1034
        /* To simplify callers, always return NULL instead of a zero-item array. */
1035
        if (strv_isempty(l))
39,438✔
1036
                return NULL;
5,428✔
1037
        return l;
1038
}
1039

1040
int strv_extend_n(char ***l, const char *value, size_t n) {
660✔
1041
        size_t i, k;
660✔
1042
        char **nl;
660✔
1043

1044
        assert(l);
660✔
1045

1046
        if (!value)
660✔
1047
                return 0;
1048
        if (n == 0)
660✔
1049
                return 0;
1050

1051
        /* Adds the value n times to l */
1052

1053
        k = strv_length(*l);
659✔
1054
        if (n >= SIZE_MAX - k)
659✔
1055
                return -ENOMEM;
1056

1057
        nl = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(k + n + 1), sizeof(char *));
659✔
1058
        if (!nl)
659✔
1059
                return -ENOMEM;
1060

1061
        *l = nl;
659✔
1062

1063
        for (i = k; i < k + n; i++) {
1,357✔
1064
                nl[i] = strdup(value);
698✔
1065
                if (!nl[i])
698✔
1066
                        goto rollback;
×
1067
        }
1068
        nl[i] = NULL;
659✔
1069

1070
        return 0;
659✔
1071

1072
rollback:
×
1073
        for (size_t j = k; j < i; j++)
×
1074
                free(nl[j]);
×
1075
        nl[k] = NULL;
×
1076

1077
        return -ENOMEM;
×
1078
}
1079

1080
int strv_extend_assignment(char ***l, const char *lhs, const char *rhs) {
8,001,472✔
1081
        char *j;
8,001,472✔
1082

1083
        assert(l);
8,001,472✔
1084
        assert(lhs);
8,001,472✔
1085

1086
        if (!rhs) /* value is optional, in which case we suppress the field */
8,001,472✔
1087
                return 0;
1088

1089
        j = strjoin(lhs, "=", rhs);
5,666,041✔
1090
        if (!j)
5,666,041✔
1091
                return -ENOMEM;
1092

1093
        return strv_consume(l, j);
5,666,041✔
1094
}
1095

1096
int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) {
16,315✔
1097
        bool b = false;
16,315✔
1098
        int r;
16,315✔
1099

1100
        assert(f);
16,315✔
1101

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

1104
        if (!space)
16,315✔
1105
                space = &b;
15✔
1106

1107
        STRV_FOREACH(s, l) {
16,753✔
1108
                r = fputs_with_separator(f, *s, separator, space);
438✔
1109
                if (r < 0)
438✔
1110
                        return r;
16,315✔
1111
        }
1112

1113
        return 0;
1114
}
1115

1116
void string_strv_hashmap_remove(Hashmap *h, const char *key, const char *value) {
515✔
1117
        assert(key);
515✔
1118

1119
        if (value) {
515✔
1120
                char **l = hashmap_get(h, key);
515✔
1121
                if (!l)
515✔
1122
                        return;
489✔
1123

1124
                strv_remove(l, value);
30✔
1125
                if (!strv_isempty(l))
515✔
1126
                        return;
1127
        }
1128

1129
        _unused_ _cleanup_free_ char *key_free = NULL;
26✔
1130
        strv_free(hashmap_remove2(h, key, (void**) &key_free));
26✔
1131
}
1132

1133
void string_strv_ordered_hashmap_remove(OrderedHashmap *h, const char *key, const char *value) {
4✔
1134
        string_strv_hashmap_remove(PLAIN_HASHMAP(h), key, value);
4✔
1135
}
4✔
1136

1137
static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const char *value) {
526,177✔
1138
        char **l;
526,177✔
1139
        int r;
526,177✔
1140

1141
        assert(h);
526,177✔
1142
        assert(key);
526,177✔
1143
        assert(value);
526,177✔
1144

1145
        l = hashmap_get(h, key);
526,177✔
1146
        if (l) {
526,177✔
1147
                /* A list for this key already exists, let's append to it if it is not listed yet */
1148
                if (strv_contains(l, value))
33,425✔
1149
                        return 0;
526,177✔
1150

1151
                r = strv_extend(&l, value);
33,412✔
1152
                if (r < 0)
33,412✔
1153
                        return r;
1154

1155
                assert_se(hashmap_update(h, key, l) >= 0);
33,412✔
1156
        } else {
1157
                /* No list for this key exists yet, create one */
1158
                _cleanup_strv_free_ char **l2 = NULL;
×
1159
                _cleanup_free_ char *t = NULL;
492,752✔
1160

1161
                t = strdup(key);
492,752✔
1162
                if (!t)
492,752✔
1163
                        return -ENOMEM;
1164

1165
                r = strv_extend(&l2, value);
492,752✔
1166
                if (r < 0)
492,752✔
1167
                        return r;
1168

1169
                r = hashmap_put(h, t, l2);
492,752✔
1170
                if (r < 0)
492,752✔
1171
                        return r;
1172

1173
                TAKE_PTR(t);
492,752✔
1174
                TAKE_PTR(l2);
492,752✔
1175
        }
1176

1177
        return 1;
1178
}
1179

1180
int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value) {
526,151✔
1181
        int r;
526,151✔
1182

1183
        assert(h);
526,151✔
1184
        assert(key);
526,151✔
1185
        assert(value);
526,151✔
1186

1187
        r = hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free);
526,151✔
1188
        if (r < 0)
526,151✔
1189
                return r;
1190

1191
        return string_strv_hashmap_put_internal(*h, key, value);
526,151✔
1192
}
1193

1194
int string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value) {
26✔
1195
        int r;
26✔
1196

1197
        assert(h);
26✔
1198
        assert(key);
26✔
1199
        assert(value);
26✔
1200

1201
        r = ordered_hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free);
26✔
1202
        if (r < 0)
26✔
1203
                return r;
1204

1205
        return string_strv_hashmap_put_internal(PLAIN_HASHMAP(*h), key, value);
26✔
1206
}
1207

1208
int strv_rebreak_lines(char **l, size_t width, char ***ret) {
56,094✔
1209
        _cleanup_strv_free_ char **broken = NULL;
56,094✔
1210
        int r;
56,094✔
1211

1212
        assert(ret);
56,094✔
1213

1214
        /* Implements a simple UTF-8 line breaking algorithm
1215
         *
1216
         * Goes through all entries in *l, and line-breaks each line that is longer than the specified
1217
         * character width. Breaks at the end of words/beginning of whitespace. Lines that do not contain whitespace are not
1218
         * broken. Retains whitespace at beginning of lines, removes it at end of lines. */
1219

1220
        if (width == SIZE_MAX) { /* NOP? */
56,094✔
1221
                broken = strv_copy(l);
7,608✔
1222
                if (!broken)
7,608✔
1223
                        return -ENOMEM;
1224

1225
                *ret = TAKE_PTR(broken);
7,608✔
1226
                return 0;
7,608✔
1227
        }
1228

1229
        STRV_FOREACH(i, l) {
97,098✔
1230
                const char *start = *i, *whitespace_begin = NULL, *whitespace_end = NULL;
1231
                bool in_prefix = true; /* still in the whitespace in the beginning of the line? */
1232
                size_t w = 0;
1233

1234
                for (const char *p = start; *p != 0; p = utf8_next_char(p)) {
2,724,437✔
1235
                        if (strchr(NEWLINE, *p)) {
2,675,825✔
1236
                                in_prefix = true;
1237
                                whitespace_begin = whitespace_end = NULL;
1238
                                w = 0;
1239
                        } else if (strchr(WHITESPACE, *p)) {
2,675,825✔
1240
                                if (!in_prefix && (!whitespace_begin || whitespace_end)) {
244,776✔
1241
                                        whitespace_begin = p;
244,179✔
1242
                                        whitespace_end = NULL;
244,179✔
1243
                                }
1244
                        } else {
1245
                                if (whitespace_begin && !whitespace_end)
2,431,049✔
1246
                                        whitespace_end = p;
244,170✔
1247

1248
                                in_prefix = false;
1249
                        }
1250

1251
                        int cw = utf8_char_console_width(p);
2,675,825✔
1252
                        if (cw < 0) {
2,675,825✔
1253
                                log_debug_errno(cw, "Comment to line break contains invalid UTF-8, ignoring.");
×
1254
                                cw = 1;
1255
                        }
1256

1257
                        w += cw;
2,675,825✔
1258

1259
                        if (w > width && whitespace_begin && whitespace_end) {
2,675,825✔
1260
                                _cleanup_free_ char *truncated = NULL;
×
1261

1262
                                truncated = strndup(start, whitespace_begin - start);
12,401✔
1263
                                if (!truncated)
12,401✔
1264
                                        return -ENOMEM;
1265

1266
                                r = strv_consume(&broken, TAKE_PTR(truncated));
12,401✔
1267
                                if (r < 0)
12,401✔
1268
                                        return r;
1269

1270
                                p = start = whitespace_end;
1271
                                whitespace_begin = whitespace_end = NULL;
1272
                                w = cw;
1273
                        }
1274
                }
1275

1276
                /* Process rest of the line */
1277
                assert(start);
48,612✔
1278
                if (in_prefix) /* Never seen anything non-whitespace? Generate empty line! */
48,612✔
1279
                        r = strv_extend(&broken, "");
1✔
1280
                else if (whitespace_begin && !whitespace_end) { /* Ends in whitespace? Chop it off! */
48,611✔
1281
                        _cleanup_free_ char *truncated = strndup(start, whitespace_begin - start);
×
1282
                        if (!truncated)
9✔
1283
                                return -ENOMEM;
×
1284

1285
                        r = strv_consume(&broken, TAKE_PTR(truncated));
9✔
1286
                } else /* Otherwise use line as is */
1287
                        r = strv_extend(&broken, start);
48,602✔
1288
                if (r < 0)
48,612✔
1289
                        return r;
1290
        }
1291

1292
        *ret = TAKE_PTR(broken);
48,486✔
1293
        return 0;
48,486✔
1294
}
1295

1296
char** strv_filter_prefix(char * const *l, const char *prefix) {
4✔
1297

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

1300
        if (isempty(prefix))
4✔
1301
                return strv_copy(l);
5✔
1302

1303
        _cleanup_strv_free_ char **f = NULL;
3✔
1304
        size_t sz = 0;
3✔
1305

1306
        STRV_FOREACH(i, l) {
24✔
1307
                if (!startswith(*i, prefix))
21✔
1308
                        continue;
16✔
1309

1310
                if (strv_extend_with_size(&f, &sz, *i) < 0)
5✔
1311
                        return NULL;
1312
        }
1313

1314
        return TAKE_PTR(f);
3✔
1315
}
1316

1317
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