Lely core libraries 1.9.2
config_ini.c
Go to the documentation of this file.
1
24#include "util.h"
25#include <lely/util/config.h>
26#include <lely/util/diag.h>
27#include <lely/util/frbuf.h>
28#include <lely/util/fwbuf.h>
29#include <lely/util/lex.h>
30#include <lely/util/membuf.h>
31#include <lely/util/print.h>
32
33#include <assert.h>
34
35static int issection(int c);
36static int iskey(int c);
37static int isvalue(int c);
38
39static size_t skip(const char *begin, const char *end, struct floc *at);
40
41static void membuf_print_chars(struct membuf *buf, const char *s, size_t n);
42
43static void config_print_ini_func(const char *section, const char *key,
44 const char *value, void *data);
45
46size_t
47config_parse_ini_file(config_t *config, const char *filename)
48{
49 frbuf_t *buf = frbuf_create(filename);
50 if (!buf) {
51 diag(DIAG_ERROR, get_errc(), "%s", filename);
52 return 0;
53 }
54
55 size_t size = 0;
56 const void *map = frbuf_map(buf, 0, &size);
57 if (!map) {
58 diag(DIAG_ERROR, get_errc(), "%s: unable to map file",
59 filename);
60 frbuf_destroy(buf);
61 return 0;
62 }
63
64 const char *begin = map;
65 const char *end = begin + size;
66 struct floc at = { filename, 1, 1 };
67 size_t chars = config_parse_ini_text(config, begin, end, &at);
68
69 frbuf_destroy(buf);
70
71 return chars;
72}
73
74size_t
75config_parse_ini_text(config_t *config, const char *begin, const char *end,
76 struct floc *at)
77{
78 assert(config);
79 assert(begin);
80
81 struct membuf section = MEMBUF_INIT;
82 struct membuf key = MEMBUF_INIT;
83 struct membuf value = MEMBUF_INIT;
84
85 const char *cp = begin;
86 size_t chars = 0;
87
88 for (;;) {
89 // Skip comments and empty lines.
90 for (;;) {
91 cp += skip(cp, end, at);
92 if ((chars = lex_break(cp, end, at)) > 0)
93 cp += chars;
94 else
95 break;
96 }
97 if ((end && cp >= end) || !*cp)
98 break;
99
100 if ((chars = lex_char('[', cp, end, at)) > 0) {
101 cp += chars;
102 cp += skip(cp, end, at);
103 if ((chars = lex_ctype(&issection, cp, end, at)) > 0) {
104 membuf_print_chars(&section, cp, chars);
105 cp += chars;
106 cp += skip(cp, end, at);
107 if ((chars = lex_char(']', cp, end, at)) > 0)
108 cp += chars;
109 else
110 diag_if(DIAG_ERROR, 0, at,
111 "expected ']' after section name");
112 } else {
113 diag_if(DIAG_ERROR, 0, at,
114 "expected section name after '['");
115 }
116 cp += lex_line_comment(NULL, cp, end, at);
117 } else if ((chars = lex_ctype(&iskey, cp, end, at)) > 0) {
118 membuf_print_chars(&key, cp, chars);
119 cp += chars;
120 cp += skip(cp, end, at);
121 if ((chars = lex_char('=', cp, end, at)) > 0) {
122 cp += chars;
123 cp += skip(cp, end, at);
124 if ((chars = lex_char('\"', cp, end, at)) > 0) {
125 cp += chars;
126 lex_c99_str(cp, end, at, NULL, &chars);
127 chars++;
128 membuf_clear(&value);
129 membuf_reserve(&value, chars);
130 char *s = membuf_alloc(&value, &chars);
131 if (s && chars)
132 s[--chars] = '\0';
133 cp += lex_c99_str(cp, end, NULL, s,
134 &chars);
135 // clang-format off
136 if ((chars = lex_char('\"', cp, end,
137 at)) > 0)
138 // clang-format on
139 cp += chars;
140 else
141 diag_if(DIAG_ERROR, 0, at,
142 "expected '\"' after string");
143 } else {
144 chars = lex_ctype(
145 &isvalue, cp, end, at);
146 membuf_print_chars(&value, cp, chars);
147 cp += chars;
148 }
149 config_set(config, membuf_begin(&section),
150 membuf_begin(&key),
151 membuf_begin(&value));
152 membuf_clear(&value);
153 } else {
154 diag_if(DIAG_ERROR, 0, at,
155 "expected '=' after key");
156 }
157 cp += lex_line_comment(NULL, cp, end, at);
158 } else {
159 if (isgraph((unsigned char)*cp))
160 diag_if(DIAG_ERROR, 0, at,
161 "unknown character '%c'", *cp);
162 else
163 diag_if(DIAG_ERROR, 0, at,
164 "unknown character '\\%o'",
165 *cp);
166 // Skip the offending character.
167 cp += lex_char(*cp, cp, end, at);
168 }
169 }
170
171 membuf_fini(&value);
172 membuf_fini(&key);
173 membuf_fini(&section);
174
175 return cp - begin;
176}
177
178size_t
179config_print_ini_file(const config_t *config, const char *filename)
180{
181 fwbuf_t *buf = fwbuf_create(filename);
182 if (!buf) {
183 diag(DIAG_ERROR, get_errc(), "%s", filename);
184 return 0;
185 }
186
187 size_t size = config_print_ini_text(config, NULL, NULL);
188 void *map = fwbuf_map(buf, 0, &size);
189 if (!map) {
190 diag(DIAG_ERROR, get_errc(), "%s: unable to map file",
191 filename);
192 fwbuf_destroy(buf);
193 return 0;
194 }
195
196 char *begin = map;
197 char *end = begin + size;
198 size_t chars = config_print_ini_text(config, &begin, end);
199
200 if (fwbuf_commit(buf) == -1) {
201 diag(DIAG_ERROR, get_errc(), "%s: unable to commit file",
202 filename);
203 fwbuf_destroy(buf);
204 return 0;
205 }
206
207 fwbuf_destroy(buf);
208
209 return chars;
210}
211
212size_t
213config_print_ini_text(const config_t *config, char **pbegin, char *end)
214{
215 assert(config);
216
217 struct {
218 char **pbegin;
219 char *end;
220 const char *section;
221 size_t chars;
222 } ctx = { .pbegin = pbegin, .end = end, .section = NULL, .chars = 0 };
223
224 config_foreach(config, &config_print_ini_func, &ctx);
225
226 return ctx.chars;
227}
228
229static int
230issection(int c)
231{
232 return isgraph(c) && c != '#' && c != ';' && c != '[' && c != ']';
233}
234
235static int
236iskey(int c)
237{
238 return isgraph(c) && c != '#' && c != ';' && c != '=';
239}
240
241static int
242isvalue(int c)
243{
244 return isprint(c) && c != '#' && c != ';';
245}
246
247static size_t
248skip(const char *begin, const char *end, struct floc *at)
249{
250 assert(begin);
251
252 const char *cp = begin;
253
254 cp += lex_ctype(&isblank, cp, end, at);
255 cp += lex_line_comment("#", cp, end, at);
256 cp += lex_line_comment(";", cp, end, at);
257
258 return cp - begin;
259}
260
261static void
262membuf_print_chars(struct membuf *buf, const char *s, size_t n)
263{
264 assert(buf);
265
266 // Remove trailing whitespace.
267 while (n && isspace((unsigned char)s[n - 1]))
268 n--;
269
270 membuf_clear(buf);
271 if (!membuf_reserve(buf, n + 1))
272 return;
273 membuf_write(buf, s, n);
274 membuf_write(buf, "", 1);
275}
276
277static void
278config_print_ini_func(const char *section, const char *key, const char *value,
279 void *data)
280{
281 assert(section);
282 assert(key);
283 assert(value);
284 struct {
285 char **pbegin;
286 char *end;
287 const char *section;
288 size_t chars;
289 } *ctx = data;
290 assert(ctx);
291 char **pbegin = ctx->pbegin;
292 char *end = ctx->end;
293 size_t chars = ctx->chars;
294
295 if (ctx->section != section) {
296 ctx->section = section;
297 // Prepend the section with a newline, if necessary.
298 if (chars)
299 chars += print_char(pbegin, end, '\n');
300 if (*section) {
301 chars += print_char(pbegin, end, '[');
302 while (*section)
303 chars += print_char(pbegin, end, *section++);
304 chars += print_char(pbegin, end, ']');
305 chars += print_char(pbegin, end, '\n');
306 }
307 }
308
309 while (*key)
310 chars += print_char(pbegin, end, *key++);
311
312 chars += print_char(pbegin, end, ' ');
313 chars += print_char(pbegin, end, '=');
314
315 if (*value) {
316 chars += print_char(pbegin, end, ' ');
317 size_t n = strlen(value);
318 // Check for leading or trailing whitespace.
319 int esc = isspace((unsigned char)value[0])
320 || isspace((unsigned char)value[n - 1]);
321 // Check for non-printable ASCII characters or comments.
322 for (size_t i = 0; !esc && i < n; i++)
323 esc = !isvalue((unsigned char)value[i]);
324 if (esc) {
325 chars += print_char(pbegin, end, '"');
326 chars += print_c99_str(pbegin, end, value, n);
327 chars += print_char(pbegin, end, '"');
328 } else {
329 while (*value)
330 chars += print_char(pbegin, end, *value++);
331 }
332 }
333
334 chars += print_char(pbegin, end, '\n');
335
336 ctx->chars = chars;
337}
This header file is part of the utilities library; it contains the configuration functions.
const char * config_set(config_t *config, const char *section, const char *key, const char *value)
Sets a key in or removes a key from a configuration struct.
Definition: config.c:213
void config_foreach(const config_t *config, config_foreach_func_t *func, void *data)
Invokes a function for each key in a configuration struct.
Definition: config.c:233
size_t config_parse_ini_file(config_t *config, const char *filename)
Parses an INI file and adds the keys to a configuration struct.
Definition: config_ini.c:47
size_t config_print_ini_file(const config_t *config, const char *filename)
Prints a configuration struct to an INI file.
Definition: config_ini.c:179
size_t config_parse_ini_text(config_t *config, const char *begin, const char *end, struct floc *at)
Parses a string in INI-format and adds the keys to a configuration struct.
Definition: config_ini.c:75
size_t config_print_ini_text(const config_t *config, char **pbegin, char *end)
Prints a configuration struct in INI-format to a memory buffer.
Definition: config_ini.c:213
This header file is part of the utilities library; it contains the diagnostic declarations.
@ DIAG_ERROR
An error.
Definition: diag.h:49
void diag_if(enum diag_severity severity, int errc, const struct floc *at, const char *format,...)
Emits a diagnostic message occurring at a location in a text file.
Definition: diag.c:190
void diag(enum diag_severity severity, int errc, const char *format,...)
Emits a diagnostic message.
Definition: diag.c:156
int get_errc(void)
Returns the last (thread-specific) native error code set by a system call or library function.
Definition: errnum.c:947
This header file is part of the utilities library; it contains the read file buffer declarations.
const void * frbuf_map(frbuf_t *buf, intmax_t pos, size_t *psize)
Maps (part of) the contents of a read file buffer to memory.
Definition: frbuf.c:389
void frbuf_destroy(frbuf_t *buf)
Destroys a read file buffer.
Definition: frbuf.c:158
frbuf_t * frbuf_create(const char *filename)
Creates a new read file buffer.
Definition: frbuf.c:133
This header file is part of the utilities library; it contains the (atomic) write file buffer declara...
void * fwbuf_map(fwbuf_t *buf, intmax_t pos, size_t *psize)
Maps (part of) the contents of a write file buffer to memory.
Definition: fwbuf.c:654
void fwbuf_destroy(fwbuf_t *buf)
Destroys a write file buffer.
Definition: fwbuf.c:326
int fwbuf_commit(fwbuf_t *buf)
Commits all changes to a write file buffer to disk if all previous file operations were successful,...
Definition: fwbuf.c:974
fwbuf_t * fwbuf_create(const char *filename)
Creates a new (atomic) write file buffer.
Definition: fwbuf.c:301
This header file is part of the utilities library; it contains the lexer function declarations.
size_t lex_char(int c, const char *begin, const char *end, struct floc *at)
Lexes the specified character from a memory buffer.
Definition: lex.c:38
size_t lex_ctype(int(*ctype)(int), const char *begin, const char *end, struct floc *at)
Greedily lexes a sequence of characters of the specified class from a memory buffer.
Definition: lex.c:51
size_t lex_line_comment(const char *delim, const char *begin, const char *end, struct floc *at)
Lexes a single line-comment (excluding the line break) starting with the specified delimiter from a m...
Definition: lex.c:634
size_t lex_c99_str(const char *begin, const char *end, struct floc *at, char *s, size_t *pn)
Lexes a UTF-8 encoded Unicode string from a memory buffer.
Definition: lex.c:247
size_t lex_break(const char *begin, const char *end, struct floc *at)
Lexes a single line break from a memory buffer.
Definition: lex.c:66
This header file is part of the utilities library; it contains the memory buffer declarations.
void * membuf_begin(const struct membuf *buf)
Returns a pointer to the first byte in a memory buffer.
Definition: membuf.h:144
size_t membuf_reserve(struct membuf *buf, size_t size)
Resizes a memory buffer, if necessary, to make room for at least an additional size bytes.
Definition: membuf.c:46
void membuf_fini(struct membuf *buf)
Finalizes a memory buffer.
Definition: membuf.c:38
size_t membuf_write(struct membuf *buf, const void *ptr, size_t size)
Writes data to a memory buffer.
Definition: membuf.h:192
void * membuf_alloc(struct membuf *buf, size_t *size)
Creates region of *size bytes in a memory buffer, starting at the current position indicator given by...
Definition: membuf.h:184
void membuf_clear(struct membuf *buf)
Clears a memory buffer.
Definition: membuf.h:150
#define MEMBUF_INIT
The static initializer for struct membuf.
Definition: membuf.h:45
This header file is part of the utilities library; it contains the printing function declarations.
size_t print_c99_str(char **pbegin, char *end, const char *s, size_t n)
Prints a UTF-8 encoded Unicode string to a memory buffer.
Definition: print.c:190
size_t print_char(char **pbegin, char *end, int c)
Prints a single character to a memory buffer.
Definition: print.h:284
This is the internal header file of the utilities library.
A configuration struct.
Definition: config.c:35
An read file buffer struct.
Definition: frbuf.c:49
An (atomic) write file buffer struct.
Definition: fwbuf.c:56
A location in a text file.
Definition: diag.h:31
const char * filename
The name of the file.
Definition: diag.h:33
A memory buffer.
Definition: membuf.h:35
char * end
A pointer to one past the last byte in the buffer.
Definition: membuf.h:41
char * begin
A pointer to the first byte in the buffer.
Definition: membuf.h:39