Rolf Meyer / Mbed 2 deprecated iniparser

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers iniparser.c Source File

iniparser.c

Go to the documentation of this file.
00001 
00002 /*-------------------------------------------------------------------------*/
00003 /**
00004    @file    iniparser.c
00005    @author  N. Devillard
00006    @date    Sep 2007
00007    @version 3.0
00008    @brief   Parser for ini files.
00009 */
00010 /*--------------------------------------------------------------------------*/
00011 /*
00012     $Id: iniparser.c,v 2.18 2008-01-03 18:35:39 ndevilla Exp $
00013     $Revision: 2.18 $
00014     $Date: 2008-01-03 18:35:39 $
00015 */
00016 /*---------------------------- Includes ------------------------------------*/
00017 #include <ctype.h>
00018 #include "iniparser.h"
00019 
00020 /*---------------------------- Defines -------------------------------------*/
00021 #define ASCIILINESZ         (1024)
00022 #define INI_INVALID_KEY     ((char*)-1)
00023 
00024 /*---------------------------------------------------------------------------
00025                         Private to this module
00026  ---------------------------------------------------------------------------*/
00027 /**
00028  * This enum stores the status for each parsed line (internal use only).
00029  */
00030 typedef enum _line_status_ {
00031     LINE_UNPROCESSED,
00032     LINE_ERROR,
00033     LINE_EMPTY,
00034     LINE_COMMENT,
00035     LINE_SECTION,
00036     LINE_VALUE
00037 } line_status ;
00038 
00039 /*-------------------------------------------------------------------------*/
00040 /**
00041   @brief    Convert a string to lowercase.
00042   @param    s   String to convert.
00043   @return   ptr to statically allocated string.
00044 
00045   This function returns a pointer to a statically allocated string
00046   containing a lowercased version of the input string. Do not free
00047   or modify the returned string! Since the returned string is statically
00048   allocated, it will be modified at each function call (not re-entrant).
00049  */
00050 /*--------------------------------------------------------------------------*/
00051 static char * strlwc(const char * s)
00052 {
00053     static char l[ASCIILINESZ+1];
00054     int i ;
00055 
00056     if (s==NULL) return NULL ;
00057     memset(l, 0, ASCIILINESZ+1);
00058     i=0 ;
00059     while (s[i] && i<ASCIILINESZ) {
00060         l[i] = (char)tolower((int)s[i]);
00061         i++ ;
00062     }
00063     l[ASCIILINESZ]=(char)0;
00064     return l ;
00065 }
00066 
00067 /*-------------------------------------------------------------------------*/
00068 /**
00069   @brief    Remove blanks at the beginning and the end of a string.
00070   @param    s   String to parse.
00071   @return   ptr to statically allocated string.
00072 
00073   This function returns a pointer to a statically allocated string,
00074   which is identical to the input string, except that all blank
00075   characters at the end and the beg. of the string have been removed.
00076   Do not free or modify the returned string! Since the returned string
00077   is statically allocated, it will be modified at each function call
00078   (not re-entrant).
00079  */
00080 /*--------------------------------------------------------------------------*/
00081 static char * strstrip(char * s)
00082 {
00083     static char l[ASCIILINESZ+1];
00084     char * last ;
00085     
00086     if (s==NULL) return NULL ;
00087     
00088     while (isspace((int)*s) && *s) s++;
00089     memset(l, 0, ASCIILINESZ+1);
00090     strcpy(l, s);
00091     last = l + strlen(l);
00092     while (last > l) {
00093         if (!isspace((int)*(last-1)))
00094             break ;
00095         last -- ;
00096     }
00097     *last = (char)0;
00098     return (char*)l ;
00099 }
00100 
00101 /*-------------------------------------------------------------------------*/
00102 /**
00103   @brief    Get number of sections in a dictionary
00104   @param    d   Dictionary to examine
00105   @return   int Number of sections found in dictionary
00106 
00107   This function returns the number of sections found in a dictionary.
00108   The test to recognize sections is done on the string stored in the
00109   dictionary: a section name is given as "section" whereas a key is
00110   stored as "section:key", thus the test looks for entries that do not
00111   contain a colon.
00112 
00113   This clearly fails in the case a section name contains a colon, but
00114   this should simply be avoided.
00115 
00116   This function returns -1 in case of error.
00117  */
00118 /*--------------------------------------------------------------------------*/
00119 int iniparser_getnsec(dictionary * d)
00120 {
00121     int i ;
00122     int nsec ;
00123 
00124     if (d==NULL) return -1 ;
00125     nsec=0 ;
00126     for (i=0 ; i<d->size ; i++) {
00127         if (d->key[i]==NULL)
00128             continue ;
00129         if (strchr(d->key[i], ':')==NULL) {
00130             nsec ++ ;
00131         }
00132     }
00133     return nsec ;
00134 }
00135 
00136 /*-------------------------------------------------------------------------*/
00137 /**
00138   @brief    Get name for section n in a dictionary.
00139   @param    d   Dictionary to examine
00140   @param    n   Section number (from 0 to nsec-1).
00141   @return   Pointer to char string
00142 
00143   This function locates the n-th section in a dictionary and returns
00144   its name as a pointer to a string statically allocated inside the
00145   dictionary. Do not free or modify the returned string!
00146 
00147   This function returns NULL in case of error.
00148  */
00149 /*--------------------------------------------------------------------------*/
00150 char * iniparser_getsecname(dictionary * d, int n)
00151 {
00152     int i ;
00153     int foundsec ;
00154 
00155     if (d==NULL || n<0) return NULL ;
00156     foundsec=0 ;
00157     for (i=0 ; i<d->size ; i++) {
00158         if (d->key[i]==NULL)
00159             continue ;
00160         if (strchr(d->key[i], ':')==NULL) {
00161             foundsec++ ;
00162             if (foundsec>n)
00163                 break ;
00164         }
00165     }
00166     if (foundsec<=n) {
00167         return NULL ;
00168     }
00169     return d->key[i] ;
00170 }
00171 
00172 /*-------------------------------------------------------------------------*/
00173 /**
00174   @brief    Dump a dictionary to an opened file pointer.
00175   @param    d   Dictionary to dump.
00176   @param    f   Opened file pointer to dump to.
00177   @return   void
00178 
00179   This function prints out the contents of a dictionary, one element by
00180   line, onto the provided file pointer. It is OK to specify @c stderr
00181   or @c stdout as output files. This function is meant for debugging
00182   purposes mostly.
00183  */
00184 /*--------------------------------------------------------------------------*/
00185 void iniparser_dump(dictionary * d, FILE * f)
00186 {
00187     int     i ;
00188 
00189     if (d==NULL || f==NULL) return ;
00190     for (i=0 ; i<d->size ; i++) {
00191         if (d->key[i]==NULL)
00192             continue ;
00193         if (d->val[i]!=NULL) {
00194             fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
00195         } else {
00196             fprintf(f, "[%s]=UNDEF\n", d->key[i]);
00197         }
00198     }
00199     return ;
00200 }
00201 
00202 /*-------------------------------------------------------------------------*/
00203 /**
00204   @brief    Save a dictionary to a loadable ini file
00205   @param    d   Dictionary to dump
00206   @param    f   Opened file pointer to dump to
00207   @return   void
00208 
00209   This function dumps a given dictionary into a loadable ini file.
00210   It is Ok to specify @c stderr or @c stdout as output files.
00211  */
00212 /*--------------------------------------------------------------------------*/
00213 void iniparser_dump_ini(dictionary * d, FILE * f)
00214 {
00215     int     i, j ;
00216     char    keym[ASCIILINESZ+1];
00217     int     nsec ;
00218     char *  secname ;
00219     int     seclen ;
00220 
00221     if (d==NULL || f==NULL) return ;
00222 
00223     nsec = iniparser_getnsec(d);
00224     if (nsec<1) {
00225         /* No section in file: dump all keys as they are */
00226         for (i=0 ; i<d->size ; i++) {
00227             if (d->key[i]==NULL)
00228                 continue ;
00229             fprintf(f, "%s = %s\n", d->key[i], d->val[i]);
00230         }
00231         return ;
00232     }
00233     for (i=0 ; i<nsec ; i++) {
00234         secname = iniparser_getsecname(d, i) ;
00235         seclen  = (int)strlen(secname);
00236         fprintf(f, "\n[%s]\n", secname);
00237         sprintf(keym, "%s:", secname);
00238         for (j=0 ; j<d->size ; j++) {
00239             if (d->key[j]==NULL)
00240                 continue ;
00241             if (!strncmp(d->key[j], keym, seclen+1)) {
00242                 fprintf(f,
00243                         "%-30s = %s\n",
00244                         d->key[j]+seclen+1,
00245                         d->val[j] ? d->val[j] : "");
00246             }
00247         }
00248     }
00249     fprintf(f, "\n");
00250     return ;
00251 }
00252 
00253 /*-------------------------------------------------------------------------*/
00254 /**
00255   @brief    Get the string associated to a key
00256   @param    d       Dictionary to search
00257   @param    key     Key string to look for
00258   @param    def     Default value to return if key not found.
00259   @return   pointer to statically allocated character string
00260 
00261   This function queries a dictionary for a key. A key as read from an
00262   ini file is given as "section:key". If the key cannot be found,
00263   the pointer passed as 'def' is returned.
00264   The returned char pointer is pointing to a string allocated in
00265   the dictionary, do not free or modify it.
00266  */
00267 /*--------------------------------------------------------------------------*/
00268 char * iniparser_getstring(dictionary * d, const char * key, char * def)
00269 {
00270     char * lc_key ;
00271     char * sval ;
00272 
00273     if (d==NULL || key==NULL)
00274         return def ;
00275 
00276     lc_key = strlwc(key);
00277     sval = dictionary_get(d, lc_key, def);
00278     return sval ;
00279 }
00280 
00281 /*-------------------------------------------------------------------------*/
00282 /**
00283   @brief    Get the string associated to a key, convert to an int
00284   @param    d Dictionary to search
00285   @param    key Key string to look for
00286   @param    notfound Value to return in case of error
00287   @return   integer
00288 
00289   This function queries a dictionary for a key. A key as read from an
00290   ini file is given as "section:key". If the key cannot be found,
00291   the notfound value is returned.
00292 
00293   Supported values for integers include the usual C notation
00294   so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
00295   are supported. Examples:
00296 
00297   "42"      ->  42
00298   "042"     ->  34 (octal -> decimal)
00299   "0x42"    ->  66 (hexa  -> decimal)
00300 
00301   Warning: the conversion may overflow in various ways. Conversion is
00302   totally outsourced to strtol(), see the associated man page for overflow
00303   handling.
00304 
00305   Credits: Thanks to A. Becker for suggesting strtol()
00306  */
00307 /*--------------------------------------------------------------------------*/
00308 int iniparser_getint(dictionary * d, const char * key, int notfound)
00309 {
00310     char    *   str ;
00311 
00312     str = iniparser_getstring(d, key, INI_INVALID_KEY);
00313     if (str==INI_INVALID_KEY) return notfound ;
00314     return (int)strtol(str, NULL, 0);
00315 }
00316 
00317 /*-------------------------------------------------------------------------*/
00318 /**
00319   @brief    Get the string associated to a key, convert to a double
00320   @param    d Dictionary to search
00321   @param    key Key string to look for
00322   @param    notfound Value to return in case of error
00323   @return   double
00324 
00325   This function queries a dictionary for a key. A key as read from an
00326   ini file is given as "section:key". If the key cannot be found,
00327   the notfound value is returned.
00328  */
00329 /*--------------------------------------------------------------------------*/
00330 double iniparser_getdouble(dictionary * d, char * key, double notfound)
00331 {
00332     char    *   str ;
00333 
00334     str = iniparser_getstring(d, key, INI_INVALID_KEY);
00335     if (str==INI_INVALID_KEY) return notfound ;
00336     return atof(str);
00337 }
00338 
00339 /*-------------------------------------------------------------------------*/
00340 /**
00341   @brief    Get the string associated to a key, convert to a boolean
00342   @param    d Dictionary to search
00343   @param    key Key string to look for
00344   @param    notfound Value to return in case of error
00345   @return   integer
00346 
00347   This function queries a dictionary for a key. A key as read from an
00348   ini file is given as "section:key". If the key cannot be found,
00349   the notfound value is returned.
00350 
00351   A true boolean is found if one of the following is matched:
00352 
00353   - A string starting with 'y'
00354   - A string starting with 'Y'
00355   - A string starting with 't'
00356   - A string starting with 'T'
00357   - A string starting with '1'
00358 
00359   A false boolean is found if one of the following is matched:
00360 
00361   - A string starting with 'n'
00362   - A string starting with 'N'
00363   - A string starting with 'f'
00364   - A string starting with 'F'
00365   - A string starting with '0'
00366 
00367   The notfound value returned if no boolean is identified, does not
00368   necessarily have to be 0 or 1.
00369  */
00370 /*--------------------------------------------------------------------------*/
00371 int iniparser_getboolean(dictionary * d, const char * key, int notfound)
00372 {
00373     char    *   c ;
00374     int         ret ;
00375 
00376     c = iniparser_getstring(d, key, INI_INVALID_KEY);
00377     if (c==INI_INVALID_KEY) return notfound ;
00378     if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
00379         ret = 1 ;
00380     } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {
00381         ret = 0 ;
00382     } else {
00383         ret = notfound ;
00384     }
00385     return ret;
00386 }
00387 
00388 /*-------------------------------------------------------------------------*/
00389 /**
00390   @brief    Finds out if a given entry exists in a dictionary
00391   @param    ini     Dictionary to search
00392   @param    entry   Name of the entry to look for
00393   @return   integer 1 if entry exists, 0 otherwise
00394 
00395   Finds out if a given entry exists in the dictionary. Since sections
00396   are stored as keys with NULL associated values, this is the only way
00397   of querying for the presence of sections in a dictionary.
00398  */
00399 /*--------------------------------------------------------------------------*/
00400 int iniparser_find_entry(
00401     dictionary  *   ini,
00402     char        *   entry
00403 )
00404 {
00405     int found=0 ;
00406     if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) {
00407         found = 1 ;
00408     }
00409     return found ;
00410 }
00411 
00412 /*-------------------------------------------------------------------------*/
00413 /**
00414   @brief    Set an entry in a dictionary.
00415   @param    ini     Dictionary to modify.
00416   @param    entry   Entry to modify (entry name)
00417   @param    val     New value to associate to the entry.
00418   @return   int 0 if Ok, -1 otherwise.
00419 
00420   If the given entry can be found in the dictionary, it is modified to
00421   contain the provided value. If it cannot be found, -1 is returned.
00422   It is Ok to set val to NULL.
00423  */
00424 /*--------------------------------------------------------------------------*/
00425 int iniparser_set(dictionary * ini, char * entry, char * val)
00426 {
00427     return dictionary_set(ini, strlwc(entry), val) ;
00428 }
00429 
00430 /*-------------------------------------------------------------------------*/
00431 /**
00432   @brief    Delete an entry in a dictionary
00433   @param    ini     Dictionary to modify
00434   @param    entry   Entry to delete (entry name)
00435   @return   void
00436 
00437   If the given entry can be found, it is deleted from the dictionary.
00438  */
00439 /*--------------------------------------------------------------------------*/
00440 void iniparser_unset(dictionary * ini, char * entry)
00441 {
00442     dictionary_unset(ini, strlwc(entry));
00443 }
00444 
00445 /*-------------------------------------------------------------------------*/
00446 /**
00447   @brief    Load a single line from an INI file
00448   @param    input_line  Input line, may be concatenated multi-line input
00449   @param    section     Output space to store section
00450   @param    key         Output space to store key
00451   @param    value       Output space to store value
00452   @return   line_status value
00453  */
00454 /*--------------------------------------------------------------------------*/
00455 static line_status iniparser_line(
00456     char * input_line,
00457     char * section,
00458     char * key,
00459     char * value)
00460 {   
00461     line_status sta ;
00462     char        line[ASCIILINESZ+1];
00463     int         len ;
00464 
00465     strcpy(line, strstrip(input_line));
00466     len = (int)strlen(line);
00467 
00468     sta = LINE_UNPROCESSED ;
00469     if (len<1) {
00470         /* Empty line */
00471         sta = LINE_EMPTY ;
00472     } else if (line[0]=='#') {
00473         /* Comment line */
00474         sta = LINE_COMMENT ; 
00475     } else if (line[0]=='[' && line[len-1]==']') {
00476         /* Section name */
00477         sscanf(line, "[%[^]]", section);
00478         strcpy(section, strstrip(section));
00479         strcpy(section, strlwc(section));
00480         sta = LINE_SECTION ;
00481     } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
00482            ||  sscanf (line, "%[^=] = '%[^\']'",   key, value) == 2
00483            ||  sscanf (line, "%[^=] = %[^;#]",     key, value) == 2) {
00484         /* Usual key=value, with or without comments */
00485         strcpy(key, strstrip(key));
00486         strcpy(key, strlwc(key));
00487         strcpy(value, strstrip(value));
00488         /*
00489          * sscanf cannot handle '' or "" as empty values
00490          * this is done here
00491          */
00492         if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {
00493             value[0]=0 ;
00494         }
00495         sta = LINE_VALUE ;
00496     } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2
00497            ||  sscanf(line, "%[^=] %[=]", key, value) == 2) {
00498         /*
00499          * Special cases:
00500          * key=
00501          * key=;
00502          * key=#
00503          */
00504         strcpy(key, strstrip(key));
00505         strcpy(key, strlwc(key));
00506         value[0]=0 ;
00507         sta = LINE_VALUE ;
00508     } else {
00509         /* Generate syntax error */
00510         sta = LINE_ERROR ;
00511     }
00512     return sta ;
00513 }
00514 
00515 /*-------------------------------------------------------------------------*/
00516 /**
00517   @brief    Parse an ini file and return an allocated dictionary object
00518   @param    ininame Name of the ini file to read.
00519   @return   Pointer to newly allocated dictionary
00520 
00521   This is the parser for ini files. This function is called, providing
00522   the name of the file to be read. It returns a dictionary object that
00523   should not be accessed directly, but through accessor functions
00524   instead.
00525 
00526   The returned dictionary must be freed using iniparser_freedict().
00527  */
00528 /*--------------------------------------------------------------------------*/
00529 dictionary * iniparser_load(const char * ininame)
00530 {
00531     FILE * in ;
00532 
00533     char line    [ASCIILINESZ+1] ;
00534     char section [ASCIILINESZ+1] ;
00535     char key     [ASCIILINESZ+1] ;
00536     char tmp     [ASCIILINESZ+1] ;
00537     char val     [ASCIILINESZ+1] ;
00538 
00539     int  last=0 ;
00540     int  len ;
00541     int  lineno=0 ;
00542     int  errs=0;
00543 
00544     dictionary * dict ;
00545 
00546     if ((in=fopen(ininame, "r"))==NULL) {
00547         fprintf(stderr, "iniparser: cannot open %s\n", ininame);
00548         return NULL ;
00549     }
00550 
00551     dict = dictionary_new(0) ;
00552     if (!dict) {
00553         fclose(in);
00554         return NULL ;
00555     }
00556 
00557     memset(line,    0, ASCIILINESZ);
00558     memset(section, 0, ASCIILINESZ);
00559     memset(key,     0, ASCIILINESZ);
00560     memset(val,     0, ASCIILINESZ);
00561     last=0 ;
00562 
00563     while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) {
00564         lineno++ ;
00565         len = (int)strlen(line)-1;
00566         /* Safety check against buffer overflows */
00567         if (line[len]!='\n') {
00568             fprintf(stderr,
00569                     "iniparser: input line too long in %s (%d)\n",
00570                     ininame,
00571                     lineno);
00572             dictionary_del(dict);
00573             fclose(in);
00574             return NULL ;
00575         }
00576         /* Get rid of \n and spaces at end of line */
00577         while ((len>=0) &&
00578                 ((line[len]=='\n') || (isspace(line[len])))) {
00579             line[len]=0 ;
00580             len-- ;
00581         }
00582         /* Detect multi-line */
00583         if (line[len]=='\\') {
00584             /* Multi-line value */
00585             last=len ;
00586             continue ;
00587         } else {
00588             last=0 ;
00589         }
00590         switch (iniparser_line(line, section, key, val)) {
00591             case LINE_EMPTY:
00592             case LINE_COMMENT:
00593             break ;
00594 
00595             case LINE_SECTION:
00596             errs = dictionary_set(dict, section, NULL);
00597             break ;
00598 
00599             case LINE_VALUE:
00600             sprintf(tmp, "%s:%s", section, key);
00601             errs = dictionary_set(dict, tmp, val) ;
00602             break ;
00603 
00604             case LINE_ERROR:
00605             fprintf(stderr, "iniparser: syntax error in %s (%d):\n",
00606                     ininame,
00607                     lineno);
00608             fprintf(stderr, "-> %s\n", line);
00609             errs++ ;
00610             break;
00611 
00612             default:
00613             break ;
00614         }
00615         memset(line, 0, ASCIILINESZ);
00616         last=0;
00617         if (errs<0) {
00618             fprintf(stderr, "iniparser: memory allocation failure\n");
00619             break ;
00620         }
00621     }
00622     if (errs) {
00623         dictionary_del(dict);
00624         dict = NULL ;
00625     }
00626     fclose(in);
00627     return dict ;
00628 }
00629 
00630 /*-------------------------------------------------------------------------*/
00631 /**
00632   @brief    Free all memory associated to an ini dictionary
00633   @param    d Dictionary to free
00634   @return   void
00635 
00636   Free all memory associated to an ini dictionary.
00637   It is mandatory to call this function before the dictionary object
00638   gets out of the current context.
00639  */
00640 /*--------------------------------------------------------------------------*/
00641 void iniparser_freedict(dictionary * d)
00642 {
00643     dictionary_del(d);
00644 }
00645 
00646 /* vim: set ts=4 et sw=4 tw=75 */