]>
Commit | Line | Data |
---|---|---|
c86edd1d Q |
1 | /* parser.c */ |
2 | ||
3 | #include "parser.h" | |
4 | #include "../lib/sstring.h" | |
5 | #include "../lib/irc_string.h" | |
6 | #include "../core/error.h" | |
7 | #include <assert.h> | |
8 | #include <stdlib.h> | |
9 | #include <string.h> | |
10 | #include <ctype.h> | |
11 | #include <stdio.h> | |
12 | ||
13 | /* Local functions */ | |
14 | int insertcommand(Command *c, CommandTree *ct, int depth); | |
15 | int deletecommand(sstring *cmdname, CommandTree *ct, int depth, CommandHandler handler); | |
16 | Command *findcommand(CommandTree *ct, const char *command, int depth); | |
17 | int countcommandtree(CommandTree *ct); | |
18 | ||
19 | /* newcommandtree: | |
20 | * | |
21 | * This creates a new command tree. | |
22 | * | |
23 | * Uses malloc() as it's a pretty rare event. | |
24 | */ | |
25 | ||
26 | CommandTree *newcommandtree() { | |
27 | CommandTree *nct; | |
28 | ||
29 | nct=(void *)malloc(sizeof(CommandTree)); | |
30 | memset(nct,0,sizeof(CommandTree)); | |
31 | ||
32 | return nct; | |
33 | } | |
34 | ||
35 | /* destroycommandtree: | |
36 | * | |
37 | * This frees a tree and all it's subtrees | |
38 | */ | |
39 | ||
40 | void destroycommandtree(CommandTree *ct) { | |
41 | int i; | |
42 | ||
43 | for (i=0;i<26;i++) { | |
44 | if (ct->next[i]) { | |
45 | destroycommandtree((CommandTree *)ct->next[i]); | |
46 | } | |
47 | } | |
429f366c P |
48 | |
49 | if(ct->cmd) { | |
50 | if(ct->cmd->command) | |
51 | freesstring(ct->cmd->command); | |
52 | if(ct->cmd->help) | |
53 | free(ct->cmd->help); | |
54 | free(ct->cmd); | |
55 | } | |
c86edd1d Q |
56 | free(ct); |
57 | } | |
58 | ||
59 | /* countcommandtree: | |
60 | * | |
61 | * This returns the number of commands registered in | |
62 | * a particular (sub-)tree. This will come in handy | |
63 | * later for deleting commands etc. | |
64 | */ | |
65 | ||
66 | int countcommandtree(CommandTree *ct) { | |
67 | int i; | |
68 | int sum; | |
69 | ||
70 | if (ct->cmd!=NULL) { | |
71 | sum=1; | |
72 | } else { | |
73 | sum=0; | |
74 | } | |
75 | ||
76 | for(i=0;i<26;i++) | |
77 | if (ct->next[i]) | |
78 | sum+=countcommandtree((CommandTree *)ct->next[i]); | |
79 | ||
80 | return sum; | |
81 | } | |
82 | ||
3b8df066 | 83 | /* sanitisecommandname: |
84 | * | |
85 | * Converts the given command name to uppercase and checks for bad chars. | |
86 | * | |
87 | * Returns 1 if bad chars were found | |
88 | */ | |
3b2d84f5 | 89 | static int sanitisecommandname(const char *cmdname, char *cmdbuf) { |
3b8df066 | 90 | int len,i; |
91 | ||
92 | strncpy(cmdbuf,cmdname,MAX_COMMAND_LEN); | |
93 | cmdbuf[MAX_COMMAND_LEN-1]='\0'; | |
94 | ||
95 | len=strlen(cmdbuf); | |
96 | ||
97 | /* Sanity check the string */ | |
98 | for (i=0;i<len;i++) { | |
99 | cmdbuf[i]=toupper(cmdbuf[i]); | |
100 | if (cmdbuf[i]<'A' || cmdbuf[i]>'Z') { | |
101 | /* Someone tried to register an invalid command name */ | |
102 | return 1; | |
103 | } | |
104 | } | |
105 | ||
106 | return 0; | |
107 | } | |
108 | ||
c86edd1d | 109 | /* |
38cee035 | 110 | * addcommandhelptotree: |
c86edd1d | 111 | * |
38cee035 | 112 | * This installs a specific command in a tree, with a help paramater. |
c86edd1d Q |
113 | * |
114 | * This function builds the Command structure in addition to | |
115 | * installing it in the tree | |
116 | */ | |
117 | ||
38cee035 | 118 | Command *addcommandhelptotree(CommandTree *ct, const char *cmdname, int level, int maxparams, CommandHandler handler, const char *help) { |
c86edd1d | 119 | Command *nc, *c; |
3b8df066 | 120 | char cmdbuf[MAX_COMMAND_LEN]; |
121 | ||
122 | if (sanitisecommandname(cmdname, cmdbuf)) | |
123 | return NULL; | |
124 | ||
c86edd1d Q |
125 | /* Generate the struct.. */ |
126 | nc=(void *)malloc(sizeof(Command)); | |
3b8df066 | 127 | nc->command=getsstring(cmdbuf, MAX_COMMAND_LEN); |
c86edd1d Q |
128 | nc->level=level; |
129 | nc->maxparams=maxparams; | |
130 | nc->handler=handler; | |
12240b77 | 131 | nc->ext=NULL; |
c86edd1d | 132 | nc->next=NULL; |
38cee035 CP |
133 | if (help) { |
134 | int len=strlen(help); | |
135 | nc->help=(char *)malloc(len+1); | |
136 | if(nc->help) { | |
137 | strncpy(nc->help, help, len); | |
138 | nc->help[len] = '\0'; | |
139 | } | |
140 | } else { | |
141 | nc->help=NULL; | |
142 | } | |
143 | ||
c86edd1d Q |
144 | if ((c=findcommandintree(ct,cmdname,1))!=NULL) { |
145 | /* Found something already. Append our entry to the end */ | |
146 | while (c->next!=NULL) | |
147 | c=(Command *)c->next; | |
148 | c->next=(struct Command *)nc; | |
149 | } else if (insertcommand(nc,ct,0)) { | |
150 | /* Erk, that didn't work.. */ | |
151 | freesstring(nc->command); | |
38cee035 CP |
152 | if(nc->help) |
153 | free(nc->help); | |
c86edd1d Q |
154 | free(nc); |
155 | return NULL; | |
156 | } | |
157 | ||
158 | return nc; | |
159 | } | |
160 | ||
161 | /* | |
162 | * insertcommand: internal recursive function to do actual command insertion | |
163 | */ | |
164 | ||
165 | int insertcommand(Command *c, CommandTree *ct, int depth) { | |
166 | int nextcharindex=c->command->content[depth]-'A'; | |
167 | ||
168 | if ((c->command->length==depth) || (countcommandtree(ct)==0)) { | |
169 | if(ct->cmd!=NULL) { | |
170 | int oldcharindex; | |
171 | /* There is already another command at this level */ | |
172 | if(ct->final[0]=='\0') { | |
173 | /* It's a conflict with us, shouldn't happen */ | |
174 | return 1; | |
175 | } | |
176 | oldcharindex=ct->final[0]-'A'; | |
177 | if (ct->next[oldcharindex] != NULL) { | |
178 | /* Shouldn't happen */ | |
179 | Error("parser",ERR_ERROR,"Unexpected command subtree conflicts with final value"); | |
180 | } else { | |
181 | ct->next[oldcharindex]=(struct CommandTree *)newcommandtree(); | |
182 | } | |
183 | insertcommand(ct->cmd,(CommandTree *)ct->next[oldcharindex],depth+1); | |
184 | } | |
185 | ct->cmd=c; | |
bf459480 | 186 | /* Use a static NUL string rather than the allocated one if possible. */ |
e4c2572e | 187 | if (c->command->length > depth) |
bf459480 | 188 | ct->final=&(c->command->content[depth]); |
189 | else | |
190 | ct->final=""; | |
c86edd1d Q |
191 | return 0; |
192 | } else { | |
193 | if ((ct->cmd!=NULL) && (ct->final[0]!='\0')) { | |
194 | int oldcharindex=ct->final[0]-'A'; | |
195 | /* Someone marked this node as final, we have to undo that since we're now here too */ | |
196 | if (ct->next[oldcharindex] != NULL) { | |
197 | Error("parser",ERR_ERROR,"Unexpected command subtree conflicts with final value"); | |
198 | /* Shouldn't happen */ | |
199 | } else { | |
200 | ct->next[oldcharindex]=(struct CommandTree *)newcommandtree(); | |
201 | } | |
202 | insertcommand(ct->cmd,(CommandTree *)ct->next[oldcharindex],depth+1); | |
203 | ct->cmd=NULL; | |
204 | ct->final=""; | |
205 | } | |
206 | ||
207 | if (ct->next[nextcharindex]==NULL) { | |
208 | ct->next[nextcharindex]=(struct CommandTree *)newcommandtree(); | |
209 | } | |
210 | return insertcommand(c,(CommandTree *)ct->next[nextcharindex],depth+1); | |
211 | } | |
212 | } | |
213 | ||
214 | int deletecommandfromtree(CommandTree *ct, const char *cmdname, CommandHandler handler) { | |
215 | int i; | |
3b8df066 | 216 | char cmdbuf[MAX_COMMAND_LEN+1]; |
c86edd1d Q |
217 | sstring *tmpstr; |
218 | ||
3b8df066 | 219 | if (sanitisecommandname(cmdname, cmdbuf)) |
220 | return 1; | |
c86edd1d | 221 | |
3b8df066 | 222 | tmpstr=getsstring(cmdbuf, MAX_COMMAND_LEN); |
c86edd1d Q |
223 | i=deletecommand(tmpstr,ct,0,handler); |
224 | freesstring(tmpstr); | |
3b8df066 | 225 | |
c86edd1d Q |
226 | return i; |
227 | } | |
228 | ||
229 | int deletecommand(sstring *cmdname, CommandTree *ct, int depth, CommandHandler handler) { | |
230 | Command **ch, *c; | |
231 | int nextcharindex=(cmdname->content[depth])-'A'; | |
232 | int i; | |
233 | ||
234 | if (depth==cmdname->length) { | |
235 | /* Hit the end of the string.. the command should be in this node */ | |
236 | if ((ct->cmd==NULL) || | |
237 | (ct->cmd->command->length != cmdname->length) || | |
238 | (strncmp(ct->cmd->command->content,cmdname->content,cmdname->length))) { | |
239 | return 1; | |
240 | } | |
241 | for(ch=&(ct->cmd);*ch;ch=(Command **)&((*ch)->next)) { | |
242 | if ((*ch)->handler==handler) { | |
243 | c=*ch; | |
244 | (*ch)=(Command *)((*ch)->next); | |
245 | freesstring(c->command); | |
38cee035 CP |
246 | if(c->help) |
247 | free(c->help); | |
c86edd1d Q |
248 | free(c); |
249 | return 0; | |
250 | } | |
251 | } | |
252 | return 1; | |
253 | } else if ((ct->final) && (ct->final[0]==cmdname->content[depth])) { | |
254 | /* We have a potentially matching final string here, double check */ | |
255 | if ((ct->cmd->command->length != cmdname->length) || | |
256 | (strncmp(ct->cmd->command->content,cmdname->content,cmdname->length))) { | |
257 | return 1; | |
258 | } | |
259 | /* Find the command in the potential chain and remove it */ | |
260 | for(ch=&(ct->cmd);*ch;ch=(Command **)&((*ch)->next)) { | |
261 | if ((*ch)->handler==handler) { | |
262 | c=*ch; | |
263 | (*ch)=(Command *)((*ch)->next); | |
264 | freesstring(c->command); | |
38cee035 CP |
265 | if(c->help) |
266 | free(c->help); | |
c86edd1d | 267 | free(c); |
bf459480 | 268 | |
269 | /* We need to regenerate the final pointer if needed; | |
270 | * if ct->cmd is still pointing to a command it has the same name. | |
271 | * Otherwise we should clear it.*/ | |
272 | if (ct->cmd) | |
273 | ct->final=&(ct->cmd->command->content[depth]); | |
274 | else | |
275 | ct->final=""; | |
276 | ||
c86edd1d Q |
277 | return 0; |
278 | } | |
279 | } | |
280 | return 1; | |
281 | } else { | |
282 | /* We're going to have to recurse.. */ | |
283 | if (ct->next[nextcharindex]==NULL) { | |
284 | return 1; | |
285 | } else { | |
286 | i=deletecommand(cmdname,(CommandTree *)ct->next[nextcharindex],depth+1,handler); | |
287 | if (countcommandtree((CommandTree *)ct->next[nextcharindex])==0) { | |
288 | free(ct->next[nextcharindex]); | |
289 | ct->next[nextcharindex]=NULL; | |
290 | } | |
291 | return i; | |
292 | } | |
293 | } | |
294 | } | |
295 | ||
296 | /* | |
297 | * findcommandintree: Takes a command string and returns the relevant | |
298 | * structure, if found. | |
299 | */ | |
300 | ||
301 | Command *findcommandintree(CommandTree *ct, const char *command, int strictcheck) { | |
302 | Command *c; | |
303 | ||
304 | c=findcommand(ct,command,0); | |
305 | ||
306 | if (c==NULL) | |
307 | return NULL; | |
308 | ||
309 | if (strictcheck && | |
310 | (ircd_strncmp(command,c->command->content,c->command->length) || | |
311 | (c->command->length != strlen(command)))) | |
312 | return NULL; | |
313 | ||
314 | return c; | |
315 | } | |
316 | ||
317 | /* | |
318 | * findcommand: Internal recursive function to find a command | |
319 | */ | |
320 | ||
321 | Command *findcommand(CommandTree *ct, const char *command, int depth) { | |
322 | int nextchar=toupper(command[depth]); | |
323 | ||
324 | /* If we've run out of string, we return whatever we find in this node, | |
325 | * be it NULL or otherwise. */ | |
326 | if (nextchar=='\0') { | |
327 | return ct->cmd; | |
328 | } | |
329 | ||
330 | if ((ct->cmd!=NULL) && ct->final[0]==nextchar) { | |
331 | return ct->cmd; | |
332 | } | |
333 | ||
334 | if (nextchar<'A' || nextchar>'Z') { | |
335 | return NULL; | |
336 | } | |
337 | ||
338 | if (ct->next[nextchar-'A']==NULL) { | |
339 | return NULL; | |
340 | } else { | |
341 | return findcommand((CommandTree *)ct->next[nextchar-'A'], command, depth+1); | |
342 | } | |
343 | } | |
344 | ||
345 | /* | |
346 | * getcommandlist: Returns the contents of a CommandTree in a user-supplied Command * array | |
347 | */ | |
348 | ||
349 | int getcommandlist(CommandTree *ct, Command **commandlist, int maxcommands) { | |
350 | int i=0,count=0; | |
351 | ||
352 | if (maxcommands<0) { | |
353 | return 0; | |
354 | } | |
355 | ||
356 | if (ct->cmd) { | |
357 | commandlist[count++]=ct->cmd; | |
358 | } | |
359 | ||
360 | for (i=0;i<26;i++) { | |
361 | if(ct->next[i]) { | |
362 | count+=getcommandlist(ct->next[i], &commandlist[count], maxcommands-count); | |
363 | } | |
364 | } | |
365 | ||
366 | return count; | |
367 | } | |
0a659cde CP |
368 | |
369 | /* Returns the command name given a handler */ | |
370 | sstring *getcommandname(CommandTree *ct, CommandHandler handler) { | |
371 | int i; | |
372 | sstring *s; | |
373 | ||
374 | if(ct->cmd && ct->cmd->handler == handler) { | |
375 | return ct->cmd->command; | |
376 | } | |
377 | ||
378 | for (i=0;i<26;i++) { | |
379 | if(ct->next[i]) { | |
380 | s=getcommandname(ct->next[i], handler); | |
381 | if(s) | |
382 | return s; | |
383 | } | |
384 | } | |
385 | ||
386 | return NULL; | |
387 | } |