]> jfr.im git - irc/quakenet/snircd.git/blame - ircd/crule.c
Should be unsigned long for A
[irc/quakenet/snircd.git] / ircd / crule.c
CommitLineData
189935b1 1/**
2 * @file
3 * @brief Connection rule parser and checker
4 * @version $Id: crule.c,v 1.9 2005/03/20 16:06:17 entrope Exp $
5 *
6 * by Tony Vencill (Tonto on IRC) <vencill@bga.com>
7 *
8 * The majority of this file is a recursive descent parser used to convert
9 * connection rules into expression trees when the conf file is read.
10 * All parsing structures and types are hidden in the interest of good
11 * programming style and to make possible future data structure changes
12 * without affecting the interface between this patch and the rest of the
13 * server. The only functions accessible externally are crule_parse,
14 * crule_free, and crule_eval. Prototypes for these functions can be
15 * found in h.h.
16 *
17 * Please direct any connection rule or SmartRoute questions to Tonto on
18 * IRC or by email to vencill@bga.com.
19 *
20 * For parser testing, defining CR_DEBUG generates a stand-alone parser
21 * that takes rules from stdin and prints out memory allocation
22 * information and the parsed rule. This stand alone parser is ignorant
23 * of the irc server and thus cannot do rule evaluation. Do not define
24 * this flag when compiling the server! If you wish to generate the
25 * test parser, compile from the ircd directory with a line similar to
26 * cc -o parser -DCR_DEBUG crule.c
27 *
28 * The define CR_CHKCONF is provided to generate routines needed in
29 * chkconf. These consist of the parser, a different crule_parse that
30 * prints errors to stderr, and crule_free (just for good style and to
31 * more closely simulate the actual ircd environment). crule_eval and
32 * the rule functions are made empty functions as in the stand-alone
33 * test parser.
34 *
35 * The production rules for the grammar are as follows ("rule" is the
36 * starting production):
37 *
38 * rule:
39 * orexpr END END is end of input or :
40 * orexpr:
41 * andexpr
42 * andexpr || orexpr
43 * andexpr:
44 * primary
45 * primary && andexpr
46 * primary:
47 * function
48 * ! primary
49 * ( orexpr )
50 * function:
51 * word ( ) word is alphanumeric string, first character
52 * word ( arglist ) must be a letter
53 * arglist:
54 * word
55 * word , arglist
56 */
57#include "config.h"
58
59#include "crule.h"
60#ifndef CR_DEBUG
61
62/* ircd functions and types we need */
63#include "client.h"
64#include "ircd.h"
65#include "ircd_alloc.h"
66#include "ircd_chattr.h"
67#include "ircd_string.h"
68#include "match.h"
69#include "s_bsd.h"
70#include "s_debug.h"
71#include "struct.h"
72
73#include <stdio.h>
74#include <stdlib.h>
75
76#else /* includes and defines to make the stand-alone test parser */
77
78#include <stdio.h>
79#include <stdlib.h>
80
81#define BadPtr(x) (!(x) || (*(x) == '\0'))
82#define DupString(x,y) \
83 do { \
84 x = (char*) MyMalloc(strlen(y)+1); \
85 strcpy(x,y); \
86 } while(0)
87
88/* We don't care about collation discrepancies here, it seems.... */
89#define ircd_strcmp strcasecmp
90
91#endif
92
93#include <string.h>
94
95
96#if defined(CR_DEBUG) || defined(CR_CHKCONF)
97#undef MyMalloc
98#undef malloc
99#define MyMalloc malloc
100#undef MyFree
101#undef free
102#define MyFree free
103#endif
104
105/* some constants and shared data types */
106#define CR_MAXARGLEN 80 /**< Maximum arg length (must be > HOSTLEN) */
107#define CR_MAXARGS 3 /**< Maximum number of args for a rule */
108
109/*
110 * Some symbols for easy reading
111 */
112
113/** Input scanner tokens. */
114enum crule_token {
115 CR_UNKNOWN, /**< Unknown token type. */
116 CR_END, /**< End of input ('\\0' or ':'). */
117 CR_AND, /**< Logical and operator (&&). */
118 CR_OR, /**< Logical or operator (||). */
119 CR_NOT, /**< Logical not operator (!). */
120 CR_OPENPAREN, /**< Open parenthesis. */
121 CR_CLOSEPAREN, /**< Close parenthesis. */
122 CR_COMMA, /**< Comma. */
123 CR_WORD /**< Something that looks like a hostmask (alphanumerics, "*?.-"). */
124};
125
126/** Parser error codes. */
127enum crule_errcode {
128 CR_NOERR, /**< No error. */
129 CR_UNEXPCTTOK, /**< Invalid token given context. */
130 CR_UNKNWTOK, /**< Input did not form a valid token. */
131 CR_EXPCTAND, /**< Did not see expected && operator. */
132 CR_EXPCTOR, /**< Did not see expected || operator. */
133 CR_EXPCTPRIM, /**< Expected a primitive (parentheses, ! or word). */
134 CR_EXPCTOPEN, /**< Expected an open parenthesis after function name. */
135 CR_EXPCTCLOSE, /**< Expected a close parenthesis to match open parenthesis. */
136 CR_UNKNWFUNC, /**< Attempt to use an unknown function. */
137 CR_ARGMISMAT /**< Wrong number of arguments to function. */
138};
139
140/*
141 * Expression tree structure, function pointer, and tree pointer local!
142 */
143/** Evaluation function for a connection rule. */
144typedef int (*crule_funcptr) (int, void **);
145
146/** Node in a connection rule tree. */
147struct CRuleNode {
148 crule_funcptr funcptr; /**< Evaluation function for this node. */
149 int numargs; /**< Number of arguments. */
150 void *arg[CR_MAXARGS]; /**< Array of arguments. For operators, each arg
151 is a tree element; for functions, each arg is
152 a string. */
153};
154
155/** Typedef to save typing effort. */
156typedef struct CRuleNode* CRuleNodePtr;
157
158/* local rule function prototypes */
159static int crule_connected(int, void *[]);
160static int crule_directcon(int, void *[]);
161static int crule_via(int, void *[]);
162static int crule_directop(int, void *[]);
163static int crule__andor(int, void *[]);
164static int crule__not(int, void *[]);
165
166/* local parsing function prototypes */
167static int crule_gettoken(int* token, const char** str);
168static void crule_getword(char*, int*, size_t, const char**);
169static int crule_parseandexpr(CRuleNodePtr*, int *, const char**);
170static int crule_parseorexpr(CRuleNodePtr*, int *, const char**);
171static int crule_parseprimary(CRuleNodePtr*, int *, const char**);
172static int crule_parsefunction(CRuleNodePtr*, int *, const char**);
173static int crule_parsearglist(CRuleNodePtr, int *, const char**);
174
175#if defined(CR_DEBUG) || defined(CR_CHKCONF)
176/*
177 * Prototypes for the test parser; if not debugging,
178 * these are defined in h.h
179 */
180struct CRuleNode* crule_parse(const char*);
181void crule_free(struct CRuleNode**);
182#ifdef CR_DEBUG
183void print_tree(CRuleNodePtr);
184#endif
185#endif
186
187/** Error messages, indexed by the corresponding crule_errcode. */
188char *crule_errstr[] = {
189 "Unknown error", /* NOERR? - for completeness */
190 "Unexpected token", /* UNEXPCTTOK */
191 "Unknown token", /* UNKNWTOK */
192 "And expr expected", /* EXPCTAND */
193 "Or expr expected", /* EXPCTOR */
194 "Primary expected", /* EXPCTPRIM */
195 "( expected", /* EXPCTOPEN */
196 ") expected", /* EXPCTCLOSE */
197 "Unknown function", /* UNKNWFUNC */
198 "Argument mismatch" /* ARGMISMAT */
199};
200
201/** Connection rule function table entry. */
202struct crule_funclistent {
203 char name[15]; /**< Function name. */
204 int reqnumargs; /**< Required number of arguments. */
205 crule_funcptr funcptr; /**< Handler function. */
206};
207
208/** Defined connection rules. */
209struct crule_funclistent crule_funclist[] = {
210 /* maximum function name length is 14 chars */
211 {"connected", 1, crule_connected},
212 {"directcon", 1, crule_directcon},
213 {"via", 2, crule_via},
214 {"directop", 0, crule_directop},
215 {"", 0, NULL} /* this must be here to mark end of list */
216};
217
218/** Check whether any connected server matches crulearg[0].
219 * @param[in] numargs Number of valid args in \a crulearg.
220 * @param[in] crulearg Argument array.
221 * @return Non-zero if the condition is true, zero if not.
222 */
223static int crule_connected(int numargs, void *crulearg[])
224{
225#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
226 struct Client *acptr;
227
228 /* taken from m_links */
229 for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr))
230 {
231 if (!IsServer(acptr) && !IsMe(acptr))
232 continue;
233 if (match((char *)crulearg[0], cli_name(acptr)))
234 continue;
235 return (1);
236 }
237#endif
238 return (0);
239}
240
241/** Check whether any directly connected server matches crulearg[0].
242 * @param[in] numargs Number of valid args in \a crulearg.
243 * @param[in] crulearg Argument array.
244 * @return Non-zero if the condition is true, zero if not.
245 */
246static int crule_directcon(int numargs, void *crulearg[])
247{
248#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
249 int i;
250 struct Client *acptr;
251
252 /* adapted from m_trace and exit_one_client */
253 for (i = 0; i <= HighestFd; i++)
254 {
255 if (!(acptr = LocalClientArray[i]) || !IsServer(acptr))
256 continue;
257 if (match((char *)crulearg[0], cli_name(acptr)))
258 continue;
259 return (1);
260 }
261#endif
262 return (0);
263}
264
265/** Check whether a connected server matching crulearg[1] is
266 * connnected to me behind one matching crulearg[0].
267 * @param[in] numargs Number of valid args in \a crulearg.
268 * @param[in] crulearg Argument array.
269 * @return Non-zero if the condition is true, zero if not.
270 */
271static int crule_via(int numargs, void *crulearg[])
272{
273#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
274 struct Client *acptr;
275
276 /* adapted from m_links */
277 for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr))
278 {
279 if (!IsServer(acptr) && !IsMe(acptr))
280 continue;
281 if (match((char *)crulearg[1], cli_name(acptr)))
282 continue;
283 if (match((char *)crulearg[0], cli_name(cli_from(acptr))))
284 continue;
285 return (1);
286 }
287#endif
288 return (0);
289}
290
291/** Check whether we have a local IRC operator.
292 * @param[in] numargs Number of valid args in \a crulearg.
293 * @param[in] crulearg Argument array.
294 * @return Non-zero if the condition is true, zero if not.
295 */
296static int crule_directop(int numargs, void *crulearg[])
297{
298#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
299 int i;
300 struct Client *acptr;
301
302 /* adapted from m_trace */
303 for (i = 0; i <= HighestFd; i++)
304 {
305 if (!(acptr = LocalClientArray[i]) || !IsAnOper(acptr))
306 continue;
307 return (1);
308 }
309#endif
310 return (0);
311}
312
313/** Evaluate a connection rule.
314 * @param[in] rule Rule to evalute.
315 * @return Non-zero if the rule allows the connection, zero otherwise.
316 */
317int crule_eval(struct CRuleNode* rule)
318{
319 return (rule->funcptr(rule->numargs, rule->arg));
320}
321
322/** Perform an and-or-or test on crulearg[0] and crulearg[1].
323 * If crulearg[2] is non-NULL, it means do OR; if it is NULL, do AND.
324 * @param[in] numargs Number of valid args in \a crulearg.
325 * @param[in] crulearg Argument array.
326 * @return Non-zero if the condition is true, zero if not.
327 */
328static int crule__andor(int numargs, void *crulearg[])
329{
330 int result1;
331
332 result1 = crule_eval(crulearg[0]);
333 if (crulearg[2]) /* or */
334 return (result1 || crule_eval(crulearg[1]));
335 else
336 return (result1 && crule_eval(crulearg[1]));
337}
338
339/** Logically invert the result of crulearg[0].
340 * @param[in] numargs Number of valid args in \a crulearg.
341 * @param[in] crulearg Argument array.
342 * @return Non-zero if the condition is true, zero if not.
343 */
344static int crule__not(int numargs, void *crulearg[])
345{
346 return (!crule_eval(crulearg[0]));
347}
348
349/** Scan an input token from \a ruleptr.
350 * @param[out] next_tokp Receives type of next token.
351 * @param[in,out] ruleptr Next readable character from input.
352 * @return Either CR_UNKNWTOK if the input was unrecognizable, else CR_NOERR.
353 */
354static int crule_gettoken(int* next_tokp, const char** ruleptr)
355{
356 char pending = '\0';
357
358 *next_tokp = CR_UNKNOWN;
359 while (*next_tokp == CR_UNKNOWN)
360 switch (*(*ruleptr)++)
361 {
362 case ' ':
363 case '\t':
364 break;
365 case '&':
366 if (pending == '\0')
367 pending = '&';
368 else if (pending == '&')
369 *next_tokp = CR_AND;
370 else
371 return (CR_UNKNWTOK);
372 break;
373 case '|':
374 if (pending == '\0')
375 pending = '|';
376 else if (pending == '|')
377 *next_tokp = CR_OR;
378 else
379 return (CR_UNKNWTOK);
380 break;
381 case '!':
382 *next_tokp = CR_NOT;
383 break;
384 case '(':
385 *next_tokp = CR_OPENPAREN;
386 break;
387 case ')':
388 *next_tokp = CR_CLOSEPAREN;
389 break;
390 case ',':
391 *next_tokp = CR_COMMA;
392 break;
393 case '\0':
394 (*ruleptr)--;
395 *next_tokp = CR_END;
396 break;
397 case ':':
398 *next_tokp = CR_END;
399 break;
400 default:
401 if ((IsAlpha(*(--(*ruleptr)))) || (**ruleptr == '*') ||
402 (**ruleptr == '?') || (**ruleptr == '.') || (**ruleptr == '-'))
403 *next_tokp = CR_WORD;
404 else
405 return (CR_UNKNWTOK);
406 break;
407 }
408 return CR_NOERR;
409}
410
411/** Scan a word from \a ruleptr.
412 * @param[out] word Output buffer.
413 * @param[out] wordlenp Length of word written to \a word (not including terminating NUL).
414 * @param[in] maxlen Maximum number of bytes writable to \a word.
415 * @param[in,out] ruleptr Next readable character from input.
416 */
417static void crule_getword(char* word, int* wordlenp, size_t maxlen, const char** ruleptr)
418{
419 char *word_ptr;
420
421 word_ptr = word;
422 while ((size_t)(word_ptr - word) < maxlen
423 && (IsAlnum(**ruleptr)
424 || **ruleptr == '*' || **ruleptr == '?'
425 || **ruleptr == '.' || **ruleptr == '-'))
426 *word_ptr++ = *(*ruleptr)++;
427 *word_ptr = '\0';
428 *wordlenp = word_ptr - word;
429}
430
431/** Parse an entire rule.
432 * @param[in] rule Text form of rule.
433 * @return CRuleNode for rule, or NULL if there was a parse error.
434 */
435struct CRuleNode* crule_parse(const char *rule)
436{
437 const char* ruleptr = rule;
438 int next_tok;
439 struct CRuleNode* ruleroot = 0;
440 int errcode = CR_NOERR;
441
442 if ((errcode = crule_gettoken(&next_tok, &ruleptr)) == CR_NOERR) {
443 if ((errcode = crule_parseorexpr(&ruleroot, &next_tok, &ruleptr)) == CR_NOERR) {
444 if (ruleroot != NULL) {
445 if (next_tok == CR_END)
446 return (ruleroot);
447 else
448 errcode = CR_UNEXPCTTOK;
449 }
450 else
451 errcode = CR_EXPCTOR;
452 }
453 }
454 if (ruleroot != NULL)
455 crule_free(&ruleroot);
456#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
457 Debug((DEBUG_ERROR, "%s in rule: %s", crule_errstr[errcode], rule));
458#else
459 fprintf(stderr, "%s in rule: %s\n", crule_errstr[errcode], rule);
460#endif
461 return 0;
462}
463
464/** Parse an or expression.
465 * @param[out] orrootp Receives parsed node.
466 * @param[in,out] next_tokp Next input token type.
467 * @param[in,out] ruleptr Next input character.
468 * @return A crule_errcode value.
469 */
470static int crule_parseorexpr(CRuleNodePtr * orrootp, int *next_tokp, const char** ruleptr)
471{
472 int errcode = CR_NOERR;
473 CRuleNodePtr andexpr;
474 CRuleNodePtr orptr;
475
476 *orrootp = NULL;
477 while (errcode == CR_NOERR)
478 {
479 errcode = crule_parseandexpr(&andexpr, next_tokp, ruleptr);
480 if ((errcode == CR_NOERR) && (*next_tokp == CR_OR))
481 {
482 orptr = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
483#ifdef CR_DEBUG
484 fprintf(stderr, "allocating or element at %ld\n", orptr);
485#endif
486 orptr->funcptr = crule__andor;
487 orptr->numargs = 3;
488 orptr->arg[2] = (void *)1;
489 if (*orrootp != NULL)
490 {
491 (*orrootp)->arg[1] = andexpr;
492 orptr->arg[0] = *orrootp;
493 }
494 else
495 orptr->arg[0] = andexpr;
496 *orrootp = orptr;
497 }
498 else
499 {
500 if (*orrootp != NULL)
501 {
502 if (andexpr != NULL)
503 {
504 (*orrootp)->arg[1] = andexpr;
505 return (errcode);
506 }
507 else
508 {
509 (*orrootp)->arg[1] = NULL; /* so free doesn't seg fault */
510 return (CR_EXPCTAND);
511 }
512 }
513 else
514 {
515 *orrootp = andexpr;
516 return (errcode);
517 }
518 }
519 if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
520 return (errcode);
521 }
522 return (errcode);
523}
524
525/** Parse an and expression.
526 * @param[out] androotp Receives parsed node.
527 * @param[in,out] next_tokp Next input token type.
528 * @param[in,out] ruleptr Next input character.
529 * @return A crule_errcode value.
530 */
531static int crule_parseandexpr(CRuleNodePtr * androotp, int *next_tokp, const char** ruleptr)
532{
533 int errcode = CR_NOERR;
534 CRuleNodePtr primary;
535 CRuleNodePtr andptr;
536
537 *androotp = NULL;
538 while (errcode == CR_NOERR)
539 {
540 errcode = crule_parseprimary(&primary, next_tokp, ruleptr);
541 if ((errcode == CR_NOERR) && (*next_tokp == CR_AND))
542 {
543 andptr = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
544#ifdef CR_DEBUG
545 fprintf(stderr, "allocating and element at %ld\n", andptr);
546#endif
547 andptr->funcptr = crule__andor;
548 andptr->numargs = 3;
549 andptr->arg[2] = (void *)0;
550 if (*androotp != NULL)
551 {
552 (*androotp)->arg[1] = primary;
553 andptr->arg[0] = *androotp;
554 }
555 else
556 andptr->arg[0] = primary;
557 *androotp = andptr;
558 }
559 else
560 {
561 if (*androotp != NULL)
562 {
563 if (primary != NULL)
564 {
565 (*androotp)->arg[1] = primary;
566 return (errcode);
567 }
568 else
569 {
570 (*androotp)->arg[1] = NULL; /* so free doesn't seg fault */
571 return (CR_EXPCTPRIM);
572 }
573 }
574 else
575 {
576 *androotp = primary;
577 return (errcode);
578 }
579 }
580 if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
581 return (errcode);
582 }
583 return (errcode);
584}
585
586/** Parse a primary expression.
587 * @param[out] primrootp Receives parsed node.
588 * @param[in,out] next_tokp Next input token type.
589 * @param[in,out] ruleptr Next input character.
590 * @return A crule_errcode value.
591 */
592static int crule_parseprimary(CRuleNodePtr* primrootp, int *next_tokp, const char** ruleptr)
593{
594 CRuleNodePtr *insertionp;
595 int errcode = CR_NOERR;
596
597 *primrootp = NULL;
598 insertionp = primrootp;
599 while (errcode == CR_NOERR)
600 {
601 switch (*next_tokp)
602 {
603 case CR_OPENPAREN:
604 if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
605 break;
606 if ((errcode = crule_parseorexpr(insertionp, next_tokp, ruleptr)) != CR_NOERR)
607 break;
608 if (*insertionp == NULL)
609 {
610 errcode = CR_EXPCTAND;
611 break;
612 }
613 if (*next_tokp != CR_CLOSEPAREN)
614 {
615 errcode = CR_EXPCTCLOSE;
616 break;
617 }
618 errcode = crule_gettoken(next_tokp, ruleptr);
619 break;
620 case CR_NOT:
621 *insertionp = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
622#ifdef CR_DEBUG
623 fprintf(stderr, "allocating primary element at %ld\n", *insertionp);
624#endif
625 (*insertionp)->funcptr = crule__not;
626 (*insertionp)->numargs = 1;
627 (*insertionp)->arg[0] = NULL;
628 insertionp = (CRuleNodePtr *) & ((*insertionp)->arg[0]);
629 if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
630 break;
631 continue;
632 case CR_WORD:
633 errcode = crule_parsefunction(insertionp, next_tokp, ruleptr);
634 break;
635 default:
636 if (*primrootp == NULL)
637 errcode = CR_NOERR;
638 else
639 errcode = CR_EXPCTPRIM;
640 break;
641 }
642 return (errcode);
643 }
644 return (errcode);
645}
646
647/** Parse a function call.
648 * @param[out] funcrootp Receives parsed node.
649 * @param[in,out] next_tokp Next input token type.
650 * @param[in,out] ruleptr Next input character.
651 * @return A crule_errcode value.
652 */
653static int crule_parsefunction(CRuleNodePtr* funcrootp, int* next_tokp, const char** ruleptr)
654{
655 int errcode = CR_NOERR;
656 char funcname[CR_MAXARGLEN];
657 int namelen;
658 int funcnum;
659
660 *funcrootp = NULL;
661 crule_getword(funcname, &namelen, CR_MAXARGLEN - 1, ruleptr);
662 if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
663 return (errcode);
664 if (*next_tokp == CR_OPENPAREN)
665 {
666 for (funcnum = 0;; funcnum++)
667 {
668 if (0 == ircd_strcmp(crule_funclist[funcnum].name, funcname))
669 break;
670 if (crule_funclist[funcnum].name[0] == '\0')
671 return (CR_UNKNWFUNC);
672 }
673 if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
674 return (errcode);
675 *funcrootp = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
676#ifdef CR_DEBUG
677 fprintf(stderr, "allocating function element at %ld\n", *funcrootp);
678#endif
679 (*funcrootp)->funcptr = NULL; /* for freeing aborted trees */
680 if ((errcode =
681 crule_parsearglist(*funcrootp, next_tokp, ruleptr)) != CR_NOERR)
682 return (errcode);
683 if (*next_tokp != CR_CLOSEPAREN)
684 return (CR_EXPCTCLOSE);
685 if ((crule_funclist[funcnum].reqnumargs != (*funcrootp)->numargs) &&
686 (crule_funclist[funcnum].reqnumargs != -1))
687 return (CR_ARGMISMAT);
688 if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
689 return (errcode);
690 (*funcrootp)->funcptr = crule_funclist[funcnum].funcptr;
691 return (CR_NOERR);
692 }
693 else
694 return (CR_EXPCTOPEN);
695}
696
697/** Parse the argument list to a CRuleNode.
698 * @param[in,out] argrootp Node whos argument list is being populated.
699 * @param[in,out] next_tokp Next input token type.
700 * @param[in,out] ruleptr Next input character.
701 * @return A crule_errcode value.
702 */
703static int crule_parsearglist(CRuleNodePtr argrootp, int *next_tokp, const char** ruleptr)
704{
705 int errcode = CR_NOERR;
706 char *argelemp = NULL;
707 char currarg[CR_MAXARGLEN];
708 int arglen = 0;
709 char word[CR_MAXARGLEN];
710 int wordlen = 0;
711
712 argrootp->numargs = 0;
713 currarg[0] = '\0';
714 while (errcode == CR_NOERR)
715 {
716 switch (*next_tokp)
717 {
718 case CR_WORD:
719 crule_getword(word, &wordlen, CR_MAXARGLEN - 1, ruleptr);
720 if (currarg[0] != '\0')
721 {
722 if ((arglen + wordlen) < (CR_MAXARGLEN - 1))
723 {
724 strcat(currarg, " ");
725 strcat(currarg, word);
726 arglen += wordlen + 1;
727 }
728 }
729 else
730 {
731 strcpy(currarg, word);
732 arglen = wordlen;
733 }
734 errcode = crule_gettoken(next_tokp, ruleptr);
735 break;
736 default:
737#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
738 collapse(currarg);
739#endif
740 if (!BadPtr(currarg))
741 {
742 DupString(argelemp, currarg);
743 argrootp->arg[argrootp->numargs++] = (void *)argelemp;
744 }
745 if (*next_tokp != CR_COMMA)
746 return (CR_NOERR);
747 currarg[0] = '\0';
748 errcode = crule_gettoken(next_tokp, ruleptr);
749 break;
750 }
751 }
752 return (errcode);
753}
754
755/*
756 * This function is recursive.. I wish I knew a nonrecursive way but
757 * I don't. Anyway, recursion is fun.. :)
758 * DO NOT CALL THIS FUNCTION WITH A POINTER TO A NULL POINTER
759 * (i.e.: If *elem is NULL, you're doing it wrong - seg fault)
760 */
761/** Free a connection rule and all its children.
762 * @param[in,out] elem Pointer to pointer to element to free. MUST NOT BE NULL.
763 */
764void crule_free(struct CRuleNode** elem)
765{
766 int arg, numargs;
767
768 if ((*(elem))->funcptr == crule__not)
769 {
770 /* type conversions and ()'s are fun! ;) here have an aspirin.. */
771 if ((*(elem))->arg[0] != NULL)
772 crule_free((struct CRuleNode**) &((*(elem))->arg[0]));
773 }
774 else if ((*(elem))->funcptr == crule__andor)
775 {
776 crule_free((struct CRuleNode**) &((*(elem))->arg[0]));
777 if ((*(elem))->arg[1] != NULL)
778 crule_free((struct CRuleNode**) &((*(elem))->arg[1]));
779 }
780 else
781 {
782 numargs = (*(elem))->numargs;
783 for (arg = 0; arg < numargs; arg++)
784 MyFree((*(elem))->arg[arg]);
785 }
786#ifdef CR_DEBUG
787 fprintf(stderr, "freeing element at %ld\n", *elem);
788#endif
789 MyFree(*elem);
790 *elem = 0;
791}
792
793#ifdef CR_DEBUG
794/** Display a connection rule as text.
795 * @param[in] printelem Connection rule to display.
796 */
797static void print_tree(CRuleNodePtr printelem)
798{
799 int funcnum, arg;
800
801 if (printelem->funcptr == crule__not)
802 {
803 printf("!( ");
804 print_tree((CRuleNodePtr) printelem->arg[0]);
805 printf(") ");
806 }
807 else if (printelem->funcptr == crule__andor)
808 {
809 printf("( ");
810 print_tree((CRuleNodePtr) printelem->arg[0]);
811 if (printelem->arg[2])
812 printf("|| ");
813 else
814 printf("&& ");
815 print_tree((CRuleNodePtr) printelem->arg[1]);
816 printf(") ");
817 }
818 else
819 {
820 for (funcnum = 0;; funcnum++)
821 {
822 if (printelem->funcptr == crule_funclist[funcnum].funcptr)
823 break;
824 if (crule_funclist[funcnum].funcptr == NULL)
825 MyCoreDump;
826 }
827 printf("%s(", crule_funclist[funcnum].name);
828 for (arg = 0; arg < printelem->numargs; arg++)
829 {
830 if (arg != 0)
831 printf(",");
832 printf("%s", (char *)printelem->arg[arg]);
833 }
834 printf(") ");
835 }
836}
837
838#endif
839
840#ifdef CR_DEBUG
841/** Read connection rules from stdin and display parsed forms as text.
842 * @return Zero.
843 */
844int main(void)
845{
846 char indata[256];
847 CRuleNode* rule;
848
849 printf("rule: ");
850 while (fgets(indata, 256, stdin) != NULL)
851 {
852 indata[strlen(indata) - 1] = '\0'; /* lose the newline */
853 if ((rule = crule_parse(indata)) != NULL)
854 {
855 printf("equivalent rule: ");
856 print_tree((CRuleNodePtr) rule);
857 printf("\n");
858 crule_free(&rule);
859 }
860 printf("\nrule: ");
861 }
862 printf("\n");
863
864 return 0;
865}
866
867#endif