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

systemd / systemd / 13000802859

27 Jan 2025 10:32PM UTC coverage: 71.53% (-0.1%) from 71.661%
13000802859

push

github

bluca
mkosi: add loongarch64 to Debian's list of EFI arches

291382 of 407356 relevant lines covered (71.53%)

711598.59 hits per line

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

95.46
/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 "memory-util.h"
16
#include "nulstr-util.h"
17
#include "sort-util.h"
18
#include "string-util.h"
19
#include "strv.h"
20
#include "utf8.h"
21

22
char* strv_find(char * const *l, const char *name) {
17,270,209✔
23
        assert(name);
17,270,209✔
24

25
        STRV_FOREACH(i, l)
189,087,880✔
26
                if (streq(*i, name))
173,296,771✔
27
                        return *i;
28

29
        return NULL;
30
}
31

32
char* strv_find_case(char * const *l, const char *name) {
1,297,674✔
33
        assert(name);
1,297,674✔
34

35
        STRV_FOREACH(i, l)
5,298,327✔
36
                if (strcaseeq(*i, name))
4,648,404✔
37
                        return *i;
38

39
        return NULL;
40
}
41

42
char* strv_find_prefix(char * const *l, const char *name) {
149✔
43
        assert(name);
149✔
44

45
        STRV_FOREACH(i, l)
1,333✔
46
                if (startswith(*i, name))
1,305✔
47
                        return *i;
48

49
        return NULL;
50
}
51

52
char* strv_find_startswith(char * const *l, const char *name) {
1,131,355✔
53
        assert(name);
1,131,355✔
54

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

58
        STRV_FOREACH(i, l) {
2,284,136✔
59
                char *e;
1,156,645✔
60

61
                e = startswith(*i, name);
1,156,645✔
62
                if (e)
1,156,645✔
63
                        return e;
64
        }
65

66
        return NULL;
67
}
68

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

73
        assert(name);
28✔
74

75
        STRV_FOREACH(s, l) {
180✔
76
                char *e = startswith(*s, name);
152✔
77
                if (!e)
152✔
78
                        continue;
92✔
79

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

87
        return best;
28✔
88
}
89

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

94
        assert(name);
13✔
95

96
        STRV_FOREACH(i, l) {
71✔
97
                ssize_t distance;
58✔
98

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

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

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

114
        return best;
115
}
116

117
char* strv_find_closest(char * const *l, const char *name) {
28✔
118
        assert(name);
28✔
119

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

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

128
        return strv_find_closest_by_levenshtein(l, name);
13✔
129
}
130

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

138
        return NULL;
139
}
140

141
char** strv_free(char **l) {
5,927,050✔
142
        STRV_FOREACH(k, l)
21,999,536✔
143
                free(*k);
16,072,486✔
144

145
        return mfree(l);
5,927,050✔
146
}
147

148
char** strv_free_erase(char **l) {
18,795✔
149
        STRV_FOREACH(i, l)
20,855✔
150
                erase_and_freep(i);
2,060✔
151

152
        return mfree(l);
18,795✔
153
}
154

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

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

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

164
char** strv_copy_n(char * const *l, size_t m) {
151,446✔
165
        _cleanup_strv_free_ char **result = NULL;
151,446✔
166
        char **k;
151,446✔
167

168
        result = new(char*, MIN(strv_length(l), m) + 1);
151,446✔
169
        if (!result)
151,446✔
170
                return NULL;
171

172
        k = result;
173
        STRV_FOREACH(i, l) {
429,518✔
174
                if (m == 0)
278,078✔
175
                        break;
176

177
                *k = strdup(*i);
278,072✔
178
                if (!*k)
278,072✔
179
                        return NULL;
180
                k++;
278,072✔
181

182
                if (m != SIZE_MAX)
278,072✔
183
                        m--;
84✔
184
        }
185

186
        *k = NULL;
151,446✔
187
        return TAKE_PTR(result);
151,446✔
188
}
189

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

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

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

202
        *ret = TAKE_PTR(copy);
41✔
203
        return 1;
41✔
204
}
205

206
size_t strv_length(char * const *l) {
10,159,857✔
207
        size_t n = 0;
10,159,857✔
208

209
        STRV_FOREACH(i, l)
269,392,685✔
210
                n++;
259,232,828✔
211

212
        return n;
10,159,857✔
213
}
214

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

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

