* Full support of XPATH 1.0 according to https://www.w3.org/TR/xpath-10 using yacc/lex

* The previous XPATH imlementation was very restricted.
  * The only function implemented is the Yang extension "current()". No other functions are implemented (eg last(), count()).
This commit is contained in:
Olof hagsand 2018-07-16 16:13:31 +02:00
parent 60ce7b12bd
commit bf728b37b8
26 changed files with 2856 additions and 481 deletions

View file

@ -2,6 +2,9 @@
## 3.7.0 (Upcoming) ## 3.7.0 (Upcoming)
### Major changes: ### Major changes:
* Full support of XPATH 1.0 according to https://www.w3.org/TR/xpath-10 using yacc/lex
* The previous XPATH imlementation was very restricted.
* The only function implemented is the Yang extension "current()". No other functions are implemented (eg last(), count()).
* Support for YANG identity and identityref according to RFC 7950 Sec 7.18 and 9.10 * Support for YANG identity and identityref according to RFC 7950 Sec 7.18 and 9.10
* Previous support did no validation of values. * Previous support did no validation of values.
* Validation of types and CLI expansion * Validation of types and CLI expansion
@ -9,7 +12,7 @@
* Applications which have not strictly enforced the identities may now have problems with validation and may need to be modified. * Applications which have not strictly enforced the identities may now have problems with validation and may need to be modified.
### Minor changes: ### Minor changes:
* Dedicated xml,json,yang and xsl parser utility programs added * Dedicated xml,json,yang and xpath parser utility programs added
* CDATA xml support (patch by David Cornejo, Netgate) * CDATA xml support (patch by David Cornejo, Netgate)
* Encode and decode (parsing) support * Encode and decode (parsing) support
* Validation of yang bits type space-separated list value * Validation of yang bits type space-separated list value
@ -47,7 +50,7 @@
* See FAQ and example * See FAQ and example
### Corrected Bugs ### Corrected Bugs
* Prefix of rpc was ignored * Prefix of rpc was ignored (thanks Dmitri at netgate)
* https://github.com/clicon/clixon/issues/30 * https://github.com/clicon/clixon/issues/30
* Added cli returna value also for single commands (eg -1) * Added cli returna value also for single commands (eg -1)
* Fixed JSON unbalanced braces resulting in assert. * Fixed JSON unbalanced braces resulting in assert.

View file

@ -81,6 +81,8 @@
#include <clixon/clixon_options.h> #include <clixon/clixon_options.h>
#include <clixon/clixon_xml_map.h> #include <clixon/clixon_xml_map.h>
#include <clixon/clixon_xml_db.h> #include <clixon/clixon_xml_db.h>
#include <clixon/clixon_xpath_ctx.h>
#include <clixon/clixon_xpath.h>
#include <clixon/clixon_xsl.h> #include <clixon/clixon_xsl.h>
#include <clixon/clixon_json.h> #include <clixon/clixon_json.h>
#include <clixon/clixon_netconf_lib.h> #include <clixon/clixon_netconf_lib.h>

View file

@ -46,6 +46,8 @@ static const map_str2int atmap[] = {
{NULL, -1} {NULL, -1}
}; };
* @endcode * @endcode
* @see clicon_int2str
* @see clicon_str2int
*/ */
struct map_str2int{ struct map_str2int{
char *ms_str; char *ms_str;

95
lib/clixon/clixon_xpath.h Normal file
View file

@ -0,0 +1,95 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 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
*/
#ifndef _CLIXON_XPATH_H
#define _CLIXON_XPATH_H
/*
* Types
*/
enum xp_op{
XO_AND,
XO_OR,
XO_DIV,
XO_MOD,
XO_ADD,
XO_MULT,
XO_SUB,
XO_EQ,
XO_NE,
XO_GE,
XO_LE,
XO_LT,
XO_GT,
XO_UNION,
};
/* Axis specifiers according to https://www.w3.org/TR/xpath-10/#NT-AxisName */
enum axis_type{
A_NAN = 0, /* Not set */
A_ANCESTOR,
A_ANCESTOR_OR_SELF,
A_ATTRIBUTE,
A_CHILD,
A_DESCENDANT,
A_DESCENDANT_OR_SELF,
A_FOLLOWING,
A_FOLLOWING_SIBLING,
A_NAMESPACE,
A_PARENT,
A_PRECEEDING,
A_PRECEEDING_SIBLING,
A_SELF,
A_ROOT /* XXX Not in https://www.w3.org/TR/xpath-10 */
};
/*
* Variables
*/
extern const map_str2int xpopmap[];
/*
* Prototypes
*/
#if defined(__GNUC__) && __GNUC__ >= 3
int xpath_vec_nodeset(cxobj *xcur, char *format, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 5)));
int xpath_vec_bool(cxobj *xcur, char *format, ...) __attribute__ ((format (printf, 2, 3)));
#else
int xpath_vec_nodeset(cxobj *xcur, char *format, cxobj ***vec, size_t *veclen, ...);
int xpath_vec_bool(cxobj *xcur, char *format, ...);
#endif
int xpath_vec_ctx(cxobj *xcur, char *xpath, xp_ctx **xrp);
#endif /* _CLIXON_XPATH_H */

View file

@ -0,0 +1,103 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 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
* This file defines XPATH contexts using in traversing the XPATH parse tree.
*/
#ifndef _CLIXON_XPATH_CTX_H
#define _CLIXON_XPATH_CTX_H
/*
* Types
*/
/*! XPATH expression type
* An expression is evaluated to yield an object, which has one of the following four basic types:
* node-set (an unordered collection of nodes without duplicates)
* boolean (true or false)
* number (a floating-point number)
* string (a sequence of UCS characters)
*/
enum xp_objtype{
XT_NODESET,
XT_BOOL,
XT_NUMBER,
XT_STRING
};
/* Expression evaluation occurs with respect to a context. XSLT and XPointer specify how the context is
* determined for XPath expressions used in XSLT and XPointer respectively. The context consists of:
* a node (the context node)
* a pair of non-zero positive integers (the context position and the context size)
* a set of variable bindings
* a function library
* the set of namespace declarations in scope for the expression
* For each node in the node-set to be filtered, the PredicateExpr is
* evaluated with that node as the context node, with the number of nodes
* in the node-set as the context size, and with the proximity position
* of the node in the node-set with respect to the axis as the context
* position; if PredicateExpr evaluates to true for that node, the node
* is included in the new node-set; otherwise, it is not included.
*/
struct xp_ctx{
enum xp_objtype xc_type;
cxobj **xc_nodeset; /* if type XT_NODESET */
size_t xc_size; /* Length of nodeset */
int xc_bool; /* if xc_type XT_BOOL */
double xc_number; /* if xc_type XT_NUMBER */
char *xc_string; /* if xc_type XT_STRING */
cxobj *xc_node; /* Node in nodeset XXX maybe not needed*/
cxobj *xc_initial; /* RFC 7960 10.1.1 extension: for current() */
int xc_descendant; /* // */
/* NYI: a set of variable bindings, set of namespace declarations */
};
typedef struct xp_ctx xp_ctx;
/*
* Variables
*/
extern const map_str2int ctxmap[];
/*
* Prototypes
*/
int ctx_free(xp_ctx *xc);
xp_ctx *ctx_dup(xp_ctx *xc);
int ctx_nodeset_replace(xp_ctx *xc, cxobj **vec, size_t veclen);
int ctx_print(cbuf *cb, int id, xp_ctx *xc, char *str);
int ctx2boolean(xp_ctx *xc);
int ctx2string(xp_ctx *xc, char **str0);
int ctx2number(xp_ctx *xc, double *n0);
#endif /* _CLIXON_XPATH_CTX_H */

View file

@ -72,18 +72,19 @@ SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \
clixon_json.c clixon_yang.c clixon_yang_type.c \ clixon_json.c clixon_yang.c clixon_yang_type.c \
clixon_hash.c clixon_options.c clixon_plugin.c \ clixon_hash.c clixon_options.c clixon_plugin.c \
clixon_proto.c clixon_proto_client.c \ clixon_proto.c clixon_proto_client.c \
clixon_xsl.c clixon_sha1.c clixon_xml_db.c clixon_netconf_lib.c clixon_xsl.c clixon_xpath.c clixon_xpath_ctx.c clixon_sha1.c \
clixon_xml_db.c clixon_netconf_lib.c
YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \ YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
lex.clixon_yang_parse.o clixon_yang_parse.tab.o \ lex.clixon_yang_parse.o clixon_yang_parse.tab.o \
lex.clixon_json_parse.o clixon_json_parse.tab.o lex.clixon_json_parse.o clixon_json_parse.tab.o \
lex.clixon_xpath_parse.o clixon_xpath_parse.tab.o
# Extra applications. Utilities, unit testings. Not installed. # Extra applications. Utilities, unit testings. Not installed.
APPSRC = clixon_util_xml.c APPSRC = clixon_util_xml.c
APPSRC += clixon_util_json.c APPSRC += clixon_util_json.c
APPSRC += clixon_util_yang.c APPSRC += clixon_util_yang.c
APPSRC += clixon_util_xsl.c APPSRC += clixon_util_xpath.c
APPS = $(APPSRC:.c=) APPS = $(APPSRC:.c=)
@ -107,9 +108,11 @@ clean:
rm -f clixon_xml_parse.tab.[ch] clixon_xml_parse.yy.[co] rm -f clixon_xml_parse.tab.[ch] clixon_xml_parse.yy.[co]
rm -f clixon_yang_parse.tab.[ch] clixon_yang_parse.[co] rm -f clixon_yang_parse.tab.[ch] clixon_yang_parse.[co]
rm -f clixon_json_parse.tab.[ch] clixon_json_parse.[co] rm -f clixon_json_parse.tab.[ch] clixon_json_parse.[co]
rm -f clixon_xpath_parse.tab.[ch] clixon_xpath_parse.[co]
rm -f lex.clixon_xml_parse.c rm -f lex.clixon_xml_parse.c
rm -f lex.clixon_yang_parse.c rm -f lex.clixon_yang_parse.c
rm -f lex.clixon_json_parse.c rm -f lex.clixon_json_parse.c
rm -f lex.clixon_xpath_parse.c
############################################################################# #############################################################################
# Implicit rules for lex and yacc. # Implicit rules for lex and yacc.
@ -159,6 +162,18 @@ clixon_json_parse.tab.c clixon_json_parse.tab.h: clixon_json_parse.y
lex.clixon_json_parse.o : lex.clixon_json_parse.c clixon_json_parse.tab.h lex.clixon_json_parse.o : lex.clixon_json_parse.c clixon_json_parse.tab.h
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $< $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
# xpath parser
lex.clixon_xpath_parse.c : clixon_xpath_parse.l clixon_xpath_parse.tab.h
$(LEX) -Pclixon_xpath_parse clixon_xpath_parse.l # -d is debug
clixon_xpath_parse.tab.c clixon_xpath_parse.tab.h: clixon_xpath_parse.y
$(YACC) -l -d -p clixon_xpath_parse clixon_xpath_parse.y # -t is debug
mv y.tab.c clixon_xpath_parse.tab.c
mv y.tab.h clixon_xpath_parse.tab.h
lex.clixon_xpath_parse.o : lex.clixon_xpath_parse.c clixon_xpath_parse.tab.h
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
# APPS # APPS
clixon_util_xml: clixon_util_xml.c $(MYLIB) clixon_util_xml: clixon_util_xml.c $(MYLIB)
$(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $^ $(LIBS) -o $@ $(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $^ $(LIBS) -o $@
@ -169,7 +184,7 @@ clixon_util_json: clixon_util_json.c $(MYLIB)
clixon_util_yang: clixon_util_yang.c $(MYLIB) clixon_util_yang: clixon_util_yang.c $(MYLIB)
$(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $^ $(LIBS) -o $@ $(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $^ $(LIBS) -o $@
clixon_util_xsl: clixon_util_xsl.c $(MYLIB) clixon_util_xpath: clixon_util_xpath.c $(MYLIB)
$(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $^ $(LIBS) -o $@ $(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $^ $(LIBS) -o $@
distclean: clean distclean: clean

231
lib/src/clixon_util_xpath.c Normal file
View file

@ -0,0 +1,231 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 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 *****
See https://www.w3.org/TR/xpath/
* Turn this on to get an xpath test program
* Usage: xpath [<xpath>]
* read xpath on first line and xml on rest of lines from input
* Example compile:
gcc -g -o xpath -I. -I../clixon ./clixon_xsl.c -lclixon -lcligen
* Example run:
echo "a\n<a><b/></a>" | xpath
*/
#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>
/* cligen */
#include <cligen/cligen.h>
/* clixon */
#include <clixon/clixon.h>
static int
usage(char *argv0)
{
fprintf(stderr, "usage:%s [options]\n"
"where options are\n"
"\t-h \t\tHelp\n"
"\t-D <level> \tDebug\n"
"\t-f <file> \tXML file\n"
"\t-p <xpath> \tPrimary XPATH string\n"
"\t-i <xpath0>\t(optional) Initial XPATH string\n"
"and the following extra rules:\n"
"\tif -f is not given, XML input is expected on stdin\n"
"\tif -p is not given, <xpath> is expected as the first line on stdin\n"
"This means that with no arguments, <xpath> and XML is expected on stadin.\n",
argv0
);
exit(0);
}
static int
ctx_print2(cbuf *cb,
xp_ctx *xc)
{
int i;
cprintf(cb, "%s:", (char*)clicon_int2str(ctxmap, xc->xc_type));
switch (xc->xc_type){
case XT_NODESET:
for (i=0; i<xc->xc_size; i++){
cprintf(cb, "%d:", i);
clicon_xml2cbuf(cb, xc->xc_nodeset[i], 0, 0);
}
break;
case XT_BOOL:
cprintf(cb, "%s", xc->xc_bool?"true":"false");
break;
case XT_NUMBER:
cprintf(cb, "%lf", xc->xc_number);
break;
case XT_STRING:
cprintf(cb, "%s", xc->xc_string);
break;
}
return 0;
}
int
main(int argc, char **argv)
{
int retval = -1;
char *argv0 = argv[0];
int i;
cxobj **xv = NULL;
cxobj *x0 = NULL;
cxobj *x;
char c;
int len;
char *buf = NULL;
int ret;
int fd = 0; /* unless overriden by argv[1] */
char *xpath = NULL;
char *xpath0 = NULL;
char *filename;
xp_ctx *xc = NULL;
cbuf *cb;
clicon_log_init("xpath", LOG_DEBUG, CLICON_LOG_STDERR);
optind = 1;
opterr = 0;
while ((c = getopt(argc, argv, "hDf:p:i:")) != -1)
switch (c) {
case 'h':
usage(argv0);
break;
case 'D':
debug++;
break;
case 'f': /* XML file */
filename = optarg;
if ((fd = open(filename, O_RDONLY)) < 0){
clicon_err(OE_UNIX, errno, "open(%s)", argv[1]);
goto done;
}
break;
case 'p': /* Primary XPATH string */
xpath = optarg;
break;
case 'i': /* Optional initial XPATH string */
xpath0 = optarg;
break;
default:
usage(argv[0]);
break;
}
if (xpath==NULL){
/* First read xpath */
len = 1024; /* any number is fine */
if ((buf = malloc(len)) == NULL){
perror("pt_file malloc");
return -1;
}
memset(buf, 0, len);
i = 0;
while (1){
if ((ret = read(0, &c, 1)) < 0){
perror("read");
goto done;
}
if (ret == 0)
break;
if (c == '\n')
break;
if (len==i){
if ((buf = realloc(buf, 2*len)) == NULL){
fprintf(stderr, "%s: realloc: %s\n", __FUNCTION__, strerror(errno));
return -1;
}
memset(buf+len, 0, len);
len *= 2;
}
buf[i++] = (char)(c&0xff);
}
xpath = buf;
}
/*
* If fd=0, then continue reading from stdin (after CR)
* If fd>0, reading from file opened as argv[1]
*/
if (xml_parse_file(fd, "</clicon>", NULL, &x0) < 0){
fprintf(stderr, "Error: parsing: %s\n", clicon_err_reason);
return -1;
}
/* If xpath0 given, position current x */
if (xpath0){
if ((x = xpath_first(x0, "%s", xpath0)) == NULL){
fprintf(stderr, "Error: xpath0 returned NULL\n");
return -1;
}
}
else
x = x0;
/* Parse XML */
if (xpath_vec_ctx(x, xpath, &xc) < 0)
return -1;
/* Print results */
cb = cbuf_new();
ctx_print2(cb, xc);
fprintf(stdout, "%s\n", cbuf_get(cb));
retval = 0;
done:
if (cb)
cbuf_free(cb);
if (xc)
ctx_free(xc);
if (xv)
free(xv);
if (buf)
free(buf);
if (x0)
xml_free(x0);
if (fd > 0)
close(fd);
return retval;
}

View file

@ -1,141 +0,0 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 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 *****
See https://www.w3.org/TR/xpath/
* Turn this on to get an xpath test program
* Usage: xpath [<xpath>]
* read xpath on first line and xml on rest of lines from input
* Example compile:
gcc -g -o xpath -I. -I../clixon ./clixon_xsl.c -lclixon -lcligen
* Example run:
echo "a\n<a><b/></a>" | xpath
*/
#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>
/* cligen */
#include <cligen/cligen.h>
/* clixon */
#include <clixon/clixon.h>
static int
usage(char *argv0)
{
fprintf(stderr, "usage:%s <xpath>.\n\tInput on stdin\n", argv0);
exit(0);
}
int
main(int argc, char **argv)
{
int retval = -1;
int i;
cxobj **xv;
cxobj *x;
cxobj *xn;
size_t xlen = 0;
int c;
int len;
char *buf = NULL;
int ret;
if (argc != 1){
usage(argv[0]);
return -1;
}
/* First read xpath */
len = 1024; /* any number is fine */
if ((buf = malloc(len)) == NULL){
perror("pt_file malloc");
return -1;
}
memset(buf, 0, len);
i = 0;
while (1){
if ((ret = read(0, &c, 1)) < 0){
perror("read");
goto done;
}
if (ret == 0)
break;
if (c == '\n')
break;
if (len==i){
if ((buf = realloc(buf, 2*len)) == NULL){
fprintf(stderr, "%s: realloc: %s\n", __FUNCTION__, strerror(errno));
return -1;
}
memset(buf+len, 0, len);
len *= 2;
}
buf[i++] = (char)(c&0xff);
}
x = NULL;
if (xml_parse_file(0, "</clicon>", NULL, &x) < 0){
fprintf(stderr, "Error: parsing: %s\n", clicon_err_reason);
return -1;
}
if (xpath_vec(x, "%s", &xv, &xlen, buf) < 0)
return -1;
if (xv){
for (i=0; i<xlen; i++){
xn = xv[i];
fprintf(stdout, "%d:", i);
clicon_xml2file(stdout, xn, 0, 0);
fprintf(stdout, "\n");
}
free(xv);
}
retval = 0;
done:
if (x)
xml_free(x);
return retval;
}

View file

@ -1487,8 +1487,6 @@ int
xml_copy_one(cxobj *x0, xml_copy_one(cxobj *x0,
cxobj *x1) cxobj *x1)
{ {
cg_var *cv1;
xml_type_set(x1, xml_type(x0)); xml_type_set(x1, xml_type(x0));
if (xml_value(x0)){ /* malloced string */ if (xml_value(x0)){ /* malloced string */
if ((x1->x_value = strdup(x0->x_value)) == NULL){ if ((x1->x_value = strdup(x0->x_value)) == NULL){

View file

@ -424,13 +424,15 @@ xml_yang_validate_all(cxobj *xt,
void *arg) void *arg)
{ {
int retval = -1; int retval = -1;
yang_stmt *ys; yang_stmt *ys; /* yang node */
yang_stmt *ytype; yang_stmt *yc; /* yang child */
char *xpath;
/* if not given by argument (overide) use default link /* if not given by argument (overide) use default link
and !Node has a config sub-statement and it is false */ and !Node has a config sub-statement and it is false */
if ((ys = xml_spec(xt)) != NULL && if ((ys = xml_spec(xt)) != NULL &&
yang_config(ys) != 0){ yang_config(ys) != 0){
/* Node-specific validation */
switch (ys->ys_keyword){ switch (ys->ys_keyword){
case Y_LEAF: case Y_LEAF:
/* fall thru */ /* fall thru */
@ -438,20 +440,29 @@ xml_yang_validate_all(cxobj *xt,
/* Special case if leaf is leafref, then first check against /* Special case if leaf is leafref, then first check against
current xml tree current xml tree
*/ */
if ((ytype = yang_find((yang_node*)ys, Y_TYPE, NULL)) != NULL){ if ((yc = yang_find((yang_node*)ys, Y_TYPE, NULL)) != NULL){
if (strcmp(ytype->ys_argument, "leafref") == 0){ if (strcmp(yc->ys_argument, "leafref") == 0){
if (validate_leafref(xt, ytype) < 0) if (validate_leafref(xt, yc) < 0)
goto done; goto done;
} }
else if (strcmp(ytype->ys_argument, "identityref") == 0){ else if (strcmp(yc->ys_argument, "identityref") == 0){
if (validate_identityref(xt, ys, ytype) < 0) if (validate_identityref(xt, ys, yc) < 0)
goto done; goto done;
} }
} }
break; break;
case Y_MUST: /* RFC 7950 Sec 7.5.3 */
break;
default: default:
break; break;
} }
/* "when" sub-node RFC 7950 Sec 7.21.5 */
if ((yc = yang_find((yang_node*)ys, Y_WHEN, NULL)) != NULL){
xpath = yc->ys_argument; /* "when" has xpath argument */
if (xpath_first(xt, "%s", xpath))
;
fprintf(stderr, "%s %s\n", __FUNCTION__, xpath);
}
} }
retval = 0; retval = 0;
done: done:
@ -1372,7 +1383,6 @@ xml_spec_populate(cxobj *x,
return retval; return retval;
} }
/*! Translate from restconf api-path in cvv form to xml xpath /*! Translate from restconf api-path in cvv form to xml xpath
* eg a/b=c -> a/[b=c] * eg a/b=c -> a/[b=c]
* @param[in] yspec Yang spec * @param[in] yspec Yang spec

1183
lib/src/clixon_xpath.c Normal file

File diff suppressed because it is too large Load diff

294
lib/src/clixon_xpath_ctx.c Normal file
View file

@ -0,0 +1,294 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 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
* This file defines XPATH contexts using in traversing the XPATH parse tree.
*/
#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_xpath_parse.h"
#include "clixon_xpath_ctx.h"
/*
* Variables
*/
const map_str2int ctxmap[] = {
{"nodeset", XT_NODESET},
{"bool", XT_BOOL},
{"number", XT_NUMBER},
{"string", XT_STRING},
{NULL, -1}
};
/*! Free xpath context */
int
ctx_free(xp_ctx *xc)
{
if (xc->xc_nodeset)
free(xc->xc_nodeset);
if (xc->xc_string)
free(xc->xc_string);
free(xc);
return 0;
}
/*! Duplicate xpath context */
xp_ctx *
ctx_dup(xp_ctx *xc0)
{
static xp_ctx *xc = NULL;
if ((xc = malloc(sizeof(*xc))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(xc, 0, sizeof(*xc));
*xc = *xc0;
if (xc0->xc_size){
if ((xc->xc_nodeset = calloc(xc0->xc_size, sizeof(cxobj*))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
memcpy(xc->xc_nodeset, xc0->xc_nodeset, xc->xc_size*sizeof(cxobj*));
}
if (xc0->xc_string)
if ((xc->xc_string = strdup(xc0->xc_string)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
done:
return xc;
}
/*! Print XPATH context */
int
ctx_print(cbuf *cb,
int id,
xp_ctx *xc,
char *str)
{
static int ident = 0;
int i;
if (id<0)
ident += id;
cprintf(cb, "%*s%s ", ident, "", str?str:"");
if (id>0)
ident += id;
if (xc){
cprintf(cb, "%s: ", (char*)clicon_int2str(ctxmap, xc->xc_type));
switch (xc->xc_type){
case XT_NODESET:
for (i=0; i<xc->xc_size; i++)
cprintf(cb, "%s ", xml_name(xc->xc_nodeset[i]));
break;
case XT_BOOL:
cprintf(cb, "%s", xc->xc_bool?"true":"false");
break;
case XT_NUMBER:
cprintf(cb, "%lf", xc->xc_number);
break;
case XT_STRING:
cprintf(cb, "%s", xc->xc_string);
break;
}
}
return 0;
}
/*! Convert xpath context to boolean according to boolean() function in XPATH spec
* @param[in] xc XPATH context
* @retval 0 False
* @retval 1 True
* a number is true if and only if it is neither positive or negative zero nor NaN
* a node-set is true if and only if it is non-empty
* a string is true if and only if its length is non-zero
* an object of a type other than the four basic types is converted to a boolean
* in a way that is dependent on that type
*/
int
ctx2boolean(xp_ctx *xc)
{
int b;
switch (xc->xc_type){
case XT_NODESET:
b = (xc->xc_size != 0);
break;
case XT_BOOL:
b = xc->xc_bool;
break;
case XT_NUMBER:
b = (xc->xc_number != 0.0 && xc->xc_number != NAN);
break;
case XT_STRING:
b = (xc->xc_string && strlen(xc->xc_string));
break;
}
return b;
}
/*! Convert xpath context to string according to string() function in XPATH spec
* @param[in] xc XPATH context
* @param[out] str0 Malloced result string
* @retval 0 OK
* @retval -1 Error
* @note string malloced.
*/
int
ctx2string(xp_ctx *xc,
char **str0)
{
int retval = -1;
char *str = NULL;
int len;
char *b;
switch (xc->xc_type){
case XT_NODESET:
if (xc->xc_size && (b = xml_body(xc->xc_nodeset[0]))){
if ((str = strdup(b)) == NULL){
clicon_err(OE_XML, errno, "strdup");
goto done;
}
}
else
if ((str = strdup("")) == NULL){
clicon_err(OE_XML, errno, "strdup");
goto done;
}
break;
case XT_BOOL:
if ((str = strdup(xc->xc_bool == 0?"false":"true")) == NULL){
clicon_err(OE_XML, errno, "strdup");
goto done;
}
break;
case XT_NUMBER:
len = snprintf(NULL, 0, "%0lf", xc->xc_number);
len++;
if ((str = malloc(len)) == NULL){
clicon_err(OE_XML, errno, "malloc");
goto done;
}
snprintf(str, len, "%0lf", xc->xc_number);
break;
case XT_STRING:
if ((str = strdup(xc->xc_string)) == NULL){
clicon_err(OE_XML, errno, "strdup");
goto done;
}
break;
}
*str0 = str;
retval = 0;
done:
return retval;
}
/*! Convert xpath context to number according to number() function in XPATH spec
* @param[in] xc XPATH context
* @param[out] n0 Floating point or NAN
* @retval 0 OK
* @retval -1 Error
*/
int
ctx2number(xp_ctx *xc,
double *n0)
{
int retval = -1;
char *str = NULL;
double n;
switch (xc->xc_type){
case XT_NODESET:
if (ctx2string(xc, &str) < 0)
goto done;
if (sscanf(str, "%lf",&n) != 1)
n = NAN;
break;
case XT_BOOL:
n = (double)xc->xc_bool;
break;
case XT_NUMBER:
n = xc->xc_number;
break;
case XT_STRING:
if (sscanf(xc->xc_string, "%lf",&n) != 1)
n = NAN;
break;
}
*n0 = n;
retval = 0;
done:
if (str)
free(str);
return retval;
}
/*! Replace a nodeset of a XPATH context with a new nodeset
*/
int
ctx_nodeset_replace(xp_ctx *xc,
cxobj **vec,
size_t veclen)
{
if (xc->xc_nodeset)
free(xc->xc_nodeset);
xc->xc_nodeset = vec;
xc->xc_size = veclen;
return 0;
}

View file

@ -0,0 +1,101 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 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 *****
*/
#ifndef _CLIXON_XPATH_PARSE_H_
#define _CLIXON_XPATH_PARSE_H_
/*
* Types
*/
/* used as non-terminal type in yacc rules */
enum xp_type{
XP_EXP,
XP_AND,
XP_RELEX,
XP_ADD,
XP_UNION,
XP_PATHEXPR,
XP_LOCPATH,
XP_ABSPATH,
XP_RELLOCPATH,
XP_STEP,
XP_NODE,
XP_NODE_FN,
XP_PRED,
XP_PRI0,
XP_PRIME_NR,
XP_PRIME_STR,
XP_PRIME_FN,
};
/*! XPATH Parsing generates a tree of nodes that is later traversed
*/
struct xpath_tree{
enum xp_type xs_type;
int xs_int;
double xs_double;
char *xs_s0;
char *xs_s1;
struct xpath_tree *xs_c0; /* child 0 */
struct xpath_tree *xs_c1; /* child 1 */
};
typedef struct xpath_tree xpath_tree;
struct clicon_xpath_yacc_arg{ /* XXX: mostly unrelevant */
const char *xy_name; /* Name of syntax (for error string) */
int xy_linenum; /* Number of \n in parsed buffer */
char *xy_parse_string; /* original (copy of) parse string */
void *xy_lexbuf; /* internal parse buffer from lex */
xpath_tree *xy_top;
};
/*
* Variables
*/
extern char *clixon_xpath_parsetext;
/*
* Prototypes
*/
int xpath_scan_init(struct clicon_xpath_yacc_arg *jy);
int xpath_scan_exit(struct clicon_xpath_yacc_arg *jy);
int xpath_parse_init(struct clicon_xpath_yacc_arg *jy);
int xpath_parse_exit(struct clicon_xpath_yacc_arg *jy);
int clixon_xpath_parselex(void *);
int clixon_xpath_parseparse(void *);
void clixon_xpath_parseerror(void *, char*);
#endif /* _CLIXON_XPATH_PARSE_H_ */

View file

@ -0,0 +1,174 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 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 *****
*/
%{
#include "clixon_config.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <netinet/in.h>
#include "clixon_xpath_parse.tab.h" /* generated */
#include <cligen/cligen.h>
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_log.h"
#include "clixon_string.h"
#include "clixon_xml.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_xpath_parse.h"
/* Redefine main lex function so that you can send arguments to it: _yy is added to arg list */
#define YY_DECL int clixon_xpath_parselex(void *_yy)
/* Dont use input function (use user-buffer) */
#define YY_NO_INPUT
/* typecast macro */
#define _XY ((struct clicon_xpath_yacc_arg *)_yy)
#define MAXBUF 4*4*64*1024
#undef clixon_xpath_parsewrap
int
clixon_xpath_parsewrap(void)
{
return 1;
}
%}
digit [0-9]
integer {digit}+
real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+)
%x TOKEN
%s QLITERAL
%s ALITERAL
%%
<TOKEN>[ \t]
<TOKEN>\n { _XY->xy_linenum++; }
<TOKEN>\r { }
<TOKEN><<EOF>> { return X_EOF; }
<TOKEN>".." { return DOUBLEDOT; }
<TOKEN>[()\[\]\.@,/:|] { return *yytext; }
<TOKEN>"::" { return DOUBLECOLON; }
<TOKEN>and { clixon_xpath_parselval.intval = clicon_str2int(xpopmap, yytext); return LOGOP; }
<TOKEN>or { clixon_xpath_parselval.intval = clicon_str2int(xpopmap, yytext); return LOGOP; }
<TOKEN>div { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; }
<TOKEN>mod { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; }
<TOKEN>[+*\-] { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; }
<TOKEN>\? { return *yytext; }
<TOKEN>"//" { return DOUBLESLASH; }
<TOKEN>"!=" { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return RELOP; }
<TOKEN>">=" { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; }
<TOKEN>"<=" { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; }
<TOKEN>[<>=] { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; }
<TOKEN>last { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
<TOKEN>position { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
<TOKEN>count { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
<TOKEN>ancestor { clixon_xpath_parselval.intval = A_ANCESTOR; return AXISNAME; }
<TOKEN>ancestor-or-self { clixon_xpath_parselval.intval = A_ANCESTOR_OR_SELF; return AXISNAME; }
<TOKEN>attribute { clixon_xpath_parselval.intval = A_ATTRIBUTE; return AXISNAME; }
<TOKEN>child { clixon_xpath_parselval.intval = A_CHILD; return AXISNAME; }
<TOKEN>descendant { clixon_xpath_parselval.intval = A_DESCENDANT; return AXISNAME; }
<TOKEN>descendant-or-self { clixon_xpath_parselval.intval = A_DESCENDANT_OR_SELF; return AXISNAME; }
<TOKEN>following { clixon_xpath_parselval.intval = A_FOLLOWING; return AXISNAME; }
<TOKEN>following-sibling { clixon_xpath_parselval.intval = A_FOLLOWING_SIBLING; return AXISNAME; }
<TOKEN>namespace { clixon_xpath_parselval.intval = A_NAMESPACE; return AXISNAME; }
<TOKEN>parent { clixon_xpath_parselval.intval = A_PARENT; return AXISNAME; }
<TOKEN>preceding { clixon_xpath_parselval.intval = A_PRECEEDING; return AXISNAME; }
<TOKEN>preceding-sibling { clixon_xpath_parselval.intval = A_PRECEEDING_SIBLING; return AXISNAME; }
<TOKEN>self { clixon_xpath_parselval.intval = A_SELF; return AXISNAME; }
<TOKEN>current { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; }
<TOKEN>comment { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; }
<TOKEN>text { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; }
<TOKEN>processing-instructions { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; }
<TOKEN>node { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; }
<TOKEN>\" { BEGIN(QLITERAL); return QUOTE; }
<TOKEN>\' { BEGIN(ALITERAL); return APOST; }
<TOKEN>\-?({integer}|{real}) { sscanf(yytext,"%lf",&clixon_xpath_parselval.dval); return NUMBER;}
<TOKEN>[0-9A-Za-z_\-]+ { clixon_xpath_parselval.string = strdup(yytext);
return NAME; /* rather be catch-all */
}
<TOKEN>. { fprintf(stderr,"LEXICAL ERROR\n"); return -1; }
<QLITERAL>\" { BEGIN(TOKEN); return QUOTE; }
<QLITERAL>. { clixon_xpath_parselval.string = strdup(yytext);
return CHAR;}
<ALITERAL>\' { BEGIN(TOKEN); return APOST; }
<ALITERAL>. { clixon_xpath_parselval.string = strdup(yytext);
return CHAR;}
%%
/*! Initialize scanner.
*/
int
xpath_scan_init(struct clicon_xpath_yacc_arg *xy)
{
BEGIN(TOKEN);
xy->xy_lexbuf = yy_scan_string (xy->xy_parse_string);
#if 1 /* XXX: just to use unput to avoid warning */
if (0)
yyunput(0, "");
#endif
return 0;
}
/*
* free buffers
* Even within Flex version 2.5 (this is assumed), freeing buffers is different.
*/
int
xpath_scan_exit(struct clicon_xpath_yacc_arg *xy)
{
yy_delete_buffer(xy->xy_lexbuf);
clixon_xpath_parselex_destroy(); /* modern */
return 0;
}

View file

@ -0,0 +1,289 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 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 *****
* XPATH Parser
* From https://www.w3.org/TR/xpath-10/
* The primary syntactic construct in XPath is the expression.
* An expression matches the production Expr
* see https://www.w3.org/TR/xpath-10/#NT-Expr)
* Lexical structure is defined by ExprToken, see
* see https://www.w3.org/TR/xpath-10/#exprlex
*/
%start start
%union {
int intval;
double dval;
char *string;
void *stack; /* xpath_tree */
}
%token <intval> AXISNAME
%token <intval> LOGOP
%token <intval> ADDOP
%token <intval> RELOP
%token <dval> NUMBER
%token <string> X_EOF
%token <string> QUOTE
%token <string> APOST
%token <string> CHAR
%token <string> NAME
%token <string> NODETYPE
%token <string> DOUBLEDOT
%token <string> DOUBLECOLON
%token <string> DOUBLESLASH
%token <string> FUNCTIONNAME
%type <intval> axisspec
%type <string> string
%type <stack> expr
%type <stack> andexpr
%type <stack> relexpr
%type <stack> addexpr
%type <stack> unionexpr
%type <stack> pathexpr
%type <stack> locationpath
%type <stack> abslocpath
%type <stack> rellocpath
%type <stack> step
%type <stack> nodetest
%type <stack> predicates
%type <stack> primaryexpr
%lex-param {void *_xy} /* Add this argument to parse() and lex() function */
%parse-param {void *_xy}
%{
/* Here starts user C-code */
/* typecast macro */
#define _XY ((struct clicon_xpath_yacc_arg *)_xy)
#define _YYERROR(msg) {clicon_err(OE_XML, 0, "YYERROR %s '%s' %d", (msg), clixon_xpath_parsetext, _XY->xy_linenum); YYERROR;}
/* add _yy to error paramaters */
#define YY_(msgid) msgid
#include "clixon_config.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <fnmatch.h>
#include <assert.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <cligen/cligen.h>
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_queue.h"
#include "clixon_string.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_xpath_parse.h"
extern int clixon_xpath_parseget_lineno (void);
/*
also called from yacc generated code *
*/
void
clixon_xpath_parseerror(void *_xy,
char *s)
{
clicon_err(OE_XML, 0, "%s on line %d: %s at or before: '%s'",
_XY->xy_name,
_XY->xy_linenum ,
s,
clixon_xpath_parsetext);
return;
}
int
xpath_parse_init(struct clicon_xpath_yacc_arg *xy)
{
// clicon_debug_init(2, NULL);
return 0;
}
int
xpath_parse_exit(struct clicon_xpath_yacc_arg *xy)
{
return 0;
}
static xpath_tree *
xp_new(enum xp_type type,
int i0,
double d0,
char *s0,
char *s1,
xpath_tree *c0,
xpath_tree *c1)
{
xpath_tree *xs = NULL;
if ((xs = malloc(sizeof(xpath_tree))) == NULL){
clicon_err(OE_XML, errno, "malloc");
goto done;
}
memset(xs, 0, sizeof(*xs));
xs->xs_type = type;
xs->xs_int = i0;
xs->xs_double = d0;
xs->xs_s0 = s0;
xs->xs_s1 = s1;
xs->xs_c0 = c0;
xs->xs_c1 = c1;
done:
return xs;
}
%}
%%
/*
*/
start : expr X_EOF { _XY->xy_top=$1;clicon_debug(1,"start->expr"); YYACCEPT; }
| locationpath X_EOF { _XY->xy_top=$1;clicon_debug(1,"start->locationpath"); YYACCEPT; }
;
expr : expr LOGOP andexpr { $$=xp_new(XP_EXP,$2,0.0,NULL,NULL,$1, $3);clicon_debug(1,"expr->expr or andexpr"); }
| andexpr { $$=xp_new(XP_EXP,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"expr-> andexpr"); }
;
andexpr : andexpr LOGOP relexpr { $$=xp_new(XP_AND,$2,0.0,NULL,NULL,$1, $3);clicon_debug(1,"andexpr-> andexpr and relexpr"); }
| relexpr { $$=xp_new(XP_AND,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"andexpr-> relexpr"); }
;
relexpr : relexpr RELOP addexpr { $$=xp_new(XP_RELEX,$2,0.0,NULL,NULL,$1, $3);clicon_debug(1,"relexpr-> relexpr relop addexpr"); }
| addexpr { $$=xp_new(XP_RELEX,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"relexpr-> addexpr"); }
;
addexpr : addexpr ADDOP unionexpr { $$=xp_new(XP_ADD,$2,0.0,NULL,NULL,$1, $3);clicon_debug(1,"addexpr-> addexpr ADDOP unionexpr"); }
| unionexpr { $$=xp_new(XP_ADD,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"addexpr-> unionexpr"); }
;
/* node-set */
unionexpr : unionexpr '|' pathexpr { $$=xp_new(XP_UNION,A_NAN,0.0,NULL,NULL,$1, $3);clicon_debug(1,"unionexpr-> unionexpr | pathexpr"); }
| pathexpr { $$=xp_new(XP_UNION,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"unionexpr-> pathexpr"); }
;
pathexpr : locationpath { $$=xp_new(XP_PATHEXPR,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"pathexpr-> locationpath"); }
| primaryexpr { $$=xp_new(XP_PATHEXPR,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"pathexpr-> primaryexpr"); }
;
/* location path returns a node-set */
locationpath : rellocpath { $$=xp_new(XP_LOCPATH,A_NAN,0.0,NULL,NULL,$1, NULL); clicon_debug(1,"locationpath-> rellocpath"); }
| abslocpath { $$=xp_new(XP_LOCPATH,A_NAN,0.0,NULL,NULL,$1, NULL); clicon_debug(1,"locationpath-> abslocpath"); }
;
abslocpath : '/' { $$=xp_new(XP_ABSPATH,A_ROOT,0.0,NULL,NULL,NULL, NULL);clicon_debug(1,"abslocpath-> /"); }
| '/' rellocpath { $$=xp_new(XP_ABSPATH,A_ROOT,0.0,NULL,NULL,$2, NULL);clicon_debug(1,"abslocpath->/ rellocpath");}
/* // is short for /descendant-or-self::node()/ */
| DOUBLESLASH rellocpath {$$=xp_new(XP_ABSPATH,A_DESCENDANT_OR_SELF,0.0,NULL,NULL,$2, NULL); clicon_debug(1,"abslocpath-> // rellocpath"); }
;
rellocpath : step { $$=xp_new(XP_RELLOCPATH,A_NAN,0.0,NULL,NULL,$1, NULL); clicon_debug(1,"rellocpath-> step"); }
| rellocpath '/' step { $$=xp_new(XP_RELLOCPATH,A_NAN,0.0,NULL,NULL,$1, $3);clicon_debug(1,"rellocpath-> rellocpath / step"); }
| rellocpath DOUBLESLASH step { $$=xp_new(XP_RELLOCPATH,A_DESCENDANT_OR_SELF,0.0,NULL,NULL,$1, $3); clicon_debug(1,"rellocpath-> rellocpath // step"); }
;
step : axisspec nodetest predicates {$$=xp_new(XP_STEP,$1,0.0, NULL, NULL, $2, $3);clicon_debug(1,"step->axisspec(%d) nodetest", $1); }
| '.' { $$=xp_new(XP_STEP,A_SELF, 0.0,NULL, NULL, NULL, NULL); clicon_debug(1,"step-> ."); }
| DOUBLEDOT { $$=xp_new(XP_STEP, A_PARENT, 0.0,NULL, NULL, NULL, NULL); clicon_debug(1,"step-> .."); }
;
axisspec : AXISNAME DOUBLECOLON { clicon_debug(1,"axisspec-> AXISNAME(%d) ::", $1); $$=$1;}
| '@' { $$=A_ATTRIBUTE; clicon_debug(1,"axisspec-> @"); }
| { clicon_debug(1,"axisspec-> "); $$=A_CHILD;}
;
nodetest : '*' { $$=xp_new(XP_NODE,A_NAN,0.0, NULL, NULL, NULL, NULL); clicon_debug(1,"nodetest-> *"); }
| NAME { $$=xp_new(XP_NODE,A_NAN,0.0, NULL, $1, NULL, NULL); clicon_debug(1,"nodetest-> name(%s)",$1); }
| NAME ':' NAME { $$=xp_new(XP_NODE,A_NAN,0.0, $1, $3, NULL, NULL);clicon_debug(1,"nodetest-> name(%s) : name(%s)", $1, $3); }
| NAME ':' '*' { $$=xp_new(XP_NODE,A_NAN,0.0, $1, NULL, NULL, NULL);clicon_debug(1,"nodetest-> name(%s) : *", $1); }
| NODETYPE '(' ')' { $$=xp_new(XP_NODE_FN,A_NAN,0.0, $1, NULL, NULL, NULL); clicon_debug(1,"nodetest-> nodetype()"); }
;
/* evaluates to boolean */
predicates : predicates '[' expr ']' { $$=xp_new(XP_PRED,A_NAN,0.0, NULL, NULL, $1, $3); clicon_debug(1,"predicates-> [ expr ]"); }
| { $$=xp_new(XP_PRED,A_NAN,0.0, NULL, NULL, NULL, NULL); clicon_debug(1,"predicates->"); }
;
primaryexpr : '(' expr ')' { $$=xp_new(XP_PRI0,A_NAN,0.0, NULL, NULL, $2, NULL); clicon_debug(1,"primaryexpr-> ( expr )"); }
| NUMBER { $$=xp_new(XP_PRIME_NR,A_NAN, $1, NULL, NULL, NULL, NULL);clicon_debug(1,"primaryexpr-> NUMBER(%lf)", $1); }
| QUOTE string QUOTE { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, $2, NULL, NULL, NULL);clicon_debug(1,"primaryexpr-> \" string \""); }
| QUOTE QUOTE { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, NULL, NULL, NULL, NULL);clicon_debug(1,"primaryexpr-> \" \""); }
| APOST string APOST { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, $2, NULL, NULL, NULL);clicon_debug(1,"primaryexpr-> ' string '"); }
| APOST APOST { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, NULL, NULL, NULL, NULL);clicon_debug(1,"primaryexpr-> ' '"); }
| FUNCTIONNAME '(' ')' { $$=xp_new(XP_PRIME_FN,A_NAN,0.0, $1, NULL, NULL, NULL);clicon_debug(1,"primaryexpr-> functionname ( arguments )"); }
;
/* XXX Adding this between FUNCTIONNAME() breaks parser,..
arguments : arguments expr { clicon_debug(1,"arguments-> arguments expr"); }
| { clicon_debug(1,"arguments-> "); }
;
*/
string : string CHAR {
int len = strlen($1);
$$ = realloc($1, len+strlen($2) + 1);
sprintf($$+len, "%s", $2);
free($2);
clicon_debug(1,"string-> string CHAR");
}
| CHAR { clicon_debug(1,"string-> "); }
;
%%

View file

@ -35,54 +35,12 @@
* NOTE: there is a main function at the end of this file where you can test out * NOTE: there is a main function at the end of this file where you can test out
* different xpath expressions. * different xpath expressions.
* Look at the end of the file for a test unit program * Look at the end of the file for a test unit program
*/
/*
See https://www.w3.org/TR/xpath/
Implementation of a limited xslt xpath syntax. Some examples. Given the following
xml tree:
<aaa>
<bbb x="hello"><ccc>42</ccc></bbb>
<bbb x="bye"><ccc>99</ccc></bbb>
<ddd><ccc>22</ccc></ddd>
</aaa>
With the following xpath examples. There are some diffs and many limitations compared
to the xml standards:
/ whole tree <aaa>...</aaa>
/bbb
/aaa/bbb <bbb x="hello"><ccc>42</ccc></bbb>
<bbb x="bye"><ccc>99</ccc></bbb>
//bbb as above
//b?b as above
//b\* as above
//b\*\/ccc <ccc>42</ccc>
<ccc>99</ccc>
//\*\/ccc <ccc>42</ccc>
<ccc>99</ccc>
<ccc>22</ccc>
-- //bbb@x x="hello"
//bbb[@x] <bbb x="hello"><ccc>42</ccc></bbb>
<bbb x="bye"><ccc>99</ccc></bbb>
//bbb[@x=hello] <bbb x="hello"><ccc>42</ccc></bbb>
//bbb[@x="hello"] as above
//bbb[0] <bbb x="hello"><ccc>42</ccc></bbb>
//bbb[ccc=99] <bbb x="bye"><ccc>99</ccc></bbb>
--- //\*\/[ccc=99] same as above
'//bbb | //ddd' <bbb><ccc>42</ccc></bbb>
<bbb x="hello"><ccc>99</ccc></bbb>
<ddd><ccc>22</ccc></ddd> (NB spaces)
etc
For xpath v1.0 see http://www.w3.org/TR/xpath/
record[name=c][time=d]
in
<record>
<name>c</name>
<time>d</time>
<userid>45654df4-2292-45d3-9ca5-ee72452568a8</userid>
</record>
The code is implemented according to XPATH 1.0:
https://www.w3.org/TR/xpath-10/
The primary syntactic construct in XPath is the expression. An expression matches
the production Expr (see https://www.w3.org/TR/xpath-10/#NT-Expr)
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -95,6 +53,7 @@ in
#include <assert.h> #include <assert.h>
#include <syslog.h> #include <syslog.h>
#include <fcntl.h> #include <fcntl.h>
#include <math.h>
/* cligen */ /* cligen */
#include <cligen/cligen.h> #include <cligen/cligen.h>
@ -108,14 +67,18 @@ in
#include "clixon_handle.h" #include "clixon_handle.h"
#include "clixon_yang.h" #include "clixon_yang.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_xsl.h" #include "clixon_xsl.h"
/* Constants */ /* Constants */
#define XPATH_VEC_START 128 #define XPATH_VEC_START 128
/* /*
* Types * Types
*/ */
struct searchvec{ struct searchvec{
cxobj **sv_v0; /* here is result */ cxobj **sv_v0; /* here is result */
int sv_v0len; int sv_v0len;
@ -127,13 +90,22 @@ typedef struct searchvec searchvec;
/* Local types /* Local types
*/ */
enum axis_type{
A_SELF, struct xpath_predicate{
A_CHILD, struct xpath_predicate *xp_next;
A_PARENT, char *xp_expr;
A_ROOT, };
A_ANCESTOR,
A_DESCENDANT_OR_SELF, /* actually descendant-or-self */ /* XPATH Axis according to https://www.w3.org/TR/xpath-10/#NT-Step
* Axis ::= AxisSpecifier NodeTest Predicate*
* Eg "child::
*/
struct xpath_element{
struct xpath_element *xe_next;
enum axis_type xe_type;
char *xe_prefix; /* eg for namespaces */
char *xe_str; /* eg for child */
struct xpath_predicate *xe_predicate; /* eg within [] */
}; };
/* Mapping between axis type string <--> int */ /* Mapping between axis type string <--> int */
@ -147,23 +119,12 @@ static const map_str2int axismap[] = {
{NULL, -1} {NULL, -1}
}; };
struct xpath_predicate{
struct xpath_predicate *xp_next;
char *xp_expr;
};
struct xpath_element{
struct xpath_element *xe_next;
enum axis_type xe_type;
char *xe_prefix; /* eg for namespaces */
char *xe_str; /* eg for child */
struct xpath_predicate *xe_predicate; /* eg within [] */
};
static int xpath_split(char *xpathstr, char **pathexpr); static int xpath_split(char *xpathstr, char **pathexpr);
/*! Print xpath structure for debug */
static int static int
xpath_print(FILE *f, struct xpath_element *xplist) xpath_print(FILE *f,
struct xpath_element *xplist)
{ {
struct xpath_element *xe; struct xpath_element *xe;
struct xpath_predicate *xp; struct xpath_predicate *xp;
@ -217,6 +178,11 @@ xpath_parse_predicate(struct xpath_element *xe,
return retval; return retval;
} }
/*! XPATH parse, create new child element
* @param[in] atype Axis type, see https://www.w3.org/TR/xpath-10/#axes
* @param[in] str
* @param[out] xpnext
*/
static int static int
xpath_element_new(enum axis_type atype, xpath_element_new(enum axis_type atype,
char *str, char *str,
@ -309,8 +275,17 @@ xpath_free(struct xpath_element *xplist)
return 0; return 0;
} }
/* /*! Parse xpath to xpath_element structure
* // is short for /descendant-or-self::node()/
*
* [1] LocationPath ::= RelativeLocationPath
* | AbsoluteLocationPath
* [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
* | AbbreviatedAbsoluteLocationPath
* [3] RelativeLocationPath ::= Step
| RelativeLocationPath '/' Step
| AbbreviatedRelativeLocationPath
* @see https://www.w3.org/TR/xpath-10/#NT-LocationPath
*/ */
static int static int
xpath_parse(char *xpath, xpath_parse(char *xpath,
@ -355,6 +330,7 @@ xpath_parse(char *xpath,
} }
s++; s++;
} }
/* Iterate through steps (s), see https://www.w3.org/TR/xpath-10/#NT-Step */
s = s0; s = s0;
for (i=0; i<nvec; i++){ for (i=0; i<nvec; i++){
if ((i==0 && strcmp(s,"")==0)) /* Initial / or // */ if ((i==0 && strcmp(s,"")==0)) /* Initial / or // */
@ -364,21 +340,10 @@ xpath_parse(char *xpath,
else if (strncmp(s,"descendant-or-self::", strlen("descendant-or-self::"))==0){ else if (strncmp(s,"descendant-or-self::", strlen("descendant-or-self::"))==0){
xpath_element_new(A_DESCENDANT_OR_SELF, s+strlen("descendant-or-self::"), &xpnext); xpath_element_new(A_DESCENDANT_OR_SELF, s+strlen("descendant-or-self::"), &xpnext);
} }
#if 1
else if (strncmp(s,"..", strlen(".."))==0) /* abbreviatedstep */ else if (strncmp(s,"..", strlen(".."))==0) /* abbreviatedstep */
xpath_element_new(A_PARENT, s+strlen(".."), &xpnext); xpath_element_new(A_PARENT, s+strlen(".."), &xpnext);
#else
else if (strncmp(s,"..", strlen(s))==0) /* abbreviatedstep */
xpath_element_new(A_PARENT, NULL, &xpnext);
#endif
#if 1 /* Problems with .[userid=1321] */
else if (strncmp(s,".", strlen("."))==0) else if (strncmp(s,".", strlen("."))==0)
xpath_element_new(A_SELF, s+strlen("."), &xpnext); xpath_element_new(A_SELF, s+strlen("."), &xpnext);
#else
else if (strncmp(s,".", strlen(s))==0) /* abbreviatedstep */
xpath_element_new(A_SELF, NULL, &xpnext);
#endif
else if (strncmp(s,"self::", strlen("self::"))==0) else if (strncmp(s,"self::", strlen("self::"))==0)
xpath_element_new(A_SELF, s+strlen("self::"), &xpnext); xpath_element_new(A_SELF, s+strlen("self::"), &xpnext);
@ -406,17 +371,15 @@ xpath_parse(char *xpath,
* The xv_* arguments are filled in nodes found earlier. * The xv_* arguments are filled in nodes found earlier.
* args: * args:
* @param[in] xn_parent Base XML object * @param[in] xn_parent Base XML object
* @param[in] name shell wildcard pattern to match with node name * @param[in] pattern Shell wildcard pattern to match with node name
* @param[in] node_type CX_ELMNT, CX_ATTR or CX_BODY * @param[in] node_type CX_ELMNT, CX_ATTR or CX_BODY
* @param[in,out] vec1 internal buffers with results * @param[in,out] vec0 Internal buffers with results
* @param[in,out] vec0 internal buffers with results * @param[in,out] vec0len Internal buffers with length of vec0
* @param[in,out] vec_len internal buffers with length of vec0,vec1
* @param[in,out] vec_max internal buffers with max of vec0,vec1
* returns: * returns:
* 0 on OK, -1 on error * 0 on OK, -1 on error
*/ */
static int static int
recursive_find(cxobj *xn, xpath_recursive_find(cxobj *xn,
char *pattern, char *pattern,
int node_type, int node_type,
uint16_t flags, uint16_t flags,
@ -439,7 +402,7 @@ recursive_find(cxobj *xn,
goto done; goto done;
// continue; /* Dont go deeper */ // continue; /* Dont go deeper */
} }
if (recursive_find(xsub, pattern, node_type, flags, &vec, &veclen) < 0) if (xpath_recursive_find(xsub, pattern, node_type, flags, &vec, &veclen) < 0)
goto done; goto done;
} }
retval = 0; retval = 0;
@ -695,7 +658,7 @@ xpath_find(cxobj *xcur,
if (descendants0){ if (descendants0){
for (i=0; i<vec0len; i++){ for (i=0; i<vec0len; i++){
xv = vec0[i]; xv = vec0[i];
if (recursive_find(xv, xe->xe_str, CX_ELMNT, flags, &vec1, &vec1len) < 0) if (xpath_recursive_find(xv, xe->xe_str, CX_ELMNT, flags, &vec1, &vec1len) < 0)
goto done; goto done;
} }
} }
@ -738,7 +701,6 @@ xpath_find(cxobj *xcur,
} }
} }
} }
for (xp = xe->xe_predicate; xp; xp = xp->xp_next){ for (xp = xe->xe_predicate; xp; xp = xp->xp_next){
if (xpath_expr(xcur, xp->xp_expr, flags, &vec0, &vec0len) < 0) if (xpath_expr(xcur, xp->xp_expr, flags, &vec0, &vec0len) < 0)
goto done; goto done;
@ -798,6 +760,7 @@ xpath_split(char *xpathstr,
* @param[in] flags if != 0, only match xml nodes matching flags * @param[in] flags if != 0, only match xml nodes matching flags
* @param[out] vec2 Result XML node vector * @param[out] vec2 Result XML node vector
* @param[out] vec2len Length of result vector. * @param[out] vec2len Length of result vector.
* @see https://www.w3.org/TR/xpath-10/#NT-LocationPath
*/ */
static int static int
xpath_exec(cxobj *xcur, xpath_exec(cxobj *xcur,
@ -831,14 +794,20 @@ xpath_exec(cxobj *xcur,
* @param[in] xcur xml-tree where to search * @param[in] xcur xml-tree where to search
* @param[in] xpath string with XPATH syntax * @param[in] xpath string with XPATH syntax
* @param[in] flags if != 0, only match xml nodes matching flags * @param[in] flags if != 0, only match xml nodes matching flags
* @param[in] vec1 vector of XML trees * @param[out] vec1 vector of XML trees
* @param[in] vec1len length of XML trees * @param[out] vec1len length of XML trees
* For example: xpath = //a | //b. * For example: xpath = //a | //b.
* xpath_first+ splits xpath up in several subcalls * xpath_first+ splits xpath up in several subcalls
* (eg xpath=//a and xpath=//b) and collects the results. * (eg xpath=//a and xpath=//b) and collects the results.
* Note: if a match is found in both, two (or more) same results will be * Note: if a match is found in both, two (or more) same results will be
* returned. * returned.
* Note, this could be 'folded' into xpath1 but I judged it too complex. * Note, this could be 'folded' into xpath1 but I judged it too complex.
* @see https://www.w3.org/TR/xpath-10/#NT-Expr
* An 'Expr' is composed of compositions of and, or, =, +, -, down to:
* PathExpr ::= LocationPath
* | FilterExpr
* | FilterExpr '/' RelativeLocationPath
* | FilterExpr '//' RelativeLocationPath
*/ */
static int static int
xpath_choice(cxobj *xcur, xpath_choice(cxobj *xcur,
@ -848,7 +817,7 @@ xpath_choice(cxobj *xcur,
size_t *vec1len) size_t *vec1len)
{ {
int retval = -1; int retval = -1;
char *s0; char *s0 = NULL;
char *s1; char *s1;
char *s2; char *s2;
char *xpath; char *xpath;
@ -1020,7 +989,7 @@ xpath_each(cxobj *xcur,
/*! A restricted xpath that returns a vector of matches /*! A restricted xpath that returns a vector of matches
* *
* See xpath1() on details for subset * See xpath1() on details for subset
. * @param[in] xcur xml-tree where to search * @param[in] xcur xml-tree where to search
* @param[in] xpath string with XPATH syntax * @param[in] xpath string with XPATH syntax
* @param[out] vec vector of xml-trees. Vector must be free():d after use * @param[out] vec vector of xml-trees. Vector must be free():d after use
* @param[out] veclen returns length of vector in return value * @param[out] veclen returns length of vector in return value
@ -1052,7 +1021,7 @@ xpath_vec(cxobj *xcur,
int retval = -1; int retval = -1;
va_list ap; va_list ap;
size_t len; size_t len;
char *xpath; char *xpath = NULL;
va_start(ap, veclen); va_start(ap, veclen);
len = vsnprintf(NULL, 0, format, ap); len = vsnprintf(NULL, 0, format, ap);
@ -1079,6 +1048,7 @@ xpath_vec(cxobj *xcur,
return retval; return retval;
} }
/* A restricted xpath that returns a vector of matches (only nodes marked with flags) /* A restricted xpath that returns a vector of matches (only nodes marked with flags)
* @param[in] xcur xml-tree where to search * @param[in] xcur xml-tree where to search
* @param[in] xpath string with XPATH syntax * @param[in] xpath string with XPATH syntax
@ -1140,96 +1110,3 @@ xpath_vec_flag(cxobj *xcur,
return retval; return retval;
} }
/*
* Turn this on to get an xpath test program
* Usage: xpath [<xpath>]
* read xpath on first line and xml on rest of lines from input
* Example compile:
gcc -g -o xpath -I. -I../clixon ./clixon_xsl.c -lclixon -lcligen
* Example run:
echo "a\n<a><b/></a>" | xpath
*/
#if 0 /* Test program */
static int
usage(char *argv0)
{
fprintf(stderr, "usage:%s <xml file name>.\n\tInput on stdin\n", argv0);
exit(0);
}
int
main(int argc, char **argv)
{
int retval = -1;
int i;
cxobj **xv;
cxobj *x;
cxobj *xn;
size_t xlen = 0;
int c;
int len;
char *buf = NULL;
char *filename;
int fd;
if (argc != 2){
usage(argv[0]);
goto done;
}
filename = argv[1];
if ((fd = open(filename, O_RDONLY)) < 0){
clicon_err(OE_UNIX, errno, "open(%s)", filename);
goto done;
}
/* Read xpath */
len = 1024; /* any number is fine */
if ((buf = malloc(len)) == NULL){
perror("pt_file malloc");
return -1;
}
memset(buf, 0, len);
i = 0;
while (1){ /* read the whole file */
if ((c = fgetc(stdin)) == EOF)
return -1;
if (c == '\n')
break;
if (len==i){
if ((buf = realloc(buf, 2*len)) == NULL){
fprintf(stderr, "%s: realloc: %s\n", __FUNCTION__, strerror(errno));
return -1;
}
memset(buf+len, 0, len);
len *= 2;
}
buf[i++] = (char)(c&0xff);
}
x = NULL;
if (xml_parse_file(fd, "</clicon>", NULL, &x) < 0){
fprintf(stderr, "Error: parsing: %s\n", clicon_err_reason);
return -1;
}
close (fd);
printf("\n");
if (xpath_vec(x, "%s", &xv, &xlen, buf) < 0)
return -1;
if (xv){
for (i=0; i<xlen; i++){
xn = xv[i];
fprintf(stdout, "[%d]:\n", i);
clicon_xml2file(stdout, xn, 0, 1);
}
free(xv);
}
if (x)
xml_free(x);
retval = 0;
done:
return retval;
}
#endif /* Test program */

View file

@ -39,7 +39,7 @@ err(){
echo -e "\e[0m" echo -e "\e[0m"
echo "$ret"| od -t c > $dir/clixon-ret echo "$ret"| od -t c > $dir/clixon-ret
echo "$expect"| od -t c > $dir/clixon-expect echo "$expect"| od -t c > $dir/clixon-expect
diff $dir/clixon-ret $dir/clixon-expect diff $dir/clixon-expect $dir/clixon-ret
exit $testnr exit $testnr
} }

151
test/test_xpath.sh Executable file
View file

@ -0,0 +1,151 @@
#!/bin/bash
# Test: XPATH tests
PROG=../lib/src/clixon_util_xpath
# include err() and new() functions and creates $dir
. ./lib.sh
# XML file (alt provide it in stdin after xpath)
xml=$dir/xml.xml
xml2=$dir/xml2.xml
cat <<EOF > $xml
<aaa>
<bbb x="hello"><ccc>42</ccc></bbb>
<bbb x="bye"><ccc>99</ccc></bbb>
<ddd><ccc>22</ccc></ddd>
</aaa>
EOF
cat <<EOF > $xml2
<if:interfaces>
<if:interface>
<if:name>e0</if:name>
<ip:ipv6>
<ip:enabled>true</ip:enabled>
</ip:ipv6>
</if:interface>
</if:interfaces>
<rt:name>e0</rt:name>
<address-family>myfamily</address-family>
<aaa>
<rt:address-family>v6ur:ipv6-unicast</rt:address-family>
<name>foo</name>
<bbb>
<routing>
<ribs>
<rib>
<name>bar</name>
<address-family>myfamily</address-family>
</rib>
</ribs>
</routing>
<max-rtr-adv-interval>22</max-rtr-adv-interval>
<valid-lifetime>99</valid-lifetime>
<connection-type>responder-only</connection-type>
<type>rt:static</type>
<rib-name>bar</rib-name>
<here>0</here>
<here2><here/></here2>
<ifType>ethernet</ifType>
<ifMTU>1500</ifMTU>
</bbb>
</aaa>
EOF
new "xpath /"
expecteof "$PROG -f $xml -p /" 0 "" "^nodeset:0:<aaa><bbb x=\"hello\"><ccc>42</ccc></bbb><bbb x=\"bye\"><ccc>99</ccc></bbb><ddd><ccc>22</ccc></ddd></aaa>$"
new "xpath /aaa"
expecteof "$PROG -f $xml -p /aaa" 0 "" "^nodeset:0:<aaa><bbb x=\"hello\"><ccc>42</ccc></bbb><bbb x=\"bye\"><ccc>99</ccc></bbb><ddd><ccc>22</ccc></ddd></aaa>$"
new "xpath /bbb"
expecteof "$PROG -f $xml -p /bbb" 0 "" "^nodeset:$"
new "xpath /aaa/bbb"
expecteof "$PROG -f $xml -p /aaa/bbb" 0 "" "^0:<bbb x=\"hello\"><ccc>42</ccc></bbb>
1:<bbb x=\"bye\"><ccc>99</ccc></bbb>$"
new "xpath //bbb"
expecteof "$PROG -f $xml -p //bbb" 0 "" "0:<bbb x=\"hello\"><ccc>42</ccc></bbb>
1:<bbb x=\"bye\"><ccc>99</ccc></bbb>"
new "xpath //b?b"
#expecteof "$PROG -f $xml" 0 "//b?b" ""
new "xpath //b*"
#expecteof "$PROG -f $xml" 0 "//b*" ""
new "xpath //b*/ccc"
#expecteof "$PROG -f $xml" 0 "//b*/ccc" ""
new "xpath //bbb[0]"
expecteof "$PROG -f $xml -p //bbb[0]" 0 "" "^nodeset:0:<bbb x=\"hello\"><ccc>42</ccc></bbb>$"
new "xpath //bbb[ccc=99]"
expecteof "$PROG -f $xml -p //bbb[ccc=99]" 0 "" "^nodeset:0:<bbb x=\"bye\"><ccc>99</ccc></bbb>$"
new "xpath ../connection-type = 'responder-only'"
expecteof "$PROG -f $xml2 -p ../connection-type='responder-only' -i /aaa/bbb/here" 0 "" "^bool:true$"
new "xpath ../connection-type = 'no-responder'"
expecteof "$PROG -f $xml2 -p ../connection-type='no-responder' -i /aaa/bbb/here" 0 "" "^bool:false$"
new "xpath . <= 0.75 * ../max-rtr-adv-interval"
expecteof "$PROG -f $xml2 -i /aaa/bbb/here" 0 ". <= 0.75 * ../max-rtr-adv-interval" "^bool:true$"
new "xpath . > 0.75 * ../max-rtr-adv-interval"
expecteof "$PROG -f $xml2 -i /aaa/bbb/here" 0 ". > 0.75 * ../max-rtr-adv-interval" "^bool:false$"
new "xpath . <= ../valid-lifetime"
expecteof "$PROG -f $xml2 -i /aaa/bbb/here" 0 ". <= ../valid-lifetime" "^bool:true$"
new "xpath ../../rt:address-family = 'v6ur:ipv6-unicast'"
expecteof "$PROG -f $xml2 -i /aaa/bbb/here" 0 "../../rt:address-family = 'v6ur:ipv6-unicast'" "^bool:true$"
new "xpath ../../../rt:address-family = 'v6ur:ipv6-unicast'"
expecteof "$PROG -f $xml2 -i /aaa/bbb/here2/here" 0 "../../../rt:address-family = 'v6ur:ipv6-unicast'" "^bool:true$"
new "xpath /if:interfaces/if:interface[if:name=current()/rt:name]/ip:ipv6/ip:enabled='true'"
expecteof "$PROG -f $xml2" 0 "/if:interfaces/if:interface[if:name=current()/rt:name]/ip:ipv6/ip:enabled='true'" "^bool:true$"
new "xpath rt:address-family='v6ur:ipv6-unicast'"
expecteof "$PROG -f $xml2 -i /aaa" 0 "rt:address-family='v6ur:ipv6-unicast'" "^bool:true$"
new "xpath ../type='rt:static'"
expecteof "$PROG -f $xml2 -i /aaa/bbb/here" 0 "../type='rt:static'" "^bool:true$"
new "xpath rib-name != ../../name"
expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "rib-name != ../../name" "^bool:true$"
new "xpath routing/ribs/rib[name=current()/rib-name]/address-family=../../address-family"
expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "routing/ribs/rib[name=current()/rib-name]/address-family=../../address-family" "^bool:true$"
new "xpath ifType = \"ethernet\" or ifMTU = 1500"
expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "ifType = \"ethernet\" or ifMTU = 1500" "^bool:true$"
new "xpath ifType != \"ethernet\" or ifMTU = 1500"
expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "ifType != \"ethernet\" or ifMTU = 1500" "^bool:true$"
new "xpath ifType = \"ethernet\" or ifMTU = 1400"
expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "ifType = \"ethernet\" or ifMTU = 1400" "^bool:true$"
new "xpath ifType != \"ethernet\" or ifMTU = 1400"
expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "ifType != \"ethernet\" or ifMTU = 1400" "^bool:false$"
new "xpath ifType = \"ethernet\" and ifMTU = 1500"
expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "ifType = \"ethernet\" and ifMTU = 1500" "^bool:true$"
new "xpath ifType != \"ethernet\" and ifMTU = 1500"
expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "ifType != \"ethernet\" and ifMTU = 1500" "^bool:false$"
new "xpath ifType = \"ethernet\" and ifMTU = 1400"
expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "ifType = \"ethernet\" and ifMTU = 1400" "^bool:false$"
new "xpath ifType != \"ethernet\" and ifMTU = 1400"
expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "ifType != \"ethernet\" and ifMTU = 1400" "^bool:false$"
new "xpath ifType != \"atm\" or (ifMTU <= 17966 and ifMTU >= 64)"
expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "ifType != \"atm\" or (ifMTU <= 17966 and ifMTU >= 64)" "^bool:true$"
rm -rf $dir

View file

@ -1,12 +0,0 @@
#!/bin/bash
# Test: XSL tests
PROG=../lib/src/clixon_util_xsl
# include err() and new() functions and creates $dir
. ./lib.sh
new "xsl test"
expecteof $PROG 0 "a
<a><b/></a>" "^0:<a><b/></a>$"
rm -rf $dir

View file

@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
# Test4: Yang specifics: multi-keys and empty type # Yang specifics: multi-keys and empty type
APPNAME=example APPNAME=example
# include err() and new() functions and creates $dir # include err() and new() functions and creates $dir
. ./lib.sh . ./lib.sh