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 <stdlib.h>
11: #include <ctype.h>
12:
13: #include "nest/bird.h"
14: #include "lib/resource.h"
15: #include "lib/string.h"
16: #include "client/client.h"
17:
18: struct cmd_info {
19: char *command;
20: char *args;
21: char *help;
22: int is_real_cmd;
23: };
24:
25: static struct cmd_info command_table[] = {
26: #include "conf/commands.h"
27: };
28:
29: struct cmd_node {
30: struct cmd_node *sibling, *son, **plastson;
31: struct cmd_info *cmd, *help;
32: int len;
33: signed char prio;
34: char token[1];
35: };
36:
37: static struct cmd_node cmd_root;
38:
39: #define isspace_(X) isspace((unsigned char) (X))
40:
41: void
42: cmd_build_tree(void)
43: {
44: uint i;
45:
46: cmd_root.plastson = &cmd_root.son;
47:
48: for(i=0; i<ARRAY_SIZE(command_table); i++)
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;
58: while (*c && !isspace_(*c))
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: {
65: int size = sizeof(struct cmd_node) + c-d;
66: new = malloc(size);
67: bzero(new, size);
68: *old->plastson = new;
69: old->plastson = &new->sibling;
70: new->plastson = &new->son;
71: new->len = c-d;
72: memcpy(new->token, d, c-d);
73: new->prio = (new->len == 3 && (!memcmp(new->token, "roa", 3) || !memcmp(new->token, "rip", 3))) ? 0 : 1; /* Hack */
74: }
75: old = new;
76: while (isspace_(*c))
77: c++;
78: }
79: if (cmd->is_real_cmd)
80: old->cmd = cmd;
81: else
82: old->help = cmd;
83: }
84: }
85:
86: static void
87: cmd_do_display_help(struct cmd_info *c)
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:
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:
104: static struct cmd_node *
105: cmd_find_abbrev(struct cmd_node *root, char *cmd, int len, int *pambiguous)
106: {
107: struct cmd_node *m, *best = NULL, *best2 = NULL;
108:
109: *pambiguous = 0;
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: {
116: if (best && best->prio > m->prio)
117: continue;
118: if (best && best->prio == m->prio)
119: best2 = best;
120: best = m;
121: }
122: }
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)
137: if (m->len > len && !memcmp(m->token, cmd, len))
138: cmd_display_help(m->help, m->cmd);
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;
147: int ambig;
148:
149: n = &cmd_root;
150: while (cmd < end)
151: {
152: if (isspace_(*cmd))
153: {
154: cmd++;
155: continue;
156: }
157: z = cmd;
158: while (cmd < end && !isspace_(*cmd))
159: cmd++;
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: }
166: if (!m)
167: break;
168: n = m;
169: }
170: cmd_display_help(n->cmd, NULL);
171: for (m=n->son; m; m=m->sibling)
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;
179: int best, best_prio, i;
180:
181: *pcount = 0;
182: best = -1;
183: best_prio = -1;
184: for(m=root->son; m; m=m->sibling)
185: {
186: if (m->len < len || memcmp(m->token, cmd, len))
187: continue;
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:
198: (*pcount)++;
199: if (best < 0)
200: {
201: strcpy(buf, m->token + len);
202: best = m->len - len;
203: best_prio = m->prio;
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 */
227: for(fin=end; fin > start && !isspace_(fin[-1]); fin--)
228: ;
229:
230: /* Find the context */
231: n = &cmd_root;
232: while (cmd < fin && n->son)
233: {
234: if (isspace_(*cmd))
235: {
236: cmd++;
237: continue;
238: }
239: z = cmd;
240: while (cmd < fin && !isspace_(*cmd))
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;
282: }
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: {
295: if (isspace_(*c))
296: {
297: c++;
298: continue;
299: }
300: b = c;
301: while (*c && !isspace_(*c))
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: {
317: puts("No such command. Press `?' for help.");
318: return NULL;
319: }
320: b = malloc(strlen(n->cmd->command) + strlen(args) + 1);
321: sprintf(b, "%s%s", n->cmd->command, args);
322: return b;
323: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>