diff options
-rw-r--r-- | lib/command.c | 2 | ||||
-rw-r--r-- | lib/command_graph.c | 131 | ||||
-rw-r--r-- | lib/command_graph.h | 1 |
3 files changed, 133 insertions, 1 deletions
diff --git a/lib/command.c b/lib/command.c index 7a53357e7..0e19a3dfe 100644 --- a/lib/command.c +++ b/lib/command.c @@ -341,6 +341,7 @@ install_element (enum node_type ntype, struct cmd_element *cmd) graph_new_node (graph, token, (void (*)(void *)) &cmd_token_del); cmd_graph_parse (graph, cmd); + cmd_graph_names (graph); cmd_graph_merge (cnode->cmdgraph, graph, +1); graph_delete_graph (graph); @@ -387,6 +388,7 @@ uninstall_element (enum node_type ntype, struct cmd_element *cmd) graph_new_node (graph, token, (void (*)(void *)) &cmd_token_del); cmd_graph_parse (graph, cmd); + cmd_graph_names (graph); cmd_graph_merge (cnode->cmdgraph, graph, -1); graph_delete_graph (graph); diff --git a/lib/command_graph.c b/lib/command_graph.c index 7e611e362..62d0610e1 100644 --- a/lib/command_graph.c +++ b/lib/command_graph.c @@ -81,7 +81,28 @@ cmd_token_dup (struct cmd_token *token) void cmd_token_varname_set(struct cmd_token *token, const char *varname) { XFREE (MTYPE_CMD_VAR, token->varname); - token->varname = varname ? XSTRDUP (MTYPE_CMD_VAR, varname) : NULL; + if (!varname) + { + token->varname = NULL; + return; + } + + size_t len = strlen (varname), i; + token->varname = XMALLOC (MTYPE_CMD_VAR, len + 1); + + for (i = 0; i < len; i++) + switch (varname[i]) + { + case '-': + case '+': + case '*': + case ':': + token->varname[i] = '_'; + break; + default: + token->varname[i] = tolower (varname[i]); + } + token->varname[len] = '\0'; } static bool @@ -162,6 +183,10 @@ cmd_nodes_equal (struct graph_node *ga, struct graph_node *gb) /* one a ..., the other not. */ if (cmd_nodes_link (ga, ga) != cmd_nodes_link (gb, gb)) return false; + if (!a->varname != !b->varname) + return false; + if (a->varname && strcmp (a->varname, b->varname)) + return false; switch (a->type) { @@ -349,3 +374,107 @@ cmd_graph_merge (struct graph *old, struct graph *new, int direction) vector_slot (old->nodes, 0), vector_slot (new->nodes, 0), direction); } + +static void +cmd_node_names (struct graph_node *gn, struct graph_node *join, + const char *prevname) +{ + size_t i; + struct cmd_token *tok = gn->data, *jointok; + struct graph_node *stop = cmd_loopstop (gn); + + switch (tok->type) + { + case WORD_TKN: + prevname = tok->text; + break; + + case VARIABLE_TKN: + if (!tok->varname + && strcmp (tok->text, "WORD") + && strcmp (tok->text, "NAME")) + cmd_token_varname_set (tok, tok->text); + /* fallthrough */ + case RANGE_TKN: + case IPV4_TKN: + case IPV4_PREFIX_TKN: + case IPV6_TKN: + case IPV6_PREFIX_TKN: + if (!tok->varname && prevname) + cmd_token_varname_set (tok, prevname); + prevname = NULL; + break; + + case START_TKN: + case END_TKN: + case JOIN_TKN: + /* "<foo|bar> WORD" -> word is not "bar" or "foo" */ + prevname = NULL; + break; + + case FORK_TKN: + /* apply "<A.B.C.D|X:X::X:X>$name" */ + jointok = tok->forkjoin->data; + if (!jointok->varname) + break; + for (i = 0; i < vector_active (tok->forkjoin->from); i++) + { + struct graph_node *tail = vector_slot (tok->forkjoin->from, i); + struct cmd_token *tailtok = tail->data; + if (tail == gn || tailtok->varname) + continue; + cmd_token_varname_set (tailtok, jointok->varname); + } + break; + } + + for (i = 0; i < vector_active (gn->to); i++) + { + struct graph_node *next = vector_slot (gn->to, i); + if (next == stop || next == join) + continue; + cmd_node_names (next, join, prevname); + } + + if (tok->type == FORK_TKN && tok->forkjoin != join) + cmd_node_names (tok->forkjoin, join, NULL); +} + +void +cmd_graph_names (struct graph *graph) +{ + struct graph_node *start; + + assert (vector_active (graph->nodes) >= 1); + start = vector_slot (graph->nodes, 0); + + /* apply varname on initial "[no]" */ + do + { + if (vector_active (start->to) != 1) + break; + + struct graph_node *first = vector_slot (start->to, 0); + struct cmd_token *tok = first->data; + /* looking for an option with 2 choices, nothing or "no" */ + if (tok->type != FORK_TKN || vector_active (first->to) != 2) + break; + + struct graph_node *next0 = vector_slot (first->to, 0); + struct graph_node *next1 = vector_slot (first->to, 1); + /* one needs to be empty */ + if (next0 != tok->forkjoin && next1 != tok->forkjoin) + break; + + struct cmd_token *tok0 = next0->data; + struct cmd_token *tok1 = next1->data; + /* the other one needs to be "no" (only one will match here) */ + if ((tok0->type == WORD_TKN && !strcmp(tok0->text, "no"))) + cmd_token_varname_set (tok0, "no"); + if ((tok1->type == WORD_TKN && !strcmp(tok1->text, "no"))) + cmd_token_varname_set (tok1, "no"); + } + while (0); + + cmd_node_names (start, NULL, NULL); +} diff --git a/lib/command_graph.h b/lib/command_graph.h index e6d49f2f5..11cea9bd4 100644 --- a/lib/command_graph.h +++ b/lib/command_graph.h @@ -111,6 +111,7 @@ extern void cmd_token_del (struct cmd_token *); extern void cmd_token_varname_set(struct cmd_token *token, const char *varname); extern void cmd_graph_parse (struct graph *graph, struct cmd_element *cmd); +extern void cmd_graph_names (struct graph *graph); extern void cmd_graph_merge (struct graph *old, struct graph *new, int direction); #endif /* _FRR_COMMAND_GRAPH_H */ |