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

systemd / systemd / 19020191358

02 Nov 2025 05:04PM UTC coverage: 72.222% (-0.02%) from 72.241%
19020191358

push

github

web-flow
Enhance docs for ukify and direct kernel boots (#39516)

305246 of 422650 relevant lines covered (72.22%)

1085243.28 hits per line

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

95.62
/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) {
22,426,254✔
21
        assert(name);
22,426,254✔
22

23
        STRV_FOREACH(i, l)
196,194,455✔
24
                if (streq(*i, name))
175,598,723✔
25
                        return *i;
26

27
        return NULL;
28
}
29

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

33
        STRV_FOREACH(i, l)
5,320,986✔
34
                if (strcaseeq(*i, name))
4,670,377✔
35
                        return *i;
36

37
        return NULL;
38
}
39

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

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

47
        return NULL;
48
}
49

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

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

56
        STRV_FOREACH(i, l) {
1,259,409✔
57
                char *e;
641,727✔
58

59
                e = startswith(*i, name);
641,727✔
60
                if (e)
641,727✔
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) {
197✔
74
                char *e = startswith(*s, name);
168✔
75
                if (!e)
168✔
76
                        continue;
106✔
77

78
                size_t n = strlen(e);
62✔
79
                if (n < best_distance) {
62✔
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✔
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 */
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,916,739✔
140
        STRV_FOREACH(k, l)
20,923,118✔
141
                free(*k);
15,006,379✔
142

143
        return mfree(l);
5,916,739✔
144
}
145

146
char** strv_free_erase(char **l) {
70,489✔
147
        STRV_FOREACH(i, l)
78,874✔
148
                erase_and_freep(i);
8,385✔
149

150
        return mfree(l);
70,489✔
151
}
152

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

156
        FOREACH_ARRAY (i, strvs, n)
116✔
157
                strv_free(*i);
72✔
158

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

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

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

170
        k = result;
171
        STRV_FOREACH(i, l) {
863,422✔
172
                if (m == 0)
727,122✔
173
                        break;
174

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

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

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

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

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

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

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

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

207
        STRV_FOREACH(i, l)
300,316,840✔
208
                n++;
290,511,098✔
209

210
        return n;
9,805,742✔
211
}
212

213
char** strv_new_ap(const char *x, va_list ap) {
33,680✔
214
        _cleanup_strv_free_ char **a = NULL;
33,680✔
215
        size_t n = 0, i = 0;
33,680✔
216
        va_list aq;
33,680✔
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);
33,680✔
224
        for (const char *s = x; s; s = va_arg(aq, const char*)) {
198,466✔
225
                if (s == STRV_IGNORE)
164,786✔
226
                        continue;
4,831✔
227

228
                n++;
159,955✔
229
        }
230
        va_end(aq);
33,680✔
231

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

236
        for (const char *s = x; s; s = va_arg(ap, const char*)) {
198,466✔
237
                if (s == STRV_IGNORE)
164,786✔
238
                        continue;
4,831✔
239

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

244
                i++;
159,955✔
245
        }
246

247
        a[i] = NULL;
33,680✔
248

249
        return TAKE_PTR(a);
33,680✔
250
}
251

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

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

260
        return r;
26,480✔
261
}
262

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

266
        assert(a);
22,191✔
267

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

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

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

280
        t[p] = NULL;
19,436✔
281
        *a = t;
19,436✔
282

