diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4ba02309..723c870d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,18 @@
## 3.10.0/4.0.0 (Upcoming)
### Major New features
+* Regexp improvements: Libxml2 XSD, multiple patterns, optimization
+ * Support for multiple patterns as described in RFC7950 Section 9.4.7
+ * Libxml2 support for full XSD matching as alternative to Posix translation
+ * Configure with: `./configure --with-libxml2`
+ * Set `CLICON_YANG_REGEXP` to libxml2 (default is posix)
+ * Note you need to configure cligen as well with `--with-libxml2`
+ * Better compliance with XSD regexps in the default Posix translation regex mode
+ * Added `\p{L}` and `\p{N}`
+ * Added escaping of `$`
+ * Added clixon_util_regexp utility function
+ * Added extensive regexp test [test/test_pattern.sh] for both posix and libxml2
+ * Added regex cache to type resolution
* Yang "min-element" and "max-element" feature supported
* According to RFC 7950 7.7.4 and 7.7.5
* See (tests)[test/test_minmax.sh]
@@ -54,6 +66,9 @@
### API changes on existing features (you may need to change your code)
+* Added compiled regexp parameter as part of internal yang type resolution functions
+ * `yang_type_resolve()`, `yang_type_get()`
+* All internal `ys_populate_*()` functions (except ys_populate()) have switched parameters: `clicon_handle, yang_stmt *)`
* Added clicon_handle as parameter to all validate functions
* Just add `clixon_handle h` to all calls.
* Clixon transaction mechanism has changed which may affect your backend plugin callbacks:
@@ -141,14 +156,6 @@
### Minor changes
-* Regexp improvements: Added libxml2 XSD regexp mode as alternative to posix translation
- * Configure with: `./configure --with-libxml2`
- * Set `CLICON_YANG_REGEXP` to libxml2 (default is posix)
- * Better compliance with XSD regexps (when transforming to Posix regexps)
- * Added `\p{L}` and `\p{N}`
- * Added escaping of `$`
- * Added clixon_util_regexp utility function
- * Added regexp test [test/test_pattern.sh] for both posix and libxml2
* Yang state get improvements
* Integrated state and config into same tree on retrieval, not separate trees
* Added cli functions `cli_show_config_state()` and `cli_show_auto_state()` for showing combined config and state info.
diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c
index 723f4b0e..e9a827d4 100644
--- a/apps/backend/backend_client.c
+++ b/apps/backend/backend_client.c
@@ -477,7 +477,7 @@ from_client_edit_config(clicon_handle h,
/* Cant do this earlier since we dont have a yang spec to
* the upper part of the tree, until we get the "config" tree.
*/
- if (xml_apply0(xc, CX_ELMNT, xml_sort, NULL) < 0)
+ if (xml_apply0(xc, CX_ELMNT, xml_sort, h) < 0)
goto done;
if ((ret = xmldb_put(h, target, operation, xc, username, cbret)) < 0){
clicon_debug(1, "%s ERROR PUT", __FUNCTION__);
diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c
index eb31cc6b..b869912d 100644
--- a/apps/backend/backend_commit.c
+++ b/apps/backend/backend_commit.c
@@ -206,7 +206,7 @@ startup_common(clicon_handle h,
/* After upgrading, XML tree needs to be sorted and yang spec populated */
if (xml_apply0(xt, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
- if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0)
+ if (xml_apply0(xt, CX_ELMNT, xml_sort, h) < 0)
goto done;
/* Handcraft transition with with only add tree */
td->td_target = xt;
diff --git a/apps/cli/cli_generate.c b/apps/cli/cli_generate.c
index 914f785f..a78d55f1 100644
--- a/apps/cli/cli_generate.c
+++ b/apps/cli/cli_generate.c
@@ -272,6 +272,42 @@ yang2cli_var_range(yang_stmt *ys,
return retval;
}
+/*! Generate CLI code for Yang variable pattern statement
+ * @param[in] h Clixon handle
+ * @param[in] patterns Cvec of regexp patterns
+ * @param[out] cb Buffer where cligen code is written
+ * @see cv_validate_pattern for netconf validate code
+ */
+static int
+yang2cli_var_pattern(clicon_handle h,
+ cvec *patterns,
+ cbuf *cb)
+{
+ int retval = -1;
+ char *mode;
+ cg_var *cvp;
+ char *pattern;
+
+ mode = clicon_yang_regexp(h);
+ cvp = NULL; /* Loop over compiled regexps */
+ while ((cvp = cvec_each(patterns, cvp)) != NULL){
+ pattern = cv_string_get(cvp);
+ if (strcmp(mode, "posix") == 0){
+ char *posix = NULL;
+ if (regexp_xsd2posix(pattern, &posix) < 0)
+ goto done;
+ cprintf(cb, " regexp:\"%s\"", posix);
+ if (posix)
+ free(posix);
+ }
+ else
+ cprintf(cb, " regexp:\"%s\"", pattern);
+ }
+ retval = 0;
+ done:
+ return retval;
+}
+
/* Forward */
static int yang2cli_stmt(clicon_handle h, yang_stmt *ys,
enum genmodel_type gt, int level, cbuf *cb);
@@ -289,7 +325,7 @@ static int yang2cli_var_union(clicon_handle h, yang_stmt *ys, char *origtype,
* @param[in] cvtype
* @param[in] options Flags field of optional values, see YANG_OPTIONS_*
* @param[in] cvv Cvec with array of range_min/range_max cv:s
- * @param[in] pattern String of POSIX regexp pattern
+ * @param[in] patterns Cvec of regexp patterns
* @param[in] fraction for decimal64, how many digits after period
* @param[out] cb Buffer where cligen code is written
* @see yang_type_resolve for options and other arguments
@@ -302,7 +338,7 @@ yang2cli_var_sub(clicon_handle h,
enum cv_type cvtype,
int options,
cvec *cvv,
- char *pattern,
+ cvec *patterns,
uint8_t fraction_digits,
cbuf *cb
)
@@ -358,19 +394,9 @@ yang2cli_var_sub(clicon_handle h,
if (yang2cli_var_range(ys, options, cvv, cb) < 0)
goto done;
}
- if (options & YANG_OPTIONS_PATTERN){
- char *mode;
- mode = clicon_yang_regexp(h);
- if (strcmp(mode, "posix") == 0){
- char *posix = NULL;
- if (regexp_xsd2posix(pattern, &posix) < 0)
- goto done;
- cprintf(cb, " regexp:\"%s\"", posix);
- if (posix)
- free(posix);
- }
- else
- cprintf(cb, " regexp:\"%s\"", pattern);
+ if (patterns && cvec_len(patterns)){
+ if (yang2cli_var_pattern(h, patterns, cb) < 0)
+ goto done;
}
cprintf(cb, ">");
if (helptext)
@@ -402,16 +428,20 @@ yang2cli_var_union_one(clicon_handle h,
int retval = -1;
int options = 0;
cvec *cvv = NULL;
- char *pattern = NULL;
+ cvec *patterns = NULL;
uint8_t fraction_digits = 0;
enum cv_type cvtype;
yang_stmt *ytype; /* resolved type */
char *restype;
+ if ((patterns = cvec_new(0)) == NULL){
+ clicon_err(OE_UNIX, errno, "cvec_new");
+ goto done;
+ }
/* Resolve the sub-union type to a resolved type */
if (yang_type_resolve(ys, ys, ytsub, /* in */
&ytype, &options, /* resolved type */
- &cvv, &pattern, &fraction_digits) < 0)
+ &cvv, patterns, NULL, &fraction_digits) < 0)
goto done;
restype = ytype?yang_argument_get(ytype):NULL;
@@ -423,11 +453,13 @@ yang2cli_var_union_one(clicon_handle h,
if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0)
goto done;
if ((retval = yang2cli_var_sub(h, ys, ytype, helptext, cvtype,
- options, cvv, pattern, fraction_digits, cb)) < 0)
+ options, cvv, patterns, fraction_digits, cb)) < 0)
goto done;
}
retval = 0;
done:
+ if (patterns)
+ cvec_free(patterns);
return retval;
}
@@ -494,15 +526,19 @@ yang2cli_var(clicon_handle h,
yang_stmt *yrestype; /* resolved type */
char *restype; /* resolved type */
cvec *cvv = NULL;
- char *pattern = NULL;
+ cvec *patterns = NULL;
uint8_t fraction_digits = 0;
enum cv_type cvtype;
int options = 0;
int completionp;
char *type;
+ if ((patterns = cvec_new(0)) == NULL){
+ clicon_err(OE_UNIX, errno, "cvec_new");
+ goto done;
+ }
if (yang_type_get(ys, &origtype, &yrestype,
- &options, &cvv, &pattern, &fraction_digits) < 0)
+ &options, &cvv, patterns, NULL, &fraction_digits) < 0)
goto done;
restype = yrestype?yang_argument_get(yrestype):NULL;
@@ -539,7 +575,7 @@ yang2cli_var(clicon_handle h,
if (completionp)
cprintf(cb, "(");
if ((retval = yang2cli_var_sub(h, ys, yrestype, helptext, cvtype,
- options, cvv, pattern, fraction_digits, cb)) < 0)
+ options, cvv, patterns, fraction_digits, cb)) < 0)
goto done;
if (completionp){
if (cli_expand_var_generate(h, ys, cvtype,
@@ -552,6 +588,8 @@ yang2cli_var(clicon_handle h,
}
retval = 0;
done:
+ if (patterns)
+ cvec_free(patterns);
return retval;
}
diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h
index 32fc07bf..c5347d89 100644
--- a/lib/clixon/clixon_yang.h
+++ b/lib/clixon/clixon_yang.h
@@ -153,8 +153,6 @@ char *yang_argument_get(yang_stmt *ys);
cg_var *yang_cv_get(yang_stmt *ys);
cvec *yang_cvec_get(yang_stmt *ys);
int yang_cvec_set(yang_stmt *ys, cvec *cvv);
-void *yang_regex_cache_get(yang_stmt *ys);
-int yang_regex_cache_set(yang_stmt *ys, void *regex);
/* Other functions */
yang_stmt *yspec_new(void);
diff --git a/lib/clixon/clixon_yang_type.h b/lib/clixon/clixon_yang_type.h
index 34c3b663..8aa6a4fa 100644
--- a/lib/clixon/clixon_yang_type.h
+++ b/lib/clixon/clixon_yang_type.h
@@ -43,8 +43,7 @@
*/
#define YANG_OPTIONS_LENGTH 0x01
#define YANG_OPTIONS_RANGE 0x02
-#define YANG_OPTIONS_PATTERN 0x04
-#define YANG_OPTIONS_FRACTION_DIGITS 0x08
+#define YANG_OPTIONS_FRACTION_DIGITS 0x04
/*
* Types
@@ -57,10 +56,11 @@ typedef struct yang_type_cache yang_type_cache;
*/
int yang_type_cache_set(yang_type_cache **ycache,
yang_stmt *resolved, int options,
- cvec *cvv, char *pattern, uint8_t fraction);
+ cvec *cvv, cvec *patterns, cvec *regexps,
+ uint8_t fraction);
int yang_type_cache_get(yang_type_cache *ycache, yang_stmt **resolved,
- int *options, cvec **cvv, char **pattern,
- uint8_t *fraction);
+ int *options, cvec **cvv, cvec *patterns,
+ cvec *regexps, uint8_t *fraction);
int yang_type_cache_cp(yang_type_cache **ycnew, yang_type_cache *ycold);
int yang_type_cache_free(yang_type_cache *ycache);
int ys_resolve_type(yang_stmt *ys, void *arg);
@@ -70,11 +70,14 @@ yang_stmt *yang_find_identity(yang_stmt *ys, char *identity);
int ys_cv_validate(clicon_handle h, cg_var *cv, yang_stmt *ys, char **reason);
int clicon_type2cv(char *type, char *rtype, yang_stmt *ys, enum cv_type *cvtype);
int yang_type_get(yang_stmt *ys, char **otype, yang_stmt **restype,
- int *options, cvec **cvv, char **pattern,
+ int *options, cvec **cvv,
+ cvec *patterns, cvec *regexps,
uint8_t *fraction_digits);
-int yang_type_resolve(yang_stmt *yorig, yang_stmt *ys, yang_stmt *ytype,
+int yang_type_resolve(yang_stmt *yorig, yang_stmt *ys,
+ yang_stmt *ytype,
yang_stmt **restype, int *options,
- cvec **cvv, char **pattern, uint8_t *fraction);
+ cvec **cvv, cvec *patterns, cvec *regexps,
+ uint8_t *fraction);
#endif /* _CLIXON_YANG_TYPE_H_ */
diff --git a/lib/src/clixon_datastore_read.c b/lib/src/clixon_datastore_read.c
index 80bf71a6..0b24ef23 100644
--- a/lib/src/clixon_datastore_read.c
+++ b/lib/src/clixon_datastore_read.c
@@ -437,7 +437,7 @@ xmldb_get_nocache(clicon_handle h,
goto done;
/* Add default values (if not set) */
- if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
+ if (xml_apply(xt, CX_ELMNT, xml_default, h) < 0)
goto done;
#if 0 /* debug */
if (xml_apply0(xt, -1, xml_sort_verify, NULL) < 0)
@@ -542,7 +542,7 @@ xmldb_get_cache(clicon_handle h,
goto done;
/* x1t is wrong here should be .. but is .. */
/* XXX where should we apply default values once? */
- if (xml_apply(x1t, CX_ELMNT, xml_default, NULL) < 0)
+ if (xml_apply(x1t, CX_ELMNT, xml_default, h) < 0)
goto done;
/* Copy the matching parts of the (relevant) XML tree.
@@ -622,7 +622,7 @@ xmldb_get_zerocopy(clicon_handle h,
xml_apply_ancestor(x0, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
}
/* Apply default values (removed in clear function) */
- if (xml_apply(x0t, CX_ELMNT, xml_default, NULL) < 0)
+ if (xml_apply(x0t, CX_ELMNT, xml_default, h) < 0)
goto done;
if (debug>1)
clicon_xml2file(stderr, x0t, 0, 1);
diff --git a/lib/src/clixon_datastore_write.c b/lib/src/clixon_datastore_write.c
index c8a01526..35b13bad 100644
--- a/lib/src/clixon_datastore_write.c
+++ b/lib/src/clixon_datastore_write.c
@@ -398,7 +398,7 @@ text_modify(clicon_handle h,
} /* else Y_CONTAINER */
#ifndef USE_XML_INSERT
if (changed)
- xml_sort(x0p, NULL);
+ xml_sort(x0p, h);
#endif
retval = 1;
done:
diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c
index 57b1b254..2aea61c5 100644
--- a/lib/src/clixon_options.c
+++ b/lib/src/clixon_options.c
@@ -181,7 +181,7 @@ parse_configfile(clicon_handle h,
goto done;
if (xml_apply0(xc, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
- if (xml_apply0(xc, CX_ELMNT, xml_sort, NULL) < 0)
+ if (xml_apply0(xc, CX_ELMNT, xml_sort, h) < 0)
goto done;
}
else
@@ -192,7 +192,7 @@ parse_configfile(clicon_handle h,
goto done;
}
}
- if (xml_apply0(xc, CX_ELMNT, xml_default, yspec) < 0)
+ if (xml_apply0(xc, CX_ELMNT, xml_default, h) < 0)
goto done;
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c
index ad8086d1..f7663ba2 100644
--- a/lib/src/clixon_xml.c
+++ b/lib/src/clixon_xml.c
@@ -2394,51 +2394,3 @@ clicon_log_xml(int level,
return retval;
}
-/*
- * Turn this on to get a xml parse and pretty print test program
- * Usage: xpath
- * read xml from input
- * Example compile:
- gcc -g -o xml -I. -I../clixon ./clixon_xml.c -lclixon -lcligen
- * Example run:
- echo "" | xml
-*/
-#if 1 /* Test program */
-
-static int
-usage(char *argv0)
-{
- fprintf(stderr, "usage:%s.\n\tInput on stdin\n", argv0);
- exit(0);
-}
-
-int
-main(int argc, char **argv)
-{
- cxobj *xt = NULL;
- cxobj *xc;
- cbuf *cb = cbuf_new();
-
- if (argc != 1){
- usage(argv[0]);
- return 0;
- }
- if (xml_parse_file(0, "", NULL, &xt) < 0){
- fprintf(stderr, "xml parse error %s\n", clicon_err_reason);
- return -1;
- }
- xc = NULL;
- while ((xc = xml_child_each(xt, xc, -1)) != NULL) {
- xmltree2cbuf(cb, xc, 0); /* dump data structures */
- //clicon_xml2cbuf(cb, xc, 0, 1); /* print xml */
- }
- fprintf(stdout, "%s", cbuf_get(cb));
- if (xt)
- xml_free(xt);
- if (cb)
- cbuf_free(cb);
- return 0;
-}
-
-#endif /* Test program */
-
diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c
index fbc1ec41..6cddca59 100644
--- a/lib/src/clixon_xml_map.c
+++ b/lib/src/clixon_xml_map.c
@@ -3004,7 +3004,7 @@ yang_enum_int_value(cxobj *node,
if ((ytype = yang_find(ys, Y_TYPE, NULL)) == NULL)
goto done;
if (yang_type_resolve(ys, ys, ytype, &yrestype,
- NULL, NULL, NULL, NULL) < 0)
+ NULL, NULL, NULL, NULL, NULL) < 0)
goto done;
if (yrestype==NULL || strcmp(yrestype->ys_argument, "enumeration"))
goto done;
diff --git a/lib/src/clixon_xml_sort.c b/lib/src/clixon_xml_sort.c
index e3cef110..35be1a14 100644
--- a/lib/src/clixon_xml_sort.c
+++ b/lib/src/clixon_xml_sort.c
@@ -95,7 +95,7 @@ xml_cv_cache(cxobj *x,
goto ok;
if ((y = xml_spec(x)) == NULL)
goto ok;
- if (yang_type_get(y, NULL, &yrestype, &options, NULL, NULL, &fraction) < 0)
+ if (yang_type_get(y, NULL, &yrestype, &options, NULL, NULL, NULL, &fraction) < 0)
goto done;
yang2cv_type(yang_argument_get(yrestype), &cvtype);
if (cvtype==CGV_ERR){
diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c
index 7a8013aa..144990da 100644
--- a/lib/src/clixon_yang.c
+++ b/lib/src/clixon_yang.c
@@ -239,30 +239,6 @@ yang_cvec_set(yang_stmt *ys,
return 0;
}
-/*! Get regular expression cache - the compiled regex
- * @param[in] ys Yang statement node
- * @retval re Compiled regex
- * @see regcomp
- */
-void*
-yang_regex_cache_get(yang_stmt *ys)
-{
- return ys->ys_regex_cache;
-}
-
-/*! Set regular expression cache - the compiled regex
- * @param[in] ys Yang statement node
- * @param[in] re Compiled regex
- * @see regcomp
- */
-int
-yang_regex_cache_set(yang_stmt *ys,
- void *regex)
-{
- ys->ys_regex_cache = regex;
- return 0;
-}
-
/* End access functions */
/*! Create new yang specification
@@ -307,17 +283,6 @@ ys_new(enum rfc_6020 keyw)
return ys;
}
-
-static int
-yang_regex_cache_free(yang_stmt *ys)
-{
- if (ys->ys_regex_cache){
- regfree(ys->ys_regex_cache);
- free(ys->ys_regex_cache);
- }
- return 0;
-}
-
/*! Free a single yang statement */
static int
ys_free1(yang_stmt *ys)
@@ -332,7 +297,6 @@ ys_free1(yang_stmt *ys)
cvec_free(ys->ys_cvec);
if (ys->ys_typecache)
yang_type_cache_free(ys->ys_typecache);
- yang_regex_cache_free(ys);
free(ys);
return 0;
}
@@ -1344,8 +1308,8 @@ yang_print_cbuf(cbuf *cb,
* @retval -1 Error with clicon_err called
*/
static int
-ys_populate_leaf(yang_stmt *ys,
- void *arg)
+ys_populate_leaf(clicon_handle h,
+ yang_stmt *ys)
{
int retval = -1;
cg_var *cv = NULL;
@@ -1363,7 +1327,7 @@ ys_populate_leaf(yang_stmt *ys,
yparent = ys->ys_parent; /* Find parent: list/container */
/* 1. Find type specification and set cv type accordingly */
- if (yang_type_get(ys, &type, &yrestype, &options, NULL, NULL, &fraction_digits)
+ if (yang_type_get(ys, &type, &yrestype, &options, NULL, NULL, NULL, &fraction_digits)
< 0)
goto done;
restype = yrestype?yrestype->ys_argument:NULL;
@@ -1416,8 +1380,8 @@ ys_populate_leaf(yang_stmt *ys,
}
static int
-ys_populate_list(yang_stmt *ys,
- void *arg)
+ys_populate_list(clicon_handle h,
+ yang_stmt *ys)
{
yang_stmt *ykey;
@@ -1523,8 +1487,8 @@ range_parse(yang_stmt *ys,
* ascending order
*/
static int
-ys_populate_range(yang_stmt *ys,
- void *arg)
+ys_populate_range(clicon_handle h,
+ yang_stmt *ys)
{
int retval = -1;
yang_stmt *yparent; /* type */
@@ -1541,7 +1505,7 @@ ys_populate_range(yang_stmt *ys,
goto done;
}
if (yang_type_resolve(ys, ys, (yang_stmt*)yparent, &yrestype,
- &options, NULL, NULL, &fraction_digits) < 0)
+ &options, NULL, NULL, NULL, &fraction_digits) < 0)
goto done;
restype = yrestype?yrestype->ys_argument:NULL;
origtype = yarg_id((yang_stmt*)yparent);
@@ -1572,8 +1536,8 @@ ys_populate_range(yang_stmt *ys,
* be disjoint and MUST be in ascending order.
*/
static int
-ys_populate_length(yang_stmt *ys,
- void *arg)
+ys_populate_length(clicon_handle h,
+ yang_stmt *ys)
{
int retval = -1;
yang_stmt *yparent; /* type */
@@ -1598,8 +1562,9 @@ ys_populate_length(yang_stmt *ys,
* @
*/
static int
-ys_populate_type(yang_stmt *ys,
- void *arg)
+ys_populate_type(clicon_handle h,
+ yang_stmt *ys)
+
{
int retval = -1;
yang_stmt *ybase;
@@ -1636,8 +1601,9 @@ ys_populate_type(yang_stmt *ys,
* @see validate_identityref which in runtime validates actual values
*/
static int
-ys_populate_identity(yang_stmt *ys,
- char *idref)
+ys_populate_identity(clicon_handle h,
+ yang_stmt *ys,
+ char *idref)
{
int retval = -1;
yang_stmt *yc = NULL;
@@ -1695,7 +1661,7 @@ ys_populate_identity(yang_stmt *ys,
cv = NULL;
}
/* Transitive to the root */
- if (ys_populate_identity(ybaseid, idref) < 0)
+ if (ys_populate_identity(h, ybaseid, idref) < 0)
goto done;
}
retval = 0;
@@ -1800,7 +1766,8 @@ ys_populate_feature(clicon_handle h,
/*! Populate the unique statement with a cvec
*/
static int
-ys_populate_unique(yang_stmt *ys)
+ys_populate_unique(clicon_handle h,
+ yang_stmt *ys)
{
if (ys->ys_cvec)
cvec_free(ys->ys_cvec);
@@ -1812,7 +1779,8 @@ ys_populate_unique(yang_stmt *ys)
/*! Populate unknown node with extension
*/
static int
-ys_populate_unknown(yang_stmt *ys)
+ys_populate_unknown(clicon_handle h,
+ yang_stmt *ys)
{
int retval = -1;
int cvret;
@@ -1861,28 +1829,28 @@ ys_populate_unknown(yang_stmt *ys)
* After this pass, cv:s are set for LEAFs and LEAF-LISTs
*/
int
-ys_populate(yang_stmt *ys,
- void *arg)
+ys_populate(yang_stmt *ys,
+ void *arg)
{
int retval = -1;
- // clicon_handle h = (clicon_handle)arg;
+ clicon_handle h = (clicon_handle)arg;
switch(ys->ys_keyword){
case Y_LEAF:
case Y_LEAF_LIST:
- if (ys_populate_leaf(ys, NULL) < 0)
+ if (ys_populate_leaf(h, ys) < 0)
goto done;
break;
case Y_LIST:
- if (ys_populate_list(ys, NULL) < 0)
+ if (ys_populate_list(h, ys) < 0)
goto done;
break;
case Y_RANGE:
- if (ys_populate_range(ys, NULL) < 0)
+ if (ys_populate_range(h, ys) < 0)
goto done;
break;
case Y_LENGTH:
- if (ys_populate_length(ys, NULL) < 0)
+ if (ys_populate_length(h, ys) < 0)
goto done;
break;
case Y_MANDATORY: /* call yang_mandatory() to check if set */
@@ -1891,19 +1859,19 @@ ys_populate(yang_stmt *ys,
goto done;
break;
case Y_TYPE:
- if (ys_populate_type(ys, NULL) < 0)
+ if (ys_populate_type(h, ys) < 0)
goto done;
break;
case Y_IDENTITY:
- if (ys_populate_identity(ys, NULL) < 0)
+ if (ys_populate_identity(h, ys, NULL) < 0)
goto done;
break;
case Y_UNIQUE:
- if (ys_populate_unique(ys) < 0)
+ if (ys_populate_unique(h, ys) < 0)
goto done;
break;
case Y_UNKNOWN:
- if (ys_populate_unknown(ys) < 0)
+ if (ys_populate_unknown(h, ys) < 0)
goto done;
break;
default:
@@ -2631,7 +2599,7 @@ yang_parse_post(clicon_handle h,
* Must be done using static binding.
*/
for (i=modnr; iys_len; i++)
- if (yang_apply(yspec->ys_stmt[i], Y_TYPE, ys_resolve_type, NULL) < 0)
+ if (yang_apply(yspec->ys_stmt[i], Y_TYPE, ys_resolve_type, h) < 0)
goto done;
/* Up to here resolving is made in the context they are defined, rather
diff --git a/lib/src/clixon_yang_internal.h b/lib/src/clixon_yang_internal.h
index 4fad6644..f36692ff 100644
--- a/lib/src/clixon_yang_internal.h
+++ b/lib/src/clixon_yang_internal.h
@@ -58,7 +58,8 @@ struct yang_type_cache{
cvec *yc_cvv; /* Range and length restriction. (if YANG_OPTION_
LENGTH|RANGE. Can be a vector if multiple
ranges*/
- char *yc_pattern; /* regex (posix) (if YANG_OPTIONS_PATTERN) */
+ cvec *yc_patterns; /* list of regexp, if cvec_len() > 0 */
+ cvec *yc_regexps; /* list of _compiled_ regexp, if cvec_len() > 0 */
uint8_t yc_fraction; /* Fraction digits for decimal64 (if
YANG_OPTIONS_FRACTION_DIGITS */
yang_stmt *yc_resolved; /* Resolved type object, can be NULL - note direct ptr */
@@ -95,7 +96,6 @@ struct yang_stmt{
Y_TYPE & identity: store all derived types
*/
yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data except unions */
- void *ys_regex_cache; /* regex cache */
int _ys_vector_i; /* internal use: yn_each */
};
diff --git a/lib/src/clixon_yang_parse.l b/lib/src/clixon_yang_parse.l
index 1c3e05d5..5afcbeda 100644
--- a/lib/src/clixon_yang_parse.l
+++ b/lib/src/clixon_yang_parse.l
@@ -170,6 +170,7 @@ identifier [A-Za-z_][A-Za-z0-9_\-\.]*
key { BEGIN(STRING); return K_KEY; }
length { BEGIN(STRING); return K_LENGTH; }
max-elements { BEGIN(STRING); return K_MAX_ELEMENTS; }
+modifier { BEGIN(STRING); return K_MODIFIER; }
must { BEGIN(STRING); return K_MUST; }
namespace { BEGIN(STRING); return K_NAMESPACE; }
ordered-by { BEGIN(STRING); return K_ORDERED_BY; }
diff --git a/lib/src/clixon_yang_parse.y b/lib/src/clixon_yang_parse.y
index a858316b..050a2d5e 100644
--- a/lib/src/clixon_yang_parse.y
+++ b/lib/src/clixon_yang_parse.y
@@ -863,7 +863,7 @@ pattern_substmt : modifier_stmt { clicon_debug(2,"pattern-substmt -> modifier
;
modifier_stmt : K_MODIFIER string stmtend
- { if (ysp_add(_yy, Y_DEFAULT, $2, NULL)== NULL) _YYERROR("modifier_stmt");
+ { if (ysp_add(_yy, Y_MODIFIER, $2, NULL)== NULL) _YYERROR("modifier_stmt");
clicon_debug(2,"modifier-stmt -> MODIFIER string"); }
;
diff --git a/lib/src/clixon_yang_type.c b/lib/src/clixon_yang_type.c
index 2d48a0bf..34b1cf2a 100644
--- a/lib/src/clixon_yang_type.c
+++ b/lib/src/clixon_yang_type.c
@@ -32,6 +32,20 @@
***** END LICENSE BLOCK *****
* Yang type related functions
+ * Part of this is type resolving which is pretty complex
+ *
+ * (called at parse / set cache)
+ * ys_resolve_type --+ ys_populate_range, yang_enum_int_value(NULL)
+ * \ | cml
+ * v v v
+ * yang_type_get --> yang_type_resolve --> resolve_restrictions
+ * (leaf(list) front) (recursive core fn) (regexps, length, ranges, ...)
+ * ^ ^ ^ ^
+ * | | | |
+ * | yang2cli_var | yang2cli_var_union_one
+ * ys_cv_validate ys_cv_validate_union_one
+ * |
+ * ys_populate_leaf, xml_cv_cache (NULL)
*/
#ifdef HAVE_CONFIG_H
@@ -145,7 +159,8 @@ yang_type_cache_set(yang_type_cache **ycache0,
yang_stmt *resolved,
int options,
cvec *cvv,
- char *pattern,
+ cvec *patterns,
+ cvec *regexps,
uint8_t fraction)
{
int retval = -1;
@@ -166,8 +181,12 @@ yang_type_cache_set(yang_type_cache **ycache0,
goto done;
}
}
- if (pattern && (ycache->yc_pattern = strdup(pattern)) == NULL){
- clicon_err(OE_UNIX, errno, "strdup");
+ if (patterns && (ycache->yc_patterns = cvec_dup(patterns)) == NULL){
+ clicon_err(OE_UNIX, errno, "cvec_dup");
+ goto done;
+ }
+ if (regexps && (ycache->yc_regexps = cvec_dup(regexps)) == NULL){
+ clicon_err(OE_UNIX, errno, "cvec_dup");
goto done;
}
ycache->yc_fraction = fraction;
@@ -176,26 +195,42 @@ yang_type_cache_set(yang_type_cache **ycache0,
return retval;
}
-/*! Get individual fields (direct/destructively) from yang type cache. */
+/*! Get individual fields (direct/destructively) from yang type cache.
+ * @param[out] patterns Initialized cvec of regexp patterns strings
+ */
int
yang_type_cache_get(yang_type_cache *ycache,
yang_stmt **resolved,
int *options,
cvec **cvv,
- char **pattern,
+ cvec *patterns,
+ cvec *regexps,
uint8_t *fraction)
{
+ int retval = -1;
+ cg_var *cv = NULL;
+
if (resolved)
*resolved = ycache->yc_resolved;
if (options)
*options = ycache->yc_options;
if (cvv)
*cvv = ycache->yc_cvv;
- if (pattern)
- *pattern = ycache->yc_pattern;
+ if (patterns){
+ cv = NULL;
+ while ((cv = cvec_each(ycache->yc_patterns, cv)) != NULL)
+ cvec_append_var(patterns, cv);
+ }
+ if (regexps){
+ cv = NULL;
+ while ((cv = cvec_each(ycache->yc_regexps, cv)) != NULL)
+ cvec_append_var(regexps, cv);
+ }
if (fraction)
*fraction = ycache->yc_fraction;
- return 0;
+ retval = 0;
+ // done:
+ return retval;
}
int
@@ -205,15 +240,28 @@ yang_type_cache_cp(yang_type_cache **ycnew,
int retval = -1;
int options;
cvec *cvv;
- char *pattern;
+ cvec *patterns = NULL;
+ cvec *regexps = NULL;
uint8_t fraction;
yang_stmt *resolved;
- yang_type_cache_get(ycold, &resolved, &options, &cvv, &pattern, &fraction);
- if (yang_type_cache_set(ycnew, resolved, options, cvv, pattern, fraction) < 0)
+ if ((patterns = cvec_new(0)) == NULL){
+ clicon_err(OE_UNIX, errno, "cvec_new");
+ goto done;
+ }
+ if ((regexps = cvec_new(0)) == NULL){
+ clicon_err(OE_UNIX, errno, "cvec_new");
+ goto done;
+ }
+ yang_type_cache_get(ycold, &resolved, &options, &cvv, patterns, regexps, &fraction);
+ if (yang_type_cache_set(ycnew, resolved, options, cvv, patterns, regexps, fraction) < 0)
goto done;
retval = 0;
done:
+ if (patterns)
+ cvec_free(patterns);
+ if (regexps)
+ cvec_free(regexps);
return retval;
}
@@ -222,12 +270,59 @@ yang_type_cache_free(yang_type_cache *ycache)
{
if (ycache->yc_cvv)
cvec_free(ycache->yc_cvv);
- if (ycache->yc_pattern)
- free(ycache->yc_pattern);
+ if (ycache->yc_patterns)
+ cvec_free(ycache->yc_patterns);
+ if (ycache->yc_regexps)
+ cvec_free(ycache->yc_regexps);
free(ycache);
return 0;
}
+/* Compile yang patterns in string form to regex compiled void* form
+ * and re-store into "patterns" cvec.
+ * This is done here instead of deep in resolve code (resolve_restrictions)
+ * since it id dependent on clicon_handle.
+ * The downside is that all accesses to "patterns" must pass via the cache.
+ * If calls to yang_type_resolve is made without the cache is set, will be
+ * wrong.
+ */
+static int
+compile_pattern2regexp(clicon_handle h,
+ cvec *patterns,
+ cvec *regexps)
+{
+ int retval = -1;
+ cg_var *pcv; /* pattern cv */
+ cg_var *rcv; /* regexp cv */
+ void *re;
+ int ret;
+ char *pattern;
+
+ pcv = NULL;
+ while ((pcv = cvec_each(patterns, pcv)) != NULL){
+ if (cv_type_get(pcv) == CGV_VOID)
+ continue; /* already compiled */
+ pattern = cv_string_get(pcv);
+ /* Compile yang pattern. handle necessary to select regex engine */
+ if ((ret = regex_compile(h, pattern, &re)) < 0)
+ goto done;
+ if (ret == 0){
+ clicon_err(OE_YANG, errno, "regexp compile fail: \"%s\"",
+ pattern);
+ goto done;
+ break;
+ }
+ if ((rcv = cvec_add(regexps, CGV_VOID)) == NULL){
+ clicon_err(OE_UNIX, errno, "cvec_add");
+ goto done;
+ }
+ cv_void_set(rcv, re);
+ }
+ retval = 1;
+ done:
+ return retval;
+}
+
/*! Resolve types: populate type caches
* @param[in] ys This is a type statement
* @param[in] arg Not used
@@ -235,31 +330,58 @@ yang_type_cache_free(yang_type_cache *ycache)
* @note unions not cached
*/
int
-ys_resolve_type(yang_stmt *ys,
- void *arg)
+ys_resolve_type(yang_stmt *ys,
+ void *arg)
{
+ clicon_handle h = (clicon_handle)arg;
int retval = -1;
int options = 0x0;
cvec *cvv = NULL;
- char *pattern = NULL;
+ cvec *patterns = NULL;
+ cvec *regexps = NULL;
uint8_t fraction = 0;
yang_stmt *resolved = NULL;
-
- assert(ys->ys_keyword == Y_TYPE);
+
+ if (yang_keyword_get(ys) != Y_TYPE){
+ clicon_err(OE_YANG, EINVAL, "Expected Y_TYPE");
+ goto done;
+ }
+ if ((patterns = cvec_new(0)) == NULL){
+ clicon_err(OE_UNIX, errno, "cvec_new");
+ goto done;
+ }
/* Recursively resolve ys -> resolve with restrictions(options, etc)
* Note that the resolved type could be ys itself.
*/
- if (yang_type_resolve((yang_stmt*)ys->ys_parent, (yang_stmt*)ys->ys_parent,
+ if (yang_type_resolve(ys->ys_parent, ys->ys_parent,
ys, &resolved,
- &options, &cvv, &pattern, &fraction) < 0)
+ &options, &cvv, patterns, NULL, &fraction) < 0)
goto done;
- /* Cache the resolve locally */
+ /* If pattern strings, then compile regexps as well */
+ if (cvec_len(patterns)){
+ if ((regexps = cvec_new(0)) == NULL){
+ clicon_err(OE_UNIX, errno, "cvec_new");
+ goto done;
+ }
+ /* Compile from pattern strings to compiled regexps */
+ if (compile_pattern2regexp(h, patterns, regexps) < 1)
+ goto done;
+ }
+ /* Cache the type resolving locally. Only place where this is done.
+ * Why not do it in yang_type_resolve? (compile regexps needs clicon_handle)
+ */
if (yang_type_cache_set(&ys->ys_typecache,
- resolved, options, cvv, pattern, fraction) < 0)
+ resolved, options, cvv,
+ patterns, regexps,
+ fraction) < 0)
goto done;
retval = 0;
done:
+ if (patterns)
+ cvec_free(patterns);
+ if (regexps)
+ cvec_free(regexps);
return retval;
}
@@ -368,6 +490,48 @@ clicon_type2cv(char *origtype,
return retval;
}
+/*! Validate CLIgen variable with pattern statements
+ * @param[in] h Clicon handle
+ * @param[in] regexps Vector of compiled regexps
+ * @param[out] reason If given, and return value is 0, contains malloced string
+ * @retval -1 Error (fatal), with errno set to indicate error
+ * @retval 0 Validation not OK, malloced reason is returned. Free reason with free()
+ * @retval 1 Validation OK
+ */
+static int
+cv_validate_pattern(clicon_handle h,
+ cvec *regexps,
+ yang_stmt *yrestype,
+ char *str,
+ char **reason)
+{
+ int retval = -1;
+ cg_var *cvr;
+ void *re = NULL;
+ int ret;
+
+ cvr = NULL; /* Loop over compiled regexps */
+ while ((cvr = cvec_each(regexps, cvr)) != NULL){
+ re = cv_void_get(cvr);
+ if ((ret = regex_exec(h, re, str?str:"")) < 0)
+ goto done;
+ if (ret == 0){
+ if (reason)
+ *reason = cligen_reason("regexp match fail: pattern does not match %s",
+ str);
+ goto fail;
+ break;
+ }
+ }
+ retval = 1; /* match */
+ done:
+ return retval;
+ fail:
+ retval = 0; /* validation failed */
+ goto done;
+
+}
+
/* cf cligen/cligen_var.c */
#define range_check(i, rmin, rmax, type) \
((rmin && (i) < cv_##type##_get(rmin)) || \
@@ -375,9 +539,17 @@ clicon_type2cv(char *origtype,
/*! Validate CLIgen variable
+ * @param[in] h Clicon handle
+ * @param[in] cv A cligen variable to validate. This is a correctly parsed cv.
+ * @param[in] cvtype Resolved type of cv
+ * string describing reason why validation failed.
+ * @param[in] regexps Vector of compiled regexps
+ * @param[out] reason If given, and return value is 0, contains malloced str
+
* @retval -1 Error (fatal), with errno set to indicate error
* @retval 0 Validation not OK, malloced reason is returned. Free reason with free()
* @retval 1 Validation OK
+ * @note reason if given must be freed by caller
*/
static int
cv_validate1(clicon_handle h,
@@ -385,7 +557,7 @@ cv_validate1(clicon_handle h,
enum cv_type cvtype,
int options,
cvec *cvv,
- char *pattern,
+ cvec *regexps,
yang_stmt *yrestype,
char *restype,
char **reason)
@@ -554,30 +726,12 @@ cv_validate1(clicon_handle h,
}
}
}
- if ((options & YANG_OPTIONS_PATTERN) != 0) {
- void *re = NULL;
- if ((re = yang_regex_cache_get(yrestype)) == NULL){
- if ((ret = regex_compile(h, pattern, &re)) < 0)
- goto done;
- if (ret == 0){
- if (reason)
- *reason = cligen_reason("regexp compile fail: \"%s\"",
- pattern);
- goto fail;
- break;
- }
- yang_regex_cache_set(yrestype, re);
- }
- if ((ret = regex_exec(h, re, str?str:"")) < 0)
- goto done;
- if (ret == 0){
- if (reason)
- *reason = cligen_reason("regexp match fail: \"%s\" does not match %s",
- str, pattern);
+ if (regexps && cvec_len(regexps)) {
+ if ((ret = cv_validate_pattern(h, regexps, yrestype, str, reason)) < 0)
+ goto done;
+ if (ret == 0)
goto fail;
- break;
- }
- }
+ }
break;
case CGV_VOID:
break; /* empty type OK */
@@ -605,29 +759,34 @@ static int ys_cv_validate_union(clicon_handle h,yang_stmt *ys, char **reason,
yang_stmt *yrestype, char *type, char *val);
/*!
+ * @param[out] reason If given and return val is 0, contains a malloced string
* @retval -1 Error (fatal), with errno set to indicate error
* @retval 0 Validation not OK, malloced reason is returned. Free reason with free()
* @retval 1 Validation OK
*/
static int
ys_cv_validate_union_one(clicon_handle h,
- yang_stmt *ys,
- char **reason,
- yang_stmt *yt,
- char *type, /* orig type */
- char *val)
+ yang_stmt *ys,
+ char **reason,
+ yang_stmt *yt,
+ char *type, /* orig type */
+ char *val)
{
int retval = -1;
yang_stmt *yrt; /* union subtype */
int options = 0;
cvec *cvv = NULL;
- char *pattern = NULL;
+ cvec *regexps = NULL;
uint8_t fraction = 0;
char *restype;
enum cv_type cvtype;
cg_var *cvt=NULL;
- if (yang_type_resolve(ys, ys, yt, &yrt, &options, &cvv, &pattern,
+ if ((regexps = cvec_new(0)) == NULL){
+ clicon_err(OE_UNIX, errno, "cvec_new");
+ goto done;
+ }
+ if (yang_type_resolve(ys, ys, yt, &yrt, &options, &cvv, NULL, regexps,
&fraction) < 0)
goto done;
restype = yrt?yrt->ys_argument:NULL;
@@ -650,16 +809,19 @@ ys_cv_validate_union_one(clicon_handle h,
if (retval == 0)
goto done;
if ((retval = cv_validate1(h, cvt, cvtype, options, cvv,
- pattern, yrt, restype, reason)) < 0)
+ regexps, yrt, restype, reason)) < 0)
goto done;
}
done:
+ if (regexps)
+ cvec_free(regexps);
if (cvt)
cv_free(cvt);
return retval;
}
/*!
+ * @param[out] reason If given, and return value is 0, contains malloced string
* @retval -1 Error (fatal), with errno set to indicate error
* @retval 0 Validation not OK, malloced reason is returned. Free reason with free()
* @retval 1 Validation OK
@@ -705,26 +867,28 @@ ys_cv_validate_union(clicon_handle h,
/*! Validate cligen variable cv using yang statement as spec
*
+ * @param[in] h Clicon handle
* @param[in] cv A cligen variable to validate. This is a correctly parsed cv.
* @param[in] ys A yang statement, must be leaf or leaf-list.
- * @param[out] reason If given, and if return value is 0, contains a malloced string
- * describing the reason why the validation failed. Must be freed.
+ * @param[out] reason If given, and if return value is 0, contains malloced
+ * string describing reason why validation failed.
* @retval -1 Error (fatal), with errno set to indicate error
* @retval 0 Validation not OK, malloced reason is returned. Free reason with free()
* @retval 1 Validation OK
* See also cv_validate - the code is similar.
+ * @note reason if given must be freed by caller
*/
int
ys_cv_validate(clicon_handle h,
- cg_var *cv,
- yang_stmt *ys,
- char **reason)
+ cg_var *cv,
+ yang_stmt *ys,
+ char **reason)
{
int retval = -1;
cg_var *ycv; /* cv of yang-statement */
int options = 0;
cvec *cvv = NULL;
- char *pattern = NULL;
+ cvec *regexps = NULL;
enum cv_type cvtype;
char *type; /* orig type */
yang_stmt *yrestype; /* resolved type */
@@ -741,8 +905,14 @@ ys_cv_validate(clicon_handle h,
goto done;
}
ycv = ys->ys_cv;
+ if ((regexps = cvec_new(0)) == NULL){
+ clicon_err(OE_UNIX, errno, "cvec_new");
+ goto done;
+ }
if (yang_type_get(ys, &type, &yrestype,
- &options, &cvv, &pattern, &fraction) < 0)
+ &options, &cvv,
+ NULL, regexps,
+ &fraction) < 0)
goto done;
restype = yrestype?yrestype->ys_argument:NULL;
if (clicon_type2cv(type, restype, ys, &cvtype) < 0)
@@ -767,10 +937,12 @@ ys_cv_validate(clicon_handle h,
retval = retval2; /* invalid (0) with latest reason or valid 1 */
}
else
- if ((retval = cv_validate1(h, cv, cvtype, options, cvv, pattern,
- yrestype, restype, reason)) < 0)
+ if ((retval = cv_validate1(h, cv, cvtype, options, cvv,
+ regexps, yrestype, restype, reason)) < 0)
goto done;
done:
+ if (regexps)
+ cvec_free(regexps);
if (cvt)
cv_free(cvt);
return retval;
@@ -876,45 +1048,63 @@ yang_find_identity(yang_stmt *ys,
return yid;
}
-/*! Resolve type restrictions, return contraining parameters
- * @param[in] yrange Yang type range restriction if any
- * @param[in] ylength Yang type length restriction if any
- * @param[in] ypattern Yang type pattern restriction if any
- * @param[in] yfraction Yang type fraction restriction if any
+/*! Resolve type restrictions, return constraining parameters
+ *
+ * This is for types with range/length/regexp restrictions of the base type
+ * Also fraction-digits for decimal64 is handled as that.
+ * @param[in] ytype yang-stmt object containing currently resolving type
* @param[out] options Pointer to flags field of optional values. optional
* @param[out] cvv Pointer to cvec with min range or length.
* If options&YANG_OPTIONS_RANGE or YANG_OPTIONS_LENGTH
- * @param[out] pattern Pointer to static string of yang string pattern. optional
+ * @param[out] regexps Pointer to cvec of compiled patterns
* @param[out] fraction For decimal64, how many digits after period
- * @retval 0 OK.
+ * @retval -1 Error
+ * @retval 0 OK.
*/
static int
-resolve_restrictions(yang_stmt *yrange,
- yang_stmt *ylength,
- yang_stmt *ypattern,
- yang_stmt *yfraction,
+resolve_restrictions(yang_stmt *ytype,
int *options,
cvec **cvv,
- char **pattern,
+ cvec *regexps,
uint8_t *fraction)
{
- if (options && cvv && yrange != NULL){
- *cvv = yrange->ys_cvec;
+ int retval = -1;
+ yang_stmt *ys;
+ cg_var *cv;
+ char *pattern;
+
+ if (options && cvv &&
+ (ys = yang_find(ytype, Y_RANGE, NULL)) != NULL){
+ *cvv = ys->ys_cvec;
*options |= YANG_OPTIONS_RANGE;
}
- if (options && cvv && ylength != NULL){
- *cvv = ylength->ys_cvec;
+ if (options && cvv &&
+ (ys = yang_find(ytype, Y_LENGTH, NULL)) != NULL){
+ *cvv = ys->ys_cvec;
*options |= YANG_OPTIONS_LENGTH;
}
- if (options && pattern && ypattern != NULL){
- *pattern = ypattern->ys_argument;
- *options |= YANG_OPTIONS_PATTERN;
+ /* Find all patterns */
+ if (options && regexps){
+ ys = NULL;
+ while ((ys = yn_each(ytype, ys)) != NULL) {
+ if (yang_keyword_get(ys) != Y_PATTERN)
+ continue;
+ if ((cv = cvec_add(regexps, CGV_STRING)) == NULL){
+ clicon_err(OE_UNIX, errno, "cvec_add");
+ goto done;
+ }
+ pattern = ys->ys_argument; /* clear text pattern */
+ cv_string_set(cv, pattern);
+ }
}
- if (options && fraction && yfraction != NULL){
- *fraction = cv_uint8_get(yfraction->ys_cv);
+ if (options && fraction &&
+ (ys = yang_find(ytype, Y_FRACTION_DIGITS, NULL)) != NULL){
+ *fraction = cv_uint8_get(ys->ys_cv);
*options |= YANG_OPTIONS_FRACTION_DIGITS;
}
- return 0;
+ retval = 0;
+ done:
+ return retval;
}
/*! Recursively resolve a yang type to built-in type with optional restrictions
@@ -926,8 +1116,8 @@ resolve_restrictions(yang_stmt *yrange,
* @param[out] cvv Cvec with min/max range or length.
* Present if options&YANG_OPTIONS_RANGE|_LENGTH.
* Can be a vector if multiple ranges
- * @param[out] pattern String of POSIX regexp pattern
- * Present if options&YANG_OPTIONS_PATTERN
+ * @param[out] patterns Initialized cvec of regexp patterns strings (if any)
+ * @param[out] regexps Initialized cvec of compiled regexps (if any)
* @param[out] fraction for decimal64, how many digits after period
* Present if options&YANG_OPTIONS_FRACTION_DIGITS
* @retval 0 OK. Note yrestype may still be NULL.
@@ -935,8 +1125,8 @@ resolve_restrictions(yang_stmt *yrange,
* The setting of the options argument has the following semantics:
* options&YANG_OPTIONS_RANGE or YANG_OPTIONS_LENGTH --> cvv is set containing
* array of range_min, range_max cv:s
- * options&YANG_OPTIONS_PATTERN --> pattern is set
* options&YANG_OPTIONS_FRACTION_DIGITS --> fraction is set
+ * patterns && cvec_len(patterns) --> there are patterns
* Note that the static output strings (type, pattern) should be copied if used asap.
* Note also that for all pointer arguments, if NULL is given, no value is assigned.
*/
@@ -947,15 +1137,12 @@ yang_type_resolve(yang_stmt *yorig,
yang_stmt **yrestype,
int *options,
cvec **cvv,
- char **pattern,
+ cvec *patterns,
+ cvec *regexps,
uint8_t *fraction)
{
yang_stmt *rytypedef = NULL; /* Resolved typedef of ytype */
yang_stmt *rytype; /* Resolved type of ytype */
- yang_stmt *yrange;
- yang_stmt *ylength;
- yang_stmt *ypattern;
- yang_stmt *yfraction;
char *type;
char *prefix = NULL;
int retval = -1;
@@ -968,23 +1155,18 @@ yang_type_resolve(yang_stmt *yorig,
type = yarg_id(ytype); /* This is the type to resolve */
prefix = yarg_prefix(ytype); /* And this its prefix */
/* Cache does not work for eg string length 32? */
- if (!yang_builtin(type) && ytype->ys_typecache != NULL){
+ if (/*!yang_builtin(type) &&*/ ytype->ys_typecache != NULL){
if (yang_type_cache_get(ytype->ys_typecache, yrestype,
- options, cvv, pattern, fraction) < 0)
+ options, cvv, patterns, regexps, fraction) < 0)
goto done;
goto ok;
}
- /* Resolving type restrictions */
- yrange = yang_find(ytype, Y_RANGE, NULL);
- ylength = yang_find(ytype, Y_LENGTH, NULL);
- ypattern = yang_find(ytype, Y_PATTERN, NULL);
- yfraction = yang_find(ytype, Y_FRACTION_DIGITS, NULL);
/* Check if type is basic type. If so, return that */
- if (prefix == NULL && yang_builtin(type)){
+ if ((prefix == NULL && yang_builtin(type))){
*yrestype = ytype;
- resolve_restrictions(yrange, ylength, ypattern, yfraction, options,
- cvv, pattern, fraction);
+ if (resolve_restrictions(ytype, options, cvv, patterns, fraction) < 0)
+ goto done;
goto ok;
}
@@ -1023,11 +1205,13 @@ yang_type_resolve(yang_stmt *yorig,
}
/* recursively resolve this new type */
if (yang_type_resolve(yorig, ys, rytype, yrestype,
- options, cvv, pattern, fraction) < 0)
+ options, cvv,
+ patterns, regexps,
+ fraction) < 0)
+ goto done;
+ /* appends patterns, overwrites others if any */
+ if (resolve_restrictions(ytype, options, cvv, patterns, fraction) < 0)
goto done;
- /* overwrites the resolved if any */
- resolve_restrictions(yrange, ylength, ypattern, yfraction, options,
- cvv, pattern, fraction);
}
ok:
retval = 0;
@@ -1043,17 +1227,17 @@ yang_type_resolve(yang_stmt *yorig,
* yang_stmt *yrestype;
* int options;
* cvec *cvv = NULL;
- * char *pattern;
+ * cvec *patterns = cvec_new(0);
+ * cvec *regexps = cvec_new(0);
* uint8_t fraction;
*
- * if (yang_type_get(ys, &type, &yrestype, &options, &cvv, &pattern, &fraction) < 0)
+ * if (yang_type_get(ys, &type, &yrestype, &options, &cvv,
+ * patterns, regexps, &fraction) < 0)
* goto err;
* if (yrestype == NULL) # unresolved
* goto err;
* if (options & YANG_OPTIONS_LENGTH != 0)
* printf("%d..%d\n", min , max);
- * if (options & YANG_OPTIONS_PATTERN != 0)
- * printf("regexp: %s\n", pattern);
* @endcode
* @param[in] ys yang-stmt, leaf or leaf-list
* @param[out] origtype original type may be derived or built-in
@@ -1062,8 +1246,8 @@ yang_type_resolve(yang_stmt *yorig,
* @param[out] cvv Cvec with min/max range or length.
* Present if options&YANG_OPTIONS_RANGE|_LENGTH.
* Can be a vector if multiple ranges
- * @param[out] pattern yang string pattern POSIX regexp patterns
- * Present if options&YANG_OPTIONS_PATTERN
+ * @param[out] pattern yang cvec pattern POSIX regexp patterns
+ * @param[out] regexps Initialized cvec of compiled regexps (if any)
* @param[out] fraction for decimal64, how many digits after period
* Present if options&YANG_OPTIONS_FRACTION_DIGITS
* @retval 0 OK, but note that restype==NULL means not resolved.
@@ -1071,7 +1255,6 @@ yang_type_resolve(yang_stmt *yorig,
* The setting of the options argument has the following semantics:
* options&YANG_OPTIONS_RANGE or YANG_OPTIONS_LENGTH --> cvv is set containing
* array of range_min, range_max cv:s
- * options&YANG_OPTIONS_PATTERN --> pattern is set
* options&YANG_OPTIONS_FRACTION_DIGITS --> fraction is set
* Note that the static output strings (type, pattern) should be copied if used asap.
* Note also that for all pointer arguments, if NULL is given, no value is assigned.
@@ -1083,7 +1266,8 @@ yang_type_get(yang_stmt *ys,
yang_stmt **yrestype,
int *options,
cvec **cvv,
- char **pattern,
+ cvec *patterns,
+ cvec *regexps,
uint8_t *fraction
)
{
@@ -1103,7 +1287,7 @@ yang_type_get(yang_stmt *ys,
if (origtype)
*origtype = type;
if (yang_type_resolve(ys, ys, ytype, yrestype,
- options, cvv, pattern, fraction) < 0)
+ options, cvv, patterns, regexps, fraction) < 0)
goto done;
clicon_debug(3, "%s: %s %s->%s", __FUNCTION__, ys->ys_argument, type,
*yrestype?(*yrestype)->ys_argument:"null");
diff --git a/test/test_pattern.sh b/test/test_pattern.sh
index 513edcfb..d546c667 100755
--- a/test/test_pattern.sh
+++ b/test/test_pattern.sh
@@ -52,6 +52,41 @@ module pattern{
description
"The container contains a leaf per pattern case in test_regexp.sh
which are the unique patterns from yang-models";
+ leaf rfc2{
+ description "RFC 7950 Sec 9.4.7 2nd example";
+ type string {
+ length "0..4";
+ pattern "[0-9a-fA-F]*";
+ }
+ }
+ leaf rfc3{
+ description "RFC 7950 Sec 9.4.7 3rd example";
+ type string {
+ length "1..max";
+ pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*';
+/*
+ pattern '[xX][mM][lL].*' {
+ modifier invert-match;
+ }
+*/
+ }
+ }
+ typedef twomatchtype {
+ description "Example of double patterns in single type";
+ type string{
+ pattern "[a-z]+";
+ pattern "g[^g]*";
+ }
+ }
+ leaf twomatch{
+ type twomatchtype;
+ }
+ leaf threematch{
+ description "Two patterns plus one local";
+ type twomatchtype {
+ pattern "[a-z]{3,6}";
+ }
+ }
leaf p1{
description "juniper regexp";
type string {
@@ -325,29 +360,26 @@ EOF
# It assumes a yang with a hardcoded container
to work properly
# The function can expect matching or fail (negative test)
testrun(){
- pnr="$1" # leafnr
+ leaf="$1" # leaf tag under with pattern to test
mat="$2" # expected match (1) or fail (0)
- co0="$3" # content string (to match against)
+ str0="$3" # content string (to match against)
# URI-encode the string to be sent with netconf
- co=$(echo "$co0" | sed 's/&/\&/g; s/\</g; s/>/\>/g; s/"/\"/g; s/'"'"'/\'/g')
-# echo "pnr:$pnr"
+ str=$(echo "$str0" | sed 's/&/\&/g; s/\</g; s/>/\>/g; s/"/\"/g; s/'"'"'/\'/g')
+# echo "leaf:$leaf"
# echo "mat:$mat"
-# echo "co:$co"
- trunc=$(echo "$co"|cut -c1-15)
+# echo "str:$str"
+ trunc=$(echo "$str"|cut -c1-15)
- new "pattern edit p$pnr string: $trunc"
-# ret=$(echo "