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

systemd / systemd / 14554080340

19 Apr 2025 11:46AM UTC coverage: 72.101% (-0.03%) from 72.13%
14554080340

push

github

web-flow
Add two new paragraphs to coding style about header files (#37188)

296880 of 411754 relevant lines covered (72.1%)

687547.52 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 <errno.h>
4
#include <fnmatch.h>
5
#include <stdarg.h>
6
#include <stdio.h>
7
#include <stdlib.h>
8

9
#include "alloc-util.h"
10
#include "env-util.h"
11
#include "escape.h"
12
#include "extract-word.h"
13
#include "fileio.h"
14
#include "gunicode.h"
15
#include "log.h"
16
#include "memory-util.h"
17
#include "nulstr-util.h"
18
#include "sort-util.h"
19
#include "string-util.h"
20
#include "strv.h"
21
#include "utf8.h"
22

23
char* strv_find(char * const *l, const char *name) {
17,222,503✔
24
        assert(name);
17,222,503✔
25

26
        STRV_FOREACH(i, l)
183,081,491✔
27
                if (streq(*i, name))
167,129,417✔
28
                        return *i;
29

30
        return NULL;
31
}
32

33
char* strv_find_case(char * const *l, const char *name) {
1,220,519✔
34
        assert(name);
1,220,519✔
35

36
        STRV_FOREACH(i, l)
5,015,478✔
37
                if (strcaseeq(*i, name))
4,394,717✔
38
                        return *i;
39

40
        return NULL;
41
}
42

43
char* strv_find_prefix(char * const *l, const char *name) {
319✔
44
        assert(name);
319✔
45

46
        STRV_FOREACH(i, l)
3,872✔
47
                if (startswith(*i, name))
3,636✔
48
                        return *i;
49

50
        return NULL;
51
}
52

53
char* strv_find_startswith(char * const *l, const char *name) {
978,347✔
54
        assert(name);
978,347✔
55

56
        /* Like strv_find_prefix, but actually returns only the
57
         * suffix, not the whole item */
58

59
        STRV_FOREACH(i, l) {
1,976,231✔
60
                char *e;
999,320✔
61

62
                e = startswith(*i, name);
999,320✔
63
                if (e)
999,320✔
64
                        return e;
65
        }
66

67
        return NULL;
68
}
69

70
static char* strv_find_closest_prefix(char * const *l, const char *name) {
29✔
71
        size_t best_distance = SIZE_MAX;
29✔
72
        char *best = NULL;
29✔
73

74
        assert(name);
29✔
75

76
        STRV_FOREACH(s, l) {
196✔
77
                char *e = startswith(*s, name);
167✔
78
                if (!e)
167✔
79
                        continue;
106✔
80

81
                size_t n = strlen(e);
61✔
82
                if (n < best_distance) {
61✔
83
                        best_distance = n;
16✔
84
                        best = *s;
16✔
85
                }
86
        }
87

88
        return best;
29✔
89
}
90

91
static char* strv_find_closest_by_levenshtein(char * const *l, const char *name) {
14✔
92
        ssize_t best_distance = SSIZE_MAX;
14✔
93
        char *best = NULL;
14✔
94

95
        assert(name);
14✔
96

97
        STRV_FOREACH(i, l) {
86✔
98
                ssize_t distance;
72✔
99

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

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

109
                if (distance < best_distance) {
33✔
110
                        best_distance = distance;
15✔
111
                        best = *i;
15✔
112
                }
113
        }
114

115
        return best;
116
}
117

118
char* strv_find_closest(char * const *l, const char *name) {
29✔
119
        assert(name);
29✔
120

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

125
        char *found = strv_find_closest_prefix(l, name);
29✔
126
        if (found)
29✔
127
                return found;
128

129
        return strv_find_closest_by_levenshtein(l, name);
14✔
130
}
131

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

139
        return NULL;
140
}
141

142
char** strv_free(char **l) {
5,621,417✔
143
        STRV_FOREACH(k, l)
21,154,584✔
144
                free(*k);
15,533,167✔
145

146
        return mfree(l);
5,621,417✔
147
}
148

149
char** strv_free_erase(char **l) {
38,045✔
150
        STRV_FOREACH(i, l)
44,517✔
151
                erase_and_freep(i);
6,472✔
152

153
        return mfree(l);
38,045✔
154
}
155

156
void strv_free_many(char ***strvs, size_t n) {
8✔
157
        assert(strvs || n == 0);
8✔
158

159
        FOREACH_ARRAY (i, strvs, n)
32✔
160
                strv_free(*i);
24✔
161

162
        free(strvs);
8✔
163
}
8✔
164

165
char** strv_copy_n(char * const *l, size_t m) {
146,016✔
166
        _cleanup_strv_free_ char **result = NULL;
146,016✔
167
        char **k;
146,016✔
168

169
        result = new(char*, MIN(strv_length(l), m) + 1);
146,016✔
170
        if (!result)
146,016✔
171
                return NULL;
172

173
        k = result;
174
        STRV_FOREACH(i, l) {
406,120✔
175
                if (m == 0)
260,110✔
176
                        break;
177

178
                *k = strdup(*i);
260,104✔
179
                if (!*k)
260,104✔
180
                        return NULL;
181
                k++;
260,104✔
182

183
                if (m != SIZE_MAX)
260,104✔
184
                        m--;
84✔
185
        }
186

187
        *k = NULL;
146,016✔
188
        return TAKE_PTR(result);
146,016✔
189
}
190

