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

systemd / systemd / 14895667988

07 May 2025 08:57PM UTC coverage: 72.225% (-0.007%) from 72.232%
14895667988

push

github

yuwata
network: log_link_message_debug_errno() automatically append %m if necessary

Follow-up for d28746ef5.
Fixes CID#1609753.

0 of 1 new or added line in 1 file covered. (0.0%)

20297 existing lines in 338 files now uncovered.

297407 of 411780 relevant lines covered (72.22%)

695716.85 hits per line

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

95.55
/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,409,616✔
24
        assert(name);
17,409,616✔
25

26
        STRV_FOREACH(i, l)
184,171,880✔
27
                if (streq(*i, name))
168,064,502✔
28
                        return *i;
29

30
        return NULL;
31
}
32

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

36
        STRV_FOREACH(i, l)
5,118,367✔
37
                if (strcaseeq(*i, name))
4,483,861✔
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) {
981,820✔
54
        assert(name);
981,820✔
55

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

59
        STRV_FOREACH(i, l) {
1,984,259✔
60
                char *e;
1,003,873✔
61

62
                e = startswith(*i, name);
1,003,873✔
63
                if (e)
1,003,873✔
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,740,316✔
143
        STRV_FOREACH(k, l)
21,927,507✔
144
                free(*k);
16,187,191✔
145

146
        return mfree(l);
5,740,316✔
147
}
148

149
char** strv_free_erase(char **l) {
38,653✔
150
        STRV_FOREACH(i, l)
45,367✔
151
                erase_and_freep(i);
6,714✔
152

153
        return mfree(l);
38,653✔
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,352✔
166
        _cleanup_strv_free_ char **result = NULL;
146,352✔
167
        char **k;
146,352✔
168

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

173
        k = result;
174
        STRV_FOREACH(i, l) {
404,592✔
175
                if (m == 0)
258,246✔
176
                        break;
177

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

183
                if (m != SIZE_MAX)
258,240✔
184
                        m--;
83✔
185
        }
186

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

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

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

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

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

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

210
        STRV_FOREACH(i, l)
289,901,491✔
211
                n++;
280,422,706✔
212

213
        return n;
9,478,785✔
214
}
215

216
char** strv_new_ap(const char *x, va_list ap) {
31,760✔
217
        _cleanup_strv_free_ char **a = NULL;
31,760✔
218
        size_t n = 0, i = 0;
31,760✔
219
        va_list aq;
31,760✔
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);
31,760✔
227
        for (const char *s = x; s; s = va_arg(aq, const char*)) {
181,669✔
228
                if (s == STRV_IGNORE)
149,909✔
229
                        continue;
5,000✔
230

231
                n++;
144,909✔
232
        }
233
        va_end(aq);
31,760✔
234

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

239
        for (const char *s = x; s; s = va_arg(ap, const char*)) {
181,669✔
240
                if (s == STRV_IGNORE)
149,909✔
241
                        continue;
5,000✔
242

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

247
                i++;
144,909✔
248
        }
249

250
        a[i] = NULL;
31,760✔
251

252
        return TAKE_PTR(a);
31,760✔
253
}
254

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

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

263
        return r;
24,783✔
264
}
265

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

269
        assert(a);
36,537✔
270

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

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

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

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

