Login
fossil-core.h at [a5a9733acd]
Login

File include/fossil-scm/fossil-core.h artifact 2f15ac76e3 part of check-in a5a9733acd


/* -*- 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_CORE_H_INCLUDED)
#define NET_FOSSIL_SCM_FSL_CORE_H_INCLUDED
/*
  Copyright (c) 2013 D. Richard Hipp

  This program is free software; you can redistribute it and/or
  modify it under the terms of the Simplified BSD License (also
  known as the "2-Clause License" or "FreeBSD License".)

  This program is distributed in the hope that it will be useful,
  but without any warranty; without even the implied warranty of
  merchantability or fitness for a particular purpose.

  Author contact information:
  drh@hwaci.com
  http://www.hwaci.com/drh/

  *******************************************************************
  This file declares the core SCM-related public APIs.
*/

#include "fossil-scm/fossil-util.h" /* MUST come first b/c of config macros */
#include <time.h> /* struct tm, time_t */
#if defined(__cplusplus)
/*
  The fsl namespace is reserved for an eventual C++ wrapper for the API.
*/
namespace fsl {}
extern "C" {
#endif

/**
   @struct fsl_cx

   The main Fossil "context" type. This is the first argument to
   many Fossil library API routines, and holds all state related
   to a checkout and/or repository and/or global fossil configuration
   database(s).

   An instance's lifetime looks something like this:

   @code
   int rc;
   fsl_cx * f = NULL; // ALWAYS initialize to NULL or results are undefined
   rc = fsl_cx_init( &f, NULL );
   assert(!rc);
   rc = fsl_repo_open( f, "myrepo.fsl" );
   ...use the context, and clean up when done...
   fsl_cx_finalize(f);
   @endcode

   The contents of an fsl_cx instance are strictly private, for use
   only by APIs in this library. Any client-side dependencies on
   them will lead to undefined behaviour at some point.

   Design note: this type is currently opaque to client
   code. Having it non-opaque also has advantages, though, and i'd
   generally prefer that (to allow client-side allocation and
   embedding in other structs). Binary compatibility concerns might
   force us to keep it opaque.

*/

typedef struct fsl_cx fsl_cx;
typedef struct fsl_cx_config fsl_cx_config;
typedef struct fsl_db fsl_db;
typedef struct fsl_cx_init_opt fsl_cx_init_opt;
typedef struct fsl_stmt fsl_stmt;

/**
   This enum defines type ID tags with which the API tags fsl_db
   instances so that the library can figure out which DB is
   which. This is primarily important for certain queries, which
   need to know whether they are accessing the repo or config db,
   for example.

   All that said, i'm not yet fully convinced that a straight port
   of the v1 model is the best option for how we internally manage
   DBs, so this is subject to eventual change or removal.
*/
enum fsl_db_role_t {
/**
   Sentinel "no role" value.
*/
FSL_DB_ROLE_NONE = 0,
/**
   Analog to v1's "configdb".
*/
FSL_DB_ROLE_CONFIG = 0x01,
/**
   Analog to v1's "repository".
*/
FSL_DB_ROLE_REPO = 0x02,
/**
   Analog to v1's "localdb".
*/
FSL_DB_ROLE_CHECKOUT = 0x04,
/**
   Analog to v1's "main", which is basically an alias for the first
   db opened.
*/
FSL_DB_ROLE_MAIN = 0x08
};
typedef enum fsl_db_role_t fsl_db_role_t;

/**
   Roles equate to permissions in Fossil v1. Here we implement them
   as a bitmask and hope we never need more than 31 of them.

   Actually... all of this app-level stuff. The library doesn't
   really care about users (and therefore access rights) at all.
   Nonetheless, the library should provide some base functionality
   for it, based on the v1 model. Clients are of course not
   prohibited from adding their own security models/policies.
*/
enum fsl_user_role_t {
FSL_UROLE_DEFAULT = 0,
FSL_UROLE_GUEST = FSL_UROLE_DEFAULT,
FSL_UROLE_ANONYMOUS = 1,
FSL_UROLE_ADMIN = 1 << 1,
FSL_UROLE_SETUP  = 1 << 2,
FSL_UROLE_READ = 1 << 3,
FSL_UROLE_COMMIT = 1 << 4,
FSL_UROLE_DELETE = 1 << 5,
FSL_UROLE_PASSWORD = 1 << 6,
FSL_UROLE_QUERY = 1 << 7,
FSL_UROLE_WRITE = 1 << 8,
FSL_UROLE_HYPERLINK = 1 << 9,
FSL_UROLE_CLONE = 1 << 10,
FSL_UROLE_WIKI_READ = 1 << 11,
FSL_UROLE_WIKI_CREATE = 1 << 12,
FSL_UROLE_WIKI_APPEND = 1 << 13,
FSL_UROLE_WIKI_EDIT = 1 << 14,
FSL_UROLE_WIKI_MODERATE = 1 << 15,
FSL_UROLE_TKT_READ = 1 << 16,
FSL_UROLE_TKT_NEW = 1 << 17,
FSL_UROLE_TKT_APPEND = 1 << 18,
FSL_UROLE_TKT_WRITE = 1 << 19,
FSL_UROLE_TKT_MODERATE = 1 << 20,
FSL_UROLE_TKT_RPT_FORMAT = 1 << 21,
FSL_UROLE_ATTACH = 1 << 22,
FSL_UROLE_READ_ADDR = 1 << 23,
FSL_UROLE_ZIP = 1 << 24,
FSL_UROLE_PRIVATE = 1 << 25,

FSL_UROLE_ALL = 0x7FFFFFFF
/* unsigned 32-bit+ enums are not portable :/ */
};
typedef enum fsl_user_role_t fsl_user_role_t;



/**
   Bitmask values specifying "configuration sets."  The values in
   this enum come directly from fossil(1), but they are not part of
   the db structure, so may be changed over time.
*/
enum fsl_configset_t {
/** Sentinel value. */
FSL_CONFSET_NONE = 0x000000,
/** Style sheet only */
FSL_CONFIGSET_CSS = 0x000001,
/** WWW interface appearance */
FSL_CONFIGSET_SKIN = 0x000002,
/** Ticket configuration */
FSL_CONFIGSET_TKT = 0x000004,
/** Project name */
FSL_CONFIGSET_PROJ = 0x000008,
/** Shun settings */
FSL_CONFIGSET_SHUN = 0x000010,
/** The USER table */
FSL_CONFIGSET_USER = 0x000020,
/** The CONCEALED table */
FSL_CONFIGSET_ADDR = 0x000040,
/** Transfer configuration */
FSL_CONFIGSET_XFER = 0x000080,
/** Everything */
FSL_CONFIGSET_ALL = 0x0000ff,
/** Causes overwrite instead of merge */
FSL_CONFIGSET_OVERWRITE = 0x100000,
/** Use the legacy format */
FSL_CONFIGSET_OLDFORMAT = 0x200000
};
typedef enum fsl_configset_t fsl_configset_t;

/**
   Runtime-configurable flags for a fsl_cx instance.
*/
enum fsl_cx_flag_t {
FSL_CX_F_NONE = 0,
/**
   Tells us whether or not we want to calculate R-cards by default.
   Historically they were initially required but eventually made
   optional due largely to their memory costs.
*/
FSL_CX_F_CALC_R_CARD = 0x01,
/**
   Internal use only to prevent duplicate initialization of some
   bits.
*/
FSL_CX_F_IS_OPENING_CKOUT = 0x02,

/**
   Default flags for all fsl_cx instances.
*/
FSL_CX_F_DEFAULTS = FSL_CX_F_NONE

};
typedef enum fsl_cx_flag_t fsl_cx_flag_t;

/**
   Most functions in this API which return an int type return error
   codes from the fsl_rc_t enum.  None of these entries are
   (currently) guaranteed to have a specific value across Fossil
   versions except for FSL_RC_OK, which is guaranteed to always be
   0 (and the API guarantees that no other code shall have a value
   of zero).

   The only reasons numbers are hard-coded to the values (or some of
   them) is to simplify debugging during development. Clients may use
   fsl_rc_cstr() to get some human-readable (or programmer-readable)
   form for any given value in this enum.

   Maintenance reminder: as entries are added/changed, update
   fsl_rc_cstr().
*/
enum fsl_rc_t {
/**
   The quintessential not-an-error value.
*/
FSL_RC_OK = 0,
/**
   Generic/unknown error.
*/
FSL_RC_ERROR = 100,
/**
   A placeholder return value for "not yet implemented" functions.
*/
FSL_RC_NYI = 101,
/**
   Out of memory. Indicates that a resource allocation request
   failed.
*/
FSL_RC_OOM = 102,
/*
  API misuse (invalid args)
*/
FSL_RC_MISUSE = 103,
/**
   Some range was violated (function argument, UTF character, etc.).
*/
FSL_RC_RANGE = 104,
/**
   Indicates that access to or locking of a resource was denied
   by some security mechanism or other.
*/
FSL_RC_ACCESS = 105,
/**
   Indicates an I/O error. Whether it was reading or writing is
   context-dependent.
*/
FSL_RC_IO = 106,
/**
   requested resource not found
*/
FSL_RC_NOT_FOUND = 107,
/**
   Indicates that a to-be-created resource already exists.
*/
FSL_RC_ALREADY_EXISTS = 108,
/**
   Data consistency problem
*/
FSL_RC_CONSISTENCY = 109,

/**
   Indicates that the requested repo needs to be rebuilt.
*/
FSL_RC_REPO_NEEDS_REBUILD = 110,

/**
   Indicates that the requested repo is not, in fact, a repo. Also
   used by some APIs to indicate that they require a repository db
   but none has been opened.
*/
FSL_RC_NOT_A_REPO = 111,

/**
   Indicates an attempt to open a too-old or too-new repository db.
*/
FSL_RC_REPO_VERSION = 112,

/**
   Indicates db-level error (e.g. statement prep failed). In such
   cases, the error state of the related db handle (fsl_db) or
   Fossilc context (fsl_cx) will be updated to contain more
   information directly from the db driver.
*/
FSL_RC_DB = 113,

/**
   Used by some iteration routines to indicate that iteration should
   stop prematurely without an error.
*/
FSL_RC_BREAK = 114,

/**
   Indicates that fsl_stmt_step() has fetched a row and the cursor
   may be used to access the current row state (e.g. using
   fsl_stmt_get_int32() and friends). It is strictly illegal to use
   the fsl_stmt_get_xxx() APIs unless fsl_stmt_step() has returned
   this code.
*/
FSL_RC_STEP_ROW = 115,

/**
   Indicates that fsl_stmt_step() has reached the end of the result
   set and that there is no row data to process. This is also the
   result for non-fetching queries (INSERT and friends). It is strictly
   illegal to use the fsl_stmt_get_xxx() APIs after fsl_stmt_step() has
   returned this code.
*/
FSL_RC_STEP_DONE = 116,

/**
   Indicates that a db-level error occurred during a
   fsl_stmt_step() iteration.
*/
FSL_RC_STEP_ERROR = 117,

/**
   Indicates that some data type or logical type is incorrect
   (e.g. an invalid card type in conjunction with a given
   fsl_deck).
*/
FSL_RC_TYPE = 118,

/**
   Indicates that an operation which requires a checkout does not
   have a checkout to work on.
*/
FSL_RC_NOT_A_CHECKOUT = 119,

/**
   Not yet used. Indicates that a repo and checkout do not belong
   together. The public API currently does not allow that to
   happen.
*/
FSL_RC_REPO_MISMATCH = 120,
/**
   Indicates that a checksum comparison failed, possibly indicating
   that corrupted or unexpected data was just read.
*/
FSL_RC_CHECKSUM_MISMATCH = 121,

/**
   This is a special case of FSL_RC_NOT_FOUND, intended specifically
   to differentiate from "file not found in filesystem"
   (FSL_RC_NOT_FOUND) and "fossil does not know about this file" in
   routines for which both might be an error case. An example is a
   an operation which wants to update a repo file with contents
   from the filesystem - the file might not exist or it might not be
   in the current repo db.

   That said, this can also be used for APIs which search for other
   resources (UUIDs, tickets, etc.), but FSL_RC_NOT_FOUND is already
   fairly well entrenched in those cases and is unambiguous, so this
   code is only needed by APIs for which both cases described above
   might happen.
*/
FSL_RC_UNKNOWN_RESOURCE,

/**
   Indicates that a size comparison check failed.

   TODO: remove this if it is not used.
*/
FSL_RC_SIZE_MISMATCH,

/**
   Indicates that an invalid separator was encountered while
   parsing a delta.
*/
FSL_RC_DELTA_INVALID_SEPARATOR,

/**
   Indicates that an invalid size value was encountered while
   parsing a delta.
*/
FSL_RC_DELTA_INVALID_SIZE,

/**
   Indicates that an invalid operator was encountered while parsing
   a delta.
*/
FSL_RC_DELTA_INVALID_OPERATOR,

/**
   Indicates that an invalid terminator was encountered while
   parsing a delta.
*/
FSL_RC_DELTA_INVALID_TERMINATOR,

/**
   Indicates a generic syntax error in a control artifact. Some
   types of manifest-releated errors are reported with more
   specific error codes, e.g. FSL_RC_RANGE if a given Card type
   appears too often.
*/
FSL_RC_CA_SYNTAX,

/**
   Indicates that some value or expression is ambiguous. Typically
   caused by trying to resolve ambiguous symbolic names or partial
   UUIDs to their full UUIDs.
*/
FSL_RC_AMBIGUOUS
};
typedef enum fsl_rc_t fsl_rc_t;

/**
   Filesystem-level permissions flags supported by fossil
   Manifests. Their numeric values are a hard-coded part of the
   Fossil architecture and must not be changed. Note that these
   refer to manifest-level permissions and not filesystem-level
   permissions (though they translate to/from filesystem-level
   meanings at some point).
*/
enum fsl_file_perm_t {
/** Indicates a regular, writable file. */
FSL_FILE_PERM_REGULAR = 0,
/** Indicates a regular file with the executable bit set. */
FSL_FILE_PERM_EXE = 0x1,
/** Indicates a symlink. Note that symlinks do not have the
    executable bit set separately on Unix systems. Also note
    that libfossil does NOT YET IMPLEMENT symlink support
    like fossil(1) does - it currently treats symlinks as
    Unix treats symlinks.
*/
FSL_FILE_PERM_LINK = 0x2
};
typedef enum fsl_file_perm_t fsl_file_perm_t;


/**
   Returns a "standard" string form for a fsl_rc_t code.  The string
   is primarily intended for debugging purposes.  The returned bytes
   are guaranteed to be static and NUL-terminated. They are not
   guaranteed to contain anything useful for any purposes other than
   debugging and tracking down problems.
*/
FSL_EXPORT char const * fsl_rc_cstr(int);

/**
   Returns the value of FSL_LIBRARY_VERSION used to compile the
   library. If this value differs from the value the caller was
   compiled with, Chaos might ensue.

   The API does not yet have any mechanism for determining
   compatibility between repository versions and it also currently
   does no explicit checking to disallow incompatible versions.
*/
FSL_EXPORT char const * fsl_library_version();

/**
   Returns true (non-0) if yourLibVersion compares lexically
   equal to FSL_LIBRARY_VERSION, else it returns false (0).
*/
FSL_EXPORT char fsl_library_version_matches(char const * yourLibVersion);

/**
   This type, accessible to clients via the ::fsl_lib_configurable
   global, contains configuration-related data for the library
   which can be swapped out by clients.
*/
struct fsl_lib_configurable_t {
  /**
     Library-wide allocator. It may be replaced by the client IFF
     it is replaced before the library allocates any memory. The
     default implementation uses the C-standard
     de/re/allocators. Modifying this member while any memory
     allocated through it is still "live" leads to undefined
     results. There is an exception: a "read-only" middleman proxy
     which does not change how the memory is allocated or
     intepreted can safely be swapped in or out at any time
     provided the underlying allocator stays the same and the
     client can ensure that there are no thread-related race
     conditions. e.g. it is legal to swap this out with a proxy
     which logs allocation requests and then forwards the call on
     to the original implementation, and it is legal to do so at
     essentially any time. The important thing this that all of the
     library-allocated memory goes through a single underlying
     (de)allocator for the lifetime of the application.
  */
  fsl_allocator allocator;
};
typedef struct fsl_lib_configurable_t fsl_lib_configurable_t;
extern fsl_lib_configurable_t fsl_lib_configurable;

/**
   A part of the configuration used by fsl_cx_init() and friends.

*/
struct fsl_cx_config {
  /**
     If true, all SQL which goes through the fossil engine
     will be traced to the fsl_output()-configured channel.
  */
  char traceSql;
  /**
     If true, the print() SQL function will output its output to the
     fsl_output()-configured channel, else it is a no-op.
  */
  char sqlPrint;
};

/**
   fsl_cx_config instance initialized with defaults, intended for
   in-struct initialization.
*/
#define fsl_cx_config_empty_m {                 \
    0/*traceSql*/,                              \
      0/*sqlPrint*/                             \
      }

