2000-01-19 15:07:00 +00:00
|
|
|
/*
|
|
|
|
* BIRD Client -- Command Handling
|
|
|
|
*
|
|
|
|
* (c) 1999--2000 Martin Mares <mj@ucw.cz>
|
|
|
|
*
|
|
|
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
|
|
|
*/
|
|
|
|
|
2000-02-17 22:00:13 +00:00
|
|
|
#include <stdio.h>
|
2016-06-09 08:11:39 +00:00
|
|
|
#include <stdlib.h>
|
2000-02-17 23:37:16 +00:00
|
|
|
#include <ctype.h>
|
2000-02-17 22:00:13 +00:00
|
|
|
|
2000-01-19 15:07:00 +00:00
|
|
|
#include "nest/bird.h"
|
2000-02-17 22:00:13 +00:00
|
|
|
#include "lib/resource.h"
|
2000-03-31 23:30:21 +00:00
|
|
|
#include "lib/string.h"
|
2000-01-19 15:07:00 +00:00
|
|
|
#include "client/client.h"
|
|
|
|
|
|
|
|
struct cmd_info {
|
2016-04-15 09:41:08 +00:00
|
|
|
/* use for build cmd tree and cli commands */
|
2000-01-19 15:07:00 +00:00
|
|
|
char *command;
|
|
|
|
char *args;
|
|
|
|
char *help;
|
2016-04-15 09:41:08 +00:00
|
|
|
|
|
|
|
/* only for build tree */
|
2000-02-17 22:00:13 +00:00
|
|
|
int is_real_cmd;
|
2016-04-15 09:41:08 +00:00
|
|
|
u32 flags; /* Mask of (CLI_SF_*) */
|
2000-01-19 15:07:00 +00:00
|
|
|
};
|
|
|
|
|
2000-02-17 22:00:13 +00:00
|
|
|
static struct cmd_info command_table[] = {
|
2000-01-19 15:07:00 +00:00
|
|
|
#include "conf/commands.h"
|
|
|
|
};
|
2000-02-17 22:00:13 +00:00
|
|
|
|
|
|
|
struct cmd_node {
|
2016-04-15 09:41:08 +00:00
|
|
|
struct cmd_node *sibling;
|
|
|
|
struct cmd_node *son;
|
|
|
|
struct cmd_node **plastson; /* Helping pointer to son */
|
|
|
|
struct cmd_info *cmd; /* Short info */
|
|
|
|
struct cmd_info *help; /* Detailed info */
|
|
|
|
signed char prio; /* Priority */
|
|
|
|
u32 flags; /* Mask of (CLI_SF_*) */
|
|
|
|
uint len; /* Length of string in token */
|
|
|
|
char token[1]; /* Name of command */
|
2000-02-17 22:00:13 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct cmd_node cmd_root;
|
|
|
|
|
|
|
|
void
|
|
|
|
cmd_build_tree(void)
|
|
|
|
{
|
2015-05-19 06:53:34 +00:00
|
|
|
uint i;
|
2000-02-17 22:00:13 +00:00
|
|
|
|
|
|
|
cmd_root.plastson = &cmd_root.son;
|
|
|
|
|
2000-04-12 13:21:23 +00:00
|
|
|
for(i=0; i<ARRAY_SIZE(command_table); i++)
|
2000-02-17 22:00:13 +00:00
|
|
|
{
|
|
|
|
struct cmd_info *cmd = &command_table[i];
|
|
|
|
struct cmd_node *old, *new;
|
|
|
|
char *c = cmd->command;
|
|
|
|
|
|
|
|
old = &cmd_root;
|
|
|
|
while (*c)
|
|
|
|
{
|
|
|
|
char *d = c;
|
2000-02-27 22:00:19 +00:00
|
|
|
while (*c && !isspace(*c))
|
2000-02-17 22:00:13 +00:00
|
|
|
c++;
|
|
|
|
for(new=old->son; new; new=new->sibling)
|
|
|
|
if (new->len == c-d && !memcmp(new->token, d, c-d))
|
|
|
|
break;
|
|
|
|
if (!new)
|
|
|
|
{
|
2000-02-17 23:37:16 +00:00
|
|
|
int size = sizeof(struct cmd_node) + c-d;
|
2016-05-12 19:29:04 +00:00
|
|
|
new = malloc(size);
|
2000-02-17 23:37:16 +00:00
|
|
|
bzero(new, size);
|
2000-02-17 22:00:13 +00:00
|
|
|
*old->plastson = new;
|
|
|
|
old->plastson = &new->sibling;
|
|
|
|
new->plastson = &new->son;
|
|
|
|
new->len = c-d;
|
|
|
|
memcpy(new->token, d, c-d);
|
2015-10-05 10:14:50 +00:00
|
|
|
new->prio = (new->len == 3 && (!memcmp(new->token, "roa", 3) || !memcmp(new->token, "rip", 3))) ? 0 : 1; /* Hack */
|
2000-02-17 22:00:13 +00:00
|
|
|
}
|
|
|
|
old = new;
|
2000-02-27 22:00:19 +00:00
|
|
|
while (isspace(*c))
|
2000-02-17 22:00:13 +00:00
|
|
|
c++;
|
|
|
|
}
|
2000-02-17 23:37:16 +00:00
|
|
|
if (cmd->is_real_cmd)
|
|
|
|
old->cmd = cmd;
|
|
|
|
else
|
|
|
|
old->help = cmd;
|
2016-04-15 09:41:08 +00:00
|
|
|
old->flags |= cmd->flags;
|
2000-02-17 22:00:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2000-02-17 23:37:16 +00:00
|
|
|
cmd_do_display_help(struct cmd_info *c)
|
2000-02-17 22:00:13 +00:00
|
|
|
{
|
|
|
|
char buf[strlen(c->command) + strlen(c->args) + 4];
|
|
|
|
|
|
|
|
sprintf(buf, "%s %s", c->command, c->args);
|
|
|
|
printf("%-45s %s\n", buf, c->help);
|
|
|
|
}
|
|
|
|
|
2000-02-17 23:37:16 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2000-02-17 22:00:13 +00:00
|
|
|
static struct cmd_node *
|
2016-04-15 09:41:08 +00:00
|
|
|
cmd_find_abbrev(struct cmd_node *root, char *cmd, uint len, int *pambiguous)
|
2000-02-17 22:00:13 +00:00
|
|
|
{
|
|
|
|
struct cmd_node *m, *best = NULL, *best2 = NULL;
|
|
|
|
|
2000-02-17 23:37:16 +00:00
|
|
|
*pambiguous = 0;
|
2000-02-17 22:00:13 +00:00
|
|
|
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))
|
|
|
|
{
|
2012-03-22 10:46:38 +00:00
|
|
|
if (best && best->prio > m->prio)
|
|
|
|
continue;
|
|
|
|
if (best && best->prio == m->prio)
|
|
|
|
best2 = best;
|
2000-02-17 22:00:13 +00:00
|
|
|
best = m;
|
|
|
|
}
|
|
|
|
}
|
2000-02-17 23:37:16 +00:00
|
|
|
if (best2)
|
|
|
|
{
|
|
|
|
*pambiguous = 1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return best;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-04-15 09:41:08 +00:00
|
|
|
cmd_list_ambiguous(struct cmd_node *root, char *cmd, uint len)
|
2000-02-17 23:37:16 +00:00
|
|
|
{
|
|
|
|
struct cmd_node *m;
|
|
|
|
|
|
|
|
for(m=root->son; m; m=m->sibling)
|
2016-04-15 09:41:08 +00:00
|
|
|
if (m->len > len && !memcmp(m->token, cmd, len))
|
2000-02-17 23:37:16 +00:00
|
|
|
cmd_display_help(m->help, m->cmd);
|
2016-04-15 09:41:08 +00:00
|
|
|
|
|
|
|
struct cli_symbol *sym;
|
|
|
|
list *syms = cli_get_symbol_list();
|
|
|
|
WALK_LIST(sym, *syms)
|
|
|
|
{
|
|
|
|
if ((sym->flags & root->flags) && sym->len > len && memcmp(sym->name, cmd, len) == 0)
|
|
|
|
printf("%s\n", sym->name);
|
|
|
|
}
|
2000-02-17 22:00:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cmd_help(char *cmd, int len)
|
|
|
|
{
|
|
|
|
char *end = cmd + len;
|
|
|
|
struct cmd_node *n, *m;
|
|
|
|
char *z;
|
2000-02-17 23:37:16 +00:00
|
|
|
int ambig;
|
2000-02-17 22:00:13 +00:00
|
|
|
|
|
|
|
n = &cmd_root;
|
|
|
|
while (cmd < end)
|
|
|
|
{
|
2000-02-27 22:00:19 +00:00
|
|
|
if (isspace(*cmd))
|
2000-02-17 22:00:13 +00:00
|
|
|
{
|
|
|
|
cmd++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
z = cmd;
|
2000-02-27 22:00:19 +00:00
|
|
|
while (cmd < end && !isspace(*cmd))
|
2000-02-17 22:00:13 +00:00
|
|
|
cmd++;
|
2000-02-17 23:37:16 +00:00
|
|
|
m = cmd_find_abbrev(n, z, cmd-z, &ambig);
|
|
|
|
if (ambig)
|
|
|
|
{
|
|
|
|
cmd_list_ambiguous(n, z, cmd-z);
|
|
|
|
return;
|
|
|
|
}
|
2000-02-17 22:00:13 +00:00
|
|
|
if (!m)
|
|
|
|
break;
|
|
|
|
n = m;
|
|
|
|
}
|
2000-02-17 23:37:16 +00:00
|
|
|
cmd_display_help(n->cmd, NULL);
|
2000-02-17 22:00:13 +00:00
|
|
|
for (m=n->son; m; m=m->sibling)
|
2000-02-17 23:37:16 +00:00
|
|
|
cmd_display_help(m->help, m->cmd);
|
|
|
|
}
|
|
|
|
|
2016-04-15 09:41:08 +00:00
|
|
|
/*
|
|
|
|
* Return length of common prefix of all matches,
|
|
|
|
* Write common prefix string into buf
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
cmd_merge_match_with_others(int max_common_len, const char *token_name, int token_len, char *buf, int from)
|
|
|
|
{
|
|
|
|
if (max_common_len < 0)
|
|
|
|
{
|
|
|
|
/* For a case that we'll have exactly one match */
|
|
|
|
strcpy(buf, token_name + from);
|
|
|
|
max_common_len = token_len - from;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
while (i < max_common_len && i < token_len - from && buf[i] == token_name[from+i])
|
|
|
|
i++;
|
|
|
|
max_common_len = i;
|
|
|
|
}
|
|
|
|
return max_common_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return length of common prefix of all matches,
|
|
|
|
* Write count of all matches into pcount,
|
|
|
|
* Write common prefix string into buf
|
|
|
|
*/
|
2000-02-17 23:37:16 +00:00
|
|
|
static int
|
2016-04-15 09:41:08 +00:00
|
|
|
cmd_find_common_match(struct cmd_node *root, char *cmd, uint len, int *pcount, char *buf)
|
2000-02-17 23:37:16 +00:00
|
|
|
{
|
|
|
|
struct cmd_node *m;
|
2016-04-15 09:41:08 +00:00
|
|
|
int max_common_len;
|
|
|
|
int best_prio;
|
2000-02-17 23:37:16 +00:00
|
|
|
|
|
|
|
*pcount = 0;
|
2016-04-15 09:41:08 +00:00
|
|
|
max_common_len = -1;
|
2012-03-22 10:46:38 +00:00
|
|
|
best_prio = -1;
|
2000-02-17 23:37:16 +00:00
|
|
|
for(m=root->son; m; m=m->sibling)
|
|
|
|
{
|
|
|
|
if (m->len < len || memcmp(m->token, cmd, len))
|
|
|
|
continue;
|
2012-03-22 10:46:38 +00:00
|
|
|
|
|
|
|
if (best_prio > m->prio)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (best_prio < m->prio)
|
|
|
|
{
|
|
|
|
*pcount = 0;
|
2016-04-15 09:41:08 +00:00
|
|
|
max_common_len = -1;
|
2012-03-22 10:46:38 +00:00
|
|
|
}
|
|
|
|
|
2016-04-15 09:41:08 +00:00
|
|
|
if (max_common_len < 0)
|
|
|
|
best_prio = m->prio;
|
|
|
|
|
2000-02-17 23:37:16 +00:00
|
|
|
(*pcount)++;
|
2016-04-15 09:41:08 +00:00
|
|
|
max_common_len = cmd_merge_match_with_others(max_common_len, m->token, m->len, buf, len);
|
2000-02-17 23:37:16 +00:00
|
|
|
}
|
2016-04-15 09:41:08 +00:00
|
|
|
|
|
|
|
list *syms = cli_get_symbol_list();
|
|
|
|
struct cli_symbol *sym;
|
|
|
|
WALK_LIST(sym, *syms)
|
|
|
|
{
|
|
|
|
if (!(sym->flags & root->flags))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (sym->len < len || memcmp(sym->name, cmd, len))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
(*pcount)++;
|
|
|
|
max_common_len = cmd_merge_match_with_others(max_common_len, sym->name, sym->len, buf, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
return max_common_len;
|
2000-02-17 23:37:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
cmd_complete(char *cmd, int len, char *buf, int again)
|
|
|
|
{
|
|
|
|
char *start = cmd;
|
|
|
|
char *end = cmd + len;
|
|
|
|
char *fin;
|
2016-04-15 09:41:08 +00:00
|
|
|
struct cmd_node *n, *m = NULL;
|
2000-02-17 23:37:16 +00:00
|
|
|
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;
|
2016-04-15 09:41:08 +00:00
|
|
|
while (cmd < fin)
|
2000-02-17 23:37:16 +00:00
|
|
|
{
|
2000-02-27 22:00:19 +00:00
|
|
|
if (isspace(*cmd))
|
2000-02-17 23:37:16 +00:00
|
|
|
{
|
|
|
|
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;
|
2016-04-15 09:41:08 +00:00
|
|
|
|
|
|
|
/* Try skip a parameter/word */
|
|
|
|
if (m->flags & CLI_SF_PARAMETER)
|
|
|
|
{
|
|
|
|
z = cmd;
|
|
|
|
|
|
|
|
/* Skip spaces before parameter */
|
|
|
|
while (cmd < fin && isspace(*cmd))
|
|
|
|
cmd++;
|
|
|
|
|
|
|
|
/* Skip one parameter/word */
|
|
|
|
while (cmd < fin && !isspace(*cmd))
|
|
|
|
cmd++;
|
|
|
|
|
|
|
|
/* Check ending of parameter */
|
|
|
|
if (isspace(*cmd))
|
|
|
|
{
|
|
|
|
if (m->flags & CLI_SF_OPTIONAL)
|
|
|
|
m = n;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
cmd = z;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do not enter to optional command nodes */
|
|
|
|
if (!(m->flags & CLI_SF_OPTIONAL))
|
|
|
|
n = m;
|
2000-02-17 23:37:16 +00:00
|
|
|
}
|
|
|
|
|
2016-04-15 09:41:08 +00:00
|
|
|
/* Enter to the last command node */
|
|
|
|
if (m && (m->flags & CLI_SF_PARAMETER))
|
|
|
|
n = m;
|
2000-02-17 23:37:16 +00:00
|
|
|
|
|
|
|
/* 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;
|
2000-02-17 22:00:13 +00:00
|
|
|
}
|
2000-02-27 22:00:19 +00:00
|
|
|
|
|
|
|
char *
|
|
|
|
cmd_expand(char *cmd)
|
|
|
|
{
|
2016-04-15 09:41:08 +00:00
|
|
|
struct cmd_node *n, *m, *last_real_cmd = NULL;
|
|
|
|
char *c, *b, *args, *lrc_args = NULL;
|
2000-02-27 22:00:19 +00:00
|
|
|
int ambig;
|
|
|
|
|
|
|
|
args = c = cmd;
|
|
|
|
n = &cmd_root;
|
|
|
|
while (*c)
|
|
|
|
{
|
|
|
|
if (isspace(*c))
|
|
|
|
{
|
|
|
|
c++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
b = c;
|
|
|
|
while (*c && !isspace(*c))
|
|
|
|
c++;
|
|
|
|
m = cmd_find_abbrev(n, b, c-b, &ambig);
|
|
|
|
if (!m)
|
|
|
|
{
|
|
|
|
if (!ambig)
|
|
|
|
break;
|
|
|
|
puts("Ambiguous command, possible expansions are:");
|
|
|
|
cmd_list_ambiguous(n, b, c-b);
|
|
|
|
return NULL;
|
|
|
|
}
|
2016-04-15 09:41:08 +00:00
|
|
|
|
2000-02-27 22:00:19 +00:00
|
|
|
args = c;
|
|
|
|
n = m;
|
2016-04-15 09:41:08 +00:00
|
|
|
|
|
|
|
if (m->cmd)
|
|
|
|
{
|
|
|
|
last_real_cmd = m;
|
|
|
|
lrc_args = c;
|
|
|
|
}
|
2000-02-27 22:00:19 +00:00
|
|
|
}
|
2016-04-15 09:41:08 +00:00
|
|
|
|
|
|
|
if (!n->cmd && !last_real_cmd)
|
2000-02-27 22:00:19 +00:00
|
|
|
{
|
2000-05-31 22:39:06 +00:00
|
|
|
puts("No such command. Press `?' for help.");
|
2000-02-27 22:00:19 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2016-04-15 09:41:08 +00:00
|
|
|
|
|
|
|
if (last_real_cmd && last_real_cmd != n)
|
|
|
|
{
|
|
|
|
n = last_real_cmd;
|
|
|
|
args = lrc_args;
|
|
|
|
}
|
2016-05-12 19:29:04 +00:00
|
|
|
b = malloc(strlen(n->cmd->command) + strlen(args) + 1);
|
2000-02-27 22:00:19 +00:00
|
|
|
sprintf(b, "%s%s", n->cmd->command, args);
|
|
|
|
return b;
|
|
|
|
}
|