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

systemd / systemd / 28272947092

26 Jun 2026 08:38PM UTC coverage: 72.893% (+0.2%) from 72.703%
28272947092

push

github

poettering
sysupdate: Address review feedback on CheckNew varlink scaffolding

Follow-up to #42422:

 - Rename process_image() to context_process_image(), since it now
   operates on a Context object.
 - Use IN_SET() in image_type_can_sysupdate() instead of a switch.
 - Name the return parameters of context_list_components() ret_xyz, per
   our coding style.
 - Drop a redundant "else" after a return in vl_method_check_new().

9 of 11 new or added lines in 1 file covered. (81.82%)

12567 existing lines in 144 files now uncovered.

341026 of 467845 relevant lines covered (72.89%)

1339355.33 hits per line

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

95.25
/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) {
39,864,357✔
21
        assert(name);
39,864,357✔
22

23
        STRV_FOREACH(i, l)
367,966,922✔
24
                if (streq(*i, name))
330,366,582✔
25
                        return *i;
26

27
        return NULL;
28
}
29

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

33
        STRV_FOREACH(i, l)
10,001,004✔
34
                if (strcaseeq(*i, name))
8,843,628✔
35
                        return *i;
36

37
        return NULL;
38
}
39

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

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

47
        return NULL;
48
}
49

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

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

56
        STRV_FOREACH(i, l) {
3,896,277✔
57
                char *e;
1,978,392✔
58

59
                e = startswith(*i, name);
1,978,392✔
60
                if (e)
1,978,392✔
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) {
232✔
74
                char *e = startswith(*s, name);
199✔
75
                if (!e)
199✔
76
                        continue;
134✔
77

78
                size_t n = strlen(e);
65✔
79
                if (n < best_distance) {
65✔
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) {
10,097,405✔
140
        STRV_FOREACH(k, l)
29,268,940✔
141
                free(*k);
19,171,535✔
142

143
        return mfree(l);
10,097,405✔
144
}
145

146
char** strv_free_erase(char **l) {
97,813✔
147
        STRV_FOREACH(i, l)
111,754✔
148
                erase_and_freep(i);
13,941✔
149

150
        return mfree(l);
97,813✔
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) {
162,717✔
163
        _cleanup_strv_free_ char **result = NULL;
162,717✔
164
        char **k;
162,717✔
165

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

170
        k = result;
171
        STRV_FOREACH(i, l) {
961,304✔
172
                if (n == 0)
798,593✔
173
                        break;
174

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

180
                if (n != SIZE_MAX)
798,587✔
181
                        n--;
82✔
182
        }
183

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

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

191
        if (strv_isempty(l)) {
664✔
192
                *ret = NULL;
664✔
193
                return 0;
664✔
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,701,910✔
205
        size_t n = 0;
13,701,910✔
206

207
        STRV_FOREACH(i, l)
306,046,839✔
208
                n++;
292,344,929✔
209

210
        return n;
13,701,910✔
211
}
212

213
char** strv_new_ap(const char *x, va_list ap) {
82,678✔
214
        _cleanup_strv_free_ char **a = NULL;
82,678✔
215
        size_t n = 0, i = 0;
82,678✔
216
        va_list aq;
82,678✔
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);
82,678✔
224
        for (const char *s = x; s; s = va_arg(aq, const char*)) {
331,464✔
225
                if (s == STRV_IGNORE)
248,786✔
226
                        continue;
7,243✔
227

228
                n++;
241,543✔
229
        }
230
        va_end(aq);
82,678✔
231

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

236
        for (const char *s = x; s; s = va_arg(ap, const char*)) {
331,464✔
237
                if (s == STRV_IGNORE)
248,786✔
238
                        continue;
7,243✔
239

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

244
                i++;
241,543✔
245
        }
246

247
        a[i] = NULL;
82,678✔
248

249
        return TAKE_PTR(a);
82,678✔
250
}
251

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

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

260
        return r;
66,308✔
261
}
262

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

266
        assert(a);
24,260✔
267

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

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

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

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

