iniparser.c 24.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

/*-------------------------------------------------------------------------*/
/**
   @file    iniparser.c
   @author  N. Devillard
   @brief   Parser for ini files.
*/
/*--------------------------------------------------------------------------*/
/*---------------------------- Includes ------------------------------------*/
#include <ctype.h>
#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 ;

/*-------------------------------------------------------------------------*/
/**
34
  @brief    Convert a string to lowercase.
E
Emmanuel Leblond 已提交
35 36 37 38
  @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.
39

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

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

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

64 65
  This is a replacement for strdup(). This implementation is provided
  for systems that do not have it.
E
Emmanuel Leblond 已提交
66 67
 */
/*--------------------------------------------------------------------------*/
68
static char * xstrdup(const char * s)
E
Emmanuel Leblond 已提交
69
{
70 71 72 73 74 75 76 77 78 79 80
    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 已提交
81
}
82

E
Emmanuel Leblond 已提交
83 84 85 86 87
/*-------------------------------------------------------------------------*/
/**
  @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.
88 89
 */
/*--------------------------------------------------------------------------*/
E
Emmanuel Leblond 已提交
90
unsigned strstrip(char * s)
91
{
E
Emmanuel Leblond 已提交
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
    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;
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
}

/*-------------------------------------------------------------------------*/
/**
  @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 已提交
128
int iniparser_getnsec(const dictionary * d)
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
{
    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 已提交
159
const char * iniparser_getsecname(const dictionary * d, int n)
160 161 162 163 164 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
{
    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 已提交
194
void iniparser_dump(const dictionary * d, FILE * f)
195 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
{
    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 已提交
222
void iniparser_dump_ini(const dictionary * d, FILE * f)
223
{
E
Emmanuel Leblond 已提交
224 225 226
    int          i ;
    int          nsec ;
    const char * secname ;
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241

    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 已提交
242
        iniparser_dumpsection_ini(d, secname, f);
243 244 245 246 247
    }
    fprintf(f, "\n");
    return ;
}

248 249 250 251 252 253 254 255 256 257 258 259
/*-------------------------------------------------------------------------*/
/**
  @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 已提交
260
void iniparser_dumpsection_ini(const dictionary * d, const char * s, FILE * f)
261 262
{
    int     j ;
E
Emmanuel Leblond 已提交
263 264
    char    keym[ASCIILINESZ+1];
    int     seclen ;
265 266 267 268

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

E
Emmanuel Leblond 已提交
269
    seclen  = (int)strlen(s);
270
    fprintf(f, "\n[%s]\n", s);
E
Emmanuel Leblond 已提交
271
    sprintf(keym, "%s:", s);
272 273 274
    for (j=0 ; j<d->size ; j++) {
        if (d->key[j]==NULL)
            continue ;
E
Emmanuel Leblond 已提交
275
        if (!strncmp(d->key[j], keym, seclen+1)) {
276 277
            fprintf(f,
                    "%-30s = %s\n",
E
Emmanuel Leblond 已提交
278
                    d->key[j]+seclen+1,
279 280 281 282 283 284 285
                    d->val[j] ? d->val[j] : "");
        }
    }
    fprintf(f, "\n");
    return ;
}

286 287 288 289 290 291 292 293
/*-------------------------------------------------------------------------*/
/**
  @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 已提交
294
int iniparser_getsecnkeys(const dictionary * d, const char * s)
295
{
E
Emmanuel Leblond 已提交
296 297
    int     seclen, nkeys ;
    char    keym[ASCIILINESZ+1];
298 299 300 301 302 303 304
    int j ;

    nkeys = 0;

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

E
Emmanuel Leblond 已提交
305
    seclen  = (int)strlen(s);
D
dobragab 已提交
306 307
    strlwc(s, keym, sizeof(keym));
    keym[seclen] = ':';
308 309 310 311

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

316 317 318 319 320 321 322
    return nkeys;

}

/*-------------------------------------------------------------------------*/
/**
  @brief    Get the number of keys in a section of a dictionary.
323 324 325 326
  @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
327 328

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

332 333 334 335
  Each pointer in the returned char pointer-to-pointer is pointing to
  a string allocated in the dictionary; do not free or modify them.
 */
/*--------------------------------------------------------------------------*/
336
const char ** iniparser_getseckeys(const dictionary * d, const char * s, const char ** keys)
337
{
338 339
    int i, j, seclen ;
    char keym[ASCIILINESZ+1];
340

341 342
    if (d==NULL || keys==NULL) return NULL;
    if (! iniparser_find_entry(d, s)) return NULL;
343

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

348 349 350 351 352
    i = 0;

    for (j=0 ; j<d->size ; j++) {
        if (d->key[j]==NULL)
            continue ;
E
Emmanuel Leblond 已提交
353
        if (!strncmp(d->key[j], keym, seclen+1)) {
354 355 356 357
            keys[i] = d->key[j];
            i++;
        }
    }
E
Emmanuel Leblond 已提交
358

359 360 361
    return keys;
}

