mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-09 20:22:36 +00:00
Merge pull request #1046 from qlyoung/fix-cli-ambiguous
fix handling of ambiguous cli
This commit is contained in:
commit
272e15e723
@ -46,8 +46,9 @@ DEFINE_MTYPE_STATIC(LIB, CMD_MATCHSTACK, "Command Match Stack")
|
|||||||
static int add_nexthops(struct list *, struct graph_node *,
|
static int add_nexthops(struct list *, struct graph_node *,
|
||||||
struct graph_node **, size_t);
|
struct graph_node **, size_t);
|
||||||
|
|
||||||
static struct list *command_match_r(struct graph_node *, vector, unsigned int,
|
static enum matcher_rv command_match_r(struct graph_node *, vector,
|
||||||
struct graph_node **);
|
unsigned int, struct graph_node **,
|
||||||
|
struct list **);
|
||||||
|
|
||||||
static int score_precedence(enum cmd_token_type);
|
static int score_precedence(enum cmd_token_type);
|
||||||
|
|
||||||
@ -80,14 +81,12 @@ static enum match_type match_variable(struct cmd_token *, const char *);
|
|||||||
|
|
||||||
static enum match_type match_mac(const char *, bool);
|
static enum match_type match_mac(const char *, bool);
|
||||||
|
|
||||||
/* matching functions */
|
|
||||||
static enum matcher_rv matcher_rv;
|
|
||||||
|
|
||||||
enum matcher_rv command_match(struct graph *cmdgraph, vector vline,
|
enum matcher_rv command_match(struct graph *cmdgraph, vector vline,
|
||||||
struct list **argv, const struct cmd_element **el)
|
struct list **argv, const struct cmd_element **el)
|
||||||
{
|
{
|
||||||
struct graph_node *stack[MAXDEPTH];
|
struct graph_node *stack[MAXDEPTH];
|
||||||
matcher_rv = MATCHER_NO_MATCH;
|
enum matcher_rv status;
|
||||||
|
*argv = NULL;
|
||||||
|
|
||||||
// prepend a dummy token to match that pesky start node
|
// prepend a dummy token to match that pesky start node
|
||||||
vector vvline = vector_init(vline->alloced + 1);
|
vector vvline = vector_init(vline->alloced + 1);
|
||||||
@ -97,9 +96,8 @@ enum matcher_rv command_match(struct graph *cmdgraph, vector vline,
|
|||||||
vvline->active = vline->active + 1;
|
vvline->active = vline->active + 1;
|
||||||
|
|
||||||
struct graph_node *start = vector_slot(cmdgraph->nodes, 0);
|
struct graph_node *start = vector_slot(cmdgraph->nodes, 0);
|
||||||
if ((*argv = command_match_r(start, vvline, 0,
|
status = command_match_r(start, vvline, 0, stack, argv);
|
||||||
stack))) // successful match
|
if (status == MATCHER_OK) { // successful match
|
||||||
{
|
|
||||||
struct listnode *head = listhead(*argv);
|
struct listnode *head = listhead(*argv);
|
||||||
struct listnode *tail = listtail(*argv);
|
struct listnode *tail = listtail(*argv);
|
||||||
|
|
||||||
@ -115,6 +113,9 @@ enum matcher_rv command_match(struct graph *cmdgraph, vector vline,
|
|||||||
// input, with each cmd_token->arg holding the corresponding
|
// input, with each cmd_token->arg holding the corresponding
|
||||||
// input
|
// input
|
||||||
assert(*el);
|
assert(*el);
|
||||||
|
} else if (*argv) {
|
||||||
|
del_arglist(*argv);
|
||||||
|
*argv = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!*el) {
|
if (!*el) {
|
||||||
@ -129,7 +130,7 @@ enum matcher_rv command_match(struct graph *cmdgraph, vector vline,
|
|||||||
// free vector
|
// free vector
|
||||||
vector_free(vvline);
|
vector_free(vvline);
|
||||||
|
|
||||||
return matcher_rv;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -183,11 +184,15 @@ enum matcher_rv command_match(struct graph *cmdgraph, vector vline,
|
|||||||
*
|
*
|
||||||
* If no match was found, the return value is NULL.
|
* If no match was found, the return value is NULL.
|
||||||
*/
|
*/
|
||||||
static struct list *command_match_r(struct graph_node *start, vector vline,
|
static enum matcher_rv command_match_r(struct graph_node *start, vector vline,
|
||||||
unsigned int n, struct graph_node **stack)
|
unsigned int n,
|
||||||
|
struct graph_node **stack,
|
||||||
|
struct list **currbest)
|
||||||
{
|
{
|
||||||
assert(n < vector_active(vline));
|
assert(n < vector_active(vline));
|
||||||
|
|
||||||
|
enum matcher_rv status = MATCHER_NO_MATCH;
|
||||||
|
|
||||||
// get the minimum match level that can count as a full match
|
// get the minimum match level that can count as a full match
|
||||||
struct cmd_token *token = start->data;
|
struct cmd_token *token = start->data;
|
||||||
enum match_type minmatch = min_match_level(token->type);
|
enum match_type minmatch = min_match_level(token->type);
|
||||||
@ -196,11 +201,11 @@ static struct list *command_match_r(struct graph_node *start, vector vline,
|
|||||||
* this disallows matching the same one more than once if there is a
|
* this disallows matching the same one more than once if there is a
|
||||||
* circle in the graph (used for keyword arguments) */
|
* circle in the graph (used for keyword arguments) */
|
||||||
if (n == MAXDEPTH)
|
if (n == MAXDEPTH)
|
||||||
return NULL;
|
return MATCHER_NO_MATCH;
|
||||||
if (!token->allowrepeat)
|
if (!token->allowrepeat)
|
||||||
for (size_t s = 0; s < n; s++)
|
for (size_t s = 0; s < n; s++)
|
||||||
if (stack[s] == start)
|
if (stack[s] == start)
|
||||||
return NULL;
|
return MATCHER_NO_MATCH;
|
||||||
|
|
||||||
// get the current operating input token
|
// get the current operating input token
|
||||||
char *input_token = vector_slot(vline, n);
|
char *input_token = vector_slot(vline, n);
|
||||||
@ -231,7 +236,7 @@ static struct list *command_match_r(struct graph_node *start, vector vline,
|
|||||||
|
|
||||||
// if we don't match this node, die
|
// if we don't match this node, die
|
||||||
if (match_token(token, input_token) < minmatch)
|
if (match_token(token, input_token) < minmatch)
|
||||||
return NULL;
|
return MATCHER_NO_MATCH;
|
||||||
|
|
||||||
stack[n] = start;
|
stack[n] = start;
|
||||||
|
|
||||||
@ -244,86 +249,92 @@ static struct list *command_match_r(struct graph_node *start, vector vline,
|
|||||||
add_nexthops(next, start, NULL, 0);
|
add_nexthops(next, start, NULL, 0);
|
||||||
|
|
||||||
// determine the best match
|
// determine the best match
|
||||||
int ambiguous = 0;
|
|
||||||
struct list *currbest = NULL;
|
|
||||||
for (ALL_LIST_ELEMENTS_RO(next, ln, gn)) {
|
for (ALL_LIST_ELEMENTS_RO(next, ln, gn)) {
|
||||||
// if we've matched all input we're looking for END_TKN
|
// if we've matched all input we're looking for END_TKN
|
||||||
if (n + 1 == vector_active(vline)) {
|
if (n + 1 == vector_active(vline)) {
|
||||||
struct cmd_token *tok = gn->data;
|
struct cmd_token *tok = gn->data;
|
||||||
if (tok->type == END_TKN) {
|
if (tok->type == END_TKN) {
|
||||||
if (currbest) // there is more than one END_TKN
|
// if more than one END_TKN in the follow set
|
||||||
// in the follow set
|
if (*currbest) {
|
||||||
{
|
status = MATCHER_AMBIGUOUS;
|
||||||
ambiguous = 1;
|
|
||||||
break;
|
break;
|
||||||
|
} else {
|
||||||
|
status = MATCHER_OK;
|
||||||
}
|
}
|
||||||
currbest = list_new();
|
*currbest = list_new();
|
||||||
// node should have one child node with the
|
// node should have one child node with the
|
||||||
// element
|
// element
|
||||||
struct graph_node *leaf =
|
struct graph_node *leaf =
|
||||||
vector_slot(gn->to, 0);
|
vector_slot(gn->to, 0);
|
||||||
// last node in the list will hold the
|
// last node in the list will hold the
|
||||||
// cmd_element;
|
// cmd_element; this is important because
|
||||||
// this is important because list_delete()
|
// list_delete() expects that all nodes have
|
||||||
// expects
|
// the same data type, so when deleting this
|
||||||
// that all nodes have the same data type, so
|
// list the last node must be manually deleted
|
||||||
// when
|
|
||||||
// deleting this list the last node must be
|
|
||||||
// manually deleted
|
|
||||||
struct cmd_element *el = leaf->data;
|
struct cmd_element *el = leaf->data;
|
||||||
listnode_add(currbest, el);
|
listnode_add(*currbest, el);
|
||||||
currbest->del =
|
(*currbest)->del =
|
||||||
(void (*)(void *)) & cmd_token_del;
|
(void (*)(void *)) & cmd_token_del;
|
||||||
// do not break immediately; continue walking
|
// do not break immediately; continue walking
|
||||||
// through the follow set
|
// through the follow set to ensure that there
|
||||||
// to ensure that there is exactly one END_TKN
|
// is exactly one END_TKN
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// else recurse on candidate child node
|
// else recurse on candidate child node
|
||||||
struct list *result = command_match_r(gn, vline, n + 1, stack);
|
struct list *result = NULL;
|
||||||
|
enum matcher_rv rstat =
|
||||||
|
command_match_r(gn, vline, n + 1, stack, &result);
|
||||||
|
|
||||||
// save the best match
|
// save the best match
|
||||||
if (result && currbest) {
|
if (result && *currbest) {
|
||||||
// pick the best of two matches
|
// pick the best of two matches
|
||||||
struct list *newbest =
|
struct list *newbest =
|
||||||
disambiguate(currbest, result, vline, n + 1);
|
disambiguate(*currbest, result, vline, n + 1);
|
||||||
// set ambiguity flag
|
|
||||||
ambiguous =
|
// current best and result are ambiguous
|
||||||
!newbest || (ambiguous && newbest == currbest);
|
if (!newbest)
|
||||||
|
status = MATCHER_AMBIGUOUS;
|
||||||
|
// current best is still the best, but ambiguous
|
||||||
|
else if (newbest == *currbest
|
||||||
|
&& status == MATCHER_AMBIGUOUS)
|
||||||
|
status = MATCHER_AMBIGUOUS;
|
||||||
|
// result is better, but also ambiguous
|
||||||
|
else if (newbest == result
|
||||||
|
&& rstat == MATCHER_AMBIGUOUS)
|
||||||
|
status = MATCHER_AMBIGUOUS;
|
||||||
|
// one or the other is superior and not ambiguous
|
||||||
|
else
|
||||||
|
status = MATCHER_OK;
|
||||||
|
|
||||||
// delete the unnecessary result
|
// delete the unnecessary result
|
||||||
struct list *todelete =
|
struct list *todelete =
|
||||||
((newbest && newbest == result) ? currbest
|
((newbest && newbest == result) ? *currbest
|
||||||
: result);
|
: result);
|
||||||
del_arglist(todelete);
|
del_arglist(todelete);
|
||||||
|
|
||||||
currbest = newbest ? newbest : currbest;
|
*currbest = newbest ? newbest : *currbest;
|
||||||
} else if (result)
|
} else if (result) {
|
||||||
currbest = result;
|
status = rstat;
|
||||||
}
|
*currbest = result;
|
||||||
|
} else if (!*currbest) {
|
||||||
if (currbest) {
|
status = MAX(rstat, status);
|
||||||
if (ambiguous) {
|
|
||||||
del_arglist(currbest);
|
|
||||||
currbest = NULL;
|
|
||||||
matcher_rv = MATCHER_AMBIGUOUS;
|
|
||||||
} else {
|
|
||||||
// copy token, set arg and prepend to currbest
|
|
||||||
struct cmd_token *token = start->data;
|
|
||||||
struct cmd_token *copy = cmd_token_dup(token);
|
|
||||||
copy->arg = XSTRDUP(MTYPE_CMD_ARG, input_token);
|
|
||||||
listnode_add_before(currbest, currbest->head, copy);
|
|
||||||
matcher_rv = MATCHER_OK;
|
|
||||||
}
|
}
|
||||||
} else if (n + 1 == vector_active(vline)
|
}
|
||||||
&& matcher_rv == MATCHER_NO_MATCH)
|
if (*currbest) {
|
||||||
matcher_rv = MATCHER_INCOMPLETE;
|
// copy token, set arg and prepend to currbest
|
||||||
|
struct cmd_token *token = start->data;
|
||||||
|
struct cmd_token *copy = cmd_token_dup(token);
|
||||||
|
copy->arg = XSTRDUP(MTYPE_CMD_ARG, input_token);
|
||||||
|
listnode_add_before(*currbest, (*currbest)->head, copy);
|
||||||
|
} else if (n + 1 == vector_active(vline) && status == MATCHER_NO_MATCH)
|
||||||
|
status = MATCHER_INCOMPLETE;
|
||||||
|
|
||||||
// cleanup
|
// cleanup
|
||||||
list_delete(next);
|
list_delete(next);
|
||||||
|
|
||||||
return currbest;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stack_del(void *val)
|
static void stack_del(void *val)
|
||||||
@ -432,12 +443,12 @@ enum matcher_rv command_complete(struct graph *graph, vector vline,
|
|||||||
* next = set of all nodes reachable from all nodes in `matched`
|
* next = set of all nodes reachable from all nodes in `matched`
|
||||||
*/
|
*/
|
||||||
|
|
||||||
matcher_rv = idx == vector_active(vline) && next->count
|
enum matcher_rv mrv = idx == vector_active(vline) && next->count
|
||||||
? MATCHER_OK
|
? MATCHER_OK
|
||||||
: MATCHER_NO_MATCH;
|
: MATCHER_NO_MATCH;
|
||||||
|
|
||||||
*completions = NULL;
|
*completions = NULL;
|
||||||
if (!MATCHER_ERROR(matcher_rv)) {
|
if (!MATCHER_ERROR(mrv)) {
|
||||||
// extract cmd_token into list
|
// extract cmd_token into list
|
||||||
*completions = list_new();
|
*completions = list_new();
|
||||||
for (ALL_LIST_ELEMENTS_RO(next, node, gstack)) {
|
for (ALL_LIST_ELEMENTS_RO(next, node, gstack)) {
|
||||||
@ -448,7 +459,7 @@ enum matcher_rv command_complete(struct graph *graph, vector vline,
|
|||||||
list_delete(current);
|
list_delete(current);
|
||||||
list_delete(next);
|
list_delete(next);
|
||||||
|
|
||||||
return matcher_rv;
|
return mrv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user