-/*
- * SmartRoute phase 1
- * connection rule patch
+/**
+ * @file
+ * @brief Connection rule parser and checker
+ * @version $Id$
+ *
* by Tony Vencill (Tonto on IRC) <vencill@bga.com>
*
- * The majority of this file is a recusive descent parser used to convert
+ * 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
* the rule functions are made empty functions as in the stand-alone
* test parser.
*
- * $Id$
+ * 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
*/
+#include "config.h"
+
#include "crule.h"
#ifndef CR_DEBUG
strcpy(x,y); \
} while(0)
-/* We don't care about collation discrepacies here, it seems.... */
+/* We don't care about collation discrepancies here, it seems.... */
#define ircd_strcmp strcasecmp
#endif
#endif
/* some constants and shared data types */
-#define CR_MAXARGLEN 80 /* why 80? why not? it's > hostname lengths */
-#define CR_MAXARGS 3 /* There's a better way to do this,
- but not now. */
+#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, CR_END, CR_AND, CR_OR, CR_NOT, CR_OPENPAREN, CR_CLOSEPAREN,
- CR_COMMA, CR_WORD
+ 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, CR_UNEXPCTTOK, CR_UNKNWTOK, CR_EXPCTAND, CR_EXPCTOR,
- CR_EXPCTPRIM, CR_EXPCTOPEN, CR_EXPCTCLOSE, CR_UNKNWFUNC, CR_ARGMISMAT
+ 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 **);
+/** Node in a connection rule tree. */
struct CRuleNode {
- crule_funcptr funcptr;
- int numargs;
- void *arg[CR_MAXARGS]; /* For operators arg points to a tree element;
- for functions arg points to a char string. */
+ crule_funcptr funcptr; /**< Evaluation function for this node. */
+ int numargs; /**< Number of arguments. */
+ void *arg[CR_MAXARGS]; /**< 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 **);
+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);
#endif
#endif
-/* error messages */
+/** Error messages, indexed by the corresponding crule_errcode. */
char *crule_errstr[] = {
"Unknown error", /* NOERR? - for completeness */
"Unexpected token", /* UNEXPCTTOK */
"Argument mismatch" /* ARGMISMAT */
};
-/* function table - null terminated */
+/** Connection rule function table entry. */
struct crule_funclistent {
- char name[15]; /* MAXIMUM FUNCTION NAME LENGTH IS 14 CHARS!! */
- int reqnumargs;
- crule_funcptr funcptr;
+ char name[15]; /**< Function name. */
+ int reqnumargs; /**< Required number of arguments. */
+ crule_funcptr funcptr; /**< Handler function. */
};
+/** Defined connection rules. */
struct crule_funclistent crule_funclist[] = {
/* maximum function name length is 14 chars */
{"connected", 1, crule_connected},
{"", 0, NULL} /* this must be here to mark end of list */
};
-#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
+/** Check whether any connected server matches crulearg[0].
+ * @param[in] numargs Number of valid args in \a crulearg.
+ * @param[in] crulearg Argument array.
+ * @return Non-zero if the condition is true, zero if not.
+ */
static int crule_connected(int numargs, void *crulearg[])
{
+#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
struct Client *acptr;
/* taken from m_links */
- for (acptr = GlobalClientList; acptr; acptr = acptr->next)
+ for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr))
{
if (!IsServer(acptr) && !IsMe(acptr))
continue;
continue;
return (1);
}
+#endif
return (0);
}
-#else
-static int crule_connected(int numargs, void **crulearg)
-{
- return (0);
-}
-#endif
-#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
+/** Check whether any directly connected server matches crulearg[0].
+ * @param[in] numargs Number of valid args in \a crulearg.
+ * @param[in] crulearg Argument array.
+ * @return Non-zero if the condition is true, zero if not.
+ */
static int crule_directcon(int numargs, void *crulearg[])
{
+#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
int i;
struct Client *acptr;
continue;
return (1);
}
+#endif
return (0);
}
-#else
-static int crule_directcon(int numargs, void **crulearg)
-{
- return (0);
-}
-#endif
-#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
+/** 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.
+ * @return Non-zero if the condition is true, zero if not.
+ */
static int crule_via(int numargs, void *crulearg[])
{
+#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
struct Client *acptr;
/* adapted from m_links */
- for (acptr = GlobalClientList; acptr; acptr = acptr->next)
+ 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(LocalClientArray[(cli_from(acptr))->fd])))
+ if (match((char *)crulearg[0], cli_name(cli_from(acptr))))
continue;
return (1);
}
+#endif
return (0);
}
-#else
-static int crule_via(int numargs, void **crulearg)
-{
- return (0);
-}
-#endif
-static int crule_directop(int numargs, void **crulearg)
+/** Check whether we have a local IRC operator.
+ * @param[in] numargs Number of valid args in \a crulearg.
+ * @param[in] crulearg Argument array.
+ * @return Non-zero if the condition is true, zero if not.
+ */
+static int crule_directop(int numargs, void *crulearg[])
{
#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
int i;
return (0);
}
+/** Evaluate a connection rule.
+ * @param[in] rule Rule to evalute.
+ * @return Non-zero if the rule allows the connection, zero otherwise.
+ */
+int crule_eval(struct CRuleNode* rule)
+{
+ return (rule->funcptr(rule->numargs, rule->arg));
+}
+
+/** 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.
+ * @return Non-zero if the condition is true, zero if not.
+ */
static int crule__andor(int numargs, void *crulearg[])
{
int result1;
- result1 = ((CRuleNodePtr) crulearg[0])->funcptr
- (((CRuleNodePtr) crulearg[0])->numargs,
- ((CRuleNodePtr) crulearg[0])->arg);
+ result1 = crule_eval(crulearg[0]);
if (crulearg[2]) /* or */
- return (result1 ||
- ((CRuleNodePtr) crulearg[1])->funcptr
- (((CRuleNodePtr) crulearg[1])->numargs,
- ((CRuleNodePtr) crulearg[1])->arg));
+ return (result1 || crule_eval(crulearg[1]));
else
- return (result1 &&
- ((CRuleNodePtr) crulearg[1])->funcptr
- (((CRuleNodePtr) crulearg[1])->numargs,
- ((CRuleNodePtr) crulearg[1])->arg));
+ return (result1 && crule_eval(crulearg[1]));
}
+/** Logically invert the result of crulearg[0].
+ * @param[in] numargs Number of valid args in \a crulearg.
+ * @param[in] crulearg Argument array.
+ * @return Non-zero if the condition is true, zero if not.
+ */
static int crule__not(int numargs, void *crulearg[])
{
- return (!((CRuleNodePtr) crulearg[0])->funcptr
- (((CRuleNodePtr) crulearg[0])->numargs,
- ((CRuleNodePtr) crulearg[0])->arg));
-}
-
-#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
-int crule_eval(struct CRuleNode* rule)
-{
- return (rule->funcptr(rule->numargs, rule->arg));
+ return (!crule_eval(crulearg[0]));
}
-#endif
+/** 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';
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;
*wordlenp = word_ptr - word;
}
-/*
- * Grammar
- * 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
+/** 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)
{
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;
return (errcode);
}
+/** 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.
+ */
static int crule_parseandexpr(CRuleNodePtr * androotp, int *next_tokp, const char** ruleptr)
{
int errcode = CR_NOERR;
return (errcode);
}
+/** 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.
+ */
static int crule_parseprimary(CRuleNodePtr* primrootp, int *next_tokp, const char** ruleptr)
{
CRuleNodePtr *insertionp;
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;
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;
/*
* This function is recursive.. I wish I knew a nonrecursive way but
- * I dont. anyway, recursion is fun.. :)
- * DO NOT CALL THIS FUNTION WITH A POINTER TO A NULL POINTER
- * (ie: If *elem is NULL, you're doing it wrong - seg fault)
+ * 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)
{
if ((*(elem))->funcptr == crule__not)
{
- /* type conversions and ()'s are fun! ;) here have an asprin.. */
+ /* type conversions and ()'s are fun! ;) here have an aspirin.. */
if ((*(elem))->arg[0] != NULL)
crule_free((struct CRuleNode**) &((*(elem))->arg[0]));
}
}
#ifdef CR_DEBUG
+/** Display a connection rule as text.
+ * @param[in] printelem Connection rule to display.
+ */
static void print_tree(CRuleNodePtr printelem)
{
int funcnum, arg;
#endif
#ifdef CR_DEBUG
+/** Read connection rules from stdin and display parsed forms as text.
+ * @return Zero.
+ */
int main(void)
{
char indata[256];