what
[hdmi-switcher.git] / lel / inih / ini.c
1 /* inih -- simple .INI file parser
2
3 inih is released under the New BSD license (see LICENSE.txt). Go to the project
4 home page for more info:
5
6 http://code.google.com/p/inih/
7
8 */
9
10 #include <stdio.h>
11 #include <ctype.h>
12 #include <string.h>
13
14 #include "ini.h"
15
16 #if !INI_USE_STACK
17 #include <stdlib.h>
18 #endif
19
20 #define MAX_SECTION 50
21 #define MAX_NAME 50
22
23 /* Strip whitespace chars off end of given string, in place. Return s. */
24 static char* rstrip(char* s)
25 {
26 char* p = s + strlen(s);
27 while (p > s && isspace((unsigned char)(*--p)))
28 *p = '\0';
29 return s;
30 }
31
32 /* Return pointer to first non-whitespace char in given string. */
33 static char* lskip(const char* s)
34 {
35 while (*s && isspace((unsigned char)(*s)))
36 s++;
37 return (char*)s;
38 }
39
40 /* Return pointer to first char c or ';' comment in given string, or pointer to
41 null at end of string if neither found. ';' must be prefixed by a whitespace
42 character to register as a comment. */
43 static char* find_char_or_comment(const char* s, char c)
44 {
45 int was_whitespace = 0;
46 while (*s && *s != c && !(was_whitespace && *s == ';')) {
47 was_whitespace = isspace((unsigned char)(*s));
48 s++;
49 }
50 return (char*)s;
51 }
52
53 /* Version of strncpy that ensures dest (size bytes) is null-terminated. */
54 static char* strncpy0(char* dest, const char* src, size_t size)
55 {
56 strncpy(dest, src, size);
57 dest[size - 1] = '\0';
58 return dest;
59 }
60
61 /* See documentation in header file. */
62 int ini_parse_file(FILE* file,
63 int (*handler)(void*, const char*, const char*,
64 const char*),
65 void* user)
66 {
67 /* Uses a fair bit of stack (use heap instead if you need to) */
68 #if INI_USE_STACK
69 char line[INI_MAX_LINE];
70 #else
71 char* line;
72 #endif
73 char section[MAX_SECTION] = "";
74 char prev_name[MAX_NAME] = "";
75
76 char* start;
77 char* end;
78 char* name;
79 char* value;
80 int lineno = 0;
81 int error = 0;
82
83 #if !INI_USE_STACK
84 line = (char*)malloc(INI_MAX_LINE);
85 if (!line) {
86 return -2;
87 }
88 #endif
89
90 /* Scan through file line by line */
91 while (fgets(line, INI_MAX_LINE, file) != NULL) {
92 lineno++;
93
94 start = line;
95 #if INI_ALLOW_BOM
96 if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
97 (unsigned char)start[1] == 0xBB &&
98 (unsigned char)start[2] == 0xBF) {
99 start += 3;
100 }
101 #endif
102 start = lskip(rstrip(start));
103
104 if (*start == ';' || *start == '#') {
105 /* Per Python ConfigParser, allow '#' comments at start of line */
106 }
107 #if INI_ALLOW_MULTILINE
108 else if (*prev_name && *start && start > line) {
109 /* Non-black line with leading whitespace, treat as continuation
110 of previous name's value (as per Python ConfigParser). */
111 if (!handler(user, section, prev_name, start) && !error)
112 error = lineno;
113 }
114 #endif
115 else if (*start == '[') {
116 /* A "[section]" line */
117 end = find_char_or_comment(start + 1, ']');
118 if (*end == ']') {
119 *end = '\0';
120 strncpy0(section, start + 1, sizeof(section));
121 *prev_name = '\0';
122 }
123 else if (!error) {
124 /* No ']' found on section line */
125 error = lineno;
126 }
127 }
128 else if (*start && *start != ';') {
129 /* Not a comment, must be a name[=:]value pair */
130 end = find_char_or_comment(start, '=');
131 if (*end != '=') {
132 end = find_char_or_comment(start, ':');
133 }
134 if (*end == '=' || *end == ':') {
135 *end = '\0';
136 name = rstrip(start);
137 value = lskip(end + 1);
138 end = find_char_or_comment(value, '\0');
139 if (*end == ';')
140 *end = '\0';
141 rstrip(value);
142
143 /* Valid name[=:]value pair found, call handler */
144 strncpy0(prev_name, name, sizeof(prev_name));
145 if (!handler(user, section, name, value) && !error)
146 error = lineno;
147 }
148 else if (!error) {
149 /* No '=' or ':' found on name[=:]value line */
150 error = lineno;
151 }
152 }
153
154 #if INI_STOP_ON_FIRST_ERROR
155 if (error)
156 break;
157 #endif
158 }
159
160 #if !INI_USE_STACK
161 free(line);
162 #endif
163
164 return error;
165 }
166
167 /* See documentation in header file. */
168 int ini_parse(const char* filename,
169 int (*handler)(void*, const char*, const char*, const char*),
170 void* user)
171 {
172 FILE* file;
173 int error;
174
175 file = fopen(filename, "r");
176 if (!file)
177 return -1;
178 error = ini_parse_file(file, handler, user);
179 fclose(file);
180 return error;
181 }