## 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] */