modules/rpsl/syntax_api.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following functions.
  1. my_g_strcasecmp
  2. attribute_clean
  3. attribute_clean_lines
  4. str_ends_with
  5. generic_list_split
  6. attribute_list_split
  7. ripe_list_split
  8. rpsl_error_assign
  9. rpsl_error_add
  10. rpsl_attr_syntax_check
  11. rpsl_attr_init
  12. rpsl_error_copy
  13. rpsl_attr_copy
  14. rpsl_attr_clean_copy
  15. rpsl_attr_delete
  16. rpsl_attr_delete_list
  17. rpsl_attr_get_name
  18. rpsl_attr_get_ofs
  19. rpsl_attr_get_value
  20. rpsl_attr_get_clean_value
  21. rpsl_attr_get_clean_lines
  22. rpsl_attr_get_split_list
  23. rpsl_attr_split_multiple
  24. rpsl_attr_errors
  25. object_is_comment
  26. template_check_needed
  27. renumber_attr
  28. rpsl_empty_attr
  29. rpsl_object_init
  30. rpsl_object_copy
  31. rpsl_object_copy_flattened
  32. rpsl_object_delete_helper
  33. rpsl_object_delete
  34. rpsl_object_get_class
  35. next_tabstop
  36. separate_leading_whitespace
  37. whitespace_length
  38. remove_columns
  39. add_aligned_val
  40. rpsl_object_get_text
  41. rpsl_object_get_num_attr
  42. rpsl_object_get_all_attr
  43. rpsl_object_get_attr
  44. rpsl_object_get_attr_by_ofs
  45. add_attr_to_object
  46. rpsl_object_append_attr
  47. rpsl_object_add_attr
  48. rpsl_object_remove_attr
  49. rpsl_object_remove_attr_name
  50. rpsl_object_errors
  51. rpsl_attr_is_required
  52. rpsl_attr_is_generated
  53. rpsl_attr_is_multivalued
  54. rpsl_attr_is_lookup
  55. rpsl_attr_is_key
  56. rpsl_object_is_deleted
  57. search_errors
  58. rpsl_attr_has_error
  59. rpsl_object_has_error
  60. rpsl_get_attr_id
  61. rpsl_get_class_id
  62. rpsl_load_dictionary
  63. rpsl_read_dictionary
  64. rpsl_get_classes
  65. rpsl_get_template
  66. ret_val
  67. rpsl_attr_check
  68. count_attr_in_list
  69. rpsl_object_check
  70. main

   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)
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 283 {
 284     return generic_list_split(val, ",");
 285 }
 286 
 287 static gchar **
 288 ripe_list_split (const char *val)
     /* [<][>][^][v][top][bottom][index][help] */
 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, 
     /* [<][>][^][v][top][bottom][index][help] */
 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, 
     /* [<][>][^][v][top][bottom][index][help] */
 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,
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 767 {
 768     chk_attr(attr);
 769 
 770     return attr->errors;
 771 }
 772 
 773 static gboolean 
 774 object_is_comment (const gchar *s)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 847 {
 848     attr->num = num;
 849 }
 850 
 851 static rpsl_attr_t *
 852 rpsl_empty_attr ()
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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, 
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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,
     /* [<][>][^][v][top][bottom][index][help] */
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,
     /* [<][>][^][v][top][bottom][index][help] */
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,
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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,
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
2031 {
2032     rpsl_level = level;
2033 }
2034 
2035 int 
2036 rpsl_read_dictionary ()
     /* [<][>][^][v][top][bottom][index][help] */
2037 {
2038     return rpsl_level;
2039 }
2040 
2041 
2042 const gchar* const *
2043 rpsl_get_classes ()
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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);
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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)
     /* [<][>][^][v][top][bottom][index][help] */
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()
     /* [<][>][^][v][top][bottom][index][help] */
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

/* [<][>][^][v][top][bottom][index][help] */