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