Good idea?  Bad idea?  Good idea but bad code?  Would other crule
functions be useful?  Should I be spending more time on other feature
requests?

(doc/example.conf is not yet updated; the syntax is basically the same
as before, but with quoted strings instead of unquoted strings.  The
crule_expr production in ircd_parser.y should be clear.)

Entrope

2006-07-25  Michael Poole <[EMAIL PROTECTED]>

        * include/crule.h (crule_free): Remove a level of indirection.
        (crule_parse): Remove declaration.
        (crule_text): New function.
        (crule_make_and): New function.
        (crule_make_or): New function.
        (crule_make_not): New function.
        (crule_make_connected): New function.
        (crule_make_directcon): New function.
        (crule_make_via): New function.
        (crule_make_directop): New function.

        * ircd/crule.c: Rewrite.

        * ircd/ircd_lexer.l: Recognize new tokens &&, ||, CONNECTED,
        DIRECTCON, DIRECTOP, VIA.

        * ircd/ircd_parser.y: Declare the new tokens.
        (optall): New production with type <num>.
        (crule_expr): New production with type <crule>.
        (%union): Add new member to expression type union.
        (cruleblock): Rewrite to not use {} and to use optall, crule_expr.

        * ircd/s_conf.c (conf_erase_crule_list): Remove a level of
        indirection for crule_free().

--- orig/include/crule.h
+++ mod/include/crule.h
@@ -6,16 +6,23 @@
 #define INCLUDED_crule_h
 
 /*
- * Proto types
- */
-
-/*
  * opaque node pointer
  */
 struct CRuleNode;
 
-extern void crule_free(struct CRuleNode** elem);
 extern int crule_eval(struct CRuleNode* rule);
-extern struct CRuleNode* crule_parse(const char* rule);
+extern char *crule_text(struct CRuleNode *rule);
+
+extern struct CRuleNode* crule_make_and(struct CRuleNode *left,
+                                        struct CRuleNode *right);
+extern struct CRuleNode* crule_make_or(struct CRuleNode *left,
+                                       struct CRuleNode *right);
+extern struct CRuleNode* crule_make_not(struct CRuleNode *arg);
+extern struct CRuleNode* crule_make_connected(char *arg);
+extern struct CRuleNode* crule_make_directcon(char *arg);
+extern struct CRuleNode* crule_make_via(char *neighbor,
+                                        char *server);
+extern struct CRuleNode* crule_make_directop(void);
+extern void crule_free(struct CRuleNode* elem);
 
 #endif /* INCLUDED_crule_h */


--- orig/ircd/crule.c
+++ mod/ircd/crule.c
@@ -4,866 +4,294 @@
  * @version $Id: crule.c,v 1.11 2006/01/06 11:39:04 isomer Exp $
  *
  * by Tony Vencill (Tonto on IRC) <[EMAIL PROTECTED]>
+ * rewritten by Michael Poole <[EMAIL PROTECTED]>
  *
- * The majority of this file is a recursive descent parser used to convert
- * connection rules into expression trees when the conf file is read.
- * All parsing structures and types are hidden in the interest of good
- * programming style and to make possible future data structure changes
- * without affecting the interface between this patch and the rest of the
- * server.  The only functions accessible externally are crule_parse,
- * crule_free, and crule_eval.  Prototypes for these functions can be
- * found in h.h.
- *
- * Please direct any connection rule or SmartRoute questions to Tonto on
- * IRC or by email to [EMAIL PROTECTED]
- *
- * For parser testing, defining CR_DEBUG generates a stand-alone parser
- * that takes rules from stdin and prints out memory allocation
- * information and the parsed rule.  This stand alone parser is ignorant
- * of the irc server and thus cannot do rule evaluation.  Do not define
- * this flag when compiling the server!  If you wish to generate the
- * test parser, compile from the ircd directory with a line similar to
- * cc -o parser -DCR_DEBUG crule.c
- *
- * The define CR_CHKCONF is provided to generate routines needed in
- * chkconf.  These consist of the parser, a different crule_parse that
- * prints errors to stderr, and crule_free (just for good style and to
- * more closely simulate the actual ircd environment).  crule_eval and
- * the rule functions are made empty functions as in the stand-alone
- * test parser.
- *
- * The production rules for the grammar are as follows ("rule" is the
- * starting production):
- *
- *   rule:
- *     orexpr END          END is end of input or :
- *   orexpr:
- *     andexpr
- *     andexpr || orexpr
- *   andexpr:
- *     primary
- *     primary && andexpr
- *  primary:
- *    function
- *    ! primary
- *    ( orexpr )
- *  function:
- *    word ( )             word is alphanumeric string, first character
- *    word ( arglist )       must be a letter
- *  arglist:
- *    word
- *    word , arglist
+ * This used to have a recursive descent parser and debugging helper
+ * code.  That all got ripped out and moved into the config parser.
  */
-#include "config.h"
 
+#include "config.h"
 #include "crule.h"
-#ifndef CR_DEBUG
-
-/* ircd functions and types we need */
 #include "client.h"
 #include "ircd.h"
 #include "ircd_alloc.h"
-#include "ircd_chattr.h"
-#include "ircd_log.h"
+#include "ircd_log.h" /* for assert() */
 #include "ircd_string.h"
+#include "ircd_snprintf.h"
+#include "list.h"
 #include "match.h"
 #include "s_bsd.h"
-#include "s_debug.h"
 #include "struct.h"
 
-#include <stdio.h>
-#include <stdlib.h>
-
-#else /* includes and defines to make the stand-alone test parser */
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#define BadPtr(x) (!(x) || (*(x) == '\0'))
-#define DupString(x,y) \
-        do { \
-          x = (char*) MyMalloc(strlen(y)+1); \
-        strcpy(x,y); \
-        } while(0)
-
-/* We don't care about collation discrepancies here, it seems.... */
-#define ircd_strcmp strcasecmp
-
-#endif
-
-#include <string.h>
-
-
-#if defined(CR_DEBUG) || defined(CR_CHKCONF)
-#undef MyMalloc
-#undef malloc
-#define MyMalloc malloc
-#undef MyFree
-#undef free
-#define MyFree free
-#endif
-
-/* some constants and shared data types */
-#define CR_MAXARGLEN 80         /**< Maximum arg length (must be > HOSTLEN) */
-#define CR_MAXARGS 3            /**< Maximum number of args for a rule */
-
-/*
- * Some symbols for easy reading
- */
-
-/** Input scanner tokens. */
-enum crule_token {
-  CR_UNKNOWN,    /**< Unknown token type. */
-  CR_END,        /**< End of input ('\\0' or ':'). */
-  CR_AND,        /**< Logical and operator (&&). */
-  CR_OR,         /**< Logical or operator (||). */
-  CR_NOT,        /**< Logical not operator (!). */
-  CR_OPENPAREN,  /**< Open parenthesis. */
-  CR_CLOSEPAREN, /**< Close parenthesis. */
-  CR_COMMA,      /**< Comma. */
-  CR_WORD        /**< Something that looks like a hostmask (alphanumerics, 
"*?.-"). */
-};
-
-/** Parser error codes. */
-enum crule_errcode {
-  CR_NOERR,      /**< No error. */
-  CR_UNEXPCTTOK, /**< Invalid token given context. */
-  CR_UNKNWTOK,   /**< Input did not form a valid token. */
-  CR_EXPCTAND,   /**< Did not see expected && operator. */
-  CR_EXPCTOR,    /**< Did not see expected || operator. */
-  CR_EXPCTPRIM,  /**< Expected a primitive (parentheses, ! or word). */
-  CR_EXPCTOPEN,  /**< Expected an open parenthesis after function name. */
-  CR_EXPCTCLOSE, /**< Expected a close parenthesis to match open parenthesis. 
*/
-  CR_UNKNWFUNC,  /**< Attempt to use an unknown function. */
-  CR_ARGMISMAT   /**< Wrong number of arguments to function. */
-};
-
-/*
- * Expression tree structure, function pointer, and tree pointer local!
- */
 /** Evaluation function for a connection rule. */
-typedef int (*crule_funcptr) (int, void **);
+typedef int (*crule_funcptr) (struct CRuleNode *);
 
 /** Node in a connection rule tree. */
 struct CRuleNode {
   crule_funcptr funcptr; /**< Evaluation function for this node. */
   int numargs;           /**< Number of arguments. */
-  void *arg[CR_MAXARGS]; /**< Array of arguments.  For operators, each arg
+  void *arg[1];          /**< Array of arguments.  For operators, each arg
                             is a tree element; for functions, each arg is
                             a string. */
 };
 
-/** Typedef to save typing effort. */
-typedef struct CRuleNode* CRuleNodePtr;
-
-/* local rule function prototypes */
-static int crule_connected(int, void *[]);
-static int crule_directcon(int, void *[]);
-static int crule_via(int, void *[]);
-static int crule_directop(int, void *[]);
-static int crule__andor(int, void *[]);
-static int crule__not(int, void *[]);
-
-/* local parsing function prototypes */
-static int crule_gettoken(int* token, const char** str);
-static void crule_getword(char*, int*, size_t, const char**);
-static int crule_parseandexpr(CRuleNodePtr*, int *, const char**);
-static int crule_parseorexpr(CRuleNodePtr*, int *, const char**);
-static int crule_parseprimary(CRuleNodePtr*, int *, const char**);
-static int crule_parsefunction(CRuleNodePtr*, int *, const char**);
-static int crule_parsearglist(CRuleNodePtr, int *, const char**);
-
-#if defined(CR_DEBUG) || defined(CR_CHKCONF)
-/*
- * Prototypes for the test parser; if not debugging,
- * these are defined in h.h
- */
-struct CRuleNode* crule_parse(const char*);
-void crule_free(struct CRuleNode**);
-#ifdef CR_DEBUG
-void print_tree(CRuleNodePtr);
-#endif
-#endif
-
-/** Error messages, indexed by the corresponding crule_errcode. */
-static char *crule_errstr[] = {
-  "Unknown error",              /* NOERR? - for completeness */
-  "Unexpected token",           /* UNEXPCTTOK */
-  "Unknown token",              /* UNKNWTOK */
-  "And expr expected",          /* EXPCTAND */
-  "Or expr expected",           /* EXPCTOR */
-  "Primary expected",           /* EXPCTPRIM */
-  "( expected",                 /* EXPCTOPEN */
-  ") expected",                 /* EXPCTCLOSE */
-  "Unknown function",           /* UNKNWFUNC */
-  "Argument mismatch"           /* ARGMISMAT */
-};
-
-/** Connection rule function table entry. */
-struct crule_funclistent {
-  char name[15];         /**< Function name. */
-  int reqnumargs;        /**< Required number of arguments. */
-  crule_funcptr funcptr; /**< Handler function. */
-};
+/** Create a CRuleNode.
+ * @param[in] funcptr Evaluation function for the node.
+ * @param[in] numargs Number of arguments.
+ * @param[in] ... Arguments as void pointers.
+ * @return Newly allocated CRuleNode.
+ */
+static struct CRuleNode *crule_make(crule_funcptr funcptr, int numargs, ...)
+{
+  struct CRuleNode *res;
+  va_list va;
+  int ii;
+
+  /* be sure we allocate at least sizeof(struct CRuleNode) */
+  if (numargs > 0)
+    res = MyMalloc(sizeof(*res) + sizeof(res->arg[0]) * (numargs - 1));
+  else
+    res = MyMalloc(sizeof(*res));
+  res->funcptr = funcptr;
+  res->numargs = numargs;
+  va_start(va, numargs);
+  for (ii = 0; ii < numargs; ++ii)
+    res->arg[ii] = va_arg(va, void*);
+  va_end(va);
+  return res;
+}
+
+/** Find any linked server with a name matching \a mask behind \a start.
+ * @param[in] mask Server name mask to search for.
+ * @param[in] start Server at which to start search.
+ * @return A pointer to a server matching \a mask, or NULL if none exist.
+ */
+static int crule_connected_from(const char *mask, struct Client *start)
+{
+  struct DLink *dl;
+
+  assert(cli_serv(start) != NULL);
+
+  for (dl = cli_serv(start)->down; dl; dl = dl->next)
+    if (match(mask, cli_name(dl->value.cptr))
+        || crule_connected_from(mask, dl->value.cptr))
+      return 1;
 
-/** Defined connection rules. */
-static struct crule_funclistent crule_funclist[] = {
-  /* maximum function name length is 14 chars */
-  {"connected", 1, crule_connected},
-  {"directcon", 1, crule_directcon},
-  {"via", 2, crule_via},
-  {"directop", 0, crule_directop},
-  {"", 0, NULL}                 /* this must be here to mark end of list */
-};
+  return 0;
+}
 
