/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 
/* vim: set ts=2 et sw=2 tw=80: */
#if !defined(NET_FOSSIL_SCM_FSL_CONFDB_H_INCLUDED)
#define NET_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).


  ******************************************************************************
  This file declares public APIs for working with fossil's persistent
  configuration settings.
*/

#include "fossil-core.h" /* MUST come first b/c of config macros */

#if defined(__cplusplus)
extern "C" {
#endif

/**
   A flag type for specifying which configuration database a given
   API should be applied to. Used by fsl_config_get_int32() and
   friends.
*/
enum fsl_confdb_t {
/**
   Signfies the global-level (per system user) configuration area.
*/
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,
/**
   Signifies the versioned-level (via ".fossil-settings") configuration area.
*/
FSL_CONFDB_VERSIONED = 4,
/**
   Signifies the default-level (and other settings metadata)
   configuration area. This area is read-only, and only contains the
   publicly documented settings.
*/
FSL_CONFDB_METADATA = 5
};
typedef enum fsl_confdb_t fsl_confdb_t;

/**
   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.
*/
FSL_EXPORT char const * fsl_config_table_for_role(fsl_confdb_t mode);

/**
   Returns a handle to the db associates with the given fsl_confdb_t
   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.
*/
FSL_EXPORT fsl_db * fsl_config_for_role(fsl_cx * f, fsl_confdb_t 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 * f, fsl_confdb_t 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 * f, fsl_confdb_t 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 * f, fsl_confdb_t mode,
                            fsl_id_t dflt, char const * key );
/**
   double counterpart of fsl_config_get_int32().
*/
FSL_EXPORT double fsl_config_get_double( fsl_cx * f, fsl_confdb_t 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 char fsl_config_get_bool( fsl_cx * f, fsl_confdb_t mode,
                                     char dflt, char const * key );

/**
   The string-type counterpart of fsl_config_get_int32().

   Returns a copy of the configuration value if it finds one, else
   returns NULL.

   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.
*/
FSL_EXPORT char * fsl_config_get_text( fsl_cx * f, fsl_confdb_t mode,
                            char const * key, fsl_size_t * len );

/**
   Like fsl_config_get_text() with one notable exception: regardless
   of the mode parameter if key refers to a versionable property
   and such a property is readable in the current checkout, the
   contents of that versioned property is returned (as per
   fsl_config_get_versionable()), otherwise this behaves exactly
   like fsl_config_get_text().
*/
FSL_EXPORT char * fsl_config_get_text2( fsl_cx * f, fsl_confdb_t mode,
                             char const * key, fsl_size_t * len );


/**
   fsl_buffer counterpart of fsl_config_get_text(). Appends the
   value from the config table to b. Returns 0 on success,
   FSL_RC_NOT_FOUND if no row was found or the requested db is not
   opened, FSL_RC_OOM on allocation errror.
*/
FSL_EXPORT int fsl_config_get_buffer( fsl_cx * f, fsl_confdb_t mode,
                           char const * key, fsl_buffer * b );

/**
   fsl_buffer counterpart of fsl_config_get_text2(). Appends the
   value from the config table to b. Returns 0 on success,
   FSL_RC_NOT_FOUND if no row was found or the requested db is not
   opened, FSL_RC_OOM on allocation errror.
*/
FSL_EXPORT int fsl_config_get_buffer2( fsl_cx * f, fsl_confdb_t mode,
                            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 val is NULL then an SQL NULL is bound instead of an empty
   string.
*/
FSL_EXPORT int fsl_config_set_text( fsl_cx * f, fsl_confdb_t 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 then an SQL NULL is bound instead of an empty
   blob.
*/
FSL_EXPORT int fsl_config_set_blob( fsl_cx * f, fsl_confdb_t 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 * f, fsl_confdb_t mode,
                          char const * key, int32_t val );
/**
   int64 counterpart of fsl_config_set_text().
*/
FSL_EXPORT int fsl_config_set_int64( fsl_cx * f, fsl_confdb_t 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 * f, fsl_confdb_t 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 * f, fsl_confdb_t mode,
                           char const * key, double val );
/**
   Boolean counterpart of fsl_config_set_text().

   See fsl_str_bool() for what string values are considered to be
   "true" vs "false".
*/
FSL_EXPORT int fsl_config_set_bool( fsl_cx * f, fsl_confdb_t mode,
                         char const * key, char 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.
*/
FSL_EXPORT int fsl_config_unset( fsl_cx * f, fsl_confdb_t 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 * f, fsl_confdb_t 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 * f, fsl_confdb_t mode, char rollback);

/**
   Holds metadata for fossil-defined configuration settings.

   @see fsl_config_ctrl_get()
   @see fsl_config_has_versionable()
   @see fsl_config_get_versionable()
   @see fsl_config_key_is_valid()
   @see fsl_config_key_is_versionable()
   @see fsl_config_key_default_value()
*/  
struct fsl_config_ctrl {
  /** Name of the setting */
  char const *name;
  /**
     Historical (fossil(1)) internal variable name used by
     db_set(). Not currently used by this impl.
  */
  char const *var;
  /**
     Historical (HTML UI). Width of display.  0 for boolean values.
  */
  int width;
  /**
     Is this setting versionable?
  */
  int versionable;
  /**
     Default value
  */
  char const *def;
};
typedef struct fsl_config_ctrl fsl_config_ctrl;

/**
   If key is the name of a fossil-defined config key, this returns
   the fsl_config_ctrl value describing that configuration property,
   else it returns NULL.
*/
FSL_EXPORT fsl_config_ctrl const * fsl_config_ctrl_get(char const * key);

/**
   Returns true if key is the name of a config property
   as defined by fossil(1).
*/
FSL_EXPORT char fsl_config_key_is_valid(char const * key);


/**
   Returns true if key is the name of a versionable property,
   as defined by fossil(1).
*/
FSL_EXPORT char fsl_config_key_is_versionable(char const * key);

/**
   If key refers to a fossil-defined configuration setting, this
   returns its default value as a NUL-terminated string. Its bytes are
   static and immutable. Returns NULL if key is not a known
   configuration key.
*/
FSL_EXPORT char const * fsl_config_key_default_value(char const * key);

/**
   Tries to load a "versionable property" in a file named
   CKOUT/.fossil-settings/KEY, where CKOUT is the root directory of
   f's current checkout and key is the name of a versionable
   configuration setting.

   If it finds one and it can read the file's contents, it returns 0
   and assigns *pOut to the contents, which are owned by the caller,
   who must eventually fsl_free() them. As a special case, if an
   empty file is read, 0 is returned but *pOut will be NULL.

   On any sort of error, non-0 is returned. In the grand scheme of
   things, the inability to load a versionable setting is not truly
   any error, so f's error state is not updated by this function and
   clients are not expected to treat it as fatal unless perhaps it
   returns FSL_RC_OOM, in which case it likely is.

   Note that versionable properties are always treated as strings,
   and there is not (yet) a family of getter APIs for various value
   types as there is for the db-level configuration storage.

   Example usage:

   @code
   char const * rv = NULL;
   int rc = fsl_config_get_versionable(f, "ignore-glob", &rv);
   if(rc){
   // error. Just for example's sake:
   assert(!rv);
   }else{
   assert(rv);
   ...use rv...
   }
   @endcode

*/
FSL_EXPORT int fsl_config_get_versionable( fsl_cx * f, char const * key, char **pOut );

/**
   Returns true if f's current checkout contains the given
   versionable configuration setting, else false.

   @see fsl_config_ctrl
*/
FSL_EXPORT char fsl_config_has_versionable( fsl_cx * f, char const * key );

/**
   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.
*/
FSL_EXPORT int fsl_config_globs_load(fsl_cx * f, fsl_list * 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 simply uses
   the Windows home directory + "/_fossil" or "/.fossil", depending on
   the build-time environment.
*/
FSL_EXPORT int fsl_config_global_preferred_name(char ** zOut);

#if defined(__cplusplus)
} /*extern "C"*/
#endif
#endif
/* NET_FOSSIL_SCM_FSL_CONFDB_H_INCLUDED */
