ircu2.10.12 pk910 fork
[ircu2.10.12-pk.git] / ircd / crule.c
1 /**
2  * @file
3  * @brief Connection rule parser and checker
4  * @version $Id: crule.c 1334 2005-03-20 16:06:30Z entrope $
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. */
114 enum 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. */
127 enum 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. */
144 typedef int (*crule_funcptr) (int, void **);
145
146 /** Node in a connection rule tree. */
147 struct 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. */
156 typedef struct CRuleNode* CRuleNodePtr;
157
158 /* local rule function prototypes */
159 static int crule_connected(int, void *[]);
160 static int crule_directcon(int, void *[]);
161 static int crule_via(int, void *[]);
162 static int crule_directop(int, void *[]);
163 static int crule__andor(int, void *[]);
164 static int crule__not(int, void *[]);
165
166 /* local parsing function prototypes */
167 static int crule_gettoken(int* token, const char** str);
168 static void crule_getword(char*, int*, size_t, const char**);
169 static int crule_parseandexpr(CRuleNodePtr*, int *, const char**);
170 static int crule_parseorexpr(CRuleNodePtr*, int *, const char**);
171 static int crule_parseprimary(CRuleNodePtr*, int *, const char**);
172 static int crule_parsefunction(CRuleNodePtr*, int *, const char**);
173 static 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  */
180 struct CRuleNode* crule_parse(const char*);
181 void crule_free(struct CRuleNode**);
182 #ifdef CR_DEBUG
183 void print_tree(CRuleNodePtr);
184 #endif
185 #endif
186
187 /** Error messages, indexed by the corresponding crule_errcode. */
188 char *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. */
202 struct 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. */
209 struct 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  */
223 static 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  */
246 static 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  */
271 static 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  */
296 static 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  */
317 int 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  */
328 static 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  */
344 static 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  */
354 static 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  */
417 static 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  */
435 struct 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  */
470 static 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  */
531 static 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  */
592 static 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  */
653 static 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  */
703 static 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  */
764 void 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  */
797 static 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  */
844 int 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