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