LCOV - code coverage report
Current view: top level - external/libucl/src - ucl_schema.c (source / functions) Hit Total Coverage
Test: rapport Lines: 185 629 29.4 %
Date: 2021-12-10 16:22:55 Functions: 15 23 65.2 %
Branches: 127 471 27.0 %

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

Generated by: LCOV version 1.15