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

systemd / systemd / 30055979440

23 Jul 2026 05:57PM UTC coverage: 73.111% (+0.04%) from 73.067%
30055979440

push

github

yuwata
user-record: validate JSON shell fields with valid_shell()

The user record loader currently uses a generic filename-or-path check
for shell and fallbackShell. This allows values that homectl rejects,
including relative names, control characters, colons, and trailing
slashes.

Use valid_shell() for all three record locations and cover the top-level,
matching per-machine, and status fallback fields at the loader boundary.

Fixes #43066

35 of 36 new or added lines in 2 files covered. (97.22%)

514 existing lines in 41 files now uncovered.

346901 of 474487 relevant lines covered (73.11%)

1323376.8 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 <fnmatch.h>
4
#include <stdio.h>
5

6
#include "alloc-util.h"
7
#include "env-util.h"
8
#include "escape.h"
9
#include "extract-word.h"
10
#include "fileio.h"
11
#include "hashmap.h"
12
#include "log.h"
13
#include "memory-util.h"
14
#include "sort-util.h"
15
#include "string-util.h"
16
#include "strv.h"
17
#include "utf8.h"
18

19
char* strv_find(char * const *l, const char *name) {
37,422,611✔
20
        assert(name);
37,422,611✔
21

22
        STRV_FOREACH(i, l)
342,780,188✔
23
                if (streq(*i, name))
307,607,922✔
24
                        return *i;
25

26
        return NULL;
27
}
28

29
char* strv_find_case(char * const *l, const char *name) {
2,396,797✔
30
        assert(name);
2,396,797✔
31

32
        STRV_FOREACH(i, l)
10,233,003✔
33
                if (strcaseeq(*i, name))
9,049,498✔
34
                        return *i;
35

36
        return NULL;
37
}
38

39
char* strv_find_prefix(char * const *l, const char *name) {
604✔
40
        assert(name);
604✔
41

42
        STRV_FOREACH(i, l)
9,030✔
43
                if (startswith(*i, name))
8,623✔
44
                        return *i;
45

46
        return NULL;
47
}
48

49
char* strv_find_startswith(char * const *l, const char *name) {
1,938,227✔
50
        assert(name);
1,938,227✔
51

52
        /* Like strv_find_prefix, but actually returns only the
53
         * suffix, not the whole item */
54

55
        STRV_FOREACH(i, l) {
3,928,603✔
56
                char *e;
1,994,516✔
57

58
                e = startswith(*i, name);
1,994,516✔
59
                if (e)
1,994,516✔
60
                        return e;
61
        }
62

63
        return NULL;
64
}
65

66
static char* strv_find_closest_prefix(char * const *l, const char *name) {
33✔
67
        size_t best_distance = SIZE_MAX;
33✔
68
        char *best = NULL;
33✔
69

70
        assert(name);
33✔
71

72
        STRV_FOREACH(s, l) {
232✔
73
                char *e = startswith(*s, name);
199✔
74
                if (!e)
199✔
75
                        continue;
134✔
76

77
                size_t n = strlen(e);
65✔
78
                if (n < best_distance) {
65✔
79
                        best_distance = n;
15✔
80
                        best = *s;
15✔
81
                }
82
        }
83

84
        return best;
33✔
85
}
86

87
static char* strv_find_closest_by_levenshtein(char * const *l, const char *name) {
18✔
88
        ssize_t best_distance = SSIZE_MAX;
18✔
89
        char *best = NULL;
18✔
90

91
        assert(name);
18✔
92

93
        STRV_FOREACH(i, l) {
118✔
94
                ssize_t distance;
100✔
95

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

102
                if (distance > 5) /* If the distance is just too far off, don't make a bad suggestion */
100✔
103
                        continue;
61✔
104

105
                if (distance < best_distance) {
39✔
106
                        best_distance = distance;
17✔
107
                        best = *i;
17✔
108
                }
109
        }
110

111
        return best;
112
}
113

114
char* strv_find_closest(char * const *l, const char *name) {
33✔
115
        assert(name);
33✔
116

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

121
        char *found = strv_find_closest_prefix(l, name);
33✔
122
        if (found)
33✔
123
                return found;
124

125
        return strv_find_closest_by_levenshtein(l, name);
18✔
126
}
127

128
char* strv_find_first_field(char * const *needles, char * const *haystack) {
9,019✔
129
        STRV_FOREACH(k, needles) {
23,689✔
130
                char *value = strv_env_pairs_get((char **)haystack, *k);
20,437✔
131
                if (value)
20,437✔
132
                        return value;
133
        }
134

135
        return NULL;
136
}
137

138
char** strv_free(char **l) {
10,297,612✔
139
        STRV_FOREACH(k, l)
29,665,478✔
140
                free(*k);
19,367,866✔
141

142
        return mfree(l);
10,297,612✔
143
}
144

145
char** strv_free_erase(char **l) {
100,355✔
146
        STRV_FOREACH(i, l)
114,598✔
147
                erase_and_freep(i);
14,243✔
148

149
        return mfree(l);
100,355✔
150
}
151

152
void strv_free_many(char ***strvs, size_t n) {
45✔
153
        assert(strvs || n == 0);
45✔
154

155
        FOREACH_ARRAY (i, strvs, n)
120✔
156
                strv_free(*i);
75✔
157

158
        free(strvs);
45✔
159
}
45✔
160

