]>
Commit | Line | Data |
---|---|---|
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 | ||
18 | struct cmd_info { | |
19 | char *command; | |
20 | char *args; | |
21 | char *help; | |
0223d4ff | 22 | int is_real_cmd; |
9fac310d MM |
23 | }; |
24 | ||
0223d4ff | 25 | static struct cmd_info command_table[] = { |
9fac310d MM |
26 | #include "conf/commands.h" |
27 | }; | |
0223d4ff | 28 | |
0223d4ff MM |
29 | struct 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 | ||
37 | static struct cmd_node cmd_root; | |
38 | ||
c72b660b OZ |
39 | #define isspace_(X) isspace((unsigned char) (X)) |
40 | ||
0223d4ff MM |
41 | void |
42 | cmd_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 | ||
86 | static void | |
fae0396e | 87 | cmd_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 |
95 | static void |
96 | cmd_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 | 104 | static struct cmd_node * |
fae0396e | 105 | cmd_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 | ||
131 | static void | |
132 | cmd_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 | ||
141 | void | |
142 | cmd_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 | ||
175 | static int | |
176 | cmd_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 | ||
216 | int | |
217 | cmd_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 | |
284 | char * | |
285 | cmd_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 | } |