lib: Implement hidden and deprecated commands

Each token now knows whether it is part of a hidden
or deprecated command. Command completion logic hides
such tokens when generating completions. Command
matching logic works as before and will still match on
hidden and deprecated commands.

Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
This commit is contained in:
Quentin Young 2016-11-10 23:17:07 +00:00
parent 4c4ff4c136
commit ce882f8168
4 changed files with 57 additions and 38 deletions

View File

@ -235,7 +235,7 @@ install_node (struct cmd_node *node,
node->cmdgraph = graph_new (); node->cmdgraph = graph_new ();
node->cmd_vector = vector_init (VECTOR_MIN_SIZE); node->cmd_vector = vector_init (VECTOR_MIN_SIZE);
// add start node // add start node
struct cmd_token *token = new_cmd_token (START_TKN, NULL, NULL); struct cmd_token *token = new_cmd_token (START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
graph_new_node (node->cmdgraph, token, (void (*)(void *)) &del_cmd_token); graph_new_node (node->cmdgraph, token, (void (*)(void *)) &del_cmd_token);
node->cmd_hash = hash_create (cmd_hash_key, cmd_hash_cmp); node->cmd_hash = hash_create (cmd_hash_key, cmd_hash_cmp);
} }
@ -2383,10 +2383,11 @@ cmd_init (int terminal)
} }
struct cmd_token * struct cmd_token *
new_cmd_token (enum cmd_token_type type, char *text, char *desc) new_cmd_token (enum cmd_token_type type, u_char attr, char *text, char *desc)
{ {
struct cmd_token *token = XMALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_token)); struct cmd_token *token = XMALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_token));
token->type = type; token->type = type;
token->attr = attr;
token->text = text; token->text = text;
token->desc = desc; token->desc = desc;
token->arg = NULL; token->arg = NULL;
@ -2412,7 +2413,7 @@ del_cmd_token (struct cmd_token *token)
struct cmd_token * struct cmd_token *
copy_cmd_token (struct cmd_token *token) copy_cmd_token (struct cmd_token *token)
{ {
struct cmd_token *copy = new_cmd_token (token->type, NULL, NULL); struct cmd_token *copy = new_cmd_token (token->type, token->attr, NULL, NULL);
copy->max = token->max; copy->max = token->max;
copy->min = token->min; copy->min = token->min;
copy->text = token->text ? XSTRDUP (MTYPE_CMD_TOKENS, token->text) : NULL; copy->text = token->text ? XSTRDUP (MTYPE_CMD_TOKENS, token->text) : NULL;

View File

@ -182,24 +182,25 @@ enum cmd_token_type
END_TKN, // last token in line END_TKN, // last token in line
}; };
/** /* Command attributes */
* Token struct. enum
*/ {
CMD_ATTR_NORMAL,
CMD_ATTR_DEPRECATED,
CMD_ATTR_HIDDEN,
};
/* Comamand token struct. */
struct cmd_token struct cmd_token
{ {
enum cmd_token_type type; // token type enum cmd_token_type type; // token type
u_char attr; // token attributes
char *text; // token text char *text; // token text
char *desc; // token description char *desc; // token description
long long min, max; // for ranges long long min, max; // for ranges
char *arg; // user input that matches this token char *arg; // user input that matches this token
}; };
enum
{
CMD_ATTR_DEPRECATED = 1,
CMD_ATTR_HIDDEN,
};
/* Structure of command element. */ /* Structure of command element. */
struct cmd_element struct cmd_element
{ {
@ -425,7 +426,7 @@ copy_cmd_element(struct cmd_element *cmd);
/* memory management for cmd_token */ /* memory management for cmd_token */
struct cmd_token * struct cmd_token *
new_cmd_token (enum cmd_token_type, char *, char *); new_cmd_token (enum cmd_token_type, u_char attr, char *, char *);
void void
del_cmd_token (struct cmd_token *); del_cmd_token (struct cmd_token *);
struct cmd_token * struct cmd_token *

View File

@ -331,6 +331,10 @@ command_complete (struct graph *graph,
for (ALL_LIST_ELEMENTS_RO (current,node,gn)) for (ALL_LIST_ELEMENTS_RO (current,node,gn))
{ {
struct cmd_token *token = gn->data; struct cmd_token *token = gn->data;
if (token->attr == CMD_ATTR_HIDDEN || token->attr == CMD_ATTR_DEPRECATED)
continue;
enum match_type minmatch = min_match_level (token->type); enum match_type minmatch = min_match_level (token->type);
#ifdef TRACE_MATCHER #ifdef TRACE_MATCHER
fprintf (stdout, "\"%s\" matches \"%s\" (%d) ? ", input_token, token->text, token->type); fprintf (stdout, "\"%s\" matches \"%s\" (%d) ? ", input_token, token->text, token->type);

View File

@ -121,7 +121,8 @@
static struct graph_node * static struct graph_node *
new_token_node (struct graph *, new_token_node (struct graph *,
enum cmd_token_type type, enum cmd_token_type type,
char *text, char *doc); u_char attr, char *text,
char *doc);
static void static void
terminate_graph (struct graph *, terminate_graph (struct graph *,
@ -134,7 +135,7 @@
/* yyparse parameters */ /* yyparse parameters */
%parse-param { struct graph *graph } %parse-param { struct graph *graph }
%parse-param { struct cmd_element *element } %parse-param { struct cmd_element *el }
/* called automatically before yyparse */ /* called automatically before yyparse */
%initial-action { %initial-action {
@ -144,15 +145,15 @@
startnode = vector_slot (graph->nodes, 0); startnode = vector_slot (graph->nodes, 0);
/* set string to parse */ /* set string to parse */
set_lexer_string (element->string); set_lexer_string (el->string);
/* copy docstring and keep a pointer to the copy */ /* copy docstring and keep a pointer to the copy */
if (element->doc) if (el->doc)
{ {
// allocate a new buffer, making room for a flag // allocate a new buffer, making room for a flag
size_t length = (size_t) strlen (element->doc) + 2; size_t length = (size_t) strlen (el->doc) + 2;
docstr = malloc (length); docstr = malloc (length);
memcpy (docstr, element->doc, strlen (element->doc)); memcpy (docstr, el->doc, strlen (el->doc));
// set the flag so doc_next knows when to print a warning // set the flag so doc_next knows when to print a warning
docstr[length - 2] = 0x03; docstr[length - 2] = 0x03;
// null terminate // null terminate
@ -167,7 +168,7 @@ start:
sentence_root cmd_token_seq sentence_root cmd_token_seq
{ {
// tack on the command element // tack on the command element
terminate_graph (graph, currnode, element); terminate_graph (graph, currnode, el);
} }
| sentence_root cmd_token_seq placeholder_token '.' '.' '.' | sentence_root cmd_token_seq placeholder_token '.' '.' '.'
{ {
@ -179,14 +180,14 @@ start:
add_edge_dedup (currnode, currnode); add_edge_dedup (currnode, currnode);
// tack on the command element // tack on the command element
terminate_graph (graph, currnode, element); terminate_graph (graph, currnode, el);
} }
; ;
sentence_root: WORD sentence_root: WORD
{ {
struct graph_node *root = struct graph_node *root =
new_token_node (graph, WORD_TKN, strdup ($1), doc_next(element)); new_token_node (graph, WORD_TKN, el->attr, strdup ($1), doc_next(el));
if ((currnode = add_edge_dedup (startnode, root)) != root) if ((currnode = add_edge_dedup (startnode, root)) != root)
graph_delete_node (graph, root); graph_delete_node (graph, root);
@ -227,7 +228,7 @@ compound_token:
literal_token: WORD literal_token: WORD
{ {
$$ = new_token_node (graph, WORD_TKN, strdup($1), doc_next(element)); $$ = new_token_node (graph, WORD_TKN, el->attr, strdup($1), doc_next(el));
free ($1); free ($1);
} }
; ;
@ -235,32 +236,32 @@ literal_token: WORD
placeholder_token: placeholder_token:
IPV4 IPV4
{ {
$$ = new_token_node (graph, IPV4_TKN, strdup($1), doc_next(element)); $$ = new_token_node (graph, IPV4_TKN, el->attr, strdup($1), doc_next(el));
free ($1); free ($1);
} }
| IPV4_PREFIX | IPV4_PREFIX
{ {
$$ = new_token_node (graph, IPV4_PREFIX_TKN, strdup($1), doc_next(element)); $$ = new_token_node (graph, IPV4_PREFIX_TKN, el->attr, strdup($1), doc_next(el));
free ($1); free ($1);
} }
| IPV6 | IPV6
{ {
$$ = new_token_node (graph, IPV6_TKN, strdup($1), doc_next(element)); $$ = new_token_node (graph, IPV6_TKN, el->attr, strdup($1), doc_next(el));
free ($1); free ($1);
} }
| IPV6_PREFIX | IPV6_PREFIX
{ {
$$ = new_token_node (graph, IPV6_PREFIX_TKN, strdup($1), doc_next(element)); $$ = new_token_node (graph, IPV6_PREFIX_TKN, el->attr, strdup($1), doc_next(el));
free ($1); free ($1);
} }
| VARIABLE | VARIABLE
{ {
$$ = new_token_node (graph, VARIABLE_TKN, strdup($1), doc_next(element)); $$ = new_token_node (graph, VARIABLE_TKN, el->attr, strdup($1), doc_next(el));
free ($1); free ($1);
} }
| RANGE | RANGE
{ {
$$ = new_token_node (graph, RANGE_TKN, strdup($1), doc_next(element)); $$ = new_token_node (graph, RANGE_TKN, el->attr, strdup($1), doc_next(el));
struct cmd_token *token = $$->data; struct cmd_token *token = $$->data;
// get the numbers out // get the numbers out
@ -270,7 +271,7 @@ placeholder_token:
token->max = strtoll (yylval.string, &yylval.string, 10); token->max = strtoll (yylval.string, &yylval.string, 10);
// validate range // validate range
if (token->min > token->max) yyerror (graph, element, "Invalid range."); if (token->min > token->max) yyerror (graph, el, "Invalid range.");
free ($1); free ($1);
} }
@ -279,8 +280,8 @@ placeholder_token:
selector: '<' selector_seq_seq '>' selector: '<' selector_seq_seq '>'
{ {
$$ = malloc (sizeof (struct subgraph)); $$ = malloc (sizeof (struct subgraph));
$$->start = new_token_node (graph, SELECTOR_TKN, NULL, NULL); $$->start = new_token_node (graph, SELECTOR_TKN, el->attr, NULL, NULL);
$$->end = new_token_node (graph, NUL_TKN, NULL, NULL); $$->end = new_token_node (graph, NUL_TKN, el->attr, NULL, NULL);
for (unsigned int i = 0; i < vector_active ($2->start->to); i++) for (unsigned int i = 0; i < vector_active ($2->start->to); i++)
{ {
struct graph_node *sn = vector_slot ($2->start->to, i), struct graph_node *sn = vector_slot ($2->start->to, i),
@ -362,8 +363,8 @@ option: '[' option_token_seq ']'
{ {
// make a new option // make a new option
$$ = malloc (sizeof (struct subgraph)); $$ = malloc (sizeof (struct subgraph));
$$->start = new_token_node (graph, OPTION_TKN, NULL, NULL); $$->start = new_token_node (graph, OPTION_TKN, el->attr, NULL, NULL);
$$->end = new_token_node (graph, NUL_TKN, NULL, NULL); $$->end = new_token_node (graph, NUL_TKN, el->attr, NULL, NULL);
// add a path through the sequence to the end // add a path through the sequence to the end
graph_add_edge ($$->start, $2->start); graph_add_edge ($$->start, $2->start);
graph_add_edge ($2->end, $$->end); graph_add_edge ($2->end, $$->end);
@ -434,13 +435,15 @@ cleanup()
} }
static void static void
terminate_graph (struct graph *graph, struct graph_node *finalnode, struct cmd_element *element) terminate_graph (struct graph *graph, struct graph_node *finalnode,
struct cmd_element *element)
{ {
// end of graph should look like this // end of graph should look like this
// * -> finalnode -> END_TKN -> cmd_element // * -> finalnode -> END_TKN -> cmd_element
struct graph_node *end_token_node = struct graph_node *end_token_node =
new_token_node (graph, new_token_node (graph,
END_TKN, END_TKN,
element->attr,
strdup (CMD_CR_TEXT), strdup (CMD_CR_TEXT),
strdup ("")); strdup (""));
struct graph_node *end_element_node = struct graph_node *end_element_node =
@ -467,9 +470,10 @@ doc_next (struct cmd_element *el)
} }
static struct graph_node * static struct graph_node *
new_token_node (struct graph *graph, enum cmd_token_type type, char *text, char *doc) new_token_node (struct graph *graph, enum cmd_token_type type,
u_char attr, char *text, char *doc)
{ {
struct cmd_token *token = new_cmd_token (type, text, doc); struct cmd_token *token = new_cmd_token (type, attr, text, doc);
return graph_new_node (graph, token, (void (*)(void *)) &del_cmd_token); return graph_new_node (graph, token, (void (*)(void *)) &del_cmd_token);
} }
@ -507,7 +511,16 @@ static struct graph_node *
add_edge_dedup (struct graph_node *from, struct graph_node *to) add_edge_dedup (struct graph_node *from, struct graph_node *to)
{ {
struct graph_node *existing = node_adjacent (from, to); struct graph_node *existing = node_adjacent (from, to);
return existing ? existing : graph_add_edge (from, to); if (existing)
{
struct cmd_token *ex_tok = existing->data;
struct cmd_token *to_tok = to->data;
// NORMAL takes precedence over DEPRECATED takes precedence over HIDDEN
ex_tok->attr = (ex_tok->attr < to_tok->attr) ? ex_tok->attr : to_tok->attr;
return existing;
}
else
return graph_add_edge (from, to);
} }
/** /**