283
        STRV_FOREACH(s, b) {
44,340✔
284
                if (filter_duplicates && strv_contains(t, *s))
24,904✔
285
                        continue;
75✔
286

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

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

295
        assert(i <= q);
19,436✔
296

297
        return (int) i;
19,436✔
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) {
120,822✔
306
        _cleanup_strv_free_ char **b_consume = b;
120,822✔
307
        size_t p, q, i;
120,822✔
308

309
        assert(a);
120,822✔
310

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

315
        p = strv_length(*a);
65,765✔
316
        if (p == 0) {
65,765✔
317
                strv_free_and_replace(*a, b_consume);
54,898✔
318

319
                if (filter_duplicates)
54,898✔
320
                        strv_uniq(*a);
11,974✔
321

322
                return strv_length(*a);
54,898✔
323
        }
324

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

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

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

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

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

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

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

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

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

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

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

364
        assert(a);
62,678✔
365

366
        STRV_FOREACH(s, b) {
311,990✔
367
                char *v;
249,312✔
368

369
                v = strjoin(strempty(prefix), *s, suffix);
498,534✔
370
                if (!v)
249,312✔
371
                        return -ENOMEM;
372

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

378
        return 0;
379
}
380

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

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

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

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

405
char** strv_split_newlines(const char *s) {
49,787✔
406
        char **ret;
49,787✔
407

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

411
        return ret;
49,787✔
412
}
413

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

419
        assert(t);
755,487✔
420
        assert(s);
755,487✔
421

422
        for (;;) {
14,016,227✔
423
                _cleanup_free_ char *word = NULL;
6,630,379✔
424

425
                r = extract_first_word(&s, &word, separators, flags);
7,385,857✔
426
                if (r < 0)
7,385,857✔
427
                        return r;
428
                if (r == 0)
7,385,848✔
429
                        break;
430

431
                if (!GREEDY_REALLOC(l, n + 2))
6,630,370✔
432
                        return -ENOMEM;
433

434
                l[n++] = TAKE_PTR(word);
6,630,370✔
435
                l[n] = NULL;
6,630,370✔
436
        }
437

438
        if (!l) {
755,478✔
439
                l = new0(char*, 1);
76,934✔
440
                if (!l)
76,934✔
441
                        return -ENOMEM;
442
        }
443

444
        *t = TAKE_PTR(l);
755,478✔
445

446
        return (int) n;
755,478✔
447
}
448

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

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

455
        return ret;
14,865✔
456
}
457

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

536
        if (!separator)
65,236✔
537
                separator = " ";
1,240✔
538

539
        k = strlen(separator);
65,236✔
540
        m = strlen_ptr(prefix);
65,236✔
541

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

545
        n = 0;
546
        STRV_FOREACH(s, l) {
265,201✔
547
                if (s != l)
199,965✔
548
                        n += k;
136,704✔
549

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

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

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

559
        e = r;
560
        STRV_FOREACH(s, l) {
265,201✔
561
                if (s != l)
199,965✔
562
                        e = stpcpy(e, separator);
136,704✔
563

564
                if (prefix)
199,965✔
565
                        e = stpcpy(e, prefix);
17✔
566

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

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

579
        *e = 0;
65,236✔
580

581
        return r;
65,236✔
582
}
583

584
int strv_push_with_size(char ***l, size_t *n, char *value) {
7,355,746✔
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,355,746✔
591
                return 0;
592

593
        size_t size = n ? *n : SIZE_MAX;
7,355,744✔
594
        if (size == SIZE_MAX)
172,692✔
595
                size = strv_length(*l);
7,183,071✔
596

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

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

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

608
        *l = c;
7,355,744✔
609
        if (n)
7,355,744✔
610
                *n = size + 1;
172,692✔
611
        return 0;
612
}
613

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

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

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

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

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

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

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

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

646
        assert(l);
5,202✔
647

648
        if (!value)
5,202✔
649
                return 0;
650

651
        n = strv_length(*l);
5,201✔
652
        position = MIN(position, n);
5,201✔
653

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

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

663
        if (n > position)
5,201✔
664
                memmove(c + position + 1, c + position, (n - position) * sizeof(char*));
1,284✔
665

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

669
        *l = c;
5,201✔
670
        return 0;
5,201✔
671
}
672

673
int strv_consume_with_size(char ***l, size_t *n, char *value) {
7,307,990✔
674
        int r;
7,307,990✔
675

676
        r = strv_push_with_size(l, n, value);
7,307,990✔
677
        if (r < 0)
7,307,990✔
678
                free(value);
×
679

680
        return r;
7,307,990✔
681
}
682

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

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

692
        return r;
337✔
693
}
694

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

698
        r = strv_push_prepend(l, value);
5,196✔
699
        if (r < 0)
5,196✔
700
                free(value);
×
701

702
        return r;
5,196✔
703
}
704

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

708
        if (!value)
4,300✔
709
                return 0;
710

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

715
        return strv_consume_prepend(l, v);
