Index: common.make ================================================================== --- common.make +++ common.make @@ -1,7 +1,7 @@ all: -SHELL:=/bin/bash +SHELL:=/bin/sh ifeq (0,1) # FIXME: update for 3.82+ MAKE_REQUIRED_VERSION := 381# MAKE_VERSION stripped of any dots VERSION_CHECK := \ $(shell \ DELETED fossil2.c Index: fossil2.c ================================================================== --- fossil2.c +++ /dev/null @@ -1,289 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* -** 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 "fossil/fossil2.h" -#include -#include -#include -#include -#include -#include - -const fsl_buffer fsl_buffer_empty = fsl_buffer_empty_m; -const fsl_init_param fsl_init_param_empty = fsl_init_param_empty_m; -const fsl_init_param fsl_init_param_default = fsl_init_param_default_m; -const fsl_outputer fsl_outputer_FILE = fsl_outputer_FILE_m; -const fsl_ctx fsl_ctx_empty = fsl_ctx_empty_m; -const fsl_db fsl_db_empty = fsl_db_empty_m; -const fsl_stmt fsl_stmt_empty = { -NULL/*f*/, -NULL/*stmt*/, -NULL/*db*/, -fsl_buffer_empty_m/*sql*/, -0/*colCount*/, -0/*paramCount*/ -}; - -#define fsl_db_record_empty_m { \ -FSL_TYPE_INVALID /*typeId*/,\ -0/*dbId*/,\ -NULL/*next*/\ -} -const fsl_db_record fsl_db_record_empty = fsl_db_record_empty_m; - -const fsl_user fsl_user_empty = { -fsl_db_record_empty_m /*base*/, -fsl_buffer_empty_m /* name */, -FSL_ROLE_GUEST /* roles */, -0/*mtime*/ -}; - -const fsl_tag fsl_tag_empty = { -fsl_db_record_empty_m /*base*/, -fsl_buffer_empty_m /* key */, -fsl_buffer_empty_m /* value */ -}; - -const fsl_blob fsl_blob_empty = { -fsl_db_record_empty_m /*base*/, -0/*mtime*/, -{/*sha1*/0} -}; - -const fsl_allocator fsl_allocator_stdalloc = { -fsl_realloc_f_stdalloc, -NULL -}; - -fsl_allocator fsl_memory_allocator = { -fsl_realloc_f_stdalloc, -NULL -}; - - - -void * fsl_malloc( fsl_size_t n ){ - return n - ? fsl_memory_allocator.f(fsl_memory_allocator.state, NULL, n) - : NULL; -} - -void fsl_free( void * mem ){ - if(mem) fsl_memory_allocator.f(fsl_memory_allocator.state, mem, 0); -} - -void * fsl_realloc( void * mem, fsl_size_t n ){ - if(!mem){ - return fsl_malloc(n); - }else if(!n){ - fsl_free(mem); - return NULL; - }else{ - return fsl_memory_allocator.f(fsl_memory_allocator.state, mem, n); - } -} - -void * fsl_realloc_f_stdalloc(void * state, void * mem, fsl_size_t n){ - if(!mem){ - return malloc(n); - }else if(!n){ - free(mem); - return NULL; - }else{ - return realloc(mem, n); - } -} - - - -int fsl_flush_f_FILE(void * _FILE){ - return _FILE - ? (fflush((FILE*)_FILE) ? FSL_RC_IO : 0) - : FSL_RC_MISUSE; -} - -int fsl_output_f_FILE( void * state, - void const * src, fsl_size_t n ){ - return !state - ? FSL_RC_MISUSE - : ((1 == fwrite(src, n, 1, state ? (FILE*)state : stdout)) - ? 0 - : FSL_RC_IO); -} - -void fsl_finalizer_f_FILE( void * state, void * mem ){ - if(mem && (stdout != mem) && (stderr != mem)){ - fclose((FILE*)mem); - } -} - -int fsl_init( fsl_ctx ** tgt, fsl_init_param * param ){ - fsl_init_param paramDefaults = fsl_init_param_default; - fsl_ctx * f; - if(!tgt) return FSL_RC_MISUSE; - else if(!param){ - param = ¶mDefaults; - } - f = (fsl_ctx*)fsl_malloc(sizeof(fsl_ctx)); - if(!f) return FSL_RC_OOM; - *tgt = f; - - *f = fsl_ctx_empty; - f->output = param->output; - return 0; -} - - -int fsl_finalize( fsl_ctx * f ){ - if(!f) return FSL_RC_MISUSE; - else{ - if(f->clientState.finalize.f){ - f->clientState.finalize.f( f->clientState.finalize.state, - f->clientState.state ); - } - if(f->output.state.finalize.f){ - f->output.state.finalize.f( f->output.state.finalize.state, - f->output.state.state ); - } - fsl_buffer_reserve(&f->error.msg, 0); - fsl_buffer_reserve(&f->dbRepo.filename, 0); - assert(0 == f->dbRepo.openStatementCount); - if(f->dbRepo.dbh){ - fsl_db_close(f, &f->dbRepo); - } - *f = fsl_ctx_empty; - fsl_free(f); - return 0; - } -} - -int fsl_output( fsl_ctx * cx, void const * src, fsl_size_t n ){ - if(!cx || !src) return FSL_RC_MISUSE; - else if(!n || !cx->output.out) return 0; - else return cx->output.out( cx->output.state.state, - src, n ); -} - -int fsl_err_setv( fsl_ctx * f, int code, char const * fmt, - va_list args ){ - if(!f) return FSL_RC_MISUSE; - else if(!code){ /* clear error state */ - f->error.code = 0; - f->error.msg.used = 0; - if(f->error.msg.mem){ - f->error.msg.mem[0] = 0; - } - return 0; - }else{ - int rc = 0; - f->error.msg.used = 0; - f->error.code = code; - if(FSL_RC_OOM!=code){ - rc = fsl_buffer_appendfv(&f->error.msg, - fmt ? fmt : fsl_rc_cstr(code), - args); - } - return rc ? rc : code; - } -} - -int fsl_err_set( fsl_ctx * f, int code, char const * fmt, - ... ){ - if(!f || !fmt) return FSL_RC_MISUSE; - else{ - int rc; - va_list args; - va_start(args,fmt); - rc = fsl_err_setv( f, code, fmt, args ); - va_end(args); - return rc; - } -} - -int fsl_err_get( fsl_ctx * f, char const ** str, fsl_size_t * len ){ - if(!f) return FSL_RC_MISUSE; - else{ - if(str) *str = (char const *)f->error.msg.mem; - if(len) *len = f->error.msg.used; - return f->error.code; - } -} - -/* -** fsl_appendf_f() impl which sends its output to fsl_output(). -*/ -static long fsl_appendf_f_fsl_output( void * S, char const * s, - long n ){ - fsl_ctx * f = (fsl_ctx *)S; - int rc = fsl_output( f, s, (fsl_size_t)n ); - return rc ? -1 : n; -} - -int fsl_outputfv( fsl_ctx * f, char const * fmt, va_list args ){ - if(!f || !fmt) return FSL_RC_MISUSE; - else{ - long const prc = fsl_appendfv( fsl_appendf_f_fsl_output, - f, fmt, args ); - return prc>=0 ? 0 : FSL_RC_IO; - } -} - - -int fsl_outputf( fsl_ctx * f, char const * fmt, ... ){ - if(!f || !fmt) return FSL_RC_MISUSE; - else{ - int rc; - va_list args; - va_start(args,fmt); - rc = fsl_outputfv( f, fmt, args ); - va_end(args); - return rc; - } -} - - -char const * fsl_rc_cstr(int rc){ - switch(rc){ -#define STR(T) case FSL_RC_##T: return "FSL_RC_" #T - STR(OK); - STR(NYI); - STR(ERROR); - STR(OOM); - STR(MISUSE); - STR(RANGE); - STR(ACCESS); - STR(IO); - STR(NOT_FOUND); - STR(ALREADY_EXISTS); - STR(CONSISTENCY); - STR(REPO_NEEDS_REBUILD); - STR(NOT_A_REPO); - STR(REPO_VERSION); - STR(DB); -#undef STR - default: - return "Unknown result code"; - } -} - -char const * fsl_library_version(){ - return FSL_LIBRARY_VERSION; -} DELETED fsl_appendf.c Index: fsl_appendf.c ================================================================== --- fsl_appendf.c +++ /dev/null @@ -1,1383 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/************************************************************************ -The printf-like implementation in this file is based on the one found -in the sqlite3 distribution is in the Public Domain. - -This copy was forked for use with the clob API in Feb 2008 by Stephan -Beal (http://wanderinghorse.net/home/stephan/) and modified to send -its output to arbitrary targets via a callback mechanism. Also -refactored the %X specifier handlers a bit to make adding/removing -specific handlers easier. - -All code in this file is released into the Public Domain. - -The printf implementation (fsl_appendfv()) is pretty easy to extend -(e.g. adding or removing %-specifiers for fsl_appendfv()) if you're -willing to poke around a bit and see how the specifiers are declared -and dispatched. For an example, grep for 'etSTRING' and follow it -through the process of declaration to implementation. - -See below for several FSLPRINTF_OMIT_xxx macros which can be set to -remove certain features/extensions. -************************************************************************/ - -#include /* FILE */ -#include /* strlen() */ -#include /* free/malloc() */ -#include -#include -#include -#include "fossil/fossil2.h" - -/* FIXME: determine this type at compile time */ -typedef long double LONGDOUBLE_TYPE; - -/* - If FSLPRINTF_OMIT_FLOATING_POINT is defined to a true value, then - floating point conversions are disabled. -*/ -#ifndef FSLPRINTF_OMIT_FLOATING_POINT -# define FSLPRINTF_OMIT_FLOATING_POINT 0 -#endif - -/* - If FSLPRINTF_OMIT_SIZE is defined to a true value, then - the %n specifier is disabled. -*/ -#ifndef FSLPRINTF_OMIT_SIZE -# define FSLPRINTF_OMIT_SIZE 0 -#endif - -/* - If FSLPRINTF_OMIT_SQL is defined to a true value, then - the %q and %Q specifiers are disabled. -*/ -#ifndef FSLPRINTF_OMIT_SQL -# define FSLPRINTF_OMIT_SQL 0 -#endif - -/* - If FSLPRINTF_OMIT_HTML is defined to a true value then the %h (HTML - escape), %t (URL escape), and %T (URL unescape) specifiers are - disabled. -*/ -#ifndef FSLPRINTF_OMIT_HTML -# define FSLPRINTF_OMIT_HTML 0 -#endif - -/* - Most C compilers handle variable-sized arrays, so we enable - that by default. Some (e.g. tcc) do not, so we provide a way - to disable it: set FSLPRINTF_HAVE_VARARRAY to 0 - - One approach would be to look at: - - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) - - but some compilers support variable-sized arrays even when not - explicitly running in c99 mode. -*/ -#if !defined(FSLPRINTF_HAVE_VARARRAY) -# if defined(__TINYC__) -# define FSLPRINTF_HAVE_VARARRAY 0 -# else -# if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) -# define FSLPRINTF_HAVE_VARARRAY 1 /*use 1 in C99 mode */ -# else -# define FSLPRINTF_HAVE_VARARRAY 0 -# endif -# endif -#endif - -/** - FSLPRINTF_CHARARRAY is a helper to allocate variable-sized arrays. - This exists mainly so this code can compile with the tcc compiler. -*/ -#if FSLPRINTF_HAVE_VARARRAY -# define FSLPRINTF_CHARARRAY(V,N) char V[N+1]; memset(V,0,N+1); -# define FSLPRINTF_CHARARRAY_FREE(V) -#else -# define FSLPRINTF_CHARARRAY(V,N) char * V = (char *)malloc(N+1); memset(V,0,N+1); -# define FSLPRINTF_CHARARRAY_FREE(V) free(V) -#endif - -/* - Conversion types fall into various categories as defined by the - following enumeration. -*/ -enum PrintfCategory {etRADIX = 1, /* Integer types. %d, %x, %o, and so forth */ - etFLOAT = 2, /* Floating point. %f */ - etEXP = 3, /* Exponentional notation. %e and %E */ - etGENERIC = 4, /* Floating or exponential, depending on exponent. %g */ - etSIZE = 5, /* Return number of characters processed so far. %n */ - etSTRING = 6, /* Strings. %s */ - etDYNSTRING = 7, /* Dynamically allocated strings. %z */ - etPERCENT = 8, /* Percent symbol. %% */ - etCHARX = 9, /* Characters. %c */ - /* The rest are extensions, not normally found in printf() */ - etCHARLIT = 10, /* Literal characters. %' */ -#if !FSLPRINTF_OMIT_SQL - etSQLESCAPE = 11, /* Strings with '\'' doubled. %q */ - etSQLESCAPE2 = 12, /* Strings with '\'' doubled and enclosed in '', - NULL pointers replaced by SQL NULL. %Q */ - etSQLESCAPE3 = 16, /* %w -> Strings with '\"' doubled */ -#endif /* !FSLPRINTF_OMIT_SQL */ - etPOINTER = 15, /* The %p conversion */ - etORDINAL = 17, /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */ -#if ! FSLPRINTF_OMIT_HTML - etHTML = 18, /* %h -> basic HTML escaping. */ - etURLENCODE = 19, /* %t -> URL encoding. */ - etURLDECODE = 20, /* %T -> URL decoding. */ -#endif - etPLACEHOLDER = 100 - }; - -/* - An "etByte" is an 8-bit unsigned value. -*/ -typedef unsigned char etByte; - -/* - Each builtin conversion character (ex: the 'd' in "%d") is described - by an instance of the following structure -*/ -typedef struct et_info { /* Information about each format field */ - char fmttype; /* The format field code letter */ - etByte base; /* The base for radix conversion */ - etByte flags; /* One or more of FLAG_ constants below */ - etByte type; /* Conversion paradigm */ - etByte charset; /* Offset into aDigits[] of the digits string */ - etByte prefix; /* Offset into aPrefix[] of the prefix string */ -} et_info; - -/* - Allowed values for et_info.flags -*/ -enum et_info_flags { FLAG_SIGNED = 1, /* True if the value to convert is signed */ - FLAG_EXTENDED = 2, /* True if for internal/extended use only. */ - FLAG_STRING = 4 /* Allow infinity precision */ -}; - -/* - Historically, the following table was searched linearly, so the most - common conversions were kept at the front. - - Change 2008 Oct 31 by Stephan Beal: we reserve an array or ordered - entries for all chars in the range [32..126]. Format character - checks can now be done in constant time by addressing that array - directly. This takes more static memory, but reduces the time and - per-call overhead costs of fsl_appendfv(). -*/ -static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; -static const char aPrefix[] = "-x0\000X0"; -static const et_info fmtinfo[] = { -/** - If FSLPRINTF_FMTINFO_FIXED is 1 then we use the original - implementation: a linear list of entries. Search time is linear. If - FSLPRINTF_FMTINFO_FIXED is 0 then we use a fixed-size array which - we index directly using the format char as the key. -*/ -#define FSLPRINTF_FMTINFO_FIXED 0 -#if FSLPRINTF_FMTINFO_FIXED -{ 'd', 10, FLAG_SIGNED, etRADIX, 0, 0 }, -{ 's', 0, FLAG_STRING, etSTRING, 0, 0 }, -{ 'g', 0, FLAG_SIGNED, etGENERIC, 30, 0 }, -{ 'z', 0, FLAG_STRING, etDYNSTRING, 0, 0 }, -{ 'c', 0, 0, etCHARX, 0, 0 }, -{ 'o', 8, 0, etRADIX, 0, 2 }, -{ 'u', 10, 0, etRADIX, 0, 0 }, -{ 'x', 16, 0, etRADIX, 16, 1 }, -{ 'X', 16, 0, etRADIX, 0, 4 }, -{ 'i', 10, FLAG_SIGNED, etRADIX, 0, 0 }, -#if !FSLPRINTF_OMIT_FLOATING_POINT -{ 'f', 0, FLAG_SIGNED, etFLOAT, 0, 0 }, -{ 'e', 0, FLAG_SIGNED, etEXP, 30, 0 }, -{ 'E', 0, FLAG_SIGNED, etEXP, 14, 0 }, -{ 'G', 0, FLAG_SIGNED, etGENERIC, 14, 0 }, -#endif /* !FSLPRINTF_OMIT_FLOATING_POINT */ -{ '%', 0, 0, etPERCENT, 0, 0 }, -{ 'p', 16, 0, etPOINTER, 0, 1 }, -{ 'r', 10, (FLAG_EXTENDED|FLAG_SIGNED), etORDINAL, 0, 0 }, -#if ! FSLPRINTF_OMIT_SQL -{ 'q', 0, FLAG_STRING, etSQLESCAPE, 0, 0 }, -{ 'Q', 0, FLAG_STRING, etSQLESCAPE2, 0, 0 }, -{ 'w', 0, FLAG_STRING, etSQLESCAPE3, 0, 0 }, -#endif /* !FSLPRINTF_OMIT_SQL */ -#if ! FSLPRINTF_OMIT_HTML -{ 'h', 0, FLAG_STRING, etHTML, 0, 0 }, -{ 't', 0, FLAG_STRING, etURLENCODE, 0, 0 }, -{ 'T', 0, FLAG_STRING, etURLDECODE, 0, 0 }, -#endif /* !FSLPRINTF_OMIT_HTML */ -#if !FSLPRINTF_OMIT_SIZE -{ 'n', 0, 0, etSIZE, 0, 0 }, -#endif -#else /* FSLPRINTF_FMTINFO_FIXED */ -/* - These entries MUST stay in ASCII order, sorted - on their fmttype member! -*/ -{' '/*32*/, 0, 0, 0, 0, 0 }, -{'!'/*33*/, 0, 0, 0, 0, 0 }, -{'"'/*34*/, 0, 0, 0, 0, 0 }, -{'#'/*35*/, 0, 0, 0, 0, 0 }, -{'$'/*36*/, 0, 0, 0, 0, 0 }, -{'%'/*37*/, 0, 0, etPERCENT, 0, 0 }, -{'&'/*38*/, 0, 0, 0, 0, 0 }, -{'\''/*39*/, 0, 0, 0, 0, 0 }, -{'('/*40*/, 0, 0, 0, 0, 0 }, -{')'/*41*/, 0, 0, 0, 0, 0 }, -{'*'/*42*/, 0, 0, 0, 0, 0 }, -{'+'/*43*/, 0, 0, 0, 0, 0 }, -{','/*44*/, 0, 0, 0, 0, 0 }, -{'-'/*45*/, 0, 0, 0, 0, 0 }, -{'.'/*46*/, 0, 0, 0, 0, 0 }, -{'/'/*47*/, 0, 0, 0, 0, 0 }, -{'0'/*48*/, 0, 0, 0, 0, 0 }, -{'1'/*49*/, 0, 0, 0, 0, 0 }, -{'2'/*50*/, 0, 0, 0, 0, 0 }, -{'3'/*51*/, 0, 0, 0, 0, 0 }, -{'4'/*52*/, 0, 0, 0, 0, 0 }, -{'5'/*53*/, 0, 0, 0, 0, 0 }, -{'6'/*54*/, 0, 0, 0, 0, 0 }, -{'7'/*55*/, 0, 0, 0, 0, 0 }, -{'8'/*56*/, 0, 0, 0, 0, 0 }, -{'9'/*57*/, 0, 0, 0, 0, 0 }, -{':'/*58*/, 0, 0, 0, 0, 0 }, -{';'/*59*/, 0, 0, 0, 0, 0 }, -{'<'/*60*/, 0, 0, 0, 0, 0 }, -{'='/*61*/, 0, 0, 0, 0, 0 }, -{'>'/*62*/, 0, 0, 0, 0, 0 }, -{'?'/*63*/, 0, 0, 0, 0, 0 }, -{'@'/*64*/, 0, 0, 0, 0, 0 }, -{'A'/*65*/, 0, 0, 0, 0, 0 }, -{'B'/*66*/, 0, 0, 0, 0, 0 }, -{'C'/*67*/, 0, 0, 0, 0, 0 }, -{'D'/*68*/, 0, 0, 0, 0, 0 }, -{'E'/*69*/, 0, FLAG_SIGNED, etEXP, 14, 0 }, -{'F'/*70*/, 0, 0, 0, 0, 0 }, -{'G'/*71*/, 0, FLAG_SIGNED, etGENERIC, 14, 0 }, -{'H'/*72*/, 0, 0, 0, 0, 0 }, -{'I'/*73*/, 0, 0, 0, 0, 0 }, -{'J'/*74*/, 0, 0, 0, 0, 0 }, -{'K'/*75*/, 0, 0, 0, 0, 0 }, -{'L'/*76*/, 0, 0, 0, 0, 0 }, -{'M'/*77*/, 0, 0, 0, 0, 0 }, -{'N'/*78*/, 0, 0, 0, 0, 0 }, -{'O'/*79*/, 0, 0, 0, 0, 0 }, -{'P'/*80*/, 0, 0, 0, 0, 0 }, -#if FSLPRINTF_OMIT_SQL -{'Q'/*81*/, 0, 0, 0, 0, 0 }, -#else -{'Q'/*81*/, 0, FLAG_STRING, etSQLESCAPE2, 0, 0 }, -#endif -{'R'/*82*/, 0, 0, 0, 0, 0 }, -{'S'/*83*/, 0, 0, 0, 0, 0 }, -{'T'/*84*/, 0, FLAG_STRING, etURLDECODE, 0, 0 }, -{'U'/*85*/, 0, 0, 0, 0, 0 }, -{'V'/*86*/, 0, 0, 0, 0, 0 }, -{'W'/*87*/, 0, 0, 0, 0, 0 }, -{'X'/*88*/, 16, 0, etRADIX, 0, 4 }, -{'Y'/*89*/, 0, 0, 0, 0, 0 }, -{'Z'/*90*/, 0, 0, 0, 0, 0 }, -{'['/*91*/, 0, 0, 0, 0, 0 }, -{'\\'/*92*/, 0, 0, 0, 0, 0 }, -{']'/*93*/, 0, 0, 0, 0, 0 }, -{'^'/*94*/, 0, 0, 0, 0, 0 }, -{'_'/*95*/, 0, 0, 0, 0, 0 }, -{'`'/*96*/, 0, 0, 0, 0, 0 }, -{'a'/*97*/, 0, 0, 0, 0, 0 }, -{'b'/*98*/, 0, 0, 0, 0, 0 }, -{'c'/*99*/, 0, 0, etCHARX, 0, 0 }, -{'d'/*100*/, 10, FLAG_SIGNED, etRADIX, 0, 0 }, -{'e'/*101*/, 0, FLAG_SIGNED, etEXP, 30, 0 }, -{'f'/*102*/, 0, FLAG_SIGNED, etFLOAT, 0, 0}, -{'g'/*103*/, 0, FLAG_SIGNED, etGENERIC, 30, 0 }, -{'h'/*104*/, 0, FLAG_STRING, etHTML, 0, 0 }, -{'i'/*105*/, 10, FLAG_SIGNED, etRADIX, 0, 0}, -{'j'/*106*/, 0, 0, 0, 0, 0 }, -{'k'/*107*/, 0, 0, 0, 0, 0 }, -{'l'/*108*/, 0, 0, 0, 0, 0 }, -{'m'/*109*/, 0, 0, 0, 0, 0 }, -{'n'/*110*/, 0, 0, etSIZE, 0, 0 }, -{'o'/*111*/, 8, 0, etRADIX, 0, 2 }, -{'p'/*112*/, 16, 0, etPOINTER, 0, 1 }, -#if FSLPRINTF_OMIT_SQL -{'q'/*113*/, 0, 0, 0, 0, 0 }, -#else -{'q'/*113*/, 0, FLAG_STRING, etSQLESCAPE, 0, 0 }, -#endif -{'r'/*114*/, 10, (FLAG_EXTENDED|FLAG_SIGNED), etORDINAL, 0, 0}, -{'s'/*115*/, 0, FLAG_STRING, etSTRING, 0, 0 }, -{'t'/*116*/, 0, FLAG_STRING, etURLENCODE, 0, 0 }, -{'u'/*117*/, 10, 0, etRADIX, 0, 0 }, -{'v'/*118*/, 0, 0, 0, 0, 0 }, -#if FSLPRINTF_OMIT_SQL -{'w'/*119*/, 0, 0, 0, 0, 0 }, -#else -{'w'/*119*/, 0, FLAG_STRING, etSQLESCAPE3, 0, 0 }, -#endif -{'x'/*120*/, 16, 0, etRADIX, 16, 1 }, -{'y'/*121*/, 0, 0, 0, 0, 0 }, -{'z'/*122*/, 0, FLAG_STRING, etDYNSTRING, 0, 0}, -{'{'/*123*/, 0, 0, 0, 0, 0 }, -{'|'/*124*/, 0, 0, 0, 0, 0 }, -{'}'/*125*/, 0, 0, 0, 0, 0 }, -{'~'/*126*/, 0, 0, 0, 0, 0 }, -#endif /* FSLPRINTF_FMTINFO_FIXED */ -}; -#define etNINFO (sizeof(fmtinfo)/sizeof(fmtinfo[0])) - -#if ! FSLPRINTF_OMIT_FLOATING_POINT -/* - "*val" is a double such that 0.1 <= *val < 10.0 - Return the ascii code for the leading digit of *val, then - multiply "*val" by 10.0 to renormalize. - ** - Example: - input: *val = 3.14159 - output: *val = 1.4159 function return = '3' - ** - The counter *cnt is incremented each time. After counter exceeds - 16 (the number of significant digits in a 64-bit float) '0' is - always returned. -*/ -static int et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ - int digit; - LONGDOUBLE_TYPE d; - if( (*cnt)++ >= 16 ) return '0'; - digit = (int)*val; - d = digit; - digit += '0'; - *val = (*val - d)*10.0; - return digit; -} -#endif /* !FSLPRINTF_OMIT_FLOATING_POINT */ - -/* - On machines with a small(?) stack size, you can redefine the - FSLPRINTF_BUF_SIZE to be less than 350. But beware - for smaller - values some %f conversions may go into an infinite loop. -*/ -#ifndef FSLPRINTF_BUF_SIZE -# define FSLPRINTF_BUF_SIZE 350 /* Size of the output buffer for numeric conversions */ -#endif - -#if ! defined(__STDC__) && !defined(__TINYC__) -#ifdef FSLPRINTF_INT64_TYPE -typedef FSLPRINTF_INT64_TYPE int64_t; -typedef unsigned FSLPRINTF_INT64_TYPE uint64_t; -#elif defined(_MSC_VER) || defined(__BORLANDC__) -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -#else -typedef long long int int64_t; -typedef unsigned long long int uint64_t; -#endif -#endif - -#if 0 -/ Not yet used. */ -enum PrintfArgTypes { -TypeInt = 0, -TypeIntP = 1, -TypeFloat = 2, -TypeFloatP = 3, -TypeCString = 4 -}; -#endif - - -#if 0 -/ Not yet used. */ -typedef struct fsl_appendf_spec_handler_def -{ - char letter; / e.g. %s */ - int xtype; /* reference to the etXXXX values, or fmtinfo[*].type. */ - int ntype; /* reference to PrintfArgTypes enum. */ -} spec_handler; -#endif - -/** - fsl_appendf_spec_handler is an almost-generic interface for farming - work out of fsl_appendfv()'s code into external functions. It doesn't - actually save much (if any) overall code, but it makes the fsl_appendfv() - code more manageable. - - - REQUIREMENTS of implementations: - - - Expects an implementation-specific vargp pointer. - fsl_appendfv() passes a pointer to the converted value of - an entry from the format va_list. If it passes a type - other than the expected one, undefined results. - - - If it calls pf then it must return the return value - from that function. - - - If it calls pf it must do: pf( pfArg, D, N ), where D is - the data to export and N is the number of bytes to export. - It may call pf() an arbitrary number of times - - - If pf() successfully is called, the return value must be the - accumulated totals of its return value(s), plus (possibly, but - unlikely) an imnplementation-specific amount. - - - If it does not call pf() then it must return 0 (success) - or a negative number (an error) or do all of the export - processing itself and return the number of bytes exported. - - - SIGNIFICANT LIMITATIONS: - - - Has no way of iterating over the format string, so handling - precisions and such here can't work too well. (Nevermind: - precision/justification is handled in fsl_appendfv().) -*/ -typedef long (*fsl_appendf_spec_handler)( fsl_appendf_f pf, - void * pfArg, - unsigned int pfLen, - void * vargp ); - - -/** - fsl_appendf_spec_handler for etSTRING types. It assumes that varg - is a NUL-terminated (char [const] *) -*/ -static long spech_string( fsl_appendf_f pf, - void * pfArg, - unsigned int pfLen, - void * varg ) -{ - char const * ch = (char const *) varg; - return ch ? pf( pfArg, ch, pfLen ) : 0; -} - -/** - fsl_appendf_spec_handler for etDYNSTRING types. It assumes that - varg is a non-const (char *). It behaves identically to - spec_string() and then calls free() on that (char *). -*/ -static long spech_dynstring( fsl_appendf_f pf, - void * pfArg, - unsigned int pfLen, - void * varg ) -{ - long ret = spech_string( pf, pfArg, pfLen, varg ); - fsl_free( varg ); - return ret; -} - -#if !FSLPRINTF_OMIT_HTML -static long spech_string_to_html( fsl_appendf_f pf, - void * pfArg, - unsigned int pfLen, - void * varg ) -{ - char const * ch = (char const *) varg; - unsigned int i; - long ret = 0; - if( ! ch ) return 0; - ret = 0; - for( i = 0; (i= 32 && c <=47) - || ( c>=58 && c<=64) - || ( c>=91 && c<=96) - || ( c>=123 && c<=126) - || ( c<32 || c>=127) - ); -} - -/** - The handler for the etURLENCODE specifier. - - It expects varg to be a string value, which it will preceed to - encode using an URL encoding algothrim (certain characters are - converted to %XX, where XX is their hex value) and passes the - encoded string to pf(). It returns the total length of the output - string. -*/ -static long spech_urlencode( fsl_appendf_f pf, - void * pfArg, - unsigned int pfLen, - void * varg ) -{ - char const * str = (char const *) varg; - long ret = 0; - char ch = 0; - char const * hex = "0123456789ABCDEF"; -#define xbufsz 10 - char xbuf[xbufsz]; - int slen = 0; - if( ! str ) return 0; - memset( xbuf, 0, xbufsz ); - ch = *str; -#define xbufsz 10 - slen = 0; - for( ; ch; ch = *(++str) ) - { - if( ! httpurl_needs_escape( ch ) ) - { - ret += pf( pfArg, str, 1 ); - continue; - } - else { - xbuf[0] = '%'; - xbuf[1] = hex[((ch>>4)&0xf)]; - xbuf[2] = hex[(ch&0xf)]; - xbuf[3] = 0; - slen = 3; - ret += pf( pfArg, xbuf, slen ); - } - } -#undef xbufsz - return ret; -} - -/* - hexchar_to_int(): - - For 'a'-'f', 'A'-'F' and '0'-'9', returns the appropriate decimal - number. For any other character it returns -1. -*/ -static int hexchar_to_int( int ch ) -{ - if( (ch>='a' && ch<='f') ) return ch-'a'+10; - else if( (ch>='A' && ch<='F') ) return ch-'A'+10; - else if( (ch>='0' && ch<='9') ) return ch-'0'; - return -1; -} - -/** - The handler for the etURLDECODE specifier. - - It expects varg to be a ([const] char *), possibly encoded - with URL encoding. It decodes the string using a URL decode - algorithm and passes the decoded string to - pf(). It returns the total length of the output string. - If the input string contains malformed %XX codes then this - function will return prematurely. -*/ -static long spech_urldecode( fsl_appendf_f pf, - void * pfArg, - unsigned int pfLen, - void * varg ) -{ - char const * str = (char const *) varg; - long ret = 0; - char ch = 0; - char ch2 = 0; - char xbuf[4]; - int decoded; - if( ! str ) return 0; - ch = *str; - while( ch ) - { - if( ch == '%' ) - { - ch = *(++str); - ch2 = *(++str); - if( isxdigit(ch) && - isxdigit(ch2) ) - { - decoded = (hexchar_to_int( ch ) * 16) - + hexchar_to_int( ch2 ); - xbuf[0] = (char)decoded; - xbuf[1] = 0; - ret += pf( pfArg, xbuf, 1 ); - ch = *(++str); - continue; - } - else - { - xbuf[0] = '%'; - xbuf[1] = ch; - xbuf[2] = ch2; - xbuf[3] = 0; - ret += pf( pfArg, xbuf, 3 ); - ch = *(++str); - continue; - } - } - else if( ch == '+' ) - { - xbuf[0] = ' '; - xbuf[1] = 0; - ret += pf( pfArg, xbuf, 1 ); - ch = *(++str); - continue; - } - xbuf[0] = ch; - xbuf[1] = 0; - ret += pf( pfArg, xbuf, 1 ); - ch = *(++str); - } - return ret; -} - -#endif /* !FSLPRINTF_OMIT_HTML */ - - -#if !FSLPRINTF_OMIT_SQL -/** - Quotes the (char *) varg as an SQL string 'should' - be quoted. The exact type of the conversion - is specified by xtype, which must be one of - etSQLESCAPE, etSQLESCAPE2, or etSQLESCAPE3. - - Search this file for those constants to find - the associated documentation. -*/ -static long spech_sqlstring_main( int xtype, - fsl_appendf_f pf, - void * pfArg, - unsigned int pfLen, - void * varg ) -{ - unsigned int i, n; - int j, ch, isnull; - int needQuote; - char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */ - char const * escarg = (char const *) varg; - char * bufpt = NULL; - long ret; - isnull = escarg==0; - if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)"); - for(i=n=0; (i='0' && c<='9' ){ - width = width*10 + c - '0'; - c = *++fmt; - } - } - if( width > FSLPRINTF_BUF_SIZE-10 ){ - width = FSLPRINTF_BUF_SIZE-10; - } - /* Get the precision */ - if( c=='.' ){ - precision = 0; - c = *++fmt; - if( c=='*' ){ - precision = va_arg(ap,int); - if( precision<0 ) precision = -precision; - c = *++fmt; - }else{ - while( c>='0' && c<='9' ){ - precision = precision*10 + c - '0'; - c = *++fmt; - } - } - }else{ - precision = -1; - } - /* Get the conversion type modifier */ - if( c=='l' ){ - flag_long = 1; - c = *++fmt; - if( c=='l' ){ - flag_longlong = 1; - c = *++fmt; - }else{ - flag_longlong = 0; - } - }else{ - flag_long = flag_longlong = 0; - } - /* Fetch the info entry for the field */ - infop = 0; -#if FSLPRINTF_FMTINFO_FIXED - for(idx=0; idxflags & FLAG_EXTENDED)==0 ){ - xtype = infop->type; - }else{ - FSLPRINTF_RETURN; - } - break; - } - } -#else -#define FMTNDX(N) (N - fmtinfo[0].fmttype) -#define FMTINFO(N) (fmtinfo[ FMTNDX(N) ]) - infop = ((c>=(fmtinfo[0].fmttype)) && (cfmttype,infop->type);*/ - if( infop ) xtype = infop->type; -#undef FMTINFO -#undef FMTNDX -#endif /* FSLPRINTF_FMTINFO_FIXED */ - zExtra = 0; - if( (!infop) || (!infop->type) ){ - FSLPRINTF_RETURN; - } - - - /* Limit the precision to prevent overflowing buf[] during conversion */ - if( precision>FSLPRINTF_BUF_SIZE-40 && (infop->flags & FLAG_STRING)==0 ){ - precision = FSLPRINTF_BUF_SIZE-40; - } - - /* - At this point, variables are initialized as follows: - ** - flag_alternateform TRUE if a '#' is present. - flag_altform2 TRUE if a '!' is present. - flag_plussign TRUE if a '+' is present. - flag_leftjustify TRUE if a '-' is present or if the - field width was negative. - flag_zeropad TRUE if the width began with 0. - flag_long TRUE if the letter 'l' (ell) prefixed - the conversion character. - flag_longlong TRUE if the letter 'll' (ell ell) prefixed - the conversion character. - flag_blanksign TRUE if a ' ' is present. - width The specified field width. This is - always non-negative. Zero is the default. - precision The specified precision. The default - is -1. - xtype The class of the conversion. - infop Pointer to the appropriate info struct. - */ - switch( xtype ){ - case etPOINTER: - flag_longlong = sizeof(char*)==sizeof(int64_t); - flag_long = sizeof(char*)==sizeof(long int); - /* Fall through into the next case */ - case etORDINAL: - case etRADIX: - if( infop->flags & FLAG_SIGNED ){ - int64_t v; - if( flag_longlong ) v = va_arg(ap,int64_t); - else if( flag_long ) v = va_arg(ap,long int); - else v = va_arg(ap,int); - if( v<0 ){ - longvalue = -v; - prefix = '-'; - }else{ - longvalue = v; - if( flag_plussign ) prefix = '+'; - else if( flag_blanksign ) prefix = ' '; - else prefix = 0; - } - }else{ - if( flag_longlong ) longvalue = va_arg(ap,uint64_t); - else if( flag_long ) longvalue = va_arg(ap,unsigned long int); - else longvalue = va_arg(ap,unsigned int); - prefix = 0; - } - if( longvalue==0 ) flag_alternateform = 0; - if( flag_zeropad && precision=4 || (longvalue/10)%10==1 ){ - x = 0; - } - buf[FSLPRINTF_BUF_SIZE-3] = zOrd[x*2]; - buf[FSLPRINTF_BUF_SIZE-2] = zOrd[x*2+1]; - bufpt -= 2; - } - { - const char *cset; - int base; - cset = &aDigits[infop->charset]; - base = infop->base; - do{ /* Convert to ascii */ - *(--bufpt) = cset[longvalue%base]; - longvalue = longvalue/base; - }while( longvalue>0 ); - } - length = &buf[FSLPRINTF_BUF_SIZE-1]-bufpt; - for(idx=precision-length; idx>0; idx--){ - *(--bufpt) = '0'; /* Zero pad */ - } - if( prefix ) *(--bufpt) = prefix; /* Add sign */ - if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */ - const char *pre; - char x; - pre = &aPrefix[infop->prefix]; - if( *bufpt!=pre[0] ){ - for(; (x=(*pre))!=0; pre++) *(--bufpt) = x; - } - } - length = &buf[FSLPRINTF_BUF_SIZE-1]-bufpt; - break; - case etFLOAT: - case etEXP: - case etGENERIC: - realvalue = va_arg(ap,double); -#if ! FSLPRINTF_OMIT_FLOATING_POINT - if( precision<0 ) precision = 6; /* Set default precision */ - if( precision>FSLPRINTF_BUF_SIZE/2-10 ) precision = FSLPRINTF_BUF_SIZE/2-10; - if( realvalue<0.0 ){ - realvalue = -realvalue; - prefix = '-'; - }else{ - if( flag_plussign ) prefix = '+'; - else if( flag_blanksign ) prefix = ' '; - else prefix = 0; - } - if( xtype==etGENERIC && precision>0 ) precision--; -#if 0 - /* Rounding works like BSD when the constant 0.4999 is used. Wierd! */ - for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1); -#else - /* It makes more sense to use 0.5 */ - for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1){} -#endif - if( xtype==etFLOAT ) realvalue += rounder; - /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ - exp = 0; -#if 1 - if( (realvalue)!=(realvalue) ){ - /* from sqlite3: #define sqlite3_isnan(X) ((X)!=(X)) */ - /* This weird array thing is to avoid constness violations - when assinging, e.g. "NaN" to bufpt. - */ - static char NaN[4] = {'N','a','N','\0'}; - bufpt = NaN; - length = 3; - break; - } -#endif - if( realvalue>0.0 ){ - while( realvalue>=1e32 && exp<=350 ){ realvalue *= 1e-32; exp+=32; } - while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; } - while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; } - while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; } - while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; } - if( exp>350 || exp<-350 ){ - if( prefix=='-' ){ - static char Inf[5] = {'-','I','n','f','\0'}; - bufpt = Inf; - }else if( prefix=='+' ){ - static char Inf[5] = {'+','I','n','f','\0'}; - bufpt = Inf; - }else{ - static char Inf[4] = {'I','n','f','\0'}; - bufpt = Inf; - } - length = strlen(bufpt); - break; - } - } - bufpt = buf; - /* - If the field type is etGENERIC, then convert to either etEXP - or etFLOAT, as appropriate. - */ - flag_exp = xtype==etEXP; - if( xtype!=etFLOAT ){ - realvalue += rounder; - if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } - } - if( xtype==etGENERIC ){ - flag_rtz = !flag_alternateform; - if( exp<-4 || exp>precision ){ - xtype = etEXP; - }else{ - precision = precision - exp; - xtype = etFLOAT; - } - }else{ - flag_rtz = 0; - } - if( xtype==etEXP ){ - e2 = 0; - }else{ - e2 = exp; - } - nsd = 0; - flag_dp = (precision>0) | flag_alternateform | flag_altform2; - /* The sign in front of the number */ - if( prefix ){ - *(bufpt++) = prefix; - } - /* Digits prior to the decimal point */ - if( e2<0 ){ - *(bufpt++) = '0'; - }else{ - for(; e2>=0; e2--){ - *(bufpt++) = et_getdigit(&realvalue,&nsd); - } - } - /* The decimal point */ - if( flag_dp ){ - *(bufpt++) = '.'; - } - /* "0" digits after the decimal point but before the first - significant digit of the number */ - for(e2++; e2<0 && precision>0; precision--, e2++){ - *(bufpt++) = '0'; - } - /* Significant digits after the decimal point */ - while( (precision--)>0 ){ - *(bufpt++) = et_getdigit(&realvalue,&nsd); - } - /* Remove trailing zeros and the "." if no digits follow the "." */ - if( flag_rtz && flag_dp ){ - while( bufpt[-1]=='0' ) *(--bufpt) = 0; - /* assert( bufpt>buf ); */ - if( bufpt[-1]=='.' ){ - if( flag_altform2 ){ - *(bufpt++) = '0'; - }else{ - *(--bufpt) = 0; - } - } - } - /* Add the "eNNN" suffix */ - if( flag_exp || (xtype==etEXP && exp) ){ - *(bufpt++) = aDigits[infop->charset]; - if( exp<0 ){ - *(bufpt++) = '-'; exp = -exp; - }else{ - *(bufpt++) = '+'; - } - if( exp>=100 ){ - *(bufpt++) = (exp/100)+'0'; /* 100's digit */ - exp %= 100; - } - *(bufpt++) = exp/10+'0'; /* 10's digit */ - *(bufpt++) = exp%10+'0'; /* 1's digit */ - } - *bufpt = 0; - - /* The converted number is in buf[] and zero terminated. Output it. - Note that the number is in the usual order, not reversed as with - integer conversions. */ - length = bufpt-buf; - bufpt = buf; - - /* Special case: Add leading zeros if the flag_zeropad flag is - set and we are not left justified */ - if( flag_zeropad && !flag_leftjustify && length < width){ - int i; - int nPad = width - length; - for(i=width; i>=nPad; i--){ - bufpt[i] = bufpt[i-nPad]; - } - i = prefix!=0; - while( nPad-- ) bufpt[i++] = '0'; - length = width; - } -#endif /* !FSLPRINTF_OMIT_FLOATING_POINT */ - break; -#if !FSLPRINTF_OMIT_SIZE - case etSIZE: - *(va_arg(ap,int*)) = outCount; - length = width = 0; - break; -#endif - case etPERCENT: - buf[0] = '%'; - bufpt = buf; - length = 1; - break; - case etCHARLIT: - case etCHARX: - c = buf[0] = (xtype==etCHARX ? va_arg(ap,int) : *++fmt); - if( precision>=0 ){ - for(idx=1; idx=0 && precision0 ){ - FSLPRINTF_SPACES(nspace); - } - } - if( length>0 ){ - pfrc = pfAppend( pfAppendArg, bufpt, length); - FSLPRINTF_CHECKERR(0); - } - if( flag_leftjustify ){ - int nspace; - nspace = width-length; - if( nspace>0 ){ - FSLPRINTF_SPACES(nspace); - } - } - if( zExtra ){ - free(zExtra); - zExtra = 0; - } - }/* End for loop over the format string */ - FSLPRINTF_RETURN; -} /* End of function */ - - -#undef FSLPRINTF_SPACES -#undef FSLPRINTF_CHECKERR -#undef FSLPRINTF_RETURN -#undef FSLPRINTF_OMIT_FLOATING_POINT -#undef FSLPRINTF_OMIT_SIZE -#undef FSLPRINTF_OMIT_SQL -#undef FSLPRINTF_BUF_SIZE -#undef FSLPRINTF_OMIT_HTML - -long fsl_appendf(fsl_appendf_f pfAppend, /* Accumulate results here */ - void * pfAppendArg, /* Passed as first arg to pfAppend. */ - const char *fmt, /* Format string */ - ... ){ - long ret; - va_list vargs; - va_start( vargs, fmt ); - ret = fsl_appendfv( pfAppend, pfAppendArg, fmt, vargs ); - va_end(vargs); - return ret; -} - - -/* -** fsl_appendf_f() impl which requires that state be-a writable -** (FILE*). -*/ -static long fsl_appendf_f_FILE( void * state, - char const * s, long n ){ - if( !state ) return -1; - else return (1==fwrite( s, (size_t)n, 1, (FILE *)state )) - ? n : -2; -} - - -long fsl_appendf_FILE( FILE * fp, char const * fmt, ... ){ - if(!fp || !fmt) return -1; - else { - long ret; - va_list vargs; - va_start( vargs, fmt ); - ret = fsl_appendfv( fsl_appendf_f_FILE, fp, fmt, vargs ); - va_end(vargs); - return ret; - } -} - -char * fsl_mprintfv( char const * fmt, va_list vargs ){ - if( !fmt ) return 0; - else{ - fsl_buffer buf = fsl_buffer_empty; - int const rc = fsl_buffer_appendfv( &buf, fmt, vargs ); - if(rc){ - fsl_buffer_reserve(&buf, 0); - assert(0==buf.mem); - } - return (char*)buf.mem /*transfer ownership*/; - } -} - -char * fsl_mprintf( char const * fmt, ... ){ - char * ret; - va_list vargs; - va_start( vargs, fmt ); - ret = fsl_mprintfv( fmt, vargs ); - va_end( vargs ); - return ret; -} DELETED fsl_buffer.c Index: fsl_buffer.c ================================================================== --- fsl_buffer.c +++ /dev/null @@ -1,416 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* -** 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 "fossil/fossil2.h" -#include -#include -#include /* strlen() */ -#include /* NULL on linux */ - -#include - -int fsl_buffer_reset( fsl_buffer * b ){ - if(!b) return FSL_RC_MISUSE; - else{ - if(b->capacity){ - assert(b->mem); - b->mem[0] = 0; - } - b->used = 0; - return 0; - } -} - - -int fsl_buffer_reserve( fsl_buffer * buf, fsl_size_t n ){ - if( ! buf ) return FSL_RC_MISUSE; - else if( 0 == n ){ - fsl_free(buf->mem); - *buf = fsl_buffer_empty; - return 0; - } - else if( buf->capacity >= n ){ - return 0; - } - else{ - unsigned char * x = (unsigned char *)fsl_realloc( buf->mem, n ); - if( ! x ) return FSL_RC_OOM; - memset( x + buf->used, 0, n - buf->used ); - buf->mem = x; - buf->capacity = n; - return 0; - } -} - -int fsl_buffer_resize( fsl_buffer * buf, fsl_size_t n ){ - if( !buf ) return FSL_RC_MISUSE; - else if(n && (buf->capacity == n+1)){ - buf->used = n; - buf->mem[n] = 0; - return 0; - } - else { - unsigned char * x = (unsigned char *)fsl_realloc( buf->mem, - n+1/*NUL*/ ); - if( ! x ) return FSL_RC_OOM; - if(n > buf->capacity){ - /* zero-fill new parts */ - memset( x + buf->capacity, 0, n - buf->capacity +1/*NUL*/ ); - } - buf->capacity = n + 1 /*NUL*/; - buf->used = n; - buf->mem = x; - buf->mem[buf->used] = 0; - return 0; - } -} - -int fsl_buffer_compare(fsl_buffer const * lhs, fsl_buffer const * rhs){ - fsl_size_t const szL = lhs->used; - fsl_size_t const szR = rhs->used; - fsl_size_t const sz = (szLmem, rhs->mem, sz); - if(0 == rc){ - rc = (szL==szR) - ? 0 - : ((szLused; - fsl_size_t const szR = rhs->used; - fsl_size_t i; - unsigned char const *buf1; - unsigned char const *buf2; - unsigned char rc = 0; - if( szL!=szR || szL==0 ) return 1; - buf1 = lhs->mem; - buf2 = rhs->mem; - for( i=0; iused + len + 1/*NUL*/; - rc = fsl_buffer_reserve( b, sz ); - if(rc) return rc; - else{ - assert(b->capacity >= sz); - memcpy(b->mem + b->used, data, (size_t)len); - b->used += len; - b->mem[b->used] = 0; - return 0; - } - } -} - -/* -** Internal helper for implementing fsl_buffer_appendf() -*/ -typedef struct BufferAppender { - fsl_buffer * b; - /* - ** Result code of the appending process. - */ - int rc; -} BufferAppender; - -/* -** fsl_appendf_f() impl which requires arg to be a (fsl_buffer*). -** It appends the data to arg. -*/ -static long fsl_appendf_f_buffer( void * arg, - char const * data, long n ){ - BufferAppender * ba = (BufferAppender*)arg; - fsl_buffer * sb = ba->b; - if( !sb || (n<0) ) return -1; - else if( ! n ) return 0; - else{ - long rc; - size_t npos = sb->used + n; - if( npos >= sb->capacity ){ - const size_t asz = npos ? ((4 * npos / 3) + 1) : 16; - if( asz < npos ) { - ba->rc = FSL_RC_RANGE; - return -1; /* overflow */ - } - else{ - rc = fsl_buffer_reserve( sb, asz ); - if(rc) { - ba->rc = FSL_RC_OOM; - return -1; - } - } - } - rc = 0; - for( ; rc < n; ++rc, ++sb->used ){ - sb->mem[sb->used] = data[rc]; - } - sb->mem[sb->used] = 0; - return rc; - } -} - -int fsl_buffer_appendfv( fsl_buffer * b, - char const * fmt, va_list args){ - if(!b || !fmt) return FSL_RC_MISUSE; - else{ - BufferAppender ba; - ba.b = b; - ba.rc = 0; - fsl_appendfv( fsl_appendf_f_buffer, &ba, fmt, args ); - return ba.rc; - } -} - -char const * fsl_buffer_cstr(fsl_buffer const *b){ - return b ? (char const *)b->mem : NULL; -} - -char * fsl_buffer_str(fsl_buffer *b){ - return b ? (char *)b->mem : NULL; -} - -int fsl_buffer_appendf( fsl_buffer * b, - char const * fmt, ... ){ - if(!b || !fmt) return FSL_RC_MISUSE; - else{ - int rc; - va_list args; - va_start(args,fmt); - rc = fsl_buffer_appendfv( b, fmt, args ); - va_end(args); - return rc; - } -} - -int fsl_buffer_compress(fsl_buffer const *pIn, fsl_buffer *pOut){ - unsigned int nIn = pIn->used; - unsigned int nOut = 13 + nIn + (nIn+999)/1000; - fsl_buffer temp = fsl_buffer_empty; - int rc = fsl_buffer_resize(&temp, nOut+4); - if(rc) return rc; - else{ - unsigned long int nOut2; - unsigned char *outBuf; - outBuf = temp.mem; - outBuf[0] = nIn>>24 & 0xff; - outBuf[1] = nIn>>16 & 0xff; - outBuf[2] = nIn>>8 & 0xff; - outBuf[3] = nIn & 0xff; - nOut2 = (long int)nOut; - rc = compress(&outBuf[4], &nOut2, - pIn->mem, pIn->used); - if(rc){ - fsl_buffer_reserve(&temp, 0); - return FSL_RC_ERROR; - } - fsl_buffer_reserve(pOut, 0); - *pOut = temp; - if(!rc){ - rc = fsl_buffer_resize(pOut, nOut2+4); - if(!rc){ - pOut->used = nOut2+4; - } - } - return rc; - } -} - -int fsl_buffer_compress2(fsl_buffer *pIn1, - fsl_buffer *pIn2, fsl_buffer *pOut){ - unsigned int nIn = pIn1->used + pIn2->used; - unsigned int nOut = 13 + nIn + (nIn+999)/1000; - fsl_buffer temp = fsl_buffer_empty; - int rc; - rc = fsl_buffer_resize(&temp, nOut+4); - if(rc) return rc; - else{ - unsigned char *outBuf; - z_stream stream; - outBuf = temp.mem; - outBuf[0] = nIn>>24 & 0xff; - outBuf[1] = nIn>>16 & 0xff; - outBuf[2] = nIn>>8 & 0xff; - outBuf[3] = nIn & 0xff; - stream.zalloc = (alloc_func)0; - stream.zfree = (free_func)0; - stream.opaque = 0; - stream.avail_out = nOut; - stream.next_out = &outBuf[4]; - deflateInit(&stream, 9); - stream.avail_in = pIn1->used; - stream.next_in = pIn1->mem; - deflate(&stream, 0); - stream.avail_in = pIn2->used; - stream.next_in = pIn2->mem; - deflate(&stream, 0); - deflate(&stream, Z_FINISH); - rc = fsl_buffer_resize(&temp, stream.total_out + 4); - deflateEnd(&stream); - if(!rc){ - temp.used = stream.total_out + 4; - if( pOut==pIn1 ) fsl_buffer_reserve(pOut, 0); - else if( pOut==pIn2 ) fsl_buffer_reserve(pOut, 0); - assert(!pOut->mem); - *pOut = temp; - }else{ - fsl_buffer_reserve(&temp, 0); - } - return rc; - } -} - -int fsl_buffer_uncompress(fsl_buffer const *pIn, fsl_buffer *pOut){ - unsigned int nOut; - unsigned char *inBuf; - unsigned int nIn = pIn->used; - fsl_buffer temp = fsl_buffer_empty; - int rc; - unsigned long int nOut2; - if( nIn<=4 ){ - return FSL_RC_RANGE; - } - inBuf = pIn->mem; - nOut = (inBuf[0]<<24) + (inBuf[1]<<16) + (inBuf[2]<<8) + inBuf[3]; - rc = fsl_buffer_reserve(&temp, nOut+1); - if(rc) return rc; - nOut2 = (long int)nOut; - rc = uncompress(temp.mem, - &nOut2, - &inBuf[4], - nIn - 4); - if( rc!=Z_OK ){ - fsl_buffer_reserve(&temp, 0); - return FSL_RC_ERROR; - } - rc = fsl_buffer_resize(&temp, nOut2); - if(!rc){ - temp.used = (fsl_size_t)nOut2; - if( pOut==pIn ){ - fsl_buffer_reserve(pOut, 0); - } - assert(!pOut->mem); - *pOut = temp; - }else{ - fsl_buffer_reserve(&temp, 0); - } - return rc; -} - - -int fsl_buffer_fill_from( fsl_buffer * dest, fsl_input_f src, void * state ) -{ - int rc; - enum { BufSize = 512 * 4 }; - char rbuf[BufSize]; - fsl_size_t total = 0; - fsl_size_t rlen = 0; - if( !dest || ! src ) return FSL_RC_MISUSE; - dest->used = 0; - while(1){ - rlen = BufSize; - rc = src( state, rbuf, &rlen ); - if( rc ) break; - total += rlen; - if(totalcapacity < (total+1) ){ - rc = fsl_buffer_reserve( dest, - total + ((rlenmem + dest->used, rbuf, rlen ); - dest->used += rlen; - if( rlen < BufSize ) break; - } - if( !rc && dest->used ){ - assert( dest->used < dest->capacity ); - dest->mem[dest->used] = 0; - } - return rc; -} - -int fsl_input_FILE( void * state, void * dest, fsl_size_t * n ){ - FILE * f = (FILE*) state; - if( ! state || ! n || !dest ) return FSL_RC_MISUSE; - else if( !*n ) return FSL_RC_RANGE; - *n = (fsl_size_t)fread( dest, 1, *n, f ); - return !*n - ? (feof(f) ? 0 : FSL_RC_IO) - : 0; -} - -int fsl_buffer_fill_from_FILE( fsl_buffer * dest, FILE * src ){ - return (!dest || !src) - ? FSL_RC_MISUSE - : fsl_buffer_fill_from( dest, fsl_input_FILE, src ); -} - - -int fsl_buffer_fill_from_filename( fsl_buffer * dest, char const * filename ){ - if(!dest || !filename || !*filename) return FSL_RC_MISUSE; - else{ - int rc; - FILE * src = (('-'==*filename)&&!*(filename+1)) ? stdin : fopen(filename,"rb"); - if(!src) return FSL_RC_IO; - rc = fsl_buffer_fill_from( dest, fsl_input_FILE, src ); - if(stdin!=src) fclose(src); - return rc; - } -} - -fsl_size_t fsl_strlen( char const * src ){ - fsl_size_t i = 0; - for( i = 0; src && *src; ++i ){} - return i; -} - -char * fsl_strdup( char const * src ){ - if(!src) return NULL; - else{ - fsl_buffer b = fsl_buffer_empty; - fsl_buffer_append( &b, src, fsl_strlen(src) ); - return (char*)b.mem; - } -} DELETED fsl_db.c Index: fsl_db.c ================================================================== --- fsl_db.c +++ /dev/null @@ -1,337 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* -** 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 "fossil/fossil2.h" -#include -#include -#include /* NULL on linux */ - -/* -** -** Helper which sets fsl_err_set() to some string -** pulled from the db connection. dbCode should be -** the result code which came from an error'd -** sqlite API call. -*/ -static int fsl_err_repo_db( fsl_ctx * f, int dbCode ){ - assert(f && f->dbRepo.dbh); - return fsl_err_set(f, FSL_RC_DB, - "Db error #%d: %s", - dbCode, sqlite3_errmsg(f->dbRepo.dbh)); -} - -int fsl_db_close( fsl_ctx * f, fsl_db * db ){ - if(!f || !db) return FSL_RC_MISUSE; - else{ - if(db->dbh){ - sqlite3_close(db->dbh) - /* ignoring result in the style of - "destructors may not throw". - */; - } - assert(0==db->openStatementCount); - fsl_buffer_reserve(&db->filename, 0); - *db = fsl_db_empty; - return 0; - } -} - -int fsl_repo_close( fsl_ctx * f ){ - if(!f) return FSL_RC_MISUSE; - else if(!f->dbRepo.dbh) return FSL_RC_NOT_A_REPO; - else return fsl_db_close( f, &f->dbRepo ); -} - -/* -** This file contains the fsl_db_xxx() and fsl_stmt_xxx() -** parts of the API. -*/ -int fsl_stmt_preparev( fsl_db *db, fsl_stmt * tgt, char const * sql, va_list args ){ - if(!db || !db->dbh || !tgt || !sql) return FSL_RC_MISUSE; - else if(!*sql) return FSL_RC_RANGE; - else if(!tgt->stmt) return FSL_RC_ALREADY_EXISTS; - else{ - int rc; - fsl_buffer buf = fsl_buffer_empty; - fsl_stmt_t * liteStmt = NULL; - fsl_ctx * f = db->f; - rc = fsl_buffer_appendfv( &buf, sql, args ); - if(!rc){ - sql = fsl_buffer_cstr(&buf); - rc = sqlite3_prepare_v2(db->dbh, sql, (int)buf.used, - &liteStmt, 0); - if(rc){ - rc = fsl_err_set(f, rc, "Db statement preparation failed. " - "SQL: [%.*s]. Error #%d: %s\n", - (int)buf.used, sql, rc, - sqlite3_errmsg(db->dbh)); - } - } - if(!rc){ - ++db->openStatementCount; - tgt->stmt = liteStmt; - tgt->db = db; - tgt->sql = buf /*transfer ownership*/; - tgt->colCount = sqlite3_column_count(tgt->stmt); - tgt->paramCount = sqlite3_bind_parameter_count(tgt->stmt); - }else{ - assert(!liteStmt); - fsl_buffer_reserve(&buf, 0); - } - return rc; - } -} - -int fsl_stmt_prepare( fsl_db *db, fsl_stmt * tgt, char const * sql, ... ){ - if(!db || !db->dbh || !tgt || !sql) return FSL_RC_MISUSE; - else if(!*sql) return FSL_RC_RANGE; - else if(!tgt->stmt) return FSL_RC_ALREADY_EXISTS; - else{ - int rc; - va_list args; - va_start(args,sql); - rc = fsl_stmt_preparev( db, tgt, sql, args ); - va_end(args); - return rc; - } -} - -int fsl_repo_prepare( fsl_ctx *f, fsl_stmt * tgt, char const * sql, - ... ){ - if(!f) return FSL_RC_MISUSE; - else if(!f->dbRepo.dbh) return FSL_RC_NOT_A_REPO; - else{ - int rc; - va_list args; - va_start(args,sql); - rc = fsl_stmt_preparev( &f->dbRepo, tgt, sql, args ); - va_end(args); - return rc; - } -} - -int fsl_repo_preparev( fsl_ctx *f, fsl_stmt * tgt, char const * sql, - va_list args ){ - if(!f) return FSL_RC_MISUSE; - else if(!f->dbRepo.dbh) return FSL_RC_NOT_A_REPO; - else return fsl_stmt_preparev(&f->dbRepo, tgt, sql, args); -} - - -int fsl_stmt_finalize( fsl_stmt * stmt ){ - if(!stmt) return FSL_RC_MISUSE; - else{ - assert(stmt->f); - assert(stmt->stmt); - assert(stmt->db); - --stmt->db->openStatementCount; - fsl_buffer_reserve(&stmt->sql, 0); - sqlite3_finalize( stmt->stmt ); - *stmt = fsl_stmt_empty; - return 0; - } -} - -fsl_step_t fsl_stmt_step( fsl_stmt * stmt ){ - if(!stmt || !stmt->stmt) return FSL_RC_MISUSE; - else{ - int const rc = sqlite3_step(stmt->stmt); - assert(stmt->f); - switch( rc ){ - case SQLITE_ROW: - return FSL_STEP_ROW; - case SQLITE_DONE: - return FSL_STEP_DONE; - default: - fsl_err_repo_db( stmt->f, rc ); - return FSL_STEP_ERROR; - } - } -} - -int fsl_stmt_each( fsl_stmt * stmt, fsl_stmt_each_f callback, - void * callbackState ){ - if(!stmt || !callback) return FSL_RC_MISUSE; - else{ - fsl_step_t strc; - int rc = 0; - char doBreak = 0; - while( !doBreak && (FSL_STEP_ROW == (strc=fsl_stmt_step(stmt)))){ - rc = callback( stmt, callbackState ); - switch(rc){ - case 0: continue; - case FSL_RC_BREAK: - rc = 0; - /* fall through */ - default: - doBreak = 1; - break; - } - } - return rc - ? rc - : ((FSL_STEP_ERROR==strc) - ? FSL_RC_DB - : 0); - } -} - -int fsl_stmt_reset( fsl_stmt * stmt ){ - if(!stmt || !stmt->stmt) return FSL_RC_MISUSE; - else{ - int rc = sqlite3_reset(stmt->stmt); - return rc - ? fsl_err_repo_db(stmt->f, rc) - : 0; - } -} - -int fsl_stmt_col_count( fsl_stmt const * stmt ){ - return (!stmt || !stmt->stmt) - ? -1 - : stmt->colCount - ; -} - -int fsl_stmt_param_count( fsl_stmt const * stmt ){ - return (!stmt || !stmt->stmt) - ? -1 - : stmt->paramCount; -} - -int fsl_stmt_bind_null( fsl_stmt * stmt, int ndx ){ - if(!stmt || !stmt->stmt) return FSL_RC_MISUSE; - else{ - int const rc = sqlite3_bind_null( stmt->stmt, ndx ); - return rc ? fsl_err_repo_db(stmt->f, rc) : 0; - } -} - -int fsl_stmt_bind_int32( fsl_stmt * stmt, int ndx, fsl_int32_t v ){ - if(!stmt || !stmt->stmt) return FSL_RC_MISUSE; - else{ - int const rc = sqlite3_bind_int( stmt->stmt, ndx, (int)v ); - return rc ? fsl_err_repo_db(stmt->f, rc) : 0; - } -} - -int fsl_stmt_bind_int64( fsl_stmt * stmt, int ndx, fsl_int64_t v ){ - if(!stmt || !stmt->stmt) return FSL_RC_MISUSE; - else{ - int const rc = sqlite3_bind_int64( stmt->stmt, ndx, (sqlite3_int64)v ); - return rc ? fsl_err_repo_db(stmt->f, rc) : 0; - } -} - -int fsl_stmt_bind_double( fsl_stmt * stmt, int ndx, fsl_double_t v ){ - if(!stmt || !stmt->stmt) return FSL_RC_MISUSE; - else{ - int const rc = sqlite3_bind_double( stmt->stmt, ndx, (double)v ); - return rc ? fsl_err_repo_db(stmt->f, rc) : 0; - } -} - -#define GET_CHECK if(!stmt || !stmt->colCount) return FSL_RC_MISUSE; \ - else if((ndx<0) || (ndx>=stmt->colCount)) return FSL_RC_RANGE -int fsl_stmt_get_int32( fsl_stmt * stmt, int ndx, fsl_int32_t * v ){ - GET_CHECK; - else { - if(v) *v = (fsl_int32_t)sqlite3_column_int(stmt->stmt, ndx); - return 0; - } -} -int fsl_stmt_get_int64( fsl_stmt * stmt, int ndx, fsl_int64_t * v ){ - GET_CHECK; - else { - if(v) *v = (fsl_int64_t)sqlite3_column_int64(stmt->stmt, ndx); - return 0; - } -} - -int fsl_stmt_get_double( fsl_stmt * stmt, int ndx, fsl_double_t * v ){ - GET_CHECK; - else { - if(v) *v = (fsl_double_t)sqlite3_column_double(stmt->stmt, ndx); - return 0; - } -} - -int fsl_stmt_get_text( fsl_stmt * stmt, int ndx, char const **out, - fsl_int_t * outLen ){ - GET_CHECK; - else { - unsigned char const * t = (out || outLen) - ? sqlite3_column_text(stmt->stmt, ndx) - : NULL; - if(out) *out = (char const *)t; - if(outLen) *outLen = t ? sqlite3_column_bytes(stmt->stmt, ndx) : 0; - return 0; - } -} - -int fsl_stmt_get_blob( fsl_stmt * stmt, int ndx, void const **out, - fsl_int_t * outLen ){ - GET_CHECK; - else { - void const * t = (out || outLen) - ? sqlite3_column_blob(stmt->stmt, ndx) - : NULL; - if(out) *out = (char const *)t; - if(outLen) *outLen = t ? sqlite3_column_bytes(stmt->stmt, ndx) : 0; - return 0; - } -} - -#undef GET_CHECK - -int fsl_repo_open_db( fsl_ctx * f, char const * repoDbFile, ...){ - if(!f || !repoDbFile || !*repoDbFile) return FSL_RC_MISUSE; - else if(f->dbRepo.dbh) return FSL_RC_MISUSE; - else { - int rc = sqlite3_open_v2( repoDbFile, - &f->dbRepo.dbh, - SQLITE_OPEN_READWRITE, - NULL ); - if(rc){ - if(f->dbRepo.dbh){ - /* By some complete coincidence, FSL_RC_DB==SQLITE_CANTOPEN. */ - rc = fsl_err_set(f, FSL_RC_DB, - "Opening db file [%s] failed with " - "sqlite code #%d: %s", - repoDbFile, rc, sqlite3_errmsg(f->dbRepo.dbh)); - sqlite3_close(f->dbRepo.dbh); - f->dbRepo.dbh = NULL; - }else{ - rc = fsl_err_set(f, FSL_RC_DB, - "Opening db file [%s] failed with " - "sqlite code #%d", - repoDbFile, rc); - - } - rc = FSL_RC_DB; - } - f->dbRepo.f = f; - if(!rc){ - rc = fsl_buffer_append(&f->dbRepo.filename, repoDbFile, -1); - } - return rc; - } -} - DELETED fsl_fs.c Index: fsl_fs.c ================================================================== --- fsl_fs.c +++ /dev/null @@ -1,50 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* -** 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 -#include -#include /* strlen() */ -#include /* NULL on linux */ -#include -#ifdef _WIN32 -# include -#else -# include /* access(2) */ -#endif - - - -#include "fossil/fossil2.h" -#include "fsl_internal.h" - -/* -** Wrapper around the access() system call. -*/ -int fsl_file_access(const char *zFilename, int flags){ -#ifdef _WIN32 - wchar_t *zMbcs = fsl_utf8_to_filename(zFilename); - int rc = _waccess(zMbcs, flags); -#else - char *zMbcs = (char*)fsl_utf8_to_filename(zFilename); - int rc = access(zMbcs, flags); -#endif - fsl_filename_free(zMbcs); - return rc; -} DELETED fsl_internal.h Index: fsl_internal.h ================================================================== --- fsl_internal.h +++ /dev/null @@ -1,133 +0,0 @@ -/* -*- 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_INTERNAL_H_INCLUDED) -#define NET_FOSSIL_SCM_FSL_INTERNAL_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 library-level internal APIs which are shared -** across the library. -*/ -#include /* FILE type */ - -#if defined(__cplusplus) && !defined(__STDC_FORMAT_MACROS) -/* inttypes.h needs this for the PRI* and SCN* macros in C++ mode. */ -# define __STDC_FORMAT_MACROS -#endif - -#include "fossil/fossil2.h" - -#if defined(__cplusplus) -extern "C" { -#endif - - /* - ** Quivalent to fopen(3) but expects name to be UTF8-encoded. - */ - FILE * fsl_fopen(char const * name, char const *mode); -#ifdef _WIN32 - /* - ** Translate MBCS to UTF-8. Return a pointer to the translated - ** text. ACHTUNG: Call fsl_mbcs_free() (not fsl_free()) to - ** deallocate any memory used to store the returned pointer when - ** done. - */ - char * fsl_mbcs_to_utf8(char const * mbcs); - - /* - ** Frees a string allocated from fsl_mbcs_to_utf8(). Results are undefined - ** if mbcs was allocated using any other mechanism. - */ - void fsl_mbcs_free(char * mbcs); -#endif - /* _WIN32 */ - - /* - ** Translate Unicode text into UTF-8. - ** Return a pointer to the translated text. - ** Call fsl_unicode_free() to deallocate any memory used to store the - ** returned pointer when done. - ** - ** This function exists only for Windows. On other platforms it - ** behaves like fsl_strdup(). - */ - char *fsl_unicode_to_utf8(const void *zUnicode); - - /* - ** Translate UTF-8 to unicode for use in system calls. Return a - ** pointer to the translated text. The returned value must - ** eventually be passed to fsl_free() to deallocate any memory used - ** to store the returned pointer when done. - ** - ** This function exists only for Windows. On other platforms - ** it behaves like fsl_strdup(). - ** - */ - void *fsl_utf8_to_unicode(const char *zUtf8); - - /* - ** Translate text from the filename character set into UTF-8. - ** Return a pointer to the translated text. Call - ** fsl_filename_free() to deallocate any memory used to store the - ** returned pointer when done. - ** - ** This function must not convert '\' to '/' on windows/cygwin, as it is - ** used in places where we are not sure it's really filenames we are handling, - ** e.g. fsl_getenv() or handling the argv arguments from main(). - ** - ** On Windows, translate some characters in the in the range - ** U+F001 - U+F07F (private use area) to ASCII. Cygwin sometimes - ** generates such filenames. See: - ** - */ - char *fsl_filename_to_utf8(const void *zFilename); - - /* - ** Translate text from UTF-8 to the filename character set. - ** Return a pointer to the translated text. - ** Call fossil_filename_free() to deallocate any memory used to store the - ** returned pointer when done. - ** - ** On Windows, characters in the range U+0001 to U+0031 and the - ** characters '"', '*', ':', '<', '>', '?' and '|' are invalid - ** to be used. Therefore, translate those to characters in the - ** in the range U+F001 - U+F07F (private use area), so those - ** characters never arrive in any Windows API. The filenames might - ** look strange in Windows explorer, but in the cygwin shell - ** everything looks as expected. - ** - ** See: - ** - */ - void *fsl_utf8_to_filename(const char *zUtf8); - - - /* - ** Deallocate pOld, which must have been allocated by - ** fsl_filename_to_utf8() or fsl_utf8_to_filename(). - */ - void fsl_filename_free(void *pOld); - - /* - ** Returns true (non-0) if ch is-a alpha character. - */ - char fsl_isalpha(int ch); - -#if defined(__cplusplus) -} /*extern "C"*/ -#endif -#endif -/* NET_FOSSIL_SCM_FSL_INTERNAL_H_INCLUDED */ DELETED fsl_utf8.c Index: fsl_utf8.c ================================================================== --- fsl_utf8.c +++ /dev/null @@ -1,217 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* -** 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 "fossil/fossil2.h" -#include "fsl_internal.h" - -#include -#include -#include /* strlen() */ -#include /* NULL on linux */ -#include - -#ifdef _WIN32 -# include -#endif -#ifdef __CYGWIN__ -# include -# define CP_UTF8 65001 - __declspec(dllimport) extern __stdcall int WideCharToMultiByte(int, int, - const char *, int, const char *, int, const char *, const char *); - __declspec(dllimport) extern __stdcall int MultiByteToWideChar(int, int, - const char *, int, wchar_t*, int); -#endif - -#ifdef _WIN32 -char *fossil_mbcs_to_utf8(const char *zMbcs){ - extern char *sqlite3_win32_mbcs_to_utf8(const char*); - return sqlite3_win32_mbcs_to_utf8(zMbcs); -} - -void fossil_mbcs_free(char *zOld){ - sqlite3_free(zOld); -} -#endif /* _WIN32 */ - -char *fsl_unicode_to_utf8(const void *zUnicode){ -#if defined(_WIN32) || defined(__CYGWIN__) - int nByte = WideCharToMultiByte(CP_UTF8, 0, zUnicode, -1, 0, 0, 0, 0); - char *zUtf = fsl_malloc( nByte ); - if( zUtf==0 ){ - return 0; - } - WideCharToMultiByte(CP_UTF8, 0, zUnicode, -1, zUtf, nByte, 0, 0); - return zUtf; -#else - return fsl_strdup(zUnicode); /* TODO: implement for unix */ -#endif -} - -void *fsl_utf8_to_unicode(const char *zUtf8){ -#if defined(_WIN32) || defined(__CYGWIN__) - int nByte = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0); - wchar_t *zUnicode = fsl_malloc( nByte * 2 ); - if( zUnicode==0 ){ - return 0; - } - MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nByte); - return zUnicode; -#else - return fsl_strdup(zUtf8); /* TODO: implement for unix */ -#endif -} - - -void fsl_filename_free(void *pOld){ -#if defined(_WIN32) - sqlite3_free(pOld); -#elif (defined(__APPLE__) && !defined(WITHOUT_ICONV)) || defined(__CYGWIN__) - fsl_free(pOld); -#else - /* No-op on all other unix */ -#endif -} - - -#if defined(__APPLE__) && !defined(WITHOUT_ICONV) -# include -#endif - -char *fsl_filename_to_utf8(const void *zFilename){ -#if defined(_WIN32) - int nByte = WideCharToMultiByte(CP_UTF8, 0, zFilename, -1, 0, 0, 0, 0); - char *zUtf = fsl_malloc( nByte ); - char *pUtf, *qUtf; - if( zUtf==0 ){ - return 0; - } - WideCharToMultiByte(CP_UTF8, 0, zFilename, -1, zUtf, nByte, 0, 0); - pUtf = qUtf = zUtf; - while( *pUtf ) { - if( *pUtf == (char)0xef ){ - wchar_t c = ((pUtf[1]&0x3f)<<6)|(pUtf[2]&0x3f); - /* Only really convert it when the resulting char is in range. */ - if ( c && ((c < ' ') || wcschr(L"\"*:<>?|", c)) ){ - *qUtf++ = c; pUtf+=3; continue; - } - } - *qUtf++ = *pUtf++; - } - *qUtf = 0; - return zUtf; -#elif defined(__CYGWIN__) - char *zOut; - zOut = fsl_strdup(zFilename); - return zOut; -#elif defined(__APPLE__) && !defined(WITHOUT_ICONV) - char *zIn = (char*)zFilename; - char *zOut; - iconv_t cd; - size_t n, x; - for(n=0; zIn[n]>0 && zIn[n]<=0x7f; n++){} - if( zIn[n]!=0 && (cd = iconv_open("UTF-8", "UTF-8-MAC"))!=(iconv_t)-1 ){ - char *zOutx; - char *zOrig = zIn; - size_t nIn, nOutx; - nIn = n = strlen(zIn); - nOutx = nIn+100; - zOutx = zOut = fsl_malloc( nOutx+1 ); - if(!zOutx) return NULL; - x = iconv(cd, &zIn, &nIn, &zOutx, &nOutx); - if( x==(size_t)-1 ){ - fsl_free(zOut); - zOut = fsl_strdup(zOrig); - }else{ - zOut[n+100-nOutx] = 0; - } - iconv_close(cd); - }else{ - zOut = fossil_strdup(zFilename); - } - return zOut; -#else - return (char *)zFilename; /* No-op on non-mac unix */ -#endif -} - -char fsl_isalpha(int ch){ - return isalpha(ch) ? 1 : 0; -} - -void *fsl_utf8_to_filename(const char *zUtf8){ -#ifdef _WIN32 - int nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0); - wchar_t *zUnicode = fsl_malloc( nChar * 2 ); - wchar_t *wUnicode = zUnicode; - if( zUnicode==0 ){ - return 0; - } - MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nChar); - /* If path starts with ":/" or ":\", don't translate the ':' */ - if( fsl_isalpha(zUtf8[0]) && zUtf8[1]==':' - && (zUtf8[2]=='\\' || zUtf8[2]=='/')) { - zUnicode[2] = '\\'; - wUnicode += 3; - } - while( *wUnicode != '\0' ){ - if ( (*wUnicode < ' ') || wcschr(L"\"*:<>?|", *wUnicode) ){ - *wUnicode |= 0xF000; - }else if( *wUnicode == '/' ){ - *wUnicode = '\\'; - } - ++wUnicode; - } - return zUnicode; -#elif defined(__CYGWIN__) - char *zPath, *p; - if( fsl_isalpha(zUtf8[0]) && (zUtf8[1]==':') - && (zUtf8[2]=='\\' || zUtf8[2]=='/')) { - /* win32 absolute path starting with drive specifier. */ - int nByte; - wchar_t zUnicode[2000]; - wchar_t *wUnicode = zUnicode; - MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, count(zUnicode)); - while( *wUnicode != '\0' ){ - if( *wUnicode == '/' ){ - *wUnicode = '\\'; - } - ++wUnicode; - } - nByte = cygwin_conv_path(CCP_WIN_W_TO_POSIX, zUnicode, NULL, 0); - zPath = fsl_malloc(nByte); - if(!zPath) return NULL; - cygwin_conv_path(CCP_WIN_W_TO_POSIX, zUnicode, zPath, nByte); - }else{ - zPath = fsl_strdup(zUtf8); - if(!zPath) return NULL; - zUtf8 = p = zPath; - while( (*p = *zUtf8++) != 0){ - if( *p++ == '\\' ) { - p[-1] = '/'; - } - } - } - return zPath; -#elif defined(__APPLE__) && !defined(WITHOUT_ICONV) - return fossil_strdup(zUtf8); -#else - return (void *)zUtf8; /* No-op on unix */ -#endif -} DELETED include/fossil/fossil2.h Index: include/fossil/fossil2.h ================================================================== --- include/fossil/fossil2.h +++ /dev/null @@ -1,1605 +0,0 @@ -/* -*- 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 */ - -/* -** This file sketches out a potential API for a library form of Fossil -** v2. 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 :). -** -** 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). When adding code snippets and whatnot to docs, please use -** doxygen conventions if it is not too much of an inconvenience. -** -** - 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!). -*/ - -#if defined(__cplusplus) && !defined(__STDC_FORMAT_MACROS) -/* inttypes.h needs this for the PRI* and SCN* macros in C++ mode. */ -# define __STDC_FORMAT_MACROS -#endif -#include /* C99! */ -#include /* C99! */ -#include /* va_list */ - -#if defined(__cplusplus) -extern "C" { -#endif - -#define FSL_LIBRARY_VERSION "0.0.1-pre-alphalpha" - - typedef uint64_t fsl_size_t; - typedef int32_t fsl_int32_t; - typedef uint32_t fsl_uint32_t; - typedef int64_t fsl_int64_t; - typedef uint64_t fsl_uint64_t; - typedef fsl_int64_t fsl_int_t; - typedef fsl_uint64_t fsl_uint_t; - typedef double fsl_double_t; - typedef fsl_int64_t fsl_id_t; -#define FSL_SIZE_T_PFMT PRIu64 -#define FSL_SIZE_T_SFMT SCNu64 -#define FSL_INT_T_PFMT PRIi64 -#define FSL_INT_T_SFMT SCNi64 -#define FSL_UINT_T_PFMT PRIu64 -#define FSL_UINT_T_SFMT SCNu64 -#define FSL_ID_T_PFMT PRIi64 -#define FSL_ID_T_SFMT SCNi64 - /* - ** The type used to represent type values. Unless noted otherwise, - ** the general convention is "Unix + milliseconds," or ((Unix Epoch - ** x 1000)+milliseconds). - */ - typedef fsl_uint64_t /*int64 instead of uint64?*/ fsl_time_t; - - typedef struct fsl_outputer fsl_outputer; - typedef struct fsl_ctx fsl_ctx; - typedef struct fsl_allocator fsl_allocator; - typedef struct fsl_db fsl_db; - typedef struct fsl_buffer fsl_buffer; - typedef struct fsl_error fsl_error; - typedef struct fsl_state fsl_state; - - /* - ** 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. - */ - enum fsl_rc_t { - /* - ** The quintessential non-error value. - */ - FSL_RC_OK = 0, - /* - ** A placeholder return value for "not yet implemented" functions. - */ - FSL_RC_NYI, - /* - ** Generic/unknown error. - */ - FSL_RC_ERROR, - /* - ** Out of memory. Indicates that a resource allocation request - ** failed. - */ - FSL_RC_OOM, - /* - ** API misuse (invalid args) - */ - FSL_RC_MISUSE, - /* - ** Some range was violated (function argument, UTF character, etc.). - */ - FSL_RC_RANGE, - /* - ** Indicates that access to or locking of a resource was denied - ** by some security mechanism or other. - */ - FSL_RC_ACCESS, - /* - ** Indicates an I/O error. Whether it was reading or writing is - ** context-dependent. - */ - FSL_RC_IO, - /* - ** requested resource not found - */ - FSL_RC_NOT_FOUND, - /* - ** Immutable resource already exists - */ - FSL_RC_ALREADY_EXISTS, - /* - ** Data consistency problem - */ - FSL_RC_CONSISTENCY, - - /* - ** Indicates that the requested repo needs to be rebuilt. - */ - FSL_RC_REPO_NEEDS_REBUILD, - - /* - ** 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, - /* - ** Tried to load a too-old or too-new repo - */ - FSL_RC_REPO_VERSION, - /* - ** db-level error (e.g. statement prep failed) - */ - FSL_RC_DB, - /* - ** Used by some iteration routines to indicate that iteration should - ** stop prematurely without an error. - */ - FSL_RC_BREAK, - /* ...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. - */ - char const * fsl_rc_cstr(int); - - /* - ** Returns the value of FSL_LIBRARY_VERSION used to compile - ** this code. - */ - char const * fsl_library_version(); - - /* - ** 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 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); - - /* - ** fsl_flush_f() impl which expects _FILE to be-a (FILE*), - ** which this function passes to fflush(). If fflush() returns 0, - ** so does this function, else it returns FSL_RC_IO. - */ - int fsl_flush_f_FILE(void * _FILE); - - /* - ** 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 ); - - /* - ** fsl_output_f() impl which requires state to be-a (FILE*), - ** which this function passes to fwrite(). Returns 0 on succes, - ** FSL_RC_IO on error. - */ - int fsl_output_f_FILE( void * state, void const * src, fsl_size_t n ); - /* - ** Performs output on behalf of a fsl_ctx instance. Why? So that - ** we can do interesting things like output to buffers, files, - ** sockets, etc.. Real-life example of this model in action. - */ - 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} \ - }\ - } - - /* - ** General-purpose buffer, analog to Fossil v1's Blob class. It is - ** not called fsl_blob to avoid confusion with DB-side Blobs. - */ - 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. - */ - 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; - - /* - ** For storing a combination of error message and code. - */ - struct fsl_error { - /* - ** Error message text. It is 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} - - /* - ** Placeholder for sqlite3/4 type. - */ - typedef struct sqlite3 sqlite3; - typedef sqlite3 fsl_dbh_t; - - /* - ** Db handle wrapper/helper. - */ - struct fsl_db { - /* - ** Fossil Context on whose behalf this instance is operating. - */ - fsl_ctx * f; - /* - ** Underlying db driver handle. - */ - fsl_dbh_t * dbh; - /* - ** Not currently used. - */ - fsl_error error; - /* - ** Holds the file name used when opening this db. - */ - fsl_buffer filename; - /* - ** Debugging/test counter. - */ - int openStatementCount; - }; - /** Empty-initialized fsl_db instance. */ -#define fsl_db_empty_m {\ - NULL/*f*/, \ - NULL/*dbh*/,\ - fsl_error_empty_m /*error*/, \ - fsl_buffer_empty_m/*filename*/,\ - 0/*openStatementCount*/ \ -} - - /** Empty-initialized fsl_db instance. */ - extern const fsl_db fsl_db_empty; - /* - ** 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; - - - /* - ** The main Fossil "context" type. This is the first argument to - ** most Fossil v2 API routines. - ** - ** 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_ctx * f = NULL; - ** rc = fsl_init( &f, NULL ); - ** assert(!rc); - ** rc = fsl_repo_open_db( f, "myrepo.fsl" ); - ** ... - ** fsl_finalize(f); - ** @endcode - */ - struct fsl_ctx { - /* - ** Current repository db. - */ - fsl_db dbRepo; - /* - ** Output channel used by fsl_output() and friends. - */ - fsl_outputer output; - /* - ** Can be used to tie client-specific data to the context. Its - ** finalizer is called when fsl_finalize() cleans up. - */ - fsl_state clientState; - /* - ** Holds error state. As a general rule, this information is - ** updated only by routines which need to return more info than a - ** simple integer error string. This is primarily db-related - ** routines, where we add the db-driver-provided error state - ** here. It is not used by "simple" routines for which an integer - ** code always suffices. APIs which set this should denote it - ** with a comment like "sets the context's error state on error." - */ - fsl_error error; - /* - ** Optimization: reusable scratchpad for creating strings. - */ - fsl_buffer scratch; - - /* - ** The directory part - */ - fsl_buffer repoDir; - - /* no state related to server/user/etc. That is higher-level stuff. */ - }; - - /** Initialized-with-defaults fsl_ctx instance. */ -#define fsl_ctx_empty_m { fsl_db_empty_m /*dbRepo*/, \ - fsl_outputer_FILE_m /*output*/, \ - fsl_state_empty_m /*clientState*/, \ - fsl_error_empty_m /*error*/, \ - fsl_buffer_empty_m /*scratch*/ \ - } - - /** Initialized-with-defaults fsl_ctx instance. */ - extern const fsl_ctx fsl_ctx_empty; - - /* - ** 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_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 context which prepared this statement. - */ - fsl_ctx *f; - /* - ** 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; - }; - typedef struct fsl_stmt fsl_stmt; - /* - ** 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_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 (which - ** will normally be stack-allocated. - */ - int fsl_stmt_finalize( fsl_stmt * stmt ); - - /* - ** Result codes for use with fsl_stmt_step(). - */ - enum fsl_step_t { - /* - ** Indicates that a row has been fetched and the cursor may be used - ** to access the current row state. - */ - FSL_STEP_ROW = 1, - /* - ** 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_STEP_DONE, - /* - ** Indicates that a db-level error occurred during iteration. - ** fsl_err_get() should contain more info about the problem. - */ - FSL_STEP_ERROR - }; - typedef enum fsl_step_t fsl_step_t; - fsl_step_t 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. - */ - typedef int (*fsl_stmt_each_f)( fsl_stmt * stmt, void * state ); - /* - ** 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(). - */ - int fsl_stmt_reset( fsl_stmt * stmt ); - - /* - ** Returns the column count for the given statement, or -1 - ** if !stmt or it has not been prepared. - */ - 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. - */ - int fsl_stmt_param_count( fsl_stmt const * stmt ); - - /* - ** Binds NULL to the given 1-based parameter index. - ** Returns 0 on succcess. Sets the Fossil context'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 Fossil context'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 Fossil context's error state on error. - */ - int fsl_stmt_bind_int64( fsl_stmt * stmt, int index, fsl_int64_t v ); - - - /* - ** TODO. - ** - ** Binds the first n bytes of v as text to the given 1-based bound - ** parameter column in the given statement. If isStatic is true, it - ** avoids making an extra copy of v. - */ - int fsl_stmt_bind_text( fsl_stmt * stmt, int index, - char const * v, fsl_int_t n, - char isStatic ); - /* - ** TODO. - ** - ** Binds the first n bytes of v as a blob to the given 1-based bound - ** parameter column in the given statement. If isStatic is true, it - ** avoids making an extra copy of v. - */ - int fsl_stmt_bind_blob( fsl_stmt * stmt, int index, - void const * v, fsl_int_t len, - char isStatic ); - - - /* - ** 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 ); - - /* - ** 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. - ** - ** 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 ); - - /* - ** Gets a blob 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. - ** - ** 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 ); - - int fsl_db_exec( fsl_ctx * f, char const * sql, ... ); - int fsl_db_execv( fsl_ctx * f, char const * sql, va_list args ); - int fsl_db_get_int32( fsl_ctx * f, fsl_int32_t * stmt, fsl_int32_t defaultValue, char const * sql, ... ); - int fsl_db_get_int64( fsl_ctx * f, fsl_int64_t * stmt, fsl_int64_t defaultValue, char const * sql, ... ); - int fsl_db_get_text( fsl_ctx * f, char ** stmt, fsl_size_t * stmtLen, - char const * sql, ... ); - int fsl_db_get_int32( fsl_ctx * f, fsl_int32_t * stmt, fsl_int32_t defaultValue, char const * sql, ... ); - int fsl_db_get_int64( fsl_ctx * f, fsl_int64_t * stmt, fsl_int64_t defaultValue, char const * sql, ... ); - int fsl_db_get_blob( fsl_ctx * f, void ** stmt, fsl_size_t * stmtLen, - char const * sql, ... ); - int fsl_db_get_buffer( fsl_ctx * f, fsl_buffer * stmt, - char const * sql, ... ); - int fsl_db_step_query( fsl_ctx * f, fsl_stmt_each_f callback, - void * callbackState, char const * sql, ... ); - int fsl_db_step_queryv( fsl_ctx * f, fsl_stmt_each_f callback, - void * callbackState, char const * sql, va_list args ); - - /** - Parameters for fsl_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_ctx, 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_ctx 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. - */ - }; - 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_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} - - /** 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_init() is passed a NULL value for this parameter. - */ - extern const fsl_init_param fsl_init_param_default; - - /* - ** Initializes a fsl_ctx instance. tgt must be a pointer to NULL, - ** e.g.: - ** - ** @code - ** fsl_cxt * f = NULL; - ** int rc = fsl_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_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_init( fsl_ctx ** tgt, fsl_init_param * param ); - - /* - ** Frees all memory associated with f, which must have been - ** initialized using fsl_init() (or equivalent). - ** - ** Returns FSL_RC_MISUSE if !f, else 0. - */ - int fsl_finalize( fsl_ctx * 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_err_set( fsl_ctx * f, int code, char const * fmt, - ... ); - - /** va_list counterpart to fsl_err_set(). */ - int fsl_err_setv( fsl_ctx * f, int code, char const * fmt, - va_list args ); - - /* - ** Fetches the error state set using fsl_err_set(). If !f it - ** returns FSL_RC_MISUSE without side-effects, else it returns f'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). If len is not NULL then *len will hold the length - ** of the string (in bytes). The memory for the string is owned by - ** f and may be invalidated by any calls which take f as a - ** parameter, so the client is required to copy it if it is needed - ** for later on. - */ - int fsl_err_get( fsl_ctx * 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 (f, 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 (f, 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 !f or - ** !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. Returns 0 on success, - ** FSL_RC_MISUSE if !buf. - */ - 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->used to n. - ** - ** - On success it always NUL-terminates the buffer at - ** offset buf->used. - ** - ** Returns 0 on success, FSL_RC_MISUSE if !f or !buf, FSL_RC_OOM if - ** (re)allocation fails. After success buf->capacity might not be - ** _exactly_ n, as this routine allocates one extra byte to ensure - ** that buf is always NUL-terminated. - */ - int fsl_buffer_resize( fsl_buffer * buf, fsl_size_t n ); - - /* - ** Appends the first n bytes of src to b, expanding b as - ** necessary. If n is less than 0 then the equivalent of - ** strlen((char const*)src) is used. - ** - ** 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. - ** - ** 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 give 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!=pIn1then 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); - - /* - ** 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); - - /* - ** 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 *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 - ** (returning a negative value). - */ - int fsl_buffer_compare(fsl_buffer const * lhs, fsl_buffer const * rhs); - - /* - ** Compare two buffers in constant 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_constant_time(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(f,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_ctx * 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_ctx * f, char const * fmt, ... ); - - /** va_list counterpart to fsl_outputf(). */ - int fsl_outputfv( fsl_ctx * 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 ); - - /* - ** TODO. - ** - ** Should initialize f for being used with an opened repo found in - ** (or above) the given directory. - ** - ** Note that this is a lower level operation than the fossil - ** binary's 'open' command, and simply initialized f's db-related - ** resources. It will/could/maybe should look under the given dir - ** for an opened repo (analog to how the v1 binary does). - */ - int fsl_repo_open_dir( fsl_ctx * f, char const * repoDir, int flags /*???*/ ); - - /* - ** If fsl_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. - */ - int fsl_repo_close( fsl_ctx * f ); - - /* - ** Convenience form of fsl_stmt_prepare() which uses f's opened - ** repository db. Returns 0 on success, FSL_RC_MISUSE if !f or !sql, - ** FSL_RC_RANGE if !*sql, FSL_RC_NOT_A_REPO if fsl_repo_open_db() or - ** similar has not been used to open a repository. - */ - int fsl_repo_prepare( fsl_ctx *f, fsl_stmt * tgt, char const * sql, ... ); - - /* - ** va_list equivalent of fsl_repo_prepare(). - */ - int fsl_repo_preparev( fsl_ctx *f, fsl_stmt * tgt, char const * sql, va_list args ); - - /* - ** @internal - ** - ** Closes the given db. - */ - int fsl_db_close( fsl_ctx * f, fsl_db * db ); - - /* - ** Opens a db handle in the context of f. 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, or if - ** f already has an opened db. - ** - ** FIXME: we will possibly eventually need multiple DB handles per - ** context. If so, make fsl_db a linked list and manage them that - ** way, closing them in reverse-opened order in fsl_finalize(). Note - ** that this function is intended for a repo db, not a config db or - ** some such. - ** - ** Note that this is a lower level operation than the fossil - ** binary's 'open' command, and simply initialized f's db-related - ** resources. - */ - int fsl_repo_open_db( fsl_ctx * f, char const * repoDbFile, ... /*???*/ ); - - /* - ** 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_repo_create_db( fsl_ctx * f, char const * repoDbFile, int flags /*???*/ ); - - /** - @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, as far as i'm aware.) - - - CURRENT (documented) PRINTF EXTENSIONS: - - %%z works like %%s, but takes a non-const (char *) and vappendf - deletes the string (using free()) after appending it to the output. - - %%h (HTML) works like %s but converts certain characters (like '<' 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. - - (The %%q and %%Q specifiers are options inherited from this printf - implementation's sqlite3 genes.) - - These extensions may be disabled by setting certain macros when - compiling vappendf.c (see that file for details). - */ - long fsl_appendfv( - fsl_appendf_f pfAppend, /* Accumulate results here */ - void * pfAppendArg, /* Passed as first arg to pfAppend. */ - const char *fmt, /* Format string */ - va_list ap /* arguments */ - ); - - /** - 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(). - */ - 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 null-terminated - string is owned by the caller and it must be cleaned up using - fsl_free(f,...). If !fmt or if the expanded string evaluates to - empty, null is returned, not a 0-byte string. - */ - char * fsl_mprintf( char const * fmt, ... ); - - /** - va_list counterpart to fsl_mprintf(). - */ - char * fsl_mprintfv(char const * fmt, va_list vargs ); - - -#if defined(__cplusplus) -} /*extern "C"*/ -#endif -#endif -/* NET_FOSSIL_SCM_FOSSIL2_H_INCLUDED */ ADDED src/fossil2.c Index: src/fossil2.c ================================================================== --- /dev/null +++ src/fossil2.c @@ -0,0 +1,289 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* +** 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 "fossil/fossil2.h" +#include +#include +#include +#include +#include +#include + +const fsl_buffer fsl_buffer_empty = fsl_buffer_empty_m; +const fsl_init_param fsl_init_param_empty = fsl_init_param_empty_m; +const fsl_init_param fsl_init_param_default = fsl_init_param_default_m; +const fsl_outputer fsl_outputer_FILE = fsl_outputer_FILE_m; +const fsl_ctx fsl_ctx_empty = fsl_ctx_empty_m; +const fsl_db fsl_db_empty = fsl_db_empty_m; +const fsl_stmt fsl_stmt_empty = { +NULL/*f*/, +NULL/*stmt*/, +NULL/*db*/, +fsl_buffer_empty_m/*sql*/, +0/*colCount*/, +0/*paramCount*/ +}; + +#define fsl_db_record_empty_m { \ +FSL_TYPE_INVALID /*typeId*/,\ +0/*dbId*/,\ +NULL/*next*/\ +} +const fsl_db_record fsl_db_record_empty = fsl_db_record_empty_m; + +const fsl_user fsl_user_empty = { +fsl_db_record_empty_m /*base*/, +fsl_buffer_empty_m /* name */, +FSL_ROLE_GUEST /* roles */, +0/*mtime*/ +}; + +const fsl_tag fsl_tag_empty = { +fsl_db_record_empty_m /*base*/, +fsl_buffer_empty_m /* key */, +fsl_buffer_empty_m /* value */ +}; + +const fsl_blob fsl_blob_empty = { +fsl_db_record_empty_m /*base*/, +0/*mtime*/, +{/*sha1*/0} +}; + +const fsl_allocator fsl_allocator_stdalloc = { +fsl_realloc_f_stdalloc, +NULL +}; + +fsl_allocator fsl_memory_allocator = { +fsl_realloc_f_stdalloc, +NULL +}; + + + +void * fsl_malloc( fsl_size_t n ){ + return n + ? fsl_memory_allocator.f(fsl_memory_allocator.state, NULL, n) + : NULL; +} + +void fsl_free( void * mem ){ + if(mem) fsl_memory_allocator.f(fsl_memory_allocator.state, mem, 0); +} + +void * fsl_realloc( void * mem, fsl_size_t n ){ + if(!mem){ + return fsl_malloc(n); + }else if(!n){ + fsl_free(mem); + return NULL; + }else{ + return fsl_memory_allocator.f(fsl_memory_allocator.state, mem, n); + } +} + +void * fsl_realloc_f_stdalloc(void * state, void * mem, fsl_size_t n){ + if(!mem){ + return malloc(n); + }else if(!n){ + free(mem); + return NULL; + }else{ + return realloc(mem, n); + } +} + + + +int fsl_flush_f_FILE(void * _FILE){ + return _FILE + ? (fflush((FILE*)_FILE) ? FSL_RC_IO : 0) + : FSL_RC_MISUSE; +} + +int fsl_output_f_FILE( void * state, + void const * src, fsl_size_t n ){ + return !state + ? FSL_RC_MISUSE + : ((1 == fwrite(src, n, 1, state ? (FILE*)state : stdout)) + ? 0 + : FSL_RC_IO); +} + +void fsl_finalizer_f_FILE( void * state, void * mem ){ + if(mem && (stdout != mem) && (stderr != mem)){ + fclose((FILE*)mem); + } +} + +int fsl_init( fsl_ctx ** tgt, fsl_init_param * param ){ + fsl_init_param paramDefaults = fsl_init_param_default; + fsl_ctx * f; + if(!tgt) return FSL_RC_MISUSE; + else if(!param){ + param = ¶mDefaults; + } + f = (fsl_ctx*)fsl_malloc(sizeof(fsl_ctx)); + if(!f) return FSL_RC_OOM; + *tgt = f; + + *f = fsl_ctx_empty; + f->output = param->output; + return 0; +} + + +int fsl_finalize( fsl_ctx * f ){ + if(!f) return FSL_RC_MISUSE; + else{ + if(f->clientState.finalize.f){ + f->clientState.finalize.f( f->clientState.finalize.state, + f->clientState.state ); + } + if(f->output.state.finalize.f){ + f->output.state.finalize.f( f->output.state.finalize.state, + f->output.state.state ); + } + fsl_buffer_reserve(&f->error.msg, 0); + fsl_buffer_reserve(&f->dbRepo.filename, 0); + assert(0 == f->dbRepo.openStatementCount); + if(f->dbRepo.dbh){ + fsl_db_close(f, &f->dbRepo); + } + *f = fsl_ctx_empty; + fsl_free(f); + return 0; + } +} + +int fsl_output( fsl_ctx * cx, void const * src, fsl_size_t n ){ + if(!cx || !src) return FSL_RC_MISUSE; + else if(!n || !cx->output.out) return 0; + else return cx->output.out( cx->output.state.state, + src, n ); +} + +int fsl_err_setv( fsl_ctx * f, int code, char const * fmt, + va_list args ){ + if(!f) return FSL_RC_MISUSE; + else if(!code){ /* clear error state */ + f->error.code = 0; + f->error.msg.used = 0; + if(f->error.msg.mem){ + f->error.msg.mem[0] = 0; + } + return 0; + }else{ + int rc = 0; + f->error.msg.used = 0; + f->error.code = code; + if(FSL_RC_OOM!=code){ + rc = fsl_buffer_appendfv(&f->error.msg, + fmt ? fmt : fsl_rc_cstr(code), + args); + } + return rc ? rc : code; + } +} + +int fsl_err_set( fsl_ctx * f, int code, char const * fmt, + ... ){ + if(!f || !fmt) return FSL_RC_MISUSE; + else{ + int rc; + va_list args; + va_start(args,fmt); + rc = fsl_err_setv( f, code, fmt, args ); + va_end(args); + return rc; + } +} + +int fsl_err_get( fsl_ctx * f, char const ** str, fsl_size_t * len ){ + if(!f) return FSL_RC_MISUSE; + else{ + if(str) *str = (char const *)f->error.msg.mem; + if(len) *len = f->error.msg.used; + return f->error.code; + } +} + +/* +** fsl_appendf_f() impl which sends its output to fsl_output(). +*/ +static long fsl_appendf_f_fsl_output( void * S, char const * s, + long n ){ + fsl_ctx * f = (fsl_ctx *)S; + int rc = fsl_output( f, s, (fsl_size_t)n ); + return rc ? -1 : n; +} + +int fsl_outputfv( fsl_ctx * f, char const * fmt, va_list args ){ + if(!f || !fmt) return FSL_RC_MISUSE; + else{ + long const prc = fsl_appendfv( fsl_appendf_f_fsl_output, + f, fmt, args ); + return prc>=0 ? 0 : FSL_RC_IO; + } +} + + +int fsl_outputf( fsl_ctx * f, char const * fmt, ... ){ + if(!f || !fmt) return FSL_RC_MISUSE; + else{ + int rc; + va_list args; + va_start(args,fmt); + rc = fsl_outputfv( f, fmt, args ); + va_end(args); + return rc; + } +} + + +char const * fsl_rc_cstr(int rc){ + switch(rc){ +#define STR(T) case FSL_RC_##T: return "FSL_RC_" #T + STR(OK); + STR(NYI); + STR(ERROR); + STR(OOM); + STR(MISUSE); + STR(RANGE); + STR(ACCESS); + STR(IO); + STR(NOT_FOUND); + STR(ALREADY_EXISTS); + STR(CONSISTENCY); + STR(REPO_NEEDS_REBUILD); + STR(NOT_A_REPO); + STR(REPO_VERSION); + STR(DB); +#undef STR + default: + return "Unknown result code"; + } +} + +char const * fsl_library_version(){ + return FSL_LIBRARY_VERSION; +} ADDED src/fsl_appendf.c Index: src/fsl_appendf.c ================================================================== --- /dev/null +++ src/fsl_appendf.c @@ -0,0 +1,1383 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/************************************************************************ +The printf-like implementation in this file is based on the one found +in the sqlite3 distribution is in the Public Domain. + +This copy was forked for use with the clob API in Feb 2008 by Stephan +Beal (http://wanderinghorse.net/home/stephan/) and modified to send +its output to arbitrary targets via a callback mechanism. Also +refactored the %X specifier handlers a bit to make adding/removing +specific handlers easier. + +All code in this file is released into the Public Domain. + +The printf implementation (fsl_appendfv()) is pretty easy to extend +(e.g. adding or removing %-specifiers for fsl_appendfv()) if you're +willing to poke around a bit and see how the specifiers are declared +and dispatched. For an example, grep for 'etSTRING' and follow it +through the process of declaration to implementation. + +See below for several FSLPRINTF_OMIT_xxx macros which can be set to +remove certain features/extensions. +************************************************************************/ + +#include /* FILE */ +#include /* strlen() */ +#include /* free/malloc() */ +#include +#include +#include +#include "fossil/fossil2.h" + +/* FIXME: determine this type at compile time */ +typedef long double LONGDOUBLE_TYPE; + +/* + If FSLPRINTF_OMIT_FLOATING_POINT is defined to a true value, then + floating point conversions are disabled. +*/ +#ifndef FSLPRINTF_OMIT_FLOATING_POINT +# define FSLPRINTF_OMIT_FLOATING_POINT 0 +#endif + +/* + If FSLPRINTF_OMIT_SIZE is defined to a true value, then + the %n specifier is disabled. +*/ +#ifndef FSLPRINTF_OMIT_SIZE +# define FSLPRINTF_OMIT_SIZE 0 +#endif + +/* + If FSLPRINTF_OMIT_SQL is defined to a true value, then + the %q and %Q specifiers are disabled. +*/ +#ifndef FSLPRINTF_OMIT_SQL +# define FSLPRINTF_OMIT_SQL 0 +#endif + +/* + If FSLPRINTF_OMIT_HTML is defined to a true value then the %h (HTML + escape), %t (URL escape), and %T (URL unescape) specifiers are + disabled. +*/ +#ifndef FSLPRINTF_OMIT_HTML +# define FSLPRINTF_OMIT_HTML 0 +#endif + +/* + Most C compilers handle variable-sized arrays, so we enable + that by default. Some (e.g. tcc) do not, so we provide a way + to disable it: set FSLPRINTF_HAVE_VARARRAY to 0 + + One approach would be to look at: + + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + + but some compilers support variable-sized arrays even when not + explicitly running in c99 mode. +*/ +#if !defined(FSLPRINTF_HAVE_VARARRAY) +# if defined(__TINYC__) +# define FSLPRINTF_HAVE_VARARRAY 0 +# else +# if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +# define FSLPRINTF_HAVE_VARARRAY 1 /*use 1 in C99 mode */ +# else +# define FSLPRINTF_HAVE_VARARRAY 0 +# endif +# endif +#endif + +/** + FSLPRINTF_CHARARRAY is a helper to allocate variable-sized arrays. + This exists mainly so this code can compile with the tcc compiler. +*/ +#if FSLPRINTF_HAVE_VARARRAY +# define FSLPRINTF_CHARARRAY(V,N) char V[N+1]; memset(V,0,N+1); +# define FSLPRINTF_CHARARRAY_FREE(V) +#else +# define FSLPRINTF_CHARARRAY(V,N) char * V = (char *)malloc(N+1); memset(V,0,N+1); +# define FSLPRINTF_CHARARRAY_FREE(V) free(V) +#endif + +/* + Conversion types fall into various categories as defined by the + following enumeration. +*/ +enum PrintfCategory {etRADIX = 1, /* Integer types. %d, %x, %o, and so forth */ + etFLOAT = 2, /* Floating point. %f */ + etEXP = 3, /* Exponentional notation. %e and %E */ + etGENERIC = 4, /* Floating or exponential, depending on exponent. %g */ + etSIZE = 5, /* Return number of characters processed so far. %n */ + etSTRING = 6, /* Strings. %s */ + etDYNSTRING = 7, /* Dynamically allocated strings. %z */ + etPERCENT = 8, /* Percent symbol. %% */ + etCHARX = 9, /* Characters. %c */ + /* The rest are extensions, not normally found in printf() */ + etCHARLIT = 10, /* Literal characters. %' */ +#if !FSLPRINTF_OMIT_SQL + etSQLESCAPE = 11, /* Strings with '\'' doubled. %q */ + etSQLESCAPE2 = 12, /* Strings with '\'' doubled and enclosed in '', + NULL pointers replaced by SQL NULL. %Q */ + etSQLESCAPE3 = 16, /* %w -> Strings with '\"' doubled */ +#endif /* !FSLPRINTF_OMIT_SQL */ + etPOINTER = 15, /* The %p conversion */ + etORDINAL = 17, /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */ +#if ! FSLPRINTF_OMIT_HTML + etHTML = 18, /* %h -> basic HTML escaping. */ + etURLENCODE = 19, /* %t -> URL encoding. */ + etURLDECODE = 20, /* %T -> URL decoding. */ +#endif + etPLACEHOLDER = 100 + }; + +/* + An "etByte" is an 8-bit unsigned value. +*/ +typedef unsigned char etByte; + +/* + Each builtin conversion character (ex: the 'd' in "%d") is described + by an instance of the following structure +*/ +typedef struct et_info { /* Information about each format field */ + char fmttype; /* The format field code letter */ + etByte base; /* The base for radix conversion */ + etByte flags; /* One or more of FLAG_ constants below */ + etByte type; /* Conversion paradigm */ + etByte charset; /* Offset into aDigits[] of the digits string */ + etByte prefix; /* Offset into aPrefix[] of the prefix string */ +} et_info; + +/* + Allowed values for et_info.flags +*/ +enum et_info_flags { FLAG_SIGNED = 1, /* True if the value to convert is signed */ + FLAG_EXTENDED = 2, /* True if for internal/extended use only. */ + FLAG_STRING = 4 /* Allow infinity precision */ +}; + +/* + Historically, the following table was searched linearly, so the most + common conversions were kept at the front. + + Change 2008 Oct 31 by Stephan Beal: we reserve an array or ordered + entries for all chars in the range [32..126]. Format character + checks can now be done in constant time by addressing that array + directly. This takes more static memory, but reduces the time and + per-call overhead costs of fsl_appendfv(). +*/ +static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; +static const char aPrefix[] = "-x0\000X0"; +static const et_info fmtinfo[] = { +/** + If FSLPRINTF_FMTINFO_FIXED is 1 then we use the original + implementation: a linear list of entries. Search time is linear. If + FSLPRINTF_FMTINFO_FIXED is 0 then we use a fixed-size array which + we index directly using the format char as the key. +*/ +#define FSLPRINTF_FMTINFO_FIXED 0 +#if FSLPRINTF_FMTINFO_FIXED +{ 'd', 10, FLAG_SIGNED, etRADIX, 0, 0 }, +{ 's', 0, FLAG_STRING, etSTRING, 0, 0 }, +{ 'g', 0, FLAG_SIGNED, etGENERIC, 30, 0 }, +{ 'z', 0, FLAG_STRING, etDYNSTRING, 0, 0 }, +{ 'c', 0, 0, etCHARX, 0, 0 }, +{ 'o', 8, 0, etRADIX, 0, 2 }, +{ 'u', 10, 0, etRADIX, 0, 0 }, +{ 'x', 16, 0, etRADIX, 16, 1 }, +{ 'X', 16, 0, etRADIX, 0, 4 }, +{ 'i', 10, FLAG_SIGNED, etRADIX, 0, 0 }, +#if !FSLPRINTF_OMIT_FLOATING_POINT +{ 'f', 0, FLAG_SIGNED, etFLOAT, 0, 0 }, +{ 'e', 0, FLAG_SIGNED, etEXP, 30, 0 }, +{ 'E', 0, FLAG_SIGNED, etEXP, 14, 0 }, +{ 'G', 0, FLAG_SIGNED, etGENERIC, 14, 0 }, +#endif /* !FSLPRINTF_OMIT_FLOATING_POINT */ +{ '%', 0, 0, etPERCENT, 0, 0 }, +{ 'p', 16, 0, etPOINTER, 0, 1 }, +{ 'r', 10, (FLAG_EXTENDED|FLAG_SIGNED), etORDINAL, 0, 0 }, +#if ! FSLPRINTF_OMIT_SQL +{ 'q', 0, FLAG_STRING, etSQLESCAPE, 0, 0 }, +{ 'Q', 0, FLAG_STRING, etSQLESCAPE2, 0, 0 }, +{ 'w', 0, FLAG_STRING, etSQLESCAPE3, 0, 0 }, +#endif /* !FSLPRINTF_OMIT_SQL */ +#if ! FSLPRINTF_OMIT_HTML +{ 'h', 0, FLAG_STRING, etHTML, 0, 0 }, +{ 't', 0, FLAG_STRING, etURLENCODE, 0, 0 }, +{ 'T', 0, FLAG_STRING, etURLDECODE, 0, 0 }, +#endif /* !FSLPRINTF_OMIT_HTML */ +#if !FSLPRINTF_OMIT_SIZE +{ 'n', 0, 0, etSIZE, 0, 0 }, +#endif +#else /* FSLPRINTF_FMTINFO_FIXED */ +/* + These entries MUST stay in ASCII order, sorted + on their fmttype member! +*/ +{' '/*32*/, 0, 0, 0, 0, 0 }, +{'!'/*33*/, 0, 0, 0, 0, 0 }, +{'"'/*34*/, 0, 0, 0, 0, 0 }, +{'#'/*35*/, 0, 0, 0, 0, 0 }, +{'$'/*36*/, 0, 0, 0, 0, 0 }, +{'%'/*37*/, 0, 0, etPERCENT, 0, 0 }, +{'&'/*38*/, 0, 0, 0, 0, 0 }, +{'\''/*39*/, 0, 0, 0, 0, 0 }, +{'('/*40*/, 0, 0, 0, 0, 0 }, +{')'/*41*/, 0, 0, 0, 0, 0 }, +{'*'/*42*/, 0, 0, 0, 0, 0 }, +{'+'/*43*/, 0, 0, 0, 0, 0 }, +{','/*44*/, 0, 0, 0, 0, 0 }, +{'-'/*45*/, 0, 0, 0, 0, 0 }, +{'.'/*46*/, 0, 0, 0, 0, 0 }, +{'/'/*47*/, 0, 0, 0, 0, 0 }, +{'0'/*48*/, 0, 0, 0, 0, 0 }, +{'1'/*49*/, 0, 0, 0, 0, 0 }, +{'2'/*50*/, 0, 0, 0, 0, 0 }, +{'3'/*51*/, 0, 0, 0, 0, 0 }, +{'4'/*52*/, 0, 0, 0, 0, 0 }, +{'5'/*53*/, 0, 0, 0, 0, 0 }, +{'6'/*54*/, 0, 0, 0, 0, 0 }, +{'7'/*55*/, 0, 0, 0, 0, 0 }, +{'8'/*56*/, 0, 0, 0, 0, 0 }, +{'9'/*57*/, 0, 0, 0, 0, 0 }, +{':'/*58*/, 0, 0, 0, 0, 0 }, +{';'/*59*/, 0, 0, 0, 0, 0 }, +{'<'/*60*/, 0, 0, 0, 0, 0 }, +{'='/*61*/, 0, 0, 0, 0, 0 }, +{'>'/*62*/, 0, 0, 0, 0, 0 }, +{'?'/*63*/, 0, 0, 0, 0, 0 }, +{'@'/*64*/, 0, 0, 0, 0, 0 }, +{'A'/*65*/, 0, 0, 0, 0, 0 }, +{'B'/*66*/, 0, 0, 0, 0, 0 }, +{'C'/*67*/, 0, 0, 0, 0, 0 }, +{'D'/*68*/, 0, 0, 0, 0, 0 }, +{'E'/*69*/, 0, FLAG_SIGNED, etEXP, 14, 0 }, +{'F'/*70*/, 0, 0, 0, 0, 0 }, +{'G'/*71*/, 0, FLAG_SIGNED, etGENERIC, 14, 0 }, +{'H'/*72*/, 0, 0, 0, 0, 0 }, +{'I'/*73*/, 0, 0, 0, 0, 0 }, +{'J'/*74*/, 0, 0, 0, 0, 0 }, +{'K'/*75*/, 0, 0, 0, 0, 0 }, +{'L'/*76*/, 0, 0, 0, 0, 0 }, +{'M'/*77*/, 0, 0, 0, 0, 0 }, +{'N'/*78*/, 0, 0, 0, 0, 0 }, +{'O'/*79*/, 0, 0, 0, 0, 0 }, +{'P'/*80*/, 0, 0, 0, 0, 0 }, +#if FSLPRINTF_OMIT_SQL +{'Q'/*81*/, 0, 0, 0, 0, 0 }, +#else +{'Q'/*81*/, 0, FLAG_STRING, etSQLESCAPE2, 0, 0 }, +#endif +{'R'/*82*/, 0, 0, 0, 0, 0 }, +{'S'/*83*/, 0, 0, 0, 0, 0 }, +{'T'/*84*/, 0, FLAG_STRING, etURLDECODE, 0, 0 }, +{'U'/*85*/, 0, 0, 0, 0, 0 }, +{'V'/*86*/, 0, 0, 0, 0, 0 }, +{'W'/*87*/, 0, 0, 0, 0, 0 }, +{'X'/*88*/, 16, 0, etRADIX, 0, 4 }, +{'Y'/*89*/, 0, 0, 0, 0, 0 }, +{'Z'/*90*/, 0, 0, 0, 0, 0 }, +{'['/*91*/, 0, 0, 0, 0, 0 }, +{'\\'/*92*/, 0, 0, 0, 0, 0 }, +{']'/*93*/, 0, 0, 0, 0, 0 }, +{'^'/*94*/, 0, 0, 0, 0, 0 }, +{'_'/*95*/, 0, 0, 0, 0, 0 }, +{'`'/*96*/, 0, 0, 0, 0, 0 }, +{'a'/*97*/, 0, 0, 0, 0, 0 }, +{'b'/*98*/, 0, 0, 0, 0, 0 }, +{'c'/*99*/, 0, 0, etCHARX, 0, 0 }, +{'d'/*100*/, 10, FLAG_SIGNED, etRADIX, 0, 0 }, +{'e'/*101*/, 0, FLAG_SIGNED, etEXP, 30, 0 }, +{'f'/*102*/, 0, FLAG_SIGNED, etFLOAT, 0, 0}, +{'g'/*103*/, 0, FLAG_SIGNED, etGENERIC, 30, 0 }, +{'h'/*104*/, 0, FLAG_STRING, etHTML, 0, 0 }, +{'i'/*105*/, 10, FLAG_SIGNED, etRADIX, 0, 0}, +{'j'/*106*/, 0, 0, 0, 0, 0 }, +{'k'/*107*/, 0, 0, 0, 0, 0 }, +{'l'/*108*/, 0, 0, 0, 0, 0 }, +{'m'/*109*/, 0, 0, 0, 0, 0 }, +{'n'/*110*/, 0, 0, etSIZE, 0, 0 }, +{'o'/*111*/, 8, 0, etRADIX, 0, 2 }, +{'p'/*112*/, 16, 0, etPOINTER, 0, 1 }, +#if FSLPRINTF_OMIT_SQL +{'q'/*113*/, 0, 0, 0, 0, 0 }, +#else +{'q'/*113*/, 0, FLAG_STRING, etSQLESCAPE, 0, 0 }, +#endif +{'r'/*114*/, 10, (FLAG_EXTENDED|FLAG_SIGNED), etORDINAL, 0, 0}, +{'s'/*115*/, 0, FLAG_STRING, etSTRING, 0, 0 }, +{'t'/*116*/, 0, FLAG_STRING, etURLENCODE, 0, 0 }, +{'u'/*117*/, 10, 0, etRADIX, 0, 0 }, +{'v'/*118*/, 0, 0, 0, 0, 0 }, +#if FSLPRINTF_OMIT_SQL +{'w'/*119*/, 0, 0, 0, 0, 0 }, +#else +{'w'/*119*/, 0, FLAG_STRING, etSQLESCAPE3, 0, 0 }, +#endif +{'x'/*120*/, 16, 0, etRADIX, 16, 1 }, +{'y'/*121*/, 0, 0, 0, 0, 0 }, +{'z'/*122*/, 0, FLAG_STRING, etDYNSTRING, 0, 0}, +{'{'/*123*/, 0, 0, 0, 0, 0 }, +{'|'/*124*/, 0, 0, 0, 0, 0 }, +{'}'/*125*/, 0, 0, 0, 0, 0 }, +{'~'/*126*/, 0, 0, 0, 0, 0 }, +#endif /* FSLPRINTF_FMTINFO_FIXED */ +}; +#define etNINFO (sizeof(fmtinfo)/sizeof(fmtinfo[0])) + +#if ! FSLPRINTF_OMIT_FLOATING_POINT +/* + "*val" is a double such that 0.1 <= *val < 10.0 + Return the ascii code for the leading digit of *val, then + multiply "*val" by 10.0 to renormalize. + ** + Example: + input: *val = 3.14159 + output: *val = 1.4159 function return = '3' + ** + The counter *cnt is incremented each time. After counter exceeds + 16 (the number of significant digits in a 64-bit float) '0' is + always returned. +*/ +static int et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ + int digit; + LONGDOUBLE_TYPE d; + if( (*cnt)++ >= 16 ) return '0'; + digit = (int)*val; + d = digit; + digit += '0'; + *val = (*val - d)*10.0; + return digit; +} +#endif /* !FSLPRINTF_OMIT_FLOATING_POINT */ + +/* + On machines with a small(?) stack size, you can redefine the + FSLPRINTF_BUF_SIZE to be less than 350. But beware - for smaller + values some %f conversions may go into an infinite loop. +*/ +#ifndef FSLPRINTF_BUF_SIZE +# define FSLPRINTF_BUF_SIZE 350 /* Size of the output buffer for numeric conversions */ +#endif + +#if ! defined(__STDC__) && !defined(__TINYC__) +#ifdef FSLPRINTF_INT64_TYPE +typedef FSLPRINTF_INT64_TYPE int64_t; +typedef unsigned FSLPRINTF_INT64_TYPE uint64_t; +#elif defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#endif +#endif + +#if 0 +/ Not yet used. */ +enum PrintfArgTypes { +TypeInt = 0, +TypeIntP = 1, +TypeFloat = 2, +TypeFloatP = 3, +TypeCString = 4 +}; +#endif + + +#if 0 +/ Not yet used. */ +typedef struct fsl_appendf_spec_handler_def +{ + char letter; / e.g. %s */ + int xtype; /* reference to the etXXXX values, or fmtinfo[*].type. */ + int ntype; /* reference to PrintfArgTypes enum. */ +} spec_handler; +#endif + +/** + fsl_appendf_spec_handler is an almost-generic interface for farming + work out of fsl_appendfv()'s code into external functions. It doesn't + actually save much (if any) overall code, but it makes the fsl_appendfv() + code more manageable. + + + REQUIREMENTS of implementations: + + - Expects an implementation-specific vargp pointer. + fsl_appendfv() passes a pointer to the converted value of + an entry from the format va_list. If it passes a type + other than the expected one, undefined results. + + - If it calls pf then it must return the return value + from that function. + + - If it calls pf it must do: pf( pfArg, D, N ), where D is + the data to export and N is the number of bytes to export. + It may call pf() an arbitrary number of times + + - If pf() successfully is called, the return value must be the + accumulated totals of its return value(s), plus (possibly, but + unlikely) an imnplementation-specific amount. + + - If it does not call pf() then it must return 0 (success) + or a negative number (an error) or do all of the export + processing itself and return the number of bytes exported. + + + SIGNIFICANT LIMITATIONS: + + - Has no way of iterating over the format string, so handling + precisions and such here can't work too well. (Nevermind: + precision/justification is handled in fsl_appendfv().) +*/ +typedef long (*fsl_appendf_spec_handler)( fsl_appendf_f pf, + void * pfArg, + unsigned int pfLen, + void * vargp ); + + +/** + fsl_appendf_spec_handler for etSTRING types. It assumes that varg + is a NUL-terminated (char [const] *) +*/ +static long spech_string( fsl_appendf_f pf, + void * pfArg, + unsigned int pfLen, + void * varg ) +{ + char const * ch = (char const *) varg; + return ch ? pf( pfArg, ch, pfLen ) : 0; +} + +/** + fsl_appendf_spec_handler for etDYNSTRING types. It assumes that + varg is a non-const (char *). It behaves identically to + spec_string() and then calls free() on that (char *). +*/ +static long spech_dynstring( fsl_appendf_f pf, + void * pfArg, + unsigned int pfLen, + void * varg ) +{ + long ret = spech_string( pf, pfArg, pfLen, varg ); + fsl_free( varg ); + return ret; +} + +#if !FSLPRINTF_OMIT_HTML +static long spech_string_to_html( fsl_appendf_f pf, + void * pfArg, + unsigned int pfLen, + void * varg ) +{ + char const * ch = (char const *) varg; + unsigned int i; + long ret = 0; + if( ! ch ) return 0; + ret = 0; + for( i = 0; (i= 32 && c <=47) + || ( c>=58 && c<=64) + || ( c>=91 && c<=96) + || ( c>=123 && c<=126) + || ( c<32 || c>=127) + ); +} + +/** + The handler for the etURLENCODE specifier. + + It expects varg to be a string value, which it will preceed to + encode using an URL encoding algothrim (certain characters are + converted to %XX, where XX is their hex value) and passes the + encoded string to pf(). It returns the total length of the output + string. +*/ +static long spech_urlencode( fsl_appendf_f pf, + void * pfArg, + unsigned int pfLen, + void * varg ) +{ + char const * str = (char const *) varg; + long ret = 0; + char ch = 0; + char const * hex = "0123456789ABCDEF"; +#define xbufsz 10 + char xbuf[xbufsz]; + int slen = 0; + if( ! str ) return 0; + memset( xbuf, 0, xbufsz ); + ch = *str; +#define xbufsz 10 + slen = 0; + for( ; ch; ch = *(++str) ) + { + if( ! httpurl_needs_escape( ch ) ) + { + ret += pf( pfArg, str, 1 ); + continue; + } + else { + xbuf[0] = '%'; + xbuf[1] = hex[((ch>>4)&0xf)]; + xbuf[2] = hex[(ch&0xf)]; + xbuf[3] = 0; + slen = 3; + ret += pf( pfArg, xbuf, slen ); + } + } +#undef xbufsz + return ret; +} + +/* + hexchar_to_int(): + + For 'a'-'f', 'A'-'F' and '0'-'9', returns the appropriate decimal + number. For any other character it returns -1. +*/ +static int hexchar_to_int( int ch ) +{ + if( (ch>='a' && ch<='f') ) return ch-'a'+10; + else if( (ch>='A' && ch<='F') ) return ch-'A'+10; + else if( (ch>='0' && ch<='9') ) return ch-'0'; + return -1; +} + +/** + The handler for the etURLDECODE specifier. + + It expects varg to be a ([const] char *), possibly encoded + with URL encoding. It decodes the string using a URL decode + algorithm and passes the decoded string to + pf(). It returns the total length of the output string. + If the input string contains malformed %XX codes then this + function will return prematurely. +*/ +static long spech_urldecode( fsl_appendf_f pf, + void * pfArg, + unsigned int pfLen, + void * varg ) +{ + char const * str = (char const *) varg; + long ret = 0; + char ch = 0; + char ch2 = 0; + char xbuf[4]; + int decoded; + if( ! str ) return 0; + ch = *str; + while( ch ) + { + if( ch == '%' ) + { + ch = *(++str); + ch2 = *(++str); + if( isxdigit(ch) && + isxdigit(ch2) ) + { + decoded = (hexchar_to_int( ch ) * 16) + + hexchar_to_int( ch2 ); + xbuf[0] = (char)decoded; + xbuf[1] = 0; + ret += pf( pfArg, xbuf, 1 ); + ch = *(++str); + continue; + } + else + { + xbuf[0] = '%'; + xbuf[1] = ch; + xbuf[2] = ch2; + xbuf[3] = 0; + ret += pf( pfArg, xbuf, 3 ); + ch = *(++str); + continue; + } + } + else if( ch == '+' ) + { + xbuf[0] = ' '; + xbuf[1] = 0; + ret += pf( pfArg, xbuf, 1 ); + ch = *(++str); + continue; + } + xbuf[0] = ch; + xbuf[1] = 0; + ret += pf( pfArg, xbuf, 1 ); + ch = *(++str); + } + return ret; +} + +#endif /* !FSLPRINTF_OMIT_HTML */ + + +#if !FSLPRINTF_OMIT_SQL +/** + Quotes the (char *) varg as an SQL string 'should' + be quoted. The exact type of the conversion + is specified by xtype, which must be one of + etSQLESCAPE, etSQLESCAPE2, or etSQLESCAPE3. + + Search this file for those constants to find + the associated documentation. +*/ +static long spech_sqlstring_main( int xtype, + fsl_appendf_f pf, + void * pfArg, + unsigned int pfLen, + void * varg ) +{ + unsigned int i, n; + int j, ch, isnull; + int needQuote; + char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */ + char const * escarg = (char const *) varg; + char * bufpt = NULL; + long ret; + isnull = escarg==0; + if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)"); + for(i=n=0; (i='0' && c<='9' ){ + width = width*10 + c - '0'; + c = *++fmt; + } + } + if( width > FSLPRINTF_BUF_SIZE-10 ){ + width = FSLPRINTF_BUF_SIZE-10; + } + /* Get the precision */ + if( c=='.' ){ + precision = 0; + c = *++fmt; + if( c=='*' ){ + precision = va_arg(ap,int); + if( precision<0 ) precision = -precision; + c = *++fmt; + }else{ + while( c>='0' && c<='9' ){ + precision = precision*10 + c - '0'; + c = *++fmt; + } + } + }else{ + precision = -1; + } + /* Get the conversion type modifier */ + if( c=='l' ){ + flag_long = 1; + c = *++fmt; + if( c=='l' ){ + flag_longlong = 1; + c = *++fmt; + }else{ + flag_longlong = 0; + } + }else{ + flag_long = flag_longlong = 0; + } + /* Fetch the info entry for the field */ + infop = 0; +#if FSLPRINTF_FMTINFO_FIXED + for(idx=0; idxflags & FLAG_EXTENDED)==0 ){ + xtype = infop->type; + }else{ + FSLPRINTF_RETURN; + } + break; + } + } +#else +#define FMTNDX(N) (N - fmtinfo[0].fmttype) +#define FMTINFO(N) (fmtinfo[ FMTNDX(N) ]) + infop = ((c>=(fmtinfo[0].fmttype)) && (cfmttype,infop->type);*/ + if( infop ) xtype = infop->type; +#undef FMTINFO +#undef FMTNDX +#endif /* FSLPRINTF_FMTINFO_FIXED */ + zExtra = 0; + if( (!infop) || (!infop->type) ){ + FSLPRINTF_RETURN; + } + + + /* Limit the precision to prevent overflowing buf[] during conversion */ + if( precision>FSLPRINTF_BUF_SIZE-40 && (infop->flags & FLAG_STRING)==0 ){ + precision = FSLPRINTF_BUF_SIZE-40; + } + + /* + At this point, variables are initialized as follows: + ** + flag_alternateform TRUE if a '#' is present. + flag_altform2 TRUE if a '!' is present. + flag_plussign TRUE if a '+' is present. + flag_leftjustify TRUE if a '-' is present or if the + field width was negative. + flag_zeropad TRUE if the width began with 0. + flag_long TRUE if the letter 'l' (ell) prefixed + the conversion character. + flag_longlong TRUE if the letter 'll' (ell ell) prefixed + the conversion character. + flag_blanksign TRUE if a ' ' is present. + width The specified field width. This is + always non-negative. Zero is the default. + precision The specified precision. The default + is -1. + xtype The class of the conversion. + infop Pointer to the appropriate info struct. + */ + switch( xtype ){ + case etPOINTER: + flag_longlong = sizeof(char*)==sizeof(int64_t); + flag_long = sizeof(char*)==sizeof(long int); + /* Fall through into the next case */ + case etORDINAL: + case etRADIX: + if( infop->flags & FLAG_SIGNED ){ + int64_t v; + if( flag_longlong ) v = va_arg(ap,int64_t); + else if( flag_long ) v = va_arg(ap,long int); + else v = va_arg(ap,int); + if( v<0 ){ + longvalue = -v; + prefix = '-'; + }else{ + longvalue = v; + if( flag_plussign ) prefix = '+'; + else if( flag_blanksign ) prefix = ' '; + else prefix = 0; + } + }else{ + if( flag_longlong ) longvalue = va_arg(ap,uint64_t); + else if( flag_long ) longvalue = va_arg(ap,unsigned long int); + else longvalue = va_arg(ap,unsigned int); + prefix = 0; + } + if( longvalue==0 ) flag_alternateform = 0; + if( flag_zeropad && precision=4 || (longvalue/10)%10==1 ){ + x = 0; + } + buf[FSLPRINTF_BUF_SIZE-3] = zOrd[x*2]; + buf[FSLPRINTF_BUF_SIZE-2] = zOrd[x*2+1]; + bufpt -= 2; + } + { + const char *cset; + int base; + cset = &aDigits[infop->charset]; + base = infop->base; + do{ /* Convert to ascii */ + *(--bufpt) = cset[longvalue%base]; + longvalue = longvalue/base; + }while( longvalue>0 ); + } + length = &buf[FSLPRINTF_BUF_SIZE-1]-bufpt; + for(idx=precision-length; idx>0; idx--){ + *(--bufpt) = '0'; /* Zero pad */ + } + if( prefix ) *(--bufpt) = prefix; /* Add sign */ + if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */ + const char *pre; + char x; + pre = &aPrefix[infop->prefix]; + if( *bufpt!=pre[0] ){ + for(; (x=(*pre))!=0; pre++) *(--bufpt) = x; + } + } + length = &buf[FSLPRINTF_BUF_SIZE-1]-bufpt; + break; + case etFLOAT: + case etEXP: + case etGENERIC: + realvalue = va_arg(ap,double); +#if ! FSLPRINTF_OMIT_FLOATING_POINT + if( precision<0 ) precision = 6; /* Set default precision */ + if( precision>FSLPRINTF_BUF_SIZE/2-10 ) precision = FSLPRINTF_BUF_SIZE/2-10; + if( realvalue<0.0 ){ + realvalue = -realvalue; + prefix = '-'; + }else{ + if( flag_plussign ) prefix = '+'; + else if( flag_blanksign ) prefix = ' '; + else prefix = 0; + } + if( xtype==etGENERIC && precision>0 ) precision--; +#if 0 + /* Rounding works like BSD when the constant 0.4999 is used. Wierd! */ + for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1); +#else + /* It makes more sense to use 0.5 */ + for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1){} +#endif + if( xtype==etFLOAT ) realvalue += rounder; + /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ + exp = 0; +#if 1 + if( (realvalue)!=(realvalue) ){ + /* from sqlite3: #define sqlite3_isnan(X) ((X)!=(X)) */ + /* This weird array thing is to avoid constness violations + when assinging, e.g. "NaN" to bufpt. + */ + static char NaN[4] = {'N','a','N','\0'}; + bufpt = NaN; + length = 3; + break; + } +#endif + if( realvalue>0.0 ){ + while( realvalue>=1e32 && exp<=350 ){ realvalue *= 1e-32; exp+=32; } + while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; } + while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; } + while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; } + while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; } + if( exp>350 || exp<-350 ){ + if( prefix=='-' ){ + static char Inf[5] = {'-','I','n','f','\0'}; + bufpt = Inf; + }else if( prefix=='+' ){ + static char Inf[5] = {'+','I','n','f','\0'}; + bufpt = Inf; + }else{ + static char Inf[4] = {'I','n','f','\0'}; + bufpt = Inf; + } + length = strlen(bufpt); + break; + } + } + bufpt = buf; + /* + If the field type is etGENERIC, then convert to either etEXP + or etFLOAT, as appropriate. + */ + flag_exp = xtype==etEXP; + if( xtype!=etFLOAT ){ + realvalue += rounder; + if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } + } + if( xtype==etGENERIC ){ + flag_rtz = !flag_alternateform; + if( exp<-4 || exp>precision ){ + xtype = etEXP; + }else{ + precision = precision - exp; + xtype = etFLOAT; + } + }else{ + flag_rtz = 0; + } + if( xtype==etEXP ){ + e2 = 0; + }else{ + e2 = exp; + } + nsd = 0; + flag_dp = (precision>0) | flag_alternateform | flag_altform2; + /* The sign in front of the number */ + if( prefix ){ + *(bufpt++) = prefix; + } + /* Digits prior to the decimal point */ + if( e2<0 ){ + *(bufpt++) = '0'; + }else{ + for(; e2>=0; e2--){ + *(bufpt++) = et_getdigit(&realvalue,&nsd); + } + } + /* The decimal point */ + if( flag_dp ){ + *(bufpt++) = '.'; + } + /* "0" digits after the decimal point but before the first + significant digit of the number */ + for(e2++; e2<0 && precision>0; precision--, e2++){ + *(bufpt++) = '0'; + } + /* Significant digits after the decimal point */ + while( (precision--)>0 ){ + *(bufpt++) = et_getdigit(&realvalue,&nsd); + } + /* Remove trailing zeros and the "." if no digits follow the "." */ + if( flag_rtz && flag_dp ){ + while( bufpt[-1]=='0' ) *(--bufpt) = 0; + /* assert( bufpt>buf ); */ + if( bufpt[-1]=='.' ){ + if( flag_altform2 ){ + *(bufpt++) = '0'; + }else{ + *(--bufpt) = 0; + } + } + } + /* Add the "eNNN" suffix */ + if( flag_exp || (xtype==etEXP && exp) ){ + *(bufpt++) = aDigits[infop->charset]; + if( exp<0 ){ + *(bufpt++) = '-'; exp = -exp; + }else{ + *(bufpt++) = '+'; + } + if( exp>=100 ){ + *(bufpt++) = (exp/100)+'0'; /* 100's digit */ + exp %= 100; + } + *(bufpt++) = exp/10+'0'; /* 10's digit */ + *(bufpt++) = exp%10+'0'; /* 1's digit */ + } + *bufpt = 0; + + /* The converted number is in buf[] and zero terminated. Output it. + Note that the number is in the usual order, not reversed as with + integer conversions. */ + length = bufpt-buf; + bufpt = buf; + + /* Special case: Add leading zeros if the flag_zeropad flag is + set and we are not left justified */ + if( flag_zeropad && !flag_leftjustify && length < width){ + int i; + int nPad = width - length; + for(i=width; i>=nPad; i--){ + bufpt[i] = bufpt[i-nPad]; + } + i = prefix!=0; + while( nPad-- ) bufpt[i++] = '0'; + length = width; + } +#endif /* !FSLPRINTF_OMIT_FLOATING_POINT */ + break; +#if !FSLPRINTF_OMIT_SIZE + case etSIZE: + *(va_arg(ap,int*)) = outCount; + length = width = 0; + break; +#endif + case etPERCENT: + buf[0] = '%'; + bufpt = buf; + length = 1; + break; + case etCHARLIT: + case etCHARX: + c = buf[0] = (xtype==etCHARX ? va_arg(ap,int) : *++fmt); + if( precision>=0 ){ + for(idx=1; idx=0 && precision0 ){ + FSLPRINTF_SPACES(nspace); + } + } + if( length>0 ){ + pfrc = pfAppend( pfAppendArg, bufpt, length); + FSLPRINTF_CHECKERR(0); + } + if( flag_leftjustify ){ + int nspace; + nspace = width-length; + if( nspace>0 ){ + FSLPRINTF_SPACES(nspace); + } + } + if( zExtra ){ + free(zExtra); + zExtra = 0; + } + }/* End for loop over the format string */ + FSLPRINTF_RETURN; +} /* End of function */ + + +#undef FSLPRINTF_SPACES +#undef FSLPRINTF_CHECKERR +#undef FSLPRINTF_RETURN +#undef FSLPRINTF_OMIT_FLOATING_POINT +#undef FSLPRINTF_OMIT_SIZE +#undef FSLPRINTF_OMIT_SQL +#undef FSLPRINTF_BUF_SIZE +#undef FSLPRINTF_OMIT_HTML + +long fsl_appendf(fsl_appendf_f pfAppend, /* Accumulate results here */ + void * pfAppendArg, /* Passed as first arg to pfAppend. */ + const char *fmt, /* Format string */ + ... ){ + long ret; + va_list vargs; + va_start( vargs, fmt ); + ret = fsl_appendfv( pfAppend, pfAppendArg, fmt, vargs ); + va_end(vargs); + return ret; +} + + +/* +** fsl_appendf_f() impl which requires that state be-a writable +** (FILE*). +*/ +static long fsl_appendf_f_FILE( void * state, + char const * s, long n ){ + if( !state ) return -1; + else return (1==fwrite( s, (size_t)n, 1, (FILE *)state )) + ? n : -2; +} + + +long fsl_appendf_FILE( FILE * fp, char const * fmt, ... ){ + if(!fp || !fmt) return -1; + else { + long ret; + va_list vargs; + va_start( vargs, fmt ); + ret = fsl_appendfv( fsl_appendf_f_FILE, fp, fmt, vargs ); + va_end(vargs); + return ret; + } +} + +char * fsl_mprintfv( char const * fmt, va_list vargs ){ + if( !fmt ) return 0; + else{ + fsl_buffer buf = fsl_buffer_empty; + int const rc = fsl_buffer_appendfv( &buf, fmt, vargs ); + if(rc){ + fsl_buffer_reserve(&buf, 0); + assert(0==buf.mem); + } + return (char*)buf.mem /*transfer ownership*/; + } +} + +char * fsl_mprintf( char const * fmt, ... ){ + char * ret; + va_list vargs; + va_start( vargs, fmt ); + ret = fsl_mprintfv( fmt, vargs ); + va_end( vargs ); + return ret; +} ADDED src/fsl_buffer.c Index: src/fsl_buffer.c ================================================================== --- /dev/null +++ src/fsl_buffer.c @@ -0,0 +1,416 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* +** 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 "fossil/fossil2.h" +#include +#include +#include /* strlen() */ +#include /* NULL on linux */ + +#include + +int fsl_buffer_reset( fsl_buffer * b ){ + if(!b) return FSL_RC_MISUSE; + else{ + if(b->capacity){ + assert(b->mem); + b->mem[0] = 0; + } + b->used = 0; + return 0; + } +} + + +int fsl_buffer_reserve( fsl_buffer * buf, fsl_size_t n ){ + if( ! buf ) return FSL_RC_MISUSE; + else if( 0 == n ){ + fsl_free(buf->mem); + *buf = fsl_buffer_empty; + return 0; + } + else if( buf->capacity >= n ){ + return 0; + } + else{ + unsigned char * x = (unsigned char *)fsl_realloc( buf->mem, n ); + if( ! x ) return FSL_RC_OOM; + memset( x + buf->used, 0, n - buf->used ); + buf->mem = x; + buf->capacity = n; + return 0; + } +} + +int fsl_buffer_resize( fsl_buffer * buf, fsl_size_t n ){ + if( !buf ) return FSL_RC_MISUSE; + else if(n && (buf->capacity == n+1)){ + buf->used = n; + buf->mem[n] = 0; + return 0; + } + else { + unsigned char * x = (unsigned char *)fsl_realloc( buf->mem, + n+1/*NUL*/ ); + if( ! x ) return FSL_RC_OOM; + if(n > buf->capacity){ + /* zero-fill new parts */ + memset( x + buf->capacity, 0, n - buf->capacity +1/*NUL*/ ); + } + buf->capacity = n + 1 /*NUL*/; + buf->used = n; + buf->mem = x; + buf->mem[buf->used] = 0; + return 0; + } +} + +int fsl_buffer_compare(fsl_buffer const * lhs, fsl_buffer const * rhs){ + fsl_size_t const szL = lhs->used; + fsl_size_t const szR = rhs->used; + fsl_size_t const sz = (szLmem, rhs->mem, sz); + if(0 == rc){ + rc = (szL==szR) + ? 0 + : ((szLused; + fsl_size_t const szR = rhs->used; + fsl_size_t i; + unsigned char const *buf1; + unsigned char const *buf2; + unsigned char rc = 0; + if( szL!=szR || szL==0 ) return 1; + buf1 = lhs->mem; + buf2 = rhs->mem; + for( i=0; iused + len + 1/*NUL*/; + rc = fsl_buffer_reserve( b, sz ); + if(rc) return rc; + else{ + assert(b->capacity >= sz); + memcpy(b->mem + b->used, data, (size_t)len); + b->used += len; + b->mem[b->used] = 0; + return 0; + } + } +} + +/* +** Internal helper for implementing fsl_buffer_appendf() +*/ +typedef struct BufferAppender { + fsl_buffer * b; + /* + ** Result code of the appending process. + */ + int rc; +} BufferAppender; + +/* +** fsl_appendf_f() impl which requires arg to be a (fsl_buffer*). +** It appends the data to arg. +*/ +static long fsl_appendf_f_buffer( void * arg, + char const * data, long n ){ + BufferAppender * ba = (BufferAppender*)arg; + fsl_buffer * sb = ba->b; + if( !sb || (n<0) ) return -1; + else if( ! n ) return 0; + else{ + long rc; + size_t npos = sb->used + n; + if( npos >= sb->capacity ){ + const size_t asz = npos ? ((4 * npos / 3) + 1) : 16; + if( asz < npos ) { + ba->rc = FSL_RC_RANGE; + return -1; /* overflow */ + } + else{ + rc = fsl_buffer_reserve( sb, asz ); + if(rc) { + ba->rc = FSL_RC_OOM; + return -1; + } + } + } + rc = 0; + for( ; rc < n; ++rc, ++sb->used ){ + sb->mem[sb->used] = data[rc]; + } + sb->mem[sb->used] = 0; + return rc; + } +} + +int fsl_buffer_appendfv( fsl_buffer * b, + char const * fmt, va_list args){ + if(!b || !fmt) return FSL_RC_MISUSE; + else{ + BufferAppender ba; + ba.b = b; + ba.rc = 0; + fsl_appendfv( fsl_appendf_f_buffer, &ba, fmt, args ); + return ba.rc; + } +} + +char const * fsl_buffer_cstr(fsl_buffer const *b){ + return b ? (char const *)b->mem : NULL; +} + +char * fsl_buffer_str(fsl_buffer *b){ + return b ? (char *)b->mem : NULL; +} + +int fsl_buffer_appendf( fsl_buffer * b, + char const * fmt, ... ){ + if(!b || !fmt) return FSL_RC_MISUSE; + else{ + int rc; + va_list args; + va_start(args,fmt); + rc = fsl_buffer_appendfv( b, fmt, args ); + va_end(args); + return rc; + } +} + +int fsl_buffer_compress(fsl_buffer const *pIn, fsl_buffer *pOut){ + unsigned int nIn = pIn->used; + unsigned int nOut = 13 + nIn + (nIn+999)/1000; + fsl_buffer temp = fsl_buffer_empty; + int rc = fsl_buffer_resize(&temp, nOut+4); + if(rc) return rc; + else{ + unsigned long int nOut2; + unsigned char *outBuf; + outBuf = temp.mem; + outBuf[0] = nIn>>24 & 0xff; + outBuf[1] = nIn>>16 & 0xff; + outBuf[2] = nIn>>8 & 0xff; + outBuf[3] = nIn & 0xff; + nOut2 = (long int)nOut; + rc = compress(&outBuf[4], &nOut2, + pIn->mem, pIn->used); + if(rc){ + fsl_buffer_reserve(&temp, 0); + return FSL_RC_ERROR; + } + fsl_buffer_reserve(pOut, 0); + *pOut = temp; + if(!rc){ + rc = fsl_buffer_resize(pOut, nOut2+4); + if(!rc){ + pOut->used = nOut2+4; + } + } + return rc; + } +} + +int fsl_buffer_compress2(fsl_buffer *pIn1, + fsl_buffer *pIn2, fsl_buffer *pOut){ + unsigned int nIn = pIn1->used + pIn2->used; + unsigned int nOut = 13 + nIn + (nIn+999)/1000; + fsl_buffer temp = fsl_buffer_empty; + int rc; + rc = fsl_buffer_resize(&temp, nOut+4); + if(rc) return rc; + else{ + unsigned char *outBuf; + z_stream stream; + outBuf = temp.mem; + outBuf[0] = nIn>>24 & 0xff; + outBuf[1] = nIn>>16 & 0xff; + outBuf[2] = nIn>>8 & 0xff; + outBuf[3] = nIn & 0xff; + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = 0; + stream.avail_out = nOut; + stream.next_out = &outBuf[4]; + deflateInit(&stream, 9); + stream.avail_in = pIn1->used; + stream.next_in = pIn1->mem; + deflate(&stream, 0); + stream.avail_in = pIn2->used; + stream.next_in = pIn2->mem; + deflate(&stream, 0); + deflate(&stream, Z_FINISH); + rc = fsl_buffer_resize(&temp, stream.total_out + 4); + deflateEnd(&stream); + if(!rc){ + temp.used = stream.total_out + 4; + if( pOut==pIn1 ) fsl_buffer_reserve(pOut, 0); + else if( pOut==pIn2 ) fsl_buffer_reserve(pOut, 0); + assert(!pOut->mem); + *pOut = temp; + }else{ + fsl_buffer_reserve(&temp, 0); + } + return rc; + } +} + +int fsl_buffer_uncompress(fsl_buffer const *pIn, fsl_buffer *pOut){ + unsigned int nOut; + unsigned char *inBuf; + unsigned int nIn = pIn->used; + fsl_buffer temp = fsl_buffer_empty; + int rc; + unsigned long int nOut2; + if( nIn<=4 ){ + return FSL_RC_RANGE; + } + inBuf = pIn->mem; + nOut = (inBuf[0]<<24) + (inBuf[1]<<16) + (inBuf[2]<<8) + inBuf[3]; + rc = fsl_buffer_reserve(&temp, nOut+1); + if(rc) return rc; + nOut2 = (long int)nOut; + rc = uncompress(temp.mem, + &nOut2, + &inBuf[4], + nIn - 4); + if( rc!=Z_OK ){ + fsl_buffer_reserve(&temp, 0); + return FSL_RC_ERROR; + } + rc = fsl_buffer_resize(&temp, nOut2); + if(!rc){ + temp.used = (fsl_size_t)nOut2; + if( pOut==pIn ){ + fsl_buffer_reserve(pOut, 0); + } + assert(!pOut->mem); + *pOut = temp; + }else{ + fsl_buffer_reserve(&temp, 0); + } + return rc; +} + + +int fsl_buffer_fill_from( fsl_buffer * dest, fsl_input_f src, void * state ) +{ + int rc; + enum { BufSize = 512 * 4 }; + char rbuf[BufSize]; + fsl_size_t total = 0; + fsl_size_t rlen = 0; + if( !dest || ! src ) return FSL_RC_MISUSE; + dest->used = 0; + while(1){ + rlen = BufSize; + rc = src( state, rbuf, &rlen ); + if( rc ) break; + total += rlen; + if(totalcapacity < (total+1) ){ + rc = fsl_buffer_reserve( dest, + total + ((rlenmem + dest->used, rbuf, rlen ); + dest->used += rlen; + if( rlen < BufSize ) break; + } + if( !rc && dest->used ){ + assert( dest->used < dest->capacity ); + dest->mem[dest->used] = 0; + } + return rc; +} + +int fsl_input_FILE( void * state, void * dest, fsl_size_t * n ){ + FILE * f = (FILE*) state; + if( ! state || ! n || !dest ) return FSL_RC_MISUSE; + else if( !*n ) return FSL_RC_RANGE; + *n = (fsl_size_t)fread( dest, 1, *n, f ); + return !*n + ? (feof(f) ? 0 : FSL_RC_IO) + : 0; +} + +int fsl_buffer_fill_from_FILE( fsl_buffer * dest, FILE * src ){ + return (!dest || !src) + ? FSL_RC_MISUSE + : fsl_buffer_fill_from( dest, fsl_input_FILE, src ); +} + + +int fsl_buffer_fill_from_filename( fsl_buffer * dest, char const * filename ){ + if(!dest || !filename || !*filename) return FSL_RC_MISUSE; + else{ + int rc; + FILE * src = (('-'==*filename)&&!*(filename+1)) ? stdin : fopen(filename,"rb"); + if(!src) return FSL_RC_IO; + rc = fsl_buffer_fill_from( dest, fsl_input_FILE, src ); + if(stdin!=src) fclose(src); + return rc; + } +} + +fsl_size_t fsl_strlen( char const * src ){ + fsl_size_t i = 0; + for( i = 0; src && *src; ++i ){} + return i; +} + +char * fsl_strdup( char const * src ){ + if(!src) return NULL; + else{ + fsl_buffer b = fsl_buffer_empty; + fsl_buffer_append( &b, src, fsl_strlen(src) ); + return (char*)b.mem; + } +} ADDED src/fsl_db.c Index: src/fsl_db.c ================================================================== --- /dev/null +++ src/fsl_db.c @@ -0,0 +1,337 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* +** 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 "fossil/fossil2.h" +#include +#include +#include /* NULL on linux */ + +/* +** +** Helper which sets fsl_err_set() to some string +** pulled from the db connection. dbCode should be +** the result code which came from an error'd +** sqlite API call. +*/ +static int fsl_err_repo_db( fsl_ctx * f, int dbCode ){ + assert(f && f->dbRepo.dbh); + return fsl_err_set(f, FSL_RC_DB, + "Db error #%d: %s", + dbCode, sqlite3_errmsg(f->dbRepo.dbh)); +} + +int fsl_db_close( fsl_ctx * f, fsl_db * db ){ + if(!f || !db) return FSL_RC_MISUSE; + else{ + if(db->dbh){ + sqlite3_close(db->dbh) + /* ignoring result in the style of + "destructors may not throw". + */; + } + assert(0==db->openStatementCount); + fsl_buffer_reserve(&db->filename, 0); + *db = fsl_db_empty; + return 0; + } +} + +int fsl_repo_close( fsl_ctx * f ){ + if(!f) return FSL_RC_MISUSE; + else if(!f->dbRepo.dbh) return FSL_RC_NOT_A_REPO; + else return fsl_db_close( f, &f->dbRepo ); +} + +/* +** This file contains the fsl_db_xxx() and fsl_stmt_xxx() +** parts of the API. +*/ +int fsl_stmt_preparev( fsl_db *db, fsl_stmt * tgt, char const * sql, va_list args ){ + if(!db || !db->dbh || !tgt || !sql) return FSL_RC_MISUSE; + else if(!*sql) return FSL_RC_RANGE; + else if(!tgt->stmt) return FSL_RC_ALREADY_EXISTS; + else{ + int rc; + fsl_buffer buf = fsl_buffer_empty; + fsl_stmt_t * liteStmt = NULL; + fsl_ctx * f = db->f; + rc = fsl_buffer_appendfv( &buf, sql, args ); + if(!rc){ + sql = fsl_buffer_cstr(&buf); + rc = sqlite3_prepare_v2(db->dbh, sql, (int)buf.used, + &liteStmt, 0); + if(rc){ + rc = fsl_err_set(f, rc, "Db statement preparation failed. " + "SQL: [%.*s]. Error #%d: %s\n", + (int)buf.used, sql, rc, + sqlite3_errmsg(db->dbh)); + } + } + if(!rc){ + ++db->openStatementCount; + tgt->stmt = liteStmt; + tgt->db = db; + tgt->sql = buf /*transfer ownership*/; + tgt->colCount = sqlite3_column_count(tgt->stmt); + tgt->paramCount = sqlite3_bind_parameter_count(tgt->stmt); + }else{ + assert(!liteStmt); + fsl_buffer_reserve(&buf, 0); + } + return rc; + } +} + +int fsl_stmt_prepare( fsl_db *db, fsl_stmt * tgt, char const * sql, ... ){ + if(!db || !db->dbh || !tgt || !sql) return FSL_RC_MISUSE; + else if(!*sql) return FSL_RC_RANGE; + else if(!tgt->stmt) return FSL_RC_ALREADY_EXISTS; + else{ + int rc; + va_list args; + va_start(args,sql); + rc = fsl_stmt_preparev( db, tgt, sql, args ); + va_end(args); + return rc; + } +} + +int fsl_repo_prepare( fsl_ctx *f, fsl_stmt * tgt, char const * sql, + ... ){ + if(!f) return FSL_RC_MISUSE; + else if(!f->dbRepo.dbh) return FSL_RC_NOT_A_REPO; + else{ + int rc; + va_list args; + va_start(args,sql); + rc = fsl_stmt_preparev( &f->dbRepo, tgt, sql, args ); + va_end(args); + return rc; + } +} + +int fsl_repo_preparev( fsl_ctx *f, fsl_stmt * tgt, char const * sql, + va_list args ){ + if(!f) return FSL_RC_MISUSE; + else if(!f->dbRepo.dbh) return FSL_RC_NOT_A_REPO; + else return fsl_stmt_preparev(&f->dbRepo, tgt, sql, args); +} + + +int fsl_stmt_finalize( fsl_stmt * stmt ){ + if(!stmt) return FSL_RC_MISUSE; + else{ + assert(stmt->f); + assert(stmt->stmt); + assert(stmt->db); + --stmt->db->openStatementCount; + fsl_buffer_reserve(&stmt->sql, 0); + sqlite3_finalize( stmt->stmt ); + *stmt = fsl_stmt_empty; + return 0; + } +} + +fsl_step_t fsl_stmt_step( fsl_stmt * stmt ){ + if(!stmt || !stmt->stmt) return FSL_RC_MISUSE; + else{ + int const rc = sqlite3_step(stmt->stmt); + assert(stmt->f); + switch( rc ){ + case SQLITE_ROW: + return FSL_STEP_ROW; + case SQLITE_DONE: + return FSL_STEP_DONE; + default: + fsl_err_repo_db( stmt->f, rc ); + return FSL_STEP_ERROR; + } + } +} + +int fsl_stmt_each( fsl_stmt * stmt, fsl_stmt_each_f callback, + void * callbackState ){ + if(!stmt || !callback) return FSL_RC_MISUSE; + else{ + fsl_step_t strc; + int rc = 0; + char doBreak = 0; + while( !doBreak && (FSL_STEP_ROW == (strc=fsl_stmt_step(stmt)))){ + rc = callback( stmt, callbackState ); + switch(rc){ + case 0: continue; + case FSL_RC_BREAK: + rc = 0; + /* fall through */ + default: + doBreak = 1; + break; + } + } + return rc + ? rc + : ((FSL_STEP_ERROR==strc) + ? FSL_RC_DB + : 0); + } +} + +int fsl_stmt_reset( fsl_stmt * stmt ){ + if(!stmt || !stmt->stmt) return FSL_RC_MISUSE; + else{ + int rc = sqlite3_reset(stmt->stmt); + return rc + ? fsl_err_repo_db(stmt->f, rc) + : 0; + } +} + +int fsl_stmt_col_count( fsl_stmt const * stmt ){ + return (!stmt || !stmt->stmt) + ? -1 + : stmt->colCount + ; +} + +int fsl_stmt_param_count( fsl_stmt const * stmt ){ + return (!stmt || !stmt->stmt) + ? -1 + : stmt->paramCount; +} + +int fsl_stmt_bind_null( fsl_stmt * stmt, int ndx ){ + if(!stmt || !stmt->stmt) return FSL_RC_MISUSE; + else{ + int const rc = sqlite3_bind_null( stmt->stmt, ndx ); + return rc ? fsl_err_repo_db(stmt->f, rc) : 0; + } +} + +int fsl_stmt_bind_int32( fsl_stmt * stmt, int ndx, fsl_int32_t v ){ + if(!stmt || !stmt->stmt) return FSL_RC_MISUSE; + else{ + int const rc = sqlite3_bind_int( stmt->stmt, ndx, (int)v ); + return rc ? fsl_err_repo_db(stmt->f, rc) : 0; + } +} + +int fsl_stmt_bind_int64( fsl_stmt * stmt, int ndx, fsl_int64_t v ){ + if(!stmt || !stmt->stmt) return FSL_RC_MISUSE; + else{ + int const rc = sqlite3_bind_int64( stmt->stmt, ndx, (sqlite3_int64)v ); + return rc ? fsl_err_repo_db(stmt->f, rc) : 0; + } +} + +int fsl_stmt_bind_double( fsl_stmt * stmt, int ndx, fsl_double_t v ){ + if(!stmt || !stmt->stmt) return FSL_RC_MISUSE; + else{ + int const rc = sqlite3_bind_double( stmt->stmt, ndx, (double)v ); + return rc ? fsl_err_repo_db(stmt->f, rc) : 0; + } +} + +#define GET_CHECK if(!stmt || !stmt->colCount) return FSL_RC_MISUSE; \ + else if((ndx<0) || (ndx>=stmt->colCount)) return FSL_RC_RANGE +int fsl_stmt_get_int32( fsl_stmt * stmt, int ndx, fsl_int32_t * v ){ + GET_CHECK; + else { + if(v) *v = (fsl_int32_t)sqlite3_column_int(stmt->stmt, ndx); + return 0; + } +} +int fsl_stmt_get_int64( fsl_stmt * stmt, int ndx, fsl_int64_t * v ){ + GET_CHECK; + else { + if(v) *v = (fsl_int64_t)sqlite3_column_int64(stmt->stmt, ndx); + return 0; + } +} + +int fsl_stmt_get_double( fsl_stmt * stmt, int ndx, fsl_double_t * v ){ + GET_CHECK; + else { + if(v) *v = (fsl_double_t)sqlite3_column_double(stmt->stmt, ndx); + return 0; + } +} + +int fsl_stmt_get_text( fsl_stmt * stmt, int ndx, char const **out, + fsl_int_t * outLen ){ + GET_CHECK; + else { + unsigned char const * t = (out || outLen) + ? sqlite3_column_text(stmt->stmt, ndx) + : NULL; + if(out) *out = (char const *)t; + if(outLen) *outLen = t ? sqlite3_column_bytes(stmt->stmt, ndx) : 0; + return 0; + } +} + +int fsl_stmt_get_blob( fsl_stmt * stmt, int ndx, void const **out, + fsl_int_t * outLen ){ + GET_CHECK; + else { + void const * t = (out || outLen) + ? sqlite3_column_blob(stmt->stmt, ndx) + : NULL; + if(out) *out = (char const *)t; + if(outLen) *outLen = t ? sqlite3_column_bytes(stmt->stmt, ndx) : 0; + return 0; + } +} + +#undef GET_CHECK + +int fsl_repo_open_db( fsl_ctx * f, char const * repoDbFile, ...){ + if(!f || !repoDbFile || !*repoDbFile) return FSL_RC_MISUSE; + else if(f->dbRepo.dbh) return FSL_RC_MISUSE; + else { + int rc = sqlite3_open_v2( repoDbFile, + &f->dbRepo.dbh, + SQLITE_OPEN_READWRITE, + NULL ); + if(rc){ + if(f->dbRepo.dbh){ + /* By some complete coincidence, FSL_RC_DB==SQLITE_CANTOPEN. */ + rc = fsl_err_set(f, FSL_RC_DB, + "Opening db file [%s] failed with " + "sqlite code #%d: %s", + repoDbFile, rc, sqlite3_errmsg(f->dbRepo.dbh)); + sqlite3_close(f->dbRepo.dbh); + f->dbRepo.dbh = NULL; + }else{ + rc = fsl_err_set(f, FSL_RC_DB, + "Opening db file [%s] failed with " + "sqlite code #%d", + repoDbFile, rc); + + } + rc = FSL_RC_DB; + } + f->dbRepo.f = f; + if(!rc){ + rc = fsl_buffer_append(&f->dbRepo.filename, repoDbFile, -1); + } + return rc; + } +} + ADDED src/fsl_fs.c Index: src/fsl_fs.c ================================================================== --- /dev/null +++ src/fsl_fs.c @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* +** 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 +#include +#include /* strlen() */ +#include /* NULL on linux */ +#include +#ifdef _WIN32 +# include +#else +# include /* access(2) */ +#endif + + + +#include "fossil/fossil2.h" +#include "fsl_internal.h" + +/* +** Wrapper around the access() system call. +*/ +int fsl_file_access(const char *zFilename, int flags){ +#ifdef _WIN32 + wchar_t *zMbcs = fsl_utf8_to_filename(zFilename); + int rc = _waccess(zMbcs, flags); +#else + char *zMbcs = (char*)fsl_utf8_to_filename(zFilename); + int rc = access(zMbcs, flags); +#endif + fsl_filename_free(zMbcs); + return rc; +} ADDED src/fsl_internal.h Index: src/fsl_internal.h ================================================================== --- /dev/null +++ src/fsl_internal.h @@ -0,0 +1,133 @@ +/* -*- 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_INTERNAL_H_INCLUDED) +#define NET_FOSSIL_SCM_FSL_INTERNAL_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 library-level internal APIs which are shared +** across the library. +*/ +#include /* FILE type */ + +#if defined(__cplusplus) && !defined(__STDC_FORMAT_MACROS) +/* inttypes.h needs this for the PRI* and SCN* macros in C++ mode. */ +# define __STDC_FORMAT_MACROS +#endif + +#include "fossil/fossil2.h" + +#if defined(__cplusplus) +extern "C" { +#endif + + /* + ** Quivalent to fopen(3) but expects name to be UTF8-encoded. + */ + FILE * fsl_fopen(char const * name, char const *mode); +#ifdef _WIN32 + /* + ** Translate MBCS to UTF-8. Return a pointer to the translated + ** text. ACHTUNG: Call fsl_mbcs_free() (not fsl_free()) to + ** deallocate any memory used to store the returned pointer when + ** done. + */ + char * fsl_mbcs_to_utf8(char const * mbcs); + + /* + ** Frees a string allocated from fsl_mbcs_to_utf8(). Results are undefined + ** if mbcs was allocated using any other mechanism. + */ + void fsl_mbcs_free(char * mbcs); +#endif + /* _WIN32 */ + + /* + ** Translate Unicode text into UTF-8. + ** Return a pointer to the translated text. + ** Call fsl_unicode_free() to deallocate any memory used to store the + ** returned pointer when done. + ** + ** This function exists only for Windows. On other platforms it + ** behaves like fsl_strdup(). + */ + char *fsl_unicode_to_utf8(const void *zUnicode); + + /* + ** Translate UTF-8 to unicode for use in system calls. Return a + ** pointer to the translated text. The returned value must + ** eventually be passed to fsl_free() to deallocate any memory used + ** to store the returned pointer when done. + ** + ** This function exists only for Windows. On other platforms + ** it behaves like fsl_strdup(). + ** + */ + void *fsl_utf8_to_unicode(const char *zUtf8); + + /* + ** Translate text from the filename character set into UTF-8. + ** Return a pointer to the translated text. Call + ** fsl_filename_free() to deallocate any memory used to store the + ** returned pointer when done. + ** + ** This function must not convert '\' to '/' on windows/cygwin, as it is + ** used in places where we are not sure it's really filenames we are handling, + ** e.g. fsl_getenv() or handling the argv arguments from main(). + ** + ** On Windows, translate some characters in the in the range + ** U+F001 - U+F07F (private use area) to ASCII. Cygwin sometimes + ** generates such filenames. See: + ** + */ + char *fsl_filename_to_utf8(const void *zFilename); + + /* + ** Translate text from UTF-8 to the filename character set. + ** Return a pointer to the translated text. + ** Call fossil_filename_free() to deallocate any memory used to store the + ** returned pointer when done. + ** + ** On Windows, characters in the range U+0001 to U+0031 and the + ** characters '"', '*', ':', '<', '>', '?' and '|' are invalid + ** to be used. Therefore, translate those to characters in the + ** in the range U+F001 - U+F07F (private use area), so those + ** characters never arrive in any Windows API. The filenames might + ** look strange in Windows explorer, but in the cygwin shell + ** everything looks as expected. + ** + ** See: + ** + */ + void *fsl_utf8_to_filename(const char *zUtf8); + + + /* + ** Deallocate pOld, which must have been allocated by + ** fsl_filename_to_utf8() or fsl_utf8_to_filename(). + */ + void fsl_filename_free(void *pOld); + + /* + ** Returns true (non-0) if ch is-a alpha character. + */ + char fsl_isalpha(int ch); + +#if defined(__cplusplus) +} /*extern "C"*/ +#endif +#endif +/* NET_FOSSIL_SCM_FSL_INTERNAL_H_INCLUDED */ ADDED src/fsl_utf8.c Index: src/fsl_utf8.c ================================================================== --- /dev/null +++ src/fsl_utf8.c @@ -0,0 +1,217 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* +** 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 "fossil/fossil2.h" +#include "fsl_internal.h" + +#include +#include +#include /* strlen() */ +#include /* NULL on linux */ +#include + +#ifdef _WIN32 +# include +#endif +#ifdef __CYGWIN__ +# include +# define CP_UTF8 65001 + __declspec(dllimport) extern __stdcall int WideCharToMultiByte(int, int, + const char *, int, const char *, int, const char *, const char *); + __declspec(dllimport) extern __stdcall int MultiByteToWideChar(int, int, + const char *, int, wchar_t*, int); +#endif + +#ifdef _WIN32 +char *fossil_mbcs_to_utf8(const char *zMbcs){ + extern char *sqlite3_win32_mbcs_to_utf8(const char*); + return sqlite3_win32_mbcs_to_utf8(zMbcs); +} + +void fossil_mbcs_free(char *zOld){ + sqlite3_free(zOld); +} +#endif /* _WIN32 */ + +char *fsl_unicode_to_utf8(const void *zUnicode){ +#if defined(_WIN32) || defined(__CYGWIN__) + int nByte = WideCharToMultiByte(CP_UTF8, 0, zUnicode, -1, 0, 0, 0, 0); + char *zUtf = fsl_malloc( nByte ); + if( zUtf==0 ){ + return 0; + } + WideCharToMultiByte(CP_UTF8, 0, zUnicode, -1, zUtf, nByte, 0, 0); + return zUtf; +#else + return fsl_strdup(zUnicode); /* TODO: implement for unix */ +#endif +} + +void *fsl_utf8_to_unicode(const char *zUtf8){ +#if defined(_WIN32) || defined(__CYGWIN__) + int nByte = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0); + wchar_t *zUnicode = fsl_malloc( nByte * 2 ); + if( zUnicode==0 ){ + return 0; + } + MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nByte); + return zUnicode; +#else + return fsl_strdup(zUtf8); /* TODO: implement for unix */ +#endif +} + + +void fsl_filename_free(void *pOld){ +#if defined(_WIN32) + sqlite3_free(pOld); +#elif (defined(__APPLE__) && !defined(WITHOUT_ICONV)) || defined(__CYGWIN__) + fsl_free(pOld); +#else + /* No-op on all other unix */ +#endif +} + + +#if defined(__APPLE__) && !defined(WITHOUT_ICONV) +# include +#endif + +char *fsl_filename_to_utf8(const void *zFilename){ +#if defined(_WIN32) + int nByte = WideCharToMultiByte(CP_UTF8, 0, zFilename, -1, 0, 0, 0, 0); + char *zUtf = fsl_malloc( nByte ); + char *pUtf, *qUtf; + if( zUtf==0 ){ + return 0; + } + WideCharToMultiByte(CP_UTF8, 0, zFilename, -1, zUtf, nByte, 0, 0); + pUtf = qUtf = zUtf; + while( *pUtf ) { + if( *pUtf == (char)0xef ){ + wchar_t c = ((pUtf[1]&0x3f)<<6)|(pUtf[2]&0x3f); + /* Only really convert it when the resulting char is in range. */ + if ( c && ((c < ' ') || wcschr(L"\"*:<>?|", c)) ){ + *qUtf++ = c; pUtf+=3; continue; + } + } + *qUtf++ = *pUtf++; + } + *qUtf = 0; + return zUtf; +#elif defined(__CYGWIN__) + char *zOut; + zOut = fsl_strdup(zFilename); + return zOut; +#elif defined(__APPLE__) && !defined(WITHOUT_ICONV) + char *zIn = (char*)zFilename; + char *zOut; + iconv_t cd; + size_t n, x; + for(n=0; zIn[n]>0 && zIn[n]<=0x7f; n++){} + if( zIn[n]!=0 && (cd = iconv_open("UTF-8", "UTF-8-MAC"))!=(iconv_t)-1 ){ + char *zOutx; + char *zOrig = zIn; + size_t nIn, nOutx; + nIn = n = strlen(zIn); + nOutx = nIn+100; + zOutx = zOut = fsl_malloc( nOutx+1 ); + if(!zOutx) return NULL; + x = iconv(cd, &zIn, &nIn, &zOutx, &nOutx); + if( x==(size_t)-1 ){ + fsl_free(zOut); + zOut = fsl_strdup(zOrig); + }else{ + zOut[n+100-nOutx] = 0; + } + iconv_close(cd); + }else{ + zOut = fossil_strdup(zFilename); + } + return zOut; +#else + return (char *)zFilename; /* No-op on non-mac unix */ +#endif +} + +char fsl_isalpha(int ch){ + return isalpha(ch) ? 1 : 0; +} + +void *fsl_utf8_to_filename(const char *zUtf8){ +#ifdef _WIN32 + int nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0); + wchar_t *zUnicode = fsl_malloc( nChar * 2 ); + wchar_t *wUnicode = zUnicode; + if( zUnicode==0 ){ + return 0; + } + MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nChar); + /* If path starts with ":/" or ":\", don't translate the ':' */ + if( fsl_isalpha(zUtf8[0]) && zUtf8[1]==':' + && (zUtf8[2]=='\\' || zUtf8[2]=='/')) { + zUnicode[2] = '\\'; + wUnicode += 3; + } + while( *wUnicode != '\0' ){ + if ( (*wUnicode < ' ') || wcschr(L"\"*:<>?|", *wUnicode) ){ + *wUnicode |= 0xF000; + }else if( *wUnicode == '/' ){ + *wUnicode = '\\'; + } + ++wUnicode; + } + return zUnicode; +#elif defined(__CYGWIN__) + char *zPath, *p; + if( fsl_isalpha(zUtf8[0]) && (zUtf8[1]==':') + && (zUtf8[2]=='\\' || zUtf8[2]=='/')) { + /* win32 absolute path starting with drive specifier. */ + int nByte; + wchar_t zUnicode[2000]; + wchar_t *wUnicode = zUnicode; + MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, count(zUnicode)); + while( *wUnicode != '\0' ){ + if( *wUnicode == '/' ){ + *wUnicode = '\\'; + } + ++wUnicode; + } + nByte = cygwin_conv_path(CCP_WIN_W_TO_POSIX, zUnicode, NULL, 0); + zPath = fsl_malloc(nByte); + if(!zPath) return NULL; + cygwin_conv_path(CCP_WIN_W_TO_POSIX, zUnicode, zPath, nByte); + }else{ + zPath = fsl_strdup(zUtf8); + if(!zPath) return NULL; + zUtf8 = p = zPath; + while( (*p = *zUtf8++) != 0){ + if( *p++ == '\\' ) { + p[-1] = '/'; + } + } + } + return zPath; +#elif defined(__APPLE__) && !defined(WITHOUT_ICONV) + return fossil_strdup(zUtf8); +#else + return (void *)zUtf8; /* No-op on unix */ +#endif +} ADDED src/include/fossil/fossil2.h Index: src/include/fossil/fossil2.h ================================================================== --- /dev/null +++ src/include/fossil/fossil2.h @@ -0,0 +1,1605 @@ +/* -*- 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 */ + +/* +** This file sketches out a potential API for a library form of Fossil +** v2. 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 :). +** +** 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). When adding code snippets and whatnot to docs, please use +** doxygen conventions if it is not too much of an inconvenience. +** +** - 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!). +*/ + +#if defined(__cplusplus) && !defined(__STDC_FORMAT_MACROS) +/* inttypes.h needs this for the PRI* and SCN* macros in C++ mode. */ +# define __STDC_FORMAT_MACROS +#endif +#include /* C99! */ +#include /* C99! */ +#include /* va_list */ + +#if defined(__cplusplus) +extern "C" { +#endif + +#define FSL_LIBRARY_VERSION "0.0.1-pre-alphalpha" + + typedef uint64_t fsl_size_t; + typedef int32_t fsl_int32_t; + typedef uint32_t fsl_uint32_t; + typedef int64_t fsl_int64_t; + typedef uint64_t fsl_uint64_t; + typedef fsl_int64_t fsl_int_t; + typedef fsl_uint64_t fsl_uint_t; + typedef double fsl_double_t; + typedef fsl_int64_t fsl_id_t; +#define FSL_SIZE_T_PFMT PRIu64 +#define FSL_SIZE_T_SFMT SCNu64 +#define FSL_INT_T_PFMT PRIi64 +#define FSL_INT_T_SFMT SCNi64 +#define FSL_UINT_T_PFMT PRIu64 +#define FSL_UINT_T_SFMT SCNu64 +#define FSL_ID_T_PFMT PRIi64 +#define FSL_ID_T_SFMT SCNi64 + /* + ** The type used to represent type values. Unless noted otherwise, + ** the general convention is "Unix + milliseconds," or ((Unix Epoch + ** x 1000)+milliseconds). + */ + typedef fsl_uint64_t /*int64 instead of uint64?*/ fsl_time_t; + + typedef struct fsl_outputer fsl_outputer; + typedef struct fsl_ctx fsl_ctx; + typedef struct fsl_allocator fsl_allocator; + typedef struct fsl_db fsl_db; + typedef struct fsl_buffer fsl_buffer; + typedef struct fsl_error fsl_error; + typedef struct fsl_state fsl_state; + + /* + ** 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. + */ + enum fsl_rc_t { + /* + ** The quintessential non-error value. + */ + FSL_RC_OK = 0, + /* + ** A placeholder return value for "not yet implemented" functions. + */ + FSL_RC_NYI, + /* + ** Generic/unknown error. + */ + FSL_RC_ERROR, + /* + ** Out of memory. Indicates that a resource allocation request + ** failed. + */ + FSL_RC_OOM, + /* + ** API misuse (invalid args) + */ + FSL_RC_MISUSE, + /* + ** Some range was violated (function argument, UTF character, etc.). + */ + FSL_RC_RANGE, + /* + ** Indicates that access to or locking of a resource was denied + ** by some security mechanism or other. + */ + FSL_RC_ACCESS, + /* + ** Indicates an I/O error. Whether it was reading or writing is + ** context-dependent. + */ + FSL_RC_IO, + /* + ** requested resource not found + */ + FSL_RC_NOT_FOUND, + /* + ** Immutable resource already exists + */ + FSL_RC_ALREADY_EXISTS, + /* + ** Data consistency problem + */ + FSL_RC_CONSISTENCY, + + /* + ** Indicates that the requested repo needs to be rebuilt. + */ + FSL_RC_REPO_NEEDS_REBUILD, + + /* + ** 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, + /* + ** Tried to load a too-old or too-new repo + */ + FSL_RC_REPO_VERSION, + /* + ** db-level error (e.g. statement prep failed) + */ + FSL_RC_DB, + /* + ** Used by some iteration routines to indicate that iteration should + ** stop prematurely without an error. + */ + FSL_RC_BREAK, + /* ...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. + */ + char const * fsl_rc_cstr(int); + + /* + ** Returns the value of FSL_LIBRARY_VERSION used to compile + ** this code. + */ + char const * fsl_library_version(); + + /* + ** 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 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); + + /* + ** fsl_flush_f() impl which expects _FILE to be-a (FILE*), + ** which this function passes to fflush(). If fflush() returns 0, + ** so does this function, else it returns FSL_RC_IO. + */ + int fsl_flush_f_FILE(void * _FILE); + + /* + ** 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 ); + + /* + ** fsl_output_f() impl which requires state to be-a (FILE*), + ** which this function passes to fwrite(). Returns 0 on succes, + ** FSL_RC_IO on error. + */ + int fsl_output_f_FILE( void * state, void const * src, fsl_size_t n ); + /* + ** Performs output on behalf of a fsl_ctx instance. Why? So that + ** we can do interesting things like output to buffers, files, + ** sockets, etc.. Real-life example of this model in action. + */ + 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} \ + }\ + } + + /* + ** General-purpose buffer, analog to Fossil v1's Blob class. It is + ** not called fsl_blob to avoid confusion with DB-side Blobs. + */ + 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. + */ + 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; + + /* + ** For storing a combination of error message and code. + */ + struct fsl_error { + /* + ** Error message text. It is 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} + + /* + ** Placeholder for sqlite3/4 type. + */ + typedef struct sqlite3 sqlite3; + typedef sqlite3 fsl_dbh_t; + + /* + ** Db handle wrapper/helper. + */ + struct fsl_db { + /* + ** Fossil Context on whose behalf this instance is operating. + */ + fsl_ctx * f; + /* + ** Underlying db driver handle. + */ + fsl_dbh_t * dbh; + /* + ** Not currently used. + */ + fsl_error error; + /* + ** Holds the file name used when opening this db. + */ + fsl_buffer filename; + /* + ** Debugging/test counter. + */ + int openStatementCount; + }; + /** Empty-initialized fsl_db instance. */ +#define fsl_db_empty_m {\ + NULL/*f*/, \ + NULL/*dbh*/,\ + fsl_error_empty_m /*error*/, \ + fsl_buffer_empty_m/*filename*/,\ + 0/*openStatementCount*/ \ +} + + /** Empty-initialized fsl_db instance. */ + extern const fsl_db fsl_db_empty; + /* + ** 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; + + + /* + ** The main Fossil "context" type. This is the first argument to + ** most Fossil v2 API routines. + ** + ** 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_ctx * f = NULL; + ** rc = fsl_init( &f, NULL ); + ** assert(!rc); + ** rc = fsl_repo_open_db( f, "myrepo.fsl" ); + ** ... + ** fsl_finalize(f); + ** @endcode + */ + struct fsl_ctx { + /* + ** Current repository db. + */ + fsl_db dbRepo; + /* + ** Output channel used by fsl_output() and friends. + */ + fsl_outputer output; + /* + ** Can be used to tie client-specific data to the context. Its + ** finalizer is called when fsl_finalize() cleans up. + */ + fsl_state clientState; + /* + ** Holds error state. As a general rule, this information is + ** updated only by routines which need to return more info than a + ** simple integer error string. This is primarily db-related + ** routines, where we add the db-driver-provided error state + ** here. It is not used by "simple" routines for which an integer + ** code always suffices. APIs which set this should denote it + ** with a comment like "sets the context's error state on error." + */ + fsl_error error; + /* + ** Optimization: reusable scratchpad for creating strings. + */ + fsl_buffer scratch; + + /* + ** The directory part + */ + fsl_buffer repoDir; + + /* no state related to server/user/etc. That is higher-level stuff. */ + }; + + /** Initialized-with-defaults fsl_ctx instance. */ +#define fsl_ctx_empty_m { fsl_db_empty_m /*dbRepo*/, \ + fsl_outputer_FILE_m /*output*/, \ + fsl_state_empty_m /*clientState*/, \ + fsl_error_empty_m /*error*/, \ + fsl_buffer_empty_m /*scratch*/ \ + } + + /** Initialized-with-defaults fsl_ctx instance. */ + extern const fsl_ctx fsl_ctx_empty; + + /* + ** 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_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 context which prepared this statement. + */ + fsl_ctx *f; + /* + ** 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; + }; + typedef struct fsl_stmt fsl_stmt; + /* + ** 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_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 (which + ** will normally be stack-allocated. + */ + int fsl_stmt_finalize( fsl_stmt * stmt ); + + /* + ** Result codes for use with fsl_stmt_step(). + */ + enum fsl_step_t { + /* + ** Indicates that a row has been fetched and the cursor may be used + ** to access the current row state. + */ + FSL_STEP_ROW = 1, + /* + ** 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_STEP_DONE, + /* + ** Indicates that a db-level error occurred during iteration. + ** fsl_err_get() should contain more info about the problem. + */ + FSL_STEP_ERROR + }; + typedef enum fsl_step_t fsl_step_t; + fsl_step_t 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. + */ + typedef int (*fsl_stmt_each_f)( fsl_stmt * stmt, void * state ); + /* + ** 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(). + */ + int fsl_stmt_reset( fsl_stmt * stmt ); + + /* + ** Returns the column count for the given statement, or -1 + ** if !stmt or it has not been prepared. + */ + 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. + */ + int fsl_stmt_param_count( fsl_stmt const * stmt ); + + /* + ** Binds NULL to the given 1-based parameter index. + ** Returns 0 on succcess. Sets the Fossil context'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 Fossil context'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 Fossil context's error state on error. + */ + int fsl_stmt_bind_int64( fsl_stmt * stmt, int index, fsl_int64_t v ); + + + /* + ** TODO. + ** + ** Binds the first n bytes of v as text to the given 1-based bound + ** parameter column in the given statement. If isStatic is true, it + ** avoids making an extra copy of v. + */ + int fsl_stmt_bind_text( fsl_stmt * stmt, int index, + char const * v, fsl_int_t n, + char isStatic ); + /* + ** TODO. + ** + ** Binds the first n bytes of v as a blob to the given 1-based bound + ** parameter column in the given statement. If isStatic is true, it + ** avoids making an extra copy of v. + */ + int fsl_stmt_bind_blob( fsl_stmt * stmt, int index, + void const * v, fsl_int_t len, + char isStatic ); + + + /* + ** 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 ); + + /* + ** 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. + ** + ** 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 ); + + /* + ** Gets a blob 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. + ** + ** 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 ); + + int fsl_db_exec( fsl_ctx * f, char const * sql, ... ); + int fsl_db_execv( fsl_ctx * f, char const * sql, va_list args ); + int fsl_db_get_int32( fsl_ctx * f, fsl_int32_t * stmt, fsl_int32_t defaultValue, char const * sql, ... ); + int fsl_db_get_int64( fsl_ctx * f, fsl_int64_t * stmt, fsl_int64_t defaultValue, char const * sql, ... ); + int fsl_db_get_text( fsl_ctx * f, char ** stmt, fsl_size_t * stmtLen, + char const * sql, ... ); + int fsl_db_get_int32( fsl_ctx * f, fsl_int32_t * stmt, fsl_int32_t defaultValue, char const * sql, ... ); + int fsl_db_get_int64( fsl_ctx * f, fsl_int64_t * stmt, fsl_int64_t defaultValue, char const * sql, ... ); + int fsl_db_get_blob( fsl_ctx * f, void ** stmt, fsl_size_t * stmtLen, + char const * sql, ... ); + int fsl_db_get_buffer( fsl_ctx * f, fsl_buffer * stmt, + char const * sql, ... ); + int fsl_db_step_query( fsl_ctx * f, fsl_stmt_each_f callback, + void * callbackState, char const * sql, ... ); + int fsl_db_step_queryv( fsl_ctx * f, fsl_stmt_each_f callback, + void * callbackState, char const * sql, va_list args ); + + /** + Parameters for fsl_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_ctx, 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_ctx 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. + */ + }; + 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_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} + + /** 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_init() is passed a NULL value for this parameter. + */ + extern const fsl_init_param fsl_init_param_default; + + /* + ** Initializes a fsl_ctx instance. tgt must be a pointer to NULL, + ** e.g.: + ** + ** @code + ** fsl_cxt * f = NULL; + ** int rc = fsl_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_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_init( fsl_ctx ** tgt, fsl_init_param * param ); + + /* + ** Frees all memory associated with f, which must have been + ** initialized using fsl_init() (or equivalent). + ** + ** Returns FSL_RC_MISUSE if !f, else 0. + */ + int fsl_finalize( fsl_ctx * 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_err_set( fsl_ctx * f, int code, char const * fmt, + ... ); + + /** va_list counterpart to fsl_err_set(). */ + int fsl_err_setv( fsl_ctx * f, int code, char const * fmt, + va_list args ); + + /* + ** Fetches the error state set using fsl_err_set(). If !f it + ** returns FSL_RC_MISUSE without side-effects, else it returns f'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). If len is not NULL then *len will hold the length + ** of the string (in bytes). The memory for the string is owned by + ** f and may be invalidated by any calls which take f as a + ** parameter, so the client is required to copy it if it is needed + ** for later on. + */ + int fsl_err_get( fsl_ctx * 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 (f, 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 (f, 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 !f or + ** !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. Returns 0 on success, + ** FSL_RC_MISUSE if !buf. + */ + 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->used to n. + ** + ** - On success it always NUL-terminates the buffer at + ** offset buf->used. + ** + ** Returns 0 on success, FSL_RC_MISUSE if !f or !buf, FSL_RC_OOM if + ** (re)allocation fails. After success buf->capacity might not be + ** _exactly_ n, as this routine allocates one extra byte to ensure + ** that buf is always NUL-terminated. + */ + int fsl_buffer_resize( fsl_buffer * buf, fsl_size_t n ); + + /* + ** Appends the first n bytes of src to b, expanding b as + ** necessary. If n is less than 0 then the equivalent of + ** strlen((char const*)src) is used. + ** + ** 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. + ** + ** 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 give 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!=pIn1then 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); + + /* + ** 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); + + /* + ** 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 *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 + ** (returning a negative value). + */ + int fsl_buffer_compare(fsl_buffer const * lhs, fsl_buffer const * rhs); + + /* + ** Compare two buffers in constant 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_constant_time(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(f,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_ctx * 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_ctx * f, char const * fmt, ... ); + + /** va_list counterpart to fsl_outputf(). */ + int fsl_outputfv( fsl_ctx * 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 ); + + /* + ** TODO. + ** + ** Should initialize f for being used with an opened repo found in + ** (or above) the given directory. + ** + ** Note that this is a lower level operation than the fossil + ** binary's 'open' command, and simply initialized f's db-related + ** resources. It will/could/maybe should look under the given dir + ** for an opened repo (analog to how the v1 binary does). + */ + int fsl_repo_open_dir( fsl_ctx * f, char const * repoDir, int flags /*???*/ ); + + /* + ** If fsl_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. + */ + int fsl_repo_close( fsl_ctx * f ); + + /* + ** Convenience form of fsl_stmt_prepare() which uses f's opened + ** repository db. Returns 0 on success, FSL_RC_MISUSE if !f or !sql, + ** FSL_RC_RANGE if !*sql, FSL_RC_NOT_A_REPO if fsl_repo_open_db() or + ** similar has not been used to open a repository. + */ + int fsl_repo_prepare( fsl_ctx *f, fsl_stmt * tgt, char const * sql, ... ); + + /* + ** va_list equivalent of fsl_repo_prepare(). + */ + int fsl_repo_preparev( fsl_ctx *f, fsl_stmt * tgt, char const * sql, va_list args ); + + /* + ** @internal + ** + ** Closes the given db. + */ + int fsl_db_close( fsl_ctx * f, fsl_db * db ); + + /* + ** Opens a db handle in the context of f. 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, or if + ** f already has an opened db. + ** + ** FIXME: we will possibly eventually need multiple DB handles per + ** context. If so, make fsl_db a linked list and manage them that + ** way, closing them in reverse-opened order in fsl_finalize(). Note + ** that this function is intended for a repo db, not a config db or + ** some such. + ** + ** Note that this is a lower level operation than the fossil + ** binary's 'open' command, and simply initialized f's db-related + ** resources. + */ + int fsl_repo_open_db( fsl_ctx * f, char const * repoDbFile, ... /*???*/ ); + + /* + ** 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_repo_create_db( fsl_ctx * f, char const * repoDbFile, int flags /*???*/ ); + + /** + @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, as far as i'm aware.) + + + CURRENT (documented) PRINTF EXTENSIONS: + + %%z works like %%s, but takes a non-const (char *) and vappendf + deletes the string (using free()) after appending it to the output. + + %%h (HTML) works like %s but converts certain characters (like '<' 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. + + (The %%q and %%Q specifiers are options inherited from this printf + implementation's sqlite3 genes.) + + These extensions may be disabled by setting certain macros when + compiling vappendf.c (see that file for details). + */ + long fsl_appendfv( + fsl_appendf_f pfAppend, /* Accumulate results here */ + void * pfAppendArg, /* Passed as first arg to pfAppend. */ + const char *fmt, /* Format string */ + va_list ap /* arguments */ + ); + + /** + 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(). + */ + 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 null-terminated + string is owned by the caller and it must be cleaned up using + fsl_free(f,...). If !fmt or if the expanded string evaluates to + empty, null is returned, not a 0-byte string. + */ + char * fsl_mprintf( char const * fmt, ... ); + + /** + va_list counterpart to fsl_mprintf(). + */ + char * fsl_mprintfv(char const * fmt, va_list vargs ); + + +#if defined(__cplusplus) +} /*extern "C"*/ +#endif +#endif +/* NET_FOSSIL_SCM_FOSSIL2_H_INCLUDED */ ADDED src/test.c Index: src/test.c ================================================================== --- /dev/null +++ src/test.c @@ -0,0 +1,168 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* +** 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/ +** +******************************************************************************* +** +*/ + +/* Force assert() to always work... */ +#if defined(NDEBUG) +#undef NDEBUG +#define DEBUG 1 +#endif + +#include +#include /* atexit() */ +#include /* strlen() */ +#include "fossil/fossil2.h" + +#define MARKER(pfexp) \ + do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ + printf pfexp; \ + } while(0) + +#define VERBOSE(pfexp) \ + if(App.verbose) do { \ + printf("VERBOSE: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ + printf pfexp; \ + } while(0) + +static struct _App { + fsl_ctx * f; + char const * name; + char const * dbFile; + char verbose; +} App = { +NULL/*f*/, +NULL/*name*/, +NULL/*dbFile*/, +0/*verbose*/ +}; + + +static int test_buffer_0(){ + fsl_buffer buf = fsl_buffer_empty; + int rc; + fsl_ctx * f = App.f; + fsl_size_t sz, szOrig; + char const * infile = 0 + ? __FILE__ + : "fsl_buffer.c"; + assert(f); + rc = fsl_buffer_fill_from_filename(&buf, infile); + assert(!rc); + sz = szOrig = buf.used; + rc = fsl_buffer_compress( &buf, &buf ); + assert(!rc); + assert(buf.used < sz); + MARKER(("Compressed [%s]. Size: %"FSL_SIZE_T_PFMT + " => %"FSL_SIZE_T_PFMT"\n", infile, szOrig, buf.used)); + sz = buf.used; + rc = fsl_buffer_uncompress(&buf, &buf); + assert(!rc); + MARKER(("Uncompressed [%s]. Size: %"FSL_SIZE_T_PFMT + " => %"FSL_SIZE_T_PFMT"\n", infile, sz, buf.used)); + assert(szOrig == buf.used); + rc = fsl_buffer_reserve(&buf, 0); + assert(!rc); + return rc; +} + +static int test0(){ + fsl_ctx * f = NULL; + int rc; +#if 0 + fsl_init_param init = fsl_init_param_empty; + init.output = fsl_outputer_FILE; + init.output.state.state = stdout; + init.allocator = fsl_allocator_stdalloc; + rc = fsl_init( &f, &init ); +#elif 0 + fsl_init_param init = fsl_init_param_default; + rc = fsl_init( &f, &init ); +#else + rc = fsl_init( &f, NULL ); +#endif + assert(!rc); + App.f = f; + VERBOSE(("Initialized fsl @%p\n", (void const *)f)); + + if(App.dbFile){ + VERBOSE(("Trying to open db file [%s]...\n", App.dbFile)); + rc = fsl_repo_open_db( f, App.dbFile ); + if(rc) return rc; + VERBOSE(("Opened db file [%s]\n", + fsl_buffer_cstr(&f->dbRepo.filename))); + } + return 0; +} + +static void my_atexit(){ + if(App.f){ + int rc; + VERBOSE(("Finalizing fsl @%p\n", (void const *)App.f)); + rc = fsl_finalize( App.f ); + assert(!rc); + App.f = 0; + } +} + +static int process_argv( int argc, char * const * argv ){ + int i; + for( i = 1; i < argc; ++i ){ + char const * arg = argv[i]; +#define ARG(F) if(0==strcmp(F,arg)) + ARG("-r") { + App.dbFile = argv[++i]; + continue; + } + ARG("-v") { + App.verbose = 1; + continue; + } + else if('-' == *arg){ + MARKER(("Unknown flag: %s\n", arg)); + return FSL_RC_MISUSE; + } +#undef ARG + } + return 0; +} + +int main(int argc, char * const * argv ){ + int rc; + rc = process_argv( argc, argv ); + if(rc) goto end; + atexit( my_atexit ); + rc = test0(); + if(!rc) rc = test_buffer_0(); + + end: + MARKER(("Done! rc=%d (%s)\n", rc, fsl_rc_cstr(rc))); + if(rc && App.f){ + char const * msg = NULL; + int errRc = fsl_err_get( App.f, &msg, NULL ); + if(msg){ + MARKER(("Fossil says: error code #%d (%s): %s\n", + errRc, fsl_rc_cstr(errRc), msg)); + } + } + return rc; +} + +#undef MARKER +#undef VERBOSE DELETED test.c Index: test.c ================================================================== --- test.c +++ /dev/null @@ -1,168 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* -** 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/ -** -******************************************************************************* -** -*/ - -/* Force assert() to always work... */ -#if defined(NDEBUG) -#undef NDEBUG -#define DEBUG 1 -#endif - -#include -#include /* atexit() */ -#include /* strlen() */ -#include "fossil/fossil2.h" - -#define MARKER(pfexp) \ - do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ - printf pfexp; \ - } while(0) - -#define VERBOSE(pfexp) \ - if(App.verbose) do { \ - printf("VERBOSE: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ - printf pfexp; \ - } while(0) - -static struct _App { - fsl_ctx * f; - char const * name; - char const * dbFile; - char verbose; -} App = { -NULL/*f*/, -NULL/*name*/, -NULL/*dbFile*/, -0/*verbose*/ -}; - - -static int test_buffer_0(){ - fsl_buffer buf = fsl_buffer_empty; - int rc; - fsl_ctx * f = App.f; - fsl_size_t sz, szOrig; - char const * infile = 0 - ? __FILE__ - : "fsl_buffer.c"; - assert(f); - rc = fsl_buffer_fill_from_filename(&buf, infile); - assert(!rc); - sz = szOrig = buf.used; - rc = fsl_buffer_compress( &buf, &buf ); - assert(!rc); - assert(buf.used < sz); - MARKER(("Compressed [%s]. Size: %"FSL_SIZE_T_PFMT - " => %"FSL_SIZE_T_PFMT"\n", infile, szOrig, buf.used)); - sz = buf.used; - rc = fsl_buffer_uncompress(&buf, &buf); - assert(!rc); - MARKER(("Uncompressed [%s]. Size: %"FSL_SIZE_T_PFMT - " => %"FSL_SIZE_T_PFMT"\n", infile, sz, buf.used)); - assert(szOrig == buf.used); - rc = fsl_buffer_reserve(&buf, 0); - assert(!rc); - return rc; -} - -static int test0(){ - fsl_ctx * f = NULL; - int rc; -#if 0 - fsl_init_param init = fsl_init_param_empty; - init.output = fsl_outputer_FILE; - init.output.state.state = stdout; - init.allocator = fsl_allocator_stdalloc; - rc = fsl_init( &f, &init ); -#elif 0 - fsl_init_param init = fsl_init_param_default; - rc = fsl_init( &f, &init ); -#else - rc = fsl_init( &f, NULL ); -#endif - assert(!rc); - App.f = f; - VERBOSE(("Initialized fsl @%p\n", (void const *)f)); - - if(App.dbFile){ - VERBOSE(("Trying to open db file [%s]...\n", App.dbFile)); - rc = fsl_repo_open_db( f, App.dbFile ); - if(rc) return rc; - VERBOSE(("Opened db file [%s]\n", - fsl_buffer_cstr(&f->dbRepo.filename))); - } - return 0; -} - -static void my_atexit(){ - if(App.f){ - int rc; - VERBOSE(("Finalizing fsl @%p\n", (void const *)App.f)); - rc = fsl_finalize( App.f ); - assert(!rc); - App.f = 0; - } -} - -static int process_argv( int argc, char * const * argv ){ - int i; - for( i = 1; i < argc; ++i ){ - char const * arg = argv[i]; -#define ARG(F) if(0==strcmp(F,arg)) - ARG("-r") { - App.dbFile = argv[++i]; - continue; - } - ARG("-v") { - App.verbose = 1; - continue; - } - else if('-' == *arg){ - MARKER(("Unknown flag: %s\n", arg)); - return FSL_RC_MISUSE; - } -#undef ARG - } - return 0; -} - -int main(int argc, char * const * argv ){ - int rc; - rc = process_argv( argc, argv ); - if(rc) goto end; - atexit( my_atexit ); - rc = test0(); - if(!rc) rc = test_buffer_0(); - - end: - MARKER(("Done! rc=%d (%s)\n", rc, fsl_rc_cstr(rc))); - if(rc && App.f){ - char const * msg = NULL; - int errRc = fsl_err_get( App.f, &msg, NULL ); - if(msg){ - MARKER(("Fossil says: error code #%d (%s): %s\n", - errRc, fsl_rc_cstr(errRc), msg)); - } - } - return rc; -} - -#undef MARKER -#undef VERBOSE