1 2 /*-------------------------------------------------------------------------*/ 3 /** 4 @file iniparser.c 5 @author N. Devillard 6 @brief Parser for ini files. 7 */ 8 /*--------------------------------------------------------------------------*/ 9 /*---------------------------- Includes ------------------------------------*/ 10 #include <ctype.h> 11 #include "iniparser.h" 12 13 /*---------------------------- Defines -------------------------------------*/ 14 #define ASCIILINESZ (1024) 15 #define INI_INVALID_KEY ((char*)-1) 16 17 /*--------------------------------------------------------------------------- 18 Private to this module 19 ---------------------------------------------------------------------------*/ 20 /** 21 * This enum stores the status for each parsed line (internal use only). 22 */ 23 typedef enum _line_status_ { 24 LINE_UNPROCESSED, 25 LINE_ERROR, 26 LINE_EMPTY, 27 LINE_COMMENT, 28 LINE_SECTION, 29 LINE_VALUE 30 } line_status ; 31 32 /*-------------------------------------------------------------------------*/ 33 /** 34 @brief Convert a string to lowercase. 35 @param s String to convert. 36 @return ptr to statically allocated string. 37 38 This function returns a pointer to a statically allocated string 39 containing a lowercased version of the input string. Do not free 40 or modify the returned string! Since the returned string is statically 41 allocated, it will be modified at each function call (not re-entrant). 42 */ 43 /*--------------------------------------------------------------------------*/ 44 static char * strlwc(const char * s) 45 { 46 static char l[ASCIILINESZ+1]; 47 int i ; 48 49 if (s==NULL) return NULL ; 50 memset(l, 0, ASCIILINESZ+1); 51 i=0 ; 52 while (s[i] && i<ASCIILINESZ) { 53 l[i] = (char)tolower((int)s[i]); 54 i++ ; 55 } 56 l[ASCIILINESZ]=(char)0; 57 return l ; 58 } 59 60 /*-------------------------------------------------------------------------*/ 61 /** 62 @brief Remove blanks at the beginning and the end of a string. 63 @param s String to parse. 64 @return ptr to statically allocated string. 65 66 This function returns a pointer to a statically allocated string, 67 which is identical to the input string, except that all blank 68 characters at the end and the beg. of the string have been removed. 69 Do not free or modify the returned string! Since the returned string 70 is statically allocated, it will be modified at each function call 71 (not re-entrant). 72 */ 73 /*--------------------------------------------------------------------------*/ 74 static char * strstrip(const char * s) 75 { 76 static char l[ASCIILINESZ+1]; 77 char * last ; 78 79 if (s==NULL) return NULL ; 80 81 while (isspace((int)*s) && *s) s++; 82 memset(l, 0, ASCIILINESZ+1); 83 strcpy(l, s); 84 last = l + strlen(l); 85 while (last > l) { 86 if (!isspace((int)*(last-1))) 87 break ; 88 last -- ; 89 } 90 *last = (char)0; 91 return (char*)l ; 92 } 93 94 /*-------------------------------------------------------------------------*/ 95 /** 96 @brief Get number of sections in a dictionary 97 @param d Dictionary to examine 98 @return int Number of sections found in dictionary 99 100 This function returns the number of sections found in a dictionary. 101 The test to recognize sections is done on the string stored in the 102 dictionary: a section name is given as "section" whereas a key is 103 stored as "section:key", thus the test looks for entries that do not 104 contain a colon. 105 106 This clearly fails in the case a section name contains a colon, but 107 this should simply be avoided. 108 109 This function returns -1 in case of error. 110 */ 111 /*--------------------------------------------------------------------------*/ 112 int iniparser_getnsec(dictionary * d) 113 { 114 int i ; 115 int nsec ; 116 117 if (d==NULL) return -1 ; 118 nsec=0 ; 119 for (i=0 ; i<d->size ; i++) { 120 if (d->key[i]==NULL) 121 continue ; 122 if (strchr(d->key[i], ':')==NULL) { 123 nsec ++ ; 124 } 125 } 126 return nsec ; 127 } 128 129 /*-------------------------------------------------------------------------*/ 130 /** 131 @brief Get name for section n in a dictionary. 132 @param d Dictionary to examine 133 @param n Section number (from 0 to nsec-1). 134 @return Pointer to char string 135 136 This function locates the n-th section in a dictionary and returns 137 its name as a pointer to a string statically allocated inside the 138 dictionary. Do not free or modify the returned string! 139 140 This function returns NULL in case of error. 141 */ 142 /*--------------------------------------------------------------------------*/ 143 char * iniparser_getsecname(dictionary * d, int n) 144 { 145 int i ; 146 int foundsec ; 147 148 if (d==NULL || n<0) return NULL ; 149 foundsec=0 ; 150 for (i=0 ; i<d->size ; i++) { 151 if (d->key[i]==NULL) 152 continue ; 153 if (strchr(d->key[i], ':')==NULL) { 154 foundsec++ ; 155 if (foundsec>n) 156 break ; 157 } 158 } 159 if (foundsec<=n) { 160 return NULL ; 161 } 162 return d->key[i] ; 163 } 164 165 /*-------------------------------------------------------------------------*/ 166 /** 167 @brief Dump a dictionary to an opened file pointer. 168 @param d Dictionary to dump. 169 @param f Opened file pointer to dump to. 170 @return void 171 172 This function prints out the contents of a dictionary, one element by 173 line, onto the provided file pointer. It is OK to specify @c stderr 174 or @c stdout as output files. This function is meant for debugging 175 purposes mostly. 176 */ 177 /*--------------------------------------------------------------------------*/ 178 void iniparser_dump(dictionary * d, FILE * f) 179 { 180 int i ; 181 182 if (d==NULL || f==NULL) return ; 183 for (i=0 ; i<d->size ; i++) { 184 if (d->key[i]==NULL) 185 continue ; 186 if (d->val[i]!=NULL) { 187 fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]); 188 } else { 189 fprintf(f, "[%s]=UNDEF\n", d->key[i]); 190 } 191 } 192 return ; 193 } 194 195 /*-------------------------------------------------------------------------*/ 196 /** 197 @brief Save a dictionary to a loadable ini file 198 @param d Dictionary to dump 199 @param f Opened file pointer to dump to 200 @return void 201 202 This function dumps a given dictionary into a loadable ini file. 203 It is Ok to specify @c stderr or @c stdout as output files. 204 */ 205 /*--------------------------------------------------------------------------*/ 206 void iniparser_dump_ini(dictionary * d, FILE * f) 207 { 208 int i ; 209 int nsec ; 210 char * secname ; 211 212 if (d==NULL || f==NULL) return ; 213 214 nsec = iniparser_getnsec(d); 215 if (nsec<1) { 216 /* No section in file: dump all keys as they are */ 217 for (i=0 ; i<d->size ; i++) { 218 if (d->key[i]==NULL) 219 continue ; 220 fprintf(f, "%s = %s\n", d->key[i], d->val[i]); 221 } 222 return ; 223 } 224 for (i=0 ; i<nsec ; i++) { 225 secname = iniparser_getsecname(d, i) ; 226 iniparser_dumpsection_ini(d, secname, f) ; 227 } 228 fprintf(f, "\n"); 229 return ; 230 } 231 232 /*-------------------------------------------------------------------------*/ 233 /** 234 @brief Save a dictionary section to a loadable ini file 235 @param d Dictionary to dump 236 @param s Section name of dictionary to dump 237 @param f Opened file pointer to dump to 238 @return void 239 240 This function dumps a given section of a given dictionary into a loadable ini 241 file. It is Ok to specify @c stderr or @c stdout as output files. 242 */ 243 /*--------------------------------------------------------------------------*/ 244 void iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f) 245 { 246 int j ; 247 char keym[ASCIILINESZ+1]; 248 int seclen ; 249 250 if (d==NULL || f==NULL) return ; 251 if (! iniparser_find_entry(d, s)) return ; 252 253 seclen = (int)strlen(s); 254 fprintf(f, "\n[%s]\n", s); 255 sprintf(keym, "%s:", s); 256 for (j=0 ; j<d->size ; j++) { 257 if (d->key[j]==NULL) 258 continue ; 259 if (!strncmp(d->key[j], keym, seclen+1)) { 260 fprintf(f, 261 "%-30s = %s\n", 262 d->key[j]+seclen+1, 263 d->val[j] ? d->val[j] : ""); 264 } 265 } 266 fprintf(f, "\n"); 267 return ; 268 } 269 270 /*-------------------------------------------------------------------------*/ 271 /** 272 @brief Get the number of keys in a section of a dictionary. 273 @param d Dictionary to examine 274 @param s Section name of dictionary to examine 275 @return Number of keys in section 276 */ 277 /*--------------------------------------------------------------------------*/ 278 int iniparser_getsecnkeys(dictionary * d, char * s) 279 { 280 int seclen, nkeys ; 281 char keym[ASCIILINESZ+1]; 282 int j ; 283 284 nkeys = 0; 285 286 if (d==NULL) return nkeys; 287 if (! iniparser_find_entry(d, s)) return nkeys; 288 289 seclen = (int)strlen(s); 290 sprintf(keym, "%s:", s); 291 292 for (j=0 ; j<d->size ; j++) { 293 if (d->key[j]==NULL) 294 continue ; 295 if (!strncmp(d->key[j], keym, seclen+1)) 296 nkeys++; 297 } 298 299 return nkeys; 300 301 } 302 303 /*-------------------------------------------------------------------------*/ 304 /** 305 @brief Get the number of keys in a section of a dictionary. 306 @param d Dictionary to examine 307 @param s Section name of dictionary to examine 308 @return pointer to statically allocated character strings 309 310 This function queries a dictionary and finds all keys in a given section. 311 Each pointer in the returned char pointer-to-pointer is pointing to 312 a string allocated in the dictionary; do not free or modify them. 313 314 This function returns NULL in case of error. 315 */ 316 /*--------------------------------------------------------------------------*/ 317 char ** iniparser_getseckeys(dictionary * d, char * s) 318 { 319 320 char **keys; 321 322 int i, j ; 323 char keym[ASCIILINESZ+1]; 324 int seclen, nkeys ; 325 326 keys = NULL; 327 328 if (d==NULL) return keys; 329 if (! iniparser_find_entry(d, s)) return keys; 330 331 nkeys = iniparser_getsecnkeys(d, s); 332 333 keys = (char**) malloc(nkeys*sizeof(char*)); 334 335 seclen = (int)strlen(s); 336 sprintf(keym, "%s:", s); 337 338 i = 0; 339 340 for (j=0 ; j<d->size ; j++) { 341 if (d->key[j]==NULL) 342 continue ; 343 if (!strncmp(d->key[j], keym, seclen+1)) { 344 keys[i] = d->key[j]; 345 i++; 346 } 347 } 348 349 return keys; 350 351 } 352 353 /*-------------------------------------------------------------------------*/ 354 /** 355 @brief Get the string associated to a key 356 @param d Dictionary to search 357 @param key Key string to look for 358 @param def Default value to return if key not found. 359 @return pointer to statically allocated character string 360 361 This function queries a dictionary for a key. A key as read from an 362 ini file is given as "section:key". If the key cannot be found, 363 the pointer passed as 'def' is returned. 364 The returned char pointer is pointing to a string allocated in 365 the dictionary, do not free or modify it. 366 */ 367 /*--------------------------------------------------------------------------*/ 368 char * iniparser_getstring(dictionary * d, const char * key, char * def) 369 { 370 char * lc_key ; 371 char * sval ; 372 373 if (d==NULL || key==NULL) 374 return def ; 375 376 lc_key = strlwc(key); 377 sval = dictionary_get(d, lc_key, def); 378 return sval ; 379 } 380 381 /*-------------------------------------------------------------------------*/ 382 /** 383 @brief Get the string associated to a key, convert to an int 384 @param d Dictionary to search 385 @param key Key string to look for 386 @param notfound Value to return in case of error 387 @return integer 388 389 This function queries a dictionary for a key. A key as read from an 390 ini file is given as "section:key". If the key cannot be found, 391 the notfound value is returned. 392 393 Supported values for integers include the usual C notation 394 so decimal, octal (starting with 0) and hexadecimal (starting with 0x) 395 are supported. Examples: 396 397 "42" -> 42 398 "042" -> 34 (octal -> decimal) 399 "0x42" -> 66 (hexa -> decimal) 400 401 Warning: the conversion may overflow in various ways. Conversion is 402 totally outsourced to strtol(), see the associated man page for overflow 403 handling. 404 405 Credits: Thanks to A. Becker for suggesting strtol() 406 */ 407 /*--------------------------------------------------------------------------*/ 408 int iniparser_getint(dictionary * d, const char * key, int notfound) 409 { 410 char * str ; 411 412 str = iniparser_getstring(d, key, INI_INVALID_KEY); 413 if (str==INI_INVALID_KEY) return notfound ; 414 return (int)strtol(str, NULL, 0); 415 } 416 417 /*-------------------------------------------------------------------------*/ 418 /** 419 @brief Get the string associated to a key, convert to a double 420 @param d Dictionary to search 421 @param key Key string to look for 422 @param notfound Value to return in case of error 423 @return double 424 425 This function queries a dictionary for a key. A key as read from an 426 ini file is given as "section:key". If the key cannot be found, 427 the notfound value is returned. 428 */ 429 /*--------------------------------------------------------------------------*/ 430 double iniparser_getdouble(dictionary * d, const char * key, double notfound) 431 { 432 char * str ; 433 434 str = iniparser_getstring(d, key, INI_INVALID_KEY); 435 if (str==INI_INVALID_KEY) return notfound ; 436 return atof(str); 437 } 438 439 /*-------------------------------------------------------------------------*/ 440 /** 441 @brief Get the string associated to a key, convert to a boolean 442 @param d Dictionary to search 443 @param key Key string to look for 444 @param notfound Value to return in case of error 445 @return integer 446 447 This function queries a dictionary for a key. A key as read from an 448 ini file is given as "section:key". If the key cannot be found, 449 the notfound value is returned. 450 451 A true boolean is found if one of the following is matched: 452 453 - A string starting with 'y' 454 - A string starting with 'Y' 455 - A string starting with 't' 456 - A string starting with 'T' 457 - A string starting with '1' 458 459 A false boolean is found if one of the following is matched: 460 461 - A string starting with 'n' 462 - A string starting with 'N' 463 - A string starting with 'f' 464 - A string starting with 'F' 465 - A string starting with '0' 466 467 The notfound value returned if no boolean is identified, does not 468 necessarily have to be 0 or 1. 469 */ 470 /*--------------------------------------------------------------------------*/ 471 int iniparser_getboolean(dictionary * d, const char * key, int notfound) 472 { 473 char * c ; 474 int ret ; 475 476 c = iniparser_getstring(d, key, INI_INVALID_KEY); 477 if (c==INI_INVALID_KEY) return notfound ; 478 if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') { 479 ret = 1 ; 480 } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') { 481 ret = 0 ; 482 } else { 483 ret = notfound ; 484 } 485 return ret; 486 } 487 488 /*-------------------------------------------------------------------------*/ 489 /** 490 @brief Finds out if a given entry exists in a dictionary 491 @param ini Dictionary to search 492 @param entry Name of the entry to look for 493 @return integer 1 if entry exists, 0 otherwise 494 495 Finds out if a given entry exists in the dictionary. Since sections 496 are stored as keys with NULL associated values, this is the only way 497 of querying for the presence of sections in a dictionary. 498 */ 499 /*--------------------------------------------------------------------------*/ 500 int iniparser_find_entry( 501 dictionary * ini, 502 const char * entry 503 ) 504 { 505 int found=0 ; 506 if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) { 507 found = 1 ; 508 } 509 return found ; 510 } 511 512 /*-------------------------------------------------------------------------*/ 513 /** 514 @brief Set an entry in a dictionary. 515 @param ini Dictionary to modify. 516 @param entry Entry to modify (entry name) 517 @param val New value to associate to the entry. 518 @return int 0 if Ok, -1 otherwise. 519 520 If the given entry can be found in the dictionary, it is modified to 521 contain the provided value. If it cannot be found, -1 is returned. 522 It is Ok to set val to NULL. 523 */ 524 /*--------------------------------------------------------------------------*/ 525 int iniparser_set(dictionary * ini, const char * entry, const char * val) 526 { 527 return dictionary_set(ini, strlwc(entry), val) ; 528 } 529 530 /*-------------------------------------------------------------------------*/ 531 /** 532 @brief Delete an entry in a dictionary 533 @param ini Dictionary to modify 534 @param entry Entry to delete (entry name) 535 @return void 536 537 If the given entry can be found, it is deleted from the dictionary. 538 */ 539 /*--------------------------------------------------------------------------*/ 540 void iniparser_unset(dictionary * ini, const char * entry) 541 { 542 dictionary_unset(ini, strlwc(entry)); 543 } 544 545 /*-------------------------------------------------------------------------*/ 546 /** 547 @brief Load a single line from an INI file 548 @param input_line Input line, may be concatenated multi-line input 549 @param section Output space to store section 550 @param key Output space to store key 551 @param value Output space to store value 552 @return line_status value 553 */ 554 /*--------------------------------------------------------------------------*/ 555 static line_status iniparser_line( 556 const char * input_line, 557 char * section, 558 char * key, 559 char * value) 560 { 561 line_status sta ; 562 char line[ASCIILINESZ+1]; 563 int len ; 564 565 strcpy(line, strstrip(input_line)); 566 len = (int)strlen(line); 567 568 sta = LINE_UNPROCESSED ; 569 if (len<1) { 570 /* Empty line */ 571 sta = LINE_EMPTY ; 572 } else if (line[0]=='#' || line[0]==';') { 573 /* Comment line */ 574 sta = LINE_COMMENT ; 575 } else if (line[0]=='[' && line[len-1]==']') { 576 /* Section name */ 577 sscanf(line, "[%[^]]", section); 578 strcpy(section, strstrip(section)); 579 strcpy(section, strlwc(section)); 580 sta = LINE_SECTION ; 581 } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2 582 || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2 583 || sscanf (line, "%[^=] = %[^;#]", key, value) == 2) { 584 /* Usual key=value, with or without comments */ 585 strcpy(key, strstrip(key)); 586 strcpy(key, strlwc(key)); 587 strcpy(value, strstrip(value)); 588 /* 589 * sscanf cannot handle '' or "" as empty values 590 * this is done here 591 */ 592 if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) { 593 value[0]=0 ; 594 } 595 sta = LINE_VALUE ; 596 } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2 597 || sscanf(line, "%[^=] %[=]", key, value) == 2) { 598 /* 599 * Special cases: 600 * key= 601 * key=; 602 * key=# 603 */ 604 strcpy(key, strstrip(key)); 605 strcpy(key, strlwc(key)); 606 value[0]=0 ; 607 sta = LINE_VALUE ; 608 } else { 609 /* Generate syntax error */ 610 sta = LINE_ERROR ; 611 } 612 return sta ; 613 } 614 615 /*-------------------------------------------------------------------------*/ 616 /** 617 @brief Parse an ini file and return an allocated dictionary object 618 @param ininame Name of the ini file to read. 619 @return Pointer to newly allocated dictionary 620 621 This is the parser for ini files. This function is called, providing 622 the name of the file to be read. It returns a dictionary object that 623 should not be accessed directly, but through accessor functions 624 instead. 625 626 The returned dictionary must be freed using iniparser_freedict(). 627 */ 628 /*--------------------------------------------------------------------------*/ 629 dictionary * iniparser_load(const char * ininame) 630 { 631 FILE * in ; 632 633 char line [ASCIILINESZ+1] ; 634 char section [ASCIILINESZ+1] ; 635 char key [ASCIILINESZ+1] ; 636 char tmp [ASCIILINESZ+1] ; 637 char val [ASCIILINESZ+1] ; 638 639 int last=0 ; 640 int len ; 641 int lineno=0 ; 642 int errs=0; 643 644 dictionary * dict ; 645 646 if ((in=fopen(ininame, "r"))==NULL) { 647 fprintf(stderr, "iniparser: cannot open %s\n", ininame); 648 return NULL ; 649 } 650 651 dict = dictionary_new(0) ; 652 if (!dict) { 653 fclose(in); 654 return NULL ; 655 } 656 657 memset(line, 0, ASCIILINESZ); 658 memset(section, 0, ASCIILINESZ); 659 memset(key, 0, ASCIILINESZ); 660 memset(val, 0, ASCIILINESZ); 661 last=0 ; 662 663 while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) { 664 lineno++ ; 665 len = (int)strlen(line)-1; 666 if (len==0) 667 continue; 668 /* Safety check against buffer overflows */ 669 if (line[len]!='\n') { 670 fprintf(stderr, 671 "iniparser: input line too long in %s (%d)\n", 672 ininame, 673 lineno); 674 dictionary_del(dict); 675 fclose(in); 676 return NULL ; 677 } 678 /* Get rid of \n and spaces at end of line */ 679 while ((len>=0) && 680 ((line[len]=='\n') || (isspace(line[len])))) { 681 line[len]=0 ; 682 len-- ; 683 } 684 /* Detect multi-line */ 685 if (line[len]=='\\') { 686 /* Multi-line value */ 687 last=len ; 688 continue ; 689 } else { 690 last=0 ; 691 } 692 switch (iniparser_line(line, section, key, val)) { 693 case LINE_EMPTY: 694 case LINE_COMMENT: 695 break ; 696 697 case LINE_SECTION: 698 errs = dictionary_set(dict, section, NULL); 699 break ; 700 701 case LINE_VALUE: 702 sprintf(tmp, "%s:%s", section, key); 703 errs = dictionary_set(dict, tmp, val) ; 704 break ; 705 706 case LINE_ERROR: 707 fprintf(stderr, "iniparser: syntax error in %s (%d):\n", 708 ininame, 709 lineno); 710 fprintf(stderr, "-> %s\n", line); 711 errs++ ; 712 break; 713 714 default: 715 break ; 716 } 717 memset(line, 0, ASCIILINESZ); 718 last=0; 719 if (errs<0) { 720 fprintf(stderr, "iniparser: memory allocation failure\n"); 721 break ; 722 } 723 } 724 if (errs) { 725 dictionary_del(dict); 726 dict = NULL ; 727 } 728 fclose(in); 729 return dict ; 730 } 731 732 /*-------------------------------------------------------------------------*/ 733 /** 734 @brief Free all memory associated to an ini dictionary 735 @param d Dictionary to free 736 @return void 737 738 Free all memory associated to an ini dictionary. 739 It is mandatory to call this function before the dictionary object 740 gets out of the current context. 741 */ 742 /*--------------------------------------------------------------------------*/ 743 void iniparser_freedict(dictionary * d) 744 { 745 dictionary_del(d); 746 } 747 748 /* vim: set ts=4 et sw=4 tw=75 */ 749