]> git.ipfire.org Git - thirdparty/bird.git/blob - client/commands.c
Completion works. Unfortunately, we have to access a couple of internal
[thirdparty/bird.git] / client / commands.c
1 /*
2 * BIRD Client -- Command Handling
3 *
4 * (c) 1999--2000 Martin Mares <mj@ucw.cz>
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL.
7 */
8
9 #include <stdio.h>
10 #include <string.h>
11 #include <ctype.h>
12
13 #include "nest/bird.h"
14 #include "lib/resource.h"
15 #include "client/client.h"
16
17 struct cmd_info {
18 char *command;
19 char *args;
20 char *help;
21 int is_real_cmd;
22 };
23
24 static struct cmd_info command_table[] = {
25 #include "conf/commands.h"
26 };
27
28 /* FIXME: There should exist some system of aliases, so that `show' can be abbreviated as `s' etc. */
29
30 struct cmd_node {
31 struct cmd_node *sibling, *son, **plastson;
32 struct cmd_info *cmd, *help;
33 int len;
34 char token[1];
35 };
36
37 static struct cmd_node cmd_root;
38
39 void
40 cmd_build_tree(void)
41 {
42 unsigned int i;
43
44 cmd_root.plastson = &cmd_root.son;
45
46 for(i=0; i<sizeof(command_table) / sizeof(struct cmd_info); i++)
47 {
48 struct cmd_info *cmd = &command_table[i];
49 struct cmd_node *old, *new;
50 char *c = cmd->command;
51
52 old = &cmd_root;
53 while (*c)
54 {
55 char *d = c;
56 while (*c && *c != ' ')
57 c++;
58 for(new=old->son; new; new=new->sibling)
59 if (new->len == c-d && !memcmp(new->token, d, c-d))
60 break;
61 if (!new)
62 {
63 int size = sizeof(struct cmd_node) + c-d;
64 new = xmalloc(size);
65 bzero(new, size);
66 *old->plastson = new;
67 old->plastson = &new->sibling;
68 new->plastson = &new->son;
69 new->len = c-d;
70 memcpy(new->token, d, c-d);
71 }
72 old = new;
73 while (*c == ' ')
74 c++;
75 }
76 if (cmd->is_real_cmd)
77 old->cmd = cmd;
78 else
79 old->help = cmd;
80 }
81 }
82
83 static void
84 cmd_do_display_help(struct cmd_info *c)
85 {
86 char buf[strlen(c->command) + strlen(c->args) + 4];
87
88 sprintf(buf, "%s %s", c->command, c->args);
89 printf("%-45s %s\n", buf, c->help);
90 }
91
92 static void
93 cmd_display_help(struct cmd_info *c1, struct cmd_info *c2)
94 {
95 if (c1)
96 cmd_do_display_help(c1);
97 else if (c2)
98 cmd_do_display_help(c2);
99 }
100
101 static struct cmd_node *
102 cmd_find_abbrev(struct cmd_node *root, char *cmd, int len, int *pambiguous)
103 {
104 struct cmd_node *m, *best = NULL, *best2 = NULL;
105
106 *pambiguous = 0;
107 for(m=root->son; m; m=m->sibling)
108 {
109 if (m->len == len && !memcmp(m->token, cmd, len))
110 return m;
111 if (m->len > len && !memcmp(m->token, cmd, len))
112 {
113 best2 = best;
114 best = m;
115 }
116 }
117 if (best2)
118 {
119 *pambiguous = 1;
120 return NULL;
121 }
122 return best;
123 }
124
125 static void
126 cmd_list_ambiguous(struct cmd_node *root, char *cmd, int len)
127 {
128 struct cmd_node *m;
129
130 for(m=root->son; m; m=m->sibling)
131 if (m->len > len && !memcmp(m->token, cmd, len))
132 cmd_display_help(m->help, m->cmd);
133 }
134
135 void
136 cmd_help(char *cmd, int len)
137 {
138 char *end = cmd + len;
139 struct cmd_node *n, *m;
140 char *z;
141 int ambig;
142
143 n = &cmd_root;
144 while (cmd < end)
145 {
146 if (*cmd == ' ' || *cmd == '\t')
147 {
148 cmd++;
149 continue;
150 }
151 z = cmd;
152 while (cmd < end && *cmd != ' ' && *cmd != '\t')
153 cmd++;
154 m = cmd_find_abbrev(n, z, cmd-z, &ambig);
155 if (ambig)
156 {
157 cmd_list_ambiguous(n, z, cmd-z);
158 return;
159 }
160 if (!m)
161 break;
162 n = m;
163 }
164 cmd_display_help(n->cmd, NULL);
165 for (m=n->son; m; m=m->sibling)
166 cmd_display_help(m->help, m->cmd);
167 }
168
169 static int
170 cmd_find_common_match(struct cmd_node *root, char *cmd, int len, int *pcount, char *buf)
171 {
172 struct cmd_node *m;
173 int best, i;
174
175 *pcount = 0;
176 best = -1;
177 for(m=root->son; m; m=m->sibling)
178 {
179 if (m->len < len || memcmp(m->token, cmd, len))
180 continue;
181 (*pcount)++;
182 if (best < 0)
183 {
184 strcpy(buf, m->token + len);
185 best = m->len - len;
186 }
187 else
188 {
189 i = 0;
190 while (i < best && i < m->len - len && buf[i] == m->token[len+i])
191 i++;
192 best = i;
193 }
194 }
195 return best;
196 }
197
198 int
199 cmd_complete(char *cmd, int len, char *buf, int again)
200 {
201 char *start = cmd;
202 char *end = cmd + len;
203 char *fin;
204 struct cmd_node *n, *m;
205 char *z;
206 int ambig, cnt = 0, common;
207
208 /* Find the last word we want to complete */
209 for(fin=end; fin > start && !isspace(fin[-1]); fin--)
210 ;
211
212 /* Find the context */
213 n = &cmd_root;
214 while (cmd < fin && n->son)
215 {
216 if (*cmd == ' ' || *cmd == '\t')
217 {
218 cmd++;
219 continue;
220 }
221 z = cmd;
222 while (cmd < fin && !isspace(*cmd))
223 cmd++;
224 m = cmd_find_abbrev(n, z, cmd-z, &ambig);
225 if (ambig)
226 {
227 if (!again)
228 return -1;
229 input_start_list();
230 cmd_list_ambiguous(n, z, cmd-z);
231 input_stop_list();
232 return 0;
233 }
234 if (!m)
235 return -1;
236 n = m;
237 }
238
239 /* Completion of parameters is not yet supported */
240 if (!n->son)
241 return -1;
242
243 /* We know the context, let's try to complete */
244 common = cmd_find_common_match(n, fin, end-fin, &cnt, buf);
245 if (!cnt)
246 return -1;
247 if (cnt == 1)
248 {
249 buf[common++] = ' ';
250 buf[common] = 0;
251 return 1;
252 }
253 if (common > 0)
254 {
255 buf[common] = 0;
256 return 1;
257 }
258 if (!again)
259 return -1;
260 input_start_list();
261 cmd_list_ambiguous(n, fin, end-fin);
262 input_stop_list();
263 return 0;
264 }