-/** Check whether any connected server matches crulearg[0].
- * @param[in] numargs Number of valid args in \a crulearg.
- * @param[in] crulearg Argument array.
+/** Check whether any connected server matches \a rule->arg[0].
+ * @param[in] rule The rule to evaluate.
  * @return Non-zero if the condition is true, zero if not.
  */
-static int crule_connected(int numargs, void *crulearg[])
+static int crule_connected(struct CRuleNode *rule)
 {
-#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
-  struct Client *acptr;
+  assert(rule->numargs == 1);
 
-  /* taken from m_links */
-  for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr))
-  {
-    if (!IsServer(acptr) && !IsMe(acptr))
-      continue;
-    if (match((char *)crulearg[0], cli_name(acptr)))
-      continue;
-    return (1);
-  }
-#endif
-  return (0);
+  return crule_connected_from(rule->arg[0], &me);
 }
 
-/** Check whether any directly connected server matches crulearg[0].
- * @param[in] numargs Number of valid args in \a crulearg.
- * @param[in] crulearg Argument array.
+/** Check whether any directly connected server matches \a rule->arg[0].
+ * @param[in] rule The rule to evaluate.
  * @return Non-zero if the condition is true, zero if not.
  */
-static int crule_directcon(int numargs, void *crulearg[])
+static int crule_directcon(struct CRuleNode *rule)
 {
-#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
-  int i;
-  struct Client *acptr;
+  struct DLink *dl;
 
-  /* adapted from m_trace and exit_one_client */
-  for (i = 0; i <= HighestFd; i++)
-  {
-    if (!(acptr = LocalClientArray[i]) || !IsServer(acptr))
-      continue;
-    if (match((char *)crulearg[0], cli_name(acptr)))
-      continue;
-    return (1);
-  }
-#endif
-  return (0);
+  assert(rule->numargs == 1);
+
+  for (dl = cli_serv(&me)->down; dl; dl = dl->next)
+    if (match(rule->arg[0], cli_name(dl->value.cptr)))
+      return 1;
+
+  return 0;
 }
 
-/** Check whether a connected server matching crulearg[1] is
- * connnected to me behind one matching crulearg[0].
- * @param[in] numargs Number of valid args in \a crulearg.
- * @param[in] crulearg Argument array.
+/** Check whether a connected server matching \a rule->arg[1] is
+ * connnected to me behind one matching \a rule->arg[0].
+ * @param[in] rule The rule to evaluate.
  * @return Non-zero if the condition is true, zero if not.
  */
-static int crule_via(int numargs, void *crulearg[])
+static int crule_via(struct CRuleNode *rule)
 {
-#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
-  struct Client *acptr;
+  struct DLink *dl;
 
-  /* adapted from m_links */
-  for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr))
-  {
-    if (!IsServer(acptr) && !IsMe(acptr))
-      continue;
-    if (match((char *)crulearg[1], cli_name(acptr)))
-      continue;
-    if (match((char *)crulearg[0], cli_name(cli_from(acptr))))
-      continue;
-    return (1);
-  }
-#endif
-  return (0);
+  assert(rule->numargs == 2);
+
+  for (dl = cli_serv(&me)->down; dl; dl = dl->next)
+    if (match(rule->arg[0], cli_name(dl->value.cptr))
+        && crule_connected_from(rule->arg[1], dl->value.cptr))
+      return 1;
+
+  return 0;
 }
 
 /** Check whether we have a local IRC operator.
- * @param[in] numargs Number of valid args in \a crulearg.
- * @param[in] crulearg Argument array.
+ * @param[in] rule The rule to evaluate.
  * @return Non-zero if the condition is true, zero if not.
  */
-static int crule_directop(int numargs, void *crulearg[])
+static int crule_directop(struct CRuleNode *rule)
 {
-#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
-  int i;
   struct Client *acptr;
+  int i;
+
+  assert(rule->numargs == 0);
 
-  /* adapted from m_trace */
   for (i = 0; i <= HighestFd; i++)
-  {
-    if (!(acptr = LocalClientArray[i]) || !IsAnOper(acptr))
-      continue;
-    return (1);
-  }
-#endif
-  return (0);
+    if ((acptr = LocalClientArray[i]) && IsAnOper(acptr))
+      return 1;
+
+  return 0;
 }
 
-/** Evaluate a connection rule.
- * @param[in] rule Rule to evalute.
- * @return Non-zero if the rule allows the connection, zero otherwise.
+/** Perform an 'and' test on \a rule->arg[0] and \a rule->arg[1].
+ * @param[in] rule Rule to evaluate.
+ * @return Non-zero if the condition is true, zero if not.
  */
