iniparser.c 25.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10

/*-------------------------------------------------------------------------*/
/**
   @file    iniparser.c
   @author  N. Devillard
   @brief   Parser for ini files.
*/
/*--------------------------------------------------------------------------*/
/*---------------------------- Includes ------------------------------------*/
#include <ctype.h>
11
#include <stdarg.h>
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
#include "iniparser.h"

/*---------------------------- Defines -------------------------------------*/
#define ASCIILINESZ         (1024)
#define INI_INVALID_KEY     ((char*)-1)

/*---------------------------------------------------------------------------
                        Private to this module
 ---------------------------------------------------------------------------*/
/**
 * This enum stores the status for each parsed line (internal use only).
 */
typedef enum _line_status_ {
    LINE_UNPROCESSED,
    LINE_ERROR,
    LINE_EMPTY,
    LINE_COMMENT,
    LINE_SECTION,
    LINE_VALUE
} line_status ;

/*-------------------------------------------------------------------------*/
/**
35
  @brief    Convert a string to lowercase.
E
Emmanuel Leblond 已提交
36 37 38 39
  @param    in   String to convert.
  @param    out Output buffer.
  @param    len Size of the out buffer.
  @return   ptr to the out buffer or NULL if an error occured.
40

E
Emmanuel Leblond 已提交
41 42
  This function convert a string into lowercase.
  At most len - 1 elements of the input string will be converted.
43 44
 */
/*--------------------------------------------------------------------------*/
E
Emmanuel Leblond 已提交
45
static const char * strlwc(const char * in, char *out, unsigned len)
46
{
E
Emmanuel Leblond 已提交
47
    unsigned i ;
48

E
Emmanuel Leblond 已提交
49
    if (in==NULL || out == NULL || len==0) return NULL ;
50
    i=0 ;
E
Emmanuel Leblond 已提交
51 52
    while (in[i] != '\0' && i < len-1) {
        out[i] = (char)tolower((int)in[i]);
53 54
        i++ ;
    }
E
Emmanuel Leblond 已提交
55 56
    out[i] = '\0';
    return out ;
57 58 59 60
}

/*-------------------------------------------------------------------------*/
/**
61 62 63
  @brief    Duplicate a string
  @param    s String to duplicate
  @return   Pointer to a newly allocated string, to be freed with free()
E
Emmanuel Leblond 已提交
64

65 66
  This is a replacement for strdup(). This implementation is provided
  for systems that do not have it.
E
Emmanuel Leblond 已提交
67 68
 */
/*--------------------------------------------------------------------------*/
69
static char * xstrdup(const char * s)
E
Emmanuel Leblond 已提交
70
{
71 72 73 74 75 76 77 78 79 80 81
    char * t ;
    size_t len ;
    if (!s)
        return NULL ;

    len = strlen(s) + 1 ;
    t = (char*) malloc(len) ;
    if (t) {
        memcpy(t, s, len) ;
    }
    return t ;
E
Emmanuel Leblond 已提交
82
}
83

E
Emmanuel Leblond 已提交
84 85 86 87 88
/*-------------------------------------------------------------------------*/
/**
  @brief    Remove blanks at the beginning and the end of a string.
  @param    str  String to parse and alter.
  @return   unsigned New size of the string.
89 90
 */
/*--------------------------------------------------------------------------*/
E
Emmanuel Leblond 已提交
91
unsigned strstrip(char * s)
92
{
E
Emmanuel Leblond 已提交
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
    char *last = NULL ;
    char *dest = s;

    if (s==NULL) return 0;

    last = s + strlen(s);
    while (isspace((int)*s) && *s) s++;
    while (last > s) {
        if (!isspace((int)*(last-1)))
            break ;
        last -- ;
    }
    *last = (char)0;

    memmove(dest,s,last - s + 1);
    return last - s;
109 110
}

111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
/*-------------------------------------------------------------------------*/
/**
  @brief    Default error callback for iniparser: wraps `fprintf(stderr, ...)`.
 */
/*--------------------------------------------------------------------------*/
static int default_error_callback(const char *format, ...)
{
  int ret;
  va_list argptr;
  va_start(argptr, format);
  ret = vfprintf(stderr, format, argptr);
  va_end(argptr);
  return ret;
}

