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

systemd / systemd / 12898275511

21 Jan 2025 10:06PM UTC coverage: 0.117%. Remained the same
12898275511

push

github

poettering
update TODO

478 of 408181 relevant lines covered (0.12%)

1.45 hits per line

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

0.0
/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) {
×
23
        assert(name);
×
24

25
        STRV_FOREACH(i, l)
×
26
                if (streq(*i, name))
×
27
                        return *i;
28

29
        return NULL;
30
}
31

32
char* strv_find_case(char * const *l, const char *name) {
×
33
        assert(name);
×
34

35
        STRV_FOREACH(i, l)
×
36
                if (strcaseeq(*i, name))
×
37
                        return *i;
38

39
        return NULL;
40
}
41

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

45
        STRV_FOREACH(i, l)
×
46
                if (startswith(*i, name))
×
47
                        return *i;
48

49
        return NULL;
50
}
51

52
char* strv_find_startswith(char * const *l, const char *name) {
×
53
        assert(name);
×
54

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

58
        STRV_FOREACH(i, l) {
×
59
                char *e;
×
60

61
                e = startswith(*i, name);
×
62
                if (e)
×
63
                        return e;
64
        }
65

66
        return NULL;
67
}
68

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

73
        assert(name);
×
74

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

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

87
        return best;
×
88
}
89

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

94
        assert(name);
×
95

96
        STRV_FOREACH(i, l) {
×
97
                ssize_t distance;
×
98

99
                distance = strlevenshtein(*i, name);
×
100
                if (distance < 0) {
×
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 */
×
106
                        continue;
×
107

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

114
        return best;
115
}
116

117
char* strv_find_closest(char * const *l, const char *name) {
×
118
        assert(name);
×
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);
×
125
        if (found)
×
126
                return found;
127

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

131
char* strv_find_first_field(char * const *needles, char * const *haystack) {
×
132
        STRV_FOREACH(k, needles) {
×
133
                char *value = strv_env_pairs_get((char **)haystack, *k);
×
134
                if (value)
×
135
                        return value;
136
        }
137

138
        return NULL;
139
}
140

141
char** strv_free(char **l) {
×
142
        STRV_FOREACH(k, l)
×
143
                free(*k);
×
144

145
        return mfree(l);
×
146
}
147

148
char** strv_free_erase(char **l) {
×
149
        STRV_FOREACH(i, l)
×
150
                erase_and_freep(i);
×
151

152
        return mfree(l);
×
153
}
154

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

158
        FOREACH_ARRAY (i, strvs, n)
×
159
                strv_free(*i);
×
160

161
        free(strvs);
×
162
}
×
163

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

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

172
        k = result;
173
        STRV_FOREACH(i, l) {
×
174
                if (m == 0)
×
175
                        break;
176

177
                *k = strdup(*i);
×
178
                if (!*k)
×
179
                        return NULL;
180
                k++;
×
181

182
                if (m != SIZE_MAX)
×
183
                        m--;
×
184
        }
185

186
        *k = NULL;
×
187
        return TAKE_PTR(result);
×
188
}
189

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

193
        if (strv_isempty(l)) {
×
194
                *ret = NULL;
×
195
                return 0;
×
196
        }
197

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

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

206
size_t strv_length(char * const *l) {
×
207
        size_t n = 0;
×
208

209
        STRV_FOREACH(i, l)
×
210
                n++;
×
211

212
        return n;
×
213
}
214

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

230
                n++;
×
231
        }
232
        va_end(aq);
×
233

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

238
        for (const char *s = x; s; s = va_arg(ap, const char*)) {
×
239
                if (s == STRV_IGNORE)
×
240
                        continue;
×
241

242
                a[i] = strdup(s);
×
243
                if (!a[i])
×
244
                        return NULL;
245

246
                i++;
×
247
        }
248

249
        a[i] = NULL;
×
250

251
        return TAKE_PTR(a);
×
252
}
253

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

258
        va_start(ap, x);
×
259
        r = strv_new_ap(x, ap);
×
260
        va_end(ap);
×
261

262
        return r;
×
263
}
264

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

268
        assert(a);
×
269

270
        q = strv_length(b);
×
271
        if (q == 0)
×
272
                return 0;
273

274
        p = strv_length(*a);
×
275
        if (p >= SIZE_MAX - q)
×
276
                return -ENOMEM;
277

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

282
        t[p] = NULL;
×
283
        *a = t;
×
284

285
        STRV_FOREACH(s, b) {
×
286
                if (filter_duplicates && strv_contains(t, *s))
×
287
                        continue;
×
288

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

293
                i++;
×
294
                t[p+i] = NULL;
×
295
        }
296

297
        assert(i <= q);
×
298

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

311
        assert(a);
×
312

313
        q = strv_length(b);
×
314
        if (q == 0)
×
315
                return 0;
316

317
        p = strv_length(*a);
×
318
        if (p == 0) {
×
319
                strv_free_and_replace(*a, b_consume);
×
320

321
                if (filter_duplicates)
×
322
                        strv_uniq(*a);
×
323

324
                return strv_length(*a);
×
325
        }
326

327
        if (p >= SIZE_MAX - q)
×
328
                return -ENOMEM;
329

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

334
        t[p] = NULL;
×
335
        *a = t;
×
336

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

343
                STRV_FOREACH(s, b) {
×
344
                        if (strv_contains(t, *s)) {
×
345
                                free(*s);
×
346
                                continue;
×
347
                        }
348

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

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

356
        assert(i <= q);
×
357

358
        b_consume = mfree(b_consume);
×
359

360
        return (int) i;
×
361
}
362

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

366
        assert(a);
×
367

368
        STRV_FOREACH(s, b) {
×
369
                char *v;
×
370

371
                v = strjoin(strempty(prefix), *s, suffix);
×
372
                if (!v)
×
373
                        return -ENOMEM;
374

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

380
        return 0;
381
}
382

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

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

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

403
        *ret = TAKE_PTR(l);
×
404
        return n;
×
405
}
406

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

412
        assert(t);
×
413
        assert(s);
×
414

415
        for (;;) {
×
416
                _cleanup_free_ char *word = NULL;
×
417

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

424
                if (!GREEDY_REALLOC(l, n + 2))
×
425
                        return -ENOMEM;
426

427
                l[n++] = TAKE_PTR(word);
×
428
                l[n] = NULL;
×
429
        }
430

431
        if (!l) {
×
432
                l = new0(char*, 1);
×
433
                if (!l)
×
434
                        return -ENOMEM;
435
        }
436

437
        *t = TAKE_PTR(l);
×
438

439
        return (int) n;
×
440
}
441

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

446
        assert(t);
×
447
        assert(s);
×
448

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

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

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

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

465
        assert(t);
×
466
        assert(s);
×
467

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

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

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

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

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

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

498
                l[n] = NULL;
×
499
        }
500

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

507
        *t = TAKE_PTR(l);
×
508

509
        return (int) n;
×
510
}
511

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

516
        if (!separator)
×
517
                separator = " ";
×
518

519
        k = strlen(separator);
×
520
        m = strlen_ptr(prefix);
×
521

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

525
        n = 0;
526
        STRV_FOREACH(s, l) {
×
527
                if (s != l)
×
528
                        n += k;
×
529

530
                bool needs_escaping = escape_separator && strchr(*s, *separator);
×
531

532
                n += m + strlen(*s) * (1 + needs_escaping);
×
533
        }
534

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

539
        e = r;
540
        STRV_FOREACH(s, l) {
×
541
                if (s != l)
×
542
                        e = stpcpy(e, separator);
×
543

544
                if (prefix)
×
545
                        e = stpcpy(e, prefix);
×
546

547
                bool needs_escaping = escape_separator && strchr(*s, *separator);
×
548

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

559
        *e = 0;
×
560

561
        return r;
×
562
}
563

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

573
        size_t size = n ? *n : SIZE_MAX;
×
574
        if (size == SIZE_MAX)
×
575
                size = strv_length(*l);
×
576

577
        /* Check for overflow */
578
        if (size > SIZE_MAX-2)
×
579
                return -ENOMEM;
580

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

585
        c[size] = value;
×
586
        c[size+1] = NULL;
×
587

588
        *l = c;
×
589
        if (n)
×
590
                *n = size + 1;
×
591
        return 0;
592
}
593

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

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

601
        n = strv_length(*l);
×
602

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

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

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

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

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

626
        assert(l);
×
627

628
        if (!value)
×
629
                return 0;
630

631
        n = strv_length(*l);
×
632
        position = MIN(position, n);
×
633

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

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

643
        if (n > position)
×
644
                memmove(c + position + 1, c + position, (n - position) * sizeof(char*));
×
645

646
        c[position] = value;
×
647
        c[n + 1] = NULL;
×
648

649
        *l = c;
×
650
        return 0;
×
651
}
652

653
int strv_consume_with_size(char ***l, size_t *n, char *value) {
×
654
        int r;
×
655

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

660
        return r;
×
661
}
662

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

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

672
        return r;
×
673
}
674

675
int strv_consume_prepend(char ***l, char *value) {
×
676
        int r;
×
677

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

682
        return r;
×
683
}
684

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

688
        if (!value)
×
689
                return 0;
690

