/*! 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; }
/* * Help function to append a cv to a cvec. For expansion cvec passed to pt_expand_2 * IN: * co A cligen variable that has a matching value * cmd Value in string of the variable * OUT: * cvec The cligen variable vector to push a cv with name of co and value in cmd */ static cg_var * add_cov_to_cvec(cg_obj *co, char *cmd, cvec *cvec) { cg_var *cv = NULL; if ((cv = cvec_add(cvec, co->co_vtype)) == NULL) return NULL; cv_name_set(cv, co->co_command); cv_const_set(cv, iskeyword(co)); if (cv_parse(cmd, cv) < 0) { cv_reset(cv); cvec_del(cvec, cv); return NULL; } return cv; }
/* * 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; }