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

systemd / systemd / 15263807472

26 May 2025 08:53PM UTC coverage: 72.046% (-0.002%) from 72.048%
15263807472

push

github

yuwata
src/core/manager.c: log preset activity on first boot

This gives us a little more information about what units were enabled
or disabled on that first boot and will be useful for OS developers
tracking down the source of unit state.

An example with this enabled looks like:

```
NET: Registered PF_VSOCK protocol family
systemd[1]: Applying preset policy.
systemd[1]: Unit /etc/systemd/system/dnsmasq.service is masked, ignoring.
systemd[1]: Unit /etc/systemd/system/systemd-repart.service is masked, ignoring.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket'.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir.mount' → '/etc/systemd/system/var-mnt-workdir.mount'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir\x2dtmp.mount' → '/etc/systemd/system/var-mnt-workdir\x2dtmp.mount'.
systemd[1]: Created symlink '/etc/systemd/system/afterburn-sshkeys.target.requires/afterburn-sshkeys@core.service' → '/usr/lib/systemd/system/afterburn-sshkeys@.service'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket' → '/usr/lib/systemd/system/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket' → '/usr/lib/systemd/system/systemd-resolved-monitor.socket'.
systemd[1]: Populated /etc with preset unit settings.
```

Considering it only happens on first boot and not on every boot I think
the extra information is worth the extra verbosity in the logs just for
that boot.

5 of 6 new or added lines in 1 file covered. (83.33%)

5463 existing lines in 165 files now uncovered.

299151 of 415222 relevant lines covered (72.05%)

702386.45 hits per line

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

95.53
/src/basic/utf8.c
1
/* SPDX-License-Identifier: LGPL-2.0-or-later */
2

3
/* Parts of this file are based on the GLIB utf8 validation functions. The original copyright follows.
4
 *
5
 * gutf8.c - Operations on UTF-8 strings.
6
 * Copyright (C) 1999 Tom Tromey
7
 * Copyright (C) 2000 Red Hat, Inc.
8
 */
9

10
#include "alloc-util.h"
11
#include "gunicode.h"
12
#include "hexdecoct.h"
13
#include "string-util.h"
14
#include "utf8.h"
15

16
bool unichar_is_valid(char32_t ch) {
84,693✔
17

18
        if (ch >= 0x110000) /* End of unicode space */
84,693✔
19
                return false;
20
        if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
84,693✔
21
                return false;
22
        if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
84,693✔
23
                return false;
24
        if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
84,693✔
25
                return false;
6✔
26

27
        return true;
28
}
29

30
static bool unichar_is_control(char32_t ch) {
101,703,697✔
31

32
        /*
33
          0 to ' '-1 is the C0 range.
34
          DEL=0x7F, and DEL+1 to 0x9F is C1 range.
35
          '\t' is in C0 range, but more or less harmless and commonly used.
36
        */
37

38
        return (ch < ' ' && !IN_SET(ch, '\t', '\n')) ||
101,703,697✔
39
                (0x7F <= ch && ch <= 0x9F);
101,703,525✔
40
}
41

42
/* count of characters used to encode one unicode char */
43
static size_t utf8_encoded_expected_len(uint8_t c) {
633,129,351✔
44
        if (c < 0x80)
633,129,351✔
45
                return 1;
46
        if ((c & 0xe0) == 0xc0)
271,959✔
47
                return 2;
48
        if ((c & 0xf0) == 0xe0)
245,367✔
49
                return 3;
50
        if ((c & 0xf8) == 0xf0)
1,027✔
51
                return 4;
52
        if ((c & 0xfc) == 0xf8)
64✔
53
                return 5;
54
        if ((c & 0xfe) == 0xfc)
64✔
UNCOV
55
                return 6;
×
56

57
        return 0;
58
}
59

60
/* decode one unicode char */
61
int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
107,657,781✔
62
        char32_t unichar;
107,657,781✔
63
        size_t len;
107,657,781✔
64

65
        assert(str);
107,657,781✔
66

67
        len = utf8_encoded_expected_len(str[0]);
107,657,781✔
68

69
        switch (len) {
107,657,781✔
70
        case 1:
107,471,774✔
71
                *ret_unichar = (char32_t)str[0];
107,471,774✔
72
                return 1;
107,471,774✔
73
        case 2:
22,023✔
74
                unichar = str[0] & 0x1f;
22,023✔
75
                break;
22,023✔
76
        case 3:
163,407✔
77
                unichar = (char32_t)str[0] & 0x0f;
163,407✔
78
                break;
163,407✔
79
        case 4:
577✔
80
                unichar = (char32_t)str[0] & 0x07;
577✔
81
                break;
577✔
UNCOV
82
        case 5:
×
UNCOV
83
                unichar = (char32_t)str[0] & 0x03;
×
UNCOV
84
                break;
×
UNCOV
85
        case 6:
×
UNCOV
86
                unichar = (char32_t)str[0] & 0x01;
×
87
                break;
×
88
        default:
89
                return -EINVAL;
90
        }
91

92
        for (size_t i = 1; i < len; i++) {
536,567✔
93
                if (((char32_t)str[i] & 0xc0) != 0x80)
350,566✔
94
                        return -EINVAL;
95

96
                unichar <<= 6;
350,560✔
97
                unichar |= (char32_t)str[i] & 0x3f;
350,560✔
98
        }
99

100
        *ret_unichar = unichar;
186,001✔
101
        return len;
186,001✔
102
}
103

104
bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) {
3,319,290✔
105
        assert(str);
3,319,290✔
106

107
        for (const char *p = str; length > 0;) {
105,022,815✔
108
                int encoded_len;
101,703,698✔
109
                char32_t val;
101,703,698✔
110

111
                encoded_len = utf8_encoded_valid_unichar(p, length);
101,703,698✔
112
                if (encoded_len < 0)
101,703,698✔
113
                        return false;
173✔
114
                assert(encoded_len > 0 && (size_t) encoded_len <= length);
101,703,697✔
115

116
                if (utf8_encoded_to_unichar(p, &val) < 0 ||
101,703,697✔
117
                    unichar_is_control(val) ||
101,703,697✔
118
                    (!allow_newline && val == '\n'))
23,843,686✔
119
                        return false;
120

121
                length -= encoded_len;
101,703,525✔
122
                p += encoded_len;
101,703,525✔
123
        }
124

125
        return true;
126
}
127

128
char* utf8_is_valid_n(const char *str, size_t len_bytes) {
24,563,871✔
129
        /* Check if the string is composed of valid utf8 characters. If length len_bytes is given, stop after
130
         * len_bytes. Otherwise, stop at NUL. */
131

132
        assert(str);
24,563,871✔
133

134
        for (size_t i = 0; len_bytes != SIZE_MAX ? i < len_bytes : str[i] != '\0'; ) {
422,712,025✔
135
                int len;
398,149,283✔
136

137
                if (_unlikely_(str[i] == '\0'))
398,149,283✔
138
                        return NULL; /* embedded NUL */
139

140
                len = utf8_encoded_valid_unichar(str + i,
398,149,280✔
141
                                                 len_bytes != SIZE_MAX ? len_bytes - i : SIZE_MAX);
142
                if (_unlikely_(len < 0))
398,149,280✔
143
                        return NULL; /* invalid character */
144

145
                i += len;
398,148,154✔
146
        }
147

148
        return (char*) str;
149
}
150

151
char* utf8_escape_invalid(const char *str) {
2,568✔
152
        char *p, *s;
2,568✔
153

154
        assert(str);
2,568✔
155

156
        p = s = malloc(strlen(str) * 4 + 1);
2,568✔
157
        if (!p)
2,568✔
158
                return NULL;
159

160
        while (*str) {
59,855✔
161
                int len;
57,287✔
162

163
                len = utf8_encoded_valid_unichar(str, SIZE_MAX);
57,287✔
164
                if (len > 0) {
57,287✔
165
                        s = mempcpy(s, str, len);
57,271✔
166
                        str += len;
57,271✔
167
                } else {
168
                        s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
16✔
169
                        str += 1;
16✔
170
                }
171
        }
172

173
        *s = '\0';
2,568✔
174
        return str_realloc(p);
2,568✔
175
}
176

177
int utf8_char_console_width(const char *str) {
4,470,313✔
178
        char32_t c;
4,470,313✔
179
        int r;
4,470,313✔
180

181
        r = utf8_encoded_to_unichar(str, &c);
4,470,313✔
182
        if (r < 0)
4,470,313✔
183
                return r;
4,470,313✔
184

185
        if (c == '\t')
4,470,312✔
186
                return 8; /* Assume a tab width of 8 */
187

188
        /* TODO: we should detect combining characters */
189

190
        return unichar_iswide(c) ? 2 : 1;
4,459,749✔
191
}
192

193
char* utf8_escape_non_printable_full(const char *str, size_t console_width, bool force_ellipsis) {
551✔
194
        char *p, *s, *prev_s;
551✔
195
        size_t n = 0; /* estimated print width */
551✔
196

197
        assert(str);
551✔
198

199
        if (console_width == 0)
551✔
200
                return strdup("");
9✔
201

202
        p = s = prev_s = malloc(strlen(str) * 4 + 1);
542✔
203
        if (!p)
542✔
204
                return NULL;
205

206
        for (;;) {
20,681✔
207
                int len;
20,681✔
208
                char *saved_s = s;
20,681✔
209

210
                if (!*str) { /* done! */
20,681✔
211
                        if (force_ellipsis)
468✔
212
                                goto truncation;
73✔
213
                        else
214
                                goto finish;
395✔
215
                }
216

217
                len = utf8_encoded_valid_unichar(str, SIZE_MAX);
20,213✔
218
                if (len > 0) {
20,213✔
219
                        if (utf8_is_printable(str, len)) {
20,117✔
220
                                int w;
19,954✔
221

222
                                w = utf8_char_console_width(str);
19,954✔
223
                                assert(w >= 0);
19,954✔
224
                                if (n + w > console_width)
19,954✔
225
                                        goto truncation;
34✔
226

227
                                s = mempcpy(s, str, len);
19,920✔
228
                                str += len;
19,920✔
229
                                n += w;
19,920✔
230

231
                        } else {
232
                                for (; len > 0; len--) {
288✔
233
                                        if (n + 4 > console_width)
163✔
234
                                                goto truncation;
38✔
235

236
                                        *(s++) = '\\';
125✔
237
                                        *(s++) = 'x';
125✔
238
                                        *(s++) = hexchar((int) *str >> 4);
125✔
239
                                        *(s++) = hexchar((int) *str);
125✔
240

241
                                        str += 1;
125✔
242
                                        n += 4;
125✔
243
                                }
244
                        }
245
                } else {
246
                        if (n + 1 > console_width)
96✔
247
                                goto truncation;
2✔
248

249
                        s = mempcpy(s, UTF8_REPLACEMENT_CHARACTER, strlen(UTF8_REPLACEMENT_CHARACTER));
94✔
250
                        str += 1;
94✔
251
                        n += 1;
94✔
252
                }
253

254
                prev_s = saved_s;
255
        }
256

257
 truncation:
147✔
258
        /* Try to go back one if we don't have enough space for the ellipsis */
259
        if (n + 1 > console_width)
147✔
260
                s = prev_s;
88✔
261

262
        s = mempcpy(s, "…", strlen("…"));
147✔
263

264
 finish:
542✔
265
        *s = '\0';
542✔
266
        return str_realloc(p);
542✔
267
}
268

269
char* ascii_is_valid_n(const char *str, size_t len) {
48,968✔
270
        /* Check whether the string consists of valid ASCII bytes, i.e values between 1 and 127, inclusive.
271
         * Stops at len, or NUL byte if len is SIZE_MAX. */
272

273
        assert(str);
48,968✔
274

275
        for (size_t i = 0; len != SIZE_MAX ? i < len : str[i] != '\0'; i++)
1,304,526✔
276
                if ((unsigned char) str[i] >= 128 || str[i] == '\0')
1,258,904✔
277
                        return NULL;
278

279
        return (char*) str;
280
}
281

282
int utf8_to_ascii(const char *str, char replacement_char, char **ret) {
34✔
283
        /* Convert to a string that has only ASCII chars, replacing anything that is not ASCII
284
         * by replacement_char. */
285

286
        _cleanup_free_ char *ans = new(char, strlen(str) + 1);
68✔
287
        if (!ans)
34✔
288
                return -ENOMEM;
289

290
        char *q = ans;
291

292
        for (const char *p = str; *p; q++) {
211✔
293
                int l;
179✔
294

295
                l = utf8_encoded_valid_unichar(p, SIZE_MAX);
179✔
296
                if (l < 0)  /* Non-UTF-8, let's not even try to propagate the garbage */
179✔
297
                        return l;
298

299
                if (l == 1)
177✔
300
                        *q = *p;
160✔
301
                else
302
                        /* non-ASCII, we need to replace it */
303
                        *q = replacement_char;
17✔
304

305
                p += l;
177✔
306
        }
307
        *q = '\0';
32✔
308

309
        *ret = TAKE_PTR(ans);
32✔
310
        return 0;
32✔
311
}
312

313
/**
314
 * utf8_encode_unichar() - Encode single UCS-4 character as UTF-8
315
 * @out_utf8: output buffer of at least 4 bytes or NULL
316
 * @g: UCS-4 character to encode
317
 *
318
 * This encodes a single UCS-4 character as UTF-8 and writes it into @out_utf8.
319
 * The length of the character is returned. It is not zero-terminated! If the
320
 * output buffer is NULL, only the length is returned.
321
 *
322
 * Returns: The length in bytes that the UTF-8 representation does or would
323
 *          occupy.
324
 */
325
size_t utf8_encode_unichar(char *out_utf8, char32_t g) {
68,024✔
326

327
        if (g < (1 << 7)) {
68,024✔
328
                if (out_utf8)
67,900✔
329
                        out_utf8[0] = g & 0x7f;
67,900✔
330
                return 1;
67,900✔
331
        } else if (g < (1 << 11)) {
124✔
332
                if (out_utf8) {
17✔
333
                        out_utf8[0] = 0xc0 | ((g >> 6) & 0x1f);
17✔
334
                        out_utf8[1] = 0x80 | (g & 0x3f);
17✔
335
                }
336
                return 2;
17✔
337
        } else if (g < (1 << 16)) {
107✔
338
                if (out_utf8) {
96✔
339
                        out_utf8[0] = 0xe0 | ((g >> 12) & 0x0f);
96✔
340
                        out_utf8[1] = 0x80 | ((g >> 6) & 0x3f);
96✔
341
                        out_utf8[2] = 0x80 | (g & 0x3f);
96✔
342
                }
343
                return 3;
96✔
344
        } else if (g < (1 << 21)) {
11✔
345
                if (out_utf8) {
11✔
346
                        out_utf8[0] = 0xf0 | ((g >> 18) & 0x07);
11✔
347
                        out_utf8[1] = 0x80 | ((g >> 12) & 0x3f);
11✔
348
                        out_utf8[2] = 0x80 | ((g >> 6) & 0x3f);
11✔
349
                        out_utf8[3] = 0x80 | (g & 0x3f);
11✔
350
                }
351
                return 4;
11✔
352
        }
353

354
        return 0;
355
}
356

357
char* utf16_to_utf8(const char16_t *s, size_t length /* bytes! */) {
1,917✔
358
        const uint8_t *f;
1,917✔
359
        char *r, *t;
1,917✔
360

361
        if (length == 0)
1,917✔
UNCOV
362
                return new0(char, 1);
×
363

364
        assert(s);
1,917✔
365

366
        if (length == SIZE_MAX) {
1,917✔
367
                length = char16_strlen(s);
6✔
368

369
                if (length > SIZE_MAX/2)
6✔
370
                        return NULL; /* overflow */
371

372
                length *= 2;
6✔
373
        }
374

375
        /* Input length is in bytes, i.e. the shortest possible character takes 2 bytes. Each unicode character may
376
         * take up to 4 bytes in UTF-8. Let's also account for a trailing NUL byte. */
377
        if (length > (SIZE_MAX - 1) / 2)
1,917✔
378
                return NULL; /* overflow */
379

380
        r = new(char, length * 2 + 1);
1,917✔
381
        if (!r)
1,917✔
382
                return NULL;
383

384
        f = (const uint8_t*) s;
385
        t = r;
386

387
        while (f + 1 < (const uint8_t*) s + length) {
69,752✔
388
                char16_t w1, w2;
67,835✔
389

390
                /* see RFC 2781 section 2.2 */
391

392
                w1 = f[1] << 8 | f[0];
67,835✔
393
                f += 2;
67,835✔
394

395
                if (!utf16_is_surrogate(w1)) {
67,835✔
396
                        t += utf8_encode_unichar(t, w1);
67,828✔
397
                        continue;
67,828✔
398
                }
399

400
                if (utf16_is_trailing_surrogate(w1))
7✔
401
                        continue; /* spurious trailing surrogate, ignore */
1✔
402

403
                if (f + 1 >= (const uint8_t*) s + length)
6✔
404
                        break;
405

406
                w2 = f[1] << 8 | f[0];
6✔
407
                f += 2;
6✔
408

409
                if (!utf16_is_trailing_surrogate(w2)) {
6✔
410
                        f -= 2;
1✔
411
                        continue; /* surrogate missing its trailing surrogate, ignore */
1✔
412
                }
413

414
                t += utf8_encode_unichar(t, utf16_surrogate_pair_to_unichar(w1, w2));
5✔
415
        }
416

417
        *t = 0;
1,917✔
418
        return r;
1,917✔
419
}
420

421
size_t utf16_encode_unichar(char16_t *out, char32_t c) {
16✔
422

423
        /* Note that this encodes as little-endian. */
424

425
        switch (c) {
16✔
426

427
        case 0 ... 0xd7ffU:
12✔
428
        case 0xe000U ... 0xffffU:
429
                out[0] = htole16(c);
12✔
430
                return 1;
12✔
431

432
        case 0x10000U ... 0x10ffffU:
4✔
433
                c -= 0x10000U;
4✔
434
                out[0] = htole16((c >> 10) + 0xd800U);
4✔
435
                out[1] = htole16((c & 0x3ffU) + 0xdc00U);
4✔
436
                return 2;
4✔
437

438
        default: /* A surrogate (invalid) */
439
                return 0;
440
        }
441
}
442

443
char16_t *utf8_to_utf16(const char *s, size_t length) {
139✔
444
        char16_t *n, *p;
139✔
445
        int r;
139✔
446

447
        if (length == 0)
139✔
UNCOV
448
                return new0(char16_t, 1);
×
449

450
        assert(s);
139✔
451

452
        if (length == SIZE_MAX)
139✔
453
                length = strlen(s);
138✔
454

455
        if (length > SIZE_MAX - 1)
138✔
456
                return NULL; /* overflow */
457

458
        n = new(char16_t, length + 1);
139✔
459
        if (!n)
139✔
460
                return NULL;
461

462
        p = n;
463

464
        for (size_t i = 0; i < length;) {
1,373✔
465
                char32_t unichar;
1,234✔
466
                size_t e;
1,234✔
467

468
                e = utf8_encoded_expected_len(s[i]);
1,234✔
469
                if (e <= 1) /* Invalid and single byte characters are copied as they are */
1,234✔
470
                        goto copy;
1,218✔
471

472
                if (i + e > length) /* sequence longer than input buffer, then copy as-is */
16✔
UNCOV
473
                        goto copy;
×
474

475
                r = utf8_encoded_to_unichar(s + i, &unichar);
16✔
476
                if (r < 0) /* sequence invalid, then copy as-is */
16✔
UNCOV
477
                        goto copy;
×
478

479
                p += utf16_encode_unichar(p, unichar);
16✔
480
                i += e;
16✔
481
                continue;
16✔
482

483
        copy:
1,218✔
484
                *(p++) = htole16(s[i++]);
1,218✔
485
        }
486

487
        *p = 0;
139✔
488
        return n;
139✔
489
}
490

491
size_t char16_strlen(const char16_t *s) {
403✔
492
        size_t n = 0;
403✔
493

494
        assert(s);
403✔
495

496
        while (*s != 0)
4,818✔
497
                n++, s++;
4,415✔
498

499
        return n;
403✔
500
}
501

502
size_t char16_strsize(const char16_t *s) {
3✔
503
        return s ? (char16_strlen(s) + 1) * sizeof(*s) : 0;
3✔
504
}
505

