1 | /****************** 2 | Copyright (c) 2002 RIPE NCC 3 | 4 | All Rights Reserved 5 | 6 | Permission to use, copy, modify, and distribute this software and its 7 | documentation for any purpose and without fee is hereby granted, 8 | provided that the above copyright notice appear in all copies and that 9 | both that copyright notice and this permission notice appear in 10 | supporting documentation, and that the name of the author not be 11 | used in advertising or publicity pertaining to distribution of the 12 | software without specific, written prior permission. 13 | 14 | THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 15 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL 16 | AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY 17 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 18 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 | ***************************************/ 21 | 22 | #include <ctype.h> 23 | #include <string.h> 24 | #include <stdlib.h> 25 | #include <stdarg.h> 26 | #include <glib.h> 27 | #include <pthread.h> 28 | #include "syntax_api.h" 29 | #include "syntax.h" 30 | #include "attribute.h" 31 | #include "class.h" 32 | 33 | #include <assert.h> 34 | 35 | #define RUNTIME_CHECK 0 36 | 37 | #if RUNTIME_CHECK 38 | #include <stdio.h> 39 | #define chk_obj(o) rpsl_object_check((o),__FILE__,__LINE__) 40 | #define chk_attr(a) rpsl_attr_check((a),__FILE__,__LINE__) 41 | #define chk_err(e) rpsl_error_check((e),__FILE__,__LINE__) 42 | 43 | void rpsl_error_check(const GList *errors, const char *file, int line); 44 | void rpsl_attr_check(const rpsl_attr_t *attr, const char *file, int line); 45 | void rpsl_object_check(const rpsl_object_t *obj, const char *file, int line); 46 | #else 47 | #define chk_obj(o) 48 | #define chk_attr(a) 49 | #define chk_err(e) 50 | #endif /* RUNTIME_CHECK */ 51 | 52 | /* type of check currently in place */ 53 | static int rpsl_level = RPSL_DICT_FRONT_END; 54 | 55 | /* return true if the character is an RPSL line-continuation */ 56 | #define is_rpsl_line_cont(c) (((c)==' ') || ((c) == '\t') || ((c) == '+')) 57 | 58 | /* needed by hash table */ 59 | static guint 60 | my_g_str_hash (gconstpointer v) 61 | { 62 | gchar *s; 63 | guint hash; 64 | 65 | s = g_strdup(v); 66 | g_strdown(s); 67 | hash = g_str_hash(s); 68 | g_free(s); 69 | return hash; 70 | } 71 | 72 | static gint 73 | my_g_strcasecmp (gconstpointer a, gconstpointer b) 74 | { 75 | return (g_strcasecmp(a, b) == 0); 76 | } 77 | 78 | 79 | /* remove comments, join lines, and compress whitespace */ 80 | static gchar * 81 | attribute_clean (const gchar *val) 82 | { 83 | gchar **lines; 84 | int i; 85 | gchar *p; 86 | gchar *q; 87 | gchar *ret_val; 88 | 89 | /* split our value up into lines */ 90 | lines = g_strsplit(val, "\n", 0); 91 | 92 | for (i=0; lines[i] != NULL; i++) { 93 | /* remove comments */ 94 | p = strchr(lines[i], '#'); 95 | if (p != NULL) { 96 | *p = '\0'; 97 | } 98 | 99 | /* convert line continuation characters to whitespace */ 100 | if (i > 0) { 101 | /* XXX: error in attribute */ 102 | assert(is_rpsl_line_cont(lines[i][0])); 103 | lines[i][0] = ' '; 104 | } 105 | } 106 | 107 | /* join the lines into a single string */ 108 | ret_val = g_strjoinv("", lines); 109 | g_strfreev(lines); 110 | 111 | /* we read from p, and write to q */ 112 | p = ret_val; 113 | q = ret_val; 114 | 115 | /* skip leading whitespace */ 116 | while (isspace((int)*p)) { 117 | p++; 118 | } 119 | 120 | /* convert all series of whitespace into a single space */ 121 | /* (this will happily convert '\n' into ' ') */ 122 | while (*p != '\0') { 123 | if (isspace((int)*p)) { 124 | *q = ' '; 125 | q++; 126 | do { 127 | p++; 128 | } while (isspace((int)*p)); 129 | } else { 130 | *q = *p; 131 | q++; 132 | p++; 133 | } 134 | } 135 | 136 | /* remove any trailing whitespace (there will be at most one space, 137 | because of the whitespace compression already performed above) */ 138 | if ((q > ret_val) && isspace((int)*(q-1))) { 139 | q--; 140 | } 141 | 142 | /* NUL-terminate our string */ 143 | *q = '\0'; 144 | 145 | /* return our new line */ 146 | return (gchar *)ret_val; 147 | } 148 | 149 | /* remove comments, clean lines, and compress whitespace */ 150 | static gchar * 151 | attribute_clean_lines (const gchar *val) 152 | { 153 | gchar **lines; 154 | int i; 155 | gchar *p; 156 | gchar *q; 157 | gchar *ret_val; 158 | 159 | /* split our value up into lines */ 160 | lines = g_strsplit(val, "\n", 0); 161 | 162 | /* clean each line separately */ 163 | for (i=0; lines[i] != NULL; i++) { 164 | /* remove comments */ 165 | p = strchr(lines[i], '#'); 166 | if (p != NULL) { 167 | *p = '\0'; 168 | } 169 | 170 | /* convert line continuation characters to whitespace */ 171 | if (i > 0) { 172 | /* XXX: error in attribute */ 173 | assert(is_rpsl_line_cont(lines[i][0])); 174 | lines[i][0] = ' '; 175 | } 176 | 177 | /* we read from p, and write to q, for whitespace compression */ 178 | p = lines[i]; 179 | q = lines[i]; 180 | 181 | /* skip leading whitespace */ 182 | while (isspace((int)*p)) { 183 | p++; 184 | } 185 | 186 | /* convert all series of whitespace into a single space */ 187 | /* (this will happily convert '\n' into ' ') */ 188 | while (*p != '\0') { 189 | if (isspace((int)*p)) { 190 | *q = ' '; 191 | q++; 192 | do { 193 | p++; 194 | } while (isspace((int)*p)); 195 | } else { 196 | *q = *p; 197 | q++; 198 | p++; 199 | } 200 | } 201 | 202 | /* remove any trailing whitespace (there will be at most one space, 203 | because of the whitespace compression already performed above) */ 204 | if ((q > lines[i]) && isspace((int)*(q-1))) { 205 | q--; 206 | } 207 | 208 | /* NUL-terminate our line */ 209 | *q = '\0'; 210 | } 211 | 212 | /* join the lines into a single string */ 213 | ret_val = g_strjoinv("\n", lines); 214 | g_strfreev(lines); 215 | 216 | /* return our new line */ 217 | return (gchar *)ret_val; 218 | } 219 | 220 | /* see if the given string ends with the other string */ 221 | static gboolean 222 | str_ends_with (const char *s1, const char *s2) 223 | { 224 | int s1_len; 225 | int s2_len; 226 | 227 | s1_len = strlen(s1); 228 | s2_len = strlen(s2); 229 | if (s1_len < s2_len) { 230 | return FALSE; 231 | } 232 | if (strcmp(s1 + s1_len - s2_len, s2) == 0) { 233 | return TRUE; 234 | } else { 235 | return FALSE; 236 | } 237 | } 238 | 239 | /* return each element in the list as a seperate gchar* */ 240 | /* be sure to call g_strfreev() when done with this result!!! */ 241 | static gchar ** 242 | generic_list_split (const char *val, const char *separator_char) 243 | { 244 | gchar *tmp_str; 245 | gchar **ret_val; 246 | gboolean has_empty_last_element; 247 | int i; 248 | int list_size; 249 | 250 | /* clean up to remove comments and newlines */ 251 | tmp_str = attribute_clean(val); 252 | 253 | /* check for empty last element, which g_strsplit() will silently drop */ 254 | has_empty_last_element = str_ends_with(tmp_str, separator_char); 255 | 256 | /* split based on separator character */ 257 | ret_val = g_strsplit(tmp_str, separator_char, 0); 258 | 259 | /* free our temporary variable */ 260 | g_free(tmp_str); 261 | 262 | /* clean whitespace from each element */ 263 | list_size = 0; 264 | for (i=0; ret_val[i] != NULL; i++) { 265 | g_strstrip(ret_val[i]); 266 | list_size++; 267 | } 268 | 269 | /* if we have an empty last element, add it back in */ 270 | if (has_empty_last_element) { 271 | ret_val = g_renew(gchar*, ret_val, list_size+2); 272 | ret_val[list_size] = g_strdup(""); 273 | ret_val[list_size+1] = NULL; 274 | } 275 | 276 | /* return our array */ 277 | return ret_val; 278 | } 279 | 280 | 281 | static gchar ** 282 | attribute_list_split (const char *val) 283 | { 284 | return generic_list_split(val, ","); 285 | } 286 | 287 | static gchar ** 288 | ripe_list_split (const char *val) 289 | { 290 | /* 291 | We can call generic_list_split() because it calls 292 | attribute_clean() before using g_strsplit() to divide the string, 293 | and attribute_clean() converts any runs of whitespace into a 294 | single space. 295 | */ 296 | return generic_list_split(val, " "); 297 | } 298 | 299 | static void 300 | rpsl_error_assign (rpsl_error_t *error, 301 | gint level, 302 | gint code, 303 | gchar *descr_fmt, 304 | ...) 305 | { 306 | va_list args; 307 | 308 | if (error != NULL) { 309 | error->level = level; 310 | error->code = code; 311 | va_start(args, descr_fmt); 312 | error->descr = g_strdup_vprintf(descr_fmt, args); 313 | va_end(args); 314 | error->attr_num = -1; 315 | } 316 | } 317 | 318 | static void 319 | rpsl_error_add (GList **errors, gint level, gint code, gint attr_num, 320 | gchar *descr_fmt, ...) 321 | { 322 | rpsl_error_t *error; 323 | va_list args; 324 | 325 | error = g_new(rpsl_error_t, 1); 326 | error->level = level; 327 | error->code = code; 328 | va_start(args, descr_fmt); 329 | error->descr = g_strdup_vprintf(descr_fmt, args); 330 | va_end(args); 331 | error->attr_num = attr_num; 332 | *errors = g_list_append(*errors, error); 333 | } 334 | 335 | /* returns TRUE if okay, else FALSE */ 336 | static gboolean 337 | rpsl_attr_syntax_check (const attribute_t *attr_info, 338 | const gchar *value, 339 | GList **errors) 340 | { 341 | int level; 342 | gchar **split_val; 343 | int i; 344 | int error_cnt; 345 | GPtrArray *parse_errors; 346 | gchar *parse_descr; 347 | 348 | /* set up for exit */ 349 | split_val = NULL; 350 | 351 | /* set our syntax checking level */ 352 | if (rpsl_level == RPSL_DICT_CORE) { 353 | level = SYNTAX_CHECK_CORE; 354 | } else { 355 | level = SYNTAX_CHECK_FRONT_END; 356 | } 357 | 358 | error_cnt = 0; 359 | 360 | /* check the syntax */ 361 | if ((attr_info->is_list) || (attr_info->is_ripe_list)) { 362 | if (attr_info->is_list) { 363 | split_val = attribute_list_split(value); 364 | } else { 365 | split_val = ripe_list_split(value); 366 | } 367 | if (split_val[0] == NULL) { 368 | rpsl_error_add(errors, 369 | RPSL_ERRLVL_ERROR, 370 | RPSL_ERR_EMPTYLIST, 371 | -1, 372 | "Attribute \"%s\" has an empty list", 373 | attr_info->name); 374 | goto exit_rpsl_syntax; 375 | } 376 | } else { 377 | split_val = g_new(gchar*, 2); 378 | split_val[0] = attribute_clean(value); 379 | split_val[1] = NULL; 380 | } 381 | 382 | for (i=0; split_val[i] != NULL; i++) { 383 | parse_errors = syntax_check_by_offset(attr_info->syntax_offset, 384 | level, 385 | split_val[i]); 386 | error_cnt += parse_errors->len; 387 | while (parse_errors->len > 0) { 388 | parse_descr = g_ptr_array_remove_index(parse_errors, 0); 389 | rpsl_error_add(errors, 390 | RPSL_ERRLVL_ERROR, 391 | RPSL_ERR_SYNERR, 392 | -1, 393 | "%s", 394 | parse_descr); 395 | g_free(parse_descr); 396 | } 397 | g_ptr_array_free(parse_errors, TRUE); 398 | } 399 | 400 | exit_rpsl_syntax: 401 | if (split_val != NULL) { 402 | g_strfreev(split_val); 403 | } 404 | if (error_cnt == 0) { 405 | return TRUE; 406 | } else { 407 | return FALSE; 408 | } 409 | } 410 | 411 | /* 412 | returns NULL on *coding errors* 413 | non-existant class specified 414 | attribute does not exist for class 415 | attribute without class in ambiguous 416 | returns a structure otherwise 417 | on *syntax errors* errors are in the rpsl_attr_t structure 418 | */ 419 | 420 | /* XXX: there should be a way to preserve the original text, so 421 | that garbage attributes still retain meaning 422 | */ 423 | rpsl_attr_t * 424 | rpsl_attr_init (const gchar *s, const gchar *class) 425 | { 426 | rpsl_attr_t *retval; 427 | gchar **attr_val; 428 | const class_t *class_info; 429 | const class_attr_t *class_attr_info; 430 | const attribute_t *attr_info; 431 | gboolean is_ambiguous; 432 | 433 | /* return NULL if string is empty */ 434 | if ((s == NULL) || (s[0] == '\0')) { 435 | return NULL; 436 | } 437 | 438 | /* get class info as early as possible */ 439 | if (class != NULL) { 440 | class_info = class_lookup(class); 441 | if (class_info == NULL) { 442 | return NULL; 443 | } 444 | } else { 445 | class_info = NULL; 446 | } 447 | 448 | /* initialize the structure */ 449 | retval = g_new(rpsl_attr_t, 1); 450 | retval->name = NULL; 451 | retval->lcase_name = NULL; 452 | retval->value = NULL; 453 | retval->errors = NULL; 454 | retval->num = -1; 455 | retval->attr_info = NULL; 456 | 457 | /* prepare for early return */ 458 | attr_val = NULL; 459 | 460 | /* split into attribute and value */ 461 | /* g_strsplit() puts max # of tokens + the rest of the string */ 462 | /* so in this case we will have 1 token (attr name maybe) and the rest */ 463 | if (strchr(s, ':') == NULL) { 464 | /* this is a critical error - very bad if it is a class attribute */ 465 | rpsl_error_add(&retval->errors, 466 | RPSL_ERRLVL_CRIT, 467 | RPSL_ERR_BADATTR, 468 | -1, 469 | "Attribute missing colon, ':'"); 470 | retval->name = g_strdup(""); 471 | retval->lcase_name = g_strdup(""); 472 | retval->value = g_strdup(""); 473 | goto exit_rpsl_attr_init; 474 | 475 | } 476 | attr_val = g_strsplit(s, ":", 1); 477 | assert(attr_val[0] != NULL); 478 | 479 | /* assign our name and value */ 480 | retval->name = g_strdup(attr_val[0]); 481 | retval->lcase_name = g_strdup(attr_val[0]); 482 | g_strdown(retval->lcase_name); 483 | if (attr_val[1] == NULL) { 484 | /* possible if nothing after the ':' */ 485 | retval->value = g_strdup(""); 486 | } else { 487 | /* the usual case, copy our data */ 488 | retval->value = g_strdup(attr_val[1]); 489 | assert(attr_val[2] == NULL); 490 | } 491 | 492 | /* get our attribute information */ 493 | if (class_info != NULL) { 494 | class_attr_info = class_attr_lookup(class_info, retval->name); 495 | } else { 496 | class_attr_info = NULL; 497 | } 498 | if ((class_info != NULL) && (class_attr_info != NULL)) { 499 | attr_info = attribute_lookup_by_offset(class_attr_info->offset); 500 | assert(attr_info != NULL); 501 | } else { 502 | attr_info = attribute_lookup(retval->name, &is_ambiguous); 503 | if (is_ambiguous) { 504 | rpsl_attr_delete(retval); 505 | retval = NULL; 506 | goto exit_rpsl_attr_init; 507 | } 508 | if (attr_info == NULL) { 509 | /* this is a critical error - bad if it is a class attribute */ 510 | rpsl_error_add(&retval->errors, 511 | RPSL_ERRLVL_CRIT, 512 | RPSL_ERR_UNKNOWNATTR, 513 | -1, 514 | "\"%s\" is not a known RPSL attribute", 515 | retval->name); 516 | goto exit_rpsl_attr_init; 517 | } 518 | } 519 | /* dangerous, but we promise not to make any changes to this value! */ 520 | retval->attr_info = (void *)attr_info; 521 | 522 | /* check for errors (adds to the error list) */ 523 | rpsl_attr_syntax_check(attr_info, retval->value, &retval->errors); 524 | 525 | /* clean up and leave */ 526 | exit_rpsl_attr_init: 527 | if (attr_val != NULL) { 528 | g_strfreev(attr_val); 529 | } 530 | 531 | chk_attr(retval); 532 | 533 | return retval; 534 | } 535 | 536 | static rpsl_error_t * 537 | rpsl_error_copy (const rpsl_error_t *err) 538 | { 539 | rpsl_error_t *retval; 540 | 541 | retval = g_new(rpsl_error_t, 1); 542 | retval->level = err->level; 543 | retval->code = err->code; 544 | retval->descr = g_strdup(err->descr); 545 | retval->attr_num = err->attr_num; 546 | return retval; 547 | } 548 | 549 | rpsl_attr_t * 550 | rpsl_attr_copy (const rpsl_attr_t *attr) 551 | { 552 | rpsl_attr_t *retval; 553 | GList *ptr; 554 | 555 | chk_attr(attr); 556 | 557 | retval = g_new(rpsl_attr_t, 1); 558 | retval->name = g_strdup(attr->name); 559 | retval->lcase_name = g_strdup(attr->lcase_name); 560 | retval->value = g_strdup(attr->value); 561 | retval->errors = NULL; 562 | for (ptr=attr->errors; ptr != NULL; ptr = g_list_next(ptr)) { 563 | retval->errors = g_list_append(retval->errors, 564 | rpsl_error_copy(ptr->data)); 565 | } 566 | retval->num = attr->num; 567 | retval->attr_info = attr->attr_info; 568 | 569 | chk_attr(retval); 570 | 571 | return retval; 572 | } 573 | 574 | rpsl_attr_t * 575 | rpsl_attr_clean_copy (const rpsl_attr_t *attr) 576 | { 577 | rpsl_attr_t *retval; 578 | 579 | chk_attr(attr); 580 | 581 | retval = g_new(rpsl_attr_t, 1); 582 | retval->name = g_strdup(attr->name); 583 | retval->lcase_name = g_strdup(attr->lcase_name); 584 | retval->value = attribute_clean(attr->value); 585 | retval->errors = NULL; 586 | retval->num = attr->num; 587 | retval->attr_info = attr->attr_info; 588 | 589 | chk_attr(retval); 590 | 591 | return retval; 592 | } 593 | 594 | 595 | void 596 | rpsl_attr_delete (rpsl_attr_t *attr) 597 | { 598 | GList *ptr; 599 | rpsl_error_t *err; 600 | 601 | chk_attr(attr); 602 | 603 | g_free(attr->name); 604 | attr->name = NULL; 605 | g_free(attr->lcase_name); 606 | attr->lcase_name = NULL; 607 | g_free(attr->value); 608 | attr->value = NULL; 609 | for (ptr=attr->errors; ptr != NULL; ptr = g_list_next(ptr)) { 610 | err = ptr->data; 611 | g_free(err->descr); 612 | g_free(err); 613 | } 614 | g_list_free(attr->errors); 615 | attr->errors = NULL; 616 | attr->num = -1; 617 | attr->attr_info = NULL; 618 | g_free(attr); 619 | } 620 | 621 | void 622 | rpsl_attr_delete_list (GList *attributes) 623 | { 624 | GList *ptr; 625 | 626 | for (ptr=attributes; ptr != NULL; ptr = g_list_next(ptr)) { 627 | rpsl_attr_delete(ptr->data); 628 | } 629 | g_list_free(attributes); 630 | } 631 | 632 | const gchar * 633 | rpsl_attr_get_name (const rpsl_attr_t *attr) 634 | { 635 | chk_attr(attr); 636 | 637 | /* XXX: there should be a way to get the original name */ 638 | /*return attr->name;*/ 639 | return attr->lcase_name; 640 | } 641 | 642 | gint 643 | rpsl_attr_get_ofs (const rpsl_attr_t *attr) 644 | { 645 | chk_attr(attr); 646 | 647 | return attr->num; 648 | } 649 | 650 | const gchar * 651 | rpsl_attr_get_value (const rpsl_attr_t *attr) 652 | { 653 | chk_attr(attr); 654 | 655 | return attr->value; 656 | } 657 | 658 | gchar * 659 | rpsl_attr_get_clean_value (const rpsl_attr_t *attr) 660 | { 661 | gchar *tmp; 662 | gchar *retval; 663 | 664 | chk_attr(attr); 665 | 666 | /* don't just return the value from attribute_clean(), since we 667 | need to return memory that can be freed via free(), and the 668 | gchar* returned by attribute_clean needs to be freed via g_free() */ 669 | tmp = attribute_clean(attr->value); 670 | retval = strdup(tmp); 671 | g_free(tmp); 672 | return retval; 673 | } 674 | 675 | gchar * 676 | rpsl_attr_get_clean_lines (const rpsl_attr_t *attr) 677 | { 678 | gchar *tmp; 679 | gchar *retval; 680 | 681 | chk_attr(attr); 682 | 683 | /* don't just return the value from attribute_clean_lines(), since we 684 | need to return memory that can be freed via free(), and the 685 | gchar* returned by attribute_clean needs to be freed via g_free() */ 686 | tmp = attribute_clean_lines(attr->value); 687 | retval = strdup(tmp); 688 | g_free(tmp); 689 | return retval; 690 | } 691 | 692 | GList * 693 | rpsl_attr_get_split_list (const rpsl_attr_t *attr) 694 | { 695 | const attribute_t *attr_info; 696 | GList *retval; 697 | gchar **split; 698 | int i; 699 | rpsl_attr_t *newattr; 700 | 701 | chk_attr(attr); 702 | 703 | attr_info = attr->attr_info; 704 | if ((attr_info!=NULL) && (attr_info->is_list || attr_info->is_ripe_list)) { 705 | if (attr_info->is_list) { 706 | split = attribute_list_split(attr->value); 707 | } else { 708 | split = ripe_list_split(attr->value); 709 | } 710 | retval = NULL; 711 | for (i=0; split[i] != NULL; i++) { 712 | /* XXX: perpaps consolidate this with rpsl_attr_init()? */ 713 | newattr = g_new(rpsl_attr_t, 1); 714 | assert(newattr != NULL); 715 | newattr->name = g_strdup(attr->name); 716 | newattr->lcase_name = g_strdup(attr->lcase_name); 717 | newattr->value = g_strdup(split[i]); 718 | newattr->errors = NULL; 719 | newattr->num = attr->num; 720 | newattr->attr_info = attr->attr_info; 721 | retval = g_list_append(retval, newattr); 722 | } 723 | g_strfreev(split); 724 | return retval; 725 | } else { 726 | return g_list_append(NULL, rpsl_attr_clean_copy(attr)); 727 | } 728 | } 729 | 730 | void 731 | rpsl_attr_split_multiple (GList **attrs) 732 | { 733 | GList *new; 734 | GList *old; 735 | rpsl_attr_t *oldattr; 736 | GList *newattrs; 737 | 738 | new = NULL; 739 | for (old=*attrs; old != NULL; old = g_list_next(old)) { 740 | oldattr = (rpsl_attr_t *)old->data; 741 | newattrs = rpsl_attr_get_split_list(oldattr); 742 | new = g_list_concat(new, newattrs); 743 | } 744 | rpsl_attr_delete_list(*attrs); 745 | *attrs = new; 746 | } 747 | 748 | void 749 | rpsl_attr_replace_value (rpsl_attr_t *attr, const gchar *value) 750 | { 751 | chk_attr(attr); 752 | 753 | /* perform check to add any new errors */ 754 | if (attr->attr_info != NULL) { 755 | rpsl_attr_syntax_check(attr->attr_info, value, &attr->errors); 756 | } 757 | 758 | /* copy the value */ 759 | g_free(attr->value); 760 | attr->value = g_strdup(value); 761 | 762 | chk_attr(attr); 763 | } 764 | 765 | const GList * 766 | rpsl_attr_errors (const rpsl_attr_t *attr) 767 | { 768 | chk_attr(attr); 769 | 770 | return attr->errors; 771 | } 772 | 773 | static gboolean 774 | object_is_comment (const gchar *s) 775 | { 776 | const gchar *p, *q; 777 | 778 | /* skip blank lines */ 779 | p = s; 780 | for (;;) { 781 | while ((*p == ' ') || (*p == '\t')) { 782 | p++; 783 | } 784 | /* if object is only blank lines, then we are *not* a comment */ 785 | if (*p == '\0') { 786 | return FALSE; 787 | } 788 | if (*p != '\n') { 789 | break; 790 | } 791 | p++; 792 | } 793 | /* skip comment lines */ 794 | for (;;) { 795 | if ((*p != '%') && (*p != '#')) { 796 | break; 797 | } 798 | q = strchr(p, '\n'); 799 | /* if we end on a comment without newline, we *are* a comment */ 800 | if (q == NULL) { 801 | return TRUE; 802 | } 803 | p = q + 1; 804 | } 805 | /* skip trailing blank lines */ 806 | for (;;) { 807 | while ((*p == ' ') || (*p == '\t')) { 808 | p++; 809 | } 810 | if (*p != '\n') { 811 | break; 812 | } 813 | p++; 814 | } 815 | /* see if we skipped the whole object */ 816 | if (*p == '\0') { 817 | return TRUE; 818 | } else { 819 | return FALSE; 820 | } 821 | } 822 | 823 | /* we don't want to check whether an attribute belongs in the template 824 | if it is a bad attribute, or and unknown attribute */ 825 | static gboolean 826 | template_check_needed (const rpsl_attr_t *attr) 827 | { 828 | const GList *p; 829 | const rpsl_error_t *error; 830 | 831 | p = rpsl_attr_errors(attr); 832 | while (p != NULL) { 833 | error = p->data; 834 | if (error->code == RPSL_ERR_BADATTR) { 835 | return FALSE; 836 | } 837 | if (error->code == RPSL_ERR_UNKNOWNATTR) { 838 | return FALSE; 839 | } 840 | p = g_list_next(p); 841 | } 842 | return TRUE; 843 | } 844 | 845 | static void 846 | renumber_attr (rpsl_attr_t *attr, int num) 847 | { 848 | attr->num = num; 849 | } 850 | 851 | static rpsl_attr_t * 852 | rpsl_empty_attr () 853 | { 854 | rpsl_attr_t *retval; 855 | 856 | retval = g_new(rpsl_attr_t, 1); 857 | retval->name = g_strdup(""); 858 | retval->lcase_name = g_strdup(""); 859 | retval->value = g_strdup(""); 860 | retval->errors = NULL; 861 | retval->num = -1; 862 | retval->attr_info = NULL; 863 | return retval; 864 | } 865 | 866 | rpsl_object_t * 867 | rpsl_object_init (const gchar *s) 868 | { 869 | rpsl_object_t *retval; 870 | GPtrArray *lines; 871 | const gchar *p, *q; 872 | gchar *line; 873 | rpsl_attr_t *attr; 874 | const class_t *class_info; 875 | GList *attr_list; 876 | const class_attr_t *class_attr_info; 877 | const attribute_t *attr_info; 878 | const gchar *attr_name; 879 | const gchar *class_name; 880 | int i; 881 | gboolean removed_trailing_empty_lines; 882 | 883 | /* initialize the structure */ 884 | retval = g_new(rpsl_object_t, 1); 885 | retval->attributes = NULL; 886 | retval->attr_lookup = g_hash_table_new(my_g_str_hash, my_g_strcasecmp); 887 | retval->errors = NULL; 888 | retval->class_info = NULL; 889 | 890 | /* make some lines */ 891 | lines = g_ptr_array_new(); 892 | 893 | /* see if entire string is comments */ 894 | if (object_is_comment(s)) { 895 | rpsl_error_add(&retval->errors, 896 | RPSL_ERRLVL_WARN, 897 | RPSL_ERR_ONLYCOMMENTS, 898 | -1, 899 | "Object contains only comments"); 900 | goto exit_rpsl_object_init; 901 | } 902 | 903 | /* p is the beginning of the current attribute, q searches for the end */ 904 | p = s; 905 | for (;;) { 906 | /* done with string, finish */ 907 | if (*p == '\0') { 908 | break; 909 | } 910 | 911 | /* search for end of attribute */ 912 | q = strchr(p, '\n'); 913 | while ((q != NULL) && is_rpsl_line_cont(q[1])) { 914 | q = strchr(q+1, '\n'); 915 | } 916 | 917 | if (q == NULL) { 918 | /* add the final attribute */ 919 | g_ptr_array_add(lines, g_strdup(p)); 920 | /* and exit */ 921 | break; 922 | } else { 923 | /* add this attribute */ 924 | g_ptr_array_add(lines, g_strndup(p, q-p)); 925 | /* and proceed to the next one */ 926 | p = q+1; 927 | } 928 | } 929 | 930 | /* be nice and strip empty lines at the end */ 931 | removed_trailing_empty_lines = FALSE; 932 | while (lines->len > 0) { 933 | line = g_ptr_array_index(lines, lines->len - 1); 934 | if (line[0] != '\0') { 935 | break; 936 | } 937 | g_ptr_array_remove_index_fast(lines, lines->len - 1); 938 | g_free(line); 939 | removed_trailing_empty_lines = TRUE; 940 | } 941 | if (removed_trailing_empty_lines) { 942 | rpsl_error_add(&retval->errors, 943 | RPSL_ERRLVL_INFO, 944 | RPSL_ERR_EMPTYATTR, 945 | -1, 946 | "Trailing blank lines ignored"); 947 | } 948 | 949 | /* verify we have at least one line */ 950 | if (lines->len <= 0) { 951 | rpsl_error_add(&retval->errors, 952 | RPSL_ERRLVL_CRIT, 953 | RPSL_ERR_BADCLASS, 954 | -1, 955 | "Empty object"); 956 | goto exit_rpsl_object_init; 957 | } 958 | 959 | /* create the magic first attribute, which is the class */ 960 | attr = rpsl_attr_init(g_ptr_array_index(lines, 0), NULL); 961 | if (attr == NULL) { 962 | rpsl_error_add(&retval->errors, 963 | RPSL_ERRLVL_CRIT, 964 | RPSL_ERR_BADCLASS, 965 | -1, 966 | "Error with class attribute, class invalid"); 967 | goto exit_rpsl_object_init; 968 | } 969 | renumber_attr(attr, 0); 970 | 971 | /* check for errors with the class attribute */ 972 | /* only critical errors matter - let innocent syntax errors pass through */ 973 | if (rpsl_attr_has_error(attr, RPSL_ERRLVL_CRIT)) { 974 | rpsl_error_add(&retval->errors, 975 | RPSL_ERRLVL_CRIT, 976 | RPSL_ERR_BADCLASS, 977 | -1, 978 | "Error with class attribute, class invalid"); 979 | rpsl_attr_delete(attr); 980 | goto exit_rpsl_object_init; 981 | } 982 | 983 | 984 | /* get the class information */ 985 | class_name = rpsl_attr_get_name(attr); 986 | class_info = class_lookup(class_name); 987 | if (class_info == NULL) { 988 | rpsl_error_add(&retval->errors, 989 | RPSL_ERRLVL_CRIT, 990 | RPSL_ERR_UNKNOWNCLASS, 991 | -1, 992 | "First attribute, \"%s\", is not a known RPSL class", 993 | class_name); 994 | rpsl_attr_delete(attr); 995 | goto exit_rpsl_object_init; 996 | } 997 | 998 | /* check for syntax errors with the class attribute */ 999 | if (rpsl_attr_errors(attr) != NULL) { 1000 | rpsl_error_add(&retval->errors, 1001 | RPSL_ERRLVL_ERROR, 1002 | RPSL_ERR_BADATTR, 1003 | 0, 1004 | "Error with attribute \"%s\"", 1005 | class_name); 1006 | } 1007 | 1008 | /* possibly dangerous, but we promise only to read this value! */ 1009 | retval->class_info = (void *)class_info; 1010 | 1011 | /* add class attribute */ 1012 | retval->attributes = g_list_append(NULL, attr); 1013 | attr_list = g_list_append(NULL, attr); 1014 | g_hash_table_insert(retval->attr_lookup, 1015 | (void *)rpsl_attr_get_name(attr), 1016 | attr_list); 1017 | 1018 | /* valid class, process each attribute */ 1019 | for (i=1; i < lines->len; i++) { 1020 | attr = rpsl_attr_init(g_ptr_array_index(lines, i), class_name); 1021 | if (attr == NULL) { 1022 | /* XXX: we should preserve the original information somehow */ 1023 | attr = rpsl_empty_attr(); 1024 | rpsl_error_add(&(attr->errors), 1025 | RPSL_ERRLVL_ERROR, 1026 | RPSL_ERR_BADATTR, 1027 | -1, 1028 | "Attribute not valid in this class"); 1029 | } 1030 | assert(attr != NULL); 1031 | renumber_attr(attr, i); 1032 | 1033 | /* add the attribute to the list of attributes for this object */ 1034 | retval->attributes = g_list_append(retval->attributes, attr); 1035 | 1036 | /* check for errors at attribute level */ 1037 | if (rpsl_attr_errors(attr) != NULL) { 1038 | attr_name = rpsl_attr_get_name(attr); 1039 | if (attr_name != NULL) { 1040 | rpsl_error_add(&retval->errors, 1041 | RPSL_ERRLVL_ERROR, 1042 | RPSL_ERR_BADATTR, 1043 | i, 1044 | "Error with attribute \"%s\"", 1045 | attr_name); 1046 | } else { 1047 | rpsl_error_add(&retval->errors, 1048 | RPSL_ERRLVL_ERROR, 1049 | RPSL_ERR_BADATTR, 1050 | i, 1051 | "Error with attribute"); 1052 | /* no name - no sense to process this attr further */ 1053 | continue; 1054 | } 1055 | } 1056 | 1057 | 1058 | /* get list of existing attributes of this name, if any */ 1059 | attr_list = g_hash_table_lookup(retval->attr_lookup, 1060 | rpsl_attr_get_name(attr)); 1061 | 1062 | 1063 | /* perform template checking if attribute is known type */ 1064 | if (template_check_needed(attr)) { 1065 | 1066 | /* verify this attribute can go in this class */ 1067 | class_attr_info = class_attr_lookup(class_info, 1068 | rpsl_attr_get_name(attr)); 1069 | if (class_attr_info == NULL) { 1070 | rpsl_error_add(&retval->errors, 1071 | RPSL_ERRLVL_ERROR, 1072 | RPSL_ERR_ATTRNOTALLOWED, 1073 | i, 1074 | "Attribute \"%s\" is not allowed in this class", 1075 | rpsl_attr_get_name(attr)); 1076 | } else { 1077 | /* if we have added a "single" attribute more than once */ 1078 | if ((class_attr_info->number == ATTR_SINGLE) && 1079 | (attr_list != NULL)) 1080 | { 1081 | rpsl_error_add(&retval->errors, 1082 | RPSL_ERRLVL_ERROR, 1083 | RPSL_ERR_ATTRSINGLE, 1084 | i, 1085 | "Attribute \"%s\" appears more than once", 1086 | rpsl_attr_get_name(attr)); 1087 | } 1088 | /* if we have tried to initialize a "generated" attribute */ 1089 | if (class_attr_info->choice == ATTR_GENERATED) { 1090 | rpsl_error_add(&retval->errors, 1091 | RPSL_ERRLVL_ERROR, 1092 | RPSL_ERR_ATTRGENERATED, 1093 | i, 1094 | "Attribute \"%s\" is generated automatically", 1095 | rpsl_attr_get_name(attr)); 1096 | } 1097 | } 1098 | 1099 | } /* template_check_required(attr)) */ 1100 | 1101 | /* add the attribute to the hash table for the class */ 1102 | attr_list = g_list_append(attr_list, attr); 1103 | g_hash_table_insert(retval->attr_lookup, 1104 | (void *)rpsl_attr_get_name(attr), 1105 | attr_list); /* replaces any old value */ 1106 | } 1107 | 1108 | /* check for missing required attributes */ 1109 | for (i=0; i<class_info->num_attr; i++) { 1110 | class_attr_info = &class_info->attr[i]; 1111 | if (class_attr_info->choice == ATTR_MANDATORY) { 1112 | attr_info = attribute_lookup_by_offset(class_attr_info->offset); 1113 | attr_list = g_hash_table_lookup(retval->attr_lookup, 1114 | attr_info->name); 1115 | if (attr_list == NULL) { 1116 | rpsl_error_add(&retval->errors, 1117 | RPSL_ERRLVL_ERROR, 1118 | RPSL_ERR_MISSINGATTR, 1119 | -1, 1120 | "Required attribute \"%s\" is missing", 1121 | attr_info->name); 1122 | if (attr_info->is_primary) { 1123 | rpsl_error_add(&retval->errors, 1124 | RPSL_ERRLVL_ERROR, 1125 | RPSL_ERR_MISSINGKEY, 1126 | -1, 1127 | "Primary key \"%s\" is missing", 1128 | attr_info->name); 1129 | } 1130 | } 1131 | } 1132 | } 1133 | 1134 | /* done - enjoy your new object */ 1135 | 1136 | exit_rpsl_object_init: 1137 | /* free memory used by split lines */ 1138 | for (i=0; i<lines->len; i++) { 1139 | g_free(g_ptr_array_index(lines, i)); 1140 | } 1141 | g_ptr_array_free(lines, TRUE); 1142 | lines = NULL; 1143 | 1144 | chk_obj(retval); 1145 | 1146 | /* return our object */ 1147 | return retval; 1148 | } 1149 | 1150 | rpsl_object_t * 1151 | rpsl_object_copy (const rpsl_object_t *object) 1152 | { 1153 | rpsl_object_t *retval; 1154 | GList *p; 1155 | rpsl_attr_t *attr; 1156 | GList *attr_list; 1157 | 1158 | chk_obj(object); 1159 | 1160 | retval = g_new(rpsl_object_t, 1); 1161 | retval->attributes = NULL; 1162 | retval->attr_lookup = g_hash_table_new(my_g_str_hash, my_g_strcasecmp); 1163 | retval->errors = NULL; 1164 | retval->class_info = object->class_info; 1165 | 1166 | /* copy attributes */ 1167 | for (p=object->attributes; p != NULL; p = g_list_next(p)) { 1168 | /* insert copy of attribute into list */ 1169 | attr = rpsl_attr_copy(p->data); 1170 | retval->attributes = g_list_append(retval->attributes, attr); 1171 | 1172 | /* insert copy of attribute into hash table */ 1173 | attr_list = g_hash_table_lookup(retval->attr_lookup, 1174 | rpsl_attr_get_name(attr)); 1175 | attr_list = g_list_append(attr_list, attr); /* works for NULL too */ 1176 | g_hash_table_insert(retval->attr_lookup, 1177 | (void *)rpsl_attr_get_name(attr), 1178 | attr_list); /* replaces any old value */ 1179 | } 1180 | 1181 | /* copy errors */ 1182 | for (p=object->errors; p != NULL; p = g_list_next(p)) { 1183 | retval->errors = g_list_append(retval->errors, 1184 | rpsl_error_copy(p->data)); 1185 | } 1186 | 1187 | chk_obj(retval); 1188 | 1189 | /* return the copy */ 1190 | return retval; 1191 | } 1192 | 1193 | rpsl_object_t * 1194 | rpsl_object_copy_flattened (const rpsl_object_t *object) 1195 | { 1196 | rpsl_object_t *retval; 1197 | GList *p1, *p2; 1198 | GList *split_attr; 1199 | rpsl_attr_t *attr; 1200 | GList *attr_list; 1201 | int num_attr; 1202 | 1203 | chk_obj(object); 1204 | 1205 | retval = g_new(rpsl_object_t, 1); 1206 | retval->attributes = NULL; 1207 | retval->attr_lookup = g_hash_table_new(my_g_str_hash, my_g_strcasecmp); 1208 | retval->errors = NULL; 1209 | retval->class_info = object->class_info; 1210 | 1211 | /* copy attributes */ 1212 | num_attr = 0; 1213 | for (p1=object->attributes; p1 != NULL; p1 = g_list_next(p1)) { 1214 | /* split the attribute into separate values (may be 1) */ 1215 | split_attr = rpsl_attr_get_split_list(p1->data); 1216 | 1217 | /* add each resulting attribute */ 1218 | for (p2=split_attr; p2 != NULL; p2 = g_list_next(p2)) { 1219 | attr = p2->data; 1220 | 1221 | /* renumber attribute */ 1222 | renumber_attr(attr, num_attr); 1223 | num_attr++; 1224 | 1225 | /* insert split attribute into list */ 1226 | retval->attributes = g_list_append(retval->attributes, attr); 1227 | 1228 | /* insert split attribute into hash table */ 1229 | attr_list = g_hash_table_lookup(retval->attr_lookup, 1230 | rpsl_attr_get_name(attr)); 1231 | attr_list = g_list_append(attr_list, attr); /* works for NULL too */ 1232 | g_hash_table_insert(retval->attr_lookup, 1233 | (void *)rpsl_attr_get_name(attr), 1234 | attr_list); /* replaces any old value */ 1235 | } 1236 | 1237 | /* free the list */ 1238 | g_list_free(split_attr); 1239 | } 1240 | 1241 | chk_obj(retval); 1242 | 1243 | /* return the copy */ 1244 | return retval; 1245 | } 1246 | 1247 | static void 1248 | rpsl_object_delete_helper (gpointer attr_name, 1249 | gpointer attr_list, 1250 | gpointer null) 1251 | { 1252 | g_list_free((GList *)attr_list); 1253 | } 1254 | 1255 | void 1256 | rpsl_object_delete (rpsl_object_t *object) 1257 | { 1258 | GList *p; 1259 | rpsl_error_t *err; 1260 | 1261 | chk_obj(object); 1262 | 1263 | /* free the attributes */ 1264 | for (p=object->attributes; p != NULL; p = g_list_next(p)) { 1265 | rpsl_attr_delete(p->data); 1266 | } 1267 | g_list_free(object->attributes); 1268 | object->attributes = NULL; 1269 | 1270 | /* remove the lists from the hash table */ 1271 | g_hash_table_foreach(object->attr_lookup, rpsl_object_delete_helper, NULL); 1272 | g_hash_table_destroy(object->attr_lookup); 1273 | object->attr_lookup = NULL; 1274 | 1275 | /* free the errors */ 1276 | for (p=object->errors; p != NULL; p = g_list_next(p)) { 1277 | err = p->data; 1278 | g_free(err->descr); 1279 | g_free(err); 1280 | } 1281 | g_list_free(object->errors); 1282 | object->errors = NULL; 1283 | 1284 | /* free the object itself */ 1285 | g_free(object); 1286 | } 1287 | 1288 | const char * 1289 | rpsl_object_get_class (const rpsl_object_t *object) 1290 | { 1291 | rpsl_attr_t *attr; 1292 | 1293 | chk_obj(object); 1294 | 1295 | if (object->attributes != NULL) { 1296 | attr = (rpsl_attr_t *)object->attributes->data; 1297 | return attr->lcase_name; 1298 | } else { 1299 | return NULL; 1300 | } 1301 | } 1302 | 1303 | /* default number of spaces per tab character */ 1304 | #define TABSTOP 8 1305 | 1306 | /* returns the position of the next tab stop */ 1307 | static guint 1308 | next_tabstop (guint col) 1309 | { 1310 | guint tab; 1311 | 1312 | tab = (col / TABSTOP) + 1; 1313 | return tab * TABSTOP; 1314 | } 1315 | 1316 | /* gets the leading whitespace from the given string */ 1317 | static void 1318 | separate_leading_whitespace (const gchar *str, GString **ws, GString **non_ws) 1319 | { 1320 | int n; 1321 | 1322 | n = 0; 1323 | while ((str[n] == ' ') || (str[n] == '\t')) { 1324 | n++; 1325 | } 1326 | 1327 | *ws = g_string_new(str); 1328 | g_string_truncate(*ws, n); 1329 | *non_ws = g_string_new(str + n); 1330 | } 1331 | 1332 | /* gets the length of a string of whitespace */ 1333 | static int 1334 | whitespace_length (const gchar *str, int start_col) 1335 | { 1336 | int len; 1337 | 1338 | len = start_col; 1339 | for (;;) { 1340 | if (*str == ' ') { 1341 | len++; 1342 | } else if (*str == '\t') { 1343 | len = next_tabstop(len); 1344 | } else { 1345 | break; 1346 | } 1347 | str++; 1348 | } 1349 | return len; 1350 | } 1351 | 1352 | /* removes the number of columns specified from the string, from the right */ 1353 | static void 1354 | remove_columns (GString *s, int col, int start_col) 1355 | { 1356 | int old_len; 1357 | int new_len; 1358 | int col_removed; 1359 | 1360 | col_removed = 0; 1361 | 1362 | /* first, remove characters until we've removed enough */ 1363 | while ((s->len > 0) && (col_removed < col)) { 1364 | old_len = whitespace_length(s->str, start_col); 1365 | g_string_truncate(s, s->len-1); 1366 | new_len = whitespace_length(s->str, start_col); 1367 | col_removed += old_len - new_len; 1368 | } 1369 | 1370 | /* if we've removed too many, add some spaces back */ 1371 | while (col_removed > col) { 1372 | g_string_append_c(s, ' '); 1373 | col_removed--; 1374 | } 1375 | } 1376 | 1377 | /* align the text of the attribute to the specific column */ 1378 | static void 1379 | add_aligned_val (GString *s, const rpsl_attr_t *attr, int col) 1380 | { 1381 | const gchar *name; 1382 | const gchar *val; 1383 | int start_col; 1384 | const gchar *p, *q; 1385 | int col_to_add; 1386 | int col_to_sub; 1387 | gchar **lines; 1388 | int i, j; 1389 | GString *ws; 1390 | GString *non_ws; 1391 | 1392 | /* get the information from the attribute */ 1393 | name = rpsl_attr_get_name(attr); 1394 | val = rpsl_attr_get_value(attr); 1395 | 1396 | /* calculate the column we're at after the attribute name */ 1397 | start_col = strlen(name) + 1; 1398 | 1399 | /* if the desired column is too small based on the name of the 1400 | attribute, set to the smallest allowed column */ 1401 | if (col < (start_col + 1)) { 1402 | col = start_col + 1; 1403 | } 1404 | 1405 | 1406 | /* find out what column the attribute value currently starts at */ 1407 | p = val; 1408 | for (;;) { 1409 | if (*p == ' ') { 1410 | start_col++; 1411 | } else if (*p == '\t') { 1412 | start_col = next_tabstop(start_col); 1413 | } else { 1414 | break; 1415 | } 1416 | p++; 1417 | } 1418 | 1419 | /* special case: 1420 | if there are *only* whitespace on the first line, or if it only 1421 | contains a comment, then use "as-is" */ 1422 | if ((*p == '\0') || (*p == '\n') || (*p == '#')) { 1423 | g_string_append(s, val); 1424 | g_string_append_c(s, '\n'); 1425 | /* EARLY RETURN */ 1426 | return; 1427 | } 1428 | 1429 | /* next, see how many columns we need to add or subtract */ 1430 | col_to_add = col - start_col; 1431 | 1432 | /* adding is (relatively) easy */ 1433 | if (col_to_add > 0) { 1434 | lines = g_strsplit(val, "\n", 0); 1435 | /* for the first line, append the spaces and the line itself */ 1436 | q = lines[0]; 1437 | while ((*q == ' ') || (*q == '\t')) { 1438 | g_string_append_c(s, *q); 1439 | q++; 1440 | } 1441 | for (j=0; j<col_to_add; j++) { 1442 | g_string_append_c(s, ' '); 1443 | } 1444 | g_string_append(s, q); 1445 | g_string_append_c(s, '\n'); 1446 | for (i=1; lines[i] != NULL; i++) { 1447 | /* for subsequent lines... */ 1448 | /* append the first (line continuation) character */ 1449 | g_string_append_c(s, lines[i][0]); 1450 | /* append any leading whitespace */ 1451 | q = lines[i]+1; 1452 | while ((*q == ' ') || (*q == '\t')) { 1453 | g_string_append_c(s, *q); 1454 | q++; 1455 | } 1456 | /* now append the spaces and the remainder of the line */ 1457 | for (j=0; j<col_to_add; j++) { 1458 | g_string_append_c(s, ' '); 1459 | } 1460 | g_string_append(s, q); 1461 | g_string_append_c(s, '\n'); 1462 | } 1463 | g_strfreev(lines); 1464 | } 1465 | /* subtracting is a bit more tricky, due to tabs (AKA "minions of evil") */ 1466 | else if (col_to_add < 0) { 1467 | col_to_sub = -col_to_add; 1468 | 1469 | lines = g_strsplit(val, "\n", 0); 1470 | 1471 | /* add first line after subtracting columns */ 1472 | separate_leading_whitespace(lines[0], &ws, &non_ws); 1473 | remove_columns(ws, col_to_sub, strlen(name)+1); 1474 | g_string_append(s, ws->str); 1475 | g_string_append(s, non_ws->str); 1476 | g_string_append_c(s, '\n'); 1477 | g_string_free(ws, TRUE); 1478 | g_string_free(non_ws, TRUE); 1479 | 1480 | for (i=1; lines[i] != NULL; i++) { 1481 | separate_leading_whitespace(lines[i]+1, &ws, &non_ws); 1482 | /* if the line continuation character is a tab and 1483 | we don't have enough columns, convert it to spaces */ 1484 | if (lines[i][0] == '\t') { 1485 | if (whitespace_length(ws->str, 0) < col_to_sub) { 1486 | lines[i][0] = ' '; 1487 | for (j=1; j<TABSTOP; j++) { 1488 | g_string_prepend_c(ws, ' '); 1489 | } 1490 | } 1491 | } 1492 | remove_columns(ws, col_to_sub, 0); 1493 | g_string_append_c(s, lines[i][0]); 1494 | g_string_append(s, ws->str); 1495 | g_string_append(s, non_ws->str); 1496 | g_string_append_c(s, '\n'); 1497 | g_string_free(ws, TRUE); 1498 | g_string_free(non_ws, TRUE); 1499 | } 1500 | g_strfreev(lines); 1501 | } 1502 | /* and if no adjustment is necessary, it's trivial */ 1503 | else { 1504 | g_string_append(s, val); 1505 | g_string_append_c(s, '\n'); 1506 | } 1507 | } 1508 | 1509 | gchar * 1510 | rpsl_object_get_text (const rpsl_object_t *object, guint data_column) 1511 | { 1512 | GString *s; 1513 | GList *p; 1514 | rpsl_attr_t *attr; 1515 | gchar *retval; 1516 | const gchar *name; 1517 | 1518 | chk_obj(object); 1519 | 1520 | /* return NULL on empty object, as promised */ 1521 | if (object->attributes == NULL) { 1522 | return NULL; 1523 | } 1524 | 1525 | /* concatinate attribute names and values together */ 1526 | s = g_string_new(""); 1527 | for (p=object->attributes; p != NULL; p = g_list_next(p)) { 1528 | attr = p->data; 1529 | name = rpsl_attr_get_name(attr); 1530 | if (name != NULL) { 1531 | g_string_append(s, name); 1532 | g_string_append_c(s, ':'); 1533 | if (data_column > 0) { 1534 | add_aligned_val(s, attr, data_column); 1535 | } else { 1536 | g_string_append(s, rpsl_attr_get_value(attr)); 1537 | g_string_append_c(s, '\n'); 1538 | } 1539 | } 1540 | } 1541 | 1542 | /* copy value to return */ 1543 | retval = (gchar *)malloc(s->len + 1); 1544 | if (retval != NULL) { 1545 | strcpy(retval, s->str); 1546 | } 1547 | 1548 | /* free string */ 1549 | g_string_free(s, TRUE); 1550 | 1551 | /* return result (returns NULL if memory allocation failed) */ 1552 | return retval; 1553 | } 1554 | 1555 | gint 1556 | rpsl_object_get_num_attr (const rpsl_object_t *object) 1557 | { 1558 | chk_obj(object); 1559 | 1560 | return g_list_length(object->attributes); 1561 | } 1562 | 1563 | const GList * 1564 | rpsl_object_get_all_attr (const rpsl_object_t *object) 1565 | { 1566 | chk_obj(object); 1567 | 1568 | return object->attributes; 1569 | } 1570 | 1571 | GList * 1572 | rpsl_object_get_attr (const rpsl_object_t *object, const gchar *name) 1573 | { 1574 | GList *attr_list; 1575 | GList *retval; 1576 | 1577 | chk_obj(object); 1578 | 1579 | retval = NULL; 1580 | attr_list = g_hash_table_lookup(object->attr_lookup, name); 1581 | while (attr_list != NULL) { 1582 | retval = g_list_append(retval, rpsl_attr_copy(attr_list->data)); 1583 | attr_list = g_list_next(attr_list); 1584 | } 1585 | return retval; 1586 | } 1587 | 1588 | const rpsl_attr_t * 1589 | rpsl_object_get_attr_by_ofs (const rpsl_object_t *object, gint ofs) 1590 | { 1591 | rpsl_attr_t *attr; 1592 | 1593 | chk_obj(object); 1594 | attr = g_list_nth_data(object->attributes, ofs); 1595 | chk_attr(attr); 1596 | 1597 | return attr; 1598 | } 1599 | 1600 | /* using -1 for offset (ofs) to append to the end of the object */ 1601 | static int 1602 | add_attr_to_object(rpsl_object_t *object, 1603 | rpsl_attr_t *attr, 1604 | gint ofs, 1605 | rpsl_error_t *error) 1606 | { 1607 | const gchar *attr_name; 1608 | class_t *class_info; 1609 | const class_attr_t *class_attr_info; 1610 | GList *attr_list; 1611 | gint num_attr; 1612 | gint i; 1613 | GList *p; 1614 | rpsl_attr_t *tmp; 1615 | GList *err_list; 1616 | rpsl_error_t *err; 1617 | 1618 | chk_obj(object); 1619 | chk_attr(attr); 1620 | 1621 | /* empty object - bogus, reject, abort, error */ 1622 | if (object->attributes == NULL) { 1623 | rpsl_error_assign(error, 1624 | RPSL_ERRLVL_ERROR, 1625 | RPSL_ERR_BADCLASS, 1626 | "Empty class"); 1627 | chk_obj(object); 1628 | chk_attr(attr); 1629 | return 0; 1630 | } 1631 | 1632 | /* check our offset number */ 1633 | num_attr = rpsl_object_get_num_attr(object); 1634 | if ((ofs == 0) || (ofs > num_attr)) { 1635 | rpsl_error_assign(error, 1636 | RPSL_ERRLVL_ERROR, 1637 | RPSL_ERR_BADOFFSET, 1638 | "Offset %d not between 1 and %d", ofs, num_attr); 1639 | chk_obj(object); 1640 | chk_attr(attr); 1641 | return 0; 1642 | } 1643 | 1644 | /* get attributes with this name (may be NULL, which is okay) */ 1645 | attr_name = rpsl_attr_get_name(attr); 1646 | attr_list = g_hash_table_lookup(object->attr_lookup, attr_name); 1647 | 1648 | /* get class info */ 1649 | class_info = object->class_info; 1650 | if (class_info != NULL) { /* we can only check for valid classes... */ 1651 | 1652 | /* verify this attribute can go in this class */ 1653 | class_attr_info = class_attr_lookup(class_info, attr_name); 1654 | if (class_attr_info == NULL) { 1655 | rpsl_error_assign(error, 1656 | RPSL_ERRLVL_ERROR, 1657 | RPSL_ERR_ATTRNOTALLOWED, 1658 | "Attribute \"%s\" is not allowed in this class", 1659 | attr_name); 1660 | chk_obj(object); 1661 | chk_attr(attr); 1662 | return 0; 1663 | } 1664 | 1665 | /* check to see if it is "single" and already found */ 1666 | if ((class_attr_info->number == ATTR_SINGLE) && (attr_list != NULL)) { 1667 | rpsl_error_assign(error, 1668 | RPSL_ERRLVL_ERROR, 1669 | RPSL_ERR_ATTRSINGLE, 1670 | "Attribute \"%s\" already appears in this class", 1671 | attr_name); 1672 | chk_obj(object); 1673 | chk_attr(attr); 1674 | return 0; 1675 | } 1676 | 1677 | /* otherwise we can safely add this attribute */ 1678 | } 1679 | 1680 | /* update any attribute offsets in the error list */ 1681 | err_list = object->errors; 1682 | while (err_list != NULL) { 1683 | err = err_list->data; 1684 | if (err->attr_num >= ofs) { 1685 | /* increment errors from later attributes */ 1686 | err->attr_num++; 1687 | } 1688 | err_list = g_list_next(err_list); 1689 | } 1690 | 1691 | /* add attribute to attribute list */ 1692 | if ((ofs < 0) || (ofs >= num_attr)) { 1693 | renumber_attr(attr, num_attr); 1694 | object->attributes = g_list_append(object->attributes, attr); 1695 | } else { 1696 | /* insert the entry at the appriate offset */ 1697 | renumber_attr(attr, ofs); 1698 | object->attributes = g_list_insert(object->attributes, attr, ofs); 1699 | num_attr++; 1700 | 1701 | /* renumber entries moved down */ 1702 | p = g_list_nth(object->attributes, ofs+1); 1703 | for (i=ofs+1; p != NULL; i++, p = g_list_next(p)) { 1704 | tmp = p->data; 1705 | renumber_attr(tmp, i); 1706 | } 1707 | } 1708 | 1709 | /* add attribute to hash table */ 1710 | attr_list = g_list_append(attr_list, attr); 1711 | g_hash_table_insert(object->attr_lookup, (void *)attr_name, attr_list); 1712 | 1713 | chk_obj(object); 1714 | chk_attr(attr); 1715 | 1716 | return 1; 1717 | } 1718 | 1719 | int 1720 | rpsl_object_append_attr (rpsl_object_t *object, 1721 | rpsl_attr_t *attr, 1722 | rpsl_error_t *error) 1723 | { 1724 | return add_attr_to_object(object, attr, -1, error); 1725 | } 1726 | 1727 | int 1728 | rpsl_object_add_attr (rpsl_object_t *object, 1729 | rpsl_attr_t *attr, 1730 | gint ofs, 1731 | rpsl_error_t *error) 1732 | { 1733 | if (ofs <= 0) { 1734 | rpsl_error_assign(error, 1735 | RPSL_ERRLVL_ERROR, 1736 | RPSL_ERR_BADOFFSET, 1737 | "Offset %d is less than 1", ofs); 1738 | return 0; 1739 | } else { 1740 | return add_attr_to_object(object, attr, ofs, error); 1741 | } 1742 | } 1743 | 1744 | rpsl_attr_t * 1745 | rpsl_object_remove_attr (rpsl_object_t *object, gint ofs, rpsl_error_t *error) 1746 | { 1747 | gint num_attr; 1748 | rpsl_attr_t *attr; 1749 | const gchar *attr_name; 1750 | const gchar *new_attr_name; 1751 | class_t *class_info; 1752 | const class_attr_t *class_attr_info; 1753 | GList *attr_list; 1754 | GList *p; 1755 | gint i; 1756 | rpsl_attr_t *tmp; 1757 | GList *err_list, *tmp_err_list; 1758 | rpsl_error_t *err; 1759 | 1760 | chk_obj(object); 1761 | 1762 | num_attr = rpsl_object_get_num_attr(object); 1763 | if ((ofs <= 0) || (ofs >= num_attr)) { 1764 | rpsl_error_assign(error, 1765 | RPSL_ERRLVL_ERROR, 1766 | RPSL_ERR_BADOFFSET, 1767 | "Offset %d not between 1 and %d", ofs, num_attr); 1768 | chk_obj(object); 1769 | return NULL; 1770 | } 1771 | attr = g_list_nth_data(object->attributes, ofs); 1772 | attr_name = rpsl_attr_get_name(attr); 1773 | 1774 | /* get class info */ 1775 | class_info = object->class_info; 1776 | if (class_info != NULL) { /* we must check valid classes... */ 1777 | 1778 | /* verify this attribute can be removed */ 1779 | class_attr_info = class_attr_lookup(class_info, attr_name); 1780 | if ((class_attr_info != NULL) && 1781 | (class_attr_info->choice == ATTR_MANDATORY)) 1782 | { 1783 | rpsl_error_assign(error, 1784 | RPSL_ERRLVL_ERROR, 1785 | RPSL_ERR_ATTRNOTALLOWED, 1786 | "Attribute \"%s\" is required in this class", 1787 | attr_name); 1788 | } 1789 | } 1790 | 1791 | /* remove from list and renumber */ 1792 | object->attributes = g_list_remove(object->attributes, attr); 1793 | for (i=0, p=object->attributes; p != NULL; i++, p = g_list_next(p)) { 1794 | tmp = p->data; 1795 | renumber_attr(tmp, i); 1796 | } 1797 | 1798 | /* remove from hash table */ 1799 | attr_list = g_hash_table_lookup(object->attr_lookup, attr_name); 1800 | assert(attr_list != NULL); 1801 | g_hash_table_remove(object->attr_lookup, attr_name); 1802 | attr_list = g_list_remove(attr_list, attr); 1803 | if (attr_list != NULL) { 1804 | new_attr_name = rpsl_attr_get_name((rpsl_attr_t *)attr_list->data); 1805 | g_hash_table_insert(object->attr_lookup, 1806 | (void *)new_attr_name, 1807 | attr_list); 1808 | } 1809 | 1810 | /* fix any attribute offsets in the error list */ 1811 | err_list = object->errors; 1812 | while (err_list != NULL) { 1813 | err = err_list->data; 1814 | if (err->attr_num == ofs) { 1815 | /* remove errors from this attribute */ 1816 | /* XXX: is this safe? should I just scan from the beginning? */ 1817 | tmp_err_list = g_list_next(err_list); 1818 | object->errors = g_list_remove_link(object->errors, err_list); 1819 | g_free(err->descr); 1820 | g_free(err); 1821 | g_list_free(err_list); 1822 | err_list = tmp_err_list; 1823 | } else if (err->attr_num > ofs) { 1824 | /* decrement errors from later attributes */ 1825 | err->attr_num--; 1826 | err_list = g_list_next(err_list); 1827 | } else { 1828 | /* ignore earlier attributes */ 1829 | err_list = g_list_next(err_list); 1830 | } 1831 | } 1832 | 1833 | chk_obj(object); 1834 | chk_attr(attr); 1835 | 1836 | return attr; 1837 | } 1838 | 1839 | rpsl_attr_t * 1840 | rpsl_object_remove_attr_name (rpsl_object_t *object, 1841 | const gchar *name, 1842 | rpsl_error_t *error) 1843 | { 1844 | GList *attr_list; 1845 | rpsl_attr_t *attr; 1846 | rpsl_attr_t *retval; 1847 | 1848 | chk_obj(object); 1849 | 1850 | attr_list = g_hash_table_lookup(object->attr_lookup, name); 1851 | if (attr_list == NULL) { 1852 | rpsl_error_assign(error, 1853 | RPSL_ERRLVL_ERROR, 1854 | RPSL_ERR_NOSUCHATTR, 1855 | "Attribute \"%s\" not in this object", 1856 | name); 1857 | return NULL; 1858 | } 1859 | attr = attr_list->data; 1860 | 1861 | retval = rpsl_object_remove_attr(object, attr->num, error); 1862 | 1863 | chk_obj(object); 1864 | if (retval != NULL) { 1865 | chk_attr(retval); 1866 | } 1867 | 1868 | return retval; 1869 | } 1870 | 1871 | const GList * 1872 | rpsl_object_errors (const rpsl_object_t *object) 1873 | { 1874 | chk_obj(object); 1875 | 1876 | return object->errors; 1877 | } 1878 | 1879 | gboolean 1880 | rpsl_attr_is_required (const rpsl_object_t *object, const gchar *attr) 1881 | { 1882 | const class_attr_t *class_attr_info; 1883 | 1884 | chk_obj(object); 1885 | 1886 | class_attr_info = class_attr_lookup(object->class_info, attr); 1887 | return (class_attr_info != NULL) && 1888 | (class_attr_info->choice == ATTR_MANDATORY); 1889 | } 1890 | 1891 | gboolean 1892 | rpsl_attr_is_generated (const rpsl_object_t *object, const gchar *attr) 1893 | { 1894 | const class_attr_t *class_attr_info; 1895 | 1896 | chk_obj(object); 1897 | 1898 | class_attr_info = class_attr_lookup(object->class_info, attr); 1899 | return (class_attr_info != NULL) && 1900 | (class_attr_info->choice == ATTR_GENERATED); 1901 | } 1902 | 1903 | gboolean 1904 | rpsl_attr_is_multivalued (const rpsl_object_t *object, const gchar *attr) 1905 | { 1906 | const class_attr_t *class_attr_info; 1907 | 1908 | chk_obj(object); 1909 | 1910 | class_attr_info = class_attr_lookup(object->class_info, attr); 1911 | return (class_attr_info == NULL) || 1912 | (class_attr_info->number == ATTR_MULTIPLE); 1913 | } 1914 | 1915 | gboolean 1916 | rpsl_attr_is_lookup (const rpsl_object_t *object, const gchar *attr) 1917 | { 1918 | const class_attr_t *class_attr_info; 1919 | const attribute_t *attr_info; 1920 | 1921 | chk_obj(object); 1922 | 1923 | class_attr_info = class_attr_lookup(object->class_info, attr); 1924 | if (class_attr_info == NULL) { 1925 | return FALSE; 1926 | } else { 1927 | attr_info = attribute_lookup_by_offset(class_attr_info->offset); 1928 | assert(attr_info != NULL); 1929 | return attr_info->is_lookup || attr_info->is_inverse; 1930 | } 1931 | } 1932 | 1933 | gboolean 1934 | rpsl_attr_is_key (const rpsl_object_t *object, const gchar *attr) 1935 | { 1936 | const class_attr_t *class_attr_info; 1937 | const attribute_t *attr_info; 1938 | 1939 | chk_obj(object); 1940 | 1941 | class_attr_info = class_attr_lookup(object->class_info, attr); 1942 | if (class_attr_info == NULL) { 1943 | return FALSE; 1944 | } else { 1945 | attr_info = attribute_lookup_by_offset(class_attr_info->offset); 1946 | assert(attr_info != NULL); 1947 | return attr_info->is_primary; 1948 | } 1949 | } 1950 | 1951 | gboolean 1952 | rpsl_object_is_deleted (const rpsl_object_t *object) 1953 | { 1954 | GList *attr_list; 1955 | 1956 | chk_obj(object); 1957 | 1958 | attr_list = g_hash_table_lookup(object->attr_lookup, "delete"); 1959 | if (attr_list != NULL) { 1960 | return TRUE; 1961 | } else { 1962 | return FALSE; 1963 | } 1964 | } 1965 | 1966 | static gboolean 1967 | search_errors (const GList *errors, int error_level) 1968 | { 1969 | rpsl_error_t *e; 1970 | 1971 | while (errors != NULL) { 1972 | e = errors->data; 1973 | if (e->level >= error_level) { 1974 | return TRUE; 1975 | } 1976 | errors = g_list_next(errors); 1977 | } 1978 | return FALSE; 1979 | } 1980 | 1981 | 1982 | gboolean 1983 | rpsl_attr_has_error (const rpsl_attr_t *attr, int error_level) 1984 | { 1985 | chk_attr(attr); 1986 | 1987 | return search_errors(attr->errors, error_level); 1988 | } 1989 | 1990 | gboolean 1991 | rpsl_object_has_error (const rpsl_object_t *object, int error_level) 1992 | { 1993 | chk_obj(object); 1994 | 1995 | return search_errors(object->errors, error_level); 1996 | } 1997 | 1998 | gint 1999 | rpsl_get_attr_id (const gchar *attr_name) 2000 | { 2001 | const attribute_t *attr_info; 2002 | gboolean is_ambiguous; 2003 | 2004 | attr_info = attribute_lookup(attr_name, &is_ambiguous); 2005 | if (attr_info == NULL) { 2006 | return -1; 2007 | } else { 2008 | return attr_info->id; 2009 | } 2010 | } 2011 | 2012 | gint 2013 | rpsl_get_class_id (const gchar *class_name) 2014 | { 2015 | const class_t *class_info; 2016 | 2017 | if (class_name == NULL) { 2018 | return -1; 2019 | } 2020 | 2021 | class_info = class_lookup(class_name); 2022 | if (class_info == NULL) { 2023 | return -1; 2024 | } else { 2025 | return class_info->id; 2026 | } 2027 | } 2028 | 2029 | void 2030 | rpsl_load_dictionary (int level) 2031 | { 2032 | rpsl_level = level; 2033 | } 2034 | 2035 | int 2036 | rpsl_read_dictionary () 2037 | { 2038 | return rpsl_level; 2039 | } 2040 | 2041 | 2042 | const gchar* const * 2043 | rpsl_get_classes () 2044 | { 2045 | return get_class_names(); 2046 | } 2047 | 2048 | 2049 | /* mapping from class name to class template */ 2050 | static GHashTable *class_name_to_template = NULL; 2051 | static pthread_mutex_t class_name_to_template_mutex = PTHREAD_MUTEX_INITIALIZER; 2052 | 2053 | const rpsl_template_t* const * 2054 | rpsl_get_template (const gchar *class) 2055 | { 2056 | const class_t *class_info; 2057 | rpsl_template_t **ret_val; 2058 | int i; 2059 | const class_attr_t *class_attr; 2060 | const attribute_t *attr; 2061 | rpsl_template_t *t; 2062 | 2063 | pthread_mutex_lock(&class_name_to_template_mutex); 2064 | 2065 | if (class_name_to_template == NULL) { 2066 | class_name_to_template = g_hash_table_new(my_g_str_hash, 2067 | my_g_strcasecmp); 2068 | } 2069 | 2070 | ret_val = g_hash_table_lookup(class_name_to_template, class); 2071 | if (ret_val == NULL) { 2072 | class_info = class_lookup(class); 2073 | if (class_info != NULL) { 2074 | ret_val = g_new(rpsl_template_t*, class_info->num_attr+1); 2075 | for (i=0; i<class_info->num_attr; i++) { 2076 | class_attr = &class_info->attr[i]; 2077 | attr = attribute_lookup_by_offset(class_attr->offset); 2078 | t = g_new(rpsl_template_t, 1); 2079 | t->name = attr->name; 2080 | t->code = attr->code; 2081 | t->is_required = (class_attr->choice == ATTR_MANDATORY); 2082 | t->is_generated = (class_attr->choice == ATTR_GENERATED); 2083 | t->is_multivalued = (class_attr->number == ATTR_MULTIPLE); 2084 | t->is_lookup = attr->is_lookup; 2085 | t->is_inverse = attr->is_inverse; 2086 | t->is_primary = attr->is_primary; 2087 | t->is_list = attr->is_list; 2088 | t->is_ripe_list = attr->is_ripe_list; 2089 | ret_val[i] = t; 2090 | } 2091 | ret_val[i] = NULL; 2092 | g_hash_table_insert(class_name_to_template, (void *)class, ret_val); 2093 | } 2094 | } 2095 | 2096 | 2097 | pthread_mutex_unlock(&class_name_to_template_mutex); 2098 | 2099 | return (const rpsl_template_t **)ret_val; 2100 | } 2101 | 2102 | 2103 | #if RUNTIME_CHECK 2104 | static void 2105 | rpsl_error_check (const GList *errors, const char *file, int line) 2106 | { 2107 | const rpsl_error_t *err; 2108 | 2109 | while (errors != NULL) { 2110 | err = errors->data; 2111 | switch (err->level) { 2112 | case RPSL_ERRLVL_NONE: 2113 | case RPSL_ERRLVL_DEBUG: 2114 | case RPSL_ERRLVL_INFO: 2115 | case RPSL_ERRLVL_NOTICE: 2116 | case RPSL_ERRLVL_WARN: 2117 | case RPSL_ERRLVL_ERROR: 2118 | case RPSL_ERRLVL_CRIT: 2119 | case RPSL_ERRLVL_FATAL: 2120 | break; 2121 | default: 2122 | fprintf(stderr, "rpsl_error_check: bad level %d at %s:%d\n", 2123 | err->level, file, line); 2124 | exit(1); 2125 | } 2126 | /* XXX: could check attr-codes ONLY appear in attr, and so on */ 2127 | switch (err->code) { 2128 | case RPSL_ERR_BADATTR: 2129 | case RPSL_ERR_UNKNOWNATTR: 2130 | case RPSL_ERR_EMPTYLIST: 2131 | case RPSL_ERR_EMPTYATTR: 2132 | case RPSL_ERR_SYNERR: 2133 | case RPSL_ERR_ONLYCOMMENTS: 2134 | case RPSL_ERR_BADCLASS: 2135 | case RPSL_ERR_UNKNOWNCLASS: 2136 | case RPSL_ERR_ATTRNOTALLOWED: 2137 | case RPSL_ERR_ATTRSINGLE: 2138 | case RPSL_ERR_ATTRGENERATED: 2139 | case RPSL_ERR_MISSINGATTR: 2140 | case RPSL_ERR_MISSINGKEY: 2141 | case RPSL_ERR_BADOFFSET: 2142 | case RPSL_ERR_NOSUCHATTR: 2143 | break; 2144 | default: 2145 | fprintf(stderr, "rpsl_error_check: bad code %d at %s:%d\n", 2146 | err->code, file, line); 2147 | exit(1); 2148 | } 2149 | if (err->descr == NULL) { 2150 | fprintf(stderr, "rpsl_error_check: NULL descr at %s:%d\n", 2151 | file, line); 2152 | exit(1); 2153 | } 2154 | /* XXX: should check attr_num is within object */ 2155 | if (err->attr_num < -1) { 2156 | fprintf(stderr, "rpsl_error_check: bad attr_num %d at %s:%d\n", 2157 | err->attr_num, file, line); 2158 | exit(1); 2159 | } 2160 | errors = g_list_next(errors); 2161 | } 2162 | } 2163 | 2164 | static void 2165 | rpsl_attr_check (const rpsl_attr_t *attr, const char *file, int line) 2166 | { 2167 | const GList *errors; 2168 | const rpsl_error_t *err; 2169 | 2170 | if (attr == NULL) { 2171 | fprintf(stderr, "rpsl_attr_check: NULL attr at %s:%d\n", 2172 | file, line); 2173 | exit(1); 2174 | } 2175 | if (attr->name == NULL) { 2176 | fprintf(stderr, "rpsl_attr_check: NULL name at %s:%d\n", 2177 | file, line); 2178 | exit(1); 2179 | } 2180 | if (attr->lcase_name == NULL) { 2181 | fprintf(stderr, "rpsl_attr_check: NULL name at %s:%d\n", 2182 | file, line); 2183 | exit(1); 2184 | } 2185 | if (attr->value == NULL) { 2186 | fprintf(stderr, "rpsl_attr_check: NULL value at %s:%d\n", 2187 | file, line); 2188 | exit(1); 2189 | } 2190 | rpsl_error_check(attr->errors, file, line); 2191 | if (attr->num < -1) { 2192 | fprintf(stderr, "rpsl_attr_check: bad num %d at %s:%d\n", 2193 | attr->num, file, line); 2194 | exit(1); 2195 | } 2196 | for (errors=attr->errors; errors != NULL; errors=g_list_next(errors)) { 2197 | err = errors->data; 2198 | if (err->attr_num != -1) { 2199 | fprintf(stderr, 2200 | "rpsl_attr_check: attr_num (%d) != -1 at %s:%d\n", 2201 | err->attr_num, file, line); 2202 | exit(1); 2203 | } 2204 | } 2205 | /* XXX: think of a check for attr_info.... */ 2206 | } 2207 | 2208 | /* XXX: could also verify keys - but that's a bit extreme */ 2209 | static void 2210 | count_attr_in_list (gpointer key, gpointer value, gpointer user_data) 2211 | { 2212 | GList *l; 2213 | int sum; 2214 | int *cnt; 2215 | 2216 | sum = 0; 2217 | for (l=value; l != NULL; l = g_list_next(l)) { 2218 | sum++; 2219 | } 2220 | cnt = (int *)user_data; 2221 | *cnt += sum; 2222 | } 2223 | 2224 | static void 2225 | rpsl_object_check (const rpsl_object_t *obj, const char *file, int line) 2226 | { 2227 | const GList *l; 2228 | int i; 2229 | const rpsl_attr_t *attr; 2230 | const GList *errors; 2231 | const rpsl_error_t *err; 2232 | int num_attr; 2233 | gboolean attr_in_list; 2234 | int cnt; 2235 | 2236 | if (obj == NULL) { 2237 | fprintf(stderr, "rpsl_object_check: NULL object at %s:%d\n", 2238 | file, line); 2239 | exit(1); 2240 | } 2241 | if (obj->attributes == NULL) { 2242 | fprintf(stderr, "rpsl_object_check: NULL attributes at %s:%d\n", 2243 | file, line); 2244 | exit(1); 2245 | } 2246 | if (obj->attr_lookup == NULL) { 2247 | fprintf(stderr, "rpsl_object_check: NULL attr_lookup at %s:%d\n", 2248 | file, line); 2249 | exit(1); 2250 | } 2251 | /* make sure each attribute in the hash is in the list */ 2252 | num_attr = g_list_length(obj->attributes); 2253 | cnt = 0; 2254 | g_hash_table_foreach(obj->attr_lookup, count_attr_in_list, &cnt); 2255 | if (num_attr != cnt) { 2256 | fprintf(stderr, 2257 | "rpsl_object_check: list count (%d) != hash count (%d) at %s:%d\n", 2258 | num_attr, cnt, 2259 | file, line); 2260 | exit(1); 2261 | } 2262 | for (l=obj->attributes, i=0; l != NULL; l=g_list_next(l), i++) { 2263 | attr = l->data; 2264 | rpsl_attr_check(attr, file, line); 2265 | /* make sure each attribute is in the hash table */ 2266 | l = g_hash_table_lookup(obj->attr_lookup, rpsl_attr_get_name(attr)); 2267 | attr_in_list = FALSE; 2268 | while ((l != NULL) && !attr_in_list) { 2269 | if (l->data == attr) { 2270 | attr_in_list = TRUE; 2271 | } 2272 | l = g_list_next(l); 2273 | } 2274 | if (!attr_in_list) { 2275 | fprintf(stderr, 2276 | "rpsl_object_check: attr #%d not in hash for %p %s:%d\n", 2277 | i, obj, file, line); 2278 | exit(1); 2279 | } 2280 | if (attr->num != i) { 2281 | fprintf(stderr, 2282 | "rpsl_object_check: attr #%d does not match offset %d %s:%d\n", 2283 | attr->num, i, file, line); 2284 | exit(1); 2285 | } 2286 | } 2287 | rpsl_error_check(obj->errors, file, line); 2288 | for (errors=attr->errors; errors != NULL; errors=g_list_next(errors)) { 2289 | err = errors->data; 2290 | if (err->attr_num >= num_attr) { 2291 | fprintf(stderr, 2292 | "rpsl_object_check: attr_num (%d) >= num_attr (%d) at %s:%d\n", 2293 | err->attr_num, num_attr, file, line); 2294 | exit(1); 2295 | } 2296 | } 2297 | /* XXX: think of a check for class_info... */ 2298 | } 2299 | #endif /* RUNTIME_CHECK */ 2300 | 2301 | #ifdef TEST 2302 | 2303 | #include <stdio.h> 2304 | 2305 | /* for a test, check to make sure our we convert the following values into 2306 | the expected results */ 2307 | struct { 2308 | gchar *input; 2309 | gchar *expected_result; 2310 | } test_attr[] = { 2311 | /* all tests on a single-line attributes */ 2312 | { "unmodified", "unmodified" }, 2313 | { "also unmodified", "also unmodified" }, 2314 | { " leading whitespace", "leading whitespace" }, 2315 | { "trailing whitespace ", "trailing whitespace" }, 2316 | { "compressed \t whitespace", "compressed whitespace" }, 2317 | { "value # some comment", "value" }, 2318 | { " lots of stuff# here too ", "lots of stuff" }, 2319 | { "", "" }, 2320 | /* basic tests on multi-line attributes */ 2321 | { "multiple\n" 2322 | " lines", 2323 | "multiple lines" }, 2324 | { "multiple\n" 2325 | "\ttablines", 2326 | "multiple tablines" }, 2327 | { "multiple\n" 2328 | "+pluslines", 2329 | "multiple pluslines" }, 2330 | { "\n" 2331 | " \n" 2332 | "\t\n" 2333 | "+\n", 2334 | "" }, 2335 | /* multi-line whitespace tests */ 2336 | { " leading\n" 2337 | " multiline whitespace", 2338 | "leading multiline whitespace" }, 2339 | { "\tleading\n" 2340 | "\ttabs multiline", 2341 | "leading tabs multiline" }, 2342 | { "\t \tleading\n" 2343 | "++ multiline", 2344 | "leading + multiline" }, 2345 | { "trailing\n" 2346 | " multiline \t", 2347 | "trailing multiline" }, 2348 | { "trailing\n" 2349 | "\ttabful multiline ", 2350 | "trailing tabful multiline" }, 2351 | { "trailing\n" 2352 | "+plus multiline\t", 2353 | "trailing plus multiline" }, 2354 | { "multiline \n" 2355 | " whitespace \n" 2356 | "+compression", 2357 | "multiline whitespace compression" }, 2358 | { " more \t\tmultiline \t\n" 2359 | "+ whitespace \t \t\n" 2360 | "+compression \t", 2361 | "more multiline whitespace compression" }, 2362 | /* multi-line comment tests */ 2363 | { "There # once was a man from Nantucket,\n" 2364 | "\tWhose nic-hdl # fell in the bitbucket.\n" 2365 | "\t\tHe grabbed his # nic-handle,\n" 2366 | "\t\tAnd made the mail queue # full.\n" 2367 | "\tBut # the mail bounced (we just chucked it).", 2368 | "There Whose nic-hdl He grabbed his And made the mail queue But" }, 2369 | { " # this is an evil,\n" 2370 | " # but legal,\n" 2371 | " # thing to do", 2372 | "" }, 2373 | { "this # is also \n" 2374 | "+ # legal, but less evil I suppose\n", 2375 | "this" }, 2376 | 2377 | }; 2378 | 2379 | #define NUM_TEST_ATTR (sizeof(test_attr) / sizeof(test_attr[0])) 2380 | 2381 | int 2382 | main() 2383 | { 2384 | int i; 2385 | gchar *actual_result; 2386 | int num_error; 2387 | 2388 | num_error = 0; 2389 | 2390 | /* test the attribute_clean() function */ 2391 | for (i=0; i<NUM_TEST_ATTR; i++) { 2392 | actual_result = attribute_clean(test_attr[i].input); 2393 | if (strcmp(actual_result, test_attr[i].expected_result) != 0) { 2394 | puts("ERROR: test failed"); 2395 | puts("--------[ input ]--------"); 2396 | puts(test_attr[i].input); 2397 | puts("---[ expected result ]---"); 2398 | puts(test_attr[i].expected_result); 2399 | puts("----[ actual result ]----"); 2400 | puts(actual_result); 2401 | puts("-------------------------"); 2402 | num_error++; 2403 | } 2404 | } 2405 | if (num_error == 0) { 2406 | printf("SUCCESS: all tests passed\n"); 2407 | return 0; 2408 | } else { 2409 | return 1; 2410 | } 2411 | } 2412 | 2413 | #endif