static int (*iniparser_error_callback)(const char*, ...) = default_error_callback;

/*-------------------------------------------------------------------------*/
/**
  @brief    Configure a function to receive the error messages.
  @param    errback  Function to call.

  By default, the error will be printed on stderr. If a null pointer is passed
  as errback the error callback will be switched back to default.
 */
/*--------------------------------------------------------------------------*/
void iniparser_set_error_callback(int (*errback)(const char *, ...))
{
  if (errback) {
    iniparser_error_callback = errback;
  } else {
    iniparser_error_callback = default_error_callback;
  }
}

146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
/*-------------------------------------------------------------------------*/
/**
  @brief    Get number of sections in a dictionary
  @param    d   Dictionary to examine
  @return   int Number of sections found in dictionary

  This function returns the number of sections found in a dictionary.
  The test to recognize sections is done on the string stored in the
  dictionary: a section name is given as "section" whereas a key is
  stored as "section:key", thus the test looks for entries that do not
  contain a colon.

  This clearly fails in the case a section name contains a colon, but
  this should simply be avoided.

  This function returns -1 in case of error.
 */
/*--------------------------------------------------------------------------*/
E
Emmanuel Leblond 已提交
164
int iniparser_getnsec(const dictionary * d)
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
{
    int i ;
    int nsec ;

    if (d==NULL) return -1 ;
    nsec=0 ;
    for (i=0 ; i<d->size ; i++) {
        if (d->key[i]==NULL)
            continue ;
        if (strchr(d->key[i], ':')==NULL) {
            nsec ++ ;
        }
    }
    return nsec ;
}

/*-------------------------------------------------------------------------*/
/**
  @brief    Get name for section n in a dictionary.
  @param    d   Dictionary to examine
  @param    n   Section number (from 0 to nsec-1).
  @return   Pointer to char string

  This function locates the n-th section in a dictionary and returns
  its name as a pointer to a string statically allocated inside the
  dictionary. Do not free or modify the returned string!

  This function returns NULL in case of error.
 */
/*--------------------------------------------------------------------------*/
E
Emmanuel Leblond 已提交
195
const char * iniparser_getsecname(const dictionary * d, int n)
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
{
    int i ;
    int foundsec ;

    if (d==NULL || n<0) return NULL ;
    foundsec=0 ;
    for (i=0 ; i<d->size ; i++) {
        if (d->key[i]==NULL)
            continue ;
        if (strchr(d->key[i], ':')==NULL) {
            foundsec++ ;
            if (foundsec>n)
                break ;
        }
    }
    if (foundsec<=n) {
        return NULL ;
    }
    return d->key[i] ;
}

/*-------------------------------------------------------------------------*/
/**
  @brief    Dump a dictionary to an opened file pointer.
  @param    d   Dictionary to dump.
  @param    f   Opened file pointer to dump to.
  @return   void

  This function prints out the contents of a dictionary, one element by
  line, onto the provided file pointer. It is OK to specify @c stderr
  or @c stdout as output files. This function is meant for debugging
  purposes mostly.
 */
/*--------------------------------------------------------------------------*/
E
Emmanuel Leblond 已提交
230
void iniparser_dump(const dictionary * d, FILE * f)
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
{
    int     i ;

    if (d==NULL || f==NULL) return ;
    for (i=0 ; i<d->size ; i++) {
        if (d->key[i]==NULL)
            continue ;
        if (d->val[i]!=NULL) {
            fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
        } else {
            fprintf(f, "[%s]=UNDEF\n", d->key[i]);
        }
    }
    return ;
}

/*-------------------------------------------------------------------------*/
/**
  @brief    Save a dictionary to a loadable ini file
  @param    d   Dictionary to dump
  @param    f   Opened file pointer to dump to
  @return   void

  This function dumps a given dictionary into a loadable ini file.
  It is Ok to specify @c stderr or @c stdout as output files.
 */
