/*! CLI generic callback printing the variable vector and argument */ int callback(cligen_handle handle, cvec *cvv, cvec *argv) { int i = 1; cg_var *cv; char buf[64]; fprintf(stderr, "function: %s\n", cligen_fn_str_get(handle)); fprintf(stderr, "variables:\n"); cv = NULL; while ((cv = cvec_each1(cvv, cv)) != NULL) { cv2str(cv, buf, sizeof(buf)-1); fprintf(stderr, "\t%d name:%s type:%s value:%s\n", i++, cv_name_get(cv), cv_type2str(cv_type_get(cv)), buf ); } if (argv){ cv = NULL; i=0; while ((cv = cvec_each(argv, cv)) != NULL) { cv2str(cv, buf, sizeof(buf)-1); fprintf(stderr, "arg %d: %s\n", i++, buf); } } return 0; }
/*! Delete a cv variable from a cvec. Note: cv is not reset & cv may be stale! * * @param[in] cvv Cligen variable vector * @param[in] del variable to delete * * @note This is a dangerous command since the cv it deletes (such as created by * cvec_add) may have been modified with realloc (eg cvec_add/delete) and * therefore can not be used as a reference. Safer methods are to use * cvec_find/cvec_i to find a cv and then to immediately remove it. */ int cvec_del(cvec *cvv, cg_var *del) { int i; cg_var *cv; if (cvec_len(cvv) == 0) return 0; i = 0; cv = NULL; while ((cv = cvec_each(cvv, cv)) != NULL) { if (cv == del) break; i++; } if (i >= cvec_len(cvv)) /* Not found !?! */ return cvec_len(cvv); if (i != cvec_len(cvv)-1) /* If not last entry, move the remaining cv's */ memmove(&cvv->vr_vec[i], &cvv->vr_vec[i+1], (cvv->vr_len-i-1) * sizeof(cvv->vr_vec[0])); cvv->vr_len--; cvv->vr_vec = realloc(cvv->vr_vec, cvv->vr_len*sizeof(cvv->vr_vec[0])); /* Shrink should not fail? */ return cvec_len(cvv); }
/*! Call expand callback and insert expanded commands in place of variable * variable argument callback variant * @see pt_expand_fn */ static int pt_expand_fnv(cligen_handle h, cg_obj *co, cvec *cvv, parse_tree *ptn, cg_obj *parent) { int retval = -1; cvec *commands = cvec_new(0); cvec *helptexts = cvec_new(0); cg_var *cv = NULL; char *helpstr; cg_obj *con; int i; if ((*co->co_expandv_fn)( cligen_userhandle(h)?cligen_userhandle(h):h, co->co_expand_fn_str, cvv, co->co_expand_fn_vec, commands, helptexts) < 0) goto done; i = 0; while ((cv = cvec_each(commands, cv)) != NULL) { if (i < cvec_len(helptexts)){ helpstr = strdup(cv_string_get(cvec_i(helptexts, i))); } else helpstr = NULL; i++; pt_realloc(ptn); if (co_expand_sub(co, parent, &ptn->pt_vec[ptn->pt_len-1]) < 0) goto done; con = ptn->pt_vec[ptn->pt_len-1]; if (transform_var_to_cmd(con, strdup(cv_string_get(cv)), helpstr) < 0) goto done; } if (commands) cvec_free(commands); if (helptexts) cvec_free(helptexts); retval = 0; done: return retval; }
/*! Reset cligen variable vector resetting it to an initial state as returned by cvec_new * * @param[in] cvv Cligen variable vector * @see also cvec_free. But this function does not actually free the cvec. */ int cvec_reset(cvec *cvv) { cg_var *cv = NULL; while ((cv = cvec_each(cvv, cv)) != NULL) cv_reset(cv); if (cvv->vr_vec) free(cvv->vr_vec); if (cvv->vr_name) free(cvv->vr_name); memset(cvv, 0, sizeof(*cvv)); return 0; }
/*! Recursively copy callback structure to all terminal nodes in the parse-tree. * XXX: Actually copies to every node, even if not terminal * Problem is that the argument is modified according to reference rule * @param[in] pt Parse-tree * @param[in] cc0 This is the parameter (calling) callback. eg fn in @sub,fn(). * * The function installs the calling callback in all executable non-terminal nodes. * That is, it only install callbacks in non-terminal with NULL child which is * where a ';' is in the syntax, eg: * a ;{} # Here a is executable * b {} # But b is not * Somewhat strange semantics though: * - Always replace (or add if empty) original callback in co0 * - Use local argument-list _unless_ there is none, then use callback list from cc0 */ static int pt_callback_reference(parse_tree pt, struct cg_callback *cc0) { int i; cg_obj *co; parse_tree *ptc; int retval = -1; struct cg_callback *cc; cg_var *cv; for (i=0; i<pt.pt_len; i++){ if ((co = pt.pt_vec[i]) == NULL) continue; ptc = &co->co_pt; /* Filter out non-executable non-terminals. */ if (ptc->pt_len && ptc->pt_vec[0] == NULL){ /* Copy the callback from top */ if ((cc = co->co_callbacks) == NULL){ if (co_callback_copy(cc0, &co->co_callbacks) < 0) return -1; } else { #ifdef CALLBACK_SINGLEARG cc->cc_fn = cc0->cc_fn; /* iterate */ #endif cc->cc_fn_vec = cc0->cc_fn_vec; /* */ if (cc0->cc_fn_str){ if (cc->cc_fn_str) free (cc->cc_fn_str); cc->cc_fn_str = strdup(cc0->cc_fn_str); } /* Append original parameters to end of call */ if (cc0->cc_cvec){ cv = NULL; while ((cv = cvec_each(cc0->cc_cvec, cv)) != NULL) cvec_append_var(cc->cc_cvec, cv); } } } if (pt_callback_reference(co->co_pt, cc0) < 0) goto done; } retval = 0; done: return retval; }
/* * generic_validate * * key values are checked for validity independent of user-defined callbacks * They are checked as follows: * 1. If no value and default value defined, add it. * 2. If no value and mandatory flag set in spec, report error. * 3. Validate value versus spec, and report error if no match. Currently only int ranges and * string regexp checked. */ static int generic_validate(clicon_handle h, char *dbname, const struct dbdiff *dd) { int i, j; char *key; cvec *cvec = NULL; cg_var *cv; cg_varspec *cs; int retval = -1; cg_obj *co; cg_obj *cov; parse_tree *dbspec_co; char *reason = NULL; parse_tree *pt; if ((dbspec_co = clicon_dbspec_pt(h)) == NULL) goto done; /* dd->df_ents[].dfe_key1 (running), dd->df_ents[].dfe_key2 (candidate) */ for (i = 0; i < dd->df_nr; i++) { if ((key = dd->df_ents[i].dfe_key2) == NULL) continue; if ((co = key2spec_co(dbspec_co, key)) == NULL) continue; /* read variable list from db */ if ((cvec = dbkey2cvec(dbname, key)) == NULL) goto done; /* Loop over all co:s children (spec) and check if actual values in db(cv) satisfies them */ pt = &co->co_pt; for (j=0; j<pt->pt_len; j++){ if ((cov = pt->pt_vec[j]) == NULL) continue; if (cov->co_type == CO_VARIABLE){ /* There is no db-value, but dbspec has default value */ if ((cv = dbspec_default_get(cov)) != NULL && cvec_find(cvec, cov->co_command) == NULL){ cv_flag_set(cv, V_DEFAULT); /* mark it as default XXX not survive DB */ /* add default value to cvec */ if (cvec_add_cv(cvec, cv) < 0){ clicon_err(OE_CFG, 0, "cvec_add_cv"); goto done; } /* Write to database */ if (cvec2dbkey(dbname, key, cvec) < 0) goto done; } else if (!dbspec_optional_get(cov) && cvec_find(cvec, cov->co_command) == NULL){ clicon_err(OE_CFG, 0, "key %s: Missing mandatory variable: %s\n", key, cov->co_command); goto done; } } } cv = NULL; /* Loop over all actual db/cv:s och check their validity */ while ((cv = cvec_each(cvec, cv))) { if ((cov = co_find_one(*pt, cv_name_get(cv))) == NULL){ clicon_err(OE_CFG, 0, "key %s: variable %s not found in co-spec", key, cv_name_get(cv)); goto done; } if ((cs = co2varspec(cov)) == NULL) continue; if (cv_validate(cv, cs, &reason) != 1){ /* We ignore errors */ clicon_err(OE_DB, 0, "key %s: validation of %s failed\n", key, cov->co_command); goto done; } } if (cvec){ cvec_free(cvec); cvec = NULL; } } /* for */ retval = 0; done: if (cvec) cvec_free(cvec); if (reason) free(reason); return retval; }