283
        STRV_FOREACH(s, b) {
48,985✔
284
                if (filter_duplicates && strv_contains(t, *s))
28,482✔
285
                        continue;
29✔
286

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

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

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

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

309
        assert(a);
199,531✔
310

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

315
        p = strv_length(*a);
133,840✔
316
        if (p == 0) {
133,840✔
317
                strv_free_and_replace(*a, b_consume);
115,046✔
318

319
                if (filter_duplicates)
115,046✔
320
                        strv_uniq(*a);
32,178✔
321

322
                return strv_length(*a);
115,046✔
323
        }
324

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

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

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

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

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

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

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

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

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

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

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

364
        assert(a);
77,120✔
365

366
        STRV_FOREACH(s, b) {
383,811✔
367
                char *v;
306,691✔
368

369
                v = strjoin(strempty(prefix), *s, suffix);
613,380✔
370
                if (!v)
306,691✔
371
                        return -ENOMEM;
372

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

378
        return 0;
379
}
380

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

386
        assert(ret);
606,962✔
387
        assert(s);
606,962✔
388

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

392
        r = strv_split_full(&l, s, NEWLINE, flags);
606,962✔
393
        if (r < 0)
606,962✔
394
                return r;
395

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

402
        *ret = TAKE_PTR(l);
606,962✔
403
        return n;
606,962✔
404
}
405

406
char** strv_split_newlines(const char *s) {
64,168✔
407
        char **ret;
64,168✔
408

409
        if (strv_split_newlines_full(&ret, s, 0) < 0)
64,168✔
410
                return NULL;
64,168✔
411

412
        return ret;
64,168✔
413
}
414

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

420
        assert(t);
910,814✔
421
        assert(s);
910,814✔
422

423
        for (;;) {
16,638,356✔
424
                _cleanup_free_ char *word = NULL;
7,863,780✔
425

426
                r = extract_first_word(&s, &word, separators, flags);
8,774,585✔
427
                if (r < 0)
8,774,585✔
428
                        return r;
429
                if (r == 0)
8,774,576✔
430
                        break;
431

432
                if (!GREEDY_REALLOC(l, n + 2))
7,863,771✔
433
                        return -ENOMEM;
434

435
                l[n++] = TAKE_PTR(word);
7,863,771✔
436
                l[n] = NULL;
7,863,771✔
437
        }
438

439
        if (!l) {
910,805✔
440
                l = new0(char*, 1);
71,855✔
441
                if (!l)
71,855✔
442
                        return -ENOMEM;
443
        }
444

445
        *t = TAKE_PTR(l);
910,805✔
446

447
        return (int) n;
910,805✔
448
}
449

450
char** strv_split(const char *s, const char *separators) {
19,176✔
451
        char **ret;
19,176✔
452

453
        if (strv_split_full(&ret, s, separators, EXTRACT_RETAIN_ESCAPE) < 0)
19,176✔
454
                return NULL;
19,176✔
455

456
        return ret;
19,176✔
457
}
458

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

463
        assert(t);
1,015✔
464
        assert(s);
1,015✔
465

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

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

474
        return (int) strv_length(*t);
1,015✔
475
}
476

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

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

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

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

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

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

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

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

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

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

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

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

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

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

537
        if (!separator)
205,917✔
538
                separator = " ";
1,622✔
539

540
        k = strlen(separator);
205,917✔
541
        m = strlen_ptr(prefix);
205,917✔
542

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

546
        n = 0;
547
        STRV_FOREACH(s, l) {
1,019,990✔
548
                if (s != l)
814,073✔
549
                        n += k;
610,850✔
550

551
                bool needs_escaping = escape_separator && strchr(*s, *separator);
814,073✔
552

553
                n += m + strlen(*s) * (1 + needs_escaping);
814,073✔
554
        }
555

556
        r = new(char, n+1);
205,917✔
557
        if (!r)
205,917✔
558
                return NULL;
559

560
        e = r;
