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

systemd / systemd / 13380515387

17 Feb 2025 09:20PM UTC coverage: 71.822% (+0.1%) from 71.714%
13380515387

push

github

DaanDeMeyer
ukify: print all remaining log-like output to stderr

We want to be able to capture stdout for json and such, so convert
all remaining logging to stderr.

293883 of 409184 relevant lines covered (71.82%)

716959.33 hits per line

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

95.45
/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,369,919✔
23
        assert(name);
17,369,919✔
24

25
        STRV_FOREACH(i, l)
189,658,020✔
26
                if (streq(*i, name))
173,767,985✔
27
                        return *i;
28

29
        return NULL;
30
}
31

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

35
        STRV_FOREACH(i, l)
5,410,625✔
36
                if (strcaseeq(*i, name))
4,746,048✔
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,138,643✔
53
        assert(name);
1,138,643✔
54

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

58
        STRV_FOREACH(i, l) {
2,299,347✔
59
                char *e;
1,164,602✔
60

61
                e = startswith(*i, name);
1,164,602✔
62
                if (e)
1,164,602✔
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,941,990✔
142
        STRV_FOREACH(k, l)
22,125,083✔
143
                free(*k);
16,183,093✔
144

145
        return mfree(l);
5,941,990✔
146
}
147

148
char** strv_free_erase(char **l) {
19,764✔
149
        STRV_FOREACH(i, l)
22,319✔
150
                erase_and_freep(i);
2,555✔
151

152
        return mfree(l);
19,764✔
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) {
152,530✔
165
        _cleanup_strv_free_ char **result = NULL;
152,530✔
166
        char **k;
152,530✔
167

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

172
        k = result;
173
        STRV_FOREACH(i, l) {
433,455✔
174
                if (m == 0)
280,931✔
175
                        break;
176

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

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

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

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

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

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

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

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

209
        STRV_FOREACH(i, l)
270,166,422✔
210
                n++;
259,988,667✔
211

212
        return n;
10,177,755✔
213
}
214

215
char** strv_new_ap(const char *x, va_list ap) {
23,655✔
216
        _cleanup_strv_free_ char **a = NULL;
23,655✔
217
        size_t n = 0, i = 0;
23,655✔
218
        va_list aq;
23,655✔
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,655✔
226
        for (const char *s = x; s; s = va_arg(aq, const char*)) {
166,498✔
227
                if (s == STRV_IGNORE)
142,843✔
228
                        continue;
4,972✔
229

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

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

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

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

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

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

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

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

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

262
        return r;
21,350✔
263
}
264

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

268
        assert(a);
36,312✔
269

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

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

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

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

285
        STRV_FOREACH(s, b) {
73,604✔
286
                if (filter_duplicates && strv_contains(t, *s))
39,905✔
287
                        continue;
53✔
288

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

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

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

299
        return (int) i;
33,699✔
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,978✔
308
        _cleanup_strv_free_ char **b_consume = b;
132,978✔
309
        size_t p, q, i;
132,978✔
310

311
        assert(a);
132,978✔
312

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

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

321
                if (filter_duplicates)
59,306✔
322
                        strv_uniq(*a);
16,410✔
323

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

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

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

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

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

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

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

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

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

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

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

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

366
        assert(a);
81,320✔
367

368
        STRV_FOREACH(s, b) {
405,531✔
369
                char *v;
324,211✔
370

371
                v = strjoin(strempty(prefix), *s, suffix);
648,332✔
372
                if (!v)
324,211✔
373
                        return -ENOMEM;
374

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

380
        return 0;
381
}
382

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

388
        assert(s);
634,097✔
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);
634,097✔
394
        if (r < 0)
634,097✔
395
                return r;
396

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

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

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

412
        assert(t);
890,295✔
413
        assert(s);
890,295✔
414

415
        for (;;) {
17,558,949✔
416
                _cleanup_free_ char *word = NULL;
8,334,336✔
417

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

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

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

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

437
        *t = TAKE_PTR(l);
890,286✔
438

439
        return (int) n;
890,286✔
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) {
71,252✔
513
        char *r, *e;
71,252✔
514
        size_t n, k, m;
71,252✔
515

516
        if (!separator)
71,252✔
517
                separator = " ";
2✔
518

519
        k = strlen(separator);
71,252✔
520
        m = strlen_ptr(prefix);
71,252✔
521

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

525
        n = 0;
526
        STRV_FOREACH(s, l) {
290,467✔
527
                if (s != l)
219,215✔
528
                        n += k;
148,792✔
529

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

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

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

539
        e = r;
540
        STRV_FOREACH(s, l) {
290,467✔
541
                if (s != l)
219,215✔
542
                        e = stpcpy(e, separator);
148,792✔
543

544
                if (prefix)
219,215✔
545
                        e = stpcpy(e, prefix);
17✔
546

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

549
                if (needs_escaping)
219,200✔
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);
219,200✔
557
        }
558

559
        *e = 0;
71,252✔
560

561
        return r;
71,252✔
562
}
563

564
int strv_push_with_size(char ***l, size_t *n, char *value) {
7,220,297✔
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,220,297✔
571
                return 0;
572

573
        size_t size = n ? *n : SIZE_MAX;
7,220,295✔
574
        if (size == SIZE_MAX)
411✔
575
                size = strv_length(*l);
7,219,897✔
576

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

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

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

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

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

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

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

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

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

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

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

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

626
        assert(l);
4,194✔
627

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

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

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

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

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

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

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

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

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

660
        return r;
7,165,523✔
661
}
662

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

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

672
        return r;
429✔
673
}
674

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

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

682
        return r;
4,176✔
683
}
684

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

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

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

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

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