691
        v = strdup(value);
×
692
        if (!v)
×
693
                return -ENOMEM;
694

695
        return strv_consume_prepend(l, v);
×
696
}
697

698
int strv_extend_with_size(char ***l, size_t *n, const char *value) {
×
699
        char *v;
×
700

701
        if (!value)
×
702
                return 0;
703

704
        v = strdup(value);
×
705
        if (!v)
×
706
                return -ENOMEM;
707

708
        return strv_consume_with_size(l, n, v);
×
709
}
710

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

716
        assert(l);
×
717

718
        m = n = strv_length(*l);
×
719

720
        r = 0;
×
721
        va_start(ap, value);
×
722
        for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
×
723
                if (!s)
×
724
                        continue;
×
725

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

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

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

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

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

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

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

775
        STRV_FOREACH(i, l)
×
776
                strv_remove(i+1, *i);
×
777

778
        return l;
×
779
}
780

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

786
        return true;
787
}
788

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

792
        if (!l)
×
793
                return NULL;
794

795
        assert(s);
×
796

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

800
        for (f = t = l; *f; f++)
×
801
                if (streq(*f, s))
×
802
                        free(*f);
×
803
                else
804
                        *(t++) = *f;
×
805

806
        *t = NULL;
×
807
        return l;
×
808
}
809

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

815
        return false;
816
}
817

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

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

827
char** strv_sort_uniq(char **l) {
×
828
        if (strv_isempty(l))
×
829
                return l;
830

831
        char **tail = strv_sort(l), *prev = NULL;
×
832
        STRV_FOREACH(i, l)
×
833
                if (streq_ptr(*i, prev))
×
834
                        free(*i);
×
835
                else
836
                        *(tail++) = prev = *i;
×
837

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

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

845
        if (strv_isempty(a)) {
×
846
                if (strv_isempty(b))
×
847
                        return 0;
×
848
                else
849
                        return -1;
850
        }
851

852
        if (strv_isempty(b))
×
853
                return 1;
854

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

861
        return 0;
862
}
863

864
bool strv_equal_ignore_order(char **a, char **b) {
×
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 repititions) */
869

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

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

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

881
        return true;
882
}
883

884
void strv_print_full(char * const *l, const char *prefix) {
×
885
        STRV_FOREACH(s, l)
×
886
                printf("%s%s\n", strempty(prefix), *s);
×
887
}
×
888

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

894
        va_start(ap, format);
×
895
        r = vasprintf(&x, format, ap);
×
896
        va_end(ap);
×
897

898
        if (r < 0)
×
899
                return -ENOMEM;
×
900

901
        return strv_consume(l, x);
×
902
}
903

904
char* startswith_strv(const char *s, char * const *l) {
×
905
        STRV_FOREACH(i, l) {
×
906
                char *found = startswith(s, *i);
×
907
                if (found)
×
908
                        return found;
909
        }
910

911
        return NULL;
912
}
913

914
char* endswith_strv(const char *s, char * const *l) {
×
915
        STRV_FOREACH(i, l) {
×
916
                char *found = endswith(s, *i);
×
917
                if (found)
×
918
                        return found;
919
        }
920

921
        return NULL;
922
}
923

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

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

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

934
        return l;
935
}
936

937
char** strv_shell_escape(char **l, const char *bad) {
×
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) {
×
942
                char *v;
×
943

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

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

951
        return l;
952
}
953

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

960
        assert(s);
×
961

962
        if (patterns)
×
963
                for (size_t i = 0; patterns[i]; i++)
×
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) {
×
967
                                if (ret_matched_pos)
×
968
                                        *ret_matched_pos = i;
×
969
                                return true;
×
970
                        }
971

972
        if (ret_matched_pos)
×
973
                *ret_matched_pos = SIZE_MAX;
×
974

975
        return false;
976
}
977

978
char** strv_skip(char **l, size_t n) {
×
979

980
        while (n > 0) {
×
981
                if (strv_isempty(l))
×
982
                        return l;
983

984
                l++, n--;
×
985
        }
986

987
        return l;
988
}
989

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

994
        assert(l);
×
995

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

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

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

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

1011
        *l = nl;
×
1012

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

1020
        return 0;
×
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) {
×
1031
        char *j;
×
1032

1033
        assert(l);
×
1034
        assert(lhs);
×
1035

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

1039
        j = strjoin(lhs, "=", rhs);
×
1040
        if (!j)
×
1041
                return -ENOMEM;
1042

1043
        return strv_consume(l, j);
×
1044
}
1045

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

1050
        assert(f);
×
1051

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

1054
        if (!space)
×
1055
                space = &b;
×
1056

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

1063
        return 0;
1064
}
1065

