double predicates in xpath; leaks; grideye debug

This commit is contained in:
Olof hagsand 2016-03-26 10:47:58 +01:00
parent c1c1670a74
commit 741fb97a9f
13 changed files with 453 additions and 242 deletions

View file

@ -244,7 +244,7 @@ from_client_xmlput(clicon_handle h,
cvec *cvv = NULL; cvec *cvv = NULL;
char *str = NULL; char *str = NULL;
char *xml = NULL; char *xml = NULL;
cxobj *xt; cxobj *xt = NULL;
int piddb; int piddb;
if (clicon_msg_xmlput_decode(msg, if (clicon_msg_xmlput_decode(msg,
@ -283,6 +283,8 @@ from_client_xmlput(clicon_handle h,
free(str); free(str);
if (cvv) if (cvv)
cvec_free (cvv); cvec_free (cvv);
if (xt)
xml_free(xt);
return retval; return retval;
} }

View file

@ -112,33 +112,23 @@ generic_validate(yang_spec *yspec,
return retval; return retval;
} }
/*! Do a diff between candidate and running, then start a commit transaction /*! Common code of candidate_validate and candidate_commit
* */
* The code reverts changes if the commit fails. But if the revert static int
* fails, we just ignore the errors and proceed. Maybe we should validate_common(clicon_handle h,
* do something more drastic? char *candidate,
* @param[in] h Clicon handle transaction_data_t *td)
*/
int
candidate_commit(clicon_handle h,
char *candidate)
{ {
int retval = -1; int retval = -1;
yang_spec *yspec;
int i; int i;
cxobj *xn; cxobj *xn;
void *firsterr = NULL;
yang_spec *yspec;
transaction_data_t *td = NULL;
if ((yspec = clicon_dbspec_yang(h)) == NULL){ if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC"); clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done; goto done;
} }
/* 1. Start transaction */
if ((td = transaction_new()) == NULL)
goto done;
/* 2. Parse xml trees */ /* 2. Parse xml trees */
if (xmldb_get(h, "running", "/", 0, &td->td_src, NULL, NULL) < 0) if (xmldb_get(h, "running", "/", 0, &td->td_src, NULL, NULL) < 0)
goto done; goto done;
@ -196,6 +186,32 @@ candidate_commit(clicon_handle h,
/* 7. Call plugin transaction complete callbacks */ /* 7. Call plugin transaction complete callbacks */
if (plugin_transaction_complete(h, td) < 0) if (plugin_transaction_complete(h, td) < 0)
goto done; goto done;
retval = 0;
done:
return retval;
}
/*! Do a diff between candidate and running, then start a commit transaction
*
* The code reverts changes if the commit fails. But if the revert
* fails, we just ignore the errors and proceed. Maybe we should
* do something more drastic?
* @param[in] h Clicon handle
*/
int
candidate_commit(clicon_handle h,
char *candidate)
{
int retval = -1;
transaction_data_t *td = NULL;
/* 1. Start transaction */
if ((td = transaction_new()) == NULL)
goto done;
/* Common steps (with validate) */
if (validate_common(h, candidate, td) < 0)
goto done;
/* 7. Call plugin transaction commit callbacks */ /* 7. Call plugin transaction commit callbacks */
if (plugin_transaction_commit(h, td) < 0) if (plugin_transaction_commit(h, td) < 0)
@ -214,7 +230,6 @@ candidate_commit(clicon_handle h,
clicon_log(LOG_NOTICE, "Error in rollback, trying to continue"); clicon_log(LOG_NOTICE, "Error in rollback, trying to continue");
goto done; goto done;
} }
retval = 0; retval = 0;
done: done:
/* In case of failure, call plugin transaction termination callbacks */ /* In case of failure, call plugin transaction termination callbacks */
@ -222,8 +237,6 @@ candidate_commit(clicon_handle h,
plugin_transaction_abort(h, td); plugin_transaction_abort(h, td);
if (td) if (td)
transaction_free(td); transaction_free(td);
if (firsterr)
clicon_err_restore(firsterr);
return retval; return retval;
} }
@ -237,72 +250,14 @@ candidate_validate(clicon_handle h,
char *candidate) char *candidate)
{ {
int retval = -1; int retval = -1;
yang_spec *yspec;
transaction_data_t *td = NULL; transaction_data_t *td = NULL;
int i;
cxobj *xn;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done;
}
/* 1. Start transaction */ /* 1. Start transaction */
if ((td = transaction_new()) == NULL) if ((td = transaction_new()) == NULL)
goto done; goto done;
/* 2. Parse xml trees */ /* Common steps (with commit) */
if (xmldb_get(h, "running", "/", 0, &td->td_src, NULL, NULL) < 0) if (validate_common(h, candidate, td) < 0)
goto done;
if (xmldb_get(h, "candidate", "/", 0, &td->td_target, NULL, NULL) < 0)
goto done;
/* 3. Compute differences */
if (xml_diff(yspec,
td->td_src,
td->td_target,
&td->td_dvec, /* removed: only in running */
&td->td_dlen,
&td->td_avec, /* added: only in candidate */
&td->td_alen,
&td->td_scvec, /* changed: original values */
&td->td_tcvec, /* changed: wanted values */
&td->td_clen) < 0)
goto done;
if (debug)
transaction_print(stderr, td);
/* Mark as changed in tree */
for (i=0; i<td->td_dlen; i++){ /* Also down */
xn = td->td_dvec[i];
xml_flag_set(xn, XML_FLAG_DEL);
}
for (i=0; i<td->td_alen; i++){ /* Also down */
xn = td->td_avec[i];
xml_flag_set(xn, XML_FLAG_ADD);
}
for (i=0; i<td->td_clen; i++){ /* Also up */
xn = td->td_scvec[i];
xml_flag_set(xn, XML_FLAG_CHANGE);
xn = td->td_tcvec[i];
xml_flag(xn, XML_FLAG_CHANGE);
}
/* 4. Call plugin start transaction callbacks */
if (plugin_transaction_begin(h, td) < 0)
goto done;
/* 5. Make generic validation on all new or changed data. */
if (generic_validate(yspec, td) < 0)
goto done;
/* 6. Call plugin validate transaction callbacks */
if (plugin_transaction_validate(h, td) < 0)
goto done;
/* 7. Call plugin complete transaction callbacks */
if (plugin_transaction_complete(h, td) < 0)
goto done; goto done;
retval = 0; retval = 0;

View file

@ -632,6 +632,15 @@ plugin_transaction_complete(clicon_handle h,
return retval; return retval;
} }
/*! Revert a commit
* @param[in] h CLICON handle
* @param[in] td Transaction data
* @param[in] nr The plugin where an error occured.
* @retval 0 OK
* @retval -1 Error
* The revert is made in plugin before this one. Eg if error occurred in
* plugin 2, then the revert will be made in plugins 1 and 0.
*/
int int
plugin_transaction_revert(clicon_handle h, plugin_transaction_revert(clicon_handle h,
transaction_data_t *td, transaction_data_t *td,
@ -651,10 +660,11 @@ plugin_transaction_revert(clicon_handle h,
tr.td_dvec = td->td_avec; tr.td_dvec = td->td_avec;
tr.td_alen = td->td_dlen; tr.td_alen = td->td_dlen;
tr.td_avec = td->td_dvec; tr.td_avec = td->td_dvec;
tr.td_clen = td->td_clen;
tr.td_scvec = td->td_tcvec; tr.td_scvec = td->td_tcvec;
tr.td_tcvec = td->td_scvec; tr.td_tcvec = td->td_scvec;
for (i = nr-1; i; i--){ for (i = nr-1; i>=0; i--){
p = &plugins[i]; p = &plugins[i];
if (p->p_trans_commit) if (p->p_trans_commit)
if ((p->p_trans_commit)(h, (transaction_data)&tr) < 0){ if ((p->p_trans_commit)(h, (transaction_data)&tr) < 0){

View file

@ -167,6 +167,7 @@ backend_notify_xml(clicon_handle h,
cbuf *cb = NULL; cbuf *cb = NULL;
struct handle_subscription *hs; struct handle_subscription *hs;
clicon_debug(1, "%s %s", __FUNCTION__, stream);
/* Now go thru all clients(sessions), and all subscriptions and find matches */ /* Now go thru all clients(sessions), and all subscriptions and find matches */
for (ce = backend_client_list(h); ce; ce = ce->ce_next) for (ce = backend_client_list(h); ce; ce = ce->ce_next)
for (su = ce->ce_subscription; su; su = su->su_next) for (su = ce->ce_subscription; su; su = su->su_next)
@ -185,8 +186,6 @@ backend_notify_xml(clicon_handle h,
} }
} }
/* Then go thru all global (handle) subscriptions and find matches */ /* Then go thru all global (handle) subscriptions and find matches */
/* XXX: x contains name==dk-ore, but filter is
id==/[userid=d2d5e46c-c6f9-42f3-9a69-fb52fe60940d] */
hs = NULL; hs = NULL;
while ((hs = subscription_each(h, hs)) != NULL){ while ((hs = subscription_each(h, hs)) != NULL){
if (hs->hs_format != MSG_NOTIFY_XML) if (hs->hs_format != MSG_NOTIFY_XML)

View file

@ -310,7 +310,7 @@ cli_commit(clicon_handle h,
"running", "running",
snapshot, /* snapshot */ snapshot, /* snapshot */
snapshot)) < 0){ /* startup */ snapshot)) < 0){ /* startup */
cli_output(stderr, "Commit failed. Edit and try again or discard changes\n"); cli_output(stderr, "Commit failed. Edit and try again or discard changes");
goto done; goto done;
} }
retval = 0; retval = 0;
@ -327,7 +327,7 @@ cli_validate(clicon_handle h, cvec *vars, cg_var *arg)
int retval = -1; int retval = -1;
if ((retval = clicon_rpc_validate(h, "candidate")) < 0) if ((retval = clicon_rpc_validate(h, "candidate")) < 0)
clicon_err(OE_CFG, 0, "Validate failed. Edit and try again or discard changes\n"); clicon_err(OE_CFG, 0, "Validate failed. Edit and try again or discard changes");
return retval; return retval;
} }
@ -578,7 +578,7 @@ load_config_file(clicon_handle h,
} }
filename = vecp[0]; filename = vecp[0];
if (stat(filename, &st) < 0){ if (stat(filename, &st) < 0){
clicon_err(OE_UNIX, 0, "load_config: stat(%s): %s\n", clicon_err(OE_UNIX, 0, "load_config: stat(%s): %s",
filename, strerror(errno)); filename, strerror(errno));
goto done; goto done;
} }
@ -901,7 +901,7 @@ cli_notification_register(clicon_handle h,
if ((p = hash_value(cdat, logname, &len)) != NULL) if ((p = hash_value(cdat, logname, &len)) != NULL)
s_exist = *(int*)p; s_exist = *(int*)p;
if (status){ if (status){ /* start */
if (s_exist!=-1){ if (s_exist!=-1){
clicon_err(OE_PLUGIN, 0, "%s: result log socket already exists", __FUNCTION__); clicon_err(OE_PLUGIN, 0, "%s: result log socket already exists", __FUNCTION__);
goto done; goto done;
@ -913,7 +913,7 @@ cli_notification_register(clicon_handle h,
if (hash_add(cdat, logname, &s, sizeof(s)) == NULL) if (hash_add(cdat, logname, &s, sizeof(s)) == NULL)
goto done; goto done;
} }
else{ else{ /* stop */
if (s_exist != -1){ if (s_exist != -1){
cligen_unregfd(s_exist); cligen_unregfd(s_exist);
} }

View file

@ -113,4 +113,8 @@ int cxvec_append(cxobj *x, cxobj ***vec, size_t *len);
int xml_apply(cxobj *xn, enum cxobj_type type, xml_applyfn_t fn, void *arg); int xml_apply(cxobj *xn, enum cxobj_type type, xml_applyfn_t fn, void *arg);
int xml_apply_ancestor(cxobj *xn, xml_applyfn_t fn, void *arg); int xml_apply_ancestor(cxobj *xn, xml_applyfn_t fn, void *arg);
int xml_body_parse(cxobj *xb, enum cv_type type, cg_var **cvp);
int xml_body_int32(cxobj *xb, int32_t *val);
int xml_body_uint32(cxobj *xb, uint32_t *val);
#endif /* _CLIXON_XML_H */ #endif /* _CLIXON_XML_H */

View file

@ -44,7 +44,7 @@ int xml2cli(FILE *f, cxobj *x, char *prepend, enum genmodel_type gt, const char
int xml2json(FILE *f, cxobj *x, int level); int xml2json(FILE *f, cxobj *x, int level);
int xml_yang_validate(cxobj *xt, yang_stmt *ys) ; int xml_yang_validate(cxobj *xt, yang_stmt *ys) ;
int xml2cvec(cxobj *xt, yang_stmt *ys, cvec **cvv0); int xml2cvec(cxobj *xt, yang_stmt *ys, cvec **cvv0);
int cvec2xml_1(cvec *cvv, char *toptag, cxobj **xt0); int cvec2xml_1(cvec *cvv, char *toptag, cxobj *xp, cxobj **xt0);
int xml_diff(yang_spec *yspec, cxobj *xt1, cxobj *xt2, int xml_diff(yang_spec *yspec, cxobj *xt1, cxobj *xt2,
cxobj ***first, size_t *firstlen, cxobj ***first, size_t *firstlen,
cxobj ***second, size_t *secondlen, cxobj ***second, size_t *secondlen,

View file

@ -430,10 +430,11 @@ clicon_rpc_subscription(clicon_handle h,
goto done; goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, s0, __FUNCTION__) < 0) if (clicon_rpc_msg(h, msg, NULL, NULL, s0, __FUNCTION__) < 0)
goto done; goto done;
if (status == 0){ if (status == 0 && s0){
close(*s0); close(*s0);
*s0 = -1; *s0 = -1;
} }
retval = 0;
done: done:
unchunk_group(__FUNCTION__); unchunk_group(__FUNCTION__);
return retval; return retval;

View file

@ -90,7 +90,8 @@ xml_name(cxobj *xn)
* @retval 0 OK * @retval 0 OK
*/ */
int int
xml_name_set(cxobj *xn, char *name) xml_name_set(cxobj *xn,
char *name)
{ {
if (xn->x_name){ if (xn->x_name){
free(xn->x_name); free(xn->x_name);
@ -122,7 +123,8 @@ xml_namespace(cxobj *xn)
* @retval 0 OK * @retval 0 OK
*/ */
int int
xml_namespace_set(cxobj *xn, char *namespace) xml_namespace_set(cxobj *xn,
char *namespace)
{ {
if (xn->x_namespace){ if (xn->x_namespace){
free(xn->x_namespace); free(xn->x_namespace);
@ -153,7 +155,8 @@ xml_parent(cxobj *xn)
* @retval 0 OK * @retval 0 OK
*/ */
int int
xml_parent_set(cxobj *xn, cxobj *parent) xml_parent_set(cxobj *xn,
cxobj *parent)
{ {
xn->x_up = parent; xn->x_up = parent;
return 0; return 0;
@ -164,7 +167,8 @@ xml_parent_set(cxobj *xn, cxobj *parent)
* @retval flag Flags value, see XML_FLAG_* * @retval flag Flags value, see XML_FLAG_*
*/ */
uint16_t uint16_t
xml_flag(cxobj *xn, uint16_t flag) xml_flag(cxobj *xn,
uint16_t flag)
{ {
return xn->x_flags&flag; return xn->x_flags&flag;
} }
@ -174,7 +178,8 @@ xml_flag(cxobj *xn, uint16_t flag)
* @param[in] flag Flags value to set, see XML_FLAG_* * @param[in] flag Flags value to set, see XML_FLAG_*
*/ */
int int
xml_flag_set(cxobj *xn, uint16_t flag) xml_flag_set(cxobj *xn,
uint16_t flag)
{ {
xn->x_flags |= flag; xn->x_flags |= flag;
return 0; return 0;
@ -185,7 +190,8 @@ xml_flag_set(cxobj *xn, uint16_t flag)
* @param[in] flag Flags value to reset, see XML_FLAG_* * @param[in] flag Flags value to reset, see XML_FLAG_*
*/ */
int int
xml_flag_reset(cxobj *xn, uint16_t flag) xml_flag_reset(cxobj *xn,
uint16_t flag)
{ {
xn->x_flags &= ~flag; xn->x_flags &= ~flag;
return 0; return 0;
@ -208,7 +214,8 @@ xml_value(cxobj *xn)
* @retval 0 OK * @retval 0 OK
*/ */
int int
xml_value_set(cxobj *xn, char *val) xml_value_set(cxobj *xn,
char *val)
{ {
if (xn->x_value){ if (xn->x_value){
free(xn->x_value); free(xn->x_value);
@ -377,7 +384,8 @@ xml_child_each(cxobj *xparent,
* Note: does not do anything with child, you may need to set its parent, etc * Note: does not do anything with child, you may need to set its parent, etc
*/ */
static int static int
xml_child_append(cxobj *x, cxobj *xc) xml_child_append(cxobj *x,
cxobj *xc)
{ {
x->x_childvec_len++; x->x_childvec_len++;
x->x_childvec = realloc(x->x_childvec, x->x_childvec_len*sizeof(cxobj*)); x->x_childvec = realloc(x->x_childvec, x->x_childvec_len*sizeof(cxobj*));
@ -397,7 +405,8 @@ xml_child_append(cxobj *x, cxobj *xc)
* @endcode * @endcode
*/ */
int int
xml_childvec_set(cxobj *x, int len) xml_childvec_set(cxobj *x,
int len)
{ {
x->x_childvec_len = len; x->x_childvec_len = len;
if ((x->x_childvec = calloc(len, sizeof(cxobj*))) == NULL){ if ((x->x_childvec = calloc(len, sizeof(cxobj*))) == NULL){
@ -416,7 +425,8 @@ xml_childvec_set(cxobj *x, int len)
* @retval NULL if error and clicon_err() called * @retval NULL if error and clicon_err() called
*/ */
cxobj * cxobj *
xml_new(char *name, cxobj *xp) xml_new(char *name,
cxobj *xp)
{ {
cxobj *xn; cxobj *xn;
@ -438,7 +448,9 @@ xml_new(char *name, cxobj *xp)
/*! Create new xml node given a name, parent and spec. Free it with xml_free(). /*! Create new xml node given a name, parent and spec. Free it with xml_free().
*/ */
cxobj * cxobj *
xml_new_spec(char *name, cxobj *xp, void *spec) xml_new_spec(char *name,
cxobj *xp,
void *spec)
{ {
cxobj *x; cxobj *x;
@ -518,7 +530,8 @@ xml_addsub(cxobj *xp,
* The name of the function is somewhat misleading * The name of the function is somewhat misleading
*/ */
cxobj * cxobj *
xml_insert(cxobj *xp, char *tag) xml_insert(cxobj *xp,
char *tag)
{ {
cxobj *xc; /* new child */ cxobj *xc; /* new child */
@ -666,7 +679,8 @@ xml_body(cxobj *xn)
* See also xml_find_body * See also xml_find_body
*/ */
char * char *
xml_find_value(cxobj *x_up, char *name) xml_find_value(cxobj *x_up,
char *name)
{ {
cxobj *x; cxobj *x;
@ -684,7 +698,8 @@ xml_find_value(cxobj *x_up, char *name)
* @see xml_find_value * @see xml_find_value
*/ */
char * char *
xml_find_body(cxobj *xn, char *name) xml_find_body(cxobj *xn,
char *name)
{ {
cxobj *x; cxobj *x;
@ -826,7 +841,8 @@ clicon_xml2cbuf(cbuf *cb,
* @see clicon_xml_parse_file clicon_xml_parse_string * @see clicon_xml_parse_file clicon_xml_parse_string
*/ */
static int static int
xml_parse(char **str, cxobj *x_up) xml_parse(char **str,
cxobj *x_up)
{ {
int retval = -1; int retval = -1;
struct xml_parse_yacc_arg ya = {0,}; struct xml_parse_yacc_arg ya = {0,};
@ -853,7 +869,9 @@ xml_parse(char **str, cxobj *x_up)
* FSM to detect a substring * FSM to detect a substring
*/ */
static inline int static inline int
FSM(char *tag, char ch, int state) FSM(char *tag,
char ch,
int state)
{ {
if (tag[state] == ch) if (tag[state] == ch)
return state+1; return state+1;
@ -977,7 +995,8 @@ clicon_xml_parse_string(char **str,
/*! Copy single xml node without copying children /*! Copy single xml node without copying children
*/ */
static int static int
copy_one(cxobj *xn0, cxobj *xn1) copy_one(cxobj *xn0,
cxobj *xn1)
{ {
xml_type_set(xn1, xml_type(xn0)); xml_type_set(xn1, xml_type(xn0));
if (xml_value(xn0)){ /* malloced string */ if (xml_value(xn0)){ /* malloced string */
@ -1002,7 +1021,8 @@ copy_one(cxobj *xn0, cxobj *xn1)
* @endcode * @endcode
*/ */
int int
xml_copy(cxobj *x0, cxobj *x1) xml_copy(cxobj *x0,
cxobj *x1)
{ {
int retval = -1; int retval = -1;
cxobj *x; cxobj *x;
@ -1164,3 +1184,93 @@ xml_apply_ancestor(cxobj *xn,
done: done:
return retval; return retval;
} }
/*! Generic parse function for xml values
* @param[in] xb xml tree body node, ie containing a value to be parsed
* @param[in] type Type of value to be parsed in value
* @param[out] cvp CLIgen variable containing the parsed value
* @note free cv with cv_free after use.
* @see xml_body_int32 etc, for type-specific parse functions
*/
int
xml_body_parse(cxobj *xb,
enum cv_type type,
cg_var **cvp)
{
int retval = -1;
cg_var *cv = NULL;
int cvret;
char *bstr;
char *reason = NULL;
if ((bstr = xml_body(xb)) == NULL){
clicon_err(OE_XML, 0, "No body found");
goto done;
}
if ((cv = cv_new(type)) == NULL){
clicon_err(OE_XML, errno, "cv_new");
goto done;
}
if ((cvret = cv_parse1(bstr, cv, &reason)) < 0){
clicon_err(OE_XML, errno, "cv_parse");
goto done;
}
if (cvret == 0){ /* parsing failed */
clicon_err(OE_XML, errno, "Parsing CV: %s", &reason);
if (reason)
free(reason);
}
*cvp = cv;
retval = 0;
done:
if (retval < 0 && cv != NULL)
cv_free(cv);
return retval;
}
/*! Parse an xml body as int32
* The real parsing functions are in the cligen code
* @param[in] xb xml tree body node, ie containing a value to be parsed
* @param[out] val Value after parsing
* @retval 0 OK, parsed value in 'val'
* @retval -1 Error, one of: body not found, parse error,
* alloc error.
* @note extend to all other cligen var types and generalize
* @note use yang type info?
*/
int
xml_body_int32(cxobj *xb,
int32_t *val)
{
cg_var *cv = NULL;
if (xml_body_parse(xb, CGV_INT32, &cv) < 0)
return -1;
*val = cv_int32_get(cv);
cv_free(cv);
return 0;
}
/*! Parse an xml body as uint32
* The real parsing functions are in the cligen code
* @param[in] xb xml tree body node, ie containing a value to be parsed
* @param[out] val Value after parsing
* @retval 0 OK, parsed value in 'val'
* @retval -1 Error, one of: body not found, parse error,
* alloc error.
* @note extend to all other cligen var types and generalize
* @note use yang type info?
*/
int
xml_body_uint32(cxobj *xb,
uint32_t *val)
{
cg_var *cv = NULL;
if (xml_body_parse(xb, CGV_UINT32, &cv) < 0)
return -1;
*val = cv_uint32_get(cv);
cv_free(cv);
return 0;
}

View file

@ -137,6 +137,8 @@ yang2xmlkeyfmt_1(yang_stmt *ys, cbuf *cb)
} /* switch */ } /* switch */
retval = 0; retval = 0;
done: done:
if (cvk)
cvec_free(cvk);
return retval; return retval;
} }
@ -154,8 +156,10 @@ yang2xmlkeyfmt(yang_stmt *ys, char **xkfmt)
int retval = -1; int retval = -1;
cbuf *cb = NULL; cbuf *cb = NULL;
if ((cb = cbuf_new()) == NULL) if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done; goto done;
}
if (yang2xmlkeyfmt_1(ys, cb) < 0) if (yang2xmlkeyfmt_1(ys, cb) < 0)
goto done; goto done;
if ((*xkfmt = strdup(cbuf_get(cb))) == NULL){ if ((*xkfmt = strdup(cbuf_get(cb))) == NULL){
@ -208,8 +212,10 @@ xmlkeyfmt2key(char *xkfmt,
// goto done; // goto done;
} }
#endif #endif
if ((cb = cbuf_new()) == NULL) if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done; goto done;
}
j = 1; /* j==0 is cli string */ j = 1; /* j==0 is cli string */
for (i=0; i<strlen(xkfmt); i++){ for (i=0; i<strlen(xkfmt); i++){
c = xkfmt[i]; c = xkfmt[i];
@ -282,8 +288,10 @@ xmlkeyfmt2xpath(char *xkfmt,
// goto done; // goto done;
} }
#endif #endif
if ((cb = cbuf_new()) == NULL) if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done; goto done;
}
j = 1; /* j==0 is cli string */ j = 1; /* j==0 is cli string */
for (i=0; i<strlen(xkfmt); i++){ for (i=0; i<strlen(xkfmt); i++){
c = xkfmt[i]; c = xkfmt[i];
@ -325,7 +333,7 @@ xmlkeyfmt2xpath(char *xkfmt,
cprintf(cb, "%c", c); cprintf(cb, "%c", c);
} }
} }
if ((*xk = strdup(cbuf_get(cb))) == NULL){ if ((*xk = strdup4(cbuf_get(cb))) == NULL){
clicon_err(OE_UNIX, errno, "strdup"); clicon_err(OE_UNIX, errno, "strdup");
goto done; goto done;
} }
@ -419,7 +427,7 @@ db2file(clicon_handle h,
goto done; goto done;
} }
cprintf(cb, "%s/%s_db", dir, db); cprintf(cb, "%s/%s_db", dir, db);
if ((*filename = strdup(cbuf_get(cb))) == NULL){ if ((*filename = strdup4(cbuf_get(cb))) == NULL){
clicon_err(OE_UNIX, errno, "strdup"); clicon_err(OE_UNIX, errno, "strdup");
goto done; goto done;
} }
@ -472,43 +480,29 @@ append_listkeys(cbuf *ckey,
} }
/*! Help function to create xml key values /*! Help function to create xml key values
* @param[in] x * @param[in] x Parent
* @param[in] xc0
* @param[in] y * @param[in] y
* @param[in] keyname yang key name * @param[in] keyname yang key name
*/ */
static int static int
create_keyvalues(cxobj *x, create_keyvalues(cxobj *x,
yang_stmt *y,
yang_stmt *ykey, yang_stmt *ykey,
char *name,
char *arg, char *arg,
char *keyname, char *keyname)
cxobj **xcp)
{ {
int retval = -1; int retval = -1;
cbuf *cpath = NULL;
cxobj *xb; cxobj *xb;
cxobj *xc = NULL;
if ((cpath = cbuf_new()) == NULL)
goto done;
cprintf(cpath, "%s[%s=%s]", name, keyname, arg);
/* Check if key node exists */ /* Check if key node exists */
if ((xc = xpath_first(x, cbuf_get(cpath)))==NULL){ if ((xb = xml_new_spec(keyname, x, ykey)) == NULL)
if ((xc = xml_new_spec(name, x, y)) == NULL)
goto done;
if ((xb = xml_new_spec(keyname, xc, ykey)) == NULL)
goto done; goto done;
if ((xb = xml_new("body", xb)) == NULL) if ((xb = xml_new("body", xb)) == NULL)
goto done; goto done;
xml_type_set(xb, CX_BODY); xml_type_set(xb, CX_BODY);
xml_value_set(xb, arg); xml_value_set(xb, arg);
}
retval = 0; retval = 0;
done: done:
*xcp = xc;
if (cpath)
cbuf_free(cpath);
return retval; return retval;
} }
@ -582,6 +576,7 @@ get(char *dbname,
cvec *cvk = NULL; /* vector of index keys */ cvec *cvk = NULL; /* vector of index keys */
char *keyname; char *keyname;
char *arg; char *arg;
cbuf *cb;
x = xt; x = xt;
if (xk == NULL || *xk!='/'){ if (xk == NULL || *xk!='/'){
@ -645,26 +640,47 @@ get(char *dbname,
/* The value is a list of keys: <key>[ <key>]* */ /* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL) if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done; goto done;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
cvi = NULL; cvi = NULL;
/* Iterate over individual yang keys */ /* Iterate over individual yang keys */
while ((cvi = cvec_each(cvk, cvi)) != NULL) { cprintf(cb, "%s", name);
keyname = cv_string_get(cvi); while ((cvi = cvec_each(cvk, cvi)) != NULL){
i++; i++;
if (i>=nvec){ if (i>=nvec){
clicon_err(OE_XML, errno, "List %s without argument", name); clicon_err(OE_XML, errno, "List %s without argument", name);
goto done; goto done;
} }
arg = vec[i]; arg = vec[i];
if (create_keyvalues(x, cprintf(cb, "[%s=%s]", cv_string_get(cvi), arg);
y, }
ykey, if ((xc = xpath_first(x, cbuf_get(cb))) == NULL){
name, if ((xc = xml_new_spec(name, x, y)) == NULL)
arg, goto done;
keyname, cvi = NULL;
&xc) < 0) i -= cvec_len(cvk);
/* Iterate over individual yang keys */
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
i++;
arg = vec[i];
if (create_keyvalues(xc,
ykey,
arg,
keyname) < 0)
goto done; goto done;
x = xc;
} /* while */ } /* while */
}
if (cb){
cbuf_free(cb);
cb = NULL;
}
if (cvk){
cvec_free(cvk);
cvk = NULL;
}
break; break;
case Y_LEAF: case Y_LEAF:
case Y_CONTAINER: case Y_CONTAINER:
@ -690,6 +706,8 @@ get(char *dbname,
retval = 0; retval = 0;
done: done:
unchunk_group(__FUNCTION__); unchunk_group(__FUNCTION__);
if (cvk)
cvec_free(cvk);
return retval; return retval;
} }
@ -705,15 +723,15 @@ xml_sanity(cxobj *x,
ys = (yang_stmt*)xml_spec(x); ys = (yang_stmt*)xml_spec(x);
if (ys==NULL){ if (ys==NULL){
clicon_err(OE_XML, 0, "No spec for xml node %s", xml_name(x)); clicon_err(OE_XML, 0, "No spec for xml node %s", xml_name(x));
return -1; goto done;
} }
if (strcmp(xml_name(x), ys->ys_argument)){ if (strstr(ys->ys_argument, xml_name(x))==NULL){
clicon_err(OE_XML, 0, "xml node name '%s' does not match yang spec arg '%s'", clicon_err(OE_XML, 0, "xml node name '%s' does not match yang spec arg '%s'",
xml_name(x), ys->ys_argument); xml_name(x), ys->ys_argument);
return -1; goto done;
} }
retval = 0; retval = 0;
// done: done:
return retval; return retval;
} }
@ -1063,8 +1081,10 @@ put(char *dbname,
if (get_operation(xt, &op) < 0) if (get_operation(xt, &op) < 0)
goto done; goto done;
body = xml_body(xt); body = xml_body(xt);
if ((cbxk = cbuf_new()) == NULL) if ((cbxk = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done; goto done;
}
cprintf(cbxk, "%s/%s", xk0, xml_name(xt)); cprintf(cbxk, "%s/%s", xk0, xml_name(xt));
switch (ys->ys_keyword){ switch (ys->ys_keyword){
case Y_LIST: /* Note: can have many keys */ case Y_LIST: /* Note: can have many keys */
@ -1231,10 +1251,14 @@ xmldb_put_xkey_local(clicon_handle h,
clicon_err(OE_DB, 0, "Invalid key: %s", xk); clicon_err(OE_DB, 0, "Invalid key: %s", xk);
goto done; goto done;
} }
if ((ckey = cbuf_new()) == NULL) if ((ckey = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done; goto done;
if ((csubkey = cbuf_new()) == NULL) }
if ((csubkey = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done; goto done;
}
if ((vec = clicon_strsplit(xk, "/", &nvec, __FUNCTION__)) == NULL) if ((vec = clicon_strsplit(xk, "/", &nvec, __FUNCTION__)) == NULL)
goto done; goto done;
if (nvec < 2){ if (nvec < 2){
@ -1304,6 +1328,10 @@ xmldb_put_xkey_local(clicon_handle h,
if (db_set(filename, cbuf_get(csubkey), val2, strlen(val2)+1) < 0) if (db_set(filename, cbuf_get(csubkey), val2, strlen(val2)+1) < 0)
goto done; goto done;
} }
if (cvk){
cvec_free(cvk);
cvk = NULL;
}
break; break;
default: default:
if (op == OP_MERGE || op == OP_REPLACE || op == OP_CREATE) if (op == OP_MERGE || op == OP_REPLACE || op == OP_CREATE)
@ -1341,8 +1369,10 @@ xmldb_put_xkey_local(clicon_handle h,
} }
case OP_REMOVE: case OP_REMOVE:
/* Read in complete database (this can be optimized) */ /* Read in complete database (this can be optimized) */
if ((crx = cbuf_new()) == NULL) if ((crx = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done; goto done;
}
cprintf(crx, "^%s.*$", xk); cprintf(crx, "^%s.*$", xk);
if ((npairs = db_regexp(filename, cbuf_get(crx), __FUNCTION__, &pairs, 0)) < 0) if ((npairs = db_regexp(filename, cbuf_get(crx), __FUNCTION__, &pairs, 0)) < 0)
goto done; goto done;
@ -1364,16 +1394,18 @@ xmldb_put_xkey_local(clicon_handle h,
cbuf_free(csubkey); cbuf_free(csubkey);
if (crx) if (crx)
cbuf_free(crx); cbuf_free(crx);
if (cvk)
cvec_free(cvk);
unchunk_group(__FUNCTION__); unchunk_group(__FUNCTION__);
return retval; return retval;
} }
/*! Modify database provided an XML database key and an operation /*! Modify database provided an XML database key and an operation
* @param[in] dbname Name of database to search in (filename including dir path) * @param[in] h CLICON handle
* @param[in] db Database name
* @param[in] xk XML Key, eg /aa/bb/17/name * @param[in] xk XML Key, eg /aa/bb/17/name
* @param[in] val Key value, eg "17" * @param[in] val Key value, eg "17"
* @param[in] yspec Yang specification
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc * @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
@ -1719,7 +1751,7 @@ main(int argc, char **argv)
yangmod = argv[4]; yangmod = argv[4];
db_init(db); db_init(db);
if ((yspec = yspec_new()) == NULL) if ((yspec = yspec_new()) == NULL)
goto done; goto done
if (yang_parse(h, yangdir, yangmod, NULL, yspec) < 0) if (yang_parse(h, yangdir, yangmod, NULL, yspec) < 0)
goto done; goto done;
if (strcmp(cmd, "get")==0){ if (strcmp(cmd, "get")==0){

View file

@ -513,6 +513,7 @@ xml2cvec(cxobj *xt,
/*! Translate a cligen variable vector to an XML tree with depth one /*! Translate a cligen variable vector to an XML tree with depth one
* @param[in] cvv CLIgen variable vector. Should be freed by cvec_free() * @param[in] cvv CLIgen variable vector. Should be freed by cvec_free()
* @param[in] toptag The XML tree in xt will have this XML tag * @param[in] toptag The XML tree in xt will have this XML tag
* @param[in] xt Parent, or NULL
* @param[out] xt Pointer to XML tree containing one top node. Should be freed with xml_free * @param[out] xt Pointer to XML tree containing one top node. Should be freed with xml_free
* @retval 0 Everything OK, cvv allocated and set * @retval 0 Everything OK, cvv allocated and set
* @retval -1 Something wrong, clicon_err() called to set error. No xt returned * @retval -1 Something wrong, clicon_err() called to set error. No xt returned
@ -522,6 +523,7 @@ xml2cvec(cxobj *xt,
int int
cvec2xml_1(cvec *cvv, cvec2xml_1(cvec *cvv,
char *toptag, char *toptag,
cxobj *xp,
cxobj **xt0) cxobj **xt0)
{ {
int retval = -1; int retval = -1;
@ -536,7 +538,7 @@ cvec2xml_1(cvec *cvv,
cv = NULL; cv = NULL;
while ((cv = cvec_each(cvv, cv)) != NULL) while ((cv = cvec_each(cvv, cv)) != NULL)
len++; len++;
if ((xt = xml_new(toptag, NULL)) == NULL) if ((xt = xml_new(toptag, xp)) == NULL)
goto err; goto err;
if (xml_childvec_set(xt, len) < 0) if (xml_childvec_set(xt, len) < 0)
goto err; goto err;
@ -672,6 +674,10 @@ xml_diff1(yang_stmt *ys,
else else
if (cxvec_append(x1, first, firstlen) < 0) if (cxvec_append(x1, first, firstlen) < 0)
goto done; goto done;
if (cvk){
cvec_free(cvk);
cvk = NULL;
}
break; break;
case Y_CONTAINER: case Y_CONTAINER:
/* Equal regardless */ /* Equal regardless */
@ -764,6 +770,10 @@ xml_diff1(yang_stmt *ys,
if (!equal) if (!equal)
if (cxvec_append(x2, second, secondlen) < 0) if (cxvec_append(x2, second, secondlen) < 0)
goto done; goto done;
if (cvk){
cvec_free(cvk);
cvk = NULL;
}
break; break;
case Y_CONTAINER: case Y_CONTAINER:
/* Equal regardless */ /* Equal regardless */
@ -788,6 +798,8 @@ xml_diff1(yang_stmt *ys,
} /* while xt1 */ } /* while xt1 */
retval = 0; retval = 0;
done: done:
if (cvk)
cvec_free(cvk);
return retval; return retval;
} }

View file

@ -23,6 +23,8 @@
* different xpath expressions. * different xpath expressions.
*/ */
/* /*
https://www.w3.org/TR/xpath/
Implementation of a limited xslt xpath syntax. Some examples. Given the following Implementation of a limited xslt xpath syntax. Some examples. Given the following
xml tree: xml tree:
<aaa> <aaa>
@ -58,6 +60,15 @@ to the xml standards:
<ddd><ccc>22</ccc></ddd> (NB spaces) <ddd><ccc>22</ccc></ddd> (NB spaces)
etc etc
For xpath v1.0 see http://www.w3.org/TR/xpath/ 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>
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -121,11 +132,16 @@ static const struct map_str2int atmap[] = {
{NULL, -1} {NULL, -1}
}; };
struct xpath_predicate{
struct xpath_predicate *xp_next;
char *xp_expr;
};
struct xpath_element{ struct xpath_element{
struct xpath_element *xe_next; struct xpath_element *xe_next;
enum axis_type xe_type; enum axis_type xe_type;
char *xe_str; /* eg for child */ char *xe_str; /* eg for child */
char *xe_predicate; /* eg within [] */ struct xpath_predicate *xe_predicate; /* eg within [] */
}; };
static int xpath_split(char *xpathstr, char **pathexpr); static int xpath_split(char *xpathstr, char **pathexpr);
@ -147,14 +163,54 @@ static int
xpath_print(FILE *f, struct xpath_element *xplist) xpath_print(FILE *f, struct xpath_element *xplist)
{ {
struct xpath_element *xe; struct xpath_element *xe;
struct xpath_predicate *xp;
for (xe=xplist; xe; xe=xe->xe_next) for (xe=xplist; xe; xe=xe->xe_next){
fprintf(f, "\t:%s %s %s\n", axis_type2str(xe->xe_type), fprintf(f, "\t:%s %s ", axis_type2str(xe->xe_type),
xe->xe_str?xe->xe_str:"", xe->xe_str?xe->xe_str:"");
xe->xe_predicate?xe->xe_predicate:""); for (xp=xe->xe_predicate; xp; xp=xp->xp_next)
fprintf(f, "[%s]", xp->xp_expr);
}
return 0; return 0;
} }
static int
xpath_parse_predicate(struct xpath_element *xe,
char *pred)
{
int retval = -1;
struct xpath_predicate *xp;
char *s;
int i;
int len;
len = strlen(pred);
for (i=len-2; i>=0; i--){ /* -2 since we search for ][ */
s = &pred[i];
if (i==0 ||
(*(s)==']' && *(s+1)=='[')){
if (i) {
*(s)= '\0';
s += 2;
}
if ((xp = malloc(sizeof(*xp))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(xp, 0, sizeof(*xp));
if ((xp->xp_expr = strdup(s)) == NULL){
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
goto done;
}
xp->xp_next = xe->xe_predicate;
xe->xe_predicate = xp;
}
}
retval = 0;
done:
return retval;
}
static int static int
xpath_element_new(enum axis_type atype, xpath_element_new(enum axis_type atype,
char *str, char *str,
@ -176,7 +232,7 @@ xpath_element_new(enum axis_type atype,
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__); clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
goto done; goto done;
} }
if (xpath_split(str1, &pred) < 0) if (xpath_split(str1, &pred) < 0) /* Can be more predicates */
goto done; goto done;
if (strlen(str1)){ if (strlen(str1)){
if ((xe->xe_str = strdup(str1)) == NULL){ if ((xe->xe_str = strdup(str1)) == NULL){
@ -190,8 +246,8 @@ xpath_element_new(enum axis_type atype,
goto done; goto done;
} }
} }
if (pred && strlen(pred) && (xe->xe_predicate = strdup(pred)) == NULL){ if (pred && strlen(pred)){
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__); if (xpath_parse_predicate(xe, pred) < 0)
goto done; goto done;
} }
} }
@ -207,10 +263,16 @@ xpath_element_new(enum axis_type atype,
static int static int
xpath_element_free(struct xpath_element *xe) xpath_element_free(struct xpath_element *xe)
{ {
struct xpath_predicate *xp;
if (xe->xe_str) if (xe->xe_str)
free(xe->xe_str); free(xe->xe_str);
if (xe->xe_predicate) while ((xp = xe->xe_predicate) != NULL){
free(xe->xe_predicate); xe->xe_predicate = xp->xp_next;
if (xp->xp_expr)
free(xp->xp_expr);
free(xp);
}
free(xe); free(xe);
return 0; return 0;
} }
@ -231,7 +293,8 @@ xpath_free(struct xpath_element *xplist)
* // is short for /descendant-or-self::node()/ * // is short for /descendant-or-self::node()/
*/ */
static int static int
xpath_parse(char *xpath, struct xpath_element **xplist0) xpath_parse(char *xpath,
struct xpath_element **xplist0)
{ {
int retval = -1; int retval = -1;
int nvec = 0; int nvec = 0;
@ -334,8 +397,21 @@ recursive_find(cxobj *xn,
return retval; return retval;
} }
/*! XPath predicate expression check
* @param[in] predicate_expression xpath expression as a string
* @param[in] flags Extra xml flag checks that must match (apart from predicate)
* @param[in,out] vec0 Vector or xml nodes that are checked. Not matched are filtered
* @param[in,out] vec0len Length of vector or matches
* On input, vec0 contains a list of xml nodes to match.
* On output, vec0 contains only the subset that matched the epxression.
* The predicate expression is a subset of the standard, namely:
* - @<attr>=<value>
* - <number>
* - <name>=<value>
* @see https://www.w3.org/TR/xpath/#predicates
*/
static int static int
xpath_expr(char *e00, xpath_expr(char *predicate_expression,
uint16_t flags, uint16_t flags,
cxobj ***vec0, cxobj ***vec0,
size_t *vec0len) size_t *vec0len)
@ -354,7 +430,7 @@ xpath_expr(char *e00,
char *e0; char *e0;
char *e; char *e;
if ((e0 = strdup(e00)) == NULL){ if ((e0 = strdup(predicate_expression)) == NULL){
clicon_err(OE_UNIX, errno, "strdup"); clicon_err(OE_UNIX, errno, "strdup");
goto done; goto done;
} }
@ -436,6 +512,7 @@ xpath_expr(char *e00,
* @param[in] descendants0 * @param[in] descendants0
* @param[in] vec0 * @param[in] vec0
* @param[in] vec0len * @param[in] vec0len
* @param[in] flags
* @param[out] vec1 * @param[out] vec1
* @param[out] vec1len * @param[out] vec1len
* XXX: Kommer in i funktionen med vec0, resultatet appendas i vec1 * XXX: Kommer in i funktionen med vec0, resultatet appendas i vec1
@ -463,6 +540,7 @@ xpath_find(struct xpath_element *xe,
int descendants = 0; int descendants = 0;
cxobj **vec1 = NULL; cxobj **vec1 = NULL;
size_t vec1len = 0; size_t vec1len = 0;
struct xpath_predicate *xp;
if (xe == NULL){ if (xe == NULL){
/* append */ /* append */
@ -543,9 +621,11 @@ xpath_find(struct xpath_element *xe,
} }
} }
} }
if (xe->xe_predicate)
if (xpath_expr(xe->xe_predicate, flags, &vec0, &vec0len) < 0) for (xp = xe->xe_predicate; xp; xp = xp->xp_next){
if (xpath_expr(xp->xp_expr, flags, &vec0, &vec0len) < 0)
goto done; goto done;
}
if (xpath_find(xe->xe_next, descendants, if (xpath_find(xe->xe_next, descendants,
vec0, vec0len, flags, vec0, vec0len, flags,
vec2, vec2len) < 0) vec2, vec2len) < 0)
@ -555,13 +635,19 @@ xpath_find(struct xpath_element *xe,
return retval; return retval;
} }
/*! Transform eg "a/b[kalle]" -> "a/b" e="kalle" */ /*! Transform eg "a/b[kalle]" -> "a/b" e="kalle"
* @param[in,out] xpathstr Eg "a/b[kalle]" -> "a/b"
* @param[out] pathexpr Eg "kalle"
* Which also means:
* "a/b[foo][bar]" -> pathexpr: "foo][bar"
* @note destructively modify xpathstr, no new strings allocated
*/
static int static int
xpath_split(char *xpathstr, char **pathexpr) xpath_split(char *xpathstr,
char **pathexpr)
{ {
int retval = -1; int retval = -1;
int last; int last;
int i;
char *pe = NULL; char *pe = NULL;
if (strlen(xpathstr)){ if (strlen(xpathstr)){
@ -569,12 +655,9 @@ xpath_split(char *xpathstr, char **pathexpr)
if (xpathstr[last] == ']'){ if (xpathstr[last] == ']'){
xpathstr[last] = '\0'; xpathstr[last] = '\0';
if (strlen(xpathstr)){ if (strlen(xpathstr)){
last = strlen(xpathstr) - 1; /* recompute due to null */ if ((pe = index(xpathstr,'[')) != NULL){
for (i=last; i>=0; i--){ *pe = '\0';
if (xpathstr[i] == '['){ pe++;
xpathstr[i] = '\0';
pe = &xpathstr[i+1];
break;
} }
} }
if (pe==NULL){ if (pe==NULL){
@ -583,7 +666,6 @@ xpath_split(char *xpathstr, char **pathexpr)
} }
} }
} }
}
retval = 0; retval = 0;
done: done:
*pathexpr = pe; *pathexpr = pe;
@ -594,6 +676,7 @@ xpath_split(char *xpathstr, char **pathexpr)
* @param[in] xpath * @param[in] xpath
* @param[in] vec0 * @param[in] vec0
* @param[in] vec0len * @param[in] vec0len
* @param[in] flags
* @param[out] vec * @param[out] vec
* @param[out] veclen * @param[out] veclen
*/ */
@ -778,20 +861,20 @@ xpath_each(cxobj *cxtop,
* @param[in] cxtop xml-tree where to search * @param[in] cxtop xml-tree where to search
* @param[in] xpath string with XPATH syntax * @param[in] xpath string with XPATH syntax
* @param[out] vec vector of xml-trees. Vector must be free():d after use * @param[out] vec vector of xml-trees. Vector must be free():d after use
* @param[out] xv_len returns length of vector in return value * @param[out] veclen returns length of vector in return value
* @retval 0 OK * @retval 0 OK
* @retval -1 error. * @retval -1 error.
* *
* @code * @code
* cxobj **xv; * cxobj **vec;
* size_t xlen; * size_t veclen;
* if (xpath_vec(cxtop, "//symbol/foo", &xv, &xlen) < 0) * if (xpath_vec(cxtop, "//symbol/foo", &vec, &veclen) < 0)
* got err; * got err;
* for (i=0; i<xlen; i++){ * for (i=0; i<veclen; i++){
* xn = xv[i]; * xn = vec[i];
* ... * ...
* } * }
* free(xv); * free(vec);
* @endcode * @endcode
* Note that although the returned vector must be freed after use, the returned xml * Note that although the returned vector must be freed after use, the returned xml
* trees need not be. * trees need not be.
@ -825,10 +908,12 @@ xpath_vec_flag(cxobj *cxtop,
/* /*
* Turn this on to get an xpath test program * Turn this on to get an xpath test program
* Usage: clicon_xpath [<xpath>] * Usage: xpath [<xpath>]
* read xml from input * read xml from input
* Example compile: * Example compile:
gcc -g -o xpath -I. -I../clicon ./clicon_xsl.c -lclicon -lcligen gcc -g -o xpath -I. -I../clixon ./clixon_xsl.c -lclixon -lcligen
* Example run:
echo "<a><b/></a>" | xpath "a"
*/ */
#if 0 /* Test program */ #if 0 /* Test program */
@ -847,7 +932,7 @@ main(int argc, char **argv)
cxobj **xv; cxobj **xv;
cxobj *x; cxobj *x;
cxobj *xn; cxobj *xn;
int xlen = 0; size_t xlen = 0;
if (argc != 2){ if (argc != 2){
usage(argv[0]); usage(argv[0]);
@ -860,8 +945,8 @@ main(int argc, char **argv)
printf("\n"); printf("\n");
if (xpath_vec(x, argv[1], &xv, &xlen) < 0) if (xpath_vec(x, argv[1], &xv, &xlen) < 0)
goto done; return -1;
if (xv) if (xv){
for (i=0; i<xlen; i++){ for (i=0; i<xlen; i++){
xn = xv[i]; xn = xv[i];
fprintf(stdout, "[%d]:\n", i); fprintf(stdout, "[%d]:\n", i);

View file

@ -1947,7 +1947,7 @@ yang_spec_main(clicon_handle h,
* @param[in] ys Yang statement * @param[in] ys Yang statement
* @param[in] delimiter Delimiter character (eg ' ' or ',') * @param[in] delimiter Delimiter character (eg ' ' or ',')
* @retval NULL Error * @retval NULL Error
* @retval cvec Vector of strings * @retval cvec Vector of strings. Free with cvec_free()
* @code * @code
* cvec *cvv; * cvec *cvv;
* cg_var *cv = NULL; * cg_var *cv = NULL;
@ -1960,7 +1960,8 @@ yang_spec_main(clicon_handle h,
* Note: must free return value after use w cvec_free * Note: must free return value after use w cvec_free
*/ */
cvec * cvec *
yang_arg2cvec(yang_stmt *ys, char *delim) yang_arg2cvec(yang_stmt *ys,
char *delim)
{ {
char **vec; char **vec;
int i; int i;