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