4,184✔
716
}
717

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

721
        if (!value)
1,263,463✔
722
                return 0;
723

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

728
        return strv_consume_with_size(l, n, v);
1,259,489✔
729
}
730

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

736
        assert(l);
423✔
737

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

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

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

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

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

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

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

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

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

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

795
        STRV_FOREACH(i, l)
273,729✔
796
                strv_remove(i+1, *i);
226,464✔
797

798
        return l;
47,265✔
799
}
800

801
bool strv_is_uniq(char * const *l) {
18,567✔
802
        STRV_FOREACH(i, l)
43,704✔
803
                if (strv_contains(i+1, *i))
25,138✔
804
                        return false;
805

806
        return true;
807
}
808

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

812
        if (!l)
226,534✔
813
                return NULL;
814

815
        assert(s);
226,534✔
816

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

820
        for (f = t = l; *f; f++)
1,218,986✔
821
                if (streq(*f, s))
992,452✔
822
                        free(*f);
29,974✔
823
                else
824
                        *(t++) = *f;
962,478✔
825

826
        *t = NULL;
226,534✔
827
        return l;
226,534✔
828
}
829

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

835
        return false;
836
}
837

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

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

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

851
        char **tail = strv_sort(l), *prev = NULL;
257✔
852
        STRV_FOREACH(i, l)
3,721✔
853
                if (streq_ptr(*i, prev))
3,464✔
854
                        free(*i);
27✔
855
                else
856
                        *(tail++) = prev = *i;
3,437✔
857

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

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

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

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

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

881
        return 0;
882
}
883

884
bool strv_equal_ignore_order(char * const *a, char * const *b) {
458✔
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)
458✔
891
                return true;
892

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

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

901
        return true;
902
}
903

904
void strv_print_full(char * const *l, const char *prefix) {
100✔
905
        STRV_FOREACH(s, l)
3,012✔
906
                printf("%s%s\n", strempty(prefix), *s);
5,824✔
907
}
100✔
908

909
int strv_extendf_with_size(char ***l, size_t *n, const char *format, ...) {
1,016,142✔
910
        va_list ap;
1,016,142✔
911
        char *x;
1,016,142✔
912
        int r;
1,016,142✔
913

914
        va_start(ap, format);
1,016,142✔
915
        r = vasprintf(&x, format, ap);
1,016,142✔
916
        va_end(ap);
1,016,142✔
917

918
        if (r < 0)
1,016,142✔
919
                return -ENOMEM;
1,016,142✔
920

921
        return strv_consume_with_size(l, n, x);
1,016,142✔
922
}
923

924
int strv_extend_joined_with_size_sentinel(char ***l, size_t *n, ...) {
49,045✔
925
        va_list ap;
49,045✔
926

927
        va_start(ap, n);
49,045✔
928
        char *x = strextendv_with_separator(/* x= */ NULL, /* separator=*/ NULL, ap);
49,045✔
929
        va_end(ap);
49,045✔
930
        if (!x)
49,045✔
931
                return -ENOMEM;
49,045✔
932

933
        return strv_consume_with_size(l, n, x);
49,045✔
934
}
935

936
char* startswith_strv(const char *s, char * const *l) {
11,714,484✔
937
        STRV_FOREACH(i, l) {
35,787,568✔
938
                char *found = startswith(s, *i);
24,156,413✔
939
                if (found)
24,156,413✔
940
                        return found;
941
        }
942

943
        return NULL;
944
}
945

946
char* endswith_strv(const char *s, char * const *l) {
2,951,920✔
947
        STRV_FOREACH(i, l) {
8,837,004✔
948
                char *found = endswith(s, *i);
5,915,234✔
949
                if (found)
5,915,234✔
950
                        return found;
951
        }
952

953
        return NULL;
954
}
955

956
char** strv_reverse(char **l) {
844✔
957
        size_t n;
844✔
958

959
        n = strv_length(l);
844✔
960
        if (n <= 1)
844✔
961
                return l;
962

963
        for (size_t i = 0; i < n / 2; i++)
294✔
964
                SWAP_TWO(l[i], l[n-1-i]);
151✔
965

966
        return l;
967
}
968

969
char** strv_shell_escape(char **l, const char *bad) {
3✔
970
        /* Escapes every character in every string in l that is in bad,
971
         * edits in-place, does not roll-back on error. */
972

973
        STRV_FOREACH(s, l) {
8✔
974
                char *v;
5✔
975

976
                v = shell_escape(*s, bad);
5✔
977
                if (!v)
5✔
978
                        return NULL;
3✔
979

980
                free_and_replace(*s, v);
5✔
981
        }
982

983
        return l;
984
}
985

986
bool strv_fnmatch_full(
74,021✔
987
                char* const* patterns,
988
                const char *s,
989
                int flags,
990
                size_t *ret_matched_pos) {
991

992
        assert(s);
74,021✔
993

994
        if (patterns)
74,021✔
995
                for (size_t i = 0; patterns[i]; i++)
135,082✔
996
                        /* NB: We treat all fnmatch() errors as equivalent to FNM_NOMATCH, i.e. if fnmatch() fails to
997
                         * process the pattern for some reason we'll consider this equivalent to non-matching. */
998
                        if (fnmatch(patterns[i], s, flags) == 0) {
77,251✔
999
                                if (ret_matched_pos)
13,123✔
1000
                                        *ret_matched_pos = i;
1,612✔
1001
                                return true;
13,123✔
1002
                        }
1003

1004
        if (ret_matched_pos)
60,898✔
1005
                *ret_matched_pos = SIZE_MAX;
24,346✔
1006

1007
        return false;
1008
}
1009

1010
char** strv_skip(char **l, size_t n) {
26,293✔
1011
        while (n > 0) {
75,558✔
1012
                if (strv_isempty(l))
50,357✔
1013
                        return NULL;
1014

1015
                l++, n--;
49,265✔
1016
        }
1017

1018
        /* To simplify callers, always return NULL instead of a zero-item array. */
1019
        if (strv_isempty(l))
25,201✔
1020
                return NULL;
11,899✔
1021
        return l;
1022
}
1023

1024
int strv_extend_n(char ***l, const char *value, size_t n) {
582✔
1025
        size_t i, k;
582✔
1026
        char **nl;
582✔
1027

1028
        assert(l);
582✔
1029

1030
        if (!value)
582✔
1031
                return 0;
1032
        if (n == 0)
582✔
1033
                return 0;
1034

1035
        /* Adds the value n times to l */
1036

1037
        k = strv_length(*l);
581✔
1038
        if (n >= SIZE_MAX - k)
581✔
1039
                return -ENOMEM;
1040

1041
        nl = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(k + n + 1), sizeof(char *));
581✔
1042
        if (!nl)
581✔
1043
                return -ENOMEM;
1044

1045
        *l = nl;
581✔
1046

1047
        for (i = k; i < k + n; i++) {
1,202✔
1048
                nl[i] = strdup(value);
621✔
1049
                if (!nl[i])
621✔
1050
                        goto rollback;
×
1051
        }
1052
        nl[i] = NULL;
581✔
1053

1054
        return 0;
581✔
1055

1056
rollback:
×
1057
        for (size_t j = k; j < i; j++)
×
1058
                free(nl[j]);
×
1059
        nl[k] = NULL;
×
1060

1061
        return -ENOMEM;
×
1062
}
1063

1064
int strv_extend_assignment(char ***l, const char *lhs, const char *rhs) {
5,629,386✔
1065
        char *j;
5,629,386✔
1066

1067
        assert(l);
5,629,386✔
1068
        assert(lhs);
5,629,386✔
1069

1070
        if (!rhs) /* value is optional, in which case we suppress the field */
5,629,386✔
1071
                return 0;
1072

1073
        j = strjoin(lhs, "=", rhs);
4,041,407✔
1074
        if (!j)
4,041,407✔
1075
                return -ENOMEM;
1076

1077
        return strv_consume(l, j);
4,041,407✔
1078
}
1079

1080
int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) {
15,901✔
1081
        bool b = false;
15,901✔
1082
        int r;
15,901✔
1083

1084
        assert(f);
15,901✔
1085

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

1088
        if (!space)
15,901✔
1089
                space = &b;
15✔
1090

1091
        STRV_FOREACH(s, l) {
16,341✔
1092
                r = fputs_with_separator(f, *s, separator, space);
440✔
1093
                if (r < 0)
440✔
1094
                        return r;
15,901✔
1095
        }
1096

1097
        return 0;
1098
}
1099

1100
void string_strv_hashmap_remove(Hashmap *h, const char *key, const char *value) {
146✔
1101
        assert(key);
146✔
1102

1103
        if (value) {
146✔
1104
                char **l = hashmap_get(h, key);
146✔
1105
                if (!l)
146✔
1106
                        return;
140✔
1107

1108
                strv_remove(l, value);
10✔
1109
                if (!strv_isempty(l))
146✔
1110
                        return;
1111
        }
1112

1113
        _unused_ _cleanup_free_ char *key_free = NULL;
6✔
1114
        strv_free(hashmap_remove2(h, key, (void**) &key_free));
6✔
1115
}
1116

1117
void string_strv_ordered_hashmap_remove(OrderedHashmap *h, const char *key, const char *value) {
4✔
1118
        string_strv_hashmap_remove(PLAIN_HASHMAP(h), key, value);
4✔
1119
}
4✔
1120

1121
static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const char *value) {
266,158✔
1122
        char **l;
266,158✔
1123
        int r;
266,158✔
1124

1125
        assert(h);
266,158✔
1126
        assert(key);
266,158✔
1127
        assert(value);
266,158✔
1128

1129
        l = hashmap_get(h, key);
266,158✔
1130
        if (l) {
266,158✔
1131
                /* A list for this key already exists, let's append to it if it is not listed yet */
1132
                if (strv_contains(l, value))
8,853✔
1133
                        return 0;
266,158✔
1134

1135
                r = strv_extend(&l, value);
8,840✔
1136
                if (r < 0)
8,840✔
1137
                        return r;
1138

1139
                assert_se(hashmap_update(h, key, l) >= 0);
8,840✔
1140
        } else {
1141
                /* No list for this key exists yet, create one */
1142
                _cleanup_strv_free_ char **l2 = NULL;
×
1143
                _cleanup_free_ char *t = NULL;
257,305✔
1144

1145
                t = strdup(key);
257,305✔
1146
                if (!t)
257,305✔
1147
                        return -ENOMEM;
1148

1149
                r = strv_extend(&l2, value);
257,305✔
1150
                if (r < 0)
257,305✔
1151
                        return r;
1152

1153
                r = hashmap_put(h, t, l2);
257,305✔
1154
                if (r < 0)
257,305✔
1155
                        return r;
1156

1157
                TAKE_PTR(t);
257,305✔
1158
                TAKE_PTR(l2);
257,305✔
1159
        }
1160

1161
        return 1;
1162
}
1163

1164
int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value) {
266,132✔
1165
        int r;
266,132✔
1166

1167
        assert(h);
266,132✔
1168
        assert(key);
266,132✔
1169
        assert(value);
266,132✔
1170

1171
        r = hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free);
266,132✔
1172
        if (r < 0)
266,132✔
1173
                return r;
1174

1175
        return string_strv_hashmap_put_internal(*h, key, value);
266,132✔
1176
}
1177

1178
int string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value) {
26✔
1179
        int r;
26✔
1180

1181
        assert(h);
26✔
1182
        assert(key);
26✔
1183
        assert(value);
26✔
1184

1185
        r = ordered_hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free);
26✔
1186
        if (r < 0)
26✔
1187
                return r;
1188

1189
        return string_strv_hashmap_put_internal(PLAIN_HASHMAP(*h), key, value);
26✔
1190
}
1191

1192
int strv_rebreak_lines(char **l, size_t width, char ***ret) {
27,273✔
1193
        _cleanup_strv_free_ char **broken = NULL;
27,273✔
1194
        int r;
27,273✔
1195

1196
        assert(ret);
27,273✔
1197

1198
        /* Implements a simple UTF-8 line breaking algorithm
1199
         *
1200
         * Goes through all entries in *l, and line-breaks each line that is longer than the specified
1201
         * character width. Breaks at the end of words/beginning of whitespace. Lines that do not contain whitespace are not
1202
         * broken. Retains whitespace at beginning of lines, removes it at end of lines. */
1203

1204
        if (width == SIZE_MAX) { /* NOP? */
27,273✔
1205
                broken = strv_copy(l);
5,082✔
1206
                if (!broken)
5,082✔
1207
                        return -ENOMEM;
1208

1209
                *ret = TAKE_PTR(broken);
5,082✔
1210
                return 0;
5,082✔
1211
        }
1212

1213
        STRV_FOREACH(i, l) {
44,508✔
1214
                const char *start = *i, *whitespace_begin = NULL, *whitespace_end = NULL;
1215
                bool in_prefix = true; /* still in the whitespace in the beginning of the line? */
1216
                size_t w = 0;
1217

1218
                for (const char *p = start; *p != 0; p = utf8_next_char(p)) {
1,298,478✔
1219
                        if (strchr(NEWLINE, *p)) {
1,276,161✔
1220
                                in_prefix = true;
1221
                                whitespace_begin = whitespace_end = NULL;
1222
                                w = 0;
1223
                        } else if (strchr(WHITESPACE, *p)) {
1,276,161✔
1224
                                if (!in_prefix && (!whitespace_begin || whitespace_end)) {
131,406✔
1225
                                        whitespace_begin = p;
130,809✔
1226
                                        whitespace_end = NULL;
130,809✔
1227
                                }
1228
                        } else {
1229
                                if (whitespace_begin && !whitespace_end)
1,144,755✔
1230
                                        whitespace_end = p;
130,800✔
1231

1232
                                in_prefix = false;
1233
                        }
1234

1235
                        int cw = utf8_char_console_width(p);
1,276,161✔
1236
                        if (cw < 0) {
1,276,161✔
1237
                                log_debug_errno(cw, "Comment to line break contains invalid UTF-8, ignoring.");
×
1238
                                cw = 1;
1239
                        }
1240

1241
                        w += cw;
1,276,161✔
1242

1243
                        if (w > width && whitespace_begin && whitespace_end) {
1,276,161✔
1244
                                _cleanup_free_ char *truncated = NULL;
×
1245

1246
                                truncated = strndup(start, whitespace_begin - start);
7,037✔
1247
                                if (!truncated)
7,037✔
1248
                                        return -ENOMEM;
1249

1250
                                r = strv_consume(&broken, TAKE_PTR(truncated));
7,037✔
1251
                                if (r < 0)
7,037✔
1252
                                        return r;
1253

1254
                                p = start = whitespace_end;
1255
                                whitespace_begin = whitespace_end = NULL;
1256
                                w = cw;
1257
                        }
1258
                }
1259

1260
                /* Process rest of the line */
1261
                assert(start);
22,317✔
1262
                if (in_prefix) /* Never seen anything non-whitespace? Generate empty line! */
22,317✔
1263
                        r = strv_extend(&broken, "");
1✔
1264
                else if (whitespace_begin && !whitespace_end) { /* Ends in whitespace? Chop it off! */
22,316✔
1265
                        _cleanup_free_ char *truncated = strndup(start, whitespace_begin - start);
×
1266
                        if (!truncated)
9✔
1267
                                return -ENOMEM;
×
1268

1269
                        r = strv_consume(&broken, TAKE_PTR(truncated));
9✔
1270
                } else /* Otherwise use line as is */
1271
                        r = strv_extend(&broken, start);
22,307✔
1272
                if (r < 0)
22,317✔
1273
                        return r;
1274
        }
1275

1276
        *ret = TAKE_PTR(broken);
22,191✔
1277
        return 0;
22,191✔
1278
}
1279

1280
char** strv_filter_prefix(char * const *l, const char *prefix) {
4✔
1281

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

1284
        if (isempty(prefix))
4✔
1285
                return strv_copy(l);
5✔
1286

1287
        _cleanup_strv_free_ char **f = NULL;
3✔
1288
        size_t sz = 0;
3✔
1289

1290
        STRV_FOREACH(i, l) {
24✔
1291
                if (!startswith(*i, prefix))
21✔
1292
                        continue;
16✔
1293

1294
                if (strv_extend_with_size(&f, &sz, *i) < 0)
5✔
1295
                        return NULL;
1296
        }
1297

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