161
char** strv_copy_n(char * const *l, size_t n) {
163,976✔
162
        _cleanup_strv_free_ char **result = NULL;
163,976✔
163
        char **k;
163,976✔
164

165
        result = new(char*, MIN(strv_length(l), n) + 1);
163,976✔
166
        if (!result)
163,976✔
167
                return NULL;
168

169
        k = result;
170
        STRV_FOREACH(i, l) {
955,833✔
171
                if (n == 0)
791,863✔
172
                        break;
173

174
                *k = strdup(*i);
791,857✔
175
                if (!*k)
791,857✔
176
                        return NULL;
177
                k++;
791,857✔
178

179
                if (n != SIZE_MAX)
791,857✔
180
                        n--;
82✔
181
        }
182

183
        *k = NULL;
163,976✔
184
        return TAKE_PTR(result);
163,976✔
185
}
186

187
int strv_copy_unless_empty(char * const *l, char ***ret) {
688✔
188
        assert(ret);
688✔
189

190
        if (strv_isempty(l)) {
688✔
191
                *ret = NULL;
688✔
192
                return 0;
688✔
193
        }
194

UNCOV
195
        char **copy = strv_copy(l);
×
196
        if (!copy)
×
197
                return -ENOMEM;
198

UNCOV
199
        *ret = TAKE_PTR(copy);
×
200
        return 1;
×
201
}
202

203
size_t strv_length(char * const *l) {
13,910,607✔
204
        size_t n = 0;
13,910,607✔
205

206
        STRV_FOREACH(i, l)
308,108,517✔
207
                n++;
294,197,910✔
208

209
        return n;
13,910,607✔
210
}
211

212
char** strv_new_ap(const char *x, va_list ap) {
84,840✔
213
        _cleanup_strv_free_ char **a = NULL;
84,840✔
214
        size_t n = 0, i = 0;
84,840✔
215
        va_list aq;
84,840✔
216

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

222
        va_copy(aq, ap);
84,840✔
223
        for (const char *s = x; s; s = va_arg(aq, const char*)) {
352,638✔
224
                if (s == STRV_IGNORE)
267,798✔
225
                        continue;
7,813✔
226

227
                n++;
259,985✔
228
        }
229
        va_end(aq);
84,840✔
230

231
        a = new(char*, n+1);
84,840✔
232
        if (!a)
84,840✔
233
                return NULL;
234

235
        for (const char *s = x; s; s = va_arg(ap, const char*)) {
352,638✔
236
                if (s == STRV_IGNORE)
267,798✔
237
                        continue;
7,813✔
238

239
                a[i] = strdup(s);
259,985✔
240
                if (!a[i])
259,985✔
241
                        return NULL;
242

243
                i++;
259,985✔
244
        }
245

246
        a[i] = NULL;
84,840✔
247

248
        return TAKE_PTR(a);
84,840✔
249
}
250

251
char** strv_new_internal(const char *x, ...) {
67,974✔
252
        char **r;
67,974✔
253
        va_list ap;
67,974✔
254

255
        va_start(ap, x);
67,974✔
256
        r = strv_new_ap(x, ap);
67,974✔
257
        va_end(ap);
67,974✔
258

259
        return r;
67,974✔
260
}
261

262
int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
24,757✔
263
        size_t p, q, i = 0;
24,757✔
264

265
        assert(a);
24,757✔
266

267
        q = strv_length(b);
24,757✔
268
        if (q == 0)
24,757✔
269
                return 0;
270

271
        p = strv_length(*a);
20,685✔
272
        if (p >= SIZE_MAX - q)
20,685✔
273
                return -ENOMEM;
274

275
        char **t = reallocarray(*a, GREEDY_ALLOC_ROUND_UP(p + q + 1), sizeof(char *));
20,685✔
276
        if (!t)
20,685✔
277
                return -ENOMEM;
278

279
        t[p] = NULL;
20,685✔
280
        *a = t;
20,685✔
281

282
        STRV_FOREACH(s, b) {
49,447✔
283
                if (filter_duplicates && strv_contains(t, *s))
28,762✔
284
                        continue;
47✔
285

286
                t[p+i] = strdup(*s);
28,715✔
287
                if (!t[p+i])
28,715✔
UNCOV
288
                        goto rollback;
×
289

290
                i++;
28,715✔
291
                t[p+i] = NULL;
28,715✔
292
        }
293

294
        assert(i <= q);
20,685✔
295

296
        return (int) i;
20,685✔
297

UNCOV
298
rollback:
×
299
        free_many_charp(t + p, i);
×
300
        t[p] = NULL;
×
301
        return -ENOMEM;
×
302
}
303

304
int strv_extend_strv_consume(char ***a, char **b, bool filter_duplicates) {
203,605✔
305
        _cleanup_strv_free_ char **b_consume = b;
203,605✔
306
        size_t p, q, i;
203,605✔
307

308
        assert(a);
203,605✔
309

310
        q = strv_length(b);
203,605✔
311
        if (q == 0)
203,605✔
312
                return 0;
313

314
        p = strv_length(*a);
136,710✔
315
        if (p == 0) {
136,710✔
316
                strv_free_and_replace(*a, b_consume);
117,808✔
317

318
                if (filter_duplicates)
117,808✔
319
                        strv_uniq(*a);
32,687✔
320

321
                return strv_length(*a);
117,808✔
322
        }
323

324
        if (p >= SIZE_MAX - q)
18,902✔
325
                return -ENOMEM;
326

327
        char **t = reallocarray(*a, GREEDY_ALLOC_ROUND_UP(p + q + 1), sizeof(char *));
18,902✔
328
        if (!t)
18,902✔
329
                return -ENOMEM;
330

331
        t[p] = NULL;
18,902✔
332
        *a = t;
18,902✔
333

334
        if (!filter_duplicates) {
18,902✔
335
                *mempcpy_typesafe(t + p, b, q) = NULL;
18,232✔
336
                i = q;
18,232✔
337
        } else {
338
                i = 0;
339

340
                STRV_FOREACH(s, b) {
1,935✔
341
                        if (strv_contains(t, *s)) {
1,265✔
342
                                free(*s);
201✔
343
                                continue;
201✔
344
                        }
345

346
                        t[p+i] = *s;
1,064✔
347

348
                        i++;
1,064✔
349
                        t[p+i] = NULL;
1,064✔
350
                }
351
        }
352

353
        assert(i <= q);
18,902✔
354

355
        b_consume = mfree(b_consume);
18,902✔
356

357
        return (int) i;
18,902✔
358
}
359

