ini_reader.c
Go to the documentation of this file.
00001
00038 #include "ini_reader.h"
00039 #include "cfg/cfg_ini_reader.h"
00040 #include <string.h>
00041 #include <stdio.h>
00042 #include <stdlib.h> //strtol
00043 #include <ctype.h>
00044
00045 static bool lineEmpty(const char *line)
00046 {
00047     while (*line)
00048     {
00049         if (!isspace((unsigned char)*line++))
00050             return false;
00051     }
00052     return true;
00053 }
00054
00055 /*
00056  * Returns when the line containing the section is found.
00057  * The file pointer is positioned at the start of the next line or
00058  * after the last non-empty line if the section is not found.
00059  * Returns EOF if no section was found, 0 otherwise.
00060  */
00061 static int findSection(KFile *fd, const char *section, size_t section_len, char *line, size_t size)
00062 {
00063     kfile_off_t last_full = fd->seek_pos;
00064
00065     int err;
00066     do
00067     {
00068         char *ptr = line;
00069         unsigned i;
00070         err = kfile_gets(fd, line, size);
00071
00072         /* Remember the last filled line in file */
00073         if (!lineEmpty(line))
00074             last_full = fd->seek_pos;
00075
00076         /* accept only sections that begin at first char */
00077         if (*ptr++ != '[')
00078             continue;
00079
00080         /* find the end-of-section character */
00081         for (i = 0; i < size && *ptr != ']'; ++i, ++ptr)
00082             ;
00083
00084         /* The found section could be long that our section key */
00085         if (section_len != i)
00086             continue;
00087
00088         /* did we find the correct section? */
00089         if(strncmp(&line[1], section, section_len))
00090             continue;
00091         else
00092             return 0;
00093     }
00094     while (err != EOF);
00095
00096     kfile_seek(fd, last_full, KSM_SEEK_SET);
00097     return EOF;
00098 }
00099
00100 /*
00101  * Fills the argument with the key found in line
00102  */
00103 static char *getKey(const char *line, char *key, size_t size)
00104 {
00105     /* null-terminated string */
00106     while (isspace((unsigned char)*line))
00107         ++line;
00108     int i = 0;
00109     while (*line != '=' && !isspace((unsigned char)*line) && size)
00110     {
00111         key[i++] = *line;
00112         ++line;
00113         --size;
00114     }
00115     size ? (key[i] = '\0') : (key[i-1] = '\0');
00116     return key;
00117 }
00118
00119 /*
00120  * Fills the argument with the value found in line.
00121  */
00122 static char *getValue(const char *line, char *value, size_t size)
00123 {
00124     while (*line++ != '=')
00125         ;
00126     while (isspace((unsigned char)*line))
00127         ++line;
00128     int i = 0;
00129     while (*line && size)
00130     {
00131         value[i++] = *line++;
00132         --size;
00133     }
00134     size ? (value[i] = '\0') : (value[i-1] = '\0');
00135     return value;
00136 }
00137
00145 static int findKey(KFile *fd, const char *key, char *line, size_t size)
00146 {
00147     int err;
00148     char curr_key[30];
00149     kfile_off_t last_full = fd->seek_pos;
00150     kfile_off_t key_pos = fd->seek_pos;
00151
00152     do
00153     {
00154         err = kfile_gets(fd, line, size);
00155
00156         getKey(line, curr_key, 30);
00157         /* check key */
00158         if (!strcmp(curr_key, key))
00159         {
00160             kfile_seek(fd, key_pos, KSM_SEEK_SET);
00161             return 0;
00162         }
00163
00164         /* Remember the last filled line in the section */
00165         if (!lineEmpty(line) && *line != '[')
00166             last_full = fd->seek_pos;
00167         key_pos = fd->seek_pos;
00168     }
00169     while (err != EOF && *line != '[');
00170     kfile_seek(fd, last_full, KSM_SEEK_SET);
00171     return EOF;
00172 }
00173
00174 /*
00175  * On errors, the function returns EOF and fills the buffer with the default value.
00176  */
00177 int ini_getString(KFile *fd, const char *section, const char *key, const char *default_value, char *buf, size_t size)
00178 {
00179     char line[CONFIG_INI_MAX_LINE_LEN];
00180
00181     if (kfile_seek(fd, 0, KSM_SEEK_SET) == EOF)
00182         goto error;
00183
00184     if (findSection(fd, section, strlen(section), line, CONFIG_INI_MAX_LINE_LEN) == EOF)
00185         goto error;
00186
00187     if (findKey(fd, key, line, CONFIG_INI_MAX_LINE_LEN) == EOF)
00188         goto error;
00189     else
00190         getValue(line, buf, size);
00191     return 0;
00192
00193 error:
00194     strncpy(buf, default_value, size);
00195     if (size > 0)
00196         buf[size - 1] = '\0';
00197     return EOF;
00198 }
00199
00200 int ini_getInteger(KFile *fd, const char *section, const char *key, long default_value, long *val, int base)
00201 {
00202     char buf[CONFIG_INI_MAX_LINE_LEN];
00203
00204     if (ini_getString(fd, section, key, "", buf, sizeof(buf)) == EOF)
00205         goto error;
00206
00207     char **endptr = NULL;
00208     *val = strtol(buf, endptr, base);
00209
00210     if (buf[0] == 0 || **endptr != 0)
00211         goto error;
00212
00213     return 0;
00214
00215 error:
00216     *val = default_value;
00217     return EOF;
00218 }
00219
00220 /*
00221  * Return the position immediatly following the last non-empty line in the file,
00222  * starting from current position.
00223  * The file seek position is unchanged at function exit.
00224  */
00225 static kfile_off_t findLastLine(KFile *fd, char *line, size_t size)
00226 {
00227     kfile_off_t start = fd->seek_pos;
00228     kfile_off_t last_full = start;
00229
00230     int err;
00231     do
00232     {
00233         err = kfile_gets(fd, line, size);
00234         if (!lineEmpty(line))
00235             last_full = fd->seek_pos;
00236     }
00237     while (err != EOF);
00238     kfile_seek(fd, start, KSM_SEEK_SET);
00239     return last_full;
00240 }
00241
00242 int ini_setString(KFile *in, KFile *out, const char *section, const char *key, const char *value)
00243 {
00244     char line[CONFIG_INI_MAX_LINE_LEN];
00245
00246     if (kfile_seek(in, 0, KSM_SEEK_SET) == EOF)
00247         return EOF;
00248
00249     if (kfile_seek(out, 0, KSM_SEEK_SET) == EOF)
00250         return EOF;
00251
00252     bool section_found = false;
00253     bool key_found = false;
00254     if (findSection(in, section, strlen(section), line, CONFIG_INI_MAX_LINE_LEN) != EOF)
00255     {
00256         section_found = true;
00257         key_found = (findKey(in, key, line, CONFIG_INI_MAX_LINE_LEN) != EOF);
00258     }
00259
00260     kfile_off_t len = in->seek_pos;
00261
00262     /* Copy until key */
00263     if (kfile_seek(in, 0, KSM_SEEK_SET) == EOF)
00264             return EOF;
00265     if (kfile_copy(in, out, len) != len)
00266             return EOF;
00267
00268     if (!section_found && value)
00269     {
00270         if ((size_t)kfile_printf(out, "\n[%s]\n", section) != (strlen(section) + 4))
00271             return EOF;
00272     }
00273     if (value)
00274     {
00275         if ((size_t)kfile_printf(out, "%s=%s\n", key, value) != (strlen(key) + strlen(value) + 2))
00276             return EOF;
00277     }
00278
00279     /* Skip the old line with the key */
00280     if (key_found)
00281         kfile_gets(in, line, CONFIG_INI_MAX_LINE_LEN);
00282
00283     /*
00284      * Copy the rest of the input file.
00285      * Do not return error if the copied bytes are less than expected
00286      * but at least enough to write the last non-empty line.
00287      * This is needed for example if the out KFile is of fixed size (like memories).
00288      */
00289     len = in->size - in->seek_pos;
00290     kfile_off_t bytes_needed = findLastLine(in, line, CONFIG_INI_MAX_LINE_LEN) - in->seek_pos;
00291     if (kfile_copy(in, out, len) < bytes_needed)
00292         return EOF;
00293
00294     /*
00295      * Clear the rest of the out file in order to wipe out garbage if the resulting
00296      * file is shorter than before.
00297      */
00298     kfile_off_t fill = out->size - out->seek_pos;
00299
00300     if (fill--)
00301     {
00302         if (kfile_putc('\n', out) == EOF)
00303             return EOF;
00304         while (fill--)
00305         {
00306             if (kfile_putc(' ', out) == EOF)
00307                 return EOF;
00308         }
00309     }
00310
00311     return 0;
00312 }
00313