summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/client.c34
-rw-r--r--client/client.h5
-rw-r--r--client/commands.c115
-rw-r--r--conf/gen_commands.m45
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