* 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

@ -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_hash.c clixon_options.c clixon_plugin.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 \
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.
APPSRC = clixon_util_xml.c
APPSRC += clixon_util_json.c
APPSRC += clixon_util_yang.c
APPSRC += clixon_util_xsl.c
APPSRC += clixon_util_xpath.c
APPS = $(APPSRC:.c=)
@ -107,9 +108,11 @@ clean:
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_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_yang_parse.c
rm -f lex.clixon_json_parse.c
rm -f lex.clixon_xpath_parse.c
#############################################################################
# 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
$(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
clixon_util_xml: clixon_util_xml.c $(MYLIB)
$(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)
$(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 $@
distclean: clean

View file

@ -52,24 +52,24 @@
* clicon_hash_t *hash = hash_init();
*
* n = 234;
* hash_add (hash, "APA", &n, sizeof(n));
* hash_add(hash, "APA", &n, sizeof(n));
* hash_dump(hash, stdout);
*
* puts("----");
*
* hash_add (hash, "BEPA", "hoppla Polle!", strlen("hoppla Polle!")+1);
* hash_add(hash, "BEPA", "hoppla Polle!", strlen("hoppla Polle!")+1);
* puts((char *)hash_value(hash, "BEPA", NULL));
* hash_dump(hash, stdout);
*
* puts("----");
*
* n = 33;
* hash_add (hash, "CEPA", &n, sizeof(n));
* hash_add(hash, "CEPA", &n, sizeof(n));
* hash_dump(hash, stdout);
*
* puts("----");
*
* hash_del (hash, "APA");
* hash_del(hash, "APA");
* hash_dump(hash, stdout);
*
* hash_free(hash);
@ -118,11 +118,11 @@ hash_init (void)
{
clicon_hash_t *hash;
if ((hash = (clicon_hash_t *)malloc (sizeof (clicon_hash_t) * HASH_SIZE)) == NULL){
if ((hash = (clicon_hash_t *)malloc(sizeof(clicon_hash_t) * HASH_SIZE)) == NULL){
clicon_err(OE_UNIX, errno, "malloc: %s", strerror(errno));
return NULL;
}
memset (hash, 0, sizeof(clicon_hash_t)*HASH_SIZE);
memset(hash, 0, sizeof(clicon_hash_t)*HASH_SIZE);
return hash;
}
@ -167,7 +167,7 @@ hash_lookup(clicon_hash_t *hash,
h = hash[bkt];
if (h) {
do {
if (!strcmp (h->h_key, key))
if (!strcmp(h->h_key, key))
return h;
h = NEXTQ(clicon_hash_t, h);
} while (h != hash[bkt]);
@ -218,15 +218,15 @@ hash_add(clicon_hash_t *hash,
clicon_hash_t new = NULL;
/* If variable exist, don't allocate a new. just replace value */
h = hash_lookup (hash, key);
h = hash_lookup(hash, key);
if (h == NULL) {
if ((new = (clicon_hash_t)malloc (sizeof (*new))) == NULL){
if ((new = (clicon_hash_t)malloc(sizeof(*new))) == NULL){
clicon_err(OE_UNIX, errno, "malloc: %s", strerror(errno));
goto catch;
}
memset (new, 0, sizeof (*new));
memset(new, 0, sizeof(*new));
new->h_key = strdup (key);
new->h_key = strdup(key);
if (new->h_key == NULL){
clicon_err(OE_UNIX, errno, "strdup: %s", strerror(errno));
goto catch;
@ -241,11 +241,11 @@ hash_add(clicon_hash_t *hash,
clicon_err(OE_UNIX, errno, "malloc: %s", strerror(errno));
goto catch;
}
memcpy (newval, val, vlen);
memcpy(newval, val, vlen);
/* Free old value if existing variable */
if (h->h_val)
free (h->h_val);
free(h->h_val);
h->h_val = newval;
h->h_vlen = vlen;
@ -258,8 +258,8 @@ hash_add(clicon_hash_t *hash,
catch:
if (new) {
if (new->h_key)
free (new->h_key);
free (new);
free(new->h_key);
free(new);
}
return NULL;
@ -279,15 +279,15 @@ hash_del(clicon_hash_t *hash,
{
clicon_hash_t h;
h = hash_lookup (hash, key);
h = hash_lookup(hash, key);
if (h == NULL)
return -1;
DELQ(h, hash[hash_bucket(key)], clicon_hash_t);
free (h->h_key);
free (h->h_val);
free (h);
free(h->h_key);
free(h->h_val);
free(h);
return 0;
}

View file

@ -140,11 +140,11 @@ clicon_strjoin(int argc,
len += 1; /* '\0' */
if ((str = malloc(len)) == NULL)
return NULL;
memset (str, '\0', len);
memset(str, '\0', len);
for (i = 0; i < argc; i++) {
if (i != 0)
strncat (str, delim, len - strlen(str));
strncat (str, argv[i], len - strlen(str));
strncat(str, delim, len - strlen(str));
strncat(str, argv[i], len - strlen(str));
}
return str;
}
@ -518,21 +518,21 @@ clicon_str2int(const map_str2int *mstab,
*/
#ifndef HAVE_STRNDUP
char *
clicon_strndup (const char *str,
size_t len)
clicon_strndup(const char *str,
size_t len)
{
char *new;
size_t slen;
slen = strlen (str);
slen = strlen(str);
len = (len < slen ? len : slen);
new = malloc (len + 1);
new = malloc(len + 1);
if (new == NULL)
return NULL;
new[len] = '\0';
memcpy (new, str, len);
memcpy(new, str, len);
return new;
}

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,
cxobj *x1)
{
cg_var *cv1;
xml_type_set(x1, xml_type(x0));
if (xml_value(x0)){ /* malloced string */
if ((x1->x_value = strdup(x0->x_value)) == NULL){

View file

@ -424,13 +424,15 @@ xml_yang_validate_all(cxobj *xt,
void *arg)
{
int retval = -1;
yang_stmt *ys;
yang_stmt *ytype;
yang_stmt *ys; /* yang node */
yang_stmt *yc; /* yang child */
char *xpath;
/* if not given by argument (overide) use default link
and !Node has a config sub-statement and it is false */
if ((ys = xml_spec(xt)) != NULL &&
yang_config(ys) != 0){
/* Node-specific validation */
switch (ys->ys_keyword){
case Y_LEAF:
/* fall thru */
@ -438,20 +440,29 @@ xml_yang_validate_all(cxobj *xt,
/* Special case if leaf is leafref, then first check against
current xml tree
*/
if ((ytype = yang_find((yang_node*)ys, Y_TYPE, NULL)) != NULL){
if (strcmp(ytype->ys_argument, "leafref") == 0){
if (validate_leafref(xt, ytype) < 0)
if ((yc = yang_find((yang_node*)ys, Y_TYPE, NULL)) != NULL){
if (strcmp(yc->ys_argument, "leafref") == 0){
if (validate_leafref(xt, yc) < 0)
goto done;
}
else if (strcmp(ytype->ys_argument, "identityref") == 0){
if (validate_identityref(xt, ys, ytype) < 0)
else if (strcmp(yc->ys_argument, "identityref") == 0){
if (validate_identityref(xt, ys, yc) < 0)
goto done;
}
}
break;
case Y_MUST: /* RFC 7950 Sec 7.5.3 */
break;
default:
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;
done:
@ -1372,7 +1383,6 @@ xml_spec_populate(cxobj *x,
return retval;
}
/*! Translate from restconf api-path in cvv form to xml xpath
* eg a/b=c -> a/[b=c]
* @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
* different xpath expressions.
* 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 <stdlib.h>
@ -95,6 +53,7 @@ in
#include <assert.h>
#include <syslog.h>
#include <fcntl.h>
#include <math.h>
/* cligen */
#include <cligen/cligen.h>
@ -108,14 +67,18 @@ in
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_xsl.h"
/* Constants */
#define XPATH_VEC_START 128
/*
* Types
*/
struct searchvec{
cxobj **sv_v0; /* here is result */
int sv_v0len;
@ -127,13 +90,22 @@ typedef struct searchvec searchvec;
/* Local types
*/
enum axis_type{
A_SELF,
A_CHILD,
A_PARENT,
A_ROOT,
A_ANCESTOR,
A_DESCENDANT_OR_SELF, /* actually descendant-or-self */
struct xpath_predicate{
struct xpath_predicate *xp_next;
char *xp_expr;
};
/* 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 */
@ -147,23 +119,12 @@ static const map_str2int axismap[] = {
{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);
/*! Print xpath structure for debug */
static int
xpath_print(FILE *f, struct xpath_element *xplist)
xpath_print(FILE *f,
struct xpath_element *xplist)
{
struct xpath_element *xe;
struct xpath_predicate *xp;
@ -217,6 +178,11 @@ xpath_parse_predicate(struct xpath_element *xe,
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
xpath_element_new(enum axis_type atype,
char *str,
@ -309,8 +275,17 @@ xpath_free(struct xpath_element *xplist)
return 0;
}
/*
* // is short for /descendant-or-self::node()/
/*! Parse xpath to xpath_element structure
*
* [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
xpath_parse(char *xpath,
@ -355,6 +330,7 @@ xpath_parse(char *xpath,
}
s++;
}
/* Iterate through steps (s), see https://www.w3.org/TR/xpath-10/#NT-Step */
s = s0;
for (i=0; i<nvec; i++){
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){
xpath_element_new(A_DESCENDANT_OR_SELF, s+strlen("descendant-or-self::"), &xpnext);
}
#if 1
else if (strncmp(s,"..", strlen(".."))==0) /* abbreviatedstep */
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)
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)
xpath_element_new(A_SELF, s+strlen("self::"), &xpnext);
@ -405,23 +370,21 @@ xpath_parse(char *xpath,
*
* The xv_* arguments are filled in nodes found earlier.
* args:
* @param[in] xn_parent Base XML object
* @param[in] name shell wildcard pattern to match with node name
* @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] vec_len internal buffers with length of vec0,vec1
* @param[in,out] vec_max internal buffers with max of vec0,vec1
* @param[in] xn_parent Base XML object
* @param[in] pattern Shell wildcard pattern to match with node name
* @param[in] node_type CX_ELMNT, CX_ATTR or CX_BODY
* @param[in,out] vec0 Internal buffers with results
* @param[in,out] vec0len Internal buffers with length of vec0
* returns:
* 0 on OK, -1 on error
*/
static int
recursive_find(cxobj *xn,
char *pattern,
int node_type,
uint16_t flags,
cxobj ***vec0,
size_t *vec0len)
xpath_recursive_find(cxobj *xn,
char *pattern,
int node_type,
uint16_t flags,
cxobj ***vec0,
size_t *vec0len)
{
int retval = -1;
cxobj *xsub;
@ -439,7 +402,7 @@ recursive_find(cxobj *xn,
goto done;
// 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;
}
retval = 0;
@ -695,7 +658,7 @@ xpath_find(cxobj *xcur,
if (descendants0){
for (i=0; i<vec0len; 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;
}
}
@ -738,7 +701,6 @@ xpath_find(cxobj *xcur,
}
}
}
for (xp = xe->xe_predicate; xp; xp = xp->xp_next){
if (xpath_expr(xcur, xp->xp_expr, flags, &vec0, &vec0len) < 0)
goto done;
@ -798,6 +760,7 @@ xpath_split(char *xpathstr,
* @param[in] flags if != 0, only match xml nodes matching flags
* @param[out] vec2 Result XML node vector
* @param[out] vec2len Length of result vector.
* @see https://www.w3.org/TR/xpath-10/#NT-LocationPath
*/
static int
xpath_exec(cxobj *xcur,
@ -831,14 +794,20 @@ xpath_exec(cxobj *xcur,
* @param[in] xcur xml-tree where to search
* @param[in] xpath string with XPATH syntax
* @param[in] flags if != 0, only match xml nodes matching flags
* @param[in] vec1 vector of XML trees
* @param[in] vec1len length of XML trees
* @param[out] vec1 vector of XML trees
* @param[out] vec1len length of XML trees
* For example: xpath = //a | //b.
* xpath_first+ splits xpath up in several subcalls
* (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
* returned.
* 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
xpath_choice(cxobj *xcur,
@ -848,13 +817,13 @@ xpath_choice(cxobj *xcur,
size_t *vec1len)
{
int retval = -1;
char *s0;
char *s0 = NULL;
char *s1;
char *s2;
char *xpath;
cxobj **vec0 = NULL;
size_t vec0len = 0;
if ((s0 = strdup(xpath0)) == NULL){
clicon_err(OE_XML, errno, "strdup");
goto done;
@ -878,7 +847,7 @@ xpath_choice(cxobj *xcur,
goto done;
}
retval = 0;
done:
done:
if (s0)
free(s0);
if (vec0)
@ -1020,7 +989,7 @@ xpath_each(cxobj *xcur,
/*! A restricted xpath that returns a vector of matches
*
* 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[out] vec vector of xml-trees. Vector must be free():d after use
* @param[out] veclen returns length of vector in return value
@ -1052,7 +1021,7 @@ xpath_vec(cxobj *xcur,
int retval = -1;
va_list ap;
size_t len;
char *xpath;
char *xpath = NULL;
va_start(ap, veclen);
len = vsnprintf(NULL, 0, format, ap);
@ -1079,6 +1048,7 @@ xpath_vec(cxobj *xcur,
return retval;
}
/* A restricted xpath that returns a vector of matches (only nodes marked with flags)
* @param[in] xcur xml-tree where to search
* @param[in] xpath string with XPATH syntax
@ -1140,96 +1110,3 @@ xpath_vec_flag(cxobj *xcur,
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 */