Statistics
| Branch: | Revision:

janus-gateway / config.c @ 1f44763c

History | View | Annotate | Download (12.3 KB)

1
/*! \file    config.c
2
 * \author   Lorenzo Miniero <lorenzo@meetecho.com>
3
 * \copyright GNU General Public License v3
4
 * \brief    Configuration files parsing
5
 * \details  Implementation of a parser of INI configuration files.
6
 * 
7
 * \ingroup core
8
 * \ref core
9
 */
10

    
11
#include <stdio.h>
12
#include <stdlib.h>
13
#include <string.h>
14
#include <ctype.h>
15
#include <errno.h>
16

    
17
#include "config.h"
18
#include "debug.h"
19
#include "utils.h"
20

    
21

    
22
/* Filename helper */
23
static char *get_filename(const char *path) {
24
        char *filename = NULL;
25
        if(path)
26
                filename = strrchr(path, '/')+1;
27
        return filename;
28
}
29

    
30
/* Trimming helper */
31
static char *ltrim(char *s) {
32
        if(strlen(s) == 0)
33
                return s;
34
        while(isspace(*s))
35
                s++;
36
        return s;
37
}
38

    
39
static char *rtrim(char *s) {
40
        if(strlen(s) == 0)
41
                return s;
42
        char *back = s + strlen(s);
43
        while(isspace(*--back));
44
        *(back+1) = '\0';
45
        return s;
46
}
47

    
48
static char *trim(char *s) {
49
        if(strlen(s) == 0)
50
                return s;
51
        return rtrim(ltrim(s)); 
52
}
53

    
54

    
55
/* Memory management helpers */
56
static void janus_config_free_item(gpointer data) {
57
        janus_config_item *i = (janus_config_item *)data;
58
        if(i) {
59
                if(i->name)
60
                        g_free((gpointer)i->name);
61
                if(i->value)
62
                        g_free((gpointer)i->value);
63
                g_free(i);
64
        }
65
}
66

    
67
static void janus_config_free_category(gpointer data) {
68
        janus_config_category *c = (janus_config_category *)data;
69
        if(c) {
70
                if(c->name)
71
                        g_free((gpointer)c->name);
72
                if(c->items)
73
                        g_list_free_full(c->items, janus_config_free_item);
74
                g_free(c);
75
        }
76
}
77

    
78

    
79
/* Public methods */
80
janus_config *janus_config_parse(const char *config_file) {
81
        if(config_file == NULL)
82
                return NULL;
83
        char *filename = get_filename(config_file);
84
        if(filename == NULL) {
85
                JANUS_LOG(LOG_ERR, "Invalid filename %s\n", config_file);
86
                return NULL;
87
        }
88
        /* Open file */
89
        FILE *file = fopen(config_file, "rt");
90
        if(!file) {
91
                JANUS_LOG(LOG_ERR, "  -- Error reading configuration file '%s'... error %d (%s)\n", filename, errno, strerror(errno));
92
                return NULL;
93
        }
94
        /* Create configuration instance */
95
        janus_config *jc = g_malloc0(sizeof(janus_config));
96
        jc->name = g_strdup(filename);
97
        /* Traverse and parse it */
98
        int line_number = 0;
99
        char line_buffer[BUFSIZ];
100
        janus_config_category *cg = NULL;
101
        while(fgets(line_buffer, sizeof(line_buffer), file)) {
102
                line_number++;
103
                if(strlen(line_buffer) == 0)
104
                        continue;
105
                /* Strip comments */
106
                char *line = line_buffer, *sc = line, *c = NULL;
107
                while((c = strchr(sc, ';')) != NULL) {
108
                        if(c == line || *(c-1) != '\\') {
109
                                /* Comment starts here */
110
                                *c = '\0';
111
                                break;
112
                        }
113
                        /* Escaped semicolon, remove the slash */
114
                        sc = c-1;
115
                        /* length will be at least 2: ';' '\0' */
116
                        memmove(sc, c, strlen(c)+1);
117
                        /* Go on */
118
                        sc++;
119
                }
120
                /* Trim (will remove newline characters too) */
121
                line = trim(line);
122
                if(strlen(line) == 0)
123
                        continue;
124
                /* Parse */
125
                if(line[0] == '[') {
126
                        /* Category */
127
                        line++;
128
                        char *end = strchr(line, ']');
129
                        if(end == NULL) {
130
                                JANUS_LOG(LOG_ERR, "Error parsing category at line %d: syntax error (%s)\n", line_number, filename);
131
                                janus_config_destroy(jc);
132
                                return NULL;
133
                        }
134
                        *end = '\0';
135
                        line = trim(line);
136
                        if(strlen(line) == 0) {
137
                                JANUS_LOG(LOG_ERR, "Error parsing category at line %d: no name (%s)\n", line_number, filename);
138
                                janus_config_destroy(jc);
139
                                return NULL;
140
                        }
141
                        cg = janus_config_add_category(jc, line);
142
                        if(cg == NULL) {
143
                                JANUS_LOG(LOG_ERR, "Error adding category %s (%s)\n", line, filename);
144
                                janus_config_destroy(jc);
145
                                return NULL;
146
                        }
147
                } else {
148
                        /* Item */
149
                        char *name = line, *value = strchr(line, '=');
150
                        if(value == NULL || value == line) {
151
                                JANUS_LOG(LOG_ERR, "Error parsing item at line %d (%s)\n", line_number, filename);
152
                                janus_config_destroy(jc);
153
                                return NULL;
154
                        }
155
                        *value = '\0';
156
                        name = trim(name);
157
                        if(strlen(name) == 0) {
158
                                JANUS_LOG(LOG_ERR, "Error parsing item at line %d: no name (%s)\n", line_number, filename);
159
                                janus_config_destroy(jc);
160
                                return NULL;
161
                        }
162
                        value++;
163
                        value = trim(value);
164
                        if(strlen(value) == 0) {
165
                                JANUS_LOG(LOG_ERR, "Error parsing item at line %d: no value (%s)\n", line_number, filename);
166
                                janus_config_destroy(jc);
167
                                return NULL;
168
                        }
169
                        if(*value == '>') {
170
                                value++;
171
                                value = trim(value);
172
                                if(strlen(value) == 0) {
173
                                        JANUS_LOG(LOG_ERR, "Error parsing item at line %d: no value (%s)\n", line_number, filename);
174
                                        janus_config_destroy(jc);
175
                                        return NULL;
176
                                }
177
                        }
178
                        if(janus_config_add_item(jc, cg ? cg->name : NULL, name, value) == NULL) {
179
                                if(cg == NULL)
180
                                        JANUS_LOG(LOG_ERR, "Error adding item %s (%s)\n", name, filename);
181
                                else
182
                                        JANUS_LOG(LOG_ERR, "Error adding item %s to category %s (%s)\n", name, cg->name, filename);
183
                                janus_config_destroy(jc);
184
                                return NULL;
185
                        }
186
                }
187
        }
188
        fclose(file);
189
        return jc;
190
}
191

    
192
janus_config *janus_config_create(const char *name) {
193
        janus_config *jc = g_malloc0(sizeof(janus_config));
194
        if(jc == NULL) {
195
                JANUS_LOG(LOG_FATAL, "Memory error!\n");
196
                return NULL;
197
        }
198
        if(name != NULL) {
199
                jc->name = g_strdup(name);
200
        }
201
        return jc;
202
}
203

    
204
GList *janus_config_get_categories(janus_config *config) {
205
        if(config == NULL)
206
                return NULL;
207
        return config->categories;
208
}
209

    
210
janus_config_category *janus_config_get_category(janus_config *config, const char *name) {
211
        if(config == NULL || name == NULL)
212
                return NULL;
213
        if(config->categories == NULL)
214
                return NULL;
215
        GList *l = config->categories;
216
        while(l) {
217
                janus_config_category *c = (janus_config_category *)l->data;
218
                if(c && c->name && !strcasecmp(name, c->name))
219
                        return c;
220
                l = l->next;
221
        }
222
        return NULL;
223
}
224

    
225
GList *janus_config_get_items(janus_config_category *category) {
226
        if(category == NULL)
227
                return NULL;
228
        return category->items;
229
}
230

    
231
janus_config_item *janus_config_get_item(janus_config_category *category, const char *name) {
232
        if(category == NULL || name == NULL)
233
                return NULL;
234
        if(category->items == NULL)
235
                return NULL;
236
        GList *l = category->items;
237
        while(l) {
238
                janus_config_item *i = (janus_config_item *)l->data;
239
                if(i && i->name && !strcasecmp(name, i->name))
240
                        return i;
241
                l = l->next;
242
        }
243
        return NULL;
244
}
245

    
246
janus_config_item *janus_config_get_item_drilldown(janus_config *config, const char *category, const char *name) {
247
        if(config == NULL || category == NULL || name == NULL)
248
                return NULL;
249
        janus_config_category *c = janus_config_get_category(config, category);
250
        if(c == NULL)
251
                return NULL;
252
        return janus_config_get_item(c, name);
253
}
254

    
255
janus_config_category *janus_config_add_category(janus_config *config, const char *category) {
256
        if(config == NULL || category == NULL)
257
                return NULL;
258
        janus_config_category *c = janus_config_get_category(config, category);
259
        if(c != NULL) {
260
                /* Category exists, return this */
261
                return c;
262
        }
263
        c = g_malloc0(sizeof(janus_config_category));
264
        if(c == NULL) {
265
                JANUS_LOG(LOG_FATAL, "Memory error!\n");
266
                return NULL;
267
        }
268
        c->name = g_strdup(category);
269
        config->categories = g_list_append(config->categories, c);
270
        return c;
271
}
272

    
273
int janus_config_remove_category(janus_config *config, const char *category) {
274
        if(config == NULL || category == NULL)
275
                return -1;
276
        janus_config_category *c = janus_config_get_category(config, category);
277
        if(c) {
278
                config->categories = g_list_remove(config->categories, c);
279
                janus_config_free_category(c);
280
                return 0;
281
        }
282
        return -2;
283
}
284

    
285
janus_config_item *janus_config_add_item(janus_config *config, const char *category, const char *name, const char *value) {
286
        if(config == NULL || name == NULL || value == NULL)
287
                return NULL;
288
        /* This will return the existing category, if it exists already */
289
        janus_config_category *c = category ? janus_config_add_category(config, category) : NULL;
290
        if(category != NULL && c == NULL) {
291
                /* Create it */
292
                JANUS_LOG(LOG_FATAL, "Category error!\n");
293
                return NULL;
294
        }
295
        janus_config_item *item = c ? janus_config_get_item(c, name) : NULL;
296
        if(item == NULL) {
297
                /* Create it */
298
                item = g_malloc0(sizeof(janus_config_item));
299
                if(item == NULL) {
300
                        JANUS_LOG(LOG_FATAL, "Memory error!\n");
301
                        return NULL;
302
                }
303
                item->name = g_strdup(name);
304
                item->value = g_strdup(value);
305
                if(c != NULL) {
306
                        /* Add to category */
307
                        c->items = g_list_append(c->items, item);
308
                } else {
309
                        /* Uncategorized item */
310
                        config->items = g_list_append(config->items, item);
311
                }
312
        } else {
313
                /* Update it */
314
                char *item_value = g_strdup(value);
315
                if(item->value)
316
                        g_free((gpointer)item->value);
317
                item->value = item_value;
318
        }
319
        return item;
320
}
321

    
322
int janus_config_remove_item(janus_config *config, const char *category, const char *name) {
323
        if(config == NULL || category == NULL || name == NULL)
324
                return -1;
325
        janus_config_category *c = janus_config_add_category(config, category);
326
        if(c == NULL)
327
                return -2;
328
        janus_config_item *item = janus_config_get_item(c, name);
329
        if(item == NULL)
330
                return -3;
331
        c->items = g_list_remove(c->items, item);
332
        janus_config_free_item(item);
333
        return 0;
334
}
335

    
336
void janus_config_print(janus_config *config) {
337
        if(config == NULL)
338
                return;
339
        JANUS_LOG(LOG_VERB, "[%s]\n", config->name ? config->name : "??");
340
        if(config->items) {
341
                GList *l = config->items;
342
                while(l) {
343
                        janus_config_item *i = (janus_config_item *)l->data;
344
                        JANUS_LOG(LOG_VERB, "        %s: %s\n", i->name ? i->name : "??", i->value ? i->value : "??");
345
                        l = l->next;
346
                }
347
        }
348
        if(config->categories) {
349
                GList *l = config->categories;
350
                while(l) {
351
                        janus_config_category *c = (janus_config_category *)l->data;
352
                        JANUS_LOG(LOG_VERB, "    [%s]\n", c->name ? c->name : "??");
353
                        if(c->items) {
354
                                GList *li = c->items;
355
                                while(li) {
356
                                        janus_config_item *i = (janus_config_item *)li->data;
357
                                        JANUS_LOG(LOG_VERB, "        %s: %s\n", i->name ? i->name : "??", i->value ? i->value : "??");
358
                                        li = li->next;
359
                                }
360
                        }
361
                        l = l->next;
362
                }
363
        }
364
}
365

    
366
gboolean janus_config_save(janus_config *config, const char *folder, const char *filename) {
367
        if(config == NULL)
368
                return -1;
369
        FILE *file = NULL;
370
        char path[1024];
371
        if(folder != NULL) {
372
                /* Create folder, if needed */
373
                if(janus_mkdir(folder, 0755) < 0) {
374
                        JANUS_LOG(LOG_ERR, "Couldn't save configuration file, error creating folder '%s'...\n", folder);
375
                        return -2;
376
                }
377
                g_snprintf(path, 1024, "%s/%s.cfg", folder, filename);
378
        } else {
379
                g_snprintf(path, 1024, "%s.cfg", filename);
380
        }
381
        file = fopen(path, "wt");
382
        if(file == NULL) {
383
                JANUS_LOG(LOG_ERR, "Couldn't save configuration file, error opening file '%s'...\n", path);
384
                return -3;
385
        }
386
        /* Print a header */
387
        char date[64], header[256];
388
        struct tm tmresult;
389
        time_t ltime = time(NULL);
390
        localtime_r(&ltime, &tmresult);
391
        strftime(date, sizeof(date), "%a %b %e %T %Y", &tmresult);
392
        g_snprintf(header, 256, ";\n; File automatically generated on %s\n;\n\n", date);
393
        fwrite(header, sizeof(char), strlen(header), file);
394
        /* Go on with the configuration */
395
        if(config->items) {
396
                GList *l = config->items;
397
                while(l) {
398
                        janus_config_item *i = (janus_config_item *)l->data;
399
                        if(i->name && i->value) {
400
                                fwrite(i->name, sizeof(char), strlen(i->name), file);
401
                                fwrite(" = ", sizeof(char), 3, file);
402
                                fwrite(i->value, sizeof(char), strlen(i->value), file);
403
                                fwrite("\n", sizeof(char), 1, file);
404
                        }
405
                        l = l->next;
406
                }
407
        }
408
        if(config->categories) {
409
                GList *l = config->categories;
410
                while(l) {
411
                        janus_config_category *c = (janus_config_category *)l->data;
412
                        if(c->name) {
413
                                fwrite("[", sizeof(char), 1, file);
414
                                fwrite(c->name, sizeof(char), strlen(c->name), file);
415
                                fwrite("]\n", sizeof(char), 2, file);
416
                                if(c->items) {
417
                                        GList *li = c->items;
418
                                        while(li) {
419
                                                janus_config_item *i = (janus_config_item *)li->data;
420
                                                if(i->name && i->value) {
421
                                                        fwrite(i->name, sizeof(char), strlen(i->name), file);
422
                                                        fwrite(" = ", sizeof(char), 3, file);
423
                                                        /* If the value contains a semicolon, escape it */
424
                                                        if(strchr(i->value, ';')) {
425
                                                                char *value = g_strdup(i->value);
426
                                                                value = janus_string_replace((char *)value, ";", "\\;");
427
                                                                fwrite(value, sizeof(char), strlen(value), file);
428
                                                                fwrite("\n", sizeof(char), 1, file);
429
                                                                g_free(value);
430
                                                        } else {
431
                                                                /* No need to escape */
432
                                                                fwrite(i->value, sizeof(char), strlen(i->value), file);
433
                                                                fwrite("\n", sizeof(char), 1, file);
434
                                                        }
435
                                                }
436
                                                li = li->next;
437
                                        }
438
                                }
439
                        }
440
                        fwrite("\r\n", sizeof(char), 2, file);
441
                        l = l->next;
442
                }
443
        }
444
        fclose(file);
445
        return 0;
446
}
447

    
448
void janus_config_destroy(janus_config *config) {
449
        if(config == NULL)
450
                return;
451
        if(config->items) {
452
                g_list_free_full(config->items, janus_config_free_item);
453
                config->items = NULL;
454
        }
455
        if(config->categories) {
456
                g_list_free_full(config->categories, janus_config_free_category);
457
                config->items = NULL;
458
        }
459
        if(config->name)
460
                g_free((gpointer)config->name);
461
        g_free((gpointer)config);
462
        config = NULL;
463
}