From fc4413d0ee5ae3d8e9c6c8758dc9091ed5c9b10e Mon Sep 17 00:00:00 2001 From: Noel Power Date: Wed, 15 Jan 2014 20:55:17 +0000 Subject: [PATCH] modify iniparser to build unbounded keys & values from multi-line input Instead of a size limit of ASCIILINESZ for multi-line input now there is no limit, a multi-line can be *any* size (and hence the key and value now also have a dynamic size) * there is a limit still on input line size (since multi lines are built from multiple lines where each line part is limited to ASCIILINESZ) Note: there is no limit to the size of the multi-line itself (it's only limited by available memory) * all stack & static fixed string usage has been removed with the exception of parsing line (or a multi-line portion) input, fgets still reads into a fixed string buffer of size ASCIILINESZ. Signed-off-by: Noel Power --- src/dictionary.c | 2 +- src/dictionary.h | 12 ++ src/iniparser.c | 319 ++++++++++++++++++++++++++++++++++------------- 3 files changed, 245 insertions(+), 88 deletions(-) diff --git a/src/dictionary.c b/src/dictionary.c index 3ee8121..3372ea5 100644 --- a/src/dictionary.c +++ b/src/dictionary.c @@ -58,7 +58,7 @@ static void * mem_double(void * ptr, size_t size) for systems that do not have it. */ /*--------------------------------------------------------------------------*/ -static char * xstrdup(const char * s) +char * xstrdup(const char * s) { char * t ; size_t len ; diff --git a/src/dictionary.h b/src/dictionary.h index 87db733..965dfd6 100644 --- a/src/dictionary.h +++ b/src/dictionary.h @@ -166,6 +166,18 @@ void dictionary_unset(dictionary * d, const char * key); /*--------------------------------------------------------------------------*/ void dictionary_dump(dictionary * d, FILE * out); +/*-------------------------------------------------------------------------*/ +/** + @brief Duplicate a string + @param s String to duplicate + @return Pointer to a newly allocated string, to be freed with free() + + This is a replacement for strdup(). This implementation is provided + for systems that do not have it. + */ +/*--------------------------------------------------------------------------*/ +char * xstrdup(const char * s); + #ifdef __cplusplus } #endif diff --git a/src/iniparser.c b/src/iniparser.c index 18dbbbe..06f6433 100644 --- a/src/iniparser.c +++ b/src/iniparser.c @@ -33,62 +33,50 @@ typedef enum _line_status_ { /** @brief Convert a string to lowercase. @param s String to convert. - @return ptr to statically allocated string. - This function returns a pointer to a statically allocated string - containing a lowercased version of the input string. Do not free - or modify the returned string! Since the returned string is statically - allocated, it will be modified at each function call (not re-entrant). + This function modifies the string passed, the modified string + contains a lowercased version of the input string. */ /*--------------------------------------------------------------------------*/ -static char * strlwc(const char * s) + +static void strlwc(char * s) { - static char l[ASCIILINESZ+1]; int i ; - if (s==NULL) return NULL ; - memset(l, 0, ASCIILINESZ+1); + if (s==NULL) return; i=0 ; - while (s[i] && i l) { + while (last > s) { if (!isspace((int)*(last-1))) break ; last -- ; } *last = (char)0; - return (char*)l ; + + memmove(dest,s,last - s + 1); } /*-------------------------------------------------------------------------*/ @@ -244,26 +232,28 @@ void iniparser_dump_ini(dictionary * d, FILE * f) void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f) { int j ; - char keym[ASCIILINESZ+1]; - int seclen ; + char *keym; + int secsize ; if (d==NULL || f==NULL) return ; if (! iniparser_find_entry(d, s)) return ; - seclen = (int)strlen(s); fprintf(f, "\n[%s]\n", s); - sprintf(keym, "%s:", s); + secsize = (int)strlen(s) + 2; + keym = malloc(secsize); + snprintf(keym, secsize, "%s:", s); for (j=0 ; jsize ; j++) { if (d->key[j]==NULL) continue ; - if (!strncmp(d->key[j], keym, seclen+1)) { + if (!strncmp(d->key[j], keym, secsize-1)) { fprintf(f, "%-30s = %s\n", - d->key[j]+seclen+1, + d->key[j]+secsize-1, d->val[j] ? d->val[j] : ""); } } fprintf(f, "\n"); + free(keym); return ; } @@ -277,8 +267,8 @@ void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f) /*--------------------------------------------------------------------------*/ int iniparser_getsecnkeys(dictionary * d, char * s) { - int seclen, nkeys ; - char keym[ASCIILINESZ+1]; + int secsize, nkeys ; + char *keym; int j ; nkeys = 0; @@ -286,16 +276,17 @@ int iniparser_getsecnkeys(dictionary * d, char * s) if (d==NULL) return nkeys; if (! iniparser_find_entry(d, s)) return nkeys; - seclen = (int)strlen(s); - sprintf(keym, "%s:", s); + secsize = (int)strlen(s)+2; + keym = malloc(secsize); + snprintf(keym, secsize, "%s:", s); for (j=0 ; jsize ; j++) { if (d->key[j]==NULL) continue ; - if (!strncmp(d->key[j], keym, seclen+1)) + if (!strncmp(d->key[j], keym, secsize-1)) nkeys++; } - + free(keym); return nkeys; } @@ -320,8 +311,8 @@ char ** iniparser_getseckeys(dictionary * d, char * s) char **keys; int i, j ; - char keym[ASCIILINESZ+1]; - int seclen, nkeys ; + char *keym; + int secsize, nkeys ; keys = NULL; @@ -332,15 +323,16 @@ char ** iniparser_getseckeys(dictionary * d, char * s) keys = (char**) malloc(nkeys*sizeof(char*)); - seclen = (int)strlen(s); - sprintf(keym, "%s:", s); + secsize = (int)strlen(s) + 2; + keym = malloc(secsize); + snprintf(keym, secsize, "%s:", s); i = 0; for (j=0 ; jsize ; j++) { if (d->key[j]==NULL) continue ; - if (!strncmp(d->key[j], keym, seclen+1)) { + if (!strncmp(d->key[j], keym, secsize-1)) { keys[i] = d->key[j]; i++; } @@ -373,8 +365,10 @@ char * iniparser_getstring(dictionary * d, const char * key, char * def) if (d==NULL || key==NULL) return def ; - lc_key = strlwc(key); + lc_key = xstrdup(key); + strlwc(lc_key); sval = dictionary_get(d, lc_key, def); + free(lc_key); return sval ; } @@ -524,7 +518,12 @@ int iniparser_find_entry( /*--------------------------------------------------------------------------*/ int iniparser_set(dictionary * ini, const char * entry, const char * val) { - return dictionary_set(ini, strlwc(entry), val) ; + int result = 0; + char *lc_entry = xstrdup(entry); + strlwc(lc_entry); + result = dictionary_set(ini, lc_entry, val) ; + free(lc_entry); + return result; } /*-------------------------------------------------------------------------*/ @@ -539,7 +538,10 @@ int iniparser_set(dictionary * ini, const char * entry, const char * val) /*--------------------------------------------------------------------------*/ 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); } /*-------------------------------------------------------------------------*/ @@ -553,18 +555,48 @@ void iniparser_unset(dictionary * ini, const char * entry) */ /*--------------------------------------------------------------------------*/ static line_status iniparser_line( + int line_size, const char * input_line, - char * section, - char * key, - char * value) + char ** section_out, + char ** key_out, + char ** value_out) { line_status sta ; - char line[ASCIILINESZ+1]; - int len ; + 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, strstrip(input_line)); + 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 */ @@ -574,17 +606,20 @@ static line_status iniparser_line( sta = LINE_COMMENT ; } else if (line[0]=='[' && line[len-1]==']') { /* Section name */ - sscanf(line, "[%[^]]", section); - strcpy(section, strstrip(section)); - strcpy(section, strlwc(section)); + sscanf(line, "[%[^]]", key); + strstrip(key); + strlwc(key); sta = LINE_SECTION ; - } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2 + *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) { + || sscanf (line, "%[^=] = %[^;#]", key, value) == 2)) { /* Usual key=value, with or without comments */ - strcpy(key, strstrip(key)); - strcpy(key, strlwc(key)); - strcpy(value, strstrip(value)); + strstrip(key); + strlwc(key); + strstrip(value); /* * sscanf cannot handle '' or "" as empty values * this is done here @@ -592,23 +627,46 @@ static line_status iniparser_line( if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) { value[0]=0 ; } + *key_out = key; + *value_out = value; + key = NULL; + value = NULL; sta = LINE_VALUE ; - } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2 - || sscanf(line, "%[^=] %[=]", key, value) == 2) { + } else if (equals && (sscanf(line, "%[^=] = %[;#]", key, value)==2 + || sscanf(line, "%[^=] %[=]", key, value) == 2)) { /* * Special cases: * key= * key=; * key=# */ - strcpy(key, strstrip(key)); - strcpy(key, strlwc(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 ; } @@ -631,15 +689,17 @@ dictionary * iniparser_load(const char * ininame) FILE * in ; char line [ASCIILINESZ+1] ; - char section [ASCIILINESZ+1] ; - char key [ASCIILINESZ+1] ; - char tmp [(ASCIILINESZ * 2) + 1] ; - char val [ASCIILINESZ+1] ; + char *section = xstrdup(""); + char *current_section = NULL; + char *key = NULL; + char *val = NULL; + char* full_line = NULL; + char* prev_line = NULL; - int last=0 ; int len ; int lineno=0 ; int errs=0; + int seckey_size=0; dictionary * dict ; @@ -655,12 +715,22 @@ dictionary * iniparser_load(const char * ininame) } memset(line, 0, ASCIILINESZ); - memset(section, 0, ASCIILINESZ); - memset(key, 0, ASCIILINESZ); - memset(val, 0, ASCIILINESZ); - last=0 ; - while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) { + 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++ ; len = (int)strlen(line)-1; if (len==0) @@ -671,9 +741,8 @@ dictionary * iniparser_load(const char * ininame) "iniparser: input line too long in %s (%d)\n", ininame, lineno); - dictionary_del(dict); - fclose(in); - return NULL ; + errs++; + goto out; } /* Get rid of \n and spaces at end of line */ while ((len>=0) && @@ -681,33 +750,93 @@ dictionary * iniparser_load(const char * ininame) line[len]=0 ; len-- ; } + /* Detect multi-line */ if (line[len]=='\\') { + multi_line = 1; + } + if (multi_line) { /* Multi-line value */ - last=len ; + /* 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 ; - } else { - last=0 ; } - switch (iniparser_line(line, section, key, val)) { + + switch (iniparser_line(total_size, full_line, ¤t_section, &key, &val)) { case LINE_EMPTY: case LINE_COMMENT: break ; case LINE_SECTION: - errs = dictionary_set(dict, section, NULL); + if (section) { + free(section); + section=NULL; + } + errs = dictionary_set(dict, current_section, NULL); + section = current_section; break ; case LINE_VALUE: - snprintf(tmp, sizeof(tmp), "%s:%s", section, key); - errs = dictionary_set(dict, tmp, val) ; + { + 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; + } + 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", line); + fprintf(stderr, "-> %s\n", full_line); errs++ ; break; @@ -715,16 +844,32 @@ dictionary * iniparser_load(const char * ininame) break ; } memset(line, 0, ASCIILINESZ); - last=0; + if (full_line) { + free(full_line); + full_line = NULL; + } if (errs<0) { fprintf(stderr, "iniparser: memory allocation failure\n"); break ; } } +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 ; } -- GitLab