/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following functions.
- my_g_strcasecmp
- attribute_clean
- attribute_clean_lines
- str_ends_with
- generic_list_split
- attribute_list_split
- ripe_list_split
- rpsl_error_assign
- rpsl_error_add
- rpsl_attr_syntax_check
- rpsl_attr_init
- rpsl_error_copy
- rpsl_attr_copy
- rpsl_attr_clean_copy
- rpsl_attr_delete
- rpsl_attr_delete_list
- rpsl_attr_get_name
- rpsl_attr_get_ofs
- rpsl_attr_get_value
- rpsl_attr_get_clean_value
- rpsl_attr_get_clean_lines
- rpsl_attr_get_split_list
- rpsl_attr_split_multiple
- rpsl_attr_errors
- object_is_comment
- template_check_needed
- renumber_attr
- rpsl_empty_attr
- rpsl_object_init
- rpsl_object_copy
- rpsl_object_copy_flattened
- rpsl_object_delete_helper
- rpsl_object_delete
- rpsl_object_get_class
- next_tabstop
- separate_leading_whitespace
- whitespace_length
- remove_columns
- add_aligned_val
- rpsl_object_get_text
- rpsl_object_get_num_attr
- rpsl_object_get_all_attr
- rpsl_object_get_attr
- rpsl_object_get_attr_by_ofs
- add_attr_to_object
- rpsl_object_append_attr
- rpsl_object_add_attr
- rpsl_object_remove_attr
- rpsl_object_remove_attr_name
- rpsl_object_errors
- rpsl_attr_is_required
- rpsl_attr_is_generated
- rpsl_attr_is_multivalued
- rpsl_attr_is_lookup
- rpsl_attr_is_key
- rpsl_object_is_deleted
- search_errors
- rpsl_attr_has_error
- rpsl_object_has_error
- rpsl_get_attr_id
- rpsl_get_class_id
- rpsl_load_dictionary
- rpsl_read_dictionary
- rpsl_get_classes
- rpsl_get_template
- ret_val
- rpsl_attr_check
- count_attr_in_list
- rpsl_object_check
- 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