360
int strv_extend_strv_biconcat(char ***a, const char *prefix, const char* const *b, const char *suffix) {
79,760✔
361
        int r;
79,760✔
362

363
        assert(a);
79,760✔
364

365
        STRV_FOREACH(s, b) {
396,880✔
366
                char *v;
317,120✔
367

368
                v = strjoin(strempty(prefix), *s, suffix);
634,238✔
369
                if (!v)
317,120✔
370
                        return -ENOMEM;
371

372
                r = strv_consume(a, v);
317,120✔
373
                if (r < 0)
317,120✔
374
                        return r;
375
        }
376

377
        return 0;
378
}
379

380
int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags) {
615,563✔
381
        _cleanup_strv_free_ char **l = NULL;
615,563✔
382
        size_t n;
615,563✔
383
        int r;
615,563✔
384

385
        assert(ret);
615,563✔
386
        assert(s);
615,563✔
387

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

391
        r = strv_split_full(&l, s, NEWLINE, flags);
615,563✔
392
        if (r < 0)
615,563✔
393
                return r;
394

395
        n = strv_length(l);
615,563✔
396
        if (n > 0 && isempty(l[n - 1])) {
615,563✔
UNCOV
397
                l[n - 1] = mfree(l[n - 1]);
×
398
                n--;
×
399
        }
400

401
        *ret = TAKE_PTR(l);
615,563✔
402
        return n;
615,563✔
403
}
404

405
char** strv_split_newlines(const char *s) {
64,460✔
406
        char **ret;
64,460✔
407

408
        if (strv_split_newlines_full(&ret, s, 0) < 0)
64,460✔
409
                return NULL;
64,460✔
410

411
        return ret;
64,460✔
412
}
413

414
int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags) {
951,714✔
415
        _cleanup_strv_free_ char **l = NULL;
951,714✔
416
        size_t n = 0;
951,714✔
417
        int r;
951,714✔
418

419
        assert(t);
951,714✔
420
        assert(s);
951,714✔
421

422
        for (;;) {
16,758,026✔
423
                _cleanup_free_ char *word = NULL;
7,903,165✔
424

425
                r = extract_first_word(&s, &word, separators, flags);
8,854,870✔
426
                if (r < 0)
8,854,870✔
427
                        return r;
428
                if (r == 0)
8,854,861✔
429
                        break;
430

431
                if (!GREEDY_REALLOC(l, n + 2))
7,903,156✔
432
                        return -ENOMEM;
433

434
                l[n++] = TAKE_PTR(word);
7,903,156✔
435
                l[n] = NULL;
7,903,156✔
436
        }
437

438
        if (!l) {
951,705✔
439
                l = new0(char*, 1);
75,308✔
440
                if (!l)
75,308✔
441
                        return -ENOMEM;
442
        }
443

444
        *t = TAKE_PTR(l);
951,705✔
445

446
        return (int) n;
951,705✔
447
}
448

449
char** strv_split(const char *s, const char *separators) {
47,374✔
450
        char **ret;
47,374✔
451

452
        if (strv_split_full(&ret, s, separators, EXTRACT_RETAIN_ESCAPE) < 0)
47,374✔
453
                return NULL;
47,374✔
454

455
        return ret;
47,374✔
456
}
457

458
int strv_split_and_extend_full(char ***t, const char *s, const char *separators, bool filter_duplicates, ExtractFlags flags) {
1,025✔
459
        char **l;
1,025✔
460
        int r;
1,025✔
461

462
        assert(t);
1,025✔
463
        assert(s);
1,025✔
464

465
        r = strv_split_full(&l, s, separators, flags);
1,025✔
466
        if (r < 0)
1,025✔
467
                return r;
1,025✔
468

469
        r = strv_extend_strv_consume(t, l, filter_duplicates);
1,025✔
470
        if (r < 0)
1,025✔
471
                return r;
472

473
        return (int) strv_length(*t);
1,025✔
474
}
475

476
int strv_split_and_extend(char ***t, const char *s, const char *separators, bool filter_duplicates) {
1,023✔
477
        return strv_split_and_extend_full(t, s, separators, filter_duplicates, 0);
1,023✔
478
}
479

480
int strv_split_colon_pairs(char ***t, const char *s) {
9✔
481
        _cleanup_strv_free_ char **l = NULL;
9✔
482
        size_t n = 0;
9✔
483
        int r;
9✔
484

485
        assert(t);
9✔
486
        assert(s);
9✔
487

488
        for (;;) {
29✔
489
                _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL, *second_or_empty = NULL;
29✔
490

491
                r = extract_first_word(&s, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
29✔
492
                if (r < 0)
29✔
493
                        return r;
494
                if (r == 0)
29✔
495
                        break;
496

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

508
                second_or_empty = strdup(strempty(second));
20✔
509
                if (!second_or_empty)
20✔
510
                        return -ENOMEM;
511

512
                if (!GREEDY_REALLOC(l, n + 3))
20✔
513
                        return -ENOMEM;
514

515
                l[n++] = TAKE_PTR(first);
20✔
516
                l[n++] = TAKE_PTR(second_or_empty);
20✔
517

518
                l[n] = NULL;
20✔
519
        }
520

521
        if (!l) {
8✔
UNCOV
522
                l = new0(char*, 1);
×
523
                if (!l)
×
524
                        return -ENOMEM;
525
        }
526

527
        *t = TAKE_PTR(l);
8✔
528

529
        return (int) n;
8✔
530
}
531

532
char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separator) {
210,328✔
533
        char *r, *e;
210,328✔
534
        size_t n, k, m;
210,328✔
535

536
        if (!separator)
210,328✔
537
                separator = " ";
1,656✔
538

539
        k = strlen(separator);
210,328✔
540
        m = strlen_ptr(prefix);
210,328✔
541

542
        if (escape_separator) /* If the separator was multi-char, we wouldn't know how to escape it. */
210,328✔
543
                assert(k == 1);
687✔
544

545
        n = 0;
546
        STRV_FOREACH(s, l) {
1,041,112✔
547
                if (s != l)
830,784✔
548
                        n += k;
623,316✔
549

550
                bool needs_escaping = escape_separator && strchr(*s, *separator);
830,784✔
551

552
                n += m + strlen(*s) * (1 + needs_escaping);
830,784✔
553
        }
554

555
        r = new(char, n+1);
210,328✔
556
        if (!r)
210,328✔
557
                return NULL;
558

559
        e = r;
560
        STRV_FOREACH(s, l) {
1,041,112✔
561
                if (s != l)
830,784✔
562
                        e = stpcpy(e, separator);
623,316✔
563

564
                if (prefix)
830,784✔
565
                        e = stpcpy(e, prefix);
17✔
566

567
                bool needs_escaping = escape_separator && strchr(*s, *separator);
830,784✔
568

569
                if (needs_escaping)
830,773✔
570
                        for (size_t i = 0; (*s)[i]; i++) {
71✔
571
                                if ((*s)[i] == *separator)
60✔
572
                                        *(e++) = '\\';
14✔
573
                                *(e++) = (*s)[i];
60✔
574
                        }
575
                else
576
                        e = stpcpy(e, *s);
830,773✔
577
        }
578

579
        *e = 0;
210,328✔
580

581
        return r;
210,328✔
582
}
583

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

590
        assert(l);
10,195,289✔
591
        POINTER_MAY_BE_NULL(n);
10,195,289✔
592

593
        if (!value)
10,195,289✔
594
                return 0;
595

596
        size_t size = n ? *n : SIZE_MAX;
10,195,287✔
597
        if (size == SIZE_MAX)
330,839✔
598
                size = strv_length(*l);
9,864,471✔
599

600
        /* Check for overflow */
601
        if (size > SIZE_MAX-2)
10,195,287✔
602
                return -ENOMEM;
603

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

608
        c[size] = value;
10,195,287✔
609
        c[size+1] = NULL;
10,195,287✔
610

611
        *l = c;
10,195,287✔
612
        if (n)
10,195,287✔
613
                *n = size + 1;
330,839✔
614
        return 0;
615
}
616

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

621
        assert(l);
28,472✔
622

623
        if (!a && !b)
28,472✔
624
                return 0;
625

626
        n = strv_length(*l);
28,472✔
627

628
        /* Check for overflow */
629
        if (n > SIZE_MAX-3)
28,472✔
630
                return -ENOMEM;
631

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

637
        if (a)
28,472✔
638
                c[n++] = a;
28,472✔
639
        if (b)
28,472✔
640
                c[n++] = b;
28,472✔
641
        c[n] = NULL;
28,472✔
642

643
        *l = c;
28,472✔
644
        return 0;
28,472✔
645
}
646

647
int strv_insert(char ***l, size_t position, char *value) {
7,167✔
648
        char **c;
7,167✔
649
        size_t n, m;
7,167✔
650

651
        assert(l);
7,167✔
652

653
        if (!value)
7,167✔
654
                return 0;
655

656
        n = strv_length(*l);
7,166✔
657
        position = MIN(position, n);
7,166✔
658

659
        /* check for overflow and increase */
660
        if (n > SIZE_MAX - 2)
7,166✔
661
                return -ENOMEM;
662
        m = n + 2;
7,166✔
663

664
        c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m), sizeof(char*));
7,166✔
665
        if (!c)
7,166✔
666
                return -ENOMEM;
667

668
        if (n > position)
7,166✔
669
                memmove(c + position + 1, c + position, (n - position) * sizeof(char*));
3,088✔
670

671
        c[position] = value;
7,166✔
672
        c[n + 1] = NULL;
7,166✔
673

674
        *l = c;
7,166✔
675
        return 0;
7,166✔
676
}
677

678
int strv_consume_with_size(char ***l, size_t *n, char *value) {
10,130,173✔
679
        int r;
10,130,173✔
680

681
        r = strv_push_with_size(l, n, value);
10,130,173✔
682
        if (r < 0)
10,130,173✔
UNCOV
683
                free(value);
×
684

685
        return r;
10,130,173✔
686
}
687

688
int strv_consume_pair(char ***l, char *a, char *b) {
343✔
689
        int r;
343✔
690

691
        r = strv_push_pair(l, a, b);
343✔
692
        if (r < 0) {
343✔
UNCOV
693
                free(a);
×
694
                free(b);
×
695
        }
696

697
        return r;
343✔
698
}
699

700
int strv_consume_prepend(char ***l, char *value) {
7,161✔
701
        int r;
7,161✔
702

703
        r = strv_push_prepend(l, value);
7,161✔
704
        if (r < 0)
7,161✔
UNCOV
705
                free(value);
×
706

707
        return r;
7,161✔
708
}
709

