/// \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. /// /// 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 #endif #include #include #include #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; /// Settings for libpom. /// /// Start by initializing this to zero, /// then fill out only the fields you need. typedef struct pom_settings { /// Data to pass to allocation functions. void *allocator_udata; /// calloc function to use for all purposes, or `NULL` to use libc `calloc`. void *(*calloc)(void *udata, size_t nmemb, size_t sz); /// realloc function to use for all purposes, or `NULL` to use libc `realloc`. void *(*realloc)(void *udata, void *ptr, size_t new_sz); /// free function to use for all purposes, or `NULL` to use libc `free`. void (*free)(void *udata, void *ptr); /// Language for error messages. /// /// Should be an IETF-style language tag. /// Closest-matching implemented language will be used (e.g. `"fr-CA"` will use `"fr"`), /// or else `"en"` if there are no matches. /// /// Current supported languages: `en`, `fr`. char error_lang[16]; /// Reserved for future use. Must be set to 0. void *reserved[4]; } pom_settings; /// 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. /// /// `settings` can be `NULL`, in which case the default settings are used. /// /// 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 returns the number of bytes read, or 0 if the end of the file was reached. /// It can fill out as much or as little of the buffer as it wants — /// a short read count is not interpreted as the end of the file. /// `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 pom_settings *settings, 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 pom_settings *settings, 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 pom_settings *settings, 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 pom_settings *settings, 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 or is not a valid integer /// (decimal or `0x`/`0X`-prefixed hexadecimal, absolute value less than 253), /// returns an error. /// `*value` is set to 0 in these cases. /// /// The returned error must be `free()`d. 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 /// (decimal or `0x`/`0X`-prefixed hexadecimal, absolute value less than 253), /// returns an error. /// `*value` is still set to `dflt` in this case. /// /// The returned error must be `free()`d. 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 is not a valid unsigned integer /// (decimal or `0x`/`0X`-prefixed hexadecimal, less than 253), /// returns an error. /// `*value` is set to 0 in these case cases. /// /// The returned error must be `free()`d. 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 /// (decimal or `0x`/`0X`-prefixed hexadecimal, less than 253), /// returns an error. /// `*value` is set to 0 in this case. /// /// The returned error must be `free()`d. 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 is set but not a valid floating-point number, /// returns an error. /// `*value` is set to 0.0 in this case. /// /// The returned error must be `free()`d. 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. /// `*value` is still set to `dflt` in this case. /// /// The returned error must be `free()`d. 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 is set but not a valid boolean /// (`off`/`false`/`no`/`on`/`true`/`yes`), /// returns an error. /// `*value` is set to `false` in this case. /// /// The returned error must be `free()`d. 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 /// (`off`/`false`/`no`/`on`/`true`/`yes`), /// returns an error. /// `*value` is still set to `dflt` in this case. /// /// The returned error must be `free()`d. 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. /// /// 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_