225
        va_copy(aq, ap);
23,380✔
226
        for (const char *s = x; s; s = va_arg(aq, const char*)) {
166,146✔
227
                if (s == STRV_IGNORE)
142,766✔
228
                        continue;
4,971✔
229

230
                n++;
137,795✔
231
        }
232
        va_end(aq);
23,380✔
233

234
        a = new(char*, n+1);
23,380✔
235
        if (!a)
23,380✔
236
                return NULL;
237

238
        for (const char *s = x; s; s = va_arg(ap, const char*)) {
166,146✔
239
                if (s == STRV_IGNORE)
142,766✔
240
                        continue;
4,971✔
241

242
                a[i] = strdup(s);
137,795✔
243
                if (!a[i])
137,795✔
244
                        return NULL;
245

246
                i++;
137,795✔
247
        }
248

249
        a[i] = NULL;
23,380✔
250

251
        return TAKE_PTR(a);
23,380✔
252
}
253

254
char** strv_new_internal(const char *x, ...) {
21,083✔
255
        char **r;
21,083✔
256
        va_list ap;
21,083✔
257

258
        va_start(ap, x);
21,083✔
259
        r = strv_new_ap(x, ap);
21,083✔
260
        va_end(ap);
21,083✔
261

262
        return r;
21,083✔
263
}
264

265
int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
36,299✔
266
        size_t p, q, i = 0;
36,299✔
267

268
        assert(a);
36,299✔
269

270
        q = strv_length(b);
36,299✔
271
        if (q == 0)
36,299✔
272
                return 0;
273

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

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

282
        t[p] = NULL;
33,719✔
283
        *a = t;
33,719✔
284

285
        STRV_FOREACH(s, b) {
73,625✔
286
                if (filter_duplicates && strv_contains(t, *s))
39,906✔
287
                        continue;
51✔
288

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

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

297
        assert(i <= q);
33,719✔
298

299
        return (int) i;
33,719✔
300

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

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

311
        assert(a);
132,778✔
312

313
        q = strv_length(b);
132,778✔
314
        if (q == 0)
132,778✔
315
                return 0;
316

317
        p = strv_length(*a);
72,926✔
318
        if (p == 0) {
72,926✔
319
                strv_free_and_replace(*a, b_consume);
59,426✔
320

321
                if (filter_duplicates)
59,426✔
322
                        strv_uniq(*a);
16,440✔
323

324
                return strv_length(*a);
59,426✔
325
        }
326

327
        if (p >= SIZE_MAX - q)
13,500✔
328
                return -ENOMEM;
329

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

334
        t[p] = NULL;
13,500✔
335
        *a = t;
13,500✔
336

337
        if (!filter_duplicates) {
13,500✔
338
                *mempcpy_typesafe(t + p, b, q) = NULL;
13,010✔
339
                i = q;
13,010✔
340
        } else {
341
                i = 0;
342

343
                STRV_FOREACH(s, b) {
1,383✔
344
                        if (strv_contains(t, *s)) {
893✔
345
                                free(*s);
173✔
346
                                continue;
173✔
347
                        }
348

349
                        t[p+i] = *s;
720✔
350

351
                        i++;
720✔
352
                        t[p+i] = NULL;
720✔
353
                }
354
        }
355

356
        assert(i <= q);
13,500✔
357

358
        b_consume = mfree(b_consume);
13,500✔
359

360
        return (int) i;
13,500✔
361
}
362

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

366
        assert(a);
80,923✔
367

368
        STRV_FOREACH(s, b) {
403,550✔
369
                char *v;
322,627✔
370

371
                v = strjoin(strempty(prefix), *s, suffix);
645,164✔
372
                if (!v)
322,627✔
373
                        return -ENOMEM;
374

375
                r = strv_consume(a, v);
322,627✔
376
                if (r < 0)
322,627✔
377
                        return r;
378
        }
379

380
        return 0;
381
}
382

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

388
        assert(s);
638,677✔
389

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

393
        r = strv_split_full(&l, s, NEWLINE, flags);
638,677✔
394
        if (r < 0)
638,677✔
395
                return r;
396

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

403
        *ret = TAKE_PTR(l);
638,677✔
404
        return n;
638,677✔
405
}
406

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

412
        assert(t);
893,736✔
413
        assert(s);
893,736✔
414

415
        for (;;) {
17,375,126✔
416
                _cleanup_free_ char *word = NULL;
8,240,704✔
417

418
                r = extract_first_word(&s, &word, separators, flags);
9,134,431✔
419
                if (r < 0)
9,134,431✔
420
                        return r;
421
                if (r == 0)
9,134,422✔
422
                        break;
423

424
                if (!GREEDY_REALLOC(l, n + 2))
8,240,695✔
425
                        return -ENOMEM;
426

427
                l[n++] = TAKE_PTR(word);
8,240,695✔
428
                l[n] = NULL;
8,240,695✔
429
        }
430

431
        if (!l) {
893,727✔
432
                l = new0(char*, 1);
112,249✔
433
                if (!l)
112,249✔
434
                        return -ENOMEM;
435
        }
436

437
        *t = TAKE_PTR(l);
893,727✔
438

439
        return (int) n;
893,727✔
440
}
441

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

446
        assert(t);
747✔
447
        assert(s);
747✔
448

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

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

457
        return (int) strv_length(*t);
747✔
458
}
459

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

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

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

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

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

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

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

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

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

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

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

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

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

516
        if (!separator)
69,451✔
517
                separator = " ";
2✔
518

519
        k = strlen(separator);
69,451✔
520
        m = strlen_ptr(prefix);
69,451✔
521

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

525
        n = 0;
526
        STRV_FOREACH(s, l) {
282,423✔
527
                if (s != l)
212,972✔
528
                        n += k;
144,351✔
529

530
                bool needs_escaping = escape_separator && strchr(*s, *separator);
212,972✔
531

532
                n += m + strlen(*s) * (1 + needs_escaping);
212,972✔
533
        }
534

535
        r = new(char, n+1);
69,451✔
536
        if (!r)
69,451✔
537
                return NULL;
538

539
        e = r;
540
        STRV_FOREACH(s, l) {
282,423✔
541
                if (s != l)
212,972✔
542
                        e = stpcpy(e, separator);
144,351✔
543

544
                if (prefix)
212,972✔
545
                        e = stpcpy(e, prefix);
17✔
546

547
                bool needs_escaping = escape_separator && strchr(*s, *separator);
212,972✔
548

549
                if (needs_escaping)
212,957✔
550
                        for (size_t i = 0; (*s)[i]; i++) {
118✔
551
                                if ((*s)[i] == *separator)
103✔
552
                                        *(e++) = '\\';
20✔
553
                                *(e++) = (*s)[i];
103✔
554
                        }
555
                else
556
                        e = stpcpy(e, *s);
212,957✔
557
        }
558

559
        *e = 0;
69,451✔
560

561
        return r;
69,451✔
562
}
563

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

570
        if (!value)
7,205,489✔
571
                return 0;
572

573
        size_t size = n ? *n : SIZE_MAX;
7,205,487✔
574
        if (size == SIZE_MAX)
395✔
575
                size = strv_length(*l);
7,205,105✔
576

577
        /* Check for overflow */
578
        if (size > SIZE_MAX-2)
7,205,487✔
579
                return -ENOMEM;
580

581
        char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(size + 2), sizeof(char*));
7,205,487✔
582
        if (!c)
7,205,487✔
583
                return -ENOMEM;
584

585
        c[size] = value;
7,205,487✔
586
        c[size+1] = NULL;
7,205,487✔
587

588
        *l = c;
7,205,487✔
589
        if (n)
7,205,487✔
590
                *n = size + 1;
395✔
591
        return 0;
592
}
593

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

598
        if (!a && !b)
470✔
599
                return 0;
600

601
        n = strv_length(*l);
470✔
602

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

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

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

618
        *l = c;
470✔
619
        return 0;
470✔
620
}
621

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

626
        assert(l);
4,149✔
627

628
        if (!value)
4,149✔
629
                return 0;
630

631
        n = strv_length(*l);
4,148✔
632
        position = MIN(position, n);
4,148✔
633

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

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

643
        if (n > position)
4,148✔
644
                memmove(c + position + 1, c + position, (n - position) * sizeof(char*));
1,071✔
645

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

649
        *l = c;
4,148✔
650
        return 0;
4,148✔
651
}
652

653
int strv_consume_with_size(char ***l, size_t *n, char *value) {
7,152,503✔
654
        int r;
7,152,503✔
655

656
        r = strv_push_with_size(l, n, value);
7,152,503✔
657
        if (r < 0)
7,152,503✔
658
                free(value);
×
659

660
        return r;
7,152,503✔
661
}
662

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

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

672
        return r;
425✔
673
}
674

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

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

682
        return r;
4,131✔
683
}
684

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

688
        if (!value)
3,406✔
689
                return 0;
690

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

695
        return strv_consume_prepend(l, v);
3,320✔
696
}
697

698
int strv_extend_with_size(char ***l, size_t *n, const char *value) {
1,134,779✔
699
        char *v;
1,134,779✔
700

701
        if (!value)
1,134,779✔
702
                return 0;
703

704
        v = strdup(value);
1,130,087✔
705
        if (!v)
1,130,087✔
706
                return -ENOMEM;
707

708
        return strv_consume_with_size(l, n, v);
1,130,087✔
709
}
710

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

716
        assert(l);
341✔
717

718
        m = n = strv_length(*l);
341✔
719

720
        r = 0;
341✔
721
        va_start(ap, value);
341✔
722
        for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
1,256✔
723
                if (!s)
915✔
724
                        continue;
27✔
725

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

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

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

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

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

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

767
        c[i] = NULL;
341✔
768
        return 0;
341✔
769
}
770

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

775
        STRV_FOREACH(i, l)
236,906✔
776
                strv_remove(i+1, *i);
193,594✔
777

778
        return l;
43,312✔
779
}
780

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

786
        return true;
787
}
788

789
char** strv_remove(char **l, const char *s) {
193,650✔
790
        char **f, **t;
193,650✔
791

792
        if (!l)
193,650✔
793
                return NULL;
794

795
        assert(s);
193,650✔
796

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

800
        for (f = t = l; *f; f++)
1,128,207✔
801
                if (streq(*f, s))
934,557✔
802
                        free(*f);
30,316✔
803
                else
804
                        *(t++) = *f;
904,241✔
805

806
        *t = NULL;
193,650✔
807
        return l;
193,650✔
808
}
809

810
bool strv_overlap(char * const *a, char * const *b) {
381✔
811
        STRV_FOREACH(i, a)
694✔
812
                if (strv_contains(b, *i))
384✔
813
                        return true;
814

815
        return false;
816
}
817

818
static int str_compare(char * const *a, char * const *b) {
432,940✔
819
        return strcmp(*a, *b);
432,940✔
820
}
821

822
char** strv_sort(char **l) {
11,386✔
823
        typesafe_qsort(l, strv_length(l), str_compare);
11,386✔
824
        return l;
11,386✔
825
}
826

827
char** strv_sort_uniq(char **l) {
45,275✔
828
        if (strv_isempty(l))
45,275✔
829
                return l;
830

831
        char **tail = strv_sort(l), *prev = NULL;
191✔
832
        STRV_FOREACH(i, l)
3,413✔
833
                if (streq_ptr(*i, prev))
3,222✔
834
                        free(*i);
27✔
835
                else
836
                        *(tail++) = prev = *i;
3,195✔
837

838
        *tail = NULL;
191✔
839
        return l;
191✔
840
}
841

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

845
        if (strv_isempty(a)) {
3,055✔
846
                if (strv_isempty(b))
1,109✔
847
                        return 0;
1,065✔
848
                else
849
                        return -1;
850
        }
851

852
        if (strv_isempty(b))
1,946✔
853
                return 1;
854

855
        for ( ; *a || *b; ++a, ++b) {
4,789✔
856
                r = strcmp_ptr(*a, *b);
2,970✔
857
                if (r != 0)
2,970✔
858
                        return r;
859
        }
860

861
        return 0;
862
}
863

864
bool strv_equal_ignore_order(char **a, char **b) {
35✔
865

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

870
        if (a == b)
35✔
871
                return true;
872

873
        STRV_FOREACH(i, a)
35✔
874
                if (!strv_contains(b, *i))
22✔
875
                        return false;
876

877
        STRV_FOREACH(i, b)
31✔
878
                if (!strv_contains(a, *i))
22✔
879
                        return false;
880

881
        return true;
882
}
883

884
void strv_print_full(char * const *l, const char *prefix) {
59✔
885
        STRV_FOREACH(s, l)
2,504✔
886
                printf("%s%s\n", strempty(prefix), *s);
4,890✔
887
}
59✔
888

889
int strv_extendf(char ***l, const char *format, ...) {
1,090,667✔
890
        va_list ap;
1,090,667✔
891
        char *x;
1,090,667✔
892
        int r;
1,090,667✔
893

894
        va_start(ap, format);
1,090,667✔
895
        r = vasprintf(&x, format, ap);
1,090,667✔
896
        va_end(ap);
1,090,667✔
897

898
        if (r < 0)
1,090,667✔
899
                return -ENOMEM;
1,090,667✔
900

901
        return strv_consume(l, x);
1,090,667✔
902
}
903

904
char* startswith_strv(const char *s, char * const *l) {
13,080,202✔
905
        STRV_FOREACH(i, l) {
39,571,101✔
906
                char *found = startswith(s, *i);
26,559,730✔
907
                if (found)
26,559,730✔
908
                        return found;
909
        }
910

911
        return NULL;
912
}
913

914
char* endswith_strv(const char *s, char * const *l) {
3,427,021✔
915
        STRV_FOREACH(i, l) {
10,260,372✔
916
                char *found = endswith(s, *i);
6,860,715✔
917
                if (found)
6,860,715✔
918
                        return found;
919
        }
920

921
        return NULL;
922
}
923

924
char** strv_reverse(char **l) {
832✔
925
        size_t n;
832✔
926

927
        n = strv_length(l);
832✔
928
        if (n <= 1)
832✔
929
                return l;
930

931
        for (size_t i = 0; i < n / 2; i++)
270✔
932
                SWAP_TWO(l[i], l[n-1-i]);
139✔
933

934
        return l;
935
}
936

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

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

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

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

951
        return l;
952
}
953

954
bool strv_fnmatch_full(
57,683✔
955
                char* const* patterns,
956
                const char *s,
957
                int flags,
958
                size_t *ret_matched_pos) {
959

960
        assert(s);
57,683✔
961

962
        if (patterns)
57,683✔
963
                for (size_t i = 0; patterns[i]; i++)
107,902✔
964
                        /* NB: We treat all fnmatch() errors as equivalent to FNM_NOMATCH, i.e. if fnmatch() fails to
965
                         * process the pattern for some reason we'll consider this equivalent to non-matching. */
966
                        if (fnmatch(patterns[i], s, flags) == 0) {
60,661✔
967
                                if (ret_matched_pos)
7,609✔
968
                                        *ret_matched_pos = i;
1,315✔
969
                                return true;
7,609✔
970
                        }
971

972
        if (ret_matched_pos)
50,074✔
973
                *ret_matched_pos = SIZE_MAX;
18,286✔
974

975
        return false;
976
}
977

978
char** strv_skip(char **l, size_t n) {
30,075✔
979

980
        while (n > 0) {
82,275✔
981
                if (strv_isempty(l))
52,354✔
982
                        return l;
983

984
                l++, n--;
52,200✔
985
        }
986

987
        return l;
988
}
989

990
int strv_extend_n(char ***l, const char *value, size_t n) {
888✔
991
        size_t i, k;
888✔
992
        char **nl;
888✔
993

994
        assert(l);
888✔
995

996
        if (!value)
888✔
997
                return 0;
998
        if (n == 0)
888✔
999
                return 0;
1000

1001
        /* Adds the value n times to l */
1002

1003
        k = strv_length(*l);
887✔
1004
        if (n >= SIZE_MAX - k)
887✔
1005
                return -ENOMEM;
1006

1007
        nl = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(k + n + 1), sizeof(char *));
887✔
1008
        if (!nl)
887✔
1009
                return -ENOMEM;
1010

1011
        *l = nl;
887✔
1012

1013
        for (i = k; i < k + n; i++) {
1,863✔
1014
                nl[i] = strdup(value);
976✔
1015
                if (!nl[i])
976✔
1016
                        goto rollback;
×
1017
        }
1018
        nl[i] = NULL;
887✔
1019

1020
        return 0;
887✔
1021

1022
rollback:
×
1023
        for (size_t j = k; j < i; j++)
×
1024
                free(nl[j]);
×
1025
        nl[k] = NULL;
×
1026

1027
        return -ENOMEM;
×
1028
}
1029

1030
int strv_extend_assignment(char ***l, const char *lhs, const char *rhs) {
5,363,829✔
1031
        char *j;
5,363,829✔
1032

1033
        assert(l);
5,363,829✔
1034
        assert(lhs);
5,363,829✔
1035

1036
        if (!rhs) /* value is optional, in which case we suppress the field */
5,363,829✔
1037
                return 0;
1038

1039
        j = strjoin(lhs, "=", rhs);
3,984,980✔
1040
        if (!j)
3,984,980✔
1041
                return -ENOMEM;
1042

1043
        return strv_consume(l, j);
3,984,980✔
1044
}
1045

1046
int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) {
13,761✔
1047
        bool b = false;
13,761✔
1048
        int r;
13,761✔
1049

1050
        assert(f);
13,761✔
1051

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

1054
        if (!space)
13,761✔
1055
                space = &b;
15✔
1056

1057
        STRV_FOREACH(s, l) {
13,925✔
1058
                r = fputs_with_separator(f, *s, separator, space);
164✔
1059
                if (r < 0)
164✔
1060
                        return r;
13,761✔
1061
        }
1062

1063
        return 0;
1064
}
1065

1066
static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const char *value) {
217,292✔
1067
        char **l;
217,292✔
1068
        int r;
217,292✔
1069

1070
        assert(h);
217,292✔
1071
        assert(key);
217,292✔
1072
        assert(value);
217,292✔
1073

1074
        l = hashmap_get(h, key);
217,292✔
1075
        if (l) {
217,292✔
1076
                /* A list for this key already exists, let's append to it if it is not listed yet */
1077
                if (strv_contains(l, value))
7,886✔
1078
                        return 0;
217,292✔
1079

1080
                r = strv_extend(&l, value);
7,874✔
1081
                if (r < 0)
7,874✔
1082
                        return r;
1083

1084
                assert_se(hashmap_update(h, key, l) >= 0);
7,874✔
1085
        } else {
1086
                /* No list for this key exists yet, create one */
1087
                _cleanup_strv_free_ char **l2 = NULL;
×
1088
                _cleanup_free_ char *t = NULL;
209,406✔
1089

1090
                t = strdup(key);
209,406✔
1091
                if (!t)
209,406✔
1092
                        return -ENOMEM;
1093

1094
                r = strv_extend(&l2, value);
209,406✔
1095
                if (r < 0)
209,406✔
1096
                        return r;
1097

1098
                r = hashmap_put(h, t, l2);
209,406✔
1099
                if (r < 0)
209,406✔
1100
                        return r;
1101

1102
                TAKE_PTR(t);
209,406✔
1103
                TAKE_PTR(l2);
209,406✔
1104
        }
1105

1106
        return 1;
1107
}
1108

1109
int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value  HASHMAP_DEBUG_PARAMS) {
216,772✔
1110
        int r;
216,772✔
1111

1112
        assert(h);
216,772✔
1113
        assert(key);
216,772✔
1114
        assert(value);
216,772✔
1115

1116
        r = _hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free  HASHMAP_DEBUG_PASS_ARGS);
216,772✔
1117
        if (r < 0)
216,772✔
1118
                return r;
1119

1120
        return string_strv_hashmap_put_internal(*h, key, value);
216,772✔
1121
}
1122

1123
int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value  HASHMAP_DEBUG_PARAMS) {
520✔
1124
        int r;
520✔
1125

1126
        assert(h);
520✔
1127
        assert(key);
520✔
1128
        assert(value);
520✔
1129

1130
        r = _ordered_hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free  HASHMAP_DEBUG_PASS_ARGS);
520✔
1131
        if (r < 0)
520✔
1132
                return r;
1133

1134
        return string_strv_hashmap_put_internal(PLAIN_HASHMAP(*h), key, value);
520✔
1135
}
1136

1137
int strv_rebreak_lines(char **l, size_t width, char ***ret) {
10,805✔
1138
        _cleanup_strv_free_ char **broken = NULL;
10,805✔
1139
        int r;
10,805✔
1140

1141
        assert(ret);
10,805✔
1142

1143
        /* Implements a simple UTF-8 line breaking algorithm
1144
         *
1145
         * Goes through all entries in *l, and line-breaks each line that is longer than the specified
1146
         * character width. Breaks at the end of words/beginning of whitespace. Lines that do not contain whitespace are not
1147
         * broken. Retains whitespace at beginning of lines, removes it at end of lines. */
1148

1149
        if (width == SIZE_MAX) { /* NOP? */
10,805✔
1150
                broken = strv_copy(l);
2,498✔
1151
                if (!broken)
2,498✔
1152
                        return -ENOMEM;
1153

1154
                *ret = TAKE_PTR(broken);
2,498✔
1155
                return 0;
2,498✔
1156
        }
1157

1158
        STRV_FOREACH(i, l) {
16,740✔
1159
                const char *start = *i, *whitespace_begin = NULL, *whitespace_end = NULL;
1160
                bool in_prefix = true; /* still in the whitespace in the beginning of the line? */
1161
                size_t w = 0;
1162

1163
                for (const char *p = start; *p != 0; p = utf8_next_char(p)) {
462,814✔
1164
                        if (strchr(NEWLINE, *p)) {
454,381✔
1165
                                in_prefix = true;
1166
                                whitespace_begin = whitespace_end = NULL;
1167
                                w = 0;
1168
                        } else if (strchr(WHITESPACE, *p)) {
454,381✔
1169
                                if (!in_prefix && (!whitespace_begin || whitespace_end)) {
67,157✔
1170
                                        whitespace_begin = p;
66,560✔
1171
                                        whitespace_end = NULL;
66,560✔
1172
                                }
1173
                        } else {
1174
                                if (whitespace_begin && !whitespace_end)
387,224✔
1175
                                        whitespace_end = p;
66,551✔
1176

1177
                                in_prefix = false;
1178
                        }
1179

1180
                        int cw = utf8_char_console_width(p);
454,381✔
1181
                        if (cw < 0) {
454,381✔
1182
                                log_debug_errno(cw, "Comment to line break contains invalid UTF-8, ignoring.");
×
1183
                                cw = 1;
1184
                        }
1185

1186
                        w += cw;
454,381✔
1187

1188
                        if (w > width && whitespace_begin && whitespace_end) {
454,381✔
1189
                                _cleanup_free_ char *truncated = NULL;
3,627✔
1190

1191
                                truncated = strndup(start, whitespace_begin - start);
3,627✔
1192
                                if (!truncated)
3,627✔
1193
                                        return -ENOMEM;
1194

1195
                                r = strv_consume(&broken, TAKE_PTR(truncated));
3,627✔
1196
                                if (r < 0)
3,627✔
1197
                                        return r;
1198

1199
                                p = start = whitespace_end;
1200
                                whitespace_begin = whitespace_end = NULL;
1201
                                w = cw;
1202
                        }
1203
                }
1204

1205
                /* Process rest of the line */
1206
                assert(start);
8,433✔
1207
                if (in_prefix) /* Never seen anything non-whitespace? Generate empty line! */
8,433✔
1208
                        r = strv_extend(&broken, "");
1✔
1209
                else if (whitespace_begin && !whitespace_end) { /* Ends in whitespace? Chop it off! */
8,432✔
1210
                        _cleanup_free_ char *truncated = strndup(start, whitespace_begin - start);
×
1211
                        if (!truncated)
9✔
1212
                                return -ENOMEM;
×
1213

1214
                        r = strv_consume(&broken, TAKE_PTR(truncated));
9✔
1215
                } else /* Otherwise use line as is */
1216
                        r = strv_extend(&broken, start);
8,423✔
1217
                if (r < 0)
8,433✔
1218
                        return r;
1219
        }
1220

1221
        *ret = TAKE_PTR(broken);
8,307✔
1222
        return 0;
8,307✔
1223
}
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