710
int strv_prepend(char ***l, const char *value) {
5,860✔
711
        char *v;
5,860✔
712

713
        if (!value)
5,860✔
714
                return 0;
715

716
        v = strdup(value);
5,734✔
717
        if (!v)
5,734✔
718
                return -ENOMEM;
719

720
        return strv_consume_prepend(l, v);
5,734✔
721
}
722

723
int strv_extend_with_size(char ***l, size_t *n, const char *value) {
1,848,358✔
724
        char *v;
1,848,358✔
725

726
        if (!value)
1,848,358✔
727
                return 0;
728

729
        v = strdup(value);
1,844,008✔
730
        if (!v)
1,844,008✔
731
                return -ENOMEM;
732

733
        return strv_consume_with_size(l, n, v);
1,844,008✔
734
}
735

736
int strv_extend_many_internal(char ***l, const char *value, ...) {
3,807✔
737
        va_list ap;
3,807✔
738
        size_t n, m;
3,807✔
739
        int r;
3,807✔
740

741
        assert(l);
3,807✔
742

743
        m = n = strv_length(*l);
3,807✔
744

745
        r = 0;
3,807✔
746
        va_start(ap, value);
3,807✔
747
        for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
12,201✔
748
                if (!s)
8,394✔
749
                        continue;
37✔
750

751
                if (m > SIZE_MAX-1) { /* overflow */
8,357✔
752
                        r = -ENOMEM;
753
                        break;
754
                }
755
                m++;
8,357✔
756
        }
757
        va_end(ap);
3,807✔
758

759
        if (r < 0)
3,807✔
760
                return r;
3,807✔
761
        if (m > SIZE_MAX-1)
3,807✔
762
                return -ENOMEM;
763

764
        char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m+1), sizeof(char*));
3,807✔
765
        if (!c)
3,807✔
766
                return -ENOMEM;
767
        *l = c;
3,807✔
768

769
        r = 0;
3,807✔
770
        size_t i = n;
3,807✔
771
        va_start(ap, value);
3,807✔
772
        for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
12,201✔
773
                if (!s)
8,394✔
774
                        continue;
37✔
775

776
                c[i] = strdup(s);
8,357✔
777
                if (!c[i]) {
8,357✔
778
                        r = -ENOMEM;
779
                        break;
780
                }
781
                i++;
8,357✔
782
        }
783
        va_end(ap);
3,807✔
784

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

792
        c[i] = NULL;
3,807✔
793
        return 0;
3,807✔
794
}
795

796
char** strv_uniq(char **l) {
99,229✔
797
        /* Drops duplicate entries. The first identical string will be
798
         * kept, the others dropped */
799

800
        STRV_FOREACH(i, l)
460,265✔
801
                strv_remove(i+1, *i);
361,036✔
802

803
        return l;
99,229✔
804
}
805

806
bool strv_is_uniq(char * const *l) {
55,675✔
807
        STRV_FOREACH(i, l)
128,606✔
808
                if (strv_contains(i+1, *i))
72,932✔
809
                        return false;
810

811
        return true;
812
}
813

814
char** strv_remove(char **l, const char *s) {
361,140✔
815
        if (!l)
361,140✔
816
                return NULL;
817

818
        assert(s);
361,140✔
819

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

822
        char **f, **t;
823
        for (f = t = l; *f; f++)
1,920,416✔
824
                if (streq(*f, s))
1,559,276✔
825
                        free(*f);
47,578✔
826
                else
827
                        *(t++) = *f;
1,511,698✔
828

829
        *t = NULL;
361,140✔
830
        return l;
361,140✔
831
}
832

833
char** strv_remove_strv(char **l, char *const*ll) {
15✔
834

835
        if (strv_isempty(l))
15✔
836
                return l;
837

838
        STRV_FOREACH(i, ll)
23✔
839
                strv_remove(l, *i);
10✔
840

841
        return l;
842
}
843

844
bool strv_overlap(char * const *a, char * const *b) {
1,007✔
845
        POINTER_MAY_BE_NULL(a);
1,007✔
846
        POINTER_MAY_BE_NULL(b);
1,007✔
847

848
        STRV_FOREACH(i, a)
1,784✔
849
                if (strv_contains(b, *i))
1,010✔
850
                        return true;
851

852
        return false;
853
}
854

855
static int str_compare(char * const *a, char * const *b) {
1,277,086✔
856
        /* This is called from qsort()s inner loops. Correctly implemented qsort will never pass NULL so we
857
           just suppress the check via POINTER_MAY_BE_NULL instead of assert() to avoid the runtime cost. */
858
        POINTER_MAY_BE_NULL(a);
1,277,086✔
859
        POINTER_MAY_BE_NULL(b);
1,277,086✔
860

861
        return strcmp(*a, *b);
1,277,086✔
862
}
863

864
char** strv_sort(char **l) {
50,720✔
865
        typesafe_qsort(l, strv_length(l), str_compare);
50,720✔
866
        return l;
50,720✔
867
}
868

869
char** strv_sort_uniq(char **l) {
43,124✔
870
        if (strv_isempty(l))
43,124✔
871
                return l;
872

873
        char **tail = strv_sort(l), *prev = NULL;
498✔
874
        STRV_FOREACH(i, l)
5,206✔
875
                if (streq_ptr(*i, prev))
4,210✔
876
                        free(*i);
33✔
877
                else
878
                        *(tail++) = prev = *i;
4,177✔
879

880
        *tail = NULL;
498✔
881
        return l;
498✔
882
}
883