1066
DEFINE_PRIVATE_HASH_OPS_FULL(string_strv_hash_ops, char, string_hash_func, string_compare_func, free, char*, strv_free);
×
1067

1068
static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const char *value) {
×
1069
        char **l;
×
1070
        int r;
×
1071

1072
        assert(h);
×
1073
        assert(key);
×
1074
        assert(value);
×
1075

1076
        l = hashmap_get(h, key);
×
1077
        if (l) {
×
1078
                /* A list for this key already exists, let's append to it if it is not listed yet */
1079
                if (strv_contains(l, value))
×
1080
                        return 0;
×
1081

1082
                r = strv_extend(&l, value);
×
1083
                if (r < 0)
×
1084
                        return r;
1085

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

1092
                t = strdup(key);
×
1093
                if (!t)
×
1094
                        return -ENOMEM;
1095

1096
                r = strv_extend(&l2, value);
×
1097
                if (r < 0)
×
1098
                        return r;
1099

1100
                r = hashmap_put(h, t, l2);
×
1101
                if (r < 0)
×
1102
                        return r;
1103

1104
                TAKE_PTR(t);
×
1105
                TAKE_PTR(l2);
×
1106
        }
1107

1108
        return 1;
1109
}
1110

1111
int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value  HASHMAP_DEBUG_PARAMS) {
×
1112
        int r;
×
1113

1114
        assert(h);
×
1115
        assert(key);
×
1116
        assert(value);
×
1117

1118
        r = _hashmap_ensure_allocated(h, &string_strv_hash_ops  HASHMAP_DEBUG_PASS_ARGS);
×
1119
        if (r < 0)
×
1120
                return r;
1121

1122
        return string_strv_hashmap_put_internal(*h, key, value);
×
1123
}
1124

1125
int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value  HASHMAP_DEBUG_PARAMS) {
×
1126
        int r;
×
1127

1128
        assert(h);
×
1129
        assert(key);
×
1130
        assert(value);
×
1131

1132
        r = _ordered_hashmap_ensure_allocated(h, &string_strv_hash_ops  HASHMAP_DEBUG_PASS_ARGS);
×
1133
        if (r < 0)
×
1134
                return r;
1135

1136
        return string_strv_hashmap_put_internal(PLAIN_HASHMAP(*h), key, value);
×
1137
}
1138

1139
int strv_rebreak_lines(char **l, size_t width, char ***ret) {
×
1140
        _cleanup_strv_free_ char **broken = NULL;
×
1141
        int r;
×
1142

1143
        assert(ret);
×
1144

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

1151
        if (width == SIZE_MAX) { /* NOP? */
×
1152
                broken = strv_copy(l);
×
1153
                if (!broken)
×
1154
                        return -ENOMEM;
1155

1156
                *ret = TAKE_PTR(broken);
×
1157
                return 0;
×
1158
        }
1159

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

1165
                for (const char *p = start; *p != 0; p = utf8_next_char(p)) {
×
1166
                        if (strchr(NEWLINE, *p)) {
×
1167
                                in_prefix = true;
1168
                                whitespace_begin = whitespace_end = NULL;
1169
                                w = 0;
1170
                        } else if (strchr(WHITESPACE, *p)) {
×
1171
                                if (!in_prefix && (!whitespace_begin || whitespace_end)) {
×
1172
                                        whitespace_begin = p;
×
1173
                                        whitespace_end = NULL;
×
1174
                                }
1175
                        } else {
1176
                                if (whitespace_begin && !whitespace_end)
×
1177
                                        whitespace_end = p;
×
1178

1179
                                in_prefix = false;
1180
                        }
1181

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

1188
                        w += cw;
×
1189

1190
                        if (w > width && whitespace_begin && whitespace_end) {
×
1191
                                _cleanup_free_ char *truncated = NULL;
×
1192

1193
                                truncated = strndup(start, whitespace_begin - start);
×
1194
                                if (!truncated)
×
1195
                                        return -ENOMEM;
1196

1197
                                r = strv_consume(&broken, TAKE_PTR(truncated));
×
1198
                                if (r < 0)
×
1199
                                        return r;
1200

1201
                                p = start = whitespace_end;
1202
                                whitespace_begin = whitespace_end = NULL;
1203
                                w = cw;
1204
                        }
1205
                }
1206

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

1216
                        r = strv_consume(&broken, TAKE_PTR(truncated));
×
1217
                } else /* Otherwise use line as is */
1218
                        r = strv_extend(&broken, start);
×
1219
                if (r < 0)
×
1220
                        return r;
1221
        }
1222

1223
        *ret = TAKE_PTR(broken);
×
1224
        return 0;
×
1225
}
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