]> jfr.im git - irc/quakenet/newserv.git/blob - parser/parser.c
Damned constants.
[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 * addcommandtotree:
78 *
79 * This installs a specific command in a tree.
80 *
81 * This function builds the Command structure in addition to
82 * installing it in the tree
83 */
84
85 Command *addcommandtotree(CommandTree *ct, const char *cmdname, int level, int maxparams, CommandHandler handler) {
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
98 /* Sanity check the string */
99 for (i=0;i<nc->command->length;i++) {
100 nc->command->content[i]=toupper(nc->command->content[i]);
101 if (nc->command->content[i]<'A' || nc->command->content[i]>'Z') {
102 /* Someone tried to register an invalid command name */
103 freesstring(nc->command);
104 free(nc);
105 return NULL;
106 }
107 }
108
109 if ((c=findcommandintree(ct,cmdname,1))!=NULL) {
110 /* Found something already. Append our entry to the end */
111 while (c->next!=NULL)
112 c=(Command *)c->next;
113 c->next=(struct Command *)nc;
114 } else if (insertcommand(nc,ct,0)) {
115 /* Erk, that didn't work.. */
116 freesstring(nc->command);
117 free(nc);
118 return NULL;
119 }
120
121 return nc;
122 }
123
124 /*
125 * insertcommand: internal recursive function to do actual command insertion
126 */
127
128 int insertcommand(Command *c, CommandTree *ct, int depth) {
129 int nextcharindex=c->command->content[depth]-'A';
130
131 if ((c->command->length==depth) || (countcommandtree(ct)==0)) {
132 if(ct->cmd!=NULL) {
133 int oldcharindex;
134 /* There is already another command at this level */
135 if(ct->final[0]=='\0') {
136 /* It's a conflict with us, shouldn't happen */
137 return 1;
138 }
139 oldcharindex=ct->final[0]-'A';
140 if (ct->next[oldcharindex] != NULL) {
141 /* Shouldn't happen */
142 Error("parser",ERR_ERROR,"Unexpected command subtree conflicts with final value");
143 } else {
144 ct->next[oldcharindex]=(struct CommandTree *)newcommandtree();
145 }
146 insertcommand(ct->cmd,(CommandTree *)ct->next[oldcharindex],depth+1);
147 }
148 ct->cmd=c;
149 ct->final=&(c->command->content[depth]);
150 return 0;
151 } else {
152 if ((ct->cmd!=NULL) && (ct->final[0]!='\0')) {
153 int oldcharindex=ct->final[0]-'A';
154 /* Someone marked this node as final, we have to undo that since we're now here too */
155 if (ct->next[oldcharindex] != NULL) {
156 Error("parser",ERR_ERROR,"Unexpected command subtree conflicts with final value");
157 /* Shouldn't happen */
158 } else {
159 ct->next[oldcharindex]=(struct CommandTree *)newcommandtree();
160 }
161 insertcommand(ct->cmd,(CommandTree *)ct->next[oldcharindex],depth+1);
162 ct->cmd=NULL;
163 ct->final="";
164 }
165
166 if (ct->next[nextcharindex]==NULL) {
167 ct->next[nextcharindex]=(struct CommandTree *)newcommandtree();
168 }
169 return insertcommand(c,(CommandTree *)ct->next[nextcharindex],depth+1);
170 }
171 }
172
173 int deletecommandfromtree(CommandTree *ct, const char *cmdname, CommandHandler handler) {
174 int i;
175 sstring *tmpstr;
176
177 tmpstr=getsstring(cmdname,MAX_COMMAND_LEN);
178
179 /* Sanity check input string */
180 for (i=0;i<tmpstr->length;i++) {
181 tmpstr->content[i]=toupper(tmpstr->content[i]);
182 if (tmpstr->content[i]<'A' || tmpstr->content[i]>'Z') {
183 /* Someone tried to delete an invalid command name */
184 freesstring(tmpstr);
185 return 1;
186 }
187 }
188 i=deletecommand(tmpstr,ct,0,handler);
189 freesstring(tmpstr);
190 return i;
191 }
192
193 int deletecommand(sstring *cmdname, CommandTree *ct, int depth, CommandHandler handler) {
194 Command **ch, *c;
195 int nextcharindex=(cmdname->content[depth])-'A';
196 int i;
197
198 if (depth==cmdname->length) {
199 /* Hit the end of the string.. the command should be in this node */
200 if ((ct->cmd==NULL) ||
201 (ct->cmd->command->length != cmdname->length) ||
202 (strncmp(ct->cmd->command->content,cmdname->content,cmdname->length))) {
203 return 1;
204 }
205 for(ch=&(ct->cmd);*ch;ch=(Command **)&((*ch)->next)) {
206 if ((*ch)->handler==handler) {
207 c=*ch;
208 (*ch)=(Command *)((*ch)->next);
209 freesstring(c->command);
210 free(c);
211 return 0;
212 }
213 }
214 return 1;
215 } else if ((ct->final) && (ct->final[0]==cmdname->content[depth])) {
216 /* We have a potentially matching final string here, double check */
217 if ((ct->cmd->command->length != cmdname->length) ||
218 (strncmp(ct->cmd->command->content,cmdname->content,cmdname->length))) {
219 return 1;
220 }
221 /* Find the command in the potential chain and remove it */
222 for(ch=&(ct->cmd);*ch;ch=(Command **)&((*ch)->next)) {
223 if ((*ch)->handler==handler) {
224 c=*ch;
225 (*ch)=(Command *)((*ch)->next);
226 freesstring(c->command);
227 free(c);
228 return 0;
229 }
230 }
231 return 1;
232 } else {
233 /* We're going to have to recurse.. */
234 if (ct->next[nextcharindex]==NULL) {
235 return 1;
236 } else {
237 i=deletecommand(cmdname,(CommandTree *)ct->next[nextcharindex],depth+1,handler);
238 if (countcommandtree((CommandTree *)ct->next[nextcharindex])==0) {
239 free(ct->next[nextcharindex]);
240 ct->next[nextcharindex]=NULL;
241 }
242 return i;
243 }
244 }
245 }
246
247 /*
248 * findcommandintree: Takes a command string and returns the relevant
249 * structure, if found.
250 */
251
252 Command *findcommandintree(CommandTree *ct, const char *command, int strictcheck) {
253 Command *c;
254
255 c=findcommand(ct,command,0);
256
257 if (c==NULL)
258 return NULL;
259
260 if (strictcheck &&
261 (ircd_strncmp(command,c->command->content,c->command->length) ||
262 (c->command->length != strlen(command))))
263 return NULL;
264
265 return c;
266 }
267
268 /*
269 * findcommand: Internal recursive function to find a command
270 */
271
272 Command *findcommand(CommandTree *ct, const char *command, int depth) {
273 int nextchar=toupper(command[depth]);
274
275 /* If we've run out of string, we return whatever we find in this node,
276 * be it NULL or otherwise. */
277 if (nextchar=='\0') {
278 return ct->cmd;
279 }
280
281 if ((ct->cmd!=NULL) && ct->final[0]==nextchar) {
282 return ct->cmd;
283 }
284
285 if (nextchar<'A' || nextchar>'Z') {
286 return NULL;
287 }
288
289 if (ct->next[nextchar-'A']==NULL) {
290 return NULL;
291 } else {
292 return findcommand((CommandTree *)ct->next[nextchar-'A'], command, depth+1);
293 }
294 }
295
296 /*
297 * getcommandlist: Returns the contents of a CommandTree in a user-supplied Command * array
298 */
299
300 int getcommandlist(CommandTree *ct, Command **commandlist, int maxcommands) {
301 int i=0,count=0;
302
303 if (maxcommands<0) {
304 return 0;
305 }
306
307 if (ct->cmd) {
308 commandlist[count++]=ct->cmd;
309 }
310
311 for (i=0;i<26;i++) {
312 if(ct->next[i]) {
313 count+=getcommandlist(ct->next[i], &commandlist[count], maxcommands-count);
314 }
315 }
316
317 return count;
318 }