Author: Bleep <tomh@inxpress.net>
[ircu2.10.12-pk.git] / ircd / crule.c
1 /*
2  * SmartRoute phase 1
3  * connection rule patch
4  * by Tony Vencill (Tonto on IRC) <vencill@bga.com>
5  *
6  * The majority of this file is a recusive descent parser used to convert
7  * connection rules into expression trees when the conf file is read.
8  * All parsing structures and types are hidden in the interest of good
9  * programming style and to make possible future data structure changes
10  * without affecting the interface between this patch and the rest of the
11  * server.  The only functions accessible externally are crule_parse,
12  * crule_free, and crule_eval.  Prototypes for these functions can be
13  * found in h.h.
14  *
15  * Please direct any connection rule or SmartRoute questions to Tonto on
16  * IRC or by email to vencill@bga.com.
17  *
18  * For parser testing, defining CR_DEBUG generates a stand-alone parser
19  * that takes rules from stdin and prints out memory allocation
20  * information and the parsed rule.  This stand alone parser is ignorant
21  * of the irc server and thus cannot do rule evaluation.  Do not define
22  * this flag when compiling the server!  If you wish to generate the
23  * test parser, compile from the ircd directory with a line similar to
24  * cc -o parser -DCR_DEBUG crule.c
25  *
26  * The define CR_CHKCONF is provided to generate routines needed in
27  * chkconf.  These consist of the parser, a different crule_parse that
28  * prints errors to stderr, and crule_free (just for good style and to
29  * more closely simulate the actual ircd environment).  crule_eval and
30  * the rule functions are made empty functions as in the stand-alone
31  * test parser.
32  *
33  * $Id$
34  */
35 #include "crule.h"
36 #ifndef CR_DEBUG
37
38 /* ircd functions and types we need */
39 #include "client.h"
40 #include "ircd.h"
41 #include "ircd_alloc.h"
42 #include "ircd_chattr.h"
43 #include "ircd_string.h"
44 #include "match.h"
45 #include "s_bsd.h"
46 #include "s_debug.h"
47 #include "struct.h"
48
49 #include <stdio.h>
50 #include <stdlib.h>
51
52 #else /* includes and defines to make the stand-alone test parser */
53
54 #include <stdio.h>
55 #include <stdlib.h>
56
57 #define BadPtr(x) (!(x) || (*(x) == '\0'))
58 #define DupString(x,y) \
59         do { \
60           x = (char*) MyMalloc(strlen(y)+1); \
61         strcpy(x,y); \
62         } while(0)
63
64 /* We don't care about collation discrepacies here, it seems.... */
65 #define ircd_strcmp strcasecmp
66
67 #endif
68
69 #include <string.h>
70
71
72 #if defined(CR_DEBUG) || defined(CR_CHKCONF)
73 #undef MyMalloc
74 #undef malloc
75 #define MyMalloc malloc
76 #undef MyFree
77 #undef free
78 #define MyFree free
79 #endif
80
81 /* some constants and shared data types */
82 #define CR_MAXARGLEN 80         /* why 80? why not? it's > hostname lengths */
83 #define CR_MAXARGS 3            /* There's a better way to do this,
84                                    but not now. */
85
86 /*
87  * Some symbols for easy reading
88  */
89
90 enum crule_token {
91   CR_UNKNOWN, CR_END, CR_AND, CR_OR, CR_NOT, CR_OPENPAREN, CR_CLOSEPAREN,
92   CR_COMMA, CR_WORD
93 };
94
95 enum crule_errcode {
96   CR_NOERR, CR_UNEXPCTTOK, CR_UNKNWTOK, CR_EXPCTAND, CR_EXPCTOR,
97   CR_EXPCTPRIM, CR_EXPCTOPEN, CR_EXPCTCLOSE, CR_UNKNWFUNC, CR_ARGMISMAT
98 };
99
100 /*
101  * Expression tree structure, function pointer, and tree pointer local!
102  */
103 typedef int (*crule_funcptr) (int, void **);
104
105 struct crule_treestruct {
106   crule_funcptr funcptr;
107   int numargs;
108   void *arg[CR_MAXARGS];        /* For operators arg points to a tree element;
109                                    for functions arg points to a char string. */
110 };
111
112 typedef struct crule_treestruct crule_treeelem;
113 typedef crule_treeelem *crule_treeptr;
114
115 /* local rule function prototypes */
116 static int crule_connected(int, void **);
117 static int crule_directcon(int, void **);
118 static int crule_via(int, void **);
119 static int crule_directop(int, void **);
120 static int crule__andor(int, void **);
121 static int crule__not(int, void **);
122
123 /* local parsing function prototypes */
124 static int crule_gettoken(int *, char **);
125 static void crule_getword(char *, int *, size_t, char **);
126 static int crule_parseandexpr(crule_treeptr *, int *, char **);
127 static int crule_parseorexpr(crule_treeptr *, int *, char **);
128 static int crule_parseprimary(crule_treeptr *, int *, char **);
129 static int crule_parsefunction(crule_treeptr *, int *, char **);
130 static int crule_parsearglist(crule_treeptr, int *, char **);
131
132 #if defined(CR_DEBUG) || defined(CR_CHKCONF)
133 /*
134  * Prototypes for the test parser; if not debugging,
135  * these are defined in h.h
136  */
137 char *crule_parse(char *);
138 void crule_free(char **);
139 #ifdef CR_DEBUG
140 void print_tree(crule_treeptr);
141 #endif
142 #endif
143
144 /* error messages */
145 char *crule_errstr[] = {
146   "Unknown error",              /* NOERR? - for completeness */
147   "Unexpected token",           /* UNEXPCTTOK */
148   "Unknown token",              /* UNKNWTOK */
149   "And expr expected",          /* EXPCTAND */
150   "Or expr expected",           /* EXPCTOR */
151   "Primary expected",           /* EXPCTPRIM */
152   "( expected",                 /* EXPCTOPEN */
153   ") expected",                 /* EXPCTCLOSE */
154   "Unknown function",           /* UNKNWFUNC */
155   "Argument mismatch"           /* ARGMISMAT */
156 };
157
158 /* function table - null terminated */
159 struct crule_funclistent {
160   char name[15];                /* MAXIMUM FUNCTION NAME LENGTH IS 14 CHARS!! */
161   int reqnumargs;
162   crule_funcptr funcptr;
163 };
164
165 struct crule_funclistent crule_funclist[] = {
166   /* maximum function name length is 14 chars */
167   {"connected", 1, crule_connected},
168   {"directcon", 1, crule_directcon},
169   {"via", 2, crule_via},
170   {"directop", 0, crule_directop},
171   {"", 0, NULL}                 /* this must be here to mark end of list */
172 };
173
174 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
175 static int crule_connected(int numargs, void *crulearg[])
176 {
177   struct Client *acptr;
178
179   /* taken from m_links */
180   for (acptr = GlobalClientList; acptr; acptr = acptr->next)
181   {
182     if (!IsServer(acptr) && !IsMe(acptr))
183       continue;
184     if (match((char *)crulearg[0], acptr->name))
185       continue;
186     return (1);
187   }
188   return (0);
189 }
190 #else
191 static int crule_connected(int numargs, void **crulearg)
192 {
193   return (0);
194 }
195 #endif
196
197 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
198 static int crule_directcon(int numargs, void *crulearg[])
199 {
200   int i;
201   struct Client *acptr;
202
203   /* adapted from m_trace and exit_one_client */
204   for (i = 0; i <= HighestFd; i++)
205   {
206     if (!(acptr = LocalClientArray[i]) || !IsServer(acptr))
207       continue;
208     if (match((char *)crulearg[0], acptr->name))
209       continue;
210     return (1);
211   }
212   return (0);
213 }
214 #else
215 static int crule_directcon(int numargs, void **crulearg)
216 {
217   return (0);
218 }
219 #endif
220
221 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
222 static int crule_via(int numargs, void *crulearg[])
223 {
224   struct Client *acptr;
225
226   /* adapted from m_links */
227   for (acptr = GlobalClientList; acptr; acptr = acptr->next)
228   {
229     if (!IsServer(acptr) && !IsMe(acptr))
230       continue;
231     if (match((char *)crulearg[1], acptr->name))
232       continue;
233     if (match((char *)crulearg[0], (LocalClientArray[acptr->from->fd])->name))
234       continue;
235     return (1);
236   }
237   return (0);
238 }
239 #else
240 static int crule_via(int numargs, void **crulearg)
241 {
242   return (0);
243 }
244 #endif
245
246 static int crule_directop(int numargs, void **crulearg)
247 {
248 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
249   int i;
250   struct Client *acptr;
251
252   /* adapted from m_trace */
253   for (i = 0; i <= HighestFd; i++)
254   {
255     if (!(acptr = LocalClientArray[i]) || !IsAnOper(acptr))
256       continue;
257     return (1);
258   }
259 #endif
260   return (0);
261 }
262
263 static int crule__andor(int numargs, void *crulearg[])
264 {
265   int result1;
266
267   result1 = ((crule_treeptr) crulearg[0])->funcptr
268       (((crule_treeptr) crulearg[0])->numargs,
269       ((crule_treeptr) crulearg[0])->arg);
270   if (crulearg[2])              /* or */
271     return (result1 ||
272         ((crule_treeptr) crulearg[1])->funcptr
273         (((crule_treeptr) crulearg[1])->numargs,
274         ((crule_treeptr) crulearg[1])->arg));
275   else
276     return (result1 &&
277         ((crule_treeptr) crulearg[1])->funcptr
278         (((crule_treeptr) crulearg[1])->numargs,
279         ((crule_treeptr) crulearg[1])->arg));
280 }
281
282 static int crule__not(int numargs, void *crulearg[])
283 {
284   return (!((crule_treeptr) crulearg[0])->funcptr
285       (((crule_treeptr) crulearg[0])->numargs,
286       ((crule_treeptr) crulearg[0])->arg));
287 }
288
289 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
290 int crule_eval(char *rule)
291 {
292   return (((crule_treeptr) rule)->funcptr
293       (((crule_treeptr) rule)->numargs, ((crule_treeptr) rule)->arg));
294 }
295 #endif
296
297 static int crule_gettoken(int *next_tokp, char **ruleptr)
298 {
299   char pending = '\0';
300
301   *next_tokp = CR_UNKNOWN;
302   while (*next_tokp == CR_UNKNOWN)
303     switch (*(*ruleptr)++)
304     {
305       case ' ':
306       case '\t':
307         break;
308       case '&':
309         if (pending == '\0')
310           pending = '&';
311         else if (pending == '&')
312           *next_tokp = CR_AND;
313         else
314           return (CR_UNKNWTOK);
315         break;
316       case '|':
317         if (pending == '\0')
318           pending = '|';
319         else if (pending == '|')
320           *next_tokp = CR_OR;
321         else
322           return (CR_UNKNWTOK);
323         break;
324       case '!':
325         *next_tokp = CR_NOT;
326         break;
327       case '(':
328         *next_tokp = CR_OPENPAREN;
329         break;
330       case ')':
331         *next_tokp = CR_CLOSEPAREN;
332         break;
333       case ',':
334         *next_tokp = CR_COMMA;
335         break;
336       case '\0':
337         (*ruleptr)--;
338         *next_tokp = CR_END;
339         break;
340       case ':':
341         *next_tokp = CR_END;
342         break;
343       default:
344         if ((IsAlpha(*(--(*ruleptr)))) || (**ruleptr == '*') ||
345             (**ruleptr == '?') || (**ruleptr == '.') || (**ruleptr == '-'))
346           *next_tokp = CR_WORD;
347         else
348           return (CR_UNKNWTOK);
349         break;
350     }
351   return CR_NOERR;
352 }
353
354 static void crule_getword(char *word, int *wordlenp, size_t maxlen,
355     char **ruleptr)
356 {
357   char *word_ptr;
358
359   word_ptr = word;
360   while ((size_t)(word_ptr - word) < maxlen
361       && (IsAlnum(**ruleptr)
362       || **ruleptr == '*' || **ruleptr == '?'
363       || **ruleptr == '.' || **ruleptr == '-'))
364     *word_ptr++ = *(*ruleptr)++;
365   *word_ptr = '\0';
366   *wordlenp = word_ptr - word;
367 }
368
369 /*
370  * Grammar
371  *   rule:
372  *     orexpr END          END is end of input or :
373  *   orexpr:
374  *     andexpr
375  *     andexpr || orexpr
376  *   andexpr:
377  *     primary
378  *     primary && andexpr
379  *  primary:
380  *    function
381  *    ! primary
382  *    ( orexpr )
383  *  function:
384  *    word ( )             word is alphanumeric string, first character
385  *    word ( arglist )       must be a letter
386  *  arglist:
387  *    word
388  *    word , arglist
389  */
390 char *crule_parse(char *rule)
391 {
392   char *ruleptr = rule;
393   int next_tok;
394   crule_treeptr ruleroot = NULL;
395   int errcode = CR_NOERR;
396
397   if ((errcode = crule_gettoken(&next_tok, &ruleptr)) == CR_NOERR)
398   {
399     if ((errcode = crule_parseorexpr(&ruleroot, &next_tok, &ruleptr))
400         == CR_NOERR)
401     {
402       if (ruleroot != NULL)
403       {
404         if (next_tok == CR_END)
405           return ((char *)ruleroot);
406         else
407           errcode = CR_UNEXPCTTOK;
408       }
409       else
410         errcode = CR_EXPCTOR;
411     }
412   }
413   if (ruleroot != NULL)
414     crule_free((char **)&ruleroot);
415 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
416   Debug((DEBUG_ERROR, "%s in rule: %s", crule_errstr[errcode], rule));
417 #else
418   fprintf(stderr, "%s in rule: %s\n", crule_errstr[errcode], rule);
419 #endif
420   return NULL;
421 }
422
423 static int crule_parseorexpr(crule_treeptr * orrootp, int *next_tokp,
424     char **ruleptr)
425 {
426   int errcode = CR_NOERR;
427   crule_treeptr andexpr;
428   crule_treeptr orptr;
429
430   *orrootp = NULL;
431   while (errcode == CR_NOERR)
432   {
433     errcode = crule_parseandexpr(&andexpr, next_tokp, ruleptr);
434     if ((errcode == CR_NOERR) && (*next_tokp == CR_OR))
435     {
436       orptr = (crule_treeptr) MyMalloc(sizeof(crule_treeelem));
437 #ifdef CR_DEBUG
438       fprintf(stderr, "allocating or element at %ld\n", orptr);
439 #endif
440       orptr->funcptr = crule__andor;
441       orptr->numargs = 3;
442       orptr->arg[2] = (void *)1;
443       if (*orrootp != NULL)
444       {
445         (*orrootp)->arg[1] = andexpr;
446         orptr->arg[0] = *orrootp;
447       }
448       else
449         orptr->arg[0] = andexpr;
450       *orrootp = orptr;
451     }
452     else
453     {
454       if (*orrootp != NULL)
455       {
456         if (andexpr != NULL)
457         {
458           (*orrootp)->arg[1] = andexpr;
459           return (errcode);
460         }
461         else
462         {
463           (*orrootp)->arg[1] = NULL;    /* so free doesn't seg fault */
464           return (CR_EXPCTAND);
465         }
466       }
467       else
468       {
469         *orrootp = andexpr;
470         return (errcode);
471       }
472     }
473     if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
474       return (errcode);
475   }
476   return (errcode);
477 }
478
479 static int crule_parseandexpr(crule_treeptr * androotp, int *next_tokp,
480     char **ruleptr)
481 {
482   int errcode = CR_NOERR;
483   crule_treeptr primary;
484   crule_treeptr andptr;
485
486   *androotp = NULL;
487   while (errcode == CR_NOERR)
488   {
489     errcode = crule_parseprimary(&primary, next_tokp, ruleptr);
490     if ((errcode == CR_NOERR) && (*next_tokp == CR_AND))
491     {
492       andptr = (crule_treeptr) MyMalloc(sizeof(crule_treeelem));
493 #ifdef CR_DEBUG
494       fprintf(stderr, "allocating and element at %ld\n", andptr);
495 #endif
496       andptr->funcptr = crule__andor;
497       andptr->numargs = 3;
498       andptr->arg[2] = (void *)0;
499       if (*androotp != NULL)
500       {
501         (*androotp)->arg[1] = primary;
502         andptr->arg[0] = *androotp;
503       }
504       else
505         andptr->arg[0] = primary;
506       *androotp = andptr;
507     }
508     else
509     {
510       if (*androotp != NULL)
511       {
512         if (primary != NULL)
513         {
514           (*androotp)->arg[1] = primary;
515           return (errcode);
516         }
517         else
518         {
519           (*androotp)->arg[1] = NULL;   /* so free doesn't seg fault */
520           return (CR_EXPCTPRIM);
521         }
522       }
523       else
524       {
525         *androotp = primary;
526         return (errcode);
527       }
528     }
529     if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
530       return (errcode);
531   }
532   return (errcode);
533 }
534
535 static int crule_parseprimary(crule_treeptr * primrootp,
536     int *next_tokp, char **ruleptr)
537 {
538   crule_treeptr *insertionp;
539   int errcode = CR_NOERR;
540
541   *primrootp = NULL;
542   insertionp = primrootp;
543   while (errcode == CR_NOERR)
544   {
545     switch (*next_tokp)
546     {
547       case CR_OPENPAREN:
548         if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
549           break;
550         if ((errcode = crule_parseorexpr(insertionp, next_tokp,
551             ruleptr)) != CR_NOERR)
552           break;
553         if (*insertionp == NULL)
554         {
555           errcode = CR_EXPCTAND;
556           break;
557         }
558         if (*next_tokp != CR_CLOSEPAREN)
559         {
560           errcode = CR_EXPCTCLOSE;
561           break;
562         }
563         errcode = crule_gettoken(next_tokp, ruleptr);
564         break;
565       case CR_NOT:
566         *insertionp = (crule_treeptr) MyMalloc(sizeof(crule_treeelem));
567 #ifdef CR_DEBUG
568         fprintf(stderr, "allocating primary element at %ld\n", *insertionp);
569 #endif
570         (*insertionp)->funcptr = crule__not;
571         (*insertionp)->numargs = 1;
572         (*insertionp)->arg[0] = NULL;
573         insertionp = (crule_treeptr *) & ((*insertionp)->arg[0]);
574         if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
575           break;
576         continue;
577       case CR_WORD:
578         errcode = crule_parsefunction(insertionp, next_tokp, ruleptr);
579         break;
580       default:
581         if (*primrootp == NULL)
582           errcode = CR_NOERR;
583         else
584           errcode = CR_EXPCTPRIM;
585         break;
586     }
587     return (errcode);
588   }
589   return (errcode);
590 }
591
592 static int crule_parsefunction(crule_treeptr * funcrootp,
593     int *next_tokp, char **ruleptr)
594 {
595   int errcode = CR_NOERR;
596   char funcname[CR_MAXARGLEN];
597   int namelen;
598   int funcnum;
599
600   *funcrootp = NULL;
601   crule_getword(funcname, &namelen, CR_MAXARGLEN - 1, ruleptr);
602   if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
603     return (errcode);
604   if (*next_tokp == CR_OPENPAREN)
605   {
606     for (funcnum = 0;; funcnum++)
607     {
608       if (0 == ircd_strcmp(crule_funclist[funcnum].name, funcname))
609         break;
610       if (crule_funclist[funcnum].name[0] == '\0')
611         return (CR_UNKNWFUNC);
612     }
613     if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
614       return (errcode);
615     *funcrootp = (crule_treeptr) MyMalloc(sizeof(crule_treeelem));
616 #ifdef CR_DEBUG
617     fprintf(stderr, "allocating function element at %ld\n", *funcrootp);
618 #endif
619     (*funcrootp)->funcptr = NULL;       /* for freeing aborted trees */
620     if ((errcode =
621         crule_parsearglist(*funcrootp, next_tokp, ruleptr)) != CR_NOERR)
622       return (errcode);
623     if (*next_tokp != CR_CLOSEPAREN)
624       return (CR_EXPCTCLOSE);
625     if ((crule_funclist[funcnum].reqnumargs != (*funcrootp)->numargs) &&
626         (crule_funclist[funcnum].reqnumargs != -1))
627       return (CR_ARGMISMAT);
628     if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
629       return (errcode);
630     (*funcrootp)->funcptr = crule_funclist[funcnum].funcptr;
631     return (CR_NOERR);
632   }
633   else
634     return (CR_EXPCTOPEN);
635 }
636
637 static int crule_parsearglist(crule_treeptr argrootp, int *next_tokp,
638     char **ruleptr)
639 {
640   int errcode = CR_NOERR;
641   char *argelemp = NULL;
642   char currarg[CR_MAXARGLEN];
643   int arglen = 0;
644   char word[CR_MAXARGLEN];
645   int wordlen = 0;
646
647   argrootp->numargs = 0;
648   currarg[0] = '\0';
649   while (errcode == CR_NOERR)
650   {
651     switch (*next_tokp)
652     {
653       case CR_WORD:
654         crule_getword(word, &wordlen, CR_MAXARGLEN - 1, ruleptr);
655         if (currarg[0] != '\0')
656         {
657           if ((arglen + wordlen) < (CR_MAXARGLEN - 1))
658           {
659             strcat(currarg, " ");
660             strcat(currarg, word);
661             arglen += wordlen + 1;
662           }
663         }
664         else
665         {
666           strcpy(currarg, word);
667           arglen = wordlen;
668         }
669         errcode = crule_gettoken(next_tokp, ruleptr);
670         break;
671       default:
672 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
673         collapse(currarg);
674 #endif
675         if (!BadPtr(currarg))
676         {
677           DupString(argelemp, currarg);
678           argrootp->arg[argrootp->numargs++] = (void *)argelemp;
679         }
680         if (*next_tokp != CR_COMMA)
681           return (CR_NOERR);
682         currarg[0] = '\0';
683         errcode = crule_gettoken(next_tokp, ruleptr);
684         break;
685     }
686   }
687   return (errcode);
688 }
689
690 /*
691  * This function is recursive..  I wish I knew a nonrecursive way but
692  * I dont.  anyway, recursion is fun..  :)
693  * DO NOT CALL THIS FUNTION WITH A POINTER TO A NULL POINTER
694  * (ie: If *elem is NULL, you're doing it wrong - seg fault)
695  */
696 void crule_free(char **elem)
697 {
698   int arg, numargs;
699
700   if ((*((crule_treeptr *) elem))->funcptr == crule__not)
701   {
702     /* type conversions and ()'s are fun! ;)  here have an asprin.. */
703     if ((*((crule_treeptr *) elem))->arg[0] != NULL)
704       crule_free((char **)&((*((crule_treeptr *) elem))->arg[0]));
705   }
706   else if ((*((crule_treeptr *) elem))->funcptr == crule__andor)
707   {
708     crule_free((char **)&((*((crule_treeptr *) elem))->arg[0]));
709     if ((*((crule_treeptr *) elem))->arg[1] != NULL)
710       crule_free((char **)&((*((crule_treeptr *) elem))->arg[1]));
711   }
712   else
713   {
714     numargs = (*((crule_treeptr *) elem))->numargs;
715     for (arg = 0; arg < numargs; arg++)
716       MyFree((*((crule_treeptr *) elem))->arg[arg]);
717   }
718 #ifdef CR_DEBUG
719   fprintf(stderr, "freeing element at %ld\n", *elem);
720 #endif
721   MyFree(*elem);
722   *elem = NULL;
723 }
724
725 #ifdef CR_DEBUG
726 static void print_tree(crule_treeptr printelem)
727 {
728   int funcnum, arg;
729
730   if (printelem->funcptr == crule__not)
731   {
732     printf("!( ");
733     print_tree((crule_treeptr) printelem->arg[0]);
734     printf(") ");
735   }
736   else if (printelem->funcptr == crule__andor)
737   {
738     printf("( ");
739     print_tree((crule_treeptr) printelem->arg[0]);
740     if (printelem->arg[2])
741       printf("|| ");
742     else
743       printf("&& ");
744     print_tree((crule_treeptr) printelem->arg[1]);
745     printf(") ");
746   }
747   else
748   {
749     for (funcnum = 0;; funcnum++)
750     {
751       if (printelem->funcptr == crule_funclist[funcnum].funcptr)
752         break;
753       if (crule_funclist[funcnum].funcptr == NULL)
754         MyCoreDump;
755     }
756     printf("%s(", crule_funclist[funcnum].name);
757     for (arg = 0; arg < printelem->numargs; arg++)
758     {
759       if (arg != 0)
760         printf(",");
761       printf("%s", (char *)printelem->arg[arg]);
762     }
763     printf(") ");
764   }
765 }
766
767 #endif
768
769 #ifdef CR_DEBUG
770 int main(void)
771 {
772   char indata[256];
773   char *rule;
774
775   printf("rule: ");
776   while (fgets(indata, 256, stdin) != NULL)
777   {
778     indata[strlen(indata) - 1] = '\0';  /* lose the newline */
779     if ((rule = crule_parse(indata)) != NULL)
780     {
781       printf("equivalent rule: ");
782       print_tree((crule_treeptr) rule);
783       printf("\n");
784       crule_free(&rule);
785     }
786     printf("\nrule: ");
787   }
788   printf("\n");
789
790   return 0;
791 }
792
793 #endif