* XPATH canonical form implemented for NETCONF get and get-config.
This commit is contained in:
parent
8cdb0bb062
commit
03acf8e19c
14 changed files with 430 additions and 86 deletions
|
|
@ -48,12 +48,12 @@
|
|||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
cvec *xml_nsctx_init(char *prefix, char *namespace);
|
||||
int xml_nsctx_free(cvec *nsc);
|
||||
char *xml_nsctx_get(cvec *nsc, char *prefix);
|
||||
int xml_nsctx_get_prefix(cvec *cvv, char *namespace, char **prefix);
|
||||
int xml_nsctx_set(cvec *nsc, char *prefix, char *namespace);
|
||||
cvec *xml_nsctx_init(char *prefix, char *namespace);
|
||||
int xml_nsctx_add(cvec *nsc, char *prefix, char *namespace);
|
||||
int xml_nsctx_node(cxobj *x, cvec **ncp);
|
||||
int xml_nsctx_yang(yang_stmt *yn, cvec **ncp);
|
||||
int xml_nsctx_free(cvec *nsc);
|
||||
|
||||
#endif /* _CLIXON_XML_NSCTX_H */
|
||||
|
|
|
|||
|
|
@ -115,8 +115,9 @@ typedef struct xpath_tree xpath_tree;
|
|||
char* xpath_tree_int2str(int nodetype);
|
||||
int xpath_tree_print_cb(cbuf *cb, xpath_tree *xs);
|
||||
int xpath_tree_print(FILE *f, xpath_tree *xs);
|
||||
int xpath_tree2cbuf(xpath_tree *xs, cbuf *xpathcb);
|
||||
int xpath_tree_free(xpath_tree *xs);
|
||||
int xpath_parse(cvec *nsc, char *xpath, xpath_tree **xptree);
|
||||
int xpath_parse(char *xpath, xpath_tree **xptree);
|
||||
int xpath_vec_ctx(cxobj *xcur, cvec *nsc, char *xpath, xp_ctx **xrp);
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ >= 3
|
||||
|
|
@ -151,5 +152,6 @@ int xpath_vec(cxobj *xcur, char *xpformat, cxobj ***vec, size_t *veclen, ...)
|
|||
cxobj *xpath_first(cxobj *xcur, char *xpformat, ...);
|
||||
int xpath_vec(cxobj *xcur, char *xpformat, cxobj ***vec, size_t *veclen, ...);
|
||||
#endif
|
||||
int xpath2canonical(char *xpath0, cvec *nsc0, yang_stmt *yspec, char **xpath1, cvec **nsc1);
|
||||
|
||||
#endif /* _CLIXON_XPATH_H */
|
||||
|
|
|
|||
|
|
@ -295,7 +295,7 @@ nscache_set(cxobj *x,
|
|||
goto done;
|
||||
}
|
||||
else
|
||||
return xml_nsctx_set(x->x_ns_cache, prefix, namespace);
|
||||
return xml_nsctx_add(x->x_ns_cache, prefix, namespace);
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
|
|
|
|||
|
|
@ -2513,7 +2513,7 @@ api_path2xpath_cvv(cvec *api_path,
|
|||
xprefix = yang_find_myprefix(y);
|
||||
clicon_debug(1, "%s prefix not found add it %s", __FUNCTION__, xprefix);
|
||||
/* not found, add it to nsc */
|
||||
if (xml_nsctx_set(nsc, xprefix, namespace) < 0)
|
||||
if (xml_nsctx_add(nsc, xprefix, namespace) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Check if has value, means '=' */
|
||||
|
|
|
|||
|
|
@ -68,6 +68,53 @@
|
|||
#include "clixon_xml.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
|
||||
/*! Create and initialize XML namespace context
|
||||
* @param[in] prefix Namespace prefix, or NULL for default
|
||||
* @param[in] namespace Set this namespace. If NULL create empty nsctx
|
||||
* @retval nsc Return namespace context in form of a cvec
|
||||
* @retval NULL Error
|
||||
* @code
|
||||
* cvec *nsc = NULL;
|
||||
* if ((nsc = xml_nsctx_init(NULL, "urn:example:example")) == NULL)
|
||||
* err;
|
||||
* ...
|
||||
* xml_nsctx_free(nsc);
|
||||
* @endcode
|
||||
* @see xml_nsctx_node Use namespace context of an existing XML node
|
||||
* @see xml_nsctx_free Free the reutned handle
|
||||
*/
|
||||
cvec *
|
||||
xml_nsctx_init(char *prefix,
|
||||
char *namespace)
|
||||
{
|
||||
cvec *cvv = NULL;
|
||||
|
||||
if ((cvv = cvec_new(0)) == NULL){
|
||||
clicon_err(OE_XML, errno, "cvec_new");
|
||||
goto done;
|
||||
}
|
||||
if (namespace && xml_nsctx_add(cvv, prefix, namespace) < 0)
|
||||
goto done;
|
||||
done:
|
||||
return cvv;
|
||||
}
|
||||
|
||||
/*! Free XML namespace context
|
||||
* @param[in] prefix Namespace prefix, or NULL for default
|
||||
* @param[in] namespace Cached namespace to set (assume non-null?)
|
||||
* @retval nsc Return namespace context in form of a cvec
|
||||
* @retval NULL Error
|
||||
*/
|
||||
int
|
||||
xml_nsctx_free(cvec *nsc)
|
||||
{
|
||||
cvec *cvv = (cvec*)nsc;
|
||||
|
||||
if (cvv)
|
||||
cvec_free(cvv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Get namespace given prefix (or NULL for default) from namespace context
|
||||
* @param[in] cvv Namespace context
|
||||
* @param[in] prefix Namespace prefix, or NULL for default
|
||||
|
|
@ -120,7 +167,7 @@ xml_nsctx_get_prefix(cvec *cvv,
|
|||
* @retval -1 Error
|
||||
*/
|
||||
int
|
||||
xml_nsctx_set(cvec *cvv,
|
||||
xml_nsctx_add(cvec *cvv,
|
||||
char *prefix,
|
||||
char *namespace)
|
||||
{
|
||||
|
|
@ -136,36 +183,6 @@ xml_nsctx_set(cvec *cvv,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Create and initialize XML namespace context
|
||||
* @param[in] prefix Namespace prefix, or NULL for default
|
||||
* @param[in] namespace Set this namespace. If NULL create empty nsctx
|
||||
* @retval nsc Return namespace context in form of a cvec
|
||||
* @retval NULL Error
|
||||
* @code
|
||||
* cvec *nsc = NULL;
|
||||
* if ((nsc = xml_nsctx_init(NULL, "urn:example:example")) == NULL)
|
||||
* err;
|
||||
* ...
|
||||
* xml_nsctx_free(nsc);
|
||||
* @endcode
|
||||
* @see xml_nsctx_node Use namespace context of an existing XML node
|
||||
* @see xml_nsctx_free Free the reutned handle
|
||||
*/
|
||||
cvec *
|
||||
xml_nsctx_init(char *prefix,
|
||||
char *namespace)
|
||||
{
|
||||
cvec *cvv = NULL;
|
||||
|
||||
if ((cvv = cvec_new(0)) == NULL){
|
||||
clicon_err(OE_XML, errno, "cvec_new");
|
||||
goto done;
|
||||
}
|
||||
if (namespace && xml_nsctx_set(cvv, prefix, namespace) < 0)
|
||||
goto done;
|
||||
done:
|
||||
return cvv;
|
||||
}
|
||||
|
||||
static int
|
||||
xml_nsctx_node1(cxobj *xn,
|
||||
|
|
@ -188,7 +205,7 @@ xml_nsctx_node1(cxobj *xn,
|
|||
if (strcmp(nm, "xmlns")==0 && /* set default namespace context */
|
||||
xml_nsctx_get(nsc, NULL) == NULL){
|
||||
val = xml_value(xa);
|
||||
if (xml_nsctx_set(nsc, NULL, val) < 0)
|
||||
if (xml_nsctx_add(nsc, NULL, val) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
|
@ -196,7 +213,7 @@ xml_nsctx_node1(cxobj *xn,
|
|||
if (strcmp(pf, "xmlns")==0 && /* set prefixed namespace context */
|
||||
xml_nsctx_get(nsc, nm) == NULL){
|
||||
val = xml_value(xa);
|
||||
if (xml_nsctx_set(nsc, nm, val) < 0)
|
||||
if (xml_nsctx_add(nsc, nm, val) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
|
@ -204,7 +221,7 @@ xml_nsctx_node1(cxobj *xn,
|
|||
#ifdef USE_NETCONF_NS_AS_DEFAULT
|
||||
/* If not default namespace defined, use the base netconf ns as default */
|
||||
if (xml_nsctx_get(nsc, NULL) == NULL)
|
||||
if (xml_nsctx_set(nsc, NULL, NETCONF_BASE_NAMESPACE) < 0)
|
||||
if (xml_nsctx_add(nsc, NULL, NETCONF_BASE_NAMESPACE) < 0)
|
||||
goto done;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -300,9 +317,9 @@ xml_nsctx_yang(yang_stmt *yn,
|
|||
goto done;
|
||||
}
|
||||
/* Add my prefix and default namespace (from real module) */
|
||||
if (xml_nsctx_set(nc, NULL, mynamespace) < 0)
|
||||
if (xml_nsctx_add(nc, NULL, mynamespace) < 0)
|
||||
goto done;
|
||||
if (xml_nsctx_set(nc, myprefix, mynamespace) < 0)
|
||||
if (xml_nsctx_add(nc, myprefix, mynamespace) < 0)
|
||||
goto done;
|
||||
/* Find top-most module or sub-module and get prefixes from that */
|
||||
if ((ymod = ys_module(yn)) == NULL){
|
||||
|
|
@ -328,7 +345,7 @@ xml_nsctx_yang(yang_stmt *yn,
|
|||
continue;
|
||||
if ((namespace = yang_argument_get(yns)) == NULL)
|
||||
continue;
|
||||
if (xml_nsctx_set(nc, prefix, namespace) < 0)
|
||||
if (xml_nsctx_add(nc, prefix, namespace) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
|
@ -338,18 +355,3 @@ xml_nsctx_yang(yang_stmt *yn,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Free XML namespace context
|
||||
* @param[in] prefix Namespace prefix, or NULL for default
|
||||
* @param[in] namespace Cached namespace to set (assume non-null?)
|
||||
* @retval nsc Return namespace context in form of a cvec
|
||||
* @retval NULL Error
|
||||
*/
|
||||
int
|
||||
xml_nsctx_free(cvec *nsc)
|
||||
{
|
||||
cvec *cvv = (cvec*)nsc;
|
||||
|
||||
if (cvv)
|
||||
cvec_free(cvv);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,6 +163,7 @@ xpath_tree_print_cb(cbuf *cb,
|
|||
/*! Print a xpath_tree
|
||||
* @param[in] f UNIX output stream
|
||||
* @param[in] xs XPATH tree
|
||||
* @see xpath_tree2str
|
||||
*/
|
||||
int
|
||||
xpath_tree_print(FILE *f,
|
||||
|
|
@ -182,7 +183,87 @@ xpath_tree_print(FILE *f,
|
|||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Create an xpath string from an xpath tree, ie "unparsing"
|
||||
* @param[in] xs XPATH tree
|
||||
* @param[out] xpath Xpath string as CLIgen buf
|
||||
* @see xpath_tree_print
|
||||
* @note NOT COMPLETE, just simple xpaths eg a/b
|
||||
*/
|
||||
int
|
||||
xpath_tree2cbuf(xpath_tree *xs,
|
||||
cbuf *xcb)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
switch (xs->xs_type){
|
||||
case XP_NODE: /* s0 is namespace prefix, s1 is name */
|
||||
if (xs->xs_s0)
|
||||
cprintf(xcb, "%s:", xs->xs_s0);
|
||||
cprintf(xcb, "%s", xs->xs_s1);
|
||||
break;
|
||||
case XP_ABSPATH:
|
||||
if (xs->xs_int == A_DESCENDANT_OR_SELF)
|
||||
cprintf(xcb, "/");
|
||||
cprintf(xcb, "/");
|
||||
break;
|
||||
case XP_PRIME_STR:
|
||||
cprintf(xcb, "'%s'", xs->xs_s0);
|
||||
break;
|
||||
case XP_PRIME_NR:
|
||||
cprintf(xcb, "%lf", xs->xs_double);
|
||||
break;
|
||||
case XP_STEP:
|
||||
switch (xs->xs_int){
|
||||
case A_SELF:
|
||||
cprintf(xcb, ".");
|
||||
break;
|
||||
case A_PARENT:
|
||||
cprintf(xcb, "..");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (xs->xs_c0 && xpath_tree2cbuf(xs->xs_c0, xcb) < 0)
|
||||
goto done;
|
||||
switch (xs->xs_type){
|
||||
case XP_RELLOCPATH:
|
||||
if (xs->xs_c1){
|
||||
if (xs->xs_int == A_DESCENDANT_OR_SELF)
|
||||
cprintf(xcb, "/");
|
||||
cprintf(xcb, "/");
|
||||
}
|
||||
break;
|
||||
case XP_PRED:
|
||||
if (xs->xs_c1)
|
||||
cprintf(xcb, "[");
|
||||
break;
|
||||
case XP_RELEX:
|
||||
if (xs->xs_c1)
|
||||
cprintf(xcb, "%s", clicon_int2str(xpopmap, xs->xs_int));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (xs->xs_c1 && xpath_tree2cbuf(xs->xs_c1, xcb) < 0)
|
||||
goto done;
|
||||
switch (xs->xs_type){
|
||||
case XP_PRED:
|
||||
if (xs->xs_c1)
|
||||
cprintf(xcb, "]");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Free a xpath_tree
|
||||
* @param[in] xs XPATH tree
|
||||
* @see xpath_parse creates a xpath_tree
|
||||
|
|
@ -202,26 +283,22 @@ xpath_tree_free(xpath_tree *xs)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! 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] nsc External XML namespace context, or NULL
|
||||
/*! Given xpath, parse it, and return structured xpath tree
|
||||
* @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
|
||||
* xpath_tree *xpt = NULL;
|
||||
* if (xpath_parse(NULL, xpath, &xpt) < 0)
|
||||
* if (xpath_parse(xpath, &xpt) < 0)
|
||||
* err;
|
||||
* if (xpt)
|
||||
* xptree_free(xpt);
|
||||
* xpath_tree_free(xpt);
|
||||
* @endcode
|
||||
* @see xpath_tree_free
|
||||
*/
|
||||
int
|
||||
xpath_parse(cvec *nsc,
|
||||
char *xpath,
|
||||
xpath_parse(char *xpath,
|
||||
xpath_tree **xptree)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -264,7 +341,7 @@ xpath_parse(cvec *nsc,
|
|||
* 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 External XML namespace context, or NULL
|
||||
* @param[in] nsc External XML namespace context, or NULL
|
||||
* @param[in] xpath String with XPATH 1.0 syntax
|
||||
* @param[out] xrp Return XPATH context
|
||||
* @retval 0 OK
|
||||
|
|
@ -287,7 +364,7 @@ xpath_vec_ctx(cxobj *xcur,
|
|||
xpath_tree *xptree = NULL;
|
||||
xp_ctx xc = {0,};
|
||||
|
||||
if (xpath_parse(nsc, xpath, &xptree) < 0)
|
||||
if (xpath_parse(xpath, &xptree) < 0)
|
||||
goto done;
|
||||
xc.xc_type = XT_NODESET;
|
||||
xc.xc_node = xcur;
|
||||
|
|
@ -688,3 +765,136 @@ xpath_vec_bool(cxobj *xcur,
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
traverse_canonical(xpath_tree *xs,
|
||||
yang_stmt *yspec,
|
||||
cvec *nsc0,
|
||||
cvec *nsc1)
|
||||
{
|
||||
int retval = -1;
|
||||
char *prefix0;
|
||||
char *prefix1;
|
||||
char *namespace;
|
||||
yang_stmt *ymod;
|
||||
|
||||
switch (xs->xs_type){
|
||||
case XP_NODE: /* s0 is namespace prefix, s1 is name */
|
||||
prefix0 = xs->xs_s0;
|
||||
if ((namespace = xml_nsctx_get(nsc0, prefix0)) == NULL){
|
||||
clicon_err(OE_XML, ENOENT, "No namespace found for prefix: %s",
|
||||
prefix0);
|
||||
goto done;
|
||||
}
|
||||
if ((ymod = yang_find_module_by_namespace(yspec, namespace)) == NULL){
|
||||
clicon_err(OE_XML, ENOENT, "No modules found for namespace: %s",
|
||||
namespace);
|
||||
goto done;
|
||||
}
|
||||
if ((prefix1 = yang_find_myprefix(ymod)) == NULL){
|
||||
clicon_err(OE_XML, ENOENT, "No prefix found in module: %s",
|
||||
yang_argument_get(ymod));
|
||||
goto done;
|
||||
}
|
||||
if (xml_nsctx_get(nsc1, prefix1) == NULL)
|
||||
if (xml_nsctx_add(nsc1, prefix1, namespace) < 0)
|
||||
goto done;
|
||||
if (prefix0==NULL || strcmp(prefix0, prefix1) != 0){
|
||||
if (xs->xs_s0)
|
||||
free(xs->xs_s0);
|
||||
if ((xs->xs_s0 = strdup(prefix1)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (xs->xs_c0)
|
||||
if (traverse_canonical(xs->xs_c0, yspec, nsc0, nsc1) < 0)
|
||||
goto done;
|
||||
if (xs->xs_c1)
|
||||
if (traverse_canonical(xs->xs_c1, yspec, nsc0, nsc1) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Translate an xpath/nsc pair to a "canonical" form using yang prefixes
|
||||
* @param[in] xpath0 Input xpath
|
||||
* @param[in] nsc0 Input namespace context
|
||||
* @param[in] yspec Yang spec containing all modules, associated with namespaces
|
||||
* @param[out] xpath1 Output xpath. Free after use with free
|
||||
* @param[out] nsc1 Output namespace context. Free after use with xml_nsctx_free
|
||||
* @retval 0 OK, xpath1 and nsc1 allocated
|
||||
* @retval -1 Error
|
||||
* Example:
|
||||
* Module A has prefix a and namespace urn:example:a and symbols x
|
||||
* Module B with prefix b and namespace urn:example:b and symbols y
|
||||
* Then incoming:
|
||||
* xpath0: /x/c:y
|
||||
* nsc0: NULL:"urn:example:a"; c:"urn:example:b"
|
||||
* will be translated to:
|
||||
* xpath1: /a:x/b:y
|
||||
* nsc1: a:"urn:example:a"; b:"urn:example:b"
|
||||
* @code
|
||||
* char *xpath1 = NULL;
|
||||
* cvec *nsc1 = NULL;
|
||||
* if (xpath2canonical(xpath0, nsc0, yspec, &xpath1, &nsc1) < 0)
|
||||
* err;
|
||||
* ...
|
||||
* if (xpath1) free(xpath1);
|
||||
* if (nsc1) xml_nsctx_free(nsc1);
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
xpath2canonical(char *xpath0,
|
||||
cvec *nsc0,
|
||||
yang_stmt *yspec,
|
||||
char **xpath1,
|
||||
cvec **nsc1p)
|
||||
{
|
||||
int retval = -1;
|
||||
xpath_tree *xpt = NULL;
|
||||
cvec *nsc1 = NULL;
|
||||
cbuf *xcb = NULL;
|
||||
|
||||
/* Parse input xpath into an xpath-tree */
|
||||
if (xpath_parse(xpath0, &xpt) < 0)
|
||||
goto done;
|
||||
/* Create new nsc */
|
||||
if ((nsc1 = xml_nsctx_init(NULL, NULL)) == NULL)
|
||||
goto done;
|
||||
/* Traverse tree to find prefixes, transform them to canonical form and
|
||||
* create a canonical network namespace
|
||||
*/
|
||||
if (traverse_canonical(xpt, yspec, nsc0, nsc1) < 0)
|
||||
goto done;
|
||||
/* Print tree with new prefixes */
|
||||
if ((xcb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if (xpath_tree2cbuf(xpt, xcb) < 0)
|
||||
goto done;
|
||||
if (xpath1){
|
||||
if ((*xpath1 = strdup(cbuf_get(xcb))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if (nsc1p){
|
||||
*nsc1p = nsc1;
|
||||
nsc1 = NULL;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (xcb)
|
||||
cbuf_free(xcb);
|
||||
if (nsc1)
|
||||
xml_nsctx_free(nsc1);
|
||||
if (xpt)
|
||||
xpath_tree_free(xpt);
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -807,8 +807,8 @@ yang_find_mynamespace(yang_stmt *ys)
|
|||
* @param[out] prefix Local prefix to access module with (direct pointer)
|
||||
* @retval 0 not found
|
||||
* @retval -1 found
|
||||
* @code
|
||||
* @note prefix NULL is not returned, if own module, then return its prefix
|
||||
* @code
|
||||
* char *pfx = yang_find_prefix_by_namespace(ys, "urn:example:clixon", &prefix);
|
||||
* @endcode
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue