diff --git a/.gitignore b/.gitignore
index 4927378c..6826005a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,6 +49,7 @@ build-root/*.rpm
build-root/rpmbuild
util/clixon_util_datastore
+util/clixon_util_insert
util/clixon_util_json
util/clixon_util_stream
util/clixon_util_xml
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5a658ce8..2fb75b97 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -86,7 +86,15 @@
* rpc get and get-config api function has an added namespace argument:
* `clicon_rpc_get_config(clicon_handle h, char *db, char *xpath, char *namespace, cxobj **xt);`
* `int clicon_rpc_get(clicon_handle h, char *xpath, char *namespace, cxobj **xt);`
-
+* Error messages for invalid number ranges and string lengths have been uniformed and changed.
+ * Error messages for invalid ranges are now on the form:
+ ```
+ Number 23 out of range: 1 - 10
+ String length 23 out of range: 1 - 10
+ ```
+* On validation callbacks, XML_FLAG_ADD is added to all nodes at startup validation, not just the top-level. This is the same behaviour as for steady-state validation.
+* All hash_ functions have been prefixed with `clicon_` to avoid name collision with other packages (frr)
+ * All calls to the following functions must be changed: `hash_init`, `hash_free`, `hash_lookup`, `hash_value`, `hash_add`, `hash_del`, `hash_dump`, and `hash_keys`.
* RESTCONF strict namespace validation of data in POST and PUT.
* Accepted:
```
@@ -209,7 +217,9 @@
### Minor changes
+* Added new API function `xpath_parse()` to split parsing and xml evaluation.
* Rewrote `api_path2xpath` to handle namespaces.
+* `api_path2xml_vec` strict mode check added if list key length mismatch
* `startup_extraxml` triggers unnecessary validation
* Renamed startup_db_reset -> xmldb_db_reset (its a general function)
* In startup_extraxml(), check if reset callbacks or extraxml file actually makes and changes to the tmp db.
@@ -260,6 +270,9 @@
### Corrected Bugs
+* Return 404 Not found error if restconf GET does not return requested instance
+* Fixed [Wrong yang-generated cli code for typeref identityref combination #88](https://github.com/clicon/clixon/issues/88)
+* Fixed [identityref validation fails when using typedef #87](https://github.com/clicon/clixon/issues/87)
* Fixed a problem with some netconf error messages caused restconf daemon to exit due to no XML encoding
* Check cligen tab mode, dont start if CLICON_CLI_TAB_MODE is undefined
* Startup transactions did not mark added tree with XML_FLAG_ADD as it should.
diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c
index 357ac8a2..66d2a628 100644
--- a/apps/backend/backend_commit.c
+++ b/apps/backend/backend_commit.c
@@ -214,7 +214,8 @@ startup_common(clicon_handle h,
xt = NULL;
x = NULL;
while ((x = xml_child_each(td->td_target, x, CX_ELMNT)) != NULL){
- xml_flag_set(x, XML_FLAG_ADD);
+ xml_flag_set(x, XML_FLAG_ADD); /* Also down */
+ xml_apply(x, CX_ELMNT, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_ADD);
if (cxvec_append(x, &td->td_avec, &td->td_alen) < 0)
goto done;
}
diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c
index 1a0d013e..206aac7d 100644
--- a/apps/cli/cli_common.c
+++ b/apps/cli/cli_common.c
@@ -107,7 +107,7 @@ cli_notification_register(clicon_handle h,
goto done;
}
snprintf(logname, len, "log_socket_%s", stream);
- if ((p = hash_value(cdat, logname, &len)) != NULL)
+ if ((p = clicon_hash_value(cdat, logname, &len)) != NULL)
s_exist = *(int*)p;
if (status){ /* start */
@@ -119,14 +119,14 @@ cli_notification_register(clicon_handle h,
goto done;
if (cligen_regfd(s, fn, arg) < 0)
goto done;
- if (hash_add(cdat, logname, &s, sizeof(s)) == NULL)
+ if (clicon_hash_add(cdat, logname, &s, sizeof(s)) == NULL)
goto done;
}
else{ /* stop */
if (s_exist != -1){
cligen_unregfd(s_exist);
}
- hash_del(cdat, logname);
+ clicon_hash_del(cdat, logname);
#if 0 /* cant turn off */
if (clicon_rpc_create_subscription(h, status, stream, format, filter, NULL) < 0)
goto done;
diff --git a/apps/cli/cli_generate.c b/apps/cli/cli_generate.c
index 5f95915e..5a0c4cf6 100644
--- a/apps/cli/cli_generate.c
+++ b/apps/cli/cli_generate.c
@@ -163,7 +163,6 @@ cli_callback_generate(clicon_handle h,
return retval;
}
-
/*! Generate identityref statements for CLI variables
* @param[in] ys Yang statement
* @param[in] ytype Yang union type being resolved
@@ -186,13 +185,13 @@ yang2cli_var_identityref(yang_stmt *ys,
char *id;
int i;
- /* Add a wildchar string first -let validate take it for default prefix */
- cprintf(cb, ">");
- if (helptext)
- cprintf(cb, "(\"%s\")", helptext);
if ((ybaseref = yang_find(ytype, Y_BASE, NULL)) != NULL &&
(ybaseid = yang_find_identity(ys, yang_argument_get(ybaseref))) != NULL){
if (cvec_len(yang_cvec_get(ybaseid)) > 0){
+ /* Add a wildchar string first -let validate take it for default prefix */
+ cprintf(cb, ">");
+ if (helptext)
+ cprintf(cb, "(\"%s\")", helptext);
cprintf(cb, "|<%s:%s choice:", yang_argument_get(ys), cvtypestr);
i = 0;
while ((cv = cvec_each(yang_cvec_get(ybaseid), cv)) != NULL){
diff --git a/apps/restconf/clixon_restconf.h b/apps/restconf/clixon_restconf.h
index 4c93c104..06c6ed86 100644
--- a/apps/restconf/clixon_restconf.h
+++ b/apps/restconf/clixon_restconf.h
@@ -55,7 +55,7 @@ int test(FCGX_Request *r, int dbg);
cbuf *readdata(FCGX_Request *r);
int get_user_cookie(char *cookiestr, char *attribute, char **val);
int api_return_err(clicon_handle h, FCGX_Request *r, cxobj *xerr,
- int pretty, int use_xml);
+ int pretty, int use_xml, int code);
#endif /* _CLIXON_RESTCONF_H_ */
diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c
index bb2c8771..43f094e3 100644
--- a/apps/restconf/restconf_lib.c
+++ b/apps/restconf/restconf_lib.c
@@ -394,13 +394,16 @@ get_user_cookie(char *cookiestr,
* @param[in] xerr XML error message from backend
* @param[in] pretty Set to 1 for pretty-printed xml/json output
* @param[in] use_xml Set to 0 for JSON and 1 for XML
+ * @param[in] code If 0 use rfc8040 sec 7 netconf2restconf error-tag mapping
+ * otherwise use this code
*/
int
api_return_err(clicon_handle h,
FCGX_Request *r,
cxobj *xerr,
int pretty,
- int use_xml)
+ int use_xml,
+ int code0)
{
int retval = -1;
cbuf *cb = NULL;
@@ -417,8 +420,12 @@ api_return_err(clicon_handle h,
goto ok;
}
tagstr = xml_body(xtag);
- if ((code = restconf_err2code(tagstr)) < 0)
- code = 500; /* internal server error */
+ if (code0 != 0)
+ code = code0;
+ else{
+ if ((code = restconf_err2code(tagstr)) < 0)
+ code = 500; /* internal server error */
+ }
if ((reason_phrase = restconf_code2reason(code)) == NULL)
reason_phrase="";
if (xml_name_set(xerr, "error") < 0)
diff --git a/apps/restconf/restconf_lib.h b/apps/restconf/restconf_lib.h
index 57839bc8..1b90ef30 100644
--- a/apps/restconf/restconf_lib.h
+++ b/apps/restconf/restconf_lib.h
@@ -60,7 +60,7 @@ int test(FCGX_Request *r, int dbg);
cbuf *readdata(FCGX_Request *r);
int get_user_cookie(char *cookiestr, char *attribute, char **val);
int api_return_err(clicon_handle h, FCGX_Request *r, cxobj *xerr,
- int pretty, int use_xml);
+ int pretty, int use_xml, int code);
int restconf_terminate(clicon_handle h);
#endif /* _RESTCONF_LIB_H_ */
diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c
index 74dd5e02..8a4931f7 100644
--- a/apps/restconf/restconf_main.c
+++ b/apps/restconf/restconf_main.c
@@ -397,7 +397,7 @@ api_restconf(clicon_handle h,
if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0)
goto done;
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xerr, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c
index c6aa7c59..5c21d3a5 100644
--- a/apps/restconf/restconf_methods.c
+++ b/apps/restconf/restconf_methods.c
@@ -216,7 +216,7 @@ api_data_get2(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -233,7 +233,7 @@ api_data_get2(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -252,7 +252,7 @@ api_data_get2(clicon_handle h,
#endif
/* Check if error return */
if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -283,7 +283,20 @@ api_data_get2(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
+ goto done;
+ goto ok;
+ }
+ /* Check if not exists */
+ if (xlen == 0){
+ /* 4.3: If a retrieval request for a data resource represents an
+ instance that does not exist, then an error response containing
+ a "404 Not Found" status-line MUST be returned by the server.
+ The error-tag value "invalid-value" is used in this case. */
+ if (netconf_invalid_value_xml(&xe, "application", "Instance does not exist") < 0)
+ goto done;
+ /* override invalid-value default 400 with 404 */
+ if (api_return_err(h, r, xe, pretty, use_xml, 404) < 0)
goto done;
goto ok;
}
@@ -495,7 +508,7 @@ api_data_post(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -509,7 +522,7 @@ api_data_post(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -522,7 +535,7 @@ api_data_post(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -531,7 +544,7 @@ api_data_post(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -546,7 +559,7 @@ api_data_post(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -568,14 +581,15 @@ api_data_post(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (debug){
- cbuf *ccc=cbuf_new();
- if (clicon_xml2cbuf(ccc, xe, 0, 0) < 0)
- goto done;
- clicon_debug(1, "%s XE:%s", __FUNCTION__, cbuf_get(ccc));
- }
-
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+#if 0
+ if (debug){
+ cbuf *ccc=cbuf_new();
+ if (clicon_xml2cbuf(ccc, xe, 0, 0) < 0)
+ goto done;
+ clicon_debug(1, "%s XE:%s", __FUNCTION__, cbuf_get(ccc));
+ }
+#endif
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -607,7 +621,7 @@ api_data_post(clicon_handle h,
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0)
goto done;
if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -629,7 +643,7 @@ api_data_post(clicon_handle h,
/* log errors from discard, but ignore */
if ((xpath_first(xretdis, NULL, "//rpc-error")) != NULL)
clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
- if (api_return_err(h, r, xe, pretty, use_xml) < 0) /* Use original xe */
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0) /* Use original xe */
goto done;
goto ok;
}
@@ -843,7 +857,7 @@ api_data_put(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -858,7 +872,7 @@ api_data_put(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -871,7 +885,7 @@ api_data_put(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -880,7 +894,7 @@ api_data_put(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -895,7 +909,7 @@ api_data_put(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -915,7 +929,7 @@ api_data_put(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -955,7 +969,7 @@ api_data_put(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -980,7 +994,7 @@ api_data_put(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -1004,7 +1018,7 @@ api_data_put(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -1039,9 +1053,8 @@ api_data_put(clicon_handle h,
clicon_debug(1, "%s xml: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path);
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0)
goto done;
-
if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -1062,7 +1075,7 @@ api_data_put(clicon_handle h,
/* log errors from discard, but ignore */
if ((xpath_first(xretdis, NULL, "//rpc-error")) != NULL)
clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -1195,7 +1208,7 @@ api_data_delete(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -1219,7 +1232,7 @@ api_data_delete(clicon_handle h,
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0)
goto done;
if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -1241,7 +1254,7 @@ api_data_delete(clicon_handle h,
/* log errors from discard, but ignore */
if ((xpath_first(xretdis, NULL, "//rpc-error")) != NULL)
clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -1433,7 +1446,7 @@ api_operations_post_input(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto fail;
}
@@ -1446,7 +1459,7 @@ api_operations_post_input(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto fail;
}
@@ -1455,7 +1468,7 @@ api_operations_post_input(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto fail;
}
@@ -1488,7 +1501,7 @@ api_operations_post_input(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto fail;
}
@@ -1562,7 +1575,7 @@ api_operations_post_output(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto fail;
}
@@ -1599,7 +1612,7 @@ api_operations_post_output(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto fail;
}
@@ -1732,7 +1745,7 @@ api_operations_post(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -1751,7 +1764,7 @@ api_operations_post(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -1762,7 +1775,7 @@ api_operations_post(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -1791,7 +1804,7 @@ api_operations_post(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -1831,7 +1844,7 @@ api_operations_post(clicon_handle h,
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto ok;
}
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -1860,7 +1873,7 @@ api_operations_post(clicon_handle h,
goto done;
/* Local error: return it and quit */
if ((xe = xpath_first(xret, NULL, "rpc-reply/rpc-error")) != NULL){
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -1869,7 +1882,7 @@ api_operations_post(clicon_handle h,
if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0)
goto done;
if ((xe = xpath_first(xret, NULL, "rpc-reply/rpc-error")) != NULL){
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
diff --git a/apps/restconf/restconf_stream.c b/apps/restconf/restconf_stream.c
index 7d825b4a..7ab03e48 100644
--- a/apps/restconf/restconf_stream.c
+++ b/apps/restconf/restconf_stream.c
@@ -269,7 +269,7 @@ restconf_stream(clicon_handle h,
if (clicon_rpc_netconf(h, cbuf_get(cb), &xret, &s) < 0)
goto done;
if ((xe = xpath_first(xret, NULL, "rpc-reply/rpc-error")) != NULL){
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
@@ -417,7 +417,7 @@ api_stream(clicon_handle h,
if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0)
goto done;
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xerr, pretty, use_xml, 0) < 0)
goto done;
goto ok;
}
diff --git a/doc/INSTALL.md b/doc/INSTALL.md
index 6efca64f..4afd8173 100644
--- a/doc/INSTALL.md
+++ b/doc/INSTALL.md
@@ -42,6 +42,51 @@ Docker is used to build Alpine Linux
### Build docker image
## FreeBSD
-### Package install
+
+FreeBSD has ports for both cligen and clixon available.
+You can install them as binary packages, or you can build
+them in a ports source tree locally.
+
+If you install using binary packages or build from the
+ports collection, the installation locations comply
+with FreeBSD standards and you have some assurance
+that the installed package is correct and functional.
+
+The nginx setup for RESTCONF is altered - the system user
+www is used, and the restconf daemon is placed in
+/usr/local/sbin.
+
+### Binary package install
+
+To install the pre-built binary package, use the FreeBSD
+pkg command.
+
+```
+% pkg install clixon
+```
+
+This will install clixon and all the dependencies needed.
+
### Build from source
+If you prefer you can also build clixon from the
+[FreeBSD ports collection](https://www.freebsd.org/doc/handbook/ports-using.html)
+
+Once you have the Ports Collection installed, you build
+clixon like this:
+
+```
+% cd /usr/ports/devel/clixon
+% make && make install
+```
+
+One issue with using the Ports Collection is that it may
+not install the latest version from GitHub. The port is
+generally updated soon after an official release, but there
+is still a lag between it and the master branch. The maintainer
+for the port tries to assure that the master branch will
+compile always, but no FreeBSD specific functional testing
+is done.
+
+
+
diff --git a/lib/clixon/clixon_hash.h b/lib/clixon/clixon_hash.h
index 090af542..6fcca465 100644
--- a/lib/clixon/clixon_hash.h
+++ b/lib/clixon/clixon_hash.h
@@ -44,14 +44,14 @@ struct clicon_hash {
};
typedef struct clicon_hash *clicon_hash_t;
-clicon_hash_t *hash_init (void);
-void hash_free (clicon_hash_t *);
-clicon_hash_t hash_lookup (clicon_hash_t *head, const char *key);
-void *hash_value (clicon_hash_t *head, const char *key, size_t *vlen);
-clicon_hash_t hash_add (clicon_hash_t *head, const char *key, void *val, size_t vlen);
-int hash_del (clicon_hash_t *head, const char *key);
-int hash_dump(clicon_hash_t *head, FILE *f);
-int hash_keys(clicon_hash_t *hash, char ***vector, size_t *nkeys);
+clicon_hash_t *clicon_hash_init (void);
+void clicon_hash_free (clicon_hash_t *);
+clicon_hash_t clicon_hash_lookup (clicon_hash_t *head, const char *key);
+void *clicon_hash_value (clicon_hash_t *head, const char *key, size_t *vlen);
+clicon_hash_t clicon_hash_add (clicon_hash_t *head, const char *key, void *val, size_t vlen);
+int clicon_hash_del (clicon_hash_t *head, const char *key);
+int clicon_hash_dump(clicon_hash_t *head, FILE *f);
+int clicon_hash_keys(clicon_hash_t *hash, char ***vector, size_t *nkeys);
/*
* Macros to iterate over hash contents.
@@ -59,24 +59,23 @@ int hash_keys(clicon_hash_t *hash, char ***vector, size_t *nkeys);
*
* Example:
* char *k;
- * clicon_hash_t *h = hash_init();
+ * clicon_hash_t *h = clicon_hash_init();
*
- * hash_add(h, "colour", "red", 6);
- * hash_add(h, "name", "rudolf" 7);
- * hash_add(h, "species", "reindeer" 9);
+ * clicon_hash_add(h, "colour", "red", 6);
+ * clicon_hash_add(h, "name", "rudolf" 7);
+ * clicon_hash_add(h, "species", "reindeer" 9);
*
- * hash_each(h, k) {
- * printf ("%s = %s\n", k, (char *)hash_value(h, k, NULL));
+ * clicon_hash_each(h, k) {
+ * printf ("%s = %s\n", k, (char *)clicon_hash_value(h, k, NULL));
* } hash_each_end();
*/
-#define hash_each(__hash__, __key__) \
+#define clicon_hash_each(__hash__, __key__) \
{ \
int __i__; \
size_t __n__; \
char **__k__ = hash_keys((__hash__),&__n__); \
if (__k__) { \
for(__i__ = 0; __i__ < __n__ && ((__key__) = __k__[__i__]); __i__++)
-#define hash_each_end(__hash__) if (__k__) free(__k__); } }
-
+#define clicon_hash_each_end(__hash__) if (__k__) free(__k__); } }
#endif /* _CLIXON_HASH_H_ */
diff --git a/lib/clixon/clixon_netconf_lib.h b/lib/clixon/clixon_netconf_lib.h
index f3651da5..83fc2117 100644
--- a/lib/clixon/clixon_netconf_lib.h
+++ b/lib/clixon/clixon_netconf_lib.h
@@ -43,6 +43,7 @@
*/
int netconf_in_use(cbuf *cb, char *type, char *message);
int netconf_invalid_value(cbuf *cb, char *type, char *message);
+int netconf_invalid_value_xml(cxobj **xret, char *type, char *message);
int netconf_too_big(cbuf *cb, char *type, char *message);
int netconf_missing_attribute(cbuf *cb, char *type, char *info, char *message);
int netconf_bad_attribute(cbuf *cb, char *type, char *info, char *message);
diff --git a/lib/clixon/clixon_xpath.h b/lib/clixon/clixon_xpath.h
index f159579d..50856b8a 100644
--- a/lib/clixon/clixon_xpath.h
+++ b/lib/clixon/clixon_xpath.h
@@ -75,31 +75,61 @@ enum axis_type{
A_ROOT /* XXX Not in https://www.w3.org/TR/xpath-10 */
};
-/*
- * Variables
- */
-extern const map_str2int xpopmap[];
+/* used as non-terminal type in yacc rules */
+enum xp_type{
+ XP_EXP,
+ XP_AND,
+ XP_RELEX,
+ XP_ADD,
+ XP_UNION,
+ XP_PATHEXPR,
+ XP_LOCPATH,
+ XP_ABSPATH,
+ XP_RELLOCPATH,
+ XP_STEP,
+ XP_NODE, /* s0 is namespace prefix, s1 is name */
+ XP_NODE_FN,
+ XP_PRED,
+ XP_PRI0,
+ XP_PRIME_NR,
+ XP_PRIME_STR,
+ XP_PRIME_FN,
+};
-extern int xpatherrordiff;
+/*! XPATH Parsing generates a tree of nodes that is later traversed
+ */
+struct xpath_tree{
+ enum xp_type xs_type;
+ int xs_int;
+ double xs_double;
+ char *xs_s0;
+ char *xs_s1;
+ struct xpath_tree *xs_c0; /* child 0 */
+ struct xpath_tree *xs_c1; /* child 1 */
+};
+typedef struct xpath_tree xpath_tree;
/*
* Prototypes
*/
+char* xpath_tree_int2str(int nodetype);
+int xpath_tree_print(cbuf *cb, xpath_tree *xs);
+int xpath_tree_free(xpath_tree *xs);
+int xpath_parse(cvec *nsc, char *xpath, xpath_tree **xptree);
+
#if defined(__GNUC__) && __GNUC__ >= 3
+cxobj *xpath_first(cxobj *xcur, cvec *nsc, char *format, ...) __attribute__ ((format (printf, 3, 4)));
int xpath_vec(cxobj *xcur, cvec *nsc, char *format, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 3, 6)));
int xpath_vec_flag(cxobj *xcur, cvec *nsc, char *format, uint16_t flags,
cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 3, 7)));
-cxobj *xpath_first(cxobj *xcur, cvec *nsc, char *format, ...) __attribute__ ((format (printf, 3, 4)));
int xpath_vec_bool(cxobj *xcur, cvec *nsc, char *format, ...) __attribute__ ((format (printf, 3, 4)));
-
#else
+cxobj *xpath_first(cxobj *xcur, cvec *nsc, char *format, ...);
int xpath_vec(cxobj *xcur, cvec *nsc, char *format, cxobj ***vec, size_t *veclen, ...);
int xpath_vec_flag(cxobj *xcur, cvec *nsc, char *format, uint16_t flags,
cxobj ***vec, size_t *veclen, ...);
-cxobj *xpath_first(cxobj *xcur, cvec *nsc, char *format, ...);
int xpath_vec_bool(cxobj *xcur, cvec *nsc, char *format, ...);
#endif
-
int xpath_vec_ctx(cxobj *xcur, cvec *nsc, char *xpath, xp_ctx **xrp);
#endif /* _CLIXON_XPATH_H */
diff --git a/lib/src/Makefile.in b/lib/src/Makefile.in
index 57c0ee75..ef2c4d21 100644
--- a/lib/src/Makefile.in
+++ b/lib/src/Makefile.in
@@ -73,7 +73,7 @@ SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \
clixon_yang_cardinality.c clixon_xml_changelog.c clixon_xml_nsctx.c \
clixon_hash.c clixon_options.c clixon_data.c clixon_plugin.c \
clixon_proto.c clixon_proto_client.c \
- clixon_xpath.c clixon_xpath_ctx.c clixon_sha1.c \
+ clixon_xpath.c clixon_xpath_ctx.c clixon_xpath_eval.c clixon_sha1.c \
clixon_datastore.c clixon_datastore_write.c clixon_datastore_read.c \
clixon_datastore_tree.c \
clixon_netconf_lib.c clixon_stream.c clixon_nacm.c
diff --git a/lib/src/clixon_data.c b/lib/src/clixon_data.c
index 56871268..cb38c34d 100644
--- a/lib/src/clixon_data.c
+++ b/lib/src/clixon_data.c
@@ -83,7 +83,7 @@ clicon_dbspec_yang(clicon_handle h)
size_t len;
void *p;
- if ((p = hash_value(cdat, "dbspec_yang", &len)) != NULL)
+ if ((p = clicon_hash_value(cdat, "dbspec_yang", &len)) != NULL)
return *(yang_stmt **)p;
return NULL;
}
@@ -100,7 +100,7 @@ clicon_dbspec_yang_set(clicon_handle h,
/* It is the pointer to ys that should be copied by hash,
so we send a ptr to the ptr to indicate what to copy.
*/
- if (hash_add(cdat, "dbspec_yang", &ys, sizeof(ys)) == NULL)
+ if (clicon_hash_add(cdat, "dbspec_yang", &ys, sizeof(ys)) == NULL)
return -1;
return 0;
}
@@ -118,7 +118,7 @@ clicon_nacm_ext(clicon_handle h)
size_t len;
void *p;
- if ((p = hash_value(cdat, "nacm_xml", &len)) != NULL)
+ if ((p = clicon_hash_value(cdat, "nacm_xml", &len)) != NULL)
return *(cxobj **)p;
return NULL;
}
@@ -141,7 +141,7 @@ clicon_nacm_ext_set(clicon_handle h,
/* It is the pointer to xn that should be copied by hash,
so we send a ptr to the ptr to indicate what to copy.
*/
- if (hash_add(cdat, "nacm_xml", &xn, sizeof(xn)) == NULL)
+ if (clicon_hash_add(cdat, "nacm_xml", &xn, sizeof(xn)) == NULL)
return -1;
return 0;
}
@@ -158,7 +158,7 @@ clicon_config_yang(clicon_handle h)
size_t len;
void *p;
- if ((p = hash_value(cdat, "control_yang", &len)) != NULL)
+ if ((p = clicon_hash_value(cdat, "control_yang", &len)) != NULL)
return *(yang_stmt **)p;
return NULL;
}
@@ -175,7 +175,7 @@ clicon_config_yang_set(clicon_handle h,
/* It is the pointer to ys that should be copied by hash,
so we send a ptr to the ptr to indicate what to copy.
*/
- if (hash_add(cdat, "control_yang", &ys, sizeof(ys)) == NULL)
+ if (clicon_hash_add(cdat, "control_yang", &ys, sizeof(ys)) == NULL)
return -1;
return 0;
}
@@ -192,7 +192,7 @@ clicon_conf_xml(clicon_handle h)
size_t len;
void *p;
- if ((p = hash_value(cdat, "clixon_conf", &len)) != NULL)
+ if ((p = clicon_hash_value(cdat, "clixon_conf", &len)) != NULL)
return *(cxobj **)p;
return NULL;
}
@@ -209,7 +209,7 @@ clicon_conf_xml_set(clicon_handle h,
/* It is the pointer to x that should be copied by hash,
* so we send a ptr to the ptr to indicate what to copy.
*/
- if (hash_add(cdat, "clixon_conf", &x, sizeof(x)) == NULL)
+ if (clicon_hash_add(cdat, "clixon_conf", &x, sizeof(x)) == NULL)
return -1;
return 0;
}
@@ -223,7 +223,7 @@ clicon_username_get(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
- return (char*)hash_value(cdat, "username", NULL);
+ return (char*)clicon_hash_value(cdat, "username", NULL);
}
/*! Set authorized user name
@@ -238,8 +238,8 @@ clicon_username_set(clicon_handle h,
clicon_hash_t *cdat = clicon_data(h);
if (username == NULL)
- return hash_del(cdat, "username");
- return hash_add(cdat, "username", username, strlen(username)+1)==NULL?-1:0;
+ return clicon_hash_del(cdat, "username");
+ return clicon_hash_add(cdat, "username", username, strlen(username)+1)==NULL?-1:0;
}
/*! Get backend daemon startup status
@@ -252,7 +252,7 @@ clicon_startup_status_get(clicon_handle h)
clicon_hash_t *cdat = clicon_data(h);
void *p;
- if ((p = hash_value(cdat, "startup_status", NULL)) != NULL)
+ if ((p = clicon_hash_value(cdat, "startup_status", NULL)) != NULL)
return *(enum startup_status *)p;
return STARTUP_ERR;
}
@@ -268,7 +268,7 @@ clicon_startup_status_set(clicon_handle h,
enum startup_status status)
{
clicon_hash_t *cdat = clicon_data(h);
- if (hash_add(cdat, "startup_status", &status, sizeof(status))==NULL)
+ if (clicon_hash_add(cdat, "startup_status", &status, sizeof(status))==NULL)
return -1;
return 0;
}
@@ -284,7 +284,7 @@ clicon_socket_get(clicon_handle h)
clicon_hash_t *cdat = clicon_data(h);
void *p;
- if ((p = hash_value(cdat, "socket", NULL)) == NULL)
+ if ((p = clicon_hash_value(cdat, "socket", NULL)) == NULL)
return -1;
return *(int*)p;
}
@@ -302,8 +302,8 @@ clicon_socket_set(clicon_handle h,
clicon_hash_t *cdat = clicon_data(h);
if (s == -1)
- return hash_del(cdat, "socket");
- return hash_add(cdat, "socket", &s, sizeof(int))==NULL?-1:0;
+ return clicon_hash_del(cdat, "socket");
+ return clicon_hash_add(cdat, "socket", &s, sizeof(int))==NULL?-1:0;
}
/*! Get module state cache
@@ -319,7 +319,7 @@ clicon_modst_cache_get(clicon_handle h,
clicon_hash_t *cdat = clicon_data(h);
void *p;
- if ((p = hash_value(cdat, brief?"modst_brief":"modst_full", NULL)) != NULL)
+ if ((p = clicon_hash_value(cdat, brief?"modst_brief":"modst_full", NULL)) != NULL)
return *(cxobj **)p;
return NULL;
}
@@ -346,7 +346,7 @@ clicon_modst_cache_set(clicon_handle h,
assert(strcmp(xml_name(xms),"modules-state")==0);
if ((x = xml_dup(xms)) == NULL)
return -1;
- if (hash_add(cdat, brief?"modst_brief":"modst_full", &x, sizeof(x))==NULL)
+ if (clicon_hash_add(cdat, brief?"modst_brief":"modst_full", &x, sizeof(x))==NULL)
return -1;
ok:
return 0;
@@ -363,7 +363,7 @@ clicon_xml_changelog_get(clicon_handle h)
clicon_hash_t *cdat = clicon_data(h);
void *p;
- if ((p = hash_value(cdat, "xml-changelog", NULL)) != NULL)
+ if ((p = clicon_hash_value(cdat, "xml-changelog", NULL)) != NULL)
return *(cxobj **)p;
return NULL;
}
@@ -381,7 +381,7 @@ clicon_xml_changelog_set(clicon_handle h,
{
clicon_hash_t *cdat = clicon_data(h);
- if (hash_add(cdat, "xml-changelog", &xchlog, sizeof(xchlog))==NULL)
+ if (clicon_hash_add(cdat, "xml-changelog", &xchlog, sizeof(xchlog))==NULL)
return -1;
return 0;
}
@@ -403,12 +403,12 @@ clicon_argv_get(clicon_handle h,
void *p;
if (argc){
- if ((p = hash_value(cdat, "argc", NULL)) == NULL)
+ if ((p = clicon_hash_value(cdat, "argc", NULL)) == NULL)
return -1;
*argc = *(int*)p;
}
if (argv){
- if ((p = hash_value(cdat, "argv", NULL)) == NULL)
+ if ((p = clicon_hash_value(cdat, "argv", NULL)) == NULL)
return -1;
*argv = (char**)p;
}
@@ -444,10 +444,10 @@ clicon_argv_set(clicon_handle h,
memcpy(argvv+1, argv, argc*sizeof(char*));
argvv[0] = prgm;
/* Note the value is the argv vector (which is copied) */
- if (hash_add(cdat, "argv", argvv, len*sizeof(char*))==NULL)
+ if (clicon_hash_add(cdat, "argv", argvv, len*sizeof(char*))==NULL)
goto done;
argc += 1;
- if (hash_add(cdat, "argc", &argc, sizeof(argc))==NULL)
+ if (clicon_hash_add(cdat, "argc", &argc, sizeof(argc))==NULL)
goto done;
retval = 0;
done:
@@ -470,7 +470,7 @@ clicon_db_elmnt_get(clicon_handle h,
clicon_hash_t *cdat = clicon_db_elmnt(h);
void *p;
- if ((p = hash_value(cdat, db, NULL)) != NULL)
+ if ((p = clicon_hash_value(cdat, db, NULL)) != NULL)
return (db_elmnt *)p;
return NULL;
}
@@ -491,7 +491,7 @@ clicon_db_elmnt_set(clicon_handle h,
{
clicon_hash_t *cdat = clicon_db_elmnt(h);
- if (hash_add(cdat, db, de, sizeof(*de))==NULL)
+ if (clicon_hash_add(cdat, db, de, sizeof(*de))==NULL)
return -1;
return 0;
}
diff --git a/lib/src/clixon_datastore.c b/lib/src/clixon_datastore.c
index d2f58762..a7becb1c 100644
--- a/lib/src/clixon_datastore.c
+++ b/lib/src/clixon_datastore.c
@@ -161,10 +161,10 @@ xmldb_disconnect(clicon_handle h)
int i;
db_elmnt *de;
- if (hash_keys(clicon_db_elmnt(h), &keys, &klen) < 0)
+ if (clicon_hash_keys(clicon_db_elmnt(h), &keys, &klen) < 0)
goto done;
for(i = 0; i < klen; i++)
- if ((de = hash_value(clicon_db_elmnt(h), keys[i], NULL)) != NULL){
+ if ((de = clicon_hash_value(clicon_db_elmnt(h), keys[i], NULL)) != NULL){
if (de->de_xml){
xml_free(de->de_xml);
de->de_xml = NULL;
@@ -311,7 +311,7 @@ xmldb_unlock_all(clicon_handle h,
int i;
db_elmnt *de;
- if (hash_keys(clicon_db_elmnt(h), &keys, &klen) < 0)
+ if (clicon_hash_keys(clicon_db_elmnt(h), &keys, &klen) < 0)
goto done;
for (i = 0; i < klen; i++)
if ((de = clicon_db_elmnt_get(h, keys[i])) != NULL &&
diff --git a/lib/src/clixon_datastore_read.c b/lib/src/clixon_datastore_read.c
index ca5cb90a..966d2bc4 100644
--- a/lib/src/clixon_datastore_read.c
+++ b/lib/src/clixon_datastore_read.c
@@ -596,10 +596,6 @@ xmldb_get_zerocopy(clicon_handle h,
db_elmnt *de = NULL;
db_elmnt de0 = {0,};
- if (!clicon_option_bool(h, "CLICON_XMLDB_CACHE")){
- clicon_err(OE_CFG, 0, "CLICON_XMLDB_CACHE must be set");
- goto done;
- }
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done;
diff --git a/lib/src/clixon_handle.c b/lib/src/clixon_handle.c
index 5d91c7c8..b4b5a303 100644
--- a/lib/src/clixon_handle.c
+++ b/lib/src/clixon_handle.c
@@ -111,15 +111,15 @@ clicon_handle_init0(int size)
}
memset(ch, 0, size);
ch->ch_magic = CLICON_MAGIC;
- if ((ch->ch_copt = hash_init()) == NULL){
+ if ((ch->ch_copt = clicon_hash_init()) == NULL){
clicon_handle_exit((clicon_handle)ch);
goto done;
}
- if ((ch->ch_data = hash_init()) == NULL){
+ if ((ch->ch_data = clicon_hash_init()) == NULL){
clicon_handle_exit((clicon_handle)ch);
goto done;
}
- if ((ch->ch_db_elmnt = hash_init()) == NULL){
+ if ((ch->ch_db_elmnt = clicon_hash_init()) == NULL){
clicon_handle_exit((clicon_handle)ch);
goto done;
}
@@ -154,12 +154,12 @@ clicon_handle_exit(clicon_handle h)
clicon_hash_t *ha;
if ((ha = clicon_options(h)) != NULL)
- hash_free(ha);
+ clicon_hash_free(ha);
if ((ha = clicon_data(h)) != NULL)
- hash_free(ha);
+ clicon_hash_free(ha);
if ((ha = clicon_db_elmnt(h)) != NULL)
- hash_free(ha);
+ clicon_hash_free(ha);
stream_delete_all(h, 1);
free(ch);
retval = 0;
diff --git a/lib/src/clixon_hash.c b/lib/src/clixon_hash.c
index b14e10ad..f757743b 100644
--- a/lib/src/clixon_hash.c
+++ b/lib/src/clixon_hash.c
@@ -115,7 +115,7 @@ hash_bucket(const char *str)
* @see hash_free For freeing the hash-table
*/
clicon_hash_t *
-hash_init(void)
+clicon_hash_init(void)
{
clicon_hash_t *hash;
@@ -133,7 +133,7 @@ hash_init(void)
* @retval void
*/
void
-hash_free(clicon_hash_t *hash)
+clicon_hash_free(clicon_hash_t *hash)
{
int i;
clicon_hash_t tmp;
@@ -157,8 +157,8 @@ hash_free(clicon_hash_t *hash)
* @retval NULL Not found
*/
clicon_hash_t
-hash_lookup(clicon_hash_t *hash,
- const char *key)
+clicon_hash_lookup(clicon_hash_t *hash,
+ const char *key)
{
uint32_t bkt;
clicon_hash_t h;
@@ -183,13 +183,13 @@ hash_lookup(clicon_hash_t *hash,
* @retval NULL Key not found or value NULL
*/
void *
-hash_value(clicon_hash_t *hash,
- const char *key,
- size_t *vlen)
+clicon_hash_value(clicon_hash_t *hash,
+ const char *key,
+ size_t *vlen)
{
clicon_hash_t h;
- h = hash_lookup(hash, key);
+ h = clicon_hash_lookup(hash, key);
if (h == NULL)
return NULL; /* OK, key not found */
@@ -209,10 +209,10 @@ hash_value(clicon_hash_t *hash,
* @note special case val is NULL and vlen==0
*/
clicon_hash_t
-hash_add(clicon_hash_t *hash,
- const char *key,
- void *val,
- size_t vlen)
+clicon_hash_add(clicon_hash_t *hash,
+ const char *key,
+ void *val,
+ size_t vlen)
{
void *newval = NULL;
clicon_hash_t h;
@@ -225,7 +225,7 @@ hash_add(clicon_hash_t *hash,
goto catch;
}
/* If variable exist, don't allocate a new. just replace value */
- h = hash_lookup(hash, key);
+ h = clicon_hash_lookup(hash, key);
if (h == NULL) {
if ((new = (clicon_hash_t)malloc(sizeof(*new))) == NULL){
clicon_err(OE_UNIX, errno, "malloc: %s", strerror(errno));
@@ -283,12 +283,12 @@ catch:
* @retval -1 Key not found
*/
int
-hash_del(clicon_hash_t *hash,
- const char *key)
+clicon_hash_del(clicon_hash_t *hash,
+ const char *key)
{
clicon_hash_t h;
- h = hash_lookup(hash, key);
+ h = clicon_hash_lookup(hash, key);
if (h == NULL)
return -1;
@@ -311,9 +311,9 @@ hash_del(clicon_hash_t *hash,
* @note: vector needs to be deallocated with free
*/
int
-hash_keys(clicon_hash_t *hash,
- char ***vector,
- size_t *nkeys)
+clicon_hash_keys(clicon_hash_t *hash,
+ char ***vector,
+ size_t *nkeys)
{
int retval = -1;
int bkt;
@@ -357,8 +357,8 @@ catch:
* @retval -1 Error
*/
int
-hash_dump(clicon_hash_t *hash,
- FILE *f)
+clicon_hash_dump(clicon_hash_t *hash,
+ FILE *f)
{
int retval = -1;
int i;
@@ -369,10 +369,10 @@ hash_dump(clicon_hash_t *hash,
if (hash == NULL)
goto ok;
- if (hash_keys(hash, &keys, &klen) < 0)
+ if (clicon_hash_keys(hash, &keys, &klen) < 0)
goto done;
for(i = 0; i < klen; i++) {
- val = hash_value(hash, keys[i], &vlen);
+ val = clicon_hash_value(hash, keys[i], &vlen);
printf("%s =\t 0x%p , length %zu\n", keys[i], val, vlen);
}
diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c
index 42d9f382..2db4e3ea 100644
--- a/lib/src/clixon_netconf_lib.c
+++ b/lib/src/clixon_netconf_lib.c
@@ -108,6 +108,48 @@ netconf_in_use(cbuf *cb,
goto done;
}
+/*! Create Netconf invalid-value error XML tree according to RFC 6241 Appendix A
+ *
+ * The request specifies an unacceptable value for one or more parameters.
+ * @param[out] xret Error XML tree. Free with xml_free after use
+ * @param[in] type Error type: "application" or "protocol"
+ * @param[in] message Error message (will be XML encoded)
+ */
+int
+netconf_invalid_value_xml(cxobj **xret,
+ char *type,
+ char *message)
+{
+ int retval =-1;
+ cxobj *xerr;
+ char *encstr = NULL;
+
+ if (*xret == NULL){
+ if ((*xret = xml_new("rpc-reply", NULL, NULL)) == NULL)
+ goto done;
+ }
+ else if (xml_name_set(*xret, "rpc-reply") < 0)
+ goto done;
+ if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL)
+ goto done;
+ if (xml_parse_va(&xerr, NULL, "%s"
+ "invalid-value"
+ "error", type) < 0)
+ goto done;
+ if (message){
+ if (xml_chardata_encode(&encstr, "%s", message) < 0)
+ goto done;
+ if (xml_parse_va(&xerr, NULL, "%s",
+ encstr) < 0)
+ goto done;
+ }
+ retval = 0;
+ done:
+ if (encstr)
+ free(encstr);
+ return retval;
+}
+
/*! Create Netconf invalid-value error XML tree according to RFC 6241 Appendix A
*
* The request specifies an unacceptable value for one or more parameters.
@@ -120,6 +162,20 @@ netconf_invalid_value(cbuf *cb,
char *type,
char *message)
{
+#if 1
+ int retval = -1;
+ cxobj *xret = NULL;
+
+ if (netconf_invalid_value_xml(&xret, type, message) < 0)
+ goto done;
+ if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
+ goto done;
+ retval = 0;
+ done:
+ if (xret)
+ xml_free(xret);
+ return retval;
+#else
int retval = -1;
char *encstr = NULL;
@@ -145,6 +201,7 @@ netconf_invalid_value(cbuf *cb,
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
+#endif
}
/*! Create Netconf too-big error XML tree according to RFC 6241 Appendix A
diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c
index 1986d684..4c7ee2d1 100644
--- a/lib/src/clixon_options.c
+++ b/lib/src/clixon_options.c
@@ -112,10 +112,10 @@ clicon_option_dump(clicon_handle h,
size_t vlen;
cxobj *x = NULL;
- if (hash_keys(hash, &keys, &klen) < 0)
+ if (clicon_hash_keys(hash, &keys, &klen) < 0)
goto done;
for(i = 0; i < klen; i++) {
- val = hash_value(hash, keys[i], &vlen);
+ val = clicon_hash_value(hash, keys[i], &vlen);
if (vlen){
if (((char*)val)[vlen-1]=='\0') /* assume string */
clicon_debug(dbglevel, "%s =\t \"%s\"", keys[i], (char*)val);
@@ -234,7 +234,7 @@ parse_configfile(clicon_handle h,
/* Used as an arg to this fn */
if (strcmp(name,"CLICON_CONFIGFILE")==0)
continue;
- if (hash_add(copt,
+ if (clicon_hash_add(copt,
name,
body,
strlen(body)+1) == NULL)
@@ -284,7 +284,7 @@ clicon_option_add(clicon_handle h,
name, value, name) < 0)
goto done;
}
- if (hash_add(copt,
+ if (clicon_hash_add(copt,
name,
value,
strlen(value)+1) == NULL)
@@ -319,10 +319,10 @@ clicon_options_main(clicon_handle h,
/*
* Set configure file if not set by command-line above
*/
- if (!hash_lookup(copt, "CLICON_CONFIGFILE")){
+ if (!clicon_hash_lookup(copt, "CLICON_CONFIGFILE")){
clicon_option_str_set(h, "CLICON_CONFIGFILE", CLIXON_DEFAULT_CONFIG);
}
- configfile = hash_value(copt, "CLICON_CONFIGFILE", NULL);
+ configfile = clicon_hash_value(copt, "CLICON_CONFIGFILE", NULL);
clicon_debug(1, "CLICON_CONFIGFILE=%s", configfile);
/* File must end with .xml */
if ((suffix = rindex(configfile, '.')) != NULL){
@@ -385,7 +385,7 @@ clicon_option_exists(clicon_handle h,
{
clicon_hash_t *copt = clicon_options(h);
- return (hash_lookup(copt, (char*)name) != NULL);
+ return (clicon_hash_lookup(copt, (char*)name) != NULL);
}
/*! Get a single string option string via handle
@@ -404,9 +404,9 @@ clicon_option_str(clicon_handle h,
{
clicon_hash_t *copt = clicon_options(h);
- if (hash_lookup(copt, (char*)name) == NULL)
+ if (clicon_hash_lookup(copt, (char*)name) == NULL)
return NULL;
- return hash_value(copt, (char*)name, NULL);
+ return clicon_hash_value(copt, (char*)name, NULL);
}
/*! Set a single string option via handle
@@ -423,7 +423,7 @@ clicon_option_str_set(clicon_handle h,
{
clicon_hash_t *copt = clicon_options(h);
- return hash_add(copt, (char*)name, val, strlen(val)+1)==NULL?-1:0;
+ return clicon_hash_add(copt, (char*)name, val, strlen(val)+1)==NULL?-1:0;
}
/*! Get options as integer but stored as string
@@ -518,7 +518,7 @@ clicon_option_del(clicon_handle h,
{
clicon_hash_t *copt = clicon_options(h);
- return hash_del(copt, (char*)name);
+ return clicon_hash_del(copt, (char*)name);
}
/*-----------------------------------------------------------------
diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c
index 89bbf0c2..08c13588 100644
--- a/lib/src/clixon_xml_map.c
+++ b/lib/src/clixon_xml_map.c
@@ -343,11 +343,12 @@ validate_identityref(cxobj *xt,
{
int retval = -1;
- char *node;
+ char *node = NULL;
yang_stmt *ybaseref; /* This is the type's base reference */
yang_stmt *ybaseid;
char *prefix = NULL;
cbuf *cb = NULL;
+ cbuf *cb2 = NULL;
/* Get idref value. Then see if this value is derived from ytype.
* Always add default prefix because derived identifiers are stored with
@@ -380,10 +381,13 @@ validate_identityref(cxobj *xt,
* The derived node list is a cvec computed XXX
*/
if (cvec_find(yang_cvec_get(ybaseid), node) == NULL){
- cbuf_reset(cb);
- cprintf(cb, "Identityref validation failed, %s not derived from %s",
+ if ((cb2 = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
+ goto done;
+ }
+ cprintf(cb2, "Identityref validation failed, %s not derived from %s",
node, yang_argument_get(ybaseid));
- if (netconf_operation_failed_xml(xret, "application", cbuf_get(cb)) < 0)
+ if (netconf_operation_failed_xml(xret, "application", cbuf_get(cb2)) < 0)
goto done;
goto fail;
}
@@ -391,6 +395,8 @@ validate_identityref(cxobj *xt,
done:
if (cb)
cbuf_free(cb);
+ if (cb2)
+ cbuf_free(cb2);
return retval;
fail:
retval = 0;
@@ -1221,19 +1227,20 @@ xml_yang_validate_all(clicon_handle h,
/* Special case if leaf is leafref, then first check against
current xml tree
*/
- if ((yc = yang_find(ys, Y_TYPE, NULL)) != NULL){
- if (strcmp(yc->ys_argument, "leafref") == 0){
- if ((ret = validate_leafref(xt, yc, xret)) < 0)
- goto done;
- if (ret == 0)
- goto fail;
- }
- else if (strcmp(yc->ys_argument, "identityref") == 0){
- if ((ret = validate_identityref(xt, ys, yc, xret)) < 0)
- goto done;
- if (ret == 0)
- goto fail;
+ /* Get base type yc */
+ if (yang_type_get(ys, NULL, &yc, NULL, NULL, NULL, NULL, NULL) < 0)
+ goto done;
+ if (strcmp(yang_argument_get(yc), "leafref") == 0){
+ if ((ret = validate_leafref(xt, yc, xret)) < 0)
+ goto done;
+ if (ret == 0)
+ goto fail;
}
+ else if (strcmp(yang_argument_get(yc), "identityref") == 0){
+ if ((ret = validate_identityref(xt, ys, yc, xret)) < 0)
+ goto done;
+ if (ret == 0)
+ goto fail;
}
break;
default:
@@ -2557,7 +2564,7 @@ api_path2xml_vec(char **vec,
else{
if ((valvec = clicon_strsep(restval, ",", &nvalvec)) == NULL)
goto done;
- if (nvalvec != cvec_len(cvk)){
+ if ((nvalvec != cvec_len(cvk)) && strict){
clicon_err(OE_XML, EINVAL, "List key %s length mismatch", name);
goto fail;
}
diff --git a/lib/src/clixon_xpath.c b/lib/src/clixon_xpath.c
index 2eec425d..e6d99843 100644
--- a/lib/src/clixon_xpath.c
+++ b/lib/src/clixon_xpath.c
@@ -52,36 +52,8 @@
* Then you need to fix API functions and this is the real work:
* - Replace all existing functions or create new?
* - Expose explicit namespace parameter, or xml object, or default namespace?
- *
- * On namespaces and xpath
- * =======================
- * XPATHs may contain prefixes such as /if:a/if:b
- * XPATHs excecutes in a "namespace context" (nsc)
- * The namespace context is either:
- * (1) the same as the xml that is evaluated, typical for basic XML, or
- * (2) separate from the XML that is evaluated. typical netconf and yang.
- * 1. Same nsc as XML
- * This happens in base XML (not yang), where the nsc is given implicitly by
- * the XML being evaluated. In node comparisons (eg of ip:a) only name and
- * prefixes are compared.
- * XML:
- * XPATH: /if:a/ip:b
- * When you call an xpath function, then call it with nsc=NULL.
- * 2. Separate nsc.
- * This happens if the namespace context is independent from the XML. It can
- * happen for example in NETCONF GET using :xpath when the XML is not known
- * so that xpath and xml may use different prefixes for the same namespace.
- * In that case you cannot rely on the prefix but must compare namespaces.
- * The namespace context of the XML is given (by the XML), but the xpath
- * nsc must then be explicitly given in the xpath call.
- * Example:
- * XML:
- * NETCONF: int */
-const map_str2int xpopmap[] = {
- {"and", XO_AND},
- {"or", XO_OR},
- {"div", XO_DIV},
- {"mod", XO_MOD},
- {"+", XO_ADD},
- {"*", XO_MULT},
- {"-", XO_SUB},
- {"=", XO_EQ},
- {"!=", XO_NE},
- {">=", XO_GE},
- {"<=", XO_LE},
- {"<", XO_LT},
- {">", XO_GT},
- {"|", XO_UNION},
- {NULL, -1}
-};
+/* Mapping between xpath_tree node name string <--> int */
static const map_str2int xpath_tree_map[] = {
{"expr", XP_EXP},
{"andexpr", XP_AND},
@@ -163,16 +116,26 @@ static const map_str2int xpath_tree_map[] = {
{NULL, -1}
};
+
/*
* XPATH parse tree type
*/
+
+/*! Map from xpath_tree node name int to string
+ */
+char*
+xpath_tree_int2str(int nodetype)
+{
+ return (char*)clicon_int2str(xpath_tree_map, nodetype);
+}
+
/*! Print XPATH parse tree */
static int
xpath_tree_print0(cbuf *cb,
xpath_tree *xs,
int level)
{
- cprintf(cb, "%*s%s:", level*3, "", clicon_int2str(xpath_tree_map, xs->xs_type));
+ cprintf(cb, "%*s%s:", level*3, "", xpath_tree_int2str(xs->xs_type));
if (xs->xs_s0){
cprintf(cb, "\"%s\" ", xs->xs_s0);
if (xs->xs_s1)
@@ -186,7 +149,11 @@ xpath_tree_print0(cbuf *cb,
return 0;
}
-static int
+/*! Print a xpath_tree
+ * @param[out] cb CLIgen buffer
+ * @param[in] xs XPATH tree
+ */
+int
xpath_tree_print(cbuf *cb,
xpath_tree *xs)
{
@@ -194,7 +161,11 @@ xpath_tree_print(cbuf *cb,
return 0;
}
-static int
+/*! Free a xpath_tree
+ * @param[in] xs XPATH tree
+ * @see xpath_parse creates a xpath_tree
+ */
+int
xpath_tree_free(xpath_tree *xs)
{
if (xs->xs_s0)
@@ -209,971 +180,12 @@ xpath_tree_free(xpath_tree *xs)
return 0;
}
-/*!
- * @retval -1 Error XXX: retval -1 not properly handled
- * @retval 0 No match
- * @retval 1 Match
- */
-static int
-nodetest_eval_node(cxobj *x,
- xpath_tree *xs,
- cvec *nsc)
-{
- int retval = -1;
- char *name1 = xml_name(x);
- char *prefix1 = xml_prefix(x);
- char *nsxml = NULL; /* xml body namespace */
- char *nsxpath = NULL; /* xpath context namespace */
- char *prefix2 = NULL;
- char *name2 = NULL;
-
- /* Namespaces is s0, name is s1 */
- if (strcmp(xs->xs_s1, "*")==0)
- return 1;
- /* get namespace of xml tree */
- if (xml2ns(x, prefix1, &nsxml) < 0)
- goto done;
- prefix2 = xs->xs_s0;
- name2 = xs->xs_s1;
- /* Before going into namespaces, check name equality and filter out noteq */
- if (strcmp(name1, name2) != 0){
- retval = 0; /* no match */
- goto done;
- }
- /* here names are equal
- * Now look for namespaces
- * 1) prefix1 and prefix2 point to same namespace <<-- try this first
- * 2) prefix1 is equal to prefix2 <<-- then try this
- * (1) is strict yang xml
- * (2) without yang
- */
- if (nsc != NULL) { /* solution (1) */
- nsxpath = xml_nsctx_get(nsc, prefix2);
- if (nsxml != NULL && nsxpath != NULL)
- retval = (strcmp(nsxml, nsxpath) == 0);
- else
- retval = (nsxml == nsxpath); /* True only if both are NULL */
- }
- else{ /* solution (2) */
- if (prefix1 == NULL && prefix2 == NULL)
- retval = 1;
- else if (prefix1 == NULL || prefix2 == NULL)
- retval = 0;
- else
- retval = strcmp(prefix1, prefix2) == 0;
- }
- /* If retval == 0 here, then there is name match, but not ns match */
- if (retval == 0){
- fprintf(stderr, "%s NOMATCH xml: (%s)%s\n\t\t xpath: (%s)%s\n", __FUNCTION__,
- name1, nsxml,
- name2, nsxpath);
- if (xpatherrordiff)
- assert(retval == 1);
- }
- done:
- return retval;
-}
-
-/*! Make a nodetest
- * @param[in] xs XPATH stack of type XP_NODE or XP_NODE_FN
- * @param[in] nsc XML Namespace context
- * @retval -1 Error
- * @retval 0 No match
- * @retval 1 Match
- * - node() is true for any node of any type whatsoever.
- * - text() is true for any text node.
- */
-static int
-nodetest_eval(cxobj *x,
- xpath_tree *xs,
- cvec *nsc)
-{
- int retval = 0; /* NB: no match is default (not error) */
- char *fn;
-
- if (xs->xs_type == XP_NODE)
- retval = nodetest_eval_node(x, xs, nsc);
- else if (xs->xs_type == XP_NODE_FN){
- fn = xs->xs_s0;
- if (strcmp(fn, "node")==0)
- retval = 1;
- else if (strcmp(fn, "text")==0)
- retval = 1;
- }
- /* note, retval set by previous statement */
- return retval;
-}
-
-/*!
- * @param[in] xn
- * @param[in] nodetest XPATH stack
- * @param[in] node_type
- * @param[in] flags
- * @param[in] nsc XML Namespace context
- * @param[out] vec0
- * @param[out] vec0len
- */
-int
-nodetest_recursive(cxobj *xn,
- xpath_tree *nodetest,
- int node_type,
- uint16_t flags,
- cvec *nsc,
- cxobj ***vec0,
- size_t *vec0len)
-{
- int retval = -1;
- cxobj *xsub;
- cxobj **vec = *vec0;
- size_t veclen = *vec0len;
-
- xsub = NULL;
- while ((xsub = xml_child_each(xn, xsub, node_type)) != NULL) {
- if (nodetest_eval(xsub, nodetest, nsc) == 1){
- clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xsub, flags));
- if (flags==0x0 || xml_flag(xsub, flags))
- if (cxvec_append(xsub, &vec, &veclen) < 0)
- goto done;
- // continue; /* Dont go deeper */
- }
- if (nodetest_recursive(xsub, nodetest, node_type, flags, nsc, &vec, &veclen) < 0)
- goto done;
- }
- retval = 0;
- *vec0 = vec;
- *vec0len = veclen;
- done:
- return retval;
-}
-
-static int xp_eval(xp_ctx *xc, xpath_tree *xs, cvec *nsc, xp_ctx **xrp);
-
-/*! Evaluate xpath step rule of an XML tree
- *
- * @param[in] xc0 Incoming context
- * @param[in] xs XPATH node tree
- * @param[in] nsc XML Namespace context
- * @param[out] xrp Resulting context
- *
- * - A node test that is a QName is true if and only if the type of the node (see [5 Data Model])
- * is the principal node type and has an expanded-name equal to the expanded-name specified by the QName.
- * - A node test * is true for any node of the principal node type.
- * - node() is true for any node of any type whatsoever.
- * - text() is true for any text node.
- */
-static int
-xp_eval_step(xp_ctx *xc0,
- xpath_tree *xs,
- cvec *nsc,
- xp_ctx **xrp)
-{
- int retval = -1;
- int i;
- cxobj *x;
- cxobj *xv;
- cxobj *xp;
- cxobj **vec = NULL;
- size_t veclen = 0;
- xpath_tree *nodetest = xs->xs_c0;
- xp_ctx *xc = NULL;
-
- /* Create new xc */
- if ((xc = ctx_dup(xc0)) == NULL)
- goto done;
- switch (xs->xs_int){
- case A_ANCESTOR:
- break;
- case A_ANCESTOR_OR_SELF:
- break;
- case A_ATTRIBUTE: /* principal node type is attribute */
- break;
- case A_CHILD:
- if (xc->xc_descendant){
- for (i=0; ixc_size; i++){
- xv = xc->xc_nodeset[i];
- if (nodetest_recursive(xv, nodetest, CX_ELMNT, 0x0, nsc, &vec, &veclen) < 0)
- goto done;
- }
- xc->xc_descendant = 0;
- }
- else{
- if (nodetest->xs_type==XP_NODE_FN &&
- nodetest->xs_s0 &&
- strcmp(nodetest->xs_s0,"current")==0){
- if (cxvec_append(xc->xc_initial, &vec, &veclen) < 0)
- goto done;
- }
- else for (i=0; ixc_size; i++){
- xv = xc->xc_nodeset[i];
- x = NULL;
- while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
- /* xs->xs_c0 is nodetest */
- if (nodetest == NULL || nodetest_eval(x, nodetest, nsc) == 1)
- if (cxvec_append(x, &vec, &veclen) < 0)
- goto done;
- }
- }
- }
- ctx_nodeset_replace(xc, vec, veclen);
- break;
- case A_DESCENDANT:
- case A_DESCENDANT_OR_SELF:
- for (i=0; ixc_size; i++){
- xv = xc->xc_nodeset[i];
- if (nodetest_recursive(xv, xs->xs_c0, CX_ELMNT, 0x0, nsc, &vec, &veclen) < 0)
- goto done;
- }
- ctx_nodeset_replace(xc, vec, veclen);
- break;
- case A_FOLLOWING:
- break;
- case A_FOLLOWING_SIBLING:
- break;
- case A_NAMESPACE: /* principal node type is namespace */
- break;
- case A_PARENT:
- veclen = xc->xc_size;
- vec = xc->xc_nodeset;
- xc->xc_size = 0;
- xc->xc_nodeset = NULL;
- for (i=0; ixc_nodeset, &xc->xc_size) < 0)
- goto done;
- }
- if (vec){
- free(vec);
- vec = NULL;
- }
- break;
- case A_PRECEEDING:
- break;
- case A_PRECEEDING_SIBLING:
- break;
- case A_SELF:
- break;
- default:
- clicon_err(OE_XML, 0, "No such axisname: %d", xs->xs_int);
- goto done;
- break;
- }
- if (xs->xs_c1){
- if (xp_eval(xc, xs->xs_c1, nsc, xrp) < 0)
- goto done;
- }
- else{
- *xrp = xc;
- xc = NULL;
- }
- assert(*xrp);
- retval = 0;
- done:
- if (xc)
- ctx_free(xc);
- return retval;
-}
-
-/*! Evaluate xpath predicates rule
- *
- * pred -> pred expr
- * @param[in] xc Incoming context
- * @param[in] xs XPATH node tree
- * @param[in] nsc XML Namespace context
- * @param[out] xrp Resulting context
- *
- * A predicate filters a node-set with respect to an axis to produce a new
- * node-set. For each node in the node-set to be filtered, the PredicateExpr is
- * evaluated with that node as the context node, with the number of nodes in
- * the node-set as the context size, and with the proximity position of the node
- * in the node-set with respect to the axis as the context position; if
- * PredicateExpr evaluates to true for that node, the node is included in the
- * new node-set; otherwise, it is not included.
- * A PredicateExpr is evaluated by evaluating the Expr and converting the result
- * to a boolean. If the result is a
- * - number, the result will be converted to true if the number is equal to the
- * context position and will be converted to false otherwise;
- * - if the result is not a number, then the result will be converted as if by a
- * call to the boolean function.
- * Thus a location path para[3] is equivalent to para[position()=3].
- */
-static int
-xp_eval_predicate(xp_ctx *xc,
- xpath_tree *xs,
- cvec *nsc,
- xp_ctx **xrp)
-{
- int retval = -1;
- xp_ctx *xr0 = NULL;
- xp_ctx *xr1 = NULL;
- xp_ctx *xrc = NULL;
- int i;
- cxobj *x;
- xp_ctx *xcc;
-
- if (xs->xs_c0 == NULL){ /* empty */
- if ((xr0 = ctx_dup(xc)) == NULL)
- goto done;
- }
- else{ /* eval previous predicates */
- if (xp_eval(xc, xs->xs_c0, nsc, &xr0) < 0)
- goto done;
- }
- if (xs->xs_c1){
- /* Loop over each node in the nodeset */
- assert (xr0->xc_type == XT_NODESET);
- if ((xr1 = malloc(sizeof(*xr1))) == NULL){
- clicon_err(OE_UNIX, errno, "malloc");
- goto done;
- }
- memset(xr1, 0, sizeof(*xr1));
- xr1->xc_type = XT_NODESET;
- xr1->xc_node = xc->xc_node;
- xr1->xc_initial = xc->xc_initial;
- for (i=0; ixc_size; i++){
- x = xr0->xc_nodeset[i];
- /* Create new context */
- if ((xcc = malloc(sizeof(*xcc))) == NULL){
- clicon_err(OE_XML, errno, "malloc");
- goto done;
- }
- memset(xcc, 0, sizeof(*xcc));
- xcc->xc_type = XT_NODESET;
- xcc->xc_initial = xc->xc_initial;
- xcc->xc_node = x;
- /* For each node in the node-set to be filtered, the PredicateExpr is
- * evaluated with that node as the context node */
- if (cxvec_append(x, &xcc->xc_nodeset, &xcc->xc_size) < 0)
- goto done;
- if (xp_eval(xcc, xs->xs_c1, nsc, &xrc) < 0)
- goto done;
- if (xcc)
- ctx_free(xcc);
- if (xrc->xc_type == XT_NUMBER){
- /* If the result is a number, the result will be converted to true
- if the number is equal to the context position */
- if ((int)xrc->xc_number == i)
- if (cxvec_append(x, &xr1->xc_nodeset, &xr1->xc_size) < 0)
- goto done;
- }
- else {
- /* if PredicateExpr evaluates to true for that node, the node is
- included in the new node-set */
- if (ctx2boolean(xrc))
- if (cxvec_append(x, &xr1->xc_nodeset, &xr1->xc_size) < 0)
- goto done;
- }
- if (xrc)
- ctx_free(xrc);
- }
-
- }
- assert(xr0||xr1);
- if (xr1){
- *xrp = xr1;
- xr1 = NULL;
- }
- else
- if (xr0){
- *xrp = xr0;
- xr0 = NULL;
- }
- retval = 0;
- done:
- if (xr0)
- ctx_free(xr0);
- if (xr1)
- ctx_free(xr1);
- return retval;
-}
-
-/*! Given two XPATH contexts, eval logical operations: or,and
- * The logical operators convert their operands to booleans
- * @param[in] xc1 Context of operand1
- * @param[in] xc2 Context of operand2
- * @param[in] op Relational operator
- * @param[out] xrp Result context
- * @retval 0 OK
- * @retval -1 Error
- */
-static int
-xp_logop(xp_ctx *xc1,
- xp_ctx *xc2,
- enum xp_op op,
- xp_ctx **xrp)
-{
- int retval = -1;
- xp_ctx *xr = NULL;
- int b1;
- int b2;
-
- if ((xr = malloc(sizeof(*xr))) == NULL){
- clicon_err(OE_UNIX, errno, "malloc");
- goto done;
- }
- memset(xr, 0, sizeof(*xr));
- xr->xc_initial = xc1->xc_initial;
- xr->xc_type = XT_BOOL;
- if ((b1 = ctx2boolean(xc1)) < 0)
- goto done;
- if ((b2 = ctx2boolean(xc2)) < 0)
- goto done;
- switch (op){
- case XO_AND:
- xr->xc_bool = b1 && b2;
- break;
- case XO_OR:
- xr->xc_bool = b1 || b2;
- break;
- default:
- clicon_err(OE_UNIX, errno, "%s:Invalid operator %s in this context",
- __FUNCTION__, clicon_int2str(xpopmap,op));
- goto done;
- }
- *xrp = xr;
- retval = 0;
- done:
- return retval;
-}
-
-/*! Given two XPATH contexts, eval numeric operations: +-*,div,mod
- * The numeric operators convert their operands to numbers as if by
- * calling the number function.
- * @param[in] xc1 Context of operand1
- * @param[in] xc2 Context of operand2
- * @param[in] op Relational operator
- * @param[out] xrp Result context
- * @retval 0 OK
- * @retval -1 Error
- */
-static int
-xp_numop(xp_ctx *xc1,
- xp_ctx *xc2,
- enum xp_op op,
- xp_ctx **xrp)
-{
- int retval = -1;
- xp_ctx *xr = NULL;
- double n1;
- double n2;
-
- if ((xr = malloc(sizeof(*xr))) == NULL){
- clicon_err(OE_UNIX, errno, "malloc");
- goto done;
- }
- memset(xr, 0, sizeof(*xr));
- xr->xc_initial = xc1->xc_initial;
- xr->xc_type = XT_NUMBER;
- if (ctx2number(xc1, &n1) < 0)
- goto done;
- if (ctx2number(xc2, &n2) < 0)
- goto done;
- if (isnan(n1) || isnan(n2))
- xr->xc_number = NAN;
- else
- switch (op){
- case XO_DIV:
- xr->xc_number = n1/n2;
- break;
- case XO_MOD:
- xr->xc_number = ((int)n1)%((int)n2);
- break;
- case XO_ADD:
- xr->xc_number = n1+n2;
- break;
- case XO_MULT:
- xr->xc_number = n1*n2;
- break;
- case XO_SUB:
- xr->xc_number = n1-n2;
- break;
- default:
- clicon_err(OE_UNIX, errno, "Invalid operator %s in this context",
- clicon_int2str(xpopmap,op));
- goto done;
- }
- *xrp = xr;
- retval = 0;
- done:
- return retval;
-}
-
-/*! Given two XPATH contexts, eval relational operations: <>=
- * A RelationalExpr is evaluated by comparing the objects that result from
- * evaluating the two operands.
- * This is covered:
- * (a) Both are INTs, BOOLs, STRINGs. Result type is boolean
- * (b) Both are nodesets and one is empty. Result type is boolean.
- * (c) One is nodeset and other is INT or STRING. Result type is nodeset
- * (d) All others (eg two nodesets, BOOL+STRING) are not supported.
- * Op is = EQ
- * From XPATH 1.0 standard, the evaluation has three variants:
- * (1) comparisons that involve node-sets are defined in terms of comparisons that
- * do not involve node-sets; this is defined uniformly for =, !=, <=, <, >= and >.
- * (2) comparisons that do not involve node-sets are defined for = and !=.
- * (3) comparisons that do not involve node-sets are defined for <=, <, >= and >.
- * @param[in] xc1 Context of operand1
- * @param[in] xc2 Context of operand2
- * @param[in] op Relational operator
- * @param[out] xrp Result context
- * @retval 0 OK
- * @retval -1 Error
- */
-static int
-xp_relop(xp_ctx *xc1,
- xp_ctx *xc2,
- enum xp_op op,
- xp_ctx **xrp)
-{
- int retval = -1;
- xp_ctx *xr = NULL;
- xp_ctx *xc;
- cxobj *x;
- int i;
- int j;
- int b;
- char *s1;
- char *s2;
- int reverse = 0;
- double n1, n2;
-
- if ((xr = malloc(sizeof(*xr))) == NULL){
- clicon_err(OE_UNIX, errno, "malloc");
- goto done;
- }
- memset(xr, 0, sizeof(*xr));
- xr->xc_initial = xc1->xc_initial;
- xr->xc_type = XT_BOOL;
- if (xc1->xc_type == xc2->xc_type){ /* cases (2-3) above */
- switch (xc1->xc_type){
- case XT_NODESET:
- /* If both are node-sets, then it is true iff the string value of one
- node in the first node-set and one in the second node-set is true */
- for (i=0; ixc_size; i++){
- if ((s1 = xml_body(xc1->xc_nodeset[i])) == NULL){
- xr->xc_bool = 0;
- goto ok;
- }
- for (j=0; jxc_size; j++){
- if ((s2 = xml_body(xc2->xc_nodeset[j])) == NULL){
- xr->xc_bool = 0;
- goto ok;
- }
- switch(op){
- case XO_EQ:
- xr->xc_bool = (strcmp(s1, s2)==0);
- break;
- case XO_NE:
- xr->xc_bool = (strcmp(s1, s2)!=0);
- break;
- case XO_GE:
- xr->xc_bool = (strcmp(s1, s2)>=0);
- break;
- case XO_LE:
- xr->xc_bool = (strcmp(s1, s2)<=0);
- break;
- case XO_LT:
- xr->xc_bool = (strcmp(s1, s2)<0);
- break;
- case XO_GT:
- xr->xc_bool = (strcmp(s1, s2)>0);
- break;
- default:
- clicon_err(OE_XML, 0, "Operator %s not supported for nodeset/nodeset comparison", clicon_int2str(xpopmap,op));
- goto done;
- break;
- }
- if (xr->xc_bool) /* enough to find a single node */
- break;
- }
- if (xr->xc_bool) /* enough to find a single node */
- break;
- }
- break;
- case XT_BOOL:
- xr->xc_bool = (xc1->xc_bool == xc2->xc_bool);
- break;
- case XT_NUMBER:
- xr->xc_bool = (xc1->xc_number == xc2->xc_number);
- break;
- case XT_STRING:
- xr->xc_bool = (strcmp(xc1->xc_string, xc2->xc_string)==0);
- break;
- }
- }
- else if (xc1->xc_type != XT_NODESET &&
- xc2->xc_type != XT_NODESET){
- clicon_err(OE_XML, 0, "Mixed types not supported, %d %d", xc1->xc_type, xc2->xc_type);
- goto done;
- }
- else{ /* one is nodeset, ie (1) above */
- if (xc2->xc_type == XT_NODESET){
- xc = xc2;
- xc2 = xc1;
- xc1 = xc;
- reverse++; /* reverse */
- }
- /* xc1 is nodeset
- * xc2 is something else */
- switch (xc2->xc_type){
- case XT_BOOL:
- /* comparison on the boolean and the result of converting the
- node-set to a boolean using the boolean function is true. */
- b = ctx2boolean(xc1);
- switch(op){
- case XO_EQ:
- xr->xc_bool = (b == xc2->xc_bool);
- break;
- case XO_NE:
- xr->xc_bool = (b != xc2->xc_bool);
- break;
- default:
- clicon_err(OE_XML, 0, "Operator %s not supported for nodeset and bool", clicon_int2str(xpopmap,op));
- goto done;
- break;
- } /* switch op */
- break;
- case XT_STRING:
- /* If one object to be compared is a node-set and the
- other is a string, then the comparison will be true if and only
- if there is a node in the node-set such that the result of
- performing the comparison on the string-value of the node and
- the other string is true.*/
- s2 = xc2->xc_string;
- for (i=0; ixc_size; i++){
- x = xc1->xc_nodeset[i]; /* node in nodeset */
- s1 = xml_body(x);
- switch(op){
- case XO_EQ:
- if (s1 == NULL || s2 == NULL)
- xr->xc_bool = (s1==NULL && s2 == NULL);
- else
- xr->xc_bool = (strcmp(s1, s2)==0);
- break;
- case XO_NE:
- if (s1 == NULL || s2 == NULL)
- xr->xc_bool = !(s1==NULL && s2 == NULL);
- else
- xr->xc_bool = (strcmp(s1, s2));
- break;
- default:
- clicon_err(OE_XML, 0, "Operator %s not supported for nodeset and string", clicon_int2str(xpopmap,op));
- goto done;
- break;
- }
- if (xr->xc_bool) /* enough to find a single node */
- break;
- }
- break;
- case XT_NUMBER:
- for (i=0; ixc_size; i++){
- x = xc1->xc_nodeset[i]; /* node in nodeset */
- if (sscanf(xml_body(x), "%lf", &n1) != 1)
- n1 = NAN;
- n2 = xc2->xc_number;
- switch(op){
- case XO_EQ:
- xr->xc_bool = (n1 == n2);
- break;
- case XO_NE:
- xr->xc_bool = (n1 != n2);
- break;
- case XO_GE:
- xr->xc_bool = reverse?(n2 >= n1):(n1 >= n2);
- break;
- case XO_LE:
- xr->xc_bool = reverse?(n2 <= n1):(n1 <= n2);
- break;
- case XO_LT:
- xr->xc_bool = reverse?(n2 < n1):(n1 < n2);
- break;
- case XO_GT:
- xr->xc_bool = reverse?(n2 > n1):(n1 > n2);
- break;
- default:
- clicon_err(OE_XML, 0, "Operator %s not supported for nodeset and number", clicon_int2str(xpopmap,op));
- goto done;
- break;
- }
- if (xr->xc_bool) /* enough to find a single node */
- break;
- }
- break;
- default:
- clicon_err(OE_XML, 0, "Type %d not supported", xc2->xc_type);
- } /* switch type */
- }
- ok:
- /* Just ensure bool is 0 or 1 */
- if (xr->xc_type == XT_BOOL && xr->xc_bool != 0)
- xr->xc_bool = 1;
- *xrp = xr;
- retval = 0;
- done:
- return retval;
-}
-
-/*! Given two XPATH contexts, eval union operation
- * Both operands must be nodesets, otherwise empty nodeset is returned
- * @param[in] xc1 Context of operand1
- * @param[in] xc2 Context of operand2
- * @param[in] op Relational operator
- * @param[out] xrp Result context
- * @retval 0 OK
- * @retval -1 Error
- */
-static int
-xp_union(xp_ctx *xc1,
- xp_ctx *xc2,
- enum xp_op op,
- xp_ctx **xrp)
-{
- int retval = -1;
- xp_ctx *xr = NULL;
- int i;
-
- if (op != XO_UNION){
- clicon_err(OE_UNIX, errno, "%s:Invalid operator %s in this context",
- __FUNCTION__, clicon_int2str(xpopmap,op));
- goto done;
- }
- if ((xr = malloc(sizeof(*xr))) == NULL){
- clicon_err(OE_UNIX, errno, "malloc");
- goto done;
- }
- memset(xr, 0, sizeof(*xr));
- xr->xc_initial = xc1->xc_initial;
- xr->xc_type = XT_NODESET;
-
- for (i=0; ixc_size; i++)
- if (cxvec_append(xc1->xc_nodeset[i], &xr->xc_nodeset, &xr->xc_size) < 0)
- goto done;
- for (i=0; ixc_size; i++){
- if (cxvec_append(xc2->xc_nodeset[i], &xr->xc_nodeset, &xr->xc_size) < 0)
- goto done;
- }
- *xrp = xr;
- retval = 0;
- done:
- return retval;
-}
-
-/*! Evaluate an XPATH on an XML tree
-
- * The initial sequence of steps selects a set of nodes relative to a context node.
- * Each node in that set is used as a context node for the following step.
- * @param[in] xc Incoming context
- * @param[in] xs XPATH node tree
- * @param[in] nsc XML Namespace context
- * @param[out] xrp Resulting context
- * @retval 0 OK
- * @retval -1 Error
- */
-static int
-xp_eval(xp_ctx *xc,
- xpath_tree *xs,
- cvec *nsc,
- xp_ctx **xrp)
-{
- int retval = -1;
- cxobj *x;
- xp_ctx *xr0 = NULL;
- xp_ctx *xr1 = NULL;
- xp_ctx *xr2 = NULL;
- int use_xr0 = 0; /* In 2nd child use transitively result of 1st child */
-
- if (debug>1){
- cbuf *cb;
- if ((cb = cbuf_new()) == NULL){
- clicon_err(OE_UNIX, errno, "cbuf_new");
- goto done;
- }
- ctx_print(cb, +2, xc, (char*)clicon_int2str(xpath_tree_map, xs->xs_type));
- clicon_debug(2, "%s", cbuf_get(cb));
- cbuf_free(cb);
- }
- /* Pre-actions before check first child c0
- */
- switch (xs->xs_type){
- case XP_RELLOCPATH:
- if (xs->xs_int == A_DESCENDANT_OR_SELF)
- xc->xc_descendant = 1; /* XXX need to set to 0 in sub */
- break;
- case XP_ABSPATH:
- /* Set context node to top node, and nodeset to that node only */
- x = xc->xc_node;
- while (xml_parent(x) != NULL)
- x = xml_parent(x);
- xc->xc_node = x;
- xc->xc_nodeset[0] = x;
- xc->xc_size=1;
- /* // is short for /descendant-or-self::node()/ */
- if (xs->xs_int == A_DESCENDANT_OR_SELF)
- xc->xc_descendant = 1; /* XXX need to set to 0 in sub */
-
- break;
- case XP_STEP: /* XP_NODE is first argument -not called explicitly */
- if (xp_eval_step(xc, xs, nsc, xrp) < 0)
- goto done;
- goto ok;
- break;
- case XP_PRED:
- if (xp_eval_predicate(xc, xs, nsc, xrp) < 0)
- goto done;
- goto ok;
- break;
- default:
- break;
- }
- /* Eval first child c0
- */
- if (xs->xs_c0){
- if (xp_eval(xc, xs->xs_c0, nsc, &xr0) < 0)
- goto done;
- }
- /* Actions between first and second child
- */
- switch (xs->xs_type){
- case XP_EXP:
- break;
- case XP_AND:
- break;
- case XP_RELEX: /* relexpr --> addexpr | relexpr relop addexpr */
- break;
- case XP_ADD: /* combine mult and add ops */
- break;
- case XP_UNION:
- break;
- case XP_PATHEXPR:
- break;
- case XP_LOCPATH:
- break;
- case XP_ABSPATH:
- use_xr0++;
- /* Special case, no c0 or c1, single "/" */
- if (xs->xs_c0 == NULL){
- if ((xr0 = malloc(sizeof(*xr0))) == NULL){
- clicon_err(OE_UNIX, errno, "malloc");
- goto done;
- }
- memset(xr0, 0, sizeof(*xr0));
- xr0->xc_initial = xc->xc_initial;
- xr0->xc_type = XT_NODESET;
- x = NULL;
- while ((x = xml_child_each(xc->xc_node, x, CX_ELMNT)) != NULL) {
- if (cxvec_append(x, &xr0->xc_nodeset, &xr0->xc_size) < 0)
- goto done;
- }
- }
- break;
- case XP_RELLOCPATH:
- use_xr0++;
- if (xs->xs_int == A_DESCENDANT_OR_SELF)
- xc->xc_descendant = 1; /* XXX need to set to 0 in sub */
- break;
- case XP_NODE:
- break;
- case XP_NODE_FN:
- break;
- case XP_PRI0:
- break;
- case XP_PRIME_NR: /* primaryexpr -> [] */
- if ((xr0 = malloc(sizeof(*xr0))) == NULL){
- clicon_err(OE_UNIX, errno, "malloc");
- goto done;
- }
- memset(xr0, 0, sizeof(*xr0));
- xr0->xc_initial = xc->xc_initial;
- xr0->xc_type = XT_NUMBER;
- xr0->xc_number = xs->xs_double;
- break;
- case XP_PRIME_STR:
- if ((xr0 = malloc(sizeof(*xr0))) == NULL){
- clicon_err(OE_UNIX, errno, "malloc");
- goto done;
- }
- memset(xr0, 0, sizeof(*xr0));
- xr0->xc_initial = xc->xc_initial;
- xr0->xc_type = XT_STRING;
- xr0->xc_string = xs->xs_s0?strdup(xs->xs_s0):NULL;
- break;
- case XP_PRIME_FN:
- break;
- default:
- break;
- }
- /* Eval second child c0
- * Note, some operators like locationpath, need transitive context (use_xr0)
- */
- if (xs->xs_c1)
- if (xp_eval(use_xr0?xr0:xc, xs->xs_c1, nsc, &xr1) < 0)
- goto done;
- /* Actions after second child
- */
- if (xs->xs_c1)
- switch (xs->xs_type){
- case XP_AND: /* combine and and or ops */
- if (xp_logop(xr0, xr1, xs->xs_int, &xr2) < 0)
- goto done;
- break;
- case XP_RELEX: /* relexpr --> addexpr | relexpr relop addexpr */
- if (xp_relop(xr0, xr1, xs->xs_int, &xr2) < 0)
- goto done;
- break;
- case XP_ADD: /* combine mult and add ops */
- if (xp_numop(xr0, xr1, xs->xs_int, &xr2) < 0)
- goto done;
- break;
- case XP_UNION: /* combine and and or ops */
- if (xp_union(xr0, xr1, xs->xs_int, &xr2) < 0)
- goto done;
- default:
- break;
- }
- xc->xc_descendant = 0;
- assert(xr0||xr1||xr2);
- if (xr2){
- *xrp = xr2;
- xr2 = NULL;
- }
- else if (xr1){
- *xrp = xr1;
- xr1 = NULL;
- }
- else
- if (xr0){
- *xrp = xr0;
- xr0 = NULL;
- }
- ok:
- if (debug){
- cbuf *cb;
- if ((cb = cbuf_new()) == NULL){
- clicon_err(OE_UNIX, errno, "cbuf_new");
- goto done;
- }
- ctx_print(cb, -2, *xrp, (char*)clicon_int2str(xpath_tree_map, xs->xs_type));
- clicon_debug(2, "%s", cbuf_get(cb));
- cbuf_free(cb);
- }
- retval = 0;
- done:
- if (xr2)
- ctx_free(xr2);
- if (xr1)
- ctx_free(xr1);
- if (xr0)
- ctx_free(xr0);
- return retval;
-} /* xp_eval */
-
/*! Given XML tree and xpath, parse xpath, eval it and return xpath context,
* This is a raw form of xpath where you can do type conversion, etc,
* not just a nodeset.
- * @param[in] xcur XML-tree where to search
- * @param[in] xpath String with XPATH 1.0 syntax
* @param[in] nsc XML Namespace context
- * @param[out] xrp Return XPATH context
+ * @param[in] xpath String with XPATH 1.0 syntax
+ * @param[out] xptree Xpath-tree, parsed, structured XPATH, free:xpath_tree_free
* @retval 0 OK
* @retval -1 Error
* @code
@@ -1183,15 +195,14 @@ xp_eval(xp_ctx *xc,
* if (xc)
* ctx_free(xc);
* @endcode
+ * @see xpath_tree_free
*/
int
-xpath_vec_ctx(cxobj *xcur,
- cvec *nsc,
- char *xpath,
- xp_ctx **xrp)
+xpath_parse(cvec *nsc,
+ char *xpath,
+ xpath_tree **xptree)
{
int retval = -1;
- xp_ctx xc = {0,};
struct clicon_xpath_yacc_arg xy = {0,};
xy.xy_parse_string = xpath;
@@ -1214,24 +225,60 @@ xpath_vec_ctx(cxobj *xcur,
clicon_debug(2, "xpath parse tree:\n%s", cbuf_get(cb));
cbuf_free(cb);
}
+ /* done: */
+ xpath_parse_exit(&xy);
+ xpath_scan_exit(&xy);
+ *xptree = xy.xy_top;
+ retval = 0;
+ done:
+ return retval;
+}
+
+/*! Given XML tree and xpath, parse xpath, eval it and return xpath context,
+ * This is a raw form of xpath where you can do type conversion of the return
+ * value, etc, not just a nodeset.
+ * @param[in] xcur XML-tree where to search
+ * @param[in] nsc XML Namespace context
+ * @param[in] xpath String with XPATH 1.0 syntax
+ * @param[out] xrp Return XPATH context
+ * @retval 0 OK
+ * @retval -1 Error
+ * @code
+ * xp_ctx *xc = NULL;
+ * if (xpath_vec_ctx(x, NULL, xpath, &xc) < 0)
+ * err;
+ * if (xc)
+ * ctx_free(xc);
+ * @endcode
+ */
+int
+xpath_vec_ctx(cxobj *xcur,
+ cvec *nsc,
+ char *xpath,
+ xp_ctx **xrp)
+{
+ int retval = -1;
+ xpath_tree *xptree = NULL;
+ xp_ctx xc = {0,};
+
+ if (xpath_parse(nsc, xpath, &xptree) < 0)
+ goto done;
xc.xc_type = XT_NODESET;
xc.xc_node = xcur;
xc.xc_initial = xcur;
if (cxvec_append(xcur, &xc.xc_nodeset, &xc.xc_size) < 0)
goto done;
- if (xp_eval(&xc, xy.xy_top, nsc, xrp) < 0)
+ if (xp_eval(&xc, xptree, nsc, xrp) < 0)
goto done;
if (xc.xc_nodeset){
free(xc.xc_nodeset);
xc.xc_nodeset = NULL;
}
- /* done: */
- xpath_parse_exit(&xy);
- xpath_scan_exit(&xy);
retval = 0;
done:
- if (xy.xy_top)
- xpath_tree_free(xy.xy_top);
+ if (xptree)
+ xpath_tree_free(xptree);
+
return retval;
}
@@ -1295,7 +342,6 @@ xpath_first(cxobj *xcur,
return cx;
}
-
/*! Given XML tree and xpath, returns nodeset as xml node vector
* If result is not nodeset, return empty nodeset
* @param[in] xcur xml-tree where to search
diff --git a/lib/src/clixon_xpath_ctx.c b/lib/src/clixon_xpath_ctx.c
index bf7b5dec..5915a184 100644
--- a/lib/src/clixon_xpath_ctx.c
+++ b/lib/src/clixon_xpath_ctx.c
@@ -59,8 +59,9 @@
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
-#include "clixon_xpath_parse.h"
#include "clixon_xpath_ctx.h"
+#include "clixon_xpath.h"
+#include "clixon_xpath_parse.h"
/*
* Variables
diff --git a/lib/src/clixon_xpath_eval.c b/lib/src/clixon_xpath_eval.c
new file mode 100644
index 00000000..41bdf200
--- /dev/null
+++ b/lib/src/clixon_xpath_eval.c
@@ -0,0 +1,1062 @@
+/*
+ *
+ ***** BEGIN LICENSE BLOCK *****
+
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
+
+ This file is part of CLIXON.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ Alternatively, the contents of this file may be used under the terms of
+ the GNU General Public License Version 3 or later (the "GPL"),
+ in which case the provisions of the GPL are applicable instead
+ of those above. If you wish to allow use of your version of this file only
+ under the terms of the GPL, and not to allow others to
+ use your version of this file under the terms of Apache License version 2, indicate
+ your decision by deleting the provisions above and replace them with the
+ notice and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this file under
+ the terms of any one of the Apache License version 2 or the GPL.
+
+ ***** END LICENSE BLOCK *****
+
+ * Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10
+ *
+ * Some notes on namespace extensions in Netconf/Yang
+ * RFC6241 8.9.1
+ * The set of namespace declarations are those in scope on the element.
+ *
+ *
+ *
+ *
+ * We need to add namespace context to the cpath tree, typically in eval. How do
+ * we do that?
+ * One observation is that the namespace context is static, so it can not be a part
+ * of the xpath-tree, which is context-dependent.
+ * Best is to send it as a (read-only) parameter to the xp_eval family of functions
+ * as an exlicit namespace context.
+ * For that you need an API to get/set namespaces: clixon_xml_nscache.c?
+ * Then you need to fix API functions and this is the real work:
+ * - Replace all existing functions or create new?
+ * - Expose explicit namespace parameter, or xml object, or default namespace?
+ */
+#ifdef HAVE_CONFIG_H
+#include "clixon_config.h" /* generated by config & autoconf */
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* cligen */
+#include
+
+/* clicon */
+#include "clixon_err.h"
+#include "clixon_log.h"
+#include "clixon_string.h"
+#include "clixon_queue.h"
+#include "clixon_hash.h"
+#include "clixon_handle.h"
+#include "clixon_yang.h"
+#include "clixon_xml.h"
+#include "clixon_xml_nsctx.h"
+#include "clixon_xpath_ctx.h"
+#include "clixon_xpath.h"
+#include "clixon_xpath_eval.h"
+
+/* Mapping between XPATH operator string <--> int */
+const map_str2int xpopmap[] = {
+ {"and", XO_AND},
+ {"or", XO_OR},
+ {"div", XO_DIV},
+ {"mod", XO_MOD},
+ {"+", XO_ADD},
+ {"*", XO_MULT},
+ {"-", XO_SUB},
+ {"=", XO_EQ},
+ {"!=", XO_NE},
+ {">=", XO_GE},
+ {"<=", XO_LE},
+ {"<", XO_LT},
+ {">", XO_GT},
+ {"|", XO_UNION},
+ {NULL, -1}
+};
+
+/*!
+ * @retval -1 Error XXX: retval -1 not properly handled
+ * @retval 0 No match
+ * @retval 1 Match
+ */
+static int
+nodetest_eval_node(cxobj *x,
+ xpath_tree *xs,
+ cvec *nsc)
+{
+ int retval = -1;
+ char *name1 = xml_name(x);
+ char *prefix1 = xml_prefix(x);
+ char *nsxml = NULL; /* xml body namespace */
+ char *nsxpath = NULL; /* xpath context namespace */
+ char *prefix2 = NULL;
+ char *name2 = NULL;
+
+ /* Namespaces is s0, name is s1 */
+ if (strcmp(xs->xs_s1, "*")==0)
+ return 1;
+ /* get namespace of xml tree */
+ if (xml2ns(x, prefix1, &nsxml) < 0)
+ goto done;
+ prefix2 = xs->xs_s0;
+ name2 = xs->xs_s1;
+ /* Before going into namespaces, check name equality and filter out noteq */
+ if (strcmp(name1, name2) != 0){
+ retval = 0; /* no match */
+ goto done;
+ }
+ /* here names are equal
+ * Now look for namespaces
+ * 1) prefix1 and prefix2 point to same namespace <<-- try this first
+ * 2) prefix1 is equal to prefix2 <<-- then try this
+ * (1) is strict yang xml
+ * (2) without yang
+ */
+ if (nsc != NULL) { /* solution (1) */
+ nsxpath = xml_nsctx_get(nsc, prefix2);
+ if (nsxml != NULL && nsxpath != NULL)
+ retval = (strcmp(nsxml, nsxpath) == 0);
+ else
+ retval = (nsxml == nsxpath); /* True only if both are NULL */
+ }
+ else{ /* solution (2) */
+ if (prefix1 == NULL && prefix2 == NULL)
+ retval = 1;
+ else if (prefix1 == NULL || prefix2 == NULL)
+ retval = 0;
+ else
+ retval = strcmp(prefix1, prefix2) == 0;
+ }
+ /* If retval == 0 here, then there is name match, but not ns match */
+ if (retval == 0){
+ fprintf(stderr, "%s NOMATCH xml: (%s)%s\n\t\t xpath: (%s)%s\n", __FUNCTION__,
+ name1, nsxml,
+ name2, nsxpath);
+ }
+ done:
+ return retval;
+}
+
+/*! Make a nodetest
+ * @param[in] xs XPATH stack of type XP_NODE or XP_NODE_FN
+ * @param[in] nsc XML Namespace context
+ * @retval -1 Error
+ * @retval 0 No match
+ * @retval 1 Match
+ * - node() is true for any node of any type whatsoever.
+ * - text() is true for any text node.
+ */
+static int
+nodetest_eval(cxobj *x,
+ xpath_tree *xs,
+ cvec *nsc)
+{
+ int retval = 0; /* NB: no match is default (not error) */
+ char *fn;
+
+ if (xs->xs_type == XP_NODE)
+ retval = nodetest_eval_node(x, xs, nsc);
+ else if (xs->xs_type == XP_NODE_FN){
+ fn = xs->xs_s0;
+ if (strcmp(fn, "node")==0)
+ retval = 1;
+ else if (strcmp(fn, "text")==0)
+ retval = 1;
+ }
+ /* note, retval set by previous statement */
+ return retval;
+}
+
+/*!
+ * @param[in] xn
+ * @param[in] nodetest XPATH stack
+ * @param[in] node_type
+ * @param[in] flags
+ * @param[in] nsc XML Namespace context
+ * @param[out] vec0
+ * @param[out] vec0len
+ */
+int
+nodetest_recursive(cxobj *xn,
+ xpath_tree *nodetest,
+ int node_type,
+ uint16_t flags,
+ cvec *nsc,
+ cxobj ***vec0,
+ size_t *vec0len)
+{
+ int retval = -1;
+ cxobj *xsub;
+ cxobj **vec = *vec0;
+ size_t veclen = *vec0len;
+
+ xsub = NULL;
+ while ((xsub = xml_child_each(xn, xsub, node_type)) != NULL) {
+ if (nodetest_eval(xsub, nodetest, nsc) == 1){
+ clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xsub, flags));
+ if (flags==0x0 || xml_flag(xsub, flags))
+ if (cxvec_append(xsub, &vec, &veclen) < 0)
+ goto done;
+ // continue; /* Dont go deeper */
+ }
+ if (nodetest_recursive(xsub, nodetest, node_type, flags, nsc, &vec, &veclen) < 0)
+ goto done;
+ }
+ retval = 0;
+ *vec0 = vec;
+ *vec0len = veclen;
+ done:
+ return retval;
+}
+
+/*! Evaluate xpath step rule of an XML tree
+ *
+ * @param[in] xc0 Incoming context
+ * @param[in] xs XPATH node tree
+ * @param[in] nsc XML Namespace context
+ * @param[out] xrp Resulting context
+ *
+ * - A node test that is a QName is true if and only if the type of the node (see [5 Data Model])
+ * is the principal node type and has an expanded-name equal to the expanded-name specified by the QName.
+ * - A node test * is true for any node of the principal node type.
+ * - node() is true for any node of any type whatsoever.
+ * - text() is true for any text node.
+ */
+static int
+xp_eval_step(xp_ctx *xc0,
+ xpath_tree *xs,
+ cvec *nsc,
+ xp_ctx **xrp)
+{
+ int retval = -1;
+ int i;
+ cxobj *x;
+ cxobj *xv;
+ cxobj *xp;
+ cxobj **vec = NULL;
+ size_t veclen = 0;
+ xpath_tree *nodetest = xs->xs_c0;
+ xp_ctx *xc = NULL;
+
+ /* Create new xc */
+ if ((xc = ctx_dup(xc0)) == NULL)
+ goto done;
+ switch (xs->xs_int){
+ case A_ANCESTOR:
+ break;
+ case A_ANCESTOR_OR_SELF:
+ break;
+ case A_ATTRIBUTE: /* principal node type is attribute */
+ break;
+ case A_CHILD:
+ if (xc->xc_descendant){
+ for (i=0; ixc_size; i++){
+ xv = xc->xc_nodeset[i];
+ if (nodetest_recursive(xv, nodetest, CX_ELMNT, 0x0, nsc, &vec, &veclen) < 0)
+ goto done;
+ }
+ xc->xc_descendant = 0;
+ }
+ else{
+ if (nodetest->xs_type==XP_NODE_FN &&
+ nodetest->xs_s0 &&
+ strcmp(nodetest->xs_s0,"current")==0){
+ if (cxvec_append(xc->xc_initial, &vec, &veclen) < 0)
+ goto done;
+ }
+ else for (i=0; ixc_size; i++){
+ xv = xc->xc_nodeset[i];
+ x = NULL;
+ while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
+ /* xs->xs_c0 is nodetest */
+ if (nodetest == NULL || nodetest_eval(x, nodetest, nsc) == 1)
+ if (cxvec_append(x, &vec, &veclen) < 0)
+ goto done;
+ }
+ }
+ }
+ ctx_nodeset_replace(xc, vec, veclen);
+ break;
+ case A_DESCENDANT:
+ case A_DESCENDANT_OR_SELF:
+ for (i=0; ixc_size; i++){
+ xv = xc->xc_nodeset[i];
+ if (nodetest_recursive(xv, xs->xs_c0, CX_ELMNT, 0x0, nsc, &vec, &veclen) < 0)
+ goto done;
+ }
+ ctx_nodeset_replace(xc, vec, veclen);
+ break;
+ case A_FOLLOWING:
+ break;
+ case A_FOLLOWING_SIBLING:
+ break;
+ case A_NAMESPACE: /* principal node type is namespace */
+ break;
+ case A_PARENT:
+ veclen = xc->xc_size;
+ vec = xc->xc_nodeset;
+ xc->xc_size = 0;
+ xc->xc_nodeset = NULL;
+ for (i=0; ixc_nodeset, &xc->xc_size) < 0)
+ goto done;
+ }
+ if (vec){
+ free(vec);
+ vec = NULL;
+ }
+ break;
+ case A_PRECEEDING:
+ break;
+ case A_PRECEEDING_SIBLING:
+ break;
+ case A_SELF:
+ break;
+ default:
+ clicon_err(OE_XML, 0, "No such axisname: %d", xs->xs_int);
+ goto done;
+ break;
+ }
+ if (xs->xs_c1){
+ if (xp_eval(xc, xs->xs_c1, nsc, xrp) < 0)
+ goto done;
+ }
+ else{
+ *xrp = xc;
+ xc = NULL;
+ }
+ assert(*xrp);
+ retval = 0;
+ done:
+ if (xc)
+ ctx_free(xc);
+ return retval;
+}
+
+/*! Evaluate xpath predicates rule
+ *
+ * pred -> pred expr
+ * @param[in] xc Incoming context
+ * @param[in] xs XPATH node tree
+ * @param[in] nsc XML Namespace context
+ * @param[out] xrp Resulting context
+ *
+ * A predicate filters a node-set with respect to an axis to produce a new
+ * node-set. For each node in the node-set to be filtered, the PredicateExpr is
+ * evaluated with that node as the context node, with the number of nodes in
+ * the node-set as the context size, and with the proximity position of the node
+ * in the node-set with respect to the axis as the context position; if
+ * PredicateExpr evaluates to true for that node, the node is included in the
+ * new node-set; otherwise, it is not included.
+ * A PredicateExpr is evaluated by evaluating the Expr and converting the result
+ * to a boolean. If the result is a
+ * - number, the result will be converted to true if the number is equal to the
+ * context position and will be converted to false otherwise;
+ * - if the result is not a number, then the result will be converted as if by a
+ * call to the boolean function.
+ * Thus a location path para[3] is equivalent to para[position()=3].
+ */
+static int
+xp_eval_predicate(xp_ctx *xc,
+ xpath_tree *xs,
+ cvec *nsc,
+ xp_ctx **xrp)
+{
+ int retval = -1;
+ xp_ctx *xr0 = NULL;
+ xp_ctx *xr1 = NULL;
+ xp_ctx *xrc = NULL;
+ int i;
+ cxobj *x;
+ xp_ctx *xcc;
+
+ if (xs->xs_c0 == NULL){ /* empty */
+ if ((xr0 = ctx_dup(xc)) == NULL)
+ goto done;
+ }
+ else{ /* eval previous predicates */
+ if (xp_eval(xc, xs->xs_c0, nsc, &xr0) < 0)
+ goto done;
+ }
+ if (xs->xs_c1){
+ /* Loop over each node in the nodeset */
+ assert (xr0->xc_type == XT_NODESET);
+ if ((xr1 = malloc(sizeof(*xr1))) == NULL){
+ clicon_err(OE_UNIX, errno, "malloc");
+ goto done;
+ }
+ memset(xr1, 0, sizeof(*xr1));
+ xr1->xc_type = XT_NODESET;
+ xr1->xc_node = xc->xc_node;
+ xr1->xc_initial = xc->xc_initial;
+ for (i=0; ixc_size; i++){
+ x = xr0->xc_nodeset[i];
+ /* Create new context */
+ if ((xcc = malloc(sizeof(*xcc))) == NULL){
+ clicon_err(OE_XML, errno, "malloc");
+ goto done;
+ }
+ memset(xcc, 0, sizeof(*xcc));
+ xcc->xc_type = XT_NODESET;
+ xcc->xc_initial = xc->xc_initial;
+ xcc->xc_node = x;
+ /* For each node in the node-set to be filtered, the PredicateExpr is
+ * evaluated with that node as the context node */
+ if (cxvec_append(x, &xcc->xc_nodeset, &xcc->xc_size) < 0)
+ goto done;
+ if (xp_eval(xcc, xs->xs_c1, nsc, &xrc) < 0)
+ goto done;
+ if (xcc)
+ ctx_free(xcc);
+ if (xrc->xc_type == XT_NUMBER){
+ /* If the result is a number, the result will be converted to true
+ if the number is equal to the context position */
+ if ((int)xrc->xc_number == i)
+ if (cxvec_append(x, &xr1->xc_nodeset, &xr1->xc_size) < 0)
+ goto done;
+ }
+ else {
+ /* if PredicateExpr evaluates to true for that node, the node is
+ included in the new node-set */
+ if (ctx2boolean(xrc))
+ if (cxvec_append(x, &xr1->xc_nodeset, &xr1->xc_size) < 0)
+ goto done;
+ }
+ if (xrc)
+ ctx_free(xrc);
+ }
+
+ }
+ assert(xr0||xr1);
+ if (xr1){
+ *xrp = xr1;
+ xr1 = NULL;
+ }
+ else
+ if (xr0){
+ *xrp = xr0;
+ xr0 = NULL;
+ }
+ retval = 0;
+ done:
+ if (xr0)
+ ctx_free(xr0);
+ if (xr1)
+ ctx_free(xr1);
+ return retval;
+}
+
+/*! Given two XPATH contexts, eval logical operations: or,and
+ * The logical operators convert their operands to booleans
+ * @param[in] xc1 Context of operand1
+ * @param[in] xc2 Context of operand2
+ * @param[in] op Relational operator
+ * @param[out] xrp Result context
+ * @retval 0 OK
+ * @retval -1 Error
+ */
+static int
+xp_logop(xp_ctx *xc1,
+ xp_ctx *xc2,
+ enum xp_op op,
+ xp_ctx **xrp)
+{
+ int retval = -1;
+ xp_ctx *xr = NULL;
+ int b1;
+ int b2;
+
+ if ((xr = malloc(sizeof(*xr))) == NULL){
+ clicon_err(OE_UNIX, errno, "malloc");
+ goto done;
+ }
+ memset(xr, 0, sizeof(*xr));
+ xr->xc_initial = xc1->xc_initial;
+ xr->xc_type = XT_BOOL;
+ if ((b1 = ctx2boolean(xc1)) < 0)
+ goto done;
+ if ((b2 = ctx2boolean(xc2)) < 0)
+ goto done;
+ switch (op){
+ case XO_AND:
+ xr->xc_bool = b1 && b2;
+ break;
+ case XO_OR:
+ xr->xc_bool = b1 || b2;
+ break;
+ default:
+ clicon_err(OE_UNIX, errno, "%s:Invalid operator %s in this context",
+ __FUNCTION__, clicon_int2str(xpopmap,op));
+ goto done;
+ }
+ *xrp = xr;
+ retval = 0;
+ done:
+ return retval;
+}
+
+/*! Given two XPATH contexts, eval numeric operations: +-*,div,mod
+ * The numeric operators convert their operands to numbers as if by
+ * calling the number function.
+ * @param[in] xc1 Context of operand1
+ * @param[in] xc2 Context of operand2
+ * @param[in] op Relational operator
+ * @param[out] xrp Result context
+ * @retval 0 OK
+ * @retval -1 Error
+ */
+static int
+xp_numop(xp_ctx *xc1,
+ xp_ctx *xc2,
+ enum xp_op op,
+ xp_ctx **xrp)
+{
+ int retval = -1;
+ xp_ctx *xr = NULL;
+ double n1;
+ double n2;
+
+ if ((xr = malloc(sizeof(*xr))) == NULL){
+ clicon_err(OE_UNIX, errno, "malloc");
+ goto done;
+ }
+ memset(xr, 0, sizeof(*xr));
+ xr->xc_initial = xc1->xc_initial;
+ xr->xc_type = XT_NUMBER;
+ if (ctx2number(xc1, &n1) < 0)
+ goto done;
+ if (ctx2number(xc2, &n2) < 0)
+ goto done;
+ if (isnan(n1) || isnan(n2))
+ xr->xc_number = NAN;
+ else
+ switch (op){
+ case XO_DIV:
+ xr->xc_number = n1/n2;
+ break;
+ case XO_MOD:
+ xr->xc_number = ((int)n1)%((int)n2);
+ break;
+ case XO_ADD:
+ xr->xc_number = n1+n2;
+ break;
+ case XO_MULT:
+ xr->xc_number = n1*n2;
+ break;
+ case XO_SUB:
+ xr->xc_number = n1-n2;
+ break;
+ default:
+ clicon_err(OE_UNIX, errno, "Invalid operator %s in this context",
+ clicon_int2str(xpopmap,op));
+ goto done;
+ }
+ *xrp = xr;
+ retval = 0;
+ done:
+ return retval;
+}
+
+/*! Given two XPATH contexts, eval relational operations: <>=
+ * A RelationalExpr is evaluated by comparing the objects that result from
+ * evaluating the two operands.
+ * This is covered:
+ * (a) Both are INTs, BOOLs, STRINGs. Result type is boolean
+ * (b) Both are nodesets and one is empty. Result type is boolean.
+ * (c) One is nodeset and other is INT or STRING. Result type is nodeset
+ * (d) All others (eg two nodesets, BOOL+STRING) are not supported.
+ * Op is = EQ
+ * From XPATH 1.0 standard, the evaluation has three variants:
+ * (1) comparisons that involve node-sets are defined in terms of comparisons that
+ * do not involve node-sets; this is defined uniformly for =, !=, <=, <, >= and >.
+ * (2) comparisons that do not involve node-sets are defined for = and !=.
+ * (3) comparisons that do not involve node-sets are defined for <=, <, >= and >.
+ * @param[in] xc1 Context of operand1
+ * @param[in] xc2 Context of operand2
+ * @param[in] op Relational operator
+ * @param[out] xrp Result context
+ * @retval 0 OK
+ * @retval -1 Error
+ */
+static int
+xp_relop(xp_ctx *xc1,
+ xp_ctx *xc2,
+ enum xp_op op,
+ xp_ctx **xrp)
+{
+ int retval = -1;
+ xp_ctx *xr = NULL;
+ xp_ctx *xc;
+ cxobj *x;
+ int i;
+ int j;
+ int b;
+ char *s1;
+ char *s2;
+ int reverse = 0;
+ double n1, n2;
+
+ if ((xr = malloc(sizeof(*xr))) == NULL){
+ clicon_err(OE_UNIX, errno, "malloc");
+ goto done;
+ }
+ memset(xr, 0, sizeof(*xr));
+ xr->xc_initial = xc1->xc_initial;
+ xr->xc_type = XT_BOOL;
+ if (xc1->xc_type == xc2->xc_type){ /* cases (2-3) above */
+ switch (xc1->xc_type){
+ case XT_NODESET:
+ /* If both are node-sets, then it is true iff the string value of one
+ node in the first node-set and one in the second node-set is true */
+ for (i=0; ixc_size; i++){
+ if ((s1 = xml_body(xc1->xc_nodeset[i])) == NULL){
+ xr->xc_bool = 0;
+ goto ok;
+ }
+ for (j=0; jxc_size; j++){
+ if ((s2 = xml_body(xc2->xc_nodeset[j])) == NULL){
+ xr->xc_bool = 0;
+ goto ok;
+ }
+ switch(op){
+ case XO_EQ:
+ xr->xc_bool = (strcmp(s1, s2)==0);
+ break;
+ case XO_NE:
+ xr->xc_bool = (strcmp(s1, s2)!=0);
+ break;
+ case XO_GE:
+ xr->xc_bool = (strcmp(s1, s2)>=0);
+ break;
+ case XO_LE:
+ xr->xc_bool = (strcmp(s1, s2)<=0);
+ break;
+ case XO_LT:
+ xr->xc_bool = (strcmp(s1, s2)<0);
+ break;
+ case XO_GT:
+ xr->xc_bool = (strcmp(s1, s2)>0);
+ break;
+ default:
+ clicon_err(OE_XML, 0, "Operator %s not supported for nodeset/nodeset comparison", clicon_int2str(xpopmap,op));
+ goto done;
+ break;
+ }
+ if (xr->xc_bool) /* enough to find a single node */
+ break;
+ }
+ if (xr->xc_bool) /* enough to find a single node */
+ break;
+ }
+ break;
+ case XT_BOOL:
+ xr->xc_bool = (xc1->xc_bool == xc2->xc_bool);
+ break;
+ case XT_NUMBER:
+ xr->xc_bool = (xc1->xc_number == xc2->xc_number);
+ break;
+ case XT_STRING:
+ xr->xc_bool = (strcmp(xc1->xc_string, xc2->xc_string)==0);
+ break;
+ }
+ }
+ else if (xc1->xc_type != XT_NODESET &&
+ xc2->xc_type != XT_NODESET){
+ clicon_err(OE_XML, 0, "Mixed types not supported, %d %d", xc1->xc_type, xc2->xc_type);
+ goto done;
+ }
+ else{ /* one is nodeset, ie (1) above */
+ if (xc2->xc_type == XT_NODESET){
+ xc = xc2;
+ xc2 = xc1;
+ xc1 = xc;
+ reverse++; /* reverse */
+ }
+ /* xc1 is nodeset
+ * xc2 is something else */
+ switch (xc2->xc_type){
+ case XT_BOOL:
+ /* comparison on the boolean and the result of converting the
+ node-set to a boolean using the boolean function is true. */
+ b = ctx2boolean(xc1);
+ switch(op){
+ case XO_EQ:
+ xr->xc_bool = (b == xc2->xc_bool);
+ break;
+ case XO_NE:
+ xr->xc_bool = (b != xc2->xc_bool);
+ break;
+ default:
+ clicon_err(OE_XML, 0, "Operator %s not supported for nodeset and bool", clicon_int2str(xpopmap,op));
+ goto done;
+ break;
+ } /* switch op */
+ break;
+ case XT_STRING:
+ /* If one object to be compared is a node-set and the
+ other is a string, then the comparison will be true if and only
+ if there is a node in the node-set such that the result of
+ performing the comparison on the string-value of the node and
+ the other string is true.*/
+ s2 = xc2->xc_string;
+ for (i=0; ixc_size; i++){
+ x = xc1->xc_nodeset[i]; /* node in nodeset */
+ s1 = xml_body(x);
+ switch(op){
+ case XO_EQ:
+ if (s1 == NULL || s2 == NULL)
+ xr->xc_bool = (s1==NULL && s2 == NULL);
+ else
+ xr->xc_bool = (strcmp(s1, s2)==0);
+ break;
+ case XO_NE:
+ if (s1 == NULL || s2 == NULL)
+ xr->xc_bool = !(s1==NULL && s2 == NULL);
+ else
+ xr->xc_bool = (strcmp(s1, s2));
+ break;
+ default:
+ clicon_err(OE_XML, 0, "Operator %s not supported for nodeset and string", clicon_int2str(xpopmap,op));
+ goto done;
+ break;
+ }
+ if (xr->xc_bool) /* enough to find a single node */
+ break;
+ }
+ break;
+ case XT_NUMBER:
+ for (i=0; ixc_size; i++){
+ x = xc1->xc_nodeset[i]; /* node in nodeset */
+ if (sscanf(xml_body(x), "%lf", &n1) != 1)
+ n1 = NAN;
+ n2 = xc2->xc_number;
+ switch(op){
+ case XO_EQ:
+ xr->xc_bool = (n1 == n2);
+ break;
+ case XO_NE:
+ xr->xc_bool = (n1 != n2);
+ break;
+ case XO_GE:
+ xr->xc_bool = reverse?(n2 >= n1):(n1 >= n2);
+ break;
+ case XO_LE:
+ xr->xc_bool = reverse?(n2 <= n1):(n1 <= n2);
+ break;
+ case XO_LT:
+ xr->xc_bool = reverse?(n2 < n1):(n1 < n2);
+ break;
+ case XO_GT:
+ xr->xc_bool = reverse?(n2 > n1):(n1 > n2);
+ break;
+ default:
+ clicon_err(OE_XML, 0, "Operator %s not supported for nodeset and number", clicon_int2str(xpopmap,op));
+ goto done;
+ break;
+ }
+ if (xr->xc_bool) /* enough to find a single node */
+ break;
+ }
+ break;
+ default:
+ clicon_err(OE_XML, 0, "Type %d not supported", xc2->xc_type);
+ } /* switch type */
+ }
+ ok:
+ /* Just ensure bool is 0 or 1 */
+ if (xr->xc_type == XT_BOOL && xr->xc_bool != 0)
+ xr->xc_bool = 1;
+ *xrp = xr;
+ retval = 0;
+ done:
+ return retval;
+}
+
+/*! Given two XPATH contexts, eval union operation
+ * Both operands must be nodesets, otherwise empty nodeset is returned
+ * @param[in] xc1 Context of operand1
+ * @param[in] xc2 Context of operand2
+ * @param[in] op Relational operator
+ * @param[out] xrp Result context
+ * @retval 0 OK
+ * @retval -1 Error
+ */
+static int
+xp_union(xp_ctx *xc1,
+ xp_ctx *xc2,
+ enum xp_op op,
+ xp_ctx **xrp)
+{
+ int retval = -1;
+ xp_ctx *xr = NULL;
+ int i;
+
+ if (op != XO_UNION){
+ clicon_err(OE_UNIX, errno, "%s:Invalid operator %s in this context",
+ __FUNCTION__, clicon_int2str(xpopmap,op));
+ goto done;
+ }
+ if ((xr = malloc(sizeof(*xr))) == NULL){
+ clicon_err(OE_UNIX, errno, "malloc");
+ goto done;
+ }
+ memset(xr, 0, sizeof(*xr));
+ xr->xc_initial = xc1->xc_initial;
+ xr->xc_type = XT_NODESET;
+
+ for (i=0; ixc_size; i++)
+ if (cxvec_append(xc1->xc_nodeset[i], &xr->xc_nodeset, &xr->xc_size) < 0)
+ goto done;
+ for (i=0; ixc_size; i++){
+ if (cxvec_append(xc2->xc_nodeset[i], &xr->xc_nodeset, &xr->xc_size) < 0)
+ goto done;
+ }
+ *xrp = xr;
+ retval = 0;
+ done:
+ return retval;
+}
+
+/*! Evaluate an XPATH on an XML tree
+
+ * The initial sequence of steps selects a set of nodes relative to a context node.
+ * Each node in that set is used as a context node for the following step.
+ * @param[in] xc Incoming context
+ * @param[in] xs XPATH node tree
+ * @param[in] nsc XML Namespace context
+ * @param[out] xrp Resulting context
+ * @retval 0 OK
+ * @retval -1 Error
+ */
+int
+xp_eval(xp_ctx *xc,
+ xpath_tree *xs,
+ cvec *nsc,
+ xp_ctx **xrp)
+{
+ int retval = -1;
+ cxobj *x;
+ xp_ctx *xr0 = NULL;
+ xp_ctx *xr1 = NULL;
+ xp_ctx *xr2 = NULL;
+ int use_xr0 = 0; /* In 2nd child use transitively result of 1st child */
+
+ if (debug>1){
+ cbuf *cb;
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
+ goto done;
+ }
+ ctx_print(cb, +2, xc, xpath_tree_int2str(xs->xs_type));
+ clicon_debug(2, "%s", cbuf_get(cb));
+ cbuf_free(cb);
+ }
+ /* Pre-actions before check first child c0
+ */
+ switch (xs->xs_type){
+ case XP_RELLOCPATH:
+ if (xs->xs_int == A_DESCENDANT_OR_SELF)
+ xc->xc_descendant = 1; /* XXX need to set to 0 in sub */
+ break;
+ case XP_ABSPATH:
+ /* Set context node to top node, and nodeset to that node only */
+ x = xc->xc_node;
+ while (xml_parent(x) != NULL)
+ x = xml_parent(x);
+ xc->xc_node = x;
+ xc->xc_nodeset[0] = x;
+ xc->xc_size=1;
+ /* // is short for /descendant-or-self::node()/ */
+ if (xs->xs_int == A_DESCENDANT_OR_SELF)
+ xc->xc_descendant = 1; /* XXX need to set to 0 in sub */
+
+ break;
+ case XP_STEP: /* XP_NODE is first argument -not called explicitly */
+ if (xp_eval_step(xc, xs, nsc, xrp) < 0)
+ goto done;
+ goto ok;
+ break;
+ case XP_PRED:
+ if (xp_eval_predicate(xc, xs, nsc, xrp) < 0)
+ goto done;
+ goto ok;
+ break;
+ default:
+ break;
+ }
+ /* Eval first child c0
+ */
+ if (xs->xs_c0){
+ if (xp_eval(xc, xs->xs_c0, nsc, &xr0) < 0)
+ goto done;
+ }
+ /* Actions between first and second child
+ */
+ switch (xs->xs_type){
+ case XP_EXP:
+ break;
+ case XP_AND:
+ break;
+ case XP_RELEX: /* relexpr --> addexpr | relexpr relop addexpr */
+ break;
+ case XP_ADD: /* combine mult and add ops */
+ break;
+ case XP_UNION:
+ break;
+ case XP_PATHEXPR:
+ break;
+ case XP_LOCPATH:
+ break;
+ case XP_ABSPATH:
+ use_xr0++;
+ /* Special case, no c0 or c1, single "/" */
+ if (xs->xs_c0 == NULL){
+ if ((xr0 = malloc(sizeof(*xr0))) == NULL){
+ clicon_err(OE_UNIX, errno, "malloc");
+ goto done;
+ }
+ memset(xr0, 0, sizeof(*xr0));
+ xr0->xc_initial = xc->xc_initial;
+ xr0->xc_type = XT_NODESET;
+ x = NULL;
+ while ((x = xml_child_each(xc->xc_node, x, CX_ELMNT)) != NULL) {
+ if (cxvec_append(x, &xr0->xc_nodeset, &xr0->xc_size) < 0)
+ goto done;
+ }
+ }
+ break;
+ case XP_RELLOCPATH:
+ use_xr0++;
+ if (xs->xs_int == A_DESCENDANT_OR_SELF)
+ xc->xc_descendant = 1; /* XXX need to set to 0 in sub */
+ break;
+ case XP_NODE:
+ break;
+ case XP_NODE_FN:
+ break;
+ case XP_PRI0:
+ break;
+ case XP_PRIME_NR: /* primaryexpr -> [] */
+ if ((xr0 = malloc(sizeof(*xr0))) == NULL){
+ clicon_err(OE_UNIX, errno, "malloc");
+ goto done;
+ }
+ memset(xr0, 0, sizeof(*xr0));
+ xr0->xc_initial = xc->xc_initial;
+ xr0->xc_type = XT_NUMBER;
+ xr0->xc_number = xs->xs_double;
+ break;
+ case XP_PRIME_STR:
+ if ((xr0 = malloc(sizeof(*xr0))) == NULL){
+ clicon_err(OE_UNIX, errno, "malloc");
+ goto done;
+ }
+ memset(xr0, 0, sizeof(*xr0));
+ xr0->xc_initial = xc->xc_initial;
+ xr0->xc_type = XT_STRING;
+ xr0->xc_string = xs->xs_s0?strdup(xs->xs_s0):NULL;
+ break;
+ case XP_PRIME_FN:
+ break;
+ default:
+ break;
+ }
+ /* Eval second child c0
+ * Note, some operators like locationpath, need transitive context (use_xr0)
+ */
+ if (xs->xs_c1)
+ if (xp_eval(use_xr0?xr0:xc, xs->xs_c1, nsc, &xr1) < 0)
+ goto done;
+ /* Actions after second child
+ */
+ if (xs->xs_c1)
+ switch (xs->xs_type){
+ case XP_AND: /* combine and and or ops */
+ if (xp_logop(xr0, xr1, xs->xs_int, &xr2) < 0)
+ goto done;
+ break;
+ case XP_RELEX: /* relexpr --> addexpr | relexpr relop addexpr */
+ if (xp_relop(xr0, xr1, xs->xs_int, &xr2) < 0)
+ goto done;
+ break;
+ case XP_ADD: /* combine mult and add ops */
+ if (xp_numop(xr0, xr1, xs->xs_int, &xr2) < 0)
+ goto done;
+ break;
+ case XP_UNION: /* combine and and or ops */
+ if (xp_union(xr0, xr1, xs->xs_int, &xr2) < 0)
+ goto done;
+ default:
+ break;
+ }
+ xc->xc_descendant = 0;
+ assert(xr0||xr1||xr2);
+ if (xr2){
+ *xrp = xr2;
+ xr2 = NULL;
+ }
+ else if (xr1){
+ *xrp = xr1;
+ xr1 = NULL;
+ }
+ else
+ if (xr0){
+ *xrp = xr0;
+ xr0 = NULL;
+ }
+ ok:
+ if (debug){
+ cbuf *cb;
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
+ goto done;
+ }
+ ctx_print(cb, -2, *xrp, xpath_tree_int2str(xs->xs_type));
+ clicon_debug(2, "%s", cbuf_get(cb));
+ cbuf_free(cb);
+ }
+ retval = 0;
+ done:
+ if (xr2)
+ ctx_free(xr2);
+ if (xr1)
+ ctx_free(xr1);
+ if (xr0)
+ ctx_free(xr0);
+ return retval;
+} /* xp_eval */
+
diff --git a/lib/src/clixon_xpath_eval.h b/lib/src/clixon_xpath_eval.h
new file mode 100644
index 00000000..5b74bb5a
--- /dev/null
+++ b/lib/src/clixon_xpath_eval.h
@@ -0,0 +1,49 @@
+/*
+ *
+ ***** BEGIN LICENSE BLOCK *****
+
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
+
+ This file is part of CLIXON.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ Alternatively, the contents of this file may be used under the terms of
+ the GNU General Public License Version 3 or later (the "GPL"),
+ in which case the provisions of the GPL are applicable instead
+ of those above. If you wish to allow use of your version of this file only
+ under the terms of the GPL, and not to allow others to
+ use your version of this file under the terms of Apache License version 2,
+ indicate your decision by deleting the provisions above and replace them with
+ the notice and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this file under
+ the terms of any one of the Apache License version 2 or the GPL.
+
+ ***** END LICENSE BLOCK *****
+
+ * Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10
+ */
+#ifndef _CLIXON_XPATH_EVAL_H
+#define _CLIXON_XPATH_EVAL_H
+
+/*
+ * Variables
+ */
+extern const map_str2int xpopmap[];
+
+/*
+ * Prototypes
+ */
+int xp_eval(xp_ctx *xc, xpath_tree *xs, cvec *nsc, xp_ctx **xrp);
+
+#endif /* _CLIXON_XPATH_EVAL_H */
diff --git a/lib/src/clixon_xpath_parse.h b/lib/src/clixon_xpath_parse.h
index 3e23e8c2..72ba992b 100644
--- a/lib/src/clixon_xpath_parse.h
+++ b/lib/src/clixon_xpath_parse.h
@@ -39,40 +39,6 @@
/*
* Types
*/
-/* used as non-terminal type in yacc rules */
-enum xp_type{
- XP_EXP,
- XP_AND,
- XP_RELEX,
- XP_ADD,
- XP_UNION,
- XP_PATHEXPR,
- XP_LOCPATH,
- XP_ABSPATH,
- XP_RELLOCPATH,
- XP_STEP,
- XP_NODE, /* s0 is namespace prefix, s1 is name */
- XP_NODE_FN,
- XP_PRED,
- XP_PRI0,
- XP_PRIME_NR,
- XP_PRIME_STR,
- XP_PRIME_FN,
-};
-
-/*! XPATH Parsing generates a tree of nodes that is later traversed
- */
-struct xpath_tree{
- enum xp_type xs_type;
- int xs_int;
- double xs_double;
- char *xs_s0;
- char *xs_s1;
- struct xpath_tree *xs_c0; /* child 0 */
- struct xpath_tree *xs_c1; /* child 1 */
-};
-typedef struct xpath_tree xpath_tree;
-
struct clicon_xpath_yacc_arg{ /* XXX: mostly unrelevant */
const char *xy_name; /* Name of syntax (for error string) */
int xy_linenum; /* Number of \n in parsed buffer */
diff --git a/lib/src/clixon_xpath_parse.l b/lib/src/clixon_xpath_parse.l
index 7fd27c38..180adc9c 100644
--- a/lib/src/clixon_xpath_parse.l
+++ b/lib/src/clixon_xpath_parse.l
@@ -57,6 +57,7 @@
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_xpath_parse.h"
+#include "clixon_xpath_eval.h"
/* Redefine main lex function so that you can send arguments to it: _yy is added to arg list */
#define YY_DECL int clixon_xpath_parselex(void *_yy)
diff --git a/lib/src/clixon_yang_type.c b/lib/src/clixon_yang_type.c
index a6b2fdd6..36818ecd 100644
--- a/lib/src/clixon_yang_type.c
+++ b/lib/src/clixon_yang_type.c
@@ -588,6 +588,105 @@ cv_validate_pattern(clicon_handle h,
(rmax && (i) > cv_##type##_get(rmax)))
+/*! Error messsage for int violating ranges
+ * @note contains kludge - duplicate loop
+ */
+static int
+outofrange(cg_var *cv0,
+ cvec *cvv,
+ char **reason)
+{
+ int retval = -1;
+ cbuf *cb = NULL;
+ cg_var *cv1;
+ cg_var *cv2;
+ int i;
+
+ if ((cb = cbuf_new()) == NULL)
+ goto done;
+ cprintf(cb, "Number ");
+ cv2cbuf(cv0, cb);
+ cprintf(cb, " out of range: ");
+ /* Kludge: need to repeat the same loop as in the main function in
+ cv_validate1 */
+ i = 0;
+ while (i2)
+ cprintf(cb, ", ");
+ cv2cbuf(cv1, cb);
+ cprintf(cb, " - ");
+ cv2cbuf(cv2, cb);
+ }
+ if (reason && (*reason = strdup(cbuf_get(cb))) == NULL)
+ goto done;
+ if (cb)
+ cbuf_free(cb);
+ retval = 0;
+ done:
+ return retval;
+}
+
+/*! Error messsage for string violating string limits
+ * @note contains kludge - duplicate loop
+ */
+static int
+outoflength(uint64_t u64,
+ cvec *cvv,
+ char **reason)
+{
+ int retval = -1;
+ cbuf *cb = NULL;
+ cg_var *cv1;
+ cg_var *cv2;
+ int i;
+
+ if ((cb = cbuf_new()) == NULL)
+ goto done;
+ cprintf(cb, "String length %" PRIu64 " out of range: ", u64);
+
+ /* Kludge: need to repeat the same loop as in the main function in
+ cv_validate1 */
+ i = 0;
+ while (i2)
+ cprintf(cb, ", ");
+ cv2cbuf(cv1, cb);
+ cprintf(cb, " - ");
+ cv2cbuf(cv2, cb);
+ }
+ if (reason && (*reason = strdup(cbuf_get(cb))) == NULL)
+ goto done;
+ if (cb)
+ cbuf_free(cb);
+ retval = 0;
+ done:
+ return retval;
+}
+
/*! Validate CLIgen variable
* @param[in] h Clicon handle
* @param[in] cv A cligen variable to validate. This is a correctly parsed cv.
@@ -600,6 +699,7 @@ cv_validate_pattern(clicon_handle h,
* @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
+ * @see cv_validate Corresponding type check in cligen
*/
static int
cv_validate1(clicon_handle h,
@@ -705,14 +805,13 @@ cv_validate1(clicon_handle h,
/* Check fails */
if (i==cvec_len(cvv)){ /* And it is last */
if (reason){
- if (reti)
- *reason = cligen_reason("Number out of range: %"
- PRId64, ii);
- else if (retu)
- *reason = cligen_reason("Number out of range: %"
- PRIu64, uu);
+ if (reti || retu){
+ if (outofrange(cv, cvv, reason) < 0)
+ goto done;
+ }
else
- *reason = cligen_reason("string length out of range: %" PRIu64, uu);
+ if (outoflength(uu, cvv, reason) < 0)
+ goto done;
}
goto fail;
}
@@ -1317,13 +1416,14 @@ yang_type_resolve(yang_stmt *yorig,
*
* @code
* yang_stmt *yrestype;
+ * char *origtype = NULL;
* int options;
* cvec *cvv = NULL;
* cvec *patterns = cvec_new(0);
* cvec *regexps = cvec_new(0);
* uint8_t fraction;
*
- * if (yang_type_get(ys, &type, &yrestype, &options, &cvv,
+ * if (yang_type_get(ys, &origtype, &yrestype, &options, &cvv,
* patterns, regexps, &fraction) < 0)
* goto err;
* if (yrestype == NULL) # unresolved
diff --git a/test/all.sh b/test/all.sh
index 6711558f..18ebde01 100755
--- a/test/all.sh
+++ b/test/all.sh
@@ -6,7 +6,8 @@
: ${pattern:=test_*.sh}
if [ $# -gt 0 ]; then
- echo "usage: $0 # detailed logs and stopon first error"
+ echo "usage: $0 # detailed logs and stop on first error. Use pattern=\"\" $0 to"
+ echo " Use pattern= $0 to narrow down test cases"
exit -1
fi
diff --git a/test/mem.sh b/test/mem.sh
index d9c0e1dd..f40f52cd 100755
--- a/test/mem.sh
+++ b/test/mem.sh
@@ -86,7 +86,20 @@ done
testnr=0
for c in $cmds; do
if [ $testnr != 0 ]; then echo; fi
- echo "Mem test for $c"
- echo "================="
+ echo "Mem test $c begin"
+ length=$(echo "Mem test $c begin" | wc -c)
+ let i=1
+ while [ $i -lt $length ]; do
+ echo -n "="
+ let i++
+ done
+ echo
memonce $c
+ echo "Mem test $c done"
+ let i=1
+ while [ $i -lt $length ]; do
+ echo -n "="
+ let i++
+ done
+ echo
done
diff --git a/test/test_augment.sh b/test/test_augment.sh
index a3a9d7ef..a5a61153 100755
--- a/test/test_augment.sh
+++ b/test/test_augment.sh
@@ -154,7 +154,7 @@ if [ $BE -ne 0 ]; then
start_backend -s init -f $cfg
new "waiting"
- sleep $RCWAIT
+ wait_backend
fi
# mandatory-leaf See RFC7950 Sec 7.17
diff --git a/test/test_choice.sh b/test/test_choice.sh
index 145f0dac..2e352993 100755
--- a/test/test_choice.sh
+++ b/test/test_choice.sh
@@ -124,7 +124,9 @@ new "start restconf daemon"
start_restconf -f $cfg
new "waiting"
-sleep $RCWAIT
+wait_backend
+wait_restconf
+
# First vanilla (protocol) case
new "netconf validate empty"
diff --git a/test/test_cli_history.sh b/test/test_cli_history.sh
index f6c50382..034daf1d 100755
--- a/test/test_cli_history.sh
+++ b/test/test_cli_history.sh
@@ -48,7 +48,7 @@ if [ $BE -ne 0 ]; then
start_backend -s init -f $cfg
new "waiting"
- sleep $RCWAIT
+ wait_backend
fi
new "cli read and add entry to existing history"
diff --git a/test/test_feature.sh b/test/test_feature.sh
index 98febbe5..619e0c1c 100755
--- a/test/test_feature.sh
+++ b/test/test_feature.sh
@@ -85,7 +85,7 @@ if [ $BE -ne 0 ]; then
start_backend -s init -f $cfg
new "waiting"
- sleep $RCWAIT
+ wait_backend
fi
new "cli enabled feature"
diff --git a/test/test_identity.sh b/test/test_identity.sh
index a31b1bbb..81f956e5 100755
--- a/test/test_identity.sh
+++ b/test/test_identity.sh
@@ -106,7 +106,37 @@ cat < $fyang
container aes-parameters {
when "../crypto = 'mc:aes'";
}
- }
+ identity acl-base;
+ typedef acl-type {
+ description "problem detected in ietf-access-control-list.yang";
+ type identityref {
+ base acl-base;
+ }
+ }
+ identity ipv4-acl-type {
+ base mc:acl-base;
+ }
+ identity ipv6-acl-type {
+ base mc:acl-base;
+ }
+ container acls {
+ list acl {
+ key name;
+ leaf name {
+ type string;
+ }
+ leaf type {
+ type acl-type;
+ }
+ }
+ }
+ identity empty; /* some errors with an empty identity set */
+ leaf e {
+ type identityref {
+ base mc:empty;
+ }
+ }
+ }
EOF
new "test params: -f $cfg"
@@ -120,7 +150,7 @@ if [ $BE -ne 0 ]; then
start_backend -s init -f $cfg
new "waiting"
- sleep $RCWAIT
+ wait_backend
fi
new "Set crypto to aes"
@@ -186,6 +216,43 @@ expectfn "$clixon_cli -1 -f $cfg -l o set crypto des:des3" 0 "^$"
new "cli validate"
expectfn "$clixon_cli -1 -f $cfg -l o validate" 0 "^$"
+new "Netconf set acl-type"
+expecteof "$clixon_netconf -qf $cfg" 0 'xmc:ipv4-acl-type]]>]]>' '^]]>]]>$'
+
+new "netconf validate "
+expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"
+
+new "Netconf set undefined acl-type"
+expecteof "$clixon_netconf -qf $cfg" 0 'xundefined]]>]]>' '^]]>]]>$'
+
+new "netconf validate fail"
+expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^applicationoperation-failederrorIdentityref validation failed, mc:undefined not derived from acl-base]]>]]>'
+
+new "netconf discard-changes"
+expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"
+
+new "CLI set acl-type"
+expectfn "$clixon_cli -1 -f $cfg -l o set acls acl x type mc:ipv4-acl-type" 0 "^$"
+
+new "cli validate"
+expectfn "$clixon_cli -1 -f $cfg -l o validate" 0 "^$"
+
+new "CLI set wrong acl-type"
+expectfn "$clixon_cli -1 -f $cfg -l o set acls acl x type undefined" 0 "^$"
+
+new "cli validate"
+expectfn "$clixon_cli -1 -f $cfg -l o validate" 255 "Identityref validation failed"
+
+# test empty identityref list
+new "cli set empty"
+expectfn "$clixon_cli -1 -f $cfg -l o set e undefined" 0 "^$"
+
+new "cli validate"
+expectfn "$clixon_cli -1 -f $cfg -l o validate" 255 "Identityref validation failed"
+
+new "netconf discard-changes"
+expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"
+
if [ $BE -eq 0 ]; then
exit # BE
fi
diff --git a/test/test_insert.sh b/test/test_insert.sh
index 9699b314..97a53327 100755
--- a/test/test_insert.sh
+++ b/test/test_insert.sh
@@ -26,7 +26,6 @@ cat < $cfg
/usr/local/var/$APPNAME/$APPNAME.sock
/usr/local/var/$APPNAME/$APPNAME.pidfile
$dir
- true
EOF
diff --git a/test/test_leafref.sh b/test/test_leafref.sh
index b67cb26e..ef8c62b5 100755
--- a/test/test_leafref.sh
+++ b/test/test_leafref.sh
@@ -125,7 +125,7 @@ if [ $BE -ne 0 ]; then
start_backend -s init -f $cfg
new "waiting"
- sleep $RCWAIT
+ wait_backend
fi
new "leafref base config"
diff --git a/test/test_minmax.sh b/test/test_minmax.sh
index 5c7faf5d..fd6da008 100755
--- a/test/test_minmax.sh
+++ b/test/test_minmax.sh
@@ -115,7 +115,7 @@ if [ $BE -ne 0 ]; then
start_backend -s init -f $cfg
new "waiting"
- sleep $RCWAIT
+ wait_backend
fi
new "minmax: minimal"
diff --git a/test/test_nacm.sh b/test/test_nacm.sh
index b7b914ae..c50308ab 100755
--- a/test/test_nacm.sh
+++ b/test/test_nacm.sh
@@ -124,16 +124,15 @@ fi
new "kill old restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf"
-sleep 1
new "start restconf daemon (-a is enable basic authentication)"
start_restconf -f $cfg -- -a
new "waiting"
-sleep $RCWAIT
+wait_backend
+wait_restconf
new "auth get"
-expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 'null
-
'
+expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-type": "application","error-tag": "invalid-value","error-severity": "error","error-message": "Instance does not exist"}}}}
'
# explicitly disable nacm (regression on netgate bug)
new "disable nacm"
diff --git a/test/test_nacm_default.sh b/test/test_nacm_default.sh
index 715ee306..5916ffa4 100755
--- a/test/test_nacm_default.sh
+++ b/test/test_nacm_default.sh
@@ -101,14 +101,12 @@ EOF
new "kill old restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf"
- sleep 1
new "start restconf daemon (-a is enable basic authentication)"
start_restconf -f $cfg -- -a
new "waiting"
wait_backend
wait_restconf
-
#----------- First get
case "$ret1" in
@@ -117,8 +115,7 @@ EOF
;;
1) ret='{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}}
'
;;
- 2) ret='null
-
'
+ 2) ret='{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-type": "application","error-tag": "invalid-value","error-severity": "error","error-message": "Instance does not exist"}}}}
'
;;
esac
@@ -142,8 +139,7 @@ EOF
;;
1) ret='{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}}
'
;;
- 2) ret='null
-
'
+ 2) ret='{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-type": "application","error-tag": "invalid-value","error-severity": "error","error-message": "Instance does not exist"}}}}
'
;;
3) ret='{"nacm-example:x": 42}
'
diff --git a/test/test_nacm_module_read.sh b/test/test_nacm_module_read.sh
index b32774c0..1d87a9c1 100755
--- a/test/test_nacm_module_read.sh
+++ b/test/test_nacm_module_read.sh
@@ -139,12 +139,12 @@ fi
new "kill old restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf"
-sleep 1
new "start restconf daemon (-a is enable basic authentication)"
start_restconf -f $cfg -- -a
new "waiting"
-sleep $RCWAIT
+wait_backend
+wait_restconf
new "auth set authentication config"
expecteof "$clixon_netconf -qf $cfg" 0 "$RULES]]>]]>" "^]]>]]>$"
@@ -200,8 +200,7 @@ expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-e
'
new "limit read other module fail"
-expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 'null
-
'
+expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-type": "application","error-tag": "invalid-value","error-severity": "error","error-message": "Instance does not exist"}}}}
'
new "limit read state OK"
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:state)" 0 '{"clixon-example:state": {"op": ["42","41","43"]}}
diff --git a/test/test_nacm_module_write.sh b/test/test_nacm_module_write.sh
index d1bbe6d0..a88951e8 100755
--- a/test/test_nacm_module_write.sh
+++ b/test/test_nacm_module_write.sh
@@ -148,12 +148,12 @@ fi
new "kill old restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf"
-sleep 1
new "start restconf daemon (-a is enable basic authentication)"
start_restconf -f $cfg -- -a
new "waiting"
-sleep $RCWAIT
+wait_backend
+wait_restconf
# Set nacm from scratch
nacm(){
diff --git a/test/test_nacm_protocol.sh b/test/test_nacm_protocol.sh
index c9092ac9..59ce33c7 100755
--- a/test/test_nacm_protocol.sh
+++ b/test/test_nacm_protocol.sh
@@ -148,12 +148,12 @@ fi
new "kill old restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf"
-sleep 1
new "start restconf daemon (-a is enable basic authentication)"
start_restconf -f $cfg -- -a
new "waiting"
-sleep $RCWAIT
+wait_backend
+wait_restconf
new "auth set authentication config"
expecteof "$clixon_netconf -qf $cfg" 0 "$RULES]]>]]>" "^]]>]]>$"
@@ -191,9 +191,8 @@ expecteq "$(curl -u guest:bar -sS -X DELETE http://localhost/restconf/data)" 0 '
new "deny-delete-config: limited fail (restconf) ok"
expecteq "$(curl -u wilma:bar -sS -X DELETE http://localhost/restconf/data)" 0 ''
-new "admin get nacm (should be null)"
-expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 'null
-
'
+new "admin get nacm (should fail)"
+expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-type": "application","error-tag": "invalid-value","error-severity": "error","error-message": "Instance does not exist"}}}}
'
new "deny-delete-config: admin ok (restconf)"
expecteq "$(curl -u andy:bar -sS -X DELETE http://localhost/restconf/data)" 0 ''
diff --git a/test/test_order.sh b/test/test_order.sh
index 9bb23462..0f4ea9f3 100755
--- a/test/test_order.sh
+++ b/test/test_order.sh
@@ -162,7 +162,7 @@ if [ $BE -ne 0 ]; then
start_backend -s running -f $cfg -- -s
new "waiting"
- sleep $RCWAIT
+ wait_backend
fi
# STATE (should not be ordered)
diff --git a/test/test_restconf2.sh b/test/test_restconf2.sh
index 84aba720..ec1486d7 100755
--- a/test/test_restconf2.sh
+++ b/test/test_restconf2.sh
@@ -137,7 +137,7 @@ new "restconf DELETE"
expectfn 'curl -s -X DELETE http://localhost/restconf/data/example:cont1' 0 ""
new "restconf GET null datastore"
-expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1" 0 'null'
+expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1" 0 '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-type": "application","error-tag": "invalid-value","error-severity": "error","error-message": "Instance does not exist"}}}}'
new "restconf POST initial tree"
expectfn 'curl -s -X POST -d {"example:cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' 0 ""
@@ -149,7 +149,7 @@ new "restconf DELETE whole datastore"
expectfn 'curl -s -X DELETE http://localhost/restconf/data' 0 ""
new "restconf GET null datastore"
-expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1" 0 'null'
+expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1" 0 '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-type": "application","error-tag": "invalid-value","error-severity": "error","error-message": "Instance does not exist"}}}}'
new "restconf PUT initial datastore"
expectfn 'curl -s -X PUT -d {"data":{"example:cont1":{"interface":{"name":"local0","type":"regular"}}}} http://localhost/restconf/data' 0 ""
diff --git a/test/test_restconf_err.sh b/test/test_restconf_err.sh
new file mode 100755
index 00000000..fb5e27da
--- /dev/null
+++ b/test/test_restconf_err.sh
@@ -0,0 +1,120 @@
+#!/bin/bash
+# Restconf error-code functionality
+# See RFC8040
+# Testcases:
+# Sec 4.3 (GET): If a retrieval request for a data resource represents an
+# instance that does not exist, then an error response containing a "404 Not
+# Found" status-line MUST be returned by the server. The error-tag
+# value "invalid-value" is used in this case.
+
+# Magic line must be first in script (see README.md)
+s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
+
+APPNAME=example
+
+cfg=$dir/conf.xml
+fyang=$dir/restconf.yang
+fxml=$dir/initial.xml
+
+# example
+cat < $cfg
+
+ $cfg
+ /usr/local/share/clixon
+ $IETFRFC
+ $fyang
+ false
+ /usr/local/var/$APPNAME/$APPNAME.sock
+ $dir/restconf.pidfile
+ /usr/local/var/$APPNAME
+
+EOF
+
+cat < $fyang
+module example{
+ yang-version 1.1;
+ namespace "urn:example:clixon";
+ prefix ex;
+ list a {
+ key k;
+ leaf k {
+ type int32;
+ }
+ leaf description{
+ type string;
+ }
+ leaf b{
+ type string;
+ }
+ container c{
+ presence "for test";
+ }
+ list d{
+ key k;
+ leaf k {
+ type string;
+ }
+ }
+ }
+}
+EOF
+
+# Initial tree
+XML=$(cat <0No leaf b, No container c, No leaf d
+EOF
+ )
+
+new "test params: -f $cfg"
+
+if [ $BE -ne 0 ]; then
+ new "kill old backend"
+ sudo clixon_backend -zf $cfg
+ if [ $? -ne 0 ]; then
+ err
+ fi
+ sudo pkill clixon_backend # to be sure
+ new "start backend -s init -f $cfg"
+ start_backend -s init -f $cfg
+fi
+
+new "kill old restconf daemon"
+sudo pkill -u www-data -f "/www-data/clixon_restconf"
+
+new "start restconf daemon"
+start_restconf -f $cfg
+
+new "waiting"
+wait_backend
+wait_restconf
+
+new "restconf POST initial tree"
+expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' -d "$XML" http://localhost/restconf/data)" 0 ''
+
+new "restconf GET initial datastore"
+expecteq "$(curl -s -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf/data/example:a)" 0 "$XML
+
"
+
+new "restconf GET non-existent container header"
+expectfn "curl -s -I -X GET http://localhost/restconf/data/example:a/c" 0 "HTTP/1.1 404 Not Found"
+
+new "restconf GET non-existent container body"
+expectfn "curl -s -X GET http://localhost/restconf/data/example:a/c" 0 '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-type": "application","error-tag": "invalid-value","error-severity": "error","error-message": "Instance does not exist"}}}}'
+
+new "Kill restconf daemon"
+stop_restconf
+
+if [ $BE -eq 0 ]; then
+ exit # BE
+fi
+
+new "Kill backend"
+# Check if premature kill
+pid=`pgrep -u root -f clixon_backend`
+if [ -z "$pid" ]; then
+ err "backend already dead"
+fi
+# kill backend
+stop_backend -f $cfg
+
+rm -rf $dir
diff --git a/test/test_restconf_startup.sh b/test/test_restconf_startup.sh
index 36037af8..2513c7a8 100755
--- a/test/test_restconf_startup.sh
+++ b/test/test_restconf_startup.sh
@@ -68,7 +68,8 @@ testrun(){
start_restconf -f $cfg -y $fyang $option
new "waiting"
- sleep $RCWAIT
+ wait_backend
+ wait_restconf
new "restconf put 42"
expecteq "$(curl -s -X PUT http://localhost/restconf/data/example:x/y=42 -d '{"example:y":{"a":"42","b":"42"}}')" 0 ""
diff --git a/test/test_rpc.sh b/test/test_rpc.sh
index 64ed55c3..8190608a 100755
--- a/test/test_rpc.sh
+++ b/test/test_rpc.sh
@@ -50,7 +50,9 @@ new "start restconf daemon"
start_restconf -f $cfg
new "waiting"
-sleep $RCWAIT
+wait_backend
+wait_restconf
+
new "rpc tests"
diff --git a/test/test_startup.sh b/test/test_startup.sh
index 691c7df7..afb35ed8 100755
--- a/test/test_startup.sh
+++ b/test/test_startup.sh
@@ -86,7 +86,7 @@ testrun(){
start_backend -s $mode -f $cfg -c $dir/extra_db
new "waiting"
- sleep $RCWAIT
+ wait_backend
else
new "Restart backend as eg follows: -Ff $cfg -s $mode -c $dir/extra_db # $BETIMEOUT s"
sleep $BETIMEOUT
diff --git a/test/test_transaction.sh b/test/test_transaction.sh
index 69bddea0..fefe6122 100755
--- a/test/test_transaction.sh
+++ b/test/test_transaction.sh
@@ -121,8 +121,9 @@ if [ $BE -ne 0 ]; then
fi
new "start backend -s init -f $cfg -l f$flog -- -t /x/y[a=$errnr]"
start_backend -s init -f $cfg -l f$flog -- -t /x/y[a=$errnr] # -t means transaction logging
+
new "waiting"
- sleep $RCWAIT
+ wait_backend
fi
let nr=0
@@ -177,7 +178,7 @@ new "3. Validate system-error config (9999 not in range)"
expecteof "$clixon_netconf -qf $cfg" 0 "$nr9999]]>]]>" '^]]>]]>$'
new "Validate system-error validate (should fail)"
-expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' '^applicationbad-elementberrorNumber out of range: 9999]]>]]>$'
+expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' '^applicationbad-elementberrorNumber 9999 out of range: 0 - 100]]>]]>$'
new "Validate system-error discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"
diff --git a/test/test_type.sh b/test/test_type.sh
index fb3e9aaa..96ac57da 100755
--- a/test/test_type.sh
+++ b/test/test_type.sh
@@ -192,6 +192,10 @@ module example{
pattern '[a-zA-Z_][a-zA-Z0-9_\-.]*';
}
}
+ leaf bool {
+ description "For testing different truth values in CLI";
+ type boolean;
+ }
}
EOF
@@ -231,14 +235,14 @@ EOF
start_backend -s init -f $cfg
new "waiting"
- sleep $RCWAIT
+ wait_backend
fi
new "cli set transitive string. type is alpha followed by number and is defined in three levels of modules"
expectfn "$clixon_cli -1f $cfg -l o set c talle x99" 0 '^$'
new "cli set transitive string error. Wrong type"
- expectfn "$clixon_cli -1f $cfg -l o set c talle 9xx" 255 '^CLI syntax error: "set c talle 9xx": Unknown command$'
+ expectfn "$clixon_cli -1f $cfg -l o set c talle 9xx" 255 '^CLI syntax error: "set c talle 9xx": regexp match fail: 9xx does not match \[a-z\]\[0-9\]\*$'
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"
@@ -266,10 +270,10 @@ EOF
expectfn "$clixon_cli -1f $cfg -l o -l o validate" 0 '^$'
new "cli set transitive union error. should fail"
- expectfn "$clixon_cli -1f $cfg -l o set c ulle kalle" 255 '^CLI syntax error: "set c ulle kalle": Unknown command$'
+ expectfn "$clixon_cli -1f $cfg -l o set c ulle kalle" 255 "^CLI syntax error: \"set c ulle kalle\": 'kalle' is not a number$"
new "cli set transitive union error int"
- expectfn "$clixon_cli -1f $cfg -l o set c ulle 55" 255 '^CLI syntax error: "set c ulle 55": Unknown command$'
+ expectfn "$clixon_cli -1f $cfg -l o set c ulle 55" 255 '^CLI syntax error: "set c ulle 55": Number 55 out of range: 4 - 44$'
new "netconf set transitive union error int"
expecteof "$clixon_netconf -qf $cfg" 0 '55]]>]]>' "^]]>]]>"
@@ -354,13 +358,13 @@ EOF
#expectfn "$clixon_cli -1f $cfg -l o set num1 \-100" 0 '^$'
new "cli range test num1 2 error"
- expectfn "$clixon_cli -1f $cfg -l o set num1 2" 255 '^CLI syntax error: "set num1 2": Unknown command$'
+ expectfn "$clixon_cli -1f $cfg -l o set num1 2" 255 '^CLI syntax error: "set num1 2": Number 2 out of range: 1 - 1$'
new "netconf range set num1 -1"
expecteof "$clixon_netconf -qf $cfg" 0 '-1]]>]]>' "^]]>]]>$"
new "netconf validate num1 -1 wrong"
- expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^applicationbad-elementnum1errorNumber out of range: -1]]>]]>$'
+ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^applicationbad-elementnum1errorNumber -1 out of range: 1 - 1]]>]]>$'
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"
@@ -368,19 +372,19 @@ EOF
#-------- num2 range and blanks
new "cli range test num2 3 error"
- expectfn "$clixon_cli -1f $cfg -l o set num2 3" 255 '^CLI syntax error: "set num2 3": Number out of range: 3$'
+ expectfn "$clixon_cli -1f $cfg -l o set num2 3" 255 '^CLI syntax error: "set num2 3": Number 3 out of range: 4 - 4000$'
new "cli range test num2 1000 ok"
expectfn "$clixon_cli -1f $cfg -l o set num2 1000" 0 '^$'
new "cli range test num2 5000 error"
- expectfn "$clixon_cli -1f $cfg -l o set num2 5000" 255 '^CLI syntax error: "set num2 5000": Unknown command$'
+ expectfn "$clixon_cli -1f $cfg -l o set num2 5000" 255 '^CLI syntax error: "set num2 5000": Number 5000 out of range: 4 - 4000$'
new "netconf range set num2 3 fail"
expecteof "$clixon_netconf -qf $cfg" 0 '3]]>]]>' "^]]>]]>$"
new "netconf validate num2 3 fail"
- expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^applicationbad-elementnum2errorNumber out of range: 3]]>]]>$'
+ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^applicationbad-elementnum2errorNumber 3 out of range: 4 - 4000]]>]]>$'
new "netconf range set num2 1000 ok"
expecteof "$clixon_netconf -qf $cfg" 0 '1000]]>]]>' "^]]>]]>$"
@@ -392,7 +396,7 @@ EOF
expecteof "$clixon_netconf -qf $cfg" 0 '5000]]>]]>' "^]]>]]>$"
new "netconf validate num2 5000 fail"
- expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^applicationbad-elementnum2errorNumber out of range: 5000]]>]]>$'
+ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^applicationbad-elementnum2errorNumber 5000 out of range: 4 - 4000]]>]]>$'
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"
@@ -403,7 +407,7 @@ EOF
expectfn "$clixon_cli -1f $cfg -l o set num3 42" 0 '^$'
new "cli range test num3 260 fail"
- expectfn "$clixon_cli -1f $cfg -l o set num3 260" 255 '^CLI syntax error: "set num3 260": Unknown command$'
+ expectfn "$clixon_cli -1f $cfg -l o set num3 260" 255 '^CLI syntax error: "set num3 260": Number 260 out of range: 0 - 255$'
new "cli range test num3 -1 fail"
expectfn "$clixon_cli -1f $cfg -l o set num3 -1" 255 "CLI syntax error:"
@@ -412,7 +416,7 @@ EOF
expecteof "$clixon_netconf -qf $cfg" 0 '260]]>]]>' "^]]>]]>$"
new "netconf validate num3 260 fail"
- expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^applicationbad-elementnum3error260 is out of range(type is uint8)]]>]]>$'
+ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^applicationbad-elementnum3errorNumber 260 out of range: 0 - 255]]>]]>$'
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"
@@ -420,19 +424,19 @@ EOF
#-------- num4 multiple ranges 1..2 | 42..50
new "cli range test num4 multiple 0 fail"
- expectfn "$clixon_cli -1f $cfg -l o set num4 0" 255 '^CLI syntax error: "set num4 0": Number out of range: 0$'
+ expectfn "$clixon_cli -1f $cfg -l o set num4 0" 255 '^CLI syntax error: "set num4 0": Number 0 out of range: 1 - 2, 42 - 50$'
new "cli range test num4 multiple 2 ok"
expectfn "$clixon_cli -1f $cfg -l e set num4 2" 0 '^$'
new "cli range test num4 multiple 20 fail"
- expectfn "$clixon_cli -1f $cfg -l o set num4 20" 255 '^CLI syntax error: "set num4 20": Unknown command$'
+ expectfn "$clixon_cli -1f $cfg -l o set num4 20" 255 '^CLI syntax error: "set num4 20": Number 20 out of range: 1 - 2, 42 - 50$'
new "cli range test num4 multiple 42 ok"
expectfn "$clixon_cli -1f $cfg -l o set num4 42" 0 '^$'
new "cli range test num4 multiple 99 fail"
- expectfn "$clixon_cli -1f $cfg -l o set num4 99" 255 '^CLI syntax error: "set num4 99": Unknown command$'
+ expectfn "$clixon_cli -1f $cfg -l o set num4 99" 255 '^CLI syntax error: "set num4 99": Number 99 out of range: 1 - 2, 42 - 50$'
new "netconf range set num4 multiple 2"
expecteof "$clixon_netconf -qf $cfg" 0 '42]]>]]>' "^]]>]]>$"
@@ -467,7 +471,7 @@ EOF
expectfn "$clixon_cli -1f $cfg -l o set dec 15.0" 0 '^$'
new "cli range dec64 multiple 30.0 fail"
- expectfn "$clixon_cli -1f $cfg -l o set dec 30.0" 255 '^CLI syntax error: "set dec 30.0": Unknown command$'
+ expectfn "$clixon_cli -1f $cfg -l o set dec 30.0" 255 '^CLI syntax error: "set dec 30.0": Number 30.000 out of range: -3.500 - -2.500, 0.000 - 0.000, 10.000 - 20.000$'
new "dec64 discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"
@@ -477,7 +481,7 @@ EOF
expecteof "$clixon_netconf -qf $cfg" 0 '-3.59]]>]]>' "^]]>]]>$"
new "netconf range dec64 -3.59 validate fail"
- expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^applicationbad-elementdecerrorNumber out of range'
+ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^applicationbad-elementdecerrorNumber -3.590 out of range'
new "netconf range dec64 -3.5"
expecteof "$clixon_netconf -qf $cfg" 0 '-3.500]]>]]>' "^]]>]]>$"
@@ -489,13 +493,13 @@ EOF
expecteof "$clixon_netconf -qf $cfg" 0 '-2]]>]]>' "^]]>]]>$"
new "netconf range dec64 -2 validate fail"
- expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^applicationbad-elementdecerrorNumber out of range'
+ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^applicationbad-elementdecerrorNumber -2.000 out of range'
new "netconf range dec64 -0.001"
expecteof "$clixon_netconf -qf $cfg" 0 '-0.001]]>]]>' "^]]>]]>$"
new "netconf range dec64 -0.001 validate fail"
- expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^applicationbad-elementdecerrorNumber out of range'
+ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^applicationbad-elementdecerrorNumber -0.001 out of range'
new "netconf range dec64 0.0"
expecteof "$clixon_netconf -qf $cfg" 0 '0.0]]>]]>' "^]]>]]>$"
@@ -507,18 +511,18 @@ EOF
expecteof "$clixon_netconf -qf $cfg" 0 '+0.001]]>]]>' "^]]>]]>$"
new "netconf range dec64 +0.001 validate fail"
- expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^applicationbad-elementdecerrorNumber out of range'
+ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^applicationbad-elementdecerrorNumber 0.001 out of range'
#----------------string ranges---------------------
#-------- len1 single range (2)
new "cli length test len1 1 fail"
- expectfn "$clixon_cli -1f $cfg -l o set len1 x" 255 '^CLI syntax error: "set len1 x": String length not within limits: 1$'
+ expectfn "$clixon_cli -1f $cfg -l o set len1 x" 255 '^CLI syntax error: "set len1 x": String length 1 out of range: 2 - 2$'
new "cli length test len1 2 OK"
expectfn "$clixon_cli -1f $cfg -l o set len1 xy" 0 '^$'
new "cli length test len1 3 error"
- expectfn "$clixon_cli -1f $cfg -l o set len1 hej" 255 '^CLI syntax error: "set len1 hej": Unknown command$'
+ expectfn "$clixon_cli -1f $cfg -l o set len1 hej" 255 '^CLI syntax error: "set len1 hej": String length 3 out of range: 2 - 2$'
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"
@@ -527,12 +531,12 @@ EOF
expecteof "$clixon_netconf -qf $cfg" 0 'x]]>]]>' "^]]>]]>$"
new "netconf validate len1 1 wrong"
- expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^applicationbad-elementlen1errorstring length out of range: 1]]>]]>$'
+ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^applicationbad-elementlen1errorString length 1 out of range: 2 - 2]]>]]>$'
#-------- len2 range and blanks
new "cli length test len2 3 error"
- expectfn "$clixon_cli -1f $cfg -l o set len2 ab" 255 '^CLI syntax error: "set len2 ab": String length not within limits: 2$'
+ expectfn "$clixon_cli -1f $cfg -l o set len2 ab" 255 '^CLI syntax error: "set len2 ab": String length 2 out of range: 4 - 4000$'
new "cli length test len2 42 ok"
expectfn "$clixon_cli -1f $cfg -l o set len2 hejhophdsakjhkjsadhkjsahdkjsad" 0 '^$'
@@ -547,32 +551,32 @@ EOF
#-------- len4 multiple ranges 2..3 | 20-29
new "cli length test len4 1 error"
- expectfn "$clixon_cli -1f $cfg -l o set len4 a" 255 '^CLI syntax error: "set len4 a": String length not within limits: 1$'
+ expectfn "$clixon_cli -1f $cfg -l o set len4 a" 255 '^CLI syntax error: "set len4 a": String length 1 out of range: 2 - 3, 20 - 29$'
new "cli length test len4 2 ok"
expectfn "$clixon_cli -1f $cfg -l o set len4 ab" 0 '^$'
new "cli length test len4 10 error"
- expectfn "$clixon_cli -1f $cfg -l o set len4 abcdefghij" 255 '^CLI syntax error: "set len4 abcdefghij": Unknown command$'
+ expectfn "$clixon_cli -1f $cfg -l o set len4 abcdefghij" 255 '^CLI syntax error: "set len4 abcdefghij": String length 10 out of range: 2 - 3, 20 - 29$'
new "cli length test len4 20 ok"
expectfn "$clixon_cli -1f $cfg -l o set len4 abcdefghijabcdefghija" 0 '^$'
new "cli length test len4 30 error"
- expectfn "$clixon_cli -1f $cfg -l o set len4 abcdefghijabcdefghijabcdefghij" 255 '^CLI syntax error: "set len4 abcdefghijabcdefghijabcdefghij": Unknown command$'
+ expectfn "$clixon_cli -1f $cfg -l o set len4 abcdefghijabcdefghijabcdefghij" 255 '^CLI syntax error: "set len4 abcdefghijabcdefghijabcdefghij": String length 30 out of range: 2 - 3, 20 - 29$'
# XSD schema -> POSIX ECE translation
new "cli yang pattern \d ok"
expectfn "$clixon_cli -1f $cfg -l o set digit4 0123" 0 '^$'
new "cli yang pattern \d error"
- expectfn "$clixon_cli -1f $cfg -l o set digit4 01b2" 255 '^CLI syntax error: "set digit4 01b2": Unknown command$'
+ expectfn "$clixon_cli -1f $cfg -l o set digit4 01b2" 255 '^CLI syntax error: "set digit4 01b2": regexp match fail: 01b2 does not match'
new "cli yang pattern \w ok"
expectfn "$clixon_cli -1f $cfg -l o set word4 abc9" 0 '^$'
new "cli yang pattern \w error"
- expectfn "$clixon_cli -1f $cfg -l o set word4 ab%3" 255 '^CLI syntax error: "set word4 ab%3": Unknown command$'
+ expectfn "$clixon_cli -1f $cfg -l o set word4 ab%3" 255 '^CLI syntax error: "set word4 ab%3": regexp match fail: ab%3 does not match'
new "netconf pattern \w"
expecteof "$clixon_netconf -qf $cfg" 0 'aXG9]]>]]>' "^]]>]]>$"
@@ -589,7 +593,6 @@ EOF
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"
-
#------ minus
new "type with minus"
@@ -601,6 +604,27 @@ EOF
#new "cli type with minus"
#expectfn "$clixon_cli -1f $cfg -l o set name my-name" 0 '^$'
+ #------ cli truth-values: true/on/enable false/off/disable
+
+ new "cli truth: true"
+ expectfn "$clixon_cli -1f $cfg -l o set bool true" 0 '^$'
+ new "cli truth: false"
+ expectfn "$clixon_cli -1f $cfg -l o set bool false" 0 '^$'
+ new "cli truth: on"
+ expectfn "$clixon_cli -1f $cfg -l o set bool on" 0 '^$'
+ new "cli verify on translates to true"
+ expectfn "$clixon_cli -1f $cfg -l o show conf" 0 'bool true;'
+ new "cli truth: off"
+ expectfn "$clixon_cli -1f $cfg -l o set bool off" 0 '^$'
+ new "cli verify off translates to false"
+ expectfn "$clixon_cli -1f $cfg -l o show conf" 0 'bool false;'
+ new "cli truth: enable"
+ expectfn "$clixon_cli -1f $cfg -l o set bool enable" 0 '^$'
+ new "cli truth: disable"
+ expectfn "$clixon_cli -1f $cfg -l o set bool disable" 0 '^$'
+ new "cli truth: wrong"
+ expectfn "$clixon_cli -1f $cfg -l o set bool wrong" 255 "'wrong' is not a boolean value"
+
if [ $BE -ne 0 ]; then
new "Kill backend"
# Check if premature kill
@@ -619,7 +643,7 @@ testrun nocache
# Run with db cache
testrun cache
-# Run with
-testrun cache-zerocopy
+# Run with zero-copy XXX does not work
+#testrun cache-zerocopy
rm -rf $dir
diff --git a/test/test_type_range.sh b/test/test_type_range.sh
new file mode 100755
index 00000000..0760d485
--- /dev/null
+++ b/test/test_type_range.sh
@@ -0,0 +1,317 @@
+#!/bin/bash
+# Range type tests.
+# Mainly error messages and multiple ranges
+# Tests all int types including decimal64 and string length ranges
+# See also test_type.sh
+
+# Magic line must be first in script (see README.md)
+s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
+
+APPNAME=example
+
+# Which format to use as datastore format internally
+: ${format:=xml}
+
+cfg=$dir/conf_yang.xml
+fyang=$dir/type.yang
+dclispec=$dir/clispec/
+
+# XXX: add more types, now only uint8 and int8
+cat < $fyang
+module example{
+ yang-version 1.1;
+ namespace "urn:example:clixon";
+ prefix ex;
+ typedef tint8{
+ type int8{
+ range "1..10 | 14..20";
+ }
+ }
+ typedef tint16{
+ type int16{
+ range "1..10 | 14..20";
+ }
+ }
+ typedef tint32{
+ type int32{
+ range "1..10 | 14..20";
+ }
+ }
+ typedef tint64{
+ type int64{
+ range "1..10 | 14..20";
+ }
+ }
+ typedef tuint8{
+ type uint8{
+ range "1..10 | 14..20";
+ }
+ }
+ typedef tuint16{
+ type uint16{
+ range "1..10 | 14..20";
+ }
+ }
+ typedef tuint32{
+ type uint32{
+ range "1..10 | 14..20";
+ }
+ }
+ typedef tuint64{
+ type uint64{
+ range "1..10 | 14..20";
+ }
+ }
+ typedef tdecimal64{
+ type decimal64{
+ fraction-digits 3;
+ range "1..10 | 14..20";
+ }
+ }
+ typedef tstring{
+ type string{
+ length "1..10 | 14..20";
+ }
+ }
+ /* here follows constrained ints */
+ leaf lint8 {
+ type tint8;
+ }
+ leaf lint16 {
+ type tint16;
+ }
+ leaf lint32 {
+ type tint32;
+ }
+ leaf lint64 {
+ type tint64;
+ }
+ leaf luint8 {
+ type tuint8;
+ }
+ leaf luint16 {
+ type tuint16;
+ }
+ leaf luint32 {
+ type tuint32;
+ }
+ leaf luint64 {
+ type tuint64;
+ }
+ leaf ldecimal64 {
+ type tdecimal64;
+ }
+ leaf lstring {
+ type tstring;
+ }
+ /* here follows unlimited ints */
+ leaf rint8 {
+ type int8;
+ }
+ leaf rint16 {
+ type int16;
+ }
+ leaf rint32 {
+ type int32;
+ }
+ leaf rint64 {
+ type int64;
+ }
+ leaf ruint8 {
+ type uint8;
+ }
+ leaf ruint16 {
+ type uint16;
+ }
+ leaf ruint32 {
+ type uint32;
+ }
+ leaf ruint64 {
+ type uint64;
+ }
+ leaf rdecimal64 {
+ type decimal64{
+ fraction-digits 3;
+ }
+ }
+}
+EOF
+
+mkdir $dclispec
+
+# clispec for both generated cli and a hardcoded range check
+cat < $dclispec/clispec.cli
+ CLICON_MODE="example";
+ CLICON_PROMPT="%U@%H> ";
+ CLICON_PLUGIN="example_cli";
+
+ # Manually added (not generated)
+ manual hint8 ;
+ manual hint16 ;
+ manual hint32 ;
+ manual hint64 ;
+
+ manual huint8 ;
+ manual huint16 ;
+ manual huint32 ;
+ manual huint64 ;
+
+ manual hdecimal64 ;
+
+ manual hstring ;
+
+ # Generated cli
+ set @datamodel, cli_set();
+ merge @datamodel, cli_merge();
+ create @datamodel, cli_create();
+ show, cli_show_config("candidate", "text", "/");
+ quit("Quit"), cli_quit();
+EOF
+
+cat < $cfg
+
+ $cfg
+ $dir
+ /usr/local/share/clixon
+ $IETFRFC
+ $fyang
+ $dclispec
+ /usr/local/lib/$APPNAME/cli
+ $APPNAME
+ /usr/local/var/$APPNAME/$APPNAME.sock
+ /usr/local/var/$APPNAME/$APPNAME.pidfile
+ 1
+ /usr/local/var/$APPNAME
+ $format
+
+EOF
+
+# Type range tests.
+# Parameters: 1: type (eg uint8)
+# 2: val OK
+# 3: eval Invalid value
+# 4: post (eg .000 - special for decimal64, others should have "")
+testrange(){
+ t=$1
+ val=$2
+ eval=$3
+ post=$4
+
+ if [ $t = "string" ]; then # special case for string type error msg
+ len=$(echo -n "$eval" | wc -c)
+ errmsg="String length $len out of range: 1$post - 10$post, 14$post - 20$post"
+ else
+ errmsg="Number $eval$post out of range: 1$post - 10$post, 14$post - 20$post"
+ fi
+
+ new "generated cli set $t leaf invalid"
+ expectfn "$clixon_cli -1f $cfg -l o set l$t $eval" 255 "$errmsg";
+
+ new "generated cli set $t leaf OK"
+ expectfn "$clixon_cli -1f $cfg -l o set l$t $val" 0 '^$'
+
+ # XXX Error in cligen order: Unknown command vs Number out of range
+ # olof@vandal> set luint8 0
+ # CLI syntax error: "set luint8 0": Number 0 is out of range: 14 - 20
+ # olof@vandal> set luint8 1
+ # olof@vandal> set luint8 0
+ # CLI syntax error: "set luint8 0": Unknown command
+# (SAME AS FIRST ^)
+ new "generated cli set $t leaf invalid"
+ expectfn "$clixon_cli -1f $cfg -l o set l$t $eval" 255 "$errmsg"
+
+ new "manual cli set $t leaf OK"
+ expectfn "$clixon_cli -1f $cfg -l o man h$t $val" 0 '^$'
+
+ new "manual cli set $t leaf invalid"
+ echo "$clixon_cli -1f $cfg -l o set h$t $eval"
+ expectfn "$clixon_cli -1f $cfg -l o set l$t $eval" 255 "$errmsg"
+
+ new "discard"
+ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"
+
+ new "Netconf set invalid $t leaf"
+ expecteof "$clixon_netconf -qf $cfg" 0 "$eval]]>]]>" "^]]>]]>$"
+
+ new "netconf get config"
+ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^$eval]]>]]>$"
+
+ new "netconf validate invalid range"
+ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^applicationbad-elementl$terror$errmsg]]>]]>$"
+
+ new "discard"
+ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"
+}
+
+# Type unlimited value range test. Only test invalid number out of range of type
+# Parameters: 1: type (eg uint8)
+# 2: val
+# 3: post (eg .000 - special for decimal64, others should have "")
+testunlimit(){
+ t=$1
+ val=$2
+ rmin=$3
+ rmax=$4
+ post=$5
+
+ errmsg="Number $val$post out of range: $rmin$post - $rmax$post"
+
+ new "Netconf set invalid $t leaf"
+ expecteof "$clixon_netconf -qf $cfg" 0 "$val]]>]]>" "^]]>]]>$"
+
+ new "netconf validate invalid range"
+ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^applicationbad-elementr$terror$errmsg]]>]]>$"
+
+ new "discard"
+ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$"
+}
+
+if [ $BE -ne 0 ]; then
+ new "kill old backend"
+ sudo clixon_backend -zf $cfg
+ if [ $? -ne 0 ]; then
+ err
+ fi
+ new "start backend -s init -f $cfg"
+ start_backend -s init -f $cfg
+
+ new "waiting"
+ wait_backend
+fi
+
+new "test params: -f $cfg"
+
+# Test all int types
+testunlimit int8 300 -128 127 ""
+testunlimit int16 73000 -32768 32767 ""
+testunlimit int32 4900000000 -2147483648 2147483647 ""
+testunlimit int64 49739274983274983274983274 -9223372036854775808 9223372036854775807 ""
+testunlimit uint8 300 0 255 ""
+testunlimit uint16 73000 0 65535 ""
+testunlimit uint32 4900000000 0 4294967295 ""
+testunlimit uint64 49739274983274983274983274 0 18446744073709551615 ""
+#testunlimit decimal64 49739274983274983274983274 -9223372036854775808 9223372036854775807 ".000"
+
+# Test all int types
+for t in int8 int16 int32 int64 uint8 uint16 uint32 uint64; do
+ testrange $t 1 0 ""
+done
+
+# decimal64 requires 3 decimals as postfix
+testrange decimal64 1 0 ".000"
+
+# test string with lengthlimit
+testrange string "012" "01234567890" ""
+
+if [ $BE -ne 0 ]; then
+ new "Kill backend"
+ # Check if premature kill
+ pid=`pgrep -u root -f clixon_backend`
+ if [ -z "$pid" ]; then
+ err "backend already dead"
+ fi
+ # kill backend
+ stop_backend -f $cfg
+fi
+
+rm -rf $dir
diff --git a/test/test_union.sh b/test/test_union.sh
index 8fa52a7b..7931ede3 100755
--- a/test/test_union.sh
+++ b/test/test_union.sh
@@ -89,7 +89,7 @@ if [ $BE -ne 0 ]; then
start_backend -s init -f $cfg
new "waiting"
- sleep $RCWAIT
+ wait_backend
fi
new "cli set transitive string"
@@ -99,7 +99,7 @@ new "cli set transitive union"
expectfn "$clixon_cli -1f $cfg -l o set c ulle 33" 0 "^$"
new "cli set transitive union error"
-expectfn "$clixon_cli -1f $cfg -l o set c ulle kalle" 255 '^CLI syntax error: "set c ulle kalle": Unknown command$'
+expectfn "$clixon_cli -1f $cfg -l o set c ulle kalle" 255 "^CLI syntax error: \"set c ulle kalle\": 'kalle' is not a number$"
if [ $BE -eq 0 ]; then
exit # BE
diff --git a/test/test_unique.sh b/test/test_unique.sh
index 799a51bb..a921ff53 100755
--- a/test/test_unique.sh
+++ b/test/test_unique.sh
@@ -92,7 +92,7 @@ if [ $BE -ne 0 ]; then
start_backend -s init -f $cfg
new "waiting"
- sleep $RCWAIT
+ wait_backend
fi
# RFC test two-field caes
diff --git a/test/test_upgrade.sh b/test/test_upgrade.sh
index 03b59f7b..a3885f28 100755
--- a/test/test_upgrade.sh
+++ b/test/test_upgrade.sh
@@ -265,7 +265,7 @@ runtest(){
start_backend -s $mode -f $cfg -o "CLICON_XMLDB_MODSTATE=$modstate"
new "waiting"
- sleep $RCWAIT
+ wait_backend
else
new "Restart backend as eg follows: -Ff $cfg -s $mode -o \"CLICON_XMLDB_MODSTATE=$modstate\" ($BETIMEOUT s)"
sleep $BETIMEOUT
diff --git a/test/test_upgrade_auto.sh b/test/test_upgrade_auto.sh
index 2165ecbe..bc78c449 100755
--- a/test/test_upgrade_auto.sh
+++ b/test/test_upgrade_auto.sh
@@ -254,8 +254,6 @@ if [ $BE -ne 0 ]; then
new "start backend -s $mode -f $cfg"
start_backend -s $mode -f $cfg
fi
-new "waiting"
-sleep $RCWAIT
new "kill old restconf daemon"
sudo pkill -u www-data clixon_restconf
@@ -264,7 +262,8 @@ new "start restconf daemon"
start_restconf -f $cfg
new "waiting"
-sleep $RCWAIT
+wait_backend
+wait_restconf
new "Check running db content"
expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' "^$XML]]>]]>$"
diff --git a/test/test_upgrade_interfaces.sh b/test/test_upgrade_interfaces.sh
index c72e7b2c..5bba6688 100755
--- a/test/test_upgrade_interfaces.sh
+++ b/test/test_upgrade_interfaces.sh
@@ -265,8 +265,6 @@ testrun(){
new "start backend -s startup -f $cfg -- -u"
start_backend -s startup -f $cfg -- -u
fi
- new "waiting"
- sleep $RCWAIT
new "kill old restconf daemon"
sudo pkill -u www-data clixon_restconf
@@ -275,7 +273,8 @@ testrun(){
start_restconf -f $cfg
new "waiting"
- sleep $RCWAIT
+ wait_backend
+ wait_restconf
new "Check running db content"
expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' "^$runxml]]>]]>$"
diff --git a/test/test_upgrade_repair.sh b/test/test_upgrade_repair.sh
index e752d1d5..a9c9bb80 100755
--- a/test/test_upgrade_repair.sh
+++ b/test/test_upgrade_repair.sh
@@ -112,8 +112,6 @@ if [ $BE -ne 0 ]; then
new "start backend -s $mode -f $cfg"
start_backend -s $mode -f $cfg
fi
-new "waiting"
-sleep $RCWAIT
new "kill old restconf daemon"
sudo pkill -u www-data clixon_restconf
@@ -122,7 +120,8 @@ new "start restconf daemon"
start_restconf -f $cfg
new "waiting"
-sleep $RCWAIT
+wait_backend
+wait_restconf
new "Check running db content is failsafe"
expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' "^$SAMEXML]]>]]>$"
diff --git a/test/test_when_must.sh b/test/test_when_must.sh
index a57c4fe4..2fc4dcf7 100755
--- a/test/test_when_must.sh
+++ b/test/test_when_must.sh
@@ -102,7 +102,7 @@ if [ $BE -ne 0 ]; then
start_backend -s init -f $cfg
new "waiting"
- sleep $RCWAIT
+ wait_backend
fi
new "when: add static route"
diff --git a/test/test_yang_load.sh b/test/test_yang_load.sh
index 19c826d5..df5e7195 100755
--- a/test/test_yang_load.sh
+++ b/test/test_yang_load.sh
@@ -85,7 +85,7 @@ if [ $BE -ne 0 ]; then
start_backend -s init -f $cfg
new "waiting"
- sleep $RCWAIT
+ wait_backend
fi
new "1. Set newex"
@@ -134,7 +134,7 @@ if [ $BE -ne 0 ]; then
start_backend -s init -f $cfg
new "waiting"
- sleep $RCWAIT
+ wait_backend
fi
new "Set oldex"
@@ -178,7 +178,7 @@ if [ $BE -ne 0 ]; then
start_backend -s init -f $cfg
new "waiting"
- sleep $RCWAIT
+ wait_backend
fi
new "Set newex"
@@ -222,7 +222,7 @@ if [ $BE -ne 0 ]; then
start_backend -s init -f $cfg
new "waiting"
- sleep $RCWAIT
+ wait_backend
fi
new "Set oldex"
@@ -266,7 +266,7 @@ if [ $BE -ne 0 ]; then
start_backend -s init -f $cfg
new "waiting"
- sleep $RCWAIT
+ wait_backend
fi
new "Set newex"
@@ -312,7 +312,7 @@ if [ $BE -ne 0 ]; then
start_backend -s init -f $cfg
new "waiting"
- sleep $RCWAIT
+ wait_backend
fi
new "Set oldex"
expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^]]>]]>$'
@@ -357,7 +357,7 @@ if [ $BE -ne 0 ]; then
start_backend -s init -f $cfg
new "waiting"
- sleep $RCWAIT
+ wait_backend
fi
new "Set oldex"
@@ -403,7 +403,7 @@ if [ $BE -ne 0 ]; then
start_backend -s init -f $cfg
new "waiting"
- sleep $RCWAIT
+ wait_backend
fi
new "Set oldex"
diff --git a/test/test_yang_namespace.sh b/test/test_yang_namespace.sh
index d5c925eb..113d0426 100755
--- a/test/test_yang_namespace.sh
+++ b/test/test_yang_namespace.sh
@@ -83,7 +83,8 @@ new "start restconf daemon"
start_restconf -f $cfg
new "waiting"
-sleep $RCWAIT
+wait_backend
+wait_restconf
new "netconf set x in example1"
expecteof "$clixon_netconf -qf $cfg" 0 '42]]>]]>' '^]]>]]>$'