/*--------------------------------------------------------------------------*/
E
Emmanuel Leblond 已提交
258
void iniparser_dump_ini(const dictionary * d, FILE * f)
259
{
E
Emmanuel Leblond 已提交
260 261 262
    int          i ;
    int          nsec ;
    const char * secname ;
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277

    if (d==NULL || f==NULL) return ;

    nsec = iniparser_getnsec(d);
    if (nsec<1) {
        /* No section in file: dump all keys as they are */
        for (i=0 ; i<d->size ; i++) {
            if (d->key[i]==NULL)
                continue ;
            fprintf(f, "%s = %s\n", d->key[i], d->val[i]);
        }
        return ;
    }
    for (i=0 ; i<nsec ; i++) {
        secname = iniparser_getsecname(d, i) ;
E
Emmanuel Leblond 已提交
278
        iniparser_dumpsection_ini(d, secname, f);
279 280 281 282 283
    }
    fprintf(f, "\n");
    return ;
}

284 285 286 287 288 289 290 291 292 293 294 295
/*-------------------------------------------------------------------------*/
/**
  @brief    Save a dictionary section to a loadable ini file
  @param    d   Dictionary to dump
  @param    s   Section name of dictionary to dump
  @param    f   Opened file pointer to dump to
  @return   void

  This function dumps a given section of a given dictionary into a loadable ini
  file.  It is Ok to specify @c stderr or @c stdout as output files.
 */
/*--------------------------------------------------------------------------*/
E
Emmanuel Leblond 已提交
296
void iniparser_dumpsection_ini(const dictionary * d, const char * s, FILE * f)
297 298
{
    int     j ;
E
Emmanuel Leblond 已提交
299 300
    char    keym[ASCIILINESZ+1];
    int     seclen ;
301 302 303 304

    if (d==NULL || f==NULL) return ;
    if (! iniparser_find_entry(d, s)) return ;

E
Emmanuel Leblond 已提交
305
    seclen  = (int)strlen(s);
306
    fprintf(f, "\n[%s]\n", s);
E
Emmanuel Leblond 已提交
307
    sprintf(keym, "%s:", s);
308 309 310
    for (j=0 ; j<d->size ; j++) {
        if (d->key[j]==NULL)
            continue ;
E
Emmanuel Leblond 已提交
311
        if (!strncmp(d->key[j], keym, seclen+1)) {
312 313
            fprintf(f,
                    "%-30s = %s\n",
E
Emmanuel Leblond 已提交
314
                    d->key[j]+seclen+1,
315 316 317 318 319 320 321
                    d->val[j] ? d->val[j] : "");
        }
    }
    fprintf(f, "\n");
    return ;
}

322 323 324 325 326 327 328 329
/*-------------------------------------------------------------------------*/
/**
  @brief    Get the number of keys in a section of a dictionary.
  @param    d   Dictionary to examine
  @param    s   Section name of dictionary to examine
  @return   Number of keys in section
 */
/*--------------------------------------------------------------------------*/
E
Emmanuel Leblond 已提交
330
int iniparser_getsecnkeys(const dictionary * d, const char * s)
331
{
E
Emmanuel Leblond 已提交
332 333
    int     seclen, nkeys ;
    char    keym[ASCIILINESZ+1];
334 335 336 337 338 339 340
    int j ;

    nkeys = 0;

    if (d==NULL) return nkeys;
    if (! iniparser_find_entry(d, s)) return nkeys;

E
Emmanuel Leblond 已提交
341
    seclen  = (int)strlen(s);
D
dobragab 已提交
342 343
    strlwc(s, keym, sizeof(keym));
    keym[seclen] = ':';
344 345 346 347

    for (j=0 ; j<d->size ; j++) {
        if (d->key[j]==NULL)
            continue ;
E
Emmanuel Leblond 已提交
348
        if (!strncmp(d->key[j], keym, seclen+1))
349 350
            nkeys++;
    }
E
Emmanuel Leblond 已提交
351

352 353 354 355 356 357 358
    return nkeys;

}

/*-------------------------------------------------------------------------*/
/**
  @brief    Get the number of keys in a section of a dictionary.
359 360 361 362
  @param    d    Dictionary to examine
  @param    s    Section name of dictionary to examine
  @param    keys Already allocated array to store the keys in
  @return   The pointer passed as `keys` argument or NULL in case of error
363 364

  This function queries a dictionary and finds all keys in a given section.
365 366 367
  The keys argument should be an array of pointers which size has been
  determined by calling `iniparser_getsecnkeys` function prior to this one.

368 369 370 371
  Each pointer in the returned char pointer-to-pointer is pointing to
  a string allocated in the dictionary; do not free or modify them.
 */
/*--------------------------------------------------------------------------*/
372
const char ** iniparser_getseckeys(const dictionary * d, const char * s, const char ** keys)
373
{
374 375
    int i, j, seclen ;
    char keym[ASCIILINESZ+1];
376

377 378
    if (d==NULL || keys==NULL) return NULL;
    if (! iniparser_find_entry(d, s)) return NULL;
379

E
Emmanuel Leblond 已提交
380
    seclen  = (int)strlen(s);
D
dobragab 已提交
381 382
    strlwc(s, keym, sizeof(keym));
    keym[seclen] = ':';
A
Andrey Mazo 已提交
383

384 385 386 387 388
    i = 0;

    for (j=0 ; j<d->size ; j++) {
        if (d->key[j]==NULL)
            continue ;
E
Emmanuel Leblond 已提交
389
        if (!strncmp(d->key[j], keym, seclen+1)) {
390 391 392 393
            keys[i] = d->key[j];
            i++;
        }
    }
E
Emmanuel Leblond 已提交
394

395 396 397
    return keys;
}

398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
/*-------------------------------------------------------------------------*/
/**
  @brief    Get the string associated to a key
  @param    d       Dictionary to search
  @param    key     Key string to look for
  @param    def     Default value to return if key not found.
  @return   pointer to statically allocated character string

  This function queries a dictionary for a key. A key as read from an
  ini file is given as "section:key". If the key cannot be found,
  the pointer passed as 'def' is returned.
  The returned char pointer is pointing to a string allocated in
  the dictionary, do not free or modify it.
 */
/*--------------------------------------------------------------------------*/
E
Emmanuel Leblond 已提交
413
const char * iniparser_getstring(const dictionary * d, const char * key, const char * def)
414
{
E
Emmanuel Leblond 已提交
415 416 417
    const char * lc_key ;
    const char * sval ;
    char tmp_str[ASCIILINESZ+1];
418 419 420 421

    if (d==NULL || key==NULL)
        return def ;

E
Emmanuel Leblond 已提交
422
    lc_key = strlwc(key, tmp_str, sizeof(tmp_str));
423 424 425 426 427 428
    sval = dictionary_get(d, lc_key, def);
    return sval ;
}

/*-------------------------------------------------------------------------*/
/**
429
  @brief    Get the string associated to a key, convert to an long int
430 431 432
  @param    d Dictionary to search
  @param    key Key string to look for
  @param    notfound Value to return in case of error
433
  @return   long integer
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453

  This function queries a dictionary for a key. A key as read from an
  ini file is given as "section:key". If the key cannot be found,
  the notfound value is returned.

  Supported values for integers include the usual C notation
  so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
  are supported. Examples:

  "42"      ->  42
  "042"     ->  34 (octal -> decimal)
  "0x42"    ->  66 (hexa  -> decimal)

  Warning: the conversion may overflow in various ways. Conversion is
  totally outsourced to strtol(), see the associated man page for overflow
  handling.

  Credits: Thanks to A. Becker for suggesting strtol()
 */
/*--------------------------------------------------------------------------*/
454
long int iniparser_getlongint(const dictionary * d, const char * key, long int notfound)
455
{
E
Emmanuel Leblond 已提交
456
    const char * str ;
457 458 459

    str = iniparser_getstring(d, key, INI_INVALID_KEY);
    if (str==INI_INVALID_KEY) return notfound ;
460
    return strtol(str, NULL, 0);
461 462
}

463

L
linghuazaii 已提交
464 465
/*-------------------------------------------------------------------------*/
/**
466
  @brief    Get the string associated to a key, convert to an int
L
linghuazaii 已提交
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
  @param    d Dictionary to search
  @param    key Key string to look for
  @param    notfound Value to return in case of error
  @return   integer

  This function queries a dictionary for a key. A key as read from an
  ini file is given as "section:key". If the key cannot be found,
  the notfound value is returned.

  Supported values for integers include the usual C notation
  so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
  are supported. Examples:

  "42"      ->  42
  "042"     ->  34 (octal -> decimal)
  "0x42"    ->  66 (hexa  -> decimal)

  Warning: the conversion may overflow in various ways. Conversion is
  totally outsourced to strtol(), see the associated man page for overflow
  handling.
487 488

  Credits: Thanks to A. Becker for suggesting strtol()
L
linghuazaii 已提交
489 490
 */
