source: thirdparty/SZ/sz/src/iniparser.c @ 2c47b73

Revision 2c47b73, 23.2 KB checked in by Hal Finkel <hfinkel@…>, 6 years ago (diff)

more work on adding SZ (latest version)

  • Property mode set to 100644
Line 
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 */
23typedef 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/*--------------------------------------------------------------------------*/
44static 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/*--------------------------------------------------------------------------*/
74static 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    strncpy(l, s, ASCIILINESZ);
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/*--------------------------------------------------------------------------*/
112int 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/*--------------------------------------------------------------------------*/
143char * 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/*--------------------------------------------------------------------------*/
178void 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/*--------------------------------------------------------------------------*/
206void 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/*--------------------------------------------------------------------------*/
244void 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/*--------------------------------------------------------------------------*/
278int 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/*--------------------------------------------------------------------------*/
317char ** 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/*--------------------------------------------------------------------------*/
368char * 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/*--------------------------------------------------------------------------*/
408int 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 long
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   long
424
425  Credits: This function bases completely on int iniparser_getint and was
426  slightly modified to return long instead of int.
427 */
428/*--------------------------------------------------------------------------*/
429long iniparser_getlint(dictionary * d, const char * key, int notfound)
430{
431    char    *   str ;
432
433    str = iniparser_getstring(d, key, INI_INVALID_KEY);
434    if (str==INI_INVALID_KEY) return notfound ;
435    return strtol(str, NULL, 0);
436}
437
438/*-------------------------------------------------------------------------*/
439/**
440  @brief    Get the string associated to a key, convert to a double
441  @param    d Dictionary to search
442  @param    key Key string to look for
443  @param    notfound Value to return in case of error
444  @return   double
445
446  This function queries a dictionary for a key. A key as read from an
447  ini file is given as "section:key". If the key cannot be found,
448  the notfound value is returned.
449 */
450/*--------------------------------------------------------------------------*/
451double iniparser_getdouble(dictionary * d, const char * key, double notfound)
452{
453    char    *   str ;
454
455    str = iniparser_getstring(d, key, INI_INVALID_KEY);
456    if (str==INI_INVALID_KEY) return notfound ;
457    return atof(str);
458}
459
460/*-------------------------------------------------------------------------*/
461/**
462  @brief    Get the string associated to a key, convert to a boolean
463  @param    d Dictionary to search
464  @param    key Key string to look for
465  @param    notfound Value to return in case of error
466  @return   integer
467
468  This function queries a dictionary for a key. A key as read from an
469  ini file is given as "section:key". If the key cannot be found,
470  the notfound value is returned.
471
472  A true boolean is found if one of the following is matched:
473
474  - A string starting with 'y'
475  - A string starting with 'Y'
476  - A string starting with 't'
477  - A string starting with 'T'
478  - A string starting with '1'
479
480  A false boolean is found if one of the following is matched:
481
482  - A string starting with 'n'
483  - A string starting with 'N'
484  - A string starting with 'f'
485  - A string starting with 'F'
486  - A string starting with '0'
487
488  The notfound value returned if no boolean is identified, does not
489  necessarily have to be 0 or 1.
490 */
491/*--------------------------------------------------------------------------*/
492int iniparser_getboolean(dictionary * d, const char * key, int notfound)
493{
494    char    *   c ;
495    int         ret ;
496
497    c = iniparser_getstring(d, key, INI_INVALID_KEY);
498    if (c==INI_INVALID_KEY) return notfound ;
499    if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
500        ret = 1 ;
501    } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {
502        ret = 0 ;
503    } else {
504        ret = notfound ;
505    }
506    return ret;
507}
508
509/*-------------------------------------------------------------------------*/
510/**
511  @brief    Finds out if a given entry exists in a dictionary
512  @param    ini     Dictionary to search
513  @param    entry   Name of the entry to look for
514  @return   integer 1 if entry exists, 0 otherwise
515
516  Finds out if a given entry exists in the dictionary. Since sections
517  are stored as keys with NULL associated values, this is the only way
518  of querying for the presence of sections in a dictionary.
519 */
520/*--------------------------------------------------------------------------*/
521int iniparser_find_entry(
522    dictionary  *   ini,
523    const char  *   entry
524)
525{
526    int found=0 ;
527    if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) {
528        found = 1 ;
529    }
530    return found ;
531}
532
533/*-------------------------------------------------------------------------*/
534/**
535  @brief    Set an entry in a dictionary.
536  @param    ini     Dictionary to modify.
537  @param    entry   Entry to modify (entry name)
538  @param    val     New value to associate to the entry.
539  @return   int 0 if Ok, -1 otherwise.
540
541  If the given entry can be found in the dictionary, it is modified to
542  contain the provided value. If it cannot be found, -1 is returned.
543  It is Ok to set val to NULL.
544 */
545/*--------------------------------------------------------------------------*/
546int iniparser_set(dictionary * ini, const char * entry, const char * val)
547{
548    return dictionary_set(ini, strlwc(entry), val) ;
549}
550
551/*-------------------------------------------------------------------------*/
552/**
553  @brief    Delete an entry in a dictionary
554  @param    ini     Dictionary to modify
555  @param    entry   Entry to delete (entry name)
556  @return   void
557
558  If the given entry can be found, it is deleted from the dictionary.
559 */
560/*--------------------------------------------------------------------------*/
561void iniparser_unset(dictionary * ini, const char * entry)
562{
563    dictionary_unset(ini, strlwc(entry));
564}
565
566/*-------------------------------------------------------------------------*/
567/**
568  @brief    Load a single line from an INI file
569  @param    input_line  Input line, may be concatenated multi-line input
570  @param    section     Output space to store section
571  @param    key         Output space to store key
572  @param    value       Output space to store value
573  @return   line_status value
574 */
575/*--------------------------------------------------------------------------*/
576static line_status iniparser_line(
577    const char * input_line,
578    char * section,
579    char * key,
580    char * value)
581{
582    line_status sta ;
583    char        line[ASCIILINESZ+1];
584    int         len ;
585
586    memset(line, 0, ASCIILINESZ + 1);
587    len = (int)strlen(strstrip(input_line));
588    if (len > ASCIILINESZ)
589        len = ASCIILINESZ;
590    strncpy(line, strstrip(input_line), len);
591    len = (int)strlen(line);
592
593    sta = LINE_UNPROCESSED ;
594    if (len<1) {
595        /* Empty line */
596        sta = LINE_EMPTY ;
597    } else if (line[0]=='#' || line[0]==';') {
598        /* Comment line */
599        sta = LINE_COMMENT ;
600    } else if (line[0]=='[' && line[len-1]==']') {
601        /* Section name */
602        sscanf(line, "[%[^]]", section);
603        strcpy(section, strstrip(section));
604        strcpy(section, strlwc(section));
605        sta = LINE_SECTION ;
606    } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
607           ||  sscanf (line, "%[^=] = '%[^\']'",   key, value) == 2
608           ||  sscanf (line, "%[^=] = %[^;#]",     key, value) == 2) {
609        /* Usual key=value, with or without comments */
610        strcpy(key, strstrip(key));
611        strcpy(key, strlwc(key));
612        strcpy(value, strstrip(value));
613        /*
614         * sscanf cannot handle '' or "" as empty values
615         * this is done here
616         */
617        if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {
618            value[0]=0 ;
619        }
620        sta = LINE_VALUE ;
621    } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2
622           ||  sscanf(line, "%[^=] %[=]", key, value) == 2) {
623        /*
624         * Special cases:
625         * key=
626         * key=;
627         * key=#
628         */
629        strcpy(key, strstrip(key));
630        strcpy(key, strlwc(key));
631        value[0]=0 ;
632        sta = LINE_VALUE ;
633    } else {
634        /* Generate syntax error */
635        sta = LINE_ERROR ;
636        printf("===== > %s   ===> %s\n", input_line, line);
637    }
638    return sta ;
639}
640
641/*-------------------------------------------------------------------------*/
642/**
643  @brief    Parse an ini file and return an allocated dictionary object
644  @param    ininame Name of the ini file to read.
645  @return   Pointer to newly allocated dictionary
646
647  This is the parser for ini files. This function is called, providing
648  the name of the file to be read. It returns a dictionary object that
649  should not be accessed directly, but through accessor functions
650  instead.
651
652  The returned dictionary must be freed using iniparser_freedict().
653 */
654/*--------------------------------------------------------------------------*/
655dictionary * iniparser_load(const char * ininame)
656{
657    FILE * in ;
658
659    char line    [ASCIILINESZ+1] ;
660    char section [ASCIILINESZ+1] ;
661    char key     [ASCIILINESZ+1] ;
662    char tmp     [ASCIILINESZ+1] ;
663    char val     [ASCIILINESZ+1] ;
664
665    int  last=0 ;
666    int  len ;
667    int  lineno=0 ;
668    int  errs=0;
669
670    dictionary * dict ;
671
672    if ((in=fopen(ininame, "r"))==NULL) {
673        fprintf(stderr, "iniparser: cannot open %s\n", ininame);
674        return NULL ;
675    }
676
677    dict = dictionary_new(0) ;
678    if (!dict) {
679        fclose(in);
680        return NULL ;
681    }
682
683    memset(line,    0, ASCIILINESZ);
684    memset(section, 0, ASCIILINESZ);
685    memset(key,     0, ASCIILINESZ);
686    memset(val,     0, ASCIILINESZ);
687    last=0 ;
688
689    while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) {
690        lineno++ ;
691        len = (int)strlen(line)-1;
692        if (len==0)
693            continue;
694        /* Safety check against buffer overflows */
695        if (line[len]!='\n') {
696            fprintf(stderr,
697                    "iniparser: input line too long in %s (%d)\n",
698                    ininame,
699                    lineno);
700            dictionary_del(dict);
701            fclose(in);
702            return NULL ;
703        }
704        /* Get rid of \n and spaces at end of line */
705        while ((len>=0) &&
706                ((line[len]=='\n') || (isspace(line[len])))) {
707            line[len]=0 ;
708            len-- ;
709        }
710        /* Detect multi-line */
711        if (line[len]=='\\') {
712            /* Multi-line value */
713            last=len ;
714            continue ;
715        } else {
716            last=0 ;
717        }
718        switch (iniparser_line(line, section, key, val)) {
719            case LINE_EMPTY:
720            case LINE_COMMENT:
721            break ;
722
723            case LINE_SECTION:
724            errs = dictionary_set(dict, section, NULL);
725            break ;
726
727            case LINE_VALUE:
728            sprintf(tmp, "%s:%s", section, key);
729            errs = dictionary_set(dict, tmp, val) ;
730            break ;
731
732            case LINE_ERROR:
733            fprintf(stderr, "iniparser: syntax error in %s (%d):\n",
734                    ininame,
735                    lineno);
736            fprintf(stderr, "-> %s\n", line);
737            errs++ ;
738            break;
739
740            default:
741            break ;
742        }
743        memset(line, 0, ASCIILINESZ);
744        last=0;
745        if (errs<0) {
746            fprintf(stderr, "iniparser: memory allocation failure\n");
747            break ;
748        }
749    }
750    if (errs) {
751        dictionary_del(dict);
752        dict = NULL ;
753    }
754    fclose(in);
755    return dict ;
756}
757
758/*-------------------------------------------------------------------------*/
759/**
760  @brief    Free all memory associated to an ini dictionary
761  @param    d Dictionary to free
762  @return   void
763
764  Free all memory associated to an ini dictionary.
765  It is mandatory to call this function before the dictionary object
766  gets out of the current context.
767 */
768/*--------------------------------------------------------------------------*/
769void iniparser_freedict(dictionary * d)
770{
771    dictionary_del(d);
772}
773
774/* vim: set ts=4 et sw=4 tw=75 */
Note: See TracBrowser for help on using the repository browser.