double predicates in xpath; leaks; grideye debug
This commit is contained in:
parent
c1c1670a74
commit
741fb97a9f
13 changed files with 453 additions and 242 deletions
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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){
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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){
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue