unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
crule.c (23446B)
1 /** 2 * @file 3 * @brief Connection rule parser and checker 4 * @version $Id$ 5 * 6 * by Tony Vencill (Tonto on IRC) <vencill@bga.com> 7 * 8 * The majority of this file is a recursive descent parser used to convert 9 * connection rules into expression trees when the conf file is read. 10 * All parsing structures and types are hidden in the interest of good 11 * programming style and to make possible future data structure changes 12 * without affecting the interface between this patch and the rest of the 13 * server. The only functions accessible externally are crule_parse, 14 * crule_free, and crule_eval. Prototypes for these functions can be 15 * found in h.h. 16 * 17 * Please direct any connection rule or SmartRoute questions to Tonto on 18 * IRC or by email to vencill@bga.com. 19 * 20 * For parser testing, defining CR_DEBUG generates a stand-alone parser 21 * that takes rules from stdin and prints out memory allocation 22 * information and the parsed rule. This stand alone parser is ignorant 23 * of the irc server and thus cannot do rule evaluation. Do not define 24 * this flag when compiling the server! If you wish to generate the 25 * test parser, compile from the ircd directory with a line similar to 26 * cc -o parser -DCR_DEBUG crule.c 27 * 28 * The define CR_CHKCONF is provided to generate routines needed in 29 * chkconf. These consist of the parser, a different crule_parse that 30 * prints errors to stderr, and crule_free (just for good style and to 31 * more closely simulate the actual ircd environment). crule_eval and 32 * the rule functions are made empty functions as in the stand-alone 33 * test parser. 34 * 35 * The production rules for the grammar are as follows ("rule" is the 36 * starting production): 37 * 38 * rule: 39 * orexpr END END is end of input or : 40 * orexpr: 41 * andexpr 42 * andexpr || orexpr 43 * andexpr: 44 * primary 45 * primary && andexpr 46 * primary: 47 * function 48 * ! primary 49 * ( orexpr ) 50 * function: 51 * word ( ) word is alphanumeric string, first character 52 * word ( arglist ) must be a letter 53 * arglist: 54 * word 55 * word , arglist 56 */ 57 58 /* Last update of parser functions taken from ircu on 2023-03-19 59 * matching ircu's ircd/crule.c from 2021-09-04. 60 * Then ported / UnrealIRCd-ized by Syzop and re-adding crule_test() 61 * and such. All the actual "functions" like crule_connected() are 62 * our own and not re-feteched (but were based on older versions). 63 */ 64 65 #ifndef CR_DEBUG 66 /* ircd functions and types we need */ 67 #include "struct.h" 68 #include "common.h" 69 #include "sys.h" 70 #include "h.h" 71 #include <string.h> 72 73 char *collapse(char *pattern); 74 extern Client *client; 75 76 ID_Copyright("(C) Tony Vincell"); 77 78 #else 79 /* includes and defines to make the stand-alone test parser */ 80 #include <stdio.h> 81 #include <string.h> 82 #define BadPtr(x) (!(x) || (*(x) == '\0')) 83 #endif 84 85 #if defined(CR_DEBUG) || defined(CR_CHKCONF) 86 #undef safe_free 87 #undef free 88 #define safe_free free 89 #endif 90 91 /* 92 * Some symbols for easy reading 93 */ 94 95 /** Input scanner tokens. */ 96 enum crule_token { 97 CR_UNKNOWN, /**< Unknown token type. */ 98 CR_END, /**< End of input ('\\0' or ':'). */ 99 CR_AND, /**< Logical and operator (&&). */ 100 CR_OR, /**< Logical or operator (||). */ 101 CR_NOT, /**< Logical not operator (!). */ 102 CR_OPENPAREN, /**< Open parenthesis. */ 103 CR_CLOSEPAREN, /**< Close parenthesis. */ 104 CR_COMMA, /**< Comma. */ 105 CR_WORD /**< Something that looks like a hostmask (alphanumerics, "*?.-"). */ 106 }; 107 108 /** Parser error codes. */ 109 enum crule_errcode { 110 CR_NOERR, /**< No error. */ 111 CR_UNEXPCTTOK, /**< Invalid token given context. */ 112 CR_UNKNWTOK, /**< Input did not form a valid token. */ 113 CR_EXPCTAND, /**< Did not see expected && operator. */ 114 CR_EXPCTOR, /**< Did not see expected || operator. */ 115 CR_EXPCTPRIM, /**< Expected a primitive (parentheses, ! or word). */ 116 CR_EXPCTOPEN, /**< Expected an open parenthesis after function name. */ 117 CR_EXPCTCLOSE, /**< Expected a close parenthesis to match open parenthesis. */ 118 CR_UNKNWFUNC, /**< Attempt to use an unknown function. */ 119 CR_ARGMISMAT /**< Wrong number of arguments to function. */ 120 }; 121 122 /* 123 * Expression tree structure, function pointer, and tree pointer local! 124 */ 125 126 /* rule function prototypes - local! */ 127 static int crule_connected(int, void **); 128 static int crule_directcon(int, void **); 129 static int crule_via(int, void **); 130 static int crule_directop(int, void **); 131 static int crule__andor(int, void **); 132 static int crule__not(int, void **); 133 134 /* parsing function prototypes - local! */ 135 static int crule_gettoken(int* token, const char** str); 136 static void crule_getword(char*, int*, size_t, const char**); 137 static int crule_parseandexpr(CRuleNodePtr*, int *, const char**); 138 static int crule_parseorexpr(CRuleNodePtr*, int *, const char**); 139 static int crule_parseprimary(CRuleNodePtr*, int *, const char**); 140 static int crule_parsefunction(CRuleNodePtr*, int *, const char**); 141 static int crule_parsearglist(CRuleNodePtr, int *, const char**); 142 143 #if defined(CR_DEBUG) || defined(CR_CHKCONF) 144 /* 145 * Prototypes for the test parser; if not debugging, 146 * these are defined in h.h 147 */ 148 struct CRuleNode* crule_parse(const char*); 149 void crule_free(struct CRuleNode**); 150 #ifdef CR_DEBUG 151 void print_tree(CRuleNodePtr); 152 #endif 153 #endif 154 155 /* error messages */ 156 char *crule_errstr[] = { 157 "Unknown error", /* NOERR? - for completeness */ 158 "Unexpected token", /* UNEXPCTTOK */ 159 "Unknown token", /* UNKNWTOK */ 160 "And expr expected", /* EXPCTAND */ 161 "Or expr expected", /* EXPCTOR */ 162 "Primary expected", /* EXPCTPRIM */ 163 "( expected", /* EXPCTOPEN */ 164 ") expected", /* EXPCTCLOSE */ 165 "Unknown function", /* UNKNWFUNC */ 166 "Argument mismatch" /* ARGMISMAT */ 167 }; 168 169 /* function table - null terminated */ 170 struct crule_funclistent { 171 char name[15]; /* MAXIMUM FUNCTION NAME LENGTH IS 14 CHARS!! */ 172 int reqnumargs; 173 crule_funcptr funcptr; 174 }; 175 struct crule_funclistent crule_funclist[] = { 176 /* maximum function name length is 14 chars */ 177 {"connected", 1, crule_connected}, 178 {"directcon", 1, crule_directcon}, 179 {"via", 2, crule_via}, 180 {"directop", 0, crule_directop}, 181 {"", 0, NULL} /* this must be here to mark end of list */ 182 }; 183 184 static int crule_connected(int numargs, void *crulearg[]) 185 { 186 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF) 187 Client *client; 188 189 /* taken from cmd_links */ 190 /* Faster this way -- codemastr*/ 191 list_for_each_entry(client, &global_server_list, client_node) 192 { 193 if (!match_simple((char *)crulearg[0], client->name)) 194 continue; 195 return (1); 196 } 197 return (0); 198 #endif 199 } 200 201 static int crule_directcon(int numargs, void *crulearg[]) 202 { 203 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF) 204 Client *client; 205 206 /* adapted from cmd_trace and exit_one_client */ 207 /* XXX: iterate server_list when added */ 208 list_for_each_entry(client, &lclient_list, lclient_node) 209 { 210 if (!IsServer(client)) 211 continue; 212 if (!match_simple((char *)crulearg[0], client->name)) 213 continue; 214 return (1); 215 } 216 return (0); 217 #endif 218 } 219 220 static int crule_via(int numargs, void *crulearg[]) 221 { 222 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF) 223 Client *client; 224 225 /* adapted from cmd_links */ 226 /* Faster this way -- codemastr */ 227 list_for_each_entry(client, &global_server_list, client_node) 228 { 229 if (!match_simple((char *)crulearg[1], client->name)) 230 continue; 231 if (!match_simple((char *)crulearg[0], client->uplink->name)) 232 continue; 233 return (1); 234 } 235 return (0); 236 #endif 237 } 238 239 static int crule_directop(int numargs, void *crulearg[]) 240 { 241 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF) 242 Client *client; 243 244 /* adapted from cmd_trace */ 245 list_for_each_entry(client, &lclient_list, lclient_node) 246 { 247 if (!IsOper(client)) 248 continue; 249 250 return (1); 251 } 252 253 return (0); 254 #endif 255 } 256 257 /** Evaluate a connection rule. 258 * @param[in] rule Rule to evalute. 259 * @return Non-zero if the rule allows the connection, zero otherwise. 260 */ 261 int crule_eval(struct CRuleNode* rule) 262 { 263 return (rule->funcptr(rule->numargs, rule->arg)); 264 } 265 266 /** Perform an and-or-or test on crulearg[0] and crulearg[1]. 267 * If crulearg[2] is non-NULL, it means do OR; if it is NULL, do AND. 268 * @param[in] numargs Number of valid args in \a crulearg. 269 * @param[in] crulearg Argument array. 270 * @return Non-zero if the condition is true, zero if not. 271 */ 272 static int crule__andor(int numargs, void *crulearg[]) 273 { 274 int result1; 275 276 result1 = crule_eval(crulearg[0]); 277 if (crulearg[2]) /* or */ 278 return (result1 || crule_eval(crulearg[1])); 279 else 280 return (result1 && crule_eval(crulearg[1])); 281 } 282 283 /** Logically invert the result of crulearg[0]. 284 * @param[in] numargs Number of valid args in \a crulearg. 285 * @param[in] crulearg Argument array. 286 * @return Non-zero if the condition is true, zero if not. 287 */ 288 static int crule__not(int numargs, void *crulearg[]) 289 { 290 return (!crule_eval(crulearg[0])); 291 } 292 293 /** Scan an input token from \a ruleptr. 294 * @param[out] next_tokp Receives type of next token. 295 * @param[in,out] ruleptr Next readable character from input. 296 * @return Either CR_UNKNWTOK if the input was unrecognizable, else CR_NOERR. 297 */ 298 static int crule_gettoken(int* next_tokp, const char** ruleptr) 299 { 300 char pending = '\0'; 301 302 *next_tokp = CR_UNKNOWN; 303 while (*next_tokp == CR_UNKNOWN) 304 switch (*(*ruleptr)++) 305 { 306 case ' ': 307 case '\t': 308 break; 309 case '&': 310 if (pending == '\0') 311 pending = '&'; 312 else if (pending == '&') 313 *next_tokp = CR_AND; 314 else 315 return (CR_UNKNWTOK); 316 break; 317 case '|': 318 if (pending == '\0') 319 pending = '|'; 320 else if (pending == '|') 321 *next_tokp = CR_OR; 322 else 323 return (CR_UNKNWTOK); 324 break; 325 case '!': 326 *next_tokp = CR_NOT; 327 break; 328 case '(': 329 *next_tokp = CR_OPENPAREN; 330 break; 331 case ')': 332 *next_tokp = CR_CLOSEPAREN; 333 break; 334 case ',': 335 *next_tokp = CR_COMMA; 336 break; 337 case '\0': 338 (*ruleptr)--; 339 *next_tokp = CR_END; 340 break; 341 case ':': 342 *next_tokp = CR_END; 343 break; 344 default: 345 if ((isalpha(*(--(*ruleptr)))) || (**ruleptr == '*') || 346 (**ruleptr == '?') || (**ruleptr == '.') || (**ruleptr == '-')) 347 *next_tokp = CR_WORD; 348 else 349 return (CR_UNKNWTOK); 350 break; 351 } 352 return CR_NOERR; 353 } 354 355 /** Scan a word from \a ruleptr. 356 * @param[out] word Output buffer. 357 * @param[out] wordlenp Length of word written to \a word (not including terminating NUL). 358 * @param[in] maxlen Maximum number of bytes writable to \a word. 359 * @param[in,out] ruleptr Next readable character from input. 360 */ 361 static void crule_getword(char* word, int* wordlenp, size_t maxlen, const char** ruleptr) 362 { 363 char *word_ptr; 364 365 word_ptr = word; 366 while ((size_t)(word_ptr - word) < maxlen 367 && (isalnum(**ruleptr) 368 || **ruleptr == '*' || **ruleptr == '?' 369 || **ruleptr == '.' || **ruleptr == '-')) 370 *word_ptr++ = *(*ruleptr)++; 371 *word_ptr = '\0'; 372 *wordlenp = word_ptr - word; 373 } 374 375 /** Parse an entire rule. 376 * @param[in] rule Text form of rule. 377 * @return CRuleNode for rule, or NULL if there was a parse error. 378 */ 379 struct CRuleNode* crule_parse(const char *rule) 380 { 381 const char* ruleptr = rule; 382 int next_tok; 383 struct CRuleNode* ruleroot = 0; 384 int errcode = CR_NOERR; 385 386 if ((errcode = crule_gettoken(&next_tok, &ruleptr)) == CR_NOERR) { 387 if ((errcode = crule_parseorexpr(&ruleroot, &next_tok, &ruleptr)) == CR_NOERR) { 388 if (ruleroot != NULL) { 389 if (next_tok == CR_END) 390 return (ruleroot); 391 else 392 errcode = CR_UNEXPCTTOK; 393 } 394 else 395 errcode = CR_EXPCTOR; 396 } 397 } 398 if (ruleroot != NULL) 399 crule_free(&ruleroot); 400 #if defined(CR_DEBUG) || defined(CR_CHKCONF) 401 fprintf(stderr, "%s in rule: %s\n", crule_errstr[errcode], rule); 402 #endif 403 return 0; 404 } 405 406 /** Test-parse an entire rule. 407 * @param[in] rule Text form of rule. 408 * @return error code, or 0 for no failure 409 */ 410 int crule_test(const char *rule) 411 { 412 const char* ruleptr = rule; 413 int next_tok; 414 struct CRuleNode* ruleroot = 0; 415 int errcode = CR_NOERR; 416 417 if ((errcode = crule_gettoken(&next_tok, &ruleptr)) == CR_NOERR) { 418 if ((errcode = crule_parseorexpr(&ruleroot, &next_tok, &ruleptr)) == CR_NOERR) { 419 if (ruleroot != NULL) { 420 if (next_tok == CR_END) 421 { 422 /* PASS */ 423 crule_free(&ruleroot); 424 return 0; 425 } else { 426 errcode = CR_UNEXPCTTOK; 427 } 428 } 429 else 430 errcode = CR_EXPCTOR; 431 } 432 } 433 if (ruleroot != NULL) 434 crule_free(&ruleroot); 435 #if defined(CR_DEBUG) || defined(CR_CHKCONF) 436 fprintf(stderr, "%s in rule: %s\n", crule_errstr[errcode], rule); 437 #endif 438 return errcode + 1; 439 } 440 441 const char *crule_errstring(int errcode) 442 { 443 if (errcode == 0) 444 return "No error"; 445 else 446 return crule_errstr[errcode-1]; 447 } 448 449 /** Parse an or expression. 450 * @param[out] orrootp Receives parsed node. 451 * @param[in,out] next_tokp Next input token type. 452 * @param[in,out] ruleptr Next input character. 453 * @return A crule_errcode value. 454 */ 455 static int crule_parseorexpr(CRuleNodePtr * orrootp, int *next_tokp, const char** ruleptr) 456 { 457 int errcode = CR_NOERR; 458 CRuleNodePtr andexpr; 459 CRuleNodePtr orptr; 460 461 *orrootp = NULL; 462 while (errcode == CR_NOERR) 463 { 464 errcode = crule_parseandexpr(&andexpr, next_tokp, ruleptr); 465 if ((errcode == CR_NOERR) && (*next_tokp == CR_OR)) 466 { 467 orptr = safe_alloc(sizeof(struct CRuleNode)); 468 #ifdef CR_DEBUG 469 fprintf(stderr, "allocating or element at %ld\n", orptr); 470 #endif 471 orptr->funcptr = crule__andor; 472 orptr->numargs = 3; 473 orptr->arg[2] = (void *)1; 474 if (*orrootp != NULL) 475 { 476 (*orrootp)->arg[1] = andexpr; 477 orptr->arg[0] = *orrootp; 478 } 479 else 480 orptr->arg[0] = andexpr; 481 *orrootp = orptr; 482 } 483 else 484 { 485 if (*orrootp != NULL) 486 { 487 if (andexpr != NULL) 488 { 489 (*orrootp)->arg[1] = andexpr; 490 return (errcode); 491 } 492 else 493 { 494 (*orrootp)->arg[1] = NULL; /* so free doesn't seg fault */ 495 return (CR_EXPCTAND); 496 } 497 } 498 else 499 { 500 *orrootp = andexpr; 501 return (errcode); 502 } 503 } 504 errcode = crule_gettoken(next_tokp, ruleptr); 505 } 506 return (errcode); 507 } 508 509 /** Parse an and expression. 510 * @param[out] androotp Receives parsed node. 511 * @param[in,out] next_tokp Next input token type. 512 * @param[in,out] ruleptr Next input character. 513 * @return A crule_errcode value. 514 */ 515 static int crule_parseandexpr(CRuleNodePtr * androotp, int *next_tokp, const char** ruleptr) 516 { 517 int errcode = CR_NOERR; 518 CRuleNodePtr primary; 519 CRuleNodePtr andptr; 520 521 *androotp = NULL; 522 while (errcode == CR_NOERR) 523 { 524 errcode = crule_parseprimary(&primary, next_tokp, ruleptr); 525 if ((errcode == CR_NOERR) && (*next_tokp == CR_AND)) 526 { 527 andptr = safe_alloc(sizeof(struct CRuleNode)); 528 #ifdef CR_DEBUG 529 fprintf(stderr, "allocating and element at %ld\n", andptr); 530 #endif 531 andptr->funcptr = crule__andor; 532 andptr->numargs = 3; 533 andptr->arg[2] = (void *)0; 534 if (*androotp != NULL) 535 { 536 (*androotp)->arg[1] = primary; 537 andptr->arg[0] = *androotp; 538 } 539 else 540 andptr->arg[0] = primary; 541 *androotp = andptr; 542 } 543 else 544 { 545 if (*androotp != NULL) 546 { 547 if (primary != NULL) 548 { 549 (*androotp)->arg[1] = primary; 550 return (errcode); 551 } 552 else 553 { 554 (*androotp)->arg[1] = NULL; /* so free doesn't seg fault */ 555 return (CR_EXPCTPRIM); 556 } 557 } 558 else 559 { 560 *androotp = primary; 561 return (errcode); 562 } 563 } 564 errcode = crule_gettoken(next_tokp, ruleptr); 565 } 566 return (errcode); 567 } 568 569 /** Parse a primary expression. 570 * @param[out] primrootp Receives parsed node. 571 * @param[in,out] next_tokp Next input token type. 572 * @param[in,out] ruleptr Next input character. 573 * @return A crule_errcode value. 574 */ 575 static int crule_parseprimary(CRuleNodePtr* primrootp, int *next_tokp, const char** ruleptr) 576 { 577 CRuleNodePtr *insertionp; 578 int errcode = CR_NOERR; 579 580 *primrootp = NULL; 581 insertionp = primrootp; 582 while (errcode == CR_NOERR) 583 { 584 switch (*next_tokp) 585 { 586 case CR_OPENPAREN: 587 if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR) 588 break; 589 if ((errcode = crule_parseorexpr(insertionp, next_tokp, ruleptr)) != CR_NOERR) 590 break; 591 if (*insertionp == NULL) 592 { 593 errcode = CR_EXPCTAND; 594 break; 595 } 596 if (*next_tokp != CR_CLOSEPAREN) 597 { 598 errcode = CR_EXPCTCLOSE; 599 break; 600 } 601 errcode = crule_gettoken(next_tokp, ruleptr); 602 break; 603 case CR_NOT: 604 *insertionp = safe_alloc(sizeof(struct CRuleNode)); 605 #ifdef CR_DEBUG 606 fprintf(stderr, "allocating primary element at %ld\n", *insertionp); 607 #endif 608 (*insertionp)->funcptr = crule__not; 609 (*insertionp)->numargs = 1; 610 (*insertionp)->arg[0] = NULL; 611 insertionp = (CRuleNodePtr *) & ((*insertionp)->arg[0]); 612 if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR) 613 break; 614 continue; 615 case CR_WORD: 616 errcode = crule_parsefunction(insertionp, next_tokp, ruleptr); 617 break; 618 default: 619 if (*primrootp == NULL) 620 errcode = CR_NOERR; 621 else 622 errcode = CR_EXPCTPRIM; 623 break; 624 } 625 break; /* loop only continues after a CR_NOT */ 626 } 627 return (errcode); 628 } 629 630 /** Parse a function call. 631 * @param[out] funcrootp Receives parsed node. 632 * @param[in,out] next_tokp Next input token type. 633 * @param[in,out] ruleptr Next input character. 634 * @return A crule_errcode value. 635 */ 636 static int crule_parsefunction(CRuleNodePtr* funcrootp, int* next_tokp, const char** ruleptr) 637 { 638 int errcode = CR_NOERR; 639 char funcname[CR_MAXARGLEN]; 640 int namelen; 641 int funcnum; 642 643 *funcrootp = NULL; 644 crule_getword(funcname, &namelen, CR_MAXARGLEN - 1, ruleptr); 645 if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR) 646 return (errcode); 647 if (*next_tokp == CR_OPENPAREN) 648 { 649 for (funcnum = 0;; funcnum++) 650 { 651 if (0 == strcasecmp(crule_funclist[funcnum].name, funcname)) 652 break; 653 if (crule_funclist[funcnum].name[0] == '\0') 654 return (CR_UNKNWFUNC); 655 } 656 if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR) 657 return (errcode); 658 *funcrootp = safe_alloc(sizeof(struct CRuleNode)); 659 #ifdef CR_DEBUG 660 fprintf(stderr, "allocating function element at %ld\n", *funcrootp); 661 #endif 662 (*funcrootp)->funcptr = NULL; /* for freeing aborted trees */ 663 if ((errcode = 664 crule_parsearglist(*funcrootp, next_tokp, ruleptr)) != CR_NOERR) 665 return (errcode); 666 if (*next_tokp != CR_CLOSEPAREN) 667 return (CR_EXPCTCLOSE); 668 if ((crule_funclist[funcnum].reqnumargs != (*funcrootp)->numargs) && 669 (crule_funclist[funcnum].reqnumargs != -1)) 670 return (CR_ARGMISMAT); 671 if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR) 672 return (errcode); 673 (*funcrootp)->funcptr = crule_funclist[funcnum].funcptr; 674 return (CR_NOERR); 675 } 676 else 677 return (CR_EXPCTOPEN); 678 } 679 680 /** Parse the argument list to a CRuleNode. 681 * @param[in,out] argrootp Node whos argument list is being populated. 682 * @param[in,out] next_tokp Next input token type. 683 * @param[in,out] ruleptr Next input character. 684 * @return A crule_errcode value. 685 */ 686 static int crule_parsearglist(CRuleNodePtr argrootp, int *next_tokp, const char** ruleptr) 687 { 688 int errcode = CR_NOERR; 689 char *argelemp = NULL; 690 char currarg[CR_MAXARGLEN]; 691 int arglen = 0; 692 char word[CR_MAXARGLEN]; 693 int wordlen = 0; 694 695 argrootp->numargs = 0; 696 currarg[0] = '\0'; 697 while (errcode == CR_NOERR) 698 { 699 switch (*next_tokp) 700 { 701 case CR_WORD: 702 crule_getword(word, &wordlen, CR_MAXARGLEN - 1, ruleptr); 703 if (currarg[0] != '\0') 704 { 705 if ((arglen + wordlen) < (CR_MAXARGLEN - 1)) 706 { 707 strcat(currarg, " "); 708 strcat(currarg, word); 709 arglen += wordlen + 1; 710 } 711 } 712 else 713 { 714 strcpy(currarg, word); 715 arglen = wordlen; 716 } 717 errcode = crule_gettoken(next_tokp, ruleptr); 718 break; 719 default: 720 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF) 721 collapse(currarg); 722 #endif 723 if (currarg[0] != '\0') 724 { 725 argelemp = raw_strdup(currarg); 726 argrootp->arg[argrootp->numargs++] = (void *)argelemp; 727 } 728 if (*next_tokp != CR_COMMA) 729 return (CR_NOERR); 730 currarg[0] = '\0'; 731 errcode = crule_gettoken(next_tokp, ruleptr); 732 break; 733 } 734 } 735 return (errcode); 736 } 737 738 /* 739 * This function is recursive.. I wish I knew a nonrecursive way but 740 * I don't. Anyway, recursion is fun.. :) 741 * DO NOT CALL THIS FUNCTION WITH A POINTER TO A NULL POINTER 742 * (i.e.: If *elem is NULL, you're doing it wrong - seg fault) 743 */ 744 /** Free a connection rule and all its children. 745 * @param[in,out] elem Pointer to pointer to element to free. MUST NOT BE NULL. 746 */ 747 void crule_free(struct CRuleNode** elem) 748 { 749 int arg, numargs; 750 751 if ((*(elem))->funcptr == crule__not) 752 { 753 /* type conversions and ()'s are fun! ;) here have an aspirin.. */ 754 if ((*(elem))->arg[0] != NULL) 755 crule_free((struct CRuleNode**) &((*(elem))->arg[0])); 756 } 757 else if ((*(elem))->funcptr == crule__andor) 758 { 759 crule_free((struct CRuleNode**) &((*(elem))->arg[0])); 760 if ((*(elem))->arg[1] != NULL) 761 crule_free((struct CRuleNode**) &((*(elem))->arg[1])); 762 } 763 else 764 { 765 numargs = (*(elem))->numargs; 766 for (arg = 0; arg < numargs; arg++) 767 safe_free((*(elem))->arg[arg]); 768 } 769 #ifdef CR_DEBUG 770 fprintf(stderr, "freeing element at %ld\n", *elem); 771 #endif 772 safe_free(*elem); 773 *elem = 0; 774 } 775 776 #ifdef CR_DEBUG 777 /** Display a connection rule as text. 778 * @param[in] printelem Connection rule to display. 779 */ 780 static void print_tree(CRuleNodePtr printelem) 781 { 782 int funcnum, arg; 783 784 if (printelem->funcptr == crule__not) 785 { 786 printf("!( "); 787 print_tree((CRuleNodePtr) printelem->arg[0]); 788 printf(") "); 789 } 790 else if (printelem->funcptr == crule__andor) 791 { 792 printf("( "); 793 print_tree((CRuleNodePtr) printelem->arg[0]); 794 if (printelem->arg[2]) 795 printf("|| "); 796 else 797 printf("&& "); 798 print_tree((CRuleNodePtr) printelem->arg[1]); 799 printf(") "); 800 } 801 else 802 { 803 for (funcnum = 0;; funcnum++) 804 { 805 if (printelem->funcptr == crule_funclist[funcnum].funcptr) 806 break; 807 if (crule_funclist[funcnum].funcptr == NULL) 808 MyCoreDump; 809 } 810 printf("%s(", crule_funclist[funcnum].name); 811 for (arg = 0; arg < printelem->numargs; arg++) 812 { 813 if (arg != 0) 814 printf(","); 815 printf("%s", (char *)printelem->arg[arg]); 816 } 817 printf(") "); 818 } 819 } 820 821 #endif 822 823 #ifdef CR_DEBUG 824 /** Read connection rules from stdin and display parsed forms as text. 825 * @return Zero. 826 */ 827 int main(void) 828 { 829 char indata[256]; 830 CRuleNode* rule; 831 832 printf("rule: "); 833 while (fgets(indata, 256, stdin) != NULL) 834 { 835 indata[strlen(indata) - 1] = '\0'; /* lose the newline */ 836 if ((rule = crule_parse(indata)) != NULL) 837 { 838 printf("equivalent rule: "); 839 print_tree((CRuleNodePtr) rule); 840 printf("\n"); 841 crule_free(&rule); 842 } 843 printf("\nrule: "); 844 } 845 printf("\n"); 846 847 return 0; 848 } 849 850 #endif