-int crule_eval(struct CRuleNode* rule)
+static int crule_and(struct CRuleNode *rule)
 {
-  return (rule->funcptr(rule->numargs, rule->arg));
+  assert(rule->numargs == 2);
+  return crule_eval(rule->arg[0]) && crule_eval(rule->arg[1]);
 }
 
-/** Perform an and-or-or test on crulearg[0] and crulearg[1].
- * If crulearg[2] is non-NULL, it means do OR; if it is NULL, do AND.
- * @param[in] numargs Number of valid args in \a crulearg.
- * @param[in] crulearg Argument array.
+/** Perform an 'or' test on \a rule->arg[0] and \a rule->arg[1].
+ * @param[in] rule Rule to evaluate.
  * @return Non-zero if the condition is true, zero if not.
  */
-static int crule__andor(int numargs, void *crulearg[])
+static int crule_or(struct CRuleNode *rule)
 {
-  int result1;
-
-  result1 = crule_eval(crulearg[0]);
-  if (crulearg[2])              /* or */
-    return (result1 || crule_eval(crulearg[1]));
-  else
-    return (result1 && crule_eval(crulearg[1]));
+  assert(rule->numargs == 2);
+  return crule_eval(rule->arg[0]) || crule_eval(rule->arg[1]);
 }
 
-/** Logically invert the result of crulearg[0].
- * @param[in] numargs Number of valid args in \a crulearg.
- * @param[in] crulearg Argument array.
+/** Invert the logical sense of \a rule->arg[0].
+ * @param[in] rule Rule to evaluate.
  * @return Non-zero if the condition is true, zero if not.
  */
-static int crule__not(int numargs, void *crulearg[])
+static int crule_not(struct CRuleNode *rule)
 {
-  return (!crule_eval(crulearg[0]));
-}
-
-/** Scan an input token from \a ruleptr.
- * @param[out] next_tokp Receives type of next token.
- * @param[in,out] ruleptr Next readable character from input.
- * @return Either CR_UNKNWTOK if the input was unrecognizable, else CR_NOERR.
- */
-static int crule_gettoken(int* next_tokp, const char** ruleptr)
-{
-  char pending = '\0';
-
-  *next_tokp = CR_UNKNOWN;
-  while (*next_tokp == CR_UNKNOWN)
-    switch (*(*ruleptr)++)
-    {
-      case ' ':
-      case '\t':
-        break;
-      case '&':
-        if (pending == '\0')
-          pending = '&';
-        else if (pending == '&')
-          *next_tokp = CR_AND;
-        else
-          return (CR_UNKNWTOK);
-        break;
-      case '|':
-        if (pending == '\0')
-          pending = '|';
-        else if (pending == '|')
-          *next_tokp = CR_OR;
-        else
-          return (CR_UNKNWTOK);
-        break;
-      case '!':
-        *next_tokp = CR_NOT;
-        break;
-      case '(':
-        *next_tokp = CR_OPENPAREN;
-        break;
-      case ')':
-        *next_tokp = CR_CLOSEPAREN;
-        break;
-      case ',':
-        *next_tokp = CR_COMMA;
-        break;
-      case '\0':
-        (*ruleptr)--;
-        *next_tokp = CR_END;
-        break;
-      case ':':
-        *next_tokp = CR_END;
-        break;
-      default:
-        if ((IsAlpha(*(--(*ruleptr)))) || (**ruleptr == '*') ||
-            (**ruleptr == '?') || (**ruleptr == '.') || (**ruleptr == '-'))
-          *next_tokp = CR_WORD;
-        else
-          return (CR_UNKNWTOK);
-        break;
-    }
-  return CR_NOERR;
-}
-
-/** Scan a word from \a ruleptr.
- * @param[out] word Output buffer.
- * @param[out] wordlenp Length of word written to \a word (not including 
terminating NUL).
- * @param[in] maxlen Maximum number of bytes writable to \a word.
- * @param[in,out] ruleptr Next readable character from input.
- */
-static void crule_getword(char* word, int* wordlenp, size_t maxlen, const 
char** ruleptr)
-{
-  char *word_ptr;
-
-  word_ptr = word;
-  while ((size_t)(word_ptr - word) < maxlen
-      && (IsAlnum(**ruleptr)
-      || **ruleptr == '*' || **ruleptr == '?'
-      || **ruleptr == '.' || **ruleptr == '-'))
-    *word_ptr++ = *(*ruleptr)++;
-  *word_ptr = '\0';
-  *wordlenp = word_ptr - word;
-}
-
-/** Parse an entire rule.
- * @param[in] rule Text form of rule.
- * @return CRuleNode for rule, or NULL if there was a parse error.
- */
-struct CRuleNode* crule_parse(const char *rule)
-{
-  const char* ruleptr = rule;
-  int next_tok;
-  struct CRuleNode* ruleroot = 0;
-  int errcode = CR_NOERR;
-
-  if ((errcode = crule_gettoken(&next_tok, &ruleptr)) == CR_NOERR) {
-    if ((errcode = crule_parseorexpr(&ruleroot, &next_tok, &ruleptr)) == 
CR_NOERR) {
-      if (ruleroot != NULL) {
-        if (next_tok == CR_END)
-          return (ruleroot);
-        else
-          errcode = CR_UNEXPCTTOK;
-      }
-      else
-        errcode = CR_EXPCTOR;
-    }
-  }
-  if (ruleroot != NULL)
-    crule_free(&ruleroot);
-#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
-  log_write(LS_CONFIG, L_WARNING, 0, "%s in rule: %s", 
-                 crule_errstr[errcode], rule);
-#else
-  fprintf(stderr, "%s in rule: %s\n", crule_errstr[errcode], rule);
-#endif
-  return 0;
-}
-
-/** Parse an or expression.
- * @param[out] orrootp Receives parsed node.
- * @param[in,out] next_tokp Next input token type.
- * @param[in,out] ruleptr Next input character.
- * @return A crule_errcode value.
- */
-static int crule_parseorexpr(CRuleNodePtr * orrootp, int *next_tokp, const 
char** ruleptr)
-{
-  int errcode = CR_NOERR;
-  CRuleNodePtr andexpr;
-  CRuleNodePtr orptr;
-
-  *orrootp = NULL;
-  while (errcode == CR_NOERR)
-  {
-    errcode = crule_parseandexpr(&andexpr, next_tokp, ruleptr);
-    if ((errcode == CR_NOERR) && (*next_tokp == CR_OR))
-    {
-      orptr = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
-#ifdef CR_DEBUG
-      fprintf(stderr, "allocating or element at %ld\n", orptr);
-#endif
-      orptr->funcptr = crule__andor;
-      orptr->numargs = 3;
-      orptr->arg[2] = (void *)1;
-      if (*orrootp != NULL)
-      {
-        (*orrootp)->arg[1] = andexpr;
-        orptr->arg[0] = *orrootp;
-      }
-      else
-        orptr->arg[0] = andexpr;
-      *orrootp = orptr;
-    }
-    else
-    {
-      if (*orrootp != NULL)
-      {
-        if (andexpr != NULL)
-        {
-          (*orrootp)->arg[1] = andexpr;
-          return (errcode);
-        }
-        else
-        {
-          (*orrootp)->arg[1] = NULL;    /* so free doesn't seg fault */
-          return (CR_EXPCTAND);
-        }
-      }
-      else
-      {
-        *orrootp = andexpr;
-        return (errcode);
-      }
-    }
-    if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
-      return (errcode);
-  }
-  return (errcode);
+  assert(rule->numargs == 1);
+  return !crule_eval(rule->arg[0]);
 }
 
-/** Parse an and expression.
- * @param[out] androotp Receives parsed node.
- * @param[in,out] next_tokp Next input token type.
- * @param[in,out] ruleptr Next input character.
- * @return A crule_errcode value.
+/** Evaluate a connection rule.
+ * @param[in] rule Rule to evalute.
+ * @return Non-zero if the rule allows the connection, zero otherwise.
  */
-static int crule_parseandexpr(CRuleNodePtr * androotp, int *next_tokp, const 
char** ruleptr)
+int crule_eval(struct CRuleNode* rule)
 {
-  int errcode = CR_NOERR;
-  CRuleNodePtr primary;
-  CRuleNodePtr andptr;
-
-  *androotp = NULL;
-  while (errcode == CR_NOERR)
-  {
-    errcode = crule_parseprimary(&primary, next_tokp, ruleptr);
-    if ((errcode == CR_NOERR) && (*next_tokp == CR_AND))
-    {
-      andptr = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
-#ifdef CR_DEBUG
-      fprintf(stderr, "allocating and element at %ld\n", andptr);
-#endif
-      andptr->funcptr = crule__andor;
-      andptr->numargs = 3;
-      andptr->arg[2] = (void *)0;
-      if (*androotp != NULL)
-      {
-        (*androotp)->arg[1] = primary;
-        andptr->arg[0] = *androotp;
-      }
-      else
-        andptr->arg[0] = primary;
-      *androotp = andptr;
-    }
-    else
-    {
-      if (*androotp != NULL)
-      {
-        if (primary != NULL)
-        {
-          (*androotp)->arg[1] = primary;
-          return (errcode);
-        }
-        else
-        {
-          (*androotp)->arg[1] = NULL;   /* so free doesn't seg fault */
-          return (CR_EXPCTPRIM);
-        }
-      }
-      else
-      {
-        *androotp = primary;
-        return (errcode);
-      }
-    }
-    if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
-      return (errcode);
-  }
-  return (errcode);
+  return (rule->funcptr(rule));
 }
 
-/** Parse a primary expression.
- * @param[out] primrootp Receives parsed node.
- * @param[in,out] next_tokp Next input token type.
- * @param[in,out] ruleptr Next input character.
- * @return A crule_errcode value.
+/** Free a connection rule and all its children.
+ * @param[in] elem Pointer to element to free.
  */
-static int crule_parseprimary(CRuleNodePtr* primrootp, int *next_tokp, const 
char** ruleptr)
+void crule_free(struct CRuleNode* elem)
 {
-  CRuleNodePtr *insertionp;
-  int errcode = CR_NOERR;
-
-  *primrootp = NULL;
-  insertionp = primrootp;
-  while (errcode == CR_NOERR)
+  if (!elem)
+      return;
+  if (elem->funcptr == crule_not)
   {
-    switch (*next_tokp)
-    {
-      case CR_OPENPAREN:
-        if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
-          break;
-        if ((errcode = crule_parseorexpr(insertionp, next_tokp, ruleptr)) != 
CR_NOERR)
-          break;
-        if (*insertionp == NULL)
-        {
-          errcode = CR_EXPCTAND;
-          break;
-        }
-        if (*next_tokp != CR_CLOSEPAREN)
-        {
-          errcode = CR_EXPCTCLOSE;
-          break;
-        }
-        errcode = crule_gettoken(next_tokp, ruleptr);
-        break;
-      case CR_NOT:
-        *insertionp = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
-#ifdef CR_DEBUG
-        fprintf(stderr, "allocating primary element at %ld\n", *insertionp);
-#endif
-        (*insertionp)->funcptr = crule__not;
-        (*insertionp)->numargs = 1;
-        (*insertionp)->arg[0] = NULL;
-        insertionp = (CRuleNodePtr *) & ((*insertionp)->arg[0]);
-        if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
-          break;
-        continue;
-      case CR_WORD:
-        errcode = crule_parsefunction(insertionp, next_tokp, ruleptr);
-        break;
-      default:
-        if (*primrootp == NULL)
-          errcode = CR_NOERR;
-        else
-          errcode = CR_EXPCTPRIM;
-        break;
-    }
-    return (errcode);
+    crule_free(elem->arg[0]);
   }
-  return (errcode);
-}
-
-/** Parse a function call.
- * @param[out] funcrootp Receives parsed node.
- * @param[in,out] next_tokp Next input token type.
- * @param[in,out] ruleptr Next input character.
- * @return A crule_errcode value.
- */
-static int crule_parsefunction(CRuleNodePtr* funcrootp, int* next_tokp, const 
char** ruleptr)
-{
-  int errcode = CR_NOERR;
-  char funcname[CR_MAXARGLEN];
-  int namelen;
-  int funcnum;
-
-  *funcrootp = NULL;
-  crule_getword(funcname, &namelen, CR_MAXARGLEN - 1, ruleptr);
-  if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
-    return (errcode);
-  if (*next_tokp == CR_OPENPAREN)
+  else if (elem->funcptr == crule_and || elem->funcptr == crule_or)
   {
-    for (funcnum = 0;; funcnum++)
-    {
-      if (0 == ircd_strcmp(crule_funclist[funcnum].name, funcname))
-        break;
-      if (crule_funclist[funcnum].name[0] == '\0')
-        return (CR_UNKNWFUNC);
-    }
-    if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
-      return (errcode);
-    *funcrootp = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
-#ifdef CR_DEBUG
-    fprintf(stderr, "allocating function element at %ld\n", *funcrootp);
-#endif
-    (*funcrootp)->funcptr = NULL;       /* for freeing aborted trees */
-    if ((errcode =
-        crule_parsearglist(*funcrootp, next_tokp, ruleptr)) != CR_NOERR)
-      return (errcode);
-    if (*next_tokp != CR_CLOSEPAREN)
-      return (CR_EXPCTCLOSE);
-    if ((crule_funclist[funcnum].reqnumargs != (*funcrootp)->numargs) &&
-        (crule_funclist[funcnum].reqnumargs != -1))
-      return (CR_ARGMISMAT);
-    if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
-      return (errcode);
-    (*funcrootp)->funcptr = crule_funclist[funcnum].funcptr;
-    return (CR_NOERR);
+    crule_free(elem->arg[0]);
+    crule_free(elem->arg[1]);
   }
   else
-    return (CR_EXPCTOPEN);
-}
-
-/** Parse the argument list to a CRuleNode.
- * @param[in,out] argrootp Node whos argument list is being populated.
- * @param[in,out] next_tokp Next input token type.
- * @param[in,out] ruleptr Next input character.
- * @return A crule_errcode value.
- */
-static int crule_parsearglist(CRuleNodePtr argrootp, int *next_tokp, const 
char** ruleptr)
-{
-  int errcode = CR_NOERR;
-  char *argelemp = NULL;
-  char currarg[CR_MAXARGLEN];
-  int arglen = 0;
-  char word[CR_MAXARGLEN];
-  int wordlen = 0;
-
-  argrootp->numargs = 0;
-  currarg[0] = '\0';
-  while (errcode == CR_NOERR)
   {
-    switch (*next_tokp)
-    {
-      case CR_WORD:
-        crule_getword(word, &wordlen, CR_MAXARGLEN - 1, ruleptr);
-        if (currarg[0] != '\0')
-        {
-          if ((arglen + wordlen) < (CR_MAXARGLEN - 1))
-          {
-            strcat(currarg, " ");
-            strcat(currarg, word);
-            arglen += wordlen + 1;
-          }
-        }
-        else
-        {
-          strcpy(currarg, word);
-          arglen = wordlen;
-        }
-        errcode = crule_gettoken(next_tokp, ruleptr);
-        break;
-      default:
-#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
-        collapse(currarg);
-#endif
-        if (!BadPtr(currarg))
-        {
-          DupString(argelemp, currarg);
-          argrootp->arg[argrootp->numargs++] = (void *)argelemp;
-        }
-        if (*next_tokp != CR_COMMA)
-          return (CR_NOERR);
-        currarg[0] = '\0';
-        errcode = crule_gettoken(next_tokp, ruleptr);
-        break;
-    }
+    while (elem->numargs)
+      MyFree(elem->arg[--elem->numargs]);
   }
-  return (errcode);
+  MyFree(elem);
 }
 
-/*
- * This function is recursive..  I wish I knew a nonrecursive way but
- * I don't.  Anyway, recursion is fun..  :)
- * DO NOT CALL THIS FUNCTION WITH A POINTER TO A NULL POINTER
- * (i.e.: If *elem is NULL, you're doing it wrong - seg fault)
- */
-/** Free a connection rule and all its children.
- * @param[in,out] elem Pointer to pointer to element to free.  MUST NOT BE 
NULL.
- */
-void crule_free(struct CRuleNode** elem)
+struct CRuleNode *
+crule_make_and(struct CRuleNode *left, struct CRuleNode *right)
 {
-  int arg, numargs;
+  return crule_make(crule_and, 2, left, right);
+}
 
-  if ((*(elem))->funcptr == crule__not)
-  {
-    /* type conversions and ()'s are fun! ;)  here have an aspirin.. */
-    if ((*(elem))->arg[0] != NULL)
-      crule_free((struct CRuleNode**) &((*(elem))->arg[0]));
-  }
-  else if ((*(elem))->funcptr == crule__andor)
-  {
-    crule_free((struct CRuleNode**) &((*(elem))->arg[0]));
-    if ((*(elem))->arg[1] != NULL)
-      crule_free((struct CRuleNode**) &((*(elem))->arg[1]));
-  }
-  else
-  {
-    numargs = (*(elem))->numargs;
-    for (arg = 0; arg < numargs; arg++)
-      MyFree((*(elem))->arg[arg]);
-  }
-#ifdef CR_DEBUG
-  fprintf(stderr, "freeing element at %ld\n", *elem);
-#endif
-  MyFree(*elem);
-  *elem = 0;
+struct CRuleNode *
+crule_make_or(struct CRuleNode *left, struct CRuleNode *right)
+{
+  return crule_make(crule_or, 2, left, right);
 }
 
-#ifdef CR_DEBUG
-/** Display a connection rule as text.
- * @param[in] printelem Connection rule to display.
- */
-static void print_tree(CRuleNodePtr printelem)
+struct CRuleNode *
+crule_make_not(struct CRuleNode *arg)
 {
-  int funcnum, arg;
+  return crule_make(crule_not, 1, arg);
+}
 
-  if (printelem->funcptr == crule__not)
-  {
-    printf("!( ");
-    print_tree((CRuleNodePtr) printelem->arg[0]);
-    printf(") ");
-  }
-  else if (printelem->funcptr == crule__andor)
-  {
-    printf("( ");
-    print_tree((CRuleNodePtr) printelem->arg[0]);
-    if (printelem->arg[2])
-      printf("|| ");
-    else
-      printf("&& ");
-    print_tree((CRuleNodePtr) printelem->arg[1]);
-    printf(") ");
-  }
-  else
-  {
-    for (funcnum = 0;; funcnum++)
-    {
-      if (printelem->funcptr == crule_funclist[funcnum].funcptr)
-        break;
-      if (crule_funclist[funcnum].funcptr == NULL)
-        MyCoreDump;
-    }
-    printf("%s(", crule_funclist[funcnum].name);
-    for (arg = 0; arg < printelem->numargs; arg++)
-    {
-      if (arg != 0)
-        printf(",");
-      printf("%s", (char *)printelem->arg[arg]);
-    }
-    printf(") ");
-  }
+struct CRuleNode *
+crule_make_connected(char *arg)
+{
+  return crule_make(crule_connected, 1, arg);
 }
 
-#endif
+struct CRuleNode *
+crule_make_directcon(char *arg)
+{
+  return crule_make(crule_directcon, 1, arg);
+}
 
-#ifdef CR_DEBUG
-/** Read connection rules from stdin and display parsed forms as text.
- * @return Zero.
- */
-int main(void)
+struct CRuleNode *
+crule_make_via(char *neighbor, char *server)
 {
-  char indata[256];
-  CRuleNode* rule;
+  return crule_make(crule_via, 2, neighbor, server);
+}
 
-  printf("rule: ");
-  while (fgets(indata, 256, stdin) != NULL)
-  {
-    indata[strlen(indata) - 1] = '\0';  /* lose the newline */
-    if ((rule = crule_parse(indata)) != NULL)
-    {
-      printf("equivalent rule: ");
-      print_tree((CRuleNodePtr) rule);
-      printf("\n");
-      crule_free(&rule);
-    }
-    printf("\nrule: ");
-  }
-  printf("\n");
+struct CRuleNode *
+crule_make_directop(void)
+{
+  return crule_make(crule_directop, 0);
+}
 
-  return 0;
+static int
+crule_cat(struct CRuleNode *rule, char *buf, size_t remain)
+{
+  if (rule->funcptr == crule_connected) {
+    assert(rule->numargs == 1);
+    return ircd_snprintf(NULL, buf, remain, "connected(\"%s\")", rule->arg[0]);
+  } else if (rule->funcptr == crule_directcon) {
+    assert(rule->numargs == 1);
+    return ircd_snprintf(NULL, buf, remain, "directcon(\"%s\")", rule->arg[0]);
+  } else if (rule->funcptr == crule_via) {
+    assert(rule->numargs == 2);
+    return ircd_snprintf(NULL, buf, remain, "via(\"%s\", \"%s\")",
+                         rule->arg[0], rule->arg[1]);
+  } else if (rule->funcptr == crule_directop) {
+    assert(rule->numargs == 0);
+    return ircd_snprintf(NULL, buf, remain, "directop()", rule->arg[0]);
+  } else if (rule->funcptr == crule_and || rule->funcptr == crule_or) {
+    const char *op = (rule->funcptr == crule_and) ? " && " : " || ";
+    size_t used = 0;
+
+    assert(rule->numargs == 2);
+    if (remain > used++)
+      buf[used - 1] = '(';
+    used += crule_cat(rule->arg[0], buf + used, (remain < used) ? 0 : remain - 
used);
+    if (remain > used)
+      used += ircd_snprintf(NULL, buf + used, remain - used, op);
+    used += crule_cat(rule->arg[1], buf + used, (remain < used) ? 0 : remain - 
used);
+    if (remain > used++)
+      buf[used - 1] = ')';
+    return used;
+  } else if (rule->funcptr == crule_not) {
+    if (remain)
+      buf[0] = '!';
+    return 1 + crule_cat(rule->arg[0], buf + 1, (remain < 1) ? 0 : remain - 1);
+  } else
+    return ircd_snprintf(NULL, buf, remain, "???");
 }
 
-#endif
+char *crule_text(struct CRuleNode *rule)
+{
+  char buf[BUFSIZE], *res;
+  buf[sizeof(buf) - 1] = '\0';
+  crule_cat(rule, buf, sizeof(buf) - 1);
+  return DupString(res, buf);
+}


--- orig/ircd/ircd_lexer.l
+++ mod/ircd/ircd_lexer.l
@@ -73,6 +73,8 @@
 #.*            ;
 <<EOF>>                { yypop_buffer_state(); if (YY_CURRENT_BUFFER) return 
TEOF; else yyterminate(); }
 
+\&\&            return LOGICAL_AND;
+\|\|            return LOGICAL_OR;
 ADMIN          return ADMIN;
 ADMINISTRATOR  return ADMIN;
 APASS_OPMODE   return TPRIV_APASS_OPMODE;
@@ -86,6 +88,7 @@
 CLASS          return CLASS;
 CLIENT         return CLIENT;
 CONNECT                return CONNECT;
+CONNECTED       return CONNECTED;
 CONNECTFREQ    return CONNECTFREQ;
 CONTACT                return CONTACT;
 CRULE          return CRULE;
@@ -94,6 +97,8 @@
 DEOP_LCHAN     return TPRIV_DEOP_LCHAN;
 DESCRIPTION    return DESCRIPTION;
 DIE            return TPRIV_DIE;
+DIRECTCON       return DIRECTCON;
+DIRECTOP        return DIRECTOP;
 DISPLAY                return TPRIV_DISPLAY;
 FAST           return FAST;
 FEATURES       return FEATURES;
@@ -175,6 +180,7 @@
 USERNAME       return USERNAME;
 UWORLD         return UWORLD;
 VHOST          return VHOST;
+VIA             return VIA;
 WALK_LCHAN     return TPRIV_WALK_LCHAN;
 WEEKS          return WEEKS;
 WIDE_GLINE     return TPRIV_WIDE_GLINE;


--- orig/ircd/ircd_parser.y
+++ mod/ircd/ircd_parser.y
@@ -207,6 +207,8 @@
 %token LINESYNC
 %token FROM
 %token TEOF
+%token LOGICAL_AND LOGICAL_OR
+%token CONNECTED DIRECTCON VIA DIRECTOP
 /* and now a lot of privileges... */
 %token TPRIV_CHAN_LIMIT TPRIV_MODE_LCHAN TPRIV_DEOP_LCHAN TPRIV_WALK_LCHAN
 %token TPRIV_LOCAL_KILL TPRIV_REHASH TPRIV_RESTART TPRIV_DIE
@@ -221,10 +223,17 @@
 %type <num> timespec timefactor factoredtimes factoredtime
 %type <num> expr yesorno privtype
 %type <num> blocklimit blocktypes blocktype
+%type <num> optall
+%type <crule> crule_expr
+%left LOGICAL_OR
+%left LOGICAL_AND
 %left '+' '-'
 %left '*' '/'
+%nonassoc '!'
+%nonassoc '(' ')'
 
 %union{
+ struct CRuleNode *crule;
  char *text;
  int num;
 }
@@ -946,62 +955,33 @@
  dconf->message = $3;
 };
 
-cruleblock: CRULE
+cruleblock: CRULE optall QSTRING optall crule_expr ';'
 {
-  tconn = CRULE_AUTO;
-} '{' cruleitems '}' ';'
-{
-  struct CRuleNode *node = NULL;
-  if (!permitted(BLOCK_CRULE, 1))
-    ;
-  else if (host == NULL)
-    parse_error("Missing host in crule block");
-  else if (pass == NULL)
-    parse_error("Missing rule in crule block");
-  else if ((node = crule_parse(pass)) == NULL)
-    parse_error("Invalid rule '%s' in crule block", pass);
-  else
+  if (permitted(BLOCK_CRULE, 1) && $5)
   {
     struct CRuleConf *p = (struct CRuleConf*) MyMalloc(sizeof(*p));
-    p->hostmask = host;
-    p->rule = pass;
-    p->type = tconn;
-    p->node = node;
+    p->hostmask = collapse($3);
+    p->rule = crule_text($5);
+    p->type = ($2 || $4) ? CRULE_ALL : CRULE_AUTO;
+    p->node = $5;
     p->next = cruleConfList;
     cruleConfList = p;
   }
-  if (!node)
-  {
-    MyFree(host);
-    MyFree(pass);
-  }
-  host = pass = NULL;
-  tconn = 0;
 };
 
-cruleitems: cruleitem cruleitems | cruleitem;
-cruleitem: cruleserver | crulerule | cruleall;
+optall: { $$ = 0; };
+  | ALL { $$ = 1; };
 
-cruleserver: SERVER '=' QSTRING ';'
-{
-  MyFree(host);
-  collapse($3);
-  host = $3;
-};
-
-crulerule: RULE '=' QSTRING ';'
-{
- MyFree(pass);
- pass = $3;
-};
-
-cruleall: ALL '=' YES ';'
-{
- tconn = CRULE_ALL;
-} | ALL '=' NO ';'
-{
- tconn = CRULE_AUTO;
-};
+crule_expr:
+    '(' crule_expr ')' { $$ = $2; }
+  | crule_expr LOGICAL_AND crule_expr { $$ = crule_make_and($1, $3); }
+  | crule_expr LOGICAL_OR crule_expr { $$ = crule_make_or($1, $3); }
+  | '!' crule_expr { $$ = crule_make_not($2); }
+  | CONNECTED '(' QSTRING ')' { $$ = crule_make_connected($3); }
+  | DIRECTCON '(' QSTRING ')' { $$ = crule_make_directcon($3); }
+  | VIA '(' QSTRING ',' QSTRING ')' { $$ = crule_make_via($3, $5); }
+  | DIRECTOP '(' ')' { $$ = crule_make_directop(); }
+  ;
 
 motdblock: MOTD '{' motditems '}' ';'
 {


--- orig/ircd/s_conf.c
+++ mod/ircd/s_conf.c
@@ -751,7 +751,7 @@
 
   for ( ; p; p = next) {
     next = p->next;
-    crule_free(&p->node);
+    crule_free(p->node);
     MyFree(p->hostmask);
     MyFree(p->rule);
     MyFree(p);
_______________________________________________
Coder-com mailing list
Coder-com@undernet.org
http://undernet.sbg.org/mailman/listinfo/coder-com

Reply via email to