/**
   fsl_cx_config instance initialized with defaults, intended for
   copy-initialization.
*/
extern const fsl_cx_config fsl_cx_config_empty;

/**
   Parameters for fsl_cx_init().
*/
struct fsl_cx_init_opt {
  /**
     The output channel for the Fossil instance.
  */
  fsl_outputer output;
  /**
     Basic configuration parameters.
  */
  fsl_cx_config config;
};


/** Empty-initialized fsl_cx_init_opt instance. */
#define fsl_cx_init_opt_empty_m {fsl_outputer_empty_m, fsl_cx_config_empty_m}
/**
   fsl_cx_init_opt instance initialized to use stdout for output and
   the standard system memory allocator.
*/
#define fsl_cx_init_opt_default_m {fsl_outputer_FILE_m, fsl_cx_config_empty_m}

/** Empty-initialized fsl_cx_init_opt instance. */
extern const fsl_cx_init_opt fsl_cx_init_opt_empty;

/**
   fsl_cx_init_opt instance initialized to use stdout for output and
   the standard system memory allocator. Used as the default when
   fsl_cx_init() is passed a NULL value for this parameter.
*/
extern const fsl_cx_init_opt fsl_cx_init_opt_default;

/**
   Allocates a new fsl_cx instance, which must eventually
   be passed to fsl_cx_finalize() to clean it up.
   Normally clients do not need this - they can simply pass
   a pointer to NULL as the first argument to fsl_cx_init()
   to let it allocate an instance for them.
*/
FSL_EXPORT fsl_cx * fsl_cx_malloc();

