unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

crule.c (23446B)

      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 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 
     58 /* Last update of parser functions taken from ircu on 2023-03-19
     59  * matching ircu's ircd/crule.c from 2021-09-04.
     60  * Then ported / UnrealIRCd-ized by Syzop and re-adding crule_test()
     61  * and such. All the actual "functions" like crule_connected() are
     62  * our own and not re-feteched (but were based on older versions).
     63  */
     64 
     65 #ifndef CR_DEBUG
     66 /* ircd functions and types we need */
     67 #include "struct.h"
     68 #include "common.h"
     69 #include "sys.h"
     70 #include "h.h"
     71 #include <string.h>
     72 
     73 char *collapse(char *pattern);
     74 extern Client *client;
     75 
     76 ID_Copyright("(C) Tony Vincell");
     77 
     78 #else
     79 /* includes and defines to make the stand-alone test parser */
     80 #include <stdio.h>
     81 #include <string.h>
     82 #define BadPtr(x) (!(x) || (*(x) == '\0'))
     83 #endif
     84 
     85 #if defined(CR_DEBUG) || defined(CR_CHKCONF)
     86 #undef safe_free
     87 #undef free
     88 #define safe_free free
     89 #endif
     90 
     91 /*
     92  * Some symbols for easy reading
     93  */
     94 
     95 /** Input scanner tokens. */
     96 enum crule_token {
     97   CR_UNKNOWN,    /**< Unknown token type. */
     98   CR_END,        /**< End of input ('\\0' or ':'). */
     99   CR_AND,        /**< Logical and operator (&&). */
    100   CR_OR,         /**< Logical or operator (||). */
    101   CR_NOT,        /**< Logical not operator (!). */
    102   CR_OPENPAREN,  /**< Open parenthesis. */
    103   CR_CLOSEPAREN, /**< Close parenthesis. */
    104   CR_COMMA,      /**< Comma. */
    105   CR_WORD        /**< Something that looks like a hostmask (alphanumerics, "*?.-"). */
    106 };
    107 
    108 /** Parser error codes. */
    109 enum crule_errcode {
    110   CR_NOERR,      /**< No error. */
    111   CR_UNEXPCTTOK, /**< Invalid token given context. */
    112   CR_UNKNWTOK,   /**< Input did not form a valid token. */
    113   CR_EXPCTAND,   /**< Did not see expected && operator. */
    114   CR_EXPCTOR,    /**< Did not see expected || operator. */
    115   CR_EXPCTPRIM,  /**< Expected a primitive (parentheses, ! or word). */
    116   CR_EXPCTOPEN,  /**< Expected an open parenthesis after function name. */
    117   CR_EXPCTCLOSE, /**< Expected a close parenthesis to match open parenthesis. */
    118   CR_UNKNWFUNC,  /**< Attempt to use an unknown function. */
    119   CR_ARGMISMAT   /**< Wrong number of arguments to function. */
    120 };
    121 
    122 /*
    123  * Expression tree structure, function pointer, and tree pointer local!
    124  */
    125 
    126 /* rule function prototypes - local! */
    127 static int crule_connected(int, void **);
    128 static int crule_directcon(int, void **);
    129 static int crule_via(int, void **);
    130 static int crule_directop(int, void **);
    131 static int crule__andor(int, void **);
    132 static int crule__not(int, void **);
    133 
    134 /* parsing function prototypes - local! */
    135 static int crule_gettoken(int* token, const char** str);
    136 static void crule_getword(char*, int*, size_t, const char**);
    137 static int crule_parseandexpr(CRuleNodePtr*, int *, const char**);
    138 static int crule_parseorexpr(CRuleNodePtr*, int *, const char**);
    139 static int crule_parseprimary(CRuleNodePtr*, int *, const char**);
    140 static int crule_parsefunction(CRuleNodePtr*, int *, const char**);
    141 static int crule_parsearglist(CRuleNodePtr, int *, const char**);
    142 
    143 #if defined(CR_DEBUG) || defined(CR_CHKCONF)
    144 /*
    145  * Prototypes for the test parser; if not debugging,
    146  * these are defined in h.h
    147  */
    148 struct CRuleNode* crule_parse(const char*);
    149 void crule_free(struct CRuleNode**);
    150 #ifdef CR_DEBUG
    151 void print_tree(CRuleNodePtr);
    152 #endif
    153 #endif
    154 
    155 /* error messages */
    156 char *crule_errstr[] = {
    157 	"Unknown error",	/* NOERR? - for completeness */
    158 	"Unexpected token",	/* UNEXPCTTOK */
    159 	"Unknown token",	/* UNKNWTOK */
    160 	"And expr expected",	/* EXPCTAND */
    161 	"Or expr expected",	/* EXPCTOR */
    162 	"Primary expected",	/* EXPCTPRIM */
    163 	"( expected",		/* EXPCTOPEN */
    164 	") expected",		/* EXPCTCLOSE */
    165 	"Unknown function",	/* UNKNWFUNC */
    166 	"Argument mismatch"	/* ARGMISMAT */
    167 };
    168 
    169 /* function table - null terminated */
    170 struct crule_funclistent {
    171 	char name[15];		/* MAXIMUM FUNCTION NAME LENGTH IS 14 CHARS!! */
    172 	int  reqnumargs;
    173 	crule_funcptr funcptr;
    174 };
    175 struct crule_funclistent crule_funclist[] = {
    176 	/* maximum function name length is 14 chars */
    177 	{"connected", 1, crule_connected},
    178 	{"directcon", 1, crule_directcon},
    179 	{"via", 2, crule_via},
    180 	{"directop", 0, crule_directop},
    181 	{"", 0, NULL}		/* this must be here to mark end of list */
    182 };
    183 
    184 static int crule_connected(int numargs, void *crulearg[])
    185 {
    186 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
    187 	Client *client;
    188 
    189 	/* taken from cmd_links */
    190 	/* Faster this way -- codemastr*/
    191 	list_for_each_entry(client, &global_server_list, client_node)
    192 	{
    193 		if (!match_simple((char *)crulearg[0], client->name))
    194 			continue;
    195 		return (1);
    196 	}
    197 	return (0);
    198 #endif
    199 }
    200 
    201 static int crule_directcon(int numargs, void *crulearg[])
    202 {
    203 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
    204 	Client *client;
    205 
    206 	/* adapted from cmd_trace and exit_one_client */
    207 	/* XXX: iterate server_list when added */
    208 	list_for_each_entry(client, &lclient_list, lclient_node)
    209 	{
    210 		if (!IsServer(client))
    211 			continue;
    212 		if (!match_simple((char *)crulearg[0], client->name))
    213 			continue;
    214 		return (1);
    215 	}
    216 	return (0);
    217 #endif
    218 }
    219 
    220 static int crule_via(int numargs, void *crulearg[])
    221 {
    222 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
    223 	Client *client;
    224 
    225 	/* adapted from cmd_links */
    226 	/* Faster this way -- codemastr */
    227 	list_for_each_entry(client, &global_server_list, client_node)
    228 	{
    229 		if (!match_simple((char *)crulearg[1], client->name))
    230 			continue;
    231 		if (!match_simple((char *)crulearg[0], client->uplink->name))
    232 			continue;
    233 		return (1);
    234 	}
    235 	return (0);
    236 #endif
    237 }
    238 
    239 static int crule_directop(int numargs, void *crulearg[])
    240 {
    241 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
    242 	Client *client;
    243 
    244 	/* adapted from cmd_trace */
    245 	list_for_each_entry(client, &lclient_list, lclient_node)
    246 	{
    247 		if (!IsOper(client))
    248 			continue;
    249 
    250 		return (1);
    251 	}
    252 
    253 	return (0);
    254 #endif
    255 }
    256 
    257 /** Evaluate a connection rule.
    258  * @param[in] rule Rule to evalute.
    259  * @return Non-zero if the rule allows the connection, zero otherwise.
    260  */
    261 int crule_eval(struct CRuleNode* rule)
    262 {
    263   return (rule->funcptr(rule->numargs, rule->arg));
    264 }
    265 
    266 /** Perform an and-or-or test on crulearg[0] and crulearg[1].
    267  * If crulearg[2] is non-NULL, it means do OR; if it is NULL, do AND.
    268  * @param[in] numargs Number of valid args in \a crulearg.
    269  * @param[in] crulearg Argument array.
    270  * @return Non-zero if the condition is true, zero if not.
    271  */
    272 static int crule__andor(int numargs, void *crulearg[])
    273 {
    274   int result1;
    275 
    276   result1 = crule_eval(crulearg[0]);
    277   if (crulearg[2])              /* or */
    278     return (result1 || crule_eval(crulearg[1]));
    279   else
    280     return (result1 && crule_eval(crulearg[1]));
    281 }
    282 
    283 /** Logically invert the result of crulearg[0].
    284  * @param[in] numargs Number of valid args in \a crulearg.
    285  * @param[in] crulearg Argument array.
    286  * @return Non-zero if the condition is true, zero if not.
    287  */
    288 static int crule__not(int numargs, void *crulearg[])
    289 {
    290   return (!crule_eval(crulearg[0]));
    291 }
    292 
    293 /** Scan an input token from \a ruleptr.
    294  * @param[out] next_tokp Receives type of next token.
    295  * @param[in,out] ruleptr Next readable character from input.
    296  * @return Either CR_UNKNWTOK if the input was unrecognizable, else CR_NOERR.
    297  */
    298 static int crule_gettoken(int* next_tokp, const char** ruleptr)
    299 {
    300   char pending = '\0';
    301 
    302   *next_tokp = CR_UNKNOWN;
    303   while (*next_tokp == CR_UNKNOWN)
    304     switch (*(*ruleptr)++)
    305     {
    306       case ' ':
    307       case '\t':
    308         break;
    309       case '&':
    310         if (pending == '\0')
    311           pending = '&';
    312         else if (pending == '&')
    313           *next_tokp = CR_AND;
    314         else
    315           return (CR_UNKNWTOK);
    316         break;
    317       case '|':
    318         if (pending == '\0')
    319           pending = '|';
    320         else if (pending == '|')
    321           *next_tokp = CR_OR;
    322         else
    323           return (CR_UNKNWTOK);
    324         break;
    325       case '!':
    326         *next_tokp = CR_NOT;
    327         break;
    328       case '(':
    329         *next_tokp = CR_OPENPAREN;
    330         break;
    331       case ')':
    332         *next_tokp = CR_CLOSEPAREN;
    333         break;
    334       case ',':
    335         *next_tokp = CR_COMMA;
    336         break;
    337       case '\0':
    338         (*ruleptr)--;
    339         *next_tokp = CR_END;
    340         break;
    341       case ':':
    342         *next_tokp = CR_END;
    343         break;
    344       default:
    345         if ((isalpha(*(--(*ruleptr)))) || (**ruleptr == '*') ||
    346             (**ruleptr == '?') || (**ruleptr == '.') || (**ruleptr == '-'))
    347           *next_tokp = CR_WORD;
    348         else
    349           return (CR_UNKNWTOK);
    350         break;
    351     }
    352   return CR_NOERR;
    353 }
    354 
    355 /** Scan a word from \a ruleptr.
    356  * @param[out] word Output buffer.
    357  * @param[out] wordlenp Length of word written to \a word (not including terminating NUL).
    358  * @param[in] maxlen Maximum number of bytes writable to \a word.
    359  * @param[in,out] ruleptr Next readable character from input.
    360  */
    361 static void crule_getword(char* word, int* wordlenp, size_t maxlen, const char** ruleptr)
    362 {
    363   char *word_ptr;
    364 
    365   word_ptr = word;
    366   while ((size_t)(word_ptr - word) < maxlen
    367       && (isalnum(**ruleptr)
    368       || **ruleptr == '*' || **ruleptr == '?'
    369       || **ruleptr == '.' || **ruleptr == '-'))
    370     *word_ptr++ = *(*ruleptr)++;
    371   *word_ptr = '\0';
    372   *wordlenp = word_ptr - word;
    373 }
    374 
    375 /** Parse an entire rule.
    376  * @param[in] rule Text form of rule.
    377  * @return CRuleNode for rule, or NULL if there was a parse error.
    378  */
    379 struct CRuleNode* crule_parse(const char *rule)
    380 {
    381   const char* ruleptr = rule;
    382   int next_tok;
    383   struct CRuleNode* ruleroot = 0;
    384   int errcode = CR_NOERR;
    385 
    386   if ((errcode = crule_gettoken(&next_tok, &ruleptr)) == CR_NOERR) {
    387     if ((errcode = crule_parseorexpr(&ruleroot, &next_tok, &ruleptr)) == CR_NOERR) {
    388       if (ruleroot != NULL) {
    389         if (next_tok == CR_END)
    390           return (ruleroot);
    391         else
    392           errcode = CR_UNEXPCTTOK;
    393       }
    394       else
    395         errcode = CR_EXPCTOR;
    396     }
    397   }
    398   if (ruleroot != NULL)
    399     crule_free(&ruleroot);
    400 #if defined(CR_DEBUG) || defined(CR_CHKCONF)
    401   fprintf(stderr, "%s in rule: %s\n", crule_errstr[errcode], rule);
    402 #endif
    403   return 0;
    404 }
    405 
    406 /** Test-parse an entire rule.
    407  * @param[in] rule Text form of rule.
    408  * @return error code, or 0 for no failure
    409  */
    410 int crule_test(const char *rule)
    411 {
    412   const char* ruleptr = rule;
    413   int next_tok;
    414   struct CRuleNode* ruleroot = 0;
    415   int errcode = CR_NOERR;
    416 
    417   if ((errcode = crule_gettoken(&next_tok, &ruleptr)) == CR_NOERR) {
    418     if ((errcode = crule_parseorexpr(&ruleroot, &next_tok, &ruleptr)) == CR_NOERR) {
    419       if (ruleroot != NULL) {
    420         if (next_tok == CR_END)
    421         {
    422           /* PASS */
    423           crule_free(&ruleroot);
    424           return 0;
    425         } else {
    426           errcode = CR_UNEXPCTTOK;
    427         }
    428       }
    429       else
    430         errcode = CR_EXPCTOR;
    431     }
    432   }
    433   if (ruleroot != NULL)
    434     crule_free(&ruleroot);
    435 #if defined(CR_DEBUG) || defined(CR_CHKCONF)
    436   fprintf(stderr, "%s in rule: %s\n", crule_errstr[errcode], rule);
    437 #endif
    438   return errcode + 1;
    439 }
    440 
    441 const char *crule_errstring(int errcode)
    442 {
    443   if (errcode == 0)
    444     return "No error";
    445   else
    446     return crule_errstr[errcode-1];
    447 }
    448 
    449 /** Parse an or expression.
    450  * @param[out] orrootp Receives parsed node.
    451  * @param[in,out] next_tokp Next input token type.
    452  * @param[in,out] ruleptr Next input character.
    453  * @return A crule_errcode value.
    454  */
    455 static int crule_parseorexpr(CRuleNodePtr * orrootp, int *next_tokp, const char** ruleptr)
    456 {
    457   int errcode = CR_NOERR;
    458   CRuleNodePtr andexpr;
    459   CRuleNodePtr orptr;
    460 
    461   *orrootp = NULL;
    462   while (errcode == CR_NOERR)
    463   {
    464     errcode = crule_parseandexpr(&andexpr, next_tokp, ruleptr);
    465     if ((errcode == CR_NOERR) && (*next_tokp == CR_OR))
    466     {
    467       orptr = safe_alloc(sizeof(struct CRuleNode));
    468 #ifdef CR_DEBUG
    469       fprintf(stderr, "allocating or element at %ld\n", orptr);
    470 #endif
    471       orptr->funcptr = crule__andor;
    472       orptr->numargs = 3;
    473       orptr->arg[2] = (void *)1;
    474       if (*orrootp != NULL)
    475       {
    476         (*orrootp)->arg[1] = andexpr;
    477         orptr->arg[0] = *orrootp;
    478       }
    479       else
    480         orptr->arg[0] = andexpr;
    481       *orrootp = orptr;
    482     }
    483     else
    484     {
    485       if (*orrootp != NULL)
    486       {
    487         if (andexpr != NULL)
    488         {
    489           (*orrootp)->arg[1] = andexpr;
    490           return (errcode);
    491         }
    492         else
    493         {
    494           (*orrootp)->arg[1] = NULL;    /* so free doesn't seg fault */
    495           return (CR_EXPCTAND);
    496         }
    497       }
    498       else
    499       {
    500         *orrootp = andexpr;
    501         return (errcode);
    502       }
    503     }
    504     errcode = crule_gettoken(next_tokp, ruleptr);
    505   }
    506   return (errcode);
    507 }
    508 
    509 /** Parse an and expression.
    510  * @param[out] androotp Receives parsed node.
    511  * @param[in,out] next_tokp Next input token type.
    512  * @param[in,out] ruleptr Next input character.
    513  * @return A crule_errcode value.
    514  */
    515 static int crule_parseandexpr(CRuleNodePtr * androotp, int *next_tokp, const char** ruleptr)
    516 {
    517   int errcode = CR_NOERR;
    518   CRuleNodePtr primary;
    519   CRuleNodePtr andptr;
    520 
    521   *androotp = NULL;
    522   while (errcode == CR_NOERR)
    523   {
    524     errcode = crule_parseprimary(&primary, next_tokp, ruleptr);
    525     if ((errcode == CR_NOERR) && (*next_tokp == CR_AND))
    526     {
    527       andptr = safe_alloc(sizeof(struct CRuleNode));
    528 #ifdef CR_DEBUG
    529       fprintf(stderr, "allocating and element at %ld\n", andptr);
    530 #endif
    531       andptr->funcptr = crule__andor;
    532       andptr->numargs = 3;
    533       andptr->arg[2] = (void *)0;
    534       if (*androotp != NULL)
    535       {
    536         (*androotp)->arg[1] = primary;
    537         andptr->arg[0] = *androotp;
    538       }
    539       else
    540         andptr->arg[0] = primary;
    541       *androotp = andptr;
    542     }
    543     else
    544     {
    545       if (*androotp != NULL)
    546       {
    547         if (primary != NULL)
    548         {
    549           (*androotp)->arg[1] = primary;
    550           return (errcode);
    551         }
    552         else
    553         {
    554           (*androotp)->arg[1] = NULL;   /* so free doesn't seg fault */
    555           return (CR_EXPCTPRIM);
    556         }
    557       }
    558       else
    559       {
    560         *androotp = primary;
    561         return (errcode);
    562       }
    563     }
    564     errcode = crule_gettoken(next_tokp, ruleptr);
    565   }
    566   return (errcode);
    567 }
    568 
    569 /** Parse a primary expression.
    570  * @param[out] primrootp Receives parsed node.
    571  * @param[in,out] next_tokp Next input token type.
    572  * @param[in,out] ruleptr Next input character.
    573  * @return A crule_errcode value.
    574  */
    575 static int crule_parseprimary(CRuleNodePtr* primrootp, int *next_tokp, const char** ruleptr)
    576 {
    577   CRuleNodePtr *insertionp;
    578   int errcode = CR_NOERR;
    579 
    580   *primrootp = NULL;
    581   insertionp = primrootp;
    582   while (errcode == CR_NOERR)
    583   {
    584     switch (*next_tokp)
    585     {
    586       case CR_OPENPAREN:
    587         if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
    588           break;
    589         if ((errcode = crule_parseorexpr(insertionp, next_tokp, ruleptr)) != CR_NOERR)
    590           break;
    591         if (*insertionp == NULL)
    592         {
    593           errcode = CR_EXPCTAND;
    594           break;
    595         }
    596         if (*next_tokp != CR_CLOSEPAREN)
    597         {
    598           errcode = CR_EXPCTCLOSE;
    599           break;
    600         }
    601         errcode = crule_gettoken(next_tokp, ruleptr);
    602         break;
    603       case CR_NOT:
    604         *insertionp = safe_alloc(sizeof(struct CRuleNode));
    605 #ifdef CR_DEBUG
    606         fprintf(stderr, "allocating primary element at %ld\n", *insertionp);
    607 #endif
    608         (*insertionp)->funcptr = crule__not;
    609         (*insertionp)->numargs = 1;
    610         (*insertionp)->arg[0] = NULL;
    611         insertionp = (CRuleNodePtr *) & ((*insertionp)->arg[0]);
    612         if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
    613           break;
    614         continue;
    615       case CR_WORD:
    616         errcode = crule_parsefunction(insertionp, next_tokp, ruleptr);
    617         break;
    618       default:
    619         if (*primrootp == NULL)
    620           errcode = CR_NOERR;
    621         else
    622           errcode = CR_EXPCTPRIM;
    623         break;
    624     }
    625     break; /* loop only continues after a CR_NOT */
    626   }
    627   return (errcode);
    628 }
    629 
    630 /** Parse a function call.
    631  * @param[out] funcrootp Receives parsed node.
    632  * @param[in,out] next_tokp Next input token type.
    633  * @param[in,out] ruleptr Next input character.
    634  * @return A crule_errcode value.
    635  */
    636 static int crule_parsefunction(CRuleNodePtr* funcrootp, int* next_tokp, const char** ruleptr)
    637 {
    638   int errcode = CR_NOERR;
    639   char funcname[CR_MAXARGLEN];
    640   int namelen;
    641   int funcnum;
    642 
    643   *funcrootp = NULL;
    644   crule_getword(funcname, &namelen, CR_MAXARGLEN - 1, ruleptr);
    645   if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
    646     return (errcode);
    647   if (*next_tokp == CR_OPENPAREN)
    648   {
    649     for (funcnum = 0;; funcnum++)
    650     {
    651       if (0 == strcasecmp(crule_funclist[funcnum].name, funcname))
    652         break;
    653       if (crule_funclist[funcnum].name[0] == '\0')
    654         return (CR_UNKNWFUNC);
    655     }
    656     if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
    657       return (errcode);
    658     *funcrootp = safe_alloc(sizeof(struct CRuleNode));
    659 #ifdef CR_DEBUG
    660     fprintf(stderr, "allocating function element at %ld\n", *funcrootp);
    661 #endif
    662     (*funcrootp)->funcptr = NULL;       /* for freeing aborted trees */
    663     if ((errcode =
    664         crule_parsearglist(*funcrootp, next_tokp, ruleptr)) != CR_NOERR)
    665       return (errcode);
    666     if (*next_tokp != CR_CLOSEPAREN)
    667       return (CR_EXPCTCLOSE);
    668     if ((crule_funclist[funcnum].reqnumargs != (*funcrootp)->numargs) &&
    669         (crule_funclist[funcnum].reqnumargs != -1))
    670       return (CR_ARGMISMAT);
    671     if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
    672       return (errcode);
    673     (*funcrootp)->funcptr = crule_funclist[funcnum].funcptr;
    674     return (CR_NOERR);
    675   }
    676   else
    677     return (CR_EXPCTOPEN);
    678 }
    679 
    680 /** Parse the argument list to a CRuleNode.
    681  * @param[in,out] argrootp Node whos argument list is being populated.
    682  * @param[in,out] next_tokp Next input token type.
    683  * @param[in,out] ruleptr Next input character.
    684  * @return A crule_errcode value.
    685  */
    686 static int crule_parsearglist(CRuleNodePtr argrootp, int *next_tokp, const char** ruleptr)
    687 {
    688   int errcode = CR_NOERR;
    689   char *argelemp = NULL;
    690   char currarg[CR_MAXARGLEN];
    691   int arglen = 0;
    692   char word[CR_MAXARGLEN];
    693   int wordlen = 0;
    694 
    695   argrootp->numargs = 0;
    696   currarg[0] = '\0';
    697   while (errcode == CR_NOERR)
    698   {
    699     switch (*next_tokp)
    700     {
    701       case CR_WORD:
    702         crule_getword(word, &wordlen, CR_MAXARGLEN - 1, ruleptr);
    703         if (currarg[0] != '\0')
    704         {
    705           if ((arglen + wordlen) < (CR_MAXARGLEN - 1))
    706           {
    707             strcat(currarg, " ");
    708             strcat(currarg, word);
    709             arglen += wordlen + 1;
    710           }
    711         }
    712         else
    713         {
    714           strcpy(currarg, word);
    715           arglen = wordlen;
    716         }
    717         errcode = crule_gettoken(next_tokp, ruleptr);
    718         break;
    719       default:
    720 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
    721         collapse(currarg);
    722 #endif
    723         if (currarg[0] != '\0')
    724         {
    725           argelemp = raw_strdup(currarg);
    726           argrootp->arg[argrootp->numargs++] = (void *)argelemp;
    727         }
    728         if (*next_tokp != CR_COMMA)
    729           return (CR_NOERR);
    730         currarg[0] = '\0';
    731         errcode = crule_gettoken(next_tokp, ruleptr);
    732         break;
    733     }
    734   }
    735   return (errcode);
    736 }
    737 
    738 /*
    739  * This function is recursive..  I wish I knew a nonrecursive way but
    740  * I don't.  Anyway, recursion is fun..  :)
    741  * DO NOT CALL THIS FUNCTION WITH A POINTER TO A NULL POINTER
    742  * (i.e.: If *elem is NULL, you're doing it wrong - seg fault)
    743  */
    744 /** Free a connection rule and all its children.
    745  * @param[in,out] elem Pointer to pointer to element to free.  MUST NOT BE NULL.
    746  */
    747 void crule_free(struct CRuleNode** elem)
    748 {
    749   int arg, numargs;
    750 
    751   if ((*(elem))->funcptr == crule__not)
    752   {
    753     /* type conversions and ()'s are fun! ;)  here have an aspirin.. */
    754     if ((*(elem))->arg[0] != NULL)
    755       crule_free((struct CRuleNode**) &((*(elem))->arg[0]));
    756   }
    757   else if ((*(elem))->funcptr == crule__andor)
    758   {
    759     crule_free((struct CRuleNode**) &((*(elem))->arg[0]));
    760     if ((*(elem))->arg[1] != NULL)
    761       crule_free((struct CRuleNode**) &((*(elem))->arg[1]));
    762   }
    763   else
    764   {
    765     numargs = (*(elem))->numargs;
    766     for (arg = 0; arg < numargs; arg++)
    767       safe_free((*(elem))->arg[arg]);
    768   }
    769 #ifdef CR_DEBUG
    770   fprintf(stderr, "freeing element at %ld\n", *elem);
    771 #endif
    772   safe_free(*elem);
    773   *elem = 0;
    774 }
    775 
    776 #ifdef CR_DEBUG
    777 /** Display a connection rule as text.
    778  * @param[in] printelem Connection rule to display.
    779  */
    780 static void print_tree(CRuleNodePtr printelem)
    781 {
    782   int funcnum, arg;
    783 
    784   if (printelem->funcptr == crule__not)
    785   {
    786     printf("!( ");
    787     print_tree((CRuleNodePtr) printelem->arg[0]);
    788     printf(") ");
    789   }
    790   else if (printelem->funcptr == crule__andor)
    791   {
    792     printf("( ");
    793     print_tree((CRuleNodePtr) printelem->arg[0]);
    794     if (printelem->arg[2])
    795       printf("|| ");
    796     else
    797       printf("&& ");
    798     print_tree((CRuleNodePtr) printelem->arg[1]);
    799     printf(") ");
    800   }
    801   else
    802   {
    803     for (funcnum = 0;; funcnum++)
    804     {
    805       if (printelem->funcptr == crule_funclist[funcnum].funcptr)
    806         break;
    807       if (crule_funclist[funcnum].funcptr == NULL)
    808         MyCoreDump;
    809     }
    810     printf("%s(", crule_funclist[funcnum].name);
    811     for (arg = 0; arg < printelem->numargs; arg++)
    812     {
    813       if (arg != 0)
    814         printf(",");
    815       printf("%s", (char *)printelem->arg[arg]);
    816     }
    817     printf(") ");
    818   }
    819 }
    820 
    821 #endif
    822 
    823 #ifdef CR_DEBUG
    824 /** Read connection rules from stdin and display parsed forms as text.
    825  * @return Zero.
    826  */
    827 int main(void)
    828 {
    829   char indata[256];
    830   CRuleNode* rule;
    831 
    832   printf("rule: ");
    833   while (fgets(indata, 256, stdin) != NULL)
    834   {
    835     indata[strlen(indata) - 1] = '\0';  /* lose the newline */
    836     if ((rule = crule_parse(indata)) != NULL)
    837     {
    838       printf("equivalent rule: ");
    839       print_tree((CRuleNodePtr) rule);
    840       printf("\n");
    841       crule_free(&rule);
    842     }
    843     printf("\nrule: ");
    844   }
    845   printf("\n");
    846 
    847   return 0;
    848 }
    849 
    850 #endif