diff --git a/CHANGELOG.md b/CHANGELOG.md index c65ce124..316c9cad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,7 @@ Developers may need to change their code ### Minor features +* Refactored cli-syntax code to use cligen pt_head instead (long overdue) * Modified backend exit strategy so that 2nd ^C actually exits * Performance: A change in the `merge` code made "co-located" config and non-config get retrieval go considerable faster. This is done by a specialized `xml_child_each_attr()` function. * CLI: Added `show statistics` example code for backend and CLI memory stats diff --git a/apps/cli/cli_handle.c b/apps/cli/cli_handle.c index 003efd68..b8f2a3cf 100644 --- a/apps/cli/cli_handle.c +++ b/apps/cli/cli_handle.c @@ -87,7 +87,6 @@ struct cli_handle { /* ------ end of common handle ------ */ cligen_handle cl_cligen; /* cligen handle */ - cli_syntax_t *cl_stx; /* CLI syntax structure */ }; /*! Return a clicon handle for other CLICON API calls @@ -121,14 +120,9 @@ int cli_handle_exit(clicon_handle h) { cligen_handle ch = cligen(h); - struct cli_handle *cl = handle(h); - if (cl->cl_stx) - free(cl->cl_stx); clicon_handle_exit(h); /* frees h and options */ - cligen_exit(ch); - return 0; } @@ -136,28 +130,6 @@ cli_handle_exit(clicon_handle h) * cli-specific handle access functions *----------------------------------------------------------*/ -/*! Get current syntax-group */ -cli_syntax_t * -cli_syntax(clicon_handle h) -{ - struct cli_handle *cl = handle(h); - return cl->cl_stx; -} - -/*! Set current syntax-group */ -int -cli_syntax_set(clicon_handle h, - cli_syntax_t *stx) -{ - struct cli_handle *cl = handle(h); - - if (cl->cl_stx) - free(cl->cl_stx); - cl->cl_stx = stx; - return 0; -} - - /*! Return clicon handle */ cligen_handle cli_cligen(clicon_handle h) diff --git a/apps/cli/cli_handle.h b/apps/cli/cli_handle.h index 8c71f5f0..4370b7aa 100644 --- a/apps/cli/cli_handle.h +++ b/apps/cli/cli_handle.h @@ -57,9 +57,4 @@ int cli_prompt_set(clicon_handle h, char *prompt); int cli_logsyntax_set(clicon_handle h, int status); -/* Internal functions for handling cli groups */ -cli_syntax_t *cli_syntax(clicon_handle h); - -int cli_syntax_set(clicon_handle h, cli_syntax_t *stx); - #endif /* _CLI_HANDLE_H_ */ diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index 535d9d3b..9b73ce53 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -239,13 +239,18 @@ cli_interactive(clicon_handle h) char *new_mode; cligen_result result; int ret; + pt_head *ph; /* Loop through all commands */ while(!cligen_exiting(cli_cligen(h))) { - new_mode = cli_syntax_mode(h); + if ((ph = cligen_pt_head_active_get(cli_cligen(h))) == NULL){ + clicon_err(OE_UNIX, 0, "active tree not found"); + goto done; + } + new_mode = cligen_ph_name_get(ph); cmd = NULL; /* Read a CLI string, including expand handling */ - if ((ret = clicon_cliread(h, &cmd)) < 0) + if ((ret = clicon_cliread(h, ph, &cmd)) < 0) goto done; if (ret == 0) continue; @@ -686,7 +691,7 @@ main(int argc, { char *dir; /* Load cli .so plugins before yangs are loaded (eg extension callbacks) and - * before CLI is loaded by cli_syntax_load below */ + * before CLI is loaded by clispec_load below */ if ((dir = clicon_cli_dir(h)) != NULL && clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir, NULL) < 0) goto done; @@ -752,7 +757,7 @@ main(int argc, /* Initialize cli syntax. * Plugins have already been loaded by clixon_plugins_load above */ - if (cli_syntax_load(h) < 0) + if (clispec_load(h) < 0) goto done; /* Set syntax mode if specified from command-line or config-file. */ diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c index 3906955e..9aceb00e 100644 --- a/apps/cli/cli_plugin.c +++ b/apps/cli/cli_plugin.c @@ -33,7 +33,6 @@ ***** END LICENSE BLOCK ***** - * */ #ifdef HAVE_CONFIG_H @@ -80,117 +79,34 @@ * */ -/*! Find syntax mode named 'mode'. Create if specified - */ -static cli_syntaxmode_t * -syntax_mode_find(cli_syntax_t *stx, - const char *mode, - int create) -{ - cli_syntaxmode_t *csm; - - csm = stx->stx_modes; - if (csm) { - do { - if (strcmp(csm->csm_name, mode) == 0) - return csm; - csm = NEXTQ(cli_syntaxmode_t *, csm); - } while (csm && csm != stx->stx_modes); - } - - if (create == 0) - return NULL; - - if ((csm = malloc(sizeof(cli_syntaxmode_t))) == NULL) { - clicon_err(OE_UNIX, errno, "malloc"); - return NULL; - } - memset(csm, 0, sizeof(*csm)); - if ((csm->csm_name = strdup(mode)) == NULL){ - clicon_err(OE_UNIX, errno, "strdup"); - return NULL; - } - if ((csm->csm_prompt = strdup(CLI_DEFAULT_PROMPT)) == NULL){ - clicon_err(OE_UNIX, errno, "strdup"); - return NULL; - } - if ((csm->csm_pt = pt_new()) == NULL){ - clicon_err(OE_UNIX, errno, "pt_new"); - return NULL; - } - INSQ(csm, stx->stx_modes); - stx->stx_nmodes++; - - return csm; -} - -/*! Generate parse tree for syntax mode +/*! Generate CLIgen parse tree for syntax mode + * * @param[in] h Clicon handle * @param[in] m Syntax mode struct */ static int -gen_parse_tree(clicon_handle h, - cli_syntaxmode_t *m) +gen_parse_tree(clicon_handle h, + char *name, + parse_tree *pt, + pt_head **php) { int retval = -1; pt_head *ph; - if ((ph = cligen_ph_add(cli_cligen(h), m->csm_name)) == NULL) + if ((ph = cligen_ph_add(cli_cligen(h), name)) == NULL) goto done; - if (cligen_ph_parsetree_set(ph, m->csm_pt) < 0) + if (cligen_ph_parsetree_set(ph, pt) < 0) goto done; + if (cligen_ph_prompt_set(ph, CLI_DEFAULT_PROMPT) < 0){ + clicon_err(OE_UNIX, errno, "cligen_ph_prompt_set"); + goto done; + } + *php = ph; retval = 0; done: return retval; } -/*! Append syntax - * @param[in] h Clicon handle - */ -static int -syntax_append(clicon_handle h, - cli_syntax_t *stx, - const char *name, - parse_tree *pt) -{ - cli_syntaxmode_t *csm; - - if ((csm = syntax_mode_find(stx, name, 1)) == NULL) - return -1; - if (cligen_parsetree_merge(csm->csm_pt, NULL, pt) < 0){ - clicon_err(OE_PLUGIN, errno, "cligen_parsetree_merge"); - return -1; - } - return 0; -} - -/*! Remove all cligen syntax modes - * @param[in] h Clicon handle - */ -static int -cli_syntax_unload(clicon_handle h) -{ - cli_syntax_t *stx = cli_syntax(h); - cli_syntaxmode_t *csm; - - if (stx == NULL) - return 0; - - while (stx->stx_nmodes > 0) { - csm = stx->stx_modes; - DELQ(csm, stx->stx_modes, cli_syntaxmode_t *); - if (csm){ - if (csm->csm_name) - free(csm->csm_name); - if (csm->csm_prompt) - free(csm->csm_prompt); - free(csm); - } - stx->stx_nmodes--; - } - return 0; -} - /*! Dynamic linking loader string to function mapper * * Maps strings from the CLI specification file to real funtions using dlopen @@ -247,7 +163,7 @@ clixon_str2fn(char *name, return NULL; } -/*! Load a file containing syntax and append to specified modes, also load C plugin +/*! Load a file containing clispec syntax and append to specified modes, also load C plugin * * First load CLIgen file, * Then find which .so to load by looking in the "CLICON_PLUGIN" variable in that file. @@ -257,13 +173,15 @@ clixon_str2fn(char *name, * @param[in] filename Name of file where syntax is specified (in syntax-group dir) * @param[in] dir Name of dir, or NULL * @param[out] ptall Universal CLIgen parse tree: apply to all modes + * @param[out] modes Keep track of all modes * @see clixon_plugins_load Where .so plugin code has been loaded prior to this */ static int -cli_load_syntax_file(clicon_handle h, - const char *filename, - const char *dir, - parse_tree *ptall) +clispec_load_file(clicon_handle h, + const char *filename, + const char *dir, + parse_tree *ptall, + cvec *modes) { void *handle = NULL; /* Handle to plugin .so module */ char *mode = NULL; /* Name of syntax mode to append new syntax */ @@ -274,8 +192,10 @@ cli_load_syntax_file(clicon_handle h, cvec *cvv = NULL; char *prompt = NULL; char **vec = NULL; - int i, nvec; + int i; + int nvec; char *plgnam; + pt_head *ph; #ifndef CLIXON_STATIC_PLUGINS clixon_plugin_t *cp; #endif @@ -299,7 +219,7 @@ cli_load_syntax_file(clicon_handle h, } /* Assuming this plugin is first in queue */ - if (cli_parse_file(h, f, filepath, pt, cvv) < 0){ + if (clispec_parse_file(h, f, filepath, NULL, pt, cvv) < 0){ clicon_err(OE_PLUGIN, 0, "failed to parse cli file %s", filepath); fclose(f); goto done; @@ -309,6 +229,7 @@ cli_load_syntax_file(clicon_handle h, * CLICON_MODE: which mode(s) this syntax applies to * CLICON_PROMPT: Cli prompt in this mode (see cli_prompt_get) * CLICON_PLUGIN: Name of C API plugin + * CLICON_PIPETREE: terminals are automatically expanded with this tree * Note: the base case is that it is: * (1) a single mode or * (2) "*" all modes or "m1:m2" - a list of modes @@ -330,6 +251,7 @@ cli_load_syntax_file(clicon_handle h, } } #endif + /* Resolve callback names to function pointers. */ if (cligen_callbackv_str2fn(pt, (cgv_str2fn_t*)clixon_str2fn, handle) < 0){ clicon_err(OE_PLUGIN, 0, "Mismatch between CLIgen file '%s' and CLI plugin file '%s'. Some possible errors:\n\t1. A function given in the CLIgen file does not exist in the plugin (ie link error)\n\t2. The CLIgen spec does not point to the correct plugin .so file (CLICON_PLUGIN=\"%s\" is wrong)", @@ -366,17 +288,42 @@ cli_load_syntax_file(clicon_handle h, } else { for (i = 0; i < nvec; i++) { - if (syntax_append(h, - cli_syntax(h), - vec[i], - pt) < 0) { + char *name = vec[i]; + parse_tree *ptnew = NULL; + cg_var *cv; + if ((ph = cligen_ph_find(cli_cligen(h), name)) == NULL){ + if ((ptnew = pt_new()) == NULL){ + clicon_err(OE_UNIX, errno, "pt_new"); + goto done; + } + if (gen_parse_tree(h, name, ptnew, &ph) < 0) + goto done; + if (ph == NULL) + goto done; + if ((cv = cv_new(CGV_STRING)) == NULL){ + clicon_err(OE_UNIX, errno, "cv_new"); + goto done; + } + cv_string_set(cv, name); + if (cvec_append_var(modes, cv) < 0){ + clicon_err(OE_UNIX, errno, "cvec_append_var"); + goto done; + } + if (cv) + cv_free(cv); + } + if (cligen_parsetree_merge(cligen_ph_parsetree_get(ph), NULL, pt) < 0){ + clicon_err(OE_PLUGIN, errno, "cligen_parsetree_merge"); goto done; } - if (prompt) - cli_set_prompt(h, vec[i], prompt); + if (prompt){ + if (cligen_ph_prompt_set(ph, prompt) < 0){ + clicon_err(OE_UNIX, errno, "cligen_ph_prompt_set"); + return -1; + } + } } } - cligen_parsetree_free(pt, 1); retval = 0; done: @@ -395,7 +342,7 @@ done: * XXX The parsetree loading needs a rewrite for multiple parse-trees */ int -cli_syntax_load(clicon_handle h) +clispec_load(clicon_handle h) { int retval = -1; char *clispec_dir = NULL; @@ -403,37 +350,29 @@ cli_syntax_load(clicon_handle h) int ndp; int i; struct dirent *dp = NULL; - cli_syntax_t *stx; - cli_syntaxmode_t *m; cligen_susp_cb_t *fns = NULL; cligen_interrupt_cb_t *fni = NULL; clixon_plugin_t *cp; parse_tree *ptall = NULL; /* Universal CLIgen parse tree all modes */ + cvec *modes = NULL; /* Keep track of created modes */ + pt_head *ph; + cg_var *cv = NULL; - /* Syntax already loaded. XXX should we re-load?? */ - if ((stx = cli_syntax(h)) != NULL) - return 0; if ((ptall = pt_new()) == NULL){ clicon_err(OE_UNIX, errno, "pt_new"); goto done; } - + if ((modes = cvec_new(0)) == NULL){ + clicon_err(OE_UNIX, errno, "cvec_new"); + goto done; + } /* Format plugin directory path */ clispec_dir = clicon_clispec_dir(h); clispec_file = clicon_option_str(h, "CLICON_CLISPEC_FILE"); - /* Allocate plugin group object */ - if ((stx = malloc(sizeof(*stx))) == NULL) { - clicon_err(OE_UNIX, errno, "malloc"); - goto done; - } - memset(stx, 0, sizeof(*stx)); /* Zero out all */ - - cli_syntax_set(h, stx); - /* Load single specific clispec file */ if (clispec_file){ - if (cli_load_syntax_file(h, clispec_file, NULL, ptall) < 0) + if (clispec_load_file(h, clispec_file, NULL, ptall, modes) < 0) goto done; } /* Load all clispec .cli files in directory */ @@ -443,33 +382,31 @@ cli_syntax_load(clicon_handle h) goto done; /* Load the syntax parse trees into cli_syntax stx structure */ for (i = 0; i < ndp; i++) { - clicon_debug(1, "DEBUG: Loading syntax '%.*s'", - (int)strlen(dp[i].d_name)-4, dp[i].d_name); - if (cli_load_syntax_file(h, dp[i].d_name, clispec_dir, ptall) < 0) + clicon_debug(CLIXON_DBG_DEFAULT, "Loading clispec syntax: '%s/%s'", + clispec_dir, dp[i].d_name); + if (clispec_load_file(h, dp[i].d_name, clispec_dir, ptall, modes) < 0) goto done; } } /* Were any syntax modes successfully loaded? If not, leave */ - if (stx->stx_nmodes <= 0) { - retval = 0; - goto done; - } - + if (cvec_len(modes) == 0) + goto ok; /* Go thorugh all modes and : * 1) Add the universal syntax * 2) add syntax tree (of those modes - "activate" syntax from stx to CLIgen) */ - m = stx->stx_modes; - do { - if (cligen_parsetree_merge(m->csm_pt, NULL, ptall) < 0){ + cv = NULL; + while ((cv = cvec_each(modes, cv)) != NULL){ + if ((ph = cligen_ph_find(cli_cligen(h), cv_string_get(cv))) == NULL) + continue; + + if (cligen_parsetree_merge(cligen_ph_parsetree_get(ph), + NULL, + ptall) < 0){ clicon_err(OE_PLUGIN, errno, "cligen_parsetree_merge"); goto done; } - if (gen_parse_tree(h, m) != 0) - goto done; - m = NEXTQ(cli_syntaxmode_t *, m); - } while (m && m != stx->stx_modes); - + } /* Set susp and interrupt callbacks into CLIgen */ cp = NULL; while ((cp = clixon_plugin_each(h, cp)) != NULL) { @@ -480,29 +417,24 @@ cli_syntax_load(clicon_handle h) if (cli_interrupt_hook(h, fni) < 0) goto done; } - + ok: /* All good. We can now proudly return a new group */ retval = 0; done: - if (retval != 0) { - cli_syntax_unload(h); - cli_syntax_set(h, NULL); - } cligen_parsetree_free(ptall, 1); + if (modes) + cvec_free(modes); if (dp) free(dp); return retval; } -/*! Remove syntax modes and remove syntax +/*! Free resources in plugin * @param[in] h Clicon handle */ int cli_plugin_finish(clicon_handle h) { - /* Remove all cligen syntax modes */ - cli_syntax_unload(h); - cli_syntax_set(h, NULL); return 0; } @@ -556,8 +488,6 @@ clicon_parse(clicon_handle h, int retval = -1; char *modename; int ret; - cli_syntax_t *stx = NULL; - cli_syntaxmode_t *csm; parse_tree *pt; /* Orig */ cg_obj *match_obj = NULL; cvec *cvv = NULL; @@ -565,24 +495,16 @@ clicon_parse(clicon_handle h, FILE *f; char *reason = NULL; cligen_handle ch; + pt_head *ph; ch = cli_cligen(h); if (clicon_get_logflags()&CLICON_LOG_STDOUT) f = stdout; else f = stderr; - stx = cli_syntax(h); - if ((modename = *modenamep) == NULL) { - csm = stx->stx_active_mode; - modename = csm->csm_name; - } - else { - if ((csm = syntax_mode_find(stx, modename, 0)) == NULL) { - fprintf(f, "Can't find syntax mode '%s'\n", modename); - goto done; - } - } - if (csm != NULL){ + modename = *modenamep; + ph = cligen_ph_find(cli_cligen(h), modename); + if (ph != NULL){ if (cligen_ph_active_set_byname(ch, modename) < 0){ fprintf(f, "No such parse-tree registered: %s\n", modename); goto done; @@ -743,7 +665,9 @@ cli_prompt_get(clicon_handle h, } /*! Read command from CLIgen's cliread() using current syntax mode. + * * @param[in] h Clicon handle + * @param[in] ph Parse-tree head * @param[out] stringp Pointer to command buffer or NULL on EOF * @retval 1 OK * @retval 0 Fail but continue @@ -751,36 +675,35 @@ cli_prompt_get(clicon_handle h, */ int clicon_cliread(clicon_handle h, + pt_head *ph, char **stringp) { int retval = -1; + char *name; char *pfmt = NULL; - cli_syntaxmode_t *mode; - cli_syntax_t *stx; cli_prompthook_t *fn; clixon_plugin_t *cp; char *promptstr; - stx = cli_syntax(h); - mode = stx->stx_active_mode; + name = cligen_ph_name_get(ph); /* Get prompt from plugin callback? */ cp = NULL; while ((cp = clixon_plugin_each(h, cp)) != NULL) { if ((fn = clixon_plugin_api_get(cp)->ca_prompt) == NULL) continue; - pfmt = fn(h, mode->csm_name); + pfmt = fn(h, name); break; } if (clicon_quiet_mode(h)) cli_prompt_set(h, ""); else{ - if ((promptstr = cli_prompt_get(h, pfmt ? pfmt : mode->csm_prompt)) == NULL) + if ((promptstr = cli_prompt_get(h, + pfmt?pfmt:cligen_ph_prompt_get(ph) + )) == NULL) goto done; cli_prompt_set(h, promptstr); free(promptstr); } - cligen_ph_active_set_byname(cli_cligen(h), mode->csm_name); - clicon_err_reset(); if (cliread(cli_cligen(h), stringp) < 0){ cli_handler_err(stdout); @@ -807,17 +730,19 @@ clicon_cliread(clicon_handle h, /*! Set syntax mode mode for existing current plugin group. * @param[in] h Clicon handle + * @retval 1 OK + * @retval 0 Not found / error */ int cli_set_syntax_mode(clicon_handle h, - const char *name) + char *name) { - cli_syntaxmode_t *mode; - - if ((mode = syntax_mode_find(cli_syntax(h), name, 1)) == NULL) + + pt_head *ph; + + if ((ph = cligen_ph_find(cli_cligen(h), name)) == NULL) return 0; - - cli_syntax(h)->stx_active_mode = mode; + cligen_pt_head_active_set(cli_cligen(h), ph); return 1; } @@ -827,38 +752,10 @@ cli_set_syntax_mode(clicon_handle h, char * cli_syntax_mode(clicon_handle h) { - cli_syntaxmode_t *csm; - - if ((csm = cli_syntax(h)->stx_active_mode) == NULL) - return NULL; - return csm->csm_name; -} - -/*! Callback from cli_set_prompt(). Set prompt format for syntax mode - * @param[in] h Clicon handle - * @param[in] name Name of syntax mode - * @param[in] prompt Prompt format - * @retval 0 OK - * @retval -1 Error - */ -int -cli_set_prompt(clicon_handle h, - const char *name, - const char *prompt) -{ - cli_syntaxmode_t *csm; - - if ((csm = syntax_mode_find(cli_syntax(h), name, 1)) == NULL) - return -1; + pt_head *ph; - if (csm->csm_prompt != NULL){ - free(csm->csm_prompt); - csm->csm_prompt = NULL; - } - if ((csm->csm_prompt = strdup(prompt)) == NULL){ - clicon_err(OE_UNIX, errno, "strdup"); - return -1; - } - return 0; + if ((ph = cligen_pt_head_active_get(cli_cligen(h))) == NULL) + return NULL; + return cligen_ph_name_get(ph); } diff --git a/apps/cli/cli_plugin.h b/apps/cli/cli_plugin.h index e4ade59b..0098e9fe 100644 --- a/apps/cli/cli_plugin.h +++ b/apps/cli/cli_plugin.h @@ -45,32 +45,12 @@ /* clicon generic callback pointer */ typedef void (clicon_callback_t)(clicon_handle h); -/* List of syntax modes - * XXX: syntax modes seem not needed, could be replaced by existing (new) cligen structures, such - * as pt_head and others. But code is arcane and difficult to modify. - */ -typedef struct { - qelem_t csm_qelem; /* List header */ - char *csm_name; /* Syntax mode name */ - char *csm_prompt; /* Prompt for mode */ - int csm_nsyntax; /* Num syntax specs registered by plugin */ - parse_tree *csm_pt; /* CLIgen parse tree */ -} cli_syntaxmode_t; - -/* Plugin group object. Just a single object, not list. part of cli_handle - */ -typedef struct { - int stx_nmodes; /* Number of syntax modes */ - cli_syntaxmode_t *stx_active_mode; /* Current active syntax mode */ - cli_syntaxmode_t *stx_modes; /* List of syntax modes */ -} cli_syntax_t; - void *clixon_str2fn(char *name, void *handle, char **error); int clicon_parse(clicon_handle h, char *cmd, char **mode, cligen_result *result, int *evalres); -int clicon_cliread(clicon_handle h, char **stringp); +int clicon_cliread(clicon_handle h, pt_head *ph, char **stringp); int cli_plugin_finish(clicon_handle h); diff --git a/apps/cli/clixon_cli_api.h b/apps/cli/clixon_cli_api.h index 63b9f70a..c7ff5e0e 100644 --- a/apps/cli/clixon_cli_api.h +++ b/apps/cli/clixon_cli_api.h @@ -62,11 +62,11 @@ typedef enum autocli_listkw autocli_listkw_t; * Function Declarations */ /* cli_plugin.c */ -int cli_set_syntax_mode(clicon_handle h, const char *mode); +int cli_set_syntax_mode(clicon_handle h, char *mode); char *cli_syntax_mode(clicon_handle h); -int cli_syntax_load(clicon_handle h); +int clispec_load(clicon_handle h); int cli_handler_err(FILE *fd); -int cli_set_prompt(clicon_handle h, const char *mode, const char *prompt); +int cli_set_prompt(clicon_handle h, char *mode, char *prompt); int cli_ptpush(clicon_handle h, char *mode, char *string, char *op); int cli_ptpop(clicon_handle h, char *mode, char *op);