ucl 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE-DEPENDENCIES.md +161 -0
- data/README.md +104 -15
- data/ext/extconf.rb +42 -40
- data/ext/libucl/COPYING +23 -0
- data/ext/libucl/include/ucl.h +1689 -0
- data/ext/libucl/klib/khash.h +627 -0
- data/ext/libucl/klib/kvec.h +161 -0
- data/ext/libucl/src/mum.h +440 -0
- data/ext/libucl/src/tree.h +209 -0
- data/ext/libucl/src/ucl_chartable.h +267 -0
- data/ext/libucl/src/ucl_emitter.c +721 -0
- data/ext/libucl/src/ucl_emitter_streamline.c +178 -0
- data/ext/libucl/src/ucl_emitter_utils.c +521 -0
- data/ext/libucl/src/ucl_hash.c +642 -0
- data/ext/libucl/src/ucl_hash.h +115 -0
- data/ext/libucl/src/ucl_internal.h +668 -0
- data/ext/libucl/src/ucl_msgpack.c +1574 -0
- data/ext/libucl/src/ucl_parser.c +3247 -0
- data/ext/libucl/src/ucl_schema.c +1102 -0
- data/ext/libucl/src/ucl_sexp.c +229 -0
- data/ext/libucl/src/ucl_util.c +4004 -0
- data/ext/libucl/uthash/uthash.h +720 -0
- data/ext/libucl/uthash/utlist.h +1076 -0
- data/ext/libucl/uthash/utstring.h +412 -0
- data/ext/ucl.c +308 -89
- data/test/test_ucl.rb +129 -0
- data/ucl.gemspec +20 -7
- metadata +25 -17
|
@@ -0,0 +1,1102 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2014, Vsevolod Stakhov
|
|
3
|
+
*
|
|
4
|
+
* All rights reserved.
|
|
5
|
+
*
|
|
6
|
+
* Redistribution and use in source and binary forms, with or without
|
|
7
|
+
* modification, are permitted provided that the following conditions are met:
|
|
8
|
+
* * Redistributions of source code must retain the above copyright
|
|
9
|
+
* notice, this list of conditions and the following disclaimer.
|
|
10
|
+
* * Redistributions in binary form must reproduce the above copyright
|
|
11
|
+
* notice, this list of conditions and the following disclaimer in the
|
|
12
|
+
* documentation and/or other materials provided with the distribution.
|
|
13
|
+
*
|
|
14
|
+
* THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
|
|
15
|
+
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
16
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
17
|
+
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
|
|
18
|
+
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
19
|
+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
20
|
+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
21
|
+
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
22
|
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
23
|
+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
#include "ucl.h"
|
|
27
|
+
#include "ucl_internal.h"
|
|
28
|
+
#include "tree.h"
|
|
29
|
+
#include "utlist.h"
|
|
30
|
+
#ifdef HAVE_STDARG_H
|
|
31
|
+
#include <stdarg.h>
|
|
32
|
+
#endif
|
|
33
|
+
#ifdef HAVE_STDIO_H
|
|
34
|
+
#include <stdio.h>
|
|
35
|
+
#endif
|
|
36
|
+
#ifdef HAVE_REGEX_H
|
|
37
|
+
#include <regex.h>
|
|
38
|
+
#endif
|
|
39
|
+
#ifdef HAVE_MATH_H
|
|
40
|
+
#include <math.h>
|
|
41
|
+
#endif
|
|
42
|
+
#include <inttypes.h>
|
|
43
|
+
|
|
44
|
+
static bool ucl_schema_validate(const ucl_object_t *schema,
|
|
45
|
+
const ucl_object_t *obj, bool try_array,
|
|
46
|
+
struct ucl_schema_error *err,
|
|
47
|
+
const ucl_object_t *root,
|
|
48
|
+
ucl_object_t *ext_ref);
|
|
49
|
+
|
|
50
|
+
/*
|
|
51
|
+
* Create validation error
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
#ifdef __GNUC__
|
|
55
|
+
static inline void
|
|
56
|
+
ucl_schema_create_error(struct ucl_schema_error *err,
|
|
57
|
+
enum ucl_schema_error_code code, const ucl_object_t *obj,
|
|
58
|
+
const char *fmt, ...)
|
|
59
|
+
__attribute__((format(printf, 4, 5)));
|
|
60
|
+
#endif
|
|
61
|
+
|
|
62
|
+
static inline void
|
|
63
|
+
ucl_schema_create_error(struct ucl_schema_error *err,
|
|
64
|
+
enum ucl_schema_error_code code, const ucl_object_t *obj,
|
|
65
|
+
const char *fmt, ...)
|
|
66
|
+
{
|
|
67
|
+
va_list va;
|
|
68
|
+
|
|
69
|
+
if (err != NULL) {
|
|
70
|
+
err->code = code;
|
|
71
|
+
err->obj = obj;
|
|
72
|
+
va_start(va, fmt);
|
|
73
|
+
vsnprintf(err->msg, sizeof(err->msg), fmt, va);
|
|
74
|
+
va_end(va);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/*
|
|
79
|
+
* Check whether we have a pattern specified
|
|
80
|
+
*/
|
|
81
|
+
static const ucl_object_t *
|
|
82
|
+
ucl_schema_test_pattern(const ucl_object_t *obj, const char *pattern, bool recursive)
|
|
83
|
+
{
|
|
84
|
+
const ucl_object_t *res = NULL;
|
|
85
|
+
#ifdef HAVE_REGEX_H
|
|
86
|
+
regex_t reg;
|
|
87
|
+
const ucl_object_t *elt;
|
|
88
|
+
ucl_object_iter_t iter = NULL;
|
|
89
|
+
|
|
90
|
+
if (regcomp(®, pattern, REG_EXTENDED | REG_NOSUB) == 0) {
|
|
91
|
+
if (recursive) {
|
|
92
|
+
while ((elt = ucl_object_iterate(obj, &iter, true)) != NULL) {
|
|
93
|
+
if (regexec(®, ucl_object_key(elt), 0, NULL, 0) == 0) {
|
|
94
|
+
res = elt;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
if (regexec(®, ucl_object_key(obj), 0, NULL, 0) == 0)
|
|
101
|
+
res = obj;
|
|
102
|
+
}
|
|
103
|
+
regfree(®);
|
|
104
|
+
}
|
|
105
|
+
#endif
|
|
106
|
+
return res;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/*
|
|
110
|
+
* Check dependencies for an object
|
|
111
|
+
*/
|
|
112
|
+
static bool
|
|
113
|
+
ucl_schema_validate_dependencies(const ucl_object_t *deps,
|
|
114
|
+
const ucl_object_t *obj, struct ucl_schema_error *err,
|
|
115
|
+
const ucl_object_t *root,
|
|
116
|
+
ucl_object_t *ext_ref)
|
|
117
|
+
{
|
|
118
|
+
const ucl_object_t *elt, *cur, *cur_dep;
|
|
119
|
+
ucl_object_iter_t iter = NULL, piter;
|
|
120
|
+
bool ret = true;
|
|
121
|
+
|
|
122
|
+
while (ret && (cur = ucl_object_iterate(deps, &iter, true)) != NULL) {
|
|
123
|
+
elt = ucl_object_lookup(obj, ucl_object_key(cur));
|
|
124
|
+
if (elt != NULL) {
|
|
125
|
+
/* Need to check dependencies */
|
|
126
|
+
if (cur->type == UCL_ARRAY) {
|
|
127
|
+
piter = NULL;
|
|
128
|
+
while (ret && (cur_dep = ucl_object_iterate(cur, &piter, true)) != NULL) {
|
|
129
|
+
if (ucl_object_lookup(obj, ucl_object_tostring(cur_dep)) == NULL) {
|
|
130
|
+
ucl_schema_create_error(err, UCL_SCHEMA_MISSING_DEPENDENCY, elt,
|
|
131
|
+
"dependency %s is missing for key %s",
|
|
132
|
+
ucl_object_tostring(cur_dep), ucl_object_key(cur));
|
|
133
|
+
ret = false;
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else if (cur->type == UCL_OBJECT) {
|
|
139
|
+
ret = ucl_schema_validate(cur, obj, true, err, root, ext_ref);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return ret;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/*
|
|
148
|
+
* Validate object
|
|
149
|
+
*/
|
|
150
|
+
static bool
|
|
151
|
+
ucl_schema_validate_object(const ucl_object_t *schema,
|
|
152
|
+
const ucl_object_t *obj, struct ucl_schema_error *err,
|
|
153
|
+
const ucl_object_t *root,
|
|
154
|
+
ucl_object_t *ext_ref)
|
|
155
|
+
{
|
|
156
|
+
const ucl_object_t *elt, *prop, *found, *additional_schema = NULL,
|
|
157
|
+
*required = NULL, *pat, *pelt;
|
|
158
|
+
ucl_object_iter_t iter = NULL, piter = NULL;
|
|
159
|
+
bool ret = true, allow_additional = true;
|
|
160
|
+
int64_t minmax;
|
|
161
|
+
|
|
162
|
+
while (ret && (elt = ucl_object_iterate(schema, &iter, true)) != NULL) {
|
|
163
|
+
if (elt->type == UCL_OBJECT &&
|
|
164
|
+
strcmp(ucl_object_key(elt), "properties") == 0) {
|
|
165
|
+
piter = NULL;
|
|
166
|
+
while (ret && (prop = ucl_object_iterate(elt, &piter, true)) != NULL) {
|
|
167
|
+
found = ucl_object_lookup(obj, ucl_object_key(prop));
|
|
168
|
+
if (found) {
|
|
169
|
+
ret = ucl_schema_validate(prop, found, true, err, root,
|
|
170
|
+
ext_ref);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
else if (strcmp(ucl_object_key(elt), "additionalProperties") == 0) {
|
|
175
|
+
if (elt->type == UCL_BOOLEAN) {
|
|
176
|
+
if (!ucl_object_toboolean(elt)) {
|
|
177
|
+
/* Deny additional fields completely */
|
|
178
|
+
allow_additional = false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else if (elt->type == UCL_OBJECT) {
|
|
182
|
+
/* Define validator for additional fields */
|
|
183
|
+
additional_schema = elt;
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
ucl_schema_create_error(err, UCL_SCHEMA_INVALID_SCHEMA, elt,
|
|
187
|
+
"additionalProperties attribute is invalid in schema");
|
|
188
|
+
ret = false;
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
else if (strcmp(ucl_object_key(elt), "required") == 0) {
|
|
193
|
+
if (elt->type == UCL_ARRAY) {
|
|
194
|
+
required = elt;
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
ucl_schema_create_error(err, UCL_SCHEMA_INVALID_SCHEMA, elt,
|
|
198
|
+
"required attribute is invalid in schema");
|
|
199
|
+
ret = false;
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
else if (strcmp(ucl_object_key(elt), "minProperties") == 0 && ucl_object_toint_safe(elt, &minmax)) {
|
|
204
|
+
if (obj->len < minmax) {
|
|
205
|
+
ucl_schema_create_error(err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
206
|
+
"object has not enough properties: %u, minimum is: %u",
|
|
207
|
+
obj->len, (unsigned) minmax);
|
|
208
|
+
ret = false;
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
else if (strcmp(ucl_object_key(elt), "maxProperties") == 0 && ucl_object_toint_safe(elt, &minmax)) {
|
|
213
|
+
if (obj->len > minmax) {
|
|
214
|
+
ucl_schema_create_error(err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
215
|
+
"object has too many properties: %u, maximum is: %u",
|
|
216
|
+
obj->len, (unsigned) minmax);
|
|
217
|
+
ret = false;
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
else if (strcmp(ucl_object_key(elt), "patternProperties") == 0) {
|
|
222
|
+
const ucl_object_t *vobj;
|
|
223
|
+
ucl_object_iter_t viter;
|
|
224
|
+
piter = NULL;
|
|
225
|
+
while (ret && (prop = ucl_object_iterate(elt, &piter, true)) != NULL) {
|
|
226
|
+
viter = NULL;
|
|
227
|
+
while (ret && (vobj = ucl_object_iterate(obj, &viter, true)) != NULL) {
|
|
228
|
+
found = ucl_schema_test_pattern(vobj, ucl_object_key(prop), false);
|
|
229
|
+
if (found) {
|
|
230
|
+
ret = ucl_schema_validate(prop, found, true, err, root,
|
|
231
|
+
ext_ref);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
else if (elt->type == UCL_OBJECT &&
|
|
237
|
+
strcmp(ucl_object_key(elt), "dependencies") == 0) {
|
|
238
|
+
ret = ucl_schema_validate_dependencies(elt, obj, err, root,
|
|
239
|
+
ext_ref);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (ret) {
|
|
244
|
+
/* Additional properties */
|
|
245
|
+
if (!allow_additional || additional_schema != NULL) {
|
|
246
|
+
/* Check if we have exactly the same properties in schema and object */
|
|
247
|
+
iter = ucl_object_iterate_new(obj);
|
|
248
|
+
prop = ucl_object_lookup(schema, "properties");
|
|
249
|
+
while ((elt = ucl_object_iterate_safe(iter, true)) != NULL) {
|
|
250
|
+
found = ucl_object_lookup(prop, ucl_object_key(elt));
|
|
251
|
+
if (found == NULL) {
|
|
252
|
+
/* Try patternProperties */
|
|
253
|
+
pat = ucl_object_lookup(schema, "patternProperties");
|
|
254
|
+
piter = ucl_object_iterate_new(pat);
|
|
255
|
+
while ((pelt = ucl_object_iterate_safe(piter, true)) != NULL) {
|
|
256
|
+
found = ucl_schema_test_pattern(obj, ucl_object_key(pelt), true);
|
|
257
|
+
if (found != NULL) {
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
ucl_object_iterate_free(piter);
|
|
262
|
+
piter = NULL;
|
|
263
|
+
}
|
|
264
|
+
if (found == NULL) {
|
|
265
|
+
if (!allow_additional) {
|
|
266
|
+
ucl_schema_create_error(err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
267
|
+
"object has non-allowed property %s",
|
|
268
|
+
ucl_object_key(elt));
|
|
269
|
+
ret = false;
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
else if (additional_schema != NULL) {
|
|
273
|
+
if (!ucl_schema_validate(additional_schema, elt,
|
|
274
|
+
true, err, root, ext_ref)) {
|
|
275
|
+
ret = false;
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
ucl_object_iterate_free(iter);
|
|
282
|
+
iter = NULL;
|
|
283
|
+
}
|
|
284
|
+
/* Required properties */
|
|
285
|
+
if (required != NULL) {
|
|
286
|
+
iter = NULL;
|
|
287
|
+
while ((elt = ucl_object_iterate(required, &iter, true)) != NULL) {
|
|
288
|
+
if (ucl_object_lookup(obj, ucl_object_tostring(elt)) == NULL) {
|
|
289
|
+
ucl_schema_create_error(err, UCL_SCHEMA_MISSING_PROPERTY, obj,
|
|
290
|
+
"object has missing property %s",
|
|
291
|
+
ucl_object_tostring(elt));
|
|
292
|
+
ret = false;
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
return ret;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
static bool
|
|
304
|
+
ucl_schema_validate_number(const ucl_object_t *schema,
|
|
305
|
+
const ucl_object_t *obj, struct ucl_schema_error *err)
|
|
306
|
+
{
|
|
307
|
+
const ucl_object_t *elt, *test;
|
|
308
|
+
ucl_object_iter_t iter = NULL;
|
|
309
|
+
bool ret = true, exclusive = false;
|
|
310
|
+
double constraint, val;
|
|
311
|
+
const double alpha = 1e-16;
|
|
312
|
+
|
|
313
|
+
while (ret && (elt = ucl_object_iterate(schema, &iter, true)) != NULL) {
|
|
314
|
+
if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
|
|
315
|
+
strcmp(ucl_object_key(elt), "multipleOf") == 0) {
|
|
316
|
+
constraint = ucl_object_todouble(elt);
|
|
317
|
+
if (constraint <= 0) {
|
|
318
|
+
ucl_schema_create_error(err, UCL_SCHEMA_INVALID_SCHEMA, elt,
|
|
319
|
+
"multipleOf must be greater than zero");
|
|
320
|
+
ret = false;
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
val = ucl_object_todouble(obj);
|
|
324
|
+
if (fabs(remainder(val, constraint)) > alpha) {
|
|
325
|
+
ucl_schema_create_error(err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
326
|
+
"number %.4f is not multiple of %.4f, remainder is %.7f",
|
|
327
|
+
val, constraint, remainder(val, constraint));
|
|
328
|
+
ret = false;
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
|
|
333
|
+
strcmp(ucl_object_key(elt), "maximum") == 0) {
|
|
334
|
+
constraint = ucl_object_todouble(elt);
|
|
335
|
+
test = ucl_object_lookup(schema, "exclusiveMaximum");
|
|
336
|
+
if (test && test->type == UCL_BOOLEAN) {
|
|
337
|
+
exclusive = ucl_object_toboolean(test);
|
|
338
|
+
}
|
|
339
|
+
val = ucl_object_todouble(obj);
|
|
340
|
+
if (val > constraint || (exclusive && val >= constraint)) {
|
|
341
|
+
ucl_schema_create_error(err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
342
|
+
"number is too big: %.3f, maximum is: %.3f",
|
|
343
|
+
val, constraint);
|
|
344
|
+
ret = false;
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
|
|
349
|
+
strcmp(ucl_object_key(elt), "minimum") == 0) {
|
|
350
|
+
constraint = ucl_object_todouble(elt);
|
|
351
|
+
test = ucl_object_lookup(schema, "exclusiveMinimum");
|
|
352
|
+
if (test && test->type == UCL_BOOLEAN) {
|
|
353
|
+
exclusive = ucl_object_toboolean(test);
|
|
354
|
+
}
|
|
355
|
+
val = ucl_object_todouble(obj);
|
|
356
|
+
if (val < constraint || (exclusive && val <= constraint)) {
|
|
357
|
+
ucl_schema_create_error(err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
358
|
+
"number is too small: %.3f, minimum is: %.3f",
|
|
359
|
+
val, constraint);
|
|
360
|
+
ret = false;
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return ret;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
static bool
|
|
370
|
+
ucl_schema_validate_string(const ucl_object_t *schema,
|
|
371
|
+
const ucl_object_t *obj, struct ucl_schema_error *err)
|
|
372
|
+
{
|
|
373
|
+
const ucl_object_t *elt;
|
|
374
|
+
ucl_object_iter_t iter = NULL;
|
|
375
|
+
bool ret = true;
|
|
376
|
+
int64_t constraint;
|
|
377
|
+
#ifdef HAVE_REGEX_H
|
|
378
|
+
regex_t re;
|
|
379
|
+
#endif
|
|
380
|
+
|
|
381
|
+
while (ret && (elt = ucl_object_iterate(schema, &iter, true)) != NULL) {
|
|
382
|
+
if (elt->type == UCL_INT &&
|
|
383
|
+
strcmp(ucl_object_key(elt), "maxLength") == 0) {
|
|
384
|
+
constraint = ucl_object_toint(elt);
|
|
385
|
+
if (obj->len > constraint) {
|
|
386
|
+
ucl_schema_create_error(err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
387
|
+
"string is too big: %u, maximum is: %" PRId64,
|
|
388
|
+
obj->len, constraint);
|
|
389
|
+
ret = false;
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
else if (elt->type == UCL_INT &&
|
|
394
|
+
strcmp(ucl_object_key(elt), "minLength") == 0) {
|
|
395
|
+
constraint = ucl_object_toint(elt);
|
|
396
|
+
if (obj->len < constraint) {
|
|
397
|
+
ucl_schema_create_error(err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
398
|
+
"string is too short: %u, minimum is: %" PRId64,
|
|
399
|
+
obj->len, constraint);
|
|
400
|
+
ret = false;
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
#ifdef HAVE_REGEX_H
|
|
405
|
+
else if (elt->type == UCL_STRING &&
|
|
406
|
+
strcmp(ucl_object_key(elt), "pattern") == 0) {
|
|
407
|
+
if (regcomp(&re, ucl_object_tostring(elt),
|
|
408
|
+
REG_EXTENDED | REG_NOSUB) != 0) {
|
|
409
|
+
ucl_schema_create_error(err, UCL_SCHEMA_INVALID_SCHEMA, elt,
|
|
410
|
+
"cannot compile pattern %s", ucl_object_tostring(elt));
|
|
411
|
+
ret = false;
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
if (regexec(&re, ucl_object_tostring(obj), 0, NULL, 0) != 0) {
|
|
415
|
+
ucl_schema_create_error(err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
416
|
+
"string doesn't match regexp %s",
|
|
417
|
+
ucl_object_tostring(elt));
|
|
418
|
+
ret = false;
|
|
419
|
+
}
|
|
420
|
+
regfree(&re);
|
|
421
|
+
}
|
|
422
|
+
#endif
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
return ret;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
struct ucl_compare_node {
|
|
429
|
+
const ucl_object_t *obj;
|
|
430
|
+
TREE_ENTRY(ucl_compare_node)
|
|
431
|
+
link;
|
|
432
|
+
struct ucl_compare_node *next;
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
typedef TREE_HEAD(_tree, ucl_compare_node) ucl_compare_tree_t;
|
|
436
|
+
|
|
437
|
+
TREE_DEFINE(ucl_compare_node, link)
|
|
438
|
+
|
|
439
|
+
static int
|
|
440
|
+
ucl_schema_elt_compare(struct ucl_compare_node *n1, struct ucl_compare_node *n2)
|
|
441
|
+
{
|
|
442
|
+
const ucl_object_t *o1 = n1->obj, *o2 = n2->obj;
|
|
443
|
+
|
|
444
|
+
return ucl_object_compare(o1, o2);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
static bool
|
|
448
|
+
ucl_schema_array_is_unique(const ucl_object_t *obj, struct ucl_schema_error *err)
|
|
449
|
+
{
|
|
450
|
+
ucl_compare_tree_t tree = TREE_INITIALIZER(ucl_schema_elt_compare);
|
|
451
|
+
ucl_object_iter_t iter = NULL;
|
|
452
|
+
const ucl_object_t *elt;
|
|
453
|
+
struct ucl_compare_node *node, test, *nodes = NULL, *tmp;
|
|
454
|
+
bool ret = true;
|
|
455
|
+
|
|
456
|
+
while ((elt = ucl_object_iterate(obj, &iter, true)) != NULL) {
|
|
457
|
+
test.obj = elt;
|
|
458
|
+
node = TREE_FIND(&tree, ucl_compare_node, link, &test);
|
|
459
|
+
if (node != NULL) {
|
|
460
|
+
ucl_schema_create_error(err, UCL_SCHEMA_CONSTRAINT, elt,
|
|
461
|
+
"duplicate values detected while uniqueItems is true");
|
|
462
|
+
ret = false;
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
node = calloc(1, sizeof(*node));
|
|
466
|
+
if (node == NULL) {
|
|
467
|
+
ucl_schema_create_error(err, UCL_SCHEMA_UNKNOWN, elt,
|
|
468
|
+
"cannot allocate tree node");
|
|
469
|
+
ret = false;
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
node->obj = elt;
|
|
473
|
+
TREE_INSERT(&tree, ucl_compare_node, link, node);
|
|
474
|
+
LL_PREPEND(nodes, node);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
LL_FOREACH_SAFE(nodes, node, tmp)
|
|
478
|
+
{
|
|
479
|
+
free(node);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return ret;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
static bool
|
|
486
|
+
ucl_schema_validate_array(const ucl_object_t *schema,
|
|
487
|
+
const ucl_object_t *obj, struct ucl_schema_error *err,
|
|
488
|
+
const ucl_object_t *root,
|
|
489
|
+
ucl_object_t *ext_ref)
|
|
490
|
+
{
|
|
491
|
+
const ucl_object_t *elt, *it, *found, *additional_schema = NULL,
|
|
492
|
+
*first_unvalidated = NULL;
|
|
493
|
+
ucl_object_iter_t iter = NULL, piter = NULL;
|
|
494
|
+
bool ret = true, allow_additional = true, need_unique = false;
|
|
495
|
+
int64_t minmax;
|
|
496
|
+
unsigned int idx = 0;
|
|
497
|
+
|
|
498
|
+
while (ret && (elt = ucl_object_iterate(schema, &iter, true)) != NULL) {
|
|
499
|
+
if (strcmp(ucl_object_key(elt), "items") == 0) {
|
|
500
|
+
if (elt->type == UCL_ARRAY) {
|
|
501
|
+
found = ucl_array_head(obj);
|
|
502
|
+
while (ret && (it = ucl_object_iterate(elt, &piter, true)) != NULL) {
|
|
503
|
+
if (found) {
|
|
504
|
+
ret = ucl_schema_validate(it, found, false, err,
|
|
505
|
+
root, ext_ref);
|
|
506
|
+
found = ucl_array_find_index(obj, ++idx);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
if (found != NULL) {
|
|
510
|
+
/* The first element that is not validated */
|
|
511
|
+
first_unvalidated = found;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
else if (elt->type == UCL_OBJECT) {
|
|
515
|
+
/* Validate all items using the specified schema */
|
|
516
|
+
while (ret && (it = ucl_object_iterate(obj, &piter, true)) != NULL) {
|
|
517
|
+
ret = ucl_schema_validate(elt, it, false, err, root,
|
|
518
|
+
ext_ref);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
ucl_schema_create_error(err, UCL_SCHEMA_INVALID_SCHEMA, elt,
|
|
523
|
+
"items attribute is invalid in schema");
|
|
524
|
+
ret = false;
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
else if (strcmp(ucl_object_key(elt), "additionalItems") == 0) {
|
|
529
|
+
if (elt->type == UCL_BOOLEAN) {
|
|
530
|
+
if (!ucl_object_toboolean(elt)) {
|
|
531
|
+
/* Deny additional fields completely */
|
|
532
|
+
allow_additional = false;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
else if (elt->type == UCL_OBJECT) {
|
|
536
|
+
/* Define validator for additional fields */
|
|
537
|
+
additional_schema = elt;
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
ucl_schema_create_error(err, UCL_SCHEMA_INVALID_SCHEMA, elt,
|
|
541
|
+
"additionalItems attribute is invalid in schema");
|
|
542
|
+
ret = false;
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
else if (elt->type == UCL_BOOLEAN &&
|
|
547
|
+
strcmp(ucl_object_key(elt), "uniqueItems") == 0) {
|
|
548
|
+
need_unique = ucl_object_toboolean(elt);
|
|
549
|
+
}
|
|
550
|
+
else if (strcmp(ucl_object_key(elt), "minItems") == 0 && ucl_object_toint_safe(elt, &minmax)) {
|
|
551
|
+
if (obj->len < minmax) {
|
|
552
|
+
ucl_schema_create_error(err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
553
|
+
"array has not enough items: %u, minimum is: %u",
|
|
554
|
+
obj->len, (unsigned) minmax);
|
|
555
|
+
ret = false;
|
|
556
|
+
break;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
else if (strcmp(ucl_object_key(elt), "maxItems") == 0 && ucl_object_toint_safe(elt, &minmax)) {
|
|
560
|
+
if (obj->len > minmax) {
|
|
561
|
+
ucl_schema_create_error(err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
562
|
+
"array has too many items: %u, maximum is: %u",
|
|
563
|
+
obj->len, (unsigned) minmax);
|
|
564
|
+
ret = false;
|
|
565
|
+
break;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (ret) {
|
|
571
|
+
/* Additional properties */
|
|
572
|
+
if (!allow_additional || additional_schema != NULL) {
|
|
573
|
+
if (first_unvalidated != NULL) {
|
|
574
|
+
if (!allow_additional) {
|
|
575
|
+
ucl_schema_create_error(err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
576
|
+
"array has undefined item");
|
|
577
|
+
ret = false;
|
|
578
|
+
}
|
|
579
|
+
else if (additional_schema != NULL) {
|
|
580
|
+
elt = ucl_array_find_index(obj, idx);
|
|
581
|
+
while (elt) {
|
|
582
|
+
if (!ucl_schema_validate(additional_schema, elt, false,
|
|
583
|
+
err, root, ext_ref)) {
|
|
584
|
+
ret = false;
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
587
|
+
elt = ucl_array_find_index(obj, idx++);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
/* Required properties */
|
|
593
|
+
if (ret && need_unique) {
|
|
594
|
+
ret = ucl_schema_array_is_unique(obj, err);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
return ret;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/*
|
|
602
|
+
* Returns whether this object is allowed for this type
|
|
603
|
+
*/
|
|
604
|
+
static bool
|
|
605
|
+
ucl_schema_type_is_allowed(const ucl_object_t *type, const ucl_object_t *obj,
|
|
606
|
+
struct ucl_schema_error *err)
|
|
607
|
+
{
|
|
608
|
+
ucl_object_iter_t iter = NULL;
|
|
609
|
+
const ucl_object_t *elt;
|
|
610
|
+
const char *type_str;
|
|
611
|
+
ucl_type_t t;
|
|
612
|
+
|
|
613
|
+
if (type == NULL) {
|
|
614
|
+
/* Any type is allowed */
|
|
615
|
+
return true;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (type->type == UCL_ARRAY) {
|
|
619
|
+
/* One of allowed types */
|
|
620
|
+
while ((elt = ucl_object_iterate(type, &iter, true)) != NULL) {
|
|
621
|
+
if (ucl_schema_type_is_allowed(elt, obj, err)) {
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
else if (type->type == UCL_STRING) {
|
|
627
|
+
type_str = ucl_object_tostring(type);
|
|
628
|
+
if (!ucl_object_string_to_type(type_str, &t)) {
|
|
629
|
+
ucl_schema_create_error(err, UCL_SCHEMA_INVALID_SCHEMA, type,
|
|
630
|
+
"Type attribute is invalid in schema");
|
|
631
|
+
return false;
|
|
632
|
+
}
|
|
633
|
+
if (obj->type != t) {
|
|
634
|
+
/* Some types are actually compatible */
|
|
635
|
+
if (obj->type == UCL_TIME && t == UCL_FLOAT) {
|
|
636
|
+
return true;
|
|
637
|
+
}
|
|
638
|
+
else if (obj->type == UCL_INT && t == UCL_FLOAT) {
|
|
639
|
+
return true;
|
|
640
|
+
}
|
|
641
|
+
else {
|
|
642
|
+
ucl_schema_create_error(err, UCL_SCHEMA_TYPE_MISMATCH, obj,
|
|
643
|
+
"Invalid type of %s, expected %s",
|
|
644
|
+
ucl_object_type_to_string(obj->type),
|
|
645
|
+
ucl_object_type_to_string(t));
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
else {
|
|
649
|
+
/* Types are equal */
|
|
650
|
+
return true;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
return false;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/*
|
|
658
|
+
* Check if object is equal to one of elements of enum
|
|
659
|
+
*/
|
|
660
|
+
static bool
|
|
661
|
+
ucl_schema_validate_enum(const ucl_object_t *en, const ucl_object_t *obj,
|
|
662
|
+
struct ucl_schema_error *err)
|
|
663
|
+
{
|
|
664
|
+
ucl_object_iter_t iter = NULL;
|
|
665
|
+
const ucl_object_t *elt;
|
|
666
|
+
bool ret = false;
|
|
667
|
+
|
|
668
|
+
while ((elt = ucl_object_iterate(en, &iter, true)) != NULL) {
|
|
669
|
+
if (ucl_object_compare(elt, obj) == 0) {
|
|
670
|
+
ret = true;
|
|
671
|
+
break;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
if (!ret) {
|
|
676
|
+
ucl_schema_create_error(err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
677
|
+
"object is not one of enumerated patterns");
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
return ret;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
/*
|
|
685
|
+
* Check a single ref component
|
|
686
|
+
*/
|
|
687
|
+
static const ucl_object_t *
|
|
688
|
+
ucl_schema_resolve_ref_component(const ucl_object_t *cur,
|
|
689
|
+
const char *refc, int len,
|
|
690
|
+
struct ucl_schema_error *err)
|
|
691
|
+
{
|
|
692
|
+
const ucl_object_t *res = NULL;
|
|
693
|
+
char *err_str;
|
|
694
|
+
int num, i;
|
|
695
|
+
|
|
696
|
+
if (cur->type == UCL_OBJECT) {
|
|
697
|
+
/* Find a key inside an object */
|
|
698
|
+
res = ucl_object_lookup_len(cur, refc, len);
|
|
699
|
+
if (res == NULL) {
|
|
700
|
+
ucl_schema_create_error(err, UCL_SCHEMA_INVALID_SCHEMA, cur,
|
|
701
|
+
"reference %s is invalid, missing path component", refc);
|
|
702
|
+
return NULL;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
else if (cur->type == UCL_ARRAY) {
|
|
706
|
+
/* We must figure out a number inside array */
|
|
707
|
+
num = strtoul(refc, &err_str, 10);
|
|
708
|
+
if (err_str != NULL && *err_str != '/' && *err_str != '\0') {
|
|
709
|
+
ucl_schema_create_error(err, UCL_SCHEMA_INVALID_SCHEMA, cur,
|
|
710
|
+
"reference %s is invalid, invalid item number", refc);
|
|
711
|
+
return NULL;
|
|
712
|
+
}
|
|
713
|
+
res = ucl_array_head(cur);
|
|
714
|
+
i = 0;
|
|
715
|
+
while (res != NULL) {
|
|
716
|
+
if (i == num) {
|
|
717
|
+
break;
|
|
718
|
+
}
|
|
719
|
+
res = res->next;
|
|
720
|
+
}
|
|
721
|
+
if (res == NULL) {
|
|
722
|
+
ucl_schema_create_error(err, UCL_SCHEMA_INVALID_SCHEMA, cur,
|
|
723
|
+
"reference %s is invalid, item number %d does not exist",
|
|
724
|
+
refc, num);
|
|
725
|
+
return NULL;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
else {
|
|
729
|
+
ucl_schema_create_error(err, UCL_SCHEMA_INVALID_SCHEMA, res,
|
|
730
|
+
"reference %s is invalid, contains primitive object in the path",
|
|
731
|
+
refc);
|
|
732
|
+
return NULL;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
return res;
|
|
736
|
+
}
|
|
737
|
+
/*
|
|
738
|
+
* Find reference schema
|
|
739
|
+
*/
|
|
740
|
+
static const ucl_object_t *
|
|
741
|
+
ucl_schema_resolve_ref(const ucl_object_t *root, const char *ref,
|
|
742
|
+
struct ucl_schema_error *err, ucl_object_t *ext_ref,
|
|
743
|
+
ucl_object_t const **nroot)
|
|
744
|
+
{
|
|
745
|
+
UT_string *url_err = NULL;
|
|
746
|
+
struct ucl_parser *parser;
|
|
747
|
+
const ucl_object_t *res = NULL, *ext_obj = NULL;
|
|
748
|
+
ucl_object_t *url_obj;
|
|
749
|
+
const char *p, *c, *hash_ptr = NULL;
|
|
750
|
+
char *url_copy = NULL;
|
|
751
|
+
unsigned char *url_buf;
|
|
752
|
+
size_t url_buflen;
|
|
753
|
+
|
|
754
|
+
if (ref[0] != '#') {
|
|
755
|
+
hash_ptr = strrchr(ref, '#');
|
|
756
|
+
|
|
757
|
+
if (hash_ptr) {
|
|
758
|
+
url_copy = malloc(hash_ptr - ref + 1);
|
|
759
|
+
|
|
760
|
+
if (url_copy == NULL) {
|
|
761
|
+
ucl_schema_create_error(err, UCL_SCHEMA_INTERNAL_ERROR, root,
|
|
762
|
+
"cannot allocate memory");
|
|
763
|
+
return NULL;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
ucl_strlcpy(url_copy, ref, hash_ptr - ref + 1);
|
|
767
|
+
p = url_copy;
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
/* Full URL */
|
|
771
|
+
p = ref;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
ext_obj = ucl_object_lookup(ext_ref, p);
|
|
775
|
+
|
|
776
|
+
if (ext_obj == NULL) {
|
|
777
|
+
if (ucl_strnstr(p, "://", strlen(p)) != NULL) {
|
|
778
|
+
if (!ucl_fetch_url(p, &url_buf, &url_buflen, &url_err, true)) {
|
|
779
|
+
|
|
780
|
+
ucl_schema_create_error(err,
|
|
781
|
+
UCL_SCHEMA_INVALID_SCHEMA,
|
|
782
|
+
root,
|
|
783
|
+
"cannot fetch reference %s: %s",
|
|
784
|
+
p,
|
|
785
|
+
url_err != NULL ? utstring_body(url_err)
|
|
786
|
+
: "unknown");
|
|
787
|
+
free(url_copy);
|
|
788
|
+
|
|
789
|
+
return NULL;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
else {
|
|
793
|
+
if (!ucl_fetch_file(p, &url_buf, &url_buflen, &url_err,
|
|
794
|
+
true)) {
|
|
795
|
+
ucl_schema_create_error(err,
|
|
796
|
+
UCL_SCHEMA_INVALID_SCHEMA,
|
|
797
|
+
root,
|
|
798
|
+
"cannot fetch reference %s: %s",
|
|
799
|
+
p,
|
|
800
|
+
url_err != NULL ? utstring_body(url_err)
|
|
801
|
+
: "unknown");
|
|
802
|
+
free(url_copy);
|
|
803
|
+
|
|
804
|
+
return NULL;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
parser = ucl_parser_new(0);
|
|
809
|
+
|
|
810
|
+
if (!ucl_parser_add_chunk(parser, url_buf, url_buflen)) {
|
|
811
|
+
ucl_schema_create_error(err, UCL_SCHEMA_INVALID_SCHEMA, root,
|
|
812
|
+
"cannot fetch reference %s: %s", p,
|
|
813
|
+
ucl_parser_get_error(parser));
|
|
814
|
+
ucl_parser_free(parser);
|
|
815
|
+
free(url_copy);
|
|
816
|
+
|
|
817
|
+
return NULL;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
url_obj = ucl_parser_get_object(parser);
|
|
821
|
+
ext_obj = url_obj;
|
|
822
|
+
ucl_object_insert_key(ext_ref, url_obj, p, 0, true);
|
|
823
|
+
free(url_buf);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
free(url_copy);
|
|
827
|
+
|
|
828
|
+
if (hash_ptr) {
|
|
829
|
+
p = hash_ptr + 1;
|
|
830
|
+
}
|
|
831
|
+
else {
|
|
832
|
+
p = "";
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
else {
|
|
836
|
+
p = ref + 1;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
res = ext_obj != NULL ? ext_obj : root;
|
|
840
|
+
*nroot = res;
|
|
841
|
+
|
|
842
|
+
if (*p == '/') {
|
|
843
|
+
p++;
|
|
844
|
+
}
|
|
845
|
+
else if (*p == '\0') {
|
|
846
|
+
return res;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
c = p;
|
|
850
|
+
|
|
851
|
+
while (*p != '\0') {
|
|
852
|
+
if (*p == '/') {
|
|
853
|
+
if (p - c == 0) {
|
|
854
|
+
ucl_schema_create_error(err, UCL_SCHEMA_INVALID_SCHEMA, res,
|
|
855
|
+
"reference %s is invalid, empty path component", ref);
|
|
856
|
+
return NULL;
|
|
857
|
+
}
|
|
858
|
+
/* Now we have some url part, so we need to figure out where we are */
|
|
859
|
+
res = ucl_schema_resolve_ref_component(res, c, p - c, err);
|
|
860
|
+
if (res == NULL) {
|
|
861
|
+
return NULL;
|
|
862
|
+
}
|
|
863
|
+
c = p + 1;
|
|
864
|
+
}
|
|
865
|
+
p++;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
if (p - c != 0) {
|
|
869
|
+
res = ucl_schema_resolve_ref_component(res, c, p - c, err);
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
if (res == NULL || res->type != UCL_OBJECT) {
|
|
873
|
+
ucl_schema_create_error(err, UCL_SCHEMA_INVALID_SCHEMA, res,
|
|
874
|
+
"reference %s is invalid, cannot find specified object",
|
|
875
|
+
ref);
|
|
876
|
+
return NULL;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
return res;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
static bool
|
|
883
|
+
ucl_schema_validate_values(const ucl_object_t *schema, const ucl_object_t *obj,
|
|
884
|
+
struct ucl_schema_error *err)
|
|
885
|
+
{
|
|
886
|
+
const ucl_object_t *elt, *cur;
|
|
887
|
+
int64_t constraint, i;
|
|
888
|
+
|
|
889
|
+
elt = ucl_object_lookup(schema, "maxValues");
|
|
890
|
+
if (elt != NULL && elt->type == UCL_INT) {
|
|
891
|
+
constraint = ucl_object_toint(elt);
|
|
892
|
+
cur = obj;
|
|
893
|
+
i = 0;
|
|
894
|
+
while (cur) {
|
|
895
|
+
if (i > constraint) {
|
|
896
|
+
ucl_schema_create_error(err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
897
|
+
"object has more values than defined: %ld",
|
|
898
|
+
(long int) constraint);
|
|
899
|
+
return false;
|
|
900
|
+
}
|
|
901
|
+
i++;
|
|
902
|
+
cur = cur->next;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
elt = ucl_object_lookup(schema, "minValues");
|
|
906
|
+
if (elt != NULL && elt->type == UCL_INT) {
|
|
907
|
+
constraint = ucl_object_toint(elt);
|
|
908
|
+
cur = obj;
|
|
909
|
+
i = 0;
|
|
910
|
+
while (cur) {
|
|
911
|
+
if (i >= constraint) {
|
|
912
|
+
break;
|
|
913
|
+
}
|
|
914
|
+
i++;
|
|
915
|
+
cur = cur->next;
|
|
916
|
+
}
|
|
917
|
+
if (i < constraint) {
|
|
918
|
+
ucl_schema_create_error(err, UCL_SCHEMA_CONSTRAINT, obj,
|
|
919
|
+
"object has less values than defined: %ld",
|
|
920
|
+
(long int) constraint);
|
|
921
|
+
return false;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
return true;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
static bool
|
|
929
|
+
ucl_schema_validate(const ucl_object_t *schema,
|
|
930
|
+
const ucl_object_t *obj, bool try_array,
|
|
931
|
+
struct ucl_schema_error *err,
|
|
932
|
+
const ucl_object_t *root,
|
|
933
|
+
ucl_object_t *external_refs)
|
|
934
|
+
{
|
|
935
|
+
const ucl_object_t *elt, *cur, *ref_root;
|
|
936
|
+
ucl_object_iter_t iter = NULL;
|
|
937
|
+
bool ret;
|
|
938
|
+
|
|
939
|
+
if (schema->type != UCL_OBJECT) {
|
|
940
|
+
ucl_schema_create_error(err, UCL_SCHEMA_INVALID_SCHEMA, schema,
|
|
941
|
+
"schema is %s instead of object",
|
|
942
|
+
ucl_object_type_to_string(schema->type));
|
|
943
|
+
return false;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
if (try_array) {
|
|
947
|
+
/*
|
|
948
|
+
* Special case for multiple values
|
|
949
|
+
*/
|
|
950
|
+
if (!ucl_schema_validate_values(schema, obj, err)) {
|
|
951
|
+
return false;
|
|
952
|
+
}
|
|
953
|
+
LL_FOREACH(obj, cur)
|
|
954
|
+
{
|
|
955
|
+
if (!ucl_schema_validate(schema, cur, false, err, root, external_refs)) {
|
|
956
|
+
return false;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
return true;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
elt = ucl_object_lookup(schema, "enum");
|
|
963
|
+
if (elt != NULL && elt->type == UCL_ARRAY) {
|
|
964
|
+
if (!ucl_schema_validate_enum(elt, obj, err)) {
|
|
965
|
+
return false;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
elt = ucl_object_lookup(schema, "allOf");
|
|
970
|
+
if (elt != NULL && elt->type == UCL_ARRAY) {
|
|
971
|
+
iter = NULL;
|
|
972
|
+
while ((cur = ucl_object_iterate(elt, &iter, true)) != NULL) {
|
|
973
|
+
ret = ucl_schema_validate(cur, obj, true, err, root, external_refs);
|
|
974
|
+
if (!ret) {
|
|
975
|
+
return false;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
elt = ucl_object_lookup(schema, "anyOf");
|
|
981
|
+
if (elt != NULL && elt->type == UCL_ARRAY) {
|
|
982
|
+
iter = NULL;
|
|
983
|
+
while ((cur = ucl_object_iterate(elt, &iter, true)) != NULL) {
|
|
984
|
+
ret = ucl_schema_validate(cur, obj, true, err, root, external_refs);
|
|
985
|
+
if (ret) {
|
|
986
|
+
break;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
if (!ret) {
|
|
990
|
+
return false;
|
|
991
|
+
}
|
|
992
|
+
else {
|
|
993
|
+
/* Reset error */
|
|
994
|
+
err->code = UCL_SCHEMA_OK;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
elt = ucl_object_lookup(schema, "oneOf");
|
|
999
|
+
if (elt != NULL && elt->type == UCL_ARRAY) {
|
|
1000
|
+
iter = NULL;
|
|
1001
|
+
ret = false;
|
|
1002
|
+
while ((cur = ucl_object_iterate(elt, &iter, true)) != NULL) {
|
|
1003
|
+
if (!ret) {
|
|
1004
|
+
ret = ucl_schema_validate(cur, obj, true, err, root, external_refs);
|
|
1005
|
+
}
|
|
1006
|
+
else if (ucl_schema_validate(cur, obj, true, err, root, external_refs)) {
|
|
1007
|
+
ret = false;
|
|
1008
|
+
break;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
if (!ret) {
|
|
1012
|
+
return false;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
elt = ucl_object_lookup(schema, "not");
|
|
1017
|
+
if (elt != NULL && elt->type == UCL_OBJECT) {
|
|
1018
|
+
if (ucl_schema_validate(elt, obj, true, err, root, external_refs)) {
|
|
1019
|
+
return false;
|
|
1020
|
+
}
|
|
1021
|
+
else {
|
|
1022
|
+
/* Reset error */
|
|
1023
|
+
err->code = UCL_SCHEMA_OK;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
elt = ucl_object_lookup(schema, "$ref");
|
|
1028
|
+
if (elt != NULL) {
|
|
1029
|
+
ref_root = root;
|
|
1030
|
+
cur = ucl_schema_resolve_ref(root, ucl_object_tostring(elt),
|
|
1031
|
+
err, external_refs, &ref_root);
|
|
1032
|
+
|
|
1033
|
+
if (cur == NULL) {
|
|
1034
|
+
return false;
|
|
1035
|
+
}
|
|
1036
|
+
if (!ucl_schema_validate(cur, obj, try_array, err, ref_root,
|
|
1037
|
+
external_refs)) {
|
|
1038
|
+
return false;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
elt = ucl_object_lookup(schema, "type");
|
|
1043
|
+
if (!ucl_schema_type_is_allowed(elt, obj, err)) {
|
|
1044
|
+
return false;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
switch (obj->type) {
|
|
1048
|
+
case UCL_OBJECT:
|
|
1049
|
+
return ucl_schema_validate_object(schema, obj, err, root, external_refs);
|
|
1050
|
+
break;
|
|
1051
|
+
case UCL_ARRAY:
|
|
1052
|
+
return ucl_schema_validate_array(schema, obj, err, root, external_refs);
|
|
1053
|
+
break;
|
|
1054
|
+
case UCL_INT:
|
|
1055
|
+
case UCL_FLOAT:
|
|
1056
|
+
return ucl_schema_validate_number(schema, obj, err);
|
|
1057
|
+
break;
|
|
1058
|
+
case UCL_STRING:
|
|
1059
|
+
return ucl_schema_validate_string(schema, obj, err);
|
|
1060
|
+
break;
|
|
1061
|
+
default:
|
|
1062
|
+
break;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
return true;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
bool ucl_object_validate(const ucl_object_t *schema,
|
|
1069
|
+
const ucl_object_t *obj, struct ucl_schema_error *err)
|
|
1070
|
+
{
|
|
1071
|
+
return ucl_object_validate_root_ext(schema, obj, schema, NULL, err);
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
bool ucl_object_validate_root(const ucl_object_t *schema,
|
|
1075
|
+
const ucl_object_t *obj,
|
|
1076
|
+
const ucl_object_t *root,
|
|
1077
|
+
struct ucl_schema_error *err)
|
|
1078
|
+
{
|
|
1079
|
+
return ucl_object_validate_root_ext(schema, obj, root, NULL, err);
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
bool ucl_object_validate_root_ext(const ucl_object_t *schema,
|
|
1083
|
+
const ucl_object_t *obj,
|
|
1084
|
+
const ucl_object_t *root,
|
|
1085
|
+
ucl_object_t *ext_refs,
|
|
1086
|
+
struct ucl_schema_error *err)
|
|
1087
|
+
{
|
|
1088
|
+
bool ret, need_unref = false;
|
|
1089
|
+
|
|
1090
|
+
if (ext_refs == NULL) {
|
|
1091
|
+
ext_refs = ucl_object_typed_new(UCL_OBJECT);
|
|
1092
|
+
need_unref = true;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
ret = ucl_schema_validate(schema, obj, true, err, root, ext_refs);
|
|
1096
|
+
|
|
1097
|
+
if (need_unref) {
|
|
1098
|
+
ucl_object_unref(ext_refs);
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
return ret;
|
|
1102
|
+
}
|