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