]> git.ipfire.org Git - thirdparty/bird.git/blame - client/commands.c
Conf: Allow keywords to be redefined by user symbols
[thirdparty/bird.git] / client / commands.c
CommitLineData
9fac310d
MM
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
0223d4ff 9#include <stdio.h>
044e123f 10#include <stdlib.h>
fae0396e 11#include <ctype.h>
0223d4ff 12
9fac310d 13#include "nest/bird.h"
0223d4ff 14#include "lib/resource.h"
221135d6 15#include "lib/string.h"
9fac310d
MM
16#include "client/client.h"
17
18struct cmd_info {
19 char *command;
20 char *args;
21 char *help;
0223d4ff 22 int is_real_cmd;
9fac310d
MM
23};
24
0223d4ff 25static struct cmd_info command_table[] = {
9fac310d
MM
26#include "conf/commands.h"
27};
0223d4ff 28
0223d4ff
MM
29struct cmd_node {
30 struct cmd_node *sibling, *son, **plastson;
fae0396e 31 struct cmd_info *cmd, *help;
0223d4ff 32 int len;
c47d037e 33 signed char prio;
0223d4ff
MM
34 char token[1];
35};
36
37static struct cmd_node cmd_root;
38
c72b660b
OZ
39#define isspace_(X) isspace((unsigned char) (X))
40
0223d4ff
MM
41void
42cmd_build_tree(void)
43{
ae80a2de 44 uint i;
0223d4ff
MM
45
46 cmd_root.plastson = &cmd_root.son;
47
77506349 48 for(i=0; i<ARRAY_SIZE(command_table); i++)
0223d4ff
MM
49 {
50 struct cmd_info *cmd = &command_table[i];
51 struct cmd_node *old, *new;
52 char *c = cmd->command;
53
54 old = &cmd_root;
55 while (*c)
56 {
57 char *d = c;
c72b660b 58 while (*c && !isspace_(*c))
0223d4ff
MM
59 c++;
60 for(new=old->son; new; new=new->sibling)
61 if (new->len == c-d && !memcmp(new->token, d, c-d))
62 break;
63 if (!new)
64 {
fae0396e 65 int size = sizeof(struct cmd_node) + c-d;
3f2c7600 66 new = malloc(size);
fae0396e 67 bzero(new, size);
0223d4ff
MM
68 *old->plastson = new;
69 old->plastson = &new->sibling;
0223d4ff 70 new->plastson = &new->son;
0223d4ff
MM
71 new->len = c-d;
72 memcpy(new->token, d, c-d);
8465dccb 73 new->prio = (new->len == 3 && (!memcmp(new->token, "roa", 3) || !memcmp(new->token, "rip", 3))) ? 0 : 1; /* Hack */
0223d4ff
MM
74 }
75 old = new;
c72b660b 76 while (isspace_(*c))
0223d4ff
MM
77 c++;
78 }
fae0396e
MM
79 if (cmd->is_real_cmd)
80 old->cmd = cmd;
81 else
82 old->help = cmd;
0223d4ff
MM
83 }
84}
85
86static void
fae0396e 87cmd_do_display_help(struct cmd_info *c)
0223d4ff
MM
88{
89 char buf[strlen(c->command) + strlen(c->args) + 4];
90
91 sprintf(buf, "%s %s", c->command, c->args);
92 printf("%-45s %s\n", buf, c->help);
93}
94
fae0396e
MM
95static void
96cmd_display_help(struct cmd_info *c1, struct cmd_info *c2)
97{
98 if (c1)
99 cmd_do_display_help(c1);
100 else if (c2)
101 cmd_do_display_help(c2);
102}
103
0223d4ff 104static struct cmd_node *
fae0396e 105cmd_find_abbrev(struct cmd_node *root, char *cmd, int len, int *pambiguous)
0223d4ff
MM
106{
107 struct cmd_node *m, *best = NULL, *best2 = NULL;
108
fae0396e 109 *pambiguous = 0;
0223d4ff
MM
110 for(m=root->son; m; m=m->sibling)
111 {
112 if (m->len == len && !memcmp(m->token, cmd, len))
113 return m;
114 if (m->len > len && !memcmp(m->token, cmd, len))
115 {
c47d037e
OZ
116 if (best && best->prio > m->prio)
117 continue;
118 if (best && best->prio == m->prio)
119 best2 = best;
0223d4ff
MM
120 best = m;
121 }
122 }
fae0396e
MM
123 if (best2)
124 {
125 *pambiguous = 1;
126 return NULL;
127 }
128 return best;
129}
130
131static void
132cmd_list_ambiguous(struct cmd_node *root, char *cmd, int len)
133{
134 struct cmd_node *m;
135
136 for(m=root->son; m; m=m->sibling)
c72b660b 137 if (m->len > len && !memcmp(m->token, cmd, len))
fae0396e 138 cmd_display_help(m->help, m->cmd);
0223d4ff
MM
139}
140
141void
142cmd_help(char *cmd, int len)
143{
144 char *end = cmd + len;
145 struct cmd_node *n, *m;
146 char *z;
fae0396e 147 int ambig;
0223d4ff
MM
148
149 n = &cmd_root;
150 while (cmd < end)
151 {
c72b660b 152 if (isspace_(*cmd))
0223d4ff
MM
153 {
154 cmd++;
155 continue;
156 }
157 z = cmd;
c72b660b 158 while (cmd < end && !isspace_(*cmd))
0223d4ff 159 cmd++;
fae0396e
MM
160 m = cmd_find_abbrev(n, z, cmd-z, &ambig);
161 if (ambig)
162 {
163 cmd_list_ambiguous(n, z, cmd-z);
164 return;
165 }
0223d4ff
MM
166 if (!m)
167 break;
168 n = m;
169 }
fae0396e 170 cmd_display_help(n->cmd, NULL);
0223d4ff 171 for (m=n->son; m; m=m->sibling)
fae0396e
MM
172 cmd_display_help(m->help, m->cmd);
173}
174
175static int
176cmd_find_common_match(struct cmd_node *root, char *cmd, int len, int *pcount, char *buf)
177{
178 struct cmd_node *m;
c47d037e 179 int best, best_prio, i;
fae0396e
MM
180
181 *pcount = 0;
182 best = -1;
c47d037e 183 best_prio = -1;
fae0396e
MM
184 for(m=root->son; m; m=m->sibling)
185 {
186 if (m->len < len || memcmp(m->token, cmd, len))
187 continue;
c47d037e
OZ
188
189 if (best_prio > m->prio)
190 continue;
191
192 if (best_prio < m->prio)
193 {
194 *pcount = 0;
195 best = -1;
196 }
197
fae0396e
MM
198 (*pcount)++;
199 if (best < 0)
200 {
201 strcpy(buf, m->token + len);
202 best = m->len - len;
c47d037e 203 best_prio = m->prio;
fae0396e
MM
204 }
205 else
206 {
207 i = 0;
208 while (i < best && i < m->len - len && buf[i] == m->token[len+i])
209 i++;
210 best = i;
211 }
212 }
213 return best;
214}
215
216int
217cmd_complete(char *cmd, int len, char *buf, int again)
218{
219 char *start = cmd;
220 char *end = cmd + len;
221 char *fin;
222 struct cmd_node *n, *m;
223 char *z;
224 int ambig, cnt = 0, common;
225
226 /* Find the last word we want to complete */
c72b660b 227 for(fin=end; fin > start && !isspace_(fin[-1]); fin--)
fae0396e
MM
228 ;
229
230 /* Find the context */
231 n = &cmd_root;
232 while (cmd < fin && n->son)
233 {
c72b660b 234 if (isspace_(*cmd))
fae0396e
MM
235 {
236 cmd++;
237 continue;
238 }
239 z = cmd;
c72b660b 240 while (cmd < fin && !isspace_(*cmd))
fae0396e
MM
241 cmd++;
242 m = cmd_find_abbrev(n, z, cmd-z, &ambig);
243 if (ambig)
244 {
245 if (!again)
246 return -1;
247 input_start_list();
248 cmd_list_ambiguous(n, z, cmd-z);
249 input_stop_list();
250 return 0;
251 }
252 if (!m)
253 return -1;
254 n = m;
255 }
256
257 /* Completion of parameters is not yet supported */
258 if (!n->son)
259 return -1;
260
261 /* We know the context, let's try to complete */
262 common = cmd_find_common_match(n, fin, end-fin, &cnt, buf);
263 if (!cnt)
264 return -1;
265 if (cnt == 1)
266 {
267 buf[common++] = ' ';
268 buf[common] = 0;
269 return 1;
270 }
271 if (common > 0)
272 {
273 buf[common] = 0;
274 return 1;
275 }
276 if (!again)
277 return -1;
278 input_start_list();
279 cmd_list_ambiguous(n, fin, end-fin);
280 input_stop_list();
281 return 0;
0223d4ff 282}
e69e4ed9
MM
283
284char *
285cmd_expand(char *cmd)
286{
287 struct cmd_node *n, *m;
288 char *c, *b, *args;
289 int ambig;
290
291 args = c = cmd;
292 n = &cmd_root;
293 while (*c)
294 {
c72b660b 295 if (isspace_(*c))
e69e4ed9
MM
296 {
297 c++;
298 continue;
299 }
300 b = c;
c72b660b 301 while (*c && !isspace_(*c))
e69e4ed9
MM
302 c++;
303 m = cmd_find_abbrev(n, b, c-b, &ambig);
304 if (!m)
305 {
306 if (!ambig)
307 break;
308 puts("Ambiguous command, possible expansions are:");
309 cmd_list_ambiguous(n, b, c-b);
310 return NULL;
311 }
312 args = c;
313 n = m;
314 }
315 if (!n->cmd)
316 {
2983460b 317 puts("No such command. Press `?' for help.");
e69e4ed9
MM
318 return NULL;
319 }
3f2c7600 320 b = malloc(strlen(n->cmd->command) + strlen(args) + 1);
e69e4ed9
MM
321 sprintf(b, "%s%s", n->cmd->command, args);
322 return b;
323}