362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
/*-------------------------------------------------------------------------*/
/**
  @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 已提交
377
const char * iniparser_getstring(const dictionary * d, const char * key, const char * def)
378
{
E
Emmanuel Leblond 已提交
379 380 381
    const char * lc_key ;
    const char * sval ;
    char tmp_str[ASCIILINESZ+1];
382 383 384 385

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

E
Emmanuel Leblond 已提交
386
    lc_key = strlwc(key, tmp_str, sizeof(tmp_str));
387 388 389 390 391 392
    sval = dictionary_get(d, lc_key, def);
    return sval ;
}

/*-------------------------------------------------------------------------*/
/**
393
  @brief    Get the string associated to a key, convert to an long int
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
  @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.
 */
/*--------------------------------------------------------------------------*/
416
long int iniparser_getlongint(const dictionary * d, const char * key, long int notfound)
417
{
E
Emmanuel Leblond 已提交
418
    const char * str ;
419 420 421

    str = iniparser_getstring(d, key, INI_INVALID_KEY);
    if (str==INI_INVALID_KEY) return notfound ;
422
    return strtol(str, NULL, 0);
423 424
}

425

L
linghuazaii 已提交
426 427
/*-------------------------------------------------------------------------*/
/**
428
  @brief    Get the string associated to a key, convert to an int
L
linghuazaii 已提交
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
  @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.
449 450

  Credits: Thanks to A. Becker for suggesting strtol()
L
linghuazaii 已提交
451 452
 */
/*--------------------------------------------------------------------------*/
453
int iniparser_getint(const dictionary * d, const char * key, int notfound)
L
linghuazaii 已提交
454
{
455
    return (int)iniparser_getlongint(d, key, notfound);
L
linghuazaii 已提交
456 457
}

458 459 460 461 462 463 464 465 466 467 468 469 470
/*-------------------------------------------------------------------------*/
/**
  @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 已提交
471
double iniparser_getdouble(const dictionary * d, const char * key, double notfound)
472
{
E
Emmanuel Leblond 已提交
473
    const char * str ;
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511

    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 已提交
512
int iniparser_getboolean(const dictionary * d, const char * key, int notfound)
513
{
E
Emmanuel Leblond 已提交
514 515
    int          ret ;
    const char * c ;
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

    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 已提交
541
int iniparser_find_entry(const dictionary * ini, const char * entry)
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558
{
    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 已提交
559
  contain the provided value. If it cannot be found, the entry is created.
560 561 562
  It is Ok to set val to NULL.
 */
/*--------------------------------------------------------------------------*/
563
int iniparser_set(dictionary * ini, const char * entry, const char * val)
564
{
E
Emmanuel Leblond 已提交
565 566
    char tmp_str[ASCIILINESZ+1];
    return dictionary_set(ini, strlwc(entry, tmp_str, sizeof(tmp_str)), val) ;
567 568 569 570 571 572 573 574 575 576 577 578
}

/*-------------------------------------------------------------------------*/
/**
  @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.
 */
/*--------------------------------------------------------------------------*/
579
void iniparser_unset(dictionary * ini, const char * entry)
580
{
E
Emmanuel Leblond 已提交
581 582
    char tmp_str[ASCIILINESZ+1];
    dictionary_unset(ini, strlwc(entry, tmp_str, sizeof(tmp_str)));
583 584 585 586
}

/*-------------------------------------------------------------------------*/
/**
587
  @brief    Load a single line from an INI file
588 589 590 591 592 593 594 595
  @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(
596
    const char * input_line,
E
Emmanuel Leblond 已提交
597 598 599
    char * section,
    char * key,
    char * value)
A
Andrey Mazo 已提交
600
{
601
    line_status sta ;
E
Emmanuel Leblond 已提交
602 603
    char * line = NULL;
    size_t      len ;
604

605
    line = xstrdup(input_line);
E
Emmanuel Leblond 已提交
606
    len = strstrip(line);
607

608 609 610 611
    sta = LINE_UNPROCESSED ;
    if (len<1) {
        /* Empty line */
        sta = LINE_EMPTY ;
612
    } else if (line[0]=='#' || line[0]==';') {
613
        /* Comment line */
A
Andrey Mazo 已提交
614
        sta = LINE_COMMENT ;
615 616
    } else if (line[0]=='[' && line[len-1]==']') {
        /* Section name */
E
Emmanuel Leblond 已提交
617 618 619
        sscanf(line, "[%[^]]", section);
        strstrip(section);
        strlwc(section, section, len);
620
        sta = LINE_SECTION ;
E
Emmanuel Leblond 已提交
621
    } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
J
Jesper Jensen 已提交
622 623
           ||  sscanf (line, "%[^=] = '%[^\']'",   key, value) == 2) {
        /* Usual key=value with quotes, with or without comments */
624
        strstrip(key);
E
Emmanuel Leblond 已提交
625
        strlwc(key, key, len);
J
Jesper Jensen 已提交
626
        /* Don't strip spaces from values surrounded with quotes */
E
Emmanuel Leblond 已提交
627 628 629 630 631 632
        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);