884
int strv_compare(char * const *a, char * const *b) {
15,180✔
885
        int r;
15,180✔
886

887
        POINTER_MAY_BE_NULL(a);
15,180✔
888
        POINTER_MAY_BE_NULL(b);
15,180✔
889

890
        if (strv_isempty(a)) {
15,180✔
891
                if (strv_isempty(b))
3,352✔
892
                        return 0;
3,264✔
893
                else
894
                        return -1;
895
        }
896

897
        if (strv_isempty(b))
11,828✔
898
                return 1;
899

900
        for ( ; *a || *b; ++a, ++b) {
26,756✔
901
                r = strcmp_ptr(*a, *b);
15,109✔
902
                if (r != 0)
15,109✔
903
                        return r;
904
        }
905

906
        return 0;
907
}
908

909
bool strv_equal_ignore_order(char * const *a, char * const *b) {
520✔
910

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

915
        if (a == b)
520✔
916
                return true;
917

918
        STRV_FOREACH(i, a)
975✔
919
                if (!strv_contains(b, *i))
503✔
920
                        return false;
921

922
        STRV_FOREACH(i, b)
971✔
923
                if (!strv_contains(a, *i))
503✔
924
                        return false;
925

926
        return true;
927
}
928

929
void strv_print_full(char * const *l, const char *prefix) {
117✔
930
        STRV_FOREACH(s, l)
3,160✔
931
                printf("%s%s\n", strempty(prefix), *s);
6,086✔
932
}
117✔
933

934
int strv_extendf_with_size(char ***l, size_t *n, const char *format, ...) {
1,331,352✔
935
        va_list ap;
1,331,352✔
936
        char *x;
1,331,352✔
937
        int r;
1,331,352✔
938

939
        va_start(ap, format);
1,331,352✔
940
        r = vasprintf(&x, format, ap);
1,331,352✔
941
        va_end(ap);
1,331,352✔
942

943
        if (r < 0)
1,331,352✔
944
                return -ENOMEM;
1,331,352✔
945

946
        return strv_consume_with_size(l, n, x);
1,331,352✔
947
}
948

949
int strv_extend_joined_with_size_sentinel(char ***l, size_t *n, ...) {
62,841✔
950
        va_list ap;
62,841✔
951

952
        va_start(ap, n);
62,841✔
953
        char *x = strextendv_with_separator(/* x= */ NULL, /* separator= */ NULL, ap);
62,841✔
954
        va_end(ap);
62,841✔
955
        if (!x)
62,841✔
956
                return -ENOMEM;
62,841✔
957

958
        return strv_consume_with_size(l, n, x);
62,841✔
959
}
960

961
char* startswith_strv_internal(const char *s, char * const *l) {
13,409,077✔
962
        STRV_FOREACH(i, l) {
40,771,832✔
963
                char *found = (char*) startswith(s, *i);
27,504,548✔
964
                if (found)
27,504,548✔
965
                        return found;
966
        }
967

968
        return NULL;
969
}
970

971
char* endswith_strv_internal(const char *s, char * const *l) {
6,186,635✔
972
        STRV_FOREACH(i, l) {
18,569,160✔
973
                char *found = (char*) endswith(s, *i);
12,420,260✔
974
                if (found)
12,420,260✔
975
                        return found;
976
        }
977

978
        return NULL;
979
}
980

981
char** strv_reverse(char **l) {
932✔
982
        size_t n;
932✔
983

984
        n = strv_length(l);
932✔
985
        if (n <= 1)
932✔
986
                return l;
987

988
        for (size_t i = 0; i < n / 2; i++)
458✔
989
                SWAP_TWO(l[i], l[n-1-i]);
286✔
990

991
        return l;
992
}
993

994
char** strv_shell_escape(char **l, const char *bad) {
3✔
995
        /* Escapes every character in every string in l that is in bad,
996
         * edits in-place, does not roll-back on error. */
997

998
        STRV_FOREACH(s, l) {
8✔
999
                char *v;
5✔
1000

1001
                v = shell_escape(*s, bad);
5✔
1002
                if (!v)
5✔
1003
                        return NULL;
3✔
1004

1005
                free_and_replace(*s, v);
5✔
1006
        }
1007

1008
        return l;
1009
}
1010

1011
bool strv_fnmatch_full(
105,350✔
1012
                char* const* patterns,
1013
                const char *s,
1014
                int flags,
1015
                size_t *ret_matched_pos) {
1016

1017
        assert(s);
105,350✔
1018

1019
        if (patterns)
105,350✔
1020
                for (size_t i = 0; patterns[i]; i++)
190,158✔
1021
                        /* NB: We treat all fnmatch() errors as equivalent to FNM_NOMATCH, i.e. if fnmatch() fails to
1022
                         * process the pattern for some reason we'll consider this equivalent to non-matching. */
1023
                        if (fnmatch(patterns[i], s, flags) == 0) {
109,070✔
1024
                                if (ret_matched_pos)
20,820✔
1025
                                        *ret_matched_pos = i;
1,663✔
1026
                                return true;
1027
                        }
1028

1029
        if (ret_matched_pos)
84,530✔
1030
                *ret_matched_pos = SIZE_MAX;
20,019✔
1031

1032
        return false;
1033
}
1034

1035
char** strv_skip(char **l, size_t n) {
27,561✔
1036
        while (n > 0) {
59,949✔
1037
                if (strv_isempty(l))
33,742✔
1038
                        return NULL;
1039

1040
                l++, n--;
32,388✔
1041
        }
1042

1043
        /* To simplify callers, always return NULL instead of a zero-item array. */
1044
        if (strv_isempty(l))
26,207✔
1045
                return NULL;
5,361✔
1046
        return l;
1047
}
1048

