]> jfr.im git - irc/quakenet/newserv.git/blob - parser/parser.c
fix sstring leak
[irc/quakenet/newserv.git] / parser / parser.c
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->help)
53 free(ct->cmd->help);
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 *addcommandhelptotree(CommandTree *ct, const char *cmdname, int level, int maxparams, CommandHandler handler, const char *help) {
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 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
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);
152 if(nc->help)
153 free(nc->help);
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;
186 /* Use a static NUL string rather than the allocated one if possible. */
187 if (c->command->length > depth)
188 ct->final=&(c->command->content[depth]);
189 else
190 ct->final="";
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;
216 char cmdbuf[MAX_COMMAND_LEN+1];
217 sstring *tmpstr;
218
219 if (sanitisecommandname(cmdname, cmdbuf))
220 return 1;
221
222 tmpstr=getsstring(cmdbuf, MAX_COMMAND_LEN);
223 i=deletecommand(tmpstr,ct,0,handler);
224 freesstring(tmpstr);
225
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);
246 if(c->help)
247 free(c->help);
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);
265 if(c->help)
266 free(c->help);
267 free(c);
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
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 }
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 }