/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ #if !defined(ORG_FOSSIL_SCM_FSL_CONFDB_H_INCLUDED) #define ORG_FOSSIL_SCM_FSL_CONFDB_H_INCLUDED /* Copyright 2013-2021 The Libfossil Authors, see LICENSES/BSD-2-Clause.txt SPDX-License-Identifier: BSD-2-Clause-FreeBSD SPDX-FileCopyrightText: 2021 The Libfossil Authors SPDX-ArtifactOfProjectName: Libfossil SPDX-FileType: Code Heavily indebted to the Fossil SCM project (https://fossil-scm.org). */ /** @file confdb.h fossil-confdb.h declares APIs dealing with fossil's various configuration option storage backends. */ #include "core.h" /* MUST come first b/c of config macros */ #if defined(__cplusplus) extern "C" { #endif /** A flag type for specifying which configuration backend a given API should be applied to. Used by most of the fsl_config_XXX() APIS, e.g. fsl_config_get_int32() and friends. This type is named fsl_confdb_e (not the "db" part) because 3 of the 4 fossil-supported config storage backends are databases. The "versioned setting" backend, which deals with non-db local files, was encapsulated into this API long after the db-related options were */ enum fsl_confdb_e { /** Sentinel value. Not for use with public APIs. */ FSL_CONFDB_NONE = 0, /** Signfies the global-level (per system user) configuration area. Note that fsl_config_... APIs which use this do not currently open that database on demand due to the potential for downstream locking-related issues when the config db is left open, as well as locking-related issues here when a separate client or instance is holding that db open. That behaviour may change in the future. */ FSL_CONFDB_GLOBAL = 1, /** Signfies the repository-level configuration area. */ FSL_CONFDB_REPO = 2, /** Signfies the checkout-level (a.k.a. "local") configuration area. */ FSL_CONFDB_CKOUT = 3, /** The obligatory special case... Versionable settings are stored directly in SCM-controlled files, each of which has the same name as the setting and lives in the .fossil-settings directory of a checkout. Though versionable settings _can_ be read from a non-checked-out repository, doing so requires knowning which version to fetch and is horribly inefficient, so there are currently no APIs for doing so. Note that the APIs which read and write versioned settings do not care whether those settings are valid for fossil(1). Reminder to self: SQL to find the checkins in which a versioned settings file was added or modified (ignoring renames and branches and whatnot). ``` select strftime('%Y-%m-%d %H:%M:%S',e.mtime) mtime, b.rid, b.uuid from mlink m, filename f, blob b, event e where f.fnid=m.fnid and m.mid=b.rid and b.rid=e.objid and f.name='.fossil-settings/ignore-glob' order by e.mtime desc ; ``` */ FSL_CONFDB_VERSIONABLE = 4 }; typedef enum fsl_confdb_e fsl_confdb_e; /** Returns the name of the db table associated with the given mode. Results are undefined if mode is an invalid value. The returned bytes are static and constant. Returns NULL for the role FSL_CONFDB_VERSIONABLE. */ FSL_EXPORT char const * fsl_config_table_for_role(fsl_confdb_e mode); /** Returns a handle to the db associates with the given fsl_confdb_e value. Returns NULL if !f or if f has no db opened for that configuration role. Results are undefined if mode is an invalid value. For FSL_CONFDB_VERSIONABLE it returns the results of fsl_cx_db(), even though there is no database-side support for versionable files (which live in files in a checkout). */ FSL_EXPORT fsl_db * fsl_config_for_role(fsl_cx * const f, fsl_confdb_e mode); /** Returns the int32 value of a property from one of f's config dbs, as specified by the mode parameter. Returns dflt if !f, f does not have the requested config db opened, no entry is found, or on db-level errors. */ FSL_EXPORT int32_t fsl_config_get_int32( fsl_cx * const f, fsl_confdb_e mode, int32_t dflt, char const * key ); /** int64_t counterpart of fsl_config_get_int32(). */ FSL_EXPORT int64_t fsl_config_get_int64( fsl_cx * const f, fsl_confdb_e mode, int64_t dflt, char const * key ); /** fsl_id_t counterpart of fsl_config_get_int32(). */ FSL_EXPORT fsl_id_t fsl_config_get_id( fsl_cx * const f, fsl_confdb_e mode, fsl_id_t dflt, char const * key ); /** double counterpart of fsl_config_get_int32(). */ FSL_EXPORT double fsl_config_get_double( fsl_cx * const f, fsl_confdb_e mode, double dflt, char const * key ); /** Boolean countertpart of fsl_config_get_int32(). fsl_str_bool() is used to determine the booleanness (booleanity?) of a given config option. */ FSL_EXPORT bool fsl_config_get_bool( fsl_cx * const f, fsl_confdb_e mode, bool dflt, char const * key ); /** A convenience form of fsl_config_get_buffer(). If it finds a config entry it returns its value. If *len is not NULL then *len is assigned the length of the returned string, in bytes (and is set to 0 if NULL is returned). Any errors encounters while looking for the entry are suppressed and NULL is returned. The returned memory must eventually be freed using fsl_free(). If len is not NULL then it is set to the length of the returned string. Returns NULL for any sort of error or for a NULL db value. If capturing error state is important for a given use case, use fsl_config_get_buffer() instead, which provides the same features as this one but propagates any error state. */ FSL_EXPORT char * fsl_config_get_text( fsl_cx * const f, fsl_confdb_e mode, char const * key, fsl_size_t * len ); /** The fsl_buffer-type counterpart of fsl_config_get_int32(). Replaces the contents of the given buffer (re-using any memory it might have) with a value from a config region. Returns 0 on success, FSL_RC_NOT_FOUND if no entry was found or the requested db is not opened, FSL_RC_OOM on allocation errror. If mode is FSL_CONFDB_VERSIONABLE, this operation requires a checkout and returns (if possible) the contents of the file named {CHECKOUT_ROOT}/.fossil-settings/{key}. On any sort of error, including the inability to find or open a versionable-settings file, non-0 is returned: - FSL_RC_OOM on allocation error - FSL_RC_NOT_FOUND if either the database referred to by mode is not opened or a matching setting cannot be found. - FSL_RC_ACCESS is a versionable setting file is found but cannot be opened for reading. - FSL_RC_NOT_A_CKOUT if mode is FSL_CONFDB_VERSIONABLE and no checkout is opened. - Potentially one of several db-related codes if reading a non-versioned setting fails. In the grand scheme of things, the inability to load a setting is not generally an error, so clients are not expected to treat it as fatal unless perhaps it returns FSL_RC_OOM, in which case it likely is. @see fsl_config_has_versionable() */ FSL_EXPORT int fsl_config_get_buffer( fsl_cx * const f, fsl_confdb_e mode, char const * key, fsl_buffer * const b ); /** If f has an opened checkout, this replaces b's contents (re-using any existing memory) with an absolute path to the filename for that setting: {CHECKOUT_ROOT}/.fossil-settings/{key} This routine neither verifies that the given key is a valid versionable setting name nor that the file exists: it's purely a string operation. Returns 0 on success, FSL_RC_NOT_A_CKOUT if f has no checkout opened, FSL_RC_MISUSE if key is NULL, empty, or if fsl_is_simple_pathname() returns false for the key, and FSL_RC_OOM if appending to the buffer fails. */ FSL_EXPORT int fsl_config_versionable_filename(fsl_cx *f, char const * key, fsl_buffer *b); /** Sets a configuration variable in one of f's config databases, as specified by the mode parameter. Returns 0 on success. val may be NULL. Returns FSL_RC_MISUSE if !f, f does not have that database opened, or !key, FSL_RC_RANGE if !key. If mode is FSL_CONFDB_VERSIONABLE, it attempts to write/overwrite the corresponding file in the current checkout. If mem is NULL and mode is not FSL_CONFDB_VERSIONABLE then an SQL NULL is bound instead of an empty blob. For FSL_CONFDB_VERSIONABLE an empty file will be written for that case. If mode is FSL_CONFDB_VERSIONABLE, this does NOT queue any newly-created versionable setting file for inclusion into the SCM. That is up to the caller. See fsl_config_versionable_filename() for info about an additional potential usage error case with FSL_CONFDB_VERSIONABLE. Pedantic side-note: the input text is saved as-is. No trailing newline is added when saving to FSL_CONFDB_VERSIONABLE because doing so would require making a copy of the input bytes just to add a newline to it. The non-text fsl_config_set_XXX() APIs add a newline when writing to their values out to a versionable config file because it costs them nothing to do so and text files "should" have a trailing newline. Potential TODO: if mode is FSL_CONFDB_VERSIONABLE and the key contains directory components, e,g, "app/x", we should arguably use fsl_mkdir_for_file() to create those components. As of this writing (2021-03-14), no such config keys have ever been used in fossil. @see fsl_config_versionable_filename() */ FSL_EXPORT int fsl_config_set_text( fsl_cx * const f, fsl_confdb_e mode, char const * key, char const * val ); /** The blob counterpart of fsl_config_set_text(). If len is negative then fsl_strlen(mem) is used to determine the length of the memory. If mem is NULL and mode is not FSL_CONFDB_VERSIONABLE then an SQL NULL is bound instead of an empty blob. For FSL_CONFDB_VERSIONABLE an empty file will be written for that case. */ FSL_EXPORT int fsl_config_set_blob( fsl_cx * const f, fsl_confdb_e mode, char const * key, void const * mem, fsl_int_t len ); /** int32 counterpart of fsl_config_set_text(). */ FSL_EXPORT int fsl_config_set_int32( fsl_cx * const f, fsl_confdb_e mode, char const * key, int32_t val ); /** int64 counterpart of fsl_config_set_text(). */ FSL_EXPORT int fsl_config_set_int64( fsl_cx * const f, fsl_confdb_e mode, char const * key, int64_t val ); /** fsl_id_t counterpart of fsl_config_set_text(). */ FSL_EXPORT int fsl_config_set_id( fsl_cx * const f, fsl_confdb_e mode, char const * key, fsl_id_t val ); /** fsl_double counterpart of fsl_config_set_text(). */ FSL_EXPORT int fsl_config_set_double( fsl_cx * const f, fsl_confdb_e mode, char const * key, double val ); /** Boolean counterpart of fsl_config_set_text(). For compatibility with fossil conventions, the value will be saved in the string form "on" or "off". When mode is FSL_CONFDB_VERSIONABLE, that value will include a trailing newline. */ FSL_EXPORT int fsl_config_set_bool( fsl_cx * const f, fsl_confdb_e mode, char const * key, bool val ); /** "Unsets" (removes) the given key from the given configuration database. It is not considered to be an error if the config table does not contain that key. Returns FSL_RC_UNSUPPORTED, without side effects, if mode is FSL_CONFDB_VERSIONABLE. It "could" hypothetically remove a checked-out copy of a versioned setting, then queue the file for removal in the next checkin, but it does not do so. It might, in the future, be changed to do so, or at least to remove the local settings file. */ FSL_EXPORT int fsl_config_unset( fsl_cx * const f, fsl_confdb_e mode, char const * key ); /** Begins (or recurses) a transaction on the given configuration database. Returns 0 on success, non-0 on error. On success, fsl_config_transaction_end() must eventually be called with the same parameters to pop the transaction stack. Returns FSL_RC_MISUSE if no db handle is opened for the given configuration mode. Assuming all arguments are valid, this returns the result of fsl_db_transaction_end() and propagates any db-side error into the f object's error state. This is primarily intended as an optimization when an app is making many changes to a config database. It is not needed when the app is only making one or two changes. @see fsl_config_transaction_end() @see fsl_db_transaction_begin() */ FSL_EXPORT int fsl_config_transaction_begin(fsl_cx * const f, fsl_confdb_e mode); /** Pops the transaction stack pushed by fsl_config_transaction_begin(). If rollback is true then the transaction is set roll back, otherwise it is allowed to continue (if recursive) or committed immediately (if not recursive). Returns 0 on success, non-0 on error. Returns FSL_RC_MISUSE if no db handle is opened for the given configuration mode. Assuming all arguments are valid, this returns the result of fsl_db_transaction_end() and propagates any db-side error into the f object's error state. @see fsl_config_transaction_begin() @see fsl_db_transaction_end() */ FSL_EXPORT int fsl_config_transaction_end(fsl_cx * const f, fsl_confdb_e mode, bool rollback); /** Populates li as a glob list from the given configuration key. Uses (versionable/repo/global) config settings, in that order. It is not an error if one or more of those sources is missing - they are simply skipped. Note that gets any new globs appended to it, as per fsl_glob_list_append(), as opposed to replacing any existing contents. Returns 0 on success, but that only means that there were no errors, not that any entries were necessarily added to li. Arguably a bug: this function does not open the global config if it was not already opened, but will use it if it is opened. This function should arbuably open and close it in that case. That said, there are cases which prohibit closing of the config db and this function cannot know if one of those cases is active. */ FSL_EXPORT int fsl_config_globs_load(fsl_cx * const f, fsl_list * const li, char const * key); /** Fetches the preferred name of the "global" db file for the current user by assigning it to *zOut. Returns 0 on success, in which case *zOut is updated and non-0 on error, in which case *zOut is not modified. On success, ownership of *zOut is transferred to the caller, who must eventually free it using fsl_free(). The locations searched for the database file are platform-dependent... Unix-like systems are searched in the following order: 1) If the FOSSIL_HOME environment var is set, use $FOSSIL_HOME/.fossil. 2) If $HOME/.fossil already exists, use that. 3) If XDG_CONFIG_HOME environment var is set, use $XDG_CONFIG_HOME/fossil.db. 4) If $HOME/.config is a directory, use $HOME/.config/fossil.db 5) Fall back to $HOME/.fossil (historical name). Except where listed above, this function does not check whether the file already exists or is a database. Windows: - We need a Windows port of this routine. Currently it `#error`'s out at compile-time on Windows. */ FSL_EXPORT int fsl_config_global_preferred_name(char ** zOut); /** A variant of fsl_config_get_int32() which can search through multiple config sources in an order specified by the caller. zCfg must be a NUL-terminated list of case-sensitive letters corresponding to config sources: any of (c, r, v, g) for checkout, repo, versionable, and global, respectively. Each source, if available, is searched, in the order provided, until either the given key is found in that source or no more sources are available, in which case dflt is returned. Any missing sources are silently skipped over (e.g. if 'c' is provided by no checkout is opened, that is not considered an error by this function). Errors are silently ignored and cause dflt to be returned. @see fsl_configs_get_int64() @see fsl_configs_get_id() @see fsl_configs_get_bool() @see fsl_configs_get_text() */ FSL_EXPORT int32_t fsl_configs_get_int32(fsl_cx * const f, char const * zCfg, int32_t dflt, char const * key); /** The int64_t counterpart of fsl_configs_get_int32(). */ FSL_EXPORT int64_t fsl_configs_get_int64(fsl_cx * const f, char const * zCfg, int64_t dflt, char const * key); /** The fsl_id_t counterpart of fsl_configs_get_int32(). */ FSL_EXPORT fsl_id_t fsl_configs_get_id(fsl_cx * const f, char const * zCfg, fsl_id_t dflt, char const * key); /** The bool counterpart of fsl_configs_get_int32(). */ FSL_EXPORT bool fsl_configs_get_bool(fsl_cx * const f, char const * zCfg, bool dflt, char const * key); /** The double counterpart of fsl_configs_get_int32(). */ FSL_EXPORT double fsl_configs_get_double(fsl_cx * const f, char const * zCfg, double dflt, char const * key); /** The buffer counterpart of fsl_configs_get_int32(). This variant collects, if found, its results in the given buffer (its memory, if any, is reused by this routine). It returns 0 if an entry is found, FSL_RC_OOM on buffer allocation error, and FSL_RC_NOT_FOUND if no match is found in any of the listed sources (noting that missing sources are silently skipped). The full range of errors reported by fsl_config_get_buffer() is not supported here: only FSL_RC_OOM and FSL_RC_NOT_FOUND are reported. @see fsl_configs_get_text() */ FSL_EXPORT int fsl_configs_get_buffer( fsl_cx * const f, char const *zCfg, char const * key, fsl_buffer * const b ); /** The C-string counterpart of fsl_configs_get_int32(). If the final argument is not NULL then if a match is found then its length (in bytes) is assigned to (*len). Returns NULL if no match is found. If non-NULL is returned, ownership is transfered to the caller, who must eventually pass the value to fsl_free(). Note that this function silently ignores any errors, e.g. allocation failure for the result string will be reported by returning NULL (which is indistinguishable from it not finding a match). @see fsl_configs_get_buffer() */ FSL_EXPORT char * fsl_configs_get_text(fsl_cx * const f, char const * zCfg, char const * key, fsl_size_t * len); #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* ORG_FOSSIL_SCM_FSL_CONFDB_H_INCLUDED */