286
        STRV_FOREACH(s, b) {
73,983✔
287
                if (filter_duplicates && strv_contains(t, *s))
39,987✔
288
                        continue;
53✔
289

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

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

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

300
        return (int) i;
33,996✔
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) {
116,637✔
309
        _cleanup_strv_free_ char **b_consume = b;
116,637✔
310
        size_t p, q, i;
116,637✔
311

312
        assert(a);
116,637✔
313

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

318
        p = strv_length(*a);
62,991✔
319
        if (p == 0) {
62,991✔
320
                strv_free_and_replace(*a, b_consume);
52,843✔
321

322
                if (filter_duplicates)
52,843✔
323
                        strv_uniq(*a);
16,511✔
324

325
                return strv_length(*a);
52,843✔
326
        }
327

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

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

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

338
        if (!filter_duplicates) {
10,148✔
339
                *mempcpy_typesafe(t + p, b, q) = NULL;
9,621✔
340
                i = q;
9,621✔
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,148✔
358

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

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

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

367
        assert(a);
82,154✔
368

369
        STRV_FOREACH(s, b) {
409,490✔
370
                char *v;
327,336✔
371

372
                v = strjoin(strempty(prefix), *s, suffix);
654,582✔
373
                if (!v)
327,336✔
374
                        return -ENOMEM;
375

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

381
        return 0;
382
}
383

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

389
        assert(s);
613,674✔
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);
613,674✔
395
        if (r < 0)
613,674✔
396
                return r;
397

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

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

408
char** strv_split_newlines(const char *s) {
50,679✔
409
        char **ret;
50,679✔
410

411
        if (strv_split_newlines_full(&ret, s, 0) < 0)
50,679✔
412
                return NULL;
50,679✔
413

414
        return ret;
50,679✔
415
}
416

417
int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags) {
866,020✔
418
        _cleanup_strv_free_ char **l = NULL;
866,020✔
419
        size_t n = 0;
866,020✔
420
        int r;
866,020✔
421

422
        assert(t);
866,020✔
423
        assert(s);
866,020✔
424

425
        for (;;) {
18,330,170✔
426
                _cleanup_free_ char *word = NULL;
8,732,084✔
427

428
                r = extract_first_word(&s, &word, separators, flags);
9,598,095✔
429
                if (r < 0)
9,598,095✔
430
                        return r;
431
                if (r == 0)
9,598,086✔
432
                        break;
433

434
                if (!GREEDY_REALLOC(l, n + 2))
8,732,075✔
435
                        return -ENOMEM;
436

437
                l[n++] = TAKE_PTR(word);
8,732,075✔
438
                l[n] = NULL;
8,732,075✔
439
        }
440

441
        if (!l) {
866,011✔
442
                l = new0(char*, 1);
73,439✔
443
                if (!l)
73,439✔
444
                        return -ENOMEM;
445
        }
446

447
        *t = TAKE_PTR(l);
866,011✔
448

449
        return (int) n;
866,011✔
450
}
451

452
char** strv_split(const char *s, const char *separators) {
6,946✔
453
        char **ret;
6,946✔
454

455
        if (strv_split_full(&ret, s, separators, EXTRACT_RETAIN_ESCAPE) < 0)
6,946✔
456
                return NULL;
6,946✔
457

458
        return ret;
6,946✔
459
}
460

461
int strv_split_and_extend_full(char ***t, const char *s, const char *separators, bool filter_duplicates, ExtractFlags flags) {
758✔
462
        char **l;
758✔
463
        int r;
758✔
464

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

468
        r = strv_split_full(&l, s, separators, flags);
758✔
469
        if (r < 0)
758✔
470
                return r;
758✔
471

472
        r = strv_extend_strv_consume(t, l, filter_duplicates);
758✔
473
        if (r < 0)
758✔
474
                return r;
475

476
        return (int) strv_length(*t);
758✔
477
}
478

479
int strv_split_and_extend(char ***t, const char *s, const char *separators, bool filter_duplicates) {
756✔
480
        return strv_split_and_extend_full(t, s, separators, filter_duplicates, 0);
756✔
481
}
482

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

488
        assert(t);
4✔
489
        assert(s);
4✔
490

491
        for (;;) {
16✔
492
                _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL, *second_or_empty = NULL;
16✔
493

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

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

511
                second_or_empty = strdup(strempty(second));
12✔
512
                if (!second_or_empty)
12✔
513
                        return -ENOMEM;
514

515
                if (!GREEDY_REALLOC(l, n + 3))
12✔
516
                        return -ENOMEM;
517

518
                l[n++] = TAKE_PTR(first);
12✔
519
                l[n++] = TAKE_PTR(second_or_empty);
12✔
520

521
                l[n] = NULL;
12✔
522
        }
523

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

530
        *t = TAKE_PTR(l);
3✔
531

532
        return (int) n;
3✔
533
}
534

