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