root/honeytrap/trunk/src/parseconf.c

Revision 1693, 18.2 kB (checked in by till, 3 months ago)

honeytrap
- common's changes to the cpuEmu plugin added
- new plugin priorities: PPRIO_DYNSRV and PPRIO_PERREAD
- heartbeat routine in submitMWserv plugin fixed
- signal-aware sleep() routine for usage in plugins added

Line 
1 /* parseconf.c
2  * Copyright (C) 2007 Tillmann Werner <tillmann.werner@gmx.de>
3  *
4  * This file is free software; as a special exception the author gives
5  * unlimited permission to copy and/or distribute it, with or without
6  * modifications, as long as this notice is preserved.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
10  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11  *
12  * The config file parser is based on lcfg from Paul Baecher.
13  */
14
15 #include <ctype.h>
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <stdarg.h>
19 #include <stddef.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/mman.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27
28 #include "parseconf.h"
29 #include "readconf.h"
30 #include "logging.h"
31
32
33 struct lcfg_scanner *    lcfg_scanner_new(struct lcfg *, int fd);
34 enum lcfg_status         lcfg_scanner_init(struct lcfg_scanner *);
35 enum lcfg_status         lcfg_scanner_next_token(struct lcfg_scanner *, struct lcfg_token *);
36 int                      lcfg_scanner_has_next(struct lcfg_scanner *);
37 void                     lcfg_scanner_delete(struct lcfg_scanner *);
38
39 struct lcfg_parser *  lcfg_parser_new(struct lcfg *, const char *);
40 enum lcfg_status      lcfg_parser_run(struct lcfg_parser *);
41 enum lcfg_status      lcfg_parser_accept(struct lcfg_parser *, lcfg_visitor_function, void *);
42 void                  lcfg_parser_delete(struct lcfg_parser *);
43
44
45 int lcfg_string_set(struct lcfg_string *s, const char *cstr) {
46         lcfg_string_trunc(s, 0);
47         return lcfg_string_cat_cstr(s, cstr);
48 }
49
50
51 /* make sure new_size bytes fit into the string */
52 inline static void lcfg_string_grow(struct lcfg_string *s, unsigned int new_size) {
53         /* always allocate one byte more than needed
54          * to make _cstr() working in any case without realloc. */
55         while( (new_size + 1) > s->capacity ) {
56                 s->capacity *= 2;
57                 s->str = realloc(s->str, s->capacity);
58         }
59 }
60
61 struct lcfg_string *lcfg_string_new() {
62         struct lcfg_string *s = malloc(sizeof(struct lcfg_string));
63        
64         s->capacity = 8;
65         s->size = 0;
66         s->str = malloc(s->capacity);
67        
68         return s;
69 }
70
71 struct lcfg_string *lcfg_string_new_copy(struct lcfg_string *s) {
72         struct lcfg_string *s_new = malloc(sizeof(struct lcfg_string));
73        
74         s_new->capacity = s->capacity;
75         s_new->size = s->size;
76         s_new->str = malloc(s_new->capacity);
77        
78         memcpy(s_new->str, s->str, s_new->size);
79        
80         return s_new;
81 }
82
83 int lcfg_string_cat_uint(struct lcfg_string *s, unsigned int i) {
84         unsigned int size_needed = 1;
85         unsigned int ii = i;
86         char c;
87        
88         while( ii > 10 ) {
89                 size_needed++;
90                 ii /= 10;
91         }
92        
93         lcfg_string_grow(s, s->size + size_needed);
94        
95         ii = size_needed - 1;
96         do {
97                 c = '0' + i % 10;
98                 s->str[s->size + ii--] = c;
99                 i /= 10;
100         } while( i != 0 );
101        
102         s->size += size_needed;
103        
104         return s->size;
105 }
106
107 int lcfg_string_find(struct lcfg_string *s, char c) {
108         int i;
109        
110         for( i = 0; i < s->size; i++ ) if( s->str[i] == c ) return i;
111        
112         return -1;
113 }
114
115 int lcfg_string_rfind(struct lcfg_string *s, char c) {
116         int i;
117        
118         for( i = s->size - 1; i >= 0; i-- ) if( s->str[i] == c ) return i;
119        
120         return -1;
121 }
122
123 void lcfg_string_trunc(struct lcfg_string *s, unsigned int max_size) {
124         if( max_size < s->size ) s->size = max_size;
125 }
126
127 int lcfg_string_cat_cstr(struct lcfg_string *s, const char *cstr) {
128         size_t len = strlen(cstr);
129        
130         lcfg_string_grow(s, s->size + len);
131        
132         memcpy(s->str + s->size, cstr, len);
133        
134         s->size += len;
135        
136         return s->size;
137 }
138
139 int lcfg_string_cat_char(struct lcfg_string *s, char c) {
140         lcfg_string_grow(s, s->size + 1);
141        
142         s->str[s->size++] = c;
143        
144         return s->size;
145 }
146
147 inline const char *lcfg_string_cstr(struct lcfg_string *s) {
148         s->str[s->size] = '\0';
149         return s->str;
150 }
151
152 inline unsigned int lcfg_string_len(struct lcfg_string *s) {
153         return s->size;
154 }
155
156 void lcfg_string_delete(struct lcfg_string *s) {
157         free(s->str);
158         free(s);
159 }
160
161 const char *lcfg_token_map[] = {
162         "null_token",
163         "T_IDENTIFIER",
164         "`='",
165         "T_STRING",
166         "`['",
167         "`]'",
168         "`,'",
169         "`{'",
170         "`}'"
171 };
172
173 struct lcfg_scanner {
174         struct lcfg *lcfg;
175        
176         int fd;
177         char buffer[LCFG_BUFSIZ];
178         int offset;
179         int size;
180         int eof;
181        
182         short line;
183         short col;
184        
185         struct lcfg_token prepared_token;
186         int token_eof;
187 };
188
189
190 static enum lcfg_status lcfg_scanner_buffer_fill(struct lcfg_scanner *s) {
191         if( (s->size = read(s->fd, s->buffer, LCFG_BUFSIZ)) < 0 ) {
192                 lcfg_error_set(s->lcfg, "read(): %m");
193                 return lcfg_status_error;
194         } else if( s->size == 0 ) s->eof = !0;
195         else s->offset = 0;
196        
197         return lcfg_status_ok;
198 }
199
200 static inline int lcfg_scanner_char_eof(struct lcfg_scanner *s) {
201         if( s->eof ) return !0;
202
203         if( s->size == 0 || s->offset == LCFG_BUFSIZ) lcfg_scanner_buffer_fill(s);
204         if( s->size < LCFG_BUFSIZ && s->offset == s->size ) s->eof = !0;
205
206         return s->eof;
207 }
208
209 static enum lcfg_status lcfg_scanner_char_read(struct lcfg_scanner *s, char *c) {
210         if( lcfg_scanner_char_eof(s) ) {
211                 lcfg_error_set(s->lcfg, "%s", "cannot read beyond eof");
212                 return lcfg_status_error;
213         }
214        
215         *c = s->buffer[s->offset++];
216
217         return lcfg_status_ok;
218 }
219
220 static enum lcfg_status lcfg_scanner_char_peek(struct lcfg_scanner *s, char *c) {
221         if( lcfg_scanner_char_eof(s) ) {
222                 lcfg_error_set(s->lcfg, "%s", "cannot peek beyond eof");
223                 return lcfg_status_error;
224         }
225        
226         *c = s->buffer[s->offset];
227
228         return lcfg_status_ok;
229 }
230
231 /* the beautiful lowlevel fsm */
232 static enum lcfg_status lcfg_scanner_token_read(struct lcfg_scanner *s) {
233         enum scanner_state {
234                 start = 0,
235                 comm_start,
236                 in_oneline,
237                 in_multiline,
238                 multiline_end,
239                 in_identifier,
240                 in_str,
241                 in_esc,
242                 esc_hex_exp_first,
243                 esc_hex_exp_second,
244                 invalid
245         };
246         enum scanner_state state = start;
247         char c = '\0';
248         char hex[3];
249        
250         s->prepared_token.type = lcfg_null_token;
251        
252         while( !lcfg_scanner_char_eof(s) ) {
253                 int consume = !0;
254                 lcfg_scanner_char_peek(s, &c);
255                
256                 switch( state ) {
257                 case start:
258                         switch( c ) {
259                         case ' ':
260                         case '\t':
261                         case '\r':
262                         case '\n':
263                                 break;
264                         case '=':
265                                 s->prepared_token.type = lcfg_equals;
266                                 break;
267                         case '[':
268                                 s->prepared_token.type = lcfg_sbracket_open;
269                                 break;
270                         case ']':
271                                 s->prepared_token.type = lcfg_sbracket_close;
272                                 break;
273                         case '{':
274                                 s->prepared_token.type = lcfg_brace_open;
275                                 break;
276                         case '}':
277                                 s->prepared_token.type = lcfg_brace_close;
278                                 break;
279                         case ',':
280                                 s->prepared_token.type = lcfg_comma;
281                                 break;
282                         case '/':
283                                 state = comm_start;
284                                 break;
285                         case '"':
286                                 state = in_str;
287                                 lcfg_string_trunc(s->prepared_token.string, 0);
288                                 break;
289                         default:
290                                 if( isalpha(c) ) {
291                                         lcfg_string_trunc(s->prepared_token.string, 0);
292                                         lcfg_string_cat_char(s->prepared_token.string, c);
293                                         state = in_identifier;
294                                 } else {
295                                         lcfg_error_set(s->lcfg, "parse error: invalid input character `%c' (0x%02x) near line %d, col %d",
296                                                 isprint(c) ? c : '.', c, s->line, s->col);
297                                         state = invalid;
298                                 }
299                         }
300                         break;
301                 case comm_start:
302                         if( c == '/' ) state = in_oneline;
303                         else if( c == '*' ) state = in_multiline;
304                         else {
305                                 lcfg_error_set(s->lcfg, "parse error: invalid input character `%c' (0x%02x) near line %d, col %d",
306                                         isprint(c) ? c : '.', c, s->line, s->col);
307                                 state = invalid;
308                         }
309                         break;
310                 case in_oneline:
311                         if( c == '\n' ) state = start;
312                         break;
313                 case in_multiline:
314                         if( c == '*' ) state = multiline_end;
315                         break;
316                 case multiline_end:
317                         if( c == '/' ) state = start;
318                         else if( c != '*' ) state = in_multiline;
319                         break;
320                 case in_identifier:
321                         if( isalnum(c) || c == '-' || c == '_' ) lcfg_string_cat_char(s->prepared_token.string, c);
322                         else {
323                                 s->prepared_token.type = lcfg_identifier;
324                                 consume = 0;
325                                 state = start;
326                         }
327                         break;
328                 case in_str:
329                         if( c == '"' ) {
330                                 s->prepared_token.type = lcfg_string;
331                                 state = start;
332                         } else if( c == '\\' ) {
333                                 state = in_esc;
334                         } else {
335                                 lcfg_string_cat_char(s->prepared_token.string, c);
336                         }
337                         break;
338                 case in_esc:
339                         state = in_str;
340                         switch( c ) {
341                         case '"':
342                                 lcfg_string_cat_char(s->prepared_token.string, '"');
343                                 break;
344                         case '\\':
345                                 lcfg_string_cat_char(s->prepared_token.string, '\\');
346                                 break;
347                         case 'n':
348                                 lcfg_string_cat_char(s->prepared_token.string, '\n');
349                                 break;
350                         case 't':
351                                 lcfg_string_cat_char(s->prepared_token.string, '\t');
352                                 break;
353                         case 'r':
354                                 lcfg_string_cat_char(s->prepared_token.string, '\r');
355                                 break;
356                         case '0':
357                                 lcfg_string_cat_char(s->prepared_token.string, '\0');
358                                 break;
359                         case 'x':
360                                 state = esc_hex_exp_first;
361                                 break;
362                         default:
363                                 lcfg_error_set(s->lcfg, "invalid string escape sequence `%c' near line %d, col %d", c, s->line, s->col);
364                                 state = invalid;
365                         }
366                         break;
367                 case esc_hex_exp_first:
368                         if( !isxdigit(c) ) {
369                                 lcfg_error_set(s->lcfg, "invalid hex escape sequence `%c' on line %d column %d", c, s->line, s->col);
370                                 state = invalid;
371                         }
372                         hex[0] = c;
373                         state = esc_hex_exp_second;
374                         break;
375                 case esc_hex_exp_second:
376                         if( !isxdigit(c) ) {
377                                 lcfg_error_set(s->lcfg, "invalid hex escape sequence `%c' on line %d column %d", c, s->line, s->col);
378                                 state = invalid;
379                         }
380                         hex[1] = c;
381                         hex[2] = '\0';
382                         lcfg_string_cat_char(s->prepared_token.string, strtoul(hex, NULL, 16));
383                         state = in_str;
384                         break;
385                 case invalid:
386                         break;
387                 }
388                
389                 /* this is technically not optimal (token position identified by last char), but it will suffice for now */
390                 s->prepared_token.line = s->line;
391                 s->prepared_token.col = s->col;
392        
393                 if( consume ) {
394                         lcfg_scanner_char_read(s, &c);
395                         if( c == '\n' ) {
396                                 s->line++;
397                                 s->col = 1;
398                         } else s->col++;
399                 }
400                
401                 if( s->prepared_token.type != lcfg_null_token || state == invalid ) break;
402         }
403        
404         if( state != start ) {
405                 if( state != invalid ) lcfg_error_set(s->lcfg, "parse error: premature end of file near line %d, col %d", s->line, s->col);
406                
407                 return lcfg_status_error;
408         }
409        
410         return lcfg_status_ok;
411 }
412
413 enum lcfg_status lcfg_scanner_init(struct lcfg_scanner *s) {
414         /* prepare the first token */
415         return lcfg_scanner_token_read(s);
416 }
417
418 int lcfg_scanner_has_next(struct lcfg_scanner *s) {
419         return s->prepared_token.type != lcfg_null_token;
420 }
421
422 enum lcfg_status lcfg_scanner_next_token(struct lcfg_scanner *s, struct lcfg_token *t) {
423         if( !lcfg_scanner_has_next(s) ) {
424                 lcfg_error_set(s->lcfg, "%s", "cannot access tokenstream beyond eof");
425                 return lcfg_status_error;
426         }
427        
428         memcpy(t, &s->prepared_token, sizeof(struct lcfg_token));
429         t->string = lcfg_string_new_copy(s->prepared_token.string);
430        
431         /* prepare the next token */
432         return lcfg_scanner_token_read(s);
433 }
434
435 struct lcfg_scanner *lcfg_scanner_new(struct lcfg *c, int fd) {
436         struct lcfg_scanner *s = malloc(sizeof(struct lcfg_scanner));
437        
438         memset(s, 0, sizeof(struct lcfg_scanner));
439        
440         s->lcfg = c;
441         s->fd = fd;
442        
443         s->line = s->col = 1;
444        
445         s->prepared_token.string = lcfg_string_new();
446        
447         return s;
448 }
449
450 void lcfg_scanner_delete(struct lcfg_scanner *s) {
451         lcfg_string_delete(s->prepared_token.string);
452         free(s);
453 }
454
455 struct lcfg_parser_value_pair {
456         char *key;
457         struct lcfg_string *value;
458 };
459
460
461 struct lcfg_parser {
462         struct lcfg *lcfg;
463         char *filename;
464         struct lcfg_scanner *scanner;
465        
466         struct lcfg_parser_value_pair *values;
467         unsigned int value_length;
468         unsigned int value_capacity;
469 };
470
471 static int lcfg_parser_add_value(struct lcfg_parser *p, const char *key, struct lcfg_string *value) {
472         if( p->value_length == p->value_capacity ) {
473                 p->value_capacity *= 2;
474                 p->values = realloc(p->values, sizeof(struct lcfg_parser_value_pair) * p->value_capacity);
475         }
476        
477         p->values[p->value_length].key = strdup(key);
478         p->values[p->value_length].value = lcfg_string_new_copy(value);
479        
480         return ++p->value_length;
481 }
482
483 struct lcfg_parser *lcfg_parser_new(struct lcfg *c, const char *filename) {
484         struct lcfg_parser *p = malloc(sizeof(struct lcfg_parser));
485        
486         memset(p, 0, sizeof(struct lcfg_parser));
487        
488         p->filename = strdup(filename);
489         p->lcfg = c;
490        
491         p->value_length = 0;
492         p->value_capacity = 8;
493         p->values = malloc(sizeof(struct lcfg_parser_value_pair) * p->value_capacity);
494        
495         return p;
496 }
497
498 /* this is a basic push down automata */
499 static enum lcfg_status lcfg_parser_parse(struct lcfg_parser *p) {
500         enum state { top_level = 0, exp_equals, exp_value, in_list, in_map, invalid };
501         /*const char *state_map[] = { "top_level", "exp_equals", "exp_value", "in_list", "in_map", "invalid" };*/
502
503         struct state_element {
504                 enum state s;
505                 int list_counter;
506         };
507
508         /* start of ugly preproc stuff */
509         #define STATE_STACK_PUSH(t) \
510         if( ssi + 1 == state_stack_size ) \
511         { \
512                 state_stack_size *= 2; \
513                 state_stack = realloc(state_stack, state_stack_size * sizeof(struct state_element)); \
514         } \
515         state_stack[++ssi].s = t; \
516         state_stack[ssi].list_counter = 0
517         #define STATE_STACK_POP() ssi--
518         #define PATH_PUSH_STR(s) \
519         if( lcfg_string_len(current_path) != 0 ) \
520         { \
521                 lcfg_string_cat_char(current_path, '.'); \
522         } \
523         lcfg_string_cat_cstr(current_path, s);
524         #define PATH_PUSH_INT(i) \
525         if( lcfg_string_len(current_path) != 0 ) \
526         { \
527                 lcfg_string_cat_char(current_path, '.'); \
528         } \
529         lcfg_string_cat_uint(current_path, i);
530         #define PATH_POP() \
531         if( lcfg_string_rfind(current_path, '.') != -1 ) \
532         { \
533                 lcfg_string_trunc(current_path, lcfg_string_rfind(current_path, '.')); \
534         } \
535         else \
536         { \
537                 lcfg_string_trunc(current_path, 0); \
538         }
539         /* end of ugly preproc stuff */
540
541         if( lcfg_scanner_init(p->scanner) != lcfg_status_ok ) return lcfg_status_error;
542        
543         int state_stack_size = 8;
544         int ssi = 0; /* ssi = state stack index */
545         struct state_element *state_stack = malloc(sizeof(struct state_element) * state_stack_size);
546        
547         state_stack[ssi].s = top_level;
548         state_stack[ssi].list_counter = 0;
549        
550         struct lcfg_token t;
551         struct lcfg_string *current_path = lcfg_string_new();
552        
553         while( lcfg_scanner_has_next(p->scanner) && state_stack[ssi].s != invalid ) {
554                 if( lcfg_scanner_next_token(p->scanner, &t) != lcfg_status_ok ) {
555                         free(state_stack);
556                         lcfg_string_delete(t.string);
557                         lcfg_string_delete(current_path);
558                         return lcfg_status_error;
559                 }
560                
561                 switch( state_stack[ssi].s ) {
562                 case top_level:
563                 case in_map:
564                         if( t.type == lcfg_identifier ) {
565                                 PATH_PUSH_STR(lcfg_string_cstr(t.string));
566                                 STATE_STACK_PUSH(exp_equals);
567                         } else if( state_stack[ssi].s == in_map && t.type == lcfg_brace_close ) {
568                                 STATE_STACK_POP();
569                                 PATH_POP();
570                         } else {
571                                 lcfg_error_set(p->lcfg, "invalid token (%s) near line %d column %d: expected identifier%s",
572                                         lcfg_token_map[t.type], t.line, t.col, state_stack[ssi].s == in_map ? " or `}'" : "");
573                                 state_stack[ssi].s = invalid;
574                         }
575                         break;
576                 case exp_equals:
577                         if( t.type == lcfg_equals ) state_stack[ssi].s = exp_value;
578                         else {
579                                 lcfg_error_set(p->lcfg, "invalid token (%s) near line %d column %d: expected `='",
580                                         lcfg_token_map[t.type], t.line, t.col);
581                                 state_stack[ssi].s = invalid;
582                         }
583                         break;
584                 case exp_value:
585                         if( t.type == lcfg_string ) {
586                                 lcfg_parser_add_value(p, lcfg_string_cstr(current_path), t.string);
587                                 STATE_STACK_POP();
588                                 PATH_POP();
589                         } else if( t.type == lcfg_sbracket_open ) state_stack[ssi].s = in_list;
590                         else if( t.type == lcfg_brace_open ) state_stack[ssi].s = in_map;
591                         else {
592                                 lcfg_error_set(p->lcfg, "invalid token (%s) near line %d column %d: expected string, `[' or `{'",
593                                         lcfg_token_map[t.type], t.line, t.col);
594                                 state_stack[ssi].s = invalid;
595                         }
596                         break;
597                 case in_list:
598                         if( t.type == lcfg_comma ); /* ignore comma */
599                         else if( t.type == lcfg_string ) {
600                                 PATH_PUSH_INT(state_stack[ssi].list_counter);
601                                 lcfg_parser_add_value(p, lcfg_string_cstr(current_path), t.string);
602                                 PATH_POP();
603                                 state_stack[ssi].list_counter++;
604                         } else if( t.type == lcfg_sbracket_open ) {
605                                 PATH_PUSH_INT(state_stack[ssi].list_counter);
606                                 state_stack[ssi].list_counter++;
607                                 STATE_STACK_PUSH(in_list);
608                         } else if( t.type == lcfg_brace_open ) {
609                                 PATH_PUSH_INT(state_stack[ssi].list_counter);
610                                 state_stack[ssi].list_counter++;
611                                 STATE_STACK_PUSH(in_map);
612                         } else if( t.type == lcfg_sbracket_close ) {
613                                 PATH_POP();
614                                 STATE_STACK_POP();
615                         } else {
616                                 lcfg_error_set(p->lcfg, "invalid token (%s) near line %d column %d: expected string, `[', `{', `,' or `]'",
617                                         lcfg_token_map[t.type], t.line, t.col);
618                                 state_stack[ssi].s = invalid;
619                         }
620                         break;
621                 case invalid: /* unreachable */
622                         break;
623                 }
624                
625                 lcfg_string_delete(t.string);
626         }
627
628         lcfg_string_delete(current_path);       
629        
630         if( state_stack[ssi].s == top_level && ssi == 0 ) {
631                 free(state_stack);
632                 return lcfg_status_ok;
633         } else {
634                 free(state_stack);
635                 return lcfg_status_error;
636         }
637 }
638
639 enum lcfg_status lcfg_parser_run(struct lcfg_parser *p) {       
640         int fd = open(p->filename, 0);
641         enum lcfg_status status;
642        
643         if( fd < 0 ) {
644                 lcfg_error_set(p->lcfg, "open(): %m");
645                 return lcfg_status_error;
646         }
647        
648         p->scanner = lcfg_scanner_new(p->lcfg, fd);
649        
650         status = lcfg_parser_parse(p); 
651        
652         close(fd);
653        
654         return status;
655 }
656
657 enum lcfg_status lcfg_parser_accept(struct lcfg_parser *p, lcfg_visitor_function fn, void *user_data) {
658         int i;
659        
660         for( i = 0; i < p->value_length; i++ ) {
661                 if( fn(p->values[i].key, (void *)lcfg_string_cstr(p->values[i].value),
662                         lcfg_string_len(p->values[i].value), user_data) != lcfg_status_ok ) {
663                         lcfg_error_set(p->lcfg, "%s", "configuration value traversal aborted upon user request");
664                         return lcfg_status_error;
665                 }
666         }
667        
668         return lcfg_status_ok;
669 }
670
671 void lcfg_parser_delete(struct lcfg_parser *p) {
672         if (p->scanner) lcfg_scanner_delete(p->scanner);
673        
674         int i;
675        
676         for( i = 0; i < p->value_length; i++ ) {
677                 free(p->values[i].key);
678                 lcfg_string_delete(p->values[i].value);
679         }
680         free(p->values);
681         free(p->filename);
682         free(p);
683 }
684
685 struct lcfg *lcfg_new(const char *filename) {
686         struct lcfg *c = malloc(sizeof(struct lcfg));
687         memset(c, 0, sizeof(struct lcfg));
688        
689         c->parser = lcfg_parser_new(c, filename);
690        
691         return c;
692 }
693
694 void lcfg_delete(struct lcfg *c) {
695         if (c->parser) lcfg_parser_delete(c->parser);
696         free(c);
697 }
698
699 const char *lcfg_error_get(struct lcfg *c) {
700         return c->error;
701 }
702
703 enum lcfg_status lcfg_parse(struct lcfg *c) {
704         return lcfg_parser_run(c->parser);
705 }
706
707 enum lcfg_status lcfg_accept(struct lcfg *c, lcfg_visitor_function fn, void *user_data) {
708         return lcfg_parser_accept(c->parser, fn, user_data);
709 }
710
711 void lcfg_error_set(struct lcfg *c, const char *fmt, ...) {     
712         va_list ap;
713         va_start(ap, fmt);
714         vsnprintf(c->error, sizeof(c->error), fmt, ap);
715         va_end(ap);
716 }
717
718 struct lcfg *parse_config_file(const char *filename) {
719         struct lcfg *c = NULL;
720
721         if (!filename) {
722                 fprintf(stderr, "  Error - No configuration file name given.\n");
723                 return(NULL);
724         }
725
726         /* parse file */
727         c = lcfg_new(filename);
728         if (lcfg_parse(c) != lcfg_status_ok) {
729                 fprintf(stderr, "  Error -  Unable to parse %s: %s\n", filename, lcfg_error_get(c));
730                 lcfg_delete(c);
731                 return(NULL);
732         }
733        
734         return(c);
735 }
Note: See TracBrowser for help on using the browser.