]>
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 MM |
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 | ||
77506349 | 44 | for(i=0; i<ARRAY_SIZE(command_table); i++) |
0223d4ff MM |
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; | |
e69e4ed9 | 54 | while (*c && !isspace(*c)) |
0223d4ff MM |
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 | { | |
fae0396e MM |
61 | int size = sizeof(struct cmd_node) + c-d; |
62 | new = xmalloc(size); | |
63 | bzero(new, size); | |
0223d4ff MM |
64 | *old->plastson = new; |
65 | old->plastson = &new->sibling; | |
0223d4ff | 66 | new->plastson = &new->son; |
0223d4ff MM |
67 | new->len = c-d; |
68 | memcpy(new->token, d, c-d); | |
0223d4ff MM |
69 | } |
70 | old = new; | |
e69e4ed9 | 71 | while (isspace(*c)) |
0223d4ff MM |
72 | c++; |
73 | } | |
fae0396e MM |
74 | if (cmd->is_real_cmd) |
75 | old->cmd = cmd; | |
76 | else | |
77 | old->help = cmd; | |
0223d4ff MM |
78 | } |
79 | } | |
80 | ||
81 | static void | |
fae0396e | 82 | cmd_do_display_help(struct cmd_info *c) |
0223d4ff MM |
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 | ||
fae0396e MM |
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 | ||
0223d4ff | 99 | static struct cmd_node * |
fae0396e | 100 | cmd_find_abbrev(struct cmd_node *root, char *cmd, int len, int *pambiguous) |
0223d4ff MM |
101 | { |
102 | struct cmd_node *m, *best = NULL, *best2 = NULL; | |
103 | ||
fae0396e | 104 | *pambiguous = 0; |
0223d4ff MM |
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 | } | |
fae0396e MM |
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); | |
0223d4ff MM |
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; | |
fae0396e | 139 | int ambig; |
0223d4ff MM |
140 | |
141 | n = &cmd_root; | |
142 | while (cmd < end) | |
143 | { | |
e69e4ed9 | 144 | if (isspace(*cmd)) |
0223d4ff MM |
145 | { |
146 | cmd++; | |
147 | continue; | |
148 | } | |
149 | z = cmd; | |
e69e4ed9 | 150 | while (cmd < end && !isspace(*cmd)) |
0223d4ff | 151 | cmd++; |
fae0396e MM |
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 | } | |
0223d4ff MM |
158 | if (!m) |
159 | break; | |
160 | n = m; | |
161 | } | |
fae0396e | 162 | cmd_display_help(n->cmd, NULL); |
0223d4ff | 163 | for (m=n->son; m; m=m->sibling) |
fae0396e MM |
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 | { | |
e69e4ed9 | 214 | if (isspace(*cmd)) |
fae0396e MM |
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; | |
0223d4ff | 262 | } |
e69e4ed9 MM |
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 | { | |
2983460b | 297 | puts("No such command. Press `?' for help."); |
e69e4ed9 MM |
298 | return NULL; |
299 | } | |
f33c6c66 | 300 | b = xmalloc(strlen(n->cmd->command) + strlen(args) + 1); |
e69e4ed9 MM |
301 | sprintf(b, "%s%s", n->cmd->command, args); |
302 | return b; | |
303 | } |