]> jfr.im git - irc/quakenet/newserv.git/blob - parser/parser.c
Merge
[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 /*
77 * addcommandhelptotree:
78 *
79 * This installs a specific command in a tree, with a help paramater.
80 *
81 * This function builds the Command structure in addition to
82 * installing it in the tree
83 */
84
85 Command *addcommandhelptotree(CommandTree *ct, const char *cmdname, int level, int maxparams, CommandHandler handler, const char *help) {
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;
95 nc->ext=NULL;
96 nc->next=NULL;
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
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);
114 if(nc->help)
115 free(nc->help);
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);
129 if(nc->help)
130 free(nc->help);
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;
163 /* Use a static NUL string rather than the allocated one if possible. */
164 if (c->command->length > depth)
165 ct->final=&(c->command->content[depth]);
166 else
167 ct->final="";
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);
228 if(c->help)
229 free(c->help);
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);
247 if(c->help)
248 free(c->help);
249 free(c);
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
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 }
350
351 /* Returns the command name given a handler */
352 sstring *getcommandname(CommandTree *ct, CommandHandler handler) {
353 int i;
354 sstring *s;
355
356 if(ct->cmd && ct->cmd->handler == handler) {
357 return ct->cmd->command;
358 }
359
360 for (i=0;i<26;i++) {
361 if(ct->next[i]) {
362 s=getcommandname(ct->next[i], handler);
363 if(s)
364 return s;
365 }
366 }
367
368 return NULL;
369 }