/*! Go through tree and clean & delete all extra memory from pt_expand_2() * More specifically, delete all co_values and co_pt_exp. * @see pt_expand_2 */ int pt_expand_cleanup_2(parse_tree pt) { int i; cg_obj *co; if (pt.pt_vec == NULL) return 0; for (i=0; i<pt.pt_len; i++){ if ((co = pt.pt_vec[i]) != NULL){ if (co_value_set(co, NULL) < 0) return -1; if (co->co_pt_exp.pt_vec != NULL){ if (cligen_parsetree_free(co->co_pt_exp, 0) < 0) return -1; memset(&co->co_pt_exp, 0, sizeof(parse_tree)); } if (pt_expand_cleanup_2(co->co_pt) < 0) return -1; } } return 0; }
/* * match_pattern_node * Non-terminal. Need to match exact. * INPUT: * h CLIgen handle * string0 Input string to match * pt Vector of commands (array of cligen object pointers (cg_obj) * pt_max Length of the pt array * level How many levels (words) into string0 * use_pref Set this flag value if you want to use the preferences between * matches. It is only when you want a final exact match (not * completion or show options) that you should set this. * RETURNS: * The number of matches (0-n) in pt or -1 on error. See matchlen below. * OUTPUT: * ptp Returns the vector at the place of matching * matchv A vector of integers containing which * matchlen Length of matchv. That is, # of matches and same as return * value (if 0-n) * cvec cligen variable vector containing vars/values pair for completion * reason0 If retval = 0, this may be malloced to indicate reason for not * matching variables, if given. Need to be free:d */ static int match_pattern_node(cligen_handle h, char *string0, parse_tree pt, int level, int use_pref, int hide, pt_vec *ptp, int *matchv[], int *matchlen, cvec *cvec, char **reason0 ) { char *string = NULL; int i; int match; int matches = 0; int perfect = 0; int retval = -1; cg_obj *co, *co_match; cg_obj *co_orig; int rest_match = -1; int cmd_levels; int p; int preference = 0; int exact; char *reason; int findreason; parse_tree ptn={0,}; /* Expanded */ cg_var *cv = NULL; co_match = NULL; if (level > command_levels(string0)){ fprintf(stderr, "%s: level > command_level in %s\n", __FUNCTION__, string0); return -1; } /* If there are only variables in the list, then keep track of variable match errors */ findreason = 0; if (reason0) for (i=0; i<pt.pt_len; i++){ if ((co = pt.pt_vec[i]) == NULL) continue; if (co->co_type != CO_VARIABLE){ findreason = 0; break; } findreason++; } extract_substring(string0, level, &string); for (i=0; i<pt.pt_len; i++){ if ((co = pt.pt_vec[i]) == NULL) continue; reason = NULL; if ((match = match_object(string, co, &exact, findreason?&reason:NULL)) < 0) goto error; if (match){ assert(reason==NULL); /* Special case to catch rest variable and space delimited arguments after it */ if (co->co_type == CO_VARIABLE && co->co_vtype == CGV_REST) rest_match = i; if (match_perfect(string, co)){ if (!perfect){ matches = 0; perfect = 1; } } else{ if (perfect) break; if (1 || use_pref){ p = co_pref(co, exact); if (p < preference) continue; /* ignore */ if (p > preference){ preference = p; matches = 0; /* Start again at this level */ } } } co_match = co; matches++; } /* match == 0, co type is variable and findreason, then reason is set this may not be the best preference, we just set the first */ if (reason){ if (*reason0 == NULL) *reason0 = reason; reason = NULL; findreason = 0; } } /* for */ if (matches != 0 && reason0 && *reason0){ free(*reason0); *reason0 = NULL; } if (matches != 1) { #ifdef notneeded if (matches == 0){ cligen_nomatch_set(h, "Unrecognized command"); } else cligen_nomatch_set(h, "Ambigious command"); #endif retval = 0; goto quit; } assert(co_match); if ((cmd_levels = command_levels(string0)) < 0) goto error; /* co_orig is original object in case of expansion */ co_orig = co_match->co_ref?co_match->co_ref: co_match; if (pt_expand_1(h, co_match, &co_match->co_pt) < 0) /* sub-tree expansion */ goto error; if (co_match->co_type == CO_VARIABLE){ if ((cv = add_cov_to_cvec(co_match, string, cvec)) == NULL) goto error; } else if (co_match->co_type == CO_COMMAND && co_orig->co_type == CO_VARIABLE) if ((cv = add_cov_to_cvec(co_orig, string, cvec)) == NULL) goto error; if (pt_expand_2(h, &co_match->co_pt, cvec, &ptn, hide) < 0) /* expand/choice variables */ goto error; if (level+1 == cmd_levels) retval = match_pattern_terminal(h, string0, ptn, level+1, use_pref, ptp, matchv, matchlen, reason0); else retval = match_pattern_node(h, string0, ptn, level+1, use_pref, hide, ptp, matchv, matchlen, cvec, reason0); if (pt_expand_add(co_orig, ptn) < 0) /* add expanded ptn to orig parsetree */ goto error; if (co_match->co_type == CO_COMMAND && co_orig->co_type == CO_VARIABLE) if (co_value_set(co_orig, co_match->co_command) < 0) goto error; /* Cleanup made on top-level */ /* * Special case: we have matched a REST variable (anything) and * there is more text have this word, then we can match REST */ if (retval == 0 && rest_match != -1){ retval = 1; if (*matchlen < 1){ *matchlen = 1; if ((*matchv = realloc(*matchv, (*matchlen)*sizeof(int))) == NULL){ fprintf(stderr, "%s: realloc: %s\n", __FUNCTION__, strerror(errno)); return -1; } } else *matchlen = 1; *ptp = pt.pt_vec; (*matchv)[0] = rest_match; } quit: if (cv){ /* cv may be stale */ cv = cvec_i(cvec, cvec_len(cvec)-1); cv_reset(cv); cvec_del(cvec, cv); } /* Only the last level may have multiple matches */ if (string) free(string); return retval; error: retval = -1; goto quit; }
/*! Take a pattern and expand all <variables> with option 'choice' or 'expand'. * The pattern is expanded by examining the objects they point * to: those objects that are expand or choice variables * (eg <string expand:foo>) are transformed into a set of new commands * with a reference point back to the original. * @param[in] h cligen handle * @param[in] ptr original parse-tree consisting of a vector of cligen objects * @param[in] hide Respect hide setting of commands (dont show) * @param[in] expandvar Set if VARS should be expanded, eg ? <tab> * @param[out] ptn shadow parse-tree initially an empty pointer, its value is returned. */ int pt_expand_2(cligen_handle h, parse_tree *ptr, cvec *cvv, int hide, int expandvar, parse_tree *ptn) { int i; cg_obj *co; cg_obj *parent = NULL; int retval = -1; ptn->pt_len = 0; ptn->pt_vec = NULL; if (ptr->pt_vec == NULL) return 0; for (i=0; i<ptr->pt_len; i++){ /* Build ptn (new) from ptr (orig) */ if ((co = ptr->pt_vec[i]) != NULL){ if (co_value_set(co, NULL) < 0) goto done; if (hide && co->co_hide) continue; /* * Choice variable - Insert the static choices as commands in place * of the variable */ if (co->co_type == CO_VARIABLE && co->co_choice != NULL){ if (pt_expand_choice(co, ptn, parent) < 0) goto done; } /* Expand variable - call expand callback and insert expanded * commands in place of the variable */ else if (co->co_type == CO_VARIABLE && co->co_expandv_fn != NULL){ #ifdef EXPAND_ONLY_INTERACTIVE /* If I add conditional here, you need to explicitly have a * a "free" variable expression, not just expands. * eg (<v:int expand_dbvar()>|<v:int>) * If I put the conditional in the if-statement above you * may then get two variables and ambiguous command * MAYBE you could see if there are any other same variables on * this iteration and if not add it? */ if (expandvar) #endif if (pt_expand_fnv(h, co, cvv, ptn, parent) < 0) goto done; } else{ /* Copy vector element */ pt_realloc(ptn); /* Copy original cg_obj to shadow list*/ if (co_expand_sub(co, parent, &ptn->pt_vec[ptn->pt_len-1]) < 0) goto done; /* Reference old cg_obj */ // con = ptn->pt_vec[ptn->pt_len-1]; } } else{ pt_realloc(ptn); /* empty child */ } } /* for */ cligen_parsetree_sort(*ptn, 1); if (cligen_logsyntax(h) > 0){ fprintf(stderr, "%s:\n", __FUNCTION__); cligen_print(stderr, *ptn, 0); } retval = 0; done: return retval; }
/* * match_pattern_terminal * This is the last level. Multiple matches may be OK and is used * for completion. * We must have a preference when matching when it matches a command * and some variables. * The preference is: * command > ipv4,mac > string > rest * return in matchvector which element match (their index) * and how many that match. * return value is the number of matches on return (also returned in matchlen) * index is the index of the first such match. * INPUT: * h CLIgen handle * string0 Input string to match * pt Vector of commands (array of cligen object pointers (cg_obj) * pt_max Length of the pt array * level How many levels (words) into string0 * use_pref Set this flag value if you want to use the preferences between * matches. It is only when you want a final exact match (not * completion or show options) that you should set this. * RETURNS: * The number of matches (0-n) in pt or -1 on error. See matchlen below. * * OUTPUT: * ptp Returns the vector at the place of matching * matchv A vector of integers containing which * matchlen Length of matchv. That is, # of matches and same as return * value (if 0-n) * reason0 If retval = 0, this may be malloced to indicate reason for not * matching variables, if given. Neeed to be free:d */ static int match_pattern_terminal(cligen_handle h, char *string0, parse_tree pt, int level, int use_pref, pt_vec *ptp, int *matchv[], int *matchlen, char **reason0 ) { char *string; int i; int match; int matches = 0; int preference = 0; int p; cg_obj *co, *co_match; cg_obj *co_orig; int exact; char *reason; int findreason; co_match = NULL; if (level > command_levels(string0)){ fprintf(stderr, "%s: level > command_level in %s\n", __FUNCTION__, string0); return -1; } /* If there are only variables in the list, then keep track of variable match errors */ findreason = 0; if (reason0) for (i=0; i<pt.pt_len; i++){ if ((co = pt.pt_vec[i]) == NULL) continue; if (co->co_type != CO_VARIABLE){ findreason = 0; break; } findreason++; } extract_substring(string0, level, &string); for (i=0; i<pt.pt_len; i++){ if ((co = pt.pt_vec[i]) == NULL) continue; reason = NULL; if ((match = match_object(string, co, &exact, findreason?&reason:NULL)) < 0) goto error; if (match){ assert(reason==NULL); if (use_pref){ p = co_pref(co, exact); if (p < preference) continue; /* ignore */ if (p > preference){ preference = p; matches = 0; /* Start again at this level */ } } *matchlen = *matchlen + 1; if ((*matchv = realloc(*matchv, (*matchlen)*sizeof(int))) == NULL){ fprintf(stderr, "%s: realloc: %s\n", __FUNCTION__, strerror(errno)); return -1; } co_match = co; (*matchv)[matches++] = i; } /* match == 0, co type is variable and findreason, then reason is set this may not be the best preference, we just set the first */ if (reason){ if (*reason0 == NULL) *reason0 = reason; reason = NULL; findreason = 0; } } if (matches){ *ptp = pt.pt_vec; if (reason0 && *reason0){ free(*reason0); *reason0 = NULL; } if (matches == 1){ assert(co_match); co_orig = co_match->co_ref?co_match->co_ref: co_match; if (co_match->co_type == CO_COMMAND && co_orig->co_type == CO_VARIABLE){ if (co_value_set(co_orig, co_match->co_command) < 0) goto error; } /* Cleanup made on top-level */ } } *matchlen = matches; if (string) free(string); return matches; error: if (string) free(string); return -1; }