Completion works. Unfortunately, we have to access a couple of internal
symbols of libreadline :-(
This commit is contained in:
parent
0223d4fff1
commit
fae0396ea4
4 changed files with 195 additions and 22 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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]])
|
||||||
|
|
Loading…
Reference in a new issue