The reason is to be conservative with the API. However, less used functions, such as xpath_vec_bool(), xpath_vec_ctx() and xpath_vec_flag() are changed with a new `nsc`parameter, which should be set to NULL in most cases.
664 lines
18 KiB
C
664 lines
18 KiB
C
/*
|
|
*
|
|
***** BEGIN LICENSE BLOCK *****
|
|
|
|
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
|
|
|
This file is part of CLIXON.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
|
|
Alternatively, the contents of this file may be used under the terms of
|
|
the GNU General Public License Version 3 or later (the "GPL"),
|
|
in which case the provisions of the GPL are applicable instead
|
|
of those above. If you wish to allow use of your version of this file only
|
|
under the terms of the GPL, and not to allow others to
|
|
use your version of this file under the terms of Apache License version 2, indicate
|
|
your decision by deleting the provisions above and replace them with the
|
|
notice and other provisions required by the GPL. If you do not delete
|
|
the provisions above, a recipient may use your version of this file under
|
|
the terms of any one of the Apache License version 2 or the GPL.
|
|
|
|
***** END LICENSE BLOCK *****
|
|
|
|
* Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10
|
|
*
|
|
* Some notes on namespace extensions in Netconf/Yang
|
|
* RFC6241 8.9.1
|
|
* The set of namespace declarations are those in scope on the <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?
|
|
*
|
|
* @see README.md#xml-and-xpath for description of xpath implementation
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "clixon_config.h" /* generated by config & autoconf */
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <fnmatch.h>
|
|
#include <stdint.h>
|
|
#include <assert.h>
|
|
#include <syslog.h>
|
|
#include <fcntl.h>
|
|
#include <math.h>
|
|
|
|
/* cligen */
|
|
#include <cligen/cligen.h>
|
|
|
|
/* clicon */
|
|
#include "clixon_err.h"
|
|
#include "clixon_log.h"
|
|
#include "clixon_string.h"
|
|
#include "clixon_queue.h"
|
|
#include "clixon_hash.h"
|
|
#include "clixon_handle.h"
|
|
#include "clixon_yang.h"
|
|
#include "clixon_xml.h"
|
|
#include "clixon_xml_nsctx.h"
|
|
#include "clixon_xpath_ctx.h"
|
|
#include "clixon_xpath.h"
|
|
#include "clixon_xpath_parse.h"
|
|
#include "clixon_xpath_eval.h"
|
|
|
|
/*
|
|
* Variables
|
|
*/
|
|
|
|
/* Mapping between xpath_tree node name string <--> int */
|
|
static const map_str2int xpath_tree_map[] = {
|
|
{"expr", XP_EXP},
|
|
{"andexpr", XP_AND},
|
|
{"relexpr", XP_RELEX},
|
|
{"addexpr", XP_ADD},
|
|
{"unionexpr", XP_UNION},
|
|
{"pathexpr", XP_PATHEXPR},
|
|
{"locationpath", XP_LOCPATH},
|
|
{"abslocpath", XP_ABSPATH},
|
|
{"rellocpath", XP_RELLOCPATH},
|
|
{"step", XP_STEP},
|
|
{"nodetest", XP_NODE},
|
|
{"nodetest fn", XP_NODE_FN},
|
|
{"predicates", XP_PRED},
|
|
{"primaryexpr", XP_PRI0},
|
|
{"primaryexpr nr", XP_PRIME_NR},
|
|
{"primaryexpr str", XP_PRIME_STR},
|
|
{"primaryexpr fn", XP_PRIME_FN},
|
|
{NULL, -1}
|
|
};
|
|
|
|
|
|
/*
|
|
* XPATH parse tree type
|
|
*/
|
|
|
|
/*! Map from xpath_tree node name int to string
|
|
*/
|
|
char*
|
|
xpath_tree_int2str(int nodetype)
|
|
{
|
|
return (char*)clicon_int2str(xpath_tree_map, nodetype);
|
|
}
|
|
|
|
/*! Print XPATH parse tree */
|
|
static int
|
|
xpath_tree_print0(cbuf *cb,
|
|
xpath_tree *xs,
|
|
int level)
|
|
{
|
|
cprintf(cb, "%*s%s:", level*3, "", xpath_tree_int2str(xs->xs_type));
|
|
if (xs->xs_s0){
|
|
cprintf(cb, "\"%s\" ", xs->xs_s0);
|
|
if (xs->xs_s1)
|
|
cprintf(cb,"\"%s\" ", xs->xs_s1);
|
|
}
|
|
cprintf(cb, "\n");
|
|
if (xs->xs_c0)
|
|
xpath_tree_print0(cb, xs->xs_c0,level+1);
|
|
if (xs->xs_c1)
|
|
xpath_tree_print0(cb, xs->xs_c1, level+1);
|
|
return 0;
|
|
}
|
|
|
|
/*! Print a xpath_tree
|
|
* @param[out] cb CLIgen buffer
|
|
* @param[in] xs XPATH tree
|
|
*/
|
|
int
|
|
xpath_tree_print(cbuf *cb,
|
|
xpath_tree *xs)
|
|
{
|
|
xpath_tree_print0(cb, xs, 0);
|
|
return 0;
|
|
}
|
|
|
|
/*! Free a xpath_tree
|
|
* @param[in] xs XPATH tree
|
|
* @see xpath_parse creates a xpath_tree
|
|
*/
|
|
int
|
|
xpath_tree_free(xpath_tree *xs)
|
|
{
|
|
if (xs->xs_s0)
|
|
free(xs->xs_s0);
|
|
if (xs->xs_s1)
|
|
free(xs->xs_s1);
|
|
if (xs->xs_c0)
|
|
xpath_tree_free(xs->xs_c0);
|
|
if (xs->xs_c1)
|
|
xpath_tree_free(xs->xs_c1);
|
|
free(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
|
|
* @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)
|
|
* err;
|
|
* if (xpt)
|
|
* xptree_free(xpt);
|
|
* @endcode
|
|
* @see xpath_tree_free
|
|
*/
|
|
int
|
|
xpath_parse(cvec *nsc,
|
|
char *xpath,
|
|
xpath_tree **xptree)
|
|
{
|
|
int retval = -1;
|
|
struct clicon_xpath_yacc_arg xy = {0,};
|
|
|
|
xy.xy_parse_string = xpath;
|
|
xy.xy_name = "xpath parser";
|
|
xy.xy_linenum = 1;
|
|
if (xpath_scan_init(&xy) < 0)
|
|
goto done;
|
|
if (xpath_parse_init(&xy) < 0)
|
|
goto done;
|
|
clicon_debug(2,"%s",__FUNCTION__);
|
|
if (clixon_xpath_parseparse(&xy) != 0) { /* yacc returns 1 on error */
|
|
clicon_log(LOG_NOTICE, "XPATH error: on line %d", xy.xy_linenum);
|
|
if (clicon_errno == 0)
|
|
clicon_err(OE_XML, 0, "XPATH parser error with no error code (should not happen)");
|
|
goto done;
|
|
}
|
|
if (debug > 1){
|
|
cbuf *cb = cbuf_new();
|
|
xpath_tree_print(cb, xy.xy_top);
|
|
clicon_debug(2, "xpath parse tree:\n%s", cbuf_get(cb));
|
|
cbuf_free(cb);
|
|
}
|
|
/* done: */
|
|
xpath_parse_exit(&xy);
|
|
xpath_scan_exit(&xy);
|
|
*xptree = xy.xy_top;
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Given XML tree and xpath, parse xpath, eval it and return xpath context,
|
|
* This is a raw form of xpath where you can do type conversion of the return
|
|
* value, etc, not just a nodeset.
|
|
* @param[in] xcur XML-tree where to search
|
|
* @param[in] nsc External XML namespace context, or NULL
|
|
* @param[in] xpath String with XPATH 1.0 syntax
|
|
* @param[out] xrp Return XPATH context
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @code
|
|
* xp_ctx *xc = NULL;
|
|
* if (xpath_vec_ctx(x, NULL, xpath, &xc) < 0)
|
|
* err;
|
|
* if (xc)
|
|
* ctx_free(xc);
|
|
* @endcode
|
|
*/
|
|
int
|
|
xpath_vec_ctx(cxobj *xcur,
|
|
cvec *nsc,
|
|
char *xpath,
|
|
xp_ctx **xrp)
|
|
{
|
|
int retval = -1;
|
|
xpath_tree *xptree = NULL;
|
|
xp_ctx xc = {0,};
|
|
|
|
if (xpath_parse(nsc, xpath, &xptree) < 0)
|
|
goto done;
|
|
xc.xc_type = XT_NODESET;
|
|
xc.xc_node = xcur;
|
|
xc.xc_initial = xcur;
|
|
if (cxvec_append(xcur, &xc.xc_nodeset, &xc.xc_size) < 0)
|
|
goto done;
|
|
if (xp_eval(&xc, xptree, nsc, xrp) < 0)
|
|
goto done;
|
|
if (xc.xc_nodeset){
|
|
free(xc.xc_nodeset);
|
|
xc.xc_nodeset = NULL;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (xptree)
|
|
xpath_tree_free(xptree);
|
|
return retval;
|
|
}
|
|
|
|
/*! Xpath nodeset function where only the first matching entry is returned
|
|
*
|
|
* @param[in] xcur XML tree where to search
|
|
* @param[in] nsc External XML namespace context, or NULL
|
|
* @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_nsc(xtop, nsc, "//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
|
|
* @see also xpath_vec.
|
|
*/
|
|
cxobj *
|
|
xpath_first_nsc(cxobj *xcur,
|
|
cvec *nsc,
|
|
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, nsc, xpath, &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 only the first matching entry is returned
|
|
*
|
|
* @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;
|
|
* if ((x = xpath_first(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
|
|
* @see also xpath_vec.
|
|
* @see xpath_first_nsc which is more generic with namespace context
|
|
*/
|
|
cxobj *
|
|
xpath_first(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, &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;
|
|
}
|
|
|
|
/*! 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] nsc External XML namespace context, or NULL
|
|
* @param[in] xpformat Format string for XPATH syntax
|
|
* @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_nsc(xcur, nsc, "//symbol/foo", &vec, &veclen) < 0)
|
|
* goto err;
|
|
* for (i=0; i<veclen; i++){
|
|
* xn = vec[i];
|
|
* ...
|
|
* }
|
|
* free(vec);
|
|
* @endcode
|
|
*/
|
|
int
|
|
xpath_vec_nsc(cxobj *xcur,
|
|
cvec *nsc,
|
|
char *xpformat,
|
|
cxobj ***vec,
|
|
size_t *veclen,
|
|
...)
|
|
{
|
|
int retval = -1;
|
|
va_list ap;
|
|
size_t len;
|
|
char *xpath = NULL;
|
|
xp_ctx *xr = NULL;
|
|
|
|
va_start(ap, veclen);
|
|
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, veclen);
|
|
if (vsnprintf(xpath, len+1, xpformat, ap) < 0){
|
|
clicon_err(OE_UNIX, errno, "vsnprintf");
|
|
va_end(ap);
|
|
goto done;
|
|
}
|
|
va_end(ap);
|
|
*vec=NULL;
|
|
*veclen = 0;
|
|
if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0)
|
|
goto done;
|
|
if (xr && xr->xc_type == XT_NODESET){
|
|
*vec = xr->xc_nodeset;
|
|
xr->xc_nodeset = NULL;
|
|
*veclen = xr->xc_size;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (xr)
|
|
ctx_free(xr);
|
|
if (xpath)
|
|
free(xpath);
|
|
return retval;
|
|
}
|
|
|
|
/*! 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] xpformat Format string for XPATH syntax
|
|
* @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
|
|
* cxobj **vec;
|
|
* size_t veclen;
|
|
* if (xpath_vec(xcur, "//symbol/foo", &vec, &veclen) < 0)
|
|
* goto err;
|
|
* for (i=0; i<veclen; i++){
|
|
* xn = vec[i];
|
|
* ...
|
|
* }
|
|
* free(vec);
|
|
* @endcode
|
|
* @see xpath_vec_nsc which is more generic with namespace context
|
|
*/
|
|
int
|
|
xpath_vec(cxobj *xcur,
|
|
char *xpformat,
|
|
cxobj ***vec,
|
|
size_t *veclen,
|
|
...)
|
|
{
|
|
int retval = -1;
|
|
va_list ap;
|
|
size_t len;
|
|
char *xpath = NULL;
|
|
xp_ctx *xr = NULL;
|
|
|
|
va_start(ap, veclen);
|
|
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, veclen);
|
|
if (vsnprintf(xpath, len+1, xpformat, ap) < 0){
|
|
clicon_err(OE_UNIX, errno, "vsnprintf");
|
|
va_end(ap);
|
|
goto done;
|
|
}
|
|
va_end(ap);
|
|
*vec=NULL;
|
|
*veclen = 0;
|
|
if (xpath_vec_ctx(xcur, NULL, xpath, &xr) < 0)
|
|
goto done;
|
|
if (xr && xr->xc_type == XT_NODESET){
|
|
*vec = xr->xc_nodeset;
|
|
xr->xc_nodeset = NULL;
|
|
*veclen = xr->xc_size;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (xr)
|
|
ctx_free(xr);
|
|
if (xpath)
|
|
free(xpath);
|
|
return retval;
|
|
}
|
|
|
|
/* Xpath that returns a vector of matches (only nodes marked with flags)
|
|
* @param[in] xcur xml-tree where to search
|
|
* @param[in] xpformat Format string for 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
|
|
* @retval 0 OK
|
|
* @retval -1 error.
|
|
* @code
|
|
* cxobj **vec;
|
|
* size_t veclen;
|
|
* 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];
|
|
* ...
|
|
* }
|
|
* free(vec);
|
|
* @endcode
|
|
* @Note that although the returned vector must be freed after use, the returned xml trees need not be.
|
|
* @see also xpath_vec This is a specialized version.
|
|
*/
|
|
int
|
|
xpath_vec_flag(cxobj *xcur,
|
|
cvec *nsc,
|
|
char *xpformat,
|
|
uint16_t flags,
|
|
cxobj ***vec,
|
|
size_t *veclen,
|
|
...)
|
|
{
|
|
int retval = -1;
|
|
va_list ap;
|
|
size_t len;
|
|
char *xpath = NULL;
|
|
xp_ctx *xr = NULL;
|
|
int i;
|
|
cxobj *x;
|
|
|
|
va_start(ap, veclen);
|
|
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, veclen);
|
|
if (vsnprintf(xpath, len+1, xpformat, ap) < 0){
|
|
clicon_err(OE_UNIX, errno, "vsnprintf");
|
|
va_end(ap);
|
|
goto done;
|
|
}
|
|
va_end(ap);
|
|
*vec=NULL;
|
|
*veclen = 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++){
|
|
x = xr->xc_nodeset[i];
|
|
if (flags==0x0 || xml_flag(x, flags))
|
|
if (cxvec_append(x, vec, veclen) < 0)
|
|
goto done;
|
|
}
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (xr)
|
|
ctx_free(xr);
|
|
if (xpath)
|
|
free(xpath);
|
|
return retval;
|
|
}
|
|
|
|
/*! 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] xpformat Format string for XPATH syntax
|
|
* @retval 1 True
|
|
* @retval 0 False
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
xpath_vec_bool(cxobj *xcur,
|
|
cvec *nsc,
|
|
char *xpformat,
|
|
...)
|
|
{
|
|
int retval = -1;
|
|
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, nsc, xpath, &xr) < 0)
|
|
goto done;
|
|
if (xr)
|
|
retval = ctx2boolean(xr);
|
|
done:
|
|
if (xr)
|
|
ctx_free(xr);
|
|
if (xpath)
|
|
free(xpath);
|
|
return retval;
|
|
}
|
|
|