* C-API: Added xpath_first_localonly() as an xpath function that skips prefix and namespace checks.

* Added experimental code for optizing XPath search using binary search.
  * Enable with XPATH_LIST_OPTIMIZE
* Changed `clicon_rpc_generate_error(msg, xerr)` to `clicon_rpc_generate_error(xerr, msg, arg)`
This commit is contained in:
Olof hagsand 2019-12-20 18:21:46 +01:00
parent ab46ce9820
commit 7ad16bd84b
56 changed files with 602 additions and 227 deletions

View file

@ -48,7 +48,6 @@
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <assert.h>
#include <syslog.h>
#include <fcntl.h>

View file

@ -49,7 +49,6 @@
#include <limits.h>
#include <stdint.h>
#include <syslog.h>
#include <assert.h>
/* cligen */
#include <cligen/cligen.h>

View file

@ -113,7 +113,6 @@ object.
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>

View file

@ -48,7 +48,6 @@
#include <string.h>
#include <time.h>
#include <errno.h>
#include <assert.h>
#include <sys/time.h>
#include <sys/types.h>

View file

@ -47,7 +47,6 @@
#include <string.h>
#include <limits.h>
#include <stdint.h>
#include <assert.h>
/* cligen */
#include <cligen/cligen.h>
@ -1360,13 +1359,13 @@ netconf_err2cb(cxobj *xerr,
int retval = -1;
cxobj *x;
if ((x=xpath_first(xerr, NULL, "error-type"))!=NULL)
if ((x=xpath_first(xerr, NULL, "//error-type"))!=NULL)
cprintf(cberr, "%s ", xml_body(x));
if ((x=xpath_first(xerr, NULL, "error-tag"))!=NULL)
if ((x=xpath_first(xerr, NULL, "//error-tag"))!=NULL)
cprintf(cberr, "%s ", xml_body(x));
if ((x=xpath_first(xerr, NULL, "error-message"))!=NULL)
if ((x=xpath_first(xerr, NULL, "//error-message"))!=NULL)
cprintf(cberr, "%s ", xml_body(x));
if ((x=xpath_first(xerr, NULL, "error-info"))!=NULL)
if ((x=xpath_first(xerr, NULL, "//error-info"))!=NULL)
clicon_xml2cbuf(cberr, xml_child_i(x,0), 0, 0, -1);
retval = 0;
return retval;

View file

@ -47,7 +47,6 @@
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <syslog.h>
#include <signal.h>
#include <ctype.h>

View file

@ -225,13 +225,18 @@ clicon_rpc_netconf_xml(clicon_handle h,
return retval;
}
/*! Generate and log clicon error function call from Netconf error message
* @param[in] prefix Print this string (if given) before: "<prefix>: <error>"
* @param[in] xerr Netconf error message on the level: <rpc-error>
/*! Generate clicon error from Netconf error message
*
* Get a text error message from netconf error message and generate error on the form:
* <msg>: "<arg>": <netconf-error> or <msg>: <netconf-error>
* @param[in] xerr Netconf error xml tree on the form: <rpc-error>
* @param[in] format Format string
* @param[in] arg String argument to format (optional)
*/
int
clicon_rpc_generate_error(const char *prefix,
cxobj *xerr)
clicon_rpc_generate_error(cxobj *xerr,
const char *msg,
const char *arg)
{
int retval = -1;
cbuf *cb = NULL;
@ -242,10 +247,12 @@ clicon_rpc_generate_error(const char *prefix,
}
if (netconf_err2cb(xerr, cb) < 0)
goto done;
if (prefix)
clicon_log(LOG_ERR, "%s: %s", prefix, cbuf_get(cb));
else
clicon_log(LOG_ERR, "%s", cbuf_get(cb));
if (arg){
cprintf(cb, "%s: \"%s\": ", msg, arg);
clicon_err(OE_CFG, EINVAL, "%s", cbuf_get(cb));
}
else /* XXX: should really be clicon_err but dont do it yet for backward compatability */
clicon_log(LOG_ERR, "%s: %s", msg, cbuf_get(cb));
retval = 0;
done:
if (cb)
@ -273,7 +280,7 @@ clicon_rpc_generate_error(const char *prefix,
* if (clicon_rpc_get_config(h, NULL, "running", "/hello/world", nsc, &xt) < 0)
* err;
* if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){
* clicon_rpc_generate_error("", xerr);
* clicon_rpc_generate_error(xerr, "msg", "/hello/world");
* err;
* }
* if (xt)
@ -395,7 +402,7 @@ clicon_rpc_edit_config(clicon_handle h,
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Editing configuration", xerr);
clicon_rpc_generate_error(xerr, "Editing configuration", NULL);
goto done;
}
retval = 0;
@ -442,7 +449,7 @@ clicon_rpc_copy_config(clicon_handle h,
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Copying configuration", xerr);
clicon_rpc_generate_error(xerr, "Copying configuration", NULL);
goto done;
}
retval = 0;
@ -482,7 +489,7 @@ clicon_rpc_delete_config(clicon_handle h,
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Deleting configuration", xerr);
clicon_rpc_generate_error(xerr, "Deleting configuration", NULL);
goto done;
}
retval = 0;
@ -518,7 +525,7 @@ clicon_rpc_lock(clicon_handle h,
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Locking configuration", xerr);
clicon_rpc_generate_error(xerr, "Locking configuration", NULL);
goto done;
}
retval = 0;
@ -553,7 +560,7 @@ clicon_rpc_unlock(clicon_handle h,
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Configuration unlock", xerr);
clicon_rpc_generate_error(xerr, "Configuration unlock", NULL);
goto done;
}
retval = 0;
@ -587,7 +594,7 @@ clicon_rpc_unlock(clicon_handle h,
* if (clicon_rpc_get(h, "/hello/world", nsc, CONTENT_ALL, -1, &xt) < 0)
* err;
* if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){
* clicon_rpc_generate_error(xerr);
* clicon_rpc_generate_error(xerr, "clicon_rpc_get", NULL);
* err;
* }
* if (xt)
@ -694,7 +701,7 @@ clicon_rpc_close_session(clicon_handle h)
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Close session", xerr);
clicon_rpc_generate_error(xerr, "Close session", NULL);
goto done;
}
retval = 0;
@ -730,7 +737,7 @@ clicon_rpc_kill_session(clicon_handle h,
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Kill session", xerr);
clicon_rpc_generate_error(xerr, "Kill session", NULL);
goto done;
}
retval = 0;
@ -765,7 +772,7 @@ clicon_rpc_validate(clicon_handle h,
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error(CLIXON_ERRSTR_VALIDATE_FAILED, xerr);
clicon_rpc_generate_error(xerr, CLIXON_ERRSTR_VALIDATE_FAILED, NULL);
goto done;
}
retval = 0;
@ -798,7 +805,7 @@ clicon_rpc_commit(clicon_handle h)
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error(CLIXON_ERRSTR_COMMIT_FAILED, xerr);
clicon_rpc_generate_error(xerr, CLIXON_ERRSTR_COMMIT_FAILED, NULL);
goto done;
}
retval = 0;
@ -831,7 +838,7 @@ clicon_rpc_discard_changes(clicon_handle h)
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Discard changes", xerr);
clicon_rpc_generate_error(xerr, "Discard changes", NULL);
goto done;
}
retval = 0;
@ -877,7 +884,7 @@ clicon_rpc_create_subscription(clicon_handle h,
if (clicon_rpc_msg(h, msg, &xret, s0) < 0)
goto done;
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Create subscription", xerr);
clicon_rpc_generate_error(xerr, "Create subscription", NULL);
goto done;
}
retval = 0;
@ -912,7 +919,7 @@ clicon_rpc_debug(clicon_handle h,
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Debug",xerr);
clicon_rpc_generate_error(xerr, "Debug", NULL);
goto done;
}
if (xpath_first(xret, NULL, "//rpc-reply/ok") == NULL){
@ -955,7 +962,7 @@ clicon_hello_req(clicon_handle h,
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Hello", xerr);
clicon_rpc_generate_error(xerr, "Hello", NULL);
goto done;
}
if ((x = xpath_first(xret, NULL, "hello/session-id")) == NULL){

View file

@ -692,6 +692,26 @@ clixon_trim(char *str)
return s;
}
/*! check string equals (NULL is equal)
* @param[in] s1 String 1
* @param[in] s2 String 2
* @retval 0 Equal
* @retval -1 Not equal
* @retval 1 Not equal
*/
int
clicon_strcmp(char *s1,
char *s2)
{
if (s1 == NULL && s2 == NULL)
return 0;
if (s1 == NULL) /* empty string first */
return -1;
if (s2 == NULL)
return 1;
return strcmp(s1, s2);
}
/*! strndup() for systems without it, such as xBSD
*/
#ifndef HAVE_STRNDUP
@ -717,7 +737,6 @@ clicon_strndup(const char *str,
#endif /* ! HAVE_STRNDUP */
/*
* Turn this on for uni-test programs
* Usage: clixon_string join

View file

@ -1059,8 +1059,8 @@ xml_find(cxobj *x_up,
while ((x = xml_child_each(x_up, x, -1)) != NULL)
if (strcmp(name, xml_name(x)) == 0)
return x;
return NULL;
break; /* x is set */
return x;
}
/*! Append xc as child to xp. Remove xc from previous parent.

View file

@ -88,7 +88,7 @@ changelog_rename(clicon_handle h,
clicon_err(OE_XML, 0, "tag required");
goto done;
}
if (xpath_vec_ctx(xw, nsc, tag, &xctx) < 0)
if (xpath_vec_ctx(xw, nsc, tag, 0, &xctx) < 0)
goto done;
if (ctx2string(xctx, &str) < 0)
goto done;
@ -258,7 +258,7 @@ changelog_op(clicon_handle h,
xw = wvec[i];
/* If 'when' exists and is false, skip this target */
if (whenxpath){
if (xpath_vec_ctx(xw, nsc, whenxpath, &xctx) < 0)
if (xpath_vec_ctx(xw, nsc, whenxpath, 0, &xctx) < 0)
goto done;
if ((ret = ctx2boolean(xctx)) < 0)
goto done;

View file

@ -51,7 +51,6 @@
#include <string.h>
#include <limits.h>
#include <stdint.h>
#include <assert.h>
/* cligen */
#include <cligen/cligen.h>

View file

@ -290,7 +290,14 @@ xml_cmp(cxobj *x1,
goto done;
if (xml_cv_cache(x2, &cv2) < 0) /* error case */
goto done;
equal = cv_cmp(cv1, cv2);
if (cv1 != NULL && cv2 != NULL)
equal = cv_cmp(cv1, cv2);
else if (cv1 == NULL && cv2 == NULL)
equal = 0;
else if (cv1 == NULL)
equal = -1;
else
equal = 1;
}
break;
case Y_LIST: /* Match with key values
@ -309,10 +316,9 @@ xml_cmp(cxobj *x1,
else{
if (xml_cv_cache(x1b, &cv1) < 0) /* error case */
goto done;
// assert(cv1);
if (xml_cv_cache(x2b, &cv2) < 0) /* error case */
goto done;
// assert(cv2);
assert(cv1 && cv2);
if ((equal = cv_cmp(cv1, cv2)) != 0)
goto done;
}
@ -831,15 +837,26 @@ match_base_child(cxobj *x0,
}
/*! Experimental API for binary search
* @param[in] xp Parent xml node.
* @param[in] name Node name of child (list)
* @param[in] keyname Yang list key name
* @param[in] keyval XML key value
* @param[out] xret Found XML object, NULL if not founs
* @retval 0 OK, see xret
* @retval -1 Error
* Multiple keys?
* Can extend to leaf-list?
*/
cxobj *
xml_binsearch(cxobj *xp,
char *name,
char *keyname,
char *keyval)
int
xml_binsearch(cxobj *xp,
char *name,
char *keyname,
char *keyval,
cxobj **xretp)
{
int retval = -1;
cxobj *xc = NULL;
cxobj *xa = NULL;
// cxobj *xa = NULL;
cxobj *xret = NULL;
yang_stmt *yp;
yang_stmt *yc;
@ -852,15 +869,22 @@ xml_binsearch(cxobj *xp,
clicon_err(OE_YANG, ENOENT, "yang not found");
goto done;
}
if ((xc = xml_new(name, xp, yc)) == NULL)
if (xml_parse_va(&xc, yc, "<%s><%s>%s</%s></%s>", name, keyname, keyval, keyname, name) < 0)
goto done;
if ((xa = xml_new(keyname, xc, NULL)) == NULL)
if (xml_rootchild(xc, 0, &xc) < 0)
goto done;
if (xml_value_set(xa, keyval) < 0)
#if 0
if (xml_apply0(xc, CX_ELMNT, xml_spec_populate, ys_spec(yc)) < 0)
goto done;
#else
if (xml_spec_set(xc, yc) < 0)
goto done;
#endif
xret = xml_search(xp, xc, yc);
*xretp = xret; /* XXX possibly use *xretp directly */
retval = 0;
done:
if (xc)
xml_free(xc);
return xret;
return retval;
}

View file

@ -66,9 +66,9 @@
#include <string.h>
#include <limits.h>
#include <stdint.h>
#include <assert.h>
#include <syslog.h>
#include <fcntl.h>
#include <assert.h>
/* cligen */
#include <cligen/cligen.h>
@ -306,6 +306,84 @@ xpath_tree2cbuf(xpath_tree *xs,
return retval;
}
/*! Check if two xpath-trees (parsed xpaths) ar equal
*
* @param[in] xt1 XPath parse 1
* @param[in] xt2 XPath parse 2
* @param[in,out] mv Match vector consisting of <name,val> pairs
* @retval 0 If equal
* @retval -1 If not equal
* The function returns 0 if the two trees are equal, otherwise -1
* But the "mv" parameter is a way to add pattern matching to some variables
* On input, mv contains a vector of CLIgen string variables with names that may match string
* fields s0 and s1 in an xpath_tree. If the names match the values of s0 or s1, the field is
* considered equal and is stored as value in the CLIgen variable vector.
* Example:
* xt1: _x[_y='_z']
* xt2: y[k='3']
* match[in]: {_x, _y, _z}
* match[out]: {_x:y, _y:k, _z:3}
*/
int
xpath_tree_eq(xpath_tree *xt1,
xpath_tree *xt2,
cvec *match)
{
int retval = -1; /* not equal */
xpath_tree *xc1;
xpath_tree *xc2;
cg_var *cv;
/* node itself */
if (xt1->xs_type != xt2->xs_type)
goto neq;
if (xt1->xs_int != xt2->xs_int)
goto neq;
if (xt1->xs_double != xt2->xs_double)
goto neq;
if (clicon_strcmp(xt1->xs_s0, xt2->xs_s0)){
if (xt1->xs_s0 && xt2->xs_s0 &&
(cv = cvec_find(match, xt1->xs_s0)) != NULL){
cv_string_set(cv, xt2->xs_s0);
goto ok;
}
goto neq;
}
if (clicon_strcmp(xt1->xs_s1, xt2->xs_s1)){
if (xt1->xs_s1 && xt2->xs_s1 &&
(cv = cvec_find(match, xt1->xs_s1)) != NULL){
cv_string_set(cv, xt2->xs_s1);
goto ok;
}
goto neq;
}
xc1 = xt1->xs_c0;
xc2 = xt2->xs_c0;
if (xc1 == NULL && xc2 == NULL)
;
else{
if (xc1 == NULL || xc2 == NULL)
goto neq;
if (xpath_tree_eq(xc1, xc2, match))
goto neq;
}
xc1 = xt1->xs_c1;
xc2 = xt2->xs_c1;
if (xc1 == NULL && xc2 == NULL)
;
else{
if (xc1 == NULL || xc2 == NULL)
goto neq;
if (xpath_tree_eq(xc1, xc2, match))
goto neq;
}
ok:
retval = 0; /* equal */
neq:
return retval;
}
/*! Free a xpath_tree
* @param[in] xs XPATH tree
* @see xpath_parse creates a xpath_tree
@ -387,12 +465,13 @@ xpath_parse(char *xpath,
* @param[in] xcur XML-tree where to search
* @param[in] nsc External XML namespace context, or NULL
* @param[in] xpath String with XPATH 1.0 syntax
* @param[in] localonly Skip prefix and namespace tests (non-standard)
* @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)
* if (xpath_vec_ctx(x, NULL, xpath, 0, &xc) < 0)
* err;
* if (xc)
* ctx_free(xc);
@ -402,6 +481,7 @@ int
xpath_vec_ctx(cxobj *xcur,
cvec *nsc,
char *xpath,
int localonly,
xp_ctx **xrp)
{
int retval = -1;
@ -415,7 +495,7 @@ xpath_vec_ctx(cxobj *xcur,
xc.xc_initial = xcur;
if (cxvec_append(xcur, &xc.xc_nodeset, &xc.xc_size) < 0)
goto done;
if (xp_eval(&xc, xptree, nsc, xrp) < 0)
if (xp_eval(&xc, xptree, nsc, localonly, xrp) < 0)
goto done;
if (xc.xc_nodeset){
free(xc.xc_nodeset);
@ -475,7 +555,66 @@ xpath_first(cxobj *xcur,
goto done;
}
va_end(ap);
if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0)
if (xpath_vec_ctx(xcur, nsc, xpath, 0, &xr) < 0)
goto done;
if (xr && xr->xc_type == XT_NODESET && xr->xc_size)
cx = xr->xc_nodeset[0];
done:
if (xr)
ctx_free(xr);
if (xpath)
free(xpath);
return cx;
}
/*! XPath nodeset function where prefixes are skipped, only first matching is returned
*
* Reason for skipping prefix/namespace check may be with incomplete tree, for example.
* @param[in] xcur XML tree where to search
* @param[in] xpformat Format string for XPATH syntax
* @retval xml-tree XML tree of first match
* @retval NULL Error or not found
*
* @code
* cxobj *x;
* cvec *nsc; // namespace context
* if ((x = xpath_first_localonly(xtop, "//symbol/foo")) != NULL) {
* ...
* }
* @endcode
* @note the returned pointer points into the original tree so should not be freed after use.
* @note return value does not see difference between error and not found
* @note Prefixes and namespaces are ignored so this is NOT according to standard
* @see also xpath_first.
*/
cxobj *
xpath_first_localonly(cxobj *xcur,
char *xpformat,
...)
{
cxobj *cx = NULL;
va_list ap;
size_t len;
char *xpath = NULL;
xp_ctx *xr = NULL;
va_start(ap, xpformat);
len = vsnprintf(NULL, 0, xpformat, ap);
va_end(ap);
/* allocate a message string exactly fitting the message length */
if ((xpath = malloc(len+1)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
/* second round: compute write message from reason and args */
va_start(ap, xpformat);
if (vsnprintf(xpath, len+1, xpformat, ap) < 0){
clicon_err(OE_UNIX, errno, "vsnprintf");
va_end(ap);
goto done;
}
va_end(ap);
if (xpath_vec_ctx(xcur, NULL, xpath, 1, &xr) < 0)
goto done;
if (xr && xr->xc_type == XT_NODESET && xr->xc_size)
cx = xr->xc_nodeset[0];
@ -541,7 +680,7 @@ xpath_vec(cxobj *xcur,
va_end(ap);
*vec=NULL;
*veclen = 0;
if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0)
if (xpath_vec_ctx(xcur, nsc, xpath, 0, &xr) < 0)
goto done;
if (xr && xr->xc_type == XT_NODESET){
*vec = xr->xc_nodeset;
@ -616,7 +755,7 @@ xpath_vec_flag(cxobj *xcur,
va_end(ap);
*vec=NULL;
*veclen = 0;
if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0)
if (xpath_vec_ctx(xcur, nsc, xpath, 0, &xr) < 0)
goto done;
if (xr && xr->xc_type == XT_NODESET){
for (i=0; i<xr->xc_size; i++){
@ -672,7 +811,7 @@ xpath_vec_bool(cxobj *xcur,
goto done;
}
va_end(ap);
if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0)
if (xpath_vec_ctx(xcur, nsc, xpath, 0, &xr) < 0)
goto done;
if (xr)
retval = ctx2boolean(xr);

View file

@ -41,7 +41,6 @@
#include <string.h>
#include <limits.h>
#include <stdint.h>
#include <assert.h>
#include <syslog.h>
#include <fcntl.h>
#include <math.h> /* NaN */
@ -113,21 +112,28 @@ ctx_dup(xp_ctx *xc0)
return xc;
}
/*! Print XPATH context */
/*! Print XPATH context to CLIgen buf
* @param[in] cb CLIgen buf to print to
* @param[in] xc XPATH evaluation context
* @param[in] ind Indentation margin
* @param[in] str Prefix string in printout
*/
int
ctx_print(cbuf *cb,
int id,
xp_ctx *xc,
char *str)
ctx_print_cb(cbuf *cb,
xp_ctx *xc,
int ind,
char *str)
{
static int ident = 0;
static int indent = 0;
int i;
if (id<0)
ident += id;
cprintf(cb, "%*s%s ", ident, "", str?str:"");
if (id>0)
ident += id;
if (ind<0)
indent += ind;
cprintf(cb, "%*s%s ", indent, "", str?str:"");
if (ind>0)
indent += ind;
if (xc){
cprintf(cb, "%s: ", (char*)clicon_int2str(ctxmap, xc->xc_type));
switch (xc->xc_type){
@ -149,6 +155,32 @@ ctx_print(cbuf *cb,
return 0;
}
/*! Print XPATH context
* @param[in] f File to print to
* @param[in] xc XPATH evaluation context
* @param[in] str Prefix string in printout
*/
int
ctx_print(FILE *f,
xp_ctx *xc,
char *str)
{
int retval = -1;
cbuf *cb = NULL;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
ctx_print_cb(cb, xc, 0, str);
fprintf(f, "%s", cbuf_get(cb));
retval = 0;
done:
if (cb)
cbuf_free(cb);
return retval;
}
/*! Convert xpath context to boolean according to boolean() function in XPATH spec
* @param[in] xc XPATH context
* @retval 0 False

View file

@ -81,6 +81,7 @@
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_sort.h"
#include "clixon_xml_nsctx.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
@ -105,7 +106,7 @@ const map_str2int xpopmap[] = {
{NULL, -1}
};
/*!
/*! Eval an XPATH nodetest
* @retval -1 Error XXX: retval -1 not properly handled
* @retval 0 No match
* @retval 1 Match
@ -169,10 +170,37 @@ nodetest_eval_node(cxobj *x,
done: /* retval set in preceding statement */
return retval;
}
/*! Eval an XPATH nodetest but skip prefix and namespace tests
* This is NOT according to standard
*/
static int
nodetest_eval_node_localonly(cxobj *x,
xpath_tree *xs,
cvec *nsc)
{
int retval = -1;
char *name1 = xml_name(x);
char *name2 = NULL;
/* Namespaces is s0, name is s1 */
if (strcmp(xs->xs_s1, "*")==0)
return 1;
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;
}
done: /* retval set in preceding statement */
return retval;
}
/*! Make a nodetest
* @param[in] x XML node
* @param[in] xs XPATH stack of type XP_NODE or XP_NODE_FN
* @param[in] nsc XML Namespace context
* @param[in] localonly Skip prefix and namespace tests (non-standard)
* @retval -1 Error
* @retval 0 No match
* @retval 1 Match
@ -182,13 +210,18 @@ nodetest_eval_node(cxobj *x,
static int
nodetest_eval(cxobj *x,
xpath_tree *xs,
cvec *nsc)
cvec *nsc,
int localonly)
{
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);
if (xs->xs_type == XP_NODE){
if (localonly)
retval = nodetest_eval_node_localonly(x, xs, nsc);
else
retval = nodetest_eval_node(x, xs, nsc);
}
else if (xs->xs_type == XP_NODE_FN){
fn = xs->xs_s0;
if (strcmp(fn, "node")==0)
@ -206,6 +239,7 @@ nodetest_eval(cxobj *x,
* @param[in] node_type
* @param[in] flags
* @param[in] nsc XML Namespace context
* @param[in] localonly Skip prefix and namespace tests (non-standard)
* @param[out] vec0
* @param[out] vec0len
*/
@ -215,6 +249,7 @@ nodetest_recursive(cxobj *xn,
int node_type,
uint16_t flags,
cvec *nsc,
int localonly,
cxobj ***vec0,
size_t *vec0len)
{
@ -225,14 +260,14 @@ nodetest_recursive(cxobj *xn,
xsub = NULL;
while ((xsub = xml_child_each(xn, xsub, node_type)) != NULL) {
if (nodetest_eval(xsub, nodetest, nsc) == 1){
if (nodetest_eval(xsub, nodetest, nsc, localonly) == 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)
if (nodetest_recursive(xsub, nodetest, node_type, flags, nsc, localonly, &vec, &veclen) < 0)
goto done;
}
retval = 0;
@ -242,11 +277,113 @@ nodetest_recursive(cxobj *xn,
return retval;
}
#ifdef XPATH_LIST_OPTIMIZE
/*! Pattern matching to find fastpath
*
* @param[in] xt XPath tree
* @param[in] xv XML base node
* @param[out] xp XML found node (retval == 1)
* @param[out] key
* @param[out] keyval
* @retval 0 Match
* @retval -1 No match
XPath:
y[k=3] # corresponds to: <name>[<keyname>=<keyval>]
*/
static int
xpath_list_optimize(xpath_tree *xt,
cxobj *xv,
cxobj **xp)
{
int retval = -1;
xpath_tree *xmtop = NULL; /* pattern match tree top */
xpath_tree *xm = NULL;
cg_var *cv0;
cg_var *cv1;
cg_var *cv2;
cvec *match = NULL;
char *name;
char *keyname;
char *keyval;
yang_stmt *yp;
yang_stmt *yc;
/* Parse */
if (xpath_parse("_x[_y='_z']", &xmtop) < 0) /* XXX: "y[k=3]" */
goto done;
/* Go down two steps */
if (xmtop && (xm = xmtop->xs_c0) && (xm = xm->xs_c0))
;
if (xm == NULL)
goto ok;
/* Create a cvec match vector and initialize variables */
if ((match = cvec_new(3)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_new");
goto done;
}
cv0 = cvec_i(match, 0);
cv_name_set(cv0, "_x");
cv_type_set(cv0, CGV_STRING);
cv1 = cvec_i(match, 1);
cv_name_set(cv1, "_y");
cv_type_set(cv1, CGV_STRING);
cv2 = cvec_i(match, 2);
cv_name_set(cv2, "_z");
cv_type_set(cv2, CGV_STRING);
/* Check if equal */
if (xpath_tree_eq(xm, xt, match) != 0)
goto ok; /* no match */
/* Extract variables XXX strdups */
name = cv_string_get(cv0);
keyname = cv_string_get(cv1);
keyval = cv_string_get(cv2);
assert(name && keyname && keyval);
/* Check yang and that only a list with key as index is a special case can do bin search */
if ((yp = xml_spec(xv)) == NULL)
goto ok;
if ((yc = yang_find(yp, 0, name)) == NULL)
goto ok;
if (yang_keyword_get(yc) != Y_LIST)
goto ok;
#if 0
{
cvec *cvv = NULL;
if ((cvv = yang_cvec_get(yc)) == NULL)
goto ok;
if (cvec_len(cvv) != 1)
goto ok;
}
#else
{
int ret;
if ((ret = yang_key_match(yc, keyname)) < 0)
goto done;
if (ret != 1)
goto ok;
}
#endif
if (xml_binsearch(xv, name, keyname, keyval, xp) < 0)
goto done;
retval = 1; /* match */
done:
if (match)
cvec_free(match);
if (xmtop)
xpath_tree_free(xmtop);
return retval;
ok: /* no match, not special case */
retval = 0;
goto done;
}
#endif /* XPATH_LIST_OPTIMIZE */
/*! 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[in] localonly Skip prefix and namespace tests (non-standard)
* @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])
@ -259,6 +396,7 @@ static int
xp_eval_step(xp_ctx *xc0,
xpath_tree *xs,
cvec *nsc,
int localonly,
xp_ctx **xrp)
{
int retval = -1;
@ -270,6 +408,9 @@ xp_eval_step(xp_ctx *xc0,
size_t veclen = 0;
xpath_tree *nodetest = xs->xs_c0;
xp_ctx *xc = NULL;
#ifdef XPATH_LIST_OPTIMIZE
int ret;
#endif
/* Create new xc */
if ((xc = ctx_dup(xc0)) == NULL)
@ -285,7 +426,7 @@ xp_eval_step(xp_ctx *xc0,
if (xc->xc_descendant){
for (i=0; i<xc->xc_size; i++){
xv = xc->xc_nodeset[i];
if (nodetest_recursive(xv, nodetest, CX_ELMNT, 0x0, nsc, &vec, &veclen) < 0)
if (nodetest_recursive(xv, nodetest, CX_ELMNT, 0x0, nsc, localonly, &vec, &veclen) < 0)
goto done;
}
xc->xc_descendant = 0;
@ -297,17 +438,33 @@ xp_eval_step(xp_ctx *xc0,
if (cxvec_append(xc->xc_initial, &vec, &veclen) < 0)
goto done;
}
else for (i=0; i<xc->xc_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;
else for (i=0; i<xc->xc_size; i++){
xv = xc->xc_nodeset[i];
x = NULL;
#ifdef XPATH_LIST_OPTIMIZE
/* Identify XPATH special cases and if match, use binary search.
* it returns: -1 fatal error, quit
* 0: not special case, do normal processing
* 1: special case, use x (found if != NULL)
*/
if ((ret = xpath_list_optimize(xs, xv, &x)) < 0)
goto done;
if (ret == 1){
fprintf(stderr, "XPATH_LIST_OPTIMIZE\n");
if (x)
if (cxvec_append(x, &vec, &veclen) < 0)
goto done;
}
else
#endif
while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
/* xs->xs_c0 is nodetest */
if (nodetest == NULL || nodetest_eval(x, nodetest, nsc, localonly) == 1){
if (cxvec_append(x, &vec, &veclen) < 0)
goto done;
}
}
}
}
}
ctx_nodeset_replace(xc, vec, veclen);
break;
@ -315,7 +472,7 @@ xp_eval_step(xp_ctx *xc0,
case A_DESCENDANT_OR_SELF:
for (i=0; i<xc->xc_size; i++){
xv = xc->xc_nodeset[i];
if (nodetest_recursive(xv, xs->xs_c0, CX_ELMNT, 0x0, nsc, &vec, &veclen) < 0)
if (nodetest_recursive(xv, xs->xs_c0, CX_ELMNT, 0x0, nsc, localonly, &vec, &veclen) < 0)
goto done;
}
ctx_nodeset_replace(xc, vec, veclen);
@ -354,7 +511,7 @@ xp_eval_step(xp_ctx *xc0,
break;
}
if (xs->xs_c1){
if (xp_eval(xc, xs->xs_c1, nsc, xrp) < 0)
if (xp_eval(xc, xs->xs_c1, nsc, localonly, xrp) < 0)
goto done;
}
else{
@ -375,6 +532,7 @@ xp_eval_step(xp_ctx *xc0,
* @param[in] xc Incoming context
* @param[in] xs XPATH node tree
* @param[in] nsc XML Namespace context
* @param[in] localonly Skip prefix and namespace tests (non-standard)
* @param[out] xrp Resulting context
*
* A predicate filters a node-set with respect to an axis to produce a new
@ -396,6 +554,7 @@ static int
xp_eval_predicate(xp_ctx *xc,
xpath_tree *xs,
cvec *nsc,
int localonly,
xp_ctx **xrp)
{
int retval = -1;
@ -411,7 +570,7 @@ xp_eval_predicate(xp_ctx *xc,
goto done;
}
else{ /* eval previous predicates */
if (xp_eval(xc, xs->xs_c0, nsc, &xr0) < 0)
if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0)
goto done;
}
if (xs->xs_c1){
@ -440,7 +599,7 @@ xp_eval_predicate(xp_ctx *xc,
* 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)
if (xp_eval(xcc, xs->xs_c1, nsc, localonly, &xrc) < 0)
goto done;
if (xcc)
ctx_free(xcc);
@ -860,6 +1019,7 @@ xp_union(xp_ctx *xc1,
* @param[in] xc Incoming context
* @param[in] xs XPATH node tree
* @param[in] nsc XML Namespace context
* @param[in] localonly Skip prefix and namespace tests (non-standard)
* @param[out] xrp Resulting context
* @retval 0 OK
* @retval -1 Error
@ -868,6 +1028,7 @@ int
xp_eval(xp_ctx *xc,
xpath_tree *xs,
cvec *nsc,
int localonly,
xp_ctx **xrp)
{
int retval = -1;
@ -877,16 +1038,8 @@ xp_eval(xp_ctx *xc,
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);
}
if (debug)
ctx_print(stderr, xc, xpath_tree_int2str(xs->xs_type));
/* Pre-actions before check first child c0
*/
switch (xs->xs_type){
@ -908,12 +1061,12 @@ xp_eval(xp_ctx *xc,
break;
case XP_STEP: /* XP_NODE is first argument -not called explicitly */
if (xp_eval_step(xc, xs, nsc, xrp) < 0)
if (xp_eval_step(xc, xs, nsc, localonly, xrp) < 0)
goto done;
goto ok;
break;
case XP_PRED:
if (xp_eval_predicate(xc, xs, nsc, xrp) < 0)
if (xp_eval_predicate(xc, xs, nsc, localonly, xrp) < 0)
goto done;
goto ok;
break;
@ -923,7 +1076,7 @@ xp_eval(xp_ctx *xc,
/* Eval first child c0
*/
if (xs->xs_c0){
if (xp_eval(xc, xs->xs_c0, nsc, &xr0) < 0)
if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0)
goto done;
}
/* Actions between first and second child
@ -1001,7 +1154,7 @@ xp_eval(xp_ctx *xc,
* 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)
if (xp_eval(use_xr0?xr0:xc, xs->xs_c1, nsc, localonly, &xr1) < 0)
goto done;
/* Actions after second child
*/
@ -1041,16 +1194,8 @@ xp_eval(xp_ctx *xc,
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);
}
if (debug)
ctx_print(stderr, *xrp, xpath_tree_int2str(xs->xs_type));
retval = 0;
done:
if (xr2)

View file

@ -44,6 +44,6 @@ extern const map_str2int xpopmap[];
/*
* Prototypes
*/
int xp_eval(xp_ctx *xc, xpath_tree *xs, cvec *nsc, xp_ctx **xrp);
int xp_eval(xp_ctx *xc, xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
#endif /* _CLIXON_XPATH_EVAL_H */

View file

@ -85,6 +85,10 @@ digit [0-9]
integer {digit}+
real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+)
namestart [A-Z_a-z]
namechar [A-Z_a-z\-\.0-9]
ncname {namestart}{namechar}*
%x TOKEN
%s QLITERAL
%s ALITERAL
@ -141,9 +145,9 @@ real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+)
<TOKEN>\" { BEGIN(QLITERAL); return QUOTE; }
<TOKEN>\' { BEGIN(ALITERAL); return APOST; }
<TOKEN>\-?({integer}|{real}) { clixon_xpath_parselval.string = strdup(yytext); return NUMBER; }
<TOKEN>[0-9A-Za-z_\-]+ { clixon_xpath_parselval.string = strdup(yytext);
<TOKEN>{ncname} { clixon_xpath_parselval.string = strdup(yytext);
return NAME; /* rather be catch-all */
}
}
<TOKEN>. { fprintf(stderr,"LEXICAL ERROR\n"); return -1; }
<QLITERAL>\" { BEGIN(TOKEN); return QUOTE; }

View file

@ -103,7 +103,6 @@
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>

View file

@ -51,7 +51,6 @@
#include <sys/types.h>
#include <fcntl.h>
#include <syslog.h>
#include <assert.h>
#include <sys/stat.h>
#include <netinet/in.h>

View file

@ -52,7 +52,6 @@
#include <sys/types.h>
#include <fcntl.h>
#include <syslog.h>
#include <assert.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <sys/param.h>