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;
|
||||
char *str = NULL;
|
||||
char *xml = NULL;
|
||||
cxobj *xt;
|
||||
cxobj *xt = NULL;
|
||||
int piddb;
|
||||
|
||||
if (clicon_msg_xmlput_decode(msg,
|
||||
|
|
@ -283,6 +283,8 @@ from_client_xmlput(clicon_handle h,
|
|||
free(str);
|
||||
if (cvv)
|
||||
cvec_free (cvv);
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -112,51 +112,41 @@ generic_validate(yang_spec *yspec,
|
|||
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)
|
||||
/*! Common code of candidate_validate and candidate_commit
|
||||
*/
|
||||
static int
|
||||
validate_common(clicon_handle h,
|
||||
char *candidate,
|
||||
transaction_data_t *td)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
cxobj *xn;
|
||||
void *firsterr = NULL;
|
||||
yang_spec *yspec;
|
||||
transaction_data_t *td = NULL;
|
||||
int retval = -1;
|
||||
yang_spec *yspec;
|
||||
int i;
|
||||
cxobj *xn;
|
||||
|
||||
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
||||
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)
|
||||
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;
|
||||
/* 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 */
|
||||
|
|
@ -183,19 +173,45 @@ candidate_commit(clicon_handle h,
|
|||
|
||||
/* 4. Call plugin transaction start callbacks */
|
||||
if (plugin_transaction_begin(h, td) < 0)
|
||||
goto done;
|
||||
goto done;
|
||||
|
||||
/* 5. Make generic validation on all new or changed data. */
|
||||
if (generic_validate(yspec, td) < 0)
|
||||
goto done;
|
||||
goto done;
|
||||
|
||||
/* 6. Call plugin transaction validate callbacks */
|
||||
if (plugin_transaction_validate(h, td) < 0)
|
||||
goto done;
|
||||
if (plugin_transaction_validate(h, td) < 0)
|
||||
goto done;
|
||||
|
||||
/* 7. Call plugin transaction complete callbacks */
|
||||
if (plugin_transaction_complete(h, td) < 0)
|
||||
goto done;
|
||||
/* 7. Call plugin transaction complete callbacks */
|
||||
if (plugin_transaction_complete(h, td) < 0)
|
||||
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 */
|
||||
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");
|
||||
goto done;
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
done:
|
||||
/* In case of failure, call plugin transaction termination callbacks */
|
||||
|
|
@ -222,8 +237,6 @@ candidate_commit(clicon_handle h,
|
|||
plugin_transaction_abort(h, td);
|
||||
if (td)
|
||||
transaction_free(td);
|
||||
if (firsterr)
|
||||
clicon_err_restore(firsterr);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -237,73 +250,15 @@ candidate_validate(clicon_handle h,
|
|||
char *candidate)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_spec *yspec;
|
||||
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 */
|
||||
if ((td = transaction_new()) == NULL)
|
||||
goto done;
|
||||
|
||||
/* 2. Parse xml trees */
|
||||
if (xmldb_get(h, "running", "/", 0, &td->td_src, NULL, NULL) < 0)
|
||||
/* Common steps (with commit) */
|
||||
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;
|
||||
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
|
|||
|
|
@ -632,6 +632,15 @@ plugin_transaction_complete(clicon_handle h,
|
|||
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
|
||||
plugin_transaction_revert(clicon_handle h,
|
||||
transaction_data_t *td,
|
||||
|
|
@ -651,10 +660,11 @@ plugin_transaction_revert(clicon_handle h,
|
|||
tr.td_dvec = td->td_avec;
|
||||
tr.td_alen = td->td_dlen;
|
||||
tr.td_avec = td->td_dvec;
|
||||
tr.td_clen = td->td_clen;
|
||||
tr.td_scvec = td->td_tcvec;
|
||||
tr.td_tcvec = td->td_scvec;
|
||||
|
||||
for (i = nr-1; i; i--){
|
||||
for (i = nr-1; i>=0; i--){
|
||||
p = &plugins[i];
|
||||
if (p->p_trans_commit)
|
||||
if ((p->p_trans_commit)(h, (transaction_data)&tr) < 0){
|
||||
|
|
|
|||
|
|
@ -167,6 +167,7 @@ backend_notify_xml(clicon_handle h,
|
|||
cbuf *cb = NULL;
|
||||
struct handle_subscription *hs;
|
||||
|
||||
clicon_debug(1, "%s %s", __FUNCTION__, stream);
|
||||
/* Now go thru all clients(sessions), and all subscriptions and find matches */
|
||||
for (ce = backend_client_list(h); ce; ce = ce->ce_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 */
|
||||
/* XXX: x contains name==dk-ore, but filter is
|
||||
id==/[userid=d2d5e46c-c6f9-42f3-9a69-fb52fe60940d] */
|
||||
hs = NULL;
|
||||
while ((hs = subscription_each(h, hs)) != NULL){
|
||||
if (hs->hs_format != MSG_NOTIFY_XML)
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ cli_commit(clicon_handle h,
|
|||
"running",
|
||||
snapshot, /* snapshot */
|
||||
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;
|
||||
}
|
||||
retval = 0;
|
||||
|
|
@ -327,7 +327,7 @@ cli_validate(clicon_handle h, cvec *vars, cg_var *arg)
|
|||
int retval = -1;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -578,7 +578,7 @@ load_config_file(clicon_handle h,
|
|||
}
|
||||
filename = vecp[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));
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -901,7 +901,7 @@ cli_notification_register(clicon_handle h,
|
|||
if ((p = hash_value(cdat, logname, &len)) != NULL)
|
||||
s_exist = *(int*)p;
|
||||
|
||||
if (status){
|
||||
if (status){ /* start */
|
||||
if (s_exist!=-1){
|
||||
clicon_err(OE_PLUGIN, 0, "%s: result log socket already exists", __FUNCTION__);
|
||||
goto done;
|
||||
|
|
@ -913,7 +913,7 @@ cli_notification_register(clicon_handle h,
|
|||
if (hash_add(cdat, logname, &s, sizeof(s)) == NULL)
|
||||
goto done;
|
||||
}
|
||||
else{
|
||||
else{ /* stop */
|
||||
if (s_exist != -1){
|
||||
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_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 */
|
||||
|
|
|
|||
|
|
@ -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 xml_yang_validate(cxobj *xt, yang_stmt *ys) ;
|
||||
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,
|
||||
cxobj ***first, size_t *firstlen,
|
||||
cxobj ***second, size_t *secondlen,
|
||||
|
|
|
|||
|
|
@ -430,10 +430,11 @@ clicon_rpc_subscription(clicon_handle h,
|
|||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, NULL, NULL, s0, __FUNCTION__) < 0)
|
||||
goto done;
|
||||
if (status == 0){
|
||||
if (status == 0 && s0){
|
||||
close(*s0);
|
||||
*s0 = -1;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
unchunk_group(__FUNCTION__);
|
||||
return retval;
|
||||
|
|
|
|||
|
|
@ -90,7 +90,8 @@ xml_name(cxobj *xn)
|
|||
* @retval 0 OK
|
||||
*/
|
||||
int
|
||||
xml_name_set(cxobj *xn, char *name)
|
||||
xml_name_set(cxobj *xn,
|
||||
char *name)
|
||||
{
|
||||
if (xn->x_name){
|
||||
free(xn->x_name);
|
||||
|
|
@ -122,7 +123,8 @@ xml_namespace(cxobj *xn)
|
|||
* @retval 0 OK
|
||||
*/
|
||||
int
|
||||
xml_namespace_set(cxobj *xn, char *namespace)
|
||||
xml_namespace_set(cxobj *xn,
|
||||
char *namespace)
|
||||
{
|
||||
if (xn->x_namespace){
|
||||
free(xn->x_namespace);
|
||||
|
|
@ -153,7 +155,8 @@ xml_parent(cxobj *xn)
|
|||
* @retval 0 OK
|
||||
*/
|
||||
int
|
||||
xml_parent_set(cxobj *xn, cxobj *parent)
|
||||
xml_parent_set(cxobj *xn,
|
||||
cxobj *parent)
|
||||
{
|
||||
xn->x_up = parent;
|
||||
return 0;
|
||||
|
|
@ -164,7 +167,8 @@ xml_parent_set(cxobj *xn, cxobj *parent)
|
|||
* @retval flag Flags value, see XML_FLAG_*
|
||||
*/
|
||||
uint16_t
|
||||
xml_flag(cxobj *xn, uint16_t flag)
|
||||
xml_flag(cxobj *xn,
|
||||
uint16_t 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_*
|
||||
*/
|
||||
int
|
||||
xml_flag_set(cxobj *xn, uint16_t flag)
|
||||
xml_flag_set(cxobj *xn,
|
||||
uint16_t flag)
|
||||
{
|
||||
xn->x_flags |= flag;
|
||||
return 0;
|
||||
|
|
@ -185,7 +190,8 @@ xml_flag_set(cxobj *xn, uint16_t flag)
|
|||
* @param[in] flag Flags value to reset, see XML_FLAG_*
|
||||
*/
|
||||
int
|
||||
xml_flag_reset(cxobj *xn, uint16_t flag)
|
||||
xml_flag_reset(cxobj *xn,
|
||||
uint16_t flag)
|
||||
{
|
||||
xn->x_flags &= ~flag;
|
||||
return 0;
|
||||
|
|
@ -208,7 +214,8 @@ xml_value(cxobj *xn)
|
|||
* @retval 0 OK
|
||||
*/
|
||||
int
|
||||
xml_value_set(cxobj *xn, char *val)
|
||||
xml_value_set(cxobj *xn,
|
||||
char *val)
|
||||
{
|
||||
if (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
|
||||
*/
|
||||
static int
|
||||
xml_child_append(cxobj *x, cxobj *xc)
|
||||
xml_child_append(cxobj *x,
|
||||
cxobj *xc)
|
||||
{
|
||||
x->x_childvec_len++;
|
||||
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
|
||||
*/
|
||||
int
|
||||
xml_childvec_set(cxobj *x, int len)
|
||||
xml_childvec_set(cxobj *x,
|
||||
int len)
|
||||
{
|
||||
x->x_childvec_len = len;
|
||||
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
|
||||
*/
|
||||
cxobj *
|
||||
xml_new(char *name, cxobj *xp)
|
||||
xml_new(char *name,
|
||||
cxobj *xp)
|
||||
{
|
||||
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().
|
||||
*/
|
||||
cxobj *
|
||||
xml_new_spec(char *name, cxobj *xp, void *spec)
|
||||
xml_new_spec(char *name,
|
||||
cxobj *xp,
|
||||
void *spec)
|
||||
{
|
||||
cxobj *x;
|
||||
|
||||
|
|
@ -518,7 +530,8 @@ xml_addsub(cxobj *xp,
|
|||
* The name of the function is somewhat misleading
|
||||
*/
|
||||
cxobj *
|
||||
xml_insert(cxobj *xp, char *tag)
|
||||
xml_insert(cxobj *xp,
|
||||
char *tag)
|
||||
{
|
||||
cxobj *xc; /* new child */
|
||||
|
||||
|
|
@ -578,7 +591,7 @@ xml_purge(cxobj *xc)
|
|||
*/
|
||||
int
|
||||
xml_child_rm(cxobj *xp,
|
||||
int i)
|
||||
int i)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xc = NULL;
|
||||
|
|
@ -666,7 +679,8 @@ xml_body(cxobj *xn)
|
|||
* See also xml_find_body
|
||||
*/
|
||||
char *
|
||||
xml_find_value(cxobj *x_up, char *name)
|
||||
xml_find_value(cxobj *x_up,
|
||||
char *name)
|
||||
{
|
||||
cxobj *x;
|
||||
|
||||
|
|
@ -684,7 +698,8 @@ xml_find_value(cxobj *x_up, char *name)
|
|||
* @see xml_find_value
|
||||
*/
|
||||
char *
|
||||
xml_find_body(cxobj *xn, char *name)
|
||||
xml_find_body(cxobj *xn,
|
||||
char *name)
|
||||
{
|
||||
cxobj *x;
|
||||
|
||||
|
|
@ -826,7 +841,8 @@ clicon_xml2cbuf(cbuf *cb,
|
|||
* @see clicon_xml_parse_file clicon_xml_parse_string
|
||||
*/
|
||||
static int
|
||||
xml_parse(char **str, cxobj *x_up)
|
||||
xml_parse(char **str,
|
||||
cxobj *x_up)
|
||||
{
|
||||
int retval = -1;
|
||||
struct xml_parse_yacc_arg ya = {0,};
|
||||
|
|
@ -853,7 +869,9 @@ xml_parse(char **str, cxobj *x_up)
|
|||
* FSM to detect a substring
|
||||
*/
|
||||
static inline int
|
||||
FSM(char *tag, char ch, int state)
|
||||
FSM(char *tag,
|
||||
char ch,
|
||||
int state)
|
||||
{
|
||||
if (tag[state] == ch)
|
||||
return state+1;
|
||||
|
|
@ -977,7 +995,8 @@ clicon_xml_parse_string(char **str,
|
|||
/*! Copy single xml node without copying children
|
||||
*/
|
||||
static int
|
||||
copy_one(cxobj *xn0, cxobj *xn1)
|
||||
copy_one(cxobj *xn0,
|
||||
cxobj *xn1)
|
||||
{
|
||||
xml_type_set(xn1, xml_type(xn0));
|
||||
if (xml_value(xn0)){ /* malloced string */
|
||||
|
|
@ -1002,7 +1021,8 @@ copy_one(cxobj *xn0, cxobj *xn1)
|
|||
* @endcode
|
||||
*/
|
||||
int
|
||||
xml_copy(cxobj *x0, cxobj *x1)
|
||||
xml_copy(cxobj *x0,
|
||||
cxobj *x1)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x;
|
||||
|
|
@ -1164,3 +1184,93 @@ xml_apply_ancestor(cxobj *xn,
|
|||
done:
|
||||
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 */
|
||||
retval = 0;
|
||||
done:
|
||||
if (cvk)
|
||||
cvec_free(cvk);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -154,8 +156,10 @@ yang2xmlkeyfmt(yang_stmt *ys, char **xkfmt)
|
|||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if (yang2xmlkeyfmt_1(ys, cb) < 0)
|
||||
goto done;
|
||||
if ((*xkfmt = strdup(cbuf_get(cb))) == NULL){
|
||||
|
|
@ -208,8 +212,10 @@ xmlkeyfmt2key(char *xkfmt,
|
|||
// goto done;
|
||||
}
|
||||
#endif
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
j = 1; /* j==0 is cli string */
|
||||
for (i=0; i<strlen(xkfmt); i++){
|
||||
c = xkfmt[i];
|
||||
|
|
@ -282,8 +288,10 @@ xmlkeyfmt2xpath(char *xkfmt,
|
|||
// goto done;
|
||||
}
|
||||
#endif
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
j = 1; /* j==0 is cli string */
|
||||
for (i=0; i<strlen(xkfmt); i++){
|
||||
c = xkfmt[i];
|
||||
|
|
@ -325,7 +333,7 @@ xmlkeyfmt2xpath(char *xkfmt,
|
|||
cprintf(cb, "%c", c);
|
||||
}
|
||||
}
|
||||
if ((*xk = strdup(cbuf_get(cb))) == NULL){
|
||||
if ((*xk = strdup4(cbuf_get(cb))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -419,7 +427,7 @@ db2file(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
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");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -472,43 +480,29 @@ append_listkeys(cbuf *ckey,
|
|||
}
|
||||
|
||||
/*! Help function to create xml key values
|
||||
* @param[in] x
|
||||
* @param[in] x Parent
|
||||
* @param[in] xc0
|
||||
* @param[in] y
|
||||
* @param[in] keyname yang key name
|
||||
*/
|
||||
static int
|
||||
create_keyvalues(cxobj *x,
|
||||
yang_stmt *y,
|
||||
yang_stmt *ykey,
|
||||
char *name,
|
||||
char *arg,
|
||||
char *keyname,
|
||||
cxobj **xcp)
|
||||
char *keyname)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cpath = NULL;
|
||||
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 */
|
||||
if ((xc = xpath_first(x, cbuf_get(cpath)))==NULL){
|
||||
if ((xc = xml_new_spec(name, x, y)) == NULL)
|
||||
goto done;
|
||||
if ((xb = xml_new_spec(keyname, xc, ykey)) == NULL)
|
||||
goto done;
|
||||
if ((xb = xml_new("body", xb)) == NULL)
|
||||
goto done;
|
||||
xml_type_set(xb, CX_BODY);
|
||||
xml_value_set(xb, arg);
|
||||
}
|
||||
if ((xb = xml_new_spec(keyname, x, ykey)) == NULL)
|
||||
goto done;
|
||||
if ((xb = xml_new("body", xb)) == NULL)
|
||||
goto done;
|
||||
xml_type_set(xb, CX_BODY);
|
||||
xml_value_set(xb, arg);
|
||||
retval = 0;
|
||||
done:
|
||||
*xcp = xc;
|
||||
if (cpath)
|
||||
cbuf_free(cpath);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -582,6 +576,7 @@ get(char *dbname,
|
|||
cvec *cvk = NULL; /* vector of index keys */
|
||||
char *keyname;
|
||||
char *arg;
|
||||
cbuf *cb;
|
||||
|
||||
x = xt;
|
||||
if (xk == NULL || *xk!='/'){
|
||||
|
|
@ -593,7 +588,7 @@ get(char *dbname,
|
|||
/* Element 0 is NULL '/',
|
||||
Element 1 is top symbol and needs to find subs in all modules:
|
||||
spec->module->syntaxnode
|
||||
*/
|
||||
*/
|
||||
if (nvec < 2){
|
||||
clicon_err(OE_XML, 0, "Malformed key: %s", xk);
|
||||
goto done;
|
||||
|
|
@ -645,26 +640,47 @@ get(char *dbname,
|
|||
/* The value is a list of keys: <key>[ <key>]* */
|
||||
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
|
||||
goto done;
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cvi = NULL;
|
||||
/* Iterate over individual yang keys */
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
keyname = cv_string_get(cvi);
|
||||
cprintf(cb, "%s", name);
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL){
|
||||
i++;
|
||||
if (i>=nvec){
|
||||
clicon_err(OE_XML, errno, "List %s without argument", name);
|
||||
goto done;
|
||||
}
|
||||
arg = vec[i];
|
||||
if (create_keyvalues(x,
|
||||
y,
|
||||
ykey,
|
||||
name,
|
||||
arg,
|
||||
keyname,
|
||||
&xc) < 0)
|
||||
cprintf(cb, "[%s=%s]", cv_string_get(cvi), arg);
|
||||
}
|
||||
if ((xc = xpath_first(x, cbuf_get(cb))) == NULL){
|
||||
if ((xc = xml_new_spec(name, x, y)) == NULL)
|
||||
goto done;
|
||||
x = xc;
|
||||
} /* while */
|
||||
cvi = NULL;
|
||||
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;
|
||||
} /* while */
|
||||
}
|
||||
if (cb){
|
||||
cbuf_free(cb);
|
||||
cb = NULL;
|
||||
}
|
||||
if (cvk){
|
||||
cvec_free(cvk);
|
||||
cvk = NULL;
|
||||
}
|
||||
break;
|
||||
case Y_LEAF:
|
||||
case Y_CONTAINER:
|
||||
|
|
@ -690,6 +706,8 @@ get(char *dbname,
|
|||
retval = 0;
|
||||
done:
|
||||
unchunk_group(__FUNCTION__);
|
||||
if (cvk)
|
||||
cvec_free(cvk);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -705,15 +723,15 @@ xml_sanity(cxobj *x,
|
|||
ys = (yang_stmt*)xml_spec(x);
|
||||
if (ys==NULL){
|
||||
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'",
|
||||
xml_name(x), ys->ys_argument);
|
||||
return -1;
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
// done:
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -1063,8 +1081,10 @@ put(char *dbname,
|
|||
if (get_operation(xt, &op) < 0)
|
||||
goto done;
|
||||
body = xml_body(xt);
|
||||
if ((cbxk = cbuf_new()) == NULL)
|
||||
if ((cbxk = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cbxk, "%s/%s", xk0, xml_name(xt));
|
||||
switch (ys->ys_keyword){
|
||||
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);
|
||||
goto done;
|
||||
}
|
||||
if ((ckey = cbuf_new()) == NULL)
|
||||
if ((ckey = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
if ((csubkey = cbuf_new()) == NULL)
|
||||
}
|
||||
if ((csubkey = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if ((vec = clicon_strsplit(xk, "/", &nvec, __FUNCTION__)) == NULL)
|
||||
goto done;
|
||||
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)
|
||||
goto done;
|
||||
}
|
||||
if (cvk){
|
||||
cvec_free(cvk);
|
||||
cvk = NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (op == OP_MERGE || op == OP_REPLACE || op == OP_CREATE)
|
||||
|
|
@ -1341,8 +1369,10 @@ xmldb_put_xkey_local(clicon_handle h,
|
|||
}
|
||||
case OP_REMOVE:
|
||||
/* 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;
|
||||
}
|
||||
cprintf(crx, "^%s.*$", xk);
|
||||
if ((npairs = db_regexp(filename, cbuf_get(crx), __FUNCTION__, &pairs, 0)) < 0)
|
||||
goto done;
|
||||
|
|
@ -1364,16 +1394,18 @@ xmldb_put_xkey_local(clicon_handle h,
|
|||
cbuf_free(csubkey);
|
||||
if (crx)
|
||||
cbuf_free(crx);
|
||||
if (cvk)
|
||||
cvec_free(cvk);
|
||||
unchunk_group(__FUNCTION__);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! 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] val Key value, eg "17"
|
||||
* @param[in] yspec Yang specification
|
||||
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
|
|
@ -1719,7 +1751,7 @@ main(int argc, char **argv)
|
|||
yangmod = argv[4];
|
||||
db_init(db);
|
||||
if ((yspec = yspec_new()) == NULL)
|
||||
goto done;
|
||||
goto done
|
||||
if (yang_parse(h, yangdir, yangmod, NULL, yspec) < 0)
|
||||
goto done;
|
||||
if (strcmp(cmd, "get")==0){
|
||||
|
|
|
|||
|
|
@ -513,6 +513,7 @@ xml2cvec(cxobj *xt,
|
|||
/*! 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] 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
|
||||
* @retval 0 Everything OK, cvv allocated and set
|
||||
* @retval -1 Something wrong, clicon_err() called to set error. No xt returned
|
||||
|
|
@ -522,6 +523,7 @@ xml2cvec(cxobj *xt,
|
|||
int
|
||||
cvec2xml_1(cvec *cvv,
|
||||
char *toptag,
|
||||
cxobj *xp,
|
||||
cxobj **xt0)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -536,7 +538,7 @@ cvec2xml_1(cvec *cvv,
|
|||
cv = NULL;
|
||||
while ((cv = cvec_each(cvv, cv)) != NULL)
|
||||
len++;
|
||||
if ((xt = xml_new(toptag, NULL)) == NULL)
|
||||
if ((xt = xml_new(toptag, xp)) == NULL)
|
||||
goto err;
|
||||
if (xml_childvec_set(xt, len) < 0)
|
||||
goto err;
|
||||
|
|
@ -672,6 +674,10 @@ xml_diff1(yang_stmt *ys,
|
|||
else
|
||||
if (cxvec_append(x1, first, firstlen) < 0)
|
||||
goto done;
|
||||
if (cvk){
|
||||
cvec_free(cvk);
|
||||
cvk = NULL;
|
||||
}
|
||||
break;
|
||||
case Y_CONTAINER:
|
||||
/* Equal regardless */
|
||||
|
|
@ -764,6 +770,10 @@ xml_diff1(yang_stmt *ys,
|
|||
if (!equal)
|
||||
if (cxvec_append(x2, second, secondlen) < 0)
|
||||
goto done;
|
||||
if (cvk){
|
||||
cvec_free(cvk);
|
||||
cvk = NULL;
|
||||
}
|
||||
break;
|
||||
case Y_CONTAINER:
|
||||
/* Equal regardless */
|
||||
|
|
@ -788,6 +798,8 @@ xml_diff1(yang_stmt *ys,
|
|||
} /* while xt1 */
|
||||
retval = 0;
|
||||
done:
|
||||
if (cvk)
|
||||
cvec_free(cvk);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@
|
|||
* different xpath expressions.
|
||||
*/
|
||||
/*
|
||||
https://www.w3.org/TR/xpath/
|
||||
|
||||
Implementation of a limited xslt xpath syntax. Some examples. Given the following
|
||||
xml tree:
|
||||
<aaa>
|
||||
|
|
@ -58,6 +60,15 @@ to the xml standards:
|
|||
<ddd><ccc>22</ccc></ddd> (NB spaces)
|
||||
etc
|
||||
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 <stdlib.h>
|
||||
|
|
@ -121,11 +132,16 @@ static const struct map_str2int atmap[] = {
|
|||
{NULL, -1}
|
||||
};
|
||||
|
||||
struct xpath_predicate{
|
||||
struct xpath_predicate *xp_next;
|
||||
char *xp_expr;
|
||||
};
|
||||
|
||||
struct xpath_element{
|
||||
struct xpath_element *xe_next;
|
||||
enum axis_type xe_type;
|
||||
char *xe_str; /* eg for child */
|
||||
char *xe_predicate; /* eg within [] */
|
||||
struct xpath_element *xe_next;
|
||||
enum axis_type xe_type;
|
||||
char *xe_str; /* eg for child */
|
||||
struct xpath_predicate *xe_predicate; /* eg within [] */
|
||||
};
|
||||
|
||||
static int xpath_split(char *xpathstr, char **pathexpr);
|
||||
|
|
@ -146,24 +162,64 @@ axis_type2str(enum axis_type type)
|
|||
static int
|
||||
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)
|
||||
fprintf(f, "\t:%s %s %s\n", axis_type2str(xe->xe_type),
|
||||
xe->xe_str?xe->xe_str:"",
|
||||
xe->xe_predicate?xe->xe_predicate:"");
|
||||
for (xe=xplist; xe; xe=xe->xe_next){
|
||||
fprintf(f, "\t:%s %s ", axis_type2str(xe->xe_type),
|
||||
xe->xe_str?xe->xe_str:"");
|
||||
for (xp=xe->xe_predicate; xp; xp=xp->xp_next)
|
||||
fprintf(f, "[%s]", xp->xp_expr);
|
||||
}
|
||||
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
|
||||
xpath_element_new(enum axis_type atype,
|
||||
char *str,
|
||||
struct xpath_element ***xpnext)
|
||||
{
|
||||
int retval = -1;
|
||||
struct xpath_element *xe;
|
||||
char *str1 = NULL;
|
||||
char *pred;
|
||||
int retval = -1;
|
||||
struct xpath_element *xe;
|
||||
char *str1 = NULL;
|
||||
char *pred;
|
||||
|
||||
if ((xe = malloc(sizeof(*xe))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
|
|
@ -176,7 +232,7 @@ xpath_element_new(enum axis_type atype,
|
|||
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
|
||||
goto done;
|
||||
}
|
||||
if (xpath_split(str1, &pred) < 0)
|
||||
if (xpath_split(str1, &pred) < 0) /* Can be more predicates */
|
||||
goto done;
|
||||
if (strlen(str1)){
|
||||
if ((xe->xe_str = strdup(str1)) == NULL){
|
||||
|
|
@ -190,9 +246,9 @@ xpath_element_new(enum axis_type atype,
|
|||
goto done;
|
||||
}
|
||||
}
|
||||
if (pred && strlen(pred) && (xe->xe_predicate = strdup(pred)) == NULL){
|
||||
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
|
||||
goto done;
|
||||
if (pred && strlen(pred)){
|
||||
if (xpath_parse_predicate(xe, pred) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
(**xpnext) = xe;
|
||||
|
|
@ -207,10 +263,16 @@ xpath_element_new(enum axis_type atype,
|
|||
static int
|
||||
xpath_element_free(struct xpath_element *xe)
|
||||
{
|
||||
struct xpath_predicate *xp;
|
||||
|
||||
if (xe->xe_str)
|
||||
free(xe->xe_str);
|
||||
if (xe->xe_predicate)
|
||||
free(xe->xe_predicate);
|
||||
while ((xp = xe->xe_predicate) != NULL){
|
||||
xe->xe_predicate = xp->xp_next;
|
||||
if (xp->xp_expr)
|
||||
free(xp->xp_expr);
|
||||
free(xp);
|
||||
}
|
||||
free(xe);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -231,7 +293,8 @@ xpath_free(struct xpath_element *xplist)
|
|||
* // is short for /descendant-or-self::node()/
|
||||
*/
|
||||
static int
|
||||
xpath_parse(char *xpath, struct xpath_element **xplist0)
|
||||
xpath_parse(char *xpath,
|
||||
struct xpath_element **xplist0)
|
||||
{
|
||||
int retval = -1;
|
||||
int nvec = 0;
|
||||
|
|
@ -295,10 +358,10 @@ xpath_parse(char *xpath, struct xpath_element **xplist0)
|
|||
* @param[in] xn_parent Base XML object
|
||||
* @param[in] name shell wildcard pattern to match with node name
|
||||
* @param[in] node_type CX_ELMNT, CX_ATTR or CX_BODY
|
||||
* @param[in,out] vec1 internal buffers with results
|
||||
* @param[in,out] vec0 internal buffers with results
|
||||
* @param[in,out] vec_len internal buffers with length of vec0,vec1
|
||||
* @param[in,out] vec_max internal buffers with max of vec0,vec1
|
||||
* @param[in,out] vec1 internal buffers with results
|
||||
* @param[in,out] vec0 internal buffers with results
|
||||
* @param[in,out] vec_len internal buffers with length of vec0,vec1
|
||||
* @param[in,out] vec_max internal buffers with max of vec0,vec1
|
||||
* returns:
|
||||
* 0 on OK, -1 on error
|
||||
*/
|
||||
|
|
@ -334,8 +397,21 @@ recursive_find(cxobj *xn,
|
|||
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
|
||||
xpath_expr(char *e00,
|
||||
xpath_expr(char *predicate_expression,
|
||||
uint16_t flags,
|
||||
cxobj ***vec0,
|
||||
size_t *vec0len)
|
||||
|
|
@ -354,7 +430,7 @@ xpath_expr(char *e00,
|
|||
char *e0;
|
||||
char *e;
|
||||
|
||||
if ((e0 = strdup(e00)) == NULL){
|
||||
if ((e0 = strdup(predicate_expression)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -436,6 +512,7 @@ xpath_expr(char *e00,
|
|||
* @param[in] descendants0
|
||||
* @param[in] vec0
|
||||
* @param[in] vec0len
|
||||
* @param[in] flags
|
||||
* @param[out] vec1
|
||||
* @param[out] vec1len
|
||||
* XXX: Kommer in i funktionen med vec0, resultatet appendas i vec1
|
||||
|
|
@ -463,6 +540,7 @@ xpath_find(struct xpath_element *xe,
|
|||
int descendants = 0;
|
||||
cxobj **vec1 = NULL;
|
||||
size_t vec1len = 0;
|
||||
struct xpath_predicate *xp;
|
||||
|
||||
if (xe == NULL){
|
||||
/* 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;
|
||||
}
|
||||
if (xpath_find(xe->xe_next, descendants,
|
||||
vec0, vec0len, flags,
|
||||
vec2, vec2len) < 0)
|
||||
|
|
@ -555,13 +635,19 @@ xpath_find(struct xpath_element *xe,
|
|||
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
|
||||
xpath_split(char *xpathstr, char **pathexpr)
|
||||
xpath_split(char *xpathstr,
|
||||
char **pathexpr)
|
||||
{
|
||||
int retval = -1;
|
||||
int last;
|
||||
int i;
|
||||
char *pe = NULL;
|
||||
|
||||
if (strlen(xpathstr)){
|
||||
|
|
@ -569,19 +655,15 @@ xpath_split(char *xpathstr, char **pathexpr)
|
|||
if (xpathstr[last] == ']'){
|
||||
xpathstr[last] = '\0';
|
||||
if (strlen(xpathstr)){
|
||||
last = strlen(xpathstr) - 1; /* recompute due to null */
|
||||
for (i=last; i>=0; i--){
|
||||
if (xpathstr[i] == '['){
|
||||
xpathstr[i] = '\0';
|
||||
pe = &xpathstr[i+1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pe==NULL){
|
||||
clicon_err(OE_XML, errno, "%s: mismatched []: %s", __FUNCTION__, xpathstr);
|
||||
goto done;
|
||||
if ((pe = index(xpathstr,'[')) != NULL){
|
||||
*pe = '\0';
|
||||
pe++;
|
||||
}
|
||||
}
|
||||
if (pe==NULL){
|
||||
clicon_err(OE_XML, errno, "%s: mismatched []: %s", __FUNCTION__, xpathstr);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
|
|
@ -594,6 +676,7 @@ xpath_split(char *xpathstr, char **pathexpr)
|
|||
* @param[in] xpath
|
||||
* @param[in] vec0
|
||||
* @param[in] vec0len
|
||||
* @param[in] flags
|
||||
* @param[out] vec
|
||||
* @param[out] veclen
|
||||
*/
|
||||
|
|
@ -778,20 +861,20 @@ xpath_each(cxobj *cxtop,
|
|||
* @param[in] cxtop xml-tree where to search
|
||||
* @param[in] xpath string with XPATH syntax
|
||||
* @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 -1 error.
|
||||
*
|
||||
* @code
|
||||
* cxobj **xv;
|
||||
* size_t xlen;
|
||||
* if (xpath_vec(cxtop, "//symbol/foo", &xv, &xlen) < 0)
|
||||
* cxobj **vec;
|
||||
* size_t veclen;
|
||||
* if (xpath_vec(cxtop, "//symbol/foo", &vec, &veclen) < 0)
|
||||
* got err;
|
||||
* for (i=0; i<xlen; i++){
|
||||
* xn = xv[i];
|
||||
* for (i=0; i<veclen; i++){
|
||||
* xn = vec[i];
|
||||
* ...
|
||||
* }
|
||||
* free(xv);
|
||||
* free(vec);
|
||||
* @endcode
|
||||
* Note that although the returned vector must be freed after use, the returned xml
|
||||
* trees need not be.
|
||||
|
|
@ -825,10 +908,12 @@ xpath_vec_flag(cxobj *cxtop,
|
|||
|
||||
/*
|
||||
* Turn this on to get an xpath test program
|
||||
* Usage: clicon_xpath [<xpath>]
|
||||
* Usage: xpath [<xpath>]
|
||||
* read xml from input
|
||||
* 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 */
|
||||
|
||||
|
|
@ -847,7 +932,7 @@ main(int argc, char **argv)
|
|||
cxobj **xv;
|
||||
cxobj *x;
|
||||
cxobj *xn;
|
||||
int xlen = 0;
|
||||
size_t xlen = 0;
|
||||
|
||||
if (argc != 2){
|
||||
usage(argv[0]);
|
||||
|
|
@ -860,8 +945,8 @@ main(int argc, char **argv)
|
|||
printf("\n");
|
||||
|
||||
if (xpath_vec(x, argv[1], &xv, &xlen) < 0)
|
||||
goto done;
|
||||
if (xv)
|
||||
return -1;
|
||||
if (xv){
|
||||
for (i=0; i<xlen; i++){
|
||||
xn = xv[i];
|
||||
fprintf(stdout, "[%d]:\n", i);
|
||||
|
|
|
|||
|
|
@ -1947,7 +1947,7 @@ yang_spec_main(clicon_handle h,
|
|||
* @param[in] ys Yang statement
|
||||
* @param[in] delimiter Delimiter character (eg ' ' or ',')
|
||||
* @retval NULL Error
|
||||
* @retval cvec Vector of strings
|
||||
* @retval cvec Vector of strings. Free with cvec_free()
|
||||
* @code
|
||||
* cvec *cvv;
|
||||
* cg_var *cv = NULL;
|
||||
|
|
@ -1960,7 +1960,8 @@ yang_spec_main(clicon_handle h,
|
|||
* Note: must free return value after use w cvec_free
|
||||
*/
|
||||
cvec *
|
||||
yang_arg2cvec(yang_stmt *ys, char *delim)
|
||||
yang_arg2cvec(yang_stmt *ys,
|
||||
char *delim)
|
||||
{
|
||||
char **vec;
|
||||
int i;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue