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