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