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