Author: Bleep <helveytw@home.com>
[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 = acptr->next)
180   {
181     if (!IsServer(acptr) && !IsMe(acptr))
182       continue;
183     if (match((char *)crulearg[0], acptr->name))
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], acptr->name))
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 = acptr->next)
227   {
228     if (!IsServer(acptr) && !IsMe(acptr))
229       continue;
230     if (match((char *)crulearg[1], acptr->name))
231       continue;
232     if (match((char *)crulearg[0], (LocalClientArray[acptr->from->fd])->name))
233       continue;
234     return (1);
235   }
236   return (0);
237 }
238 #else
239 static int crule_via(int numargs, void **crulearg)
240 {
241   return (0);
242 }
243 #endif
244
245 static int crule_directop(int numargs, void **crulearg)
246 {
247 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
248   int i;
249   struct Client *acptr;
250
251   /* adapted from m_trace */
252   for (i = 0; i <= HighestFd; i++)
253   {
254     if (!(acptr = LocalClientArray[i]) || !IsAnOper(acptr))
255       continue;
256     return (1);
257   }
258 #endif
259   return (0);
260 }
261
262 static int crule__andor(int numargs, void *crulearg[])
263 {
264   int result1;
265
266   result1 = ((CRuleNodePtr) crulearg[0])->funcptr
267       (((CRuleNodePtr) crulearg[0])->numargs,
268       ((CRuleNodePtr) crulearg[0])->arg);
269   if (crulearg[2])              /* or */
270     return (result1 ||
271         ((CRuleNodePtr) crulearg[1])->funcptr
272         (((CRuleNodePtr) crulearg[1])->numargs,
273         ((CRuleNodePtr) crulearg[1])->arg));
274   else
275     return (result1 &&
276         ((CRuleNodePtr) crulearg[1])->funcptr
277         (((CRuleNodePtr) crulearg[1])->numargs,
278         ((CRuleNodePtr) crulearg[1])->arg));
279 }
280
281 static int crule__not(int numargs, void *crulearg[])
282 {
283   return (!((CRuleNodePtr) crulearg[0])->funcptr
284       (((CRuleNodePtr) crulearg[0])->numargs,
285       ((CRuleNodePtr) crulearg[0])->arg));
286 }
287
288 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
289 int crule_eval(struct CRuleNode* rule)
290 {
291   return (rule->funcptr(rule->numargs, rule->arg));
292 }
293 #endif
294
295 static int crule_gettoken(int* next_tokp, const char** ruleptr)
296 {
297   char pending = '\0';
298
299   *next_tokp = CR_UNKNOWN;
300   while (*next_tokp == CR_UNKNOWN)
301     switch (*(*ruleptr)++)
302     {
303       case ' ':
304       case '\t':
305         break;
306       case '&':
307         if (pending == '\0')
308           pending = '&';
309         else if (pending == '&')
310           *next_tokp = CR_AND;
311         else
312           return (CR_UNKNWTOK);
313         break;
314       case '|':
315         if (pending == '\0')
316           pending = '|';
317         else if (pending == '|')
318           *next_tokp = CR_OR;
319         else
320           return (CR_UNKNWTOK);
321         break;
322       case '!':
323         *next_tokp = CR_NOT;
324         break;
325       case '(':
326         *next_tokp = CR_OPENPAREN;
327         break;
328       case ')':
329         *next_tokp = CR_CLOSEPAREN;
330         break;
331       case ',':
332         *next_tokp = CR_COMMA;
333         break;
334       case '\0':
335         (*ruleptr)--;
336         *next_tokp = CR_END;
337         break;
338       case ':':
339         *next_tokp = CR_END;
340         break;
341       default:
342         if ((IsAlpha(*(--(*ruleptr)))) || (**ruleptr == '*') ||
343             (**ruleptr == '?') || (**ruleptr == '.') || (**ruleptr == '-'))
344           *next_tokp = CR_WORD;
345         else
346           return (CR_UNKNWTOK);
347         break;
348     }
349   return CR_NOERR;
350 }
351
352 static void crule_getword(char* word, int* wordlenp, size_t maxlen, const char** ruleptr)
353 {
354   char *word_ptr;
355
356   word_ptr = word;
357   while ((size_t)(word_ptr - word) < maxlen
358       && (IsAlnum(**ruleptr)
359       || **ruleptr == '*' || **ruleptr == '?'
360       || **ruleptr == '.' || **ruleptr == '-'))
361     *word_ptr++ = *(*ruleptr)++;
362   *word_ptr = '\0';
363   *wordlenp = word_ptr - word;
364 }
365
366 /*
367  * Grammar
368  *   rule:
369  *     orexpr END          END is end of input or :
370  *   orexpr:
371  *     andexpr
372  *     andexpr || orexpr
373  *   andexpr:
374  *     primary
375  *     primary && andexpr
376  *  primary:
377  *    function
378  *    ! primary
379  *    ( orexpr )
380  *  function:
381  *    word ( )             word is alphanumeric string, first character
382  *    word ( arglist )       must be a letter
383  *  arglist:
384  *    word
385  *    word , arglist
386  */
387 struct CRuleNode* crule_parse(const char *rule)
388 {
389   const char* ruleptr = rule;
390   int next_tok;
391   struct CRuleNode* ruleroot = 0;
392   int errcode = CR_NOERR;
393
394   if ((errcode = crule_gettoken(&next_tok, &ruleptr)) == CR_NOERR) {
395     if ((errcode = crule_parseorexpr(&ruleroot, &next_tok, &ruleptr)) == CR_NOERR) {
396       if (ruleroot != NULL) {
397         if (next_tok == CR_END)
398           return (ruleroot);
399         else
400           errcode = CR_UNEXPCTTOK;
401       }
402       else
403         errcode = CR_EXPCTOR;
404     }
405   }
406   if (ruleroot != NULL)
407     crule_free(&ruleroot);
408 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
409   Debug((DEBUG_ERROR, "%s in rule: %s", crule_errstr[errcode], rule));
410 #else
411   fprintf(stderr, "%s in rule: %s\n", crule_errstr[errcode], rule);
412 #endif
413   return 0;
414 }
415
416 static int crule_parseorexpr(CRuleNodePtr * orrootp, int *next_tokp, const char** ruleptr)
417 {
418   int errcode = CR_NOERR;
419   CRuleNodePtr andexpr;
420   CRuleNodePtr orptr;
421
422   *orrootp = NULL;
423   while (errcode == CR_NOERR)
424   {
425     errcode = crule_parseandexpr(&andexpr, next_tokp, ruleptr);
426     if ((errcode == CR_NOERR) && (*next_tokp == CR_OR))
427     {
428       orptr = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
429 #ifdef CR_DEBUG
430       fprintf(stderr, "allocating or element at %ld\n", orptr);
431 #endif
432       orptr->funcptr = crule__andor;
433       orptr->numargs = 3;
434       orptr->arg[2] = (void *)1;
435       if (*orrootp != NULL)
436       {
437         (*orrootp)->arg[1] = andexpr;
438         orptr->arg[0] = *orrootp;
439       }
440       else
441         orptr->arg[0] = andexpr;
442       *orrootp = orptr;
443     }
444     else
445     {
446       if (*orrootp != NULL)
447       {
448         if (andexpr != NULL)
449         {
450           (*orrootp)->arg[1] = andexpr;
451           return (errcode);
452         }
453         else
454         {
455           (*orrootp)->arg[1] = NULL;    /* so free doesn't seg fault */
456           return (CR_EXPCTAND);
457         }
458       }
459       else
460       {
461         *orrootp = andexpr;
462         return (errcode);
463       }
464     }
465     if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
466       return (errcode);
467   }
468   return (errcode);
469 }
470
471 static int crule_parseandexpr(CRuleNodePtr * androotp, int *next_tokp, const char** ruleptr)
472 {
473   int errcode = CR_NOERR;
474   CRuleNodePtr primary;
475   CRuleNodePtr andptr;
476
477   *androotp = NULL;
478   while (errcode == CR_NOERR)
479   {
480     errcode = crule_parseprimary(&primary, next_tokp, ruleptr);
481     if ((errcode == CR_NOERR) && (*next_tokp == CR_AND))
482     {
483       andptr = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
484 #ifdef CR_DEBUG
485       fprintf(stderr, "allocating and element at %ld\n", andptr);
486 #endif
487       andptr->funcptr = crule__andor;
488       andptr->numargs = 3;
489       andptr->arg[2] = (void *)0;
490       if (*androotp != NULL)
491       {
492         (*androotp)->arg[1] = primary;
493         andptr->arg[0] = *androotp;
494       }
495       else
496         andptr->arg[0] = primary;
497       *androotp = andptr;
498     }
499     else
500     {
501       if (*androotp != NULL)
502       {
503         if (primary != NULL)
504         {
505           (*androotp)->arg[1] = primary;
506           return (errcode);
507         }
508         else
509         {
510           (*androotp)->arg[1] = NULL;   /* so free doesn't seg fault */
511           return (CR_EXPCTPRIM);
512         }
513       }
514       else
515       {
516         *androotp = primary;
517         return (errcode);
518       }
519     }
520     if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
521       return (errcode);
522   }
523   return (errcode);
524 }
525
526 static int crule_parseprimary(CRuleNodePtr* primrootp, int *next_tokp, const char** ruleptr)
527 {
528   CRuleNodePtr *insertionp;
529   int errcode = CR_NOERR;
530
531   *primrootp = NULL;
532   insertionp = primrootp;
533   while (errcode == CR_NOERR)
534   {
535     switch (*next_tokp)
536     {
537       case CR_OPENPAREN:
538         if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
539           break;
540         if ((errcode = crule_parseorexpr(insertionp, next_tokp, ruleptr)) != CR_NOERR)
541           break;
542         if (*insertionp == NULL)
543         {
544           errcode = CR_EXPCTAND;
545           break;
546         }
547         if (*next_tokp != CR_CLOSEPAREN)
548         {
549           errcode = CR_EXPCTCLOSE;
550           break;
551         }
552         errcode = crule_gettoken(next_tokp, ruleptr);
553         break;
554       case CR_NOT:
555         *insertionp = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
556 #ifdef CR_DEBUG
557         fprintf(stderr, "allocating primary element at %ld\n", *insertionp);
558 #endif
559         (*insertionp)->funcptr = crule__not;
560         (*insertionp)->numargs = 1;
561         (*insertionp)->arg[0] = NULL;
562         insertionp = (CRuleNodePtr *) & ((*insertionp)->arg[0]);
563         if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
564           break;
565         continue;
566       case CR_WORD:
567         errcode = crule_parsefunction(insertionp, next_tokp, ruleptr);
568         break;
569       default:
570         if (*primrootp == NULL)
571           errcode = CR_NOERR;
572         else
573           errcode = CR_EXPCTPRIM;
574         break;
575     }
576     return (errcode);
577   }
578   return (errcode);
579 }
580
581 static int crule_parsefunction(CRuleNodePtr* funcrootp, int* next_tokp, const char** ruleptr)
582 {
583   int errcode = CR_NOERR;
584   char funcname[CR_MAXARGLEN];
585   int namelen;
586   int funcnum;
587
588   *funcrootp = NULL;
589   crule_getword(funcname, &namelen, CR_MAXARGLEN - 1, ruleptr);
590   if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
591     return (errcode);
592   if (*next_tokp == CR_OPENPAREN)
593   {
594     for (funcnum = 0;; funcnum++)
595     {
596       if (0 == ircd_strcmp(crule_funclist[funcnum].name, funcname))
597         break;
598       if (crule_funclist[funcnum].name[0] == '\0')
599         return (CR_UNKNWFUNC);
600     }
601     if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
602       return (errcode);
603     *funcrootp = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
604 #ifdef CR_DEBUG
605     fprintf(stderr, "allocating function element at %ld\n", *funcrootp);
606 #endif
607     (*funcrootp)->funcptr = NULL;       /* for freeing aborted trees */
608     if ((errcode =
609         crule_parsearglist(*funcrootp, next_tokp, ruleptr)) != CR_NOERR)
610       return (errcode);
611     if (*next_tokp != CR_CLOSEPAREN)
612       return (CR_EXPCTCLOSE);
613     if ((crule_funclist[funcnum].reqnumargs != (*funcrootp)->numargs) &&
614         (crule_funclist[funcnum].reqnumargs != -1))
615       return (CR_ARGMISMAT);
616     if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
617       return (errcode);
618     (*funcrootp)->funcptr = crule_funclist[funcnum].funcptr;
619     return (CR_NOERR);
620   }
621   else
622     return (CR_EXPCTOPEN);
623 }
624
625 static int crule_parsearglist(CRuleNodePtr argrootp, int *next_tokp, const char** ruleptr)
626 {
627   int errcode = CR_NOERR;
628   char *argelemp = NULL;
629   char currarg[CR_MAXARGLEN];
630   int arglen = 0;
631   char word[CR_MAXARGLEN];
632   int wordlen = 0;
633
634   argrootp->numargs = 0;
635   currarg[0] = '\0';
636   while (errcode == CR_NOERR)
637   {
638     switch (*next_tokp)
639     {
640       case CR_WORD:
641         crule_getword(word, &wordlen, CR_MAXARGLEN - 1, ruleptr);
642         if (currarg[0] != '\0')
643         {
644           if ((arglen + wordlen) < (CR_MAXARGLEN - 1))
645           {
646             strcat(currarg, " ");
647             strcat(currarg, word);
648             arglen += wordlen + 1;
649           }
650         }
651         else
652         {
653           strcpy(currarg, word);
654           arglen = wordlen;
655         }
656         errcode = crule_gettoken(next_tokp, ruleptr);
657         break;
658       default:
659 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
660         collapse(currarg);
661 #endif
662         if (!BadPtr(currarg))
663         {
664           DupString(argelemp, currarg);
665           argrootp->arg[argrootp->numargs++] = (void *)argelemp;
666         }
667         if (*next_tokp != CR_COMMA)
668           return (CR_NOERR);
669         currarg[0] = '\0';
670         errcode = crule_gettoken(next_tokp, ruleptr);
671         break;
672     }
673   }
674   return (errcode);
675 }
676
677 /*
678  * This function is recursive..  I wish I knew a nonrecursive way but
679  * I dont.  anyway, recursion is fun..  :)
680  * DO NOT CALL THIS FUNTION WITH A POINTER TO A NULL POINTER
681  * (ie: If *elem is NULL, you're doing it wrong - seg fault)
682  */
683 void crule_free(struct CRuleNode** elem)
684 {
685   int arg, numargs;
686
687   if ((*(elem))->funcptr == crule__not)
688   {
689     /* type conversions and ()'s are fun! ;)  here have an asprin.. */
690     if ((*(elem))->arg[0] != NULL)
691       crule_free((struct CRuleNode**) &((*(elem))->arg[0]));
692   }
693   else if ((*(elem))->funcptr == crule__andor)
694   {
695     crule_free((struct CRuleNode**) &((*(elem))->arg[0]));
696     if ((*(elem))->arg[1] != NULL)
697       crule_free((struct CRuleNode**) &((*(elem))->arg[1]));
698   }
699   else
700   {
701     numargs = (*(elem))->numargs;
702     for (arg = 0; arg < numargs; arg++)
703       MyFree((*(elem))->arg[arg]);
704   }
705 #ifdef CR_DEBUG
706   fprintf(stderr, "freeing element at %ld\n", *elem);
707 #endif
708   MyFree(*elem);
709   *elem = 0;
710 }
711
712 #ifdef CR_DEBUG
713 static void print_tree(CRuleNodePtr printelem)
714 {
715   int funcnum, arg;
716
717   if (printelem->funcptr == crule__not)
718   {
719     printf("!( ");
720     print_tree((CRuleNodePtr) printelem->arg[0]);
721     printf(") ");
722   }
723   else if (printelem->funcptr == crule__andor)
724   {
725     printf("( ");
726     print_tree((CRuleNodePtr) printelem->arg[0]);
727     if (printelem->arg[2])
728       printf("|| ");
729     else
730       printf("&& ");
731     print_tree((CRuleNodePtr) printelem->arg[1]);
732     printf(") ");
733   }
734   else
735   {
736     for (funcnum = 0;; funcnum++)
737     {
738       if (printelem->funcptr == crule_funclist[funcnum].funcptr)
739         break;
740       if (crule_funclist[funcnum].funcptr == NULL)
741         MyCoreDump;
742     }
743     printf("%s(", crule_funclist[funcnum].name);
744     for (arg = 0; arg < printelem->numargs; arg++)
745     {
746       if (arg != 0)
747         printf(",");
748       printf("%s", (char *)printelem->arg[arg]);
749     }
750     printf(") ");
751   }
752 }
753
754 #endif
755
756 #ifdef CR_DEBUG
757 int main(void)
758 {
759   char indata[256];
760   CRuleNode* rule;
761
762   printf("rule: ");
763   while (fgets(indata, 256, stdin) != NULL)
764   {
765     indata[strlen(indata) - 1] = '\0';  /* lose the newline */
766     if ((rule = crule_parse(indata)) != NULL)
767     {
768       printf("equivalent rule: ");
769       print_tree((CRuleNodePtr) rule);
770       printf("\n");
771       crule_free(&rule);
772     }
773     printf("\nrule: ");
774   }
775   printf("\n");
776
777   return 0;
778 }
779
780 #endif