701
        if (!value)
1,138,791✔
702
                return 0;
703

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

708
        return strv_consume_with_size(l, n, v);
1,134,075✔
709
}
710

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

716
        assert(l);
343✔
717

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

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

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

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

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

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

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

760
        if (r < 0) {
343✔
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;
343✔
768
        return 0;
343✔
769
}
770

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

775
        STRV_FOREACH(i, l)
235,988✔
776
                strv_remove(i+1, *i);
192,826✔
777

778
        return l;
43,162✔
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) {
192,892✔
790
        char **f, **t;
192,892✔
791

792
        if (!l)
192,892✔
793
                return NULL;
794

795
        assert(s);
192,892✔
796

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

800
        for (f = t = l; *f; f++)
1,126,111✔
801
                if (streq(*f, s))
933,219✔
802
                        free(*f);
30,345✔
803
                else
804
                        *(t++) = *f;
902,874✔
805

806
        *t = NULL;
192,892✔
807
        return l;
192,892✔
808
}
809

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

815
        return false;
816
}
817

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

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

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

831
        char **tail = strv_sort(l), *prev = NULL;
192✔
832
        STRV_FOREACH(i, l)
3,421✔
833
                if (streq_ptr(*i, prev))
3,229✔
834
                        free(*i);
27✔
835
                else
836
                        *(tail++) = prev = *i;
3,202✔
837

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

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

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

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

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

861
        return 0;
862
}
863

864
bool strv_equal_ignore_order(char **a, char **b) {
416✔
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)
416✔
871
                return true;
872

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

877
        STRV_FOREACH(i, b)
803✔
878
                if (!strv_contains(a, *i))
416✔
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,507✔
886
                printf("%s%s\n", strempty(prefix), *s);
4,896✔
887
}
59✔
888

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

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

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

901
        return strv_consume(l, x);
1,088,361✔
902
}
903

904
char* startswith_strv(const char *s, char * const *l) {
13,390,477✔
905
        STRV_FOREACH(i, l) {
40,829,344✔
906
                char *found = startswith(s, *i);
27,520,370✔
907
                if (found)
27,520,370✔
908
                        return found;
909
        }
910

911
        return NULL;
912
}
913

914
char* endswith_strv(const char *s, char * const *l) {
3,421,823✔
915
        STRV_FOREACH(i, l) {
10,244,594✔
916
                char *found = endswith(s, *i);
6,850,265✔
917
                if (found)
6,850,265✔
918
                        return found;
919
        }
920

921
        return NULL;
922
}
923

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

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

931
        for (size_t i = 0; i < n / 2; i++)
294✔
932
                SWAP_TWO(l[i], l[n-1-i]);
151✔
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,685✔
955
                char* const* patterns,
956
                const char *s,
957
                int flags,
958
                size_t *ret_matched_pos) {
959

960
        assert(s);
57,685✔
961

962
        if (patterns)
57,685✔
963
                for (size_t i = 0; patterns[i]; i++)
107,823✔
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,588✔
967
                                if (ret_matched_pos)
7,617✔
968
                                        *ret_matched_pos = i;
1,329✔
969
                                return true;
7,617✔
970
                        }
971

972
        if (ret_matched_pos)
50,068✔
973
                *ret_matched_pos = SIZE_MAX;
18,447✔
974

975
        return false;
976
}
977

978
char** strv_skip(char **l, size_t n) {
31,074✔
979

980
        while (n > 0) {
86,421✔
981
                if (strv_isempty(l))
55,503✔
982
                        return l;
983

984
                l++, n--;
55,347✔
985
        }
986

987
        return l;
988
}
989

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

994
        assert(l);
889✔
995

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

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

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

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

1011
        *l = nl;
888✔
1012

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

1020
        return 0;
888✔
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,377,840✔
1031
        char *j;
5,377,840✔
1032

1033
        assert(l);
5,377,840✔
1034
        assert(lhs);
5,377,840✔
1035

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

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

1043
        return strv_consume(l, j);
3,992,276✔
1044
}
1045

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

1050
        assert(f);
14,084✔
1051

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

1054
        if (!space)
14,084✔
1055
                space = &b;
15✔
1056

1057
        STRV_FOREACH(s, l) {
14,515✔
1058
                r = fputs_with_separator(f, *s, separator, space);
431✔
1059
                if (r < 0)
431✔
1060
                        return r;
14,084✔
1061
        }
1062

1063
        return 0;
1064
}
1065

1066
void string_strv_hashmap_remove(Hashmap *h, const char *key, const char *value) {
115✔
1067
        assert(key);
115✔
1068

1069
        if (value) {
115✔
1070
                char **l = hashmap_get(h, key);
115✔
1071
                if (!l)
115✔
1072
                        return;
109✔
1073

1074
                strv_remove(l, value);
10✔
1075
                if (!strv_isempty(l))
115✔
1076
                        return;
1077
        }
1078

1079
        _unused_ _cleanup_free_ char *key_free = NULL;
6✔
1080
        strv_free(hashmap_remove2(h, key, (void**) &key_free));
6✔
1081
}
1082

1083
static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const char *value) {
218,796✔
1084
        char **l;
218,796✔
1085
        int r;
218,796✔
1086

1087
        assert(h);
218,796✔
1088
        assert(key);
218,796✔
1089
        assert(value);
218,796✔
1090

1091
        l = hashmap_get(h, key);
218,796✔
1092
        if (l) {
218,796✔
1093
                /* A list for this key already exists, let's append to it if it is not listed yet */
1094
                if (strv_contains(l, value))
7,936✔
1095
                        return 0;
218,796✔
1096

1097
                r = strv_extend(&l, value);
7,924✔
1098
                if (r < 0)
7,924✔
1099
                        return r;
1100

1101
                assert_se(hashmap_update(h, key, l) >= 0);
7,924✔
1102
        } else {
1103
                /* No list for this key exists yet, create one */
1104
                _cleanup_strv_free_ char **l2 = NULL;
×
1105
                _cleanup_free_ char *t = NULL;
210,860✔
1106

1107
                t = strdup(key);
210,860✔
1108
                if (!t)
210,860✔
1109
                        return -ENOMEM;
1110

1111
                r = strv_extend(&l2, value);
210,860✔
1112
                if (r < 0)
210,860✔
1113
                        return r;
1114

1115
                r = hashmap_put(h, t, l2);
210,860✔
1116
                if (r < 0)
210,860✔
1117
                        return r;
1118

1119
                TAKE_PTR(t);
210,860✔
1120
                TAKE_PTR(l2);
210,860✔
1121
        }
1122

1123
        return 1;
1124
}
1125

1126
int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value  HASHMAP_DEBUG_PARAMS) {
218,276✔
1127
        int r;
218,276✔
1128

1129
        assert(h);
218,276✔
1130
        assert(key);
218,276✔
1131
        assert(value);
218,276✔
1132

1133
        r = _hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free  HASHMAP_DEBUG_PASS_ARGS);
218,276✔
1134
        if (r < 0)
218,276✔
1135
                return r;
1136

1137
        return string_strv_hashmap_put_internal(*h, key, value);
218,276✔
1138
}
1139

