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

systemd / systemd / 29132483780

10 Jul 2026 08:43PM UTC coverage: 72.912% (+0.2%) from 72.702%
29132483780

push

github

bluca
man: run forgotten 'update-man-rules'

344600 of 472622 relevant lines covered (72.91%)

1365091.83 hits per line

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

56.28
/src/sysupdate/sysupdate-pattern.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include "alloc-util.h"
4
#include "hexdecoct.h"
5
#include "list.h"
6
#include "log.h"
7
#include "parse-util.h"
8
#include "path-util.h"
9
#include "string-util.h"
10
#include "strv.h"
11
#include "sysupdate-instance.h"
12
#include "sysupdate-pattern.h"
13
#include "time-util.h"
14

15
typedef enum PatternElementType {
16
        PATTERN_LITERAL,
17
        PATTERN_VERSION,
18
        PATTERN_PARTITION_UUID,
19
        PATTERN_PARTITION_FLAGS,
20
        PATTERN_MTIME,
21
        PATTERN_MODE,
22
        PATTERN_SIZE,
23
        PATTERN_TRIES_DONE,
24
        PATTERN_TRIES_LEFT,
25
        PATTERN_NO_AUTO,
26
        PATTERN_READ_ONLY,
27
        PATTERN_GROWFS,
28
        PATTERN_SHA256SUM,
29
        PATTERN_SLASH,
30
        _PATTERN_ELEMENT_TYPE_MAX,
31
        _PATTERN_ELEMENT_TYPE_INVALID = -EINVAL,
32
} PatternElementType;
33

34
typedef struct PatternElement PatternElement;
35

36
struct PatternElement {
37
        PatternElementType type;
38
        LIST_FIELDS(PatternElement, elements);
39
        char literal[];
40
};
41

42
static PatternElement *pattern_element_free_all(PatternElement *e) {
341,630✔
43
        LIST_CLEAR(elements, e, free);
1,410,447✔
44

45
        return NULL;
341,630✔
46
}
47

48
DEFINE_TRIVIAL_CLEANUP_FUNC(PatternElement*, pattern_element_free_all);
705,575✔
49

50
static PatternElementType pattern_element_type_from_char(char c) {
381,321✔
51
        switch (c) {
381,321✔
52
        case 'v':
53
                return PATTERN_VERSION;
54
        case 'u':
×
55
                return PATTERN_PARTITION_UUID;
×
56
        case 'f':
×
57
                return PATTERN_PARTITION_FLAGS;
×
58
        case 't':
×
59
                return PATTERN_MTIME;
×
60
        case 'm':
×
61
                return PATTERN_MODE;
×
62
        case 's':
×
63
                return PATTERN_SIZE;
×
64
        case 'd':
5,884✔
65
                return PATTERN_TRIES_DONE;
5,884✔
66
        case 'l':
11,492✔
67
                return PATTERN_TRIES_LEFT;
11,492✔
68
        case 'a':
×
69
                return PATTERN_NO_AUTO;
×
70
        case 'r':
×
71
                return PATTERN_READ_ONLY;
×
72
        case 'g':
×
73
                return PATTERN_GROWFS;
×
74
        case 'h':
×
75
                return PATTERN_SHA256SUM;
×
76
        default:
×
77
                return _PATTERN_ELEMENT_TYPE_INVALID;
×
78
        }
79
}
80

81
static bool valid_char(char x) {
4,742,869✔
82

83
        /* Let's refuse control characters here, and let's reserve some characters typically used in pattern
84
         * languages so that we can use them later, possibly. */
85

86
        if ((unsigned) x < ' ' || x >= 127)
4,742,869✔
87
                return false;
88

89
        return !IN_SET(x, '$', '*', '?', '[', ']', '!', '\\', '|');
4,742,869✔
90
}
91

92
static int pattern_split(
363,945✔
93
                const char *pattern,
94
                PatternElement **ret) {
95

96
        _cleanup_(pattern_element_free_allp) PatternElement *first = NULL;
363,945✔
97
        bool at = false, last_literal = true, last_slash = false;
363,945✔
98
        PatternElement *last = NULL;
363,945✔
99
        uint64_t mask_found = 0;
363,945✔
100
        size_t l, k = 0;
363,945✔
101

102
        assert(pattern);
363,945✔
103

104
        l = strlen(pattern);
363,945✔
105

106
        for (const char *e = pattern; *e != 0; e++) {
5,885,960✔
107
                if (*e == '@') {
5,522,015✔
108
                        if (!at) {
381,321✔
109
                                at = true;
381,321✔
110
                                continue;
381,321✔
111
                        }
112

113
                        /* Two at signs in a sequence, write out one */
114
                        at = false;
115

116
                } else if (at) {
5,140,694✔
117
                        PatternElementType t;
381,321✔
118
                        uint64_t bit;
381,321✔
119

120
                        t = pattern_element_type_from_char(*e);
381,321✔
121
                        if (t < 0)
381,321✔
122
                                return log_debug_errno(t, "Unknown pattern field marker '@%c'.", *e);
×
123

124
                        bit = UINT64_C(1) << t;
381,321✔
125
                        if (mask_found & bit)
381,321✔
126
                                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Pattern field marker '@%c' appears twice in pattern.", *e);
×
127

128
                        /* We insist that two pattern field markers are separated by some literal string that
129
                         * we can use to separate the fields when parsing. */
130
                        if (!last_literal && !last_slash)
381,321✔
131
                                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Found two pattern field markers without separating literal.");
×
132

133
                        if (ret) {
381,321✔
134
                                PatternElement *z;
355,310✔
135

136
                                z = malloc(offsetof(PatternElement, literal));
355,310✔
137
                                if (!z)
355,310✔
138
                                        return -ENOMEM;
139

140
                                z->type = t;
355,310✔
141
                                LIST_INSERT_AFTER(elements, first, last, z);
355,310✔
142
                                last = z;
143
                        }
144

145
                        mask_found |= bit;
381,321✔
146
                        last_slash = last_literal = at = false;
381,321✔
147
                        continue;
381,321✔
148
                }
149

150
                if (*e == '/') {
4,759,373✔
151
                        if (ret) {
16,504✔
152
                                PatternElement *z;
14,033✔
153

154
                                z = malloc(offsetof(PatternElement, literal));
14,033✔
155
                                if (!z)
14,033✔
156
                                        return -ENOMEM;
157

158
                                z->type = PATTERN_SLASH;
14,033✔
159
                                LIST_INSERT_AFTER(elements, first, last, z);
14,033✔
160
                                last = z;
161
                        }
162

163
                        last_literal = false;
16,504✔
164
                        last_slash = true;
16,504✔
165
                        continue ;
16,504✔
166
                }
167

168
                if (!valid_char(*e))
4,742,869✔
169
                        return log_debug_errno(
×
170
                                        SYNTHETIC_ERRNO(EBADRQC),
171
                                        "Invalid character 0x%0x in pattern, refusing.",
172
                                        (unsigned) *e);
173

174
                last_literal = true;
4,742,869✔
175
                last_slash = false;
4,742,869✔
176

177
                if (!ret)
4,742,869✔
178
                        continue;
305,785✔
179

180
                if (!last || last->type != PATTERN_LITERAL) {
4,437,084✔
181
                        PatternElement *z;
699,474✔
182

183
                        z = malloc0(offsetof(PatternElement, literal) + l + 1); /* l is an upper bound to all literal elements */
699,474✔
184
                        if (!z)
699,474✔
185
                                return -ENOMEM;
186

187
                        z->type = PATTERN_LITERAL;
699,474✔
188
                        k = 0;
699,474✔
189

190
                        LIST_INSERT_AFTER(elements, first, last, z);
699,474✔
191
                        last = z;
192
                }
193

194
                assert(last);
699,474✔
195
                assert(last->type == PATTERN_LITERAL);
4,437,084✔
196

197
                last->literal[k++] = *e;
4,437,084✔
198
        }
199

200
        if (at)
363,945✔
201
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Trailing @ character found, refusing.");
×
202
        if (!(mask_found & (UINT64_C(1) << PATTERN_VERSION)))
363,945✔
203
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Version field marker '@v' not specified in pattern, refusing.");
×
204

205
        if (ret)
363,945✔
206
                *ret = TAKE_PTR(first);
341,630✔
207

208
        return 0;
209
}
210

211
int pattern_match(const char *pattern, const char *s, InstanceMetadata *ret) {
339,648✔
212
        _cleanup_(instance_metadata_destroy) InstanceMetadata found = INSTANCE_METADATA_NULL;
339,648✔
213
        _cleanup_(pattern_element_free_allp) PatternElement *elements = NULL;
339,648✔
214
        const char *p;
339,648✔
215
        int r;
339,648✔
216

217
        assert(pattern);
339,648✔
218
        assert(s);
339,648✔
219

220
        r = pattern_split(pattern, &elements);
339,648✔
221
        if (r < 0)
339,648✔
222
                return r;
223

224
        p = s;
339,648✔
225
        LIST_FOREACH(elements, e, elements) {
599,673✔
226
                _cleanup_free_ char *t = NULL;
348,528✔
227
                const char *n;
530,922✔
228

229
                if (e->type == PATTERN_SLASH) {
530,922✔
230
                        if (*p == '/') {
8,833✔
231
                                ++p;
4,497✔
232
                                continue;
4,497✔
233
                        } else if (*p == '\0')
4,336✔
234
                                goto retry;
4,336✔
235
                        else
236
                                goto nope;
×
237
                }
238

239
                if (e->type == PATTERN_LITERAL) {
522,089✔
240
                        const char *k;
415,928✔
241

242
                        /* Skip literal fields */
243
                        k = startswith(p, e->literal);
415,928✔
244
                        if (!k)
415,928✔
245
                                goto nope;
242,367✔
246

247
                        p = k;
173,561✔
248
                        continue;
173,561✔
249
                }
250

251
                if (e->elements_next) {
106,161✔
252
                        if (e->elements_next->type == PATTERN_LITERAL) {
95,988✔
253
                                n = strstr(p, e->elements_next->literal);
95,975✔
254
                                if (!n)
95,975✔
255
                                        goto nope;
21,978✔
256
                        } else if (e->elements_next->type == PATTERN_SLASH)
13✔
257
                                n = strchrnul(p, '/');
13✔
258
                        else
259
                                assert_not_reached();
×
260

261
                } else
262
                        /* End of the string */
263
                        assert_se(n = strchr(p, 0));
10,173✔
264
                t = strndup(p, n - p);
84,183✔
265
                if (!t)
84,183✔
266
                        return -ENOMEM;
267

268
                switch (e->type) {
84,183✔
269

270
                case PATTERN_VERSION:
77,547✔
271
                        if (!version_is_valid(t)) {
77,547✔
272
                                log_debug("Version string is not valid, refusing: %s", t);
4✔
273
                                goto nope;
4✔
274
                        }
275

276
                        assert(!found.version);
77,543✔
277
                        found.version = TAKE_PTR(t);
77,543✔
278
                        break;
77,543✔
279

280
                case PATTERN_PARTITION_UUID: {
×
281
                        sd_id128_t id;
×
282

283
                        if (sd_id128_from_string(t, &id) < 0)
×
284
                                goto nope;
×
285

286
                        assert(!found.partition_uuid_set);
×
287
                        found.partition_uuid = id;
×
288
                        found.partition_uuid_set = true;
×
289
                        break;
×
290
                }
291

292
                case PATTERN_PARTITION_FLAGS: {
×
293
                        uint64_t f;
×
294

295
                        if (safe_atoux64(t, &f) < 0)
×
296
                                goto nope;
×
297

298
                        if (found.partition_flags_set && found.partition_flags != f)
×
299
                                goto nope;
×
300

301
                        assert(!found.partition_flags_set);
×
302
                        found.partition_flags = f;
×
303
                        found.partition_flags_set = true;
×
304
                        break;
×
305
                }
306

307
                case PATTERN_MTIME: {
×
308
                        uint64_t v;
×
309

310
                        if (safe_atou64(t, &v) < 0)
×
311
                                goto nope;
×
312
                        if (v == USEC_INFINITY) /* Don't permit our internal special infinity value */
×
313
                                goto nope;
×
314
                        if (v / 1000000U > TIME_T_MAX) /* Make sure this fits in a timespec structure */
×
315
                                goto nope;
316

317
                        assert(found.mtime == USEC_INFINITY);
×
318
                        found.mtime = v;
×
319
                        break;
×
320
                }
321

322
                case PATTERN_MODE: {
×
323
                        mode_t m;
×
324

325
                        r = parse_mode(t, &m);
×
326
                        if (r < 0)
×
327
                                goto nope;
×
328
                        if (m & ~0775) /* Don't allow world-writable files or suid files to be generated this way */
×
329
                                goto nope;
×
330

331
                        assert(found.mode == MODE_INVALID);
×
332
                        found.mode = m;
×
333
                        break;
×
334
                }
335

336
                case PATTERN_SIZE: {
×
337
                        uint64_t u;
×
338

339
                        r = safe_atou64(t, &u);
×
340
                        if (r < 0)
×
341
                                goto nope;
×
342
                        if (u == UINT64_MAX)
×
343
                                goto nope;
×
344

345
                        assert(found.size == UINT64_MAX);
×
346
                        found.size = u;
×
347
                        break;
×
348
                }
349

350
                case PATTERN_TRIES_DONE: {
2,212✔
351
                        uint64_t u;
2,212✔
352

353
                        r = safe_atou64(t, &u);
2,212✔
354
                        if (r < 0)
2,212✔
355
                                goto nope;
×
356
                        if (u == UINT64_MAX)
2,212✔
357
                                goto nope;
×
358

359
                        assert(found.tries_done == UINT64_MAX);
2,212✔
360
                        found.tries_done = u;
2,212✔
361
                        break;
2,212✔
362
                }
363

364
                case PATTERN_TRIES_LEFT: {
4,424✔
365
                        uint64_t u;
4,424✔
366

367
                        r = safe_atou64(t, &u);
4,424✔
368
                        if (r < 0)
4,424✔
369
                                goto nope;
2,212✔
370
                        if (u == UINT64_MAX)
2,212✔
371
                                goto nope;
×
372

373
                        assert(found.tries_left == UINT64_MAX);
2,212✔
374
                        found.tries_left = u;
2,212✔
375
                        break;
2,212✔
376
                }
377

378
                case PATTERN_NO_AUTO:
×
379
                        r = parse_boolean(t);
×
380
                        if (r < 0)
×
381
                                goto nope;
×
382

383
                        assert(found.no_auto < 0);
×
384
                        found.no_auto = r;
×
385
                        break;
×
386

387
                case PATTERN_READ_ONLY:
×
388
                        r = parse_boolean(t);
×
389
                        if (r < 0)
×
390
                                goto nope;
×
391

392
                        assert(found.read_only < 0);
×
393
                        found.read_only = r;
×
394
                        break;
×
395

396
                case PATTERN_GROWFS:
×
397
                        r = parse_boolean(t);
×
398
                        if (r < 0)
×
399
                                goto nope;
×
400

401
                        assert(found.growfs < 0);
×
402
                        found.growfs = r;
×
403
                        break;
×
404

405
                case PATTERN_SHA256SUM: {
×
406
                        _cleanup_free_ void *d = NULL;
×
407
                        size_t l;
×
408

409
                        if (strlen(t) != sizeof(found.sha256sum) * 2)
×
410
                                goto nope;
×
411

412
                        r = unhexmem_full(t, sizeof(found.sha256sum) * 2, /* secure= */ false, &d, &l);
×
413
                        if (r == -ENOMEM)
×
414
                                return r;
×
415
                        if (r < 0)
×
416
                                goto nope;
×
417

418
                        assert(!found.sha256sum_set);
×
419
                        assert(l == sizeof(found.sha256sum));
×
420
                        memcpy(found.sha256sum, d, l);
×
421
                        found.sha256sum_set = true;
×
422
                        break;
×
423
                }
424

425
                default:
×
426
                        assert_not_reached();
×
427
                }
428

429
                p = n;
81,967✔
430
        }
431

432
        /* We matched the whole pattern, but if the string continues over the end of the pattern, refuse */
433
        if (*p != '\0')
68,751✔
434
                goto nope;
10,844✔
435

436
        if (ret) {
57,907✔
437
                *ret = found;
57,886✔
438
                found = (InstanceMetadata) INSTANCE_METADATA_NULL;
57,886✔
439
        }
440

441
        return PATTERN_MATCH_YES;
442

443
nope:
277,405✔
444
        if (ret)
277,405✔
445
                *ret = (InstanceMetadata) INSTANCE_METADATA_NULL;
277,395✔
446

447
        return PATTERN_MATCH_NO;
448

449
retry:
4,336✔
450
        if (ret)
4,336✔
451
                *ret = (InstanceMetadata) INSTANCE_METADATA_NULL;
4,336✔
452

453
        return PATTERN_MATCH_RETRY;
454
}
455

