summaryrefslogtreecommitdiff
path: root/pom.h
blob: 79bdcd713ea7ac4bf4c1516486e954e3bafe3067 (plain)
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
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
/// \file
/// POM configuration parser for C.
///
/// ## Thread-safety
///
/// Of course, you should not free or \ref pom_conf_merge into
/// a configuration while another thread is using it.
///
/// \ref pom_set_error_language is not thread-safe — see its documentation for more notes.
///
/// Otherwise, libpom is 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_next_unread_key
/// — this will not lead to UB but you may get unexpected results —
/// ensure there is synchronization so that all threads
/// have certainly read their keys before calling it.
///
/// If C11 atomics are not available, you can almost certainly still get away
/// with sharing configurations across threads, as long as you use proper
/// synchronization for \ref pom_conf_next_unread_key.
/// (Essentially, libpom may end up writing the same value to the same address
///  from separate threads, which is *technically* undefined behaviour, but will
///  likely never be an issue on any real machine.)
/// Even if you are extremely paranoid, you can still use
/// distinct configurations in different threads without worry.
///
/// ## Notes
///
/// Every libpom function may change the value of `errno` arbitrarily
/// (its value after any libpom call should be ignored).

/// \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>

#ifdef __cplusplus
extern "C" {
#endif

#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;

/// An opaque iterator produced by \ref pom_conf_next_unread_key.
typedef struct pom_unread_key_iter pom_unread_key_iter;

/// An opaque iterator produced by \ref pom_conf_next_item.
typedef struct pom_item_iter pom_item_iter;

/// An opaque iterator produced by \ref pom_conf_next_key.
typedef struct pom_key_iter pom_key_iter;

/// Item returned by \ref pom_conf_next_item
typedef struct pom_item {
	/// The key.
	///
	/// This pointer is valid until \ref pom_conf_free is called.
	const char *key;
	/// The value of the key.
	///
	/// This pointer is valid until \ref pom_conf_free is called.
	const char *value;
	/// The file where the key was defined.
	///
	/// This pointer is valid until \ref pom_conf_free is called.
	const char *file;
	/// The line number where the key was defined.
	uint64_t line;
} pom_item;

/// Set language for error messages.
///
/// This function is **not** thread-safe. Ensure synchronization between calling
/// this and any functions which can return errors.
///
/// If `lang` is `NULL` or unrecognized, the default of `"en-US"` will be used.
///
/// Currently supported languages:
///
/// - `en-US`
void pom_set_error_language(const char *lang);

/// Load a configuration using a `read`-like function.
///
/// 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.
///
/// 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`.
///
/// `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.
/// `read_func` will not be called excessively/with lots of tiny reads—it's
/// okay to do unbuffered reads in it.
///
/// `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 *` opened in read-binary mode.
///
/// 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`
/// (or set to `NULL` if no memory is available),
/// 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`
/// (or set to `NULL` if no memory is available),
/// 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"`
///
/// Error messages may change arbitrarily in future versions.
///
/// The returned pointer is valid until the error is freed.
///
/// 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.
///
/// The returned pointer is valid until the error is freed.
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, or `NULL` if this is the last error.
///
/// You can only call \ref pom_error_message, \ref pom_error_file, and \ref pom_error_line
/// on the returned error, not \ref pom_error_print or \ref pom_error_to_string.
/// (This is enforced with `const`-ness.)
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(pom_error *error);
#endif

/// Convert error to string. Return value is valid until `error` is `free()`’d.
///
/// Includes every error in an error list (see \ref pom_error_next).
const char *
pom_error_to_string(pom_error *error);

/// Returns `true` if `key` is present in `conf`, `false` otherwise.
bool
pom_conf_has(const pom_conf *conf, const char *key);

/// Get location where `key` was defined in `conf`.
///
/// Returns `true` and sets `*file` and `*line` if the `key` is present.
/// The pointer which `*file` is set to is valid until \ref pom_conf_free is called.
///
/// Returns `false` and sets `*file = NULL`, `*line = 0` if `key` is not present.
///
/// Either of `file`, `line` may be `NULL`, in which case it is not set.
bool
pom_conf_location(const pom_conf *conf, const char *key, const char **file, uint64_t *line);

/// Get value of `key` in configuration, or `NULL` if key is not present.
///
/// The returned pointer is valid until \ref pom_conf_free is called.
const char *
pom_conf_get(const pom_conf *conf, const char *key);

/// Get value of `key` in configuration, or use `dflt` if not present.
///
/// The returned pointer is valid until \ref pom_conf_free is called.
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, returns `""`.
/// If `key` is set but not a valid integer
/// (decimal or `0x`/`0X`-prefixed hexadecimal, absolute value less than 2<sup>53</sup>),
/// returns the value of `key`.
/// `*value` is set to 0 in these cases.
///
/// The returned pointer is valid until \ref pom_conf_free is called.
const char *
pom_conf_get_int(const pom_conf *conf, const char *key, int64_t *value);

/// 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
/// (decimal or `0x`/`0X`-prefixed hexadecimal, absolute value less than 2<sup>53</sup>),
/// returns the value of `key`.
/// `*value` is still set to `dflt` in this case.
///
/// The returned pointer is valid until \ref pom_conf_free is called.
const char *
pom_conf_get_int_or_default(const pom_conf *conf, const char *key, int64_t *value, int64_t dflt);

/// Get unsigned integer value of `key`.
///
/// Returns `NULL` on success, putting the unsigned integer value in `*value`.
///
/// If `key` is not set, returns `""`. If `key` is set but not a valid unsigned integer
/// (decimal or `0x`/`0X`-prefixed hexadecimal, less than 2<sup>53</sup>),
/// returns the value of `key`.
/// `*value` is set to 0 in these case cases.
///
/// The returned pointer is valid until \ref pom_conf_free is called.
const char *
pom_conf_get_uint(const pom_conf *conf, const char *key, uint64_t *value);

/// 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
/// (decimal or `0x`/`0X`-prefixed hexadecimal, less than 2<sup>53</sup>),
/// returns the value of `key`.
/// `*value` is set to 0 in this case.
///
/// The returned pointer is valid until \ref pom_conf_free is called.
const char *
pom_conf_get_uint_or_default(const pom_conf *conf, const char *key, uint64_t *value, uint64_t dflt);

/// Get floating-point value of `key`.
///
/// Returns `NULL` on success, putting the floating-point value in `*value`.
///
/// If `key` is not set, returns `""`. If `key` is set but not a valid floating-point number,
/// returns the value of `key`.
/// `*value` is set to 0.0 in this case.
///
/// The returned pointer is valid until \ref pom_conf_free is called.
const char *
pom_conf_get_float(const pom_conf *conf, const char *key, double *value);

/// 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 the value of `key`.
/// `*value` is still set to `dflt` in this case.
///
/// The returned pointer is valid until \ref pom_conf_free is called.
const char *
pom_conf_get_float_or_default(const pom_conf *conf, const char *key, double *value, double dflt);

/// Get boolean value of `key`.
///
/// Returns `NULL` on success, putting the boolean value in `*value`.
///
/// If `key` is not set, returns `""`. If `key` is set but not a valid boolean
/// (`off`/`false`/`no`/`on`/`true`/`yes`),
/// returns the value of `key`.
/// `*value` is set to `false` in this case.
///
/// The returned pointer is valid until \ref pom_conf_free is called.
const char *
pom_conf_get_bool(const pom_conf *conf, const char *key, bool *value);

/// 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
/// (`off`/`false`/`no`/`on`/`true`/`yes`),
/// returns the value of `key`.
/// `*value` is still set to `dflt` in this case.
///
/// The returned pointer is valid until \ref pom_conf_free is called.
const char *
pom_conf_get_bool_or_default(const pom_conf *conf, const char *key, bool *value, bool dflt);

/// 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.
///
/// Specifically, this returns the configuration consisting of all keys
/// prefixed by `section.`, with that prefix removed, and their corresponding
/// values.
///
/// 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);

/// Get all ‘direct’ keys in `conf` (i.e. all first components of defined keys).
///
/// The first call to this function should be with `*iter = NULL`.
/// Each time it is called with the same `iter`, it returns the next
/// direct key in the `conf`.
/// After all direct keys have been returned (or if an out-of-memory error occurs),
/// `NULL` is returned, any memory
/// associated with the iterator is freed, and `*iter` is set to `NULL`.
/// This function may modify `*iter` arbitrarily — do not attempt
/// to copy `*iter` or compare it with other iterators.
/// The keys are returned in an arbitrary order that may change in future versions.
/// If you decide to stop iterating halfway through,
/// you should still call this function repeatedly until you get `NULL`;
/// otherwise memory associated with the iterator may be leaked.
///
/// The correct usage for this function is:
///
/// ```C
/// pom_key_iter *iter = NULL;
/// const char *key;
/// while ((key = pom_conf_next_key(conf, &iter))) {
///     printf("Key: %s\n", key);
/// }
/// ```
const char *
pom_conf_next_key(const pom_conf *conf, pom_key_iter **iter);

/// Get all key-value pairs in `conf`.
///
/// The first call to this function should be with `*iter = NULL`.
/// Each time it is called with the same `conf` and `iter`, it returns the next
/// key-value pair in `conf`.
/// After all items have been returned (or if an out-of-memory error occurs),
/// `NULL` is returned, any memory
/// associated with the iterator is freed, and `*iter` is set to `NULL`.
/// This function may modify `*iter` arbitrarily — do not attempt
/// to copy `*iter` or compare it with other iterators.
/// The items are returned in an arbitrary order that may change in future versions.
/// If you decide to stop iterating halfway through,
/// you should still call this function repeatedly until you get `NULL`;
/// otherwise memory associated with the iterator may be leaked.
///
/// The returned pointer is only valid until the next call to \ref pom_conf_next_item.
///
/// The correct usage for this function is:
///
/// ```C
/// pom_item_iter *iter = NULL;
/// const pom_item *item;
/// while ((item = pom_conf_next_item(conf, &iter))) {
///     printf("Key: %s, Value: %s\n", item->key, item->value);
/// }
/// ```
const pom_item *
pom_conf_next_item(const pom_conf *conf, pom_item_iter **iter);

/// Create a copy of `conf`.
///
/// The copy must be freed with \ref pom_conf_free.
///
/// Returns `NULL` on 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`.
///
/// Sections obtained from \ref pom_conf_section and iterators
/// (\ref pom_conf_next_unread_key, \ref pom_conf_next_item, \ref pom_conf_next_key)
/// will still point to the old configuration.
/// For this reason, the old configuration is kept in memory until \ref pom_conf_free
/// is called, so if you are repeatedly calling this function (unlikely, but who knows),
/// you should make a copy of `conf` after each call (with \ref pom_conf_copy),
/// then free the old value of `conf`.
///
/// Returns `false` on out-of-memory, in which case `conf` is unchanged.
bool
pom_conf_merge(pom_conf *conf, const pom_conf *other);

/// Get all unread keys in `conf`.
///
/// The first call to this function should be with `*iter = NULL`.
/// Each time it is called with the same `iter`, it returns the next
/// key in the `conf` that has not been accessed with a `pom_conf_get*` function.
/// After all such keys have been returned (or if an out-of-memory error occurs),
/// `NULL` is returned, any memory
/// associated with the iterator is freed, and `*iter` is set to `NULL`.
/// This function may modify `*iter` arbitrarily — do not attempt
/// to copy `*iter` or compare it with other iterators.
/// The keys are returned in an arbitrary order that may change in future versions.
/// If you decide to stop iterating halfway through,
/// you should still call this function repeatedly until you get `NULL`;
/// otherwise memory associated with the iterator may be leaked.
///
/// The correct usage for this function is:
///
/// ```C
/// pom_unread_key_iter *iter = NULL;
/// const char *key;
/// while ((key = pom_conf_next_unread_key(conf, &iter))) {
///     printf("Unknown key: %s\n", key);
/// }
/// ```
const char *
pom_conf_next_unread_key(const pom_conf *conf, pom_unread_key_iter **iter);

#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);

#ifdef __cplusplus
} // extern "C"
#endif

#endif // POM_H_