Index: CHANGES.md ================================================================== --- CHANGES.md +++ CHANGES.md @@ -1,30 +1,5 @@ -**fnc 0.6** 2021-11-22 - -- implement support for user-defined colours in tl, diff, tree and blame views -- make branch view accessible from all other views -- optimise colour scheme initialisation -- implement new 'fnc config' interface to configure per-project settings -- improve build by making make file make(1) compatible -- rename old make file to GNUmakefile to keep gmake support -- more robust error handling of invalid user input to new config command -- fix tree symlink colour regular expression so symlink entries are colourised -- improve colourised header highlighting when in splitscreen mode -- fix display of colourised headers in tmux -- more robust handling of cached statements when switching views -- fix branch-to-tree-to-timeline segv bug when file is missing -- improve const correctness of fcli struct declarations -- enhance diff command with support for arbitrary repository blob artifacts -- quiet error reporting in non-debug builds -- fix commit builder bug that may omit timeline commits when switching views -- improved branch view resource deallocation -- implement new timeline command --filter option and key binding -- enhance timeline --type option with support for tag control artifacts -- general documentation improvements -- assorted updates from upstream libfossil including compiler warning fix -- expand support for user-defined colours to include the branch view - **fnc 0.5** 2021-11-03 - simplify the build system to make-only and remove autosetup (patch from sdk) - remove invalid fsl_errno_to_rc() call and redundant alloc error check - reduce noise on app exit by removing redundant error output DELETED GNUmakefile Index: GNUmakefile ================================================================== --- GNUmakefile +++ GNUmakefile @@ -1,94 +0,0 @@ -# -# FNC Makefile -# - -# CONFIGUGRAION -CC ?= cc -PREFIX ?= /usr/local -MANDIR ?= /share/man -VERSION ?= 0.6 - -# USED BELOW FOR PLATFORM SPECIFIC LDFLAGS -UNAME := $(shell uname -s) - -# FLAGS NEEDED TO BUILD SQLITE3 -SQLITE_CFLAGS = ${CFLAGS} -Wall -Werror -Wno-sign-compare -pedantic -std=c99 \ - -DNDEBUG=1 \ - -DSQLITE_DQS=0 \ - -DSQLITE_THREADSAFE=0 \ - -DSQLITE_DEFAULT_MEMSTATUS=0 \ - -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \ - -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \ - -DSQLITE_OMIT_DECLTYPE \ - -DSQLITE_OMIT_PROGRESS_CALLBACK \ - -DSQLITE_OMIT_SHARED_CACHE \ - -DSQLITE_OMIT_LOAD_EXTENSION \ - -DSQLITE_MAX_EXPR_DEPTH=0 \ - -DSQLITE_USE_ALLOCA \ - -DSQLITE_ENABLE_LOCKING_STYLE=0 \ - -DSQLITE_DEFAULT_FILE_FORMAT=4 \ - -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ - -DSQLITE_ENABLE_FTS4 \ - -DSQLITE_ENABLE_DBSTAT_VTAB \ - -DSQLITE_ENABLE_JSON1 \ - -DSQLITE_ENABLE_FTS5 \ - -DSQLITE_ENABLE_STMTVTAB \ - -DSQLITE_HAVE_ZLIB \ - -DSQLITE_INTROSPECTION_PRAGMAS \ - -DSQLITE_ENABLE_DBPAGE_VTAB \ - -DSQLITE_TRUSTED_SCHEMA=0 - -# This makefile -MAKEFILE := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) - -# FLAGS NEEDED TO BUILD LIBFOSSIL -FOSSIL_CFLAGS = ${CFLAGS} -Wall -Werror -Wsign-compare -pedantic -std=c99 - -# On SOME Linux (e.g., Ubuntu 18.04.6), we have to include wchar curses from -# I/.../ncursesw, but linking to -lncursesw (w/ no special -L path) works fine. -# FLAGS NEEDED TO BUILD FNC -FNC_CFLAGS = ${CFLAGS} -Wall -Werror -Wsign-compare -pedantic -std=c99 \ - -I./lib -I./include -I/usr/include/ncursesw \ - -D_XOPEN_SOURCE_EXTENDED -DVERSION=${VERSION} - -FNC_LDFLAGS = ${LDFLAGS} -lm -lutil -lz -lpthread -fPIC - -# OSX has ncursesw, and needs iconv. -ifeq ($(UNAME),Darwin) -# OSX -FNC_LDFLAGS += -lncurses -lpanel -liconv -else -# Linux (tested on Debian), OpenBSD, FreeBSD -FNC_LDFLAGS += -lncursesw -lpanelw -endif - -all: bin - -bin: lib/sqlite3.o lib/libfossil.o src/fnc.o src/fnc - -lib/sqlite3.o: lib/sqlite3.c lib/sqlite3.h $(MAKEFILE) - ${CC} ${SQLITE_CFLAGS} -c $< -o $@ - -lib/libfossil.o: lib/libfossil.c lib/libfossil.h $(MAKEFILE) - ${CC} ${FOSSIL_CFLAGS} -c $< -o $@ - -src/fnc.o: src/fnc.c include/settings.h $(MAKEFILE) - ${CC} ${FNC_CFLAGS} -c $< -o $@ - -src/fnc: src/fnc.o lib/libfossil.o lib/sqlite3.o $(MAKEFILE) - ${CC} -o $@ src/fnc.o lib/libfossil.o lib/sqlite3.o ${FNC_LDFLAGS} - -install: - install -s -m 0755 src/fnc ${PREFIX}/bin/fnc - install -m 0644 src/fnc.1 ${PREFIX}${MANDIR}/man1/fnc.1 - -uninstall: - rm -f ${PREFIX}/bin/fnc ${PREFIX}${MANDIR}/man1/fnc.1 - -clean: - rm -f lib/*.o src/*.o src/fnc - -release: clean - tar czvf ../fnc-${VERSION}.tgz -C .. fnc-${VERSION} - -.PHONY: clean release Index: Makefile ================================================================== --- Makefile +++ Makefile @@ -4,14 +4,14 @@ # CONFIGUGRAION CC ?= cc PREFIX ?= /usr/local MANDIR ?= /share/man -VERSION ?= 0.6 +VERSION ?= 0.5 # USED BELOW FOR PLATFORM SPECIFIC LDFLAGS -UNAME != uname -s +UNAME := $(shell uname -s) # FLAGS NEEDED TO BUILD SQLITE3 SQLITE_CFLAGS = ${CFLAGS} -Wall -Werror -Wno-sign-compare -pedantic -std=c99 \ -DNDEBUG=1 \ -DSQLITE_DQS=0 \ @@ -36,45 +36,48 @@ -DSQLITE_HAVE_ZLIB \ -DSQLITE_INTROSPECTION_PRAGMAS \ -DSQLITE_ENABLE_DBPAGE_VTAB \ -DSQLITE_TRUSTED_SCHEMA=0 +# This makefile +MAKEFILE := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) + # FLAGS NEEDED TO BUILD LIBFOSSIL FOSSIL_CFLAGS = ${CFLAGS} -Wall -Werror -Wsign-compare -pedantic -std=c99 # On SOME Linux (e.g., Ubuntu 18.04.6), we have to include wchar curses from # I/.../ncursesw, but linking to -lncursesw (w/ no special -L path) works fine. # FLAGS NEEDED TO BUILD FNC FNC_CFLAGS = ${CFLAGS} -Wall -Werror -Wsign-compare -pedantic -std=c99 \ - -I./lib -I./include -I/usr/include/ncursesw \ - -D_XOPEN_SOURCE_EXTENDED -DVERSION=${VERSION} + -I./lib -I/usr/include/ncursesw -D_XOPEN_SOURCE_EXTENDED \ + -DVERSION=${VERSION} FNC_LDFLAGS = ${LDFLAGS} -lm -lutil -lz -lpthread -fPIC # OSX has ncursesw, and needs iconv. -.if $(UNAME) == Darwin +ifeq ($(UNAME),Darwin) # OSX FNC_LDFLAGS += -lncurses -lpanel -liconv -.else +else # Linux (tested on Debian), OpenBSD, FreeBSD FNC_LDFLAGS += -lncursesw -lpanelw -.endif +endif all: bin bin: lib/sqlite3.o lib/libfossil.o src/fnc.o src/fnc -lib/sqlite3.o: lib/sqlite3.c lib/sqlite3.h +lib/sqlite3.o: lib/sqlite3.c lib/sqlite3.h $(MAKEFILE) ${CC} ${SQLITE_CFLAGS} -c $< -o $@ -lib/libfossil.o: lib/libfossil.c lib/libfossil.h +lib/libfossil.o: lib/libfossil.c lib/libfossil.h $(MAKEFILE) ${CC} ${FOSSIL_CFLAGS} -c $< -o $@ -src/fnc.o: src/fnc.c include/settings.h +src/fnc.o: src/fnc.c $(MAKEFILE) ${CC} ${FNC_CFLAGS} -c $< -o $@ -src/fnc: src/fnc.o lib/libfossil.o lib/sqlite3.o Makefile +src/fnc: src/fnc.o lib/libfossil.o lib/sqlite3.o $(MAKEFILE) ${CC} -o $@ src/fnc.o lib/libfossil.o lib/sqlite3.o ${FNC_LDFLAGS} install: install -s -m 0755 src/fnc ${PREFIX}/bin/fnc install -m 0644 src/fnc.1 ${PREFIX}${MANDIR}/man1/fnc.1 Index: README.md ================================================================== --- README.md +++ README.md @@ -1,15 +1,15 @@ # README -# fnc 0.5 +# fnc 0.4 -## An ncurses browser for [Fossil][0] repositories in the terminal. +## A read-only ncurses browser for [Fossil][0] repositories in the terminal. `fnc` uses [libfossil][1] to create a [`fossil ui`][2] experience in the terminal. -Tested and confirmed to run on the following amd64 systems (additional platforms +Tested and confirmed to run on the following x64 systems (additional platforms noted inline): 1. OpenBSD 6.8- and 6.9-release 2. macOS 10.15.7 (Catalina) and 11.5.2 (Big Sur) 3. Linux Mint 20.2 (32- and 64-bit ARM) DELETED include/settings.h Index: include/settings.h ================================================================== --- include/settings.h +++ include/settings.h @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2021 Mark Jamsek - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* - * All configurable fnc settings, which can be stored in either the fossil(1) - * repository (e.g., repo.fossil) or global (i.e., $HOME/.fossil) db. Note that - * each setting _MAY_ be mapped to a valid enum, in which case its index here - * _MUST_ be mapped to the index/value of its enum counterpart (i.e., either - * explicitly set equivalent values in the enum, or use offsets when indexing - * them). - */ -#define SETTINGS(SET) \ - SET(FNC_START_SETTINGS) /* 0 */ \ - SET(FNC_COLOUR_COMMIT) /* 1 */ \ - SET(FNC_COLOUR_USER) /* 2 */ \ - SET(FNC_COLOUR_DATE) /* 3 */ \ - SET(FNC_COLOUR_DIFF_META) /* 4 */ \ - SET(FNC_COLOUR_DIFF_MINUS) /* 5 */ \ - SET(FNC_COLOUR_DIFF_PLUS) /* 6 */ \ - SET(FNC_COLOUR_DIFF_CHUNK) /* 7 */ \ - SET(FNC_COLOUR_DIFF_TAGS) /* 8 */ \ - SET(FNC_COLOUR_TREE_LINK) /* 9 */ \ - SET(FNC_COLOUR_TREE_DIR) /* 10 */ \ - SET(FNC_COLOUR_TREE_EXEC) /* 11 */ \ - SET(FNC_COLOUR_BRANCH_OPEN) /* 12 */ \ - SET(FNC_COLOUR_BRANCH_CLOSED) /* 13 */ \ - SET(FNC_COLOUR_BRANCH_CURRENT) /* 14 */ \ - SET(FNC_COLOUR_BRANCH_PRIVATE) /* 15 */ \ - SET(FNC_EOF_SETTINGS) /* 16 */ - -/* - * To construct the string array and enum: - * static const char *fnc_settings[] = { SETTINGS(GEN_STR) }; - * enum settings { SETTINGS(GEN_ENUM) }; - */ -#define GEN_ENUM(_id) _id, -#define GEN_STR(_str) #_str, - Index: lib/libfossil-config.h ================================================================== --- lib/libfossil-config.h +++ lib/libfossil-config.h @@ -1,11 +1,11 @@ #if !defined(_NET_FOSSIL_SCM_FSL_AMALGAMATION_CONFIG_H_INCLUDED_) #define _NET_FOSSIL_SCM_FSL_AMALGAMATION_CONFIG_H_INCLUDED_ 1 #define FSL_AUX_SCHEMA "2015-01-24" #define FSL_CONTENT_SCHEMA "2" #define FSL_PACKAGE_NAME "libfossil" -#define FSL_LIBRARY_VERSION "0.5-ish" +#define FSL_LIBRARY_VERSION "0.0.1-alphabeta" /* Tweak the following for your system... */ #if !defined(HAVE_COMPRESS) # define HAVE_COMPRESS 1 #endif #if !defined(HAVE_DLFCN_H) @@ -81,13 +81,13 @@ #endif #endif /* _WIN32 */ -#define FSL_LIB_VERSION_HASH "2a405470c0d32a84f8644de1b5c509635a7e78cb" -#define FSL_LIB_VERSION_TIMESTAMP "2021-11-18 17:40:56.695 UTC" -#define FSL_LIB_CONFIG_TIME "2021-11-18 18:02 GMT" +#define FSL_LIB_VERSION_HASH "acbd39275f0d573a74d996caea7918afafff2ff7" +#define FSL_LIB_VERSION_TIMESTAMP "2021-10-08 03:15:51.855 UTC" +#define FSL_LIB_CONFIG_TIME "2021-10-08 04:07 GMT" #if defined(_MSC_VER) #define FSL_PLATFORM_OS "windows" #define FSL_PLATFORM_IS_WINDOWS 1 #define FSL_PLATFORM_IS_UNIX 0 #define FSL_PLATFORM_PLATFORM "windows" @@ -134,20 +134,9 @@ #define FSL_PLATFORM_PLATFORM "unix" #define FSL_PLATFORM_PATH_SEPARATOR ":" #define FSL_CHECKOUTDB_NAME "./.fslckout" #endif -#if !defined(HAVE_SIGACTION) -# if defined(FSL_AMALGAMATION_BUILD) -# if defined(FSL_PLATFORM_IS_UNIX) -# define HAVE_SIGACTION 1 -# else -# define HAVE_SIGACTION 0 -# endif -# else -# define HAVE_SIGACTION 0 -# endif -#endif #endif /* _NET_FOSSIL_SCM_FSL_AMALGAMATION_CONFIG_H_INCLUDED_ */ Index: lib/libfossil.c ================================================================== --- lib/libfossil.c +++ lib/libfossil.c @@ -37,11 +37,11 @@ /* Please keep all fsl_XXX_empty initializers in one place (here) and lexically sorted. */ -const fsl__bccache fsl__bccache_empty = fsl__bccache_empty_m; +const fsl_acache fsl_acache_empty = fsl_acache_empty_m; const fsl_branch_opt fsl_branch_opt_empty = fsl_branch_opt_empty_m; const fsl_buffer fsl_buffer_empty = fsl_buffer_empty_m; const fsl_card_F fsl_card_F_empty = fsl_card_F_empty_m; const fsl_card_F_list fsl_card_F_list_empty = fsl_card_F_list_empty_m; const fsl_card_J fsl_card_J_empty = fsl_card_J_empty_m; @@ -67,17 +67,16 @@ const fsl_error fsl_error_empty = fsl_error_empty_m; const fsl_checkin_queue_opt fsl_checkin_queue_opt_empty = fsl_checkin_queue_opt_empty_m; const fsl_fstat fsl_fstat_empty = fsl_fstat_empty_m; const fsl_list fsl_list_empty = fsl_list_empty_m; -const fsl__mcache fsl__mcache_empty = fsl__mcache_empty_m; +const fsl_mcache fsl_mcache_empty = fsl_mcache_empty_m; const fsl_merge_opt fsl_merge_opt_empty = fsl_merge_opt_empty_m; const fsl_outputer fsl_outputer_FILE = fsl_outputer_FILE_m; const fsl_outputer fsl_outputer_empty = fsl_outputer_empty_m; const fsl_pathfinder fsl_pathfinder_empty = fsl_pathfinder_empty_m; -const fsl__pq fsl__pq_empty = fsl__pq_empty_m; -const fsl_rebuild_step fsl_rebuild_step_empty = fsl_rebuild_step_empty_m; +const fsl_pq fsl_pq_empty = fsl_pq_empty_m; const fsl_repo_create_opt fsl_repo_create_opt_empty = fsl_repo_create_opt_empty_m; const fsl_repo_extract_opt fsl_repo_extract_opt_empty = fsl_repo_extract_opt_empty_m; const fsl_repo_extract_state fsl_repo_extract_state_empty = @@ -160,22 +159,27 @@ default: return 0; } } void fsl_error_clear( fsl_error * const err ){ - fsl_buffer_clear(&err->msg); - *err = fsl_error_empty; + if(err){ + fsl_buffer_clear(&err->msg); + *err = fsl_error_empty; + } } void fsl_error_reset( fsl_error * const err ){ - err->code = 0; - err->msg.used = err->msg.cursor = 0; - if(err->msg.mem) err->msg.mem[0] = 0; + if(err){ + err->code = 0; + err->msg.used = err->msg.cursor = 0; + if(err->msg.mem) err->msg.mem[0] = 0; + } } + int fsl_error_copy( fsl_error const * const src, fsl_error * const dest ){ - if(src==dest) return FSL_RC_MISUSE; + if(!src || !dest || (src==dest)) return FSL_RC_MISUSE; else { int rc = 0; dest->msg.used = dest->msg.cursor = 0; dest->code = src->code; if(FSL_RC_OOM!=src->code){ @@ -193,11 +197,12 @@ *higher = err; } int fsl_error_setv( fsl_error * const err, int code, char const * fmt, va_list args ){ - if(!code){ /* clear error state */ + if(!err) return FSL_RC_MISUSE; + else if(!code){ /* clear error state */ err->code = 0; err->msg.used = err->msg.cursor = 0; if(err->msg.mem){ err->msg.mem[0] = 0; } @@ -227,15 +232,18 @@ } int fsl_error_get( fsl_error const * const err, char const ** str, fsl_size_t * const len ){ - if(str) *str = err->msg.used - ? (char const *)err->msg.mem - : NULL; - if(len) *len = err->msg.used; - return err->code; + if(!err) return FSL_RC_MISUSE; + else{ + if(str) *str = err->msg.used + ? (char const *)err->msg.mem + : NULL; + if(len) *len = err->msg.used; + return err->code; + } } char const * fsl_rc_cstr(int rc){ fsl_rc_e const RC = (fsl_rc_e)rc @@ -259,11 +267,10 @@ STR(DELTA_INVALID_TERMINATOR); STR(DIFF_BINARY); STR(DIFF_WS_ONLY); STR(end); STR(ERROR); - STR(INTERRUPTED); STR(IO); STR(MISSING_INFO); STR(MISUSE); STR(NOOP); STR(NOT_A_CKOUT); @@ -396,12 +403,12 @@ char * fsl_strdup( char const * src ){ return fsl_strndup(src, -1); } -fsl_size_t fsl_strlcpy(char * dst, const char * src, fsl_size_t dstsz){ - fsl_size_t offset = 0; +size_t fsl_strlcpy(char *restrict dst, const char *restrict src, size_t dstsz){ + size_t offset = 0; if(dstsz<1){ goto end; } while((*(dst+offset) = *(src+offset))!='\0'){ @@ -416,12 +423,12 @@ ++offset; /* Return src length. */ } return offset; } -fsl_size_t fsl_strlcat(char *dst, const char *src, fsl_size_t dstsz){ - fsl_size_t offset; +size_t fsl_strlcat(char *restrict dst, const char *restrict src, size_t dstsz){ + size_t offset; int dstlen, srclen, idx = 0; offset = dstlen = fsl_strlen(dst); srclen = fsl_strlen(src); @@ -500,11 +507,11 @@ return true; } } } -char * fsl_user_name_guess(){ +char * fsl_guess_user_name(){ char const ** e; static char const * list[] = { "FOSSIL_USER", #if defined(_WIN32) "USERNAME", @@ -533,18 +540,18 @@ } } return rv; } -void fsl__fatal( int code, char const * fmt, ... ){ +void fsl_fatal( int code, char const * fmt, ... ){ static bool inFatal = false; if(inFatal){ /* This can only happen if the fsl_appendv() bits call this AND trigger it via fsl_fprintf() below, neither of which is currently the case. */ - assert(!"fsl__fatal() called recursively."); + assert(!"fsl_fatal() called recursively."); abort(); }else{ va_list args; inFatal = true; fsl_fprintf(stderr, "FATAL ERROR: code=%d (%s)\n", @@ -940,11 +947,11 @@ char fsl_isatty(int fd){ return isatty(fd) ? 1 : 0; } -bool fsl__is_reserved_fn_windows(const char *zPath, fsl_int_t nameLen){ +bool fsl_is_reserved_fn_windows(const char *zPath, fsl_int_t nameLen){ static const char *const azRes[] = { "CON", "PRN", "AUX", "NUL", "COM", "LPT" }; unsigned int i; char const * zEnd; @@ -971,11 +978,11 @@ ? (fsl_size_t)nameLen : fsl_strlen(zFilename); char const * zEnd; int gotSuffix = 0; assert( zFilename && "API misuse" ); #if FSL_PLATFORM_IS_WINDOWS // || 1 - if(nFilename>2 && fsl__is_reserved_fn_windows(zFilename, nameLen)){ + if(nFilename>2 && fsl_is_reserved_fn_windows(zFilename, nameLen)){ return true; } #endif if( nFilename<8 ) return false; /* strlen("_FOSSIL_") */ zEnd = zFilename + nFilename; @@ -1059,11 +1066,11 @@ ** The status of an annotation operation is recorded by an instance ** of the following structure. */ typedef struct Annotator Annotator; struct Annotator { - fsl__diff_cx c; /* The diff-engine context */ + fsl_diff_cx c; /* The diff-engine context */ fsl_buffer headVersion;/*starting version of the content*/ struct AnnLine { /* Lines of the original files... */ const char *z; /* The text of the line. Points into this->headVersion. */ short int n; /* Number of bytes (omitting trailing \n) */ @@ -1083,11 +1090,11 @@ unsigned int naVers; /* # of entries allocated in this->aVers */ fsl_timer_state timer; }; static const Annotator Annotator_empty = { -fsl__diff_cx_empty_m, +fsl_diff_cx_empty_m, fsl_buffer_empty_m/*headVersion*/, NULL/*aOrig*/, 0U/*nOrig*/, 0U/*nVers*/, false/*bMoreToDo*/, 0/*origId*/, @@ -1427,11 +1434,11 @@ int fsl_annotate( fsl_cx * const f, fsl_annotate_opt const * const opt ){ int rc; Annotator ann = Annotator_empty; unsigned int i; - fsl_buffer * const scratch = fsl__cx_scratchpad(f); + fsl_buffer * const scratch = fsl_cx_scratchpad(f); fsl_annotate_step aStep; assert(opt->out); if(opt->limitMs>0) fsl_timer_start(&ann.timer); rc = fsl__annotate_file(f, &ann, opt); @@ -1482,11 +1489,11 @@ } rc = opt->out(opt->outState, opt, &aStep); } end: - fsl__cx_scratchpad_yield(f, scratch); + fsl_cx_scratchpad_yield(f, scratch); fsl__annotator_clean(&ann); return rc; } #undef MARKER @@ -2042,16 +2049,14 @@ int rc = 0; char ch = 0; char ch2 = 0; char xbuf[4]; int decoded; - char const * end = str + pfLen; - if( !str || !pfLen ) return 0; + if( ! str ) return 0; ch = *str; - while(0==rc && ch && str=end) goto outro/*invalid partial encoding - simply skip it*/; ch = *(++str); ch2 = *(++str); if( isxdigit((int)ch) && isxdigit((int)ch2) ) { @@ -2076,11 +2081,10 @@ xbuf[1] = 0; rc = pf( pfArg, xbuf, 1 ); ch = *(++str); continue; } - outro: xbuf[0] = ch; xbuf[1] = 0; rc = pf( pfArg, xbuf, 1 ); ch = *(++str); } @@ -2108,21 +2112,14 @@ int ch; char const q = xtype==etSQLESCAPE3 ?'"':'\''; /* Quote character */ char const * escarg = (char const *) varg; bool const isnull = escarg==0; bool const needQuote = - !isnull && (xtype==etSQLESCAPE2 - || xtype==etBLOBSQL - || xtype==etSQLESCAPE3); + !isnull && (xtype==etSQLESCAPE2 || xtype==etSQLESCAPE3); if( isnull ){ - if(xtype==etSQLESCAPE2||xtype==etSQLESCAPE3){ - escarg = "NULL"; - pfLen = 4; - }else{ - escarg = "(NULL)";; - pfLen = 6; - } + escarg = (xtype==etSQLESCAPE2||xtype==etSQLESCAPE3) + ? "NULL" : "(NULL)"; } if( needQuote ) buf[j++] = q; for(i=0; (ch=escarg[i])!=0 && i=0 && precision=0 && precision=0 && precision=0 && precisionused ? fsl_buffer_append( dest, src->mem, src->used ) : 0; } @@ -4355,38 +4350,38 @@ #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) -bool fsl__bccache_expire_oldest(fsl__bccache * const c){ +bool fsl_acache_expire_oldest(fsl_acache * c){ static uint16_t const sentinel = 0xFFFF; uint16_t i; - fsl_uint_t mnAge = c->nextAge; + fsl_int_t mnAge = c->nextAge; uint16_t mn = sentinel; for(i=0; iused; i++){ if( c->list[i].agelist[i].age; mn = i; } } if( mninCache, c->list[mn].rid); - c->szTotal -= (unsigned)c->list[mn].content.capacity; + c->szTotal -= (uint32_t)c->list[mn].content.capacity; fsl_buffer_clear(&c->list[mn].content); --c->used; c->list[mn] = c->list[c->used]; } return sentinel!=mn; } -int fsl__bccache_insert(fsl__bccache * const c, fsl_id_t rid, fsl_buffer * const pBlob){ - fsl__bccache_line *p; +int fsl_acache_insert(fsl_acache * c, fsl_id_t rid, fsl_buffer *pBlob){ + fsl_acache_line *p; if( c->used>c->usedLimit || c->szTotal>c->szLimit ){ fsl_size_t szBefore; do{ szBefore = c->szTotal; - fsl__bccache_expire_oldest(c); + fsl_acache_expire_oldest(c); }while( c->szTotal>c->szLimit && c->szTotalusedLimit || !c->szLimit) || (c->used+1 >= c->usedLimit)){ fsl_buffer_clear(pBlob); @@ -4397,73 +4392,53 @@ void * remem = c->list ? fsl_realloc(c->list, cap*sizeof(c->list[0])) : fsl_malloc( cap*sizeof(c->list[0]) ); assert((c->capacity && capcapacity) ? !"Numeric overflow" : 1); if(c->capacity && capcapacity){ - fsl__fatal(FSL_RC_RANGE,"Numeric overflow. Bump " - "fsl__bccache::capacity to a larger int type."); + fsl_fatal(FSL_RC_RANGE,"Numeric overflow. Bump " + "fsl_acache::capacity to a larger int type."); } if(!remem){ fsl_buffer_clear(pBlob) /* for consistency */; return FSL_RC_OOM; } c->capacity = cap; - c->list = (fsl__bccache_line*)remem; - } - int const rc = fsl_id_bag_insert(&c->inCache, rid); - if(0==rc){ - p = &c->list[c->used++]; - p->rid = rid; - p->age = c->nextAge++; - c->szTotal += pBlob->capacity; - p->content = *pBlob /* Transfer ownership */; - *pBlob = fsl_buffer_empty; - }else{ - fsl_buffer_clear(pBlob); - } - return rc; + c->list = (fsl_acache_line*)remem; + } + p = &c->list[c->used++]; + p->rid = rid; + p->age = c->nextAge++; + c->szTotal += pBlob->capacity; + p->content = *pBlob /* Transfer ownership */; + *pBlob = fsl_buffer_empty; + return fsl_id_bag_insert(&c->inCache, rid); } -void fsl__bccache_clear(fsl__bccache * const c){ +void fsl_acache_clear(fsl_acache * c){ #if 0 - while(fsl__bccache_expire_oldest(c)){} + while(fsl_acache_expire_oldest(c)){} #else fsl_size_t i; - for(i=0; iused; ++i){ + for(i=0; iused; i++){ fsl_buffer_clear(&c->list[i].content); } #endif fsl_free(c->list); fsl_id_bag_clear(&c->missing); fsl_id_bag_clear(&c->available); fsl_id_bag_clear(&c->inCache); - *c = fsl__bccache_empty; -} - -void fsl__bccache_reset(fsl__bccache * const c){ - static const fsl__bccache_line line_empty = fsl__bccache_line_empty_m; - fsl_size_t i; - for(i=0; iused; ++i){ - fsl_buffer_clear(&c->list[i].content); - c->list[i] = line_empty; - } - c->used = 0; - c->szTotal = 0; - c->nextAge = 0; - fsl_id_bag_reset(&c->missing); - fsl_id_bag_reset(&c->available); - fsl_id_bag_reset(&c->inCache); + *c = fsl_acache_empty; } -int fsl__bccache_check_available(fsl_cx * const f, fsl_id_t rid){ +int fsl_acache_check_available(fsl_cx * f, fsl_id_t rid){ fsl_id_t srcid; int depth = 0; /* Limit to recursion depth */ static const int limit = 10000000 /* historical value */; int rc; - fsl__bccache * const c = &f->cache.blobContent; + fsl_acache * c = &f->cache.arty; assert(f); assert(c); assert(rid>0); assert(fsl_cx_db_repo(f)); while( depth++ < limit ){ @@ -4544,20 +4519,20 @@ */ static int fsl_checkin_import_file( fsl_cx * f, char const * zRelName, fsl_id_t parentRid, bool allowMergeConflict, fsl_id_t *rid, fsl_uuid_str * uuid){ - fsl_buffer * nbuf = fsl__cx_scratchpad(f); + fsl_buffer * nbuf = fsl_cx_scratchpad(f); fsl_size_t const oldSize = nbuf->used; fsl_buffer * fbuf = &f->fileContent; char const * fn; int rc; fsl_id_t fnid = 0; fsl_id_t rcRid = 0; assert(!fbuf->used && "Misuse of f->fileContent"); assert(f->ckout.dir); - rc = fsl__repo_filename_fnid2(f, zRelName, &fnid, 1); + rc = fsl_repo_filename_fnid2(f, zRelName, &fnid, 1); if(rc) goto end; assert(fnid>0); rc = fsl_buffer_appendf(nbuf, "%s%s", f->ckout.dir, zRelName); nbuf->used = oldSize; @@ -4574,15 +4549,15 @@ "File contains a merge conflict marker: %s", zRelName); goto end; } - rc = fsl__content_put( f, fbuf, &rcRid ); + rc = fsl_content_put( f, fbuf, &rcRid ); if(!rc){ assert(rcRid > 0); if(parentRid>0){ - rc = fsl__content_deltify(f, parentRid, rcRid, 0); + rc = fsl_content_deltify(f, parentRid, rcRid, 0); } if(!rc){ if(rid) *rid = rcRid; if(uuid){ *uuid = fsl_rid_to_uuid(f, rcRid); @@ -4589,12 +4564,12 @@ if(!*uuid) rc = (f->error.code ? f->error.code : FSL_RC_OOM); } } } end: - fsl__cx_scratchpad_yield(f, nbuf); - fsl__cx_content_buffer_yield(f); + fsl_cx_scratchpad_yield(f, nbuf); + fsl_cx_content_buffer_yield(f); assert(0==fbuf->used); return rc; } int fsl_filename_to_vfile_ids( fsl_cx * f, fsl_id_t vid, @@ -4603,11 +4578,11 @@ fsl_stmt st = fsl_stmt_empty; fsl_db * const db = fsl_needs_ckout(f); int rc; fsl_buffer * sql = 0; if(!db) return FSL_RC_NOT_A_CKOUT; - sql = fsl__cx_scratchpad(f); + sql = fsl_cx_scratchpad(f); if(0>=vid) vid = f->ckout.rid; if(zName && *zName && !('.'==*zName && !zName[1])){ rc = fsl_buffer_appendf(sql, "SELECT id FROM vfile WHERE vid=%" @@ -4632,11 +4607,11 @@ while(!rc && (FSL_RC_STEP_ROW == (rc=fsl_stmt_step(&st)))){ rc = fsl_id_bag_insert( dest, fsl_stmt_g_id(&st, 0) ); } if(FSL_RC_STEP_DONE==rc) rc = 0; end: - fsl__cx_scratchpad_yield(f, sql); + fsl_cx_scratchpad_yield(f, sql); fsl_stmt_finalize(&st); if(rc && !f->error.code && db->error.code){ fsl_cx_uplift_db_error(f, db); } return rc; @@ -4720,11 +4695,11 @@ } int fsl_checkin_enqueue(fsl_cx * f, fsl_checkin_queue_opt const * opt){ fsl_db * const db = fsl_needs_ckout(f); if(!db) return FSL_RC_NOT_A_CKOUT; - fsl_buffer * const canon = opt->vfileIds ? 0 : fsl__cx_scratchpad(f); + fsl_buffer * const canon = opt->vfileIds ? 0 : fsl_cx_scratchpad(f); fsl_stmt qName = fsl_stmt_empty; fsl_id_bag _vfileIds = fsl_id_bag_empty; fsl_id_bag const * const vfileIds = opt->vfileIds ? opt->vfileIds : &_vfileIds; int rc = fsl_db_transaction_begin(db); @@ -4772,11 +4747,11 @@ if(opt->vfileIds){ assert(!canon); assert(!_vfileIds.list); }else{ assert(canon); - fsl__cx_scratchpad_yield(f, canon); + fsl_cx_scratchpad_yield(f, canon); fsl_id_bag_clear(&_vfileIds); } fsl_stmt_finalize(&qName); if(rc) fsl_db_transaction_rollback(db); else{ @@ -4793,11 +4768,11 @@ fsl_id_bag list = fsl_id_bag_empty; fsl_buffer * canon = 0; char const * fn; fsl_stmt qName = fsl_stmt_empty; if(opt->filename && *opt->filename){ - canon = fsl__cx_scratchpad(f); + canon = fsl_cx_scratchpad(f); rc = fsl_ckout_filename_check(f, opt->relativeToCwd, opt->filename, canon); if(rc) goto end; else fsl_buffer_strip_slashes(canon); } @@ -4814,11 +4789,11 @@ rc = fsl_xqueue_callback(f, db, &qName, nid, opt); } } } end: - if(canon) fsl__cx_scratchpad_yield(f, canon); + if(canon) fsl_cx_scratchpad_yield(f, canon); fsl_stmt_finalize(&qName); fsl_id_bag_clear(&list); if(rc) fsl_db_transaction_rollback(db); else{ rc = fsl_cx_uplift_db_error2(f, db, fsl_db_transaction_commit(db)); @@ -4835,11 +4810,11 @@ /* Behave like fsl_is_enqueued() SQL function. */ return true; } else { bool rv = false; - fsl_buffer * const canon = fsl__cx_scratchpad(f); + fsl_buffer * const canon = fsl_cx_scratchpad(f); int rc = fsl_ckout_filename_check(f, relativeToCwd, zName, canon); if(!rc){ fsl_id_t vfid = 0; rc = fsl_filename_to_vfile_id(f, 0, fsl_buffer_cstr(canon), &vfid); @@ -4848,11 +4823,11 @@ : ((vfid>0) ? fsl_id_bag_contains(&f->ckin.selectedIds, vfid) /* ^^^^ asserts that arg2!=0*/ : false); } - fsl__cx_scratchpad_yield(f, canon); + fsl_cx_scratchpad_yield(f, canon); return rv; } } @@ -5002,11 +4977,11 @@ continue; } if(isExe) perm = FSL_FILE_PERM_EXE; else if(isLink){ - fsl__fatal(FSL_RC_NYI, "This code does not yet deal " + fsl_fatal(FSL_RC_NYI, "This code does not yet deal " "with symlinks. file: %s", zName) /* does not return */; perm = FSL_FILE_PERM_LINK; } /* @@ -5018,11 +4993,11 @@ if(rc) break; } #if 0 if(mergeRid && (mergeRid != rid)){ - fsl__fatal(FSL_RC_NYI, "This code does not yet deal " + fsl_fatal(FSL_RC_NYI, "This code does not yet deal " "with merges. file: %s", zName) /* does not return */; } #endif while(pFile && fsl_strcmp(pFile->name, zName)<0){ @@ -5672,16 +5647,16 @@ "fsl_is_enqueued(id); " "UPDATE vfile SET rid=mrid, mhash=NULL, " "chnged=0, deleted=0, origname=NULL " "WHERE fsl_is_enqueued(id)", vid, d->rid); - if(!rc) rc = fsl__ckout_version_write(f, d->rid, NULL); + if(!rc) rc = fsl_ckout_version_write(f, d->rid, NULL); RC; assert(d->f == f); rc = fsl_checkin_add_unsent(f, d->rid); RC; - rc = fsl__ckout_clear_merge_state(f); + rc = fsl_ckout_clear_merge_state(f); RC; /* todo(?) from fossil(1) follows. Most of this seems to be what the vfile handling does (above). @@ -5737,11 +5712,11 @@ char const * p0 = (char const *)d->P.list[0]; fsl_id_t const prid = fsl_uuid_to_rid(f, p0); /* MARKER(("Deltifying parent manifest #%d...\n", (int)prid)); */ assert(p0); assert(prid>0); - rc = fsl__content_deltify(f, prid, d->rid, 0); + rc = fsl_content_deltify(f, prid, d->rid, 0); RC; } end: f->flags = oldFlags; @@ -5829,11 +5804,11 @@ else{ char const * zLocalRoot; char const * zFull; fsl_size_t nLocalRoot; fsl_size_t nFull; - fsl_buffer * full = fsl__cx_scratchpad(f); + fsl_buffer * full = fsl_cx_scratchpad(f); int (*xCmp)(char const *, char const *,fsl_size_t); bool endsWithSlash; assert(f->ckout.dir); zLocalRoot = f->ckout.dir; assert(zLocalRoot); @@ -5899,11 +5874,11 @@ if(pOut){ rc = fsl_buffer_append(pOut, zFull + nLocalRoot, nFull - nLocalRoot); } end: - fsl__cx_scratchpad_yield(f, full); + fsl_cx_scratchpad_yield(f, full); } return rc; } @@ -5915,11 +5890,11 @@ there are more checkout-level change codes than vfile-level changes. We could still consolidate them, giving the vfile changes their hard-coded values and leaving room in the enum for upward growth of that set. */ -static fsl_ckout_change_e fsl__vfile_to_ckout_change(int vChange){ +static fsl_ckout_change_e fsl_vfile_to_ckout_change(int vChange){ switch((fsl_vfile_change_e)vChange){ #define EE(X) case FSL_VFILE_CHANGE_##X: return FSL_CKOUT_CHANGE_##X EE(NONE); EE(MOD); EE(MERGE_MOD); @@ -5995,11 +5970,11 @@ fsl_cx_err_reset(f) /* keep FSL_RC_NOT_FOUND from bubbling up to the client! */; }else if(!changed){ continue; }else{ - coChange = fsl__vfile_to_ckout_change(changed); + coChange = fsl_vfile_to_ckout_change(changed); } } if(!coChange){ MARKER(("INTERNAL ERROR: unhandled vfile.chnged " "value %d for file [%s]\n", @@ -6165,11 +6140,11 @@ rc = fsl_cx_err_set(cas->f, rc, "fsl_dircrawl() returned %s.", fsl_rc_cstr(rc)); } }else{ assert(!"Cannot happen - caught higher up"); - fsl__fatal(FSL_RC_ERROR, "Internal API misuse in/around %s().", + fsl_fatal(FSL_RC_ERROR, "Internal API misuse in/around %s().", __func__); } break; default: rc = fsl_cx_err_set(cas->f, FSL_RC_TYPE, @@ -6198,31 +6173,31 @@ else if(!fsl_needs_ckout(f)) return FSL_RC_NOT_A_CKOUT; assert(f->ckout.rid>=0); opt = *opt_ /*use a copy in case the user manages to modify opt_ from a callback. */; - cas.absBuf = fsl__cx_scratchpad(f); - cas.coRelBuf = fsl__cx_scratchpad(f); + cas.absBuf = fsl_cx_scratchpad(f); + cas.coRelBuf = fsl_cx_scratchpad(f); rc = fsl_file_canonical_name(opt.filename, cas.absBuf, false); if(!rc){ cas.f = f; cas.opt = &opt; rc = co_add_one(&cas, false); opt_->counts = opt.counts; } - fsl__cx_scratchpad_yield(f, cas.absBuf); - fsl__cx_scratchpad_yield(f, cas.coRelBuf); + fsl_cx_scratchpad_yield(f, cas.absBuf); + fsl_cx_scratchpad_yield(f, cas.coRelBuf); return rc; } /** Creates, if needed, a TEMP TABLE named [tableName] with a single [id] field and populates it with all ids from the given bag. Returns 0 on success, any number of non-0 codes on error. */ -static int fsl_ckout_bag_to_ids(fsl_cx * const f, fsl_db * const db, +static int fsl_ckout_bag_to_ids(fsl_cx *f, fsl_db * db, char const * tableName, fsl_id_bag const * bag){ fsl_stmt insId = fsl_stmt_empty; int rc = fsl_db_exec_multi(db, "CREATE TEMP TABLE IF NOT EXISTS " @@ -6256,11 +6231,11 @@ assert(rc); rc = fsl_cx_uplift_db_error2(f, db, rc); goto end; } -int fsl_ckout_unmanage(fsl_cx * const f, fsl_ckout_unmanage_opt const * opt){ +int fsl_ckout_unmanage(fsl_cx * f, fsl_ckout_unmanage_opt const * opt){ int rc; fsl_db * const db = fsl_needs_ckout(f); fsl_buffer * fname = 0; fsl_id_t const vid = f->ckout.rid; fsl_stmt q = fsl_stmt_empty; @@ -6295,11 +6270,11 @@ "/* %s() */", vid, __func__); if(rc) goto dberr; } }else{// Process opt->filename - fname = fsl__cx_scratchpad(f); + fname = fsl_cx_scratchpad(f); rc = fsl_ckout_filename_check(f, opt->relativeToCwd, opt->filename, fname); if(rc) goto end; char const * zNorm = fsl_buffer_cstr(fname); /* MARKER(("fsl_ckout_unmanage(%d, %s) ==> %s\n", relativeToCwd, zFilename, zNorm)); */ @@ -6352,11 +6327,11 @@ "DELETE FROM vfile WHERE vid=%" FSL_ID_T_PFMT " AND rid=0 AND deleted", vid); if(rc) goto dberr; end: - if(fname) fsl__cx_scratchpad_yield(f, fname); + if(fname) fsl_cx_scratchpad_yield(f, fname); fsl_stmt_finalize(&q); if(opt->vfileIds){ fsl_db_exec(db, "DROP TABLE IF EXISTS fx_unmanage_id /* %s() */", __func__) /* Ignoring result code */; @@ -6371,15 +6346,15 @@ rc = fsl_cx_uplift_db_error2(f, db, rc); goto end; } -int fsl_ckout_changes_scan(fsl_cx * const f){ +int fsl_ckout_changes_scan(fsl_cx * f){ return fsl_vfile_changes_scan(f, -1, 0); } -int fsl_ckout_install_schema(fsl_cx * const f, bool dropIfExists){ +int fsl_ckout_install_schema(fsl_cx *f, bool dropIfExists){ char const * tNames[] = { "vvar", "vfile", "vmerge", 0 }; int rc; fsl_db * const db = fsl_needs_ckout(f); @@ -6416,11 +6391,11 @@ "OR coalesce(origname != pathname, 0) " "/*%s()*/", __func__) || fsl_db_exists(db,"SELECT 1 FROM vmerge /*%s()*/", __func__); } -int fsl__ckout_clear_merge_state( fsl_cx *f ){ +int fsl_ckout_clear_merge_state( fsl_cx *f ){ fsl_db * const d = fsl_needs_ckout(f); int rc; if(d){ rc = fsl_db_exec(d,"DELETE FROM vmerge /*%s()*/", __func__); rc = fsl_cx_uplift_db_error2(f, d, rc); @@ -6494,15 +6469,15 @@ return fsl_cx_err_set(f, FSL_RC_NOT_FOUND, "Directory not found or inaccessible: %s", opt->targetDir); } } - cwd = fsl__cx_scratchpad(f); + cwd = fsl_cx_scratchpad(f); assert(!cwd->used); if((rc = fsl_cx_getcwd(f, cwd))){ assert(!cwd->used); - fsl__cx_scratchpad_yield(f, cwd); + fsl_cx_scratchpad_yield(f, cwd); return fsl_cx_err_set(f, rc, "Error %d [%s]: unable to " "determine current directory.", rc, fsl_rc_cstr(rc)); } /** @@ -6527,17 +6502,17 @@ if(opt->checkForOpenedCkout){ /* Check target and parent dirs for a checkout and bail out if we find one. If opt->checkForOpenedCkout is false then we will use the dbOverwritePolicy to determine what to do if we find a checkout db in cwd (as opposed to a parent). */ - fsl_buffer * const foundAt = fsl__cx_scratchpad(f); + fsl_buffer * const foundAt = fsl_cx_scratchpad(f); if (!fsl_ckout_db_search(fsl_buffer_cstr(cwd), true, foundAt)) { rc = fsl_cx_err_set(f, FSL_RC_ALREADY_EXISTS, "There is already a checkout db at %b", foundAt); } - fsl__cx_scratchpad_yield(f, foundAt); + fsl_cx_scratchpad_yield(f, foundAt); if(rc) goto end; } /** Create and attach ckout db... @@ -6569,11 +6544,11 @@ end: if(opt->targetDir && *opt->targetDir && cwd->used){ fsl_chdir(fsl_buffer_cstr(cwd)) /* Ignoring error because we have no recovery strategy! */; } - fsl__cx_scratchpad_yield(f, cwd); + fsl_cx_scratchpad_yield(f, cwd); if(!rc){ fsl_db * const dbR = fsl_cx_db_for_role(f, FSL_DBROLE_REPO); assert(dbR); assert(dbR->filename && *dbR->filename); rc = fsl_config_set_text(f, FSL_CONFDB_CKOUT, "repository", @@ -6582,22 +6557,21 @@ if(!rc) rc = fsl_update_ckout_dir(f); fsl_buffer_clear(cwd); return rc; } -int fsl__is_locally_modified(fsl_cx * const f, - const char * zFilename, +int fsl_is_locally_modified(fsl_cx * f, const char * zFilename, fsl_size_t origSize, const char * zOrigHash, fsl_int_t zOrigHashLen, fsl_fileperm_e origPerm, int * isModified){ int rc = 0; int const hashLen = zOrigHashLen>=0 ? zOrigHashLen : fsl_is_uuid(zOrigHash); fsl_buffer * hash = 0; - fsl_buffer * fname = fsl__cx_scratchpad(f); + fsl_buffer * fname = fsl_cx_scratchpad(f); fsl_fstat * const fst = &f->cache.fstat; int mod = 0; if(!fsl_is_uuid_len(hashLen)){ return fsl_cx_err_set(f, FSL_RC_RANGE, "%s(): invalid hash length " "%d for file: %s", __func__, hashLen, zFilename); @@ -6638,20 +6612,20 @@ rc = fsl_cx_err_set(f, rc, "%s(): stat() failed for file: %s", __func__, zFilename); } goto end; } - hash = fsl__cx_scratchpad(f); + hash = fsl_cx_scratchpad(f); switch(hashLen){ case FSL_STRLEN_SHA1: rc = fsl_sha1sum_filename(zFilename, hash); break; case FSL_STRLEN_K256: rc = fsl_sha3sum_filename(zFilename, hash); break; default: - fsl__fatal(FSL_RC_UNSUPPORTED, "This cannot happen. %s()", + fsl_fatal(FSL_RC_UNSUPPORTED, "This cannot happen. %s()", __func__); } if(rc){ rc = fsl_cx_err_set(f, rc, "%s: error hashing file: %s", __func__, zFilename); @@ -6662,12 +6636,12 @@ /*MARKER(("%d: %s %s %s\n", *isModified, zOrigHash, (char const *)hash.mem, zFilename));*/ } end: if(!rc && isModified) *isModified = mod; - fsl__cx_scratchpad_yield(f, fname); - if(hash) fsl__cx_scratchpad_yield(f, hash); + fsl_cx_scratchpad_yield(f, fname); + if(hash) fsl_cx_scratchpad_yield(f, hash); return rc; } /** Infrastructure for fsl_repo_ckout(), @@ -6872,11 +6846,11 @@ ? FSL_CKUP_FCHANGE_NONE : FSL_CKUP_FCHANGE_UPDATED; break; } default: - fsl__fatal(FSL_RC_UNSUPPORTED,"Internal error: invalid " + fsl_fatal(FSL_RC_UNSUPPORTED,"Internal error: invalid " "fsl_reco_is_file_modified() response."); } } switch(rec->confirmAnswer.response){ case FSL_CRESPONSE_NO: @@ -6997,11 +6971,11 @@ static int fsl_repo_ckout_rm_list_fini(fsl_cx * f, RepoExtractCkup * rec){ int rc; fsl_db * db = fsl_cx_db_ckout(f); fsl_stmt q = fsl_stmt_empty; - fsl_buffer * absPath = fsl__cx_scratchpad(f); + fsl_buffer * absPath = fsl_cx_scratchpad(f); fsl_size_t const ckdirLen = f->ckout.dirLen; char const *zAbs; int rmCounter = 0; fsl_ckup_opt const * cOpt = rec->cOpt; fsl_ckup_state cuState = fsl_ckup_state_empty; @@ -7112,11 +7086,11 @@ "confirmation callback. " "Filesystem contents may now be " "in an inconsistent state!"); goto end; default: - fsl__fatal(FSL_RC_UNSUPPORTED,"Internal error: invalid " + fsl_fatal(FSL_RC_UNSUPPORTED,"Internal error: invalid " "fsl_cx_confirm() response #%d.", rec->confirmAnswer.response); break; } if(!cOpt->callback) continue; @@ -7174,11 +7148,11 @@ } if(FSL_RC_STEP_DONE==rc) rc = 0; } end: fsl_stmt_finalize(&q); - fsl__cx_scratchpad_yield(f, absPath); + fsl_cx_scratchpad_yield(f, absPath); return fsl_cx_uplift_db_error2(f, db, rc); } int fsl_repo_ckout(fsl_cx * f, fsl_ckup_opt const * cOpt){ int rc = 0; @@ -7188,11 +7162,11 @@ fsl_confirmer oldConfirm = fsl_confirmer_empty; if(!dbR) return f->error.code; else if(!fsl_needs_ckout(f)) return f->error.code; rc = fsl_cx_transaction_begin(f); if(rc) return rc; - rec.tgtDir = fsl__cx_scratchpad(f); + rec.tgtDir = fsl_cx_scratchpad(f); if(cOpt->confirmer.callback){ fsl_cx_confirmer(f, &cOpt->confirmer, &oldConfirm); } //MARKER(("ckout.rid=%d\n",(int)prevRid)); if(prevRid>=0 && cOpt->scanForChanges){ @@ -7279,11 +7253,11 @@ rc = fsl_ckout_manifest_write(f, -1, -1, -1, NULL); end: if(!rc){ rc = fsl_vfile_unload_except(f, cOpt->checkinRid); - if(!rc) rc = fsl__ckout_clear_merge_state(f); + if(!rc) rc = fsl_ckout_clear_merge_state(f); } /* TODO: if "repo-cksum" config db setting is set, confirm R-card of cOpt->checkinRid against on-disk contents. */ @@ -7291,11 +7265,11 @@ fsl_cx_confirmer(f, &oldConfirm, NULL); } fsl_stmt_finalize(&rec.stChanged); fsl_stmt_finalize(&rec.stIsInVfile); fsl_stmt_finalize(&rec.stRidSize); - fsl__cx_scratchpad_yield(f, rec.tgtDir); + fsl_cx_scratchpad_yield(f, rec.tgtDir); int const rc2 = fsl_cx_transaction_end(f, rc || cOpt->dryRun); return rc ? rc : rc2; } int fsl_ckout_update(fsl_cx * f, fsl_ckup_opt const *cuOpt){ @@ -7517,11 +7491,11 @@ uState.dryRun = cuOpt->dryRun; uState.fileRmInfo = FSL_CKUP_RM_NOT; rec.originRid = ckRid; rec.eOpt = &eOpt; rec.cOpt = cuOpt; - rec.tgtDir = fsl__cx_scratchpad(f); + rec.tgtDir = fsl_cx_scratchpad(f); rec.tgtDirLen = f->ckout.dirLen; rc = fsl_buffer_append(rec.tgtDir, f->ckout.dir, (fsl_int_t)f->ckout.dirLen); if(rc) goto end; @@ -7532,13 +7506,13 @@ - file_unsafe_in_tree_path() (done, untested) - file_nondir_objects_on_path() (done, untested) - symlink_create() (done, untested) - ... */ - bFullPath = fsl__cx_scratchpad(f); - bFullNewPath = fsl__cx_scratchpad(f); - bFileUuid = fsl__cx_scratchpad(f); + bFullPath = fsl_cx_scratchpad(f); + bFullNewPath = fsl_cx_scratchpad(f); + bFileUuid = fsl_cx_scratchpad(f); rc = fsl_buffer_append(bFullPath, f->ckout.dir, (fsl_int_t)f->ckout.dirLen); if(rc) goto end; rc = fsl_buffer_append(bFullNewPath, f->ckout.dir, (fsl_int_t)f->ckout.dirLen); @@ -7619,11 +7593,11 @@ //fsl_outputf(f, "ADD %s\n", zName); uState.fileChangeType = FSL_CKUP_FCHANGE_ADDED; } //if( !dryRunFlag && !internalUpdate ) undo_save(zName); if( !cuOpt->dryRun ){ - rc = fsl__vfile_to_ckout(f, idt, &wasWritten); + rc = fsl_vfile_to_ckout(f, idt, &wasWritten); if(rc) goto end; } }else if( idt>0 && idv>0 && ridt!=ridv && (chnged==0 || deleted) ){ /* The file is unedited. Change it to the target version */ if( deleted ){ @@ -7632,20 +7606,20 @@ }else{ //fossil_print("UPDATE %s\n", zName); uState.fileChangeType = FSL_CKUP_FCHANGE_UPDATED; } if( !cuOpt->dryRun ){ - rc = fsl__vfile_to_ckout(f, idt, &wasWritten); + rc = fsl_vfile_to_ckout(f, idt, &wasWritten); if(rc) goto end; } }else if( idt>0 && idv>0 && !deleted && 0!=fsl_stat(zFullPath, NULL, false) ){ /* The file is missing from the local check-out. Restore it to ** the version that appears in the target. */ uState.fileChangeType = FSL_CKUP_FCHANGE_UPDATED; if( !cuOpt->dryRun ){ - rc = fsl__vfile_to_ckout(f, idt, &wasWritten); + rc = fsl_vfile_to_ckout(f, idt, &wasWritten); if(rc) goto end; } }else if( idt==0 && idv>0 ){ /* Is in the current version but not in the target. */ if( ridv==0 ){ @@ -7814,20 +7788,20 @@ rc = fsl_repo_ckout_rm_list_fini(f, &rec); if(!rc){ rc = fsl_vfile_unload_except(f, tid); } if(!rc){ - rc = fsl__ckout_version_write(f, tid, 0); + rc = fsl_ckout_version_write(f, tid, 0); } end: /* clang bug? If we declare rc2 here, it says "expression expected". Moving the decl to the top resolves it. Wha? */ - if(rec.tgtDir) fsl__cx_scratchpad_yield(f, rec.tgtDir); - if(bFullPath) fsl__cx_scratchpad_yield(f, bFullPath); - if(bFullNewPath) fsl__cx_scratchpad_yield(f, bFullNewPath); - if(bFileUuid) fsl__cx_scratchpad_yield(f, bFileUuid); + if(rec.tgtDir) fsl_cx_scratchpad_yield(f, rec.tgtDir); + if(bFullPath) fsl_cx_scratchpad_yield(f, bFullPath); + if(bFullNewPath) fsl_cx_scratchpad_yield(f, bFullNewPath); + if(bFileUuid) fsl_cx_scratchpad_yield(f, bFileUuid); for(int i = 0; i < MergeBufCount; ++i){ fsl_buffer_clear(&bufMerge[i]); } fsl_stmt_finalize(&rec.stRidSize); fsl_stmt_finalize(&rec.stChanged); @@ -7896,11 +7870,11 @@ FSL_TAGID_BRANCH, FSL_TAGID_BRANCH, ckRid ); if(rc) goto end; else if( fsl_leaves_computed_count(f)>1 ){ AmbiguousLeavesOutput alo = AmbiguousLeavesOutput_empty; - alo.buffer = fsl__cx_scratchpad(f); + alo.buffer = fsl_cx_scratchpad(f); rc = fsl_buffer_append(alo.buffer, "Multiple viable descendants found: ", -1); if(!rc){ fsl_stmt q = fsl_stmt_empty; rc = fsl_db_prepare(dbRepo, &q, "SELECT uuid FROM blob " @@ -7911,11 +7885,11 @@ fsl_stmt_finalize(&q); } if(!rc){ rc = fsl_cx_err_set(f, FSL_RC_AMBIGUOUS, "%b", alo.buffer); } - fsl__cx_scratchpad_yield(f, alo.buffer); + fsl_cx_scratchpad_yield(f, alo.buffer); } end: if(!rc){ tgtRid = fsl_leaves_computed_latest(f); *outRid = tgtRid; @@ -7973,11 +7947,11 @@ return fsl_cx_err_set(f, FSL_RC_RANGE, "Checkout RID is 0, so it has no manifest."); } int W = 0; int rc = 0; - fsl_buffer * b = fsl__cx_scratchpad(f); + fsl_buffer * b = fsl_cx_scratchpad(f); fsl_buffer * content = &f->fileContent; char * str = 0; fsl_time_t const mtime = f->ckout.mtime>0 ? fsl_julian_to_unix(f->ckout.mtime) : 0; @@ -8090,11 +8064,11 @@ fsl_file_unlink(fsl_buffer_cstr(b)); } end: if(wrote) *wrote = W; - fsl__cx_scratchpad_yield(f, b); + fsl_cx_scratchpad_yield(f, b); fsl_buffer_reuse(content); return rc; } /** @@ -8130,11 +8104,11 @@ int fsl_ckout_nondir_file_check(fsl_cx *f, char const * zFilename, fsl_size_t * errLen){ if(!fsl_needs_ckout(f)) return FSL_RC_NOT_A_CKOUT; int rc = 0; int frc; - fsl_buffer * const fn = fsl__cx_scratchpad(f); + fsl_buffer * const fn = fsl_cx_scratchpad(f); if(!fsl_is_rooted_in_ckout(f, zFilename)){ assert(!"Misuse of this API. This condition should never fail."); rc = fsl_cx_err_set(f, FSL_RC_MISUSE, "Path is not rooted at the " "current checkout directory: %s", zFilename); goto end; @@ -8163,18 +8137,18 @@ } z[j] = '/'; i = j; } end: - fsl__cx_scratchpad_yield(f, fn); + fsl_cx_scratchpad_yield(f, fn); return rc; } -int fsl__ckout_safe_file_check(fsl_cx * const f, char const * zFilename){ +int fsl_ckout_safe_file_check(fsl_cx *f, char const * zFilename){ if(!fsl_needs_ckout(f)) return FSL_RC_NOT_A_CKOUT; int rc = 0; - fsl_buffer * const fn = fsl__cx_scratchpad(f); + fsl_buffer * const fn = fsl_cx_scratchpad(f); if(!fsl_is_absolute_path(zFilename)){ rc = fsl_file_canonical_name2(f->ckout.dir, zFilename, fn, false); if(rc) goto end; zFilename = fsl_buffer_cstr(fn); }else if(!fsl_is_rooted_in_ckout(f, zFilename)){ @@ -8190,11 +8164,11 @@ rc = fsl_cx_err_set(f, FSL_RC_TYPE, "Directory part of path refers " "to a non-directory: %.*s", (int)errLen, zFilename); } end: - fsl__cx_scratchpad_yield(f, fn); + fsl_cx_scratchpad_yield(f, fn); return rc; } bool fsl_is_rooted_in_ckout(fsl_cx *f, char const *zAbsPath){ return f->ckout.dir @@ -8213,30 +8187,30 @@ zAbsPath); } return rc; } -int fsl__ckout_symlink_create(fsl_cx * const f, char const *zTgtFile, +int fsl_ckout_symlink_create(fsl_cx * f, char const *zTgtFile, char const * zLinkFile){ if(!fsl_needs_ckout(f)) return FSL_RC_NOT_A_CKOUT; int rc = 0; - fsl_buffer * const fn = fsl__cx_scratchpad(f); + fsl_buffer * const fn = fsl_cx_scratchpad(f); if(!fsl_is_absolute_path(zLinkFile)){ rc = fsl_file_canonical_name2(f->ckout.dir, zLinkFile, fn, false); if(rc) goto end; zLinkFile = fsl_buffer_cstr(fn); }else if(0!=(rc = fsl_is_rooted_in_ckout2(f, zLinkFile))){ goto end; } - fsl_buffer * const b = fsl__cx_scratchpad(f); + fsl_buffer * const b = fsl_cx_scratchpad(f); rc = fsl_buffer_append(b, zTgtFile, -1); if(!rc){ rc = fsl_buffer_to_filename(b, fsl_buffer_cstr(fn)); } - fsl__cx_scratchpad_yield(f, b); + fsl_cx_scratchpad_yield(f, b); end: - fsl__cx_scratchpad_yield(f, fn); + fsl_cx_scratchpad_yield(f, fn); return rc; } /** Queues the directory part of the given filename into temp table @@ -8271,11 +8245,11 @@ ignores rmdir failure but will return non-0 for db errors. */ static int fsl_revert_rmdir_fini(fsl_cx * f, fsl_db * db){ int rc; fsl_stmt st = fsl_stmt_empty; - fsl_buffer * const b = fsl__cx_scratchpad(f); + fsl_buffer * const b = fsl_cx_scratchpad(f); rc = fsl_db_prepare(db, &st, "SELECT fsl_ckout_dir()||n " "FROM fx_revert_rmdir " "ORDER BY length(n) DESC /* %s() */", __func__); @@ -8287,20 +8261,19 @@ rc = fsl_buffer_append(b, zDir, (fsl_int_t)nDir); if(rc) break; fsl_ckout_rm_empty_dirs(f, b); } end: - fsl__cx_scratchpad_yield(f, b); + fsl_cx_scratchpad_yield(f, b); fsl_stmt_finalize(&st); return rc; dberr: rc = fsl_cx_uplift_db_error2(f, db, rc); goto end; } -int fsl_ckout_revert( fsl_cx * const f, - fsl_ckout_revert_opt const * opt ){ +int fsl_ckout_revert( fsl_cx * f, fsl_ckout_revert_opt const * opt ){ /** Reminder to whoever works on this code: the initial implementation was done almost entirely without the benefit of looking at fossil's implementation, thus this code is notably different from fossil's. If any significant misbehaviors are @@ -8318,15 +8291,15 @@ fsl_stmt qRmdir = fsl_stmt_empty; fsl_buffer * sql = 0; if(!db) return FSL_RC_NOT_A_CKOUT; assert(vid>=0); if(!opt->vfileIds && opt->filename && *opt->filename){ - fname = fsl__cx_scratchpad(f); + fname = fsl_cx_scratchpad(f); rc = fsl_ckout_filename_check(f, opt->relativeToCwd, opt->filename, fname); if(rc){ - fsl__cx_scratchpad_yield(f, fname); + fsl_cx_scratchpad_yield(f, fname); return rc; } zNorm = fsl_buffer_cstr(fname); /* MARKER(("fsl_ckout_unmanage(%d, %s) ==> %s\n", opt->relativeToCwd, opt->filename, zNorm)); */ assert(zNorm); @@ -8342,11 +8315,11 @@ inTrans = true; if(opt->scanForChanges){ rc = fsl_vfile_changes_scan(f, 0, 0); if(rc) goto end; } - sql = fsl__cx_scratchpad(f); + sql = fsl_cx_scratchpad(f); rc = fsl_buffer_appendf(sql, "SELECT id, rid, deleted, " "fsl_ckout_dir()||pathname, " "fsl_ckout_dir()||origname " "FROM vfile WHERE vid=%" FSL_ID_T_PFMT " ", @@ -8376,15 +8349,15 @@ " <>pathname" ")", -1); } assert(!rc); rc = fsl_db_prepare(db, &q, "%b /* %s() */", sql, __func__); - fsl__cx_scratchpad_yield(f, sql); + fsl_cx_scratchpad_yield(f, sql); sql = 0; if(rc) goto dberr; if((!zNorm || !*zNorm) && !opt->vfileIds){ - rc = fsl__ckout_clear_merge_state(f); + rc = fsl_ckout_clear_merge_state(f); if(rc) goto end; } while((FSL_RC_STEP_ROW==fsl_stmt_step(&q))){ fsl_id_t const id = fsl_stmt_g_id(&q, 0); fsl_id_t const rid = fsl_stmt_g_id(&q, 1); @@ -8406,11 +8379,11 @@ rc = fsl_cx_err_set(f, rc, "mkdir() failed for file: %s", zNameOrig); break; } /* Move, if possible, the new name back over the original - name. This will possibly allow fsl__vfile_to_ckout() to + name. This will possibly allow fsl_vfile_to_ckout() to avoid having to load that file's contents and overwrite it. */ int mvCheck = fsl_stat(zName, NULL, false); if(0==mvCheck || FSL_RC_NOT_FOUND==mvCheck){ mvCheck = fsl_file_unlink(zNameOrig); @@ -8432,14 +8405,14 @@ "origname=NULL " "WHERE id=?1 /*%s()*/", __func__); if(rc) goto dberr; } rc = fsl_stmt_bind_step(&vfUpdate, "R", id) - /* Has to be done before fsl__vfile_to_ckout() because that + /* Has to be done before fsl_vfile_to_ckout() because that function writes to vfile.pathname. */; if(rc) goto dberr; - rc = fsl__vfile_to_ckout(f, id, &wasWritten); + rc = fsl_vfile_to_ckout(f, id, &wasWritten); if(rc) break; if(opt->callback){ if(renamed){ changeType = FSL_REVERT_RENAME; }else if(wasWritten){ @@ -8457,12 +8430,12 @@ changeType, opt->callbackState); if(rc) break; } }/*step() loop*/ end: - if(fname) fsl__cx_scratchpad_yield(f, fname); - if(sql) fsl__cx_scratchpad_yield(f, sql); + if(fname) fsl_cx_scratchpad_yield(f, fname); + if(sql) fsl_cx_scratchpad_yield(f, sql); fsl_stmt_finalize(&q); fsl_stmt_finalize(&vfUpdate); if(qRmdir.stmt){ fsl_stmt_finalize(&qRmdir); if(!rc) rc = fsl_revert_rmdir_fini(f, db); @@ -8483,32 +8456,32 @@ assert(rc); rc = fsl_cx_uplift_db_error2(f, db, rc); goto end; } -int fsl_ckout_vfile_ids( fsl_cx * const f, fsl_id_t vid, - fsl_id_bag * const dest, char const * zName, +int fsl_ckout_vfile_ids( fsl_cx * f, fsl_id_t vid, + fsl_id_bag * dest, char const * zName, bool relativeToCwd, bool changedOnly ) { if(!fsl_needs_ckout(f)) return FSL_RC_NOT_A_CKOUT; - fsl_buffer * const canon = fsl__cx_scratchpad(f); + fsl_buffer * const canon = fsl_cx_scratchpad(f); int rc = fsl_ckout_filename_check(f, relativeToCwd, zName, canon); if(!rc){ fsl_buffer_strip_slashes(canon); rc = fsl_filename_to_vfile_ids(f, vid, dest, fsl_buffer_cstr(canon), changedOnly); } - fsl__cx_scratchpad_yield(f, canon); + fsl_cx_scratchpad_yield(f, canon); return rc; } int fsl_ckout_file_content(fsl_cx * const f, bool relativeToCwd, char const * zName, fsl_buffer * const dest ){ int rc; fsl_buffer * fname; if(!fsl_needs_ckout(f)) return FSL_RC_NOT_A_CKOUT; - fname = fsl__cx_scratchpad(f); + fname = fsl_cx_scratchpad(f); rc = fsl_file_canonical_name2( relativeToCwd ? NULL : fsl_cx_ckout_dir_name(f, NULL), zName, fname, 1 ); if(!rc){ @@ -8534,11 +8507,11 @@ fsl_rc_cstr(rc), fname); } } } } - fsl__cx_scratchpad_yield(f, fname); + fsl_cx_scratchpad_yield(f, fname); return rc; } int fsl_card_F_ckout_mtime(fsl_cx * const f, fsl_id_t vid, @@ -8711,11 +8684,11 @@ Only(?) here for testing purposes. We don't really need/want to update the repo db on each open of the checkout db, do we? Or do we? */ - fsl__repo_record_filename(f) /* ignore rc - not critical */; + fsl_repo_record_filename(f) /* ignore rc - not critical */; #endif } return rc; } @@ -9227,11 +9200,11 @@ Proxies fslAllocOrig.f() and abort()s on OOM conditions. */ static void * fsl_realloc_f_failing(void * state, void * mem, fsl_size_t n){ void * rv = fslAllocOrig.f(fslAllocOrig.state, mem, n); if(n && !rv){ - fsl__fatal(FSL_RC_OOM, NULL)/*does not return*/; + fsl_fatal(FSL_RC_OOM, NULL)/*does not return*/; } return rv; } /** @@ -9242,55 +9215,18 @@ static const fsl_allocator fcli_allocator = { fsl_realloc_f_failing, NULL/*state*/ }; -#if !defined(FCLI_USE_SIGACTION) -# if (defined(_POSIX_C_SOURCE) || defined(sa_sigaction/*BSD*/)) \ - && defined(HAVE_SIGACTION) -/* ^^^ on Linux, sigaction() is only available in - if _POSIX_C_SOURCE is set */ -# define FCLI_USE_SIGACTION HAVE_SIGACTION -# else -# define FCLI_USE_SIGACTION 0 -# endif -#endif - -#if FCLI_USE_SIGACTION -#include /* sigaction(), if our feature macros are set right */ -/** - SIGINT handler which calls fsl_cx_interrupt(). -*/ -static void fcli__sigc_handler(int s){ - static fsl_cx * f = 0; - if(f) return/*disable concurrent interruption*/; - f = fcli_cx(); - if(f && !fsl_cx_interrupted(f)){ - //f_out("^C\n"); // no - this would interfere with curses apps - fsl_cx_interrupt(f, FSL_RC_INTERRUPTED, - "Interrupted by signal #%d.", s); - f = NULL; - } -} -#endif -/* ^^^ FCLI_USE_SIGACTION */ - void fcli_pre_setup(void){ static int run = 0; if(run++) return; fslAllocOrig = fsl_lib_configurable.allocator; fsl_lib_configurable.allocator = fcli_allocator /* This MUST be done BEFORE the fsl API allocates ANY memory! */; atexit(fcli_shutdown); -#if FCLI_USE_SIGACTION - struct sigaction sigIntHandler; - sigIntHandler.sa_handler = fcli__sigc_handler; - sigemptyset(&sigIntHandler.sa_mask); - sigIntHandler.sa_flags = 0; - sigaction(SIGINT, &sigIntHandler, NULL); -#endif } /** oldMode must be true if fcli.cliFlags is NULL, else false. */ static int fcli_setup_common1(bool oldMode, int argc, char const * const *argv){ @@ -9360,11 +9296,11 @@ if(!rc){ char const * userName = fcli.transient.userArg; if(userName){ fsl_cx_user_set(f, userName); }else if(!fsl_cx_user_get(f)){ - char * u = fsl_user_name_guess(); + char * u = fsl_guess_user_name(); fsl_cx_user_set(f, u); fsl_free(u); } } return rc; @@ -9433,14 +9369,10 @@ int fcli_err_report2(bool clear, char const * file, int line){ int errRc = 0; char const * msg = NULL; errRc = fsl_error_get( fcli__error, &msg, NULL ); - if(!errRc && fcli.f && fcli.f->interrupted){ - errRc = fcli.f->interrupted; - msg = "Interrupted."; - } if(FCLI_RC_HELP==errRc){ errRc = 0; }else if(errRc || msg){ if(fcli.clientFlags.verbose>0){ fcli_printf("%s %s:%d: ERROR #%d (%s): %s\n", @@ -9449,14 +9381,11 @@ }else{ fcli_printf("%s: ERROR #%d (%s): %s\n", fcli.appName, errRc, fsl_rc_cstr(errRc), msg); } } - if(clear){ - fcli_err_reset(); - if(fcli.f) fsl_cx_interrupt(fcli.f, 0, NULL); - } + if(clear) fcli_err_reset(); return errRc; } const char * fcli_next_arg(bool remove){ @@ -9900,29 +9829,14 @@ (int)st->cachedHits, &st->sql); } } } -void fcli_dump_cache_metrics(void){ - fsl_cx * const f = fcli.f; - if(!f) return; - f_out("fsl_cx::cache::mcache hits = %u misses = %u\n", - f->cache.mcache.hits, - f->cache.mcache.misses); - f_out("fsl_cx::cache::blobContent hits = %u misses = %u. " - "Entry count=%u totaling %u byte(s).\n", - f->cache.blobContent.metrics.hits, - f->cache.blobContent.metrics.misses, - f->cache.blobContent.used, - f->cache.blobContent.szTotal); -} - #undef FCLI_V3 #undef fcli_empty_m #undef fcli__error #undef MARKER -#undef FCLI_USE_SIGACTION /* end of file cli.c */ /* start of file content.c */ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* @@ -9977,38 +9891,38 @@ bool fsl_content_is_available(fsl_cx * f, fsl_id_t rid){ fsl_id_t srcid = 0; int rc = 0, depth = 0 /* Limit delta recursion depth */; while( depth++ < 100000 ){ - if( fsl_id_bag_contains(&f->cache.blobContent.missing, rid) ){ + if( fsl_id_bag_contains(&f->cache.arty.missing, rid) ){ return false; - }else if( fsl_id_bag_contains(&f->cache.blobContent.available, rid) ){ + }else if( fsl_id_bag_contains(&f->cache.arty.available, rid) ){ return true; }else if( fsl_content_size(f, rid)<0 ){ - fsl_id_bag_insert(&f->cache.blobContent.missing, rid) + fsl_id_bag_insert(&f->cache.arty.missing, rid) /* ignore possible OOM error */; return false; } rc = fsl_delta_src_id(f, rid, &srcid); if(rc) break; else if( 0==srcid ){ - fsl_id_bag_insert(&f->cache.blobContent.available, rid); + fsl_id_bag_insert(&f->cache.arty.available, rid); return true; } rid = srcid; } if(0==rc){ /* This "cannot happen" (never has historically, and would be indicative of what amounts to corruption in the repo). */ - fsl__fatal(FSL_RC_RANGE,"delta-loop in repository"); + fsl_fatal(FSL_RC_RANGE,"delta-loop in repository"); } return false; } -int fsl_content_blob( fsl_cx * const f, fsl_id_t blobRid, fsl_buffer * const tgt ){ +int fsl_content_blob( fsl_cx * f, fsl_id_t blobRid, fsl_buffer * tgt ){ fsl_db * const dbR = fsl_cx_db_repo(f); if(blobRid<=0) return FSL_RC_RANGE; else if(!dbR) return FSL_RC_NOT_A_REPO; else{ int rc; @@ -10085,33 +9999,31 @@ } else{ int rc; bool gotIt = 0; fsl_id_t nextRid; - fsl__bccache * const ac = &f->cache.blobContent; + fsl_acache * const ac = &f->cache.arty; fsl_buffer_reuse(tgt); if(fsl_id_bag_contains(&ac->missing, rid)){ /* Early out if we know the content is not available */ return FSL_RC_NOT_FOUND; } /* Look for the artifact in the cache first */ - if(0!=(FSL_CX_F_BLOB_CACHE & f->flags) - && fsl_id_bag_contains(&ac->inCache, rid) ){ + if( fsl_id_bag_contains(&ac->inCache, rid) ){ fsl_size_t i; - fsl__bccache_line * line; + fsl_acache_line * line; for(i=0; iused; ++i){ line = &ac->list[i]; if( line->rid==rid ){ - ++ac->metrics.hits; - rc = fsl_buffer_copy(tgt, &line->content); + rc = fsl_buffer_copy(&line->content, tgt); line->age = ac->nextAge++; return rc; } } } - ++ac->metrics.misses; + nextRid = 0; rc = fsl_delta_src_id(f, rid, &nextRid); /* MARKER(("rc=%d, nextRid=%"FSL_ID_T_PFMT"\n", rc, nextRid)); */ if(rc) return rc; if( nextRid == 0 ){ @@ -10200,23 +10112,22 @@ 10.5s without this cache 5.2s with this cache We shave another 0.5s if we always cache instead of using this mysterious (mx-n)%8 heuristic. - - Later testing with f-rebuild gives much different results: - the (mx-n)%8 heuristic provides the best results of the - variations tested, including always caching. */ //MARKER(("mx=%d, n=%d, (mx-n)%%8=%d\n", //(int)mx, (int)n, (int)(mx-n)%8)); //MARKER(("nAlloc=%d\n", (int)nAlloc)); if( (mx-n)%8==0 ){ //MARKER(("Caching artifact %d\n", (int)a[n+1])); - fsl__bccache_insert( ac, a[n+1], tgt ) - /*Ignoring error (OOM) - it's not (yet) fatal. */; - assert(!tgt->mem && "Passed to artifact cache (even on failure)."); + rc = fsl_acache_insert( ac, a[n+1], tgt ); + if(rc){ + fsl_buffer_clear(&next); + goto end_delta; + } + assert(!tgt->mem && "Passed to artifact cache."); }else{ fsl_buffer_clear(tgt); } #else if(mx){/*unused var*/} @@ -10230,12 +10141,12 @@ gotIt = 0==rc; } if(!rc){ rc = fsl_id_bag_insert(gotIt - ? &f->cache.blobContent.available - : &f->cache.blobContent.missing, + ? &f->cache.arty.available + : &f->cache.arty.missing, rid); } return rc; } } @@ -10255,26 +10166,26 @@ Mark artifact rid as being available now. Update f's cache to show that everything that was formerly unavailable because rid was missing is now available. Returns 0 on success. f must have an opened repo and rid must be valid. */ -static int fsl_content_mark_available(fsl_cx * const f, fsl_id_t rid){ +static int fsl_content_mark_available(fsl_cx * f, fsl_id_t rid){ fsl_id_bag pending = fsl_id_bag_empty; int rc; fsl_stmt * st = NULL; fsl_db * db = fsl_cx_db_repo(f); assert(f); assert(db); assert(rid>0); - if( fsl_id_bag_contains(&f->cache.blobContent.available, rid) ) return 0; + if( fsl_id_bag_contains(&f->cache.arty.available, rid) ) return 0; rc = fsl_id_bag_insert(&pending, rid); if(rc) goto end; - while( 0==rc && (rid = fsl_id_bag_first(&pending))!=0 ){ + while( (rid = fsl_id_bag_first(&pending))!=0 ){ fsl_id_bag_remove(&pending, rid); - rc = fsl_id_bag_insert(&f->cache.blobContent.available, rid); + rc = fsl_id_bag_insert(&f->cache.arty.available, rid); if(rc) goto end; - fsl_id_bag_remove(&f->cache.blobContent.missing, rid); + fsl_id_bag_remove(&f->cache.arty.missing, rid); if(!st){ rc = fsl_db_prepare_cached(db, &st, "SELECT rid FROM delta " "WHERE srcid=?" "/*%s()*/",__func__); @@ -10284,10 +10195,11 @@ while( !rc && (FSL_RC_STEP_ROW==fsl_stmt_step(st)) ){ fsl_id_t const nx = fsl_stmt_g_id(st,0); assert(nx>0); rc = fsl_id_bag_insert(&pending, nx); } + } end: if(st) fsl_stmt_cached_yield(st); fsl_id_bag_clear(&pending); return rc; @@ -10294,15 +10206,15 @@ } /** When a record is converted from a phantom to a real record, if that record has other records that are derived by delta, then call - fsl__deck_crosslink() on those other records. + fsl_deck_crosslink() on those other records. If the formerly phantom record or any of the other records derived by delta from the former phantom are a baseline manifest, then also - invoke fsl__deck_crosslink() on the delta-manifests associated with + invoke fsl_deck_crosslink() on the delta-manifests associated with that baseline. Tail recursion is used to minimize stack depth. Returns 0 on success, any number of non-0 results on error. @@ -10328,11 +10240,11 @@ if(doCrosslink){ fsl_deck deck = fsl_deck_empty; rc = fsl_deck_load_rid(f, &deck, rid, FSL_SATYPE_ANY); if(!rc){ assert(aChild[i]==deck.rid); - rc = fsl__deck_crosslink(&deck); + rc = fsl_deck_crosslink(&deck); } fsl_deck_finalize(&deck); if(rc) break; } /* Parse all delta-manifests that depend on baseline-manifest rid */ @@ -10354,11 +10266,11 @@ for(i=0; icache.blobContent.missing, rid) && - (srcId==0 || (0==fsl__bccache_check_available(f,srcId)))){ + && fsl_id_bag_contains(&f->cache.arty.missing, rid) && + (srcId==0 || (0==fsl_acache_check_available(f,srcId)))){ /* TODO: document what this is for. TODO: figure out what that is. */ rc = fsl_content_mark_available(f, rid); @@ -10681,11 +10593,11 @@ rc = fsl_db_exec(dbR, "INSERT OR IGNORE INTO unsent " "VALUES(%"FSL_ID_T_PFMT")", rid); if(rc) goto end; } - rc = fsl__repo_verify_before_commit(f, rid); + rc = fsl_repo_verify_before_commit(f, rid); if(rc) goto end /* FSL_RC_OOM is basically the "only possible" failure after this point. */; /* Code after end: relies on the following 2 lines: */ rc = fsl_db_transaction_end(dbR, false); inTrans = false; @@ -10702,12 +10614,12 @@ fsl_buffer_clear(&cmpr); }/* else cmpr.mem (if any) belongs to pBlob */ return rc; } -int fsl__content_put( fsl_cx * const f, fsl_buffer const * pBlob, fsl_id_t * newRid){ - return fsl__content_put_ex(f, pBlob, NULL, 0, 0, 0, newRid); +int fsl_content_put( fsl_cx * const f, fsl_buffer const * pBlob, fsl_id_t * newRid){ + return fsl_content_put_ex(f, pBlob, NULL, 0, 0, 0, newRid); } int fsl_uuid_is_shunned(fsl_cx * const f, fsl_uuid_cstr zUuid){ fsl_db * db = fsl_cx_db_repo(f); if( !db || zUuid==0 || zUuid[0]==0 ) return 0; @@ -10719,11 +10631,11 @@ return 1==fsl_db_g_int32( db, 0, "SELECT 1 FROM shun WHERE uuid=%Q", zUuid); } -int fsl__content_new( fsl_cx * f, fsl_uuid_cstr uuid, bool isPrivate, +int fsl_content_new( fsl_cx * f, fsl_uuid_cstr uuid, bool isPrivate, fsl_id_t * newId ){ fsl_id_t rid = 0; int rc; fsl_db * db = fsl_cx_db_repo(f); fsl_stmt * s1 = NULL, * s2 = NULL; @@ -10778,11 +10690,11 @@ if(FSL_RC_STEP_DONE!=rc) goto end; else rc = 0; } } - if(!rc) rc = fsl_id_bag_insert(&f->cache.blobContent.missing, rid); + if(!rc) rc = fsl_id_bag_insert(&f->cache.arty.missing, rid); end: if(rc){ if(db->error.code && !f->error.code){ fsl_cx_uplift_db_error(f, db); @@ -10797,11 +10709,11 @@ } } return rc; } -int fsl__content_undeltify(fsl_cx * const f, fsl_id_t rid){ +int fsl_content_undeltify(fsl_cx * const f, fsl_id_t rid){ int rc; fsl_db * db = f ? fsl_cx_db_repo(f) : NULL; fsl_id_t srcid = 0; fsl_buffer x = fsl_buffer_empty; fsl_stmt s = fsl_stmt_empty; @@ -10853,11 +10765,11 @@ Potential TODO: f->flags FSL_CX_F_PEDANTIC_VERIFICATION, which enables the R-card and this check, and any similarly superfluous ones. */ - if(!rc) fsl__repo_verify_before_commit(f, rid); + if(!rc) fsl_repo_verify_before_commit(f, rid); #endif end: fsl_buffer_clear(&x); fsl_stmt_finalize(&s); if(rc) fsl_db_transaction_rollback(db); @@ -10867,11 +10779,11 @@ assert(rc); rc = fsl_cx_uplift_db_error2(f, db, rc); goto end; } -int fsl__content_deltify(fsl_cx * f, fsl_id_t rid, +int fsl_content_deltify(fsl_cx * f, fsl_id_t rid, fsl_id_t srcid, bool force){ fsl_id_t s; fsl_buffer data = fsl_buffer_empty; fsl_buffer src = fsl_buffer_empty; fsl_buffer delta = fsl_buffer_empty; @@ -10915,11 +10827,11 @@ */ s = srcid; while( (0==(rc=fsl_delta_src_id(f, s, &s))) && (s>0) ){ if( s==rid ){ - rc = fsl__content_undeltify(f, srcid); + rc = fsl_content_undeltify(f, srcid); break; } } if(rc) return rc; /* As of here, don't return on error. Use (goto end) instead, or be @@ -10963,11 +10875,11 @@ } } } fsl_stmt_cached_yield(s1); fsl_stmt_cached_yield(s2); - if(!rc) fsl__repo_verify_before_commit(f, rid); + if(!rc) fsl_repo_verify_before_commit(f, rid); } end: if(rc && db->error.code && !f->error.code){ fsl_cx_uplift_db_error(f,db); } @@ -10979,11 +10891,11 @@ /** Removes all entries from the repo's blob table which are listed in the shun table. */ -int fsl__repo_shun_artifacts(fsl_cx * const f){ +int fsl_repo_shun_artifacts(fsl_cx * f){ fsl_stmt q = fsl_stmt_empty; int rc; fsl_db * db = f ? fsl_cx_db_repo(f) : NULL; if(!f) return FSL_RC_MISUSE; else if(!db) return FSL_RC_NOT_A_REPO; @@ -11003,11 +10915,11 @@ "SELECT rid FROM delta WHERE srcid IN toshun" ); if(rc) goto end; while( !rc && (FSL_RC_STEP_ROW==fsl_stmt_step(&q)) ){ fsl_id_t const srcid = fsl_stmt_g_id(&q, 0); - rc = fsl__content_undeltify(f, srcid); + rc = fsl_content_undeltify(f, srcid); } fsl_stmt_finalize(&q); if(!rc){ rc = fsl_db_exec_multi(db, "DELETE FROM delta WHERE rid IN toshun;" @@ -11224,12 +11136,12 @@ fsl_db * db = f ? fsl_needs_repo(f) : NULL; char inTrans = 0; if(!zName || !*zName) return FSL_RC_MISUSE; else if(!f->ckout.dir) return FSL_RC_NOT_A_CKOUT; else if(!db) return FSL_RC_NOT_A_REPO; - canon = fsl__cx_scratchpad(f); - nbuf = fsl__cx_scratchpad(f); + canon = fsl_cx_scratchpad(f); + nbuf = fsl_cx_scratchpad(f); assert(!fbuf->used && "Misuse of f->fileContent"); assert(f->ckout.dir); /* Normalize the name... i often regret having @@ -11243,11 +11155,11 @@ rc = fsl_db_transaction_begin(db); if(rc) goto end; inTrans = 1; - rc = fsl__repo_filename_fnid2(f, fn, &fnid, 1); + rc = fsl_repo_filename_fnid2(f, fn, &fnid, 1); if(rc) goto end; /* Import the file... */ assert(fnid>0); rc = fsl_buffer_appendf(nbuf, "%s%s", f->ckout.dir, fn); @@ -11258,16 +11170,16 @@ fsl_cx_err_set(f, rc, "Error %s importing file: %s", fsl_rc_cstr(rc), fn); goto end; } fn = NULL; - rc = fsl__content_put( f, fbuf, &rcRid ); + rc = fsl_content_put( f, fbuf, &rcRid ); if(!rc){ assert(rcRid > 0); if(parentRid>0){ /* Make parent version a delta of this one, if possible... */ - rc = fsl__content_deltify(f, parentRid, rcRid, 0); + rc = fsl_content_deltify(f, parentRid, rcRid, 0); } if(!rc){ if(rid) *rid = rcRid; if(uuid){ fsl_cx_err_reset(f); @@ -11282,14 +11194,14 @@ inTrans = 0; rc = fsl_db_transaction_commit(db); } end: - fsl__cx_content_buffer_yield(f); + fsl_cx_content_buffer_yield(f); assert(0==fbuf->used); - fsl__cx_scratchpad_yield(f, canon); - fsl__cx_scratchpad_yield(f, nbuf); + fsl_cx_scratchpad_yield(f, canon); + fsl_cx_scratchpad_yield(f, nbuf); if(inTrans) fsl_db_transaction_rollback(db); return rc; } fsl_hash_types_e fsl_validate_hash(const char *zHash, int nHash){ @@ -11356,11 +11268,11 @@ if(rc) goto end; rc = fsl_cx_prepare(f, &q, "SELECT rid FROM delta WHERE srcid IN toshun" ); while( 0==rc && FSL_RC_STEP_ROW==fsl_stmt_step(&q) ){ - rc = fsl__content_undeltify(f, fsl_stmt_g_id(&q, 0)); + rc = fsl_content_undeltify(f, fsl_stmt_g_id(&q, 0)); } fsl_stmt_finalize(&q); if(rc) goto end; rc = fsl_cx_exec_multi(f, "DELETE FROM delta WHERE rid IN toshun;" @@ -11409,19 +11321,10 @@ but we're only expecting 3 values for table here and each one will only be cached once. The 2nd %s must be __FILE__. */ #define SELECT_FROM_CONFIG "SELECT value FROM %s WHERE name=?/*%s*/" -static int fsl__confdb_to_role(fsl_confdb_e m){ - switch(m){ - case FSL_CONFDB_REPO: return FSL_DBROLE_REPO; - case FSL_CONFDB_CKOUT: return FSL_DBROLE_CKOUT; - case FSL_CONFDB_GLOBAL: return FSL_DBROLE_CONFIG; - default: return FSL_DBROLE_NONE; - } -} - char const * fsl_config_table_for_role(fsl_confdb_e mode){ switch(mode){ case FSL_CONFDB_REPO: return "config"; case FSL_CONFDB_CKOUT: return "vvar"; case FSL_CONFDB_GLOBAL: return "global_config"; @@ -11430,11 +11333,11 @@ assert(!"Invalid fsl_confdb_e value"); return NULL; } } -fsl_db * fsl_config_for_role(fsl_cx * const f, fsl_confdb_e mode){ +fsl_db * fsl_config_for_role(fsl_cx * f, fsl_confdb_e mode){ switch(mode){ case FSL_CONFDB_REPO: return fsl_cx_db_repo(f); case FSL_CONFDB_CKOUT: return fsl_cx_db_ckout(f); case FSL_CONFDB_GLOBAL: return fsl_cx_db_config(f); case FSL_CONFDB_VERSIONABLE: return fsl_cx_db(f); @@ -11454,11 +11357,11 @@ return fsl_buffer_appendf(b, "%s.fossil-settings/%s", f->ckout.dir, key); } -int fsl_config_unset( fsl_cx * const f, fsl_confdb_e mode, char const * key ){ +int fsl_config_unset( fsl_cx * f, fsl_confdb_e mode, char const * key ){ fsl_db * db = fsl_config_for_role(f, mode); if(!db || !key || !*key) return FSL_RC_MISUSE; else if(mode==FSL_CONFDB_VERSIONABLE) return FSL_RC_UNSUPPORTED; else{ char const * table = fsl_config_table_for_role(mode); @@ -11465,11 +11368,11 @@ assert(table); return fsl_db_exec(db, "DELETE FROM %s WHERE name=%Q", table, key); } } -int32_t fsl_config_get_int32( fsl_cx * const f, fsl_confdb_e mode, +int32_t fsl_config_get_int32( fsl_cx * f, fsl_confdb_e mode, int32_t dflt, char const * key ){ int32_t rv = dflt; switch(mode){ case FSL_CONFDB_VERSIONABLE:{ char * val = fsl_config_get_text(f, mode, key, NULL); @@ -11478,19 +11381,18 @@ fsl_free(val); } break; } default: { - fsl_db * const db = fsl_config_for_role(f, mode); + fsl_db * db = fsl_config_for_role(f, mode); char const * table = fsl_config_table_for_role(mode); assert(table); if(db){ fsl_stmt * st = NULL; fsl_db_prepare_cached(db, &st, SELECT_FROM_CONFIG, table, __FILE__); if(st){ - st->role = fsl__confdb_to_role(mode); fsl_stmt_bind_text(st, 1, key, -1, 0); if(FSL_RC_STEP_ROW==fsl_stmt_step(st)){ rv = fsl_stmt_g_int32(st, 0); } fsl_stmt_cached_yield(st); @@ -11500,11 +11402,11 @@ } } return rv; } -int64_t fsl_config_get_int64( fsl_cx * const f, fsl_confdb_e mode, +int64_t fsl_config_get_int64( fsl_cx * f, fsl_confdb_e mode, int64_t dflt, char const * key ){ int64_t rv = dflt; switch(mode){ case FSL_CONFDB_VERSIONABLE:{ char * val = fsl_config_get_text(f, mode, key, NULL); @@ -11521,11 +11423,10 @@ if(db){ fsl_stmt * st = NULL; fsl_db_prepare_cached(db, &st, SELECT_FROM_CONFIG, table, __FILE__); if(st){ - st->role = fsl__confdb_to_role(mode); fsl_stmt_bind_text(st, 1, key, -1, 0); if(FSL_RC_STEP_ROW==fsl_stmt_step(st)){ rv = fsl_stmt_g_int64(st, 0); } fsl_stmt_cached_yield(st); @@ -11535,18 +11436,18 @@ } } return rv; } -fsl_id_t fsl_config_get_id( fsl_cx * const f, fsl_confdb_e mode, +fsl_id_t fsl_config_get_id( fsl_cx * f, fsl_confdb_e mode, fsl_id_t dflt, char const * key ){ return (sizeof(fsl_id_t)==sizeof(int32_t)) ? (fsl_id_t)fsl_config_get_int32(f, mode, dflt, key) : (fsl_id_t)fsl_config_get_int64(f, mode, dflt, key); } -double fsl_config_get_double( fsl_cx * const f, fsl_confdb_e mode, +double fsl_config_get_double( fsl_cx * f, fsl_confdb_e mode, double dflt, char const * key ){ double rv = dflt; switch(mode){ case FSL_CONFDB_VERSIONABLE:{ char * val = fsl_config_get_text(f, mode, key, NULL); @@ -11563,11 +11464,10 @@ char const * table = fsl_config_table_for_role(mode); assert(table); fsl_db_prepare_cached(db, &st, SELECT_FROM_CONFIG, table, __FILE__); if(st){ - st->role = fsl__confdb_to_role(mode); fsl_stmt_bind_text(st, 1, key, -1, 0); if(FSL_RC_STEP_ROW==fsl_stmt_step(st)){ rv = fsl_stmt_g_double(st, 0); } fsl_stmt_cached_yield(st); @@ -11576,11 +11476,11 @@ } } return rv; } -char * fsl_config_get_text( fsl_cx * const f, fsl_confdb_e mode, +char * fsl_config_get_text( fsl_cx * f, fsl_confdb_e mode, char const * key, fsl_size_t * len ){ char * rv = NULL; fsl_buffer val = fsl_buffer_empty; if(fsl_config_get_buffer(f, mode, key, &val)){ fsl_cx_err_reset(f); @@ -11591,21 +11491,20 @@ rv = fsl_buffer_take(&val); } return rv; } -int fsl_config_get_buffer( fsl_cx * const f, fsl_confdb_e mode, - char const * key, fsl_buffer * const b ){ +int fsl_config_get_buffer( fsl_cx * f, fsl_confdb_e mode, + char const * key, fsl_buffer * b ){ int rc = FSL_RC_NOT_FOUND; - fsl_buffer_reuse(b); switch(mode){ case FSL_CONFDB_VERSIONABLE:{ if(!fsl_needs_ckout(f)){ rc = FSL_RC_NOT_A_CKOUT; break; } - fsl_buffer * fname = fsl__cx_scratchpad(f); + fsl_buffer * fname = fsl_cx_scratchpad(f); rc = fsl_config_versionable_filename(f, key, fname); if(!rc){ char const * zFile = fsl_buffer_cstr(fname); rc = fsl_stat(zFile, 0, false); if(rc){ @@ -11613,11 +11512,11 @@ zFile); }else{ rc = fsl_buffer_fill_from_filename(b, zFile); } } - fsl__cx_scratchpad_yield(f,fname); + fsl_cx_scratchpad_yield(f,fname); break; } default: { char const * table = fsl_config_table_for_role(mode); assert(table); @@ -11628,11 +11527,10 @@ table, __FILE__); if(rc){ rc = fsl_cx_uplift_db_error2(f, db, rc); break; } - st->role = fsl__confdb_to_role(mode); fsl_stmt_bind_text(st, 1, key, -1, 0); if(FSL_RC_STEP_ROW==fsl_stmt_step(st)){ fsl_size_t len = 0; char const * s = fsl_stmt_g_text(st, 0, &len); rc = s ? fsl_buffer_append(b, s, len) : 0; @@ -11644,11 +11542,11 @@ } } return rc; } -bool fsl_config_get_bool( fsl_cx * const f, fsl_confdb_e mode, +bool fsl_config_get_bool( fsl_cx * f, fsl_confdb_e mode, bool dflt, char const * key ){ bool rv = dflt; switch(mode){ case FSL_CONFDB_VERSIONABLE:{ char * val = fsl_config_get_text(f, mode, key, NULL); @@ -11668,11 +11566,10 @@ if(!db) break; assert(table); rc = fsl_db_prepare_cached(db, &st, SELECT_FROM_CONFIG, table, __FILE__); if(!rc){ - st->role = fsl__confdb_to_role(mode); fsl_stmt_bind_text(st, 1, key, -1, 0); if(FSL_RC_STEP_ROW==fsl_stmt_step(st)){ char const * col = fsl_stmt_g_text(st, 0, NULL); rv = col ? fsl_str_bool(col) : dflt /* 0? */; } @@ -11690,26 +11587,23 @@ must bind() parameter #2 and step() the statement, then fsl_stmt_cached_yield() it. Returns non-0 on error. */ -static int fsl_config_set_prepare( fsl_cx * const f, fsl_stmt **st, +static int fsl_config_set_prepare( fsl_cx * f, fsl_stmt **st, fsl_confdb_e mode, char const * key ){ char const * table = fsl_config_table_for_role(mode); fsl_db * db = fsl_config_for_role(f,mode); assert(table); if(!db || !key) return FSL_RC_MISUSE; else if(!*key) return FSL_RC_RANGE; else{ const char * sql = FSL_CONFDB_REPO==mode - ? "REPLACE INTO %!Q(name,value,mtime) VALUES(?,?,now())/*%s()*/" - : "REPLACE INTO %!Q(name,value) VALUES(?,?)/*%s()*/"; + ? "REPLACE INTO %s(name,value,mtime) VALUES(?,?,now())/*%s()*/" + : "REPLACE INTO %s(name,value) VALUES(?,?)/*%s()*/"; int rc = fsl_db_prepare_cached(db, st, sql, table, __func__); - if(!rc){ - (*st)->role = fsl__confdb_to_role(mode); - rc = fsl_stmt_bind_text(*st, 1, key, -1, 1); - } + if(!rc) rc = fsl_stmt_bind_text(*st, 1, key, -1, 1); if(rc && !f->error.code){ fsl_cx_uplift_db_error(f, db); } return rc; } @@ -11724,31 +11618,31 @@ /** Writes valLen bytes of val to a versioned-setting file. Returns 0 on success. Requires a checkout db. */ -static int fsl_config_set_versionable( fsl_cx * const f, char const * key, +static int fsl_config_set_versionable( fsl_cx * f, char const * key, char const * val, fsl_size_t valLen){ assert(key && *key); if(!fsl_needs_ckout(f)){ return FSL_RC_NOT_A_CKOUT; } - fsl_buffer * fName = fsl__cx_scratchpad(f); + fsl_buffer * fName = fsl_cx_scratchpad(f); int rc = fsl_config_versionable_filename(f, key, fName); if(!rc){ fsl_buffer fake = fsl_buffer_empty; fake.mem = (void*)val; fake.capacity = fake.used = valLen; rc = fsl_buffer_to_filename(&fake, fsl_buffer_cstr(fName)); } - fsl__cx_scratchpad_yield(f, fName); + fsl_cx_scratchpad_yield(f, fName); return rc; } -int fsl_config_set_text( fsl_cx * const f, fsl_confdb_e mode, +int fsl_config_set_text( fsl_cx * f, fsl_confdb_e mode, char const * key, char const * val ){ if(!key) return FSL_RC_MISUSE; else if(!*key) return FSL_RC_RANGE; else if(FSL_CONFDB_VERSIONABLE==mode){ return fsl_config_set_versionable(f, key, val, @@ -11774,11 +11668,11 @@ } if(rc && !f->error.code) fsl_cx_uplift_db_error(f, db); return rc; } -int fsl_config_set_blob( fsl_cx * const f, fsl_confdb_e mode, char const * key, +int fsl_config_set_blob( fsl_cx * f, fsl_confdb_e mode, char const * key, void const * val, fsl_int_t len ){ if(!key) return FSL_RC_MISUSE; else if(!*key) return FSL_RC_RANGE; else if(FSL_CONFDB_VERSIONABLE==mode){ return fsl_config_set_versionable(f, key, val, @@ -11805,11 +11699,11 @@ } if(rc && !f->error.code) fsl_cx_uplift_db_error(f, db); return rc; } -int fsl_config_set_int32( fsl_cx * const f, fsl_confdb_e mode, +int fsl_config_set_int32( fsl_cx * f, fsl_confdb_e mode, char const * key, int32_t val ){ if(!key) return FSL_RC_MISUSE; else if(!*key) return FSL_RC_RANGE; else if(FSL_CONFDB_VERSIONABLE==mode){ char buf[64] = {0}; @@ -11831,11 +11725,11 @@ } if(rc && !f->error.code) fsl_cx_uplift_db_error(f, db); return rc; } -int fsl_config_set_int64( fsl_cx * const f, fsl_confdb_e mode, +int fsl_config_set_int64( fsl_cx * f, fsl_confdb_e mode, char const * key, int64_t val ){ if(!key) return FSL_RC_MISUSE; else if(!*key) return FSL_RC_RANGE; else if(FSL_CONFDB_VERSIONABLE==mode){ char buf[64] = {0}; @@ -11857,11 +11751,11 @@ } if(rc && !f->error.code) fsl_cx_uplift_db_error(f, db); return rc; } -int fsl_config_set_id( fsl_cx * const f, fsl_confdb_e mode, +int fsl_config_set_id( fsl_cx * f, fsl_confdb_e mode, char const * key, fsl_id_t val ){ if(!key) return FSL_RC_MISUSE; else if(!*key) return FSL_RC_RANGE; else if(FSL_CONFDB_VERSIONABLE==mode){ char buf[64] = {0}; @@ -11883,11 +11777,11 @@ } if(rc && !f->error.code) fsl_cx_uplift_db_error(f, db); return rc; } -int fsl_config_set_double( fsl_cx * const f, fsl_confdb_e mode, +int fsl_config_set_double( fsl_cx * f, fsl_confdb_e mode, char const * key, double val ){ if(!key) return FSL_RC_MISUSE; else if(!*key) return FSL_RC_RANGE; else if(FSL_CONFDB_VERSIONABLE==mode){ char buf[128] = {0}; @@ -11909,11 +11803,11 @@ } if(rc && !f->error.code) fsl_cx_uplift_db_error(f, db); return rc; } -int fsl_config_set_bool( fsl_cx * const f, fsl_confdb_e mode, +int fsl_config_set_bool( fsl_cx * f, fsl_confdb_e mode, char const * key, bool val ){ if(!key) return FSL_RC_MISUSE; else if(!*key) return FSL_RC_RANGE; char buf[4] = {'o','n','\n','\n'}; if(!val){ @@ -11937,31 +11831,31 @@ } if(rc && !f->error.code) fsl_cx_uplift_db_error(f, db); return rc; } -int fsl_config_transaction_begin(fsl_cx * const f, fsl_confdb_e mode){ +int fsl_config_transaction_begin(fsl_cx * f, fsl_confdb_e mode){ fsl_db * db = fsl_config_for_role(f,mode); if(!db) return FSL_RC_MISUSE; else{ int const rc = fsl_db_transaction_begin(db); if(rc) fsl_cx_uplift_db_error(f, db); return rc; } } -int fsl_config_transaction_end(fsl_cx * const f, fsl_confdb_e mode, bool rollback){ +int fsl_config_transaction_end(fsl_cx * f, fsl_confdb_e mode, char rollback){ fsl_db * db = fsl_config_for_role(f,mode); if(!db) return FSL_RC_MISUSE; else{ int const rc = fsl_db_transaction_end(db, rollback); if(rc) fsl_cx_uplift_db_error(f, db); return rc; } } -int fsl_config_globs_load(fsl_cx * const f, fsl_list * const li, char const * key){ +int fsl_config_globs_load(fsl_cx * f, fsl_list * li, char const * key){ int rc = 0; char * val = NULL; if(!f || !li || !key || !*key) return FSL_RC_MISUSE; else if(fsl_cx_db_ckout(f)){ /* Try versionable settings... */ @@ -12110,11 +12004,11 @@ Return a pointer to a string that contains the RHS of an IN operator that will select CONFIG table names that are part of the configuration that matches iMatch. The returned string must eventually be fsl_free()'d. */ -char *fsl__config_inop_rhs(int iMask){ +char *fsl_config_inop_rhs(int iMask){ fsl_buffer x = fsl_buffer_empty; const char *zSep = ""; const int n = (int)ARRAYLEN(fslConfigXfer); int i; int rc = fsl_buffer_append(&x, "(", 1); @@ -12212,11 +12106,11 @@ Returns true if f's current checkout contains the given versionable configuration setting, else false. @see fsl_config_ctrl */ -FSL_EXPORT bool fsl_config_has_versionable( fsl_cx * const f, char const * key ); +FSL_EXPORT bool fsl_config_has_versionable( fsl_cx * f, char const * key ); static fsl_config_ctrl const fslConfigCtrl[] = { /* These MUST stay sorted by name and the .defaultValue field MUST have a non-NULL value so that some API guarantees can be made. @@ -12333,196 +12227,23 @@ char const * fsl_config_key_default_value(char const * key){ fsl_config_ctrl const * fcc = fsl_config_ctrl_get(key); return (fcc && fcc->name) ? fcc->defaultValue : NULL; } -bool fsl_config_has_versionable( fsl_cx * const f, char const * key ){ +bool fsl_config_has_versionable( fsl_cx * f, char const * key ){ if(!f || !key || !*key || !f->ckout.dir) return 0; else if(!fsl_config_key_is_fossil(key)) return 0; else{ - fsl_buffer * fn = fsl__cx_scratchpad(f); + fsl_buffer * fn = fsl_cx_scratchpad(f); int rc = fsl_config_versionable_filename(f, key, fn); if(!rc) rc = fsl_stat(fsl_buffer_cstr(fn), NULL, 0); - fsl__cx_scratchpad_yield(f, fn); + fsl_cx_scratchpad_yield(f, fn); return 0==rc; } } -static fsl_confdb_e fsl__char_to_confdb(char ch){ - fsl_confdb_e rc = FSL_CONFDB_NONE; - switch(ch){ - case 'c': rc = FSL_CONFDB_CKOUT; break; - case 'r': rc = FSL_CONFDB_REPO; break; - case 'g': rc = FSL_CONFDB_GLOBAL; break; - case 'v': rc = FSL_CONFDB_VERSIONABLE; break; - default: break; - } - return rc; -} - -#define fsl__configs_get_v(CONV) { \ - char * val = fsl_config_get_text(f, FSL_CONFDB_VERSIONABLE, key, NULL); \ - fsl_cx_err_reset(f); \ - if(val){ rv = CONV; fsl_free(val); goto end; } \ - break; } - -#define fsl__configs_get_x1 \ - fsl_db * const db = fsl_config_for_role(f, mode); \ - char const * table = fsl_config_table_for_role(mode); \ - assert(table); \ - if(db){ \ - fsl_stmt * st = NULL; \ - fsl_db_prepare_cached(db, &st, SELECT_FROM_CONFIG, table, __FILE__); \ - if(st){ \ - fsl_stmt_bind_text(st, 1, key, -1, 0); \ - if(FSL_RC_STEP_ROW==fsl_stmt_step(st)){ (void)0 -#define fsl__configs_get_x2 \ - fsl_stmt_cached_yield(st); \ - goto end; \ - } \ - fsl_stmt_cached_yield(st); \ - } \ - } \ - break - -int32_t fsl_configs_get_int32(fsl_cx * const f, char const * zCfg, int32_t dflt, char const * key){ - int32_t rv = dflt; - for( char const * z = zCfg; *z; ++z ){ - fsl_confdb_e const mode = fsl__char_to_confdb(*z); - switch(mode){ - case FSL_CONFDB_VERSIONABLE: fsl__configs_get_v((int32_t)atoi(val)); - case FSL_CONFDB_CKOUT: - case FSL_CONFDB_REPO: - case FSL_CONFDB_GLOBAL: { - fsl__configs_get_x1; - rv = fsl_stmt_g_int32(st, 0); - fsl__configs_get_x2; - } - default: continue; - } - } - end: - return rv; -} - -int64_t fsl_configs_get_int64(fsl_cx * const f, char const * zCfg, int64_t dflt, char const * key){ - int64_t rv = dflt; - for( char const * z = zCfg; *z; ++z ){ - fsl_confdb_e const mode = fsl__char_to_confdb(*z); - switch(mode){ - case FSL_CONFDB_VERSIONABLE: fsl__configs_get_v((int64_t)strtoll(val, NULL, 10)); - case FSL_CONFDB_CKOUT: - case FSL_CONFDB_REPO: - case FSL_CONFDB_GLOBAL: { - fsl__configs_get_x1; - rv = fsl_stmt_g_int64(st, 0); - fsl__configs_get_x2; - } - default: continue; - } - } - end: - return rv; -} - -fsl_id_t fsl_configs_get_id(fsl_cx * const f, char const * zCfg, fsl_id_t dflt, char const * key){ - return (sizeof(fsl_id_t)==sizeof(int32_t)) - ? (fsl_id_t)fsl_configs_get_int32(f, zCfg, dflt, key) - : (fsl_id_t)fsl_configs_get_int64(f, zCfg, dflt, key); -} - -bool fsl_configs_get_bool(fsl_cx * const f, char const * zCfg, bool dflt, char const * key){ - bool rv = dflt; - for( char const * z = zCfg; *z; ++z ){ - fsl_confdb_e const mode = fsl__char_to_confdb(*z); - switch(mode){ - case FSL_CONFDB_VERSIONABLE: fsl__configs_get_v(fsl_str_bool(val)); - case FSL_CONFDB_CKOUT: - case FSL_CONFDB_REPO: - case FSL_CONFDB_GLOBAL: { - fsl__configs_get_x1; - char const * col = fsl_stmt_g_text(st, 0, NULL); - rv = col ? fsl_str_bool(col) : dflt; - fsl__configs_get_x2; - } - default: continue; - } - } - end: - return rv; -} - -double fsl_configs_get_double(fsl_cx * const f, char const * zCfg, double dflt, char const * key){ - double rv = dflt; - for( char const * z = zCfg; *z; ++z ){ - fsl_confdb_e const mode = fsl__char_to_confdb(*z); - switch(mode){ - case FSL_CONFDB_VERSIONABLE: fsl__configs_get_v(strtod(val,NULL)); - case FSL_CONFDB_CKOUT: - case FSL_CONFDB_REPO: - case FSL_CONFDB_GLOBAL: { - fsl__configs_get_x1; - rv = fsl_stmt_g_double(st, 0); - fsl__configs_get_x2; - } - default: continue; - } - } - end: - return rv; -} - -char * fsl_configs_get_text(fsl_cx * const f, char const * zCfg, char const * key, - fsl_size_t * len){ - char * rv = NULL; - fsl_buffer val = fsl_buffer_empty; - if(fsl_configs_get_buffer(f, zCfg, key, &val)){ - fsl_cx_err_reset(f); - if(len) *len = 0; - fsl_buffer_clear(&val)/*in case of partial read failure*/; - }else{ - if(len) *len = val.used; - rv = fsl_buffer_take(&val); - } - return rv; -} - -int fsl_configs_get_buffer(fsl_cx * const f, char const * zCfg, char const * key, - fsl_buffer * const b){ - int rc = FSL_RC_NOT_FOUND; - fsl_buffer_reuse(b); - for( char const * z = zCfg; - (rc && FSL_RC_OOM!=rc) && *z; ++z ){ - fsl_confdb_e const mode = fsl__char_to_confdb(*z); - switch(mode){ - case FSL_CONFDB_VERSIONABLE: - rc = fsl_config_get_buffer(f, mode, key, b); - if(rc){ - if(FSL_RC_OOM!=rc) rc = FSL_RC_NOT_FOUND; - fsl_cx_err_reset(f); - } - break; - case FSL_CONFDB_CKOUT: - case FSL_CONFDB_REPO: - case FSL_CONFDB_GLOBAL: { - fsl__configs_get_x1; - fsl_size_t len = 0; - char const * s = fsl_stmt_g_text(st, 0, &len); - rc = s ? fsl_buffer_append(b, s, len) : 0; - fsl__configs_get_x2; - } - default: break; - } - } - end: - return rc; -} - - -#undef fsl__configs_get_v -#undef fsl__configs_get_x1 -#undef fsl__configs_get_x2 + #undef SELECT_FROM_CONFIG #undef MARKER #undef ARRAYLEN /* end of file config.c */ /* start of file cx.c */ @@ -12601,11 +12322,11 @@ state or output channel. Results are undefined if !f or f's memory has not been properly initialized. */ -static void fsl__cx_reset( fsl_cx * const f, bool closeDatabases ); +static void fsl_cx_reset( fsl_cx * const f, bool closeDatabases ); int fsl_cx_init( fsl_cx ** tgt, fsl_cx_init_opt const * param ){ static fsl_cx_init_opt paramDefaults = fsl_cx_init_opt_default_m; int rc = 0; @@ -12619,11 +12340,11 @@ } param = ¶mDefaults; } if(*tgt){ void const * allocStamp = (*tgt)->allocStamp; - fsl__cx_reset(*tgt, true) /* just to be safe */; + fsl_cx_reset(*tgt, true) /* just to be safe */; f = *tgt; *f = fsl_cx_empty; f->allocStamp = allocStamp; }else{ f = fsl_cx_malloc(); @@ -12692,57 +12413,56 @@ f->dbMem.role = FSL_DBROLE_MAIN; } return rc; } -static void fsl__cx_mcache_clear(fsl_cx * const f){ +static void fsl_cx_mcache_clear(fsl_cx *f){ const unsigned cacheLen = - (unsigned)(sizeof(fsl__mcache_empty.aAge) - /sizeof(fsl__mcache_empty.aAge[0])); + (unsigned)(sizeof(fsl_mcache_empty.aAge) + /sizeof(fsl_mcache_empty.aAge[0])); for(unsigned i = 0; i < cacheLen; ++i){ fsl_deck_finalize(&f->cache.mcache.decks[i]); } - f->cache.mcache = fsl__mcache_empty; + f->cache.mcache = fsl_mcache_empty; } -static void fsl__cx_reset(fsl_cx * const f, bool closeDatabases){ +static void fsl_cx_reset(fsl_cx * const f, bool closeDatabases){ fsl_checkin_discard(f); #define SFREE(X) fsl_free(X); X = NULL if(closeDatabases){ fsl_cx_close_dbs(f); /* Reminder: f->dbMem is NOT closed here: it's an internal detail, not public state. We could arguably close and reopen it here, but then we introduce a potenital error case (OOM) where we currently have none (thus the void return). - - 2021-11-09: it turns out we've had an error case all along - here: if any cached statements are opened for one of the dbs, - that can prohibit its detachement. */ SFREE(f->ckout.dir); f->ckout.dirLen = 0; /* assert(NULL==f->dbMain); */ + assert(!f->repo.db.dbh); + assert(!f->ckout.db.dbh); + assert(!f->config.db.dbh); + assert(!f->repo.db.filename); + assert(!f->ckout.db.filename); + assert(!f->config.db.filename); } SFREE(f->repo.user); SFREE(f->ckout.uuid); SFREE(f->cache.projectCode); #undef SFREE fsl_error_clear(&f->error); - f->interrupted = 0; - fsl__card_J_list_free(&f->ticket.customFields, 1); + fsl_card_J_list_free(&f->ticket.customFields, 1); fsl_buffer_clear(&f->fileContent); for(int i = 0; i < FSL_CX_NSCRATCH; ++i){ fsl_buffer_clear(&f->scratchpads.buf[i]); f->scratchpads.used[i] = false; } - fsl__bccache_clear(&f->cache.blobContent); - fsl__cx_mcache_clear(f); + fsl_acache_clear(&f->cache.arty); fsl_id_bag_clear(&f->cache.leafCheck); fsl_id_bag_clear(&f->cache.toVerify); - fsl__cx_clear_mf_seen(f, true); - assert(NULL==f->cache.mfSeen.list); + fsl_cx_clear_mf_seen(f); if(f->xlinkers.list){ fsl_free(f->xlinkers.list); f->xlinkers = fsl_xlinker_list_empty; } #define SLIST(L) fsl_list_visit_free(L, 1) @@ -12750,29 +12470,29 @@ GLOBL(ignore); GLOBL(binary); GLOBL(crnl); #undef GLOBL #undef SLIST + fsl_cx_mcache_clear(f); f->cache = fsl_cx_empty.cache; } -void fsl__cx_clear_mf_seen(fsl_cx * const f, bool freeMemory){ - if(freeMemory) fsl_id_bag_clear(&f->cache.mfSeen); - else fsl_id_bag_reset(&f->cache.mfSeen); +void fsl_cx_clear_mf_seen(fsl_cx * f){ + fsl_id_bag_clear(&f->cache.mfSeen); } -void fsl_cx_finalize( fsl_cx * const f ){ +void fsl_cx_finalize( fsl_cx * f ){ void const * allocStamp = f ? f->allocStamp : NULL; if(!f) return; if(f->clientState.finalize.f){ f->clientState.finalize.f( f->clientState.finalize.state, f->clientState.state ); } f->clientState = fsl_state_empty; f->output = fsl_outputer_empty; - fsl__cx_reset(f, true); + fsl_cx_reset(f, true); fsl_db_close(&f->dbMem); *f = fsl_cx_empty; if(&fsl_cx_empty == allocStamp){ fsl_free(f); }else{ @@ -12793,20 +12513,21 @@ #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); #endif } -void fsl_cx_err_reset(fsl_cx * const f){ - //f->interrupted = 0; // No! ONLY modify this via fsl_cx_interrupt() - fsl_error_reset(&f->error); - fsl_db_err_reset(&f->dbMem); - fsl_db_err_reset(&f->repo.db); - fsl_db_err_reset(&f->config.db); - fsl_db_err_reset(&f->ckout.db); +void fsl_cx_err_reset(fsl_cx * f){ + if(f){ + fsl_error_reset(&f->error); + fsl_db_err_reset(&f->dbMem); + fsl_db_err_reset(&f->repo.db); + fsl_db_err_reset(&f->config.db); + fsl_db_err_reset(&f->ckout.db); + } } -int fsl_cx_err_set_e( fsl_cx * const f, fsl_error * const err ){ +int fsl_cx_err_set_e( fsl_cx * f, fsl_error * err ){ if(!f) return FSL_RC_MISUSE; else if(!err){ return fsl_cx_err_set(f, 0, NULL); }else{ fsl_error_move(err, &f->error); @@ -12813,18 +12534,18 @@ fsl_error_clear(err); return f->error.code; } } -int fsl_cx_err_setv( fsl_cx * const f, int code, char const * fmt, +int fsl_cx_err_setv( fsl_cx * f, int code, char const * fmt, va_list args ){ return f ? fsl_error_setv( &f->error, code, fmt, args ) : FSL_RC_MISUSE; } -int fsl_cx_err_set( fsl_cx * const f, int code, char const * fmt, +int fsl_cx_err_set( fsl_cx * f, int code, char const * fmt, ... ){ if(!f) return FSL_RC_MISUSE; else{ int rc; va_list args; @@ -12833,21 +12554,14 @@ va_end(args); return rc; } } -int fsl_cx_err_get( fsl_cx * const f, char const ** str, fsl_size_t * len ){ -#if 1 - return fsl_error_get( &f->error, str, len ); -#else - /* For the docs: - If fsl_cx_interrupted() has been called with an error code and the - context has no other pending error state, that code is returned. - */ - int const rc = fsl_error_get( &f->error, str, len ); - return rc ? rc : f->interrupted; -#endif +int fsl_cx_err_get( fsl_cx * f, char const ** str, fsl_size_t * len ){ + return f + ? fsl_error_get( &f->error, str, len ) + : FSL_RC_MISUSE; } fsl_id_t fsl_cx_last_insert_id(fsl_cx * const f){ return (f && f->dbMain && f->dbMain->dbh) ? fsl_db_last_insert_id(f->dbMain) @@ -12877,10 +12591,11 @@ else return 0; } int fsl_cx_uplift_db_error( fsl_cx * const f, fsl_db * db ){ assert(f); + if(!f) return FSL_RC_MISUSE; if(!db){ db = f->dbMain; assert(db && "misuse: no DB handle to uplift error from!"); if(!db) return FSL_RC_MISUSE; } @@ -12887,30 +12602,29 @@ fsl_error_move( &db->error, &f->error ); return f->error.code; } int fsl_cx_uplift_db_error2(fsl_cx * const f, fsl_db * db, int rc){ - assert(f); - if(!f->error.code && rc && rc!=FSL_RC_OOM){ - if(!db) db = f->dbMain; - assert(db && "misuse: no DB handle to uplift error from!"); - if(db->error.code) rc = fsl_cx_uplift_db_error(f, db); + if(!db) db = f->dbMain; + assert(db); + if(rc && FSL_RC_OOM!=rc && !f->error.code && db->error.code){ + rc = fsl_cx_uplift_db_error(f, db); } return rc; } fsl_db * fsl_cx_db_config( fsl_cx * const f ){ if(!f) return NULL; - else if(f->dbMain && (FSL_DBROLE_CONFIG & f->dbMain->role)) return f->dbMain; else if(f->config.db.dbh) return &f->config.db; + else if(f->dbMain && (FSL_DBROLE_CONFIG & f->dbMain->role)) return f->dbMain; else return NULL; } fsl_db * fsl_cx_db_repo( fsl_cx * const f ){ if(!f) return NULL; - else if(f->dbMain && (FSL_DBROLE_REPO & f->dbMain->role)) return f->dbMain; else if(f->repo.db.dbh) return &f->repo.db; + else if(f->dbMain && (FSL_DBROLE_REPO & f->dbMain->role)) return f->dbMain; else return NULL; } fsl_db * fsl_needs_repo(fsl_cx * const f){ fsl_db * const db = fsl_cx_db_repo(f); @@ -12930,12 +12644,12 @@ return db; } fsl_db * fsl_cx_db_ckout( fsl_cx * const f ){ if(!f) return NULL; - else if(f->dbMain && (FSL_DBROLE_CKOUT & f->dbMain->role)) return f->dbMain; else if(f->ckout.db.dbh) return &f->ckout.db; + else if(f->dbMain && (FSL_DBROLE_CKOUT & f->dbMain->role)) return f->dbMain; else return NULL; } fsl_db * fsl_cx_db( fsl_cx * const f ){ return f ? f->dbMain : NULL; @@ -12972,40 +12686,29 @@ /** Detaches the given db role from f->dbMain and removes the role from f->dbMain->role. */ -static int fsl_cx_detach_role(fsl_cx * const f, fsl_dbrole_e r){ - if(NULL==f->dbMain) return FSL_RC_MISUSE; +static int fsl_cx_detach_role(fsl_cx * f, fsl_dbrole_e r){ + if(!f || !f->dbMain) return FSL_RC_MISUSE; else if(!(r & f->dbMain->role)){ assert(!"Misuse: cannot detach unattached role."); return FSL_RC_NOT_FOUND; } else{ - fsl_db * const db = fsl_cx_db_for_role(f,r); + fsl_db * db = fsl_cx_db_for_role(f,r); int rc; - assert(db && "Internal API misuse."); + if(!db) return FSL_RC_RANGE; assert(f->dbMain != db); - rc = fsl__db_cached_clear_role(f->dbMain, r) - /* Make sure that we destroy any cached statements which are - known to be tied to this db role. This is primarily a kludge - for the global config db to avoid that closing it fails due - to a lock held by those statements. This is a special case - for the global db (as opposed to the repo/ckout dbs) because - exactly when that db is opened and close is not as tightly - controlled/funneled as the other dbs. */; - if(0==rc){ - rc = fsl_db_detach( f->dbMain, fsl_db_role_label(r) ); - //MARKER(("rc=%s %s %s\n", fsl_rc_cstr(rc), fsl_db_role_label(r), - // fsl_buffer_cstr(&f->dbMain->error.msg))); - if(rc){ - fsl_cx_uplift_db_error(f, f->dbMain); - }else{ - f->dbMain->role &= ~r; - fsl__db_clear_strings(db, true); - } - } + f->dbMain->role &= ~r; + rc = fsl_db_detach( f->dbMain, fsl_db_role_label(r) ); + //MARKER(("rc=%s %s %s\n", fsl_rc_cstr(rc), fsl_db_role_label(r), + // fsl_buffer_cstr(&f->dbMain->error.msg))); + fsl_free(db->filename); + fsl_free(db->name); + db->filename = NULL; + db->name = NULL; return rc; } } @@ -13015,15 +12718,15 @@ be static but we need it in fsl_repo.c when creating a new repository. */ int fsl_cx_attach_role(fsl_cx * const f, const char *zDbName, fsl_dbrole_e r){ char const * label = fsl_db_role_label(r); - fsl_db * const db = fsl_cx_db_for_role(f, r); + fsl_db * db = fsl_cx_db_for_role(f, r); char ** nameDest = NULL; int rc; if(!f->dbMain){ - fsl__fatal(FSL_RC_MISUSE,"Internal API misuse: f->dbMain has " + fsl_fatal(FSL_RC_MISUSE,"Internal API misuse: f->dbMain has " "not been set, so cannot attach role."); return FSL_RC_MISUSE; } else if(r & f->dbMain->role){ assert(!"Misuse: role is already attached."); @@ -13081,27 +12784,31 @@ } return rc; } int fsl_config_close( fsl_cx * const f ){ - int rc = 0; - fsl_db * const db = &f->config.db; - if(f->dbMain && (FSL_DBROLE_CONFIG & f->dbMain->role)){ - /* Config db is ATTACHed. */ - rc = fsl_cx_detach_role(f, FSL_DBROLE_CONFIG); - }else{ - fsl_db_close(db); - } - return rc; + if(!f) return FSL_RC_MISUSE; + else{ + int rc; + fsl_db * const db = &f->config.db; + if(f->dbMain && (FSL_DBROLE_CONFIG & f->dbMain->role)){ + /* Config db is ATTACHed. */ + rc = fsl_cx_detach_role(f, FSL_DBROLE_CONFIG); + } + else rc = FSL_RC_NOT_FOUND; + assert(!db->dbh); + fsl_db_clear_strings(db, 1); + return rc; + } } int fsl_repo_close( fsl_cx * const f ){ if(fsl_cx_transaction_level(f)){ return fsl_cx_err_set(f, FSL_RC_MISUSE, - "Cannot close repo with an opened transaction."); + "Cannot close repo with opened transaction."); }else{ - int rc = 0; + int rc; fsl_db * const db = &f->repo.db; if(f->dbMain && (FSL_DBROLE_REPO & f->dbMain->role)){ /* Repo db is ATTACHed. */ if(FSL_DBROLE_CKOUT & f->dbMain->role){ rc = fsl_cx_err_set(f, FSL_RC_MISUSE, @@ -13109,42 +12816,45 @@ "opened."); }else{ assert(f->dbMain!=db); rc = fsl_cx_detach_role(f, FSL_DBROLE_REPO); } - }else{ - fsl_db_close(db); } + else rc = FSL_RC_NOT_FOUND; assert(!db->dbh); + fsl_db_clear_strings(db, true); return rc; } } int fsl_ckout_close( fsl_cx * const f ){ if(fsl_cx_transaction_level(f)){ return fsl_cx_err_set(f, FSL_RC_MISUSE, "Cannot close checkout with opened transaction."); }else{ - int rc = 0; + int rc; fsl_db * const db = &f->ckout.db; if(f->dbMain && (FSL_DBROLE_CKOUT & f->dbMain->role)){ /* Checkout db is ATTACHed. */ rc = fsl_cx_detach_role(f, FSL_DBROLE_CKOUT); - fsl_repo_close(f) - /* Because the repo is implicitly opened, we "should" - implicitly close it. This is debatable but "probably almost - always" desired. i can't currently envisage a reasonable - use-case which requires closing the checkout but keeping - the repo opened. The repo can always be re-opened by - itself. */; + fsl_repo_close(f) /* + Because the repo is implicitly opened, we + "should" implicitly close it. This is + debatable but "probably almost always" + desired. i can't currently envisage a + reasonable use-case which requires closing + the checkout but keeping the repo opened. + The repo can always be re-opened by itself. + */; }else{ - fsl_db_close(db); + rc = FSL_RC_NOT_FOUND; } fsl_free(f->ckout.uuid); f->ckout.uuid = NULL; f->ckout.rid = 0; assert(!db->dbh); + fsl_db_clear_strings(db, true); return rc; } } /** @@ -13216,39 +12926,20 @@ va_start(args,sql); rc = fsl_cx_preparev( f, tgt, sql, args ); va_end(args); return rc; } - -int fsl_cx_preparev_cached( fsl_cx * const f, fsl_stmt ** tgt, char const * sql, - va_list args ){ - int const rc = (f->dbMain && tgt) - ? fsl_db_preparev_cached(f->dbMain, tgt, sql, args) - : FSL_RC_MISUSE; - return rc ? fsl_cx_uplift_db_error2(f, f->dbMain, rc) : 0; -} - -int fsl_cx_prepare_cached( fsl_cx * const f, fsl_stmt ** tgt, char const * sql, - ... ){ - int rc; - va_list args; - va_start(args,sql); - rc = fsl_cx_preparev_cached( f, tgt, sql, args ); - va_end(args); - return rc; -} - /** Passes the fsl_schema_config() SQL code through a new/truncated file named dbName. If the file exists before this call, it is unlink()ed and fails if that operation fails. FIXME: this was broken by the addition of "cfg." prefix on the schema's tables. */ -static int fsl_config_file_reset(fsl_cx * const f, char const * dbName){ +static int fsl_config_file_reset(fsl_cx * f, char const * dbName){ fsl_db DB = fsl_db_empty; fsl_db * db = &DB; int rc = 0; bool isAttached = false; const char * zPrefix = fsl_db_role_label(FSL_DBROLE_CONFIG); @@ -13344,13 +13035,13 @@ int fsl_config_open( fsl_cx * const f, char const * openDbName ){ int rc = 0; const char * zDbName = 0; char * zPrefName = 0; - if(fsl_cx_db_config(f)){ - if(NULL==openDbName || 0==*openDbName) return 0/*nothing to do*/; - else fsl_config_close(f); + if(!f) return FSL_RC_MISUSE; + else if(f->config.db.dbh){ + fsl_config_close(f); } if(openDbName && *openDbName){ zDbName = openDbName; }else{ rc = fsl_config_global_preferred_name(&zPrefName); @@ -13374,11 +13065,10 @@ "Configuration database [%s] " "must be writeable.", zDbName); goto end; } #endif - assert(NULL==fsl_cx_db_config(f)); rc = fsl_cx_attach_role(f, zDbName, FSL_DBROLE_CONFIG); end: fsl_free(zPrefName); return rc; } @@ -13448,11 +13138,11 @@ To be called after a repo or checkout/repo combination has been opened. This updates some internal cached info based on the checkout and/or repo. */ static int fsl_cx_after_open(fsl_cx * f){ - int rc = fsl__ckout_version_fetch(f); + int rc = fsl_ckout_version_fetch(f); if(!rc) rc = fsl_cx_load_glob_lists(f); return rc; } @@ -13564,11 +13254,11 @@ "WHERE objid=%" FSL_ID_T_PFMT, f->ckout.rid) : 0.0; } -int fsl__ckout_version_fetch( fsl_cx * const f ){ +int fsl_ckout_version_fetch( fsl_cx *f ){ fsl_id_t rid = 0; int rc = 0; fsl_db * dbC = fsl_cx_db_ckout(f); fsl_db * dbR = dbC ? fsl_needs_repo(f) : NULL; assert(!dbC || (dbC && dbR)); @@ -13619,11 +13309,11 @@ be the UUID for the given RID and is copies to f->ckout.uuid. Returns 0 on success, FSL_RC_OOM if copying uuid fails, or some error from fsl_rid_to_uuid() if that fails. - Does not write the changes to disk. Use fsl__ckout_version_write() + Does not write the changes to disk. Use fsl_ckout_version_write() for that. That routine also calls this one, so there's no need to call both. */ static int fsl_cx_ckout_version_set(fsl_cx *f, fsl_id_t rid, fsl_uuid_cstr uuid){ @@ -13638,17 +13328,17 @@ f->ckout.uuid = u; fsl_ckout_mtime_set(f); return 0; } -int fsl__ckout_version_write( fsl_cx * const f, fsl_id_t vid, +int fsl_ckout_version_write( fsl_cx *f, fsl_id_t vid, fsl_uuid_cstr hash ){ int rc = 0; if(!fsl_needs_ckout(f)) return FSL_RC_NOT_A_CKOUT; else if(vid<0){ return fsl_cx_err_set(f, FSL_RC_MISUSE, - "Invalid vid for fsl__ckout_version_write()"); + "Invalid vid for fsl_ckout_version_write()"); } if(f->ckout.rid!=vid){ rc = fsl_cx_ckout_version_set(f, vid, hash); } if(!rc){ @@ -13659,11 +13349,11 @@ "checkout-hash", f->ckout.uuid); } } if(!rc){ char * zFingerprint = 0; - rc = fsl__repo_fingerprint_search(f, 0, &zFingerprint); + rc = fsl_repo_fingerprint_search(f, 0, &zFingerprint); if(!rc){ rc = fsl_config_set_text(f, FSL_CONFDB_CKOUT, "fingerprint", zFingerprint); fsl_free(zFingerprint); } @@ -13910,15 +13600,15 @@ if(len) *len = f->ckout.dirLen; } return rc; } -int fsl_cx_flags_get( fsl_cx const * const f ){ +int fsl_cx_flags_get( fsl_cx * f ){ return f->flags; } -int fsl_cx_flag_set( fsl_cx * const f, int flags, bool enable ){ +int fsl_cx_flag_set( fsl_cx * f, int flags, bool enable ){ int const oldFlags = f->flags; if(enable) f->flags |= flags; else f->flags &= ~flags; return oldFlags; } @@ -13961,11 +13651,11 @@ x->state = cbState; x->name = name; return 0; } -int fsl_cx_user_set( fsl_cx * const f, char const * userName ){ +int fsl_cx_user_set( fsl_cx * f, char const * userName ){ if(!f) return FSL_RC_MISUSE; else if(!userName || !*userName){ fsl_free(f->repo.user); f->repo.user = NULL; return 0; @@ -13978,25 +13668,12 @@ return 0; } } } -char const * fsl_cx_user_guess(fsl_cx * const f){ - if(!f->repo.user){ - char * u = fsl_user_name_guess(); - if(u){ - fsl_free(f->repo.user); - f->repo.user = u; - // don't use fsl_cx_user_set(f, u), to avoid another strdup() - } - } - return f->repo.user; -} - - -char const * fsl_cx_user_get( fsl_cx const * const f ){ - return f->repo.user; +char const * fsl_cx_user_get( fsl_cx const * f ){ + return f ? f->repo.user : NULL; } int fsl_cx_schema_ticket(fsl_cx * f, fsl_buffer * pOut){ fsl_db * db = f ? fsl_needs_repo(f) : NULL; if(!f || !pOut) return FSL_RC_MISUSE; @@ -14019,16 +13696,16 @@ char const * zName, fsl_fstat * const tgt, fsl_buffer * const nameOut, bool fullPath){ int rc; fsl_buffer * b = 0; fsl_buffer * bufRel = 0; - fsl_size_t n = 0; + fsl_size_t n; assert(f); if(!zName || !*zName) return FSL_RC_MISUSE; else if(!fsl_needs_ckout(f)) return FSL_RC_NOT_A_CKOUT; - b = fsl__cx_scratchpad(f); - bufRel = fsl__cx_scratchpad(f); + b = fsl_cx_scratchpad(f); + bufRel = fsl_cx_scratchpad(f); #if 1 rc = fsl_ckout_filename_check(f, relativeToCwd, zName, bufRel); if(rc) goto end; zName = fsl_buffer_cstr2( bufRel, &n ); #else @@ -14063,12 +13740,12 @@ ? fsl_buffer_append(nameOut, b->mem, b->used) : fsl_buffer_append(nameOut, zName, n); } } end: - fsl__cx_scratchpad_yield(f, b); - fsl__cx_scratchpad_yield(f, bufRel); + fsl_cx_scratchpad_yield(f, b); + fsl_cx_scratchpad_yield(f, bufRel); return rc; } int fsl_cx_stat(fsl_cx * const f, bool relativeToCwd, char const * zName, fsl_fstat * const tgt){ @@ -14087,11 +13764,11 @@ char const * fsl_cx_filename_collation(fsl_cx const * f){ return f->cache.caseInsensitive ? "COLLATE nocase" : ""; } -void fsl__cx_content_buffer_yield(fsl_cx * const f){ +void fsl_cx_content_buffer_yield(fsl_cx * const f){ enum { MaxSize = 1024 * 1024 * 2 }; assert(f); if(f->fileContent.capacity>MaxSize){ fsl_buffer_resize(&f->fileContent, MaxSize); assert(f->fileContent.capacity<=MaxSize+1); @@ -14103,37 +13780,24 @@ return f ? &f->error : NULL; } int fsl_cx_close_dbs( fsl_cx * const f ){ if(fsl_cx_transaction_level(f)){ - /* Is this really necessary? */ return fsl_cx_err_set(f, FSL_RC_MISUSE, "Cannot close the databases when a " "transaction is pending."); } - if(NULL==f->dbMain) return 0; int rc = 0, rc1; - rc = fsl__db_cached_clear_role(f->dbMain, 0); - if(rc) return fsl_cx_uplift_db_error(f, f->dbMain); rc1 = fsl_ckout_close(f); if(rc1) rc = rc1; rc1 = fsl_repo_close(f); if(rc1) rc = rc1; rc1 = fsl_config_close(f); if(rc1) rc = rc1; - /* Forcibly reset the role and db strings for this case, even - if closing ostensibly fails. */ - f->dbMain->role = FSL_DBROLE_MAIN; - fsl__db_clear_strings(&f->repo.db, true); - fsl__db_clear_strings(&f->ckout.db, true); - fsl__db_clear_strings(&f->config.db, true); assert(!f->repo.db.dbh); assert(!f->ckout.db.dbh); assert(!f->config.db.dbh); - assert(!f->repo.db.filename); - assert(!f->ckout.db.filename); - assert(!f->config.db.filename); return rc; } char const * fsl_cx_glob_matches( fsl_cx * const f, int gtype, char const * str ){ @@ -14258,11 +13922,11 @@ void fsl_cx_confirmer_get(fsl_cx const * f, fsl_confirmer * dest){ *dest = f->confirmer; } -int fsl_cx_confirm(fsl_cx * const f, fsl_confirm_detail const * detail, +int fsl_cx_confirm(fsl_cx *f, fsl_confirm_detail const * detail, fsl_confirm_response *outAnswer){ if(f->confirmer.callback){ return f->confirmer.callback(detail, outAnswer, f->confirmer.callbackState); } @@ -14278,18 +13942,18 @@ case FSL_CEVENT_MULTIPLE_VERSIONS: outAnswer->response = FSL_CRESPONSE_CANCEL; break; default: assert(!"Unhandled fsl_confirm_event_e value"); - fsl__fatal(FSL_RC_UNSUPPORTED, + fsl_fatal(FSL_RC_UNSUPPORTED, "Unhandled fsl_confirm_event_e value: %d", detail->eventId)/*does not return*/; } return 0; } -int fsl__cx_update_seen_delta_deck(fsl_cx * const f){ +int fsl_cx_update_seen_delta_mf(fsl_cx *f){ int rc = 0; fsl_db * const d = fsl_cx_db_repo(f); if(d && f->cache.seenDeltaManifest <= 0){ f->cache.seenDeltaManifest = 1; rc = fsl_config_set_bool(f, FSL_CONFDB_REPO, @@ -14309,18 +13973,18 @@ "Filename is reserved, not legal " "for adding to a repository: %.*s", (int)nPath, zPath); } if(!(f->flags & FSL_CX_F_ALLOW_WINDOWS_RESERVED_NAMES) - && fsl__is_reserved_fn_windows(zPath, nPath)){ + && fsl_is_reserved_fn_windows(zPath, nPath)){ return fsl_cx_err_set(f, errRc, "Filename is a Windows reserved name: %.*s", (int)nPath, zPath); } if((z1 = fsl_cx_db_file_for_role(f, FSL_DBROLE_REPO, NULL))){ - fsl_buffer * c1 = fsl__cx_scratchpad(f); - fsl_buffer * c2 = fsl__cx_scratchpad(f); + fsl_buffer * c1 = fsl_cx_scratchpad(f); + fsl_buffer * c2 = fsl_cx_scratchpad(f); rc = fsl_file_canonical_name2(relativeToCwd ? NULL : f->ckout.dir/*NULL is okay*/, z1, c1, false); if(!rc) rc = fsl_file_canonical_name2(relativeToCwd ? NULL : f->ckout.dir, zPath, c2, false); //MARKER(("\nzPath=%s\nc1=%s\nc2=%s\n", zPath, @@ -14328,12 +13992,12 @@ if(!rc && c1->used == c2->used && 0==fsl_stricmp(fsl_buffer_cstr(c1), fsl_buffer_cstr(c2))){ rc = fsl_cx_err_set(f, errRc, "File is the repository database: %.*s", (int)nPath, zPath); } - fsl__cx_scratchpad_yield(f, c1); - fsl__cx_scratchpad_yield(f, c2); + fsl_cx_scratchpad_yield(f, c1); + fsl_cx_scratchpad_yield(f, c2); if(rc) return rc; } assert(!rc); while(f->ckout.dir || relativeToCwd){ int manifestSetting = 0; @@ -14347,11 +14011,11 @@ {FSL_MANIFEST_MAIN, "manifest"}, {FSL_MANIFEST_UUID, "manifest.uuid"}, {FSL_MANIFEST_TAGS, "manifest.tags"}, {0,0} }; - fsl_buffer * c1 = fsl__cx_scratchpad(f); + fsl_buffer * c1 = fsl_cx_scratchpad(f); if(f->ckout.dir){ rc = fsl_ckout_filename_check(f, relativeToCwd, zPath, c1); }else{ rc = fsl_file_canonical_name2("", zPath, c1, false); } @@ -14368,17 +14032,17 @@ m->fn); break; } } yield: - fsl__cx_scratchpad_yield(f, c1); + fsl_cx_scratchpad_yield(f, c1); break; } return rc; } -fsl_buffer * fsl__cx_scratchpad(fsl_cx * const f){ +fsl_buffer * fsl_cx_scratchpad(fsl_cx *f){ fsl_buffer * rc = 0; int i = (f->scratchpads.nextscratchpads.next : 0; for(; i < FSL_CX_NSCRATCH; ++i){ if(!f->scratchpads.used[i]){ @@ -14390,23 +14054,23 @@ break; } } if(!rc){ assert(!"Fatal fsl_cx::scratchpads misuse."); - fsl__fatal(FSL_RC_MISUSE, + fsl_fatal(FSL_RC_MISUSE, "Fatal internal fsl_cx::scratchpads misuse: " "too many unyielded buffer requests."); }else if(0!=rc->used){ assert(!"Fatal fsl_cx::scratchpads misuse."); - fsl__fatal(FSL_RC_MISUSE, + fsl_fatal(FSL_RC_MISUSE, "Fatal internal fsl_cx::scratchpads misuse: " "used buffer after yielding it."); } return rc; } -void fsl__cx_scratchpad_yield(fsl_cx * const f, fsl_buffer * const b){ +void fsl_cx_scratchpad_yield(fsl_cx *f, fsl_buffer * b){ int i; assert(b); for(i = 0; i < FSL_CX_NSCRATCH; ++i){ if(b == &f->scratchpads.buf[i]){ assert(f->scratchpads.next != i); @@ -14417,11 +14081,11 @@ //MARKER(("Yielded scratchpad[%d] w/ capacity=%d, next=%d\n", // i, (int)b->capacity, f->scratchpads.next)); return; } } - fsl__fatal(FSL_RC_MISUSE, + fsl_fatal(FSL_RC_MISUSE, "Fatal internal fsl_cx::scratchpads misuse: " "passed a non-scratchpad buffer."); } @@ -14477,28 +14141,28 @@ } } return rc; } -unsigned int fsl_ckout_rm_empty_dirs(fsl_cx * const f, fsl_buffer * const tgtDir){ +unsigned int fsl_ckout_rm_empty_dirs(fsl_cx * f, fsl_buffer * tgtDir){ int rc = f->ckout.dir ? 0 : FSL_RC_NOT_A_CKOUT; if(!rc){ rc = fsl_rm_empty_dirs(f->ckout.dir, f->ckout.dirLen, tgtDir); } return rc; } -int fsl_ckout_rm_empty_dirs_for_file(fsl_cx * const f, char const *zAbsPath){ +int fsl_ckout_rm_empty_dirs_for_file(fsl_cx * f, char const *zAbsPath){ if(!fsl_is_rooted_in_ckout(f, zAbsPath)){ assert(!"Internal API misuse!"); return FSL_RC_MISUSE; }else{ - fsl_buffer * const p = fsl__cx_scratchpad(f); + fsl_buffer * const p = fsl_cx_scratchpad(f); fsl_int_t const nAbs = (fsl_int_t)fsl_strlen(zAbsPath); int const rc = fsl_file_dirpart(zAbsPath, nAbs, p, false); if(!rc) fsl_rm_empty_dirs(f->ckout.dir, f->ckout.dirLen, p); - fsl__cx_scratchpad_yield(f,p); + fsl_cx_scratchpad_yield(f,p); return rc; } } bool fsl_repo_forbids_delta_manifests(fsl_cx * f){ @@ -14511,11 +14175,11 @@ if(!db) return 0; int rc = 0; char const * zCkout = 0; char * zRepo = 0; fsl_id_t rcvCkout = 0; - fsl_buffer * const buf = fsl__cx_scratchpad(f); + fsl_buffer * const buf = fsl_cx_scratchpad(f); rc = fsl_config_get_buffer(f, FSL_CONFDB_CKOUT, "fingerprint", buf); if(FSL_RC_NOT_FOUND==rc){ /* Older checkout with no fingerprint. Assume it's okay. */ rc = 0; goto end; @@ -14526,11 +14190,11 @@ #if 0 /* Inject a bogus byte for testing purposes */ buf->mem[6] = 'x'; #endif rcvCkout = (fsl_id_t)atoi(zCkout); - rc = fsl__repo_fingerprint_search(f, rcvCkout, &zRepo); + rc = fsl_repo_fingerprint_search(f, rcvCkout, &zRepo); switch(rc){ case FSL_RC_NOT_FOUND: goto mismatch; case 0: assert(zRepo); if(fsl_strcmp(zRepo,zCkout)){ @@ -14539,11 +14203,11 @@ break; default: break; } end: - fsl__cx_scratchpad_yield(f, buf); + fsl_cx_scratchpad_yield(f, buf); fsl_free(zRepo); return rc; mismatch: rc = fsl_cx_err_set(f, FSL_RC_REPO_MISMATCH, "Mismatch found between repo/checkout " @@ -14553,30 +14217,10 @@ bool fsl_cx_has_ckout(fsl_cx const * const f ){ return f->ckout.dir ? true : false; } -int fsl_cx_interruptv(fsl_cx * const f, int code, char const * fmt, va_list args){ - f->interrupted = code; - if(code && NULL!=fmt){ - code = fsl_cx_err_setv(f, code, fmt, args); - } - return code; -} - -int fsl_cx_interrupt(fsl_cx * const f, int code, char const * fmt, ...){ - int rc; - va_list args; - va_start(args,fmt); - rc = fsl_cx_interruptv(f, code, fmt, args); - va_end(args); - return rc; -} - -int fsl_cx_interrupted(fsl_cx const * const f){ - return f->interrupted; -} #if 0 struct tm * fsl_cx_localtime( fsl_cx const * f, const time_t * clock ){ if(!clock) return NULL; else if(!f) return localtime(clock); @@ -14641,11 +14285,11 @@ if(obj) fsl_stmt_finalize( (fsl_stmt*)obj ); return 0; } #endif -void fsl__db_clear_strings(fsl_db * const db, bool alsoErrorState ){ +void fsl_db_clear_strings(fsl_db * const db, bool alsoErrorState ){ fsl_free(db->filename); db->filename = NULL; fsl_free(db->name); db->name = NULL; if(alsoErrorState) fsl_error_clear(&db->error); @@ -14753,11 +14397,11 @@ if(db->dbh){ sqlite3_close(db->dbh); /* ignoring results in the style of "destructors may not throw". */ } - fsl__db_clear_strings(db, true); + fsl_db_clear_strings(db, 1); fsl_db_cleanup_beforeCommit(db); fsl_buffer_clear(&db->cachePrepBuf); *db = fsl_db_empty; if(&fsl_db_empty == allocStamp){ fsl_free( db ); @@ -14862,11 +14506,11 @@ return s; } enum fsl_stmt_flags_e { /** - fsl_stmt::flags bit indicating that fsl_db_preparev_cached() has + fsl_stmt::flags bit indicating that fsl_db_preparev_cache() has doled out this statement, effectively locking it until fsl_stmt_cached_yield() is called to release it. */ FSL_STMT_F_CACHE_HELD = 0x01, @@ -15026,13 +14670,10 @@ fsl_free(st); st = 0; }else{ st->sql.cursor = buf->cursor/*hash value!*/; st->next = db->cacheHead; - st->role = db->role - /* Pessimistic assumption for purposes of invalidating - fsl__db_cached_clear_role(). */; db->cacheHead = st; st->flags = FSL_STMT_F_CACHE_HELD; *rv = st; } end: @@ -15135,45 +14776,10 @@ } return 0; } } -int fsl__db_cached_clear_role(fsl_db * const db, int role){ - int rc = 0; - fsl_stmt * s; - fsl_stmt * prev = 0; - fsl_stmt * next = 0; - for( s = db->cacheHead; s; s = next ){ - next = s->next; - if(0!=role && 0==(s->role & role)){ - prev = s; - continue; - } - else if(FSL_STMT_F_CACHE_HELD & s->flags){ - rc = fsl_error_set(&db->error, FSL_RC_MISUSE, - "Cannot clear cached SQL statement " - "for role #%d because it is currently " - "being held by a call to " - "fsl_db_preparev_cached(). SQL=%B", - &s->sql); - break; - } - //MARKER(("Closing cached stmt: %s\n", fsl_buffer_cstr(&s->sql))); - if(prev){ - prev->next = next; - }else if(s==db->cacheHead){ - db->cacheHead = next; - } - s->next = 0; - s->flags = 0; - s->role = FSL_DBROLE_NONE; - fsl_stmt_finalize(s); - break; - } - return rc; -} - int fsl_stmt_step( fsl_stmt * const stmt ){ if(!stmt || !stmt->stmt) return FSL_RC_MISUSE; else{ int const rc = sqlite3_step(stmt->stmt); assert(stmt->db); @@ -15982,11 +15588,11 @@ f->cache.caseInsensitive ? fsl_stricmp : fsl_strcmp; if(0==cmp(p1, p2)){ sqlite3_result_int(context, 1); return; } - b = fsl__cx_scratchpad(f); + b = fsl_cx_scratchpad(f); rc = fsl_buffer_appendf(b, "%s/", p2); if(rc) goto oom; else if(cmp(p1, fsl_buffer_cstr(b))>0){ b->mem[b->used-1] = '0'; if(cmp(p1, fsl_buffer_cstr(b))<0) @@ -15993,11 +15599,11 @@ rc = 2; } assert(0==rc || 2==rc); sqlite3_result_int(context, rc); end: - fsl__cx_scratchpad_yield(f, b); + fsl_cx_scratchpad_yield(f, b); return; oom: sqlite3_result_error_nomem(context); goto end; } @@ -16079,11 +15685,11 @@ /* Returns 0 if db appears to have a current repository schema, 1 if it appears to have an out of date schema, and -1 if it appears to not be a repository. */ -int fsl__db_repo_verify_schema(fsl_db * db){ +int fsl_db_repo_verify_schema(fsl_db * db){ if(fsl_db_repo_schema_is_outofdate(db)) return 1; else return fsl_db_exists(db, "SELECT 1 FROM config " "WHERE name='project-code'") ? 0 : -1; @@ -16104,11 +15710,11 @@ not a proper fsl_db. */ fsl_db * db = (fsl_db *)db_fsl; assert(db && "What else could it be?"); assert(db->dbh && "Else we can't have been called by sqlite3, could we have?"); if(db->beginCount>0){ - fsl__fatal(FSL_RC_MISUSE,"SQL: COMMIT was called from " + fsl_fatal(FSL_RC_MISUSE,"SQL: COMMIT was called from " "outside of fsl_db_transaction_end() while a " "fsl_db_transaction_begin()-started transaction " "is pending."); return 2; } @@ -16187,21 +15793,22 @@ } } db->dbh = dbh; if(FSL_OPEN_F_SCHEMA_VALIDATE & openFlags){ int check; - check = fsl__db_repo_verify_schema(db); + check = fsl_db_repo_verify_schema(db); if(0 != check){ rc = (check<0) ? fsl_error_set(&db->error, FSL_RC_NOT_A_REPO, "DB file [%s] does not appear to be " "a repository.", dbFile) : fsl_error_set(&db->error, FSL_RC_REPO_NEEDS_REBUILD, "DB file [%s] appears to be a fossil " "repsitory, but is out-of-date and needs " "a rebuild.", - dbFile); + dbFile) + ; assert(rc == db->error.code); goto end; } } @@ -16449,11 +16056,12 @@ if(doRollback) ++db->doRollback /* ACHTUNG: note that db->dbRollback is set before continuing so that if we return due to a non-0 beginCount that the rollback flag propagates through the transaction's stack. - */; + */ + ; if(--db->beginCount > 0) return 0; assert(0==db->beginCount && "The commit-hook check relies on this."); assert(db->doRollback>=0); if((0==db->doRollback) && (db->priorChanges < sqlite3_total_changes(db->dbh))){ @@ -16472,13 +16080,13 @@ Much later: we have that routine now but will need to replace all relevant calls to fsl_db_transaction_begin()/end() with those routines before we can consider moving this there. */ - rc = fsl__repo_leafdo_pending_checks(db->f); + rc = fsl_repo_leaf_do_pending_checks(db->f); if(!rc && db->f->cache.toVerify.used){ - rc = fsl__repo_verify_at_commit(db->f); + rc = fsl_repo_verify_at_commit(db->f); }else{ fsl_repo_verify_cancel(db->f); } } db->doRollback = rc ? 1 : 0; @@ -17161,14 +16769,14 @@ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) typedef int StaticAssertMCacheArraySizes[ - ((sizeof(fsl__mcache_empty.aAge) - /sizeof(fsl__mcache_empty.aAge[0])) - == (sizeof(fsl__mcache_empty.decks) - /sizeof(fsl__mcache_empty.decks[0]))) + ((sizeof(fsl_mcache_empty.aAge) + /sizeof(fsl_mcache_empty.aAge[0])) + == (sizeof(fsl_mcache_empty.decks) + /sizeof(fsl_mcache_empty.decks[0]))) ? 1 : -1 ]; enum fsl_card_F_list_flags_e { FSL_CARD_F_LIST_NEEDS_SORT = 0x01 @@ -17180,19 +16788,19 @@ calling this the caller must behave as if the deck had been passed to fsl_deck_finalize(). If manifest caching is disabled for f, d is immediately finalized. */ -static void fsl__cx_mcache_insert(fsl_cx *f, fsl_deck * d){ +static void fsl_cx_mcache_insert(fsl_cx *f, fsl_deck * d){ if(!(f->flags & FSL_CX_F_MANIFEST_CACHE)){ fsl_deck_finalize(d); return; } static const unsigned cacheLen = - (unsigned)(sizeof(fsl__mcache_empty.aAge) - /sizeof(fsl__mcache_empty.aAge[0])); - fsl__mcache * const mc = &f->cache.mcache; + (unsigned)(sizeof(fsl_mcache_empty.aAge) + /sizeof(fsl_mcache_empty.aAge[0])); + fsl_mcache * const mc = &f->cache.mcache; while( d ){ unsigned i; fsl_deck *pBaseline = d->B.baseline; d->B.baseline = 0; for(i=0; iflags & FSL_CX_F_MANIFEST_CACHE)){ - ++f->cache.mcache.misses; - return false; - } +static bool fsl_cx_mcache_search(fsl_cx * f, fsl_id_t rid, fsl_deck * tgt){ + if(!(f->flags & FSL_CX_F_MANIFEST_CACHE)) return false; static const unsigned cacheLen = - (int)(sizeof(fsl__mcache_empty.aAge) - /sizeof(fsl__mcache_empty.aAge[0])); + (int)(sizeof(fsl_mcache_empty.aAge) + /sizeof(fsl_mcache_empty.aAge[0])); unsigned i; assert(cacheLen == - (unsigned)(sizeof(fsl__mcache_empty.decks) - /sizeof(fsl__mcache_empty.decks[0]))); + (unsigned)(sizeof(fsl_mcache_empty.decks) + /sizeof(fsl_mcache_empty.decks[0]))); for(i=0; icache.mcache.decks[i].rid==rid ){ *tgt = f->cache.mcache.decks[i]; f->cache.mcache.decks[i] = fsl_deck_empty; ++f->cache.mcache.hits; @@ -17256,45 +16860,10 @@ } } ++f->cache.mcache.misses; return false; } - -/** - Code duplication reducer for fsl_deck_parse2() and - fsl_deck_load_rid(). Checks fsl__cx_mcache_search() for rid. If - found, overwrites tgt with its contents and returns true. If not - found, returns false. If an entry is found and type!=FSL_SATYPE_ANY - and the found deck->type differs from type then false is returned, - FSL_RC_TYPE is returned via *rc, and f's error state is updated - with a description of the problem. In all other case *rc is set to - 0. - */ -static bool fsl__cx_mcache_search2(fsl_cx * const f, fsl_id_t rid, - fsl_deck * const tgt, - fsl_satype_e type, - int * const rc){ - *rc = 0; - if(fsl__cx_mcache_search(f, rid, tgt)){ - assert(f == tgt->f); - if(type!=FSL_SATYPE_ANY && type!=tgt->type){ - *rc = fsl_cx_err_set(f, FSL_RC_TYPE, - "Unexpected match of RID #%" FSL_ID_T_PFMT " " - "to a different artifact type (%d) " - "than requested (%d).", - tgt->type, type); - fsl__cx_mcache_insert(f, tgt); - assert(!tgt->f); - return false; - }else{ - //MARKER(("Got cached deck: rid=%d\n", (int)d->rid)); - return true; - } - } - return false; -} - /** If mem is NULL or inside d->content.mem then this function does nothing, else it passes mem to fsl_free(). Intended to be used to clean up d->XXX string members (or sub-members) which have been @@ -17510,19 +17079,19 @@ rc->allocStamp = &fsl_deck_empty; } return rc; } -void fsl_deck_init( fsl_cx * const f, fsl_deck * const cards, fsl_satype_e type ){ +void fsl_deck_init( fsl_cx * f, fsl_deck * cards, fsl_satype_e type ){ void const * allocStamp = cards->allocStamp; *cards = fsl_deck_empty; cards->allocStamp = allocStamp; cards->f = f; cards->type = type; } -void fsl__card_J_list_free(fsl_list * li, bool alsoListMem){ +void fsl_card_J_list_free(fsl_list * li, bool alsoListMem){ if(li->used) fsl_list_visit(li, 0, fsl_list_v_card_J_free, NULL); if(alsoListMem) fsl_list_reserve(li, 0); else li->used = 0; } @@ -17571,11 +17140,11 @@ } static void fsl_deck_clean_I(fsl_deck * const m){ fsl_deck_clean_string(m, &m->I); } static void fsl_deck_clean_J(fsl_deck * const m, bool alsoListMem){ - fsl__card_J_list_free(&m->J, alsoListMem); + fsl_card_J_list_free(&m->J, alsoListMem); } static void fsl_deck_clean_K(fsl_deck * const m){ fsl_deck_clean_string(m, &m->K); } static void fsl_deck_clean_L(fsl_deck * const m){ @@ -18292,11 +17861,11 @@ if(!rc){ fsl_md5_final(&md5, digest); fsl_md5_digest_to_base16(digest, hex); } end: - fsl__cx_content_buffer_yield(f); + fsl_cx_content_buffer_yield(f); assert(0==buf->used); if(rc) return rc; fsl_deck_F_rewind(mf); theHash = hex; } @@ -18606,11 +18175,11 @@ /* Holds error state for propagating back to the client. */ fsl_error error; /** Scratch buffer for fossilizing bytes and other temporary work. - This value comes from fsl__cx_scratchpad(). + This value comes from fsl_cx_scratchpad(). */ fsl_buffer * scratch; }; typedef struct fsl_deck_out_state fsl_deck_out_state; static const fsl_deck_out_state fsl_deck_out_state_empty = { @@ -18915,11 +18484,11 @@ /** A comparison routine for qsort(3) which compares fsl_card_J instances in a lexical manner based on their names. The order is important for card ordering in generated manifests. */ -int fsl__qsort_cmp_J_cards( void const * lhs, void const * rhs ){ +int fsl_qsort_cmp_J_cards( void const * lhs, void const * rhs ){ fsl_card_J const * l = *((fsl_card_J const **)lhs); fsl_card_J const * r = *((fsl_card_J const **)rhs); /* Compare NULL as larger so that NULLs move to the right. That said, we aren't expecting any NULLs. */ assert(l); @@ -19521,11 +19090,11 @@ fsl_list * li; int rc = 0; if(!d || !d->f) return FSL_RC_MISUSE; fsl_cx_err_reset(d->f); #define SORT(CARD,CMP) li = &d->CARD; fsl_list_sort(li, CMP) - SORT(J,fsl__qsort_cmp_J_cards); + SORT(J,fsl_qsort_cmp_J_cards); SORT(M,qsort_cmp_strings); SORT(Q,qsort_cmp_Q_cards); SORT(T,fsl_card_T_cmp); #undef SORT if(FSL_SATYPE_CHECKIN!=d->type){ @@ -19594,11 +19163,11 @@ } os->d = d; os->out = out; os->outState = outputState; - os->scratch = fsl__cx_scratchpad(f); + os->scratch = fsl_cx_scratchpad(f); switch(d->type){ case FSL_SATYPE_CLUSTER: rc = fsl_deck_output_cluster(os); break; case FSL_SATYPE_CONTROL: @@ -19634,11 +19203,11 @@ } if(!rc){ rc = fsl_deck_out_Z( os ); } end: - fsl__cx_scratchpad_yield(f, os->scratch); + fsl_cx_scratchpad_yield(f, os->scratch); if(os->rc && os->error.code){ fsl_error_move(&os->error, &f->error); } fsl_error_clear(&os->error); return os->rc ? os->rc : rc; @@ -19658,11 +19227,11 @@ /** Adds a record in the pending_xlink temp table, to be processed when crosslinking is completed. Returns 0 on success, non-0 for db error. */ -static int fsl__deck_crosslink_add_pending(fsl_cx * f, char cType, fsl_uuid_cstr uuid){ +static int fsl_deck_crosslink_add_pending(fsl_cx * f, char cType, fsl_uuid_cstr uuid){ int rc = 0; assert(f->cache.isCrosslinking); rc = fsl_db_exec(f->dbMain, "INSERT OR IGNORE INTO pending_xlink VALUES('%c%q')", cType, uuid); @@ -19713,20 +19282,20 @@ assert(f); assert(db); assert(db->beginCount>0); //MARKER(("%s() pmid=%d mid=%d\n", __func__, (int)pmid, (int)mid)); - rc = fsl__repo_filename_fnid2(f, zFilename, &fnid, 1); + rc = fsl_repo_filename_fnid2(f, zFilename, &fnid, 1); if(rc) return rc; if( zPrior && *zPrior ){ - rc = fsl__repo_filename_fnid2(f, zPrior, &pfnid, 1); + rc = fsl_repo_filename_fnid2(f, zPrior, &pfnid, 1); if(rc) return rc; }else{ pfnid = 0; } if( zFromUuid && *zFromUuid ){ - pid = fsl__uuid_to_rid2(f, zFromUuid, FSL_PHANTOM_PUBLIC); + pid = fsl_uuid_to_rid2(f, zFromUuid, FSL_PHANTOM_PUBLIC); if(pid<0){ assert(f->error.code); return f->error.code; } assert(pid>0); @@ -19733,11 +19302,11 @@ }else{ pid = 0; } if( zToUuid && *zToUuid ){ - fid = fsl__uuid_to_rid2(f, zToUuid, FSL_PHANTOM_PUBLIC); + fid = fsl_uuid_to_rid2(f, zToUuid, FSL_PHANTOM_PUBLIC); if(fid<0){ assert(f->error.code); return f->error.code; }else if( isPublic ){ rc = fsl_content_make_public(f, fid); @@ -19794,11 +19363,11 @@ } } if(!rc && pid>0 && fid){ /* Reminder to self: this costs almost 1ms per checkin in very basic tests with 2003 checkins on my NUC unit. */ - rc = fsl__content_deltify(f, pid, fid, 0); + rc = fsl_content_deltify(f, pid, fid, 0); } end: return rc; } @@ -19831,11 +19400,11 @@ indicating whether only an exact match or the "closest match" is acceptable, but currently (2021-03-10) only the fusefs module uses the closest-match option. It's a trivial code change but currently looks like YAGNI. */ -static fsl_card_F * fsl__deck_F_seek_base(fsl_deck * d, +static fsl_card_F * fsl_deck_F_seek_base(fsl_deck * d, char const * zName, uint32_t * atNdx ){ /* Maintenance reminder: this algo relies on the various counters being signed. */ fsl_int_t lwr, upr; @@ -19880,17 +19449,17 @@ } return NULL; #undef FCARD } -fsl_card_F * fsl__deck_F_seek(fsl_deck * const d, const char *zName){ +fsl_card_F * fsl_deck_F_seek(fsl_deck * const d, const char *zName){ fsl_card_F *pFile; assert(d); assert(zName && *zName); if(!d || (FSL_SATYPE_CHECKIN!=d->type) || !zName || !*zName || !d->F.used) return NULL; - pFile = fsl__deck_F_seek_base(d, zName, NULL); + pFile = fsl_deck_F_seek_base(d, zName, NULL); if( !pFile && (d->B.baseline /* we have a baseline or... */ || (d->f && d->B.uuid) /* we can load the baseline */ )){ /* Check baseline manifest... @@ -19906,11 +19475,11 @@ assert(d->f->error.code); }else if( d->B.baseline ){ assert(d->B.baseline->f && "How can this happen?"); assert((d->B.baseline->f == d->f) && "Universal laws are out of balance."); - pFile = fsl__deck_F_seek_base(d->B.baseline, zName, NULL); + pFile = fsl_deck_F_seek_base(d->B.baseline, zName, NULL); if(pFile){ assert(pFile->uuid && "Per fossil-dev thread with DRH on 20140422, " "baselines never have removed files."); } @@ -19919,11 +19488,11 @@ return pFile; } fsl_card_F const * fsl_deck_F_search(fsl_deck *d, const char *zName){ assert(d); - return fsl__deck_F_seek(d, zName); + return fsl_deck_F_seek(d, zName); } int fsl_deck_F_set( fsl_deck * d, char const * zName, char const * uuid, fsl_fileperm_e perms, @@ -19935,11 +19504,11 @@ "%s() cannot be applied to a saved deck.", __func__); }else if(!fsl_deck_check_type(d, 'F')){ return d->f->error.code; } - fc = fsl__deck_F_seek_base(d, zName, &fcNdx); + fc = fsl_deck_F_seek_base(d, zName, &fcNdx); if(!uuid){ if(fc){ fsl_card_F_list_remove(&d->F, fcNdx); return 0; }else{ @@ -20000,13 +19569,13 @@ assert(zHash); if(!rid){ fsl_card_F const * fc; /* This is new content. Save it, then see if we have a previous version to delta against this one. */ - rc = fsl__content_put_ex(d->f, src, zHash, 0, 0, false, &rid); + rc = fsl_content_put_ex(d->f, src, zHash, 0, 0, false, &rid); if(rc) goto end; - fc = fsl__deck_F_seek(d, zName); + fc = fsl_deck_F_seek(d, zName); if(fc){ prevRid = fsl_uuid_to_rid(d->f, fc->uuid); if(prevRid<0) goto end; else if(!prevRid){ assert(!"cannot happen"); @@ -20013,21 +19582,21 @@ rc = fsl_cx_err_set(d->f, FSL_RC_NOT_FOUND, "Cannot find RID of file content %s [%s]\n", fc->name, fc->uuid); goto end; } - rc = fsl__content_deltify(d->f, prevRid, rid, false); + rc = fsl_content_deltify(d->f, prevRid, rid, false); if(rc) goto end; } } rc = fsl_deck_F_set(d, zName, zHash, perm, priorName); end: fsl_free(zHash); return rc; } -void fsl__deck_clean_cards(fsl_deck * const d, char const * letters){ +void fsl_deck_clean_cards(fsl_deck * d, char const * letters){ char const * c = letters ? letters : "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; for( ; *c; ++c ){ switch(*c){ @@ -20075,11 +19644,11 @@ rc = d->f->error.code; } if(rc) return rc; } d->rid = 0; - fsl__deck_clean_cards(d, "ACDEGHIJKLMNQRTUW"); + fsl_deck_clean_cards(d, "ACDEGHIJKLMNQRTUW"); while(d->B.uuid){ /* This is a delta manifest. Convert this deck into a baseline by build a new, complete F-card list. */ fsl_card_F const * fc; fsl_card_F_list flist = fsl_card_F_list_empty; @@ -20226,11 +19795,11 @@ }else{ pParent = &dOther; otherRid = pmid; } - if(otherRid && !fsl__cx_mcache_search(f, otherRid, &dOther)){ + if(otherRid && !fsl_cx_mcache_search(f, otherRid, &dOther)){ rc = fsl_content_get(f, otherRid, &otherContent); if(rc){ /* fossil(1) simply ignores errors here and returns. We'll ignore the phantom case because (1) erroring out here would be bad and (2) fossil does so. The exact implications of doing so are @@ -20269,11 +19838,11 @@ if( !isPrimary && otherRid==cid ){ assert(pChild->P.used); if(!fsl_db_exists(db,"SELECT 1 FROM blob WHERE uuid=%Q AND size>0", (char const *)pChild->P.list[0])){ rc = 0; - fsl__cx_mcache_insert(f, &dOther); + fsl_cx_mcache_insert(f, &dOther); goto end; } } if(pmid>0){ @@ -20280,13 +19849,13 @@ /* Try to make the parent manifest a delta from the child, if that is an appropriate thing to do. For a new baseline, make the previous baseline a delta from the current baseline. */ if( (pParent->B.uuid==0)==(pChild->B.uuid==0) ){ - rc = fsl__content_deltify(f, pmid, cid, 0); + rc = fsl_content_deltify(f, pmid, cid, 0); }else if( pChild->B.uuid==NULL && pParent->B.uuid!=NULL ){ - rc = fsl__content_deltify(f, pParent->B.baseline->rid, cid, 0); + rc = fsl_content_deltify(f, pParent->B.baseline->rid, cid, 0); } if(rc) goto end; } /* Remember all children less than a few seconds younger than their parent, @@ -20314,11 +19883,11 @@ iF.used; ++i, pChildFile=FCARD(pChild,i)){ fsl_fileperm_e const mperm = pChildFile->perm; if( pChildFile->priorName ){ pParentFile = pmid - ? fsl__deck_F_seek(pParent, pChildFile->priorName) + ? fsl_deck_F_seek(pParent, pChildFile->priorName) : 0; if( pParentFile ){ /* File with name change */ /* libfossil checkin 8625a31eff708dea93b16582e4ec5d583794d1af @@ -20341,11 +19910,11 @@ rc = fsl_mlink_add_one(f, pmid, 0, cid, pChildFile->uuid, pChildFile->name, 0, isPublic, isPrimary, mperm); } }else if(pmid){ - pParentFile = fsl__deck_F_seek(pParent, pChildFile->name); + pParentFile = fsl_deck_F_seek(pParent, pChildFile->name); if(!pParentFile || !pParentFile->uuid){ /* Parent does not have it or it was removed in parent. */ if( pChildFile->uuid ){ /* A new or re-added file */ rc = fsl_mlink_add_one(f, pmid, 0, cid, pChildFile->uuid, @@ -20371,16 +19940,16 @@ in the child. */ for(i=0, pParentFile=FCARD(pParent,0); iF.used; ++i, pParentFile = FCARD(pParent,i)){ if( pParentFile->uuid ){ - pChildFile = fsl__deck_F_seek_base(pChild, pParentFile->name, NULL); + pChildFile = fsl_deck_F_seek_base(pChild, pParentFile->name, NULL); if( !pChildFile || !pChildFile->uuid){ /* The child file reverts to baseline or is deleted. Show this as a change. */ if(!pChildFile){ - pChildFile = fsl__deck_F_seek(pChild, pParentFile->name); + pChildFile = fsl_deck_F_seek(pChild, pParentFile->name); } if( pChildFile && pChildFile->uuid ){ rc = fsl_mlink_add_one(f, pmid, pParentFile->uuid, cid, pChildFile->uuid, pChildFile->name, 0, isPublic, isPrimary, @@ -20387,11 +19956,11 @@ pChildFile->perm); } } }else{ /* Was deleted in the parent. */ - pChildFile = fsl__deck_F_seek(pChild, pParentFile->name); + pChildFile = fsl_deck_F_seek(pChild, pParentFile->name); if( pChildFile && pChildFile->uuid ){ /* File resurrected in the child after having been deleted in the parent. Show this as an added file. */ rc = fsl_mlink_add_one(f, pmid, 0, cid, pChildFile->uuid, pChildFile->name, 0, isPublic, @@ -20407,21 +19976,21 @@ having been deleted. */ fsl_card_F const * cfc = NULL; fsl_deck_F_rewind(pParent); while( (0==(rc=fsl_deck_F_next(pParent,&cfc))) && cfc){ pParentFile = cfc; - pChildFile = fsl__deck_F_seek(pChild, pParentFile->name); + pChildFile = fsl_deck_F_seek(pChild, pParentFile->name); if( (!pChildFile || !pChildFile->uuid) && pParentFile->uuid ){ rc = fsl_mlink_add_one(f, pmid, pParentFile->uuid, cid, 0, pParentFile->name, 0, isPublic, isPrimary, pParentFile->perm); } } if(rc) goto end; } - fsl__cx_mcache_insert(f, &dOther); + fsl_cx_mcache_insert(f, &dOther); /* If pParent is the primary parent of pChild, also run this analysis ** for all merge parents of pChild */ if( pmid && isPrimary ){ for(i=1; iP.used; i++){ @@ -20453,11 +20022,11 @@ Apply all tags defined in deck d. If parentId is >0 then any propagating tags from that parent are well and duly propagated. Returns 0 on success. Potential TODO: if parentId<=0 and d->P.used>0 then use d->P.list[0] in place of parentId. */ -static int fsl__deck_crosslink_apply_tags(fsl_cx * f, fsl_deck *d, +static int fsl_deck_crosslink_apply_tags(fsl_cx * f, fsl_deck *d, fsl_db * db, fsl_id_t rid, fsl_id_t parentId){ int rc = 0; fsl_size_t i; fsl_list const * li = &d->T; @@ -20486,16 +20055,16 @@ rc = fsl_cx_err_set(f, FSL_RC_RANGE, "Could not get RID for [%.12s].", tag->uuid); break; } - rc = fsl__tag_insert(f, tag->type, + rc = fsl_tag_insert(f, tag->type, tag->name, tag->value, rid, tagTime, tid, NULL); } if( !rc && (parentId>0) ){ - rc = fsl__tag_propagate_all(f, parentId); + rc = fsl_tag_propagate_all(f, parentId); } end: return rc; } @@ -20535,11 +20104,11 @@ fsl_snprintf( zBaseId, sizeof(zBaseId), "NULL" ); } *parentId = 0; for(i=0; iP.used; ++i){ char const * parentUuid = (char const *)d->P.list[i]; - fsl_id_t const pid = fsl__uuid_to_rid2(f, parentUuid, FSL_PHANTOM_PUBLIC); + fsl_id_t const pid = fsl_uuid_to_rid2(f, parentUuid, FSL_PHANTOM_PUBLIC); if(pid<0){ assert(f->error.code); rc = f->error.code; goto end; } @@ -20608,11 +20177,11 @@ } /** Applies the value of a "parent" tag (reparent) to the given artifact id. zTagVal must be the value of a parent tag (a list of - full UUIDs). This is only to be run as part of fsl__crosslink_end(). + full UUIDs). This is only to be run as part of fsl_crosslink_end(). Returns 0 on success. POTENTIAL fixme: perhaps return without side effects if rid is not found (like fossil(1) does). That said, this step is only run after @@ -20698,11 +20267,11 @@ Inserts plink entries for FORUM, WIKI, and TECHNOTE manifests. May assert for other manifest types. If a parent entry exists, it also propagates any tags for that parent. This is a no-op if the deck has no parents. */ -static int fsl__deck_crosslink_fwt_plink(fsl_deck * d){ +static int fsl_deck_crosslink_fwt_plink(fsl_deck * d){ int i; fsl_id_t parentId = 0; fsl_db * db; int rc = 0; assert(d->type==FSL_SATYPE_WIKI || @@ -20710,14 +20279,14 @@ d->type==FSL_SATYPE_TECHNOTE); assert(d->f); assert(d->rid>0); if(!d->P.used) return rc; db = fsl_cx_db_repo(d->f); - fsl__phantom_e const fantomMode = fsl_content_is_private(d->f, d->rid) + fsl_phantom_e const fantomMode = fsl_content_is_private(d->f, d->rid) ? FSL_PHANTOM_PRIVATE : FSL_PHANTOM_PUBLIC; for(i=0; 0==rc && i<(int)d->P.used; ++i){ - fsl_id_t const pid = fsl__uuid_to_rid2(d->f, (char const *)d->P.list[i], + fsl_id_t const pid = fsl_uuid_to_rid2(d->f, (char const *)d->P.list[i], fantomMode); if(0==i) parentId = pid; rc = fsl_db_exec_multi(db, "INSERT OR IGNORE INTO plink" "(pid, cid, isprim, mtime, baseid)" @@ -20724,32 +20293,33 @@ "VALUES(%"FSL_ID_T_PFMT", %"FSL_ID_T_PFMT", " "%d, %"FSL_JULIAN_T_PFMT", NULL)", pid, d->rid, i==0, d->D); } if(!rc && parentId){ - rc = fsl__tag_propagate_all(d->f, parentId); + rc = fsl_tag_propagate_all(d->f, parentId); } return rc; } /** Overrideable crosslink listener which updates the timeline for attachment records. */ -static int fsl_deck_xlink_f_attachment(fsl_deck * const d, void * state){ +static int fsl_deck_xlink_f_attachment(fsl_deck * d, void * state){ if(FSL_SATYPE_ATTACHMENT!=d->type) return 0; int rc; - fsl_db * const db = fsl_cx_db_repo(d->f); + fsl_db * db; fsl_buffer comment = fsl_buffer_empty; const char isAdd = (d->A.src && *d->A.src) ? 1 : 0; char attachToType = 'w' /* Assume wiki until we know otherwise, keeping in mind that the d->A.tgt might not yet be in the blob table, in which case we are unable to know, for certain, what the target is. That only affects the timeline (event table), though, not the crosslinking of the attachment itself. */; + db = fsl_cx_db_repo(d->f); assert(db); if(fsl_is_uuid(d->A.tgt)){ if( fsl_db_exists(db, "SELECT 1 FROM tag WHERE tagname='tkt-%q'", d->A.tgt)){ attachToType = 't' /* attach to a known ticket */; @@ -20812,11 +20382,11 @@ /** Overrideable crosslink listener which updates the timeline for checkin records. */ -static int fsl_deck_xlink_f_checkin(fsl_deck * const d, void * state){ +static int fsl_deck_xlink_f_checkin(fsl_deck * d, void * state){ if(FSL_SATYPE_CHECKIN!=d->type) return 0; int rc; fsl_db * db; db = fsl_cx_db_repo(d->f); assert(db); @@ -20860,11 +20430,11 @@ (int)FSL_TAGID_COMMENT, d->rid, d->D ); return fsl_cx_uplift_db_error2(d->f, db, rc); } -static int fsl_deck_xlink_f_control(fsl_deck * const d, void * state){ +static int fsl_deck_xlink_f_control(fsl_deck * d, void * state){ if(FSL_SATYPE_CONTROL!=d->type) return 0; /* Create timeline event entry for all tags in this control construct. Note that we are using a lot of historical code which hard-codes english-lanuage text and links which only work in @@ -21007,11 +20577,11 @@ fsl_buffer_clear(&comment); return rc; } -static int fsl_deck_xlink_f_forum(fsl_deck * const d, void * state){ +static int fsl_deck_xlink_f_forum(fsl_deck * d, void * state){ if(FSL_SATYPE_FORUMPOST!=d->type) return 0; int rc = 0; fsl_db * const db = fsl_cx_db_repo(d->f); assert(db); fsl_cx * const f = d->f; @@ -21093,11 +20663,11 @@ assert(db->error.code); return fsl_cx_uplift_db_error(f, db); } -static int fsl_deck_xlink_f_technote(fsl_deck * const d, void * state){ +static int fsl_deck_xlink_f_technote(fsl_deck * d, void * state){ if(FSL_SATYPE_TECHNOTE!=d->type) return 0; char buf[FSL_STRLEN_K256 + 7 /* event-UUID\0 */] = {0}; fsl_id_t tagid; char const * zTag; int rc = 0; @@ -21142,11 +20712,11 @@ (int)FSL_TAGID_BGCOLOR, d->rid); } return rc; } -static int fsl_deck_xlink_f_wiki(fsl_deck * const d, void * state){ +static int fsl_deck_xlink_f_wiki(fsl_deck * d, void * state){ if(FSL_SATYPE_WIKI!=d->type) return 0; int rc; char const * zWiki; fsl_size_t nWiki = 0; char cPrefix = 0; @@ -21195,11 +20765,11 @@ /** @internal Installs the core overridable crosslink listeners. "The plan" is to do all updates to the event (timeline) table via these crosslinkers and perform the core, UI-agnostic, crosslinking bits - in the internal fsl__deck_crosslink_XXX() functions. That should + in the internal fsl_deck_crosslink_XXX() functions. That should allow clients to override how the timeline is updated without requiring them to understand the rest of the required schema updates. */ int fsl_cx_install_timeline_crosslinkers(fsl_cx * const f){ @@ -21220,11 +20790,11 @@ fsl_deck_xlink_f_wiki, 0); return rc; } -static int fsl__deck_crosslink_checkin(fsl_deck * const d, +static int fsl_deck_crosslink_checkin(fsl_deck * const d, fsl_id_t *parentid ){ int rc = 0; fsl_cx * const f = d->f; fsl_db * const db = fsl_cx_db_repo(f); @@ -21249,17 +20819,17 @@ if(!fsl_repo_has_mlink_mid(db, d->rid)){ rc = fsl_deck_add_checkin_linkages(d, parentid); if(rc) goto end; /* FSL-MISSING: assert( manifest_event_triggers_are_enabled ); */ - rc = fsl__search_doc_touch(f, d->type, d->rid, 0); + rc = fsl_search_doc_touch(f, d->type, d->rid, 0); if(rc) goto end; /* If this is a delta-manifest, record the fact that this repository contains delta manifests, to free the "commit" logic to generate new delta manifests. */ if(d->B.uuid){ - rc = fsl__cx_update_seen_delta_deck(f); + rc = fsl_cx_update_seen_delta_mf(f); if(rc) goto end; } assert(!rc); }/*!exists mlink*/ end: @@ -21267,11 +20837,11 @@ fsl_cx_uplift_db_error(f, db); } return rc; } -static int fsl__deck_crosslink_wiki(fsl_deck *d){ +static int fsl_deck_crosslink_wiki(fsl_deck *d){ char zLength[40] = {0}; fsl_id_t prior = 0; char const * zWiki; fsl_size_t nWiki = 0; int rc; @@ -21292,37 +20862,37 @@ one contains embedded NULs in the content. "Shouldn't happen," but the API doesn't explicitly prohibit it. */; fsl_snprintf(zLength, sizeof(zLength), "%"FSL_SIZE_T_PFMT, (fsl_size_t)nWiki); - rc = fsl__tag_insert(f, FSL_TAGTYPE_ADD, zTag, zLength, + rc = fsl_tag_insert(f, FSL_TAGTYPE_ADD, zTag, zLength, d->rid, d->D, d->rid, NULL ); if(rc) goto end; if(d->P.used){ prior = fsl_uuid_to_rid(f, (const char *)d->P.list[0]); } if(prior>0){ - rc = fsl__content_deltify(f, prior, d->rid, 0); + rc = fsl_content_deltify(f, prior, d->rid, 0); if(rc) goto end; } - rc = fsl__search_doc_touch(f, d->type, d->rid, d->L); + rc = fsl_search_doc_touch(f, d->type, d->rid, d->L); if(rc) goto end; if( f->cache.isCrosslinking ){ - rc = fsl__deck_crosslink_add_pending(f, 'w',d->L); + rc = fsl_deck_crosslink_add_pending(f, 'w',d->L); if(rc) goto end; }else{ /* FSL-MISSING: backlink_wiki_refresh(d->L); */ } assert(0==rc); - rc = fsl__deck_crosslink_fwt_plink(d); + rc = fsl_deck_crosslink_fwt_plink(d); end: fsl_free(zTag); return rc; } -static int fsl__deck_crosslink_attachment(fsl_deck * const d){ +static int fsl_deck_crosslink_attachment(fsl_deck * const d){ int rc; fsl_cx * const f = d->f; fsl_db * const db = fsl_cx_db_repo(f); rc = fsl_db_exec(db, @@ -21347,23 +20917,18 @@ d->A.tgt, d->A.name); } return rc; } -static int fsl__deck_crosslink_cluster(fsl_deck * const d){ +static int fsl_deck_crosslink_cluster(fsl_deck * const d){ /* Clean up the unclustered table... */ fsl_size_t i; fsl_stmt * st = NULL; int rc; fsl_cx * const f = d->f; fsl_db * const db = fsl_cx_db_repo(f); - assert(d->rid>0); - rc = fsl__tag_insert(f, FSL_TAGTYPE_ADD, "cluster", NULL, - d->rid, d->D, d->rid, NULL); - if(rc) return rc; - rc = fsl_db_prepare_cached(db, &st, "DELETE FROM unclustered WHERE rid=?" "/*%s()*/",__func__); if(rc) return fsl_cx_uplift_db_error(f, db); assert(st); @@ -21383,15 +20948,15 @@ fsl_stmt_cached_yield(st); return rc; } #if 0 -static int fsl__deck_crosslink_control(fsl_deck *const d){ +static int fsl_deck_crosslink_control(fsl_deck *const d){ } #endif -static int fsl__deck_crosslink_forum(fsl_deck * const d){ +static int fsl_deck_crosslink_forum(fsl_deck * const d){ int rc = 0; fsl_cx * const f = d->f; rc = fsl_repo_install_schema_forum(f); if(rc) return rc; fsl_db * const db = fsl_cx_db_repo(f); @@ -21406,19 +20971,19 @@ "nullif(%" FSL_ID_T_PFMT ",0),%"FSL_JULIAN_T_PFMT")", d->rid, froot, fprev, firt, d->D ); rc = fsl_cx_uplift_db_error2(f, db, rc); if(!rc){ - rc = fsl__search_doc_touch(f, d->type, d->rid, 0); + rc = fsl_search_doc_touch(f, d->type, d->rid, 0); } if(!rc){ - rc = fsl__deck_crosslink_fwt_plink(d); + rc = fsl_deck_crosslink_fwt_plink(d); } return rc; } -static int fsl__deck_crosslink_technote(fsl_deck * const d){ +static int fsl_deck_crosslink_technote(fsl_deck * const d){ char buf[FSL_STRLEN_K256 + 7 /* event-UUID\0 */] = {0}; char zLength[40] = {0}; fsl_id_t tagid; fsl_id_t prior = 0, subsequent; char const * zWiki; @@ -21444,11 +21009,11 @@ /* Historical behaviour: strip leading spaces. */ } nWiki = fsl_strlen(zWiki); fsl_snprintf( zLength, sizeof(zLength), "%"FSL_SIZE_T_PFMT, (fsl_size_t)nWiki); - rc = fsl__tag_insert(f, FSL_TAGTYPE_ADD, zTag, zLength, + rc = fsl_tag_insert(f, FSL_TAGTYPE_ADD, zTag, zLength, d->rid, d->D, d->rid, NULL ); if(rc) goto end; if(d->P.used){ prior = fsl_uuid_to_rid(f, (const char *)d->P.list[0]); if(prior<0){ @@ -21470,11 +21035,11 @@ assert(db->error.code); rc = fsl_cx_uplift_db_error(f, db); goto end; } else if( prior > 0 ){ - rc = fsl__content_deltify(f, prior, d->rid, 0); + rc = fsl_content_deltify(f, prior, d->rid, 0); if( !rc && !subsequent ){ rc = fsl_db_exec(db, "DELETE FROM event" " WHERE type='e'" " AND tagid=%"FSL_ID_T_PFMT @@ -21484,26 +21049,26 @@ tagid, tagid); } } if(rc) goto end; if( subsequent>0 ){ - rc = fsl__content_deltify(f, d->rid, subsequent, 0); + rc = fsl_content_deltify(f, d->rid, subsequent, 0); }else{ /* timeline update is deferred to another crosslink handler */ - rc = fsl__search_doc_touch(f, d->type, d->rid, 0); + rc = fsl_search_doc_touch(f, d->type, d->rid, 0); /* FSL-MISSING: assert( manifest_event_triggers_are_enabled ); */ } if(!rc){ - rc = fsl__deck_crosslink_fwt_plink(d); + rc = fsl_deck_crosslink_fwt_plink(d); } end: return rc; } -static int fsl__deck_crosslink_ticket(fsl_deck * const d){ +static int fsl_deck_crosslink_ticket(fsl_deck * const d){ int rc; fsl_cx * const f = d->f; #if 0 fsl_db * const db = fsl_cx_db_repo(f); #endif @@ -21524,23 +21089,28 @@ "manifest_crosslink(). It requires infrastructure " "libfossil does not yet have."); return rc; } -int fsl__deck_crosslink_one( fsl_deck * const d ){ +int fsl_deck_crosslink_one( fsl_deck * const d ){ int rc; - assert(d->f && "API misuse:fsl_deck::f == NULL"); - rc = fsl__crosslink_begin(d->f); + if(!d->f) return FSL_RC_MISUSE; + rc = fsl_crosslink_begin(d->f); if(rc) return rc; - rc = fsl__deck_crosslink(d); - assert(0!=fsl_db_transaction_level(fsl_cx_db_repo(d->f)) - && "Expecting transaction level from fsl__crosslink_begin()"); - rc = fsl__crosslink_end(d->f, rc); + rc = fsl_deck_crosslink(d); + if(rc){ + fsl_db_transaction_rollback(fsl_cx_db_repo(d->f)) + /* Ignore result - keep existing error state */; + d->f->cache.isCrosslinking = false; + }else{ + assert(fsl_db_transaction_level(fsl_cx_db_repo(d->f))); + rc = fsl_crosslink_end(d->f); + } return rc; } -int fsl__deck_crosslink( fsl_deck /* const */ * const d ){ +int fsl_deck_crosslink( fsl_deck /* const */ * const d ){ int rc = 0; fsl_cx * f = d->f; fsl_db * db = f ? fsl_needs_repo(f) : NULL; fsl_id_t parentid = 0; fsl_int_t const rid = d->rid; @@ -21568,44 +21138,44 @@ if(rc) goto end; assert(d->B.baseline); } switch(d->type){ case FSL_SATYPE_CHECKIN: - rc = fsl__deck_crosslink_checkin(d, &parentid); + rc = fsl_deck_crosslink_checkin(d, &parentid); break; case FSL_SATYPE_CLUSTER: - rc = fsl__deck_crosslink_cluster(d); + rc = fsl_deck_crosslink_cluster(d); break; default: break; } if(rc) goto end; switch(d->type){ case FSL_SATYPE_CONTROL: case FSL_SATYPE_CHECKIN: case FSL_SATYPE_TECHNOTE: - rc = fsl__deck_crosslink_apply_tags(f, d, db, rid, parentid); + rc = fsl_deck_crosslink_apply_tags(f, d, db, rid, parentid); break; default: break; } if(rc) goto end; switch(d->type){ case FSL_SATYPE_WIKI: - rc = fsl__deck_crosslink_wiki(d); + rc = fsl_deck_crosslink_wiki(d); break; case FSL_SATYPE_FORUMPOST: - rc = fsl__deck_crosslink_forum(d); + rc = fsl_deck_crosslink_forum(d); break; case FSL_SATYPE_TECHNOTE: - rc = fsl__deck_crosslink_technote(d); + rc = fsl_deck_crosslink_technote(d); break; case FSL_SATYPE_TICKET: - rc = fsl__deck_crosslink_ticket(d); + rc = fsl_deck_crosslink_ticket(d); break; case FSL_SATYPE_ATTACHMENT: - rc = fsl__deck_crosslink_attachment(d); + rc = fsl_deck_crosslink_attachment(d); break; /* FSL_SATYPE_CONTROL is handled above except for the timeline update, which is handled by a callback below */ default: break; @@ -21621,19 +21191,15 @@ rc = xl->f( d, xl->state ); } if(rc){ assert(xl); if(!f->error.code){ - if(f->dbMain->error.code){ - fsl_cx_uplift_db_error(f, f->dbMain); - }else{ - fsl_cx_err_set(f, rc, "Crosslink callback handler " - "'%s' failed with code %d (%s) for " - "artifact RID #%" FSL_ID_T_PFMT ".", - xl->name, rc, fsl_rc_cstr(rc), - d->rid); - } + fsl_cx_err_set(f, rc, "Crosslink callback handler " + "'%s' failed with code %d (%s) for " + "artifact RID #%" FSL_ID_T_PFMT ".", + xl->name, rc, fsl_rc_cstr(rc), + d->rid); } } }/*end crosslink callbacks*/ end: if(!rc){ @@ -21643,11 +21209,11 @@ fsl_cx_uplift_db_error(f,db); } fsl_db_transaction_end(db, true); } return rc; -}/*end fsl__deck_crosslink()*/ +}/*end fsl_deck_crosslink()*/ /** Return true if z points to the first character after a blank line. @@ -21691,59 +21257,26 @@ ? 1 : -1; } } - -/** @internal - - Remove the PGP signature from a raw artifact, if there is one. - - Expects *pz to point to *pn bytes of string memory which might - or might not be prefixed by a PGP signature. If the string is - enveloped in a signature, then upon returning *pz will point to - the first byte after the end of the PGP header and *pn will - contain the length of the content up to, but not including, the - PGP footer. - - If *pz does not look like a PGP header then this is a no-op. - - Neither pointer may be NULL and *pz must point to *pn bytes of - valid memory. If *pn is initially less than 59, this is a no-op. -*/ -static void fsl__remove_pgp_signature(unsigned char const **pz, fsl_size_t *pn){ +void fsl_remove_pgp_signature(unsigned char const **pz, fsl_size_t *pn){ unsigned char const *z = *pz; fsl_int_t n = (fsl_int_t)*pn; fsl_int_t i; if( n<59 || memcmp(z, "-----BEGIN PGP SIGNED MESSAGE-----", 34)!=0 ) return; for(i=34; i=n ) return; z += i; - *pz = z; -#if 1 - unsigned char const * bps = - (unsigned char const *)strstr((char const *)z, "\n-----BEGIN PGP SIGNATURE-"); - if(bps){ - n = (fsl_int_t)(bps - z) + 1 /*newline*/; - } -#else n -= i; + *pz = z; for(i=n-1; i>=0; i--){ if( z[i]=='\n' && memcmp(&z[i],"\n-----BEGIN PGP SIGNATURE-", 25)==0 ){ - /** valgrind warns on ^^^^ this ^^^^ line: - Conditional jump or move depends on uninitialised value(s) - - It affects at least 2 artifacts in the libfossil repo: - - 240deb757b12bf953b5dbf5c087c80f60ae68934 - f4e5795f9ec7df587756f08ea875c8be259b7917 - */ n = i+1; break; } } -#endif *pn = (fsl_size_t)n; return; } @@ -21857,23 +21390,23 @@ else if(L('D') && L('T') && L('U')) return FSL_SATYPE_CONTROL; #undef L return FSL_SATYPE_ANY; } -bool fsl_might_be_artifact(fsl_buffer const * const src){ +bool fsl_might_be_artifact(fsl_buffer const * src){ unsigned const char * z = src->mem; fsl_size_t n = src->used; - if(n<36) return false; - fsl__remove_pgp_signature(&z, &n); - if(n<36) return false; + if(n<36) return 0; + fsl_remove_pgp_signature(&z, &n); + if(n<36) return 0; else if(z[0]<'A' || z[0]>'Z' || z[1]!=' ' || z[n-35]!='Z' || z[n-34]!=' ' || !fsl_validate16((const char *)z+n-33, FSL_STRLEN_MD5)){ - return false; + return 0; } - return true; + return 1; } int fsl_deck_parse2(fsl_deck * const d, fsl_buffer * const src, fsl_id_t rid){ #ifdef ERROR # undef ERROR @@ -21902,32 +21435,17 @@ lettersSeen keeps track of the card letters we have seen so that we can then relatively quickly figure out what type of manifest we have parsed without having to inspect the card contents. Each index records a count of how many of that card we've seen. */ - int lettersSeen[27] = {0/*A*/,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0/*Z*/, - 0 /* sentinel element for reasons lost to - history but removing it breaks stuff. */}; + int lettersSeen[27] = {0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0}; if(!d->f || !z) return FSL_RC_MISUSE; /* Every control artifact ends with a '\n' character. Exit early if that is not the case for this artifact. */ f = d->f; - - if(rid>0){ - d->rid = rid; -#if 1 - if(fsl__cx_mcache_search2(f, rid, d, FSL_SATYPE_ANY, &rc) || rc){ - if(0==rc){ - assert(d->rid == rid); - fsl_buffer_clear(src); - } - return rc; - } -#endif - } - err = &f->error; if(!*z || !n || ( '\n' != z[n-1]) ){ return fsl_error_set(err, FSL_RC_SYNTAX, "%s.", n ? "Not terminated with \\n" : "Zero-length input"); @@ -21954,11 +21472,12 @@ /* Verify that the first few characters of the artifact look like a control artifact. */ if( !fsl_might_be_artifact(src) ){ - SYNTAX("Content does not look like a structural artifact"); + ERROR(FSL_RC_SYNTAX, "Content does not look like " + "a structural artifact"); } /* Strip off the PGP signature if there is one. Example of signed manifest: @@ -21965,11 +21484,11 @@ https://fossil-scm.org/index.html/artifact/28987096ac */ { unsigned char const * zz = z; - fsl__remove_pgp_signature(&zz, &n); + fsl_remove_pgp_signature(&zz, &n); z = (unsigned char *)zz; } /* Verify the Z card */ if( fsl_deck_verify_Z_card(z, n) < 0 ){ @@ -22041,11 +21560,11 @@ attachment is connected. */ case 'A':{ unsigned char * name, * src; if(1N = (char *)token; @@ -22379,11 +21899,11 @@ this artifact. The first parent is the primary parent. All others are parents by merge. */ case 'P':{ if(1R = (char *)token; @@ -22540,11 +22060,11 @@ } TOKEN(0); TOKEN_EXISTS("Missing size token for W-card"); wlen = fsl_str_to_size((char const *)token); if((fsl_size_t)-1==wlen){ - SYNTAX("Wiki size token is invalid"); + ERROR(FSL_RC_RANGE,"Wiki size token is invalid"); } if( (&x.z[wlen+1]) > x.zEnd){ SYNTAX("Not enough content after W-card"); } rc = fsl_buffer_append(&d->W, x.z, wlen); @@ -22673,18 +22193,14 @@ } assert(!d->content.mem); if(stealBuf>0){ /* We stashed something which points to src->mem, so we need to - steal that memory. */ + steal that memory. + */ d->content = *src; *src = fsl_buffer_empty; - }else{ - /* Clearing the source buffer if we don't take it over - provides more consistency in the public API than _sometimes_ - requiring the client to clear it. */ - fsl_buffer_clear(src); } d->rid = rid; d->F.flags &= ~FSL_CARD_F_LIST_NEEDS_SORT/*we know all cards were read in order*/; return 0; @@ -22723,13 +22239,25 @@ "Invalid RID for fsl_deck_load_rid(): " "%"FSL_ID_T_PFMT, rid); } fsl_deck_clean(d); d->f = f; - if(fsl__cx_mcache_search2(f, rid, d, type, &rc) || rc){ + if(fsl_cx_mcache_search(f, rid, d)){ + assert(d->f); + if(type!=FSL_SATYPE_ANY && type!=d->type){ + rc = fsl_cx_err_set(f, FSL_RC_TYPE, + "Unexpected match of RID #%" FSL_ID_T_PFMT " " + "to a different artifact type (%d) " + "than requested (%d).", + d->type, type); + fsl_cx_mcache_insert(f, d); + assert(!d->f); + }else{ + //MARKER(("Got cached deck: %s\n", d->uuid)); + } return rc; - } + } rc = fsl_content_get(f, rid, &buf); if(rc) goto end; #if 0 MARKER(("fsl_content_get(%d) len=%d =\n%.*s\n", (int)rid, (int)buf.used, (int)buf.used, (char const*)buf.mem)); @@ -22754,11 +22282,11 @@ "but the caller requested type %s.", rid, fsl_satype_cstr(d->type), fsl_satype_cstr(type)); }else if(d->B.uuid ){ - rc = fsl__cx_update_seen_delta_deck(f); + rc = fsl_cx_update_seen_delta_mf(f); } } end: if(0==rc) d->rid = rid; fsl_buffer_clear(&buf); @@ -22981,11 +22509,11 @@ /* Starting here, don't return, use (goto end) instead. */ f->cache.markPrivate = isPrivate; { - rc = fsl__content_put_ex(f, buf, NULL, 0, + rc = fsl_content_put_ex(f, buf, NULL, 0, 0U, isPrivate, &newRid); if(rc) goto end; assert(newRid>0); } @@ -23003,11 +22531,11 @@ if(d->P.used){ fsl_id_t pid; assert(FSL_SATYPE_CHECKIN == d->type); pid = fsl_uuid_to_rid(f, (char const *)d->P.list[0]); if(pid>0){ - rc = fsl__content_deltify(f, pid, d->rid, 0); + rc = fsl_content_deltify(f, pid, d->rid, 0); if(rc) goto end; } } #endif @@ -23031,11 +22559,11 @@ "for P-card[0] (%s).", (char const *)d->P.list[0]); } goto end; } - rc = fsl__content_deltify(f, pid, d->rid, 0); + rc = fsl_content_deltify(f, pid, d->rid, 0); if(rc) goto end; } rc = fsl_db_exec_multi(db, "INSERT OR IGNORE INTO unsent " "VALUES(%"FSL_ID_T_PFMT");" @@ -23047,12 +22575,12 @@ goto end; } } rc = f->cache.isCrosslinking - ? fsl__deck_crosslink(d) - : fsl__deck_crosslink_one(d); + ? fsl_deck_crosslink(d) + : fsl_deck_crosslink_one(d); end: f->cache.markPrivate = oldPrivate; if(!rc) rc = fsl_db_transaction_end(db, 0); else fsl_db_transaction_end(db, 1); @@ -23064,41 +22592,28 @@ } fsl_buffer_reuse(buf); return rc; } -int fsl__crosslink_end(fsl_cx * const f, int resultCode){ +int fsl_crosslink_end(fsl_cx * f){ int rc = 0; - fsl_db * const db = fsl_cx_db_repo(f); + fsl_db * db = fsl_cx_db_repo(f); fsl_stmt q = fsl_stmt_empty; fsl_stmt u = fsl_stmt_empty; int i; assert(f); assert(db); - assert(f->cache.isCrosslinking && "Internal API misuse."); + assert(f->cache.isCrosslinking); if(!f->cache.isCrosslinking){ - fsl__fatal(FSL_RC_MISUSE, - "Internal API misuse: %s() called while " - "f->cache.isCrosslinking is false.", __func__); return fsl_cx_err_set(f, FSL_RC_MISUSE, "Crosslink is not running."); } f->cache.isCrosslinking = false; - if(resultCode){ - assert(0!=fsl_cx_transaction_level(f) - && "Expecting a transaction level from fsl__crosslink_begin()"); - fsl_db_transaction_end(db, true) - /* pop transaction started from fsl__crosslink_begin(). We use - fsl_db_transaction_end() instead of fsl_cx_transaction_end() - so that any db-level error which is set during a failed - rollback does not trump any pending f->error.code. */; - return resultCode; - } assert(db->beginCount > 0); /* Handle any reparenting via tags... */ - rc = fsl_cx_prepare(f, &q, + rc = fsl_db_prepare(db, &q, "SELECT rid, value FROM tagxref" " WHERE tagid=%d AND tagtype=%d", (int)FSL_TAGID_PARENT, (int)FSL_TAGTYPE_ADD); if(rc) goto end; while(FSL_RC_STEP_ROW==fsl_stmt_step(&q)){ @@ -23109,11 +22624,11 @@ } fsl_stmt_finalize(&q); if(rc) goto end; /* Process entries from pending_xlink temp table... */ - rc = fsl_cx_prepare(f, &q, "SELECT id FROM pending_xlink"); + rc = fsl_db_prepare(db, &q, "SELECT id FROM pending_xlink"); if(rc) goto end; while( FSL_RC_STEP_ROW==fsl_stmt_step(&q) ){ const char *zId = fsl_stmt_g_text(&q, 0, NULL); char cType; if(!zId || !*zId) continue; @@ -23128,24 +22643,24 @@ backlink_wiki_refresh(zId) */ continue; } } fsl_stmt_finalize(&q); - rc = fsl_cx_exec(f, "DROP TABLE pending_xlink"); + rc = fsl_db_exec(db, "DROP TABLE pending_xlink"); if(rc) goto end; /* If multiple check-ins happen close together in time, adjust their times by a few milliseconds to make sure they appear in chronological order. */ - rc = fsl_cx_prepare(f, &q, + rc = fsl_db_prepare(db, &q, "UPDATE time_fudge SET m1=m2-:incr " "WHERE m1>=m2 AND m1cache.isCrosslinking); if(f->cache.isCrosslinking){ return fsl_cx_err_set(f, FSL_RC_MISUSE, "Crosslink is already running."); } - rc = fsl_cx_transaction_begin(f); - if(rc) return rc; - rc = fsl_cx_exec_multi(f, + rc = fsl_db_transaction_begin(db); + if(rc) return fsl_cx_uplift_db_error(f, db); + rc = fsl_db_exec_multi(db, "CREATE TEMP TABLE pending_xlink(id TEXT PRIMARY KEY)WITHOUT ROWID;" "CREATE TEMP TABLE time_fudge(" " mid INTEGER PRIMARY KEY," /* The rid of a manifest */ " m1 REAL," /* The timestamp on mid */ " cid INTEGER," /* A child or mid */ " m2 REAL" /* Timestamp on the child */ ");"); - if(0==rc){ - f->cache.isCrosslinking = true; + if(!rc){ + f->cache.isCrosslinking = 1; + return 0; }else{ - fsl_cx_transaction_end(f, true); + rc = fsl_cx_uplift_db_error2(f, db, rc); + fsl_db_transaction_rollback(db); + return rc; } - return rc; } #undef MARKER #undef AGE_FUDGE_WINDOW #undef AGE_ADJUST_INCREMENT @@ -24130,11 +23652,11 @@ exhaustive search. This version of the LCS is only used for shorter input strings since runtime is O(N*N) where N is the input string length. */ static void fsl__diff_optimal_lcs( - fsl__diff_cx * const p, /* Two files being compared */ + fsl_diff_cx * const p, /* Two files being compared */ int iS1, int iE1, /* Range of lines in p->aFrom[] */ int iS2, int iE2, /* Range of lines in p->aTo[] */ int *piSX, int *piEX, /* Write p->aFrom[] common segment here */ int *piSY, int *piEY /* Write p->aTo[] common segment here */ ){ @@ -24184,11 +23706,11 @@ as well. But if the O(N) algorithm doesn't get a good solution and N is not too large, we fall back to an exact solution by calling fsl__diff_optimal_lcs(). */ static void fsl__diff_lcs( - fsl__diff_cx * const p, /* Two files being compared */ + fsl_diff_cx * const p, /* Two files being compared */ int iS1, int iE1, /* Range of lines in p->aFrom[] */ int iS2, int iE2, /* Range of lines in p->aTo[] */ int *piSX, int *piEX, /* Write p->aFrom[] common segment here */ int *piSY, int *piEY /* Write p->aTo[] common segment here */ ){ @@ -24284,11 +23806,11 @@ *piEY = iEYb; } } -void fsl__dump_triples(fsl__diff_cx const * const p, +void fsl__dump_triples(fsl_diff_cx const * const p, char const * zFile, int ln ){ // Compare this with (fossil xdiff --raw) on the same inputs fprintf(stderr,"%s:%d: Compare this with (fossil xdiff --raw) on the same inputs:\n", zFile, ln); for(int i = 0; p->aEdit[i] || p->aEdit[i+1] || p->aEdit[i+2]; i+=3){ @@ -24298,11 +23820,11 @@ } /** @internal Expand the size of p->aEdit array to hold at least nEdit elements. */ -static int fsl__diff_expand_edit(fsl__diff_cx * const p, int nEdit){ +static int fsl__diff_expand_edit(fsl_diff_cx * const p, int nEdit){ void * re = fsl_realloc(p->aEdit, nEdit*sizeof(int)); if(!re) return FSL_RC_OOM; else{ p->aEdit = (int*)re; p->nEditAlloc = nEdit; @@ -24313,11 +23835,11 @@ /** Append a new COPY/DELETE/INSERT triple. Returns 0 on success, FSL_RC_OOM on OOM. */ -static int appendTriple(fsl__diff_cx *p, int nCopy, int nDel, int nIns){ +static int appendTriple(fsl_diff_cx *p, int nCopy, int nDel, int nIns){ /* printf("APPEND %d/%d/%d\n", nCopy, nDel, nIns); */ if( p->nEdit>=3 ){ if( p->aEdit[p->nEdit-1]==0 ){ if( p->aEdit[p->nEdit-2]==0 ){ p->aEdit[p->nEdit-3] += nCopy; @@ -24357,11 +23879,11 @@ the two segments being diffed. Then recursively compute differences on the blocks before and after that common segment. Special cases apply if either input segment is empty or if the two segments have no text in common. */ -static int diff_step(fsl__diff_cx *p, int iS1, int iE1, int iS2, int iE2){ +static int diff_step(fsl_diff_cx *p, int iS1, int iE1, int iS2, int iE2){ int iSX, iEX, iSY, iEY; int rc = 0; if( iE1<=iS1 ){ /* The first segment is empty */ if( iE2>iS2 ){ @@ -24392,11 +23914,11 @@ rc = appendTriple(p, 0, iE1-iS1, iE2-iS2); } return rc; } -int fsl__diff_all(fsl__diff_cx * const p){ +int fsl__diff_all(fsl_diff_cx * const p){ int mnE, iS, iE1, iE2; int rc = 0; /* Carve off the common header and footer */ iE1 = p->nFrom; iE2 = p->nTo; @@ -24462,11 +23984,11 @@ + int func3(int x){ int func3(int x){ return x/5; return x/5; } } */ -void fsl__diff_optimize(fsl__diff_cx * const p){ +void fsl__diff_optimize(fsl_diff_cx * const p){ int r; /* Index of current triple */ int lnFrom; /* Line number in p->aFrom */ int lnTo; /* Line number in p->aTo */ int cpy, del, ins; @@ -24541,11 +24063,11 @@ /* Given a raw diff p[] in which the p->aEdit[] array has been filled in, compute a context diff into pOut. */ static int contextDiff( - fsl__diff_cx *p, /* The difference */ + fsl_diff_cx *p, /* The difference */ DiffOutState *pOut, /* Output a context diff to here */ ReCompiled *pRe, /* Only show changes that match this regex */ u64 diffFlags /* Flags controlling the diff format */ ){ fsl_dline *A; /* Left side of the diff */ @@ -25423,11 +24945,11 @@ /* Given a diff context in which the aEdit[] array has been filled in, compute a side-by-side diff into pOut. */ static int sbsDiff( - fsl__diff_cx *p, /* The computed diff */ + fsl_diff_cx *p, /* The computed diff */ DiffOutState *pOut, /* Write the results here */ ReCompiled *pRe, /* Only show changes that match this regex */ u64 diffFlags /* Flags controlling the diff */ ){ fsl_dline *A; /* Left side of the diff */ @@ -25750,20 +25272,20 @@ short sbsWidth, int diffFlags_, /* FSL_DIFF_* flags */ int ** outRaw ){ int rc; - fsl__diff_cx c = fsl__diff_cx_empty; + fsl_diff_cx c = fsl_diff_cx_empty; uint64_t diffFlags = fsl_diff_flags_convert(diffFlags_) | DIFF_CONTEXT_EX /* to shoehorn newer 0-handling semantics into older (ported-in) code. */; if(!pA || !pB || (out && outRaw) || (!out && !outRaw)) return FSL_RC_MISUSE; else if(contextLines<0) contextLines = 5; - else if(contextLines & ~FSL__LINE_LENGTH_MASK){ - contextLines = (int)FSL__LINE_LENGTH_MASK; + else if(contextLines & ~FSL_LINE_LENGTH_MASK){ + contextLines = (int)FSL_LINE_LENGTH_MASK; } - diffFlags |= (FSL__LINE_LENGTH_MASK & contextLines); + diffFlags |= (FSL_LINE_LENGTH_MASK & contextLines); /* Encode SBS width... */ if(sbsWidth<0 || ((DIFF_SIDEBYSIDE & diffFlags) && !sbsWidth) ) sbsWidth = 80; if(sbsWidth) diffFlags |= DIFF_SIDEBYSIDE; diffFlags |= ((int)(sbsWidth & 0xFF))<<16; @@ -25830,11 +25352,11 @@ fsl_free(c.aTo); fsl_free(c.aEdit); return rc; } -int fsl__diff_text_raw(fsl_buffer const *p1, fsl_buffer const *p2, +int fsl_diff_text_raw(fsl_buffer const *p1, fsl_buffer const *p2, int diffFlags, int ** outRaw){ return fsl_diff_text_impl(p1, p2, NULL, NULL, 0, 0, diffFlags, outRaw); } int fsl_diff_text(fsl_buffer const *pA, fsl_buffer const *pB, @@ -25939,19 +25461,19 @@ const fsl_diff_opt fsl_diff_opt_empty = fsl_diff_opt_empty_m; const fsl_diff_builder fsl_diff_builder_empty = fsl_diff_builder_empty_m; const fsl_dline fsl_dline_empty = fsl_dline_empty_m; const fsl_dline_change fsl_dline_change_empty = fsl_dline_change_empty_m; -const fsl__diff_cx fsl__diff_cx_empty = fsl__diff_cx_empty_m; +const fsl_diff_cx fsl_diff_cx_empty = fsl_diff_cx_empty_m; -void fsl__diff_cx_clean(fsl__diff_cx * const cx){ +void fsl__diff_cx_clean(fsl_diff_cx * const cx){ fsl_free(cx->aFrom); fsl_free(cx->aTo); fsl_free(cx->aEdit); cx->aFrom = cx->aTo = NULL; cx->aEdit = NULL; - *cx = fsl__diff_cx_empty; + *cx = fsl_diff_cx_empty; } /** Counts the number of lines in the first n bytes of the given string. If n<0 then fsl_strlen() is used to count it. @@ -26013,11 +25535,11 @@ i = 0; do{ zNL = strchr(z,'\n'); if( zNL==0 ) zNL = z+n; nn = (uint32_t)(zNL - z); - if( nn>FSL__LINE_LENGTH_MASK ){ + if( nn>FSL_LINE_LENGTH_MASK ){ fsl_free(a); *pOut = 0; *pnLine = 0; return FSL_RC_DIFF_BINARY; } @@ -26054,11 +25576,11 @@ m = 0; memcpy(&m, z+x, k-k2); h ^= m; } a[i].indent = s; - a[i].h = h = ((h%281474976710597LL)<h!=pB->h ) return 1; - return memcmp(pA->z,pB->z, pA->h&FSL__LINE_LENGTH_MASK); + return memcmp(pA->z,pB->z, pA->h&FSL_LINE_LENGTH_MASK); } int fsl_dline_cmp_ignore_ws(const fsl_dline * const pA, const fsl_dline * const pB){ unsigned short a = pA->indent, b = pB->indent; @@ -26708,11 +26230,11 @@ /* ** Format a diff using a fsl_diff_builder object */ static int fdb__format( - fsl__diff_cx * const cx, + fsl_diff_cx * const cx, fsl_diff_builder * const pBuilder ){ const fsl_dline *A; /* Left side of the diff */ const fsl_dline *B; /* Right side of the diff */ fsl_diff_opt * const pOpt = pBuilder->opt; @@ -27000,11 +26522,11 @@ fsl_buffer const *pB, fsl_diff_builder * const pBuilder, fsl_diff_opt const * const opt_, int ** outRaw){ int rc = 0; - fsl__diff_cx c = fsl__diff_cx_empty; + fsl_diff_cx c = fsl_diff_cx_empty; bool ignoreWs = false; int ansiOptCount = 0; fsl_diff_opt opt = *opt_ /*we need a copy for the sake of the FSL_DIFF2_INVERT flag*/; if(!pA || !pB || (pBuilder && outRaw)) return FSL_RC_MISUSE; @@ -29466,11 +28988,11 @@ if(outLen) *outLen = buf->used; if(pOut) *pOut = (char const *)buf->mem; return 0; } -char * fsl__file_without_drive_letter(char * zIn){ +char * fsl_file_without_drive_letter(char * zIn){ #ifdef _WIN32 if( zIn && fsl_isalpha(zIn[0]) && zIn[1]==':' ) zIn += 2; #endif return zIn; } @@ -30135,11 +29657,11 @@ "SELECT 1 FROM event " "WHERE objid=%" FSL_ID_T_PFMT " AND type='ci'", rid); } -int fsl__repo_leafcheck(fsl_cx * const f, fsl_id_t rid){ +int fsl_repo_leaf_check(fsl_cx * f, fsl_id_t rid){ fsl_db * const db = f ? fsl_cx_db_repo(f) : NULL; if(!db || !db->dbh) return FSL_RC_MISUSE; else if(rid<=0) return FSL_RC_RANGE; else { int rc = 0; @@ -30166,11 +29688,11 @@ } return rc; } } -int fsl__repo_leafeventually_check( fsl_cx * const f, fsl_id_t rid){ +int fsl_repo_leaf_eventually_check( fsl_cx * f, fsl_id_t rid){ fsl_db * db = f ? fsl_cx_db_repo(f) : NULL; if(!f) return FSL_RC_MISUSE; else if(rid<=0) return FSL_RC_RANGE; else if(!db) return FSL_RC_NOT_A_REPO; else { @@ -30192,18 +29714,18 @@ return rc; } } -int fsl__repo_leafdo_pending_checks(fsl_cx * const f){ +int fsl_repo_leaf_do_pending_checks(fsl_cx *f){ fsl_id_t rid; int rc = 0; for(rid=fsl_id_bag_first(&f->cache.leafCheck); !rc && rid; rid=fsl_id_bag_next(&f->cache.leafCheck,rid)){ - rc = fsl__repo_leafcheck(f, rid); + rc = fsl_repo_leaf_check(f, rid); } - fsl_id_bag_reset(&f->cache.leafCheck); + fsl_id_bag_clear(&f->cache.leafCheck); return rc; } int fsl_leaves_compute(fsl_cx * f, fsl_id_t vid, fsl_leaves_compute_e closeMode){ @@ -30236,15 +29758,14 @@ /* Initialize the bags. */ rc = fsl_id_bag_insert(&pending, vid); if(rc) goto cleanup; - /* This query returns all non-branch-merge children of check-in - ** RID (?1). + /* This query returns all non-branch-merge children of check-in :rid. ** ** If a child is a merge of a fork within the same branch, it is - ** returned. Only merge children in different branches are excluded. + ** returned. Only merge children in different branches are excluded. */ rc = fsl_db_prepare(db, &q1, "SELECT cid FROM plink" " WHERE pid=?1" " AND (isprim" @@ -30256,21 +29777,23 @@ /* FIXME? main-branch? */ , FSL_TAGID_BRANCH, FSL_TAGID_BRANCH ); if(rc) goto cleanup; - /* This query returns a single row if check-in RID (?1) is the - ** first check-in of a new branch. */ + /* This query returns a single row if check-in :rid is the first + ** check-in of a new branch. + */ rc = fsl_db_prepare(db, &isBr, "SELECT 1 FROM tagxref" " WHERE rid=?1 AND tagid=%d AND tagtype=2" " AND srcid>0", FSL_TAGID_BRANCH ); if(rc) goto cleanup; - /* This statement inserts check-in RID (?1) into the LEAVES table.*/ + /* This statement inserts check-in :rid into the LEAVES table. + */ rc = fsl_db_prepare(db, &ins, "INSERT OR IGNORE INTO leaves VALUES(?1)"); if(rc) goto cleanup; while( fsl_id_bag_count(&pending) ){ @@ -30623,11 +30146,11 @@ #undef US4B #undef US4C #undef US0A int fsl_looks_like_utf8(fsl_buffer const * const b, int stopFlags){ - fsl_size_t n = 0; + fsl_size_t n; const char *z = fsl_buffer_cstr2(b, &n); int j, c, flags = FSL_LOOKSLIKE_NONE; /* Assume UTF-8 text, prove otherwise */ if( n==0 ) return flags; /* Empty file -> text */ c = *z; @@ -30651,11 +30174,11 @@ if( c2=='\r' ){ flags |= (FSL_LOOKSLIKE_CR | FSL_LOOKSLIKE_CRLF); /* Found LF preceded by CR */ }else{ flags |= FSL_LOOKSLIKE_LONE_LF; } - if( j>FSL__LINE_LENGTH_MASK ){ + if( j>FSL_LINE_LENGTH_MASK ){ flags |= FSL_LOOKSLIKE_LONG; /* Very long line -> binary */ } j = 0; }else if( c=='\r' ){ flags |= FSL_LOOKSLIKE_CR; @@ -30665,11 +30188,11 @@ } } if( n ){ flags |= FSL_LOOKSLIKE_SHORT; /* The whole blob was not examined */ } - if( j>FSL__LINE_LENGTH_MASK ){ + if( j>FSL_LINE_LENGTH_MASK ){ flags |= FSL_LOOKSLIKE_LONG; /* Very long line -> binary */ } return flags; } @@ -31453,12 +30976,12 @@ ** is the number of lines of text to copy directly from the pivot, ** the second integer is the number of lines of text to omit from the ** pivot, and the third integer is the number of lines of text that are ** inserted. The edit array ends with a triple of 0,0,0. */ - rc = fsl__diff_text_raw(pPivot, pV1, 0, &aC1); - if(!rc) rc = fsl__diff_text_raw(pPivot, pV2, 0, &aC2); + rc = fsl_diff_text_raw(pPivot, pV1, 0, &aC1); + if(!rc) rc = fsl_diff_text_raw(pPivot, pV2, 0, &aC2); RC; assert(aC1 && aC2); /* Rewind inputs: Needed to reconstruct output */ fsl_buffer_rewind(pV1); @@ -31884,36 +31407,36 @@ This file houses the priority queue class. */ #include -void fsl__pq_clear(fsl__pq *p){ +void fsl_pq_clear(fsl_pq *p){ fsl_free(p->list); - *p = fsl__pq_empty; + *p = fsl_pq_empty; } /* Change the size of the queue so that it contains N slots */ -static int fsl__pq_resize(fsl__pq *p, fsl_size_t N){ - void * re = fsl_realloc(p->list, sizeof(fsl__pq_entry)*N); +static int fsl_pq_resize(fsl_pq *p, fsl_size_t N){ + void * re = fsl_realloc(p->list, sizeof(fsl_pq_entry)*N); if(!re) return FSL_RC_OOM; else{ - p->list = (fsl__pq_entry*)re; + p->list = (fsl_pq_entry*)re; p->capacity = N; return 0; } } /** Insert element e into the queue. */ -int fsl__pq_insert(fsl__pq *p, fsl_id_t e, +int fsl_pq_insert(fsl_pq *p, fsl_id_t e, double v, void *pData){ fsl_size_t i, j; if( p->used+1>p->capacity ){ - int const rc = fsl__pq_resize(p, p->used+5); + int const rc = fsl_pq_resize(p, p->used+5); if(rc) return rc; } for(i=0; iused; ++i){ if( p->list[i].priority>v ){ for(j=p->used; j>i; --j){ @@ -31927,11 +31450,11 @@ p->list[i].priority = v; ++p->used; return 0; } -fsl_id_t fsl__pq_extract(fsl__pq *p, void **pp){ +fsl_id_t fsl_pq_extract(fsl_pq *p, void **pp){ fsl_id_t e, i; if( p->used==0 ){ if( pp ) *pp = 0; return 0; } @@ -32012,11 +31535,11 @@ int fsl_branch_of_rid(fsl_cx *f, fsl_int_t rid, char **zOut ) So that we can distinguish "not found" from OOM errors. */ -static char * fsl_branch_of_rid(fsl_cx * const f, fsl_int_t rid){ +static char * fsl_branch_of_rid(fsl_cx *f, fsl_int_t rid){ char *zBr = 0; fsl_db * const db = fsl_cx_db_repo(f); fsl_stmt * st = 0; int rc; assert(db); @@ -32034,11 +31557,10 @@ } end: fsl_stmt_cached_yield(st); if( !rc && zBr==0 ){ zBr = fsl_config_get_text(f, FSL_CONFDB_REPO, "main-branch", 0); - if(!zBr) zBr = fsl_strdup("trunk"); } return zBr; } /** @@ -32162,14 +31684,11 @@ goto oom; } } return ans; oom: - if(!f->error.code){ - fsl_cx_err_set(f, FSL_RC_OOM, NULL); - }/* Else assume the OOM is really a misleading - side-effect of another failure. */ + fsl_cx_err_set(f, FSL_RC_OOM, NULL); return -1; } int fsl_sym_to_rid( fsl_cx * const f, char const * sym, fsl_satype_e type, fsl_id_t * rv ){ @@ -32388,23 +31907,23 @@ assert(0==rc); *rv = rid; return rc; } -fsl_id_t fsl__uuid_to_rid2( fsl_cx * const f, fsl_uuid_cstr uuid, - fsl__phantom_e mode ){ +fsl_id_t fsl_uuid_to_rid2( fsl_cx * f, fsl_uuid_cstr uuid, + fsl_phantom_e mode ){ if(!f) return -1; else if(!fsl_is_uuid(uuid)){ fsl_cx_err_set(f, FSL_RC_MISUSE, - "fsl__uuid_to_rid2() requires a " + "fsl_uuid_to_rid2() requires a " "full UUID. Got: %s", uuid); return -2; }else{ fsl_id_t rv; rv = fsl_uuid_to_rid(f, uuid); if((0==rv) && (FSL_PHANTOM_NONE!=mode) - && 0!=fsl__content_new(f, uuid, + && 0!=fsl_content_new(f, uuid, (FSL_PHANTOM_PRIVATE==mode), &rv)){ assert(f->error.code); rv = -3; } @@ -32536,15 +32055,15 @@ } } fsl_id_t fsl_repo_filename_fnid( fsl_cx * f, char const * fn ){ fsl_id_t rv = 0; - int const rc = fsl__repo_filename_fnid2(f, fn, &rv, false); + int const rc = fsl_repo_filename_fnid2(f, fn, &rv, false); return rv>=0 ? rv : (rc>0 ? -rc : rc); } -int fsl__repo_filename_fnid2( fsl_cx * f, char const * fn, fsl_id_t * rv, bool createNew ){ +int fsl_repo_filename_fnid2( fsl_cx * f, char const * fn, fsl_id_t * rv, bool createNew ){ fsl_db * db = fsl_cx_db_repo(f); fsl_id_t fnid = 0; fsl_stmt * qSel = NULL; int rc; assert(f); @@ -32633,11 +32152,11 @@ } } -int fsl__repo_verify_before_commit( fsl_cx * const f, fsl_id_t rid ){ +int fsl_repo_verify_before_commit( fsl_cx * f, fsl_id_t rid ){ if(0){ /* v1 adds a commit hook here on the first entry, but it only seems to ever use one commit hook, so the infrastructure seems like overkill here. Thus this final verification is called from @@ -32650,17 +32169,17 @@ return rid>0 ? fsl_id_bag_insert(&f->cache.toVerify, rid) : FSL_RC_RANGE; } -void fsl_repo_verify_cancel( fsl_cx * const f ){ +void fsl_repo_verify_cancel( fsl_cx * f ){ fsl_id_bag_clear(&f->cache.toVerify); } int fsl_rid_to_uuid2(fsl_cx * const f, fsl_id_t rid, fsl_buffer *uuid){ fsl_db * db = f ? fsl_cx_db_repo(f) : NULL; - if(!db || (rid<=0)){ + if(!f || !db || (rid<=0)){ return fsl_cx_err_set(f, FSL_RC_MISUSE, "fsl_rid_to_uuid2() requires " "an opened repository and a " "positive RID value. rid=%" FSL_ID_T_PFMT, rid); @@ -32812,11 +32331,11 @@ fsl_buffer_clear(&content); return rc; } -int fsl__repo_verify_at_commit( fsl_cx * const f ){ +int fsl_repo_verify_at_commit( fsl_cx * f ){ fsl_id_t rid; int rc = 0; fsl_id_bag * bag = &f->cache.toVerify; /* v1 does content_cache_clear() here. */ f->cache.inFinalVerify = 1; @@ -32831,11 +32350,11 @@ } fsl_id_bag_clear(bag); f->cache.inFinalVerify = 0; if(rc && !f->error.code){ fsl_cx_err_set(f, rc, - "Error #%d (%s) in fsl__repo_verify_at_commit()", + "Error #%d (%s) in fsl_repo_verify_at_commit()", rc, fsl_rc_cstr(rc)); } return rc; } @@ -32932,11 +32451,11 @@ if(rc){ goto end2; } db = fsl_cx_db(f); if(!f->repo.user){ - f->repo.user = fsl_user_name_guess() + f->repo.user = fsl_guess_user_name() /* Ignore OOM error here - we'll use 'root' by default (but if we're really OOM here then the next op will fail). */; } @@ -33056,11 +32575,11 @@ and installing the initial schemas outside a transaction is horribly slow. */ if( opt->configRepo && *opt->configRepo ){ bool inTrans2 = false; - char * inopConfig = fsl__config_inop_rhs(FSL_CONFIGSET_ALL); + char * inopConfig = fsl_config_inop_rhs(FSL_CONFIGSET_ALL); char * inopDb = inopConfig ? fsl_db_setting_inop_rhs() : NULL; if(!inopConfig || !inopDb){ fsl_free(inopConfig); rc = FSL_RC_OOM; goto end2; @@ -33323,22 +32842,22 @@ assert(f->dbMain); return sqlite3_db_readonly(f->dbMain->dbh, zRole) ? 1 : 0; } } -int fsl__repo_record_filename(fsl_cx * const f){ +int fsl_repo_record_filename(fsl_cx * f){ + fsl_buffer full = fsl_buffer_empty; fsl_db * dbR = fsl_needs_repo(f); fsl_db * dbC; fsl_db * dbConf; char const * zCDir; char const * zName = dbR ? dbR->filename : NULL; int rc; if(!dbR) return FSL_RC_NOT_A_REPO; - fsl_buffer * const full = fsl__cx_scratchpad(f); assert(zName); assert(f); - rc = fsl_file_canonical_name(zName, full, 0); + rc = fsl_file_canonical_name(zName, &full, 0); if(rc){ fsl_cx_err_set(f, rc, "Error %s canonicalizing filename: %s", zName); goto end; } @@ -33351,11 +32870,11 @@ ? FSL_DBROLE_MAIN : FSL_DBROLE_CONFIG; rc = fsl_db_exec(dbConf, "INSERT OR IGNORE INTO %s.global_config(name,value) " "VALUES('repo:%q',1)", fsl_db_role_label(dbRole), - fsl_buffer_cstr(full)); + fsl_buffer_cstr(&full)); if(rc) goto end; } dbC = fsl_cx_db_ckout(f); if(dbC && (zCDir=f->ckout.dir)){ @@ -33399,11 +32918,11 @@ end: if(rc && !f->error.code && f->dbMain->error.code){ fsl_cx_uplift_db_error(f, f->dbMain); } - fsl__cx_scratchpad_yield(f, full); + fsl_buffer_clear(&full); return rc; } char fsl_rid_is_a_checkin(fsl_cx * f, fsl_id_t rid){ @@ -33506,11 +33025,11 @@ break; } } }/* for-each-F-card loop */ end: - fsl__cx_content_buffer_yield(f); + fsl_cx_content_buffer_yield(f); fsl_deck_finalize(&mf); return rc; } } @@ -33526,11 +33045,11 @@ if(rc){ rc = fsl_cx_err_set(f, rc, "Error filling buffer from input source."); }else{ fsl_id_t theRid = 0; - rc = fsl__content_put_ex( f, &buf, NULL, 0, 0, 0, &theRid); + rc = fsl_content_put_ex( f, &buf, NULL, 0, 0, 0, &theRid); if(!rc){ if(rid) *rid = theRid; if(uuid){ *uuid = fsl_rid_to_uuid(f, theRid); if(!uuid) rc = FSL_RC_OOM; @@ -33565,11 +33084,11 @@ int rc; fsl_buffer hash_ = fsl_buffer_empty; fsl_buffer * hash; fsl_id_t rid = 0; if(!fsl_cx_db_repo(f)) return FSL_RC_NOT_A_REPO; - hash = hashOut ? &hash_ : fsl__cx_scratchpad(f); + hash = hashOut ? &hash_ : fsl_cx_scratchpad(f); /* First check the auxiliary hash to see if there is already an artifact that uses the auxiliary hash name */ rc = fsl_cx_hash_buffer(f, true, src, hash); if(FSL_RC_UNSUPPORTED==rc){ // The auxiliary hash option is incompatible with our hash policy. @@ -33603,21 +33122,20 @@ } if(hash == &hash_){ fsl_buffer_clear(hash); }else{ assert(!hash_.mem); - fsl__cx_scratchpad_yield(f, hash); + fsl_cx_scratchpad_yield(f, hash); } return rc; } -int fsl__repo_fingerprint_search( fsl_cx * const f, fsl_id_t rcvid, - char ** zOut ){ +int fsl_repo_fingerprint_search( fsl_cx *f, fsl_id_t rcvid, char ** zOut ){ int rc = 0; fsl_db * const db = fsl_needs_repo(f); if(!db) return FSL_RC_NOT_A_REPO; - fsl_buffer * const sql = fsl__cx_scratchpad(f); + fsl_buffer * const sql = fsl_cx_scratchpad(f); fsl_stmt q = fsl_stmt_empty; int version = 1 /* Fingerprint version to check: 0 or 1 */; try_again: /* * We check both v1 and v0 fingerprints, in that order. From Fossil @@ -33677,11 +33195,11 @@ default: rc = fsl_cx_uplift_db_error2(f, db, rc); break; } end: - fsl__cx_scratchpad_yield(f, sql); + fsl_cx_scratchpad_yield(f, sql); fsl_stmt_finalize(&q); return rc; } int fsl_repo_manifest_write(fsl_cx *f, @@ -33715,11 +33233,11 @@ rc = fsl_content_get(f, manifestRid, pManifest); if(rc) goto end; } if(pHash){ if(f->ckout.rid!=manifestRid){ - bHash = fsl__cx_scratchpad(f); + bHash = fsl_cx_scratchpad(f); rc = fsl_rid_to_uuid2(f, manifestRid, bHash); if(rc) goto end; ridHash = (char *)bHash->mem; }else{ ridHash = f->ckout.uuid; @@ -33757,561 +33275,33 @@ } fsl_stmt_finalize(&q); } end: if(bHash){ - fsl__cx_scratchpad_yield(f, bHash); - } - return rc; -} - -/** - Internal state for the rebuild process. -*/ -struct FslRebuildState { - fsl_cx * f; - fsl_db * db; - fsl_rebuild_opt const * opt; - fsl_stmt qDeltas; - fsl_stmt qSize; - fsl_stmt qChild; - fsl_id_bag idsDone; - fsl_rebuild_step step; -}; -typedef struct FslRebuildState FslRebuildState; -const FslRebuildState FslRebuildState_empty = { -NULL, NULL, NULL, -fsl_stmt_empty_m, fsl_stmt_empty_m, fsl_stmt_empty_m, -fsl_id_bag_empty_m/*idsDone*/, -fsl_rebuild_step_empty_m -}; - -static int fsl__rebuild_update_schema(FslRebuildState * const frs){ - int rc = 0; - char * zBlobSchema = NULL; - - /* Verify that the PLINK table has a new column added by the - ** 2014-11-28 schema change. Create it if necessary. This code - ** can be removed in the future, once all users have upgraded to the - ** 2014-11-28 or later schema. - */ - if(!fsl_db_table_has_column(frs->db, "plink", "baseid")){ - rc = fsl_cx_exec(frs->f, - "ALTER TABLE repository.plink ADD COLUMN baseid"); - if(rc) goto end; - - } - - /* Verify that the MLINK table has the newer columns added by the - ** 2015-01-24 schema change. Create them if necessary. This code - ** can be removed in the future, once all users have upgraded to the - ** 2015-01-24 or later schema. - */ - if( !fsl_db_table_has_column(frs->db,"mlink","isaux") ){ - rc = fsl_cx_exec_multi(frs->f, - "ALTER TABLE repo.mlink ADD COLUMN pmid INTEGER DEFAULT 0;" - "ALTER TABLE repo.mlink ADD COLUMN isaux BOOLEAN DEFAULT 0;" - ); - if(rc) goto end; - } - - /* We're going to skip several older (2011) schema updates for the - time being on the grounds of YAGNI. */ - - /** - Update the repository schema for Fossil version 2.0. (2017-02-28) - (1) Change the CHECK constraint on BLOB.UUID so that the length - is greater than or equal to 40, not exactly equal to 40. - */ - zBlobSchema = - fsl_db_g_text(frs->db, NULL, "SELECT sql FROM %!Q.sqlite_schema" - " WHERE name='blob'", fsl_db_role_label(FSL_DBROLE_REPO)); - if(!zBlobSchema){ - /* ^^^^ reminder: fossil(1) simply ignores this case, silently - doing nothing instead. */ - rc = fsl_cx_uplift_db_error(frs->f, frs->db); - if(!rc){ - rc = fsl_cx_err_set(frs->f, FSL_RC_DB, - "Unknown error fetching blob table schema."); - } - goto end; - } - /* Search for: length(uuid)==40 - ** 0123456789 12345 */ - for(int i=10; zBlobSchema[i]; i++){ - if( zBlobSchema[i]=='=' - && fsl_strncmp(&zBlobSchema[i-6],"(uuid)==40",10)==0 ){ - int rc2 = 0; - zBlobSchema[i] = '>'; - sqlite3_db_config(frs->db->dbh, SQLITE_DBCONFIG_DEFENSIVE, 0, &rc2); - rc = fsl_cx_exec_multi(frs->f, - "PRAGMA writable_schema=ON;" - "UPDATE %!Q.sqlite_schema SET sql=%Q WHERE name LIKE 'blob';" - "PRAGMA writable_schema=OFF;", - fsl_db_role_label(FSL_DBROLE_REPO), zBlobSchema - ); - sqlite3_db_config(frs->db->dbh, SQLITE_DBCONFIG_DEFENSIVE, 1, &rc2); - break; - } - } - if(rc) goto end; - rc = fsl_cx_exec(frs->f, - "CREATE VIEW IF NOT EXISTS " - " %!Q.artifact(rid,rcvid,size,atype,srcid,hash,content) AS " - " SELECT blob.rid,rcvid,size,1,srcid,uuid,content" - " FROM blob LEFT JOIN delta ON (blob.rid=delta.rid);", - fsl_db_role_label(FSL_DBROLE_REPO) - ); - - end: - fsl_free(zBlobSchema); - return rc; -} - -#define INTCHECK frs->f->interrupted ? frs->f->interrupted : -/** - Inserts rid into frs->idsDone and calls frs->opt->callback. Returns - 0 on success. -*/ -static int fsl__rebuild_step_done(FslRebuildState * const frs, fsl_id_t rid){ - assert( !fsl_id_bag_contains(&frs->idsDone, rid) ); - int rc = fsl_id_bag_insert(&frs->idsDone, rid); - if(0==rc && frs->opt->callback){ - ++frs->step.stepNumber; - frs->step.rid = rid; - rc = frs->opt->callback(&frs->step); - } - return rc ? rc : (INTCHECK 0); -} - -/** - Rebuilds cross-referencing state for the given RID and its content, - recursively on all of its descendents. The contents of the input - buffer are taken over by this routine. - - If other artifacts are deltas based off of the given artifact, they - are processed as well. - - Returns 0 on success. -*/ -static int fsl__rebuild_step(FslRebuildState * const frs, fsl_id_t rid, - int64_t blobSize, fsl_buffer * const content){ - fsl_deck deck = fsl_deck_empty; - fsl_buffer deckContent = fsl_buffer_empty; - fsl_id_bag idsChildren = fsl_id_bag_empty; - int rc = frs->f->interrupted; - if(rc) goto end; - assert(rid>0); - if(!frs->qSize.stmt){ - rc = fsl_cx_prepare(frs->f, &frs->qSize, - "UPDATE blob SET size=?1 WHERE rid=?2/*%s()*/", - __func__); - if(rc) goto end; - }else{ - fsl_stmt_reset(&frs->qSize); - } - if(!frs->qDeltas.stmt){ - rc = fsl_cx_prepare(frs->f, &frs->qDeltas, - "SELECT rid FROM delta WHERE srcid=?1/*%s()*/", - __func__); - }else{ - fsl_stmt_reset(&frs->qDeltas); - } - while(0==rc && rid>0){ - //MARKER(("TODO: %s(rid=%d)\n", __func__, (int)rid)); - if(blobSize != (int64_t)content->used){ - /* Fix [blob.size] field if needed. (Why would this ever - be needed?) */ - rc = fsl_stmt_bind_step(&frs->qSize, "IR", (int64_t)content->used, rid); - if(rc){ - fsl_cx_uplift_db_error(frs->f, frs->qSize.db); - break; - } - blobSize = (int64_t)content->used; - } - /* Find all deltas based off of rid... */ - rc = fsl_stmt_bind_fmt(&frs->qDeltas, "R", rid); - if(rc){ - fsl_cx_uplift_db_error(frs->f, frs->qDeltas.db); - break; - } - fsl_id_bag_reset(&idsChildren); - while(0==rc && FSL_RC_STEP_ROW==fsl_stmt_step(&frs->qDeltas)){ - fsl_id_t const cid = fsl_stmt_g_id(&frs->qDeltas, 0); - if(!fsl_id_bag_contains(&frs->idsDone, cid)){ - rc = fsl_id_bag_insert(&idsChildren, cid); - } - } - fsl_stmt_reset(&frs->qDeltas); - rc = INTCHECK rc; - if(rc) break; - - fsl_size_t const nChild = fsl_id_bag_count(&idsChildren); - if(fsl_id_bag_contains(&frs->idsDone, rid)){ - /* Kludge! This check should not be necessary. Testing with the - libfossil repo, this check is not required on the main x86 - dev machine but is on a Raspberry Pi 4, for reasons - as-yet-unknown. On the latter, artifact - 1ee529429e2aa6ffbeffb0cf73bb51a34a8547b8 (an opaque file, not - a fossil artifact) makes it into frs->idsDone - unexpectedly(?), triggering an assert() in - fsl__rebuild_step_done(). */ - goto doChildren; - } - if(nChild){ - /* Parsing the deck will mutate the buffer, so we need a copy of - the input content to apply the next delta to. */ - rc = fsl_buffer_copy(&deckContent, content); - if(rc) break; - }else{ - /* We won't be applying any deltas, so use the deck content the - user passed in. */ - fsl_buffer_swap(content, &deckContent); - fsl_buffer_clear(content); - } - frs->step.blobSize = blobSize; - /* At this point fossil(1) decides between rebuild and - deconstruct, performing different work for each. We're skipping - the deconstruct option for now but may want to add it - later. See fossil's rebuild.c:rebuild_step(). Note that - deconstruct is not a capability intended for normal client - use. It's primarily for testing of fossil itself. */ - fsl_deck_init(frs->f, &deck, FSL_SATYPE_ANY); - //MARKER(("rid=%d\n", (int)rid)); - rc = INTCHECK fsl_deck_parse2(&deck, &deckContent, rid) - /* But isn't it okay if rid is not an artifact? */; - switch(rc){ - case FSL_RC_SYNTAX: - /* Assume deck is not an artifact. Fall through and continue - processing the delta children. */ - fsl_cx_err_reset(frs->f); - rc = 0; - frs->step.artifactType = FSL_SATYPE_INVALID; - break; - case 0: - frs->step.artifactType = deck.type; - rc = INTCHECK fsl__deck_crosslink(&deck); - break; - default: -#if 0 - MARKER(("err=%s for rid=%d content=\n%.*s\n", fsl_rc_cstr(rc), (int)rid, - (int)deckContent.used, (char const *)deckContent.mem)); -#endif - break; - } - fsl_buffer_clear(&deckContent); - fsl_deck_finalize(&deck); - rc = INTCHECK 0; - if(0==rc && 0==(rc = frs->f->interrupted)){ - rc = fsl__rebuild_step_done( frs, rid ); - } - if(rc) break; - /* Process all dependent deltas recursively... */ - doChildren: - rid = 0; - fsl_size_t i = 1; - for(fsl_id_t cid = fsl_id_bag_first(&idsChildren); - 0==rc && cid!=0; cid = fsl_id_bag_next(&idsChildren, cid), ++i){ - int64_t sz; - if(!frs->qChild.stmt){ - rc = fsl_cx_prepare(frs->f, &frs->qChild, - "SELECT content, size " - "FROM blob WHERE rid=?1/*%s()*/", - __func__); - if(rc) break; - }else{ - fsl_stmt_reset(&frs->qChild); - } - fsl_stmt_bind_id(&frs->qChild, 1, cid); - if( FSL_RC_STEP_ROW==fsl_stmt_step(&frs->qChild) && - (sz = fsl_stmt_g_int64(&frs->qChild, 1))>=0 ){ - fsl_buffer next = fsl_buffer_empty; - fsl_buffer delta = fsl_buffer_empty; - void const * blob = 0; - fsl_size_t deltaBlobSize = 0; - rc = INTCHECK fsl_stmt_get_blob(&frs->qChild, 0, &blob, &deltaBlobSize) - /* Library-level TODO: a heuristic/convention in fsl_buffer - APIs which says that if buf.mem is not NULL and buf.capacity - is 0 then the memory is owned elsewhere and must be copied - if/when it would be modified by the buffer API. That would allow - us to emulate some of the buffer-copy optimization which fossil does, - e.g. in this very spot. - */; - if(rc) goto outro; - rc = fsl_buffer_append(&delta, blob, (fsl_int_t)deltaBlobSize); - fsl_stmt_reset(&frs->qChild); - rc = INTCHECK fsl_buffer_uncompress(&delta, &delta); - if(rc) goto outro; - rc = INTCHECK fsl_buffer_delta_apply(content, &delta, &next); - fsl_buffer_clear(&delta); - if(rc){ - if(FSL_RC_OOM!=rc){ - rc = fsl_cx_err_set(frs->f, rc, - "Error applying delta #%" FSL_ID_T_PFMT - " to parent #%" FSL_ID_T_PFMT, cid, rid); - } - goto outro; - } - if(iqChild); - fsl_buffer_clear(&delta); - fsl_buffer_clear(&next); - break; - }else{ - fsl_stmt_reset(&frs->qChild); - fsl_buffer_clear(content); - } - } - } - end: - fsl_deck_finalize(&deck); - fsl_buffer_clear(content); - fsl_buffer_clear(&deckContent); - fsl_id_bag_clear(&idsChildren); - return rc ? rc : (INTCHECK 0); -} -#undef INTCHECK -/** - Check to see if the "sym-trunk" tag exists. If not, create it and - attach it to the very first check-in. Returns 0 on success. -*/ -static int fsl__rebuild_tag_trunk(FslRebuildState * const frs){ - fsl_id_t const tagid = - fsl_db_g_id(frs->db, 0, - "SELECT 1 FROM tag WHERE tagname='sym-trunk'"); - if(tagid>0) return 0; - fsl_id_t const rid = - fsl_db_g_id(frs->db, 0, - "SELECT pid FROM plink AS x WHERE NOT EXISTS" - "(SELECT 1 FROM plink WHERE cid=x.pid)"); - if(rid==0) return 0; - - /* Add the trunk tag to the root of the whole tree */ - int rc = 0; - fsl_buffer * const b = fsl__cx_scratchpad(frs->f); - rc = fsl_rid_to_uuid2(frs->f, rid, b); - switch(rc){ - case FSL_RC_NOT_FOUND: - rc = 0/*fossil ignores this case without an error*/; - break; - case 0: { - fsl_deck d = fsl_deck_empty; - char const * zUuid = fsl_buffer_cstr(b); - fsl_deck_init(frs->f, &d, FSL_SATYPE_CONTROL); - rc = fsl_deck_T_add(&d, FSL_TAGTYPE_PROPAGATING, - zUuid, "sym-trunk", NULL); - if(0==rc) rc = fsl_deck_T_add(&d, FSL_TAGTYPE_PROPAGATING, - zUuid, "branch", "trunk"); - if(0==rc){ - char const * userName = fsl_cx_user_guess(frs->f); - if(!userName){ - rc = fsl_cx_err_set(frs->f, FSL_RC_NOT_FOUND, - "Cannot determine user name for " - "control artifact."); - }else{ - rc = fsl_deck_U_set(&d, userName); - } - } - if(0==rc){ - rc = fsl_deck_save(&d, fsl_content_is_private(frs->f, rid)); - } - fsl_deck_finalize(&d); - break; - } - default: break; - } - fsl__cx_scratchpad_yield(frs->f, b); - return rc; -} - - - -#define FSL__REBUILD_SKIP_TICKETS 1 /* remove this once crosslinking does - something useful with tickets. */ - -static int fsl__rebuild(fsl_cx * const f, fsl_rebuild_opt const * const opt){ - fsl_stmt s = fsl_stmt_empty; - fsl_stmt q = fsl_stmt_empty; - fsl_db * const db = fsl_cx_db_repo(f); - int rc; - FslRebuildState frs = FslRebuildState_empty; - fsl_buffer sql = fsl_buffer_empty; - assert(db); - frs.f = frs.step.f = f; - frs.db = db; - frs.opt = frs.step.opt = opt; - rc = fsl__rebuild_update_schema(&frs); - if(!rc) rc = fsl_buffer_reserve(&sql, 1024 * 4); - if(rc) goto end; - - fsl__cx_clear_mf_seen(f, false); - rc = fsl_cx_prepare(f, &q, - "SELECT name FROM %!Q.sqlite_schema /*scan*/" - " WHERE type='table'" - " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias'," - "'config','shun','private','reportfmt'," - "'concealed','accesslog','modreq'," - "'purgeevent','purgeitem','unversioned'," -#if FSL__REBUILD_SKIP_TICKETS - "'ticket','ticketchng'," -#endif - "'subscriber','pending_alert','chat'" - ")" - " AND name NOT GLOB 'sqlite_*'" - " AND name NOT GLOB 'fx_*'", - fsl_db_role_label(FSL_DBROLE_REPO) - ); - while( 0==rc && FSL_RC_STEP_ROW==fsl_stmt_step(&q) ){ - rc = fsl_buffer_appendf(&sql, "DROP TABLE IF EXISTS %!Q;\n", - fsl_stmt_g_text(&q, 0, NULL)); - } - fsl_stmt_finalize(&q); - if(0==rc && fsl_buffer_size(&sql)){ - rc = fsl_cx_exec_multi(f, "%b", &sql); - } - if(rc) goto end; - - rc = fsl_cx_exec_multi(f, "%s", fsl_schema_repo2()); -#if !FSL__REBUILD_SKIP_TICKETS - if(0==rc) rc = fsl__cx_ticket_create_table(f); -#endif - if(0==rc) rc = fsl__shunned_remove(f); - if(0==rc){ - rc = fsl_cx_exec_multi(f, - "INSERT INTO unclustered" - " SELECT rid FROM blob EXCEPT SELECT rid FROM private;" - "DELETE FROM unclustered" - " WHERE rid IN (SELECT rid FROM shun JOIN blob USING(uuid));" - "DELETE FROM config WHERE name IN ('remote-code', 'remote-maxid');" - "UPDATE user SET mtime=now() WHERE mtime IS NULL;" - ); - } - if(rc) goto end; - - /* The following should be count(*) instead of max(rid). max(rid) is - ** an adequate approximation, however, and is much faster for large - ** repositories. */ - if(1){ - frs.step.artifactCount = - (uint32_t)fsl_db_g_id(db, 0, "SELECT count(*) FROM blob"); - }else{ - frs.step.artifactCount = - (uint32_t)fsl_db_g_id(db, 0, "SELECT max(rid) FROM blob"); - } - - //totalSize += incrSize*2; - rc = fsl_cx_prepare(f, &s, - "SELECT rid, size FROM blob /*scan*/" - " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" - " AND NOT EXISTS(SELECT 1 FROM delta WHERE rid=blob.rid)" - ); - if(rc) goto end; - rc = fsl__crosslink_begin(f) - /* Maintenace reminder: if this call succeeds, BE SURE that - we do not skip past the fsl__crosslink_end() call via - (goto end). Doing so would get the transaction stack out - of sync. */; - if(rc) goto end /*to skip fsl__crosslink_end() call!*/; - while( 0==rc && FSL_RC_STEP_ROW==fsl_stmt_step(&s) ){ - fsl_id_t const rid = fsl_stmt_g_id(&s, 0); - int64_t const size = fsl_stmt_g_int64(&s, 1); - if( size>=0 ){ - fsl_buffer content = fsl_buffer_empty; - rc = fsl_content_get(f, rid, &content); - if(0==rc){ - rc = fsl__rebuild_step(&frs, rid, size, &content); - assert(!content.mem); - } - fsl_buffer_clear(&content); - } - } - fsl_stmt_finalize(&s); - if(rc) goto crosslink_end; - rc = fsl_cx_prepare(f, &s, - "SELECT rid, size FROM blob" - " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" - ); - while( 0==rc && FSL_RC_STEP_ROW==fsl_stmt_step(&s) ){ - fsl_id_t const rid = fsl_stmt_g_id(&s, 0); - int64_t const size = fsl_stmt_g_int64(&s, 1); - if( size>=0 ){ - if( !fsl_id_bag_contains(&frs.idsDone, rid) ){ - fsl_buffer content = fsl_buffer_empty; - rc = fsl_content_get(f, rid, &content); - if(0==rc){ - rc = fsl__rebuild_step(&frs, rid, size, &content); - assert(!content.mem); - } - fsl_buffer_clear(&content); - } - }else{ - rc = fsl_cx_exec_multi(f, "INSERT OR IGNORE INTO phantom " - "VALUES(%" FSL_ID_T_PFMT ")", rid); - if(0==rc){ - frs.step.blobSize = -1; - frs.step.artifactType = FSL_SATYPE_INVALID; - rc = fsl__rebuild_step_done(&frs, rid); - } - } - } - fsl_stmt_finalize(&s); - crosslink_end: - rc = fsl__crosslink_end(f, rc); - if(rc) goto end; - rc = fsl__rebuild_tag_trunk(&frs); - if(rc) goto end; - //if( opt->clustering ) rc = fsl__create_cluster(f); - rc = fsl_cx_exec_multi(f, - "REPLACE INTO config(name,value,mtime) VALUES('content-schema',%Q,now());" - "REPLACE INTO config(name,value,mtime) VALUES('aux-schema',%Q,now());" - "REPLACE INTO config(name,value,mtime) VALUES('rebuilt',%Q,now());", - FSL_CONTENT_SCHEMA, FSL_AUX_SCHEMA, - "libfossil " FSL_LIB_VERSION_HASH " " FSL_LIB_VERSION_TIMESTAMP - ); - end: - if(0==rc && frs.opt->callback){ - frs.step.stepNumber = 0; - frs.step.rid = 0; - frs.step.blobSize = 0; - rc = frs.opt->callback(&frs.step); - } - fsl_buffer_clear(&sql); - fsl_stmt_finalize(&s); - fsl_stmt_finalize(&frs.qDeltas); - fsl_stmt_finalize(&frs.qSize); - fsl_stmt_finalize(&frs.qChild); - fsl_id_bag_clear(&frs.idsDone); - return rc; -} - -int fsl_repo_rebuild(fsl_cx * const f, fsl_rebuild_opt const * const opt){ - int rc = 0; - fsl_db * const db = fsl_needs_repo(f); - if(!db) return rc; - rc = fsl_cx_transaction_begin(f); - if(0==rc){ - rc = fsl__rebuild(f, opt); - int const rc2 = fsl_cx_transaction_end(f, opt->dryRun || rc!=0); - if(0==rc && 0!=rc2) rc = rc2; - } - fsl_cx_interrupt(f, 0, NULL); - return rc; -} - -#undef FSL__REBUILD_SKIP_TICKETS + fsl_cx_scratchpad_yield(f, bHash); + } + return rc; +} + +/** + NOT YET IMPLEMENTED. (We have the infrastructure, just need to glue + it together.) + + Re-crosslinks all artifacts of the given type (or all artifacts if + the 2nd argument is FSL_SATYPE_ANY). This is an expensive + operation, involving dropping the contents of any corresponding + auxiliary tables, loading and parsing the appropriate artifacts, + and re-creating the auxiliary tables. + + TODO: add a way for callers to get some sort of progress feedback + and abort the process by returning non-0 from that handler. We can + possibly do that via defining an internal-use crosslink listener + which carries more state, e.g. for calculating completion progress. +*/ +//FSL_EXPORT int fsl_repo_relink_artifacts(fsl_cx *f, void * someOptionsType); + + #undef MARKER /* end of file repo.c */ /* start of file schema.c */ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ @@ -34421,11 +33411,11 @@ assert(!"Internal misuse of fsl_satype_letter()"); return 0; } } -int fsl__search_doc_touch(fsl_cx * const f, fsl_satype_e saType, +int fsl_search_doc_touch(fsl_cx * const f, fsl_satype_e saType, fsl_id_t rid, const char * docName){ if(!fsl_search_ndx_exists(f) || fsl_content_is_private(f, rid)) return 0; char zType[2] = {0,0}; zType[0] = fsl_satype_letter(saType); #if 0 @@ -34434,11 +33424,11 @@ return *zType ? 0 : FSL_RC_MISUSE; #else /* Reminder: fossil(1) does some once-per-connection init here which installs UDFs used by the search process. Those will be significant for us if we add the search features to the library. */ - assert(zType[0] && "Misuse of fsl__search_doc_touch()'s 2nd parameter."); + assert(zType[0] && "Misuse of fsl_search_doc_touch()'s 2nd parameter."); fsl_db * const db = fsl_cx_db_repo(f); int rc = fsl_db_exec(db, "DELETE FROM ftsidx WHERE docid IN" " (SELECT rowid FROM ftsdocs WHERE type=%Q AND rid=%"FSL_ID_T_PFMT" AND idxed)", zType, rid ); @@ -37019,19 +36009,19 @@ * Public-domain relatively quick-and-dirty implemenation of * ANSI library routine for System V Unix systems. * * Arnold Robbins * - ***************************************************************************** + ***************************************************************************** * Bug reports, patches, comments, suggestions should be sent to: * (Note: this routine is provided as is, without support for those sites that * do not have strftime in their library) * * Syd Weinstein, Elm Coordinator * elm@DSI.COM dsinc!elm * - ***************************************************************************** + ***************************************************************************** * $Log: strftime.c,v $ * Revision 1.3 1993/10/09 19:38:51 smace * Update to elm 2.4 pl23 release version * * Revision 5.8 1993/08/23 02:46:51 syd @@ -37074,11 +36064,11 @@ * From: many via syd * * Revision 5.1 1993/01/27 18:52:15 syd * Initial checkin of contributed public domain routine. * This routine is provided as is and not covered by Elm Copyright. - ****************************************************************************/ + ****************************************************************************/ /* * strftime.c * * Public-domain relatively quick-and-dirty implementation of @@ -37121,11 +36111,11 @@ * Removed ancient non-ANSI decls. Added some headers to get it to * compile for me. Renamed functions to fit into my project. Replaced * hard tabs with 4 spaces. Added #undefs for all file-private #defines * to safe-ify inclusion from/with other files. * - */ +*/ #include /* #include */ #include /* strchr() and friends */ #include /* snprintf() */ @@ -37183,407 +36173,418 @@ /* minimum --- return minimum of two numbers */ static inline int minimum(int a, int b) { - return (a < b ? a : b); + return (a < b ? a : b); } /* maximum --- return maximum of two numbers */ static inline int maximum(int a, int b) { - return (a > b ? a : b); + return (a > b ? a : b); } /* strftime --- produce formatted time */ fsl_size_t fsl_strftime(char *s, fsl_size_t maxsize, const char *format, const struct tm *timeptr) { - enum {TBufLen = 100U}; - char *endp = s + maxsize; - char *start = s; - char tbuf[TBufLen]; - int i; - static short first = 1; -#ifdef POSIX_SEMANTICS - enum {TZBufLen = 200U}; - static char savetz[TZBufLen] = {0}; - char *tz; -#endif /* POSIX_SEMANTICS */ - - /* various tables, useful in North America */ - static char *days_a[] = { - "Sun", "Mon", "Tue", "Wed", - "Thu", "Fri", "Sat" - }; - static char *days_l[] = { - "Sunday", "Monday", "Tuesday", "Wednesday", - "Thursday", "Friday", "Saturday" - }; - static char *months_a[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; - static char *months_l[] = { - "January", "February", "March", "April", - "May", "June", "July", "August", "September", - "October", "November", "December" - }; - static char *ampm[] = { "AM", "PM" }; - - if (s == NULL || format == NULL || timeptr == NULL || maxsize == 0) - return 0; - - if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize) - return 0; - -#ifndef POSIX_SEMANTICS - if (first) { - tzset(); - first = 0; - } -#else /* POSIX_SEMANTICS */ - tz = getenv("TZ"); - if (first) { - if (tz != NULL) { - fsl_strlcpy(savetz, tz, TZBufLen); - } - tzset(); - first = 0; - } - /* if we have a saved TZ, and it is different, recapture and reset */ - if(tz && (tz[0] != savetz[0] || strcmp(tz, savetz) != 0)) { - i = strlen(tz) + 1; - if (i < TZBufLen) { - fsl_strlcpy(savetz, tz, TZBufLen); - } - tzset(); - } -#endif /* POSIX_SEMANTICS */ - - for (; *format && s < endp - 1; format++) { - tbuf[0] = '\0'; - if (*format != '%') { - *s++ = *format; - continue; - } - again: - switch (*++format) { - case '\0': - *s++ = '%'; - goto out; - - case '%': - *s++ = '%'; - continue; - - case 'a': /* abbreviated weekday name */ - if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6) - fsl_strlcpy(tbuf, "?", TBufLen); - else - fsl_strlcpy(tbuf, days_a[timeptr->tm_wday], TBufLen); - break; - - case 'A': /* full weekday name */ - if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6) - fsl_strlcpy(tbuf, "?", TBufLen); - else - fsl_strlcpy(tbuf, days_l[timeptr->tm_wday], TBufLen); - break; - -#ifdef SYSV_EXT - case 'h': /* abbreviated month name */ -#endif - case 'b': /* abbreviated month name */ - if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11) - fsl_strlcpy(tbuf, "?", TBufLen); - else - fsl_strlcpy(tbuf, months_a[timeptr->tm_mon], TBufLen); - break; - - case 'B': /* full month name */ - if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11) - fsl_strlcpy(tbuf, "?", TBufLen); - else - fsl_strlcpy(tbuf, months_l[timeptr->tm_mon], TBufLen); - break; - - case 'c': /* appropriate date and time representation */ - snprintf(tbuf, TBufLen, "%s %s %2d %02d:%02d:%02d %d", - days_a[range(0, timeptr->tm_wday, 6)], - months_a[range(0, timeptr->tm_mon, 11)], - range(1, timeptr->tm_mday, 31), - range(0, timeptr->tm_hour, 23), - range(0, timeptr->tm_min, 59), - range(0, timeptr->tm_sec, 61), - timeptr->tm_year + 1900); - break; - - case 'd': /* day of the month, 01 - 31 */ - i = range(1, timeptr->tm_mday, 31); - snprintf(tbuf, TBufLen, "%02d", i); - break; - - case 'H': /* hour, 24-hour clock, 00 - 23 */ - i = range(0, timeptr->tm_hour, 23); - snprintf(tbuf, TBufLen, "%02d", i); - break; - - case 'I': /* hour, 12-hour clock, 01 - 12 */ - i = range(0, timeptr->tm_hour, 23); - if (i == 0) - i = 12; - else if (i > 12) - i -= 12; - snprintf(tbuf, TBufLen, "%02d", i); - break; - - case 'j': /* day of the year, 001 - 366 */ - snprintf(tbuf, TBufLen, "%03d", timeptr->tm_yday + 1); - break; - - case 'm': /* month, 01 - 12 */ - i = range(0, timeptr->tm_mon, 11); - snprintf(tbuf, TBufLen, "%02d", i + 1); - break; - - case 'M': /* minute, 00 - 59 */ - i = range(0, timeptr->tm_min, 59); - snprintf(tbuf, TBufLen, "%02d", i); - break; - - case 'p': /* am or pm based on 12-hour clock */ - i = range(0, timeptr->tm_hour, 23); - if (i < 12) - fsl_strlcpy(tbuf, ampm[0], TBufLen); - else - fsl_strlcpy(tbuf, ampm[1], TBufLen); - break; - - case 'S': /* second, 00 - 61 */ - i = range(0, timeptr->tm_sec, 61); - snprintf(tbuf, TBufLen, "%02d", i); - break; - - case 'U': /* week of year, Sunday is first day of week */ - snprintf(tbuf, TBufLen, "%d", weeknumber(timeptr, 0)); - break; - - case 'w': /* weekday, Sunday == 0, 0 - 6 */ - i = range(0, timeptr->tm_wday, 6); - snprintf(tbuf, TBufLen, "%d", i); - break; - - case 'W': /* week of year, Monday is first day of week */ - snprintf(tbuf, TBufLen, "%d", weeknumber(timeptr, 1)); - break; - - case 'x': /* appropriate date representation */ - snprintf(tbuf, TBufLen, "%s %s %2d %d", - days_a[range(0, timeptr->tm_wday, 6)], - months_a[range(0, timeptr->tm_mon, 11)], - range(1, timeptr->tm_mday, 31), - timeptr->tm_year + 1900); - break; - - case 'X': /* appropriate time representation */ - snprintf(tbuf, TBufLen, "%02d:%02d:%02d", - range(0, timeptr->tm_hour, 23), - range(0, timeptr->tm_min, 59), - range(0, timeptr->tm_sec, 61)); - break; - - case 'y': /* year without a century, 00 - 99 */ - i = timeptr->tm_year % 100; - snprintf(tbuf, TBufLen, "%d", i); - break; - - case 'Y': /* year with century */ - snprintf(tbuf, TBufLen, "%d", 1900 + timeptr->tm_year); - break; - -#if HAVE_GET_TZ_NAME - case 'Z': /* time zone name or abbrevation */ - fsl_strlcpy(tbuf, get_tz_name(timeptr), TBufLen); - break; -#endif - -#ifdef SYSV_EXT - case 'n': /* same as \n */ - tbuf[0] = '\n'; - tbuf[1] = '\0'; - break; - - case 't': /* same as \t */ - tbuf[0] = '\t'; - tbuf[1] = '\0'; - break; - - case 'D': /* date as %m/%d/%y */ - fsl_strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr); - break; - - case 'e': /* day of month, blank padded */ - snprintf(tbuf, TBufLen, "%2d", range(1, timeptr->tm_mday, 31)); - break; - - case 'r': /* time as %I:%M:%S %p */ - fsl_strftime(tbuf, sizeof tbuf, "%I:%M:%S %p", timeptr); - break; - - case 'R': /* time as %H:%M */ - fsl_strftime(tbuf, sizeof tbuf, "%H:%M", timeptr); - break; - - case 'T': /* time as %H:%M:%S */ - fsl_strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr); - break; -#endif - -#ifdef SUNOS_EXT - case 'k': /* hour, 24-hour clock, blank pad */ - snprintf(tbuf, TBufLen, "%2d", range(0, timeptr->tm_hour, 23)); - break; - - case 'l': /* hour, 12-hour clock, 1 - 12, blank pad */ - i = range(0, timeptr->tm_hour, 23); - if (i == 0) - i = 12; - else if (i > 12) - i -= 12; - snprintf(tbuf, TBufLen, "%2d", i); - break; + enum {TBufLen = 100U}; + char *endp = s + maxsize; + char *start = s; + char tbuf[TBufLen]; + int i; + static short first = 1; +#ifdef POSIX_SEMANTICS + static char *savetz = NULL; + static int savetzlen = 0; + char *tz; +#endif /* POSIX_SEMANTICS */ + + /* various tables, useful in North America */ + static char *days_a[] = { + "Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat", + }; + static char *days_l[] = { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday", + }; + static char *months_a[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + }; + static char *months_l[] = { + "January", "February", "March", "April", + "May", "June", "July", "August", "September", + "October", "November", "December", + }; + static char *ampm[] = { "AM", "PM", }; + + if (s == NULL || format == NULL || timeptr == NULL || maxsize == 0) + return 0; + + if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize) + return 0; + +#ifndef POSIX_SEMANTICS + if (first) { + tzset(); + first = 0; + } +#else /* POSIX_SEMANTICS */ + tz = getenv("TZ"); + if (first) { + if (tz != NULL) { + int tzlen = strlen(tz); + + savetz = (char *) malloc(tzlen + 1); + if (savetz != NULL) { + savetzlen = tzlen + 1; + strcpy(savetz, tz); + } + } + tzset(); + first = 0; + } + /* if we have a saved TZ, and it is different, recapture and reset */ + if (tz && savetz && (tz[0] != savetz[0] || strcmp(tz, savetz) != 0)) { + i = strlen(tz) + 1; + if (i > savetzlen) { + savetz = (char *) realloc(savetz, i); + if (savetz) { + savetzlen = i; + strcpy(savetz, tz); + } + } else + strcpy(savetz, tz); + tzset(); + } +#endif /* POSIX_SEMANTICS */ + + for (; *format && s < endp - 1; format++) { + tbuf[0] = '\0'; + if (*format != '%') { + *s++ = *format; + continue; + } + again: + switch (*++format) { + case '\0': + *s++ = '%'; + goto out; + + case '%': + *s++ = '%'; + continue; + + case 'a': /* abbreviated weekday name */ + if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6) + strcpy(tbuf, "?"); + else + strcpy(tbuf, days_a[timeptr->tm_wday]); + break; + + case 'A': /* full weekday name */ + if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6) + strcpy(tbuf, "?"); + else + strcpy(tbuf, days_l[timeptr->tm_wday]); + break; + +#ifdef SYSV_EXT + case 'h': /* abbreviated month name */ +#endif + case 'b': /* abbreviated month name */ + if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11) + strcpy(tbuf, "?"); + else + strcpy(tbuf, months_a[timeptr->tm_mon]); + break; + + case 'B': /* full month name */ + if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11) + strcpy(tbuf, "?"); + else + strcpy(tbuf, months_l[timeptr->tm_mon]); + break; + + case 'c': /* appropriate date and time representation */ + snprintf(tbuf, TBufLen, "%s %s %2d %02d:%02d:%02d %d", + days_a[range(0, timeptr->tm_wday, 6)], + months_a[range(0, timeptr->tm_mon, 11)], + range(1, timeptr->tm_mday, 31), + range(0, timeptr->tm_hour, 23), + range(0, timeptr->tm_min, 59), + range(0, timeptr->tm_sec, 61), + timeptr->tm_year + 1900); + break; + + case 'd': /* day of the month, 01 - 31 */ + i = range(1, timeptr->tm_mday, 31); + snprintf(tbuf, TBufLen, "%02d", i); + break; + + case 'H': /* hour, 24-hour clock, 00 - 23 */ + i = range(0, timeptr->tm_hour, 23); + snprintf(tbuf, TBufLen, "%02d", i); + break; + + case 'I': /* hour, 12-hour clock, 01 - 12 */ + i = range(0, timeptr->tm_hour, 23); + if (i == 0) + i = 12; + else if (i > 12) + i -= 12; + snprintf(tbuf, TBufLen, "%02d", i); + break; + + case 'j': /* day of the year, 001 - 366 */ + snprintf(tbuf, TBufLen, "%03d", timeptr->tm_yday + 1); + break; + + case 'm': /* month, 01 - 12 */ + i = range(0, timeptr->tm_mon, 11); + snprintf(tbuf, TBufLen, "%02d", i + 1); + break; + + case 'M': /* minute, 00 - 59 */ + i = range(0, timeptr->tm_min, 59); + snprintf(tbuf, TBufLen, "%02d", i); + break; + + case 'p': /* am or pm based on 12-hour clock */ + i = range(0, timeptr->tm_hour, 23); + if (i < 12) + strcpy(tbuf, ampm[0]); + else + strcpy(tbuf, ampm[1]); + break; + + case 'S': /* second, 00 - 61 */ + i = range(0, timeptr->tm_sec, 61); + snprintf(tbuf, TBufLen, "%02d", i); + break; + + case 'U': /* week of year, Sunday is first day of week */ + snprintf(tbuf, TBufLen, "%d", weeknumber(timeptr, 0)); + break; + + case 'w': /* weekday, Sunday == 0, 0 - 6 */ + i = range(0, timeptr->tm_wday, 6); + snprintf(tbuf, TBufLen, "%d", i); + break; + + case 'W': /* week of year, Monday is first day of week */ + snprintf(tbuf, TBufLen, "%d", weeknumber(timeptr, 1)); + break; + + case 'x': /* appropriate date representation */ + snprintf(tbuf, TBufLen, "%s %s %2d %d", + days_a[range(0, timeptr->tm_wday, 6)], + months_a[range(0, timeptr->tm_mon, 11)], + range(1, timeptr->tm_mday, 31), + timeptr->tm_year + 1900); + break; + + case 'X': /* appropriate time representation */ + snprintf(tbuf, TBufLen, "%02d:%02d:%02d", + range(0, timeptr->tm_hour, 23), + range(0, timeptr->tm_min, 59), + range(0, timeptr->tm_sec, 61)); + break; + + case 'y': /* year without a century, 00 - 99 */ + i = timeptr->tm_year % 100; + snprintf(tbuf, TBufLen, "%d", i); + break; + + case 'Y': /* year with century */ + snprintf(tbuf, TBufLen, "%d", 1900 + timeptr->tm_year); + break; + +#if HAVE_GET_TZ_NAME + case 'Z': /* time zone name or abbrevation */ + strcpy(tbuf, get_tz_name(timeptr)); + break; +#endif + +#ifdef SYSV_EXT + case 'n': /* same as \n */ + tbuf[0] = '\n'; + tbuf[1] = '\0'; + break; + + case 't': /* same as \t */ + tbuf[0] = '\t'; + tbuf[1] = '\0'; + break; + + case 'D': /* date as %m/%d/%y */ + fsl_strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr); + break; + + case 'e': /* day of month, blank padded */ + snprintf(tbuf, TBufLen, "%2d", range(1, timeptr->tm_mday, 31)); + break; + + case 'r': /* time as %I:%M:%S %p */ + fsl_strftime(tbuf, sizeof tbuf, "%I:%M:%S %p", timeptr); + break; + + case 'R': /* time as %H:%M */ + fsl_strftime(tbuf, sizeof tbuf, "%H:%M", timeptr); + break; + + case 'T': /* time as %H:%M:%S */ + fsl_strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr); + break; +#endif + +#ifdef SUNOS_EXT + case 'k': /* hour, 24-hour clock, blank pad */ + snprintf(tbuf, TBufLen, "%2d", range(0, timeptr->tm_hour, 23)); + break; + + case 'l': /* hour, 12-hour clock, 1 - 12, blank pad */ + i = range(0, timeptr->tm_hour, 23); + if (i == 0) + i = 12; + else if (i > 12) + i -= 12; + snprintf(tbuf, TBufLen, "%2d", i); + break; #endif #ifdef VMS_EXT - case 'v': /* date as dd-bbb-YYYY */ - snprintf(tbuf, TBufLen, "%2d-%3.3s-%4d", - range(1, timeptr->tm_mday, 31), - months_a[range(0, timeptr->tm_mon, 11)], - timeptr->tm_year + 1900); - for (i = 3; i < 6; i++) - if (islower((int)tbuf[i])) - tbuf[i] = toupper((int)tbuf[i]); - break; + case 'v': /* date as dd-bbb-YYYY */ + snprintf(tbuf, TBufLen, "%2d-%3.3s-%4d", + range(1, timeptr->tm_mday, 31), + months_a[range(0, timeptr->tm_mon, 11)], + timeptr->tm_year + 1900); + for (i = 3; i < 6; i++) + if (islower((int)tbuf[i])) + tbuf[i] = toupper((int)tbuf[i]); + break; #endif #ifdef POSIX2_DATE - case 'C': - snprintf(tbuf, TBufLen, "%02d", (timeptr->tm_year + 1900) / 100); - break; + case 'C': + snprintf(tbuf, TBufLen, "%02d", (timeptr->tm_year + 1900) / 100); + break; - case 'E': - case 'O': - /* POSIX locale extensions, ignored for now */ - goto again; + case 'E': + case 'O': + /* POSIX locale extensions, ignored for now */ + goto again; - case 'V': /* week of year according ISO 8601 */ + case 'V': /* week of year according ISO 8601 */ #if defined(GAWK) && defined(VMS_EXT) { - extern int do_lint; - extern void warning(); - static int warned = 0; - - if (! warned && do_lint) { - warned = 1; - warning( - "conversion %%V added in P1003.2/11.3; for VMS style date, use %%v"); - } + extern int do_lint; + extern void warning(); + static int warned = 0; + + if (! warned && do_lint) { + warned = 1; + warning( + "conversion %%V added in P1003.2/11.3; for VMS style date, use %%v"); + } } #endif - snprintf(tbuf, TBufLen, "%d", iso8601wknum(timeptr)); - break; + snprintf(tbuf, TBufLen, "%d", iso8601wknum(timeptr)); + break; - case 'u': + case 'u': /* ISO 8601: Weekday as a decimal number [1 (Monday) - 7] */ - snprintf(tbuf, TBufLen, "%d", timeptr->tm_wday == 0 ? 7 : - timeptr->tm_wday); - break; + snprintf(tbuf, TBufLen, "%d", timeptr->tm_wday == 0 ? 7 : + timeptr->tm_wday); + break; #endif /* POSIX2_DATE */ - default: - tbuf[0] = '%'; - tbuf[1] = *format; - tbuf[2] = '\0'; - break; - } - i = strlen(tbuf); - if (i){ - if (s + i < endp - 1) { - fsl_strlcpy(s, tbuf, TBufLen); - s += i; - } else return 0; - /* reminder: above IF originally had ambiguous else - placement (no braces). This placement _appears_ to be - correct.*/ - } - } - out: - if (s < endp && *format == '\0') { - *s = '\0'; - return (s - start); - } else - return 0; + default: + tbuf[0] = '%'; + tbuf[1] = *format; + tbuf[2] = '\0'; + break; + } + i = strlen(tbuf); + if (i){ + if (s + i < endp - 1) { + strcpy(s, tbuf); + s += i; + } else return 0; + /* reminder: above IF originally had ambiguous else + placement (no braces). This placement _appears_ to be + correct.*/ + } + } +out: + if (s < endp && *format == '\0') { + *s = '\0'; + return (s - start); + } else + return 0; } #ifdef POSIX2_DATE /* iso8601wknum --- compute week number according to ISO 8601 */ static int iso8601wknum(const struct tm *timeptr) { - /* - * From 1003.2 D11.3: - * If the week (Monday to Sunday) containing January 1 - * has four or more days in the new year, then it is week 1; - * otherwise it is week 53 of the previous year, and the - * next week is week 1. - * - * ADR: This means if Jan 1 was Monday through Thursday, - * it was week 1, otherwise week 53. - */ - - int simple_wknum, jan1day, diff, ret; - - /* get week number, Monday as first day of the week */ - simple_wknum = weeknumber(timeptr, 1) + 1; - - /* - * With thanks and tip of the hatlo to tml@tik.vtt.fi - * - * What day of the week does January 1 fall on? - * We know that - * (timeptr->tm_yday - jan1.tm_yday) MOD 7 == - * (timeptr->tm_wday - jan1.tm_wday) MOD 7 - * and that - * jan1.tm_yday == 0 - * and that - * timeptr->tm_wday MOD 7 == timeptr->tm_wday - * from which it follows that. . . - */ - jan1day = timeptr->tm_wday - (timeptr->tm_yday % 7); - if (jan1day < 0) - jan1day += 7; - - /* - * If Jan 1 was a Monday through Thursday, it was in - * week 1. Otherwise it was last year's week 53, which is - * this year's week 0. - */ - if (jan1day >= 1 && jan1day <= 4) - diff = 0; - else - diff = 1; - ret = simple_wknum - diff; - if (ret == 0) /* we're in the first week of the year */ - ret = 53; - return ret; + /* + * From 1003.2 D11.3: + * If the week (Monday to Sunday) containing January 1 + * has four or more days in the new year, then it is week 1; + * otherwise it is week 53 of the previous year, and the + * next week is week 1. + * + * ADR: This means if Jan 1 was Monday through Thursday, + * it was week 1, otherwise week 53. + */ + + int simple_wknum, jan1day, diff, ret; + + /* get week number, Monday as first day of the week */ + simple_wknum = weeknumber(timeptr, 1) + 1; + + /* + * With thanks and tip of the hatlo to tml@tik.vtt.fi + * + * What day of the week does January 1 fall on? + * We know that + * (timeptr->tm_yday - jan1.tm_yday) MOD 7 == + * (timeptr->tm_wday - jan1.tm_wday) MOD 7 + * and that + * jan1.tm_yday == 0 + * and that + * timeptr->tm_wday MOD 7 == timeptr->tm_wday + * from which it follows that. . . + */ + jan1day = timeptr->tm_wday - (timeptr->tm_yday % 7); + if (jan1day < 0) + jan1day += 7; + + /* + * If Jan 1 was a Monday through Thursday, it was in + * week 1. Otherwise it was last year's week 53, which is + * this year's week 0. + */ + if (jan1day >= 1 && jan1day <= 4) + diff = 0; + else + diff = 1; + ret = simple_wknum - diff; + if (ret == 0) /* we're in the first week of the year */ + ret = 53; + return ret; } #endif /* weeknumber --- figure how many weeks into the year */ @@ -37590,14 +36591,14 @@ /* With thanks and tip of the hatlo to ado@elsie.nci.nih.gov */ static int weeknumber(const struct tm *timeptr, int firstweekday) { - if (firstweekday == 0) - return (timeptr->tm_yday + 7 - timeptr->tm_wday) / 7; - else - return (timeptr->tm_yday + 7 - + if (firstweekday == 0) + return (timeptr->tm_yday + 7 - timeptr->tm_wday) / 7; + else + return (timeptr->tm_yday + 7 - (timeptr->tm_wday ? (timeptr->tm_wday - 1) : 6)) / 7; } #undef SYSV_EXT #undef SUNOS_EXT @@ -37702,11 +36703,11 @@ } } return t; } -fsl_id_t fsl_tag_id( fsl_cx * const f, char const * tag, bool create ){ +fsl_id_t fsl_tag_id( fsl_cx * f, char const * tag, bool create ){ fsl_db * db = fsl_cx_db_repo(f); int64_t id = 0; int rc; if(!db || !tag) return FSL_RC_MISUSE; else if(!*tag) return FSL_RC_RANGE; @@ -37727,16 +36728,16 @@ } return id; } -int fsl__tag_propagate(fsl_cx * const f, fsl_tagtype_e tagType, +int fsl_tag_propagate(fsl_cx *f, fsl_tagtype_e tagType, fsl_id_t pid, fsl_id_t tagid, fsl_id_t origId, const char *zValue, double mtime){ int rc; - fsl__pq queue = fsl__pq_empty /* Queue of artifacts to be tagged */; + fsl_pq queue = fsl_pq_empty /* Queue of artifacts to be tagged */; fsl_stmt s = fsl_stmt_empty /* Query the children of :pid to which to propagate */; fsl_stmt ins = fsl_stmt_empty /* INSERT INTO tagxref */; fsl_stmt eventupdate = fsl_stmt_empty /* UPDATE event */; fsl_db * const db = fsl_needs_repo(f); @@ -37751,11 +36752,11 @@ || (FSL_TAGTYPE_PROPAGATING==tagType && origId<=0)){ return FSL_RC_RANGE; } else if(!db) return FSL_RC_NOT_A_REPO; - rc = fsl__pq_insert(&queue, pid, 0.0, NULL); + rc = fsl_pq_insert(&queue, pid, 0.0, NULL); if(rc) return rc; rc = fsl_db_prepare(db, &s, "SELECT cid, plink.mtime," " coalesce(srcid=0 AND " @@ -37802,11 +36803,11 @@ "UPDATE event SET bgcolor=%Q " "WHERE objid=:rid", zValue); if(rc) goto end; } - while( 0 != (pid = fsl__pq_extract(&queue,NULL))){ + while( 0 != (pid = fsl_pq_extract(&queue,NULL))){ fsl_stmt_bind_id_name(&s, ":pid", pid); #if 0 MARKER(("Walking over pid %"FSL_ID_T_PFMT ", queue.used=%"FSL_SIZE_T_PFMT"\n", pid, queue.used)); #endif @@ -37815,11 +36816,11 @@ if(doit){ fsl_id_t cid = fsl_stmt_g_id(&s, 0); double mtime = fsl_stmt_g_double(&s,1); assert(cid>0); assert(mtime>0.0); - rc = fsl__pq_insert(&queue, cid, mtime, NULL); + rc = fsl_pq_insert(&queue, cid, mtime, NULL); if(!rc) rc = fsl_stmt_bind_id_name(&ins, ":rid", cid); if(rc) goto end; else { rc = fsl_stmt_step(&ins); if(FSL_RC_STEP_DONE != rc) goto end; @@ -37833,26 +36834,26 @@ if(FSL_RC_STEP_DONE != rc) goto end; rc = 0; } fsl_stmt_reset(&eventupdate); }else if( FSL_TAGID_BRANCH == tagid ){ - rc = fsl__repo_leafeventually_check(f, cid); + rc = fsl_repo_leaf_eventually_check(f, cid); } } } fsl_stmt_reset(&s); } end: fsl_stmt_finalize(&s); fsl_stmt_finalize(&ins); fsl_stmt_finalize(&eventupdate); - fsl__pq_clear(&queue); + fsl_pq_clear(&queue); return rc; } -int fsl__tag_propagate_all(fsl_cx * const f, fsl_id_t pid){ +int fsl_tag_propagate_all(fsl_cx * f, fsl_id_t pid){ fsl_stmt q = fsl_stmt_empty; int rc; fsl_db * const db = fsl_cx_db_repo(f); if(!f) return FSL_RC_MISUSE; else if(pid<=0) return FSL_RC_RANGE; @@ -37868,19 +36869,19 @@ double const mtime = fsl_stmt_g_double(&q, 2); const char *zValue = fsl_stmt_g_text(&q, 3, NULL); fsl_id_t const origid = fsl_stmt_g_id(&q, 4); if( FSL_TAGTYPE_ADD==tagtype ) tagtype = FSL_TAGTYPE_CANCEL /* For propagating purposes */; - rc = fsl__tag_propagate(f, tagtype, pid, tagid, + rc = fsl_tag_propagate(f, tagtype, pid, tagid, origid, zValue, mtime); } fsl_stmt_finalize(&q); return rc; } -int fsl__tag_insert( fsl_cx * const f, fsl_tagtype_e tagtype, +int fsl_tag_insert( fsl_cx * f, fsl_tagtype_e tagtype, char const * zTag, char const * zValue, fsl_id_t srcId, double mtime, fsl_id_t rid, fsl_id_t *outRid ){ fsl_db * db = f ? fsl_cx_db_repo(f) : NULL; fsl_stmt q = fsl_stmt_empty; @@ -37887,11 +36888,11 @@ fsl_id_t tagid; int rc = 0; char const * zCol; if(!f || !zTag) return FSL_RC_MISUSE; else if(!db) return FSL_RC_NOT_A_REPO; - tagid = fsl_tag_id(f, zTag, true); + tagid = fsl_tag_id(f, zTag, 1); if(tagid<0){ assert(f->error.code); return f->error.code; } if( mtime<=0.0 ){ @@ -37943,11 +36944,11 @@ if(FSL_RC_STEP_DONE != rc) goto end; rc = 0; fsl_stmt_finalize(&q); if(FSL_TAGID_BRANCH == tagid ){ - rc = fsl__repo_leafeventually_check(f, rid); + rc = fsl_repo_leaf_eventually_check(f, rid); if(rc) goto end; } #if 0 /* Historical: we have valid use cases for the value here. @@ -38004,15 +37005,19 @@ zValue, (fsl_id_t)rid); if(rc) goto end; } if( FSL_TAGTYPE_ADD == tagtype ) tagtype = FSL_TAGTYPE_CANCEL /* For propagation purposes */; - rc = fsl__tag_propagate(f, tagtype, rid, tagid, + rc = fsl_tag_propagate(f, tagtype, rid, tagid, rid, zValue, mtime); end: - fsl_stmt_finalize(&q); - if(0==rc && outRid) *outRid = tagid; + if(rc){ + fsl_stmt_finalize(&q); + }else{ + assert(!q.stmt); + if(outRid) *outRid = tagid; + } return rc; } int fsl_tag_sym( fsl_cx * f, fsl_tagtype_e tagType, @@ -38020,11 +37025,11 @@ char const * tagName, char const * tagValue, char const * userName, double mtime, fsl_id_t * outId ){ - if(!tagName || !symToTag || !userName) return FSL_RC_MISUSE; + if(!f || !tagName || !symToTag || !userName) return FSL_RC_MISUSE; else if(!*tagName || !*userName || !*symToTag) return FSL_RC_RANGE; else{ fsl_id_t resolvedRid = 0; int rc; rc = fsl_sym_to_rid( f, symToTag, FSL_SATYPE_ANY, &resolvedRid ); @@ -38036,19 +37041,19 @@ } return rc; } } -int fsl_tag_an_rid( fsl_cx * const f, +int fsl_tag_an_rid( fsl_cx * f, fsl_tagtype_e tagType, fsl_id_t idToTag, char const * tagName, char const * tagValue, char const * userName, double mtime, fsl_id_t * outId ){ - fsl_db * const dbR = fsl_cx_db_repo(f); + fsl_db * dbR = f ? fsl_cx_db_repo(f) : NULL; fsl_deck c = fsl_deck_empty; char * resolvedUuid = NULL; fsl_buffer mfout = fsl_buffer_empty; int rc; @@ -38192,17 +37197,17 @@ rc = fsl_deck_T_add(&deck, FSL_TAGTYPE_PROPAGATING, NULL, "branch", opt->name); if(!rc){ /* Add tag named sym-BRANCHNAME... */ - fsl_buffer * buf = fsl__cx_scratchpad(f); + fsl_buffer * buf = fsl_cx_scratchpad(f); rc = fsl_buffer_appendf(buf, "sym-%s", opt->name); if(!rc){ rc = fsl_deck_T_add(&deck, FSL_TAGTYPE_PROPAGATING, NULL, fsl_buffer_cstr(buf), NULL); } - fsl__cx_scratchpad_yield(f, buf); + fsl_cx_scratchpad_yield(f, buf); } if(rc) goto end; #if 1 rc = fsl_db_transaction_begin(db); @@ -38232,11 +37237,11 @@ rc = fsl_db_exec(db, "INSERT OR IGNORE INTO " "unsent VALUES(%"FSL_ID_T_PFMT")", (fsl_id_t)deck.rid); if(!rc){ /* Make the parent a delta of this one. */ - rc = fsl__content_deltify(f, parent.rid, deck.rid, 0); + rc = fsl_content_deltify(f, parent.rid, deck.rid, 0); } } } if(!rc) rc = fsl_db_transaction_commit(db); else fsl_db_transaction_rollback(db); @@ -38280,11 +37285,11 @@ This file implements ticket-related parts of the library. */ #include #include /* memcmp() */ -int fsl__cx_ticket_create_table(fsl_cx * const f){ +int fsl_cx_ticket_create_table(fsl_cx * const f){ fsl_db * const db = fsl_needs_repo(f); int rc; if(!db) return FSL_RC_NOT_A_REPO; rc = fsl_cx_exec_multi(f, "DROP TABLE IF EXISTS ticket;" @@ -38307,19 +37312,19 @@ if( !fsl_strcmp(zFieldName, jc->field) ) return i; } return -1; } -int fsl__cx_ticket_load_fields(fsl_cx * const f, bool forceReload){ +int fsl_cx_ticket_load_fields(fsl_cx * f, bool forceReload){ fsl_stmt q = fsl_stmt_empty; int i, rc = 0; fsl_list * li = &f->ticket.customFields; fsl_card_J * jc; fsl_db * db; if(li->used){ if(!forceReload) return 0; - fsl__card_J_list_free(li, 0); + fsl_card_J_list_free(li, 0); /* Fall through and reload ... */ } if( !(db = fsl_needs_repo(f)) ){ return FSL_RC_NOT_A_REPO; } @@ -38372,11 +37377,11 @@ } } fsl_stmt_finalize(&q); end: if(!rc){ - fsl_list_sort(li, fsl__qsort_cmp_J_cards); + fsl_list_sort(li, fsl_qsort_cmp_J_cards); } return rc; } /* end of file ticket.c */ /* start of file utf8.c */ @@ -38895,11 +37900,11 @@ fsl_stmt q = fsl_stmt_empty; int rc = 0; fsl_db * const db = fsl_needs_ckout(f); fsl_fstat fst = fsl_fstat_empty; fsl_size_t rootLen; - fsl_buffer * fileCksum = fsl__cx_scratchpad(f); + fsl_buffer * fileCksum = fsl_cx_scratchpad(f); bool const useMtime = (cksigFlags & FSL_VFILE_CKSIG_HASH)==0 && fsl_config_get_bool(f, FSL_CONFDB_REPO, true, "mtime-changes"); if(!db) return FSL_RC_NOT_A_CKOUT; assert(f->ckout.dir); if(vid<=0) vid = f->ckout.rid; @@ -39118,14 +38123,14 @@ "WHERE vf.vid=%"FSL_ID_T_PFMT" " "AND (chnged<>0 OR pathname<>origname OR deleted<>0)" "ORDER BY vf.id", vid); #endif end: - fsl__cx_scratchpad_yield(f, fileCksum); + fsl_cx_scratchpad_yield(f, fileCksum); if(!rc && (cksigFlags & FSL_VFILE_CKSIG_WRITE_CKOUT_VERSION) && (f->ckout.rid != vid)){ - rc = fsl__ckout_version_write(f, vid, 0); + rc = fsl_ckout_version_write(f, vid, 0); }else if(rc){ rc = fsl_cx_uplift_db_error2(f, db, rc); } if(rc) { fsl_db_transaction_rollback(db); @@ -39138,11 +38143,11 @@ fsl_stmt_cached_yield(stUpdate); fsl_stmt_finalize(&q); return rc; } -int fsl__vfile_to_ckout(fsl_cx * const f, fsl_id_t vfileId, +int fsl_vfile_to_ckout(fsl_cx * f, fsl_id_t vfileId, int * wasWritten){ int rc = 0; fsl_db * const db = fsl_needs_ckout(f); fsl_stmt q = fsl_stmt_empty; int counter = 0; @@ -39192,15 +38197,15 @@ char const * zHash = fsl_stmt_g_text(&q, 5, &hashLen); char const * zRelName = &zName[f->ckout.dirLen]; int isMod = 0; ++counter; assert(nameLen > f->ckout.dirLen); - rc = fsl__ckout_safe_file_check(f, zName); + rc = fsl_ckout_safe_file_check(f, zName); if(rc) break; assert(fsl_is_uuid_len(hashLen)); f->cache.fstat = fsl_fstat_empty; - rc = fsl__is_locally_modified(f, zName, sz, zHash, + rc = fsl_is_locally_modified(f, zName, sz, zHash, (fsl_int_t)hashLen, isExe ? FSL_FILE_PERM_EXE : (isLink ? FSL_FILE_PERM_LINK : FSL_FILE_PERM_REGULAR), @@ -39220,44 +38225,44 @@ else if((rc=fsl_mkdir_for_file(zName, true))){ rc = fsl_cx_err_set(f, rc, "mkdir() failed for file: %s", zName); break; } - if(FSL__LOCALMOD_LINK & isMod){ + if(FSL_LOCALMOD_LINK & isMod){ assert(((isLink && FSL_FILE_PERM_LINK!=fst->perm) ||(!isLink && FSL_FILE_PERM_LINK==fst->perm)) - && "Expected fsl__is_locally_modified() to set this."); + && "Expected fsl_is_locally_modified() to set this."); rc = fsl_file_unlink(zName); if(rc){ rc = fsl_cx_err_set(f, rc, "Error removing target to replace it: %s", zRelName); break; } } - if(isLink || (isMod & (FSL__LOCALMOD_NOTFOUND - | FSL__LOCALMOD_LINK - | FSL__LOCALMOD_CONTENT))){ + if(isLink || (isMod & (FSL_LOCALMOD_NOTFOUND + | FSL_LOCALMOD_LINK + | FSL_LOCALMOD_CONTENT))){ /* switched link type, content changed, or was not found in the filesystem. */ rc = fsl_content_get(f, rid, &content); if(rc) break; } if(isLink){ - rc = fsl__ckout_symlink_create(f, zName, + rc = fsl_ckout_symlink_create(f, zName, fsl_buffer_cstr(&content)); if(wasWritten && !rc) *wasWritten = 2; - }else if(isMod & (FSL__LOCALMOD_NOTFOUND | FSL__LOCALMOD_CONTENT)){ + }else if(isMod & (FSL_LOCALMOD_NOTFOUND | FSL_LOCALMOD_CONTENT)){ /* Not found locally or its contents differ. */ rc = fsl_buffer_to_filename(&content, zName); if(rc){ rc = fsl_cx_err_set(f, rc, "Error writing to file: %s", zRelName); }else if(wasWritten){ *wasWritten = 2; } - }else if(wasWritten && (isMod & FSL__LOCALMOD_PERM)){ + }else if(wasWritten && (isMod & FSL_LOCALMOD_PERM)){ *wasWritten = 1; } if(rc) break; fsl_file_exec_set(zName, !!isExe); fsl_buffer_reuse(&content); @@ -39902,11 +38907,11 @@ if(rc) goto end; { char * u = NULL; if(!userName) userName = fsl_cx_user_get(f); if(!userName){ - u = fsl_user_name_guess(); + u = fsl_guess_user_name(); if(!u) rc = FSL_RC_OOM; } if(!rc) rc = fsl_deck_U_set(&d, u ? u : userName); if(u) fsl_free(u); if(rc) goto end; @@ -39987,22 +38992,22 @@ S = atoi(&zDate[17]); z->dosTime = (H<<11) + (M<<5) + (S>>1); z->dosDate = ((y-1980)<<9) + (m<<5) + d; } -fsl_buffer const * fsl_zip_body( fsl_zip_writer const * const z ){ +fsl_buffer const * fsl_zip_body( fsl_zip_writer const * z ){ return z ? &z->body : NULL; } -void fsl_zip_timestamp_set_julian(fsl_zip_writer * const z, double rDate){ +void fsl_zip_timestamp_set_julian(fsl_zip_writer *z, double rDate){ char buf[20] = {0}; fsl_julian_to_iso8601(rDate, buf, 0); fzip_timestamp_from_str(z, buf); z->unixTime = (fsl_time_t)((rDate - 2440587.5)*86400.0); } -void fsl_zip_timestamp_set_unix(fsl_zip_writer * const z, fsl_time_t epochTime){ +void fsl_zip_timestamp_set_unix(fsl_zip_writer *z, fsl_time_t epochTime){ char buf[20] = {0}; fsl_julian_to_iso8601(fsl_unix_to_julian(epochTime), buf, 0); fzip_timestamp_from_str(z, buf); z->unixTime = epochTime; } @@ -40011,11 +39016,11 @@ /** Adds all directories for the given file to the zip if they are not in there already. Returns 0 on success, non-0 on error (namely OOM). */ -static int fzip_mkdir(fsl_zip_writer * const z, char const *zName); +static int fzip_mkdir(fsl_zip_writer * z, char const *zName); /** Adds a file entry to zw's zip output. zName is the virtual name of the file or directory. If pSrc is NULL then it is assumed that we are creating a directory, otherwise the zip's entry is populated @@ -40022,11 +39027,11 @@ from pSrc. mPerms specify the fossil-specific permission flags from the fsl_fileperm_e enum. If doMkDirs is true then fzip_mkdir() is called to create the directory entries for zName, otherwise they are not. */ -static int fzip_file_add(fsl_zip_writer * const zw, char const * zName, +static int fzip_file_add(fsl_zip_writer *zw, char const * zName, fsl_buffer const * pSrc, int mPerm, char doMkDirs){ int rc = 0; z_stream stream; fsl_size_t nameLen; @@ -40166,11 +39171,11 @@ ++zw->entryCount; return rc; } -int fzip_mkdir(fsl_zip_writer * const z, char const *zName){ +int fzip_mkdir(fsl_zip_writer * z, char const *zName){ fsl_size_t i; fsl_size_t j; int rc = 0; char const * dirName; fsl_size_t nDir = z->dirs.used; @@ -40194,16 +39199,16 @@ } } return rc; } -int fsl_zip_file_add(fsl_zip_writer * const z, char const * zName, +int fsl_zip_file_add(fsl_zip_writer *z, char const * zName, fsl_buffer const * pSrc, int mPerm){ return fzip_file_add(z, zName, pSrc, mPerm, 1); } -int fsl_zip_root_set(fsl_zip_writer * const z, char const * zRoot ){ +int fsl_zip_root_set(fsl_zip_writer * z, char const * zRoot ){ if(!z) return FSL_RC_MISUSE; else if(zRoot && *zRoot && fsl_is_absolute_path(zRoot)){ return FSL_RC_RANGE; }else{ fsl_free(z->rootDir); @@ -40249,11 +39254,11 @@ } return 0; } } -static void fsl_zip_finalize_impl(fsl_zip_writer * const z, bool alsoBody){ +static void fsl_zip_finalize_impl(fsl_zip_writer * z, char alsoBody){ if(z){ fsl_buffer_clear(&z->toc); fsl_buffer_clear(&z->scratch); fsl_list_visit_free(&z->dirs, 1); assert(NULL==z->dirs.list); @@ -40267,16 +39272,16 @@ z->body = cp; } } } -void fsl_zip_finalize(fsl_zip_writer * const z){ +void fsl_zip_finalize(fsl_zip_writer * z){ fsl_zip_finalize_impl(z, 1); } -int fsl_zip_end( fsl_zip_writer * const z ){ +int fsl_zip_end( fsl_zip_writer * z ){ int rc; fsl_int_t iTocStart; fsl_int_t iTocEnd; char zBuf[30]; @@ -40300,11 +39305,11 @@ fsl_zip_finalize_impl(z, 0); assert(z->body.used); return rc; } -int fsl_zip_end_take( fsl_zip_writer * const z, fsl_buffer * dest ){ +int fsl_zip_end_take( fsl_zip_writer * z, fsl_buffer * dest ){ if(!z) return FSL_RC_MISUSE; else{ int rc; if(!dest){ rc = FSL_RC_MISUSE; @@ -40317,11 +39322,11 @@ fsl_zip_finalize( z ); return rc; } } -int fsl_zip_end_to_filename( fsl_zip_writer * const z, char const * filename ){ +int fsl_zip_end_to_filename( fsl_zip_writer * z, char const * filename ){ if(!z) return FSL_RC_MISUSE; else{ int rc; if(!filename || !*filename){ rc = FSL_RC_MISUSE; @@ -40439,17 +39444,17 @@ if(rc) goto end; } } /** - Always write the manifest files to the zip, regardless of - the repo-level settings. This decision is up for debate. */ + Always write he manifest files to the zip, regardless of + the repo-level settings. */ if(rc) goto end; else { fsl_buffer * const bManifest = &f->fileContent; - fsl_buffer * const bHash = fsl__cx_scratchpad(f); - fsl_buffer * const bTags = fsl__cx_scratchpad(f); + fsl_buffer * const bHash = fsl_cx_scratchpad(f); + fsl_buffer * const bTags = fsl_cx_scratchpad(f); fsl_buffer_reuse(bManifest); rc = fsl_repo_manifest_write(f, mf.rid, bManifest, bHash, bTags); if(rc) goto mf_end; rc = fsl_zip_file_add(&zs.z, "manifest", bManifest, FSL_FILE_PERM_REGULAR); @@ -40459,12 +39464,12 @@ if(rc) goto mf_end; rc = fsl_zip_file_add(&zs.z, "manifest.tags", bTags, FSL_FILE_PERM_REGULAR); mf_end: fsl_buffer_reuse(bManifest); - fsl__cx_scratchpad_yield(f, bHash); - fsl__cx_scratchpad_yield(f, bTags); + fsl_cx_scratchpad_yield(f, bHash); + fsl_cx_scratchpad_yield(f, bTags); } if(rc) goto end; rc = fsl_zip_end( &zs.z ); if(!rc) rc = fsl_buffer_to_filename( fsl_zip_body(&zs.z), fileName ); Index: lib/libfossil.h ================================================================== --- lib/libfossil.h +++ lib/libfossil.h @@ -465,10 +465,11 @@ /** Empty-initialized fsl_list structure, intended for copy initialization. */ FSL_EXPORT const fsl_list fsl_list_empty; + /** 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), @@ -1030,24 +1031,24 @@ */ FSL_EXPORT int fsl_error_setv( fsl_error * const err, int code, char const * fmt, va_list args ); /** - Fetches the error state from err. Returns err's current error code. + Fetches the error state from err. If !err it returns + FSL_RC_MISUSE without side-effects, else it returns err's current + error code. If str is not NULL then *str will be assigned to the raw (NUL-terminated) error string (which might be empty or even NULL). The memory for the string is owned by err and may be - invalidated by any calls which take err as a non-const parameter OR - which might modify it indirectly through a container object, so the - client is required to copy it if it is needed for later - reference. As a special case, if the error object has no message - then the returned string is set to NULL, as opposed to an empty - string. + invalidated by any calls which take err as a non-const parameter + OR which might modify it indirectly through a container object, + so the client is required to copy it if it is needed for later + reference. If len is not NULL then *len will be assigned to the length of - the (*str) string (in bytes). + the returned string (in bytes). @see fsl_error_set() @see fsl_error_clear() @see fsl_error_move() */ @@ -1054,12 +1055,12 @@ FSL_EXPORT int fsl_error_get( fsl_error const * const err, char const ** str, fsl_size_t * const len ); /** Frees up any resources owned by err and sets its error code to 0, - but does not free err. This is harmless no-op if err holds no - dynamically allocated no memory. + but does not free err. This is harmless no-op if !err or if err + holds no dynamically allocated no memory. @see fsl_error_set() @see fsl_error_get() @see fsl_error_move() @see fsl_error_reset() @@ -1495,12 +1496,12 @@ Overwrites dest's contents with a copy of those from src (reusing dest's memory if it has any). Results are undefined if either pointer is NULL or invalid. Returns 0 on success, FSL_RC_OOM on allocation error. */ -FSL_EXPORT int fsl_buffer_copy( fsl_buffer * const dest, - fsl_buffer const * const src ); +FSL_EXPORT int fsl_buffer_copy( fsl_buffer const * src, fsl_buffer * dest ); + /** Apply the delta in pDelta to the original content pOriginal to generate the target content pTarget. All three pointers must point to properly initialized memory. @@ -1782,19 +1783,19 @@ /** BSD strlcpy() variant which is less error prone than strncpy. Copy up to dstsz - 1 characters from src to dst and NUL-terminate the resulting string if dstsz is not 0. */ -FSL_EXPORT fsl_size_t fsl_strlcpy(char *dst, const char *src, fsl_size_t dstsz); +FSL_EXPORT size_t fsl_strlcpy(char *restrict dst, const char *restrict src, size_t dstsz); /** BSD strlcat() variant which is less error prone than strncat. Append src to the end of dst. Append at most dstsz - strlen(dst - 1) characters, and NUL-terminate unless dstsize is 0 or the passed in dst string was longer than dstsz to begin with. */ -FSL_EXPORT fsl_size_t fsl_strlcat(char *dst, const char *src, fsl_size_t dstsz); +FSL_EXPORT size_t fsl_strlcat(char *restrict dst, const char *restrict src, size_t dstsz); /** Equivalent to fsl_strncmp(lhs, rhs, X), where X is either FSL_STRLEN_SHA1 or FSL_STRLEN_K256: if both lhs and rhs are longer than FSL_STRLEN_SHA1 then they are assumed to be @@ -2814,11 +2815,11 @@ NULL. @see fsl_cx_user_set() @see fsl_cx_user_get() */ -FSL_EXPORT char * fsl_user_name_guess(); +FSL_EXPORT char * fsl_guess_user_name(); /** Tries to find the user's home directory. If found, 0 is returned, tgt's memory is _overwritten_ (not appended) with the path, and tgt->used is set to the path's string length. (Design @@ -3142,41 +3143,41 @@ during application of the delta are described in more detail in pErr. If pErr is NULL this behaves exactly as documented for fsl_delta_apply(). */ FSL_EXPORT int fsl_delta_apply2( unsigned char const *zSrc, - fsl_size_t lenSrc, - unsigned char const *zDelta, - fsl_size_t lenDelta, - unsigned char *zOut, - fsl_error * pErr); + fsl_size_t lenSrc, + unsigned char const *zDelta, + fsl_size_t lenDelta, + unsigned char *zOut, + fsl_error * pErr); /* Calculates the size (in bytes) of the output from applying a the given delta. On success 0 is returned and *appliedSize will be updated with the amount of memory required for applying the delta. zDelta must point to lenDelta bytes of memory in the format emitted by fsl_delta_create(). It is legal for appliedSize to point to the same memory as the 2nd argument. - Returns FSL_RC_RANGE if lenDelta is too short to be a delta. Returns - FSL_RC_DELTA_INVALID_TERMINATOR if the delta's encoded length is not - properly terminated. - - Results are undefined if any pointer argument is NULL. - - This routine is provided so that an procedure that is able to call - fsl_delta_apply() can learn how much space is required for the - output and hence allocate no more space that is really needed. + Returns FSL_RC_MISUSE if any pointer argument is NULL. Returns + FSL_RC_RANGE if lenDelta is too short to be a delta. Returns + FSL_RC_DELTA_INVALID_TERMINATOR if the delta's encoded length + is not properly terminated. + + This routine is provided so that an procedure that is able to + call fsl_delta_apply() can learn how much space is required for + the output and hence allocate nor more space that is really + needed. TODO?: consolidate 2nd and 3rd parameters into one i/o parameter? @see fsl_delta_apply() @see fsl_delta_create() */ FSL_EXPORT int fsl_delta_applied_size(unsigned char const *zDelta, - fsl_size_t lenDelta, - fsl_size_t * appliedSize); + fsl_size_t lenDelta, + fsl_size_t * appliedSize); /** "Fossilizes" the first len bytes of the given input string. If (len<0) then fsl_strlen(inp) is used to calculate its length. The output is appended to out, which is expanded as needed and @@ -3352,11 +3353,11 @@ Maintenance reminder: these values are holy and must not be changed without also changing the corresponding code in diff.c. - @deprecated Prefer fsl_diff2_flag_e and fsl_diff_v2() instead. + @deprecated in favor of fsl_diff2_flag_e and fsl_diff_v2() */ enum fsl_diff_flag_e { /** Ignore end-of-line whitespace */ FSL_DIFF_IGNORE_EOLWS = 0x01, /** Ignore end-of-line whitespace */ @@ -3460,12 +3461,11 @@ */ FSL_EXPORT int fsl_diff_text_to_buffer(fsl_buffer const *pA, fsl_buffer const *pB, fsl_buffer *pOut, short contextLines, short sbsWidth, int diffFlags ); -/** @enum fsl_diff2_flag_e - +/** Flags for use with the 2021-era text-diff generation APIs (fsl_diff_builder and friends). This set of flags may still change considerably. Maintenance reminder: some of these values are holy and must not be @@ -4682,12 +4682,11 @@ will pass a copy of zRoot through fsl_file_simplify_name() first). @see fsl_zip_finalize() */ -FSL_EXPORT int fsl_zip_root_set(fsl_zip_writer * const z, - char const * zRoot ); +FSL_EXPORT int fsl_zip_root_set(fsl_zip_writer * z, char const * zRoot ); /** Adds a file or directory to the ZIP writer z. zFilename is the virtual name of the file or directory. If pContent is NULL then it is assumed that we are creating one or more directories, @@ -4759,14 +4758,12 @@ @see fsl_zip_timestamp_set_unix() @see fsl_zip_end() @see fsl_zip_body() @see fsl_zip_finalize() */ -FSL_EXPORT int fsl_zip_file_add( fsl_zip_writer * const z, - char const * zFilename, - fsl_buffer const * pContent, - int permsFlag ); +FSL_EXPORT int fsl_zip_file_add( fsl_zip_writer * z, char const * zFilename, + fsl_buffer const * pContent, int permsFlag ); /** Ends the ZIP-creation process, padding all buffers, writing all final required values, and freeing up most of the memory owned by z. After calling this, z->body contains the full generated @@ -4785,11 +4782,11 @@ @see fsl_zip_file_add() @see fsl_zip_body() @see fsl_zip_finalize() @see fsl_zip_end_take() */ -FSL_EXPORT int fsl_zip_end( fsl_zip_writer * const z ); +FSL_EXPORT int fsl_zip_end( fsl_zip_writer * z ); /** This variant of fsl_zip_end() transfers the current contents of the zip's body to dest, replacing (freeing) any contents it may hold when this is called, then passes z to fsl_zip_finalize() @@ -4801,12 +4798,11 @@ error, the transfer of contents to dest does NOT take place, but z is finalized (if it is not NULL) regardless of success or failure (even if dest is NULL). i.e. on error z is still cleaned up. */ -FSL_EXPORT int fsl_zip_end_take( fsl_zip_writer * const z, - fsl_buffer * const dest ); +FSL_EXPORT int fsl_zip_end_take( fsl_zip_writer * z, fsl_buffer * dest ); /** This variant of fsl_zip_end_take() passes z to fsl_zip_end(), write's the ZIP body to the given filename, passes z to fsl_zip_finalize(), and returns the result of @@ -4817,12 +4813,11 @@ given file. On error z is STILL cleaned up, and the file might have been partially populated (only on I/O error after writing started). In either case, z is cleaned up and ready for re-use or (in the case of a heap-allocated instance) freed. */ -FSL_EXPORT int fsl_zip_end_to_filename( fsl_zip_writer * const z, - char const * filename ); +FSL_EXPORT int fsl_zip_end_to_filename( fsl_zip_writer * z, char const * filename ); /** Returns a pointer to z's ZIP content buffer. The contents are ONLY valid after fsl_zip_end() returns 0. @@ -4832,11 +4827,11 @@ @see fsl_zip_file_add() @see fsl_zip_end() @see fsl_zip_end_take() @see fsl_zip_finalize() */ -FSL_EXPORT fsl_buffer const * fsl_zip_body( fsl_zip_writer const * const z ); +FSL_EXPORT fsl_buffer const * fsl_zip_body( fsl_zip_writer const * z ); /** Frees all memory owned by z and resets it to a clean state, but does not free z. Any fsl_zip_writer instance which has been modified via the fsl_zip_xxx() family of functions MUST @@ -4848,11 +4843,11 @@ @see fsl_zip_timestamp_set_unix() @see fsl_zip_file_add() @see fsl_zip_end() @see fsl_zip_body() */ -FSL_EXPORT void fsl_zip_finalize(fsl_zip_writer * const z); +FSL_EXPORT void fsl_zip_finalize(fsl_zip_writer * z); /** Set z's date and time from a Julian Day number. Results are undefined if !z. Results will be invalid if rDate is negative. The timestamp is applied to all fsl_zip_file_add() operations until it @@ -4861,21 +4856,19 @@ @see fsl_zip_timestamp_set_unix() @see fsl_zip_file_add() @see fsl_zip_end() @see fsl_zip_body() */ -FSL_EXPORT void fsl_zip_timestamp_set_julian(fsl_zip_writer * const z, - double rDate); +FSL_EXPORT void fsl_zip_timestamp_set_julian(fsl_zip_writer *z, double rDate); /** Set z's date and time from a Unix Epoch time. Results are undefined if !z. Results will be invalid if rDate is negative. The timestamp is applied to all fsl_zip_file_add() operations until it is re-set. */ -FSL_EXPORT void fsl_zip_timestamp_set_unix(fsl_zip_writer * const z, - fsl_time_t epochTime); +FSL_EXPORT void fsl_zip_timestamp_set_unix(fsl_zip_writer *z, fsl_time_t epochTime); /** State for the fsl_timer_xxx() family of functions. @see fsl_timer_start() @@ -5929,13 +5922,10 @@ }; typedef enum fsl_configset_e fsl_configset_e; /** Runtime-configurable flags for a fsl_cx instance. - - @see fsl_cx_flag_set() - @see fsl_cx_flags_get() */ enum fsl_cx_flags_e { /** The "no flags" value. Guaranteed to be 0 and this is the only entry in this enum which is guaranteed to have a stable value. @@ -5966,32 +5956,25 @@ FSL_CX_F_ALLOW_WINDOWS_RESERVED_NAMES = 0x04, /** If on (the default) then an internal cache will be used for artifact loading to speed up operations which do lots of that. - Disabling this will save memory but may hurt performance for + Disabling this will save memory but will hurt performance badly for certain operations. */ FSL_CX_F_MANIFEST_CACHE = 0x08, -/** - If on (the default) then fsl_content_get() will use an internal - cache to speed up loading of repeatedly-fetched artifacts. - Disabling this can be costly. -*/ -FSL_CX_F_BLOB_CACHE = 0x10, - /** Internal use only to prevent duplicate initialization of some bits. */ FSL_CX_F_IS_OPENING_CKOUT = 0x100, /** Default flags for all fsl_cx instances. */ -FSL_CX_F_DEFAULTS = FSL_CX_F_MANIFEST_CACHE | FSL_CX_F_BLOB_CACHE +FSL_CX_F_DEFAULTS = FSL_CX_F_MANIFEST_CACHE }; typedef enum fsl_cx_flags_e fsl_cx_flags_e; /** @@ -6268,16 +6251,10 @@ Triggered by some diff APIs to indicate that only whitespace changes we found and the diff was requested to ignore whitespace. */ FSL_RC_DIFF_WS_ONLY, -/** - Intended to be used with fsl_cx_interrupt() by signal handlers - and UI threads. -*/ -FSL_RC_INTERRUPTED, - /** Must be the final entry in the enum. Used for creating client-side result codes which are guaranteed to live outside of this one's range. */ @@ -6499,30 +6476,26 @@ or output channel. This is a no-op if !f and is effectively a no-op if f has no state to destruct. */ -FSL_EXPORT void fsl_cx_finalize( fsl_cx * const f ); +FSL_EXPORT void fsl_cx_finalize( fsl_cx * f ); /** Sets or unsets one or more option flags on the given fossil context. flags is the flag or a bitmask of flags to set (from the fsl_cx_flags_e enum). If enable is true the flag(s) is (are) set, else it (they) is (are) unset. Returns the _previous_ set of flags (that is, the state they were in before this call was made). - - @see fsl_cx_flags_get() */ -FSL_EXPORT int fsl_cx_flag_set( fsl_cx * const f, int flags, bool enable ); +FSL_EXPORT int fsl_cx_flag_set( fsl_cx * f, int flags, bool enable ); /** Returns f's flags. - - @see fsl_cx_flag_set() */ -FSL_EXPORT int fsl_cx_flags_get( fsl_cx const * const f ); +FSL_EXPORT int fsl_cx_flags_get( fsl_cx * f ); /** Sets the Fossil error state to the given error code and fsl_appendf()-style format string/arguments. On success it returns the code parameter. It does not return 0 unless code is @@ -6538,23 +6511,23 @@ is real). As a special case, if code is 0 (the non-error value) then fmt is ignored and any error state is cleared. */ -FSL_EXPORT int fsl_cx_err_set( fsl_cx * const f, int code, char const * fmt, ... ); +FSL_EXPORT int fsl_cx_err_set( fsl_cx * f, int code, char const * fmt, ... ); /** va_list counterpart to fsl_cx_err_set(). */ -FSL_EXPORT int fsl_cx_err_setv( fsl_cx * const f, int code, char const * fmt, +FSL_EXPORT int fsl_cx_err_setv( fsl_cx * f, int code, char const * fmt, va_list args ); /** Fetches the error state from f. See fsl_error_get() for the semantics of the parameters and return value. */ -FSL_EXPORT int fsl_cx_err_get( fsl_cx * const f, char const ** str, fsl_size_t * len ); +FSL_EXPORT int fsl_cx_err_get( fsl_cx * f, char const ** str, fsl_size_t * len ); /** Returns f's error state object. This pointer is guaranteed by the API to be stable until f is finalized, but its contents are modified my routines as part of the error reporting process. @@ -6563,29 +6536,28 @@ */ FSL_EXPORT fsl_error const * fsl_cx_err_get_e(fsl_cx const * f); /** Resets's f's error state, basically equivalent to - fsl_cx_err_set(f,0,NULL). This may be necessary for apps if they - rely on looking at fsl_cx_err_get() at the end of their - app/routine, because error state survives until it is cleared, even - if the error held there was caught and recovered. This function - might keep error string memory around for re-use later on. - - This does NOT reset the fsl_cx_interrupted() flag! + fsl_cx_err_set(f,0,NULL). Is a no-op if f is NULL. This may be + necessary for apps if they rely on looking at fsl_cx_err_get() + at the end of their app/routine, because error state survives + until it is cleared, even if the error held there was caught and + recovered. This function might keep error string memory around + for re-use later on. */ -FSL_EXPORT void fsl_cx_err_reset(fsl_cx * const f); +FSL_EXPORT void fsl_cx_err_reset(fsl_cx * f); /** Replaces f's error state with the contents of err, taking over any memory owned by err (but not err itself). Returns the new error state code (the value of err->code before this call) on success. The only error case is if !f (FSL_RC_MISUSE). If err is NULL then f's error state is cleared and 0 is returned. err's error state is cleared by this call. */ -FSL_EXPORT int fsl_cx_err_set_e( fsl_cx * const f, fsl_error * const err ); +FSL_EXPORT int fsl_cx_err_set_e( fsl_cx * f, fsl_error * err ); /** If f has error state then it outputs its error state to its output channel and returns the result of fsl_output(). Returns FSL_RC_MISUSE if !f, 0 if f has no error state our output of the @@ -6596,20 +6568,17 @@ error reporting mechanism for a full-fledged application. */ FSL_EXPORT int fsl_cx_err_report( fsl_cx * const f, bool addNewline ); /** - Unconditionally Moves db->error's state into f (without requiring - any allocation). If db is NULL then f's primary db connection is - used. Returns FSL_RC_MISUSE if (!db && f-is-not-opened), with the - caveat f _always_ has a db connection under the current connection - architecture. On success it returns f's new error code (which may be - 0). - - The main purpose of this function is to propagate db-level errors - up to higher-level code which deals directly with the f object but - not the underlying db(s). + Unconditionally Moves db->error's state into f. If db is NULL then + f's primary db connection is used. Returns FSL_RC_MISUSE if !f or + (!db && f-is-not-opened). On success it returns f's new error code. + + The main purpose of this function is to propagate db-level + errors up to higher-level code which deals directly with the f + object but not the underlying db(s). @see fsl_cx_uplift_db_error2() */ FSL_EXPORT int fsl_cx_uplift_db_error( fsl_cx * const f, fsl_db * db ); @@ -6618,15 +6587,10 @@ fsl_cx_uplift_db_error() and returns its result, else returns rc. If db is NULL, f's main db connection is used. It is intended to be called immediately after calling a db operation which might have failed, and passed that operation's result. - As a special case, if rc is FSL_RC_OOM, this function has no side - effects and returns rc. The intention of that is to keep a - propagated db-level error (which may perhaps be stale by the time - this is called) from hiding an OOM error. - Results are undefined if db is NULL and f has no main db connection. */ FSL_EXPORT int fsl_cx_uplift_db_error2(fsl_cx * const f, fsl_db * db, int rc); @@ -6718,17 +6682,13 @@ */ FSL_EXPORT int fsl_repo_open( fsl_cx * const f, char const * repoDbFile/*, char readOnlyCurrentlyIgnored*/ ); /** If fsl_repo_open_xxx() has been used to open a respository db, this - call closes that db. - - Returns 0 on success or if no config db is opened. It may propagate - an error from the db layer if closing/detaching the db - fails. Returns FSL_RC_MISUSE if f has any transactions pending or - if f still has a checkout opened (a checkout db is only valid in - conjunction with its repository db). + call closes that db and returns 0. Returns FSL_RC_NOT_FOUND if f + has not opened a repository (that can normally be ignored but is + provided for completeness's sake). If a repository is opened "indirectly" via fsl_ckout_open_dir() then attempting to close it using this function will result in FSL_RC_MISUSE and f's error state will hold a description of the problem (the checkout must be closed before closing its @@ -6747,42 +6707,26 @@ Returns 0 on success, FSL_RC_MISUSE if f is NULL, FSL_RC_OOM if copying of the userName fails. Example usage: ``` - char * u = fsl_user_name_guess(); + char * u = fsl_guess_user_name(); int rc = fsl_cx_user_set(f, u); fsl_free(u); ``` (Sorry about the extra string copy there, but adding a function which passes ownership of the name string seems like overkill.) - - @see fsl_cx_user_guess() -*/ -FSL_EXPORT int fsl_cx_user_set( fsl_cx * const f, char const * userName ); - -/** - If f has a user name set (via fsl_cx_user_set()) then this function - returns that value. If none has been set, this tries to guess one, - as per fsl_user_name_guess(), and then assign it as f's current - user name. Returns NULL only on allocation error or if an - environment setup error prevents detection of the user name. - - The returned bytes are owned by f and will be invalidated by - any future calls to fsl_cx_user_set(). -*/ -FSL_EXPORT char const * fsl_cx_user_guess(fsl_cx * const f); +*/ +FSL_EXPORT int fsl_cx_user_set( fsl_cx * f, char const * userName ); /** Returns the name set by fsl_cx_user_set(), or NULL if f has no - default user name set. The returned bytes are owned by f and will - be invalidated by any future calls to fsl_cx_user_set(). - - @see fsl_cx_user_guess() + default user name set. The returned bytes are owned by f and may + be invalidated by any call to fsl_cx_user_set(). */ -FSL_EXPORT char const * fsl_cx_user_get( fsl_cx const * const f ); +FSL_EXPORT char const * fsl_cx_user_get( fsl_cx const * f ); /** Configuration parameters for fsl_repo_create(). Always copy-construct these from fsl_repo_create_opt_empty resp. fsl_repo_create_opt_empty_m in order to ensure proper @@ -7035,28 +6979,24 @@ fsl_buffer * const pOut ); /** If fsl_ckout_open_dir() (or similar) has been used to open a - checkout db, this call closes that db. - - Returns 0 on success or if no config db is opened. It may propagate - an error from the db layer if closing/detaching the db - fails. Returns FSL_RC_MISUSE if f has any transactions pending. + checkout db, this call closes that db and returns 0. Returns + FSL_RC_MISUSE if f has any transactions pending, FSL_RC_NOT_FOUND + if f has not opened a checkout (which can safely be ignored and + does not update f's error state). This also closes the repository which was implicitly opened for the checkout. */ FSL_EXPORT int fsl_ckout_close( fsl_cx * const f ); /** Attempts to close any opened databases (repo/checkout/config). This will fail if any transactions are pending. Any databases which are - already closed are silently skipped. This will fail if any cached - statements are currently active for the being-closed - db(s). "Active" means that fsl_db_prepare_cached() was used without - a corresponding call to fsl_stmt_cached_yield(). + already closed are silently skipped. */ FSL_EXPORT int fsl_cx_close_dbs( fsl_cx * const f ); /** If f is not NULL and has a checkout db opened then this function @@ -7238,54 +7178,34 @@ @see fsl_needs_ckout() */ FSL_EXPORT bool fsl_cx_has_ckout(fsl_cx const * const f ); /** - Opens the given database file as f's configuration database. - - If f already has a config database opened then: - - 1) If passed a NULL dbName or dbName is an empty string then this - function returns without side-effects. - - 2) If passed a non-NULL/non-empty dbName, any existing config db is - closed before opening the named one. The database is created and - populated with an initial schema if needed. + Opens the given database file as f's configuration database. If f + already has a config database opened, it is closed before opening + the new one. The database is created and populated with an + initial schema if needed. If dbName is NULL or empty then it uses a default db name, - "probably" under the user's home directory (see - fsl_config_global_preferred_name()). To get the name of the - database after it has been opened/attached, use + "probably" under the user's home directory. To get the name of + the database after it has been opened/attached, use fsl_cx_db_file_config(). - Results are undefined if f is NULL or not properly initialized. - - TODO(?): strongly consider supporting non-attached + TODO: strongly consider supporting non-attached (i.e. sqlite3_open()'d) use of the config db. Comments in fossil(1) suggest that it is possible to lock the config db for other apps - when it is attached to a long-running op by a fossil process. That - change is easier said than done, as it affects many different - functions and rules out any SQL JOINs against the rest of the - repository state (whether any such joins are needed is as yet - unknown). + when it is attached to a long-running op by a fossil process. @see fsl_cx_db_config() @see fsl_config_close() - @see fsl_config_global_preferred_name() */ FSL_EXPORT int fsl_config_open( fsl_cx * const f, char const * dbName ); /** Closes/detaches the database connection opened by - fsl_config_open(). Returns 0 on success or if no config - db is opened. It may propagate an error from the db layer - if closing/detaching the db fails. - - ACHTUNG: the config handle cannot be closed if any active - (stepped-but-not-reset) statements are opened on any of f's db - handles because the attached config db will be locked for the - duration of such statements. In such cases, this routine WILL FAIL. + fsl_config_open(). Returns 0 on succes, FSL_RC_MISUSE if !f, + FSL_RC_NOT_FOUND if no config db connection is opened/attached. @see fsl_cx_db_config() @see fsl_config_open() */ FSL_EXPORT int fsl_config_close( fsl_cx * const f ); @@ -7313,26 +7233,10 @@ va_list counterpart of fsl_cx_prepare(). */ FSL_EXPORT int fsl_cx_preparev( fsl_cx * const f, fsl_stmt * const tgt, char const * sql, va_list args ); -/** - Convenience form of fsl_db_prepare_cached() which uses f's main db. - Returns 0 on success. On preparation error, any db error state is - uplifted from the db object to the fsl_cx object. Returns - FSL_RC_MISUSE if f has no db opened (should never happen) or !sql, - FSL_RC_RANGE if !*sql. -*/ -FSL_EXPORT int fsl_cx_prepare_cached( fsl_cx * const f, fsl_stmt ** tgt, - char const * sql, ... ); - -/** - va_list counterpart of fsl_cx_prepare_cached(). -*/ -FSL_EXPORT int fsl_cx_preparev_cached( fsl_cx * const f, fsl_stmt ** tgt, - char const * sql, va_list args ); - /** Convenience form of fsl_db_exec() which uses f's main db handle. Returns 0 on success. On statement preparation or execution error, the db's error state is uplifted into f and that result is returned. Returns FSL_RC_MISUSE, without additional error @@ -7827,75 +7731,13 @@ confirmations. @see fsl_confirm_callback_f @see fsl_cx_confirmer() */ -FSL_EXPORT int fsl_cx_confirm(fsl_cx * const f, fsl_confirm_detail const * detail, +FSL_EXPORT int fsl_cx_confirm(fsl_cx *f, fsl_confirm_detail const * detail, fsl_confirm_response *outAnswer); -/** - Sets f's is-interrupted flag and, if the 3rd argument is not NULL, - its error state. The is-interrupted flag is separate from f's - normal error state and is _not_ cleared by fsl_cx_err_reset(). To - clear the interrupted flag, call this function with values of 0 and - NULL for the 2nd and 3rd arguments, respective. This flag is _not_ - fetched by fsl_cx_err_get() but: - - 1) If this function is passed a non-NULL 3rd argument, then the - normal error state, as well as the is-interrupted flag, is updated - and can be fetched normally via fsl_cx_err_get(). However... - - 2) It is possible for any error message provided via this routine - to be overwritten or reset by another routine before the - interrupted flag can be acted upon, whereas the interrupted flag - itself can only be modified by this routine. - - Returns its 2nd argument on success or FSL_RC_OOM if given a - formatted string and allocation of it fails. In either case, the - interrupted flag, as returned by fsl_cx_interrupted(), is _always_ - assigned to the passed-in code. - - If passed a code of 0, the is-interrupted flag is reset but the - general error state is not modified. - - Results are undefined if this function is called twice concurrently - with the same fsl_cx object. i.e. all calls for a given fsl_cx must - come from a single thread. Results are also undefined if it is - called while f is in its finalization phase (typically during - application shutdown). - - ACHTUNG: this is new as of 2021-11-18 and is not yet widely honored - within the API. - - Library maintenance notes: - - - Long-running actions which honor this flag should, if it is set, - clear it before returning its error code. Also, they should prefer - to pass on non-interruption errors if one has been set set, in - addition to clearing the interruption flag. Only routines which - honor this flag, or top-most routines in the application, should - ever clear this flag. - - @see fsl_cx_interrupted() - @see fsl_cx_interruptv() -*/ -FSL_EXPORT int fsl_cx_interrupt(fsl_cx * const f, int code, - const char * fmt, ...); - -/** - The va_list counterpart of fsl_cx_interrupt(). -*/ -FSL_EXPORT int fsl_cx_interruptv(fsl_cx * const f, int code, char const * fmt, va_list args); - -/** - If f's is-interrupted flag is set, this function returns its - value. Note that there is inherently a race condition when calling - fsl_cx_interrupt() (to set the flag) from another thread (e.g. a - UI thread while showing a progress indicator). -*/ -FSL_EXPORT int fsl_cx_interrupted(fsl_cx const * const f); - #if 0 /** DO NOT USE - not yet tested and ready. Returns the result of either localtime(clock) or gmtime(clock), @@ -8368,21 +8210,10 @@ The number of times this statement has fetched a row via fsl_stmt_step(). */ fsl_size_t rowCount; - /** - Describes the database(s) used by this statement handle, in the - form of a bitmask of fsl_dbrole_e values. This is a bit of a - kludge used to allow the internals to flush cached statements - from the fossil global config db when detaching that database. - Code which requires this to be set must set it itself and must - set it correctly. Hypothetically, no client-level code requires - it but _some_ libfossil-internal code does. - */ - int role; - /** Internal state flags. */ short flags; @@ -8414,11 +8245,10 @@ NULL/*stmt*/, \ fsl_buffer_empty_m/*sql*/, \ 0/*colCount*/, \ 0/*paramCount*/, \ 0/*rowCount*/, \ - 0/*role*/, \ 0/*flags*/, \ 0/*cachedHits*/, \ NULL/*next*/, \ NULL/*allocStamp*/ \ } @@ -9759,11 +9589,11 @@ 1) It calls fsl_stmt_reset() before binding the arguments. 2) If binding succeeds then it steps the given statement a single time. - 3) If the result is _NOT_ FSL_RC_STEP_ROW then it also resets the + 3) If the result is NOT FSL_RC_STEP_ROW then it also resets the statement before returning. It does not do so for FSL_RC_STEP_ROW because doing so would remove the fetched columns (and this is why it resets in step (1)). Returns 0 if stepping results in FSL_RC_STEP_DONE, FSL_RC_STEP_ROW @@ -10517,11 +10347,11 @@ properly traversing the file list in delta manifests. Maintenance notes: internal updates to this member are the only reason some of the deck APIs require a non-const deck. This type needs to be signed for compatibility with some of the older - algos, e.g. fsl__deck_F_seek_base(). + algos, e.g. fsl_deck_F_seek_base(). */ int32_t cursor; /** Internal flags. Never, ever modify these from client code. */ @@ -11522,17 +11352,12 @@ /** Must be called to initialize a newly-created/allocated deck instance. This function clears out all contents of the d parameter except for its (f, type, allocStamp) members, sets its (f, type) members, and leaves d->allocStamp intact. - - Note that, prior to calling this, the deck _must_ have been cleanly - initialized via copying from fsl_deck_empty or (depending on the - context) fsl_deck_empty_m or results are undefined. */ -FSL_EXPORT void fsl_deck_init( fsl_cx * const cx, fsl_deck * const d, - fsl_satype_e type ); +FSL_EXPORT void fsl_deck_init( fsl_cx * cx, fsl_deck * d, fsl_satype_e type ); /** Returns true if d contains data for all _required_ cards, as determined by the value of d->type, else returns false. It returns false if d->type==FSL_SATYPE_ANY, as that is a placeholder value @@ -11703,14 +11528,51 @@ Maintenance reminder: this function also does a very small bit of artifact-type-specific processing. @see fsl_deck_output() - @see fsl__content_put_ex() + @see fsl_content_put_ex() */ FSL_EXPORT int fsl_deck_save( fsl_deck * const d, bool isPrivate ); +/** + This starts a transaction (possibly nested) on the repository db + and initializes some temporary db state needed for the + crosslinking certain artifact types. It "should" (see below) be + called at the start of the crosslinking process. Crosslinking + *can* work without this but certain steps for certain (subject to + change) artifact types will be skipped, possibly leading to + unexpected timeline data or similar funkiness. No permanent + SCM-relevant state will be missing, but the timeline might not be + updated and tickets might not be fully processed. This should be + used before crosslinking any artifact types, but will only have + significant side effects for certain (subject to change) types. + + Returns 0 on success. + + If this function succeeds, the caller is OBLIGATED to either call + fsl_crosslink_end() or fsl_db_transaction_rollback(), depending + on whether the work done after this call succeeds + resp. fails. This process may install temporary tables and/or + triggers, so failing to call one or the other of those will result + in misbehavior. + + @see fsl_deck_crosslink() +*/ +int fsl_crosslink_begin(fsl_cx * f); + +/** + Must not be called unless fsl_crosslink_begin() has + succeeded. This performs crosslink post-processing on certain + artifact types and cleans up any temporary db state initialized by + fsl_crosslink_begin(). + + Returns 0 on success. On error it initiates (or propagates) a + rollback for the current transaction. +*/ +int fsl_crosslink_end(fsl_cx * f); + /** Parses src as Control Artifact content and populates d with it. d will be cleaned up before parsing if it has any contents, retaining its d->f member (which must be non-NULL for @@ -11727,24 +11589,22 @@ src->mem will be NULL, and all other members will be reset to their default state. This function only takes over the contents if it decides to implement certain memory optimizations. Ownership of src itself is never changed by this function, only - the ownership of its contents. On success, this function always - clears the buffer's contents, possibly (but not necessarily) - transfering ownership of them to the deck. - - In any case, the content of the source buffer is (normally) - modified by this function because (A) that simplifies tokenization - greatly, (B) saves us having to make another copy to work on, (C) - the original implementation did it that way, (D) because in + (possibly!) the ownership of its contents. + + In any case, the content of the source buffer is modified by + this function because (A) that simplifies tokenization greatly, + (B) saves us having to make another copy to work on, (C) the + original implementation did it that way, (D) because in historical use the source is normally thrown away after parsing, anyway, and (E) in combination with taking ownership of src's - contents it allows us to optimize away some memory allocations by - re-using the internal memory of the buffer. This function never - changes src's size, but it mutilates its contents (injecting NUL - bytes as token delimiters). + contents it allows us to optimize away some memory allocations + by re-using the internal memory of the buffer. This function + never changes src's size, but it mutilates its contents + (injecting NUL bytes as token delimiters). If d->type is _not_ FSL_SATYPE_ANY when this is called, then this function requires that the input to be of that type. We can fail relatively quickly in that case, and this can be used to save some downstream code some work. Note that the initial type @@ -11751,37 +11611,27 @@ for decks created using fsl_deck_malloc() or copy-initialized from ::fsl_deck_empty is FSL_SATYPE_ANY, so normally clients do not need to set this (unless they want to, as a small optimization). - On success it returns 0 and d will be updated with the state from - the input artifact and the contents of the source buffer will - either be cleared or taken over by d. (Ideally, outputing d via - fsl_deck_output() will produce a lossless copy of the original, but - timestamp granularity might prevent that.) + On success it returns 0 and d will be updated with the state + from the input artifact. (Ideally, outputing d via + fsl_deck_output() will produce a lossless copy of the original.) On error, if there is error information to propagate beyond the - result code then it is stored in d->f (if that is not NULL), else - in d->error. Whether or not such error info is propagated depends - on the type of error, but anything more trivial than invalid - arguments will be noted there. On error, the source buffer's - contents _might_, depending on which phase the error happened, be - left in place but may have been modified by the parsing process. - If the error happens after _certain_ parsing steps then the deck - will be required to take over the buffer's memory in order to keep - memory management sane. On error, clients should always eventually - pass the source buffer to fsl_buffer_clear(). On success its - contents are guaranteed to have been cleared or or transfered to - the destination deck when this function returns. + result code then it is stored in d->f (if that is not NULL), + else in d->error. Whether or not such error info is propagated + depends on the type of error, but anything more trivial than + invalid arguments will be noted there. d might be partially populated on error, so regardless of success or failure, the client must eventually pass d to fsl_deck_finalize() to free its memory. Error result codes include: - - FSL_RC_MISUSE if any pointer argument is NULL or d->f is NULL. + - FSL_RC_MISUSE if any pointer argument is NULL. - FSL_RC_SYNTAX on syntax errors. - FSL_RC_CONSISTENCY if validation of a Z-card fails. @@ -11801,14 +11651,14 @@ This variant of fsl_deck_parse() works identically to that function except for the 3rd argument. If you happen to know the _correct_ RID for the deck being parsed, pass it as the rid argument, else pass 0. A negative value will - result in a FSL_RC_RANGE error. This value is (or may be) only - used as an optimization in this function and/or downstream - functions. Passing a positive value will cause d->f to do a cache - lookup which may avoid it having to parse the deck at all. + result in a FSL_RC_RANGE error. This value is (or will be) only + used as an optimization in other places. Passing a positive value + has no effect on how the content is parsed or on the result - it + only affects internal details/optimizations. */ FSL_EXPORT int fsl_deck_parse2(fsl_deck * const d, fsl_buffer * const src, fsl_id_t rid); /** Quickly determines whether the content held by the given buffer @@ -11818,11 +11668,11 @@ given buffer's contents are, with 100% certainty, *not* a structural artifact. If it returns true then they *might* be, but being 100% certain requires passing the contents to fsl_deck_parse() to fully parse them. */ -FSL_EXPORT bool fsl_might_be_artifact(fsl_buffer const * const src); +FSL_EXPORT bool fsl_might_be_artifact(fsl_buffer const * src); /** Loads the content from given rid and tries to parse it as a Fossil artifact. If rid==0 the current checkout (if opened) is used. (Trivia: there can never be a checkout with rid==0 but @@ -11954,11 +11804,11 @@ can be flagged as "being crosslinked" and fail mutation operations such as card adders/setters. @see fsl_xlink_listener() */ -typedef int (*fsl_deck_xlink_f)(fsl_deck * const d, void * state); +typedef int (*fsl_deck_xlink_f)(fsl_deck * d, void * state); /** A type for holding a callback/state pair for manifest crosslinking callbacks. */ @@ -12006,11 +11856,11 @@ fsl_xlinker * fsl_xlinker_by_name( fsl_cx * f, char const * name ); /** Adds the given function as a "crosslink callback" for the given Fossil context. The callback is called at the end of a - successfull fsl__deck_crosslink() operation and provides a way + successfull fsl_deck_crosslink() operation and provides a way for the client to perform their own work based on the app having crosslinked an artifact. Crosslinking happens when artifacts are saved or upon a rebuild operation. This function returns 0 on success, non-0 on error. Behaviour is @@ -12310,11 +12160,11 @@ pageName is the name of the page to update or create. b contains the content for the page. userName specifies the user name to apply to the change. If NULL - or empty then fsl_cx_user_get() or fsl_user_name_guess() are + or empty then fsl_cx_user_get() or fsl_guess_user_name() are used (in that order) to determine the name. mimeType specifies the mime type for the content (may be NULL). Mime type names supported directly by fossil(1) include (as of this writing): text/x-fossil-wiki, text/x-markdown, @@ -12910,11 +12760,11 @@ tagName is the name of the tag. Must not be NULL/empty. tagValue is the optional value for the tag. May be NULL. userName is the user's name to apply to the artifact. May not be - empty/NULL. Use fsl_user_name_guess() to try to figure out a + empty/NULL. Use fsl_guess_user_name() to try to figure out a proper user name based on the environment. See also: fsl_cx_user_get(), but note that the application must first use fsl_cx_user_set() to set a context's user name. mtime is the Julian Day timestamp for the new artifact. Pass a @@ -12930,11 +12780,11 @@ If the artifact being tagged is private, the new tag is also marked as private. */ -FSL_EXPORT int fsl_tag_an_rid( fsl_cx * const f, fsl_tagtype_e tagType, +FSL_EXPORT int fsl_tag_an_rid( fsl_cx * f, fsl_tagtype_e tagType, fsl_id_t artifactRidToTag, char const * tagName, char const * tagValue, char const * userName, double mtime, fsl_id_t * newId ); /** @@ -12943,11 +12793,11 @@ record is found and create is true (non-0) then a tag is created and its entry id is returned. Returns 0 if it finds no entry, a negative value on error. On db-level error, f's error state is updated. */ -FSL_EXPORT fsl_id_t fsl_tag_id( fsl_cx * const f, char const * tag, bool create ); +FSL_EXPORT fsl_id_t fsl_tag_id( fsl_cx * f, char const * tag, bool create ); /** Returns true if the checkin with the given rid is a leaf, false if not. Returns false if f has no repo db opened, the query fails @@ -13179,11 +13029,11 @@ That said: this routine has no way of associating and older version (if any) of the same content with this newly-imported version, and therefore cannot delta-compress the older version. Maintenance reminder: this is basically just a glorified form of - the internal fsl__content_put(). Interestingly, fsl__content_put() + the internal fsl_content_put(). Interestingly, fsl_content_put() always sets content to public (by default - the f object may override that later). It is not yet clear whether this routine needs to have a flag to set the blob private or not. Generally speaking, privacy is applied to fossil artifacts, as opposed to content blobs. @@ -13886,178 +13736,10 @@ NULL. */ FSL_EXPORT int fsl_annotate( fsl_cx * const f, fsl_annotate_opt const * const opt ); - -/** Convenience typedef and forward decl. */ -typedef struct fsl_rebuild_opt fsl_rebuild_opt; - -/** - State for use with the fsl_rebuild_f() callback type. -*/ -struct fsl_rebuild_step { - /** - Fossil context for which this rebuild is being performed. - */ - fsl_cx * f; - /** - The options object used for this invocation of fsl_repo_rebuild(). - */ - fsl_rebuild_opt const * opt; - /** - An _approximate_ upper bound on the number of files - fsl_repo_rebuild() will process. This number is very likely - somewhat larger than the number of times which opt->callback() - will be called, but it is close enough to give some indication of - how far along a rebuild is. - */ - uint32_t artifactCount; - /** - One-based counter of total artifacts processed so far. After - rebuilding is finished, opt->callback will be called one final - time with this value set to 0. The callback may use the value 0 - to recognize that this is post-rebuild step and finalize any - output or whatever it wants to do. - */ - uint32_t stepNumber; - - /** - The `blob.rid` value of the just-processed blob. If stepNumber is 0, - this will be 0. - */ - fsl_id_t rid; - - /** - The size of the just-processed blob, -1 if it is a phantom - blob, or 0 if this->stepNumber is 0. - */ - fsl_int_t blobSize; - - /** - Set to FSL_SATYPE_INVALID if the just-processed blob - is _not_ a fossil artifact, else false. This will never be - set to FSL_SATYPE_ANY. - */ - fsl_satype_e artifactType; -}; -/** Convenience typedef. */ -typedef struct fsl_rebuild_step fsl_rebuild_step; - -/** Initialized-with-defaults fsl_rebuild_step structure, intended for - const-copy initialization. */ -#define fsl_rebuild_step_empty_m {NULL,NULL,0,0,-1,false} - -/** Initialized-with-defaults fsl_rebuild_step structure, intended for - non-const copy initialization. */ -FSL_EXPORT const fsl_rebuild_step fsl_rebuild_step_empty; - -/** - Callback for use with fsl_repo_rebuild() in order to report - progress. It must return 0 on success, and any non-0 results will - abort the rebuild and be propagated back to the caller. - - Each time this is called, state->stepNumber will be incremented, starting - at 1. After rebuilding is complete, it is called with a stepNumber - of 0 to give the callback a chance to do any final bookkeeping or output - cleanup or whatever. -*/ -typedef int (*fsl_rebuild_f)(fsl_rebuild_step const * const state); - -/** - Options for the rebuild process. -*/ -struct fsl_rebuild_opt { - /** - Scan artifacts in a random order (generally only of use in testing - the library's code). - - NOT YET IMPLEMENTED. - */ - bool randomize; - /** - True if clusters should be created. - - NOT YET IMPLEMENTED. - */ - bool clustering; - /** - If true, the transaction started by the rebuild process will end - in a rollback even on success. In that case, if a transaction is - started before the rebuild is initiated, it will be left in the - rolling-back state after rebuild completes. - */ - bool dryRun; - /** - If not NULL, this gets called after each blob table entry is - processed so that the client can provide some form of feedback - about the rebuild progress. - */ - fsl_rebuild_f callback; - /** - Optional state for the callback function. - */ - void * callbackState; -}; - -/** Initialized-with-defaults fsl_rebuild_opt structure, intended for - const-copy initialization. */ -#define fsl_rebuild_opt_empty_m {false,false,false} - -/** Initialized-with-defaults fsl_rebuild_opt structure, intended for - non-const copy initialization. */ -FSL_EXPORT const fsl_rebuild_opt fsl_rebuild_opt_empty; - - -/** - "Rebuilds" the current repository database. This involves _at least_ - the following: - - - DROPPING all transient repository tables. ALL tables in the db - which do not specifically belong to fossil and do not start with - the name `fx_` _will be dropped_. - - - Recreating all transient tables from immutable state in the - database. - - - Updating the schema, if needed, from "older" versions of the - fossil schema to the "current" one. This library does not - currently check for updates which need to be made to a decade-old - schema, however! That is, schema changes which were introduced - 10+ years ago are not currently addressed by this library - because, frankly, it's not expected that anyone using this - library will be using it with such ancient repositories (and - those who do can rebuild it once with fossil(1) to update it). - - - Crosslinking all blobs which appear to be artifacts (meaning they - can be parsed as such). Any crosslink handlers registered via - fsl_xlink_listener() will be called. - - - Returns 0 on success, non-0 on error (with any number of potential - result codes from the db or crosslinking layers). It runs in a - transaction and will roll back on non-0, so it "shouldn't" leave a - mess on error. If opt->dryRun is true then it also rolls back the - transaction but it is not treated as an error. - - During the rebuild process opt->callback will, if it is not NULL, - be called to provide feedback on its progress. - - This operation honors interruption via fsl_cx_interrupt() but, due - to intricacies of timing, it's possible that a triggered interrupt - gets trumped by another error which happens while the check for the - interrupt flag is pending. In both cases this function could return - a non-0 code, though. - - @todo As of this writing, this library cannot crosslink ticket - artifacts, so this routine does _not_ drop and rebuild the - ticket-related tables. If the FSL_CX_F_SKIP_UNKNOWN_CROSSLINKS - flag is _not_ set on f, this routine _will fail_ if it encounters - any ticket artifacts. -*/ -FSL_EXPORT int fsl_repo_rebuild(fsl_cx * const f, fsl_rebuild_opt const * const opt); - #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* ORG_FOSSIL_SCM_FSL_REPO_H_INCLUDED */ @@ -14345,11 +14027,11 @@ */ char const * filename; /** An alternative to assigning this->filename is to point this->vfileIds to a bag of vfile.id values. If this member is not - NULL, fsl_ckout_unmanage() will ignore this->filename. + NULL, fsl_ckout_revert() will ignore this->filename. @see fsl_filename_to_vfile_ids() */ fsl_id_bag const * vfileIds; /** @@ -16109,11 +15791,11 @@ callback unless the reversion was the un-queuing of an ADD or REMOVE operation. Returns 0 on success, any number of non-0 results on error. */ -FSL_EXPORT int fsl_ckout_revert( fsl_cx * const f, +FSL_EXPORT int fsl_ckout_revert( fsl_cx * f, fsl_ckout_revert_opt const * opt ); /** Expects f to have an opened checkout and zName to be the name of an entry in the vfile table where vfile.vid == vid. If vid<=0 then @@ -16166,12 +15848,12 @@ because it is possible for a given name to be in both fields (in different records) at the same time. @see fsl_ckout_vfile_ids() */ -FSL_EXPORT int fsl_filename_to_vfile_ids( fsl_cx * const f, fsl_id_t vid, - fsl_id_bag * const dest, +FSL_EXPORT int fsl_filename_to_vfile_ids( fsl_cx * f, fsl_id_t vid, + fsl_id_bag * dest, char const * zName, bool changedOnly); /** This is a variant of fsl_filename_to_vfile_ids() which accepts @@ -16188,12 +15870,12 @@ Returns 0 on success, FSL_RC_MISUSE if zName is NULL or empty, FSL_RC_OOM on allocation error, FSL_RC_NOT_A_CKOUT if f has no opened checkout. */ -FSL_EXPORT int fsl_ckout_vfile_ids( fsl_cx * const f, fsl_id_t vid, - fsl_id_bag * const dest, char const * zName, +FSL_EXPORT int fsl_ckout_vfile_ids( fsl_cx * f, fsl_id_t vid, + fsl_id_bag * dest, char const * zName, bool relativeToCwd, bool changedOnly ); /** This "mostly internal" routine (re)populates f's checkout vfile @@ -16574,12 +16256,10 @@ "versioned setting" backend, which deals with non-db local files, was encapsulated into this API long after the db-related options were */ enum fsl_confdb_e { -/** Sentinel value. Not for use with public APIs. */ -FSL_CONFDB_NONE = 0, /** Signfies the global-level (per system user) configuration area. */ FSL_CONFDB_GLOBAL = 1, /** @@ -16641,45 +16321,45 @@ For FSL_CONFDB_VERSIONABLE it returns the results of fsl_cx_db(), even though there is no database-side support for versionable files (which live in files in a checkout). */ -FSL_EXPORT fsl_db * fsl_config_for_role(fsl_cx * const f, fsl_confdb_e mode); +FSL_EXPORT fsl_db * fsl_config_for_role(fsl_cx * f, fsl_confdb_e mode); /** Returns the int32 value of a property from one of f's config dbs, as specified by the mode parameter. Returns dflt if !f, f does not have the requested config db opened, no entry is found, or on db-level errors. */ -FSL_EXPORT int32_t fsl_config_get_int32( fsl_cx * const f, fsl_confdb_e mode, +FSL_EXPORT int32_t fsl_config_get_int32( fsl_cx * f, fsl_confdb_e mode, int32_t dflt, char const * key ); /** int64_t counterpart of fsl_config_get_int32(). */ -FSL_EXPORT int64_t fsl_config_get_int64( fsl_cx * const f, fsl_confdb_e mode, +FSL_EXPORT int64_t fsl_config_get_int64( fsl_cx * f, fsl_confdb_e mode, int64_t dflt, char const * key ); /** fsl_id_t counterpart of fsl_config_get_int32(). */ -FSL_EXPORT fsl_id_t fsl_config_get_id( fsl_cx * const f, fsl_confdb_e mode, +FSL_EXPORT fsl_id_t fsl_config_get_id( fsl_cx * f, fsl_confdb_e mode, fsl_id_t dflt, char const * key ); /** double counterpart of fsl_config_get_int32(). */ -FSL_EXPORT double fsl_config_get_double( fsl_cx * const f, fsl_confdb_e mode, +FSL_EXPORT double fsl_config_get_double( fsl_cx * f, fsl_confdb_e mode, double dflt, char const * key ); /** Boolean countertpart of fsl_config_get_int32(). fsl_str_bool() is used to determine the booleanness (booleanity?) of a given config option. */ -FSL_EXPORT bool fsl_config_get_bool( fsl_cx * const f, fsl_confdb_e mode, +FSL_EXPORT bool fsl_config_get_bool( fsl_cx * f, fsl_confdb_e mode, bool dflt, char const * key ); /** A convenience form of fsl_config_get_buffer(). If it finds a config entry it returns its value. If *len is not NULL then *len is @@ -16693,11 +16373,11 @@ If capturing error state is important for a given use case, use fsl_config_get_buffer() instead, which provides the same features as this one but propagates any error state. */ -FSL_EXPORT char * fsl_config_get_text( fsl_cx * const f, fsl_confdb_e mode, +FSL_EXPORT char * fsl_config_get_text( fsl_cx * f, fsl_confdb_e mode, char const * key, fsl_size_t * len ); /** The fsl_buffer-type counterpart of fsl_config_get_int32(). @@ -16733,12 +16413,12 @@ fatal unless perhaps it returns FSL_RC_OOM, in which case it likely is. @see fsl_config_has_versionable() */ -FSL_EXPORT int fsl_config_get_buffer( fsl_cx * const f, fsl_confdb_e mode, - char const * key, fsl_buffer * const b ); +FSL_EXPORT int fsl_config_get_buffer( fsl_cx * f, fsl_confdb_e mode, + char const * key, fsl_buffer * b ); /** If f has an opened checkout, this replaces b's contents (re-using any existing memory) with an absolute path to the filename for that @@ -16787,11 +16467,11 @@ fsl_mkdir_for_file() to create those components. As of this writing (2021-03-14), no such config keys have ever been used in fossil. @see fsl_config_versionable_filename() */ -FSL_EXPORT int fsl_config_set_text( fsl_cx * const f, fsl_confdb_e mode, char const * key, char const * val ); +FSL_EXPORT int fsl_config_set_text( fsl_cx * f, fsl_confdb_e mode, char const * key, char const * val ); /** The blob counterpart of fsl_config_set_text(). If len is negative then fsl_strlen(mem) is used to determine the length of the memory. @@ -16798,40 +16478,40 @@ If mem is NULL and mode is not FSL_CONFDB_VERSIONABLE then an SQL NULL is bound instead of an empty blob. For FSL_CONFDB_VERSIONABLE an empty file will be written for that case. */ -FSL_EXPORT int fsl_config_set_blob( fsl_cx * const f, fsl_confdb_e mode, char const * key, +FSL_EXPORT int fsl_config_set_blob( fsl_cx * f, fsl_confdb_e mode, char const * key, void const * mem, fsl_int_t len ); /** int32 counterpart of fsl_config_set_text(). */ -FSL_EXPORT int fsl_config_set_int32( fsl_cx * const f, fsl_confdb_e mode, +FSL_EXPORT int fsl_config_set_int32( fsl_cx * f, fsl_confdb_e mode, char const * key, int32_t val ); /** int64 counterpart of fsl_config_set_text(). */ -FSL_EXPORT int fsl_config_set_int64( fsl_cx * const f, fsl_confdb_e mode, +FSL_EXPORT int fsl_config_set_int64( fsl_cx * f, fsl_confdb_e mode, char const * key, int64_t val ); /** fsl_id_t counterpart of fsl_config_set_text(). */ -FSL_EXPORT int fsl_config_set_id( fsl_cx * const f, fsl_confdb_e mode, +FSL_EXPORT int fsl_config_set_id( fsl_cx * f, fsl_confdb_e mode, char const * key, fsl_id_t val ); /** fsl_double counterpart of fsl_config_set_text(). */ -FSL_EXPORT int fsl_config_set_double( fsl_cx * const f, fsl_confdb_e mode, +FSL_EXPORT int fsl_config_set_double( fsl_cx * f, fsl_confdb_e mode, char const * key, double val ); /** Boolean counterpart of fsl_config_set_text(). For compatibility with fossil conventions, the value will be saved in the string form "on" or "off". When mode is FSL_CONFDB_VERSIONABLE, that value will include a trailing newline. */ -FSL_EXPORT int fsl_config_set_bool( fsl_cx * const f, fsl_confdb_e mode, +FSL_EXPORT int fsl_config_set_bool( fsl_cx * f, fsl_confdb_e mode, char const * key, bool val ); /** "Unsets" (removes) the given key from the given configuration database. It is not considered to be an error if the config table does not @@ -16842,11 +16522,11 @@ checked-out copy of a versioned setting, then queue the file for removal in the next checkin, but it does not do so. It might, in the future, be changed to do so, or at least to remove the local settings file. */ -FSL_EXPORT int fsl_config_unset( fsl_cx * const f, fsl_confdb_e mode, +FSL_EXPORT int fsl_config_unset( fsl_cx * f, fsl_confdb_e mode, char const * key ); /** Begins (or recurses) a transaction on the given configuration database. Returns 0 on success, non-0 on error. On success, @@ -16862,11 +16542,11 @@ the app is only making one or two changes. @see fsl_config_transaction_end() @see fsl_db_transaction_begin() */ -FSL_EXPORT int fsl_config_transaction_begin(fsl_cx * const f, fsl_confdb_e mode); +FSL_EXPORT int fsl_config_transaction_begin(fsl_cx * f, fsl_confdb_e mode); /** Pops the transaction stack pushed by fsl_config_transaction_begin(). If rollback is true then the transaction is set roll back, otherwise it is allowed to @@ -16878,11 +16558,11 @@ any db-side error into the f object's error state. @see fsl_config_transaction_begin() @see fsl_db_transaction_end() */ -FSL_EXPORT int fsl_config_transaction_end(fsl_cx * const f, fsl_confdb_e mode, bool rollback); +FSL_EXPORT int fsl_config_transaction_end(fsl_cx * f, fsl_confdb_e mode, char rollback); /** Populates li as a glob list from the given configuration key. Uses (versionable/repo/global) config settings, in that order. It is not an error if one or more of those sources is missing - @@ -16895,15 +16575,13 @@ Returns 0 on success, but that only means that there were no errors, not that any entries were necessarily added to li. Arguably a bug: this function does not open the global config if it was not already opened, but will use it if it is opened. This - function should arbuably open and close it in that case. That said, - there are cases which prohibit closing of the config db and this - function cannot know if one of those cases is active. + function should arbuably open and close it in that case. */ -FSL_EXPORT int fsl_config_globs_load(fsl_cx * const f, fsl_list * const li, char const * key); +FSL_EXPORT int fsl_config_globs_load(fsl_cx * f, fsl_list * li, char const * key); /** Fetches the preferred name of the "global" db file for the current user by assigning it to *zOut. Returns 0 on success, in which case *zOut is updated and non-0 on error, in which case *zOut is not @@ -16930,90 +16608,16 @@ Except where listed above, this function does not check whether the file already exists or is a database. Windows: - - We need a Windows port of this routine. Currently it `#error`'s - out at compile-time on Windows. + - We need a Windows port of this routine. Currently it simply uses + the Windows home directory + "/_fossil" or "/.fossil", depending on + the build-time environment. */ FSL_EXPORT int fsl_config_global_preferred_name(char ** zOut); -/** - A variant of fsl_config_get_int32() which can search through multiple - config sources in an order specified by the caller. - - zCfg must be a NUL-terminated list of case-sensitive letters - corresponding to config sources: any of (c, r, v, g) for checkout, - repo, versionable, and global, respectively. Each source, if - available, is searched, in the order provided, until either the - given key is found in that source or no more sources are available, - in which case dflt is returned. Any missing sources are silently - skipped over (e.g. if 'c' is provided by no checkout is opened, - that is not considered an error by this function). - - Errors are silently ignored and cause dflt to be returned. - - @see fsl_configs_get_int64() - @see fsl_configs_get_id() - @see fsl_configs_get_bool() - @see fsl_configs_get_text() -*/ -FSL_EXPORT int32_t fsl_configs_get_int32(fsl_cx * const f, char const * zCfg, int32_t dflt, char const * key); - -/** - The int64_t counterpart of fsl_configs_get_int32(). -*/ -FSL_EXPORT int64_t fsl_configs_get_int64(fsl_cx * const f, char const * zCfg, int64_t dflt, char const * key); - -/** - The fsl_id_t counterpart of fsl_configs_get_int32(). -*/ -FSL_EXPORT fsl_id_t fsl_configs_get_id(fsl_cx * const f, char const * zCfg, fsl_id_t dflt, char const * key); - -/** - The bool counterpart of fsl_configs_get_int32(). -*/ -FSL_EXPORT bool fsl_configs_get_bool(fsl_cx * const f, char const * zCfg, bool dflt, char const * key); - -/** - The double counterpart of fsl_configs_get_int32(). -*/ -FSL_EXPORT double fsl_configs_get_double(fsl_cx * const f, char const * zCfg, double dflt, char const * key); - -/** - The buffer counterpart of fsl_configs_get_int32(). This variant - collects, if found, its results in the given buffer (its memory, if - any, is reused by this routine). It returns 0 if an entry is found, - FSL_RC_OOM on buffer allocation error, and FSL_RC_NOT_FOUND if no - match is found in any of the listed sources (noting that missing - sources are silently skipped). The full range of errors reported by - fsl_config_get_buffer() is not supported here: only FSL_RC_OOM and - FSL_RC_NOT_FOUND are reported. - - @see fsl_configs_get_text() -*/ -FSL_EXPORT int fsl_configs_get_buffer( fsl_cx * const f, char const *zCfg, - char const * key, fsl_buffer * const b ); - -/** - The C-string counterpart of fsl_configs_get_int32(). If the final - argument is not NULL then if a match is found then its length (in - bytes) is assigned to (*len). Returns NULL if no match is found. - - If non-NULL is returned, ownership is transfered to the caller, who - must eventually pass the value to fsl_free(). - - Note that this function silently ignores any errors, - e.g. allocation failure for the result string will be reported by - returning NULL (which is indistinguishable from it not finding a - match). - - @see fsl_configs_get_buffer() -*/ -FSL_EXPORT char * fsl_configs_get_text(fsl_cx * const f, char const * zCfg, char const * key, - fsl_size_t * len); - #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* ORG_FOSSIL_SCM_FSL_CONFDB_H_INCLUDED */ @@ -17280,261 +16884,253 @@ #if defined(__cplusplus) extern "C" { #endif -typedef struct fsl__bccache fsl__bccache; -typedef struct fsl__bccache_line fsl__bccache_line; -typedef struct fsl__pq fsl__pq; -typedef struct fsl__pq_entry fsl__pq_entry; +typedef struct fsl_acache fsl_acache; +typedef struct fsl_acache_line fsl_acache_line; +typedef struct fsl_pq fsl_pq; +typedef struct fsl_pq_entry fsl_pq_entry; /** @internal - Queue entry type for the fsl__pq class. + Queue entry type for the fsl_pq class. Potential TODO: we don't currently use the (data) member. We can probably remove it. */ -struct fsl__pq_entry { +struct fsl_pq_entry { /** RID of the entry. */ fsl_id_t id; /** Raw data associated with this entry. */ void * data; /** Priority of this element. */ double priority; }; /** @internal - Empty-initialized fsl__pq_entry structure. + Empty-initialized fsl_pq_entry structure. */ -#define fsl__pq_entry_empty_m {0,NULL,0.0} +#define fsl_pq_entry_empty_m {0,NULL,0.0} /** @internal A simple priority queue class. Instances _must_ be initialized - by copying fsl__pq_empty or fsl__pq_empty_m (depending on where + by copying fsl_pq_empty or fsl_pq_empty_m (depending on where the instance lives). */ -struct fsl__pq { +struct fsl_pq { /** Number of items allocated in this->list. */ uint16_t capacity; /** Number of items used in this->list. */ uint16_t used; /** The queue. It is kept sorted by entry->priority. */ - fsl__pq_entry * list; + fsl_pq_entry * list; }; /** @internal - Empty-initialized fsl__pq struct, intended for const-copy initialization. + Empty-initialized fsl_pq struct, intended for const-copy initialization. */ -#define fsl__pq_empty_m {0,0,NULL} +#define fsl_pq_empty_m {0,0,NULL} /** @internal - Empty-initialized fsl__pq struct, intended for copy initialization. + Empty-initialized fsl_pq struct, intended for copy initialization. */ -extern const fsl__pq fsl__pq_empty; +extern const fsl_pq fsl_pq_empty; /** @internal Clears the contents of p, freeing any memory it owns, but not freeing p. Results are undefined if !p. */ -void fsl__pq_clear(fsl__pq * p); +void fsl_pq_clear(fsl_pq * p); /** @internal Insert element e into the queue. Returns 0 on success, FSL_RC_OOM on error. Results are undefined if !p. pData may be NULL. */ -int fsl__pq_insert(fsl__pq *p, fsl_id_t e, +int fsl_pq_insert(fsl_pq *p, fsl_id_t e, double v, void *pData); /** @internal Extracts (removes) the first element from the queue (the element with the smallest value) and return its ID. Return 0 if the queue is empty. If pp is not NULL then *pp is (on success) assigned to opaquedata pointer mapped to the entry. */ -fsl_id_t fsl__pq_extract(fsl__pq *p, void **pp); +fsl_id_t fsl_pq_extract(fsl_pq *p, void **pp); /** @internal - Holds one "line" of a fsl__bccache cache. + Holds one "line" of a fsl_acache cache. */ -struct fsl__bccache_line { +struct fsl_acache_line { /** RID of the cached record. */ fsl_id_t rid; /** Age. Newer is larger. */ - fsl_uint_t age; + fsl_int_t age; /** Content of the artifact. */ fsl_buffer content; }; /** @internal - Empty-initialized fsl__bccache_line structure. + Empty-initialized fsl_acache_line structure. */ -#define fsl__bccache_line_empty_m { 0,0,fsl_buffer_empty_m } +#define fsl_acache_line_empty_m { 0,0,fsl_buffer_empty_m } /** @internal - A cache for tracking the existence of blobs while the internal - goings-on of fsl_content_get() and friends are going on. + A cache for tracking the existence of artifacts while the + internal goings-on of control artifacts are going on. - "bc" ==> blob cache. + Currently the artifact cache is unused because it costs much + more than it gives us. Once the library supports certain + operations (like rebuild and sync) caching will become more + useful. Historically fossil caches artifacts as their blob content, but libfossil will likely (at some point) to instead cache fsl_deck instances, which contain all of the same data in pre-parsed form. It cost more memory, though. That approach also precludes caching non-structural artifacts (i.e. opaque client blobs). - Potential TODO: the limits of the cache size are currently - hard-coded and changing them "by hand" won't have much effect. - We should possibly have an API to tweak these limits. + Potential TODO: the limits of the cache size are hard-coded in + fsl_acache_insert. Those really should be part of this struct. */ -struct fsl__bccache { +struct fsl_acache { /** Total amount of buffer memory (in bytes) used by cached content. This does not account for memory held by this->list. */ - unsigned szTotal; + fsl_size_t szTotal; /** Limit on the (approx.) amount of memory (in bytes) which can be taken up by the cached buffers at one time. Fossil's historical value is 50M. */ - unsigned szLimit; + fsl_size_t szLimit; /** Number of entries "used" in this->list. */ uint16_t used; /** Approximate upper limit on the number of entries in this->list. This limit may be violated slightly. - This list gets searched linearly so this number should ideally be - relatively small: 3 digits or less. Fossil's historical value is - 500. + This number should ideally be relatively small: 3 digits or less. + Fossil's historical value is 500. */ uint16_t usedLimit; /** Number of allocated slots in this->list. */ uint16_t capacity; /** Next cache counter age. Higher is newer. */ - fsl_uint_t nextAge; + fsl_int_t nextAge; /** List of cached content, ordered by age. */ - fsl__bccache_line * list; + fsl_acache_line * list; /** - RIDs of all artifacts currently in the cache. + All artifacts currently in the cache. */ fsl_id_bag inCache; /** - RIDs of known-missing content. + Cache of known-missing content. */ fsl_id_bag missing; /** - RIDs of known-existing content (not necessarily in - the cache). + Cache of of known-existing content. */ fsl_id_bag available; - /** - Metrics solely for internal use in looking for - optimizations. These are only updated by fsl_content_get(). - */ - struct { - unsigned hits; - unsigned misses; - } metrics; }; /** @internal - Empty-initialized fsl__bccache structure, intended + Empty-initialized fsl_acache structure, intended for const-copy initialization. */ -#define fsl__bccache_empty_m { \ - 0U/*szTotal*/, \ - 20000000U/*szLimit. Historical fossil value=50M*/, \ - 0U/*used*/,300U/*usedLimit. Historical fossil value=500*/,\ +#define fsl_acache_empty_m { \ + 0/*szTotal*/, \ + 20000000/*szLimit. Historical fossil value=50M*/, \ + 0/*used*/,300U/*usedLimit. Historical fossil value=500*/,\ 0/*capacity*/, \ 0/*nextAge*/,NULL/*list*/, \ fsl_id_bag_empty_m/*inCache*/, \ fsl_id_bag_empty_m/*missing*/, \ - fsl_id_bag_empty_m/*available*/, \ - {/*metrics*/ 0U/*hits*/,0U/*misses*/} \ + fsl_id_bag_empty_m/*available*/ \ } /** @internal - Empty-initialized fsl__bccache structure, intended + Empty-initialized fsl_acache structure, intended for copy initialization. */ -extern const fsl__bccache fsl__bccache_empty; +extern const fsl_acache fsl_acache_empty; /** @internal Very internal. "Manifest cache" for fsl_deck entries encountered during crosslinking. This type is intended only to be embedded in fsl_cx. The routines for managing this cache are static in deck.c: - fsl__cx_mcache_insert() and fsl__cx_mcache_search(). + fsl_cx_mcache_insert() and fsl_cx_mcache_search(). The array members in this struct MUST have the same length or results are undefined. */ -struct fsl__mcache { +struct fsl_mcache { /** Next age value. No clue how the cache will react once this overflows. */ - fsl_uint_t nextAge; + unsigned nextAge; + /** The virtual age of each deck in the cache. They get evicted + oldest first. */ + unsigned aAge[4]; /** Counts the number of cache hits. */ unsigned hits; /** Counts the number of cache misses. */ unsigned misses; - /** The virtual age of each deck in the cache. They get evicted - oldest first. */ - fsl_uint_t aAge[4]; /** Stores bitwise copies of decks. Storing a fsl_deck_malloc() deck into the cache requires bitwise-copying is contents, wiping out its contents via assignment from fsl_deck_empty, then fsl_free()'ing it (as opposed to fsl_deck_finalize(), which would attempt to clean up memory which now belongs to the cache's copy). Array sizes of 6 and 10 do not appreciably change the hit rate - compared to 4, at least not for current (2021-11-18) uses. + compared to 4, at least not for current (2021-03-26) uses. */ fsl_deck decks[4]; }; /** Convenience typedef. */ -typedef struct fsl__mcache fsl__mcache; +typedef struct fsl_mcache fsl_mcache; -/** Initialized-with-defaults fsl__mcache structure, intended for +/** Initialized-with-defaults fsl_mcache structure, intended for const-copy initialization. */ -#define fsl__mcache_empty_m { \ - 0,0,0, \ - {/*aAge*/0,0,0,0}, \ - {fsl_deck_empty_m,fsl_deck_empty_m, \ - fsl_deck_empty_m, fsl_deck_empty_m}\ +#define fsl_mcache_empty_m {\ + 0, \ + {0,0,0,0},\ + 0,0, \ + {fsl_deck_empty_m,fsl_deck_empty_m,fsl_deck_empty_m, \ + fsl_deck_empty_m} \ } -/** Initialized-with-defaults fsl__mcache structure, intended for +/** Initialized-with-defaults fsl_mcache structure, intended for non-const copy initialization. */ -extern const fsl__mcache fsl__mcache_empty; +extern const fsl_mcache fsl_mcache_empty; /* The fsl_cx class is documented in main public header. */ struct fsl_cx { /** @@ -17728,12 +17324,12 @@ Reuseable scratchpads for low-level file canonicalization buffering and whatnot. Not intended for huge content: use this->fileContent for that. This list should stay relatively short. - @see fsl__cx_scratchpad() - @see fsl__cx_scratchpad_yield() + @see fsl_cx_scratchpad() + @see fsl_cx_scratchpad_yield() */ struct { /** Strictly-internal temporary buffers we intend to reuse many times, mostly for filename canonicalization, holding hash @@ -17745,21 +17341,21 @@ If needed, the lengths of this->buf[] and this->used[] may be extended, but anything beyond 8, maybe 10, seems a bit extreme. They should only be increased if we find code paths which require it. As of this writing (2021-03-17), the peak - concurrently used was 5. In any case fsl__cx_scratchpad() fails + concurrently used was 5. In any case fsl_cx_scratchpad() fails fatally if it needs more than it has, so we won't fail to overlook such a case. */ fsl_buffer buf[8]; /** Flags telling us which of this->buf is currenly in use. */ bool used[8]; /** - A cursor _hint_ to try to speed up fsl__cx_scratchpad() by about + A cursor _hint_ to try to speed up fsl_cx_scratchpad() by about half a nanosecond, making it O(1) instead of O(small N) for the common case. */ short next; } scratchpads; @@ -17775,17 +17371,10 @@ client (see fsl_cx_flags_e). We can get rid of this and add the flags to the cache member along with the rest of them. */ int flags; - /** - Error flag which is intended to be set via signal handlers or a - UI thread to tell this context to cancel any currently - long-running operation. Not all operations honor this check. - */ - volatile int interrupted; - /** List of callbacks for deck crosslinking purposes. */ fsl_xlinker_list xlinkers; @@ -17813,12 +17402,12 @@ multiple levels of API. */ bool markPrivate; /** - True if fsl__crosslink_begin() has been called but - fsl__crosslink_end() is still pending. + True if fsl_crosslink_begin() has been called but + fsl_crosslink_end() is still pending. */ bool isCrosslinking; /** Flag indicating that only cluster control artifacts should be @@ -17892,13 +17481,13 @@ implemented. */ fsl_id_t rcvId; /** - fsl_content_get() cache. + Artifact cache used during processing of manifests. */ - fsl__bccache blobContent; + fsl_acache arty; /** Used during manifest parsing to keep track of artifacts we have seen. Whether that's really necessary or is now an unnecessary porting artifact (haha) is unclear. */ @@ -17934,11 +17523,11 @@ fsl_fstat fstat; /** Parsed-deck cache. */ - fsl__mcache mcache; + fsl_mcache mcache; /** Holds various glob lists. That said... these features are actually app-level stuff which the library itself does not resp. should not enforce. We can keep track of these for users @@ -18050,11 +17639,10 @@ {false,false,false,false,false,false}, \ 0/*next*/ \ }, \ fsl_cx_config_empty_m /*cxConfig*/, \ FSL_CX_F_DEFAULTS/*flags*/, \ - 0/*interrupted*/, \ fsl_xlinker_list_empty_m/*xlinkers*/, \ {/*cache*/ \ false/*caseInsensitive*/, \ false/*ignoreDephantomizations*/, \ false/*markPrivate*/, \ @@ -18064,18 +17652,18 @@ -1/*allowSymlinks*/, \ -1/*seenDeltaManifest*/, \ -1/*searchIndexExists*/, \ -1/*manifestSetting*/,\ 0/*rcvId*/, \ - fsl__bccache_empty_m/*blobContent*/, \ + fsl_acache_empty_m/*arty*/, \ fsl_id_bag_empty_m/*mfSeen*/, \ fsl_id_bag_empty_m/*leafCheck*/, \ fsl_id_bag_empty_m/*toVerify*/, \ 0/*mtimeManifest*/, \ NULL/*projectCode*/, \ fsl_fstat_empty_m/*fstat*/, \ - fsl__mcache_empty_m/*mcache*/, \ + fsl_mcache_empty_m/*mcache*/, \ {/*globs*/ \ fsl_list_empty_m/*ignore*/, \ fsl_list_empty_m/*binary*/, \ fsl_list_empty_m/*crnl*/ \ } \ @@ -18090,11 +17678,11 @@ } /** @internal Initialized-with-defaults fsl_cx instance. */ -extern const fsl_cx fsl_cx_empty; +FSL_EXPORT const fsl_cx fsl_cx_empty; /* TODO: int fsl_buffer_append_getenv( fsl_buffer * b, char const * env ) @@ -18108,11 +17696,11 @@ /** @internal Expires the single oldest entry in c. Returns true if it removes an item, else false. */ -bool fsl__bccache_expire_oldest(fsl__bccache * const c); +FSL_EXPORT bool fsl_acache_expire_oldest(fsl_acache * c); /** @internal Add an entry to the content cache. @@ -18120,39 +17708,29 @@ regardless of success or failure. The cache will deallocate the memory when it has finished with it. If the cache cannot add the entry due to cache-internal constraints, as opposed to allocation errors, it clears the buffer - (for consistency's sake) and returns 0. + (for consistency's sake) and returned 0. Returns 0 on success, FSL_RC_OOM on allocation error. Has undefined behaviour if !c, rid is not semantically valid, !pBlob. An empty blob is normally semantically illegal but is not strictly illegal for this cache's purposes. */ -int fsl__bccache_insert(fsl__bccache * const c, fsl_id_t rid, - fsl_buffer * const pBlob); +FSL_EXPORT int fsl_acache_insert(fsl_acache * c, fsl_id_t rid, fsl_buffer *pBlob); /** @internal Frees all memory held by c, and clears out c's state, but does not free c. Results are undefined if !c. */ -void fsl__bccache_clear(fsl__bccache * const c); +FSL_EXPORT void fsl_acache_clear(fsl_acache * c); /** @internal - Resets all bags associated with the given cache and frees all - cached buffer memory, but keeps any fsl_id_bag memory intact for - re-use. This does not reset the hit/miss metrics. -*/ -void fsl__bccache_reset(fsl__bccache * const c); - - -/** @internal - - Checks f->cache.blobContent to see if rid is available in the + Checks f->cache.arty to see if rid is available in the repository opened by f. Returns 0 if the content for the given rid is available in the repo or the cache. Returns FSL_RC_NOT_FOUND if it is not in the repo nor the cache. Returns some other non-0 code for "real @@ -18161,11 +17739,11 @@ If this function detects a loop in artifact lineage, it fails an assert() in debug builds and returns FSL_RC_CONSISTENCY in non-debug builds. That doesn't happen in real life, though. */ -int fsl__bccache_check_available(fsl_cx * const f, fsl_id_t rid); +FSL_EXPORT int fsl_acache_check_available(fsl_cx * f, fsl_id_t rid); /** @internal This is THE ONLY routine which adds content to the blob table. @@ -18218,28 +17796,28 @@ Potential TODO: we don't really need the uncompSize param - we can deduce it, if needed, based on pBlob's content. We cannot, however, know the UUID of the decompressed content unless the client passes it in to us. - @see fsl__content_put() + @see fsl_content_put() */ -int fsl__content_put_ex( fsl_cx * const f, - fsl_buffer const * pBlob, - fsl_uuid_cstr zUuid, fsl_id_t srcId, - fsl_size_t uncompSize, bool isPrivate, - fsl_id_t * outRid); +FSL_EXPORT int fsl_content_put_ex( fsl_cx * const f, + fsl_buffer const * pBlob, + fsl_uuid_cstr zUuid, fsl_id_t srcId, + fsl_size_t uncompSize, bool isPrivate, + fsl_id_t * outRid); /** @internal - Equivalent to fsl__content_put_ex(f,pBlob,NULL,0,0,0,newRid). + Equivalent to fsl_content_put_ex(f,pBlob,NULL,0,0,0,newRid). This must only be used for saving raw (non-delta) content. - @see fsl__content_put_ex() + @see fsl_content_put_ex() */ -int fsl__content_put( fsl_cx * const f, - fsl_buffer const * pBlob, - fsl_id_t * newRid); +FSL_EXPORT int fsl_content_put( fsl_cx * const f, + fsl_buffer const * pBlob, + fsl_id_t * newRid); /** @internal If the given blob ID refers to deltified repo content, this routine @@ -18250,18 +17828,18 @@ f has no opened repository, FSL_RC_RANGE if rid is not positive, and any number of other potential errors during the db and content operations. This function treats already unexpanded content as success. - @see fsl__content_deltify() + @see fsl_content_deltify() */ -int fsl__content_undeltify(fsl_cx * const f, fsl_id_t rid); +FSL_EXPORT int fsl_content_undeltify(fsl_cx * const f, fsl_id_t rid); /** @internal - The converse of fsl__content_undeltify(), this replaces the storage + The converse of fsl_content_undeltify(), this replaces the storage of the given blob record so that it is a delta of srcid. If rid is already a delta from some other place then no conversion occurs and this is a no-op unless force is true. @@ -18287,14 +17865,14 @@ left untouched. Returns 0 if a delta is successfully made or none needs to be made, non-0 on error. - @see fsl__content_undeltify() + @see fsl_content_undeltify() */ -int fsl__content_deltify(fsl_cx * f, fsl_id_t rid, - fsl_id_t srcid, bool force); +FSL_EXPORT int fsl_content_deltify(fsl_cx * f, fsl_id_t rid, + fsl_id_t srcid, bool force); /** @internal Creates a new phantom blob with the given UUID and return its @@ -18306,12 +17884,12 @@ _or_ f has been flagged as being in "private mode" then the new content is flagged as private. newId may be NULL, but if it is then the caller will have to find the record id himself by using the UUID (see fsl_uuid_to_rid()). */ -int fsl__content_new( fsl_cx * f, fsl_uuid_cstr uuid, bool isPrivate, - fsl_id_t * newId ); +FSL_EXPORT int fsl_content_new( fsl_cx * f, fsl_uuid_cstr uuid, bool isPrivate, + fsl_id_t * newId ); /** @internal Check to see if checkin "rid" is a leaf and either add it to the LEAF table if it is, or remove it if it is not. @@ -18319,30 +17897,30 @@ Returns 0 on success, FSL_RC_MISUSE if !f or f has no repo db opened, FSL_RC_RANGE if pid is <=0. Other errors (e.g. FSL_RC_DB) may indicate that db is not a repo. On error db's error state may be updated. */ -int fsl__repo_leafcheck(fsl_cx * const f, fsl_id_t pid); +FSL_EXPORT int fsl_repo_leaf_check(fsl_cx * f, fsl_id_t pid); /** @internal Schedules a leaf check for "rid" and its parents. Returns 0 on success. */ -int fsl__repo_leafeventually_check( fsl_cx * const f, fsl_id_t rid); +FSL_EXPORT int fsl_repo_leaf_eventually_check( fsl_cx * f, fsl_id_t rid); /** @internal Perform all pending leaf checks. Returns 0 on success or if it has nothing to do. */ -int fsl__repo_leafdo_pending_checks(fsl_cx * const f); +FSL_EXPORT int fsl_repo_leaf_do_pending_checks(fsl_cx *f); /** @internal Inserts a tag into f's repo db. It does not create the related - control artifact - use fsl_tag_an_rid() for that. + control artifact - use fsl_tag_add_artifact() for that. rid is the artifact to which the tag is being applied. srcId is the artifact that contains the tag. It is often, but not always, the same as rid. This is often the RID of the @@ -18361,24 +17939,24 @@ *outRid (if outRid is not NULL) and no new entry is created. Returns 0 on success, and has a huge number of potential error codes. */ -int fsl__tag_insert( fsl_cx * const f, - fsl_tagtype_e tagtype, - char const * zTag, - char const * zValue, - fsl_id_t srcId, - double mtime, - fsl_id_t rid, - fsl_id_t *outRid ); +FSL_EXPORT int fsl_tag_insert( fsl_cx * f, + fsl_tagtype_e tagtype, + char const * zTag, + char const * zValue, + fsl_id_t srcId, + double mtime, + fsl_id_t rid, + fsl_id_t *outRid ); /** @internal Propagate all propagatable tags in artifact pid to the children of pid. Returns 0 on... non-error. Returns FSL_RC_RANGE if pid<=0. */ -int fsl__tag_propagate_all(fsl_cx * const f, fsl_id_t pid); +FSL_EXPORT int fsl_tag_propagate_all(fsl_cx * f, fsl_id_t pid); /** @internal Propagates a tag through the various internal pipelines. @@ -18406,29 +17984,44 @@ This function is unforgiving of invalid values/ranges, and may assert in debug mode if passed invalid ids (values<=0), a NULL f, or if f has no opened repo. */ -int fsl__tag_propagate(fsl_cx * const f, - fsl_tagtype_e tagType, - fsl_id_t pid, - fsl_id_t tagid, - fsl_id_t origId, - const char *zValue, - double mtime ); +FSL_EXPORT int fsl_tag_propagate(fsl_cx *f, + fsl_tagtype_e tagType, + fsl_id_t pid, + fsl_id_t tagid, + fsl_id_t origId, + const char *zValue, + double mtime ); + +/** @internal + + Remove the PGP signature from a raw artifact, if there is one. + + Expects *pz to point to *pn bytes of string memory which might + or might not be prefixed by a PGP signature. If the string is + enveloped in a signature, then upon returning *pz will point to + the first byte after the end of the PGP header and *pn will + contain the length of the content up to, but not including, the + PGP footer. + + If *pz does not look like a PGP header then this is a no-op. + + Neither pointer may be NULL and *pz must point to *pn bytes of + valid memory. If *pn is initially less than 59, this is a no-op. +*/ +FSL_EXPORT void fsl_remove_pgp_signature(unsigned char const **pz, fsl_size_t *pn); /** @internal Clears the "seen" cache used by manifest parsing. Should be called by routines which initialize parsing, but not until their work has finished all parsing (so that recursive parsing can - use that cache). - - If freeMemory is true the cache's list memory is freed, otherwise - the cache is reset for reuse without clearing its memory. + use it). */ -void fsl__cx_clear_mf_seen(fsl_cx * const f, bool freeMemory); +FSL_EXPORT void fsl_cx_clear_mf_seen(fsl_cx * f); /** @internal Generates an fsl_appendf()-formatted message to stderr and fatally aborts the application by calling exit(). This is only @@ -18438,11 +18031,11 @@ fmt may be empty or NULL, in which case only the code and its fsl_rc_cstr() representation are output. This function does not return. */ -void fsl__fatal( int code, char const * fmt, ... ) +FSL_EXPORT void fsl_fatal( int code, char const * fmt, ... ) #ifdef __GNUC__ __attribute__ ((noreturn)) #endif ; @@ -18457,30 +18050,30 @@ In debug builds, this function asserts that no pointer arguments are NULL and that f has an opened repository. */ -int fsl__repo_filename_fnid2( fsl_cx * f, char const * filename, +FSL_EXPORT int fsl_repo_filename_fnid2( fsl_cx * f, char const * filename, fsl_id_t * rv, bool createNew ); /** @internal Clears and frees all (char*) members of db but leaves the rest intact. If alsoErrorState is true then the error state is also freed, else it is kept as well. */ -void fsl__db_clear_strings(fsl_db * const db, bool alsoErrorState ); +FSL_EXPORT void fsl_db_clear_strings(fsl_db * const db, bool alsoErrorState ); /** @internal Returns 0 if db appears to have a current repository schema, 1 if it appears to have an out of date schema, and -1 if it appears to not be a repository. Results are undefined if db is NULL or not opened. */ -int fsl__db_repo_verify_schema(fsl_db * db); +FSL_EXPORT int fsl_db_repo_verify_schema(fsl_db * db); /** @internal Flags for APIs which add phantom blobs to the repository. The @@ -18490,28 +18083,28 @@ we have no content. This normally happens during sync or rebuild operations, but can also happen when artifacts are stored directly as files in a repo (like this project's repository does, storing artifacts from *other* projects for testing purposes). */ -enum fsl__phantom_e { +enum fsl_phantom_e { /** - Indicates to fsl__uuid_to_rid2() that no phantom artifact + Indicates to fsl_uuid_to_rid2() that no phantom artifact should be created. */ FSL_PHANTOM_NONE = 0, /** - Indicates to fsl__uuid_to_rid2() that a public phantom + Indicates to fsl_uuid_to_rid2() that a public phantom artifact should be created if no artifact is found. */ FSL_PHANTOM_PUBLIC = 1, /** - Indicates to fsl__uuid_to_rid2() that a private phantom + Indicates to fsl_uuid_to_rid2() that a private phantom artifact should be created if no artifact is found. */ FSL_PHANTOM_PRIVATE = 2 }; -typedef enum fsl__phantom_e fsl__phantom_e; +typedef enum fsl_phantom_e fsl_phantom_e; /** @internal Works like fsl_uuid_to_rid(), with these differences: @@ -18521,17 +18114,17 @@ it will add either a public or private phantom entry and return its new rid. If mode is FSL_PHANTOM_NONE then this this behaves just like fsl_uuid_to_rid(). Returns a positive value on success, 0 if it finds no entry and - mode==FSL_PHANTOM_NONE, and a negative value on error (e.g. if + mode==FSL_PHANTOM_NONE, and a negative value on error (e.g. if fsl_is_uuid(uuid) returns false). Errors which happen after argument validation will "most likely" update f's error state with details. */ -fsl_id_t fsl__uuid_to_rid2( fsl_cx * const f, fsl_uuid_cstr uuid, - fsl__phantom_e mode ); +FSL_EXPORT fsl_id_t fsl_uuid_to_rid2( fsl_cx * f, fsl_uuid_cstr uuid, + fsl_phantom_e mode ); /** @internal Schedules the given rid to be verified at the next commit. This is used by routines which add artifact records to the blob @@ -18539,23 +18132,23 @@ The only error case, assuming the arguments are valid, is an allocation error while appending rid to the internal to-verify queue. - @see fsl__repo_verify_at_commit() + @see fsl_repo_verify_at_commit() @see fsl_repo_verify_cancel() */ -int fsl__repo_verify_before_commit( fsl_cx * const f, fsl_id_t rid ); +FSL_EXPORT int fsl_repo_verify_before_commit( fsl_cx * f, fsl_id_t rid ); /** @internal Clears f's verify-at-commit list of RIDs. - @see fsl__repo_verify_at_commit() - @see fsl__repo_verify_before_commit() + @see fsl_repo_verify_at_commit() + @see fsl_repo_verify_before_commit() */ -void fsl_repo_verify_cancel( fsl_cx * const f ); +FSL_EXPORT void fsl_repo_verify_cancel( fsl_cx * f ); /** @internal Processes all pending verify-at-commit entries and clears the to-verify list. Returns 0 on success. On error f's error state @@ -18569,13 +18162,13 @@ any deltas it may need to) fails or a checksum does not match then this routine fails and returns non-0. On error f's error state will be updated. @see fsl_repo_verify_cancel() - @see fsl__repo_verify_before_commit() + @see fsl_repo_verify_before_commit() */ -int fsl__repo_verify_at_commit( fsl_cx * const f ); +FSL_EXPORT int fsl_repo_verify_at_commit( fsl_cx * f ); /** @internal Removes all entries from the repo's blob table which are listed in the shun table. Returns 0 on success. This operation is @@ -18582,11 +18175,11 @@ wrapped in a transaction. Delta contant which depend on to-be-shunned content are replaced with their undeltad forms. Returns 0 on success. */ -int fsl__repo_shun_artifacts(fsl_cx * const f); +FSL_EXPORT int fsl_repo_shun_artifacts(fsl_cx * f); /** @internal. Return a pointer to a string that contains the RHS of an SQL IN operator which will select config.name values that are part of @@ -18596,11 +18189,11 @@ fsl_free(). Returns NULL on allocation error. Reminder to self: this is part of the infrastructure for copying config state from an existing repo when creating new repo. */ -char *fsl__config_inop_rhs(int iMask); +FSL_EXPORT char *fsl_config_inop_rhs(int iMask); /** @internal Return a pointer to a string that contains the RHS of an IN operator that will select config.name values that are in the @@ -18609,11 +18202,11 @@ fsl_free(). Returns NULL on allocation error. Reminder to self: this is part of the infrastructure for copying config state from an existing repo when creating new repo. */ -char *fsl_db_setting_inop_rhs(); +FSL_EXPORT char *fsl_db_setting_inop_rhs(); /** @internal Creates the ticket and ticketchng tables in f's repository db, DROPPING them if they already exist. The schema comes from @@ -18622,11 +18215,11 @@ TODO? Add a flag specifying whether to drop or keep existing copies. Returns 0 on success. */ -int fsl__cx_ticket_create_table(fsl_cx * const f); +FSL_EXPORT int fsl_cx_ticket_create_table(fsl_cx * const f); /** @internal Frees all J-card entries in the given list. @@ -18634,11 +18227,11 @@ instances. If alsoListMem is true then any memory owned by li is also freed. li itself is not freed. Results are undefined if li is NULL. */ -void fsl__card_J_list_free( fsl_list * li, bool alsoListMem ); +FSL_EXPORT void fsl_card_J_list_free( fsl_list * li, bool alsoListMem ); /** @internal Values for fsl_card_J::flags. */ @@ -18670,11 +18263,11 @@ Returns 0 on success. @see fsl_cx::ticket::customFields */ -int fsl__cx_ticket_load_fields(fsl_cx * const f, bool forceReload); +FSL_EXPORT int fsl_cx_ticket_load_fields(fsl_cx * f, bool forceReload); /** @internal A comparison routine for qsort(3) which compares fsl_card_J instances in a lexical manner based on their names. The order is @@ -18681,11 +18274,11 @@ important for card ordering in generated manifests. This routine expects to get passed (fsl_card_J**) (namely from fsl_list entries), and will not work on an array of J-cards. */ -int fsl__qsort_cmp_J_cards( void const * lhs, void const * rhs ); +FSL_EXPORT int fsl_qsort_cmp_J_cards( void const * lhs, void const * rhs ); /** @internal This function updates the repo and/or global config databases with links between the dbs intended for various fossil-level @@ -18697,11 +18290,11 @@ opened) is updated to know about the opened repo db. If a checkout is opened, global config (if opened) and the repo are updated to point to the checked-out db. */ -int fsl__repo_record_filename(fsl_cx * const f); +FSL_EXPORT int fsl_repo_record_filename(fsl_cx * f); /** @internal Updates f->ckout.uuid and f->ckout.rid to reflect the current checkout state. If no checkout is opened, the uuid is freed/NULLed @@ -18712,13 +18305,13 @@ This is done when a checkout db is opened, when performing a checkin, and otherwise as needed, and so calling it from other code is normally not necessary. - @see fsl__ckout_version_write() + @see fsl_ckout_version_write() */ -int fsl__ckout_version_fetch( fsl_cx * const f ); +FSL_EXPORT int fsl_ckout_version_fetch( fsl_cx *f ); /** @internal Updates f->ckout's state to reflect the given version info and writes the 'checkout' and 'checkout-hash' properties to the @@ -18735,15 +18328,15 @@ This routine also updates or removes the checkout's manifest files, as per fsl_ckout_manifest_write(). If vid is 0 then it removes any such files which themselves are not part of the current checkout. - @see fsl__ckout_version_fetch() + @see fsl_ckout_version_fetch() @see fsl_cx_ckout_version_set() */ -int fsl__ckout_version_write( fsl_cx * const f, fsl_id_t vid, - fsl_uuid_cstr uuid ); +FSL_EXPORT int fsl_ckout_version_write( fsl_cx *f, fsl_id_t vid, + fsl_uuid_cstr uuid ); /** @internal Exports the file with the given [vfile].[id] to the checkout, @@ -18780,20 +18373,20 @@ multiple files at once, but (A) that's not part of the interface and may change and (B) the 3rd parameter makes little sense in that case unless maybe we change it to a callback, which seems like overkill for our use cases. */ -int fsl__vfile_to_ckout(fsl_cx * const f, fsl_id_t vfileId, - int * wasWritten); +FSL_EXPORT int fsl_vfile_to_ckout(fsl_cx * f, fsl_id_t vfileId, + int * wasWritten); /** @internal On Windows platforms (only), if fsl_isalpha(*zFile) and ':' == zFile[1] then this returns zFile+2, otherwise it returns zFile. */ -char * fsl__file_without_drive_letter(char * zFile); +FSL_EXPORT char * fsl_file_without_drive_letter(char * zFile); /** @internal This is identical to the public-API member fsl_deck_F_search(), except that it returns a non-const F-card. @@ -18818,20 +18411,20 @@ search was found. Because searches are normally done in lexical order (because of architectural reasons), this is normally an O(1) operation. It degrades to O(N) if out-of-lexical-order searches are performed. */ -fsl_card_F * fsl__deck_F_seek(fsl_deck * const d, const char *zName); +FSL_EXPORT fsl_card_F * fsl_deck_F_seek(fsl_deck * const d, const char *zName); /** @internal Part of the fsl_cx::fileContent optimization. This sets f->fileContent.used to 0 and if its capacity is over a certain (unspecified, unconfigurable) size then it is trimmed to that size. */ -void fsl__cx_content_buffer_yield(fsl_cx * const f); +FSL_EXPORT void fsl_cx_content_buffer_yield(fsl_cx * const f); /** @internal Currently disabled (always returns 0) pending resolution of a "wha???" result from one of the underlying queries. @@ -18845,12 +18438,12 @@ If the repository database has no search index or the given content is marked as private, this function returns 0 and makes no changes to the db. */ -int fsl__search_doc_touch(fsl_cx * const f, fsl_satype_e saType, - fsl_id_t rid, const char * docName); +FSL_EXPORT int fsl_search_doc_touch(fsl_cx * const f, fsl_satype_e saType, + fsl_id_t rid, const char * docName); /** @internal Performs the same job as fsl_diff_text() but produces the results in the low-level form of an array of "copy/delete/insert triples." @@ -18863,12 +18456,12 @@ eventually be fsl_free()'d by the caller. On error *outRaw is not modified. @deprecated Use fsl_diff_v2_raw() instead. */ -int fsl__diff_text_raw(fsl_buffer const *p1, fsl_buffer const *p2, - int diffFlags, int ** outRaw); +FSL_EXPORT int fsl_diff_text_raw(fsl_buffer const *p1, fsl_buffer const *p2, + int diffFlags, int ** outRaw); /** @internal If the given file name is a reserved filename (case-insensitive) on Windows platforms, a pointer to the reserved part of the name, else @@ -18876,18 +18469,18 @@ zPath must be a canonical path with forward-slash directory separators. nameLen is the length of zPath. If negative, fsl_strlen() is used to determine its length. */ -bool fsl__is_reserved_fn_windows(const char *zPath, fsl_int_t nameLen); +FSL_EXPORT bool fsl_is_reserved_fn_windows(const char *zPath, fsl_int_t nameLen); /** @internal Clears any pending merge state from the checkout db's vmerge table. Returns 0 on success. */ -int fsl__ckout_clear_merge_state( fsl_cx *f ); +FSL_EXPORT int fsl_ckout_clear_merge_state( fsl_cx *f ); /** @internal Installs or reinstalls the checkout database schema into f's open @@ -18898,11 +18491,11 @@ beforehand if they exist. "It's the only way to be sure." If dropIfExists is false and the schema appears to already exists (without actually validating its validity), 0 is returned. */ -int fsl_ckout_install_schema(fsl_cx * const f, bool dropIfExists); +FSL_EXPORT int fsl_ckout_install_schema(fsl_cx *f, bool dropIfExists); /** @internal Attempts to remove empty directories from under a checkout, starting with tgtDir and working upwards until it either cannot @@ -18920,11 +18513,11 @@ f's current checkout. There are any number of valid reasons removal of a directory might fail, and this routine stops at the first one which does. */ -unsigned int fsl_ckout_rm_empty_dirs(fsl_cx * const f, fsl_buffer * const tgtDir); +FSL_EXPORT unsigned int fsl_ckout_rm_empty_dirs(fsl_cx * f, fsl_buffer * tgtDir); /** @internal This is intended to be passed the name of a file which was just deleted and "might" have left behind an empty directory. The name @@ -18939,50 +18532,51 @@ that). @see fsl_is_rooted_in_ckout() @see fsl_rm_empty_dirs() */ -int fsl_ckout_rm_empty_dirs_for_file(fsl_cx * const f, char const *zAbsPath); +FSL_EXPORT int fsl_ckout_rm_empty_dirs_for_file(fsl_cx * f, char const *zAbsPath); /** @internal If f->cache.seenDeltaManifest<=0 then this routine sets it to 1 and sets the 'seen-delta-manifest' repository config setting to 1, else this has no side effects. Returns 0 on success, non-0 if there is an error while writing to the repository config. */ -int fsl__cx_update_seen_delta_deck(fsl_cx * const f); +FSL_EXPORT int fsl_cx_update_seen_delta_mf(fsl_cx *f); /** @internal Very, VERY internal. Returns the next available buffer from f->scratchpads. Fatally aborts if there are no free buffers because "that should not happen." Calling this obligates the caller to eventually pass - its result to fsl__cx_scratchpad_yield(). + its result to fsl_cx_scratchpad_yield(). This function guarantees the returned buffer's 'used' member will be set to 0. Maintenance note: the number of buffers is hard-coded in the fsl_cx::scratchpads anonymous struct. */ -fsl_buffer * fsl__cx_scratchpad(fsl_cx * const f); +FSL_EXPORT fsl_buffer * fsl_cx_scratchpad(fsl_cx *f); /** @internal Very, VERY internal. - "Yields" a buffer which was returned from fsl__cx_scratchpad(), + "Yields" a buffer which was returned from fsl_cx_scratchpad(), making it available for re-use. The caller must treat the buffer as if this routine frees it: using the buffer after having passed it to this function will internally be flagged as explicit misuse and will lead to a fatal crash the next time that buffer is fetched via - fsl__cx_scratchpad(). So don't do that. + fsl_cx_scratchpad(). So don't do that. */ -void fsl__cx_scratchpad_yield(fsl_cx * const f, fsl_buffer * const b); +FSL_EXPORT void fsl_cx_scratchpad_yield(fsl_cx *f, fsl_buffer * b); + /** @internal Run automatically by fsl_deck_save(), so it needn't normally be run aside from that, at least not from average client code. @@ -18998,34 +18592,34 @@ Returns 0 on succes, FSL_RC_MISUSE !d->f, FSL_RC_RANGE if d->rid<=0, FSL_RC_MISUSE (with more error info in f) if d does not contain all required cards for its d->type value. It may return various other codes from the many routines it delegates work to. - Crosslinking of ticket artifacts is currently (2021-11) missing. + Crosslinking of ticket artifacts is currently (2021-03) missing. Design note: d "really should" be const here but some internals (d->F.cursor and delayed baseline loading) prohibit it. - @see fsl__deck_crosslink_one() + @see fsl_deck_crosslink_one() */ -int fsl__deck_crosslink( fsl_deck /* const */ * const d ); +FSL_EXPORT int fsl_deck_crosslink( fsl_deck /* const */ * const d ); /** @internal Run automatically by fsl_deck_save(), so it needn't normally be run aside from that, at least not from average client code. This is a convience form of crosslinking which must only be used when a single deck (and only a single deck) is to be crosslinked. This function wraps the crosslinking in fsl_crosslink_begin() - and fsl__crosslink_end(), but otherwise behaves the same as - fsl__deck_crosslink(). If crosslinking fails, any in-progress + and fsl_crosslink_end(), but otherwise behaves the same as + fsl_deck_crosslink(). If crosslinking fails, any in-progress transaction will be flagged as failed. Returns 0 on success. */ -int fsl__deck_crosslink_one( fsl_deck * const d ); +FSL_EXPORT int fsl_deck_crosslink_one( fsl_deck * const d ); /** @internal Checks whether the given filename is "safe" for writing to within f's current checkout. @@ -19047,11 +18641,11 @@ is not considered an error. Returns 0 on success. On error f's error state is updated with information about the problem. */ -int fsl__ckout_safe_file_check(fsl_cx * const f, char const * zFilename); +FSL_EXPORT int fsl_ckout_safe_file_check(fsl_cx *f, char const * zFilename); /** @internal UNTESTED! Creates a file named zLinkFile and populates its contents with a @@ -19077,12 +18671,12 @@ addition of symlinks support into fossil was, IMHO, a poor decision for $REASONS. That might (might) be reflected long-term in this API by only supporting them in the way fossil does for platforms which do not support symlinks. */ -int fsl__ckout_symlink_create(fsl_cx * const f, char const *zTgtFile, - char const * zLinkFile); +FSL_EXPORT int fsl_ckout_symlink_create(fsl_cx * f, char const *zTgtFile, + char const * zLinkFile); /** Compute all file name changes that occur going from check-in iFrom to check-in iTo. Requires an opened repository. @@ -19102,44 +18696,44 @@ of the problem. Space to hold *aiChng is obtained from fsl_malloc() and must be released by the caller. */ -int fsl__find_filename_changes(fsl_cx * const f, - fsl_id_t iFrom, - fsl_id_t iTo, - bool revOK, - uint32_t *pnChng, - fsl_id_t **aiChng); +FSL_EXPORT int fsl__find_filename_changes(fsl_cx * const f, + fsl_id_t iFrom, + fsl_id_t iTo, + bool revOK, + uint32_t *pnChng, + fsl_id_t **aiChng); /** Bitmask of file change types for use with - fsl__is_locally_modified(). + fsl_is_locally_modified(). */ -enum fsl__localmod_e { +enum fsl_localmod_e { /** Sentinel value. */ -FSL__LOCALMOD_NONE = 0, +FSL_LOCALMOD_NONE = 0, /** Permissions changed. */ -FSL__LOCALMOD_PERM = 0x01, +FSL_LOCALMOD_PERM = 0x01, /** File size or hash (i.e. content) differ. */ -FSL__LOCALMOD_CONTENT = 0x02, +FSL_LOCALMOD_CONTENT = 0x02, /** The file type was switched between symlink and normal file. In this case, no check for content change, beyond the file size change, is performed. */ -FSL__LOCALMOD_LINK = 0x04, +FSL_LOCALMOD_LINK = 0x04, /** File was not found in the local checkout. */ -FSL__LOCALMOD_NOTFOUND = 0x10 +FSL_LOCALMOD_NOTFOUND = 0x10 }; -typedef enum fsl__localmod_e fsl__localmod_e; +typedef enum fsl_localmod_e fsl_localmod_e; /** @internal Checks whether the given file has been locally modified compared to a known size, hash value, and permissions. Requires that f has an opened checkout. @@ -19164,27 +18758,27 @@ the fossil database, where files have no timestamps, the local file's timestamp is never considered for purposes of modification checking. If isModified is not NULL then on success it is set to a bitmask of - values from the fsl__localmod_e enum specifying the type(s) of + values from the fsl_localmod_e enum specifying the type(s) of change(s) detected: - - FSL__LOCALMOD_PERM = permissions changed. + - FSL_LOCALMOD_PERM = permissions changed. - - FSL__LOCALMOD_CONTENT = file size or hash (i.e. content) differ. + - FSL_LOCALMOD_CONTENT = file size or hash (i.e. content) differ. - - FSL__LOCALMOD_LINK = the file type was switched between symlink + - FSL_LOCALMOD_LINK = the file type was switched between symlink and normal file. In this case, no check for content change, beyond the file size change, is performed. - - FSL__LOCALMOD_NOFOUND = file was not found in the local checkout. + - FSL_LOCALMOD_NOFOUND = file was not found in the local checkout. Noting that: - - Combined values of (FSL__LOCALMOD_PERM | FSL__LOCALMOD_CONTENT) are - possible, but FSL__LOCALMOD_NOFOUND will never be combined with one + - Combined values of (FSL_LOCALMOD_PERM | FSL_LOCALMOD_CONTENT) are + possible, but FSL_LOCALMOD_NOFOUND will never be combined with one of the other values. If stat() fails for any reason other than file-not-found (e.g. permissions), an error is triggered. @@ -19210,17 +18804,17 @@ may become dependent on the repository's 'allow-symlinks' setting. Internal detail, not relevant for clients: this updates f's cache stat entry. */ -int fsl__is_locally_modified(fsl_cx * const f, - const char * zFilename, - fsl_size_t fileSize, - const char * zOrigHash, - fsl_int_t zOrigHashLen, - fsl_fileperm_e origPerm, - int * isModified); +FSL_EXPORT int fsl_is_locally_modified(fsl_cx * f, + const char * zFilename, + fsl_size_t fileSize, + const char * zOrigHash, + fsl_int_t zOrigHashLen, + fsl_fileperm_e origPerm, + int * isModified); /** @internal This routine cleans up the state of selected cards in the given deck. The 2nd argument is an list of upper-case letters @@ -19227,65 +18821,11 @@ representing the cards which should be cleaned up, e.g. "ADG". If it is NULL, all cards are cleaned up but d has non-card state which is not cleaned up by this routine. Unknown letters are simply ignored. */ -void fsl__deck_clean_cards(fsl_deck * const d, char const * letters); - -/** @internal - - This starts a transaction (possibly nested) on the repository db - and initializes some temporary db state needed for the - crosslinking certain artifact types. It "should" (see below) be - called at the start of the crosslinking process. Crosslinking - *can* work without this but certain steps for certain (subject to - change) artifact types will be skipped, possibly leading to - unexpected timeline data or similar funkiness. No permanent - SCM-relevant state will be missing, but the timeline might not be - updated and tickets might not be fully processed. This should be - used before crosslinking any artifact types, but will only have - significant side effects for certain (subject to change) types. - - Returns 0 on success. - - If it returns 0 then the caller is OBLIGATED to either 1) call - fsl__crosslink_end() or 2) call fsl_db_transaction_rollback() and - set f->cache.isCrosslinking to false. This process may install - temporary tables and/or triggers, so failing to call one or the - other of those will result in misbehavior. - - @see fsl__deck_crosslink() -*/ -int fsl__crosslink_begin(fsl_cx * const f); - -/** @internal - - Must not be called unless fsl_crosslink_begin() has - succeeded. This performs crosslink post-processing on certain - artifact types and cleans up any temporary db state initialized by - fsl__crosslink_begin(). - - If the 2nd argument is not 0 then this routine triggers a rollback - of the transaction started by fsl__crosslink_begin() and - propagates any pending error code from f or (if f has no error - code) from f's db handle. - - The second argument is intended to be the value of any pending - result code (0 or otherwise) from any work done _after_ - fsl__crosslink_begin() succeeded. If passed 0, it assumes that - there is no propagating error state and will attempt to complete - the crosslinking process. If passed non-0, it triggers a rollback - and unsets the f->cache.isCrosslinking flag, but does no - additional work, then returns resultCode. - - Returns 0 on success. On error it initiates (or propagates) a - rollback for the current transaction. If called when a rollback is - pending, it unsets the crosslink-is-running flag and returns the - propagating result code. -*/ -int fsl__crosslink_end(fsl_cx * const f, int resultCode); - +FSL_EXPORT void fsl_deck_clean_cards(fsl_deck * d, char const * letters); /** @internal Searches the current repository database for a fingerprint and returns it as a string in *zOut. @@ -19311,12 +18851,11 @@ expected to be in many repositories which are accessed via this library. Practice has, however, revealed some. @see fsl_ckout_fingerprint_check() */ -int fsl__repo_fingerprint_search(fsl_cx * const f, fsl_id_t rcvid, - char ** zOut); +FSL_EXPORT int fsl_repo_fingerprint_search(fsl_cx *f, fsl_id_t rcvid, char ** zOut); /** A context for running a raw diff. The aEdit[] array describes the raw diff. Each triple of integers in @@ -19327,11 +18866,11 @@ (3) INSERT: Number of lines found only in aTo The triples repeat until all lines of both aFrom and aTo are accounted for. */ -struct fsl__diff_cx { +struct fsl_diff_cx { /*TODO unsigned*/ int *aEdit; /* Array of copy/delete/insert triples */ /*TODO unsigned*/ int nEdit; /* Number of integers (3x num of triples) in aEdit[] */ /*TODO unsigned*/ int nEditAlloc; /* Space allocated for aEdit[] */ fsl_dline *aFrom; /* File on left side of the diff */ /*TODO unsigned*/ int nFrom; /* Number of lines in aFrom[] */ @@ -19340,26 +18879,26 @@ int (*cmpLine)(const fsl_dline * const, const fsl_dline *const); /* Function to be used for comparing */ }; /** Convenience typeef. */ -typedef struct fsl__diff_cx fsl__diff_cx; -/** Initialized-with-defaults fsl__diff_cx structure, intended for +typedef struct fsl_diff_cx fsl_diff_cx; +/** Initialized-with-defaults fsl_diff_cx structure, intended for const-copy initialization. */ -#define fsl__diff_cx_empty_m {\ +#define fsl_diff_cx_empty_m {\ NULL,0,0,NULL,0,NULL,0,fsl_dline_cmp \ } -/** Initialized-with-defaults fsl__diff_cx structure, intended for +/** Initialized-with-defaults fsl_diff_cx structure, intended for non-const copy initialization. */ -extern const fsl__diff_cx fsl__diff_cx_empty; +extern const fsl_diff_cx fsl_diff_cx_empty; /** @internal Compute the differences between two files already loaded into - the fsl__diff_cx structure. + the fsl_diff_cx structure. A divide and conquer technique is used. We look for a large block of common text that is in the middle of both files. Then compute the difference on those parts of the file before and after the common block. This technique is fast, but it does @@ -19370,25 +18909,25 @@ Any common text at the beginning and end of the two files is removed before starting the divide-and-conquer algorithm. Returns 0 on succes, FSL_RC_OOM on an allocation error. */ -int fsl__diff_all(fsl__diff_cx * const p); +int fsl__diff_all(fsl_diff_cx * const p); /** @internal */ -void fsl__diff_optimize(fsl__diff_cx * const p); +void fsl__diff_optimize(fsl_diff_cx * const p); /** @internal */ -void fsl__diff_cx_clean(fsl__diff_cx * const cx); +void fsl__diff_cx_clean(fsl_diff_cx * const cx); /** @internal Undocumented. For internal debugging only. */ -void fsl__dump_triples(fsl__diff_cx const * const p, +void fsl__dump_triples(fsl_diff_cx const * const p, char const * zFile, int ln ); /** @internal Removes from the BLOB table all artifacts that are in the SHUN @@ -19396,40 +18935,19 @@ opened. */ int fsl__shunned_remove(fsl_cx * const f); - -/** @internal - - This is a fossil-specific internal detail not needed by the more - generic parts of the fsl_db API. It loops through all "cached" - prepared statements for which stmt->role has been assigned a value - which bitmasks as true against the given role and finalizes - them. If such a statement is currently held by a call to/via - fsl_db_prepare_cachedv() then this will NOT finalize that - statement, will update db's error state, and return - FSL_RC_MISUSE. - - Returns 0 on success. - - As a special case, if role==0 then ALL cached statements are - closed, with the caveat that the process will still fail if any - statement is currently flagged as active. -*/ -int fsl__db_cached_clear_role(fsl_db * const db, int role); - - /** @internal Maximum length of a line in a text file, in bytes. (2**15 = 32k) */ -#define FSL__LINE_LENGTH_MASK_SZ 15 +#define FSL_LINE_LENGTH_MASK_SZ 15 /** @internal */ -#define FSL__LINE_LENGTH_MASK ((1<, \&. Move selection cursor down the timeline. .It Cm Arrow-up, k, <, \&, Move selection cursor up the timeline. -.It Cm C-f, Page-down +.It Cm Ctrl+f, Page-down Move selection cursor one page down the timeline. -.It Cm C-b, Page-up +.It Cm Ctrl+b, Page-up Move selection cursor one page up the timeline. .It Cm G, End Move selection cursor to the last commit on the timeline (i.e., oldest commit in the repository). .It Cm gg, Home @@ -306,20 +248,14 @@ in the repository). .It Cm Enter, Space Open a .Cm diff view displaying the changeset of the currently selected commit. -.It Cm b -Open and populate branch view with all repository branches. .It Cm c Toggle colourised timeline. On supported terminals, .Nm will default to displaying the timeline in colour. -.It Cm F -Prompt to enter a search term to filter a new timeline view by commits with -comment, user, or branch fields that match the entered pattern. If no commits -match, a message is displayed on screen. .It Cm t Display the tree of the repository corresponding to the currently selected commit. .It Cm / Prompt to enter a search term to begin searching for commits matching @@ -339,11 +275,11 @@ .El .Tg di .It Cm diff Oo Fl C | -no-colour Oc Oo Fl h | -help Oc Oo Fl i | -invert \ Oc Oo Fl w | -whitespace Oc Oo Fl x | -context Ar n Oc \ Oo Ar artifact1 Oo Ar artifact2 Oc Oc Op Ar path ... -.Dl Pq alias: Cm di +.Dl (alias: Cm di ) Display the differences between two repository artifacts, or between the local changes on disk and a given commit. If neither .Ar artifact1 nor .Ar artifact2 @@ -379,13 +315,10 @@ .It Fl C , -no-colour Disable coloured diff output, which is enabled by default on supported terminals. If this option is not used, colour can be toggled with the .Sy c diff view key binding as documented below. -User-defined colours are also supported, see -.Sx ENVIRONMENT -for details. .It Fl h , -help Display diff command help and usage information then exit. .It Fl i , -invert Invert the difference between artifacts when displaying the diff. .It Fl w , -whitespace @@ -399,38 +332,10 @@ .Pp Key bindings for .Cm fnc diff are as follows: .Bl -tag -width Ds -.It Cm Arrow-down, j -Scroll down one line of diff output. -.It Cm Arrow-up, k -Scroll up one line of diff output. -.It Cm C-f, Page-down, Space -Scroll down one page of diff output. -.It Cm C-b, Page-up -Scroll up one page of diff output. -.It Cm G, End -Scroll to the end of the view (i.e., last line of diff output). -.It Cm gg, Home -Scroll to the top of the view (i.e., first line of diff output). -.It Cm C-k, K, <, \&, -Move up the -.Cm timeline -to the previous (i.e., newer) commit and display its diff. (Only available -if the diff was accessed from the timeline.) -.It Cm C-j, J, >, \&. -Move down the -.Cm timeline -to the next (i.e., older) commit and display its diff. (Only available -if the diff was accessed from the timeline.) -.It Cm \&-, \&_ -Decrease the number of context lines shown in diff output. -.It Cm \&=, \&+ -Increase the number of context lines shown in diff output. -.It Cm b -Open and populate branch view with all repository branches. .It Cm c Toggle coloured diff output. On supported terminals, .Nm will default to displaying changes and diff metadata in colour. .It Cm i @@ -440,10 +345,34 @@ .Nm will display the entire content of newly added or deleted files. .It Cm w Toggle whether whitespace-only changes are ignored when comparing lines in the diff. +.It Cm Arrow-down, j +Scroll down one line of diff output. +.It Cm Arrow-up, k +Scroll up one line of diff output. +.It Cm Ctrl+f, Page-down, Space +Scroll down one page of diff output. +.It Cm Ctrl+b, Page-up +Scroll up one page of diff output. +.It Cm G, End +Scroll to the end of the view (i.e., last line of diff output). +.It Cm gg, Home +Scroll to the top of the view (i.e., first line of diff output). +.It Cm \&-, \&_ +Decrease the number of context lines shown in diff output. +.It Cm \&=, \&+ +Increase the number of context lines shown in diff output. +.It Cm Ctrl+k, K, <, \&, +Move up the +.Cm timeline +to the previous (i.e., more recent) commit and display its diff. +.It Cm Ctrl+j, J, >, \&. +Move down the +.Cm timeline +to the next (i.e., earlier) commit and display its diff. .It Cm / Prompt to enter a search term to begin searching the diff output for lines matching the pattern provided. The search term is an extended regular expression, which is documented in .Xr re_format 7 . @@ -453,11 +382,11 @@ Find the previous line that matches the current search term. .El .Tg dir .It Cm tree Oo Fl C | -no-colour Oc Oo Fl c | -commit Ar commit Oc \ Oo Fl h | -help Oc Op Ar path -.Dl Pq aliases: Cm dir , Cm tr +.Dl (aliases: Cm dir , Cm tr ) Display navigable, hierarchical tree of a repository. If a .Ar path is specified, display tree nodes of this path. The .Ar path may be absolute, relative to the current working directory, or relative to the @@ -468,11 +397,11 @@ assumes a local checkout is open in or above the current working directory. .Pp Tree nodes are lexicographically ordered and may be postfixed with an identifier corresponding to the mode of the file object on disk as returned by .Xr lstat 2 : -.Bl -column -offset Ds YXZ description +.Bl -column YXZ description .It / Ta directory .It * Ta executable .It @ Ta symbolic link .El .Pp @@ -486,13 +415,10 @@ .It Fl C , -no-colour Disable coloured output, which is enabled by default on supported terminals. If this option is not used, colour can be toggled with the .Sy c tree view key binding as documented below. -User-defined colours are also supported, see -.Sx ENVIRONMENT -for details. .It Fl c , -commit Ar commit The displayed tree will reflect the state of the repository as at the check-in identified by .Ar commit . The expected argument is either the name of a branch, which will resolve @@ -517,20 +443,18 @@ Move up a level to the parent directory. This is a no-op when in the root tree. .It Cm Arrow-down, j Move selection cursor one node down the tree. .It Cm Arrow-up, k Move selection cursor one node up the tree. -.It Cm Page-down, C-f +.It Cm Page-down, Ctrl+f Move selection cursor one page down the tree. -.It Cm Page-up, C-b +.It Cm Page-up, Ctrl+b Move selection cursor one page up the tree. .It Cm Home, gg Move selection cursor to the first node in the tree. .It Cm End, G Move selection cursor to the last node in the tree. -.It Cm b -Open and populate branch view with all repository branches. .It Cm c Toggle coloured output. On supported terminals, .Nm will default to displaying the tree in colour. .It Cm t @@ -554,11 +478,11 @@ .El .Tg praise .It Cm blame Oo Fl C | -no-colour Oc \ Oo Fl c | -commit Ar commit Oo Fl r | -reverse Oc Oc Oo Fl h | -help Oc \ Oo Fl n | -limit Ar n Oc Ar path -.Dl Pq aliases: Cm praise , Cm annotate , Cm bl , Cm pr , Cm an +.Dl (aliases: Cm praise , Cm annotate , Cm bl , Cm pr , Cm an ) Show commit attribution history for each line of the file at the specified .Ar path , which may be absolute, relative to the current working directory, or relative to the repository root. This command must be executed from within or below the top level directory of the repository; that is, @@ -572,13 +496,10 @@ .It Fl C , -no-colour Disable coloured output, which is enabled by default on supported terminals. If this option is not used, colour can be toggled with the .Sy c blame view key binding as documented below. -User-defined colours are also supported, see -.Sx ENVIRONMENT -for details. .It Fl c , -commit Ar commit Start blame of file at the specified .Ar path from the check-in identified by .Ar commit . @@ -619,13 +540,13 @@ .Bl -tag -width Ds .It Cm Arrow-down, j Move selection cursor down one line. .It Cm Arrow-up, k Move selection cursor up one line. -.It Cm Page-down, C-f +.It Cm Page-down, Ctrl+f Move selection cursor down one page. -.It Cm Page-up, C-b +.It Cm Page-up, Ctrl+b Move selection cursor up one page. .It Cm Home, gg Move selection cursor to the first line in the file. .It Cm End, G Move selection cursor to the last line in the file. @@ -639,12 +560,10 @@ .It Cm p Blame the version of the file corresponding to the parent of the commit in the currently selected line. .It Cm B, Backspace Reload the previous blamed version of the file. -.It Cm T -Open and populate branch view with all repository branches. .It Cm c Toggle coloured output. On supported terminals, .Nm will default to displaying the blamed file in colour. .It Cm / @@ -660,11 +579,11 @@ .Tg tag .It Cm branch Oo Fl C | -no-colour Oc Oo Fl -after Ar date | \ Fl -before Ar date Oc Oo Fl h | -help Oc Oo Fl -open | Fl -closed Oc \ Oo Fl p | -no-private Oc Oo Fl r | -reverse Oc \ Oo Fl s | -sort Ar order Oc Op Ar glob -.Dl Pq aliases: Cm tag , Cm br +.Dl (aliases: Cm tag , Cm br ) Display navigable list of repository branches. If .Ar glob , is specified, only display branches matching the pattern provided. Pattern matching comparisons depend on the .Xr sqlite3 1 @@ -678,11 +597,11 @@ .Pp Branches are lexicographically ordered by default, and are prefixed with an identifier corresponding to the branch state (i.e., open/closed). The current and private branches are additionally annotated with a postfixed identifier: -.Bl -column -offset Ds ABCDEFGHIJ description +.Bl -column ABCDEFGHIJ description .It +dev-foo Ta open .It -rm-bar Ta closed .It +trunk@ Ta current .It +wip-baz* Ta private .El @@ -692,24 +611,21 @@ .Pp Options for .Cm fnc branch are as follows: .Bl -tag -width Ds +.It Fl C , -no-colour +Disable coloured output, which is enabled by default on supported terminals. +If this option is not used, colour can be toggled with the +.Sy c +branch view key binding as documented below. .It Fl a , -after Ar date Display only those branches with activity after the specified .Ar date . .It Fl b , -before Ar date Display only those branches with activity before the specified .Ar date . -.It Fl C , -no-colour -Disable coloured output, which is enabled by default on supported terminals. -If this option is not used, colour can be toggled with the -.Sy c -branch view key binding as documented below. -User-defined colours are also supported, see -.Sx ENVIRONMENT -for details. .It Fl c , -close Display only closed branches. .It Fl h , -help Display branch command help and usage information then exit. .It Fl o , -open @@ -723,11 +639,11 @@ Sort branches by .Ar order . Valid .Ar order values are as follows: -.Bl -column -offset 2s YXZ description +.Bl -column YXZ description .Sy mru Ta most recently used .Sy state Ta open/closed state .El .Pp Branches are sorted in lexicographical order by default. @@ -739,13 +655,13 @@ .Bl -tag -width Ds .It Cm Arrow-down, j Move selection cursor down one branch. .It Cm Arrow-up, k Move selection cursor up one branch. -.It Cm Page-down, C-f +.It Cm Page-down, Ctrl+f Move selection cursor down one page. -.It Cm Page-up, C-b +.It Cm Page-up, Ctrl+b Move selection cursor up one page. .It Cm Home, gg Move selection cursor to the first branch in the list. .It Cm End, G Move selection cursor to the last branch in the list. @@ -764,11 +680,11 @@ of the commit on the tip of said branch. .It Cm t Open the .Cm tree view of the currently selected branch. -.It Cm R, C-l +.It Cm R, Ctrl+l Reload the view with all repository branches, irrespective of which options were used in this .Cm fnc branch invocation. .It Cm / @@ -780,112 +696,13 @@ Find the next branch that matches the current search pattern. .It Cm N Find the previous branch that matches the current search pattern. .El .El -.Sh ENVIRONMENT -.Nm -displays coloured output by default in supported terminals. Each colour object -identified below can be defined by either exporting environment variables -.Po e.g., -.Cm export FNC_COLOUR_COMMIT=red -.Pc , -or with -.Nm Cm config -as documented above. At startup, -.Nm -will search for user-defined colours in the following order: -.Bl -column " environment variables " description -offset Ds -.It 1. repository settings Ta Pa repo.fossil -.It 3. environment variables Ta Sy shell -.El -.Pp -If none are found, the default colour scheme will be displayed. This enables -setting per-project colours to visually distinguish the current repository -being viewed, and globally changing the colour scheme for all repositories with -no local settings configured. Colours supported in -.Nm -are: -.Bl -column "black" "yellow" "magenta" "default" -offset indent-two -.It Qq black Ta Qq green Ta Qq blue Ta Qq cyan -.It Qq red Ta Qq yellow Ta Qq magenta Ta Qq default -.El -.Pp -Where -.Qq default -is the current foreground (i.e., text) colour in the terminal. -User-definable colour objects displayed in various -.Nm -views are as follows: -.Bl -tag -width FNC_COLOUR_BRANCH_PRIVATE -.It Ev FNC_COLOUR_COMMIT -The commit hash ID field displayed in the timeline, diff, tree, and blame views. -If not defined, the default value is -.Qq green . -.It Ev FNC_COLOUR_USER -The username field displayed in the timeline and diff views. If not defined, -the default value is -.Qq cyan . -.It Ev FNC_COLOUR_DATE -The date field displayed in the timeline and diff views. If not defined, the -default value is -.Qq yellow . -.It Ev FNC_COLOUR_DIFF_MINUS -Removed lines displayed in the diff view. If not defined, the default value is -.Qq magenta . -.It Ev FNC_COLOUR_DIFF_PLUS -Added lines displayed in the diff view. If not defined, the default value is -.Qq cyan . -.It Ev FNC_COLOUR_DIFF_CHUNK -Chunk header lines -.Po e.g., -.Ql @@ -732,34 +747,40 @@ -.Pc -displayed in the diff view. If not defined, the default value is -.Qq yellow . -.It Ev FNC_COLOUR_DIFF_META -Metadata displayed in the diff view. If not defined, the default value is -.Qq green . -.It Ev FNC_COLOUR_DIFF_TAGS -The tag field displayed in the diff view. If not defined, the default value is -.Qq magenta . -.It Ev FNC_COLOUR_TREE_DIR -Directory entries displayed in the tree view. If not defined, the default value -is -.Qq cyan . -.It Ev FNC_COLOUR_TREE_EXEC -Executable file entries displayed in the tree view. If not defined, the default -value is -.Qq green . -.It Ev FNC_COLOUR_TREE_LINK -Symbolic link entries displayed in the tree view. If not defined, the default -value is -.Qq magenta . -.It Ev FNC_COLOUR_BRANCH_OPEN -Open branches displayed in the branch view. If not defined, the default value -is -.Qq cyan . -.It Ev FNC_COLOUR_BRANCH_CLOSED -Closed branches displayed in the branch view. If not defined, the default value -is -.Qq magenta . -.It Ev FNC_COLOUR_BRANCH_CURRENT -The branch corresponding to the current checkout displayed in the branch view. -If not defined, the default value is -.Qq green . -.It Ev FNC_COLOUR_BRANCH_PRIVATE -Private branches displayed in the branch view. If not defined, the default -value is -.Qq yellow . -.El -.Pp -To clear environment variables, issue -.Cm unset Ar ENVIRONMENT_VARIABLE -in the shell. .Sh EXIT STATUS .Ex -std fnc .Sh SEE ALSO .Xr fossil 1 , .Xr re_format 7 .Xr sqlite3 1 .Sh AUTHOR .An Mark Jamsek Aq Mt mark@jamsek.com Index: src/fnc.c ================================================================== --- src/fnc.c +++ src/fnc.c @@ -64,11 +64,10 @@ #include #include #include #include "libfossil.h" -#include "settings.h" #define FNC_VERSION VERSION /* cf. Makefile */ /* Utility macros. */ #define MIN(a, b) (((a) < (b)) ? (a) : (b)) @@ -145,18 +144,16 @@ static void usage_timeline(void); static void usage_diff(void); static void usage_tree(void); static void usage_blame(void); static void usage_branch(void); -static void usage_config(void); static int fcli_flag_type_arg_cb(fcli_cliflag const *); static int cmd_timeline(fcli_command const *); static int cmd_diff(fcli_command const *); static int cmd_tree(fcli_command const *); static int cmd_blame(fcli_command const *); static int cmd_branch(fcli_command const *); -static int cmd_config(fcli_command const *); /* * Singleton initialising global configuration and state for app startup. */ static struct fnc_setup { @@ -171,20 +168,19 @@ /* Timeline options. */ struct artifact_types { const char **values; short nitems; - } filter_types; /* Only load commits of . */ + } *filter_types; /* Only load commits of . */ union { const char *zlimit; int limit; } nrecords; /* Number of commits to load. */ const char *filter_tag; /* Only load commits with . */ const char *filter_branch; /* Only load commits from . */ const char *filter_user; /* Only load commits from . */ const char *filter_type; /* Placeholder for repeatable types. */ - const char *glob; /* Only load commits containing glob */ bool utc; /* Display UTC sans user local time. */ /* Diff options. */ const char *context; /* Number of context lines. */ bool ws; /* Ignore whitespace-only changes. */ @@ -198,39 +194,33 @@ const char *sort; /* Lexicographical, MRU, open/closed. */ bool closed; /* Show only closed branches. */ bool open; /* Show only open branches */ bool noprivate; /* Don't show private branches. */ - /* Config options. */ - bool lsconf; /* List all defined settings. */ - bool unset; /* Unset the specified setting. */ - /* Command line flags and help. */ fcli_help_info fnc_help; /* Global help. */ fcli_cliflag cliflags_global[3]; /* Global options. */ - fcli_command cmd_args[7]; /* App commands. */ - fcli_cliflag cliflags_timeline[12]; /* Timeline options. */ + fcli_command cmd_args[6]; /* App commands. */ + fcli_cliflag cliflags_timeline[11]; /* Timeline options. */ fcli_cliflag cliflags_diff[7]; /* Diff options. */ fcli_cliflag cliflags_tree[4]; /* Tree options. */ fcli_cliflag cliflags_blame[6]; /* Blame options. */ fcli_cliflag cliflags_branch[10]; /* Branch options. */ - fcli_cliflag cliflags_config[4]; /* Config options. */ } fnc_init = { NULL, /* cmdarg copy of argv[1] to aid usage/error report. */ NULL, /* sym(bolic name) of commit to open defaults to tip. */ NULL, /* path for tree to open or timeline to find commits. */ 0, /* err fnc error state. */ false, /* hflag if --help is requested. */ false, /* vflag if --version is requested. */ false, /* reverse branch sort/annotation defaults to off. */ - {NULL, 0}, /* filter_types defaults to indiscriminate. */ + NULL, /* filter_types defaults to indiscriminate. */ {0}, /* nrecords defaults to all commits. */ NULL, /* filter_tag defaults to indiscriminate. */ NULL, /* filter_branch defaults to indiscriminate. */ NULL, /* filter_user defaults to indiscriminate. */ - NULL, /* filter_type temp placeholder for filter_types cb. */ - NULL, /* glob filter defaults to off; all commits are shown */ + NULL, /* filter_type temporary placeholder. */ false, /* utc defaults to off (i.e., show user local time). */ NULL, /* context defaults to five context lines. */ false, /* ws defaults to acknowledge whitespace. */ false, /* nocolour defaults to off (i.e., use diff colours). */ false, /* quiet defaults to off (i.e., verbose diff is on). */ @@ -239,16 +229,14 @@ NULL, /* after defaults to any time. */ NULL, /* sort by MRU or open/closed (dflt: lexicographical) */ false, /* closed only branches is off (defaults to all). */ false, /* open only branches is off by (defaults to all). */ false, /* noprivate is off (default to show private branch). */ - false, /* do not list all defined settings by default. */ - false, /* default to set—not unset—the specified setting. */ { /* fnc_help global app help details. */ - "An ncurses browser for Fossil repositories in the terminal.", - NULL, usage + "A read-only ncurses browser for Fossil repositories in the " + "terminal.", NULL, usage }, { /* cliflags_global global app options. */ FCLI_FLAG_BOOL("h", "help", &fnc_init.hflag, "Display program help and usage then exit."), @@ -271,184 +259,171 @@ "Show commit attribution history for each line of a file.", cmd_blame, usage_blame, fnc_init.cliflags_blame}, {"branch", "br\0tag\0", "Show navigable list of repository branches.", cmd_branch, usage_branch, fnc_init.cliflags_branch}, - {"config", "conf\0cfg\0settings\0set\0", - "Configure or view currently available settings.", - cmd_config, usage_config, fnc_init.cliflags_config}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel. */ }, { /* cliflags_timeline timeline command related options. */ FCLI_FLAG_BOOL("C", "no-colour", &fnc_init.nocolour, - "Disable colourised timeline, which is enabled by default on\n " - "supported terminals. Colour can also be toggled with the 'c' " - "\n key binding in timeline view when this option is not used."), + "Disable colourised timeline, which is enabled by default on\n " + "supported terminals. Colour can also be toggled with the 'c' " + "\n key binding in timeline view when this option is not used."), FCLI_FLAG("T", "tag", "", &fnc_init.filter_tag, - "Only display commits with T cards containing ."), + "Only display commits with T cards containing ."), FCLI_FLAG("b", "branch", "", &fnc_init.filter_branch, - "Only display commits that reside on the given ."), + "Only display commits that reside on the given ."), FCLI_FLAG("c", "commit", "", &fnc_init.sym, - "Open the timeline from . Common symbols are:\n" - "\tSHA{1,3} hash\n" - "\tSHA{1,3} unique prefix\n" - "\tbranch\n" - "\ttag:TAG\n" - "\troot:BRANCH\n" - "\tISO8601 date\n" - "\tISO8601 timestamp\n" - "\t{tip,current,prev,next}\n " - "For a complete list of symbols see Fossil's Check-in Names:\n " - "https://fossil-scm.org/home/doc/trunk/www/checkin_names.wiki"), - FCLI_FLAG_CSTR("f", "filter", "", &fnc_init.glob, - "Populate the timeline with commits containing in the commit" - "\n comment, user, or branch field."), + "Open the timeline from . Common symbols are:\n" + "\tSHA{1,3} hash\n" + "\tSHA{1,3} unique prefix\n" + "\tbranch\n" + "\ttag:TAG\n" + "\troot:BRANCH\n" + "\tISO8601 date\n" + "\tISO8601 timestamp\n" + "\t{tip,current,prev,next}\n " + "For a complete list of symbols see Fossil's Check-in Names:\n " + "https://fossil-scm.org/home/doc/trunk/www/checkin_names.wiki"), FCLI_FLAG_BOOL("h", "help", NULL, - "Display timeline command help and usage."), + "Display timeline command help and usage."), FCLI_FLAG("n", "limit", "", &fnc_init.nrecords.zlimit, - "Limit display to latest commits; defaults to entire history " - "of\n current checkout. Negative values are a no-op."), + "Limit display to latest commits; defaults to entire history " + "of\n current checkout. Negative values are a no-op."), FCLI_FLAG_X("t", "type", "", &fnc_init.filter_type, fcli_flag_type_arg_cb, - "Only display commits. Valid types are:\n" - "\tci - check-in\n" - "\tw - wiki\n" - "\tt - ticket\n" - "\te - technote\n" - "\tf - forum post\n" - "\tg - tag artifact\n" - " n.b. This is a repeatable flag (e.g., -t ci -t w)."), + "Only display commits. Valid types are:\n" + "\tci - check-in\n" + "\tw - wiki\n" + "\tt - ticket\n" + "\te - technote\n" + "\tf - forum post\n" + " n.b. This is a repeatable flag (e.g., -t ci -t w)."), FCLI_FLAG("u", "username", "", &fnc_init.filter_user, - "Only display commits authored by ."), + "Only display commits authored by ."), FCLI_FLAG_BOOL("z", "utc", &fnc_init.utc, - "Use UTC (instead of local) time."), + "Use UTC (instead of local) time."), fcli_cliflag_empty_m }, /* End cliflags_timeline. */ { /* cliflags_diff diff command related options. */ FCLI_FLAG_BOOL("C", "no-colour", &fnc_init.nocolour, - "Disable coloured diff output, which is enabled by default on\n " - "supported terminals. Colour can also be toggled with the 'c' " - "\n key binding in diff view when this option is not used."), + "Disable coloured diff output, which is enabled by default on\n " + "supported terminals. Colour can also be toggled with the 'c' " + "\n key binding in diff view when this option is not used."), FCLI_FLAG_BOOL("h", "help", NULL, - "Display diff command help and usage."), + "Display diff command help and usage."), FCLI_FLAG_BOOL("i", "invert", &fnc_init.invert, - "Invert difference between artifacts. Inversion can also be " - "toggled\n with the 'i' key binding in diff view."), + "Invert difference between artifacts. Inversion can also be " + "toggled\n with the 'i' key binding in diff view."), FCLI_FLAG_BOOL("q", "quiet", &fnc_init.quiet, - "Disable verbose diff output; that is, do not output complete" - " content\n of newly added or deleted files. Verbosity can also" - " be toggled with\n the 'v' key binding in diff view."), + "Disable verbose diff output; that is, do not output complete" + " content\n of newly added or deleted files. Verbosity can also" + " be toggled with\n the 'v' key binding in diff view."), FCLI_FLAG_BOOL("w", "whitespace", &fnc_init.ws, - "Ignore whitespace-only changes when displaying diff. This option " - "can\n also be toggled with the 'w' key binding in diff view."), + "Ignore whitespace-only changes when displaying diff. This option " + "can\n also be toggled with the 'w' key binding in diff view."), FCLI_FLAG("x", "context", "", &fnc_init.context, - "Show context lines when displaying diff; is capped at 64." - "\n Negative values are a no-op."), + "Show context lines when displaying diff; is capped at 64." + "\n Negative values are a no-op."), fcli_cliflag_empty_m }, /* End cliflags_diff. */ { /* cliflags_tree tree command related options. */ FCLI_FLAG_BOOL("C", "no-colour", &fnc_init.nocolour, - "Disable coloured output, which is enabled by default on supported" - "\n terminals. Colour can also be toggled with the 'c' key " - "binding when\n this option is not used."), + "Disable coloured output, which is enabled by default on supported" + "\n terminals. Colour can also be toggled with the 'c' key " + "binding when\n this option is not used."), FCLI_FLAG("c", "commit", "", &fnc_init.sym, - "Display tree that reflects repository state as at .\n" - " Common symbols are:" - "\n\tSHA{1,3} hash\n" - "\tSHA{1,3} unique prefix\n" - "\tbranch\n" - "\ttag:TAG\n" - "\troot:BRANCH\n" - "\tISO8601 date\n" - "\tISO8601 timestamp\n" - "\t{tip,current,prev,next}\n " - "For a complete list of symbols see Fossil's Check-in Names:\n " - "https://fossil-scm.org/home/doc/trunk/www/checkin_names.wiki"), + "Display tree that reflects repository state as at .\n" + " Common symbols are:" + "\n\tSHA{1,3} hash\n" + "\tSHA{1,3} unique prefix\n" + "\tbranch\n" + "\ttag:TAG\n" + "\troot:BRANCH\n" + "\tISO8601 date\n" + "\tISO8601 timestamp\n" + "\t{tip,current,prev,next}\n " + "For a complete list of symbols see Fossil's Check-in Names:\n " + "https://fossil-scm.org/home/doc/trunk/www/checkin_names.wiki"), FCLI_FLAG_BOOL("h", "help", NULL, - "Display tree command help and usage."), + "Display tree command help and usage."), fcli_cliflag_empty_m }, /* End cliflags_tree. */ { /* cliflags_blame blame command related options. */ FCLI_FLAG_BOOL("C", "no-colour", &fnc_init.nocolour, - "Disable coloured output, which is enabled by default on supported" - "\n terminals. Colour can also be toggled with the 'c' key " - "binding when\n this option is not used."), + "Disable coloured output, which is enabled by default on supported" + "\n terminals. Colour can also be toggled with the 'c' key " + "binding when\n this option is not used."), FCLI_FLAG("c", "commit", "", &fnc_init.sym, - "Start blame of specified file from . Common symbols are:\n" - "\tSHA{1,3} hash\n" - "\tSHA{1,3} unique prefix\n" - "\tbranch\n" - "\ttag:TAG\n" - "\troot:BRANCH\n" - "\tISO8601 date\n" - "\tISO8601 timestamp\n" - "\t{tip,current,prev,next}\n " - "For a complete list of symbols see Fossil's Check-in Names:\n " - "https://fossil-scm.org/home/doc/trunk/www/checkin_names.wiki"), + "Start blame of specified file from . Common symbols are:\n" + "\tSHA{1,3} hash\n" + "\tSHA{1,3} unique prefix\n" + "\tbranch\n" + "\ttag:TAG\n" + "\troot:BRANCH\n" + "\tISO8601 date\n" + "\tISO8601 timestamp\n" + "\t{tip,current,prev,next}\n " + "For a complete list of symbols see Fossil's Check-in Names:\n " + "https://fossil-scm.org/home/doc/trunk/www/checkin_names.wiki"), FCLI_FLAG_BOOL("h", "help", NULL, - "Display blame command help and usage."), - FCLI_FLAG("n", "limit", "", &fnc_init.nrecords.zlimit, + "Display blame command help and usage."), + FCLI_FLAG("n", "limit", "", &fnc_init.nrecords.zlimit, "Limit depth of blame history to commits or seconds. Denote the" "\n latter by postfixing 's' (e.g., 30s). Useful for large files" " with\n extensive history. Persists for the duration of the " "session."), - FCLI_FLAG_BOOL("r", "reverse", &fnc_init.reverse, + FCLI_FLAG_BOOL("r", "reverse", &fnc_init.reverse, "Reverse annotate the file starting from a historical commit. " "Rather\n than show the most recent change of each line, show " "the first time\n each line was modified after the specified " "commit. Requires -c|--commit."), fcli_cliflag_empty_m }, /* End cliflags_blame. */ { /* cliflags_branch branch command related options. */ FCLI_FLAG_BOOL("C", "no-colour", &fnc_init.nocolour, - "Disable coloured output, which is enabled by default on supported" - "\n terminals. Colour can also be toggled with the 'c' key " - "binding when\n this option is not used."), + "Disable coloured output, which is enabled by default on supported" + "\n terminals. Colour can also be toggled with the 'c' key " + "binding when\n this option is not used."), FCLI_FLAG("a", "after", "", &fnc_init.after, - "Show branches with last activity occuring after , which is\n" - " expected to be either an ISO8601 (e.g., 2020-10-10) or " - "unambiguous\n DD/MM/YYYY or MM/DD/YYYY formatted date."), + "Show branches with last activity occuring after , which is\n" + " expected to be either an ISO8601 (e.g., 2020-10-10) or " + "unambiguous\n DD/MM/YYYY or MM/DD/YYYY formatted date."), FCLI_FLAG("b", "before", "", &fnc_init.before, - "Show branches with last activity occuring before , which is" - "\n expected to be either an ISO8601 (e.g., 2020-10-10) or " - "unambiguous\n DD/MM/YYYY or MM/DD/YYYY formatted date."), - FCLI_FLAG_BOOL("c", "closed", &fnc_init.closed, + "Show branches with last activity occuring before , which is" + "\n expected to be either an ISO8601 (e.g., 2020-10-10) or " + "unambiguous\n DD/MM/YYYY or MM/DD/YYYY formatted date."), + FCLI_FLAG_BOOL("c", "closed", &fnc_init.closed, "Show closed branches only. Open and closed branches are listed by " "\n default."), FCLI_FLAG_BOOL("h", "help", NULL, - "Display branch command help and usage."), - FCLI_FLAG_BOOL("o", "open", &fnc_init.open, + "Display branch command help and usage."), + FCLI_FLAG_BOOL("o", "open", &fnc_init.open, "Show open branches only. Open and closed branches are listed by " "\n default."), - FCLI_FLAG_BOOL("p", "no-private", &fnc_init.noprivate, + FCLI_FLAG_BOOL("p", "no-private", &fnc_init.noprivate, "Do not show private branches, which are otherwise included in the" "\n list of displayed branches by default."), - FCLI_FLAG_BOOL("r", "reverse", &fnc_init.reverse, + FCLI_FLAG_BOOL("r", "reverse", &fnc_init.reverse, "Reverse the order in which branches are displayed."), FCLI_FLAG("s", "sort", "", &fnc_init.sort, - "Sort branches by . Available options are:\n" - "\tmru - most recently used\n" - "\tstate - open/closed state\n " - "Branches are sorted in lexicographical order by default."), + "Sort branches by . Available options are:\n" + "\tmru - most recently used\n" + "\tstate - open/closed state\n " + "Branches are sorted in lexicographical order by default."), fcli_cliflag_empty_m }, /* End cliflags_blame. */ - - { /* cliflags_config config command related options. */ - FCLI_FLAG_BOOL("h", "help", NULL, - "Display config command help and usage."), - FCLI_FLAG_BOOL(NULL, "ls", &fnc_init.lsconf, - "Display a list of all currently defined settings."), - FCLI_FLAG_BOOL("u", "unset", &fnc_init.unset, - "Unset (i.e., remove) the specified repository setting."), - fcli_cliflag_empty_m - }, /* End cliflags_tree. */ - +}; + +enum fsl_list_object { + FNC_ARTIFACT_OBJ, + FNC_COLOUR_OBJ }; enum date_string { ISO8601_DATE_ONLY = 10, ISO8601_TIMESTAMP = 20 @@ -474,29 +449,22 @@ SEARCH_COMPLETE, SEARCH_NO_MATCH, SEARCH_FOR_END }; -/* - * User-definable setting that must map to SETTINGS in "settings.h". - */ -enum fnc_colour_obj { - FNC_COLOUR_COMMIT = 1, - FNC_COLOUR_USER, - FNC_COLOUR_DATE, - FNC_COLOUR_DIFF_META, - FNC_COLOUR_DIFF_MINUS, - FNC_COLOUR_DIFF_PLUS, - FNC_COLOUR_DIFF_CHUNK, - FNC_COLOUR_DIFF_TAGS, - FNC_COLOUR_TREE_LINK, - FNC_COLOUR_TREE_DIR, - FNC_COLOUR_TREE_EXEC, - FNC_COLOUR_BRANCH_OPEN, - FNC_COLOUR_BRANCH_CLOSED, - FNC_COLOUR_BRANCH_CURRENT, - FNC_COLOUR_BRANCH_PRIVATE +enum fnc_colours { + FNC_DIFF_META = 1, + FNC_DIFF_MINUS, + FNC_DIFF_PLUS, + FNC_DIFF_CHNK, + FNC_TREE_LINK, + FNC_TREE_DIR, + FNC_TREE_EXEC, + FNC_COMMIT_ID, + FNC_USER_STR, + FNC_DATE_STR, + FNC_TAGS_STR }; enum fnc_diff_type { FNC_DIFF_CKOUT, FNC_DIFF_COMMIT, @@ -503,15 +471,17 @@ FNC_DIFF_BLOB, FNC_DIFF_WIKI }; struct fnc_colour { - STAILQ_ENTRY(fnc_colour) entries; regex_t regex; uint8_t scheme; }; -STAILQ_HEAD(fnc_colours, fnc_colour); + +struct fsl_list_state { + enum fsl_list_object obj; +}; struct fnc_commit_artifact { fsl_buffer wiki; fsl_buffer pwiki; fsl_list changeset; @@ -619,13 +589,13 @@ /* * XXX Is there a more elegant solution to retrieving return codes from * thread functions while pinging between, but before we join, threads? */ int rc; + bool tree_open; bool endjmp; bool timeline_end; - bool needs_reset; sig_atomic_t *quit; pthread_cond_t commit_consumer; pthread_cond_t commit_producer; }; @@ -635,11 +605,11 @@ struct commit_entry *first_commit_onscreen; struct commit_entry *last_commit_onscreen; struct commit_entry *selected_commit; struct commit_entry *matched_commit; struct commit_entry *search_commit; - struct fnc_colours colours; + fsl_list colours; const char *curr_ckout_uuid; char *path; /* Match commits involving path. */ int selected_idx; sig_atomic_t quit; pthread_t thread_id; @@ -657,11 +627,11 @@ struct fnc_diff_view_state { struct fnc_view *timeline_view; struct fnc_commit_artifact *selected_commit; struct fnc_pathlist_head *paths; fsl_buffer buf; - struct fnc_colours colours; + fsl_list colours; FILE *f; fsl_uuid_str id1; fsl_uuid_str id2; int first_line_onscreen; int last_line_onscreen; @@ -686,11 +656,11 @@ struct fnc_tree_object *tree; /* Currently displayed tree */ struct fnc_tree_entry *first_entry_onscreen; struct fnc_tree_entry *last_entry_onscreen; struct fnc_tree_entry *selected_entry; struct fnc_tree_entry *matched_entry; - struct fnc_colours colours; + fsl_list colours; char *tree_label; /* Headline string. */ fsl_uuid_str commit_id; fsl_id_t rid; int ndisplayed; int selected_idx; @@ -745,11 +715,11 @@ struct fnc_blame_view_state { struct fnc_blame blame; struct fnc_commit_id_queue blamed_commits; struct fnc_commit_qid *blamed_commit; struct fnc_commit_artifact *selected_commit; - struct fnc_colours colours; + fsl_list colours; fsl_uuid_str commit_id; char *path; int first_line_onscreen; int last_line_onscreen; int selected_line; @@ -781,11 +751,10 @@ struct fnc_branchlist_head branches; struct fnc_branchlist_entry *first_branch_onscreen; struct fnc_branchlist_entry *last_branch_onscreen; struct fnc_branchlist_entry *matched_branch; struct fnc_branchlist_entry *selected_branch; - struct fnc_colours colours; const char *branch_glob; double dateline; int branch_flags; #define BRANCH_LS_CLOSED_ONLY 0x001 /* Show closed branches only. */ #define BRANCH_LS_OPEN_ONLY 0x002 /* Show open branches only. */ @@ -847,11 +816,11 @@ static void fnc_show_version(void); static int init_curses(void); static struct fnc_view *view_open(int, int, int, int, enum fnc_view_id); static int open_timeline_view(struct fnc_view *, fsl_id_t, - const char *, const char *); + const char *); static int view_loop(struct fnc_view *); static int show_timeline_view(struct fnc_view *); static void *tl_producer_thread(void *); static int block_main_thread_signals(void); static int build_commits(struct fnc_tl_thread_cx *); @@ -914,12 +883,11 @@ const fsl_card_F *, fsl_id_t, const fsl_card_F *, fsl_ckout_change_e, int, int, int, enum fnc_diff_type); static int show_diff(struct fnc_view *); static int write_diff(struct fnc_view *, char *); -static int match_line(const char *, regex_t *, size_t, - regmatch_t *); +static int match_line(const void *, const void *); static int write_matched_line(int *, const char *, int, int, WINDOW *, regmatch_t *); static void draw_vborder(struct fnc_view *); static int diff_input_handler(struct fnc_view **, struct fnc_view *, int); @@ -1019,11 +987,11 @@ static int branch_search_next(struct fnc_view *); static int branch_search_init(struct fnc_view *); static int match_branchlist_entry(struct fnc_branchlist_entry *, regex_t *); static int close_branch_view(struct fnc_view *); -static void fnc_free_branches(struct fnc_branchlist_head *); +static void fnc_free_branches(struct fnc_branch_view_state *); static void fnc_branch_close(struct fnc_branch *); static void view_set_child(struct fnc_view *, struct fnc_view *); static int view_close_child(struct fnc_view *); static int close_tree_view(struct fnc_view *); static int close_timeline_view(struct fnc_view *); @@ -1033,33 +1001,22 @@ static bool screen_is_shared(struct fnc_view *); static void fnc_resizeterm(void); static int join_tl_thread(struct fnc_tl_view_state *); static void fnc_free_commits(struct commit_queue *); static void fnc_commit_artifact_close(struct fnc_commit_artifact*); -static int fsl_file_artifact_free(void *, void *); +static int fsl_list_object_free(void *, void *); static void sigwinch_handler(int); static void sigpipe_handler(int); static void sigcont_handler(int); static int strtonumcheck(int *, const char *, const int, const int); static int fnc_date_to_mtime(double *, const char *, int); static char *fnc_strsep (char **, const char *); -static int set_colours(struct fnc_colours *, enum fnc_view_id); -static int set_colour_scheme(struct fnc_colours *, - const int (*)[2], const char **, int); -static int init_colour(enum fnc_colour_obj); -static char *fnc_conf_get(enum fnc_colour_obj, bool); -static int default_colour(enum fnc_colour_obj); -static int fnc_conf_set(enum fnc_colour_obj, const char *, bool); -static void free_colours(struct fnc_colours *); +static int set_colours(fsl_list *, enum fnc_view_id vid); +static int match_colour(const void *, const void *); static bool fnc_home(struct fnc_view *); -static int fnc_conf_ls_settings(bool); -static int fnc_conf_str2enum(const char *); -static const char *fnc_conf_enum2str(int); -static struct fnc_colour *get_colour(struct fnc_colours *, int); -static struct fnc_colour *match_colour(struct fnc_colours *, - const char *); +static struct fnc_colour *get_colour(fsl_list *, int); static struct fnc_tree_entry *get_tree_entry(struct fnc_tree_object *, int); static struct fnc_tree_entry *find_tree_entry(struct fnc_tree_object *, const char *, size_t); @@ -1068,17 +1025,20 @@ { fcli_command *cmd = NULL; char *path = NULL; int rc = 0; + fnc_init.filter_types = + (struct artifact_types *)fsl_malloc(sizeof(struct artifact_types)); + fnc_init.filter_types->values = fsl_malloc(sizeof(char *)); + fnc_init.filter_types->nitems = 0; + if (!setlocale(LC_CTYPE, "")) fsl_fprintf(stderr, "[!] Warning: Can't set locale.\n"); fnc_init.cmdarg = argv[1]; /* Which cmd to show usage if needed. */ -#if DEBUG fcli.clientFlags.verbose = 2; /* Verbose error reporting. */ -#endif fcli.cliFlags = fnc_init.cliflags_global; fcli.appHelp = &fnc_init.fnc_help; rc = fcli_setup(argc, argv); if (rc) goto end; @@ -1138,11 +1098,11 @@ endwin(); if (rc) { if (rc == FCLI_RC_HELP) rc = 0; else if (rc == FSL_RC_BREAK) { - const fsl_cx *const f = fcli_cx(); + fsl_cx *f = fcli_cx(); const char *errstr; fsl_error_get(&f->error, &errstr, NULL); fsl_fprintf(stdout, "%s", errstr); fcli_err_reset(); /* For fcli_end_of_main() */ rc = 0; @@ -1154,13 +1114,13 @@ static int cmd_timeline(fcli_command const *argv) { struct fnc_view *v; - fsl_cx *const f = fcli_cx(); - char *glob = NULL, *path = NULL; - fsl_id_t rid = 0; + fsl_cx *f = fcli_cx(); + fsl_id_t rid = -1; + char *path = NULL; int rc = 0; rc = fcli_process_flags(argv->flags); if (rc || (rc = fcli_has_unused_flags(false))) return rc; @@ -1170,18 +1130,16 @@ fnc_init.nrecords.zlimit, INT_MIN, INT_MAX))) return rc; if (fnc_init.sym != NULL) { rc = fsl_sym_to_rid(f, fnc_init.sym, FSL_SATYPE_CHECKIN, &rid); - if (rc || !rid) + if (rc || rid < 0) return RC(FSL_RC_TYPE, "artifact [%s] not resolvable to a commit", fnc_init.sym); } - if (fnc_init.glob) - glob = fsl_strdup(fnc_init.glob); if (fnc_init.path) path = fsl_strdup(fnc_init.path); else rc = map_repo_path(&path); if (!rc) @@ -1188,18 +1146,17 @@ rc = init_curses(); if (rc) goto end; v = view_open(0, 0, 0, 0, FNC_VIEW_TIMELINE); if (v == NULL) { - rc = RC(FSL_RC_ERROR, "%s", "view_open"); + RC(FSL_RC_ERROR, "%s", "view_open"); goto end; } - rc = open_timeline_view(v, rid, path, glob); + rc = open_timeline_view(v, rid, path); if (!rc) rc = view_loop(v); end: - fsl_free(glob); fsl_free(path); return rc; } /* @@ -1505,23 +1462,20 @@ keypad(view->window, TRUE); return view; } static int -open_timeline_view(struct fnc_view *view, fsl_id_t rid, const char *path, - const char *glob) +open_timeline_view(struct fnc_view *view, fsl_id_t rid, const char *path) { struct fnc_tl_view_state *s = &view->state.timeline; - fsl_cx *const f = fcli_cx(); + fsl_cx *f = fcli_cx(); fsl_db *db = fsl_cx_db_repo(f); fsl_buffer sql = fsl_buffer_empty; char *startdate = NULL; fsl_id_t idtag = 0; int idx, rc = 0; - f->clientState.state = &s->thread_cx; - if (path != s->path) { fsl_free(s->path); s->path = fsl_strdup(path); if (s->path == NULL) return RC(FSL_RC_ERROR, "%s", "fsl_strdup"); @@ -1541,11 +1495,11 @@ /* s->selected_idx = 0; */ /* Unnecessary? */ TAILQ_INIT(&s->commits.head); s->commits.ncommits = 0; - if (rid) + if (rid != -1) startdate = fsl_mprintf("(SELECT mtime FROM event " "WHERE objid=%d)", rid); else fsl_ckout_version_info(f, NULL, &s->curr_ckout_uuid); @@ -1554,16 +1508,13 @@ * valid repository path in the repository tree as at either the * latest check-in or the specified commit. */ if (s->curr_ckout_uuid == NULL && path[1]) { fsl_deck d = fsl_deck_empty; - fsl_uuid_str id = NULL; bool ispath = false; - if (rid) - id = fsl_rid_to_uuid(f, rid); rc = fsl_deck_load_sym(f, &d, fnc_init.sym ? fnc_init.sym : - id ? id : "tip", FSL_SATYPE_CHECKIN); + "tip", FSL_SATYPE_CHECKIN); fsl_deck_F_rewind(&d); if (fsl_deck_F_search(&d, path + 1 /* Slash */) == NULL) { const fsl_card_F *cf; fsl_deck_F_next(&d, &cf); do { @@ -1575,11 +1526,10 @@ } } while (cf); } else ispath = true; fsl_deck_finalize(&d); - fsl_free(id); if (!ispath) return RC(FSL_RC_NOT_FOUND, "'%s' invalid path in [%s]", path + 1, fnc_init.sym ? fnc_init.sym : "tip"); } @@ -1593,11 +1543,11 @@ "%s", "pthread_cond_init"); goto end; } fsl_buffer_appendf(&sql, "SELECT " - /* 0 */"uuid, " + /* 0 */"uuid, " /* 1 */"datetime(event.mtime%s), " /* 2 */"coalesce(euser, user), " /* 3 */"rid AS rid, " /* 4 */"event.type AS eventtype, " /* 5 */"(SELECT group_concat(substr(tagname,5), ',') " @@ -1605,79 +1555,65 @@ "AND tag.tagid=tagxref.tagid AND tagxref.rid=blob.rid " "AND tagxref.tagtype > 0) as tags, " /*6*/"coalesce(ecomment, comment) AS comment FROM event JOIN blob " "WHERE blob.rid=event.objid", fnc_init.utc ? "" : ", 'localtime'"); - if (fnc_init.filter_types.nitems) { + if (fnc_init.filter_types->nitems) { fsl_buffer_appendf(&sql, " AND ("); - for (idx = 0; idx < fnc_init.filter_types.nitems; ++idx) + for (idx = 0; idx < fnc_init.filter_types->nitems; ++idx) { fsl_buffer_appendf(&sql, " eventtype=%Q%s", - fnc_init.filter_types.values[idx], (idx + 1) < - fnc_init.filter_types.nitems ? " OR " : ")"); + fnc_init.filter_types->values[idx], (idx + 1) < + fnc_init.filter_types->nitems ? " OR " : ")"); + /* This produces a double-free? */ + /* fsl_free((char *)fnc_init.filter_types->values[idx]); */ + fnc_init.filter_types->values[idx] = NULL; + } + fsl_free(fnc_init.filter_types->values); + fsl_free(fnc_init.filter_types); } if (fnc_init.filter_branch) { idtag = fsl_db_g_id(db, 0, - "SELECT tagid FROM tag WHERE tagname='sym-%q'", - fnc_init.filter_branch); + "SELECT tagid FROM tag WHERE tagname='sym-%q'", + fnc_init.filter_branch); if (idtag > 0) fsl_buffer_appendf(&sql, - " AND EXISTS(SELECT 1 FROM tagxref" - " WHERE tagid=%"FSL_ID_T_PFMT - " AND tagtype > 0 AND rid=blob.rid)", idtag); + " AND EXISTS(SELECT 1 FROM tagxref" + " WHERE tagid=%"FSL_ID_T_PFMT + " AND tagtype > 0 AND rid=blob.rid)", idtag); else { rc = RC(FSL_RC_NOT_FOUND, "Invalid branch name [%s]", fnc_init.filter_branch); goto end; } } if (fnc_init.filter_tag) { idtag = fsl_db_g_id(db, 0, - "SELECT tagid FROM tag WHERE tagname GLOB 'sym-%q'", - fnc_init.filter_tag); - if (idtag == 0) + "SELECT tagid FROM tag WHERE tagname GLOB 'sym-%q'", + fnc_init.filter_tag); + if (idtag == 0) idtag = fsl_db_g_id(db, 0, "SELECT tagid FROM tag WHERE tagname='%q'", fnc_init.filter_tag); if (idtag > 0) fsl_buffer_appendf(&sql, - " AND EXISTS(SELECT 1 FROM tagxref" - " WHERE tagid=%"FSL_ID_T_PFMT - " AND tagtype > 0 AND rid=blob.rid)", idtag); + " AND EXISTS(SELECT 1 FROM tagxref" + " WHERE tagid=%"FSL_ID_T_PFMT + " AND tagtype > 0 AND rid=blob.rid)", idtag); else { rc = RC(FSL_RC_NOT_FOUND, "Invalid tag [%s]", fnc_init.filter_tag); goto end; } } if (fnc_init.filter_user) if ((rc = fsl_buffer_appendf(&sql, - " AND coalesce(euser, user) GLOB lower('*%q*')", - fnc_init.filter_user))) - goto end; - - if (glob) { - /* Filter commits on comment, user, and branch name. */ - char *like = fsl_mprintf("%%%%%s%%%%", glob); - idtag = fsl_db_g_id(db, 0, - "SELECT tagid FROM tag WHERE tagname LIKE 'sym-%q'", - like); - rc = fsl_buffer_appendf(&sql, - " AND (coalesce(ecomment, comment) LIKE %Q " - " OR coalesce(euser, user) LIKE %Q%c", - like, like, idtag ? ' ' : ')'); - if (!rc && idtag > 0) - rc = fsl_buffer_appendf(&sql, - " OR EXISTS(SELECT 1 FROM tagxref" - " WHERE tagid=%"FSL_ID_T_PFMT - " AND tagtype > 0 AND rid=blob.rid))", idtag); - fsl_free(like); - if (rc) - goto end; - } + " AND coalesce(euser, user) GLOB lower('*%q*')", + fnc_init.filter_user))) + goto end; if (startdate) { fsl_buffer_appendf(&sql, " AND event.mtime <= %s", startdate); fsl_free(startdate); } @@ -1722,20 +1658,22 @@ if (rc) { rc = RC(rc, "%s", "fsl_db_prepare"); goto end; } rc = fsl_stmt_step(s->thread_cx.q); - switch (rc) { - case FSL_RC_STEP_ROW: - rc = 0; - break; - case FSL_RC_STEP_ERROR: - rc = RC(rc, "%s", "fsl_stmt_step"); - goto end; - case FSL_RC_STEP_DONE: - rc = RC(FSL_RC_BREAK, "%s", "no matching records"); - goto end; + if (rc) { + switch (rc) { + case FSL_RC_STEP_ROW: + rc = 0; + break; + case FSL_RC_STEP_ERROR: + rc = RC(rc, "%s", "fsl_stmt_step"); + goto end; + case FSL_RC_STEP_DONE: + rc = RC(FSL_RC_BREAK, "%s", "no matching records"); + goto end; + } } s->colour = !fnc_init.nocolour && has_colors(); s->thread_cx.rc = 0; s->thread_cx.db = db; @@ -1748,20 +1686,17 @@ s->thread_cx.selected_commit = &s->selected_commit; s->thread_cx.searching = &view->searching; s->thread_cx.search_status = &view->search_status; s->thread_cx.regex = &view->regex; s->thread_cx.path = s->path; - s->thread_cx.needs_reset = false; - if (s->colour) { - STAILQ_INIT(&s->colours); - rc = set_colours(&s->colours, FNC_VIEW_TIMELINE); - } + if (s->colour) + set_colours(&s->colours, FNC_VIEW_TIMELINE); end: fsl_buffer_clear(&sql); if (rc) { - view_close(view); + close_timeline_view(view); if (db->error.code) rc = fsl_cx_uplift_db_error(f, db); } return rc; } @@ -2011,22 +1946,21 @@ } static int build_commits(struct fnc_tl_thread_cx *cx) { - int rc = 0; + int rc = 0; - if (cx->needs_reset) { + if (cx->tree_open) { /* - * XXX If a {tree,branch} view has been opened with the '{t,b}' - * key binding, there may be cached statements that necessitate - * the commit builder statement being reset otherwise one of the - * SQLite3 APIs down the fsl_stmt_step() call stack fails. This - * is irrespective of whether fsl_db_prepare_cached() was used. + * XXX If a tree has been opened with the 't' key binding, the + * commit builder statement needs to be reset otherwise one of + * the SQLite3 APIs down the fsl_stmt_step() call stack fails, + * irrespective of whether fsl_db_prepare_cached() is called. */ - fsl_size_t loaded = cx->commits->ncommits + 1; - cx->needs_reset = false; + fsl_size_t loaded = cx->q->rowCount; + cx->tree_open = false; rc = fsl_stmt_reset(cx->q); if (rc) return RC(rc, "%s", "fsl_stmt_reset"); while (loaded--) if ((rc = fsl_stmt_step(cx->q)) != FSL_RC_STEP_ROW) @@ -2096,11 +2030,11 @@ * eventually be disposed of with fnc_commit_artifact_close(). */ static int commit_builder(struct fnc_commit_artifact **ptr, fsl_id_t rid, fsl_stmt *q) { - fsl_cx *const f = fcli_cx(); + fsl_cx *f = fcli_cx(); fsl_db *db = fsl_needs_repo(f); struct fnc_commit_artifact *commit = NULL; fsl_buffer buf = fsl_buffer_empty; const char *comment, *prefix, *type; int rc = 0; @@ -2335,24 +2269,24 @@ goto end; werase(view->window); if (screen_is_shared(view)) - wattron(view->window, A_REVERSE); + wstandout(view->window); if (s->colour) - c = get_colour(&s->colours, FNC_COLOUR_COMMIT); + c = get_colour(&s->colours, FNC_COMMIT_ID); if (c) wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); waddwstr(view->window, wcstr); + if (c) + wattr_off(view->window, COLOR_PAIR(c->scheme), NULL); while (wstrlen < view->ncols) { waddch(view->window, ' '); ++wstrlen; } - if (c) - wattr_off(view->window, COLOR_PAIR(c->scheme), NULL); if (screen_is_shared(view)) - wattroff(view->window, A_REVERSE); + wstandend(view->window); fsl_free(wcstr); if (view->nlines <= 1) goto end; /* Parse commits to be written on screen for the longest username. */ @@ -2557,11 +2491,11 @@ date = fsl_strdup(commit->timestamp); while (!fsl_isspace(date[i++])) {} date[i] = '\0'; col_pos = MIN(view->ncols, ISO8601_DATE_ONLY + 1); if (s->colour) - c = get_colour(&s->colours, FNC_COLOUR_DATE); + c = get_colour(&s->colours, FNC_DATE_STR); if (c) wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); waddnstr(view->window, date, col_pos); if (c) wattr_off(view->window, COLOR_PAIR(c->scheme), NULL); @@ -2569,11 +2503,11 @@ goto end; /* If enough columns, write abbreviated commit hash. */ if (view->ncols >= 110) { if (s->colour) - c = get_colour(&s->colours, FNC_COLOUR_COMMIT); + c = get_colour(&s->colours, FNC_COMMIT_ID); if (c) wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); wprintw(view->window, "%.9s ", commit->uuid); if (c) wattr_off(view->window, COLOR_PAIR(c->scheme), NULL); @@ -2595,11 +2529,11 @@ rc = formatln(&usr_wcstr, &usrlen, user, view->ncols - col_pos, col_pos); if (rc) goto end; if (s->colour) - c = get_colour(&s->colours, FNC_COLOUR_USER); + c = get_colour(&s->colours, FNC_USER_STR); if (c) wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); waddwstr(view->window, usr_wcstr); pad = fsl_mprintf("%*c", max_usrlen - usrlen + 2, ' '); waddstr(view->window, pad); @@ -2797,17 +2731,14 @@ {""}, {""}, /* Timeline */ {" <,, ", " ❬<❭❬,❭ "}, {" >,. ", " ❬>❭❬.❭ "}, {" Enter,Space ", " ❬Enter❭❬Space❭ "}, - {" b ", " ❬b❭ "}, - {" F ", " ❬F❭ "}, {" t ", " ❬t❭ "}, {""}, {""}, /* Diff */ {" Space ", " ❬Space❭ "}, - {" b ", " ❬b❭ "}, {" i ", " ❬i❭ "}, {" v ", " ❬v❭ "}, {" w ", " ❬w❭ "}, {" -,_ ", " ❬-❭❬_❭ "}, {" +,= ", " ❬+❭❬=❭ "}, @@ -2815,21 +2746,19 @@ {" C-j,J,>,. ", " ❬C-j❭❬J❭❬>❭❬.❭ "}, {""}, {""}, /* Tree */ {" l,Enter, ", " ❬→❭❬l❭❬Enter❭ "}, {" h,, ", " ❬←❭❬h❭❬⌫❭ "}, - {" b ", " ❬b❭ "}, {" i ", " ❬i❭ "}, {" t ", " ❬t❭ "}, {""}, {""}, /* Blame */ {" Space ", " ❬Space❭ "}, {" Enter ", " ❬Enter❭ "}, {" b ", " ❬b❭ "}, {" p ", " ❬p❭ "}, {" B ", " ❬B❭ "}, - {" T ", " ❬T❭ "}, {""}, {""}, /* Branch */ {" Enter,Space ", " ❬Enter❭❬Space❭ "}, {" d ", " ❬d❭ "}, {" i ", " ❬i❭ "}, @@ -2849,28 +2778,25 @@ "Scroll down one page", "Jump to first line or start of the view", "Jump to last line or end of the view", "Switch focus between open views", "Toggle coloured output", - "Toggle fullscreen", + "Toggle fullscreen", "Open prompt to enter search term (not available in this view)", "Find next line or token matching the current search term", "Find previous line or token matching the current search term", - "Quit the active view", + "Quit the active view", "Quit the program", "", "Timeline", "Move selection cursor up one commit", "Move selection cursor down one commit", "Open diff view of the selected commit", - "Open and populate branch view with all repository branches", - "Open prompt to enter term with which to filter new timeline view", "Display a tree reflecting the state of the selected commit", "", "Diff", "Scroll down one page of diff output", - "Open and populate branch view with all repository branches", "Toggle inversion of diff output", "Toggle verbosity of diff output", "Toggle ignore whitespace-only changes in diff", "Decrease the number of context lines", "Increase the number of context lines", @@ -2878,29 +2804,27 @@ "Display diff of previous (older) commit in the timeline", "", "Tree", "Move into the selected directory", "Return to the parent directory", - "Open and populate branch view with all repository branches", - "Toggle display of file artifact SHA hash ID", + "Toggle display of file artifact SHA hashes", "Display timeline of all commits modifying the selected entry", "", "Blame", "Scroll down one page", "Display the diff of the commit corresponding to the selected line", "Blame the version of the file found in the selected line's commit", "Blame the version of the file found in the selected line's parent " "commit", "Reload the previous blamed version of the file", - "Open and populate branch view with all repository branches", "", "Branch", "Display the timeline of the currently selected branch", "Toggle display of the date when the branch last received changes", "Toggle display of the SHA hash that identifies the branch", "Open a tree view of the currently selected branch", - "Reload view with all repository branches and no filters applied", + "Reload view with all repostory branches and no filters applied", "", " See fnc(1) for complete list of options and key bindings." }; int cs, ln, width = 0, rc = 0; @@ -3067,12 +2991,11 @@ static int tl_input_handler(struct fnc_view **new_view, struct fnc_view *view, int ch) { struct fnc_tl_view_state *s = &view->state.timeline; - struct fnc_view *branch_view = NULL, *diff_view = NULL; - struct fnc_view *tree_view = NULL; + struct fnc_view *diff_view = NULL, *tree_view = NULL; int rc = 0, start_col = 0; switch (ch) { case KEY_DOWN: case 'j': @@ -3176,87 +3099,13 @@ diff_view->parent = view; view->focus_child = true; } else *new_view = diff_view; break; - case 'b': - if (view_is_parent(view)) - start_col = view_split_start_col(view->start_col); - branch_view = view_open(view->nlines, view->ncols, - view->start_ln, start_col, FNC_VIEW_BRANCH); - if (branch_view == NULL) - return RC(FSL_RC_ERROR, "%s", "view_open"); - rc = open_branch_view(branch_view, BRANCH_LS_OPEN_CLOSED, NULL, - 0, 0); - if (rc) { - view_close(branch_view); - return rc; - } - s->thread_cx.needs_reset = true; - view->active = false; - branch_view->active = true; - if (view_is_parent(view)) { - rc = view_close_child(view); - if (rc) - return rc; - view_set_child(view, branch_view); - view->focus_child = true; - } else - *new_view = branch_view; - break; case 'c': s->colour = !s->colour; break; - case 'F': { - struct fnc_view *new; - char glob[BUFSIZ]; - int retval; - mvwaddstr(view->window, view->start_ln + view->nlines - 1, 0, - "/"); - wclrtoeol(view->window); - nocbreak(); - echo(); - retval = wgetnstr(view->window, glob, sizeof(glob)); - cbreak(); - noecho(); - if (retval == ERR) - return rc; - if (view_is_parent(view)) - start_col = view_split_start_col(view->start_col); - new = view_open(view->nlines, view->ncols, view->start_ln, - start_col, FNC_VIEW_TIMELINE); - if (new == NULL) - return RC(FSL_RC_ERROR, "%s", "view_open"); - rc = open_timeline_view(new, 0, "/", glob); - if (rc) { - if (rc != FSL_RC_BREAK) - return rc; - wattr_on(view->window, A_BOLD, NULL); - mvwaddstr(view->window, - view->start_ln + view->nlines - 1, 0, - "-- no matching commits --"); - wclrtoeol(view->window); - wattr_off(view->window, A_BOLD, NULL); - fcli_err_reset(); - rc = 0; - update_panels(); - doupdate(); - sleep(1); - break; - } - view->active = false; - new->active = true; - if (view_is_parent(view)) { - rc = view_close_child(view); - if (rc) - return rc; - view_set_child(view, new); - view->focus_child = true; - } else - *new_view = new; - break; - } case 't': if (s->selected_commit == NULL) break; if (!fsl_rid_is_a_checkin(fcli_cx(), s->selected_commit->commit->rid)) { @@ -3277,11 +3126,11 @@ start_col = view_split_start_col(view->start_col); rc = browse_commit_tree(&tree_view, start_col, s->selected_commit, s->path); if (rc) break; - s->thread_cx.needs_reset = true; + s->thread_cx.tree_open = true; view->active = false; tree_view->active = true; if (view_is_parent(view)) { rc = view_close_child(view); if (rc) @@ -3612,11 +3461,11 @@ } static int view_close(struct fnc_view *view) { - int rc = 0; + int rc = 0; if (view->child) { view_close(view->child); view->child = NULL; } @@ -3633,16 +3482,17 @@ static int close_timeline_view(struct fnc_view *view) { struct fnc_tl_view_state *s = &view->state.timeline; + struct fsl_list_state st = { FNC_COLOUR_OBJ }; int rc = 0; rc = join_tl_thread(s); fsl_stmt_finalize(s->thread_cx.q); fnc_free_commits(&s->commits); - free_colours(&s->colours); + fsl_list_clear(&s->colours, fsl_list_object_free, &st); regfree(&view->regex); fsl_free(s->path); s->path = NULL; return rc; @@ -3714,10 +3564,12 @@ } static void fnc_commit_artifact_close(struct fnc_commit_artifact *commit) { + struct fsl_list_state st = { FNC_ARTIFACT_OBJ }; + if (commit->branch) fsl_free(commit->branch); if (commit->comment) fsl_free(commit->comment); if (commit->timestamp) @@ -3726,24 +3578,40 @@ fsl_free(commit->type); if (commit->user) fsl_free(commit->user); fsl_free(commit->uuid); fsl_free(commit->puuid); - fsl_list_clear(&commit->changeset, fsl_file_artifact_free, NULL); + fsl_list_clear(&commit->changeset, fsl_list_object_free, &st); fsl_list_reserve(&commit->changeset, 0); fsl_free(commit); } static int -fsl_file_artifact_free(void *elem, void *state) -{ - struct fsl_file_artifact *ffa = elem; - fsl_free(ffa->fc->name); - fsl_free(ffa->fc->uuid); - fsl_free(ffa->fc->priorName); - fsl_free(ffa->fc); - fsl_free(ffa); +fsl_list_object_free(void *elem, void *state) +{ + struct fsl_list_state *st = state; + + switch (st->obj) { + case FNC_ARTIFACT_OBJ: { + struct fsl_file_artifact *ffa = elem; + fsl_free(ffa->fc->name); + fsl_free(ffa->fc->uuid); + fsl_free(ffa->fc->priorName); + fsl_free(ffa->fc); + fsl_free(ffa); + break; + } + case FNC_COLOUR_OBJ: { + struct fnc_colour *c = elem; + regfree(&c->regex); + fsl_free(c); + break; + } + default: + return RC(FSL_RC_MISSING_INFO, + "fsl_list_state.obj missing or invalid: %d", st->obj); + } return 0; } static int @@ -3784,31 +3652,29 @@ s->sbs = 0; verbosity ? s->diff_flags |= FSL_DIFF_VERBOSE : 0; ignore_ws ? s->diff_flags |= FSL_DIFF_IGNORE_ALLWS : 0; invert ? s->diff_flags |= FSL_DIFF_INVERT : 0; s->timeline_view = timeline_view; + s->colours = fsl_list_empty; s->colour = !fnc_init.nocolour && has_colors(); s->showmeta = showmeta; - if (s->colour) { - STAILQ_INIT(&s->colours); - rc = set_colours(&s->colours, FNC_VIEW_DIFF); - if (rc) - return rc; - } - + if (s->colour) + set_colours(&s->colours, FNC_VIEW_DIFF); if (timeline_view && screen_is_split(view)) show_timeline_view(timeline_view); /* draw vborder */ show_diff_status(view); s->line_offsets = NULL; s->nlines = 0; s->ncols = view->ncols; rc = create_diff(s); if (rc) { - if (s->colour) - free_colours(&s->colours); + if (s->colour) { + struct fsl_list_state st = { FNC_COLOUR_OBJ }; + fsl_list_clear(&s->colours, fsl_list_object_free, &st); + } return rc; } view->show = show_diff; view->input = diff_input_handler; @@ -3943,11 +3809,11 @@ } static int create_changeset(struct fnc_commit_artifact *commit) { - fsl_cx *const f = fcli_cx(); + fsl_cx *f = fcli_cx(); fsl_stmt *st = NULL; fsl_list changeset = fsl_list_empty; int rc = 0; st = fsl_stmt_malloc(); @@ -4188,11 +4054,11 @@ */ static int diff_commit(fsl_buffer *buf, struct fnc_commit_artifact *commit, int diff_flags, int context, int sbs, struct fnc_pathlist_head *paths) { - fsl_cx *const f = fcli_cx(); + fsl_cx *f = fcli_cx(); const fsl_card_F *fc1 = NULL; const fsl_card_F *fc2 = NULL; fsl_deck d1 = fsl_deck_empty; fsl_deck d2 = fsl_deck_empty; fsl_id_t id1; @@ -4306,11 +4172,11 @@ */ static int diff_checkout(fsl_buffer *buf, fsl_id_t vid, int diff_flags, int context, int sbs, struct fnc_pathlist_head *paths) { - fsl_cx *const f = fcli_cx(); + fsl_cx *f = fcli_cx(); fsl_stmt *st = NULL; fsl_buffer sql, abspath, bminus; fsl_uuid_str xminus = NULL; fsl_id_t cid; int rc = 0; @@ -4581,11 +4447,11 @@ static int diff_file(fsl_buffer *buf, fsl_buffer *bminus, const char *zminus, fsl_uuid_str xminus, const char *abspath, enum fsl_ckout_change_e change, int diff_flags, int context, bool sbs) { - fsl_cx *const f = fcli_cx(); + fsl_cx *f = fcli_cx(); fsl_buffer bplus = fsl_buffer_empty; fsl_buffer xplus = fsl_buffer_empty; const char *zplus = NULL; int rc = 0; bool verbose; @@ -4677,11 +4543,11 @@ */ static int diff_non_checkin(fsl_buffer *buf, struct fnc_commit_artifact *commit, int diff_flags, int context, int sbs) { - fsl_cx *const f = fcli_cx(); + fsl_cx *f = fcli_cx(); fsl_buffer wiki = fsl_buffer_empty; fsl_buffer pwiki = fsl_buffer_empty; fsl_id_t prid = 0; fsl_size_t idx; int rc = 0; @@ -4747,11 +4613,11 @@ fsl_buffer_append(&wiki, d->W.mem, d->W.used); if (commit->puuid == NULL) { if (d->P.used > 0) commit->puuid = fsl_strdup(d->P.list[0]); else { - fsl_buffer_copy(buf, &wiki); + fsl_buffer_copy(&wiki, buf); goto end; } } /* Diff the artifacts if a parent is found. */ @@ -4791,11 +4657,11 @@ static int diff_file_artifact(fsl_buffer *buf, fsl_id_t vid1, const fsl_card_F *a, fsl_id_t vid2, const fsl_card_F *b, enum fsl_ckout_change_e change, int diff_flags, int context, int sbs, enum fnc_diff_type diff_type) { - fsl_cx *const f = fcli_cx(); + fsl_cx *f = fcli_cx(); fsl_stmt stmt = fsl_stmt_empty; fsl_buffer fbuf1 = fsl_buffer_empty; fsl_buffer fbuf2 = fsl_buffer_empty; char *zminus0 = NULL, *zplus0 = NULL; const char *zplus = NULL, *zminus = NULL; @@ -4937,10 +4803,11 @@ off_t line_offset; int wstrlen; int max_lines = view->nlines; int nlines = s->nlines; int rc = 0, nprintln = 0; + int match = -1; line_offset = s->line_offsets[s->first_line_onscreen - 1]; if (fseeko(s->f, line_offset, SEEK_SET)) return RC(fsl_errno_to_rc(errno, FSL_RC_ERROR), "%s", "fseeko"); @@ -4955,20 +4822,18 @@ fsl_free(headln); if (rc) return rc; if (screen_is_shared(view)) - wattron(view->window, A_REVERSE); + wstandout(view->window); waddwstr(view->window, wcstr); fsl_free(wcstr); wcstr = NULL; - while (wstrlen < view->ncols) { - waddch(view->window, ' '); - ++wstrlen; - } if (screen_is_shared(view)) - wattroff(view->window, A_REVERSE); + wstandend(view->window); + if (wstrlen <= view->ncols - 1) + waddch(view->window, '\n'); if (max_lines <= 1) return rc; --max_lines; } @@ -4986,12 +4851,13 @@ RC(ferror(s->f) ? fsl_errno_to_rc(errno, FSL_RC_IO) : FSL_RC_IO, "%s", "getline"); return rc; } - if (s->colour) - c = match_colour(&s->colours, line); + if (s->colour && (match = fsl_list_index_of(&s->colours, line, + match_line)) != -1) + c = s->colours.list[match]; if (c) wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); if (s->first_line_onscreen + nprintln == s->matched_line && regmatch->rm_so >= 0 && regmatch->rm_so < regmatch->rm_eo) { rc = write_matched_line(&wstrlen, line, view->ncols, 0, @@ -5008,12 +4874,14 @@ } waddwstr(view->window, wcstr); fsl_free(wcstr); wcstr = NULL; } - if (c) + if (c) { wattr_off(view->window, COLOR_PAIR(c->scheme), NULL); + c = NULL; + } if (wstrlen <= view->ncols - 1) waddch(view->window, '\n'); ++nprintln; } fsl_free(line); @@ -5035,10 +4903,19 @@ wstandend(view->window); } return rc; } + +static int +match_line(const void *ln, const void *key) +{ + struct fnc_colour *c = (struct fnc_colour *)key; + const char *line = ln; + + return regexec(&c->regex, line, 0, NULL, 0); +} static bool screen_is_shared(struct fnc_view *view) { if (view_is_parent(view)) { @@ -5150,11 +5027,10 @@ } static int diff_input_handler(struct fnc_view **new_view, struct fnc_view *view, int ch) { - struct fnc_view *branch_view; struct fnc_diff_view_state *s = &view->state.diff; struct fnc_tl_view_state *tlstate; struct commit_entry *previous_selection; char *line = NULL; ssize_t linelen; @@ -5214,36 +5090,10 @@ break; /* FALL THROUGH */ case KEY_HOME: s->first_line_onscreen = 1; break; - case 'b': { - int start_col = 0; - if (view_is_parent(view)) - start_col = view_split_start_col(view->start_col); - branch_view = view_open(view->nlines, view->ncols, - view->start_ln, start_col, FNC_VIEW_BRANCH); - if (branch_view == NULL) - return RC(FSL_RC_ERROR, "%s", "view_open"); - rc = open_branch_view(branch_view, BRANCH_LS_OPEN_CLOSED, NULL, - 0, 0); - if (rc) { - view_close(branch_view); - return rc; - } - view->active = false; - branch_view->active = true; - if (view_is_parent(view)) { - rc = view_close_child(view); - if (rc) - return rc; - view_set_child(view, branch_view); - view->focus_child = true; - } else - *new_view = branch_view; - break; - } case 'c': case 'i': case 'v': case 'w': if (ch == 'c') @@ -5413,20 +5263,21 @@ static int close_diff_view(struct fnc_view *view) { struct fnc_diff_view_state *s = &view->state.diff; + struct fsl_list_state st = { FNC_COLOUR_OBJ }; int rc = 0; if (s->f && fclose(s->f) == EOF) rc = RC(fsl_errno_to_rc(errno, FSL_RC_IO), "%s", "fclose"); fsl_free(s->id1); s->id1 = NULL; fsl_free(s->id2); s->id2 = NULL; fsl_free(s->line_offsets); - free_colours(&s->colours); + fsl_list_clear(&s->colours, fsl_list_object_free, &st); s->line_offsets = NULL; s->nlines = 0; return rc; } @@ -5489,30 +5340,24 @@ return 0; } /* * Consume repeatable arguments containing artifact type values used in - * constructing the SQL query to generate commit records of the specified type - * for the timeline. n.b. filter_types->values is owned by fcli—do not free. - * TODO: Enhance to generalise processing of various repeatable args--paths, - * usernames, branches, etc.--so we can filter on multiples of these values. + * constructing the SQL query to generate commit records of the specified + * type for the timeline. TODO: Enhance this to generalise processing of + * various repeatable arguments--paths, usernames, branches, etc.--so we + * can filter on multiples of these values. */ static int fcli_flag_type_arg_cb(fcli_cliflag const *v) { - struct artifact_types *ft = &fnc_init.filter_types; - const char *t = *((const char **)v->flagValue); - - if (*t != 'e' && *t != 'f' && *t != 'g' && - *t != 't' && *t != 'w' && fsl_strcmp(t, "ci")) { - fnc_init.err = RC(FSL_RC_TYPE, "invalid type: %s", t); - usage(); - /* NOT REACHED */ - } - - ft->values = fsl_realloc(ft->values, (ft->nitems + 1) * sizeof(char *)); - ft->values[ft->nitems++] = t; + if (fnc_init.filter_types->nitems) + fnc_init.filter_types->values = + fsl_realloc(fnc_init.filter_types->values, + (fnc_init.filter_types->nitems + 1) * sizeof(char *)); + fnc_init.filter_types->values[fnc_init.filter_types->nitems++] = + *((const char **)v->flagValue); return FCLI_RC_FLAG_AGAIN; } static void @@ -5558,11 +5403,11 @@ * f->output fsl_outputer implementation as we would like. */ /* fsl_cx *f = fcli_cx(); */ /* f->output = fsl_outputer_FILE; */ /* f->output.state.state = (fnc_init.err == true) ? stderr : stdout; */ - FILE *f = fnc_init.err ? stderr : stdout; + FILE *f = fnc_init.err ? stderr : stdout; size_t idx = 0; endwin(); /* If a command was passed on the CLI, output its corresponding help. */ @@ -5588,12 +5433,12 @@ static void usage_timeline(void) { fsl_fprintf(fnc_init.err ? stderr : stdout, " usage: %s timeline [-C|--no-colour] [-T tag] [-b branch] " - "[-c commit] [-f glob] [-h|--help] [-n n] [-t type] [-u user] " - "[-z|--utc] [path]\n" + "[-c commit] [-h|--help] [-n n] [-t type] [-u user] [-z|--utc] " + "[path]\n" " e.g.: %s timeline --type ci -u jimmy src/frobnitz.c\n\n", fcli_progname(), fcli_progname()); } static void @@ -5635,23 +5480,14 @@ "[-p|--no-private] [-r|--reverse] [-s|--sort order] [glob]\n" " e.g.: %s branch -b 2020-10-10\n\n" , fcli_progname(), fcli_progname()); } -static void -usage_config(void) -{ - fsl_fprintf(fnc_init.err ? stderr : stdout, - " usage: %s config [-h|--help] [--ls] [setting [value|--unset]]\n" - " e.g.: %s config FNC_DIFF_COMMIT blue\n\n" , - fcli_progname(), fcli_progname()); -} - static int cmd_diff(fcli_command const *argv) { - fsl_cx *const f = fcli_cx(); + fsl_cx *f = fcli_cx(); struct fnc_view *view; struct fnc_commit_artifact *commit = NULL; struct fnc_pathlist_head paths; struct fnc_pathlist_entry *pe; fsl_deck d = fsl_deck_empty; @@ -5659,11 +5495,11 @@ const char *artifact1 = NULL, *artifact2 = NULL; char *path0 = NULL; fsl_id_t prid = -1, rid = -1; int context = DIFF_DEF_CTXT, rc = 0; unsigned short blob = 0; - enum fnc_diff_type diff_type = FNC_DIFF_CKOUT; + enum fnc_diff_type diff_type; bool showmeta = false; rc = fcli_process_flags(argv->flags); if (rc || (rc = fcli_has_unused_flags(false))) return rc; @@ -5682,18 +5518,34 @@ * fnc diff sym3 f1 -> diff f1 on disk against f1 found in checkin sym3 * fnc diff sym1 sym2 f1 f2 -> diff f{1,2} between checkins sym1 & sym2 */ if (!fsl_sym_to_rid(f, fcli_next_arg(false), FSL_SATYPE_ANY, &prid)) { artifact1 = fcli_next_arg(true); - if (!fsl_rid_is_a_checkin(f, prid)) + if (!fsl_rid_is_a_checkin(f, prid)) { + if (fsl_rid_to_artifact_uuid(f, prid, FSL_SATYPE_ANY) + != NULL) { + rc = RC(FSL_RC_TYPE, + "artifact [%s] not resolvable to a checkin", + artifact1); + goto end; + } ++blob; + } if (!fsl_sym_to_rid(f, fcli_next_arg(false), FSL_SATYPE_ANY, &rid)) { artifact2 = fcli_next_arg(true); diff_type = FNC_DIFF_COMMIT; - if (!fsl_rid_is_a_checkin(f, rid)) + if (!fsl_rid_is_a_checkin(f, rid)) { + if (fsl_rid_to_artifact_uuid(f, + prid, FSL_SATYPE_ANY) != NULL) { + rc = RC(FSL_RC_TYPE, "artifact [%s] " + "not resolvable to a checkin", + artifact2); + goto end; + } ++blob; + } } } if (fcli_error()->code == FSL_RC_NOT_FOUND) { fcli_err_reset(); /* If args aren't symbols, treat as paths. */ rc = 0; @@ -5707,10 +5559,11 @@ rc = RC(rc, "%s", "fsl_sym_to_rid"); goto end; } } if (!artifact2 && diff_type != FNC_DIFF_BLOB) { + diff_type = FNC_DIFF_CKOUT; fsl_ckout_version_info(f, &rid, NULL); if ((rc = fsl_ckout_changes_scan(f))) return RC(rc, "%s", "fsl_ckout_changes_scan"); if (!fsl_strcmp(artifact1, "current") && !fsl_ckout_has_changes(f)) { @@ -5725,14 +5578,12 @@ path = path0; while (path[0] == '/') ++path; if (rc) { if (rc != FSL_RC_NOT_FOUND || - (!fsl_strcmp(artifact1, "current") && !artifact2)) { - rc = RC(rc, "invalid artifact hash: %s", path); + (!fsl_strcmp(artifact1, "current") && !artifact2)) goto end; - } rc = 0; fcli_err_reset(); /* Path may be valid in tree of specified commit(s). */ const fsl_card_F *cf = NULL; rc = fsl_deck_load_sym(f, &d, artifact1, @@ -5854,11 +5705,11 @@ } static int cmd_tree(fcli_command const *argv) { - fsl_cx *const f = fcli_cx(); + fsl_cx *f = fcli_cx(); struct fnc_view *view; char *path = NULL; fsl_id_t rid; int rc = 0; @@ -5925,11 +5776,11 @@ } static int open_tree_view(struct fnc_view *view, const char *path, fsl_id_t rid) { - fsl_cx *const f = fcli_cx(); + fsl_cx *f = fcli_cx(); struct fnc_tree_view_state *s = &view->state.tree; int rc = 0; TAILQ_INIT(&s->parents); s->show_id = false; @@ -5973,16 +5824,12 @@ } s->first_entry_onscreen = &s->tree->entries[0]; s->selected_entry = &s->tree->entries[0]; - if (s->colour) { - STAILQ_INIT(&s->colours); - rc = set_colours(&s->colours, FNC_VIEW_TREE); - if (rc) - goto end; - } + if (s->colour) + set_colours(&s->colours, FNC_VIEW_TREE); view->show = show_tree_view; view->input = tree_input_handler; view->close = close_tree_view; view->search_init = tree_search_init; @@ -6075,11 +5922,11 @@ */ static int create_repository_tree(struct fnc_repository_tree **repo, fsl_uuid_str *id, fsl_id_t rid) { - fsl_cx *const f = fcli_cx(); + fsl_cx *f = fcli_cx(); struct fnc_repository_tree *ptr; fsl_deck d = fsl_deck_empty; const fsl_card_F *cf = NULL; int rc = 0; @@ -6086,11 +5933,11 @@ ptr = fsl_malloc(sizeof(struct fnc_repository_tree)); if (ptr == NULL) return RC(FSL_RC_ERROR, "%s", "fsl_malloc"); memset(ptr, 0, sizeof(struct fnc_repository_tree)); - rc = fsl_deck_load_rid(f, &d, rid, FSL_SATYPE_CHECKIN); + rc = fsl_deck_load_rid(fcli_cx(), &d, rid, FSL_SATYPE_CHECKIN); if (rc) return RC(rc, "fsl_deck_load_rid(%d) [%s]", rid, id); rc = fsl_deck_F_rewind(&d); if (rc) goto end;; @@ -6226,19 +6073,20 @@ */ static int link_tree_node(struct fnc_repository_tree *tree, const char *path, const char *uuid, fsl_time_t mtime) { + fsl_cx *f = fcli_cx(); struct fnc_repo_tree_node *parent_dir; fsl_buffer buf = fsl_buffer_empty; struct stat s; int i, rc = 0; parent_dir = tree->tail; while (parent_dir != 0 && (strncmp(parent_dir->path, path, parent_dir->pathlen) != 0 || - path[parent_dir->pathlen] != '/')) + path[parent_dir->pathlen] != '/')) parent_dir = parent_dir->parent_dir; i = parent_dir ? parent_dir->pathlen + 1 : 0; while (path[i]) { @@ -6299,12 +6147,12 @@ while (path[i] == '/') /* Consume slashes. */ ++i; parent_dir = tn; /* Stat path for tree display features. */ - rc = fsl_file_canonical_name2(fcli_cx()->ckout.dir, tn->path, - &buf, false); + rc = fsl_file_canonical_name2(f->ckout.dir, tn->path, &buf, + false); if (rc) goto end; if (lstat(fsl_buffer_cstr(&buf), &s) == -1) { if (errno == ENOENT) tn->mode = (!fsl_strcmp(tn->path, path) && @@ -6423,11 +6271,11 @@ { struct fnc_tree_view_state *s = &view->state.tree; struct fnc_tree_entry *te; struct fnc_colour *c = NULL; wchar_t *wcstr; - int rc = 0; + int match = -1, rc = 0; int wstrlen, n, idx, nentries; int limit = view->nlines; uint_fast8_t hashlen = FSL_UUID_STRLEN_MIN; s->ndisplayed = 0; @@ -6438,26 +6286,24 @@ /* Write (highlighted) headline (if view is active in splitscreen). */ rc = formatln(&wcstr, &wstrlen, s->tree_label, view->ncols, 0); if (rc) return rc; if (screen_is_shared(view)) - wattron(view->window, A_REVERSE); + wstandout(view->window); if (s->colour) - c = get_colour(&s->colours, FNC_COLOUR_COMMIT); + c = get_colour(&s->colours, FNC_COMMIT_ID); if (c) wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); waddwstr(view->window, wcstr); - while (wstrlen < view->ncols) { - waddch(view->window, ' '); - ++wstrlen; - } if (c) wattr_off(view->window, COLOR_PAIR(c->scheme), NULL); if (screen_is_shared(view)) - wattroff(view->window, A_REVERSE); + wstandend(view->window); fsl_free(wcstr); wcstr = NULL; + if (wstrlen < view->ncols - 1) + waddch(view->window, '\n'); if (--limit <= 0) return rc; /* Write this (sub)tree's absolute repository path subheader. */ rc = formatln(&wcstr, &wstrlen, treepath, view->ncols, 0); @@ -6562,12 +6408,15 @@ if (n == s->selected_idx) { if (view->active) wattr_on(view->window, A_REVERSE, NULL); s->selected_entry = te; } - if (s->colour) - c = match_colour(&s->colours, line); + if (s->colour && (match = fsl_list_index_of(&s->colours, line, + match_line)) != -1) + c = s->colours.list[match]; + else + c = NULL; if (c) wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); waddwstr(view->window, wcstr); if (c) wattr_off(view->window, COLOR_PAIR(c->scheme), NULL); @@ -6647,43 +6496,16 @@ } static int tree_input_handler(struct fnc_view **new_view, struct fnc_view *view, int ch) { - struct fnc_view *branch_view, *timeline_view; + struct fnc_view *timeline_view/*, *branch_view */; struct fnc_tree_view_state *s = &view->state.tree; - struct fnc_tl_thread_cx *tcx = NULL; struct fnc_tree_entry *te; int n, start_col = 0, rc = 0; switch (ch) { - case 'b': - if (view_is_parent(view)) - start_col = view_split_start_col(view->start_col); - branch_view = view_open(view->nlines, view->ncols, - view->start_ln, start_col, FNC_VIEW_BRANCH); - if (branch_view == NULL) - return RC(FSL_RC_ERROR, "%s", "view_open"); - rc = open_branch_view(branch_view, BRANCH_LS_OPEN_CLOSED, NULL, - 0, 0); - if (rc) { - view_close(branch_view); - return rc; - } - tcx = fcli_cx()->clientState.state; - tcx->needs_reset = true; - view->active = false; - branch_view->active = true; - if (view_is_parent(view)) { - rc = view_close_child(view); - if (rc) - return rc; - view_set_child(view, branch_view); - view->focus_child = true; - } else - *new_view = branch_view; - break; case 'c': s->colour = !s->colour; break; case 'i': s->show_id = !s->show_id; @@ -6692,12 +6514,10 @@ if (!s->selected_entry) break; if (view_is_parent(view)) start_col = view_split_start_col(view->start_col); rc = timeline_tree_entry(&timeline_view, start_col, s); - if (rc) - return rc; view->active = false; timeline_view->active = true; if (view_is_parent(view)) { rc = view_close_child(view); if (rc) @@ -6868,12 +6688,14 @@ /* Construct repository relative path for timeline query. */ rc = tree_entry_path(&path, &s->parents, s->selected_entry); if (rc) return rc; - rc = open_timeline_view(timeline_view, s->rid, path, NULL); - if (!rc) + rc = open_timeline_view(timeline_view, s->rid, path); + if (rc) + view_close(timeline_view); + else *new_view = timeline_view; fsl_free(path); return rc; } @@ -7088,12 +6910,13 @@ static int close_tree_view(struct fnc_view *view) { struct fnc_tree_view_state *s = &view->state.tree; + struct fsl_list_state st = { FNC_COLOUR_OBJ }; - free_colours(&s->colours); + fsl_list_clear(&s->colours, fsl_list_object_free, &st); fsl_free(s->tree_label); s->tree_label = NULL; fsl_free(s->commit_id); s->commit_id = NULL; @@ -7145,129 +6968,10 @@ tn = next; } fsl_free(repo); } -static int -cmd_config(const fcli_command *argv) -{ - const char *set = NULL, *value = NULL; - int setid, rc = 0; - - rc = fcli_process_flags(argv->flags); - if (rc || (rc = fcli_has_unused_flags(false))) - return rc; - - set = fcli_next_arg(true); - if (set == NULL || fnc_init.lsconf) { - if (fnc_init.unset) { - fnc_init.err = RC(FSL_RC_MISSING_INFO, - "%s", "-u|--unset requires "); - usage(); - /* NOT REACHED */ - } - return fnc_conf_ls_settings(fnc_init.lsconf ? false : true); - } - - setid = fnc_conf_str2enum(set); - if (!setid) /* Presently, the only valid settings are colours. */ - return RC(FSL_RC_NOT_FOUND, "invalid setting: %s", set); - - value = fcli_next_arg(true); - if (value || fnc_init.unset) { - if (value && fnc_init.unset) - return RC(FSL_RC_MISUSE, "\n--unset or set %s to %s?", - set, value); - char *prev = fnc_conf_get(setid, true); - rc = fnc_conf_set(setid, value, fnc_init.unset); - if (!rc) - f_out("%s: %s -> %s (local)", fnc_conf_enum2str(setid), - prev ? prev : "default", value ? value : "default"); - fsl_free(prev); - } else { - char *v = fnc_conf_get(setid, true); - f_out("%s = %s", fnc_conf_enum2str(setid), v ? v : "default"); - fsl_free(v); - } - - return rc; -} - -static int -fnc_conf_ls_settings(bool all) -{ - static const char *fnc_settings[] = { - SETTINGS(GEN_STR) - }; - enum settings { - SETTINGS(GEN_ENUM) - }; - char *value = NULL; - int idx, last = 0; - size_t maxlen = 0; - - for (idx = FNC_START_SETTINGS + 1; idx < FNC_EOF_SETTINGS; ++idx) { - last = (value = fnc_conf_get(idx, true)) ? idx : last; - maxlen = MAX(fsl_strlen(fnc_settings[idx]), maxlen); - fsl_free(value); - } - - if (!last && !all) { - f_out("No user-defined settings: " - "'%s config' for list of available settings.", - fcli_progname()); - return 0; - } - - for (idx = FNC_START_SETTINGS + 1; idx < FNC_EOF_SETTINGS; ++idx) { - value = fnc_conf_get(idx, true); - if (value || all) - f_out("%-*s%s%s%c", maxlen + 2, fnc_settings[idx], - value ? " = " : "", value ? value : "", - all ? (idx + 1 < FNC_EOF_SETTINGS ? '\n' : '\0') : - idx < last ? '\n' : '\0'); - fsl_free(value); - value = NULL; - } - - return 0; -} - -static int -fnc_conf_str2enum(const char *str) -{ - static const char *fnc_settings[] = { - SETTINGS(GEN_STR) - }; - enum settings { - SETTINGS(GEN_ENUM) - }; - int idx; - - for (idx = FNC_START_SETTINGS + 1; idx < FNC_EOF_SETTINGS; ++idx) - if (!fsl_stricmp(str, fnc_settings[idx])) - return idx; - - return FNC_START_SETTINGS; -} - -static const char * -fnc_conf_enum2str(int id) -{ - static const char *fnc_settings[] = { - SETTINGS(GEN_STR) - }; - enum settings { - SETTINGS(GEN_ENUM) - }; - - if (id <= FNC_START_SETTINGS || id >= FNC_EOF_SETTINGS) - return NULL; - - return fnc_settings[id]; -} - static int view_close_child(struct fnc_view *view) { int rc = 0; @@ -7286,97 +6990,73 @@ view->child = child; child->parent = view; } static int -set_colours(struct fnc_colours *s, enum fnc_view_id vid) +set_colours(fsl_list *s, enum fnc_view_id vid) { - int rc = 0; + struct fnc_colour *colour; + const char **regexp = NULL; + const char *regexp_blame[] = {"^"}; + const char *regexp_timeline[] = {"^$", "^$", "^$"}; + const char *regexp_tree[] = {"@$", "/$", "\\*$", "^$"}; + const char *regexp_diff[] = { + "^((checkin|wiki|ticket|technote) " + "[0-9a-f]|hash [+-] |\\[[+~>-]] |" + "[+-]{3} )", "^user:", "^date:", "^tags:", + "^-", "^\\+", "^@@" + }; + int pairs_diff[][2] = { + {FNC_DIFF_META, COLOR_GREEN}, + {FNC_USER_STR, COLOR_CYAN}, + {FNC_DATE_STR, COLOR_YELLOW}, + {FNC_TAGS_STR, COLOR_MAGENTA}, + {FNC_DIFF_MINUS, COLOR_MAGENTA}, + {FNC_DIFF_PLUS, COLOR_CYAN}, + {FNC_DIFF_CHNK, COLOR_YELLOW} + }; + int pairs_tree[][2] = { + {FNC_TREE_LINK, COLOR_MAGENTA}, + {FNC_TREE_DIR, COLOR_CYAN}, + {FNC_TREE_EXEC, COLOR_GREEN}, + {FNC_COMMIT_ID, COLOR_GREEN} + }; + int pairs_timeline[][2] = { + {FNC_COMMIT_ID, COLOR_GREEN}, + {FNC_USER_STR, COLOR_CYAN}, + {FNC_DATE_STR, COLOR_YELLOW} + }; + int pairs_blame[][2] = { + {FNC_COMMIT_ID, COLOR_GREEN} + }; + int (*pairs)[2], rc = 0; + fsl_size_t idx, n; switch (vid) { - case FNC_VIEW_DIFF: { - static const char *regexp_diff[] = { - "^((checkin|wiki|ticket|technote) " - "[0-9a-f]|hash [+-] |\\[[+~>-]] |[+-]{3} )", - "^user:", "^date:", "^tags:", "^-", "^\\+", "^@@" - }; - const int pairs_diff[][2] = { - {FNC_COLOUR_DIFF_META, init_colour(FNC_COLOUR_DIFF_META)}, - {FNC_COLOUR_USER, init_colour(FNC_COLOUR_USER)}, - {FNC_COLOUR_DATE, init_colour(FNC_COLOUR_DATE)}, - {FNC_COLOUR_DIFF_TAGS, init_colour(FNC_COLOUR_DIFF_TAGS)}, - {FNC_COLOUR_DIFF_MINUS, init_colour(FNC_COLOUR_DIFF_MINUS)}, - {FNC_COLOUR_DIFF_PLUS, init_colour(FNC_COLOUR_DIFF_PLUS)}, - {FNC_COLOUR_DIFF_CHUNK, init_colour(FNC_COLOUR_DIFF_CHUNK)} - }; - rc = set_colour_scheme(s, pairs_diff, regexp_diff, - nitems(regexp_diff)); - break; - } - case FNC_VIEW_TREE: { - static const char *regexp_tree[] = {"@ ->", "/$", "\\*$", "^$"}; - const int pairs_tree[][2] = { - {FNC_COLOUR_TREE_LINK, init_colour(FNC_COLOUR_TREE_LINK)}, - {FNC_COLOUR_TREE_DIR, init_colour(FNC_COLOUR_TREE_DIR)}, - {FNC_COLOUR_TREE_EXEC, init_colour(FNC_COLOUR_TREE_EXEC)}, - {FNC_COLOUR_COMMIT, init_colour(FNC_COLOUR_COMMIT)} - }; - rc = set_colour_scheme(s, pairs_tree, regexp_tree, - nitems(regexp_tree)); - break; - } - case FNC_VIEW_TIMELINE: { - static const char *regexp_timeline[] = {"^$", "^$", "^$"}; - const int pairs_timeline[][2] = { - {FNC_COLOUR_COMMIT, init_colour(FNC_COLOUR_COMMIT)}, - {FNC_COLOUR_USER, init_colour(FNC_COLOUR_USER)}, - {FNC_COLOUR_DATE, init_colour(FNC_COLOUR_DATE)} - }; - rc = set_colour_scheme(s, pairs_timeline, regexp_timeline, - nitems(regexp_timeline)); - break; - } - case FNC_VIEW_BLAME: { - static const char *regexp_blame[] = {"^"}; - const int pairs_blame[][2] = { - {FNC_COLOUR_COMMIT, init_colour(FNC_COLOUR_COMMIT)} - }; - rc = set_colour_scheme(s, pairs_blame, regexp_blame, - nitems(regexp_blame)); - break; - } - case FNC_VIEW_BRANCH: { - static const char *regexp_branch[] = { - "^\\ +", "^ -", "@$", "\\*$" - }; - const int pairs_branch[][2] = { - {FNC_COLOUR_BRANCH_OPEN, - init_colour(FNC_COLOUR_BRANCH_OPEN)}, - {FNC_COLOUR_BRANCH_CLOSED, - init_colour(FNC_COLOUR_BRANCH_CLOSED)}, - {FNC_COLOUR_BRANCH_CURRENT, - init_colour(FNC_COLOUR_BRANCH_CURRENT)}, - {FNC_COLOUR_BRANCH_PRIVATE, - init_colour(FNC_COLOUR_BRANCH_PRIVATE)} - }; - rc = set_colour_scheme(s, pairs_branch, regexp_branch, - nitems(regexp_branch)); - break; - } + case FNC_VIEW_DIFF: + n = nitems(regexp_diff); + regexp = regexp_diff; + pairs = pairs_diff; + break; + case FNC_VIEW_TREE: + n = nitems(regexp_tree); + regexp = regexp_tree; + pairs = pairs_tree; + break; + case FNC_VIEW_TIMELINE: + n = nitems(regexp_timeline); + regexp = regexp_timeline; + pairs = pairs_timeline; + break; + case FNC_VIEW_BLAME: + n = nitems(regexp_blame); + regexp = regexp_blame; + pairs = pairs_blame; + break; default: - rc = RC(FSL_RC_TYPE, "invalid fnc_view_id: %s", vid); - } - - return rc; -} - -static int -set_colour_scheme(struct fnc_colours *colours, const int (*pairs)[2], - const char **regexp, int n) -{ - struct fnc_colour *colour; - int idx, rc = 0; + return RC(FSL_RC_TYPE, "%s", "invalid fnc_view_id"); + } for (idx = 0; idx < n; ++idx) { colour = fsl_malloc(sizeof(*colour)); if (colour == NULL) return RC(fsl_errno_to_rc(errno, FSL_RC_ERROR), @@ -7392,193 +7072,38 @@ regexp[idx], regerr); } colour->scheme = pairs[idx][0]; init_pair(colour->scheme, pairs[idx][1], -1); - STAILQ_INSERT_HEAD(colours, colour, entries); - } - - return rc; -} - -static int -init_colour(enum fnc_colour_obj id) -{ - char *val = NULL; - int rc = 0; - - val = fnc_conf_get(id, false); - - if (val == NULL) - return default_colour(id); - - if (!fsl_stricmp(val, "black")) - rc = COLOR_BLACK; - else if (!fsl_stricmp(val, "red")) - rc = COLOR_RED; - else if (!fsl_stricmp(val, "green")) - rc = COLOR_GREEN; - else if (!fsl_stricmp(val, "yellow")) - rc = COLOR_YELLOW; - else if (!fsl_stricmp(val, "blue")) - rc = COLOR_BLUE; - else if (!fsl_stricmp(val, "magenta")) - rc = COLOR_MAGENTA; - else if (!fsl_stricmp(val, "cyan")) - rc = COLOR_CYAN; - else if (!fsl_stricmp(val, "white")) - rc = COLOR_WHITE; - else if (!fsl_stricmp(val, "default")) - rc = -1; /* Terminal default foreground colour. */ - - fsl_free(val); - return rc ? rc : default_colour(id); -} - -/* - * Lookup setting id from the repository db. If not found, search envvars. If - * found, return a dynamically allocated string obtained from fsl_db_g_text() or - * strdup(), which must be disposed of by the caller. Alternatively, if ls is - * set, search local settings and envvars for id. If found, dynamically allocate - * and return a formatted string for pretty printing the current state of id, - * which the caller must free. In either case, if not found, return NULL. - */ -static char * -fnc_conf_get(enum fnc_colour_obj id, bool ls) -{ - fsl_cx *const f = fcli_cx(); - fsl_db *db = NULL; - char *colour = NULL, *colour_g = NULL; - - db = fsl_needs_repo(f); - - if (!db) { - /* Theoretically, this shouldn't happen. */ - RC(FSL_RC_DB, "%s", "fsl_needs_repo"); - return NULL; - } - - colour = fsl_db_g_text(db, NULL, - "SELECT value FROM config WHERE name=%Q", fnc_conf_enum2str(id)); - - if (colour == NULL || ls) - colour_g = fsl_strdup(getenv(fnc_conf_enum2str(id))); - - if (ls && (colour || colour_g)) { - char *colour_ls = fsl_mprintf("%s%s%s%s%s", - colour ? colour : "", colour ? " (local)" : "", - colour && colour_g ? ", " : "", - colour_g ? colour_g : "", colour_g ? " (envvar)" : ""); - fsl_free(colour); - fsl_free(colour_g); - colour = colour_ls; - } - - return ls ? colour : (colour ? colour : colour_g); -} - -static int -default_colour(enum fnc_colour_obj obj) -{ - if (obj == FNC_COLOUR_COMMIT) - return COLOR_GREEN; - if (obj == FNC_COLOUR_USER) - return COLOR_CYAN; - if (obj == FNC_COLOUR_DATE) - return COLOR_YELLOW; - if (obj == FNC_COLOUR_DIFF_META) - return COLOR_GREEN; - if (obj == FNC_COLOUR_DIFF_MINUS) - return COLOR_MAGENTA; - if (obj == FNC_COLOUR_DIFF_PLUS) - return COLOR_CYAN; - if (obj == FNC_COLOUR_DIFF_CHUNK) - return COLOR_YELLOW; - if (obj == FNC_COLOUR_DIFF_TAGS) - return COLOR_MAGENTA; - if (obj == FNC_COLOUR_TREE_LINK) - return COLOR_MAGENTA; - if (obj == FNC_COLOUR_TREE_DIR) - return COLOR_CYAN; - if (obj == FNC_COLOUR_TREE_EXEC) - return COLOR_GREEN; - if (obj == FNC_COLOUR_BRANCH_OPEN) - return COLOR_CYAN; - if (obj == FNC_COLOUR_BRANCH_CLOSED) - return COLOR_MAGENTA; - if (obj == FNC_COLOUR_BRANCH_CURRENT) - return COLOR_GREEN; - if (obj == FNC_COLOUR_BRANCH_PRIVATE) - return COLOR_YELLOW; - - return -1; /* Terminal default foreground colour. */ -} - -static int -fnc_conf_set(enum fnc_colour_obj id, const char *val, bool unset) -{ - fsl_cx *const f = fcli_cx(); - fsl_db *db = NULL; - - db = fsl_needs_repo(f); - - if (!db) /* Theoretically, this shouldn't happen. */ - return RC(FSL_RC_DB, "%s", "fsl_needs_repo"); - - if (unset) - return fsl_db_exec(db, "DELETE FROM config WHERE name=%Q", - fnc_conf_enum2str(id)); - - return fsl_db_exec(db, - "INSERT OR REPLACE INTO config(name, value, mtime) " - "VALUES(%Q, %Q, now())", fnc_conf_enum2str(id), val); -} - -struct fnc_colour * -get_colour(struct fnc_colours *colours, int scheme) -{ - struct fnc_colour *c = NULL; - - STAILQ_FOREACH(c, colours, entries) { - if (c->scheme == scheme) - return c; - } - - return NULL; -} - -struct fnc_colour * -match_colour(struct fnc_colours *colours, const char *line) -{ - struct fnc_colour *c = NULL; - - STAILQ_FOREACH(c, colours, entries) { - if (match_line(line, &c->regex, 0, NULL)) - return c; - } - - return NULL; -} - -static int -match_line(const char *line, regex_t *regex, size_t nmatch, - regmatch_t *regmatch) -{ - return regexec(regex, line, nmatch, regmatch, 0) == 0; -} - -static void -free_colours(struct fnc_colours *colours) -{ - struct fnc_colour *c; - - while (!STAILQ_EMPTY(colours)) { - c = STAILQ_FIRST(colours); - STAILQ_REMOVE_HEAD(colours, entries); - regfree(&c->regex); - fsl_free(c); - } + fsl_list_append(s, colour); + } + + return rc; +} + +struct fnc_colour * +get_colour(fsl_list *colours, int scheme) +{ + struct fnc_colour cs; + int match = -1; + + cs.scheme = scheme; + match = fsl_list_index_of(colours, &cs, match_colour); + + if (match != -1) + return colours->list[match]; + + return NULL; +} + +static int +match_colour(const void *target, const void *key) +{ + struct fnc_colour *c = (struct fnc_colour *)key; + struct fnc_colour *t = (struct fnc_colour *)target; + + return (c->scheme == t->scheme) ? 0 : 1; } /* * Emulate vim(1) gg: User has 1 sec to follow first 'g' keypress with another. */ @@ -7596,11 +7121,11 @@ } static int cmd_blame(fcli_command const *argv) { - fsl_cx *const f = fcli_cx(); + fsl_cx *f = fcli_cx(); struct fnc_view *view; char *path = NULL; fsl_uuid_str commit_id = NULL; fsl_id_t tip = 0, rid = 0; int nlimit = 0, rc = 0; @@ -7710,11 +7235,10 @@ s->blame.nlimit = nlimit; s->spin_idx = 0; s->colour = !fnc_init.nocolour && has_colors(); if (s->colour) { - STAILQ_INIT(&s->colours); rc = set_colours(&s->colours, FNC_VIEW_BLAME); if (rc) return rc; } @@ -7728,11 +7252,11 @@ } static int run_blame(struct fnc_view *view) { - fsl_cx *const f = fcli_cx(); + fsl_cx *f = fcli_cx(); struct fnc_blame_view_state *s = &view->state.blame; struct fnc_blame *blame = &s->blame; fsl_deck d = fsl_deck_empty; fsl_buffer buf = fsl_buffer_empty; fsl_annotate_opt *opt = NULL; @@ -7982,20 +7506,19 @@ } static void * blame_thread(void *state) { - fsl_cx *const f = fcli_cx(); struct fnc_blame_thread_cx *cx = state; int rc0, rc; rc = block_main_thread_signals(); if (rc) return (void *)(intptr_t)rc; - rc = fsl_annotate(f, &cx->blame_opt); - if (rc && fsl_cx_err_get_e(f)->code == FSL_RC_BREAK) { + rc = fsl_annotate(fcli_cx(), &cx->blame_opt); + if (rc && fsl_cx_err_get_e(fcli_cx())->code == FSL_RC_BREAK) { fcli_err_reset(); rc = 0; } rc0 = pthread_mutex_lock(&fnc_mutex); @@ -8095,24 +7618,20 @@ fsl_free(line); line = NULL; if (rc) return rc; if (screen_is_shared(view)) - wattron(view->window, A_REVERSE); + wstandout(view->window); if (s->colour) - c = get_colour(&s->colours, FNC_COLOUR_COMMIT); + c = get_colour(&s->colours, FNC_COMMIT_ID); if (c) wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); waddwstr(view->window, wcstr); - while (width < view->ncols) { - waddch(view->window, ' '); - ++width; - } if (c) wattr_off(view->window, COLOR_PAIR(c->scheme), NULL); if (screen_is_shared(view)) - wattroff(view->window, A_REVERSE); + wstandend(view->window); fsl_free(wcstr); wcstr = NULL; if (width < view->ncols - 1) waddch(view->window, '\n'); @@ -8169,11 +7688,11 @@ return RC(FSL_RC_ERROR, "%s", "fsl_strdup"); } if (s->colour) c = get_colour(&s->colours, - FNC_COLOUR_COMMIT); + FNC_COMMIT_ID); if (c) wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); wprintw(view->window, "%.*s", idfield - 1, id_str); @@ -8238,11 +7757,11 @@ } static int blame_input_handler(struct fnc_view **new_view, struct fnc_view *view, int ch) { - struct fnc_view *branch_view, *diff_view; + struct fnc_view *diff_view; struct fnc_blame_view_state *s = &view->state.blame; int start_col = 0, rc = 0; switch (ch) { case 'q': @@ -8304,11 +7823,11 @@ id = get_selected_commit_id(s->blame.lines, s->blame.nlines, s->first_line_onscreen, s->selected_line); if (id == NULL) break; if (ch == 'p') { - fsl_cx *const f = fcli_cx(); + fsl_cx *f = fcli_cx(); fsl_db *db = fsl_needs_repo(f); fsl_deck d = fsl_deck_empty; fsl_id_t rid = fsl_uuid_to_rid(f, id); fsl_uuid_str pid = fsl_db_g_text(db, NULL, "SELECT uuid FROM plink, blob WHERE plink.cid=%d " @@ -8387,37 +7906,13 @@ rc = run_blame(view); if (rc) break; break; } - case 'T': - if (view_is_parent(view)) - start_col = view_split_start_col(view->start_col); - branch_view = view_open(view->nlines, view->ncols, - view->start_ln, start_col, FNC_VIEW_BRANCH); - if (branch_view == NULL) - return RC(FSL_RC_ERROR, "%s", "view_open"); - rc = open_branch_view(branch_view, BRANCH_LS_OPEN_CLOSED, NULL, - 0, 0); - if (rc) { - view_close(branch_view); - return rc; - } - view->active = false; - branch_view->active = true; - if (view_is_parent(view)) { - rc = view_close_child(view); - if (rc) - return rc; - view_set_child(view, branch_view); - view->focus_child = true; - } else - *new_view = branch_view; - break; case KEY_ENTER: case '\r': { - fsl_cx *const f = fcli_cx(); + fsl_cx *f = fcli_cx(); struct fnc_commit_artifact *commit = NULL; fsl_stmt *q = NULL; fsl_uuid_cstr id = NULL; id = get_selected_commit_id(s->blame.lines, s->blame.nlines, @@ -8609,10 +8104,11 @@ static int close_blame_view(struct fnc_view *view) { struct fnc_blame_view_state *s = &view->state.blame; + struct fsl_list_state st = { FNC_COLOUR_OBJ }; int rc = 0; rc = stop_blame(&s->blame); while (!CONCAT(STAILQ, _EMPTY)(&s->blamed_commits)) { @@ -8621,11 +8117,11 @@ CONCAT(STAILQ, _REMOVE_HEAD)(&s->blamed_commits, entry); fnc_commit_qid_free(blamed_commit); } fsl_free(s->path); - free_colours(&s->colours); + fsl_list_clear(&s->colours, fsl_list_object_free, &st); return rc; } static int @@ -8764,12 +8260,11 @@ if (rc) goto end; rc = view_loop(view); end: - if (rc) - fnc_free_branches(&view->state.branch.branches); + fnc_free_branches(&view->state.branch); fsl_free(glob); return rc; } static int @@ -8788,22 +8283,20 @@ rc = fnc_load_branches(s); if (rc) return rc; - if (s->colour) { - STAILQ_INIT(&s->colours); - rc = set_colours(&s->colours, FNC_VIEW_BRANCH); - } + if (s->colour) + init_pair(FNC_COMMIT_ID, COLOR_GREEN, -1); view->show = show_branch_view; view->input = branch_input_handler; view->close = close_branch_view; view->search_init = branch_search_init; view->search_next = branch_search_next; - return rc; + return 0; } static int fnc_load_branches(struct fnc_branch_view_state *s) { @@ -8843,63 +8336,47 @@ if (s->branch_glob) { char *like = fsl_mprintf("%%%%%s%%%%", s->branch_glob); rc = fsl_buffer_appendf(&sql, " AND name LIKE %Q", like); fsl_free(like); - if (rc) - goto end; - } - - if (FLAG_CHK(s->branch_flags, BRANCH_LS_NO_PRIVATE)) { - rc = fsl_buffer_append(&sql, " AND NOT isprivate", -1); - if (rc) - goto end; - } - + } + if (FLAG_CHK(s->branch_flags, BRANCH_LS_NO_PRIVATE)) + rc = fsl_buffer_append(&sql, " AND NOT isprivate", -1); if (FLAG_CHK(s->branch_flags, BRANCH_SORT_MTIME)) rc = fsl_buffer_append(&sql, " ORDER BY -mtime", -1); else if (FLAG_CHK(s->branch_flags, BRANCH_SORT_STATUS)) rc = fsl_buffer_append(&sql, " ORDER BY isclosed", -1); else rc = fsl_buffer_append(&sql, " ORDER BY name COLLATE nocase", -1); - if (!rc && FLAG_CHK(s->branch_flags, BRANCH_SORT_REVERSE)) + + if (!rc && (FLAG_CHK(s->branch_flags, BRANCH_SORT_REVERSE))) rc = fsl_buffer_append(&sql," DESC", -1); - if (rc) - goto end; stmt = fsl_stmt_malloc(); - if (stmt == NULL) { - rc = RC(FSL_RC_ERROR, "%s", "fsl_stmt_malloc"); - goto end; - } - - rc = fsl_cx_prepare(f, stmt, fsl_buffer_cstr(&sql)); - if (rc) - goto end; + if (stmt == NULL) + goto end; + if (!rc) + rc = fsl_cx_prepare(f, stmt, fsl_buffer_cstr(&sql)); fsl_ckout_version_info(f, &ckoutrid, NULL); curr_branch = fsl_db_g_text(fsl_needs_repo(f), NULL, "SELECT value FROM tagxref WHERE rid=%d AND tagid=%d", ckoutrid, 8); while (fsl_stmt_step(stmt) == FSL_RC_STEP_ROW) { struct fnc_branch *new_branch; struct fnc_branchlist_entry *be; const char *brname = fsl_stmt_g_text(stmt, 0, NULL); - bool priv = (curr_branch && fsl_stmt_g_int32(stmt, 1) == 1); + bool private = (curr_branch && fsl_stmt_g_int32(stmt, 1) == 1); bool open = fsl_stmt_g_int32(stmt, 2) == 0; double mtime = fsl_stmt_g_int64(stmt, 3); bool curr = curr_branch && !fsl_strcmp(curr_branch, brname); if ((s->when > 0 && mtime < s->dateline) || (s->when < 0 && mtime > s->dateline)) continue; - rc = alloc_branch(&new_branch, brname, mtime, open, priv, curr); - if (rc) - goto end; - rc = fnc_branchlist_insert(&be, &s->branches, new_branch); - if (rc) - goto end; + alloc_branch(&new_branch, brname, mtime, open, private, curr); + fnc_branchlist_insert(&be, &s->branches, new_branch); if (be) be->idx = s->nbranches++; } s->first_branch_onscreen = TAILQ_FIRST(&s->branches); @@ -9033,11 +8510,10 @@ static int show_branch_view(struct fnc_view *view) { struct fnc_branch_view_state *s = &view->state.branch; struct fnc_branchlist_entry *be; - struct fnc_colour *c = NULL; char *line = NULL; wchar_t *wline; int limit, n, width, rc = 0; werase(view->window); @@ -9057,18 +8533,14 @@ if (rc) { fsl_free(line); return rc; } if (screen_is_shared(view)) - wattron(view->window, A_REVERSE); + wstandout(view->window); waddwstr(view->window, wline); - while (width < view->ncols) { - waddch(view->window, ' '); - ++width; - } if (screen_is_shared(view)) - wattroff(view->window, A_REVERSE); + wstandend(view->window); fsl_free(wline); wline = NULL; fsl_free(line); line = NULL; if (width < view->ncols - 1) @@ -9088,13 +8560,10 @@ s->show_date ? ':' : 0, s->show_date ? be->branch->date : 0); if (line == NULL) return RC(FSL_RC_ERROR, "%s", "fsl_mprintf"); - if (s->colour) - c = match_colour(&s->colours, line); - rc = formatln(&wline, &width, line, view->ncols, 0); if (rc) { fsl_free(line); return rc; } @@ -9102,15 +8571,15 @@ if (n == s->selected) { if (view->active) wattr_on(view->window, A_REVERSE, NULL); s->selected_branch = be; } - if (c) - wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); + if (s->colour) + wattr_on(view->window, COLOR_PAIR(FNC_COMMIT_ID), NULL); waddwstr(view->window, wline); - if (c) - wattr_off(view->window, COLOR_PAIR(c->scheme), NULL); + if (s->colour) + wattr_off(view->window, COLOR_PAIR(FNC_COMMIT_ID), NULL); if (width < view->ncols - 1) waddch(view->window, '\n'); if (n == s->selected && view->active) wattr_off(view->window, A_REVERSE, NULL); @@ -9242,11 +8711,11 @@ } branch_scroll_down(s, view->nlines - 1); break; case CTRL('l'): case 'R': - fnc_free_branches(&s->branches); + fnc_free_branches(s); s->branch_glob = NULL; /* Shared pointer. */ s->when = 0; s->branch_flags = BRANCH_LS_OPEN_CLOSED; rc = fnc_load_branches(s); break; @@ -9274,15 +8743,20 @@ rid = fsl_uuid_to_rid(fcli_cx(), be->branch->id); if (rid < 0) return RC(rc, "%s", "fsl_uuid_to_rid"); timeline_view = view_open(0, 0, 0, start_col, FNC_VIEW_TIMELINE); - if (timeline_view == NULL) - return RC(FSL_RC_ERROR, "%s", "view_open"); + if (timeline_view == NULL) { + rc = RC(FSL_RC_ERROR, "%s", "view_open"); + goto end; + } - rc = open_timeline_view(timeline_view, rid, "/", NULL); - if (!rc) + rc = open_timeline_view(timeline_view, rid, "/"); +end: + if (rc) + view_close(timeline_view); + else *new_view = timeline_view; return rc; } static int @@ -9434,24 +8908,23 @@ static int close_branch_view(struct fnc_view *view) { struct fnc_branch_view_state *s = &view->state.branch; - fnc_free_branches(&s->branches); - free_colours(&s->colours); + fnc_free_branches(s); return 0; } static void -fnc_free_branches(struct fnc_branchlist_head *branches) +fnc_free_branches(struct fnc_branch_view_state *s) { struct fnc_branchlist_entry *be; - while (!TAILQ_EMPTY(branches)) { - be = TAILQ_FIRST(branches); - TAILQ_REMOVE(branches, be, entries); + while (!TAILQ_EMPTY(&s->branches)) { + be = TAILQ_FIRST(&s->branches); + TAILQ_REMOVE(&s->branches, be, entries); fnc_branch_close(be->branch); fsl_free(be); } }