456
int pattern_match_many(char **patterns, const char *s, InstanceMetadata *ret) {
330,461✔
457
        _cleanup_(instance_metadata_destroy) InstanceMetadata matched = INSTANCE_METADATA_NULL;
330,461✔
458
        bool have_match = false, retry_descent = false;
330,461✔
459
        int r;
330,461✔
460

461
        /* Evaluate all patterns and report the combined result: a direct match wins and provides the
462
         * extracted fields, but when another pattern would descend into a subdirectory this is reported
463
         * too, so the caller can descend as fallback when the direct match is no valid candidate. */
464
        STRV_FOREACH(p, patterns) {
670,078✔
465
                _cleanup_(instance_metadata_destroy) InstanceMetadata found = INSTANCE_METADATA_NULL;
339,617✔
466
                const char *pat = *p, *input_path;
339,617✔
467

468
                if (have_match && retry_descent)
339,617✔
469
                        break; /* nothing more to learn */
470

471
                /* A glob directory prefix on a pattern means to match the rest of the pattern against the
472
                 * last path component only (the basename, "find this file anywhere under the source tree") */
473
                if (pattern_skip_glob_directory_prefix(&pat))
339,617✔
474
                        input_path = last_path_component(s);
313✔
475
                else
476
                        input_path = s;
477

478
                r = pattern_match(pat, input_path, &found);
339,617✔
479
                if (r < 0)
339,617✔
480
                        return r;
×
481
                if (r == PATTERN_MATCH_YES && !have_match) {
339,617✔
482
                        matched = TAKE_GENERIC(found, InstanceMetadata, INSTANCE_METADATA_NULL);
55,674✔
483
                        have_match = true;
55,674✔
484
                }
485
                if (r == PATTERN_MATCH_RETRY)
339,617✔
486
                        retry_descent = true;
4,336✔
487
        }
488

489
        if (ret)
330,461✔
490
                *ret = TAKE_GENERIC(matched, InstanceMetadata, INSTANCE_METADATA_NULL);
330,441✔
491

492
        if (have_match)
330,461✔
493
                return retry_descent ? PATTERN_MATCH_YES_AND_RETRY : PATTERN_MATCH_YES;
55,674✔
494
        return retry_descent ? PATTERN_MATCH_RETRY : PATTERN_MATCH_NO;
274,787✔
495
}
496

497
bool pattern_skip_glob_directory_prefix(const char **pattern) {
398,631✔
498
        assert(pattern);
398,631✔
499
        assert(*pattern);
398,631✔
500

501
        const char *e = startswith(*pattern, "**/");
398,631✔
502
        if (!e)
398,631✔
503
                return false;
504

505
        *pattern = e;
355✔
506
        return true;
355✔
507
}
508

509
int pattern_valid(const char *pattern) {
22,315✔
510
        int r;
22,315✔
511

512
        r = pattern_split(pattern, NULL);
22,315✔
513
        if (r == -EINVAL)
22,315✔
514
                return false;
515
        if (r < 0)
22,315✔
516
                return r;
×
517

518
        return true;
519
}
520

521
int pattern_format(
1,982✔
522
                const char *pattern,
523
                const InstanceMetadata *fields,
524
                char **ret) {
525

526
        _cleanup_(pattern_element_free_allp) PatternElement *elements = NULL;
×
527
        _cleanup_free_ char *j = NULL;
1,982✔
528
        int r;
1,982✔
529

530
        assert(pattern);
1,982✔
531
        assert(fields);
1,982✔
532
        assert(ret);
1,982✔
533

534
        r = pattern_split(pattern, &elements);
1,982✔
535
        if (r < 0)
1,982✔
536
                return r;
537

538
        LIST_FOREACH(elements, e, elements) {
8,807✔
539

540
                switch (e->type) {
6,825✔
541

542
                case PATTERN_SLASH:
304✔
543
                        if (!strextend(&j, "/"))
304✔
544
                                return -ENOMEM;
545

546
                        break;
547

548
                case PATTERN_LITERAL:
3,987✔
549
                        if (!strextend(&j, e->literal))
3,987✔
550
                                return -ENOMEM;
551

552
                        break;
553

554
                case PATTERN_VERSION:
1,982✔
555
                        if (!fields->version)
1,982✔
556
                                return -ENXIO;
557

558
                        if (!strextend(&j, fields->version))
1,982✔
559
                                return -ENOMEM;
560
                        break;
561

562
                case PATTERN_PARTITION_UUID: {
×
563
                        char formatted[SD_ID128_STRING_MAX];
×
564

565
                        if (!fields->partition_uuid_set)
×
566
                                return -ENXIO;
×
567

568
                        if (!strextend(&j, sd_id128_to_string(fields->partition_uuid, formatted)))
×
569
                                return -ENOMEM;
570

571
                        break;
×
572
                }
573

574
                case PATTERN_PARTITION_FLAGS:
×
575
                        if (!fields->partition_flags_set)
×
576
                                return -ENXIO;
577

578
                        r = strextendf(&j, "%" PRIx64, fields->partition_flags);
×
579
                        if (r < 0)
×
580
                                return r;
581

582
                        break;
583

584
                case PATTERN_MTIME:
×
585
                        if (fields->mtime == USEC_INFINITY)
×
586
                                return -ENXIO;
587

588
                        r = strextendf(&j, "%" PRIu64, fields->mtime);
×
589
                        if (r < 0)
×
590
                                return r;
591

592
                        break;
593

594
                case PATTERN_MODE:
×
595
                        if (fields->mode == MODE_INVALID)
×
596
                                return -ENXIO;
597

598
                        r = strextendf(&j, "%03o", fields->mode);
×
599
                        if (r < 0)
×
600
                                return r;
601

602
                        break;
603

604
                case PATTERN_SIZE:
×
605
                        if (fields->size == UINT64_MAX)
×
606
                                return -ENXIO;
607

608
                        r = strextendf(&j, "%" PRIu64, fields->size);
×
609
                        if (r < 0)
×
610
                                return r;
611
                        break;
612

613
                case PATTERN_TRIES_DONE:
276✔
614
                        if (fields->tries_done == UINT64_MAX)
276✔
615
                                return -ENXIO;
616

617
                        r = strextendf(&j, "%" PRIu64, fields->tries_done);
276✔
618
                        if (r < 0)
276✔
619
                                return r;
620
                        break;
621

622
                case PATTERN_TRIES_LEFT:
276✔
623
                        if (fields->tries_left == UINT64_MAX)
276✔
624
                                return -ENXIO;
625

626
                        r = strextendf(&j, "%" PRIu64, fields->tries_left);
276✔
627
                        if (r < 0)
276✔
628
                                return r;
629
                        break;
630

631
                case PATTERN_NO_AUTO:
×
632
                        if (fields->no_auto < 0)
×
633
                                return -ENXIO;
634

635
                        if (!strextend(&j, one_zero(fields->no_auto)))
×
636
                                return -ENOMEM;
637

638
                        break;
639

640
                case PATTERN_READ_ONLY:
×
641
                        if (fields->read_only < 0)
×
642
                                return -ENXIO;
643

644
                        if (!strextend(&j, one_zero(fields->read_only)))
×
645
                                return -ENOMEM;
646

647
                        break;
648

649
                case PATTERN_GROWFS:
×
650
                        if (fields->growfs < 0)
×
651
                                return -ENXIO;
652

653
                        if (!strextend(&j, one_zero(fields->growfs)))
×
654
                                return -ENOMEM;
655

656
                        break;
657

658
                case PATTERN_SHA256SUM: {
×
659
                        _cleanup_free_ char *h = NULL;
×
660

661
                        if (!fields->sha256sum_set)
×
662
                                return -ENXIO;
663

664
                        h = hexmem(fields->sha256sum, sizeof(fields->sha256sum));
×
665
                        if (!h)
×
666
                                return -ENOMEM;
667

668
                        if (!strextend(&j, h))
×
669
                                return -ENOMEM;
670

671
                        break;
×
672
                }
673

674
                default:
×
675
                        assert_not_reached();
×
676
                }
677
        }
678

679
        *ret = TAKE_PTR(j);
1,982✔
680
        return 0;
1,982✔
681
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc