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

systemd / systemd / 28272947092

26 Jun 2026 08:38PM UTC coverage: 72.893% (+0.2%) from 72.703%
28272947092

push

github

poettering
sysupdate: Address review feedback on CheckNew varlink scaffolding

Follow-up to #42422:

 - Rename process_image() to context_process_image(), since it now
   operates on a Context object.
 - Use IN_SET() in image_type_can_sysupdate() instead of a switch.
 - Name the return parameters of context_list_components() ret_xyz, per
   our coding style.
 - Drop a redundant "else" after a return in vl_method_check_new().

9 of 11 new or added lines in 1 file covered. (81.82%)

12567 existing lines in 144 files now uncovered.

341026 of 467845 relevant lines covered (72.89%)

1339355.33 hits per line

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

91.93
/src/basic/string-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <stdio.h>
4

5
#include "alloc-util.h"
6
#include "escape.h"
7
#include "extract-word.h"
8
#include "glyph-util.h"
9
#include "gunicode.h"
10
#include "locale-util.h"
11
#include "log.h"
12
#include "memory-util.h"
13
#include "memstream-util.h"
14
#include "path-util.h"
15
#include "string-util.h"
16
#include "strv.h"
17
#include "terminal-util.h"
18
#include "utf8.h"
19

20
char* first_word(const char *s, const char *word) {
4,592,831✔
21
        assert(s);
4,592,831✔
22
        assert(word);
4,592,831✔
23

24
        /* Checks if the string starts with the specified word, either followed by NUL or by whitespace.
25
         * Returns a pointer to the NUL or the first character after the whitespace. */
26

27
        if (isempty(word))
4,592,831✔
28
                return (char*) s;
29

30
        const char *p = startswith(s, word);
4,592,830✔
31
        if (!p)
4,592,830✔
32
                return NULL;
33
        if (*p == '\0')
253,149✔
34
                return (char*) p;
35

36
        const char *nw = skip_leading_chars(p, WHITESPACE);
253,148✔
37
        if (p == nw)
253,148✔
38
                return NULL;
1✔
39

40
        return (char*) nw;
41
}
42

43
char* strextendn(char **x, const char *s, size_t l) {
33,174✔
44
        assert(x);
33,174✔
45
        assert(s || l == 0);
33,174✔
46

47
        if (l > 0)
33,174✔
48
                l = strnlen(s, l); /* ignore trailing noise */
32,840✔
49

50
        if (l > 0 || !*x) {
33,174✔
51
                size_t q;
33,014✔
52
                char *m;
33,014✔
53

54
                q = strlen_ptr(*x);
33,014✔
55
                m = realloc(*x, q + l + 1);
33,014✔
56
                if (!m)
33,014✔
57
                        return NULL;
58

59
                *mempcpy_typesafe(m + q, s, l) = 0;
33,014✔
60

61
                *x = m;
33,014✔
62
        }
63

64
        return *x;
33,174✔
65
}
66

67
char* strstrip(char *s) {
10,294,581✔
68
        if (!s)
10,294,581✔
69
                return NULL;
70

71
        /* Drops trailing whitespace. Modifies the string in place. Returns pointer to first non-space character */
72

73
        return delete_trailing_chars(skip_leading_chars(s, WHITESPACE), WHITESPACE);
10,291,869✔
74
}
75

76
char* delete_chars(char *s, const char *bad) {
9✔
77
        char *f, *t;
9✔
78

79
        /* Drops all specified bad characters, regardless where in the string */
80

81
        if (!s)
9✔
82
                return NULL;
83

84
        if (!bad)
9✔
85
                bad = WHITESPACE;
×
86

87
        for (f = s, t = s; *f; f++) {
131✔
88
                if (strchr(bad, *f))
122✔
89
                        continue;
67✔
90

91
                *(t++) = *f;
55✔
92
        }
93

94
        *t = 0;
9✔
95

96
        return s;
9✔
97
}
98

99
char* delete_trailing_chars(char *s, const char *bad) {
11,923,183✔
100
        char *c = s;
11,923,183✔
101

102
        /* Drops all specified bad characters, at the end of the string */
103

104
        if (!s)
11,923,183✔
105
                return NULL;
106

107
        if (!bad)
11,923,183✔
108
                bad = WHITESPACE;
88,806✔
109

110
        for (char *p = s; *p; p++)
387,379,750✔
111
                if (!strchr(bad, *p))
375,456,567✔
112
                        c = p + 1;
360,654,304✔
113

114
        *c = 0;
11,923,183✔
115

116
        return s;
11,923,183✔
117
}
118

119
char* truncate_nl_full(char *s, size_t *ret_len) {
2,917,972✔
120
        size_t n;
2,917,972✔
121

122
        assert(s);
2,917,972✔
123

124
        n = strcspn(s, NEWLINE);
2,917,972✔
125
        s[n] = '\0';
2,917,972✔
126
        if (ret_len)
2,917,972✔
127
                *ret_len = n;
6✔
128
        return s;
2,917,972✔
129
}
130

131
char ascii_tolower(char x) {
52,410,362✔
132

133
        if (x >= 'A' && x <= 'Z')
52,410,362✔
134
                return x - 'A' + 'a';
3,605,711✔
135

136
        return x;
137
}
138

139
char ascii_toupper(char x) {
331,041✔
140

141
        if (x >= 'a' && x <= 'z')
331,041✔
142
                return x - 'a' + 'A';
204,609✔
143

144
        return x;
145
}
146

147
char* ascii_strlower(char *s) {
24,035✔
148
        assert(s);
24,035✔
149

150
        for (char *p = s; *p; p++)
155,966✔
151
                *p = ascii_tolower(*p);
131,931✔
152

153
        return s;
24,035✔
154
}
155

156
char* ascii_strupper(char *s) {
23,051✔
157
        assert(s);
23,051✔
158

159
        for (char *p = s; *p; p++)
353,962✔
160
                *p = ascii_toupper(*p);
330,911✔
161

162
        return s;
23,051✔
163
}
164

165
char* ascii_strlower_n(char *s, size_t n) {
1,793,548✔
166
        assert(n <= 0 || s);
1,793,548✔
167

168
        if (n <= 0)
1,793,548✔
169
                return s;
170

171
        for (size_t i = 0; i < n; i++)
15,073,071✔
172
                s[i] = ascii_tolower(s[i]);
13,279,866✔
173

174
        return s;
175
}
176

177
int ascii_strcasecmp_n(const char *a, const char *b, size_t n) {
2,582,169✔
178

179
        assert(a);
2,582,169✔
180
        assert(b);
2,582,169✔
181

182
        for (; n > 0; a++, b++, n--) {
21,478,598✔
183
                int x, y;
19,494,432✔
184

185
                x = (int) (uint8_t) ascii_tolower(*a);
19,494,432✔
186
                y = (int) (uint8_t) ascii_tolower(*b);
19,494,432✔
187

188
                if (x != y)
19,494,432✔
189
                        return x - y;
598,003✔
190
        }
191

192
        return 0;
193
}
194

195
int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) {
1,096,068✔
196
        int r;
1,096,068✔
197

198
        r = ascii_strcasecmp_n(a, b, MIN(n, m));
1,096,068✔
199
        if (r != 0)
1,096,068✔
200
                return r;
201

202
        return CMP(n, m);
966,773✔
203
}
204

205
bool chars_intersect(const char *a, const char *b) {
12,403✔
206
        /* Returns true if any of the chars in a are in b. */
207
        for (const char *p = a; *p; p++)
179,650✔
208
                if (strchr(b, *p))
167,269✔
209
                        return true;
210

211
        return false;
212
}
213

214
bool string_has_cc(const char *p, const char *ok) {
308,204✔
215
        assert(p);
308,204✔
216

217
        /*
218
         * Check if a string contains control characters. If 'ok' is
219
         * non-NULL it may be a string containing additional CCs to be
220
         * considered OK.
221
         */
222

223
        for (const char *t = p; *t; t++) {
2,955,646✔
224
                if (ok && strchr(ok, *t))
2,647,458✔
225
                        continue;
8✔
226

227
                if (char_is_cc(*t))
2,647,450✔
228
                        return true;
229
        }
230

231
        return false;
232
}
233

234
static int write_ellipsis(char *buf, bool unicode) {
4,413✔
235
        const char *s = glyph_full(GLYPH_ELLIPSIS, unicode);
4,413✔
236
        assert(strlen(s) == 3);
4,413✔
237
        memcpy(buf, s, 3);
4,413✔
238
        return 3;
4,413✔
239
}
240

241
static size_t ansi_sequence_length(const char *s, size_t len) {
6,342✔
242
        assert(s);
6,342✔
243

244
        if (len < 2)
6,342✔
245
                return 0;
246

247
        if (s[0] != 0x1B)  /* ASCII 27, aka ESC, aka Ctrl-[ */
6,250✔
248
                return 0;  /* Not the start of a sequence */
249

250
        if (s[1] == 0x5B) { /* [, start of CSI sequence */
909✔
251
                size_t i = 2;
908✔
252

253
                if (i == len)
908✔
254
                        return 0;
255

256
                while (s[i] >= 0x30 && s[i] <= 0x3F) /* Parameter bytes */
9,251✔
257
                        if (++i == len)
8,343✔
258
                                return 0;
259
                while (s[i] >= 0x20 && s[i] <= 0x2F) /* Intermediate bytes */
908✔
UNCOV
260
                        if (++i == len)
×
261
                                return 0;
262
                if (s[i] >= 0x40 && s[i] <= 0x7E) /* Final byte */
908✔
263
                        return i + 1;
908✔
264
                return 0;  /* Bad sequence */
265

266
        } else if (s[1] >= 0x40 && s[1] <= 0x5F) /* other non-CSI Fe sequence */
1✔
UNCOV
267
                return 2;
×
268

269
        return 0;  /* Bad escape? */
270
}
271

272
static bool string_has_ansi_sequence(const char *s, size_t len) {
7,977✔
273
        const char *t = s;
7,977✔
274

275
        while ((t = memchr(t, 0x1B, len - (t - s)))) {
7,978✔
276
                if (ansi_sequence_length(t, len - (t - s)) > 0)
157✔
277
                        return true;
278
                t++;
1✔
279
        }
280
        return false;
281
}
282

283
static size_t previous_ansi_sequence(const char *s, size_t length, const char **ret_where) {
269✔
284

285
        assert(s);
269✔
286
        assert(ret_where);
269✔
287

288
        /* Locate the previous ANSI sequence and save its start in *ret_where and return length. */
289

290
        if (length < 2) {
269✔
291
                /* Need at least two bytes for an ANSI sequence */
292
                *ret_where = NULL;
2✔
293
                return 0;
2✔
294
        }
295

296
        for (size_t i = length - 2; i > 0; i--) {  /* -2 because at least two bytes are needed */
3,444✔
297
                size_t slen = ansi_sequence_length(s + (i - 1), length - (i - 1));
3,442✔
298
                if (slen == 0)
3,442✔
299
                        continue;
3,177✔
300

301
                *ret_where = s + (i - 1);
265✔
302
                return slen;
265✔
303
        }
304

305
        *ret_where = NULL;
2✔
306
        return 0;
2✔
307
}
308

309
static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
4,421✔
310
        size_t x, need_space, suffix_len;
4,421✔
311
        char *t;
4,421✔
312

313
        assert(s);
4,421✔
314
        assert(percent <= 100);
4,421✔
315
        assert(new_length != SIZE_MAX);
4,421✔
316

317
        if (old_length <= new_length)
4,421✔
318
                return strndup(s, old_length);
1,800✔
319

320
        /* Special case short ellipsations */
321
        switch (new_length) {
2,621✔
322

UNCOV
323
        case 0:
×
UNCOV
324
                return strdup("");
×
325

326
        case 1:
65✔
327
                if (is_locale_utf8())
65✔
328
                        return strdup("…");
65✔
329
                else
UNCOV
330
                        return strdup(".");
×
331

332
        case 2:
35✔
333
                if (!is_locale_utf8())
35✔
UNCOV
334
                        return strdup("..");
×
335
                break;
336
        }
337

338
        /* Calculate how much space the ellipsis will take up. If we are in UTF-8 mode we only need space for one
339
         * character ("…"), otherwise for three characters ("..."). Note that in both cases we need 3 bytes of storage,
340
         * either for the UTF-8 encoded character or for three ASCII characters. */
341
        need_space = is_locale_utf8() ? 1 : 3;
2,556✔
342

343
        t = new(char, new_length+3);
2,556✔
344
        if (!t)
2,556✔
345
                return NULL;
346

347
        assert(new_length >= need_space);
2,556✔
348

349
        x = ((new_length - need_space) * percent + 50) / 100;
2,556✔
350
        assert(x <= new_length - need_space);
2,556✔
351

352
        write_ellipsis(mempcpy(t, s, x), /* unicode= */ false);
2,556✔
353
        suffix_len = new_length - x - need_space;
2,556✔
354
        memcpy(t + x + 3, s + old_length - suffix_len, suffix_len);
2,556✔
355
        *(t + x + 3 + suffix_len) = '\0';
2,556✔
356

357
        return t;
2,556✔
358
}
359

360
char* ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
8,148✔
361
        size_t x, k, len, len2;
8,148✔
362
        const char *i, *j;
8,148✔
363
        int r;
8,148✔
364

365
        /* Note that 'old_length' refers to bytes in the string, while 'new_length' refers to character cells taken up
366
         * on screen. This distinction doesn't matter for ASCII strings, but it does matter for non-ASCII UTF-8
367
         * strings.
368
         *
369
         * Ellipsation is done in a locale-dependent way:
370
         * 1. If the string passed in is fully ASCII and the current locale is not UTF-8, three dots are used ("...")
371
         * 2. Otherwise, a unicode ellipsis is used ("…")
372
         *
373
         * In other words: you'll get a unicode ellipsis as soon as either the string contains non-ASCII characters or
374
         * the current locale is UTF-8.
375
         */
376

377
        assert(s);
8,148✔
378
        assert(percent <= 100);
8,148✔
379

380
        if (new_length == SIZE_MAX)
8,148✔
UNCOV
381
                return strndup(s, old_length);
×
382

383
        if (new_length == 0)
8,148✔
384
                return strdup("");
171✔
385

386
        bool has_ansi_seq = string_has_ansi_sequence(s, old_length);
7,977✔
387

388
        /* If no multibyte characters or ANSI sequences, use ascii_ellipsize_mem for speed */
389
        if (!has_ansi_seq && ascii_is_valid_n(s, old_length))
7,977✔
390
                return ascii_ellipsize_mem(s, old_length, new_length, percent);
4,421✔
391

392
        x = (new_length - 1) * percent / 100;
3,556✔
393
        assert(x <= new_length - 1);
3,556✔
394

395
        k = 0;
396
        for (i = s; i < s + old_length; ) {
49,923✔
397
                size_t slen = has_ansi_seq ? ansi_sequence_length(i, old_length - (i - s)) : 0;
48,731✔
398
                if (slen > 0) {
1,014✔
399
                        i += slen;
225✔
400
                        continue;  /* ANSI sequences don't take up any space in output */
225✔
401
                }
402

403
                char32_t c;
48,506✔
404
                r = utf8_encoded_to_unichar(i, &c);
48,506✔
405
                if (r < 0)
48,506✔
UNCOV
406
                        return NULL;
×
407

408
                int w = unichar_iswide(c) ? 2 : 1;
48,506✔
409
                if (k + w > x)
48,506✔
410
                        break;
411

412
                k += w;
46,142✔
413
                i += r;
46,142✔
414
        }
415

416
        const char *ansi_start = s + old_length;
3,556✔
417
        size_t ansi_len = 0;
3,556✔
418

419
        for (const char *t = j = s + old_length; t > i && k < new_length; ) {
13,248✔
420
                char32_t c;
9,778✔
421
                int w;
9,778✔
422
                const char *tt;
9,778✔
423

424
                if (has_ansi_seq && ansi_start >= t)
9,778✔
425
                        /* Figure out the previous ANSI sequence, if any */
426
                        ansi_len = previous_ansi_sequence(s, t - s, &ansi_start);
269✔
427

428
                /* If the sequence extends all the way to the current position, skip it. */
429
                if (has_ansi_seq && ansi_len > 0 && ansi_start + ansi_len == t) {
9,778✔
430
                        t = ansi_start;
114✔
431
                        continue;
114✔
432
                }
433

434
                tt = utf8_prev_char(t);
9,664✔
435
                r = utf8_encoded_to_unichar(tt, &c);
9,664✔
436
                if (r < 0)
9,664✔
UNCOV
437
                        return NULL;
×
438

439
                w = unichar_iswide(c) ? 2 : 1;
9,664✔
440
                if (k + w > new_length)
9,664✔
441
                        break;
442

443
                k += w;
9,578✔
444
                j = t = tt;  /* j should always point to the first "real" character */
9,578✔
445
        }
446

447
        /* We don't actually need to ellipsize */
448
        if (i >= j)
3,556✔
449
                return memdup_suffix0(s, old_length);
1,723✔
450

451
        if (k >= new_length) {
1,833✔
452
                /* Make space for ellipsis, if required and possible. We know that the edge character is not
453
                 * part of an ANSI sequence (because then we'd skip it). If the last character we looked at
454
                 * was wide, we don't need to make space. */
455
                if (j < s + old_length)
1,747✔
456
                        j = utf8_next_char(j);
1,747✔
UNCOV
457
                else if (i > s)
×
UNCOV
458
                        i = utf8_prev_char(i);
×
459
        }
460

461
        len = i - s;
1,833✔
462
        len2 = s + old_length - j;
1,833✔
463

464
        /* If we have ANSI, allow the same length as the source string + ellipsis. It'd be too involved to
465
         * figure out what exact space is needed. Strings with ANSI sequences are most likely to be fairly
466
         * short anyway. */
467
        size_t alloc_len = has_ansi_seq ? old_length + 3 + 1 : len + 3 + len2 + 1;
1,833✔
468

469
        char *e = new(char, alloc_len);
1,833✔
470
        if (!e)
1,833✔
471
                return NULL;
472

473
        memcpy_safe(e, s, len);
1,833✔
474
        write_ellipsis(e + len, /* unicode= */ true);
1,833✔
475

476
        char *dst = e + len + 3;
1,833✔
477

478
        if (has_ansi_seq)
1,833✔
479
                /* Copy over any ANSI sequences in full */
480
                for (const char *p = s + len; p < j; ) {
1,864✔
481
                        size_t slen = ansi_sequence_length(p, j - p);
1,729✔
482
                        if (slen > 0) {
1,729✔
483
                                dst = mempcpy(dst, p, slen);
262✔
484
                                p += slen;
262✔
485
                        } else
486
                                p = utf8_next_char(p);
1,467✔
487
                }
488

489
        memcpy_safe(dst, j, len2);
1,833✔
490
        dst[len2] = '\0';
1,833✔
491

492
        return e;
1,833✔
493
}
494

495
char* cellescape(char *buf, size_t len, const char *s) {
57,097✔
496
        /* Escape and ellipsize s into buffer buf of size len. Only non-control ASCII
497
         * characters are copied as they are, everything else is escaped. The result
498
         * is different then if escaping and ellipsization was performed in two
499
         * separate steps, because each sequence is either stored in full or skipped.
500
         *
501
         * This function should be used for logging about strings which expected to
502
         * be plain ASCII in a safe way.
503
         *
504
         * An ellipsis will be used if s is too long. It was always placed at the
505
         * very end.
506
         */
507

508
        size_t i = 0, last_char_width[4] = {}, k = 0;
57,097✔
509

510
        assert(buf);
57,097✔
511
        assert(len > 0); /* at least a terminating NUL */
57,097✔
512
        assert(s);
57,097✔
513

514
        for (;;) {
1,222,571✔
515
                char four[4];
639,834✔
516
                int w;
639,834✔
517

518
                if (*s == 0) /* terminating NUL detected? then we are done! */
639,834✔
519
                        goto done;
57,061✔
520

521
                w = cescape_char(*s, four);
582,773✔
522
                if (i + w + 1 > len) /* This character doesn't fit into the buffer anymore? In that case let's
582,773✔
523
                                      * ellipsize at the previous location */
524
                        break;
525

526
                /* OK, there was space, let's add this escaped character to the buffer */
527
                memcpy(buf + i, four, w);
582,737✔
528
                i += w;
582,737✔
529

530
                /* And remember its width in the ring buffer */
531
                last_char_width[k] = w;
582,737✔
532
                k = (k + 1) % 4;
582,737✔
533

534
                s++;
582,737✔
535
        }
536

537
        /* Ellipsation is necessary. This means we might need to truncate the string again to make space for 4
538
         * characters ideally, but the buffer is shorter than that in the first place take what we can get */
539
        for (size_t j = 0; j < ELEMENTSOF(last_char_width); j++) {
81✔
540

541
                if (i + 4 <= len) /* nice, we reached our space goal */
81✔
542
                        break;
543

544
                k = k == 0 ? 3 : k - 1;
57✔
545
                if (last_char_width[k] == 0) /* bummer, we reached the beginning of the strings */
57✔
546
                        break;
547

548
                assert(i >= last_char_width[k]);
45✔
549
                i -= last_char_width[k];
45✔
550
        }
551

552
        if (i + 4 <= len) /* yay, enough space */
36✔
553
                i += write_ellipsis(buf + i, /* unicode= */ false);
24✔
554
        else if (i + 3 <= len) { /* only space for ".." */
12✔
555
                buf[i++] = '.';
4✔
556
                buf[i++] = '.';
4✔
557
        } else if (i + 2 <= len) /* only space for a single "." */
8✔
558
                buf[i++] = '.';
4✔
559
        else
560
                assert(i + 1 <= len);
4✔
561

562
done:
4✔
563
        buf[i] = '\0';
57,097✔
564
        return buf;
57,097✔
565
}
566

567
char* strshorten(char *s, size_t l) {
313,184✔
568
        assert(s);
313,184✔
569

570
        if (l >= SIZE_MAX-1) /* Would not change anything */
313,184✔
571
                return s;
572

573
        if (strnlen(s, l+1) > l)
313,182✔
574
                s[l] = 0;
114✔
575

576
        return s;
577
}
578

579
int strgrowpad0(char **s, size_t l) {
26✔
580
        size_t sz;
26✔
581

582
        assert(s);
26✔
583

584
        if (*s) {
26✔
585
                sz = strlen(*s) + 1;
26✔
586
                if (sz >= l) /* never shrink */
26✔
587
                        return 0;
588
        } else
589
                sz = 0;
590

591
        char *q = realloc(*s, l);
26✔
592
        if (!q)
26✔
593
                return -ENOMEM;
594

595
        *s = q;
26✔
596

597
        memzero(*s + sz, l - sz);
26✔
598
        return 0;
599
}
600