/**
   Initializes a fsl_cx instance. tgt must be a pointer to NULL,
   e.g.:

   @code
   fsl_cxt * f = NULL; // NULL is important - see below
   int rc = fsl_cx_init( &f, NULL );
   @endcode

   It is very important that f be initialized to NULL _or_ to an
   instance which has been properly allocated and empty-initialized
   (e.g. via fsl_cx_malloc()). If *tgt is NULL, this routine
   allocates the context, else it assumes the caller did. If f
   points to unitialized memory then results are undefined.

   If the second parameter is NULL then default implementations are
   used for the context's output routine and other options. If it
   is not NULL then param->allocator and param->output must be
   initialized properly before calling this function. The contents
   of param are bitwise copied by this function and ownership of
   the returned value is transfered to *tgt in all cases except
   one:

   If this function cannot allocate a new instance it
   returns FSL_RC_OOM and does not modify *tgt. In this case,
   ownership of param's contents is not changed. On any other
   error, ownership of param's contents are transfered to *tgt and
   the client is responsible for passing *tgt ot
   fsl_cxt_finalize() when he is done with it. Note that (like in
   sqlite3), *tgt may be valid memory even if this function fails,
   and the caller must pass it to fsl_cx_finalize() whether or
   not this function succeeds unless it fails at the initial OOM
   (which the client can check by seeing if (*tgt) is NULL, but
   only if he set it to NULL before calling this).

   Returns 0 on success, FSL_RC_OOM on an allocation error,
   FSL_RC_MISUSE if (!tgt).

   @see fsl_cx_finalize()
   @see fsl_cx_reset()
*/
FSL_EXPORT int fsl_cx_init( fsl_cx ** tgt, fsl_cx_init_opt const * param );

/**
   Clears (most) dynamic state in f, but does not free f and does
   not free "static" state (that set up by the init process). If
   closeDatabases is true then any databases managed by f are
   closed, else they are kept open.

   Client code will not normally need this - it is intended for a
   particular potential memory optimization case. If (and only if)
   closeDatabases is true then after calling this, f may be legally
   re-used as a target for fsl_cx_init().

   This function does not trigger any finializers set for f's
   client state or output channel. It _does_ clear any user name
   set fsl_cx_user_set().

   This is a no-op if !f.
*/
FSL_EXPORT void fsl_cx_reset( fsl_cx * f, char closeDatabases );

/**
   Frees all memory associated with f, which must have been
   allocated/initialized using fsl_cx_malloc(), fsl_cx_init(), or
   equivalent, or created on the stack and properly initialized
   (via fsl_cx_init() or copy-constructed from fsl_cx_empty).

   This function triggers any finializers set for f's client state
   or output channel.

   This is a no-op if !f and is effectively a no-op if f has no
   state to destruct.
*/
FSL_EXPORT void fsl_cx_finalize( fsl_cx * f );


/**
   Sets or unsets one or more option flags on the given fossil
   context.  flags is the flag or a bitmask of flags to set (from
   the fsl_cx_flag_t enum).  If enable is true the flag(s) is (are)
   set, else it (they) is (are) unset. Returns the new set of
   flags, or -1 if !f.
*/
FSL_EXPORT int fsl_cx_flag_set( fsl_cx * f, int flags, char enable );

/**
   Returns f's flags, or -1 if !f.
*/
FSL_EXPORT int fsl_cx_flags_get( fsl_cx * f );

