/* -*- 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_FOSSIL2_H_INCLUDED) #define NET_FOSSIL_SCM_FOSSIL2_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/ ** ******************************************************************************* ** */ #include /* FILE type */ #include "config.h" /* ** This file sketches out a potential API for a library form of the ** Fossil SCM. This API concerns itself only with the components of ** fossil which do not need user interaction or the display of UI ** components (including HTML and CLI output). It is intended only to ** model the core internals of fossil, off of which user-level ** applications could be built. ** ** This code is 100% hypothetical/potential, and does not represent ** any Official Version 2.0. All Fossil users are encouraged to ** participate in its development, but if you are reading this then ** you probably already knew that :). Rather than think of this as v2, ** i would prefer for people to think of it as a refactoring of v1, as ** the intention is for it to be completely compatible with v1 ** repositories, just providing other means of accessing them. ** ** Conventions: ** ** - API docs (as you have probably already noticed), follow Fossil's ** comment style (see the '**' at the start of each line? That's what ** i mean) EXCEPT that each block MUST start with two or more ** asterisks, or '*!', or doxygen apparently doesn't understand it ** (http://www.stack.nl/~dimitri/doxygen/manual/docblocks.html). When ** adding code snippets and whatnot to docs, please use doxygen ** conventions if it is not too much of an inconvenience. All public ** APIs must be documented with a useful amount of detail. If you hate ** documenting, let me know and i'll document it (it's what i do for ** fun). ** ** - API members have a fsl_ or FSL_ prefix (fossil_ seems too long?) ** ** - Structs and functions use lower_underscore_style() ** ** - Overall style should follow Fossil v1.x. ** ** - Structs and enums all get the optional typedef so that they do ** not need to be qualified with 'struct' resp. 'enum' when used. ** ** - Structs intended to be created on the stack are accompanied by a ** const instance named fsl_STRUCT_NAME_empty, and possibly by a macro ** named fsl_STRUCT_NAME_empty_m, both of which are ** "default-initialized" instances of that struct. This is superiour ** to using memset() for struct initialization because we can set ** arbitrary default values this way and all clients who ** copy-construct them are unaffected by many types of changes to the ** struct's signature (though they may need a recompile). ** ** - Function typedefs are named fsl_XXX_f. Implementations of such ** typedefs/interfaces are typically named fsl_XXX_f_SUFFIX(), where ** SUFFIX describes the implementation's specialization. ** ** - Typedefs for non-struct types tend to be named fsl_XXX_t and ** structs do not have a _t extensions. ** ** Notes about "my style" (this===stephan)... i tend to add a lot of ** little things which most people consider frivilous, mysterious, ** overkill, or YAGNI, e.g. the various fsl_XXX_empty_m macros. They ** are there because my experience has been that they're really ** useful. They are of course open to discussion (as is everything in ** here - none of this is holy!). ** ** Happy Hacking! ** ** ----- stephan@wanderinghorse.net / sgbeal@googlemail.com */ #include /* va_list */ #if defined(__cplusplus) extern "C" { #endif typedef struct fsl_outputer fsl_outputer; typedef struct fsl_cx fsl_cx; typedef struct fsl_allocator fsl_allocator; typedef struct fsl_db fsl_db; typedef struct fsl_stmt fsl_stmt; typedef struct fsl_buffer fsl_buffer; typedef struct fsl_error fsl_error; typedef struct fsl_state fsl_state; typedef struct fsl_cx_config fsl_cx_config; /** ** @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. ** ** This type will likely eventually be made opaque to client code - ** do not depend on any of its members/contents. Having it ** non-opaque also has advantages, though. We'll see. Binary ** compatibility concerns might force us to make it opaque. But for ** now having it public simplifies testing and debugging. ** ** An instance's lifetime looks like: ** ** @code ** int rc; ** fsl_cx * f = NULL; ** rc = fsl_cx_init( &f, NULL ); ** assert(!rc); ** rc = fsl_repo_open_db( f, "myrepo.fsl" ); ** ... ** fsl_cx_finalize(f); ** @endcode */ /** ** Most funcs 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. */ 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 no repo has been opened yet. */ FSL_RC_NOT_A_REPO = 111, /** ** Tried to load a too-old or too-new repo */ FSL_RC_REPO_VERSION = 112, /** ** db-level error (e.g. statement prep failed) */ 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 a row has been fetched and the cursor may be used ** to access the current row state. */ FSL_RC_STEP_ROW = 115, /** ** Indicates that the end of the result set has been reached and ** that there is no row data to process. This is also the result for ** non-fetching queries (INSERT and friends). */ FSL_RC_STEP_DONE = 116, /** ** Indicates that a db-level error occurred during step() iteration. */ FSL_RC_STEP_ERROR = 117, /** ** Indicates that some data type is incorrect. */ FSL_RC_TYPE = 118, FSL_RC_NOT_A_CHECKOUT, /* ...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); /** ** Generic interface for streaming out data. Implementations must ** write n bytes from s to their destination channel and return 0 on ** success, non-0 on error (assumed to be a value from the fsl_rc_t ** enum). The state parameter is the implementation-specified ** output channel. */ typedef int (*fsl_output_f)( void * state, void const * src, fsl_size_t n ); /** ** Generic interface for streaming in data. Implementations must ** read (at most) *n bytes from their input, copy it to dest, assign ** *n to the number of bytes actually read, return 0 on success, and ** return non-0 on error (assumed to be a value from the fsl_rc_t ** enum). When called, *n is the max length to read. On return, *n ** is the actual amount read. The state parameter is the ** implementation-specified input file/buffer/whatever channel. */ typedef int (*fsl_input_f)( void * state, void * dest, fsl_size_t * n ); /** ** A fsl_input_f() implementation which requires that state be ** a readable (FILE*) handle. */ int fsl_input_FILE( void * state, void * dest, fsl_size_t * n ); /** ** Generic interface for finalizing/freeing memory. Intended ** primarily for use as a destructor/finalizer for high-level ** structs. Implementations must semantically behave like free(mem), ** regardless of whether or not they actually free the memory. At ** the very least, they generally should clean up any memory owned by ** mem (e.g. db resources or buffers), even if they do not free() mem. ** some implementations assume that mem is stack-allocated ** and they only clean up resources owned by mem. ** ** The state parameter is any state needed by the finalizer ** (e.g. a memory allocation context) and mem is the memory which is ** being finalized. ** ** The exact interpretaion of the state and mem are of course ** implementation-specific. */ typedef void (*fsl_finalizer_f)( void * state, void * mem ); /** ** Generic interface for memory finalizers. */ struct fsl_finalizer { /** ** State to be passed as the first argument to f(). */ void * state; /** ** Finalizer function. Should be called like this->f( this->state, ... ). */ fsl_finalizer_f f; }; typedef struct fsl_finalizer fsl_finalizer; /** Empty-initialized fsl_finalizer instance. */ #define fsl_finalizer_empty_m {NULL,NULL} /** ** Generic list container type. ** ** It is up to the APIs using this type to manage the entry count ** member and use fsl_list_reserve() to manage the "alloced" ** member. ** ** @see fsl_list_reserve() ** @see fsl_list_append() */ struct fsl_list { /** ** Array of entries. It contains this->alloced entries, ** this->count of which are "valid" (in use). */ void ** list; /** ** Number of "used" entries in the list. */ fsl_size_t count; /** ** Number of slots allocated in this->list. Use fsl_list_reserve() ** to modify this. Doing so might move the this->list pointer but ** the values it points to will stay stable. */ fsl_size_t alloced; }; typedef struct fsl_list fsl_list; /** ** Empty-initialized fsl_list object. */ #define fsl_list_empty_m { NULL, 0, 0 } /** ** Empty-initialized fsl_list object. */ extern const fsl_list fsl_list_empty; /** Generic state-with-finalizer holder */ struct fsl_state { /** ** Arbitrary context-dependent state. */ void * state; /** ** Finalizer for this->state. If used, it should be called like: ** ** @code ** this->finalize.f( this->finalize.state, this->state ); ** @endcode ** ** After which this->state must be treated as if it has been ** free(3)'d. */ fsl_finalizer finalize; }; /** Empty-initialized fsl_state instance. */ #define fsl_state_empty_m {NULL,fsl_finalizer_empty_m} /** ** Generic interface for flushing arbitrary output streams. Must ** return 0 on success, non-0 on error, but the result code "should" ** (to avoid downstream confusion) be one of the fsl_rc_t ** values. When in doubt, return FSL_RC_IO on error. */ typedef int (*fsl_flush_f)(void * state); /** ** A fsl_flush_f() impl which expects _FILE to be-a (FILE*), which ** this function passes the call on to fflush(). If fflush() returns ** 0, so does this function, else it returns FSL_RC_IO. */ int fsl_flush_f_FILE(void * _FILE); /** ** A fsl_finalizer_f() impl which requires that mem be-a (FILE*). ** If mem is not (stdout, stderr) then this function fclose()es ** it, else it is a no-op. The state parameter is ignored. */ void fsl_finalizer_f_FILE( void * state, void * mem ); /** ** A fsl_output_f() impl which requires state to be-a (FILE*), which ** this function passes the call on to fwrite(). Returns 0 on ** success, FSL_RC_IO on error. */ int fsl_output_f_FILE( void * state, void const * src, fsl_size_t n ); /** ** A fsl_output_f() impl which requires state to be-a (fsl_buffer*), ** which this function passes to fsl_buffer_append(). Returns 0 on ** success, FSL_RC_OOM (probably) on error. */ int fsl_output_f_buffer( void * state, void const * src, fsl_size_t n ); /** ** fsl_finalizer_f() impl which requires that mem be-a ** (fsl_buffer*). This function frees all memory associated with ** that buffer and zeroes out the structure, but does not free mem ** (because it is rare that fsl_buffers are created on the ** heap). The state parameter is ignored. */ int fsl_finalizer_f_buffer( void * state, void * mem ); /** ** An interface which encapsulates data for managing an output ** destination, primarily intended for use with fsl_output(). Why ** abstract it to this level? So that we can do interesting things ** like output to buffers, files, sockets, etc., using the core ** output mechanism. e.g. so script bindings can send their output ** to the same channel used by the library and other library ** clients. */ struct fsl_outputer { /** ** Output channel. */ fsl_output_f out; /** ** flush() implementation. */ fsl_flush_f flush; /** ** State to be used when calling this->out(), namely: ** this->out( this->state.state, ... ). */ fsl_state state; }; /** Empty-initialized fsl_outputer instance. */ #define fsl_outputer_empty_m {NULL,NULL,fsl_state_empty_m} /** ** A fsl_outputer instance which is initialized to output to a ** (FILE*). To use it, this value then set the copy's state.state ** member to an opened-for-write (FILE*) handle. By default it will ** use stdout. Its finalizer (if called!) will fclose(3) ** self.state.state if self.state.state is not one of (stdout, ** stderr). To disable the closing behaviour (and not close the ** file), set self.state.finalize.f to NULL (but then be sure that ** the file handle outlives this object and to fclose(3) it when ** finished with it). */ extern const fsl_outputer fsl_outputer_FILE; /** ** fsl_flush_f() implementation which requires state to be ** a writeable (FILE*) handle. */ int fsl_flush_f_FILE(void * state); /** ** fsl_output_f() implementation which requires state to be ** a writeable (FILE*) handle. */ int fsl_output_f_FILE( void * state, void const * src, fsl_size_t n ); /** ** fsl_outputer initializer which uses fsl_flush_f_FILE(), ** fsl_output_f_FILE(), and fsl_finalizer_f_FILE(). */ #define fsl_outputer_FILE_m { \ fsl_output_f_FILE, \ fsl_flush_f_FILE, \ {/*state*/ \ NULL, \ {NULL,fsl_finalizer_f_FILE} \ } \ } /** ** ** A general-purpose buffer buffer, analog to Fossil v1's Blob ** class. It is not called fsl_blob to avoid confusion with DB-side ** Blobs. Buffers are used extensively in fossil to do everything ** from reading files to compressing artifacts to creating ** dynamically-formatted strings. Because they are such a pervasive ** low-level type, and have such a simple structure, their members ** (unlike most other structs in this API) may be considered public ** and used directly by client code (as long as they do not mangle ** their state, e.g. by setting this->capacity smaller than ** this->used!). ** ** @see fsl_buffer_reserve() ** @see fsl_buffer_append() ** @see fsl_buffer_appendf() */ struct fsl_buffer { /** ** The raw memory owned by this buffer. It is this->capacity bytes ** long, of which this->used are considered "used" by the client. ** The difference beween (this->capacity - this->used) represents ** space the buffer has available for use before it will require ** another expansion/reallocation. */ unsigned char * mem; /** ** Number of bytes allocated for this buffer. */ fsl_size_t capacity; /** ** Number of "used" bytes in the buffer. This is generally ** interpreted as the virtual EOF (the one-past-the-end) position ** of this->mem. ** ** Library routines which manipulate buffers must ensure that ** (this->used<=this->capacity) is always true, expanding the ** buffer if necessary. Much of the API assumes that precondition ** is always met, and any violation of it opens the code to ** undefined behaviour (which is okay, just don't ever break that ** precondition). */ fsl_size_t used; }; /** Empty-initialized fsl_buffer instance. */ #define fsl_buffer_empty_m {NULL,0U,0U} /** Empty-initialized fsl_buffer instance. */ extern const fsl_buffer fsl_buffer_empty; /** ** A container for storing generic error state. */ struct fsl_error { /** ** Error message text is stored in this->msg.mem. The usable text ** part is this->msg.used bytes long. */ fsl_buffer msg; /** ** Error code, generally assumed to be a fsl_rc_t value. */ int code; }; /** Empty-initialized fsl_error instance. */ #define fsl_error_empty_m {fsl_buffer_empty_m,0} /** Empty-initialized fsl_error instance. */ extern const fsl_error fsl_error_empty; /** ** Populates err with the given code and formatted string, replacing ** any existing state. If fmt==NULL then fsl_rc_cstr(rc) is used to ** get the error string. ** ** Returns code on success, some other non-0 code on error. ** ** As a special case, if 0==code then fmt is ignored and the error ** state is cleared. This will not free any memory held by err but ** will re-set its string to start with a NUL byte, read for re-use ** later on. ** ** As a special case, if code==FSL_RC_OOM then fmt is ignored ** to avoid a memory allocation (which would presumably fail). ** ** @see fsl_error_get() ** @see fsl_error_clean() ** @see fsl_error_move() */ int fsl_error_set( fsl_error * err, int code, char const * fmt, ... ); /** va_list counterpart to fsl_cx_err_set(). */ int fsl_error_setv( fsl_error * err, int code, char const * fmt, va_list args ); /** ** Fetches the error state from err. If !err it returns ** FSL_RC_MISUSE without side-effects, else it returns err's current ** error code. ** ** If str is not NULL then *str will point to the raw ** (NUL-terminated) error string (which might be empty or even ** NULL). The memory for the string is owned by err and may be ** invalidated by any calls which take err as a non-const parameter ** OR which might modify it indirectly through its container, so the ** client is required to copy it if it is needed for later on. ** ** If len is not NULL then *len will hold the length of the string ** (in bytes). ** ** @see fsl_error_set() ** @see fsl_error_clean() ** @see fsl_error_move() */ int fsl_error_get( fsl_error * err, char const ** str, fsl_size_t * len ); /** ** Frees up any resources owned by err and sets its error code to 0, ** but does not free err. This is harmless no-op if !err or if err ** holds no dynamically allocated no memory. ** ** @see fsl_error_set() ** @see fsl_error_get() ** @see fsl_error_move() */ void fsl_error_clean( fsl_error * err ); /** ** Swaps the error state of the two given error objects and then ** sets lower->code to 0 and lower->msg.used = 0, effectively ** clearing the error state but not immediately deallocating the ** memory. ** ** This is intended for "uplifting" an error from one context ** to a higher one. ** ** Results are undefined if either parameter is NULL or either is ** not properly initialized. i.e. neither may refer to uninitialized ** memory. Copying fsl_error_empty at declaration-time is a simple ** way to ensure that instances are cleanly initialized. */ void fsl_error_move( fsl_error * lower, fsl_error * higher ); /** ** This code replaces the v1 concept of g.zMainDbType, and provides ** a type ID tag which can be applied to 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 checkout db opened. */ FSL_DB_ROLE_MAIN = 0x08 }; typedef enum fsl_db_role_t fsl_db_role_t; /** ** Placeholder for sqlite3/4 type. We currently use v3 but will ** almost certainly switch to v4 at some point. Before we can do ** that we need an upgrade/migration path. */ typedef struct sqlite3 sqlite3; /** ** A level of indirection to hide the actual db driver ** implementation from the public API. Whether or not the API ** uses/will use sqlite3 or 4 is undecided at this point. We ** currently use 3 because (A) it bootstraps development and ** testing by letting us use existing fossil repos for testing and ** (B) it reduces the number of potential problems when porting ** SQL-heavy code from the v1 tree. */ typedef sqlite3 fsl_dbh_t; /** ** Db handle wrapper/helper. */ struct fsl_db { /** ** Fossil Context on whose behalf this instance is operating. */ fsl_cx * f; /** ** Underlying db driver handle. */ fsl_dbh_t * dbh; /** ** Holds error state from the underlying driver. fsl_db and ** fsl_stmt operations which fail at the driver level "should" ** update this state to include error info from the driver. ** fsl_cx APIs which fail at the DB level then uplift this (using ** fsl_error_move()) so that they can pass it on to the caller. */ fsl_error error; /** ** Holds the file name used when opening this db. */ fsl_buffer filename; /** ** Holds the database name for use in creating queries. ** Might or might not be set/needed, depending on ** the context. */ fsl_buffer name; /** ** Describes what role this db connection plays in ** fossil (if any). We may or may not still need this. */ int role; /** ** Debugging/test counter. Closing a db with opened statements ** might assert() or trigger debug output when the db is closed. */ int openStatementCount; /** ** A marker which tells fsl_db_close() whether or not this ** API allocated this instance (in which case fsl_db_close() ** will fsl_free() it) or not (in which case it does not free() ** it). */ void const * allocStamp; }; /** Empty-initialized fsl_db instance. */ #define fsl_db_empty_m { \ NULL/*f*/, \ NULL/*dbh*/, \ fsl_error_empty_m /*error*/, \ fsl_buffer_empty_m/*filename*/, \ fsl_buffer_empty_m/*name*/, \ FSL_DB_ROLE_NONE, \ 0/*openStatementCount*/, \ NULL/*allocStamp*/ \ } /** Empty-initialized fsl_db instance. */ extern const fsl_db fsl_db_empty; /** ** If db is not NULL then this function returns its name (the one ** used to open it). The bytes are valid until the 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. */ char const * fsl_db_filename(fsl_db const * db, fsl_size_t * len); /** ** Generic memory alloc/free/realloc interface(). ** ** Implementations must behave as follows: ** ** - If 0==n then semantically behave like free(3) and return ** NULL. ** ** - If 0!=n and !mem then semantically behave like malloc(3). ** ** - If 0!=n and NULL!=mem then semantically behave like ** realloc(3). Note that realloc specifies: "If n was equal to 0, ** either NULL or a pointer suitable to be passed to free() is ** returned." Which is kind of useless, and thus implementations ** MUST return NULL when n==0. */ typedef void *(*fsl_realloc_f)(void * state, void * mem, fsl_size_t n); /** ** Holds an allocator function and its related state. */ struct fsl_allocator { /** ** Base allocator function. It must be passed this->state ** as its first parameter. */ fsl_realloc_f f; /** ** State intended to be passed as the first parameter to ** this->f(). */ void * state; }; /** Empty-initialized fsl_allocator instance. */ #define fsl_allocator_empty_m {NULL,NULL} /** ** A fsl_realloc_f() implementation which uses the standard ** malloc()/free()/realloc(). The state parameter is ignored. */ void * fsl_realloc_f_stdalloc(void * state, void * mem, fsl_size_t n); /** ** Library-wide allocator. If modified by the client then it must be ** changed before the library allocates any resources. The default ** uses the C-standard de/re/allocators. */ extern fsl_allocator fsl_memory_allocator; /** ** Placeholder for external sqlite3_stmt. */ typedef struct sqlite3_stmt sqlite3_stmt; typedef sqlite3_stmt fsl_stmt_t; /** ** Represents a prepared statement handle. ** Intended usage: ** ** @code ** fsl_stmt st = fsl_stmt_empty; ** int rc = fsl_stmt_prepare( f, &st, "..." ); ** if(rc){ ** assert(!st.stmt); ** // Error! Use fsl_cx_err_get() to find out if ** // the db driver told us something helpful. ** }else{ ** // ...use st and eventually finalize it: ** fsl_stmt_finalize( &st ); ** } ** @endcode */ struct fsl_stmt { /** ** The db which prepared this statement. */ fsl_db * db; /** ** Underlying db driver-level statement handle. */ fsl_stmt_t * stmt; /** ** SQL used for preparing this statement. */ fsl_buffer sql; /** ** Number of result columns in this statement. */ int colCount; /** ** Number of bound parameter indexes in this statement. */ int paramCount; /** ** A marker which tells fsl_stmt_finalize() whether or not this ** API allocated this instance (in which case fsl_stmt_finalize() ** will fsl_free() it) or not (in which case it does not free() ** it). */ void const * allocStamp; }; /** ** Empty-initialized fsl_stmt instance, intended for ** copy-constructing. */ extern const fsl_stmt fsl_stmt_empty; /** ** Prepares an SQL statement for execution. On success it returns 0, ** populates tgt with the statement's state, and the caller is ** obligated to eventually pass tgt to fsl_stmt_finalize(). ** ** On error non-0 is returned and tgt is not modified. If ** preparation of the statement fails at the db level then FSL_RC_DB ** is returned f's error state (fsl_cx_err_get()) will contain more ** details about the problem. ** ** sql and the following arguments are applied as printf-style formatting, ** and any formatting options supported by fsl_appendf() may be used ** here. ** */ int fsl_stmt_prepare( fsl_db *db, fsl_stmt * tgt, char const * sql, ... ); /** ** va_list counterpart of fsl_stmt_prepare(). */ int fsl_stmt_preparev( fsl_db *db, fsl_stmt * tgt, char const * sql, va_list args ); /** ** Frees memory associated with stmt but does not free stmt unless ** it was allocated by fsl_stmt_malloc(). These objects are normally ** stack-allocated. Returns FSL_RC_MISUSE if !stmt or it has already ** been finalized (but was not freed). */ int fsl_stmt_finalize( fsl_stmt * stmt ); /** ** "Steps" the given SQL cursor one time and returns one of the ** following: FSL_RC_STEP_ROW, FSL_RC_STEP_DONE, FSL_RC_STEP_ERROR. ** On a db error this will update the underlying the underlying db's ** error state. ** ** Returns FSL_RC_MISUSE if !stmt or stmt has not been prepared. */ int fsl_stmt_step( fsl_stmt * stmt ); /** ** A callback for use with fsl_stmt_each(). It will be called one ** time for each row fetched, passed the statement object and the ** state parameter passed as the 3rd parameter to fsl_stmt_each(). ** If it returns non-0 then iteration stops and that code is ** returned UNLESS it returns FSL_RC_BREAK, in which case ** fsl_stmt_each() stops iteration and returns 0. ** ** It is strictly illegal for a callback to step() the statement. ** It must only read the current column data (or similar metatdata, ** e.g. column names) from the statement. */ typedef int (*fsl_stmt_each_f)( void * state, fsl_stmt * stmt ); /** ** Calls the given callback one time for each result row in the ** given statement. It applies no meaning to the callbackState ** parameter - that is passed as-is to the callback. See ** fsl_stmt_each_f() for the semantics of the callback. ** ** Returns 0 on success. Returns FSL_RC_MISUSE if !stmt or ** !callback. */ int fsl_stmt_each( fsl_stmt * stmt, fsl_stmt_each_f callback, void * callbackState ); /** ** Resets the given statement, analog to sqlite3_reset(). Should be ** called one time between step() iterations when running multiple ** INSERTS, UPDATES, etc. via the same statement. ** ** Returns 0 on success. */ int fsl_stmt_reset( fsl_stmt * stmt ); /** ** Returns the name of the given 0-based result column index, or ** NULL if !stmt, stmt is not prepared, or index is out out of ** range. The returned bytes are owned by the statement object and ** may be invalidated shortly after this is called, so the caller ** must copy the returned value if it needs to have any useful ** lifetime guarantees. It's a bit more complicated than this, but ** assume that any API calls involving the statement handle might ** invalidate the colum name bytes. ** ** The API guarantees that the returned value is either NULL or ** NUL-terminated. */ char const * fsl_stmt_col_name(fsl_stmt * stmt, int index); /** ** Returns the result column count for the given statement, or -1 if ** !stmt or it has not been prepared. Note that this value is cached ** when the statement is created. Note that non-fetching queries ** (e.g. INSERT and UPDATE) have a column count of 0. Some non-SELECT ** constructs, e.g. PRAGMA table_info(tname), behave like SELECT ** and have a positive column count. */ int fsl_stmt_col_count( fsl_stmt const * stmt ); /** ** Returns the bound parameter count for the given statement, or -1 ** if !stmt or it has not been prepared. Note that this value is ** cached when the statement is created. */ int fsl_stmt_param_count( fsl_stmt const * stmt ); /** ** Binds NULL to the given 1-based parameter index. Returns 0 on ** succcess. Sets the DB's error state on error. */ int fsl_stmt_bind_null( fsl_stmt * stmt, int index ); /** ** Binds v to the given 1-based parameter index. Returns 0 on ** succcess. Sets the DB's error state on error. */ int fsl_stmt_bind_int32( fsl_stmt * stmt, int index, fsl_int32_t v ); /** ** Binds v to the given 1-based parameter index. Returns 0 on ** succcess. Sets the DB's error state on error. */ int fsl_stmt_bind_int64( fsl_stmt * stmt, int index, fsl_int64_t v ); /** ** Binds v to the given 1-based parameter index. Returns 0 on ** succcess. Sets the Fossil context's error state on error. */ int fsl_stmt_bind_double( fsl_stmt * stmt, int index, fsl_double_t v ); /** ** Binds the first n bytes of v as text to the given 1-based bound ** parameter column in the given statement. If makeCopy is true then ** the binding makes an copy of the data. Set makeCopy to false ONLY ** if you KNOW that the bytes will outlive the binding. ** ** Returns 0 on success. On error stmt's underlying db's error state ** is updated, hopefully with a useful error message. */ int fsl_stmt_bind_text( fsl_stmt * stmt, int index, char const * v, fsl_int_t n, char makeCopy ); /** ** Binds the first n bytes of v as a blob to the given 1-based bound ** parameter column in the given statement. See fsl_stmt_bind_text() ** for the semantics of the makeCopy parameter and return value. */ int fsl_stmt_bind_blob( fsl_stmt * stmt, int index, void const * v, fsl_int_t len, char makeCopy ); /** ** Gets an integer value from the given 0-based result set column, ** assigns *v to that value, and returns 0 on success. ** ** Returns FSL_RC_RANGE if index is out of range for stmt. */ int fsl_stmt_get_int32( fsl_stmt * stmt, int index, fsl_int32_t * v ); /** ** Gets an integer value from the given 0-based result set column, ** assigns *v to that value, and returns 0 on success. ** ** Returns FSL_RC_RANGE if index is out of range for stmt. */ int fsl_stmt_get_int64( fsl_stmt * stmt, int index, fsl_int64_t * v ); /** ** Gets double value from the given 0-based result set column, ** assigns *v to that value, and returns 0 on success. ** ** Returns FSL_RC_RANGE if index is out of range for stmt. */ int fsl_stmt_get_double( fsl_stmt * stmt, int index, fsl_double_t * v ); /** ** Gets a string value from the given 0-based result set column, ** assigns *out (if out is not NULL) to that value, assigns *outLen ** (if outLen is not NULL) to *out's length, and returns 0 on ** success. Ownership of the string memory is passed to the caller, ** who must eventually pass it to fsl_free() to free it. ** ** Returns FSL_RC_RANGE if index is out of range for stmt. */ int fsl_stmt_get_text( fsl_stmt * stmt, int index, char const **out, fsl_int_t * outLen ); /** ** The Blob counterpart of fsl_stmt_get_text(). Identical to that ** function except that its output result (3rd paramter) type ** differs, and it fetches the data as a raw blob, without any sort ** of string interpretation. ** ** Returns FSL_RC_RANGE if index is out of range for stmt. */ int fsl_stmt_get_blob( fsl_stmt * stmt, int index, void const **out, fsl_int_t * outLen ); /** ** Executes multiple SQL statements, ignoring any results they might ** collect. Returns 0 on success, non-0 on error. On error ** db->error might be updated to report the problem. */ int fsl_db_exec_multi( fsl_db * db, const char * sql, ...); /** ** va_list counterpart of db_exec_multi(). */ int fsl_db_exec_multiv( fsl_db * db, const char * sql, va_list args); /** ** Executes a single SQL statement, skipping over any results ** it may have. Returns 0 on success. On error db's error state ** may be updated. */ int fsl_db_exec( fsl_db * db, char const * sql, ... ); /** ** va_list counterpart of fs_db_exec(). */ int fsl_db_execv( fsl_db * db, char const * sql, va_list args ); /** ** UNTESTED. ** ** Runs a fetch-style SQL query against DB and returns the first ** column of the first result row via *rv. If the query returns no ** rows, *rv is not modified. The intention is that the caller sets ** *rv to his preferred default (or sentinel) value before calling ** this. ** ** The format string (the sql parameter) accepts all formatting ** options supported by fsl_appendf(). ** ** Returns 0 on success. On error db's error state is updated and ** *rv is not modified. ** ** Returns FSL_RC_MISUSE without side effects if !db, !rv, !sql, ** or !*sql. */ int fsl_db_get_int32( fsl_db * db, fsl_int32_t * rv, char const * sql, ... ); /** ** va_list counterpart of fsl_db_get_int32(). */ int fsl_db_get_int32v( fsl_db * db, fsl_int32_t * rv, char const * sql, va_list args); /** ** UNTESTED. ** ** The int64 counterpart of fsl_db_get_int32(). */ int fsl_db_get_int64( fsl_db * db, fsl_int64_t * rv, char const * sql, ... ); /** ** va_list counterpart of fsl_db_get_int64(). */ int fsl_db_get_int64v( fsl_db * db, fsl_int64_t * rv, char const * sql, va_list args); /** ** UNTESTED. ** ** The double counterpart of fsl_db_get_int32(). */ int fsl_db_get_double( fsl_db * db, fsl_double_t * rv, char const * sql, ... ); /** ** va_list counterpart of fsl_db_get_double(). */ int fsl_db_get_doublev( fsl_db * db, fsl_double_t * rv, char const * sql, va_list args); /** ** UNTESTED. ** ** The String counterpart of fsl_db_get_int32(). On success *rv will ** be set to a dynamically allocated string copied from the first ** column of the first result row. If rvLen is not NULL then *rvLen ** will be assigned the byte length of that string. If no row is ** found, *rv is not modified. */ int fsl_db_get_text( fsl_db * db, char ** rv, fsl_size_t * rvLen, char const * sql, ... ); /** ** va_list counterpart of fsl_db_get_text(). */ int fsl_db_get_textv( fsl_db * db, char ** rv, fsl_size_t * rvLen, char const * sql, va_list args ); /** ** UNTESTED. ** ** The Blob counterpart of fsl_db_get_text(). Identical to that ** function except that its output result (2nd paramter) type ** differs, and it fetches the data as a raw blob, without any sort ** of string interpretation. */ int fsl_db_get_blob( fsl_db * db, void ** rv, fsl_size_t * stmtLen, char const * sql, ... ); /** ** va_list counterpart of fsl_db_get_blob(). */ int fsl_db_get_blobv( fsl_db * db, void ** rv, fsl_size_t * stmtLen, char const * sql, va_list args ); /** ** UNTESTED. ** ** Similar to fsl_db_get_text() and fsl_db_get_blob(), but writes ** its result to tgt, overwriting any existing memory it might hold. ** ** If asBlob is true then the underlying BLOB API is used to ** populate the buffer, else the underlying STRING/TEXT API is used. */ int fsl_db_get_buffer( fsl_db * db, fsl_buffer * tgt, char asBlob, char const * sql, ... ); /** ** va_list counterpart of fsl_db_get_buffer(). */ int fsl_db_get_bufferv( fsl_db * db, fsl_buffer * tgt, char asBlob, char const * sql, va_list args ); /** ** Expects sql to be a SELECT-style query which (potentially) ** returns a result set. For each row in the set callback() is ** called, as described for fsl_stmt_each(). Returns 0 on success. ** ** Returns FSL_RC_MISUSE if !db, db is not opened, !callback, ** !sql, or !*sql. */ int fsl_db_each( fsl_db * db, fsl_stmt_each_f callback, void * callbackState, char const * sql, ... ); /** ** va_list counterpart to fsl_db_each(). */ int fsl_db_eachv( fsl_db * db, fsl_stmt_each_f callback, void * callbackState, char const * sql, va_list args ); /** ** 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 useful defaults. */ #define fsl_cx_config_empty_m { \ 0/*traceSql*/, \ 0/*sqlPrint*/ \ } extern const fsl_cx_config fsl_cx_config_empty; /** Parameters for fsl_cx_init(). */ struct fsl_init_param { /** The output channel for the Fossil instance. */ fsl_outputer output; /** ... what else? Config db file name? Default repo file to open? We have a chicken/egg scenario with some bits, e.g. fsl_buffer_reserve() requires a fsl_cx, so we can't use buffers to create file name strings until after the ctx is initialized. Or we need extra buffer APIs which take a fsl_allocator instead of a fsl_cx parameter. Or we need to set the allocator as part of the buffer class. That would not be bad but would be memory-expensive - buffers are showing up everywhere. */ fsl_cx_config config; }; typedef struct fsl_init_param fsl_init_param; /** Empty-initialized fsl_init_param instance. */ #define fsl_init_param_empty_m {fsl_outputer_empty_m, fsl_cx_config_empty_m} /** ** fsl_init_param instance initialized to use stdout for output and ** the standard system memory allocator. */ #define fsl_init_param_default_m {fsl_outputer_FILE_m, fsl_cx_config_empty_m} /** Empty-initialized fsl_init_param instance. */ extern const fsl_init_param fsl_init_param_empty; /** ** fsl_init_param 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_init_param fsl_init_param_default; /** ** Initializes a fsl_cx instance. tgt must be a pointer to NULL, ** e.g.: ** ** @code ** fsl_cxt * f = NULL; ** int rc = fsl_cx_init( &f, NULL ); ** @endcode ** ** If the second parameter is NULL then default implementations ** are used for the context's output and allocation routines. 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 is ** transfered to *tgt in all cases except one: ** ** If this function cannot allocate a new instance it immediately ** 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). */ int fsl_cx_init( fsl_cx ** tgt, fsl_init_param * param ); /** ** Frees all memory associated with f, which must have been ** initialized using fsl_cx_init() (or equivalent). ** ** Returns FSL_RC_MISUSE if !f, else 0. */ int fsl_cx_finalize( 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 ); /** ** Roles equate to permissions in Fossil v1. Here we implement them ** as a bitmask and hope we never need more than 31 of them. */ enum fsl_roles_t { FSL_ROLE_GUEST = 0, FSL_ROLE_ANONYMOUS = 1, FSL_ROLE_ADMIN = 1 << 1, FSL_ROLE_SETUP = 1 << 2, FSL_ROLE_READ = 1 << 3, FSL_ROLE_COMMIT = 1 << 4, FSL_ROLE_ALL = 0x7FFFFFFF /* unsigned 32-bit+ enums are not portable :/ */ }; typedef enum fsl_roles_t fsl_roles_t; /** ** Holds type ID tags for the db-level types which have a ** fsl_db_record-derived class. */ enum fsl_db_type_t { FSL_TYPE_INVALID = 0, FSL_TYPE_USER = 1, FSL_TYPE_TAG = 2 /* ... */ }; typedef enum fsl_db_type_t fsl_db_type_t; /** ** Base type for db record classes. Each db record subclass must ** have a fsl_db_record instance as its first struct member (for ** C-level casting reasons). */ struct fsl_db_record { fsl_db_type_t typeId; /* const? */ fsl_id_t dbId; /* maybe put mtime field here b/c it's used by several classes */ /** ** For creating linked lists of records, for algorithms which ** want to return multiple records. Its concrete type is ** determined by this->typeId. */ void * next; }; typedef struct fsl_db_record fsl_db_record; extern const fsl_db_record fsl_db_record_empty; struct fsl_user { fsl_db_record base; fsl_buffer name; fsl_int32_t roles; /* bitmask of fsl_roles_t values */ fsl_time_t mtime; /* ??? */ }; typedef struct fsl_user fsl_user; extern const fsl_user fsl_user_empty; struct fsl_tag { fsl_db_record base; fsl_buffer key; fsl_buffer value; }; typedef struct fsl_tag fsl_tag; extern const fsl_tag fsl_tag_empty; /** ** Represents a db blob, not fossil's Blob class. */ struct fsl_blob { fsl_db_record base; fsl_time_t mtime; #if 0 /* "raw" SHA1 digest */ unsigned char digest[20]; /* OR... */ #else /** ** 40-byte SHA1 plus terminating NUL. */ char sha1[41]; #endif /* the latter is probably more useful/easier */ }; typedef struct fsl_blob fsl_blob; #if 0 /** ** Elided: abstractions suitable for adding an ORM-like layer. ** That would be taking it too far, i think. If, however, we ** decide to abstract away the DB completely then a CRUD ** abstraction API would indeed make sense. Except that fossil ** hasn't much need for the 'D' in CRUD, so it'd be a CRU ** abstraction API. */ #endif /** ** Semantically behaves like malloc(3), but may introduce instrumentation, ** error checking, or similar. */ void * fsl_malloc( fsl_size_t n ); /** ** Semantically behaves like free(3), but may introduce instrumentation, ** error checking, or similar. */ void fsl_free( void * mem ); /** ** Behaves like realloc(3). Clarifications on the behaviour (because ** the standard has one case of unfortunate wording involving what ** it returns when n==0): ** ** - If passed (NULL, n>0) then it semantically behaves like ** fsl_malloc(f, n). ** ** - If 0==n then it semantically behaves like free(2) and returns ** NULL (clarifying the aforementioned wording problem). ** ** - If passed (non-NULL, n) then it semantically behaves like ** realloc(mem,n). ** ** Returns NULL if !f. ** */ void * fsl_realloc( void * mem, fsl_size_t n ); /** ** Reserves at least n bytes of capacity in buf. Returns 0 on ** success, FSL_RC_OOM if allocation fails, FSL_RC_MISUSE if !buf. ** ** This does not change buf->used, nor will it shrink the buffer ** (reduce buf->capacity) unless n is 0, in which case it ** immediately frees buf->mem and sets buf->capacity and buf->used ** to 0. */ int fsl_buffer_reserve( fsl_buffer * buf, fsl_size_t n ); /** ** Resets buf->used to 0 and sets buf->mem[0] (if buf->mem is not ** NULL) to 0. Does not (de)allocate memory, only changes the ** logical "used" size of the buffer. Returns 0 on success, ** FSL_RC_MISUSE if !buf. ** ** Achtung for v1 porters: this function's semantics are much ** different from the v1 blob_reset(). To get those semantics, ** use fsl_buffer_reserve(buf, 0). */ int fsl_buffer_reset( fsl_buffer * buf ); /** ** Similar to fsl_buffer_reserve() except that... ** ** - It does not free all memory when n==0. Instead it essentially ** makes the memory a length-0, NUL-terminated string. ** ** - It will try to shrink (realloc) buf's memory if (ncapacity). ** ** - It sets buf->capacity to (n+1) and buf->used to n. This routine ** allocates one extra byte to ensure that buf is always ** NUL-terminated. ** ** - On success it always NUL-terminates the buffer at ** offset buf->used. ** ** Returns 0 on success, FSL_RC_MISUSE if !buf, FSL_RC_OOM if ** (re)allocation fails. */ int fsl_buffer_resize( fsl_buffer * buf, fsl_size_t n ); /** ** Swaps the contents of the left and right arguments. Results are ** undefined if either argument is NULL or points to uninitialized ** memory. */ void fsl_buffer_swap( fsl_buffer * left, fsl_buffer * right ); /** ** Similar fsl_buffer_swap() but it also optionally frees one of ** the buffer's memories after swapping them. If clearWhich is ** negative then the left buffer (1st arg) is cleared _after_ ** swapping (i.e., the NEW left hand side gets cleared). If ** clearWhich is greater than 0 then the right buffer (2nd arg) is ** cleared _after_ swapping (i.e. the NEW right hand side gets ** cleared). If clearWhich is 0, this function behaves identically ** to fsl_buffer_swap(). */ void fsl_buffer_swap_free( fsl_buffer * left, fsl_buffer * right, char clearWhich ); /** ** ** Appends the first n bytes of src to b, expanding b as ** necessary. If n is less than 0 then the equivalent of ** fsl_strlen((char const*)src) is used to calculate the length. ** ** Returns 0 on success, FSL_RC_MISUSE if !f, !b, or !src, ** FSL_RC_OOM if allocation of memory fails. It returns 0 without ** side-effects if 0==n or if ((n<0) and !*src). ** ** If this function appends anything, it guarantees that it ** NUL-terminates the buffer (but that the NUL terminator is not ** counted in b->used). */ int fsl_buffer_append( fsl_buffer * b, void const * src, fsl_int_t n ); /** ** Uses fsl_appendf() to append formatted output to the given buffer. ** Returns 0 on success, FSL_RC_MISUSE if !f or !dest, */ int fsl_buffer_appendf( fsl_buffer * dest, char const * fmt, ... ); /** va_list counterpart to fsl_buffer_appendfv(). */ int fsl_buffer_appendfv( fsl_buffer * dest, char const * fmt, va_list args ); /** ** Compresses the first pIn->used bytes of pIn to pOut. It is ok for ** pIn and pOut to be the same blob. ** ** pOut must either be the same as pIn or else cleanly ** initialized/empty. ** ** Results are undefined if any argument is NULL. ** ** Returns 0 on success, FSL_RC_OOM on allocation error, and FSL_RC_ERROR ** if the lower-level compression routines fail. ** ** TODO: add a streaming variant which takes the input from a ** fsl_input_f() and pushes the output to a fsl_output_f(). The code ** exists in the libwhio source tree already. ** ** TODO: if pOut!=pIn1 then re-use pOut's memory, if it has any. */ int fsl_buffer_compress(fsl_buffer const *pIn, fsl_buffer *pOut); /** ** Compress the concatenation of a blobs pIn1 and pIn2 into pOut. ** ** pOut must be either uninitialized or must be the same as either pIn1 or ** pIn2. ** ** Results are undefined if any argument is NULL. ** ** Returns 0 on success, FSL_RC_OOM on allocation error, and FSL_RC_ERROR ** if the lower-level compression routines fail. ** ** TODO: if pOut!=(pIn1 or pIn2) then re-use its memory, if it has any. */ int fsl_buffer_compress2(fsl_buffer *pIn1, fsl_buffer *pIn2, fsl_buffer *pOut); /** ** Uncompress buffer pIn and store the result in pOut. It is ok for ** pIn and pOut to be the same buffer. Returns 0 on success. On ** error pOut is not modified. ** ** pOut must be either cleanly initialized/empty or the same as pIn. ** ** Results are undefined if any argument is NULL. ** ** Returns 0 on success, FSL_RC_OOM on allocation error, and ** FSL_RC_ERROR if the lower-level decompression routines fail. ** ** TODO: add a streaming variant which takes the input from a ** fsl_input_f() and pushes the output to a fsl_output_f(). The code ** exists in the libwhio source tree already. ** ** TODO: if pOut!=(pIn1 or pIn2) then re-use its memory, if it has any. */ int fsl_buffer_uncompress(fsl_buffer const *pIn, fsl_buffer *pOut); /** ** Returns true if this function believes that mem (which must be ** at least len bytes of valid memory long) appears to have been ** compressed by fsl_buffer_compress() or equivalent. This is not ** a 100% reliable check - it could have false positives on certain ** inputs, but it is thought to be unlikely. ** ** Returns 0 if mem is NULL. */ char fsl_data_is_compressed(unsigned char const * mem, fsl_size_t len); /** ** Equivalent to fsl_data_is_compressed(buf->mem, buf->used). */ char fsl_buffer_is_compressed(fsl_buffer const * buf); /** ** Equivalent to ((char const *)b->mem), but returns NULL if !b. The ** returned memory is effectively b->used bytes long unless the user ** decides to apply his own conventions. */ char const * fsl_buffer_cstr(fsl_buffer const *b); /** ** If buf is not NULL and has any memory allocated to it, that ** memory is returned. If both b and len are not NULL then *len is ** set to b->used. If b has no dynamic memory then NULL is returned ** and *len (if len is not NULL) is set to 0. **/ char const * fsl_buffer_cstr2(fsl_buffer const *b, fsl_size_t * len); /** ** Equivalent to ((char *)b->mem), but returns NULL if !b. The ** returned memory is effectively b->used bytes long unless the user ** decides to apply his own conventions. */ char * fsl_buffer_str(fsl_buffer const *b); /* ** Returns the "used" size of b, or 0 if !b. */ fsl_size_t fsl_buffer_size(fsl_buffer const * b); /* ** Returns the current capacity of b, or 0 if !b. */ fsl_size_t fsl_buffer_capacity(fsl_buffer const * b); /** ** Compares the contents of buffers lhs and rhs using memcmp(3) ** semantics. Return negative, zero, or positive if the first ** buffer is less then, equal to, or greater than the second. ** Results are undefined if either argument is NULL. ** ** When buffers of different length match on the first N bytes, ** where N is the shorter of the two buffers' lengths, it treats the ** shorter buffer as being "less than" the longer one. */ int fsl_buffer_compare(fsl_buffer const * lhs, fsl_buffer const * rhs); /** ** Compare two buffers in constant (a.k.a. O(1)) time and return ** zero if they are equal. Constant time comparison only applies ** for buffers of the same length. If lengths are different, ** immediately returns 1. */ int fsl_buffer_compare_O1(fsl_buffer const * lhs, fsl_buffer const * rhs); /** ** Uses a fsl_input_f() function to buffer input into a fsl_buffer. ** ** dest must be a non-NULL, initialized (though possibly empty) ** fsl_buffer object. Its contents, if any, will be overwritten by ** this function, and any memory it holds might be re-used. ** ** The src function is called, and passed the state parameter, to ** fetch the input. If it returns non-0, this function returns that ** error code. src() is called, possibly repeatedly, until it ** reports that there is no more data. ** ** Whether or not this function succeeds, dest still owns any memory ** pointed to by dest->mem, and the client must eventually free it ** by calling fsl_buffer_reserve(dest,0). ** ** dest->mem might (and possibly will) be (re)allocated by this ** function, so any pointers to it held from before this call might ** be invalidated by this call. ** ** On error non-0 is returned and dest has almost certainly been ** modified but its state must be considered incomplete. ** ** Errors include: ** ** dest or src are NULL (FSL_RC_MISUSE) ** ** Allocation error (FSL_RC_OOM) ** ** src() returns an error code ** ** Whether or not the state parameter may be NULL depends on the src ** implementation requirements. ** ** On success dest will contain the contents read from the input ** source. dest->used will be the length of the read-in data, and ** dest->mem will point to the memory. dest->mem is automatically ** NUL-terminated if this function succeeds, but dest->used does not ** count that terminator. On error the state of dest->mem must be ** considered incomplete, and is not guaranteed to be ** NUL-terminated. ** ** Example usage: ** ** @code ** fsl_buffer buf = fsl_buffer_empty; ** int rc = fsl_buffer_fill_from( &buf, ** fsl_input_FILE, ** stdin ); ** if( rc ){ ** fprintf(stderr,"Error %d (%s) while filling buffer.\n", ** rc, fsl_rc_cstr(rc)); ** fsl_buffer_reserve( &buf, 0 ); ** return ...; ** } ** ... use the buf->mem ... ** ... clean up the buffer ... ** fsl_buffer_reserve( &buf, 0 ); ** @endcode ** ** To take over ownership of the buffer's memory, do: ** ** @code ** void * mem = buf.mem; ** buf = fsl_buffer_empty; ** @endcode ** ** ** In which case the memory must eventually be passed to fsl_free() ** to free it. */ int fsl_buffer_fill_from( fsl_buffer * dest, fsl_input_f src, void * state ); /** A fsl_buffer_fill_from() proxy which overwrite's dest->mem with the contents of the given FILE handler (which must be opened for read access). Returns 0 on success, after which dest->mem contains dest->used bytes of content from the input source. On error dest may be partially filled. */ int fsl_buffer_fill_from_FILE( fsl_buffer * dest, FILE * src ); /** Wrapper for fsl_buffer_fill_from_FILE() which gets its input from the given file name. As a special case it interprets the name "-" as stdin. */ int fsl_buffer_fill_from_filename( fsl_buffer * dest, char const * filename ); /** ** 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 ); /** ** Uses fsl_appendf() to append formatted output to the channel ** configured for use with fsl_output(). */ 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 ); /** ** Uses fsl_appendf() to create a dynamically-allocated string. ** On success the new string is returned to the caller, who must ** eventually pass it to fsl_free() or fsl_realloc() to free it. */ char * fsl_mprintf( char const * fmt, ... ); /** Equivalent to fsl_mprintfv(). */ char * fsl_mprintfv( char const * fmt, va_list args ); /** ** Equivalent to strdup(3) but returns NULL if !src. The returned ** memory must eventually be passed to fsl_free(). */ char * fsl_strdup( char const * src ); /** ** Equivalent to strlen(3) but returns 0 if src is NULL. ** Note that it counts bytes, not UTF characters. */ fsl_size_t fsl_strlen( char const * src ); /** ** Like strcmp() except that it accepts NULL pointers. NULL sorts ** before all non-NULL string pointers. Also, this strcmp() is a ** binary comparison that does not consider locale. */ int fsl_strcmp( char const * lhs, char const * rhs ); /** ** Case-insensitive form of fsl_strcmp(). */ int fsl_stricmp(const char *zA, const char *zB); /** ** fsl_strcmp() variant which compares at most nByte bytes ** of the given strings, case-insensitively. */ int fsl_strnicmp(const char *zA, const char *zB, int nByte); /** ** fsl_strcmp() variant which compares at most nByte bytes ** of the given strings, case-sensitively. */ int fsl_strncmp(const char *zA, const char *zB, int nByte); /** ** Flags for use with fsl_db_open() and friends. */ enum fsl_open_flags { /** ** The "no flags" value. */ FSL_OPEN_F_NONE = 0, /** ** Flag for fsl_db_open() specifying that the db should be opened ** in read-only mode. */ FSL_OPEN_F_RO = 0x01, /** ** Flag for fsl_db_open() specifying that the db should be opened ** in read-write mode, but should not create the db if it does ** not already exist. */ FSL_OPEN_F_RW = 0x02, /** ** Flag for fsl_db_open() specifying that the db should be opened in ** read-write mode, creating the db if it does not already exist. */ FSL_OPEN_F_CREATE = 0x04, /** ** Shorthand for RW+CREATE flags. */ FSL_OPEN_F_RWC = FSL_OPEN_F_RW | FSL_OPEN_F_CREATE, /** ** Indicates that a routine should not fail if the given file ** does not yet exist. It should create it in this case (if possible ** for the given routine). Not all routines honor this flag. */ FSL_OPEN_F_NOT_FOUND_OK = 0x10, /** ** Tells fsl_cx_repo_open_xxx() to confirm that the db ** is a repository. */ FSL_OPEN_F_SCHEMA_VALIDATE = 0x20 }; /** ** Quivalent to fopen(3) but expects name to be UTF8-encoded. */ FILE * fsl_fopen(char const * name, char const *mode); /** ** Opens the given db file and populates db with its handle. db ** must have been cleanly initialized by copy-constructing it from ** fsl_db_empty or allocating it using fsl_db_malloc(). Failure to ** do so will lead to undefined behaviour. ** ** openFlags may be a mask of FSL_OPEN_F_xxx values. If ** FSL_OPEN_F_NOT_FOUND_OK is _not_ set in openFlags and dbFile ** does not exist, it will return FSL_RC_NOT_FOUND. The existence ** of FSL_OPEN_F_CREATE in the flags trumps FSL_OPEN_F_NOT_FOUND_OK, ** so a not-found error is not reported if openMode contains has the ** FSL_OPEN_F_CREATE bit set. ** ** Returns FSL_RC_MISUSE if !db, !dbFile, !*dbFile, or ** if db->dbh is not NULL. ** ** On error db->dbh will not be set, but db->dbh.error might contain ** error details. ** ** Regardless of success or failure, db should be passed to ** fsl_db_close() to free up all memory associated with it. It is ** not closed automatically by this function because doing so cleans ** up the error state, which the caller will presumably want to ** have. ** ** If db->f is not NULL then it is assumed that db should be plugged ** in to the repository system, and the following additions things ** happen: ** ** - A number of SQL functions are registered with the db. ** ** - If db->FSL_OPEN_F_SCHEMA_VALIDATE is set in openFlags then the ** db is validated to see if it has a fossil schema. If that ** validation fails, FSL_RC_REPO_NEEDS_REBUILD or FSL_RC_NOT_A_REPO ** will be returned and db's error state will be updated. ** ** ** Note that this is a lower level operation than the fossil ** binary's 'open' command, and simply initializes f's db-related ** resources, and does not check out any files. */ int fsl_db_open( fsl_db * db, char const * dbFile, int openFlags ); /** ** Closes the given db handle and frees any resources owned by ** db. Returns 0 on success. ** ** If db was allocated using fsl_db_alloc() (as determined by ** examining db->allocStamp) then this routine also fsl_free()s it, ** otherwise it is assumed to either be on the stack or part of a ** larger struct and is not freed. */ int fsl_db_close( fsl_db * db ); /** ** Allocates a new fsl_db instance(). Returns NULL on allocation ** error. Note that fsl_db instances can often be used from the ** stack - allocating them dynamically is an uncommon case necessary ** for script bindings. ** ** Achtung: the returned value's allocStamp member is used for ** determining if fsl_db_close() should free the value or not. Thus ** if clients copy over this value without adjusting allocStamp back ** to its original value, the library will likely leak the instance. ** Been there, done that. */ fsl_db * fsl_db_malloc(); /** ** The fsl_stmt counterpart of fsl_db_malloc(). See that function ** for when you might want to use this and a caveat involving the ** allocStamp member of the returned value. fsl_stmt_finalize() will ** free statements created with this function. */ fsl_stmt * fsl_stmt_malloc(); /** ** 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. ** ** openFlags is a bitmask of FSL_OPEN_F_xxx. If it is 0 then this ** default is used: (FSL_OPEN_F_RW | FSL_OPEN_F_SCHEMA_VALIDATE). ** ** fsl_db_open() for the meaning of the openFlags parameter ** and various other details. */ int fsl_cx_repo_open_db( fsl_cx * f, char const * repoDbFile, int openFlags ); /** ** ** Tries to open a checked-out fossil repository db in the given ** directory. It looks for files named (_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 has 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 or not 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. ** ** 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 has already opened a checkout as ** its main database (it can only handle one at a time). ** ** - FSL_RC_OOM if an allocation fails. ** ** - 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). ** ** ** TODOs: ** ** - automatically open repo db based on checkout's repo. Awaiting ** porting of some db-level utility code. ** ** - If a repo db has already been opened, ensure that it belongs ** to the checkout being opened, else return FSL_RC_MISUSE or ** FSL_RC_CONSISTENCY, or maybe a new code. ** */ int fsl_cx_checkout_open( fsl_cx * f, char const * dirName, fsl_int_t dirNameLen ); /** ** If fsl_cx_checkout_open() 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_A_CHECKOUT if f has not opened a checkout as its ** primary DB. ** ** If alsoCloseRepo is true and a repository DB is opened then ** it is also closed. ** ** TODO: also close repo db here? */ int fsl_cx_checkout_close( fsl_cx * f, char alsoCloseRepo ); /** ** 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_cx_checkout_open() ** @see fsl_cx_checkout_dir_name() */ char const * fsl_cx_checkout_db_file(fsl_cx const * f, fsl_size_t * len); /** ** Equivalent to fsl_cx_checkout_db_file() except that ** it applies to the name of the opened repository db, ** if any. */ char const * fsl_cx_repo_db_file(fsl_cx const * f, fsl_size_t * len); /** ** Equivalent to fsl_cx_checkout_db_file() except that ** it applies to the name of the opened config db, ** if any. */ char const * fsl_cx_config_db_file(fsl_cx const * f, fsl_size_t * len); /** ** Equivalent to fsl_cx_checkout_db_file() 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. ** ** FIXME: the 1st parameter "should" be const - it is not modified ** here - but it currently cannot be without violating an internal ** constness guaranty which can be fixed with a little code ** duplication. */ char const * fsl_cx_db_file_for_role(fsl_cx * f, fsl_db_role_t r, fsl_size_t * len); /** ** If f has an opened checkout db (from fsl_cx_checkout_open()) ** 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_cx_checkout_open() ** @see fsl_cx_checkout_db_name() */ 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 current architecture sets up an in-memory DB ** as the main db and ATTACHes all others. Whether or not ** this is a simpler/better approach than managing ** multiple DB connections is as yet unclear. **/ fsl_db * fsl_cx_db( fsl_cx * f ); /** ** If f is not NULL and has had its repo opened via ** fsl_cx_repo_open_db() or similar, this returns a pointer to that ** database, else it returns NULL. */ fsl_db * fsl_cx_db_repo( fsl_cx * f ); /** ** If f is not NULL and has had a checkout opened via ** fsl_cx_checkout_open() or similar, this returns a pointer to that ** database, else it returns NULL. */ fsl_db * fsl_cx_db_checkout( fsl_cx * f ); /** ** IN PROGRESS/INCOMPLETE ** ** 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_config_db_file(). ** ** ** 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. ** ** These notes do not 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_cx_config_open( fsl_cx * f, char const * dbName ); /** ** Closes/detaches the database connection opened by ** fsl_cx_config_open(). */ int fsl_cx_config_close( fsl_cx * f ); /* ** If f has an opened configuration db then its handle is returned. ** It will return NULL if the config db was opened via "attaching" ** it. See fsl_cx_config_open(). */ fsl_db * fsl_cx_db_config( fsl_cx * f ); #if 0 /** ** TODO. ** ** Should create a new fossil repo db. Do we need some sort of progress ** callback here? Creation is fast, so probably not. ** */ int fsl_cx_repo_create_db( fsl_cx * f, char const * repoDbFile, int flags /**???*/ ); #endif /** ** If fsl_cx_repo_open_xxx() 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_A_REPO if f has not opened a repository. ** ** TODO: also close checkout db here? */ int fsl_cx_repo_close( fsl_cx * f ); /** ** Convenience form of fsl_stmt_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 ); /** ** @typedef long (*fsl_appendf_f)( void * arg, char const * data, long n ) ** ** The fsl_appendf_f typedef is used to provide fsl_appendfv() with ** a flexible output routine, so that it can be easily send its ** output to arbitrary targets. ** ** The policies which implementations need to follow are: ** ** - arg is an implementation-specific pointer (may be 0) which is ** passed to vappendf. fsl_appendfv() doesn't know what this ** argument is but passes it to its fsl_appendf_f. Typically it ** will be an object or resource handle to which string data is ** pushed or output. ** ** - The 'data' parameter is the data to append. If it contains ** embedded nulls, this function will stop at the first one. Thus ** it is not binary-safe. ** ** - n is the number of bytes to read from data. If n<0 then ** strlen(data) should be used. ** ** - Returns, on success, the number of bytes appended (may be 0). ** ** - Returns, on error, an implementation-specified negative ** number. Returning a negative error code will cause ** fsl_appendfv() to stop the processing of that string. Note that ** 0 is a success value (some printf format specifiers do not add ** anything to the output). */ typedef long (*fsl_appendf_f)( void * arg, char const * data, long n ); /** ** This function works similarly to classical printf ** implementations, but instead of outputing somewhere specific, it ** uses a callback function to push its output somewhere. This ** allows it to be used for arbitrary external representations. It ** can be used, for example, to output to an external string, a UI ** widget, or file handle (it can also emulate printf by outputing ** to stdout this way). ** ** INPUTS: ** ** pfAppend: The is a fsl_appendf_f function which is responsible ** for accumulating the output. If pfAppend returns a negative ** integer then processing stops immediately. ** ** pfAppendArg: is ignored by this function but passed as the first ** argument to pfAppend. pfAppend will presumably use it as a data ** store for accumulating its string. ** ** fmt: This is the format string, as in the usual printf(). ** ** ap: This is a pointer to a list of arguments. Same as in ** vprintf() and friends. ** ** ** OUTPUTS: ** ** The return value is the total number of characters sent to the ** function "func", or a negative number on a pre-output error. If ** this function returns an integer greater than 1 it is in general ** impossible to know if all of the elements were output. As such ** failure can only happen if the callback function returns an ** error, and this type of error is very rare in a printf-like ** context, this is not considered to be a significant problem. (The ** same is true for any classical printf implementations.) Clients ** may use their own state objects which can propagate errors from ** their own internals back to the caller, but generically speaking ** it is difficult to trace errors back through this routine. Then ** again, it only breaks when using using a broken output routine or ** bad output state. ** ** CURRENT (documented) PRINTF EXTENSIONS: ** ** %%z works like %%s, but takes a non-const (char *) and vappendf ** deletes the string (using fsl_free()) after appending it to the ** output. ** ** %%h (HTML) works like $%s but converts certain characters (namely ** '<' and '&') to their HTML escaped equivalents. ** ** %%t (URL encode) works like %%s but converts certain characters ** into a representation suitable for use in an HTTP URL. (e.g. ' ' ** gets converted to %%20) ** ** %%T (URL decode) does the opposite of %t - it decodes URL-encoded ** strings. ** ** %%r requires an int and renders it in "ordinal form". That is, ** the number 1 converts to "1st" and 398 converts to "398th". ** ** %%q quotes a string as required for SQL. That is, '\'' characters ** get doubled. ** ** %%Q as %%q, but includes the outer '\'' characters and null ** pointers replaced by SQL NULL. ** ** %%/: requires a C-string parameter. Normalizes path-like strings ** by replacing backslashes with the One True Slash. ** ** %%b: works like %%s but takes its input from a (fsl_buffer*) ** argument. ** ** %%B: works like %%Q but takes its input from a (fsl_buffer*) ** argument. ** ** These extensions may be disabled by setting certain macros when ** compiling fsl_appendf.c (see that file for details). ** ** ** FIXME? fsl_appendf_f() is an artifact of older code from which ** this implementation derives. The first parameter should arguably ** be replaced with fsl_output_f(), which does the same thing _but_ ** has different return semantics (more reliable, because the ** current semantics report partial success as success in some ** cases). Doing this would require us to change the return ** semantics of this function, but that wouldn't necessarily be a ** bad thing (we don't rely on sprintf()-like return semantics all ** that much, AFAIK?). Or we just add a proxy which forwards to a ** fsl_output_f(). Oh, hey, that's what fsl_outputf() does. ** ** TODO?: replace 'long' with either an int (using fsl_output_f() ** semantics) or an int using sprintf() semantics (as it is now). */ long fsl_appendfv(fsl_appendf_f pfAppend, void * pfAppendArg, const char *fmt, va_list ap ); /** ** ** Identical to fsl_appendfv() but takes a (...) ellipses list ** instead of a va_list. */ long fsl_appendf(fsl_appendf_f pfAppend, void * pfAppendArg, const char *fmt, ... ); /** ** Emulates fprintf() using fsl_appendf(). Returns the result of ** passing the data through fsl_appendf() to the given file handle. */ long fsl_appendf_FILE( FILE * fp, char const * fmt, ... ); /** ** Works like fsl_appendfv(), but appends all output to a ** dynamically-allocated string, expanding the string as necessary ** to collect all formatted data. The returned NUL-terminated string ** is owned by the caller and it must be cleaned up using ** fsl_free(...). If !fmt, NULL is returned. It is conceivable that ** it returns NULL on a zero-length formatted string, e.g. (%.*s) ** with (0,"...") as arguments, but it will only do that if the ** whole format string resolves to empty. */ char * fsl_mprintf( char const * fmt, ... ); /** ** va_list counterpart to fsl_mprintf(). */ char * fsl_mprintfv(char const * fmt, va_list vargs ); /** ** Possibly reallocates self->list, changing its size. This function ** ensures that self->list has at least n entries. If n is 0 then ** the list is deallocated (but the self object is not), BUT THIS ** DOES NOT DO ANY TYPE-SPECIFIC CLEANUP of the items. If n is less ** than or equal to self->alloced then there are no side effects. If ** n is greater than self->alloced, self->list is reallocated and ** self->alloced is adjusted to be at least n (it might be bigger - ** this function may pre-allocate a larger value). ** ** Passing an n of 0 when self->alloced is 0 is a no-op. ** ** Newly-allocated slots will be initialized with NUL bytes. ** ** Returns the total number of items allocated for self->list. On ** success, the value will be equal to or greater than n (in the ** special case of n==0, 0 is returned). Thus a return value smaller ** than n is an error. Note that if n is 0 or self is NULL then 0 is ** returned. ** ** The return value should be used like this: ** ** @code ** fsl_size_t const n = number of bytes to allocate; ** if( n > fsl_list_reserve( myList, n ) ) { ... error ... } ** // Or the other way around: ** if( fsl_list_reserve( myList, n ) < n ) { ... error ... } ** @endcode */ fsl_size_t fsl_list_reserve( fsl_list * self, fsl_size_t n ); /** ** Appends a bitwise copy of cp to self->list, expanding the list as ** necessary and adjusting self->count. ** ** Ownership of cp is unchanged by this call. cp may not be NULL. ** ** Returns 0 on success, FSL_RC_MISUSE if any argument is NULL, or ** FSL_RC_OOM on allocation error. */ int fsl_list_append( fsl_list * self, void * cp ); /** @typedef typedef int (*fsl_list_visitor_f)(void * p, void * visitorState ) ** ** Generic visitor interface for fsl_list lists. Used by ** fsl_list_visit(). p is the pointer held by that list entry and ** visitorState is the 4th argument passed to fsl_list_visit(). ** ** Implementations must return 0 on success. Any other value causes ** looping to stop and that value to be returned, but interpration ** of the value is up to the caller (it might or might not be an ** error, depending on the context). Note that client code may use ** custom values, and is not restricted to FSL_RC_xxx values. */ typedef int (*fsl_list_visitor_f)(void * obj, void * visitorState ); /** ** For each item in self->list, visitor(item,visitorState) is ** called. The item is owned by self. The visitor function MUST ** NOT free the item, but may manipulate its contents if ** application rules do not specify otherwise. ** ** ** If order is 0 or greater then the list is traversed from start ** to finish, else it is traverse from end to begin. ** ** ** Returns 0 on success, non-0 on error. ** ** If visitor() returns non-0 then looping stops and that code is ** returned. */ int fsl_list_visit( fsl_list * self, char order, fsl_list_visitor_f visitor, void * visitorState ); /** ** ** ** Works similarly to the visit operation without the _p suffix ** except that the pointer the visitor function gets is a (**) ** pointing back to the entry within this list. That means that ** callers can assign the entry in the list to another value during ** the traversal process (e.g. set it to 0). If shiftIfNulled is ** true then if the callback sets the list's value to 0 then it is ** removed from the list and self->count is adjusted (self->alloced ** is not changed). */ int fsl_list_visit_p( fsl_list * self, char order, char shiftIfNulled, fsl_list_visitor_f visitor, void * visitorState ); /** ** Returns 0 if the given file is readable. Flags may be any values ** accepted by the accept(2) resp. _waccept() system calls. */ int fsl_file_access(const char *zFilename, int flags); /** ** Compute a canonical pathname for a file or directory. ** Make the name absolute if it is relative. ** Remove redundant / characters. ** Remove all /./ path elements. ** Convert /A/../ to just /. ** If the slash parameter is non-zero, the trailing slash, if any, ** is retained. ** ** Returns 0 on success, FSL_RC_MISUSE if !zOrigName or !pOut, ** FSL_RC_OOM if an allocation fails. ** ** pOut is _appended_ to, so be sure to set pOut->used=0 (or pass it ** to fsl_buffer_reset()) before calling this if you want to start ** writing at the beginning. */ int fsl_file_canonical_name(const char *zOrigName, fsl_buffer *pOut, char slash); /** ** Writes the absolute path name of the current directory to zBuf, ** which must be at least nBuf bytes long (nBuf includes the space ** for a trailing NUL terminator). ** ** Returns FSL_RC_RANGE if the name would be too long for nBuf, ** FSL_RC_IO if it cannot determine the current directory (e.g. a ** side effect of having removed the directory at runtime or similar ** things), and 0 on success. ** ** On success, if outLen is not NULL then the length of the string ** written to zBuf is assigned to *outLen The output string is ** always NUL-terminated. ** ** On Windows, the name is converted from unicode to UTF8 and all '\\' ** characters are converted to '/'. No conversions are needed on ** Unix. */ int fsl_getcwd(char *zBuf, fsl_size_t nBuf, fsl_size_t * outLen); /** ** Return non-zero if string z matches glob pattern zGlob and zero ** if the pattern does not match. Always returns 0 if either ** argument is NULL. ** ** Globbing rules: ** ** '*' Matches any sequence of zero or more characters. ** ** '?' Matches exactly one character. ** ** [...] Matches one character from the enclosed list of ** characters. ** ** [^...] Matches one character not in the enclosed list. */ char fsl_str_glob(const char *zGlob, const char *z); /** ** Return the size of a file in bytes. Return -1 if the file does not ** exist or is not stat()able. */ fsl_int64_t fsl_file_size(const char *zFilename); /** ** Same as fsl_file_size(), but takes into account symlinks. ** (What does that mean, anyway?) */ fsl_int64_t fsl_file_wd_size(const char *zFilename); /** ** Return the modification time for a file. Return -1 if the file ** does not exist. */ fsl_time_t fsl_file_mtime(const char *zFilename); /** ** Same as fsl_file_mtime(), but takes into account symlinks. */ fsl_time_t fsl_file_wd_mtime(const char *zFilename); /** ** Return TRUE if the named file is an ordinary file or symlink ** and symlinks are allowed. ** Return false for directories, devices, fifos, etc. */ int fsl_file_wd_isfile_or_link(const char *zFilename); /** ** Return TRUE if the named file is an ordinary file. Return false ** for directories, devices, fifos, symlinks, etc. */ int fsl_file_isfile(const char *zFilename); /** ** Same as fsl_file_isfile(), but takes into account symlinks. */ int fsl_file_wd_isfile(const char *zFilename); /** ** Returns true if the given path appears to be absolute, else ** false. On Unix a path is absolute if it starts with a '/'. On ** Windows a path is also absolute if it starts with a letter, a ** colon, and a backslash. */ char fsl_file_is_absolute_path(const char *zPath); /** ** Returns true, else false if the given letter is an ASCII alphabet ** character. */ char fsl_isalpha(int ch); char fsl_islower(int ch); char fsl_isspace(int c); char fsl_islower(int c); char fsl_isupper(int c); char fsl_isdigit(int c); char fsl_tolower(int c); char fsl_toupper(int c); char fsl_isalpha(int c); char fsl_isalnum(int c); typedef struct fsl_sha1_cx fsl_sha1_cx; /** ** Holds state for SHA1 calculations. They are to be used like this: ** ** @code ** unsigned char digest[20] ** char hex[41]; ** fsl_sha1_cx cx; ** fsl_sha1_init(&cx); ** ...call fsl_sha1_update(&cx,...) any number of times to ** ...incrementally calculate the hash. ** fsl_sha1_final(&cx, digest); // ends the calculation ** fsl_sha1_digest_to_base16(&cx, hex); ** // digest now contains the raw 20-byte SHA1 digest. ** // hex now contains the 40-byte SHA1 + a trailing NUL ** @endcode */ struct fsl_sha1_cx { unsigned int state[5]; unsigned int count[2]; unsigned char buffer[64]; }; /** ** Initializes the given context with the initial SHA1 state. This ** must be the first routine called on an SHA1 context, and passing ** this context to other SHA1 routines without first having passed ** it to this will lead to undefined results. */ void fsl_sha1_init(fsl_sha1_cx *context); /** ** Updates the given context to include the hash of the first len bytes ** of the given data. */ void fsl_sha1_update( fsl_sha1_cx *context, const unsigned char *data, unsigned int len); /** ** Add padding and finalizes the message digest. If digest is not ** NULL then it writes 20 bytes of digest to the 2nd parameter. */ void fsl_sha1_final(fsl_sha1_cx *context, unsigned char * digest); /** ** Convert a digest into base-16. digest must be at least 20 ** bytes long (the SHA1 digest is stored in the first 20 bytes). ** zBuf must be at least 41 bytes long, for 40 characters of SHA1 ** hash and 1 NUL byte. */ void fsl_sha1_digest_to_base16(unsigned char *digest, char *zBuf); /** ** Compute the SHA1 checksum of pIn and stores the resulting ** checksum in the buffer pCksum. pCksum's memory is re-used if is ** has any allocated to it. pCksum may == pIn, in which case this is ** a destructive operation (replacing the data to hash with its ** hash code). ** ** Return 0 on success, FSL_RC_OOM if (re)allocating pCksum fails. */ int fsl_sha1sum_buffer(fsl_buffer const *pIn, fsl_buffer *pCksum); /** ** Computes the SHA1 checksum of the first len bytes of the given ** string. If len is negative then zInt must be NUL-terminated and ** fsl_strlen() is used to find its length.. The result is a ** length-40 string (+NUL byte) returned in memory obtained from ** fsl_malloc(), so it must be passed to fsl_free() to free it. If ** NULL==zIn or !len then NULL is returned. */ char *fsl_sha1sum_cstr(const char *zIn, int len); /** ** NYI: missing some permissions bits from v1. ** ** Compute the SHA1 checksum of a file on disk. Store the resulting ** checksum in the buffer pCksum. ** ** Returns 0 on success. */ int fsl_sha1sum_file(const char *zFilename, fsl_buffer *pCksum); #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* NET_FOSSIL_SCM_FOSSIL2_H_INCLUDED */