The Clixon API has been extended with namespaces, or namespace contexts in the following cases:
* CLIspec functions have added namespace parameter:
* `cli_show_config <db> <format> <xpath>` --> `cli_show_config <db> <format> <xpath> <namespace>`
* `cli_copy_config <db> <xpath> ...` --> `cli_copy_config <db> <xpath> <namespace> ...`
* Xpath API
* `xpath_first(x, format, ...)` --> `xpath_first(x, nsc, format, ...)`
* `xpath_vec(x, format, vec, veclen, ...)` --> `xpath_vec(x, nsc, format, vec, veclen, ...)`
* `xpath_vec_flag(x, format, flags, vec, veclen, ...)` --> `xpath_vec_flag(x, format, flags, vec, veclen, ...)`
* `xpath_vec_bool(x, format, ...)` --> `xpath_vec_bool(x, nsc, format, ...)`
* `xpath_vec_ctx(x, xpath, xp)` --> `xpath_vec_ctx(x, nsc, xpath, xp)`
* xmldb_get0 has an added `nsc` parameter:
* `xmldb_get0(h, db, xpath, copy, xret, msd)` --> `xmldb_get0(h, db, nsc, xpath, copy, xret, msd)`
* The plugin statedata callback (ca_statedata) has been extended with an nsc parameter:
* `int example_statedata(clicon_handle h, cvec *nsc, char *xpath, cxobj *xstate);`
* 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);`
This commit is contained in:
parent
73d8e97a01
commit
67b8685bab
78 changed files with 1507 additions and 538 deletions
|
|
@ -32,6 +32,56 @@
|
|||
***** 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 <filter> element.
|
||||
* <rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
|
||||
* <get-config>
|
||||
* <filter xmlns:t="http://example.com/schema/1.2/config"
|
||||
* type="xpath"
|
||||
* select="/t:top/t:users/t:user[t:name='fred']"/>
|
||||
* </get-config>
|
||||
* 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?
|
||||
*
|
||||
* 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: <if:a xmlns:if="urn:example:if" xmlns:ip="urn:example:ip"><ip:b/></if>
|
||||
* 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: <if:a xmlns:if="urn:example:if" xmlns:ip="urn:example:ip"><ip:b/></if>
|
||||
* NETCONF:<get-config><filter select="/x:a/y:b"
|
||||
* xmlns:x="urn:example:if" xmlns:y="urn:example:ip/>
|
||||
* Here, x,y are prefixes used for two namespaces that are given by if,ip in
|
||||
* the xml. In this case, the namespaces (eg urn:example:if) must be compared
|
||||
* instead.
|
||||
* Another case is Yang path expressions.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
|
|
@ -62,6 +112,7 @@
|
|||
#include "clixon_handle.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_xpath_parse.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
|
|
@ -69,6 +120,9 @@
|
|||
/*
|
||||
* Variables
|
||||
*/
|
||||
/* XXX assert break if xpath dont match. Set to 0 if ypu want it to pass */
|
||||
int xpatherrordiff=1;
|
||||
|
||||
/* Mapping between XPATH operator string <--> int */
|
||||
const map_str2int xpopmap[] = {
|
||||
{"and", XO_AND},
|
||||
|
|
@ -141,8 +195,7 @@ xpath_tree_print(cbuf *cb,
|
|||
}
|
||||
|
||||
static int
|
||||
xpath_tree_free(
|
||||
xpath_tree *xs)
|
||||
xpath_tree_free(xpath_tree *xs)
|
||||
{
|
||||
if (xs->xs_s0)
|
||||
free(xs->xs_s0);
|
||||
|
|
@ -156,45 +209,118 @@ xpath_tree_free(
|
|||
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
|
||||
* @retval 0 Match
|
||||
* @retval 1 No match
|
||||
* @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)
|
||||
xpath_tree *xs,
|
||||
cvec *nsc)
|
||||
{
|
||||
int retval = 0; /* NB: no match is default (not error) */
|
||||
char *fn;
|
||||
|
||||
if (xs->xs_type == XP_NODE){
|
||||
/* Namespaces is s0, name is s1 */
|
||||
if (strcmp(xs->xs_s1, "*")==0)
|
||||
return 1;
|
||||
else if (strcmp(xml_name(x), xs->xs_s1)==0)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
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)
|
||||
return 1;
|
||||
retval = 1;
|
||||
else if (strcmp(fn, "text")==0)
|
||||
return 1;
|
||||
retval = 1;
|
||||
}
|
||||
return 0;
|
||||
/* 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,
|
||||
nodetest_recursive(cxobj *xn,
|
||||
xpath_tree *nodetest,
|
||||
int node_type,
|
||||
uint16_t flags,
|
||||
cxobj ***vec0,
|
||||
size_t *vec0len)
|
||||
int node_type,
|
||||
uint16_t flags,
|
||||
cvec *nsc,
|
||||
cxobj ***vec0,
|
||||
size_t *vec0len)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xsub;
|
||||
|
|
@ -203,14 +329,14 @@ nodetest_recursive(cxobj *xn,
|
|||
|
||||
xsub = NULL;
|
||||
while ((xsub = xml_child_each(xn, xsub, node_type)) != NULL) {
|
||||
if (nodetest_eval(xsub, nodetest) == 1){
|
||||
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, &vec, &veclen) < 0)
|
||||
if (nodetest_recursive(xsub, nodetest, node_type, flags, nsc, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
|
|
@ -220,12 +346,13 @@ nodetest_recursive(cxobj *xn,
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int xp_eval(xp_ctx *xc, xpath_tree *xs, xp_ctx **xrp);
|
||||
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])
|
||||
|
|
@ -237,6 +364,7 @@ static int xp_eval(xp_ctx *xc, xpath_tree *xs, xp_ctx **xrp);
|
|||
static int
|
||||
xp_eval_step(xp_ctx *xc0,
|
||||
xpath_tree *xs,
|
||||
cvec *nsc,
|
||||
xp_ctx **xrp)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -263,7 +391,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, &vec, &veclen) < 0)
|
||||
if (nodetest_recursive(xv, nodetest, CX_ELMNT, 0x0, nsc, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
}
|
||||
xc->xc_descendant = 0;
|
||||
|
|
@ -280,7 +408,7 @@ xp_eval_step(xp_ctx *xc0,
|
|||
x = NULL;
|
||||
while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
|
||||
/* xs->xs_c0 is nodetest */
|
||||
if (nodetest == NULL || nodetest_eval(x, nodetest))
|
||||
if (nodetest == NULL || nodetest_eval(x, nodetest, nsc) == 1)
|
||||
if (cxvec_append(x, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -292,7 +420,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, &vec, &veclen) < 0)
|
||||
if (nodetest_recursive(xv, xs->xs_c0, CX_ELMNT, 0x0, nsc, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
}
|
||||
ctx_nodeset_replace(xc, vec, veclen);
|
||||
|
|
@ -331,7 +459,7 @@ xp_eval_step(xp_ctx *xc0,
|
|||
break;
|
||||
}
|
||||
if (xs->xs_c1){
|
||||
if (xp_eval(xc, xs->xs_c1, xrp) < 0)
|
||||
if (xp_eval(xc, xs->xs_c1, nsc, xrp) < 0)
|
||||
goto done;
|
||||
}
|
||||
else{
|
||||
|
|
@ -351,6 +479,7 @@ xp_eval_step(xp_ctx *xc0,
|
|||
* 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
|
||||
|
|
@ -371,6 +500,7 @@ xp_eval_step(xp_ctx *xc0,
|
|||
static int
|
||||
xp_eval_predicate(xp_ctx *xc,
|
||||
xpath_tree *xs,
|
||||
cvec *nsc,
|
||||
xp_ctx **xrp)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -386,7 +516,7 @@ xp_eval_predicate(xp_ctx *xc,
|
|||
goto done;
|
||||
}
|
||||
else{ /* eval previous predicates */
|
||||
if (xp_eval(xc, xs->xs_c0, &xr0) < 0)
|
||||
if (xp_eval(xc, xs->xs_c0, nsc, &xr0) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (xs->xs_c1){
|
||||
|
|
@ -415,7 +545,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, &xrc) < 0)
|
||||
if (xp_eval(xcc, xs->xs_c1, nsc, &xrc) < 0)
|
||||
goto done;
|
||||
if (xcc)
|
||||
ctx_free(xcc);
|
||||
|
|
@ -834,13 +964,16 @@ xp_union(xp_ctx *xc1,
|
|||
* 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;
|
||||
|
|
@ -880,12 +1013,12 @@ xp_eval(xp_ctx *xc,
|
|||
|
||||
break;
|
||||
case XP_STEP: /* XP_NODE is first argument -not called explicitly */
|
||||
if (xp_eval_step(xc, xs, xrp) < 0)
|
||||
if (xp_eval_step(xc, xs, nsc, xrp) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
break;
|
||||
case XP_PRED:
|
||||
if (xp_eval_predicate(xc, xs, xrp) < 0)
|
||||
if (xp_eval_predicate(xc, xs, nsc, xrp) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
break;
|
||||
|
|
@ -895,7 +1028,7 @@ xp_eval(xp_ctx *xc,
|
|||
/* Eval first child c0
|
||||
*/
|
||||
if (xs->xs_c0){
|
||||
if (xp_eval(xc, xs->xs_c0, &xr0) < 0)
|
||||
if (xp_eval(xc, xs->xs_c0, nsc, &xr0) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Actions between first and second child
|
||||
|
|
@ -973,7 +1106,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, &xr1) < 0)
|
||||
if (xp_eval(use_xr0?xr0:xc, xs->xs_c1, nsc, &xr1) < 0)
|
||||
goto done;
|
||||
/* Actions after second child
|
||||
*/
|
||||
|
|
@ -1032,19 +1165,20 @@ xp_eval(xp_ctx *xc,
|
|||
if (xr0)
|
||||
ctx_free(xr0);
|
||||
return retval;
|
||||
}
|
||||
} /* xp_eval */
|
||||
|
||||
/*! Given XML tree and xpath, returns xpath context
|
||||
/*! 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
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* xp_ctx *xc = NULL;
|
||||
* if (xpath_vec_ctx(x, xpath, &xc) < 0)
|
||||
* if (xpath_vec_ctx(x, NULL, xpath, &xc) < 0)
|
||||
* err;
|
||||
* if (xc)
|
||||
* ctx_free(xc);
|
||||
|
|
@ -1052,6 +1186,7 @@ xp_eval(xp_ctx *xc,
|
|||
*/
|
||||
int
|
||||
xpath_vec_ctx(cxobj *xcur,
|
||||
cvec *nsc,
|
||||
char *xpath,
|
||||
xp_ctx **xrp)
|
||||
{
|
||||
|
|
@ -1084,7 +1219,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, xy.xy_top, xrp) < 0)
|
||||
if (xp_eval(&xc, xy.xy_top, nsc, xrp) < 0)
|
||||
goto done;
|
||||
if (xc.xc_nodeset){
|
||||
free(xc.xc_nodeset);
|
||||
|
|
@ -1102,23 +1237,27 @@ xpath_vec_ctx(cxobj *xcur,
|
|||
|
||||
/*! Xpath nodeset function where only the first matching entry is returned
|
||||
* args:
|
||||
* @param[in] xcur xml-tree where to search
|
||||
* @param[in] xpath string with XPATH syntax
|
||||
* @retval xml-tree of first match
|
||||
* @retval NULL Error or not found
|
||||
* @param[in] xcur XML tree where to search
|
||||
* @param[in] nsc XML Namespace context
|
||||
* @param[in] format string with XPATH syntax
|
||||
* @retval xml-tree XML tree of first match
|
||||
* @retval NULL Error or not found
|
||||
*
|
||||
* @code
|
||||
* cxobj *x;
|
||||
* if ((x = xpath_first(xtop, "//symbol/foo")) != NULL) {
|
||||
* cvec *nsc; // namespace context
|
||||
* if ((x = xpath_first(xtop, nsc, "//symbol/foo")) != NULL) {
|
||||
* ...
|
||||
* }
|
||||
* @endcode
|
||||
* @note the returned pointer points into the original tree so should not be freed fter use.
|
||||
* @note return value does not see difference between error and not found
|
||||
* @see also xpath_vec.
|
||||
* @experimental
|
||||
*/
|
||||
cxobj *
|
||||
xpath_first(cxobj *xcur,
|
||||
cvec *nsc,
|
||||
char *format,
|
||||
...)
|
||||
{
|
||||
|
|
@ -1144,9 +1283,8 @@ xpath_first(cxobj *xcur,
|
|||
goto done;
|
||||
}
|
||||
va_end(ap);
|
||||
if (xpath_vec_ctx(xcur, xpath, &xr) < 0)
|
||||
if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0)
|
||||
goto done;
|
||||
|
||||
if (xr && xr->xc_type == XT_NODESET && xr->xc_size)
|
||||
cx = xr->xc_nodeset[0];
|
||||
done:
|
||||
|
|
@ -1157,18 +1295,21 @@ 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
|
||||
* @param[in] xpath stdarg string with XPATH 1.0 syntax
|
||||
* @param[in] format stdarg string with XPATH 1.0 syntax
|
||||
* @param[in] nsc XML Namespace context for XPATH
|
||||
* @param[out] vec vector of xml-trees. Vector must be free():d after use
|
||||
* @param[out] veclen returns length of vector in return value
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* cvec *nsc; // namespace context
|
||||
* cxobj **vec;
|
||||
* size_t veclen;
|
||||
* if (xpath_vec(xcur, "//symbol/foo", &vec, &veclen) < 0)
|
||||
* if (xpath_vec(xcur, nsc, "//symbol/foo", &vec, &veclen) < 0)
|
||||
* goto err;
|
||||
* for (i=0; i<veclen; i++){
|
||||
* xn = vec[i];
|
||||
|
|
@ -1176,9 +1317,11 @@ xpath_first(cxobj *xcur,
|
|||
* }
|
||||
* free(vec);
|
||||
* @endcode
|
||||
* @note Namespace prefix checking is not properly implemented
|
||||
*/
|
||||
int
|
||||
xpath_vec(cxobj *xcur,
|
||||
cvec *nsc,
|
||||
char *format,
|
||||
cxobj ***vec,
|
||||
size_t *veclen,
|
||||
|
|
@ -1188,8 +1331,8 @@ xpath_vec(cxobj *xcur,
|
|||
va_list ap;
|
||||
size_t len;
|
||||
char *xpath = NULL;
|
||||
xp_ctx *xr = NULL;
|
||||
|
||||
xp_ctx *xr = NULL;
|
||||
|
||||
va_start(ap, veclen);
|
||||
len = vsnprintf(NULL, 0, format, ap);
|
||||
va_end(ap);
|
||||
|
|
@ -1208,7 +1351,7 @@ xpath_vec(cxobj *xcur,
|
|||
va_end(ap);
|
||||
*vec=NULL;
|
||||
*veclen = 0;
|
||||
if (xpath_vec_ctx(xcur, xpath, &xr) < 0)
|
||||
if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0)
|
||||
goto done;
|
||||
if (xr && xr->xc_type == XT_NODESET){
|
||||
*vec = xr->xc_nodeset;
|
||||
|
|
@ -1227,6 +1370,7 @@ xpath_vec(cxobj *xcur,
|
|||
/* Xpath that returns a vector of matches (only nodes marked with flags)
|
||||
* @param[in] xcur xml-tree where to search
|
||||
* @param[in] xpath string with XPATH syntax
|
||||
* @param[in] nsc External XML namespace context, or NULL
|
||||
* @param[in] flags Set of flags that return nodes must match (0 if all)
|
||||
* @param[out] vec vector of xml-trees. Vector must be free():d after use
|
||||
* @param[out] veclen returns length of vector in return value
|
||||
|
|
@ -1235,7 +1379,8 @@ xpath_vec(cxobj *xcur,
|
|||
* @code
|
||||
* cxobj **vec;
|
||||
* size_t veclen;
|
||||
* if (xpath_vec_flag(xcur, "//symbol/foo", XML_FLAG_ADD, &vec, &veclen) < 0)
|
||||
* cvec *nsc; // namespace context (not NULL)
|
||||
* if (xpath_vec_flag(xcur, nsc, "//symbol/foo", XML_FLAG_ADD, &vec, &veclen) < 0)
|
||||
* goto err;
|
||||
* for (i=0; i<veclen; i++){
|
||||
* xn = vec[i];
|
||||
|
|
@ -1249,8 +1394,9 @@ xpath_vec(cxobj *xcur,
|
|||
*/
|
||||
int
|
||||
xpath_vec_flag(cxobj *xcur,
|
||||
cvec *nsc,
|
||||
char *format,
|
||||
uint16_t flags,
|
||||
uint16_t flags,
|
||||
cxobj ***vec,
|
||||
size_t *veclen,
|
||||
...)
|
||||
|
|
@ -1281,7 +1427,7 @@ xpath_vec_flag(cxobj *xcur,
|
|||
va_end(ap);
|
||||
*vec=NULL;
|
||||
*veclen = 0;
|
||||
if (xpath_vec_ctx(xcur, xpath, &xr) < 0)
|
||||
if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0)
|
||||
goto done;
|
||||
if (xr && xr->xc_type == XT_NODESET){
|
||||
for (i=0; i<xr->xc_size; i++){
|
||||
|
|
@ -1303,14 +1449,16 @@ xpath_vec_flag(cxobj *xcur,
|
|||
/*! Given XML tree and xpath, returns boolean
|
||||
* Returns true if the nodeset is non-empty
|
||||
* @param[in] xcur xml-tree where to search
|
||||
* @param[in] nsc External XML namespace context, or NULL
|
||||
* @param[in] xpath stdarg string with XPATH 1.0 syntax
|
||||
* @retval 1 True
|
||||
* @retval 0 False
|
||||
* @retval -1 Error
|
||||
*/
|
||||
int
|
||||
xpath_vec_bool(cxobj *xcur,
|
||||
char *format,
|
||||
xpath_vec_bool(cxobj *xcur,
|
||||
cvec *nsc,
|
||||
char *format,
|
||||
...)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -1335,7 +1483,7 @@ xpath_vec_bool(cxobj *xcur,
|
|||
goto done;
|
||||
}
|
||||
va_end(ap);
|
||||
if (xpath_vec_ctx(xcur, xpath, &xr) < 0)
|
||||
if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0)
|
||||
goto done;
|
||||
if (xr)
|
||||
retval = ctx2boolean(xr);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue