ini.c - ltk - GUI toolkit for X11 (WIP)
HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
HTML git clone https://lumidify.org/ltk.git (encrypted, but very slow)
HTML git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltk.git (over tor)
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
ini.c (5350B)
---
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 https://github.com/benhoyt/inih
7
8 */
9
10 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
11 #define _CRT_SECURE_NO_WARNINGS
12 #endif
13
14 #include <stdio.h>
15 #include <ctype.h>
16 #include <string.h>
17
18 #include "ini.h"
19
20 #if !INI_USE_STACK
21 #include <stdlib.h>
22 #endif
23
24 #define MAX_SECTION 50
25 #define MAX_NAME 50
26
27 /* Strip whitespace chars off end of given string, in place. Return s. */
28 static char* rstrip(char* s)
29 {
30 char* p = s + strlen(s);
31 while (p > s && isspace((unsigned char)(*--p)))
32 *p = '\0';
33 return s;
34 }
35
36 /* Return pointer to first non-whitespace char in given string. */
37 static char* lskip(const char* s)
38 {
39 while (*s && isspace((unsigned char)(*s)))
40 s++;
41 return (char*)s;
42 }
43
44 /* Return pointer to first char (of chars) or inline comment in given string,
45 or pointer to null at end of string if neither found. Inline comment must
46 be prefixed by a whitespace character to register as a comment. */
47 static char* find_chars_or_comment(const char* s, const char* chars)
48 {
49 #if INI_ALLOW_INLINE_COMMENTS
50 int was_space = 0;
51 while (*s && (!chars || !strchr(chars, *s)) &&
52 !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
53 was_space = isspace((unsigned char)(*s));
54 s++;
55 }
56 #else
57 while (*s && (!chars || !strchr(chars, *s))) {
58 s++;
59 }
60 #endif
61 return (char*)s;
62 }
63
64 /* Version of strncpy that ensures dest (size bytes) is null-terminated. */
65 static char* strncpy0(char* dest, const char* src, size_t size)
66 {
67 strncpy(dest, src, size);
68 dest[size - 1] = '\0';
69 return dest;
70 }
71
72 /* See documentation in header file. */
73 int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
74 void* user)
75 {
76 /* Uses a fair bit of stack (use heap instead if you need to) */
77 #if INI_USE_STACK
78 char line[INI_MAX_LINE];
79 #else
80 char* line;
81 #endif
82 char section[MAX_SECTION] = "";
83 char prev_name[MAX_NAME] = "";
84
85 char* start;
86 char* end;
87 char* name;
88 char* value;
89 int lineno = 0;
90 int error = 0;
91
92 #if !INI_USE_STACK
93 line = (char*)malloc(INI_MAX_LINE);
94 if (!line) {
95 return -2;
96 }
97 #endif
98
99 #if INI_HANDLER_LINENO
100 #define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
101 #else
102 #define HANDLER(u, s, n, v) handler(u, s, n, v)
103 #endif
104
105 /* Scan through stream line by line */
106 while (reader(line, INI_MAX_LINE, stream) != NULL) {
107 lineno++;
108
109 start = line;
110 #if INI_ALLOW_BOM
111 if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
112 (unsigned char)start[1] == 0xBB &&
113 (unsigned char)start[2] == 0xBF) {
114 start += 3;
115 }
116 #endif
117 start = lskip(rstrip(start));
118
119 if (*start == ';' || *start == '#') {
120 /* Per Python configparser, allow both ; and # comments at the
121 start of a line */
122 }
123 #if INI_ALLOW_MULTILINE
124 else if (*prev_name && *start && start > line) {
125 /* Non-blank line with leading whitespace, treat as continuation
126 of previous name's value (as per Python configparser). */
127 if (!HANDLER(user, section, prev_name, start) && !error)
128 error = lineno;
129 }
130 #endif
131 else if (*start == '[') {
132 /* A "[section]" line */
133 end = find_chars_or_comment(start + 1, "]");
134 if (*end == ']') {
135 *end = '\0';
136 strncpy0(section, start + 1, sizeof(section));
137 *prev_name = '\0';
138 }
139 else if (!error) {
140 /* No ']' found on section line */
141 error = lineno;
142 }
143 }
144 else if (*start) {
145 /* Not a comment, must be a name[=:]value pair */
146 end = find_chars_or_comment(start, "=:");
147 if (*end == '=' || *end == ':') {
148 *end = '\0';
149 name = rstrip(start);
150 value = end + 1;
151 #if INI_ALLOW_INLINE_COMMENTS
152 end = find_chars_or_comment(value, NULL);
153 if (*end)
154 *end = '\0';
155 #endif
156 value = lskip(value);
157 rstrip(value);
158
159 /* Valid name[=:]value pair found, call handler */
160 strncpy0(prev_name, name, sizeof(prev_name));
161 if (!HANDLER(user, section, name, value) && !error)
162 error = lineno;
163 }
164 else if (!error) {
165 /* No '=' or ':' found on name[=:]value line */
166 error = lineno;
167 }
168 }
169
170 #if INI_STOP_ON_FIRST_ERROR
171 if (error)
172 break;
173 #endif
174 }
175
176 #if !INI_USE_STACK
177 free(line);
178 #endif
179
180 return error;
181 }
182
183 /* See documentation in header file. */
184 int ini_parse_file(FILE* file, ini_handler handler, void* user)
185 {
186 return ini_parse_stream((ini_reader)fgets, file, handler, user);
187 }
188
189 /* See documentation in header file. */
190 int ini_parse(const char* filename, ini_handler handler, void* user)
191 {
192 FILE* file;
193 int error;
194
195 file = fopen(filename, "r");
196 if (!file)
197 return -1;
198 error = ini_parse_file(file, handler, user);
199 fclose(file);
200 return error;
201 }