191
int strv_copy_unless_empty(char * const *l, char ***ret) {
6,331✔
192
        assert(ret);
6,331✔
193

194
        if (strv_isempty(l)) {
6,331✔
195
                *ret = NULL;
6,291✔
196
                return 0;
6,291✔
197
        }
198

199
        char **copy = strv_copy(l);
40✔
200
        if (!copy)
40✔
201
                return -ENOMEM;
202

203
        *ret = TAKE_PTR(copy);
40✔
204
        return 1;
40✔
205
}
206

207
size_t strv_length(char * const *l) {
9,284,882✔
208
        size_t n = 0;
9,284,882✔
209

210
        STRV_FOREACH(i, l)
287,366,599✔
211
                n++;
278,081,717✔
212

213
        return n;
9,284,882✔
214
}
215

216
char** strv_new_ap(const char *x, va_list ap) {
25,999✔
217
        _cleanup_strv_free_ char **a = NULL;
25,999✔
218
        size_t n = 0, i = 0;
25,999✔
219
        va_list aq;
25,999✔
220

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

226
        va_copy(aq, ap);
25,999✔
227
        for (const char *s = x; s; s = va_arg(aq, const char*)) {
169,352✔
228
                if (s == STRV_IGNORE)
143,353✔
229
                        continue;
4,991✔
230

231
                n++;
138,362✔
232
        }
233
        va_end(aq);
25,999✔
234

235
        a = new(char*, n+1);
25,999✔
236
        if (!a)
25,999✔
237
                return NULL;
238

239
        for (const char *s = x; s; s = va_arg(ap, const char*)) {
169,352✔
240
                if (s == STRV_IGNORE)
143,353✔
241
                        continue;
4,991✔
242

243
                a[i] = strdup(s);
138,362✔
244
                if (!a[i])
138,362✔
245
                        return NULL;
246

247
                i++;
138,362✔
248
        }
249

250
        a[i] = NULL;
25,999✔
251

252
        return TAKE_PTR(a);
25,999✔
253
}
254

255
char** strv_new_internal(const char *x, ...) {
24,816✔
256
        char **r;
24,816✔
257
        va_list ap;
24,816✔
258

259
        va_start(ap, x);
24,816✔
260
        r = strv_new_ap(x, ap);
24,816✔
261
        va_end(ap);
24,816✔
262

263
        return r;
24,816✔
264
}
265

266
int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
35,681✔
267
        size_t p, q, i = 0;
35,681✔
268

269
        assert(a);
35,681✔
270

271
        q = strv_length(b);
35,681✔
272
        if (q == 0)
35,681✔
273
                return 0;
274

275
        p = strv_length(*a);
33,152✔
276
        if (p >= SIZE_MAX - q)
33,152✔
277
                return -ENOMEM;
278

279
        char **t = reallocarray(*a, GREEDY_ALLOC_ROUND_UP(p + q + 1), sizeof(char *));
33,152✔
280
        if (!t)
33,152✔
281
                return -ENOMEM;
282

283
        t[p] = NULL;
33,152✔
284
        *a = t;
33,152✔
285

286
        STRV_FOREACH(s, b) {
72,242✔
287
                if (filter_duplicates && strv_contains(t, *s))
39,090✔
288
                        continue;
58✔
289

290
                t[p+i] = strdup(*s);
39,032✔
291
                if (!t[p+i])
39,032✔
292
                        goto rollback;
×
293

294
                i++;
39,032✔
295
                t[p+i] = NULL;
39,032✔
296
        }
297

298
        assert(i <= q);
33,152✔
299

300
        return (int) i;
33,152✔
301

302
rollback:
×
303
        free_many_charp(t + p, i);
×
304
        t[p] = NULL;
×
305
        return -ENOMEM;
×
306
}
307

308
int strv_extend_strv_consume(char ***a, char **b, bool filter_duplicates) {
115,508✔
309
        _cleanup_strv_free_ char **b_consume = b;
115,508✔
310
        size_t p, q, i;
115,508✔
311

312
        assert(a);
115,508✔
313

314
        q = strv_length(b);
115,508✔
315
        if (q == 0)
115,508✔
316
                return 0;
317

318
        p = strv_length(*a);
61,950✔
319
        if (p == 0) {
61,950✔
320
                strv_free_and_replace(*a, b_consume);
51,904✔
321

322
                if (filter_duplicates)
51,904✔
323
                        strv_uniq(*a);
16,228✔
324

325
                return strv_length(*a);
51,904✔
326
        }
327

328
        if (p >= SIZE_MAX - q)
10,046✔
329
                return -ENOMEM;
330

331
        char **t = reallocarray(*a, GREEDY_ALLOC_ROUND_UP(p + q + 1), sizeof(char *));
10,046✔
332
        if (!t)
10,046✔
333
                return -ENOMEM;
334

335
        t[p] = NULL;
10,046✔
336
        *a = t;
10,046✔
337

338
        if (!filter_duplicates) {
10,046✔
339
                *mempcpy_typesafe(t + p, b, q) = NULL;
9,519✔
340
                i = q;
9,519✔
341
        } else {
342
                i = 0;
343

344
                STRV_FOREACH(s, b) {
1,532✔
345
                        if (strv_contains(t, *s)) {
1,005✔
346
                                free(*s);
173✔
347
                                continue;
173✔
348
                        }
349

350
                        t[p+i] = *s;
832✔
351

352
                        i++;
832✔
353
                        t[p+i] = NULL;
832✔
354
                }
355
        }
356

357
        assert(i <= q);
10,046✔
358

359
        b_consume = mfree(b_consume);
10,046✔
360

361
        return (int) i;
10,046✔
362
}
363