633 634 635 636 637 638 639 640
        /*
         * 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 已提交
641 642
    } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2
           ||  sscanf(line, "%[^=] %[=]", key, value) == 2) {
643 644 645 646 647 648
        /*
         * Special cases:
         * key=
         * key=;
         * key=#
         */
649
        strstrip(key);
E
Emmanuel Leblond 已提交
650
        strlwc(key, key, len);
651 652 653 654 655 656
        value[0]=0 ;
        sta = LINE_VALUE ;
    } else {
        /* Generate syntax error */
        sta = LINE_ERROR ;
    }
657

E
Emmanuel Leblond 已提交
658
    free(line);
659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675
    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().
 */
/*--------------------------------------------------------------------------*/
676
dictionary * iniparser_load(const char * ininame)
677
{
E
Emmanuel Leblond 已提交
678
    FILE * in ;
679 680

    char line    [ASCIILINESZ+1] ;
E
Emmanuel Leblond 已提交
681 682 683 684
    char section [ASCIILINESZ+1] ;
    char key     [ASCIILINESZ+1] ;
    char tmp     [(ASCIILINESZ * 2) + 1] ;
    char val     [ASCIILINESZ+1] ;
685

E
Emmanuel Leblond 已提交
686
    int  last=0 ;
687 688 689 690
    int  len ;
    int  lineno=0 ;
    int  errs=0;

E
Emmanuel Leblond 已提交
691
    dictionary * dict ;
692 693 694

    if ((in=fopen(ininame, "r"))==NULL) {
        fprintf(stderr, "iniparser: cannot open %s\n", ininame);
E
Emmanuel Leblond 已提交
695
        return NULL ;
696 697 698 699
    }

    dict = dictionary_new(0) ;
    if (!dict) {
E
Emmanuel Leblond 已提交
700 701
        fclose(in);
        return NULL ;
702 703 704
    }

    memset(line,    0, ASCIILINESZ);
E
Emmanuel Leblond 已提交
705 706 707 708
    memset(section, 0, ASCIILINESZ);
    memset(key,     0, ASCIILINESZ);
    memset(val,     0, ASCIILINESZ);
    last=0 ;
709

E
Emmanuel Leblond 已提交
710
    while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) {
711 712
        lineno++ ;
        len = (int)strlen(line)-1;
713
        if (len<=0)
N
ndevilla 已提交
714
            continue;
715
        /* Safety check against buffer overflows */
716
        if (line[len]!='\n' && !feof(in)) {
717 718 719 720
            fprintf(stderr,
                    "iniparser: input line too long in %s (%d)\n",
                    ininame,
                    lineno);
E
Emmanuel Leblond 已提交
721 722 723
            dictionary_del(dict);
            fclose(in);
            return NULL ;
724 725 726 727 728 729 730
        }
        /* Get rid of \n and spaces at end of line */
        while ((len>=0) &&
                ((line[len]=='\n') || (isspace(line[len])))) {
            line[len]=0 ;
            len-- ;
        }
731 732 733
        if (len < 0) { /* Line was entirely \n and/or spaces */
            len = 0;
        }
734 735 736
        /* Detect multi-line */
        if (line[len]=='\\') {
            /* Multi-line value */
E
Emmanuel Leblond 已提交
737
            last=len ;
738
            continue ;
E
Emmanuel Leblond 已提交
739 740
        } else {
            last=0 ;
741
        }
E
Emmanuel Leblond 已提交
742
        switch (iniparser_line(line, section, key, val)) {
743 744 745 746 747
            case LINE_EMPTY:
            case LINE_COMMENT:
            break ;

            case LINE_SECTION:
E
Emmanuel Leblond 已提交
748
            errs = dictionary_set(dict, section, NULL);
749 750 751
            break ;

            case LINE_VALUE:
E
Emmanuel Leblond 已提交
752 753
            sprintf(tmp, "%s:%s", section, key);
            errs = dictionary_set(dict, tmp, val) ;
754 755 756 757 758 759
            break ;

            case LINE_ERROR:
            fprintf(stderr, "iniparser: syntax error in %s (%d):\n",
                    ininame,
                    lineno);
E
Emmanuel Leblond 已提交
760
            fprintf(stderr, "-> %s\n", line);
761 762 763 764 765 766 767
            errs++ ;
            break;

            default:
            break ;
        }
        memset(line, 0, ASCIILINESZ);
E
Emmanuel Leblond 已提交
768
        last=0;
769 770 771 772 773 774 775 776 777
        if (errs<0) {
            fprintf(stderr, "iniparser: memory allocation failure\n");
            break ;
        }
    }
    if (errs) {
        dictionary_del(dict);
        dict = NULL ;
    }
E
Emmanuel Leblond 已提交
778
    fclose(in);
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796
    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);
}