561
        STRV_FOREACH(s, l) {
1,019,990✔
562
                if (s != l)
814,073✔
563
                        e = stpcpy(e, separator);
610,850✔
564

565
                if (prefix)
814,073✔
566
                        e = stpcpy(e, prefix);
17✔
567

568
                bool needs_escaping = escape_separator && strchr(*s, *separator);
814,073✔
569

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

580
        *e = 0;
205,917✔
581

582
        return r;
205,917✔
583
}
584

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

591
        assert(l);
10,043,154✔
592
        POINTER_MAY_BE_NULL(n);
10,043,154✔
593

594
        if (!value)
10,043,154✔
595
                return 0;
596

597
        size_t size = n ? *n : SIZE_MAX;
10,043,152✔
598
        if (size == SIZE_MAX)
322,639✔
599
                size = strv_length(*l);
9,720,535✔
600

601
        /* Check for overflow */
602
        if (size > SIZE_MAX-2)
10,043,152✔
603
                return -ENOMEM;
604

605
        char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(size + 2), sizeof(char*));
10,043,152✔
606
        if (!c)
10,043,152✔
607
                return -ENOMEM;
608

609
        c[size] = value;
10,043,152✔
610
        c[size+1] = NULL;
10,043,152✔
611

612
        *l = c;
10,043,152✔
613
        if (n)
10,043,152✔
614
                *n = size + 1;
322,639✔
615
        return 0;
616
}
617

618
int strv_push_pair(char ***l, char *a, char *b) {
26,090✔
619
        char **c;
26,090✔
620
        size_t n;
26,090✔
621

622
        assert(l);
26,090✔
623

624
        if (!a && !b)
26,090✔
625
                return 0;
626

627
        n = strv_length(*l);
26,090✔
628

629
        /* Check for overflow */
630
        if (n > SIZE_MAX-3)
26,090✔
631
                return -ENOMEM;
632

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

638
        if (a)
26,090✔
639
                c[n++] = a;
26,090✔
640
        if (b)
26,090✔
641
                c[n++] = b;
26,090✔
642
        c[n] = NULL;
26,090✔
643

644
        *l = c;
26,090✔
645
        return 0;
26,090✔
646
}
647

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

652
        assert(l);
7,122✔
653

654
        if (!value)
7,122✔
655
                return 0;
656

657
        n = strv_length(*l);
7,121✔
658
        position = MIN(position, n);
7,121✔
659

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

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

669
        if (n > position)
7,121✔
670
                memmove(c + position + 1, c + position, (n - position) * sizeof(char*));
3,055✔
671

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

675
        *l = c;
7,121✔
676
        return 0;
7,121✔
677
}
678

679
int strv_consume_with_size(char ***l, size_t *n, char *value) {
9,981,041✔
680
        int r;
9,981,041✔
681

682
        r = strv_push_with_size(l, n, value);
9,981,041✔
683
        if (r < 0)
9,981,041✔
UNCOV
684
                free(value);
×
685

686
        return r;
9,981,041✔
687
}
688

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

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

698
        return r;
343✔
699
}
700

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

704
        r = strv_push_prepend(l, value);
7,116✔
705
        if (r < 0)
7,116✔
UNCOV
706
                free(value);
×
707

708
        return r;
7,116✔
709
}
710

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

714
        if (!value)
5,843✔
715
                return 0;
716

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

721
        return strv_consume_prepend(l, v);
5,717✔
722
}
723

724
int strv_extend_with_size(char ***l, size_t *n, const char *value) {
1,834,781✔
725
        char *v;
1,834,781✔
726

727
        if (!value)
1,834,781✔
728
                return 0;
729

730
        v = strdup(value);
1,830,489✔
731
        if (!v)
1,830,489✔
732
                return -ENOMEM;
733

734
        return strv_consume_with_size(l, n, v);
1,830,489✔
735
}
736

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

742
        assert(l);
3,660✔
743

744
        m = n = strv_length(*l);
3,660✔
745

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

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

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

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

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

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

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

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

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

801
        STRV_FOREACH(i, l)
442,754✔
802
                strv_remove(i+1, *i);
346,553✔
803

804
        return l;
