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