xref: /freebsd/contrib/libucl/src/ucl_schema.c (revision a0409676120c1e558d0ade943019934e0f15118d)
197bd480fSBaptiste Daroussin /*
297bd480fSBaptiste Daroussin  * Copyright (c) 2014, Vsevolod Stakhov
397bd480fSBaptiste Daroussin  *
497bd480fSBaptiste Daroussin  * All rights reserved.
597bd480fSBaptiste Daroussin  *
697bd480fSBaptiste Daroussin  * Redistribution and use in source and binary forms, with or without
797bd480fSBaptiste Daroussin  * modification, are permitted provided that the following conditions are met:
897bd480fSBaptiste Daroussin  *	 * Redistributions of source code must retain the above copyright
997bd480fSBaptiste Daroussin  *	   notice, this list of conditions and the following disclaimer.
1097bd480fSBaptiste Daroussin  *	 * Redistributions in binary form must reproduce the above copyright
1197bd480fSBaptiste Daroussin  *	   notice, this list of conditions and the following disclaimer in the
1297bd480fSBaptiste Daroussin  *	   documentation and/or other materials provided with the distribution.
1397bd480fSBaptiste Daroussin  *
1497bd480fSBaptiste Daroussin  * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
1597bd480fSBaptiste Daroussin  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1697bd480fSBaptiste Daroussin  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1797bd480fSBaptiste Daroussin  * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
1897bd480fSBaptiste Daroussin  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1997bd480fSBaptiste Daroussin  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2097bd480fSBaptiste Daroussin  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2197bd480fSBaptiste Daroussin  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2297bd480fSBaptiste Daroussin  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2397bd480fSBaptiste Daroussin  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2497bd480fSBaptiste Daroussin  */
2597bd480fSBaptiste Daroussin 
2697bd480fSBaptiste Daroussin #include "ucl.h"
2797bd480fSBaptiste Daroussin #include "ucl_internal.h"
2897bd480fSBaptiste Daroussin #include "tree.h"
2997bd480fSBaptiste Daroussin #include "utlist.h"
3097bd480fSBaptiste Daroussin #ifdef HAVE_STDARG_H
3197bd480fSBaptiste Daroussin #include <stdarg.h>
3297bd480fSBaptiste Daroussin #endif
3397bd480fSBaptiste Daroussin #ifdef HAVE_STDIO_H
3497bd480fSBaptiste Daroussin #include <stdio.h>
3597bd480fSBaptiste Daroussin #endif
3697bd480fSBaptiste Daroussin #ifdef HAVE_REGEX_H
3797bd480fSBaptiste Daroussin #include <regex.h>
3897bd480fSBaptiste Daroussin #endif
3997bd480fSBaptiste Daroussin #ifdef HAVE_MATH_H
4097bd480fSBaptiste Daroussin #include <math.h>
4197bd480fSBaptiste Daroussin #endif
4297bd480fSBaptiste Daroussin 
43b04a7a0bSBaptiste Daroussin static bool ucl_schema_validate (const ucl_object_t *schema,
44b04a7a0bSBaptiste Daroussin 		const ucl_object_t *obj, bool try_array,
4597bd480fSBaptiste Daroussin 		struct ucl_schema_error *err,
46d9f0ce31SBaptiste Daroussin 		const ucl_object_t *root,
47d9f0ce31SBaptiste Daroussin 		ucl_object_t *ext_ref);
4897bd480fSBaptiste Daroussin 
4997bd480fSBaptiste Daroussin /*
5097bd480fSBaptiste Daroussin  * Create validation error
5197bd480fSBaptiste Daroussin  */
52*a0409676SBaptiste Daroussin 
53*a0409676SBaptiste Daroussin #ifdef __GNUC__
54*a0409676SBaptiste Daroussin static inline void
55*a0409676SBaptiste Daroussin ucl_schema_create_error (struct ucl_schema_error *err,
56*a0409676SBaptiste Daroussin 		enum ucl_schema_error_code code, const ucl_object_t *obj,
57*a0409676SBaptiste Daroussin 		const char *fmt, ...)
58*a0409676SBaptiste Daroussin __attribute__ (( format( printf, 4, 5) ));
59*a0409676SBaptiste Daroussin #endif
60*a0409676SBaptiste Daroussin 
61*a0409676SBaptiste Daroussin static inline void
ucl_schema_create_error(struct ucl_schema_error * err,enum ucl_schema_error_code code,const ucl_object_t * obj,const char * fmt,...)6297bd480fSBaptiste Daroussin ucl_schema_create_error (struct ucl_schema_error *err,
63b04a7a0bSBaptiste Daroussin 		enum ucl_schema_error_code code, const ucl_object_t *obj,
6497bd480fSBaptiste Daroussin 		const char *fmt, ...)
6597bd480fSBaptiste Daroussin {
6697bd480fSBaptiste Daroussin 	va_list va;
6797bd480fSBaptiste Daroussin 
6897bd480fSBaptiste Daroussin 	if (err != NULL) {
6997bd480fSBaptiste Daroussin 		err->code = code;
7097bd480fSBaptiste Daroussin 		err->obj = obj;
7197bd480fSBaptiste Daroussin 		va_start (va, fmt);
7297bd480fSBaptiste Daroussin 		vsnprintf (err->msg, sizeof (err->msg), fmt, va);
7397bd480fSBaptiste Daroussin 		va_end (va);
7497bd480fSBaptiste Daroussin 	}
7597bd480fSBaptiste Daroussin }
7697bd480fSBaptiste Daroussin 
7797bd480fSBaptiste Daroussin /*
7897bd480fSBaptiste Daroussin  * Check whether we have a pattern specified
7997bd480fSBaptiste Daroussin  */
80b04a7a0bSBaptiste Daroussin static const ucl_object_t *
ucl_schema_test_pattern(const ucl_object_t * obj,const char * pattern,bool recursive)81273c26a3SBaptiste Daroussin ucl_schema_test_pattern (const ucl_object_t *obj, const char *pattern, bool recursive)
8297bd480fSBaptiste Daroussin {
83b04a7a0bSBaptiste Daroussin 	const ucl_object_t *res = NULL;
84b04a7a0bSBaptiste Daroussin #ifdef HAVE_REGEX_H
8597bd480fSBaptiste Daroussin 	regex_t reg;
86b04a7a0bSBaptiste Daroussin 	const ucl_object_t *elt;
8797bd480fSBaptiste Daroussin 	ucl_object_iter_t iter = NULL;
8897bd480fSBaptiste Daroussin 
8997bd480fSBaptiste Daroussin 	if (regcomp (&reg, pattern, REG_EXTENDED | REG_NOSUB) == 0) {
90273c26a3SBaptiste Daroussin 		if (recursive) {
91d9f0ce31SBaptiste Daroussin 			while ((elt = ucl_object_iterate (obj, &iter, true)) != NULL) {
9297bd480fSBaptiste Daroussin 				if (regexec (&reg, ucl_object_key (elt), 0, NULL, 0) == 0) {
9397bd480fSBaptiste Daroussin 					res = elt;
9497bd480fSBaptiste Daroussin 					break;
9597bd480fSBaptiste Daroussin 				}
9697bd480fSBaptiste Daroussin 			}
97273c26a3SBaptiste Daroussin 		} else {
98273c26a3SBaptiste Daroussin 			if (regexec (&reg, ucl_object_key (obj), 0, NULL, 0) == 0)
99273c26a3SBaptiste Daroussin 				res = obj;
100273c26a3SBaptiste Daroussin 		}
10197bd480fSBaptiste Daroussin 		regfree (&reg);
10297bd480fSBaptiste Daroussin 	}
103b04a7a0bSBaptiste Daroussin #endif
10497bd480fSBaptiste Daroussin 	return res;
10597bd480fSBaptiste Daroussin }
10697bd480fSBaptiste Daroussin 
10797bd480fSBaptiste Daroussin /*
10897bd480fSBaptiste Daroussin  * Check dependencies for an object
10997bd480fSBaptiste Daroussin  */
11097bd480fSBaptiste Daroussin static bool
ucl_schema_validate_dependencies(const ucl_object_t * deps,const ucl_object_t * obj,struct ucl_schema_error * err,const ucl_object_t * root,ucl_object_t * ext_ref)111b04a7a0bSBaptiste Daroussin ucl_schema_validate_dependencies (const ucl_object_t *deps,
112b04a7a0bSBaptiste Daroussin 		const ucl_object_t *obj, struct ucl_schema_error *err,
113d9f0ce31SBaptiste Daroussin 		const ucl_object_t *root,
114d9f0ce31SBaptiste Daroussin 		ucl_object_t *ext_ref)
11597bd480fSBaptiste Daroussin {
116b04a7a0bSBaptiste Daroussin 	const ucl_object_t *elt, *cur, *cur_dep;
11797bd480fSBaptiste Daroussin 	ucl_object_iter_t iter = NULL, piter;
11897bd480fSBaptiste Daroussin 	bool ret = true;
11997bd480fSBaptiste Daroussin 
120d9f0ce31SBaptiste Daroussin 	while (ret && (cur = ucl_object_iterate (deps, &iter, true)) != NULL) {
121d9f0ce31SBaptiste Daroussin 		elt = ucl_object_lookup (obj, ucl_object_key (cur));
12297bd480fSBaptiste Daroussin 		if (elt != NULL) {
12397bd480fSBaptiste Daroussin 			/* Need to check dependencies */
12497bd480fSBaptiste Daroussin 			if (cur->type == UCL_ARRAY) {
12597bd480fSBaptiste Daroussin 				piter = NULL;
126d9f0ce31SBaptiste Daroussin 				while (ret && (cur_dep = ucl_object_iterate (cur, &piter, true)) != NULL) {
127d9f0ce31SBaptiste Daroussin 					if (ucl_object_lookup (obj, ucl_object_tostring (cur_dep)) == NULL) {
12897bd480fSBaptiste Daroussin 						ucl_schema_create_error (err, UCL_SCHEMA_MISSING_DEPENDENCY, elt,
12997bd480fSBaptiste Daroussin 								"dependency %s is missing for key %s",
13097bd480fSBaptiste Daroussin 								ucl_object_tostring (cur_dep), ucl_object_key (cur));
13197bd480fSBaptiste Daroussin 						ret = false;
13297bd480fSBaptiste Daroussin 						break;
13397bd480fSBaptiste Daroussin 					}
13497bd480fSBaptiste Daroussin 				}
13597bd480fSBaptiste Daroussin 			}
13697bd480fSBaptiste Daroussin 			else if (cur->type == UCL_OBJECT) {
137d9f0ce31SBaptiste Daroussin 				ret = ucl_schema_validate (cur, obj, true, err, root, ext_ref);
13897bd480fSBaptiste Daroussin 			}
13997bd480fSBaptiste Daroussin 		}
14097bd480fSBaptiste Daroussin 	}
14197bd480fSBaptiste Daroussin 
14297bd480fSBaptiste Daroussin 	return ret;
14397bd480fSBaptiste Daroussin }
14497bd480fSBaptiste Daroussin 
14597bd480fSBaptiste Daroussin /*
14697bd480fSBaptiste Daroussin  * Validate object
14797bd480fSBaptiste Daroussin  */
14897bd480fSBaptiste Daroussin static bool
ucl_schema_validate_object(const ucl_object_t * schema,const ucl_object_t * obj,struct ucl_schema_error * err,const ucl_object_t * root,ucl_object_t * ext_ref)149b04a7a0bSBaptiste Daroussin ucl_schema_validate_object (const ucl_object_t *schema,
150b04a7a0bSBaptiste Daroussin 		const ucl_object_t *obj, struct ucl_schema_error *err,
151d9f0ce31SBaptiste Daroussin 		const ucl_object_t *root,
152d9f0ce31SBaptiste Daroussin 		ucl_object_t *ext_ref)
15397bd480fSBaptiste Daroussin {
154b04a7a0bSBaptiste Daroussin 	const ucl_object_t *elt, *prop, *found, *additional_schema = NULL,
15597bd480fSBaptiste Daroussin 			*required = NULL, *pat, *pelt;
15697bd480fSBaptiste Daroussin 	ucl_object_iter_t iter = NULL, piter = NULL;
15797bd480fSBaptiste Daroussin 	bool ret = true, allow_additional = true;
15897bd480fSBaptiste Daroussin 	int64_t minmax;
15997bd480fSBaptiste Daroussin 
160d9f0ce31SBaptiste Daroussin 	while (ret && (elt = ucl_object_iterate (schema, &iter, true)) != NULL) {
16197bd480fSBaptiste Daroussin 		if (elt->type == UCL_OBJECT &&
16297bd480fSBaptiste Daroussin 				strcmp (ucl_object_key (elt), "properties") == 0) {
16397bd480fSBaptiste Daroussin 			piter = NULL;
164d9f0ce31SBaptiste Daroussin 			while (ret && (prop = ucl_object_iterate (elt, &piter, true)) != NULL) {
165d9f0ce31SBaptiste Daroussin 				found = ucl_object_lookup (obj, ucl_object_key (prop));
16697bd480fSBaptiste Daroussin 				if (found) {
167d9f0ce31SBaptiste Daroussin 					ret = ucl_schema_validate (prop, found, true, err, root,
168d9f0ce31SBaptiste Daroussin 							ext_ref);
16997bd480fSBaptiste Daroussin 				}
17097bd480fSBaptiste Daroussin 			}
17197bd480fSBaptiste Daroussin 		}
17297bd480fSBaptiste Daroussin 		else if (strcmp (ucl_object_key (elt), "additionalProperties") == 0) {
17397bd480fSBaptiste Daroussin 			if (elt->type == UCL_BOOLEAN) {
17497bd480fSBaptiste Daroussin 				if (!ucl_object_toboolean (elt)) {
17597bd480fSBaptiste Daroussin 					/* Deny additional fields completely */
17697bd480fSBaptiste Daroussin 					allow_additional = false;
17797bd480fSBaptiste Daroussin 				}
17897bd480fSBaptiste Daroussin 			}
17997bd480fSBaptiste Daroussin 			else if (elt->type == UCL_OBJECT) {
18097bd480fSBaptiste Daroussin 				/* Define validator for additional fields */
18197bd480fSBaptiste Daroussin 				additional_schema = elt;
18297bd480fSBaptiste Daroussin 			}
18397bd480fSBaptiste Daroussin 			else {
18497bd480fSBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
18597bd480fSBaptiste Daroussin 						"additionalProperties attribute is invalid in schema");
18697bd480fSBaptiste Daroussin 				ret = false;
18797bd480fSBaptiste Daroussin 				break;
18897bd480fSBaptiste Daroussin 			}
18997bd480fSBaptiste Daroussin 		}
19097bd480fSBaptiste Daroussin 		else if (strcmp (ucl_object_key (elt), "required") == 0) {
19197bd480fSBaptiste Daroussin 			if (elt->type == UCL_ARRAY) {
19297bd480fSBaptiste Daroussin 				required = elt;
19397bd480fSBaptiste Daroussin 			}
19497bd480fSBaptiste Daroussin 			else {
19597bd480fSBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
19697bd480fSBaptiste Daroussin 						"required attribute is invalid in schema");
19797bd480fSBaptiste Daroussin 				ret = false;
19897bd480fSBaptiste Daroussin 				break;
19997bd480fSBaptiste Daroussin 			}
20097bd480fSBaptiste Daroussin 		}
20197bd480fSBaptiste Daroussin 		else if (strcmp (ucl_object_key (elt), "minProperties") == 0
20297bd480fSBaptiste Daroussin 				&& ucl_object_toint_safe (elt, &minmax)) {
20397bd480fSBaptiste Daroussin 			if (obj->len < minmax) {
20497bd480fSBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
20597bd480fSBaptiste Daroussin 						"object has not enough properties: %u, minimum is: %u",
20697bd480fSBaptiste Daroussin 						obj->len, (unsigned)minmax);
20797bd480fSBaptiste Daroussin 				ret = false;
20897bd480fSBaptiste Daroussin 				break;
20997bd480fSBaptiste Daroussin 			}
21097bd480fSBaptiste Daroussin 		}
21197bd480fSBaptiste Daroussin 		else if (strcmp (ucl_object_key (elt), "maxProperties") == 0
21297bd480fSBaptiste Daroussin 				&& ucl_object_toint_safe (elt, &minmax)) {
21397bd480fSBaptiste Daroussin 			if (obj->len > minmax) {
21497bd480fSBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
21597bd480fSBaptiste Daroussin 						"object has too many properties: %u, maximum is: %u",
21697bd480fSBaptiste Daroussin 						obj->len, (unsigned)minmax);
21797bd480fSBaptiste Daroussin 				ret = false;
21897bd480fSBaptiste Daroussin 				break;
21997bd480fSBaptiste Daroussin 			}
22097bd480fSBaptiste Daroussin 		}
22197bd480fSBaptiste Daroussin 		else if (strcmp (ucl_object_key (elt), "patternProperties") == 0) {
222273c26a3SBaptiste Daroussin 			const ucl_object_t *vobj;
223273c26a3SBaptiste Daroussin 			ucl_object_iter_t viter;
22497bd480fSBaptiste Daroussin 			piter = NULL;
225d9f0ce31SBaptiste Daroussin 			while (ret && (prop = ucl_object_iterate (elt, &piter, true)) != NULL) {
226273c26a3SBaptiste Daroussin 				viter = NULL;
227273c26a3SBaptiste Daroussin 				while (ret && (vobj = ucl_object_iterate (obj, &viter, true)) != NULL) {
228273c26a3SBaptiste Daroussin 					found = ucl_schema_test_pattern (vobj, ucl_object_key (prop), false);
22997bd480fSBaptiste Daroussin 					if (found) {
230d9f0ce31SBaptiste Daroussin 						ret = ucl_schema_validate (prop, found, true, err, root,
231d9f0ce31SBaptiste Daroussin 								ext_ref);
23297bd480fSBaptiste Daroussin 					}
23397bd480fSBaptiste Daroussin 				}
23497bd480fSBaptiste Daroussin 			}
235273c26a3SBaptiste Daroussin 		}
23697bd480fSBaptiste Daroussin 		else if (elt->type == UCL_OBJECT &&
23797bd480fSBaptiste Daroussin 				strcmp (ucl_object_key (elt), "dependencies") == 0) {
238d9f0ce31SBaptiste Daroussin 			ret = ucl_schema_validate_dependencies (elt, obj, err, root,
239d9f0ce31SBaptiste Daroussin 					ext_ref);
24097bd480fSBaptiste Daroussin 		}
24197bd480fSBaptiste Daroussin 	}
24297bd480fSBaptiste Daroussin 
24397bd480fSBaptiste Daroussin 	if (ret) {
24497bd480fSBaptiste Daroussin 		/* Additional properties */
24597bd480fSBaptiste Daroussin 		if (!allow_additional || additional_schema != NULL) {
24697bd480fSBaptiste Daroussin 			/* Check if we have exactly the same properties in schema and object */
247*a0409676SBaptiste Daroussin 			iter = ucl_object_iterate_new (obj);
248d9f0ce31SBaptiste Daroussin 			prop = ucl_object_lookup (schema, "properties");
249*a0409676SBaptiste Daroussin 			while ((elt = ucl_object_iterate_safe (iter, true)) != NULL) {
250d9f0ce31SBaptiste Daroussin 				found = ucl_object_lookup (prop, ucl_object_key (elt));
25197bd480fSBaptiste Daroussin 				if (found == NULL) {
25297bd480fSBaptiste Daroussin 					/* Try patternProperties */
253d9f0ce31SBaptiste Daroussin 					pat = ucl_object_lookup (schema, "patternProperties");
254*a0409676SBaptiste Daroussin 					piter = ucl_object_iterate_new (pat);
255*a0409676SBaptiste Daroussin 					while ((pelt = ucl_object_iterate_safe (piter, true)) != NULL) {
256273c26a3SBaptiste Daroussin 						found = ucl_schema_test_pattern (obj, ucl_object_key (pelt), true);
25797bd480fSBaptiste Daroussin 						if (found != NULL) {
25897bd480fSBaptiste Daroussin 							break;
25997bd480fSBaptiste Daroussin 						}
26097bd480fSBaptiste Daroussin 					}
261*a0409676SBaptiste Daroussin 					ucl_object_iterate_free (piter);
262*a0409676SBaptiste Daroussin 					piter = NULL;
26397bd480fSBaptiste Daroussin 				}
26497bd480fSBaptiste Daroussin 				if (found == NULL) {
26597bd480fSBaptiste Daroussin 					if (!allow_additional) {
26697bd480fSBaptiste Daroussin 						ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
26797bd480fSBaptiste Daroussin 								"object has non-allowed property %s",
26897bd480fSBaptiste Daroussin 								ucl_object_key (elt));
26997bd480fSBaptiste Daroussin 						ret = false;
27097bd480fSBaptiste Daroussin 						break;
27197bd480fSBaptiste Daroussin 					}
27297bd480fSBaptiste Daroussin 					else if (additional_schema != NULL) {
273d9f0ce31SBaptiste Daroussin 						if (!ucl_schema_validate (additional_schema, elt,
274d9f0ce31SBaptiste Daroussin 								true, err, root, ext_ref)) {
27597bd480fSBaptiste Daroussin 							ret = false;
27697bd480fSBaptiste Daroussin 							break;
27797bd480fSBaptiste Daroussin 						}
27897bd480fSBaptiste Daroussin 					}
27997bd480fSBaptiste Daroussin 				}
28097bd480fSBaptiste Daroussin 			}
281*a0409676SBaptiste Daroussin 			ucl_object_iterate_free (iter);
282*a0409676SBaptiste Daroussin 			iter = NULL;
28397bd480fSBaptiste Daroussin 		}
28497bd480fSBaptiste Daroussin 		/* Required properties */
28597bd480fSBaptiste Daroussin 		if (required != NULL) {
28697bd480fSBaptiste Daroussin 			iter = NULL;
287d9f0ce31SBaptiste Daroussin 			while ((elt = ucl_object_iterate (required, &iter, true)) != NULL) {
288d9f0ce31SBaptiste Daroussin 				if (ucl_object_lookup (obj, ucl_object_tostring (elt)) == NULL) {
28997bd480fSBaptiste Daroussin 					ucl_schema_create_error (err, UCL_SCHEMA_MISSING_PROPERTY, obj,
29097bd480fSBaptiste Daroussin 							"object has missing property %s",
29197bd480fSBaptiste Daroussin 							ucl_object_tostring (elt));
29297bd480fSBaptiste Daroussin 					ret = false;
29397bd480fSBaptiste Daroussin 					break;
29497bd480fSBaptiste Daroussin 				}
29597bd480fSBaptiste Daroussin 			}
29697bd480fSBaptiste Daroussin 		}
29797bd480fSBaptiste Daroussin 	}
29897bd480fSBaptiste Daroussin 
29997bd480fSBaptiste Daroussin 
30097bd480fSBaptiste Daroussin 	return ret;
30197bd480fSBaptiste Daroussin }
30297bd480fSBaptiste Daroussin 
30397bd480fSBaptiste Daroussin static bool
ucl_schema_validate_number(const ucl_object_t * schema,const ucl_object_t * obj,struct ucl_schema_error * err)304b04a7a0bSBaptiste Daroussin ucl_schema_validate_number (const ucl_object_t *schema,
305b04a7a0bSBaptiste Daroussin 		const ucl_object_t *obj, struct ucl_schema_error *err)
30697bd480fSBaptiste Daroussin {
307b04a7a0bSBaptiste Daroussin 	const ucl_object_t *elt, *test;
30897bd480fSBaptiste Daroussin 	ucl_object_iter_t iter = NULL;
30997bd480fSBaptiste Daroussin 	bool ret = true, exclusive = false;
31097bd480fSBaptiste Daroussin 	double constraint, val;
31197bd480fSBaptiste Daroussin 	const double alpha = 1e-16;
31297bd480fSBaptiste Daroussin 
313d9f0ce31SBaptiste Daroussin 	while (ret && (elt = ucl_object_iterate (schema, &iter, true)) != NULL) {
31497bd480fSBaptiste Daroussin 		if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
31597bd480fSBaptiste Daroussin 				strcmp (ucl_object_key (elt), "multipleOf") == 0) {
31697bd480fSBaptiste Daroussin 			constraint = ucl_object_todouble (elt);
31797bd480fSBaptiste Daroussin 			if (constraint <= 0) {
31897bd480fSBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
31997bd480fSBaptiste Daroussin 						"multipleOf must be greater than zero");
32097bd480fSBaptiste Daroussin 				ret = false;
32197bd480fSBaptiste Daroussin 				break;
32297bd480fSBaptiste Daroussin 			}
32397bd480fSBaptiste Daroussin 			val = ucl_object_todouble (obj);
32497bd480fSBaptiste Daroussin 			if (fabs (remainder (val, constraint)) > alpha) {
32597bd480fSBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
32697bd480fSBaptiste Daroussin 						"number %.4f is not multiple of %.4f, remainder is %.7f",
327*a0409676SBaptiste Daroussin 						val, constraint, remainder (val, constraint));
32897bd480fSBaptiste Daroussin 				ret = false;
32997bd480fSBaptiste Daroussin 				break;
33097bd480fSBaptiste Daroussin 			}
33197bd480fSBaptiste Daroussin 		}
33297bd480fSBaptiste Daroussin 		else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
33397bd480fSBaptiste Daroussin 			strcmp (ucl_object_key (elt), "maximum") == 0) {
33497bd480fSBaptiste Daroussin 			constraint = ucl_object_todouble (elt);
335d9f0ce31SBaptiste Daroussin 			test = ucl_object_lookup (schema, "exclusiveMaximum");
33697bd480fSBaptiste Daroussin 			if (test && test->type == UCL_BOOLEAN) {
33797bd480fSBaptiste Daroussin 				exclusive = ucl_object_toboolean (test);
33897bd480fSBaptiste Daroussin 			}
33997bd480fSBaptiste Daroussin 			val = ucl_object_todouble (obj);
34097bd480fSBaptiste Daroussin 			if (val > constraint || (exclusive && val >= constraint)) {
34197bd480fSBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
34297bd480fSBaptiste Daroussin 						"number is too big: %.3f, maximum is: %.3f",
34397bd480fSBaptiste Daroussin 						val, constraint);
34497bd480fSBaptiste Daroussin 				ret = false;
34597bd480fSBaptiste Daroussin 				break;
34697bd480fSBaptiste Daroussin 			}
34797bd480fSBaptiste Daroussin 		}
34897bd480fSBaptiste Daroussin 		else if ((elt->type == UCL_FLOAT || elt->type == UCL_INT) &&
34997bd480fSBaptiste Daroussin 				strcmp (ucl_object_key (elt), "minimum") == 0) {
35097bd480fSBaptiste Daroussin 			constraint = ucl_object_todouble (elt);
351d9f0ce31SBaptiste Daroussin 			test = ucl_object_lookup (schema, "exclusiveMinimum");
35297bd480fSBaptiste Daroussin 			if (test && test->type == UCL_BOOLEAN) {
35397bd480fSBaptiste Daroussin 				exclusive = ucl_object_toboolean (test);
35497bd480fSBaptiste Daroussin 			}
35597bd480fSBaptiste Daroussin 			val = ucl_object_todouble (obj);
35697bd480fSBaptiste Daroussin 			if (val < constraint || (exclusive && val <= constraint)) {
35797bd480fSBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
35897bd480fSBaptiste Daroussin 						"number is too small: %.3f, minimum is: %.3f",
35997bd480fSBaptiste Daroussin 						val, constraint);
36097bd480fSBaptiste Daroussin 				ret = false;
36197bd480fSBaptiste Daroussin 				break;
36297bd480fSBaptiste Daroussin 			}
36397bd480fSBaptiste Daroussin 		}
36497bd480fSBaptiste Daroussin 	}
36597bd480fSBaptiste Daroussin 
36697bd480fSBaptiste Daroussin 	return ret;
36797bd480fSBaptiste Daroussin }
36897bd480fSBaptiste Daroussin 
36997bd480fSBaptiste Daroussin static bool
ucl_schema_validate_string(const ucl_object_t * schema,const ucl_object_t * obj,struct ucl_schema_error * err)370b04a7a0bSBaptiste Daroussin ucl_schema_validate_string (const ucl_object_t *schema,
371b04a7a0bSBaptiste Daroussin 		const ucl_object_t *obj, struct ucl_schema_error *err)
37297bd480fSBaptiste Daroussin {
373b04a7a0bSBaptiste Daroussin 	const ucl_object_t *elt;
37497bd480fSBaptiste Daroussin 	ucl_object_iter_t iter = NULL;
37597bd480fSBaptiste Daroussin 	bool ret = true;
37697bd480fSBaptiste Daroussin 	int64_t constraint;
377b04a7a0bSBaptiste Daroussin #ifdef HAVE_REGEX_H
37897bd480fSBaptiste Daroussin 	regex_t re;
379b04a7a0bSBaptiste Daroussin #endif
38097bd480fSBaptiste Daroussin 
381d9f0ce31SBaptiste Daroussin 	while (ret && (elt = ucl_object_iterate (schema, &iter, true)) != NULL) {
38297bd480fSBaptiste Daroussin 		if (elt->type == UCL_INT &&
38397bd480fSBaptiste Daroussin 			strcmp (ucl_object_key (elt), "maxLength") == 0) {
38497bd480fSBaptiste Daroussin 			constraint = ucl_object_toint (elt);
38597bd480fSBaptiste Daroussin 			if (obj->len > constraint) {
38697bd480fSBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
387*a0409676SBaptiste Daroussin 						"string is too big: %u, maximum is: %" PRId64,
38897bd480fSBaptiste Daroussin 						obj->len, constraint);
38997bd480fSBaptiste Daroussin 				ret = false;
39097bd480fSBaptiste Daroussin 				break;
39197bd480fSBaptiste Daroussin 			}
39297bd480fSBaptiste Daroussin 		}
39397bd480fSBaptiste Daroussin 		else if (elt->type == UCL_INT &&
39497bd480fSBaptiste Daroussin 				strcmp (ucl_object_key (elt), "minLength") == 0) {
39597bd480fSBaptiste Daroussin 			constraint = ucl_object_toint (elt);
39697bd480fSBaptiste Daroussin 			if (obj->len < constraint) {
39797bd480fSBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
398*a0409676SBaptiste Daroussin 						"string is too short: %u, minimum is: %" PRId64,
39997bd480fSBaptiste Daroussin 						obj->len, constraint);
40097bd480fSBaptiste Daroussin 				ret = false;
40197bd480fSBaptiste Daroussin 				break;
40297bd480fSBaptiste Daroussin 			}
40397bd480fSBaptiste Daroussin 		}
404b04a7a0bSBaptiste Daroussin #ifdef HAVE_REGEX_H
40597bd480fSBaptiste Daroussin 		else if (elt->type == UCL_STRING &&
40697bd480fSBaptiste Daroussin 				strcmp (ucl_object_key (elt), "pattern") == 0) {
40797bd480fSBaptiste Daroussin 			if (regcomp (&re, ucl_object_tostring (elt),
40897bd480fSBaptiste Daroussin 					REG_EXTENDED | REG_NOSUB) != 0) {
40997bd480fSBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
41097bd480fSBaptiste Daroussin 						"cannot compile pattern %s", ucl_object_tostring (elt));
41197bd480fSBaptiste Daroussin 				ret = false;
41297bd480fSBaptiste Daroussin 				break;
41397bd480fSBaptiste Daroussin 			}
41497bd480fSBaptiste Daroussin 			if (regexec (&re, ucl_object_tostring (obj), 0, NULL, 0) != 0) {
41597bd480fSBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
41697bd480fSBaptiste Daroussin 						"string doesn't match regexp %s",
41797bd480fSBaptiste Daroussin 						ucl_object_tostring (elt));
41897bd480fSBaptiste Daroussin 				ret = false;
41997bd480fSBaptiste Daroussin 			}
42097bd480fSBaptiste Daroussin 			regfree (&re);
42197bd480fSBaptiste Daroussin 		}
422b04a7a0bSBaptiste Daroussin #endif
42397bd480fSBaptiste Daroussin 	}
42497bd480fSBaptiste Daroussin 
42597bd480fSBaptiste Daroussin 	return ret;
42697bd480fSBaptiste Daroussin }
42797bd480fSBaptiste Daroussin 
42897bd480fSBaptiste Daroussin struct ucl_compare_node {
429b04a7a0bSBaptiste Daroussin 	const ucl_object_t *obj;
43097bd480fSBaptiste Daroussin 	TREE_ENTRY(ucl_compare_node) link;
43197bd480fSBaptiste Daroussin 	struct ucl_compare_node *next;
43297bd480fSBaptiste Daroussin };
43397bd480fSBaptiste Daroussin 
43497bd480fSBaptiste Daroussin typedef TREE_HEAD(_tree, ucl_compare_node) ucl_compare_tree_t;
43597bd480fSBaptiste Daroussin 
TREE_DEFINE(ucl_compare_node,link)43697bd480fSBaptiste Daroussin TREE_DEFINE(ucl_compare_node, link)
43797bd480fSBaptiste Daroussin 
43897bd480fSBaptiste Daroussin static int
43997bd480fSBaptiste Daroussin ucl_schema_elt_compare (struct ucl_compare_node *n1, struct ucl_compare_node *n2)
44097bd480fSBaptiste Daroussin {
441b04a7a0bSBaptiste Daroussin 	const ucl_object_t *o1 = n1->obj, *o2 = n2->obj;
44297bd480fSBaptiste Daroussin 
44397bd480fSBaptiste Daroussin 	return ucl_object_compare (o1, o2);
44497bd480fSBaptiste Daroussin }
44597bd480fSBaptiste Daroussin 
44697bd480fSBaptiste Daroussin static bool
ucl_schema_array_is_unique(const ucl_object_t * obj,struct ucl_schema_error * err)447b04a7a0bSBaptiste Daroussin ucl_schema_array_is_unique (const ucl_object_t *obj, struct ucl_schema_error *err)
44897bd480fSBaptiste Daroussin {
44997bd480fSBaptiste Daroussin 	ucl_compare_tree_t tree = TREE_INITIALIZER (ucl_schema_elt_compare);
45097bd480fSBaptiste Daroussin 	ucl_object_iter_t iter = NULL;
451b04a7a0bSBaptiste Daroussin 	const ucl_object_t *elt;
45297bd480fSBaptiste Daroussin 	struct ucl_compare_node *node, test, *nodes = NULL, *tmp;
45397bd480fSBaptiste Daroussin 	bool ret = true;
45497bd480fSBaptiste Daroussin 
455d9f0ce31SBaptiste Daroussin 	while ((elt = ucl_object_iterate (obj, &iter, true)) != NULL) {
45697bd480fSBaptiste Daroussin 		test.obj = elt;
45797bd480fSBaptiste Daroussin 		node = TREE_FIND (&tree, ucl_compare_node, link, &test);
45897bd480fSBaptiste Daroussin 		if (node != NULL) {
45997bd480fSBaptiste Daroussin 			ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, elt,
46097bd480fSBaptiste Daroussin 					"duplicate values detected while uniqueItems is true");
46197bd480fSBaptiste Daroussin 			ret = false;
46297bd480fSBaptiste Daroussin 			break;
46397bd480fSBaptiste Daroussin 		}
46497bd480fSBaptiste Daroussin 		node = calloc (1, sizeof (*node));
46597bd480fSBaptiste Daroussin 		if (node == NULL) {
46697bd480fSBaptiste Daroussin 			ucl_schema_create_error (err, UCL_SCHEMA_UNKNOWN, elt,
46797bd480fSBaptiste Daroussin 					"cannot allocate tree node");
46897bd480fSBaptiste Daroussin 			ret = false;
46997bd480fSBaptiste Daroussin 			break;
47097bd480fSBaptiste Daroussin 		}
47197bd480fSBaptiste Daroussin 		node->obj = elt;
47297bd480fSBaptiste Daroussin 		TREE_INSERT (&tree, ucl_compare_node, link, node);
47397bd480fSBaptiste Daroussin 		LL_PREPEND (nodes, node);
47497bd480fSBaptiste Daroussin 	}
47597bd480fSBaptiste Daroussin 
47697bd480fSBaptiste Daroussin 	LL_FOREACH_SAFE (nodes, node, tmp) {
47797bd480fSBaptiste Daroussin 		free (node);
47897bd480fSBaptiste Daroussin 	}
47997bd480fSBaptiste Daroussin 
48097bd480fSBaptiste Daroussin 	return ret;
48197bd480fSBaptiste Daroussin }
48297bd480fSBaptiste Daroussin 
48397bd480fSBaptiste Daroussin static bool
ucl_schema_validate_array(const ucl_object_t * schema,const ucl_object_t * obj,struct ucl_schema_error * err,const ucl_object_t * root,ucl_object_t * ext_ref)484b04a7a0bSBaptiste Daroussin ucl_schema_validate_array (const ucl_object_t *schema,
485b04a7a0bSBaptiste Daroussin 		const ucl_object_t *obj, struct ucl_schema_error *err,
486d9f0ce31SBaptiste Daroussin 		const ucl_object_t *root,
487d9f0ce31SBaptiste Daroussin 		ucl_object_t *ext_ref)
48897bd480fSBaptiste Daroussin {
489b04a7a0bSBaptiste Daroussin 	const ucl_object_t *elt, *it, *found, *additional_schema = NULL,
49097bd480fSBaptiste Daroussin 			*first_unvalidated = NULL;
49197bd480fSBaptiste Daroussin 	ucl_object_iter_t iter = NULL, piter = NULL;
49297bd480fSBaptiste Daroussin 	bool ret = true, allow_additional = true, need_unique = false;
49397bd480fSBaptiste Daroussin 	int64_t minmax;
4948e3b1ab2SBaptiste Daroussin 	unsigned int idx = 0;
49597bd480fSBaptiste Daroussin 
496d9f0ce31SBaptiste Daroussin 	while (ret && (elt = ucl_object_iterate (schema, &iter, true)) != NULL) {
49797bd480fSBaptiste Daroussin 		if (strcmp (ucl_object_key (elt), "items") == 0) {
49897bd480fSBaptiste Daroussin 			if (elt->type == UCL_ARRAY) {
4998e3b1ab2SBaptiste Daroussin 				found = ucl_array_head (obj);
500d9f0ce31SBaptiste Daroussin 				while (ret && (it = ucl_object_iterate (elt, &piter, true)) != NULL) {
50197bd480fSBaptiste Daroussin 					if (found) {
502d9f0ce31SBaptiste Daroussin 						ret = ucl_schema_validate (it, found, false, err,
503d9f0ce31SBaptiste Daroussin 								root, ext_ref);
5048e3b1ab2SBaptiste Daroussin 						found = ucl_array_find_index (obj, ++idx);
50597bd480fSBaptiste Daroussin 					}
50697bd480fSBaptiste Daroussin 				}
50797bd480fSBaptiste Daroussin 				if (found != NULL) {
50897bd480fSBaptiste Daroussin 					/* The first element that is not validated */
50997bd480fSBaptiste Daroussin 					first_unvalidated = found;
51097bd480fSBaptiste Daroussin 				}
51197bd480fSBaptiste Daroussin 			}
51297bd480fSBaptiste Daroussin 			else if (elt->type == UCL_OBJECT) {
51397bd480fSBaptiste Daroussin 				/* Validate all items using the specified schema */
514d9f0ce31SBaptiste Daroussin 				while (ret && (it = ucl_object_iterate (obj, &piter, true)) != NULL) {
515d9f0ce31SBaptiste Daroussin 					ret = ucl_schema_validate (elt, it, false, err, root,
516d9f0ce31SBaptiste Daroussin 							ext_ref);
51797bd480fSBaptiste Daroussin 				}
51897bd480fSBaptiste Daroussin 			}
51997bd480fSBaptiste Daroussin 			else {
52097bd480fSBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
52197bd480fSBaptiste Daroussin 						"items attribute is invalid in schema");
52297bd480fSBaptiste Daroussin 				ret = false;
52397bd480fSBaptiste Daroussin 				break;
52497bd480fSBaptiste Daroussin 			}
52597bd480fSBaptiste Daroussin 		}
52697bd480fSBaptiste Daroussin 		else if (strcmp (ucl_object_key (elt), "additionalItems") == 0) {
52797bd480fSBaptiste Daroussin 			if (elt->type == UCL_BOOLEAN) {
52897bd480fSBaptiste Daroussin 				if (!ucl_object_toboolean (elt)) {
52997bd480fSBaptiste Daroussin 					/* Deny additional fields completely */
53097bd480fSBaptiste Daroussin 					allow_additional = false;
53197bd480fSBaptiste Daroussin 				}
53297bd480fSBaptiste Daroussin 			}
53397bd480fSBaptiste Daroussin 			else if (elt->type == UCL_OBJECT) {
53497bd480fSBaptiste Daroussin 				/* Define validator for additional fields */
53597bd480fSBaptiste Daroussin 				additional_schema = elt;
53697bd480fSBaptiste Daroussin 			}
53797bd480fSBaptiste Daroussin 			else {
53897bd480fSBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, elt,
53997bd480fSBaptiste Daroussin 						"additionalItems attribute is invalid in schema");
54097bd480fSBaptiste Daroussin 				ret = false;
54197bd480fSBaptiste Daroussin 				break;
54297bd480fSBaptiste Daroussin 			}
54397bd480fSBaptiste Daroussin 		}
54497bd480fSBaptiste Daroussin 		else if (elt->type == UCL_BOOLEAN &&
54597bd480fSBaptiste Daroussin 				strcmp (ucl_object_key (elt), "uniqueItems") == 0) {
54697bd480fSBaptiste Daroussin 			need_unique = ucl_object_toboolean (elt);
54797bd480fSBaptiste Daroussin 		}
54897bd480fSBaptiste Daroussin 		else if (strcmp (ucl_object_key (elt), "minItems") == 0
54997bd480fSBaptiste Daroussin 				&& ucl_object_toint_safe (elt, &minmax)) {
55097bd480fSBaptiste Daroussin 			if (obj->len < minmax) {
55197bd480fSBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
55297bd480fSBaptiste Daroussin 						"array has not enough items: %u, minimum is: %u",
55397bd480fSBaptiste Daroussin 						obj->len, (unsigned)minmax);
55497bd480fSBaptiste Daroussin 				ret = false;
55597bd480fSBaptiste Daroussin 				break;
55697bd480fSBaptiste Daroussin 			}
55797bd480fSBaptiste Daroussin 		}
55897bd480fSBaptiste Daroussin 		else if (strcmp (ucl_object_key (elt), "maxItems") == 0
55997bd480fSBaptiste Daroussin 				&& ucl_object_toint_safe (elt, &minmax)) {
56097bd480fSBaptiste Daroussin 			if (obj->len > minmax) {
56197bd480fSBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
56297bd480fSBaptiste Daroussin 						"array has too many items: %u, maximum is: %u",
56397bd480fSBaptiste Daroussin 						obj->len, (unsigned)minmax);
56497bd480fSBaptiste Daroussin 				ret = false;
56597bd480fSBaptiste Daroussin 				break;
56697bd480fSBaptiste Daroussin 			}
56797bd480fSBaptiste Daroussin 		}
56897bd480fSBaptiste Daroussin 	}
56997bd480fSBaptiste Daroussin 
57097bd480fSBaptiste Daroussin 	if (ret) {
57197bd480fSBaptiste Daroussin 		/* Additional properties */
57297bd480fSBaptiste Daroussin 		if (!allow_additional || additional_schema != NULL) {
57397bd480fSBaptiste Daroussin 			if (first_unvalidated != NULL) {
57497bd480fSBaptiste Daroussin 				if (!allow_additional) {
57597bd480fSBaptiste Daroussin 					ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
57697bd480fSBaptiste Daroussin 							"array has undefined item");
57797bd480fSBaptiste Daroussin 					ret = false;
57897bd480fSBaptiste Daroussin 				}
57997bd480fSBaptiste Daroussin 				else if (additional_schema != NULL) {
5808e3b1ab2SBaptiste Daroussin 					elt = ucl_array_find_index (obj, idx);
58197bd480fSBaptiste Daroussin 					while (elt) {
58297bd480fSBaptiste Daroussin 						if (!ucl_schema_validate (additional_schema, elt, false,
583d9f0ce31SBaptiste Daroussin 								err, root, ext_ref)) {
58497bd480fSBaptiste Daroussin 							ret = false;
58597bd480fSBaptiste Daroussin 							break;
58697bd480fSBaptiste Daroussin 						}
5878e3b1ab2SBaptiste Daroussin 						elt = ucl_array_find_index (obj, idx ++);
58897bd480fSBaptiste Daroussin 					}
58997bd480fSBaptiste Daroussin 				}
59097bd480fSBaptiste Daroussin 			}
59197bd480fSBaptiste Daroussin 		}
59297bd480fSBaptiste Daroussin 		/* Required properties */
59397bd480fSBaptiste Daroussin 		if (ret && need_unique) {
59497bd480fSBaptiste Daroussin 			ret = ucl_schema_array_is_unique (obj, err);
59597bd480fSBaptiste Daroussin 		}
59697bd480fSBaptiste Daroussin 	}
59797bd480fSBaptiste Daroussin 
59897bd480fSBaptiste Daroussin 	return ret;
59997bd480fSBaptiste Daroussin }
60097bd480fSBaptiste Daroussin 
60197bd480fSBaptiste Daroussin /*
60297bd480fSBaptiste Daroussin  * Returns whether this object is allowed for this type
60397bd480fSBaptiste Daroussin  */
60497bd480fSBaptiste Daroussin static bool
ucl_schema_type_is_allowed(const ucl_object_t * type,const ucl_object_t * obj,struct ucl_schema_error * err)605b04a7a0bSBaptiste Daroussin ucl_schema_type_is_allowed (const ucl_object_t *type, const ucl_object_t *obj,
60697bd480fSBaptiste Daroussin 		struct ucl_schema_error *err)
60797bd480fSBaptiste Daroussin {
60897bd480fSBaptiste Daroussin 	ucl_object_iter_t iter = NULL;
609b04a7a0bSBaptiste Daroussin 	const ucl_object_t *elt;
61097bd480fSBaptiste Daroussin 	const char *type_str;
61197bd480fSBaptiste Daroussin 	ucl_type_t t;
61297bd480fSBaptiste Daroussin 
61397bd480fSBaptiste Daroussin 	if (type == NULL) {
61497bd480fSBaptiste Daroussin 		/* Any type is allowed */
61597bd480fSBaptiste Daroussin 		return true;
61697bd480fSBaptiste Daroussin 	}
61797bd480fSBaptiste Daroussin 
61897bd480fSBaptiste Daroussin 	if (type->type == UCL_ARRAY) {
61997bd480fSBaptiste Daroussin 		/* One of allowed types */
620d9f0ce31SBaptiste Daroussin 		while ((elt = ucl_object_iterate (type, &iter, true)) != NULL) {
62197bd480fSBaptiste Daroussin 			if (ucl_schema_type_is_allowed (elt, obj, err)) {
62297bd480fSBaptiste Daroussin 				return true;
62397bd480fSBaptiste Daroussin 			}
62497bd480fSBaptiste Daroussin 		}
62597bd480fSBaptiste Daroussin 	}
62697bd480fSBaptiste Daroussin 	else if (type->type == UCL_STRING) {
62797bd480fSBaptiste Daroussin 		type_str = ucl_object_tostring (type);
628d9f0ce31SBaptiste Daroussin 		if (!ucl_object_string_to_type (type_str, &t)) {
62997bd480fSBaptiste Daroussin 			ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, type,
63097bd480fSBaptiste Daroussin 					"Type attribute is invalid in schema");
63197bd480fSBaptiste Daroussin 			return false;
63297bd480fSBaptiste Daroussin 		}
63397bd480fSBaptiste Daroussin 		if (obj->type != t) {
63497bd480fSBaptiste Daroussin 			/* Some types are actually compatible */
63597bd480fSBaptiste Daroussin 			if (obj->type == UCL_TIME && t == UCL_FLOAT) {
63697bd480fSBaptiste Daroussin 				return true;
63797bd480fSBaptiste Daroussin 			}
63897bd480fSBaptiste Daroussin 			else if (obj->type == UCL_INT && t == UCL_FLOAT) {
63997bd480fSBaptiste Daroussin 				return true;
64097bd480fSBaptiste Daroussin 			}
64197bd480fSBaptiste Daroussin 			else {
64297bd480fSBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_TYPE_MISMATCH, obj,
64397bd480fSBaptiste Daroussin 						"Invalid type of %s, expected %s",
64497bd480fSBaptiste Daroussin 						ucl_object_type_to_string (obj->type),
64597bd480fSBaptiste Daroussin 						ucl_object_type_to_string (t));
64697bd480fSBaptiste Daroussin 			}
64797bd480fSBaptiste Daroussin 		}
64897bd480fSBaptiste Daroussin 		else {
64997bd480fSBaptiste Daroussin 			/* Types are equal */
65097bd480fSBaptiste Daroussin 			return true;
65197bd480fSBaptiste Daroussin 		}
65297bd480fSBaptiste Daroussin 	}
65397bd480fSBaptiste Daroussin 
65497bd480fSBaptiste Daroussin 	return false;
65597bd480fSBaptiste Daroussin }
65697bd480fSBaptiste Daroussin 
65797bd480fSBaptiste Daroussin /*
65897bd480fSBaptiste Daroussin  * Check if object is equal to one of elements of enum
65997bd480fSBaptiste Daroussin  */
66097bd480fSBaptiste Daroussin static bool
ucl_schema_validate_enum(const ucl_object_t * en,const ucl_object_t * obj,struct ucl_schema_error * err)661b04a7a0bSBaptiste Daroussin ucl_schema_validate_enum (const ucl_object_t *en, const ucl_object_t *obj,
66297bd480fSBaptiste Daroussin 		struct ucl_schema_error *err)
66397bd480fSBaptiste Daroussin {
66497bd480fSBaptiste Daroussin 	ucl_object_iter_t iter = NULL;
665b04a7a0bSBaptiste Daroussin 	const ucl_object_t *elt;
66697bd480fSBaptiste Daroussin 	bool ret = false;
66797bd480fSBaptiste Daroussin 
668d9f0ce31SBaptiste Daroussin 	while ((elt = ucl_object_iterate (en, &iter, true)) != NULL) {
66997bd480fSBaptiste Daroussin 		if (ucl_object_compare (elt, obj) == 0) {
67097bd480fSBaptiste Daroussin 			ret = true;
67197bd480fSBaptiste Daroussin 			break;
67297bd480fSBaptiste Daroussin 		}
67397bd480fSBaptiste Daroussin 	}
67497bd480fSBaptiste Daroussin 
67597bd480fSBaptiste Daroussin 	if (!ret) {
67697bd480fSBaptiste Daroussin 		ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
67797bd480fSBaptiste Daroussin 				"object is not one of enumerated patterns");
67897bd480fSBaptiste Daroussin 	}
67997bd480fSBaptiste Daroussin 
68097bd480fSBaptiste Daroussin 	return ret;
68197bd480fSBaptiste Daroussin }
68297bd480fSBaptiste Daroussin 
68397bd480fSBaptiste Daroussin 
68497bd480fSBaptiste Daroussin /*
68597bd480fSBaptiste Daroussin  * Check a single ref component
68697bd480fSBaptiste Daroussin  */
687b04a7a0bSBaptiste Daroussin static const ucl_object_t *
ucl_schema_resolve_ref_component(const ucl_object_t * cur,const char * refc,int len,struct ucl_schema_error * err)688b04a7a0bSBaptiste Daroussin ucl_schema_resolve_ref_component (const ucl_object_t *cur,
68997bd480fSBaptiste Daroussin 		const char *refc, int len,
69097bd480fSBaptiste Daroussin 		struct ucl_schema_error *err)
69197bd480fSBaptiste Daroussin {
692b04a7a0bSBaptiste Daroussin 	const ucl_object_t *res = NULL;
69397bd480fSBaptiste Daroussin 	char *err_str;
69497bd480fSBaptiste Daroussin 	int num, i;
69597bd480fSBaptiste Daroussin 
69697bd480fSBaptiste Daroussin 	if (cur->type == UCL_OBJECT) {
69797bd480fSBaptiste Daroussin 		/* Find a key inside an object */
698d9f0ce31SBaptiste Daroussin 		res = ucl_object_lookup_len (cur, refc, len);
69997bd480fSBaptiste Daroussin 		if (res == NULL) {
70097bd480fSBaptiste Daroussin 			ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
70197bd480fSBaptiste Daroussin 					"reference %s is invalid, missing path component", refc);
70297bd480fSBaptiste Daroussin 			return NULL;
70397bd480fSBaptiste Daroussin 		}
70497bd480fSBaptiste Daroussin 	}
70597bd480fSBaptiste Daroussin 	else if (cur->type == UCL_ARRAY) {
70697bd480fSBaptiste Daroussin 		/* We must figure out a number inside array */
70797bd480fSBaptiste Daroussin 		num = strtoul (refc, &err_str, 10);
70897bd480fSBaptiste Daroussin 		if (err_str != NULL && *err_str != '/' && *err_str != '\0') {
70997bd480fSBaptiste Daroussin 			ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
71097bd480fSBaptiste Daroussin 					"reference %s is invalid, invalid item number", refc);
71197bd480fSBaptiste Daroussin 			return NULL;
71297bd480fSBaptiste Daroussin 		}
7138e3b1ab2SBaptiste Daroussin 		res = ucl_array_head (cur);
71497bd480fSBaptiste Daroussin 		i = 0;
71597bd480fSBaptiste Daroussin 		while (res != NULL) {
71697bd480fSBaptiste Daroussin 			if (i == num) {
71797bd480fSBaptiste Daroussin 				break;
71897bd480fSBaptiste Daroussin 			}
71997bd480fSBaptiste Daroussin 			res = res->next;
72097bd480fSBaptiste Daroussin 		}
72197bd480fSBaptiste Daroussin 		if (res == NULL) {
72297bd480fSBaptiste Daroussin 			ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, cur,
72397bd480fSBaptiste Daroussin 					"reference %s is invalid, item number %d does not exist",
72497bd480fSBaptiste Daroussin 					refc, num);
72597bd480fSBaptiste Daroussin 			return NULL;
72697bd480fSBaptiste Daroussin 		}
72797bd480fSBaptiste Daroussin 	}
72897bd480fSBaptiste Daroussin 	else {
72997bd480fSBaptiste Daroussin 		ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
73097bd480fSBaptiste Daroussin 				"reference %s is invalid, contains primitive object in the path",
73197bd480fSBaptiste Daroussin 				refc);
73297bd480fSBaptiste Daroussin 		return NULL;
73397bd480fSBaptiste Daroussin 	}
73497bd480fSBaptiste Daroussin 
73597bd480fSBaptiste Daroussin 	return res;
73697bd480fSBaptiste Daroussin }
73797bd480fSBaptiste Daroussin /*
73897bd480fSBaptiste Daroussin  * Find reference schema
73997bd480fSBaptiste Daroussin  */
740b04a7a0bSBaptiste Daroussin static const ucl_object_t *
ucl_schema_resolve_ref(const ucl_object_t * root,const char * ref,struct ucl_schema_error * err,ucl_object_t * ext_ref,ucl_object_t const ** nroot)741b04a7a0bSBaptiste Daroussin ucl_schema_resolve_ref (const ucl_object_t *root, const char *ref,
742d9f0ce31SBaptiste Daroussin 		struct ucl_schema_error *err, ucl_object_t *ext_ref,
743d9f0ce31SBaptiste Daroussin 		ucl_object_t const ** nroot)
74497bd480fSBaptiste Daroussin {
745d9f0ce31SBaptiste Daroussin 	UT_string *url_err = NULL;
746d9f0ce31SBaptiste Daroussin 	struct ucl_parser *parser;
747d9f0ce31SBaptiste Daroussin 	const ucl_object_t *res = NULL, *ext_obj = NULL;
748d9f0ce31SBaptiste Daroussin 	ucl_object_t *url_obj;
749d9f0ce31SBaptiste Daroussin 	const char *p, *c, *hash_ptr = NULL;
750d9f0ce31SBaptiste Daroussin 	char *url_copy = NULL;
751d9f0ce31SBaptiste Daroussin 	unsigned char *url_buf;
752d9f0ce31SBaptiste Daroussin 	size_t url_buflen;
75397bd480fSBaptiste Daroussin 
75497bd480fSBaptiste Daroussin 	if (ref[0] != '#') {
755d9f0ce31SBaptiste Daroussin 		hash_ptr = strrchr (ref, '#');
756d9f0ce31SBaptiste Daroussin 
757d9f0ce31SBaptiste Daroussin 		if (hash_ptr) {
758d9f0ce31SBaptiste Daroussin 			url_copy = malloc (hash_ptr - ref + 1);
759d9f0ce31SBaptiste Daroussin 
760d9f0ce31SBaptiste Daroussin 			if (url_copy == NULL) {
761d9f0ce31SBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_INTERNAL_ERROR, root,
762d9f0ce31SBaptiste Daroussin 						"cannot allocate memory");
76397bd480fSBaptiste Daroussin 				return NULL;
76497bd480fSBaptiste Daroussin 			}
765d9f0ce31SBaptiste Daroussin 
766d9f0ce31SBaptiste Daroussin 			ucl_strlcpy (url_copy, ref, hash_ptr - ref + 1);
767d9f0ce31SBaptiste Daroussin 			p = url_copy;
76897bd480fSBaptiste Daroussin 		}
76997bd480fSBaptiste Daroussin 		else {
770d9f0ce31SBaptiste Daroussin 			/* Full URL */
771d9f0ce31SBaptiste Daroussin 			p = ref;
772d9f0ce31SBaptiste Daroussin 		}
773d9f0ce31SBaptiste Daroussin 
774d9f0ce31SBaptiste Daroussin 		ext_obj = ucl_object_lookup (ext_ref, p);
775d9f0ce31SBaptiste Daroussin 
776d9f0ce31SBaptiste Daroussin 		if (ext_obj == NULL) {
777d9f0ce31SBaptiste Daroussin 			if (ucl_strnstr (p, "://", strlen (p)) != NULL) {
778d9f0ce31SBaptiste Daroussin 				if (!ucl_fetch_url (p, &url_buf, &url_buflen, &url_err, true)) {
779d9f0ce31SBaptiste Daroussin 
780d9f0ce31SBaptiste Daroussin 					ucl_schema_create_error (err,
781d9f0ce31SBaptiste Daroussin 							UCL_SCHEMA_INVALID_SCHEMA,
782d9f0ce31SBaptiste Daroussin 							root,
783d9f0ce31SBaptiste Daroussin 							"cannot fetch reference %s: %s",
784d9f0ce31SBaptiste Daroussin 							p,
785d9f0ce31SBaptiste Daroussin 							url_err != NULL ? utstring_body (url_err)
786d9f0ce31SBaptiste Daroussin 											: "unknown");
787d9f0ce31SBaptiste Daroussin 					free (url_copy);
788d9f0ce31SBaptiste Daroussin 
78997bd480fSBaptiste Daroussin 					return NULL;
79097bd480fSBaptiste Daroussin 				}
791d9f0ce31SBaptiste Daroussin 			}
792d9f0ce31SBaptiste Daroussin 			else {
793d9f0ce31SBaptiste Daroussin 				if (!ucl_fetch_file (p, &url_buf, &url_buflen, &url_err,
794d9f0ce31SBaptiste Daroussin 						true)) {
795d9f0ce31SBaptiste Daroussin 					ucl_schema_create_error (err,
796d9f0ce31SBaptiste Daroussin 							UCL_SCHEMA_INVALID_SCHEMA,
797d9f0ce31SBaptiste Daroussin 							root,
798d9f0ce31SBaptiste Daroussin 							"cannot fetch reference %s: %s",
799d9f0ce31SBaptiste Daroussin 							p,
800d9f0ce31SBaptiste Daroussin 							url_err != NULL ? utstring_body (url_err)
801d9f0ce31SBaptiste Daroussin 											: "unknown");
802d9f0ce31SBaptiste Daroussin 					free (url_copy);
803d9f0ce31SBaptiste Daroussin 
804d9f0ce31SBaptiste Daroussin 					return NULL;
805d9f0ce31SBaptiste Daroussin 				}
806d9f0ce31SBaptiste Daroussin 			}
807d9f0ce31SBaptiste Daroussin 
808d9f0ce31SBaptiste Daroussin 			parser = ucl_parser_new (0);
809d9f0ce31SBaptiste Daroussin 
810d9f0ce31SBaptiste Daroussin 			if (!ucl_parser_add_chunk (parser, url_buf, url_buflen)) {
811d9f0ce31SBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, root,
812d9f0ce31SBaptiste Daroussin 						"cannot fetch reference %s: %s", p,
813d9f0ce31SBaptiste Daroussin 						ucl_parser_get_error (parser));
814d9f0ce31SBaptiste Daroussin 				ucl_parser_free (parser);
815d9f0ce31SBaptiste Daroussin 				free (url_copy);
816d9f0ce31SBaptiste Daroussin 
817d9f0ce31SBaptiste Daroussin 				return NULL;
818d9f0ce31SBaptiste Daroussin 			}
819d9f0ce31SBaptiste Daroussin 
820d9f0ce31SBaptiste Daroussin 			url_obj = ucl_parser_get_object (parser);
821d9f0ce31SBaptiste Daroussin 			ext_obj = url_obj;
822d9f0ce31SBaptiste Daroussin 			ucl_object_insert_key (ext_ref, url_obj, p, 0, true);
823d9f0ce31SBaptiste Daroussin 			free (url_buf);
824d9f0ce31SBaptiste Daroussin 		}
825d9f0ce31SBaptiste Daroussin 
826d9f0ce31SBaptiste Daroussin 		free (url_copy);
827d9f0ce31SBaptiste Daroussin 
828d9f0ce31SBaptiste Daroussin 		if (hash_ptr) {
829d9f0ce31SBaptiste Daroussin 			p = hash_ptr + 1;
830d9f0ce31SBaptiste Daroussin 		}
831d9f0ce31SBaptiste Daroussin 		else {
832d9f0ce31SBaptiste Daroussin 			p = "";
833d9f0ce31SBaptiste Daroussin 		}
834d9f0ce31SBaptiste Daroussin 	}
835d9f0ce31SBaptiste Daroussin 	else {
836d9f0ce31SBaptiste Daroussin 		p = ref + 1;
837d9f0ce31SBaptiste Daroussin 	}
838d9f0ce31SBaptiste Daroussin 
839d9f0ce31SBaptiste Daroussin 	res = ext_obj != NULL ? ext_obj : root;
840d9f0ce31SBaptiste Daroussin 	*nroot = res;
841d9f0ce31SBaptiste Daroussin 
842d9f0ce31SBaptiste Daroussin 	if (*p == '/') {
843d9f0ce31SBaptiste Daroussin 		p++;
844d9f0ce31SBaptiste Daroussin 	}
845d9f0ce31SBaptiste Daroussin 	else if (*p == '\0') {
846d9f0ce31SBaptiste Daroussin 		return res;
847d9f0ce31SBaptiste Daroussin 	}
84897bd480fSBaptiste Daroussin 
84997bd480fSBaptiste Daroussin 	c = p;
85097bd480fSBaptiste Daroussin 
85197bd480fSBaptiste Daroussin 	while (*p != '\0') {
85297bd480fSBaptiste Daroussin 		if (*p == '/') {
85397bd480fSBaptiste Daroussin 			if (p - c == 0) {
85497bd480fSBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
85597bd480fSBaptiste Daroussin 						"reference %s is invalid, empty path component", ref);
85697bd480fSBaptiste Daroussin 				return NULL;
85797bd480fSBaptiste Daroussin 			}
85897bd480fSBaptiste Daroussin 			/* Now we have some url part, so we need to figure out where we are */
85997bd480fSBaptiste Daroussin 			res = ucl_schema_resolve_ref_component (res, c, p - c, err);
86097bd480fSBaptiste Daroussin 			if (res == NULL) {
86197bd480fSBaptiste Daroussin 				return NULL;
86297bd480fSBaptiste Daroussin 			}
86397bd480fSBaptiste Daroussin 			c = p + 1;
86497bd480fSBaptiste Daroussin 		}
86597bd480fSBaptiste Daroussin 		p ++;
86697bd480fSBaptiste Daroussin 	}
86797bd480fSBaptiste Daroussin 
86897bd480fSBaptiste Daroussin 	if (p - c != 0) {
86997bd480fSBaptiste Daroussin 		res = ucl_schema_resolve_ref_component (res, c, p - c, err);
87097bd480fSBaptiste Daroussin 	}
87197bd480fSBaptiste Daroussin 
87297bd480fSBaptiste Daroussin 	if (res == NULL || res->type != UCL_OBJECT) {
87397bd480fSBaptiste Daroussin 		ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, res,
87497bd480fSBaptiste Daroussin 				"reference %s is invalid, cannot find specified object",
87597bd480fSBaptiste Daroussin 				ref);
87697bd480fSBaptiste Daroussin 		return NULL;
87797bd480fSBaptiste Daroussin 	}
87897bd480fSBaptiste Daroussin 
87997bd480fSBaptiste Daroussin 	return res;
88097bd480fSBaptiste Daroussin }
88197bd480fSBaptiste Daroussin 
88297bd480fSBaptiste Daroussin static bool
ucl_schema_validate_values(const ucl_object_t * schema,const ucl_object_t * obj,struct ucl_schema_error * err)883b04a7a0bSBaptiste Daroussin ucl_schema_validate_values (const ucl_object_t *schema, const ucl_object_t *obj,
88497bd480fSBaptiste Daroussin 		struct ucl_schema_error *err)
88597bd480fSBaptiste Daroussin {
886b04a7a0bSBaptiste Daroussin 	const ucl_object_t *elt, *cur;
88797bd480fSBaptiste Daroussin 	int64_t constraint, i;
88897bd480fSBaptiste Daroussin 
889d9f0ce31SBaptiste Daroussin 	elt = ucl_object_lookup (schema, "maxValues");
89097bd480fSBaptiste Daroussin 	if (elt != NULL && elt->type == UCL_INT) {
89197bd480fSBaptiste Daroussin 		constraint = ucl_object_toint (elt);
89297bd480fSBaptiste Daroussin 		cur = obj;
89397bd480fSBaptiste Daroussin 		i = 0;
89497bd480fSBaptiste Daroussin 		while (cur) {
89597bd480fSBaptiste Daroussin 			if (i > constraint) {
89697bd480fSBaptiste Daroussin 				ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
89797bd480fSBaptiste Daroussin 					"object has more values than defined: %ld",
89897bd480fSBaptiste Daroussin 					(long int)constraint);
89997bd480fSBaptiste Daroussin 				return false;
90097bd480fSBaptiste Daroussin 			}
90197bd480fSBaptiste Daroussin 			i ++;
90297bd480fSBaptiste Daroussin 			cur = cur->next;
90397bd480fSBaptiste Daroussin 		}
90497bd480fSBaptiste Daroussin 	}
905d9f0ce31SBaptiste Daroussin 	elt = ucl_object_lookup (schema, "minValues");
90697bd480fSBaptiste Daroussin 	if (elt != NULL && elt->type == UCL_INT) {
90797bd480fSBaptiste Daroussin 		constraint = ucl_object_toint (elt);
90897bd480fSBaptiste Daroussin 		cur = obj;
90997bd480fSBaptiste Daroussin 		i = 0;
91097bd480fSBaptiste Daroussin 		while (cur) {
91197bd480fSBaptiste Daroussin 			if (i >= constraint) {
91297bd480fSBaptiste Daroussin 				break;
91397bd480fSBaptiste Daroussin 			}
91497bd480fSBaptiste Daroussin 			i ++;
91597bd480fSBaptiste Daroussin 			cur = cur->next;
91697bd480fSBaptiste Daroussin 		}
91797bd480fSBaptiste Daroussin 		if (i < constraint) {
91897bd480fSBaptiste Daroussin 			ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
91997bd480fSBaptiste Daroussin 					"object has less values than defined: %ld",
92097bd480fSBaptiste Daroussin 					(long int)constraint);
92197bd480fSBaptiste Daroussin 			return false;
92297bd480fSBaptiste Daroussin 		}
92397bd480fSBaptiste Daroussin 	}
92497bd480fSBaptiste Daroussin 
92597bd480fSBaptiste Daroussin 	return true;
92697bd480fSBaptiste Daroussin }
92797bd480fSBaptiste Daroussin 
92897bd480fSBaptiste Daroussin static bool
ucl_schema_validate(const ucl_object_t * schema,const ucl_object_t * obj,bool try_array,struct ucl_schema_error * err,const ucl_object_t * root,ucl_object_t * external_refs)929b04a7a0bSBaptiste Daroussin ucl_schema_validate (const ucl_object_t *schema,
930b04a7a0bSBaptiste Daroussin 		const ucl_object_t *obj, bool try_array,
93197bd480fSBaptiste Daroussin 		struct ucl_schema_error *err,
932d9f0ce31SBaptiste Daroussin 		const ucl_object_t *root,
933d9f0ce31SBaptiste Daroussin 		ucl_object_t *external_refs)
93497bd480fSBaptiste Daroussin {
935d9f0ce31SBaptiste Daroussin 	const ucl_object_t *elt, *cur, *ref_root;
93697bd480fSBaptiste Daroussin 	ucl_object_iter_t iter = NULL;
93797bd480fSBaptiste Daroussin 	bool ret;
93897bd480fSBaptiste Daroussin 
93997bd480fSBaptiste Daroussin 	if (schema->type != UCL_OBJECT) {
94097bd480fSBaptiste Daroussin 		ucl_schema_create_error (err, UCL_SCHEMA_INVALID_SCHEMA, schema,
941d9f0ce31SBaptiste Daroussin 				"schema is %s instead of object",
942d9f0ce31SBaptiste Daroussin 				ucl_object_type_to_string (schema->type));
94397bd480fSBaptiste Daroussin 		return false;
94497bd480fSBaptiste Daroussin 	}
94597bd480fSBaptiste Daroussin 
94697bd480fSBaptiste Daroussin 	if (try_array) {
94797bd480fSBaptiste Daroussin 		/*
94897bd480fSBaptiste Daroussin 		 * Special case for multiple values
94997bd480fSBaptiste Daroussin 		 */
95097bd480fSBaptiste Daroussin 		if (!ucl_schema_validate_values (schema, obj, err)) {
95197bd480fSBaptiste Daroussin 			return false;
95297bd480fSBaptiste Daroussin 		}
95397bd480fSBaptiste Daroussin 		LL_FOREACH (obj, cur) {
954d9f0ce31SBaptiste Daroussin 			if (!ucl_schema_validate (schema, cur, false, err, root, external_refs)) {
95597bd480fSBaptiste Daroussin 				return false;
95697bd480fSBaptiste Daroussin 			}
95797bd480fSBaptiste Daroussin 		}
95897bd480fSBaptiste Daroussin 		return true;
95997bd480fSBaptiste Daroussin 	}
96097bd480fSBaptiste Daroussin 
961d9f0ce31SBaptiste Daroussin 	elt = ucl_object_lookup (schema, "enum");
96297bd480fSBaptiste Daroussin 	if (elt != NULL && elt->type == UCL_ARRAY) {
96397bd480fSBaptiste Daroussin 		if (!ucl_schema_validate_enum (elt, obj, err)) {
96497bd480fSBaptiste Daroussin 			return false;
96597bd480fSBaptiste Daroussin 		}
96697bd480fSBaptiste Daroussin 	}
96797bd480fSBaptiste Daroussin 
968d9f0ce31SBaptiste Daroussin 	elt = ucl_object_lookup (schema, "allOf");
96997bd480fSBaptiste Daroussin 	if (elt != NULL && elt->type == UCL_ARRAY) {
97097bd480fSBaptiste Daroussin 		iter = NULL;
971d9f0ce31SBaptiste Daroussin 		while ((cur = ucl_object_iterate (elt, &iter, true)) != NULL) {
972d9f0ce31SBaptiste Daroussin 			ret = ucl_schema_validate (cur, obj, true, err, root, external_refs);
97397bd480fSBaptiste Daroussin 			if (!ret) {
97497bd480fSBaptiste Daroussin 				return false;
97597bd480fSBaptiste Daroussin 			}
97697bd480fSBaptiste Daroussin 		}
97797bd480fSBaptiste Daroussin 	}
97897bd480fSBaptiste Daroussin 
979d9f0ce31SBaptiste Daroussin 	elt = ucl_object_lookup (schema, "anyOf");
98097bd480fSBaptiste Daroussin 	if (elt != NULL && elt->type == UCL_ARRAY) {
98197bd480fSBaptiste Daroussin 		iter = NULL;
982d9f0ce31SBaptiste Daroussin 		while ((cur = ucl_object_iterate (elt, &iter, true)) != NULL) {
983d9f0ce31SBaptiste Daroussin 			ret = ucl_schema_validate (cur, obj, true, err, root, external_refs);
98497bd480fSBaptiste Daroussin 			if (ret) {
98597bd480fSBaptiste Daroussin 				break;
98697bd480fSBaptiste Daroussin 			}
98797bd480fSBaptiste Daroussin 		}
98897bd480fSBaptiste Daroussin 		if (!ret) {
98997bd480fSBaptiste Daroussin 			return false;
99097bd480fSBaptiste Daroussin 		}
99197bd480fSBaptiste Daroussin 		else {
99297bd480fSBaptiste Daroussin 			/* Reset error */
99397bd480fSBaptiste Daroussin 			err->code = UCL_SCHEMA_OK;
99497bd480fSBaptiste Daroussin 		}
99597bd480fSBaptiste Daroussin 	}
99697bd480fSBaptiste Daroussin 
997d9f0ce31SBaptiste Daroussin 	elt = ucl_object_lookup (schema, "oneOf");
99897bd480fSBaptiste Daroussin 	if (elt != NULL && elt->type == UCL_ARRAY) {
99997bd480fSBaptiste Daroussin 		iter = NULL;
100097bd480fSBaptiste Daroussin 		ret = false;
1001d9f0ce31SBaptiste Daroussin 		while ((cur = ucl_object_iterate (elt, &iter, true)) != NULL) {
100297bd480fSBaptiste Daroussin 			if (!ret) {
1003d9f0ce31SBaptiste Daroussin 				ret = ucl_schema_validate (cur, obj, true, err, root, external_refs);
100497bd480fSBaptiste Daroussin 			}
1005d9f0ce31SBaptiste Daroussin 			else if (ucl_schema_validate (cur, obj, true, err, root, external_refs)) {
100697bd480fSBaptiste Daroussin 				ret = false;
100797bd480fSBaptiste Daroussin 				break;
100897bd480fSBaptiste Daroussin 			}
100997bd480fSBaptiste Daroussin 		}
101097bd480fSBaptiste Daroussin 		if (!ret) {
101197bd480fSBaptiste Daroussin 			return false;
101297bd480fSBaptiste Daroussin 		}
101397bd480fSBaptiste Daroussin 	}
101497bd480fSBaptiste Daroussin 
1015d9f0ce31SBaptiste Daroussin 	elt = ucl_object_lookup (schema, "not");
101697bd480fSBaptiste Daroussin 	if (elt != NULL && elt->type == UCL_OBJECT) {
1017d9f0ce31SBaptiste Daroussin 		if (ucl_schema_validate (elt, obj, true, err, root, external_refs)) {
101897bd480fSBaptiste Daroussin 			return false;
101997bd480fSBaptiste Daroussin 		}
102097bd480fSBaptiste Daroussin 		else {
102197bd480fSBaptiste Daroussin 			/* Reset error */
102297bd480fSBaptiste Daroussin 			err->code = UCL_SCHEMA_OK;
102397bd480fSBaptiste Daroussin 		}
102497bd480fSBaptiste Daroussin 	}
102597bd480fSBaptiste Daroussin 
1026d9f0ce31SBaptiste Daroussin 	elt = ucl_object_lookup (schema, "$ref");
102797bd480fSBaptiste Daroussin 	if (elt != NULL) {
1028d9f0ce31SBaptiste Daroussin 		ref_root = root;
1029d9f0ce31SBaptiste Daroussin 		cur = ucl_schema_resolve_ref (root, ucl_object_tostring (elt),
1030d9f0ce31SBaptiste Daroussin 				err, external_refs, &ref_root);
1031d9f0ce31SBaptiste Daroussin 
103297bd480fSBaptiste Daroussin 		if (cur == NULL) {
103397bd480fSBaptiste Daroussin 			return false;
103497bd480fSBaptiste Daroussin 		}
1035d9f0ce31SBaptiste Daroussin 		if (!ucl_schema_validate (cur, obj, try_array, err, ref_root,
1036d9f0ce31SBaptiste Daroussin 				external_refs)) {
103797bd480fSBaptiste Daroussin 			return false;
103897bd480fSBaptiste Daroussin 		}
103997bd480fSBaptiste Daroussin 	}
104097bd480fSBaptiste Daroussin 
1041d9f0ce31SBaptiste Daroussin 	elt = ucl_object_lookup (schema, "type");
104297bd480fSBaptiste Daroussin 	if (!ucl_schema_type_is_allowed (elt, obj, err)) {
104397bd480fSBaptiste Daroussin 		return false;
104497bd480fSBaptiste Daroussin 	}
104597bd480fSBaptiste Daroussin 
104697bd480fSBaptiste Daroussin 	switch (obj->type) {
104797bd480fSBaptiste Daroussin 	case UCL_OBJECT:
1048d9f0ce31SBaptiste Daroussin 		return ucl_schema_validate_object (schema, obj, err, root, external_refs);
104997bd480fSBaptiste Daroussin 		break;
105097bd480fSBaptiste Daroussin 	case UCL_ARRAY:
1051d9f0ce31SBaptiste Daroussin 		return ucl_schema_validate_array (schema, obj, err, root, external_refs);
105297bd480fSBaptiste Daroussin 		break;
105397bd480fSBaptiste Daroussin 	case UCL_INT:
105497bd480fSBaptiste Daroussin 	case UCL_FLOAT:
105597bd480fSBaptiste Daroussin 		return ucl_schema_validate_number (schema, obj, err);
105697bd480fSBaptiste Daroussin 		break;
105797bd480fSBaptiste Daroussin 	case UCL_STRING:
105897bd480fSBaptiste Daroussin 		return ucl_schema_validate_string (schema, obj, err);
105997bd480fSBaptiste Daroussin 		break;
106097bd480fSBaptiste Daroussin 	default:
106197bd480fSBaptiste Daroussin 		break;
106297bd480fSBaptiste Daroussin 	}
106397bd480fSBaptiste Daroussin 
106497bd480fSBaptiste Daroussin 	return true;
106597bd480fSBaptiste Daroussin }
106697bd480fSBaptiste Daroussin 
106797bd480fSBaptiste Daroussin bool
ucl_object_validate(const ucl_object_t * schema,const ucl_object_t * obj,struct ucl_schema_error * err)1068b04a7a0bSBaptiste Daroussin ucl_object_validate (const ucl_object_t *schema,
1069b04a7a0bSBaptiste Daroussin 		const ucl_object_t *obj, struct ucl_schema_error *err)
107097bd480fSBaptiste Daroussin {
1071d9f0ce31SBaptiste Daroussin 	return ucl_object_validate_root_ext (schema, obj, schema, NULL, err);
1072d9f0ce31SBaptiste Daroussin }
1073d9f0ce31SBaptiste Daroussin 
1074d9f0ce31SBaptiste Daroussin bool
ucl_object_validate_root(const ucl_object_t * schema,const ucl_object_t * obj,const ucl_object_t * root,struct ucl_schema_error * err)1075d9f0ce31SBaptiste Daroussin ucl_object_validate_root (const ucl_object_t *schema,
1076d9f0ce31SBaptiste Daroussin 		const ucl_object_t *obj,
1077d9f0ce31SBaptiste Daroussin 		const ucl_object_t *root,
1078d9f0ce31SBaptiste Daroussin 		struct ucl_schema_error *err)
1079d9f0ce31SBaptiste Daroussin {
1080d9f0ce31SBaptiste Daroussin 	return ucl_object_validate_root_ext (schema, obj, root, NULL, err);
1081d9f0ce31SBaptiste Daroussin }
1082d9f0ce31SBaptiste Daroussin 
1083d9f0ce31SBaptiste Daroussin bool
ucl_object_validate_root_ext(const ucl_object_t * schema,const ucl_object_t * obj,const ucl_object_t * root,ucl_object_t * ext_refs,struct ucl_schema_error * err)1084d9f0ce31SBaptiste Daroussin ucl_object_validate_root_ext (const ucl_object_t *schema,
1085d9f0ce31SBaptiste Daroussin 		const ucl_object_t *obj,
1086d9f0ce31SBaptiste Daroussin 		const ucl_object_t *root,
1087d9f0ce31SBaptiste Daroussin 		ucl_object_t *ext_refs,
1088d9f0ce31SBaptiste Daroussin 		struct ucl_schema_error *err)
1089d9f0ce31SBaptiste Daroussin {
1090d9f0ce31SBaptiste Daroussin 	bool ret, need_unref = false;
1091d9f0ce31SBaptiste Daroussin 
1092d9f0ce31SBaptiste Daroussin 	if (ext_refs == NULL) {
1093d9f0ce31SBaptiste Daroussin 		ext_refs = ucl_object_typed_new (UCL_OBJECT);
1094d9f0ce31SBaptiste Daroussin 		need_unref = true;
1095d9f0ce31SBaptiste Daroussin 	}
1096d9f0ce31SBaptiste Daroussin 
1097d9f0ce31SBaptiste Daroussin 	ret = ucl_schema_validate (schema, obj, true, err, root, ext_refs);
1098d9f0ce31SBaptiste Daroussin 
1099d9f0ce31SBaptiste Daroussin 	if (need_unref) {
1100d9f0ce31SBaptiste Daroussin 		ucl_object_unref (ext_refs);
1101d9f0ce31SBaptiste Daroussin 	}
1102d9f0ce31SBaptiste Daroussin 
1103d9f0ce31SBaptiste Daroussin 	return ret;
110497bd480fSBaptiste Daroussin }
1105