1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
|
/// \file
/// POM configuration parser for C.
///
/// ## Thread-safety
///
/// Of course, you should not free a configuration while
/// another thread is using it (even through a section
/// obtained via \ref pom_conf_section).
///
/// Other than that, all these functions are fully thread-safe
/// provided that C11 atomics are available
/// (`__STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__)`).
/// But beware of race conditions when using \ref pom_conf_unread_keys
/// (ensure there is synchronization so that all threads
/// have certainly read their keys before calling it).
///
/// If C11 atomics are not available, you should not use
/// the same configuration across multiple threads (even for
/// seemingly "read-only" operations), but you can still use
/// distinct configurations in different threads without worry.
/// \mainpage libpom doxygen documentation
///
/// See \ref pom.h for all types/functions.
#ifndef POM_H_
#define POM_H_
#ifndef POM_NO_STDIO
#include <stdio.h>
#endif
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#ifndef POM__MUST_USE_L
#if _MSC_VER >= 1700 // supposedly was added in VS2012
#define POM__MUST_USE_L _Check_return_
#else
#define POM__MUST_USE_L
#endif
#endif
#ifndef POM__MUST_USE_R
#if __GNUC__ >= 4
#define POM__MUST_USE_R __attribute__((warn_unused_result))
#else
#define POM__MUST_USE_R
#endif
#endif
/// A POM configuration
typedef struct pom_conf pom_conf;
/// A POM-related error
typedef struct pom_error pom_error;
/// Load a configuration using a `read`-like function.
///
/// On success, a configuration is returned and `*error` is set to `NULL`
/// if `error` is not `NULL`.
///
/// On failure, `NULL` is returned, and `*error` is filled out if `error` is not `NULL`,
/// in which case it must be freed with `free`.
/// In the extremely rare case that `error` is not `NULL` and
/// there isn’t even enough memory for an out-of-memory
/// error, `NULL` is returned and `*error` is set to `NULL`.
///
/// Most of the time, you won't need this function:
/// use \ref pom_load_file to load from a `FILE *`,
/// \ref pom_load_path to load from a path,
/// and \ref pom_load_string to load from a string.
///
/// `read_func` will be passed the `userdata` pointer passed to this function,
/// a buffer, and the length of that buffer (which will be nonzero).
/// It must fill out the buffer as much as possible,
/// and return the number of bytes read.
/// A return value less than `len` indicates the end of the file was reached.
///
/// `filename` is only used for errors.
POM__MUST_USE_L
pom_conf *
pom_load(const char *filename,
size_t (*read_func)(void *userdata, char *buf, size_t len),
void *userdata, pom_error **error)
POM__MUST_USE_R;
#ifndef POM_NO_STDIO
/// Load configuration from a `FILE *`.
///
/// On success, a configuration is returned and `*error` is set to `NULL`
/// if `error` is not `NULL`.
///
/// On failure, `NULL` is returned, and `*error` is filled out if `error` is not `NULL`,
/// in which case it must be freed with `free`.
///
/// `filename` is only used for errors.
POM__MUST_USE_L
pom_conf *
pom_load_file(const char *filename, FILE *file, pom_error **error)
POM__MUST_USE_R;
/// Load configuration from a file path.
///
/// On success, a configuration is returned and `*error` is set to `NULL`
/// if `error` is not `NULL`.
///
/// On failure, `NULL` is returned, and `*error` is filled out if `error` is not `NULL`,
/// in which case it must be freed with `free`.
POM__MUST_USE_L
pom_conf *
pom_load_path(const char *path, pom_error **error)
POM__MUST_USE_R;
#endif
/// Load configuration from a string.
///
/// On success, a configuration is returned and `*error` is set to `NULL`
/// if `error` is not `NULL`.
///
/// On failure, `NULL` is returned, and `*error` is filled out if `error` is not `NULL`,
/// in which case it must be freed with `free`.
///
/// `filename` is only used for errors.
POM__MUST_USE_L
pom_conf *
pom_load_string(const char *filename, const char *string, pom_error **error)
POM__MUST_USE_R;
/// Get the message of this error.
///
/// This will be a string such as `"Duplicate key: foo.bar"`
///
/// See also \ref pom_error_print and \ref pom_error_to_string, which
/// are probably actually what you want in most cases.
const char *
pom_error_message(const pom_error *error);
/// Get the name of the file where this error occured.
const char *
pom_error_file(const pom_error *error);
/// Get line number where this error occured.
uint64_t
pom_error_line(const pom_error *error);
/// Get next error in error list.
const pom_error *
pom_error_next(const pom_error *error);
#ifndef POM_NO_STDIO
/// Print error to `stderr`.
///
/// Includes every error in an error list (see \ref pom_error_next).
void
pom_error_print(const pom_error *error);
#endif
/// Convert error to string. Return value must be `free()`d.
///
/// Includes every error in an error list (see \ref pom_error_next).
POM__MUST_USE_L
char *
pom_error_to_string(const pom_error *error)
POM__MUST_USE_R;
/// Get value of `key` in configuration, or `NULL` if key is not present.
const char *
pom_conf_get(const pom_conf *conf, const char *key);
/// Get value of `key` in configuration, or use `dflt` if not present.
const char *
pom_conf_get_or_default(const pom_conf *conf, const char *key, const char *dflt);
/// Get signed integer value of `key`.
///
/// Returns `NULL` on success, putting the integer value in `*value`.
///
/// If `key` is not set or `key` is set but not a valid integer,
/// returns an error which must be `free()`d.
/// `*value` is set to 0 in this case.
POM__MUST_USE_L
pom_error *
pom_conf_get_int(const pom_conf *conf, const char *key, int64_t *value)
POM__MUST_USE_R;
/// Get signed integer value of `key`, or `dflt` if not present.
///
/// Returns `NULL` on success, putting the integer value in `*value`.
///
/// If `key` is set but not a valid integer,
/// returns an error which must be `free()`d.
/// `*value` is still set to `dflt` in this case.
POM__MUST_USE_L
pom_error *
pom_conf_get_int_or_default(const pom_conf *conf, const char *key, int64_t *value, int64_t dflt)
POM__MUST_USE_R;
/// Get unsigned integer value of `key`.
///
/// Returns `NULL` on success, putting the unsigned integer value in `*value`.
///
/// If `key` is not set or `key` is set but not a valid unsigned integer,
/// returns an error which must be `free()`d.
/// `*value` is set to 0 in this case.
POM__MUST_USE_L
pom_error *
pom_conf_get_uint(const pom_conf *conf, const char *key, uint64_t *value)
POM__MUST_USE_R;
/// Get unsigned integer value of `key`, or `dflt` if not present.
///
/// Returns `NULL` on success, putting the unsigned integer value in `*value`.
///
/// If `key` is set but not a valid unsigned integer,
/// returns an error which must be `free()`d.
/// `*value` is set to 0 in this case.
POM__MUST_USE_L
pom_error *
pom_conf_get_uint_or_default(const pom_conf *conf, const char *key, uint64_t *value, uint64_t dflt)
POM__MUST_USE_R;
/// Get floating-point value of `key`.
///
/// Returns `NULL` on success, putting the floating-point value in `*value`.
///
/// If `key` is not set or `key` is set but not a valid floating-point number,
/// returns an error which must be `free()`d.
/// `*value` is set to 0.0 in this case.
POM__MUST_USE_L
pom_error *
pom_conf_get_float(const pom_conf *conf, const char *key, double *value)
POM__MUST_USE_R;
/// Get floating-point value of `key`, or `dflt` if not present.
///
/// Returns `NULL` on success, putting the floating-point value in `*value`.
///
/// If `key` is set but not a valid floating-point number,
/// returns an error which must be `free()`d.
/// `*value` is still set to `dflt` in this case.
POM__MUST_USE_L
pom_error *
pom_conf_get_float_or_default(const pom_conf *conf, const char *key, double *value, double dflt)
POM__MUST_USE_R;
/// Get boolean value of `key`.
///
/// Returns `NULL` on success, putting the boolean value in `*value`.
///
/// If `key` is not set or `key` is set but not a valid boolean,
/// returns an error which must be `free()`d.
/// `*value` is set to `false` in this case.
POM__MUST_USE_L
pom_error *
pom_conf_get_bool(const pom_conf *conf, const char *key, bool *value)
POM__MUST_USE_R;
/// Get boolean value of `key`, or `dflt` if not present.
///
/// Returns `NULL` on success, putting the boolean value in `*value`.
///
/// If `key` is set but not a valid boolean,
/// returns an error which must be `free()`d.
/// `*value` is still set to `dflt` in this case.
POM__MUST_USE_L
pom_error *
pom_conf_get_bool_or_default(const pom_conf *conf, const char *key, bool *value, bool dflt)
POM__MUST_USE_R;
/// Get comma-separated list value of `key`, or `NULL` if not present. The return value must be freed with `free`.
///
/// The list is `NULL`-terminated.
///
/// Commas can be escaped in list entries using `\,`.
///
/// (`free`ing the list also frees the entries thanks to a "hack" where list entries are stored
/// inline after the entry pointers.)
POM__MUST_USE_L
char **
pom_conf_get_list(const pom_conf *conf, const char *key)
POM__MUST_USE_R;
/// Extract section out of POM configuration.
///
/// The returned section doesn't need to be freed, and is valid until
/// \ref pom_conf_free is called on the original configuration.
const pom_conf *
pom_conf_section(const pom_conf *conf, const char *section);
/// Create a copy of `conf`.
///
/// The copy must be freed with \ref pom_conf_free.
///
/// Returns `NULL` (but does not set `conf`'s error) in the (rare) case of out-of-memory.
///
/// The copy is entirely independent from `conf` — it can be passed to a separate
/// thread without worry.
POM__MUST_USE_L
pom_conf *
pom_conf_copy(const pom_conf *conf)
POM__MUST_USE_R;
/// Merge keys from `other` into `conf`, preferring keys in `other`.
void
pom_conf_merge(pom_conf *conf, const pom_conf *other);
/// Get all unread keys in `conf`.
///
/// The returned array is `NULL`-terminated, and must be freed with `free`.
///
/// (The entries of the array are stored inline with the array,
/// so they are freed when it is.)
POM__MUST_USE_L
char **
pom_conf_unread_keys(pom_conf *conf)
POM__MUST_USE_R;
#ifndef POM_NO_STDIO
/// Print `key: value` for each `key` in `conf` to `stdout`.
void
pom_conf_print(const pom_conf *conf);
#endif
/// Free a POM configuration.
void
pom_conf_free(pom_conf *conf);
#endif
|