Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From 0.6 To 0.5
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 |
︙ | ︙ |
Deleted GNUmakefile.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
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.5 # 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 \ |
︙ | ︙ | |||
34 35 36 37 38 39 40 41 42 43 44 45 46 47 | -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 \ | > > > | | | | | | | | | | 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 | -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/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 $(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: |
︙ | ︙ |
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.4 ## A read-only ncurses browser for [Fossil][0] repositories in the terminal. `fnc` uses [libfossil][1] to create a [`fossil ui`][2] experience in the terminal. Tested and confirmed to run on the following x64 systems (additional platforms noted inline): 1. OpenBSD 6.8- and 6.9-release 2. macOS 10.15.7 (Catalina) and 11.5.2 (Big Sur) 3. Linux Mint 20.2 (32- and 64-bit ARM) 4. Ubuntu 18.04 running Linux kernel 5.11 (32-bit ARM) 5. Debian GNU/Linux 8, 9, and 10 |
︙ | ︙ |
Deleted include/settings.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
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.0.1-alphabeta" /* 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 "acbd39275f0d573a74d996caea7918afafff2ff7" #define FSL_LIB_VERSION_TIMESTAMP "2021-10-08 03:15:51.855 UTC" #define FSL_LIB_CONFIG_TIME "2021-10-08 04:07 GMT" #if defined(_MSC_VER) #define FSL_PLATFORM_OS "windows" #define FSL_PLATFORM_IS_WINDOWS 1 #define FSL_PLATFORM_IS_UNIX 0 #define FSL_PLATFORM_PLATFORM "windows" #define FSL_PLATFORM_PATH_SEPARATOR ";" #define FSL_CHECKOUTDB_NAME "./_FOSSIL_" |
︙ | ︙ | |||
132 133 134 135 136 137 138 | #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 | < < < < < < < < < < < | 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_ */ |
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_acache fsl_acache_empty = fsl_acache_empty_m; const fsl_branch_opt fsl_branch_opt_empty = fsl_branch_opt_empty_m; const fsl_buffer fsl_buffer_empty = fsl_buffer_empty_m; const fsl_card_F fsl_card_F_empty = fsl_card_F_empty_m; const fsl_card_F_list fsl_card_F_list_empty = fsl_card_F_list_empty_m; const fsl_card_J fsl_card_J_empty = fsl_card_J_empty_m; 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 | 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_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 = |
︙ | ︙ | |||
158 159 160 161 162 163 164 | case FSL_STRLEN_K256: return x; default: return 0; } } void fsl_error_clear( fsl_error * const err ){ | > | | > > | | | | | > > | | 157 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 184 185 186 187 | case FSL_STRLEN_K256: return x; default: return 0; } } void fsl_error_clear( fsl_error * const err ){ if(err){ fsl_buffer_clear(&err->msg); *err = fsl_error_empty; } } void fsl_error_reset( fsl_error * const err ){ if(err){ err->code = 0; err->msg.used = err->msg.cursor = 0; if(err->msg.mem) err->msg.mem[0] = 0; } } int fsl_error_copy( fsl_error const * const src, fsl_error * const dest ){ if(!src || !dest || (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 ); } |
︙ | ︙ | |||
191 192 193 194 195 196 197 | 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 ){ | > | | 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | 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(!err) return FSL_RC_MISUSE; else if(!code){ /* clear error state */ err->code = 0; err->msg.used = err->msg.cursor = 0; if(err->msg.mem){ err->msg.mem[0] = 0; } return 0; }else{ |
︙ | ︙ | |||
225 226 227 228 229 230 231 | va_end(args); return rc; } int fsl_error_get( fsl_error const * const err, char const ** str, fsl_size_t * const len ){ | > > | | | | | > | 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | va_end(args); return rc; } int fsl_error_get( fsl_error const * const err, char const ** str, fsl_size_t * const len ){ if(!err) return FSL_RC_MISUSE; else{ if(str) *str = err->msg.used ? (char const *)err->msg.mem : NULL; if(len) *len = err->msg.used; return err->code; } } char const * fsl_rc_cstr(int rc){ fsl_rc_e const RC = (fsl_rc_e)rc /* we do this so that gcc will warn if the switch() below is missing any fsl_rc_e entries. */ |
︙ | ︙ | |||
257 258 259 260 261 262 263 | STR(DELTA_INVALID_SEPARATOR); STR(DELTA_INVALID_SIZE); STR(DELTA_INVALID_TERMINATOR); STR(DIFF_BINARY); STR(DIFF_WS_ONLY); STR(end); STR(ERROR); | < | 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); |
︙ | ︙ | |||
394 395 396 397 398 399 400 | } } char * fsl_strdup( char const * src ){ return fsl_strndup(src, -1); } | | | | | | 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 430 431 432 433 434 435 436 | } } char * fsl_strdup( char const * src ){ return fsl_strndup(src, -1); } size_t fsl_strlcpy(char *restrict dst, const char *restrict src, size_t dstsz){ size_t offset = 0; if(dstsz<1){ goto end; } while((*(dst+offset) = *(src+offset))!='\0'){ if(++offset == dstsz){ --offset; break; } } end: *(dst+offset) = '\0'; while(*(src+offset)!='\0'){ ++offset; /* Return src length. */ } return offset; } size_t fsl_strlcat(char *restrict dst, const char *restrict src, size_t dstsz){ size_t offset; int dstlen, srclen, idx = 0; offset = dstlen = fsl_strlen(dst); srclen = fsl_strlen(src); while((*(dst+offset++) = *(src+idx++))!='\0'){ if(offset==dstsz-1){ |
︙ | ︙ | |||
498 499 500 501 502 503 504 | } if(0==fsl_strncmp(buf, "off", 3)) return false; return true; } } } | | | 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 | } if(0==fsl_strncmp(buf, "off", 3)) return false; return true; } } } char * fsl_guess_user_name(){ char const ** e; static char const * list[] = { "FOSSIL_USER", #if defined(_WIN32) "USERNAME", #else "USER", |
︙ | ︙ | |||
531 532 533 534 535 536 537 | rv = kludge; break; } } return rv; } | | | | 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 | 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){ |
︙ | ︙ | |||
938 939 940 941 942 943 944 | #endif } char fsl_isatty(int fd){ return isatty(fd) ? 1 : 0; } | | | 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 | #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; |
︙ | ︙ | |||
969 970 971 972 973 974 975 | 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 | | | 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 | 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 |
︙ | ︙ | |||
1057 1058 1059 1060 1061 1062 1063 | /* ** The status of an annotation operation is recorded by an instance ** of the following structure. */ typedef struct Annotator Annotator; struct Annotator { | | | 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 | /* ** 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; |
︙ | ︙ | |||
1081 1082 1083 1084 1085 1086 1087 | 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 = { | | | 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 | 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*/, |
︙ | ︙ | |||
1425 1426 1427 1428 1429 1430 1431 | } int fsl_annotate( fsl_cx * const f, fsl_annotate_opt const * const opt ){ int rc; Annotator ann = Annotator_empty; unsigned int i; | | | 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 | } 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)); |
︙ | ︙ | |||
1480 1481 1482 1483 1484 1485 1486 | aStep.stepType = FSL_ANNOTATE_STEP_LIMITED; aStep.mtime = 0.0; } rc = opt->out(opt->outState, opt, &aStep); } end: | | | 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 | 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 */ |
︙ | ︙ | |||
2040 2041 2042 2043 2044 2045 2046 | 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; | < | | < | 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 | 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; if( ! str ) return 0; ch = *str; while( 0==rc && ch ){ if( ch == '%' ){ ch = *(++str); ch2 = *(++str); if( isxdigit((int)ch) && isxdigit((int)ch2) ) { decoded = (hexchar_to_int( ch ) * 16) + hexchar_to_int( ch2 ); |
︙ | ︙ | |||
2074 2075 2076 2077 2078 2079 2080 | }else if( ch == '+' ){ xbuf[0] = ' '; xbuf[1] = 0; rc = pf( pfArg, xbuf, 1 ); ch = *(++str); continue; } | < | 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; } |
︙ | ︙ | |||
2106 2107 2108 2109 2110 2111 2112 | 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 = | | < < | < < < | < < | 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 | 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==etSQLESCAPE3); if( isnull ){ escarg = (xtype==etSQLESCAPE2||xtype==etSQLESCAPE3) ? "NULL" : "(NULL)"; } 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 ); |
︙ | ︙ | |||
3009 3010 3011 3012 3013 3014 3015 | } #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, | | | < > | < | 3006 3007 3008 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 | } #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<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<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; bufpt = NULL; pfrc = spech_urldecode( pfAppend, pfAppendArg, (precision<length) ? precision : length, bufpt ); FSLPRINTF_CHECKERR; length = 0; break; } #endif /* FSLPRINTF_OMIT_HTML */ #if ! FSLPRINTF_OMIT_SQL case etBLOBSQL: |
︙ | ︙ | |||
3058 3059 3060 3061 3062 3063 3064 | 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, | | | 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 | 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<length) ? precision : length, bufpt ); FSLPRINTF_CHECKERR; length = 0; } #endif /* !FSLPRINTF_OMIT_SQL */ }/* End switch over the format type */ /* |
︙ | ︙ | |||
4016 4017 4018 4019 4020 4021 4022 | } 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); } | | < | 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 | } 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 * src, fsl_buffer * dest ){ 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, |
︙ | ︙ | |||
4353 4354 4355 4356 4357 4358 4359 | /* Only for debugging */ #include <stdio.h> #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) | | | | | | | | | | < < | | | | | | < < < | | | | | < < < < < < < < < < < < < < < | | | 4348 4349 4350 4351 4352 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 | /* 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_acache_expire_oldest(fsl_acache * c){ static uint16_t const sentinel = 0xFFFF; uint16_t i; fsl_int_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 -= (uint32_t)c->list[mn].content.capacity; fsl_buffer_clear(&c->list[mn].content); --c->used; c->list[mn] = c->list[c->used]; } return sentinel!=mn; } int fsl_acache_insert(fsl_acache * c, fsl_id_t rid, fsl_buffer *pBlob){ fsl_acache_line *p; if( c->used>c->usedLimit || c->szTotal>c->szLimit ){ fsl_size_t szBefore; do{ szBefore = c->szTotal; fsl_acache_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_acache::capacity to a larger int type."); } if(!remem){ fsl_buffer_clear(pBlob) /* for consistency */; return FSL_RC_OOM; } c->capacity = cap; c->list = (fsl_acache_line*)remem; } p = &c->list[c->used++]; p->rid = rid; p->age = c->nextAge++; c->szTotal += pBlob->capacity; p->content = *pBlob /* Transfer ownership */; *pBlob = fsl_buffer_empty; return fsl_id_bag_insert(&c->inCache, rid); } void fsl_acache_clear(fsl_acache * c){ #if 0 while(fsl_acache_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_acache_empty; } int fsl_acache_check_available(fsl_cx * f, fsl_id_t rid){ fsl_id_t srcid; int depth = 0; /* Limit to recursion depth */ static const int limit = 10000000 /* historical value */; int rc; fsl_acache * c = &f->cache.arty; 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) ){ |
︙ | ︙ | |||
4542 4543 4544 4545 4546 4547 4548 | 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){ | | | | | | | | | 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 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 | 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)", |
︙ | ︙ | |||
4630 4631 4632 4633 4634 4635 4636 | 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: | | | 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 | 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; } |
︙ | ︙ | |||
4718 4719 4720 4721 4722 4723 4724 | 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; | | | 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 | 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){ |
︙ | ︙ | |||
4770 4771 4772 4773 4774 4775 4776 | } end: if(opt->vfileIds){ assert(!canon); assert(!_vfileIds.list); }else{ assert(canon); | | | | | | | 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 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 | } 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){ |
︙ | ︙ | |||
5000 5001 5002 5003 5004 5005 5006 | assert(!mergeRid); /* An unselected ADDed file. Skip it. */ continue; } if(isExe) perm = FSL_FILE_PERM_EXE; else if(isLink){ | | | | 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 | 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: |
︙ | ︙ | |||
5670 5671 5672 5673 5674 5675 5676 | "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); | | | | 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 | "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")); |
︙ | ︙ | |||
5735 5736 5737 5738 5739 5740 5741 | 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); | | | 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 | 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; |
︙ | ︙ | |||
5827 5828 5829 5830 5831 5832 5833 | } #endif else{ char const * zLocalRoot; char const * zFull; fsl_size_t nLocalRoot; fsl_size_t nFull; | | | 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 | } #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; |
︙ | ︙ | |||
5897 5898 5899 5900 5901 5902 5903 | } if(pOut){ rc = fsl_buffer_append(pOut, zFull + nLocalRoot, nFull - nLocalRoot); } end: | | | | 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 | } 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); |
︙ | ︙ | |||
5993 5994 5995 5996 5997 5998 5999 | 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{ | | | 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 | 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; |
︙ | ︙ | |||
6163 6164 6165 6166 6167 6168 6169 | 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"); | | | 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 | 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); |
︙ | ︙ | |||
6196 6197 6198 6199 6200 6201 6202 | 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. */; | | | | | | | 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 | 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 *f, fsl_db * db, char const * tableName, fsl_id_bag const * bag){ fsl_stmt insId = fsl_stmt_empty; int rc = fsl_db_exec_multi(db, "CREATE TEMP TABLE IF NOT EXISTS " "[%s](id); " "DELETE FROM [%s] /* %s() */;", |
︙ | ︙ | |||
6254 6255 6256 6257 6258 6259 6260 | return rc; dberr: assert(rc); rc = fsl_cx_uplift_db_error2(f, db, rc); goto end; } | | | 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 | return rc; dberr: assert(rc); rc = fsl_cx_uplift_db_error2(f, db, rc); goto end; } int fsl_ckout_unmanage(fsl_cx * f, fsl_ckout_unmanage_opt const * opt){ int rc; fsl_db * const db = fsl_needs_ckout(f); fsl_buffer * fname = 0; fsl_id_t const vid = f->ckout.rid; fsl_stmt q = fsl_stmt_empty; bool inTrans = false; if(!db) return FSL_RC_NOT_A_CKOUT; |
︙ | ︙ | |||
6293 6294 6295 6296 6297 6298 6299 | "AND deleted " "AND id IN fx_unmanage_id " "/* %s() */", vid, __func__); if(rc) goto dberr; } }else{// Process opt->filename | | | 6268 6269 6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 | "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){ |
︙ | ︙ | |||
6350 6351 6352 6353 6354 6355 6356 | /* 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: | | | | | 6325 6326 6327 6328 6329 6330 6331 6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 | /* 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 * f){ return fsl_vfile_changes_scan(f, -1, 0); } int fsl_ckout_install_schema(fsl_cx *f, bool dropIfExists){ char const * tNames[] = { "vvar", "vfile", "vmerge", 0 }; int rc; fsl_db * const db = fsl_needs_ckout(f); if(!db) return f->error.code; if(dropIfExists){ |
︙ | ︙ | |||
6414 6415 6416 6417 6418 6419 6420 | 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__); } | | | 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 | 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; |
︙ | ︙ | |||
6492 6493 6494 6495 6496 6497 6498 | 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); } } | | | | 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 | 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! |
︙ | ︙ | |||
6525 6526 6527 6528 6529 6530 6531 | } } 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). */ | | | | 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 | } } 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)); |
︙ | ︙ | |||
6567 6568 6569 6570 6571 6572 6573 | } 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! */; } | | | < | | 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 | } 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 * 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, |
︙ | ︙ | |||
6636 6637 6638 6639 6640 6641 6642 | mod = 0x10; }else{ rc = fsl_cx_err_set(f, rc, "%s(): stat() failed for file: %s", __func__, zFilename); } goto end; } | | | | | | 6610 6611 6612 6613 6614 6615 6616 6617 6618 6619 6620 6621 6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 | 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(). */ |
︙ | ︙ | |||
6870 6871 6872 6873 6874 6875 6876 | : FSL_CRESPONSE_YES; // Overwrite it coState.fileChangeType = isSameFile ? FSL_CKUP_FCHANGE_NONE : FSL_CKUP_FCHANGE_UPDATED; break; } default: | | | 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 | : 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. |
︙ | ︙ | |||
6995 6996 6997 6998 6999 7000 7001 | 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; | | | 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 | 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; |
︙ | ︙ | |||
7110 7111 7112 7113 7114 7115 7116 | 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: | | | 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 | 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); |
︙ | ︙ | |||
7172 7173 7174 7175 7176 7177 7178 | anything but .fslckout and any non-SCM'd content. */; } if(FSL_RC_STEP_DONE==rc) rc = 0; } end: fsl_stmt_finalize(&q); | | | | 7146 7147 7148 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7160 7161 7162 7163 7164 7165 7166 7167 7168 7169 7170 7171 7172 7173 7174 | 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 |
︙ | ︙ | |||
7277 7278 7279 7280 7281 7282 7283 | 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); | | | | 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 | 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; |
︙ | ︙ | |||
7515 7516 7517 7518 7519 7520 7521 | 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; | | | | | | 7489 7490 7491 7492 7493 7494 7495 7496 7497 7498 7499 7500 7501 7502 7503 7504 7505 7506 7507 7508 7509 7510 7511 7512 7513 7514 7515 7516 7517 7518 7519 7520 | 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; |
︙ | ︙ | |||
7617 7618 7619 7620 7621 7622 7623 | 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 ){ | | | | | 7591 7592 7593 7594 7595 7596 7597 7598 7599 7600 7601 7602 7603 7604 7605 7606 7607 7608 7609 7610 7611 7612 7613 7614 7615 7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626 7627 | 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 */ |
︙ | ︙ | |||
7812 7813 7814 7815 7816 7817 7818 | */ assert(!rc); rc = fsl_repo_ckout_rm_list_fini(f, &rec); if(!rc){ rc = fsl_vfile_unload_except(f, tid); } if(!rc){ | | | | | | | 7786 7787 7788 7789 7790 7791 7792 7793 7794 7795 7796 7797 7798 7799 7800 7801 7802 7803 7804 7805 7806 7807 7808 7809 | */ 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); |
︙ | ︙ | |||
7894 7895 7896 7897 7898 7899 7900 | " 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; | | | | 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 | " 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 |
︙ | ︙ | |||
7971 7972 7973 7974 7975 7976 7977 | 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; | | | 7945 7946 7947 7948 7949 7950 7951 7952 7953 7954 7955 7956 7957 7958 7959 | 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){ |
︙ | ︙ | |||
8088 8089 8090 8091 8092 8093 8094 | rc = fsl_buffer_append(b, "manifest.tags", 13); if(rc) goto end; fsl_file_unlink(fsl_buffer_cstr(b)); } end: if(wrote) *wrote = W; | | | 8062 8063 8064 8065 8066 8067 8068 8069 8070 8071 8072 8073 8074 8075 8076 | 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 |
︙ | ︙ | |||
8128 8129 8130 8131 8132 8133 8134 | 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; | | | 8102 8103 8104 8105 8106 8107 8108 8109 8110 8111 8112 8113 8114 8115 8116 | 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); |
︙ | ︙ | |||
8161 8162 8163 8164 8165 8166 8167 | *errLen = j; break; } z[j] = '/'; i = j; } end: | | | | | | 8135 8136 8137 8138 8139 8140 8141 8142 8143 8144 8145 8146 8147 8148 8149 8150 8151 8152 8153 8154 8155 8156 8157 8158 8159 8160 8161 8162 8163 8164 8165 8166 8167 8168 8169 8170 8171 8172 8173 8174 8175 8176 | *errLen = j; break; } z[j] = '/'; i = j; } end: fsl_cx_scratchpad_yield(f, fn); return rc; } int fsl_ckout_safe_file_check(fsl_cx *f, char const * zFilename){ if(!fsl_needs_ckout(f)) return FSL_RC_NOT_A_CKOUT; int rc = 0; fsl_buffer * const fn = fsl_cx_scratchpad(f); 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, |
︙ | ︙ | |||
8211 8212 8213 8214 8215 8216 8217 | rc = fsl_cx_err_set(f, FSL_RC_RANGE, "Path is not rooted " "in the current checkout: %s", zAbsPath); } return rc; } | | | | | | | 8185 8186 8187 8188 8189 8190 8191 8192 8193 8194 8195 8196 8197 8198 8199 8200 8201 8202 8203 8204 8205 8206 8207 8208 8209 8210 8211 8212 8213 8214 8215 8216 8217 8218 | 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 * 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(). |
︙ | ︙ | |||
8269 8270 8271 8272 8273 8274 8275 | /** 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; | | | | < | 8243 8244 8245 8246 8247 8248 8249 8250 8251 8252 8253 8254 8255 8256 8257 8258 8259 8260 8261 8262 8263 8264 8265 8266 8267 8268 8269 8270 8271 8272 8273 8274 8275 8276 8277 8278 8279 8280 8281 | /** 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 * 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. |
︙ | ︙ | |||
8316 8317 8318 8319 8320 8321 8322 | 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){ | | | | | 8289 8290 8291 8292 8293 8294 8295 8296 8297 8298 8299 8300 8301 8302 8303 8304 8305 8306 8307 8308 8309 8310 8311 8312 8313 8314 8315 8316 8317 8318 8319 8320 8321 8322 8323 8324 8325 8326 8327 | 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; |
︙ | ︙ | |||
8374 8375 8376 8377 8378 8379 8380 | " OR rid=0" " OR coalesce(origname,pathname)" " <>pathname" ")", -1); } assert(!rc); rc = fsl_db_prepare(db, &q, "%b /* %s() */", sql, __func__); | | | | 8347 8348 8349 8350 8351 8352 8353 8354 8355 8356 8357 8358 8359 8360 8361 8362 8363 8364 8365 | " 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); |
︙ | ︙ | |||
8404 8405 8406 8407 8408 8409 8410 | 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 | | | 8377 8378 8379 8380 8381 8382 8383 8384 8385 8386 8387 8388 8389 8390 8391 | 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)){ |
︙ | ︙ | |||
8430 8431 8432 8433 8434 8435 8436 | "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) | | | | 8403 8404 8405 8406 8407 8408 8409 8410 8411 8412 8413 8414 8415 8416 8417 8418 8419 8420 | "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 |
︙ | ︙ | |||
8455 8456 8457 8458 8459 8460 8461 | char const * name = renamed ? zNameOrig : zName; rc = opt->callback(&name[f->ckout.dirLen], changeType, opt->callbackState); if(rc) break; } }/*step() loop*/ end: | | | | 8428 8429 8430 8431 8432 8433 8434 8435 8436 8437 8438 8439 8440 8441 8442 8443 | 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__); |
︙ | ︙ | |||
8481 8482 8483 8484 8485 8486 8487 | return rc; dberr: assert(rc); rc = fsl_cx_uplift_db_error2(f, db, rc); goto end; } | | | | | | | 8454 8455 8456 8457 8458 8459 8460 8461 8462 8463 8464 8465 8466 8467 8468 8469 8470 8471 8472 8473 8474 8475 8476 8477 8478 8479 8480 8481 8482 8483 8484 8485 8486 8487 8488 8489 | return rc; dberr: assert(rc); rc = fsl_cx_uplift_db_error2(f, db, rc); goto end; } int fsl_ckout_vfile_ids( fsl_cx * f, fsl_id_t vid, fsl_id_bag * dest, char const * zName, bool relativeToCwd, bool changedOnly ) { if(!fsl_needs_ckout(f)) return FSL_RC_NOT_A_CKOUT; fsl_buffer * const canon = fsl_cx_scratchpad(f); 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]){ |
︙ | ︙ | |||
8532 8533 8534 8535 8536 8537 8538 | if(rc){ rc = fsl_cx_err_set(f, rc, "%s error reading file; %b", fsl_rc_cstr(rc), fname); } } } } | | | 8505 8506 8507 8508 8509 8510 8511 8512 8513 8514 8515 8516 8517 8518 8519 | 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, |
︙ | ︙ | |||
8709 8710 8711 8712 8713 8714 8715 | #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? */ | | | 8682 8683 8684 8685 8686 8687 8688 8689 8690 8691 8692 8693 8694 8695 8696 | #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) |
︙ | ︙ | |||
9225 9226 9227 9228 9229 9230 9231 | /** 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){ | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 9198 9199 9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 9213 9214 9215 9216 9217 9218 9219 9220 9221 9222 9223 9224 9225 9226 9227 9228 9229 9230 9231 9232 9233 9234 | /** 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*/ }; 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); } /** 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; |
︙ | ︙ | |||
9358 9359 9360 9361 9362 9363 9364 | } } if(!rc){ char const * userName = fcli.transient.userArg; if(userName){ fsl_cx_user_set(f, userName); }else if(!fsl_cx_user_get(f)){ | | | 9294 9295 9296 9297 9298 9299 9300 9301 9302 9303 9304 9305 9306 9307 9308 | } } 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_guess_user_name(); fsl_cx_user_set(f, u); fsl_free(u); } } return rc; } |
︙ | ︙ | |||
9431 9432 9433 9434 9435 9436 9437 | 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 ); | < < < < < | < < | 9367 9368 9369 9370 9371 9372 9373 9374 9375 9376 9377 9378 9379 9380 9381 9382 9383 9384 9385 9386 9387 9388 9389 9390 9391 9392 9393 | 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); } } if(clear) fcli_err_reset(); return errRc; } const char * fcli_next_arg(bool remove){ const char * rc = (fcli.argc>0) ? fcli.argv[0] : NULL; if(rc && remove){ |
︙ | ︙ | |||
9898 9899 9900 9901 9902 9903 9904 | 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); } } } | < < < < < < < < < < < < < < < | 9827 9828 9829 9830 9831 9832 9833 9834 9835 9836 9837 9838 9839 9840 9841 9842 9843 9844 | 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 |
︙ | ︙ | |||
9975 9976 9977 9978 9979 9980 9981 | } } 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 ){ | | | | | | | | 9889 9890 9891 9892 9893 9894 9895 9896 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 | } } 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.arty.missing, rid) ){ return false; }else if( fsl_id_bag_contains(&f->cache.arty.available, rid) ){ return true; }else if( fsl_content_size(f, rid)<0 ){ fsl_id_bag_insert(&f->cache.arty.missing, rid) /* ignore possible OOM error */; return false; } rc = fsl_delta_src_id(f, rid, &srcid); if(rc) break; else if( 0==srcid ){ fsl_id_bag_insert(&f->cache.arty.available, rid); return true; } rid = srcid; } if(0==rc){ /* This "cannot happen" (never has historically, and would be indicative of what amounts to corruption in the repo). */ fsl_fatal(FSL_RC_RANGE,"delta-loop in repository"); } return false; } int fsl_content_blob( fsl_cx * f, fsl_id_t blobRid, fsl_buffer * tgt ){ fsl_db * const dbR = fsl_cx_db_repo(f); if(blobRid<=0) return FSL_RC_RANGE; else if(!dbR) return FSL_RC_NOT_A_REPO; else{ int rc; fsl_stmt * q = NULL; rc = fsl_db_prepare_cached( dbR, &q, |
︙ | ︙ | |||
10083 10084 10085 10086 10087 10088 10089 | 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; | | < | | < | | | 9997 9998 9999 10000 10001 10002 10003 10004 10005 10006 10007 10008 10009 10010 10011 10012 10013 10014 10015 10016 10017 10018 10019 10020 10021 10022 10023 10024 10025 10026 10027 10028 10029 10030 10031 | 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_acache * const ac = &f->cache.arty; fsl_buffer_reuse(tgt); if(fsl_id_bag_contains(&ac->missing, rid)){ /* Early out if we know the content is not available */ return FSL_RC_NOT_FOUND; } /* Look for the artifact in the cache first */ if( fsl_id_bag_contains(&ac->inCache, rid) ){ fsl_size_t i; fsl_acache_line * line; for(i=0; i<ac->used; ++i){ line = &ac->list[i]; if( line->rid==rid ){ rc = fsl_buffer_copy(&line->content, tgt); line->age = ac->nextAge++; return rc; } } } 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); |
︙ | ︙ | |||
10198 10199 10200 10201 10202 10203 10204 | 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. | < < < < | | > > > | | | | 10110 10111 10112 10113 10114 10115 10116 10117 10118 10119 10120 10121 10122 10123 10124 10125 10126 10127 10128 10129 10130 10131 10132 10133 10134 10135 10136 10137 10138 10139 10140 10141 10142 10143 10144 10145 10146 10147 10148 10149 10150 10151 10152 10153 10154 | 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])); rc = fsl_acache_insert( ac, a[n+1], tgt ); if(rc){ fsl_buffer_clear(&next); goto end_delta; } assert(!tgt->mem && "Passed to artifact cache."); }else{ fsl_buffer_clear(tgt); } #else if(mx){/*unused var*/} 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.arty.available : &f->cache.arty.missing, rid); } return rc; } } int fsl_content_get_sym( fsl_cx * const f, char const * sym, |
︙ | ︙ | |||
10253 10254 10255 10256 10257 10258 10259 | /** 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. */ | | | | | | > | | | 10164 10165 10166 10167 10168 10169 10170 10171 10172 10173 10174 10175 10176 10177 10178 10179 10180 10181 10182 10183 10184 10185 10186 10187 10188 10189 10190 10191 10192 10193 10194 10195 10196 10197 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 | /** 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 * 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.arty.available, rid) ) return 0; rc = fsl_id_bag_insert(&pending, rid); if(rc) goto end; while( (rid = fsl_id_bag_first(&pending))!=0 ){ fsl_id_bag_remove(&pending, rid); rc = fsl_id_bag_insert(&f->cache.arty.available, rid); if(rc) goto end; fsl_id_bag_remove(&f->cache.arty.missing, rid); if(!st){ rc = fsl_db_prepare_cached(db, &st, "SELECT rid FROM delta " "WHERE srcid=?" "/*%s()*/",__func__); 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 |
︙ | ︙ | |||
10326 10327 10328 10329 10330 10331 10332 | /* 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); | | | 10238 10239 10240 10241 10242 10243 10244 10245 10246 10247 10248 10249 10250 10251 10252 | /* 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, |
︙ | ︙ | |||
10352 10353 10354 10355 10356 10357 10358 | } 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); | | | 10264 10265 10266 10267 10268 10269 10270 10271 10272 10273 10274 10275 10276 10277 10278 | } 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, |
︙ | ︙ | |||
10406 10407 10408 10409 10410 10411 10412 | } end: fsl_stmt_finalize(&q); fsl_buffer_clear(&bufChild); return rc; } | | | 10318 10319 10320 10321 10322 10323 10324 10325 10326 10327 10328 10329 10330 10331 10332 | } 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; |
︙ | ︙ | |||
10587 10588 10589 10590 10591 10592 10593 | 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 || | | | 10499 10500 10501 10502 10503 10504 10505 10506 10507 10508 10509 10510 10511 10512 10513 | 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_acache_check_available(f, srcId)) ){ isDephantomize = true; rc = fsl_content_mark_available(f, rid); } } fsl_stmt_cached_yield(s1); s1 = NULL; if(rc) goto end; |
︙ | ︙ | |||
10647 10648 10649 10650 10651 10652 10653 | if(rc) rc = fsl_cx_uplift_db_error2(f, dbR, rc); fsl_stmt_cached_yield(s1); s1 = NULL; } if(rc) goto end; } if( !isDephantomize | | | | 10559 10560 10561 10562 10563 10564 10565 10566 10567 10568 10569 10570 10571 10572 10573 10574 | 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.arty.missing, rid) && (srcId==0 || (0==fsl_acache_check_available(f,srcId)))){ /* TODO: document what this is for. TODO: figure out what that is. */ rc = fsl_content_mark_available(f, rid); if(rc) goto end; } |
︙ | ︙ | |||
10679 10680 10681 10682 10683 10684 10685 | 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; } | | | | | | 10591 10592 10593 10594 10595 10596 10597 10598 10599 10600 10601 10602 10603 10604 10605 10606 10607 10608 10609 10610 10611 10612 10613 10614 10615 10616 10617 10618 10619 10620 10621 10622 10623 10624 10625 10626 10627 10628 10629 10630 10631 10632 10633 10634 10635 10636 10637 10638 10639 10640 10641 10642 10643 | 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; |
︙ | ︙ | |||
10776 10777 10778 10779 10780 10781 10782 | if(!rc) rc = fsl_stmt_step(s3); fsl_stmt_cached_yield(s3); if(FSL_RC_STEP_DONE!=rc) goto end; else rc = 0; } } | | | | 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 | 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.arty.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; |
︙ | ︙ | |||
10851 10852 10853 10854 10855 10856 10857 | 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. */ | | | | 10763 10764 10765 10766 10767 10768 10769 10770 10771 10772 10773 10774 10775 10776 10777 10778 10779 10780 10781 10782 10783 10784 10785 10786 10787 10788 10789 10790 10791 | 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; |
︙ | ︙ | |||
10913 10914 10915 10916 10917 10918 10919 | /** Undeltify srcid if needed... */ s = srcid; while( (0==(rc=fsl_delta_src_id(f, s, &s))) && (s>0) ){ if( s==rid ){ | | | 10825 10826 10827 10828 10829 10830 10831 10832 10833 10834 10835 10836 10837 10838 10839 | /** 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); |
︙ | ︙ | |||
10961 10962 10963 10964 10965 10966 10967 | else fsl_db_transaction_end(db, 1) /* keep rc intact */; } } } } fsl_stmt_cached_yield(s1); fsl_stmt_cached_yield(s2); | | | | 10873 10874 10875 10876 10877 10878 10879 10880 10881 10882 10883 10884 10885 10886 10887 10888 10889 10890 10891 10892 10893 10894 10895 10896 10897 10898 10899 10900 10901 10902 10903 | 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 * 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; |
︙ | ︙ | |||
11001 11002 11003 11004 11005 11006 11007 | */ 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); | | | 10913 10914 10915 10916 10917 10918 10919 10920 10921 10922 10923 10924 10925 10926 10927 | */ 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;" |
︙ | ︙ | |||
11222 11223 11224 11225 11226 11227 11228 | 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; | | | | | | | | | | 11134 11135 11136 11137 11138 11139 11140 11141 11142 11143 11144 11145 11146 11147 11148 11149 11150 11151 11152 11153 11154 11155 11156 11157 11158 11159 11160 11161 11162 11163 11164 11165 11166 11167 11168 11169 11170 11171 11172 11173 11174 11175 11176 11177 11178 11179 11180 11181 11182 11183 11184 11185 11186 11187 11188 11189 11190 11191 11192 11193 11194 11195 11196 11197 11198 11199 11200 11201 11202 11203 11204 11205 11206 11207 11208 11209 | 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; |
︙ | ︙ | |||
11354 11355 11356 11357 11358 11359 11360 | "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) ){ | | | 11266 11267 11268 11269 11270 11271 11272 11273 11274 11275 11276 11277 11278 11279 11280 | "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;" |
︙ | ︙ | |||
11407 11408 11409 11410 11411 11412 11413 | 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*/" | < < < < < < < < < | | 11319 11320 11321 11322 11323 11324 11325 11326 11327 11328 11329 11330 11331 11332 11333 11334 11335 11336 11337 11338 11339 11340 11341 11342 11343 11344 11345 | 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; } } fsl_db * fsl_config_for_role(fsl_cx * f, fsl_confdb_e mode){ switch(mode){ case FSL_CONFDB_REPO: return fsl_cx_db_repo(f); case FSL_CONFDB_CKOUT: return fsl_cx_db_ckout(f); case FSL_CONFDB_GLOBAL: return fsl_cx_db_config(f); case FSL_CONFDB_VERSIONABLE: return fsl_cx_db(f); default: assert(!"Invalid fsl_confdb_e value"); |
︙ | ︙ | |||
11452 11453 11454 11455 11456 11457 11458 | } fsl_buffer_reuse(b); return fsl_buffer_appendf(b, "%s.fossil-settings/%s", f->ckout.dir, key); } | | | | < | < | | < | | | < | | < | | 11355 11356 11357 11358 11359 11360 11361 11362 11363 11364 11365 11366 11367 11368 11369 11370 11371 11372 11373 11374 11375 11376 11377 11378 11379 11380 11381 11382 11383 11384 11385 11386 11387 11388 11389 11390 11391 11392 11393 11394 11395 11396 11397 11398 11399 11400 11401 11402 11403 11404 11405 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 11443 11444 11445 11446 11447 11448 11449 11450 11451 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 | } fsl_buffer_reuse(b); return fsl_buffer_appendf(b, "%s.fossil-settings/%s", f->ckout.dir, key); } int fsl_config_unset( fsl_cx * f, fsl_confdb_e mode, char const * key ){ fsl_db * db = fsl_config_for_role(f, mode); if(!db || !key || !*key) return FSL_RC_MISUSE; else if(mode==FSL_CONFDB_VERSIONABLE) return FSL_RC_UNSUPPORTED; else{ char const * table = fsl_config_table_for_role(mode); assert(table); return fsl_db_exec(db, "DELETE FROM %s WHERE name=%Q", table, key); } } int32_t fsl_config_get_int32( fsl_cx * f, fsl_confdb_e mode, int32_t dflt, char const * key ){ int32_t rv = dflt; switch(mode){ case FSL_CONFDB_VERSIONABLE:{ char * val = fsl_config_get_text(f, mode, key, NULL); if(val){ rv = (int32_t)atoi(val); 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){ 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 * 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){ 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 * 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 * 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){ 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 * 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 * f, fsl_confdb_e mode, char const * key, fsl_buffer * b ){ int rc = FSL_RC_NOT_FOUND; 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; } 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 * 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); |
︙ | ︙ | |||
11666 11667 11668 11669 11670 11671 11672 | 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){ | < | 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); } |
︙ | ︙ | |||
11688 11689 11690 11691 11692 11693 11694 | 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. */ | | | | < < | < | | | | | 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 | 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 * 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 %s(name,value,mtime) VALUES(?,?,now())/*%s()*/" : "REPLACE INTO %s(name,value) VALUES(?,?)/*%s()*/"; int rc = fsl_db_prepare_cached(db, st, sql, table, __func__); if(!rc) 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 * 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 * 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); } |
︙ | ︙ | |||
11772 11773 11774 11775 11776 11777 11778 | 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; } | | | 11666 11667 11668 11669 11670 11671 11672 11673 11674 11675 11676 11677 11678 11679 11680 | 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 * 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) |
︙ | ︙ | |||
11803 11804 11805 11806 11807 11808 11809 | 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; } | | | 11697 11698 11699 11700 11701 11702 11703 11704 11705 11706 11707 11708 11709 11710 11711 | 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 * 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, |
︙ | ︙ | |||
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; } | | | 11723 11724 11725 11726 11727 11728 11729 11730 11731 11732 11733 11734 11735 11736 11737 | 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 * 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, |
︙ | ︙ | |||
11855 11856 11857 11858 11859 11860 11861 | 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; } | | | 11749 11750 11751 11752 11753 11754 11755 11756 11757 11758 11759 11760 11761 11762 11763 | 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 * 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, |
︙ | ︙ | |||
11881 11882 11883 11884 11885 11886 11887 | 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; } | | | 11775 11776 11777 11778 11779 11780 11781 11782 11783 11784 11785 11786 11787 11788 11789 | 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 * 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, |
︙ | ︙ | |||
11907 11908 11909 11910 11911 11912 11913 | 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; } | | | 11801 11802 11803 11804 11805 11806 11807 11808 11809 11810 11811 11812 11813 11814 11815 | 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 * 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'; } |
︙ | ︙ | |||
11935 11936 11937 11938 11939 11940 11941 | 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 11844 11845 11846 11847 11848 11849 11850 11851 11852 11853 11854 11855 11856 11857 11858 11859 11860 11861 11862 11863 | 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 * 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 * f, fsl_confdb_e mode, char rollback){ fsl_db * db = fsl_config_for_role(f,mode); if(!db) return FSL_RC_MISUSE; else{ int const rc = fsl_db_transaction_end(db, rollback); if(rc) fsl_cx_uplift_db_error(f, db); return rc; } } int fsl_config_globs_load(fsl_cx * f, fsl_list * li, char const * key){ int rc = 0; char * val = NULL; if(!f || !li || !key || !*key) return FSL_RC_MISUSE; else if(fsl_cx_db_ckout(f)){ /* Try versionable settings... */ fsl_buffer buf = fsl_buffer_empty; rc = fsl_config_get_buffer(f, FSL_CONFDB_VERSIONABLE, key, &buf); |
︙ | ︙ | |||
12108 12109 12110 12111 12112 12113 12114 | #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. */ | | | 12002 12003 12004 12005 12006 12007 12008 12009 12010 12011 12012 12013 12014 12015 12016 | #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; |
︙ | ︙ | |||
12210 12211 12212 12213 12214 12215 12216 | /** Returns true if f's current checkout contains the given versionable configuration setting, else false. @see fsl_config_ctrl */ | | | 12104 12105 12106 12107 12108 12109 12110 12111 12112 12113 12114 12115 12116 12117 12118 | /** 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 * 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 |
︙ | ︙ | |||
12331 12332 12333 12334 12335 12336 12337 | } 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; } | | | | < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 12225 12226 12227 12228 12229 12230 12231 12232 12233 12234 12235 12236 12237 12238 12239 12240 12241 12242 12243 12244 12245 12246 12247 12248 12249 12250 12251 | } 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 * 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; } } #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: */ |
︙ | ︙ | |||
12599 12600 12601 12602 12603 12604 12605 | 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. */ | | | | 12320 12321 12322 12323 12324 12325 12326 12327 12328 12329 12330 12331 12332 12333 12334 12335 12336 12337 12338 12339 12340 12341 12342 12343 12344 12345 12346 12347 12348 12349 12350 12351 12352 | 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; |
︙ | ︙ | |||
12690 12691 12692 12693 12694 12695 12696 | }else{ f->dbMain = &f->dbMem; f->dbMem.role = FSL_DBROLE_MAIN; } return rc; } | | | | | | < < < < > > > > > > < | | < | < > | | < | | | 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 | }else{ f->dbMain = &f->dbMem; f->dbMem.role = FSL_DBROLE_MAIN; } return rc; } static void fsl_cx_mcache_clear(fsl_cx *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). */ SFREE(f->ckout.dir); f->ckout.dirLen = 0; /* assert(NULL==f->dbMain); */ assert(!f->repo.db.dbh); assert(!f->ckout.db.dbh); assert(!f->config.db.dbh); assert(!f->repo.db.filename); assert(!f->ckout.db.filename); assert(!f->config.db.filename); } SFREE(f->repo.user); SFREE(f->ckout.uuid); SFREE(f->cache.projectCode); #undef SFREE fsl_error_clear(&f->error); 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_acache_clear(&f->cache.arty); fsl_id_bag_clear(&f->cache.leafCheck); fsl_id_bag_clear(&f->cache.toVerify); fsl_cx_clear_mf_seen(f); 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 fsl_cx_mcache_clear(f); f->cache = fsl_cx_empty.cache; } void fsl_cx_clear_mf_seen(fsl_cx * f){ fsl_id_bag_clear(&f->cache.mfSeen); } void fsl_cx_finalize( fsl_cx * f ){ void const * allocStamp = f ? f->allocStamp : NULL; if(!f) return; if(f->clientState.finalize.f){ f->clientState.finalize.f( f->clientState.finalize.state, f->clientState.state ); } f->clientState = fsl_state_empty; f->output = fsl_outputer_empty; fsl_cx_reset(f, true); fsl_db_close(&f->dbMem); *f = fsl_cx_empty; if(&fsl_cx_empty == allocStamp){ fsl_free(f); }else{ f->allocStamp = allocStamp; } |
︙ | ︙ | |||
12791 12792 12793 12794 12795 12796 12797 | assert(sg_autoregctr>=0); #endif #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); #endif } | | | | | | | | | | > | | | | < | < < < < < | < < > | 12511 12512 12513 12514 12515 12516 12517 12518 12519 12520 12521 12522 12523 12524 12525 12526 12527 12528 12529 12530 12531 12532 12533 12534 12535 12536 12537 12538 12539 12540 12541 12542 12543 12544 12545 12546 12547 12548 12549 12550 12551 12552 12553 12554 12555 12556 12557 12558 12559 12560 12561 12562 12563 12564 12565 12566 12567 12568 12569 | 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 * f){ if(f){ fsl_error_reset(&f->error); fsl_db_err_reset(&f->dbMem); fsl_db_err_reset(&f->repo.db); fsl_db_err_reset(&f->config.db); fsl_db_err_reset(&f->ckout.db); } } int fsl_cx_err_set_e( fsl_cx * f, fsl_error * err ){ if(!f) return FSL_RC_MISUSE; else if(!err){ return fsl_cx_err_set(f, 0, NULL); }else{ fsl_error_move(err, &f->error); fsl_error_clear(err); return f->error.code; } } int fsl_cx_err_setv( fsl_cx * f, int code, char const * fmt, va_list args ){ return f ? fsl_error_setv( &f->error, code, fmt, args ) : FSL_RC_MISUSE; } int fsl_cx_err_set( fsl_cx * 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 * f, char const ** str, fsl_size_t * len ){ return f ? fsl_error_get( &f->error, str, len ) : FSL_RC_MISUSE; } fsl_id_t fsl_cx_last_insert_id(fsl_cx * const f){ return (f && f->dbMain && f->dbMain->dbh) ? fsl_db_last_insert_id(f->dbMain) : -1; } |
︙ | ︙ | |||
12875 12876 12877 12878 12879 12880 12881 12882 12883 12884 12885 12886 12887 12888 12889 12890 12891 | 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){ | > < < | | > | < > < > | 12589 12590 12591 12592 12593 12594 12595 12596 12597 12598 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 12632 | addNewline ? "\n" : ""); } else return 0; } int fsl_cx_uplift_db_error( fsl_cx * const f, fsl_db * db ){ assert(f); if(!f) return FSL_RC_MISUSE; if(!db){ db = f->dbMain; assert(db && "misuse: no DB handle to uplift error from!"); if(!db) return FSL_RC_MISUSE; } 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){ if(!db) db = f->dbMain; assert(db); if(rc && FSL_RC_OOM!=rc && !f->error.code && db->error.code){ rc = fsl_cx_uplift_db_error(f, db); } return rc; } fsl_db * fsl_cx_db_config( fsl_cx * const f ){ if(!f) return NULL; else if(f->config.db.dbh) return &f->config.db; else if(f->dbMain && (FSL_DBROLE_CONFIG & f->dbMain->role)) return f->dbMain; else return NULL; } fsl_db * fsl_cx_db_repo( fsl_cx * const f ){ if(!f) return NULL; else if(f->repo.db.dbh) return &f->repo.db; else if(f->dbMain && (FSL_DBROLE_REPO & f->dbMain->role)) return f->dbMain; else return NULL; } fsl_db * fsl_needs_repo(fsl_cx * const f){ fsl_db * const db = fsl_cx_db_repo(f); if(!db){ fsl_cx_err_set(f, FSL_RC_NOT_A_REPO, |
︙ | ︙ | |||
12928 12929 12930 12931 12932 12933 12934 | "Fossil context has no opened checkout db."); } return db; } fsl_db * fsl_cx_db_ckout( fsl_cx * const f ){ if(!f) return NULL; | < > | 12642 12643 12644 12645 12646 12647 12648 12649 12650 12651 12652 12653 12654 12655 12656 12657 | "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->ckout.db.dbh) return &f->ckout.db; else if(f->dbMain && (FSL_DBROLE_CKOUT & f->dbMain->role)) return f->dbMain; else return NULL; } fsl_db * fsl_cx_db( fsl_cx * const f ){ return f ? f->dbMain : NULL; } /** @internal |
︙ | ︙ | |||
12970 12971 12972 12973 12974 12975 12976 | } } /** Detaches the given db role from f->dbMain and removes the role from f->dbMain->role. */ | | | | | | < < < < < < < < | | | < | < < | < < > > | | | 12684 12685 12686 12687 12688 12689 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 | } } /** Detaches the given db role from f->dbMain and removes the role from f->dbMain->role. */ static int fsl_cx_detach_role(fsl_cx * f, fsl_dbrole_e r){ if(!f || !f->dbMain) return FSL_RC_MISUSE; else if(!(r & f->dbMain->role)){ assert(!"Misuse: cannot detach unattached role."); return FSL_RC_NOT_FOUND; } else{ fsl_db * db = fsl_cx_db_for_role(f,r); int rc; if(!db) return FSL_RC_RANGE; assert(f->dbMain != db); f->dbMain->role &= ~r; rc = fsl_db_detach( f->dbMain, fsl_db_role_label(r) ); //MARKER(("rc=%s %s %s\n", fsl_rc_cstr(rc), fsl_db_role_label(r), // fsl_buffer_cstr(&f->dbMain->error.msg))); fsl_free(db->filename); fsl_free(db->name); db->filename = NULL; db->name = NULL; return rc; } } /** @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 * 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.", |
︙ | ︙ | |||
13079 13080 13081 13082 13083 13084 13085 | f->dbMain->role |= r; } } return rc; } int fsl_config_close( fsl_cx * const f ){ | > > | | | | | < < | > > > | > | | < < > > | | | > | | | > | | < > > | 12782 12783 12784 12785 12786 12787 12788 12789 12790 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 12856 12857 12858 12859 12860 12861 12862 | f->dbMain->role |= r; } } return rc; } int fsl_config_close( fsl_cx * const f ){ if(!f) return FSL_RC_MISUSE; else{ int rc; fsl_db * const db = &f->config.db; if(f->dbMain && (FSL_DBROLE_CONFIG & f->dbMain->role)){ /* Config db is ATTACHed. */ rc = fsl_cx_detach_role(f, FSL_DBROLE_CONFIG); } else rc = FSL_RC_NOT_FOUND; assert(!db->dbh); fsl_db_clear_strings(db, 1); return rc; } } int fsl_repo_close( fsl_cx * const f ){ if(fsl_cx_transaction_level(f)){ return fsl_cx_err_set(f, FSL_RC_MISUSE, "Cannot close repo with opened transaction."); }else{ int rc; fsl_db * const db = &f->repo.db; if(f->dbMain && (FSL_DBROLE_REPO & f->dbMain->role)){ /* Repo db is ATTACHed. */ if(FSL_DBROLE_CKOUT & f->dbMain->role){ rc = fsl_cx_err_set(f, FSL_RC_MISUSE, "Cannot close repo while checkout is " "opened."); }else{ assert(f->dbMain!=db); rc = fsl_cx_detach_role(f, FSL_DBROLE_REPO); } } else rc = FSL_RC_NOT_FOUND; assert(!db->dbh); fsl_db_clear_strings(db, true); return rc; } } int fsl_ckout_close( fsl_cx * const f ){ if(fsl_cx_transaction_level(f)){ return fsl_cx_err_set(f, FSL_RC_MISUSE, "Cannot close checkout with opened transaction."); }else{ int rc; 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{ rc = FSL_RC_NOT_FOUND; } fsl_free(f->ckout.uuid); f->ckout.uuid = NULL; f->ckout.rid = 0; assert(!db->dbh); fsl_db_clear_strings(db, true); return rc; } } /** 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. |
︙ | ︙ | |||
13214 13215 13216 13217 13218 13219 13220 | int rc; va_list args; va_start(args,sql); rc = fsl_cx_preparev( f, tgt, sql, args ); va_end(args); return rc; } | < < < < < < < < < < < < < < < < < < < | | 12924 12925 12926 12927 12928 12929 12930 12931 12932 12933 12934 12935 12936 12937 12938 12939 12940 12941 12942 12943 12944 12945 12946 12947 | 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. */ static int fsl_config_file_reset(fsl_cx * f, char const * dbName){ fsl_db DB = fsl_db_empty; fsl_db * db = &DB; int rc = 0; bool isAttached = false; const char * zPrefix = fsl_db_role_label(FSL_DBROLE_CONFIG); if(-1 != fsl_file_size(dbName)){ rc = fsl_file_unlink(dbName); |
︙ | ︙ | |||
13342 13343 13344 13345 13346 13347 13348 | return rc; } int fsl_config_open( fsl_cx * const f, char const * openDbName ){ int rc = 0; const char * zDbName = 0; char * zPrefName = 0; | > | < | | 13033 13034 13035 13036 13037 13038 13039 13040 13041 13042 13043 13044 13045 13046 13047 13048 13049 | return rc; } int fsl_config_open( fsl_cx * const f, char const * openDbName ){ int rc = 0; const char * zDbName = 0; char * zPrefName = 0; if(!f) return FSL_RC_MISUSE; else if(f->config.db.dbh){ fsl_config_close(f); } if(openDbName && *openDbName){ zDbName = openDbName; }else{ rc = fsl_config_global_preferred_name(&zPrefName); if(rc) goto end; zDbName = zPrefName; |
︙ | ︙ | |||
13372 13373 13374 13375 13376 13377 13378 | 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 | < | 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){ |
︙ | ︙ | |||
13446 13447 13448 13449 13450 13451 13452 | /** 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){ | | | 13136 13137 13138 13139 13140 13141 13142 13143 13144 13145 13146 13147 13148 13149 13150 | /** 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 = |
︙ | ︙ | |||
13562 13563 13564 13565 13566 13567 13568 | ? 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; } | | | 13252 13253 13254 13255 13256 13257 13258 13259 13260 13261 13262 13263 13264 13265 13266 | ? 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 *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; |
︙ | ︙ | |||
13617 13618 13619 13620 13621 13622 13623 | 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. | | | | | | 13307 13308 13309 13310 13311 13312 13313 13314 13315 13316 13317 13318 13319 13320 13321 13322 13323 13324 13325 13326 13327 13328 13329 13330 13331 13332 13333 13334 13335 13336 13337 13338 13339 13340 13341 13342 13343 13344 13345 13346 13347 13348 13349 13350 13351 13352 13353 13354 13355 13356 13357 13358 13359 13360 13361 | 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 *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){ |
︙ | ︙ | |||
13908 13909 13910 13911 13912 13913 13914 | if(f && f->ckout.dir){ rc = f->ckout.dir; if(len) *len = f->ckout.dirLen; } return rc; } | | | | 13598 13599 13600 13601 13602 13603 13604 13605 13606 13607 13608 13609 13610 13611 13612 13613 13614 13615 13616 | if(f && f->ckout.dir){ rc = f->ckout.dir; if(len) *len = f->ckout.dirLen; } return rc; } int fsl_cx_flags_get( fsl_cx * f ){ return f->flags; } int fsl_cx_flag_set( fsl_cx * f, int flags, bool enable ){ int const oldFlags = f->flags; if(enable) f->flags |= flags; else f->flags &= ~flags; return oldFlags; } |
︙ | ︙ | |||
13959 13960 13961 13962 13963 13964 13965 | *x = fsl_xlinker_empty; x->f = cb; x->state = cbState; x->name = name; return 0; } | | < < < < < < < < < < < < < | | | 13649 13650 13651 13652 13653 13654 13655 13656 13657 13658 13659 13660 13661 13662 13663 13664 13665 13666 13667 13668 13669 13670 13671 13672 13673 13674 13675 13676 13677 13678 13679 13680 13681 | *x = fsl_xlinker_empty; x->f = cb; x->state = cbState; x->name = name; return 0; } int fsl_cx_user_set( fsl_cx * f, char const * userName ){ if(!f) return FSL_RC_MISUSE; else if(!userName || !*userName){ fsl_free(f->repo.user); f->repo.user = NULL; return 0; }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_get( fsl_cx const * f ){ return f ? f->repo.user : NULL; } int fsl_cx_schema_ticket(fsl_cx * f, fsl_buffer * pOut){ fsl_db * db = f ? fsl_needs_repo(f) : NULL; if(!f || !pOut) return FSL_RC_MISUSE; else if(!db) return FSL_RC_NOT_A_REPO; else{ |
︙ | ︙ | |||
14017 14018 14019 14020 14021 14022 14023 | 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; | | | | | 13694 13695 13696 13697 13698 13699 13700 13701 13702 13703 13704 13705 13706 13707 13708 13709 13710 13711 13712 13713 | 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; 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); |
︙ | ︙ | |||
14061 14062 14063 14064 14065 14066 14067 | }else if(nameOut){ rc = fullPath ? fsl_buffer_append(nameOut, b->mem, b->used) : fsl_buffer_append(nameOut, zName, n); } } end: | | | | 13738 13739 13740 13741 13742 13743 13744 13745 13746 13747 13748 13749 13750 13751 13752 13753 | }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); } |
︙ | ︙ | |||
14085 14086 14087 14088 14089 14090 14091 | } char const * fsl_cx_filename_collation(fsl_cx const * f){ return f->cache.caseInsensitive ? "COLLATE nocase" : ""; } | | < < < < < < < < < < < < < | 13762 13763 13764 13765 13766 13767 13768 13769 13770 13771 13772 13773 13774 13775 13776 13777 13778 13779 13780 13781 13782 13783 13784 13785 13786 13787 13788 13789 13790 13791 13792 13793 13794 13795 13796 13797 13798 13799 13800 13801 13802 13803 13804 13805 | } 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)){ return fsl_cx_err_set(f, FSL_RC_MISUSE, "Cannot close the databases when a " "transaction is pending."); } int rc = 0, rc1; 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; assert(!f->repo.db.dbh); assert(!f->ckout.db.dbh); assert(!f->config.db.dbh); 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; |
︙ | ︙ | |||
14256 14257 14258 14259 14260 14261 14262 | f->confirmer = newConfirmer ? *newConfirmer : fsl_confirmer_empty; } void fsl_cx_confirmer_get(fsl_cx const * f, fsl_confirmer * dest){ *dest = f->confirmer; } | | | | | 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 | 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 *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_mf(fsl_cx *f){ int rc = 0; fsl_db * const d = fsl_cx_db_repo(f); if(d && f->cache.seenDeltaManifest <= 0){ f->cache.seenDeltaManifest = 1; rc = fsl_config_set_bool(f, FSL_CONFDB_REPO, "seen-delta-manifest", 1); } |
︙ | ︙ | |||
14307 14308 14309 14310 14311 14312 14313 | 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) | | | | | | | | | | | | | | 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 14066 14067 14068 14069 14070 14071 14072 14073 14074 14075 14076 14077 14078 14079 14080 14081 14082 14083 14084 14085 14086 14087 14088 14089 14090 14091 14092 14093 | 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 *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 *f, fsl_buffer * b){ int i; assert(b); for(i = 0; i < FSL_CX_NSCRATCH; ++i){ if(b == &f->scratchpads.buf[i]){ assert(f->scratchpads.next != i); 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 |
︙ | ︙ | |||
14475 14476 14477 14478 14479 14480 14481 | if(fsl_rmdir(zAbs)) break; ++rc; } } return rc; } | | | | | | | | < < < < < < < < < < < < < < < < < < < < | 14139 14140 14141 14142 14143 14144 14145 14146 14147 14148 14149 14150 14151 14152 14153 14154 14155 14156 14157 14158 14159 14160 14161 14162 14163 14164 14165 14166 14167 14168 14169 14170 14171 14172 14173 14174 14175 14176 14177 14178 14179 14180 14181 14182 14183 14184 14185 14186 14187 14188 14189 14190 14191 14192 14193 14194 14195 14196 14197 14198 14199 14200 14201 14202 14203 14204 14205 14206 14207 14208 14209 14210 14211 14212 14213 14214 14215 14216 14217 14218 14219 14220 14221 14222 14223 14224 14225 14226 14227 14228 | if(fsl_rmdir(zAbs)) break; ++rc; } } return rc; } unsigned int fsl_ckout_rm_empty_dirs(fsl_cx * f, fsl_buffer * tgtDir){ int rc = f->ckout.dir ? 0 : FSL_RC_NOT_A_CKOUT; if(!rc){ rc = fsl_rm_empty_dirs(f->ckout.dir, f->ckout.dirLen, tgtDir); } return rc; } int fsl_ckout_rm_empty_dirs_for_file(fsl_cx * 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; } #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) |
︙ | ︙ | |||
14639 14640 14641 14642 14643 14644 14645 | */ static int fsl_list_v_fsl_stmt_finalize(void * obj, void * visitorState ){ if(obj) fsl_stmt_finalize( (fsl_stmt*)obj ); return 0; } #endif | | | 14283 14284 14285 14286 14287 14288 14289 14290 14291 14292 14293 14294 14295 14296 14297 | */ 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); } |
︙ | ︙ | |||
14751 14752 14753 14754 14755 14756 14757 | (int)db->openStatementCount, db->filename)); } if(db->dbh){ sqlite3_close(db->dbh); /* ignoring results in the style of "destructors may not throw". */ } | | | 14395 14396 14397 14398 14399 14400 14401 14402 14403 14404 14405 14406 14407 14408 14409 | (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, 1); 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; |
︙ | ︙ | |||
14860 14861 14862 14863 14864 14865 14866 | } } return s; } enum fsl_stmt_flags_e { /** | | | 14504 14505 14506 14507 14508 14509 14510 14511 14512 14513 14514 14515 14516 14517 14518 | } } return s; } enum fsl_stmt_flags_e { /** fsl_stmt::flags bit indicating that fsl_db_preparev_cache() has doled out this statement, effectively locking it until fsl_stmt_cached_yield() is called to release it. */ FSL_STMT_F_CACHE_HELD = 0x01, /** Propagates our intent to "statically" prepare a given statement |
︙ | ︙ | |||
15024 15025 15026 15027 15028 15029 15030 | 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; | < < < | 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; } |
︙ | ︙ | |||
15133 15134 15135 15136 15137 15138 15139 | }else{ stmt->allocStamp = allocStamp; } return 0; } } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 14774 14775 14776 14777 14778 14779 14780 14781 14782 14783 14784 14785 14786 14787 | }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 ){ case SQLITE_ROW: |
︙ | ︙ | |||
15980 15981 15982 15983 15984 15985 15986 | } int (*cmp)(char const *, char const *) = f->cache.caseInsensitive ? fsl_stricmp : fsl_strcmp; if(0==cmp(p1, p2)){ sqlite3_result_int(context, 1); return; } | | | | 15586 15587 15588 15589 15590 15591 15592 15593 15594 15595 15596 15597 15598 15599 15600 15601 15602 15603 15604 15605 15606 15607 15608 15609 15610 15611 | } 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; } /** |
︙ | ︙ | |||
16077 16078 16079 16080 16081 16082 16083 | } /* 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. */ | | | 15683 15684 15685 15686 15687 15688 15689 15690 15691 15692 15693 15694 15695 15696 15697 | } /* 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; } |
︙ | ︙ | |||
16102 16103 16104 16105 16106 16107 16108 | #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){ | | | 15708 15709 15710 15711 15712 15713 15714 15715 15716 15717 15718 15719 15720 15721 15722 | #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; |
︙ | ︙ | |||
16185 16186 16187 16188 16189 16190 16191 | rc = FSL_RC_OOM; goto end; } } db->dbh = dbh; if(FSL_OPEN_F_SCHEMA_VALIDATE & openFlags){ int check; | | | > | 15791 15792 15793 15794 15795 15796 15797 15798 15799 15800 15801 15802 15803 15804 15805 15806 15807 15808 15809 15810 15811 15812 15813 15814 15815 15816 | 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) ){ |
︙ | ︙ | |||
16447 16448 16449 16450 16451 16452 16453 | "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. | | > | 16054 16055 16056 16057 16058 16059 16060 16061 16062 16063 16064 16065 16066 16067 16068 16069 | "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; |
︙ | ︙ | |||
16470 16471 16472 16473 16474 16475 16476 | 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. */ | | | | 16078 16079 16080 16081 16082 16083 16084 16085 16086 16087 16088 16089 16090 16091 16092 16093 16094 | 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_leaf_do_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); |
︙ | ︙ | |||
17159 17160 17161 17162 17163 17164 17165 | /* Only for debugging */ #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) typedef int StaticAssertMCacheArraySizes[ | | | | | | | | | | 16767 16768 16769 16770 16771 16772 16773 16774 16775 16776 16777 16778 16779 16780 16781 16782 16783 16784 16785 16786 16787 16788 16789 16790 16791 16792 16793 16794 16795 16796 16797 16798 16799 16800 16801 16802 16803 16804 16805 16806 16807 16808 | /* 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; } |
︙ | ︙ | |||
17230 17231 17232 17233 17234 17235 17236 | 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. */ | | < | < < < | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 | 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 * f, fsl_id_t rid, fsl_deck * tgt){ if(!(f->flags & FSL_CX_F_MANIFEST_CACHE)) return false; static const unsigned cacheLen = (int)(sizeof(fsl_mcache_empty.aAge) /sizeof(fsl_mcache_empty.aAge[0])); 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; } /** 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. */ |
︙ | ︙ | |||
17508 17509 17510 17511 17512 17513 17514 | if(rc){ *rc = fsl_deck_empty; rc->allocStamp = &fsl_deck_empty; } return rc; } | | | | 17077 17078 17079 17080 17081 17082 17083 17084 17085 17086 17087 17088 17089 17090 17091 17092 17093 17094 17095 17096 17097 17098 17099 | if(rc){ *rc = fsl_deck_empty; rc->allocStamp = &fsl_deck_empty; } return rc; } void fsl_deck_init( fsl_cx * f, fsl_deck * cards, fsl_satype_e type ){ void const * allocStamp = cards->allocStamp; *cards = fsl_deck_empty; cards->allocStamp = allocStamp; cards->f = f; cards->type = type; } void fsl_card_J_list_free(fsl_list * li, bool alsoListMem){ 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) |
︙ | ︙ | |||
17569 17570 17571 17572 17573 17574 17575 | 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){ | | | 17138 17139 17140 17141 17142 17143 17144 17145 17146 17147 17148 17149 17150 17151 17152 | 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); } |
︙ | ︙ | |||
18290 18291 18292 18293 18294 18295 18296 | fsl_md5_update_buffer(&md5, buf); } if(!rc){ fsl_md5_final(&md5, digest); fsl_md5_digest_to_base16(digest, hex); } end: | | | 17859 17860 17861 17862 17863 17864 17865 17866 17867 17868 17869 17870 17871 17872 17873 | 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){ |
︙ | ︙ | |||
18604 18605 18606 18607 18608 18609 18610 | 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. | | | 18173 18174 18175 18176 18177 18178 18179 18180 18181 18182 18183 18184 18185 18186 18187 | 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*/, |
︙ | ︙ | |||
18913 18914 18915 18916 18917 18918 18919 | /** 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. */ | | | 18482 18483 18484 18485 18486 18487 18488 18489 18490 18491 18492 18493 18494 18495 18496 | /** 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; |
︙ | ︙ | |||
19519 19520 19521 19522 19523 19524 19525 | 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) | | | 19088 19089 19090 19091 19092 19093 19094 19095 19096 19097 19098 19099 19100 19101 19102 | 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')); |
︙ | ︙ | |||
19592 19593 19594 19595 19596 19597 19598 | else if(!fsl_deck_has_required_cards(d)){ return FSL_RC_SYNTAX; } os->d = d; os->out = out; os->outState = outputState; | | | 19161 19162 19163 19164 19165 19166 19167 19168 19169 19170 19171 19172 19173 19174 19175 | 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; |
︙ | ︙ | |||
19632 19633 19634 19635 19636 19637 19638 | d->type); goto end; } if(!rc){ rc = fsl_deck_out_Z( os ); } end: | | | 19201 19202 19203 19204 19205 19206 19207 19208 19209 19210 19211 19212 19213 19214 19215 | 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; } |
︙ | ︙ | |||
19656 19657 19658 19659 19660 19661 19662 | #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. */ | | | 19225 19226 19227 19228 19229 19230 19231 19232 19233 19234 19235 19236 19237 19238 19239 | #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); } |
︙ | ︙ | |||
19711 19712 19713 19714 19715 19716 19717 | 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)); | | | | | | 19280 19281 19282 19283 19284 19285 19286 19287 19288 19289 19290 19291 19292 19293 19294 19295 19296 19297 19298 19299 19300 19301 19302 19303 19304 19305 19306 19307 19308 19309 19310 19311 19312 19313 19314 | 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; } |
︙ | ︙ | |||
19792 19793 19794 19795 19796 19797 19798 | 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. */ | | | 19361 19362 19363 19364 19365 19366 19367 19368 19369 19370 19371 19372 19373 19374 19375 | 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. |
︙ | ︙ | |||
19829 19830 19831 19832 19833 19834 19835 | 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. */ | | | 19398 19399 19400 19401 19402 19403 19404 19405 19406 19407 19408 19409 19410 19411 19412 | 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; |
︙ | ︙ | |||
19878 19879 19880 19881 19882 19883 19884 | return FCARD(i); } } return NULL; #undef FCARD } | | | | | | | 19447 19448 19449 19450 19451 19452 19453 19454 19455 19456 19457 19458 19459 19460 19461 19462 19463 19464 19465 19466 19467 19468 19469 19470 19471 19472 19473 19474 19475 19476 19477 19478 19479 19480 19481 19482 19483 19484 19485 19486 19487 19488 19489 19490 19491 19492 19493 19494 19495 19496 19497 19498 19499 19500 19501 19502 19503 19504 19505 19506 19507 19508 19509 19510 19511 19512 19513 19514 19515 19516 | 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; } |
︙ | ︙ | |||
19998 19999 20000 20001 20002 20003 20004 | 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. */ | | | | | | 19567 19568 19569 19570 19571 19572 19573 19574 19575 19576 19577 19578 19579 19580 19581 19582 19583 19584 19585 19586 19587 19588 19589 19590 19591 19592 19593 19594 19595 19596 19597 19598 19599 19600 19601 19602 19603 19604 | 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 * 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; |
︙ | ︙ | |||
20073 20074 20075 20076 20077 20078 20079 | }else{ assert(d->f->error.code); rc = d->f->error.code; } if(rc) return rc; } d->rid = 0; | | | 19642 19643 19644 19645 19646 19647 19648 19649 19650 19651 19652 19653 19654 19655 19656 | }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); |
︙ | ︙ | |||
20224 20225 20226 20227 20228 20229 20230 | pChild = &dOther; otherRid = cid; }else{ pParent = &dOther; otherRid = pmid; } | | | 19793 19794 19795 19796 19797 19798 19799 19800 19801 19802 19803 19804 19805 19806 19807 | 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){ |
︙ | ︙ | |||
20267 20268 20269 20270 20271 20272 20273 | ** 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; | | | | | 19836 19837 19838 19839 19840 19841 19842 19843 19844 19845 19846 19847 19848 19849 19850 19851 19852 19853 19854 19855 19856 19857 19858 19859 19860 19861 19862 19863 | ** 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. */ |
︙ | ︙ | |||
20312 20313 20314 20315 20316 20317 20318 | : 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 | | | 19881 19882 19883 19884 19885 19886 19887 19888 19889 19890 19891 19892 19893 19894 19895 | : 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: |
︙ | ︙ | |||
20339 20340 20341 20342 20343 20344 20345 | /* 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){ | | | 19908 19909 19910 19911 19912 19913 19914 19915 19916 19917 19918 19919 19920 19921 19922 | /* 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); |
︙ | ︙ | |||
20369 20370 20371 20372 20373 20374 20375 | 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 ){ | | | | | | | 19938 19939 19940 19941 19942 19943 19944 19945 19946 19947 19948 19949 19950 19951 19952 19953 19954 19955 19956 19957 19958 19959 19960 19961 19962 19963 19964 19965 19966 19967 19968 19969 19970 19971 19972 19973 19974 19975 19976 19977 19978 19979 19980 19981 19982 19983 19984 19985 19986 19987 19988 19989 19990 19991 19992 19993 19994 19995 19996 19997 19998 | 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; |
︙ | ︙ | |||
20451 20452 20453 20454 20455 20456 20457 | /** 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. */ | | | 20020 20021 20022 20023 20024 20025 20026 20027 20028 20029 20030 20031 20032 20033 20034 | /** 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){ |
︙ | ︙ | |||
20484 20485 20486 20487 20488 20489 20490 | break; }else if(0==tid){ rc = fsl_cx_err_set(f, FSL_RC_RANGE, "Could not get RID for [%.12s].", tag->uuid); break; } | | | | 20053 20054 20055 20056 20057 20058 20059 20060 20061 20062 20063 20064 20065 20066 20067 20068 20069 20070 20071 20072 | 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 |
︙ | ︙ | |||
20533 20534 20535 20536 20537 20538 20539 | }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]; | | | 20102 20103 20104 20105 20106 20107 20108 20109 20110 20111 20112 20113 20114 20115 20116 | }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) " |
︙ | ︙ | |||
20606 20607 20608 20609 20610 20611 20612 | 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 | | | 20175 20176 20177 20178 20179 20180 20181 20182 20183 20184 20185 20186 20187 20188 20189 | 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. |
︙ | ︙ | |||
20696 20697 20698 20699 20700 20701 20702 | /** 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. */ | | | | | | | > | 20265 20266 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 20295 20296 20297 20298 20299 20300 20301 20302 20303 20304 20305 20306 20307 20308 20309 20310 20311 20312 20313 20314 20315 20316 20317 20318 20319 20320 20321 20322 20323 20324 20325 20326 20327 | /** 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 * d, void * state){ if(FSL_SATYPE_ATTACHMENT!=d->type) return 0; int rc; fsl_db * db; fsl_buffer comment = fsl_buffer_empty; const char isAdd = (d->A.src && *d->A.src) ? 1 : 0; char attachToType = 'w' /* Assume wiki until we know otherwise, keeping in mind that the d->A.tgt might not yet be in the blob table, in which case we are unable to know, for certain, what the target is. That only affects the timeline (event table), though, not the crosslinking of the attachment itself. */; db = fsl_cx_db_repo(d->f); assert(db); if(fsl_is_uuid(d->A.tgt)){ if( fsl_db_exists(db, "SELECT 1 FROM tag WHERE tagname='tkt-%q'", d->A.tgt)){ attachToType = 't' /* attach to a known ticket */; }else if( fsl_db_exists(db, "SELECT 1 FROM tag WHERE tagname='event-%q'", d->A.tgt)){ |
︙ | ︙ | |||
20810 20811 20812 20813 20814 20815 20816 | return rc; } /** Overrideable crosslink listener which updates the timeline for checkin records. */ | | | 20380 20381 20382 20383 20384 20385 20386 20387 20388 20389 20390 20391 20392 20393 20394 | return rc; } /** Overrideable crosslink listener which updates the timeline for checkin records. */ static int fsl_deck_xlink_f_checkin(fsl_deck * d, void * state){ if(FSL_SATYPE_CHECKIN!=d->type) return 0; int rc; fsl_db * db; db = fsl_cx_db_repo(d->f); assert(db); rc = fsl_db_exec(db, "REPLACE INTO event(type,mtime,objid,user,comment," |
︙ | ︙ | |||
20858 20859 20860 20861 20862 20863 20864 | (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); } | | | 20428 20429 20430 20431 20432 20433 20434 20435 20436 20437 20438 20439 20440 20441 20442 | (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 * 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 |
︙ | ︙ | |||
21005 21006 21007 21008 21009 21010 21011 | end: fsl_buffer_clear(&comment); return rc; } | | | 20575 20576 20577 20578 20579 20580 20581 20582 20583 20584 20585 20586 20587 20588 20589 | end: fsl_buffer_clear(&comment); return rc; } static int fsl_deck_xlink_f_forum(fsl_deck * d, void * state){ if(FSL_SATYPE_FORUMPOST!=d->type) return 0; int rc = 0; fsl_db * const db = fsl_cx_db_repo(d->f); assert(db); fsl_cx * const f = d->f; 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; |
︙ | ︙ | |||
21091 21092 21093 21094 21095 21096 21097 | dberr: assert(rc); assert(db->error.code); return fsl_cx_uplift_db_error(f, db); } | | | 20661 20662 20663 20664 20665 20666 20667 20668 20669 20670 20671 20672 20673 20674 20675 | dberr: assert(rc); assert(db->error.code); return fsl_cx_uplift_db_error(f, db); } static int fsl_deck_xlink_f_technote(fsl_deck * d, void * state){ if(FSL_SATYPE_TECHNOTE!=d->type) return 0; char buf[FSL_STRLEN_K256 + 7 /* event-UUID\0 */] = {0}; fsl_id_t tagid; char const * zTag; int rc = 0; fsl_cx * const f = d->f; fsl_db * const db = fsl_cx_db_repo(d->f); |
︙ | ︙ | |||
21140 21141 21142 21143 21144 21145 21146 | d->E.julian, d->rid, tagid, d->U, d->C, (int)FSL_TAGID_BGCOLOR, d->rid); } return rc; } | | | 20710 20711 20712 20713 20714 20715 20716 20717 20718 20719 20720 20721 20722 20723 20724 | 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 * 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; |
︙ | ︙ | |||
21193 21194 21195 21196 21197 21198 21199 | /** @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 | | | 20763 20764 20765 20766 20767 20768 20769 20770 20771 20772 20773 20774 20775 20776 20777 | /** @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); |
︙ | ︙ | |||
21218 21219 21220 21221 21222 21223 21224 | fsl_deck_xlink_f_technote, 0); if(!rc) rc = fsl_xlink_listener(f, "fsl/wiki/timeline", fsl_deck_xlink_f_wiki, 0); return rc; } | | | 20788 20789 20790 20791 20792 20793 20794 20795 20796 20797 20798 20799 20800 20801 20802 | 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 |
︙ | ︙ | |||
21247 21248 21249 21250 21251 21252 21253 | } } 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 ); */ | | | | | 20817 20818 20819 20820 20821 20822 20823 20824 20825 20826 20827 20828 20829 20830 20831 20832 20833 20834 20835 20836 20837 20838 20839 20840 20841 20842 20843 20844 20845 20846 20847 20848 20849 | } } 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_mf(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; |
︙ | ︙ | |||
21290 21291 21292 21293 21294 21295 21296 | 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); | | | | | | | | 20860 20861 20862 20863 20864 20865 20866 20867 20868 20869 20870 20871 20872 20873 20874 20875 20876 20877 20878 20879 20880 20881 20882 20883 20884 20885 20886 20887 20888 20889 20890 20891 20892 20893 20894 20895 20896 20897 20898 20899 20900 | 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 |
︙ | ︙ | |||
21345 21346 21347 21348 21349 21350 21351 | " WHERE target=%Q AND filename=%Q", d->A.tgt, d->A.name, d->A.tgt, d->A.name); } return rc; } | | < < < < < | 20915 20916 20917 20918 20919 20920 20921 20922 20923 20924 20925 20926 20927 20928 20929 20930 20931 20932 20933 20934 20935 20936 | " 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); 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; |
︙ | ︙ | |||
21381 21382 21383 21384 21385 21386 21387 | } } fsl_stmt_cached_yield(st); return rc; } #if 0 | | | | | | | 20946 20947 20948 20949 20950 20951 20952 20953 20954 20955 20956 20957 20958 20959 20960 20961 20962 20963 20964 20965 20966 20967 20968 20969 20970 20971 20972 20973 20974 20975 20976 20977 20978 20979 20980 20981 20982 20983 20984 20985 20986 20987 20988 20989 20990 20991 | } } 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; |
︙ | ︙ | |||
21442 21443 21444 21445 21446 21447 21448 | 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); | | | 21007 21008 21009 21010 21011 21012 21013 21014 21015 21016 21017 21018 21019 21020 21021 | 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; |
︙ | ︙ | |||
21468 21469 21470 21471 21472 21473 21474 | 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 ){ | | | | | | | 21033 21034 21035 21036 21037 21038 21039 21040 21041 21042 21043 21044 21045 21046 21047 21048 21049 21050 21051 21052 21053 21054 21055 21056 21057 21058 21059 21060 21061 21062 21063 21064 21065 21066 21067 21068 21069 21070 21071 21072 21073 21074 21075 21076 | 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 |
︙ | ︙ | |||
21522 21523 21524 21525 21526 21527 21528 | 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; } | | | | | > > > > > | < | > | | 21087 21088 21089 21090 21091 21092 21093 21094 21095 21096 21097 21098 21099 21100 21101 21102 21103 21104 21105 21106 21107 21108 21109 21110 21111 21112 21113 21114 21115 21116 21117 21118 | 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; if(!d->f) return FSL_RC_MISUSE; rc = fsl_crosslink_begin(d->f); if(rc) return rc; rc = fsl_deck_crosslink(d); if(rc){ fsl_db_transaction_rollback(fsl_cx_db_repo(d->f)) /* Ignore result - keep existing error state */; d->f->cache.isCrosslinking = false; }else{ assert(fsl_db_transaction_level(fsl_cx_db_repo(d->f))); rc = fsl_crosslink_end(d->f); } return rc; } int fsl_deck_crosslink( fsl_deck /* const */ * const d ){ int 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){ |
︙ | ︙ | |||
21566 21567 21568 21569 21570 21571 21572 | && 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: | | | | | | | | | < < < | | | | | < | | 21136 21137 21138 21139 21140 21141 21142 21143 21144 21145 21146 21147 21148 21149 21150 21151 21152 21153 21154 21155 21156 21157 21158 21159 21160 21161 21162 21163 21164 21165 21166 21167 21168 21169 21170 21171 21172 21173 21174 21175 21176 21177 21178 21179 21180 21181 21182 21183 21184 21185 21186 21187 21188 21189 21190 21191 21192 21193 21194 21195 21196 21197 21198 21199 21200 21201 21202 21203 21204 21205 21206 21207 21208 21209 21210 21211 21212 21213 21214 21215 21216 21217 21218 21219 21220 21221 | && 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){ 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 |
︙ | ︙ | |||
21689 21690 21691 21692 21693 21694 21695 | fsl_md5_digest_to_base16(digest, hex); return (0==memcmp(zHash, hex, FSL_STRLEN_MD5)) ? 1 : -1; } } | < < < < < < < < < < < < < < < < < | < < < < < < < < > < < < < < < < < < | 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 21280 21281 21282 21283 21284 | fsl_md5_digest_to_base16(digest, hex); return (0==memcmp(zHash, hex, FSL_STRLEN_MD5)) ? 1 : -1; } } 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; n -= i; *pz = z; for(i=n-1; i>=0; i--){ if( z[i]=='\n' && memcmp(&z[i],"\n-----BEGIN PGP SIGNATURE-", 25)==0 ){ n = i+1; break; } } *pn = (fsl_size_t)n; return; } /** Internal helper for parsing manifests. Holds a source file (memory |
︙ | ︙ | |||
21855 21856 21857 21858 21859 21860 21861 | 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; } | | | | | | | | 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 | 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 * src){ unsigned const char * z = src->mem; fsl_size_t n = src->used; if(n<36) return 0; fsl_remove_pgp_signature(&z, &n); if(n<36) return 0; else if(z[0]<'A' || z[0]>'Z' || z[1]!=' ' || z[n-35]!='Z' || z[n-34]!=' ' || !fsl_validate16((const char *)z+n-33, FSL_STRLEN_MD5)){ return 0; } return 1; } 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) |
︙ | ︙ | |||
21900 21901 21902 21903 21904 21905 21906 | 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. */ | | | | < < < < < < < < < < < < < < < | 21433 21434 21435 21436 21437 21438 21439 21440 21441 21442 21443 21444 21445 21446 21447 21448 21449 21450 21451 21452 21453 | 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,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0}; if(!d->f || !z) return FSL_RC_MISUSE; /* Every control artifact ends with a '\n' character. Exit early if that is not the case for this artifact. */ f = d->f; 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){ |
︙ | ︙ | |||
21952 21953 21954 21955 21956 21957 21958 | 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) ){ | > | | | 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 | 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) ){ ERROR(FSL_RC_SYNTAX, "Content does not look like " "a structural artifact"); } /* Strip off the PGP signature if there is one. Example of signed manifest: 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"); } |
︙ | ︙ | |||
22039 22040 22041 22042 22043 22044 22045 | <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)){ | | | 21558 21559 21560 21561 21562 21563 21564 21565 21566 21567 21568 21569 21570 21571 21572 | <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)){ ERROR(FSL_RC_RANGE,"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"); } |
︙ | ︙ | |||
22189 22190 22191 22192 22193 22194 22195 | */ 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));*/ | | > | 21708 21709 21710 21711 21712 21713 21714 21715 21716 21717 21718 21719 21720 21721 21722 21723 | */ 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));*/ assert(!"Unmatched perms string character!"); ERROR(FSL_RC_ERROR,"Internal error: unmatched 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)){ |
︙ | ︙ | |||
22359 22360 22361 22362 22363 22364 22365 | /* N <uuid> An N-line identifies the mimetype of wiki or comment text. */ case 'N':{ if(1<SEEN(N)){ | | | | 21879 21880 21881 21882 21883 21884 21885 21886 21887 21888 21889 21890 21891 21892 21893 21894 21895 21896 21897 21898 21899 21900 21901 21902 21903 21904 21905 21906 21907 21908 21909 21910 21911 | /* N <uuid> An N-line identifies the mimetype of wiki or comment text. */ case 'N':{ if(1<SEEN(N)){ ERROR(FSL_RC_RANGE,"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)){ ERROR(FSL_RC_RANGE,"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 |
︙ | ︙ | |||
22435 22436 22437 22438 22439 22440 22441 | R <md5sum> Specify the MD5 checksum over the name and content of all files in the manifest. */ case 'R':{ if(1<SEEN(R)){ | | | 21955 21956 21957 21958 21959 21960 21961 21962 21963 21964 21965 21966 21967 21968 21969 | R <md5sum> Specify the MD5 checksum over the name and content of all files in the manifest. */ case 'R':{ if(1<SEEN(R)){ ERROR(FSL_RC_RANGE,"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; |
︙ | ︙ | |||
22538 22539 22540 22541 22542 22543 22544 | 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){ | | | 22058 22059 22060 22061 22062 22063 22064 22065 22066 22067 22068 22069 22070 22071 22072 | 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){ ERROR(FSL_RC_RANGE,"Wiki size token is invalid"); } if( (&x.z[wlen+1]) > x.zEnd){ SYNTAX("Not enough content after W-card"); } rc = fsl_buffer_append(&d->W, x.z, wlen); if(rc) goto bailout; x.z += wlen; |
︙ | ︙ | |||
22671 22672 22673 22674 22675 22676 22677 | } default: break; } assert(!d->content.mem); if(stealBuf>0){ /* We stashed something which points to src->mem, so we need to | | > < < < < < | 22191 22192 22193 22194 22195 22196 22197 22198 22199 22200 22201 22202 22203 22204 22205 22206 22207 22208 | } 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; } 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){ |
︙ | ︙ | |||
22721 22722 22723 22724 22725 22726 22727 | 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; | | > > > > > > > > > > > > | | 22237 22238 22239 22240 22241 22242 22243 22244 22245 22246 22247 22248 22249 22250 22251 22252 22253 22254 22255 22256 22257 22258 22259 22260 22261 22262 22263 22264 22265 | 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_search(f, rid, d)){ assert(d->f); if(type!=FSL_SATYPE_ANY && type!=d->type){ rc = fsl_cx_err_set(f, FSL_RC_TYPE, "Unexpected match of RID #%" FSL_ID_T_PFMT " " "to a different artifact type (%d) " "than requested (%d).", d->type, type); fsl_cx_mcache_insert(f, d); assert(!d->f); }else{ //MARKER(("Got cached deck: %s\n", d->uuid)); } return rc; } rc = fsl_content_get(f, rid, &buf); if(rc) goto end; #if 0 MARKER(("fsl_content_get(%d) len=%d =\n%.*s\n", (int)rid, (int)buf.used, (int)buf.used, (char const*)buf.mem)); #endif fsl_deck_init(f, d, FSL_SATYPE_ANY); |
︙ | ︙ | |||
22752 22753 22754 22755 22756 22757 22758 | 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 ){ | | | 22280 22281 22282 22283 22284 22285 22286 22287 22288 22289 22290 22291 22292 22293 22294 | 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_mf(f); } } end: if(0==rc) d->rid = rid; fsl_buffer_clear(&buf); return rc; } |
︙ | ︙ | |||
22979 22980 22981 22982 22983 22984 22985 | MARKER(("Saving deck:\n%s\n", fsl_buffer_cstr(buf))); } /* Starting here, don't return, use (goto end) instead. */ f->cache.markPrivate = isPrivate; { | | | 22507 22508 22509 22510 22511 22512 22513 22514 22515 22516 22517 22518 22519 22520 22521 | 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 |
︙ | ︙ | |||
23001 23002 23003 23004 23005 23006 23007 | 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){ | | | 22529 22530 22531 22532 22533 22534 22535 22536 22537 22538 22539 22540 22541 22542 22543 | 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(): */ |
︙ | ︙ | |||
23029 23030 23031 23032 23033 23034 23035 | 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; } | | | | | | | < < < < < < < < < < < < < | | | | | | > > > | | | | | > > | | | | | > > | < | > | 22557 22558 22559 22560 22561 22562 22563 22564 22565 22566 22567 22568 22569 22570 22571 22572 22573 22574 22575 22576 22577 22578 22579 22580 22581 22582 22583 22584 22585 22586 22587 22588 22589 22590 22591 22592 22593 22594 22595 22596 22597 22598 22599 22600 22601 22602 22603 22604 22605 22606 22607 22608 22609 22610 22611 22612 22613 22614 22615 22616 22617 22618 22619 22620 22621 22622 22623 22624 22625 22626 22627 22628 22629 22630 22631 22632 22633 22634 22635 22636 22637 22638 22639 22640 22641 22642 22643 22644 22645 22646 22647 22648 22649 22650 22651 22652 22653 22654 22655 22656 22657 22658 22659 22660 22661 22662 22663 22664 22665 22666 22667 22668 22669 22670 22671 22672 22673 22674 22675 22676 22677 22678 22679 22680 22681 22682 22683 22684 22685 22686 22687 22688 22689 22690 22691 22692 22693 22694 22695 22696 22697 22698 22699 22700 22701 22702 22703 22704 22705 22706 22707 22708 22709 22710 22711 22712 22713 22714 22715 22716 22717 22718 22719 22720 22721 22722 22723 22724 22725 22726 22727 22728 22729 22730 22731 | 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 * f){ int rc = 0; fsl_db * db = fsl_cx_db_repo(f); fsl_stmt q = fsl_stmt_empty; fsl_stmt u = fsl_stmt_empty; int i; assert(f); assert(db); assert(f->cache.isCrosslinking); if(!f->cache.isCrosslinking){ return fsl_cx_err_set(f, FSL_RC_MISUSE, "Crosslink is not running."); } f->cache.isCrosslinking = false; assert(db->beginCount > 0); /* Handle any reparenting via tags... */ rc = fsl_db_prepare(db, &q, "SELECT rid, value FROM tagxref" " WHERE tagid=%d AND tagtype=%d", (int)FSL_TAGID_PARENT, (int)FSL_TAGTYPE_ADD); if(rc) goto end; while(FSL_RC_STEP_ROW==fsl_stmt_step(&q)){ 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_db_prepare(db, &q, "SELECT id FROM pending_xlink"); if(rc) goto end; while( FSL_RC_STEP_ROW==fsl_stmt_step(&q) ){ const char *zId = fsl_stmt_g_text(&q, 0, NULL); char cType; if(!zId || !*zId) continue; 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_db_exec(db, "DROP TABLE pending_xlink"); if(rc) goto end; /* If multiple check-ins happen close together in time, adjust their times by a few milliseconds to make sure they appear in chronological order. */ rc = fsl_db_prepare(db, &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_db_prepare(db, &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_db_exec(db, "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: rc = fsl_cx_uplift_db_error2(f, db, rc) /* Do before drop time_fudge to ensure we don't clear the error state by accident. */; if(!rc){ fsl_db_exec(db, "DROP TABLE time_fudge"); } if(rc) fsl_db_transaction_rollback(db); else rc = fsl_db_transaction_commit(db); return fsl_cx_uplift_db_error2(f, db, rc); } int fsl_crosslink_begin(fsl_cx * f){ int rc; fsl_db * db = fsl_cx_db_repo(f); assert(f); assert(db); assert(0==f->cache.isCrosslinking); if(f->cache.isCrosslinking){ return fsl_cx_err_set(f, FSL_RC_MISUSE, "Crosslink is already running."); } rc = fsl_db_transaction_begin(db); if(rc) return fsl_cx_uplift_db_error(f, db); rc = fsl_db_exec_multi(db, "CREATE TEMP TABLE pending_xlink(id TEXT PRIMARY KEY)WITHOUT ROWID;" "CREATE TEMP TABLE time_fudge(" " mid INTEGER PRIMARY KEY," /* The rid of a manifest */ " m1 REAL," /* The timestamp on mid */ " cid INTEGER," /* A child or mid */ " m2 REAL" /* Timestamp on the child */ ");"); if(!rc){ f->cache.isCrosslinking = 1; return 0; }else{ rc = fsl_cx_uplift_db_error2(f, db, rc); fsl_db_transaction_rollback(db); return rc; } } #undef MARKER #undef AGE_FUDGE_WINDOW #undef AGE_ADJUST_INCREMENT #undef F_at /* end of file deck.c */ |
︙ | ︙ | |||
24128 24129 24130 24131 24132 24133 24134 | /** 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( | | | 23650 23651 23652 23653 23654 23655 23656 23657 23658 23659 23660 23661 23662 23663 23664 | /** 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 */ |
︙ | ︙ | |||
24182 24183 24184 24185 24186 24187 24188 | 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( | | | 23704 23705 23706 23707 23708 23709 23710 23711 23712 23713 23714 23715 23716 23717 23718 | 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 */ |
︙ | ︙ | |||
24282 24283 24284 24285 24286 24287 24288 | *piSY = iSYb; *piEX = iEXb; *piEY = iEYb; } } | | | | | 23804 23805 23806 23807 23808 23809 23810 23811 23812 23813 23814 23815 23816 23817 23818 23819 23820 23821 23822 23823 23824 23825 23826 23827 23828 23829 23830 23831 23832 23833 23834 23835 23836 23837 23838 23839 23840 23841 23842 23843 23844 23845 23846 23847 | *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; |
︙ | ︙ | |||
24355 24356 24357 24358 24359 24360 24361 | 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. */ | | | 23877 23878 23879 23880 23881 23882 23883 23884 23885 23886 23887 23888 23889 23890 23891 | 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); } |
︙ | ︙ | |||
24390 24391 24392 24393 24394 24395 24396 | /* The two segments have nothing in common. Delete the first then insert the second. */ rc = appendTriple(p, 0, iE1-iS1, iE2-iS2); } return rc; } | | | 23912 23913 23914 23915 23916 23917 23918 23919 23920 23921 23922 23923 23924 23925 23926 | /* 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--; |
︙ | ︙ | |||
24460 24461 24462 24463 24464 24465 24466 | + return x*20; + return x*20; +} +} + int func3(int x){ int func3(int x){ return x/5; return x/5; } } */ | | | 23982 23983 23984 23985 23986 23987 23988 23989 23990 23991 23992 23993 23994 23995 23996 | + 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; |
︙ | ︙ | |||
24539 24540 24541 24542 24543 24544 24545 | /* Given a raw diff p[] in which the p->aEdit[] array has been filled in, compute a context diff into pOut. */ static int contextDiff( | | | 24061 24062 24063 24064 24065 24066 24067 24068 24069 24070 24071 24072 24073 24074 24075 | /* 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[] */ |
︙ | ︙ | |||
25421 25422 25423 25424 25425 25426 25427 | } /* Given a diff context in which the aEdit[] array has been filled in, compute a side-by-side diff into pOut. */ static int sbsDiff( | | | 24943 24944 24945 24946 24947 24948 24949 24950 24951 24952 24953 24954 24955 24956 24957 | } /* 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; |
︙ | ︙ | |||
25748 25749 25750 25751 25752 25753 25754 | /* ReCompiled *pRe, */ /* Only output changes where this Regexp matches */ short contextLines, short sbsWidth, int diffFlags_, /* FSL_DIFF_* flags */ int ** outRaw ){ int rc; | | | | | | 25270 25271 25272 25273 25274 25275 25276 25277 25278 25279 25280 25281 25282 25283 25284 25285 25286 25287 25288 25289 25290 25291 25292 25293 | /* 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; |
︙ | ︙ | |||
25828 25829 25830 25831 25832 25833 25834 | end: fsl_free(c.aFrom); fsl_free(c.aTo); fsl_free(c.aEdit); return rc; } | | | 25350 25351 25352 25353 25354 25355 25356 25357 25358 25359 25360 25361 25362 25363 25364 | 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, |
︙ | ︙ | |||
25937 25938 25939 25940 25941 25942 25943 | } 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; | | | | | 25459 25460 25461 25462 25463 25464 25465 25466 25467 25468 25469 25470 25471 25472 25473 25474 25475 25476 25477 25478 25479 25480 25481 | } 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 |
︙ | ︙ | |||
26011 26012 26013 26014 26015 26016 26017 | } assert( a ); i = 0; do{ zNL = strchr(z,'\n'); if( zNL==0 ) zNL = z+n; nn = (uint32_t)(zNL - z); | | | 25533 25534 25535 25536 25537 25538 25539 25540 25541 25542 25543 25544 25545 25546 25547 | } 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; |
︙ | ︙ | |||
26052 26053 26054 26055 26056 26057 26058 | h = (h^m)*9000000000000000041LL; } m = 0; memcpy(&m, z+x, k-k2); h ^= m; } a[i].indent = s; | | | | 25574 25575 25576 25577 25578 25579 25580 25581 25582 25583 25584 25585 25586 25587 25588 25589 25590 25591 25592 25593 25594 25595 25596 25597 25598 25599 25600 25601 25602 25603 25604 25605 | 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 ){ |
︙ | ︙ | |||
26706 26707 26708 26709 26710 26711 26712 | } /* ** Format a diff using a fsl_diff_builder object */ static int fdb__format( | | | 26228 26229 26230 26231 26232 26233 26234 26235 26236 26237 26238 26239 26240 26241 26242 | } /* ** 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[] */ |
︙ | ︙ | |||
26998 26999 27000 27001 27002 27003 27004 | */ 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; | | | 26520 26521 26522 26523 26524 26525 26526 26527 26528 26529 26530 26531 26532 26533 26534 | */ 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); |
︙ | ︙ | |||
29464 29465 29466 29467 29468 29469 29470 | gotone: if(outLen) *outLen = buf->used; if(pOut) *pOut = (char const *)buf->mem; return 0; } | | | 28986 28987 28988 28989 28990 28991 28992 28993 28994 28995 28996 28997 28998 28999 29000 | 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){ |
︙ | ︙ | |||
30133 30134 30135 30136 30137 30138 30139 | 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); } | | | 29655 29656 29657 29658 29659 29660 29661 29662 29663 29664 29665 29666 29667 29668 29669 | 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_leaf_check(fsl_cx * f, fsl_id_t rid){ fsl_db * const db = f ? fsl_cx_db_repo(f) : NULL; if(!db || !db->dbh) return FSL_RC_MISUSE; else if(rid<=0) return FSL_RC_RANGE; else { int rc = 0; bool isLeaf; fsl_cx_err_reset(f); |
︙ | ︙ | |||
30164 30165 30166 30167 30168 30169 30170 | if(rc) rc = fsl_cx_uplift_db_error2(f, db, rc); } } return rc; } } | | | 29686 29687 29688 29689 29690 29691 29692 29693 29694 29695 29696 29697 29698 29699 29700 | if(rc) rc = fsl_cx_uplift_db_error2(f, db, rc); } } return rc; } } int fsl_repo_leaf_eventually_check( fsl_cx * f, fsl_id_t rid){ fsl_db * db = f ? fsl_cx_db_repo(f) : NULL; if(!f) return FSL_RC_MISUSE; else if(rid<=0) return FSL_RC_RANGE; else if(!db) return FSL_RC_NOT_A_REPO; else { fsl_stmt * parentsOf = NULL; int rc = fsl_db_prepare_cached(db, &parentsOf, |
︙ | ︙ | |||
30190 30191 30192 30193 30194 30195 30196 | } fsl_stmt_cached_yield(parentsOf); return rc; } } | | | | | 29712 29713 29714 29715 29716 29717 29718 29719 29720 29721 29722 29723 29724 29725 29726 29727 29728 29729 29730 29731 29732 29733 | } fsl_stmt_cached_yield(parentsOf); return rc; } } int fsl_repo_leaf_do_pending_checks(fsl_cx *f){ fsl_id_t rid; int rc = 0; for(rid=fsl_id_bag_first(&f->cache.leafCheck); !rc && rid; rid=fsl_id_bag_next(&f->cache.leafCheck,rid)){ rc = fsl_repo_leaf_check(f, rid); } fsl_id_bag_clear(&f->cache.leafCheck); return rc; } int fsl_leaves_compute(fsl_cx * f, fsl_id_t vid, fsl_leaves_compute_e closeMode){ fsl_db * const db = fsl_needs_repo(f); if(!db) return FSL_RC_NOT_A_REPO; |
︙ | ︙ | |||
30234 30235 30236 30237 30238 30239 30240 | 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; | | < | | | > | > | 29756 29757 29758 29759 29760 29761 29762 29763 29764 29765 29766 29767 29768 29769 29770 29771 29772 29773 29774 29775 29776 29777 29778 29779 29780 29781 29782 29783 29784 29785 29786 29787 29788 29789 29790 29791 29792 29793 29794 29795 29796 29797 29798 29799 29800 29801 | 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. ** ** 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 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 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; |
︙ | ︙ | |||
30621 30622 30623 30624 30625 30626 30627 | #undef US3B #undef US4A #undef US4B #undef US4C #undef US0A int fsl_looks_like_utf8(fsl_buffer const * const b, int stopFlags){ | | | 30144 30145 30146 30147 30148 30149 30150 30151 30152 30153 30154 30155 30156 30157 30158 | #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; 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 */ |
︙ | ︙ | |||
30649 30650 30651 30652 30653 30654 30655 | }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; } | | | | 30172 30173 30174 30175 30176 30177 30178 30179 30180 30181 30182 30183 30184 30185 30186 30187 30188 30189 30190 30191 30192 30193 30194 30195 30196 30197 30198 30199 30200 | }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[] = { |
︙ | ︙ | |||
31451 31452 31453 31454 31455 31456 31457 | ** 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. */ | | | | 30974 30975 30976 30977 30978 30979 30980 30981 30982 30983 30984 30985 30986 30987 30988 30989 | ** 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); |
︙ | ︙ | |||
31882 31883 31884 31885 31886 31887 31888 | */ /************************************************************************ This file houses the priority queue class. */ #include <assert.h> | | | | | | | | | | 31405 31406 31407 31408 31409 31410 31411 31412 31413 31414 31415 31416 31417 31418 31419 31420 31421 31422 31423 31424 31425 31426 31427 31428 31429 31430 31431 31432 31433 31434 31435 31436 31437 31438 31439 31440 31441 31442 31443 31444 31445 31446 31447 31448 31449 31450 31451 31452 31453 31454 31455 31456 31457 31458 31459 31460 31461 31462 | */ /************************************************************************ 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; |
︙ | ︙ | |||
32010 32011 32012 32013 32014 32015 32016 | 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. */ | | | 31533 31534 31535 31536 31537 31538 31539 31540 31541 31542 31543 31544 31545 31546 31547 | 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 *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 " |
︙ | ︙ | |||
32032 32033 32034 32035 32036 32037 32038 | 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); | < | 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 |
︙ | ︙ | |||
32160 32161 32162 32163 32164 32165 32166 | fsl_free(zBr); }else{ goto oom; } } return ans; oom: | < | < < | 31682 31683 31684 31685 31686 31687 31688 31689 31690 31691 31692 31693 31694 31695 31696 | fsl_free(zBr); }else{ goto oom; } } return ans; oom: fsl_cx_err_set(f, FSL_RC_OOM, NULL); return -1; } int fsl_sym_to_rid( fsl_cx * const f, char const * sym, fsl_satype_e type, fsl_id_t * rv ){ fsl_id_t rid = 0; fsl_id_t vid; |
︙ | ︙ | |||
32386 32387 32388 32389 32390 32391 32392 | sym, fsl_satype_event_cstr(type) ); } assert(0==rc); *rv = rid; return rc; } | | | | | | 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 | sym, fsl_satype_event_cstr(type) ); } assert(0==rc); *rv = rid; return rc; } fsl_id_t fsl_uuid_to_rid2( fsl_cx * f, fsl_uuid_cstr uuid, fsl_phantom_e mode ){ if(!f) return -1; else if(!fsl_is_uuid(uuid)){ fsl_cx_err_set(f, FSL_RC_MISUSE, "fsl_uuid_to_rid2() requires a " "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; } |
︙ | ︙ | |||
32534 32535 32536 32537 32538 32539 32540 | } return rid; } } fsl_id_t fsl_repo_filename_fnid( fsl_cx * f, char const * fn ){ fsl_id_t rv = 0; | | | | 32053 32054 32055 32056 32057 32058 32059 32060 32061 32062 32063 32064 32065 32066 32067 32068 32069 32070 32071 | } 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); |
︙ | ︙ | |||
32631 32632 32633 32634 32635 32636 32637 | } return rc; } } | | | | | 32150 32151 32152 32153 32154 32155 32156 32157 32158 32159 32160 32161 32162 32163 32164 32165 32166 32167 32168 32169 32170 32171 32172 32173 32174 32175 32176 32177 32178 32179 32180 32181 32182 32183 32184 32185 32186 32187 | } return rc; } } int fsl_repo_verify_before_commit( fsl_cx * f, fsl_id_t rid ){ if(0){ /* v1 adds a commit hook here on the first entry, but it only seems to ever use one commit hook, so the infrastructure seems like overkill here. Thus this final verification is called from 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 * 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(!f || !db || (rid<=0)){ return fsl_cx_err_set(f, FSL_RC_MISUSE, "fsl_rid_to_uuid2() requires " "an opened repository and a " "positive RID value. rid=%" FSL_ID_T_PFMT, rid); }else{ fsl_stmt * st = NULL; |
︙ | ︙ | |||
32810 32811 32812 32813 32814 32815 32816 | fsl_free(uuid); fsl_buffer_clear(&hash); fsl_buffer_clear(&content); return rc; } | | | | 32329 32330 32331 32332 32333 32334 32335 32336 32337 32338 32339 32340 32341 32342 32343 32344 32345 32346 32347 32348 32349 32350 32351 32352 32353 32354 32355 32356 32357 32358 32359 32360 32361 32362 | fsl_free(uuid); fsl_buffer_clear(&hash); fsl_buffer_clear(&content); return rc; } int fsl_repo_verify_at_commit( fsl_cx * f ){ fsl_id_t rid; int rc = 0; fsl_id_bag * bag = &f->cache.toVerify; /* v1 does content_cache_clear() here. */ f->cache.inFinalVerify = 1; 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, |
︙ | ︙ | |||
32930 32931 32932 32933 32934 32935 32936 | } rc = fsl_cx_attach_role(f, opt->filename, FSL_DBROLE_REPO); if(rc){ goto end2; } db = fsl_cx_db(f); if(!f->repo.user){ | | | 32449 32450 32451 32452 32453 32454 32455 32456 32457 32458 32459 32460 32461 32462 32463 | } 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_guess_user_name() /* Ignore OOM error here - we'll use 'root' by default (but if we're really OOM here then the next op will fail). */; } userName = opt->username; |
︙ | ︙ | |||
33054 33055 33056 33057 33058 33059 33060 | "cannot ATTACH database within transaction" and installing the initial schemas outside a transaction is horribly slow. */ if( opt->configRepo && *opt->configRepo ){ bool inTrans2 = false; | | | 32573 32574 32575 32576 32577 32578 32579 32580 32581 32582 32583 32584 32585 32586 32587 | "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"); |
︙ | ︙ | |||
33321 33322 33323 33324 33325 33326 33327 | ; char const * zRole = fsl_db_role_label(roleId); assert(f->dbMain); return sqlite3_db_readonly(f->dbMain->dbh, zRole) ? 1 : 0; } } | | > < | | | 32840 32841 32842 32843 32844 32845 32846 32847 32848 32849 32850 32851 32852 32853 32854 32855 32856 32857 32858 32859 32860 32861 32862 32863 32864 32865 32866 32867 32868 32869 32870 32871 32872 32873 32874 32875 32876 32877 32878 32879 32880 32881 32882 | ; 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 * f){ fsl_buffer full = fsl_buffer_empty; fsl_db * dbR = fsl_needs_repo(f); fsl_db * dbC; fsl_db * dbConf; char const * zCDir; char const * zName = dbR ? dbR->filename : NULL; int rc; if(!dbR) return FSL_RC_NOT_A_REPO; 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 |
︙ | ︙ | |||
33397 33398 33399 33400 33401 33402 33403 | } } end: if(rc && !f->error.code && f->dbMain->error.code){ fsl_cx_uplift_db_error(f, f->dbMain); } | | | 32916 32917 32918 32919 32920 32921 32922 32923 32924 32925 32926 32927 32928 32929 32930 | } } end: if(rc && !f->error.code && f->dbMain->error.code){ fsl_cx_uplift_db_error(f, f->dbMain); } fsl_buffer_clear(&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; |
︙ | ︙ | |||
33504 33505 33506 33507 33508 33509 33510 | if(FSL_RC_BREAK==rc){ rc = 0; break; } } }/* for-each-F-card loop */ end: | | | | 33023 33024 33025 33026 33027 33028 33029 33030 33031 33032 33033 33034 33035 33036 33037 33038 33039 33040 33041 33042 33043 33044 33045 33046 33047 33048 33049 33050 33051 33052 33053 33054 33055 33056 33057 | 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; } } |
︙ | ︙ | |||
33563 33564 33565 33566 33567 33568 33569 | 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; | | | 33082 33083 33084 33085 33086 33087 33088 33089 33090 33091 33092 33093 33094 33095 33096 | 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; } |
︙ | ︙ | |||
33601 33602 33603 33604 33605 33606 33607 | if(!rc && ridOut){ *ridOut = rid; } if(hash == &hash_){ fsl_buffer_clear(hash); }else{ assert(!hash_.mem); | | | < | | 33120 33121 33122 33123 33124 33125 33126 33127 33128 33129 33130 33131 33132 33133 33134 33135 33136 33137 33138 33139 33140 33141 33142 33143 | 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 *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: * |
︙ | ︙ | |||
33675 33676 33677 33678 33679 33680 33681 | rc = FSL_RC_NOT_FOUND; break; default: rc = fsl_cx_uplift_db_error2(f, db, rc); break; } end: | | | 33193 33194 33195 33196 33197 33198 33199 33200 33201 33202 33203 33204 33205 33206 33207 | 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, |
︙ | ︙ | |||
33713 33714 33715 33716 33717 33718 33719 | if(pManifest){ fsl_buffer_reuse(pManifest); rc = fsl_content_get(f, manifestRid, pManifest); if(rc) goto end; } if(pHash){ if(f->ckout.rid!=manifestRid){ | | | 33231 33232 33233 33234 33235 33236 33237 33238 33239 33240 33241 33242 33243 33244 33245 | 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); |
︙ | ︙ | |||
33755 33756 33757 33758 33759 33760 33761 | rc = fsl_buffer_appendf(pTags, "tag %s\n", zName); if(rc) break; } fsl_stmt_finalize(&q); } end: if(bHash){ | | < < < < < < < < < < < < < < < < < < < | < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | < < | < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < | < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 33273 33274 33275 33276 33277 33278 33279 33280 33281 33282 33283 33284 33285 33286 33287 33288 33289 33290 33291 33292 33293 33294 33295 33296 33297 33298 33299 33300 33301 33302 33303 33304 33305 33306 33307 33308 33309 | 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; } /** NOT YET IMPLEMENTED. (We have the infrastructure, just need to glue it together.) Re-crosslinks all artifacts of the given type (or all artifacts if the 2nd argument is FSL_SATYPE_ANY). This is an expensive operation, involving dropping the contents of any corresponding auxiliary tables, loading and parsing the appropriate artifacts, and re-creating the auxiliary tables. TODO: add a way for callers to get some sort of progress feedback and abort the process by returning non-0 from that handler. We can possibly do that via defining an internal-use crosslink listener which carries more state, e.g. for calculating completion progress. */ //FSL_EXPORT int fsl_repo_relink_artifacts(fsl_cx *f, void * someOptionsType); #undef MARKER /* end of file repo.c */ /* start of file schema.c */ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* Copyright 2013-2021 Stephan Beal (https://wanderinghorse.net). |
︙ | ︙ | |||
34419 34420 34421 34422 34423 34424 34425 | case FSL_SATYPE_TECHNOTE: return 'e'; default: assert(!"Internal misuse of fsl_satype_letter()"); return 0; } } | | | | 33409 33410 33411 33412 33413 33414 33415 33416 33417 33418 33419 33420 33421 33422 33423 33424 33425 33426 33427 33428 33429 33430 33431 33432 33433 33434 33435 33436 | 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" |
︙ | ︙ | |||
37017 37018 37019 37020 37021 37022 37023 | * 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 * | | | | 36007 36008 36009 36010 36011 36012 36013 36014 36015 36016 36017 36018 36019 36020 36021 36022 36023 36024 36025 36026 36027 36028 36029 | * 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) |
︙ | ︙ | |||
37072 37073 37074 37075 37076 37077 37078 | * 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. | | | 36062 36063 36064 36065 36066 36067 36068 36069 36070 36071 36072 36073 36074 36075 36076 | * 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. * |
︙ | ︙ | |||
37119 37120 37121 37122 37123 37124 37125 | * 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. * | | | 36109 36110 36111 36112 36113 36114 36115 36116 36117 36118 36119 36120 36121 36122 36123 | * 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() */ |
︙ | ︙ | |||
37181 37182 37183 37184 37185 37186 37187 | #define range(low, item, hi) maximum(low, minimum(item, hi)) /* minimum --- return minimum of two numbers */ static inline int minimum(int a, int b) { | | | | | | | | | < | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > | | > | | | | | | | > > > | | > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 36171 36172 36173 36174 36175 36176 36177 36178 36179 36180 36181 36182 36183 36184 36185 36186 36187 36188 36189 36190 36191 36192 36193 36194 36195 36196 36197 36198 36199 36200 36201 36202 36203 36204 36205 36206 36207 36208 36209 36210 36211 36212 36213 36214 36215 36216 36217 36218 36219 36220 36221 36222 36223 36224 36225 36226 36227 36228 36229 36230 36231 36232 36233 36234 36235 36236 36237 36238 36239 36240 36241 36242 36243 36244 36245 36246 36247 36248 36249 36250 36251 36252 36253 36254 36255 36256 36257 36258 36259 36260 36261 36262 36263 36264 36265 36266 36267 36268 36269 36270 36271 36272 36273 36274 36275 36276 36277 36278 36279 36280 36281 36282 36283 36284 36285 36286 36287 36288 36289 36290 36291 36292 36293 36294 36295 36296 36297 36298 36299 36300 36301 36302 36303 36304 36305 36306 36307 36308 36309 36310 36311 36312 36313 36314 36315 36316 36317 36318 36319 36320 36321 36322 36323 36324 36325 36326 36327 36328 36329 36330 36331 36332 36333 36334 36335 36336 36337 36338 36339 36340 36341 36342 36343 36344 36345 36346 36347 36348 36349 36350 36351 36352 36353 36354 36355 36356 36357 36358 36359 36360 36361 36362 36363 36364 36365 36366 36367 36368 36369 36370 36371 36372 36373 36374 36375 36376 36377 36378 36379 36380 36381 36382 36383 36384 36385 36386 36387 36388 36389 36390 36391 36392 36393 36394 36395 36396 36397 36398 36399 36400 36401 36402 36403 36404 36405 36406 36407 36408 36409 36410 36411 36412 36413 36414 36415 36416 36417 36418 36419 36420 36421 36422 36423 36424 36425 36426 36427 36428 36429 36430 36431 36432 36433 36434 36435 36436 36437 36438 36439 36440 36441 36442 36443 36444 36445 36446 36447 36448 36449 36450 36451 36452 36453 36454 36455 36456 36457 36458 36459 36460 36461 36462 36463 36464 36465 36466 36467 36468 36469 36470 36471 36472 36473 36474 36475 36476 36477 36478 36479 36480 36481 36482 36483 36484 36485 36486 36487 36488 36489 36490 36491 36492 36493 36494 36495 36496 36497 36498 36499 36500 36501 36502 36503 36504 36505 36506 36507 36508 36509 36510 36511 36512 36513 36514 36515 36516 36517 36518 36519 36520 36521 36522 36523 36524 36525 36526 36527 36528 36529 36530 36531 36532 36533 36534 36535 36536 36537 36538 36539 36540 36541 36542 36543 36544 36545 36546 36547 36548 36549 36550 36551 36552 36553 36554 36555 36556 36557 36558 36559 36560 36561 36562 36563 36564 36565 36566 36567 36568 36569 36570 36571 36572 36573 36574 36575 36576 36577 36578 36579 36580 36581 36582 36583 36584 36585 36586 36587 36588 36589 36590 36591 36592 36593 36594 36595 36596 36597 36598 36599 36600 36601 36602 36603 36604 36605 36606 | #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 static char *savetz = NULL; static int savetzlen = 0; char *tz; #endif /* POSIX_SEMANTICS */ /* various tables, useful in North America */ static char *days_a[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", }; static char *days_l[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", }; static char *months_a[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; static char *months_l[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", }; static char *ampm[] = { "AM", "PM", }; if (s == NULL || format == NULL || timeptr == NULL || maxsize == 0) return 0; if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize) return 0; #ifndef POSIX_SEMANTICS if (first) { tzset(); first = 0; } #else /* POSIX_SEMANTICS */ tz = getenv("TZ"); if (first) { if (tz != NULL) { int tzlen = strlen(tz); savetz = (char *) malloc(tzlen + 1); if (savetz != NULL) { savetzlen = tzlen + 1; strcpy(savetz, tz); } } tzset(); first = 0; } /* if we have a saved TZ, and it is different, recapture and reset */ if (tz && savetz && (tz[0] != savetz[0] || strcmp(tz, savetz) != 0)) { i = strlen(tz) + 1; if (i > savetzlen) { savetz = (char *) realloc(savetz, i); if (savetz) { savetzlen = i; strcpy(savetz, tz); } } else strcpy(savetz, tz); tzset(); } #endif /* POSIX_SEMANTICS */ for (; *format && s < endp - 1; format++) { tbuf[0] = '\0'; if (*format != '%') { *s++ = *format; continue; } again: switch (*++format) { case '\0': *s++ = '%'; goto out; case '%': *s++ = '%'; continue; case 'a': /* abbreviated weekday name */ if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6) strcpy(tbuf, "?"); else strcpy(tbuf, days_a[timeptr->tm_wday]); break; case 'A': /* full weekday name */ if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6) strcpy(tbuf, "?"); else strcpy(tbuf, days_l[timeptr->tm_wday]); break; #ifdef SYSV_EXT case 'h': /* abbreviated month name */ #endif case 'b': /* abbreviated month name */ if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11) strcpy(tbuf, "?"); else strcpy(tbuf, months_a[timeptr->tm_mon]); break; case 'B': /* full month name */ if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11) strcpy(tbuf, "?"); else strcpy(tbuf, months_l[timeptr->tm_mon]); break; case 'c': /* appropriate date and time representation */ snprintf(tbuf, TBufLen, "%s %s %2d %02d:%02d:%02d %d", days_a[range(0, timeptr->tm_wday, 6)], months_a[range(0, timeptr->tm_mon, 11)], range(1, timeptr->tm_mday, 31), range(0, timeptr->tm_hour, 23), range(0, timeptr->tm_min, 59), range(0, timeptr->tm_sec, 61), timeptr->tm_year + 1900); break; case 'd': /* day of the month, 01 - 31 */ i = range(1, timeptr->tm_mday, 31); snprintf(tbuf, TBufLen, "%02d", i); break; case 'H': /* hour, 24-hour clock, 00 - 23 */ i = range(0, timeptr->tm_hour, 23); snprintf(tbuf, TBufLen, "%02d", i); break; case 'I': /* hour, 12-hour clock, 01 - 12 */ i = range(0, timeptr->tm_hour, 23); if (i == 0) i = 12; else if (i > 12) i -= 12; snprintf(tbuf, TBufLen, "%02d", i); break; case 'j': /* day of the year, 001 - 366 */ snprintf(tbuf, TBufLen, "%03d", timeptr->tm_yday + 1); break; case 'm': /* month, 01 - 12 */ i = range(0, timeptr->tm_mon, 11); snprintf(tbuf, TBufLen, "%02d", i + 1); break; case 'M': /* minute, 00 - 59 */ i = range(0, timeptr->tm_min, 59); snprintf(tbuf, TBufLen, "%02d", i); break; case 'p': /* am or pm based on 12-hour clock */ i = range(0, timeptr->tm_hour, 23); if (i < 12) strcpy(tbuf, ampm[0]); else strcpy(tbuf, ampm[1]); break; case 'S': /* second, 00 - 61 */ i = range(0, timeptr->tm_sec, 61); snprintf(tbuf, TBufLen, "%02d", i); break; case 'U': /* week of year, Sunday is first day of week */ snprintf(tbuf, TBufLen, "%d", weeknumber(timeptr, 0)); break; case 'w': /* weekday, Sunday == 0, 0 - 6 */ i = range(0, timeptr->tm_wday, 6); snprintf(tbuf, TBufLen, "%d", i); break; case 'W': /* week of year, Monday is first day of week */ snprintf(tbuf, TBufLen, "%d", weeknumber(timeptr, 1)); break; case 'x': /* appropriate date representation */ snprintf(tbuf, TBufLen, "%s %s %2d %d", days_a[range(0, timeptr->tm_wday, 6)], months_a[range(0, timeptr->tm_mon, 11)], range(1, timeptr->tm_mday, 31), timeptr->tm_year + 1900); break; case 'X': /* appropriate time representation */ snprintf(tbuf, TBufLen, "%02d:%02d:%02d", range(0, timeptr->tm_hour, 23), range(0, timeptr->tm_min, 59), range(0, timeptr->tm_sec, 61)); break; case 'y': /* year without a century, 00 - 99 */ i = timeptr->tm_year % 100; snprintf(tbuf, TBufLen, "%d", i); break; case 'Y': /* year with century */ snprintf(tbuf, TBufLen, "%d", 1900 + timeptr->tm_year); break; #if HAVE_GET_TZ_NAME case 'Z': /* time zone name or abbrevation */ strcpy(tbuf, get_tz_name(timeptr)); break; #endif #ifdef SYSV_EXT case 'n': /* same as \n */ tbuf[0] = '\n'; tbuf[1] = '\0'; break; case 't': /* same as \t */ tbuf[0] = '\t'; tbuf[1] = '\0'; break; case 'D': /* date as %m/%d/%y */ fsl_strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr); break; case 'e': /* day of month, blank padded */ snprintf(tbuf, TBufLen, "%2d", range(1, timeptr->tm_mday, 31)); break; case 'r': /* time as %I:%M:%S %p */ fsl_strftime(tbuf, sizeof tbuf, "%I:%M:%S %p", timeptr); break; case 'R': /* time as %H:%M */ fsl_strftime(tbuf, sizeof tbuf, "%H:%M", timeptr); break; case 'T': /* time as %H:%M:%S */ fsl_strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr); break; #endif #ifdef SUNOS_EXT case 'k': /* hour, 24-hour clock, blank pad */ snprintf(tbuf, TBufLen, "%2d", range(0, timeptr->tm_hour, 23)); break; case 'l': /* hour, 12-hour clock, 1 - 12, blank pad */ i = range(0, timeptr->tm_hour, 23); if (i == 0) i = 12; else if (i > 12) i -= 12; snprintf(tbuf, TBufLen, "%2d", i); break; #endif #ifdef VMS_EXT case 'v': /* date as dd-bbb-YYYY */ snprintf(tbuf, TBufLen, "%2d-%3.3s-%4d", range(1, timeptr->tm_mday, 31), months_a[range(0, timeptr->tm_mon, 11)], timeptr->tm_year + 1900); for (i = 3; i < 6; i++) if (islower((int)tbuf[i])) tbuf[i] = toupper((int)tbuf[i]); break; #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) { strcpy(s, tbuf); s += i; } else return 0; /* reminder: above IF originally had ambiguous else placement (no braces). This placement _appears_ to be correct.*/ } } out: if (s < endp && *format == '\0') { *s = '\0'; return (s - start); } else return 0; } #ifdef POSIX2_DATE /* iso8601wknum --- compute week number according to ISO 8601 */ static int iso8601wknum(const struct tm *timeptr) { /* * From 1003.2 D11.3: * If the week (Monday to Sunday) containing January 1 * has four or more days in the new year, then it is week 1; * otherwise it is week 53 of the previous year, and the * next week is week 1. * * ADR: This means if Jan 1 was Monday through Thursday, * it was week 1, otherwise week 53. */ int simple_wknum, jan1day, diff, ret; /* get week number, Monday as first day of the week */ simple_wknum = weeknumber(timeptr, 1) + 1; /* * With thanks and tip of the hatlo to tml@tik.vtt.fi * * What day of the week does January 1 fall on? * We know that * (timeptr->tm_yday - jan1.tm_yday) MOD 7 == * (timeptr->tm_wday - jan1.tm_wday) MOD 7 * and that * jan1.tm_yday == 0 * and that * timeptr->tm_wday MOD 7 == timeptr->tm_wday * from which it follows that. . . */ jan1day = timeptr->tm_wday - (timeptr->tm_yday % 7); if (jan1day < 0) jan1day += 7; /* * If Jan 1 was a Monday through Thursday, it was in * week 1. Otherwise it was last year's week 53, which is * this year's week 0. */ if (jan1day >= 1 && jan1day <= 4) diff = 0; else diff = 1; ret = simple_wknum - diff; if (ret == 0) /* we're in the first week of the year */ ret = 53; return ret; } #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 |
︙ | ︙ | |||
37700 37701 37702 37703 37704 37705 37706 | fsl_card_T_free(t); t = NULL; } } return t; } | | | 36701 36702 36703 36704 36705 36706 36707 36708 36709 36710 36711 36712 36713 36714 36715 | fsl_card_T_free(t); t = NULL; } } return t; } fsl_id_t fsl_tag_id( fsl_cx * f, char const * tag, bool create ){ fsl_db * db = fsl_cx_db_repo(f); int64_t id = 0; int rc; if(!db || !tag) return FSL_RC_MISUSE; else if(!*tag) return FSL_RC_RANGE; rc = fsl_db_get_int64( db, &id, "SELECT tagid FROM tag WHERE tagname=%Q", |
︙ | ︙ | |||
37725 37726 37727 37728 37729 37730 37731 | fsl_cx_uplift_db_error( f, db ); id = -1; } return id; } | | | | | 36726 36727 36728 36729 36730 36731 36732 36733 36734 36735 36736 36737 36738 36739 36740 36741 36742 36743 36744 36745 36746 36747 36748 36749 36750 36751 36752 36753 36754 36755 36756 36757 36758 36759 36760 36761 36762 36763 36764 | fsl_cx_uplift_db_error( f, db ); id = -1; } return id; } int fsl_tag_propagate(fsl_cx *f, fsl_tagtype_e tagType, fsl_id_t pid, fsl_id_t tagid, fsl_id_t origId, const char *zValue, double mtime){ int rc; fsl_pq queue = fsl_pq_empty /* Queue of artifacts to be tagged */; fsl_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 " |
︙ | ︙ | |||
37800 37801 37802 37803 37804 37805 37806 | if( tagid==FSL_TAGID_BGCOLOR ){ rc = fsl_db_prepare(db, &eventupdate, "UPDATE event SET bgcolor=%Q " "WHERE objid=:rid", zValue); if(rc) goto end; } | | | | | | | | | | 36801 36802 36803 36804 36805 36806 36807 36808 36809 36810 36811 36812 36813 36814 36815 36816 36817 36818 36819 36820 36821 36822 36823 36824 36825 36826 36827 36828 36829 36830 36831 36832 36833 36834 36835 36836 36837 36838 36839 36840 36841 36842 36843 36844 36845 36846 36847 36848 36849 36850 36851 36852 36853 36854 36855 36856 36857 36858 36859 36860 36861 36862 36863 36864 36865 36866 36867 36868 36869 36870 36871 36872 36873 36874 36875 36876 36877 36878 36879 36880 36881 36882 36883 36884 36885 36886 36887 36888 36889 36890 36891 36892 36893 36894 36895 36896 36897 36898 36899 36900 | 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_leaf_eventually_check(f, cid); } } } fsl_stmt_reset(&s); } end: fsl_stmt_finalize(&s); fsl_stmt_finalize(&ins); fsl_stmt_finalize(&eventupdate); fsl_pq_clear(&queue); return rc; } int fsl_tag_propagate_all(fsl_cx * f, fsl_id_t pid){ fsl_stmt q = fsl_stmt_empty; int rc; fsl_db * const db = fsl_cx_db_repo(f); if(!f) return FSL_RC_MISUSE; else if(pid<=0) return FSL_RC_RANGE; 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 * 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, 1); 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; |
︙ | ︙ | |||
37941 37942 37943 37944 37945 37946 37947 | 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 ){ | | | 36942 36943 36944 36945 36946 36947 36948 36949 36950 36951 36952 36953 36954 36955 36956 | 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_leaf_eventually_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){ |
︙ | ︙ | |||
38002 38003 38004 38005 38006 38007 38008 | " 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 */; | | > | > > | > | | | | 37003 37004 37005 37006 37007 37008 37009 37010 37011 37012 37013 37014 37015 37016 37017 37018 37019 37020 37021 37022 37023 37024 37025 37026 37027 37028 37029 37030 37031 37032 37033 37034 37035 37036 37037 37038 37039 37040 37041 37042 37043 37044 37045 37046 37047 37048 37049 37050 37051 37052 37053 37054 37055 37056 37057 37058 37059 37060 37061 | " 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: if(rc){ fsl_stmt_finalize(&q); }else{ assert(!q.stmt); if(outRid) *outRid = tagid; } return rc; } int fsl_tag_sym( fsl_cx * f, fsl_tagtype_e tagType, char const * symToTag, char const * tagName, char const * tagValue, char const * userName, double mtime, fsl_id_t * outId ){ if(!f || !tagName || !symToTag || !userName) return FSL_RC_MISUSE; else if(!*tagName || !*userName || !*symToTag) return FSL_RC_RANGE; else{ fsl_id_t resolvedRid = 0; int rc; rc = fsl_sym_to_rid( f, symToTag, FSL_SATYPE_ANY, &resolvedRid ); 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 * 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 * dbR = f ? fsl_cx_db_repo(f) : NULL; fsl_deck c = fsl_deck_empty; char * resolvedUuid = NULL; fsl_buffer mfout = fsl_buffer_empty; int rc; if(!f || !tagName || !userName) return FSL_RC_MISUSE; else if(!*tagName || !*userName || (idToTag<=0)) return FSL_RC_RANGE; |
︙ | ︙ | |||
38190 38191 38192 38193 38194 38195 38196 | if(rc) goto end; } rc = fsl_deck_T_add(&deck, FSL_TAGTYPE_PROPAGATING, NULL, "branch", opt->name); if(!rc){ /* Add tag named sym-BRANCHNAME... */ | | | | 37195 37196 37197 37198 37199 37200 37201 37202 37203 37204 37205 37206 37207 37208 37209 37210 37211 37212 37213 37214 37215 | 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{ |
︙ | ︙ | |||
38230 38231 38232 38233 38234 38235 38236 | 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. */ | | | 37235 37236 37237 37238 37239 37240 37241 37242 37243 37244 37245 37246 37247 37248 37249 | 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; } |
︙ | ︙ | |||
38278 38279 38280 38281 38282 38283 38284 | */ /************************************************************************* This file implements ticket-related parts of the library. */ #include <assert.h> #include <string.h> /* memcmp() */ | | | 37283 37284 37285 37286 37287 37288 37289 37290 37291 37292 37293 37294 37295 37296 37297 | */ /************************************************************************* 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;" ); |
︙ | ︙ | |||
38305 38306 38307 38308 38309 38310 38311 | 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; } | | | | 37310 37311 37312 37313 37314 37315 37316 37317 37318 37319 37320 37321 37322 37323 37324 37325 37326 37327 37328 37329 37330 37331 37332 | 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 * 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) ){ |
︙ | ︙ | |||
38370 38371 38372 38373 38374 38375 38376 | fsl_card_J_free(jc); break; } } fsl_stmt_finalize(&q); end: if(!rc){ | | | 37375 37376 37377 37378 37379 37380 37381 37382 37383 37384 37385 37386 37387 37388 37389 | 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: */ |
︙ | ︙ | |||
38893 38894 38895 38896 38897 38898 38899 | 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; | | | 37898 37899 37900 37901 37902 37903 37904 37905 37906 37907 37908 37909 37910 37911 37912 | 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); |
︙ | ︙ | |||
39116 39117 39118 39119 39120 39121 39122 | "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: | | | | | 38121 38122 38123 38124 38125 38126 38127 38128 38129 38130 38131 38132 38133 38134 38135 38136 38137 38138 38139 38140 38141 38142 38143 38144 38145 38146 38147 38148 38149 38150 38151 38152 38153 38154 38155 | "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 * 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; |
︙ | ︙ | |||
39190 39191 39192 39193 39194 39195 39196 | 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); | | | | 38195 38196 38197 38198 38199 38200 38201 38202 38203 38204 38205 38206 38207 38208 38209 38210 38211 38212 38213 | 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 */; |
︙ | ︙ | |||
39218 39219 39220 39221 39222 39223 39224 | } 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; } | | | | | | | | | | 38223 38224 38225 38226 38227 38228 38229 38230 38231 38232 38233 38234 38235 38236 38237 38238 38239 38240 38241 38242 38243 38244 38245 38246 38247 38248 38249 38250 38251 38252 38253 38254 38255 38256 38257 38258 38259 38260 38261 38262 38263 38264 38265 38266 38267 38268 38269 38270 | } 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){ |
︙ | ︙ | |||
39900 39901 39902 39903 39904 39905 39906 | } } if(rc) goto end; { char * u = NULL; if(!userName) userName = fsl_cx_user_get(f); if(!userName){ | | | 38905 38906 38907 38908 38909 38910 38911 38912 38913 38914 38915 38916 38917 38918 38919 | } } if(rc) goto end; { char * u = NULL; if(!userName) userName = fsl_cx_user_get(f); if(!userName){ u = fsl_guess_user_name(); if(!u) rc = FSL_RC_OOM; } if(!rc) rc = fsl_deck_U_set(&d, u ? u : userName); if(u) fsl_free(u); if(rc) goto end; } rc = fsl_deck_W_set(&d, fsl_buffer_cstr(b), (fsl_int_t)b->used); |
︙ | ︙ | |||
39985 39986 39987 39988 39989 39990 39991 | 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; } | | | | | | | 38990 38991 38992 38993 38994 38995 38996 38997 38998 38999 39000 39001 39002 39003 39004 39005 39006 39007 39008 39009 39010 39011 39012 39013 39014 39015 39016 39017 39018 39019 39020 39021 39022 39023 39024 39025 39026 39027 39028 39029 39030 39031 39032 39033 39034 39035 39036 39037 39038 39039 | 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 * z ){ return z ? &z->body : NULL; } void fsl_zip_timestamp_set_julian(fsl_zip_writer *z, double rDate){ char buf[20] = {0}; fsl_julian_to_iso8601(rDate, buf, 0); fzip_timestamp_from_str(z, buf); z->unixTime = (fsl_time_t)((rDate - 2440587.5)*86400.0); } void fsl_zip_timestamp_set_unix(fsl_zip_writer *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 * 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 *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; |
︙ | ︙ | |||
40164 40165 40166 40167 40168 40169 40170 | fzip_put16(&zExTime[2], 5); fsl_buffer_append(&zw->toc, zExTime, 9); ++zw->entryCount; return rc; } | | | 39169 39170 39171 39172 39173 39174 39175 39176 39177 39178 39179 39180 39181 39182 39183 | fzip_put16(&zExTime[2], 5); fsl_buffer_append(&zw->toc, zExTime, 9); ++zw->entryCount; return rc; } int fzip_mkdir(fsl_zip_writer * z, char const *zName){ fsl_size_t i; fsl_size_t j; int rc = 0; char const * dirName; fsl_size_t nDir = z->dirs.used; for(i=0; zName[i]; i++){ if( zName[i]=='/' ){ |
︙ | ︙ | |||
40192 40193 40194 40195 40196 40197 40198 | } } } } return rc; } | | | | 39197 39198 39199 39200 39201 39202 39203 39204 39205 39206 39207 39208 39209 39210 39211 39212 39213 39214 39215 39216 | } } } } return rc; } int fsl_zip_file_add(fsl_zip_writer *z, char const * zName, fsl_buffer const * pSrc, int mPerm){ return fzip_file_add(z, zName, pSrc, mPerm, 1); } int fsl_zip_root_set(fsl_zip_writer * 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){ |
︙ | ︙ | |||
40247 40248 40249 40250 40251 40252 40253 | return rc; } } return 0; } } | | | | | 39252 39253 39254 39255 39256 39257 39258 39259 39260 39261 39262 39263 39264 39265 39266 39267 39268 39269 39270 39271 39272 39273 39274 39275 39276 39277 39278 39279 39280 39281 39282 39283 39284 39285 39286 39287 39288 39289 | return rc; } } return 0; } } static void fsl_zip_finalize_impl(fsl_zip_writer * z, char alsoBody){ if(z){ fsl_buffer_clear(&z->toc); fsl_buffer_clear(&z->scratch); fsl_list_visit_free(&z->dirs, 1); assert(NULL==z->dirs.list); 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 * z){ fsl_zip_finalize_impl(z, 1); } int fsl_zip_end( fsl_zip_writer * 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); |
︙ | ︙ | |||
40298 40299 40300 40301 40302 40303 40304 | rc = fsl_buffer_append(&z->body, zBuf, 22); fsl_zip_finalize_impl(z, 0); assert(z->body.used); return rc; } | | | | 39303 39304 39305 39306 39307 39308 39309 39310 39311 39312 39313 39314 39315 39316 39317 39318 39319 39320 39321 39322 39323 39324 39325 39326 39327 39328 39329 39330 39331 39332 39333 39334 | 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 * 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 * 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); |
︙ | ︙ | |||
40437 40438 40439 40440 40441 40442 40443 | sym); } if(rc) goto end; } } /** | | | | | | | | 39442 39443 39444 39445 39446 39447 39448 39449 39450 39451 39452 39453 39454 39455 39456 39457 39458 39459 39460 39461 39462 39463 39464 39465 39466 39467 39468 39469 39470 39471 39472 39473 39474 39475 39476 39477 | sym); } if(rc) goto end; } } /** Always write he manifest files to the zip, regardless of the repo-level settings. */ if(rc) goto end; else { fsl_buffer * const bManifest = &f->fileContent; fsl_buffer * const bHash = fsl_cx_scratchpad(f); fsl_buffer * const bTags = fsl_cx_scratchpad(f); fsl_buffer_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.
︙ | ︙ | |||
463 464 465 466 467 468 469 470 471 472 473 474 475 476 | */ #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 | > | 463 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 |
︙ | ︙ | |||
1028 1029 1030 1031 1032 1033 1034 | /** 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 ); /** | | > > | | | | < < | | | | 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 1068 | /** 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. If !err it returns FSL_RC_MISUSE without side-effects, else it returns err's current error code. If str is not NULL then *str will be assigned to the raw (NUL-terminated) error string (which might be empty or even NULL). The memory for the string is owned by err and may be invalidated by any calls which take err as a non-const parameter OR which might modify it indirectly through a container object, so the client is required to copy it if it is needed for later reference. If len is not NULL then *len will be assigned to the length of the returned 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 or if err holds no dynamically allocated no memory. @see fsl_error_set() @see fsl_error_get() @see fsl_error_move() @see fsl_error_reset() */ FSL_EXPORT void fsl_error_clear( fsl_error * const err ); |
︙ | ︙ | |||
1493 1494 1495 1496 1497 1498 1499 | /** 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. */ | | | | 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 | /** 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 * src, fsl_buffer * dest ); /** Apply the delta in pDelta to the original content pOriginal to generate the target content pTarget. All three pointers must point to properly initialized memory. If pTarget==pOriginal then this is a destructive operation, |
︙ | ︙ | |||
1780 1781 1782 1783 1784 1785 1786 | 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. */ | | | | 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 | 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 size_t fsl_strlcpy(char *restrict dst, const char *restrict src, size_t dstsz); /** BSD strlcat() variant which is less error prone than strncat. Append src to the end of dst. Append at most dstsz - strlen(dst - 1) characters, and NUL-terminate unless dstsize is 0 or the passed in dst string was longer than dstsz to begin with. */ FSL_EXPORT size_t fsl_strlcat(char *restrict dst, const char *restrict src, size_t dstsz); /** Equivalent to fsl_strncmp(lhs, rhs, X), where X is either FSL_STRLEN_SHA1 or FSL_STRLEN_K256: if both lhs and rhs are longer than FSL_STRLEN_SHA1 then they are assumed to be FSL_STRLEN_K256 bytes long and are compared as such, else they are assumed to be FSL_STRLEN_SHA1 bytes long and compared as |
︙ | ︙ | |||
2812 2813 2814 2815 2816 2817 2818 | 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() */ | | | 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 | 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_guess_user_name(); /** Tries to find the user's home directory. If found, 0 is returned, tgt's memory is _overwritten_ (not appended) with the path, and tgt->used is set to the path's string length. (Design note: the overwrite behaviour is inconsistent with most of the API, but the implementation currently requires this.) |
︙ | ︙ | |||
3140 3141 3142 3143 3144 3145 3146 | /** 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, | | | | | | > | | | < < | | | > | | | 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 3185 | /** 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_MISUSE if any pointer argument is NULL. Returns FSL_RC_RANGE if lenDelta is too short to be a delta. Returns FSL_RC_DELTA_INVALID_TERMINATOR if the delta's encoded length is not properly terminated. This routine is provided so that an procedure that is able to call fsl_delta_apply() can learn how much space is required for the output and hence allocate nor more space that is really needed. TODO?: consolidate 2nd and 3rd parameters into one i/o parameter? @see fsl_delta_apply() @see fsl_delta_create() */ FSL_EXPORT int fsl_delta_applied_size(unsigned char const *zDelta, fsl_size_t lenDelta, fsl_size_t * appliedSize); /** "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 |
︙ | ︙ | |||
3350 3351 3352 3353 3354 3355 3356 | 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. | | | 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 | 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 in favor of fsl_diff2_flag_e and fsl_diff_v2() */ enum fsl_diff_flag_e { /** Ignore end-of-line whitespace */ FSL_DIFF_IGNORE_EOLWS = 0x01, /** Ignore end-of-line whitespace */ FSL_DIFF_IGNORE_ALLWS = 0x03, /** Generate a side-by-side diff */ |
︙ | ︙ | |||
3458 3459 3460 3461 3462 3463 3464 | @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 ); | < | | 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 ); /** 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 | 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 * 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 |
︙ | ︙ | |||
4757 4758 4759 4760 4761 4762 4763 | @see fsl_zip_timestamp_set_julian() @see fsl_zip_timestamp_set_unix() @see fsl_zip_end() @see fsl_zip_body() @see fsl_zip_finalize() */ | | < | < | 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 | @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 * 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. |
︙ | ︙ | |||
4783 4784 4785 4786 4787 4788 4789 | @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() */ | | | < | < | | | < | < | 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 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 | @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 * 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 * z, fsl_buffer * dest ); /** This variant of fsl_zip_end_take() passes z to fsl_zip_end(), write's the ZIP body to the given filename, passes z to fsl_zip_finalize(), and returns the result of 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 * 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 * 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 * 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 *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 *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() |
︙ | ︙ | |||
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. | < < < | 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, |
︙ | ︙ | |||
5964 5965 5966 5967 5968 5969 5970 | 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. | | < < < < < < < | | 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 | 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 will hurt performance badly for certain operations. */ FSL_CX_F_MANIFEST_CACHE = 0x08, /** 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 }; 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 |
︙ | ︙ | |||
6266 6267 6268 6269 6270 6271 6272 | 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, | < < < < < < | 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 }; |
︙ | ︙ | |||
6497 6498 6499 6500 6501 6502 6503 | 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. */ | | < < | < < | | | | | | | | | | < | | | < | < | < | | | < < < < < | 6474 6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495 6496 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 | 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 * 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). */ FSL_EXPORT int fsl_cx_flag_set( fsl_cx * f, int flags, bool enable ); /** Returns f's flags. */ FSL_EXPORT int fsl_cx_flags_get( fsl_cx * f ); /** Sets the Fossil error state to the given error code and fsl_appendf()-style format string/arguments. On success it returns the code parameter. It does not return 0 unless code is 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 * f, int code, char const * fmt, ... ); /** va_list counterpart to fsl_cx_err_set(). */ FSL_EXPORT int fsl_cx_err_setv( fsl_cx * f, int code, char const * fmt, va_list args ); /** Fetches the error state from f. See fsl_error_get() for the semantics of the parameters and return value. */ FSL_EXPORT int fsl_cx_err_get( fsl_cx * 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). Is a no-op if f is NULL. This may be necessary for apps if they rely on looking at fsl_cx_err_get() at the end of their app/routine, because error state survives until it is cleared, even if the error held there was caught and recovered. This function might keep error string memory around for re-use later on. */ FSL_EXPORT void fsl_cx_err_reset(fsl_cx * 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 * f, fsl_error * err ); /** If f has error state then it outputs its error state to its output channel and returns the result of fsl_output(). Returns FSL_RC_MISUSE if !f, 0 if f has no error state our output of the state succeeds. If addNewline is true then it adds a trailing newline to the output, else it does not. This is intended for testing and debugging only, and not as an error reporting mechanism for a full-fledged application. */ FSL_EXPORT int fsl_cx_err_report( fsl_cx * const f, bool addNewline ); /** Unconditionally Moves db->error's state into f. If db is NULL then f's primary db connection is used. Returns FSL_RC_MISUSE if !f or (!db && f-is-not-opened). On success it returns f's new error code. The main purpose of this function is to propagate db-level errors up to higher-level code which deals directly with the f object but not the underlying db(s). @see fsl_cx_uplift_db_error2() */ FSL_EXPORT int fsl_cx_uplift_db_error( fsl_cx * const f, fsl_db * db ); /** 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. 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); /** Outputs the first n bytes of src to f's configured output |
︙ | ︙ | |||
6716 6717 6718 6719 6720 6721 6722 | @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 | | | | < < < < | 6680 6681 6682 6683 6684 6685 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 | @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 and returns 0. Returns FSL_RC_NOT_FOUND if f has not opened a repository (that can normally be ignored but is provided for completeness's sake). If a repository is opened "indirectly" via fsl_ckout_open_dir() then attempting to close it using this function will result in FSL_RC_MISUSE and f's error state will hold a description of the problem (the checkout must be closed before closing its repository). Such a repository will be closed implicitly when the checkout db is closed. |
︙ | ︙ | |||
6745 6746 6747 6748 6749 6750 6751 | 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: ``` | | < < | < < < < < < < < < < < < | | < < | | 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734 | 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_guess_user_name(); int rc = fsl_cx_user_set(f, u); fsl_free(u); ``` (Sorry about the extra string copy there, but adding a function which passes ownership of the name string seems like overkill.) */ FSL_EXPORT int fsl_cx_user_set( fsl_cx * f, char const * userName ); /** Returns the name set by fsl_cx_user_set(), or NULL if f has no default user name set. The returned bytes are owned by f and may be invalidated by any call to fsl_cx_user_set(). */ FSL_EXPORT char const * fsl_cx_user_get( fsl_cx const * f ); /** Configuration parameters for fsl_repo_create(). Always copy-construct these from fsl_repo_create_opt_empty resp. fsl_repo_create_opt_empty_m in order to ensure proper behaviour vis-a-vis default values. |
︙ | ︙ | |||
7033 7034 7035 7036 7037 7038 7039 | 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 | | < < < | > > | < < < | 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 | 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 and returns 0. Returns FSL_RC_MISUSE if f has any transactions pending, FSL_RC_NOT_FOUND if f has not opened a checkout (which can safely be ignored and does not update f's error state). This also closes the repository which was implicitly opened for the checkout. */ FSL_EXPORT int fsl_ckout_close( fsl_cx * const f ); /** Attempts to close any opened databases (repo/checkout/config). This will fail if any transactions are pending. Any databases which are already closed are silently skipped. */ 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 |
︙ | ︙ | |||
7236 7237 7238 7239 7240 7241 7242 | false. @see fsl_needs_ckout() */ FSL_EXPORT bool fsl_cx_has_ckout(fsl_cx const * const f ); /** | | < | < < < < < | | | < | < < | | < < < < < | < < | < < < < | 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 7201 7202 7203 7204 7205 7206 7207 7208 7209 7210 7211 7212 7213 | 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, it is closed before opening the new one. The database is created and populated with an initial schema if needed. If dbName is NULL or empty then it uses a default db name, "probably" under the user's home directory. To get the name of the database after it has been opened/attached, use fsl_cx_db_file_config(). TODO: strongly consider supporting non-attached (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. @see fsl_cx_db_config() @see fsl_config_close() */ 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 succes, FSL_RC_MISUSE if !f, FSL_RC_NOT_FOUND if no config db connection is opened/attached. @see fsl_cx_db_config() @see fsl_config_open() */ FSL_EXPORT int fsl_config_close( fsl_cx * const f ); /** |
︙ | ︙ | |||
7311 7312 7313 7314 7315 7316 7317 | /** 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 ); | < < < < < < < < < < < < < < < < | 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. |
︙ | ︙ | |||
7825 7826 7827 7828 7829 7830 7831 | 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() */ | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 7729 7730 7731 7732 7733 7734 7735 7736 7737 7738 7739 7740 7741 7742 7743 7744 7745 | 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 *f, fsl_confirm_detail const * detail, fsl_confirm_response *outAnswer); #if 0 /** DO NOT USE - not yet tested and ready. Returns the result of either localtime(clock) or gmtime(clock), depending on f: |
︙ | ︙ | |||
8366 8367 8368 8369 8370 8371 8372 | /** The number of times this statement has fetched a row via fsl_stmt_step(). */ fsl_size_t rowCount; | < < < < < < < < < < < | 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 |
︙ | ︙ | |||
8412 8413 8414 8415 8416 8417 8418 | #define fsl_stmt_empty_m { \ NULL/*db*/, \ NULL/*stmt*/, \ fsl_buffer_empty_m/*sql*/, \ 0/*colCount*/, \ 0/*paramCount*/, \ 0/*rowCount*/, \ | < | 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*/ \ } /** |
︙ | ︙ | |||
9757 9758 9759 9760 9761 9762 9763 | 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. | | | 9587 9588 9589 9590 9591 9592 9593 9594 9595 9596 9597 9598 9599 9600 9601 | 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. |
︙ | ︙ | |||
10515 10516 10517 10518 10519 10520 10521 | /** 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 | | | 10345 10346 10347 10348 10349 10350 10351 10352 10353 10354 10355 10356 10357 10358 10359 | /** 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; }; |
︙ | ︙ | |||
11520 11521 11522 11523 11524 11525 11526 | 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. | < < < < | < | 11350 11351 11352 11353 11354 11355 11356 11357 11358 11359 11360 11361 11362 11363 11364 11365 | 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. */ FSL_EXPORT void fsl_deck_init( fsl_cx * cx, fsl_deck * d, fsl_satype_e type ); /** Returns true if d contains data for all _required_ cards, as determined by the value of d->type, else returns false. It returns false if d->type==FSL_SATYPE_ANY, as that is a placeholder value intended to be re-set by the deck's user. |
︙ | ︙ | |||
11701 11702 11703 11704 11705 11706 11707 | (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() | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < | | | | | | | | | < | | < | | | | < < < < < < < < | | 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 | (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 ); /** This starts a transaction (possibly nested) on the repository db and initializes some temporary db state needed for the crosslinking certain artifact types. It "should" (see below) be called at the start of the crosslinking process. Crosslinking *can* work without this but certain steps for certain (subject to change) artifact types will be skipped, possibly leading to unexpected timeline data or similar funkiness. No permanent SCM-relevant state will be missing, but the timeline might not be updated and tickets might not be fully processed. This should be used before crosslinking any artifact types, but will only have significant side effects for certain (subject to change) types. Returns 0 on success. If this function succeeds, the caller is OBLIGATED to either call fsl_crosslink_end() or fsl_db_transaction_rollback(), depending on whether the work done after this call succeeds resp. fails. This process may install temporary tables and/or triggers, so failing to call one or the other of those will result in misbehavior. @see fsl_deck_crosslink() */ int fsl_crosslink_begin(fsl_cx * f); /** Must not be called unless fsl_crosslink_begin() has succeeded. This performs crosslink post-processing on certain artifact types and cleans up any temporary db state initialized by fsl_crosslink_begin(). Returns 0 on success. On error it initiates (or propagates) a rollback for the current transaction. */ int fsl_crosslink_end(fsl_cx * f); /** Parses src as Control Artifact content and populates d with it. d will be cleaned up before parsing if it has any contents, retaining its d->f member (which must be non-NULL for 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 (possibly!) the ownership of its contents. In any case, the content of the source buffer is modified by this function because (A) that simplifies tokenization greatly, (B) saves us having to make another copy to work on, (C) the original implementation did it that way, (D) because in historical use the source is normally thrown away after parsing, anyway, and (E) in combination with taking ownership of src's contents it allows us to optimize away some memory allocations by re-using the internal memory of the buffer. This function never changes src's size, but it mutilates its contents (injecting NUL bytes as token delimiters). 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. (Ideally, outputing d via fsl_deck_output() will produce a lossless copy of the original.) On error, if there is error information to propagate beyond the result code then it is stored in d->f (if that is not NULL), else in d->error. Whether or not such error info is propagated depends on the type of error, but anything more trivial than invalid arguments will be noted there. 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. - 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. |
︙ | ︙ | |||
11799 11800 11801 11802 11803 11804 11805 | /** 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 | | | | | | | 11649 11650 11651 11652 11653 11654 11655 11656 11657 11658 11659 11660 11661 11662 11663 11664 11665 11666 11667 11668 11669 11670 11671 11672 11673 11674 11675 11676 11677 11678 11679 11680 | /** 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 will be) only used as an optimization in other places. Passing a positive value has no effect on how the content is parsed or on the result - it only affects internal details/optimizations. */ FSL_EXPORT int fsl_deck_parse2(fsl_deck * const d, fsl_buffer * const src, fsl_id_t rid); /** Quickly determines whether the content held by the given buffer "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 * 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 |
︙ | ︙ | |||
11952 11953 11954 11955 11956 11957 11958 | 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() */ | | | 11802 11803 11804 11805 11806 11807 11808 11809 11810 11811 11812 11813 11814 11815 11816 | 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 * d, void * state); /** A type for holding a callback/state pair for manifest crosslinking callbacks. */ struct fsl_xlinker { char const * name; |
︙ | ︙ | |||
12004 12005 12006 12007 12008 12009 12010 | 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 | | | 11854 11855 11856 11857 11858 11859 11860 11861 11862 11863 11864 11865 11866 11867 11868 | 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. |
︙ | ︙ | |||
12308 12309 12310 12311 12312 12313 12314 | 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 | | | 12158 12159 12160 12161 12162 12163 12164 12165 12166 12167 12168 12169 12170 12171 12172 | 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_guess_user_name() are used (in that order) to determine the name. mimeType specifies the mime type for the content (may be NULL). Mime type names supported directly by fossil(1) include (as of this writing): text/x-fossil-wiki, text/x-markdown, text/plain |
︙ | ︙ | |||
12908 12909 12910 12911 12912 12913 12914 | 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 | | | | | 12758 12759 12760 12761 12762 12763 12764 12765 12766 12767 12768 12769 12770 12771 12772 12773 12774 12775 12776 12777 12778 12779 12780 12781 12782 12783 12784 12785 12786 12787 12788 12789 12790 12791 12792 12793 12794 12795 12796 12797 12798 12799 12800 12801 12802 12803 12804 12805 | 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_guess_user_name() to try to figure out a proper user name based on the environment. See also: fsl_cx_user_get(), but note that the application must first use fsl_cx_user_set() to set a context's user name. mtime is the Julian Day timestamp for the new artifact. Pass a 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 * 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 * 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. |
︙ | ︙ | |||
13177 13178 13179 13180 13181 13182 13183 | 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 | | | 13027 13028 13029 13030 13031 13032 13033 13034 13035 13036 13037 13038 13039 13040 13041 | 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() |
︙ | ︙ | |||
13884 13885 13886 13887 13888 13889 13890 | 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 ); | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 */ |
︙ | ︙ | |||
14343 14344 14345 14346 14347 14348 14349 | 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 | | | 14025 14026 14027 14028 14029 14030 14031 14032 14033 14034 14035 14036 14037 14038 14039 | 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_revert() 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 |
︙ | ︙ | |||
16107 16108 16109 16110 16111 16112 16113 | 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. */ | | | 15789 15790 15791 15792 15793 15794 15795 15796 15797 15798 15799 15800 15801 15802 15803 | 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 * 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 |
︙ | ︙ | |||
16164 16165 16166 16167 16168 16169 16170 | 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() */ | | | | | | 15846 15847 15848 15849 15850 15851 15852 15853 15854 15855 15856 15857 15858 15859 15860 15861 15862 15863 15864 15865 15866 15867 15868 15869 15870 15871 15872 15873 15874 15875 15876 15877 15878 15879 15880 15881 15882 15883 | 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 * f, fsl_id_t vid, fsl_id_bag * dest, char const * zName, bool changedOnly); /** This is a variant of fsl_filename_to_vfile_ids() which accepts 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 * f, fsl_id_t vid, fsl_id_bag * dest, char const * zName, bool relativeToCwd, bool changedOnly ); /** This "mostly internal" routine (re)populates f's checkout vfile table with all files from the given checkin manifest. If manifestRid is 0 or less then the current checkout's RID is |
︙ | ︙ | |||
16572 16573 16574 16575 16576 16577 16578 | 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 { | < < | 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. */ |
︙ | ︙ | |||
16639 16640 16641 16642 16643 16644 16645 | 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). */ | | | | | | | | | 16319 16320 16321 16322 16323 16324 16325 16326 16327 16328 16329 16330 16331 16332 16333 16334 16335 16336 16337 16338 16339 16340 16341 16342 16343 16344 16345 16346 16347 16348 16349 16350 16351 16352 16353 16354 16355 16356 16357 16358 16359 16360 16361 16362 16363 16364 16365 16366 16367 16368 16369 16370 16371 16372 16373 16374 16375 16376 16377 16378 16379 16380 16381 16382 16383 16384 16385 | 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 |
︙ | ︙ | |||
16731 16732 16733 16734 16735 16736 16737 | 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() */ | | | | 16411 16412 16413 16414 16415 16416 16417 16418 16419 16420 16421 16422 16423 16424 16425 16426 | 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 * f, fsl_confdb_e mode, char const * key, fsl_buffer * b ); /** If f has an opened checkout, this replaces b's contents (re-using any existing memory) with an absolute path to the filename for that setting: {CHECKOUT_ROOT}/.fossil-settings/{key} |
︙ | ︙ | |||
16785 16786 16787 16788 16789 16790 16791 | 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() */ | | | | | | | | | | | | < < | | 16465 16466 16467 16468 16469 16470 16471 16472 16473 16474 16475 16476 16477 16478 16479 16480 16481 16482 16483 16484 16485 16486 16487 16488 16489 16490 16491 16492 16493 16494 16495 16496 16497 16498 16499 16500 16501 16502 16503 16504 16505 16506 16507 16508 16509 16510 16511 16512 16513 16514 16515 16516 16517 16518 16519 16520 16521 16522 16523 16524 16525 16526 16527 16528 16529 16530 16531 16532 16533 16534 16535 16536 16537 16538 16539 16540 16541 16542 16543 16544 16545 16546 16547 16548 16549 16550 16551 16552 16553 16554 16555 16556 16557 16558 16559 16560 16561 16562 16563 16564 16565 16566 16567 16568 16569 16570 16571 16572 16573 16574 16575 16576 16577 16578 16579 16580 16581 16582 16583 16584 16585 16586 16587 16588 16589 | 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * f, fsl_confdb_e mode, char rollback); /** Populates li as a glob list from the given configuration key. Uses (versionable/repo/global) config settings, in that order. It is not an error if one or more of those sources is missing - 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. */ FSL_EXPORT int fsl_config_globs_load(fsl_cx * f, fsl_list * li, char const * key); /** Fetches the preferred name of the "global" db file for the current user by assigning it to *zOut. Returns 0 on success, in which case *zOut is updated and non-0 on error, in which case *zOut is not modified. On success, ownership of *zOut is transferred to the caller, who must eventually free it using fsl_free(). |
︙ | ︙ | |||
16928 16929 16930 16931 16932 16933 16934 | 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: | | > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 16606 16607 16608 16609 16610 16611 16612 16613 16614 16615 16616 16617 16618 16619 16620 16621 16622 16623 16624 16625 | 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 simply uses the Windows home directory + "/_fossil" or "/.fossil", depending on the build-time environment. */ FSL_EXPORT int fsl_config_global_preferred_name(char ** zOut); #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 */ |
︙ | ︙ | |||
17278 17279 17280 17281 17282 17283 17284 | */ #if defined(__cplusplus) extern "C" { #endif | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > | | < | | | | | < | | | | | < < < < < < < < < | | | | | | < | | | | | > > > < < < | | | | | | > | | | | | 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 16912 16913 16914 16915 16916 16917 16918 16919 16920 16921 16922 16923 16924 16925 16926 16927 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 17022 17023 17024 17025 17026 17027 17028 17029 17030 17031 17032 17033 17034 17035 17036 17037 17038 17039 17040 17041 17042 17043 17044 17045 17046 17047 17048 17049 17050 17051 17052 17053 17054 17055 17056 17057 17058 17059 17060 17061 17062 17063 17064 17065 17066 17067 17068 17069 17070 17071 17072 17073 17074 17075 17076 17077 17078 17079 17080 17081 17082 17083 17084 17085 17086 17087 17088 17089 17090 17091 17092 17093 17094 17095 17096 17097 17098 17099 17100 17101 17102 17103 17104 17105 17106 17107 17108 17109 17110 17111 17112 17113 17114 17115 17116 17117 17118 17119 17120 17121 17122 17123 17124 17125 17126 17127 17128 17129 17130 17131 17132 17133 17134 17135 17136 17137 17138 | */ #if defined(__cplusplus) extern "C" { #endif typedef struct fsl_acache fsl_acache; typedef struct fsl_acache_line fsl_acache_line; typedef struct fsl_pq fsl_pq; typedef struct fsl_pq_entry fsl_pq_entry; /** @internal Queue entry type for the fsl_pq class. 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_acache cache. */ struct fsl_acache_line { /** RID of the cached record. */ fsl_id_t rid; /** Age. Newer is larger. */ fsl_int_t age; /** Content of the artifact. */ fsl_buffer content; }; /** @internal Empty-initialized fsl_acache_line structure. */ #define fsl_acache_line_empty_m { 0,0,fsl_buffer_empty_m } /** @internal A cache for tracking the existence of artifacts while the internal goings-on of control artifacts are going on. Currently the artifact cache is unused because it costs much more than it gives us. Once the library supports certain operations (like rebuild and sync) caching will become more useful. Historically fossil caches artifacts as their blob content, but libfossil will likely (at some point) to instead cache fsl_deck instances, which contain all of the same data in pre-parsed form. It cost more memory, though. That approach also precludes caching non-structural artifacts (i.e. opaque client blobs). Potential TODO: the limits of the cache size are hard-coded in fsl_acache_insert. Those really should be part of this struct. */ struct fsl_acache { /** Total amount of buffer memory (in bytes) used by cached content. This does not account for memory held by this->list. */ fsl_size_t szTotal; /** Limit on the (approx.) amount of memory (in bytes) which can be taken up by the cached buffers at one time. Fossil's historical value is 50M. */ fsl_size_t szLimit; /** Number of entries "used" in this->list. */ uint16_t used; /** Approximate upper limit on the number of entries in this->list. This limit may be violated slightly. This 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_int_t nextAge; /** List of cached content, ordered by age. */ fsl_acache_line * list; /** All artifacts currently in the cache. */ fsl_id_bag inCache; /** Cache of known-missing content. */ fsl_id_bag missing; /** Cache of of known-existing content. */ fsl_id_bag available; }; /** @internal Empty-initialized fsl_acache structure, intended for const-copy initialization. */ #define fsl_acache_empty_m { \ 0/*szTotal*/, \ 20000000/*szLimit. Historical fossil value=50M*/, \ 0/*used*/,300U/*usedLimit. Historical fossil value=500*/,\ 0/*capacity*/, \ 0/*nextAge*/,NULL/*list*/, \ fsl_id_bag_empty_m/*inCache*/, \ fsl_id_bag_empty_m/*missing*/, \ fsl_id_bag_empty_m/*available*/ \ } /** @internal Empty-initialized fsl_acache structure, intended for copy initialization. */ extern const fsl_acache fsl_acache_empty; /** @internal Very internal. "Manifest cache" for fsl_deck entries encountered during crosslinking. This type is intended only to be embedded in fsl_cx. The routines for managing this cache are static in deck.c: fsl_cx_mcache_insert() and fsl_cx_mcache_search(). 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. */ unsigned nextAge; /** The virtual age of each deck in the cache. They get evicted oldest first. */ unsigned aAge[4]; /** Counts the number of cache hits. */ unsigned hits; /** Counts the number of cache misses. */ unsigned misses; /** 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-03-26) 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,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. |
︙ | ︙ | |||
17726 17727 17728 17729 17730 17731 17732 | /** 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. | | | | | < < < < < < < | 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 | /** 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; /** List of callbacks for deck crosslinking purposes. */ fsl_xlinker_list xlinkers; /** A place for caching generic things. |
︙ | ︙ | |||
17811 17812 17813 17814 17815 17816 17817 | 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; /** | | | | 17400 17401 17402 17403 17404 17405 17406 17407 17408 17409 17410 17411 17412 17413 17414 17415 | 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. |
︙ | ︙ | |||
17890 17891 17892 17893 17894 17895 17896 | 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; /** | | | | 17479 17480 17481 17482 17483 17484 17485 17486 17487 17488 17489 17490 17491 17492 17493 17494 17495 | 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; /** Artifact cache used during processing of manifests. */ fsl_acache arty; /** Used during manifest parsing to keep track of artifacts we have seen. Whether that's really necessary or is now an unnecessary porting artifact (haha) is unclear. */ fsl_id_bag mfSeen; /** |
︙ | ︙ | |||
17932 17933 17934 17935 17936 17937 17938 | two functions in some cases. */ fsl_fstat fstat; /** Parsed-deck cache. */ | | | 17521 17522 17523 17524 17525 17526 17527 17528 17529 17530 17531 17532 17533 17534 17535 | 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. |
︙ | ︙ | |||
18048 18049 18050 18051 18052 18053 18054 | 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*/, \ | < | | | | | | < | < < < < < < < < < | | | 17637 17638 17639 17640 17641 17642 17643 17644 17645 17646 17647 17648 17649 17650 17651 17652 17653 17654 17655 17656 17657 17658 17659 17660 17661 17662 17663 17664 17665 17666 17667 17668 17669 17670 17671 17672 17673 17674 17675 17676 17677 17678 17679 17680 17681 17682 17683 17684 17685 17686 17687 17688 17689 17690 17691 17692 17693 17694 17695 17696 17697 17698 17699 17700 17701 17702 17703 17704 17705 17706 17707 17708 17709 17710 17711 17712 17713 17714 17715 17716 17717 17718 17719 17720 17721 17722 17723 17724 17725 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 | 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*/, \ fsl_acache_empty_m/*arty*/, \ fsl_id_bag_empty_m/*mfSeen*/, \ fsl_id_bag_empty_m/*leafCheck*/, \ fsl_id_bag_empty_m/*toVerify*/, \ 0/*mtimeManifest*/, \ NULL/*projectCode*/, \ fsl_fstat_empty_m/*fstat*/, \ fsl_mcache_empty_m/*mcache*/, \ {/*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. */ FSL_EXPORT 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. */ FSL_EXPORT bool fsl_acache_expire_oldest(fsl_acache * 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 returned 0. Returns 0 on success, FSL_RC_OOM on allocation error. Has undefined behaviour if !c, rid is not semantically valid, !pBlob. An empty blob is normally semantically illegal but is not strictly illegal for this cache's purposes. */ FSL_EXPORT int fsl_acache_insert(fsl_acache * c, fsl_id_t rid, fsl_buffer *pBlob); /** @internal Frees all memory held by c, and clears out c's state, but does not free c. Results are undefined if !c. */ FSL_EXPORT void fsl_acache_clear(fsl_acache * c); /** @internal Checks f->cache.arty to see if rid is available in the repository opened by f. Returns 0 if the content for the given rid is available in the repo or the cache. Returns FSL_RC_NOT_FOUND if it is not in the repo nor the cache. Returns some other non-0 code for "real 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. */ FSL_EXPORT int fsl_acache_check_available(fsl_cx * f, fsl_id_t rid); /** @internal This is THE ONLY routine which adds content to the blob table. This writes the given buffer content into the repository database's blob tabe. It Returns the record ID via outRid (if it |
︙ | ︙ | |||
18216 18217 18218 18219 18220 18221 18222 | to name - this function is a massive beast. Potential TODO: we don't really need the uncompSize param - we can deduce it, if needed, based on pBlob's content. We cannot, however, know the UUID of the decompressed content unless the client passes it in to us. | | | | | | | | | | | | | | | | 17794 17795 17796 17797 17798 17799 17800 17801 17802 17803 17804 17805 17806 17807 17808 17809 17810 17811 17812 17813 17814 17815 17816 17817 17818 17819 17820 17821 17822 17823 17824 17825 17826 17827 17828 17829 17830 17831 17832 17833 17834 17835 17836 17837 17838 17839 17840 17841 17842 17843 17844 17845 17846 17847 | to name - this function is a massive beast. Potential TODO: we don't really need the uncompSize param - we can deduce it, if needed, based on pBlob's content. We cannot, however, know the UUID of the decompressed content unless the client passes it in to us. @see fsl_content_put() */ FSL_EXPORT int fsl_content_put_ex( fsl_cx * const f, fsl_buffer const * pBlob, fsl_uuid_cstr zUuid, fsl_id_t srcId, fsl_size_t uncompSize, bool isPrivate, fsl_id_t * outRid); /** @internal Equivalent to fsl_content_put_ex(f,pBlob,NULL,0,0,0,newRid). This must only be used for saving raw (non-delta) content. @see fsl_content_put_ex() */ FSL_EXPORT int fsl_content_put( fsl_cx * const f, fsl_buffer const * pBlob, fsl_id_t * newRid); /** @internal If the given blob ID refers to deltified repo content, this routine undeltifies it and replaces its content with its expanded form. Returns 0 on success, FSL_RC_MISUSE if !f, FSL_RC_NOT_A_REPO if f has no opened repository, FSL_RC_RANGE if rid is not positive, and any number of other potential errors during the db and content operations. This function treats already unexpanded content as success. @see fsl_content_deltify() */ FSL_EXPORT int fsl_content_undeltify(fsl_cx * const f, fsl_id_t rid); /** @internal The converse of fsl_content_undeltify(), this replaces the storage of the given blob record so that it is a delta of srcid. If rid is already a delta from some other place then no conversion occurs and this is a no-op unless force is true. If rid's contents are not available because the the rid is a phantom or depends to one, no delta is generated and 0 is |
︙ | ︙ | |||
18285 18286 18287 18288 18289 18290 18291 | unspecified number" of bytes (currently 50), or if the resulting delta does not achieve a compression of at least 25%, the rid is left untouched. Returns 0 if a delta is successfully made or none needs to be made, non-0 on error. | | | | | | | | | | | 17863 17864 17865 17866 17867 17868 17869 17870 17871 17872 17873 17874 17875 17876 17877 17878 17879 17880 17881 17882 17883 17884 17885 17886 17887 17888 17889 17890 17891 17892 17893 17894 17895 17896 17897 17898 17899 17900 17901 17902 17903 17904 17905 17906 17907 17908 17909 17910 17911 17912 17913 17914 17915 17916 17917 17918 17919 17920 17921 17922 17923 17924 17925 17926 17927 17928 | unspecified number" of bytes (currently 50), or if the resulting delta does not achieve a compression of at least 25%, the rid is left untouched. Returns 0 if a delta is successfully made or none needs to be made, non-0 on error. @see fsl_content_undeltify() */ FSL_EXPORT int fsl_content_deltify(fsl_cx * f, fsl_id_t rid, fsl_id_t srcid, bool force); /** @internal Creates a new phantom blob with the given UUID and return its artifact ID via *newId. Returns 0 on success, FSL_RC_MISUSE if !f or !uuid, FSL_RC_RANGE if fsl_is_uuid(uuid) returns false, FSL_RC_NOT_A_REPO if f has no repository opened, FSL_RC_ACCESS if the given uuid has been shunned, and about 20 other potential error codes from the underlying db calls. If isPrivate is true _or_ f has been flagged as being in "private mode" then the new content is flagged as private. newId may be NULL, but if it is then the caller will have to find the record id himself by using the UUID (see fsl_uuid_to_rid()). */ FSL_EXPORT int fsl_content_new( fsl_cx * f, fsl_uuid_cstr uuid, bool isPrivate, fsl_id_t * newId ); /** @internal Check to see if checkin "rid" is a leaf and either add it to the LEAF table if it is, or remove it if it is not. Returns 0 on success, FSL_RC_MISUSE if !f or f has no repo db opened, FSL_RC_RANGE if pid is <=0. Other errors (e.g. FSL_RC_DB) may indicate that db is not a repo. On error db's error state may be updated. */ FSL_EXPORT int fsl_repo_leaf_check(fsl_cx * f, fsl_id_t pid); /** @internal Schedules a leaf check for "rid" and its parents. Returns 0 on success. */ FSL_EXPORT int fsl_repo_leaf_eventually_check( fsl_cx * f, fsl_id_t rid); /** @internal Perform all pending leaf checks. Returns 0 on success or if it has nothing to do. */ FSL_EXPORT int fsl_repo_leaf_do_pending_checks(fsl_cx *f); /** @internal Inserts a tag into f's repo db. It does not create the related control artifact - use fsl_tag_add_artifact() for that. rid is the artifact to which the tag is being applied. srcId is the artifact that contains the tag. It is often, but not always, the same as rid. This is often the RID of the manifest containing tags added as part of the commit, in which case rid==srcId. A Control Artifact which tags a different |
︙ | ︙ | |||
18359 18360 18361 18362 18363 18364 18365 | If a more recent (compared to mtime) entry already exists for this tag/rid combination then its tag.tagid is returned via *outRid (if outRid is not NULL) and no new entry is created. Returns 0 on success, and has a huge number of potential error codes. */ | | | | | | | | | | | 17937 17938 17939 17940 17941 17942 17943 17944 17945 17946 17947 17948 17949 17950 17951 17952 17953 17954 17955 17956 17957 17958 17959 17960 17961 17962 17963 17964 | If a more recent (compared to mtime) entry already exists for this tag/rid combination then its tag.tagid is returned via *outRid (if outRid is not NULL) and no new entry is created. Returns 0 on success, and has a huge number of potential error codes. */ FSL_EXPORT int fsl_tag_insert( fsl_cx * f, fsl_tagtype_e tagtype, char const * zTag, char const * zValue, fsl_id_t srcId, double mtime, fsl_id_t rid, fsl_id_t *outRid ); /** @internal Propagate all propagatable tags in artifact pid to the children of pid. Returns 0 on... non-error. Returns FSL_RC_RANGE if pid<=0. */ FSL_EXPORT int fsl_tag_propagate_all(fsl_cx * f, fsl_id_t pid); /** @internal Propagates a tag through the various internal pipelines. pid is the artifact id to whose children the tag should be propagated. |
︙ | ︙ | |||
18404 18405 18406 18407 18408 18409 18410 | mtime is the Julian timestamp for the tag. Must be a valid time (no defaults here). This function is unforgiving of invalid values/ranges, and may assert in debug mode if passed invalid ids (values<=0), a NULL f, or if f has no opened repo. */ | | | | | | | | > > > > > > > > > > > > > > > > > > | < < < | | | | | | | | | | | | | | | | | | | | | | | | | | 17982 17983 17984 17985 17986 17987 17988 17989 17990 17991 17992 17993 17994 17995 17996 17997 17998 17999 18000 18001 18002 18003 18004 18005 18006 18007 18008 18009 18010 18011 18012 18013 18014 18015 18016 18017 18018 18019 18020 18021 18022 18023 18024 18025 18026 18027 18028 18029 18030 18031 18032 18033 18034 18035 18036 18037 18038 18039 18040 18041 18042 18043 18044 18045 18046 18047 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 18174 18175 18176 18177 18178 18179 18180 18181 18182 18183 18184 18185 18186 18187 18188 18189 18190 18191 18192 18193 18194 18195 18196 18197 18198 18199 18200 18201 18202 18203 18204 18205 18206 18207 18208 18209 18210 18211 18212 18213 18214 18215 18216 18217 18218 18219 18220 18221 18222 18223 18224 18225 18226 18227 18228 18229 18230 18231 18232 18233 18234 18235 18236 18237 18238 18239 | mtime is the Julian timestamp for the tag. Must be a valid time (no defaults here). This function is unforgiving of invalid values/ranges, and may assert in debug mode if passed invalid ids (values<=0), a NULL f, or if f has no opened repo. */ FSL_EXPORT int fsl_tag_propagate(fsl_cx *f, fsl_tagtype_e tagType, fsl_id_t pid, fsl_id_t tagid, fsl_id_t origId, const char *zValue, double mtime ); /** @internal Remove the PGP signature from a raw artifact, if there is one. Expects *pz to point to *pn bytes of string memory which might or might not be prefixed by a PGP signature. If the string is enveloped in a signature, then upon returning *pz will point to the first byte after the end of the PGP header and *pn will contain the length of the content up to, but not including, the PGP footer. If *pz does not look like a PGP header then this is a no-op. Neither pointer may be NULL and *pz must point to *pn bytes of valid memory. If *pn is initially less than 59, this is a no-op. */ FSL_EXPORT void fsl_remove_pgp_signature(unsigned char const **pz, fsl_size_t *pn); /** @internal Clears the "seen" cache used by manifest parsing. Should be called by routines which initialize parsing, but not until their work has finished all parsing (so that recursive parsing can use it). */ FSL_EXPORT void fsl_cx_clear_mf_seen(fsl_cx * f); /** @internal Generates an fsl_appendf()-formatted message to stderr and fatally aborts the application by calling exit(). This is only (ONLY!) intended for us as a placeholder for certain test cases and is neither thread-safe nor reantrant. fmt may be empty or NULL, in which case only the code and its fsl_rc_cstr() representation are output. This function does not return. */ FSL_EXPORT void fsl_fatal( int code, char const * fmt, ... ) #ifdef __GNUC__ __attribute__ ((noreturn)) #endif ; /** @internal Translate a normalized, repo-relative filename into a filename-id (fnid). Create a new fnid if none previously exists and createNew is true. On success returns 0 and sets *rv to the filename.fnid record value. If createNew is false and no match is found, 0 is returned but *rv will be set to 0. Returns non-0 on error. Results are undefined if any parameter is NULL. In debug builds, this function asserts that no pointer arguments are NULL and that f has an opened repository. */ FSL_EXPORT int fsl_repo_filename_fnid2( fsl_cx * f, char const * filename, fsl_id_t * rv, bool createNew ); /** @internal Clears and frees all (char*) members of db but leaves the rest intact. If alsoErrorState is true then the error state is also freed, else it is kept as well. */ FSL_EXPORT void fsl_db_clear_strings(fsl_db * const db, bool alsoErrorState ); /** @internal Returns 0 if db appears to have a current repository schema, 1 if it appears to have an out of date schema, and -1 if it appears to not be a repository. Results are undefined if db is NULL or not opened. */ FSL_EXPORT int fsl_db_repo_verify_schema(fsl_db * db); /** @internal Flags for APIs which add phantom blobs to the repository. The values in this enum derive from fossil(1) code and should not be changed without careful forethought and (afterwards) testing. A phantom blob is a blob about whose existence we know but for which we have no content. This normally happens during sync or rebuild operations, but can also happen when artifacts are stored directly as files in a repo (like this project's repository does, storing artifacts from *other* projects for testing purposes). */ enum fsl_phantom_e { /** Indicates to fsl_uuid_to_rid2() that no phantom artifact should be created. */ FSL_PHANTOM_NONE = 0, /** Indicates to fsl_uuid_to_rid2() that a public phantom artifact should be created if no artifact is found. */ FSL_PHANTOM_PUBLIC = 1, /** Indicates to fsl_uuid_to_rid2() that a private phantom artifact should be created if no artifact is found. */ FSL_PHANTOM_PRIVATE = 2 }; typedef enum fsl_phantom_e fsl_phantom_e; /** @internal Works like fsl_uuid_to_rid(), with these differences: - uuid is required to be a complete UUID, not a prefix. - If it finds no entry and the mode argument specifies so then it will add either a public or private phantom entry and return its new rid. If mode is FSL_PHANTOM_NONE then this this behaves just like fsl_uuid_to_rid(). Returns a positive value on success, 0 if it finds no entry and mode==FSL_PHANTOM_NONE, and a negative value on error (e.g. if fsl_is_uuid(uuid) returns false). Errors which happen after argument validation will "most likely" update f's error state with details. */ FSL_EXPORT fsl_id_t fsl_uuid_to_rid2( fsl_cx * f, fsl_uuid_cstr uuid, fsl_phantom_e mode ); /** @internal Schedules the given rid to be verified at the next commit. This is used by routines which add artifact records to the blob table. The only error case, assuming the arguments are valid, is an allocation error while appending rid to the internal to-verify queue. @see fsl_repo_verify_at_commit() @see fsl_repo_verify_cancel() */ FSL_EXPORT int fsl_repo_verify_before_commit( fsl_cx * f, fsl_id_t rid ); /** @internal Clears f's verify-at-commit list of RIDs. @see fsl_repo_verify_at_commit() @see fsl_repo_verify_before_commit() */ FSL_EXPORT void fsl_repo_verify_cancel( fsl_cx * f ); /** @internal Processes all pending verify-at-commit entries and clears the to-verify list. Returns 0 on success. On error f's error state will likely be updated. ONLY call this from fsl_db_transaction_end() or its delegate (if refactored). Verification calls fsl_content_get() to "unpack" content added in the current transaction. If fetching the content (which applies any deltas it may need to) fails or a checksum does not match then this routine fails and returns non-0. On error f's error state will be updated. @see fsl_repo_verify_cancel() @see fsl_repo_verify_before_commit() */ FSL_EXPORT int fsl_repo_verify_at_commit( fsl_cx * f ); /** @internal Removes all entries from the repo's blob table which are listed in the shun table. Returns 0 on success. This operation is wrapped in a transaction. Delta contant which depend on to-be-shunned content are replaced with their undeltad forms. Returns 0 on success. */ FSL_EXPORT int fsl_repo_shun_artifacts(fsl_cx * f); /** @internal. Return a pointer to a string that contains the RHS of an SQL IN operator which will select config.name values that are part of the configuration that matches iMatch (a bitmask of fsl_configset_e values). Ownership of the returned string is passed to the caller, who must eventually pass it to fsl_free(). Returns NULL on allocation error. Reminder to self: this is part of the infrastructure for copying config state from an existing repo when creating new repo. */ FSL_EXPORT char *fsl_config_inop_rhs(int iMask); /** @internal Return a pointer to a string that contains the RHS of an IN operator that will select config.name values that are in the list of control settings. Ownership of the returned string is passed to the caller, who must eventually pass it to fsl_free(). Returns NULL on allocation error. Reminder to self: this is part of the infrastructure for copying config state from an existing repo when creating new repo. */ FSL_EXPORT char *fsl_db_setting_inop_rhs(); /** @internal Creates the ticket and ticketchng tables in f's repository db, DROPPING them if they already exist. The schema comes from fsl_schema_ticket(). TODO? Add a flag specifying whether to drop or keep existing copies. Returns 0 on success. */ FSL_EXPORT int fsl_cx_ticket_create_table(fsl_cx * const f); /** @internal Frees all J-card entries in the given list. li is assumed to be empty or contain (fsl_card_J*) instances. If alsoListMem is true then any memory owned by li is also freed. li itself is not freed. Results are undefined if li is NULL. */ FSL_EXPORT void fsl_card_J_list_free( fsl_list * li, bool alsoListMem ); /** @internal Values for fsl_card_J::flags. */ enum fsl_card_J_flags { /** |
︙ | ︙ | |||
18668 18669 18670 18671 18672 18673 18674 | ticket table info f. If f has already loaded the list and forceReload is false, this is a no-op. Returns 0 on success. @see fsl_cx::ticket::customFields */ | | | | | | | | | | 18261 18262 18263 18264 18265 18266 18267 18268 18269 18270 18271 18272 18273 18274 18275 18276 18277 18278 18279 18280 18281 18282 18283 18284 18285 18286 18287 18288 18289 18290 18291 18292 18293 18294 18295 18296 18297 18298 18299 18300 18301 18302 18303 18304 18305 18306 18307 18308 18309 18310 18311 18312 18313 18314 18315 18316 18317 18318 18319 18320 18321 18322 18323 18324 18325 18326 18327 18328 18329 18330 18331 18332 18333 18334 18335 18336 18337 18338 18339 18340 18341 18342 18343 18344 | ticket table info f. If f has already loaded the list and forceReload is false, this is a no-op. Returns 0 on success. @see fsl_cx::ticket::customFields */ FSL_EXPORT int fsl_cx_ticket_load_fields(fsl_cx * f, bool forceReload); /** @internal A comparison routine for qsort(3) which compares fsl_card_J instances in a lexical manner based on their names. The order is important for card ordering in generated manifests. This routine expects to get passed (fsl_card_J**) (namely from fsl_list entries), and will not work on an array of J-cards. */ FSL_EXPORT int fsl_qsort_cmp_J_cards( void const * lhs, void const * rhs ); /** @internal This function updates the repo and/or global config databases with links between the dbs intended for various fossil-level bookkeeping and housecleaning. These links are not essential to fossil's functionality but assist in certain "global" operations. If no checkout is opened but a repo is, the global config (if opened) is updated to know about the opened repo db. If a checkout is opened, global config (if opened) and the repo are updated to point to the checked-out db. */ FSL_EXPORT int fsl_repo_record_filename(fsl_cx * f); /** @internal Updates f->ckout.uuid and f->ckout.rid to reflect the current checkout state. If no checkout is opened, the uuid is freed/NULLed and the rid is set to 0. Returns 0 on success. If it returns an error (OOM or db-related), the f->ckout state is left in a potentially inconsistent state, and it should not be relied upon until/unless the error is resolved. This is done when a checkout db is opened, when performing a checkin, and otherwise as needed, and so calling it from other code is normally not necessary. @see fsl_ckout_version_write() */ FSL_EXPORT int fsl_ckout_version_fetch( fsl_cx *f ); /** @internal Updates f->ckout's state to reflect the given version info and writes the 'checkout' and 'checkout-hash' properties to the currently-opened checkout db. Returns 0 on success, FSL_RC_NOT_A_CKOUT if no checkout is opened (may assert() in that case!), or some other code if writing to the db fails. If vid is 0 then the version info is null'd out. Else if uuid is NULL then fsl_rid_to_uuid() is used to fetch the UUID for vid. If the RID differs from f->ckout.rid then f->ckout's version state is updated to the new values. This routine also updates or removes the checkout's manifest files, as per fsl_ckout_manifest_write(). If vid is 0 then it removes any such files which themselves are not part of the current checkout. @see fsl_ckout_version_fetch() @see fsl_cx_ckout_version_set() */ FSL_EXPORT int fsl_ckout_version_write( fsl_cx *f, fsl_id_t vid, fsl_uuid_cstr uuid ); /** @internal Exports the file with the given [vfile].[id] to the checkout, overwriting (if possible) anything which gets in its way. If the file is determined to have not been modified, it is |
︙ | ︙ | |||
18778 18779 18780 18781 18782 18783 18784 | Maintenance reminders: internally this code supports handling multiple files at once, but (A) that's not part of the interface and may change and (B) the 3rd parameter makes little sense in that case unless maybe we change it to a callback, which seems like overkill for our use cases. */ | | | | | 18371 18372 18373 18374 18375 18376 18377 18378 18379 18380 18381 18382 18383 18384 18385 18386 18387 18388 18389 18390 18391 18392 18393 18394 | Maintenance reminders: internally this code supports handling multiple files at once, but (A) that's not part of the interface and may change and (B) the 3rd parameter makes little sense in that case unless maybe we change it to a callback, which seems like overkill for our use cases. */ FSL_EXPORT int fsl_vfile_to_ckout(fsl_cx * f, fsl_id_t vfileId, int * wasWritten); /** @internal On Windows platforms (only), if fsl_isalpha(*zFile) and ':' == zFile[1] then this returns zFile+2, otherwise it returns zFile. */ FSL_EXPORT char * fsl_file_without_drive_letter(char * zFile); /** @internal This is identical to the public-API member fsl_deck_F_search(), except that it returns a non-const F-card. Locate a file named zName in d->F.list. Return a pointer to the |
︙ | ︙ | |||
18816 18817 18818 18819 18820 18821 18822 | search. As an optimization, to support the most common use case, searches through a deck update d->F.cursor to the last position a search was found. Because searches are normally done in lexical order (because of architectural reasons), this is normally an O(1) operation. It degrades to O(N) if out-of-lexical-order searches are performed. */ | | | | | | | | | | | 18409 18410 18411 18412 18413 18414 18415 18416 18417 18418 18419 18420 18421 18422 18423 18424 18425 18426 18427 18428 18429 18430 18431 18432 18433 18434 18435 18436 18437 18438 18439 18440 18441 18442 18443 18444 18445 18446 18447 18448 18449 18450 18451 18452 18453 18454 18455 18456 18457 18458 18459 18460 18461 18462 18463 18464 18465 18466 18467 18468 18469 18470 18471 18472 18473 18474 18475 18476 18477 18478 18479 18480 18481 18482 18483 18484 18485 18486 18487 18488 18489 18490 18491 18492 18493 18494 18495 18496 18497 18498 18499 18500 18501 18502 18503 | search. As an optimization, to support the most common use case, searches through a deck update d->F.cursor to the last position a search was found. Because searches are normally done in lexical order (because of architectural reasons), this is normally an O(1) operation. It degrades to O(N) if out-of-lexical-order searches are performed. */ FSL_EXPORT fsl_card_F * fsl_deck_F_seek(fsl_deck * const d, const char *zName); /** @internal Part of the fsl_cx::fileContent optimization. This sets f->fileContent.used to 0 and if its capacity is over a certain (unspecified, unconfigurable) size then it is trimmed to that size. */ FSL_EXPORT void fsl_cx_content_buffer_yield(fsl_cx * const f); /** @internal Currently disabled (always returns 0) pending resolution of a "wha???" result from one of the underlying queries. Queues up the given artifact for a search index update. This is only intended to be called from crosslinking steps and similar content updates. Returns 0 on success. The final argument is intended only for wiki titles (the L-card of a wiki post). If the repository database has no search index or the given content is marked as private, this function returns 0 and makes no changes to the db. */ FSL_EXPORT int fsl_search_doc_touch(fsl_cx * const f, fsl_satype_e saType, fsl_id_t rid, const char * docName); /** @internal Performs the same job as fsl_diff_text() but produces the results in the low-level form of an array of "copy/delete/insert triples." This is primarily intended for internal use in other library-internal algorithms, not for client code. Note all FSL_DIFF_xxx flags apply to this form. Returns 0 on success, any number of non-0 codes on error. On success *outRaw will contain the resulting array, which must eventually be fsl_free()'d by the caller. On error *outRaw is not modified. @deprecated Use fsl_diff_v2_raw() instead. */ FSL_EXPORT int fsl_diff_text_raw(fsl_buffer const *p1, fsl_buffer const *p2, int diffFlags, int ** outRaw); /** @internal If the given file name is a reserved filename (case-insensitive) on Windows platforms, a pointer to the reserved part of the name, else NULL is returned. zPath must be a canonical path with forward-slash directory separators. nameLen is the length of zPath. If negative, fsl_strlen() is used to determine its length. */ FSL_EXPORT bool fsl_is_reserved_fn_windows(const char *zPath, fsl_int_t nameLen); /** @internal Clears any pending merge state from the checkout db's vmerge table. Returns 0 on success. */ FSL_EXPORT int fsl_ckout_clear_merge_state( fsl_cx *f ); /** @internal Installs or reinstalls the checkout database schema into f's open checkout db. Returns 0 on success, FSL_RC_NOT_A_CKOUT if f has no opened checkout, or an code if a lower-level operation fails. If dropIfExists is true then all affected tables are dropped beforehand if they exist. "It's the only way to be sure." If dropIfExists is false and the schema appears to already exists (without actually validating its validity), 0 is returned. */ FSL_EXPORT int fsl_ckout_install_schema(fsl_cx *f, bool dropIfExists); /** @internal Attempts to remove empty directories from under a checkout, starting with tgtDir and working upwards until it either cannot remove one or it reaches the top of the checkout dir. |
︙ | ︙ | |||
18918 18919 18920 18921 18922 18923 18924 | Results are undefined if tgtDir is not an absolute path rooted in f's current checkout. There are any number of valid reasons removal of a directory might fail, and this routine stops at the first one which does. */ | | | | | | | | | > | | | | | | | 18511 18512 18513 18514 18515 18516 18517 18518 18519 18520 18521 18522 18523 18524 18525 18526 18527 18528 18529 18530 18531 18532 18533 18534 18535 18536 18537 18538 18539 18540 18541 18542 18543 18544 18545 18546 18547 18548 18549 18550 18551 18552 18553 18554 18555 18556 18557 18558 18559 18560 18561 18562 18563 18564 18565 18566 18567 18568 18569 18570 18571 18572 18573 18574 18575 18576 18577 18578 18579 18580 18581 18582 18583 18584 18585 18586 18587 18588 18589 18590 18591 18592 18593 18594 18595 18596 18597 18598 18599 18600 18601 18602 18603 18604 18605 18606 18607 18608 18609 18610 18611 18612 18613 18614 18615 18616 18617 18618 18619 18620 18621 18622 18623 18624 18625 18626 18627 | Results are undefined if tgtDir is not an absolute path rooted in f's current checkout. There are any number of valid reasons removal of a directory might fail, and this routine stops at the first one which does. */ FSL_EXPORT unsigned int fsl_ckout_rm_empty_dirs(fsl_cx * f, fsl_buffer * tgtDir); /** @internal This is intended to be passed the name of a file which was just deleted and "might" have left behind an empty directory. The name _must_ an absolute path based in f's current checkout. This routine uses fsl_file_dirpart() to strip path components from the string and remove directories until either removing one fails or the top of the checkout is reach. Since removal of a directory can fail for any given reason, this routine ignores such errors. It returns 0 on success, FSL_RC_OOM if allocation of the working buffer for the filename hackery fails, and FSL_RC_MISUSE if zFilename is not rooted in the checkout (in which case it may assert(), so don't do that). @see fsl_is_rooted_in_ckout() @see fsl_rm_empty_dirs() */ FSL_EXPORT int fsl_ckout_rm_empty_dirs_for_file(fsl_cx * f, char const *zAbsPath); /** @internal If f->cache.seenDeltaManifest<=0 then this routine sets it to 1 and sets the 'seen-delta-manifest' repository config setting to 1, else this has no side effects. Returns 0 on success, non-0 if there is an error while writing to the repository config. */ FSL_EXPORT int fsl_cx_update_seen_delta_mf(fsl_cx *f); /** @internal Very, VERY internal. Returns the next available buffer from f->scratchpads. Fatally aborts if there are no free buffers because "that should not happen." Calling this obligates the caller to eventually pass its result to fsl_cx_scratchpad_yield(). This function guarantees the returned buffer's 'used' member will be set to 0. Maintenance note: the number of buffers is hard-coded in the fsl_cx::scratchpads anonymous struct. */ FSL_EXPORT fsl_buffer * fsl_cx_scratchpad(fsl_cx *f); /** @internal Very, VERY internal. "Yields" a buffer which was returned from fsl_cx_scratchpad(), making it available for re-use. The caller must treat the buffer as if this routine frees it: using the buffer after having passed it to this function will internally be flagged as explicit misuse and will lead to a fatal crash the next time that buffer is fetched via fsl_cx_scratchpad(). So don't do that. */ FSL_EXPORT void fsl_cx_scratchpad_yield(fsl_cx *f, fsl_buffer * b); /** @internal Run automatically by fsl_deck_save(), so it needn't normally be run aside from that, at least not from average client code. Runs postprocessing on the Structural Artifact represented by d. d->f must be set, d->rid must be set and valid and d's contents must accurately represent the stored manifest for the given rid. This is normally run just after the insertion of a new manifest, but is sometimes also run after reading a deck from the database (in order to rebuild all db relations and add/update the timeline entry). Returns 0 on succes, FSL_RC_MISUSE !d->f, FSL_RC_RANGE if d->rid<=0, FSL_RC_MISUSE (with more error info in f) if d does not contain all required cards for its d->type value. It may return various other codes from the many routines it delegates work to. Crosslinking of ticket artifacts is currently (2021-03) missing. Design note: d "really should" be const here but some internals (d->F.cursor and delayed baseline loading) prohibit it. @see fsl_deck_crosslink_one() */ FSL_EXPORT int fsl_deck_crosslink( fsl_deck /* const */ * const d ); /** @internal Run automatically by fsl_deck_save(), so it needn't normally be run aside from that, at least not from average client code. This is a convience form of crosslinking which must only be used when a single deck (and only a single deck) is to be crosslinked. This function wraps the crosslinking in fsl_crosslink_begin() and fsl_crosslink_end(), but otherwise behaves the same as fsl_deck_crosslink(). If crosslinking fails, any in-progress transaction will be flagged as failed. Returns 0 on success. */ FSL_EXPORT int fsl_deck_crosslink_one( fsl_deck * const d ); /** @internal Checks whether the given filename is "safe" for writing to within f's current checkout. zFilename must be in canonical form: only '/' directory separators. |
︙ | ︙ | |||
19045 19046 19047 19048 19049 19050 19051 | If the name refers to something not (yet) in the filesystem, that is not considered an error. Returns 0 on success. On error f's error state is updated with information about the problem. */ | | | 18639 18640 18641 18642 18643 18644 18645 18646 18647 18648 18649 18650 18651 18652 18653 | If the name refers to something not (yet) in the filesystem, that is not considered an error. Returns 0 on success. On error f's error state is updated with information about the problem. */ FSL_EXPORT int fsl_ckout_safe_file_check(fsl_cx *f, char const * zFilename); /** @internal UNTESTED! Creates a file named zLinkFile and populates its contents with a single line: zTgtFile. This behaviour corresponds to how fossil manages SCM'd symlink entries on Windows and on other platforms |
︙ | ︙ | |||
19075 19076 19077 19078 19079 19080 19081 | "properly" on systems which natively support them iff f's 'allow-symlinks' repo-level config setting is true. That said: the addition of symlinks support into fossil was, IMHO, a poor decision for $REASONS. That might (might) be reflected long-term in this API by only supporting them in the way fossil does for platforms which do not support symlinks. */ | | | | 18669 18670 18671 18672 18673 18674 18675 18676 18677 18678 18679 18680 18681 18682 18683 18684 | "properly" on systems which natively support them iff f's 'allow-symlinks' repo-level config setting is true. That said: the addition of symlinks support into fossil was, IMHO, a poor decision for $REASONS. That might (might) be reflected long-term in this API by only supporting them in the way fossil does for platforms which do not support symlinks. */ FSL_EXPORT int fsl_ckout_symlink_create(fsl_cx * f, char const *zTgtFile, char const * zLinkFile); /** Compute all file name changes that occur going from check-in iFrom to check-in iTo. Requires an opened repository. If revOK is true, the algorithm is free to move backwards in the |
︙ | ︙ | |||
19100 19101 19102 19103 19104 19105 19106 | On error returns non-0, pnChng and aiChng are not modified, and f's error state might (depending on the error) contain a description of the problem. Space to hold *aiChng is obtained from fsl_malloc() and must be released by the caller. */ | | | | | | | | | | | | | | | | 18694 18695 18696 18697 18698 18699 18700 18701 18702 18703 18704 18705 18706 18707 18708 18709 18710 18711 18712 18713 18714 18715 18716 18717 18718 18719 18720 18721 18722 18723 18724 18725 18726 18727 18728 18729 18730 18731 18732 18733 18734 18735 18736 18737 18738 18739 18740 18741 | On error returns non-0, pnChng and aiChng are not modified, and f's error state might (depending on the error) contain a description of the problem. Space to hold *aiChng is obtained from fsl_malloc() and must be released by the caller. */ FSL_EXPORT int fsl__find_filename_changes(fsl_cx * const f, fsl_id_t iFrom, fsl_id_t iTo, bool revOK, uint32_t *pnChng, fsl_id_t **aiChng); /** Bitmask of file change types for use with fsl_is_locally_modified(). */ enum fsl_localmod_e { /** Sentinel value. */ FSL_LOCALMOD_NONE = 0, /** Permissions changed. */ FSL_LOCALMOD_PERM = 0x01, /** File size or hash (i.e. content) differ. */ FSL_LOCALMOD_CONTENT = 0x02, /** The file type was switched between symlink and normal file. In this case, no check for content change, beyond the file size change, is performed. */ FSL_LOCALMOD_LINK = 0x04, /** File was not found in the local checkout. */ FSL_LOCALMOD_NOTFOUND = 0x10 }; typedef enum fsl_localmod_e fsl_localmod_e; /** @internal Checks whether the given file has been locally modified compared to a known size, hash value, and permissions. Requires that f has an opened checkout. If zFilename is not an absolute path, it is assumed to be relative |
︙ | ︙ | |||
19162 19163 19164 19165 19166 19167 19168 | Because this is used for comparing local files to their state from the fossil database, where files have no timestamps, the local file's timestamp is never considered for purposes of modification checking. If isModified is not NULL then on success it is set to a bitmask of | | | | | | | | | 18756 18757 18758 18759 18760 18761 18762 18763 18764 18765 18766 18767 18768 18769 18770 18771 18772 18773 18774 18775 18776 18777 18778 18779 18780 18781 18782 18783 18784 18785 18786 | Because this is used for comparing local files to their state from the fossil database, where files have no timestamps, the local file's timestamp is never considered for purposes of modification checking. If isModified is not NULL then on success it is set to a bitmask of values from the fsl_localmod_e enum specifying the type(s) of change(s) detected: - FSL_LOCALMOD_PERM = permissions changed. - FSL_LOCALMOD_CONTENT = file size or hash (i.e. content) differ. - FSL_LOCALMOD_LINK = the file type was switched between symlink and normal file. In this case, no check for content change, beyond the file size change, is performed. - FSL_LOCALMOD_NOFOUND = file was not found in the local checkout. Noting that: - Combined values of (FSL_LOCALMOD_PERM | FSL_LOCALMOD_CONTENT) are possible, but FSL_LOCALMOD_NOFOUND will never be combined with one of the other values. If stat() fails for any reason other than file-not-found (e.g. permissions), an error is triggered. Returns 0 on success. On error, returns non-0 and f's error state will be updated and isModified... isNotModified. Errors include, |
︙ | ︙ | |||
19208 19209 19210 19211 19212 19213 19214 | This function currently does NOT follow symlinks for purposes of resolving zFilename, but that behavior may change in the future or may become dependent on the repository's 'allow-symlinks' setting. Internal detail, not relevant for clients: this updates f's cache stat entry. */ | | | | | | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 18802 18803 18804 18805 18806 18807 18808 18809 18810 18811 18812 18813 18814 18815 18816 18817 18818 18819 18820 18821 18822 18823 18824 18825 18826 18827 18828 18829 18830 18831 18832 18833 | This function currently does NOT follow symlinks for purposes of resolving zFilename, but that behavior may change in the future or may become dependent on the repository's 'allow-symlinks' setting. Internal detail, not relevant for clients: this updates f's cache stat entry. */ FSL_EXPORT int fsl_is_locally_modified(fsl_cx * f, const char * zFilename, fsl_size_t fileSize, const char * zOrigHash, fsl_int_t zOrigHashLen, fsl_fileperm_e origPerm, int * isModified); /** @internal This routine cleans up the state of selected cards in the given deck. The 2nd argument is an list of upper-case letters representing the cards which should be cleaned up, e.g. "ADG". If it is NULL, all cards are cleaned up but d has non-card state which is not cleaned up by this routine. Unknown letters are simply ignored. */ FSL_EXPORT void fsl_deck_clean_cards(fsl_deck * d, char const * letters); /** @internal Searches the current repository database for a fingerprint and returns it as a string in *zOut. If rcvid<=0 then the fingerprint matches the last entry in the |
︙ | ︙ | |||
19309 19310 19311 19312 19313 19314 19315 | fingerprints and falls back to "version 0" fingerprint if a v1 fingerprint is not found. Version 0 was very short-lived and is not expected to be in many repositories which are accessed via this library. Practice has, however, revealed some. @see fsl_ckout_fingerprint_check() */ | | < | | | | | | | | | | | < < < < < < < < < < < < < < < < < < < < < | | | 18849 18850 18851 18852 18853 18854 18855 18856 18857 18858 18859 18860 18861 18862 18863 18864 18865 18866 18867 18868 18869 18870 18871 18872 18873 18874 18875 18876 18877 18878 18879 18880 18881 18882 18883 18884 18885 18886 18887 18888 18889 18890 18891 18892 18893 18894 18895 18896 18897 18898 18899 18900 18901 18902 18903 18904 18905 18906 18907 18908 18909 18910 18911 18912 18913 18914 18915 18916 18917 18918 18919 18920 18921 18922 18923 18924 18925 18926 18927 18928 18929 18930 18931 18932 18933 18934 18935 18936 18937 18938 18939 18940 18941 18942 18943 18944 18945 18946 18947 18948 18949 18950 18951 18952 18953 18954 18955 | fingerprints and falls back to "version 0" fingerprint if a v1 fingerprint is not found. Version 0 was very short-lived and is not expected to be in many repositories which are accessed via this library. Practice has, however, revealed some. @see fsl_ckout_fingerprint_check() */ FSL_EXPORT int fsl_repo_fingerprint_search(fsl_cx *f, fsl_id_t rcvid, char ** zOut); /** A context for running a raw diff. The aEdit[] array describes the raw diff. Each triple of integers in aEdit[] means: (1) COPY: Number of lines aFrom and aTo have in common (2) DELETE: Number of lines found only in aFrom (3) INSERT: Number of lines found only in aTo The triples repeat until all lines of both aFrom and aTo are accounted for. */ struct fsl_diff_cx { /*TODO unsigned*/ int *aEdit; /* Array of copy/delete/insert triples */ /*TODO unsigned*/ int nEdit; /* Number of integers (3x num of triples) in aEdit[] */ /*TODO unsigned*/ int nEditAlloc; /* Space allocated for aEdit[] */ fsl_dline *aFrom; /* File on left side of the diff */ /*TODO unsigned*/ int nFrom; /* Number of lines in aFrom[] */ fsl_dline *aTo; /* File on right side of the diff */ /*TODO unsigned*/ int nTo; /* Number of lines in aTo[] */ int (*cmpLine)(const fsl_dline * const, const fsl_dline *const); /* Function to be used for comparing */ }; /** Convenience typeef. */ typedef struct fsl_diff_cx fsl_diff_cx; /** Initialized-with-defaults fsl_diff_cx structure, intended for const-copy initialization. */ #define fsl_diff_cx_empty_m {\ NULL,0,0,NULL,0,NULL,0,fsl_dline_cmp \ } /** Initialized-with-defaults fsl_diff_cx structure, intended for non-const copy initialization. */ extern const fsl_diff_cx fsl_diff_cx_empty; /** @internal Compute the differences between two files already loaded into the fsl_diff_cx structure. A divide and conquer technique is used. We look for a large block of common text that is in the middle of both files. Then compute the difference on those parts of the file before and after the common block. This technique is fast, but it does not necessarily generate the minimum difference set. On the other hand, we do not need a minimum difference set, only one that makes sense to human readers, which this algorithm does. Any common text at the beginning and end of the two files is removed before starting the divide-and-conquer algorithm. Returns 0 on succes, FSL_RC_OOM on an allocation error. */ int fsl__diff_all(fsl_diff_cx * const p); /** @internal */ void fsl__diff_optimize(fsl_diff_cx * const p); /** @internal */ void fsl__diff_cx_clean(fsl_diff_cx * const cx); /** @internal Undocumented. For internal debugging only. */ void fsl__dump_triples(fsl_diff_cx const * const p, char const * zFile, int ln ); /** @internal Removes from the BLOB table all artifacts that are in the SHUN table. Returns 0 on success. Requires (asserts) that a repo is opened. */ int fsl__shunned_remove(fsl_cx * const f); /** @internal Maximum length of a line in a text file, in bytes. (2**15 = 32k) */ #define FSL_LINE_LENGTH_MASK_SZ 15 /** @internal */ #define FSL_LINE_LENGTH_MASK ((1<<FSL_LINE_LENGTH_MASK_SZ)-1) #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* ORG_FOSSIL_SCM_FSL_INTERNAL_H_INCLUDED */ /* end of file ../include/fossil-scm/fossil-internal.h */ |
︙ | ︙ | |||
21438 21439 21440 21441 21442 21443 21444 | /** @internal This function is intented for use in development of libfossil. It dumps the current state of cached SQL statements to fcli_printf(). Normally its output detail level is determined by | | | < < < < < < < < < < < | 20956 20957 20958 20959 20960 20961 20962 20963 20964 20965 20966 20967 20968 20969 20970 20971 20972 20973 20974 20975 | /** @internal This function is intented for use in development of libfossil. It dumps the current state of cached SQL statements to fcli_printf(). Normally its output detail level is determined by fcli_is_verbose(), but if forceVerbose is true then it cranks up the detail all the way. */ FSL_EXPORT void fcli_dump_stmt_cache(bool forceVerbose); #if defined(__cplusplus) } /*extern "C"*/ #endif #endif /* _ORG_FOSSIL_SCM_FCLI_H_INCLUDED_ */ /* end of file ../include/fossil-scm/fossil-cli.h */ |
Added signify/fnc-04-release.pub.
> > | 1 2 | untrusted comment: fnc 0.4 public key RWRL2v5dIJ1toZNK6Y5jlEkT5uGlolQ2pS8tf2EilbX5eB6DQRYjl6AS |
Deleted signify/fnc-06-release.pub.
|
| < < |
Changes to src/fnc.1.
︙ | ︙ | |||
25 26 27 28 29 30 31 32 33 | .Op Fl h | -help .Nm .Op Fl h | -help .Op Fl v | -version .Nm .Cm timeline .Op Fl Cz .Op Fl b Ar branch .Op Fl c Ar commit | > < < | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | .Op Fl h | -help .Nm .Op Fl h | -help .Op Fl v | -version .Nm .Cm timeline .Op Fl Cz .Op Fl T Ar tag .Op Fl b Ar branch .Op Fl c Ar commit .Op Fl n Ar number .Op Fl t Ar type .Op Fl u Ar user .Op Ar path .Nm .Cm diff .Op Fl Ciw .Op Fl x Ar number |
︙ | ︙ | |||
57 58 59 60 61 62 63 | .Nm .Cm branch .Op Fl Ccopr .Op Fl a Ar date | Fl b Ar date .Op Fl s Ar order .Op Ar glob .Nm | < < < < < | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | .Nm .Cm branch .Op Fl Ccopr .Op Fl a Ar date | Fl b Ar date .Op Fl s Ar order .Op Ar glob .Nm .Op Ar path .Sh DESCRIPTION .Nm is an interactive read-only browser for .Xr fossil 1 repositories, and supports multiple views to display repository data: |
︙ | ︙ | |||
113 114 115 116 117 118 119 | .Pp Note that any global options preceding a command name will be interpreted as the command-specific variant if such an option exists. .Pp Global key bindings are as follows: .Bl -tag -width Ds .It Cm H, ?, F1 | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | < | > > > > > > > > > > > > < < < < < < < < < < < < < < < < < < < < < < < < < < < | < | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 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 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | .Pp Note that any global options preceding a command name will be interpreted as the command-specific variant if such an option exists. .Pp Global key bindings are as follows: .Bl -tag -width Ds .It Cm H, ?, F1 Open in-app help. .It Cm Tab Switch focus between open views. .It Cm f Toggle the active view between fullscreen and splitscreen mode. Note that .Nm will open nested views in splitscreen mode if the terminal window is equal to or greater than 110 columns wide. .It Cm Q Immediatey quit .Nm . .It Cm q Quit the active view. .El .Pp Commands available to .Nm are as follows: .Bl -tag -width 4v .Tg log .It Cm timeline Oo Fl C | -no-colour Oc Oo Fl T | -tag Ar tag Oc \ Oo Fl b | -branch Ar branch Oc Oo Fl c | -commit Ar commit Oc \ Oo Fl h | -help Oc Oo Fl n | -limit Ar n Oc Oo Fl t | -type Ar type Oc \ Oo Fl u | -username Ar user Oc Oo Fl z | -utc Oc Op Ar path .Dl (aliases: Cm log , Cm tl , Cm time , Cm ti ) Display commit history of a repository. If .Ar path is specified, only commits that modified the file(s) at this path will populate the timeline. The .Ar path may be absolute, relative to the current working directory, or relative to the repository root. This command must be executed from within or below the top level directory of the repository; that is, .Nm assumes a local checkout is open in or above the current working directory. .Pp If no command is explicitly specified, this command will be executed by default. .Pp Options for .Cm fnc timeline are as follows: .Bl -tag -width Ds .It Fl C , -no-colour Disable colourised timeline, which is enabled by default on supported terminals. If this option is not used, colour can be toggled with the .Sy c timeline view key binding as documented below. .It Fl T , -tag Ar tag Only display commits with T cards containing .Ar tag . By default, .Nm will indiscriminately display all commits irrespective of which T cards are attached to the commit manifest. .It Fl b , -branch Ar branch Display commits that are members of the specified .Ar branch . The expected argument is the symbolic name of a branch. By default, .Nm will display all commits irrespective of the branch on which they reside. .It Fl c , -commit Ar commit Open the timeline from the check-in identified by .Ar commit . The expected argument is either the name of a branch, which will resolve to the latest commit on the given branch, or (a unique abbreviated prefix of) a valid commit UUID SHA1 or SHA3 hash. When this option is not supplied, .Nm will open the timeline to the latest leaf on the repository tree. For a complete list of valid arguments this option accepts, see .Lk https://fossil-scm.org/home/doc/trunk/www/checkin_names.wiki \ "Fossil's Check-in Names". .It Fl h , -help Display timeline command help and usage information then exit. .It Fl n , -limit Ar n Limit timeline to the latest .Ar n commits. By default, .Nm will load the entire history of the repository's local checkout. Negative values are a no-op. .It Fl t , -type Ar type Only display .Ar type commits. Valid .Ar type values are as follows: .Bl -column YXZ description .Sy ci Ta check-in .Sy w Ta wiki .Sy t Ta ticket .Sy e Ta technote .Sy f Ta forum post .El .Pp By default, when this option is not supplied, .Nm will indiscriminately load all commits irrespective of .Ar type . Note that this is a repeatable flag (e.g., |
︙ | ︙ | |||
290 291 292 293 294 295 296 | .Cm fnc timeline are as follows: .Bl -tag -width Ds .It Cm Arrow-down, j, >, \&. Move selection cursor down the timeline. .It Cm Arrow-up, k, <, \&, Move selection cursor up the timeline. | | | < < < < < < | 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 | .Cm fnc timeline are as follows: .Bl -tag -width Ds .It Cm Arrow-down, j, >, \&. Move selection cursor down the timeline. .It Cm Arrow-up, k, <, \&, Move selection cursor up the timeline. .It Cm Ctrl+f, Page-down Move selection cursor one page down the timeline. .It Cm Ctrl+b, Page-up Move selection cursor one page up the timeline. .It Cm G, End Move selection cursor to the last commit on the timeline (i.e., oldest commit in the repository). .It Cm gg, Home Move selection cursor to the first commit on the timeline (i.e., newest commit in the repository). .It Cm Enter, Space Open a .Cm diff view displaying the changeset of the currently selected commit. .It Cm c Toggle colourised timeline. On supported terminals, .Nm will default to displaying the timeline in colour. .It Cm t Display the tree of the repository corresponding to the currently selected commit. .It Cm / Prompt to enter a search term to begin searching for commits matching the pattern provided. The search term is an extended regular expression, which is cross-referenced against a commit's comment, the username of |
︙ | ︙ | |||
337 338 339 340 341 342 343 | search will continue until either a match is found or the latest commit on the timeline is consumed. .El .Tg di .It Cm diff Oo Fl C | -no-colour Oc Oo Fl h | -help Oc Oo Fl i | -invert \ Oc Oo Fl w | -whitespace Oc Oo Fl x | -context Ar n Oc \ Oo Ar artifact1 Oo Ar artifact2 Oc Oc Op Ar path ... | | | 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 | search will continue until either a match is found or the latest commit on the timeline is consumed. .El .Tg di .It Cm diff Oo Fl C | -no-colour Oc Oo Fl h | -help Oc Oo Fl i | -invert \ Oc Oo Fl w | -whitespace Oc Oo Fl x | -context Ar n Oc \ Oo Ar artifact1 Oo Ar artifact2 Oc Oc Op Ar path ... .Dl (alias: Cm di ) Display the differences between two repository artifacts, or between the local changes on disk and a given commit. If neither .Ar artifact1 nor .Ar artifact2 are specified, .Nm |
︙ | ︙ | |||
377 378 379 380 381 382 383 | are as follows: .Bl -tag -width Ds .It Fl C , -no-colour Disable coloured diff output, which is enabled by default on supported terminals. If this option is not used, colour can be toggled with the .Sy c diff view key binding as documented below. | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > | | < < < | 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 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 | are as follows: .Bl -tag -width Ds .It Fl C , -no-colour Disable coloured diff output, which is enabled by default on supported terminals. If this option is not used, colour can be toggled with the .Sy c diff view key binding as documented below. .It Fl h , -help Display diff command help and usage information then exit. .It Fl i , -invert Invert the difference between artifacts when displaying the diff. .It Fl w , -whitespace Ignore whitespace-only changes when displaying the diff. .It Fl x , -context Ar n Set .Ar n context lines to be shown in the diff. By default, 5 context lines are shown. Negative values are a no-op. .El .Pp Key bindings for .Cm fnc diff are as follows: .Bl -tag -width Ds .It Cm c Toggle coloured diff output. On supported terminals, .Nm will default to displaying changes and diff metadata in colour. .It Cm i Toggle inversion of diff output. .It Cm v Toggle verbosity of diff output. By default, .Nm will display the entire content of newly added or deleted files. .It Cm w Toggle whether whitespace-only changes are ignored when comparing lines in the diff. .It Cm Arrow-down, j Scroll down one line of diff output. .It Cm Arrow-up, k Scroll up one line of diff output. .It Cm Ctrl+f, Page-down, Space Scroll down one page of diff output. .It Cm Ctrl+b, Page-up Scroll up one page of diff output. .It Cm G, End Scroll to the end of the view (i.e., last line of diff output). .It Cm gg, Home Scroll to the top of the view (i.e., first line of diff output). .It Cm \&-, \&_ Decrease the number of context lines shown in diff output. .It Cm \&=, \&+ Increase the number of context lines shown in diff output. .It Cm Ctrl+k, K, <, \&, Move up the .Cm timeline to the previous (i.e., more recent) commit and display its diff. .It Cm Ctrl+j, J, >, \&. Move down the .Cm timeline to the next (i.e., earlier) commit and display its diff. .It Cm / Prompt to enter a search term to begin searching the diff output for lines matching the pattern provided. The search term is an extended regular expression, which is documented in .Xr re_format 7 . .It Cm n Find the next line that matches the current search term. .It Cm N Find the previous line that matches the current search term. .El .Tg dir .It Cm tree Oo Fl C | -no-colour Oc Oo Fl c | -commit Ar commit Oc \ Oo Fl h | -help Oc Op Ar path .Dl (aliases: Cm dir , Cm tr ) Display navigable, hierarchical tree of a repository. If a .Ar path is specified, display tree nodes of this path. The .Ar path may be absolute, relative to the current working directory, or relative to the repository root. With no options passed, the tree will reflect the state of the latest commit on trunk. This command must be executed from within or below the top level directory of the repository; that is, .Nm assumes a local checkout is open in or above the current working directory. .Pp Tree nodes are lexicographically ordered and may be postfixed with an identifier corresponding to the mode of the file object on disk as returned by .Xr lstat 2 : .Bl -column YXZ description .It / Ta directory .It * Ta executable .It @ Ta symbolic link .El .Pp Nodes representing symbolic links are also annotated with the path of the source file. .Pp Options for .Cm fnc tree are as follows: .Bl -tag -width Ds .It Fl C , -no-colour Disable coloured output, which is enabled by default on supported terminals. If this option is not used, colour can be toggled with the .Sy c tree view key binding as documented below. .It Fl c , -commit Ar commit The displayed tree will reflect the state of the repository as at the check-in identified by .Ar commit . The expected argument is either the name of a branch, which will resolve to the latest commit on the given branch, or (a unique abbreviated prefix of) a valid commit UUID SHA1 or SHA3 hash. For a complete list of valid |
︙ | ︙ | |||
515 516 517 518 519 520 521 | view of the currently selected file. .It Cm Backspace, Arrow-left, h Move up a level to the parent directory. This is a no-op when in the root tree. .It Cm Arrow-down, j Move selection cursor one node down the tree. .It Cm Arrow-up, k Move selection cursor one node up the tree. | | | < < | 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 | view of the currently selected file. .It Cm Backspace, Arrow-left, h Move up a level to the parent directory. This is a no-op when in the root tree. .It Cm Arrow-down, j Move selection cursor one node down the tree. .It Cm Arrow-up, k Move selection cursor one node up the tree. .It Cm Page-down, Ctrl+f Move selection cursor one page down the tree. .It Cm Page-up, Ctrl+b Move selection cursor one page up the tree. .It Cm Home, gg Move selection cursor to the first node in the tree. .It Cm End, G Move selection cursor to the last node in the tree. .It Cm c Toggle coloured output. On supported terminals, .Nm will default to displaying the tree in colour. .It Cm t Open .Cm timeline |
︙ | ︙ | |||
552 553 554 555 556 557 558 | .It Cm N Find the previous tree node that matches the current search pattern. .El .Tg praise .It Cm blame Oo Fl C | -no-colour Oc \ Oo Fl c | -commit Ar commit Oo Fl r | -reverse Oc Oc Oo Fl h | -help Oc \ Oo Fl n | -limit Ar n Oc Ar path | | < < < | 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 | .It Cm N Find the previous tree node that matches the current search pattern. .El .Tg praise .It Cm blame Oo Fl C | -no-colour Oc \ Oo Fl c | -commit Ar commit Oo Fl r | -reverse Oc Oc Oo Fl h | -help Oc \ Oo Fl n | -limit Ar n Oc Ar path .Dl (aliases: Cm praise , Cm annotate , Cm bl , Cm pr , Cm an ) Show commit attribution history for each line of the file at the specified .Ar path , which may be absolute, relative to the current working directory, or relative to the repository root. This command must be executed from within or below the top level directory of the repository; that is, .Nm assumes a local checkout is open in or above the current working directory. .Pp Options for .Cm fnc blame are as follows: .Bl -tag -width Ds .It Fl C , -no-colour Disable coloured output, which is enabled by default on supported terminals. If this option is not used, colour can be toggled with the .Sy c blame view key binding as documented below. .It Fl c , -commit Ar commit Start blame of file at the specified .Ar path from the check-in identified by .Ar commit . The expected argument is either the name of a branch, which will resolve to the latest commit on the given branch, or (a unique abbreviated |
︙ | ︙ | |||
617 618 619 620 621 622 623 | .Cm fnc blame are as follows: .Bl -tag -width Ds .It Cm Arrow-down, j Move selection cursor down one line. .It Cm Arrow-up, k Move selection cursor up one line. | | | < < | | > > > > > < < < < < < < < | | | | 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 | .Cm fnc blame are as follows: .Bl -tag -width Ds .It Cm Arrow-down, j Move selection cursor down one line. .It Cm Arrow-up, k Move selection cursor up one line. .It Cm Page-down, Ctrl+f Move selection cursor down one page. .It Cm Page-up, Ctrl+b Move selection cursor up one page. .It Cm Home, gg Move selection cursor to the first line in the file. .It Cm End, G Move selection cursor to the last line in the file. .It Cm Enter Display the .Cm diff of the commit corresponding to the currently selected line. .It Cm b Blame the version of the file corresponding to the commit in the currently selected line. .It Cm p Blame the version of the file corresponding to the parent of the commit in the currently selected line. .It Cm B, Backspace Reload the previous blamed version of the file. .It Cm c Toggle coloured output. On supported terminals, .Nm will default to displaying the blamed file in colour. .It Cm / Prompt to enter a search term to begin searching the file for tokens matching the entered pattern. The search term is an extended regular expression, as documented in .Xr re_format 7 . .It Cm n Find the next token that matches the current search pattern. .It Cm N Find the previous token that matches the current search pattern. .El .Tg tag .It Cm branch Oo Fl C | -no-colour Oc Oo Fl -after Ar date | \ Fl -before Ar date Oc Oo Fl h | -help Oc Oo Fl -open | Fl -closed Oc \ Oo Fl p | -no-private Oc Oo Fl r | -reverse Oc \ Oo Fl s | -sort Ar order Oc Op Ar glob .Dl (aliases: Cm tag , Cm br ) Display navigable list of repository branches. If .Ar glob , is specified, only display branches matching the pattern provided. Pattern matching comparisons depend on the .Xr sqlite3 1 .B LIKE operator, which only folds case for the ASCII character set. This command must be executed from within or below the top level directory of the repository; that is, .Nm assumes a local checkout is open in or above the current working directory. .Pp Branches are lexicographically ordered by default, and are prefixed with an identifier corresponding to the branch state (i.e., open/closed). The current and private branches are additionally annotated with a postfixed identifier: .Bl -column ABCDEFGHIJ description .It +dev-foo Ta open .It -rm-bar Ta closed .It +trunk@ Ta current .It +wip-baz* Ta private .El .Pp All branches, irrespective of state or privacy, are displayed by default, but can be filtered based on several characteristics. .Pp Options for .Cm fnc branch are as follows: .Bl -tag -width Ds .It Fl C , -no-colour Disable coloured output, which is enabled by default on supported terminals. If this option is not used, colour can be toggled with the .Sy c branch view key binding as documented below. .It Fl a , -after Ar date Display only those branches with activity after the specified .Ar date . .It Fl b , -before Ar date Display only those branches with activity before the specified .Ar date . .It Fl c , -close Display only closed branches. .It Fl h , -help Display branch command help and usage information then exit. .It Fl o , -open Display only opened branches. .It Fl p , -no-private Do not show private branches, which are included in the list of displayed branches by default. .It Fl r , -reverse Reverse the order in which branches are displayed. .It Fl s , -sort Ar order Sort branches by .Ar order . Valid .Ar order values are as follows: .Bl -column YXZ description .Sy mru Ta most recently used .Sy state Ta open/closed state .El .Pp Branches are sorted in lexicographical order by default. .El .Pp Key bindings for .Cm fnc branch are as follows: .Bl -tag -width Ds .It Cm Arrow-down, j Move selection cursor down one branch. .It Cm Arrow-up, k Move selection cursor up one branch. .It Cm Page-down, Ctrl+f Move selection cursor down one page. .It Cm Page-up, Ctrl+b Move selection cursor up one page. .It Cm Home, gg Move selection cursor to the first branch in the list. .It Cm End, G Move selection cursor to the last branch in the list. .It Cm Enter, Space Display the |
︙ | ︙ | |||
762 763 764 765 766 767 768 | .It Cm i Toggle display of the SHA{1,3} hash that identifies branch, which is the hash of the commit on the tip of said branch. .It Cm t Open the .Cm tree view of the currently selected branch. | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 | .It Cm i Toggle display of the SHA{1,3} hash that identifies branch, which is the hash of the commit on the tip of said branch. .It Cm t Open the .Cm tree view of the currently selected branch. .It Cm R, Ctrl+l Reload the view with all repository branches, irrespective of which options were used in this .Cm fnc branch invocation. .It Cm / Prompt to enter a search term to begin searching the list for branches matching the entered pattern. The search term is an extended regular expression, as documented in .Xr re_format 7 . .It Cm n Find the next branch that matches the current search pattern. .It Cm N Find the previous branch that matches the current search pattern. .El .El .Sh EXIT STATUS .Ex -std fnc .Sh SEE ALSO .Xr fossil 1 , .Xr re_format 7 .Xr sqlite3 1 .Sh AUTHOR .An Mark Jamsek Aq Mt mark@jamsek.com |
Changes to src/fnc.c.
︙ | ︙ | |||
62 63 64 65 66 67 68 | #include <libgen.h> #include <regex.h> #include <signal.h> #include <wchar.h> #include <langinfo.h> #include "libfossil.h" | < | 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | #include <libgen.h> #include <regex.h> #include <signal.h> #include <wchar.h> #include <langinfo.h> #include "libfossil.h" #define FNC_VERSION VERSION /* cf. Makefile */ /* Utility macros. */ #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #if !defined(CTRL) |
︙ | ︙ | |||
143 144 145 146 147 148 149 | __dead static void usage(void); static void usage_timeline(void); static void usage_diff(void); static void usage_tree(void); static void usage_blame(void); static void usage_branch(void); | < < | < < < < < | | < | | < < < | | | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 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 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | __dead static void usage(void); static void usage_timeline(void); static void usage_diff(void); static void usage_tree(void); static void usage_blame(void); static void usage_branch(void); static int fcli_flag_type_arg_cb(fcli_cliflag const *); static int cmd_timeline(fcli_command const *); static int cmd_diff(fcli_command const *); static int cmd_tree(fcli_command const *); static int cmd_blame(fcli_command const *); static int cmd_branch(fcli_command const *); /* * Singleton initialising global configuration and state for app startup. */ static struct fnc_setup { /* Global options. */ const char *cmdarg; /* Retain argv[1] for use/err report. */ const char *sym; /* Open view from this symbolic name. */ const char *path; /* Optional path for timeline & tree. */ int err; /* Indicate fnc error state. */ bool hflag; /* Flag if --help is requested. */ bool vflag; /* Flag if --version is requested. */ bool reverse; /* Reverse branch sort or blame. */ /* Timeline options. */ struct artifact_types { const char **values; short nitems; } *filter_types; /* Only load commits of <type>. */ union { const char *zlimit; int limit; } nrecords; /* Number of commits to load. */ const char *filter_tag; /* Only load commits with <tag>. */ const char *filter_branch; /* Only load commits from <branch>. */ const char *filter_user; /* Only load commits from <user>. */ const char *filter_type; /* Placeholder for repeatable types. */ bool utc; /* Display UTC sans user local time. */ /* Diff options. */ const char *context; /* Number of context lines. */ bool ws; /* Ignore whitespace-only changes. */ bool nocolour; /* Disable colour in diff output. */ bool quiet; /* Disable verbose diff output. */ bool invert; /* Toggle inverted diff output. */ /* Branch options. */ const char *before; /* Last branch change before date. */ const char *after; /* Last branch change after date. */ const char *sort; /* Lexicographical, MRU, open/closed. */ bool closed; /* Show only closed branches. */ bool open; /* Show only open branches */ bool noprivate; /* Don't show private branches. */ /* Command line flags and help. */ fcli_help_info fnc_help; /* Global help. */ fcli_cliflag cliflags_global[3]; /* Global options. */ fcli_command cmd_args[6]; /* App commands. */ fcli_cliflag cliflags_timeline[11]; /* Timeline options. */ fcli_cliflag cliflags_diff[7]; /* Diff options. */ fcli_cliflag cliflags_tree[4]; /* Tree options. */ fcli_cliflag cliflags_blame[6]; /* Blame options. */ fcli_cliflag cliflags_branch[10]; /* Branch options. */ } fnc_init = { NULL, /* cmdarg copy of argv[1] to aid usage/error report. */ NULL, /* sym(bolic name) of commit to open defaults to tip. */ NULL, /* path for tree to open or timeline to find commits. */ 0, /* err fnc error state. */ false, /* hflag if --help is requested. */ false, /* vflag if --version is requested. */ false, /* reverse branch sort/annotation defaults to off. */ NULL, /* filter_types defaults to indiscriminate. */ {0}, /* nrecords defaults to all commits. */ NULL, /* filter_tag defaults to indiscriminate. */ NULL, /* filter_branch defaults to indiscriminate. */ NULL, /* filter_user defaults to indiscriminate. */ NULL, /* filter_type temporary placeholder. */ false, /* utc defaults to off (i.e., show user local time). */ NULL, /* context defaults to five context lines. */ false, /* ws defaults to acknowledge whitespace. */ false, /* nocolour defaults to off (i.e., use diff colours). */ false, /* quiet defaults to off (i.e., verbose diff is on). */ false, /* invert diff defaults to off. */ NULL, /* before defaults to any time. */ NULL, /* after defaults to any time. */ NULL, /* sort by MRU or open/closed (dflt: lexicographical) */ false, /* closed only branches is off (defaults to all). */ false, /* open only branches is off by (defaults to all). */ false, /* noprivate is off (default to show private branch). */ { /* fnc_help global app help details. */ "A read-only ncurses browser for Fossil repositories in the " "terminal.", NULL, usage }, { /* cliflags_global global app options. */ FCLI_FLAG_BOOL("h", "help", &fnc_init.hflag, "Display program help and usage then exit."), FCLI_FLAG_BOOL("v", "version", &fnc_init.vflag, "Display program version number and exit."), |
︙ | ︙ | |||
269 270 271 272 273 274 275 | cmd_tree, usage_tree, fnc_init.cliflags_tree}, {"blame", "bl\0praise\0pr\0annotate\0an\0", "Show commit attribution history for each line of a file.", cmd_blame, usage_blame, fnc_init.cliflags_blame}, {"branch", "br\0tag\0", "Show navigable list of repository branches.", cmd_branch, usage_branch, fnc_init.cliflags_branch}, | < < < | | | | | | | | | | | | | | | | < < < | | | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > < < < < < < < | < | > | 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 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 430 431 | cmd_tree, usage_tree, fnc_init.cliflags_tree}, {"blame", "bl\0praise\0pr\0annotate\0an\0", "Show commit attribution history for each line of a file.", cmd_blame, usage_blame, fnc_init.cliflags_blame}, {"branch", "br\0tag\0", "Show navigable list of repository branches.", cmd_branch, usage_branch, fnc_init.cliflags_branch}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel. */ }, { /* cliflags_timeline timeline command related options. */ FCLI_FLAG_BOOL("C", "no-colour", &fnc_init.nocolour, "Disable colourised timeline, which is enabled by default on\n " "supported terminals. Colour can also be toggled with the 'c' " "\n key binding in timeline view when this option is not used."), FCLI_FLAG("T", "tag", "<tag>", &fnc_init.filter_tag, "Only display commits with T cards containing <tag>."), FCLI_FLAG("b", "branch", "<branch>", &fnc_init.filter_branch, "Only display commits that reside on the given <branch>."), FCLI_FLAG("c", "commit", "<commit>", &fnc_init.sym, "Open the timeline from <commit>. Common symbols are:\n" "\tSHA{1,3} hash\n" "\tSHA{1,3} unique prefix\n" "\tbranch\n" "\ttag:TAG\n" "\troot:BRANCH\n" "\tISO8601 date\n" "\tISO8601 timestamp\n" "\t{tip,current,prev,next}\n " "For a complete list of symbols see Fossil's Check-in Names:\n " "https://fossil-scm.org/home/doc/trunk/www/checkin_names.wiki"), FCLI_FLAG_BOOL("h", "help", NULL, "Display timeline command help and usage."), FCLI_FLAG("n", "limit", "<n>", &fnc_init.nrecords.zlimit, "Limit display to <n> latest commits; defaults to entire history " "of\n current checkout. Negative values are a no-op."), FCLI_FLAG_X("t", "type", "<type>", &fnc_init.filter_type, fcli_flag_type_arg_cb, "Only display <type> commits. Valid types are:\n" "\tci - check-in\n" "\tw - wiki\n" "\tt - ticket\n" "\te - technote\n" "\tf - forum post\n" " n.b. This is a repeatable flag (e.g., -t ci -t w)."), FCLI_FLAG("u", "username", "<user>", &fnc_init.filter_user, "Only display commits authored by <username>."), FCLI_FLAG_BOOL("z", "utc", &fnc_init.utc, "Use UTC (instead of local) time."), fcli_cliflag_empty_m }, /* End cliflags_timeline. */ { /* cliflags_diff diff command related options. */ FCLI_FLAG_BOOL("C", "no-colour", &fnc_init.nocolour, "Disable coloured diff output, which is enabled by default on\n " "supported terminals. Colour can also be toggled with the 'c' " "\n key binding in diff view when this option is not used."), FCLI_FLAG_BOOL("h", "help", NULL, "Display diff command help and usage."), FCLI_FLAG_BOOL("i", "invert", &fnc_init.invert, "Invert difference between artifacts. Inversion can also be " "toggled\n with the 'i' key binding in diff view."), FCLI_FLAG_BOOL("q", "quiet", &fnc_init.quiet, "Disable verbose diff output; that is, do not output complete" " content\n of newly added or deleted files. Verbosity can also" " be toggled with\n the 'v' key binding in diff view."), FCLI_FLAG_BOOL("w", "whitespace", &fnc_init.ws, "Ignore whitespace-only changes when displaying diff. This option " "can\n also be toggled with the 'w' key binding in diff view."), FCLI_FLAG("x", "context", "<n>", &fnc_init.context, "Show <n> context lines when displaying diff; <n> is capped at 64." "\n Negative values are a no-op."), fcli_cliflag_empty_m }, /* End cliflags_diff. */ { /* cliflags_tree tree command related options. */ FCLI_FLAG_BOOL("C", "no-colour", &fnc_init.nocolour, "Disable coloured output, which is enabled by default on supported" "\n terminals. Colour can also be toggled with the 'c' key " "binding when\n this option is not used."), FCLI_FLAG("c", "commit", "<commit>", &fnc_init.sym, "Display tree that reflects repository state as at <commit>.\n" " Common symbols are:" "\n\tSHA{1,3} hash\n" "\tSHA{1,3} unique prefix\n" "\tbranch\n" "\ttag:TAG\n" "\troot:BRANCH\n" "\tISO8601 date\n" "\tISO8601 timestamp\n" "\t{tip,current,prev,next}\n " "For a complete list of symbols see Fossil's Check-in Names:\n " "https://fossil-scm.org/home/doc/trunk/www/checkin_names.wiki"), FCLI_FLAG_BOOL("h", "help", NULL, "Display tree command help and usage."), fcli_cliflag_empty_m }, /* End cliflags_tree. */ { /* cliflags_blame blame command related options. */ FCLI_FLAG_BOOL("C", "no-colour", &fnc_init.nocolour, "Disable coloured output, which is enabled by default on supported" "\n terminals. Colour can also be toggled with the 'c' key " "binding when\n this option is not used."), FCLI_FLAG("c", "commit", "<commit>", &fnc_init.sym, "Start blame of specified file from <commit>. Common symbols are:\n" "\tSHA{1,3} hash\n" "\tSHA{1,3} unique prefix\n" "\tbranch\n" "\ttag:TAG\n" "\troot:BRANCH\n" "\tISO8601 date\n" "\tISO8601 timestamp\n" "\t{tip,current,prev,next}\n " "For a complete list of symbols see Fossil's Check-in Names:\n " "https://fossil-scm.org/home/doc/trunk/www/checkin_names.wiki"), FCLI_FLAG_BOOL("h", "help", NULL, "Display blame command help and usage."), FCLI_FLAG("n", "limit", "<n>", &fnc_init.nrecords.zlimit, "Limit depth of blame history to <n> commits or seconds. Denote the" "\n latter by postfixing 's' (e.g., 30s). Useful for large files" " with\n extensive history. Persists for the duration of the " "session."), FCLI_FLAG_BOOL("r", "reverse", &fnc_init.reverse, "Reverse annotate the file starting from a historical commit. " "Rather\n than show the most recent change of each line, show " "the first time\n each line was modified after the specified " "commit. Requires -c|--commit."), fcli_cliflag_empty_m }, /* End cliflags_blame. */ { /* cliflags_branch branch command related options. */ FCLI_FLAG_BOOL("C", "no-colour", &fnc_init.nocolour, "Disable coloured output, which is enabled by default on supported" "\n terminals. Colour can also be toggled with the 'c' key " "binding when\n this option is not used."), FCLI_FLAG("a", "after", "<date>", &fnc_init.after, "Show branches with last activity occuring after <date>, which is\n" " expected to be either an ISO8601 (e.g., 2020-10-10) or " "unambiguous\n DD/MM/YYYY or MM/DD/YYYY formatted date."), FCLI_FLAG("b", "before", "<date>", &fnc_init.before, "Show branches with last activity occuring before <date>, which is" "\n expected to be either an ISO8601 (e.g., 2020-10-10) or " "unambiguous\n DD/MM/YYYY or MM/DD/YYYY formatted date."), FCLI_FLAG_BOOL("c", "closed", &fnc_init.closed, "Show closed branches only. Open and closed branches are listed by " "\n default."), FCLI_FLAG_BOOL("h", "help", NULL, "Display branch command help and usage."), FCLI_FLAG_BOOL("o", "open", &fnc_init.open, "Show open branches only. Open and closed branches are listed by " "\n default."), FCLI_FLAG_BOOL("p", "no-private", &fnc_init.noprivate, "Do not show private branches, which are otherwise included in the" "\n list of displayed branches by default."), FCLI_FLAG_BOOL("r", "reverse", &fnc_init.reverse, "Reverse the order in which branches are displayed."), FCLI_FLAG("s", "sort", "<order>", &fnc_init.sort, "Sort branches by <order>. Available options are:\n" "\tmru - most recently used\n" "\tstate - open/closed state\n " "Branches are sorted in lexicographical order by default."), fcli_cliflag_empty_m }, /* End cliflags_blame. */ }; enum fsl_list_object { FNC_ARTIFACT_OBJ, FNC_COLOUR_OBJ }; enum date_string { ISO8601_DATE_ONLY = 10, ISO8601_TIMESTAMP = 20 }; |
︙ | ︙ | |||
472 473 474 475 476 477 478 | SEARCH_WAITING, SEARCH_CONTINUE, SEARCH_COMPLETE, SEARCH_NO_MATCH, SEARCH_FOR_END }; | < < < | < < < | | | | < | | | | | | | < | > > > | 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 | SEARCH_WAITING, SEARCH_CONTINUE, SEARCH_COMPLETE, SEARCH_NO_MATCH, SEARCH_FOR_END }; enum fnc_colours { FNC_DIFF_META = 1, FNC_DIFF_MINUS, FNC_DIFF_PLUS, FNC_DIFF_CHNK, FNC_TREE_LINK, FNC_TREE_DIR, FNC_TREE_EXEC, FNC_COMMIT_ID, FNC_USER_STR, FNC_DATE_STR, FNC_TAGS_STR }; enum fnc_diff_type { FNC_DIFF_CKOUT, FNC_DIFF_COMMIT, FNC_DIFF_BLOB, FNC_DIFF_WIKI }; struct fnc_colour { regex_t regex; uint8_t scheme; }; struct fsl_list_state { enum fsl_list_object obj; }; struct fnc_commit_artifact { fsl_buffer wiki; fsl_buffer pwiki; fsl_list changeset; fsl_uuid_str uuid; fsl_uuid_str puuid; |
︙ | ︙ | |||
617 618 619 620 621 622 623 624 625 | int spin_idx; int ncommits_needed; /* * XXX Is there a more elegant solution to retrieving return codes from * thread functions while pinging between, but before we join, threads? */ int rc; bool endjmp; bool timeline_end; | > < | | 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 | int spin_idx; int ncommits_needed; /* * XXX Is there a more elegant solution to retrieving return codes from * thread functions while pinging between, but before we join, threads? */ int rc; bool tree_open; bool endjmp; bool timeline_end; sig_atomic_t *quit; pthread_cond_t commit_consumer; pthread_cond_t commit_producer; }; struct fnc_tl_view_state { struct fnc_tl_thread_cx thread_cx; struct commit_queue commits; struct commit_entry *first_commit_onscreen; struct commit_entry *last_commit_onscreen; struct commit_entry *selected_commit; struct commit_entry *matched_commit; struct commit_entry *search_commit; fsl_list colours; const char *curr_ckout_uuid; char *path; /* Match commits involving path. */ int selected_idx; sig_atomic_t quit; pthread_t thread_id; bool colour; }; |
︙ | ︙ | |||
655 656 657 658 659 660 661 | TAILQ_HEAD(fnc_pathlist_head, fnc_pathlist_entry); struct fnc_diff_view_state { struct fnc_view *timeline_view; struct fnc_commit_artifact *selected_commit; struct fnc_pathlist_head *paths; fsl_buffer buf; | | | 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 | TAILQ_HEAD(fnc_pathlist_head, fnc_pathlist_entry); struct fnc_diff_view_state { struct fnc_view *timeline_view; struct fnc_commit_artifact *selected_commit; struct fnc_pathlist_head *paths; fsl_buffer buf; fsl_list colours; FILE *f; fsl_uuid_str id1; fsl_uuid_str id2; int first_line_onscreen; int last_line_onscreen; int diff_flags; int context; |
︙ | ︙ | |||
684 685 686 687 688 689 690 | struct fnc_repository_tree *repo; /* The repository tree. */ struct fnc_tree_object *root; /* Top level repo tree. */ struct fnc_tree_object *tree; /* Currently displayed tree */ struct fnc_tree_entry *first_entry_onscreen; struct fnc_tree_entry *last_entry_onscreen; struct fnc_tree_entry *selected_entry; struct fnc_tree_entry *matched_entry; | | | 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 | struct fnc_repository_tree *repo; /* The repository tree. */ struct fnc_tree_object *root; /* Top level repo tree. */ struct fnc_tree_object *tree; /* Currently displayed tree */ struct fnc_tree_entry *first_entry_onscreen; struct fnc_tree_entry *last_entry_onscreen; struct fnc_tree_entry *selected_entry; struct fnc_tree_entry *matched_entry; fsl_list colours; char *tree_label; /* Headline string. */ fsl_uuid_str commit_id; fsl_id_t rid; int ndisplayed; int selected_idx; bool colour; bool show_id; |
︙ | ︙ | |||
743 744 745 746 747 748 749 | }; struct fnc_blame_view_state { struct fnc_blame blame; struct fnc_commit_id_queue blamed_commits; struct fnc_commit_qid *blamed_commit; struct fnc_commit_artifact *selected_commit; | | | 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 | }; struct fnc_blame_view_state { struct fnc_blame blame; struct fnc_commit_id_queue blamed_commits; struct fnc_commit_qid *blamed_commit; struct fnc_commit_artifact *selected_commit; fsl_list colours; fsl_uuid_str commit_id; char *path; int first_line_onscreen; int last_line_onscreen; int selected_line; int matched_line; int spin_idx; |
︙ | ︙ | |||
779 780 781 782 783 784 785 | struct fnc_branch_view_state { struct fnc_branchlist_head branches; struct fnc_branchlist_entry *first_branch_onscreen; struct fnc_branchlist_entry *last_branch_onscreen; struct fnc_branchlist_entry *matched_branch; struct fnc_branchlist_entry *selected_branch; | < | 749 750 751 752 753 754 755 756 757 758 759 760 761 762 | struct fnc_branch_view_state { struct fnc_branchlist_head branches; struct fnc_branchlist_entry *first_branch_onscreen; struct fnc_branchlist_entry *last_branch_onscreen; struct fnc_branchlist_entry *matched_branch; struct fnc_branchlist_entry *selected_branch; const char *branch_glob; double dateline; int branch_flags; #define BRANCH_LS_CLOSED_ONLY 0x001 /* Show closed branches only. */ #define BRANCH_LS_OPEN_ONLY 0x002 /* Show open branches only. */ #define BRANCH_LS_OPEN_CLOSED 0x003 /* Show open & closed branches (dflt). */ #define BRANCH_LS_BITMASK 0x003 |
︙ | ︙ | |||
845 846 847 848 849 850 851 | static volatile sig_atomic_t rec_sigpipe; static volatile sig_atomic_t rec_sigcont; static void fnc_show_version(void); static int init_curses(void); static struct fnc_view *view_open(int, int, int, int, enum fnc_view_id); static int open_timeline_view(struct fnc_view *, fsl_id_t, | | | 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 | static volatile sig_atomic_t rec_sigpipe; static volatile sig_atomic_t rec_sigcont; static void fnc_show_version(void); static int init_curses(void); static struct fnc_view *view_open(int, int, int, int, enum fnc_view_id); static int open_timeline_view(struct fnc_view *, fsl_id_t, const char *); static int view_loop(struct fnc_view *); static int show_timeline_view(struct fnc_view *); static void *tl_producer_thread(void *); static int block_main_thread_signals(void); static int build_commits(struct fnc_tl_thread_cx *); static int commit_builder(struct fnc_commit_artifact **, fsl_id_t, fsl_stmt *); |
︙ | ︙ | |||
912 913 914 915 916 917 918 | struct fnc_commit_artifact *, int, int, int); static int diff_file_artifact(fsl_buffer *, fsl_id_t, const fsl_card_F *, fsl_id_t, const fsl_card_F *, fsl_ckout_change_e, int, int, int, enum fnc_diff_type); static int show_diff(struct fnc_view *); static int write_diff(struct fnc_view *, char *); | | < | 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 | struct fnc_commit_artifact *, int, int, int); static int diff_file_artifact(fsl_buffer *, fsl_id_t, const fsl_card_F *, fsl_id_t, const fsl_card_F *, fsl_ckout_change_e, int, int, int, enum fnc_diff_type); static int show_diff(struct fnc_view *); static int write_diff(struct fnc_view *, char *); static int match_line(const void *, const void *); static int write_matched_line(int *, const char *, int, int, WINDOW *, regmatch_t *); static void draw_vborder(struct fnc_view *); static int diff_input_handler(struct fnc_view **, struct fnc_view *, int); static int set_selected_commit(struct fnc_diff_view_state *, struct commit_entry *); |
︙ | ︙ | |||
1017 1018 1019 1020 1021 1022 1023 | static void branch_scroll_down(struct fnc_branch_view_state *, int); static int branch_search_next(struct fnc_view *); static int branch_search_init(struct fnc_view *); static int match_branchlist_entry(struct fnc_branchlist_entry *, regex_t *); static int close_branch_view(struct fnc_view *); | | | < | < | < < < < < < < | < < > > > > > < < | 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 | static void branch_scroll_down(struct fnc_branch_view_state *, int); static int branch_search_next(struct fnc_view *); static int branch_search_init(struct fnc_view *); static int match_branchlist_entry(struct fnc_branchlist_entry *, regex_t *); static int close_branch_view(struct fnc_view *); static void fnc_free_branches(struct fnc_branch_view_state *); static void fnc_branch_close(struct fnc_branch *); static void view_set_child(struct fnc_view *, struct fnc_view *); static int view_close_child(struct fnc_view *); static int close_tree_view(struct fnc_view *); static int close_timeline_view(struct fnc_view *); static int close_diff_view(struct fnc_view *); static int view_resize(struct fnc_view *); static int screen_is_split(struct fnc_view *); static bool screen_is_shared(struct fnc_view *); static void fnc_resizeterm(void); static int join_tl_thread(struct fnc_tl_view_state *); static void fnc_free_commits(struct commit_queue *); static void fnc_commit_artifact_close(struct fnc_commit_artifact*); static int fsl_list_object_free(void *, void *); static void sigwinch_handler(int); static void sigpipe_handler(int); static void sigcont_handler(int); static int strtonumcheck(int *, const char *, const int, const int); static int fnc_date_to_mtime(double *, const char *, int); static char *fnc_strsep (char **, const char *); static int set_colours(fsl_list *, enum fnc_view_id vid); static int match_colour(const void *, const void *); static bool fnc_home(struct fnc_view *); static struct fnc_colour *get_colour(fsl_list *, int); static struct fnc_tree_entry *get_tree_entry(struct fnc_tree_object *, int); static struct fnc_tree_entry *find_tree_entry(struct fnc_tree_object *, const char *, size_t); int main(int argc, const char **argv) { fcli_command *cmd = NULL; char *path = NULL; int rc = 0; fnc_init.filter_types = (struct artifact_types *)fsl_malloc(sizeof(struct artifact_types)); fnc_init.filter_types->values = fsl_malloc(sizeof(char *)); fnc_init.filter_types->nitems = 0; if (!setlocale(LC_CTYPE, "")) fsl_fprintf(stderr, "[!] Warning: Can't set locale.\n"); fnc_init.cmdarg = argv[1]; /* Which cmd to show usage if needed. */ fcli.clientFlags.verbose = 2; /* Verbose error reporting. */ fcli.cliFlags = fnc_init.cliflags_global; fcli.appHelp = &fnc_init.fnc_help; rc = fcli_setup(argc, argv); if (rc) goto end; if (fnc_init.vflag) { |
︙ | ︙ | |||
1136 1137 1138 1139 1140 1141 1142 | end: fsl_free(path); endwin(); if (rc) { if (rc == FCLI_RC_HELP) rc = 0; else if (rc == FSL_RC_BREAK) { | | | < | > | < < | | < | 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 | end: fsl_free(path); endwin(); if (rc) { if (rc == FCLI_RC_HELP) rc = 0; else if (rc == FSL_RC_BREAK) { fsl_cx *f = fcli_cx(); const char *errstr; fsl_error_get(&f->error, &errstr, NULL); fsl_fprintf(stdout, "%s", errstr); fcli_err_reset(); /* For fcli_end_of_main() */ rc = 0; } } putchar('\n'); return fcli_end_of_main(rc); } static int cmd_timeline(fcli_command const *argv) { struct fnc_view *v; fsl_cx *f = fcli_cx(); fsl_id_t rid = -1; char *path = NULL; int rc = 0; rc = fcli_process_flags(argv->flags); if (rc || (rc = fcli_has_unused_flags(false))) return rc; if (fnc_init.nrecords.zlimit) if ((rc = strtonumcheck(&fnc_init.nrecords.limit, fnc_init.nrecords.zlimit, INT_MIN, INT_MAX))) return rc; if (fnc_init.sym != NULL) { rc = fsl_sym_to_rid(f, fnc_init.sym, FSL_SATYPE_CHECKIN, &rid); if (rc || rid < 0) return RC(FSL_RC_TYPE, "artifact [%s] not resolvable to a commit", fnc_init.sym); } if (fnc_init.path) path = fsl_strdup(fnc_init.path); else rc = map_repo_path(&path); if (!rc) rc = init_curses(); if (rc) goto end; v = view_open(0, 0, 0, 0, FNC_VIEW_TIMELINE); if (v == NULL) { RC(FSL_RC_ERROR, "%s", "view_open"); goto end; } rc = open_timeline_view(v, rid, path); if (!rc) rc = view_loop(v); end: fsl_free(path); return rc; } /* * Look for an in-repository path in **argv. If found, canonicalise it as an * absolute path relative to the repository root (e.g., /ckoutdir/found/path), |
︙ | ︙ | |||
1503 1504 1505 1506 1507 1508 1509 | } keypad(view->window, TRUE); return view; } static int | | < | < < | 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 | } keypad(view->window, TRUE); return view; } static int open_timeline_view(struct fnc_view *view, fsl_id_t rid, const char *path) { struct fnc_tl_view_state *s = &view->state.timeline; fsl_cx *f = fcli_cx(); fsl_db *db = fsl_cx_db_repo(f); fsl_buffer sql = fsl_buffer_empty; char *startdate = NULL; fsl_id_t idtag = 0; int idx, rc = 0; if (path != s->path) { fsl_free(s->path); s->path = fsl_strdup(path); if (s->path == NULL) return RC(FSL_RC_ERROR, "%s", "fsl_strdup"); } |
︙ | ︙ | |||
1539 1540 1541 1542 1543 1544 1545 | /* } */ s->thread_cx.q = NULL; /* s->selected_idx = 0; */ /* Unnecessary? */ TAILQ_INIT(&s->commits.head); s->commits.ncommits = 0; | | < < < | < | | | | > > > > > > | | | | | | | | | | | | | | < < < < < < < < < < < < < < < < < < < < | 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 | /* } */ s->thread_cx.q = NULL; /* s->selected_idx = 0; */ /* Unnecessary? */ TAILQ_INIT(&s->commits.head); s->commits.ncommits = 0; if (rid != -1) startdate = fsl_mprintf("(SELECT mtime FROM event " "WHERE objid=%d)", rid); else fsl_ckout_version_info(f, NULL, &s->curr_ckout_uuid); /* * In 'fnc timeline -R repo.fossil path' case, check that path is a * valid repository path in the repository tree as at either the * latest check-in or the specified commit. */ if (s->curr_ckout_uuid == NULL && path[1]) { fsl_deck d = fsl_deck_empty; bool ispath = false; rc = fsl_deck_load_sym(f, &d, fnc_init.sym ? fnc_init.sym : "tip", FSL_SATYPE_CHECKIN); fsl_deck_F_rewind(&d); if (fsl_deck_F_search(&d, path + 1 /* Slash */) == NULL) { const fsl_card_F *cf; fsl_deck_F_next(&d, &cf); do { fsl_deck_F_next(&d, &cf); if (cf && !fsl_strncmp(path + 1 /* Slash */, cf->name, fsl_strlen(path) - 1)) { ispath = true; break; } } while (cf); } else ispath = true; fsl_deck_finalize(&d); if (!ispath) return RC(FSL_RC_NOT_FOUND, "'%s' invalid path in [%s]", path + 1, fnc_init.sym ? fnc_init.sym : "tip"); } if ((rc = pthread_cond_init(&s->thread_cx.commit_consumer, NULL))) { RC(fsl_errno_to_rc(rc, FSL_RC_ACCESS), "%s", "pthread_cond_init"); goto end; } if ((rc = pthread_cond_init(&s->thread_cx.commit_producer, NULL))) { RC(fsl_errno_to_rc(rc, FSL_RC_ACCESS), "%s", "pthread_cond_init"); goto end; } fsl_buffer_appendf(&sql, "SELECT " /* 0 */"uuid, " /* 1 */"datetime(event.mtime%s), " /* 2 */"coalesce(euser, user), " /* 3 */"rid AS rid, " /* 4 */"event.type AS eventtype, " /* 5 */"(SELECT group_concat(substr(tagname,5), ',') " "FROM tag, tagxref WHERE tagname GLOB 'sym-*' " "AND tag.tagid=tagxref.tagid AND tagxref.rid=blob.rid " "AND tagxref.tagtype > 0) as tags, " /*6*/"coalesce(ecomment, comment) AS comment FROM event JOIN blob " "WHERE blob.rid=event.objid", fnc_init.utc ? "" : ", 'localtime'"); if (fnc_init.filter_types->nitems) { fsl_buffer_appendf(&sql, " AND ("); for (idx = 0; idx < fnc_init.filter_types->nitems; ++idx) { fsl_buffer_appendf(&sql, " eventtype=%Q%s", fnc_init.filter_types->values[idx], (idx + 1) < fnc_init.filter_types->nitems ? " OR " : ")"); /* This produces a double-free? */ /* fsl_free((char *)fnc_init.filter_types->values[idx]); */ fnc_init.filter_types->values[idx] = NULL; } fsl_free(fnc_init.filter_types->values); fsl_free(fnc_init.filter_types); } if (fnc_init.filter_branch) { idtag = fsl_db_g_id(db, 0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", fnc_init.filter_branch); if (idtag > 0) fsl_buffer_appendf(&sql, " AND EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%"FSL_ID_T_PFMT " AND tagtype > 0 AND rid=blob.rid)", idtag); else { rc = RC(FSL_RC_NOT_FOUND, "Invalid branch name [%s]", fnc_init.filter_branch); goto end; } } if (fnc_init.filter_tag) { idtag = fsl_db_g_id(db, 0, "SELECT tagid FROM tag WHERE tagname GLOB 'sym-%q'", fnc_init.filter_tag); if (idtag == 0) idtag = fsl_db_g_id(db, 0, "SELECT tagid FROM tag WHERE tagname='%q'", fnc_init.filter_tag); if (idtag > 0) fsl_buffer_appendf(&sql, " AND EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%"FSL_ID_T_PFMT " AND tagtype > 0 AND rid=blob.rid)", idtag); else { rc = RC(FSL_RC_NOT_FOUND, "Invalid tag [%s]", fnc_init.filter_tag); goto end; } } if (fnc_init.filter_user) if ((rc = fsl_buffer_appendf(&sql, " AND coalesce(euser, user) GLOB lower('*%q*')", fnc_init.filter_user))) goto end; if (startdate) { fsl_buffer_appendf(&sql, " AND event.mtime <= %s", startdate); fsl_free(startdate); } /* |
︙ | ︙ | |||
1720 1721 1722 1723 1724 1725 1726 | s->thread_cx.q = fsl_stmt_malloc(); rc = fsl_db_prepare(db, s->thread_cx.q, "%b", &sql); if (rc) { rc = RC(rc, "%s", "fsl_db_prepare"); goto end; } rc = fsl_stmt_step(s->thread_cx.q); | > | | | | | | | | | | > < | < | < | | 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 | s->thread_cx.q = fsl_stmt_malloc(); rc = fsl_db_prepare(db, s->thread_cx.q, "%b", &sql); if (rc) { rc = RC(rc, "%s", "fsl_db_prepare"); goto end; } rc = fsl_stmt_step(s->thread_cx.q); if (rc) { switch (rc) { case FSL_RC_STEP_ROW: rc = 0; break; case FSL_RC_STEP_ERROR: rc = RC(rc, "%s", "fsl_stmt_step"); goto end; case FSL_RC_STEP_DONE: rc = RC(FSL_RC_BREAK, "%s", "no matching records"); goto end; } } s->colour = !fnc_init.nocolour && has_colors(); s->thread_cx.rc = 0; s->thread_cx.db = db; s->thread_cx.spin_idx = 0; s->thread_cx.ncommits_needed = view->nlines - 1; s->thread_cx.commits = &s->commits; s->thread_cx.timeline_end = false; s->thread_cx.quit = &s->quit; s->thread_cx.first_commit_onscreen = &s->first_commit_onscreen; s->thread_cx.selected_commit = &s->selected_commit; s->thread_cx.searching = &view->searching; s->thread_cx.search_status = &view->search_status; s->thread_cx.regex = &view->regex; s->thread_cx.path = s->path; if (s->colour) set_colours(&s->colours, FNC_VIEW_TIMELINE); end: fsl_buffer_clear(&sql); if (rc) { close_timeline_view(view); if (db->error.code) rc = fsl_cx_uplift_db_error(f, db); } return rc; } static int |
︙ | ︙ | |||
2009 2010 2011 2012 2013 2014 2015 | return 0; } static int build_commits(struct fnc_tl_thread_cx *cx) { | | | | < | | | | | | 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 | return 0; } static int build_commits(struct fnc_tl_thread_cx *cx) { int rc = 0; if (cx->tree_open) { /* * XXX If a tree has been opened with the 't' key binding, the * commit builder statement needs to be reset otherwise one of * the SQLite3 APIs down the fsl_stmt_step() call stack fails, * irrespective of whether fsl_db_prepare_cached() is called. */ fsl_size_t loaded = cx->q->rowCount; cx->tree_open = false; rc = fsl_stmt_reset(cx->q); if (rc) return RC(rc, "%s", "fsl_stmt_reset"); while (loaded--) if ((rc = fsl_stmt_step(cx->q)) != FSL_RC_STEP_ROW) return RC(rc, "%s", "fsl_stmt_step"); } |
︙ | ︙ | |||
2094 2095 2096 2097 2098 2099 2100 | * Given prepared SQL statement q _XOR_ record ID rid, allocate and build the * corresponding commit artifact from the result set. The commit must * eventually be disposed of with fnc_commit_artifact_close(). */ static int commit_builder(struct fnc_commit_artifact **ptr, fsl_id_t rid, fsl_stmt *q) { | | | 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 | * Given prepared SQL statement q _XOR_ record ID rid, allocate and build the * corresponding commit artifact from the result set. The commit must * eventually be disposed of with fnc_commit_artifact_close(). */ static int commit_builder(struct fnc_commit_artifact **ptr, fsl_id_t rid, fsl_stmt *q) { fsl_cx *f = fcli_cx(); fsl_db *db = fsl_needs_repo(f); struct fnc_commit_artifact *commit = NULL; fsl_buffer buf = fsl_buffer_empty; const char *comment, *prefix, *type; int rc = 0; enum fnc_diff_type diff_type = FNC_DIFF_WIKI; |
︙ | ︙ | |||
2333 2334 2335 2336 2337 2338 2339 | rc = formatln(&wcstr, &wstrlen, headln, view->ncols, 0); if (rc) goto end; werase(view->window); if (screen_is_shared(view)) | | | > > < < | | 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 | rc = formatln(&wcstr, &wstrlen, headln, view->ncols, 0); if (rc) goto end; werase(view->window); if (screen_is_shared(view)) wstandout(view->window); if (s->colour) c = get_colour(&s->colours, FNC_COMMIT_ID); if (c) wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); waddwstr(view->window, wcstr); if (c) wattr_off(view->window, COLOR_PAIR(c->scheme), NULL); while (wstrlen < view->ncols) { waddch(view->window, ' '); ++wstrlen; } if (screen_is_shared(view)) wstandend(view->window); fsl_free(wcstr); if (view->nlines <= 1) goto end; /* Parse commits to be written on screen for the longest username. */ entry = s->first_commit_onscreen; while (entry) { |
︙ | ︙ | |||
2555 2556 2557 2558 2559 2560 2561 | /* Trim time component from timestamp for the date field. */ date = fsl_strdup(commit->timestamp); while (!fsl_isspace(date[i++])) {} date[i] = '\0'; col_pos = MIN(view->ncols, ISO8601_DATE_ONLY + 1); if (s->colour) | | | | 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 | /* Trim time component from timestamp for the date field. */ date = fsl_strdup(commit->timestamp); while (!fsl_isspace(date[i++])) {} date[i] = '\0'; col_pos = MIN(view->ncols, ISO8601_DATE_ONLY + 1); if (s->colour) c = get_colour(&s->colours, FNC_DATE_STR); if (c) wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); waddnstr(view->window, date, col_pos); if (c) wattr_off(view->window, COLOR_PAIR(c->scheme), NULL); if (col_pos > view->ncols) goto end; /* If enough columns, write abbreviated commit hash. */ if (view->ncols >= 110) { if (s->colour) c = get_colour(&s->colours, FNC_COMMIT_ID); if (c) wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); wprintw(view->window, "%.9s ", commit->uuid); if (c) wattr_off(view->window, COLOR_PAIR(c->scheme), NULL); col_pos += 10; if (col_pos > view->ncols) |
︙ | ︙ | |||
2593 2594 2595 2596 2597 2598 2599 | if (strpbrk(user, "<@>") != NULL) parse_emailaddr_username(&user); rc = formatln(&usr_wcstr, &usrlen, user, view->ncols - col_pos, col_pos); if (rc) goto end; if (s->colour) | | | 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 | if (strpbrk(user, "<@>") != NULL) parse_emailaddr_username(&user); rc = formatln(&usr_wcstr, &usrlen, user, view->ncols - col_pos, col_pos); if (rc) goto end; if (s->colour) c = get_colour(&s->colours, FNC_USER_STR); if (c) wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); waddwstr(view->window, usr_wcstr); pad = fsl_mprintf("%*c", max_usrlen - usrlen + 2, ' '); waddstr(view->window, pad); if (c) wattr_off(view->window, COLOR_PAIR(c->scheme), NULL); |
︙ | ︙ | |||
2795 2796 2797 2798 2799 2800 2801 | {" q ", " ❬q❭ "}, {" Q ", " ❬Q❭ "}, {""}, {""}, /* Timeline */ {" <,, ", " ❬<❭❬,❭ "}, {" >,. ", " ❬>❭❬.❭ "}, {" Enter,Space ", " ❬Enter❭❬Space❭ "}, | < < < < < | 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 | {" q ", " ❬q❭ "}, {" Q ", " ❬Q❭ "}, {""}, {""}, /* Timeline */ {" <,, ", " ❬<❭❬,❭ "}, {" >,. ", " ❬>❭❬.❭ "}, {" Enter,Space ", " ❬Enter❭❬Space❭ "}, {" t ", " ❬t❭ "}, {""}, {""}, /* Diff */ {" Space ", " ❬Space❭ "}, {" i ", " ❬i❭ "}, {" v ", " ❬v❭ "}, {" w ", " ❬w❭ "}, {" -,_ ", " ❬-❭❬_❭ "}, {" +,= ", " ❬+❭❬=❭ "}, {" C-k,K,<,, ", " ❬C-k❭❬K❭❬<❭❬,❭ "}, {" C-j,J,>,. ", " ❬C-j❭❬J❭❬>❭❬.❭ "}, {""}, {""}, /* Tree */ {" l,Enter,<Right> ", " ❬→❭❬l❭❬Enter❭ "}, {" h,<BS>,<Left> ", " ❬←❭❬h❭❬⌫❭ "}, {" i ", " ❬i❭ "}, {" t ", " ❬t❭ "}, {""}, {""}, /* Blame */ {" Space ", " ❬Space❭ "}, {" Enter ", " ❬Enter❭ "}, {" b ", " ❬b❭ "}, {" p ", " ❬p❭ "}, {" B ", " ❬B❭ "}, {""}, {""}, /* Branch */ {" Enter,Space ", " ❬Enter❭❬Space❭ "}, {" d ", " ❬d❭ "}, {" i ", " ❬i❭ "}, {" t ", " ❬t❭ "}, {" R,<C-l> ", " ❬R❭❬C-l❭ "}, |
︙ | ︙ | |||
2847 2848 2849 2850 2851 2852 2853 | "Move selection cursor or page down one line", "Scroll up one page", "Scroll down one page", "Jump to first line or start of the view", "Jump to last line or end of the view", "Switch focus between open views", "Toggle coloured output", | | | < < < < | < | | 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 | "Move selection cursor or page down one line", "Scroll up one page", "Scroll down one page", "Jump to first line or start of the view", "Jump to last line or end of the view", "Switch focus between open views", "Toggle coloured output", "Toggle fullscreen", "Open prompt to enter search term (not available in this view)", "Find next line or token matching the current search term", "Find previous line or token matching the current search term", "Quit the active view", "Quit the program", "", "Timeline", "Move selection cursor up one commit", "Move selection cursor down one commit", "Open diff view of the selected commit", "Display a tree reflecting the state of the selected commit", "", "Diff", "Scroll down one page of diff output", "Toggle inversion of diff output", "Toggle verbosity of diff output", "Toggle ignore whitespace-only changes in diff", "Decrease the number of context lines", "Increase the number of context lines", "Display diff of next (newer) commit in the timeline", "Display diff of previous (older) commit in the timeline", "", "Tree", "Move into the selected directory", "Return to the parent directory", "Toggle display of file artifact SHA hashes", "Display timeline of all commits modifying the selected entry", "", "Blame", "Scroll down one page", "Display the diff of the commit corresponding to the selected line", "Blame the version of the file found in the selected line's commit", "Blame the version of the file found in the selected line's parent " "commit", "Reload the previous blamed version of the file", "", "Branch", "Display the timeline of the currently selected branch", "Toggle display of the date when the branch last received changes", "Toggle display of the SHA hash that identifies the branch", "Open a tree view of the currently selected branch", "Reload view with all repostory branches and no filters applied", "", " See fnc(1) for complete list of options and key bindings." }; int cs, ln, width = 0, rc = 0; cs = (strcmp(nl_langinfo(CODESET), "UTF-8") == 0) ? 1 : 0; |
︙ | ︙ | |||
3065 3066 3067 3068 3069 3070 3071 | refresh(); } static int tl_input_handler(struct fnc_view **new_view, struct fnc_view *view, int ch) { struct fnc_tl_view_state *s = &view->state.timeline; | < | | 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 | refresh(); } static int tl_input_handler(struct fnc_view **new_view, struct fnc_view *view, int ch) { struct fnc_tl_view_state *s = &view->state.timeline; struct fnc_view *diff_view = NULL, *tree_view = NULL; int rc = 0, start_col = 0; switch (ch) { case KEY_DOWN: case 'j': case '.': case '>': |
︙ | ︙ | |||
3174 3175 3176 3177 3178 3179 3180 | return rc; view->child = diff_view; diff_view->parent = view; view->focus_child = true; } else *new_view = diff_view; break; | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 | return rc; view->child = diff_view; diff_view->parent = view; view->focus_child = true; } else *new_view = diff_view; break; case 'c': s->colour = !s->colour; break; case 't': if (s->selected_commit == NULL) break; if (!fsl_rid_is_a_checkin(fcli_cx(), s->selected_commit->commit->rid)) { wattr_on(view->window, A_BOLD, NULL); mvwaddstr(view->window, |
︙ | ︙ | |||
3275 3276 3277 3278 3279 3280 3281 | } if (view_is_parent(view)) start_col = view_split_start_col(view->start_col); rc = browse_commit_tree(&tree_view, start_col, s->selected_commit, s->path); if (rc) break; | | | 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 | } if (view_is_parent(view)) start_col = view_split_start_col(view->start_col); rc = browse_commit_tree(&tree_view, start_col, s->selected_commit, s->path); if (rc) break; s->thread_cx.tree_open = true; view->active = false; tree_view->active = true; if (view_is_parent(view)) { rc = view_close_child(view); if (rc) return rc; view_set_child(view, tree_view); |
︙ | ︙ | |||
3610 3611 3612 3613 3614 3615 3616 | return false; } static int view_close(struct fnc_view *view) { | | > | | 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 | return false; } static int view_close(struct fnc_view *view) { int rc = 0; if (view->child) { view_close(view->child); view->child = NULL; } if (view->close) rc = view->close(view); if (view->panel) del_panel(view->panel); if (view->window) delwin(view->window); free(view); return rc; } static int close_timeline_view(struct fnc_view *view) { struct fnc_tl_view_state *s = &view->state.timeline; struct fsl_list_state st = { FNC_COLOUR_OBJ }; int rc = 0; rc = join_tl_thread(s); fsl_stmt_finalize(s->thread_cx.q); fnc_free_commits(&s->commits); fsl_list_clear(&s->colours, fsl_list_object_free, &st); regfree(&view->regex); fsl_free(s->path); s->path = NULL; return rc; } |
︙ | ︙ | |||
3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 | --commits->ncommits; } } static void fnc_commit_artifact_close(struct fnc_commit_artifact *commit) { if (commit->branch) fsl_free(commit->branch); if (commit->comment) fsl_free(commit->comment); if (commit->timestamp) fsl_free(commit->timestamp); if (commit->type) fsl_free(commit->type); if (commit->user) fsl_free(commit->user); fsl_free(commit->uuid); fsl_free(commit->puuid); | > > | | > > > > | | | | | | > > > > > > > > > > > > | 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 | --commits->ncommits; } } static void fnc_commit_artifact_close(struct fnc_commit_artifact *commit) { struct fsl_list_state st = { FNC_ARTIFACT_OBJ }; if (commit->branch) fsl_free(commit->branch); if (commit->comment) fsl_free(commit->comment); if (commit->timestamp) fsl_free(commit->timestamp); if (commit->type) fsl_free(commit->type); if (commit->user) fsl_free(commit->user); fsl_free(commit->uuid); fsl_free(commit->puuid); fsl_list_clear(&commit->changeset, fsl_list_object_free, &st); fsl_list_reserve(&commit->changeset, 0); fsl_free(commit); } static int fsl_list_object_free(void *elem, void *state) { struct fsl_list_state *st = state; switch (st->obj) { case FNC_ARTIFACT_OBJ: { struct fsl_file_artifact *ffa = elem; fsl_free(ffa->fc->name); fsl_free(ffa->fc->uuid); fsl_free(ffa->fc->priorName); fsl_free(ffa->fc); fsl_free(ffa); break; } case FNC_COLOUR_OBJ: { struct fnc_colour *c = elem; regfree(&c->regex); fsl_free(c); break; } default: return RC(FSL_RC_MISSING_INFO, "fsl_list_state.obj missing or invalid: %d", st->obj); } return 0; } static int init_diff_commit(struct fnc_view **new_view, int start_col, struct fnc_commit_artifact *commit, struct fnc_view *timeline_view) |
︙ | ︙ | |||
3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 | s->f = NULL; s->context = context; s->sbs = 0; verbosity ? s->diff_flags |= FSL_DIFF_VERBOSE : 0; ignore_ws ? s->diff_flags |= FSL_DIFF_IGNORE_ALLWS : 0; invert ? s->diff_flags |= FSL_DIFF_INVERT : 0; s->timeline_view = timeline_view; s->colour = !fnc_init.nocolour && has_colors(); s->showmeta = showmeta; | > | < | < < < < | > | > | 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 | s->f = NULL; s->context = context; s->sbs = 0; verbosity ? s->diff_flags |= FSL_DIFF_VERBOSE : 0; ignore_ws ? s->diff_flags |= FSL_DIFF_IGNORE_ALLWS : 0; invert ? s->diff_flags |= FSL_DIFF_INVERT : 0; s->timeline_view = timeline_view; s->colours = fsl_list_empty; s->colour = !fnc_init.nocolour && has_colors(); s->showmeta = showmeta; if (s->colour) set_colours(&s->colours, FNC_VIEW_DIFF); if (timeline_view && screen_is_split(view)) show_timeline_view(timeline_view); /* draw vborder */ show_diff_status(view); s->line_offsets = NULL; s->nlines = 0; s->ncols = view->ncols; rc = create_diff(s); if (rc) { if (s->colour) { struct fsl_list_state st = { FNC_COLOUR_OBJ }; fsl_list_clear(&s->colours, fsl_list_object_free, &st); } return rc; } view->show = show_diff; view->input = diff_input_handler; view->close = close_diff_view; view->search_init = diff_search_init; |
︙ | ︙ | |||
3941 3942 3943 3944 3945 3946 3947 | rc = RC(FSL_RC_IO, "%s", "fflush"); return rc; } static int create_changeset(struct fnc_commit_artifact *commit) { | | | 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 | rc = RC(FSL_RC_IO, "%s", "fflush"); return rc; } static int create_changeset(struct fnc_commit_artifact *commit) { fsl_cx *f = fcli_cx(); fsl_stmt *st = NULL; fsl_list changeset = fsl_list_empty; int rc = 0; st = fsl_stmt_malloc(); rc = fsl_cx_prepare(f, st, "SELECT name, mperm, " |
︙ | ︙ | |||
4186 4187 4188 4189 4190 4191 4192 | * hash (UUID) of each F card is the same, there are no changes; if different, * both artifacts will be passed to diff_file_artifact() to be diffed. */ static int diff_commit(fsl_buffer *buf, struct fnc_commit_artifact *commit, int diff_flags, int context, int sbs, struct fnc_pathlist_head *paths) { | | | 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 | * hash (UUID) of each F card is the same, there are no changes; if different, * both artifacts will be passed to diff_file_artifact() to be diffed. */ static int diff_commit(fsl_buffer *buf, struct fnc_commit_artifact *commit, int diff_flags, int context, int sbs, struct fnc_pathlist_head *paths) { fsl_cx *f = fcli_cx(); const fsl_card_F *fc1 = NULL; const fsl_card_F *fc2 = NULL; fsl_deck d1 = fsl_deck_empty; fsl_deck d2 = fsl_deck_empty; fsl_id_t id1; int different = 0, rc = 0; |
︙ | ︙ | |||
4304 4305 4306 4307 4308 4309 4310 | * nb. This routine is only called with 'fnc diff [hash]'; that is, one or * zero args—not two—supplied to fnc's diff command line interface. */ static int diff_checkout(fsl_buffer *buf, fsl_id_t vid, int diff_flags, int context, int sbs, struct fnc_pathlist_head *paths) { | | | 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 | * nb. This routine is only called with 'fnc diff [hash]'; that is, one or * zero args—not two—supplied to fnc's diff command line interface. */ static int diff_checkout(fsl_buffer *buf, fsl_id_t vid, int diff_flags, int context, int sbs, struct fnc_pathlist_head *paths) { fsl_cx *f = fcli_cx(); fsl_stmt *st = NULL; fsl_buffer sql, abspath, bminus; fsl_uuid_str xminus = NULL; fsl_id_t cid; int rc = 0; bool allow_symlinks; |
︙ | ︙ | |||
4579 4580 4581 4582 4583 4584 4585 | * diff_flags, context, and sbs are the same parameters as diff_file_artifact() */ static int diff_file(fsl_buffer *buf, fsl_buffer *bminus, const char *zminus, fsl_uuid_str xminus, const char *abspath, enum fsl_ckout_change_e change, int diff_flags, int context, bool sbs) { | | | 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 | * diff_flags, context, and sbs are the same parameters as diff_file_artifact() */ static int diff_file(fsl_buffer *buf, fsl_buffer *bminus, const char *zminus, fsl_uuid_str xminus, const char *abspath, enum fsl_ckout_change_e change, int diff_flags, int context, bool sbs) { fsl_cx *f = fcli_cx(); fsl_buffer bplus = fsl_buffer_empty; fsl_buffer xplus = fsl_buffer_empty; const char *zplus = NULL; int rc = 0; bool verbose; /* |
︙ | ︙ | |||
4675 4676 4677 4678 4679 4680 4681 | * of the corresponding artifact when selected from the timeline. * TODO: Rename this horrible function name. */ static int diff_non_checkin(fsl_buffer *buf, struct fnc_commit_artifact *commit, int diff_flags, int context, int sbs) { | | | 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 | * of the corresponding artifact when selected from the timeline. * TODO: Rename this horrible function name. */ static int diff_non_checkin(fsl_buffer *buf, struct fnc_commit_artifact *commit, int diff_flags, int context, int sbs) { fsl_cx *f = fcli_cx(); fsl_buffer wiki = fsl_buffer_empty; fsl_buffer pwiki = fsl_buffer_empty; fsl_id_t prid = 0; fsl_size_t idx; int rc = 0; fsl_deck *d = NULL; |
︙ | ︙ | |||
4745 4746 4747 4748 4749 4750 4751 | * entire wiki card content. */ fsl_buffer_append(&wiki, d->W.mem, d->W.used); if (commit->puuid == NULL) { if (d->P.used > 0) commit->puuid = fsl_strdup(d->P.list[0]); else { | | | 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 | * entire wiki card content. */ fsl_buffer_append(&wiki, d->W.mem, d->W.used); if (commit->puuid == NULL) { if (d->P.used > 0) commit->puuid = fsl_strdup(d->P.list[0]); else { fsl_buffer_copy(&wiki, buf); goto end; } } /* Diff the artifacts if a parent is found. */ if ((rc = fsl_sym_to_rid(f, commit->puuid, FSL_SATYPE_ANY, &prid))) goto end; |
︙ | ︙ | |||
4789 4790 4791 4792 4793 4794 4795 | * sbs number of columns in which to display each side-by-side diff */ static int diff_file_artifact(fsl_buffer *buf, fsl_id_t vid1, const fsl_card_F *a, fsl_id_t vid2, const fsl_card_F *b, enum fsl_ckout_change_e change, int diff_flags, int context, int sbs, enum fnc_diff_type diff_type) { | | | 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 | * sbs number of columns in which to display each side-by-side diff */ static int diff_file_artifact(fsl_buffer *buf, fsl_id_t vid1, const fsl_card_F *a, fsl_id_t vid2, const fsl_card_F *b, enum fsl_ckout_change_e change, int diff_flags, int context, int sbs, enum fnc_diff_type diff_type) { fsl_cx *f = fcli_cx(); fsl_stmt stmt = fsl_stmt_empty; fsl_buffer fbuf1 = fsl_buffer_empty; fsl_buffer fbuf2 = fsl_buffer_empty; char *zminus0 = NULL, *zplus0 = NULL; const char *zplus = NULL, *zminus = NULL; fsl_uuid_str xplus0 = NULL, xminus0 = NULL; fsl_uuid_str xplus = NULL, xminus = NULL; |
︙ | ︙ | |||
4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 | size_t linesz = 0; ssize_t linelen; off_t line_offset; int wstrlen; int max_lines = view->nlines; int nlines = s->nlines; int rc = 0, nprintln = 0; line_offset = s->line_offsets[s->first_line_onscreen - 1]; if (fseeko(s->f, line_offset, SEEK_SET)) return RC(fsl_errno_to_rc(errno, FSL_RC_ERROR), "%s", "fseeko"); werase(view->window); if (headln) { if ((line = fsl_mprintf("[%d/%d] %s", (s->first_line_onscreen - 1 + s->current_line), nlines, headln)) == NULL) return RC(FSL_RC_RANGE, "%s", "fsl_mprintf"); rc = formatln(&wcstr, &wstrlen, line, view->ncols, 0); fsl_free(line); fsl_free(headln); if (rc) return rc; if (screen_is_shared(view)) | > | < < < < > > | | 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 | size_t linesz = 0; ssize_t linelen; off_t line_offset; int wstrlen; int max_lines = view->nlines; int nlines = s->nlines; int rc = 0, nprintln = 0; int match = -1; line_offset = s->line_offsets[s->first_line_onscreen - 1]; if (fseeko(s->f, line_offset, SEEK_SET)) return RC(fsl_errno_to_rc(errno, FSL_RC_ERROR), "%s", "fseeko"); werase(view->window); if (headln) { if ((line = fsl_mprintf("[%d/%d] %s", (s->first_line_onscreen - 1 + s->current_line), nlines, headln)) == NULL) return RC(FSL_RC_RANGE, "%s", "fsl_mprintf"); rc = formatln(&wcstr, &wstrlen, line, view->ncols, 0); fsl_free(line); fsl_free(headln); if (rc) return rc; if (screen_is_shared(view)) wstandout(view->window); waddwstr(view->window, wcstr); fsl_free(wcstr); wcstr = NULL; if (screen_is_shared(view)) wstandend(view->window); if (wstrlen <= view->ncols - 1) waddch(view->window, '\n'); if (max_lines <= 1) return rc; --max_lines; } s->eof = false; |
︙ | ︙ | |||
4984 4985 4986 4987 4988 4989 4990 | } fsl_free(line); RC(ferror(s->f) ? fsl_errno_to_rc(errno, FSL_RC_IO) : FSL_RC_IO, "%s", "getline"); return rc; } | | > | | > > | 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 4884 4885 4886 4887 4888 4889 | } fsl_free(line); RC(ferror(s->f) ? fsl_errno_to_rc(errno, FSL_RC_IO) : FSL_RC_IO, "%s", "getline"); return rc; } if (s->colour && (match = fsl_list_index_of(&s->colours, line, match_line)) != -1) c = s->colours.list[match]; if (c) wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); if (s->first_line_onscreen + nprintln == s->matched_line && regmatch->rm_so >= 0 && regmatch->rm_so < regmatch->rm_eo) { rc = write_matched_line(&wstrlen, line, view->ncols, 0, view->window, regmatch); if (rc) { fsl_free(line); return rc; } } else { rc = formatln(&wcstr, &wstrlen, line, view->ncols, 0); if (rc) { fsl_free(line); return rc; } waddwstr(view->window, wcstr); fsl_free(wcstr); wcstr = NULL; } if (c) { wattr_off(view->window, COLOR_PAIR(c->scheme), NULL); c = NULL; } if (wstrlen <= view->ncols - 1) waddch(view->window, '\n'); ++nprintln; } fsl_free(line); if (nprintln >= 1) s->last_line_onscreen = s->first_line_onscreen + (nprintln - 1); |
︙ | ︙ | |||
5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 | wstandout(view->window); waddstr(view->window, "(END)"); wstandend(view->window); } return rc; } static bool screen_is_shared(struct fnc_view *view) { if (view_is_parent(view)) { if (view->child == NULL || view->child->active || !screen_is_split(view->child)) | > > > > > > > > > | 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 | wstandout(view->window); waddstr(view->window, "(END)"); wstandend(view->window); } return rc; } static int match_line(const void *ln, const void *key) { struct fnc_colour *c = (struct fnc_colour *)key; const char *line = ln; return regexec(&c->regex, line, 0, NULL, 0); } static bool screen_is_shared(struct fnc_view *view) { if (view_is_parent(view)) { if (view->child == NULL || view->child->active || !screen_is_split(view->child)) |
︙ | ︙ | |||
5148 5149 5150 5151 5152 5153 5154 | wnoutrefresh(view->window); #endif } static int diff_input_handler(struct fnc_view **new_view, struct fnc_view *view, int ch) { | < | 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 | wnoutrefresh(view->window); #endif } static int diff_input_handler(struct fnc_view **new_view, struct fnc_view *view, int ch) { struct fnc_diff_view_state *s = &view->state.diff; struct fnc_tl_view_state *tlstate; struct commit_entry *previous_selection; char *line = NULL; ssize_t linelen; size_t linesz = 0; int i, rc = 0; |
︙ | ︙ | |||
5212 5213 5214 5215 5216 5217 5218 | case 'g': if (!fnc_home(view)) break; /* FALL THROUGH */ case KEY_HOME: s->first_line_onscreen = 1; break; | < < < < < < < < < < < < < < < < < < < < < < < < < < | 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 | case 'g': if (!fnc_home(view)) break; /* FALL THROUGH */ case KEY_HOME: s->first_line_onscreen = 1; break; case 'c': case 'i': case 'v': case 'w': if (ch == 'c') s->colour = !s->colour; if (ch == 'i') |
︙ | ︙ | |||
5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 | return 0; } static int close_diff_view(struct fnc_view *view) { struct fnc_diff_view_state *s = &view->state.diff; int rc = 0; if (s->f && fclose(s->f) == EOF) rc = RC(fsl_errno_to_rc(errno, FSL_RC_IO), "%s", "fclose"); fsl_free(s->id1); s->id1 = NULL; fsl_free(s->id2); s->id2 = NULL; fsl_free(s->line_offsets); | > | | 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 | return 0; } static int close_diff_view(struct fnc_view *view) { struct fnc_diff_view_state *s = &view->state.diff; struct fsl_list_state st = { FNC_COLOUR_OBJ }; int rc = 0; if (s->f && fclose(s->f) == EOF) rc = RC(fsl_errno_to_rc(errno, FSL_RC_IO), "%s", "fclose"); fsl_free(s->id1); s->id1 = NULL; fsl_free(s->id2); s->id2 = NULL; fsl_free(s->line_offsets); fsl_list_clear(&s->colours, fsl_list_object_free, &st); s->line_offsets = NULL; s->nlines = 0; return rc; } static void fnc_resizeterm(void) |
︙ | ︙ | |||
5487 5488 5489 5490 5491 5492 5493 | } return 0; } /* * Consume repeatable arguments containing artifact type values used in | | | | | | < | < < < < < < | | | > | 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 | } return 0; } /* * Consume repeatable arguments containing artifact type values used in * constructing the SQL query to generate commit records of the specified * type for the timeline. TODO: Enhance this to generalise processing of * various repeatable arguments--paths, usernames, branches, etc.--so we * can filter on multiples of these values. */ static int fcli_flag_type_arg_cb(fcli_cliflag const *v) { if (fnc_init.filter_types->nitems) fnc_init.filter_types->values = fsl_realloc(fnc_init.filter_types->values, (fnc_init.filter_types->nitems + 1) * sizeof(char *)); fnc_init.filter_types->values[fnc_init.filter_types->nitems++] = *((const char **)v->flagValue); return FCLI_RC_FLAG_AGAIN; } static void sigwinch_handler(int sig) { |
︙ | ︙ | |||
5556 5557 5558 5559 5560 5561 5562 | * fcli_help() after hijacking the process whenever the '--help' * argument is passsed on the command line, so we can't use the * f->output fsl_outputer implementation as we would like. */ /* fsl_cx *f = fcli_cx(); */ /* f->output = fsl_outputer_FILE; */ /* f->output.state.state = (fnc_init.err == true) ? stderr : stdout; */ | | | 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 | * fcli_help() after hijacking the process whenever the '--help' * argument is passsed on the command line, so we can't use the * f->output fsl_outputer implementation as we would like. */ /* fsl_cx *f = fcli_cx(); */ /* f->output = fsl_outputer_FILE; */ /* f->output.state.state = (fnc_init.err == true) ? stderr : stdout; */ FILE *f = fnc_init.err ? stderr : stdout; size_t idx = 0; endwin(); /* If a command was passed on the CLI, output its corresponding help. */ if (fnc_init.cmdarg) for (idx = 0; idx < nitems(fnc_init.cmd_args); ++idx) { |
︙ | ︙ | |||
5586 5587 5588 5589 5590 5591 5592 | } static void usage_timeline(void) { fsl_fprintf(fnc_init.err ? stderr : stdout, " usage: %s timeline [-C|--no-colour] [-T tag] [-b branch] " | | | | 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 | } static void usage_timeline(void) { fsl_fprintf(fnc_init.err ? stderr : stdout, " usage: %s timeline [-C|--no-colour] [-T tag] [-b branch] " "[-c commit] [-h|--help] [-n n] [-t type] [-u user] [-z|--utc] " "[path]\n" " e.g.: %s timeline --type ci -u jimmy src/frobnitz.c\n\n", fcli_progname(), fcli_progname()); } static void usage_diff(void) { |
︙ | ︙ | |||
5633 5634 5635 5636 5637 5638 5639 | " usage: %s branch [-C|--no-colour] [-a|--after date] " "[-b|--before date] [-c|--closed] [-h|--help] [-o|--open] " "[-p|--no-private] [-r|--reverse] [-s|--sort order] [glob]\n" " e.g.: %s branch -b 2020-10-10\n\n" , fcli_progname(), fcli_progname()); } | < < < < < < < < < | | | 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 | " usage: %s branch [-C|--no-colour] [-a|--after date] " "[-b|--before date] [-c|--closed] [-h|--help] [-o|--open] " "[-p|--no-private] [-r|--reverse] [-s|--sort order] [glob]\n" " e.g.: %s branch -b 2020-10-10\n\n" , fcli_progname(), fcli_progname()); } static int cmd_diff(fcli_command const *argv) { fsl_cx *f = fcli_cx(); struct fnc_view *view; struct fnc_commit_artifact *commit = NULL; struct fnc_pathlist_head paths; struct fnc_pathlist_entry *pe; fsl_deck d = fsl_deck_empty; fsl_stmt *q = NULL; const char *artifact1 = NULL, *artifact2 = NULL; char *path0 = NULL; fsl_id_t prid = -1, rid = -1; int context = DIFF_DEF_CTXT, rc = 0; unsigned short blob = 0; enum fnc_diff_type diff_type; bool showmeta = false; rc = fcli_process_flags(argv->flags); if (rc || (rc = fcli_has_unused_flags(false))) return rc; TAILQ_INIT(&paths); |
︙ | ︙ | |||
5680 5681 5682 5683 5684 5685 5686 | * card in the checkin(s) deck(s). It's tricky, but provides a smart UI: * fnc diff f1 f2 ... -> diff f{1,2,...} on disk against current ckout * fnc diff sym3 f1 -> diff f1 on disk against f1 found in checkin sym3 * fnc diff sym1 sym2 f1 f2 -> diff f{1,2} between checkins sym1 & sym2 */ if (!fsl_sym_to_rid(f, fcli_next_arg(false), FSL_SATYPE_ANY, &prid)) { artifact1 = fcli_next_arg(true); | | > > > > > > > > | > > > > > > > > > | < < | 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 | * card in the checkin(s) deck(s). It's tricky, but provides a smart UI: * fnc diff f1 f2 ... -> diff f{1,2,...} on disk against current ckout * fnc diff sym3 f1 -> diff f1 on disk against f1 found in checkin sym3 * fnc diff sym1 sym2 f1 f2 -> diff f{1,2} between checkins sym1 & sym2 */ if (!fsl_sym_to_rid(f, fcli_next_arg(false), FSL_SATYPE_ANY, &prid)) { artifact1 = fcli_next_arg(true); if (!fsl_rid_is_a_checkin(f, prid)) { if (fsl_rid_to_artifact_uuid(f, prid, FSL_SATYPE_ANY) != NULL) { rc = RC(FSL_RC_TYPE, "artifact [%s] not resolvable to a checkin", artifact1); goto end; } ++blob; } if (!fsl_sym_to_rid(f, fcli_next_arg(false), FSL_SATYPE_ANY, &rid)) { artifact2 = fcli_next_arg(true); diff_type = FNC_DIFF_COMMIT; if (!fsl_rid_is_a_checkin(f, rid)) { if (fsl_rid_to_artifact_uuid(f, prid, FSL_SATYPE_ANY) != NULL) { rc = RC(FSL_RC_TYPE, "artifact [%s] " "not resolvable to a checkin", artifact2); goto end; } ++blob; } } } if (fcli_error()->code == FSL_RC_NOT_FOUND) { fcli_err_reset(); /* If args aren't symbols, treat as paths. */ rc = 0; } if (blob == 2) diff_type = FNC_DIFF_BLOB; if (!artifact1 && diff_type != FNC_DIFF_BLOB) { artifact1 = "current"; rc = fsl_sym_to_rid(f, artifact1, FSL_SATYPE_CHECKIN, &prid); if (rc || prid < 0) { rc = RC(rc, "%s", "fsl_sym_to_rid"); goto end; } } if (!artifact2 && diff_type != FNC_DIFF_BLOB) { diff_type = FNC_DIFF_CKOUT; fsl_ckout_version_info(f, &rid, NULL); if ((rc = fsl_ckout_changes_scan(f))) return RC(rc, "%s", "fsl_ckout_changes_scan"); if (!fsl_strcmp(artifact1, "current") && !fsl_ckout_has_changes(f)) { fsl_fprintf(stdout, "No local changes.\n"); return rc; } } while (fcli_next_arg(false) && diff_type != FNC_DIFF_BLOB) { struct fnc_pathlist_entry *ins; char *path, *path_to_diff; rc = map_repo_path(&path0); path = path0; while (path[0] == '/') ++path; if (rc) { if (rc != FSL_RC_NOT_FOUND || (!fsl_strcmp(artifact1, "current") && !artifact2)) goto end; rc = 0; fcli_err_reset(); /* Path may be valid in tree of specified commit(s). */ const fsl_card_F *cf = NULL; rc = fsl_deck_load_sym(f, &d, artifact1, FSL_SATYPE_CHECKIN); if (rc) |
︙ | ︙ | |||
5852 5853 5854 5855 5856 5857 5858 | return rc; } static int cmd_tree(fcli_command const *argv) { | | | 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 | return rc; } static int cmd_tree(fcli_command const *argv) { fsl_cx *f = fcli_cx(); struct fnc_view *view; char *path = NULL; fsl_id_t rid; int rc = 0; rc = fcli_process_flags(argv->flags); if (rc || (rc = fcli_has_unused_flags(false))) |
︙ | ︙ | |||
5923 5924 5925 5926 5927 5928 5929 | fsl_free(path); return rc; } static int open_tree_view(struct fnc_view *view, const char *path, fsl_id_t rid) { | | | 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 | fsl_free(path); return rc; } static int open_tree_view(struct fnc_view *view, const char *path, fsl_id_t rid) { fsl_cx *f = fcli_cx(); struct fnc_tree_view_state *s = &view->state.tree; int rc = 0; TAILQ_INIT(&s->parents); s->show_id = false; s->colour = !fnc_init.nocolour && has_colors(); s->rid = rid; |
︙ | ︙ | |||
5971 5972 5973 5974 5975 5976 5977 | rc = RC(FSL_RC_RANGE, "%s", "fsl_mprintf"); goto end; } s->first_entry_onscreen = &s->tree->entries[0]; s->selected_entry = &s->tree->entries[0]; | | < | < < < | 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 | rc = RC(FSL_RC_RANGE, "%s", "fsl_mprintf"); goto end; } s->first_entry_onscreen = &s->tree->entries[0]; s->selected_entry = &s->tree->entries[0]; if (s->colour) set_colours(&s->colours, FNC_VIEW_TREE); view->show = show_tree_view; view->input = tree_input_handler; view->close = close_tree_view; view->search_init = tree_search_init; view->search_next = tree_search_next; end: |
︙ | ︙ | |||
6073 6074 6075 6076 6077 6078 6079 | * tree, all displayed (sub)trees are derived. File paths are extracted from F * cards of the checkin identified by id referenced in the repo database by rid. */ static int create_repository_tree(struct fnc_repository_tree **repo, fsl_uuid_str *id, fsl_id_t rid) { | | | | 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 | * tree, all displayed (sub)trees are derived. File paths are extracted from F * cards of the checkin identified by id referenced in the repo database by rid. */ static int create_repository_tree(struct fnc_repository_tree **repo, fsl_uuid_str *id, fsl_id_t rid) { fsl_cx *f = fcli_cx(); struct fnc_repository_tree *ptr; fsl_deck d = fsl_deck_empty; const fsl_card_F *cf = NULL; int rc = 0; ptr = fsl_malloc(sizeof(struct fnc_repository_tree)); if (ptr == NULL) return RC(FSL_RC_ERROR, "%s", "fsl_malloc"); memset(ptr, 0, sizeof(struct fnc_repository_tree)); rc = fsl_deck_load_rid(fcli_cx(), &d, rid, FSL_SATYPE_CHECKIN); if (rc) return RC(rc, "fsl_deck_load_rid(%d) [%s]", rid, id); rc = fsl_deck_F_rewind(&d); if (rc) goto end;; rc = fsl_deck_F_next(&d, &cf); if (rc) |
︙ | ︙ | |||
6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 | * mtime Modification time of the file * Returns 0 on success, non-zero on error. */ static int link_tree_node(struct fnc_repository_tree *tree, const char *path, const char *uuid, fsl_time_t mtime) { struct fnc_repo_tree_node *parent_dir; fsl_buffer buf = fsl_buffer_empty; struct stat s; int i, rc = 0; parent_dir = tree->tail; while (parent_dir != 0 && (strncmp(parent_dir->path, path, parent_dir->pathlen) != 0 || | > | | 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 | * mtime Modification time of the file * Returns 0 on success, non-zero on error. */ static int link_tree_node(struct fnc_repository_tree *tree, const char *path, const char *uuid, fsl_time_t mtime) { fsl_cx *f = fcli_cx(); struct fnc_repo_tree_node *parent_dir; fsl_buffer buf = fsl_buffer_empty; struct stat s; int i, rc = 0; parent_dir = tree->tail; while (parent_dir != 0 && (strncmp(parent_dir->path, path, parent_dir->pathlen) != 0 || path[parent_dir->pathlen] != '/')) parent_dir = parent_dir->parent_dir; i = parent_dir ? parent_dir->pathlen + 1 : 0; while (path[i]) { struct fnc_repo_tree_node *tn; int nodesz, slash = i; |
︙ | ︙ | |||
6297 6298 6299 6300 6301 6302 6303 | tn->mtime = mtime; while (path[i] == '/') /* Consume slashes. */ ++i; parent_dir = tn; /* Stat path for tree display features. */ | | | | 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 | tn->mtime = mtime; while (path[i] == '/') /* Consume slashes. */ ++i; parent_dir = tn; /* Stat path for tree display features. */ rc = fsl_file_canonical_name2(f->ckout.dir, tn->path, &buf, false); if (rc) goto end; if (lstat(fsl_buffer_cstr(&buf), &s) == -1) { if (errno == ENOENT) tn->mode = (!fsl_strcmp(tn->path, path) && tn->uuid) ? S_IFREG : S_IFDIR; else { |
︙ | ︙ | |||
6421 6422 6423 6424 6425 6426 6427 | static int draw_tree(struct fnc_view *view, const char *treepath) { struct fnc_tree_view_state *s = &view->state.tree; struct fnc_tree_entry *te; struct fnc_colour *c = NULL; wchar_t *wcstr; | | | | < < < < | > > | 6269 6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 6311 | static int draw_tree(struct fnc_view *view, const char *treepath) { struct fnc_tree_view_state *s = &view->state.tree; struct fnc_tree_entry *te; struct fnc_colour *c = NULL; wchar_t *wcstr; int match = -1, rc = 0; int wstrlen, n, idx, nentries; int limit = view->nlines; uint_fast8_t hashlen = FSL_UUID_STRLEN_MIN; s->ndisplayed = 0; werase(view->window); if (limit == 0) return rc; /* Write (highlighted) headline (if view is active in splitscreen). */ rc = formatln(&wcstr, &wstrlen, s->tree_label, view->ncols, 0); if (rc) return rc; if (screen_is_shared(view)) wstandout(view->window); if (s->colour) c = get_colour(&s->colours, FNC_COMMIT_ID); if (c) wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); waddwstr(view->window, wcstr); if (c) wattr_off(view->window, COLOR_PAIR(c->scheme), NULL); if (screen_is_shared(view)) wstandend(view->window); fsl_free(wcstr); wcstr = NULL; if (wstrlen < view->ncols - 1) waddch(view->window, '\n'); if (--limit <= 0) return rc; /* Write this (sub)tree's absolute repository path subheader. */ rc = formatln(&wcstr, &wstrlen, treepath, view->ncols, 0); if (rc) return rc; |
︙ | ︙ | |||
6560 6561 6562 6563 6564 6565 6566 | break; } if (n == s->selected_idx) { if (view->active) wattr_on(view->window, A_REVERSE, NULL); s->selected_entry = te; } | | > | > > | 6406 6407 6408 6409 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 | break; } if (n == s->selected_idx) { if (view->active) wattr_on(view->window, A_REVERSE, NULL); s->selected_entry = te; } if (s->colour && (match = fsl_list_index_of(&s->colours, line, match_line)) != -1) c = s->colours.list[match]; else c = NULL; if (c) wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); waddwstr(view->window, wcstr); if (c) wattr_off(view->window, COLOR_PAIR(c->scheme), NULL); if (wstrlen < view->ncols - 1) waddch(view->window, '\n'); |
︙ | ︙ | |||
6645 6646 6647 6648 6649 6650 6651 | /* *targetlnk = fsl_strdup(fsl_buffer_str(&blob)); */ /* fsl_buffer_clear(&blob); */ } static int tree_input_handler(struct fnc_view **new_view, struct fnc_view *view, int ch) { | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 6494 6495 6496 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 | /* *targetlnk = fsl_strdup(fsl_buffer_str(&blob)); */ /* fsl_buffer_clear(&blob); */ } static int tree_input_handler(struct fnc_view **new_view, struct fnc_view *view, int ch) { struct fnc_view *timeline_view/*, *branch_view */; struct fnc_tree_view_state *s = &view->state.tree; struct fnc_tree_entry *te; int n, start_col = 0, rc = 0; switch (ch) { case 'c': s->colour = !s->colour; break; case 'i': s->show_id = !s->show_id; break; case 't': if (!s->selected_entry) break; if (view_is_parent(view)) start_col = view_split_start_col(view->start_col); rc = timeline_tree_entry(&timeline_view, start_col, s); view->active = false; timeline_view->active = true; if (view_is_parent(view)) { rc = view_close_child(view); if (rc) return rc; view_set_child(view, timeline_view); |
︙ | ︙ | |||
6866 6867 6868 6869 6870 6871 6872 | return RC(FSL_RC_ERROR, "%s", "view_open"); /* Construct repository relative path for timeline query. */ rc = tree_entry_path(&path, &s->parents, s->selected_entry); if (rc) return rc; | | | > > | 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 6703 | return RC(FSL_RC_ERROR, "%s", "view_open"); /* Construct repository relative path for timeline query. */ rc = tree_entry_path(&path, &s->parents, s->selected_entry); if (rc) return rc; rc = open_timeline_view(timeline_view, s->rid, path); if (rc) view_close(timeline_view); else *new_view = timeline_view; fsl_free(path); return rc; } static void |
︙ | ︙ | |||
7086 7087 7088 7089 7090 7091 7092 7093 | return NULL; } static int close_tree_view(struct fnc_view *view) { struct fnc_tree_view_state *s = &view->state.tree; | > | | 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 | return NULL; } static int close_tree_view(struct fnc_view *view) { struct fnc_tree_view_state *s = &view->state.tree; struct fsl_list_state st = { FNC_COLOUR_OBJ }; fsl_list_clear(&s->colours, fsl_list_object_free, &st); fsl_free(s->tree_label); s->tree_label = NULL; fsl_free(s->commit_id); s->commit_id = NULL; while (!TAILQ_EMPTY(&s->parents)) { |
︙ | ︙ | |||
7143 7144 7145 7146 7147 7148 7149 | next = tn->next; fsl_free(tn); tn = next; } fsl_free(repo); } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 | next = tn->next; fsl_free(tn); tn = next; } fsl_free(repo); } static int view_close_child(struct fnc_view *view) { int rc = 0; if (view->child == NULL) return rc; |
︙ | ︙ | |||
7284 7285 7286 7287 7288 7289 7290 | view_set_child(struct fnc_view *view, struct fnc_view *child) { view->child = child; child->parent = view; } static int | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | < < < < < < < < < < < < < | | < | < < < < < < < < | > > < < < < | < < < | | > < | < < < < < | < < < | < < | < < < < < < < < < < < < | < < < < < < < < < < | < | < < < | < | < < | < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < | < | < < < < < < < | | < < < < < < | < < | | < < < < < | > | 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 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 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 | view_set_child(struct fnc_view *view, struct fnc_view *child) { view->child = child; child->parent = view; } static int set_colours(fsl_list *s, enum fnc_view_id vid) { struct fnc_colour *colour; const char **regexp = NULL; const char *regexp_blame[] = {"^"}; const char *regexp_timeline[] = {"^$", "^$", "^$"}; const char *regexp_tree[] = {"@$", "/$", "\\*$", "^$"}; const char *regexp_diff[] = { "^((checkin|wiki|ticket|technote) " "[0-9a-f]|hash [+-] |\\[[+~>-]] |" "[+-]{3} )", "^user:", "^date:", "^tags:", "^-", "^\\+", "^@@" }; int pairs_diff[][2] = { {FNC_DIFF_META, COLOR_GREEN}, {FNC_USER_STR, COLOR_CYAN}, {FNC_DATE_STR, COLOR_YELLOW}, {FNC_TAGS_STR, COLOR_MAGENTA}, {FNC_DIFF_MINUS, COLOR_MAGENTA}, {FNC_DIFF_PLUS, COLOR_CYAN}, {FNC_DIFF_CHNK, COLOR_YELLOW} }; int pairs_tree[][2] = { {FNC_TREE_LINK, COLOR_MAGENTA}, {FNC_TREE_DIR, COLOR_CYAN}, {FNC_TREE_EXEC, COLOR_GREEN}, {FNC_COMMIT_ID, COLOR_GREEN} }; int pairs_timeline[][2] = { {FNC_COMMIT_ID, COLOR_GREEN}, {FNC_USER_STR, COLOR_CYAN}, {FNC_DATE_STR, COLOR_YELLOW} }; int pairs_blame[][2] = { {FNC_COMMIT_ID, COLOR_GREEN} }; int (*pairs)[2], rc = 0; fsl_size_t idx, n; switch (vid) { case FNC_VIEW_DIFF: n = nitems(regexp_diff); regexp = regexp_diff; pairs = pairs_diff; break; case FNC_VIEW_TREE: n = nitems(regexp_tree); regexp = regexp_tree; pairs = pairs_tree; break; case FNC_VIEW_TIMELINE: n = nitems(regexp_timeline); regexp = regexp_timeline; pairs = pairs_timeline; break; case FNC_VIEW_BLAME: n = nitems(regexp_blame); regexp = regexp_blame; pairs = pairs_blame; break; default: return RC(FSL_RC_TYPE, "%s", "invalid fnc_view_id"); } for (idx = 0; idx < n; ++idx) { colour = fsl_malloc(sizeof(*colour)); if (colour == NULL) return RC(fsl_errno_to_rc(errno, FSL_RC_ERROR), "%s", "fsl_malloc"); rc = regcomp(&colour->regex, regexp[idx], REG_EXTENDED | REG_NEWLINE | REG_NOSUB); if (rc) { static char regerr[512]; regerror(rc, &colour->regex, regerr, sizeof(regerr)); fsl_free(colour); return RC(FSL_RC_ERROR, "regcomp(%s) -> %s", regexp[idx], regerr); } colour->scheme = pairs[idx][0]; init_pair(colour->scheme, pairs[idx][1], -1); fsl_list_append(s, colour); } return rc; } struct fnc_colour * get_colour(fsl_list *colours, int scheme) { struct fnc_colour cs; int match = -1; cs.scheme = scheme; match = fsl_list_index_of(colours, &cs, match_colour); if (match != -1) return colours->list[match]; return NULL; } static int match_colour(const void *target, const void *key) { struct fnc_colour *c = (struct fnc_colour *)key; struct fnc_colour *t = (struct fnc_colour *)target; return (c->scheme == t->scheme) ? 0 : 1; } /* * Emulate vim(1) gg: User has 1 sec to follow first 'g' keypress with another. */ static bool fnc_home(struct fnc_view *view) |
︙ | ︙ | |||
7594 7595 7596 7597 7598 7599 7600 | return home; } static int cmd_blame(fcli_command const *argv) { | | | 7119 7120 7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 | return home; } static int cmd_blame(fcli_command const *argv) { fsl_cx *f = fcli_cx(); struct fnc_view *view; char *path = NULL; fsl_uuid_str commit_id = NULL; fsl_id_t tip = 0, rid = 0; int nlimit = 0, rc = 0; rc = fcli_process_flags(argv->flags); |
︙ | ︙ | |||
7708 7709 7710 7711 7712 7713 7714 | s->commit_id = commit_id; s->blame.origin = tip; s->blame.nlimit = nlimit; s->spin_idx = 0; s->colour = !fnc_init.nocolour && has_colors(); if (s->colour) { | < | | 7233 7234 7235 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 | s->commit_id = commit_id; s->blame.origin = tip; s->blame.nlimit = nlimit; s->spin_idx = 0; s->colour = !fnc_init.nocolour && has_colors(); if (s->colour) { rc = set_colours(&s->colours, FNC_VIEW_BLAME); if (rc) return rc; } view->show = show_blame_view; view->input = blame_input_handler; view->close = close_blame_view; view->search_init = blame_search_init; view->search_next = blame_search_next; return run_blame(view); } static int run_blame(struct fnc_view *view) { fsl_cx *f = fcli_cx(); struct fnc_blame_view_state *s = &view->state.blame; struct fnc_blame *blame = &s->blame; fsl_deck d = fsl_deck_empty; fsl_buffer buf = fsl_buffer_empty; fsl_annotate_opt *opt = NULL; const fsl_card_F *cf; char *filepath = NULL; |
︙ | ︙ | |||
7980 7981 7982 7983 7984 7985 7986 | return rc; } static void * blame_thread(void *state) { | < | | | 7504 7505 7506 7507 7508 7509 7510 7511 7512 7513 7514 7515 7516 7517 7518 7519 7520 7521 7522 7523 7524 7525 7526 | return rc; } static void * blame_thread(void *state) { struct fnc_blame_thread_cx *cx = state; int rc0, rc; rc = block_main_thread_signals(); if (rc) return (void *)(intptr_t)rc; rc = fsl_annotate(fcli_cx(), &cx->blame_opt); if (rc && fsl_cx_err_get_e(fcli_cx())->code == FSL_RC_BREAK) { fcli_err_reset(); rc = 0; } rc0 = pthread_mutex_lock(&fnc_mutex); if (rc0) return (void *)(intptr_t)RC(fsl_errno_to_rc(rc0, FSL_RC_ACCESS), |
︙ | ︙ | |||
8093 8094 8095 8096 8097 8098 8099 | rc = formatln(&wcstr, &width, line, view->ncols, 0); fsl_free(line); line = NULL; if (rc) return rc; if (screen_is_shared(view)) | | | < < < < | | 7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626 7627 7628 7629 7630 7631 7632 7633 7634 7635 7636 7637 7638 7639 | rc = formatln(&wcstr, &width, line, view->ncols, 0); fsl_free(line); line = NULL; if (rc) return rc; if (screen_is_shared(view)) wstandout(view->window); if (s->colour) c = get_colour(&s->colours, FNC_COMMIT_ID); if (c) wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); waddwstr(view->window, wcstr); if (c) wattr_off(view->window, COLOR_PAIR(c->scheme), NULL); if (screen_is_shared(view)) wstandend(view->window); fsl_free(wcstr); wcstr = NULL; if (width < view->ncols - 1) waddch(view->window, '\n'); line = fsl_mprintf("[%d/%d] %s%s%s %c", s->first_line_onscreen - 1 + s->selected_line, blame->nlines, |
︙ | ︙ | |||
8167 8168 8169 8170 8171 8172 8173 | if (id_str == NULL) { fsl_free(line); return RC(FSL_RC_ERROR, "%s", "fsl_strdup"); } if (s->colour) c = get_colour(&s->colours, | | | 7686 7687 7688 7689 7690 7691 7692 7693 7694 7695 7696 7697 7698 7699 7700 | if (id_str == NULL) { fsl_free(line); return RC(FSL_RC_ERROR, "%s", "fsl_strdup"); } if (s->colour) c = get_colour(&s->colours, FNC_COMMIT_ID); if (c) wattr_on(view->window, COLOR_PAIR(c->scheme), NULL); wprintw(view->window, "%.*s", idfield - 1, id_str); if (c) wattr_off(view->window, |
︙ | ︙ | |||
8236 8237 8238 8239 8240 8241 8242 | return rc; } static int blame_input_handler(struct fnc_view **new_view, struct fnc_view *view, int ch) { | | | 7755 7756 7757 7758 7759 7760 7761 7762 7763 7764 7765 7766 7767 7768 7769 | return rc; } static int blame_input_handler(struct fnc_view **new_view, struct fnc_view *view, int ch) { struct fnc_view *diff_view; struct fnc_blame_view_state *s = &view->state.blame; int start_col = 0, rc = 0; switch (ch) { case 'q': s->done = true; if (s->selected_commit) |
︙ | ︙ | |||
8302 8303 8304 8305 8306 8307 8308 | case 'p': { fsl_uuid_cstr id = NULL; id = get_selected_commit_id(s->blame.lines, s->blame.nlines, s->first_line_onscreen, s->selected_line); if (id == NULL) break; if (ch == 'p') { | | | 7821 7822 7823 7824 7825 7826 7827 7828 7829 7830 7831 7832 7833 7834 7835 | case 'p': { fsl_uuid_cstr id = NULL; id = get_selected_commit_id(s->blame.lines, s->blame.nlines, s->first_line_onscreen, s->selected_line); if (id == NULL) break; if (ch == 'p') { fsl_cx *f = fcli_cx(); fsl_db *db = fsl_needs_repo(f); fsl_deck d = fsl_deck_empty; fsl_id_t rid = fsl_uuid_to_rid(f, id); fsl_uuid_str pid = fsl_db_g_text(db, NULL, "SELECT uuid FROM plink, blob WHERE plink.cid=%d " "AND blob.rid=plink.pid AND plink.isprim", rid); if (pid == NULL) |
︙ | ︙ | |||
8385 8386 8387 8388 8389 8390 8391 | fnc_commit_qid_free(s->blamed_commit); s->blamed_commit = CONCAT(STAILQ, _FIRST)(&s->blamed_commits); rc = run_blame(view); if (rc) break; break; } | < < < < < < < < < < < < < < < < < < < < < < < < | | 7904 7905 7906 7907 7908 7909 7910 7911 7912 7913 7914 7915 7916 7917 7918 7919 7920 | fnc_commit_qid_free(s->blamed_commit); s->blamed_commit = CONCAT(STAILQ, _FIRST)(&s->blamed_commits); rc = run_blame(view); if (rc) break; break; } case KEY_ENTER: case '\r': { fsl_cx *f = fcli_cx(); struct fnc_commit_artifact *commit = NULL; fsl_stmt *q = NULL; fsl_uuid_cstr id = NULL; id = get_selected_commit_id(s->blame.lines, s->blame.nlines, s->first_line_onscreen, s->selected_line); if (id == NULL) |
︙ | ︙ | |||
8607 8608 8609 8610 8611 8612 8613 8614 8615 8616 8617 8618 8619 8620 8621 8622 8623 8624 8625 | return rc; } static int close_blame_view(struct fnc_view *view) { struct fnc_blame_view_state *s = &view->state.blame; int rc = 0; rc = stop_blame(&s->blame); while (!CONCAT(STAILQ, _EMPTY)(&s->blamed_commits)) { struct fnc_commit_qid *blamed_commit; blamed_commit = CONCAT(STAILQ, _FIRST)(&s->blamed_commits); CONCAT(STAILQ, _REMOVE_HEAD)(&s->blamed_commits, entry); fnc_commit_qid_free(blamed_commit); } fsl_free(s->path); | > | | 8102 8103 8104 8105 8106 8107 8108 8109 8110 8111 8112 8113 8114 8115 8116 8117 8118 8119 8120 8121 8122 8123 8124 8125 8126 8127 8128 8129 | return rc; } static int close_blame_view(struct fnc_view *view) { struct fnc_blame_view_state *s = &view->state.blame; struct fsl_list_state st = { FNC_COLOUR_OBJ }; int rc = 0; rc = stop_blame(&s->blame); while (!CONCAT(STAILQ, _EMPTY)(&s->blamed_commits)) { struct fnc_commit_qid *blamed_commit; blamed_commit = CONCAT(STAILQ, _FIRST)(&s->blamed_commits); CONCAT(STAILQ, _REMOVE_HEAD)(&s->blamed_commits, entry); fnc_commit_qid_free(blamed_commit); } fsl_free(s->path); fsl_list_clear(&s->colours, fsl_list_object_free, &st); return rc; } static int stop_blame(struct fnc_blame *blame) { |
︙ | ︙ | |||
8762 8763 8764 8765 8766 8767 8768 | rc = open_branch_view(view, branch_flags, glob, dateline, when); if (rc) goto end; rc = view_loop(view); end: | < | | 8258 8259 8260 8261 8262 8263 8264 8265 8266 8267 8268 8269 8270 8271 8272 | rc = open_branch_view(view, branch_flags, glob, dateline, when); if (rc) goto end; rc = view_loop(view); end: fnc_free_branches(&view->state.branch); fsl_free(glob); return rc; } static int open_branch_view(struct fnc_view *view, int branch_flags, const char *glob, double dateline, int when) |
︙ | ︙ | |||
8786 8787 8788 8789 8790 8791 8792 | s->dateline = dateline; s->when = when; rc = fnc_load_branches(s); if (rc) return rc; | | | < < | | 8281 8282 8283 8284 8285 8286 8287 8288 8289 8290 8291 8292 8293 8294 8295 8296 8297 8298 8299 8300 8301 8302 8303 8304 | s->dateline = dateline; s->when = when; rc = fnc_load_branches(s); if (rc) return rc; if (s->colour) init_pair(FNC_COMMIT_ID, COLOR_GREEN, -1); view->show = show_branch_view; view->input = branch_input_handler; view->close = close_branch_view; view->search_init = branch_search_init; view->search_next = branch_search_next; return 0; } static int fnc_load_branches(struct fnc_branch_view_state *s) { fsl_cx *const f = fcli_cx(); fsl_buffer sql = fsl_buffer_empty; |
︙ | ︙ | |||
8841 8842 8843 8844 8845 8846 8847 | if (rc) goto end; if (s->branch_glob) { char *like = fsl_mprintf("%%%%%s%%%%", s->branch_glob); rc = fsl_buffer_appendf(&sql, " AND name LIKE %Q", like); fsl_free(like); | < < < | < < < < > | < < | < < | | < < | | < < | < < | 8334 8335 8336 8337 8338 8339 8340 8341 8342 8343 8344 8345 8346 8347 8348 8349 8350 8351 8352 8353 8354 8355 8356 8357 8358 8359 8360 8361 8362 8363 8364 8365 8366 8367 8368 8369 8370 8371 8372 8373 8374 8375 8376 8377 8378 8379 8380 8381 8382 8383 8384 | if (rc) goto end; if (s->branch_glob) { char *like = fsl_mprintf("%%%%%s%%%%", s->branch_glob); rc = fsl_buffer_appendf(&sql, " AND name LIKE %Q", like); fsl_free(like); } if (FLAG_CHK(s->branch_flags, BRANCH_LS_NO_PRIVATE)) rc = fsl_buffer_append(&sql, " AND NOT isprivate", -1); if (FLAG_CHK(s->branch_flags, BRANCH_SORT_MTIME)) rc = fsl_buffer_append(&sql, " ORDER BY -mtime", -1); else if (FLAG_CHK(s->branch_flags, BRANCH_SORT_STATUS)) rc = fsl_buffer_append(&sql, " ORDER BY isclosed", -1); else rc = fsl_buffer_append(&sql, " ORDER BY name COLLATE nocase", -1); if (!rc && (FLAG_CHK(s->branch_flags, BRANCH_SORT_REVERSE))) rc = fsl_buffer_append(&sql," DESC", -1); stmt = fsl_stmt_malloc(); if (stmt == NULL) goto end; if (!rc) rc = fsl_cx_prepare(f, stmt, fsl_buffer_cstr(&sql)); fsl_ckout_version_info(f, &ckoutrid, NULL); curr_branch = fsl_db_g_text(fsl_needs_repo(f), NULL, "SELECT value FROM tagxref WHERE rid=%d AND tagid=%d", ckoutrid, 8); while (fsl_stmt_step(stmt) == FSL_RC_STEP_ROW) { struct fnc_branch *new_branch; struct fnc_branchlist_entry *be; const char *brname = fsl_stmt_g_text(stmt, 0, NULL); bool private = (curr_branch && fsl_stmt_g_int32(stmt, 1) == 1); bool open = fsl_stmt_g_int32(stmt, 2) == 0; double mtime = fsl_stmt_g_int64(stmt, 3); bool curr = curr_branch && !fsl_strcmp(curr_branch, brname); if ((s->when > 0 && mtime < s->dateline) || (s->when < 0 && mtime > s->dateline)) continue; alloc_branch(&new_branch, brname, mtime, open, private, curr); fnc_branchlist_insert(&be, &s->branches, new_branch); if (be) be->idx = s->nbranches++; } s->first_branch_onscreen = TAILQ_FIRST(&s->branches); if (!stmt->rowCount) rc = RC(FSL_RC_BREAK, "no matching records: %s", |
︙ | ︙ | |||
9031 9032 9033 9034 9035 9036 9037 | } static int show_branch_view(struct fnc_view *view) { struct fnc_branch_view_state *s = &view->state.branch; struct fnc_branchlist_entry *be; | < | 8508 8509 8510 8511 8512 8513 8514 8515 8516 8517 8518 8519 8520 8521 | } static int show_branch_view(struct fnc_view *view) { struct fnc_branch_view_state *s = &view->state.branch; struct fnc_branchlist_entry *be; char *line = NULL; wchar_t *wline; int limit, n, width, rc = 0; werase(view->window); s->ndisplayed = 0; |
︙ | ︙ | |||
9055 9056 9057 9058 9059 9060 9061 | rc = formatln(&wline, &width, line, view->ncols, 0); if (rc) { fsl_free(line); return rc; } if (screen_is_shared(view)) | | < < < < | | 8531 8532 8533 8534 8535 8536 8537 8538 8539 8540 8541 8542 8543 8544 8545 8546 8547 8548 | rc = formatln(&wline, &width, line, view->ncols, 0); if (rc) { fsl_free(line); return rc; } if (screen_is_shared(view)) wstandout(view->window); waddwstr(view->window, wline); if (screen_is_shared(view)) wstandend(view->window); fsl_free(wline); wline = NULL; fsl_free(line); line = NULL; if (width < view->ncols - 1) waddch(view->window, '\n'); if (--limit <= 0) |
︙ | ︙ | |||
9086 9087 9088 9089 9090 9091 9092 | be->branch->current ? "@" : "", s->show_id ? " [" : "", s->show_id ? be->branch->id : "", s->show_id ? "]" : "", s->show_date ? ':' : 0, s->show_date ? be->branch->date : 0); if (line == NULL) return RC(FSL_RC_ERROR, "%s", "fsl_mprintf"); | < < < | | | | | 8558 8559 8560 8561 8562 8563 8564 8565 8566 8567 8568 8569 8570 8571 8572 8573 8574 8575 8576 8577 8578 8579 8580 8581 8582 8583 8584 8585 8586 8587 | be->branch->current ? "@" : "", s->show_id ? " [" : "", s->show_id ? be->branch->id : "", s->show_id ? "]" : "", s->show_date ? ':' : 0, s->show_date ? be->branch->date : 0); if (line == NULL) return RC(FSL_RC_ERROR, "%s", "fsl_mprintf"); rc = formatln(&wline, &width, line, view->ncols, 0); if (rc) { fsl_free(line); return rc; } if (n == s->selected) { if (view->active) wattr_on(view->window, A_REVERSE, NULL); s->selected_branch = be; } if (s->colour) wattr_on(view->window, COLOR_PAIR(FNC_COMMIT_ID), NULL); waddwstr(view->window, wline); if (s->colour) wattr_off(view->window, COLOR_PAIR(FNC_COMMIT_ID), NULL); if (width < view->ncols - 1) waddch(view->window, '\n'); if (n == s->selected && view->active) wattr_off(view->window, A_REVERSE, NULL); fsl_free(line); fsl_free(wline); |
︙ | ︙ | |||
9240 9241 9242 9243 9244 9245 9246 | s->selected = s->ndisplayed - 1; break; } branch_scroll_down(s, view->nlines - 1); break; case CTRL('l'): case 'R': | | | 8709 8710 8711 8712 8713 8714 8715 8716 8717 8718 8719 8720 8721 8722 8723 | s->selected = s->ndisplayed - 1; break; } branch_scroll_down(s, view->nlines - 1); break; case CTRL('l'): case 'R': fnc_free_branches(s); s->branch_glob = NULL; /* Shared pointer. */ s->when = 0; s->branch_flags = BRANCH_LS_OPEN_CLOSED; rc = fnc_load_branches(s); break; case KEY_RESIZE: if (view->nlines >= 2 && s->selected >= view->nlines - 1) |
︙ | ︙ | |||
9272 9273 9274 9275 9276 9277 9278 | *new_view = NULL; rid = fsl_uuid_to_rid(fcli_cx(), be->branch->id); if (rid < 0) return RC(rc, "%s", "fsl_uuid_to_rid"); timeline_view = view_open(0, 0, 0, start_col, FNC_VIEW_TIMELINE); | | | > | > | > | > > | 8741 8742 8743 8744 8745 8746 8747 8748 8749 8750 8751 8752 8753 8754 8755 8756 8757 8758 8759 8760 8761 8762 8763 8764 | *new_view = NULL; rid = fsl_uuid_to_rid(fcli_cx(), be->branch->id); if (rid < 0) return RC(rc, "%s", "fsl_uuid_to_rid"); timeline_view = view_open(0, 0, 0, start_col, FNC_VIEW_TIMELINE); if (timeline_view == NULL) { rc = RC(FSL_RC_ERROR, "%s", "view_open"); goto end; } rc = open_timeline_view(timeline_view, rid, "/"); end: if (rc) view_close(timeline_view); else *new_view = timeline_view; return rc; } static int browse_branch_tree(struct fnc_view **new_view, int start_col, struct fnc_branchlist_entry *be) |
︙ | ︙ | |||
9432 9433 9434 9435 9436 9437 9438 | } static int close_branch_view(struct fnc_view *view) { struct fnc_branch_view_state *s = &view->state.branch; | | < | | | | | 8906 8907 8908 8909 8910 8911 8912 8913 8914 8915 8916 8917 8918 8919 8920 8921 8922 8923 8924 8925 8926 8927 8928 8929 8930 8931 8932 | } static int close_branch_view(struct fnc_view *view) { struct fnc_branch_view_state *s = &view->state.branch; fnc_free_branches(s); return 0; } static void fnc_free_branches(struct fnc_branch_view_state *s) { struct fnc_branchlist_entry *be; while (!TAILQ_EMPTY(&s->branches)) { be = TAILQ_FIRST(&s->branches); TAILQ_REMOVE(&s->branches, be, entries); fnc_branch_close(be->branch); fsl_free(be); } } static void fnc_branch_close(struct fnc_branch *branch) |
︙ | ︙ |