Lely core libraries  2.2.5
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 
35 static int issection(int c);
36 static int iskey(int c);
37 static int isvalue(int c);
38 
39 static size_t skip(const char *begin, const char *end, struct floc *at);
40 
41 static void membuf_print_chars(struct membuf *buf, const char *s, size_t n);
42 
43 static void config_print_ini_func(const char *section, const char *key,
44  const char *value, void *data);
45 
46 size_t
47 config_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 
74 size_t
75 config_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 
178 size_t
179 config_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 
212 size_t
213 config_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 
229 static int
230 issection(int c)
231 {
232  return isgraph(c) && c != '#' && c != ';' && c != '[' && c != ']';
233 }
234 
235 static int
236 iskey(int c)
237 {
238  return isgraph(c) && c != '#' && c != ';' && c != '=';
239 }
240 
241 static int
242 isvalue(int c)
243 {
244  return isprint(c) && c != '#' && c != ';';
245 }
246 
247 static size_t
248 skip(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 
261 static void
262 membuf_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 
277 static void
278 config_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.
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
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
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.
frbuf_t * frbuf_create(const char *filename)
Creates a new read file buffer.
Definition: frbuf.c:133
void frbuf_destroy(frbuf_t *buf)
Destroys a read file buffer.
Definition: frbuf.c:158
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
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:655
void fwbuf_destroy(fwbuf_t *buf)
Destroys a write file buffer.
Definition: fwbuf.c:327
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:975
fwbuf_t * fwbuf_create(const char *filename)
Creates a new (atomic) write file buffer.
Definition: fwbuf.c:302
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:630
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