diff --git a/CHANGELOG.md b/CHANGELOG.md index 80474fd1..da943f33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -142,7 +142,8 @@ * JSON conversion problems [https://github.com/clicon/clixon/issues/66] * CDATA sections stripped from XML when converted to JSON * Restconf returns error when RPC generates "ok" reply [https://github.com/clicon/clixon/issues/69] diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h index 5a4b13b5..da732586 100644 --- a/lib/clixon/clixon_xml.h +++ b/lib/clixon/clixon_xml.h @@ -130,6 +130,8 @@ int xml_childvec_set(cxobj *x, int len); cxobj *xml_new(char *name, cxobj *xn_parent, yang_stmt *spec); yang_stmt *xml_spec(cxobj *x); int xml_spec_set(cxobj *x, yang_stmt *spec); +cg_var *xml_cv(cxobj *x); +int xml_cv_set(cxobj *x, cg_var *cv); cxobj *xml_find(cxobj *xn_parent, char *name); int xml_addsub(cxobj *xp, cxobj *xc); diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index 1d4f2741..f236c5f0 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -126,6 +126,8 @@ struct xml{ int x_flags; /* Flags according to XML_FLAG_* */ yang_stmt *x_spec; /* Pointer to specification, eg yang, by reference, dont free */ + cg_var *x_cv; /* Cached value as cligen variable + (eg xml_cmp) */ }; /* @@ -758,6 +760,34 @@ xml_spec_set(cxobj *x, return 0; } +/*! Return (cached) cligen variable value of xml node + * @param[in] x XML node (body and leaf/leaf-list) + * @retval cv CLIgen variable containing value of x body + * @retval NULL + * @note only applicable if x is body and has yang-spec and is leaf or leaf-list + */ +cg_var * +xml_cv(cxobj *x) +{ + return x->x_cv; +} + +/*! Return (cached) cligen variable value of xml node + * @param[in] x XML node (body and leaf/leaf-list) + * @param[in] cv CLIgen variable containing value of x body + * @retval 0 OK + * @note only applicable if x is body and has yang-spec and is leaf or leaf-list + */ +int +xml_cv_set(cxobj *x, + cg_var *cv) +{ + if (x->x_cv) + cv_free(x->x_cv); + x->x_cv = cv; + return 0; +} + /*! Find an XML node matching name among a parent's children. * * Get first XML node directly under x_up in the xml hierarchy with @@ -1229,6 +1259,8 @@ xml_free(cxobj *x) } if (x->x_childvec) free(x->x_childvec); + if (x->x_cv) + cv_free(x->x_cv); free(x); return 0; } diff --git a/lib/src/clixon_xml_sort.c b/lib/src/clixon_xml_sort.c index 162735f7..ce9c9916 100644 --- a/lib/src/clixon_xml_sort.c +++ b/lib/src/clixon_xml_sort.c @@ -60,14 +60,75 @@ #include "clixon_hash.h" #include "clixon_handle.h" #include "clixon_yang.h" +#include "clixon_yang_type.h" #include "clixon_xml.h" #include "clixon_options.h" #include "clixon_xml_map.h" #include "clixon_xml_sort.h" -/* - * Variables +/*! Get xml body value as cligen variable + * @param[in] x XML node (body and leaf/leaf-list) + * @param[out] cvp Pointer to cligen variable containing value of x body + * @retval 0 OK, cvp contains cv or NULL + * @retval -1 Error + * @note only applicable if x is body and has yang-spec and is leaf or leaf-list + * Move to clixon_xml.c? */ +static int +xml_cv_cache(cxobj *x, + char *body, + cg_var **cvp) +{ + int retval = -1; + cg_var *cv = NULL; + yang_stmt *y; + yang_stmt *yrestype; + enum cv_type cvtype; + int ret; + char *reason=NULL; + int options = 0; + uint8_t fraction = 0; + + if ((cv = xml_cv(x)) != NULL) + goto ok; + if ((y = xml_spec(x)) == NULL) + goto done; + if (yang_type_get(y, NULL, &yrestype, &options, NULL, NULL, &fraction) < 0) + goto done; + yang2cv_type(yrestype->ys_argument, &cvtype); + if (cvtype==CGV_ERR){ + clicon_err(OE_YANG, errno, "yang->cligen type %s mapping failed", + yrestype->ys_argument); + goto done; + } + if ((cv = cv_new(cvtype)) == NULL){ + clicon_err(OE_YANG, errno, "cv_new"); + goto done; + } + if (cvtype == CGV_DEC64) + cv_dec64_n_set(cv, fraction); + + if ((ret = cv_parse1(body, cv, &reason)) < 0){ + clicon_err(OE_YANG, errno, "cv_parse1"); + goto done; + } + if (ret == 0){ + clicon_err(OE_YANG, EINVAL, "cv parse error: %s\n", reason); + goto done; + } + if (xml_cv_set(x, cv) < 0) + goto done; + ok: + *cvp = cv; + cv = NULL; + retval = 0; + done: + if (reason) + free(reason); + if (cv) + cv_free(cv); + return retval; +} /*! Given a child name and an XML object, return yang stmt of child * If no xml parent, find root yang stmt matching name @@ -150,7 +211,9 @@ xml_cmp(const void* arg1, char *b1; char *b2; char *keyname; - + cg_var *cv1; + cg_var *cv2; + assert(x1&&x2); y1 = xml_spec(x1); y2 = xml_spec(x2); @@ -164,7 +227,7 @@ xml_cmp(const void* arg1, } /* Now y1==y2, same Yang spec, can only be list or leaf-list, * But first check exceptions, eg config false or ordered-by user - * otherwuse sort according to key + * otherwise sort according to key */ if (yang_config(y1)==0 || yang_find((yang_node*)y1, Y_ORDERED_BY, "user") != NULL) @@ -175,8 +238,13 @@ xml_cmp(const void* arg1, equal = -1; else if ((b2 = xml_body(x2)) == NULL) equal = 1; - else - equal = strcmp(b1, b2); + else{ + if (xml_cv_cache(x1, b1, &cv1) < 0) + goto done; + if (xml_cv_cache(x2, b2, &cv2) < 0) + goto done; + equal = cv_cmp(cv1, cv2); + } break; case Y_LIST: /* Match with key values * Use Y_LIST cache (see struct yang_stmt) @@ -200,7 +268,10 @@ xml_cmp(const void* arg1, } /*! Compare xml object - * @param[in] yangi Yang order + * @param[in] x XML node to compare with + * @param[in] y The yang spec of x + * @param[in] name Name to compare with x + * @param[in] keyword Yang keyword (stmt type) to compare w x/y * @param[in] keynr Length of keyvec/keyval vector when applicable * @param[in] keyvec Array of of yang key identifiers * @param[in] keyval Array of of yang key values @@ -209,6 +280,7 @@ xml_cmp(const void* arg1, * @retval <0 if arg1 is less than arg2 * @retval >0 if arg1 is greater than arg2 * @see xml_cmp Similar, but for two objects + * @note Does not care about y type of value as xml_cmp */ static int xml_cmp1(cxobj *x, @@ -220,12 +292,15 @@ xml_cmp1(cxobj *x, char **keyval, int *userorder) { - char *b; - int i; - char *keyname; - char *key; - int match = 0; + char *b; + int i; + char *keyname; + char *key; + int match = 0; + /* state data = userorder */ + if (userorder && yang_config(y)==0) + *userorder=1; /* Check if same yang spec (order in yang stmt list) */ switch (keyword){ case Y_CONTAINER: /* Match with name */ @@ -257,6 +332,7 @@ xml_cmp1(cxobj *x, default: break; } + // done: return match; /* should not reach here */ } @@ -603,7 +679,6 @@ match_base_child(cxobj *x0, yang_node *yp; /* yang parent */ *x0cp = NULL; /* init return value */ -#if 1 /* Special case is if yc parent (yp) is choice/case * then find x0 child with same yc even though it does not match lexically * However this will give another y0c != yc @@ -619,7 +694,6 @@ match_base_child(cxobj *x0, *x0cp = x0c; goto ok; /* What to do if not found? */ } -#endif switch (yc->ys_keyword){ case Y_CONTAINER: /* Equal regardless */ case Y_LEAF: /* Equal regardless */ diff --git a/test/test_order.sh b/test/test_order.sh index 1f6d0c47..ad002d27 100755 --- a/test/test_order.sh +++ b/test/test_order.sh @@ -46,7 +46,7 @@ module order-example{ namespace "urn:example:order"; prefix ex; import clixon-example { /* for state callback */ - prefix ex; + prefix ex; } container c{ leaf d{ @@ -82,7 +82,24 @@ module order-example{ } leaf a { type string; - } + } + } + container types{ + description "For testing ordering using other types than strings"; + leaf-list strings{ + type string; + ordered-by system; + } + leaf-list ints{ + type int32; + ordered-by system; + } + leaf-list decs{ + type decimal64{ + fraction-digits 3; + } + ordered-by system; + } } } EOF @@ -184,6 +201,19 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' '^cbarbfooafie]]>]]>$' +#-- order by type rather than strings. +# there are three lists: strings, ints, and decimal64 +# the strings is there for comparison +new "add type ordered entries" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' +1021 +1021 +10.02.01.0 +]]>]]>' "^]]>]]>$" + +new "get type ordered entries" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' '^110212101.02.010.0]]>]]>$' + if [ $BE -eq 0 ]; then exit # BE fi