/**
   Sets the Fossil error state to the given error code and
   fsl_appendf()-style format string/arguments. On success it
   returns the code parameter. It does not return 0 unless code is
   0, and if it returns a value other than code then something went
   seriously wrong (e.g. allocation error: FSL_RC_OOM) or the
   arguments were invalid: !f results in FSL_RC_MISUSE.

   If !fmt then fsl_rc_cstr(code) is used to create the
   error string.

   As a special case, if code is FSL_RC_OOM, no error string is
   allocated (because it would likely fail, assuming the OOM
   is real).

   As a special case, if code is 0 (the non-error value) then fmt is
   ignored and any error state is cleared.
*/
FSL_EXPORT int fsl_cx_err_set( fsl_cx * f, int code, char const * fmt, ... );

/**
   va_list counterpart to fsl_cx_err_set().
*/
FSL_EXPORT int fsl_cx_err_setv( fsl_cx * f, int code, char const * fmt,
                                va_list args );

/**
   Fetches the error state from f. See fsl_error_get() for the semantics
   of the parameters and return value.
*/
FSL_EXPORT int fsl_cx_err_get( fsl_cx * f, char const ** str, fsl_size_t * len );

/**
   Returns f's error state object. This pointer is guaranteed by the
   API to be stable until f is finalized, but its contents are
   modified my routines as part of the error reporting process.

   Returns NULL if !f.
*/
FSL_EXPORT fsl_error const * fsl_cx_err_get_e(fsl_cx const * f);

/**
   Resets's f's error state, basically equivalent to
   fsl_cx_err_set(f,0,NULL). Is a no-op if f is NULL.  This may be
   necessary for apps if they rely on looking at fsl_cx_err_get()
   at the end of their app/routine, because error state survives
   until it is cleared, even if the error held there was caught and
   recovered. This function might keep error string memory around
   for re-use later on.
*/
FSL_EXPORT void fsl_cx_err_reset(fsl_cx * f);

/**
   Replaces f's error state with the contents of err, taking over
   any memory owned by err (but not err itself). Returns the new
   error state code (the value of err->code before this call) on
   success. The only error case is if !f (FSL_RC_MISUSE). If err is
   NULL then f's error state is cleared and 0 is returned. err's
   error state is cleared by this call.
*/
FSL_EXPORT int fsl_cx_err_set_e( fsl_cx * f, fsl_error * err );

/**
   If f has error state then it outputs its error state to its
   output channel and returns the result of fsl_output(). Returns
   FSL_RC_MISUSE if !f, 0 if f has no error state our output of the
   state succeeds. If addNewline is true then it adds a trailing
   newline to the output, else it does not.

   This is intended for testing and debugging only, and not as an
   error reporting mechanism for a full-fledged application.
*/
FSL_EXPORT int fsl_cx_err_report( fsl_cx * f, char addNewline );

/**
   Moves db->error's state into f. If db is NULL then f's primary
   db connection is used. Returns FSL_RC_MISUSE if !f or (!db &&
   f-is-not-opened). On success it returns f's new error code.

   The main purpose of this function is to propagate db-level
   errors up to higher-level code which deals directly with the f
   object but not the underlying db(s).
*/
FSL_EXPORT int fsl_cx_uplift_db_error( fsl_cx * f, fsl_db * db );

/**
   Outputs the first n bytes of src to f's configured output
   channel. Returns 0 on success, FSL_RC_MISUSE if (!f || !src),
   0 (without side effects) if !n, else it returns the result of
   the underlying output call. This is a harmless no-op if f is
   configured with no output channel.

   @see fsl_outputf()
   @see fsl_flush()
*/
FSL_EXPORT int fsl_output( fsl_cx * f, void const * src, fsl_size_t n );

/**
   Flushes f's output channel. Returns 0 on success, FSL_RC_MISUSE
   if !f. If the flush routine is NULL then this is a harmless
   no-op.

   @see fsl_outputf()
   @see fsl_output()
*/
FSL_EXPORT int fsl_flush( fsl_cx * f );

/**
   Uses fsl_appendf() to append formatted output to the channel
   configured for use with fsl_output(). Returns 0 on success,
   FSL_RC_MISUSE if !f or !fmt, FSL_RC_RANGE if !*fmt, and
   FSL_RC_IO if the underlying fsl_appendf() operation fails.

   Note, however, that due to the printf()-style return semantics
   of fsl_appendf(), it is not generically possible to distinguish
   a partially-successful (i.e. failed in the middle) write from
   success. e.g. if fmt contains a format specifier which performs
   memory allocation and that allocation fails, it is unlikely that
   this function will be able to be aware of that error. The only
   way to fix that is to change the return semantics of
   fsl_appendf() (and adjust any existing code which relies on
   them).

   @see fsl_output()
   @see fsl_flush()
*/
FSL_EXPORT int fsl_outputf( fsl_cx * f, char const * fmt, ... );

/**
   va_list counterpart to fsl_outputf().
*/
FSL_EXPORT int fsl_outputfv( fsl_cx * f, char const * fmt, va_list args );

/**
   Opens the given db file name as f's repository. Returns 0 on
   success. On error it sets f's error state and returns that code
   unless the error was FSL_RC_MISUSE (which indicates invalid
   arguments and it does not set the error state).

   Fails with FSL_RC_MISUSE if !f, !repoDbFile, !*repoDbFile. Returns
   FSL_RC_ACCESS if f already has an opened repo db.

   Returns FSL_RC_NOT_FOUND if repoDbFile is not found, as this
   routine cannot create a new repository db.

   When a repository is opened, the fossil-level user name
   associated with f (if any) is overwritten with the default user
   from the repo's login table (the one with uid=1). Thus
   fsl_cx_user_get() may return a value even if the client has not
   called fsl_cx_user_set().

   It would be nice to have a parameter specifying that the repo
   should be opened read-only. That's not as straightforward as it
   sounds because of how the various dbs are internally managed
   (via one db handle). Until then, the permissions of the
   underlying repo file will determine how it is opened. i.e. a
   read-only repo will be opened read-only.


   Potentially interesting side-effects:

   - On success this re-sets the "allow-symlinks" option on f to
   match that of the opened repo, keeping its current setting if
   the repo specifies no value for that option.

   @see fsl_repo_create()
   @see fsl_repo_close()
*/
FSL_EXPORT int fsl_repo_open( fsl_cx * f, char const * repoDbFile/*, char readOnlyCurrentlyIgnored*/ );

/**
   If fsl_repo_open_xxx() or fsl_checkout_open_dir() has been
   used to open a respository db, this call closes that db and
   returns 0. Returns FSL_RC_MISUSE if !f. FSL_RC_NOT_FOUND if f
   has not opened a repository.

   If the repo is the "main" db, this also closes any associated
   checkout and config dbs.

   If a repository is opened "indirectly" via
   fsl_checkout_open_dir() then clients must not close it using
   this function; they should allow it to be closed implicitly when
   the checkout db is closed.

   @see fsl_repo_open()
   @see fsl_repo_create()
*/
FSL_EXPORT int fsl_repo_close( fsl_cx * f );

/**
   Sets or clears (if userName is NULL or empty) the default
   repository user name for operations which require one.

   Returns 0 on success, FSL_RC_MISUSE if f is NULL,
   FSL_RC_OOM if copying of the userName fails.

   Example usage:
   @code
   char * u = fsl_guess_user_name();
   int rc = fsl_cx_user_set(f, u);
   fsl_free(u);
   @endcode

   (Sorry about the extra string copy there, but adding a function
   which passes ownership of the name string seems like overkill.)
*/
FSL_EXPORT int fsl_cx_user_set( fsl_cx * f, char const * userName );