/*--------------------------------------------------------------------------*/
491
int iniparser_getint(const dictionary * d, const char * key, int notfound)
L
linghuazaii 已提交
492
{
493
    return (int)iniparser_getlongint(d, key, notfound);
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
}

/*-------------------------------------------------------------------------*/
/**
  @brief    Get the string associated to a key, convert to a double
  @param    d Dictionary to search
  @param    key Key string to look for
  @param    notfound Value to return in case of error
  @return   double

  This function queries a dictionary for a key. A key as read from an
  ini file is given as "section:key". If the key cannot be found,
  the notfound value is returned.
 */
/*--------------------------------------------------------------------------*/
E
Emmanuel Leblond 已提交
509
double iniparser_getdouble(const dictionary * d, const char * key, double notfound)
510
{
E
Emmanuel Leblond 已提交
511
    const char * str ;
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549

    str = iniparser_getstring(d, key, INI_INVALID_KEY);
    if (str==INI_INVALID_KEY) return notfound ;
    return atof(str);
}

/*-------------------------------------------------------------------------*/
/**
  @brief    Get the string associated to a key, convert to a boolean
  @param    d Dictionary to search
  @param    key Key string to look for
  @param    notfound Value to return in case of error
  @return   integer

  This function queries a dictionary for a key. A key as read from an
  ini file is given as "section:key". If the key cannot be found,
  the notfound value is returned.

  A true boolean is found if one of the following is matched:

  - A string starting with 'y'
  - A string starting with 'Y'
  - A string starting with 't'
  - A string starting with 'T'
  - A string starting with '1'

  A false boolean is found if one of the following is matched:

  - A string starting with 'n'
  - A string starting with 'N'
  - A string starting with 'f'
  - A string starting with 'F'
  - A string starting with '0'

  The notfound value returned if no boolean is identified, does not
  necessarily have to be 0 or 1.
 */
/*--------------------------------------------------------------------------*/
E
Emmanuel Leblond 已提交
550
int iniparser_getboolean(const dictionary * d, const char * key, int notfound)
551
{
E
Emmanuel Leblond 已提交
552 553
    int          ret ;
    const char * c ;
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578

    c = iniparser_getstring(d, key, INI_INVALID_KEY);
    if (c==INI_INVALID_KEY) return notfound ;
    if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
        ret = 1 ;
    } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {
        ret = 0 ;
    } else {
        ret = notfound ;
    }
    return ret;
}

/*-------------------------------------------------------------------------*/
/**
  @brief    Finds out if a given entry exists in a dictionary
  @param    ini     Dictionary to search
  @param    entry   Name of the entry to look for
  @return   integer 1 if entry exists, 0 otherwise

  Finds out if a given entry exists in the dictionary. Since sections
  are stored as keys with NULL associated values, this is the only way
  of querying for the presence of sections in a dictionary.
 */
/*--------------------------------------------------------------------------*/
E
Emmanuel Leblond 已提交
579
int iniparser_find_entry(const dictionary * ini, const char * entry)
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
{
    int found=0 ;
    if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) {
        found = 1 ;
    }
    return found ;
}

/*-------------------------------------------------------------------------*/
/**
  @brief    Set an entry in a dictionary.
  @param    ini     Dictionary to modify.
  @param    entry   Entry to modify (entry name)
  @param    val     New value to associate to the entry.
  @return   int 0 if Ok, -1 otherwise.

  If the given entry can be found in the dictionary, it is modified to
E
Emmanuel Leblond 已提交
597
  contain the provided value. If it cannot be found, the entry is created.
598 599 600
  It is Ok to set val to NULL.
 */
/*--------------------------------------------------------------------------*/
601
int iniparser_set(dictionary * ini, const char * entry, const char * val)
602
{
E
Emmanuel Leblond 已提交
603 604
    char tmp_str[ASCIILINESZ+1];
    return dictionary_set(ini, strlwc(entry, tmp_str, sizeof(tmp_str)), val) ;
605 606 607 608 609 610 611 612 613 614 615 616
}

