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