506
/* expected size used to encode one unicode char */
507
static int utf8_unichar_to_encoded_len(char32_t unichar) {
84,683✔
508

509
        if (unichar < 0x80)
84,683✔
510
                return 1;
511
        if (unichar < 0x800)
84,683✔
512
                return 2;
513
        if (unichar < 0x10000)
80,441✔
514
                return 3;
515
        if (unichar < 0x200000)
380✔
516
                return 4;
UNCOV
517
        if (unichar < 0x4000000)
×
UNCOV
518
                return 5;
×
519

520
        return 6;
521
}
522

523
/* validate one encoded unicode char and return its length */
524
int utf8_encoded_valid_unichar(const char *str, size_t length /* bytes */) {
525,470,336✔
525
        char32_t unichar;
525,470,336✔
526
        size_t len;
525,470,336✔
527
        int r;
525,470,336✔
528

529
        assert(str);
525,470,336✔
530
        assert(length > 0);
525,470,336✔
531

532
        /* We read until NUL, at most length bytes. SIZE_MAX may be used to disable the length check. */
533

534
        len = utf8_encoded_expected_len(str[0]);
525,470,336✔
535
        if (len == 0)
525,470,336✔
536
                return -EINVAL;
525,470,336✔
537

538
        /* Do we have a truncated multi-byte character? */
539
        if (len > length)
525,470,272✔
540
                return -EINVAL;
541

542
        /* ascii is valid */
543
        if (len == 1)
525,470,264✔
544
                return 1;
545

546
        /* check if expected encoded chars are available */
547
        for (size_t i = 0; i < len; i++)
337,696✔
548
                if ((str[i] & 0x80) != 0x80)
253,008✔
549
                        return -EINVAL;
550

551
        r = utf8_encoded_to_unichar(str, &unichar);
84,688✔
552
        if (r < 0)
84,688✔
553
                return r;
554

555
        /* check if encoded length matches encoded value */
556
        if (utf8_unichar_to_encoded_len(unichar) != (int) len)
84,683✔
557
                return -EINVAL;
558

559
        /* check if value has valid range */
560
        if (!unichar_is_valid(unichar))
84,683✔
561
                return -EINVAL;
6✔
562

563
        return (int) len;
564
}
565

566
size_t utf8_n_codepoints(const char *str) {
6✔
567
        size_t n = 0;
6✔
568

569
        /* Returns the number of UTF-8 codepoints in this string, or SIZE_MAX if the string is not valid UTF-8. */
570

571
        while (*str != 0) {
34✔
572
                int k;
29✔
573

574
                k = utf8_encoded_valid_unichar(str, SIZE_MAX);
29✔
575
                if (k < 0)
29✔
576
                        return SIZE_MAX;
577

578
                str += k;
28✔
579
                n++;
28✔
580
        }
581

582
        return n;
583
}
584

585
size_t utf8_console_width(const char *str) {
300,606✔
586

587
        if (isempty(str))
300,606✔
588
                return 0;
589

590
        /* Returns the approximate width a string will take on screen when printed on a character cell
591
         * terminal/console. */
592

593
        size_t n = 0;
594
        while (*str) {
4,110,264✔
595
                int w;
3,822,391✔
596

597
                w = utf8_char_console_width(str);
3,822,391✔
598
                if (w < 0)
3,822,391✔
599
                        return SIZE_MAX;
600

601
                n += w;
3,822,390✔
602
                str = utf8_next_char(str);
3,822,390✔
603
        }
604

605
        return n;
606
}
607

608
size_t utf8_last_length(const char *s, size_t n) {
9✔
609
        int r;
9✔
610

611
        assert(s);
9✔
612

613
        if (n == SIZE_MAX)
9✔
614
                n = strlen(s);
7✔
615

616
        /* Determines length in bytes of last UTF-8 codepoint in string. If the string is empty, returns
617
         * zero. Treats invalid UTF-8 codepoints as 1 sized ones. */
618

619
        for (size_t last = 0;;) {
19✔
620
                if (n == 0)
28✔
621
                        return last;
9✔
622

623
                r = utf8_encoded_valid_unichar(s, n);
19✔
624
                if (r <= 0)
19✔
UNCOV
625
                        r = 1; /* treat invalid UTF-8 as byte-wide */
×
626

627
                s += r;
19✔
628
                n -= r;
19✔
629
                last = r;
19✔
630
        }
631
}
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