]>
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); | |
6ebc9d2f P |
52 | if(ct->cmd->ext && ct->cmd->destroyext) |
53 | (ct->cmd->destroyext)(ct->cmd->ext); | |
429f366c P |
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 | ||
6ebc9d2f | 118 | Command *addcommandexttotree(CommandTree *ct, const char *cmdname, int level, int maxparams, CommandHandler handler, void *ext) { |
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; |
09285ac1 | 133 | nc->calls=0; |
6ebc9d2f | 134 | nc->destroyext=NULL; |
38cee035 | 135 | |
c86edd1d Q |
136 | if ((c=findcommandintree(ct,cmdname,1))!=NULL) { |
137 | /* Found something already. Append our entry to the end */ | |
138 | while (c->next!=NULL) | |
139 | c=(Command *)c->next; | |
140 | c->next=(struct Command *)nc; | |
141 | } else if (insertcommand(nc,ct,0)) { | |
142 | /* Erk, that didn't work.. */ | |
143 | freesstring(nc->command); | |
144 | free(nc); | |
145 | return NULL; | |
146 | } | |
6ebc9d2f P |
147 | |
148 | if (ext) | |
149 | nc->ext=(void *)ext; | |
c86edd1d Q |
150 | |
151 | return nc; | |
152 | } | |
153 | ||
154 | /* | |
155 | * insertcommand: internal recursive function to do actual command insertion | |
156 | */ | |
157 | ||
158 | int insertcommand(Command *c, CommandTree *ct, int depth) { | |
159 | int nextcharindex=c->command->content[depth]-'A'; | |
160 | ||
161 | if ((c->command->length==depth) || (countcommandtree(ct)==0)) { | |
162 | if(ct->cmd!=NULL) { | |
163 | int oldcharindex; | |
164 | /* There is already another command at this level */ | |
165 | if(ct->final[0]=='\0') { | |
166 | /* It's a conflict with us, shouldn't happen */ | |
167 | return 1; | |
168 | } | |
169 | oldcharindex=ct->final[0]-'A'; | |
170 | if (ct->next[oldcharindex] != NULL) { | |
171 | /* Shouldn't happen */ | |
172 | Error("parser",ERR_ERROR,"Unexpected command subtree conflicts with final value"); | |
173 | } else { | |
174 | ct->next[oldcharindex]=(struct CommandTree *)newcommandtree(); | |
175 | } | |
176 | insertcommand(ct->cmd,(CommandTree *)ct->next[oldcharindex],depth+1); | |
177 | } | |
178 | ct->cmd=c; | |
bf459480 | 179 | /* Use a static NUL string rather than the allocated one if possible. */ |
e4c2572e | 180 | if (c->command->length > depth) |
bf459480 | 181 | ct->final=&(c->command->content[depth]); |
182 | else | |
183 | ct->final=""; | |
c86edd1d Q |
184 | return 0; |
185 | } else { | |
186 | if ((ct->cmd!=NULL) && (ct->final[0]!='\0')) { | |
187 | int oldcharindex=ct->final[0]-'A'; | |
188 | /* Someone marked this node as final, we have to undo that since we're now here too */ | |
189 | if (ct->next[oldcharindex] != NULL) { | |
190 | Error("parser",ERR_ERROR,"Unexpected command subtree conflicts with final value"); | |
191 | /* Shouldn't happen */ | |
192 | } else { | |
193 | ct->next[oldcharindex]=(struct CommandTree *)newcommandtree(); | |
194 | } | |
195 | insertcommand(ct->cmd,(CommandTree *)ct->next[oldcharindex],depth+1); | |
196 | ct->cmd=NULL; | |
197 | ct->final=""; | |
198 | } | |
199 | ||
200 | if (ct->next[nextcharindex]==NULL) { | |
201 | ct->next[nextcharindex]=(struct CommandTree *)newcommandtree(); | |
202 | } | |
203 | return insertcommand(c,(CommandTree *)ct->next[nextcharindex],depth+1); | |
204 | } | |
205 | } | |
206 | ||
207 | int deletecommandfromtree(CommandTree *ct, const char *cmdname, CommandHandler handler) { | |
208 | int i; | |
3b8df066 | 209 | char cmdbuf[MAX_COMMAND_LEN+1]; |
c86edd1d Q |
210 | sstring *tmpstr; |
211 | ||
3b8df066 | 212 | if (sanitisecommandname(cmdname, cmdbuf)) |
213 | return 1; | |
c86edd1d | 214 | |
3b8df066 | 215 | tmpstr=getsstring(cmdbuf, MAX_COMMAND_LEN); |
c86edd1d Q |
216 | i=deletecommand(tmpstr,ct,0,handler); |
217 | freesstring(tmpstr); | |
3b8df066 | 218 | |
c86edd1d Q |
219 | return i; |
220 | } | |
221 | ||
222 | int deletecommand(sstring *cmdname, CommandTree *ct, int depth, CommandHandler handler) { | |
223 | Command **ch, *c; | |
224 | int nextcharindex=(cmdname->content[depth])-'A'; | |
225 | int i; | |
226 | ||
227 | if (depth==cmdname->length) { | |
228 | /* Hit the end of the string.. the command should be in this node */ | |
229 | if ((ct->cmd==NULL) || | |
230 | (ct->cmd->command->length != cmdname->length) || | |
231 | (strncmp(ct->cmd->command->content,cmdname->content,cmdname->length))) { | |
232 | return 1; | |
233 | } | |
234 | for(ch=&(ct->cmd);*ch;ch=(Command **)&((*ch)->next)) { | |
235 | if ((*ch)->handler==handler) { | |
236 | c=*ch; | |
237 | (*ch)=(Command *)((*ch)->next); | |
238 | freesstring(c->command); | |
6ebc9d2f P |
239 | if(c->ext && c->destroyext) |
240 | (c->destroyext)(c->ext); | |
c86edd1d Q |
241 | free(c); |
242 | return 0; | |
243 | } | |
244 | } | |
245 | return 1; | |
246 | } else if ((ct->final) && (ct->final[0]==cmdname->content[depth])) { | |
247 | /* We have a potentially matching final string here, double check */ | |
248 | if ((ct->cmd->command->length != cmdname->length) || | |
249 | (strncmp(ct->cmd->command->content,cmdname->content,cmdname->length))) { | |
250 | return 1; | |
251 | } | |
252 | /* Find the command in the potential chain and remove it */ | |
253 | for(ch=&(ct->cmd);*ch;ch=(Command **)&((*ch)->next)) { | |
254 | if ((*ch)->handler==handler) { | |
255 | c=*ch; | |
256 | (*ch)=(Command *)((*ch)->next); | |
257 | freesstring(c->command); | |
6ebc9d2f P |
258 | if(c->ext && c->destroyext) |
259 | (c->destroyext)(c->ext); | |
c86edd1d | 260 | free(c); |
bf459480 | 261 | |
262 | /* We need to regenerate the final pointer if needed; | |
263 | * if ct->cmd is still pointing to a command it has the same name. | |
264 | * Otherwise we should clear it.*/ | |
265 | if (ct->cmd) | |
266 | ct->final=&(ct->cmd->command->content[depth]); | |
267 | else | |
268 | ct->final=""; | |
269 | ||
c86edd1d Q |
270 | return 0; |
271 | } | |
272 | } | |
273 | return 1; | |
274 | } else { | |
275 | /* We're going to have to recurse.. */ | |
276 | if (ct->next[nextcharindex]==NULL) { | |
277 | return 1; | |
278 | } else { | |
279 | i=deletecommand(cmdname,(CommandTree *)ct->next[nextcharindex],depth+1,handler); | |
280 | if (countcommandtree((CommandTree *)ct->next[nextcharindex])==0) { | |
281 | free(ct->next[nextcharindex]); | |
282 | ct->next[nextcharindex]=NULL; | |
283 | } | |
284 | return i; | |
285 | } | |
286 | } | |
287 | } | |
288 | ||
289 | /* | |
290 | * findcommandintree: Takes a command string and returns the relevant | |
291 | * structure, if found. | |
292 | */ | |
293 | ||
294 | Command *findcommandintree(CommandTree *ct, const char *command, int strictcheck) { | |
295 | Command *c; | |
296 | ||
297 | c=findcommand(ct,command,0); | |
298 | ||
299 | if (c==NULL) | |
300 | return NULL; | |
301 | ||
302 | if (strictcheck && | |
303 | (ircd_strncmp(command,c->command->content,c->command->length) || | |
304 | (c->command->length != strlen(command)))) | |
305 | return NULL; | |
306 | ||
307 | return c; | |
308 | } | |
309 | ||
310 | /* | |
311 | * findcommand: Internal recursive function to find a command | |
312 | */ | |
313 | ||
314 | Command *findcommand(CommandTree *ct, const char *command, int depth) { | |
315 | int nextchar=toupper(command[depth]); | |
316 | ||
317 | /* If we've run out of string, we return whatever we find in this node, | |
318 | * be it NULL or otherwise. */ | |
319 | if (nextchar=='\0') { | |
320 | return ct->cmd; | |
321 | } | |
322 | ||
323 | if ((ct->cmd!=NULL) && ct->final[0]==nextchar) { | |
324 | return ct->cmd; | |
325 | } | |
326 | ||
327 | if (nextchar<'A' || nextchar>'Z') { | |
328 | return NULL; | |
329 | } | |
330 | ||
331 | if (ct->next[nextchar-'A']==NULL) { | |
332 | return NULL; | |
333 | } else { | |
334 | return findcommand((CommandTree *)ct->next[nextchar-'A'], command, depth+1); | |
335 | } | |
336 | } | |
337 | ||
338 | /* | |
339 | * getcommandlist: Returns the contents of a CommandTree in a user-supplied Command * array | |
340 | */ | |
341 | ||
342 | int getcommandlist(CommandTree *ct, Command **commandlist, int maxcommands) { | |
343 | int i=0,count=0; | |
344 | ||
345 | if (maxcommands<0) { | |
346 | return 0; | |
347 | } | |
348 | ||
349 | if (ct->cmd) { | |
350 | commandlist[count++]=ct->cmd; | |
351 | } | |
352 | ||
353 | for (i=0;i<26;i++) { | |
354 | if(ct->next[i]) { | |
355 | count+=getcommandlist(ct->next[i], &commandlist[count], maxcommands-count); | |
356 | } | |
357 | } | |
358 | ||
359 | return count; | |
360 | } | |
0a659cde CP |
361 | |
362 | /* Returns the command name given a handler */ | |
363 | sstring *getcommandname(CommandTree *ct, CommandHandler handler) { | |
364 | int i; | |
365 | sstring *s; | |
366 | ||
367 | if(ct->cmd && ct->cmd->handler == handler) { | |
368 | return ct->cmd->command; | |
369 | } | |
370 | ||
371 | for (i=0;i<26;i++) { | |
372 | if(ct->next[i]) { | |
373 | s=getcommandname(ct->next[i], handler); | |
374 | if(s) | |
375 | return s; | |
376 | } | |
377 | } | |
378 | ||
379 | return NULL; | |
380 | } |