/**
   Returns the name set by fsl_cx_user_set(), or NULL if f has no
   default user name set. The returned bytes are owned by f and may
   be invalidated by any call to fsl_cx_user_set().
*/
FSL_EXPORT char const * fsl_cx_user_get( fsl_cx const * f );

/**
   Configuration parameters for fsl_repo_create().  Always
   copy-construct these from fsl_repo_create_opt_empty
   resp. fsl_repo_create_opt_empty_m in order to ensure proper
   behaviour vis-a-vis default values.

   TODOs:

   - Add project name/description, and possibly other
   configuration bits.

   - Allow client to set password for default user (currently set
   randomly, as fossil(1) does).
*/
struct fsl_repo_create_opt {
  /**
     The file name for the new repository.
  */
  char const * filename;
  /**
     Fossil user name for the admin user in the new repo.  If NULL,
     defaults to the Fossil context's user (see
     fsl_cx_user_get()). If that is NULL, it defaults to
     "root" for historical reasons.
  */
  char const * username;

  /**
     The comment text used for the initial commit. If NULL or empty
     (starts with a NUL byte) then no initial check is
     created. fossil(1) is largely untested with that scenario (but
     it seems to work), so for compatibility it is not recommended
     that this be set to NULL.

     The default value (when copy-initialized) is "egg". There's a
     story behind the use of "egg" as the initial checkin comment,
     and it all started with a typo: "initial chicken"
  */
  char const * commitMessage;

  /**
     If not NULL and not empty, fsl_repo_create() will use this
     repository database to copy the configuration, copying over
     the following settings:

     - The reportfmt table, overwriting any existing entries.

     - The user table fields (cap, info, mtime, photo) are copied
     for the "system users".  The system users are: anonymous,
     nobody, developer, reader.

     - The vast majority of the config table is copied, arguably
     more than it should (e.g. the 'manifest' setting).
  */
  char const * configRepo;

  /**
     If false, fsl_repo_create() will fail if this->filename
     already exists.
  */
  char allowOverwrite;
};
typedef struct fsl_repo_create_opt fsl_repo_create_opt;

/** Initialized-with-defaults fsl_repo_create_opt struct, intended
    for in-struct initialization. */
#define fsl_repo_create_opt_empty_m {           \
    NULL/*filename*/,                           \
      NULL/*username*/,                         \
      "egg"/*commitMessage*/,                   \
      NULL/*configRepo*/,                       \
      0/*allowOverwrite*/                       \
      }

/** Initialized-with-defaults fsl_repo_create_opt struct, intended
    for copy-initialization. */
extern const fsl_repo_create_opt fsl_repo_create_opt_empty;

/**
   Creates a new repository database using the options provided in
   the second argument. If f is not NULL, it must be a valid
   context instance, though it need not have an opened
   checkout/repository (if it does, it/they will be closed by this
   operation). If f is NULL, a temporary context is used for
   creating the repository, in which case the caller will not have
   access to detailed error information (only the result code) if
   this operation fails. The opt argument may not be NULL.

   If opt->allowOverwrite is false (0) and the file exists, it
   fails with FSL_RC_ALREADY_EXISTS, otherwise is
   creates/overwrites the file. This is a destructive operation if
   opt->allowOverwrite is true (non-0), so be careful.

   This operation installs the various "static" repository schemas
   into the db, sets up some default settings, and installs a
   default user.

   This operation always closes any repository/checkout opened by f
   because setting up the new db requires wiring it to f to set up
   some of the db-side infrastructure. The one exception is if
   argument validation fails, in which case f's
   repo/checkout-related state are not modified.

   See the fsl_repo_create_opt docs for more details regarding the
   creation options.

   On success, 0 is returned and f (if not NULL) is left with the
   new repository opened and ready for use. On error, f's error
   state is updated and any number of the FSL_RC_xxx codes may be
   returned - there are no less than 30 different _potential_ error
   conditions on the way to creating a new repository.

   Example usage:

   @code
   fsl_repo_create_opt opt = fsl_repo_create_opt_empty;
   int rc;
   opt.filename = "my.fossil";
   // ... any other opt.xxx you want to set, e.g.:
   // opt.user = "fred";
   // Assume fsl is a valid fsl_cx instance:
   rc = fsl_repo_create(fsl, &opt );
   if(rc) { ...error... }
   else {
   fsl_db * db = fsl_cx_db_repo(f);
   assert(db); // == the new repo db
   ...
   }
   @endcode

   @see fsl_repo_open()
   @see fsl_repo_close()
*/
FSL_EXPORT int fsl_repo_create(fsl_cx * f, fsl_repo_create_opt const * opt );

/**
   UNTESTED.

   Returns true if f has an opened repository database which is
   opened in read-only mode, else returns false.
*/
FSL_EXPORT char fsl_repo_is_readonly(fsl_cx const * f);

/**
   Tries to open a checked-out fossil repository db in the given
   directory. It looks for files named one of (_FOSSIL_,
   .fslckout), in that order, in the given directory. If neither is
   found then it moves up the path one directory and tries again,
   until it hits the root of the dirPath (see below for a
   note/caveat).

   If dirName is NULL then it behaves as if it had been passed the
   absolute path of the current directory (as determined by
   fsl_getcwd()).

   If dirName is not NULL and dirNameLen is <0 then fsl_strlen() is
   used to calculate dirName's len.

   Achtung: if dirName is relative, this routine might not find a
   checkout where it would find one if given an absolute path
   (because it traverses the path string given it instead of its
   canonical form). Wether this is a bug or a feature is not yet
   clear. When in doubt, use fsl_file_canonical_name() to normalize
   the directory name before passing it in here. If it turns out
   that we always want that behaviour, this routine will/should be
   modified to canonicalize the name.

   If this routine finds/opens a checkout, it also tries to open
   the repository database from which the checkout derives (and
   fails if it cannot).

   Returns 0 on success. If there is an error opening or validating
   the checkout or its repository db, f's error state will be
   updated. Error codes/conditions include:

   - FSL_RC_MISUSE if f is NULL.

   - FSL_RC_ACCESS if f already has and opened checkout.

   - FSL_RC_OOM if an allocation fails.

   - FSL_RC_NOT_FOUND if no checkout is foud or if a checkout's
   repository is not found.

   - FSL_RC_RANGE if dirname is not NULL but has a length of 0,
   either because 0 was passed in for dirNameLen or because
   dirNameLen was negative and *dirName is a NUL byte.

   - Various codes from fsl_getcwd() (if dirName is NULL).

   - Various codes if opening the associated repository DB fails.

   TODO: there's really nothing in the architecture which restricts
   a checkout db to being in the same directory as the checkout,
   except for some historical bits which "could" be refactored. It
   "might be interesting" to eventually provide a variant which
   opens a checkout db file directly. We have the infrastructure,
   just need some refactoring. We would need to add the working
   directory path to the checkout db's config, but should otherwise
   require no trickery or incompatibilities with fossil(1).
*/
FSL_EXPORT int fsl_checkout_open_dir( fsl_cx * f, char const * dirName, fsl_int_t dirNameLen );

/**
   If fsl_checkout_open_dir() has been used to open a checkout db,
   this call closes that db and returns 0. Returns FSL_RC_MISUSE if
   !f, FSL_RC_NOT_FOUND if f has not opened a checkout (which can
   safely be ignored and does not update f's error state).

   This also closes the repository implicitly opened for the
   checkout.

   If the checkout is the "main" db, this also closes any
   associated config db.
*/
FSL_EXPORT int fsl_checkout_close( fsl_cx * f );

/**
   Closes any opened databases (repo/checkout/config). Any errors
   triggered by (e.g.) trying to close an unopened db are
   suppressed.
*/
FSL_EXPORT void fsl_cx_close_dbs( fsl_cx * f );

/**
   Returs version information for the current checkout.

   If f is not NULL and has an opened checkout then...

   If uuid is not NULL then *uuid is set to the UUID of the opened
   checkout. If rid is not NULL, *rid is set to the record ID of
   that checkout. The returned uuid bytes and rid are valid until
   the library closes the checkout db or updates its state to a
   newer checkout version. When in doubt about lifetime issues,
   copy the UUID immediately after calling this if they will be
   needed later.

   Corner case: a new repo with no checkins has an RID of 0
   and a UUID of NULL.

   If f is NULL or has no checkout then *uuid will be set to NULL
   and *rid will be set to 0.
*/
FSL_EXPORT void fsl_checkout_version_info(fsl_cx *f, fsl_id_t * rid, fsl_uuid_cstr * uuid );

/**
   If f is not NULL and has a checkout db opened then this function
   returns its name. The bytes are valid until that checkout db
   connection is closed. If len is not NULL then *len is (on
   success) assigned to the length of the returned string, in
   bytes. The string is NUL-terminated, so fetching the length (by
   passing a non-NULL 2nd parameter) is optional.

   Returns NULL if !f or f has no checkout opened.

   @see fsl_checkout_open_dir()
   @see fsl_cx_checkout_dir_name()
   @see fsl_cx_db_file_config()
   @see fsl_cx_db_file_repo()
*/
FSL_EXPORT char const * fsl_cx_db_file_checkout(fsl_cx const * f,
                                                fsl_size_t * len);

/**
   Equivalent to fsl_checkout_db_file() except that
   it applies to the name of the opened repository db,
   if any.

   @see fsl_cx_db_file_checkout()
   @see fsl_cx_db_file_config()
*/
FSL_EXPORT char const * fsl_cx_db_file_repo(fsl_cx const * f,
                                            fsl_size_t * len);

/**
   Equivalent to fsl_checkout_db_file() except that
   it applies to the name of the opened config db,
   if any.

   @see fsl_cx_db_file_checkout()
   @see fsl_cx_db_file_repo()
*/
FSL_EXPORT char const * fsl_cx_db_file_config(fsl_cx const * f,
                                              fsl_size_t * len);

/**
   Similar to fsl_cx_db_file_checkout() and friends except that it
   applies to db file implied by the specified role (2nd
   parameter). If no such role is opened, or the role is invalid,
   NULL is returned.
*/
FSL_EXPORT char const * fsl_cx_db_file_for_role(fsl_cx const * f,
                                                fsl_db_role_t r,
                                                fsl_size_t * len);

/**
   Similar to fsl_cx_db_file_checkout() and friends except that it
   applies to db name implied by the specified role (2nd
   parameter). If no such role is opened, or the role is invalid,
   NULL is returned.

   This is the "easiest" way to figure out the DB name of the given
   role, independent of what order f's databases were opened
   (because the first-opened db is always called 'main').
*/
FSL_EXPORT char const * fsl_cx_db_name_for_role(fsl_cx const * f,
                                                fsl_db_role_t r,
                                                fsl_size_t * len);

/**
   If f has an opened checkout db (from fsl_checkout_open_dir())
   then this function returns the directory part of the path
   for the checkout. The returned bytes are valid until that db
   connection is closed. If len is not NULL then *len is (on
   success) assigned to the length of the returned string, in bytes.
   The string is NUL-terminated, so fetching the length (by passing
   a non-NULL 2nd parameter) is optional.

   Returns NULL if !f or f has no checkout opened.

   @see fsl_checkout_open_dir()
   @see fsl_checkout_db_file()
*/
FSL_EXPORT char const * fsl_cx_checkout_dir_name(fsl_cx const * f,
                                                 fsl_size_t * len);

/**
   Returns a handle to f's main db, or NULL if !f. The returned
   handle is valid as long as that db remains open. As a rule,
   it is invalidated by fsl_checkout_close() and
   fsl_repo_close(). The object is owned by f and the client MUST NOT
   do any of the following:

   - Close the db handle.

   - Use transactions without using fsl_db_transaction_begin()
   and friends.

   - Fiddle with the handle's internals. Doing so might confuse its
   owning context.

   Clients MAY add new user-defined functions, use the handle with
   fsl_db_prepare(), and other "mundane" db-related tasks.

   Design notes:

   The current architecture designates the first db opened for a
   context as its "main" db, and the other databases (if opened)
   get attached to that one. As long as there are no table name
   collisions across databases, this does not affect how queries
   are formulated. It does, however, pose problems when
   constructing full-qualified db table names.

   @see fsl_cx_db_repo()
   @see fsl_cx_db_checkout()
*/
FSL_EXPORT fsl_db * fsl_cx_db( fsl_cx * f );

/**
   If f is not NULL and has had its repo opened via
   fsl_repo_open(), fsl_checkout_open_dir(), or similar, this
   returns a pointer to that database, else it returns NULL.

   @see fsl_cx_db()
*/
FSL_EXPORT fsl_db * fsl_cx_db_repo( fsl_cx * f );

/**
   If f is not NULL and has had a checkout opened via
   fsl_checkout_open_dir() or similar, this returns a pointer to that
   database, else it returns NULL.

   @see fsl_cx_db()
*/
FSL_EXPORT fsl_db * fsl_cx_db_checkout( fsl_cx * f );

/**
   A helper which fetches f's repository db. If f has no repo db
   then it sets f's error state to FSL_RC_NOT_A_REPO with a message
   describing the requirement, then returns NULL.  Returns NULL if
   !f.
*/
FSL_EXPORT fsl_db * fsl_needs_repo(fsl_cx * f);

/**
   The checkout-db counterpart of fsl_needs_repo().
*/
FSL_EXPORT fsl_db * fsl_needs_checkout(fsl_cx * f);


/**
   Opens the given database file as f's configuration database. If f
   already has a config database opened, it is closed before opening
   the new one. The database is created and populated with an
   initial schema if needed.

   If dbName is NULL or empty then it uses a default db name,
   "probably" under the user's home directory. To get the name of
   the database after it has been opened/attached, use
   fsl_cx_db_file_config().

   TODO: strongly consider supporting non-attached use of
   the config db. Comments in v1 suggest that it is possible
   to lock the config db for other apps when it is attached
   to a long-running op by a fossil process.

   @see fsl_cx_db_config()
   @see fsl_config_close()
*/
FSL_EXPORT int fsl_config_open( fsl_cx * f, char const * dbName );

/**
   Closes/detaches the database connection opened by
   fsl_config_open(). Returns 0 on succes, FSL_RC_MISUSE if !f,
   FSL_RC_NOT_FOUND if no config db connection is opened/attached.

   @see fsl_cx_db_config()
   @see fsl_config_open()
*/
FSL_EXPORT int fsl_config_close( fsl_cx * f );

/**
   If f has an opened/attached configuration db then its handle is
   returned, else 0 is returned.

   @see fsl_config_open()
   @see fsl_config_close()
*/
FSL_EXPORT fsl_db * fsl_cx_db_config( fsl_cx * f );

/**
   Convenience form of fsl_db_prepare() which uses f's main db.
   Returns 0 on success, FSL_RC_MISUSE if !f or !sql, FSL_RC_RANGE
   if !*sql.
*/
FSL_EXPORT int fsl_cx_prepare( fsl_cx *f, fsl_stmt * tgt, char const * sql, ... );

