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

systemd / systemd / 13710725352

06 Mar 2025 10:36PM UTC coverage: 71.765% (-0.02%) from 71.788%
13710725352

push

github

web-flow
hostnamectl: show image info in hostnamectl (#36638)

On image-based systems these properties are quite fundamental, hence
show them in the hostnamed output.

2 of 9 new or added lines in 1 file covered. (22.22%)

5360 existing lines in 93 files now uncovered.

294785 of 410763 relevant lines covered (71.77%)

717261.41 hits per line

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

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

3
#include <errno.h>
4
#include <stdarg.h>
5
#include <stdint.h>
6
#include <stdio.h>
7
#include <stdlib.h>
8

9
#include "alloc-util.h"
10
#include "escape.h"
11
#include "extract-word.h"
12
#include "fd-util.h"
13
#include "fileio.h"
14
#include "glyph-util.h"
15
#include "gunicode.h"
16
#include "locale-util.h"
17
#include "macro.h"
18
#include "memory-util.h"
19
#include "memstream-util.h"
20
#include "path-util.h"
21
#include "string-util.h"
22
#include "strv.h"
23
#include "terminal-util.h"
24
#include "utf8.h"
25

26
char* first_word(const char *s, const char *word) {
3,316,265✔
27
        assert(s);
3,316,265✔
28
        assert(word);
3,316,265✔
29

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

33
        if (isempty(word))
3,316,265✔
34
                return (char*) s;
35

36
        const char *p = startswith(s, word);
3,316,264✔
37
        if (!p)
3,316,264✔
38
                return NULL;
39
        if (*p == '\0')
117,889✔
40
                return (char*) p;
41

42
        const char *nw = skip_leading_chars(p, WHITESPACE);
117,888✔
43
        if (p == nw)
117,888✔
44
                return NULL;
1✔
45

46
        return (char*) nw;
47
}
48

49
char* strprepend(char **x, const char *s) {
5✔
50
        assert(x);
5✔
51

52
        if (isempty(s) && *x)
6✔
53
                return *x;
5✔
54

55
        char *p = strjoin(strempty(s), *x);
6✔
56
        if (!p)
5✔
57
                return NULL;
58

59
        free_and_replace(*x, p);
5✔
60
        return *x;
5✔
61
}
62

63
char* strextendn(char **x, const char *s, size_t l) {
30,663✔
64
        assert(x);
30,663✔
65
        assert(s || l == 0);
30,663✔
66

67
        if (l > 0)
30,663✔
68
                l = strnlen(s, l); /* ignore trailing noise */
30,100✔
69

70
        if (l > 0 || !*x) {
30,663✔
71
                size_t q;
30,388✔
72
                char *m;
30,388✔
73

74
                q = strlen_ptr(*x);
30,388✔
75
                m = realloc(*x, q + l + 1);
30,388✔
76
                if (!m)
30,388✔
77
                        return NULL;
78

79
                *mempcpy_typesafe(m + q, s, l) = 0;
30,388✔
80

81
                *x = m;
30,388✔
82
        }
83

84
        return *x;
30,663✔
85
}
86

87
char* strstrip(char *s) {
6,042,400✔
88
        if (!s)
6,042,400✔
89
                return NULL;
90

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

93
        return delete_trailing_chars(skip_leading_chars(s, WHITESPACE), WHITESPACE);
6,042,400✔
94
}
95

96
char* delete_chars(char *s, const char *bad) {
9✔
97
        char *f, *t;
9✔
98

99
        /* Drops all specified bad characters, regardless where in the string */
100

101
        if (!s)
9✔
102
                return NULL;
103

104
        if (!bad)
9✔
105
                bad = WHITESPACE;
×
106

107
        for (f = s, t = s; *f; f++) {
131✔
108
                if (strchr(bad, *f))
122✔
109
                        continue;
67✔
110

111
                *(t++) = *f;
55✔
112
        }
113

114
        *t = 0;
9✔
115

116
        return s;
9✔
117
}
118

119
char* delete_trailing_chars(char *s, const char *bad) {
7,146,820✔
120
        char *c = s;
7,146,820✔
121

122
        /* Drops all specified bad characters, at the end of the string */
123

124
        if (!s)
7,146,820✔
125
                return NULL;
126

127
        if (!bad)
7,146,820✔
128
                bad = WHITESPACE;
67,381✔
129

130
        for (char *p = s; *p; p++)
277,171,105✔
131
                if (!strchr(bad, *p))
270,024,285✔
132
                        c = p + 1;
259,181,504✔
133

134
        *c = 0;
7,146,820✔
135

136
        return s;
7,146,820✔
137
}
138

139
char* truncate_nl_full(char *s, size_t *ret_len) {
3,174,094✔
140
        size_t n;
3,174,094✔
141

142
        assert(s);
3,174,094✔
143

144
        n = strcspn(s, NEWLINE);
3,174,094✔
145
        s[n] = '\0';
3,174,094✔
146
        if (ret_len)
3,174,094✔
147
                *ret_len = n;
6✔
148
        return s;
3,174,094✔
149
}
150

151
char ascii_tolower(char x) {
4,409,215✔
152

153
        if (x >= 'A' && x <= 'Z')
4,409,215✔
154
                return x - 'A' + 'a';
169,705✔
155

156
        return x;
157
}
158

159
char ascii_toupper(char x) {
206,669✔
160

161
        if (x >= 'a' && x <= 'z')
206,669✔
162
                return x - 'a' + 'A';
100,887✔
163

164
        return x;
165
}
166

167
char* ascii_strlower(char *t) {
22,008✔
168
        assert(t);
22,008✔
169

170
        for (char *p = t; *p; p++)
140,990✔
171
                *p = ascii_tolower(*p);
118,982✔
172

173
        return t;
22,008✔
174
}
175

176
char* ascii_strupper(char *t) {
9,975✔
177
        assert(t);
9,975✔
178

179
        for (char *p = t; *p; p++)
216,490✔
180
                *p = ascii_toupper(*p);
206,515✔
181

182
        return t;
9,975✔
183
}
184

185
char* ascii_strlower_n(char *t, size_t n) {
737,527✔
186
        if (n <= 0)
737,527✔
187
                return t;
188

189
        for (size_t i = 0; i < n; i++)
2,455,028✔
190
                t[i] = ascii_tolower(t[i]);
1,717,791✔
191

192
        return t;
193
}
194

195
int ascii_strcasecmp_n(const char *a, const char *b, size_t n) {
652,261✔
196

197
        for (; n > 0; a++, b++, n--) {
1,787,470✔
198
                int x, y;
1,282,103✔
199

200
                x = (int) (uint8_t) ascii_tolower(*a);
1,282,103✔
201
                y = (int) (uint8_t) ascii_tolower(*b);
1,282,103✔
202

203
                if (x != y)
1,282,103✔
204
                        return x - y;
146,894✔
205
        }
206

207
        return 0;
208
}
209

210
int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) {
497,507✔
211
        int r;
497,507✔
212

213
        r = ascii_strcasecmp_n(a, b, MIN(n, m));
497,507✔
214
        if (r != 0)
497,507✔
215
                return r;
216

217
        return CMP(n, m);
455,515✔
218
}
219

220
bool chars_intersect(const char *a, const char *b) {
502✔
221
        /* Returns true if any of the chars in a are in b. */
222
        for (const char *p = a; *p; p++)
5,372✔
223
                if (strchr(b, *p))
4,883✔
224
                        return true;
225

226
        return false;
227
}
228

229
bool string_has_cc(const char *p, const char *ok) {
199,763✔
230
        assert(p);
199,763✔
231

232
        /*
233
         * Check if a string contains control characters. If 'ok' is
234
         * non-NULL it may be a string containing additional CCs to be
235
         * considered OK.
236
         */
237

238
        for (const char *t = p; *t; t++) {
1,420,344✔
239
                if (ok && strchr(ok, *t))
1,220,597✔
240
                        continue;
8✔
241

242
                if (char_is_cc(*t))
1,220,589✔
243
                        return true;
244
        }
245

246
        return false;
247
}
248

249
static int write_ellipsis(char *buf, bool unicode) {
3,117✔
250
        const char *s = special_glyph_full(SPECIAL_GLYPH_ELLIPSIS, unicode);
3,117✔
251
        assert(strlen(s) == 3);
3,117✔
252
        memcpy(buf, s, 3);
3,117✔
253
        return 3;
3,117✔
254
}
255

256
static size_t ansi_sequence_length(const char *s, size_t len) {
6,271✔
257
        assert(s);
6,271✔
258

259
        if (len < 2)
6,271✔
260
                return 0;
261

262
        if (s[0] != 0x1B)  /* ASCII 27, aka ESC, aka Ctrl-[ */
6,181✔
263
                return 0;  /* Not the start of a sequence */
264

265
        if (s[1] == 0x5B) { /* [, start of CSI sequence */
895✔
266
                size_t i = 2;
895✔
267

268
                if (i == len)
895✔
269
                        return 0;
270

271
                while (s[i] >= 0x30 && s[i] <= 0x3F) /* Parameter bytes */
9,184✔
272
                        if (++i == len)
8,289✔
273
                                return 0;
274
                while (s[i] >= 0x20 && s[i] <= 0x2F) /* Intermediate bytes */
895✔
275
                        if (++i == len)
×
276
                                return 0;
277
                if (s[i] >= 0x40 && s[i] <= 0x7E) /* Final byte */
895✔
278
                        return i + 1;
895✔
279
                return 0;  /* Bad sequence */
280

281
        } else if (s[1] >= 0x40 && s[1] <= 0x5F) /* other non-CSI Fe sequence */
×
282
                return 2;
×
283

284
        return 0;  /* Bad escape? */
285
}
286

287
static bool string_has_ansi_sequence(const char *s, size_t len) {
6,781✔
288
        const char *t = s;
6,781✔
289

290
        while ((t = memchr(s, 0x1B, len - (t - s))))
6,781✔
291
                if (ansi_sequence_length(t, len - (t - s)) > 0)
151✔
292
                        return true;
293
        return false;
294
}
295

296
static size_t previous_ansi_sequence(const char *s, size_t length, const char **ret_where) {
263✔
297
        /* Locate the previous ANSI sequence and save its start in *ret_where and return length. */
298

299
        for (size_t i = length - 2; i > 0; i--) {  /* -2 because at least two bytes are needed */
3,409✔
300
                size_t slen = ansi_sequence_length(s + (i - 1), length - (i - 1));
3,407✔
301
                if (slen == 0)
3,407✔
302
                        continue;
3,146✔
303

304
                *ret_where = s + (i - 1);
261✔
305
                return slen;
261✔
306
        }
307

308
        *ret_where = NULL;
2✔
309
        return 0;
2✔
310
}
311

312
static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
4,001✔
313
        size_t x, need_space, suffix_len;
4,001✔
314
        char *t;
4,001✔
315

316
        assert(s);
4,001✔
317
        assert(percent <= 100);
4,001✔
318
        assert(new_length != SIZE_MAX);
4,001✔
319

320
        if (old_length <= new_length)
4,001✔
321
                return strndup(s, old_length);
1,907✔
322

323
        /* Special case short ellipsations */
324
        switch (new_length) {
2,094✔
325

326
        case 0:
×
327
                return strdup("");
×
328

329
        case 1:
65✔
330
                if (is_locale_utf8())
65✔
331
                        return strdup("…");
65✔
332
                else
333
                        return strdup(".");
×
334

335
        case 2:
35✔
336
                if (!is_locale_utf8())
35✔
337
                        return strdup("..");
×
338
                break;
339
        }
340

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

346
        t = new(char, new_length+3);
2,029✔
347
        if (!t)
2,029✔
348
                return NULL;
349

350
        assert(new_length >= need_space);
2,029✔
351

352
        x = ((new_length - need_space) * percent + 50) / 100;
2,029✔
353
        assert(x <= new_length - need_space);
2,029✔
354

355
        write_ellipsis(mempcpy(t, s, x), /* unicode = */ false);
2,029✔
356
        suffix_len = new_length - x - need_space;
2,029✔
357
        memcpy(t + x + 3, s + old_length - suffix_len, suffix_len);
2,029✔
358
        *(t + x + 3 + suffix_len) = '\0';
2,029✔
359

360
        return t;
2,029✔
361
}
362

363
char* ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
6,952✔
364
        size_t x, k, len, len2;
6,952✔
365
        const char *i, *j;
6,952✔
366
        int r;
6,952✔
367

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

380
        assert(s);
6,952✔
381
        assert(percent <= 100);
6,952✔
382

383
        if (new_length == SIZE_MAX)
6,952✔
UNCOV
384
                return strndup(s, old_length);
×
385

386
        if (new_length == 0)
6,952✔
387
                return strdup("");
171✔
388

389
        bool has_ansi_seq = string_has_ansi_sequence(s, old_length);
6,781✔
390

391
        /* If no multibyte characters or ANSI sequences, use ascii_ellipsize_mem for speed */
392
        if (!has_ansi_seq && ascii_is_valid_n(s, old_length))
6,781✔
393
                return ascii_ellipsize_mem(s, old_length, new_length, percent);
4,001✔
394

395
        x = (new_length - 1) * percent / 100;
2,780✔
396
        assert(x <= new_length - 1);
2,780✔
397

398
        k = 0;
399
        for (i = s; i < s + old_length; ) {
24,291✔
400
                size_t slen = has_ansi_seq ? ansi_sequence_length(i, old_length - (i - s)) : 0;
23,100✔
401
                if (slen > 0) {
984✔
402
                        i += slen;
221✔
403
                        continue;  /* ANSI sequences don't take up any space in output */
221✔
404
                }
405

406
                char32_t c;
22,879✔
407
                r = utf8_encoded_to_unichar(i, &c);
22,879✔
408
                if (r < 0)
22,879✔
UNCOV
409
                        return NULL;
×
410

411
                int w = unichar_iswide(c) ? 2 : 1;
22,879✔
412
                if (k + w > x)
22,879✔
413
                        break;
414

415
                k += w;
21,290✔
416
                i += r;
21,290✔
417
        }
418

419
        const char *ansi_start = s + old_length;
2,780✔
420
        size_t ansi_len = 0;
2,780✔
421

422
        for (const char *t = j = s + old_length; t > i && k < new_length; ) {
8,619✔
423
                char32_t c;
5,925✔
424
                int w;
5,925✔
425
                const char *tt;
5,925✔
426

427
                if (has_ansi_seq && ansi_start >= t)
5,925✔
428
                        /* Figure out the previous ANSI sequence, if any */
429
                        ansi_len = previous_ansi_sequence(s, t - s, &ansi_start);
263✔
430

431
                /* If the sequence extends all the way to the current position, skip it. */
432
                if (has_ansi_seq && ansi_len > 0 && ansi_start + ansi_len == t) {
5,925✔
433
                        t = ansi_start;
112✔
434
                        continue;
112✔
435
                }
436

437
                tt = utf8_prev_char(t);
5,813✔
438
                r = utf8_encoded_to_unichar(tt, &c);
5,813✔
439
                if (r < 0)
5,813✔
UNCOV
440
                        return NULL;
×
441

442
                w = unichar_iswide(c) ? 2 : 1;
5,813✔
443
                if (k + w > new_length)
5,813✔
444
                        break;
445

446
                k += w;
5,727✔
447
                j = t = tt;  /* j should always point to the first "real" character */
5,727✔
448
        }
449

450
        /* We don't actually need to ellipsize */
451
        if (i >= j)
2,780✔
452
                return memdup_suffix0(s, old_length);
1,716✔
453

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

464
        len = i - s;
1,064✔
465
        len2 = s + old_length - j;
1,064✔
466

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

472
        char *e = new(char, alloc_len);
1,064✔
473
        if (!e)
1,064✔
474
                return NULL;
475

476
        memcpy_safe(e, s, len);
1,064✔
477
        write_ellipsis(e + len, /* unicode = */ true);
1,064✔
478

479
        char *dst = e + len + 3;
1,064✔
480

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

492
        memcpy_safe(dst, j, len2);
1,064✔
493
        dst[len2] = '\0';
1,064✔
494

495
        return e;
1,064✔
496
}
497

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

511
        size_t i = 0, last_char_width[4] = {}, k = 0;
51,097✔
512

513
        assert(buf);
51,097✔
514
        assert(len > 0); /* at least a terminating NUL */
51,097✔
515
        assert(s);
51,097✔
516

517
        for (;;) {
1,148,009✔
518
                char four[4];
599,553✔
519
                int w;
599,553✔
520

521
                if (*s == 0) /* terminating NUL detected? then we are done! */
599,553✔
522
                        goto done;
51,061✔
523

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

529
                /* OK, there was space, let's add this escaped character to the buffer */
530
                memcpy(buf + i, four, w);
548,456✔
531
                i += w;
548,456✔
532

533
                /* And remember its width in the ring buffer */
534
                last_char_width[k] = w;
548,456✔
535
                k = (k + 1) % 4;
548,456✔
536

537
                s++;
548,456✔
538
        }
539

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

544
                if (i + 4 <= len) /* nice, we reached our space goal */
81✔
545
                        break;
546

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

551
                assert(i >= last_char_width[k]);
45✔
552
                i -= last_char_width[k];
45✔
553
        }
554

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

565
done:
4✔
566
        buf[i] = '\0';
51,097✔
567
        return buf;
51,097✔
568
}
569

570
char* strshorten(char *s, size_t l) {
26,819✔
571
        assert(s);
26,819✔
572

573
        if (l >= SIZE_MAX-1) /* Would not change anything */
26,819✔
574
                return s;
575

576
        if (strnlen(s, l+1) > l)
26,817✔
577
                s[l] = 0;
18✔
578

579
        return s;
580
}
581

582
int strgrowpad0(char **s, size_t l) {
16✔
583
        size_t sz;
16✔
584

585
        assert(s);
16✔
586

587
        if (*s) {
16✔
588
                sz = strlen(*s) + 1;
16✔
589
                if (sz >= l) /* never shrink */
16✔
590
                        return 0;
591
        } else
592
                sz = 0;
593

594
        char *q = realloc(*s, l);
16✔
595
        if (!q)
16✔
596
                return -ENOMEM;
597

598
        *s = q;
16✔
599

600
        memzero(*s + sz, l - sz);
16✔
601
        return 0;
602
}
603

604
char* strreplace(const char *text, const char *old_string, const char *new_string) {
3,661✔
605
        size_t l, old_len, new_len;
3,661✔
606
        char *t, *ret = NULL;
3,661✔
607
        const char *f;
3,661✔
608

609
        assert(old_string);
3,661✔
610
        assert(new_string);
3,661✔
611

612
        if (!text)
3,661✔
613
                return NULL;
3,661✔
614

615
        old_len = strlen(old_string);
3,660✔
616
        new_len = strlen(new_string);
3,660✔
617

618
        l = strlen(text);
3,660✔
619
        if (!GREEDY_REALLOC(ret, l+1))
3,660✔
620
                return NULL;
621

622
        f = text;
3,660✔
623
        t = ret;
3,660✔
624
        while (*f) {
104,402✔
625
                size_t d, nl;
100,742✔
626

627
                if (!startswith(f, old_string)) {
100,742✔
628
                        *(t++) = *(f++);
98,377✔
629
                        continue;
98,377✔
630
                }
631

632
                d = t - ret;
2,365✔
633
                nl = l - old_len + new_len;
2,365✔
634

635
                if (!GREEDY_REALLOC(ret, nl + 1))
2,365✔
UNCOV
636
                        return mfree(ret);
×
637

638
                l = nl;
2,365✔
639
                t = ret + d;
2,365✔
640

641
                t = stpcpy(t, new_string);
2,365✔
642
                f += old_len;
2,365✔
643
        }
644

645
        *t = 0;
3,660✔
646
        return ret;
3,660✔
647
}
648

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

655
        if (!offsets)
392✔
656
                return;
657

658
        assert(shift);
382✔
659

660
        if ((size_t) diff < offsets[0])
382✔
UNCOV
661
                shift[0] += size;
×
662
        if ((size_t) diff < offsets[1])
382✔
UNCOV
663
                shift[1] += size;
×
664
}
665

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

679
        assert(ibuf);
85,859✔
680
        assert(*ibuf);
85,859✔
681

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

696
        isz = _isz ? *_isz : strlen(*ibuf);
85,859✔
697

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

704
        for (const char *i = *ibuf; i < *ibuf + isz + 1; i++) {
10,211,105✔
705

706
                bool eot = i >= *ibuf + isz;
10,125,246✔
707

708
                switch (state) {
10,125,246✔
709

710
                case STATE_OTHER:
10,125,163✔
711
                        if (eot)
10,125,163✔
712
                                break;
713

714
                        if (*i == '\r') {
10,039,304✔
715
                                n_carriage_returns++;
7✔
716
                                break;
7✔
717
                        } else if (*i == '\n')
10,039,297✔
718
                                /* Ignore carriage returns before new line */
719
                                n_carriage_returns = 0;
2✔
720
                        for (; n_carriage_returns > 0; n_carriage_returns--)
10,039,299✔
721
                                fputc('\r', f);
2✔
722

723
                        if (*i == '\x1B')
10,039,297✔
724
                                state = STATE_ESCAPE;
725
                        else if (*i == '\t') {
10,039,281✔
726
                                fputs("        ", f);
387✔
727
                                advance_offsets(i - *ibuf, highlight, shift, 7);
387✔
728
                        } else
729
                                fputc(*i, f);
10,038,894✔
730

731
                        break;
732

733
                case STATE_ESCAPE:
16✔
734
                        assert(n_carriage_returns == 0);
16✔
735

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

753
                        break;
754

755
                case STATE_CSI:
41✔
756
                        assert(n_carriage_returns == 0);
41✔
757

758
                        if (eot || !strchr("01234567890;m", *i)) { /* EOT or invalid chars in sequence */
41✔
759
                                fputc('\x1B', f);
5✔
760
                                fputc('[', f);
5✔
761
                                advance_offsets(i - *ibuf, highlight, shift, 2);
5✔
762
                                state = STATE_OTHER;
5✔
763
                                i = begin-1;
5✔
764
                        } else if (*i == 'm')
36✔
765
                                state = STATE_OTHER;
8✔
766

767
                        break;
768

769
                case STATE_OSC:
24✔
770
                        assert(n_carriage_returns == 0);
24✔
771

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

786
                        break;
787

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

798
                        break;
799
                }
800
        }
801

802
        char *obuf;
85,859✔
803
        if (memstream_finalize(&m, &obuf, _isz) < 0)
85,859✔
804
                return NULL;
805

806
        free_and_replace(*ibuf, obuf);
85,859✔
807

808
        if (highlight) {
85,859✔
809
                highlight[0] += shift[0];
85,850✔
810
                highlight[1] += shift[1];
85,850✔
811
        }
812

813
        return *ibuf;
85,859✔
814
}
815

816
char* strextend_with_separator_internal(char **x, const char *separator, ...) {
15,891,733✔
817
        _cleanup_free_ char *buffer = NULL;
15,891,733✔
818
        size_t f, l, l_separator;
15,891,733✔
819
        bool need_separator;
15,891,733✔
820
        char *nr, *p;
15,891,733✔
821
        va_list ap;
15,891,733✔
822

823
        if (!x)
15,891,733✔
824
                x = &buffer;
14,683,228✔
825

826
        l = f = strlen_ptr(*x);
15,891,733✔
827

828
        need_separator = !isempty(*x);
15,891,733✔
829
        l_separator = strlen_ptr(separator);
15,891,733✔
830

831
        va_start(ap, separator);
15,891,733✔
832
        for (const char *t;;) {
59,431,904✔
833
                size_t n;
59,431,904✔
834

835
                t = va_arg(ap, const char *);
59,431,904✔
836
                if (!t)
59,431,904✔
837
                        break;
838
                if (t == POINTER_MAX)
43,540,171✔
839
                        continue;
3✔
840

841
                n = strlen(t);
43,540,168✔
842

843
                if (need_separator)
43,540,168✔
844
                        n += l_separator;
28,336,115✔
845

846
                if (n >= SIZE_MAX - l) {
43,540,168✔
UNCOV
847
                        va_end(ap);
×
UNCOV
848
                        return NULL;
×
849
                }
850

851
                l += n;
43,540,168✔
852
                need_separator = true;
43,540,168✔
853
        }
854
        va_end(ap);
15,891,733✔
855

856
        need_separator = !isempty(*x);
15,891,733✔
857

858
        nr = realloc(*x, GREEDY_ALLOC_ROUND_UP(l+1));
15,891,733✔
859
        if (!nr)
15,891,733✔
860
                return NULL;
861

862
        *x = nr;
15,891,733✔
863
        p = nr + f;
15,891,733✔
864

865
        va_start(ap, separator);
15,891,733✔
866
        for (;;) {
59,431,904✔
867
                const char *t;
59,431,904✔
868

869
                t = va_arg(ap, const char *);
59,431,904✔
870
                if (!t)
59,431,904✔
871
                        break;
872
                if (t == POINTER_MAX)
43,540,171✔
873
                        continue;
3✔
874

875
                if (need_separator && separator)
43,540,168✔
876
                        p = stpcpy(p, separator);
179,519✔
877

878
                p = stpcpy(p, t);
43,540,168✔
879

880
                need_separator = true;
43,540,168✔
881
        }
882
        va_end(ap);
15,891,733✔
883

884
        assert(p == nr + l);
15,891,733✔
885
        *p = 0;
15,891,733✔
886

887
        /* If no buffer to extend was passed in return the start of the buffer */
888
        if (buffer)
15,891,733✔
889
                return TAKE_PTR(buffer);
14,683,228✔
890

891
        /* Otherwise we extended the buffer: return the end */
892
        return p;
893
}
894

895
int strextendf_with_separator(char **x, const char *separator, const char *format, ...) {
23,690✔
896
        size_t m, a, l_separator;
23,690✔
897
        va_list ap;
23,690✔
898
        int l;
23,690✔
899

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

904
        assert(x);
23,690✔
905
        assert(format);
23,690✔
906

907
        l_separator = isempty(*x) ? 0 : strlen_ptr(separator);
35,219✔
908

909
        /* Let's try to use the allocated buffer, if there's room at the end still. Otherwise let's extend by 64 chars. */
910
        if (*x) {
23,690✔
911
                m = strlen(*x);
11,529✔
912
                a = MALLOC_SIZEOF_SAFE(*x);
11,529✔
913
                assert(a >= m + 1);
11,529✔
914
        } else
915
                m = a = 0;
916

917
        if (a - m < 17 + l_separator) { /* if there's less than 16 chars space, then enlarge the buffer first */
23,690✔
918
                char *n;
18,027✔
919

920
                if (_unlikely_(l_separator > SIZE_MAX - 64)) /* overflow check #1 */
18,027✔
921
                        return -ENOMEM;
23,690✔
922
                if (_unlikely_(m > SIZE_MAX - 64 - l_separator)) /* overflow check #2 */
18,027✔
923
                        return -ENOMEM;
924

925
                n = realloc(*x, m + 64 + l_separator);
18,027✔
926
                if (!n)
18,027✔
927
                        return -ENOMEM;
928

929
                *x = n;
18,027✔
930
                a = MALLOC_SIZEOF_SAFE(*x);
18,027✔
931
        }
932

933
        /* Now, let's try to format the string into it */
934
        memcpy_safe(*x + m, separator, l_separator);
23,690✔
935
        va_start(ap, format);
23,690✔
936
        l = vsnprintf(*x + m + l_separator, a - m - l_separator, format, ap);
23,690✔
937
        va_end(ap);
23,690✔
938

939
        assert(l >= 0);
23,690✔
940

941
        if ((size_t) l < a - m - l_separator) {
23,690✔
942
                char *n;
23,508✔
943

944
                /* Nice! This worked. We are done. But first, let's return the extra space we don't
945
                 * need. This should be a cheap operation, since we only lower the allocation size here,
946
                 * never increase. */
947
                n = realloc(*x, m + (size_t) l + l_separator + 1);
23,508✔
948
                if (n)
23,508✔
949
                        *x = n;
23,508✔
950
        } else {
951
                char *n;
182✔
952

953
                /* Wasn't enough. Then let's allocate exactly what we need. */
954

955
                if (_unlikely_((size_t) l > SIZE_MAX - (l_separator + 1))) /* overflow check #1 */
182✔
UNCOV
956
                        goto oom;
×
957
                if (_unlikely_(m > SIZE_MAX - ((size_t) l + l_separator + 1))) /* overflow check #2 */
182✔
UNCOV
958
                        goto oom;
×
959

960
                a = m + (size_t) l + l_separator + 1;
182✔
961
                n = realloc(*x, a);
182✔
962
                if (!n)
182✔
UNCOV
963
                        goto oom;
×
964
                *x = n;
182✔
965

966
                va_start(ap, format);
182✔
967
                l = vsnprintf(*x + m + l_separator, a - m - l_separator, format, ap);
182✔
968
                va_end(ap);
182✔
969

970
                assert((size_t) l < a - m - l_separator);
182✔
971
        }
972

973
        return 0;
974

UNCOV
975
oom:
×
976
        /* truncate the bytes added after memcpy_safe() again */
UNCOV
977
        (*x)[m] = 0;
×
UNCOV
978
        return -ENOMEM;
×
979
}
980

981
char* strrep(const char *s, unsigned n) {
11✔
982
        char *r, *p;
11✔
983
        size_t l;
11✔
984

985
        assert(s);
11✔
986

987
        l = strlen(s);
11✔
988
        p = r = malloc(l * n + 1);
11✔
989
        if (!r)
11✔
990
                return NULL;
991

992
        for (unsigned i = 0; i < n; i++)
40,337✔
993
                p = stpcpy(p, s);
40,326✔
994

995
        *p = 0;
11✔
996
        return r;
11✔
997
}
998

999
int split_pair(const char *s, const char *sep, char **ret_first, char **ret_second) {
3,195✔
1000
        assert(s);
3,195✔
1001
        assert(!isempty(sep));
3,195✔
1002
        assert(ret_first);
3,195✔
1003
        assert(ret_second);
3,195✔
1004

1005
        const char *x = strstr(s, sep);
3,195✔
1006
        if (!x)
3,195✔
1007
                return -EINVAL;
3,195✔
1008

1009
        _cleanup_free_ char *a = strndup(s, x - s);
542✔
1010
        if (!a)
542✔
1011
                return -ENOMEM;
1012

1013
        _cleanup_free_ char *b = strdup(x + strlen(sep));
542✔
1014
        if (!b)
542✔
1015
                return -ENOMEM;
1016

1017
        *ret_first = TAKE_PTR(a);
542✔
1018
        *ret_second = TAKE_PTR(b);
542✔
1019
        return 0;
542✔
1020
}
1021

1022
int free_and_strdup(char **p, const char *s) {
1,502,701✔
1023
        char *t;
1,502,701✔
1024

1025
        assert(p);
1,502,701✔
1026

1027
        /* Replaces a string pointer with a strdup()ed new string,
1028
         * possibly freeing the old one. */
1029

1030
        if (streq_ptr(*p, s))
1,502,701✔
1031
                return 0;
1,502,701✔
1032

1033
        if (s) {
1,299,674✔
1034
                t = strdup(s);
1,295,734✔
1035
                if (!t)
1,295,734✔
1036
                        return -ENOMEM;
1037
        } else
1038
                t = NULL;
1039

1040
        free_and_replace(*p, t);
1,299,674✔
1041

1042
        return 1;
1,299,674✔
1043
}
1044

1045
int free_and_strndup(char **p, const char *s, size_t l) {
65,525✔
1046
        char *t;
65,525✔
1047

1048
        assert(p);
65,525✔
1049
        assert(s || l == 0);
65,525✔
1050

1051
        /* Replaces a string pointer with a strndup()ed new string,
1052
         * freeing the old one. */
1053

1054
        if (!*p && !s)
65,525✔
1055
                return 0;
65,525✔
1056

1057
        if (*p && s && strneq(*p, s, l) && (l > strlen(*p) || (*p)[l] == '\0'))
65,525✔
1058
                return 0;
1059

1060
        if (s) {
30,999✔
1061
                t = strndup(s, l);
31,058✔
1062
                if (!t)
31,058✔
1063
                        return -ENOMEM;
1064
        } else
1065
                t = NULL;
1066

1067
        free_and_replace(*p, t);
31,059✔
1068
        return 1;
31,059✔
1069
}
1070

1071
int strdup_to_full(char **ret, const char *src) {
541,806✔
1072
        if (!src) {
541,806✔
1073
                if (ret)
184,465✔
1074
                        *ret = NULL;
184,464✔
1075

1076
                return 0;
184,465✔
1077
        } else {
1078
                if (ret) {
357,341✔
1079
                        char *t = strdup(src);
357,339✔
1080
                        if (!t)
357,339✔
1081
                                return -ENOMEM;
1082
                        *ret = t;
357,339✔
1083
                }
1084

1085
                return 1;
357,341✔
1086
        }
1087
};
1088

1089
bool string_is_safe(const char *p) {
158,120✔
1090
        if (!p)
158,120✔
1091
                return false;
1092

1093
        /* Checks if the specified string contains no quotes or control characters */
1094

1095
        for (const char *t = p; *t; t++) {
1,605,991✔
1096
                if (*t > 0 && *t < ' ') /* no control characters */
1,447,884✔
1097
                        return false;
1098

1099
                if (strchr(QUOTES "\\\x7f", *t))
1,447,876✔
1100
                        return false;
1101
        }
1102

1103
        return true;
1104
}
1105

1106
char* string_erase(char *x) {
100✔
1107
        if (!x)
100✔
1108
                return NULL;
1109

1110
        /* A delicious drop of snake-oil! To be called on memory where we stored passphrases or so, after we
1111
         * used them. */
1112
        explicit_bzero_safe(x, strlen(x));
100✔
1113
        return x;
100✔
1114
}
1115

1116
int string_truncate_lines(const char *s, size_t n_lines, char **ret) {
210✔
1117
        const char *p = s, *e = s;
210✔
1118
        bool truncation_applied = false;
210✔
1119
        char *copy;
210✔
1120
        size_t n = 0;
210✔
1121

1122
        assert(s);
210✔
1123

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

1128
        for (;;) {
436✔
1129
                size_t k;
323✔
1130

1131
                k = strcspn(p, "\n");
323✔
1132

1133
                if (p[k] == 0) {
323✔
1134
                        if (k == 0) /* final empty line */
178✔
1135
                                break;
1136

1137
                        if (n >= n_lines) /* above threshold */
153✔
1138
                                break;
1139

1140
                        e = p + k; /* last line to include */
138✔
1141
                        break;
138✔
1142
                }
1143

1144
                assert(p[k] == '\n');
145✔
1145

1146
                if (n >= n_lines)
145✔
1147
                        break;
1148

1149
                if (k > 0)
113✔
1150
                        e = p + k;
90✔
1151

1152
                p += k + 1;
113✔
1153
                n++;
113✔
1154
        }
1155

1156
        /* e points after the last character we want to keep */
1157
        if (isempty(e))
210✔
1158
                copy = strdup(s);
142✔
1159
        else {
1160
                if (!in_charset(e, "\n")) /* We only consider things truncated if we remove something that
68✔
1161
                                           * isn't a new-line or a series of them */
1162
                        truncation_applied = true;
40✔
1163

1164
                copy = strndup(s, e - s);
68✔
1165
        }
1166
        if (!copy)
210✔
1167
                return -ENOMEM;
1168

1169
        *ret = copy;
210✔
1170
        return truncation_applied;
210✔
1171
}
1172

1173
int string_extract_line(const char *s, size_t i, char **ret) {
128,976✔
1174
        const char *p = s;
128,976✔
1175
        size_t c = 0;
128,976✔
1176

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

1183
        for (;;) {
314,072✔
1184
                const char *q;
221,524✔
1185

1186
                q = strchr(p, '\n');
221,524✔
1187
                if (i == c) {
221,524✔
1188
                        /* The line we are looking for! */
1189

1190
                        if (q) {
126,083✔
1191
                                char *m;
3,028✔
1192

1193
                                m = strndup(p, q - p);
3,028✔
1194
                                if (!m)
3,028✔
1195
                                        return -ENOMEM;
1196

1197
                                *ret = m;
3,028✔
1198
                                return !isempty(q + 1); /* More coming? */
6,056✔
1199
                        } else
1200
                                /* Tell the caller to use the input string if equal */
1201
                                return strdup_to(ret, p != s ? p : NULL);
244,864✔
1202
                }
1203

1204
                if (!q)
95,441✔
1205
                        /* No more lines, return empty line */
1206
                        return strdup_to(ret, "");
2,893✔
1207

1208
                p = q + 1;
92,548✔
1209
                c++;
92,548✔
1210
        }
1211
}
1212

1213
int string_contains_word_strv(const char *string, const char *separators, char * const *words, const char **ret_word) {
71✔
1214
        /* In the default mode with no separators specified, we split on whitespace and coalesce separators. */
1215
        const ExtractFlags flags = separators ? EXTRACT_DONT_COALESCE_SEPARATORS : 0;
71✔
1216
        const char *found = NULL;
71✔
1217
        int r;
196✔
1218

1219
        for (;;) {
321✔
1220
                _cleanup_free_ char *w = NULL;
125✔
1221

1222
                r = extract_first_word(&string, &w, separators, flags);
196✔
1223
                if (r < 0)
196✔
UNCOV
1224
                        return r;
×
1225
                if (r == 0)
196✔
1226
                        break;
1227

1228
                found = strv_find(words, w);
146✔
1229
                if (found)
146✔
1230
                        break;
1231
        }
1232

1233
        if (ret_word)
71✔
1234
                *ret_word = found;
7✔
1235
        return !!found;
71✔
1236
}
1237

1238
bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok) {
9,352✔
1239
        if (!s1 && !s2)
9,352✔
1240
                return true;
1241
        if (!s1 || !s2)
9,351✔
1242
                return false;
1243

1244
        if (!ok)
9,349✔
1245
                ok = WHITESPACE;
13✔
1246

1247
        for (; *s1 && *s2; s1++, s2++)
19,130✔
1248
                if (*s1 != *s2)
12,283✔
1249
                        break;
1250

1251
        return in_charset(s1, ok) && in_charset(s2, ok);
11,857✔
1252
}
1253

1254
char* string_replace_char(char *str, char old_char, char new_char) {
517,577✔
1255
        assert(str);
517,577✔
1256
        assert(old_char != '\0');
517,577✔
1257
        assert(new_char != '\0');
517,577✔
1258
        assert(old_char != new_char);
517,577✔
1259

1260
        for (char *p = strchr(str, old_char); p; p = strchr(p + 1, old_char))
522,075✔
1261
                *p = new_char;
4,498✔
1262

1263
        return str;
517,577✔
1264
}
1265

1266
int make_cstring(const char *s, size_t n, MakeCStringMode mode, char **ret) {
1,730✔
1267
        char *b;
1,730✔
1268

1269
        assert(s || n == 0);
1,730✔
1270
        assert(mode >= 0);
1,730✔
1271
        assert(mode < _MAKE_CSTRING_MODE_MAX);
1,730✔
1272

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

1276
        if (n == 0) {
1,730✔
1277
                if (mode == MAKE_CSTRING_REQUIRE_TRAILING_NUL)
20✔
1278
                        return -EINVAL;
1279

1280
                if (!ret)
19✔
1281
                        return 0;
1282

1283
                b = new0(char, 1);
19✔
1284
        } else {
1285
                const char *nul;
1,710✔
1286

1287
                nul = memchr(s, 0, n);
1,710✔
1288
                if (nul) {
1,710✔
1289
                        if (nul < s + n - 1 || /* embedded NUL? */
19✔
1290
                            mode == MAKE_CSTRING_REFUSE_TRAILING_NUL)
1291
                                return -EINVAL;
1292

1293
                        n--;
1294
                } else if (mode == MAKE_CSTRING_REQUIRE_TRAILING_NUL)
1,691✔
1295
                        return -EINVAL;
1296

1297
                if (!ret)
1,695✔
1298
                        return 0;
1299

1300
                b = memdup_suffix0(s, n);
1,695✔
1301
        }
1302
        if (!b)
1,714✔
1303
                return -ENOMEM;
1304

1305
        *ret = b;
1,714✔
1306
        return 0;
1,714✔
1307
}
1308

1309
size_t strspn_from_end(const char *str, const char *accept) {
494,272✔
1310
        size_t n = 0;
494,272✔
1311

1312
        if (isempty(str))
494,272✔
1313
                return 0;
1314

1315
        if (isempty(accept))
494,269✔
1316
                return 0;
1317

1318
        for (const char *p = str + strlen(str); p > str && strchr(accept, p[-1]); p--)
962,428✔
1319
                n++;
468,160✔
1320

1321
        return n;
1322
}
1323

UNCOV
1324
char* strdupspn(const char *a, const char *accept) {
×
UNCOV
1325
        if (isempty(a) || isempty(accept))
×
UNCOV
1326
                return strdup("");
×
1327

1328
        return strndup(a, strspn(a, accept));
×
1329
}
1330

1331
char* strdupcspn(const char *a, const char *reject) {
24,002✔
1332
        if (isempty(a))
24,002✔
UNCOV
1333
                return strdup("");
×
1334
        if (isempty(reject))
24,002✔
UNCOV
1335
                return strdup(a);
×
1336

1337
        return strndup(a, strcspn(a, reject));
24,002✔
1338
}
1339

1340
char* find_line_startswith(const char *haystack, const char *needle) {
16,567✔
1341
        char *p;
16,567✔
1342

1343
        assert(haystack);
16,567✔
1344
        assert(needle);
16,567✔
1345

1346
        /* Finds the first line in 'haystack' that starts with the specified string. Returns a pointer to the
1347
         * first character after it */
1348

1349
        p = strstr(haystack, needle);
16,567✔
1350
        if (!p)
16,567✔
1351
                return NULL;
1352

1353
        if (p > haystack)
16,187✔
1354
                while (p[-1] != '\n') {
15,950✔
1355
                        p = strstr(p + 1, needle);
9✔
1356
                        if (!p)
9✔
1357
                                return NULL;
1358
                }
1359

1360
        return p + strlen(needle);
16,186✔
1361
}
1362

1363
bool version_is_valid(const char *s) {
3,975✔
1364
        if (isempty(s))
3,975✔
1365
                return false;
1366

1367
        if (!filename_part_is_valid(s))
3,973✔
1368
                return false;
1369

1370
        /* This is a superset of the characters used by semver. We additionally allow "," and "_". */
1371
        if (!in_charset(s, ALPHANUMERICAL ".,_-+"))
3,973✔
UNCOV
1372
                return false;
×
1373

1374
        return true;
1375
}
1376

1377
bool version_is_valid_versionspec(const char *s) {
60✔
1378
        if (!filename_part_is_valid(s))
60✔
1379
                return false;
1380

1381
        if (!in_charset(s, ALPHANUMERICAL "-.~^"))
60✔
UNCOV
1382
                return false;
×
1383

1384
        return true;
1385
}
1386

1387
ssize_t strlevenshtein(const char *x, const char *y) {
76✔
1388
        _cleanup_free_ size_t *t0 = NULL, *t1 = NULL, *t2 = NULL;
152✔
1389
        size_t xl, yl;
76✔
1390

1391
        /* This is inspired from the Linux kernel's Levenshtein implementation */
1392

1393
        if (streq_ptr(x, y))
76✔
1394
                return 0;
1395

1396
        xl = strlen_ptr(x);
72✔
1397
        if (xl > SSIZE_MAX)
71✔
1398
                return -E2BIG;
1399

1400
        yl = strlen_ptr(y);
72✔
1401
        if (yl > SSIZE_MAX)
71✔
1402
                return -E2BIG;
1403

1404
        if (isempty(x))
72✔
1405
                return yl;
3✔
1406
        if (isempty(y))
69✔
1407
                return xl;
1✔
1408

1409
        t0 = new0(size_t, yl + 1);
68✔
1410
        if (!t0)
68✔
1411
                return -ENOMEM;
1412
        t1 = new0(size_t, yl + 1);
68✔
1413
        if (!t1)
68✔
1414
                return -ENOMEM;
1415
        t2 = new0(size_t, yl + 1);
68✔
1416
        if (!t2)
68✔
1417
                return -ENOMEM;
1418

1419
        for (size_t i = 0; i <= yl; i++)
681✔
1420
                t1[i] = i;
613✔
1421

1422
        for (size_t i = 0; i < xl; i++) {
360✔
1423
                t2[0] = i + 1;
292✔
1424

1425
                for (size_t j = 0; j < yl; j++) {
3,043✔
1426
                        /* Substitution */
1427
                        t2[j+1] = t1[j] + (x[i] != y[j]);
2,751✔
1428

1429
                        /* Swap */
1430
                        if (i > 0 && j > 0 && x[i-1] == y[j] && x[i] == y[j-1] && t2[j+1] > t0[j-1] + 1)
2,751✔
1431
                                t2[j+1] = t0[j-1] + 1;
4✔
1432

1433
                        /* Deletion */
1434
                        if (t2[j+1] > t1[j+1] + 1)
2,751✔
1435
                                t2[j+1] = t1[j+1] + 1;
88✔
1436

1437
                        /* Insertion */
1438
                        if (t2[j+1] > t2[j] + 1)
2,751✔
1439
                                t2[j+1] = t2[j] + 1;
446✔
1440
                }
1441

1442
                size_t *dummy = t0;
1443
                t0 = t1;
1444
                t1 = t2;
1445
                t2 = dummy;
1446
        }
1447

1448
        return t1[yl];
68✔
1449
}
1450

1451
char* strrstr(const char *haystack, const char *needle) {
7,317✔
1452
        /* Like strstr() but returns the last rather than the first occurrence of "needle" in "haystack". */
1453

1454
        if (!haystack || !needle)
7,317✔
1455
                return NULL;
1456

1457
        /* Special case: for the empty string we return the very last possible occurrence, i.e. *after* the
1458
         * last char, not before. */
1459
        if (*needle == 0)
7,314✔
1460
                return strchr(haystack, 0);
2✔
1461

1462
        for (const char *p = strstr(haystack, needle), *q; p; p = q) {
7,322✔
1463
                q = strstr(p + 1, needle);
21✔
1464
                if (!q)
21✔
1465
                        return (char *) p;
1466
        }
1467
        return NULL;
1468
}
1469

1470
size_t str_common_prefix(const char *a, const char *b) {
16✔
1471
        assert(a);
16✔
1472
        assert(b);
16✔
1473

1474
        /* Returns the length of the common prefix of the two specified strings, or SIZE_MAX in case the
1475
         * strings are fully identical. */
1476

1477
        for (size_t n = 0;; n++) {
39✔
1478
                char c = a[n];
55✔
1479
                if (c != b[n])
55✔
1480
                        return n;
1481
                if (c == 0)
42✔
1482
                        return SIZE_MAX;
1483
        }
1484
}
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