364
int strv_extend_strv_biconcat(char ***a, const char *prefix, const char* const *b, const char *suffix) {
80,290✔
365
        int r;
80,290✔
366

367
        assert(a);
80,290✔
368

369
        STRV_FOREACH(s, b) {
400,172✔
370
                char *v;
319,882✔
371

372
                v = strjoin(strempty(prefix), *s, suffix);
639,674✔
373
                if (!v)
319,882✔
374
                        return -ENOMEM;
375

376
                r = strv_consume(a, v);
319,882✔
377
                if (r < 0)
319,882✔
378
                        return r;
379
        }
380

381
        return 0;
382
}
383

384
int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags) {
592,861✔
385
        _cleanup_strv_free_ char **l = NULL;
592,861✔
386
        size_t n;
592,861✔
387
        int r;
592,861✔
388

389
        assert(s);
592,861✔
390

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

394
        r = strv_split_full(&l, s, NEWLINE, flags);
592,861✔
395
        if (r < 0)
592,861✔
396
                return r;
397

398
        n = strv_length(l);
592,861✔
399
        if (n > 0 && isempty(l[n - 1])) {
592,861✔
400
                l[n - 1] = mfree(l[n - 1]);
×
401
                n--;
×
402
        }
403

404
        *ret = TAKE_PTR(l);
592,861✔
405
        return n;
592,861✔
406
}
407

408
int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags) {
835,952✔
409
        _cleanup_strv_free_ char **l = NULL;
835,952✔
410
        size_t n = 0;
835,952✔
411
        int r;
835,952✔
412

413
        assert(t);
835,952✔
414
        assert(s);
835,952✔
415

416
        for (;;) {
17,274,586✔
417
                _cleanup_free_ char *word = NULL;
8,219,326✔
418

419
                r = extract_first_word(&s, &word, separators, flags);
9,055,269✔
420
                if (r < 0)
9,055,269✔
421
                        return r;
422
                if (r == 0)
9,055,260✔
423
                        break;
424

425
                if (!GREEDY_REALLOC(l, n + 2))
8,219,317✔
426
                        return -ENOMEM;
427

428
                l[n++] = TAKE_PTR(word);
8,219,317✔
429
                l[n] = NULL;
8,219,317✔
430
        }
431

432
        if (!l) {
835,943✔
433
                l = new0(char*, 1);
73,078✔
434
                if (!l)
73,078✔
435
                        return -ENOMEM;
436
        }
437

438
        *t = TAKE_PTR(l);
835,943✔
439

440
        return (int) n;
835,943✔
441
}
442

443
int strv_split_and_extend_full(char ***t, const char *s, const char *separators, bool filter_duplicates, ExtractFlags flags) {
757✔
444
        char **l;
757✔
445
        int r;
757✔
446

447
        assert(t);
757✔
448
        assert(s);
757✔
449

450
        r = strv_split_full(&l, s, separators, flags);
757✔
451
        if (r < 0)
757✔
452
                return r;
757✔
453

454
        r = strv_extend_strv_consume(t, l, filter_duplicates);
757✔
455
        if (r < 0)
757✔
456
                return r;
457

458
        return (int) strv_length(*t);
757✔
459
}
460

461
int strv_split_colon_pairs(char ***t, const char *s) {
4✔
462
        _cleanup_strv_free_ char **l = NULL;
4✔
463
        size_t n = 0;
4✔
464
        int r;
4✔
465

466
        assert(t);
4✔
467
        assert(s);
4✔
468

469
        for (;;) {
16✔
470
                _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL, *second_or_empty = NULL;
16✔
471

472
                r = extract_first_word(&s, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
16✔
473
                if (r < 0)
16✔
474
                        return r;
475
                if (r == 0)
16✔
476
                        break;
477

478
                const char *p = tuple;
13✔
479
                r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS,
13✔
480
                                       &first, &second);
481
                if (r < 0)
13✔
482
                        return r;
483
                if (r == 0)
13✔
484
                        continue;
×
485
                /* Enforce that at most 2 colon-separated words are contained in each group */
486
                if (!isempty(p))
14✔
487
                        return -EINVAL;
488

489
                second_or_empty = strdup(strempty(second));
12✔
490
                if (!second_or_empty)
12✔
491
                        return -ENOMEM;
492

493
                if (!GREEDY_REALLOC(l, n + 3))
12✔
494
                        return -ENOMEM;
495

496
                l[n++] = TAKE_PTR(first);
12✔
497
                l[n++] = TAKE_PTR(second_or_empty);
12✔
498

499
                l[n] = NULL;
12✔
500
        }
501

502
        if (!l) {
3✔
503
                l = new0(char*, 1);
×
504
                if (!l)
×
505
                        return -ENOMEM;
506
        }
507

508
        *t = TAKE_PTR(l);
3✔
509

510
        return (int) n;
3✔
511
}
512

513
char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separator) {
61,482✔
514
        char *r, *e;
61,482✔
515
        size_t n, k, m;
61,482✔
516

517
        if (!separator)
61,482✔
518
                separator = " ";
2✔
519

520
        k = strlen(separator);
61,482✔
521
        m = strlen_ptr(prefix);
61,482✔
522

523
        if (escape_separator) /* If the separator was multi-char, we wouldn't know how to escape it. */
61,482✔
524
                assert(k == 1);
362✔
525

526
        n = 0;
527
        STRV_FOREACH(s, l) {
253,991✔
528
                if (s != l)
192,509✔
529
                        n += k;
131,811✔
530

531
                bool needs_escaping = escape_separator && strchr(*s, *separator);
192,509✔
532

533
                n += m + strlen(*s) * (1 + needs_escaping);
192,509✔
534
        }
535

536
        r = new(char, n+1);
61,482✔
537
        if (!r)
61,482✔
538
                return NULL;
539

540
        e = r;
541
        STRV_FOREACH(s, l) {
253,991✔
542
                if (s != l)
192,509✔
543
                        e = stpcpy(e, separator);
131,811✔
544

545
                if (prefix)
192,509✔
546
                        e = stpcpy(e, prefix);
17✔
547

548
                bool needs_escaping = escape_separator && strchr(*s, *separator);
192,509✔
549

550
                if (needs_escaping)
192,498✔
551
                        for (size_t i = 0; (*s)[i]; i++) {
71✔
552
                                if ((*s)[i] == *separator)
60✔
553
                                        *(e++) = '\\';
14✔
554
                                *(e++) = (*s)[i];
60✔
555
                        }
556
                else
557
                        e = stpcpy(e, *s);
192,498✔
558
        }
559

560
        *e = 0;
61,482✔
561

562
        return r;
61,482✔
563
}
564

565
int strv_push_with_size(char ***l, size_t *n, char *value) {
6,718,365✔
566
        /* n is a pointer to a variable to store the size of l.
567
         * If not given (i.e. n is NULL or *n is SIZE_MAX), size will be calculated using strv_length().
568
         * If n is not NULL, the size after the push will be returned.
569
         * If value is empty, no action is taken and *n is not set. */
570

571
        if (!value)
6,718,365✔
572
                return 0;
573

574
        size_t size = n ? *n : SIZE_MAX;
6,718,363✔
575
        if (size == SIZE_MAX)
459✔
576
                size = strv_length(*l);
6,717,917✔
577

578
        /* Check for overflow */
579
        if (size > SIZE_MAX-2)
6,718,363✔
580
                return -ENOMEM;
581

582
        char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(size + 2), sizeof(char*));
6,718,363✔
583
        if (!c)
6,718,363✔
584
                return -ENOMEM;
585

586
        c[size] = value;
6,718,363✔
587
        c[size+1] = NULL;
6,718,363✔
588

589
        *l = c;
6,718,363✔
590
        if (n)
6,718,363✔
591
                *n = size + 1;
459✔
592
        return 0;
593
}
594

595
int strv_push_pair(char ***l, char *a, char *b) {
457✔
596
        char **c;
457✔
597
        size_t n;
457✔
598

599
        if (!a && !b)
457✔
600
                return 0;
601

602
        n = strv_length(*l);
457✔
603

604
        /* Check for overflow */
605
        if (n > SIZE_MAX-3)
457✔
606
                return -ENOMEM;
607

608
        /* increase and check for overflow */
609
        c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(n + !!a + !!b + 1), sizeof(char*));
457✔
610
        if (!c)
457✔
611
                return -ENOMEM;
612

613
        if (a)
457✔
614
                c[n++] = a;
457✔
615
        if (b)
457✔
616
                c[n++] = b;
457✔
617
        c[n] = NULL;
457✔
618

619
        *l = c;
457✔
620
        return 0;
457✔
621
}
622

623
int strv_insert(char ***l, size_t position, char *value) {
4,402✔
624
        char **c;
4,402✔
625
        size_t n, m;
4,402✔
626

627
        assert(l);
4,402✔
628

629
        if (!value)
4,402✔
630
                return 0;
631

632
        n = strv_length(*l);
4,401✔
633
        position = MIN(position, n);
4,401✔
634

635
        /* check for overflow and increase */
636
        if (n > SIZE_MAX - 2)
4,401✔
637
                return -ENOMEM;
638
        m = n + 2;
4,401✔
639

640
        c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m), sizeof(char*));
4,401✔
641
        if (!c)
4,401✔
642
                return -ENOMEM;
643

644
        if (n > position)
4,401✔
645
                memmove(c + position + 1, c + position, (n - position) * sizeof(char*));
1,140✔
646

647
        c[position] = value;
4,401✔
648
        c[n + 1] = NULL;
4,401✔
649

650
        *l = c;
4,401✔
651
        return 0;
4,401✔
652
}
653

