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 "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 CRuleNode {
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 CRuleNode* CRuleNodePtr;
113
114 /* local rule function prototypes */
115 static int crule_connected(int, void **);
116 static int crule_directcon(int, void **);
117 static int crule_via(int, void **);
118 static int crule_directop(int, void **);
119 static int crule__andor(int, void **);
120 static int crule__not(int, void **);
121
122 /* local parsing function prototypes */
123 static int crule_gettoken(int* token, const char** str);
124 static void crule_getword(char*, int*, size_t, const char**);
125 static int crule_parseandexpr(CRuleNodePtr*, int *, const char**);
126 static int crule_parseorexpr(CRuleNodePtr*, int *, const char**);
127 static int crule_parseprimary(CRuleNodePtr*, int *, const char**);
128 static int crule_parsefunction(CRuleNodePtr*, int *, const char**);
129 static int crule_parsearglist(CRuleNodePtr, int *, const char**);
130
131 #if defined(CR_DEBUG) || defined(CR_CHKCONF)
132 /*
133  * Prototypes for the test parser; if not debugging,
134  * these are defined in h.h
135  */
136 struct CRuleNode* crule_parse(const char*);
137 void crule_free(struct CRuleNode**);
138 #ifdef CR_DEBUG
139 void print_tree(CRuleNodePtr);
140 #endif
141 #endif
142
143 /* error messages */
144 char *crule_errstr[] = {
145   "Unknown error",              /* NOERR? - for completeness */
146   "Unexpected token",           /* UNEXPCTTOK */
147   "Unknown token",              /* UNKNWTOK */
148   "And expr expected",          /* EXPCTAND */
149   "Or expr expected",           /* EXPCTOR */
150   "Primary expected",           /* EXPCTPRIM */
151   "( expected",                 /* EXPCTOPEN */
152   ") expected",                 /* EXPCTCLOSE */
153   "Unknown function",           /* UNKNWFUNC */
154   "Argument mismatch"           /* ARGMISMAT */
155 };
156
157 /* function table - null terminated */
158 struct crule_funclistent {
159   char name[15];                /* MAXIMUM FUNCTION NAME LENGTH IS 14 CHARS!! */
160   int reqnumargs;
161   crule_funcptr funcptr;
162 };
163
164 struct crule_funclistent crule_funclist[] = {
165   /* maximum function name length is 14 chars */
166   {"connected", 1, crule_connected},
167   {"directcon", 1, crule_directcon},
168   {"via", 2, crule_via},
169   {"directop", 0, crule_directop},
170   {"", 0, NULL}                 /* this must be here to mark end of list */
171 };
172
173 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
174 static int crule_connected(int numargs, void *crulearg[])
175 {
176   struct Client *acptr;
177
178   /* taken from m_links */
179   for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr))
180   {
181     if (!IsServer(acptr) && !IsMe(acptr))
182       continue;
183     if (match((char *)crulearg[0], cli_name(acptr)))
184       continue;
185     return (1);
186   }
187   return (0);
188 }
189 #else
190 static int crule_connected(int numargs, void **crulearg)
191 {
192   return (0);
193 }
194 #endif
195
196 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
197 static int crule_directcon(int numargs, void *crulearg[])
198 {
199   int i;
200   struct Client *acptr;
201
202   /* adapted from m_trace and exit_one_client */
203   for (i = 0; i <= HighestFd; i++)
204   {
205     if (!(acptr = LocalClientArray[i]) || !IsServer(acptr))
206       continue;
207     if (match((char *)crulearg[0], cli_name(acptr)))
208       continue;
209     return (1);
210   }
211   return (0);
212 }
213 #else
214 static int crule_directcon(int numargs, void **crulearg)
215 {
216   return (0);
217 }
218 #endif
219
220 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
221 static int crule_via(int numargs, void *crulearg[])
222 {
223   struct Client *acptr;
224
225   /* adapted from m_links */
226   for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr))
227   {
228     if (!IsServer(acptr) && !IsMe(acptr))
229       continue;
230     if (match((char *)crulearg[1], cli_name(acptr)))
231       continue;
232     if (match((char *)crulearg[0],
233               cli_name(LocalClientArray[cli_fd(cli_from(acptr))])))
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 = ((CRuleNodePtr) crulearg[0])->funcptr
268       (((CRuleNodePtr) crulearg[0])->numargs,
269       ((CRuleNodePtr) crulearg[0])->arg);
270   if (crulearg[2])              /* or */
271     return (result1 ||
272         ((CRuleNodePtr) crulearg[1])->funcptr
273         (((CRuleNodePtr) crulearg[1])->numargs,
274         ((CRuleNodePtr) crulearg[1])->arg));
275   else
276     return (result1 &&
277         ((CRuleNodePtr) crulearg[1])->funcptr
278         (((CRuleNodePtr) crulearg[1])->numargs,
279         ((CRuleNodePtr) crulearg[1])->arg));
280 }
281
282 static int crule__not(int numargs, void *crulearg[])
283 {
284   return (!((CRuleNodePtr) crulearg[0])->funcptr
285       (((CRuleNodePtr) crulearg[0])->numargs,
286       ((CRuleNodePtr) crulearg[0])->arg));
287 }
288
289 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
290 int crule_eval(struct CRuleNode* rule)
291 {
292   return (rule->funcptr(rule->numargs, rule->arg));
293 }
294 #endif
295
296 static int crule_gettoken(int* next_tokp, const char** ruleptr)
297 {
298   char pending = '\0';
299
300   *next_tokp = CR_UNKNOWN;
301   while (*next_tokp == CR_UNKNOWN)
302     switch (*(*ruleptr)++)
303     {
304       case ' ':
305       case '\t':
306         break;
307       case '&':
308         if (pending == '\0')
309           pending = '&';
310         else if (pending == '&')
311           *next_tokp = CR_AND;
312         else
313           return (CR_UNKNWTOK);
314         break;
315       case '|':
316         if (pending == '\0')
317           pending = '|';
318         else if (pending == '|')
319           *next_tokp = CR_OR;
320         else
321           return (CR_UNKNWTOK);
322         break;
323       case '!':
324         *next_tokp = CR_NOT;
325         break;
326       case '(':
327         *next_tokp = CR_OPENPAREN;
328         break;
329       case ')':
330         *next_tokp = CR_CLOSEPAREN;
331         break;
332       case ',':
333         *next_tokp = CR_COMMA;
334         break;
335       case '\0':
336         (*ruleptr)--;
337         *next_tokp = CR_END;
338         break;
339       case ':':
340         *next_tokp = CR_END;
341         break;
342       default:
343         if ((IsAlpha(*(--(*ruleptr)))) || (**ruleptr == '*') ||
344             (**ruleptr == '?') || (**ruleptr == '.') || (**ruleptr == '-'))
345           *next_tokp = CR_WORD;
346         else
347           return (CR_UNKNWTOK);
348         break;
349     }
350   return CR_NOERR;
351 }
352
353 static void crule_getword(char* word, int* wordlenp, size_t maxlen, const char** ruleptr)
354 {
355   char *word_ptr;
356
357   word_ptr = word;
358   while ((size_t)(word_ptr - word) < maxlen
359       && (IsAlnum(**ruleptr)
360       || **ruleptr == '*' || **ruleptr == '?'
361       || **ruleptr == '.' || **ruleptr == '-'))
362     *word_ptr++ = *(*ruleptr)++;
363   *word_ptr = '\0';
364   *wordlenp = word_ptr - word;
365 }
366
367 /*
368  * Grammar
369  *   rule:
370  *     orexpr END          END is end of input or :
371  *   orexpr:
372  *     andexpr
373  *     andexpr || orexpr
374  *   andexpr:
375  *     primary
376  *     primary && andexpr
377  *  primary:
378  *    function
379  *    ! primary
380  *    ( orexpr )
381  *  function:
382  *    word ( )             word is alphanumeric string, first character
383  *    word ( arglist )       must be a letter
384  *  arglist:
385  *    word
386  *    word , arglist
387  */
388 struct CRuleNode* crule_parse(const char *rule)
389 {
390   const char* ruleptr = rule;
391   int next_tok;
392   struct CRuleNode* ruleroot = 0;
393   int errcode = CR_NOERR;
394
395   if ((errcode = crule_gettoken(&next_tok, &ruleptr)) == CR_NOERR) {
396     if ((errcode = crule_parseorexpr(&ruleroot, &next_tok, &ruleptr)) == CR_NOERR) {
397       if (ruleroot != NULL) {
398         if (next_tok == CR_END)
399           return (ruleroot);
400         else
401           errcode = CR_UNEXPCTTOK;
402       }
403       else
404         errcode = CR_EXPCTOR;
405     }
406   }
407   if (ruleroot != NULL)
408     crule_free(&ruleroot);
409 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
410   Debug((DEBUG_ERROR, "%s in rule: %s", crule_errstr[errcode], rule));
411 #else
412   fprintf(stderr, "%s in rule: %s\n", crule_errstr[errcode], rule);
413 #endif
414   return 0;
415 }
416
417 static int crule_parseorexpr(CRuleNodePtr * orrootp, int *next_tokp, const char** ruleptr)
418 {
419   int errcode = CR_NOERR;
420   CRuleNodePtr andexpr;
421   CRuleNodePtr orptr;
422
423   *orrootp = NULL;
424   while (errcode == CR_NOERR)
425   {
426     errcode = crule_parseandexpr(&andexpr, next_tokp, ruleptr);
427     if ((errcode == CR_NOERR) && (*next_tokp == CR_OR))
428     {
429       orptr = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
430 #ifdef CR_DEBUG
431       fprintf(stderr, "allocating or element at %ld\n", orptr);
432 #endif
433       orptr->funcptr = crule__andor;
434       orptr->numargs = 3;
435       orptr->arg[2] = (void *)1;
436       if (*orrootp != NULL)
437       {
438         (*orrootp)->arg[1] = andexpr;
439         orptr->arg[0] = *orrootp;
440       }
441       else
442         orptr->arg[0] = andexpr;
443       *orrootp = orptr;
444     }
445     else
446     {
447       if (*orrootp != NULL)
448       {
449         if (andexpr != NULL)
450         {
451           (*orrootp)->arg[1] = andexpr;
452           return (errcode);
453         }
454         else
455         {
456           (*orrootp)->arg[1] = NULL;    /* so free doesn't seg fault */
457           return (CR_EXPCTAND);
458         }
459       }
460       else
461       {
462         *orrootp = andexpr;
463         return (errcode);
464       }
465     }
466     if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
467       return (errcode);
468   }
469   return (errcode);
470 }
471
472 static int crule_parseandexpr(CRuleNodePtr * androotp, int *next_tokp, const char** ruleptr)
473 {
474   int errcode = CR_NOERR;
475   CRuleNodePtr primary;
476   CRuleNodePtr andptr;
477
478   *androotp = NULL;
479   while (errcode == CR_NOERR)
480   {
481     errcode = crule_parseprimary(&primary, next_tokp, ruleptr);
482     if ((errcode == CR_NOERR) && (*next_tokp == CR_AND))
483     {
484       andptr = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
485 #ifdef CR_DEBUG
486       fprintf(stderr, "allocating and element at %ld\n", andptr);
487 #endif
488       andptr->funcptr = crule__andor;
489       andptr->numargs = 3;
490       andptr->arg[2] = (void *)0;
491       if (*androotp != NULL)
492       {
493         (*androotp)->arg[1] = primary;
494         andptr->arg[0] = *androotp;
495       }
496       else
497         andptr->arg[0] = primary;
498       *androotp = andptr;
499     }
500     else
501     {
502       if (*androotp != NULL)
503       {
504         if (primary != NULL)
505         {
506           (*androotp)->arg[1] = primary;
507           return (errcode);
508         }
509         else
510         {
511           (*androotp)->arg[1] = NULL;   /* so free doesn't seg fault */
512           return (CR_EXPCTPRIM);
513         }
514       }
515       else
516       {
517         *androotp = primary;
518         return (errcode);
519       }
520     }
521     if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
522       return (errcode);
523   }
524   return (errcode);
525 }
526
527 static int crule_parseprimary(CRuleNodePtr* primrootp, int *next_tokp, const char** ruleptr)
528 {
529   CRuleNodePtr *insertionp;
530   int errcode = CR_NOERR;
531
532   *primrootp = NULL;
533   insertionp = primrootp;
534   while (errcode == CR_NOERR)
535   {
536     switch (*next_tokp)
537     {
538       case CR_OPENPAREN:
539         if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
540           break;
541         if ((errcode = crule_parseorexpr(insertionp, next_tokp, ruleptr)) != CR_NOERR)
542           break;
543         if (*insertionp == NULL)
544         {
545           errcode = CR_EXPCTAND;
546           break;
547         }
548         if (*next_tokp != CR_CLOSEPAREN)
549         {
550           errcode = CR_EXPCTCLOSE;
551           break;
552         }
553         errcode = crule_gettoken(next_tokp, ruleptr);
554         break;
555       case CR_NOT:
556         *insertionp = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
557 #ifdef CR_DEBUG
558         fprintf(stderr, "allocating primary element at %ld\n", *insertionp);
559 #endif
560         (*insertionp)->funcptr = crule__not;
561         (*insertionp)->numargs = 1;
562         (*insertionp)->arg[0] = NULL;
563         insertionp = (CRuleNodePtr *) & ((*insertionp)->arg[0]);
564         if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
565           break;
566         continue;
567       case CR_WORD:
568         errcode = crule_parsefunction(insertionp, next_tokp, ruleptr);
569         break;
570       default:
571         if (*primrootp == NULL)
572           errcode = CR_NOERR;
573         else
574           errcode = CR_EXPCTPRIM;
575         break;
576     }
577     return (errcode);
578   }
579   return (errcode);
580 }
581
582 static int crule_parsefunction(CRuleNodePtr* funcrootp, int* next_tokp, const char** ruleptr)
583 {
584   int errcode = CR_NOERR;
585   char funcname[CR_MAXARGLEN];
586   int namelen;
587   int funcnum;
588
589   *funcrootp = NULL;
590   crule_getword(funcname, &namelen, CR_MAXARGLEN - 1, ruleptr);
591   if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
592     return (errcode);
593   if (*next_tokp == CR_OPENPAREN)
594   {
595     for (funcnum = 0;; funcnum++)
596     {
597       if (0 == ircd_strcmp(crule_funclist[funcnum].name, funcname))
598         break;
599       if (crule_funclist[funcnum].name[0] == '\0')
600         return (CR_UNKNWFUNC);
601     }
602     if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
603       return (errcode);
604     *funcrootp = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
605 #ifdef CR_DEBUG
606     fprintf(stderr, "allocating function element at %ld\n", *funcrootp);
607 #endif
608     (*funcrootp)->funcptr = NULL;       /* for freeing aborted trees */
609     if ((errcode =
610         crule_parsearglist(*funcrootp, next_tokp, ruleptr)) != CR_NOERR)
611       return (errcode);
612     if (*next_tokp != CR_CLOSEPAREN)
613       return (CR_EXPCTCLOSE);
614     if ((crule_funclist[funcnum].reqnumargs != (*funcrootp)->numargs) &&
615         (crule_funclist[funcnum].reqnumargs != -1))
616       return (CR_ARGMISMAT);
617     if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
618       return (errcode);
619     (*funcrootp)->funcptr = crule_funclist[funcnum].funcptr;
620     return (CR_NOERR);
621   }
622   else
623     return (CR_EXPCTOPEN);
624 }
625
626 static int crule_parsearglist(CRuleNodePtr argrootp, int *next_tokp, const char** ruleptr)
627 {
628   int errcode = CR_NOERR;
629   char *argelemp = NULL;
630   char currarg[CR_MAXARGLEN];
631   int arglen = 0;
632   char word[CR_MAXARGLEN];
633   int wordlen = 0;
634
635   argrootp->numargs = 0;
636   currarg[0] = '\0';
637   while (errcode == CR_NOERR)
638   {
639     switch (*next_tokp)
640     {
641       case CR_WORD:
642         crule_getword(word, &wordlen, CR_MAXARGLEN - 1, ruleptr);
643         if (currarg[0] != '\0')
644         {
645           if ((arglen + wordlen) < (CR_MAXARGLEN - 1))
646           {
647             strcat(currarg, " ");
648             strcat(currarg, word);
649             arglen += wordlen + 1;
650           }
651         }
652         else
653         {
654           strcpy(currarg, word);
655           arglen = wordlen;
656         }
657         errcode = crule_gettoken(next_tokp, ruleptr);
658         break;
659       default:
660 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
661         collapse(currarg);
662 #endif
663         if (!BadPtr(currarg))
664         {
665           DupString(argelemp, currarg);
666           argrootp->arg[argrootp->numargs++] = (void *)argelemp;
667         }
668         if (*next_tokp != CR_COMMA)
669           return (CR_NOERR);
670         currarg[0] = '\0';
671         errcode = crule_gettoken(next_tokp, ruleptr);
672         break;
673     }
674   }
675   return (errcode);
676 }
677
678 /*
679  * This function is recursive..  I wish I knew a nonrecursive way but
680  * I dont.  anyway, recursion is fun..  :)
681  * DO NOT CALL THIS FUNTION WITH A POINTER TO A NULL POINTER
682  * (ie: If *elem is NULL, you're doing it wrong - seg fault)
683  */
684 void crule_free(struct CRuleNode** elem)
685 {
686   int arg, numargs;
687
688   if ((*(elem))->funcptr == crule__not)
689   {
690     /* type conversions and ()'s are fun! ;)  here have an asprin.. */
691     if ((*(elem))->arg[0] != NULL)
692       crule_free((struct CRuleNode**) &((*(elem))->arg[0]));
693   }
694   else if ((*(elem))->funcptr == crule__andor)
695   {
696     crule_free((struct CRuleNode**) &((*(elem))->arg[0]));
697     if ((*(elem))->arg[1] != NULL)
698       crule_free((struct CRuleNode**) &((*(elem))->arg[1]));
699   }
700   else
701   {
702     numargs = (*(elem))->numargs;
703     for (arg = 0; arg < numargs; arg++)
704       MyFree((*(elem))->arg[arg]);
705   }
706 #ifdef CR_DEBUG
707   fprintf(stderr, "freeing element at %ld\n", *elem);
708 #endif
709   MyFree(*elem);
710   *elem = 0;
711 }
712
713 #ifdef CR_DEBUG
714 static void print_tree(CRuleNodePtr printelem)
715 {
716   int funcnum, arg;
717
718   if (printelem->funcptr == crule__not)
719   {
720     printf("!( ");
721     print_tree((CRuleNodePtr) printelem->arg[0]);
722     printf(") ");
723   }
724   else if (printelem->funcptr == crule__andor)
725   {
726     printf("( ");
727     print_tree((CRuleNodePtr) printelem->arg[0]);
728     if (printelem->arg[2])
729       printf("|| ");
730     else
731       printf("&& ");
732     print_tree((CRuleNodePtr) printelem->arg[1]);
733     printf(") ");
734   }
735   else
736   {
737     for (funcnum = 0;; funcnum++)
738     {
739       if (printelem->funcptr == crule_funclist[funcnum].funcptr)
740         break;
741       if (crule_funclist[funcnum].funcptr == NULL)
742         MyCoreDump;
743     }
744     printf("%s(", crule_funclist[funcnum].name);
745     for (arg = 0; arg < printelem->numargs; arg++)
746     {
747       if (arg != 0)
748         printf(",");
749       printf("%s", (char *)printelem->arg[arg]);
750     }
751     printf(") ");
752   }
753 }
754
755 #endif
756
757 #ifdef CR_DEBUG
758 int main(void)
759 {
760   char indata[256];
761   CRuleNode* rule;
762
763   printf("rule: ");
764   while (fgets(indata, 256, stdin) != NULL)
765   {
766     indata[strlen(indata) - 1] = '\0';  /* lose the newline */
767     if ((rule = crule_parse(indata)) != NULL)
768     {
769       printf("equivalent rule: ");
770       print_tree((CRuleNodePtr) rule);
771       printf("\n");
772       crule_free(&rule);
773     }
774     printf("\nrule: ");
775   }
776   printf("\n");
777
778   return 0;
779 }
780
781 #endif