/**
   va_list counterpart of fsl_cx_prepare().
*/
FSL_EXPORT int fsl_cx_preparev( fsl_cx *f, fsl_stmt * tgt, char const * sql, va_list args );

/**
   Wrapper around fsl_db_last_insert_id() which uses f's main
   database. Returns -1 if !f or f has no opened db.

   @see fsl_cx_db()
*/
FSL_EXPORT fsl_id_t fsl_cx_last_insert_id(fsl_cx *f);

/**
   Returns the raw SQL code for a Fossil global config database.
*/
FSL_EXPORT char const * fsl_schema_config();

/**
   Returns the raw SQL code for the "static" parts of a Fossil
   repository database. These are the parts which are immutable
   (for the most part) between Fossil versions. They change _very_
   rarely.
*/
FSL_EXPORT char const * fsl_schema_repo1();

/**
   Returns the raw SQL code for the "transient" parts of a Fossil
   repository database - any parts which can be calculated via data
   held in the primary "static" schemas. These parts are
   occassionally recreated, e.g. via a 'rebuild' of a repository.
*/
FSL_EXPORT char const * fsl_schema_repo2();

/**
   Returns the raw SQL code for a Fossil checkout database.
*/
FSL_EXPORT char const * fsl_schema_checkout();

/**
   Returns the raw SQL code for a Fossil checkout db's
   _default_ core ticket-related tables.

   @see fsl_cx_schema_ticket()
*/
FSL_EXPORT char const * fsl_schema_ticket();

/**
   If f's opened repository has a config non-empty entry named
   'ticket-table', this returns its text via appending it to
   pOut. If no entry is found, fsl_schema_ticket() is appended to
   pOut.

   Returns 0 on success. On error the contents of pOut must not be
   considered valid but pOut might be partially populated.
*/
FSL_EXPORT int fsl_cx_schema_ticket(fsl_cx * f, fsl_buffer * pOut);

/**
   Returns the raw SQL code for Fossil ticket reports schemas.
   This gets installed as needed into repository databases.
*/
FSL_EXPORT char const * fsl_schema_ticket_reports();

/**
   Works similarly to fsl_stat(), except that zName must refer to a
   path under f's current checkout directory. Note that this stats
   local files, not repository-level content.

   If relativeToCwd is true (non-0) then the filename is
   resolved/canonicalized based on the current working directory
   (see fsl_getcwd()), otherwise f's current checkout directory is
   used as the virtual root. This makes a subtle yet important
   difference in how the name is resolved. Applications taking
   input from users (e.g. CLI apps) will normally want to resolve
   from the current working dir (assuming the filenames were passed
   in from the CLI). In a GUI environment, where the current
   directory is likely not the checkout root, resolving based on
   the checkout root (i.e. relativeToCwd=0) is probably saner.

   Returns 0 on success. Errors include, but are not limited to:

   - FSL_RC_MISUSE if !f or !zName.

   - FSL_RC_NOT_A_CHECKOUT if f has no opened checkout.

   - If fsl_is_simple_pathname(zName) returns false then
   fsl_checkout_filename_check() is used o normalize the name. If
   that fails, its failure core is returned.

   - As for fsl_stat().

   See fsl_stat() for more details regarding the tgt parameter.

   TODO: fossil-specific symlink support. Currently it does not
   distinguish between symlinks and non-links.

   @see fsl_cx_stat2()
*/
FSL_EXPORT int fsl_cx_stat( fsl_cx * f, char relativeToCwd,
                            char const * zName, fsl_fstat * tgt );

/**
   This works identically to fsl_cx_stat(), but provides more
   information about the file being stat'd.

   If nameOut is not NULL then the resolved/normalized path to to
   that file is appended to nameOut. If fullPath is true then an
   absolute path is written to nameOut, otherwise a
   checkout-relative path is written.

   Returns 0 on success. On stat() error, nameOut is not updated,
   but after stat()'ing, allocation of memory for nameOut's buffer
   may fail.

   If zName ends with a trailing slash, that slash is retained in
   nameOut.

   @see fsl_cx_stat()
*/
FSL_EXPORT int fsl_cx_stat2( fsl_cx * f, char relativeToCwd, char const * zName,
                             fsl_fstat * tgt, fsl_buffer * nameOut, char fullPath);


/**
   Sets the case-sensitivity flag for f to the given value. This
   flag alters how some filename-search/comparison operations
   operate. This option is only intended to have an effect on
   plaforms with case-insensitive filesystems.

   @see fsl_cx_is_case_sensitive()
*/
FSL_EXPORT void fsl_cx_case_sensitive_set(fsl_cx * f, char caseSensitive);

/**
   Returns true (non-0) if f is set for case-sensitive filename
   handling, else 0. Returns 0 if !f.

   @see fsl_cx_case_sensitive_set()
*/
FSL_EXPORT char fsl_cx_is_case_sensitive(fsl_cx const * f);

/**
   If f is set to use case-sensitive filename handling,
   returns a pointer to an empty string, otherwise a pointer
   to the string "COLLATE nocase" is returned.
   Results are undefined if f is NULL. The returned bytes
   are static. 

   @see fsl_cx_case_sensitive_set()
   @see fsl_cx_is_case_sensitive()
*/
FSL_EXPORT char const * fsl_cx_filename_collation(fsl_cx const * f);

/**
   An enumeration of the types of control artifacts used by
   Fossil. Their numeric values (with the exception of
   FSL_TYPE_CATYPE_INVALID) are a hard-coded part of the Fossil db
   architecture and must never be changed.
*/
enum fsl_catype_t {
/**
   Sentinel value used for some error reporting.
*/
FSL_CATYPE_INVALID = -1,
/**
   Sentinel value used to mark a deck as being "any" type. This is
   a placeholder on a deck's way to completion.
*/
FSL_CATYPE_ANY = 0,
/**
   Indicates a "manifest" artifact (a checkin record).
*/
FSL_CATYPE_CHECKIN = 1,
/**
   Indicates a "cluster" artifact. These are used during synchronization.
*/
FSL_CATYPE_CLUSTER = 2,
/**
   Indicates a "control" artifact (a tag change).
*/
FSL_CATYPE_CONTROL = 3,
/**
   Indicates a "wiki" artifact.
*/
FSL_CATYPE_WIKI = 4,
/**
   Indicates a "ticket" artifact.
*/
FSL_CATYPE_TICKET = 5,
/**
   Indicates an "attachment" artifact (used in the ticketing
   subsystem).
*/
FSL_CATYPE_ATTACHMENT = 6,
/**
   Indicates an "event" artifact (kind of like a blog entry).
*/
FSL_CATYPE_EVENT = 7
};
typedef enum fsl_catype_t fsl_catype_t;

/**
   Returns some arbitrary but distinct string for the given
   fsl_catype_t. The returned bytes are static and
   NUL-terminated. Intended primarily for debugging and informative
   purposes, not actual user output.
*/
FSL_EXPORT char const * fsl_catype_cstr(fsl_catype_t t);

