* 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
|
|
@ -39,6 +39,8 @@
|
||||||
* Replaced JSON `null` with `[null]` as proper empty JSON leaf/leaf-list encoding.
|
* Replaced JSON `null` with `[null]` as proper empty JSON leaf/leaf-list encoding.
|
||||||
|
|
||||||
### Minor changes
|
### Minor changes
|
||||||
|
* XPATH canonical form implemented for NETCONF get and get-config. This means that all callbacks (including state callbacks) will have the prefixes that corresponds to module prefixes. If an xpath have other prefixes (or null as default), they will be translated to canonical form before any callbacks.
|
||||||
|
* Example of a canonical form: `/a:x/a:y`, then symbols must belong to a yang module with prefix `a`.
|
||||||
* Configure and test modification for better Freebsd port
|
* Configure and test modification for better Freebsd port
|
||||||
|
|
||||||
### Corrected Bugs
|
### Corrected Bugs
|
||||||
|
|
|
||||||
|
|
@ -369,7 +369,7 @@ from_client_get_config(clicon_handle h,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *db;
|
char *db;
|
||||||
cxobj *xfilter;
|
cxobj *xfilter;
|
||||||
char *xpath = "/";
|
char *xpath = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cbuf *cbx = NULL; /* Assist cbuf */
|
cbuf *cbx = NULL; /* Assist cbuf */
|
||||||
cxobj *xnacm = NULL;
|
cxobj *xnacm = NULL;
|
||||||
|
|
@ -378,8 +378,13 @@ from_client_get_config(clicon_handle h,
|
||||||
int ret;
|
int ret;
|
||||||
char *username;
|
char *username;
|
||||||
cvec *nsc = NULL; /* Create a netconf namespace context from filter */
|
cvec *nsc = NULL; /* Create a netconf namespace context from filter */
|
||||||
|
yang_stmt *yspec;
|
||||||
|
|
||||||
username = clicon_username_get(h);
|
username = clicon_username_get(h);
|
||||||
|
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||||
|
clicon_err(OE_YANG, ENOENT, "No yang spec9");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
if ((db = netconf_db_find(xe, "source")) == NULL){
|
if ((db = netconf_db_find(xe, "source")) == NULL){
|
||||||
clicon_err(OE_XML, 0, "db not found");
|
clicon_err(OE_XML, 0, "db not found");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -395,8 +400,11 @@ from_client_get_config(clicon_handle h,
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
if ((xfilter = xml_find(xe, "filter")) != NULL){
|
if ((xfilter = xml_find(xe, "filter")) != NULL){
|
||||||
if ((xpath = xml_find_value(xfilter, "select"))==NULL)
|
char *xpath0;
|
||||||
xpath="/";
|
cvec *nsc1 = NULL;
|
||||||
|
|
||||||
|
if ((xpath0 = xml_find_value(xfilter, "select"))==NULL)
|
||||||
|
xpath0="/";
|
||||||
/* Create namespace context for xpath from <filter>
|
/* Create namespace context for xpath from <filter>
|
||||||
* The set of namespace declarations are those in scope on the
|
* The set of namespace declarations are those in scope on the
|
||||||
* <filter> element.
|
* <filter> element.
|
||||||
|
|
@ -404,6 +412,11 @@ from_client_get_config(clicon_handle h,
|
||||||
else
|
else
|
||||||
if (xml_nsctx_node(xfilter, &nsc) < 0)
|
if (xml_nsctx_node(xfilter, &nsc) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (xpath2canonical(xpath0, nsc, yspec, &xpath, &nsc1) < 0)
|
||||||
|
goto done;
|
||||||
|
if (nsc)
|
||||||
|
xml_nsctx_free(nsc);
|
||||||
|
nsc = nsc1;
|
||||||
}
|
}
|
||||||
/* Note xret can be pruned by nacm below (and change name),
|
/* Note xret can be pruned by nacm below (and change name),
|
||||||
* so zero-copy cant be used
|
* so zero-copy cant be used
|
||||||
|
|
@ -437,6 +450,8 @@ from_client_get_config(clicon_handle h,
|
||||||
ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
if (xpath)
|
||||||
|
free(xpath);
|
||||||
if (xnacm)
|
if (xnacm)
|
||||||
xml_free(xnacm);
|
xml_free(xnacm);
|
||||||
if (xvec)
|
if (xvec)
|
||||||
|
|
@ -868,7 +883,7 @@ from_client_get(clicon_handle h,
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *xfilter;
|
cxobj *xfilter;
|
||||||
char *xpath = "/";
|
char *xpath = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
cxobj **xvec = NULL;
|
cxobj **xvec = NULL;
|
||||||
|
|
@ -879,11 +894,18 @@ from_client_get(clicon_handle h,
|
||||||
char *attr;
|
char *attr;
|
||||||
netconf_content content = CONTENT_ALL;
|
netconf_content content = CONTENT_ALL;
|
||||||
int32_t depth = -1; /* Nr of levels to print, -1 is all, 0 is none */
|
int32_t depth = -1; /* Nr of levels to print, -1 is all, 0 is none */
|
||||||
|
yang_stmt *yspec;
|
||||||
|
|
||||||
username = clicon_username_get(h);
|
username = clicon_username_get(h);
|
||||||
|
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||||
|
clicon_err(OE_YANG, ENOENT, "No yang spec9");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
if ((xfilter = xml_find(xe, "filter")) != NULL){
|
if ((xfilter = xml_find(xe, "filter")) != NULL){
|
||||||
if ((xpath = xml_find_value(xfilter, "select"))==NULL)
|
char *xpath0;
|
||||||
xpath="/";
|
cvec *nsc1 = NULL;
|
||||||
|
if ((xpath0 = xml_find_value(xfilter, "select"))==NULL)
|
||||||
|
xpath0 = "/";
|
||||||
/* Create namespace context for xpath from <filter>
|
/* Create namespace context for xpath from <filter>
|
||||||
* The set of namespace declarations are those in scope on the
|
* The set of namespace declarations are those in scope on the
|
||||||
* <filter> element.
|
* <filter> element.
|
||||||
|
|
@ -891,6 +913,11 @@ from_client_get(clicon_handle h,
|
||||||
else
|
else
|
||||||
if (xml_nsctx_node(xfilter, &nsc) < 0)
|
if (xml_nsctx_node(xfilter, &nsc) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (xpath2canonical(xpath0, nsc, yspec, &xpath, &nsc1) < 0)
|
||||||
|
goto done;
|
||||||
|
if (nsc)
|
||||||
|
xml_nsctx_free(nsc);
|
||||||
|
nsc = nsc1;
|
||||||
}
|
}
|
||||||
/* Clixon extensions: depth and content */
|
/* Clixon extensions: depth and content */
|
||||||
if ((attr = xml_find_value(xe, "content")) != NULL)
|
if ((attr = xml_find_value(xe, "content")) != NULL)
|
||||||
|
|
@ -909,7 +936,6 @@ from_client_get(clicon_handle h,
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content != CONTENT_NONCONFIG){
|
if (content != CONTENT_NONCONFIG){
|
||||||
/* Get config
|
/* Get config
|
||||||
* Note xret can be pruned by nacm below and change name and
|
* Note xret can be pruned by nacm below and change name and
|
||||||
|
|
@ -958,6 +984,8 @@ from_client_get(clicon_handle h,
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||||
|
if (xpath)
|
||||||
|
free(xpath);
|
||||||
if (xnacm)
|
if (xnacm)
|
||||||
xml_free(xnacm);
|
xml_free(xnacm);
|
||||||
if (xvec)
|
if (xvec)
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,6 @@ api_data_post(clicon_handle h,
|
||||||
cxobj *xtop = NULL; /* top of api-path */
|
cxobj *xtop = NULL; /* top of api-path */
|
||||||
cxobj *xbot = NULL; /* bottom of api-path */
|
cxobj *xbot = NULL; /* bottom of api-path */
|
||||||
yang_stmt *ybot = NULL; /* yang of xbot */
|
yang_stmt *ybot = NULL; /* yang of xbot */
|
||||||
yang_stmt *ymodapi = NULL; /* yang module of api-path (if any) */
|
|
||||||
yang_stmt *ymoddata = NULL; /* yang module of data (-d) */
|
yang_stmt *ymoddata = NULL; /* yang module of data (-d) */
|
||||||
yang_stmt *yspec;
|
yang_stmt *yspec;
|
||||||
yang_stmt *ydata;
|
yang_stmt *ydata;
|
||||||
|
|
@ -156,8 +155,6 @@ api_data_post(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
if (ybot)
|
|
||||||
ymodapi = ys_module(ybot);
|
|
||||||
}
|
}
|
||||||
#if 1
|
#if 1
|
||||||
if (debug){
|
if (debug){
|
||||||
|
|
|
||||||
|
|
@ -48,12 +48,12 @@
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
|
cvec *xml_nsctx_init(char *prefix, char *namespace);
|
||||||
|
int xml_nsctx_free(cvec *nsc);
|
||||||
char *xml_nsctx_get(cvec *nsc, char *prefix);
|
char *xml_nsctx_get(cvec *nsc, char *prefix);
|
||||||
int xml_nsctx_get_prefix(cvec *cvv, char *namespace, char **prefix);
|
int xml_nsctx_get_prefix(cvec *cvv, char *namespace, char **prefix);
|
||||||
int xml_nsctx_set(cvec *nsc, char *prefix, char *namespace);
|
int xml_nsctx_add(cvec *nsc, char *prefix, char *namespace);
|
||||||
cvec *xml_nsctx_init(char *prefix, char *namespace);
|
|
||||||
int xml_nsctx_node(cxobj *x, cvec **ncp);
|
int xml_nsctx_node(cxobj *x, cvec **ncp);
|
||||||
int xml_nsctx_yang(yang_stmt *yn, cvec **ncp);
|
int xml_nsctx_yang(yang_stmt *yn, cvec **ncp);
|
||||||
int xml_nsctx_free(cvec *nsc);
|
|
||||||
|
|
||||||
#endif /* _CLIXON_XML_NSCTX_H */
|
#endif /* _CLIXON_XML_NSCTX_H */
|
||||||
|
|
|
||||||
|
|
@ -115,8 +115,9 @@ typedef struct xpath_tree xpath_tree;
|
||||||
char* xpath_tree_int2str(int nodetype);
|
char* xpath_tree_int2str(int nodetype);
|
||||||
int xpath_tree_print_cb(cbuf *cb, xpath_tree *xs);
|
int xpath_tree_print_cb(cbuf *cb, xpath_tree *xs);
|
||||||
int xpath_tree_print(FILE *f, 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_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);
|
int xpath_vec_ctx(cxobj *xcur, cvec *nsc, char *xpath, xp_ctx **xrp);
|
||||||
|
|
||||||
#if defined(__GNUC__) && __GNUC__ >= 3
|
#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, ...);
|
cxobj *xpath_first(cxobj *xcur, char *xpformat, ...);
|
||||||
int xpath_vec(cxobj *xcur, char *xpformat, cxobj ***vec, size_t *veclen, ...);
|
int xpath_vec(cxobj *xcur, char *xpformat, cxobj ***vec, size_t *veclen, ...);
|
||||||
#endif
|
#endif
|
||||||
|
int xpath2canonical(char *xpath0, cvec *nsc0, yang_stmt *yspec, char **xpath1, cvec **nsc1);
|
||||||
|
|
||||||
#endif /* _CLIXON_XPATH_H */
|
#endif /* _CLIXON_XPATH_H */
|
||||||
|
|
|
||||||
|
|
@ -295,7 +295,7 @@ nscache_set(cxobj *x,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return xml_nsctx_set(x->x_ns_cache, prefix, namespace);
|
return xml_nsctx_add(x->x_ns_cache, prefix, namespace);
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
|
|
|
||||||
|
|
@ -2513,7 +2513,7 @@ api_path2xpath_cvv(cvec *api_path,
|
||||||
xprefix = yang_find_myprefix(y);
|
xprefix = yang_find_myprefix(y);
|
||||||
clicon_debug(1, "%s prefix not found add it %s", __FUNCTION__, xprefix);
|
clicon_debug(1, "%s prefix not found add it %s", __FUNCTION__, xprefix);
|
||||||
/* not found, add it to nsc */
|
/* not found, add it to nsc */
|
||||||
if (xml_nsctx_set(nsc, xprefix, namespace) < 0)
|
if (xml_nsctx_add(nsc, xprefix, namespace) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* Check if has value, means '=' */
|
/* Check if has value, means '=' */
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,53 @@
|
||||||
#include "clixon_xml.h"
|
#include "clixon_xml.h"
|
||||||
#include "clixon_xml_nsctx.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
|
/*! Get namespace given prefix (or NULL for default) from namespace context
|
||||||
* @param[in] cvv Namespace context
|
* @param[in] cvv Namespace context
|
||||||
* @param[in] prefix Namespace prefix, or NULL for default
|
* @param[in] prefix Namespace prefix, or NULL for default
|
||||||
|
|
@ -120,7 +167,7 @@ xml_nsctx_get_prefix(cvec *cvv,
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml_nsctx_set(cvec *cvv,
|
xml_nsctx_add(cvec *cvv,
|
||||||
char *prefix,
|
char *prefix,
|
||||||
char *namespace)
|
char *namespace)
|
||||||
{
|
{
|
||||||
|
|
@ -136,36 +183,6 @@ xml_nsctx_set(cvec *cvv,
|
||||||
return retval;
|
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
|
static int
|
||||||
xml_nsctx_node1(cxobj *xn,
|
xml_nsctx_node1(cxobj *xn,
|
||||||
|
|
@ -188,7 +205,7 @@ xml_nsctx_node1(cxobj *xn,
|
||||||
if (strcmp(nm, "xmlns")==0 && /* set default namespace context */
|
if (strcmp(nm, "xmlns")==0 && /* set default namespace context */
|
||||||
xml_nsctx_get(nsc, NULL) == NULL){
|
xml_nsctx_get(nsc, NULL) == NULL){
|
||||||
val = xml_value(xa);
|
val = xml_value(xa);
|
||||||
if (xml_nsctx_set(nsc, NULL, val) < 0)
|
if (xml_nsctx_add(nsc, NULL, val) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -196,7 +213,7 @@ xml_nsctx_node1(cxobj *xn,
|
||||||
if (strcmp(pf, "xmlns")==0 && /* set prefixed namespace context */
|
if (strcmp(pf, "xmlns")==0 && /* set prefixed namespace context */
|
||||||
xml_nsctx_get(nsc, nm) == NULL){
|
xml_nsctx_get(nsc, nm) == NULL){
|
||||||
val = xml_value(xa);
|
val = xml_value(xa);
|
||||||
if (xml_nsctx_set(nsc, nm, val) < 0)
|
if (xml_nsctx_add(nsc, nm, val) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -204,7 +221,7 @@ xml_nsctx_node1(cxobj *xn,
|
||||||
#ifdef USE_NETCONF_NS_AS_DEFAULT
|
#ifdef USE_NETCONF_NS_AS_DEFAULT
|
||||||
/* If not default namespace defined, use the base 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_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;
|
goto done;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
@ -300,9 +317,9 @@ xml_nsctx_yang(yang_stmt *yn,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* Add my prefix and default namespace (from real module) */
|
/* 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;
|
goto done;
|
||||||
if (xml_nsctx_set(nc, myprefix, mynamespace) < 0)
|
if (xml_nsctx_add(nc, myprefix, mynamespace) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Find top-most module or sub-module and get prefixes from that */
|
/* Find top-most module or sub-module and get prefixes from that */
|
||||||
if ((ymod = ys_module(yn)) == NULL){
|
if ((ymod = ys_module(yn)) == NULL){
|
||||||
|
|
@ -328,7 +345,7 @@ xml_nsctx_yang(yang_stmt *yn,
|
||||||
continue;
|
continue;
|
||||||
if ((namespace = yang_argument_get(yns)) == NULL)
|
if ((namespace = yang_argument_get(yns)) == NULL)
|
||||||
continue;
|
continue;
|
||||||
if (xml_nsctx_set(nc, prefix, namespace) < 0)
|
if (xml_nsctx_add(nc, prefix, namespace) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -338,18 +355,3 @@ xml_nsctx_yang(yang_stmt *yn,
|
||||||
return retval;
|
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
|
/*! Print a xpath_tree
|
||||||
* @param[in] f UNIX output stream
|
* @param[in] f UNIX output stream
|
||||||
* @param[in] xs XPATH tree
|
* @param[in] xs XPATH tree
|
||||||
|
* @see xpath_tree2str
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xpath_tree_print(FILE *f,
|
xpath_tree_print(FILE *f,
|
||||||
|
|
@ -183,6 +184,86 @@ xpath_tree_print(FILE *f,
|
||||||
return retval;
|
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
|
/*! Free a xpath_tree
|
||||||
* @param[in] xs XPATH tree
|
* @param[in] xs XPATH tree
|
||||||
* @see xpath_parse creates a xpath_tree
|
* @see xpath_parse creates a xpath_tree
|
||||||
|
|
@ -202,26 +283,22 @@ xpath_tree_free(xpath_tree *xs)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Given XML tree and xpath, parse xpath, eval it and return xpath context,
|
/*! Given xpath, parse it, and return structured xpath tree
|
||||||
* 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
|
|
||||||
* @param[in] xpath String with XPATH 1.0 syntax
|
* @param[in] xpath String with XPATH 1.0 syntax
|
||||||
* @param[out] xptree Xpath-tree, parsed, structured XPATH, free:xpath_tree_free
|
* @param[out] xptree Xpath-tree, parsed, structured XPATH, free:xpath_tree_free
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @code
|
* @code
|
||||||
* xpath_tree *xpt = NULL;
|
* xpath_tree *xpt = NULL;
|
||||||
* if (xpath_parse(NULL, xpath, &xpt) < 0)
|
* if (xpath_parse(xpath, &xpt) < 0)
|
||||||
* err;
|
* err;
|
||||||
* if (xpt)
|
* if (xpt)
|
||||||
* xptree_free(xpt);
|
* xpath_tree_free(xpt);
|
||||||
* @endcode
|
* @endcode
|
||||||
* @see xpath_tree_free
|
* @see xpath_tree_free
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xpath_parse(cvec *nsc,
|
xpath_parse(char *xpath,
|
||||||
char *xpath,
|
|
||||||
xpath_tree **xptree)
|
xpath_tree **xptree)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
@ -287,7 +364,7 @@ xpath_vec_ctx(cxobj *xcur,
|
||||||
xpath_tree *xptree = NULL;
|
xpath_tree *xptree = NULL;
|
||||||
xp_ctx xc = {0,};
|
xp_ctx xc = {0,};
|
||||||
|
|
||||||
if (xpath_parse(nsc, xpath, &xptree) < 0)
|
if (xpath_parse(xpath, &xptree) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
xc.xc_type = XT_NODESET;
|
xc.xc_type = XT_NODESET;
|
||||||
xc.xc_node = xcur;
|
xc.xc_node = xcur;
|
||||||
|
|
@ -688,3 +765,136 @@ xpath_vec_bool(cxobj *xcur,
|
||||||
return retval;
|
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)
|
* @param[out] prefix Local prefix to access module with (direct pointer)
|
||||||
* @retval 0 not found
|
* @retval 0 not found
|
||||||
* @retval -1 found
|
* @retval -1 found
|
||||||
* @code
|
|
||||||
* @note prefix NULL is not returned, if own module, then return its prefix
|
* @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);
|
* char *pfx = yang_find_prefix_by_namespace(ys, "urn:example:clixon", &prefix);
|
||||||
* @endcode
|
* @endcode
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -334,7 +334,7 @@ expecteq(){
|
||||||
# Example: expecteq $(fn arg) 0 "my return"
|
# Example: expecteq $(fn arg) 0 "my return"
|
||||||
# - evaluated expression
|
# - evaluated expression
|
||||||
# - expected command return value (0 if OK)
|
# - expected command return value (0 if OK)
|
||||||
# - expected stdout outcome
|
# - expected stdout outcome*
|
||||||
expectpart(){
|
expectpart(){
|
||||||
r=$?
|
r=$?
|
||||||
ret=$1
|
ret=$1
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,11 @@ s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||||
xml=$dir/xml.xml
|
xml=$dir/xml.xml
|
||||||
xml2=$dir/xml2.xml
|
xml2=$dir/xml2.xml
|
||||||
xml3=$dir/xml3.xml
|
xml3=$dir/xml3.xml
|
||||||
|
ydir=$dir/yang
|
||||||
|
if [ ! -d $ydir ]; then
|
||||||
|
mkdir $ydir
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
cat <<EOF > $xml
|
cat <<EOF > $xml
|
||||||
<aaa>
|
<aaa>
|
||||||
|
|
@ -203,4 +208,54 @@ expecteof "$clixon_util_xpath -f $xml3 -p bbb[ccc='fie']" 0 "" "^nodeset:$"
|
||||||
new "xpath derived-from-or-self"
|
new "xpath derived-from-or-self"
|
||||||
expecteof "$clixon_util_xpath -f $xml3 -p 'derived-from-or-self(../../change-operation,modify)'" 0 "" "derived-from-or-self"
|
expecteof "$clixon_util_xpath -f $xml3 -p 'derived-from-or-self(../../change-operation,modify)'" 0 "" "derived-from-or-self"
|
||||||
|
|
||||||
|
# canonical namespace xpath tests
|
||||||
|
# need yang modules
|
||||||
|
cat <<EOF > $ydir/a.yang
|
||||||
|
module a{
|
||||||
|
namespace "urn:example:a";
|
||||||
|
prefix a;
|
||||||
|
container x{
|
||||||
|
leaf xa{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF > $ydir/b.yang
|
||||||
|
module b{
|
||||||
|
namespace "urn:example:b";
|
||||||
|
prefix b;
|
||||||
|
container y{
|
||||||
|
leaf ya{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
new "xpath canonical form (already canonical)"
|
||||||
|
expectpart "$($clixon_util_xpath -c -y $ydir -p /a:x/b:y -n a:urn:example:a -n b:urn:example:b)" 0 '/a:x/b:y' '0 : a = "urn:example:a"' '1 : b = "urn:example:b"'
|
||||||
|
|
||||||
|
new "xpath canonical form (default)"
|
||||||
|
expectpart "$($clixon_util_xpath -c -y $ydir -p /x/b:y -n null:urn:example:a -n b:urn:example:b)" 0 '/a:x/b:y' '0 : a = "urn:example:a"' '1 : b = "urn:example:b"'
|
||||||
|
|
||||||
|
new "xpath canonical form (other)"
|
||||||
|
expectpart "$($clixon_util_xpath -c -y $ydir -p /i:x/j:y -n i:urn:example:a -n j:urn:example:b)" 0 '/a:x/b:y' '0 : a = "urn:example:a"' '1 : b = "urn:example:b"'
|
||||||
|
|
||||||
|
new "xpath canonical form predicate 1"
|
||||||
|
expectpart "$($clixon_util_xpath -c -y $ydir -p "/i:x[j:y='e1']" -n i:urn:example:a -n j:urn:example:b)" 0 "/a:x\[b:y='e1'\]" '0 : a = "urn:example:a"' '1 : b = "urn:example:b"'
|
||||||
|
|
||||||
|
new "xpath canonical form predicate self"
|
||||||
|
expectpart "$($clixon_util_xpath -c -y $ydir -p "/i:x[.='42']" -n i:urn:example:a -n j:urn:example:b)" 0 "/a:x\[.='42'\]" '0 : a = "urn:example:a"'
|
||||||
|
|
||||||
|
new "xpath canonical form descendants"
|
||||||
|
expectpart "$($clixon_util_xpath -c -y $ydir -p "//x[.='42']" -n null:urn:example:a -n j:urn:example:b)" 0 "//a:x\[.='42'\]" '0 : a = "urn:example:a"'
|
||||||
|
|
||||||
|
new "xpath canonical form (no default should fail)"
|
||||||
|
expectpart "$($clixon_util_xpath -c -y $ydir -p /x/j:y -n i:urn:example:a -n j:urn:example:b)" 255
|
||||||
|
|
||||||
|
new "xpath canonical form (wrong namespace should fail)"
|
||||||
|
expectpart "$($clixon_util_xpath -c -y $ydir -p /i:x/j:y -n i:urn:example:c -n j:urn:example:b)" 255
|
||||||
|
|
||||||
rm -rf $dir
|
rm -rf $dir
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,7 @@ main(int argc, char **argv)
|
||||||
clicon_debug(1, "xi:");
|
clicon_debug(1, "xi:");
|
||||||
xml_print(stderr, xi);
|
xml_print(stderr, xi);
|
||||||
}
|
}
|
||||||
if (xml_insert(xb, xi, INS_LAST, NULL) < 0)
|
if (xml_insert(xb, xi, INS_LAST, NULL, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (debug){
|
if (debug){
|
||||||
clicon_debug(1, "x0:");
|
clicon_debug(1, "x0:");
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,8 @@ usage(char *argv0)
|
||||||
"\t-f <file> \tXML file\n"
|
"\t-f <file> \tXML file\n"
|
||||||
"\t-p <xpath> \tPrimary XPATH string\n"
|
"\t-p <xpath> \tPrimary XPATH string\n"
|
||||||
"\t-i <xpath0>\t(optional) Initial XPATH string\n"
|
"\t-i <xpath0>\t(optional) Initial XPATH string\n"
|
||||||
|
"\t-n <pfx:id>\tNamespace binding (pfx=NULL for default)\n"
|
||||||
|
"\t-c \t\tMap xpath to canonical form\n"
|
||||||
"\t-y <filename> \tYang filename or dir (load all files)\n"
|
"\t-y <filename> \tYang filename or dir (load all files)\n"
|
||||||
"\t-Y <dir> \tYang dirs (can be several)\n"
|
"\t-Y <dir> \tYang dirs (can be several)\n"
|
||||||
"and the following extra rules:\n"
|
"and the following extra rules:\n"
|
||||||
|
|
@ -131,6 +133,8 @@ main(int argc,
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
clicon_handle h;
|
clicon_handle h;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
cvec *nsc = NULL;
|
||||||
|
int canonical = 0;
|
||||||
|
|
||||||
clicon_log_init("xpath", LOG_DEBUG, CLICON_LOG_STDERR);
|
clicon_log_init("xpath", LOG_DEBUG, CLICON_LOG_STDERR);
|
||||||
|
|
||||||
|
|
@ -139,7 +143,7 @@ main(int argc,
|
||||||
|
|
||||||
optind = 1;
|
optind = 1;
|
||||||
opterr = 0;
|
opterr = 0;
|
||||||
while ((c = getopt(argc, argv, "hD:f:p:i:y:Y:")) != -1)
|
while ((c = getopt(argc, argv, "hD:f:p:i:n:cy:Y:")) != -1)
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'h':
|
case 'h':
|
||||||
usage(argv0);
|
usage(argv0);
|
||||||
|
|
@ -161,6 +165,29 @@ main(int argc,
|
||||||
case 'i': /* Optional initial XPATH string */
|
case 'i': /* Optional initial XPATH string */
|
||||||
xpath0 = optarg;
|
xpath0 = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'n':{ /* Namespace binding */
|
||||||
|
char *prefix;
|
||||||
|
char *id;
|
||||||
|
if (nsc == NULL &&
|
||||||
|
(nsc = xml_nsctx_init(NULL, NULL)) == NULL)
|
||||||
|
goto done;
|
||||||
|
if (nodeid_split(optarg, &prefix, &id) < 0)
|
||||||
|
goto done;
|
||||||
|
if (prefix && strcmp(prefix, "null")==0){
|
||||||
|
free(prefix);
|
||||||
|
prefix = NULL;
|
||||||
|
}
|
||||||
|
if (xml_nsctx_add(nsc, prefix, id) < 0)
|
||||||
|
goto done;
|
||||||
|
if (prefix)
|
||||||
|
free(prefix);
|
||||||
|
if (id)
|
||||||
|
free(id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'c': /* Map namespace to canonical form */
|
||||||
|
canonical = 1;
|
||||||
|
break;
|
||||||
case 'y':
|
case 'y':
|
||||||
yang_file_dir = optarg;
|
yang_file_dir = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
@ -220,6 +247,24 @@ main(int argc,
|
||||||
}
|
}
|
||||||
xpath = buf;
|
xpath = buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If canonical, translate nsc and xpath to canonical form */
|
||||||
|
if (canonical){
|
||||||
|
char *xpath1 = NULL;
|
||||||
|
cvec *nsc1 = NULL;
|
||||||
|
if (xpath2canonical(xpath, nsc, yspec, &xpath1, &nsc1) < 0)
|
||||||
|
goto done;
|
||||||
|
xpath = xpath1;
|
||||||
|
if (xpath)
|
||||||
|
fprintf(stdout, "%s\n", xpath);
|
||||||
|
if (nsc)
|
||||||
|
xml_nsctx_free(nsc);
|
||||||
|
nsc = nsc1;
|
||||||
|
if (nsc)
|
||||||
|
cvec_print(stdout, nsc);
|
||||||
|
goto ok; /* need a switch to continue, now just print and quit */
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If fd=0, then continue reading from stdin (after CR)
|
* If fd=0, then continue reading from stdin (after CR)
|
||||||
* If fd>0, reading from file opened as argv[1]
|
* If fd>0, reading from file opened as argv[1]
|
||||||
|
|
@ -270,16 +315,19 @@ main(int argc,
|
||||||
x = x0;
|
x = x0;
|
||||||
|
|
||||||
/* Parse XPATH (use nsc == NULL to indicate dont use) */
|
/* Parse XPATH (use nsc == NULL to indicate dont use) */
|
||||||
if (xpath_vec_ctx(x, NULL, xpath, &xc) < 0)
|
if (xpath_vec_ctx(x, nsc, xpath, &xc) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
/* Print results */
|
/* Print results */
|
||||||
cb = cbuf_new();
|
cb = cbuf_new();
|
||||||
ctx_print2(cb, xc);
|
ctx_print2(cb, xc);
|
||||||
fprintf(stdout, "%s\n", cbuf_get(cb));
|
fprintf(stdout, "%s\n", cbuf_get(cb));
|
||||||
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (cb)
|
if (cb)
|
||||||
cbuf_free(cb);
|
cbuf_free(cb);
|
||||||
|
if (nsc)
|
||||||
|
xml_nsctx_free(nsc);
|
||||||
if (xc)
|
if (xc)
|
||||||
ctx_free(xc);
|
ctx_free(xc);
|
||||||
if (xv)
|
if (xv)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue