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