601
char* strreplace(const char *text, const char *old_string, const char *new_string) {
7,470✔
602
        size_t l, old_len, new_len;
7,470✔
603
        char *t, *ret = NULL;
7,470✔
604
        const char *f;
7,470✔
605

606
        assert(old_string);
7,470✔
607
        assert(new_string);
7,470✔
608

609
        if (!text)
7,470✔
610
                return NULL;
7,470✔
611

612
        old_len = strlen(old_string);
7,469✔
613
        new_len = strlen(new_string);
7,469✔
614

615
        l = strlen(text);
7,469✔
616
        if (!GREEDY_REALLOC(ret, l+1))
7,469✔
617
                return NULL;
618

619
        f = text;
7,469✔
620
        t = ret;
7,469✔
621
        while (*f) {
241,923✔
622
                size_t d, nl;
234,454✔
623

624
                if (!startswith(f, old_string)) {
234,454✔
625
                        *(t++) = *(f++);
232,090✔
626
                        continue;
232,090✔
627
                }
628

629
                d = t - ret;
2,364✔
630
                nl = l - old_len + new_len;
2,364✔
631

632
                if (!GREEDY_REALLOC(ret, nl + 1))
2,364✔
UNCOV
633
                        return mfree(ret);
×
634

635
                l = nl;
2,364✔
636
                t = ret + d;
2,364✔
637

638
                t = stpcpy(t, new_string);
2,364✔
639
                f += old_len;
2,364✔
640
        }
641

642
        *t = 0;
7,469✔
643
        return ret;
7,469✔
644
}
645

646
static void advance_offsets(
406✔
647
                ssize_t diff,
648
                size_t offsets[2], /* note: we can't use [static 2] here, since this may be NULL */
649
                size_t shift[static 2],
650
                size_t size) {
651

652
        if (!offsets)
406✔
653
                return;
654

655
        assert(shift);
396✔
656

657
        if ((size_t) diff < offsets[0])
396✔
UNCOV
658
                shift[0] += size;
×
659
        if ((size_t) diff < offsets[1])
396✔
UNCOV
660
                shift[1] += size;
×
661
}
662

663
char* strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
255,853✔
664
        const char *begin = NULL;
255,853✔
665
        enum {
255,853✔
666
                STATE_OTHER,
667
                STATE_ESCAPE,
668
                STATE_CSI,
669
                STATE_OSC,
670
                STATE_OSC_CLOSING,
671
        } state = STATE_OTHER;
255,853✔
672
        _cleanup_(memstream_done) MemStream m = {};
255,853✔
673
        size_t isz, shift[2] = {}, n_carriage_returns = 0;
255,853✔
674
        FILE *f;
255,853✔
675

676
        assert(ibuf);
255,853✔
677
        assert(*ibuf);
255,853✔
678
        POINTER_MAY_BE_NULL(_isz);
255,853✔
679

680
        /* This does three things:
681
         *
682
         * 1. Replaces TABs by 8 spaces
683
         * 2. Strips ANSI color sequences (a subset of CSI), i.e. ESC '[' … 'm' sequences
684
         * 3. Strips ANSI operating system sequences (OSC), i.e. ESC ']' … ST sequences
685
         * 4. Strip trailing \r characters (since they would "move the cursor", but have no
686
         *    other effect).
687
         *
688
         * Everything else will be left as it is. In particular other ANSI sequences are left as they are, as
689
         * are any other special characters. Truncated ANSI sequences are left-as is too. This call is
690
         * supposed to suppress the most basic formatting noise, but nothing else.
691
         *
692
         * Why care for OSC sequences? Well, to undo what terminal_urlify() and friends generate. */
693

694
        isz = _isz ? *_isz : strlen(*ibuf);
255,853✔
695

696
        /* Note we turn off internal locking on f for performance reasons. It's safe to do so since we
697
         * created f here and it doesn't leave our scope. */
698
        f = memstream_init(&m);
255,853✔
699
        if (!f)
255,853✔
700
                return NULL;
701

702
        for (const char *i = *ibuf; i < *ibuf + isz + 1; i++) {
30,327,294✔
703

704
                bool eot = i >= *ibuf + isz;
30,071,441✔
705

706
                switch (state) {
30,071,441✔
707

708
                case STATE_OTHER:
30,071,190✔
709
                        if (eot)
30,071,190✔
710
                                break;
711

712
                        if (*i == '\r') {
29,815,337✔
713
                                n_carriage_returns++;
7✔
714
                                break;
7✔
715
                        } else if (*i == '\n')
29,815,330✔
716
                                /* Ignore carriage returns before new line */
717
                                n_carriage_returns = 0;
41✔
718
                        for (; n_carriage_returns > 0; n_carriage_returns--)
29,815,332✔
719
                                fputc('\r', f);
2✔
720

721
                        if (*i == '\x1B')
29,815,330✔
722
                                state = STATE_ESCAPE;
723
                        else if (*i == '\t') {
29,815,296✔
724
                                fputs("        ", f);
401✔
725
                                advance_offsets(i - *ibuf, highlight, shift, 7);
401✔
726
                        } else
727
                                fputc(*i, f);
29,814,895✔
728

729
                        break;
730

731
                case STATE_ESCAPE:
34✔
732
                        assert(n_carriage_returns == 0);
34✔
733

734
                        if (eot) {
34✔
UNCOV
735
                                fputc('\x1B', f);
×
UNCOV
736
                                advance_offsets(i - *ibuf, highlight, shift, 1);
×
737
                                break;
738
                        } else if (*i == '[') { /* ANSI CSI */
34✔
739
                                state = STATE_CSI;
31✔
740
                                begin = i + 1;
31✔
741
                        } else if (*i == ']') { /* ANSI OSC */
3✔
742
                                state = STATE_OSC;
3✔
743
                                begin = i + 1;
3✔
744
                        } else {
UNCOV
745
                                fputc('\x1B', f);
×
UNCOV
746
                                fputc(*i, f);
×
UNCOV
747
                                advance_offsets(i - *ibuf, highlight, shift, 1);
×
UNCOV
748
                                state = STATE_OTHER;
×
749
                        }
750

751
                        break;
752

753
                case STATE_CSI:
191✔
754
                        assert(n_carriage_returns == 0);
191✔
755

756
                        if (eot || !strchr(DIGITS ";:m", *i)) { /* EOT or invalid chars in sequence */
191✔
757
                                fputc('\x1B', f);
5✔
758
                                fputc('[', f);
5✔
759
                                advance_offsets(i - *ibuf, highlight, shift, 2);
5✔
760
                                state = STATE_OTHER;
5✔
761
                                i = begin-1;
5✔
762
                        } else if (*i == 'm')
186✔
763
                                state = STATE_OTHER;
26✔
764

765
                        break;
766

767
                case STATE_OSC:
24✔
768
                        assert(n_carriage_returns == 0);
24✔
769

770
                        /* There are three kinds of OSC terminators: \x07, \x1b\x5c or \x9c. We only support
771
                         * the first two, because the last one is a valid UTF-8 codepoint and hence creates
772
                         * an ambiguity (many Terminal emulators refuse to support it as well). */
773
                        if (eot || (!IN_SET(*i, '\x07', '\x1b') && !osc_char_is_valid(*i))) { /* EOT or invalid chars in sequence */
24✔
UNCOV
774
                                fputc('\x1B', f);
×
UNCOV
775
                                fputc(']', f);
×
UNCOV
776
                                advance_offsets(i - *ibuf, highlight, shift, 2);
×
UNCOV
777
                                state = STATE_OTHER;
×
UNCOV
778
                                i = begin-1;
×
779
                        } else if (*i == '\x07') /* Single character ST */
24✔
780
                                state = STATE_OTHER;
781
                        else if (*i == '\x1B')
23✔
782
                                state = STATE_OSC_CLOSING;
2✔
783

784
                        break;
785

786
                case STATE_OSC_CLOSING:
2✔
787
                        if (eot || *i != '\x5c') { /* EOT or incomplete two-byte ST in sequence */
2✔
UNCOV
788
                                fputc('\x1B', f);
×
UNCOV
789
                                fputc(']', f);
×
UNCOV
790
                                advance_offsets(i - *ibuf, highlight, shift, 2);
×
UNCOV
791
                                state = STATE_OTHER;
×
UNCOV
792
                                i = begin-1;
×
793
                        } else if (*i == '\x5c')
794
                                state = STATE_OTHER;
795

796
                        break;
797
                }
798
        }