/**
   For a given artifact type, it returns the key string used in the
   event.type db table. Returns NULL if passed an unknown value or
   a type which is not used in the event table, otherwise the
   returned bytes are static and NUL-terminated.

   The returned strings for a given type are as follows:

   - FSL_CATYPE_ANY returns "*"
   - FSL_CATYPE_CHECKIN returns "ci"
   - FSL_CATYPE_WIKI returns "w"
   - FSL_CATYPE_TAG returns "g"
   - FSL_CATYPE_TICKET returns "t"
   - FSL_CATYPE_EVENT returns "e"

   The other control artifact types to not have representations
   in the event table, and NULL is returned for them.

   All of the returned values can be used in comparison clauses in
   queries on the event table's 'type' field (but use GLOB instead
   of '=' so that the "*" returned by FSL_ATYPE_ANY can match!).
   For example, to get the comments from the most recent 5 commits:

   @code
   SELECT
   datetime(mtime),
   coalesce(ecomment,comment),
   user
   FROM event WHERE type='ci'
   ORDER BY mtime DESC LIMIT 5; 
   @endcode

   Where 'ci' in the SQL is the non-NULL return value from this
   function. When escaping this value via fsl_buffer_appendf() (or
   anything functionally similar), use the %%q/%%Q format
   specifiers to escape it.
*/
FSL_EXPORT char const * fsl_catype_event_cstr(fsl_catype_t t);  

/**
   Resolves client-provided symbol as an artifact's db record ID.
   f must have an opened repository db, and some symbols can only
   be looked up if it has an opened checkout (see the list below).

   Returns 0 and sets *rv to the id if it finds an unambiguous
   match.

   Returns FSL_RC_MISUSE if !f, !sym, !*sym, or !rv.

   Returns FSL_RC_NOT_A_REPO if f has no opened repository.

   Returns FSL_RC_AMBIGUOUS if sym is a partial UUID which matches
   multiple full UUIDs.

   Returns FSL_RC_NOT_FOUND if it cannot find anything.

   Symbols supported by this function:

   - SHA1 hash
   - SHA1 hash prefix of at least 4 characters
   - Symbolic Name
   - "tag:" + symbolic name
   - Date or date-time 
   - "date:" + Date or date-time
   - symbolic-name ":" date-time
   - "tip"
   - "root:" resolves to the root manifest of the given checkin. In
   the trunk this will always resolve to the first "empty checkin"
   manifest.

   The following additional forms are available in local checkouts:

   - "current"
   - "prev" or "previous"
   - "next"

   If type is not FSL_CATYPE_ANY then it will only match artifacts
   of the specified type. In order to resolve arbitrary UUIDs, e.g.
   those of arbitrary blob content, type needs to be
   FSL_CATYPE_ANY.

*/
FSL_EXPORT int fsl_sym_to_rid( fsl_cx * f, char const * sym, fsl_catype_t type,
                               fsl_id_t * rv );

/**
   Similar to fsl_sym_to_rid() but on success if returns a UUID
   string by assigning it to *rv. If rid is not NULL then on
   success the db record ID corresponding to the returned UUID is
   assigned to *rid. The caller must eventually free the returned
   string memory by passing it to fsl_free().
*/
FSL_EXPORT int fsl_sym_to_uuid( fsl_cx * f, char const * sym,
                                fsl_catype_t type, fsl_uuid_str * rv,
                                fsl_id_t * rid );

/**
   Searches f's repo database for the a blob with the given uuid
   (any unique UUID prefix). On success a positive record ID is
   returned. On error one of several unspecified negative values is
   returned. If no uuid match is found 0 is returned.

   Error cases include: either argument is NULL, uuid does not
   appear to be a full or partial UUID (or is too long),
   uuid is ambiguous (try providing a longer one)

   This implementation is more efficient when given a full,
   valid UUID (one for which fsl_is_uuid() returns true).
*/
FSL_EXPORT fsl_id_t fsl_uuid_to_rid( fsl_cx * f, char const * uuid );

/**
   The opposite of fsl_uuid_to_rid(), this returns the UUID string
   of the given blob record ID. Ownership of the string is passed
   to the caller and it must eventually be freed using
   fsl_free(). Returns NULL on error (invalid arguments or f has no
   repo opened) or if no blob record is found. If no record is
   found, f's error state is updated with an explanation of the
   problem.
*/
FSL_EXPORT fsl_uuid_str fsl_rid_to_uuid(fsl_cx * f, fsl_id_t rid);

/**
   This works identically to fsl_uuid_to_rid() except that it will
   only resolve to a UUID if an artifact matching the given type has
   that UUID. If no entry is found, f's error state gets updated
   with a description of the problem.

   This can be used to distinguish artifact UUIDs from file blob
   content UUIDs by passing the type FSL_CATYPE_ANY. A non-artifact
   blob will return NULL in that, but any artifact type will match
   (assuming rid is valid).
*/
FSL_EXPORT fsl_uuid_str fsl_rid_to_artifact_uuid(fsl_cx * f, fsl_id_t rid,
                                                 fsl_catype_t type);


/**
   A collection of bitmaskable values indicating categories
   of fossil-standard glob sets. These correspond to the following
   configurable settings:

   ignore-glob, crnl-glob, binary-glob
*/
enum fsl_glob_category_t{
/** Corresponds to the ignore-glob config setting. */
FSL_GLOBS_IGNORE = 0x01,
/** Corresponds to the crnl-glob config setting. */
FSL_GLOBS_CRNL = 0x02,
/** Corresponds to the binary-glob config setting. */
FSL_GLOBS_BINARY = 0x04,
/** A superset of all config-level glob categories. */
FSL_GLOBS_ANY = 0xFF
};
typedef enum fsl_glob_category_t fsl_glob_category_t;

/**
   Checks one or more of f's configurable glob lists to see if str
   matches one of them. If it finds a match, it returns a pointer to
   the matching glob (as per fsl_glob_list_matches()), the bytes
   of which are owned by f and may be invalidated via modification
   or reloading of the underlying glob list. In generally the return
   value can be used as a boolean - clients generally do not need
   to know exactly which glob matched.

   gtype specifies the glob list(s) to check in the form of a
   bitmask of fsl_glob_category_t values. Note that the order of the
   lists is unspecified, so if that is important for you then be
   sure that gtype only specifies one glob list
   (e.g. FSL_GLOBS_IGNORE) and call it again (e.g. passing
   FSL_GLOBS_BINARY) if you need to distinguish between those two
   cases.

   str must be a non-NULL, non-empty empty string.

   Returns NULL if !f, !str, !*str, gtype does not specify any known
   glob list(s), or no glob match is found.

   Performance is, abstractly speaking, horrible, because we're
   comparing arbitrarily long lists of glob patterns against an
   arbitrary string. That said, it's fast enough for our purposes.
*/
FSL_EXPORT char const * fsl_cx_glob_matches( fsl_cx * f, int gtype,
                                             char const * str );

#if 0
/**
   DO NOT USE - not yet tested and ready.

   Returns the result of either localtime(clock) or gmtime(clock),
   depending on f:

   - If f is NULL, returns localtime(clock).

   - If f has had its FSL_CX_F_LOCALTIME_GMT flag set (see
   fsl_cx_flag_set()) then returns gmtime(clock), else
   localtime(clock).

   If clock is NULL, NULL is returned.

   Note that fsl_cx instances default to using UTC for everything,
   which is the opposite of fossil(1).
*/
struct tm * fsl_cx_localtime( fsl_cx const * f, const time_t * clock );

/**
   Equivalent to fsl_cx_localtime(NULL, clock).
*/
struct tm * fsl_localtime( const time_t * clock );

/**
   DO NOT USE - not yet tested and ready.

   This function passes (f, clock) to fsl_cx_localtime(),
   then returns the result of mktime(3) on it. So...
   it adjusts a UTC Unix timestamp to either the same UTC
   local timestamp or to the local time.
*/
time_t fsl_cx_time_adj(fsl_cx const * f, time_t clock);
#endif

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