1049
int strv_extend_n(char ***l, const char *value, size_t n) {
850✔
1050
        size_t i, k;
850✔
1051
        char **nl;
850✔
1052

1053
        assert(l);
850✔
1054

1055
        if (!value)
850✔
1056
                return 0;
1057
        if (n == 0)
850✔
1058
                return 0;
1059

1060
        /* Adds the value n times to l */
1061

1062
        k = strv_length(*l);
849✔
1063
        if (n >= SIZE_MAX - k)
849✔
1064
                return -ENOMEM;
1065

1066
        nl = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(k + n + 1), sizeof(char *));
849✔
1067
        if (!nl)
849✔
1068
                return -ENOMEM;
1069

1070
        *l = nl;
849✔
1071

1072
        for (i = k; i < k + n; i++) {
1,754✔
1073
                nl[i] = strdup(value);
905✔
1074
                if (!nl[i])
905✔
UNCOV
1075
                        goto rollback;
×
1076
        }
1077
        nl[i] = NULL;
849✔
1078

1079
        return 0;
849✔
1080

UNCOV
1081
rollback:
×
1082
        for (size_t j = k; j < i; j++)
×
1083
                free(nl[j]);
×
1084
        nl[k] = NULL;
×
1085

UNCOV
1086
        return -ENOMEM;
×
1087
}
1088

1089
int strv_extend_assignment(char ***l, const char *lhs, const char *rhs) {
7,726,228✔
1090
        char *j;
7,726,228✔
1091

1092
        assert(l);
7,726,228✔
1093
        assert(lhs);
7,726,228✔
1094

1095
        if (!rhs) /* value is optional, in which case we suppress the field */
7,726,228✔
1096
                return 0;
1097

1098
        j = strjoin(lhs, "=", rhs);
5,475,743✔
1099
        if (!j)
5,475,743✔
1100
                return -ENOMEM;
1101

1102
        return strv_consume(l, j);
5,475,743✔
1103
}
1104

1105
int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) {
16,568✔
1106
        bool b = false;
16,568✔
1107
        int r;
16,568✔
1108

1109
        assert(f);
16,568✔
1110

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

1113
        if (!space)
16,568✔
1114
                space = &b;
5✔
1115

1116
        STRV_FOREACH(s, l) {
16,969✔
1117
                r = fputs_with_separator(f, *s, separator, space);
401✔
1118
                if (r < 0)
401✔
1119
                        return r;
16,568✔
1120
        }
1121

1122
        return 0;
1123
}
1124

1125
void string_strv_hashmap_remove(Hashmap *h, const char *key, const char *value) {
571✔
1126
        assert(key);
571✔
1127

1128
        if (value) {
571✔
1129
                char **l = hashmap_get(h, key);
571✔
1130
                if (!l)
571✔
1131
                        return;
543✔
1132

1133
                strv_remove(l, value);
32✔
1134
                if (!strv_isempty(l))
571✔
1135
                        return;
1136
        }
1137

1138
        _unused_ _cleanup_free_ char *key_free = NULL;
28✔
1139
        strv_free(hashmap_remove2(h, key, (void**) &key_free));
28✔
1140
}
1141

1142
void string_strv_ordered_hashmap_remove(OrderedHashmap *h, const char *key, const char *value) {
4✔
1143
        string_strv_hashmap_remove(PLAIN_HASHMAP(h), key, value);
4✔
1144
}
4✔
1145

1146
static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const char *value) {
590,767✔
1147
        char **l;
590,767✔
1148
        int r;
590,767✔
1149

1150
        assert(h);
590,767✔
1151
        assert(key);
590,767✔
1152
        assert(value);
590,767✔
1153

1154
        l = hashmap_get(h, key);
590,767✔
1155
        if (l) {
590,767✔
1156
                /* A list for this key already exists, let's append to it if it is not listed yet */
1157
                if (strv_contains(l, value))
31,065✔
1158
                        return 0;
590,767✔
1159

1160
                r = strv_extend(&l, value);
31,052✔
1161
                if (r < 0)
31,052✔
1162
                        return r;
1163

1164
                assert_se(hashmap_update(h, key, l) >= 0);
31,052✔
1165
        } else {
1166
                /* No list for this key exists yet, create one */
UNCOV
1167
                _cleanup_strv_free_ char **l2 = NULL;
×
1168
                _cleanup_free_ char *t = NULL;
559,702✔
1169

1170
                t = strdup(key);
559,702✔
1171
                if (!t)
559,702✔
1172
                        return -ENOMEM;
1173

1174
                r = strv_extend(&l2, value);
559,702✔
1175
                if (r < 0)
559,702✔
1176
                        return r;
1177

1178
                r = hashmap_put(h, t, l2);
559,702✔
1179
                if (r < 0)
559,702✔
1180
                        return r;
1181

1182
                TAKE_PTR(t);
559,702✔
1183
                TAKE_PTR(l2);
559,702✔
1184
        }
1185

1186
        return 1;
1187
}
1188

1189
int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value) {
590,741✔
1190
        int r;
590,741✔
1191

1192
        assert(h);
590,741✔
1193
        assert(key);
590,741✔
1194
        assert(value);
590,741✔
1195

1196
        r = hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free);
590,741✔
1197
        if (r < 0)
590,741✔
1198
                return r;
1199

1200
        return string_strv_hashmap_put_internal(*h, key, value);
590,741✔
1201
}
1202

1203
int string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value) {
26✔
1204
        int r;
26✔
1205

1206
        assert(h);
26✔
1207
        assert(key);
26✔
1208
        assert(value);
26✔
1209

1210
        r = ordered_hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free);
