blob: 30980988c2a7574fb254997db41e93609d61801b [file] [log] [blame]
Zhe Wang8ae3b112019-06-18 13:23:54 +08001/*
2 * Copyright (C) 2017 Amlogic Corporation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*-------------------------------------------------------------------------*/
18/**
19 @file iniparser.c
20 @author N. Devillard
21 @brief Parser for ini files.
22*/
23/*--------------------------------------------------------------------------*/
24/*---------------------------- Includes ------------------------------------*/
25#include <ctype.h>
26#include <stdarg.h>
27#include "iniparser.h"
haiyang.ren004fee82024-06-26 03:21:54 +000028#include "aml_malloc_debug.h"
Zhe Wang8ae3b112019-06-18 13:23:54 +080029
30/*---------------------------- Defines -------------------------------------*/
cheng tong5ae92692020-08-28 14:13:39 +080031#define ASCIILINESZ (8192)
Zhe Wang8ae3b112019-06-18 13:23:54 +080032#define INI_INVALID_KEY ((char*)-1)
33
34/*---------------------------------------------------------------------------
35 Private to this module
36 ---------------------------------------------------------------------------*/
37/**
38 * This enum stores the status for each parsed line (internal use only).
39 */
40typedef enum _line_status_ {
41 LINE_UNPROCESSED,
42 LINE_ERROR,
43 LINE_EMPTY,
44 LINE_COMMENT,
45 LINE_SECTION,
46 LINE_VALUE
47} line_status;
48
49/*-------------------------------------------------------------------------*/
50/**
51 @brief Convert a string to lowercase.
52 @param in String to convert.
53 @param out Output buffer.
54 @param len Size of the out buffer.
wei.du097b1682024-04-07 09:21:53 +000055 @return ptr to the out buffer or NULL if an error occurred.
Zhe Wang8ae3b112019-06-18 13:23:54 +080056
57 This function convert a string into lowercase.
58 At most len - 1 elements of the input string will be converted.
59 */
60/*--------------------------------------------------------------------------*/
61static const char *strlwc(const char *in, char *out, unsigned len)
62{
63 unsigned i = 0;
64
65 if (in == NULL || out == NULL || len == 0)
66 return NULL;
67
68 while (in[i] != '\0' && i < len-1) {
69 out[i] = (char)tolower((int)in[i]);
70 i++;
71 }
72 out[i] = '\0';
73 return out;
74}
75
76/*-------------------------------------------------------------------------*/
77/**
78 @brief Duplicate a string
79 @param s String to duplicate
80 @return Pointer to a newly allocated string, to be freed with free()
81
82 This is a replacement for strdup(). This implementation is provided
83 for systems that do not have it.
84 */
85/*--------------------------------------------------------------------------*/
86static char *xstrdup(const char *s)
87{
88 char *t;
89 size_t len;
90
91 if (!s)
92 return NULL;
93
94 len = strlen(s) + 1;
haiyang.ren004fee82024-06-26 03:21:54 +000095 t = (char*)aml_audio_malloc(len);
Zhe Wang8ae3b112019-06-18 13:23:54 +080096 if (t) {
97 memcpy(t, s, len);
98 }
99 return t;
100}
101
102/*-------------------------------------------------------------------------*/
103/**
104 @brief Remove blanks at the beginning and the end of a string.
105 @param str String to parse and alter.
106 @return unsigned New size of the string.
107 */
108/*--------------------------------------------------------------------------*/
109static unsigned strstrip(char *s)
110{
111 char *last = NULL;
112 char *dest = s;
113
114 if (s == NULL)
115 return 0;
116
117 last = s + strlen(s);
118 while (isspace((int)*s) && *s)
119 s++;
120 while (last > s) {
121 if (!isspace((int)*(last-1)))
122 break;
123 last --;
124 }
125 *last = (char)0;
126
127 memmove(dest, s, last - s + 1);
128 return last - s;
129}
130
131/*-------------------------------------------------------------------------*/
132/**
133 @brief Default error callback for iniparser: wraps `fprintf(stderr, ...)`.
134 */
135/*--------------------------------------------------------------------------*/
136static int default_error_callback(const char *format, ...)
137{
138 int ret;
139 va_list argptr;
140 va_start(argptr, format);
141 ret = vfprintf(stderr, format, argptr);
142 va_end(argptr);
143 return ret;
144}
145
146static int (*iniparser_error_callback)(const char*, ...) = default_error_callback;
147
148/*-------------------------------------------------------------------------*/
149/**
150 @brief Configure a function to receive the error messages.
151 @param errback Function to call.
152
153 By default, the error will be printed on stderr. If a null pointer is passed
154 as errback the error callback will be switched back to default.
155 */
156/*--------------------------------------------------------------------------*/
157void iniparser_set_error_callback(int (*errback)(const char *, ...))
158{
159 if (errback) {
160 iniparser_error_callback = errback;
161 } else {
162 iniparser_error_callback = default_error_callback;
163 }
164}
165
166/*-------------------------------------------------------------------------*/
167/**
168 @brief Get number of sections in a dictionary
169 @param d Dictionary to examine
170 @return int Number of sections found in dictionary
171
172 This function returns the number of sections found in a dictionary.
173 The test to recognize sections is done on the string stored in the
174 dictionary: a section name is given as "section" whereas a key is
175 stored as "section:key", thus the test looks for entries that do not
176 contain a colon.
177
178 This clearly fails in the case a section name contains a colon, but
179 this should simply be avoided.
180
181 This function returns -1 in case of error.
182 */
183/*--------------------------------------------------------------------------*/
184int iniparser_getnsec(const dictionary *d)
185{
186 int i;
187 int nsec = 0;
188
189 if (d == NULL)
190 return -1;
191
192 for (i = 0; i < d->size; i++) {
193 if (d->key[i] == NULL)
194 continue;
195 if (strchr(d->key[i], ':') == NULL) {
196 nsec ++;
197 }
198 }
199 return nsec;
200}
201
202/*-------------------------------------------------------------------------*/
203/**
204 @brief Get name for section n in a dictionary.
205 @param d Dictionary to examine
206 @param n Section number (from 0 to nsec-1).
207 @return Pointer to char string
208
209 This function locates the n-th section in a dictionary and returns
210 its name as a pointer to a string statically allocated inside the
211 dictionary. Do not free or modify the returned string!
212
213 This function returns NULL in case of error.
214 */
215/*--------------------------------------------------------------------------*/
216const char *iniparser_getsecname(const dictionary *d, int n)
217{
218 int i;
219 int foundsec = 0;
220
221 if (d == NULL || n < 0)
222 return NULL;
223
224 for (i = 0; i < d->size; i++) {
225 if (d->key[i] == NULL)
226 continue;
227 if (strchr(d->key[i], ':') == NULL) {
228 foundsec++;
229 if (foundsec>n)
230 break;
231 }
232 }
233 if (foundsec <= n) {
234 return NULL;
235 }
236 return d->key[i];
237}
238
239/*-------------------------------------------------------------------------*/
240/**
241 @brief Dump a dictionary to an opened file pointer.
242 @param d Dictionary to dump.
243 @param f Opened file pointer to dump to.
244 @return void
245
246 This function prints out the contents of a dictionary, one element by
247 line, onto the provided file pointer. It is OK to specify @c stderr
248 or @c stdout as output files. This function is meant for debugging
249 purposes mostly.
250 */
251/*--------------------------------------------------------------------------*/
252void iniparser_dump(const dictionary *d, FILE *f)
253{
254 int i;
255
256 if (d == NULL || f == NULL)
257 return;
258
259 for (i = 0; i < d->size; i++) {
260 if (d->key[i] == NULL)
261 continue;
262 if (d->val[i] != NULL) {
263 fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
264 } else {
265 fprintf(f, "[%s]=UNDEF\n", d->key[i]);
266 }
267 }
268 return;
269}
270
271/*-------------------------------------------------------------------------*/
272/**
273 @brief Save a dictionary to a loadable ini file
274 @param d Dictionary to dump
275 @param f Opened file pointer to dump to
276 @return void
277
278 This function dumps a given dictionary into a loadable ini file.
279 It is Ok to specify @c stderr or @c stdout as output files.
280 */
281/*--------------------------------------------------------------------------*/
282void iniparser_dump_ini(const dictionary *d, FILE *f)
283{
284 int i;
285 int nsec;
286 const char *secname;
287
288 if (d == NULL || f == NULL)
289 return;
290
291 nsec = iniparser_getnsec(d);
292 if (nsec<1) {
293 /* No section in file: dump all keys as they are */
294 for (i = 0; i < d->size; i++) {
295 if (d->key[i] == NULL)
296 continue;
297 fprintf(f, "%s = %s\n", d->key[i], d->val[i]);
298 }
299 return;
300 }
301 for (i = 0; i < nsec; i++) {
302 secname = iniparser_getsecname(d, i);
303 iniparser_dumpsection_ini(d, secname, f);
304 }
305 fprintf(f, "\n");
306 return;
307}
308
309/*-------------------------------------------------------------------------*/
310/**
311 @brief Save a dictionary section to a loadable ini file
312 @param d Dictionary to dump
313 @param s Section name of dictionary to dump
314 @param f Opened file pointer to dump to
315 @return void
316
317 This function dumps a given section of a given dictionary into a loadable ini
318 file. It is Ok to specify @c stderr or @c stdout as output files.
319 */
320/*--------------------------------------------------------------------------*/
321void iniparser_dumpsection_ini(const dictionary *d, const char *s, FILE *f)
322{
323 int j;
324 char keym[ASCIILINESZ + 1];
325 int seclen;
326
327 if (d == NULL || f == NULL)
328 return;
329
330 if (! iniparser_find_entry(d, s))
331 return;
332
333 seclen = (int)strlen(s);
334 fprintf(f, "\n[%s]\n", s);
335 sprintf(keym, "%s:", s);
336
337 for (j = 0; j < d->size; j++) {
338 if (d->key[j] == NULL)
339 continue;
340 if (!strncmp(d->key[j], keym, seclen + 1)) {
341 fprintf(f,
342 "%-30s = %s\n",
343 d->key[j] + seclen + 1,
344 d->val[j] ? d->val[j] : "");
345 }
346 }
347 fprintf(f, "\n");
348 return;
349}
350
351/*-------------------------------------------------------------------------*/
352/**
353 @brief Get the number of keys in a section of a dictionary.
354 @param d Dictionary to examine
355 @param s Section name of dictionary to examine
356 @return Number of keys in section
357 */
358/*--------------------------------------------------------------------------*/
359int iniparser_getsecnkeys(const dictionary *d, const char *s)
360{
361 int seclen, nkeys;
362 char keym[ASCIILINESZ + 1];
363 int j;
364
365 nkeys = 0;
366
367 if (d == NULL)
368 return nkeys;
369
370 if (! iniparser_find_entry(d, s))
371 return nkeys;
372
373 seclen = (int)strlen(s);
374 strlwc(s, keym, sizeof(keym));
375 keym[seclen] = ':';
376
377 for (j = 0; j < d->size; j++) {
378 if (d->key[j] == NULL)
379 continue;
380 if (!strncmp(d->key[j], keym, seclen + 1))
381 nkeys++;
382 }
383
384 return nkeys;
385
386}
387
388/*-------------------------------------------------------------------------*/
389/**
390 @brief Get the number of keys in a section of a dictionary.
391 @param d Dictionary to examine
392 @param s Section name of dictionary to examine
393 @param keys Already allocated array to store the keys in
394 @return The pointer passed as `keys` argument or NULL in case of error
395
396 This function queries a dictionary and finds all keys in a given section.
397 The keys argument should be an array of pointers which size has been
398 determined by calling `iniparser_getsecnkeys` function prior to this one.
399
400 Each pointer in the returned char pointer-to-pointer is pointing to
401 a string allocated in the dictionary; do not free or modify them.
402 */
403/*--------------------------------------------------------------------------*/
404const char **iniparser_getseckeys(const dictionary *d, const char *s, const char **keys)
405{
406 int i, j, seclen;
407 char keym[ASCIILINESZ + 1];
408
409 if (d == NULL || keys == NULL)
410 return NULL;
411
412 if (! iniparser_find_entry(d, s))
413 return NULL;
414
415 seclen = (int)strlen(s);
416 strlwc(s, keym, sizeof(keym));
417 keym[seclen] = ':';
418
419 i = 0;
420 for (j = 0; j < d->size; j++) {
421 if (d->key[j] == NULL)
422 continue;
423 if (!strncmp(d->key[j], keym, seclen + 1)) {
424 keys[i] = d->key[j];
425 i++;
426 }
427 }
428
429 return keys;
430}
431
432/*-------------------------------------------------------------------------*/
433/**
434 @brief Get the string associated to a key
435 @param d Dictionary to search
436 @param key Key string to look for
437 @param def Default value to return if key not found.
438 @return pointer to statically allocated character string
439
440 This function queries a dictionary for a key. A key as read from an
441 ini file is given as "section:key". If the key cannot be found,
442 the pointer passed as 'def' is returned.
443 The returned char pointer is pointing to a string allocated in
444 the dictionary, do not free or modify it.
445 */
446/*--------------------------------------------------------------------------*/
447const char *iniparser_getstring(const dictionary *d, const char *key, const char *def)
448{
449 const char *lc_key;
450 const char *sval;
451 char tmp_str[ASCIILINESZ + 1];
452
453 if (d == NULL || key == NULL)
454 return def;
455
456 lc_key = strlwc(key, tmp_str, sizeof(tmp_str));
457 sval = dictionary_get(d, lc_key, def);
458 return sval;
459}
460
461/*-------------------------------------------------------------------------*/
462/**
463 @brief Get the string associated to a key, convert to an long int
464 @param d Dictionary to search
465 @param key Key string to look for
466 @param notfound Value to return in case of error
467 @return long integer
468
469 This function queries a dictionary for a key. A key as read from an
470 ini file is given as "section:key". If the key cannot be found,
471 the notfound value is returned.
472
473 Supported values for integers include the usual C notation
474 so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
475 are supported. Examples:
476
477 "42" -> 42
478 "042" -> 34 (octal -> decimal)
479 "0x42" -> 66 (hexa -> decimal)
480
481 Warning: the conversion may overflow in various ways. Conversion is
482 totally outsourced to strtol(), see the associated man page for overflow
483 handling.
484
485 Credits: Thanks to A. Becker for suggesting strtol()
486 */
487/*--------------------------------------------------------------------------*/
488long int iniparser_getlongint(const dictionary *d, const char *key, long int notfound)
489{
490 const char *str;
491
492 str = iniparser_getstring(d, key, INI_INVALID_KEY);
493
494 if (str == INI_INVALID_KEY)
495 return notfound;
496
497 return strtol(str, NULL, 0);
498}
499
500
501/*-------------------------------------------------------------------------*/
502/**
503 @brief Get the string associated to a key, convert to an int
504 @param d Dictionary to search
505 @param key Key string to look for
506 @param notfound Value to return in case of error
507 @return integer
508
509 This function queries a dictionary for a key. A key as read from an
510 ini file is given as "section:key". If the key cannot be found,
511 the notfound value is returned.
512
513 Supported values for integers include the usual C notation
514 so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
515 are supported. Examples:
516
517 "42" -> 42
518 "042" -> 34 (octal -> decimal)
519 "0x42" -> 66 (hexa -> decimal)
520
521 Warning: the conversion may overflow in various ways. Conversion is
522 totally outsourced to strtol(), see the associated man page for overflow
523 handling.
524
525 Credits: Thanks to A. Becker for suggesting strtol()
526 */
527/*--------------------------------------------------------------------------*/
528int iniparser_getint(const dictionary *d, const char *key, int notfound)
529{
530 return (int)iniparser_getlongint(d, key, notfound);
531}
532
533/*-------------------------------------------------------------------------*/
534/**
535 @brief Get the string associated to a key, convert to a double
536 @param d Dictionary to search
537 @param key Key string to look for
538 @param notfound Value to return in case of error
539 @return double
540
541 This function queries a dictionary for a key. A key as read from an
542 ini file is given as "section:key". If the key cannot be found,
543 the notfound value is returned.
544 */
545/*--------------------------------------------------------------------------*/
546double iniparser_getdouble(const dictionary *d, const char *key, double notfound)
547{
548 const char *str;
549
550 str = iniparser_getstring(d, key, INI_INVALID_KEY);
551
552 if (str == INI_INVALID_KEY)
553 return notfound;
554
555 return atof(str);
556}
557
558/*-------------------------------------------------------------------------*/
559/**
560 @brief Get the string associated to a key, convert to a boolean
561 @param d Dictionary to search
562 @param key Key string to look for
563 @param notfound Value to return in case of error
564 @return integer
565
566 This function queries a dictionary for a key. A key as read from an
567 ini file is given as "section:key". If the key cannot be found,
568 the notfound value is returned.
569
570 A true boolean is found if one of the following is matched:
571
572 - A string starting with 'y'
573 - A string starting with 'Y'
574 - A string starting with 't'
575 - A string starting with 'T'
576 - A string starting with '1'
577
578 A false boolean is found if one of the following is matched:
579
580 - A string starting with 'n'
581 - A string starting with 'N'
582 - A string starting with 'f'
583 - A string starting with 'F'
584 - A string starting with '0'
585
586 The notfound value returned if no boolean is identified, does not
587 necessarily have to be 0 or 1.
588 */
589/*--------------------------------------------------------------------------*/
590int iniparser_getboolean(const dictionary *d, const char *key, int notfound)
591{
592 int ret;
593 const char *c;
594
595 c = iniparser_getstring(d, key, INI_INVALID_KEY);
596
597 if (c == INI_INVALID_KEY)
598 return notfound;
599
600 if (c[0] == 'y' || c[0] == 'Y' || c[0] == '1' || c[0] == 't' || c[0] == 'T') {
601 ret = 1;
602 } else if (c[0] == 'n' || c[0] == 'N' || c[0] == '0' || c[0] == 'f' || c[0] == 'F') {
603 ret = 0;
604 } else {
605 ret = notfound;
606 }
607 return ret;
608}
609
610/*-------------------------------------------------------------------------*/
611/**
612 @brief Finds out if a given entry exists in a dictionary
613 @param ini Dictionary to search
614 @param entry Name of the entry to look for
615 @return integer 1 if entry exists, 0 otherwise
616
617 Finds out if a given entry exists in the dictionary. Since sections
618 are stored as keys with NULL associated values, this is the only way
619 of querying for the presence of sections in a dictionary.
620 */
621/*--------------------------------------------------------------------------*/
622int iniparser_find_entry(const dictionary *ini, const char *entry)
623{
624 int found = 0;
625
626 if (iniparser_getstring(ini, entry, INI_INVALID_KEY) != INI_INVALID_KEY) {
627 found = 1;
628 }
629 return found;
630}
631
632/*-------------------------------------------------------------------------*/
633/**
634 @brief Set an entry in a dictionary.
635 @param ini Dictionary to modify.
636 @param entry Entry to modify (entry name)
637 @param val New value to associate to the entry.
638 @return int 0 if Ok, -1 otherwise.
639
640 If the given entry can be found in the dictionary, it is modified to
641 contain the provided value. If it cannot be found, the entry is created.
642 It is Ok to set val to NULL.
643 */
644/*--------------------------------------------------------------------------*/
645int iniparser_set(dictionary *ini, const char *entry, const char *val)
646{
647 char tmp_str[ASCIILINESZ + 1];
648
649 return dictionary_set(ini, strlwc(entry, tmp_str, sizeof(tmp_str)), val);
650}
651
652/*-------------------------------------------------------------------------*/
653/**
654 @brief Delete an entry in a dictionary
655 @param ini Dictionary to modify
656 @param entry Entry to delete (entry name)
657 @return void
658
659 If the given entry can be found, it is deleted from the dictionary.
660 */
661/*--------------------------------------------------------------------------*/
662void iniparser_unset(dictionary *ini, const char *entry)
663{
664 char tmp_str[ASCIILINESZ + 1];
665
666 dictionary_unset(ini, strlwc(entry, tmp_str, sizeof(tmp_str)));
667}
668
669/*-------------------------------------------------------------------------*/
670/**
671 @brief Load a single line from an INI file
672 @param input_line Input line, may be concatenated multi-line input
673 @param section Output space to store section
674 @param key Output space to store key
675 @param value Output space to store value
676 @return line_status value
677 */
678/*--------------------------------------------------------------------------*/
679static line_status iniparser_line(
680 const char *input_line,
681 char *section,
682 char *key,
683 char *value)
684{
685 line_status sta;
686 char *line = NULL;
687 size_t len;
688
689 line = xstrdup(input_line);
690 len = strstrip(line);
691
692 sta = LINE_UNPROCESSED;
693 if (len < 1) {
694 /*Empty line */
695 sta = LINE_EMPTY;
696 } else if (line[0] == '#' || line[0] == ';') {
697 /* Comment line */
698 sta = LINE_COMMENT;
699 } else if (line[0] == '[' && line[len - 1] == ']') {
700 /* Section name */
701 sscanf(line, "[%[^]]", section);
702 strstrip(section);
703 strlwc(section, section, len);
704 sta = LINE_SECTION;
705 } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
706 || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2) {
707 /* Usual key=value with quotes, with or without comments */
708 strstrip(key);
709 strlwc(key, key, len);
710 /* Don't strip spaces from values surrounded with quotes */
711 sta = LINE_VALUE;
712 } else if (sscanf (line, "%[^=] = %[^;#]", key, value) == 2) {
713 /* Usual key=value without quotes, with or without comments */
714 strstrip(key);
715 strlwc(key, key, len);
716 strstrip(value);
717 /*
718 * sscanf cannot handle '' or "" as empty values
719 * this is done here
720 */
721 if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {
722 value[0]=0;
723 }
724 sta = LINE_VALUE;
725 } else if (sscanf(line, "%[^=] = %[;#]", key, value) == 2
726 || sscanf(line, "%[^=] %[=]", key, value) == 2) {
727 /*
728 * Special cases:
729 * key=
730 * key=;
731 * key=#
732 */
733 strstrip(key);
734 strlwc(key, key, len);
735 value[0] = 0;
736 sta = LINE_VALUE;
737 } else {
738 /* Generate syntax error */
739 sta = LINE_ERROR;
740 }
741
haiyang.ren004fee82024-06-26 03:21:54 +0000742 aml_audio_free(line);
Zhe Wang8ae3b112019-06-18 13:23:54 +0800743 return sta;
744}
745
746/*-------------------------------------------------------------------------*/
747/**
748 @brief Parse an ini file and return an allocated dictionary object
749 @param ininame Name of the ini file to read.
750 @return Pointer to newly allocated dictionary
751
752 This is the parser for ini files. This function is called, providing
753 the name of the file to be read. It returns a dictionary object that
754 should not be accessed directly, but through accessor functions
755 instead.
756
757 The returned dictionary must be freed using iniparser_freedict().
758 */
759/*--------------------------------------------------------------------------*/
760dictionary *iniparser_load(const char *ininame)
761{
762 FILE *in;
763
yuliang.hu42b081c2024-07-23 09:34:41 +0800764 char *line = aml_audio_calloc(1, ASCIILINESZ + 1);
765 char *section = aml_audio_calloc(1, ASCIILINESZ + 1);
766 char *key = aml_audio_calloc(1, ASCIILINESZ + 1);
767 char *tmp = aml_audio_calloc(1, (ASCIILINESZ * 2) + 2);
768 char *val = aml_audio_calloc(1, ASCIILINESZ + 1);
Zhe Wang8ae3b112019-06-18 13:23:54 +0800769
770 int last = 0;
771 int len;
772 int lineno = 0;
773 int errs = 0;
774 int mem_err = 0;
775
776 dictionary *dict;
777
yuliang.hu42b081c2024-07-23 09:34:41 +0800778 if (!line || !section || !key || !tmp || !val) {
779 dict = NULL;
780 goto ERR;
781 }
782
Zhe Wang8ae3b112019-06-18 13:23:54 +0800783 if ((in = fopen(ininame, "r")) == NULL) {
784 iniparser_error_callback("iniparser: cannot open %s\n", ininame);
yuliang.hu42b081c2024-07-23 09:34:41 +0800785 dict = NULL;
786 goto ERR;
Zhe Wang8ae3b112019-06-18 13:23:54 +0800787 }
788
789 dict = dictionary_new(0);
790 if (!dict) {
791 fclose(in);
yuliang.hu42b081c2024-07-23 09:34:41 +0800792 dict = NULL;
793 goto ERR;
Zhe Wang8ae3b112019-06-18 13:23:54 +0800794 }
795
Zhe Wang8ae3b112019-06-18 13:23:54 +0800796 last = 0;
797
798 while (fgets(line+last, ASCIILINESZ-last, in) != NULL) {
799 lineno++;
800 len = (int)strlen(line)-1;
801 if (len <= 0)
802 continue;
803 /* Safety check against buffer overflows */
804 if (line[len] != '\n' && !feof(in)) {
805 iniparser_error_callback(
806 "iniparser: input line too long in %s (%d)\n",
807 ininame,
808 lineno);
809 dictionary_del(dict);
810 fclose(in);
yuliang.hu42b081c2024-07-23 09:34:41 +0800811 dict = NULL;
812 goto ERR;
Zhe Wang8ae3b112019-06-18 13:23:54 +0800813 }
814 /* Get rid of \n and spaces at end of line */
815 while ((len >= 0) &&
816 ((line[len] == '\n') || (isspace(line[len])))) {
817 line[len] = 0;
818 len--;
819 }
820 if (len < 0) { /* Line was entirely \n and/or spaces */
821 len = 0;
822 }
823 /* Detect multi-line */
824 if (line[len] == '\\') {
825 /* Multi-line value */
826 last = len;
827 continue;
828 } else {
829 last = 0;
830 }
831 switch (iniparser_line(line, section, key, val)) {
832 case LINE_EMPTY:
833 case LINE_COMMENT:
834 break;
835 case LINE_SECTION:
836 mem_err = dictionary_set(dict, section, NULL);
837 break;
838 case LINE_VALUE:
839 sprintf(tmp, "%s:%s", section, key);
840 mem_err = dictionary_set(dict, tmp, val);
841 break;
842 case LINE_ERROR:
843 iniparser_error_callback(
844 "iniparser: syntax error in %s (%d):\n-> %s\n",
845 ininame,
846 lineno,
847 line);
848 errs++;
849 break;
850 default:
851 break;
852 }
853 memset(line, 0, ASCIILINESZ);
854 last = 0;
855 if (mem_err < 0) {
856 iniparser_error_callback("iniparser: memory allocation failure\n");
857 break;
858 }
859 }
860 if (errs) {
861 dictionary_del(dict);
862 dict = NULL;
863 }
864 fclose(in);
yuliang.hu42b081c2024-07-23 09:34:41 +0800865ERR:
866 if (line) {
867 aml_audio_free(line);
868 line = NULL;
869 }
870 if (section) {
871 aml_audio_free(section);
872 section = NULL;
873 }
874 if (key) {
875 aml_audio_free(key);
876 key = NULL;
877 }
878 if (tmp) {
879 aml_audio_free(tmp);
880 tmp = NULL;
881 }
882 if (val) {
883 aml_audio_free(val);
884 val = NULL;
885 }
Zhe Wang8ae3b112019-06-18 13:23:54 +0800886 return dict;
887}
888
889/*-------------------------------------------------------------------------*/
890/**
891 @brief Free all memory associated to an ini dictionary
892 @param d Dictionary to free
893 @return void
894
895 Free all memory associated to an ini dictionary.
896 It is mandatory to call this function before the dictionary object
897 gets out of the current context.
898 */
899/*--------------------------------------------------------------------------*/
900void iniparser_freedict(dictionary *d)
901{
902 dictionary_del(d);
903}