Doxyfy crule.c.
[ircu2.10.12-pk.git] / ircd / crule.c
1 /**
2  * @file
3  * @brief Connection rule parser and checker
4  * @version $Id$
5  *
6  * by Tony Vencill (Tonto on IRC) <vencill@bga.com>
7  *
8  * The majority of this file is a recusive 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 discrepacies 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],
284               cli_name(LocalClientArray[cli_fd(cli_from(acptr))])))
285       continue;
286     return (1);
287   }
288 #endif
289   return (0);
290 }
291
292 /** Check whether we have a local IRC operator.
293  * @param[in] numargs Number of valid args in \a crulearg.
294  * @param[in] crulearg Argument array.
295  * @return Non-zero if the condition is true, zero if not.
296  */
297 static int crule_directop(int numargs, void *crulearg[])
298 {
299 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
300   int i;
301   struct Client *acptr;
302
303   /* adapted from m_trace */
304   for (i = 0; i <= HighestFd; i++)
305   {
306     if (!(acptr = LocalClientArray[i]) || !IsAnOper(acptr))
307       continue;
308     return (1);
309   }
310 #endif
311   return (0);
312 }
313
314 /** Evaluate a connection rule.
315  * @param[in] rule Rule to evalute.
316  * @return Non-zero if the rule allows the connection, zero otherwise.
317  */
318 int crule_eval(struct CRuleNode* rule)
319 {
320   return (rule->funcptr(rule->numargs, rule->arg));
321 }
322
323 /** Perform an and-or-or test on crulearg[0] and crulearg[1].
324  * If crulearg[2] is non-NULL, it means do OR; if it is NULL, do AND.
325  * @param[in] numargs Number of valid args in \a crulearg.
326  * @param[in] crulearg Argument array.
327  * @return Non-zero if the condition is true, zero if not.
328  */
329 static int crule__andor(int numargs, void *crulearg[])
330 {
331   int result1;
332
333   result1 = crule_eval(crulearg[0]);
334   if (crulearg[2])              /* or */
335     return (result1 || crule_eval(crulearg[1]));
336   else
337     return (result1 && crule_eval(crulearg[1]));
338 }
339
340 /** Logically invert the result of crulearg[0].
341  * @param[in] numargs Number of valid args in \a crulearg.
342  * @param[in] crulearg Argument array.
343  * @return Non-zero if the condition is true, zero if not.
344  */
345 static int crule__not(int numargs, void *crulearg[])
346 {
347   return (!crule_eval(crulearg[0]));
348 }
349
350 /** Scan an input token from \a ruleptr.
351  * @param[out] next_tokp Receives type of next token.
352  * @param[in,out] ruleptr Next readable character from input.
353  * @return Either CR_UNKNWTOK if the input was unrecognizable, else CR_NOERR.
354  */
355 static int crule_gettoken(int* next_tokp, const char** ruleptr)
356 {
357   char pending = '\0';
358
359   *next_tokp = CR_UNKNOWN;
360   while (*next_tokp == CR_UNKNOWN)
361     switch (*(*ruleptr)++)
362     {
363       case ' ':
364       case '\t':
365         break;
366       case '&':
367         if (pending == '\0')
368           pending = '&';
369         else if (pending == '&')
370           *next_tokp = CR_AND;
371         else
372           return (CR_UNKNWTOK);
373         break;
374       case '|':
375         if (pending == '\0')
376           pending = '|';
377         else if (pending == '|')
378           *next_tokp = CR_OR;
379         else
380           return (CR_UNKNWTOK);
381         break;
382       case '!':
383         *next_tokp = CR_NOT;
384         break;
385       case '(':
386         *next_tokp = CR_OPENPAREN;
387         break;
388       case ')':
389         *next_tokp = CR_CLOSEPAREN;
390         break;
391       case ',':
392         *next_tokp = CR_COMMA;
393         break;
394       case '\0':
395         (*ruleptr)--;
396         *next_tokp = CR_END;
397         break;
398       case ':':
399         *next_tokp = CR_END;
400         break;
401       default:
402         if ((IsAlpha(*(--(*ruleptr)))) || (**ruleptr == '*') ||
403             (**ruleptr == '?') || (**ruleptr == '.') || (**ruleptr == '-'))
404           *next_tokp = CR_WORD;
405         else
406           return (CR_UNKNWTOK);
407         break;
408     }
409   return CR_NOERR;
410 }
411
412 /** Scan a word from \a ruleptr.
413  * @param[out] word Output buffer.
414  * @param[out] wordlenp Length of word written to \a word (not including terminating NUL).
415  * @param[in] maxlen Maximum number of bytes writable to \a word.
416  * @param[in,out] ruleptr Next readable character from input.
417  */
418 static void crule_getword(char* word, int* wordlenp, size_t maxlen, const char** ruleptr)
419 {
420   char *word_ptr;
421
422   word_ptr = word;
423   while ((size_t)(word_ptr - word) < maxlen
424       && (IsAlnum(**ruleptr)
425       || **ruleptr == '*' || **ruleptr == '?'
426       || **ruleptr == '.' || **ruleptr == '-'))
427     *word_ptr++ = *(*ruleptr)++;
428   *word_ptr = '\0';
429   *wordlenp = word_ptr - word;
430 }
431
432 /** Parse an entire rule.
433  * @param[in] rule Text form of rule.
434  * @return CRuleNode for rule, or NULL if there was a parse error.
435  */
436 struct CRuleNode* crule_parse(const char *rule)
437 {
438   const char* ruleptr = rule;
439   int next_tok;
440   struct CRuleNode* ruleroot = 0;
441   int errcode = CR_NOERR;
442
443   if ((errcode = crule_gettoken(&next_tok, &ruleptr)) == CR_NOERR) {
444     if ((errcode = crule_parseorexpr(&ruleroot, &next_tok, &ruleptr)) == CR_NOERR) {
445       if (ruleroot != NULL) {
446         if (next_tok == CR_END)
447           return (ruleroot);
448         else
449           errcode = CR_UNEXPCTTOK;
450       }
451       else
452         errcode = CR_EXPCTOR;
453     }
454   }
455   if (ruleroot != NULL)
456     crule_free(&ruleroot);
457 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
458   Debug((DEBUG_ERROR, "%s in rule: %s", crule_errstr[errcode], rule));
459 #else
460   fprintf(stderr, "%s in rule: %s\n", crule_errstr[errcode], rule);
461 #endif
462   return 0;
463 }
464
465 /** Parse an or expression.
466  * @param[out] orrootp Receives parsed node.
467  * @param[in,out] next_tokp Next input token type.
468  * @param[in,out] ruleptr Next input character.
469  * @return A crule_errcode value.
470  */
471 static int crule_parseorexpr(CRuleNodePtr * orrootp, int *next_tokp, const char** ruleptr)
472 {
473   int errcode = CR_NOERR;
474   CRuleNodePtr andexpr;
475   CRuleNodePtr orptr;
476
477   *orrootp = NULL;
478   while (errcode == CR_NOERR)
479   {
480     errcode = crule_parseandexpr(&andexpr, next_tokp, ruleptr);
481     if ((errcode == CR_NOERR) && (*next_tokp == CR_OR))
482     {
483       orptr = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
484 #ifdef CR_DEBUG
485       fprintf(stderr, "allocating or element at %ld\n", orptr);
486 #endif
487       orptr->funcptr = crule__andor;
488       orptr->numargs = 3;
489       orptr->arg[2] = (void *)1;
490       if (*orrootp != NULL)
491       {
492         (*orrootp)->arg[1] = andexpr;
493         orptr->arg[0] = *orrootp;
494       }
495       else
496         orptr->arg[0] = andexpr;
497       *orrootp = orptr;
498     }
499     else
500     {
501       if (*orrootp != NULL)
502       {
503         if (andexpr != NULL)
504         {
505           (*orrootp)->arg[1] = andexpr;
506           return (errcode);
507         }
508         else
509         {
510           (*orrootp)->arg[1] = NULL;    /* so free doesn't seg fault */
511           return (CR_EXPCTAND);
512         }
513       }
514       else
515       {
516         *orrootp = andexpr;
517         return (errcode);
518       }
519     }
520     if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
521       return (errcode);
522   }
523   return (errcode);
524 }
525
526 /** Parse an and expression.
527  * @param[out] androotp Receives parsed node.
528  * @param[in,out] next_tokp Next input token type.
529  * @param[in,out] ruleptr Next input character.
530  * @return A crule_errcode value.
531  */
532 static int crule_parseandexpr(CRuleNodePtr * androotp, int *next_tokp, const char** ruleptr)
533 {
534   int errcode = CR_NOERR;
535   CRuleNodePtr primary;
536   CRuleNodePtr andptr;
537
538   *androotp = NULL;
539   while (errcode == CR_NOERR)
540   {
541     errcode = crule_parseprimary(&primary, next_tokp, ruleptr);
542     if ((errcode == CR_NOERR) && (*next_tokp == CR_AND))
543     {
544       andptr = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
545 #ifdef CR_DEBUG
546       fprintf(stderr, "allocating and element at %ld\n", andptr);
547 #endif
548       andptr->funcptr = crule__andor;
549       andptr->numargs = 3;
550       andptr->arg[2] = (void *)0;
551       if (*androotp != NULL)
552       {
553         (*androotp)->arg[1] = primary;
554         andptr->arg[0] = *androotp;
555       }
556       else
557         andptr->arg[0] = primary;
558       *androotp = andptr;
559     }
560     else
561     {
562       if (*androotp != NULL)
563       {
564         if (primary != NULL)
565         {
566           (*androotp)->arg[1] = primary;
567           return (errcode);
568         }
569         else
570         {
571           (*androotp)->arg[1] = NULL;   /* so free doesn't seg fault */
572           return (CR_EXPCTPRIM);
573         }
574       }
575       else
576       {
577         *androotp = primary;
578         return (errcode);
579       }
580     }
581     if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
582       return (errcode);
583   }
584   return (errcode);
585 }
586
587 /** Parse a primary expression.
588  * @param[out] primrootp Receives parsed node.
589  * @param[in,out] next_tokp Next input token type.
590  * @param[in,out] ruleptr Next input character.
591  * @return A crule_errcode value.
592  */
593 static int crule_parseprimary(CRuleNodePtr* primrootp, int *next_tokp, const char** ruleptr)
594 {
595   CRuleNodePtr *insertionp;
596   int errcode = CR_NOERR;
597
598   *primrootp = NULL;
599   insertionp = primrootp;
600   while (errcode == CR_NOERR)
601   {
602     switch (*next_tokp)
603     {
604       case CR_OPENPAREN:
605         if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
606           break;
607         if ((errcode = crule_parseorexpr(insertionp, next_tokp, ruleptr)) != CR_NOERR)
608           break;
609         if (*insertionp == NULL)
610         {
611           errcode = CR_EXPCTAND;
612           break;
613         }
614         if (*next_tokp != CR_CLOSEPAREN)
615         {
616           errcode = CR_EXPCTCLOSE;
617           break;
618         }
619         errcode = crule_gettoken(next_tokp, ruleptr);
620         break;
621       case CR_NOT:
622         *insertionp = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
623 #ifdef CR_DEBUG
624         fprintf(stderr, "allocating primary element at %ld\n", *insertionp);
625 #endif
626         (*insertionp)->funcptr = crule__not;
627         (*insertionp)->numargs = 1;
628         (*insertionp)->arg[0] = NULL;
629         insertionp = (CRuleNodePtr *) & ((*insertionp)->arg[0]);
630         if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
631           break;
632         continue;
633       case CR_WORD:
634         errcode = crule_parsefunction(insertionp, next_tokp, ruleptr);
635         break;
636       default:
637         if (*primrootp == NULL)
638           errcode = CR_NOERR;
639         else
640           errcode = CR_EXPCTPRIM;
641         break;
642     }
643     return (errcode);
644   }
645   return (errcode);
646 }
647
648 /** Parse a function call.
649  * @param[out] funcrootp Receives parsed node.
650  * @param[in,out] next_tokp Next input token type.
651  * @param[in,out] ruleptr Next input character.
652  * @return A crule_errcode value.
653  */
654 static int crule_parsefunction(CRuleNodePtr* funcrootp, int* next_tokp, const char** ruleptr)
655 {
656   int errcode = CR_NOERR;
657   char funcname[CR_MAXARGLEN];
658   int namelen;
659   int funcnum;
660
661   *funcrootp = NULL;
662   crule_getword(funcname, &namelen, CR_MAXARGLEN - 1, ruleptr);
663   if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
664     return (errcode);
665   if (*next_tokp == CR_OPENPAREN)
666   {
667     for (funcnum = 0;; funcnum++)
668     {
669       if (0 == ircd_strcmp(crule_funclist[funcnum].name, funcname))
670         break;
671       if (crule_funclist[funcnum].name[0] == '\0')
672         return (CR_UNKNWFUNC);
673     }
674     if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
675       return (errcode);
676     *funcrootp = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
677 #ifdef CR_DEBUG
678     fprintf(stderr, "allocating function element at %ld\n", *funcrootp);
679 #endif
680     (*funcrootp)->funcptr = NULL;       /* for freeing aborted trees */
681     if ((errcode =
682         crule_parsearglist(*funcrootp, next_tokp, ruleptr)) != CR_NOERR)
683       return (errcode);
684     if (*next_tokp != CR_CLOSEPAREN)
685       return (CR_EXPCTCLOSE);
686     if ((crule_funclist[funcnum].reqnumargs != (*funcrootp)->numargs) &&
687         (crule_funclist[funcnum].reqnumargs != -1))
688       return (CR_ARGMISMAT);
689     if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
690       return (errcode);
691     (*funcrootp)->funcptr = crule_funclist[funcnum].funcptr;
692     return (CR_NOERR);
693   }
694   else
695     return (CR_EXPCTOPEN);
696 }
697
698 /** Parse the argument list to a CRuleNode.
699  * @param[in,out] argrootp Node whos argument list is being populated.
700  * @param[in,out] next_tokp Next input token type.
701  * @param[in,out] ruleptr Next input character.
702  * @return A crule_errcode value.
703  */
704 static int crule_parsearglist(CRuleNodePtr argrootp, int *next_tokp, const char** ruleptr)
705 {
706   int errcode = CR_NOERR;
707   char *argelemp = NULL;
708   char currarg[CR_MAXARGLEN];
709   int arglen = 0;
710   char word[CR_MAXARGLEN];
711   int wordlen = 0;
712
713   argrootp->numargs = 0;
714   currarg[0] = '\0';
715   while (errcode == CR_NOERR)
716   {
717     switch (*next_tokp)
718     {
719       case CR_WORD:
720         crule_getword(word, &wordlen, CR_MAXARGLEN - 1, ruleptr);
721         if (currarg[0] != '\0')
722         {
723           if ((arglen + wordlen) < (CR_MAXARGLEN - 1))
724           {
725             strcat(currarg, " ");
726             strcat(currarg, word);
727             arglen += wordlen + 1;
728           }
729         }
730         else
731         {
732           strcpy(currarg, word);
733           arglen = wordlen;
734         }
735         errcode = crule_gettoken(next_tokp, ruleptr);
736         break;
737       default:
738 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
739         collapse(currarg);
740 #endif
741         if (!BadPtr(currarg))
742         {
743           DupString(argelemp, currarg);
744           argrootp->arg[argrootp->numargs++] = (void *)argelemp;
745         }
746         if (*next_tokp != CR_COMMA)
747           return (CR_NOERR);
748         currarg[0] = '\0';
749         errcode = crule_gettoken(next_tokp, ruleptr);
750         break;
751     }
752   }
753   return (errcode);
754 }
755
756 /*
757  * This function is recursive..  I wish I knew a nonrecursive way but
758  * I dont.  anyway, recursion is fun..  :)
759  * DO NOT CALL THIS FUNTION WITH A POINTER TO A NULL POINTER
760  * (ie: If *elem is NULL, you're doing it wrong - seg fault)
761  */
762 /** Free a connection rule and all its children.
763  * @param[in,out] elem Pointer to pointer to element to free.  MUST NOT BE NULL.
764  */
765 void crule_free(struct CRuleNode** elem)
766 {
767   int arg, numargs;
768
769   if ((*(elem))->funcptr == crule__not)
770   {
771     /* type conversions and ()'s are fun! ;)  here have an asprin.. */
772     if ((*(elem))->arg[0] != NULL)
773       crule_free((struct CRuleNode**) &((*(elem))->arg[0]));
774   }
775   else if ((*(elem))->funcptr == crule__andor)
776   {
777     crule_free((struct CRuleNode**) &((*(elem))->arg[0]));
778     if ((*(elem))->arg[1] != NULL)
779       crule_free((struct CRuleNode**) &((*(elem))->arg[1]));
780   }
781   else
782   {
783     numargs = (*(elem))->numargs;
784     for (arg = 0; arg < numargs; arg++)
785       MyFree((*(elem))->arg[arg]);
786   }
787 #ifdef CR_DEBUG
788   fprintf(stderr, "freeing element at %ld\n", *elem);
789 #endif
790   MyFree(*elem);
791   *elem = 0;
792 }
793
794 #ifdef CR_DEBUG
795 /** Display a connection rule as text.
796  * @param[in] printelem Connection rule to display.
797  */
798 static void print_tree(CRuleNodePtr printelem)
799 {
800   int funcnum, arg;
801
802   if (printelem->funcptr == crule__not)
803   {
804     printf("!( ");
805     print_tree((CRuleNodePtr) printelem->arg[0]);
806     printf(") ");
807   }
808   else if (printelem->funcptr == crule__andor)
809   {
810     printf("( ");
811     print_tree((CRuleNodePtr) printelem->arg[0]);
812     if (printelem->arg[2])
813       printf("|| ");
814     else
815       printf("&& ");
816     print_tree((CRuleNodePtr) printelem->arg[1]);
817     printf(") ");
818   }
819   else
820   {
821     for (funcnum = 0;; funcnum++)
822     {
823       if (printelem->funcptr == crule_funclist[funcnum].funcptr)
824         break;
825       if (crule_funclist[funcnum].funcptr == NULL)
826         MyCoreDump;
827     }
828     printf("%s(", crule_funclist[funcnum].name);
829     for (arg = 0; arg < printelem->numargs; arg++)
830     {
831       if (arg != 0)
832         printf(",");
833       printf("%s", (char *)printelem->arg[arg]);
834     }
835     printf(") ");
836   }
837 }
838
839 #endif
840
841 #ifdef CR_DEBUG
842 /** Read connection rules from stdin and display parsed forms as text.
843  * @return Zero.
844  */
845 int main(void)
846 {
847   char indata[256];
848   CRuleNode* rule;
849
850   printf("rule: ");
851   while (fgets(indata, 256, stdin) != NULL)
852   {
853     indata[strlen(indata) - 1] = '\0';  /* lose the newline */
854     if ((rule = crule_parse(indata)) != NULL)
855     {
856       printf("equivalent rule: ");
857       print_tree((CRuleNodePtr) rule);
858       printf("\n");
859       crule_free(&rule);
860     }
861     printf("\nrule: ");
862   }
863   printf("\n");
864
865   return 0;
866 }
867
868 #endif