535
char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separator) {
61,806✔
536
        char *r, *e;
61,806✔
537
        size_t n, k, m;
61,806✔
538

539
        if (!separator)
61,806✔
540
                separator = " ";
2✔
541

542
        k = strlen(separator);
61,806✔
543
        m = strlen_ptr(prefix);
61,806✔
544

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

548
        n = 0;
549
        STRV_FOREACH(s, l) {
255,209✔
550
                if (s != l)
193,403✔
551
                        n += k;
132,392✔
552

553
                bool needs_escaping = escape_separator && strchr(*s, *separator);
193,403✔
554

555
                n += m + strlen(*s) * (1 + needs_escaping);
193,403✔
556
        }
557

558
        r = new(char, n+1);
61,806✔
559
        if (!r)
61,806✔
560
                return NULL;
561

562
        e = r;
563
        STRV_FOREACH(s, l) {
255,209✔
564
                if (s != l)
193,403✔
565
                        e = stpcpy(e, separator);
132,392✔
566

567
                if (prefix)
193,403✔
568
                        e = stpcpy(e, prefix);
17✔
569

570
                bool needs_escaping = escape_separator && strchr(*s, *separator);
193,403✔
571

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

582
        *e = 0;
61,806✔
583

584
        return r;
61,806✔
585
}
586

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

593
        if (!value)
6,852,348✔
594
                return 0;
595

596
        size_t size = n ? *n : SIZE_MAX;
6,852,346✔
597
        if (size == SIZE_MAX)
459✔
598
                size = strv_length(*l);
6,851,900✔
599

600
        /* Check for overflow */
601
        if (size > SIZE_MAX-2)
6,852,346✔
602
                return -ENOMEM;
603

604
        char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(size + 2), sizeof(char*));
6,852,346✔
605
        if (!c)
6,852,346✔
606
                return -ENOMEM;
607

608
        c[size] = value;
6,852,346✔
609
        c[size+1] = NULL;
6,852,346✔
610

611
        *l = c;
6,852,346✔
612
        if (n)
6,852,346✔
613
                *n = size + 1;
459✔
614
        return 0;
615
}
616

617
int strv_push_pair(char ***l, char *a, char *b) {
457✔
618
        char **c;
457✔
619
        size_t n;
457✔
620

621
        if (!a && !b)
457✔
622
                return 0;
623

624
        n = strv_length(*l);
457✔
625

626
        /* Check for overflow */
627
        if (n > SIZE_MAX-3)
457✔
628
                return -ENOMEM;
629

630
        /* increase and check for overflow */
631
        c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(n + !!a + !!b + 1), sizeof(char*));
457✔
632
        if (!c)
457✔
633
                return -ENOMEM;
634

635
        if (a)
457✔
636
                c[n++] = a;
457✔
637
        if (b)
457✔
638
                c[n++] = b;
457✔
639
        c[n] = NULL;
457✔
640

641
        *l = c;
457✔
642
        return 0;
457✔
643
}
644

645
int strv_insert(char ***l, size_t position, char *value) {
4,378✔
646
        char **c;
4,378✔
647
        size_t n, m;
4,378✔
648

649
        assert(l);
4,378✔
650

651
        if (!value)
4,378✔
652
                return 0;
653

654
        n = strv_length(*l);
4,377✔
655
        position = MIN(position, n);
4,377✔
656

657
        /* check for overflow and increase */
658
        if (n > SIZE_MAX - 2)
4,377✔
659
                return -ENOMEM;
660
        m = n + 2;
4,377✔
661

662
        c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m), sizeof(char*));
4,377✔
663
        if (!c)
4,377✔
664
                return -ENOMEM;
665

666
        if (n > position)
4,377✔
667
                memmove(c + position + 1, c + position, (n - position) * sizeof(char*));
1,143✔
668

669
        c[position] = value;
4,377✔
670
        c[n + 1] = NULL;
4,377✔
671

672
        *l = c;
4,377✔
673
        return 0;
4,377✔
674
}
675

676
int strv_consume_with_size(char ***l, size_t *n, char *value) {
6,799,342✔
677
        int r;
6,799,342✔
678

679
        r = strv_push_with_size(l, n, value);
6,799,342✔
680
        if (r < 0)
6,799,342✔
681
                free(value);
×
682

683
        return r;
6,799,342✔
684
}
685

