From 1b064355f752b9bfe4644f775697bbd9b711f762 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 5 Mar 2024 19:04:10 +0100 Subject: [PATCH] Client: Add support for completion of command options We can easily extend command completion to handle also keywords for command options. Help for command options is not yet supported. --- client/commands.c | 27 +++++++++++++++++++-------- conf/gen_commands.m4 | 3 +++ conf/gen_parser.m4 | 1 + nest/config.Y | 34 +++++++++++++++++++++++++++++++++- proto/bfd/config.Y | 9 +++++++++ proto/mrt/config.Y | 5 +++++ proto/ospf/config.Y | 8 ++++++++ 7 files changed, 78 insertions(+), 9 deletions(-) diff --git a/client/commands.c b/client/commands.c index fdf2652a..318f0ecd 100644 --- a/client/commands.c +++ b/client/commands.c @@ -20,6 +20,7 @@ struct cmd_info { char *args; char *help; int is_real_cmd; + int is_option; }; static struct cmd_info command_table[] = { @@ -30,7 +31,8 @@ struct cmd_node { struct cmd_node *sibling, *son, **plastson; struct cmd_info *cmd, *help; int len; - signed char prio; + u8 final; + s8 prio; char token[1]; }; @@ -51,12 +53,13 @@ cmd_build_tree(void) struct cmd_node *old, *new; char *c = cmd->command; - old = &cmd_root; + new = &cmd_root; while (*c) { char *d = c; while (*c && !isspace_(*c)) c++; + old = new; for(new=old->son; new; new=new->sibling) if (new->len == c-d && !memcmp(new->token, d, c-d)) break; @@ -72,14 +75,17 @@ cmd_build_tree(void) memcpy(new->token, d, c-d); new->prio = (new->len == 3 && (!memcmp(new->token, "roa", 3) || !memcmp(new->token, "rip", 3))) ? 0 : 1; /* Hack */ } - old = new; while (isspace_(*c)) c++; } + if (cmd->is_real_cmd) - old->cmd = cmd; + new->cmd = cmd; else - old->help = cmd; + new->help = cmd; + + if (cmd->is_option) + old->final = 1; } } @@ -147,7 +153,7 @@ cmd_help(char *cmd, int len) int ambig; n = &cmd_root; - while (cmd < end) + while (cmd < end && !n->final) { if (isspace_(*cmd)) { @@ -168,6 +174,11 @@ cmd_help(char *cmd, int len) n = m; } cmd_display_help(n->cmd, NULL); + + /* Currently no help for options */ + if (n->final) + return; + for (m=n->son; m; m=m->sibling) cmd_display_help(m->help, m->cmd); } @@ -229,7 +240,7 @@ cmd_complete(char *cmd, int len, char *buf, int again) /* Find the context */ n = &cmd_root; - while (cmd < fin && n->son) + while (cmd < fin && n->son && !n->final) { if (isspace_(*cmd)) { @@ -290,7 +301,7 @@ cmd_expand(char *cmd) args = c = cmd; n = &cmd_root; - while (*c) + while (*c && !n->final) { if (isspace_(*c)) { diff --git a/conf/gen_commands.m4 b/conf/gen_commands.m4 index 3ed21f13..daf39da6 100644 --- a/conf/gen_commands.m4 +++ b/conf/gen_commands.m4 @@ -13,6 +13,9 @@ m4_divert(-1)') m4_define(CF_CLI_CMD, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 1 }, m4_divert(-1)') +m4_define(CF_CLI_OPT, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 1, .is_option = 1 }, +m4_divert(-1)') + m4_define(CF_CLI_HELP, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 0 }, m4_divert(-1)') diff --git a/conf/gen_parser.m4 b/conf/gen_parser.m4 index 80071aef..48c2ca50 100644 --- a/conf/gen_parser.m4 +++ b/conf/gen_parser.m4 @@ -48,6 +48,7 @@ m4_divert(2)CF_KEYWORDS(m4_translit($1, [[ ]], [[,]])) m4_divert(3)cli_cmd: CF_cmd CF_cmd: $1 $2 END') m4_define(CF_CLI_CMD, `') +m4_define(CF_CLI_OPT, `') m4_define(CF_CLI_HELP, `') # ENUM declarations are ignored diff --git a/nest/config.Y b/nest/config.Y index 63888a04..20186ece 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -650,6 +650,23 @@ CF_CLI_HELP(SHOW ROUTE, ..., [[Show routing table]]) CF_CLI(SHOW ROUTE, r_args, [[[|for |for |in ] [table ] [(import|export) table

.] [filter |where ] [all] [primary] [filtered] [(export|preexport|noexport)

] [protocol

] [stats|count]]], [[Show routing table]]) { rt_show($3); } ; +CF_CLI_OPT(SHOW ROUTE FOR, |) +CF_CLI_OPT(SHOW ROUTE IN, ) +CF_CLI_OPT(SHOW ROUTE TABLE, ) +CF_CLI_OPT(SHOW ROUTE FILTER, ) +CF_CLI_OPT(SHOW ROUTE WHERE, ) +CF_CLI_OPT(SHOW ROUTE ALL) +CF_CLI_OPT(SHOW ROUTE PRIMARY) +CF_CLI_OPT(SHOW ROUTE FILTERED) +CF_CLI_OPT(SHOW ROUTE IMPORT,

[.]) +CF_CLI_OPT(SHOW ROUTE EXPORT,

[.]) +CF_CLI_OPT(SHOW ROUTE EXPORTED,

[.]) +CF_CLI_OPT(SHOW ROUTE PREEXPORT,

[.]) +CF_CLI_OPT(SHOW ROUTE NOEXPORT,

[.]) +CF_CLI_OPT(SHOW ROUTE PROTOCOL,

) +CF_CLI_OPT(SHOW ROUTE STATS) +CF_CLI_OPT(SHOW ROUTE COUNT) + r_args: /* empty */ { $$ = cfg_allocz(sizeof(struct rt_show_data)); @@ -841,13 +858,19 @@ CF_CLI_HELP(SHOW SYMBOLS, ..., [[Show all known symbolic names]]) CF_CLI(SHOW SYMBOLS, sym_args, [table|filter|function|protocol|template|], [[Show all known symbolic names]]) { cmd_show_symbols($3); } ; +CF_CLI_OPT(SHOW SYMBOLS TABLE) +CF_CLI_OPT(SHOW SYMBOLS FILTER) +CF_CLI_OPT(SHOW SYMBOLS FUNCTION) +CF_CLI_OPT(SHOW SYMBOLS PROTOCOL) +CF_CLI_OPT(SHOW SYMBOLS TEMPLATE) + sym_args: /* empty */ { $$ = cfg_allocz(sizeof(struct sym_show_data)); } | sym_args TABLE { $$ = $1; $$->type = SYM_TABLE; } - | sym_args FUNCTION { $$ = $1; $$->type = SYM_FUNCTION; } | sym_args FILTER { $$ = $1; $$->type = SYM_FILTER; } + | sym_args FUNCTION { $$ = $1; $$->type = SYM_FUNCTION; } | sym_args PROTOCOL { $$ = $1; $$->type = SYM_PROTO; } | sym_args TEMPLATE { $$ = $1; $$->type = SYM_TEMPLATE; } | sym_args CF_SYM_KNOWN { $$ = $1; $$->sym = $2; } @@ -914,6 +937,15 @@ CF_CLI_HELP(DEBUG, ..., [[Control protocol debugging via BIRD logs]]) CF_CLI(DEBUG, debug_args, ( | | \"\" | all) (all | off | { states|routes|filters|interfaces|events|packets [, ...] }), [[Control protocol debugging via BIRD logs]]) { /* Done in debug_args */ }; +CF_CLI_OPT(DEBUG ALL) +CF_CLI_OPT(DEBUG OFF) +CF_CLI_OPT(DEBUG STATES) +CF_CLI_OPT(DEBUG ROUTES) +CF_CLI_OPT(DEBUG FILTERS) +CF_CLI_OPT(DEBUG INTERFACES) +CF_CLI_OPT(DEBUG EVENTS) +CF_CLI_OPT(DEBUG PACKETS) + debug_args: proto_patt debug_mask { proto_apply_cmd($1, proto_cmd_debug, 1, $2); } | channel_arg debug_mask { channel_cmd_debug($1, $2); } diff --git a/proto/bfd/config.Y b/proto/bfd/config.Y index 4edc13d9..1a7474b0 100644 --- a/proto/bfd/config.Y +++ b/proto/bfd/config.Y @@ -189,6 +189,15 @@ CF_CLI_HELP(SHOW BFD SESSIONS, ..., [[Show information about BFD sessions]]); CF_CLI(SHOW BFD SESSIONS, bfd_show_sessions_args, [] [address ] [(interface|dev) \"\"] [ipv4|ipv6] [direct|multihop] [all], [[Show information about BFD sessions]]) { PROTO_WALK_CMD($4->name, &proto_bfd, p) bfd_show_sessions(p, $4); }; +CF_CLI_OPT(SHOW BFD SESSIONS ADDRESS, |) +CF_CLI_OPT(SHOW BFD SESSIONS INTERFACE, \"\") +CF_CLI_OPT(SHOW BFD SESSIONS DEV, \"\") +CF_CLI_OPT(SHOW BFD SESSIONS ALL) +CF_CLI_OPT(SHOW BFD SESSIONS IPV4) +CF_CLI_OPT(SHOW BFD SESSIONS IPV6) +CF_CLI_OPT(SHOW BFD SESSIONS DIRECT) +CF_CLI_OPT(SHOW BFD SESSIONS MULTIHOP) + bfd_show_sessions_args: /* empty */ { $$ = cfg_allocz(sizeof(struct bfd_show_sessions_cmd)); } | bfd_show_sessions_args CF_SYM_KNOWN { cf_assert_symbol($2, SYM_PROTO); $$->name = $2; } diff --git a/proto/mrt/config.Y b/proto/mrt/config.Y index 4da6777a..d2136ed1 100644 --- a/proto/mrt/config.Y +++ b/proto/mrt/config.Y @@ -53,6 +53,11 @@ CF_CLI_HELP(MRT DUMP, [table |\"\"] [to \"\"] [filter |\"\"] [to \"\"] [filter |where ], [[Save mrt table dump v2 of table name right now]]) { mrt_dump_cmd($3); } ; +CF_CLI_OPT(MRT DUMP TABLE, |\"\") +CF_CLI_OPT(MRT DUMP TO, \"\") +CF_CLI_OPT(MRT DUMP FILTER, ) +CF_CLI_OPT(MRT DUMP WHERE, ) + mrt_dump_args: /* empty */ { $$ = cfg_allocz(sizeof(struct mrt_dump_data)); } | mrt_dump_args TABLE rtable { $$ = $1; $$->table_ptr = $3->table; } diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index 71d9d05b..7d35304a 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -546,6 +546,14 @@ CF_CLI(SHOW OSPF LSADB, lsadb_args, [global | area | link] [type ] [ls ospf_sh_lsadb($4); }; +CF_CLI_OPT(SHOW OSPF LSADB GLOBAL) +CF_CLI_OPT(SHOW OSPF LSADB AREA, ) +CF_CLI_OPT(SHOW OSPF LSADB LINK) +CF_CLI_OPT(SHOW OSPF LSADB TYPE, ) +CF_CLI_OPT(SHOW OSPF LSADB LSID, ) +CF_CLI_OPT(SHOW OSPF LSADB SELF) +CF_CLI_OPT(SHOW OSPF LSADB ROUTER, ) + lsadb_args: /* empty */ { $$ = cfg_allocz(sizeof(struct lsadb_show_data));