Completion works. Unfortunately, we have to access a couple of internal

symbols of libreadline :-(
This commit is contained in:
Martin Mares 2000-02-17 23:37:16 +00:00
parent 0223d4fff1
commit fae0396ea4
4 changed files with 195 additions and 22 deletions

View file

@ -71,6 +71,11 @@ parse_args(int argc, char **argv)
static void server_send(char *); static void server_send(char *);
static void io_loop(int); 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 static void
got_line(char *cmd_buffer) got_line(char *cmd_buffer)
{ {
@ -91,10 +96,40 @@ got_line(char *cmd_buffer)
free(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 static int
input_complete(int arg, int key) input_complete(int arg, int key)
{ {
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(); ding();
}
return 0; return 0;
} }
@ -103,22 +138,26 @@ input_help(int arg, int key)
{ {
int i = 0; int i = 0;
if (rl_point != rl_end || arg != 1) if (arg != 1)
return rl_insert(arg, '?'); return rl_insert(arg, '?');
while (i < rl_end) while (i < rl_point)
{ {
if (rl_line_buffer[i++] == '"') if (rl_line_buffer[i++] == '"')
do do
{ {
if (i >= rl_end) /* `?' inside quoted string -> insert */ if (i >= rl_point) /* `?' inside quoted string -> insert */
return rl_insert(1, '?'); return rl_insert(1, '?');
} }
while (rl_line_buffer[i++] != '"'); while (rl_line_buffer[i++] != '"');
} }
puts("?"); rl_begin_undo_group(); /* HACK: We want to display `?' at point position */
cmd_help(rl_line_buffer, rl_end); rl_insert_text("?");
rl_on_new_line();
rl_redisplay(); 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; return 0;
} }

View file

@ -9,8 +9,11 @@
/* client.c */ /* client.c */
void cleanup(void); void cleanup(void);
void input_start_list(void);
void input_stop_list(void);
/* commands.c */ /* commands.c */
void cmd_build_tree(void); void cmd_build_tree(void);
void cmd_help(char *cmd, int len); void cmd_help(char *cmd, int len);
int cmd_complete(char *cmd, int len, char *buf, int again);

View file

@ -7,6 +7,8 @@
*/ */
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "nest/bird.h" #include "nest/bird.h"
#include "lib/resource.h" #include "lib/resource.h"
@ -27,7 +29,7 @@ static struct cmd_info command_table[] = {
struct cmd_node { struct cmd_node {
struct cmd_node *sibling, *son, **plastson; struct cmd_node *sibling, *son, **plastson;
struct cmd_info *cmd; struct cmd_info *cmd, *help;
int len; int len;
char token[1]; char token[1];
}; };
@ -58,26 +60,28 @@ cmd_build_tree(void)
break; break;
if (!new) 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;
old->plastson = &new->sibling; old->plastson = &new->sibling;
new->sibling = new->son = NULL;
new->plastson = &new->son; new->plastson = &new->son;
new->cmd = NULL;
new->len = c-d; new->len = c-d;
memcpy(new->token, d, c-d); memcpy(new->token, d, c-d);
new->token[c-d] = 0;
} }
old = new; old = new;
while (*c == ' ') while (*c == ' ')
c++; c++;
} }
if (cmd->is_real_cmd)
old->cmd = cmd; old->cmd = cmd;
else
old->help = cmd;
} }
} }
static void 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]; 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); 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 * 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; struct cmd_node *m, *best = NULL, *best2 = NULL;
*pambiguous = 0;
for(m=root->son; m; m=m->sibling) for(m=root->son; m; m=m->sibling)
{ {
if (m->len == len && !memcmp(m->token, cmd, len)) 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; 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 void
@ -109,6 +138,7 @@ cmd_help(char *cmd, int len)
char *end = cmd + len; char *end = cmd + len;
struct cmd_node *n, *m; struct cmd_node *n, *m;
char *z; char *z;
int ambig;
n = &cmd_root; n = &cmd_root;
while (cmd < end) while (cmd < end)
@ -121,13 +151,114 @@ cmd_help(char *cmd, int len)
z = cmd; z = cmd;
while (cmd < end && *cmd != ' ' && *cmd != '\t') while (cmd < end && *cmd != ' ' && *cmd != '\t')
cmd++; 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) if (!m)
break; break;
n = m; n = m;
} }
if (n->cmd && n->cmd->is_real_cmd) cmd_display_help(n->cmd, NULL);
cmd_display_help(n->cmd);
for (m=n->son; m; m=m->sibling) 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;
} }

View file

@ -192,7 +192,7 @@ password_list:
/* Core commands */ /* Core commands */
CF_CLI_HELP(SHOW,,[[Show status information]]) CF_CLI_HELP(SHOW, ..., [[Show status information]])
CF_CLI(SHOW STATUS,,, [[Show router status]]) CF_CLI(SHOW STATUS,,, [[Show router status]])
{ cmd_show_status(); } { cmd_show_status(); }
@ -255,7 +255,7 @@ r_args:
CF_CLI(SHOW SYMBOLS, optsym, [<symbol>], [[Show all known symbolic names]]) CF_CLI(SHOW SYMBOLS, optsym, [<symbol>], [[Show all known symbolic names]])
{ cmd_show_symbols($3); } ; { cmd_show_symbols($3); } ;
CF_CLI_HELP(DEBUG, <subsystem>, [[Show debugging information]]) CF_CLI_HELP(DEBUG, ..., [[Show debugging information]])
CF_CLI(DEBUG RESOURCES,,, [[Show all allocated resource]]) CF_CLI(DEBUG RESOURCES,,, [[Show all allocated resource]])
{ rdump(&root_pool); cli_msg(0, ""); } ; { rdump(&root_pool); cli_msg(0, ""); } ;
CF_CLI(DEBUG SOCKETS,,, [[Show open sockets]]) CF_CLI(DEBUG SOCKETS,,, [[Show open sockets]])