Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
iniparser.c
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 */
Generated on Thu Jul 14 2022 07:58:10 by
1.7.2