* Optimizations

* Reduced memory for attribute and body objects, see `XML_NEW_DIFFERENTIATE` compile-time option.
  * Optimized cbuf handling in parsing and xml2cbuf functions.
  * Optimized xml scanner to read strings rather than single chars
  * Optimized xml_merge for the case of disjunct trees.
This commit is contained in:
Olof hagsand 2020-04-28 22:31:58 +02:00
parent 9a8c6cf3e6
commit 94cf4a88b3
24 changed files with 477 additions and 257 deletions

View file

@ -2,7 +2,8 @@
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
# Copyright (C) 2017-2020 Olof Hagsand
# Copyright (C) 2017-2019 Olof Hagsand
# Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
#
# This file is part of CLIXON
#

View file

@ -1266,7 +1266,12 @@ _json_parse(char *str,
}
if (failed)
goto fail;
/* This fails if xt is not bound to yang */
/* Sort the complete tree after parsing. Sorting is not really meaningful if Yang
not bound */
if (yb != YB_NONE)
if (xml_sort_recurse(xt) < 0)
goto done;
if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0)
goto done;
retval = 1;

View file

@ -314,6 +314,7 @@ uri_percent_decode(char *enc,
* ' -> "&apos; " may
* ' -> "&quot; " may
* Optionally >
* @see xml_chardata_cbuf_append for a specialized version
*/
int
xml_chardata_encode(char **escp,
@ -433,6 +434,58 @@ xml_chardata_encode(char **escp,
return retval;
}
/*! Escape characters according to XML definition and append to cbuf
* @param[in] cb CLIgen buf
* @param[in] fmt Not-encoded input string
* @see xml_chardata_encode for the generic function
*/
int
xml_chardata_cbuf_append(cbuf *cb,
char *str)
{
int retval = -1;
int i;
int cdata; /* when set, skip encoding */
/* The orignal of this code is in xml_chardata_encode */
/* Step: encode and expand str --> enc */
/* Same code again, but now actually encode into output buffer */
cdata = 0;
for (i=0; i<strlen(str); i++){
if (cdata){
if (strncmp(&str[i], "]]>", strlen("]]>")) == 0){
cdata = 0;
cbuf_append(cb, str[i++]);
cbuf_append(cb, str[i++]);
}
cbuf_append(cb, str[i]);
}
else
switch (str[i]){
case '&':
cbuf_append_str(cb, "&amp;");
break;
case '<':
if (strncmp(&str[i], "<![CDATA[", strlen("<![CDATA[")) == 0){
cbuf_append(cb, str[i]);
cdata++;
break;
}
cbuf_append_str(cb, "&lt;");
break;
case '>':
cbuf_append_str(cb, "&gt;");
break;
default:
cbuf_append(cb, str[i]);
}
}
retval = 0;
// done:
return retval;
}
/*! Split a string into a cligen variable vector using 1st and 2nd delimiter
* Split a string first into elements delimited by delim1, then into
* pairs delimited by delim2.

View file

@ -79,8 +79,12 @@
* Constants
*/
/* How many XML children to start with if any. Then add quadratic until threshold when
* add lineraly */
#define XML_CHILDVEC_SIZE_START 1
* add lineraly
* Heurestics: if child is body only single child is expected, but element children may
* have siblings
*/
#define XML_CHILDVEC_SIZE_START 1
#define XML_CHILDVEC_SIZE_START_ELMNT 16
#define XML_CHILDVEC_SIZE_THRESHOLD 65536
/* Intention of these macros is to guard against access of type-specific fields
@ -182,6 +186,8 @@ struct xml{
#endif
};
#ifdef XML_NEW_DIFFERENTIATE
/* This is experimental variant of struct xml for use by non-elements to save space
*/
struct xmlbody{
@ -196,6 +202,7 @@ struct xmlbody{
cbuf *xb_value_cb; /* attribute and body nodes have values (XXX: this consumes
memory) cv? */
};
#endif /* XML_NEW_DIFFERENTIATE */
/*
* Variables
@ -245,13 +252,13 @@ xml_stats_one(cxobj *x,
{
size_t sz = 0;
sz += sizeof(struct xml);
if (x->x_name)
sz += strlen(x->x_name) + 1;
if (x->x_prefix)
sz += strlen(x->x_prefix) + 1;
switch (xml_type(x)){
case CX_ELMNT:
sz += sizeof(struct xml);
sz += x->x_childvec_max*sizeof(struct xml*);
if (x->x_ns_cache)
sz += cvec_size(x->x_ns_cache);
@ -270,9 +277,13 @@ xml_stats_one(cxobj *x,
break;
case CX_BODY:
case CX_ATTR:
#ifdef XML_NEW_DIFFERENTIATE
sz += sizeof(struct xmlbody);
#else
sz += sizeof(struct xmlbody);
#endif
if (x->x_value_cb)
sz += cbuf_buflen(x->x_value_cb);
break;
default:
break;
@ -610,7 +621,7 @@ xml_value_set(cxobj *xn,
}
else
cbuf_reset(xn->x_value_cb);
cprintf(xn->x_value_cb, "%s", val);
cbuf_append_str(xn->x_value_cb, val);
retval = 0;
done:
return retval;
@ -642,7 +653,7 @@ xml_value_append(cxobj *xn,
goto done;
}
}
if (cprintf(xn->x_value_cb, "%s", val) < 0){
if (cbuf_append_str(xn->x_value_cb, val) < 0){
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
@ -878,17 +889,27 @@ xml_child_each(cxobj *xparent,
/*! Extend child vector with one and insert xml node there
* @note does not do anything with child, you may need to set its parent, etc
* @see xml_child_insert_pos
* XXX could insert hint if we know this is a yang list and not a leaf to increase start.
*/
static int
xml_child_append(cxobj *xp,
cxobj *xc)
{
size_t start;
if (!is_element(xp))
return 0;
start = XML_CHILDVEC_SIZE_START;
/* Heurestics: if child is body only single child is expected, but element children may
* have siblings
*/
if (xml_type(xc) == CX_ELMNT)
start = XML_CHILDVEC_SIZE_START_ELMNT;
xp->x_childvec_len++;
if (xp->x_childvec_len > xp->x_childvec_max){
if (xp->x_childvec_len < XML_CHILDVEC_SIZE_THRESHOLD)
xp->x_childvec_max = xp->x_childvec_max?2*xp->x_childvec_max:XML_CHILDVEC_SIZE_START;
xp->x_childvec_max = xp->x_childvec_max?2*xp->x_childvec_max:start;
else
xp->x_childvec_max += XML_CHILDVEC_SIZE_THRESHOLD;
xp->x_childvec = realloc(xp->x_childvec, xp->x_childvec_max*sizeof(cxobj*));
@ -983,6 +1004,49 @@ xml_childvec_get(cxobj *x)
* @endcode
* @see xml_sort_insert
*/
#ifdef XML_NEW_DIFFERENTIATE
/* Differentiate creating XML object body/element vs elenmet to reduce space */
cxobj *
xml_new(char *name,
cxobj *xp,
enum cxobj_type type)
{
struct xml *x = NULL;
size_t sz;
switch (type){
case CX_ELMNT:
sz = sizeof(struct xml);
break;
case CX_ATTR:
case CX_BODY:
sz = sizeof(struct xmlbody);
break;
default:
clicon_err(OE_XML, EINVAL, "Invalid type: %d", type);
return NULL;
break;
}
if ((x = malloc(sz)) == NULL){
clicon_err(OE_XML, errno, "malloc");
return NULL;
}
memset(x, 0, sz);
xml_type_set(x, type);
if (name && (xml_name_set(x, name)) < 0)
return NULL;
if (xp){
xml_parent_set(x, xp);
if (xml_child_append(xp, x) < 0)
return NULL;
x->_x_i = xml_child_nr(xp)-1;
}
_stats_nr++;
return x;
}
#else /* XML_NEW_DIFFERENTIATE */
cxobj *
xml_new(char *name,
cxobj *xp,
@ -1008,6 +1072,7 @@ xml_new(char *name,
_stats_nr++;
return x;
}
#endif /* XML_NEW_DIFFERENTIATE */
/*! Create a new XML node and set it's body to a value
*
@ -1045,48 +1110,6 @@ xml_new_body(char *name,
return new_node;
}
#ifdef NOTYET
/*! Create new xml node given a name and parent. Free with xml_free().
*/
cxobj *
xml_new2(char *name,
cxobj *xp,
enum cxobj_type type)
{
struct xml *x = NULL;
size_t sz;
switch (type){
case CX_ELMNT:
sz = sizeof(struct xml);
break;
case CX_ATTR:
case CX_BODY:
sz = sizeof(struct xmlbody);
break;
default:
clicon_err(OE_XML, EINVAL, "Invalid type: %d", type);
return NULL;
break;
}
if ((x = malloc(sz)) == NULL){
clicon_err(OE_XML, errno, "malloc");
return NULL;
}
memset(x, 0, sz);
xml_type_set(x, type);
if (name && (xml_name_set(x, name)) < 0)
return NULL;
if (xp){
xml_parent_set(x, xp);
if (xml_child_append(xp, x) < 0)
return NULL;
x->_x_i = xml_child_nr(xp)-1;
}
_stats_nr++;
return x;
}
#endif /* NOTYET */
/*! Return yang spec of node.
* Not necessarily set. Either has not been set yet (by xml_spec_set( or anyxml.

View file

@ -241,7 +241,6 @@ clicon_xml2cbuf(cbuf *cb,
int hasbody;
int haselement;
char *namespace;
char *encstr = NULL; /* xml encoded string */
char *val;
if (depth == 0)
@ -252,21 +251,27 @@ clicon_xml2cbuf(cbuf *cb,
case CX_BODY:
if ((val = xml_value(x)) == NULL) /* incomplete tree */
break;
if (xml_chardata_encode(&encstr, "%s", val) < 0)
if (xml_chardata_cbuf_append(cb, val) < 0)
goto done;
cprintf(cb, "%s", encstr);
break;
case CX_ATTR:
cprintf(cb, " ");
if (namespace)
cprintf(cb, "%s:", namespace);
cbuf_append_str(cb, " ");
if (namespace){
cbuf_append_str(cb, namespace);
cbuf_append_str(cb, ":");
}
cprintf(cb, "%s=\"%s\"", name, xml_value(x));
break;
case CX_ELMNT:
cprintf(cb, "%*s<", prettyprint?(level*XML_INDENT):0, "");
if (namespace)
cprintf(cb, "%s:", namespace);
cprintf(cb, "%s", name);
if (prettyprint)
cprintf(cb, "%*s<", level*XML_INDENT, "");
else
cbuf_append_str(cb, "<");
if (namespace){
cbuf_append_str(cb, namespace);
cbuf_append_str(cb, ":");
}
cbuf_append_str(cb, name);
hasbody = 0;
haselement = 0;
xc = NULL;
@ -288,11 +293,11 @@ clicon_xml2cbuf(cbuf *cb,
}
/* Check for special case <a/> instead of <a></a> */
if (hasbody==0 && haselement==0)
cprintf(cb, "/>");
cbuf_append_str(cb, "/>");
else{
cprintf(cb, ">");
cbuf_append_str(cb, ">");
if (prettyprint && hasbody == 0)
cprintf(cb, "\n");
cbuf_append_str(cb, "\n");
xc = NULL;
while ((xc = xml_child_each(x, xc, -1)) != NULL)
if (xml_type(xc) != CX_ATTR)
@ -300,13 +305,16 @@ clicon_xml2cbuf(cbuf *cb,
goto done;
if (prettyprint && hasbody == 0)
cprintf(cb, "%*s", level*XML_INDENT, "");
cprintf(cb, "</");
if (namespace)
cprintf(cb, "%s:", namespace);
cprintf(cb, "%s>", name);
cbuf_append_str(cb, "</");
if (namespace){
cbuf_append_str(cb, namespace);
cbuf_append_str(cb, ":");
}
cbuf_append_str(cb, name);
cbuf_append_str(cb, ">");
}
if (prettyprint)
cprintf(cb, "\n");
cbuf_append_str(cb, "\n");
break;
default:
break;
@ -314,8 +322,6 @@ clicon_xml2cbuf(cbuf *cb,
ok:
retval = 0;
done:
if (encstr)
free(encstr);
return retval;
}
@ -465,10 +471,11 @@ _xml_parse(const char *str,
}
if (failed)
goto fail;
/* This fails if xt is not bound to yang */
/* Sort the complete tree after parsing. Sorting is less meaningful if Yang not bound */
if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0)
goto done;
/* Sort the complete tree after parsing. Sorting is not really meaningful if Yang
not bound */
if (yb != YB_NONE)
if (xml_sort_recurse(xt) < 0)
goto done;
retval = 1;
done:
clixon_xml_parsel_exit(&xy);

View file

@ -79,6 +79,18 @@
#include "clixon_yang_type.h"
#include "clixon_xml_map.h"
/* Local types
*/
/* Merge code needs a two-phase pass where objects subject to merge are first checked for,
* the actually inserted.
* This is to mitigate a search problem where objects inserted are among the ones checked for
*/
typedef struct {
cxobj *mt_x0c;
cxobj *mt_x1c;
yang_stmt *mt_yc;
} merge_twophase;
/*! Is attribute and is either of form xmlns="", or xmlns:x="" */
int
isxmlns(cxobj *x)
@ -1264,6 +1276,7 @@ assign_namespace(cxobj *x0, /* source */
/*! Given a src element node x0 and a target node x1, assign (optional) prefix and namespace
* @param[in] x0 Source XML tree
* @param[in] x1 Target XML tree
* @param[in] x1p Target XML tree parent
* @retval 0 OK
* @retval -1 OK
* 1. Find N=namespace(x0)
@ -1380,33 +1393,42 @@ xml_merge1(cxobj *x0, /* the target */
cxobj *x0c; /* base child */
cxobj *x0b; /* base body */
cxobj *x1c; /* mod child */
char *x1name;
char *x1bstr; /* mod body string */
yang_stmt *yc; /* yang child */
cbuf *cbr = NULL; /* Reason buffer */
int ret;
int i;
struct {
cxobj *w_x0c;
cxobj *w_x1c;
yang_stmt *w_yc;
} *second_wave = NULL;
merge_twophase *twophase = NULL;
int twophase_len;
assert(x1 && xml_type(x1) == CX_ELMNT);
assert(y0);
x1name = xml_name(x1);
if (x0 == NULL){
cvec *nsc = NULL;
cg_var *cv;
char *ns;
char *px;
nsc = cvec_dup(nscache_get_all(x1));
if (xml_rm(x1) < 0)
goto done;
if (xml_insert(x0p, x1, INS_LAST, NULL, NULL) < 0)
goto done;
cv = NULL;
while ((cv = cvec_each(nsc, cv)) != NULL){
px = cv_name_get(cv);
ns = cv_string_get(cv);
/* Check if it exists */
if (xml2prefix(x1, ns, NULL) == 0)
if (xmlns_set(x1, px, ns) < 0)
goto done;
}
if (nsc)
cvec_free(nsc);
goto ok;
}
if (yang_keyword_get(y0) == Y_LEAF_LIST || yang_keyword_get(y0) == Y_LEAF){
x1bstr = xml_body(x1);
if (x0==NULL){
if ((x0 = xml_new(x1name, NULL, CX_ELMNT)) == NULL)
goto done;
xml_spec_set(x0, y0);
if (x1bstr){ /* empty type does not have body */
if ((x0b = xml_new("body", x0, CX_BODY)) == NULL)
goto done;
}
}
if (x1bstr){
if ((x0b = xml_body_get(x0)) == NULL){
if ((x0b = xml_new("body", x0, CX_BODY)) == NULL)
@ -1422,14 +1444,10 @@ xml_merge1(cxobj *x0, /* the target */
goto done;
} /* if LEAF|LEAF_LIST */
else { /* eg Y_CONTAINER, Y_LIST */
if (x0==NULL){
if ((x0 = xml_new(x1name, NULL, CX_ELMNT)) == NULL)
goto done;
xml_spec_set(x0, y0);
}
if (assign_namespace_element(x1, x0, x0p) < 0)
goto done;
if ((second_wave = calloc(xml_child_nr(x1), sizeof(*second_wave))) == NULL){
twophase_len = xml_child_nr(x1);
if ((twophase = calloc(twophase_len, sizeof(*twophase))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
@ -1457,37 +1475,37 @@ xml_merge1(cxobj *x0, /* the target */
x0c = NULL;
if (yc && match_base_child(x0, x1c, yc, &x0c) < 0)
goto done;
/* Save x0c, x1c, yc and merge in second wave, so that x1c entries "interfer"
/* Save x0c, x1c, yc and merge in second wave, so that x1c entries dont "interfer"
* with itself, ie that later searches are among earlier objects already added
* to x0 */
second_wave[i].w_x0c = x0c;
second_wave[i].w_x1c = x1c;
second_wave[i].w_yc = yc;
twophase[i].mt_x0c = x0c;
twophase[i].mt_x1c = x1c;
twophase[i].mt_yc = yc;
i++;
} /* while */
twophase_len = i; /* Inital length included non-elements */
/* Second run where actual merging is done
* Loop through children of the modification tree */
x1c = NULL;
i = 0;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
if ((ret = xml_merge1(second_wave[i].w_x0c,
second_wave[i].w_yc,
for (i=0; i<twophase_len; i++){
assert(twophase[i].mt_x1c);
if ((ret = xml_merge1(twophase[i].mt_x0c,
twophase[i].mt_yc,
x0,
second_wave[i].w_x1c,
twophase[i].mt_x1c,
reason)) < 0)
goto done;
if (ret == 0)
goto fail;
i++;
}
if (xml_parent(x0) == NULL &&
xml_insert(x0p, x0, INS_LAST, NULL, NULL) < 0)
goto done;
} /* else Y_CONTAINER */
ok:
retval = 1;
done:
if (second_wave)
free(second_wave);
if (twophase)
free(twophase);
if (cbr)
cbuf_free(cbr);
return retval;
@ -1519,12 +1537,22 @@ xml_merge(cxobj *x0,
yang_stmt *yc;
yang_stmt *ymod;
cbuf *cbr = NULL; /* Reason buffer */
int i;
merge_twophase *twophase = NULL;
int twophase_len;
int ret;
if (x0 == NULL || x1 == NULL){
clicon_err(OE_UNIX, EINVAL, "parameters x0 or x1 is NULL");
goto done;
}
twophase_len = xml_child_nr(x1);
if ((twophase = calloc(twophase_len, sizeof(*twophase))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
/* Loop through children of the modification tree */
i = 0;
x1c = NULL;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
x1cname = xml_name(x1c);
@ -1553,20 +1581,36 @@ xml_merge(cxobj *x0,
}
goto fail;
}
x0c = NULL;
/* See if there is a corresponding node (x1c) in the base tree (x0) */
if (match_base_child(x0, x1c, yc, &x0c) < 0)
if (yc && match_base_child(x0, x1c, yc, &x0c) < 0)
goto done;
/* There is a case where x0c and x1c are choice nodes, if so,
* it is treated as a match, and x0c will remain
* If it is overwritten, then x0c should be removed here.
*/
if (xml_merge1(x0c, yc, x0, x1c, reason) < 0)
/* Save x0c, x1c, yc and merge in second wave, so that x1c entries dont "interfer"
* with itself, ie that later searches are among earlier objects already added
* to x0 */
twophase[i].mt_x0c = x0c;
twophase[i].mt_x1c = x1c;
twophase[i].mt_yc = yc;
i++;
}
twophase_len = i; /* Inital length included non-elements */
/* Second run where actual merging is done
* Loop through children of the modification tree */
for (i=0; i<twophase_len; i++){
assert(twophase[i].mt_x1c);
if ((ret = xml_merge1(twophase[i].mt_x0c,
twophase[i].mt_yc,
x0,
twophase[i].mt_x1c,
reason)) < 0)
goto done;
if (*reason != NULL)
break;
if (ret == 0)
goto fail;
}
retval = 1; /* OK */
done:
if (twophase)
free(twophase);
if (cbr)
cbuf_free(cbr);
return retval;

View file

@ -510,7 +510,7 @@ xmlns_set(cxobj *x,
return retval;
}
/*! Get namespace given prefix recursively
/*! Get prefix of given namespace recursively
* @param[in] xn XML node
* @param[in] namespace Namespace
* @param[out] prefixp Pointer to prefix if found
@ -567,7 +567,8 @@ xml2prefix(cxobj *xn,
done:
return retval;
found:
*prefixp = prefix;
if (prefixp)
*prefixp = prefix;
retval = 1;
goto done;
}

View file

@ -138,6 +138,7 @@ ncname {namestart}{namechar}*
<STATEA>\r\n { clixon_xml_parselval.string = "\n"; _XY->xy_linenum++; return WHITESPACE; }
<STATEA>\r { clixon_xml_parselval.string = "\n";return WHITESPACE; }
<STATEA>\n { clixon_xml_parselval.string = "\n"; _XY->xy_linenum++;return WHITESPACE; }
<STATEA>[^&\r\n \t\<]+ { clixon_xml_parselval.string = yytext; return CHARDATA; /* Optimized */}
<STATEA>. { clixon_xml_parselval.string = yytext; return CHARDATA; }
/* @see xml_chardata_encode */

View file

@ -83,6 +83,13 @@
#include "clixon_xml_sort.h"
#include "clixon_xml_parse.h"
/* Enable for debugging, steals some cycles otherwise */
#if 0
#define _PARSE_DEBUG(s) clicon_debug(2,(s))
#else
#define _PARSE_DEBUG(s)
#endif
void
clixon_xml_parseerror(void *_xy,
char *s)
@ -94,7 +101,9 @@ clixon_xml_parseerror(void *_xy,
return;
}
/*
/*! Parse XML content, eg chars between >...<
* @param[in] xy
* @param[in] str Body string, direct pointer (copy before use, dont free)
* Note that we dont handle escaped characters correctly
* there may also be some leakage here on NULL return
*/
@ -325,39 +334,39 @@ xml_parse_attr(clixon_xml_yacc *xy,
%%
/* [1] document ::= prolog element Misc* */
document : prolog element misclist MY_EOF
{ clicon_debug(2, "document->prolog element misc* ACCEPT");
{ _PARSE_DEBUG("document->prolog element misc* ACCEPT");
YYACCEPT; }
| elist MY_EOF
{ clicon_debug(2, "document->elist ACCEPT"); /* internal exception*/
{ _PARSE_DEBUG("document->elist ACCEPT"); /* internal exception*/
YYACCEPT; }
;
/* [22] prolog ::= XMLDecl? Misc* (doctypedecl Misc*)? */
prolog : xmldcl misclist
{ clicon_debug(2, "prolog->xmldcl misc*"); }
{ _PARSE_DEBUG("prolog->xmldcl misc*"); }
| misclist
{ clicon_debug(2, "prolog->misc*"); }
{ _PARSE_DEBUG("prolog->misc*"); }
;
misclist : misclist misc { clicon_debug(2, "misclist->misclist misc"); }
| { clicon_debug(2, "misclist->"); }
misclist : misclist misc { _PARSE_DEBUG("misclist->misclist misc"); }
| { _PARSE_DEBUG("misclist->"); }
;
/* [27] Misc ::= Comment | PI | S */
misc : comment { clicon_debug(2, "misc->comment"); }
| pi { clicon_debug(2, "misc->pi"); }
| WHITESPACE { clicon_debug(2, "misc->white space"); }
misc : comment { _PARSE_DEBUG("misc->comment"); }
| pi { _PARSE_DEBUG("misc->pi"); }
| WHITESPACE { _PARSE_DEBUG("misc->white space"); }
;
xmldcl : BXMLDCL verinfo encodingdecl sddecl EQMARK
{ clicon_debug(2, "xmldcl->verinfo encodingdecl? sddecl?"); }
{ _PARSE_DEBUG("xmldcl->verinfo encodingdecl? sddecl?"); }
;
verinfo : VER '=' '\"' STRING '\"'
{ if (xml_parse_version(_XY, $4) <0) YYABORT;
clicon_debug(2, "verinfo->version=\"STRING\"");}
_PARSE_DEBUG("verinfo->version=\"STRING\"");}
| VER '=' '\'' STRING '\''
{ if (xml_parse_version(_XY, $4) <0) YYABORT;
clicon_debug(2, "verinfo->version='STRING'");}
_PARSE_DEBUG("verinfo->version='STRING'");}
;
encodingdecl : ENC '=' '\"' STRING '\"' {if ($4)free($4);}
@ -371,53 +380,53 @@ sddecl : SD '=' '\"' STRING '\"' {if ($4)free($4);}
;
/* [39] element ::= EmptyElemTag | STag content ETag */
element : '<' qname attrs element1
{ clicon_debug(2, "element -> < qname attrs element1"); }
{ _PARSE_DEBUG("element -> < qname attrs element1"); }
;
qname : NAME { if (xml_parse_prefixed_name(_XY, NULL, $1) < 0) YYABORT;
clicon_debug(2, "qname -> NAME %s", $1);}
_PARSE_DEBUG("qname -> NAME");}
| NAME ':' NAME { if (xml_parse_prefixed_name(_XY, $1, $3) < 0) YYABORT;
clicon_debug(2, "qname -> NAME : NAME");}
_PARSE_DEBUG("qname -> NAME : NAME");}
;
element1 : ESLASH {_XY->xy_xelement = NULL;
clicon_debug(2, "element1 -> />");}
_PARSE_DEBUG("element1 -> />");}
| '>' { xml_parse_endslash_pre(_XY); }
elist { xml_parse_endslash_mid(_XY); }
endtag { xml_parse_endslash_post(_XY);
clicon_debug(2, "element1 -> > elist endtag");}
_PARSE_DEBUG("element1 -> > elist endtag");}
;
endtag : BSLASH NAME '>'
{ clicon_debug(2, "endtag -> < </ NAME>");
{ _PARSE_DEBUG("endtag -> < </ NAME>");
if (xml_parse_bslash(_XY, NULL, $2) < 0) YYABORT; }
| BSLASH NAME ':' NAME '>'
{ if (xml_parse_bslash(_XY, $2, $4) < 0) YYABORT;
clicon_debug(2, "endtag -> < </ NAME:NAME >"); }
_PARSE_DEBUG("endtag -> < </ NAME:NAME >"); }
;
elist : elist content { clicon_debug(2, "elist -> elist content"); }
| content { clicon_debug(2, "elist -> content"); }
elist : elist content { _PARSE_DEBUG("elist -> elist content"); }
| content { _PARSE_DEBUG("elist -> content"); }
;
/* Rule 43 */
content : element { clicon_debug(2, "content -> element"); }
| comment { clicon_debug(2, "content -> comment"); }
| pi { clicon_debug(2, "content -> pi"); }
content : element { _PARSE_DEBUG("content -> element"); }
| comment { _PARSE_DEBUG("content -> comment"); }
| pi { _PARSE_DEBUG("content -> pi"); }
| CHARDATA { if (xml_parse_content(_XY, $1) < 0) YYABORT;
clicon_debug(2, "content -> CHARDATA %s", $1); }
_PARSE_DEBUG("content -> CHARDATA"); }
| WHITESPACE { if (xml_parse_whitespace(_XY, $1) < 0) YYABORT;
clicon_debug(2, "content -> WHITESPACE %s", $1); }
| { clicon_debug(2, "content -> "); }
_PARSE_DEBUG("content -> WHITESPACE"); }
| { _PARSE_DEBUG("content -> "); }
;
comment : BCOMMENT ECOMMENT
;
pi : BQMARK NAME EQMARK {clicon_debug(2, "pi -> <? NAME ?>"); free($2); }
pi : BQMARK NAME EQMARK {_PARSE_DEBUG("pi -> <? NAME ?>"); free($2); }
| BQMARK NAME STRING EQMARK
{ clicon_debug(2, "pi -> <? NAME STRING ?>"); free($2); free($3);}
{ _PARSE_DEBUG("pi -> <? NAME STRING ?>"); free($2); free($3);}
;

View file

@ -407,6 +407,30 @@ xml_sort(cxobj *x,
return 0;
}
/*! Recursively sort a tree
* Alt to use xml_apply
*/
int
xml_sort_recurse(cxobj *xn)
{
int retval = -1;
cxobj *x;
int ret;
x = NULL;
while ((x = xml_child_each(xn, x, CX_ELMNT)) != NULL) {
if ((ret = xml_sort(x, NULL)) < 0)
goto done;
if (ret == 1) /* This node is not sortable */
break;
if (xml_sort_recurse(x) < 0)
goto done;
}
retval = 0;
done:
return retval;
}
/*! Special case search for ordered-by user or state data where linear sort is used
*
* @param[in] xp Parent XML node (go through its childre)

View file

@ -341,8 +341,10 @@ xpath_optimize_check(xpath_tree *xs,
_optimize_hits++;
return 1; /* Optimized */
}
else
else{
clixon_xvec_free(xvec);
return 0; /* use regular code */
}
#else
return 0; /* use regular code */
#endif