96,201✔
805
}
806

807
bool strv_is_uniq(char * const *l) {
54,556✔
808
        STRV_FOREACH(i, l)
126,129✔
809
                if (strv_contains(i+1, *i))
71,574✔
810
                        return false;
811

812
        return true;
813
}
814

815
char** strv_remove(char **l, const char *s) {
346,657✔
816
        if (!l)
346,657✔
817
                return NULL;
818

819
        assert(s);
346,657✔
820

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

823
        char **f, **t;
824
        for (f = t = l; *f; f++)
1,799,895✔
825
                if (streq(*f, s))
1,453,238✔
826
                        free(*f);
43,241✔
827
                else
828
                        *(t++) = *f;
1,409,997✔
829

830
        *t = NULL;
346,657✔
831
        return l;
346,657✔
832
}
833

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

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

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

842
        return l;
843
}
844

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

849
        STRV_FOREACH(i, a)
1,766✔
850
                if (strv_contains(b, *i))
998✔
851
                        return true;
852

853
        return false;
854
}
855

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

862
        return strcmp(*a, *b);
1,266,212✔
863
}
864

865
char** strv_sort(char **l) {
49,127✔
866
        typesafe_qsort(l, strv_length(l), str_compare);
49,127✔
867
        return l;
49,127✔
868
}
869

870
char** strv_sort_uniq(char **l) {
42,465✔
871
        if (strv_isempty(l))
42,465✔
872
                return l;
873

874
        char **tail = strv_sort(l), *prev = NULL;
477✔
875
        STRV_FOREACH(i, l)
5,108✔
876
                if (streq_ptr(*i, prev))
4,154✔
877
                        free(*i);
33✔
878
                else
879
                        *(tail++) = prev = *i;
4,121✔
880

881
        *tail = NULL;
477✔
882
        return l;
477✔
883
}
884

885
int strv_compare(char * const *a, char * const *b) {
14,806✔
886
        int r;
14,806✔
887

888
        POINTER_MAY_BE_NULL(a);
14,806✔
889
        POINTER_MAY_BE_NULL(b);
14,806✔
890

891
        if (strv_isempty(a)) {
14,806✔
892
                if (strv_isempty(b))
3,268✔
893
                        return 0;
3,186✔
894
                else
895
                        return -1;
896
        }
897

898
        if (strv_isempty(b))
11,538✔
899
                return 1;
900

901
        for ( ; *a || *b; ++a, ++b) {
26,214✔
902
                r = strcmp_ptr(*a, *b);
14,853✔
903
                if (r != 0)
14,853✔
904
                        return r;
905
        }
906

907
        return 0;
908
}
909

910
bool strv_equal_ignore_order(char * const *a, char * const *b) {
511✔
911

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

916
        if (a == b)
511✔
917
                return true;
918

919
        STRV_FOREACH(i, a)
971✔
920
                if (!strv_contains(b, *i))
504✔
921
                        return false;
922

923
        STRV_FOREACH(i, b)
967✔
924
                if (!strv_contains(a, *i))
504✔
925
                        return false;
926

927
        return true;
928
}
929

930
void strv_print_full(char * const *l, const char *prefix) {
115✔
931
        STRV_FOREACH(s, l)
3,147✔
932
                printf("%s%s\n", strempty(prefix), *s);
6,064✔
933
}
115✔
934

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

940
        va_start(ap, format);
1,306,585✔
941
        r = vasprintf(&x, format, ap);
1,306,585✔
942
        va_end(ap);
1,306,585✔
943

944
        if (r < 0)
1,306,585✔
945
                return -ENOMEM;
1,306,585✔
946

947
        return strv_consume_with_size(l, n, x);
1,306,585✔
948
}
949

950
int strv_extend_joined_with_size_sentinel(char ***l, size_t *n, ...) {
61,730✔
951
        va_list ap;
61,730✔
952

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

959
        return strv_consume_with_size(l, n, x);
61,730✔
960
}
961

962
char* startswith_strv_internal(const char *s, char * const *l) {
13,220,452✔
963
        STRV_FOREACH(i, l) {
40,227,192✔
964
                char *found = (char*) startswith(s, *i);
27,149,411✔
965
                if (found)
27,149,411✔
966
                        return found;
967
        }
968

969
        return NULL;
970
}
971

972
char* endswith_strv_internal(const char *s, char * const *l) {
6,119,843✔
973
        STRV_FOREACH(i, l) {
18,367,882✔
974
                char *found = (char*) endswith(s, *i);
12,284,718✔
975
                if (found)
12,284,718✔
976
                        return found;
977
        }
978

979
        return NULL;
980
}
981

982
char** strv_reverse(char **l) {
860✔
983
        size_t n;
860✔
984

985
        n = strv_length(l);
860✔
986
        if (n <= 1)
860✔
987
                return l;
988

989
        for (size_t i = 0; i < n / 2; i++)
432✔
990
                SWAP_TWO(l[i], l[n-1-i]);
273✔
991

992
        return l;
993
}
994

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

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

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

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

1009
        return l;
1010
}
1011

1012
bool strv_fnmatch_full(
93,625✔
1013
                char* const* patterns,
1014
                const char *s,
1015
                int flags,
1016
                size_t *ret_matched_pos) {
1017

1018
        assert(s);
93,625✔
1019

1020
        if (patterns)
93,625✔
1021
                for (size_t i = 0; patterns[i]; i++)
166,375✔
1022
                        /* NB: We treat all fnmatch() errors as equivalent to FNM_NOMATCH, i.e. if fnmatch() fails to
1023
                         * process the pattern for some reason we'll consider this equivalent to non-matching. */
1024
                        if (fnmatch(patterns[i], s, flags) == 0) {
97,310✔
1025
                                if (ret_matched_pos)
21,160✔
1026
                                        *ret_matched_pos = i;
1,661✔
1027
                                return true;
1028
                        }
1029

1030
        if (ret_matched_pos)
72,465✔
1031
                *ret_matched_pos = SIZE_MAX;
20,006✔
1032

1033
        return false;
1034
}
1035

1036
char** strv_skip(char **l, size_t n) {
26,994✔
1037
        while (n > 0) {
58,506✔
1038
                if (strv_isempty(l))
32,832✔
1039
                        return NULL;
1040

1041
                l++, n--;
31,512✔
1042
        }
1043

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

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

1054
        assert(l);
879✔
1055

1056
        if (!value)
879✔
1057
                return 0;
1058
        if (n == 0)
879✔
1059
                return 0;
1060

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

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

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

1071
        *l = nl;
878✔
1072

1073
        for (i = k; i < k + n; i++) {
1,812✔
1074
                nl[i] = strdup(value);
934✔
1075
                if (!nl[i])
934✔
UNCOV
1076
                        goto rollback;
×
1077
        }
1078
        nl[i] = NULL;
878✔
1079

1080
        return 0;
878✔
1081

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

UNCOV
1087
        return -ENOMEM;
×
1088
}
1089

1090
int strv_extend_assignment(char ***l, const char *lhs, const char *rhs) {
7,601,257✔
1091
        char *j;
7,601,257✔
1092

1093
        assert(l);
7,601,257✔
1094
        assert(lhs);
7,601,257✔
1095

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

1099
        j = strjoin(lhs, "=", rhs);
5,383,477✔
1100
        if (!j)
5,383,477✔
1101
                return -ENOMEM;
1102

1103
        return strv_consume(l, j);
5,383,477✔
1104
}
1105

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

1110
        assert(f);
16,502✔
1111

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

1114
        if (!space)
16,502✔
1115
                space = &b;
5✔
1116

1117
        STRV_FOREACH(s, l) {
16,903✔
1118
                r = fputs_with_separator(f, *s, separator, space);
401✔
1119
                if (r < 0)
401✔
1120
                        return r;
16,502✔
1121
        }
1122

1123
        return 0;
1124
}
1125

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

1129
        if (value) {
559✔
1130
                char **l = hashmap_get(h, key);
559✔
1131
                if (!l)
559✔
1132
                        return;
531✔
1133

1134
                strv_remove(l, value);
32✔
1135
                if (!strv_isempty(l))
559✔
1136
                        return;
1137
        }
1138

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

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

1147
static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const char *value) {
589,112✔
1148
        char **l;
589,112✔
1149
        int r;
589,112✔
1150

1151
        assert(h);
589,112✔
1152
        assert(key);
589,112✔
1153
        assert(value);
589,112✔
1154

1155
        l = hashmap_get(h, key);
589,112✔
1156
        if (l) {
589,112✔
1157
                /* A list for this key already exists, let's append to it if it is not listed yet */
1158
                if (strv_contains(l, value))
37,017✔
1159
                        return 0;
589,112✔
1160

1161
                r = strv_extend(&l, value);
37,004✔
1162
                if (r < 0)
37,004✔
1163
                        return r;
1164

1165
                assert_se(hashmap_update(h, key, l) >= 0);
37,004✔
1166
        } else {
1167
                /* No list for this key exists yet, create one */
UNCOV
1168
                _cleanup_strv_free_ char **l2 = NULL;
×
1169
                _cleanup_free_ char *t = NULL;
552,095✔
1170

1171
                t = strdup(key);
552,095✔
1172
                if (!t)
552,095✔
1173
                        return -ENOMEM;
1174

1175
                r = strv_extend(&l2, value);
552,095✔
1176
                if (r < 0)
552,095✔
1177
                        return r;
1178

1179
                r = hashmap_put(h, t, l2);
552,095✔
1180
                if (r < 0)
552,095✔
1181
                        return r;
1182

1183
                TAKE_PTR(t);
552,095✔
1184
                TAKE_PTR(l2);
552,095✔
1185
        }
1186

1187
        return 1;
1188
}
1189

1190
int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value) {
589,086✔
1191
        int r;
589,086✔
1192

1193
        assert(h);
589,086✔
1194
        assert(key);
589,086✔
1195
        assert(value);
589,086✔
1196

1197
        r = hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free);
589,086✔
1198
        if (r < 0)
589,086✔
1199
                return r;
1200

1201
        return string_strv_hashmap_put_internal(*h, key, value);
589,086✔
1202
}
1203

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

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

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

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

1218
int strv_rebreak_lines(char **l, size_t width, char ***ret) {
66,797✔
1219
        _cleanup_strv_free_ char **broken = NULL;
66,797✔
1220
        int r;
66,797✔
1221

1222
        assert(ret);
66,797✔
1223

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

1230
        if (width == SIZE_MAX) { /* NOP? */
66,797✔
1231
                broken = strv_copy(l);
8,634✔
1232
                if (!broken)
8,634✔
1233
                        return -ENOMEM;
1234

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

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

1244
                for (const char *p = start; *p != 0; p = utf8_next_char(p)) {
3,306,412✔
1245
                        if (strchr(NEWLINE, *p)) {
3,248,123✔
1246
                                in_prefix = true;
1247
                                whitespace_begin = whitespace_end = NULL;
1248
                                w = 0;
1249
                        } else if (strchr(WHITESPACE, *p)) {
3,248,123✔
1250
                                if (!in_prefix && (!whitespace_begin || whitespace_end)) {
282,412✔
1251
                                        whitespace_begin = p;
281,815✔
1252
                                        whitespace_end = NULL;
281,815✔
1253
                                }
1254
                        } else {
1255
                                if (whitespace_begin && !whitespace_end)
2,965,711✔
1256
                                        whitespace_end = p;
281,806✔
1257

1258
                                in_prefix = false;
1259
                        }
1260

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

1267
                        w += cw;
3,248,123✔
1268

1269
                        if (w > width && whitespace_begin && whitespace_end) {
3,248,123✔
UNCOV
1270
                                _cleanup_free_ char *truncated = NULL;
×
1271

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

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

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

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

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

1302
        *ret = TAKE_PTR(broken);
58,163✔
1303
        return 0;
58,163✔
1304
}
1305

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

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

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

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

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

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

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

1327
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