]> jfr.im git - irc/quakenet/newserv.git/blame - parser/parser.c
PARSER: Added "calls" field to allow tracking how many times each command is
[irc/quakenet/newserv.git] / parser / parser.c
CommitLineData
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 */
14int insertcommand(Command *c, CommandTree *ct, int depth);
15int deletecommand(sstring *cmdname, CommandTree *ct, int depth, CommandHandler handler);
16Command *findcommand(CommandTree *ct, const char *command, int depth);
17int 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
26CommandTree *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
40void 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
66int 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 89static 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 118Command *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
158int 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
207int 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
222int 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
294Command *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
314Command *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
342int 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 */
363sstring *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}