26✔
1211
        if (r < 0)
26✔
1212
                return r;
1213

1214
        return string_strv_hashmap_put_internal(PLAIN_HASHMAP(*h), key, value);
26✔
1215
}
1216

1217
int strv_rebreak_lines(char **l, size_t width, char ***ret) {
70,204✔
1218
        _cleanup_strv_free_ char **broken = NULL;
70,204✔
1219
        int r;
70,204✔
1220

1221
        assert(ret);
70,204✔
1222

1223
        /* Implements a simple UTF-8 line breaking algorithm
1224
         *
1225
         * Goes through all entries in *l, and line-breaks each line that is longer than the specified
1226
         * character width. Breaks at the end of words/beginning of whitespace. Lines that do not contain whitespace are not
1227
         * broken. Retains whitespace at beginning of lines, removes it at end of lines. */
1228

1229
        if (width == SIZE_MAX) { /* NOP? */
70,204✔
1230
                broken = strv_copy(l);
9,472✔
1231
                if (!broken)
9,472✔
1232
                        return -ENOMEM;
1233

1234
                *ret = TAKE_PTR(broken);
9,472✔
1235
                return 0;
9,472✔
1236
        }
1237

1238
        STRV_FOREACH(i, l) {
121,587✔
1239
                const char *start = *i, *whitespace_begin = NULL, *whitespace_end = NULL;
1240
                bool in_prefix = true; /* still in the whitespace in the beginning of the line? */
1241
                size_t w = 0;
1242

1243
                for (const char *p = start; *p != 0; ) {
3,488,015✔
1244
                        if (strchr(NEWLINE, *p)) {
3,427,160✔
1245
                                in_prefix = true;
1246
                                whitespace_begin = whitespace_end = NULL;
1247
                                w = 0;
1248
                        } else if (strchr(WHITESPACE, *p)) {
3,427,160✔
1249
                                if (!in_prefix && (!whitespace_begin || whitespace_end)) {
304,425✔
1250
                                        whitespace_begin = p;
303,828✔
1251
                                        whitespace_end = NULL;
303,828✔
1252
                                }
1253
                        } else {
1254
                                if (whitespace_begin && !whitespace_end)
3,122,735✔
1255
                                        whitespace_end = p;
303,819✔
1256

1257
                                in_prefix = false;
1258
                        }
1259

1260
                        char32_t c;
3,427,160✔
1261
                        int n = utf8_encoded_to_unichar(p, &c);
3,427,160✔
1262
                        if (n < 0)
3,427,160✔
1263
                                return log_debug_errno(n, "Line to break contains invalid UTF-8, refusing: %m");
3✔
1264

1265
                        int cw = unichar_console_width(c);
3,427,157✔
1266
                        assert(cw >= 0);
3,427,157✔
1267

1268
                        w += cw;
3,427,157✔
1269

1270
                        if (w > width && whitespace_begin && whitespace_end) {
3,427,157✔
UNCOV
1271
                                _cleanup_free_ char *truncated = NULL;
×
1272

1273
                                truncated = strndup(start, whitespace_begin - start);
16,329✔
1274
                                if (!truncated)
16,329✔
1275
                                        return -ENOMEM;
1276

1277
                                r = strv_consume(&broken, TAKE_PTR(truncated));
16,329✔
1278
                                if (r < 0)
16,329✔
1279
                                        return r;
1280

1281
                                /* Continue with the next line, starting at the first character after the
1282
                                 * whitespace we broke at. Note, that character has not been measured for
1283
                                 * the new line yet, hence reset the width and reprocess it. */
1284
                                p = start = whitespace_end;
16,329✔
1285
                                whitespace_begin = whitespace_end = NULL;
16,329✔
1286
                                w = 0;
16,329✔
1287
                                continue;
16,329✔
1288
                        }
1289

1290
                        p += n;
3,410,828✔
1291
                }
1292

1293
                /* Process rest of the line */
1294
                assert(start);
60,855✔
1295
                if (in_prefix) /* Never seen anything non-whitespace? Generate empty line! */
60,855✔
1296
                        r = strv_extend(&broken, "");
1✔
1297
                else if (whitespace_begin && !whitespace_end) { /* Ends in whitespace? Chop it off! */
60,854✔
UNCOV
1298
                        _cleanup_free_ char *truncated = strndup(start, whitespace_begin - start);
×
1299
                        if (!truncated)
9✔
UNCOV
1300
                                return -ENOMEM;
×
1301

1302
                        r = strv_consume(&broken, TAKE_PTR(truncated));
9✔
1303
                } else /* Otherwise use line as is */
1304
                        r = strv_extend(&broken, start);
60,845✔
1305
                if (r < 0)
60,855✔
1306
                        return r;
1307
        }
1308

1309
        *ret = TAKE_PTR(broken);
60,729✔
1310
        return 0;
60,729✔
1311
}
1312

1313
char** strv_filter_prefix(char * const *l, const char *prefix) {
4✔
1314

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

1317
        if (isempty(prefix))
4✔
1318
                return strv_copy(l);
5✔
1319

1320
        _cleanup_strv_free_ char **f = NULL;
3✔
1321
        size_t sz = 0;
3✔
1322

1323
        STRV_FOREACH(i, l) {
24✔
1324
                if (!startswith(*i, prefix))
21✔
1325
                        continue;
16✔
1326

1327
                if (strv_extend_with_size(&f, &sz, *i) < 0)
5✔
1328
                        return NULL;
1329
        }
1330

1331
        return TAKE_PTR(f);
3✔
1332
}
1333

1334
const char* const strv_empty[] = { NULL };
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc