source: branches/stable/mondo/src/lib/mr_conf.c @ 1583

Last change on this file since 1583 was 1583, checked in by bruno, 12 years ago

Improve error messages from conf file lib

  • Property svn:eol-style set to native
File size: 11.8 KB
Line 
1/* mr_conf.c
2 *
3 * $Id$
4 *
5 * Based on the work done by Anton (c)2002-2004 Anton Kulchitsky  mailto:anton@kulchitsky.org
6 * Code (c)2006 Bruno Cornec <bruno@mondorescue.org>
7 *   
8 *     Main file of mr_conf : a very small and simple
9 *     library for configuration file reading
10 *
11 * Provided under the GPLv2
12 */
13
14
15#include <stdlib.h>
16#include <stdio.h>
17#include <string.h>
18
19#include "mr_types.h"
20#include "mr_msg.h"
21#include "mr_mem.h"
22#include "mr_gettext.h"
23
24/* error flags */
25#define MRCONF_NO_ERROR             0x0
26#define MRCONF_BAD_FILE             0x1
27#define MRCONF_READING_FAILED       0x3
28#define MRCONF_NO_GROUP_START       0x4
29#define MRCONF_NO_GROUP_END         0x5
30#define MRCONF_FIELD_NOT_FOUND      0x6
31#define MRCONF_FIELD_NO_VALUE       0x7
32#define MRCONF_TOO_LONG_STRING      0x8
33#define MRCONF_CLOSE_BUT_NOT_OPEN   0x9
34#define MRCONF_OPEN_OPENED          0xA
35#define MRCONF_CALL_BUT_NOT_OPEN    0xB
36#define MRCONF_STRING_QUOTE         0xC
37#define MRCONF_STRING_ENDQUOTE      0xD
38
39/*setting flags*/
40#define MRCONF_FLAG_VERBOSE         0x1
41
42/*All strings of the library are here*/
43#define MRCONF_STR_ERROR          _("MRCONF: Error:")
44#define MRCONF_STR_WARNING        _("MRCONF: Warning:")
45
46#define MRCONF_STR_BAD_FILE       _("cannot open the file: ")
47#define MRCONF_STR_ALLOC_FAILED   _("cannot allocate the memory")
48#define MRCONF_STR_READING_FAILED _("cannot read file into buffer")
49#define MRCONF_STR_DEFAULT_ERROR  _("default")
50#define MRCONF_STR_FIELD_NOT_FOUND   _("the field is not found, field:")
51#define MRCONF_STR_FIELD_NO_VALUE   _("the field :")
52#define MRCONF_STR_OPEN_OPENED _("attempt to open mr_conf, but it is opened: aborted")
53#define MRCONF_STR_HALT _("MRCONF: Error occured: immidiate halt")
54
55/*warnings*/
56#define MRCONF_STR_SET_TO_ZERO    _("variable will be set to 0:")
57#define MRCONF_STR_IGNORE    _("has no value, ignoring it")
58#define MRCONF_STR_CLOSE_BUT_NOT_OPEN _("attempt to close mr_conf but it has not been opened yet")
59#define MRCONF_STR_CALL_BUT_NOT_OPEN _("attempt to use mr_conf when it has not been opened yet")
60#define MRCONF_STR_STRING_QUOTE     _("string should be surrounded by quotes")
61#define MRCONF_STR_STRING_ENDQUOTE      _("a string is not finished by a quote")
62
63/*Flags of internal state*/
64#define MRCONF_INTFLAG_OPEN 0x1 /*set if memory allocated */
65
66/* Character for comments */
67#define MRCONF_COMM_CHAR '#'
68
69/*"private" members declarations*/
70static size_t mr_conf_filesize(const char *name);
71#define mr_conf_error_msg(x, y) {mr_conf_error_msg_int(x, y, __LINE__,__FILE__);}
72static void mr_conf_error_msg_int(int error_code, const char *add_line, int line, const char *file);
73static void mr_conf_remove_comments(void);
74static int mr_conf_check_int_flag(const int flag);
75static void mr_conf_set_int_flag(const int flag);
76static void mr_conf_drop_int_flag(const int flag);
77
78/*global "private" variables*/
79static char *buffer = NULL;     /*buffer for configuration file */
80static int internal_flags = 0;  /*state of the module */
81static FILE *CONF = NULL;       /* Configuration file FD */
82static char *mr_conf_filename = NULL;   /* Configuration filename */
83
84/*if output all error and warning messages*/
85static int mr_conf_flags = MRCONF_FLAG_VERBOSE;
86
87/*
88 * Format of the configuration file is as follows:
89 *
90 * attribute1 = int_value
91 * attribute2 = float_value
92 * attribute3 = string_value
93 */
94
95
96/*open and read file: each call must be coupled with mr_conf_close
97  function: return 0 if success*/
98int mr_conf_open(const char *filename) {
99    size_t length;              /*length of the buffer/file */
100    size_t res = 0;
101
102    /* check if mr_conf is already opened? */
103    if (mr_conf_check_int_flag(MRCONF_INTFLAG_OPEN)) {
104        mr_conf_error_msg(MRCONF_OPEN_OPENED, NULL);
105        return MRCONF_OPEN_OPENED;
106    }
107
108    length = mr_conf_filesize(filename);
109    CONF = fopen(filename, "r");
110
111    mr_asprintf(&mr_conf_filename,filename);
112
113    /*if file is empty or not exist => error */
114    if (length == 0) {
115        mr_conf_error_msg(MRCONF_BAD_FILE, filename);
116        return (MRCONF_BAD_FILE);
117    }
118
119    /*creating and reading buffer for file content */
120
121    /*allocate memory for the buffers */
122    buffer = (char *) mr_malloc(sizeof(char) * (length + 1));
123
124    /*set flag that module is in "open" state */
125    mr_conf_set_int_flag(MRCONF_INTFLAG_OPEN);
126
127    /*reading file in buffer (skip all 0 characters) */
128
129    res = fread(buffer, sizeof(char), length, CONF);
130    buffer[length] = (char) 0;  /*finalize the string */
131
132    if (ferror(CONF)) {
133        mr_conf_error_msg(MRCONF_READING_FAILED, "");
134        return (MRCONF_READING_FAILED);
135    }
136
137    /* finally we have to remove all comment lines */
138    mr_conf_remove_comments();
139
140    return MRCONF_NO_ERROR;
141}
142
143/*release all memory and prepare to the next possiable config file*/
144void mr_conf_close(void) {
145    /* if not opened => error */
146    if (!mr_conf_check_int_flag(MRCONF_INTFLAG_OPEN)) {
147        mr_conf_error_msg(MRCONF_CLOSE_BUT_NOT_OPEN, NULL);
148    }
149    mr_free(buffer);
150    fclose(CONF);
151
152    mr_free(mr_conf_filename);
153
154    /*set flag that module is in "close" state */
155    mr_conf_drop_int_flag(MRCONF_INTFLAG_OPEN);
156}
157
158/*read field value after string str in the current file*/
159static char *mr_conf_read(const char *field_name) {
160    char *p = NULL;             /*pointer to the field */
161
162    /* check if mr_conf is not yet opened? */
163    if (!mr_conf_check_int_flag(MRCONF_INTFLAG_OPEN)) {
164        mr_conf_error_msg(MRCONF_CALL_BUT_NOT_OPEN, NULL);
165        return NULL;
166    }
167
168    /*read the number */
169    p = strstr(buffer, field_name);
170    if (p == NULL) {
171        mr_conf_error_msg(MRCONF_FIELD_NOT_FOUND, field_name);
172        return NULL;
173    } else {
174        p += strlen(field_name);
175        while ((*p != '\n') && (*p != '\0') && (*p != '=')) {
176                p++;
177        }
178        if (*p != '=') {
179            mr_conf_error_msg(MRCONF_FIELD_NO_VALUE, field_name);
180            return NULL;
181        } else {
182            /* points after the = sign */
183            p++;
184        }
185    }
186    /* skip initial spaces and tabs after = */
187    while ((*p == ' ') || (*p == '\t')) {
188            p++;
189    }
190
191    return p;
192}
193
194/*read integer number after string str in the current file*/
195int mr_conf_iread(const char *field_name) {
196    char *p = NULL;             /*pointer to the field */
197    int ret = 0;                /*return value */
198
199    p = mr_conf_read(field_name);
200    if (p != NULL) {
201        ret = atoi(p);
202        }
203    return ret;
204}
205
206/*read float/double number after string str in the current file*/
207double mr_conf_fread(const char *field_name) {
208    char *p = NULL;             /*pointer to the field */
209    double ret = 0.0;               /*return value */
210
211    p = mr_conf_read(field_name);
212    if (p != NULL) {
213        ret = atof(p);
214    }
215    return ret;
216}
217
218
219/*
220*/
221char *mr_conf_sread(const char *field_name) {
222    char *p = NULL;             /*pointer to the field */
223    char *q = NULL;             /*pointer to the field */
224    char *r = NULL;             /*pointer to the field */
225    char *ret = NULL;           /*return value */
226    int size = 0;               /*size of returned string */
227    int i = 0;
228
229    ret = NULL;
230
231    p = mr_conf_read(field_name);
232    if (p == NULL) {
233        return(p);
234    }
235    mr_asprintf(&q, p);
236
237    if (*p != '"') {
238        mr_conf_error_msg(MRCONF_STRING_QUOTE, field_name);
239        return (NULL);
240    }
241    p++;
242
243    /* trunk at first \n */
244    r = index(q,'\n');
245    r--;
246    if (*r != '"') {
247        mr_conf_error_msg(MRCONF_STRING_QUOTE, field_name);
248        return (NULL);
249    }
250    r--;
251
252    size = r-q+1;
253    /*copy filtered data to the buffer */
254    ret = (char *) mr_malloc(sizeof(char) * (size));
255    while (i < size - 1) {
256        ret[i] = *p;
257        i++;
258        p++;
259    }
260
261    ret[i] = (char) 0;      /* and set its length */
262    mr_free(q);
263
264    return ret;
265}
266
267/*read boolean after string str in the current file*/
268bool mr_conf_bread(const char *field_name) {
269    char *p = NULL;             /*pointer to the field */
270    bool ret = FALSE;
271   
272    p = mr_conf_sread(field_name);
273    if (p != NULL) {
274        /* match if yes/true/1 */
275        if ((strncasecmp(p, "y" , (size_t)1) == 0) ||
276            (strncasecmp(p, "t" , (size_t)1) == 0) ||
277            (strncasecmp(p, "1" , (size_t)1) == 0)) {
278            ret = TRUE;
279        }
280    }
281    return ret;
282}
283
284/*removes all comments from the buffer*/
285static void mr_conf_remove_comments(void) {
286    char *tmp_buf;              /*temporary buffer without comments */
287    size_t length               /*initial length */ ;
288    size_t i;                   /*iterator */
289    size_t k;                   /*conditioned iterator for tmp_buffer */
290    bool found_quote = FALSE;
291    bool found_comment = FALSE;
292
293    length = strlen(buffer);
294
295    /*sizing the new chain */
296    k = 0;
297    i = 0;
298    while (i < length) {
299        /* Handle quotes to detect strings */
300        if ((buffer[i] == '"') && (! found_comment)) {
301            if (found_quote) {
302                found_quote = FALSE;
303            } else {
304                found_quote = TRUE;
305            }
306        }
307        /* Handle start of comment - only when not in a string */
308        if (buffer[i] == MRCONF_COMM_CHAR) {
309            if (found_quote) {
310                found_comment = FALSE;
311            } else {
312                found_comment = TRUE;
313            }
314        }
315        /* Comments end with EOL */
316        if (buffer[i] == '\n') {
317            found_comment = FALSE;
318        }
319        if (! found_comment) {
320            k++;
321            i++;
322        } else {
323            /* Skip comment */
324            while ((buffer[i] != '\n') && (buffer[i] != (char) 0)) {
325                i++;
326            }
327            if (buffer[i] == (char) 0) {
328                mr_conf_error_msg(MRCONF_STRING_ENDQUOTE, buffer);
329            }
330        }
331    }
332    /* k is new buffer length now */
333    tmp_buf = (char *) mr_malloc(sizeof(char) * (k + 1));
334
335    k = 0;
336    i = 0;
337    while (i < length) {
338        if (buffer[i] == '"') {
339            if (found_quote) {
340                found_quote = FALSE;
341            } else {
342                found_quote = TRUE;
343            }
344        }
345        if ((buffer[i] != MRCONF_COMM_CHAR) || (found_quote)) {
346            tmp_buf[k++] = buffer[i++];
347        } else {
348            /* Skip comment as it's not inside a string */
349            while ((buffer[i] != '\n') && (buffer[i] != (char) 0)) {
350                i++;
351            }
352        }
353    }
354    tmp_buf[k] = (char) 0;      /*and set its length */
355
356    mr_free(buffer);
357    /*copy filtered data to the buffer */
358    buffer = tmp_buf;
359}
360
361static int mr_conf_check_int_flag(const int flag) {
362    return (flag & internal_flags);
363}
364
365static void mr_conf_set_int_flag(const int flag) {
366    internal_flags = flag | internal_flags;
367}
368
369static void mr_conf_drop_int_flag(const int flag) {
370    internal_flags = (~flag) & internal_flags;
371}
372
373
374/*local function to define size of a file. Return 0 for mistake*/
375static size_t mr_conf_filesize(const char *name) {
376    FILE *F = fopen(name, "r"); /*file to open */
377    size_t length;              /*number to return */
378
379    if (F == NULL) {
380        return 0;
381    }
382
383    fseek(F, 0, SEEK_END);      /*set position to the end of file */
384    length = ftell(F);          /*define number of position=> this is its
385                                   length */
386    fclose(F);
387
388    return length;
389}
390
391/*output error message*/
392static void mr_conf_error_msg_int(int error_code, const char *add_line, int line, const char *file) {
393    if ((mr_conf_flags & MRCONF_FLAG_VERBOSE)) {    /*if verbose mode */
394        switch (error_code) {
395        case MRCONF_BAD_FILE:
396            mr_msg_int(1,line,file,"%s (%s) %s %s", MRCONF_STR_ERROR, mr_conf_filename, MRCONF_STR_BAD_FILE,
397                   add_line);
398            break;
399
400        case MRCONF_READING_FAILED:
401            mr_msg_int(1,line,file,"%s (%s) %s", MRCONF_STR_ERROR, mr_conf_filename, MRCONF_STR_READING_FAILED);
402            break;
403
404        case MRCONF_FIELD_NOT_FOUND:
405            mr_msg_int(1,line,file,"%s: %s (%s) %s \"%s\"", add_line, MRCONF_STR_ERROR, mr_conf_filename, MRCONF_STR_FIELD_NOT_FOUND, add_line);
406            mr_msg_int(1,line,file,"%s (%s) %s", MRCONF_STR_WARNING, mr_conf_filename, MRCONF_STR_SET_TO_ZERO);
407            break;
408
409        case MRCONF_FIELD_NO_VALUE:
410            mr_msg_int(1,line,file,"%s: %s (%s) %s \"%s\"", add_line, MRCONF_STR_ERROR, mr_conf_filename, MRCONF_STR_FIELD_NO_VALUE, add_line);
411            mr_msg_int(1,line,file,"%s (%s) %s", MRCONF_STR_WARNING, mr_conf_filename, MRCONF_STR_IGNORE);
412            break;
413
414        case MRCONF_CLOSE_BUT_NOT_OPEN:
415            mr_msg_int(1,line,file,"%s (%s) %s", MRCONF_STR_WARNING, mr_conf_filename, MRCONF_STR_CLOSE_BUT_NOT_OPEN);
416            break;
417
418        case MRCONF_CALL_BUT_NOT_OPEN:
419            mr_msg_int(1,line,file,"%s (%s) %s", MRCONF_STR_WARNING, mr_conf_filename, MRCONF_STR_CALL_BUT_NOT_OPEN);
420            break;
421
422        case MRCONF_OPEN_OPENED:
423            mr_msg_int(1,line,file,"%s (%s) %s", MRCONF_STR_ERROR, mr_conf_filename, MRCONF_STR_OPEN_OPENED);
424            break;
425
426        case MRCONF_STRING_QUOTE:
427            mr_msg_int(1,line,file,"%s: %s (%s) %s", add_line, MRCONF_STR_ERROR, mr_conf_filename, MRCONF_STR_STRING_QUOTE);
428            break;
429
430        case MRCONF_STRING_ENDQUOTE:
431            mr_msg_int(1,line,file,"%s: %s (%s) %s", add_line, MRCONF_STR_ERROR, mr_conf_filename, MRCONF_STR_STRING_ENDQUOTE);
432            break;
433
434        default:
435            mr_msg_int(1,line,file,"%s (%s) %s", MRCONF_STR_ERROR, mr_conf_filename, MRCONF_STR_DEFAULT_ERROR);
436            break;
437        }
438    }
439}
Note: See TracBrowser for help on using the repository browser.