diff --git a/CHANGELOG.md b/CHANGELOG.md index 1064a3f4..cfc98c42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ ## 4.3.0 (Expected: December 2019) ### Minor changes +* C-API: Added `xpath_first_localonly()` as an xpath function that skips prefix and namespace checks. +* Added experimental code for optizing XPath search using binary search. + * Enable with XPATH_LIST_OPTIMIZE +* Removed most assert.h includes * Added "canonical" global namespace context: `nsctx_global` * This is a normalized XML prefix:namespace pair vector computed from all loaded Yang modules. Useful when writing XML and XPATH expressions in callbacks. * Get it with `clicon_nsctx_global_get(h)` @@ -17,7 +21,9 @@ * Optional yang files are loaded only if configured with `--enable-optyangs` (flipped logic and changed from `disable-stdyangs`). NOTE: you must do this to run examples and tests. * Optional yang files can be installed in a separate dir with `--with-opt-yang-installdir=DIR` (renamed from `with-std-yang-installdir`) * C-API - * Added namespace-context parameter `nsc` to `xpath_first` and `xpath_vec`, (`xpath_vec_nsc` and xpath_first_nsc` are removed). + * Changed `clicon_rpc_generate_error(msg, xerr)` to `clicon_rpc_generate_error(xerr, msg, arg)` + * Added namespace-context parameter `nsc` to `xpath_first` and `xpath_vec`, (`xpath_vec_nsc` and +xpath_first_nsc` are removed). * Added clicon_handle as parameter to all `clicon_connect_` functions to get better error message * Added nsc parameter to `xmldb_get()` * The multi-namespace augment state may rearrange the XML namespace attributes. diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c index a22e8360..890be1d5 100644 --- a/apps/backend/backend_commit.c +++ b/apps/backend/backend_commit.c @@ -55,7 +55,6 @@ #include #include #include -#include #include /* cligen */ diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index b1091f3d..e0d70a2c 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -216,8 +216,8 @@ nacm_load_external(clicon_handle h) goto done; retval = 0; done: - if (yspec) /* The clixon yang-spec is not used after this */ - yspec_free(yspec); + // XXX if (yspec) /* The clixon yang-spec is not used after this */ + // XXX yspec_free(yspec); if (f) fclose(f); return retval; diff --git a/apps/backend/backend_startup.c b/apps/backend/backend_startup.c index 9314968c..55c0aa1c 100644 --- a/apps/backend/backend_startup.c +++ b/apps/backend/backend_startup.c @@ -55,7 +55,6 @@ #include #include #include -#include #include /* cligen */ diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index 48f92e34..c55e7358 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -58,7 +58,6 @@ #include #include #include -#include /* cligen */ #include @@ -70,7 +69,6 @@ #include "cli_common.h" - /*! Register log notification stream * @param[in] h Clicon handle * @param[in] stream Event stream. CLICON is predefined, others are application-defined @@ -715,13 +713,13 @@ compare_dbs(clicon_handle h, if (clicon_rpc_get_config(h, NULL, "running", "/", NULL, &xc1) < 0) goto done; if ((xerr = xpath_first(xc1, NULL, "/rpc-error")) != NULL){ - clicon_rpc_generate_error("Get configuration", xerr); + clicon_rpc_generate_error(xerr, "Get configuration", NULL); goto done; } if (clicon_rpc_get_config(h, NULL, "candidate", "/", NULL, &xc2) < 0) goto done; if ((xerr = xpath_first(xc2, NULL, "/rpc-error")) != NULL){ - clicon_rpc_generate_error("Get configuration", xerr); + clicon_rpc_generate_error(xerr, "Get configuration", NULL); goto done; } if (compare_xmls(xc1, xc2, astext) < 0) /* astext? */ @@ -885,7 +883,7 @@ save_config_file(clicon_handle h, goto done; } if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ - clicon_rpc_generate_error("Get configuration", xerr); + clicon_rpc_generate_error(xerr, "Get configuration", NULL); goto done; } /* get-config returns a tree. Save as tree so it can be used @@ -1225,7 +1223,7 @@ cli_copy_config(clicon_handle h, if (clicon_rpc_get_config(h, NULL, db, cbuf_get(cb), nsc, &x1) < 0) goto done; if ((xerr = xpath_first(x1, NULL, "/rpc-error")) != NULL){ - clicon_rpc_generate_error("Get configuration", xerr); + clicon_rpc_generate_error(xerr, "Get configuration", NULL); goto done; } diff --git a/apps/cli/cli_generate.c b/apps/cli/cli_generate.c index 5e3381be..6849b63c 100644 --- a/apps/cli/cli_generate.c +++ b/apps/cli/cli_generate.c @@ -48,7 +48,6 @@ #include #include #include -#include #include #include #include @@ -76,6 +75,8 @@ This is an example yang module: module m { container x { + namespace "urn:example:m"; + prefix m; list m1 { key "a"; leaf a { @@ -88,12 +89,11 @@ module m { } } -You can see which CLISPEC it generates via clixon_cli -D 1: -Jan 2 11:17:58: yang2cli: buf -} x,cli_set("/x");{ - m1 (|),cli_set("/x/m1/%s"); +You can see which CLISPEC it generates via clixon_cli -D 2: + x,cli_set("/example:x");{ + m1 a (|),overwrite_me("/example:x/m1=%s/"); { - b (|),cli_set("/x/m1/%s/b"); + b (|),overwrite_me("/example:x/m1=%s/b"); } } */ diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index 2beb7867..9f0510b3 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -53,7 +53,6 @@ #include #include #include -#include #include #include diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c index 964c4497..c8886019 100644 --- a/apps/cli/cli_plugin.c +++ b/apps/cli/cli_plugin.c @@ -464,7 +464,6 @@ cli_handler_err(FILE *f) return 0; } - /*! Evaluate a matched command * @param[in] h Clicon handle * @param[in] cmd The command string diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index c0897b61..90af1844 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -59,7 +59,6 @@ #include #include #include -#include /* cligen */ #include @@ -158,7 +157,7 @@ expand_dbvar(void *h, if (clicon_rpc_get_config(h, NULL, dbstr, xpath, nsc, &xt) < 0) /* XXX */ goto done; if ((xe = xpath_first(xt, NULL, "/rpc-error")) != NULL){ - clicon_rpc_generate_error("Get configuration", xe); + clicon_rpc_generate_error(xe, "Get configuration", NULL); goto ok; } xcur = xt; /* default top-of-tree */ @@ -175,7 +174,7 @@ expand_dbvar(void *h, if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 0, &xbot, &y, &xerr)) < 0) goto done; if (ret == 0){ - clicon_rpc_generate_error("Expand datastore symbol", xerr); + clicon_rpc_generate_error(xerr, "Expand datastore symbol", NULL); goto done; } } @@ -487,7 +486,7 @@ cli_show_config1(clicon_handle h, goto done; } if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ - clicon_rpc_generate_error("Get configuration", xerr); + clicon_rpc_generate_error(xerr, "Get configuration", NULL); goto done; } if ((yspec = clicon_dbspec_yang(h)) == NULL){ @@ -635,7 +634,7 @@ show_conf_xpath(clicon_handle h, if (clicon_rpc_get_config(h, NULL, str, xpath, nsc, &xt) < 0) goto done; if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ - clicon_rpc_generate_error("Get configuration", xerr); + clicon_rpc_generate_error(xerr, "Get configuration", NULL); goto done; } @@ -738,7 +737,7 @@ cli_show_auto1(clicon_handle h, } if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ - clicon_rpc_generate_error("Get configuration", xerr); + clicon_rpc_generate_error(xerr, "Get configuration", NULL); goto done; } if ((xp = xpath_first(xt, nsc, "%s", xpath)) != NULL) diff --git a/apps/netconf/netconf_filter.c b/apps/netconf/netconf_filter.c index 59540559..740b95c5 100644 --- a/apps/netconf/netconf_filter.c +++ b/apps/netconf/netconf_filter.c @@ -53,7 +53,6 @@ #include #include #include -#include /* cligen */ #include diff --git a/apps/netconf/netconf_lib.c b/apps/netconf/netconf_lib.c index 279d8b5e..ab63825a 100644 --- a/apps/netconf/netconf_lib.c +++ b/apps/netconf/netconf_lib.c @@ -54,7 +54,6 @@ #include #include #include -#include /* cligen */ #include diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index e526172f..2844a67d 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -54,7 +54,6 @@ #include #include #include -#include #include #include #include diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c index 4e464846..8f3315f5 100644 --- a/apps/netconf/netconf_rpc.c +++ b/apps/netconf/netconf_rpc.c @@ -60,7 +60,6 @@ #include #include #include -#include #include /* cligen */ diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index 0545cb72..5b5e96db 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -106,7 +106,6 @@ Mapping netconf error-tag -> status code #include #include #include -#include #include #include #include diff --git a/apps/restconf/restconf_methods_get.c b/apps/restconf/restconf_methods_get.c index 124b35b1..270d56f3 100644 --- a/apps/restconf/restconf_methods_get.c +++ b/apps/restconf/restconf_methods_get.c @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include diff --git a/apps/restconf/restconf_methods_post.c b/apps/restconf/restconf_methods_post.c index 40763d88..2592ebd6 100644 --- a/apps/restconf/restconf_methods_post.c +++ b/apps/restconf/restconf_methods_post.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include diff --git a/example/main/example_backend.c b/example/main/example_backend.c index 95542477..b8db088d 100644 --- a/example/main/example_backend.c +++ b/example/main/example_backend.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include diff --git a/example/main/example_cli.c b/example/main/example_cli.c index 330c38e4..52c359c7 100644 --- a/example/main/example_cli.c +++ b/example/main/example_cli.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -110,7 +109,7 @@ example_client_rpc(clicon_handle h, if (clicon_rpc_netconf_xml(h, xrpc, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error("Get configuration", xerr); + clicon_rpc_generate_error(xerr, "Get configuration", NULL); goto done; } /* Print result */ diff --git a/example/main/example_netconf.c b/example/main/example_netconf.c index 9d2dd32a..b48491ae 100644 --- a/example/main/example_netconf.c +++ b/example/main/example_netconf.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include diff --git a/include/clixon_custom.h b/include/clixon_custom.h index c60b8101..2da8699c 100644 --- a/include/clixon_custom.h +++ b/include/clixon_custom.h @@ -58,3 +58,9 @@ * on the top-level for the modules involved in the netconf operation. */ #define IDENTITYREF_KLUDGE + +/*! Optimize special list key searches in XPATH finds + * Identify xpaths that search for exactly a list key, eg: "y[k=3]" and then call + * binary search. This only works if "y" has proper yang binding and is sorted by system + */ +#undef XPATH_LIST_OPTIMIZE diff --git a/lib/clixon/clixon_proto_client.h b/lib/clixon/clixon_proto_client.h index bf1ffc0a..b4329fe0 100644 --- a/lib/clixon/clixon_proto_client.h +++ b/lib/clixon/clixon_proto_client.h @@ -44,7 +44,7 @@ int clicon_rpc_msg(clicon_handle h, struct clicon_msg *msg, cxobj **xret0, int *sock0); int clicon_rpc_netconf(clicon_handle h, char *xmlst, cxobj **xret, int *sp); int clicon_rpc_netconf_xml(clicon_handle h, cxobj *xml, cxobj **xret, int *sp); -int clicon_rpc_generate_error(const char *format, cxobj *xerr); +int clicon_rpc_generate_error(cxobj *xerr, const char *fmt, const char *arg); int clicon_rpc_get_config(clicon_handle h, char *username, char *db, char *xpath, cvec *nsc, cxobj **xret); int clicon_rpc_edit_config(clicon_handle h, char *db, enum operation_type op, char *xml); diff --git a/lib/clixon/clixon_string.h b/lib/clixon/clixon_string.h index 10ff73a3..2a923733 100644 --- a/lib/clixon/clixon_string.h +++ b/lib/clixon/clixon_string.h @@ -93,6 +93,8 @@ int clicon_str2int(const map_str2int *mstab, char *str); int clicon_str2int_search(const map_str2int *mstab, char *str, int upper); int nodeid_split(char *nodeid, char **prefix, char **id); char *clixon_trim(char *str); +int clicon_strcmp(char *s1, char *s2); + #ifndef HAVE_STRNDUP char *clicon_strndup (const char *, size_t); #endif /* ! HAVE_STRNDUP */ diff --git a/lib/clixon/clixon_xml_sort.h b/lib/clixon/clixon_xml_sort.h index dc0dfc26..83fcac63 100644 --- a/lib/clixon/clixon_xml_sort.h +++ b/lib/clixon/clixon_xml_sort.h @@ -45,6 +45,6 @@ int xml_sort(cxobj *x0, void *arg); int xml_insert(cxobj *xp, cxobj *xc, enum insert_type ins, char *key_val, cvec *nsckey); int xml_sort_verify(cxobj *x, void *arg); int match_base_child(cxobj *x0, cxobj *x1c, yang_stmt *yc, cxobj **x0cp); -cxobj *xml_binsearch(cxobj *xp, char *name, char *keyname, char *keyval); +int xml_binsearch(cxobj *xp, char *name, char *keyname, char *keyval, cxobj **xret); #endif /* _CLIXON_XML_SORT_H */ diff --git a/lib/clixon/clixon_xpath.h b/lib/clixon/clixon_xpath.h index 1b2dfbb9..5c7a76dc 100644 --- a/lib/clixon/clixon_xpath.h +++ b/lib/clixon/clixon_xpath.h @@ -101,10 +101,13 @@ enum xp_type{ }; /*! XPATH Parsing generates a tree of nodes that is later traversed + * That is, a tree-structured XPATH. + * Note that the structure follows XPATH 1.0 closely. The drawback wit this is that the tree gets + * very deep very quickly, even for simple XPATHs. */ struct xpath_tree{ enum xp_type xs_type; - int xs_int; /* step-> axis-type */ + int xs_int; /* step-> axis_type */ double xs_double; char *xs_strnr; /* original string xs_double: numeric value */ char *xs_s0; @@ -122,9 +125,10 @@ char* xpath_tree_int2str(int nodetype); int xpath_tree_print_cb(cbuf *cb, xpath_tree *xs); int xpath_tree_print(FILE *f, xpath_tree *xs); int xpath_tree2cbuf(xpath_tree *xs, cbuf *xpathcb); +int xpath_tree_eq(xpath_tree *xt1, xpath_tree *xt2, cvec *match); int xpath_tree_free(xpath_tree *xs); int xpath_parse(char *xpath, xpath_tree **xptree); -int xpath_vec_ctx(cxobj *xcur, cvec *nsc, char *xpath, xp_ctx **xrp); +int xpath_vec_ctx(cxobj *xcur, cvec *nsc, char *xpath, int localonly, xp_ctx **xrp); #if defined(__GNUC__) && __GNUC__ >= 3 int xpath_vec_bool(cxobj *xcur, cvec *nsc, char *xpformat, ...) __attribute__ ((format (printf, 3, 4))); @@ -144,9 +148,11 @@ int xpath_vec_flag(cxobj *xcur, cvec *nsc, char *xpformat, uint16_t flags, */ #if defined(__GNUC__) && __GNUC__ >= 3 cxobj *xpath_first(cxobj *xcur, cvec *nsc, char *xpformat, ...) __attribute__ ((format (printf, 3, 4))); +cxobj *xpath_first_localonly(cxobj *xcur, char *xpformat, ...) __attribute__ ((format (printf, 2, 3))); int xpath_vec(cxobj *xcur, cvec *nsc, char *xpformat, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 3, 6))); #else cxobj *xpath_first(cxobj *xcur, cvec *nsc, char *xpformat, ...); +cxobj *xpath_first_localonly(cxobj *xcur, char *xpformat, ...); int xpath_vec(cxobj *xcur, cvec *nsc, char *xpformat, cxobj ***vec, size_t *veclen, ...); #endif diff --git a/lib/clixon/clixon_xpath_ctx.h b/lib/clixon/clixon_xpath_ctx.h index 1a931da0..b1199af4 100644 --- a/lib/clixon/clixon_xpath_ctx.h +++ b/lib/clixon/clixon_xpath_ctx.h @@ -95,7 +95,8 @@ extern const map_str2int ctxmap[]; 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 ctx_print_cb(cbuf *cb, xp_ctx *xc, int indent, char *str); +int ctx_print(FILE *f, xp_ctx *xc, char *str); int ctx2boolean(xp_ctx *xc); int ctx2string(xp_ctx *xc, char **str0); int ctx2number(xp_ctx *xc, double *n0); diff --git a/lib/src/clixon_datastore_write.c b/lib/src/clixon_datastore_write.c index 95eb5379..006fe45e 100644 --- a/lib/src/clixon_datastore_write.c +++ b/lib/src/clixon_datastore_write.c @@ -48,7 +48,6 @@ #include #include #include -#include #include #include diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c index d4fb484c..5c63e608 100644 --- a/lib/src/clixon_json.c +++ b/lib/src/clixon_json.c @@ -49,7 +49,6 @@ #include #include #include -#include /* cligen */ #include diff --git a/lib/src/clixon_json_parse.y b/lib/src/clixon_json_parse.y index 5a5503da..2994c679 100644 --- a/lib/src/clixon_json_parse.y +++ b/lib/src/clixon_json_parse.y @@ -113,7 +113,6 @@ object. #include #include #include -#include #include #include #include diff --git a/lib/src/clixon_log.c b/lib/src/clixon_log.c index 8b2d3131..74c19b36 100644 --- a/lib/src/clixon_log.c +++ b/lib/src/clixon_log.c @@ -48,7 +48,6 @@ #include #include #include -#include #include #include diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c index 67233bc7..54b9072d 100644 --- a/lib/src/clixon_netconf_lib.c +++ b/lib/src/clixon_netconf_lib.c @@ -47,7 +47,6 @@ #include #include #include -#include /* cligen */ #include @@ -1360,13 +1359,13 @@ netconf_err2cb(cxobj *xerr, int retval = -1; cxobj *x; - if ((x=xpath_first(xerr, NULL, "error-type"))!=NULL) + if ((x=xpath_first(xerr, NULL, "//error-type"))!=NULL) cprintf(cberr, "%s ", xml_body(x)); - if ((x=xpath_first(xerr, NULL, "error-tag"))!=NULL) + if ((x=xpath_first(xerr, NULL, "//error-tag"))!=NULL) cprintf(cberr, "%s ", xml_body(x)); - if ((x=xpath_first(xerr, NULL, "error-message"))!=NULL) + if ((x=xpath_first(xerr, NULL, "//error-message"))!=NULL) cprintf(cberr, "%s ", xml_body(x)); - if ((x=xpath_first(xerr, NULL, "error-info"))!=NULL) + if ((x=xpath_first(xerr, NULL, "//error-info"))!=NULL) clicon_xml2cbuf(cberr, xml_child_i(x,0), 0, 0, -1); retval = 0; return retval; diff --git a/lib/src/clixon_proto.c b/lib/src/clixon_proto.c index 6c1dbd04..51870ad7 100644 --- a/lib/src/clixon_proto.c +++ b/lib/src/clixon_proto.c @@ -47,7 +47,6 @@ #include #include #include -#include #include #include #include diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index 5d4b8dba..26e38b46 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -225,13 +225,18 @@ clicon_rpc_netconf_xml(clicon_handle h, return retval; } -/*! Generate and log clicon error function call from Netconf error message - * @param[in] prefix Print this string (if given) before: ": " - * @param[in] xerr Netconf error message on the level: +/*! Generate clicon error from Netconf error message + * + * Get a text error message from netconf error message and generate error on the form: + * : "": or : + * @param[in] xerr Netconf error xml tree on the form: + * @param[in] format Format string + * @param[in] arg String argument to format (optional) */ int -clicon_rpc_generate_error(const char *prefix, - cxobj *xerr) +clicon_rpc_generate_error(cxobj *xerr, + const char *msg, + const char *arg) { int retval = -1; cbuf *cb = NULL; @@ -242,10 +247,12 @@ clicon_rpc_generate_error(const char *prefix, } if (netconf_err2cb(xerr, cb) < 0) goto done; - if (prefix) - clicon_log(LOG_ERR, "%s: %s", prefix, cbuf_get(cb)); - else - clicon_log(LOG_ERR, "%s", cbuf_get(cb)); + if (arg){ + cprintf(cb, "%s: \"%s\": ", msg, arg); + clicon_err(OE_CFG, EINVAL, "%s", cbuf_get(cb)); + } + else /* XXX: should really be clicon_err but dont do it yet for backward compatability */ + clicon_log(LOG_ERR, "%s: %s", msg, cbuf_get(cb)); retval = 0; done: if (cb) @@ -273,7 +280,7 @@ clicon_rpc_generate_error(const char *prefix, * if (clicon_rpc_get_config(h, NULL, "running", "/hello/world", nsc, &xt) < 0) * err; * if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ - * clicon_rpc_generate_error("", xerr); + * clicon_rpc_generate_error(xerr, "msg", "/hello/world"); * err; * } * if (xt) @@ -395,7 +402,7 @@ clicon_rpc_edit_config(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error("Editing configuration", xerr); + clicon_rpc_generate_error(xerr, "Editing configuration", NULL); goto done; } retval = 0; @@ -442,7 +449,7 @@ clicon_rpc_copy_config(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error("Copying configuration", xerr); + clicon_rpc_generate_error(xerr, "Copying configuration", NULL); goto done; } retval = 0; @@ -482,7 +489,7 @@ clicon_rpc_delete_config(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error("Deleting configuration", xerr); + clicon_rpc_generate_error(xerr, "Deleting configuration", NULL); goto done; } retval = 0; @@ -518,7 +525,7 @@ clicon_rpc_lock(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error("Locking configuration", xerr); + clicon_rpc_generate_error(xerr, "Locking configuration", NULL); goto done; } retval = 0; @@ -553,7 +560,7 @@ clicon_rpc_unlock(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error("Configuration unlock", xerr); + clicon_rpc_generate_error(xerr, "Configuration unlock", NULL); goto done; } retval = 0; @@ -587,7 +594,7 @@ clicon_rpc_unlock(clicon_handle h, * if (clicon_rpc_get(h, "/hello/world", nsc, CONTENT_ALL, -1, &xt) < 0) * err; * if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ - * clicon_rpc_generate_error(xerr); + * clicon_rpc_generate_error(xerr, "clicon_rpc_get", NULL); * err; * } * if (xt) @@ -694,7 +701,7 @@ clicon_rpc_close_session(clicon_handle h) if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error("Close session", xerr); + clicon_rpc_generate_error(xerr, "Close session", NULL); goto done; } retval = 0; @@ -730,7 +737,7 @@ clicon_rpc_kill_session(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error("Kill session", xerr); + clicon_rpc_generate_error(xerr, "Kill session", NULL); goto done; } retval = 0; @@ -765,7 +772,7 @@ clicon_rpc_validate(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error(CLIXON_ERRSTR_VALIDATE_FAILED, xerr); + clicon_rpc_generate_error(xerr, CLIXON_ERRSTR_VALIDATE_FAILED, NULL); goto done; } retval = 0; @@ -798,7 +805,7 @@ clicon_rpc_commit(clicon_handle h) if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error(CLIXON_ERRSTR_COMMIT_FAILED, xerr); + clicon_rpc_generate_error(xerr, CLIXON_ERRSTR_COMMIT_FAILED, NULL); goto done; } retval = 0; @@ -831,7 +838,7 @@ clicon_rpc_discard_changes(clicon_handle h) if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error("Discard changes", xerr); + clicon_rpc_generate_error(xerr, "Discard changes", NULL); goto done; } retval = 0; @@ -877,7 +884,7 @@ clicon_rpc_create_subscription(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, s0) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error("Create subscription", xerr); + clicon_rpc_generate_error(xerr, "Create subscription", NULL); goto done; } retval = 0; @@ -912,7 +919,7 @@ clicon_rpc_debug(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error("Debug",xerr); + clicon_rpc_generate_error(xerr, "Debug", NULL); goto done; } if (xpath_first(xret, NULL, "//rpc-reply/ok") == NULL){ @@ -955,7 +962,7 @@ clicon_hello_req(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error("Hello", xerr); + clicon_rpc_generate_error(xerr, "Hello", NULL); goto done; } if ((x = xpath_first(xret, NULL, "hello/session-id")) == NULL){ diff --git a/lib/src/clixon_string.c b/lib/src/clixon_string.c index cfd07b0e..be722930 100644 --- a/lib/src/clixon_string.c +++ b/lib/src/clixon_string.c @@ -692,6 +692,26 @@ clixon_trim(char *str) return s; } +/*! check string equals (NULL is equal) + * @param[in] s1 String 1 + * @param[in] s2 String 2 + * @retval 0 Equal + * @retval -1 Not equal + * @retval 1 Not equal + */ +int +clicon_strcmp(char *s1, + char *s2) +{ + if (s1 == NULL && s2 == NULL) + return 0; + if (s1 == NULL) /* empty string first */ + return -1; + if (s2 == NULL) + return 1; + return strcmp(s1, s2); +} + /*! strndup() for systems without it, such as xBSD */ #ifndef HAVE_STRNDUP @@ -717,7 +737,6 @@ clicon_strndup(const char *str, #endif /* ! HAVE_STRNDUP */ - /* * Turn this on for uni-test programs * Usage: clixon_string join diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index add0961b..c1a3c22c 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -1059,8 +1059,8 @@ xml_find(cxobj *x_up, while ((x = xml_child_each(x_up, x, -1)) != NULL) if (strcmp(name, xml_name(x)) == 0) - return x; - return NULL; + break; /* x is set */ + return x; } /*! Append xc as child to xp. Remove xc from previous parent. diff --git a/lib/src/clixon_xml_changelog.c b/lib/src/clixon_xml_changelog.c index 833ee800..4c228a90 100644 --- a/lib/src/clixon_xml_changelog.c +++ b/lib/src/clixon_xml_changelog.c @@ -88,7 +88,7 @@ changelog_rename(clicon_handle h, clicon_err(OE_XML, 0, "tag required"); goto done; } - if (xpath_vec_ctx(xw, nsc, tag, &xctx) < 0) + if (xpath_vec_ctx(xw, nsc, tag, 0, &xctx) < 0) goto done; if (ctx2string(xctx, &str) < 0) goto done; @@ -258,7 +258,7 @@ changelog_op(clicon_handle h, xw = wvec[i]; /* If 'when' exists and is false, skip this target */ if (whenxpath){ - if (xpath_vec_ctx(xw, nsc, whenxpath, &xctx) < 0) + if (xpath_vec_ctx(xw, nsc, whenxpath, 0, &xctx) < 0) goto done; if ((ret = ctx2boolean(xctx)) < 0) goto done; diff --git a/lib/src/clixon_xml_nsctx.c b/lib/src/clixon_xml_nsctx.c index 6cabf39a..b85b6dd8 100644 --- a/lib/src/clixon_xml_nsctx.c +++ b/lib/src/clixon_xml_nsctx.c @@ -51,7 +51,6 @@ #include #include #include -#include /* cligen */ #include diff --git a/lib/src/clixon_xml_sort.c b/lib/src/clixon_xml_sort.c index 6caaa1ef..368c1106 100644 --- a/lib/src/clixon_xml_sort.c +++ b/lib/src/clixon_xml_sort.c @@ -290,7 +290,14 @@ xml_cmp(cxobj *x1, goto done; if (xml_cv_cache(x2, &cv2) < 0) /* error case */ goto done; - equal = cv_cmp(cv1, cv2); + if (cv1 != NULL && cv2 != NULL) + equal = cv_cmp(cv1, cv2); + else if (cv1 == NULL && cv2 == NULL) + equal = 0; + else if (cv1 == NULL) + equal = -1; + else + equal = 1; } break; case Y_LIST: /* Match with key values @@ -309,10 +316,9 @@ xml_cmp(cxobj *x1, else{ if (xml_cv_cache(x1b, &cv1) < 0) /* error case */ goto done; - // assert(cv1); if (xml_cv_cache(x2b, &cv2) < 0) /* error case */ goto done; - // assert(cv2); + assert(cv1 && cv2); if ((equal = cv_cmp(cv1, cv2)) != 0) goto done; } @@ -831,15 +837,26 @@ match_base_child(cxobj *x0, } /*! Experimental API for binary search + * @param[in] xp Parent xml node. + * @param[in] name Node name of child (list) + * @param[in] keyname Yang list key name + * @param[in] keyval XML key value + * @param[out] xret Found XML object, NULL if not founs + * @retval 0 OK, see xret + * @retval -1 Error + * Multiple keys? + * Can extend to leaf-list? */ -cxobj * -xml_binsearch(cxobj *xp, - char *name, - char *keyname, - char *keyval) +int +xml_binsearch(cxobj *xp, + char *name, + char *keyname, + char *keyval, + cxobj **xretp) { + int retval = -1; cxobj *xc = NULL; - cxobj *xa = NULL; + // cxobj *xa = NULL; cxobj *xret = NULL; yang_stmt *yp; yang_stmt *yc; @@ -852,15 +869,22 @@ xml_binsearch(cxobj *xp, clicon_err(OE_YANG, ENOENT, "yang not found"); goto done; } - if ((xc = xml_new(name, xp, yc)) == NULL) + if (xml_parse_va(&xc, yc, "<%s><%s>%s", name, keyname, keyval, keyname, name) < 0) goto done; - if ((xa = xml_new(keyname, xc, NULL)) == NULL) + if (xml_rootchild(xc, 0, &xc) < 0) goto done; - if (xml_value_set(xa, keyval) < 0) +#if 0 + if (xml_apply0(xc, CX_ELMNT, xml_spec_populate, ys_spec(yc)) < 0) goto done; +#else + if (xml_spec_set(xc, yc) < 0) + goto done; +#endif xret = xml_search(xp, xc, yc); + *xretp = xret; /* XXX possibly use *xretp directly */ + retval = 0; done: if (xc) xml_free(xc); - return xret; + return retval; } diff --git a/lib/src/clixon_xpath.c b/lib/src/clixon_xpath.c index d6d77303..60f4a07d 100644 --- a/lib/src/clixon_xpath.c +++ b/lib/src/clixon_xpath.c @@ -66,9 +66,9 @@ #include #include #include -#include #include #include +#include /* cligen */ #include @@ -306,6 +306,84 @@ xpath_tree2cbuf(xpath_tree *xs, return retval; } + +/*! Check if two xpath-trees (parsed xpaths) ar equal + * + * @param[in] xt1 XPath parse 1 + * @param[in] xt2 XPath parse 2 + * @param[in,out] mv Match vector consisting of pairs + * @retval 0 If equal + * @retval -1 If not equal + * The function returns 0 if the two trees are equal, otherwise -1 + * But the "mv" parameter is a way to add pattern matching to some variables + * On input, mv contains a vector of CLIgen string variables with names that may match string + * fields s0 and s1 in an xpath_tree. If the names match the values of s0 or s1, the field is + * considered equal and is stored as value in the CLIgen variable vector. + * Example: + * xt1: _x[_y='_z'] + * xt2: y[k='3'] + * match[in]: {_x, _y, _z} + * match[out]: {_x:y, _y:k, _z:3} + */ +int +xpath_tree_eq(xpath_tree *xt1, + xpath_tree *xt2, + cvec *match) +{ + int retval = -1; /* not equal */ + xpath_tree *xc1; + xpath_tree *xc2; + cg_var *cv; + + /* node itself */ + if (xt1->xs_type != xt2->xs_type) + goto neq; + if (xt1->xs_int != xt2->xs_int) + goto neq; + if (xt1->xs_double != xt2->xs_double) + goto neq; + if (clicon_strcmp(xt1->xs_s0, xt2->xs_s0)){ + if (xt1->xs_s0 && xt2->xs_s0 && + (cv = cvec_find(match, xt1->xs_s0)) != NULL){ + cv_string_set(cv, xt2->xs_s0); + goto ok; + } + goto neq; + } + if (clicon_strcmp(xt1->xs_s1, xt2->xs_s1)){ + if (xt1->xs_s1 && xt2->xs_s1 && + (cv = cvec_find(match, xt1->xs_s1)) != NULL){ + cv_string_set(cv, xt2->xs_s1); + goto ok; + } + goto neq; + } + xc1 = xt1->xs_c0; + xc2 = xt2->xs_c0; + if (xc1 == NULL && xc2 == NULL) + ; + else{ + if (xc1 == NULL || xc2 == NULL) + goto neq; + if (xpath_tree_eq(xc1, xc2, match)) + goto neq; + } + xc1 = xt1->xs_c1; + xc2 = xt2->xs_c1; + if (xc1 == NULL && xc2 == NULL) + ; + else{ + if (xc1 == NULL || xc2 == NULL) + goto neq; + if (xpath_tree_eq(xc1, xc2, match)) + goto neq; + } + ok: + retval = 0; /* equal */ + neq: + return retval; +} + /*! Free a xpath_tree * @param[in] xs XPATH tree * @see xpath_parse creates a xpath_tree @@ -387,12 +465,13 @@ xpath_parse(char *xpath, * @param[in] xcur XML-tree where to search * @param[in] nsc External XML namespace context, or NULL * @param[in] xpath String with XPATH 1.0 syntax + * @param[in] localonly Skip prefix and namespace tests (non-standard) * @param[out] xrp Return XPATH context * @retval 0 OK * @retval -1 Error * @code * xp_ctx *xc = NULL; - * if (xpath_vec_ctx(x, NULL, xpath, &xc) < 0) + * if (xpath_vec_ctx(x, NULL, xpath, 0, &xc) < 0) * err; * if (xc) * ctx_free(xc); @@ -402,6 +481,7 @@ int xpath_vec_ctx(cxobj *xcur, cvec *nsc, char *xpath, + int localonly, xp_ctx **xrp) { int retval = -1; @@ -415,7 +495,7 @@ xpath_vec_ctx(cxobj *xcur, xc.xc_initial = xcur; if (cxvec_append(xcur, &xc.xc_nodeset, &xc.xc_size) < 0) goto done; - if (xp_eval(&xc, xptree, nsc, xrp) < 0) + if (xp_eval(&xc, xptree, nsc, localonly, xrp) < 0) goto done; if (xc.xc_nodeset){ free(xc.xc_nodeset); @@ -475,7 +555,66 @@ xpath_first(cxobj *xcur, goto done; } va_end(ap); - if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0) + if (xpath_vec_ctx(xcur, nsc, xpath, 0, &xr) < 0) + goto done; + if (xr && xr->xc_type == XT_NODESET && xr->xc_size) + cx = xr->xc_nodeset[0]; + done: + if (xr) + ctx_free(xr); + if (xpath) + free(xpath); + return cx; +} + +/*! XPath nodeset function where prefixes are skipped, only first matching is returned + * + * Reason for skipping prefix/namespace check may be with incomplete tree, for example. + * @param[in] xcur XML tree where to search + * @param[in] xpformat Format string for XPATH syntax + * @retval xml-tree XML tree of first match + * @retval NULL Error or not found + * + * @code + * cxobj *x; + * cvec *nsc; // namespace context + * if ((x = xpath_first_localonly(xtop, "//symbol/foo")) != NULL) { + * ... + * } + * @endcode + * @note the returned pointer points into the original tree so should not be freed after use. + * @note return value does not see difference between error and not found + * @note Prefixes and namespaces are ignored so this is NOT according to standard + * @see also xpath_first. + */ +cxobj * +xpath_first_localonly(cxobj *xcur, + char *xpformat, + ...) +{ + cxobj *cx = NULL; + va_list ap; + size_t len; + char *xpath = NULL; + xp_ctx *xr = NULL; + + va_start(ap, xpformat); + len = vsnprintf(NULL, 0, xpformat, ap); + va_end(ap); + /* allocate a message string exactly fitting the message length */ + if ((xpath = malloc(len+1)) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + /* second round: compute write message from reason and args */ + va_start(ap, xpformat); + if (vsnprintf(xpath, len+1, xpformat, ap) < 0){ + clicon_err(OE_UNIX, errno, "vsnprintf"); + va_end(ap); + goto done; + } + va_end(ap); + if (xpath_vec_ctx(xcur, NULL, xpath, 1, &xr) < 0) goto done; if (xr && xr->xc_type == XT_NODESET && xr->xc_size) cx = xr->xc_nodeset[0]; @@ -541,7 +680,7 @@ xpath_vec(cxobj *xcur, va_end(ap); *vec=NULL; *veclen = 0; - if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0) + if (xpath_vec_ctx(xcur, nsc, xpath, 0, &xr) < 0) goto done; if (xr && xr->xc_type == XT_NODESET){ *vec = xr->xc_nodeset; @@ -616,7 +755,7 @@ xpath_vec_flag(cxobj *xcur, va_end(ap); *vec=NULL; *veclen = 0; - if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0) + if (xpath_vec_ctx(xcur, nsc, xpath, 0, &xr) < 0) goto done; if (xr && xr->xc_type == XT_NODESET){ for (i=0; ixc_size; i++){ @@ -672,7 +811,7 @@ xpath_vec_bool(cxobj *xcur, goto done; } va_end(ap); - if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0) + if (xpath_vec_ctx(xcur, nsc, xpath, 0, &xr) < 0) goto done; if (xr) retval = ctx2boolean(xr); diff --git a/lib/src/clixon_xpath_ctx.c b/lib/src/clixon_xpath_ctx.c index cf8d6a0b..6a2fb5ae 100644 --- a/lib/src/clixon_xpath_ctx.c +++ b/lib/src/clixon_xpath_ctx.c @@ -41,7 +41,6 @@ #include #include #include -#include #include #include #include /* NaN */ @@ -113,21 +112,28 @@ ctx_dup(xp_ctx *xc0) return xc; } -/*! Print XPATH context */ +/*! Print XPATH context to CLIgen buf + * @param[in] cb CLIgen buf to print to + * @param[in] xc XPATH evaluation context + * @param[in] ind Indentation margin + * @param[in] str Prefix string in printout + + + */ int -ctx_print(cbuf *cb, - int id, - xp_ctx *xc, - char *str) +ctx_print_cb(cbuf *cb, + xp_ctx *xc, + int ind, + char *str) { - static int ident = 0; + static int indent = 0; int i; - if (id<0) - ident += id; - cprintf(cb, "%*s%s ", ident, "", str?str:""); - if (id>0) - ident += id; + if (ind<0) + indent += ind; + cprintf(cb, "%*s%s ", indent, "", str?str:""); + if (ind>0) + indent += ind; if (xc){ cprintf(cb, "%s: ", (char*)clicon_int2str(ctxmap, xc->xc_type)); switch (xc->xc_type){ @@ -149,6 +155,32 @@ ctx_print(cbuf *cb, return 0; } +/*! Print XPATH context + * @param[in] f File to print to + * @param[in] xc XPATH evaluation context + * @param[in] str Prefix string in printout + */ +int +ctx_print(FILE *f, + xp_ctx *xc, + char *str) +{ + int retval = -1; + cbuf *cb = NULL; + + if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); + goto done; + } + ctx_print_cb(cb, xc, 0, str); + fprintf(f, "%s", cbuf_get(cb)); + retval = 0; + done: + if (cb) + cbuf_free(cb); + return retval; +} + /*! Convert xpath context to boolean according to boolean() function in XPATH spec * @param[in] xc XPATH context * @retval 0 False diff --git a/lib/src/clixon_xpath_eval.c b/lib/src/clixon_xpath_eval.c index 077c4fb2..2cf56644 100644 --- a/lib/src/clixon_xpath_eval.c +++ b/lib/src/clixon_xpath_eval.c @@ -81,6 +81,7 @@ #include "clixon_handle.h" #include "clixon_yang.h" #include "clixon_xml.h" +#include "clixon_xml_sort.h" #include "clixon_xml_nsctx.h" #include "clixon_xpath_ctx.h" #include "clixon_xpath.h" @@ -105,7 +106,7 @@ const map_str2int xpopmap[] = { {NULL, -1} }; -/*! +/*! Eval an XPATH nodetest * @retval -1 Error XXX: retval -1 not properly handled * @retval 0 No match * @retval 1 Match @@ -169,10 +170,37 @@ nodetest_eval_node(cxobj *x, done: /* retval set in preceding statement */ return retval; } - + +/*! Eval an XPATH nodetest but skip prefix and namespace tests + * This is NOT according to standard + */ +static int +nodetest_eval_node_localonly(cxobj *x, + xpath_tree *xs, + cvec *nsc) +{ + int retval = -1; + char *name1 = xml_name(x); + char *name2 = NULL; + + /* Namespaces is s0, name is s1 */ + if (strcmp(xs->xs_s1, "*")==0) + return 1; + name2 = xs->xs_s1; + /* Before going into namespaces, check name equality and filter out noteq */ + if (strcmp(name1, name2) != 0){ + retval = 0; /* no match */ + goto done; + } + done: /* retval set in preceding statement */ + return retval; +} + /*! Make a nodetest + * @param[in] x XML node * @param[in] xs XPATH stack of type XP_NODE or XP_NODE_FN * @param[in] nsc XML Namespace context + * @param[in] localonly Skip prefix and namespace tests (non-standard) * @retval -1 Error * @retval 0 No match * @retval 1 Match @@ -182,13 +210,18 @@ nodetest_eval_node(cxobj *x, static int nodetest_eval(cxobj *x, xpath_tree *xs, - cvec *nsc) + cvec *nsc, + int localonly) { int retval = 0; /* NB: no match is default (not error) */ char *fn; - if (xs->xs_type == XP_NODE) - retval = nodetest_eval_node(x, xs, nsc); + if (xs->xs_type == XP_NODE){ + if (localonly) + retval = nodetest_eval_node_localonly(x, xs, nsc); + else + retval = nodetest_eval_node(x, xs, nsc); + } else if (xs->xs_type == XP_NODE_FN){ fn = xs->xs_s0; if (strcmp(fn, "node")==0) @@ -206,6 +239,7 @@ nodetest_eval(cxobj *x, * @param[in] node_type * @param[in] flags * @param[in] nsc XML Namespace context + * @param[in] localonly Skip prefix and namespace tests (non-standard) * @param[out] vec0 * @param[out] vec0len */ @@ -215,6 +249,7 @@ nodetest_recursive(cxobj *xn, int node_type, uint16_t flags, cvec *nsc, + int localonly, cxobj ***vec0, size_t *vec0len) { @@ -225,14 +260,14 @@ nodetest_recursive(cxobj *xn, xsub = NULL; while ((xsub = xml_child_each(xn, xsub, node_type)) != NULL) { - if (nodetest_eval(xsub, nodetest, nsc) == 1){ + if (nodetest_eval(xsub, nodetest, nsc, localonly) == 1){ clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xsub, flags)); if (flags==0x0 || xml_flag(xsub, flags)) if (cxvec_append(xsub, &vec, &veclen) < 0) goto done; // continue; /* Dont go deeper */ } - if (nodetest_recursive(xsub, nodetest, node_type, flags, nsc, &vec, &veclen) < 0) + if (nodetest_recursive(xsub, nodetest, node_type, flags, nsc, localonly, &vec, &veclen) < 0) goto done; } retval = 0; @@ -242,11 +277,113 @@ nodetest_recursive(cxobj *xn, return retval; } +#ifdef XPATH_LIST_OPTIMIZE +/*! Pattern matching to find fastpath + * + * @param[in] xt XPath tree + * @param[in] xv XML base node + * @param[out] xp XML found node (retval == 1) + * @param[out] key + * @param[out] keyval + * @retval 0 Match + * @retval -1 No match + XPath: + y[k=3] # corresponds to: [=] + */ +static int +xpath_list_optimize(xpath_tree *xt, + cxobj *xv, + cxobj **xp) +{ + int retval = -1; + xpath_tree *xmtop = NULL; /* pattern match tree top */ + xpath_tree *xm = NULL; + cg_var *cv0; + cg_var *cv1; + cg_var *cv2; + cvec *match = NULL; + char *name; + char *keyname; + char *keyval; + yang_stmt *yp; + yang_stmt *yc; + + /* Parse */ + if (xpath_parse("_x[_y='_z']", &xmtop) < 0) /* XXX: "y[k=3]" */ + goto done; + /* Go down two steps */ + if (xmtop && (xm = xmtop->xs_c0) && (xm = xm->xs_c0)) + ; + if (xm == NULL) + goto ok; + /* Create a cvec match vector and initialize variables */ + if ((match = cvec_new(3)) == NULL){ + clicon_err(OE_UNIX, errno, "cvec_new"); + goto done; + } + cv0 = cvec_i(match, 0); + cv_name_set(cv0, "_x"); + cv_type_set(cv0, CGV_STRING); + cv1 = cvec_i(match, 1); + cv_name_set(cv1, "_y"); + cv_type_set(cv1, CGV_STRING); + cv2 = cvec_i(match, 2); + cv_name_set(cv2, "_z"); + cv_type_set(cv2, CGV_STRING); + + /* Check if equal */ + if (xpath_tree_eq(xm, xt, match) != 0) + goto ok; /* no match */ + /* Extract variables XXX strdups */ + name = cv_string_get(cv0); + keyname = cv_string_get(cv1); + keyval = cv_string_get(cv2); + assert(name && keyname && keyval); + /* Check yang and that only a list with key as index is a special case can do bin search */ + if ((yp = xml_spec(xv)) == NULL) + goto ok; + if ((yc = yang_find(yp, 0, name)) == NULL) + goto ok; + if (yang_keyword_get(yc) != Y_LIST) + goto ok; +#if 0 + { + cvec *cvv = NULL; + if ((cvv = yang_cvec_get(yc)) == NULL) + goto ok; + if (cvec_len(cvv) != 1) + goto ok; + } +#else + { + int ret; + if ((ret = yang_key_match(yc, keyname)) < 0) + goto done; + if (ret != 1) + goto ok; + } +#endif + if (xml_binsearch(xv, name, keyname, keyval, xp) < 0) + goto done; + retval = 1; /* match */ + done: + if (match) + cvec_free(match); + if (xmtop) + xpath_tree_free(xmtop); + return retval; + ok: /* no match, not special case */ + retval = 0; + goto done; +} +#endif /* XPATH_LIST_OPTIMIZE */ + /*! Evaluate xpath step rule of an XML tree * * @param[in] xc0 Incoming context * @param[in] xs XPATH node tree * @param[in] nsc XML Namespace context + * @param[in] localonly Skip prefix and namespace tests (non-standard) * @param[out] xrp Resulting context * * - A node test that is a QName is true if and only if the type of the node (see [5 Data Model]) @@ -259,6 +396,7 @@ static int xp_eval_step(xp_ctx *xc0, xpath_tree *xs, cvec *nsc, + int localonly, xp_ctx **xrp) { int retval = -1; @@ -270,6 +408,9 @@ xp_eval_step(xp_ctx *xc0, size_t veclen = 0; xpath_tree *nodetest = xs->xs_c0; xp_ctx *xc = NULL; +#ifdef XPATH_LIST_OPTIMIZE + int ret; +#endif /* Create new xc */ if ((xc = ctx_dup(xc0)) == NULL) @@ -285,7 +426,7 @@ xp_eval_step(xp_ctx *xc0, if (xc->xc_descendant){ for (i=0; ixc_size; i++){ xv = xc->xc_nodeset[i]; - if (nodetest_recursive(xv, nodetest, CX_ELMNT, 0x0, nsc, &vec, &veclen) < 0) + if (nodetest_recursive(xv, nodetest, CX_ELMNT, 0x0, nsc, localonly, &vec, &veclen) < 0) goto done; } xc->xc_descendant = 0; @@ -297,17 +438,33 @@ xp_eval_step(xp_ctx *xc0, if (cxvec_append(xc->xc_initial, &vec, &veclen) < 0) goto done; } - else for (i=0; ixc_size; i++){ - xv = xc->xc_nodeset[i]; - x = NULL; - while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) { - /* xs->xs_c0 is nodetest */ - if (nodetest == NULL || nodetest_eval(x, nodetest, nsc) == 1){ - if (cxvec_append(x, &vec, &veclen) < 0) - goto done; + else for (i=0; ixc_size; i++){ + xv = xc->xc_nodeset[i]; + x = NULL; +#ifdef XPATH_LIST_OPTIMIZE + /* Identify XPATH special cases and if match, use binary search. + * it returns: -1 fatal error, quit + * 0: not special case, do normal processing + * 1: special case, use x (found if != NULL) + */ + if ((ret = xpath_list_optimize(xs, xv, &x)) < 0) + goto done; + if (ret == 1){ + fprintf(stderr, "XPATH_LIST_OPTIMIZE\n"); + if (x) + if (cxvec_append(x, &vec, &veclen) < 0) + goto done; } + else +#endif + while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) { + /* xs->xs_c0 is nodetest */ + if (nodetest == NULL || nodetest_eval(x, nodetest, nsc, localonly) == 1){ + if (cxvec_append(x, &vec, &veclen) < 0) + goto done; + } + } } - } } ctx_nodeset_replace(xc, vec, veclen); break; @@ -315,7 +472,7 @@ xp_eval_step(xp_ctx *xc0, case A_DESCENDANT_OR_SELF: for (i=0; ixc_size; i++){ xv = xc->xc_nodeset[i]; - if (nodetest_recursive(xv, xs->xs_c0, CX_ELMNT, 0x0, nsc, &vec, &veclen) < 0) + if (nodetest_recursive(xv, xs->xs_c0, CX_ELMNT, 0x0, nsc, localonly, &vec, &veclen) < 0) goto done; } ctx_nodeset_replace(xc, vec, veclen); @@ -354,7 +511,7 @@ xp_eval_step(xp_ctx *xc0, break; } if (xs->xs_c1){ - if (xp_eval(xc, xs->xs_c1, nsc, xrp) < 0) + if (xp_eval(xc, xs->xs_c1, nsc, localonly, xrp) < 0) goto done; } else{ @@ -375,6 +532,7 @@ xp_eval_step(xp_ctx *xc0, * @param[in] xc Incoming context * @param[in] xs XPATH node tree * @param[in] nsc XML Namespace context + * @param[in] localonly Skip prefix and namespace tests (non-standard) * @param[out] xrp Resulting context * * A predicate filters a node-set with respect to an axis to produce a new @@ -396,6 +554,7 @@ static int xp_eval_predicate(xp_ctx *xc, xpath_tree *xs, cvec *nsc, + int localonly, xp_ctx **xrp) { int retval = -1; @@ -411,7 +570,7 @@ xp_eval_predicate(xp_ctx *xc, goto done; } else{ /* eval previous predicates */ - if (xp_eval(xc, xs->xs_c0, nsc, &xr0) < 0) + if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0) goto done; } if (xs->xs_c1){ @@ -440,7 +599,7 @@ xp_eval_predicate(xp_ctx *xc, * evaluated with that node as the context node */ if (cxvec_append(x, &xcc->xc_nodeset, &xcc->xc_size) < 0) goto done; - if (xp_eval(xcc, xs->xs_c1, nsc, &xrc) < 0) + if (xp_eval(xcc, xs->xs_c1, nsc, localonly, &xrc) < 0) goto done; if (xcc) ctx_free(xcc); @@ -860,6 +1019,7 @@ xp_union(xp_ctx *xc1, * @param[in] xc Incoming context * @param[in] xs XPATH node tree * @param[in] nsc XML Namespace context + * @param[in] localonly Skip prefix and namespace tests (non-standard) * @param[out] xrp Resulting context * @retval 0 OK * @retval -1 Error @@ -868,6 +1028,7 @@ int xp_eval(xp_ctx *xc, xpath_tree *xs, cvec *nsc, + int localonly, xp_ctx **xrp) { int retval = -1; @@ -877,16 +1038,8 @@ xp_eval(xp_ctx *xc, xp_ctx *xr2 = NULL; int use_xr0 = 0; /* In 2nd child use transitively result of 1st child */ - if (debug>1){ - cbuf *cb; - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_UNIX, errno, "cbuf_new"); - goto done; - } - ctx_print(cb, +2, xc, xpath_tree_int2str(xs->xs_type)); - clicon_debug(2, "%s", cbuf_get(cb)); - cbuf_free(cb); - } + if (debug) + ctx_print(stderr, xc, xpath_tree_int2str(xs->xs_type)); /* Pre-actions before check first child c0 */ switch (xs->xs_type){ @@ -908,12 +1061,12 @@ xp_eval(xp_ctx *xc, break; case XP_STEP: /* XP_NODE is first argument -not called explicitly */ - if (xp_eval_step(xc, xs, nsc, xrp) < 0) + if (xp_eval_step(xc, xs, nsc, localonly, xrp) < 0) goto done; goto ok; break; case XP_PRED: - if (xp_eval_predicate(xc, xs, nsc, xrp) < 0) + if (xp_eval_predicate(xc, xs, nsc, localonly, xrp) < 0) goto done; goto ok; break; @@ -923,7 +1076,7 @@ xp_eval(xp_ctx *xc, /* Eval first child c0 */ if (xs->xs_c0){ - if (xp_eval(xc, xs->xs_c0, nsc, &xr0) < 0) + if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0) goto done; } /* Actions between first and second child @@ -1001,7 +1154,7 @@ xp_eval(xp_ctx *xc, * Note, some operators like locationpath, need transitive context (use_xr0) */ if (xs->xs_c1) - if (xp_eval(use_xr0?xr0:xc, xs->xs_c1, nsc, &xr1) < 0) + if (xp_eval(use_xr0?xr0:xc, xs->xs_c1, nsc, localonly, &xr1) < 0) goto done; /* Actions after second child */ @@ -1041,16 +1194,8 @@ xp_eval(xp_ctx *xc, xr0 = NULL; } ok: - if (debug){ - cbuf *cb; - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_UNIX, errno, "cbuf_new"); - goto done; - } - ctx_print(cb, -2, *xrp, xpath_tree_int2str(xs->xs_type)); - clicon_debug(2, "%s", cbuf_get(cb)); - cbuf_free(cb); - } + if (debug) + ctx_print(stderr, *xrp, xpath_tree_int2str(xs->xs_type)); retval = 0; done: if (xr2) diff --git a/lib/src/clixon_xpath_eval.h b/lib/src/clixon_xpath_eval.h index 5b74bb5a..d8c9b296 100644 --- a/lib/src/clixon_xpath_eval.h +++ b/lib/src/clixon_xpath_eval.h @@ -44,6 +44,6 @@ extern const map_str2int xpopmap[]; /* * Prototypes */ -int xp_eval(xp_ctx *xc, xpath_tree *xs, cvec *nsc, xp_ctx **xrp); +int xp_eval(xp_ctx *xc, xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp); #endif /* _CLIXON_XPATH_EVAL_H */ diff --git a/lib/src/clixon_xpath_parse.l b/lib/src/clixon_xpath_parse.l index 8676b9d1..9ac77880 100644 --- a/lib/src/clixon_xpath_parse.l +++ b/lib/src/clixon_xpath_parse.l @@ -85,6 +85,10 @@ digit [0-9] integer {digit}+ real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+) +namestart [A-Z_a-z] +namechar [A-Z_a-z\-\.0-9] +ncname {namestart}{namechar}* + %x TOKEN %s QLITERAL %s ALITERAL @@ -141,9 +145,9 @@ real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+) \" { BEGIN(QLITERAL); return QUOTE; } \' { BEGIN(ALITERAL); return APOST; } \-?({integer}|{real}) { clixon_xpath_parselval.string = strdup(yytext); return NUMBER; } -[0-9A-Za-z_\-]+ { clixon_xpath_parselval.string = strdup(yytext); +{ncname} { clixon_xpath_parselval.string = strdup(yytext); return NAME; /* rather be catch-all */ - } + } . { fprintf(stderr,"LEXICAL ERROR\n"); return -1; } \" { BEGIN(TOKEN); return QUOTE; } diff --git a/lib/src/clixon_xpath_parse.y b/lib/src/clixon_xpath_parse.y index a5050aaa..5586069c 100644 --- a/lib/src/clixon_xpath_parse.y +++ b/lib/src/clixon_xpath_parse.y @@ -103,7 +103,6 @@ #include #include #include -#include #include #include #include diff --git a/lib/src/clixon_yang_cardinality.c b/lib/src/clixon_yang_cardinality.c index f5f0dcdc..7db189d4 100644 --- a/lib/src/clixon_yang_cardinality.c +++ b/lib/src/clixon_yang_cardinality.c @@ -51,7 +51,6 @@ #include #include #include -#include #include #include diff --git a/lib/src/clixon_yang_module.c b/lib/src/clixon_yang_module.c index cc1fe48a..5c9af091 100644 --- a/lib/src/clixon_yang_module.c +++ b/lib/src/clixon_yang_module.c @@ -52,7 +52,6 @@ #include #include #include -#include #include #include #include diff --git a/test/test_nacm_credentials.sh b/test/test_nacm_credentials.sh index e61b803a..d0bee929 100755 --- a/test/test_nacm_credentials.sh +++ b/test/test_nacm_credentials.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Test matrching of Unix peer credentials with NACM users +# Test matching of Unix peer credentials with NACM users # Use raw unix socket instead of clients (cli/netconf/restconf) since they do # magic things with the username and here it needs to be handled explicitly. # test matrix: diff --git a/test/test_xml.sh b/test/test_xml.sh index 4ab44287..a25184d4 100755 --- a/test/test_xml.sh +++ b/test/test_xml.sh @@ -37,6 +37,9 @@ fi new "xml simple CDATA" expecteofx "$clixon_util_xml -o" 0 '' '' +new "xml CDATA right square bracket: ]" +expecteofx "$clixon_util_xml -o" 0 "" "" + new "xml simple CDATA to json" expecteofx "$clixon_util_xml -o -j" 0 '' '{"a":"a text"}' diff --git a/test/test_xpath.sh b/test/test_xpath.sh index 338a1da6..14466b09 100755 --- a/test/test_xpath.sh +++ b/test/test_xpath.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash -# Test: XPATH tests -#PROG="valgrind --leak-check=full --show-leak-kinds=all ../util/clixon_util_xpath" +# XPATH tests # Magic line must be first in script (see README.md) s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi @@ -12,11 +11,11 @@ xml=$dir/xml.xml xml2=$dir/xml2.xml xml3=$dir/xml3.xml ydir=$dir/yang + if [ ! -d $ydir ]; then mkdir $ydir fi - cat < $xml @@ -82,6 +81,7 @@ cat < $xml3 EOF new "xpath /" +echo "$clixon_util_xpath -f $xml -p /" expecteof "$clixon_util_xpath -f $xml -p /" 0 "" "^nodeset:0:429922$" new "xpath /aaa" @@ -208,54 +208,4 @@ expecteof "$clixon_util_xpath -f $xml3 -p bbb[ccc='fie']" 0 "" "^nodeset:$" new "xpath derived-from-or-self" expecteof "$clixon_util_xpath -f $xml3 -p 'derived-from-or-self(../../change-operation,modify)'" 0 "" "derived-from-or-self" -# canonical namespace xpath tests -# need yang modules -cat < $ydir/a.yang -module a{ - namespace "urn:example:a"; - prefix a; - container x{ - leaf xa{ - type string; - } - } -} -EOF - -cat < $ydir/b.yang -module b{ - namespace "urn:example:b"; - prefix b; - container y{ - leaf ya{ - type string; - } - } -} -EOF - -new "xpath canonical form (already canonical)" -expectpart "$($clixon_util_xpath -c -y $ydir -p /a:x/b:y -n a:urn:example:a -n b:urn:example:b)" 0 '/a:x/b:y' '0 : a = "urn:example:a"' '1 : b = "urn:example:b"' - -new "xpath canonical form (default)" -expectpart "$($clixon_util_xpath -c -y $ydir -p /x/b:y -n null:urn:example:a -n b:urn:example:b)" 0 '/a:x/b:y' '0 : a = "urn:example:a"' '1 : b = "urn:example:b"' - -new "xpath canonical form (other)" -expectpart "$($clixon_util_xpath -c -y $ydir -p /i:x/j:y -n i:urn:example:a -n j:urn:example:b)" 0 '/a:x/b:y' '0 : a = "urn:example:a"' '1 : b = "urn:example:b"' - -new "xpath canonical form predicate 1" -expectpart "$($clixon_util_xpath -c -y $ydir -p "/i:x[j:y='e1']" -n i:urn:example:a -n j:urn:example:b)" 0 "/a:x\[b:y='e1'\]" '0 : a = "urn:example:a"' '1 : b = "urn:example:b"' - -new "xpath canonical form predicate self" -expectpart "$($clixon_util_xpath -c -y $ydir -p "/i:x[.='42']" -n i:urn:example:a -n j:urn:example:b)" 0 "/a:x\[.='42'\]" '0 : a = "urn:example:a"' - -new "xpath canonical form descendants" -expectpart "$($clixon_util_xpath -c -y $ydir -p "//x[.='42']" -n null:urn:example:a -n j:urn:example:b)" 0 "//a:x\[.='42'\]" '0 : a = "urn:example:a"' - -new "xpath canonical form (no default should fail)" -expectpart "$($clixon_util_xpath -c -y $ydir -p /x/j:y -n i:urn:example:a -n j:urn:example:b)" 255 - -new "xpath canonical form (wrong namespace should fail)" -expectpart "$($clixon_util_xpath -c -y $ydir -p /i:x/j:y -n i:urn:example:c -n j:urn:example:b)" 255 - rm -rf $dir diff --git a/test/test_xpath_canonical.sh b/test/test_xpath_canonical.sh new file mode 100755 index 00000000..a83feffc --- /dev/null +++ b/test/test_xpath_canonical.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# Test xpath canonical namespace context form + +# Magic line must be first in script (see README.md) +s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi + +: ${clixon_util_xpath:=clixon_util_xpath} + +ydir=$dir/yang + +if [ ! -d $ydir ]; then + mkdir $ydir +fi + +# canonical namespace xpath tests +# need yang modules +cat < $ydir/a.yang +module a{ + namespace "urn:example:a"; + prefix a; + container x{ + leaf xa{ + type string; + } + } +} +EOF + +cat < $ydir/b.yang +module b{ + namespace "urn:example:b"; + prefix b; + container y{ + leaf ya{ + type string; + } + } +} +EOF + +new "xpath canonical form (already canonical)" +expectpart "$($clixon_util_xpath -c -y $ydir -p /a:x/b:y -n a:urn:example:a -n b:urn:example:b)" 0 '/a:x/b:y' '0 : a = "urn:example:a"' '1 : b = "urn:example:b"' + +new "xpath canonical form (default)" +expectpart "$($clixon_util_xpath -c -y $ydir -p /x/b:y -n null:urn:example:a -n b:urn:example:b)" 0 '/a:x/b:y' '0 : a = "urn:example:a"' '1 : b = "urn:example:b"' + +new "xpath canonical form (other)" +expectpart "$($clixon_util_xpath -c -y $ydir -p /i:x/j:y -n i:urn:example:a -n j:urn:example:b)" 0 '/a:x/b:y' '0 : a = "urn:example:a"' '1 : b = "urn:example:b"' + +new "xpath canonical form predicate 1" +expectpart "$($clixon_util_xpath -c -y $ydir -p "/i:x[j:y='e1']" -n i:urn:example:a -n j:urn:example:b)" 0 "/a:x\[b:y='e1'\]" '0 : a = "urn:example:a"' '1 : b = "urn:example:b"' + +new "xpath canonical form predicate self" +expectpart "$($clixon_util_xpath -c -y $ydir -p "/i:x[.='42']" -n i:urn:example:a -n j:urn:example:b)" 0 "/a:x\[.='42'\]" '0 : a = "urn:example:a"' + +new "xpath canonical form descendants" +expectpart "$($clixon_util_xpath -c -y $ydir -p "//x[.='42']" -n null:urn:example:a -n j:urn:example:b)" 0 "//a:x\[.='42'\]" '0 : a = "urn:example:a"' + +new "xpath canonical form (no default should fail)" +expectpart "$($clixon_util_xpath -c -y $ydir -p /x/j:y -n i:urn:example:a -n j:urn:example:b)" 255 + +new "xpath canonical form (wrong namespace should fail)" +expectpart "$($clixon_util_xpath -c -y $ydir -p /i:x/j:y -n i:urn:example:c -n j:urn:example:b)" 255 + +rm -rf $dir diff --git a/util/clixon_util_insert.c b/util/clixon_util_insert.c index 638acdd1..037fe585 100644 --- a/util/clixon_util_insert.c +++ b/util/clixon_util_insert.c @@ -45,7 +45,6 @@ #include #include #include -#include #include #include diff --git a/util/clixon_util_json.c b/util/clixon_util_json.c index 4c333c96..b2b56b98 100644 --- a/util/clixon_util_json.c +++ b/util/clixon_util_json.c @@ -49,7 +49,6 @@ #include #include #include -#include /* cligen */ #include diff --git a/util/clixon_util_socket.c b/util/clixon_util_socket.c index ed2c66c2..81e6621d 100644 --- a/util/clixon_util_socket.c +++ b/util/clixon_util_socket.c @@ -49,7 +49,6 @@ #include #include #include -#include #include #include #include diff --git a/util/clixon_util_stream.c b/util/clixon_util_stream.c index 511c62ea..526c6058 100644 --- a/util/clixon_util_stream.c +++ b/util/clixon_util_stream.c @@ -47,7 +47,6 @@ #include #include #include -#include #include #include diff --git a/util/clixon_util_xml.c b/util/clixon_util_xml.c index 30b4d9e2..2693c8c8 100644 --- a/util/clixon_util_xml.c +++ b/util/clixon_util_xml.c @@ -53,7 +53,6 @@ #include #include #include -#include #include #include #include @@ -196,7 +195,7 @@ main(int argc, if ((ret = json_parse_file(fd, yspec, &xt, &xerr)) < 0) goto done; if (ret == 0){ - clicon_rpc_generate_error("util_xml", xerr); + clicon_rpc_generate_error(xerr, "util_xml", NULL); goto done; } } diff --git a/util/clixon_util_xpath.c b/util/clixon_util_xpath.c index 9cacc0ce..184bf9ff 100644 --- a/util/clixon_util_xpath.c +++ b/util/clixon_util_xpath.c @@ -47,7 +47,6 @@ See https://www.w3.org/TR/xpath/ #include #include #include -#include #include #include #include @@ -318,7 +317,7 @@ main(int argc, x = x0; /* Parse XPATH (use nsc == NULL to indicate dont use) */ - if (xpath_vec_ctx(x, nsc, xpath, &xc) < 0) + if (xpath_vec_ctx(x, nsc, xpath, 0, &xc) < 0) return -1; /* Print results */ cb = cbuf_new(); diff --git a/util/clixon_util_yang.c b/util/clixon_util_yang.c index f1fb46ce..7918cac3 100644 --- a/util/clixon_util_yang.c +++ b/util/clixon_util_yang.c @@ -50,7 +50,6 @@ #include #include #include -#include #include #include #include