/*-------------------------------------------------------------------------*/
/**
  @brief    Delete an entry in a dictionary
  @param    ini     Dictionary to modify
  @param    entry   Entry to delete (entry name)
  @return   void

  If the given entry can be found, it is deleted from the dictionary.
 */
/*--------------------------------------------------------------------------*/
617
void iniparser_unset(dictionary * ini, const char * entry)
618
{
E
Emmanuel Leblond 已提交
619 620
    char tmp_str[ASCIILINESZ+1];
    dictionary_unset(ini, strlwc(entry, tmp_str, sizeof(tmp_str)));
621 622 623 624
}

/*-------------------------------------------------------------------------*/
/**
625
  @brief    Load a single line from an INI file
626 627 628 629 630 631 632 633
  @param    input_line  Input line, may be concatenated multi-line input
  @param    section     Output space to store section
  @param    key         Output space to store key
  @param    value       Output space to store value
  @return   line_status value
 */
/*--------------------------------------------------------------------------*/
static line_status iniparser_line(
634
    const char * input_line,
E
Emmanuel Leblond 已提交
635 636 637
    char * section,
    char * key,
    char * value)
A
Andrey Mazo 已提交
638
{
639
    line_status sta ;
E
Emmanuel Leblond 已提交
640 641
    char * line = NULL;
    size_t      len ;
642

643
    line = xstrdup(input_line);
E
Emmanuel Leblond 已提交
644
    len = strstrip(line);
645

646 647 648 649
    sta = LINE_UNPROCESSED ;
    if (len<1) {
        /* Empty line */
        sta = LINE_EMPTY ;
650
    } else if (line[0]=='#' || line[0]==';') {
651
        /* Comment line */
A
Andrey Mazo 已提交
652
        sta = LINE_COMMENT ;
653 654
    } else if (line[0]=='[' && line[len-1]==']') {
        /* Section name */
E
Emmanuel Leblond 已提交
655 656 657
        sscanf(line, "[%[^]]", section);
        strstrip(section);
        strlwc(section, section, len);
658
        sta = LINE_SECTION ;
E
Emmanuel Leblond 已提交
659
    } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
J
Jesper Jensen 已提交
660 661
           ||  sscanf (line, "%[^=] = '%[^\']'",   key, value) == 2) {
        /* Usual key=value with quotes, with or without comments */
662
        strstrip(key);
E
Emmanuel Leblond 已提交
663
        strlwc(key, key, len);
J
Jesper Jensen 已提交
664
        /* Don't strip spaces from values surrounded with quotes */
E
Emmanuel Leblond 已提交
665 666 667 668 669 670
        sta = LINE_VALUE ;
    } else if (sscanf (line, "%[^=] = %[^;#]", key, value) == 2) {
        /* Usual key=value without quotes, with or without comments */
        strstrip(key);
        strlwc(key, key, len);
        strstrip(value);
671 672 673 674 675 676 677 678
        /*
         * sscanf cannot handle '' or "" as empty values
         * this is done here
         */
        if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {
            value[0]=0 ;
        }
        sta = LINE_VALUE ;
E
Emmanuel Leblond 已提交
679 680
    } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2
           ||  sscanf(line, "%[^=] %[=]", key, value) == 2) {
681 682 683 684 685 686
        /*
         * Special cases:
         * key=
         * key=;
         * key=#
         */
687
        strstrip(key);
E
Emmanuel Leblond 已提交
688
        strlwc(key, key, len);
689 690 691 692 693 694
        value[0]=0 ;
        sta = LINE_VALUE ;
    } else {
        /* Generate syntax error */
        sta = LINE_ERROR ;
    }
695

E
Emmanuel Leblond 已提交
696
    free(line);
697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
    return sta ;
}

/*-------------------------------------------------------------------------*/
/**
  @brief    Parse an ini file and return an allocated dictionary object
  @param    ininame Name of the ini file to read.
  @return   Pointer to newly allocated dictionary

  This is the parser for ini files. This function is called, providing
  the name of the file to be read. It returns a dictionary object that
  should not be accessed directly, but through accessor functions
  instead.

  The returned dictionary must be freed using iniparser_freedict().
 */
/*--------------------------------------------------------------------------*/
714
dictionary * iniparser_load(const char * ininame)
715
{
E
Emmanuel Leblond 已提交
716
    FILE * in ;
717 718

    char line    [ASCIILINESZ+1] ;
E
Emmanuel Leblond 已提交
719 720 721 722
    char section [ASCIILINESZ+1] ;
    char key     [ASCIILINESZ+1] ;
    char tmp     [(ASCIILINESZ * 2) + 1] ;
    char val     [ASCIILINESZ+1] ;
723

E
Emmanuel Leblond 已提交
724
    int  last=0 ;
725 726 727
    int  len ;
    int  lineno=0 ;
    int  errs=0;
728
    int  mem_err=0;
729

E
Emmanuel Leblond 已提交
730
    dictionary * dict ;
731 732

    if ((in=fopen(ininame, "r"))==NULL) {
733
        iniparser_error_callback("iniparser: cannot open %s\n", ininame);
E
Emmanuel Leblond 已提交
734
        return NULL ;
735 736 737 738
    }

    dict = dictionary_new(0) ;
    if (!dict) {
E
Emmanuel Leblond 已提交
739 740
        fclose(in);
        return NULL ;
741 742 743
    }

    memset(line,    0, ASCIILINESZ);
E
Emmanuel Leblond 已提交
744 745 746 747
    memset(section, 0, ASCIILINESZ);
    memset(key,     0, ASCIILINESZ);
    memset(val,     0, ASCIILINESZ);
    last=0 ;
748

E
Emmanuel Leblond 已提交
749
    while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) {
750 751
        lineno++ ;
        len = (int)strlen(line)-1;
752
        if (len<=0)
N
ndevilla 已提交
753
            continue;
754
        /* Safety check against buffer overflows */
755
        if (line[len]!='\n' && !feof(in)) {
756 757 758 759
            iniparser_error_callback(
              "iniparser: input line too long in %s (%d)\n",
              ininame,
              lineno);
E
Emmanuel Leblond 已提交
760 761 762
            dictionary_del(dict);
            fclose(in);
            return NULL ;
763 764 765 766 767 768 769
        }
        /* Get rid of \n and spaces at end of line */
        while ((len>=0) &&
                ((line[len]=='\n') || (isspace(line[len])))) {
            line[len]=0 ;
            len-- ;
        }
770 771 772
        if (len < 0) { /* Line was entirely \n and/or spaces */
            len = 0;
        }
773 774 775
        /* Detect multi-line */
        if (line[len]=='\\') {
            /* Multi-line value */
E
Emmanuel Leblond 已提交
776
            last=len ;
777
            continue ;
E
Emmanuel Leblond 已提交
778 779
        } else {
            last=0 ;
780
        }
E
Emmanuel Leblond 已提交
781
        switch (iniparser_line(line, section, key, val)) {
782 783 784 785 786
            case LINE_EMPTY:
            case LINE_COMMENT:
            break ;

            case LINE_SECTION:
787
            mem_err = dictionary_set(dict, section, NULL);
788 789 790
            break ;

            case LINE_VALUE:
E
Emmanuel Leblond 已提交
791
            sprintf(tmp, "%s:%s", section, key);
792
            mem_err = dictionary_set(dict, tmp, val);
793 794 795
            break ;

            case LINE_ERROR:
796 797 798 799 800
            iniparser_error_callback(
              "iniparser: syntax error in %s (%d):\n-> %s\n",
              ininame,
              lineno,
              line);
801 802 803 804 805 806 807
            errs++ ;
            break;

            default:
            break ;
        }
        memset(line, 0, ASCIILINESZ);
E
Emmanuel Leblond 已提交
808
        last=0;
809
        if (mem_err<0) {
810
            iniparser_error_callback("iniparser: memory allocation failure\n");
811 812 813 814 815 816 817
            break ;
        }
    }
    if (errs) {
        dictionary_del(dict);
        dict = NULL ;
    }
E
Emmanuel Leblond 已提交
818
    fclose(in);
819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836
    return dict ;
}

/*-------------------------------------------------------------------------*/
/**
  @brief    Free all memory associated to an ini dictionary
  @param    d Dictionary to free
  @return   void

  Free all memory associated to an ini dictionary.
  It is mandatory to call this function before the dictionary object
  gets out of the current context.
 */
/*--------------------------------------------------------------------------*/
void iniparser_freedict(dictionary * d)
{
    dictionary_del(d);
}