654
int strv_consume_with_size(char ***l, size_t *n, char *value) {
6,665,527✔
655
        int r;
6,665,527✔
656

657
        r = strv_push_with_size(l, n, value);
6,665,527✔
658
        if (r < 0)
6,665,527✔
659
                free(value);
×
660

661
        return r;
6,665,527✔
662
}
663

664
int strv_consume_pair(char ***l, char *a, char *b) {
418✔
665
        int r;
418✔
666

667
        r = strv_push_pair(l, a, b);
418✔
668
        if (r < 0) {
418✔
669
                free(a);
×
670
                free(b);
×
671
        }
672

673
        return r;
418✔
674
}
675

676
int strv_consume_prepend(char ***l, char *value) {
4,384✔
677
        int r;
4,384✔
678

679
        r = strv_push_prepend(l, value);
4,384✔
680
        if (r < 0)
4,384✔
681
                free(value);
×
682

683
        return r;
4,384✔
684
}
685

686
int strv_prepend(char ***l, const char *value) {
3,610✔
687
        char *v;
3,610✔
688

689
        if (!value)
3,610✔
690
                return 0;
691

692
        v = strdup(value);
3,505✔
693
        if (!v)
3,505✔
694
                return -ENOMEM;
695

696
        return strv_consume_prepend(l, v);
3,505✔
697
}
698

699
int strv_extend_with_size(char ***l, size_t *n, const char *value) {
1,136,989✔
700
        char *v;
1,136,989✔
701

702
        if (!value)
1,136,989✔
703
                return 0;
704

705
        v = strdup(value);
1,133,388✔
706
        if (!v)
1,133,388✔
707
                return -ENOMEM;
708

709
        return strv_consume_with_size(l, n, v);
1,133,388✔
710
}
711

712
int strv_extend_many_internal(char ***l, const char *value, ...) {
329✔
713
        va_list ap;
329✔
714
        size_t n, m;
329✔
715
        int r;
329✔
716

717
        assert(l);
329✔
718

719
        m = n = strv_length(*l);
329✔
720

721
        r = 0;
329✔
722
        va_start(ap, value);
329✔
723
        for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
1,197✔
724
                if (!s)
868✔
725
                        continue;
27✔
726

727
                if (m > SIZE_MAX-1) { /* overflow */
841✔
728
                        r = -ENOMEM;
729
                        break;
730
                }
731
                m++;
841✔
732
        }
733
        va_end(ap);
329✔
734

735
        if (r < 0)
329✔
736
                return r;
329✔
737
        if (m > SIZE_MAX-1)
329✔
738
                return -ENOMEM;
739

740
        char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m+1), sizeof(char*));
329✔
741
        if (!c)
329✔
742
                return -ENOMEM;
743
        *l = c;
329✔
744

745
        r = 0;
329✔
746
        size_t i = n;
329✔
747
        va_start(ap, value);
329✔
748
        for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
1,197✔
749
                if (!s)
868✔
750
                        continue;
27✔
751

752
                c[i] = strdup(s);
841✔
753
                if (!c[i]) {
841✔
754
                        r = -ENOMEM;
755
                        break;
756
                }
757
                i++;
841✔
758
        }
759
        va_end(ap);
329✔
760

761
        if (r < 0) {
329✔
762
                /* rollback on error */
763
                for (size_t j = n; j < i; j++)
×
764
                        c[j] = mfree(c[j]);
×
765
                return r;
766
        }
767

768
        c[i] = NULL;
329✔
769
        return 0;
329✔
770
}
771

772
char** strv_uniq(char **l) {
43,726✔
773
        /* Drops duplicate entries. The first identical string will be
774
         * kept, the others dropped */
775

776
        STRV_FOREACH(i, l)
243,193✔
777
                strv_remove(i+1, *i);
199,467✔
778

779
        return l;
43,726✔
780
}
781

782
bool strv_is_uniq(char * const *l) {
4✔
783
        STRV_FOREACH(i, l)
8✔
784
                if (strv_contains(i+1, *i))
5✔
785
                        return false;
786

787
        return true;
788
}
789

790
char** strv_remove(char **l, const char *s) {
199,533✔
791
        char **f, **t;
199,533✔
792

793
        if (!l)
199,533✔
794
                return NULL;
795

796
        assert(s);
199,533✔
797

798
        /* Drops every occurrence of s in the string list, edits
799
         * in-place. */
800

801
        for (f = t = l; *f; f++)
1,134,763✔
802
                if (streq(*f, s))
935,230✔
803
                        free(*f);
30,274✔
804
                else
805
                        *(t++) = *f;
904,956✔
806

807
        *t = NULL;
199,533✔
808
        return l;
199,533✔
809
}
810

811
bool strv_overlap(char * const *a, char * const *b) {
396✔
812
        STRV_FOREACH(i, a)
723✔
813
                if (strv_contains(b, *i))
399✔
814
                        return true;
815

816
        return false;
817
}
818

819
static int str_compare(char * const *a, char * const *b) {
463,958✔
820
        return strcmp(*a, *b);
463,958✔
821
}
822

823
char** strv_sort(char **l) {
13,054✔
824
        typesafe_qsort(l, strv_length(l), str_compare);
13,054✔
825
        return l;
13,054✔
826
}
827

