diff options
Diffstat (limited to 'src/iniparser.c')
-rw-r--r-- | src/iniparser.c | 625 |
1 files changed, 491 insertions, 134 deletions
diff --git a/src/iniparser.c b/src/iniparser.c index 0934087..9f3e973 100644 --- a/src/iniparser.c +++ b/src/iniparser.c @@ -3,54 +3,81 @@ /** @file iniparser.c @author N. Devillard - @date Mar 2000 - @version $Revision: 2.17 $ @brief Parser for ini files. */ /*--------------------------------------------------------------------------*/ - -/* - $Id: iniparser.c,v 2.17 2007-05-27 13:03:43 ndevilla Exp $ - $Author: ndevilla $ - $Date: 2007-05-27 13:03:43 $ - $Revision: 2.17 $ -*/ - -/*--------------------------------------------------------------------------- - Includes - ---------------------------------------------------------------------------*/ - +/*---------------------------- Includes ------------------------------------*/ +#include <ctype.h> #include "iniparser.h" -#include "strlib.h" -#define ASCIILINESZ 1024 +/*---------------------------- Defines -------------------------------------*/ +#define ASCIILINESZ (1024) #define INI_INVALID_KEY ((char*)-1) /*--------------------------------------------------------------------------- Private to this module ---------------------------------------------------------------------------*/ +/** + * This enum stores the status for each parsed line (internal use only). + */ +typedef enum _line_status_ { + LINE_UNPROCESSED, + LINE_ERROR, + LINE_EMPTY, + LINE_COMMENT, + LINE_SECTION, + LINE_VALUE +} line_status ; + +/*-------------------------------------------------------------------------*/ +/** + @brief Convert a string to lowercase. + @param s String to convert. -/* Private: add an entry to the dictionary */ -static void iniparser_add_entry( - dictionary * d, - char * sec, - char * key, - char * val) + This function modifies the string passed, the modified string + contains a lowercased version of the input string. + */ +/*--------------------------------------------------------------------------*/ + +static void strlwc(char * s) { - char longkey[2*ASCIILINESZ+1]; + int i ; - /* Make a key as section:keyword */ - if (key!=NULL) { - sprintf(longkey, "%s:%s", sec, key); - } else { - strcpy(longkey, sec); + if (s==NULL) return; + i=0 ; + while (s[i]) { + s[i] = (char)tolower((int)s[i]); + i++ ; } - - /* Add (key,val) to dictionary */ - dictionary_set(d, longkey, val); - return ; } +/*-------------------------------------------------------------------------*/ +/** + @brief Remove blanks at the beginning and the end of a string. + @param s String to parse. + + This function modifies the input string and returns a modified string + which is identical to the input string, except that all blank + characters at the end and the beg. of the string have been removed. + */ +/*--------------------------------------------------------------------------*/ +void strstrip(char * s) +{ + char *last = s + strlen(s); + char *dest = s; + + if (s==NULL) return ; + + while (isspace((int)*s) && *s) s++; + while (last > s) { + if (!isspace((int)*(last-1))) + break ; + last -- ; + } + *last = (char)0; + + memmove(dest,s,last - s + 1); +} /*-------------------------------------------------------------------------*/ /** @@ -70,7 +97,6 @@ static void iniparser_add_entry( This function returns -1 in case of error. */ /*--------------------------------------------------------------------------*/ - int iniparser_getnsec(dictionary * d) { int i ; @@ -88,7 +114,6 @@ int iniparser_getnsec(dictionary * d) return nsec ; } - /*-------------------------------------------------------------------------*/ /** @brief Get name for section n in a dictionary. @@ -103,7 +128,6 @@ int iniparser_getnsec(dictionary * d) This function returns NULL in case of error. */ /*--------------------------------------------------------------------------*/ - char * iniparser_getsecname(dictionary * d, int n) { int i ; @@ -126,7 +150,6 @@ char * iniparser_getsecname(dictionary * d, int n) return d->key[i] ; } - /*-------------------------------------------------------------------------*/ /** @brief Dump a dictionary to an opened file pointer. @@ -168,14 +191,11 @@ void iniparser_dump(dictionary * d, FILE * f) It is Ok to specify @c stderr or @c stdout as output files. */ /*--------------------------------------------------------------------------*/ - void iniparser_dump_ini(dictionary * d, FILE * f) { - int i, j ; - char keym[ASCIILINESZ+1]; + int i ; int nsec ; char * secname ; - int seclen ; if (d==NULL || f==NULL) return ; @@ -191,50 +211,136 @@ void iniparser_dump_ini(dictionary * d, FILE * f) } for (i=0 ; i<nsec ; i++) { secname = iniparser_getsecname(d, i) ; - seclen = (int)strlen(secname); - fprintf(f, "\n[%s]\n", secname); - sprintf(keym, "%s:", secname); - for (j=0 ; j<d->size ; j++) { - if (d->key[j]==NULL) - continue ; - if (!strncmp(d->key[j], keym, seclen+1)) { - fprintf(f, - "%-30s = %s\n", - d->key[j]+seclen+1, - d->val[j] ? d->val[j] : ""); - } + iniparser_dumpsection_ini(d, secname, f) ; + } + fprintf(f, "\n"); + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Save a dictionary section to a loadable ini file + @param d Dictionary to dump + @param s Section name of dictionary to dump + @param f Opened file pointer to dump to + @return void + + This function dumps a given section of a given dictionary into a loadable ini + file. It is Ok to specify @c stderr or @c stdout as output files. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f) +{ + int j ; + char *keym; + int secsize ; + + if (d==NULL || f==NULL) return ; + if (! iniparser_find_entry(d, s)) return ; + + fprintf(f, "\n[%s]\n", s); + secsize = (int)strlen(s) + 2; + keym = malloc(secsize); + snprintf(keym, secsize, "%s:", s); + for (j=0 ; j<d->size ; j++) { + if (d->key[j]==NULL) + continue ; + if (!strncmp(d->key[j], keym, secsize-1)) { + fprintf(f, + "%-30s = %s\n", + d->key[j]+secsize-1, + d->val[j] ? d->val[j] : ""); } } fprintf(f, "\n"); + free(keym); return ; } +/*-------------------------------------------------------------------------*/ +/** + @brief Get the number of keys in a section of a dictionary. + @param d Dictionary to examine + @param s Section name of dictionary to examine + @return Number of keys in section + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getsecnkeys(dictionary * d, char * s) +{ + int secsize, nkeys ; + char *keym; + int j ; + + nkeys = 0; + if (d==NULL) return nkeys; + if (! iniparser_find_entry(d, s)) return nkeys; + secsize = (int)strlen(s)+2; + keym = malloc(secsize); + snprintf(keym, secsize, "%s:", s); + + for (j=0 ; j<d->size ; j++) { + if (d->key[j]==NULL) + continue ; + if (!strncmp(d->key[j], keym, secsize-1)) + nkeys++; + } + free(keym); + return nkeys; + +} /*-------------------------------------------------------------------------*/ /** - @brief Get the string associated to a key, return NULL if not found - @param d Dictionary to search - @param key Key string to look for - @return pointer to statically allocated character string, or NULL. + @brief Get the number of keys in a section of a dictionary. + @param d Dictionary to examine + @param s Section name of dictionary to examine + @return pointer to statically allocated character strings - This function queries a dictionary for a key. A key as read from an - ini file is given as "section:key". If the key cannot be found, - NULL is returned. - The returned char pointer is pointing to a string allocated in - the dictionary, do not free or modify it. + This function queries a dictionary and finds all keys in a given section. + Each pointer in the returned char pointer-to-pointer is pointing to + a string allocated in the dictionary; do not free or modify them. - This function is only provided for backwards compatibility with - previous versions of iniparser. It is recommended to use - iniparser_getstring() instead. + This function returns NULL in case of error. */ /*--------------------------------------------------------------------------*/ -char * iniparser_getstr(dictionary * d, const char * key) +char ** iniparser_getseckeys(dictionary * d, char * s) { - return iniparser_getstring(d, key, NULL); -} + char **keys; + + int i, j ; + char *keym; + int secsize, nkeys ; + + keys = NULL; + + if (d==NULL) return keys; + if (! iniparser_find_entry(d, s)) return keys; + + nkeys = iniparser_getsecnkeys(d, s); + + keys = (char**) malloc(nkeys*sizeof(char*)); + + secsize = (int)strlen(s) + 2; + keym = malloc(secsize); + snprintf(keym, secsize, "%s:", s); + + i = 0; + + for (j=0 ; j<d->size ; j++) { + if (d->key[j]==NULL) + continue ; + if (!strncmp(d->key[j], keym, secsize-1)) { + keys[i] = d->key[j]; + i++; + } + } + + return keys; + +} /*-------------------------------------------------------------------------*/ /** @@ -259,16 +365,13 @@ char * iniparser_getstring(dictionary * d, const char * key, char * def) if (d==NULL || key==NULL) return def ; - if (!(lc_key = strdup(strlwc(key)))) { - return NULL; - } + lc_key = xstrdup(key); + strlwc(lc_key); sval = dictionary_get(d, lc_key, def); free(lc_key); return sval ; } - - /*-------------------------------------------------------------------------*/ /** @brief Get the string associated to a key, convert to an int @@ -305,7 +408,6 @@ int iniparser_getint(dictionary * d, const char * key, int notfound) return (int)strtol(str, NULL, 0); } - /*-------------------------------------------------------------------------*/ /** @brief Get the string associated to a key, convert to a double @@ -319,7 +421,7 @@ int iniparser_getint(dictionary * d, const char * key, int notfound) the notfound value is returned. */ /*--------------------------------------------------------------------------*/ -double iniparser_getdouble(dictionary * d, char * key, double notfound) +double iniparser_getdouble(dictionary * d, const char * key, double notfound) { char * str ; @@ -328,8 +430,6 @@ double iniparser_getdouble(dictionary * d, char * key, double notfound) return atof(str); } - - /*-------------------------------------------------------------------------*/ /** @brief Get the string associated to a key, convert to a boolean @@ -379,7 +479,6 @@ int iniparser_getboolean(dictionary * d, const char * key, int notfound) return ret; } - /*-------------------------------------------------------------------------*/ /** @brief Finds out if a given entry exists in a dictionary @@ -392,10 +491,9 @@ int iniparser_getboolean(dictionary * d, const char * key, int notfound) of querying for the presence of sections in a dictionary. */ /*--------------------------------------------------------------------------*/ - int iniparser_find_entry( dictionary * ini, - char * entry + const char * entry ) { int found=0 ; @@ -405,8 +503,6 @@ int iniparser_find_entry( return found ; } - - /*-------------------------------------------------------------------------*/ /** @brief Set an entry in a dictionary. @@ -420,11 +516,14 @@ int iniparser_find_entry( It is Ok to set val to NULL. */ /*--------------------------------------------------------------------------*/ - -int iniparser_setstr(dictionary * ini, char * entry, char * val) +int iniparser_set(dictionary * ini, const char * entry, const char * val) { - dictionary_set(ini, strlwc(entry), val); - return 0 ; + int result = 0; + char *lc_entry = xstrdup(entry); + strlwc(lc_entry); + result = dictionary_set(ini, lc_entry, val) ; + free(lc_entry); + return result; } /*-------------------------------------------------------------------------*/ @@ -437,11 +536,139 @@ int iniparser_setstr(dictionary * ini, char * entry, char * val) If the given entry can be found, it is deleted from the dictionary. */ /*--------------------------------------------------------------------------*/ -void iniparser_unset(dictionary * ini, char * entry) +void iniparser_unset(dictionary * ini, const char * entry) { - dictionary_unset(ini, strlwc(entry)); + char* lc_entry = xstrdup(entry); + strlwc(lc_entry); + dictionary_unset(ini, lc_entry); + free(lc_entry); } +/*-------------------------------------------------------------------------*/ +/** + @brief Load a single line from an INI file + @param input_line Input line, may be concatenated multi-line input + @param section Output space to store section + @param key Output space to store key + @param value Output space to store value + @return line_status value + */ +/*--------------------------------------------------------------------------*/ +static line_status iniparser_line( + int line_size, + const char * input_line, + char ** section_out, + char ** key_out, + char ** value_out) +{ + line_status sta ; + int len = line_size-1; + char * line = malloc(line_size); + char * key = NULL; + char * value = NULL; + char * equals = NULL; + + if (!line) { + fprintf(stderr, "iniparser: memory alloc error\n"); + return LINE_ERROR; + } + + *line = 0; + + + strcpy(line, input_line); + strstrip(line); + len = (int)strlen(line); + + /* only allocate necessary space for key & val */ + equals = strchr(line,'='); + if (equals) { + value = malloc((len + line) - equals + 1); + key = malloc(equals - line + 1); + *value = 0; + } else { + key = malloc(line_size + 1); + } + + if (!key || (equals && !value)) { + fprintf(stderr, "iniparser: memory alloc error\n"); + return LINE_ERROR; + } + + *key = 0; + + sta = LINE_UNPROCESSED ; + if (len<1) { + /* Empty line */ + sta = LINE_EMPTY ; + } else if (line[0]=='#' || line[0]==';') { + /* Comment line */ + sta = LINE_COMMENT ; + } else if (line[0]=='[' && line[len-1]==']') { + /* Section name */ + sscanf(line, "[%[^]]", key); + strstrip(key); + strlwc(key); + sta = LINE_SECTION ; + *section_out=key; + /* don't free key's memory */ + key = NULL; + } else if (equals && (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2 + || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2 + || sscanf (line, "%[^=] = %[^;#]", key, value) == 2)) { + /* Usual key=value, with or without comments */ + strstrip(key); + strlwc(key); + strstrip(value); + /* + * sscanf cannot handle '' or "" as empty values + * this is done here + */ + if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) { + value[0]=0 ; + } + *key_out = key; + *value_out = value; + key = NULL; + value = NULL; + sta = LINE_VALUE ; + } else if (equals && (sscanf(line, "%[^=] = %[;#]", key, value)==2 + || sscanf(line, "%[^=] %[=]", key, value) == 2)) { + /* + * Special cases: + * key= + * key=; + * key=# + */ + strstrip(key); + strlwc(key); + value[0]=0 ; + *key_out = key; + *value_out = value; + + /* don't free out params key or val's memory */ + key = NULL; + value = NULL; + sta = LINE_VALUE ; + } else { + /* Generate syntax error */ + sta = LINE_ERROR ; + } + + if (line) { + free(line); + line = NULL; + } + if (key) { + free(key); + key = NULL; + } + if (value) { + free(value); + value= NULL; + } + return sta ; +} /*-------------------------------------------------------------------------*/ /** @@ -457,65 +684,196 @@ void iniparser_unset(dictionary * ini, char * entry) The returned dictionary must be freed using iniparser_freedict(). */ /*--------------------------------------------------------------------------*/ - dictionary * iniparser_load(const char * ininame) { - dictionary * d ; - char lin[ASCIILINESZ+1]; - char sec[ASCIILINESZ+1]; - char key[ASCIILINESZ+1]; - char val[ASCIILINESZ+1]; - char * where ; - FILE * ini ; - int lineno ; - - if ((ini=fopen(ininame, "r"))==NULL) { + FILE * in ; + + char line [ASCIILINESZ+1] ; + char *section = xstrdup(""); + char *current_section = NULL; + char *key = NULL; + char *val = NULL; + char* full_line = NULL; + char* prev_line = NULL; + + int len ; + int lineno=0 ; + int errs=0; + int seckey_size=0; + + dictionary * dict ; + + if ((in=fopen(ininame, "r"))==NULL) { + fprintf(stderr, "iniparser: cannot open %s\n", ininame); return NULL ; } - sec[0]=0; - - /* - * Initialize a new dictionary entry - */ - if (!(d = dictionary_new(0))) { - fclose(ini); - return NULL; + dict = dictionary_new(0) ; + if (!dict) { + fclose(in); + return NULL ; } - lineno = 0 ; - while (fgets(lin, ASCIILINESZ, ini)!=NULL) { + + memset(line, 0, ASCIILINESZ); + + while (fgets(line, ASCIILINESZ, in)!=NULL) { + int prev_line_len = 0; + int multi_line = 0; + int total_size = 0; + + if (key) { + free(key); + key = NULL; + } + if (val) { + free(val); + val = NULL; + } + + lineno++ ; - where = strskp(lin); /* Skip leading spaces */ - if (*where==';' || *where=='#' || *where==0) - continue ; /* Comment lines */ - else { - if (sscanf(where, "[%[^]]", sec)==1) { - /* Valid section name */ - strcpy(sec, strlwc(sec)); - iniparser_add_entry(d, sec, NULL, NULL); - } else if (sscanf (where, "%[^=] = \"%[^\"]\"", key, val) == 2 - || sscanf (where, "%[^=] = '%[^\']'", key, val) == 2 - || sscanf (where, "%[^=] = %[^;#]", key, val) == 2) { - strcpy(key, strlwc(strcrop(key))); - /* - * sscanf cannot handle "" or '' as empty value, - * this is done here - */ - if (!strcmp(val, "\"\"") || !strcmp(val, "''")) { - val[0] = (char)0; - } else { - strcpy(val, strcrop(val)); + len = (int)strlen(line)-1; + if (len==0) + continue; + /* Safety check against buffer overflows */ + if (line[len]!='\n' && !feof(in)) { + fprintf(stderr, + "iniparser: input line too long in %s (%d)\n", + ininame, + lineno); + errs++; + goto out; + } + /* Get rid of \n and spaces at end of line */ + while ((len>=0) && + ((line[len]=='\n') || (isspace(line[len])))) { + line[len]=0 ; + len-- ; + } + + /* Detect multi-line */ + if (line[len]=='\\') { + multi_line = 1; + } + if (multi_line) { + /* Multi-line value */ + /* length without trailing '\' */ + /* remove multi-line indicator before appending*/ + line[len] = 0; + len--; + } + + /* + * If processing a multi-line then append it the previous portion, + * at this point 'full_line' has the previously read portion of a + * multi-line line (or NULL) + */ + prev_line = full_line; + prev_line_len=0; + if (prev_line) { + prev_line_len = strlen(prev_line); + } + + /* len is not strlen(line) but strlen(line) -1 */ + total_size = (len+1) + prev_line_len + 1; + + full_line = malloc(total_size); + if (!full_line) { + fprintf(stderr, + "iniparser: out of mem\n"); + errs++; + goto out; + } + + memset(full_line,0,total_size); + + if (prev_line) { + strcpy(full_line,prev_line); + } + + strcpy(full_line+prev_line_len,line); + free(prev_line); + prev_line = NULL; + + if (multi_line) { + continue ; + } + + switch (iniparser_line(total_size, full_line, ¤t_section, &key, &val)) { + case LINE_EMPTY: + case LINE_COMMENT: + break ; + + case LINE_SECTION: + if (section) { + free(section); + section=NULL; + } + errs = dictionary_set(dict, current_section, NULL); + section = current_section; + break ; + + case LINE_VALUE: + { + char *seckey; + /* section + ':' + key + eos */ + seckey_size = strlen(section) + strlen(key) +2; + seckey = malloc(seckey_size); + if (!seckey) { + errs++; + fprintf(stderr, + "iniparser: out of mem\n"); + goto out; } - iniparser_add_entry(d, sec, key, val); + snprintf(seckey, seckey_size, "%s:%s", section, key); + errs = dictionary_set(dict, seckey, val) ; + free(seckey); + seckey = NULL; } + break ; + + case LINE_ERROR: + fprintf(stderr, "iniparser: syntax error in %s (%d):\n", + ininame, + lineno); + fprintf(stderr, "-> %s\n", full_line); + errs++ ; + break; + + default: + break ; + } + memset(line, 0, ASCIILINESZ); + if (full_line) { + free(full_line); + full_line = NULL; + } + if (errs<0) { + fprintf(stderr, "iniparser: memory allocation failure\n"); + break ; } } - fclose(ini); - return d ; +out: + if (errs) { + dictionary_del(dict); + dict = NULL ; + } + if (val) { + free(val); + val = NULL; + } + if (key) { + free(key); + key = NULL; + } + if (section) { + free(section); + section = NULL; + } + fclose(in); + return dict ; } - - /*-------------------------------------------------------------------------*/ /** @brief Free all memory associated to an ini dictionary @@ -527,7 +885,6 @@ dictionary * iniparser_load(const char * ininame) gets out of the current context. */ /*--------------------------------------------------------------------------*/ - void iniparser_freedict(dictionary * d) { dictionary_del(d); |