summaryrefslogtreecommitdiff
path: root/client/commands.c
diff options
context:
space:
mode:
authorMartin Mares <mj@ucw.cz>2000-02-17 23:37:16 +0000
committerMartin Mares <mj@ucw.cz>2000-02-17 23:37:16 +0000
commitfae0396ea4fd9d2530086eef77b8a11b6640d640 (patch)
treee20a4d7513feabab3f926c85f7098eaf95349809 /client/commands.c
parent0223d4fff11badc03470b4320fa9dfe28afd1bed (diff)
Completion works. Unfortunately, we have to access a couple of internal
symbols of libreadline :-(
Diffstat (limited to 'client/commands.c')
-rw-r--r--client/commands.c157
1 files changed, 144 insertions, 13 deletions
diff --git a/client/commands.c b/client/commands.c
index ceb036b1..09c5c344 100644
--- a/client/commands.c
+++ b/client/commands.c
@@ -7,6 +7,8 @@
*/
#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
#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;
}