828
char** strv_sort_uniq(char **l) {
35,732✔
829
        if (strv_isempty(l))
35,732✔
830
                return l;
831

832
        char **tail = strv_sort(l), *prev = NULL;
198✔
833
        STRV_FOREACH(i, l)
3,451✔
834
                if (streq_ptr(*i, prev))
3,253✔
835
                        free(*i);
27✔
836
                else
837
                        *(tail++) = prev = *i;
3,226✔
838

839
        *tail = NULL;
198✔
840
        return l;
198✔
841
}
842

843
int strv_compare(char * const *a, char * const *b) {
3,221✔
844
        int r;
3,221✔
845

846
        if (strv_isempty(a)) {
3,221✔
847
                if (strv_isempty(b))
1,164✔
848
                        return 0;
1,102✔
849
                else
850
                        return -1;
851
        }
852

853
        if (strv_isempty(b))
2,057✔
854
                return 1;
855

856
        for ( ; *a || *b; ++a, ++b) {
5,033✔
857
                r = strcmp_ptr(*a, *b);
3,105✔
858
                if (r != 0)
3,105✔
859
                        return r;
860
        }
861

862
        return 0;
863
}
864

865
bool strv_equal_ignore_order(char * const *a, char * const *b) {
445✔
866

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

871
        if (a == b)
445✔
872
                return true;
873

874
        STRV_FOREACH(i, a)
847✔
875
                if (!strv_contains(b, *i))
438✔
876
                        return false;
877

878
        STRV_FOREACH(i, b)
843✔
879
                if (!strv_contains(a, *i))
438✔
880
                        return false;
881

882
        return true;
883
}
884

885
void strv_print_full(char * const *l, const char *prefix) {
60✔
886
        STRV_FOREACH(s, l)
2,514✔
887
                printf("%s%s\n", strempty(prefix), *s);
4,908✔
888
}
60✔
889

890
int strv_extendf(char ***l, const char *format, ...) {
941,048✔
891
        va_list ap;
941,048✔
892
        char *x;
941,048✔
893
        int r;
941,048✔
894

895
        va_start(ap, format);
941,048✔
896
        r = vasprintf(&x, format, ap);
941,048✔
897
        va_end(ap);
941,048✔
898

899
        if (r < 0)
941,048✔
900
                return -ENOMEM;
941,048✔
901

902
        return strv_consume(l, x);
941,048✔
903
}
904

905
char* startswith_strv(const char *s, char * const *l) {
11,509,389✔
906
        STRV_FOREACH(i, l) {
35,184,364✔
907
                char *found = startswith(s, *i);
23,745,856✔
908
                if (found)
23,745,856✔
909
                        return found;
910
        }
911

912
        return NULL;
913
}
914

915
char* endswith_strv(const char *s, char * const *l) {
3,031,522✔
916
        STRV_FOREACH(i, l) {
9,071,390✔
917
                char *found = endswith(s, *i);
6,066,107✔
918
                if (found)
6,066,107✔
919
                        return found;
920
        }
921

922
        return NULL;
923
}
924

925
char** strv_reverse(char **l) {
844✔
926
        size_t n;
844✔
927

928
        n = strv_length(l);
844✔
929
        if (n <= 1)
844✔
930
                return l;
931

932
        for (size_t i = 0; i < n / 2; i++)
294✔
933
                SWAP_TWO(l[i], l[n-1-i]);
151✔
934

935
        return l;
936
}
937

938
char** strv_shell_escape(char **l, const char *bad) {
3✔
939
        /* Escapes every character in every string in l that is in bad,
940
         * edits in-place, does not roll-back on error. */
941

942
        STRV_FOREACH(s, l) {
8✔
943
                char *v;
5✔
944

945
                v = shell_escape(*s, bad);
5✔
946
                if (!v)
5✔
947
                        return NULL;
3✔
948

949
                free_and_replace(*s, v);
5✔
950
        }
951

952
        return l;
953
}
954

955
bool strv_fnmatch_full(
71,709✔
956
                char* const* patterns,
957
                const char *s,
958
                int flags,
959
                size_t *ret_matched_pos) {
960

961
        assert(s);
71,709✔
962

963
        if (patterns)
71,709✔
964
                for (size_t i = 0; patterns[i]; i++)
127,356✔
965
                        /* NB: We treat all fnmatch() errors as equivalent to FNM_NOMATCH, i.e. if fnmatch() fails to
966
                         * process the pattern for some reason we'll consider this equivalent to non-matching. */
967
                        if (fnmatch(patterns[i], s, flags) == 0) {
74,824✔
968
                                if (ret_matched_pos)
16,207✔
969
                                        *ret_matched_pos = i;
1,384✔
970
                                return true;
16,207✔
971
                        }
972

973
        if (ret_matched_pos)
55,502✔
974
                *ret_matched_pos = SIZE_MAX;
19,370✔
975

976
        return false;
977
}
978

979
char** strv_skip(char **l, size_t n) {
28,272✔
980
        while (n > 0) {
77,055✔
981
                if (strv_isempty(l))
49,795✔
982
                        return NULL;
983

984
                l++, n--;
48,783✔
985
        }
986

987
        /* To simplify callers, always return NULL instead of a zero-item array. */
988
        if (strv_isempty(l))
27,260✔
989
                return NULL;
11,319✔
990
        return l;
991
}
992

