diff options
-rw-r--r-- | client/client.c | 34 | ||||
-rw-r--r-- | client/client.h | 5 | ||||
-rw-r--r-- | client/commands.c | 115 | ||||
-rw-r--r-- | conf/gen_commands.m4 | 5 |
4 files changed, 156 insertions, 3 deletions
diff --git a/client/client.c b/client/client.c index 50335e4f..b9e3ab81 100644 --- a/client/client.c +++ b/client/client.c @@ -91,10 +91,43 @@ got_line(char *cmd_buffer) free(cmd_buffer); } +static int +input_complete(int arg, int key) +{ + ding(); + return 0; +} + +static int +input_help(int arg, int key) +{ + int i = 0; + + if (rl_point != rl_end || arg != 1) + return rl_insert(arg, '?'); + while (i < rl_end) + { + if (rl_line_buffer[i++] == '"') + do + { + if (i >= rl_end) /* `?' inside quoted string -> insert */ + return rl_insert(1, '?'); + } + while (rl_line_buffer[i++] != '"'); + } + puts("?"); + cmd_help(rl_line_buffer, rl_end); + rl_on_new_line(); + rl_redisplay(); + return 0; +} + static void input_init(void) { rl_readline_name = "birdc"; + rl_add_defun("bird-complete", input_complete, '\t'); + rl_add_defun("bird-help", input_help, '?'); rl_callback_handler_install("bird> ", got_line); input_initialized = 1; if (fcntl(0, F_SETFL, O_NONBLOCK) < 0) @@ -282,6 +315,7 @@ main(int argc, char **argv) #endif parse_args(argc, argv); + cmd_build_tree(); server_connect(); io_loop(0); diff --git a/client/client.h b/client/client.h index f0edeeb5..a9c99035 100644 --- a/client/client.h +++ b/client/client.h @@ -9,3 +9,8 @@ /* client.c */ void cleanup(void); + +/* commands.c */ + +void cmd_build_tree(void); +void cmd_help(char *cmd, int len); diff --git a/client/commands.c b/client/commands.c index ea9358d8..ceb036b1 100644 --- a/client/commands.c +++ b/client/commands.c @@ -6,15 +6,128 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ +#include <stdio.h> + #include "nest/bird.h" +#include "lib/resource.h" #include "client/client.h" struct cmd_info { char *command; char *args; char *help; + int is_real_cmd; }; -struct cmd_info command_table[] = { +static struct cmd_info command_table[] = { #include "conf/commands.h" }; + +/* FIXME: There should exist some system of aliases, so that `show' can be abbreviated as `s' etc. */ + +struct cmd_node { + struct cmd_node *sibling, *son, **plastson; + struct cmd_info *cmd; + int len; + char token[1]; +}; + +static struct cmd_node cmd_root; + +void +cmd_build_tree(void) +{ + unsigned int i; + + cmd_root.plastson = &cmd_root.son; + + for(i=0; i<sizeof(command_table) / sizeof(struct cmd_info); i++) + { + struct cmd_info *cmd = &command_table[i]; + struct cmd_node *old, *new; + char *c = cmd->command; + + old = &cmd_root; + while (*c) + { + char *d = c; + while (*c && *c != ' ') + c++; + for(new=old->son; new; new=new->sibling) + if (new->len == c-d && !memcmp(new->token, d, c-d)) + break; + if (!new) + { + new = xmalloc(sizeof(struct cmd_node) + c-d); + *old->plastson = new; + old->plastson = &new->sibling; + new->sibling = new->son = NULL; + new->plastson = &new->son; + new->cmd = NULL; + new->len = c-d; + memcpy(new->token, d, c-d); + new->token[c-d] = 0; + } + old = new; + while (*c == ' ') + c++; + } + old->cmd = cmd; + } +} + +static void +cmd_display_help(struct cmd_info *c) +{ + char buf[strlen(c->command) + strlen(c->args) + 4]; + + sprintf(buf, "%s %s", c->command, c->args); + printf("%-45s %s\n", buf, c->help); +} + +static struct cmd_node * +cmd_find_abbrev(struct cmd_node *root, char *cmd, int len) +{ + struct cmd_node *m, *best = NULL, *best2 = NULL; + + for(m=root->son; m; m=m->sibling) + { + if (m->len == len && !memcmp(m->token, cmd, len)) + return m; + if (m->len > len && !memcmp(m->token, cmd, len)) + { + best2 = best; + best = m; + } + } + return best2 ? NULL : best; +} + +void +cmd_help(char *cmd, int len) +{ + char *end = cmd + len; + struct cmd_node *n, *m; + char *z; + + n = &cmd_root; + while (cmd < end) + { + if (*cmd == ' ' || *cmd == '\t') + { + cmd++; + continue; + } + z = cmd; + while (cmd < end && *cmd != ' ' && *cmd != '\t') + cmd++; + m = cmd_find_abbrev(n, z, cmd-z); + if (!m) + break; + n = m; + } + if (n->cmd && n->cmd->is_real_cmd) + cmd_display_help(n->cmd); + for (m=n->son; m; m=m->sibling) + cmd_display_help(m->cmd); +} diff --git a/conf/gen_commands.m4 b/conf/gen_commands.m4 index cba10b40..a88ba014 100644 --- a/conf/gen_commands.m4 +++ b/conf/gen_commands.m4 @@ -7,9 +7,10 @@ m4_divert(-1)m4_dnl # Can be freely distributed and used under the terms of the GNU GPL. # -m4_define(CF_CLI, `CF_CLI_HELP($1, $3, $4)') +m4_define(CF_CLI, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$3", "$4", 1 }, +m4_divert(-1)') -m4_define(CF_CLI_HELP, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3" }, +m4_define(CF_CLI_HELP, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 0 }, m4_divert(-1)') # As we are processing C source, we must access all M4 primitives via |