From fae0396ea4fd9d2530086eef77b8a11b6640d640 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Thu, 17 Feb 2000 23:37:16 +0000 Subject: Completion works. Unfortunately, we have to access a couple of internal symbols of libreadline :-( --- client/client.c | 53 +++++++++++++++--- client/client.h | 3 ++ client/commands.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 193 insertions(+), 20 deletions(-) (limited to 'client') diff --git a/client/client.c b/client/client.c index b9e3ab8..9c6b4ae 100644 --- a/client/client.c +++ b/client/client.c @@ -71,6 +71,11 @@ parse_args(int argc, char **argv) static void server_send(char *); static void io_loop(int); +/* HACK: libreadline internals we need to access */ +extern int _rl_vis_botlin; +extern void _rl_move_vert(int); +extern Function *rl_last_func; + static void got_line(char *cmd_buffer) { @@ -91,10 +96,40 @@ got_line(char *cmd_buffer) free(cmd_buffer); } +void +input_start_list(void) /* Leave the currently edited line and make space for listing */ +{ + _rl_move_vert(_rl_vis_botlin); + crlf(); +} + +void +input_stop_list(void) /* Reprint the currently edited line after listing */ +{ + rl_on_new_line(); + rl_redisplay(); +} + static int input_complete(int arg, int key) { - ding(); + static int complete_flag; + char buf[256]; + + if (rl_last_func != input_complete) + complete_flag = 0; + switch (cmd_complete(rl_line_buffer, rl_point, buf, complete_flag)) + { + case 0: + complete_flag = 1; + break; + case 1: + rl_insert_text(buf); + break; + default: + complete_flag = 1; + ding(); + } return 0; } @@ -103,22 +138,26 @@ input_help(int arg, int key) { int i = 0; - if (rl_point != rl_end || arg != 1) + if (arg != 1) return rl_insert(arg, '?'); - while (i < rl_end) + while (i < rl_point) { if (rl_line_buffer[i++] == '"') do { - if (i >= rl_end) /* `?' inside quoted string -> insert */ + if (i >= rl_point) /* `?' 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_begin_undo_group(); /* HACK: We want to display `?' at point position */ + rl_insert_text("?"); rl_redisplay(); + rl_end_undo_group(); + input_start_list(); + cmd_help(rl_line_buffer, rl_point); + rl_undo_command(1, 0); + input_stop_list(); return 0; } diff --git a/client/client.h b/client/client.h index a9c9903..f63c75c 100644 --- a/client/client.h +++ b/client/client.h @@ -9,8 +9,11 @@ /* client.c */ void cleanup(void); +void input_start_list(void); +void input_stop_list(void); /* commands.c */ void cmd_build_tree(void); void cmd_help(char *cmd, int len); +int cmd_complete(char *cmd, int len, char *buf, int again); diff --git a/client/commands.c b/client/commands.c index ceb036b..09c5c34 100644 --- a/client/commands.c +++ b/client/commands.c @@ -7,6 +7,8 @@ */ #include +#include +#include #include "nest/bird.h" #include "lib/resource.h" @@ -27,7 +29,7 @@ static struct cmd_info command_table[] = { struct cmd_node { struct cmd_node *sibling, *son, **plastson; - struct cmd_info *cmd; + struct cmd_info *cmd, *help; int len; char token[1]; }; @@ -58,26 +60,28 @@ cmd_build_tree(void) break; if (!new) { - new = xmalloc(sizeof(struct cmd_node) + c-d); + int size = sizeof(struct cmd_node) + c-d; + new = xmalloc(size); + bzero(new, size); *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; + if (cmd->is_real_cmd) + old->cmd = cmd; + else + old->help = cmd; } } static void -cmd_display_help(struct cmd_info *c) +cmd_do_display_help(struct cmd_info *c) { char buf[strlen(c->command) + strlen(c->args) + 4]; @@ -85,11 +89,21 @@ cmd_display_help(struct cmd_info *c) printf("%-45s %s\n", buf, c->help); } +static void +cmd_display_help(struct cmd_info *c1, struct cmd_info *c2) +{ + if (c1) + cmd_do_display_help(c1); + else if (c2) + cmd_do_display_help(c2); +} + static struct cmd_node * -cmd_find_abbrev(struct cmd_node *root, char *cmd, int len) +cmd_find_abbrev(struct cmd_node *root, char *cmd, int len, int *pambiguous) { struct cmd_node *m, *best = NULL, *best2 = NULL; + *pambiguous = 0; for(m=root->son; m; m=m->sibling) { if (m->len == len && !memcmp(m->token, cmd, len)) @@ -100,7 +114,22 @@ cmd_find_abbrev(struct cmd_node *root, char *cmd, int len) best = m; } } - return best2 ? NULL : best; + if (best2) + { + *pambiguous = 1; + return NULL; + } + return best; +} + +static void +cmd_list_ambiguous(struct cmd_node *root, char *cmd, int len) +{ + struct cmd_node *m; + + for(m=root->son; m; m=m->sibling) + if (m->len > len && !memcmp(m->token, cmd, len)) + cmd_display_help(m->help, m->cmd); } void @@ -109,6 +138,7 @@ cmd_help(char *cmd, int len) char *end = cmd + len; struct cmd_node *n, *m; char *z; + int ambig; n = &cmd_root; while (cmd < end) @@ -121,13 +151,114 @@ cmd_help(char *cmd, int len) z = cmd; while (cmd < end && *cmd != ' ' && *cmd != '\t') cmd++; - m = cmd_find_abbrev(n, z, cmd-z); + m = cmd_find_abbrev(n, z, cmd-z, &ambig); + if (ambig) + { + cmd_list_ambiguous(n, z, cmd-z); + return; + } if (!m) break; n = m; } - if (n->cmd && n->cmd->is_real_cmd) - cmd_display_help(n->cmd); + cmd_display_help(n->cmd, NULL); for (m=n->son; m; m=m->sibling) - cmd_display_help(m->cmd); + cmd_display_help(m->help, m->cmd); +} + +static int +cmd_find_common_match(struct cmd_node *root, char *cmd, int len, int *pcount, char *buf) +{ + struct cmd_node *m; + int best, i; + + *pcount = 0; + best = -1; + for(m=root->son; m; m=m->sibling) + { + if (m->len < len || memcmp(m->token, cmd, len)) + continue; + (*pcount)++; + if (best < 0) + { + strcpy(buf, m->token + len); + best = m->len - len; + } + else + { + i = 0; + while (i < best && i < m->len - len && buf[i] == m->token[len+i]) + i++; + best = i; + } + } + return best; +} + +int +cmd_complete(char *cmd, int len, char *buf, int again) +{ + char *start = cmd; + char *end = cmd + len; + char *fin; + struct cmd_node *n, *m; + char *z; + int ambig, cnt = 0, common; + + /* Find the last word we want to complete */ + for(fin=end; fin > start && !isspace(fin[-1]); fin--) + ; + + /* Find the context */ + n = &cmd_root; + while (cmd < fin && n->son) + { + if (*cmd == ' ' || *cmd == '\t') + { + cmd++; + continue; + } + z = cmd; + while (cmd < fin && !isspace(*cmd)) + cmd++; + m = cmd_find_abbrev(n, z, cmd-z, &ambig); + if (ambig) + { + if (!again) + return -1; + input_start_list(); + cmd_list_ambiguous(n, z, cmd-z); + input_stop_list(); + return 0; + } + if (!m) + return -1; + n = m; + } + + /* Completion of parameters is not yet supported */ + if (!n->son) + return -1; + + /* We know the context, let's try to complete */ + common = cmd_find_common_match(n, fin, end-fin, &cnt, buf); + if (!cnt) + return -1; + if (cnt == 1) + { + buf[common++] = ' '; + buf[common] = 0; + return 1; + } + if (common > 0) + { + buf[common] = 0; + return 1; + } + if (!again) + return -1; + input_start_list(); + cmd_list_ambiguous(n, fin, end-fin); + input_stop_list(); + return 0; } -- cgit v1.2.3