993
int strv_extend_n(char ***l, const char *value, size_t n) {
443✔
994
        size_t i, k;
443✔
995
        char **nl;
443✔
996

997
        assert(l);
443✔
998

999
        if (!value)
443✔
1000
                return 0;
1001
        if (n == 0)
443✔
1002
                return 0;
1003

1004
        /* Adds the value n times to l */
1005

1006
        k = strv_length(*l);
442✔
1007
        if (n >= SIZE_MAX - k)
442✔
1008
                return -ENOMEM;
1009

1010
        nl = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(k + n + 1), sizeof(char *));
442✔
1011
        if (!nl)
442✔
1012
                return -ENOMEM;
1013

1014
        *l = nl;
442✔
1015

1016
        for (i = k; i < k + n; i++) {
921✔
1017
                nl[i] = strdup(value);
479✔
1018
                if (!nl[i])
479✔
1019
                        goto rollback;
×
1020
        }
1021
        nl[i] = NULL;
442✔
1022

1023
        return 0;
442✔
1024

1025
rollback:
×
1026
        for (size_t j = k; j < i; j++)
×
1027
                free(nl[j]);
×
1028
        nl[k] = NULL;
×
1029

1030
        return -ENOMEM;
×
1031
}
1032

1033
int strv_extend_assignment(char ***l, const char *lhs, const char *rhs) {
5,059,062✔
1034
        char *j;
5,059,062✔
1035

1036
        assert(l);
5,059,062✔
1037
        assert(lhs);
5,059,062✔
1038

1039
        if (!rhs) /* value is optional, in which case we suppress the field */
5,059,062✔
1040
                return 0;
1041

1042
        j = strjoin(lhs, "=", rhs);
3,671,331✔
1043
        if (!j)
3,671,331✔
1044
                return -ENOMEM;
1045

1046
        return strv_consume(l, j);
3,671,331✔
1047
}
1048

1049
int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) {
14,339✔
1050
        bool b = false;
14,339✔
1051
        int r;
14,339✔
1052

1053
        assert(f);
14,339✔
1054

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

1057
        if (!space)
14,339✔
1058
                space = &b;
15✔
1059

1060
        STRV_FOREACH(s, l) {
14,769✔
1061
                r = fputs_with_separator(f, *s, separator, space);
430✔
1062
                if (r < 0)
430✔
1063
                        return r;
14,339✔
1064
        }
1065

1066
        return 0;
1067
}
1068

1069
void string_strv_hashmap_remove(Hashmap *h, const char *key, const char *value) {
134✔
1070
        assert(key);
134✔
1071

1072
        if (value) {
134✔
1073
                char **l = hashmap_get(h, key);
134✔
1074
                if (!l)
134✔
1075
                        return;
128✔
1076

1077
                strv_remove(l, value);
10✔
1078
                if (!strv_isempty(l))
134✔
1079
                        return;
1080
        }
1081

1082
        _unused_ _cleanup_free_ char *key_free = NULL;
6✔
1083
        strv_free(hashmap_remove2(h, key, (void**) &key_free));
6✔
1084
}
1085

1086
static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const char *value) {
263,323✔
1087
        char **l;
263,323✔
1088
        int r;
263,323✔
1089

1090
        assert(h);
263,323✔
1091
        assert(key);
263,323✔
1092
        assert(value);
263,323✔
1093

1094
        l = hashmap_get(h, key);
263,323✔
1095
        if (l) {
263,323✔
1096
                /* A list for this key already exists, let's append to it if it is not listed yet */
1097
                if (strv_contains(l, value))
8,757✔
1098
                        return 0;
263,323✔
1099

1100
                r = strv_extend(&l, value);
8,744✔
1101
                if (r < 0)
8,744✔
1102
                        return r;
1103

1104
                assert_se(hashmap_update(h, key, l) >= 0);
8,744✔
1105
        } else {
1106
                /* No list for this key exists yet, create one */
1107
                _cleanup_strv_free_ char **l2 = NULL;
×
1108
                _cleanup_free_ char *t = NULL;
254,566✔
1109

1110
                t = strdup(key);
254,566✔
1111
                if (!t)
254,566✔
1112
                        return -ENOMEM;
1113

1114
                r = strv_extend(&l2, value);
254,566✔
1115
                if (r < 0)
254,566✔
1116
                        return r;
1117

1118
                r = hashmap_put(h, t, l2);
254,566✔
1119
                if (r < 0)
254,566✔
1120
                        return r;
1121

1122
                TAKE_PTR(t);
254,566✔
1123
                TAKE_PTR(l2);
254,566✔
1124
        }
1125

1126
        return 1;
1127
}
1128

1129
int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value  HASHMAP_DEBUG_PARAMS) {
263,297✔
1130
        int r;
263,297✔
1131

1132
        assert(h);
263,297✔
1133
        assert(key);
263,297✔
1134
        assert(value);
263,297✔
1135

1136
        r = _hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free  HASHMAP_DEBUG_PASS_ARGS);
263,297✔
1137
        if (r < 0)
263,297✔
1138
                return r;
1139

1140
        return string_strv_hashmap_put_internal(*h, key, value);
263,297✔
1141
}
1142

