comparison src/ini.c @ 11:e6a594f16403

*: huge refactor the config file has changed drastically, moving to an ini file from that custom format; i *would* have used the win32 functions for those, but they were barely functional, so I decided on using ini.h which is lightweight enough. additionally, I've added Deezer support so album art will be displayed! unfortunately though winhttp is a pain in the ass so if I send a request with any form of unicode chars in it it just returns a "bad request" error. I've tried debugging this but I could never really come up with anything: my hypothesis is that deezer expects their characters in percent-encoded UTF-8, but winhttp is sending them in some other encoding. the config dialog was moved out of config.c (overdue) and many more options are given in the config as well. main.c has been renamed to plugin.c to better differentiate it from... everything else.
author Paper <paper@paper.us.eu.org>
date Thu, 14 Mar 2024 20:25:37 -0400
parents
children
comparison
equal deleted inserted replaced
10:42ac054c0231 11:e6a594f16403
1 /* inih -- simple .INI file parser
2
3 SPDX-License-Identifier: BSD-3-Clause
4
5 Copyright (C) 2009-2020, Ben Hoyt
6
7 inih is released under the New BSD license (see LICENSE.txt). Go to the project
8 home page for more info:
9
10 https://github.com/benhoyt/inih
11
12 */
13
14 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
15 #define _CRT_SECURE_NO_WARNINGS
16 #endif
17
18 #include <stdio.h>
19 #include <ctype.h>
20 #include <string.h>
21
22 #include "ini.h"
23
24 #if !INI_USE_STACK
25 #if INI_CUSTOM_ALLOCATOR
26 #include <stddef.h>
27 void* ini_malloc(size_t size);
28 void ini_free(void* ptr);
29 void* ini_realloc(void* ptr, size_t size);
30 #else
31 #include <stdlib.h>
32 #define ini_malloc malloc
33 #define ini_free free
34 #define ini_realloc realloc
35 #endif
36 #endif
37
38 #define MAX_SECTION 50
39 #define MAX_NAME 50
40
41 /* Used by ini_parse_string() to keep track of string parsing state. */
42 typedef struct {
43 const char* ptr;
44 size_t num_left;
45 } ini_parse_string_ctx;
46
47 /* Strip whitespace chars off end of given string, in place. Return s. */
48 static char* ini_rstrip(char* s)
49 {
50 char* p = s + strlen(s);
51 while (p > s && isspace((unsigned char)(*--p)))
52 *p = '\0';
53 return s;
54 }
55
56 /* Return pointer to first non-whitespace char in given string. */
57 static char* ini_lskip(const char* s)
58 {
59 while (*s && isspace((unsigned char)(*s)))
60 s++;
61 return (char*)s;
62 }
63
64 /* Return pointer to first char (of chars) or inline comment in given string,
65 or pointer to NUL at end of string if neither found. Inline comment must
66 be prefixed by a whitespace character to register as a comment. */
67 static char* ini_find_chars_or_comment(const char* s, const char* chars)
68 {
69 #if INI_ALLOW_INLINE_COMMENTS
70 int was_space = 0;
71 while (*s && (!chars || !strchr(chars, *s)) &&
72 !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
73 was_space = isspace((unsigned char)(*s));
74 s++;
75 }
76 #else
77 while (*s && (!chars || !strchr(chars, *s))) {
78 s++;
79 }
80 #endif
81 return (char*)s;
82 }
83
84 /* Similar to strncpy, but ensures dest (size bytes) is
85 NUL-terminated, and doesn't pad with NULs. */
86 static char* ini_strncpy0(char* dest, const char* src, size_t size)
87 {
88 /* Could use strncpy internally, but it causes gcc warnings (see issue #91) */
89 size_t i;
90 for (i = 0; i < size - 1 && src[i]; i++)
91 dest[i] = src[i];
92 dest[i] = '\0';
93 return dest;
94 }
95
96 /* See documentation in header file. */
97 int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
98 void* user)
99 {
100 /* Uses a fair bit of stack (use heap instead if you need to) */
101 #if INI_USE_STACK
102 char line[INI_MAX_LINE];
103 size_t max_line = INI_MAX_LINE;
104 #else
105 char* line;
106 size_t max_line = INI_INITIAL_ALLOC;
107 #endif
108 #if INI_ALLOW_REALLOC && !INI_USE_STACK
109 char* new_line;
110 size_t offset;
111 #endif
112 char section[MAX_SECTION] = "";
113 char prev_name[MAX_NAME] = "";
114
115 char* start;
116 char* end;
117 char* name;
118 char* value;
119 int lineno = 0;
120 int error = 0;
121
122 #if !INI_USE_STACK
123 line = (char*)ini_malloc(INI_INITIAL_ALLOC);
124 if (!line) {
125 return -2;
126 }
127 #endif
128
129 #if INI_HANDLER_LINENO
130 #define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
131 #else
132 #define HANDLER(u, s, n, v) handler(u, s, n, v)
133 #endif
134
135 /* Scan through stream line by line */
136 while (reader(line, (int)max_line, stream) != NULL) {
137 #if INI_ALLOW_REALLOC && !INI_USE_STACK
138 offset = strlen(line);
139 while (offset == max_line - 1 && line[offset - 1] != '\n') {
140 max_line *= 2;
141 if (max_line > INI_MAX_LINE)
142 max_line = INI_MAX_LINE;
143 new_line = ini_realloc(line, max_line);
144 if (!new_line) {
145 ini_free(line);
146 return -2;
147 }
148 line = new_line;
149 if (reader(line + offset, (int)(max_line - offset), stream) == NULL)
150 break;
151 if (max_line >= INI_MAX_LINE)
152 break;
153 offset += strlen(line + offset);
154 }
155 #endif
156
157 lineno++;
158
159 start = line;
160 #if INI_ALLOW_BOM
161 if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
162 (unsigned char)start[1] == 0xBB &&
163 (unsigned char)start[2] == 0xBF) {
164 start += 3;
165 }
166 #endif
167 start = ini_lskip(ini_rstrip(start));
168
169 if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
170 /* Start-of-line comment */
171 }
172 #if INI_ALLOW_MULTILINE
173 else if (*prev_name && *start && start > line) {
174 #if INI_ALLOW_INLINE_COMMENTS
175 end = ini_find_chars_or_comment(start, NULL);
176 if (*end)
177 *end = '\0';
178 ini_rstrip(start);
179 #endif
180 /* Non-blank line with leading whitespace, treat as continuation
181 of previous name's value (as per Python configparser). */
182 if (!HANDLER(user, section, prev_name, start) && !error)
183 error = lineno;
184 }
185 #endif
186 else if (*start == '[') {
187 /* A "[section]" line */
188 end = ini_find_chars_or_comment(start + 1, "]");
189 if (*end == ']') {
190 *end = '\0';
191 ini_strncpy0(section, start + 1, sizeof(section));
192 *prev_name = '\0';
193 #if INI_CALL_HANDLER_ON_NEW_SECTION
194 if (!HANDLER(user, section, NULL, NULL) && !error)
195 error = lineno;
196 #endif
197 }
198 else if (!error) {
199 /* No ']' found on section line */
200 error = lineno;
201 }
202 }
203 else if (*start) {
204 /* Not a comment, must be a name[=:]value pair */
205 end = ini_find_chars_or_comment(start, "=:");
206 if (*end == '=' || *end == ':') {
207 *end = '\0';
208 name = ini_rstrip(start);
209 value = end + 1;
210 #if INI_ALLOW_INLINE_COMMENTS
211 end = ini_find_chars_or_comment(value, NULL);
212 if (*end)
213 *end = '\0';
214 #endif
215 value = ini_lskip(value);
216 ini_rstrip(value);
217
218 /* Valid name[=:]value pair found, call handler */
219 ini_strncpy0(prev_name, name, sizeof(prev_name));
220 if (!HANDLER(user, section, name, value) && !error)
221 error = lineno;
222 }
223 else if (!error) {
224 /* No '=' or ':' found on name[=:]value line */
225 #if INI_ALLOW_NO_VALUE
226 *end = '\0';
227 name = ini_rstrip(start);
228 if (!HANDLER(user, section, name, NULL) && !error)
229 error = lineno;
230 #else
231 error = lineno;
232 #endif
233 }
234 }
235
236 #if INI_STOP_ON_FIRST_ERROR
237 if (error)
238 break;
239 #endif
240 }
241
242 #if !INI_USE_STACK
243 ini_free(line);
244 #endif
245
246 return error;
247 }
248
249 /* See documentation in header file. */
250 int ini_parse_file(FILE* file, ini_handler handler, void* user)
251 {
252 return ini_parse_stream((ini_reader)fgets, file, handler, user);
253 }
254
255 /* See documentation in header file. */
256 int ini_parse(const char* filename, ini_handler handler, void* user)
257 {
258 FILE* file;
259 int error;
260
261 file = fopen(filename, "r");
262 if (!file)
263 return -1;
264 error = ini_parse_file(file, handler, user);
265 fclose(file);
266 return error;
267 }
268
269 /* An ini_reader function to read the next line from a string buffer. This
270 is the fgets() equivalent used by ini_parse_string(). */
271 static char* ini_reader_string(char* str, int num, void* stream) {
272 ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream;
273 const char* ctx_ptr = ctx->ptr;
274 size_t ctx_num_left = ctx->num_left;
275 char* strp = str;
276 char c;
277
278 if (ctx_num_left == 0 || num < 2)
279 return NULL;
280
281 while (num > 1 && ctx_num_left != 0) {
282 c = *ctx_ptr++;
283 ctx_num_left--;
284 *strp++ = c;
285 if (c == '\n')
286 break;
287 num--;
288 }
289
290 *strp = '\0';
291 ctx->ptr = ctx_ptr;
292 ctx->num_left = ctx_num_left;
293 return str;
294 }
295
296 /* See documentation in header file. */
297 int ini_parse_string(const char* string, ini_handler handler, void* user) {
298 ini_parse_string_ctx ctx;
299
300 ctx.ptr = string;
301 ctx.num_left = strlen(string);
302 return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
303 user);
304 }