1140
int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value  HASHMAP_DEBUG_PARAMS) {
520✔
1141
        int r;
520✔
1142

1143
        assert(h);
520✔
1144
        assert(key);
520✔
1145
        assert(value);
520✔
1146

1147
        r = _ordered_hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free  HASHMAP_DEBUG_PASS_ARGS);
520✔
1148
        if (r < 0)
520✔
1149
                return r;
1150

1151
        return string_strv_hashmap_put_internal(PLAIN_HASHMAP(*h), key, value);
520✔
1152
}
1153

1154
int strv_rebreak_lines(char **l, size_t width, char ***ret) {
11,727✔
1155
        _cleanup_strv_free_ char **broken = NULL;
11,727✔
1156
        int r;
11,727✔
1157

1158
        assert(ret);
11,727✔
1159

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

1166
        if (width == SIZE_MAX) { /* NOP? */
11,727✔
1167
                broken = strv_copy(l);
2,615✔
1168
                if (!broken)
2,615✔
1169
                        return -ENOMEM;
1170

1171
                *ret = TAKE_PTR(broken);
2,615✔
1172
                return 0;
2,615✔
1173
        }
1174

1175
        STRV_FOREACH(i, l) {
18,350✔
1176
                const char *start = *i, *whitespace_begin = NULL, *whitespace_end = NULL;
1177
                bool in_prefix = true; /* still in the whitespace in the beginning of the line? */
1178
                size_t w = 0;
1179

1180
                for (const char *p = start; *p != 0; p = utf8_next_char(p)) {
503,152✔
1181
                        if (strchr(NEWLINE, *p)) {
493,914✔
1182
                                in_prefix = true;
1183
                                whitespace_begin = whitespace_end = NULL;
1184
                                w = 0;
1185
                        } else if (strchr(WHITESPACE, *p)) {
493,914✔
1186
                                if (!in_prefix && (!whitespace_begin || whitespace_end)) {
72,716✔
1187
                                        whitespace_begin = p;
72,119✔
1188
                                        whitespace_end = NULL;
72,119✔
1189
                                }
1190
                        } else {
1191
                                if (whitespace_begin && !whitespace_end)
421,198✔
1192
                                        whitespace_end = p;
72,110✔
1193

1194
                                in_prefix = false;
1195
                        }
1196

1197
                        int cw = utf8_char_console_width(p);
493,914✔
1198
                        if (cw < 0) {
493,914✔
1199
                                log_debug_errno(cw, "Comment to line break contains invalid UTF-8, ignoring.");
×
1200
                                cw = 1;
1201
                        }
1202

1203
                        w += cw;
493,914✔
1204

1205
                        if (w > width && whitespace_begin && whitespace_end) {
493,914✔
1206
                                _cleanup_free_ char *truncated = NULL;
3,927✔
1207

1208
                                truncated = strndup(start, whitespace_begin - start);
3,927✔
1209
                                if (!truncated)
3,927✔
1210
                                        return -ENOMEM;
1211

1212
                                r = strv_consume(&broken, TAKE_PTR(truncated));
3,927✔
1213
                                if (r < 0)
3,927✔
1214
                                        return r;
1215

1216
                                p = start = whitespace_end;
1217
                                whitespace_begin = whitespace_end = NULL;
1218
                                w = cw;
1219
                        }
1220
                }
1221

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

1231
                        r = strv_consume(&broken, TAKE_PTR(truncated));
9✔
1232
                } else /* Otherwise use line as is */
1233
                        r = strv_extend(&broken, start);
9,228✔
1234
                if (r < 0)
9,238✔
1235
                        return r;
1236
        }
1237

1238
        *ret = TAKE_PTR(broken);
9,112✔
1239
        return 0;
9,112✔
1240
}
1241

1242
char** strv_filter_prefix(char *const*l, const char *prefix) {
4✔
1243
        _cleanup_strv_free_ char **f = NULL;
×
1244

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

1247
        if (isempty(prefix))
4✔
1248
                return strv_copy(l);
4✔
1249

1250
        size_t sz = 0;
3✔
1251

1252
        STRV_FOREACH(i, l) {
24✔
1253
                if (!startswith(*i, prefix))
21✔
1254
                        continue;
16✔
1255

1256
                if (strv_extend_with_size(&f, &sz, *i) < 0)
5✔
1257
                        return NULL;
1258
        }
1259

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