Author: A1kmm
authorPerry Lorier <isomer@undernet.org>
Tue, 2 Apr 2002 01:02:59 +0000 (01:02 +0000)
committerPerry Lorier <isomer@undernet.org>
Tue, 2 Apr 2002 01:02:59 +0000 (01:02 +0000)
Log message:

I missed adding some files, oops.

git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/trunk@694 c9e4aea6-c8fd-4c43-8297-357d70d61c8c

ircd/ircd_lexer.l [new file with mode: 0644]
ircd/ircd_parser.y [new file with mode: 0644]

diff --git a/ircd/ircd_lexer.l b/ircd/ircd_lexer.l
new file mode 100644 (file)
index 0000000..ea9ccb8
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * ircd_parser.y: A yacc/bison parser for ircd config files.
+ * This is part of ircu, an Internet Relay Chat server.
+ * The contents of this file are Copyright(C) 2001 by Andrew Miller, the
+ * ircd-hybrid team and the ircu team.
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ * $Id$
+ */
+
+%{
+extern int conf_fd, lineno;
+#include <unistd.h>
+#include "y.tab.h"
+#undef YY_INPUT
+/* Just stop the lexer at EOF or error. */
+#define YY_INPUT(buf,result,max_size) \
+          if ((result = read(conf_fd, buf, max_size)) <= 0) \
+            result = 0;
+%}
+%option noyywrap
+%option case-insensitive
+%option nounput
+
+WHITE [ \t\r]+
+SHCOMMENT #[^\n]*
+NUMBER [0-9]+
+QSTRING \"[^"\n]+[\"\n]
+%%
+
+{QSTRING} {yytext[yyleng-1] = 0; yylval.text = yytext+1; return QSTRING;}
+{NUMBER} {yylval.num = strtoul(yytext, NULL, 10); return NUMBER;}
+{WHITE} ;
+{SHCOMMENT} ;
+
+admin return ADMIN;
+administrator return ADMIN;
+general return GENERAL;
+location return LOCATION;
+contact return CONTACT;
+connect return CONNECT;
+class return CLASS;
+pingfreq return PINGFREQ;
+connectfreq return CONNECTFREQ;
+maxlinks return MAXLINKS;
+sendq return SENDQ;
+name return NAME;
+host return HOST;
+password return PASS;
+pass return PASS;
+seconds return SECONDS;
+minutes return MINUTES;
+hours return HOURS;
+days return DAYS;
+weeks return WEEKS;
+months return MONTHS;
+years return YEARS;
+decades return DECADES;
+bytes return BYTES;
+b return BYTES;
+kbytes return KBYTES;
+kilobytes return KBYTES;
+kb return KBYTES;
+mbytes return MBYTES;
+megabytes return MBYTES;
+mb return MBYTES;
+gbytes return GBYTES;
+gigabytes return GBYTES;
+gb return GBYTES;
+tbytes return TBYTES;
+terabytes return TBYTES;
+tb return TBYTES;
+port return PORT;
+server return SERVER;
+yes return YES;
+no return NO;
+hub return HUB;
+leaf return LEAF;
+uworld return UWORLD;
+operator return OPER;
+oper return OPER;
+local return LOCAL;
+vhost return VHOST;
+mask return MASK;
+hidden return HIDDEN;
+motd return MOTD;
+numeric return NUMERIC;
+nick return NICK;
+jupe return JUPE;
+description return DESCRIPTION;
+client return CLIENT;
+real return REAL;
+realname return REAL;
+reason return REASON;
+file return TFILE;
+rule return RULE;
+all return ALL;
+ip return IP;
+crule return CRULE;
+kill return KILL;
+features return FEATURES;
+\n lineno++;
+. return yytext[0];
diff --git a/ircd/ircd_parser.y b/ircd/ircd_parser.y
new file mode 100644 (file)
index 0000000..d49ab33
--- /dev/null
@@ -0,0 +1,811 @@
+/*
+ * ircd_parser.y: A yacc/bison parser for ircd config files.
+ * This is part of ircu, an Internet Relay Chat server.
+ * The contents of this file are Copyright(C) 2001 by Andrew Miller, the
+ * ircd-hybrid team and the ircu team.
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ * $Id$
+ */
+%{
+
+#include "config.h"
+#include "s_conf.h"
+#include "class.h"
+#include "client.h"
+#include "crule.h"
+#include "ircd_features.h"
+#include "fileio.h"
+#include "gline.h"
+#include "hash.h"
+#include "ircd.h"
+#include "ircd_alloc.h"
+#include "ircd_chattr.h"
+#include "ircd_log.h"
+#include "ircd_reply.h"
+#include "ircd_snprintf.h"
+#include "ircd_string.h"
+#include "list.h"
+#include "listener.h"
+#include "match.h"
+#include "motd.h"
+#include "numeric.h"
+#include "numnicks.h"
+#include "opercmds.h"
+#include "parse.h"
+#include "res.h"
+#include "s_bsd.h"
+#include "s_debug.h"
+#include "s_misc.h"
+#include "send.h"
+#include "struct.h"
+#include "support.h"
+#include "sys.h"
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#define MAX_STRINGS 80 /* Maximum number of feature params. */
+  extern struct LocalConf   localConf;
+  extern struct DenyConf*   denyConfList;
+  extern struct CRuleConf*  cruleConfList;
+  extern struct ServerConf* serverConfList;
+
+  int yylex(void);
+  /* Now all the globals we need :/... */
+  int tping, tconn, maxlinks, sendq, port;
+  int stringno;
+  char *name, *pass, *host;
+  char *stringlist[MAX_STRINGS];
+  struct ConnectionClass *class;
+  struct ConfItem *aconf;
+  struct DenyConf *dconf;
+  struct ServerConf *sconf;
+%}
+
+%token <text> QSTRING
+%token <num> NUMBER
+%token <text> FNAME
+
+%token GENERAL
+%token ADMIN
+%token LOCATION
+%token CONTACT
+%token CONNECT
+%token CLASS
+%token PINGFREQ
+%token CONNECTFREQ
+%token MAXLINKS
+%token SENDQ
+%token NAME
+%token HOST
+%token PASS
+%token LOCAL
+%token SECONDS
+%token MINUTES
+%token HOURS
+%token DAYS
+%token WEEKS
+%token MONTHS
+%token YEARS
+%token DECADES
+%token BYTES
+%token KBYTES
+%token MBYTES
+%token GBYTES
+%token TBYTES
+%token SERVER
+%token PORT
+%token MASK
+%token HUB
+%token LEAF
+%token UWORLD
+%token YES
+%token NO
+%token OPER
+%token PORT
+%token VHOST
+%token MASK
+%token HIDDEN
+%token MOTD
+%token JUPE
+%token NICK
+%token NUMERIC
+%token DESCRIPTION
+%token CLIENT
+%token KILL
+%token CRULE
+%token REAL
+%token REASON
+%token TFILE
+%token RULE
+%token ALL
+%token IP
+%token FEATURES
+%type <num> sizespec
+%type <num> timespec, timefactor, factoredtimes, factoredtime
+%type <num> expr
+%left '+' '-'
+%left '*' '/'
+
+%union{
+ char *text;
+ int num;
+}
+
+%%
+/* Blocks in the config file... */
+blocks: blocks block | block;
+block: adminblock | generalblock | classblock | connectblock |
+       serverblock | operblock | portblock | jupeblock | clientblock |
+       killblock | cruleblock | motdblock | featuresblock;
+
+/* The timespec, sizespec and expr was ripped straight from
+ * ircd-hybrid-7. */
+timespec: expr | factoredtimes;
+
+factoredtimes: factoredtimes factoredtime
+{
+  $$ = $1 + $2;
+} | factoredtime;
+
+factoredtime: expr timefactor
+{
+  $$ = $1 * $2;
+};
+
+timefactor: SECONDS { $$ = 1; }
+| MINUTES { $$ = 60; }
+| HOURS { $$ = 60 * 60; }
+| DAYS { $$ = 60 * 60 * 24; }
+| WEEKS { $$ = 60 * 60 * 24 * 7; }
+| MONTHS { $$ = 60 * 60 * 24 * 7 * 4; }
+| YEARS { $$ = 60 * 60 * 24 * 365; }
+| DECADES { $$ = 60 * 60 * 24 * 365 * 10; };
+
+
+sizespec:      expr    
+               = {
+                       $$ = $1;
+               }
+               | expr BYTES
+               = { 
+                       $$ = $1;
+               }
+               | expr KBYTES
+               = {
+                       $$ = $1 * 1024;
+               }
+               | expr MBYTES
+               = {
+                       $$ = $1 * 1024 * 1024;
+               }
+               | expr GBYTES
+               = {
+                       $$ = $1 * 1024 * 1024 * 1024;
+               }
+               | expr TBYTES
+               = {
+                       $$ = $1 * 1024 * 1024 * 1024;
+               }
+               ;
+
+/* this is an arithmatic expression */
+expr: NUMBER
+               = { 
+                       $$ = $1;
+               }
+               | expr '+' expr
+               = { 
+                       $$ = $1 + $3;
+               }
+               | expr '-' expr
+               = { 
+                       $$ = $1 - $3;
+               }
+               | expr '*' expr
+               = { 
+                       $$ = $1 * $3;
+               }
+               | expr '/' expr
+               = { 
+                       $$ = $1 / $3;
+               }
+/* leave this out until we find why it makes BSD yacc dump core -larne
+               | '-' expr  %prec NEG
+               = {
+                       $$ = -$2;
+               } */
+               | '(' expr ')'
+               = {
+                       $$ = $2;
+               }
+               ;
+
+jupeblock: JUPE '{' jupeitems '}' ';' ;
+jupeitems: jupeitem jupeitems | jupeitem;
+jupeitem: jupenick;
+jupenick: NICK '=' QSTRING
+{
+  addNickJupes(yylval.text);
+} ';';
+
+generalblock: GENERAL '{' generalitems '}' ';' ;
+generalitems: generalitem generalitems | generalitem;
+generalitem: generalnumeric | generalname | generalvhost | generaldesc;
+generalnumeric: NUMERIC '=' NUMBER ';'
+{
+  if (localConf.numeric == 0)
+    localConf.numeric = yylval.num;
+};
+
+generalname: NAME '=' QSTRING ';'
+{
+  if (localConf.name == NULL)
+    DupString(localConf.name, yylval.text);
+};
+
+generaldesc: DESCRIPTION '=' QSTRING ';'
+{
+  MyFree(localConf.description);
+  DupString(localConf.description, yylval.text);
+};
+
+generalvhost: VHOST '=' QSTRING ';'
+{
+  if (INADDR_NONE ==
+      (localConf.vhost_address.s_addr = inet_addr(yylval.text)))
+    localConf.vhost_address.s_addr = INADDR_ANY;
+};
+
+adminblock: ADMIN '{' adminitems '}'
+{
+  if (localConf.location1 == NULL)
+    DupString(localConf.location1, "");
+  if (localConf.location2 == NULL)
+    DupString(localConf.location2, "");
+  if (localConf.contact == NULL)
+    DupString(localConf.contact, "");
+} ';';
+adminitems: adminitems adminitem | adminitem;
+adminitem: adminlocation | admincontact;
+adminlocation: LOCATION '=' QSTRING ';'
+{
+ if (localConf.location1 == NULL)
+  DupString(localConf.location1, yylval.text);
+ else if (localConf.location2 == NULL)
+  DupString(localConf.location2, yylval.text);
+ /* Otherwise just drop it. -A1kmm */
+};
+admincontact: CONTACT '=' QSTRING ';'
+{
+ if (localConf.contact != NULL)
+  free(localConf.contact);
+ DupString(localConf.contact, yylval.text);
+};
+
+classblock: CLASS {
+ name = NULL;
+ tping = 90;
+ tconn = 0;
+ maxlinks = 0;
+ sendq = 0;
+} '{' classitems '}'
+{
+ if (name != NULL)
+ {
+  add_class(name, tping, tconn, maxlinks, sendq);
+ }
+} ';';
+classitems: classitem classitems | classitem;
+classitem: classname | classpingfreq | classconnfreq | classmaxlinks |
+           classsendq;
+classname: NAME '=' QSTRING ';'
+{
+ MyFree(name);
+ DupString(name, yylval.text);
+};
+classpingfreq: PINGFREQ '=' timespec ';'
+{
+ tping = yylval.num;
+};
+classconnfreq: CONNECTFREQ '=' timespec ';'
+{
+ tconn = yylval.num;
+};
+classmaxlinks: MAXLINKS '=' expr ';'
+{
+ maxlinks = yylval.num;
+};
+classsendq: SENDQ '=' sizespec ';'
+{
+ sendq = yylval.num;
+};
+
+connectblock: CONNECT
+{
+ name = pass = host = NULL;
+ class = NULL;
+ port = 0;
+} '{' connectitems '}'
+{
+ if (name != NULL && pass != NULL && host != NULL && class != NULL && 
+     /*ccount < MAXCONFLINKS &&*/ !strchr(host, '*') &&
+     !strchr(host, '?'))
+ {
+  aconf = MyMalloc(sizeof(*aconf));
+  aconf->status = CONF_SERVER;
+  aconf->name = name;
+  aconf->passwd = pass;
+  aconf->conn_class = class;
+  aconf->port = port;
+  aconf->status = CONF_SERVER;
+  aconf->host = host;
+  aconf->next = GlobalConfList;
+  GlobalConfList = aconf;
+  printf("Server added: %s\n", name);
+  /* ccount++; -- XXX fixme --- A1kmm */
+ }
+ else
+ {
+  MyFree(name);
+  MyFree(pass);
+  MyFree(host);
+  name = pass = host = NULL;
+ }
+}';';
+connectitems: connectitem connectitems | connectitem;
+connectitem: connectname | connectpass | connectclass | connecthost
+              | connectport;
+connectname: NAME '=' QSTRING ';'
+{
+ MyFree(name);
+ DupString(name, yylval.text);
+};
+connectpass: PASS '=' QSTRING ';'
+{
+ MyFree(pass);
+ DupString(pass, yylval.text);
+};
+connectclass: CLASS '=' QSTRING ';'
+{
+ class = find_class(yylval.text);
+};
+connecthost: HOST '=' QSTRING ';'
+{
+ MyFree(host);
+ DupString(host, yylval.text);
+};
+connectport: PORT '=' NUMBER ';'
+{
+ port = yylval.num;
+};
+
+serverblock: SERVER
+{
+ aconf = MyMalloc(sizeof(*aconf));
+ memset(aconf, 0, sizeof(*aconf));
+} '{' serveritems '}'
+{
+ if (aconf->status == 0)
+ {
+   MyFree(aconf->host);
+   MyFree(aconf->name);
+   MyFree(aconf);
+   aconf = NULL;
+ }
+ else
+ {
+   aconf->next = GlobalConfList;
+   GlobalConfList = aconf;
+ }
+} ';';
+serveritems: serveritem serveritems | serveritem;
+serveritem: servername | servermask | serverhub | serverleaf |
+             serveruworld;
+servername: NAME '=' QSTRING
+{
+ MyFree(aconf->name);
+ DupString(aconf->name, yylval.text);
+} ';' ;
+servermask: MASK '=' QSTRING
+{
+ MyFree(aconf->host);
+ DupString(aconf->host, yylval.text);
+} ';' ;
+/* XXX - perhaps we should do this the hybrid way in connect blocks
+ * instead -A1kmm. */
+serverhub: HUB '=' YES ';'
+{
+ aconf->status |= CONF_HUB;
+ aconf->status &= ~CONF_LEAF;
+}
+| HUB '=' NO
+{
+ aconf->status &= ~CONF_HUB;
+} ';'; 
+serverleaf: LEAF '=' YES ';'
+{
+ if (!(aconf->status & CONF_HUB && aconf->status & CONF_UWORLD))
+  aconf->status |= CONF_LEAF;
+}
+| LEAF '=' NO ';'
+{
+ aconf->status &= ~CONF_LEAF;
+};
+serveruworld: UWORLD '=' YES ';'
+{
+ aconf->status |= CONF_UWORLD;
+ aconf->status &= ~CONF_LEAF;
+}
+| UWORLD '=' NO ';'
+{
+  aconf->status &= ~CONF_UWORLD;
+};
+
+operblock: OPER
+{
+  aconf = MyMalloc(sizeof(*aconf));
+  memset(aconf, 0, sizeof(*aconf));
+  aconf->status = CONF_OPERATOR;
+} '{' operitems '}' ';'
+{
+  if (aconf->name != NULL && aconf->passwd != NULL && aconf->host != NULL)
+  {
+   aconf->next = GlobalConfList;
+   GlobalConfList = aconf;
+  }
+  else
+  {
+   MyFree(aconf->name);
+   MyFree(aconf->passwd);
+   MyFree(aconf->host);
+   MyFree(aconf);
+   aconf = NULL;
+  }
+};
+operitems: operitem | operitems operitem;
+operitem: opername | operpass | operlocal | operhost | operclass;
+
+opername: NAME '=' QSTRING ';'
+{
+  MyFree(aconf->name);
+  DupString(aconf->name, yylval.text);
+};
+
+operpass: PASS '=' QSTRING ';'
+{
+  MyFree(aconf->passwd);
+  DupString(aconf->passwd, yylval.text);
+};
+
+operlocal: LOCAL '=' YES ';'
+{
+  /* XXX it would be good to get rid of local operators and add same
+   * permission values here. But for now, I am just going with local 
+   * opers... */
+  aconf->status = CONF_LOCOP;
+} | LOCAL '=' NO ';'
+{
+  aconf->status = CONF_OPERATOR;
+};
+
+operhost: HOST '=' QSTRING ';'
+{
+ MyFree(aconf->host);
+ if (!strchr(yylval.text, '@'))
+ {
+   int uh_len;
+   char *b = MyMalloc((uh_len = strlen(yylval.text)+3));
+   ircd_snprintf(0, b, uh_len, "*@%s", yylval.text);
+   aconf->host = b;
+ }
+ else
+   DupString(aconf->host, yylval.text);
+};
+
+operclass: CLASS '=' QSTRING ';'
+{
+ aconf->conn_class = find_class(yylval.text);
+};
+
+/* The port block... */
+portblock: PORT {
+  port = 0;
+  host = NULL;
+  /* Hijack these for is_server, is_hidden to cut down on globals... */
+  tconn = 0;
+  tping = 0;
+  /* and this for mask... */
+  pass = NULL;
+} '{' portitems '}' ';'
+{
+  if (port > 0 && port <= 0xFFFF)
+  {
+    add_listener(port, host, pass, tconn, tping);
+    host = pass = NULL;
+  }
+  else
+  {
+    MyFree(host);
+    MyFree(pass);
+  }
+};
+portitems: portitem portitems | portitem;
+portitem: portnumber | portvhost | portmask | portserver | porthidden;
+portnumber: PORT '=' NUMBER ';'
+{
+  port = yylval.num;
+};
+
+portvhost: VHOST '=' QSTRING ';'
+{
+  MyFree(host);
+  DupString(host, yylval.text);
+};
+
+portmask: MASK '=' QSTRING ';'
+{
+  MyFree(pass);
+  DupString(pass, yylval.text);
+};
+
+portserver: SERVER '=' YES ';'
+{
+  tconn = -1;
+} | SERVER '=' NO ';'
+{
+  tconn = 0;
+};
+
+porthidden: HIDDEN '=' YES ';'
+{
+  tping = -1;
+} | HIDDEN '=' NO ';'
+{
+  tping = 0;
+};
+
+clientblock: CLIENT
+{
+  aconf = MyMalloc(sizeof(*aconf));
+  memset(aconf, 0, sizeof(*aconf));
+  aconf->status = CONF_CLIENT;
+} '{' clientitems '}'
+{
+  if ((aconf->host != NULL || aconf->name!=NULL))
+  {
+    if (aconf->host == NULL)
+      DupString(aconf->host, "");
+    if (aconf->name == NULL)
+      DupString(aconf->name, "");
+    if (aconf->conn_class == NULL)
+      aconf->conn_class = find_class("default");
+    aconf->next = GlobalConfList;
+    GlobalConfList = aconf;
+    aconf = NULL;
+  }
+  else
+  {
+   MyFree(aconf->host);
+   MyFree(aconf->passwd);
+   MyFree(aconf);
+   aconf = NULL;
+  }
+} ';';
+clientitems: clientitem clientitems | clientitem;
+clientitem: clienthost | clientclass | clientpass | clientip;
+clientip: IP '=' QSTRING ';'
+{
+  MyFree(aconf->host);
+  DupString(aconf->host, yylval.text);
+};
+
+clienthost: HOST '=' QSTRING ';'
+{
+  MyFree(aconf->name);
+  DupString(aconf->name, yylval.text);
+};
+
+clientclass: CLASS '=' QSTRING ';'
+{
+  aconf->conn_class = find_class(yylval.text);
+};
+
+clientpass: PASS '=' QSTRING ';'
+{
+  MyFree(aconf->passwd);
+  DupString(aconf->passwd, yylval.text);
+};
+
+killblock: KILL
+{
+  dconf = MyMalloc(sizeof(*dconf));
+  memset(dconf, 0, sizeof(*dconf));
+} '{' killitems '}'
+{
+  if (dconf->hostmask != NULL)
+  {
+    if (dconf->usermask == NULL)
+      DupString(dconf->usermask, "*");
+    dconf->next = denyConfList;
+    denyConfList = dconf;
+    dconf = NULL;
+  }
+  else
+  {
+    MyFree(dconf->hostmask);
+    MyFree(dconf->message);
+    MyFree(dconf);
+    dconf = NULL;
+  }
+} ';';
+killitems: killitem killitems | killitem;
+killitem: killuhost | killreal | killreasonfile | killreason;
+killuhost: HOST '=' QSTRING ';'
+{
+  char *u, *h;
+  dconf->flags &= ~DENY_FLAGS_REALNAME;
+  MyFree(dconf->hostmask);
+  MyFree(dconf->usermask);
+  if ((h = strchr(yylval.text, '@')) == NULL)
+  {
+    u = "*";
+    h = yylval.text;
+  }
+  else
+  {
+    u = yylval.text;
+    h++;
+  }
+  DupString(dconf->hostmask, h);
+  DupString(dconf->usermask, u);
+  if (strchr(yylval.text, '.'))
+  {
+    int  c_class;
+    char ipname[16];
+    int  ad[4] = { 0 };
+    int  bits2 = 0;
+    dconf->flags |= DENY_FLAGS_IP;
+    c_class = sscanf(dconf->hostmask, "%d.%d.%d.%d/%d",
+                     &ad[0], &ad[1], &ad[2], &ad[3], &bits2);
+    if (c_class != 5) {
+      dconf->bits = c_class * 8;
+    }
+    else {
+      dconf->bits = bits2;
+    }
+    ircd_snprintf(0, ipname, sizeof(ipname), "%d.%d.%d.%d", ad[0], ad[1],
+                 ad[2], ad[3]);
+    dconf->address = inet_addr(ipname);
+  }
+};
+
+killreal: REAL '=' QSTRING ';'
+{
+ dconf->flags &= ~DENY_FLAGS_IP;
+ dconf->flags |= DENY_FLAGS_REALNAME;
+ MyFree(dconf->hostmask);
+ /* Leave usermask so you can specify user and real... */
+ DupString(dconf->hostmask, yylval.text);
+};
+
+killreason: REASON '=' QSTRING ';'
+{
+ dconf->flags &= DENY_FLAGS_FILE;
+ MyFree(dconf->message);
+ DupString(dconf->message, yylval.text);
+};
+
+killreasonfile: TFILE '=' QSTRING ';'
+{
+ dconf->flags |= DENY_FLAGS_FILE;
+ MyFree(dconf->message);
+ DupString(dconf->message, yylval.text);
+};
+
+cruleblock: CRULE
+{
+  host = pass = NULL;
+  tconn = CRULE_AUTO;
+} '{' cruleitems '}'
+{
+  struct CRuleNode *node;
+  if (host != NULL && pass != NULL && (node=crule_parse(pass)) != NULL)
+  {
+    struct CRuleConf *p = MyMalloc(sizeof(*p));
+    p->hostmask = host;
+    p->rule = pass;
+    p->type = tconn;
+    p->node = node;
+    p->next = cruleConfList;
+    cruleConfList = p;
+  }
+  else
+  {
+    MyFree(host);
+    MyFree(pass);
+  }
+} ';';
+
+cruleitems: cruleitem cruleitems | cruleitem;
+cruleitem: cruleserver | crulerule | cruleall;
+
+cruleserver: SERVER '=' QSTRING ';'
+{
+  MyFree(host);
+  collapse(yylval.text);
+  DupString(host, yylval.text);
+};
+
+crulerule: RULE '=' QSTRING ';'
+{
+ MyFree(pass);
+ DupString(pass, yylval.text);
+};
+
+cruleall: ALL '=' YES ';'
+{
+ tconn = CRULE_ALL;
+} | ALL '=' NO ';'
+{
+ tconn = CRULE_AUTO;
+};
+
+motdblock: MOTD {
+ pass = host = NULL;
+} '{' motditems '}'
+{
+  if (host != NULL && pass != NULL)
+    motd_add(host, pass);
+  MyFree(host);
+  MyFree(pass);
+  host = pass = NULL;
+} ';';
+
+motditems: motditem motditems | motditem;
+motditem: motdhost | motdfile;
+motdhost: HOST '=' QSTRING ';'
+{
+  DupString(host, yylval.text);
+};
+
+motdfile: TFILE '=' QSTRING ';'
+{
+  DupString(pass, yylval.text);
+};
+
+featuresblock: FEATURES '{' featureitems '}' ';';
+featureitems: featureitems featureitem | featureitem;
+
+featureitem: QSTRING
+{
+  stringno = 0;
+} '=' stringlist ';';
+
+stringlist: QSTRING
+{
+  stringlist[0] = $1;
+  stringno = 1;
+} posextrastrings
+{
+  feature_set(NULL, (const char * const *)stringlist, stringno);
+};
+posextrastrings: /* empty */ | extrastrings;
+extrastrings: extrastrings extrastring | extrastring;
+extrastring: QSTRING
+{
+  if (stringno < MAX_STRINGS)
+    stringlist[stringno++] = $1;
+};