/* -*- 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 /* struct tm, time_t */ #if defined(__cplusplus) 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 database. ** ** An instance's lifetime looks like: ** ** @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" ); ** ... ** 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, /** ** Indicates that when "crosslinking" a CONTROL manifest, timeline ** event entries should _not_ be created for T cards (tags). The ** default, for historical reasons, is to add 'g' events. ** ** Note that the event table is generated locally for each copy of ** a repository, based on input from the manifests. Thus this only ** applies to the current copy of the repo. When the artifacts are ** synced into other repos (or somehow back into this repo, e.g. ** via a rebuild) then the timeline entry(ies) elided due to this ** flag will appear. The timeline is largely an artifact of ** fossil(1), not this library, and it is not clear if this library ** should maintain the timeline in its current form. ** ** Please help us find a better name for this flag. */ FSL_CX_F_MF_TAGS_NO_EVENT = 0x01, /** ** Don't use this - it is almost certainly not useful at the ** library level. ** ** Used by fsl_cx_localtime() to figure out whether to convert times ** to GMT or not. Note that this conversion is different from UTC ** in some cases! ** ** This has no effect on the core operations of libfossil, nor on ** data which comes from artifacts (e.g. the D-card is always left ** as-is), but may change time strings for some output routines. */ FSL_CX_F_LOCALTIME_GMT = 0x02, /** ** 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 = 0x04, /** ** Default flags for all fsl_cx instances. */ FSL_CX_F_DEFAULTS = FSL_CX_F_CALC_R_CARD }; 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. ** ** 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, /** ** Immutable 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, FSL_RC_CHECKSUM_MISMATCH = 121, FSL_RC_SIZE_MISMATCH, FSL_RC_DELTA_INVALID_SEPARATOR, FSL_RC_DELTA_INVALID_SIZE, FSL_RC_DELTA_INVALID_OPERATOR, 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, /* ...more to come... */ FSL_RC_TRAILING_COMMA_KLUDGE /* don't ask. hint: emacs macros */ }; typedef enum fsl_rc_t fsl_rc_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. */ 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, but it also currently ** does no explicit checking to disallow incompatible versions. */ char const * fsl_library_version(); /** ** Returns true (non-0) if yourLibVersion compares lexically ** equal to FSL_LIBRARY_VERSION, else it returns false (0). */ 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 ** allocated memory goes through a single underlying 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_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() */ 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. */ 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. ** ** This function triggers any finializers set for f's ** client state or output channel. ** ** This is a no-op if !f. */ 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. */ int fsl_cx_flag_set( fsl_cx * f, int flags, char enable ); /** ** Returns f's flags, or -1 if !f. */ 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. */ int fsl_cx_err_set( fsl_cx * f, int code, char const * fmt, ... ); /** ** va_list counterpart to fsl_cx_err_set(). */ 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. */ int fsl_cx_err_get( fsl_cx * f, char const ** str, fsl_size_t * len ); /** ** Clear's f's error state, if any - 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. */ void fsl_cx_err_clear(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. */ 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. */ 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), else it returns f's new error code (as will be ** returned by fsl_cx_err_get()). */ 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. */ 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. */ 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 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. */ int fsl_outputf( fsl_cx * f, char const * fmt, ... ); /** va_list counterpart to fsl_outputf(). */ 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. */ int fsl_repo_open( fsl_cx * f, char const * repoDbFile ); /** ** 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. */ int fsl_repo_close( fsl_cx * f ); /** ** Sets or clears (if userName is NULL) 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.) */ 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(). */ 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 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 */ int fsl_repo_create(fsl_cx * f, fsl_repo_create_opt const * opt ); /** ** Not yet implemented. Waiting on an internal way to easily know the ** db's name by looking at whether or not a checkout is the main db. */ 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()). ** ** 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 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). */ 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. ** ** This also closes the repository implicitly opened for the ** checkout. ** ** If the repo is the "main" db, this also closes any associated ** config db. */ int fsl_checkout_close( fsl_cx * f ); /** ** 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_dir_name_checkout() ** @see fsl_cx_db_file_config() ** @see fsl_cx_db_file_repo() */ 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() */ 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() */ 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. */ char const * fsl_cx_db_file_for_role(fsl_cx const * f, fsl_db_role_t r, fsl_size_t * len); /** ** Returns a db name string for the given fsl_db_role value. The ** string is static, guaranteed to live as long as the app. It ** returns NULL (or asserts in debug builds) if passed ** FSL_DB_ROLE_NONE or some value out of range for the enum. */ const char * fsl_db_role_label(int /*enum fsl_db_role*/ r); /** ** 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() */ char const * fsl_cx_dir_name_checkout(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. ** ** @see fsl_cx_db_repo() ** @see fsl_cx_db_checkout() */ 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_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_db * fsl_cx_db_checkout( fsl_cx * f ); /** ** UNTESTED. ** ** 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. ** ** The following notes no longer apply, but it may be (re)added ** later: ** ** If useAttach is 0 (the most common case), the db is opened in ** its own connection, otherwise it is ATTACH'd to the currently ** opened db. The latter case is unusual, but allows one to create ** queries which join against multiple databases. When in doubt, ** pass 0 here. ** */ 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. */ int fsl_config_close( fsl_cx * f ); /** ** If f has an opened/attached configuration db then its handle is ** returned. See fsl_config_open(). */ fsl_db * fsl_cx_db_config( fsl_cx * f ); /** ** Convenience form of fsl_db_prepare() which uses f's db. ** Returns 0 on success, FSL_RC_MISUSE if !f or !sql, FSL_RC_RANGE ** if !*sql. */ int fsl_cx_prepare( fsl_cx *f, fsl_stmt * tgt, char const * sql, ... ); /** ** va_list counterpart of fsl_cx_prepare(). */ 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_id_t fsl_cx_last_insert_id(fsl_cx *f); /** ** Returns the raw SQL code for a Fossil global config database. */ 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. */ 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. */ char const * fsl_schema_repo2(); /** ** Returns the raw SQL code for a Fossil checkout database. */ 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() */ 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. */ 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. */ char const * fsl_schema_ticket_reports(); /** ** Works similarly to fsl_stat(), except that zName must refer to a ** relative path under f's current checkout directory. Note that ** this stats local files, not repository content. ** ** Returns 0 on success. Errors include: ** ** - FSL_RC_MISUSE if any argument is NULL. ** ** - FSL_RC_RANGE if fsl_is_simple_pathname() returns false for zName. ** ** - FSL_RC_NOT_A_CHECKOUT if f has no opened checkout. ** ** - 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. */ int fsl_cx_stat( fsl_cx * f, char const * zName, fsl_fstat * tgt ); #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 */