799

800
        char *obuf;
255,853✔
801
        if (memstream_finalize(&m, &obuf, _isz) < 0)
255,853✔
802
                return NULL;
803

804
        free_and_replace(*ibuf, obuf);
255,853✔
805

806
        if (highlight) {
255,853✔
807
                highlight[0] += shift[0];
255,826✔
808
                highlight[1] += shift[1];
255,826✔
809
        }
810

811
        return *ibuf;
255,853✔
812
}
813

814
char* strextendv_with_separator(char **x, const char *separator, va_list ap) {
32,641,945✔
815
        _cleanup_free_ char *buffer = NULL;
32,641,945✔
816
        size_t f, l, l_separator;
32,641,945✔
817
        bool need_separator;
32,641,945✔
818
        char *nr, *p;
32,641,945✔
819

820
        if (!x)
32,641,945✔
821
                x = &buffer;
30,458,243✔
822

823
        l = f = strlen_ptr(*x);
32,641,945✔
824

825
        need_separator = !isempty(*x);
32,641,945✔
826
        l_separator = strlen_ptr(separator);
32,641,945✔
827

828
        va_list aq;
32,641,945✔
829
        va_copy(aq, ap);
32,641,945✔
830
        for (const char *t;;) {
123,096,196✔
831
                size_t n;
123,096,196✔
832

833
                t = va_arg(aq, const char *);
123,096,196✔
834
                if (!t)
123,096,196✔
835
                        break;
836
                if (t == POINTER_MAX)
90,454,251✔
837
                        continue;
3✔
838

839
                n = strlen(t);
90,454,248✔
840

841
                if (need_separator)
90,454,248✔
842
                        n += l_separator;
59,050,183✔
843

844
                if (n >= SIZE_MAX - l) {
90,454,248✔
UNCOV
845
                        va_end(aq);
×
UNCOV
846
                        return NULL;
×
847
                }
848

849
                l += n;
90,454,248✔
850
                need_separator = true;
90,454,248✔
851
        }
852
        va_end(aq);
32,641,945✔
853

854
        need_separator = !isempty(*x);
32,641,945✔
855

856
        nr = realloc(*x, GREEDY_ALLOC_ROUND_UP(l+1));
32,641,945✔
857
        if (!nr)
32,641,945✔
858
                return NULL;
859

860
        *x = nr;
32,641,945✔
861
        p = nr + f;
32,641,945✔
862

863
        for (;;) {
123,096,196✔
864
                const char *t;
123,096,196✔
865

866
                t = va_arg(ap, const char *);
123,096,196✔
867
                if (!t)
123,096,196✔
868
                        break;
869
                if (t == POINTER_MAX)
90,454,251✔
870
                        continue;
3✔
871

872
                if (need_separator && separator)
90,454,248✔
873
                        p = stpcpy(p, separator);
169,745✔
874

875
                p = stpcpy(p, t);
90,454,248✔
876

877
                need_separator = true;
90,454,248✔
878
        }
879

880
        assert(p == nr + l);
32,641,945✔
881
        *p = 0;
32,641,945✔
882

883
        /* If no buffer to extend was passed in return the start of the buffer */
884
        if (buffer)
32,641,945✔
885
                return TAKE_PTR(buffer);
30,458,243✔
886

887
        /* Otherwise we extended the buffer: return the end */
888
        return p;
889
}
890

891
char* strextend_with_separator_internal(char **x, const char *separator, ...) {
32,580,215✔
892
        va_list ap;
32,580,215✔
893
        char *ret;
32,580,215✔
894

895
        va_start(ap, separator);
32,580,215✔
896
        ret = strextendv_with_separator(x, separator, ap);
32,580,215✔
897
        va_end(ap);
32,580,215✔
898

899
        return ret;
32,580,215✔
900
}
901

902
int strextendf_with_separator(char **x, const char *separator, const char *format, ...) {
25,162✔
903
        size_t m, a, l_separator;
25,162✔
904
        va_list ap;
25,162✔
905
        int l;
25,162✔
906

907
        /* Appends a formatted string to the specified string. Don't use this in inner loops, since then
908
         * we'll spend a tonload of time in determining the length of the string passed in, over and over
909
         * again. */
910

911
        assert(x);
25,162✔
912
        assert(format);
25,162✔
913

914
        l_separator = isempty(*x) ? 0 : strlen_ptr(separator);
43,193✔
915

916
        /* Let's try to use the allocated buffer, if there's room at the end still. Otherwise let's extend by 64 chars. */
917
        if (*x) {
25,162✔
918
                m = strlen(*x);
18,031✔
919
                a = MALLOC_SIZEOF_SAFE(*x);
18,031✔
920
                assert(a >= m + 1);
18,031✔
921
        } else
922
                m = a = 0;
923

924
        if (a - m < 17 + l_separator) { /* if there's less than 16 chars space, then enlarge the buffer first */
25,162✔
925
                char *n;
13,903✔
926

927
                if (_unlikely_(l_separator > SIZE_MAX - 64)) /* overflow check #1 */
13,903✔
928
                        return -ENOMEM;
25,162✔
929
                if (_unlikely_(m > SIZE_MAX - 64 - l_separator)) /* overflow check #2 */
13,903✔
930
                        return -ENOMEM;
931

932
                n = realloc(*x, m + 64 + l_separator);
13,903✔
933
                if (!n)
13,903✔
934
                        return -ENOMEM;
935

936
                *x = n;
13,903✔
937
                a = MALLOC_SIZEOF_SAFE(*x);
13,903✔
938
        }
939

940
        /* Now, let's try to format the string into it */
941
        memcpy_safe(*x + m, separator, l_separator);
25,162✔
942
        va_start(ap, format);
25,162✔
943
        l = vsnprintf(*x + m + l_separator, a - m - l_separator, format, ap);
25,162✔
944
        va_end(ap);
25,162✔
945

946
        assert(l >= 0);
25,162✔
947

948
        if ((size_t) l < a - m - l_separator) {
25,162✔
949
                char *n;
24,657✔
950

951
                /* Nice! This worked. We are done. But first, let's return the extra space we don't
952
                 * need. This should be a cheap operation, since we only lower the allocation size here,
953
                 * never increase. */
954
                n = realloc(*x, m + (size_t) l + l_separator + 1);
24,657✔
955
                if (n)
24,657✔
956
                        *x = n;
24,657✔
957
        } else {
958
                char *n;
505✔
959

960
                /* Wasn't enough. Then let's allocate exactly what we need. */
961

962
                if (_unlikely_((size_t) l > SIZE_MAX - (l_separator + 1))) /* overflow check #1 */
505✔
UNCOV
963
                        goto oom;
×
964
                if (_unlikely_(m > SIZE_MAX - ((size_t) l + l_separator + 1))) /* overflow check #2 */
505✔
UNCOV
965
                        goto oom;
×
966

967
                a = m + (size_t) l + l_separator + 1;
505✔
968
                n = realloc(*x, a);
505✔
969
                if (!n)
505✔
UNCOV
970
                        goto oom;
×
971
                *x = n;
505✔
972

973
                va_start(ap, format);
505✔
974
                l = vsnprintf(*x + m + l_separator, a - m - l_separator, format, ap);
505✔
975
                va_end(ap);
505✔
976

977
                assert((size_t) l < a - m - l_separator);
505✔
978
        }
979

980
        return 0;
981

UNCOV
982
oom:
×
983
        /* truncate the bytes added after memcpy_safe() again */
UNCOV
984
        (*x)[m] = 0;
×
UNCOV
985
        return -ENOMEM;
×
986
}
987

988
char* strrep(const char *s, size_t n) {
14✔
989
        char *ret, *p;
14✔
990
        size_t l;
14✔
991

992
        assert(s);
14✔
993

994
        l = strlen(s);
14✔
995
        if (!MUL_ASSIGN_SAFE(&l, n))
14✔
996
                return NULL;
14✔
997
        if (!INC_SAFE(&l, 1))
13✔
998
                return NULL;
999

1000
        p = ret = malloc(l);
13✔
1001
        if (!ret)
13✔
1002
                return NULL;
1003

1004
        for (size_t i = 0; i < n; i++)
40,850✔
1005
                p = stpcpy(p, s);
40,837✔
1006

1007
        *p = 0;
13✔
1008
        return ret;
13✔
1009
}
1010

1011
int split_pair(const char *s, const char *sep, char **ret_first, char **ret_second) {
27,470✔
1012
        assert(s);
27,470✔
1013
        assert(!isempty(sep));
27,470✔
1014

1015
        const char *x = strstr(s, sep);
27,470✔
1016
        if (!x)
27,470✔
1017
                return -EINVAL;
27,470✔
1018

1019
        _cleanup_free_ char *a = NULL;
26,028✔
1020
        if (ret_first) {
26,028✔
1021
                a = strndup(s, x - s);
26,026✔
1022
                if (!a)
26,026✔
1023
                        return -ENOMEM;
1024
        }
1025

1026
        _cleanup_free_ char *b = NULL;
26,028✔
1027
        if (ret_second) {
26,028✔
1028
                b = strdup(x + strlen(sep));
26,026✔
1029
                if (!b)
26,026✔
1030
                        return -ENOMEM;
1031
        }
1032

1033
        if (ret_first)
26,028✔
1034
                *ret_first = TAKE_PTR(a);
26,026✔
1035
        if (ret_second)
26,028✔
1036
                *ret_second = TAKE_PTR(b);
26,026✔
1037
        return 0;
1038
}
1039

1040
int free_and_strdup(char **p, const char *s) {
2,437,076✔
1041
        char *t;
2,437,076✔
1042

1043
        assert(p);
2,437,076✔
1044

1045
        /* Replaces a string pointer with a strdup()ed new string,
1046
         * possibly freeing the old one. */
1047

1048
        if (streq_ptr(*p, s))
2,437,076✔
1049
                return 0;
2,437,076✔
1050

1051
        if (s) {
1,960,549✔
1052
                t = strdup(s);
1,952,271✔
1053
                if (!t)
1,952,271✔
1054
                        return -ENOMEM;
1055
        } else
1056
                t = NULL;
1057

1058
        free_and_replace(*p, t);
1,960,549✔
1059

1060
        return 1;
1,960,549✔
1061
}
1062

1063
int free_and_strdup_warn(char **p, const char *s) {
104,044✔
1064
        int r;
104,044✔
1065

1066
        r = free_and_strdup(p, s);
104,044✔
1067
        if (r < 0)
104,044✔
UNCOV
1068
                return log_oom();
×
1069
        return r;
1070
}
1071

1072
int free_and_strndup(char **p, const char *s, size_t l) {
79,134✔
1073
        char *t;
79,134✔
1074

1075
        assert(p);
79,134✔
1076
        assert(s || l == 0);
79,134✔
1077

1078
        /* Replaces a string pointer with a strndup()ed new string,
1079
         * freeing the old one. */
1080

1081
        if (!*p && !s)
79,134✔
1082
                return 0;
79,134✔
1083

1084
        if (*p && s && strneq(*p, s, l) && (l > strlen(*p) || (*p)[l] == '\0'))
79,134✔
1085
                return 0;
1086

1087
        if (s) {
37,136✔
1088
                t = strndup(s, l);
37,207✔
1089
                if (!t)
37,207✔
1090
                        return -ENOMEM;
1091
        } else
1092
                t = NULL;
1093

1094
        free_and_replace(*p, t);
37,208✔
1095
        return 1;
37,208✔
1096
}
1097

1098
int strdup_to_full(char **ret, const char *src) {
882,381✔
1099
        if (!src) {
882,381✔
1100
                if (ret)
324,444✔
1101
                        *ret = NULL;
324,443✔
1102

1103
                return 0;
1104
        } else {
1105
                if (ret) {
557,937✔
1106
                        char *t = strdup(src);
546,026✔
1107
                        if (!t)
546,026✔
1108
                                return -ENOMEM;
1109
                        *ret = t;
546,026✔
1110
                }
1111

1112
                return 1;
1113
        }
1114
};
1115

1116
bool string_is_safe(const char *p, StringSafeFlags flags) {
14,217,669✔
1117

1118
        /* Baseline checks are:
1119
         *   • No control characters (i.e. 0…31 + 127)
1120
         *   • UTF-8 valid (well, technically we skip this test if STRING_ASCII is set, since that is a tighter test)
1121
         */
1122

1123
        if (FLAGS_SET(flags, STRING_ALLOW_EMPTY) ? !p : isempty(p))
14,217,669✔
1124
                return false;
1125

1126
        if (!FLAGS_SET(flags, STRING_ASCII) && !utf8_is_valid(p))
28,434,246✔
1127
                return false;
1128

1129
        for (const char *t = p; *t; t++) {
265,578,154✔
1130
                /* never allow control characters, except for new line */
1131
                if ((*t > 0 && *t < ' ' && *t != '\n') || *t == 0x7f)
251,360,577✔
1132
                        return false;
1133

1134
                if (!FLAGS_SET(flags, STRING_ALLOW_NEWLINES) && *t == '\n')
251,360,552✔
1135
                        return false;
1136

1137
                if (!FLAGS_SET(flags, STRING_ALLOW_BACKSLASHES) && *t == '\\')
251,360,543✔
1138
                        return false;
1139

1140
                if (!FLAGS_SET(flags, STRING_ALLOW_QUOTES) && strchr(QUOTES, *t))
251,360,534✔
1141
                        return false;
1142

1143
                if (!FLAGS_SET(flags, STRING_ALLOW_GLOBS) && strchr(GLOB_CHARS, *t))
251,360,523✔
1144
                        return false;
1145

1146
                if (FLAGS_SET(flags, STRING_DISALLOW_WHITESPACE) && strchr(WHITESPACE, *t))
251,360,515✔
1147
                        return false;
1148

1149
                if (FLAGS_SET(flags, STRING_ASCII) && (uint8_t) *t >= 0x80)
251,360,510✔
1150
                        return false;
1151
        }
1152

1153
        if (FLAGS_SET(flags, STRING_FILENAME) && !filename_is_valid(p))
14,217,577✔
1154
                return false;
1155

1156
        if (FLAGS_SET(flags, STRING_FILENAME_PART) && !filename_part_is_valid(p))
14,217,565✔
1157
                return false;
23✔
1158

1159
        return true;
1160
}
1161

1162
char* str_realloc(char *p) {
58,393✔
1163
        /* Reallocate *p to actual size. Ignore failure, and return the original string on error. */
1164

1165
        if (!p)
58,393✔
1166
                return NULL;
1167

1168
        return realloc(p, strlen(p) + 1) ?: p;
58,393✔
1169
}
1170

1171
char* string_erase(char *x) {
126✔
1172
        if (!x)
126✔
1173
                return NULL;
1174

1175
        /* A delicious drop of snake-oil! To be called on memory where we stored passphrases or so, after we
1176
         * used them. */
1177
        explicit_bzero_safe(x, strlen(x));
126✔
1178
        return x;
126✔
1179
}
1180

1181
int string_truncate_lines(const char *s, size_t n_lines, char **ret) {
300✔
1182
        const char *p = s, *e = s;
300✔
1183
        bool truncation_applied = false;
300✔
1184
        char *copy;
300✔
1185
        size_t n = 0;
300✔
1186

1187
        assert(s);
300✔
1188
        assert(ret);
300✔
1189

1190
        /* Truncate after the specified number of lines. Returns > 0 if a truncation was applied or == 0 if
1191
         * there were fewer lines in the string anyway. Trailing newlines on input are ignored, and not
1192
         * generated either. */
1193

1194
        for (;;) {
530✔
1195
                size_t k;
415✔
1196

1197
                k = strcspn(p, "\n");
415✔
1198

1199
                if (p[k] == 0) {
415✔
1200
                        if (k == 0) /* final empty line */
268✔
1201
                                break;
1202

1203
                        if (n >= n_lines) /* above threshold */
243✔
1204
                                break;
1205

1206
                        e = p + k; /* last line to include */
226✔
1207
                        break;
226✔
1208
                }
1209

1210
                assert(p[k] == '\n');
147✔
1211

1212
                if (n >= n_lines)
147✔
1213
                        break;
1214

1215
                if (k > 0)
115✔
1216
                        e = p + k;
92✔
1217

1218
                p += k + 1;
115✔
1219
                n++;
115✔
1220
        }
1221

1222
        /* e points after the last character we want to keep */
1223
        if (isempty(e))
300✔
1224
                copy = strdup(s);
230✔
1225
        else {
1226
                if (!in_charset(e, "\n")) /* We only consider things truncated if we remove something that
70✔
1227
                                           * isn't a new-line or a series of them */
1228
                        truncation_applied = true;
42✔
1229

1230
                copy = strndup(s, e - s);
70✔
1231
        }
1232
        if (!copy)
300✔
1233
                return -ENOMEM;
1234

1235
        *ret = copy;
300✔
1236
        return truncation_applied;
300✔
1237
}
1238

1239
int string_extract_line(const char *s, size_t i, char **ret) {
234,272✔
1240
        const char *p = s;
234,272✔
1241
        size_t c = 0;
234,272✔
1242

1243
        assert(ret);
234,272✔
1244

1245
        /* Extract the i'nth line from the specified string. Returns > 0 if there are more lines after that,
1246
         * and == 0 if we are looking at the last line or already beyond the last line. As special
1247
         * optimization, if the first line is requested and the string only consists of one line we return
1248
         * NULL, indicating the input string should be used as is, and avoid a memory allocation for a very
1249
         * common case. */
1250

1251
        for (;;) {
422,034✔
1252
                const char *q;
328,153✔
1253

1254
                q = strchr(p, '\n');
328,153✔
1255
                if (i == c) {
328,153✔
1256
                        /* The line we are looking for! */
1257

1258
                        if (q) {
230,490✔
1259
                                char *m;
3,913✔
1260

1261
                                m = strndup(p, q - p);
3,913✔
1262
                                if (!m)
3,913✔
1263
                                        return -ENOMEM;
1264

1265
                                *ret = m;
3,913✔
1266
                                return !isempty(q + 1); /* More coming? */
7,826✔
1267
                        } else
1268
                                /* Tell the caller to use the input string if equal */
1269
                                return strdup_to(ret, p != s ? p : NULL);
451,324✔
1270
                }
1271

1272
                if (!q)
97,663✔
1273
                        /* No more lines, return empty line */
1274
                        return strdup_to(ret, "");
3,782✔
1275

1276
                p = q + 1;
93,881✔
1277
                c++;
93,881✔
1278
        }
1279
}
1280

1281
int string_contains_word_strv(const char *string, const char *separators, char * const *words, const char **ret_word) {
75✔
1282
        /* In the default mode with no separators specified, we split on whitespace and coalesce separators. */
1283
        const ExtractFlags flags = separators ? EXTRACT_DONT_COALESCE_SEPARATORS : 0;
75✔
1284
        const char *found = NULL;
75✔
1285
        int r;
202✔
1286

1287
        for (;;) {
329✔
1288
                _cleanup_free_ char *w = NULL;
127✔
1289

1290
                r = extract_first_word(&string, &w, separators, flags);
202✔
1291
                if (r < 0)
202✔
UNCOV
1292
                        return r;
×
1293
                if (r == 0)
202✔
1294
                        break;
1295

1296
                found = strv_find(words, w);
148✔
1297
                if (found)
148✔
1298
                        break;
1299
        }
1300

1301
        if (ret_word)
75✔
1302
                *ret_word = found;
7✔
1303
        return !!found;
75✔
1304
}
1305

1306
bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok) {
8,986✔
1307
        if (!s1 && !s2)
8,986✔
1308
                return true;
1309
        if (!s1 || !s2)
8,985✔
1310
                return false;
1311

1312
        if (!ok)
8,983✔
1313
                ok = WHITESPACE;
13✔
1314

1315
        for (; *s1 && *s2; s1++, s2++)
17,429✔
1316
                if (*s1 != *s2)
10,698✔
1317
                        break;
1318

1319
        return in_charset(s1, ok) && in_charset(s2, ok);
11,241✔
1320
}
1321

1322
char* string_replace_char(char *str, char old_char, char new_char) {
640,271✔
1323
        assert(str);
640,271✔
1324
        assert(old_char != '\0');
640,271✔
1325
        assert(new_char != '\0');
640,271✔
1326
        assert(old_char != new_char);
640,271✔
1327

1328
        for (char *p = strchr(str, old_char); p; p = strchr(p + 1, old_char))
646,756✔
1329
                *p = new_char;
6,485✔
1330

1331
        return str;
640,271✔
1332
}
1333

1334
int make_cstring(const void *s, size_t n, MakeCStringMode mode, char **ret) {
10,249✔
1335
        char *b;
10,249✔
1336

1337
        assert(s || n == 0);
10,249✔
1338
        assert(mode >= 0);
10,249✔
1339
        assert(mode < _MAKE_CSTRING_MODE_MAX);
10,249✔
1340

1341
        /* Converts a sized character buffer into a NUL-terminated NUL string, refusing if there are embedded
1342
         * NUL bytes. Whether to expect a trailing NUL byte can be specified via 'mode' */
1343

1344
        if (n == 0) {
10,249✔
1345
                if (mode == MAKE_CSTRING_REQUIRE_TRAILING_NUL)
20✔
1346
                        return -EINVAL;
1347

1348
                if (!ret)
19✔
1349
                        return 0;
1350

1351
                b = new0(char, 1);
19✔
1352
        } else {
1353
                const uint8_t *nul;
10,229✔
1354

1355
                nul = memchr(s, 0, n);
10,229✔
1356
                if (nul) {
10,229✔
1357
                        if (nul < (const uint8_t*) s + n - 1 || /* embedded NUL? */
29✔
1358
                            mode == MAKE_CSTRING_REFUSE_TRAILING_NUL)
1359
                                return -EINVAL;
1360

1361
                        n--;
1362
                } else if (mode == MAKE_CSTRING_REQUIRE_TRAILING_NUL)
10,200✔
1363
                        return -EINVAL;
1364

1365
                if (!ret)
10,218✔
1366
                        return 0;
1367

1368
                b = memdup_suffix0(s, n);
10,218✔
1369
        }
1370
        if (!b)
10,237✔
1371
                return -ENOMEM;
1372

1373
        *ret = b;
10,237✔
1374
        return 0;
10,237✔
1375
}
1376

1377
size_t strspn_from_end(const char *str, const char *accept) {
604,317✔
1378
        size_t n = 0;
604,317✔
1379

1380
        if (isempty(str))
604,317✔
1381
                return 0;
1382

1383
        if (isempty(accept))
604,314✔
1384
                return 0;
1385

1386
        for (const char *p = str + strlen(str); p > str && strchr(accept, p[-1]); p--)
1,166,832✔
1387
                n++;
562,519✔
1388

1389
        return n;
1390
}
1391

UNCOV
1392
char* strdupspn(const char *a, const char *accept) {
×
1393
        if (isempty(a) || isempty(accept))
×
UNCOV
1394
                return strdup("");
×
1395

UNCOV
1396
        return strndup(a, strspn(a, accept));
×
1397
}
1398

1399
char* strdupcspn(const char *a, const char *reject) {
42,542✔
1400
        if (isempty(a))
42,542✔
UNCOV
1401
                return strdup("");
×
1402
        if (isempty(reject))
42,542✔
UNCOV
1403
                return strdup(a);
×
1404

1405
        return strndup(a, strcspn(a, reject));
42,542✔
1406
}
1407

1408
char* find_line_startswith_internal(const char *haystack, const char *needle) {
6,678✔
1409
        assert(haystack);
6,678✔
1410
        assert(needle);
6,678✔
1411

1412
        /* Finds the first line in 'haystack' that starts with the specified string. Returns a pointer to the
1413
         * first character after it */
1414

1415
        char *p = (char*) strstr(haystack, needle);
6,678✔
1416
        if (!p)
6,678✔
1417
                return NULL;
1418

1419
        if (p > haystack)
1,433✔
1420
                while (p[-1] != '\n') {
116✔
1421
                        p = strstr(p + 1, needle);
9✔
1422
                        if (!p)
9✔
1423
                                return NULL;
1424
                }
1425

1426
        return p + strlen(needle);
1,432✔
1427
}
1428

1429
char* find_line_internal(const char *haystack, const char *needle) {
×
UNCOV
1430
        assert(haystack);
×
UNCOV
1431
        assert(needle);
×
1432

1433
        /* Finds the first line in 'haystack' that match the specified string. Returns a pointer to the
1434
         * beginning of the line */
1435

UNCOV
1436
        char *p = (char*) find_line_startswith(haystack, needle);
×
UNCOV
1437
        if (!p)
×
1438
                return NULL;
1439

1440
        if (*p == 0 || strchr(NEWLINE, *p))
×
UNCOV
1441
                return p - strlen(needle);
×
1442

1443
        return NULL;
1444
}
1445

1446
char* find_line_after_internal(const char *haystack, const char *needle) {
×
UNCOV
1447
        assert(haystack);
×
UNCOV
1448
        assert(needle);
×
1449

1450
        /* Finds the first line in 'haystack' that match the specified string. Returns a pointer to the
1451
         * next line after it */
1452

UNCOV
1453
        char *p = (char*) find_line_startswith(haystack, needle);
×
UNCOV
1454
        if (!p)
×
1455
                return NULL;
1456

UNCOV
1457
        if (*p == 0)
×
1458
                return p;
UNCOV
1459
        if (strchr(NEWLINE, *p))
×
UNCOV
1460
                return p + 1;
×
1461

1462
        return NULL;
1463
}
1464

1465
bool version_is_valid(const char *s) {
73,935✔
1466
        if (isempty(s))
73,935✔
1467
                return false;
1468

1469
        if (!filename_part_is_valid(s))
73,933✔
1470
                return false;
1471

1472
        /* This is a superset of the characters used by semver. We additionally allow "," and "_". */
1473
        if (!in_charset(s, ALPHANUMERICAL ".,_-+"))
73,933✔
UNCOV
1474
                return false;
×
1475

1476
        return true;
1477
}
1478

1479
bool version_is_valid_versionspec(const char *s) {
97✔
1480
        if (!filename_part_is_valid(s))
97✔
1481
                return false;
1482

1483
        if (!in_charset(s, ALPHANUMERICAL "-.~^"))
97✔
UNCOV
1484
                return false;
×
1485

1486
        return true;
1487
}
1488

1489
ssize_t strlevenshtein(const char *x, const char *y) {
122✔
1490
        _cleanup_free_ size_t *t0 = NULL, *t1 = NULL, *t2 = NULL;
244✔
1491
        size_t xl, yl;
122✔
1492

1493
        /* This is inspired from the Linux kernel's Levenshtein implementation */
1494

1495
        if (streq_ptr(x, y))
122✔
1496
                return 0;
1497

1498
        xl = strlen_ptr(x);
118✔
1499
        if (xl > SSIZE_MAX)
117✔
1500
                return -E2BIG;
1501

1502
        yl = strlen_ptr(y);
118✔
1503
        if (yl > SSIZE_MAX)
117✔
1504
                return -E2BIG;
1505

1506
        if (isempty(x))
118✔
1507
                return yl;
3✔
1508
        if (isempty(y))
115✔
1509
                return xl;
1✔
1510

1511
        t0 = new0(size_t, yl + 1);
114✔
1512
        if (!t0)
114✔
1513
                return -ENOMEM;
1514
        t1 = new0(size_t, yl + 1);
114✔
1515
        if (!t1)
114✔
1516
                return -ENOMEM;
1517
        t2 = new0(size_t, yl + 1);
114✔
1518
        if (!t2)
114✔
1519
                return -ENOMEM;
1520

1521
        for (size_t i = 0; i <= yl; i++)
1,249✔
1522
                t1[i] = i;
1,135✔
1523

1524
        for (size_t i = 0; i < xl; i++) {
712✔
1525
                t2[0] = i + 1;
598✔
1526

1527
                for (size_t j = 0; j < yl; j++) {
6,439✔
1528
                        /* Substitution */
1529
                        t2[j+1] = t1[j] + (x[i] != y[j]);
5,841✔
1530

1531
                        /* Swap */
1532
                        if (i > 0 && j > 0 && x[i-1] == y[j] && x[i] == y[j-1] && t2[j+1] > t0[j-1] + 1)
5,841✔
1533
                                t2[j+1] = t0[j-1] + 1;
14✔
1534

1535
                        /* Deletion */
1536
                        if (t2[j+1] > t1[j+1] + 1)
5,841✔
1537
                                t2[j+1] = t1[j+1] + 1;
247✔
1538

1539
                        /* Insertion */
1540
                        if (t2[j+1] > t2[j] + 1)
5,841✔
1541
                                t2[j+1] = t2[j] + 1;
880✔
1542
                }
1543

1544
                size_t *dummy = t0;
1545
                t0 = t1;
1546
                t1 = t2;
1547
                t2 = dummy;
1548
        }
1549

1550
        return t1[yl];
114✔
1551
}
1552

1553
char* strrstr_internal(const char *haystack, const char *needle) {
25,302✔
1554
        /* Like strstr() but returns the last rather than the first occurrence of "needle" in "haystack". */
1555

1556
        if (!haystack || !needle)
25,302✔
1557
                return NULL;
1558

1559
        /* Special case: for the empty string we return the very last possible occurrence, i.e. *after* the
1560
         * last char, not before. */
1561
        if (needle[0] == 0)
25,299✔
1562
                return (char*) strchr(haystack, 0);
2✔
1563

1564
        /* Special case: for single character strings, just use optimized strrchr() */
1565
        if (needle[1] == 0)
25,297✔
1566
                return (char*) strrchr(haystack, needle[0]);
3✔
1567

1568
        for (const char *p = strstr(haystack, needle), *q; p; p = q) {
25,300✔
1569
                q = strstr(p + 1, needle);
18✔
1570
                if (!q)
18✔
1571
                        return (char*) p;
1572
        }
1573
        return NULL;
1574
}
1575

1576
char* strrstr_no_case_internal(const char *haystack, const char *needle) {
41✔
1577
        if (!haystack || !needle)
41✔
1578
                return NULL;
1579

1580
        for (const char *p = strchr(haystack, 0); p > haystack; p--)
438✔
1581
                if (startswith_no_case(p, needle))
431✔
1582
                        return (char*) p;
1583

1584
        return startswith_no_case(haystack, needle) ? (char*) haystack : NULL;
7✔
1585
}
1586

1587
size_t str_common_prefix(const char *a, const char *b) {
866✔
1588
        assert(a);
866✔
1589
        assert(b);
866✔
1590

1591
        /* Returns the length of the common prefix of the two specified strings, or SIZE_MAX in case the
1592
         * strings are fully identical. */
1593

1594
        for (size_t n = 0;; n++) {
731✔
1595
                char c = a[n];
1,597✔
1596
                if (c != b[n])
1,597✔
1597
                        return n;
1598
                if (c == 0)
734✔
1599
                        return SIZE_MAX;
1600
        }
1601
}
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