Coverage Report

Created: 2025-07-11 06:26

/src/h2o/deps/yoml/yoml-parser.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2014 DeNA Co., Ltd.
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining a copy
5
 * of this software and associated documentation files (the "Software"), to
6
 * deal in the Software without restriction, including without limitation the
7
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8
 * sell copies of the Software, and to permit persons to whom the Software is
9
 * furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in
12
 * all copies or substantial portions of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20
 * IN THE SOFTWARE.
21
 */
22
#ifndef yoml_parser_h
23
#define yoml_parser_h
24
25
#ifdef __cplusplus
26
extern "C" {
27
#endif
28
29
#include <assert.h>
30
#include <stddef.h>
31
#include <stdlib.h>
32
#include <string.h>
33
#include <yaml.h>
34
#include "yoml.h"
35
36
typedef struct st_yoml_parse_args_t {
37
    const char *filename;
38
    void *(*mem_set)(void *, int, size_t);
39
    struct {
40
        yoml_t *(*cb)(const char *tag, yoml_t *node, void *cb_arg);
41
        void *cb_arg;
42
    } resolve_tag;
43
    unsigned resolve_alias : 1;
44
    unsigned resolve_merge : 1;
45
} yoml_parse_args_t;
46
47
static yoml_t *yoml__parse_node(yaml_parser_t *parser, yaml_event_type_t *last_event, yoml_parse_args_t *parse_args);
48
49
static inline char *yoml__strdup(yaml_char_t *s)
50
0
{
51
0
    return strdup((char *)s);
52
0
}
53
54
static inline size_t yoml__scalar_get_line(yaml_event_t *event)
55
0
{
56
0
    switch (event->data.scalar.style) {
57
0
    case YAML_LITERAL_SCALAR_STYLE:
58
0
    case YAML_FOLDED_SCALAR_STYLE:
59
0
        return event->start_mark.line + 1;
60
0
    default:
61
0
        return event->start_mark.line;
62
0
    }
63
0
}
64
65
static inline yoml_t *yoml__new_node(const char *filename, yoml_type_t type, size_t sz, yaml_char_t *anchor, yaml_char_t *tag,
66
                                     size_t line, size_t column)
67
0
{
68
0
    yoml_t *node = malloc(sz);
69
0
    node->filename = filename != NULL ? strdup(filename) : NULL;
70
0
    node->type = type;
71
0
    node->line = line;
72
0
    node->column = column;
73
0
    node->anchor = anchor != NULL ? yoml__strdup(anchor) : NULL;
74
0
    node->tag = tag != NULL ? yoml__strdup(tag) : NULL;
75
0
    node->_refcnt = 1;
76
0
    node->_merged = 0;
77
0
    return node;
78
0
}
79
80
static inline yoml_t *yoml__parse_sequence(yaml_parser_t *parser, yaml_event_t *event, yoml_parse_args_t *parse_args)
81
0
{
82
0
    yoml_t *seq = yoml__new_node(parse_args->filename, YOML_TYPE_SEQUENCE, offsetof(yoml_t, data.sequence.elements),
83
0
                                 event->data.sequence_start.anchor, event->data.sequence_start.tag, event->start_mark.line,
84
0
                                 event->start_mark.column);
85
0
86
0
    seq->data.sequence.size = 0;
87
0
88
0
    while (1) {
89
0
        yoml_t *new_node;
90
0
        yaml_event_type_t unhandled;
91
0
        if ((new_node = yoml__parse_node(parser, &unhandled, parse_args)) == NULL) {
92
0
            if (unhandled == YAML_SEQUENCE_END_EVENT) {
93
0
                break;
94
0
            } else {
95
0
                yoml_free(seq, parse_args->mem_set);
96
0
                seq = NULL;
97
0
                break;
98
0
            }
99
0
        }
100
0
        seq = realloc(seq, offsetof(yoml_t, data.sequence.elements) + sizeof(yoml_t *) * (seq->data.sequence.size + 1));
101
0
        seq->data.sequence.elements[seq->data.sequence.size++] = new_node;
102
0
    }
103
0
104
0
    return seq;
105
0
}
106
107
static inline yoml_t *yoml__parse_mapping(yaml_parser_t *parser, yaml_event_t *event, yoml_parse_args_t *parse_args)
108
0
{
109
0
    yoml_t *map = yoml__new_node(parse_args->filename, YOML_TYPE_MAPPING, offsetof(yoml_t, data.mapping.elements),
110
0
                                 event->data.mapping_start.anchor, event->data.mapping_start.tag, event->start_mark.line,
111
0
                                 event->start_mark.column);
112
0
113
0
    map->data.mapping.size = 0;
114
0
115
0
    while (1) {
116
0
        yoml_t *key, *value;
117
0
        yaml_event_type_t unhandled;
118
0
        if ((key = yoml__parse_node(parser, &unhandled, parse_args)) == NULL) {
119
0
            if (unhandled == YAML_MAPPING_END_EVENT) {
120
0
                break;
121
0
            } else {
122
0
                yoml_free(map, parse_args->mem_set);
123
0
                map = NULL;
124
0
                break;
125
0
            }
126
0
        }
127
0
        if ((value = yoml__parse_node(parser, NULL, parse_args)) == NULL) {
128
0
            yoml_free(key, parse_args->mem_set);
129
0
            yoml_free(map, parse_args->mem_set);
130
0
            map = NULL;
131
0
            break;
132
0
        }
133
0
        map = realloc(map, offsetof(yoml_t, data.mapping.elements) + sizeof(yoml_mapping_element_t) * (map->data.mapping.size + 1));
134
0
        map->data.mapping.elements[map->data.mapping.size].key = key;
135
0
        map->data.mapping.elements[map->data.mapping.size].value = value;
136
0
        ++map->data.mapping.size;
137
0
    }
138
0
139
0
    return map;
140
0
}
141
142
static yoml_t *yoml__parse_node(yaml_parser_t *parser, yaml_event_type_t *unhandled, yoml_parse_args_t *parse_args)
143
0
{
144
0
    yoml_t *node;
145
0
    yaml_event_t event;
146
0
147
0
    if (unhandled != NULL)
148
0
        *unhandled = YAML_NO_EVENT;
149
0
150
0
    /* wait for a node that is not a stream/doc start event */
151
0
    while (1) {
152
0
        if (!yaml_parser_parse(parser, &event))
153
0
            return NULL;
154
0
        if (!(event.type == YAML_STREAM_START_EVENT || event.type == YAML_DOCUMENT_START_EVENT))
155
0
            break;
156
0
        yaml_event_delete(&event);
157
0
    }
158
0
159
0
    switch (event.type) {
160
0
    case YAML_ALIAS_EVENT:
161
0
        node = yoml__new_node(parse_args->filename, YOML__TYPE_UNRESOLVED_ALIAS, sizeof(*node), NULL, NULL, event.start_mark.line,
162
0
                              event.start_mark.column);
163
0
        node->data.alias = yoml__strdup(event.data.alias.anchor);
164
0
        break;
165
0
    case YAML_SCALAR_EVENT:
166
0
        node = yoml__new_node(parse_args->filename, YOML_TYPE_SCALAR, sizeof(*node), event.data.scalar.anchor,
167
0
                              event.data.scalar.tag, yoml__scalar_get_line(&event), event.start_mark.column);
168
0
        node->data.scalar = yoml__strdup(event.data.scalar.value);
169
0
        if (parse_args->mem_set != NULL)
170
0
            parse_args->mem_set(event.data.scalar.value, 'A', strlen(node->data.scalar));
171
0
        break;
172
0
    case YAML_SEQUENCE_START_EVENT:
173
0
        node = yoml__parse_sequence(parser, &event, parse_args);
174
0
        break;
175
0
    case YAML_MAPPING_START_EVENT:
176
0
        node = yoml__parse_mapping(parser, &event, parse_args);
177
0
        break;
178
0
    default:
179
0
        node = NULL;
180
0
        if (unhandled != NULL)
181
0
            *unhandled = event.type;
182
0
        break;
183
0
    }
184
0
185
0
    yaml_event_delete(&event);
186
0
187
0
    return node;
188
0
}
189
190
static inline int yoml__merge(yoml_t **dest, size_t offset, size_t delete_count, yoml_t *src, yoml_parse_args_t *parse_args)
191
0
{
192
0
    assert(offset + delete_count <= (*dest)->data.mapping.size);
193
0
194
0
    if (src->type != YOML_TYPE_MAPPING)
195
0
        return -1;
196
0
197
0
    /* create new node, copy attributes and elements of `*dest` up to `offset` */
198
0
    yoml_t *new_node = yoml__new_node((*dest)->filename, (*dest)->type,
199
0
                                      offsetof(yoml_t, data.mapping.elements) +
200
0
                                          ((*dest)->data.mapping.size + src->data.mapping.size - delete_count) *
201
0
                                              sizeof(new_node->data.mapping.elements[0]),
202
0
                                      (void *)(*dest)->anchor, (void *)(*dest)->tag, (*dest)->line, (*dest)->column);
203
0
    new_node->_merged = (*dest)->_merged;
204
0
    memcpy(new_node->data.mapping.elements, (*dest)->data.mapping.elements, offset * sizeof(new_node->data.mapping.elements[0]));
205
0
    new_node->data.mapping.size = offset;
206
0
207
0
    /* copy elements from `src`, ignoring the ones that are defined later in `*dest` */
208
0
    for (size_t i = 0; i != src->data.mapping.size; ++i) {
209
0
        yoml_mapping_element_t *src_element = src->data.mapping.elements + i;
210
0
        if (src_element->key->type == YOML_TYPE_SCALAR) {
211
0
            for (size_t j = offset + delete_count; j != (*dest)->data.mapping.size; ++j) {
212
0
                if ((*dest)->data.mapping.elements[j].key->type == YOML_TYPE_SCALAR &&
213
0
                    strcmp((*dest)->data.mapping.elements[j].key->data.scalar, src_element->key->data.scalar) == 0)
214
0
                    goto Skip;
215
0
            }
216
0
        }
217
0
        new_node->data.mapping.elements[new_node->data.mapping.size++] = *src_element;
218
0
    Skip:;
219
0
    }
220
0
221
0
    /* copy elements of `*dest` after `offset + delete_count` */
222
0
    memcpy(new_node->data.mapping.elements + new_node->data.mapping.size, (*dest)->data.mapping.elements + offset + delete_count,
223
0
           ((*dest)->data.mapping.size - offset - delete_count) * sizeof(new_node->data.mapping.elements[0]));
224
0
    new_node->data.mapping.size += (*dest)->data.mapping.size - offset - delete_count;
225
0
226
0
    /* increment the reference counters of the elements being added to the newly
227
0
     * created node */
228
0
    for (size_t i = 0; i != new_node->data.mapping.size; ++i) {
229
0
        ++new_node->data.mapping.elements[i].key->_refcnt;
230
0
        ++new_node->data.mapping.elements[i].value->_refcnt;
231
0
    }
232
0
233
0
    /* replace `*dest` with `new_node` */
234
0
    yoml_free(*dest, parse_args->mem_set);
235
0
    *dest = new_node;
236
0
237
0
    return 0;
238
0
}
239
240
static inline int yoml__resolve_merge(yoml_t **target, yaml_parser_t *parser, yoml_parse_args_t *parse_args)
241
0
{
242
0
    size_t i, j;
243
0
244
0
    if ((*target)->_merged)
245
0
        return 0;
246
0
    (*target)->_merged = 1;
247
0
248
0
    switch ((*target)->type) {
249
0
    case YOML_TYPE_SCALAR:
250
0
        break;
251
0
    case YOML_TYPE_SEQUENCE:
252
0
        for (i = 0; i != (*target)->data.sequence.size; ++i) {
253
0
            if (yoml__resolve_merge((*target)->data.sequence.elements + i, parser, parse_args) != 0)
254
0
                return -1;
255
0
        }
256
0
        break;
257
0
    case YOML_TYPE_MAPPING:
258
0
        if ((*target)->data.mapping.size != 0) {
259
0
            i = (*target)->data.mapping.size;
260
0
            do {
261
0
                --i;
262
0
                if (yoml__resolve_merge(&(*target)->data.mapping.elements[i].key, parser, parse_args) != 0)
263
0
                    return -1;
264
0
                if (yoml__resolve_merge(&(*target)->data.mapping.elements[i].value, parser, parse_args) != 0)
265
0
                    return -1;
266
0
                if ((*target)->data.mapping.elements[i].key->type == YOML_TYPE_SCALAR &&
267
0
                    strcmp((*target)->data.mapping.elements[i].key->data.scalar, "<<") == 0) {
268
0
                    yoml_mapping_element_t src = (*target)->data.mapping.elements[i];
269
0
                    /* merge */
270
0
                    if (src.value->type == YOML_TYPE_SEQUENCE) {
271
0
                        for (j = 0; j != src.value->data.sequence.size; ++j)
272
0
                            if (yoml__merge(target, i, j == 0, src.value->data.sequence.elements[j], parse_args) != 0) {
273
0
                            MergeError:
274
0
                                if (parser != NULL) {
275
0
                                    parser->problem = "value of the merge key MUST be a mapping or a sequence of mappings";
276
0
                                    parser->problem_mark.line = src.key->line;
277
0
                                    parser->problem_mark.column = src.key->column;
278
0
                                }
279
0
                                return -1;
280
0
                            }
281
0
                    } else {
282
0
                        if (yoml__merge(target, i, 1, src.value, parse_args) != 0)
283
0
                            goto MergeError;
284
0
                    }
285
0
                }
286
0
            } while (i != 0);
287
0
        }
288
0
        break;
289
0
    case YOML__TYPE_UNRESOLVED_ALIAS:
290
0
        assert(!"unreachable");
291
0
        break;
292
0
    }
293
0
294
0
    return 0;
295
0
}
296
297
static inline int yoml__resolve_alias(yoml_t **target, yoml_t *doc, yaml_parser_t *parser, yoml_parse_args_t *parse_args)
298
0
{
299
0
    size_t i;
300
0
301
0
    switch ((*target)->type) {
302
0
    case YOML_TYPE_SCALAR:
303
0
        break;
304
0
    case YOML_TYPE_SEQUENCE:
305
0
        for (i = 0; i != (*target)->data.sequence.size; ++i) {
306
0
            if (yoml__resolve_alias((*target)->data.sequence.elements + i, doc, parser, parse_args) != 0)
307
0
                return -1;
308
0
        }
309
0
        break;
310
0
    case YOML_TYPE_MAPPING:
311
0
        for (i = 0; i != (*target)->data.mapping.size; ++i) {
312
0
            if (yoml__resolve_alias(&(*target)->data.mapping.elements[i].key, doc, parser, parse_args) != 0)
313
0
                return -1;
314
0
            if (yoml__resolve_alias(&(*target)->data.mapping.elements[i].value, doc, parser, parse_args) != 0)
315
0
                return -1;
316
0
        }
317
0
        break;
318
0
    case YOML__TYPE_UNRESOLVED_ALIAS: {
319
0
        yoml_t *node = yoml_find_anchor(doc, (*target)->data.alias);
320
0
        if (node == NULL) {
321
0
            if (parser != NULL) {
322
0
                parser->problem = "could not resolve the alias";
323
0
                parser->problem_mark.line = (*target)->line;
324
0
                parser->problem_mark.column = (*target)->column;
325
0
            }
326
0
            return -1;
327
0
        }
328
0
        yoml_free(*target, parse_args->mem_set);
329
0
        *target = node;
330
0
        ++node->_refcnt;
331
0
    } break;
332
0
    }
333
0
334
0
    return 0;
335
0
}
336
337
static inline int yoml__resolve_tag(yoml_t **target, yaml_parser_t *parser, yoml_parse_args_t *parse_args)
338
0
{
339
0
    size_t i;
340
0
341
0
    if (parse_args->resolve_tag.cb == NULL)
342
0
        return 0;
343
0
344
0
    if ((*target)->tag != NULL) {
345
0
        yoml_t *resolved = parse_args->resolve_tag.cb((*target)->tag, *target, parse_args->resolve_tag.cb_arg);
346
0
        if (resolved == NULL) {
347
0
            if (parser != NULL) {
348
0
                parser->problem = "tag resolution failed";
349
0
                parser->problem_mark.line = (*target)->line;
350
0
                parser->problem_mark.column = (*target)->column;
351
0
            }
352
0
            return -1;
353
0
        }
354
0
        yoml_free(*target, parse_args->mem_set);
355
0
        *target = resolved;
356
0
    }
357
0
358
0
    switch ((*target)->type) {
359
0
    case YOML_TYPE_SCALAR:
360
0
        break;
361
0
    case YOML_TYPE_SEQUENCE:
362
0
        for (i = 0; i != (*target)->data.sequence.size; ++i) {
363
0
            if (yoml__resolve_tag((*target)->data.sequence.elements + i, parser, parse_args) != 0)
364
0
                return -1;
365
0
        }
366
0
        break;
367
0
    case YOML_TYPE_MAPPING:
368
0
        for (i = 0; i != (*target)->data.mapping.size; ++i) {
369
0
            if (yoml__resolve_tag(&(*target)->data.mapping.elements[i].key, parser, parse_args) != 0)
370
0
                return -1;
371
0
            if (yoml__resolve_tag(&(*target)->data.mapping.elements[i].value, parser, parse_args) != 0)
372
0
                return -1;
373
0
        }
374
0
        break;
375
0
    case YOML__TYPE_UNRESOLVED_ALIAS:
376
0
        break;
377
0
    }
378
0
379
0
    return 0;
380
0
}
381
382
static inline yoml_t *yoml_parse_document(yaml_parser_t *parser, yaml_event_type_t *unhandled, yoml_parse_args_t *parse_args)
383
0
{
384
0
    yoml_t *doc;
385
0
386
0
    /* parse */
387
0
    if ((doc = yoml__parse_node(parser, unhandled, parse_args)) == NULL) {
388
0
        return NULL;
389
0
    }
390
0
    if (unhandled != NULL)
391
0
        *unhandled = YAML_NO_EVENT;
392
0
393
0
    /* resolve tags, aliases and merge */
394
0
    if (yoml__resolve_tag(&doc, parser, parse_args) != 0)
395
0
        goto Error;
396
0
    if (parse_args->resolve_alias && yoml__resolve_alias(&doc, doc, parser, parse_args) != 0)
397
0
        goto Error;
398
0
    if (parse_args->resolve_merge && yoml__resolve_merge(&doc, parser, parse_args) != 0)
399
0
        goto Error;
400
0
401
0
    return doc;
402
0
403
0
Error:
404
0
    yoml_free(doc, parse_args->mem_set);
405
0
    return NULL;
406
0
}
407
408
#ifdef __cplusplus
409
}
410
#endif
411
412
#endif