686
int strv_consume_pair(char ***l, char *a, char *b) {
418✔
687
        int r;
418✔
688

689
        r = strv_push_pair(l, a, b);
418✔
690
        if (r < 0) {
418✔
UNCOV
691
                free(a);
×
UNCOV
692
                free(b);
×
693
        }
694

695
        return r;
418✔
696
}
697

698
int strv_consume_prepend(char ***l, char *value) {
4,360✔
699
        int r;
4,360✔
700

701
        r = strv_push_prepend(l, value);
4,360✔
702
        if (r < 0)
4,360✔
UNCOV
703
                free(value);
×
704

705
        return r;
4,360✔
706
}
707

708
int strv_prepend(char ***l, const char *value) {
3,583✔
709
        char *v;
3,583✔
710

711
        if (!value)
3,583✔
712
                return 0;
713

714
        v = strdup(value);
3,478✔
715
        if (!v)
3,478✔
716
                return -ENOMEM;
717

718
        return strv_consume_prepend(l, v);
3,478✔
719
}
720

721
int strv_extend_with_size(char ***l, size_t *n, const char *value) {
1,137,735✔
722
        char *v;
1,137,735✔
723

724
        if (!value)
1,137,735✔
725
                return 0;
726

727
        v = strdup(value);
1,134,128✔
728
        if (!v)
1,134,128✔
729
                return -ENOMEM;
730

731
        return strv_consume_with_size(l, n, v);
1,134,128✔
732
}
733

734
int strv_extend_many_internal(char ***l, const char *value, ...) {
332✔
735
        va_list ap;
332✔
736
        size_t n, m;
332✔
737
        int r;
332✔
738

739
        assert(l);
332✔
740

741
        m = n = strv_length(*l);
332✔
742

743
        r = 0;
332✔
744
        va_start(ap, value);
332✔
745
        for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
1,207✔
746
                if (!s)
875✔
747
                        continue;
27✔
748

749
                if (m > SIZE_MAX-1) { /* overflow */
848✔
750
                        r = -ENOMEM;
751
                        break;
752
                }
753
                m++;
848✔
754
        }
755
        va_end(ap);
332✔
756

757
        if (r < 0)
332✔
758
                return r;
332✔
759
        if (m > SIZE_MAX-1)
332✔
760
                return -ENOMEM;
761

762
        char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m+1), sizeof(char*));
332✔
763
        if (!c)
332✔
764
                return -ENOMEM;
765
        *l = c;
332✔
766

767
        r = 0;
332✔
768
        size_t i = n;
332✔
769
        va_start(ap, value);
332✔
770
        for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
1,207✔
771
                if (!s)
875✔
772
                        continue;
27✔
773

774
                c[i] = strdup(s);
848✔
775
                if (!c[i]) {
848✔
776
                        r = -ENOMEM;
777
                        break;
778
                }
779
                i++;
848✔
780
        }
781
        va_end(ap);
332✔
782

783
        if (r < 0) {
332✔
784
                /* rollback on error */
UNCOV
785
                for (size_t j = n; j < i; j++)
×
UNCOV
786
                        c[j] = mfree(c[j]);
×
787
                return r;
788
        }
789

790
        c[i] = NULL;
332✔
791
        return 0;
332✔
792
}
793

794
char** strv_uniq(char **l) {
43,594✔
795
        /* Drops duplicate entries. The first identical string will be
796
         * kept, the others dropped */
797

798
        STRV_FOREACH(i, l)
241,430✔
799
                strv_remove(i+1, *i);
197,836✔
800

801
        return l;
43,594✔
802
}
803

804
bool strv_is_uniq(char * const *l) {
4✔
805
        STRV_FOREACH(i, l)
8✔
806
                if (strv_contains(i+1, *i))
5✔
807
                        return false;
808

809
        return true;
810
}
811

812
char** strv_remove(char **l, const char *s) {
197,902✔
813
        char **f, **t;
197,902✔
814

815
        if (!l)
197,902✔
816
                return NULL;
817

818
        assert(s);
197,902✔
819

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

823
        for (f = t = l; *f; f++)
1,130,414✔
824
                if (streq(*f, s))
932,512✔
825
                        free(*f);
30,318✔
826
                else
827
                        *(t++) = *f;
902,194✔
828

829
        *t = NULL;
197,902✔
830
        return l;
197,902✔
831
}
832

833
bool strv_overlap(char * const *a, char * const *b) {
401✔
834
        STRV_FOREACH(i, a)
731✔
835
                if (strv_contains(b, *i))
404✔
836
                        return true;
837

838
        return false;
839
}
840

841
static int str_compare(char * const *a, char * const *b) {
466,920✔
842
        return strcmp(*a, *b);
466,920✔
843
}
844

845
char** strv_sort(char **l) {
13,073✔
846
        typesafe_qsort(l, strv_length(l), str_compare);
13,073✔
847
        return l;
13,073✔
848
}
849

850
char** strv_sort_uniq(char **l) {
35,846✔
851
        if (strv_isempty(l))
35,846✔
852
                return l;
853

854
        char **tail = strv_sort(l), *prev = NULL;
198✔
855
        STRV_FOREACH(i, l)
3,451✔
856
                if (streq_ptr(*i, prev))
3,253✔
857
                        free(*i);
27✔
858
                else
859
                        *(tail++) = prev = *i;
3,226✔
860

861
        *tail = NULL;
198✔
862
        return l;
198✔
863
}
864

865
int strv_compare(char * const *a, char * const *b) {
3,346✔
866
        int r;
3,346✔
867

868
        if (strv_isempty(a)) {
3,346✔
869
                if (strv_isempty(b))
1,161✔
870
                        return 0;
1,098✔
871
                else
872
                        return -1;
873
        }
874

875
        if (strv_isempty(b))
2,185✔
876
                return 1;
877

878
        for ( ; *a || *b; ++a, ++b) {
5,308✔
879
                r = strcmp_ptr(*a, *b);
3,253✔
880
                if (r != 0)
3,253✔
881
                        return r;
882
        }
883

884
        return 0;
885
}
886

887
bool strv_equal_ignore_order(char * const *a, char * const *b) {
448✔
888

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

893
        if (a == b)
448✔
894
                return true;
895

896
        STRV_FOREACH(i, a)
851✔
897
                if (!strv_contains(b, *i))
440✔
898
                        return false;
899

900
        STRV_FOREACH(i, b)
847✔
901
                if (!strv_contains(a, *i))
440✔
902
                        return false;
903

904
        return true;
905
}
906

907
void strv_print_full(char * const *l, const char *prefix) {
60✔
908
        STRV_FOREACH(s, l)
2,514✔
909
                printf("%s%s\n", strempty(prefix), *s);
4,908✔
910
}
60✔
911

912
int strv_extendf(char ***l, const char *format, ...) {
962,913✔
913
        va_list ap;
962,913✔
914
        char *x;
962,913✔
915
        int r;
962,913✔
916

917
        va_start(ap, format);
962,913✔
918
        r = vasprintf(&x, format, ap);
962,913✔
919
        va_end(ap);
962,913✔
920

921
        if (r < 0)
962,913✔
922
                return -ENOMEM;
962,913✔
923

924
        return strv_consume(l, x);
962,913✔
925
}
926

927
char* startswith_strv(const char *s, char * const *l) {
11,693,531✔
928
        STRV_FOREACH(i, l) {
35,733,133✔
929
                char *found = startswith(s, *i);
24,111,558✔
930
                if (found)
24,111,558✔
931
                        return found;
932
        }
933

934
        return NULL;
935
}
936

937
char* endswith_strv(const char *s, char * const *l) {
3,104,616✔
938
        STRV_FOREACH(i, l) {
9,290,765✔
939
                char *found = endswith(s, *i);
6,212,581✔
940
                if (found)
6,212,581✔
941
                        return found;
942
        }
943

944
        return NULL;
945
}
946

947
char** strv_reverse(char **l) {
844✔
948
        size_t n;
844✔
949

950
        n = strv_length(l);
844✔
951
        if (n <= 1)
844✔
952
                return l;
953

954
        for (size_t i = 0; i < n / 2; i++)
294✔
955
                SWAP_TWO(l[i], l[n-1-i]);
151✔
956

957
        return l;
958
}
959

960
char** strv_shell_escape(char **l, const char *bad) {
3✔
961
        /* Escapes every character in every string in l that is in bad,
962
         * edits in-place, does not roll-back on error. */
963

964
        STRV_FOREACH(s, l) {
8✔
965
                char *v;
5✔
966

967
                v = shell_escape(*s, bad);
5✔
968
                if (!v)
5✔
969
                        return NULL;
3✔
970

971
                free_and_replace(*s, v);
5✔
972
        }
973

974
        return l;
975
}
976

977
bool strv_fnmatch_full(
71,900✔
978
                char* const* patterns,
979
                const char *s,
980
                int flags,
981
                size_t *ret_matched_pos) {
982

983
        assert(s);
71,900✔
984

985
        if (patterns)
71,900✔
986
                for (size_t i = 0; patterns[i]; i++)
127,690✔
987
                        /* NB: We treat all fnmatch() errors as equivalent to FNM_NOMATCH, i.e. if fnmatch() fails to
988
                         * process the pattern for some reason we'll consider this equivalent to non-matching. */
989
                        if (fnmatch(patterns[i], s, flags) == 0) {
75,047✔
990
                                if (ret_matched_pos)
16,255✔
991
                                        *ret_matched_pos = i;
1,376✔
992
                                return true;
16,255✔
993
                        }
994

995
        if (ret_matched_pos)
55,645✔
996
                *ret_matched_pos = SIZE_MAX;
19,253✔
997

998
        return false;
999
}
1000

1001
char** strv_skip(char **l, size_t n) {
28,366✔
1002
        while (n > 0) {
77,278✔
1003
                if (strv_isempty(l))
49,928✔
1004
                        return NULL;
1005

1006
                l++, n--;
48,912✔
1007
        }
1008

1009
        /* To simplify callers, always return NULL instead of a zero-item array. */
1010
        if (strv_isempty(l))
27,350✔
1011
                return NULL;
11,331✔
1012
        return l;
1013
}
1014

1015
int strv_extend_n(char ***l, const char *value, size_t n) {
510✔
1016
        size_t i, k;
510✔
1017
        char **nl;
510✔
1018

1019
        assert(l);
510✔
1020

1021
        if (!value)
510✔
1022
                return 0;
1023
        if (n == 0)
510✔
1024
                return 0;
1025

1026
        /* Adds the value n times to l */
1027

1028
        k = strv_length(*l);
509✔
1029
        if (n >= SIZE_MAX - k)
509✔
1030
                return -ENOMEM;
1031

1032
        nl = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(k + n + 1), sizeof(char *));
509✔
1033
        if (!nl)
509✔
1034
                return -ENOMEM;
1035

1036
        *l = nl;
509✔
1037

1038
        for (i = k; i < k + n; i++) {
1,055✔
1039
                nl[i] = strdup(value);
546✔
1040
                if (!nl[i])
546✔
UNCOV
1041
                        goto rollback;
×
1042
        }
1043
        nl[i] = NULL;
509✔
1044

1045
        return 0;
509✔
1046

UNCOV
1047
rollback:
×
UNCOV
1048
        for (size_t j = k; j < i; j++)
×
UNCOV
1049
                free(nl[j]);
×
UNCOV
1050
        nl[k] = NULL;
×
1051

UNCOV
1052
        return -ENOMEM;
×
1053
}
1054

1055
int strv_extend_assignment(char ***l, const char *lhs, const char *rhs) {
5,199,112✔
1056
        char *j;
5,199,112✔
1057

1058
        assert(l);
5,199,112✔
1059
        assert(lhs);
5,199,112✔
1060

1061
        if (!rhs) /* value is optional, in which case we suppress the field */
5,199,112✔
1062
                return 0;
1063

1064
        j = strjoin(lhs, "=", rhs);
3,769,873✔
1065
        if (!j)
3,769,873✔
1066
                return -ENOMEM;
1067

1068
        return strv_consume(l, j);
3,769,873✔
1069
}
1070

1071
int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) {
14,178✔
1072
        bool b = false;
14,178✔
1073
        int r;
14,178✔
1074

1075
        assert(f);
14,178✔
1076

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

1079
        if (!space)
14,178✔
1080
                space = &b;
15✔
1081

1082
        STRV_FOREACH(s, l) {
14,608✔
1083
                r = fputs_with_separator(f, *s, separator, space);
430✔
1084
                if (r < 0)
430✔
1085
                        return r;
14,178✔
1086
        }
1087

1088
        return 0;
1089
}
1090

1091
void string_strv_hashmap_remove(Hashmap *h, const char *key, const char *value) {
132✔
1092
        assert(key);
132✔
1093

1094
        if (value) {
132✔
1095
                char **l = hashmap_get(h, key);
132✔
1096
                if (!l)
132✔
1097
                        return;
126✔
1098

1099
                strv_remove(l, value);
10✔
1100
                if (!strv_isempty(l))
132✔
1101
                        return;
1102
        }
1103

1104
        _unused_ _cleanup_free_ char *key_free = NULL;
6✔
1105
        strv_free(hashmap_remove2(h, key, (void**) &key_free));
6✔
1106
}
1107

1108
static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const char *value) {
266,562✔
1109
        char **l;
266,562✔
1110
        int r;
266,562✔
1111

1112
        assert(h);
266,562✔
1113
        assert(key);
266,562✔
1114
        assert(value);
266,562✔
1115

1116
        l = hashmap_get(h, key);
266,562✔
1117
        if (l) {
266,562✔
1118
                /* A list for this key already exists, let's append to it if it is not listed yet */
1119
                if (strv_contains(l, value))
8,826✔
1120
                        return 0;
266,562✔
1121

1122
                r = strv_extend(&l, value);
8,813✔
1123
                if (r < 0)
8,813✔
1124
                        return r;
1125

1126
                assert_se(hashmap_update(h, key, l) >= 0);
8,813✔
1127
        } else {
1128
                /* No list for this key exists yet, create one */
UNCOV
1129
                _cleanup_strv_free_ char **l2 = NULL;
×
1130
                _cleanup_free_ char *t = NULL;
257,736✔
1131

1132
                t = strdup(key);
257,736✔
1133
                if (!t)
257,736✔
1134
                        return -ENOMEM;
1135

1136
                r = strv_extend(&l2, value);
257,736✔
1137
                if (r < 0)
257,736✔
1138
                        return r;
1139

1140
                r = hashmap_put(h, t, l2);
257,736✔
1141
                if (r < 0)
257,736✔
1142
                        return r;
1143

1144
                TAKE_PTR(t);
257,736✔
1145
                TAKE_PTR(l2);
257,736✔
1146
        }
1147

1148
        return 1;
1149
}
1150

1151
int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value) {
266,536✔
1152
        int r;
266,536✔
1153

1154
        assert(h);
266,536✔
1155
        assert(key);
266,536✔
1156
        assert(value);
266,536✔
1157

1158
        r = hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free);
266,536✔
1159
        if (r < 0)
266,536✔
1160
                return r;
1161

1162
        return string_strv_hashmap_put_internal(*h, key, value);
266,536✔
1163
}
1164

1165
int string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value) {
26✔
1166
        int r;
26✔
1167

1168
        assert(h);
26✔
1169
        assert(key);
26✔
1170
        assert(value);
26✔
1171

1172
        r = ordered_hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free);
26✔
1173
        if (r < 0)
26✔
1174
                return r;
1175

1176
        return string_strv_hashmap_put_internal(PLAIN_HASHMAP(*h), key, value);
26✔
1177
}
1178

1179
int strv_rebreak_lines(char **l, size_t width, char ***ret) {
12,889✔
1180
        _cleanup_strv_free_ char **broken = NULL;
12,889✔
1181
        int r;
12,889✔
1182

1183
        assert(ret);
12,889✔
1184

1185
        /* Implements a simple UTF-8 line breaking algorithm
1186
         *
1187
         * Goes through all entries in *l, and line-breaks each line that is longer than the specified
1188
         * character width. Breaks at the end of words/beginning of whitespace. Lines that do not contain whitespace are not
1189
         * broken. Retains whitespace at beginning of lines, removes it at end of lines. */
1190

1191
        if (width == SIZE_MAX) { /* NOP? */
12,889✔
1192
                broken = strv_copy(l);
2,886✔
1193
                if (!broken)
2,886✔
1194
                        return -ENOMEM;
1195

1196
                *ret = TAKE_PTR(broken);
2,886✔
1197
                return 0;
2,886✔
1198
        }
1199

1200
        STRV_FOREACH(i, l) {
20,132✔
1201
                const char *start = *i, *whitespace_begin = NULL, *whitespace_end = NULL;
1202
                bool in_prefix = true; /* still in the whitespace in the beginning of the line? */
1203
                size_t w = 0;
1204

1205
                for (const char *p = start; *p != 0; p = utf8_next_char(p)) {
549,223✔
1206
                        if (strchr(NEWLINE, *p)) {
539,094✔
1207
                                in_prefix = true;
1208
                                whitespace_begin = whitespace_end = NULL;
1209
                                w = 0;
1210
                        } else if (strchr(WHITESPACE, *p)) {
539,094✔
1211
                                if (!in_prefix && (!whitespace_begin || whitespace_end)) {
78,689✔
1212
                                        whitespace_begin = p;
78,092✔
1213
                                        whitespace_end = NULL;
78,092✔
1214
                                }
1215
                        } else {
1216
                                if (whitespace_begin && !whitespace_end)
460,405✔
1217
                                        whitespace_end = p;
78,083✔
1218

1219
                                in_prefix = false;
1220
                        }
1221

1222
                        int cw = utf8_char_console_width(p);
539,094✔
1223
                        if (cw < 0) {
539,094✔
UNCOV
1224
                                log_debug_errno(cw, "Comment to line break contains invalid UTF-8, ignoring.");
×
1225
                                cw = 1;
1226
                        }
1227

1228
                        w += cw;
539,094✔
1229

1230
                        if (w > width && whitespace_begin && whitespace_end) {
539,094✔
UNCOV
1231
                                _cleanup_free_ char *truncated = NULL;
×
1232

1233
                                truncated = strndup(start, whitespace_begin - start);
4,615✔
1234
                                if (!truncated)
4,615✔
1235
                                        return -ENOMEM;
1236

1237
                                r = strv_consume(&broken, TAKE_PTR(truncated));
4,615✔
1238
                                if (r < 0)
4,615✔
1239
                                        return r;
1240

1241
                                p = start = whitespace_end;
1242
                                whitespace_begin = whitespace_end = NULL;
1243
                                w = cw;
1244
                        }
1245
                }
1246

1247
                /* Process rest of the line */
1248
                assert(start);
10,129✔
1249
                if (in_prefix) /* Never seen anything non-whitespace? Generate empty line! */
10,129✔
1250
                        r = strv_extend(&broken, "");
1✔
1251
                else if (whitespace_begin && !whitespace_end) { /* Ends in whitespace? Chop it off! */
10,128✔
UNCOV
1252
                        _cleanup_free_ char *truncated = strndup(start, whitespace_begin - start);
×
1253
                        if (!truncated)
9✔
UNCOV
1254
                                return -ENOMEM;
×
1255

1256
                        r = strv_consume(&broken, TAKE_PTR(truncated));
9✔
1257
                } else /* Otherwise use line as is */
1258
                        r = strv_extend(&broken, start);
10,119✔
1259
                if (r < 0)
10,129✔
1260
                        return r;
1261
        }
1262

1263
        *ret = TAKE_PTR(broken);
10,003✔
1264
        return 0;
10,003✔
1265
}
1266

1267
char** strv_filter_prefix(char * const *l, const char *prefix) {
4✔
1268

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

1271
        if (isempty(prefix))
4✔
1272
                return strv_copy(l);
5✔
1273

1274
        _cleanup_strv_free_ char **f = NULL;
3✔
1275
        size_t sz = 0;
3✔
1276

1277
        STRV_FOREACH(i, l) {
24✔
1278
                if (!startswith(*i, prefix))
21✔
1279
                        continue;
16✔
1280

1281
                if (strv_extend_with_size(&f, &sz, *i) < 0)
5✔
1282
                        return NULL;
1283
        }
1284

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