Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From 0.5 To 0.6
2021-11-21 15:32 | Bump version number: 0.7 (check-in: f3d6c2ff7b user: mark tags: trunk) | |
2021-11-21 15:28 | CHANGES for 0.6 (check-in: bbf8590008 user: mark tags: trunk, 0.6) | |
2021-11-21 14:40 | Add user-defined colour support to the branch view. (check-in: 3d1f3389a3 user: mark tags: trunk) | |
2021-11-03 11:34 | Bump version number: 0.6 (check-in: 9d73a79fa8 user: mark tags: trunk) | |
2021-11-03 11:28 | CHANGES for 0.5 (check-in: 02e93d1924 user: mark tags: trunk, 0.5) | |
2021-11-03 11:06 | Complete move of fnc_strl{cpy,cat} to the library. (check-in: d10389ebe4 user: mark tags: trunk) | |
Changes to CHANGES.md.
1 2 3 4 5 6 7 | **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 - relocate fnc_strlcpy() and fnc_strlcat() routines to the library | > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | **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 - relocate fnc_strlcpy() and fnc_strlcat() routines to the library |
︙ | ︙ |
Added GNUmakefile.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | # # 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 |
Changes to Makefile.
1 2 3 4 5 6 7 8 | # # FNC Makefile # # CONFIGUGRAION CC ?= cc PREFIX ?= /usr/local MANDIR ?= /share/man | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # # FNC Makefile # # CONFIGUGRAION CC ?= cc PREFIX ?= /usr/local MANDIR ?= /share/man VERSION ?= 0.6 # USED BELOW FOR PLATFORM SPECIFIC LDFLAGS UNAME != 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 \ |
︙ | ︙ | |||
34 35 36 37 38 39 40 | -DSQLITE_ENABLE_FTS5 \ -DSQLITE_ENABLE_STMTVTAB \ -DSQLITE_HAVE_ZLIB \ -DSQLITE_INTROSPECTION_PRAGMAS \ -DSQLITE_ENABLE_DBPAGE_VTAB \ -DSQLITE_TRUSTED_SCHEMA=0 | < < < | | | | | | | | | | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | -DSQLITE_ENABLE_FTS5 \ -DSQLITE_ENABLE_STMTVTAB \ -DSQLITE_HAVE_ZLIB \ -DSQLITE_INTROSPECTION_PRAGMAS \ -DSQLITE_ENABLE_DBPAGE_VTAB \ -DSQLITE_TRUSTED_SCHEMA=0 # 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. .if $(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 ${CC} ${SQLITE_CFLAGS} -c $< -o $@ lib/libfossil.o: lib/libfossil.c lib/libfossil.h ${CC} ${FOSSIL_CFLAGS} -c $< -o $@ src/fnc.o: src/fnc.c include/settings.h ${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: |
︙ | ︙ |
Changes to README.md.
1 2 | # README | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # README # fnc 0.5 ## An 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 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) 4. Ubuntu 18.04 running Linux kernel 5.11 (32-bit ARM) 5. Debian GNU/Linux 8, 9, and 10 |
︙ | ︙ |
Added include/settings.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | /* * Copyright (c) 2021 Mark Jamsek <mark@jamsek.com> * * 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, |
Changes to lib/libfossil-config.h.
1 2 3 4 5 | #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" | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 | #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" /* Tweak the following for your system... */ #if !defined(HAVE_COMPRESS) # define HAVE_COMPRESS 1 #endif #if !defined(HAVE_DLFCN_H) # define HAVE_DLFCN_H 0 #endif |
︙ | ︙ | |||
79 80 81 82 83 84 85 | #if !defined(HAVE_STDINT_H) # define HAVE_STDINT_H 1 #endif #endif /* _WIN32 */ | | | | | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | #if !defined(HAVE_STDINT_H) # define HAVE_STDINT_H 1 #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" #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" #define FSL_PLATFORM_PATH_SEPARATOR ";" #define FSL_CHECKOUTDB_NAME "./_FOSSIL_" |
︙ | ︙ | |||
132 133 134 135 136 137 138 139 140 141 142 | #define FSL_PLATFORM_IS_WINDOWS 0 #define FSL_PLATFORM_IS_UNIX 1 #define FSL_PLATFORM_PLATFORM "unix" #define FSL_PLATFORM_PATH_SEPARATOR ":" #define FSL_CHECKOUTDB_NAME "./.fslckout" #endif #endif /* _NET_FOSSIL_SCM_FSL_AMALGAMATION_CONFIG_H_INCLUDED_ */ | > > > > > > > > > > > | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | #define FSL_PLATFORM_IS_WINDOWS 0 #define FSL_PLATFORM_IS_UNIX 1 #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_ */ |
Changes to lib/libfossil.c.
︙ | ︙ | |||
35 36 37 38 39 40 41 | printf pfexp; \ } while(0) /* Please keep all fsl_XXX_empty initializers in one place (here) and lexically sorted. */ | | | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | printf pfexp; \ } while(0) /* 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_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; const fsl_card_Q fsl_card_Q_empty = fsl_card_Q_empty_m; const fsl_card_T fsl_card_T_empty = fsl_card_T_empty_m; |
︙ | ︙ | |||
65 66 67 68 69 70 71 | const fsl_confirm_response fsl_confirm_response_empty = fsl_confirm_response_empty_m; 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; | | | > | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | const fsl_confirm_response fsl_confirm_response_empty = fsl_confirm_response_empty_m; 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_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_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 = fsl_repo_extract_state_empty_m; const fsl_repo_open_ckout_opt fsl_repo_open_ckout_opt_empty = |
︙ | ︙ | |||
157 158 159 160 161 162 163 | case FSL_STRLEN_K256: return x; default: return 0; } } void fsl_error_clear( fsl_error * const err ){ | < | | < < | | | | | < < | | 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | case FSL_STRLEN_K256: return x; default: return 0; } } void fsl_error_clear( fsl_error * const 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; } int fsl_error_copy( fsl_error const * const src, fsl_error * const dest ){ if(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){ rc = fsl_buffer_append( &dest->msg, src->msg.mem, src->msg.used ); } |
︙ | ︙ | |||
195 196 197 198 199 200 201 | lower->code = 0; lower->msg.used = lower->msg.cursor = 0; *higher = err; } int fsl_error_setv( fsl_error * const err, int code, char const * fmt, va_list args ){ | < | | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | lower->code = 0; lower->msg.used = lower->msg.cursor = 0; *higher = err; } int fsl_error_setv( fsl_error * const err, int code, char const * fmt, va_list args ){ if(!code){ /* clear error state */ err->code = 0; err->msg.used = err->msg.cursor = 0; if(err->msg.mem){ err->msg.mem[0] = 0; } return 0; }else{ |
︙ | ︙ | |||
230 231 232 233 234 235 236 | va_end(args); return rc; } int fsl_error_get( fsl_error const * const err, char const ** str, fsl_size_t * const len ){ | < < | | | | | < | 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | va_end(args); return rc; } 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; } char const * fsl_rc_cstr(int rc){ fsl_rc_e const RC = (fsl_rc_e)rc /* we do this so that gcc will warn if the switch() below is missing any fsl_rc_e entries. */ |
︙ | ︙ | |||
265 266 267 268 269 270 271 272 273 274 275 276 277 278 | STR(DELTA_INVALID_SEPARATOR); STR(DELTA_INVALID_SIZE); STR(DELTA_INVALID_TERMINATOR); STR(DIFF_BINARY); STR(DIFF_WS_ONLY); STR(end); STR(ERROR); STR(IO); STR(MISSING_INFO); STR(MISUSE); STR(NOOP); STR(NOT_A_CKOUT); STR(NOT_A_REPO); STR(NOT_FOUND); | > | 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | STR(DELTA_INVALID_SEPARATOR); STR(DELTA_INVALID_SIZE); 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); STR(NOT_A_REPO); STR(NOT_FOUND); |
︙ | ︙ | |||
401 402 403 404 405 406 407 | } } char * fsl_strdup( char const * src ){ return fsl_strndup(src, -1); } | | | | | | 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 | } } 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; if(dstsz<1){ goto end; } while((*(dst+offset) = *(src+offset))!='\0'){ if(++offset == dstsz){ --offset; break; } } end: *(dst+offset) = '\0'; while(*(src+offset)!='\0'){ ++offset; /* Return src length. */ } return offset; } fsl_size_t fsl_strlcat(char *dst, const char *src, fsl_size_t dstsz){ fsl_size_t offset; int dstlen, srclen, idx = 0; offset = dstlen = fsl_strlen(dst); srclen = fsl_strlen(src); while((*(dst+offset++) = *(src+idx++))!='\0'){ if(offset==dstsz-1){ |
︙ | ︙ | |||
505 506 507 508 509 510 511 | } if(0==fsl_strncmp(buf, "off", 3)) return false; return true; } } } | | | 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 | } if(0==fsl_strncmp(buf, "off", 3)) return false; return true; } } } char * fsl_user_name_guess(){ char const ** e; static char const * list[] = { "FOSSIL_USER", #if defined(_WIN32) "USERNAME", #else "USER", |
︙ | ︙ | |||
538 539 540 541 542 543 544 | rv = kludge; break; } } return rv; } | | | | 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 | rv = kludge; break; } } return rv; } 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."); abort(); }else{ va_list args; inFatal = true; fsl_fprintf(stderr, "FATAL ERROR: code=%d (%s)\n", code, fsl_rc_cstr(code)); if(fmt){ |
︙ | ︙ | |||
945 946 947 948 949 950 951 | #endif } char fsl_isatty(int fd){ return isatty(fd) ? 1 : 0; } | | | 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 | #endif } char fsl_isatty(int fd){ return isatty(fd) ? 1 : 0; } 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; if(nameLen<0) nameLen = (fsl_int_t)fsl_strlen(zPath); zEnd = zPath + nameLen; |
︙ | ︙ | |||
976 977 978 979 980 981 982 | bool fsl_is_reserved_fn(const char *zFilename, fsl_int_t nameLen){ fsl_size_t nFilename = nameLen>=0 ? (fsl_size_t)nameLen : fsl_strlen(zFilename); char const * zEnd; int gotSuffix = 0; assert( zFilename && "API misuse" ); #if FSL_PLATFORM_IS_WINDOWS // || 1 | | | 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 | bool fsl_is_reserved_fn(const char *zFilename, fsl_int_t nameLen){ fsl_size_t nFilename = nameLen>=0 ? (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)){ return true; } #endif if( nFilename<8 ) return false; /* strlen("_FOSSIL_") */ zEnd = zFilename + nFilename; if( nFilename>=12 ){ /* strlen("_FOSSIL_-(shm|wal)") */ /* Check for (-wal, -shm, -journal) suffixes, with an eye towards |
︙ | ︙ | |||
1064 1065 1066 1067 1068 1069 1070 | /* ** The status of an annotation operation is recorded by an instance ** of the following structure. */ typedef struct Annotator Annotator; struct Annotator { | | | 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 | /* ** 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_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) */ short int iVers; /* Level at which tag was set */ } *aOrig; |
︙ | ︙ | |||
1088 1089 1090 1091 1092 1093 1094 | double mtime; /* [event].[mtime] db entry */ } *aVers; /* For each check-in analyzed */ unsigned int naVers; /* # of entries allocated in this->aVers */ fsl_timer_state timer; }; static const Annotator Annotator_empty = { | | | 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 | double mtime; /* [event].[mtime] db entry */ } *aVers; /* For each check-in analyzed */ unsigned int naVers; /* # of entries allocated in this->aVers */ fsl_timer_state timer; }; static const Annotator Annotator_empty = { fsl__diff_cx_empty_m, fsl_buffer_empty_m/*headVersion*/, NULL/*aOrig*/, 0U/*nOrig*/, 0U/*nVers*/, false/*bMoreToDo*/, 0/*origId*/, 0/*showId*/, NULL/*aVers*/, |
︙ | ︙ | |||
1432 1433 1434 1435 1436 1437 1438 | } int fsl_annotate( fsl_cx * const f, fsl_annotate_opt const * const opt ){ int rc; Annotator ann = Annotator_empty; unsigned int i; | | | 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 | } 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_annotate_step aStep; assert(opt->out); if(opt->limitMs>0) fsl_timer_start(&ann.timer); rc = fsl__annotate_file(f, &ann, opt); if(rc) goto end; memset(&aStep,0,sizeof(fsl_annotate_step)); |
︙ | ︙ | |||
1487 1488 1489 1490 1491 1492 1493 | aStep.stepType = FSL_ANNOTATE_STEP_LIMITED; aStep.mtime = 0.0; } rc = opt->out(opt->outState, opt, &aStep); } end: | | | 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 | aStep.stepType = FSL_ANNOTATE_STEP_LIMITED; aStep.mtime = 0.0; } rc = opt->out(opt->outState, opt, &aStep); } end: fsl__cx_scratchpad_yield(f, scratch); fsl__annotator_clean(&ann); return rc; } #undef MARKER #undef blob_to_utf8_no_bom /* end of file annotate.c */ |
︙ | ︙ | |||
2047 2048 2049 2050 2051 2052 2053 | unsigned int pfLen, void * varg ){ char const * str = (char const *) varg; int rc = 0; char ch = 0; char ch2 = 0; char xbuf[4]; int decoded; | > | | > | 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 | unsigned int pfLen, void * varg ){ char const * str = (char const *) varg; int rc = 0; char ch = 0; char ch2 = 0; char xbuf[4]; int decoded; char const * end = str + pfLen; if( !str || !pfLen ) return 0; ch = *str; while(0==rc && ch && str<end){ if( ch == '%' ){ if(str+2>=end) goto outro/*invalid partial encoding - simply skip it*/; ch = *(++str); ch2 = *(++str); if( isxdigit((int)ch) && isxdigit((int)ch2) ) { decoded = (hexchar_to_int( ch ) * 16) + hexchar_to_int( ch2 ); |
︙ | ︙ | |||
2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 | }else if( ch == '+' ){ xbuf[0] = ' '; xbuf[1] = 0; rc = pf( pfArg, xbuf, 1 ); ch = *(++str); continue; } xbuf[0] = ch; xbuf[1] = 0; rc = pf( pfArg, xbuf, 1 ); ch = *(++str); } return rc; } | > | 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 | }else if( ch == '+' ){ xbuf[0] = ' '; 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); } return rc; } |
︙ | ︙ | |||
2110 2111 2112 2113 2114 2115 2116 | char buf[BufLen]; unsigned int i = 0, j = 0; int ch; char const q = xtype==etSQLESCAPE3 ?'"':'\''; /* Quote character */ char const * escarg = (char const *) varg; bool const isnull = escarg==0; bool const needQuote = | | > > | > > > | > > | 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 | char buf[BufLen]; unsigned int i = 0, j = 0; 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); if( isnull ){ if(xtype==etSQLESCAPE2||xtype==etSQLESCAPE3){ escarg = "NULL"; pfLen = 4; }else{ escarg = "(NULL)";; pfLen = 6; } } if( needQuote ) buf[j++] = q; for(i=0; (ch=escarg[i])!=0 && i<pfLen; ++i){ buf[j++] = ch; if( ch==q ) buf[j++] = ch; if(j+2>=BufLen){ int const rc = pf( pfArg, &buf[0], j ); |
︙ | ︙ | |||
3006 3007 3008 3009 3010 3011 3012 | } #endif #if ! FSLPRINTF_OMIT_HTML case etHTML:{ bufpt = va_arg(ap,char*); length = bufpt ? (int)fsl_strlen(bufpt) : 0; pfrc = spech_string_to_html( pfAppend, pfAppendArg, | | | > < | > | 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 | } #endif #if ! FSLPRINTF_OMIT_HTML case etHTML:{ bufpt = va_arg(ap,char*); length = bufpt ? (int)fsl_strlen(bufpt) : 0; pfrc = spech_string_to_html( pfAppend, pfAppendArg, (precision>=0 && precision<length) ? precision : length, bufpt ); bufpt = NULL; FSLPRINTF_CHECKERR; length = 0; break; } case etURLENCODE:{ bufpt = va_arg(ap,char*); length = bufpt ? (int)fsl_strlen(bufpt) : 0; pfrc = spech_urlencode( pfAppend, pfAppendArg, (precision>=0 && precision<length) ? precision : length, bufpt ); bufpt = NULL; FSLPRINTF_CHECKERR; length = 0; break; } case etURLDECODE:{ bufpt = va_arg(ap,char*); length = bufpt ? (int)fsl_strlen(bufpt) : 0; pfrc = spech_urldecode( pfAppend, pfAppendArg, (precision>=0 && precision<length) ? precision : length, bufpt ); bufpt = NULL; FSLPRINTF_CHECKERR; length = 0; break; } #endif /* FSLPRINTF_OMIT_HTML */ #if ! FSLPRINTF_OMIT_SQL case etBLOBSQL: |
︙ | ︙ | |||
3054 3055 3056 3057 3058 3059 3060 | length = b ? (int)fsl_buffer_size(b) : 0; if(flag_altform2) xtype = etSQLESCAPE3; }else{ bufpt = va_arg(ap,char*); length = bufpt ? (int)strlen(bufpt) : 0; } pfrc = spech_sqlstring( xtype, pfAppend, pfAppendArg, | | | 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 | length = b ? (int)fsl_buffer_size(b) : 0; if(flag_altform2) xtype = etSQLESCAPE3; }else{ bufpt = va_arg(ap,char*); length = bufpt ? (int)strlen(bufpt) : 0; } pfrc = spech_sqlstring( xtype, pfAppend, pfAppendArg, (precision>=0 && precision<length) ? precision : length, bufpt ); FSLPRINTF_CHECKERR; length = 0; } #endif /* !FSLPRINTF_OMIT_SQL */ }/* End switch over the format type */ /* |
︙ | ︙ | |||
4012 4013 4014 4015 4016 4017 4018 | } void fsl_buffer_swap_free( fsl_buffer * left, fsl_buffer * right, int clearWhich ){ fsl_buffer_swap(left, right); if(0 != clearWhich) fsl_buffer_reserve((clearWhich<0) ? left : right, 0); } | | > | 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 | } void fsl_buffer_swap_free( fsl_buffer * left, fsl_buffer * right, int clearWhich ){ fsl_buffer_swap(left, right); if(0 != clearWhich) fsl_buffer_reserve((clearWhich<0) ? left : right, 0); } int fsl_buffer_copy( fsl_buffer * const dest, fsl_buffer const * const src ){ fsl_buffer_reuse(dest); return src->used ? fsl_buffer_append( dest, src->mem, src->used ) : 0; } int fsl_buffer_delta_apply2( fsl_buffer const * orig, |
︙ | ︙ | |||
4348 4349 4350 4351 4352 4353 4354 | /* Only for debugging */ #include <stdio.h> #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) | | | | | | | | | | > > | | | | | | > > > | | | | | > > > > > > > > > > > > > > > | | | 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 | /* Only for debugging */ #include <stdio.h> #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){ static uint16_t const sentinel = 0xFFFF; uint16_t i; fsl_uint_t mnAge = c->nextAge; uint16_t mn = sentinel; for(i=0; i<c->used; i++){ if( c->list[i].age<mnAge ){ mnAge = c->list[i].age; mn = i; } } if( mn<sentinel ){ fsl_id_bag_remove(&c->inCache, c->list[mn].rid); c->szTotal -= (unsigned)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; if( c->used>c->usedLimit || c->szTotal>c->szLimit ){ fsl_size_t szBefore; do{ szBefore = c->szTotal; fsl__bccache_expire_oldest(c); }while( c->szTotal>c->szLimit && c->szTotal<szBefore ); } if((!c->usedLimit || !c->szLimit) || (c->used+1 >= c->usedLimit)){ fsl_buffer_clear(pBlob); return 0; } if( c->used>=c->capacity ){ uint16_t const cap = c->capacity ? (c->capacity*2) : 10; void * remem = c->list ? fsl_realloc(c->list, cap*sizeof(c->list[0])) : fsl_malloc( cap*sizeof(c->list[0]) ); assert((c->capacity && cap<c->capacity) ? !"Numeric overflow" : 1); if(c->capacity && cap<c->capacity){ fsl__fatal(FSL_RC_RANGE,"Numeric overflow. Bump " "fsl__bccache::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; } void fsl__bccache_clear(fsl__bccache * const c){ #if 0 while(fsl__bccache_expire_oldest(c)){} #else fsl_size_t i; for(i=0; i<c->used; ++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; i<c->used; ++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); } int fsl__bccache_check_available(fsl_cx * const 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; assert(f); assert(c); assert(rid>0); assert(fsl_cx_db_repo(f)); while( depth++ < limit ){ fsl_int_t cSize = -1; if( fsl_id_bag_contains(&c->missing, rid) ){ |
︙ | ︙ | |||
4517 4518 4519 4520 4521 4522 4523 | Returns 0 on success. On error rid and uuid are not modified. */ 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){ | | | | | | | | | 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 | Returns 0 on success. On error rid and uuid are not modified. */ 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_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); if(rc) goto end; assert(fnid>0); rc = fsl_buffer_appendf(nbuf, "%s%s", f->ckout.dir, zRelName); nbuf->used = oldSize; if(rc) goto end; fn = fsl_buffer_cstr(nbuf) + oldSize; rc = fsl_buffer_fill_from_filename( fbuf, fn ); if(rc){ fsl_cx_err_set(f, rc, "Error %s importing file: %s", fsl_rc_cstr(rc), fn); goto end; }else if(!allowMergeConflict && fsl_buffer_contains_merge_marker(fbuf)){ rc = fsl_cx_err_set(f, FSL_RC_CONFLICT, "File contains a merge conflict marker: %s", zRelName); goto end; } rc = fsl__content_put( f, fbuf, &rcRid ); if(!rc){ assert(rcRid > 0); if(parentRid>0){ rc = fsl__content_deltify(f, parentRid, rcRid, 0); } if(!rc){ if(rid) *rid = rcRid; if(uuid){ *uuid = fsl_rid_to_uuid(f, rcRid); 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); assert(0==fbuf->used); return rc; } int fsl_filename_to_vfile_ids( fsl_cx * f, fsl_id_t vid, fsl_id_bag * dest, char const * zName, bool changedOnly){ 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); if(0>=vid) vid = f->ckout.rid; if(zName && *zName && !('.'==*zName && !zName[1])){ rc = fsl_buffer_appendf(sql, "SELECT id FROM vfile WHERE vid=%" FSL_ID_T_PFMT " AND fsl_match_vfile_or_dir(pathname,%Q)", |
︙ | ︙ | |||
4605 4606 4607 4608 4609 4610 4611 | if(rc) goto end; rc = fsl_db_prepare(db, &st, "%b", sql); 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: | | | 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 | if(rc) goto end; rc = fsl_db_prepare(db, &st, "%b", sql); 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_stmt_finalize(&st); if(rc && !f->error.code && db->error.code){ fsl_cx_uplift_db_error(f, db); } return rc; } |
︙ | ︙ | |||
4693 4694 4695 4696 4697 4698 4699 | fsl_stmt_reset(st); return rc; } 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; | | | 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 | fsl_stmt_reset(st); return rc; } 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_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); if(rc) return fsl_cx_uplift_db_error2(f, db, rc); if(opt->vfileIds){ |
︙ | ︙ | |||
4745 4746 4747 4748 4749 4750 4751 | } end: if(opt->vfileIds){ assert(!canon); assert(!_vfileIds.list); }else{ assert(canon); | | | | | | | 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 | } end: if(opt->vfileIds){ assert(!canon); assert(!_vfileIds.list); }else{ assert(canon); fsl__cx_scratchpad_yield(f, canon); fsl_id_bag_clear(&_vfileIds); } fsl_stmt_finalize(&qName); if(rc) fsl_db_transaction_rollback(db); else{ rc = fsl_cx_uplift_db_error2(f, db, fsl_db_transaction_commit(db)); } return rc; } int fsl_checkin_dequeue(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; int rc = fsl_db_transaction_begin(db); if(rc) return fsl_cx_uplift_db_error2(f, db, rc); 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); rc = fsl_ckout_filename_check(f, opt->relativeToCwd, opt->filename, canon); if(rc) goto end; else fsl_buffer_strip_slashes(canon); } fn = canon ? fsl_buffer_cstr(canon) : opt->filename; rc = fsl_filename_to_vfile_ids(f, 0, &list, fn, false); if(!rc && list.entryCount){ /* Walk through each found ID and dequeue up any which are enqueued. */ for( fsl_id_t nid = fsl_id_bag_first(&list); !rc && nid; nid = fsl_id_bag_next(&list, nid)){ if(fsl_id_bag_remove(&f->ckin.selectedIds, nid) && opt->callback){ rc = fsl_xqueue_callback(f, db, &qName, nid, opt); } } } end: 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)); } return rc; } bool fsl_checkin_is_enqueued(fsl_cx * f, char const * zName, bool relativeToCwd){ fsl_db * db; if(!f || !zName || !*zName) return 0; else if(!(db = fsl_needs_ckout(f))) return 0; else if(!f->ckin.selectedIds.entryCount){ /* Behave like fsl_is_enqueued() SQL function. */ return true; } else { bool rv = false; 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); rv = (rc && (vfid>0)) ? false : ((vfid>0) ? fsl_id_bag_contains(&f->ckin.selectedIds, vfid) /* ^^^^ asserts that arg2!=0*/ : false); } fsl__cx_scratchpad_yield(f, canon); return rv; } } void fsl_checkin_discard(fsl_cx * f){ if(f){ |
︙ | ︙ | |||
4975 4976 4977 4978 4979 4980 4981 | assert(!mergeRid); /* An unselected ADDed file. Skip it. */ continue; } if(isExe) perm = FSL_FILE_PERM_EXE; else if(isLink){ | | | | 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 | assert(!mergeRid); /* An unselected ADDed file. Skip it. */ continue; } if(isExe) perm = FSL_FILE_PERM_EXE; else if(isLink){ fsl__fatal(FSL_RC_NYI, "This code does not yet deal " "with symlinks. file: %s", zName) /* does not return */; perm = FSL_FILE_PERM_LINK; } /* TODO: symlinks */ if(!f->cache.markPrivate){ rc = fsl_content_make_public(f, frid); if(rc) break; } #if 0 if(mergeRid && (mergeRid != rid)){ 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){ /* Baseline has files with lexically smaller names. Interesting corner case: |
︙ | ︙ | |||
5645 5646 5647 5648 5649 5650 5651 | "UPDATE vfile SET vid=%" FSL_ID_T_PFMT ";" "DELETE FROM vfile WHERE deleted AND " "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); | | | | 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 | "UPDATE vfile SET vid=%" FSL_ID_T_PFMT ";" "DELETE FROM vfile WHERE deleted AND " "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); RC; assert(d->f == f); rc = fsl_checkin_add_unsent(f, d->rid); RC; 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). db_multi_exec("PRAGMA %s.application_id=252006673;", db_name("repository")); db_multi_exec("PRAGMA %s.application_id=252006674;", db_name("localdb")); |
︙ | ︙ | |||
5710 5711 5712 5713 5714 5715 5716 | if(d->P.used){ /* deltify the parent manifest */ 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); | | | 5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 | if(d->P.used){ /* deltify the parent manifest */ 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; } end: f->flags = oldFlags; #undef RC f->cache.markPrivate = oldPrivate; |
︙ | ︙ | |||
5802 5803 5804 5805 5806 5807 5808 | } #endif else{ char const * zLocalRoot; char const * zFull; fsl_size_t nLocalRoot; fsl_size_t nFull; | | | 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 | } #endif else{ char const * zLocalRoot; char const * zFull; fsl_size_t nLocalRoot; fsl_size_t nFull; 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); assert(*zLocalRoot); nLocalRoot = f->ckout.dirLen; |
︙ | ︙ | |||
5872 5873 5874 5875 5876 5877 5878 | } if(pOut){ rc = fsl_buffer_append(pOut, zFull + nLocalRoot, nFull - nLocalRoot); } end: | | | | 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 | } if(pOut){ rc = fsl_buffer_append(pOut, zFull + nLocalRoot, nFull - nLocalRoot); } end: fsl__cx_scratchpad_yield(f, full); } return rc; } /** Returns a fsl_ckout_change_e value for the given fsl_vfile_change_e value. Why are these not consolidated into one enum? 2021-03-13: because 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){ 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); EE(MERGE_ADD); EE(INTEGRATE_MOD); |
︙ | ︙ | |||
5968 5969 5970 5971 5972 5973 5974 | if( fsl_cx_stat(f, false, name, &fstat ) ){ coChange = FSL_CKOUT_CHANGE_MISSING; fsl_cx_err_reset(f) /* keep FSL_RC_NOT_FOUND from bubbling up to the client! */; }else if(!changed){ continue; }else{ | | | 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 | if( fsl_cx_stat(f, false, name, &fstat ) ){ coChange = FSL_CKOUT_CHANGE_MISSING; 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); } } if(!coChange){ MARKER(("INTERNAL ERROR: unhandled vfile.chnged " "value %d for file [%s]\n", changed, name)); continue; |
︙ | ︙ | |||
6138 6139 6140 6141 6142 6143 6144 | fsl_dircrawl_f_add, cas); if(rc && !cas->f->error.code){ rc = fsl_cx_err_set(cas->f, rc, "fsl_dircrawl() returned %s.", fsl_rc_cstr(rc)); } }else{ assert(!"Cannot happen - caught higher up"); | | | 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 | fsl_dircrawl_f_add, cas); if(rc && !cas->f->error.code){ 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().", __func__); } break; default: rc = fsl_cx_err_set(cas->f, FSL_RC_TYPE, "Unhandled filesystem entry type: " "fsl_fstat_type_e #%d", cas->fst.type); |
︙ | ︙ | |||
6171 6172 6173 6174 6175 6176 6177 | fsl_ckout_manage_opt opt; if(!f) return FSL_RC_MISUSE; 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. */; | | | | | | | 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 | fsl_ckout_manage_opt opt; if(!f) return FSL_RC_MISUSE; 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); 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); 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, 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 " "[%s](id); " "DELETE FROM [%s] /* %s() */;", |
︙ | ︙ | |||
6229 6230 6231 6232 6233 6234 6235 | return rc; dberr: assert(rc); rc = fsl_cx_uplift_db_error2(f, db, rc); goto end; } | | | 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 | return rc; dberr: 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 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; bool inTrans = false; if(!db) return FSL_RC_NOT_A_CKOUT; |
︙ | ︙ | |||
6268 6269 6270 6271 6272 6273 6274 | "AND deleted " "AND id IN fx_unmanage_id " "/* %s() */", vid, __func__); if(rc) goto dberr; } }else{// Process opt->filename | | | 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 | "AND deleted " "AND id IN fx_unmanage_id " "/* %s() */", vid, __func__); if(rc) goto dberr; } }else{// Process opt->filename 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)); */ assert(zNorm); if(fname->used){ |
︙ | ︙ | |||
6325 6326 6327 6328 6329 6330 6331 | /* Remove rm'd ADDed-but-not-yet-committed entries... */ rc = fsl_db_exec(db, "DELETE FROM vfile WHERE vid=%" FSL_ID_T_PFMT " AND rid=0 AND deleted", vid); if(rc) goto dberr; end: | | | | | 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 | /* Remove rm'd ADDed-but-not-yet-committed entries... */ rc = fsl_db_exec(db, "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); fsl_stmt_finalize(&q); if(opt->vfileIds){ fsl_db_exec(db, "DROP TABLE IF EXISTS fx_unmanage_id /* %s() */", __func__) /* Ignoring result code */; } if(inTrans){ int const rc2 = fsl_db_transaction_end(db, !!rc); if(!rc) rc = rc2; } return rc; dberr: assert(rc); rc = fsl_cx_uplift_db_error2(f, db, rc); goto end; } int fsl_ckout_changes_scan(fsl_cx * const f){ return fsl_vfile_changes_scan(f, -1, 0); } int fsl_ckout_install_schema(fsl_cx * const f, bool dropIfExists){ char const * tNames[] = { "vvar", "vfile", "vmerge", 0 }; int rc; fsl_db * const db = fsl_needs_ckout(f); if(!db) return f->error.code; if(dropIfExists){ |
︙ | ︙ | |||
6389 6390 6391 6392 6393 6394 6395 | return fsl_db_exists(db, "SELECT 1 FROM vfile WHERE chnged " "OR coalesce(origname != pathname, 0) " "/*%s()*/", __func__) || fsl_db_exists(db,"SELECT 1 FROM vmerge /*%s()*/", __func__); } | | | 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 6425 6426 6427 6428 | return fsl_db_exists(db, "SELECT 1 FROM vfile WHERE chnged " "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 ){ 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); }else{ rc = FSL_RC_NOT_A_CKOUT; |
︙ | ︙ | |||
6467 6468 6469 6470 6471 6472 6473 | if(opt->targetDir && *opt->targetDir){ if(fsl_chdir(opt->targetDir)){ return fsl_cx_err_set(f, FSL_RC_NOT_FOUND, "Directory not found or inaccessible: %s", opt->targetDir); } } | | | | 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 | if(opt->targetDir && *opt->targetDir){ if(fsl_chdir(opt->targetDir)){ return fsl_cx_err_set(f, FSL_RC_NOT_FOUND, "Directory not found or inaccessible: %s", opt->targetDir); } } cwd = fsl__cx_scratchpad(f); assert(!cwd->used); if((rc = fsl_cx_getcwd(f, cwd))){ assert(!cwd->used); 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)); } /** AS OF HERE: do not use 'return'. Use goto end so that we can chdir() back to our original cwd! |
︙ | ︙ | |||
6500 6501 6502 6503 6504 6505 6506 | } } 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). */ | | | | 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 | } } 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); 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); if(rc) goto end; } /** Create and attach ckout db... */ assert(!fsl_cx_db_ckout(f)); |
︙ | ︙ | |||
6542 6543 6544 6545 6546 6547 6548 | } if(rc) rc = fsl_cx_uplift_db_error(f, theDbC); end: if(opt->targetDir && *opt->targetDir && cwd->used){ fsl_chdir(fsl_buffer_cstr(cwd)) /* Ignoring error because we have no recovery strategy! */; } | | > | | | 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 6599 6600 6601 6602 6603 6604 6605 | } if(rc) rc = fsl_cx_uplift_db_error(f, theDbC); 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); 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", dbR->filename); } 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, 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_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); }else if(!f->ckout.dir){ return fsl_cx_err_set(f, FSL_RC_NOT_A_CKOUT, |
︙ | ︙ | |||
6610 6611 6612 6613 6614 6615 6616 | mod = 0x10; }else{ rc = fsl_cx_err_set(f, rc, "%s(): stat() failed for file: %s", __func__, zFilename); } goto end; } | | | | | | 6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 | mod = 0x10; }else{ rc = fsl_cx_err_set(f, rc, "%s(): stat() failed for file: %s", __func__, zFilename); } goto end; } 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()", __func__); } if(rc){ rc = fsl_cx_err_set(f, rc, "%s: error hashing file: %s", __func__, zFilename); }else{ assert(hashLen==(int)hash->used); mod |= memcmp(hash->mem, zOrigHash, (size_t)hashLen) ? 0x02 : 0; /*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); return rc; } /** Infrastructure for fsl_repo_ckout(), fsl_ckout_update(), and fsl_ckout_merge(). */ |
︙ | ︙ | |||
6844 6845 6846 6847 6848 6849 6850 | : FSL_CRESPONSE_YES; // Overwrite it coState.fileChangeType = isSameFile ? FSL_CKUP_FCHANGE_NONE : FSL_CKUP_FCHANGE_UPDATED; break; } default: | | | 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 | : FSL_CRESPONSE_YES; // Overwrite it coState.fileChangeType = isSameFile ? FSL_CKUP_FCHANGE_NONE : FSL_CKUP_FCHANGE_UPDATED; break; } default: fsl__fatal(FSL_RC_UNSUPPORTED,"Internal error: invalid " "fsl_reco_is_file_modified() response."); } } switch(rec->confirmAnswer.response){ case FSL_CRESPONSE_NO: case FSL_CRESPONSE_NEVER: // Keep existing. |
︙ | ︙ | |||
6969 6970 6971 6972 6973 6974 6975 | TODO: do not remove dirs from the 'empty-dirs' config setting. */ 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; | | | 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 | TODO: do not remove dirs from the 'empty-dirs' config setting. */ 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_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; fsl_repo_extract_state rxState = fsl_repo_extract_state_empty; fsl_card_F fCard = fsl_card_F_empty; |
︙ | ︙ | |||
7084 7085 7086 7087 7088 7089 7090 | rc = fsl_cx_err_set(f, FSL_RC_BREAK, "Checkout operation cancelled by " "confirmation callback. " "Filesystem contents may now be " "in an inconsistent state!"); goto end; default: | | | 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 | rc = fsl_cx_err_set(f, FSL_RC_BREAK, "Checkout operation cancelled by " "confirmation callback. " "Filesystem contents may now be " "in an inconsistent state!"); goto end; default: fsl__fatal(FSL_RC_UNSUPPORTED,"Internal error: invalid " "fsl_cx_confirm() response #%d.", rec->confirmAnswer.response); break; } if(!cOpt->callback) continue; /* Now report the deletion to the callback... */ fsl_id_t const frid = fsl_stmt_g_id(&q, 0); |
︙ | ︙ | |||
7146 7147 7148 7149 7150 7151 7152 | anything but .fslckout and any non-SCM'd content. */; } if(FSL_RC_STEP_DONE==rc) rc = 0; } end: fsl_stmt_finalize(&q); | | | | 7172 7173 7174 7175 7176 7177 7178 7179 7180 7181 7182 7183 7184 7185 7186 7187 7188 7189 7190 7191 7192 7193 7194 7195 7196 7197 7198 7199 7200 | anything but .fslckout and any non-SCM'd content. */; } if(FSL_RC_STEP_DONE==rc) rc = 0; } end: fsl_stmt_finalize(&q); 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; fsl_id_t const prevRid = f->ckout.rid; fsl_db * const dbR = fsl_needs_repo(f); RepoExtractCkup rec = RepoExtractCkup_empty; 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); if(cOpt->confirmer.callback){ fsl_cx_confirmer(f, &cOpt->confirmer, &oldConfirm); } //MARKER(("ckout.rid=%d\n",(int)prevRid)); if(prevRid>=0 && cOpt->scanForChanges){ /* We need to ensure this state is current in order to determine whether a given file is locally modified vis-a-vis the |
︙ | ︙ | |||
7251 7252 7253 7254 7255 7256 7257 | if(rc) goto end; } rc = fsl_ckout_manifest_write(f, -1, -1, -1, NULL); end: if(!rc){ rc = fsl_vfile_unload_except(f, cOpt->checkinRid); | | | | 7277 7278 7279 7280 7281 7282 7283 7284 7285 7286 7287 7288 7289 7290 7291 7292 7293 7294 7295 7296 7297 7298 7299 7300 7301 7302 7303 | if(rc) goto end; } 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); } /* TODO: if "repo-cksum" config db setting is set, confirm R-card of cOpt->checkinRid against on-disk contents. */ if(cOpt->confirmer.callback){ 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); 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){ fsl_db * const dbR = fsl_needs_repo(f); fsl_db * const dbC = dbR ? fsl_needs_ckout(f) : 0; |
︙ | ︙ | |||
7489 7490 7491 7492 7493 7494 7495 | uState.extractState = &xState; uState.callbackState = cuOpt->callbackState; uState.dryRun = cuOpt->dryRun; uState.fileRmInfo = FSL_CKUP_RM_NOT; rec.originRid = ckRid; rec.eOpt = &eOpt; rec.cOpt = cuOpt; | | | | | | 7515 7516 7517 7518 7519 7520 7521 7522 7523 7524 7525 7526 7527 7528 7529 7530 7531 7532 7533 7534 7535 7536 7537 7538 7539 7540 7541 7542 7543 7544 7545 7546 | uState.extractState = &xState; uState.callbackState = cuOpt->callbackState; 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.tgtDirLen = f->ckout.dirLen; rc = fsl_buffer_append(rec.tgtDir, f->ckout.dir, (fsl_int_t)f->ckout.dirLen); if(rc) goto end; /** Missing features from fossil we still need for this include, but are not limited to: - 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); 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); if(rc) goto end; unsigned int nConflict = 0; |
︙ | ︙ | |||
7591 7592 7593 7594 7595 7596 7597 | ask the user what to do. */ }else{ //fsl_outputf(f, "ADD %s\n", zName); uState.fileChangeType = FSL_CKUP_FCHANGE_ADDED; } //if( !dryRunFlag && !internalUpdate ) undo_save(zName); if( !cuOpt->dryRun ){ | | | | | 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626 7627 7628 7629 7630 7631 7632 7633 7634 7635 7636 7637 7638 7639 7640 7641 7642 7643 7644 7645 7646 7647 7648 7649 7650 7651 7652 7653 | ask the user what to do. */ }else{ //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); 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 ){ //fossil_print("UPDATE %s - change to unmanaged file\n", zName); uState.fileChangeType = FSL_CKUP_FCHANGE_RM; }else{ //fossil_print("UPDATE %s\n", zName); uState.fileChangeType = FSL_CKUP_FCHANGE_UPDATED; } if( !cuOpt->dryRun ){ 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); if(rc) goto end; } }else if( idt==0 && idv>0 ){ /* Is in the current version but not in the target. */ if( ridv==0 ){ /* Added in current checkout. Continue to hold the file as ** as an addition */ |
︙ | ︙ | |||
7786 7787 7788 7789 7790 7791 7792 | */ assert(!rc); rc = fsl_repo_ckout_rm_list_fini(f, &rec); if(!rc){ rc = fsl_vfile_unload_except(f, tid); } if(!rc){ | | | | | | | 7812 7813 7814 7815 7816 7817 7818 7819 7820 7821 7822 7823 7824 7825 7826 7827 7828 7829 7830 7831 7832 7833 7834 7835 | */ assert(!rc); 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); } 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); for(int i = 0; i < MergeBufCount; ++i){ fsl_buffer_clear(&bufMerge[i]); } fsl_stmt_finalize(&rec.stRidSize); fsl_stmt_finalize(&rec.stChanged); fsl_stmt_finalize(&mtimeGet); fsl_stmt_finalize(&mtimeSet); |
︙ | ︙ | |||
7868 7869 7870 7871 7872 7873 7874 | " WHERE tagid=%d AND rid=%" FSL_ID_T_PFMT "))", FSL_TAGID_BRANCH, FSL_TAGID_BRANCH, ckRid ); if(rc) goto end; else if( fsl_leaves_computed_count(f)>1 ){ AmbiguousLeavesOutput alo = AmbiguousLeavesOutput_empty; | | | | 7894 7895 7896 7897 7898 7899 7900 7901 7902 7903 7904 7905 7906 7907 7908 7909 7910 7911 7912 7913 7914 7915 7916 7917 7918 7919 7920 7921 7922 7923 | " WHERE tagid=%d AND rid=%" FSL_ID_T_PFMT "))", 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); 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 " "WHERE rid IN leaves ORDER BY uuid"); if(!rc){ rc = fsl_stmt_each(&q, fsl_stmt_each_f_ambiguous_leaves, &alo); } 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); } end: if(!rc){ tgtRid = fsl_leaves_computed_latest(f); *outRid = tgtRid; fsl_leaves_computed_cleanup(f) /* We might want to keep [leaves] around for the case where we |
︙ | ︙ | |||
7945 7946 7947 7948 7949 7950 7951 | if(!db) return FSL_RC_NOT_A_CKOUT; else if(!f->ckout.rid){ return fsl_cx_err_set(f, FSL_RC_RANGE, "Checkout RID is 0, so it has no manifest."); } int W = 0; int rc = 0; | | | 7971 7972 7973 7974 7975 7976 7977 7978 7979 7980 7981 7982 7983 7984 7985 | if(!db) return FSL_RC_NOT_A_CKOUT; else if(!f->ckout.rid){ 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 * content = &f->fileContent; char * str = 0; fsl_time_t const mtime = f->ckout.mtime>0 ? fsl_julian_to_unix(f->ckout.mtime) : 0; fsl_buffer_reuse(content); if(manifest<0 || manifestUuid<0 || manifestTags<0){ |
︙ | ︙ | |||
8062 8063 8064 8065 8066 8067 8068 | rc = fsl_buffer_append(b, "manifest.tags", 13); if(rc) goto end; fsl_file_unlink(fsl_buffer_cstr(b)); } end: if(wrote) *wrote = W; | | | 8088 8089 8090 8091 8092 8093 8094 8095 8096 8097 8098 8099 8100 8101 8102 | rc = fsl_buffer_append(b, "manifest.tags", 13); if(rc) goto end; fsl_file_unlink(fsl_buffer_cstr(b)); } end: if(wrote) *wrote = W; fsl__cx_scratchpad_yield(f, b); fsl_buffer_reuse(content); return rc; } /** Check every sub-directory of f's current checkout dir along the path to zFilename. If any sub-directory part is really an ordinary file |
︙ | ︙ | |||
8102 8103 8104 8105 8106 8107 8108 | fsl_size_t * errLen); 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; | | | 8128 8129 8130 8131 8132 8133 8134 8135 8136 8137 8138 8139 8140 8141 8142 | fsl_size_t * errLen); 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); 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; } rc = fsl_buffer_append(fn, zFilename, -1); |
︙ | ︙ | |||
8135 8136 8137 8138 8139 8140 8141 | *errLen = j; break; } z[j] = '/'; i = j; } end: | | | | | | 8161 8162 8163 8164 8165 8166 8167 8168 8169 8170 8171 8172 8173 8174 8175 8176 8177 8178 8179 8180 8181 8182 8183 8184 8185 8186 8187 8188 8189 8190 8191 8192 8193 8194 8195 8196 8197 8198 8199 8200 8201 8202 | *errLen = j; break; } z[j] = '/'; i = j; } end: fsl__cx_scratchpad_yield(f, fn); return rc; } int fsl__ckout_safe_file_check(fsl_cx * const 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); 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)){ rc = fsl_cx_err_set(f, FSL_RC_MISUSE, "Path is not rooted at the " "current checkout directory: %s", zFilename); goto end; } fsl_size_t errLen = 0; rc = fsl_ckout_nondir_file_check(f, zFilename, &errLen); if(rc) goto end /* OOM */; else if(errLen){ 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); return rc; } bool fsl_is_rooted_in_ckout(fsl_cx *f, char const *zAbsPath){ return f->ckout.dir ? 0==fsl_strncmp(zAbsPath, f->ckout.dir, f->ckout.dirLen) /* ^^^ fossil(1) uses stricmp() there, but that's a bug. However, |
︙ | ︙ | |||
8185 8186 8187 8188 8189 8190 8191 | rc = fsl_cx_err_set(f, FSL_RC_RANGE, "Path is not rooted " "in the current checkout: %s", zAbsPath); } return rc; } | | | | | | | 8211 8212 8213 8214 8215 8216 8217 8218 8219 8220 8221 8222 8223 8224 8225 8226 8227 8228 8229 8230 8231 8232 8233 8234 8235 8236 8237 8238 8239 8240 8241 8242 8243 8244 | rc = fsl_cx_err_set(f, FSL_RC_RANGE, "Path is not rooted " "in the current checkout: %s", zAbsPath); } return rc; } int fsl__ckout_symlink_create(fsl_cx * const 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); 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); 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); end: fsl__cx_scratchpad_yield(f, fn); return rc; } /** Queues the directory part of the given filename into temp table fx_revert_rmdir for an eventual rmdir() attempt on it in fsl_revert_rmdir_fini(). |
︙ | ︙ | |||
8243 8244 8245 8246 8247 8248 8249 | /** Attempts to rmdir all dirs queued by fsl_revert_rmdir_queue(). Silently 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; | | | | > | 8269 8270 8271 8272 8273 8274 8275 8276 8277 8278 8279 8280 8281 8282 8283 8284 8285 8286 8287 8288 8289 8290 8291 8292 8293 8294 8295 8296 8297 8298 8299 8300 8301 8302 8303 8304 8305 8306 8307 8308 | /** Attempts to rmdir all dirs queued by fsl_revert_rmdir_queue(). Silently 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); rc = fsl_db_prepare(db, &st, "SELECT fsl_ckout_dir()||n " "FROM fx_revert_rmdir " "ORDER BY length(n) DESC /* %s() */", __func__); if(rc) goto dberr; while(FSL_RC_STEP_ROW == fsl_stmt_step(&st)){ fsl_size_t nDir = 0; char const * zDir = fsl_stmt_g_text(&st, 0, &nDir); fsl_buffer_reuse(b); 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_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 ){ /** 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 found here, vis a vis fossil, it might be worth reverting (as it were) to that implementation. |
︙ | ︙ | |||
8289 8290 8291 8292 8293 8294 8295 | fsl_stmt q = fsl_stmt_empty; fsl_stmt vfUpdate = fsl_stmt_empty; 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){ | | | | | 8316 8317 8318 8319 8320 8321 8322 8323 8324 8325 8326 8327 8328 8329 8330 8331 8332 8333 8334 8335 8336 8337 8338 8339 8340 8341 8342 8343 8344 8345 8346 8347 8348 8349 8350 8351 8352 8353 8354 | fsl_stmt q = fsl_stmt_empty; fsl_stmt vfUpdate = fsl_stmt_empty; 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); rc = fsl_ckout_filename_check(f, opt->relativeToCwd, opt->filename, fname); if(rc){ 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); if(fname->used) fsl_buffer_strip_slashes(fname); if(1==fname->used && '.'==*zNorm){ /* Special case: handle "." from ckout root intuitively */ fsl_buffer_reuse(fname); assert(0==*zNorm); } } rc = fsl_db_transaction_begin(db); if(rc) goto dberr; inTrans = true; if(opt->scanForChanges){ rc = fsl_vfile_changes_scan(f, 0, 0); if(rc) goto end; } 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 " ", vid); if(rc) goto end; |
︙ | ︙ | |||
8347 8348 8349 8350 8351 8352 8353 | " OR rid=0" " OR coalesce(origname,pathname)" " <>pathname" ")", -1); } assert(!rc); rc = fsl_db_prepare(db, &q, "%b /* %s() */", sql, __func__); | | | | 8374 8375 8376 8377 8378 8379 8380 8381 8382 8383 8384 8385 8386 8387 8388 8389 8390 8391 8392 | " OR rid=0" " OR coalesce(origname,pathname)" " <>pathname" ")", -1); } assert(!rc); rc = fsl_db_prepare(db, &q, "%b /* %s() */", sql, __func__); fsl__cx_scratchpad_yield(f, sql); sql = 0; if(rc) goto dberr; if((!zNorm || !*zNorm) && !opt->vfileIds){ 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); int32_t const deleted = fsl_stmt_g_int32(&q, 2); char const * const zName = fsl_stmt_g_text(&q, 3, NULL); |
︙ | ︙ | |||
8377 8378 8379 8380 8381 8382 8383 | if(renamed){ if((rc=fsl_mkdir_for_file(zNameOrig, true))){ rc = fsl_cx_err_set(f, rc, "mkdir() failed for file: %s", zNameOrig); break; } /* Move, if possible, the new name back over the original | | | 8404 8405 8406 8407 8408 8409 8410 8411 8412 8413 8414 8415 8416 8417 8418 | if(renamed){ if((rc=fsl_mkdir_for_file(zNameOrig, true))){ 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 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); if(0==mvCheck || FSL_RC_NOT_FOUND==mvCheck){ if(0==fsl_file_rename(zName, zNameOrig)){ |
︙ | ︙ | |||
8403 8404 8405 8406 8407 8408 8409 | "UPDATE vfile SET chnged=0, deleted=0, " "pathname=coalesce(origname,pathname), " "origname=NULL " "WHERE id=?1 /*%s()*/", __func__); if(rc) goto dberr; } rc = fsl_stmt_bind_step(&vfUpdate, "R", id) | | | | 8430 8431 8432 8433 8434 8435 8436 8437 8438 8439 8440 8441 8442 8443 8444 8445 8446 8447 | "UPDATE vfile SET chnged=0, deleted=0, " "pathname=coalesce(origname,pathname), " "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 function writes to vfile.pathname. */; if(rc) goto dberr; rc = fsl__vfile_to_ckout(f, id, &wasWritten); if(rc) break; if(opt->callback){ if(renamed){ changeType = FSL_REVERT_RENAME; }else if(wasWritten){ changeType = (2==wasWritten) ? FSL_REVERT_CONTENTS |
︙ | ︙ | |||
8428 8429 8430 8431 8432 8433 8434 | char const * name = renamed ? zNameOrig : zName; rc = opt->callback(&name[f->ckout.dirLen], changeType, opt->callbackState); if(rc) break; } }/*step() loop*/ end: | | | | 8455 8456 8457 8458 8459 8460 8461 8462 8463 8464 8465 8466 8467 8468 8469 8470 | char const * name = renamed ? zNameOrig : zName; rc = opt->callback(&name[f->ckout.dirLen], 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); fsl_stmt_finalize(&q); fsl_stmt_finalize(&vfUpdate); if(qRmdir.stmt){ fsl_stmt_finalize(&qRmdir); if(!rc) rc = fsl_revert_rmdir_fini(f, db); fsl_db_exec(db, "DROP TABLE IF EXISTS fx_revert_rmdir /* %s() */", __func__); |
︙ | ︙ | |||
8454 8455 8456 8457 8458 8459 8460 | return rc; dberr: assert(rc); rc = fsl_cx_uplift_db_error2(f, db, rc); goto end; } | | | | | | | 8481 8482 8483 8484 8485 8486 8487 8488 8489 8490 8491 8492 8493 8494 8495 8496 8497 8498 8499 8500 8501 8502 8503 8504 8505 8506 8507 8508 8509 8510 8511 8512 8513 8514 8515 8516 | return rc; dberr: 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, bool relativeToCwd, bool changedOnly ) { if(!fsl_needs_ckout(f)) return FSL_RC_NOT_A_CKOUT; 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); 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); rc = fsl_file_canonical_name2( relativeToCwd ? NULL : fsl_cx_ckout_dir_name(f, NULL), zName, fname, 1 ); if(!rc){ assert(fname->used); if('/'==fname->mem[fname->used-1]){ |
︙ | ︙ | |||
8505 8506 8507 8508 8509 8510 8511 | if(rc){ rc = fsl_cx_err_set(f, rc, "%s error reading file; %b", fsl_rc_cstr(rc), fname); } } } } | | | 8532 8533 8534 8535 8536 8537 8538 8539 8540 8541 8542 8543 8544 8545 8546 | if(rc){ rc = fsl_cx_err_set(f, rc, "%s error reading file; %b", fsl_rc_cstr(rc), fname); } } } } fsl__cx_scratchpad_yield(f, fname); return rc; } int fsl_card_F_ckout_mtime(fsl_cx * const f, fsl_id_t vid, fsl_card_F const * fc, fsl_time_t * repoMtime, |
︙ | ︙ | |||
8682 8683 8684 8685 8686 8687 8688 | #if 0 /* 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? */ | | | 8709 8710 8711 8712 8713 8714 8715 8716 8717 8718 8719 8720 8721 8722 8723 | #if 0 /* 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 */; #endif } return rc; } #define fcli__error (fcli.f ? &fcli.f->error : &fcli.err) |
︙ | ︙ | |||
9198 9199 9200 9201 9202 9203 9204 | /** 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){ | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 9225 9226 9227 9228 9229 9230 9231 9232 9233 9234 9235 9236 9237 9238 9239 9240 9241 9242 9243 9244 9245 9246 9247 9248 9249 9250 9251 9252 9253 9254 9255 9256 9257 9258 9259 9260 9261 9262 9263 9264 9265 9266 9267 9268 9269 9270 9271 9272 9273 9274 9275 9276 9277 9278 9279 9280 9281 9282 9283 9284 9285 9286 9287 9288 9289 9290 9291 9292 9293 9294 9295 9296 9297 9298 | /** 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*/; } return rv; } /** Replacement for fsl_memory_allocator() which abort()s on OOM. Why? Because fossil(1) has shown how much that can simplify error checking in an allocates-often API. */ 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 <signal.h> 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 <signal.h> /* 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){ static char once = 0; int rc = 0; |
︙ | ︙ | |||
9294 9295 9296 9297 9298 9299 9300 | } } if(!rc){ char const * userName = fcli.transient.userArg; if(userName){ fsl_cx_user_set(f, userName); }else if(!fsl_cx_user_get(f)){ | | | 9358 9359 9360 9361 9362 9363 9364 9365 9366 9367 9368 9369 9370 9371 9372 | } } 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(); fsl_cx_user_set(f, u); fsl_free(u); } } return rc; } |
︙ | ︙ | |||
9367 9368 9369 9370 9371 9372 9373 9374 9375 9376 9377 9378 9379 9380 9381 9382 9383 9384 9385 | return fcli_setup_v2(argc, argv, NULL, NULL); } 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(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", fcli.appName, file, line, errRc, fsl_rc_cstr(errRc), msg); }else{ fcli_printf("%s: ERROR #%d (%s): %s\n", fcli.appName, errRc, fsl_rc_cstr(errRc), msg); } } | > > > > > | > > | 9431 9432 9433 9434 9435 9436 9437 9438 9439 9440 9441 9442 9443 9444 9445 9446 9447 9448 9449 9450 9451 9452 9453 9454 9455 9456 9457 9458 9459 9460 9461 9462 9463 9464 | return fcli_setup_v2(argc, argv, NULL, NULL); } 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", fcli.appName, file, line, errRc, fsl_rc_cstr(errRc), msg); }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); } return errRc; } const char * fcli_next_arg(bool remove){ const char * rc = (fcli.argc>0) ? fcli.argv[0] : NULL; if(rc && remove){ |
︙ | ︙ | |||
9826 9827 9828 9829 9830 9831 9832 9833 9834 9835 9836 9837 9838 9839 9840 9841 9842 9843 9844 | if(i>0 && (forceVerbose || fcli_is_verbose()>1)){ for( i = 1, st = db->cacheHead; st; ++i, st = st->next ){ f_out("CACHED fsl_stmt #%d (%d hit(s)): %b\n", i, (int)st->cachedHits, &st->sql); } } } #undef FCLI_V3 #undef fcli_empty_m #undef fcli__error #undef MARKER /* 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: */ /* Copyright 2013-2021 The Libfossil Authors, see LICENSES/BSD-2-Clause.txt | > > > > > > > > > > > > > > > | 9897 9898 9899 9900 9901 9902 9903 9904 9905 9906 9907 9908 9909 9910 9911 9912 9913 9914 9915 9916 9917 9918 9919 9920 9921 9922 9923 9924 9925 9926 9927 9928 9929 9930 | if(i>0 && (forceVerbose || fcli_is_verbose()>1)){ for( i = 1, st = db->cacheHead; st; ++i, st = st->next ){ f_out("CACHED fsl_stmt #%d (%d hit(s)): %b\n", i, (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: */ /* Copyright 2013-2021 The Libfossil Authors, see LICENSES/BSD-2-Clause.txt |
︙ | ︙ | |||
9889 9890 9891 9892 9893 9894 9895 | } } 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 ){ | | | | | | | | 9975 9976 9977 9978 9979 9980 9981 9982 9983 9984 9985 9986 9987 9988 9989 9990 9991 9992 9993 9994 9995 9996 9997 9998 9999 10000 10001 10002 10003 10004 10005 10006 10007 10008 10009 10010 10011 10012 10013 10014 10015 10016 | } } 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) ){ return false; }else if( fsl_id_bag_contains(&f->cache.blobContent.available, rid) ){ return true; }else if( fsl_content_size(f, rid)<0 ){ fsl_id_bag_insert(&f->cache.blobContent.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); 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"); } return false; } int fsl_content_blob( fsl_cx * const f, fsl_id_t blobRid, fsl_buffer * const 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; fsl_stmt * q = NULL; rc = fsl_db_prepare_cached( dbR, &q, |
︙ | ︙ | |||
9997 9998 9999 10000 10001 10002 10003 | return fsl_cx_err_set(f, FSL_RC_NOT_A_REPO, "Fossil has no repo opened."); } else{ int rc; bool gotIt = 0; fsl_id_t nextRid; | | > | | > | | | 10083 10084 10085 10086 10087 10088 10089 10090 10091 10092 10093 10094 10095 10096 10097 10098 10099 10100 10101 10102 10103 10104 10105 10106 10107 10108 10109 10110 10111 10112 10113 10114 10115 10116 10117 10118 10119 | return fsl_cx_err_set(f, FSL_RC_NOT_A_REPO, "Fossil has no repo opened."); } else{ int rc; bool gotIt = 0; fsl_id_t nextRid; fsl__bccache * const ac = &f->cache.blobContent; 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) ){ fsl_size_t i; fsl__bccache_line * line; for(i=0; i<ac->used; ++i){ line = &ac->list[i]; if( line->rid==rid ){ ++ac->metrics.hits; rc = fsl_buffer_copy(tgt, &line->content); 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 ){ /* This is not a delta, so get its raw content. */ rc = fsl_content_blob(f, rid, tgt); |
︙ | ︙ | |||
10110 10111 10112 10113 10114 10115 10116 10117 10118 10119 10120 10121 10122 | on the libfossil repo with 2003 checkins takes: 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. */ //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])); | > > > > | | < < < | | | | 10198 10199 10200 10201 10202 10203 10204 10205 10206 10207 10208 10209 10210 10211 10212 10213 10214 10215 10216 10217 10218 10219 10220 10221 10222 10223 10224 10225 10226 10227 10228 10229 10230 10231 10232 10233 10234 10235 10236 10237 10238 10239 10240 10241 10242 10243 | on the libfossil repo with 2003 checkins takes: 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)."); }else{ fsl_buffer_clear(tgt); } #else if(mx){/*unused var*/} fsl_buffer_clear(tgt); #endif *tgt = next; } end_delta: fsl_buffer_clear(&delta); fsl_free(a); gotIt = 0==rc; } if(!rc){ rc = fsl_id_bag_insert(gotIt ? &f->cache.blobContent.available : &f->cache.blobContent.missing, rid); } return rc; } } int fsl_content_get_sym( fsl_cx * const f, char const * sym, |
︙ | ︙ | |||
10164 10165 10166 10167 10168 10169 10170 | /** 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. */ | | | | | | < | | | 10253 10254 10255 10256 10257 10258 10259 10260 10261 10262 10263 10264 10265 10266 10267 10268 10269 10270 10271 10272 10273 10274 10275 10276 10277 10278 10279 10280 10281 10282 10283 10284 10285 10286 10287 10288 10289 10290 10291 10292 10293 10294 10295 10296 10297 10298 10299 10300 10301 10302 10303 10304 10305 10306 10307 10308 10309 10310 | /** 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){ 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; rc = fsl_id_bag_insert(&pending, rid); if(rc) goto end; while( 0==rc && (rid = fsl_id_bag_first(&pending))!=0 ){ fsl_id_bag_remove(&pending, rid); rc = fsl_id_bag_insert(&f->cache.blobContent.available, rid); if(rc) goto end; fsl_id_bag_remove(&f->cache.blobContent.missing, rid); if(!st){ rc = fsl_db_prepare_cached(db, &st, "SELECT rid FROM delta " "WHERE srcid=?" "/*%s()*/",__func__); if(rc) goto end; } rc = fsl_stmt_bind_id(st, 1, rid); 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; } /** 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. 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 that baseline. Tail recursion is used to minimize stack depth. Returns 0 on success, any number of non-0 results on error. The 3rd argument must always be false except in recursive calls to |
︙ | ︙ | |||
10238 10239 10240 10241 10242 10243 10244 | /* Parse the object rid itself */ 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); | | | 10326 10327 10328 10329 10330 10331 10332 10333 10334 10335 10336 10337 10338 10339 10340 | /* Parse the object rid itself */ 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); } fsl_deck_finalize(&deck); if(rc) break; } /* Parse all delta-manifests that depend on baseline-manifest rid */ rc = fsl_db_prepare(db, &q, "SELECT rid FROM orphan WHERE baseline=%"FSL_ID_T_PFMT, |
︙ | ︙ | |||
10264 10265 10266 10267 10268 10269 10270 | } fsl_stmt_finalize(&q); for(i=0; i<nChildUsed; ++i){ fsl_deck deck = fsl_deck_empty; rc = fsl_deck_load_rid(f, &deck, aChild[i], FSL_SATYPE_ANY); if(!rc){ assert(aChild[i]==deck.rid); | | | 10352 10353 10354 10355 10356 10357 10358 10359 10360 10361 10362 10363 10364 10365 10366 | } fsl_stmt_finalize(&q); for(i=0; i<nChildUsed; ++i){ fsl_deck deck = fsl_deck_empty; rc = fsl_deck_load_rid(f, &deck, aChild[i], FSL_SATYPE_ANY); if(!rc){ assert(aChild[i]==deck.rid); rc = fsl__deck_crosslink(&deck); } fsl_deck_finalize(&deck); if(rc) goto end; } if( nChildUsed ){ rc = fsl_db_exec_multi(db, "DELETE FROM orphan WHERE baseline=%"FSL_ID_T_PFMT, |
︙ | ︙ | |||
10318 10319 10320 10321 10322 10323 10324 | } end: fsl_stmt_finalize(&q); fsl_buffer_clear(&bufChild); return rc; } | | | 10406 10407 10408 10409 10410 10411 10412 10413 10414 10415 10416 10417 10418 10419 10420 | } end: fsl_stmt_finalize(&q); fsl_buffer_clear(&bufChild); return rc; } 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_size_t size; |
︙ | ︙ | |||
10499 10500 10501 10502 10503 10504 10505 | rc = fsl_stmt_bind_step(s1, "RIBR", f->cache.rcvId, (int64_t)size, &cmpr, rid); if(!rc){ rc = fsl_db_exec(dbR, "DELETE FROM phantom " "WHERE rid=%"FSL_ID_T_PFMT, rid /* FIXME? use cached statement? */); if( !rc && (srcId==0 || | | | 10587 10588 10589 10590 10591 10592 10593 10594 10595 10596 10597 10598 10599 10600 10601 | rc = fsl_stmt_bind_step(s1, "RIBR", f->cache.rcvId, (int64_t)size, &cmpr, rid); if(!rc){ rc = fsl_db_exec(dbR, "DELETE FROM phantom " "WHERE rid=%"FSL_ID_T_PFMT, rid /* FIXME? use cached statement? */); if( !rc && (srcId==0 || 0==fsl__bccache_check_available(f, srcId)) ){ isDephantomize = true; rc = fsl_content_mark_available(f, rid); } } fsl_stmt_cached_yield(s1); s1 = NULL; if(rc) goto end; |
︙ | ︙ | |||
10559 10560 10561 10562 10563 10564 10565 | if(rc) rc = fsl_cx_uplift_db_error2(f, dbR, rc); fsl_stmt_cached_yield(s1); s1 = NULL; } if(rc) goto end; } if( !isDephantomize | | | | 10647 10648 10649 10650 10651 10652 10653 10654 10655 10656 10657 10658 10659 10660 10661 10662 | if(rc) rc = fsl_cx_uplift_db_error2(f, dbR, rc); fsl_stmt_cached_yield(s1); s1 = NULL; } if(rc) goto end; } if( !isDephantomize && fsl_id_bag_contains(&f->cache.blobContent.missing, rid) && (srcId==0 || (0==fsl__bccache_check_available(f,srcId)))){ /* TODO: document what this is for. TODO: figure out what that is. */ rc = fsl_content_mark_available(f, rid); if(rc) goto end; } |
︙ | ︙ | |||
10591 10592 10593 10594 10595 10596 10597 | if( markAsUnsent ){ /* FIXME: use a cached statement. */ rc = fsl_db_exec(dbR, "INSERT OR IGNORE INTO unsent " "VALUES(%"FSL_ID_T_PFMT")", rid); if(rc) goto end; } | | | | | | 10679 10680 10681 10682 10683 10684 10685 10686 10687 10688 10689 10690 10691 10692 10693 10694 10695 10696 10697 10698 10699 10700 10701 10702 10703 10704 10705 10706 10707 10708 10709 10710 10711 10712 10713 10714 10715 10716 10717 10718 10719 10720 10721 10722 10723 10724 10725 10726 10727 10728 10729 10730 10731 | if( markAsUnsent ){ /* FIXME: use a cached statement. */ 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); 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; if(!rc){ if(outRid) *outRid = rid; } end: if(inTrans){ assert(0!=rc); fsl_db_transaction_end(dbR,true); } fsl_buffer_clear(&hash); if(!uncompSize){ 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_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; else if(FSL_HPOLICY_SHUN_SHA1==f->cxConfig.hashPolicy && FSL_STRLEN_SHA1==fsl_is_uuid(zUuid)){ return 1; } /* TODO? cached query */ 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, 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; int const uuidLen = uuid ? fsl_is_uuid(uuid) : 0; if(!f || !uuid) return FSL_RC_MISUSE; |
︙ | ︙ | |||
10688 10689 10690 10691 10692 10693 10694 | if(!rc) rc = fsl_stmt_step(s3); fsl_stmt_cached_yield(s3); if(FSL_RC_STEP_DONE!=rc) goto end; else rc = 0; } } | | | | 10776 10777 10778 10779 10780 10781 10782 10783 10784 10785 10786 10787 10788 10789 10790 10791 10792 10793 10794 10795 10796 10797 10798 10799 10800 10801 10802 10803 10804 10805 10806 10807 10808 10809 | if(!rc) rc = fsl_stmt_step(s3); fsl_stmt_cached_yield(s3); if(FSL_RC_STEP_DONE!=rc) goto end; else rc = 0; } } if(!rc) rc = fsl_id_bag_insert(&f->cache.blobContent.missing, rid); end: if(rc){ if(db->error.code && !f->error.code){ fsl_cx_uplift_db_error(f, db); } fsl_db_transaction_rollback(db); } else{ rc = fsl_db_transaction_commit(db); if(!rc && newId) *newId = rid; else if(rc && !f->error.code){ fsl_cx_uplift_db_error(f, db); } } return rc; } 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; if(!f) return FSL_RC_MISUSE; else if(!db) return FSL_RC_NOT_A_REPO; |
︙ | ︙ | |||
10763 10764 10765 10766 10767 10768 10769 | of losing content. Hence, I didn't see the need to verify the content of artifacts that are undelta-ed." Potential TODO: f->flags FSL_CX_F_PEDANTIC_VERIFICATION, which enables the R-card and this check, and any similarly superfluous ones. */ | | | | 10851 10852 10853 10854 10855 10856 10857 10858 10859 10860 10861 10862 10863 10864 10865 10866 10867 10868 10869 10870 10871 10872 10873 10874 10875 10876 10877 10878 10879 | of losing content. Hence, I didn't see the need to verify the content of artifacts that are undelta-ed." 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); #endif end: fsl_buffer_clear(&x); fsl_stmt_finalize(&s); if(rc) fsl_db_transaction_rollback(db); else rc = fsl_db_transaction_commit(db); return rc; dberr: assert(rc); rc = fsl_cx_uplift_db_error2(f, db, rc); goto end; } 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; fsl_db * db = f ? fsl_cx_db_repo(f) : NULL; int rc = 0; |
︙ | ︙ | |||
10825 10826 10827 10828 10829 10830 10831 | /** Undeltify srcid if needed... */ s = srcid; while( (0==(rc=fsl_delta_src_id(f, s, &s))) && (s>0) ){ if( s==rid ){ | | | 10913 10914 10915 10916 10917 10918 10919 10920 10921 10922 10923 10924 10925 10926 10927 | /** Undeltify srcid if needed... */ s = srcid; while( (0==(rc=fsl_delta_src_id(f, s, &s))) && (s>0) ){ if( s==rid ){ 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 really careful, b/c buffers might need cleaning. */ rc = fsl_content_get(f, srcid, &src); |
︙ | ︙ | |||
10873 10874 10875 10876 10877 10878 10879 | else fsl_db_transaction_end(db, 1) /* keep rc intact */; } } } } fsl_stmt_cached_yield(s1); fsl_stmt_cached_yield(s2); | | | | 10961 10962 10963 10964 10965 10966 10967 10968 10969 10970 10971 10972 10973 10974 10975 10976 10977 10978 10979 10980 10981 10982 10983 10984 10985 10986 10987 10988 10989 10990 10991 | else fsl_db_transaction_end(db, 1) /* keep rc intact */; } } } } fsl_stmt_cached_yield(s1); fsl_stmt_cached_yield(s2); 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); } fsl_buffer_clear(&src); fsl_buffer_clear(&data); fsl_buffer_clear(&delta); return rc; } /** 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){ 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; rc = fsl_db_transaction_begin(db); if(rc) return rc; |
︙ | ︙ | |||
10913 10914 10915 10916 10917 10918 10919 | */ rc = fsl_db_prepare(db, &q, "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); | | | 11001 11002 11003 11004 11005 11006 11007 11008 11009 11010 11011 11012 11013 11014 11015 | */ rc = fsl_db_prepare(db, &q, "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); } fsl_stmt_finalize(&q); if(!rc){ rc = fsl_db_exec_multi(db, "DELETE FROM delta WHERE rid IN toshun;" "DELETE FROM blob WHERE rid IN toshun;" "DROP TABLE toshun;" |
︙ | ︙ | |||
11134 11135 11136 11137 11138 11139 11140 | fsl_id_t fnid = 0; fsl_id_t rcRid = 0; 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; | | | | | | | | | | 11222 11223 11224 11225 11226 11227 11228 11229 11230 11231 11232 11233 11234 11235 11236 11237 11238 11239 11240 11241 11242 11243 11244 11245 11246 11247 11248 11249 11250 11251 11252 11253 11254 11255 11256 11257 11258 11259 11260 11261 11262 11263 11264 11265 11266 11267 11268 11269 11270 11271 11272 11273 11274 11275 11276 11277 11278 11279 11280 11281 11282 11283 11284 11285 11286 11287 11288 11289 11290 11291 11292 11293 11294 11295 11296 11297 | fsl_id_t fnid = 0; fsl_id_t rcRid = 0; 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); assert(!fbuf->used && "Misuse of f->fileContent"); assert(f->ckout.dir); /* Normalize the name... i often regret having fsl_ckout_filename_check() return checkout-relative paths. */ rc = fsl_ckout_filename_check(f, relativeToCwd, zName, canon); if(rc) goto end; /* Find or create a repo.filename entry... */ fn = fsl_buffer_cstr(canon); rc = fsl_db_transaction_begin(db); if(rc) goto end; inTrans = 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); if(rc) goto end; fn = fsl_buffer_cstr(nbuf); rc = fsl_buffer_fill_from_filename( fbuf, fn ); if(rc){ 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 ); 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); } if(!rc){ if(rid) *rid = rcRid; if(uuid){ fsl_cx_err_reset(f); *uuid = fsl_rid_to_uuid(f, rcRid); if(!*uuid) rc = (f->error.code ? f->error.code : FSL_RC_OOM); } } } if(!rc){ assert(inTrans); inTrans = 0; rc = fsl_db_transaction_commit(db); } end: fsl__cx_content_buffer_yield(f); assert(0==fbuf->used); 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){ /* fossil(1) counterpart: hname_validate() */ fsl_hash_types_e rc; |
︙ | ︙ | |||
11266 11267 11268 11269 11270 11271 11272 | "INSERT INTO toshun SELECT rid FROM blob, shun WHERE blob.uuid=shun.uuid;" ); 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) ){ | | | 11354 11355 11356 11357 11358 11359 11360 11361 11362 11363 11364 11365 11366 11367 11368 | "INSERT INTO toshun SELECT rid FROM blob, shun WHERE blob.uuid=shun.uuid;" ); 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)); } fsl_stmt_finalize(&q); if(rc) goto end; rc = fsl_cx_exec_multi(f, "DELETE FROM delta WHERE rid IN toshun;" "DELETE FROM blob WHERE rid IN toshun;" "DROP TABLE toshun;" |
︙ | ︙ | |||
11318 11319 11320 11321 11322 11323 11324 11325 11326 11327 11328 11329 11330 11331 11332 11333 11334 11335 11336 11337 | they use the same underlying cache fsl_stmt handle). The first %s represents one of the config table names (config, vvfar, global_config). Normally we wouldn't use %s in a cached statement, 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*/" 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"; case FSL_CONFDB_VERSIONABLE: return NULL; default: assert(!"Invalid fsl_confdb_e value"); return NULL; } } | > > > > > > > > > | | 11406 11407 11408 11409 11410 11411 11412 11413 11414 11415 11416 11417 11418 11419 11420 11421 11422 11423 11424 11425 11426 11427 11428 11429 11430 11431 11432 11433 11434 11435 11436 11437 11438 11439 11440 11441 11442 | they use the same underlying cache fsl_stmt handle). The first %s represents one of the config table names (config, vvfar, global_config). Normally we wouldn't use %s in a cached statement, 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"; case FSL_CONFDB_VERSIONABLE: return NULL; default: assert(!"Invalid fsl_confdb_e value"); return NULL; } } fsl_db * fsl_config_for_role(fsl_cx * const 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); default: assert(!"Invalid fsl_confdb_e value"); |
︙ | ︙ | |||
11355 11356 11357 11358 11359 11360 11361 | } fsl_buffer_reuse(b); return fsl_buffer_appendf(b, "%s.fossil-settings/%s", f->ckout.dir, key); } | | | | > | > | | > | | | > | | > | | 11452 11453 11454 11455 11456 11457 11458 11459 11460 11461 11462 11463 11464 11465 11466 11467 11468 11469 11470 11471 11472 11473 11474 11475 11476 11477 11478 11479 11480 11481 11482 11483 11484 11485 11486 11487 11488 11489 11490 11491 11492 11493 11494 11495 11496 11497 11498 11499 11500 11501 11502 11503 11504 11505 11506 11507 11508 11509 11510 11511 11512 11513 11514 11515 11516 11517 11518 11519 11520 11521 11522 11523 11524 11525 11526 11527 11528 11529 11530 11531 11532 11533 11534 11535 11536 11537 11538 11539 11540 11541 11542 11543 11544 11545 11546 11547 11548 11549 11550 11551 11552 11553 11554 11555 11556 11557 11558 11559 11560 11561 11562 11563 11564 11565 11566 11567 11568 11569 11570 11571 11572 11573 11574 11575 11576 11577 11578 11579 11580 11581 11582 11583 11584 11585 11586 11587 11588 11589 11590 11591 11592 11593 11594 11595 11596 11597 11598 11599 11600 11601 11602 11603 11604 11605 11606 11607 11608 11609 11610 11611 11612 11613 11614 11615 11616 11617 11618 11619 11620 11621 11622 11623 11624 11625 11626 11627 11628 11629 11630 11631 11632 11633 11634 11635 11636 11637 11638 11639 11640 11641 11642 11643 11644 11645 11646 11647 11648 11649 11650 11651 11652 11653 11654 11655 11656 | } fsl_buffer_reuse(b); 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 ){ 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); 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 dflt, char const * key ){ int32_t rv = dflt; switch(mode){ case FSL_CONFDB_VERSIONABLE:{ char * val = fsl_config_get_text(f, mode, key, NULL); if(val){ rv = (int32_t)atoi(val); fsl_free(val); } break; } default: { 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){ 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); } } break; } } return rv; } int64_t fsl_config_get_int64( fsl_cx * const 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); if(val){ rv = (int64_t)strtoll(val, NULL, 10); fsl_free(val); } break; } default: { 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_int64(st, 0); } fsl_stmt_cached_yield(st); } } break; } } return rv; } fsl_id_t fsl_config_get_id( fsl_cx * const 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 dflt, char const * key ){ double rv = dflt; switch(mode){ case FSL_CONFDB_VERSIONABLE:{ char * val = fsl_config_get_text(f, mode, key, NULL); if(val){ rv = strtod(val, NULL); fsl_free(val); } break; } default: { fsl_db * db = fsl_config_for_role(f, mode); if(!db) break/*e.g. global config is not opened*/; fsl_stmt * st = NULL; 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); } break; } } return rv; } char * fsl_config_get_text( fsl_cx * const 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); 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_config_get_buffer( fsl_cx * const f, fsl_confdb_e mode, char const * key, fsl_buffer * const 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); 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){ rc = fsl_cx_err_set(f, rc, "Could not stat file: %s", zFile); }else{ rc = fsl_buffer_fill_from_filename(b, zFile); } } fsl__cx_scratchpad_yield(f,fname); break; } default: { char const * table = fsl_config_table_for_role(mode); assert(table); fsl_db * const db = fsl_config_for_role(f, mode); if(!db) break; fsl_stmt * st = NULL; rc = fsl_db_prepare_cached(db, &st, SELECT_FROM_CONFIG, 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; }else{ rc = FSL_RC_NOT_FOUND; } fsl_stmt_cached_yield(st); break; } } return rc; } bool fsl_config_get_bool( fsl_cx * const 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); if(val){ rv = fsl_str_bool(val); |
︙ | ︙ | |||
11564 11565 11566 11567 11568 11569 11570 11571 11572 11573 11574 11575 11576 11577 | if(!f || !key || !*key) break; db = fsl_config_for_role(f, mode); if(!db) break; assert(table); rc = fsl_db_prepare_cached(db, &st, SELECT_FROM_CONFIG, table, __FILE__); if(!rc){ 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? */; } fsl_stmt_cached_yield(st); } | > | 11666 11667 11668 11669 11670 11671 11672 11673 11674 11675 11676 11677 11678 11679 11680 | if(!f || !key || !*key) break; db = fsl_config_for_role(f, mode); 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? */; } fsl_stmt_cached_yield(st); } |
︙ | ︙ | |||
11585 11586 11587 11588 11589 11590 11591 | Sets up a REPLACE statement for the given config db and key. On success 0 is returned and *st holds the cached statement. The caller must bind() parameter #2 and step() the statement, then fsl_stmt_cached_yield() it. Returns non-0 on error. */ | | | | > > | > | | | | | 11688 11689 11690 11691 11692 11693 11694 11695 11696 11697 11698 11699 11700 11701 11702 11703 11704 11705 11706 11707 11708 11709 11710 11711 11712 11713 11714 11715 11716 11717 11718 11719 11720 11721 11722 11723 11724 11725 11726 11727 11728 11729 11730 11731 11732 11733 11734 11735 11736 11737 11738 11739 11740 11741 11742 11743 11744 11745 11746 11747 11748 11749 11750 11751 11752 11753 11754 11755 11756 | Sets up a REPLACE statement for the given config db and key. On success 0 is returned and *st holds the cached statement. The caller 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, 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()*/"; 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 && !f->error.code){ fsl_cx_uplift_db_error(f, db); } return rc; } } /* TODO/FIXME: the fsl_config_set_xxx() routines all use the same basic structure, differing only in the concrete bind() op they call. They should be consolidated somehow. */ /** 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, 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); 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); return rc; } int fsl_config_set_text( fsl_cx * const 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, val ? fsl_strlen(val) : 0); } |
︙ | ︙ | |||
11666 11667 11668 11669 11670 11671 11672 | fsl_stmt_cached_yield(st); if(FSL_RC_STEP_DONE==rc) rc = 0; } if(rc && !f->error.code) fsl_cx_uplift_db_error(f, db); return rc; } | | | 11772 11773 11774 11775 11776 11777 11778 11779 11780 11781 11782 11783 11784 11785 11786 | fsl_stmt_cached_yield(st); if(FSL_RC_STEP_DONE==rc) rc = 0; } 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, 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, (val && len<0) ? fsl_strlen((char const *)val) |
︙ | ︙ | |||
11697 11698 11699 11700 11701 11702 11703 | fsl_stmt_cached_yield(st); if(FSL_RC_STEP_DONE==rc) rc = 0; } if(rc && !f->error.code) fsl_cx_uplift_db_error(f, db); return rc; } | | | 11803 11804 11805 11806 11807 11808 11809 11810 11811 11812 11813 11814 11815 11816 11817 | fsl_stmt_cached_yield(st); if(FSL_RC_STEP_DONE==rc) rc = 0; } 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, 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}; fsl_snprintf(buf, sizeof(buf), "%" PRIi32 "\n", val); return fsl_config_set_versionable(f, key, buf, |
︙ | ︙ | |||
11723 11724 11725 11726 11727 11728 11729 | fsl_stmt_cached_yield(st); if(FSL_RC_STEP_DONE==rc) rc = 0; } if(rc && !f->error.code) fsl_cx_uplift_db_error(f, db); return rc; } | | | 11829 11830 11831 11832 11833 11834 11835 11836 11837 11838 11839 11840 11841 11842 11843 | fsl_stmt_cached_yield(st); if(FSL_RC_STEP_DONE==rc) rc = 0; } 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, 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}; fsl_snprintf(buf, sizeof(buf), "%" PRIi64 "\n", val); return fsl_config_set_versionable(f, key, buf, |
︙ | ︙ | |||
11749 11750 11751 11752 11753 11754 11755 | fsl_stmt_cached_yield(st); if(FSL_RC_STEP_DONE==rc) rc = 0; } if(rc && !f->error.code) fsl_cx_uplift_db_error(f, db); return rc; } | | | 11855 11856 11857 11858 11859 11860 11861 11862 11863 11864 11865 11866 11867 11868 11869 | fsl_stmt_cached_yield(st); if(FSL_RC_STEP_DONE==rc) rc = 0; } 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, 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}; fsl_snprintf(buf, sizeof(buf), "%" FSL_ID_T_PFMT "\n", val); return fsl_config_set_versionable(f, key, buf, |
︙ | ︙ | |||
11775 11776 11777 11778 11779 11780 11781 | fsl_stmt_cached_yield(st); if(FSL_RC_STEP_DONE==rc) rc = 0; } if(rc && !f->error.code) fsl_cx_uplift_db_error(f, db); return rc; } | | | 11881 11882 11883 11884 11885 11886 11887 11888 11889 11890 11891 11892 11893 11894 11895 | fsl_stmt_cached_yield(st); if(FSL_RC_STEP_DONE==rc) rc = 0; } 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, 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}; fsl_snprintf(buf, sizeof(buf), "%f\n", val); return fsl_config_set_versionable(f, key, buf, |
︙ | ︙ | |||
11801 11802 11803 11804 11805 11806 11807 | fsl_stmt_cached_yield(st); if(FSL_RC_STEP_DONE==rc) rc = 0; } if(rc && !f->error.code) fsl_cx_uplift_db_error(f, db); return rc; } | | | 11907 11908 11909 11910 11911 11912 11913 11914 11915 11916 11917 11918 11919 11920 11921 | fsl_stmt_cached_yield(st); if(FSL_RC_STEP_DONE==rc) rc = 0; } 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, 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){ buf[1] = buf[2] = 'f'; } |
︙ | ︙ | |||
11829 11830 11831 11832 11833 11834 11835 | fsl_stmt_cached_yield(st); if(FSL_RC_STEP_DONE==rc) rc = 0; } if(rc && !f->error.code) fsl_cx_uplift_db_error(f, db); return rc; } | | | | | 11935 11936 11937 11938 11939 11940 11941 11942 11943 11944 11945 11946 11947 11948 11949 11950 11951 11952 11953 11954 11955 11956 11957 11958 11959 11960 11961 11962 11963 11964 11965 11966 11967 11968 11969 | fsl_stmt_cached_yield(st); if(FSL_RC_STEP_DONE==rc) rc = 0; } 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){ 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){ 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 rc = 0; char * val = NULL; if(!f || !li || !key || !*key) return FSL_RC_MISUSE; else if(fsl_cx_db_ckout(f)){ /* Try versionable settings... */ fsl_buffer buf = fsl_buffer_empty; rc = fsl_config_get_buffer(f, FSL_CONFDB_VERSIONABLE, key, &buf); |
︙ | ︙ | |||
12002 12003 12004 12005 12006 12007 12008 | #define ARRAYLEN(X) (sizeof(X)/sizeof(X[0])) /* 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. */ | | | 12108 12109 12110 12111 12112 12113 12114 12115 12116 12117 12118 12119 12120 12121 12122 | #define ARRAYLEN(X) (sizeof(X)/sizeof(X[0])) /* 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){ fsl_buffer x = fsl_buffer_empty; const char *zSep = ""; const int n = (int)ARRAYLEN(fslConfigXfer); int i; int rc = fsl_buffer_append(&x, "(", 1); for(i=0; !rc && (i<n); i++){ if( (fslConfigXfer[i].groupMask & iMask)==0 ) continue; |
︙ | ︙ | |||
12104 12105 12106 12107 12108 12109 12110 | /** Returns true if f's current checkout contains the given versionable configuration setting, else false. @see fsl_config_ctrl */ | | | 12210 12211 12212 12213 12214 12215 12216 12217 12218 12219 12220 12221 12222 12223 12224 | /** 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 ); 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. FIXME: bring this up to date wrt post-2014 fossil. Or abandon it |
︙ | ︙ | |||
12225 12226 12227 12228 12229 12230 12231 | } 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; } | | | | > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 12331 12332 12333 12334 12335 12336 12337 12338 12339 12340 12341 12342 12343 12344 12345 12346 12347 12348 12349 12350 12351 12352 12353 12354 12355 12356 12357 12358 12359 12360 12361 12362 12363 12364 12365 12366 12367 12368 12369 12370 12371 12372 12373 12374 12375 12376 12377 12378 12379 12380 12381 12382 12383 12384 12385 12386 12387 12388 12389 12390 12391 12392 12393 12394 12395 12396 12397 12398 12399 12400 12401 12402 12403 12404 12405 12406 12407 12408 12409 12410 12411 12412 12413 12414 12415 12416 12417 12418 12419 12420 12421 12422 12423 12424 12425 12426 12427 12428 12429 12430 12431 12432 12433 12434 12435 12436 12437 12438 12439 12440 12441 12442 12443 12444 12445 12446 12447 12448 12449 12450 12451 12452 12453 12454 12455 12456 12457 12458 12459 12460 12461 12462 12463 12464 12465 12466 12467 12468 12469 12470 12471 12472 12473 12474 12475 12476 12477 12478 12479 12480 12481 12482 12483 12484 12485 12486 12487 12488 12489 12490 12491 12492 12493 12494 12495 12496 12497 12498 12499 12500 12501 12502 12503 12504 12505 12506 12507 12508 12509 12510 12511 12512 12513 12514 12515 12516 12517 12518 12519 12520 12521 12522 12523 12524 12525 12526 12527 12528 12529 12530 | } 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 ){ 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); 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); 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 */ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ |
︙ | ︙ | |||
12320 12321 12322 12323 12324 12325 12326 | This function does not trigger any finializers set for f's client state or output channel. Results are undefined if !f or f's memory has not been properly initialized. */ | | | | 12599 12600 12601 12602 12603 12604 12605 12606 12607 12608 12609 12610 12611 12612 12613 12614 12615 12616 12617 12618 12619 12620 12621 12622 12623 12624 12625 12626 12627 12628 12629 12630 12631 | This function does not trigger any finializers set for f's client 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 ); 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; fsl_cx * f; extern int fsl_cx_install_timeline_crosslinkers(fsl_cx * const f) /*in deck.c*/; if(!tgt) return FSL_RC_MISUSE; else if(!param){ if(!paramDefaults.output.state){ paramDefaults.output.state = stdout; } param = ¶mDefaults; } if(*tgt){ void const * allocStamp = (*tgt)->allocStamp; fsl__cx_reset(*tgt, true) /* just to be safe */; f = *tgt; *f = fsl_cx_empty; f->allocStamp = allocStamp; }else{ f = fsl_cx_malloc(); if(!f) return FSL_RC_OOM; |
︙ | ︙ | |||
12411 12412 12413 12414 12415 12416 12417 | }else{ f->dbMain = &f->dbMem; f->dbMem.role = FSL_DBROLE_MAIN; } return rc; } | | | | | | > > > > < < < < < < > | | > | > < | | > | | | 12690 12691 12692 12693 12694 12695 12696 12697 12698 12699 12700 12701 12702 12703 12704 12705 12706 12707 12708 12709 12710 12711 12712 12713 12714 12715 12716 12717 12718 12719 12720 12721 12722 12723 12724 12725 12726 12727 12728 12729 12730 12731 12732 12733 12734 12735 12736 12737 12738 12739 12740 12741 12742 12743 12744 12745 12746 12747 12748 12749 12750 12751 12752 12753 12754 12755 12756 12757 12758 12759 12760 12761 12762 12763 12764 12765 12766 12767 12768 12769 12770 12771 12772 12773 12774 12775 12776 12777 12778 12779 12780 | }else{ f->dbMain = &f->dbMem; f->dbMem.role = FSL_DBROLE_MAIN; } return rc; } static void fsl__cx_mcache_clear(fsl_cx * const f){ const unsigned cacheLen = (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; } 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); */ } 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_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_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); if(f->xlinkers.list){ fsl_free(f->xlinkers.list); f->xlinkers = fsl_xlinker_list_empty; } #define SLIST(L) fsl_list_visit_free(L, 1) #define GLOBL(X) SLIST(&f->cache.globs.X) GLOBL(ignore); GLOBL(binary); GLOBL(crnl); #undef GLOBL #undef SLIST 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_finalize( fsl_cx * const 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_db_close(&f->dbMem); *f = fsl_cx_empty; if(&fsl_cx_empty == allocStamp){ fsl_free(f); }else{ f->allocStamp = allocStamp; } |
︙ | ︙ | |||
12511 12512 12513 12514 12515 12516 12517 | assert(sg_autoregctr>=0); #endif #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); #endif } | | | | | | | | | | < | | | | > | > > > > > | < > > | 12791 12792 12793 12794 12795 12796 12797 12798 12799 12800 12801 12802 12803 12804 12805 12806 12807 12808 12809 12810 12811 12812 12813 12814 12815 12816 12817 12818 12819 12820 12821 12822 12823 12824 12825 12826 12827 12828 12829 12830 12831 12832 12833 12834 12835 12836 12837 12838 12839 12840 12841 12842 12843 12844 12845 12846 12847 12848 12849 12850 12851 12852 12853 12854 12855 | assert(sg_autoregctr>=0); #endif #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); } int fsl_cx_err_set_e( fsl_cx * const f, fsl_error * const 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); fsl_error_clear(err); return f->error.code; } } int fsl_cx_err_setv( fsl_cx * const 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, ... ){ if(!f) return FSL_RC_MISUSE; else{ int rc; va_list args; va_start(args,fmt); rc = fsl_error_setv( &f->error, code, fmt, args ); 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 } 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) : -1; } |
︙ | ︙ | |||
12589 12590 12591 12592 12593 12594 12595 | addNewline ? "\n" : ""); } else return 0; } int fsl_cx_uplift_db_error( fsl_cx * const f, fsl_db * db ){ assert(f); | < > > | | < | < > < > | 12875 12876 12877 12878 12879 12880 12881 12882 12883 12884 12885 12886 12887 12888 12889 12890 12891 12892 12893 12894 12895 12896 12897 12898 12899 12900 12901 12902 12903 12904 12905 12906 12907 12908 12909 12910 12911 12912 12913 12914 12915 12916 12917 12918 | addNewline ? "\n" : ""); } else return 0; } int fsl_cx_uplift_db_error( fsl_cx * const f, fsl_db * db ){ assert(f); if(!db){ db = f->dbMain; assert(db && "misuse: no DB handle to uplift error from!"); if(!db) return FSL_RC_MISUSE; } 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); } 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 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 return NULL; } fsl_db * fsl_needs_repo(fsl_cx * const f){ fsl_db * const db = fsl_cx_db_repo(f); if(!db){ fsl_cx_err_set(f, FSL_RC_NOT_A_REPO, |
︙ | ︙ | |||
12642 12643 12644 12645 12646 12647 12648 | "Fossil context has no opened checkout db."); } return db; } fsl_db * fsl_cx_db_ckout( fsl_cx * const f ){ if(!f) return NULL; | < > | 12928 12929 12930 12931 12932 12933 12934 12935 12936 12937 12938 12939 12940 12941 12942 12943 | "Fossil context has no opened checkout db."); } 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 return NULL; } fsl_db * fsl_cx_db( fsl_cx * const f ){ return f ? f->dbMain : NULL; } /** @internal |
︙ | ︙ | |||
12684 12685 12686 12687 12688 12689 12690 | } } /** Detaches the given db role from f->dbMain and removes the role from f->dbMain->role. */ | | | | | | > > > > > > > > | | | > | > > | < < > > | | | 12970 12971 12972 12973 12974 12975 12976 12977 12978 12979 12980 12981 12982 12983 12984 12985 12986 12987 12988 12989 12990 12991 12992 12993 12994 12995 12996 12997 12998 12999 13000 13001 13002 13003 13004 13005 13006 13007 13008 13009 13010 13011 13012 13013 13014 13015 13016 13017 13018 13019 13020 13021 13022 13023 13024 13025 13026 13027 13028 13029 13030 13031 | } } /** 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; 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); int rc; assert(db && "Internal API misuse."); 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); } } return rc; } } /** @internal Attaches the given db file to f with the given role. This function "should" 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); char ** nameDest = NULL; int rc; if(!f->dbMain){ 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."); return fsl_cx_err_set(f, FSL_RC_MISUSE, "Db role %s is already attached.", |
︙ | ︙ | |||
12782 12783 12784 12785 12786 12787 12788 | f->dbMain->role |= r; } } return rc; } int fsl_config_close( fsl_cx * const f ){ | < < | | | | | > > | < < < | < | | > > < < | | | < | | | < | | < > < | 13079 13080 13081 13082 13083 13084 13085 13086 13087 13088 13089 13090 13091 13092 13093 13094 13095 13096 13097 13098 13099 13100 13101 13102 13103 13104 13105 13106 13107 13108 13109 13110 13111 13112 13113 13114 13115 13116 13117 13118 13119 13120 13121 13122 13123 13124 13125 13126 13127 13128 13129 13130 13131 13132 13133 13134 13135 13136 13137 13138 13139 13140 13141 13142 13143 13144 13145 13146 13147 13148 13149 13150 13151 13152 | f->dbMain->role |= r; } } 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; } 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."); }else{ int rc = 0; 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, "Cannot close repo while checkout is " "opened."); }else{ assert(f->dbMain!=db); rc = fsl_cx_detach_role(f, FSL_DBROLE_REPO); } }else{ fsl_db_close(db); } assert(!db->dbh); 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; 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. */; }else{ fsl_db_close(db); } fsl_free(f->ckout.uuid); f->ckout.uuid = NULL; f->ckout.rid = 0; assert(!db->dbh); return rc; } } /** If zDbName is a valid checkout database file, open it and return 0. If it is not a valid local database file, return a non-0 code. |
︙ | ︙ | |||
12924 12925 12926 12927 12928 12929 12930 12931 12932 12933 12934 12935 12936 12937 12938 12939 | int rc; va_list args; va_start(args,sql); rc = fsl_cx_preparev( 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. */ | > > > > > > > > > > > > > > > > > > > | | 13214 13215 13216 13217 13218 13219 13220 13221 13222 13223 13224 13225 13226 13227 13228 13229 13230 13231 13232 13233 13234 13235 13236 13237 13238 13239 13240 13241 13242 13243 13244 13245 13246 13247 13248 13249 13250 13251 13252 13253 13254 13255 13256 | int rc; va_list args; 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){ 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); if(-1 != fsl_file_size(dbName)){ rc = fsl_file_unlink(dbName); |
︙ | ︙ | |||
13033 13034 13035 13036 13037 13038 13039 | return rc; } int fsl_config_open( fsl_cx * const f, char const * openDbName ){ int rc = 0; const char * zDbName = 0; char * zPrefName = 0; | < | > | | 13342 13343 13344 13345 13346 13347 13348 13349 13350 13351 13352 13353 13354 13355 13356 13357 13358 | return rc; } 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(openDbName && *openDbName){ zDbName = openDbName; }else{ rc = fsl_config_global_preferred_name(&zPrefName); if(rc) goto end; zDbName = zPrefName; |
︙ | ︙ | |||
13063 13064 13065 13066 13067 13068 13069 13070 13071 13072 13073 13074 13075 13076 | if( fsl_file_access(zDbName, W_OK) ){ rc = fsl_cx_err_set(f, FSL_RC_ACCESS, "Configuration database [%s] " "must be writeable.", zDbName); goto end; } #endif rc = fsl_cx_attach_role(f, zDbName, FSL_DBROLE_CONFIG); end: fsl_free(zPrefName); return rc; } static void fsl_cx_username_from_repo(fsl_cx * f){ | > | 13372 13373 13374 13375 13376 13377 13378 13379 13380 13381 13382 13383 13384 13385 13386 | if( fsl_file_access(zDbName, W_OK) ){ rc = fsl_cx_err_set(f, FSL_RC_ACCESS, "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; } static void fsl_cx_username_from_repo(fsl_cx * f){ |
︙ | ︙ | |||
13136 13137 13138 13139 13140 13141 13142 | /** 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){ | | | 13446 13447 13448 13449 13450 13451 13452 13453 13454 13455 13456 13457 13458 13459 13460 | /** 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); if(!rc) rc = fsl_cx_load_glob_lists(f); return rc; } static void fsl_cx_fetch_hash_policy(fsl_cx * f){ int const iPol = |
︙ | ︙ | |||
13252 13253 13254 13255 13256 13257 13258 | ? fsl_db_g_double(fsl_cx_db_repo(f), 0.0, "SELECT mtime FROM event " "WHERE objid=%" FSL_ID_T_PFMT, f->ckout.rid) : 0.0; } | | | 13562 13563 13564 13565 13566 13567 13568 13569 13570 13571 13572 13573 13574 13575 13576 | ? fsl_db_g_double(fsl_cx_db_repo(f), 0.0, "SELECT mtime FROM event " "WHERE objid=%" FSL_ID_T_PFMT, f->ckout.rid) : 0.0; } int fsl__ckout_version_fetch( fsl_cx * const 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)); fsl_free(f->ckout.uuid); f->ckout.rid = -1; |
︙ | ︙ | |||
13307 13308 13309 13310 13311 13312 13313 | NULL and rid is not 0 then the uuid is fetched using fsl_rid_to_uuid(), else if uuid is not NULL then it is assumed to 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. | | | | | | 13617 13618 13619 13620 13621 13622 13623 13624 13625 13626 13627 13628 13629 13630 13631 13632 13633 13634 13635 13636 13637 13638 13639 13640 13641 13642 13643 13644 13645 13646 13647 13648 13649 13650 13651 13652 13653 13654 13655 13656 13657 13658 13659 13660 13661 13662 13663 13664 13665 13666 13667 13668 13669 13670 13671 | NULL and rid is not 0 then the uuid is fetched using fsl_rid_to_uuid(), else if uuid is not NULL then it is assumed to 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() 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){ char * u = 0; assert(rid>=0); u = uuid ? fsl_strdup(uuid) : (rid ? fsl_rid_to_uuid(f, rid) : NULL); if(rid && !u) return FSL_RC_OOM; f->ckout.rid = rid; fsl_free(f->ckout.uuid); f->ckout.uuid = u; fsl_ckout_mtime_set(f); return 0; } int fsl__ckout_version_write( fsl_cx * const 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()"); } if(f->ckout.rid!=vid){ rc = fsl_cx_ckout_version_set(f, vid, hash); } if(!rc){ rc = fsl_config_set_id(f, FSL_CONFDB_CKOUT, "checkout", f->ckout.rid); if(!rc){ rc = fsl_config_set_text(f, FSL_CONFDB_CKOUT, "checkout-hash", f->ckout.uuid); } } if(!rc){ char * zFingerprint = 0; rc = fsl__repo_fingerprint_search(f, 0, &zFingerprint); if(!rc){ rc = fsl_config_set_text(f, FSL_CONFDB_CKOUT, "fingerprint", zFingerprint); fsl_free(zFingerprint); } } if(!rc){ |
︙ | ︙ | |||
13598 13599 13600 13601 13602 13603 13604 | if(f && f->ckout.dir){ rc = f->ckout.dir; if(len) *len = f->ckout.dirLen; } return rc; } | | | | 13908 13909 13910 13911 13912 13913 13914 13915 13916 13917 13918 13919 13920 13921 13922 13923 13924 13925 13926 | if(f && f->ckout.dir){ rc = f->ckout.dir; if(len) *len = f->ckout.dirLen; } return rc; } int fsl_cx_flags_get( fsl_cx const * const f ){ return f->flags; } int fsl_cx_flag_set( fsl_cx * const f, int flags, bool enable ){ int const oldFlags = f->flags; if(enable) f->flags |= flags; else f->flags &= ~flags; return oldFlags; } |
︙ | ︙ | |||
13649 13650 13651 13652 13653 13654 13655 | *x = fsl_xlinker_empty; x->f = cb; x->state = cbState; x->name = name; return 0; } | | > > > > > > > > > > > > > | | | 13959 13960 13961 13962 13963 13964 13965 13966 13967 13968 13969 13970 13971 13972 13973 13974 13975 13976 13977 13978 13979 13980 13981 13982 13983 13984 13985 13986 13987 13988 13989 13990 13991 13992 13993 13994 13995 13996 13997 13998 13999 14000 14001 14002 14003 14004 | *x = fsl_xlinker_empty; x->f = cb; x->state = cbState; x->name = name; return 0; } int fsl_cx_user_set( fsl_cx * const 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; }else{ char * u = fsl_strdup(userName); if(!u) return FSL_RC_OOM; else{ fsl_free(f->repo.user); f->repo.user = u; 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; } 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; else if(!db) return FSL_RC_NOT_A_REPO; else{ |
︙ | ︙ | |||
13694 13695 13696 13697 13698 13699 13700 | int fsl_cx_stat2( fsl_cx * const f, bool relativeToCwd, char const * zName, fsl_fstat * const tgt, fsl_buffer * const nameOut, bool fullPath){ int rc; fsl_buffer * b = 0; fsl_buffer * bufRel = 0; | | | | | 14017 14018 14019 14020 14021 14022 14023 14024 14025 14026 14027 14028 14029 14030 14031 14032 14033 14034 14035 14036 | int fsl_cx_stat2( fsl_cx * const f, bool relativeToCwd, 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; 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); #if 1 rc = fsl_ckout_filename_check(f, relativeToCwd, zName, bufRel); if(rc) goto end; zName = fsl_buffer_cstr2( bufRel, &n ); #else if(!fsl_is_simple_pathname(zName, 1)){ rc = fsl_ckout_filename_check(f, relativeToCwd, zName, bufRel); |
︙ | ︙ | |||
13738 13739 13740 13741 13742 13743 13744 | }else if(nameOut){ rc = fullPath ? fsl_buffer_append(nameOut, b->mem, b->used) : fsl_buffer_append(nameOut, zName, n); } } end: | | | | 14061 14062 14063 14064 14065 14066 14067 14068 14069 14070 14071 14072 14073 14074 14075 14076 | }else if(nameOut){ rc = fullPath ? 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); return rc; } int fsl_cx_stat(fsl_cx * const f, bool relativeToCwd, char const * zName, fsl_fstat * const tgt){ return fsl_cx_stat2(f, relativeToCwd, zName, tgt, NULL, false); } |
︙ | ︙ | |||
13762 13763 13764 13765 13766 13767 13768 | } char const * fsl_cx_filename_collation(fsl_cx const * f){ return f->cache.caseInsensitive ? "COLLATE nocase" : ""; } | | > > > > > > > > > > > > > | 14085 14086 14087 14088 14089 14090 14091 14092 14093 14094 14095 14096 14097 14098 14099 14100 14101 14102 14103 14104 14105 14106 14107 14108 14109 14110 14111 14112 14113 14114 14115 14116 14117 14118 14119 14120 14121 14122 14123 14124 14125 14126 14127 14128 14129 14130 14131 14132 14133 14134 14135 14136 14137 14138 14139 14140 14141 | } 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){ enum { MaxSize = 1024 * 1024 * 2 }; assert(f); if(f->fileContent.capacity>MaxSize){ fsl_buffer_resize(&f->fileContent, MaxSize); assert(f->fileContent.capacity<=MaxSize+1); } fsl_buffer_reuse(&f->fileContent); } fsl_error const * fsl_cx_err_get_e(fsl_cx const * f){ 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 ){ int i, count = 0; char const * rv = NULL; |
︙ | ︙ | |||
13920 13921 13922 13923 13924 13925 13926 | f->confirmer = newConfirmer ? *newConfirmer : fsl_confirmer_empty; } void fsl_cx_confirmer_get(fsl_cx const * f, fsl_confirmer * dest){ *dest = f->confirmer; } | | | | | 14256 14257 14258 14259 14260 14261 14262 14263 14264 14265 14266 14267 14268 14269 14270 14271 14272 14273 14274 14275 14276 14277 14278 14279 14280 14281 14282 14283 14284 14285 14286 14287 14288 14289 14290 14291 14292 14293 14294 14295 14296 14297 | f->confirmer = newConfirmer ? *newConfirmer : fsl_confirmer_empty; } 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, fsl_confirm_response *outAnswer){ if(f->confirmer.callback){ return f->confirmer.callback(detail, outAnswer, f->confirmer.callbackState); } /* Default answers... */ switch(detail->eventId){ case FSL_CEVENT_OVERWRITE_MOD_FILE: case FSL_CEVENT_OVERWRITE_UNMGD_FILE: outAnswer->response = FSL_CRESPONSE_NEVER; break; case FSL_CEVENT_RM_MOD_UNMGD_FILE: outAnswer->response = FSL_CRESPONSE_NEVER; break; case FSL_CEVENT_MULTIPLE_VERSIONS: outAnswer->response = FSL_CRESPONSE_CANCEL; break; default: assert(!"Unhandled fsl_confirm_event_e value"); 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 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, "seen-delta-manifest", 1); } |
︙ | ︙ | |||
13971 13972 13973 13974 13975 13976 13977 | if(fsl_is_reserved_fn(zPath, nPath)){ return fsl_cx_err_set(f, errRc, "Filename is reserved, not legal " "for adding to a repository: %.*s", (int)nPath, zPath); } if(!(f->flags & FSL_CX_F_ALLOW_WINDOWS_RESERVED_NAMES) | | | | | | | | | | | | | | 14307 14308 14309 14310 14311 14312 14313 14314 14315 14316 14317 14318 14319 14320 14321 14322 14323 14324 14325 14326 14327 14328 14329 14330 14331 14332 14333 14334 14335 14336 14337 14338 14339 14340 14341 14342 14343 14344 14345 14346 14347 14348 14349 14350 14351 14352 14353 14354 14355 14356 14357 14358 14359 14360 14361 14362 14363 14364 14365 14366 14367 14368 14369 14370 14371 14372 14373 14374 14375 14376 14377 14378 14379 14380 14381 14382 14383 14384 14385 14386 14387 14388 14389 14390 14391 14392 14393 14394 14395 14396 14397 14398 14399 14400 14401 14402 14403 14404 14405 14406 14407 14408 14409 14410 14411 14412 14413 14414 14415 14416 14417 14418 14419 14420 14421 14422 14423 14424 14425 14426 14427 14428 14429 | if(fsl_is_reserved_fn(zPath, nPath)){ return fsl_cx_err_set(f, errRc, "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)){ 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); 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, //fsl_buffer_cstr(c1), fsl_buffer_cstr(c2))); 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); if(rc) return rc; } assert(!rc); while(f->ckout.dir || relativeToCwd){ int manifestSetting = 0; fsl_ckout_manifest_setting(f, &manifestSetting); if(!manifestSetting) break; typedef struct { short flag; char const * fn; } MSetting; const MSetting M[] = { {FSL_MANIFEST_MAIN, "manifest"}, {FSL_MANIFEST_UUID, "manifest.uuid"}, {FSL_MANIFEST_TAGS, "manifest.tags"}, {0,0} }; 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); } if(rc) goto yield; char const * const z = fsl_buffer_cstr(c1); //MARKER(("Checking file against manifest setting 0x%03x: %s\n", //manifestSetting, z)); for( MSetting const * m = &M[0]; m->fn; ++m ){ if((m->flag & manifestSetting) && 0==fsl_strcmp(z, m->fn)){ rc = fsl_cx_err_set(f, errRc, "Filename is reserved due to the " "'manifest' setting: %s", m->fn); break; } } yield: fsl__cx_scratchpad_yield(f, c1); break; } return rc; } fsl_buffer * fsl__cx_scratchpad(fsl_cx * const f){ fsl_buffer * rc = 0; int i = (f->scratchpads.next<FSL_CX_NSCRATCH) ? f->scratchpads.next : 0; for(; i < FSL_CX_NSCRATCH; ++i){ if(!f->scratchpads.used[i]){ rc = &f->scratchpads.buf[i]; f->scratchpads.used[i] = true; ++f->scratchpads.next; //MARKER(("Doling out scratchpad[%d] w/ capacity=%d next=%d\n", // i, (int)rc->capacity, f->scratchpads.next)); break; } } if(!rc){ assert(!"Fatal fsl_cx::scratchpads 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, "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){ int i; assert(b); for(i = 0; i < FSL_CX_NSCRATCH; ++i){ if(b == &f->scratchpads.buf[i]){ assert(f->scratchpads.next != i); assert(f->scratchpads.used[i] && "Scratchpad misuse."); f->scratchpads.used[i] = false; fsl_buffer_reuse(b); if(f->scratchpads.next>i) f->scratchpads.next = i; //MARKER(("Yielded scratchpad[%d] w/ capacity=%d, next=%d\n", // i, (int)b->capacity, f->scratchpads.next)); return; } } fsl__fatal(FSL_RC_MISUSE, "Fatal internal fsl_cx::scratchpads misuse: " "passed a non-scratchpad buffer."); } /** @internal |
︙ | ︙ | |||
14139 14140 14141 14142 14143 14144 14145 | if(fsl_rmdir(zAbs)) break; ++rc; } } return rc; } | | | | | | | | > > > > > > > > > > > > > > > > > > > > | 14475 14476 14477 14478 14479 14480 14481 14482 14483 14484 14485 14486 14487 14488 14489 14490 14491 14492 14493 14494 14495 14496 14497 14498 14499 14500 14501 14502 14503 14504 14505 14506 14507 14508 14509 14510 14511 14512 14513 14514 14515 14516 14517 14518 14519 14520 14521 14522 14523 14524 14525 14526 14527 14528 14529 14530 14531 14532 14533 14534 14535 14536 14537 14538 14539 14540 14541 14542 14543 14544 14545 14546 14547 14548 14549 14550 14551 14552 14553 14554 14555 14556 14557 14558 14559 14560 14561 14562 14563 14564 14565 14566 14567 14568 14569 14570 14571 14572 14573 14574 14575 14576 14577 14578 14579 14580 14581 14582 14583 14584 | if(fsl_rmdir(zAbs)) break; ++rc; } } return rc; } unsigned int fsl_ckout_rm_empty_dirs(fsl_cx * const f, fsl_buffer * const 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){ 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_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); return rc; } } bool fsl_repo_forbids_delta_manifests(fsl_cx * f){ return fsl_config_get_bool(f, FSL_CONFDB_REPO, false, "forbid-delta-manifests"); } int fsl_ckout_fingerprint_check(fsl_cx * f){ fsl_db * const db = fsl_cx_db_ckout(f); 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); 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; }else if(rc){ goto end; } zCkout = fsl_buffer_cstr(buf); #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); switch(rc){ case FSL_RC_NOT_FOUND: goto mismatch; case 0: assert(zRepo); if(fsl_strcmp(zRepo,zCkout)){ goto mismatch; } break; default: break; } end: 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 " "fingerprints."); goto end; } 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); else return (f->flags & FSL_CX_F_LOCALTIME_GMT) ? gmtime(clock) |
︙ | ︙ | |||
14283 14284 14285 14286 14287 14288 14289 | */ static int fsl_list_v_fsl_stmt_finalize(void * obj, void * visitorState ){ if(obj) fsl_stmt_finalize( (fsl_stmt*)obj ); return 0; } #endif | | | 14639 14640 14641 14642 14643 14644 14645 14646 14647 14648 14649 14650 14651 14652 14653 | */ static int fsl_list_v_fsl_stmt_finalize(void * obj, void * visitorState ){ if(obj) fsl_stmt_finalize( (fsl_stmt*)obj ); return 0; } #endif 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); } |
︙ | ︙ | |||
14395 14396 14397 14398 14399 14400 14401 | (int)db->openStatementCount, db->filename)); } if(db->dbh){ sqlite3_close(db->dbh); /* ignoring results in the style of "destructors may not throw". */ } | | | 14751 14752 14753 14754 14755 14756 14757 14758 14759 14760 14761 14762 14763 14764 14765 | (int)db->openStatementCount, db->filename)); } 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_cleanup_beforeCommit(db); fsl_buffer_clear(&db->cachePrepBuf); *db = fsl_db_empty; if(&fsl_db_empty == allocStamp){ fsl_free( db ); }else{ db->allocStamp = allocStamp; |
︙ | ︙ | |||
14504 14505 14506 14507 14508 14509 14510 | } } return s; } enum fsl_stmt_flags_e { /** | | | 14860 14861 14862 14863 14864 14865 14866 14867 14868 14869 14870 14871 14872 14873 14874 | } } return s; } enum fsl_stmt_flags_e { /** fsl_stmt::flags bit indicating that fsl_db_preparev_cached() has doled out this statement, effectively locking it until fsl_stmt_cached_yield() is called to release it. */ FSL_STMT_F_CACHE_HELD = 0x01, /** Propagates our intent to "statically" prepare a given statement |
︙ | ︙ | |||
14668 14669 14670 14671 14672 14673 14674 14675 14676 14677 14678 14679 14680 14681 | rc = fsl_db_prepare( db, st, "%b", buf ); if(rc){ fsl_free(st); st = 0; }else{ st->sql.cursor = buf->cursor/*hash value!*/; st->next = db->cacheHead; db->cacheHead = st; st->flags = FSL_STMT_F_CACHE_HELD; *rv = st; } end: return rc; } | > > > | 15024 15025 15026 15027 15028 15029 15030 15031 15032 15033 15034 15035 15036 15037 15038 15039 15040 | rc = fsl_db_prepare( db, st, "%b", buf ); if(rc){ 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: return rc; } |
︙ | ︙ | |||
14773 14774 14775 14776 14777 14778 14779 14780 14781 14782 14783 14784 14785 14786 | fsl_free(stmt); }else{ stmt->allocStamp = allocStamp; } return 0; } } 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); switch( rc ){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 15132 15133 15134 15135 15136 15137 15138 15139 15140 15141 15142 15143 15144 15145 15146 15147 15148 15149 15150 15151 15152 15153 15154 15155 15156 15157 15158 15159 15160 15161 15162 15163 15164 15165 15166 15167 15168 15169 15170 15171 15172 15173 15174 15175 15176 15177 15178 15179 15180 | fsl_free(stmt); }else{ stmt->allocStamp = allocStamp; } 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); switch( rc ){ |
︙ | ︙ | |||
15586 15587 15588 15589 15590 15591 15592 | } int (*cmp)(char const *, char const *) = f->cache.caseInsensitive ? fsl_stricmp : fsl_strcmp; if(0==cmp(p1, p2)){ sqlite3_result_int(context, 1); return; } | | | | 15980 15981 15982 15983 15984 15985 15986 15987 15988 15989 15990 15991 15992 15993 15994 15995 15996 15997 15998 15999 16000 16001 16002 16003 16004 16005 | } int (*cmp)(char const *, char const *) = f->cache.caseInsensitive ? fsl_stricmp : fsl_strcmp; if(0==cmp(p1, p2)){ sqlite3_result_int(context, 1); return; } 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) rc = 2; } assert(0==rc || 2==rc); sqlite3_result_int(context, rc); end: fsl__cx_scratchpad_yield(f, b); return; oom: sqlite3_result_error_nomem(context); goto end; } /** |
︙ | ︙ | |||
15683 15684 15685 15686 15687 15688 15689 | } /* 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. */ | | | 16077 16078 16079 16080 16081 16082 16083 16084 16085 16086 16087 16088 16089 16090 16091 | } /* 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){ 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; } |
︙ | ︙ | |||
15708 15709 15710 15711 15712 15713 15714 | #else /* i cannot explain why, but the ptr i'm getting here is most definately 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){ | | | 16102 16103 16104 16105 16106 16107 16108 16109 16110 16111 16112 16113 16114 16115 16116 | #else /* i cannot explain why, but the ptr i'm getting here is most definately 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 " "outside of fsl_db_transaction_end() while a " "fsl_db_transaction_begin()-started transaction " "is pending."); return 2; } /* we have no context: sqlite3_result_error(context, "fsl_mtime_of_manifest_file() failed", -1); */ else return 0; |
︙ | ︙ | |||
15791 15792 15793 15794 15795 15796 15797 | rc = FSL_RC_OOM; goto end; } } db->dbh = dbh; if(FSL_OPEN_F_SCHEMA_VALIDATE & openFlags){ int check; | | | < | 16185 16186 16187 16188 16189 16190 16191 16192 16193 16194 16195 16196 16197 16198 16199 16200 16201 16202 16203 16204 16205 16206 16207 16208 16209 | rc = FSL_RC_OOM; goto end; } } db->dbh = dbh; if(FSL_OPEN_F_SCHEMA_VALIDATE & openFlags){ int check; 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); assert(rc == db->error.code); goto end; } } if( (openFlags & FSL_OPEN_F_TRACE_SQL) || (db->f && db->f->cxConfig.traceSql) ){ |
︙ | ︙ | |||
16054 16055 16056 16057 16058 16059 16060 | "No transaction is active."); } 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. | | < | 16447 16448 16449 16450 16451 16452 16453 16454 16455 16456 16457 16458 16459 16460 16461 | "No transaction is active."); } 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))){ /* Execute before-commit hooks and leaf checks */ fsl_size_t x = 0; |
︙ | ︙ | |||
16078 16079 16080 16081 16082 16083 16084 | functionality in an otherwise generic routine. Maybe we need fsl_cx_transaction_begin/end() instead. 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. */ | | | | 16470 16471 16472 16473 16474 16475 16476 16477 16478 16479 16480 16481 16482 16483 16484 16485 16486 | functionality in an otherwise generic routine. Maybe we need fsl_cx_transaction_begin/end() instead. 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); if(!rc && db->f->cache.toVerify.used){ rc = fsl__repo_verify_at_commit(db->f); }else{ fsl_repo_verify_cancel(db->f); } } db->doRollback = rc ? 1 : 0; } fsl_db_cleanup_beforeCommit(db); |
︙ | ︙ | |||
16767 16768 16769 16770 16771 16772 16773 | /* Only for debugging */ #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) typedef int StaticAssertMCacheArraySizes[ | | | | | | | | | | 17159 17160 17161 17162 17163 17164 17165 17166 17167 17168 17169 17170 17171 17172 17173 17174 17175 17176 17177 17178 17179 17180 17181 17182 17183 17184 17185 17186 17187 17188 17189 17190 17191 17192 17193 17194 17195 17196 17197 17198 17199 17200 | /* Only for debugging */ #define MARKER(pfexp) \ 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]))) ? 1 : -1 ]; enum fsl_card_F_list_flags_e { FSL_CARD_F_LIST_NEEDS_SORT = 0x01 }; /** Transfers the contents of d into f->cache.mcache. If d is dynamically allocated then it is also freed. In any case, after 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){ 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; while( d ){ unsigned i; fsl_deck *pBaseline = d->B.baseline; d->B.baseline = 0; for(i=0; i<cacheLen; ++i){ if( !mc->decks[i].rid ) break; } |
︙ | ︙ | |||
16838 16839 16840 16841 16842 16843 16844 | it is bitwise copied over tgt, that entry is removed from the cache, and true is returned. If no match is found, tgt is not modified and false is returned. If manifest caching is disabled for f, false is immediately returned without causing side effects. */ | | > | > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 17230 17231 17232 17233 17234 17235 17236 17237 17238 17239 17240 17241 17242 17243 17244 17245 17246 17247 17248 17249 17250 17251 17252 17253 17254 17255 17256 17257 17258 17259 17260 17261 17262 17263 17264 17265 17266 17267 17268 17269 17270 17271 17272 17273 17274 17275 17276 17277 17278 17279 17280 17281 17282 17283 17284 17285 17286 17287 17288 17289 17290 17291 17292 17293 17294 17295 17296 17297 17298 17299 17300 17301 17302 | it is bitwise copied over tgt, that entry is removed from the cache, and true is returned. If no match is found, tgt is not modified and false is returned. If manifest caching is disabled for f, false is immediately returned without causing side effects. */ static bool fsl__cx_mcache_search(fsl_cx * const f, fsl_id_t rid, fsl_deck * const tgt){ if(!(f->flags & FSL_CX_F_MANIFEST_CACHE)){ ++f->cache.mcache.misses; return false; } static const unsigned cacheLen = (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]))); for(i=0; i<cacheLen; ++i){ if( f->cache.mcache.decks[i].rid==rid ){ *tgt = f->cache.mcache.decks[i]; f->cache.mcache.decks[i] = fsl_deck_empty; ++f->cache.mcache.hits; return true; } } ++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 optimized away via d->content. */ |
︙ | ︙ | |||
17077 17078 17079 17080 17081 17082 17083 | if(rc){ *rc = fsl_deck_empty; rc->allocStamp = &fsl_deck_empty; } return rc; } | | | | 17508 17509 17510 17511 17512 17513 17514 17515 17516 17517 17518 17519 17520 17521 17522 17523 17524 17525 17526 17527 17528 17529 17530 | if(rc){ *rc = fsl_deck_empty; rc->allocStamp = &fsl_deck_empty; } return rc; } void fsl_deck_init( fsl_cx * const f, fsl_deck * const 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){ 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; } /* fsl_deck cleanup helpers... */ #define SFREE(X) fsl_deck_clean_string(m, &m->X) |
︙ | ︙ | |||
17138 17139 17140 17141 17142 17143 17144 | static void fsl_deck_clean_H(fsl_deck * const m){ fsl_deck_clean_string(m, &m->H); } 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){ | | | 17569 17570 17571 17572 17573 17574 17575 17576 17577 17578 17579 17580 17581 17582 17583 | static void fsl_deck_clean_H(fsl_deck * const m){ fsl_deck_clean_string(m, &m->H); } 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); } 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){ fsl_deck_clean_string(m, &m->L); } |
︙ | ︙ | |||
17859 17860 17861 17862 17863 17864 17865 | fsl_md5_update_buffer(&md5, buf); } if(!rc){ fsl_md5_final(&md5, digest); fsl_md5_digest_to_base16(digest, hex); } end: | | | 18290 18291 18292 18293 18294 18295 18296 18297 18298 18299 18300 18301 18302 18303 18304 | fsl_md5_update_buffer(&md5, buf); } if(!rc){ fsl_md5_final(&md5, digest); fsl_md5_digest_to_base16(digest, hex); } end: fsl__cx_content_buffer_yield(f); assert(0==buf->used); if(rc) return rc; fsl_deck_F_rewind(mf); theHash = hex; } assert(theHash); if(*tgt){ |
︙ | ︙ | |||
18173 18174 18175 18176 18177 18178 18179 | fsl_md5_cx md5; /* Holds error state for propagating back to the client. */ fsl_error error; /** Scratch buffer for fossilizing bytes and other temporary work. | | | 18604 18605 18606 18607 18608 18609 18610 18611 18612 18613 18614 18615 18616 18617 18618 | fsl_md5_cx md5; /* 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(). */ fsl_buffer * scratch; }; typedef struct fsl_deck_out_state fsl_deck_out_state; static const fsl_deck_out_state fsl_deck_out_state_empty = { NULL/*d*/, NULL/*out*/, |
︙ | ︙ | |||
18482 18483 18484 18485 18486 18487 18488 | /** 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. */ | | | 18913 18914 18915 18916 18917 18918 18919 18920 18921 18922 18923 18924 18925 18926 18927 | /** 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 ){ 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); assert(r); if(!l) return r ? 1 : 0; |
︙ | ︙ | |||
19088 19089 19090 19091 19092 19093 19094 | int fsl_deck_unshuffle( fsl_deck * d, bool calculateRCard ){ 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) | | | 19519 19520 19521 19522 19523 19524 19525 19526 19527 19528 19529 19530 19531 19532 19533 | int fsl_deck_unshuffle( fsl_deck * d, bool calculateRCard ){ 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(M,qsort_cmp_strings); SORT(Q,qsort_cmp_Q_cards); SORT(T,fsl_card_T_cmp); #undef SORT if(FSL_SATYPE_CHECKIN!=d->type){ assert(!fsl_card_is_legal(d->type,'R')); assert(!fsl_card_is_legal(d->type,'F')); |
︙ | ︙ | |||
19161 19162 19163 19164 19165 19166 19167 | else if(!fsl_deck_has_required_cards(d)){ return FSL_RC_SYNTAX; } os->d = d; os->out = out; os->outState = outputState; | | | 19592 19593 19594 19595 19596 19597 19598 19599 19600 19601 19602 19603 19604 19605 19606 | else if(!fsl_deck_has_required_cards(d)){ return FSL_RC_SYNTAX; } os->d = d; os->out = out; os->outState = outputState; os->scratch = fsl__cx_scratchpad(f); switch(d->type){ case FSL_SATYPE_CLUSTER: rc = fsl_deck_output_cluster(os); break; case FSL_SATYPE_CONTROL: rc = fsl_deck_output_control(os); break; |
︙ | ︙ | |||
19201 19202 19203 19204 19205 19206 19207 | d->type); goto end; } if(!rc){ rc = fsl_deck_out_Z( os ); } end: | | | 19632 19633 19634 19635 19636 19637 19638 19639 19640 19641 19642 19643 19644 19645 19646 | d->type); goto end; } if(!rc){ rc = fsl_deck_out_Z( os ); } end: 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; } |
︙ | ︙ | |||
19225 19226 19227 19228 19229 19230 19231 | #define AGE_ADJUST_INCREMENT (25.0/86400000.0) /* 25 milliseconds */ /** 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. */ | | | 19656 19657 19658 19659 19660 19661 19662 19663 19664 19665 19666 19667 19668 19669 19670 | #define AGE_ADJUST_INCREMENT (25.0/86400000.0) /* 25 milliseconds */ /** 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){ int rc = 0; assert(f->cache.isCrosslinking); rc = fsl_db_exec(f->dbMain, "INSERT OR IGNORE INTO pending_xlink VALUES('%c%q')", cType, uuid); return fsl_cx_uplift_db_error2(f, 0, rc); } |
︙ | ︙ | |||
19280 19281 19282 19283 19284 19285 19286 | int rc; bool doInsert = false; assert(f); assert(db); assert(db->beginCount>0); //MARKER(("%s() pmid=%d mid=%d\n", __func__, (int)pmid, (int)mid)); | | | | | | 19711 19712 19713 19714 19715 19716 19717 19718 19719 19720 19721 19722 19723 19724 19725 19726 19727 19728 19729 19730 19731 19732 19733 19734 19735 19736 19737 19738 19739 19740 19741 19742 19743 19744 19745 | int rc; bool doInsert = false; 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); if(rc) return rc; if( zPrior && *zPrior ){ 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); if(pid<0){ assert(f->error.code); return f->error.code; } assert(pid>0); }else{ pid = 0; } if( zToUuid && *zToUuid ){ 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); if(rc) return rc; } |
︙ | ︙ | |||
19361 19362 19363 19364 19365 19366 19367 | fsl_cx_uplift_db_error(f, db); } } } 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. */ | | | 19792 19793 19794 19795 19796 19797 19798 19799 19800 19801 19802 19803 19804 19805 19806 | fsl_cx_uplift_db_error(f, db); } } } 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); } end: return rc; } /** Do a binary search to find a file in d->F.list. |
︙ | ︙ | |||
19398 19399 19400 19401 19402 19403 19404 | Fossil(1) added another parameter to this since it was ported, 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. */ | | | 19829 19830 19831 19832 19833 19834 19835 19836 19837 19838 19839 19840 19841 19842 19843 | Fossil(1) added another parameter to this since it was ported, 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, char const * zName, uint32_t * atNdx ){ /* Maintenance reminder: this algo relies on the various counters being signed. */ fsl_int_t lwr, upr; int c; fsl_int_t i; |
︙ | ︙ | |||
19447 19448 19449 19450 19451 19452 19453 | return FCARD(i); } } return NULL; #undef FCARD } | | | | | | | 19878 19879 19880 19881 19882 19883 19884 19885 19886 19887 19888 19889 19890 19891 19892 19893 19894 19895 19896 19897 19898 19899 19900 19901 19902 19903 19904 19905 19906 19907 19908 19909 19910 19911 19912 19913 19914 19915 19916 19917 19918 19919 19920 19921 19922 19923 19924 19925 19926 19927 19928 19929 19930 19931 19932 19933 19934 19935 19936 19937 19938 19939 19940 19941 19942 19943 19944 19945 19946 19947 | return FCARD(i); } } return NULL; #undef FCARD } 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); if( !pFile && (d->B.baseline /* we have a baseline or... */ || (d->f && d->B.uuid) /* we can load the baseline */ )){ /* Check baseline manifest... Sidebar: while the delta manifest model outwardly appears to support recursive delta manifests, fossil(1) does not use them and there would seem to be little practical use for them (no notable size benefit for the majority of cases), so we're not recursing here. */ int const rc = d->B.baseline ? 0 : fsl_deck_baseline_fetch(d); if(rc){ 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); if(pFile){ assert(pFile->uuid && "Per fossil-dev thread with DRH on 20140422, " "baselines never have removed files."); } } } 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); } int fsl_deck_F_set( fsl_deck * d, char const * zName, char const * uuid, fsl_fileperm_e perms, char const * priorName){ uint32_t fcNdx = 0; fsl_card_F * fc = 0; if(d->rid>0){ return fsl_cx_err_set(d->f, FSL_RC_MISUSE, "%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); if(!uuid){ if(fc){ fsl_card_F_list_remove(&d->F, fcNdx); return 0; }else{ return FSL_RC_NOT_FOUND; } |
︙ | ︙ | |||
19567 19568 19569 19570 19571 19572 19573 | rc = fsl_repo_blob_lookup(d->f, src, &rid, &zHash); if(rc && FSL_RC_NOT_FOUND!=rc) goto end; 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. */ | | | | | | 19998 19999 20000 20001 20002 20003 20004 20005 20006 20007 20008 20009 20010 20011 20012 20013 20014 20015 20016 20017 20018 20019 20020 20021 20022 20023 20024 20025 20026 20027 20028 20029 20030 20031 20032 20033 20034 20035 | rc = fsl_repo_blob_lookup(d->f, src, &rid, &zHash); if(rc && FSL_RC_NOT_FOUND!=rc) goto end; 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); if(rc) goto end; 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"); 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); 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){ char const * c = letters ? letters : "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; for( ; *c; ++c ){ switch(*c){ case 'A': fsl_deck_clean_A(d); break; case 'B': fsl_deck_clean_B(d); break; |
︙ | ︙ | |||
19642 19643 19644 19645 19646 19647 19648 | }else{ assert(d->f->error.code); rc = d->f->error.code; } if(rc) return rc; } d->rid = 0; | | | 20073 20074 20075 20076 20077 20078 20079 20080 20081 20082 20083 20084 20085 20086 20087 | }else{ assert(d->f->error.code); rc = d->f->error.code; } if(rc) return rc; } d->rid = 0; 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; uint32_t fCount = 0; rc = fsl_deck_F_rewind(d); |
︙ | ︙ | |||
19793 19794 19795 19796 19797 19798 19799 | pChild = &dOther; otherRid = cid; }else{ pParent = &dOther; otherRid = pmid; } | | | 20224 20225 20226 20227 20228 20229 20230 20231 20232 20233 20234 20235 20236 20237 20238 | pChild = &dOther; otherRid = cid; }else{ pParent = &dOther; otherRid = pmid; } 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 unclear, though. */ if(FSL_RC_PHANTOM==rc){ |
︙ | ︙ | |||
19836 19837 19838 19839 19840 19841 19842 | ** primary parent dephantomizes. */ 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; | | | | | 20267 20268 20269 20270 20271 20272 20273 20274 20275 20276 20277 20278 20279 20280 20281 20282 20283 20284 20285 20286 20287 20288 20289 20290 20291 20292 20293 20294 | ** primary parent dephantomizes. */ 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); goto end; } } if(pmid>0){ /* 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); }else if( pChild->B.uuid==NULL && pParent->B.uuid!=NULL ){ 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, as we might want to fudge the times for those children. */ |
︙ | ︙ | |||
19881 19882 19883 19884 19885 19886 19887 | : NULL) for(i=0, pChildFile=FCARD(pChild,0); i<pChild->F.used; ++i, pChildFile=FCARD(pChild,i)){ fsl_fileperm_e const mperm = pChildFile->perm; if( pChildFile->priorName ){ pParentFile = pmid | | | 20312 20313 20314 20315 20316 20317 20318 20319 20320 20321 20322 20323 20324 20325 20326 | : NULL) for(i=0, pChildFile=FCARD(pChild,0); i<pChild->F.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) : 0; if( pParentFile ){ /* File with name change */ /* libfossil checkin 8625a31eff708dea93b16582e4ec5d583794d1af contains these two interesting F-cards: |
︙ | ︙ | |||
19908 19909 19910 19911 19912 19913 19914 | /* File name changed, but the old name is not found in the parent! Treat this like a new file. */ rc = fsl_mlink_add_one(f, pmid, 0, cid, pChildFile->uuid, pChildFile->name, 0, isPublic, isPrimary, mperm); } }else if(pmid){ | | | 20339 20340 20341 20342 20343 20344 20345 20346 20347 20348 20349 20350 20351 20352 20353 | /* File name changed, but the old name is not found in the parent! Treat this like a new file. */ 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); 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, pChildFile->name, 0, isPublic, isPrimary, mperm); |
︙ | ︙ | |||
19938 19939 19940 19941 19942 19943 19944 | are deleted or modified in the parent but which reappear or revert to baseline in the child and show such files as being added or changed in the child. */ for(i=0, pParentFile=FCARD(pParent,0); i<pParent->F.used; ++i, pParentFile = FCARD(pParent,i)){ if( pParentFile->uuid ){ | | | | | | | 20369 20370 20371 20372 20373 20374 20375 20376 20377 20378 20379 20380 20381 20382 20383 20384 20385 20386 20387 20388 20389 20390 20391 20392 20393 20394 20395 20396 20397 20398 20399 20400 20401 20402 20403 20404 20405 20406 20407 20408 20409 20410 20411 20412 20413 20414 20415 20416 20417 20418 20419 20420 20421 20422 20423 20424 20425 20426 20427 20428 20429 | are deleted or modified in the parent but which reappear or revert to baseline in the child and show such files as being added or changed in the child. */ for(i=0, pParentFile=FCARD(pParent,0); i<pParent->F.used; ++i, pParentFile = FCARD(pParent,i)){ if( pParentFile->uuid ){ 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); } if( pChildFile && pChildFile->uuid ){ rc = fsl_mlink_add_one(f, pmid, pParentFile->uuid, cid, pChildFile->uuid, pChildFile->name, 0, isPublic, isPrimary, pChildFile->perm); } } }else{ /* Was deleted in the parent. */ 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, isPrimary, pChildFile->perm); } } if(rc) goto end; } assert(0==rc); }else if( pmid && !pChild->B.uuid ){ /* pChild is a baseline with a parent. Look for files that are present in pParent but are missing from pChild and mark them as 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); 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); /* If pParent is the primary parent of pChild, also run this analysis ** for all merge parents of pChild */ if( pmid && isPrimary ){ for(i=1; i<pChild->P.used; i++){ pmid = fsl_uuid_to_rid(f, (char const*)pChild->P.list[i]); if( pmid<=0 ) continue; |
︙ | ︙ | |||
20020 20021 20022 20023 20024 20025 20026 | /** 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. */ | | | 20451 20452 20453 20454 20455 20456 20457 20458 20459 20460 20461 20462 20463 20464 20465 | /** 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, fsl_db * db, fsl_id_t rid, fsl_id_t parentId){ int rc = 0; fsl_size_t i; fsl_list const * li = &d->T; double tagTime = d->D; if(li->used && tagTime<=0){ |
︙ | ︙ | |||
20053 20054 20055 20056 20057 20058 20059 | break; }else if(0==tid){ rc = fsl_cx_err_set(f, FSL_RC_RANGE, "Could not get RID for [%.12s].", tag->uuid); break; } | | | | 20484 20485 20486 20487 20488 20489 20490 20491 20492 20493 20494 20495 20496 20497 20498 20499 20500 20501 20502 20503 | break; }else if(0==tid){ 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, tag->name, tag->value, rid, tagTime, tid, NULL); } if( !rc && (parentId>0) ){ rc = fsl__tag_propagate_all(f, parentId); } end: return rc; } /** Part of the checkin crosslink machinery: create all appropriate |
︙ | ︙ | |||
20102 20103 20104 20105 20106 20107 20108 | }else{ fsl_snprintf( zBaseId, sizeof(zBaseId), "NULL" ); } *parentId = 0; for(i=0; i<d->P.used; ++i){ char const * parentUuid = (char const *)d->P.list[i]; | | | 20533 20534 20535 20536 20537 20538 20539 20540 20541 20542 20543 20544 20545 20546 20547 | }else{ fsl_snprintf( zBaseId, sizeof(zBaseId), "NULL" ); } *parentId = 0; for(i=0; i<d->P.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); if(pid<0){ assert(f->error.code); rc = f->error.code; goto end; } rc = fsl_db_exec(db, "INSERT OR IGNORE " "INTO plink(pid, cid, isprim, mtime, baseid) " |
︙ | ︙ | |||
20175 20176 20177 20178 20179 20180 20181 | end: return rc; } /** 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 | | | 20606 20607 20608 20609 20610 20611 20612 20613 20614 20615 20616 20617 20618 20619 20620 | end: return rc; } /** 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(). 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 crosslinking and would only result in a not-found if the tagxref table contents is out of date. |
︙ | ︙ | |||
20265 20266 20267 20268 20269 20270 20271 | /** 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. */ | | | | | | | < | 20696 20697 20698 20699 20700 20701 20702 20703 20704 20705 20706 20707 20708 20709 20710 20711 20712 20713 20714 20715 20716 20717 20718 20719 20720 20721 20722 20723 20724 20725 20726 20727 20728 20729 20730 20731 20732 20733 20734 20735 20736 20737 20738 20739 20740 20741 20742 20743 20744 20745 20746 20747 20748 20749 20750 20751 20752 20753 20754 20755 20756 20757 | /** 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){ int i; fsl_id_t parentId = 0; fsl_db * db; int rc = 0; assert(d->type==FSL_SATYPE_WIKI || d->type==FSL_SATYPE_FORUMPOST || 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_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], fantomMode); if(0==i) parentId = pid; rc = fsl_db_exec_multi(db, "INSERT OR IGNORE INTO plink" "(pid, cid, isprim, mtime, baseid)" "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); } 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){ if(FSL_SATYPE_ATTACHMENT!=d->type) return 0; int rc; fsl_db * const db = fsl_cx_db_repo(d->f); 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. */; 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 */; }else if( fsl_db_exists(db, "SELECT 1 FROM tag WHERE tagname='event-%q'", d->A.tgt)){ |
︙ | ︙ | |||
20380 20381 20382 20383 20384 20385 20386 | return rc; } /** Overrideable crosslink listener which updates the timeline for checkin records. */ | | | 20810 20811 20812 20813 20814 20815 20816 20817 20818 20819 20820 20821 20822 20823 20824 | return rc; } /** Overrideable crosslink listener which updates the timeline for checkin records. */ static int fsl_deck_xlink_f_checkin(fsl_deck * const 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); rc = fsl_db_exec(db, "REPLACE INTO event(type,mtime,objid,user,comment," |
︙ | ︙ | |||
20428 20429 20430 20431 20432 20433 20434 | (int)FSL_TAGID_BGCOLOR, d->rid, (int)FSL_TAGID_USER, d->rid, (int)FSL_TAGID_COMMENT, d->rid, d->D ); return fsl_cx_uplift_db_error2(d->f, db, rc); } | | | 20858 20859 20860 20861 20862 20863 20864 20865 20866 20867 20868 20869 20870 20871 20872 | (int)FSL_TAGID_BGCOLOR, d->rid, (int)FSL_TAGID_USER, d->rid, (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){ 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 fossil(1). i would prefer to farm this out to a crosslink callback, and provide a default implementation which more or |
︙ | ︙ | |||
20575 20576 20577 20578 20579 20580 20581 | end: fsl_buffer_clear(&comment); return rc; } | | | 21005 21006 21007 21008 21009 21010 21011 21012 21013 21014 21015 21016 21017 21018 21019 | end: fsl_buffer_clear(&comment); return rc; } static int fsl_deck_xlink_f_forum(fsl_deck * const 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; fsl_id_t const froot = d->G ? fsl_uuid_to_rid(f, d->G) : d->rid; fsl_id_t const fprev = d->P.used ? fsl_uuid_to_rid(f, (char const *)d->P.list[0]): 0; |
︙ | ︙ | |||
20661 20662 20663 20664 20665 20666 20667 | dberr: assert(rc); assert(db->error.code); return fsl_cx_uplift_db_error(f, db); } | | | 21091 21092 21093 21094 21095 21096 21097 21098 21099 21100 21101 21102 21103 21104 21105 | dberr: assert(rc); 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){ 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; fsl_cx * const f = d->f; fsl_db * const db = fsl_cx_db_repo(d->f); |
︙ | ︙ | |||
20710 20711 20712 20713 20714 20715 20716 | d->E.julian, d->rid, tagid, d->U, d->C, (int)FSL_TAGID_BGCOLOR, d->rid); } return rc; } | | | 21140 21141 21142 21143 21144 21145 21146 21147 21148 21149 21150 21151 21152 21153 21154 | d->E.julian, d->rid, tagid, d->U, d->C, (int)FSL_TAGID_BGCOLOR, d->rid); } return rc; } static int fsl_deck_xlink_f_wiki(fsl_deck * const d, void * state){ if(FSL_SATYPE_WIKI!=d->type) return 0; int rc; char const * zWiki; fsl_size_t nWiki = 0; char cPrefix = 0; char * zTag = fsl_mprintf("wiki-%s", d->L); if(!zTag) return FSL_RC_OOM; |
︙ | ︙ | |||
20763 20764 20765 20766 20767 20768 20769 | /** @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 | | | 21193 21194 21195 21196 21197 21198 21199 21200 21201 21202 21203 21204 21205 21206 21207 | /** @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 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){ int rc; assert(!f->xlinkers.used); |
︙ | ︙ | |||
20788 20789 20790 20791 20792 20793 20794 | fsl_deck_xlink_f_technote, 0); if(!rc) rc = fsl_xlink_listener(f, "fsl/wiki/timeline", fsl_deck_xlink_f_wiki, 0); return rc; } | | | 21218 21219 21220 21221 21222 21223 21224 21225 21226 21227 21228 21229 21230 21231 21232 | fsl_deck_xlink_f_technote, 0); if(!rc) rc = fsl_xlink_listener(f, "fsl/wiki/timeline", fsl_deck_xlink_f_wiki, 0); return rc; } 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); /* TODO: convert these queries to cached statements, for the sake of rebuild and friends. And bind() doubles |
︙ | ︙ | |||
20817 20818 20819 20820 20821 20822 20823 | } } 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 ); */ | | | | | 21247 21248 21249 21250 21251 21252 21253 21254 21255 21256 21257 21258 21259 21260 21261 21262 21263 21264 21265 21266 21267 21268 21269 21270 21271 21272 21273 21274 21275 21276 21277 21278 21279 | } } 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); 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); if(rc) goto end; } assert(!rc); }/*!exists mlink*/ end: if(rc && !f->error.code && db->error.code){ fsl_cx_uplift_db_error(f, db); } return rc; } 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; char * zTag = fsl_mprintf("wiki-%s", d->L); fsl_cx * const f = d->f; |
︙ | ︙ | |||
20860 20861 20862 20863 20864 20865 20866 | nWiki = fsl_strlen(zWiki) /* Reminder: use strlen instead of d->W.used just in case that 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); | | | | | | | | 21290 21291 21292 21293 21294 21295 21296 21297 21298 21299 21300 21301 21302 21303 21304 21305 21306 21307 21308 21309 21310 21311 21312 21313 21314 21315 21316 21317 21318 21319 21320 21321 21322 21323 21324 21325 21326 21327 21328 21329 21330 | nWiki = fsl_strlen(zWiki) /* Reminder: use strlen instead of d->W.used just in case that 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, 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); if(rc) goto end; } 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); if(rc) goto end; }else{ /* FSL-MISSING: backlink_wiki_refresh(d->L); */ } assert(0==rc); rc = fsl__deck_crosslink_fwt_plink(d); end: fsl_free(zTag); return rc; } 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, /* REMINDER: fossil(1) uses INSERT here, but that breaks libfossil crosslinking tests due to a |
︙ | ︙ | |||
20915 20916 20917 20918 20919 20920 20921 | " WHERE target=%Q AND filename=%Q", d->A.tgt, d->A.name, d->A.tgt, d->A.name); } return rc; } | | > > > > > | 21345 21346 21347 21348 21349 21350 21351 21352 21353 21354 21355 21356 21357 21358 21359 21360 21361 21362 21363 21364 21365 21366 21367 21368 21369 21370 21371 | " WHERE target=%Q AND filename=%Q", d->A.tgt, d->A.name, d->A.tgt, d->A.name); } return rc; } 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); for( i = 0; i < d->M.used; ++i ){ fsl_id_t mid; |
︙ | ︙ | |||
20946 20947 20948 20949 20950 20951 20952 | } } fsl_stmt_cached_yield(st); return rc; } #if 0 | | | | | | | 21381 21382 21383 21384 21385 21386 21387 21388 21389 21390 21391 21392 21393 21394 21395 21396 21397 21398 21399 21400 21401 21402 21403 21404 21405 21406 21407 21408 21409 21410 21411 21412 21413 21414 21415 21416 21417 21418 21419 21420 21421 21422 21423 21424 21425 21426 | } } fsl_stmt_cached_yield(st); return rc; } #if 0 static int fsl__deck_crosslink_control(fsl_deck *const d){ } #endif 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); fsl_id_t const froot = d->G ? fsl_uuid_to_rid(f, d->G) : d->rid; fsl_id_t const fprev = d->P.used ? fsl_uuid_to_rid(f, (char const *)d->P.list[0]): 0; fsl_id_t const firt = d->I ? fsl_uuid_to_rid(f, d->I) : 0; assert(f && db); rc = fsl_db_exec_multi(db, "REPLACE INTO forumpost(fpid,froot,fprev,firt,fmtime)" "VALUES(%" FSL_ID_T_PFMT ",%" FSL_ID_T_PFMT "," "nullif(%" FSL_ID_T_PFMT ",0)," "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); } if(!rc){ rc = fsl__deck_crosslink_fwt_plink(d); } return rc; } 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; char const * zTag; fsl_size_t nWiki = 0; |
︙ | ︙ | |||
21007 21008 21009 21010 21011 21012 21013 | while( *zWiki && fsl_isspace(*zWiki) ){ ++zWiki; /* Historical behaviour: strip leading spaces. */ } nWiki = fsl_strlen(zWiki); fsl_snprintf( zLength, sizeof(zLength), "%"FSL_SIZE_T_PFMT, (fsl_size_t)nWiki); | | | 21442 21443 21444 21445 21446 21447 21448 21449 21450 21451 21452 21453 21454 21455 21456 | while( *zWiki && fsl_isspace(*zWiki) ){ ++zWiki; /* 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, 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){ assert(f->error.code); rc = f->error.code; |
︙ | ︙ | |||
21033 21034 21035 21036 21037 21038 21039 | tagid, d->D, d->rid); if(subsequent<0){ assert(db->error.code); rc = fsl_cx_uplift_db_error(f, db); goto end; } else if( prior > 0 ){ | | | | | | | 21468 21469 21470 21471 21472 21473 21474 21475 21476 21477 21478 21479 21480 21481 21482 21483 21484 21485 21486 21487 21488 21489 21490 21491 21492 21493 21494 21495 21496 21497 21498 21499 21500 21501 21502 21503 21504 21505 21506 21507 21508 21509 21510 21511 | tagid, d->D, d->rid); if(subsequent<0){ 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); if( !rc && !subsequent ){ rc = fsl_db_exec(db, "DELETE FROM event" " WHERE type='e'" " AND tagid=%"FSL_ID_T_PFMT " AND objid IN" " (SELECT rid FROM tagxref " " WHERE tagid=%"FSL_ID_T_PFMT")", tagid, tagid); } } if(rc) goto end; if( 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); /* FSL-MISSING: assert( manifest_event_triggers_are_enabled ); */ } if(!rc){ rc = fsl__deck_crosslink_fwt_plink(d); } end: return rc; } 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 /* TODO: huge block from manifest_crosslink(). A full port |
︙ | ︙ | |||
21087 21088 21089 21090 21091 21092 21093 | rc = fsl_cx_err_set(f, FSL_RC_NYI, "MISSING: a huge block of TICKET stuff from " "manifest_crosslink(). It requires infrastructure " "libfossil does not yet have."); return rc; } | | | | | < < < < < | > | < | | 21522 21523 21524 21525 21526 21527 21528 21529 21530 21531 21532 21533 21534 21535 21536 21537 21538 21539 21540 21541 21542 21543 21544 21545 21546 21547 21548 | rc = fsl_cx_err_set(f, FSL_RC_NYI, "MISSING: a huge block of TICKET stuff from " "manifest_crosslink(). It requires infrastructure " "libfossil does not yet have."); return rc; } 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(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); return rc; } 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; if(!f) return FSL_RC_MISUSE; else if(rid<=0){ |
︙ | ︙ | |||
21136 21137 21138 21139 21140 21141 21142 | && d->B.uuid && !d->B.baseline){ rc = fsl_deck_baseline_fetch(d); if(rc) goto end; assert(d->B.baseline); } switch(d->type){ case FSL_SATYPE_CHECKIN: | | | | | | | | | > > > | | | | | > | | 21566 21567 21568 21569 21570 21571 21572 21573 21574 21575 21576 21577 21578 21579 21580 21581 21582 21583 21584 21585 21586 21587 21588 21589 21590 21591 21592 21593 21594 21595 21596 21597 21598 21599 21600 21601 21602 21603 21604 21605 21606 21607 21608 21609 21610 21611 21612 21613 21614 21615 21616 21617 21618 21619 21620 21621 21622 21623 21624 21625 21626 21627 21628 21629 21630 21631 21632 21633 21634 21635 21636 21637 21638 21639 21640 21641 21642 21643 21644 21645 21646 21647 21648 21649 21650 21651 21652 21653 21654 21655 | && d->B.uuid && !d->B.baseline){ rc = fsl_deck_baseline_fetch(d); if(rc) goto end; assert(d->B.baseline); } switch(d->type){ case FSL_SATYPE_CHECKIN: rc = fsl__deck_crosslink_checkin(d, &parentid); break; case FSL_SATYPE_CLUSTER: 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); break; default: break; } if(rc) goto end; switch(d->type){ case FSL_SATYPE_WIKI: rc = fsl__deck_crosslink_wiki(d); break; case FSL_SATYPE_FORUMPOST: rc = fsl__deck_crosslink_forum(d); break; case FSL_SATYPE_TECHNOTE: rc = fsl__deck_crosslink_technote(d); break; case FSL_SATYPE_TICKET: rc = fsl__deck_crosslink_ticket(d); break; case FSL_SATYPE_ATTACHMENT: 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; } if(rc) goto end; /* Call any crosslink callbacks... */ if(f->xlinkers.list){ fsl_size_t i; fsl_xlinker * xl = NULL; for( i = 0; !rc && (i < f->xlinkers.used); ++i ){ xl = f->xlinkers.list+i; 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); } } } }/*end crosslink callbacks*/ end: if(!rc){ rc = fsl_db_transaction_end(db, false); }else{ if(db->error.code && !f->error.code){ fsl_cx_uplift_db_error(f,db); } fsl_db_transaction_end(db, true); } return rc; }/*end fsl__deck_crosslink()*/ /** Return true if z points to the first character after a blank line. Tolerate either \r\n or \n line endings. As this looks backwards in z, z must point to at least 3 characters past the beginning of |
︙ | ︙ | |||
21255 21256 21257 21258 21259 21260 21261 | fsl_md5_digest_to_base16(digest, hex); return (0==memcmp(zHash, hex, FSL_STRLEN_MD5)) ? 1 : -1; } } | > > > > > > > > > > > > > > > > > | < > > > > > > > > > > > > > > > > > | 21689 21690 21691 21692 21693 21694 21695 21696 21697 21698 21699 21700 21701 21702 21703 21704 21705 21706 21707 21708 21709 21710 21711 21712 21713 21714 21715 21716 21717 21718 21719 21720 21721 21722 21723 21724 21725 21726 21727 21728 21729 21730 21731 21732 21733 21734 21735 21736 21737 21738 21739 21740 21741 21742 21743 21744 21745 21746 21747 21748 21749 21750 21751 | fsl_md5_digest_to_base16(digest, hex); return (0==memcmp(zHash, hex, FSL_STRLEN_MD5)) ? 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){ 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 && !fsl_after_blank_line((char const *)(z+i)); i++){} if( 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; 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; } /** Internal helper for parsing manifests. Holds a source file (memory |
︙ | ︙ | |||
21388 21389 21390 21391 21392 21393 21394 | else if(L('B') || L('C') || L('F') || L('P') || L('Q') || L('R')) return FSL_SATYPE_CHECKIN; else if(L('D') && L('T') && L('U')) return FSL_SATYPE_CONTROL; #undef L return FSL_SATYPE_ANY; } | | | | | | | | 21855 21856 21857 21858 21859 21860 21861 21862 21863 21864 21865 21866 21867 21868 21869 21870 21871 21872 21873 21874 21875 21876 21877 21878 21879 21880 21881 | else if(L('B') || L('C') || L('F') || L('P') || L('Q') || L('R')) return FSL_SATYPE_CHECKIN; 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){ 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; 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 true; } int fsl_deck_parse2(fsl_deck * const d, fsl_buffer * const src, fsl_id_t rid){ #ifdef ERROR # undef ERROR #endif #define ERROR(RC,MSG) do{ rc = (RC); zMsg = (MSG); goto bailout; } while(0) |
︙ | ︙ | |||
21433 21434 21435 21436 21437 21438 21439 | unsigned nSimpleTag = 0 /* number of T cards with "+" prefix */; /* 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. */ | | | | > > > > > > > > > > > > > > > | 21900 21901 21902 21903 21904 21905 21906 21907 21908 21909 21910 21911 21912 21913 21914 21915 21916 21917 21918 21919 21920 21921 21922 21923 21924 21925 21926 21927 21928 21929 21930 21931 21932 21933 21934 21935 | unsigned nSimpleTag = 0 /* number of T cards with "+" prefix */; /* 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. */}; 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"); } else if(rid<0){ |
︙ | ︙ | |||
21470 21471 21472 21473 21474 21475 21476 | fsl_deck_init(f, d, FSL_SATYPE_ANY); /* Verify that the first few characters of the artifact look like a control artifact. */ if( !fsl_might_be_artifact(src) ){ | < | | | 21952 21953 21954 21955 21956 21957 21958 21959 21960 21961 21962 21963 21964 21965 21966 21967 21968 21969 21970 21971 21972 21973 21974 21975 21976 21977 | fsl_deck_init(f, d, FSL_SATYPE_ANY); /* 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"); } /* Strip off the PGP signature if there is one. Example of signed manifest: https://fossil-scm.org/index.html/artifact/28987096ac */ { unsigned char const * zz = z; fsl__remove_pgp_signature(&zz, &n); z = (unsigned char *)zz; } /* Verify the Z card */ if( fsl_deck_verify_Z_card(z, n) < 0 ){ ERROR(FSL_RC_CONSISTENCY, "Z-card checksum mismatch"); } |
︙ | ︙ | |||
21558 21559 21560 21561 21562 21563 21564 | <source> is omitted to delete an attachment. <target> is the name of a wiki page, technote, or ticket to which that attachment is connected. */ case 'A':{ unsigned char * name, * src; if(1<SEEN(A)){ | | | 22039 22040 22041 22042 22043 22044 22045 22046 22047 22048 22049 22050 22051 22052 22053 | <source> is omitted to delete an attachment. <target> is the name of a wiki page, technote, or ticket to which that attachment is connected. */ case 'A':{ unsigned char * name, * src; if(1<SEEN(A)){ SYNTAX("Multiple A-cards"); } TOKEN(1); TOKEN_EXISTS("Missing filename for A-card"); name = token; if(!fsl_is_simple_pathname( (char const *)name, 0 )){ SYNTAX("Invalid filename in A-card"); } |
︙ | ︙ | |||
21708 21709 21710 21711 21712 21713 21714 | */ break; case 'w': perm = FSL_FILE_PERM_REGULAR; break; case 'x': perm = FSL_FILE_PERM_EXE; break; case 'l': perm = FSL_FILE_PERM_LINK; break; default: /*MARKER(("Unmatched perms string character: %d / %c !", (int)*perms, *perms));*/ | | < | 22189 22190 22191 22192 22193 22194 22195 22196 22197 22198 22199 22200 22201 22202 22203 | */ break; case 'w': perm = FSL_FILE_PERM_REGULAR; break; case 'x': perm = FSL_FILE_PERM_EXE; break; case 'l': perm = FSL_FILE_PERM_LINK; break; default: /*MARKER(("Unmatched perms string character: %d / %c !", (int)*perms, *perms));*/ SYNTAX("Invalid perms string character"); } TOKEN(0); if(token) priorName = (char *)token; } fsl_bytes_defossilize( (unsigned char *)name, 0 ); if(priorName) fsl_bytes_defossilize( (unsigned char *)priorName, 0 ); if(fsl_is_reserved_fn(name, -1)){ |
︙ | ︙ | |||
21879 21880 21881 21882 21883 21884 21885 | /* N <uuid> An N-line identifies the mimetype of wiki or comment text. */ case 'N':{ if(1<SEEN(N)){ | | | | 22359 22360 22361 22362 22363 22364 22365 22366 22367 22368 22369 22370 22371 22372 22373 22374 22375 22376 22377 22378 22379 22380 22381 22382 22383 22384 22385 22386 22387 22388 22389 22390 22391 | /* N <uuid> An N-line identifies the mimetype of wiki or comment text. */ case 'N':{ if(1<SEEN(N)){ SYNTAX("Multiple N-cards"); } TOKEN(0); TOKEN_EXISTS("Missing UUID on N-card"); ++stealBuf; d->N = (char *)token; break; } /* P <uuid> ... Specify one or more other artifacts which are the parents of this artifact. The first parent is the primary parent. All others are parents by merge. */ case 'P':{ if(1<SEEN(P)){ SYNTAX("More than one P-card"); } TOKEN(0); #if 0 /* The docs all claim that this card does not exist on the first manifest, but in fact it does exist but has no UUID, which is invalid per all the P-card docs. Skip this check (A) for the sake of manifest #1 and (B) because |
︙ | ︙ | |||
21955 21956 21957 21958 21959 21960 21961 | R <md5sum> Specify the MD5 checksum over the name and content of all files in the manifest. */ case 'R':{ if(1<SEEN(R)){ | | | 22435 22436 22437 22438 22439 22440 22441 22442 22443 22444 22445 22446 22447 22448 22449 | R <md5sum> Specify the MD5 checksum over the name and content of all files in the manifest. */ case 'R':{ if(1<SEEN(R)){ SYNTAX("More than one R-card"); } TOKEN(0); TOKEN_EXISTS("Missing MD5 token in R-card"); TOKEN_MD5("Malformed MD5 token in R-card"); d->R = (char *)token; ++stealBuf; d->type = FSL_SATYPE_CHECKIN; |
︙ | ︙ | |||
22058 22059 22060 22061 22062 22063 22064 | if(d->W.used){ SYNTAX("More than one W-card"); } TOKEN(0); TOKEN_EXISTS("Missing size token for W-card"); wlen = fsl_str_to_size((char const *)token); if((fsl_size_t)-1==wlen){ | | | 22538 22539 22540 22541 22542 22543 22544 22545 22546 22547 22548 22549 22550 22551 22552 | if(d->W.used){ SYNTAX("More than one W-card"); } 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"); } if( (&x.z[wlen+1]) > x.zEnd){ SYNTAX("Not enough content after W-card"); } rc = fsl_buffer_append(&d->W, x.z, wlen); if(rc) goto bailout; x.z += wlen; |
︙ | ︙ | |||
22191 22192 22193 22194 22195 22196 22197 | } default: break; } assert(!d->content.mem); if(stealBuf>0){ /* We stashed something which points to src->mem, so we need to | | < > > > > > | 22671 22672 22673 22674 22675 22676 22677 22678 22679 22680 22681 22682 22683 22684 22685 22686 22687 22688 22689 22690 22691 22692 | } default: break; } assert(!d->content.mem); if(stealBuf>0){ /* We stashed something which points to src->mem, so we need to 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; bailout: if(stealBuf>0){ |
︙ | ︙ | |||
22237 22238 22239 22240 22241 22242 22243 | if(rid<0){ return fsl_cx_err_set(f, FSL_RC_RANGE, "Invalid RID for fsl_deck_load_rid(): " "%"FSL_ID_T_PFMT, rid); } fsl_deck_clean(d); d->f = f; | | < < < < < < < < < < < < | | 22721 22722 22723 22724 22725 22726 22727 22728 22729 22730 22731 22732 22733 22734 22735 22736 22737 | if(rid<0){ return fsl_cx_err_set(f, FSL_RC_RANGE, "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){ 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)); #endif fsl_deck_init(f, d, FSL_SATYPE_ANY); |
︙ | ︙ | |||
22280 22281 22282 22283 22284 22285 22286 | rc = fsl_cx_err_set(f, FSL_RC_TYPE, "RID %"FSL_ID_T_PFMT" is of type %s, " "but the caller requested type %s.", rid, fsl_satype_cstr(d->type), fsl_satype_cstr(type)); }else if(d->B.uuid ){ | | | 22752 22753 22754 22755 22756 22757 22758 22759 22760 22761 22762 22763 22764 22765 22766 | rc = fsl_cx_err_set(f, FSL_RC_TYPE, "RID %"FSL_ID_T_PFMT" is of type %s, " "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); } } end: if(0==rc) d->rid = rid; fsl_buffer_clear(&buf); return rc; } |
︙ | ︙ | |||
22507 22508 22509 22510 22511 22512 22513 | MARKER(("Saving deck:\n%s\n", fsl_buffer_cstr(buf))); } /* Starting here, don't return, use (goto end) instead. */ f->cache.markPrivate = isPrivate; { | | | 22979 22980 22981 22982 22983 22984 22985 22986 22987 22988 22989 22990 22991 22992 22993 | MARKER(("Saving deck:\n%s\n", fsl_buffer_cstr(buf))); } /* Starting here, don't return, use (goto end) instead. */ f->cache.markPrivate = isPrivate; { rc = fsl__content_put_ex(f, buf, NULL, 0, 0U, isPrivate, &newRid); if(rc) goto end; assert(newRid>0); } /* We need d->rid for crosslinking purposes, but will unset it on error because its value will no longer be in the db after |
︙ | ︙ | |||
22529 22530 22531 22532 22533 22534 22535 | is a general pattern for manifests. */ 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){ | | | 23001 23002 23003 23004 23005 23006 23007 23008 23009 23010 23011 23012 23013 23014 23015 | is a general pattern for manifests. */ 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); if(rc) goto end; } } #endif if(FSL_SATYPE_WIKI==d->type){ /* Analog to fossil's wiki.c:wiki_put(): */ |
︙ | ︙ | |||
22557 22558 22559 22560 22561 22562 22563 | rc = fsl_cx_err_set(f, FSL_RC_NOT_FOUND, "Did not find matching RID " "for P-card[0] (%s).", (char const *)d->P.list[0]); } goto end; } | | | | | | | > > > > > > > > > > > > > | | | | | | < < < | | | | | < < | | | | | < < | < > | 23029 23030 23031 23032 23033 23034 23035 23036 23037 23038 23039 23040 23041 23042 23043 23044 23045 23046 23047 23048 23049 23050 23051 23052 23053 23054 23055 23056 23057 23058 23059 23060 23061 23062 23063 23064 23065 23066 23067 23068 23069 23070 23071 23072 23073 23074 23075 23076 23077 23078 23079 23080 23081 23082 23083 23084 23085 23086 23087 23088 23089 23090 23091 23092 23093 23094 23095 23096 23097 23098 23099 23100 23101 23102 23103 23104 23105 23106 23107 23108 23109 23110 23111 23112 23113 23114 23115 23116 23117 23118 23119 23120 23121 23122 23123 23124 23125 23126 23127 23128 23129 23130 23131 23132 23133 23134 23135 23136 23137 23138 23139 23140 23141 23142 23143 23144 23145 23146 23147 23148 23149 23150 23151 23152 23153 23154 23155 23156 23157 23158 23159 23160 23161 23162 23163 23164 23165 23166 23167 23168 23169 23170 23171 23172 23173 23174 23175 23176 23177 23178 23179 23180 23181 23182 23183 23184 23185 23186 23187 23188 23189 23190 23191 23192 23193 23194 23195 23196 23197 23198 23199 23200 23201 23202 23203 23204 23205 23206 23207 23208 23209 | rc = fsl_cx_err_set(f, FSL_RC_NOT_FOUND, "Did not find matching RID " "for P-card[0] (%s).", (char const *)d->P.list[0]); } goto end; } 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");" "INSERT OR IGNORE INTO unclustered " "VALUES(%"FSL_ID_T_PFMT");", d->rid, d->rid); if(rc){ fsl_cx_uplift_db_error(f, db); goto end; } } rc = f->cache.isCrosslinking ? 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); if(rc){ d->rid = 0 /* this blob.rid will be lost after rollback */; if(!f->error.code && db->error.code){ rc = fsl_cx_uplift_db_error(f, db); } } fsl_buffer_reuse(buf); return rc; } int fsl__crosslink_end(fsl_cx * const f, int resultCode){ int rc = 0; fsl_db * const 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."); 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, "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)){ fsl_id_t const rid = fsl_stmt_g_id(&q, 0); const char *zTagVal = fsl_stmt_g_text(&q, 1, 0); rc = fsl_crosslink_reparent(f,rid, zTagVal); if(rc) break; } 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"); 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; cType = zId[0]; ++zId; if('t'==cType){ /* FSL-MISSING: ticket_rebuild_entry(zId) */ continue; }else if('w'==cType){ /* FSL-MISSING: backlink_wiki_refresh(zId) */ continue; } } fsl_stmt_finalize(&q); rc = fsl_cx_exec(f, "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, "UPDATE time_fudge SET m1=m2-:incr " "WHERE m1>=m2 AND m1<m2+:window" ); if(rc) goto end; fsl_stmt_bind_double_name(&q, ":incr", AGE_ADJUST_INCREMENT); fsl_stmt_bind_double_name(&q, ":window", AGE_FUDGE_WINDOW); rc = fsl_cx_prepare(f, &u, "UPDATE time_fudge SET m2=" "(SELECT x.m1 FROM time_fudge AS x" " WHERE x.mid=time_fudge.cid)"); for(i=0; !rc && i<30; i++){ /* where does 30 come from? */ rc = fsl_stmt_step(&q); if(FSL_RC_STEP_DONE==rc) rc=0; else break; fsl_stmt_reset(&q); if( fsl_db_changes_recent(db)==0 ) break; rc = fsl_stmt_step(&u); if(FSL_RC_STEP_DONE==rc) rc=0; else break; fsl_stmt_reset(&u); } fsl_stmt_finalize(&q); fsl_stmt_finalize(&u); if(!rc && fsl_db_exists(db,"SELECT 1 FROM time_fudge")){ rc = fsl_cx_exec(f, "UPDATE event SET" " mtime=(SELECT m1 FROM time_fudge WHERE mid=objid)" " WHERE objid IN (SELECT mid FROM time_fudge)" " AND (mtime=omtime OR omtime IS NULL)" ); } end: if(!rc){ fsl_cx_exec(f, "DROP TABLE time_fudge"); } if(rc) fsl_cx_transaction_end(f, true); else rc = fsl_cx_transaction_end(f, false); return rc; } int fsl__crosslink_begin(fsl_cx * const f){ int rc; assert(f); assert(0==f->cache.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, "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; }else{ fsl_cx_transaction_end(f, true); } return rc; } #undef MARKER #undef AGE_FUDGE_WINDOW #undef AGE_ADJUST_INCREMENT #undef F_at /* end of file deck.c */ |
︙ | ︙ | |||
23650 23651 23652 23653 23654 23655 23656 | /** Compute the optimal longest common subsequence (LCS) using an 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( | | | 24128 24129 24130 24131 24132 24133 24134 24135 24136 24137 24138 24139 24140 24141 24142 | /** Compute the optimal longest common subsequence (LCS) using an 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 */ 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 */ ){ int mxLength = 0; /* Length of longest common subsequence */ int i, j; /* Loop counters */ |
︙ | ︙ | |||
23704 23705 23706 23707 23708 23709 23710 | way too slow for larger files. So this routine uses an O(N) heuristic approximation based on hashing that usually works about 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( | | | 24182 24183 24184 24185 24186 24187 24188 24189 24190 24191 24192 24193 24194 24195 24196 | way too slow for larger files. So this routine uses an O(N) heuristic approximation based on hashing that usually works about 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 */ 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 */ ){ int i, j, k; /* Loop counters */ int n; /* Loop limit */ |
︙ | ︙ | |||
23804 23805 23806 23807 23808 23809 23810 | *piSY = iSYb; *piEX = iEXb; *piEY = iEYb; } } | | | | | 24282 24283 24284 24285 24286 24287 24288 24289 24290 24291 24292 24293 24294 24295 24296 24297 24298 24299 24300 24301 24302 24303 24304 24305 24306 24307 24308 24309 24310 24311 24312 24313 24314 24315 24316 24317 24318 24319 24320 24321 24322 24323 24324 24325 | *piSY = iSYb; *piEX = iEXb; *piEY = iEYb; } } 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){ printf(" copy %6d delete %6d insert %6d\n", p->aEdit[i], p->aEdit[i+1], p->aEdit[i+2]); } } /** @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){ void * re = fsl_realloc(p->aEdit, nEdit*sizeof(int)); if(!re) return FSL_RC_OOM; else{ p->aEdit = (int*)re; p->nEditAlloc = nEdit; return 0; } } /** 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){ /* 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; p->aEdit[p->nEdit-2] += nDel; p->aEdit[p->nEdit-1] += nIns; |
︙ | ︙ | |||
23877 23878 23879 23880 23881 23882 23883 | The algorithm is to find a block of common text near the middle of 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. */ | | | 24355 24356 24357 24358 24359 24360 24361 24362 24363 24364 24365 24366 24367 24368 24369 | The algorithm is to find a block of common text near the middle of 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){ int iSX, iEX, iSY, iEY; int rc = 0; if( iE1<=iS1 ){ /* The first segment is empty */ if( iE2>iS2 ){ rc = appendTriple(p, 0, 0, iE2-iS2); } |
︙ | ︙ | |||
23912 23913 23914 23915 23916 23917 23918 | /* The two segments have nothing in common. Delete the first then insert the second. */ rc = appendTriple(p, 0, iE1-iS1, iE2-iS2); } return rc; } | | | 24390 24391 24392 24393 24394 24395 24396 24397 24398 24399 24400 24401 24402 24403 24404 | /* The two segments have nothing in common. Delete the first then insert the second. */ rc = appendTriple(p, 0, iE1-iS1, iE2-iS2); } return rc; } 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; while( iE1>0 && iE2>0 && p->cmpLine(&p->aFrom[iE1-1], &p->aTo[iE2-1])==0 ){ iE1--; |
︙ | ︙ | |||
23982 23983 23984 23985 23986 23987 23988 | + return x*20; + return x*20; +} +} + int func3(int x){ int func3(int x){ return x/5; return x/5; } } */ | | | 24460 24461 24462 24463 24464 24465 24466 24467 24468 24469 24470 24471 24472 24473 24474 | + return x*20; + return x*20; +} +} + int func3(int x){ int func3(int x){ return x/5; return x/5; } } */ 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; //fsl__dump_triples(p, __FILE__, __LINE__); lnFrom = lnTo = 0; |
︙ | ︙ | |||
24061 24062 24063 24064 24065 24066 24067 | /* Given a raw diff p[] in which the p->aEdit[] array has been filled in, compute a context diff into pOut. */ static int contextDiff( | | | 24539 24540 24541 24542 24543 24544 24545 24546 24547 24548 24549 24550 24551 24552 24553 | /* 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 */ 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 */ fsl_dline *B; /* Right side of the diff */ int a = 0; /* Index of next line in A[] */ |
︙ | ︙ | |||
24943 24944 24945 24946 24947 24948 24949 | } /* Given a diff context in which the aEdit[] array has been filled in, compute a side-by-side diff into pOut. */ static int sbsDiff( | | | 25421 25422 25423 25424 25425 25426 25427 25428 25429 25430 25431 25432 25433 25434 25435 | } /* 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 */ 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 */ fsl_dline *B; /* Right side of the diff */ int rc = 0; |
︙ | ︙ | |||
25270 25271 25272 25273 25274 25275 25276 | /* ReCompiled *pRe, */ /* Only output changes where this Regexp matches */ short contextLines, short sbsWidth, int diffFlags_, /* FSL_DIFF_* flags */ int ** outRaw ){ int rc; | | | | | | 25748 25749 25750 25751 25752 25753 25754 25755 25756 25757 25758 25759 25760 25761 25762 25763 25764 25765 25766 25767 25768 25769 25770 25771 | /* ReCompiled *pRe, */ /* Only output changes where this Regexp matches */ short contextLines, short sbsWidth, int diffFlags_, /* FSL_DIFF_* flags */ int ** outRaw ){ int rc; 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; } 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; if( diffFlags & DIFF_INVERT ){ fsl_buffer const *pTemp = pA; |
︙ | ︙ | |||
25350 25351 25352 25353 25354 25355 25356 | end: fsl_free(c.aFrom); fsl_free(c.aTo); fsl_free(c.aEdit); return rc; } | | | 25828 25829 25830 25831 25832 25833 25834 25835 25836 25837 25838 25839 25840 25841 25842 | end: fsl_free(c.aFrom); fsl_free(c.aTo); fsl_free(c.aEdit); return rc; } 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, fsl_output_f out, void * outState, short contextLines, |
︙ | ︙ | |||
25459 25460 25461 25462 25463 25464 25465 | } while(0) 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; | | | | | 25937 25938 25939 25940 25941 25942 25943 25944 25945 25946 25947 25948 25949 25950 25951 25952 25953 25954 25955 25956 25957 25958 25959 | } while(0) 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; 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; } /** 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. It includes the last line in the count even if it lacks the \n |
︙ | ︙ | |||
25533 25534 25535 25536 25537 25538 25539 | } assert( a ); i = 0; do{ zNL = strchr(z,'\n'); if( zNL==0 ) zNL = z+n; nn = (uint32_t)(zNL - z); | | | 26011 26012 26013 26014 26015 26016 26017 26018 26019 26020 26021 26022 26023 26024 26025 | } assert( a ); i = 0; do{ zNL = strchr(z,'\n'); if( zNL==0 ) zNL = z+n; nn = (uint32_t)(zNL - z); if( nn>FSL__LINE_LENGTH_MASK ){ fsl_free(a); *pOut = 0; *pnLine = 0; return FSL_RC_DIFF_BINARY; } a[i].z = z; k = nn; |
︙ | ︙ | |||
25574 25575 25576 25577 25578 25579 25580 | h = (h^m)*9000000000000000041LL; } m = 0; memcpy(&m, z+x, k-k2); h ^= m; } a[i].indent = s; | | | | 26052 26053 26054 26055 26056 26057 26058 26059 26060 26061 26062 26063 26064 26065 26066 26067 26068 26069 26070 26071 26072 26073 26074 26075 26076 26077 26078 26079 26080 26081 26082 26083 | h = (h^m)*9000000000000000041LL; } m = 0; memcpy(&m, z+x, k-k2); h ^= m; } a[i].indent = s; a[i].h = h = ((h%281474976710597LL)<<FSL__LINE_LENGTH_MASK_SZ) | (k-s); h2 = h % nLine; a[i].iNext = a[h2].iHash; a[h2].iHash = i+1; z += nn+1; n -= nn+1; i++; }while( zNL[0]!='\0' && zNL[1]!='\0' ); assert( i==nLine ); *pnLine = nLine; *pOut = a; return 0; } int fsl_dline_cmp(const fsl_dline * const pA, const fsl_dline * const pB){ if( pA->h!=pB->h ) return 1; 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; if( pA->h==pB->h ){ while( a<pA->n || b<pB->n ){ |
︙ | ︙ | |||
26228 26229 26230 26231 26232 26233 26234 | } /* ** Format a diff using a fsl_diff_builder object */ static int fdb__format( | | | 26706 26707 26708 26709 26710 26711 26712 26713 26714 26715 26716 26717 26718 26719 26720 | } /* ** Format a diff using a fsl_diff_builder object */ static int fdb__format( 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; const int *R; /* Array of COPY/DELETE/INSERT triples */ unsigned int a; /* Index of next line in A[] */ |
︙ | ︙ | |||
26520 26521 26522 26523 26524 26525 26526 | */ static int fsl_diff2_text_impl(fsl_buffer const *pA, fsl_buffer const *pB, fsl_diff_builder * const pBuilder, fsl_diff_opt const * const opt_, int ** outRaw){ int rc = 0; | | | 26998 26999 27000 27001 27002 27003 27004 27005 27006 27007 27008 27009 27010 27011 27012 | */ static int fsl_diff2_text_impl(fsl_buffer const *pA, 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; 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; blob_to_utf8_no_bom(pA, 0); |
︙ | ︙ | |||
28986 28987 28988 28989 28990 28991 28992 | gotone: if(outLen) *outLen = buf->used; if(pOut) *pOut = (char const *)buf->mem; return 0; } | | | 29464 29465 29466 29467 29468 29469 29470 29471 29472 29473 29474 29475 29476 29477 29478 | gotone: if(outLen) *outLen = buf->used; if(pOut) *pOut = (char const *)buf->mem; return 0; } char * fsl__file_without_drive_letter(char * zIn){ #ifdef _WIN32 if( zIn && fsl_isalpha(zIn[0]) && zIn[1]==':' ) zIn += 2; #endif return zIn; } int fsl_dir_is_empty(const char *path){ |
︙ | ︙ | |||
29655 29656 29657 29658 29659 29660 29661 | if(!db) return false; return 1==fsl_db_g_int32(db, 0, "SELECT 1 FROM event " "WHERE objid=%" FSL_ID_T_PFMT " AND type='ci'", rid); } | | | 30133 30134 30135 30136 30137 30138 30139 30140 30141 30142 30143 30144 30145 30146 30147 | if(!db) return false; return 1==fsl_db_g_int32(db, 0, "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){ 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; bool isLeaf; fsl_cx_err_reset(f); |
︙ | ︙ | |||
29686 29687 29688 29689 29690 29691 29692 | if(rc) rc = fsl_cx_uplift_db_error2(f, db, rc); } } return rc; } } | | | 30164 30165 30166 30167 30168 30169 30170 30171 30172 30173 30174 30175 30176 30177 30178 | if(rc) rc = fsl_cx_uplift_db_error2(f, db, rc); } } return rc; } } int fsl__repo_leafeventually_check( fsl_cx * const 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 { fsl_stmt * parentsOf = NULL; int rc = fsl_db_prepare_cached(db, &parentsOf, |
︙ | ︙ | |||
29712 29713 29714 29715 29716 29717 29718 | } fsl_stmt_cached_yield(parentsOf); return rc; } } | | | | | 30190 30191 30192 30193 30194 30195 30196 30197 30198 30199 30200 30201 30202 30203 30204 30205 30206 30207 30208 30209 30210 30211 | } fsl_stmt_cached_yield(parentsOf); return rc; } } int fsl__repo_leafdo_pending_checks(fsl_cx * const 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); } fsl_id_bag_reset(&f->cache.leafCheck); return rc; } int fsl_leaves_compute(fsl_cx * f, fsl_id_t vid, fsl_leaves_compute_e closeMode){ fsl_db * const db = fsl_needs_repo(f); if(!db) return FSL_RC_NOT_A_REPO; |
︙ | ︙ | |||
29756 29757 29758 29759 29760 29761 29762 | fsl_stmt isBr = fsl_stmt_empty; /* Query to check to see if a check-in starts a new branch */ fsl_stmt ins = fsl_stmt_empty; /* INSERT statement for a new record */ /* Initialize the bags. */ rc = fsl_id_bag_insert(&pending, vid); if(rc) goto cleanup; | | > | | | < | < | 30234 30235 30236 30237 30238 30239 30240 30241 30242 30243 30244 30245 30246 30247 30248 30249 30250 30251 30252 30253 30254 30255 30256 30257 30258 30259 30260 30261 30262 30263 30264 30265 30266 30267 30268 30269 30270 30271 30272 30273 30274 30275 30276 30277 30278 | fsl_stmt isBr = fsl_stmt_empty; /* Query to check to see if a check-in starts a new branch */ fsl_stmt ins = fsl_stmt_empty; /* INSERT statement for a new record */ /* 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). ** ** If a child is a merge of a fork within the same branch, it is ** returned. Only merge children in different branches are excluded. */ rc = fsl_db_prepare(db, &q1, "SELECT cid FROM plink" " WHERE pid=?1" " AND (isprim" " OR coalesce((SELECT value FROM tagxref" " WHERE tagid=%d AND rid=plink.pid), 'trunk')" /* FIXME? main-branch? */ "=coalesce((SELECT value FROM tagxref" " WHERE tagid=%d AND rid=plink.cid), 'trunk'))" /* 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. */ 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.*/ rc = fsl_db_prepare(db, &ins, "INSERT OR IGNORE INTO leaves VALUES(?1)"); if(rc) goto cleanup; while( fsl_id_bag_count(&pending) ){ fsl_id_t const rid = fsl_id_bag_first(&pending); unsigned cnt = 0; |
︙ | ︙ | |||
30144 30145 30146 30147 30148 30149 30150 | #undef US3B #undef US4A #undef US4B #undef US4C #undef US0A int fsl_looks_like_utf8(fsl_buffer const * const b, int stopFlags){ | | | 30621 30622 30623 30624 30625 30626 30627 30628 30629 30630 30631 30632 30633 30634 30635 | #undef US3B #undef US4A #undef US4B #undef US4C #undef US0A int fsl_looks_like_utf8(fsl_buffer const * const b, int stopFlags){ fsl_size_t n = 0; 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; if( c==0 ){ flags |= FSL_LOOKSLIKE_NUL; /* NUL character in a file -> binary */ |
︙ | ︙ | |||
30172 30173 30174 30175 30176 30177 30178 | }else if( c=='\n' ){ flags |= FSL_LOOKSLIKE_LF; if( c2=='\r' ){ flags |= (FSL_LOOKSLIKE_CR | FSL_LOOKSLIKE_CRLF); /* Found LF preceded by CR */ }else{ flags |= FSL_LOOKSLIKE_LONE_LF; } | | | | 30649 30650 30651 30652 30653 30654 30655 30656 30657 30658 30659 30660 30661 30662 30663 30664 30665 30666 30667 30668 30669 30670 30671 30672 30673 30674 30675 30676 30677 | }else if( c=='\n' ){ flags |= FSL_LOOKSLIKE_LF; 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 ){ flags |= FSL_LOOKSLIKE_LONG; /* Very long line -> binary */ } j = 0; }else if( c=='\r' ){ flags |= FSL_LOOKSLIKE_CR; if( n<=1 || z[1]!='\n' ){ flags |= FSL_LOOKSLIKE_LONE_CR; /* Not enough chars or next char not LF */ } } } if( n ){ flags |= FSL_LOOKSLIKE_SHORT; /* The whole blob was not examined */ } if( j>FSL__LINE_LENGTH_MASK ){ flags |= FSL_LOOKSLIKE_LONG; /* Very long line -> binary */ } return flags; } unsigned char const *fsl_utf8_bom(unsigned int *pnByte){ static const unsigned char bom[] = { |
︙ | ︙ | |||
30974 30975 30976 30977 30978 30979 30980 | ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is ** an array of integer triples. Within each triple, the first integer ** 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. */ | | | | 31451 31452 31453 31454 31455 31456 31457 31458 31459 31460 31461 31462 31463 31464 31465 31466 | ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is ** an array of integer triples. Within each triple, the first integer ** 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; assert(aC1 && aC2); /* Rewind inputs: Needed to reconstruct output */ fsl_buffer_rewind(pV1); fsl_buffer_rewind(pV2); fsl_buffer_rewind(pPivot); |
︙ | ︙ | |||
31405 31406 31407 31408 31409 31410 31411 | */ /************************************************************************ This file houses the priority queue class. */ #include <assert.h> | | | | | | | | | | 31882 31883 31884 31885 31886 31887 31888 31889 31890 31891 31892 31893 31894 31895 31896 31897 31898 31899 31900 31901 31902 31903 31904 31905 31906 31907 31908 31909 31910 31911 31912 31913 31914 31915 31916 31917 31918 31919 31920 31921 31922 31923 31924 31925 31926 31927 31928 31929 31930 31931 31932 31933 31934 31935 31936 31937 31938 31939 | */ /************************************************************************ This file houses the priority queue class. */ #include <assert.h> void fsl__pq_clear(fsl__pq *p){ fsl_free(p->list); *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); if(!re) return FSL_RC_OOM; else{ 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, 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); if(rc) return rc; } for(i=0; i<p->used; ++i){ if( p->list[i].priority>v ){ for(j=p->used; j>i; --j){ p->list[j] = p->list[j-1]; } break; } } p->list[i].id = e; p->list[i].data = pData; p->list[i].priority = v; ++p->used; return 0; } 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; } e = p->list[0].id; if( pp ) *pp = p->list[0].data; |
︙ | ︙ | |||
31533 31534 31535 31536 31537 31538 31539 | TODO: figure out if this needs to be in the public API and, if it does, change its signature to: int fsl_branch_of_rid(fsl_cx *f, fsl_int_t rid, char **zOut ) So that we can distinguish "not found" from OOM errors. */ | | | 32010 32011 32012 32013 32014 32015 32016 32017 32018 32019 32020 32021 32022 32023 32024 | TODO: figure out if this needs to be in the public API and, if it does, change its signature to: 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){ char *zBr = 0; fsl_db * const db = fsl_cx_db_repo(f); fsl_stmt * st = 0; int rc; assert(db); rc = fsl_db_prepare_cached(db, &st, "SELECT value FROM tagxref " |
︙ | ︙ | |||
31555 31556 31557 31558 31559 31560 31561 31562 31563 31564 31565 31566 31567 31568 | zBr = fsl_strdup(fsl_stmt_g_text(st,0,0)); if(!zBr) rc = FSL_RC_OOM; } end: fsl_stmt_cached_yield(st); if( !rc && zBr==0 ){ zBr = fsl_config_get_text(f, FSL_CONFDB_REPO, "main-branch", 0); } return zBr; } /** morewt ==> most recent event with tag | > | 32032 32033 32034 32035 32036 32037 32038 32039 32040 32041 32042 32043 32044 32045 32046 | zBr = fsl_strdup(fsl_stmt_g_text(st,0,0)); if(!zBr) rc = FSL_RC_OOM; } 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; } /** morewt ==> most recent event with tag |
︙ | ︙ | |||
31682 31683 31684 31685 31686 31687 31688 | fsl_free(zBr); }else{ goto oom; } } return ans; oom: | > | > > | 32160 32161 32162 32163 32164 32165 32166 32167 32168 32169 32170 32171 32172 32173 32174 32175 32176 32177 | fsl_free(zBr); }else{ 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. */ return -1; } int fsl_sym_to_rid( fsl_cx * const f, char const * sym, fsl_satype_e type, fsl_id_t * rv ){ fsl_id_t rid = 0; fsl_id_t vid; |
︙ | ︙ | |||
31905 31906 31907 31908 31909 31910 31911 | sym, fsl_satype_event_cstr(type) ); } assert(0==rc); *rv = rid; return rc; } | | | | | | 32386 32387 32388 32389 32390 32391 32392 32393 32394 32395 32396 32397 32398 32399 32400 32401 32402 32403 32404 32405 32406 32407 32408 32409 32410 32411 32412 | sym, fsl_satype_event_cstr(type) ); } 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 ){ if(!f) return -1; else if(!fsl_is_uuid(uuid)){ fsl_cx_err_set(f, FSL_RC_MISUSE, "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, (FSL_PHANTOM_PRIVATE==mode), &rv)){ assert(f->error.code); rv = -3; } return rv; } |
︙ | ︙ | |||
32053 32054 32055 32056 32057 32058 32059 | } return rid; } } fsl_id_t fsl_repo_filename_fnid( fsl_cx * f, char const * fn ){ fsl_id_t rv = 0; | | | | 32534 32535 32536 32537 32538 32539 32540 32541 32542 32543 32544 32545 32546 32547 32548 32549 32550 32551 32552 | } return rid; } } 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); 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 ){ fsl_db * db = fsl_cx_db_repo(f); fsl_id_t fnid = 0; fsl_stmt * qSel = NULL; int rc; assert(f); assert(db); assert(rv); |
︙ | ︙ | |||
32150 32151 32152 32153 32154 32155 32156 | } return rc; } } | | | | | 32631 32632 32633 32634 32635 32636 32637 32638 32639 32640 32641 32642 32643 32644 32645 32646 32647 32648 32649 32650 32651 32652 32653 32654 32655 32656 32657 32658 32659 32660 32661 32662 32663 32664 32665 32666 32667 32668 | } return rc; } } int fsl__repo_verify_before_commit( fsl_cx * const 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 the commit (that's where v1 calls the hook). If we eventually add commit hooks, this is the place to do it. */ } assert( fsl_cx_db_repo(f)->beginCount > 0 ); return rid>0 ? fsl_id_bag_insert(&f->cache.toVerify, rid) : FSL_RC_RANGE; } void fsl_repo_verify_cancel( fsl_cx * const 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)){ 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); }else{ fsl_stmt * st = NULL; |
︙ | ︙ | |||
32329 32330 32331 32332 32333 32334 32335 | fsl_free(uuid); fsl_buffer_clear(&hash); fsl_buffer_clear(&content); return rc; } | | | | 32810 32811 32812 32813 32814 32815 32816 32817 32818 32819 32820 32821 32822 32823 32824 32825 32826 32827 32828 32829 32830 32831 32832 32833 32834 32835 32836 32837 32838 32839 32840 32841 32842 32843 | fsl_free(uuid); fsl_buffer_clear(&hash); fsl_buffer_clear(&content); return rc; } int fsl__repo_verify_at_commit( fsl_cx * const 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; rid = fsl_id_bag_first(bag); if(f->cxConfig.traceSql){ fsl_db_exec(f->dbMain, "SELECT 'Starting verify-at-commit.'"); } while( !rc && rid>0 ){ rc = fsl_repo_verify_rid(f, rid); if(!rc) rid = fsl_id_bag_next(bag, rid); } 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()", rc, fsl_rc_cstr(rc)); } return rc; } static int fsl_repo_create_default_users(fsl_db * db, char addOnlyUser, |
︙ | ︙ | |||
32449 32450 32451 32452 32453 32454 32455 | } rc = fsl_cx_attach_role(f, opt->filename, FSL_DBROLE_REPO); if(rc){ goto end2; } db = fsl_cx_db(f); if(!f->repo.user){ | | | 32930 32931 32932 32933 32934 32935 32936 32937 32938 32939 32940 32941 32942 32943 32944 | } rc = fsl_cx_attach_role(f, opt->filename, FSL_DBROLE_REPO); if(rc){ goto end2; } db = fsl_cx_db(f); if(!f->repo.user){ f->repo.user = fsl_user_name_guess() /* Ignore OOM error here - we'll use 'root' by default (but if we're really OOM here then the next op will fail). */; } userName = opt->username; |
︙ | ︙ | |||
32573 32574 32575 32576 32577 32578 32579 | "cannot ATTACH database within transaction" and installing the initial schemas outside a transaction is horribly slow. */ if( opt->configRepo && *opt->configRepo ){ bool inTrans2 = false; | | | 33054 33055 33056 33057 33058 33059 33060 33061 33062 33063 33064 33065 33066 33067 33068 | "cannot ATTACH database within transaction" 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 * inopDb = inopConfig ? fsl_db_setting_inop_rhs() : NULL; if(!inopConfig || !inopDb){ fsl_free(inopConfig); rc = FSL_RC_OOM; goto end2; } rc = fsl_db_attach(db, opt->configRepo, "settingSrc"); |
︙ | ︙ | |||
32840 32841 32842 32843 32844 32845 32846 | ; char const * zRole = fsl_db_role_label(roleId); assert(f->dbMain); return sqlite3_db_readonly(f->dbMain->dbh, zRole) ? 1 : 0; } } | | < > | | | 33321 33322 33323 33324 33325 33326 33327 33328 33329 33330 33331 33332 33333 33334 33335 33336 33337 33338 33339 33340 33341 33342 33343 33344 33345 33346 33347 33348 33349 33350 33351 33352 33353 33354 33355 33356 33357 33358 33359 33360 33361 33362 33363 | ; char const * zRole = fsl_db_role_label(roleId); assert(f->dbMain); return sqlite3_db_readonly(f->dbMain->dbh, zRole) ? 1 : 0; } } int fsl__repo_record_filename(fsl_cx * const f){ 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); if(rc){ fsl_cx_err_set(f, rc, "Error %s canonicalizing filename: %s", zName); goto end; } /* If global config is open, write the repo db's name to it. */ dbConf = fsl_cx_db_config(f); if(dbConf){ int const dbRole = (f->dbMain==&f->config.db) ? 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)); if(rc) goto end; } dbC = fsl_cx_db_ckout(f); if(dbC && (zCDir=f->ckout.dir)){ /* If we have a checkout, update its repo's list of checkouts... */ /* Assumption: if we have an opened checkout, dbR is ATTACHed with |
︙ | ︙ | |||
32916 32917 32918 32919 32920 32921 32922 | } } end: if(rc && !f->error.code && f->dbMain->error.code){ fsl_cx_uplift_db_error(f, f->dbMain); } | | | 33397 33398 33399 33400 33401 33402 33403 33404 33405 33406 33407 33408 33409 33410 33411 | } } end: if(rc && !f->error.code && f->dbMain->error.code){ fsl_cx_uplift_db_error(f, f->dbMain); } fsl__cx_scratchpad_yield(f, full); return rc; } char fsl_rid_is_a_checkin(fsl_cx * f, fsl_id_t rid){ fsl_db * db = f ? fsl_cx_db_repo(f) : NULL; if(!db || (rid<0)) return 0; |
︙ | ︙ | |||
33023 33024 33025 33026 33027 33028 33029 | if(FSL_RC_BREAK==rc){ rc = 0; break; } } }/* for-each-F-card loop */ end: | | | | 33504 33505 33506 33507 33508 33509 33510 33511 33512 33513 33514 33515 33516 33517 33518 33519 33520 33521 33522 33523 33524 33525 33526 33527 33528 33529 33530 33531 33532 33533 33534 33535 33536 33537 33538 | if(FSL_RC_BREAK==rc){ rc = 0; break; } } }/* for-each-F-card loop */ end: fsl__cx_content_buffer_yield(f); fsl_deck_finalize(&mf); return rc; } } int fsl_repo_import_blob( fsl_cx * f, fsl_input_f in, void * inState, fsl_id_t * rid, fsl_uuid_str * uuid ){ fsl_db * db = f ? fsl_needs_repo(f) : NULL; if(!f || !in) return FSL_RC_MISUSE; else if(!db) return FSL_RC_NOT_A_REPO; else{ int rc; fsl_buffer buf = fsl_buffer_empty; rc = fsl_buffer_fill_from(&buf, in, inState); 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); if(!rc){ if(rid) *rid = theRid; if(uuid){ *uuid = fsl_rid_to_uuid(f, theRid); if(!uuid) rc = FSL_RC_OOM; } } |
︙ | ︙ | |||
33082 33083 33084 33085 33086 33087 33088 | int fsl_repo_blob_lookup( fsl_cx * f, fsl_buffer const * src, fsl_id_t * ridOut, fsl_uuid_str * hashOut ){ 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; | | | 33563 33564 33565 33566 33567 33568 33569 33570 33571 33572 33573 33574 33575 33576 33577 | int fsl_repo_blob_lookup( fsl_cx * f, fsl_buffer const * src, fsl_id_t * ridOut, fsl_uuid_str * hashOut ){ 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); /* 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. rc = 0; } |
︙ | ︙ | |||
33120 33121 33122 33123 33124 33125 33126 | if(!rc && ridOut){ *ridOut = rid; } if(hash == &hash_){ fsl_buffer_clear(hash); }else{ assert(!hash_.mem); | | | > | | 33601 33602 33603 33604 33605 33606 33607 33608 33609 33610 33611 33612 33613 33614 33615 33616 33617 33618 33619 33620 33621 33622 33623 33624 33625 | if(!rc && ridOut){ *ridOut = rid; } if(hash == &hash_){ fsl_buffer_clear(hash); }else{ assert(!hash_.mem); fsl__cx_scratchpad_yield(f, hash); } return rc; } int fsl__repo_fingerprint_search( fsl_cx * const 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_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 * db.c: * |
︙ | ︙ | |||
33193 33194 33195 33196 33197 33198 33199 | rc = FSL_RC_NOT_FOUND; break; default: rc = fsl_cx_uplift_db_error2(f, db, rc); break; } end: | | | 33675 33676 33677 33678 33679 33680 33681 33682 33683 33684 33685 33686 33687 33688 33689 | rc = FSL_RC_NOT_FOUND; break; default: rc = fsl_cx_uplift_db_error2(f, db, rc); break; } end: fsl__cx_scratchpad_yield(f, sql); fsl_stmt_finalize(&q); return rc; } int fsl_repo_manifest_write(fsl_cx *f, fsl_id_t manifestRid, fsl_buffer * const pManifest, |
︙ | ︙ | |||
33231 33232 33233 33234 33235 33236 33237 | if(pManifest){ fsl_buffer_reuse(pManifest); rc = fsl_content_get(f, manifestRid, pManifest); if(rc) goto end; } if(pHash){ if(f->ckout.rid!=manifestRid){ | | | 33713 33714 33715 33716 33717 33718 33719 33720 33721 33722 33723 33724 33725 33726 33727 | if(pManifest){ fsl_buffer_reuse(pManifest); rc = fsl_content_get(f, manifestRid, pManifest); if(rc) goto end; } if(pHash){ if(f->ckout.rid!=manifestRid){ 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; } assert(ridHash); |
︙ | ︙ | |||
33273 33274 33275 33276 33277 33278 33279 | rc = fsl_buffer_appendf(pTags, "tag %s\n", zName); if(rc) break; } fsl_stmt_finalize(&q); } end: if(bHash){ | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > > > > > > > | > > > > > > > > > > | < > > > > > > > > > | > > > > > > > > | | < > > > | < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 33755 33756 33757 33758 33759 33760 33761 33762 33763 33764 33765 33766 33767 33768 33769 33770 33771 33772 33773 33774 33775 33776 33777 33778 33779 33780 33781 33782 33783 33784 33785 33786 33787 33788 33789 33790 33791 33792 33793 33794 33795 33796 33797 33798 33799 33800 33801 33802 33803 33804 33805 33806 33807 33808 33809 33810 33811 33812 33813 33814 33815 33816 33817 33818 33819 33820 33821 33822 33823 33824 33825 33826 33827 33828 33829 33830 33831 33832 33833 33834 33835 33836 33837 33838 33839 33840 33841 33842 33843 33844 33845 33846 33847 33848 33849 33850 33851 33852 33853 33854 33855 33856 33857 33858 33859 33860 33861 33862 33863 33864 33865 33866 33867 33868 33869 33870 33871 33872 33873 33874 33875 33876 33877 33878 33879 33880 33881 33882 33883 33884 33885 33886 33887 33888 33889 33890 33891 33892 33893 33894 33895 33896 33897 33898 33899 33900 33901 33902 33903 33904 33905 33906 33907 33908 33909 33910 33911 33912 33913 33914 33915 33916 33917 33918 33919 33920 33921 33922 33923 33924 33925 33926 33927 33928 33929 33930 33931 33932 33933 33934 33935 33936 33937 33938 33939 33940 33941 33942 33943 33944 33945 33946 33947 33948 33949 33950 33951 33952 33953 33954 33955 33956 33957 33958 33959 33960 33961 33962 33963 33964 33965 33966 33967 33968 33969 33970 33971 33972 33973 33974 33975 33976 33977 33978 33979 33980 33981 33982 33983 33984 33985 33986 33987 33988 33989 33990 33991 33992 33993 33994 33995 33996 33997 33998 33999 34000 34001 34002 34003 34004 34005 34006 34007 34008 34009 34010 34011 34012 34013 34014 34015 34016 34017 34018 34019 34020 34021 34022 34023 34024 34025 34026 34027 34028 34029 34030 34031 34032 34033 34034 34035 34036 34037 34038 34039 34040 34041 34042 34043 34044 34045 34046 34047 34048 34049 34050 34051 34052 34053 34054 34055 34056 34057 34058 34059 34060 34061 34062 34063 34064 34065 34066 34067 34068 34069 34070 34071 34072 34073 34074 34075 34076 34077 34078 34079 34080 34081 34082 34083 34084 34085 34086 34087 34088 34089 34090 34091 34092 34093 34094 34095 34096 34097 34098 34099 34100 34101 34102 34103 34104 34105 34106 34107 34108 34109 34110 34111 34112 34113 34114 34115 34116 34117 34118 34119 34120 34121 34122 34123 34124 34125 34126 34127 34128 34129 34130 34131 34132 34133 34134 34135 34136 34137 34138 34139 34140 34141 34142 34143 34144 34145 34146 34147 34148 34149 34150 34151 34152 34153 34154 34155 34156 34157 34158 34159 34160 34161 34162 34163 34164 34165 34166 34167 34168 34169 34170 34171 34172 34173 34174 34175 34176 34177 34178 34179 34180 34181 34182 34183 34184 34185 34186 34187 34188 34189 34190 34191 34192 34193 34194 34195 34196 34197 34198 34199 34200 34201 34202 34203 34204 34205 34206 34207 34208 34209 34210 34211 34212 34213 34214 34215 34216 34217 34218 34219 34220 34221 34222 34223 34224 34225 34226 34227 34228 34229 34230 34231 34232 34233 34234 34235 34236 34237 34238 34239 34240 34241 34242 34243 34244 34245 34246 34247 34248 34249 34250 34251 34252 34253 34254 34255 34256 34257 34258 34259 34260 34261 34262 34263 34264 34265 34266 34267 34268 34269 34270 34271 34272 34273 34274 34275 34276 34277 34278 34279 34280 34281 34282 34283 34284 34285 34286 34287 34288 34289 34290 34291 34292 34293 34294 34295 34296 34297 34298 34299 34300 34301 34302 34303 34304 34305 34306 34307 34308 34309 34310 34311 34312 34313 34314 34315 34316 34317 34318 34319 | rc = fsl_buffer_appendf(pTags, "tag %s\n", zName); if(rc) break; } 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(i<nChild){ rc = INTCHECK fsl__rebuild_step(frs, cid, sz, &next); assert(!next.mem); }else{ /* Tail recursion */ rid = cid; blobSize = sz; fsl_buffer_clear(content); *content = next/*transfer ownership*/; } if(0==rc) continue; outro: assert(0!=rc); fsl_stmt_reset(&frs->qChild); 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 #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: */ /* Copyright 2013-2021 Stephan Beal (https://wanderinghorse.net). |
︙ | ︙ | |||
33409 33410 33411 33412 33413 33414 33415 | case FSL_SATYPE_TECHNOTE: return 'e'; default: assert(!"Internal misuse of fsl_satype_letter()"); return 0; } } | | | | 34419 34420 34421 34422 34423 34424 34425 34426 34427 34428 34429 34430 34431 34432 34433 34434 34435 34436 34437 34438 34439 34440 34441 34442 34443 34444 34445 34446 | case FSL_SATYPE_TECHNOTE: return 'e'; default: assert(!"Internal misuse of fsl_satype_letter()"); return 0; } } 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 /* See MARKER() call in the #else block */ assert(*zType); 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."); 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 ); if(rc){ // For reasons i don't understand, this query fails with "SQL logic error" |
︙ | ︙ | |||
36007 36008 36009 36010 36011 36012 36013 | * The Elm Mail System - $Revision: 1.3 $ $State: Exp $ * * Public-domain relatively quick-and-dirty implemenation of * ANSI library routine for System V Unix systems. * * Arnold Robbins * | | | | 37017 37018 37019 37020 37021 37022 37023 37024 37025 37026 37027 37028 37029 37030 37031 37032 37033 37034 37035 37036 37037 37038 37039 | * The Elm Mail System - $Revision: 1.3 $ $State: Exp $ * * 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 * Test ANSI_C, not __STDC__ (which is not set on e.g. AIX). * From: decwrl!uunet.UU.NET!fin!chip (Chip Salzenberg) |
︙ | ︙ | |||
36062 36063 36064 36065 36066 36067 36068 | * Revision 5.2 1993/04/16 04:29:34 syd * attempt to bsdize a bit strftime * 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. | | | 37072 37073 37074 37075 37076 37077 37078 37079 37080 37081 37082 37083 37084 37085 37086 | * Revision 5.2 1993/04/16 04:29:34 syd * attempt to bsdize a bit strftime * 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 * ANSI library routine for System V Unix systems. * |
︙ | ︙ | |||
36109 36110 36111 36112 36113 36114 36115 | * And made the following changes: * * 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. * | | | 37119 37120 37121 37122 37123 37124 37125 37126 37127 37128 37129 37130 37131 37132 37133 | * And made the following changes: * * 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 <time.h> /* #include <sys/time.h> */ #include <string.h> /* strchr() and friends */ #include <stdio.h> /* snprintf() */ #include <ctype.h> /* toupper(), islower() */ |
︙ | ︙ | |||
36171 36172 36173 36174 36175 36176 36177 | #define range(low, item, hi) maximum(low, minimum(item, hi)) /* minimum --- return minimum of two numbers */ static inline int minimum(int a, int b) { | | | | | | | | | > | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < < < < < | | < | | | | | | | < < < | | < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 37181 37182 37183 37184 37185 37186 37187 37188 37189 37190 37191 37192 37193 37194 37195 37196 37197 37198 37199 37200 37201 37202 37203 37204 37205 37206 37207 37208 37209 37210 37211 37212 37213 37214 37215 37216 37217 37218 37219 37220 37221 37222 37223 37224 37225 37226 37227 37228 37229 37230 37231 37232 37233 37234 37235 37236 37237 37238 37239 37240 37241 37242 37243 37244 37245 37246 37247 37248 37249 37250 37251 37252 37253 37254 37255 37256 37257 37258 37259 37260 37261 37262 37263 37264 37265 37266 37267 37268 37269 37270 37271 37272 37273 37274 37275 37276 37277 37278 37279 37280 37281 37282 37283 37284 37285 37286 37287 37288 37289 37290 37291 37292 37293 37294 37295 37296 37297 37298 37299 37300 37301 37302 37303 37304 37305 37306 37307 37308 37309 37310 37311 37312 37313 37314 37315 37316 37317 37318 37319 37320 37321 37322 37323 37324 37325 37326 37327 37328 37329 37330 37331 37332 37333 37334 37335 37336 37337 37338 37339 37340 37341 37342 37343 37344 37345 37346 37347 37348 37349 37350 37351 37352 37353 37354 37355 37356 37357 37358 37359 37360 37361 37362 37363 37364 37365 37366 37367 37368 37369 37370 37371 37372 37373 37374 37375 37376 37377 37378 37379 37380 37381 37382 37383 37384 37385 37386 37387 37388 37389 37390 37391 37392 37393 37394 37395 37396 37397 37398 37399 37400 37401 37402 37403 37404 37405 37406 37407 37408 37409 37410 37411 37412 37413 37414 37415 37416 37417 37418 37419 37420 37421 37422 37423 37424 37425 37426 37427 37428 37429 37430 37431 37432 37433 37434 37435 37436 37437 37438 37439 37440 37441 37442 37443 37444 37445 37446 37447 37448 37449 37450 37451 37452 37453 37454 37455 37456 37457 37458 37459 37460 37461 37462 37463 37464 37465 37466 37467 37468 37469 37470 37471 37472 37473 37474 37475 37476 37477 37478 37479 37480 37481 37482 37483 37484 37485 37486 37487 37488 37489 37490 37491 37492 37493 37494 37495 37496 37497 37498 37499 37500 37501 37502 37503 37504 37505 37506 37507 37508 37509 37510 37511 37512 37513 37514 37515 37516 37517 37518 37519 37520 37521 37522 37523 37524 37525 37526 37527 37528 37529 37530 37531 37532 37533 37534 37535 37536 37537 37538 37539 37540 37541 37542 37543 37544 37545 37546 37547 37548 37549 37550 37551 37552 37553 37554 37555 37556 37557 37558 37559 37560 37561 37562 37563 37564 37565 37566 37567 37568 37569 37570 37571 37572 37573 37574 37575 37576 37577 37578 37579 37580 37581 37582 37583 37584 37585 37586 37587 37588 37589 37590 37591 37592 37593 37594 37595 37596 37597 37598 37599 37600 37601 37602 37603 37604 37605 | #define range(low, item, hi) maximum(low, minimum(item, hi)) /* minimum --- return minimum of two numbers */ static inline int minimum(int a, int 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); } /* 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; #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; #endif #ifdef POSIX2_DATE 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 '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"); } } #endif snprintf(tbuf, TBufLen, "%d", iso8601wknum(timeptr)); break; 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; #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; } #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; } #endif /* weeknumber --- figure how many weeks into the year */ /* 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 - (timeptr->tm_wday ? (timeptr->tm_wday - 1) : 6)) / 7; } #undef SYSV_EXT #undef SUNOS_EXT #undef POSIX2_DATE #undef VMS_EXT |
︙ | ︙ | |||
36701 36702 36703 36704 36705 36706 36707 | fsl_card_T_free(t); t = NULL; } } return t; } | | | 37700 37701 37702 37703 37704 37705 37706 37707 37708 37709 37710 37711 37712 37713 37714 | fsl_card_T_free(t); t = NULL; } } return t; } fsl_id_t fsl_tag_id( fsl_cx * const 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; rc = fsl_db_get_int64( db, &id, "SELECT tagid FROM tag WHERE tagname=%Q", |
︙ | ︙ | |||
36726 36727 36728 36729 36730 36731 36732 | fsl_cx_uplift_db_error( f, db ); id = -1; } return id; } | | | | | 37725 37726 37727 37728 37729 37730 37731 37732 37733 37734 37735 37736 37737 37738 37739 37740 37741 37742 37743 37744 37745 37746 37747 37748 37749 37750 37751 37752 37753 37754 37755 37756 37757 37758 37759 37760 37761 37762 37763 | fsl_cx_uplift_db_error( f, db ); id = -1; } return id; } 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){ int rc; 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); assert(FSL_TAGTYPE_CANCEL==tagType || FSL_TAGTYPE_PROPAGATING==tagType); assert(f); assert(db); assert(pid>0); assert(tagid>0); if((pid<=0 || tagid<=0) ||(FSL_TAGTYPE_PROPAGATING!=tagType && FSL_TAGTYPE_CANCEL!=tagType) || (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); if(rc) return rc; rc = fsl_db_prepare(db, &s, "SELECT cid, plink.mtime," " coalesce(srcid=0 AND " " tagxref.mtime<:mtime, %d) AS doit" " FROM plink LEFT JOIN tagxref " |
︙ | ︙ | |||
36801 36802 36803 36804 36805 36806 36807 | if( tagid==FSL_TAGID_BGCOLOR ){ rc = fsl_db_prepare(db, &eventupdate, "UPDATE event SET bgcolor=%Q " "WHERE objid=:rid", zValue); if(rc) goto end; } | | | | | | | | | | 37800 37801 37802 37803 37804 37805 37806 37807 37808 37809 37810 37811 37812 37813 37814 37815 37816 37817 37818 37819 37820 37821 37822 37823 37824 37825 37826 37827 37828 37829 37830 37831 37832 37833 37834 37835 37836 37837 37838 37839 37840 37841 37842 37843 37844 37845 37846 37847 37848 37849 37850 37851 37852 37853 37854 37855 37856 37857 37858 37859 37860 37861 37862 37863 37864 37865 37866 37867 37868 37869 37870 37871 37872 37873 37874 37875 37876 37877 37878 37879 37880 37881 37882 37883 37884 37885 37886 37887 37888 37889 37890 37891 37892 37893 37894 37895 37896 37897 37898 37899 | if( tagid==FSL_TAGID_BGCOLOR ){ rc = fsl_db_prepare(db, &eventupdate, "UPDATE event SET bgcolor=%Q " "WHERE objid=:rid", zValue); if(rc) goto end; } 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 while( !rc && (FSL_RC_STEP_ROW == fsl_stmt_step(&s)) ){ int32_t const doit = fsl_stmt_g_int32(&s, 2); 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); 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; rc = 0; } fsl_stmt_reset(&ins); if( FSL_TAGID_BGCOLOR == tagid ){ rc = fsl_stmt_bind_id_name(&eventupdate, ":rid", cid); if(!rc){ rc = fsl_stmt_step(&eventupdate); 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); } } } fsl_stmt_reset(&s); } end: fsl_stmt_finalize(&s); fsl_stmt_finalize(&ins); fsl_stmt_finalize(&eventupdate); fsl__pq_clear(&queue); return rc; } int fsl__tag_propagate_all(fsl_cx * const 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; assert(db); rc = fsl_db_prepare(db, &q, "SELECT tagid, tagtype, mtime, " "value, origid FROM tagxref" " WHERE rid=%"FSL_ID_T_PFMT, pid); while( !rc && (FSL_RC_STEP_ROW == fsl_stmt_step(&q)) ){ fsl_id_t const tagid = fsl_stmt_g_id(&q, 0); int32_t tagtype = fsl_stmt_g_int32(&q, 1); 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, origid, zValue, mtime); } fsl_stmt_finalize(&q); return rc; } 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_db * db = f ? fsl_cx_db_repo(f) : NULL; fsl_stmt q = fsl_stmt_empty; 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); if(tagid<0){ assert(f->error.code); return f->error.code; } if( mtime<=0.0 ){ mtime = fsl_db_julian_now(db); if(mtime<0) return FSL_RC_DB; |
︙ | ︙ | |||
36942 36943 36944 36945 36946 36947 36948 | if(!rc) fsl_stmt_bind_double(&q, 1, mtime); if(!rc) rc = fsl_stmt_step(&q); if(FSL_RC_STEP_DONE != rc) goto end; rc = 0; fsl_stmt_finalize(&q); if(FSL_TAGID_BRANCH == tagid ){ | | | 37941 37942 37943 37944 37945 37946 37947 37948 37949 37950 37951 37952 37953 37954 37955 | if(!rc) fsl_stmt_bind_double(&q, 1, mtime); if(!rc) rc = fsl_stmt_step(&q); 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); if(rc) goto end; } #if 0 /* Historical: we have valid use cases for the value here. */ else if(FSL_TAGTYPE_CANCEL==tagtype){ |
︙ | ︙ | |||
37003 37004 37005 37006 37007 37008 37009 | " omtime=coalesce(omtime,mtime)" "WHERE objid=%"FSL_ID_T_PFMT, zValue, (fsl_id_t)rid); if(rc) goto end; } if( FSL_TAGTYPE_ADD == tagtype ) tagtype = FSL_TAGTYPE_CANCEL /* For propagation purposes */; | | < | < < | < | | | | 38002 38003 38004 38005 38006 38007 38008 38009 38010 38011 38012 38013 38014 38015 38016 38017 38018 38019 38020 38021 38022 38023 38024 38025 38026 38027 38028 38029 38030 38031 38032 38033 38034 38035 38036 38037 38038 38039 38040 38041 38042 38043 38044 38045 38046 38047 38048 38049 38050 38051 38052 38053 38054 38055 38056 | " omtime=coalesce(omtime,mtime)" "WHERE objid=%"FSL_ID_T_PFMT, 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, rid, zValue, mtime); end: fsl_stmt_finalize(&q); if(0==rc && outRid) *outRid = tagid; return rc; } int fsl_tag_sym( fsl_cx * f, fsl_tagtype_e tagType, char const * symToTag, char const * tagName, char const * tagValue, char const * userName, double mtime, fsl_id_t * outId ){ if(!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 ); if(!rc){ assert(resolvedRid>0); rc = fsl_tag_an_rid(f, tagType, resolvedRid, tagName, tagValue, userName, mtime, outId); } return rc; } } int fsl_tag_an_rid( fsl_cx * const 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_deck c = fsl_deck_empty; char * resolvedUuid = NULL; fsl_buffer mfout = fsl_buffer_empty; int rc; if(!f || !tagName || !userName) return FSL_RC_MISUSE; else if(!*tagName || !*userName || (idToTag<=0)) return FSL_RC_RANGE; |
︙ | ︙ | |||
37195 37196 37197 37198 37199 37200 37201 | if(rc) goto end; } rc = fsl_deck_T_add(&deck, FSL_TAGTYPE_PROPAGATING, NULL, "branch", opt->name); if(!rc){ /* Add tag named sym-BRANCHNAME... */ | | | | 38190 38191 38192 38193 38194 38195 38196 38197 38198 38199 38200 38201 38202 38203 38204 38205 38206 38207 38208 38209 38210 | if(rc) goto end; } 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); 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); } if(rc) goto end; #if 1 rc = fsl_db_transaction_begin(db); if(rc) goto end; else{ |
︙ | ︙ | |||
37235 37236 37237 37238 37239 37240 37241 | if(!rc){ assert(deck.rid>0); 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. */ | | | 38230 38231 38232 38233 38234 38235 38236 38237 38238 38239 38240 38241 38242 38243 38244 | if(!rc){ assert(deck.rid>0); 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); } } } if(!rc) rc = fsl_db_transaction_commit(db); else fsl_db_transaction_rollback(db); if(rc) goto end; } |
︙ | ︙ | |||
37283 37284 37285 37286 37287 37288 37289 | */ /************************************************************************* This file implements ticket-related parts of the library. */ #include <assert.h> #include <string.h> /* memcmp() */ | | | 38278 38279 38280 38281 38282 38283 38284 38285 38286 38287 38288 38289 38290 38291 38292 | */ /************************************************************************* This file implements ticket-related parts of the library. */ #include <assert.h> #include <string.h> /* memcmp() */ 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;" "DROP TABLE IF EXISTS ticketchng;" ); |
︙ | ︙ | |||
37310 37311 37312 37313 37314 37315 37316 | for(i=0; i<(int)jli->used; ++i){ jc = (fsl_card_J const *)jli->list[i]; if( !fsl_strcmp(zFieldName, jc->field) ) return i; } return -1; } | | | | 38305 38306 38307 38308 38309 38310 38311 38312 38313 38314 38315 38316 38317 38318 38319 38320 38321 38322 38323 38324 38325 38326 38327 | for(i=0; i<(int)jli->used; ++i){ jc = (fsl_card_J const *)jli->list[i]; if( !fsl_strcmp(zFieldName, jc->field) ) return i; } return -1; } int fsl__cx_ticket_load_fields(fsl_cx * const 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); /* Fall through and reload ... */ } if( !(db = fsl_needs_repo(f)) ){ return FSL_RC_NOT_A_REPO; } rc = fsl_db_prepare(db, &q, "PRAGMA table_info(ticket)"); if(!rc) while( FSL_RC_STEP_ROW==fsl_stmt_step(&q) ){ |
︙ | ︙ | |||
37375 37376 37377 37378 37379 37380 37381 | fsl_card_J_free(jc); break; } } fsl_stmt_finalize(&q); end: if(!rc){ | | | 38370 38371 38372 38373 38374 38375 38376 38377 38378 38379 38380 38381 38382 38383 38384 | fsl_card_J_free(jc); break; } } fsl_stmt_finalize(&q); end: if(!rc){ fsl_list_sort(li, fsl__qsort_cmp_J_cards); } return rc; } /* end of file ticket.c */ /* start of file utf8.c */ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ |
︙ | ︙ | |||
37898 37899 37900 37901 37902 37903 37904 | int fsl_vfile_changes_scan(fsl_cx * const f, fsl_id_t vid, unsigned cksigFlags){ fsl_stmt * stUpdate = NULL; 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; | | | 38893 38894 38895 38896 38897 38898 38899 38900 38901 38902 38903 38904 38905 38906 38907 | int fsl_vfile_changes_scan(fsl_cx * const f, fsl_id_t vid, unsigned cksigFlags){ fsl_stmt * stUpdate = NULL; 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); 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; assert(vid>=0); rootLen = fsl_strlen(f->ckout.dir); |
︙ | ︙ | |||
38121 38122 38123 38124 38125 38126 38127 | "FROM vfile vf LEFT JOIN blob b " "ON b.rid=vf.rid " "WHERE vf.vid=%"FSL_ID_T_PFMT" " "AND (chnged<>0 OR pathname<>origname OR deleted<>0)" "ORDER BY vf.id", vid); #endif end: | | | | | 39116 39117 39118 39119 39120 39121 39122 39123 39124 39125 39126 39127 39128 39129 39130 39131 39132 39133 39134 39135 39136 39137 39138 39139 39140 39141 39142 39143 39144 39145 39146 39147 39148 39149 39150 | "FROM vfile vf LEFT JOIN blob b " "ON b.rid=vf.rid " "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); if(!rc && (cksigFlags & FSL_VFILE_CKSIG_WRITE_CKOUT_VERSION) && (f->ckout.rid != vid)){ 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); }else{ rc = fsl_db_transaction_commit(db); if(rc){ rc = fsl_cx_uplift_db_error2(f, db, rc); } } 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 * wasWritten){ int rc = 0; fsl_db * const db = fsl_needs_ckout(f); fsl_stmt q = fsl_stmt_empty; int counter = 0; fsl_buffer content = fsl_buffer_empty; char const * sql; |
︙ | ︙ | |||
38195 38196 38197 38198 38199 38200 38201 | char const * zName = fsl_stmt_g_text(&q, 1, &nameLen); fsl_size_t hashLen = 0; 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); | | | | 39190 39191 39192 39193 39194 39195 39196 39197 39198 39199 39200 39201 39202 39203 39204 39205 39206 39207 39208 | char const * zName = fsl_stmt_g_text(&q, 1, &nameLen); fsl_size_t hashLen = 0; 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); if(rc) break; assert(fsl_is_uuid_len(hashLen)); f->cache.fstat = fsl_fstat_empty; 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), &isMod) /* that updates f->cache.fstat */; |
︙ | ︙ | |||
38223 38224 38225 38226 38227 38228 38229 | } else if(!isMod) continue; else if((rc=fsl_mkdir_for_file(zName, true))){ rc = fsl_cx_err_set(f, rc, "mkdir() failed for file: %s", zName); break; } | | | | | | | | | | 39218 39219 39220 39221 39222 39223 39224 39225 39226 39227 39228 39229 39230 39231 39232 39233 39234 39235 39236 39237 39238 39239 39240 39241 39242 39243 39244 39245 39246 39247 39248 39249 39250 39251 39252 39253 39254 39255 39256 39257 39258 39259 39260 39261 39262 39263 39264 39265 | } else if(!isMod) continue; 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){ assert(((isLink && FSL_FILE_PERM_LINK!=fst->perm) ||(!isLink && FSL_FILE_PERM_LINK==fst->perm)) && "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))){ /* 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, fsl_buffer_cstr(&content)); if(wasWritten && !rc) *wasWritten = 2; }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)){ *wasWritten = 1; } if(rc) break; fsl_file_exec_set(zName, !!isExe); fsl_buffer_reuse(&content); }/*step() loop*/ switch(rc){ |
︙ | ︙ | |||
38905 38906 38907 38908 38909 38910 38911 | } } if(rc) goto end; { char * u = NULL; if(!userName) userName = fsl_cx_user_get(f); if(!userName){ | | | 39900 39901 39902 39903 39904 39905 39906 39907 39908 39909 39910 39911 39912 39913 39914 | } } if(rc) goto end; { char * u = NULL; if(!userName) userName = fsl_cx_user_get(f); if(!userName){ u = fsl_user_name_guess(); 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; } rc = fsl_deck_W_set(&d, fsl_buffer_cstr(b), (fsl_int_t)b->used); |
︙ | ︙ | |||
38990 38991 38992 38993 38994 38995 38996 | H = atoi(&zDate[11]); M = atoi(&zDate[14]); S = atoi(&zDate[17]); z->dosTime = (H<<11) + (M<<5) + (S>>1); z->dosDate = ((y-1980)<<9) + (m<<5) + d; } | | | | | | | 39985 39986 39987 39988 39989 39990 39991 39992 39993 39994 39995 39996 39997 39998 39999 40000 40001 40002 40003 40004 40005 40006 40007 40008 40009 40010 40011 40012 40013 40014 40015 40016 40017 40018 40019 40020 40021 40022 40023 40024 40025 40026 40027 40028 40029 40030 40031 40032 40033 40034 | H = atoi(&zDate[11]); M = atoi(&zDate[14]); 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 ){ return z ? &z->body : NULL; } void fsl_zip_timestamp_set_julian(fsl_zip_writer * const 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){ char buf[20] = {0}; fsl_julian_to_iso8601(fsl_unix_to_julian(epochTime), buf, 0); fzip_timestamp_from_str(z, buf); z->unixTime = epochTime; } /** 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); /** 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 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, fsl_buffer const * pSrc, int mPerm, char doMkDirs){ int rc = 0; z_stream stream; fsl_size_t nameLen; int toOut = 0; int iStart; |
︙ | ︙ | |||
39169 39170 39171 39172 39173 39174 39175 | fzip_put16(&zExTime[2], 5); fsl_buffer_append(&zw->toc, zExTime, 9); ++zw->entryCount; return rc; } | | | 40164 40165 40166 40167 40168 40169 40170 40171 40172 40173 40174 40175 40176 40177 40178 | fzip_put16(&zExTime[2], 5); fsl_buffer_append(&zw->toc, zExTime, 9); ++zw->entryCount; return rc; } int fzip_mkdir(fsl_zip_writer * const 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; for(i=0; zName[i]; i++){ if( zName[i]=='/' ){ |
︙ | ︙ | |||
39197 39198 39199 39200 39201 39202 39203 | } } } } return rc; } | | | | 40192 40193 40194 40195 40196 40197 40198 40199 40200 40201 40202 40203 40204 40205 40206 40207 40208 40209 40210 40211 | } } } } return rc; } int fsl_zip_file_add(fsl_zip_writer * const 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 ){ if(!z) return FSL_RC_MISUSE; else if(zRoot && *zRoot && fsl_is_absolute_path(zRoot)){ return FSL_RC_RANGE; }else{ fsl_free(z->rootDir); z->rootDir = NULL; if(zRoot && *zRoot){ |
︙ | ︙ | |||
39252 39253 39254 39255 39256 39257 39258 | return rc; } } return 0; } } | | | | | 40247 40248 40249 40250 40251 40252 40253 40254 40255 40256 40257 40258 40259 40260 40261 40262 40263 40264 40265 40266 40267 40268 40269 40270 40271 40272 40273 40274 40275 40276 40277 40278 40279 40280 40281 40282 40283 40284 | return rc; } } return 0; } } static void fsl_zip_finalize_impl(fsl_zip_writer * const z, bool 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); fsl_free(z->rootDir); if(alsoBody){ fsl_buffer_clear(&z->body); *z = fsl_zip_writer_empty; }else{ fsl_buffer cp = z->body; *z = fsl_zip_writer_empty; z->body = cp; } } } void fsl_zip_finalize(fsl_zip_writer * const z){ fsl_zip_finalize_impl(z, 1); } int fsl_zip_end( fsl_zip_writer * const z ){ int rc; fsl_int_t iTocStart; fsl_int_t iTocEnd; char zBuf[30]; iTocStart = (fsl_int_t)z->body.used; rc = fsl_buffer_append(&z->body, z->toc.mem, z->toc.used); |
︙ | ︙ | |||
39303 39304 39305 39306 39307 39308 39309 | rc = fsl_buffer_append(&z->body, zBuf, 22); fsl_zip_finalize_impl(z, 0); assert(z->body.used); return rc; } | | | | 40298 40299 40300 40301 40302 40303 40304 40305 40306 40307 40308 40309 40310 40311 40312 40313 40314 40315 40316 40317 40318 40319 40320 40321 40322 40323 40324 40325 40326 40327 40328 40329 | rc = fsl_buffer_append(&z->body, zBuf, 22); 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 ){ if(!z) return FSL_RC_MISUSE; else{ int rc; if(!dest){ rc = FSL_RC_MISUSE; }else{ rc = fsl_zip_end(z); if(!rc){ fsl_buffer_swap( &z->body, dest ); } } fsl_zip_finalize( z ); return rc; } } int fsl_zip_end_to_filename( fsl_zip_writer * const z, char const * filename ){ if(!z) return FSL_RC_MISUSE; else{ int rc; if(!filename || !*filename){ rc = FSL_RC_MISUSE; }else{ rc = fsl_zip_end(z); |
︙ | ︙ | |||
39442 39443 39444 39445 39446 39447 39448 | sym); } if(rc) goto end; } } /** | | | | | | | | 40437 40438 40439 40440 40441 40442 40443 40444 40445 40446 40447 40448 40449 40450 40451 40452 40453 40454 40455 40456 40457 40458 40459 40460 40461 40462 40463 40464 40465 40466 40467 40468 40469 40470 40471 40472 | sym); } if(rc) goto end; } } /** Always write the manifest files to the zip, regardless of the repo-level settings. This decision is up for debate. */ 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_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); if(rc) goto mf_end; rc = fsl_zip_file_add(&zs.z, "manifest.uuid", bHash, FSL_FILE_PERM_REGULAR); 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); } if(rc) goto end; rc = fsl_zip_end( &zs.z ); if(!rc) rc = fsl_buffer_to_filename( fsl_zip_body(&zs.z), fileName ); end: if(rc && !f->error.code){ |
︙ | ︙ |
Changes to lib/libfossil.h.
︙ | ︙ | |||
464 465 466 467 468 469 470 | #define fsl_list_empty_m { NULL, 0, 0 } /** Empty-initialized fsl_list structure, intended for copy initialization. */ FSL_EXPORT const fsl_list fsl_list_empty; | < | 464 465 466 467 468 469 470 471 472 473 474 475 476 477 | #define fsl_list_empty_m { NULL, 0, 0 } /** 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), regardless of whether or not they actually free the memory. At the very least, they generally should clean up any memory owned by mem (e.g. db resources or buffers), even if they do not free() mem. |
︙ | ︙ | |||
1029 1030 1031 1032 1033 1034 1035 | /** va_list counterpart to fsl_error_set(). */ FSL_EXPORT int fsl_error_setv( fsl_error * const err, int code, char const * fmt, va_list args ); /** | | < < | | | | > > | | | | 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 | /** va_list counterpart to fsl_error_set(). */ 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. 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. If len is not NULL then *len will be assigned to the length of the (*str) string (in bytes). @see fsl_error_set() @see fsl_error_clear() @see fsl_error_move() */ 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. @see fsl_error_set() @see fsl_error_get() @see fsl_error_move() @see fsl_error_reset() */ FSL_EXPORT void fsl_error_clear( fsl_error * const err ); |
︙ | ︙ | |||
1494 1495 1496 1497 1498 1499 1500 | /** 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. */ | | | | 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 | /** 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 ); /** 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. If pTarget==pOriginal then this is a destructive operation, |
︙ | ︙ | |||
1781 1782 1783 1784 1785 1786 1787 | FSL_EXPORT int fsl_strncmp(const char *zA, const char *zB, fsl_size_t nByte); /** 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. */ | | | | 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 | FSL_EXPORT int fsl_strncmp(const char *zA, const char *zB, fsl_size_t nByte); /** 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); /** 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); /** 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 FSL_STRLEN_K256 bytes long and are compared as such, else they are assumed to be FSL_STRLEN_SHA1 bytes long and compared as |
︙ | ︙ | |||
2813 2814 2815 2816 2817 2818 2819 | fsl_filename_free(), though fsl_getenv() requires that one). If it finds no match, or if copying the entry fails, it returns NULL. @see fsl_cx_user_set() @see fsl_cx_user_get() */ | | | 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 | fsl_filename_free(), though fsl_getenv() requires that one). If it finds no match, or if copying the entry fails, it returns NULL. @see fsl_cx_user_set() @see fsl_cx_user_get() */ FSL_EXPORT char * fsl_user_name_guess(); /** 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 note: the overwrite behaviour is inconsistent with most of the API, but the implementation currently requires this.) |
︙ | ︙ | |||
3141 3142 3143 3144 3145 3146 3147 | /** Functionally identical to fsl_delta_apply() but any errors generated 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, | | | | | | < | | | > > | | | < | | | 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 | /** Functionally identical to fsl_delta_apply() but any errors generated 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); /* 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. 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); /** "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 out->used is updated accordingly. Returns 0 on success, FSL_RC_MISUSE if !inp or !out. Returns 0 without side-effects if |
︙ | ︙ | |||
3351 3352 3353 3354 3355 3356 3357 | Flags for use with text-diff generation APIs, e.g. fsl_diff_text(). Maintenance reminder: these values are holy and must not be changed without also changing the corresponding code in diff.c. | | | 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 | Flags for use with text-diff generation APIs, e.g. fsl_diff_text(). 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. */ enum fsl_diff_flag_e { /** Ignore end-of-line whitespace */ FSL_DIFF_IGNORE_EOLWS = 0x01, /** Ignore end-of-line whitespace */ FSL_DIFF_IGNORE_ALLWS = 0x03, /** Generate a side-by-side diff */ |
︙ | ︙ | |||
3459 3460 3461 3462 3463 3464 3465 | @see fsl_diff_v2() @deprecated Prefer fsl_diff_v2() for new code. */ 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 ); | > | | 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 | @see fsl_diff_v2() @deprecated Prefer fsl_diff_v2() for new code. */ 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 changed without also changing the corresponding code in diff2.c. */ |
︙ | ︙ | |||
4680 4681 4682 4683 4684 4685 4686 | path or if zRoot cannot be normalized to a "simplified name" (as per fsl_is_simple_pathname(), with the note that this routine will pass a copy of zRoot through fsl_file_simplify_name() first). @see fsl_zip_finalize() */ | | > | 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 | path or if zRoot cannot be normalized to a "simplified name" (as per fsl_is_simple_pathname(), with the note that this routine 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 ); /** 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, otherwise the ZIP's entry is populated from pContent. The permsFlag argument specifies the fossil-specific permission |
︙ | ︙ | |||
4756 4757 4758 4759 4760 4761 4762 | @see fsl_zip_timestamp_set_julian() @see fsl_zip_timestamp_set_unix() @see fsl_zip_end() @see fsl_zip_body() @see fsl_zip_finalize() */ | | > | > | 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 | @see fsl_zip_timestamp_set_julian() @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 ); /** 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 ZIP file. |
︙ | ︙ | |||
4780 4781 4782 4783 4784 4785 4786 | @see fsl_zip_timestamp_set_julian() @see fsl_zip_timestamp_set_unix() @see fsl_zip_file_add() @see fsl_zip_body() @see fsl_zip_finalize() @see fsl_zip_end_take() */ | | | > | > | | | > | > | 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 | @see fsl_zip_timestamp_set_julian() @see fsl_zip_timestamp_set_unix() @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 ); /** 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() to free any other resources (which are invalidated by the removal of the body). Returns 0 on success, FSL_RC_MISUSE if either pointer is NULL, some non-0 code if the proxied fsl_zip_end() call fails. On 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 ); /** 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 either end/save combination. Saving is not attempted if ending the ZIP fails. On success 0 is returned and the contents of the ZIP are in the 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 ); /** Returns a pointer to z's ZIP content buffer. The contents are ONLY valid after fsl_zip_end() returns 0. @see fsl_zip_timestamp_set_julian() @see fsl_zip_timestamp_set_unix() @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 ); /** 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 eventually be passed to this function to clean up any contents it might have accumulated during its life. After this returns, z is legal for re-use in creating a new ZIP archive. @see fsl_zip_timestamp_set_julian() @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); /** 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 is re-set. @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); /** 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); /** State for the fsl_timer_xxx() family of functions. @see fsl_timer_start() @see fsl_timer_reset() @see fsl_timer_stop() |
︙ | ︙ | |||
5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 | /** Use the legacy format */ FSL_CONFIGSET_OLDFORMAT = 0x200000 }; typedef enum fsl_configset_e fsl_configset_e; /** Runtime-configurable flags for a fsl_cx instance. */ 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. */ FSL_CX_F_NONE = 0, | > > > | 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 | /** Use the legacy format */ FSL_CONFIGSET_OLDFORMAT = 0x200000 }; 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. */ FSL_CX_F_NONE = 0, |
︙ | ︙ | |||
5954 5955 5956 5957 5958 5959 5960 | be used on Windows. */ 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. | | > > > > > > > | | 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 | be used on Windows. */ 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 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 }; typedef enum fsl_cx_flags_e fsl_cx_flags_e; /** List of hash policy values. New repositories should generally use only SHA3 hashes, but older repos may contain SHA1 hashes (perhaps |
︙ | ︙ | |||
6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 | FSL_RC_DIFF_BINARY, /** 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, /** 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. */ FSL_RC_end }; | > > > > > > | 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 | FSL_RC_DIFF_BINARY, /** 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. */ FSL_RC_end }; |
︙ | ︙ | |||
6474 6475 6476 6477 6478 6479 6480 | This function triggers any finializers set for f's client state or output channel. This is a no-op if !f and is effectively a no-op if f has no state to destruct. */ | | > > | > > | | | | | | | | | | > | | | > | > | > | | | > > > > > | 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 6599 6600 6601 6602 6603 6604 6605 6606 6607 6608 6609 6610 6611 6612 6613 6614 6615 6616 6617 6618 6619 6620 6621 6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 | This function triggers any finializers set for f's client state or output channel. This is a no-op if !f and is effectively a no-op if f has no state to destruct. */ FSL_EXPORT void fsl_cx_finalize( fsl_cx * const 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 ); /** Returns f's flags. @see fsl_cx_flag_set() */ FSL_EXPORT int fsl_cx_flags_get( fsl_cx const * const f ); /** Sets the Fossil error state to the given error code and fsl_appendf()-style format string/arguments. On success it returns the code parameter. It does not return 0 unless code is 0, and if it returns a value other than code then something went seriously wrong (e.g. allocation error: FSL_RC_OOM) or the arguments were invalid: !f results in FSL_RC_MISUSE. If !fmt then fsl_rc_cstr(code) is used to create the error string. As a special case, if code is FSL_RC_OOM, no error string is allocated (because it would likely fail, assuming the OOM is real). As a special case, if code is 0 (the non-error value) then fmt is ignored and any error state is cleared. */ FSL_EXPORT int fsl_cx_err_set( fsl_cx * const 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, 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 ); /** Returns f's error state object. This pointer is guaranteed by the API to be stable until f is finalized, but its contents are modified my routines as part of the error reporting process. Returns NULL if !f. */ FSL_EXPORT fsl_error const * fsl_cx_err_get_e(fsl_cx const * f); /** Resets's f's error state, basically equivalent to fsl_cx_err_set(f,0,NULL). 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_EXPORT void fsl_cx_err_reset(fsl_cx * const 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 ); /** If f has error state then it outputs its error state to its output channel and returns the result of fsl_output(). Returns FSL_RC_MISUSE if !f, 0 if f has no error state our output of the state succeeds. If addNewline is true then it adds a trailing newline to the output, else it does not. This is intended for testing and debugging only, and not as an error reporting mechanism for a full-fledged application. */ FSL_EXPORT int fsl_cx_err_report( fsl_cx * 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). @see fsl_cx_uplift_db_error2() */ FSL_EXPORT int fsl_cx_uplift_db_error( fsl_cx * const f, fsl_db * db ); /** If rc is not 0 and f has no error state but db does, this calls 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); /** |
︙ | ︙ | |||
6680 6681 6682 6683 6684 6685 6686 | @see fsl_repo_create() @see fsl_repo_close() */ 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 | | | | > > > > | 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734 6735 6736 | @see fsl_repo_create() @see fsl_repo_close() */ 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). 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 repository). Such a repository will be closed implicitly when the checkout db is closed. |
︙ | ︙ | |||
6705 6706 6707 6708 6709 6710 6711 | repository user name for operations which require one. Returns 0 on success, FSL_RC_MISUSE if f is NULL, FSL_RC_OOM if copying of the userName fails. Example usage: ``` | | > > | > > > > > > > > > > > > | | > > | | 6745 6746 6747 6748 6749 6750 6751 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 6763 6764 6765 6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 | repository user name for operations which require one. Returns 0 on success, FSL_RC_MISUSE if f is NULL, FSL_RC_OOM if copying of the userName fails. Example usage: ``` char * u = fsl_user_name_guess(); 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); /** 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() */ FSL_EXPORT char const * fsl_cx_user_get( fsl_cx const * const f ); /** Configuration parameters for fsl_repo_create(). Always copy-construct these from fsl_repo_create_opt_empty resp. fsl_repo_create_opt_empty_m in order to ensure proper behaviour vis-a-vis default values. |
︙ | ︙ | |||
6977 6978 6979 6980 6981 6982 6983 | FSL_EXPORT int fsl_ckout_db_search( char const * dirName, bool checkParentDirs, fsl_buffer * const pOut ); /** If fsl_ckout_open_dir() (or similar) has been used to open a | | > > > | < < | > > > | 7033 7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 | FSL_EXPORT int fsl_ckout_db_search( char const * dirName, bool checkParentDirs, 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. 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(). */ 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 returns its name. The bytes are valid until that checkout db connection is closed. If len is not NULL then *len is (on |
︙ | ︙ | |||
7176 7177 7178 7179 7180 7181 7182 | false. @see fsl_needs_ckout() */ FSL_EXPORT bool fsl_cx_has_ckout(fsl_cx const * const f ); /** | | > | > > > > > | | | > | > > | | > > > > > | > > | > > > > | 7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256 7257 7258 7259 7260 7261 7262 7263 7264 7265 7266 7267 7268 7269 7270 7271 7272 7273 7274 7275 7276 7277 7278 7279 7280 7281 7282 7283 7284 7285 7286 7287 7288 7289 7290 7291 7292 7293 | false. @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. 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 fsl_cx_db_file_config(). Results are undefined if f is NULL or not properly initialized. 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). @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. @see fsl_cx_db_config() @see fsl_config_open() */ FSL_EXPORT int fsl_config_close( fsl_cx * const f ); /** |
︙ | ︙ | |||
7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241 7242 7243 7244 | /** 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_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 information if f has no database (should not be able to happen and might assert) or sql is NULL. | > > > > > > > > > > > > > > > > | 7311 7312 7313 7314 7315 7316 7317 7318 7319 7320 7321 7322 7323 7324 7325 7326 7327 7328 7329 7330 7331 7332 7333 7334 7335 7336 7337 7338 7339 7340 | /** 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 information if f has no database (should not be able to happen and might assert) or sql is NULL. |
︙ | ︙ | |||
7729 7730 7731 7732 7733 7734 7735 | that users have a way to *not* get spammed with a confirmation message showing up for each and every one of an arbitrary number of confirmations. @see fsl_confirm_callback_f @see fsl_cx_confirmer() */ | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 7825 7826 7827 7828 7829 7830 7831 7832 7833 7834 7835 7836 7837 7838 7839 7840 7841 7842 7843 7844 7845 7846 7847 7848 7849 7850 7851 7852 7853 7854 7855 7856 7857 7858 7859 7860 7861 7862 7863 7864 7865 7866 7867 7868 7869 7870 7871 7872 7873 7874 7875 7876 7877 7878 7879 7880 7881 7882 7883 7884 7885 7886 7887 7888 7889 7890 7891 7892 7893 7894 7895 7896 7897 7898 7899 7900 7901 7902 7903 | that users have a way to *not* get spammed with a confirmation message showing up for each and every one of an arbitrary number of 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_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), depending on f: |
︙ | ︙ | |||
8208 8209 8210 8211 8212 8213 8214 8215 8216 8217 8218 8219 8220 8221 | /** The number of times this statement has fetched a row via fsl_stmt_step(). */ fsl_size_t rowCount; /** Internal state flags. */ short flags; /** Internal use only: counts the number of times this query has | > > > > > > > > > > > | 8366 8367 8368 8369 8370 8371 8372 8373 8374 8375 8376 8377 8378 8379 8380 8381 8382 8383 8384 8385 8386 8387 8388 8389 8390 | /** 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; /** Internal use only: counts the number of times this query has |
︙ | ︙ | |||
8243 8244 8245 8246 8247 8248 8249 8250 8251 8252 8253 8254 8255 8256 | #define fsl_stmt_empty_m { \ NULL/*db*/, \ NULL/*stmt*/, \ fsl_buffer_empty_m/*sql*/, \ 0/*colCount*/, \ 0/*paramCount*/, \ 0/*rowCount*/, \ 0/*flags*/, \ 0/*cachedHits*/, \ NULL/*next*/, \ NULL/*allocStamp*/ \ } /** | > | 8412 8413 8414 8415 8416 8417 8418 8419 8420 8421 8422 8423 8424 8425 8426 | #define fsl_stmt_empty_m { \ NULL/*db*/, \ NULL/*stmt*/, \ fsl_buffer_empty_m/*sql*/, \ 0/*colCount*/, \ 0/*paramCount*/, \ 0/*rowCount*/, \ 0/*role*/, \ 0/*flags*/, \ 0/*cachedHits*/, \ NULL/*next*/, \ NULL/*allocStamp*/ \ } /** |
︙ | ︙ | |||
9587 9588 9589 9590 9591 9592 9593 | Works like fsl_stmt_bind_fmt() but: 1) It calls fsl_stmt_reset() before binding the arguments. 2) If binding succeeds then it steps the given statement a single time. | | | 9757 9758 9759 9760 9761 9762 9763 9764 9765 9766 9767 9768 9769 9770 9771 | Works like fsl_stmt_bind_fmt() but: 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 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 if it produces a result row, or any number of other potential non-0 codes on error. On error, the error state of st->db is updated. |
︙ | ︙ | |||
10345 10346 10347 10348 10349 10350 10351 | /** An internal cursor into this->list, used primarily for 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 | | | 10515 10516 10517 10518 10519 10520 10521 10522 10523 10524 10525 10526 10527 10528 10529 | /** An internal cursor into this->list, used primarily for 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(). */ int32_t cursor; /** Internal flags. Never, ever modify these from client code. */ uint32_t flags; }; |
︙ | ︙ | |||
11350 11351 11352 11353 11354 11355 11356 11357 | FSL_EXPORT int fsl_deck_W_set( fsl_deck * const mf, char const *content, fsl_int_t len); /** 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. */ | > > > > | > | 11520 11521 11522 11523 11524 11525 11526 11527 11528 11529 11530 11531 11532 11533 11534 11535 11536 11537 11538 11539 11540 | FSL_EXPORT int fsl_deck_W_set( fsl_deck * const mf, char const *content, fsl_int_t len); /** 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 ); /** 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 intended to be re-set by the deck's user. |
︙ | ︙ | |||
11526 11527 11528 11529 11530 11531 11532 | (has a B-card) d->f's forbid-delta-manifests configuration option is set to a truthy value. See fsl_repo_forbids_delta_manifests(). Maintenance reminder: this function also does a very small bit of artifact-type-specific processing. @see fsl_deck_output() | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | > > | | | | | | | | | > | | > | | | | > > > > > > > > | | 11701 11702 11703 11704 11705 11706 11707 11708 11709 11710 11711 11712 11713 11714 11715 11716 11717 11718 11719 11720 11721 11722 11723 11724 11725 11726 11727 11728 11729 11730 11731 11732 11733 11734 11735 11736 11737 11738 11739 11740 11741 11742 11743 11744 11745 11746 11747 11748 11749 11750 11751 11752 11753 11754 11755 11756 11757 11758 11759 11760 11761 11762 11763 11764 11765 11766 11767 11768 11769 11770 11771 11772 11773 11774 11775 11776 11777 11778 11779 11780 11781 11782 11783 11784 11785 11786 11787 11788 11789 | (has a B-card) d->f's forbid-delta-manifests configuration option is set to a truthy value. See fsl_repo_forbids_delta_manifests(). Maintenance reminder: this function also does a very small bit of artifact-type-specific processing. @see fsl_deck_output() @see fsl__content_put_ex() */ FSL_EXPORT int fsl_deck_save( fsl_deck * const d, bool isPrivate ); /** 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 error-reporting purposes). This function _might_ take over the contents of the source buffer on success or it _might_ leave it for the caller to clean up or re-use, as he sees fit. If the caller does not intend to re-use the buffer, he should simply pass it to fsl_buffer_clear() after calling this (no need to check if it has contents or not first). When taking over the contents then on success, after returning 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 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). 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 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 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. 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_SYNTAX on syntax errors. - FSL_RC_CONSISTENCY if validation of a Z-card fails. - Any number of errors coming from the allocator, database, or fsl_deck APIs used here. |
︙ | ︙ | |||
11649 11650 11651 11652 11653 11654 11655 | /** 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 | | | < | > | | 11799 11800 11801 11802 11803 11804 11805 11806 11807 11808 11809 11810 11811 11812 11813 11814 11815 11816 11817 11818 11819 11820 11821 11822 11823 11824 11825 11826 11827 11828 11829 11830 | /** 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. */ 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 "might" be a structural artifact. It performs a fast sanity check for prominent features which can be checked either in O(1) or very short O(N) time (with a fixed N). If it returns false then the 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); /** 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 rid==0 is sometimes valid for an new/empty repo devoid of commits). If type==FSL_SATYPE_ANY then it will allow any type of |
︙ | ︙ | |||
11802 11803 11804 11805 11806 11807 11808 | Potential TODO: add some client-opaque state to decks so that they can be flagged as "being crosslinked" and fail mutation operations such as card adders/setters. @see fsl_xlink_listener() */ | | | 11952 11953 11954 11955 11956 11957 11958 11959 11960 11961 11962 11963 11964 11965 11966 | Potential TODO: add some client-opaque state to decks so that they 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); /** A type for holding a callback/state pair for manifest crosslinking callbacks. */ struct fsl_xlinker { char const * name; |
︙ | ︙ | |||
11854 11855 11856 11857 11858 11859 11860 | returned object is owned by f. */ 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 | | | 12004 12005 12006 12007 12008 12009 12010 12011 12012 12013 12014 12015 12016 12017 12018 | returned object is owned by f. */ 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 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 undefined if any of the first 3 arguments are NULL. |
︙ | ︙ | |||
12158 12159 12160 12161 12162 12163 12164 | Saves wiki content to f's repository db. 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 | | | 12308 12309 12310 12311 12312 12313 12314 12315 12316 12317 12318 12319 12320 12321 12322 | Saves wiki content to f's repository db. 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 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, text/plain |
︙ | ︙ | |||
12758 12759 12760 12761 12762 12763 12764 | tagType is the type (add, cancel, or propagate) of tag. 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 | | | | | 12908 12909 12910 12911 12912 12913 12914 12915 12916 12917 12918 12919 12920 12921 12922 12923 12924 12925 12926 12927 12928 12929 12930 12931 12932 12933 12934 12935 12936 12937 12938 12939 12940 12941 12942 12943 12944 12945 12946 12947 12948 12949 12950 12951 12952 12953 12954 12955 | tagType is the type (add, cancel, or propagate) of tag. 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 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 value <=0 to use the current time. If newId is not NULL then on success the rid of the new tag control artifact is assigned to *newId. Returns 0 on success and has about a million and thirteen possible error conditions. On success a new artifact record is written to the db, its RID being written into newId as described above. 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_id_t artifactRidToTag, char const * tagName, char const * tagValue, char const * userName, double mtime, fsl_id_t * newId ); /** Searches for a repo.tag entry given name in the given context's repository db. If found, it returns the record's id. If no 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 ); /** 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 (likely indicating that it is not a repository db), or just about any other conceivable non-success case. |
︙ | ︙ | |||
13027 13028 13029 13030 13031 13032 13033 | instead read it using fsl_content_get(). 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 | | | 13177 13178 13179 13180 13181 13182 13183 13184 13185 13186 13187 13188 13189 13190 13191 | instead read it using fsl_content_get(). 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() 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. @see fsl_repo_import_buffer() |
︙ | ︙ | |||
13734 13735 13736 13737 13738 13739 13740 13741 13742 13743 13744 13745 13746 13747 | Results are undefined if either argument is invalid or opt->out is NULL. */ FSL_EXPORT int fsl_annotate( fsl_cx * const f, fsl_annotate_opt const * const opt ); #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* ORG_FOSSIL_SCM_FSL_REPO_H_INCLUDED */ /* end of file ../include/fossil-scm/fossil-repo.h */ /* start of file ../include/fossil-scm/fossil-checkout.h */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 13884 13885 13886 13887 13888 13889 13890 13891 13892 13893 13894 13895 13896 13897 13898 13899 13900 13901 13902 13903 13904 13905 13906 13907 13908 13909 13910 13911 13912 13913 13914 13915 13916 13917 13918 13919 13920 13921 13922 13923 13924 13925 13926 13927 13928 13929 13930 13931 13932 13933 13934 13935 13936 13937 13938 13939 13940 13941 13942 13943 13944 13945 13946 13947 13948 13949 13950 13951 13952 13953 13954 13955 13956 13957 13958 13959 13960 13961 13962 13963 13964 13965 13966 13967 13968 13969 13970 13971 13972 13973 13974 13975 13976 13977 13978 13979 13980 13981 13982 13983 13984 13985 13986 13987 13988 13989 13990 13991 13992 13993 13994 13995 13996 13997 13998 13999 14000 14001 14002 14003 14004 14005 14006 14007 14008 14009 14010 14011 14012 14013 14014 14015 14016 14017 14018 14019 14020 14021 14022 14023 14024 14025 14026 14027 14028 14029 14030 14031 14032 14033 14034 14035 14036 14037 14038 14039 14040 14041 14042 14043 14044 14045 14046 14047 14048 14049 14050 14051 14052 14053 14054 14055 14056 14057 14058 14059 14060 14061 14062 14063 14064 14065 | Results are undefined if either argument is invalid or opt->out is 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 */ /* end of file ../include/fossil-scm/fossil-repo.h */ /* start of file ../include/fossil-scm/fossil-checkout.h */ |
︙ | ︙ | |||
14025 14026 14027 14028 14029 14030 14031 | The file or directory name to add. If it is a directory, the add process will recurse into it. See also this->vfileIds. */ 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 | | | 14343 14344 14345 14346 14347 14348 14349 14350 14351 14352 14353 14354 14355 14356 14357 | The file or directory name to add. If it is a directory, the add process will recurse into it. See also this->vfileIds. */ 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. @see fsl_filename_to_vfile_ids() */ fsl_id_bag const * vfileIds; /** Whether to evaluate this->filename as relative to the current working directory (true) or to the current checkout root |
︙ | ︙ | |||
15789 15790 15791 15792 15793 15794 15795 | Files which are not actually reverted because their contents or permissions were not modified on disk are not reported to the 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. */ | | | 16107 16108 16109 16110 16111 16112 16113 16114 16115 16116 16117 16118 16119 16120 16121 | Files which are not actually reverted because their contents or permissions were not modified on disk are not reported to the 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_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 the current checkout RID is used. This function does not do any path resolution or normalization on zName and checks only for an |
︙ | ︙ | |||
15846 15847 15848 15849 15850 15851 15852 | This function matches only `vfile.pathname`, not `vfile.origname`, 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() */ | | | | | | 16164 16165 16166 16167 16168 16169 16170 16171 16172 16173 16174 16175 16176 16177 16178 16179 16180 16181 16182 16183 16184 16185 16186 16187 16188 16189 16190 16191 16192 16193 16194 16195 16196 16197 16198 16199 16200 16201 | This function matches only `vfile.pathname`, not `vfile.origname`, 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, char const * zName, bool changedOnly); /** This is a variant of fsl_filename_to_vfile_ids() which accepts filenames in a more flexible form than that routine. This routine works exactly like that one except for the following differences: 1) The given filename and the relativeToCwd arguments are passed to by fsl_ckout_filename_check() to canonicalize the name and ensure that it points to someplace within f's current checkout. 2) Because of (1), zName may not be NULL or empty. To fetch all of the vfile IDs for the current checkout, pass a zName of "." and relativeToCwd=false. 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, bool relativeToCwd, bool changedOnly ); /** This "mostly internal" routine (re)populates f's checkout vfile table with all files from the given checkin manifest. If manifestRid is 0 or less then the current checkout's RID is |
︙ | ︙ | |||
16254 16255 16256 16257 16258 16259 16260 16261 16262 16263 16264 16265 16266 16267 | This type is named fsl_confdb_e (not the "db" part) because 3 of the 4 fossil-supported config storage backends are databases. The "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 { /** Signfies the global-level (per system user) configuration area. */ FSL_CONFDB_GLOBAL = 1, /** Signfies the repository-level configuration area. */ | > > | 16572 16573 16574 16575 16576 16577 16578 16579 16580 16581 16582 16583 16584 16585 16586 16587 | This type is named fsl_confdb_e (not the "db" part) because 3 of the 4 fossil-supported config storage backends are databases. The "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, /** Signfies the repository-level configuration area. */ |
︙ | ︙ | |||
16319 16320 16321 16322 16323 16324 16325 | configuration role. Results are undefined if mode is an invalid value. 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). */ | | | | | | | | | 16639 16640 16641 16642 16643 16644 16645 16646 16647 16648 16649 16650 16651 16652 16653 16654 16655 16656 16657 16658 16659 16660 16661 16662 16663 16664 16665 16666 16667 16668 16669 16670 16671 16672 16673 16674 16675 16676 16677 16678 16679 16680 16681 16682 16683 16684 16685 16686 16687 16688 16689 16690 16691 16692 16693 16694 16695 16696 16697 16698 16699 16700 16701 16702 16703 16704 16705 | configuration role. Results are undefined if mode is an invalid value. 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); /** 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, 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, 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_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, 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, 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 assigned the length of the returned string, in bytes (and is set to 0 if NULL is returned). Any errors encounters while looking for the entry are suppressed and NULL is returned. The returned memory must eventually be freed using fsl_free(). If len is not NULL then it is set to the length of the returned string. Returns NULL for any sort of error or for a NULL db value. 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, char const * key, fsl_size_t * len ); /** The fsl_buffer-type counterpart of fsl_config_get_int32(). Replaces the contents of the given buffer (re-using any memory it |
︙ | ︙ | |||
16411 16412 16413 16414 16415 16416 16417 | In the grand scheme of things, the inability to load a setting is not generally an error, so clients are not expected to treat it as fatal unless perhaps it returns FSL_RC_OOM, in which case it likely is. @see fsl_config_has_versionable() */ | | | | 16731 16732 16733 16734 16735 16736 16737 16738 16739 16740 16741 16742 16743 16744 16745 16746 | In the grand scheme of things, the inability to load a setting is not generally an error, so clients are not expected to treat it as 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 ); /** 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 setting: {CHECKOUT_ROOT}/.fossil-settings/{key} |
︙ | ︙ | |||
16465 16466 16467 16468 16469 16470 16471 | Potential TODO: if mode is FSL_CONFDB_VERSIONABLE and the key contains directory components, e,g, "app/x", we should arguably use 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() */ | | | | | | | | | | | | > > | | 16785 16786 16787 16788 16789 16790 16791 16792 16793 16794 16795 16796 16797 16798 16799 16800 16801 16802 16803 16804 16805 16806 16807 16808 16809 16810 16811 16812 16813 16814 16815 16816 16817 16818 16819 16820 16821 16822 16823 16824 16825 16826 16827 16828 16829 16830 16831 16832 16833 16834 16835 16836 16837 16838 16839 16840 16841 16842 16843 16844 16845 16846 16847 16848 16849 16850 16851 16852 16853 16854 16855 16856 16857 16858 16859 16860 16861 16862 16863 16864 16865 16866 16867 16868 16869 16870 16871 16872 16873 16874 16875 16876 16877 16878 16879 16880 16881 16882 16883 16884 16885 16886 16887 16888 16889 16890 16891 16892 16893 16894 16895 16896 16897 16898 16899 16900 16901 16902 16903 16904 16905 16906 16907 16908 16909 16910 16911 | Potential TODO: if mode is FSL_CONFDB_VERSIONABLE and the key contains directory components, e,g, "app/x", we should arguably use 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 ); /** 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. 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, 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, 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, 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, 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, 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, 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 contain that key. Returns FSL_RC_UNSUPPORTED, without side effects, if mode is FSL_CONFDB_VERSIONABLE. It "could" hypothetically remove a 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, char const * key ); /** Begins (or recurses) a transaction on the given configuration database. Returns 0 on success, non-0 on error. On success, fsl_config_transaction_end() must eventually be called with the same parameters to pop the transaction stack. Returns FSL_RC_MISUSE if no db handle is opened for the given configuration mode. Assuming all arguments are valid, this returns the result of fsl_db_transaction_end() and propagates any db-side error into the f object's error state. This is primarily intended as an optimization when an app is making many changes to a config database. It is not needed when 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); /** 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 continue (if recursive) or committed immediately (if not recursive). Returns 0 on success, non-0 on error. Returns FSL_RC_MISUSE if no db handle is opened for the given configuration mode. Assuming all arguments are valid, this returns the result of fsl_db_transaction_end() and propagates 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); /** 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 - they are simply skipped. Note that gets any new globs appended to it, as per fsl_glob_list_append(), as opposed to replacing any existing contents. 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. */ FSL_EXPORT int fsl_config_globs_load(fsl_cx * const f, fsl_list * const 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 modified. On success, ownership of *zOut is transferred to the caller, who must eventually free it using fsl_free(). |
︙ | ︙ | |||
16606 16607 16608 16609 16610 16611 16612 | 5) Fall back to $HOME/.fossil (historical name). Except where listed above, this function does not check whether the file already exists or is a database. Windows: | | < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 16928 16929 16930 16931 16932 16933 16934 16935 16936 16937 16938 16939 16940 16941 16942 16943 16944 16945 16946 16947 16948 16949 16950 16951 16952 16953 16954 16955 16956 16957 16958 16959 16960 16961 16962 16963 16964 16965 16966 16967 16968 16969 16970 16971 16972 16973 16974 16975 16976 16977 16978 16979 16980 16981 16982 16983 16984 16985 16986 16987 16988 16989 16990 16991 16992 16993 16994 16995 16996 16997 16998 16999 17000 17001 17002 17003 17004 17005 17006 17007 17008 17009 17010 17011 17012 17013 17014 17015 17016 17017 17018 17019 17020 17021 | 5) Fall back to $HOME/.fossil (historical name). 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. */ 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 */ /* end of file ../include/fossil-scm/fossil-confdb.h */ /* start of file ../include/fossil-scm/fossil-vpath.h */ |
︙ | ︙ | |||
16882 16883 16884 16885 16886 16887 16888 | */ #if defined(__cplusplus) extern "C" { #endif | | | | | | | | | | | | | | | | | | | | | | | | | | < < | < | > | | | | | | > | | | | | > > > > > > > > > | | | | | | > | | | | | < < < > > > | | | | | | < | | | | | 17278 17279 17280 17281 17282 17283 17284 17285 17286 17287 17288 17289 17290 17291 17292 17293 17294 17295 17296 17297 17298 17299 17300 17301 17302 17303 17304 17305 17306 17307 17308 17309 17310 17311 17312 17313 17314 17315 17316 17317 17318 17319 17320 17321 17322 17323 17324 17325 17326 17327 17328 17329 17330 17331 17332 17333 17334 17335 17336 17337 17338 17339 17340 17341 17342 17343 17344 17345 17346 17347 17348 17349 17350 17351 17352 17353 17354 17355 17356 17357 17358 17359 17360 17361 17362 17363 17364 17365 17366 17367 17368 17369 17370 17371 17372 17373 17374 17375 17376 17377 17378 17379 17380 17381 17382 17383 17384 17385 17386 17387 17388 17389 17390 17391 17392 17393 17394 17395 17396 17397 17398 17399 17400 17401 17402 17403 17404 17405 17406 17407 17408 17409 17410 17411 17412 17413 17414 17415 17416 17417 17418 17419 17420 17421 17422 17423 17424 17425 17426 17427 17428 17429 17430 17431 17432 17433 17434 17435 17436 17437 17438 17439 17440 17441 17442 17443 17444 17445 17446 17447 17448 17449 17450 17451 17452 17453 17454 17455 17456 17457 17458 17459 17460 17461 17462 17463 17464 17465 17466 17467 17468 17469 17470 17471 17472 17473 17474 17475 17476 17477 17478 17479 17480 17481 17482 17483 17484 17485 17486 17487 17488 17489 17490 17491 17492 17493 17494 17495 17496 17497 17498 17499 17500 17501 17502 17503 17504 17505 17506 17507 17508 17509 17510 17511 17512 17513 17514 17515 17516 17517 17518 17519 17520 17521 17522 17523 17524 17525 17526 17527 17528 17529 17530 17531 17532 17533 17534 17535 17536 17537 17538 17539 17540 17541 17542 | */ #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; /** @internal 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 { /** 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. */ #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 the instance lives). */ 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; }; /** @internal Empty-initialized fsl__pq struct, intended for const-copy initialization. */ #define fsl__pq_empty_m {0,0,NULL} /** @internal Empty-initialized fsl__pq struct, intended for copy initialization. */ 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); /** @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, 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); /** @internal Holds one "line" of a fsl__bccache cache. */ struct fsl__bccache_line { /** RID of the cached record. */ fsl_id_t rid; /** Age. Newer is larger. */ fsl_uint_t age; /** Content of the artifact. */ fsl_buffer content; }; /** @internal Empty-initialized fsl__bccache_line structure. */ #define fsl__bccache_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. "bc" ==> blob cache. 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. */ struct fsl__bccache { /** Total amount of buffer memory (in bytes) used by cached content. This does not account for memory held by this->list. */ unsigned 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; /** 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. */ uint16_t usedLimit; /** Number of allocated slots in this->list. */ uint16_t capacity; /** Next cache counter age. Higher is newer. */ fsl_uint_t nextAge; /** List of cached content, ordered by age. */ fsl__bccache_line * list; /** RIDs of all artifacts currently in the cache. */ fsl_id_bag inCache; /** RIDs of known-missing content. */ fsl_id_bag missing; /** RIDs of known-existing content (not necessarily in the cache). */ 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 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*/,\ 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*/} \ } /** @internal Empty-initialized fsl__bccache structure, intended for copy initialization. */ extern const fsl__bccache fsl__bccache_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(). The array members in this struct MUST have the same length or results are undefined. */ struct fsl__mcache { /** Next age value. No clue how the cache will react once this overflows. */ fsl_uint_t nextAge; /** 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. */ fsl_deck decks[4]; }; /** Convenience typedef. */ typedef struct fsl__mcache fsl__mcache; /** 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}\ } /** Initialized-with-defaults fsl__mcache structure, intended for non-const copy initialization. */ extern const fsl__mcache fsl__mcache_empty; /* The fsl_cx class is documented in main public header. */ struct fsl_cx { /** A pointer to the "main" db handle. Exactly which db IS the main db is, because we have three DBs, not generally knowble. |
︙ | ︙ | |||
17322 17323 17324 17325 17326 17327 17328 | /** 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. | | | | | > > > > > > > | 17726 17727 17728 17729 17730 17731 17732 17733 17734 17735 17736 17737 17738 17739 17740 17741 17742 17743 17744 17745 17746 17747 17748 17749 17750 17751 17752 17753 17754 17755 17756 17757 17758 17759 17760 17761 17762 17763 17764 17765 17766 17767 17768 17769 17770 17771 17772 17773 17774 17775 17776 17777 17778 17779 17780 17781 17782 17783 17784 17785 17786 17787 17788 17789 17790 17791 17792 17793 | /** 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() */ struct { /** Strictly-internal temporary buffers we intend to reuse many times, mostly for filename canonicalization, holding hash values, and small encoding/decoding tasks. These must never be used for values which will be long-lived, nor are they intended to be used for large content, e.g. reading files, with the possible exception of holding versioned config settings, as those are typically rather small. 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 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 half a nanosecond, making it O(1) instead of O(small N) for the common case. */ short next; } scratchpads; /** A bitwise copy of the config object passed to fsl_cx_init() (or some default). */ fsl_cx_config cxConfig; /** Flags, some (or one) of which is runtime-configurable by the 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; /** A place for caching generic things. |
︙ | ︙ | |||
17400 17401 17402 17403 17404 17405 17406 | Whether or not a running commit process should be marked as private. This flag is used for communicating this flag through multiple levels of API. */ bool markPrivate; /** | | | | 17811 17812 17813 17814 17815 17816 17817 17818 17819 17820 17821 17822 17823 17824 17825 17826 | Whether or not a running commit process should be marked as private. This flag is used for communicating this flag through multiple levels of API. */ bool markPrivate; /** 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 processed by manifest crosslinking. This will only be relevant if/when the sync protocol is implemented. |
︙ | ︙ | |||
17479 17480 17481 17482 17483 17484 17485 | Record ID of rcvfrom entry during commits. This is likely to remain unused in libf until/unless the sync protocol is implemented. */ fsl_id_t rcvId; /** | | | | 17890 17891 17892 17893 17894 17895 17896 17897 17898 17899 17900 17901 17902 17903 17904 17905 17906 | Record ID of rcvfrom entry during commits. This is likely to remain unused in libf until/unless the sync protocol is implemented. */ fsl_id_t rcvId; /** fsl_content_get() cache. */ fsl__bccache blobContent; /** 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. */ fsl_id_bag mfSeen; /** |
︙ | ︙ | |||
17521 17522 17523 17524 17525 17526 17527 | two functions in some cases. */ fsl_fstat fstat; /** Parsed-deck cache. */ | | | 17932 17933 17934 17935 17936 17937 17938 17939 17940 17941 17942 17943 17944 17945 17946 | two functions in some cases. */ fsl_fstat fstat; /** Parsed-deck cache. */ 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 but the library internals _generally_ have no business using them. |
︙ | ︙ | |||
17637 17638 17639 17640 17641 17642 17643 17644 17645 17646 17647 17648 17649 17650 17651 17652 17653 17654 17655 17656 | fsl_buffer_empty_m,fsl_buffer_empty_m, \ fsl_buffer_empty_m,fsl_buffer_empty_m}, \ {false,false,false,false,false,false}, \ 0/*next*/ \ }, \ fsl_cx_config_empty_m /*cxConfig*/, \ FSL_CX_F_DEFAULTS/*flags*/, \ fsl_xlinker_list_empty_m/*xlinkers*/, \ {/*cache*/ \ false/*caseInsensitive*/, \ false/*ignoreDephantomizations*/, \ false/*markPrivate*/, \ false/*isCrosslinking*/, \ false/*xlinkClustersOnly*/, \ false/*inFinalVerify*/, \ -1/*allowSymlinks*/, \ -1/*seenDeltaManifest*/, \ -1/*searchIndexExists*/, \ -1/*manifestSetting*/,\ 0/*rcvId*/, \ | > | | | | | | > > | > > > > > > > > | | | 18048 18049 18050 18051 18052 18053 18054 18055 18056 18057 18058 18059 18060 18061 18062 18063 18064 18065 18066 18067 18068 18069 18070 18071 18072 18073 18074 18075 18076 18077 18078 18079 18080 18081 18082 18083 18084 18085 18086 18087 18088 18089 18090 18091 18092 18093 18094 18095 18096 18097 18098 18099 18100 18101 18102 18103 18104 18105 18106 18107 18108 18109 18110 18111 18112 18113 18114 18115 18116 18117 18118 18119 18120 18121 18122 18123 18124 18125 18126 18127 18128 18129 18130 18131 18132 18133 18134 18135 18136 18137 18138 18139 18140 18141 18142 18143 18144 18145 18146 18147 18148 18149 18150 18151 18152 18153 18154 18155 18156 18157 18158 18159 18160 18161 18162 18163 18164 18165 18166 18167 18168 18169 18170 18171 18172 18173 | fsl_buffer_empty_m,fsl_buffer_empty_m, \ fsl_buffer_empty_m,fsl_buffer_empty_m}, \ {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*/, \ false/*isCrosslinking*/, \ false/*xlinkClustersOnly*/, \ false/*inFinalVerify*/, \ -1/*allowSymlinks*/, \ -1/*seenDeltaManifest*/, \ -1/*searchIndexExists*/, \ -1/*manifestSetting*/,\ 0/*rcvId*/, \ fsl__bccache_empty_m/*blobContent*/, \ 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*/, \ {/*globs*/ \ fsl_list_empty_m/*ignore*/, \ fsl_list_empty_m/*binary*/, \ fsl_list_empty_m/*crnl*/ \ } \ }/*cache*/, \ {/*ticket*/ \ fsl_list_empty_m/*customFields*/, \ 0/*hasTicket*/, \ 0/*hasCTime*/, \ 0/*hasChng*/, \ 0/*hasCngRid*/ \ } \ } /** @internal Initialized-with-defaults fsl_cx instance. */ extern const fsl_cx fsl_cx_empty; /* TODO: int fsl_buffer_append_getenv( fsl_buffer * b, char const * env ) Fetches the given env var and appends it to b. Returns FSL_RC_NOT_FOUND if the env var is not set. The primary use for this would be to simplify the Windows implementation of fsl_find_home_dir(). */ /** @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); /** @internal Add an entry to the content cache. This routines transfers the contents of pBlob over to c, 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. 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); /** @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); /** @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 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 errors," e.g. FSL_RC_OOM if a cache allocation fails. This operation may update the cache's contents. 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); /** @internal This is THE ONLY routine which adds content to the blob table. This writes the given buffer content into the repository database's blob tabe. |