1143
int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value  HASHMAP_DEBUG_PARAMS) {
26✔
1144
        int r;
26✔
1145

1146
        assert(h);
26✔
1147
        assert(key);
26✔
1148
        assert(value);
26✔
1149

1150
        r = _ordered_hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free  HASHMAP_DEBUG_PASS_ARGS);
26✔
1151
        if (r < 0)
26✔
1152
                return r;
1153

1154
        return string_strv_hashmap_put_internal(PLAIN_HASHMAP(*h), key, value);
26✔
1155
}
1156

1157
int strv_rebreak_lines(char **l, size_t width, char ***ret) {
12,852✔
1158
        _cleanup_strv_free_ char **broken = NULL;
12,852✔
1159
        int r;
12,852✔
1160

1161
        assert(ret);
12,852✔
1162

1163
        /* Implements a simple UTF-8 line breaking algorithm
1164
         *
1165
         * Goes through all entries in *l, and line-breaks each line that is longer than the specified
1166
         * character width. Breaks at the end of words/beginning of whitespace. Lines that do not contain whitespace are not
1167
         * broken. Retains whitespace at beginning of lines, removes it at end of lines. */
1168

1169
        if (width == SIZE_MAX) { /* NOP? */
12,852✔
1170
                broken = strv_copy(l);
2,870✔
1171
                if (!broken)
2,870✔
1172
                        return -ENOMEM;
1173

1174
                *ret = TAKE_PTR(broken);
2,870✔
1175
                return 0;
2,870✔
1176
        }
1177

1178
        STRV_FOREACH(i, l) {
20,090✔
1179
                const char *start = *i, *whitespace_begin = NULL, *whitespace_end = NULL;
1180
                bool in_prefix = true; /* still in the whitespace in the beginning of the line? */
1181
                size_t w = 0;
1182

1183
                for (const char *p = start; *p != 0; p = utf8_next_char(p)) {
546,738✔
1184
                        if (strchr(NEWLINE, *p)) {
536,630✔
1185
                                in_prefix = true;
1186
                                whitespace_begin = whitespace_end = NULL;
1187
                                w = 0;
1188
                        } else if (strchr(WHITESPACE, *p)) {
536,630✔
1189
                                if (!in_prefix && (!whitespace_begin || whitespace_end)) {
78,304✔
1190
                                        whitespace_begin = p;
77,707✔
1191
                                        whitespace_end = NULL;
77,707✔
1192
                                }
1193
                        } else {
1194
                                if (whitespace_begin && !whitespace_end)
458,326✔
1195
                                        whitespace_end = p;
77,698✔
1196

1197
                                in_prefix = false;
1198
                        }
1199

1200
                        int cw = utf8_char_console_width(p);
536,630✔
1201
                        if (cw < 0) {
536,630✔
1202
                                log_debug_errno(cw, "Comment to line break contains invalid UTF-8, ignoring.");
×
1203
                                cw = 1;
1204
                        }
1205

1206
                        w += cw;
536,630✔
1207

1208
                        if (w > width && whitespace_begin && whitespace_end) {
536,630✔
1209
                                _cleanup_free_ char *truncated = NULL;
4,615✔
1210

1211
                                truncated = strndup(start, whitespace_begin - start);
4,615✔
1212
                                if (!truncated)
4,615✔
1213
                                        return -ENOMEM;
1214

1215
                                r = strv_consume(&broken, TAKE_PTR(truncated));
4,615✔
1216
                                if (r < 0)
4,615✔
1217
                                        return r;
1218

1219
                                p = start = whitespace_end;
1220
                                whitespace_begin = whitespace_end = NULL;
1221
                                w = cw;
1222
                        }
1223
                }
1224

1225
                /* Process rest of the line */
1226
                assert(start);
10,108✔
1227
                if (in_prefix) /* Never seen anything non-whitespace? Generate empty line! */
10,108✔
1228
                        r = strv_extend(&broken, "");
1✔
1229
                else if (whitespace_begin && !whitespace_end) { /* Ends in whitespace? Chop it off! */
10,107✔
1230
                        _cleanup_free_ char *truncated = strndup(start, whitespace_begin - start);
×
1231
                        if (!truncated)
9✔
1232
                                return -ENOMEM;
×
1233

1234
                        r = strv_consume(&broken, TAKE_PTR(truncated));
9✔
1235
                } else /* Otherwise use line as is */
1236
                        r = strv_extend(&broken, start);
10,098✔
1237
                if (r < 0)
10,108✔
1238
                        return r;
1239
        }
1240

1241
        *ret = TAKE_PTR(broken);
9,982✔
1242
        return 0;
9,982✔
1243
}
1244

1245
char** strv_filter_prefix(char * const *l, const char *prefix) {
4✔
1246

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

1249
        if (isempty(prefix))
4✔
1250
                return strv_copy(l);
5✔
1251

1252
        _cleanup_strv_free_ char **f = NULL;
3✔
1253
        size_t sz = 0;
3✔
1254

1255
        STRV_FOREACH(i, l) {
24✔
1256
                if (!startswith(*i, prefix))
21✔
1257
                        continue;
16✔
1258

1259
                if (strv_extend_with_size(&f, &sz, *i) < 0)
5✔
1260
                        return NULL;
1261
        }
1262

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