Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | merge trunk make Notepad the default comment editor on Windows |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | use-utf8-in-win-external-editor |
| Files: | files | file ages | folders |
| SHA1: |
10cf72bd3bba470c4ef81dd9620ce3dd |
| User & Date: | jan.nijtmans 2012-10-07 17:18:37.461 |
Context
|
2012-10-13
| ||
| 18:38 | merge trunk ... (check-in: 0111420601 user: jan.nijtmans tags: use-utf8-in-win-external-editor) | |
|
2012-10-07
| ||
| 17:18 | merge trunk make Notepad the default comment editor on Windows ... (check-in: 10cf72bd3b user: jan.nijtmans tags: use-utf8-in-win-external-editor) | |
| 13:55 | Enhance Tcl integration with support for notifications before and after Tcl scripts are evaluated. ... (check-in: 49c63f8c78 user: mistachkin tags: trunk) | |
|
2012-09-28
| ||
| 13:45 | merge trunk ... (check-in: 1aed9aabaa user: jan.nijtmans tags: use-utf8-in-win-external-editor) | |
Changes
Changes to Makefile.in.
| ︙ | ︙ | |||
39 40 41 42 43 44 45 46 47 48 49 50 | TCLSH = tclsh LIB = @LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@ TCC += @EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H INSTALLDIR = $(DESTDIR)@prefix@/bin USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@ FOSSIL_ENABLE_TCL = @FOSSIL_ENABLE_TCL@ include $(SRCDIR)/main.mk distclean: clean rm -f autoconfig.h config.log Makefile | > | 39 40 41 42 43 44 45 46 47 48 49 50 51 | TCLSH = tclsh LIB = @LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@ TCC += @EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H INSTALLDIR = $(DESTDIR)@prefix@/bin USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@ FOSSIL_ENABLE_TCL = @FOSSIL_ENABLE_TCL@ FOSSIL_ENABLE_TCL_STUBS = @FOSSIL_ENABLE_TCL_STUBS@ include $(SRCDIR)/main.mk distclean: clean rm -f autoconfig.h config.log Makefile |
Changes to auto.def.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# System autoconfiguration. Try: ./configure --help
use cc cc-lib
options {
with-openssl:path|auto|none
=> {Look for openssl in the given path, or auto or none}
with-zlib:path => {Look for zlib in the given path}
with-tcl:path => {Enable Tcl integration, with Tcl in the specified path}
internal-sqlite=1 => {Don't use the internal sqlite, use the system one}
static=0 => {Link a static executable}
lineedit=1 => {Disable line editing}
fossil-debug=0 => {Build with fossil debugging enabled}
json=0 => {Build with fossil JSON API enabled}
}
| > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# System autoconfiguration. Try: ./configure --help
use cc cc-lib
options {
with-openssl:path|auto|none
=> {Look for openssl in the given path, or auto or none}
with-zlib:path => {Look for zlib in the given path}
with-tcl:path => {Enable Tcl integration, with Tcl in the specified path}
with-tcl-stubs=0 => {Enable Tcl integration via stubs mechanism}
internal-sqlite=1 => {Don't use the internal sqlite, use the system one}
static=0 => {Link a static executable}
lineedit=1 => {Disable line editing}
fossil-debug=0 => {Build with fossil debugging enabled}
json=0 => {Build with fossil JSON API enabled}
}
|
| ︙ | ︙ | |||
30 31 32 33 34 35 36 |
define USE_SYSTEM_SQLITE ""
if {![opt-bool internal-sqlite]} {
proc find_internal_sqlite {} {
# On some systems (slackware), libsqlite3 requires -ldl to link. So
# search for the system SQLite once with -ldl, and once without. If
| | | 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
define USE_SYSTEM_SQLITE ""
if {![opt-bool internal-sqlite]} {
proc find_internal_sqlite {} {
# On some systems (slackware), libsqlite3 requires -ldl to link. So
# search for the system SQLite once with -ldl, and once without. If
# the library can only be found with $extralibs set to -ldl, then
# the code below will append -ldl to LIBS.
#
foreach extralibs {{} {-ldl}} {
# Locate the system SQLite by searching for sqlite3_open(). Then check
# if sqlite3_wal_checkpoint() can be found as well. If we can find
# open() but not wal_checkpoint(), then the system SQLite is too old
|
| ︙ | ︙ | |||
90 91 92 93 94 95 96 |
}
if {![cc-check-includes zlib.h] || ![cc-check-function-in-lib inflateEnd z]} {
user-error "zlib not found please install it or specify the location with --with-zlib"
}
set tclpath [opt-val with-tcl]
if {$tclpath ne ""} {
| | | > | | | | | | | > > | > > > | > > | > > > > > | | | | > | | | | | < | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 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 |
}
if {![cc-check-includes zlib.h] || ![cc-check-function-in-lib inflateEnd z]} {
user-error "zlib not found please install it or specify the location with --with-zlib"
}
set tclpath [opt-val with-tcl]
if {$tclpath ne ""} {
# Note parse-tclconfig-sh is in autosetup/local.tcl
if {$tclpath eq "1"} {
# Use the system Tcl. Look in some likely places.
array set tclconfig [parse-tclconfig-sh \
/usr /usr/local /usr/share /opt/local]
set msg "on your system"
} else {
array set tclconfig [parse-tclconfig-sh $tclpath]
set msg "at $tclpath"
}
if {![info exists tclconfig(TCL_INCLUDE_SPEC)]} {
user-error "Cannot find Tcl $msg"
}
set tclstubs [opt-bool with-tcl-stubs]
if {$tclstubs && $tclconfig(TCL_SUPPORTS_STUBS)} {
set libs "$tclconfig(TCL_STUB_LIB_SPEC)"
define FOSSIL_ENABLE_TCL_STUBS
define USE_TCL_STUBS
} else {
set libs "$tclconfig(TCL_LIB_SPEC) $tclconfig(TCL_LIBS)"
}
set cflags $tclconfig(TCL_INCLUDE_SPEC)
cc-with [list -cflags $cflags -libs $libs] {
if {$tclstubs} {
if {![cc-check-functions Tcl_InitStubs]} {
user-error "Cannot find a usable Tcl stubs library $msg"
}
} else {
if {![cc-check-functions Tcl_CreateInterp]} {
user-error "Cannot find a usable Tcl library $msg"
}
}
}
set version $tclconfig(TCL_VERSION)$tclconfig(TCL_PATCH_LEVEL)
msg-result "Found Tcl $version at $tclconfig(TCL_PREFIX)"
define-append LIBS $libs
define-append EXTRA_CFLAGS $cflags
define-append EXTRA_LDFLAGS $tclconfig(TCL_LD_FLAGS)
define FOSSIL_ENABLE_TCL
}
# Helper for openssl checking
proc check-for-openssl {msg {cflags {}}} {
msg-checking "Checking for $msg..."
set rc 0
|
| ︙ | ︙ |
Changes to src/bisect.c.
| ︙ | ︙ | |||
83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
free(zLabel);
break;
}
}
assert( r>=0 );
return r;
}
/*
** COMMAND: bisect
**
** Usage: %fossil bisect SUBCOMMAND ...
**
** Run various subcommands useful for searching for bugs.
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 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 |
free(zLabel);
break;
}
}
assert( r>=0 );
return r;
}
/*
** List a bisect path.
*/
static void bisect_list(int abbreviated){
PathNode *p;
int vid = db_lget_int("checkout", 0);
int n;
Stmt s;
int nStep;
int nHidden = 0;
bisect_path();
db_prepare(&s, "SELECT blob.uuid, datetime(event.mtime) "
" FROM blob, event"
" WHERE blob.rid=:rid AND event.objid=:rid"
" AND event.type='ci'");
nStep = path_length();
if( abbreviated ){
for(p=path_last(); p; p=p->pFrom) p->isHidden = 1;
for(p=path_last(), n=0; p; p=p->pFrom, n++){
if( p->rid==bisect.good
|| p->rid==bisect.bad
|| p->rid==vid
|| (nStep>1 && n==nStep/2)
){
p->isHidden = 0;
if( p->pFrom ) p->pFrom->isHidden = 0;
}
}
for(p=path_last(); p; p=p->pFrom){
if( p->pFrom && p->pFrom->isHidden==0 ) p->isHidden = 0;
}
}
for(p=path_last(), n=0; p; p=p->pFrom, n++){
if( p->isHidden && (nHidden || (p->pFrom && p->pFrom->isHidden)) ){
nHidden++;
continue;
}else if( nHidden ){
fossil_print(" ... eliding %d check-ins\n", nHidden);
nHidden = 0;
}
db_bind_int(&s, ":rid", p->rid);
if( db_step(&s)==SQLITE_ROW ){
const char *zUuid = db_column_text(&s, 0);
const char *zDate = db_column_text(&s, 1);
fossil_print("%s %S", zDate, zUuid);
if( p->rid==bisect.good ) fossil_print(" GOOD");
if( p->rid==bisect.bad ) fossil_print(" BAD");
if( p->rid==vid ) fossil_print(" CURRENT");
if( nStep>1 && n==nStep/2 ) fossil_print(" NEXT");
fossil_print("\n");
}
db_reset(&s);
}
db_finalize(&s);
}
/*
** COMMAND: bisect
**
** Usage: %fossil bisect SUBCOMMAND ...
**
** Run various subcommands useful for searching for bugs.
|
| ︙ | ︙ | |||
116 117 118 119 120 121 122 | ** value of a bisect option. ** ** fossil bisect reset ** ** Reinitialize a bisect session. This cancels prior bisect history ** and allows a bisect session to start over from the beginning. ** | | | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
** value of a bisect option.
**
** fossil bisect reset
**
** Reinitialize a bisect session. This cancels prior bisect history
** and allows a bisect session to start over from the beginning.
**
** fossil bisect vlist|ls ?--all?
**
** List the versions in between "bad" and "good".
*/
void bisect_cmd(void){
int n;
const char *zCmd;
db_must_be_within_tree();
|
| ︙ | ︙ | |||
177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
fossil_fatal("bisect is done - there are no more intermediate versions");
}
g.argv[1] = "update";
g.argv[2] = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pMid->rid);
g.argc = 3;
g.fNoSync = 1;
update_cmd();
}else if( memcmp(zCmd, "options", n)==0 ){
if( g.argc==3 ){
unsigned int i;
for(i=0; i<sizeof(aBisectOption)/sizeof(aBisectOption[0]); i++){
char *z = mprintf("bisect-%s", aBisectOption[i].zName);
fossil_print(" %-15s %-6s ", aBisectOption[i].zName,
db_lget(z, (char*)aBisectOption[i].zDefault));
| > | 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
fossil_fatal("bisect is done - there are no more intermediate versions");
}
g.argv[1] = "update";
g.argv[2] = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pMid->rid);
g.argc = 3;
g.fNoSync = 1;
update_cmd();
bisect_list(1);
}else if( memcmp(zCmd, "options", n)==0 ){
if( g.argc==3 ){
unsigned int i;
for(i=0; i<sizeof(aBisectOption)/sizeof(aBisectOption[0]); i++){
char *z = mprintf("bisect-%s", aBisectOption[i].zName);
fossil_print(" %-15s %-6s ", aBisectOption[i].zName,
db_lget(z, (char*)aBisectOption[i].zDefault));
|
| ︙ | ︙ | |||
211 212 213 214 215 216 217 |
}else{
usage("bisect option ?NAME? ?VALUE?");
}
}else if( memcmp(zCmd, "reset", n)==0 ){
db_multi_exec(
"DELETE FROM vvar WHERE name IN ('bisect-good', 'bisect-bad');"
);
| | < < | < < | < < < < < < < < < < < < < < < < < < < < | 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
}else{
usage("bisect option ?NAME? ?VALUE?");
}
}else if( memcmp(zCmd, "reset", n)==0 ){
db_multi_exec(
"DELETE FROM vvar WHERE name IN ('bisect-good', 'bisect-bad');"
);
}else if( memcmp(zCmd, "vlist", n)==0 || memcmp(zCmd, "ls", n)==0 ){
int fAll = find_option("all", 0, 0)!=0;
bisect_list(!fAll);
}else{
usage("bad|good|next|reset|vlist ...");
}
}
|
Changes to src/checkin.c.
| ︙ | ︙ | |||
415 416 417 418 419 420 421 422 423 424 425 426 427 428 |
if( blob_str(&ans)[0]=='y' ){
file_delete(db_column_text(&q, 0));
}
}
}
db_finalize(&q);
}
/*
** Prepare a commit comment. Let the user modify it using the
** editor specified in the global_config table or either
** the VISUAL or EDITOR environment variable.
**
** Store the final commit comment in pComment. pComment is assumed
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 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 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 |
if( blob_str(&ans)[0]=='y' ){
file_delete(db_column_text(&q, 0));
}
}
}
db_finalize(&q);
}
/*
** Prompt the user for a check-in or stash comment (given in pPrompt),
** gather the response, then return the response in pComment.
**
** Lines of the prompt that begin with # are discarded. Excess whitespace
** is removed from the reply.
**
** Appropriate encoding translations are made on windows.
*/
void prompt_for_user_comment(Blob *pComment, Blob *pPrompt){
const char *zEditor;
char *zCmd;
char *zFile;
Blob reply, line;
char *zComment;
int i;
zEditor = db_get("editor", 0);
if( zEditor==0 ){
zEditor = fossil_getenv("VISUAL");
}
if( zEditor==0 ){
zEditor = fossil_getenv("EDITOR");
}
#ifdef _WIN32
if( zEditor==0 ){
zEditor = mprintf("%s\\notepad.exe", fossil_getenv("SystemRoot"));
}
#endif
if( zEditor==0 ){
blob_append(pPrompt,
"#\n"
"# Since no default text editor is set using EDITOR or VISUAL\n"
"# environment variables or the \"fossil set editor\" command,\n"
"# and because no comment was specified using the \"-m\" or \"-M\"\n"
"# command-line options, you will need to enter the comment below.\n"
"# Type \".\" on a line by itself when you are done:\n", -1);
zFile = mprintf("-");
}else{
zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'",
g.zLocalRoot);
}
#if defined(_WIN32)
blob_add_cr(pPrompt);
#endif
blob_write_to_file(pPrompt, zFile);
if( zEditor ){
zCmd = mprintf("%s \"%s\"", zEditor, zFile);
fossil_print("%s\n", zCmd);
if( fossil_system(zCmd) ){
fossil_fatal("editor aborted: \"%s\"", zCmd);
}
blob_read_from_file(&reply, zFile);
}else{
char zIn[300];
blob_zero(&reply);
while( fgets(zIn, sizeof(zIn), stdin)!=0 ){
if( zIn[0]=='.' && (zIn[1]==0 || zIn[1]=='\r' || zIn[1]=='\n') ){
break;
}
blob_append(&reply, zIn, -1);
}
}
blob_remove_cr(&reply);
file_delete(zFile);
free(zFile);
blob_zero(pComment);
while( blob_line(&reply, &line) ){
int i, n;
char *z;
n = blob_size(&line);
z = blob_buffer(&line);
for(i=0; i<n && fossil_isspace(z[i]); i++){}
if( i<n && z[i]=='#' ) continue;
if( i<n || blob_size(pComment)>0 ){
blob_appendf(pComment, "%b", &line);
}
}
blob_reset(&reply);
zComment = blob_str(pComment);
i = strlen(zComment);
while( i>0 && fossil_isspace(zComment[i-1]) ){ i--; }
blob_resize(pComment, i);
}
/*
** Prepare a commit comment. Let the user modify it using the
** editor specified in the global_config table or either
** the VISUAL or EDITOR environment variable.
**
** Store the final commit comment in pComment. pComment is assumed
|
| ︙ | ︙ | |||
440 441 442 443 444 445 446 |
static void prepare_commit_comment(
Blob *pComment,
char *zInit,
const char *zBranch,
int parent_rid,
const char *zUserOvrd
){
| < < < < | < < > | | | | | | | > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < | < < < < | 526 527 528 529 530 531 532 533 534 535 536 537 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 |
static void prepare_commit_comment(
Blob *pComment,
char *zInit,
const char *zBranch,
int parent_rid,
const char *zUserOvrd
){
Blob prompt;
#ifdef _WIN32
static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF };
blob_init(&prompt, (const char *) bom, 3);
if( zInit && zInit[0]) {
blob_append(&prompt, zInit, -1);
}
#else
blob_init(&prompt, zInit, -1);
#endif
blob_append(&prompt,
"\n"
"# Enter comments on this check-in. Lines beginning with # are ignored.\n"
"# The check-in comment follows wiki formatting rules.\n"
"#\n", -1
);
blob_appendf(&prompt, "# user: %s\n", zUserOvrd ? zUserOvrd : g.zLogin);
if( zBranch && zBranch[0] ){
blob_appendf(&prompt, "# tags: %s\n#\n", zBranch);
}else{
char *zTags = info_tags_of_checkin(parent_rid, 1);
if( zTags ) blob_appendf(&prompt, "# tags: %z\n#\n", zTags);
}
status_report(&prompt, "# ", 1, 0);
if( g.markPrivate ){
blob_append(&prompt,
"# PRIVATE BRANCH: This check-in will be private and will not sync to\n"
"# repositories.\n"
"#\n", -1
);
}
prompt_for_user_comment(pComment, &prompt);
blob_reset(&prompt);
}
/*
** Populate the Global.aCommitFile[] based on the command line arguments
** to a [commit] command. Global.aCommitFile is an array of integers
** sized at (N+1), where N is the number of arguments passed to [commit].
** The contents are the [id] values from the vfile table corresponding
|
| ︙ | ︙ |
Changes to src/configure.c.
| ︙ | ︙ | |||
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
{ "background-image", CONFIGSET_SKIN },
{ "index-page", CONFIGSET_SKIN },
{ "timeline-block-markup", CONFIGSET_SKIN },
{ "timeline-max-comment", CONFIGSET_SKIN },
{ "adunit", CONFIGSET_SKIN },
{ "adunit-omit-if-admin", CONFIGSET_SKIN },
{ "adunit-omit-if-user", CONFIGSET_SKIN },
#ifdef FOSSIL_ENABLE_TCL
{ "tcl", CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER },
#endif
{ "project-name", CONFIGSET_PROJ },
{ "project-description", CONFIGSET_PROJ },
{ "manifest", CONFIGSET_PROJ },
{ "ignore-glob", CONFIGSET_PROJ },
{ "crnl-glob", CONFIGSET_PROJ },
{ "empty-dirs", CONFIGSET_PROJ },
{ "allow-symlinks", CONFIGSET_PROJ },
{ "ticket-table", CONFIGSET_TKT },
{ "ticket-common", CONFIGSET_TKT },
| > > > | 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
{ "background-image", CONFIGSET_SKIN },
{ "index-page", CONFIGSET_SKIN },
{ "timeline-block-markup", CONFIGSET_SKIN },
{ "timeline-max-comment", CONFIGSET_SKIN },
{ "adunit", CONFIGSET_SKIN },
{ "adunit-omit-if-admin", CONFIGSET_SKIN },
{ "adunit-omit-if-user", CONFIGSET_SKIN },
#ifdef FOSSIL_ENABLE_TCL
{ "tcl", CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER },
{ "tcl-setup", CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER },
#endif
{ "project-name", CONFIGSET_PROJ },
{ "project-description", CONFIGSET_PROJ },
{ "manifest", CONFIGSET_PROJ },
{ "binary-glob", CONFIGSET_PROJ },
{ "ignore-glob", CONFIGSET_PROJ },
{ "crnl-glob", CONFIGSET_PROJ },
{ "empty-dirs", CONFIGSET_PROJ },
{ "allow-symlinks", CONFIGSET_PROJ },
{ "ticket-table", CONFIGSET_TKT },
{ "ticket-common", CONFIGSET_TKT },
|
| ︙ | ︙ |
Changes to src/db.c.
| ︙ | ︙ | |||
2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 |
{ "auto-shun", 0, 0, 0, "on" },
{ "autosync", 0, 0, 0, "on" },
{ "binary-glob", 0, 32, 1, "" },
{ "clearsign", 0, 0, 0, "off" },
{ "case-sensitive",0, 0, 0, "on" },
{ "crnl-glob", 0, 16, 1, "" },
{ "default-perms", 0, 16, 0, "u" },
{ "diff-command", 0, 16, 0, "" },
{ "dont-push", 0, 0, 0, "off" },
{ "editor", 0, 16, 0, "" },
{ "gdiff-command", 0, 16, 0, "gdiff" },
{ "gmerge-command",0, 40, 0, "" },
{ "https-login", 0, 0, 0, "off" },
{ "ignore-glob", 0, 40, 1, "" },
| > | 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 |
{ "auto-shun", 0, 0, 0, "on" },
{ "autosync", 0, 0, 0, "on" },
{ "binary-glob", 0, 32, 1, "" },
{ "clearsign", 0, 0, 0, "off" },
{ "case-sensitive",0, 0, 0, "on" },
{ "crnl-glob", 0, 16, 1, "" },
{ "default-perms", 0, 16, 0, "u" },
{ "diff-binary", 0, 0, 0, "on" },
{ "diff-command", 0, 16, 0, "" },
{ "dont-push", 0, 0, 0, "off" },
{ "editor", 0, 16, 0, "" },
{ "gdiff-command", 0, 16, 0, "gdiff" },
{ "gmerge-command",0, 40, 0, "" },
{ "https-login", 0, 0, 0, "off" },
{ "ignore-glob", 0, 40, 1, "" },
|
| ︙ | ︙ | |||
2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 |
{ "repo-cksum", 0, 0, 0, "on" },
{ "self-register", 0, 0, 0, "off" },
{ "ssl-ca-location",0, 40, 0, "" },
{ "ssl-identity", 0, 40, 0, "" },
{ "ssh-command", 0, 32, 0, "" },
#ifdef FOSSIL_ENABLE_TCL
{ "tcl", 0, 0, 0, "off" },
#endif
{ "web-browser", 0, 32, 0, "" },
{ "white-foreground", 0, 0, 0, "off" },
{ 0,0,0,0,0 }
};
/*
| > | 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 |
{ "repo-cksum", 0, 0, 0, "on" },
{ "self-register", 0, 0, 0, "off" },
{ "ssl-ca-location",0, 40, 0, "" },
{ "ssl-identity", 0, 40, 0, "" },
{ "ssh-command", 0, 32, 0, "" },
#ifdef FOSSIL_ENABLE_TCL
{ "tcl", 0, 0, 0, "off" },
{ "tcl-setup", 0, 40, 0, "" },
#endif
{ "web-browser", 0, 32, 0, "" },
{ "white-foreground", 0, 0, 0, "off" },
{ 0,0,0,0,0 }
};
/*
|
| ︙ | ︙ | |||
2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 | ** crnl-glob A comma or newline-separated list of GLOB patterns for ** (versionable) text files in which it is ok to have CR+NL line endings. ** Set to "*" to disable CR+NL checking. ** ** default-perms Permissions given automatically to new users. For more ** information on permissions see Users page in Server ** Administration of the HTTP UI. Default: u. ** ** diff-command External command to run when performing a diff. ** If undefined, the internal text diff will be used. ** ** dont-push Prevent this repository from pushing from client to ** server. Useful when setting up a private branch. ** | > > > > | 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 | ** crnl-glob A comma or newline-separated list of GLOB patterns for ** (versionable) text files in which it is ok to have CR+NL line endings. ** Set to "*" to disable CR+NL checking. ** ** default-perms Permissions given automatically to new users. For more ** information on permissions see Users page in Server ** Administration of the HTTP UI. Default: u. ** ** diff-binary If TRUE (the default), permit files that may be binary ** or that match the "binary-glob" setting to be used with ** external diff programs. If FALSE, skip these files. ** ** diff-command External command to run when performing a diff. ** If undefined, the internal text diff will be used. ** ** dont-push Prevent this repository from pushing from client to ** server. Useful when setting up a private branch. ** |
| ︙ | ︙ | |||
2196 2197 2198 2199 2200 2201 2202 | ** This identity will be presented to SSL servers to ** authenticate this client, in addition to the normal ** password authentication. ** ** ssh-command Command used to talk to a remote machine with ** the "ssh://" protocol. ** | > | | | | > > > | > | 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 | ** This identity will be presented to SSL servers to ** authenticate this client, in addition to the normal ** password authentication. ** ** ssh-command Command used to talk to a remote machine with ** the "ssh://" protocol. ** ** tcl If enabled (and Fossil was compiled with Tcl support), ** Tcl integration commands will be added to the TH1 ** interpreter, allowing arbitrary Tcl expressions and ** scripts to be evaluated from TH1. Additionally, the Tcl ** interpreter will be able to evaluate arbitrary TH1 ** expressions and scripts. Default: off. ** ** tcl-setup This is the setup script to be evaluated after creating ** and initializing the Tcl interpreter. By default, this ** is empty and no extra setup is performed. ** ** web-browser A shell command used to launch your preferred ** web browser when given a URL as an argument. ** Defaults to "start" on windows, "open" on Mac, ** and "firefox" on Unix. ** ** Options: |
| ︙ | ︙ |
Changes to src/diff.c.
| ︙ | ︙ | |||
36 37 38 39 40 41 42 43 44 45 46 47 48 49 | #define DIFF_INLINE ((u64)0x00000000) /* Inline (not side-by-side) diff */ #define DIFF_HTML ((u64)0x10000000) /* Render for HTML */ #define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */ #define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */ #define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */ #define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */ #endif /* INTERFACE */ /* ** Maximum length of a line in a text file. (8192) */ #define LENGTH_MASK_SZ 13 #define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1) | > > > > > > > > > > | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
#define DIFF_INLINE ((u64)0x00000000) /* Inline (not side-by-side) diff */
#define DIFF_HTML ((u64)0x10000000) /* Render for HTML */
#define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */
#define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */
#define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
#define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
/*
** These error messages are shared in multiple locations. They are defined
** here for consistency.
*/
#define DIFF_CANNOT_COMPUTE_BINARY \
"cannot compute difference between binary files\n"
#define DIFF_CANNOT_COMPUTE_SYMLINK \
"cannot compute difference between symlink and regular file\n"
#endif /* INTERFACE */
/*
** Maximum length of a line in a text file. (8192)
*/
#define LENGTH_MASK_SZ 13
#define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1)
|
| ︙ | ︙ | |||
155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
z += j+1;
}
/* Return results */
*pnLine = nLine;
return a;
}
/*
** Return true if two DLine elements are identical.
*/
static int same_dline(DLine *pA, DLine *pB){
return pA->h==pB->h && memcmp(pA->z,pB->z,pA->h & LENGTH_MASK)==0;
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
z += j+1;
}
/* Return results */
*pnLine = nLine;
return a;
}
/*
** Returns non-zero if the specified content appears to be binary or
** contains a line that is too long.
*/
int looks_like_binary(Blob *pContent){
const char *z = blob_str(pContent);
int n = blob_size(pContent);
int i, j;
/* Count the number of lines. Allocate space to hold
** the returned array.
*/
for(i=j=0; i<n; i++, j++){
int c = z[i];
if( c==0 ) return 1; /* \000 byte in a file -> binary */
if( c=='\n' && z[i+1]!=0 ){
if( j>LENGTH_MASK ){
return 1; /* Very long line -> binary */
}
j = 0;
}
}
if( j>LENGTH_MASK ){
return 1; /* Very long line -> binary */
}
return 0; /* No problems seen -> not binary */
}
/*
** Return true if two DLine elements are identical.
*/
static int same_dline(DLine *pA, DLine *pB){
return pA->h==pB->h && memcmp(pA->z,pB->z,pA->h & LENGTH_MASK)==0;
}
|
| ︙ | ︙ | |||
1497 1498 1499 1500 1501 1502 1503 |
/* Prepare the input files */
memset(&c, 0, sizeof(c));
c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
&c.nFrom, ignoreEolWs);
c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
&c.nTo, ignoreEolWs);
if( c.aFrom==0 || c.aTo==0 ){
| | | | | | | | 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 |
/* Prepare the input files */
memset(&c, 0, sizeof(c));
c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
&c.nFrom, ignoreEolWs);
c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
&c.nTo, ignoreEolWs);
if( c.aFrom==0 || c.aTo==0 ){
fossil_free(c.aFrom);
fossil_free(c.aTo);
if( pOut ){
blob_appendf(pOut, DIFF_CANNOT_COMPUTE_BINARY);
}
return 0;
}
/* Compute the difference */
diff_all(&c);
if( (diffFlags & DIFF_NOOPT)==0 ) diff_optimize(&c);
if( pOut ){
/* Compute a context or side-by-side diff into pOut */
int escHtml = (diffFlags & DIFF_HTML)!=0;
if( diffFlags & DIFF_SIDEBYSIDE ){
int width = diff_width(diffFlags);
sbsDiff(&c, pOut, nContext, width, escHtml);
}else{
int showLn = (diffFlags & DIFF_LINENO)!=0;
contextDiff(&c, pOut, nContext, showLn, escHtml);
}
fossil_free(c.aFrom);
fossil_free(c.aTo);
fossil_free(c.aEdit);
return 0;
}else{
/* If a context diff is not requested, then return the
** array of COPY/DELETE/INSERT triples.
*/
free(c.aFrom);
free(c.aTo);
|
| ︙ | ︙ | |||
1700 1701 1702 1703 1704 1705 1706 |
x->iLevel = iThisLevel;
}
}
lnTo += p->c.aEdit[i+2];
}
/* Clear out the diff results */
| | | 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 |
x->iLevel = iThisLevel;
}
}
lnTo += p->c.aEdit[i+2];
}
/* Clear out the diff results */
fossil_free(p->c.aEdit);
p->c.aEdit = 0;
p->c.nEdit = 0;
p->c.nEditAlloc = 0;
/* Clear out the from file */
free(p->c.aFrom);
|
| ︙ | ︙ |
Changes to src/diffcmd.c.
| ︙ | ︙ | |||
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
** Show the difference between two files, one in memory and one on disk.
**
** The difference is the set of edits needed to transform pFile1 into
** zFile2. The content of pFile1 is in memory. zFile2 exists on disk.
**
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
** command zDiffCmd to do the diffing.
*/
void diff_file(
Blob *pFile1, /* In memory content to compare from */
const char *zFile2, /* On disk content to compare to */
const char *zName, /* Display name of the file */
const char *zDiffCmd, /* Command for comparison */
u64 diffFlags /* Flags to control the diff */
){
if( zDiffCmd==0 ){
Blob out; /* Diff output text */
Blob file2; /* Content of zFile2 */
const char *zName2; /* Name of zFile2 for display */
| > > > > > > > | 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
** Show the difference between two files, one in memory and one on disk.
**
** The difference is the set of edits needed to transform pFile1 into
** zFile2. The content of pFile1 is in memory. zFile2 exists on disk.
**
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
** command zDiffCmd to do the diffing.
**
** When using an external diff program, zBinGlob contains the GLOB patterns
** for file names to treat as binary. If fIncludeBinary is zero, these files
** will be skipped in addition to files that may contain binary content.
*/
void diff_file(
Blob *pFile1, /* In memory content to compare from */
int isBin1, /* Does the 'from' content appear to be binary */
const char *zFile2, /* On disk content to compare to */
const char *zName, /* Display name of the file */
const char *zDiffCmd, /* Command for comparison */
const char *zBinGlob, /* Treat file names matching this as binary */
int fIncludeBinary, /* Include binary files for external diff */
u64 diffFlags /* Flags to control the diff */
){
if( zDiffCmd==0 ){
Blob out; /* Diff output text */
Blob file2; /* Content of zFile2 */
const char *zName2; /* Name of zFile2 for display */
|
| ︙ | ︙ | |||
114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
/* Release memory resources */
blob_reset(&file2);
}else{
int cnt = 0;
Blob nameFile1; /* Name of temporary file to old pFile1 content */
Blob cmd; /* Text of command to run */
/* Construct a temporary file to hold pFile1 based on the name of
** zFile2 */
blob_zero(&nameFile1);
do{
blob_reset(&nameFile1);
blob_appendf(&nameFile1, "%s~%d", zFile2, cnt++);
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
/* Release memory resources */
blob_reset(&file2);
}else{
int cnt = 0;
Blob nameFile1; /* Name of temporary file to old pFile1 content */
Blob cmd; /* Text of command to run */
if( !fIncludeBinary ){
Blob file2;
if( isBin1 ){
fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
return;
}
if( zBinGlob ){
Glob *pBinary = glob_create(zBinGlob);
if( glob_match(pBinary, zName) ){
fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
glob_free(pBinary);
return;
}
glob_free(pBinary);
}
blob_zero(&file2);
if( file_wd_size(zFile2)>=0 ){
if( file_wd_islink(zFile2) ){
blob_read_link(&file2, zFile2);
}else{
blob_read_from_file(&file2, zFile2);
}
}
if( looks_like_binary(&file2) ){
fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
blob_reset(&file2);
return;
}
blob_reset(&file2);
}
/* Construct a temporary file to hold pFile1 based on the name of
** zFile2 */
blob_zero(&nameFile1);
do{
blob_reset(&nameFile1);
blob_appendf(&nameFile1, "%s~%d", zFile2, cnt++);
|
| ︙ | ︙ | |||
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 |
** Show the difference between two files, both in memory.
**
** The difference is the set of edits needed to transform pFile1 into
** pFile2.
**
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
** command zDiffCmd to do the diffing.
*/
void diff_file_mem(
Blob *pFile1, /* In memory content to compare from */
Blob *pFile2, /* In memory content to compare to */
const char *zName, /* Display name of the file */
const char *zDiffCmd, /* Command for comparison */
u64 diffFlags /* Diff flags */
){
if( diffFlags & DIFF_BRIEF ) return;
if( zDiffCmd==0 ){
Blob out; /* Diff output text */
blob_zero(&out);
text_diff(pFile1, pFile2, &out, diffFlags);
diff_print_filenames(zName, zName, diffFlags);
fossil_print("%s\n", blob_str(&out));
/* Release memory resources */
blob_reset(&out);
}else{
Blob cmd;
char zTemp1[300];
char zTemp2[300];
/* Construct a temporary file names */
file_tempname(sizeof(zTemp1), zTemp1);
file_tempname(sizeof(zTemp2), zTemp2);
blob_write_to_file(pFile1, zTemp1);
blob_write_to_file(pFile2, zTemp2);
| > > > > > > > > > > > > > > > > > > > > > > > > | 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 245 246 247 |
** Show the difference between two files, both in memory.
**
** The difference is the set of edits needed to transform pFile1 into
** pFile2.
**
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
** command zDiffCmd to do the diffing.
**
** When using an external diff program, zBinGlob contains the GLOB patterns
** for file names to treat as binary. If fIncludeBinary is zero, these files
** will be skipped in addition to files that may contain binary content.
*/
void diff_file_mem(
Blob *pFile1, /* In memory content to compare from */
Blob *pFile2, /* In memory content to compare to */
int isBin1, /* Does the 'from' content appear to be binary */
int isBin2, /* Does the 'to' content appear to be binary */
const char *zName, /* Display name of the file */
const char *zDiffCmd, /* Command for comparison */
const char *zBinGlob, /* Treat file names matching this as binary */
int fIncludeBinary, /* Include binary files for external diff */
u64 diffFlags /* Diff flags */
){
if( diffFlags & DIFF_BRIEF ) return;
if( zDiffCmd==0 ){
Blob out; /* Diff output text */
blob_zero(&out);
text_diff(pFile1, pFile2, &out, diffFlags);
diff_print_filenames(zName, zName, diffFlags);
fossil_print("%s\n", blob_str(&out));
/* Release memory resources */
blob_reset(&out);
}else{
Blob cmd;
char zTemp1[300];
char zTemp2[300];
if( !fIncludeBinary ){
if( isBin1 || isBin2 ){
fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
return;
}
if( zBinGlob ){
Glob *pBinary = glob_create(zBinGlob);
if( glob_match(pBinary, zName) ){
fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
glob_free(pBinary);
return;
}
glob_free(pBinary);
}
}
/* Construct a temporary file names */
file_tempname(sizeof(zTemp1), zTemp1);
file_tempname(sizeof(zTemp2), zTemp2);
blob_write_to_file(pFile1, zTemp1);
blob_write_to_file(pFile2, zTemp2);
|
| ︙ | ︙ | |||
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
blob_reset(&cmd);
}
}
/*
** Do a diff against a single file named in zFileTreeName from version zFrom
** against the same file on disk.
*/
static void diff_one_against_disk(
const char *zFrom, /* Name of file */
const char *zDiffCmd, /* Use this "diff" command */
u64 diffFlags, /* Diff control flags */
const char *zFileTreeName
){
Blob fname;
Blob content;
int isLink;
file_tree_name(zFileTreeName, &fname, 1);
| > > > > > > > > > > | > | < | > > > > > > > > > > | 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 |
blob_reset(&cmd);
}
}
/*
** Do a diff against a single file named in zFileTreeName from version zFrom
** against the same file on disk.
**
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
** command zDiffCmd to do the diffing.
**
** When using an external diff program, zBinGlob contains the GLOB patterns
** for file names to treat as binary. If fIncludeBinary is zero, these files
** will be skipped in addition to files that may contain binary content.
*/
static void diff_one_against_disk(
const char *zFrom, /* Name of file */
const char *zDiffCmd, /* Use this "diff" command */
const char *zBinGlob, /* Treat file names matching this as binary */
int fIncludeBinary, /* Include binary files for external diff */
u64 diffFlags, /* Diff control flags */
const char *zFileTreeName
){
Blob fname;
Blob content;
int isLink;
int isBin;
file_tree_name(zFileTreeName, &fname, 1);
historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0,
fIncludeBinary ? 0 : &isBin, 0);
if( !isLink != !file_wd_islink(zFrom) ){
fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
}else{
diff_file(&content, isBin, zFileTreeName, zFileTreeName,
zDiffCmd, zBinGlob, fIncludeBinary, diffFlags);
}
blob_reset(&content);
blob_reset(&fname);
}
/*
** Run a diff between the version zFrom and files on disk. zFrom might
** be NULL which means to simply show the difference between the edited
** files on disk and the check-out on which they are based.
**
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
** command zDiffCmd to do the diffing.
**
** When using an external diff program, zBinGlob contains the GLOB patterns
** for file names to treat as binary. If fIncludeBinary is zero, these files
** will be skipped in addition to files that may contain binary content.
*/
static void diff_all_against_disk(
const char *zFrom, /* Version to difference from */
const char *zDiffCmd, /* Use this diff command. NULL for built-in */
const char *zBinGlob, /* Treat file names matching this as binary */
int fIncludeBinary, /* Treat file names matching this as binary */
u64 diffFlags /* Flags controlling diff output */
){
int vid;
Blob sql;
Stmt q;
int asNewFile; /* Treat non-existant files as empty files */
|
| ︙ | ︙ | |||
305 306 307 308 309 310 311 312 313 314 |
}else if( isChnged==3 ){
fossil_print("ADDED_BY_MERGE %s\n", zPathname);
srcid = 0;
if( !asNewFile ){ showDiff = 0; }
}
if( showDiff ){
Blob content;
if( !isLink != !file_wd_islink(zFullName) ){
diff_print_index(zPathname, diffFlags);
diff_print_filenames(zPathname, zPathname, diffFlags);
| > | < > | > > > > > > > > > > > | > | > | < | > > > > > > > > > > > > > | > > > > > > > > > > | 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 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 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 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 |
}else if( isChnged==3 ){
fossil_print("ADDED_BY_MERGE %s\n", zPathname);
srcid = 0;
if( !asNewFile ){ showDiff = 0; }
}
if( showDiff ){
Blob content;
int isBin;
if( !isLink != !file_wd_islink(zFullName) ){
diff_print_index(zPathname, diffFlags);
diff_print_filenames(zPathname, zPathname, diffFlags);
fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
continue;
}
if( srcid>0 ){
content_get(srcid, &content);
}else{
blob_zero(&content);
}
isBin = fIncludeBinary ? 0 : looks_like_binary(&content);
diff_print_index(zPathname, diffFlags);
diff_file(&content, isBin, zFullName, zPathname, zDiffCmd,
zBinGlob, fIncludeBinary, diffFlags);
blob_reset(&content);
}
free(zToFree);
}
db_finalize(&q);
db_end_transaction(1); /* ROLLBACK */
}
/*
** Output the differences between two versions of a single file.
** zFrom and zTo are the check-ins containing the two file versions.
**
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
** command zDiffCmd to do the diffing.
**
** When using an external diff program, zBinGlob contains the GLOB patterns
** for file names to treat as binary. If fIncludeBinary is zero, these files
** will be skipped in addition to files that may contain binary content.
*/
static void diff_one_two_versions(
const char *zFrom,
const char *zTo,
const char *zDiffCmd,
const char *zBinGlob,
int fIncludeBinary,
u64 diffFlags,
const char *zFileTreeName
){
char *zName;
Blob fname;
Blob v1, v2;
int isLink1, isLink2;
int isBin1, isBin2;
if( diffFlags & DIFF_BRIEF ) return;
file_tree_name(zFileTreeName, &fname, 1);
zName = blob_str(&fname);
historical_version_of_file(zFrom, zName, &v1, &isLink1, 0,
fIncludeBinary ? 0 : &isBin1, 0);
historical_version_of_file(zTo, zName, &v2, &isLink2, 0,
fIncludeBinary ? 0 : &isBin2, 0);
if( isLink1 != isLink2 ){
diff_print_filenames(zName, zName, diffFlags);
fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
}else{
diff_file_mem(&v1, &v2, isBin1, isBin2, zName, zDiffCmd,
zBinGlob, fIncludeBinary, diffFlags);
}
blob_reset(&v1);
blob_reset(&v2);
blob_reset(&fname);
}
/*
** Show the difference between two files identified by ManifestFile
** entries.
**
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
** command zDiffCmd to do the diffing.
**
** When using an external diff program, zBinGlob contains the GLOB patterns
** for file names to treat as binary. If fIncludeBinary is zero, these files
** will be skipped in addition to files that may contain binary content.
*/
static void diff_manifest_entry(
struct ManifestFile *pFrom,
struct ManifestFile *pTo,
const char *zDiffCmd,
const char *zBinGlob,
int fIncludeBinary,
u64 diffFlags
){
Blob f1, f2;
int isBin1, isBin2;
int rid;
const char *zName = pFrom ? pFrom->zName : pTo->zName;
if( diffFlags & DIFF_BRIEF ) return;
diff_print_index(zName, diffFlags);
if( pFrom ){
rid = uuid_to_rid(pFrom->zUuid, 0);
content_get(rid, &f1);
}else{
blob_zero(&f1);
}
if( pTo ){
rid = uuid_to_rid(pTo->zUuid, 0);
content_get(rid, &f2);
}else{
blob_zero(&f2);
}
isBin1 = fIncludeBinary ? 0 : looks_like_binary(&f1);
isBin2 = fIncludeBinary ? 0 : looks_like_binary(&f2);
diff_file_mem(&f1, &f2, isBin1, isBin2, zName, zDiffCmd,
zBinGlob, fIncludeBinary, diffFlags);
blob_reset(&f1);
blob_reset(&f2);
}
/*
** Output the differences between two check-ins.
**
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
** command zDiffCmd to do the diffing.
**
** When using an external diff program, zBinGlob contains the GLOB patterns
** for file names to treat as binary. If fIncludeBinary is zero, these files
** will be skipped in addition to files that may contain binary content.
*/
static void diff_all_two_versions(
const char *zFrom,
const char *zTo,
const char *zDiffCmd,
const char *zBinGlob,
int fIncludeBinary,
u64 diffFlags
){
Manifest *pFrom, *pTo;
ManifestFile *pFromFile, *pToFile;
int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0;
pFrom = manifest_get_by_name(zFrom, 0);
|
| ︙ | ︙ | |||
423 424 425 426 427 428 429 |
cmp = -1;
}else{
cmp = fossil_strcmp(pFromFile->zName, pToFile->zName);
}
if( cmp<0 ){
fossil_print("DELETED %s\n", pFromFile->zName);
if( asNewFlag ){
| | > | > | > | 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 |
cmp = -1;
}else{
cmp = fossil_strcmp(pFromFile->zName, pToFile->zName);
}
if( cmp<0 ){
fossil_print("DELETED %s\n", pFromFile->zName);
if( asNewFlag ){
diff_manifest_entry(pFromFile, 0, zDiffCmd, zBinGlob,
fIncludeBinary, diffFlags);
}
pFromFile = manifest_file_next(pFrom,0);
}else if( cmp>0 ){
fossil_print("ADDED %s\n", pToFile->zName);
if( asNewFlag ){
diff_manifest_entry(0, pToFile, zDiffCmd, zBinGlob,
fIncludeBinary, diffFlags);
}
pToFile = manifest_file_next(pTo,0);
}else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
/* No changes */
pFromFile = manifest_file_next(pFrom,0);
pToFile = manifest_file_next(pTo,0);
}else{
if( diffFlags & DIFF_BRIEF ){
fossil_print("CHANGED %s\n", pFromFile->zName);
}else{
diff_manifest_entry(pFromFile, pToFile, zDiffCmd, zBinGlob,
fIncludeBinary, diffFlags);
}
pFromFile = manifest_file_next(pFrom,0);
pToFile = manifest_file_next(pTo,0);
}
}
manifest_destroy(pFrom);
manifest_destroy(pTo);
|
| ︙ | ︙ | |||
491 492 493 494 495 496 497 498 499 500 501 502 503 504 |
@ .t tag config ln -foreground gray
@ .t tag config chng -background {#d0d0ff}
@ .t tag config add -background {#c0ffc0}
@ .t tag config rm -background {#ffc0c0}
@ proc dehtml {x} {
@ return [string map {& & < < > > ' ' " \"} $x]
@ }
@ set in [open $cmd r]
@ while {![eof $in]} {
@ set line [gets $in]
@ if {[regexp {^<a name="chunk.*"></a>} $line]} continue
@ if {[regexp {^===} $line]} {
@ set n [string length $line]
@ if {$n>$mx} {set mx $n}
| > | 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 |
@ .t tag config ln -foreground gray
@ .t tag config chng -background {#d0d0ff}
@ .t tag config add -background {#c0ffc0}
@ .t tag config rm -background {#ffc0c0}
@ proc dehtml {x} {
@ return [string map {& & < < > > ' ' " \"} $x]
@ }
@ # puts $cmd
@ set in [open $cmd r]
@ while {![eof $in]} {
@ set line [gets $in]
@ if {[regexp {^<a name="chunk.*"></a>} $line]} continue
@ if {[regexp {^===} $line]} {
@ set n [string length $line]
@ if {$n>$mx} {set mx $n}
|
| ︙ | ︙ | |||
526 527 528 529 530 531 532 | ** to the diff command. ** ** Steps: ** (1) Write the Tcl/Tk script used for rendering into a temp file. ** (2) Invoke "wish" on the temp file using fossil_system(). ** (3) Delete the temp file. */ | | | > | > | > > > > > > > > > > > > > > > > > > > > > > > > > | 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 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 |
** to the diff command.
**
** Steps:
** (1) Write the Tcl/Tk script used for rendering into a temp file.
** (2) Invoke "wish" on the temp file using fossil_system().
** (3) Delete the temp file.
*/
void diff_tk(const char *zSubCmd, int firstArg){
int i;
Blob script;
char *zTempFile;
char *zCmd;
blob_zero(&script);
blob_appendf(&script, "set cmd {| \"%/\" %s --html -y -i",
g.nameOfExe, zSubCmd);
for(i=firstArg; i<g.argc; i++){
blob_append(&script, " ", 1);
shell_escape(&script, g.argv[i]);
}
blob_appendf(&script, "}\n%s", zDiffScript);
zTempFile = write_blob_to_temp_file(&script);
zCmd = mprintf("tclsh \"%s\"", zTempFile);
fossil_system(zCmd);
file_delete(zTempFile);
fossil_free(zCmd);
}
/*
** Returns non-zero if files that may be binary should be used with external
** diff programs.
*/
int diff_include_binary_files(void){
if( is_truth(find_option("diff-binary", 0, 1)) ){
return 1;
}
if( db_get_boolean("diff-binary", 1) ){
return 1;
}
return 0;
}
/*
** Returns the GLOB pattern for file names that should be treated as binary
** by the diff subsystem, if any.
*/
const char *diff_get_binary_glob(void){
const char *zBinGlob = find_option("binary", 0, 1);
if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
return zBinGlob;
}
/*
** COMMAND: diff
** COMMAND: gdiff
**
** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...?
|
| ︙ | ︙ | |||
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 |
** rather than any external diff program that might be configured using
** the "setting" command. If no external diff program is configured, then
** the "-i" option is a no-op. The "-i" option converts "gdiff" into "diff".
**
** The "-N" or "--new-file" option causes the complete text of added or
** deleted files to be displayed.
**
** Options:
** --branch BRANCH Show diff of all changes on BRANCH
** --brief Show filenames only
** --context|-c N Use N lines of context
** --from|-r VERSION select VERSION as source for the diff
** -i use internal diff logic
** --new-file|-N output complete text of added or deleted files
** --tk Launch a Tcl/Tk GUI for display
** --to VERSION select VERSION as target for the diff
** --side-by-side|-y side-by-side diff
** --unified unified diff
** --width|-W N Width of lines in side-by-side diff
*/
void diff_cmd(void){
int isGDiff; /* True for gdiff. False for normal diff */
int isInternDiff; /* True for internal diff */
int hasNFlag; /* True if -N or --new-file flag is used */
const char *zFrom; /* Source version number */
const char *zTo; /* Target version number */
const char *zBranch; /* Branch to diff */
const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */
u64 diffFlags = 0; /* Flags to control the DIFF */
int f;
if( find_option("tk",0,0)!=0 ){
| > > > > > > > > > > > | < > > > | > | > < > > > | > | > | | 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 |
** rather than any external diff program that might be configured using
** the "setting" command. If no external diff program is configured, then
** the "-i" option is a no-op. The "-i" option converts "gdiff" into "diff".
**
** The "-N" or "--new-file" option causes the complete text of added or
** deleted files to be displayed.
**
** The "--diff-binary" option enables or disables the inclusion of binary files
** when using an external diff program.
**
** The "--binary" option causes files matching the glob PATTERN to be treated
** as binary when considering if they should be used with external diff program.
** This option overrides the "binary-glob" setting.
**
** Options:
** --branch BRANCH Show diff of all changes on BRANCH
** --brief Show filenames only
** --context|-c N Use N lines of context
** --from|-r VERSION select VERSION as source for the diff
** -i use internal diff logic
** --new-file|-N output complete text of added or deleted files
** --tk Launch a Tcl/Tk GUI for display
** --to VERSION select VERSION as target for the diff
** --side-by-side|-y side-by-side diff
** --unified unified diff
** --width|-W N Width of lines in side-by-side diff
** --diff-binary BOOL Include binary files when using external commands
** --binary PATTERN Treat files that match the glob PATTERN as binary
*/
void diff_cmd(void){
int isGDiff; /* True for gdiff. False for normal diff */
int isInternDiff; /* True for internal diff */
int hasNFlag; /* True if -N or --new-file flag is used */
const char *zFrom; /* Source version number */
const char *zTo; /* Target version number */
const char *zBranch; /* Branch to diff */
const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */
const char *zBinGlob = 0; /* Treat file names matching this as binary */
int fIncludeBinary = 0; /* Include binary files for external diff */
u64 diffFlags = 0; /* Flags to control the DIFF */
int f;
if( find_option("tk",0,0)!=0 ){
diff_tk("diff", 2);
return;
}
isGDiff = g.argv[1][0]=='g';
isInternDiff = find_option("internal","i",0)!=0;
zFrom = find_option("from", "r", 1);
zTo = find_option("to", 0, 1);
zBranch = find_option("branch", 0, 1);
diffFlags = diff_options();
hasNFlag = find_option("new-file","N",0)!=0;
if( hasNFlag ) diffFlags |= DIFF_NEWFILE;
if( zBranch ){
if( zTo || zFrom ){
fossil_fatal("cannot use --from or --to with --branch");
}
zTo = zBranch;
zFrom = mprintf("root:%s", zBranch);
}
if( zTo==0 ){
db_must_be_within_tree();
if( !isInternDiff ){
zDiffCmd = diff_command_external(isGDiff);
}
zBinGlob = diff_get_binary_glob();
fIncludeBinary = diff_include_binary_files();
verify_all_options();
if( g.argc>=3 ){
for(f=2; f<g.argc; ++f){
diff_one_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary,
diffFlags, g.argv[f]);
}
}else{
diff_all_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary,
diffFlags);
}
}else if( zFrom==0 ){
fossil_fatal("must use --from if --to is present");
}else{
db_find_and_open_repository(0, 0);
if( !isInternDiff ){
zDiffCmd = diff_command_external(isGDiff);
}
zBinGlob = diff_get_binary_glob();
fIncludeBinary = diff_include_binary_files();
verify_all_options();
if( g.argc>=3 ){
for(f=2; f<g.argc; ++f){
diff_one_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary,
diffFlags, g.argv[f]);
}
}else{
diff_all_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary,
diffFlags);
}
}
}
/*
** WEBPAGE: vpatch
** URL vpatch?from=UUID&to=UUID
*/
void vpatch_page(void){
const char *zFrom = P("from");
const char *zTo = P("to");
login_check_credentials();
if( !g.perm.Read ){ login_needed(); return; }
if( zFrom==0 || zTo==0 ) fossil_redirect_home();
cgi_set_content_type("text/plain");
diff_all_two_versions(zFrom, zTo, 0, 0, 0, DIFF_NEWFILE);
}
|
Changes to src/finfo.c.
| ︙ | ︙ | |||
113 114 115 116 117 118 119 |
}else if( find_option("print","p",0) ){
Blob record;
Blob fname;
const char *zRevision = find_option("revision", "r", 1);
file_tree_name(g.argv[2], &fname, 1);
if( zRevision ){
| | | 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
}else if( find_option("print","p",0) ){
Blob record;
Blob fname;
const char *zRevision = find_option("revision", "r", 1);
file_tree_name(g.argv[2], &fname, 1);
if( zRevision ){
historical_version_of_file(zRevision, blob_str(&fname), &record, 0,0,0,0);
}else{
int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B %s",
&fname, filename_collation());
if( rid==0 ){
fossil_fatal("no history for file: %b", &fname);
}
content_get(rid, &record);
|
| ︙ | ︙ |
Changes to src/main.c.
| ︙ | ︙ | |||
86 87 88 89 90 91 92 |
#ifdef FOSSIL_ENABLE_TCL
/*
** All Tcl related context information is in this structure. This structure
** definition has been copied from and should be kept in sync with the one in
** "th_tcl.c".
*/
struct TclContext {
| | | > > > | > > > > > | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
#ifdef FOSSIL_ENABLE_TCL
/*
** All Tcl related context information is in this structure. This structure
** definition has been copied from and should be kept in sync with the one in
** "th_tcl.c".
*/
struct TclContext {
int argc; /* Number of original (expanded) arguments. */
char **argv; /* Full copy of the original (expanded) arguments. */
void *library; /* The Tcl library module handle. */
void *xFindExecutable; /* See tcl_FindExecutableProc in th_tcl.c. */
void *xCreateInterp; /* See tcl_CreateInterpProc in th_tcl.c. */
Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */
char *setup; /* The optional Tcl setup script. */
void *xPreEval; /* Optional, called before Tcl_Eval*(). */
void *pPreContext; /* Optional, provided to xPreEval(). */
void *xPostEval; /* Optional, called after Tcl_Eval*(). */
void *pPostContext; /* Optional, provided to xPostEval(). */
};
#endif
/*
** All global variables are in this structure.
*/
struct Global {
|
| ︙ | ︙ | |||
544 545 546 547 548 549 550 551 552 553 554 555 556 557 |
}
i += 2;
while( i<g.argc ) newArgv[j++] = g.argv[i++];
newArgv[j] = 0;
g.argc = j;
g.argv = newArgv;
}
/*
** This procedure runs first.
*/
int main(int argc, char **argv)
{
const char *zCmdName = "unknown";
| > > > > > > > > > > > > > > | 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 |
}
i += 2;
while( i<g.argc ) newArgv[j++] = g.argv[i++];
newArgv[j] = 0;
g.argc = j;
g.argv = newArgv;
}
/*
** Make a deep copy of the provided argument array and return it.
*/
static char **copy_args(int argc, char **argv){
char **zNewArgv;
int i;
zNewArgv = fossil_malloc( sizeof(char*)*(argc+1) );
memset(zNewArgv, 0, sizeof(char*)*(argc+1));
for(i=0; i<argc; i++){
zNewArgv[i] = fossil_strdup(argv[i]);
}
return zNewArgv;
}
/*
** This procedure runs first.
*/
int main(int argc, char **argv)
{
const char *zCmdName = "unknown";
|
| ︙ | ︙ | |||
572 573 574 575 576 577 578 579 | #endif g.json.outOpt = cson_output_opt_empty; g.json.outOpt.addNewline = 1; g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */; #endif /* FOSSIL_ENABLE_JSON */ expand_args_option(argc, argv); #ifdef FOSSIL_ENABLE_TCL g.tcl.argc = g.argc; | > | < | 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 |
#endif
g.json.outOpt = cson_output_opt_empty;
g.json.outOpt.addNewline = 1;
g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */;
#endif /* FOSSIL_ENABLE_JSON */
expand_args_option(argc, argv);
#ifdef FOSSIL_ENABLE_TCL
memset(&g.tcl, 0, sizeof(TclContext));
g.tcl.argc = g.argc;
g.tcl.argv = copy_args(g.argc, g.argv); /* save full arguments */
#endif
if( fossil_getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){
zCmdName = "cgi";
g.isHTTP = 1;
}else if( g.argc<2 ){
fossil_print(
"Usage: %s COMMAND ...\n"
|
| ︙ | ︙ |
Changes to src/makemake.tcl.
| ︙ | ︙ | |||
373 374 375 376 377 378 379 380 381 382 383 384 385 386 | #### Enable HTTPS support via OpenSSL (links to libssl and libcrypto) # # FOSSIL_ENABLE_SSL = 1 #### Enable scripting support via Tcl/Tk # # FOSSIL_ENABLE_TCL = 1 #### Use the Tcl source directory instead of the install directory? # This is useful when Tcl has been compiled statically with MinGW. # FOSSIL_TCL_SOURCE = 1 #### The directories where the zlib include and library files are located. | > > > > | 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 | #### Enable HTTPS support via OpenSSL (links to libssl and libcrypto) # # FOSSIL_ENABLE_SSL = 1 #### Enable scripting support via Tcl/Tk # # FOSSIL_ENABLE_TCL = 1 #### Load Tcl using the stubs mechanism # # FOSSIL_ENABLE_TCL_STUBS = 1 #### Use the Tcl source directory instead of the install directory? # This is useful when Tcl has been compiled statically with MinGW. # FOSSIL_TCL_SOURCE = 1 #### The directories where the zlib include and library files are located. |
| ︙ | ︙ | |||
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 | # used if the FOSSIL_TCL_SOURCE macro is not defined. # TCLINCDIR = $(TCLDIR)/include TCLLIBDIR = $(TCLDIR)/lib #### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)? # LIBTCL = -ltcl86 #### C Compile and options for use in building executables that # will run on the target platform. This is usually the same # as BCC, unless you are cross-compiling. This C compiler builds # the finished binary for fossil. The BCC compiler above is used # for building intermediate code-generator tools. # | > > > > | 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 | # used if the FOSSIL_TCL_SOURCE macro is not defined. # TCLINCDIR = $(TCLDIR)/include TCLLIBDIR = $(TCLDIR)/lib #### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)? # ifdef FOSSIL_ENABLE_TCL_STUBS LIBTCL = -ltclstub86 else LIBTCL = -ltcl86 endif #### C Compile and options for use in building executables that # will run on the target platform. This is usually the same # as BCC, unless you are cross-compiling. This C compiler builds # the finished binary for fossil. The BCC compiler above is used # for building intermediate code-generator tools. # |
| ︙ | ︙ | |||
460 461 462 463 464 465 466 | # With HTTPS support ifdef FOSSIL_ENABLE_SSL TCC += -DFOSSIL_ENABLE_SSL=1 RCC += -DFOSSIL_ENABLE_SSL=1 endif | | | > > > > > > > > | 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 | # With HTTPS support ifdef FOSSIL_ENABLE_SSL TCC += -DFOSSIL_ENABLE_SSL=1 RCC += -DFOSSIL_ENABLE_SSL=1 endif # With Tcl support ifdef FOSSIL_ENABLE_TCL TCC += -DFOSSIL_ENABLE_TCL=1 RCC += -DFOSSIL_ENABLE_TCL=1 # Either statically linked or via stubs ifdef FOSSIL_ENABLE_TCL_STUBS TCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS RCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS else TCC += -DSTATIC_BUILD RCC += -DSTATIC_BUILD endif endif # With JSON support ifdef FOSSIL_ENABLE_JSON TCC += -DFOSSIL_ENABLE_JSON=1 RCC += -DFOSSIL_ENABLE_JSON=1 endif |
| ︙ | ︙ | |||
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 | # LIB += -lmingwex -lz #### These libraries MUST appear in the same order as they do for Tcl # or linking with it will not work (exact reason unknown). # ifdef FOSSIL_ENABLE_TCL LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32 else LIB += -lkernel32 -lws2_32 endif #### Tcl shell for use in running the fossil test suite. This is only # used for testing. # | > > > > | 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 | # LIB += -lmingwex -lz #### These libraries MUST appear in the same order as they do for Tcl # or linking with it will not work (exact reason unknown). # ifdef FOSSIL_ENABLE_TCL ifdef FOSSIL_ENABLE_TCL_STUBS LIB += -lkernel32 -lws2_32 else LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32 endif else LIB += -lkernel32 -lws2_32 endif #### Tcl shell for use in running the fossil test suite. This is only # used for testing. # |
| ︙ | ︙ |
Changes to src/manifest.c.
| ︙ | ︙ | |||
1877 1878 1879 1880 1881 1882 1883 |
int branchMove = 0;
blob_zero(&comment);
for(i=0; i<p->nTag; i++){
zUuid = p->aTag[i].zUuid;
if( i==0 || fossil_strcmp(zUuid, p->aTag[i-1].zUuid)!=0 ){
if( i>0 ) blob_append(&comment, " ", 1);
blob_appendf(&comment,
| | | | 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 |
int branchMove = 0;
blob_zero(&comment);
for(i=0; i<p->nTag; i++){
zUuid = p->aTag[i].zUuid;
if( i==0 || fossil_strcmp(zUuid, p->aTag[i-1].zUuid)!=0 ){
if( i>0 ) blob_append(&comment, " ", 1);
blob_appendf(&comment,
"Edit [%S]:",
zUuid);
branchMove = 0;
}
zName = p->aTag[i].zName;
zValue = p->aTag[i].zValue;
if( strcmp(zName, "*branch")==0 ){
blob_appendf(&comment,
" Move to branch [/timeline?r=%h&nd&dp=%S | %h].",
|
| ︙ | ︙ |
Changes to src/path.c.
| ︙ | ︙ | |||
24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#if INTERFACE
/* Nodes for the paths through the DAG.
*/
struct PathNode {
int rid; /* ID for this node */
u8 fromIsParent; /* True if pFrom is the parent of rid */
u8 isPrim; /* True if primary side of common ancestor */
PathNode *pFrom; /* Node we came from */
union {
PathNode *pPeer; /* List of nodes of the same generation */
PathNode *pTo; /* Next on path from beginning to end */
} u;
PathNode *pAll; /* List of all nodes */
};
| > | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#if INTERFACE
/* Nodes for the paths through the DAG.
*/
struct PathNode {
int rid; /* ID for this node */
u8 fromIsParent; /* True if pFrom is the parent of rid */
u8 isPrim; /* True if primary side of common ancestor */
u8 isHidden; /* Abbreviate output in "fossil bisect ls" */
PathNode *pFrom; /* Node we came from */
union {
PathNode *pPeer; /* List of nodes of the same generation */
PathNode *pTo; /* Next on path from beginning to end */
} u;
PathNode *pAll; /* List of all nodes */
};
|
| ︙ | ︙ |
Changes to src/setup.c.
| ︙ | ︙ | |||
950 951 952 953 954 955 956 |
@ Set to zero to omit the IP address from the login cookie. A value of
@ 2 is recommended.
@ </p>
@
@ <hr />
entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766");
@ <p>The number of hours for which a login is valid. This must be a
| | | 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 |
@ Set to zero to omit the IP address from the login cookie. A value of
@ 2 is recommended.
@ </p>
@
@ <hr />
entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766");
@ <p>The number of hours for which a login is valid. This must be a
@ positive number. The default is 8766 hours which is approximately equal
@ to a year.</p>
@ <hr />
entry_attribute("Download packet limit", 10, "max-download", "mxdwn",
"5000000");
@ <p>Fossil tries to limit out-bound sync, clone, and pull packets
@ to this many bytes, uncompressed. If the client requires more data
|
| ︙ | ︙ |
Changes to src/sqlite3.c.
| ︙ | ︙ | |||
671 672 673 674 675 676 677 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.15" #define SQLITE_VERSION_NUMBER 3007015 | | | 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.15" #define SQLITE_VERSION_NUMBER 3007015 #define SQLITE_SOURCE_ID "2012-10-05 07:36:34 43155b1543bddbb84a8bc13a5b7344b228ddacb9" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version, sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
| ︙ | ︙ | |||
1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 | ** prepared statement. ^If the [SQLITE_FCNTL_PRAGMA] file control returns ** any result code other than [SQLITE_OK] or [SQLITE_NOTFOUND], that means ** that the VFS encountered an error while handling the [PRAGMA] and the ** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA] ** file control occurs at the beginning of pragma statement analysis and so ** it is able to override built-in [PRAGMA] statements. ** </ul> */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 #define SQLITE_SET_LOCKPROXYFILE 3 #define SQLITE_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 #define SQLITE_FCNTL_CHUNK_SIZE 6 #define SQLITE_FCNTL_FILE_POINTER 7 #define SQLITE_FCNTL_SYNC_OMITTED 8 #define SQLITE_FCNTL_WIN32_AV_RETRY 9 #define SQLITE_FCNTL_PERSIST_WAL 10 #define SQLITE_FCNTL_OVERWRITE 11 #define SQLITE_FCNTL_VFSNAME 12 #define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13 #define SQLITE_FCNTL_PRAGMA 14 /* ** CAPI3REF: Mutex Handle ** ** The mutex module within SQLite defines [sqlite3_mutex] to be an ** abstract type for a mutex object. The SQLite core never looks ** at the internal representation of an [sqlite3_mutex]. It only | > > > > > > > > > > > > | 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 | ** prepared statement. ^If the [SQLITE_FCNTL_PRAGMA] file control returns ** any result code other than [SQLITE_OK] or [SQLITE_NOTFOUND], that means ** that the VFS encountered an error while handling the [PRAGMA] and the ** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA] ** file control occurs at the beginning of pragma statement analysis and so ** it is able to override built-in [PRAGMA] statements. ** </ul> ** ** <li>[[SQLITE_FCNTL_BUSYHANDLER]] ** ^This file-control may be invoked by SQLite on the database file handle ** shortly after it is opened in order to provide a custom VFS with access ** to the connections busy-handler callback. The argument is of type (void **) ** - an array of two (void *) values. The first (void *) actually points ** to a function of type (int (*)(void *)). In order to invoke the connections ** busy-handler, this function should be invoked with the second (void *) in ** the array as the only argument. If it returns non-zero, then the operation ** should be retried. If it returns zero, the custom VFS should abandon the ** current operation. */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 #define SQLITE_SET_LOCKPROXYFILE 3 #define SQLITE_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 #define SQLITE_FCNTL_CHUNK_SIZE 6 #define SQLITE_FCNTL_FILE_POINTER 7 #define SQLITE_FCNTL_SYNC_OMITTED 8 #define SQLITE_FCNTL_WIN32_AV_RETRY 9 #define SQLITE_FCNTL_PERSIST_WAL 10 #define SQLITE_FCNTL_OVERWRITE 11 #define SQLITE_FCNTL_VFSNAME 12 #define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13 #define SQLITE_FCNTL_PRAGMA 14 #define SQLITE_FCNTL_BUSYHANDLER 15 /* ** CAPI3REF: Mutex Handle ** ** The mutex module within SQLite defines [sqlite3_mutex] to be an ** abstract type for a mutex object. The SQLite core never looks ** at the internal representation of an [sqlite3_mutex]. It only |
| ︙ | ︙ | |||
8379 8380 8381 8382 8383 8384 8385 8386 8387 8388 8389 8390 8391 8392 | SQLITE_PRIVATE int sqlite3BtreeSyncDisabled(Btree*); SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*); SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int); SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*); SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int); SQLITE_PRIVATE int sqlite3BtreeGetReserve(Btree*); SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int); SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *); SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int); SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*, int); SQLITE_PRIVATE int sqlite3BtreeCommit(Btree*); SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*,int); | > > > | 8391 8392 8393 8394 8395 8396 8397 8398 8399 8400 8401 8402 8403 8404 8405 8406 8407 | SQLITE_PRIVATE int sqlite3BtreeSyncDisabled(Btree*); SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*); SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int); SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*); SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int); SQLITE_PRIVATE int sqlite3BtreeGetReserve(Btree*); #if defined(SQLITE_HAS_CODEC) || defined(SQLITE_DEBUG) SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p); #endif SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int); SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *); SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int); SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*, int); SQLITE_PRIVATE int sqlite3BtreeCommit(Btree*); SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*,int); |
| ︙ | ︙ | |||
9148 9149 9150 9151 9152 9153 9154 9155 9156 9157 9158 9159 9160 9161 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); SQLITE_PRIVATE int sqlite3PagerNosync(Pager*); SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *); /* Functions used to truncate the database file. */ SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL) SQLITE_PRIVATE void *sqlite3PagerCodec(DbPage *); #endif | > | 9163 9164 9165 9166 9167 9168 9169 9170 9171 9172 9173 9174 9175 9176 9177 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); SQLITE_PRIVATE int sqlite3PagerNosync(Pager*); SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *); SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *); /* Functions used to truncate the database file. */ SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL) SQLITE_PRIVATE void *sqlite3PagerCodec(DbPage *); #endif |
| ︙ | ︙ | |||
25823 25824 25825 25826 25827 25828 25829 25830 25831 25832 25833 25834 25835 25836 |
static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
int got;
int prior = 0;
#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
i64 newOffset;
#endif
TIMER_START;
do{
#if defined(USE_PREAD)
got = osPread(id->h, pBuf, cnt, offset);
SimulateIOError( got = -1 );
#elif defined(USE_PREAD64)
got = osPread64(id->h, pBuf, cnt, offset);
SimulateIOError( got = -1 );
| > > | 25839 25840 25841 25842 25843 25844 25845 25846 25847 25848 25849 25850 25851 25852 25853 25854 |
static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
int got;
int prior = 0;
#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
i64 newOffset;
#endif
TIMER_START;
assert( cnt==(cnt&0x1ffff) );
cnt &= 0x1ffff;
do{
#if defined(USE_PREAD)
got = osPread(id->h, pBuf, cnt, offset);
SimulateIOError( got = -1 );
#elif defined(USE_PREAD64)
got = osPread64(id->h, pBuf, cnt, offset);
SimulateIOError( got = -1 );
|
| ︙ | ︙ | |||
25912 25913 25914 25915 25916 25917 25918 25919 25920 25921 25922 25923 25924 25925 |
** is set before returning.
*/
static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){
int got;
#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
i64 newOffset;
#endif
TIMER_START;
#if defined(USE_PREAD)
do{ got = osPwrite(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR );
#elif defined(USE_PREAD64)
do{ got = osPwrite64(id->h, pBuf, cnt, offset);}while( got<0 && errno==EINTR);
#else
do{
| > > | 25930 25931 25932 25933 25934 25935 25936 25937 25938 25939 25940 25941 25942 25943 25944 25945 |
** is set before returning.
*/
static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){
int got;
#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
i64 newOffset;
#endif
assert( cnt==(cnt&0x1ffff) );
cnt &= 0x1ffff;
TIMER_START;
#if defined(USE_PREAD)
do{ got = osPwrite(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR );
#elif defined(USE_PREAD64)
do{ got = osPwrite64(id->h, pBuf, cnt, offset);}while( got<0 && errno==EINTR);
#else
do{
|
| ︙ | ︙ | |||
39556 39557 39558 39559 39560 39561 39562 39563 39564 39565 39566 39567 39568 39569 |
if( rc==SQLITE_OK ){
pPager->dbFileSize = nPage;
}
}
}
return rc;
}
/*
** Set the value of the Pager.sectorSize variable for the given
** pager based on the value returned by the xSectorSize method
** of the open database file. The sector size will be used used
** to determine the size and alignment of journal header and
** master journal pointers within created journal files.
| > > > > > > > > > > > > > > > | 39576 39577 39578 39579 39580 39581 39582 39583 39584 39585 39586 39587 39588 39589 39590 39591 39592 39593 39594 39595 39596 39597 39598 39599 39600 39601 39602 39603 39604 |
if( rc==SQLITE_OK ){
pPager->dbFileSize = nPage;
}
}
}
return rc;
}
/*
** Return a sanitized version of the sector-size of OS file pFile. The
** return value is guaranteed to lie between 32 and MAX_SECTOR_SIZE.
*/
SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *pFile){
int iRet = sqlite3OsSectorSize(pFile);
if( iRet<32 ){
iRet = 512;
}else if( iRet>MAX_SECTOR_SIZE ){
assert( MAX_SECTOR_SIZE>=512 );
iRet = MAX_SECTOR_SIZE;
}
return iRet;
}
/*
** Set the value of the Pager.sectorSize variable for the given
** pager based on the value returned by the xSectorSize method
** of the open database file. The sector size will be used used
** to determine the size and alignment of journal header and
** master journal pointers within created journal files.
|
| ︙ | ︙ | |||
39592 39593 39594 39595 39596 39597 39598 |
SQLITE_IOCAP_POWERSAFE_OVERWRITE)!=0
){
/* Sector size doesn't matter for temporary files. Also, the file
** may not have been opened yet, in which case the OsSectorSize()
** call will segfault. */
pPager->sectorSize = 512;
}else{
| < < | < < < < < | 39627 39628 39629 39630 39631 39632 39633 39634 39635 39636 39637 39638 39639 39640 39641 |
SQLITE_IOCAP_POWERSAFE_OVERWRITE)!=0
){
/* Sector size doesn't matter for temporary files. Also, the file
** may not have been opened yet, in which case the OsSectorSize()
** call will segfault. */
pPager->sectorSize = 512;
}else{
pPager->sectorSize = sqlite3SectorSize(pPager->fd);
}
}
/*
** Playback the journal and thus restore the database file to
** the state it was in before we started making changes.
**
|
| ︙ | ︙ | |||
40516 40517 40518 40519 40520 40521 40522 | ** retried. If it returns zero, then the SQLITE_BUSY error is ** returned to the caller of the pager API function. */ SQLITE_PRIVATE void sqlite3PagerSetBusyhandler( Pager *pPager, /* Pager object */ int (*xBusyHandler)(void *), /* Pointer to busy-handler function */ void *pBusyHandlerArg /* Argument to pass to xBusyHandler */ | | > > > > > > > | 40544 40545 40546 40547 40548 40549 40550 40551 40552 40553 40554 40555 40556 40557 40558 40559 40560 40561 40562 40563 40564 40565 40566 40567 |
** retried. If it returns zero, then the SQLITE_BUSY error is
** returned to the caller of the pager API function.
*/
SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(
Pager *pPager, /* Pager object */
int (*xBusyHandler)(void *), /* Pointer to busy-handler function */
void *pBusyHandlerArg /* Argument to pass to xBusyHandler */
){
pPager->xBusyHandler = xBusyHandler;
pPager->pBusyHandlerArg = pBusyHandlerArg;
if( isOpen(pPager->fd) ){
void **ap = (void **)&pPager->xBusyHandler;
assert( ((int(*)(void *))(ap[0]))==xBusyHandler );
assert( ap[1]==pBusyHandlerArg );
sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap);
}
}
/*
** Change the page size used by the Pager object. The new page size
** is passed in *pPageSize.
**
** If the pager is in the error state when this function is called, it
|
| ︙ | ︙ | |||
46813 46814 46815 46816 46817 46818 46819 |
** final frame is repeated (with its commit mark) until the next sector
** boundary is crossed. Only the part of the WAL prior to the last
** sector boundary is synced; the part of the last frame that extends
** past the sector boundary is written after the sync.
*/
if( isCommit && (sync_flags & WAL_SYNC_TRANSACTIONS)!=0 ){
if( pWal->padToSectorBoundary ){
| | | 46848 46849 46850 46851 46852 46853 46854 46855 46856 46857 46858 46859 46860 46861 46862 |
** final frame is repeated (with its commit mark) until the next sector
** boundary is crossed. Only the part of the WAL prior to the last
** sector boundary is synced; the part of the last frame that extends
** past the sector boundary is written after the sync.
*/
if( isCommit && (sync_flags & WAL_SYNC_TRANSACTIONS)!=0 ){
if( pWal->padToSectorBoundary ){
int sectorSize = sqlite3SectorSize(pWal->pWalFd);
w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize;
while( iOffset<w.iSyncPoint ){
rc = walWriteOneFrame(&w, pLast, nTruncate, iOffset);
if( rc ) return rc;
iOffset += szFrame;
nExtra++;
}
|
| ︙ | ︙ | |||
50225 50226 50227 50228 50229 50230 50231 50232 50233 50234 50235 50236 50237 50238 |
/*
** Return the currently defined page size
*/
SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree *p){
return p->pBt->pageSize;
}
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM)
/*
** Return the number of bytes of space at the end of every page that
** are intentually left unused. This is the "reserved" space that is
** sometimes used by extensions.
*/
| > > > > > > > > > > > > > > > > > > | 50260 50261 50262 50263 50264 50265 50266 50267 50268 50269 50270 50271 50272 50273 50274 50275 50276 50277 50278 50279 50280 50281 50282 50283 50284 50285 50286 50287 50288 50289 50290 50291 |
/*
** Return the currently defined page size
*/
SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree *p){
return p->pBt->pageSize;
}
#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_DEBUG)
/*
** This function is similar to sqlite3BtreeGetReserve(), except that it
** may only be called if it is guaranteed that the b-tree mutex is already
** held.
**
** This is useful in one special case in the backup API code where it is
** known that the shared b-tree mutex is held, but the mutex on the
** database handle that owns *p is not. In this case if sqlite3BtreeEnter()
** were to be called, it might collide with some other operation on the
** database handle that owns *p, causing undefined behaviour.
*/
SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p){
assert( sqlite3_mutex_held(p->pBt->mutex) );
return p->pBt->pageSize - p->pBt->usableSize;
}
#endif /* SQLITE_HAS_CODEC || SQLITE_DEBUG */
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM)
/*
** Return the number of bytes of space at the end of every page that
** are intentually left unused. This is the "reserved" space that is
** sometimes used by extensions.
*/
|
| ︙ | ︙ | |||
53282 53283 53284 53285 53286 53287 53288 |
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
btreeParseCellPtr(pPage, pCell, &info);
if( info.iOverflow==0 ){
return SQLITE_OK; /* No overflow pages. Return without doing anything */
}
if( pCell+info.iOverflow+3 > pPage->aData+pPage->maskPage ){
| | | 53335 53336 53337 53338 53339 53340 53341 53342 53343 53344 53345 53346 53347 53348 53349 |
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
btreeParseCellPtr(pPage, pCell, &info);
if( info.iOverflow==0 ){
return SQLITE_OK; /* No overflow pages. Return without doing anything */
}
if( pCell+info.iOverflow+3 > pPage->aData+pPage->maskPage ){
return SQLITE_CORRUPT_BKPT; /* Cell extends past end of page */
}
ovflPgno = get4byte(&pCell[info.iOverflow]);
assert( pBt->usableSize > 4 );
ovflPageSize = pBt->usableSize - 4;
nOvfl = (info.nPayload - info.nLocal + ovflPageSize - 1)/ovflPageSize;
assert( ovflPgno==0 || nOvfl>0 );
while( nOvfl-- ){
|
| ︙ | ︙ | |||
53948 53949 53950 53951 53952 53953 53954 53955 53956 53957 53958 53959 53960 53961 |
** size of a cell stored within an internal node is always less than 1/4
** of the page-size, the aOvflSpace[] buffer is guaranteed to be large
** enough for all overflow cells.
**
** If aOvflSpace is set to a null pointer, this function returns
** SQLITE_NOMEM.
*/
static int balance_nonroot(
MemPage *pParent, /* Parent page of siblings being balanced */
int iParentIdx, /* Index of "the page" in pParent */
u8 *aOvflSpace, /* page-size bytes of space for parent ovfl */
int isRoot, /* True if pParent is a root-page */
int bBulk /* True if this call is part of a bulk load */
){
| > > > | 54001 54002 54003 54004 54005 54006 54007 54008 54009 54010 54011 54012 54013 54014 54015 54016 54017 |
** size of a cell stored within an internal node is always less than 1/4
** of the page-size, the aOvflSpace[] buffer is guaranteed to be large
** enough for all overflow cells.
**
** If aOvflSpace is set to a null pointer, this function returns
** SQLITE_NOMEM.
*/
#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM)
#pragma optimize("", off)
#endif
static int balance_nonroot(
MemPage *pParent, /* Parent page of siblings being balanced */
int iParentIdx, /* Index of "the page" in pParent */
u8 *aOvflSpace, /* page-size bytes of space for parent ovfl */
int isRoot, /* True if pParent is a root-page */
int bBulk /* True if this call is part of a bulk load */
){
|
| ︙ | ︙ | |||
54578 54579 54580 54581 54582 54583 54584 54585 54586 54587 54588 54589 54590 54591 |
}
for(i=0; i<nNew; i++){
releasePage(apNew[i]);
}
return rc;
}
/*
** This function is called when the root page of a b-tree structure is
** overfull (has one or more overflow pages).
**
** A new child page is allocated and the contents of the current root
| > > > | 54634 54635 54636 54637 54638 54639 54640 54641 54642 54643 54644 54645 54646 54647 54648 54649 54650 |
}
for(i=0; i<nNew; i++){
releasePage(apNew[i]);
}
return rc;
}
#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM)
#pragma optimize("", on)
#endif
/*
** This function is called when the root page of a b-tree structure is
** overfull (has one or more overflow pages).
**
** A new child page is allocated and the contents of the current root
|
| ︙ | ︙ | |||
56556 56557 56558 56559 56560 56561 56562 |
static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){
Pager * const pDestPager = sqlite3BtreePager(p->pDest);
const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc);
int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest);
const int nCopy = MIN(nSrcPgsz, nDestPgsz);
const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz;
#ifdef SQLITE_HAS_CODEC
| > > > | < > | 56615 56616 56617 56618 56619 56620 56621 56622 56623 56624 56625 56626 56627 56628 56629 56630 56631 56632 56633 56634 56635 56636 56637 56638 |
static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){
Pager * const pDestPager = sqlite3BtreePager(p->pDest);
const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc);
int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest);
const int nCopy = MIN(nSrcPgsz, nDestPgsz);
const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz;
#ifdef SQLITE_HAS_CODEC
/* Use BtreeGetReserveNoMutex() for the source b-tree, as although it is
** guaranteed that the shared-mutex is held by this thread, handle
** p->pSrc may not actually be the owner. */
int nSrcReserve = sqlite3BtreeGetReserveNoMutex(p->pSrc);
int nDestReserve = sqlite3BtreeGetReserve(p->pDest);
#endif
int rc = SQLITE_OK;
i64 iOff;
assert( sqlite3BtreeGetReserveNoMutex(p->pSrc)>=0 );
assert( p->bDestLocked );
assert( !isFatalError(p->rc) );
assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) );
assert( zSrcData );
/* Catch the case where the destination is an in-memory database and the
** page sizes of the source and destination differ.
|
| ︙ | ︙ | |||
91598 91599 91600 91601 91602 91603 91604 91605 91606 91607 91608 91609 91610 91611 |
** connection. If it returns SQLITE_OK, then assume that the VFS
** handled the pragma and generate a no-op prepared statement.
*/
aFcntl[0] = 0;
aFcntl[1] = zLeft;
aFcntl[2] = zRight;
aFcntl[3] = 0;
rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_PRAGMA, (void*)aFcntl);
if( rc==SQLITE_OK ){
if( aFcntl[0] ){
int mem = ++pParse->nMem;
sqlite3VdbeAddOp4(v, OP_String8, 0, mem, 0, aFcntl[0], 0);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "result", SQLITE_STATIC);
| > | 91660 91661 91662 91663 91664 91665 91666 91667 91668 91669 91670 91671 91672 91673 91674 |
** connection. If it returns SQLITE_OK, then assume that the VFS
** handled the pragma and generate a no-op prepared statement.
*/
aFcntl[0] = 0;
aFcntl[1] = zLeft;
aFcntl[2] = zRight;
aFcntl[3] = 0;
db->busyHandler.nBusy = 0;
rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_PRAGMA, (void*)aFcntl);
if( rc==SQLITE_OK ){
if( aFcntl[0] ){
int mem = ++pParse->nMem;
sqlite3VdbeAddOp4(v, OP_String8, 0, mem, 0, aFcntl[0], 0);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "result", SQLITE_STATIC);
|
| ︙ | ︙ | |||
102121 102122 102123 102124 102125 102126 102127 | #define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */ #define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */ #define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */ #define WHERE_IN_ABLE 0x000f1000 /* Able to support an IN operator */ #define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */ #define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */ #define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and x<EXPR */ | | | | | > | 102184 102185 102186 102187 102188 102189 102190 102191 102192 102193 102194 102195 102196 102197 102198 102199 102200 102201 102202 | #define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */ #define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */ #define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */ #define WHERE_IN_ABLE 0x000f1000 /* Able to support an IN operator */ #define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */ #define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */ #define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and x<EXPR */ #define WHERE_IDX_ONLY 0x00400000 /* Use index only - omit table */ #define WHERE_ORDERED 0x00800000 /* Output will appear in correct order */ #define WHERE_REVERSE 0x01000000 /* Scan in reverse order */ #define WHERE_UNIQUE 0x02000000 /* Selects no more than one row */ #define WHERE_ALL_UNIQUE 0x04000000 /* This and all prior have one row */ #define WHERE_VIRTUALTABLE 0x08000000 /* Use virtual-table processing */ #define WHERE_MULTI_OR 0x10000000 /* OR using multiple indices */ #define WHERE_TEMP_INDEX 0x20000000 /* Uses an ephemeral index */ #define WHERE_DISTINCT 0x40000000 /* Correct order for DISTINCT */ #define WHERE_COVER_SCAN 0x80000000 /* Full scan of a covering index */ /* |
| ︙ | ︙ | |||
102152 102153 102154 102155 102156 102157 102158 102159 102160 102161 102162 102163 102164 102165 | ExprList *pOrderBy; /* The ORDER BY clause */ ExprList *pDistinct; /* The select-list if query is DISTINCT */ sqlite3_index_info **ppIdxInfo; /* Index information passed to xBestIndex */ int i, n; /* Which loop is being coded; # of loops */ WhereLevel *aLevel; /* Info about outer loops */ WhereCost cost; /* Lowest cost query plan */ }; /* ** Initialize a preallocated WhereClause structure. */ static void whereClauseInit( WhereClause *pWC, /* The WhereClause to be initialized */ Parse *pParse, /* The parsing context */ | > > > > > > > > > > > | 102216 102217 102218 102219 102220 102221 102222 102223 102224 102225 102226 102227 102228 102229 102230 102231 102232 102233 102234 102235 102236 102237 102238 102239 102240 |
ExprList *pOrderBy; /* The ORDER BY clause */
ExprList *pDistinct; /* The select-list if query is DISTINCT */
sqlite3_index_info **ppIdxInfo; /* Index information passed to xBestIndex */
int i, n; /* Which loop is being coded; # of loops */
WhereLevel *aLevel; /* Info about outer loops */
WhereCost cost; /* Lowest cost query plan */
};
/*
** Return TRUE if the probe cost is less than the baseline cost
*/
static int compareCost(const WhereCost *pProbe, const WhereCost *pBaseline){
if( pProbe->rCost<pBaseline->rCost ) return 1;
if( pProbe->rCost>pBaseline->rCost ) return 0;
if( pProbe->plan.nOBSat>pBaseline->plan.nOBSat ) return 1;
if( pProbe->plan.nRow<pBaseline->plan.nRow ) return 1;
return 0;
}
/*
** Initialize a preallocated WhereClause structure.
*/
static void whereClauseInit(
WhereClause *pWC, /* The WhereClause to be initialized */
Parse *pParse, /* The parsing context */
|
| ︙ | ︙ | |||
103304 103305 103306 103307 103308 103309 103310 |
*/
static int indexIsUniqueNotNull(Index *pIdx, int nSkip){
Table *pTab = pIdx->pTable;
int i;
if( pIdx->onError==OE_None ) return 0;
for(i=nSkip; i<pIdx->nColumn; i++){
int j = pIdx->aiColumn[i];
| > | | 103379 103380 103381 103382 103383 103384 103385 103386 103387 103388 103389 103390 103391 103392 103393 103394 |
*/
static int indexIsUniqueNotNull(Index *pIdx, int nSkip){
Table *pTab = pIdx->pTable;
int i;
if( pIdx->onError==OE_None ) return 0;
for(i=nSkip; i<pIdx->nColumn; i++){
int j = pIdx->aiColumn[i];
assert( j>=0 && j<pTab->nCol );
if( pTab->aCol[j].notNull==0 ) return 0;
}
return 1;
}
/*
** This function searches the expression list passed as the second argument
** for an expression of type TK_COLUMN that refers to the same column and
|
| ︙ | ︙ | |||
103368 103369 103370 103371 103372 103373 103374 |
int base, /* Cursor number for the table pIdx is on */
ExprList *pDistinct, /* The DISTINCT expressions */
int nEqCol /* Number of index columns with == */
){
Bitmask mask = 0; /* Mask of unaccounted for pDistinct exprs */
int i; /* Iterator variable */
| > | | 103444 103445 103446 103447 103448 103449 103450 103451 103452 103453 103454 103455 103456 103457 103458 103459 |
int base, /* Cursor number for the table pIdx is on */
ExprList *pDistinct, /* The DISTINCT expressions */
int nEqCol /* Number of index columns with == */
){
Bitmask mask = 0; /* Mask of unaccounted for pDistinct exprs */
int i; /* Iterator variable */
assert( pDistinct!=0 );
if( pIdx->zName==0 || pDistinct->nExpr>=BMS ) return 0;
testcase( pDistinct->nExpr==BMS-1 );
/* Loop through all the expressions in the distinct list. If any of them
** are not simple column references, return early. Otherwise, test if the
** WHERE clause contains a "col=X" clause. If it does, the expression
** can be ignored. If it does not, and the column does not belong to the
** same table as index pIdx, return early. Finally, if there is no
|
| ︙ | ︙ | |||
103470 103471 103472 103473 103474 103475 103476 |
return 1;
}
}
return 0;
}
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 103547 103548 103549 103550 103551 103552 103553 103554 103555 103556 103557 103558 103559 103560 |
return 1;
}
}
return 0;
}
/*
** Prepare a crude estimate of the logarithm of the input value.
** The results need not be exact. This is only used for estimating
** the total cost of performing operations with O(logN) or O(NlogN)
** complexity. Because N is just a guess, it is no great tragedy if
** logN is a little off.
*/
|
| ︙ | ︙ | |||
103783 103784 103785 103786 103787 103788 103789 103790 103791 103792 103793 103794 103795 103796 |
** less than the current cost stored in pCost, replace the contents
** of pCost. */
WHERETRACE(("... multi-index OR cost=%.9g nrow=%.9g\n", rTotal, nRow));
if( rTotal<p->cost.rCost ){
p->cost.rCost = rTotal;
p->cost.used = used;
p->cost.plan.nRow = nRow;
p->cost.plan.wsFlags = flags;
p->cost.plan.u.pTerm = pTerm;
}
}
}
#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
}
| > | 103700 103701 103702 103703 103704 103705 103706 103707 103708 103709 103710 103711 103712 103713 103714 |
** less than the current cost stored in pCost, replace the contents
** of pCost. */
WHERETRACE(("... multi-index OR cost=%.9g nrow=%.9g\n", rTotal, nRow));
if( rTotal<p->cost.rCost ){
p->cost.rCost = rTotal;
p->cost.used = used;
p->cost.plan.nRow = nRow;
p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0;
p->cost.plan.wsFlags = flags;
p->cost.plan.u.pTerm = pTerm;
}
}
}
#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
}
|
| ︙ | ︙ | |||
104325 104326 104327 104328 104329 104330 104331 |
if( (SQLITE_BIG_DBL/((double)2))<rCost ){
p->cost.rCost = (SQLITE_BIG_DBL/((double)2));
}else{
p->cost.rCost = rCost;
}
p->cost.plan.u.pVtabIdx = pIdxInfo;
if( pIdxInfo->orderByConsumed ){
| | > > > | 104243 104244 104245 104246 104247 104248 104249 104250 104251 104252 104253 104254 104255 104256 104257 104258 104259 104260 |
if( (SQLITE_BIG_DBL/((double)2))<rCost ){
p->cost.rCost = (SQLITE_BIG_DBL/((double)2));
}else{
p->cost.rCost = rCost;
}
p->cost.plan.u.pVtabIdx = pIdxInfo;
if( pIdxInfo->orderByConsumed ){
p->cost.plan.wsFlags |= WHERE_ORDERED;
p->cost.plan.nOBSat = nOrderBy;
}else{
p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0;
}
p->cost.plan.nEq = 0;
pIdxInfo->nOrderBy = nOrderBy;
/* Try to find a more efficient access pattern by using multiple indexes
** to optimize an OR expression within the WHERE clause.
*/
|
| ︙ | ︙ | |||
104748 104749 104750 104751 104752 104753 104754 |
static int isOrderedColumn(WhereBestIdx *p, int iTab, int iCol, int *pbRev){
int i, j;
WhereLevel *pLevel = &p->aLevel[p->i-1];
Index *pIdx;
u8 sortOrder;
for(i=p->i-1; i>=0; i--, pLevel--){
if( pLevel->iTabCur!=iTab ) continue;
| | > > > > > | > | | | 104669 104670 104671 104672 104673 104674 104675 104676 104677 104678 104679 104680 104681 104682 104683 104684 104685 104686 104687 104688 104689 104690 104691 104692 104693 104694 104695 104696 104697 104698 |
static int isOrderedColumn(WhereBestIdx *p, int iTab, int iCol, int *pbRev){
int i, j;
WhereLevel *pLevel = &p->aLevel[p->i-1];
Index *pIdx;
u8 sortOrder;
for(i=p->i-1; i>=0; i--, pLevel--){
if( pLevel->iTabCur!=iTab ) continue;
if( (pLevel->plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
return 1;
}
if( (pLevel->plan.wsFlags & WHERE_ORDERED)==0 ){
return 0;
}
if( (pIdx = pLevel->plan.u.pIdx)!=0 ){
if( iCol<0 ){
sortOrder = 0;
testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 );
}else{
int n = pIdx->nColumn;
for(j=0; j<n; j++){
if( iCol==pIdx->aiColumn[j] ) break;
}
if( j>=n ) return 0;
sortOrder = pIdx->aSortOrder[j];
testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 );
}
}else{
if( iCol!=(-1) ) return 0;
sortOrder = 0;
testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 );
|
| ︙ | ︙ | |||
104790 104791 104792 104793 104794 104795 104796 |
** by outer loops. Return 1 if pTerm is ordered, and 0 if not.
*/
static int isOrderedTerm(WhereBestIdx *p, WhereTerm *pTerm, int *pbRev){
Expr *pExpr = pTerm->pExpr;
assert( pExpr->op==TK_EQ );
assert( pExpr->pLeft!=0 && pExpr->pLeft->op==TK_COLUMN );
assert( pExpr->pRight!=0 );
| < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 104717 104718 104719 104720 104721 104722 104723 104724 104725 104726 104727 104728 104729 104730 104731 104732 104733 104734 104735 104736 104737 104738 104739 104740 104741 104742 104743 104744 104745 104746 104747 104748 104749 104750 104751 104752 104753 104754 104755 104756 104757 104758 104759 104760 104761 104762 104763 104764 104765 104766 104767 104768 104769 104770 104771 104772 104773 104774 104775 104776 104777 104778 104779 104780 104781 104782 104783 104784 104785 104786 104787 104788 104789 104790 104791 104792 104793 104794 104795 104796 104797 104798 104799 104800 104801 104802 104803 104804 104805 104806 104807 104808 104809 104810 104811 104812 104813 104814 104815 104816 104817 104818 104819 104820 104821 104822 104823 104824 104825 104826 104827 104828 104829 104830 104831 104832 104833 104834 104835 104836 104837 104838 104839 104840 104841 104842 104843 104844 104845 104846 104847 104848 104849 104850 104851 104852 104853 104854 104855 104856 104857 104858 104859 104860 104861 104862 104863 104864 104865 104866 104867 104868 104869 104870 104871 104872 104873 104874 104875 104876 104877 104878 104879 104880 104881 104882 104883 104884 104885 104886 104887 104888 104889 104890 104891 104892 104893 104894 104895 104896 104897 104898 104899 104900 104901 104902 104903 104904 104905 104906 104907 104908 104909 104910 |
** by outer loops. Return 1 if pTerm is ordered, and 0 if not.
*/
static int isOrderedTerm(WhereBestIdx *p, WhereTerm *pTerm, int *pbRev){
Expr *pExpr = pTerm->pExpr;
assert( pExpr->op==TK_EQ );
assert( pExpr->pLeft!=0 && pExpr->pLeft->op==TK_COLUMN );
assert( pExpr->pRight!=0 );
if( pTerm->prereqRight==0 ){
return 1; /* RHS of the == is a constant */
}
if( pExpr->pRight->op==TK_COLUMN
&& isOrderedColumn(p, pExpr->pRight->iTable, pExpr->pRight->iColumn, pbRev)
){
return 1;
}
/* If we cannot prove that the constraint is ordered, assume it is not */
return 0;
}
/*
** This routine decides if pIdx can be used to satisfy the ORDER BY
** clause, either in whole or in part. The return value is the
** cumulative number of terms in the ORDER BY clause that are satisfied
** by the index pIdx and other indices in outer loops.
**
** The table being queried has a cursor number of "base". pIdx is the
** index that is postulated for use to access the table.
**
** nEqCol is the number of columns of pIdx that are used as equality
** constraints and where the other side of the == is an ordered column
** or constant. An "order column" in the previous sentence means a column
** in table from an outer loop whose values will always appear in the
** correct order due to othre index, or because the outer loop generates
** a unique result. Any of the first nEqCol columns of pIdx may be missing
** from the ORDER BY clause and the match can still be a success.
**
** The *pbRev value is set to 0 order 1 depending on whether or not
** pIdx should be run in the forward order or in reverse order.
*/
static int isSortingIndex(
WhereBestIdx *p, /* Best index search context */
Index *pIdx, /* The index we are testing */
int base, /* Cursor number for the table to be sorted */
int nEqCol, /* Number of index columns with ordered == constraints */
int wsFlags, /* Index usages flags */
int bOuterRev, /* True if outer loops scan in reverse order */
int *pbRev /* Set to 1 for reverse-order scan of pIdx */
){
int i; /* Number of pIdx terms used */
int j; /* Number of ORDER BY terms satisfied */
int sortOrder = 0; /* XOR of index and ORDER BY sort direction */
int nTerm; /* Number of ORDER BY terms */
struct ExprList_item *pTerm; /* A term of the ORDER BY clause */
ExprList *pOrderBy; /* The ORDER BY clause */
Parse *pParse = p->pParse; /* Parser context */
sqlite3 *db = pParse->db; /* Database connection */
int nPriorSat; /* ORDER BY terms satisfied by outer loops */
int seenRowid = 0; /* True if an ORDER BY rowid term is seen */
int nEqOneRow; /* Idx columns that ref unique values */
if( p->i==0 ){
nPriorSat = 0;
}else{
nPriorSat = p->aLevel[p->i-1].plan.nOBSat;
if( OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ) return nPriorSat;
}
if( nEqCol==0 ){
if( p->i && (p->aLevel[p->i-1].plan.wsFlags & WHERE_ORDERED)==0 ){
return nPriorSat;
}
nEqOneRow = 0;
}else if( p->i==0 || (p->aLevel[p->i-1].plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
nEqOneRow = nEqCol;
}else{
sortOrder = bOuterRev;
nEqOneRow = -1;
}
pOrderBy = p->pOrderBy;
assert( pOrderBy!=0 );
if( wsFlags & WHERE_COLUMN_IN ) return nPriorSat;
if( pIdx->bUnordered ) return nPriorSat;
nTerm = pOrderBy->nExpr;
assert( nTerm>0 );
/* Argument pIdx must either point to a 'real' named index structure,
** or an index structure allocated on the stack by bestBtreeIndex() to
** represent the rowid index that is part of every table. */
assert( pIdx->zName || (pIdx->nColumn==1 && pIdx->aiColumn[0]==-1) );
/* Match terms of the ORDER BY clause against columns of
** the index.
**
** Note that indices have pIdx->nColumn regular columns plus
** one additional column containing the rowid. The rowid column
** of the index is also allowed to match against the ORDER BY
** clause.
*/
for(i=0,j=nPriorSat,pTerm=&pOrderBy->a[j]; j<nTerm; i++){
Expr *pExpr; /* The expression of the ORDER BY pTerm */
CollSeq *pColl; /* The collating sequence of pExpr */
int termSortOrder; /* Sort order for this term */
int iColumn; /* The i-th column of the index. -1 for rowid */
int iSortOrder; /* 1 for DESC, 0 for ASC on the i-th index term */
const char *zColl; /* Name of the collating sequence for i-th index term */
assert( i<=pIdx->nColumn );
pExpr = pTerm->pExpr;
if( pExpr->op!=TK_COLUMN || pExpr->iTable!=base ){
/* Can not use an index sort on anything that is not a column in the
** left-most table of the FROM clause */
break;
}
pColl = sqlite3ExprCollSeq(pParse, pExpr);
if( !pColl ){
pColl = db->pDfltColl;
}
if( pIdx->zName && i<pIdx->nColumn ){
iColumn = pIdx->aiColumn[i];
if( iColumn==pIdx->pTable->iPKey ){
iColumn = -1;
}
iSortOrder = pIdx->aSortOrder[i];
zColl = pIdx->azColl[i];
}else{
iColumn = -1;
iSortOrder = 0;
zColl = pColl->zName;
}
if( pExpr->iColumn!=iColumn || sqlite3StrICmp(pColl->zName, zColl) ){
/* Term j of the ORDER BY clause does not match column i of the index */
if( i<nEqCol ){
/* If an index column that is constrained by == fails to match an
** ORDER BY term, that is OK. Just ignore that column of the index
*/
continue;
}else if( i==pIdx->nColumn ){
/* Index column i is the rowid. All other terms match. */
break;
}else{
/* If an index column fails to match and is not constrained by ==
** then the index cannot satisfy the ORDER BY constraint.
*/
return nPriorSat;
}
}
assert( pIdx->aSortOrder!=0 || iColumn==-1 );
assert( pTerm->sortOrder==0 || pTerm->sortOrder==1 );
assert( iSortOrder==0 || iSortOrder==1 );
termSortOrder = iSortOrder ^ pTerm->sortOrder;
if( i>nEqOneRow ){
if( termSortOrder!=sortOrder ){
/* Indices can only be used if all ORDER BY terms past the
** equality constraints have the correct DESC or ASC. */
break;
}
}else{
sortOrder = termSortOrder;
}
j++;
pTerm++;
if( iColumn<0 ){
seenRowid = 1;
break;
}
}
*pbRev = sortOrder;
/* If there was an "ORDER BY rowid" term that matched, or it is only
** possible for a single row from this table to match, then skip over
** any additional ORDER BY terms dealing with this table.
*/
if( seenRowid ||
( (wsFlags & WHERE_COLUMN_NULL)==0
&& i>=pIdx->nColumn
&& indexIsUniqueNotNull(pIdx, nEqCol)
)
){
/* Advance j over additional ORDER BY terms associated with base */
WhereMaskSet *pMS = p->pWC->pMaskSet;
Bitmask m = ~getMask(pMS, base);
while( j<nTerm && (exprTableUsage(pMS, pOrderBy->a[j].pExpr)&m)==0 ){
j++;
}
}
return j;
}
/*
** Find the best query plan for accessing a particular table. Write the
** best query plan and its cost into the p->cost.
**
** The lowest cost plan wins. The cost is an estimate of the amount of
** CPU and disk I/O needed to process the requested result.
|
| ︙ | ︙ | |||
104900 104901 104902 104903 104904 104905 104906 |
pIdx = 0;
}
/* Loop over all indices looking for the best one to use
*/
for(; pProbe; pIdx=pProbe=pProbe->pNext){
const tRowcnt * const aiRowEst = pProbe->aiRowEst;
| | < < < | | 104991 104992 104993 104994 104995 104996 104997 104998 104999 105000 105001 105002 105003 105004 105005 105006 105007 105008 105009 105010 105011 105012 105013 |
pIdx = 0;
}
/* Loop over all indices looking for the best one to use
*/
for(; pProbe; pIdx=pProbe=pProbe->pNext){
const tRowcnt * const aiRowEst = pProbe->aiRowEst;
WhereCost pc; /* Cost of using pProbe */
double log10N = (double)1; /* base-10 logarithm of nRow (inexact) */
int bRev = 2; /* 0=forward scan. 1=reverse. 2=undecided */
/* The following variables are populated based on the properties of
** index being evaluated. They are then used to determine the expected
** cost and number of rows returned.
**
** pc.plan.nEq:
** Number of equality terms that can be implemented using the index.
** In other words, the number of initial fields in the index that
** are used in == or IN or NOT NULL constraints of the WHERE clause.
**
** nInMul:
** The "in-multiplier". This is an estimate of how many seek operations
** SQLite must perform on the index in question. For example, if the
|
| ︙ | ︙ | |||
104958 104959 104960 104961 104962 104963 104964 |
** space to 1/16th of its original size (rangeDiv==16).
**
** bSort:
** Boolean. True if there is an ORDER BY clause that will require an
** external sort (i.e. scanning the index being evaluated will not
** correctly order records).
**
| | < | > > > > > > > | | | > | | | | | | | > | | | | | | | | | > > | > > | | | | | | | | | > | | > > > | | | | | | | | | | | | | > | | > | > | | | | | | | | > | | | | | > > | | | | | | | | | | | > | | < < < | < | < < | 105046 105047 105048 105049 105050 105051 105052 105053 105054 105055 105056 105057 105058 105059 105060 105061 105062 105063 105064 105065 105066 105067 105068 105069 105070 105071 105072 105073 105074 105075 105076 105077 105078 105079 105080 105081 105082 105083 105084 105085 105086 105087 105088 105089 105090 105091 105092 105093 105094 105095 105096 105097 105098 105099 105100 105101 105102 105103 105104 105105 105106 105107 105108 105109 105110 105111 105112 105113 105114 105115 105116 105117 105118 105119 105120 105121 105122 105123 105124 105125 105126 105127 105128 105129 105130 105131 105132 105133 105134 105135 105136 105137 105138 105139 105140 105141 105142 105143 105144 105145 105146 105147 105148 105149 105150 105151 105152 105153 105154 105155 105156 105157 105158 105159 105160 105161 105162 105163 105164 105165 105166 105167 105168 105169 105170 105171 105172 105173 105174 105175 105176 105177 105178 105179 105180 105181 105182 105183 105184 105185 105186 105187 105188 105189 105190 105191 105192 105193 105194 105195 105196 105197 105198 105199 105200 105201 105202 105203 105204 105205 105206 105207 105208 105209 105210 105211 105212 105213 105214 105215 105216 105217 105218 105219 105220 105221 105222 105223 105224 105225 105226 105227 105228 105229 105230 105231 105232 105233 105234 105235 105236 105237 105238 105239 105240 105241 105242 105243 105244 105245 105246 105247 105248 105249 105250 105251 105252 105253 105254 105255 105256 105257 105258 105259 105260 105261 105262 105263 105264 105265 105266 105267 105268 105269 105270 105271 105272 105273 105274 105275 105276 105277 105278 105279 105280 105281 105282 105283 105284 105285 105286 105287 105288 105289 105290 105291 105292 105293 105294 105295 105296 105297 105298 105299 105300 105301 105302 105303 105304 105305 105306 105307 105308 105309 105310 105311 105312 105313 105314 105315 105316 105317 105318 105319 105320 105321 105322 105323 105324 105325 105326 105327 105328 105329 105330 105331 105332 105333 105334 105335 105336 105337 105338 105339 105340 105341 105342 105343 105344 105345 105346 105347 105348 105349 105350 105351 105352 105353 105354 105355 105356 105357 105358 105359 105360 105361 105362 105363 105364 105365 105366 105367 105368 105369 105370 105371 105372 105373 105374 105375 105376 105377 105378 105379 105380 105381 105382 105383 105384 105385 105386 105387 105388 105389 105390 105391 105392 105393 105394 105395 105396 105397 105398 105399 105400 105401 105402 105403 105404 105405 105406 105407 105408 105409 105410 105411 105412 105413 105414 105415 105416 105417 105418 105419 105420 105421 105422 105423 105424 105425 105426 105427 105428 |
** space to 1/16th of its original size (rangeDiv==16).
**
** bSort:
** Boolean. True if there is an ORDER BY clause that will require an
** external sort (i.e. scanning the index being evaluated will not
** correctly order records).
**
** bDist:
** Boolean. True if there is a DISTINCT clause that will require an
** external btree.
**
** bLookup:
** Boolean. True if a table lookup is required for each index entry
** visited. In other words, true if this is not a covering index.
** This is always false for the rowid primary key index of a table.
** For other indexes, it is true unless all the columns of the table
** used by the SELECT statement are present in the index (such an
** index is sometimes described as a covering index).
** For example, given the index on (a, b), the second of the following
** two queries requires table b-tree lookups in order to find the value
** of column c, but the first does not because columns a and b are
** both available in the index.
**
** SELECT a, b FROM tbl WHERE a = 1;
** SELECT a, b, c FROM tbl WHERE a = 1;
*/
int nOrdered; /* Number of ordered terms matching index */
int bInEst = 0; /* True if "x IN (SELECT...)" seen */
int nInMul = 1; /* Number of distinct equalities to lookup */
double rangeDiv = (double)1; /* Estimated reduction in search space */
int nBound = 0; /* Number of range constraints seen */
int bSort; /* True if external sort required */
int bDist; /* True if index cannot help with DISTINCT */
int bLookup = 0; /* True if not a covering index */
int nPriorSat; /* ORDER BY terms satisfied by outer loops */
int nOrderBy; /* Number of ORDER BY terms */
WhereTerm *pTerm; /* A single term of the WHERE clause */
#ifdef SQLITE_ENABLE_STAT3
WhereTerm *pFirstTerm = 0; /* First term matching the index */
#endif
memset(&pc, 0, sizeof(pc));
nOrderBy = p->pOrderBy ? p->pOrderBy->nExpr : 0;
if( p->i ){
nPriorSat = pc.plan.nOBSat = p->aLevel[p->i-1].plan.nOBSat;
bSort = nPriorSat<nOrderBy;
bDist = 0;
}else{
nPriorSat = pc.plan.nOBSat = 0;
bSort = nOrderBy>0;
bDist = p->pDistinct!=0;
}
/* Determine the values of pc.plan.nEq and nInMul */
for(pc.plan.nEq=nOrdered=0; pc.plan.nEq<pProbe->nColumn; pc.plan.nEq++){
int j = pProbe->aiColumn[pc.plan.nEq];
pTerm = findTerm(pWC, iCur, j, p->notReady, eqTermMask, pIdx);
if( pTerm==0 ) break;
pc.plan.wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ);
testcase( pTerm->pWC!=pWC );
if( pTerm->eOperator & WO_IN ){
Expr *pExpr = pTerm->pExpr;
pc.plan.wsFlags |= WHERE_COLUMN_IN;
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
/* "x IN (SELECT ...)": Assume the SELECT returns 25 rows */
nInMul *= 25;
bInEst = 1;
}else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){
/* "x IN (value, value, ...)" */
nInMul *= pExpr->x.pList->nExpr;
}
}else if( pTerm->eOperator & WO_ISNULL ){
pc.plan.wsFlags |= WHERE_COLUMN_NULL;
if( pc.plan.nEq==nOrdered ) nOrdered++;
}else if( bSort && pc.plan.nEq==nOrdered
&& isOrderedTerm(p,pTerm,&bRev) ){
nOrdered++;
}
#ifdef SQLITE_ENABLE_STAT3
if( pc.plan.nEq==0 && pProbe->aSample ) pFirstTerm = pTerm;
#endif
pc.used |= pTerm->prereqRight;
}
/* If the index being considered is UNIQUE, and there is an equality
** constraint for all columns in the index, then this search will find
** at most a single row. In this case set the WHERE_UNIQUE flag to
** indicate this to the caller.
**
** Otherwise, if the search may find more than one row, test to see if
** there is a range constraint on indexed column (pc.plan.nEq+1) that can be
** optimized using the index.
*/
if( pc.plan.nEq==pProbe->nColumn && pProbe->onError!=OE_None ){
testcase( pc.plan.wsFlags & WHERE_COLUMN_IN );
testcase( pc.plan.wsFlags & WHERE_COLUMN_NULL );
if( (pc.plan.wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){
pc.plan.wsFlags |= WHERE_UNIQUE;
if( p->i==0 || (p->aLevel[p->i-1].plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
pc.plan.wsFlags |= WHERE_ALL_UNIQUE;
}
}
}else if( pProbe->bUnordered==0 ){
int j;
j = (pc.plan.nEq==pProbe->nColumn ? -1 : pProbe->aiColumn[pc.plan.nEq]);
if( findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){
WhereTerm *pTop, *pBtm;
pTop = findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE, pIdx);
pBtm = findTerm(pWC, iCur, j, p->notReady, WO_GT|WO_GE, pIdx);
whereRangeScanEst(pParse, pProbe, pc.plan.nEq, pBtm, pTop, &rangeDiv);
if( pTop ){
nBound = 1;
pc.plan.wsFlags |= WHERE_TOP_LIMIT;
pc.used |= pTop->prereqRight;
testcase( pTop->pWC!=pWC );
}
if( pBtm ){
nBound++;
pc.plan.wsFlags |= WHERE_BTM_LIMIT;
pc.used |= pBtm->prereqRight;
testcase( pBtm->pWC!=pWC );
}
pc.plan.wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE);
}
}
/* If there is an ORDER BY clause and the index being considered will
** naturally scan rows in the required order, set the appropriate flags
** in pc.plan.wsFlags. Otherwise, if there is an ORDER BY clause but
** the index will scan rows in a different order, set the bSort
** variable. */
assert( bRev>=0 && bRev<=2 );
if( bSort ){
testcase( bRev==0 );
testcase( bRev==1 );
testcase( bRev==2 );
pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, nOrdered,
pc.plan.wsFlags, bRev&1, &bRev);
if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_UNIQUE)!=0 ){
pc.plan.wsFlags |= WHERE_ORDERED;
}
if( nOrderBy==pc.plan.nOBSat ){
bSort = 0;
pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE;
}
if( bRev & 1 ) pc.plan.wsFlags |= WHERE_REVERSE;
}
/* If there is a DISTINCT qualifier and this index will scan rows in
** order of the DISTINCT expressions, clear bDist and set the appropriate
** flags in pc.plan.wsFlags. */
if( bDist
&& isDistinctIndex(pParse, pWC, pProbe, iCur, p->pDistinct, pc.plan.nEq)
&& (pc.plan.wsFlags & WHERE_COLUMN_IN)==0
){
bDist = 0;
pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_DISTINCT;
}
/* If currently calculating the cost of using an index (not the IPK
** index), determine if all required column data may be obtained without
** using the main table (i.e. if the index is a covering
** index for this query). If it is, set the WHERE_IDX_ONLY flag in
** pc.plan.wsFlags. Otherwise, set the bLookup variable to true. */
if( pIdx ){
Bitmask m = pSrc->colUsed;
int j;
for(j=0; j<pIdx->nColumn; j++){
int x = pIdx->aiColumn[j];
if( x<BMS-1 ){
m &= ~(((Bitmask)1)<<x);
}
}
if( m==0 ){
pc.plan.wsFlags |= WHERE_IDX_ONLY;
}else{
bLookup = 1;
}
}
/*
** Estimate the number of rows of output. For an "x IN (SELECT...)"
** constraint, do not let the estimate exceed half the rows in the table.
*/
pc.plan.nRow = (double)(aiRowEst[pc.plan.nEq] * nInMul);
if( bInEst && pc.plan.nRow*2>aiRowEst[0] ){
pc.plan.nRow = aiRowEst[0]/2;
nInMul = (int)(pc.plan.nRow / aiRowEst[pc.plan.nEq]);
}
#ifdef SQLITE_ENABLE_STAT3
/* If the constraint is of the form x=VALUE or x IN (E1,E2,...)
** and we do not think that values of x are unique and if histogram
** data is available for column x, then it might be possible
** to get a better estimate on the number of rows based on
** VALUE and how common that value is according to the histogram.
*/
if( pc.plan.nRow>(double)1 && pc.plan.nEq==1
&& pFirstTerm!=0 && aiRowEst[1]>1 ){
assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 );
if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){
testcase( pFirstTerm->eOperator==WO_EQ );
testcase( pFirstTerm->eOperator==WO_ISNULL );
whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight,
&pc.plan.nRow);
}else if( bInEst==0 ){
assert( pFirstTerm->eOperator==WO_IN );
whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList,
&pc.plan.nRow);
}
}
#endif /* SQLITE_ENABLE_STAT3 */
/* Adjust the number of output rows and downward to reflect rows
** that are excluded by range constraints.
*/
pc.plan.nRow = pc.plan.nRow/rangeDiv;
if( pc.plan.nRow<1 ) pc.plan.nRow = 1;
/* Experiments run on real SQLite databases show that the time needed
** to do a binary search to locate a row in a table or index is roughly
** log10(N) times the time to move from one row to the next row within
** a table or index. The actual times can vary, with the size of
** records being an important factor. Both moves and searches are
** slower with larger records, presumably because fewer records fit
** on one page and hence more pages have to be fetched.
**
** The ANALYZE command and the sqlite_stat1 and sqlite_stat3 tables do
** not give us data on the relative sizes of table and index records.
** So this computation assumes table records are about twice as big
** as index records
*/
if( (pc.plan.wsFlags&~(WHERE_REVERSE|WHERE_ORDERED))==WHERE_IDX_ONLY
&& (pWC->wctrlFlags & WHERE_ONEPASS_DESIRED)==0
&& sqlite3GlobalConfig.bUseCis
&& OptimizationEnabled(pParse->db, SQLITE_CoverIdxScan)
){
/* This index is not useful for indexing, but it is a covering index.
** A full-scan of the index might be a little faster than a full-scan
** of the table, so give this case a cost slightly less than a table
** scan. */
pc.rCost = aiRowEst[0]*3 + pProbe->nColumn;
pc.plan.wsFlags |= WHERE_COVER_SCAN|WHERE_COLUMN_RANGE;
}else if( (pc.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){
/* The cost of a full table scan is a number of move operations equal
** to the number of rows in the table.
**
** We add an additional 4x penalty to full table scans. This causes
** the cost function to err on the side of choosing an index over
** choosing a full scan. This 4x full-scan penalty is an arguable
** decision and one which we expect to revisit in the future. But
** it seems to be working well enough at the moment.
*/
pc.rCost = aiRowEst[0]*4;
pc.plan.wsFlags &= ~WHERE_IDX_ONLY;
if( pIdx ) pc.plan.wsFlags &= ~WHERE_ORDERED;
}else{
log10N = estLog(aiRowEst[0]);
pc.rCost = pc.plan.nRow;
if( pIdx ){
if( bLookup ){
/* For an index lookup followed by a table lookup:
** nInMul index searches to find the start of each index range
** + nRow steps through the index
** + nRow table searches to lookup the table entry using the rowid
*/
pc.rCost += (nInMul + pc.plan.nRow)*log10N;
}else{
/* For a covering index:
** nInMul index searches to find the initial entry
** + nRow steps through the index
*/
pc.rCost += nInMul*log10N;
}
}else{
/* For a rowid primary key lookup:
** nInMult table searches to find the initial entry for each range
** + nRow steps through the table
*/
pc.rCost += nInMul*log10N;
}
}
/* Add in the estimated cost of sorting the result. Actual experimental
** measurements of sorting performance in SQLite show that sorting time
** adds C*N*log10(N) to the cost, where N is the number of rows to be
** sorted and C is a factor between 1.95 and 4.3. We will split the
** difference and select C of 3.0.
*/
if( bSort ){
double m = estLog(pc.plan.nRow*(nOrderBy - pc.plan.nOBSat)/nOrderBy);
m *= (double)(pc.plan.nOBSat ? 2 : 3);
pc.rCost += pc.plan.nRow*m;
}
if( bDist ){
pc.rCost += pc.plan.nRow*estLog(pc.plan.nRow)*3;
}
/**** Cost of using this index has now been computed ****/
/* If there are additional constraints on this table that cannot
** be used with the current index, but which might lower the number
** of output rows, adjust the nRow value accordingly. This only
** matters if the current index is the least costly, so do not bother
** with this step if we already know this index will not be chosen.
** Also, never reduce the output row count below 2 using this step.
**
** It is critical that the notValid mask be used here instead of
** the notReady mask. When computing an "optimal" index, the notReady
** mask will only have one bit set - the bit for the current table.
** The notValid mask, on the other hand, always has all bits set for
** tables that are not in outer loops. If notReady is used here instead
** of notValid, then a optimal index that depends on inner joins loops
** might be selected even when there exists an optimal index that has
** no such dependency.
*/
if( pc.plan.nRow>2 && pc.rCost<=p->cost.rCost ){
int k; /* Loop counter */
int nSkipEq = pc.plan.nEq; /* Number of == constraints to skip */
int nSkipRange = nBound; /* Number of < constraints to skip */
Bitmask thisTab; /* Bitmap for pSrc */
thisTab = getMask(pWC->pMaskSet, iCur);
for(pTerm=pWC->a, k=pWC->nTerm; pc.plan.nRow>2 && k; k--, pTerm++){
if( pTerm->wtFlags & TERM_VIRTUAL ) continue;
if( (pTerm->prereqAll & p->notValid)!=thisTab ) continue;
if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){
if( nSkipEq ){
/* Ignore the first pc.plan.nEq equality matches since the index
** has already accounted for these */
nSkipEq--;
}else{
/* Assume each additional equality match reduces the result
** set size by a factor of 10 */
pc.plan.nRow /= 10;
}
}else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GT|WO_GE) ){
if( nSkipRange ){
/* Ignore the first nSkipRange range constraints since the index
** has already accounted for these */
nSkipRange--;
}else{
/* Assume each additional range constraint reduces the result
** set size by a factor of 3. Indexed range constraints reduce
** the search space by a larger factor: 4. We make indexed range
** more selective intentionally because of the subjective
** observation that indexed range constraints really are more
** selective in practice, on average. */
pc.plan.nRow /= 3;
}
}else if( pTerm->eOperator!=WO_NOOP ){
/* Any other expression lowers the output row count by half */
pc.plan.nRow /= 2;
}
}
if( pc.plan.nRow<2 ) pc.plan.nRow = 2;
}
WHERETRACE((
"%s(%s):\n"
" nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%08x\n"
" notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f\n"
" used=0x%llx nOrdered=%d nOBSat=%d\n",
pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"),
pc.plan.nEq, nInMul, (int)rangeDiv, bSort, bLookup, pc.plan.wsFlags,
p->notReady, log10N, pc.plan.nRow, pc.rCost, pc.used, nOrdered,
pc.plan.nOBSat
));
/* If this index is the best we have seen so far, then record this
** index and its cost in the p->cost structure.
*/
if( (!pIdx || pc.plan.wsFlags) && compareCost(&pc, &p->cost) ){
p->cost = pc;
p->cost.plan.wsFlags &= wsFlagMask;
p->cost.plan.u.pIdx = pIdx;
}
/* If there was an INDEXED BY clause, then only that one index is
** considered. */
if( pSrc->pIndex ) break;
|
| ︙ | ︙ | |||
105331 105332 105333 105334 105335 105336 105337 |
** in. This is used for application testing, to help find cases
** where application behaviour depends on the (undefined) order that
** SQLite outputs rows in in the absence of an ORDER BY clause. */
if( !p->pOrderBy && pParse->db->flags & SQLITE_ReverseOrder ){
p->cost.plan.wsFlags |= WHERE_REVERSE;
}
| | | < | < | 105436 105437 105438 105439 105440 105441 105442 105443 105444 105445 105446 105447 105448 105449 105450 105451 105452 105453 105454 105455 105456 105457 105458 |
** in. This is used for application testing, to help find cases
** where application behaviour depends on the (undefined) order that
** SQLite outputs rows in in the absence of an ORDER BY clause. */
if( !p->pOrderBy && pParse->db->flags & SQLITE_ReverseOrder ){
p->cost.plan.wsFlags |= WHERE_REVERSE;
}
assert( p->pOrderBy || (p->cost.plan.wsFlags&WHERE_ORDERED)==0 );
assert( p->cost.plan.u.pIdx==0 || (p->cost.plan.wsFlags&WHERE_ROWID_EQ)==0 );
assert( pSrc->pIndex==0
|| p->cost.plan.u.pIdx==0
|| p->cost.plan.u.pIdx==pSrc->pIndex
);
WHERETRACE(("best index is: %s\n",
p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk"));
bestOrClauseIndex(p);
bestAutomaticIndex(p);
p->cost.plan.wsFlags |= eqTermMask;
}
/*
|
| ︙ | ︙ | |||
106069 106070 106071 106072 106073 106074 106075 |
** query, then the caller will only allow the loop to run for
** a single iteration. This means that the first row returned
** should not have a NULL value stored in 'x'. If column 'x' is
** the first one after the nEq equality constraints in the index,
** this requires some special handling.
*/
if( (wctrlFlags&WHERE_ORDERBY_MIN)!=0
| | | 106172 106173 106174 106175 106176 106177 106178 106179 106180 106181 106182 106183 106184 106185 106186 |
** query, then the caller will only allow the loop to run for
** a single iteration. This means that the first row returned
** should not have a NULL value stored in 'x'. If column 'x' is
** the first one after the nEq equality constraints in the index,
** this requires some special handling.
*/
if( (wctrlFlags&WHERE_ORDERBY_MIN)!=0
&& (pLevel->plan.wsFlags&WHERE_ORDERED)
&& (pIdx->nColumn>nEq)
){
/* assert( pOrderBy->nExpr==1 ); */
/* assert( pOrderBy->a[0].pExpr->iColumn==pIdx->aiColumn[nEq] ); */
isMinQuery = 1;
nExtraReg = 1;
}
|
| ︙ | ︙ | |||
106890 106891 106892 106893 106894 106895 106896 |
if( (m & sWBI.notValid)==0 ){
if( j==iFrom ) iFrom++;
continue;
}
sWBI.notReady = (isOptimal ? m : sWBI.notValid);
if( sWBI.pSrc->pIndex==0 ) nUnconstrained++;
| | | | 106993 106994 106995 106996 106997 106998 106999 107000 107001 107002 107003 107004 107005 107006 107007 107008 |
if( (m & sWBI.notValid)==0 ){
if( j==iFrom ) iFrom++;
continue;
}
sWBI.notReady = (isOptimal ? m : sWBI.notValid);
if( sWBI.pSrc->pIndex==0 ) nUnconstrained++;
WHERETRACE(("=== trying table %d (%s) with isOptimal=%d ===\n",
j, sWBI.pSrc->pTab->zName, isOptimal));
assert( sWBI.pSrc->pTab );
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(sWBI.pSrc->pTab) ){
sWBI.ppIdxInfo = &pWInfo->a[j].pIdxInfo;
bestVirtualIndex(&sWBI);
}else
#endif
|
| ︙ | ︙ | |||
106932 106933 106934 106935 106936 106937 106938 |
** index specified by its INDEXED BY clause. This rule ensures
** that a best-so-far is always selected even if an impossible
** combination of INDEXED BY clauses are given. The error
** will be detected and relayed back to the application later.
** The NEVER() comes about because rule (2) above prevents
** An indexable full-table-scan from reaching rule (3).
**
| | | | < < | | > | | | | > | < < < > | 107035 107036 107037 107038 107039 107040 107041 107042 107043 107044 107045 107046 107047 107048 107049 107050 107051 107052 107053 107054 107055 107056 107057 107058 107059 107060 107061 107062 107063 107064 107065 107066 107067 107068 107069 107070 107071 107072 107073 107074 107075 107076 107077 107078 107079 107080 107081 107082 107083 107084 |
** index specified by its INDEXED BY clause. This rule ensures
** that a best-so-far is always selected even if an impossible
** combination of INDEXED BY clauses are given. The error
** will be detected and relayed back to the application later.
** The NEVER() comes about because rule (2) above prevents
** An indexable full-table-scan from reaching rule (3).
**
** (4) The plan cost must be lower than prior plans, where "cost"
** is defined by the compareCost() function above.
*/
if( (sWBI.cost.used&sWBI.notValid)==0 /* (1) */
&& (bestJ<0 || (notIndexed&m)!=0 /* (2) */
|| (bestPlan.plan.wsFlags & WHERE_NOT_FULLSCAN)==0
|| (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)
&& (nUnconstrained==0 || sWBI.pSrc->pIndex==0 /* (3) */
|| NEVER((sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0))
&& (bestJ<0 || compareCost(&sWBI.cost, &bestPlan)) /* (4) */
){
WHERETRACE(("=== table %d (%s) is best so far\n"
" cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=%08x\n",
j, sWBI.pSrc->pTab->zName,
sWBI.cost.rCost, sWBI.cost.plan.nRow,
sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags));
bestPlan = sWBI.cost;
bestJ = j;
}
if( doNotReorder ) break;
}
}
assert( bestJ>=0 );
assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) );
WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n"
" cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n",
bestJ, pTabList->a[bestJ].pTab->zName,
pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow,
bestPlan.plan.nOBSat, bestPlan.plan.wsFlags));
if( (bestPlan.plan.wsFlags & WHERE_DISTINCT)!=0 ){
assert( pWInfo->eDistinct==0 );
pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
}
andFlags &= bestPlan.plan.wsFlags;
pLevel->plan = bestPlan.plan;
pLevel->iTabCur = pTabList->a[bestJ].iCursor;
testcase( bestPlan.plan.wsFlags & WHERE_INDEXED );
testcase( bestPlan.plan.wsFlags & WHERE_TEMP_INDEX );
if( bestPlan.plan.wsFlags & (WHERE_INDEXED|WHERE_TEMP_INDEX) ){
if( (wctrlFlags & WHERE_ONETABLE_ONLY)
&& (bestPlan.plan.wsFlags & WHERE_TEMP_INDEX)==0
){
pLevel->iIdxCur = iIdxCur;
|
| ︙ | ︙ | |||
107011 107012 107013 107014 107015 107016 107017 107018 107019 107020 107021 107022 107023 107024 107025 107026 107027 107028 107029 |
}
}
}
WHERETRACE(("*** Optimizer Finished ***\n"));
if( pParse->nErr || db->mallocFailed ){
goto whereBeginError;
}
/* If the total query only selects a single row, then the ORDER BY
** clause is irrelevant.
*/
if( (andFlags & WHERE_UNIQUE)!=0 && pOrderBy ){
pWInfo->nOBSat = pOrderBy->nExpr;
}
/* If the caller is an UPDATE or DELETE statement that is requesting
** to use a one-pass algorithm, determine if this is appropriate.
** The one-pass algorithm only works if the WHERE clause constraints
** the statement to update a single row.
| > > > > > > > | 107112 107113 107114 107115 107116 107117 107118 107119 107120 107121 107122 107123 107124 107125 107126 107127 107128 107129 107130 107131 107132 107133 107134 107135 107136 107137 |
}
}
}
WHERETRACE(("*** Optimizer Finished ***\n"));
if( pParse->nErr || db->mallocFailed ){
goto whereBeginError;
}
if( nTabList ){
pLevel--;
pWInfo->nOBSat = pLevel->plan.nOBSat;
}else{
pWInfo->nOBSat = 0;
}
/* If the total query only selects a single row, then the ORDER BY
** clause is irrelevant.
*/
if( (andFlags & WHERE_UNIQUE)!=0 && pOrderBy ){
assert( nTabList==0 || (pLevel->plan.wsFlags & WHERE_ALL_UNIQUE)!=0 );
pWInfo->nOBSat = pOrderBy->nExpr;
}
/* If the caller is an UPDATE or DELETE statement that is requesting
** to use a one-pass algorithm, determine if this is appropriate.
** The one-pass algorithm only works if the WHERE clause constraints
** the statement to update a single row.
|
| ︙ | ︙ | |||
107043 107044 107045 107046 107047 107048 107049 |
for(ii=0, pLevel=pWInfo->a; ii<nTabList; ii++, pLevel++){
Table *pTab; /* Table to open */
int iDb; /* Index of database containing table/index */
struct SrcList_item *pTabItem;
pTabItem = &pTabList->a[pLevel->iFrom];
pTab = pTabItem->pTab;
| < | 107151 107152 107153 107154 107155 107156 107157 107158 107159 107160 107161 107162 107163 107164 |
for(ii=0, pLevel=pWInfo->a; ii<nTabList; ii++, pLevel++){
Table *pTab; /* Table to open */
int iDb; /* Index of database containing table/index */
struct SrcList_item *pTabItem;
pTabItem = &pTabList->a[pLevel->iFrom];
pTab = pTabItem->pTab;
pWInfo->nRowOut *= pLevel->plan.nRow;
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){
/* Do nothing */
}else
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){
|
| ︙ | ︙ | |||
114976 114977 114978 114979 114980 114981 114982 |
** operation N should be 0. The idea is that a test program (like the
** SQL Logic Test or SLT test module) can run the same SQL multiple times
** with various optimizations disabled to verify that the same answer
** is obtained in every case.
*/
case SQLITE_TESTCTRL_OPTIMIZATIONS: {
sqlite3 *db = va_arg(ap, sqlite3*);
| | | 115083 115084 115085 115086 115087 115088 115089 115090 115091 115092 115093 115094 115095 115096 115097 |
** operation N should be 0. The idea is that a test program (like the
** SQL Logic Test or SLT test module) can run the same SQL multiple times
** with various optimizations disabled to verify that the same answer
** is obtained in every case.
*/
case SQLITE_TESTCTRL_OPTIMIZATIONS: {
sqlite3 *db = va_arg(ap, sqlite3*);
db->dbOptFlags = (u8)(va_arg(ap, int) & 0xff);
break;
}
#ifdef SQLITE_N_KEYWORD
/* sqlite3_test_control(SQLITE_TESTCTRL_ISKEYWORD, const char *zWord)
**
** If zWord is a keyword recognized by the parser, then return the
|
| ︙ | ︙ | |||
135230 135231 135232 135233 135234 135235 135236 |
}
/*
** Remove the entry with rowid=iDelete from the r-tree structure.
*/
static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
int rc; /* Return code */
| | | 135337 135338 135339 135340 135341 135342 135343 135344 135345 135346 135347 135348 135349 135350 135351 |
}
/*
** Remove the entry with rowid=iDelete from the r-tree structure.
*/
static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
int rc; /* Return code */
RtreeNode *pLeaf = 0; /* Leaf node containing record iDelete */
int iCell; /* Index of iDelete cell in pLeaf */
RtreeNode *pRoot; /* Root node of rtree structure */
/* Obtain a reference to the root node to initialise Rtree.iDepth */
rc = nodeAcquire(pRtree, 1, 0, &pRoot);
|
| ︙ | ︙ | |||
135433 135434 135435 135436 135437 135438 135439 |
/* If the azData[] array contains more than one element, elements
** (azData[2]..azData[argc-1]) contain a new record to insert into
** the r-tree structure.
*/
if( rc==SQLITE_OK && nData>1 ){
/* Insert the new record into the r-tree */
| | | 135540 135541 135542 135543 135544 135545 135546 135547 135548 135549 135550 135551 135552 135553 135554 |
/* If the azData[] array contains more than one element, elements
** (azData[2]..azData[argc-1]) contain a new record to insert into
** the r-tree structure.
*/
if( rc==SQLITE_OK && nData>1 ){
/* Insert the new record into the r-tree */
RtreeNode *pLeaf = 0;
/* Figure out the rowid of the new row. */
if( bHaveRowid==0 ){
rc = newRowid(pRtree, &cell.iRowid);
}
*pRowid = cell.iRowid;
|
| ︙ | ︙ |
Changes to src/sqlite3.h.
| ︙ | ︙ | |||
105 106 107 108 109 110 111 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.15" #define SQLITE_VERSION_NUMBER 3007015 | | | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.15" #define SQLITE_VERSION_NUMBER 3007015 #define SQLITE_SOURCE_ID "2012-10-05 07:36:34 43155b1543bddbb84a8bc13a5b7344b228ddacb9" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version, sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
| ︙ | ︙ | |||
853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 | ** prepared statement. ^If the [SQLITE_FCNTL_PRAGMA] file control returns ** any result code other than [SQLITE_OK] or [SQLITE_NOTFOUND], that means ** that the VFS encountered an error while handling the [PRAGMA] and the ** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA] ** file control occurs at the beginning of pragma statement analysis and so ** it is able to override built-in [PRAGMA] statements. ** </ul> */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 #define SQLITE_SET_LOCKPROXYFILE 3 #define SQLITE_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 #define SQLITE_FCNTL_CHUNK_SIZE 6 #define SQLITE_FCNTL_FILE_POINTER 7 #define SQLITE_FCNTL_SYNC_OMITTED 8 #define SQLITE_FCNTL_WIN32_AV_RETRY 9 #define SQLITE_FCNTL_PERSIST_WAL 10 #define SQLITE_FCNTL_OVERWRITE 11 #define SQLITE_FCNTL_VFSNAME 12 #define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13 #define SQLITE_FCNTL_PRAGMA 14 /* ** CAPI3REF: Mutex Handle ** ** The mutex module within SQLite defines [sqlite3_mutex] to be an ** abstract type for a mutex object. The SQLite core never looks ** at the internal representation of an [sqlite3_mutex]. It only | > > > > > > > > > > > > | 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 | ** prepared statement. ^If the [SQLITE_FCNTL_PRAGMA] file control returns ** any result code other than [SQLITE_OK] or [SQLITE_NOTFOUND], that means ** that the VFS encountered an error while handling the [PRAGMA] and the ** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA] ** file control occurs at the beginning of pragma statement analysis and so ** it is able to override built-in [PRAGMA] statements. ** </ul> ** ** <li>[[SQLITE_FCNTL_BUSYHANDLER]] ** ^This file-control may be invoked by SQLite on the database file handle ** shortly after it is opened in order to provide a custom VFS with access ** to the connections busy-handler callback. The argument is of type (void **) ** - an array of two (void *) values. The first (void *) actually points ** to a function of type (int (*)(void *)). In order to invoke the connections ** busy-handler, this function should be invoked with the second (void *) in ** the array as the only argument. If it returns non-zero, then the operation ** should be retried. If it returns zero, the custom VFS should abandon the ** current operation. */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 #define SQLITE_SET_LOCKPROXYFILE 3 #define SQLITE_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 #define SQLITE_FCNTL_CHUNK_SIZE 6 #define SQLITE_FCNTL_FILE_POINTER 7 #define SQLITE_FCNTL_SYNC_OMITTED 8 #define SQLITE_FCNTL_WIN32_AV_RETRY 9 #define SQLITE_FCNTL_PERSIST_WAL 10 #define SQLITE_FCNTL_OVERWRITE 11 #define SQLITE_FCNTL_VFSNAME 12 #define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13 #define SQLITE_FCNTL_PRAGMA 14 #define SQLITE_FCNTL_BUSYHANDLER 15 /* ** CAPI3REF: Mutex Handle ** ** The mutex module within SQLite defines [sqlite3_mutex] to be an ** abstract type for a mutex object. The SQLite core never looks ** at the internal representation of an [sqlite3_mutex]. It only |
| ︙ | ︙ |
Changes to src/stash.c.
| ︙ | ︙ | |||
153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
static int stash_create(void){
const char *zComment; /* Comment to add to the stash */
int stashid; /* ID of the new stash */
int vid; /* Current checkout */
zComment = find_option("comment", "m", 1);
verify_all_options();
stashid = db_lget_int("stash-next", 1);
db_lset_int("stash-next", stashid+1);
vid = db_lget_int("checkout", 0);
vfile_check_signature(vid, 0, 0);
db_multi_exec(
"INSERT INTO stash(stashid,vid,comment,ctime)"
"VALUES(%d,%d,%Q,julianday('now'))",
| > > > > > > > > > > > > > > | 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 |
static int stash_create(void){
const char *zComment; /* Comment to add to the stash */
int stashid; /* ID of the new stash */
int vid; /* Current checkout */
zComment = find_option("comment", "m", 1);
verify_all_options();
if( zComment==0 ){
Blob prompt; /* Prompt for stash comment */
Blob comment; /* User comment reply */
blob_zero(&prompt);
blob_append(&prompt,
"\n"
"# Enter a description of what is being stashed. Lines beginning\n"
"# with \"#\" are ignored. Stash comments are plain text except.\n"
"# newlines are not preserved.\n",
-1);
prompt_for_user_comment(&comment, &prompt);
blob_reset(&prompt);
zComment = blob_str(&comment);
}
stashid = db_lget_int("stash-next", 1);
db_lset_int("stash-next", stashid+1);
vid = db_lget_int("checkout", 0);
vfile_check_signature(vid, 0, 0);
db_multi_exec(
"INSERT INTO stash(stashid,vid,comment,ctime)"
"VALUES(%d,%d,%Q,julianday('now'))",
|
| ︙ | ︙ | |||
265 266 267 268 269 270 271 |
nConflict);
}
}
/*
** Show the diffs associate with a single stash.
*/
| | > > > > > > > > | | > > | > > | | | | | > > > > > | > < > | | | | > | > > > | > | | 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 |
nConflict);
}
}
/*
** Show the diffs associate with a single stash.
*/
static void stash_diff(
int stashid, /* The stash entry to diff */
const char *zDiffCmd, /* Command used for diffing */
const char *zBinGlob, /* GLOB pattern to determine binary files */
int fBaseline, /* Diff against original baseline check-in if true */
int fIncludeBinary, /* Do diffs against binary files */
u64 diffFlags /* Other diff flags */
){
Stmt q;
Blob empty;
blob_zero(&empty);
db_prepare(&q,
"SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
" FROM stashfile WHERE stashid=%d",
stashid
);
while( db_step(&q)==SQLITE_ROW ){
int rid = db_column_int(&q, 0);
int isRemoved = db_column_int(&q, 1);
int isLink = db_column_int(&q, 3);
int isBin1, isBin2;
const char *zOrig = db_column_text(&q, 4);
const char *zNew = db_column_text(&q, 5);
char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
Blob delta, a, b, disk;
if( rid==0 ){
db_ephemeral_blob(&q, 6, &a);
fossil_print("ADDED %s\n", zNew);
diff_print_index(zNew, diffFlags);
isBin1 = 0;
isBin2 = fIncludeBinary ? 0 : looks_like_binary(&a);
diff_file_mem(&empty, &a, isBin1, isBin2, zNew, zDiffCmd,
zBinGlob, fIncludeBinary, diffFlags);
}else if( isRemoved ){
fossil_print("DELETE %s\n", zOrig);
if( fBaseline==0 ){
if( file_wd_islink(zOPath) ){
blob_read_link(&a, zOPath);
}else{
blob_read_from_file(&a, zOPath);
}
}else{
content_get(rid, &a);
}
diff_print_index(zNew, diffFlags);
isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a);
isBin2 = 0;
diff_file_mem(&a, &empty, isBin1, isBin2, zOrig, zDiffCmd,
zBinGlob, fIncludeBinary, diffFlags);
}else{
int isOrigLink = file_wd_islink(zOPath);
db_ephemeral_blob(&q, 6, &delta);
if( fBaseline==0 ){
if( isOrigLink ){
blob_read_link(&disk, zOPath);
}else{
blob_read_from_file(&disk, zOPath);
}
}
fossil_print("CHANGED %s\n", zNew);
if( !isOrigLink != !isLink ){
diff_print_index(zNew, diffFlags);
diff_print_filenames(zOrig, zNew, diffFlags);
printf(DIFF_CANNOT_COMPUTE_SYMLINK);
}else{
Blob *pBase = fBaseline ? &a : &disk;
content_get(rid, &a);
blob_delta_apply(&a, &delta, &b);
isBin1 = fIncludeBinary ? 0 : looks_like_binary(pBase);
isBin2 = fIncludeBinary ? 0 : looks_like_binary(&b);
diff_file_mem(fBaseline? &a : &disk, &b, isBin1, isBin2, zNew,
zDiffCmd, zBinGlob, fIncludeBinary, diffFlags);
blob_reset(&a);
blob_reset(&b);
}
if( !fBaseline ) blob_reset(&disk);
}
blob_reset(&delta);
}
db_finalize(&q);
}
/*
|
| ︙ | ︙ | |||
377 378 379 380 381 382 383 384 385 386 387 388 389 390 | ** ** fossil stash list ?--detail? ** fossil stash ls ?-l? ** ** List all changes sets currently stashed. Show information about ** individual files in each changeset if --detail or -l is used. ** ** fossil stash pop ** fossil stash apply ?STASHID? ** ** Apply STASHID or the most recently create stash to the current ** working check-out. The "pop" command deletes that changeset from ** the stash after applying it but the "apply" command retains the ** changeset. | > > > > | 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 | ** ** fossil stash list ?--detail? ** fossil stash ls ?-l? ** ** List all changes sets currently stashed. Show information about ** individual files in each changeset if --detail or -l is used. ** ** fossil stash show ?STASHID? ?DIFF-FLAGS? ** ** Show the content of a stash ** ** fossil stash pop ** fossil stash apply ?STASHID? ** ** Apply STASHID or the most recently create stash to the current ** working check-out. The "pop" command deletes that changeset from ** the stash after applying it but the "apply" command retains the ** changeset. |
| ︙ | ︙ | |||
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 |
** --all flag is used. Individual drops are undoable but --all is not.
**
** fossil stash diff ?STASHID?
** fossil stash gdiff ?STASHID?
**
** Show diffs of the current working directory and what that
** directory would be if STASHID were applied.
*/
void stash_cmd(void){
const char *zDb;
const char *zCmd;
int nCmd;
int stashid;
undo_capture_command_line();
db_must_be_within_tree();
db_begin_transaction();
zDb = db_name("localdb");
db_multi_exec(zStashInit, zDb, zDb);
if( g.argc<=2 ){
zCmd = "save";
}else{
zCmd = g.argv[2];
| > > > > > > > > > > > > > | 443 444 445 446 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 |
** --all flag is used. Individual drops are undoable but --all is not.
**
** fossil stash diff ?STASHID?
** fossil stash gdiff ?STASHID?
**
** Show diffs of the current working directory and what that
** directory would be if STASHID were applied.
**
** SUMMARY:
** fossil stash
** fossil stash save ?-m COMMENT? ?FILES...?
** fossil stash snapshot ?-m COMMENT? ?FILES...?
** fossil stash list|ls ?-l? ?--detail?
** fossil stash show ?STASHID? ?DIFF-OPTIONS?
** fossil stash pop
** fossil stash apply ?STASHID?
** fossil stash goto ?STASHID?
** fossil stash rm|drop ?STASHID? ?--all?
** fossil stash [g]diff ?STASHID? ?DIFF-OPTIONS?
*/
void stash_cmd(void){
const char *zDb;
const char *zCmd;
int nCmd;
int stashid;
undo_capture_command_line();
db_must_be_within_tree();
db_open_config(0);
db_begin_transaction();
zDb = db_name("localdb");
db_multi_exec(zStashInit, zDb, zDb);
if( g.argc<=2 ){
zCmd = "save";
}else{
zCmd = g.argv[2];
|
| ︙ | ︙ | |||
546 547 548 549 550 551 552 |
nConflict = update_to(vid);
stash_apply(stashid, nConflict);
db_multi_exec("UPDATE vfile SET mtime=0 WHERE pathname IN "
"(SELECT origname FROM stashfile WHERE stashid=%d)",
stashid);
undo_finish();
}else
| | > > > | > > | | > | | | > | | > | | > > > > | > | 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 |
nConflict = update_to(vid);
stash_apply(stashid, nConflict);
db_multi_exec("UPDATE vfile SET mtime=0 WHERE pathname IN "
"(SELECT origname FROM stashfile WHERE stashid=%d)",
stashid);
undo_finish();
}else
if( memcmp(zCmd, "diff", nCmd)==0
|| memcmp(zCmd, "gdiff", nCmd)==0
|| memcmp(zCmd, "show", nCmd)==0
){
const char *zDiffCmd = 0;
const char *zBinGlob = 0;
int fIncludeBinary = 0;
u64 diffFlags;
if( find_option("tk",0,0)!=0 ){
db_close(0);
diff_tk((zCmd[0]=='s' ? "stash show" : "stash diff"), 3);
return;
}
if( find_option("internal","i",0)==0 ){
zDiffCmd = diff_command_external(0);
}
diffFlags = diff_options();
if( g.argc>4 ) usage(mprintf("%s STASHID", zCmd));
if( zDiffCmd ){
zBinGlob = diff_get_binary_glob();
fIncludeBinary = diff_include_binary_files();
}
stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
stash_diff(stashid, zDiffCmd, zBinGlob, zCmd[0]=='s', fIncludeBinary,
diffFlags);
}else
if( memcmp(zCmd, "help", nCmd)==0 ){
g.argv[1] = "help";
g.argv[2] = "stash";
g.argc = 3;
help_cmd();
}else
{
usage("SUBCOMMAND ARGS...");
}
db_end_transaction(0);
}
|
Changes to src/th_main.c.
| ︙ | ︙ | |||
219 220 221 222 223 224 225 | /* ** TH command: hasfeature STRING ** ** Return true if the fossil binary has the given compile-time feature ** enabled. The set of features includes: ** | | > | | | 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | /* ** TH command: hasfeature STRING ** ** Return true if the fossil binary has the given compile-time feature ** enabled. The set of features includes: ** ** "json" = FOSSIL_ENABLE_JSON ** "ssl" = FOSSIL_ENABLE_SSL ** "tcl" = FOSSIL_ENABLE_TCL ** "tclStubs" = FOSSIL_ENABLE_TCL_STUBS ** */ static int hasfeatureCmd( Th_Interp *interp, void *p, int argc, const char **argv, |
| ︙ | ︙ | |||
254 255 256 257 258 259 260 261 262 263 264 265 266 267 |
rc = 1;
}
#endif
#if defined(FOSSIL_ENABLE_TCL)
else if( 0 == fossil_strnicmp( zArg, "tcl", 3 ) ){
rc = 1;
}
#endif
if( g.thTrace ){
Th_Trace("[hasfeature %#h] => %d<br />\n", argl[1], zArg, rc);
}
Th_SetResultInt(interp, rc);
return TH_OK;
}
| > > > > > | 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
rc = 1;
}
#endif
#if defined(FOSSIL_ENABLE_TCL)
else if( 0 == fossil_strnicmp( zArg, "tcl", 3 ) ){
rc = 1;
}
#endif
#if defined(FOSSIL_ENABLE_TCL_STUBS)
else if( 0 == fossil_strnicmp( zArg, "tclStubs", 8 ) ){
rc = 1;
}
#endif
if( g.thTrace ){
Th_Trace("[hasfeature %#h] => %d<br />\n", argl[1], zArg, rc);
}
Th_SetResultInt(interp, rc);
return TH_OK;
}
|
| ︙ | ︙ | |||
445 446 447 448 449 450 451 452 453 454 455 456 457 458 |
};
if( g.interp==0 ){
int i;
g.interp = Th_CreateInterp(&vtab);
th_register_language(g.interp); /* Basic scripting commands. */
#ifdef FOSSIL_ENABLE_TCL
if( getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
}
#endif
for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){
if ( !aCommand[i].zName || !aCommand[i].xProc ) continue;
Th_CreateCommand(g.interp, aCommand[i].zName, aCommand[i].xProc,
aCommand[i].pContext, 0);
| > | 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 |
};
if( g.interp==0 ){
int i;
g.interp = Th_CreateInterp(&vtab);
th_register_language(g.interp); /* Basic scripting commands. */
#ifdef FOSSIL_ENABLE_TCL
if( getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
g.tcl.setup = db_get("tcl-setup", 0); /* Grab optional setup script. */
th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
}
#endif
for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){
if ( !aCommand[i].zName || !aCommand[i].xProc ) continue;
Th_CreateCommand(g.interp, aCommand[i].zName, aCommand[i].xProc,
aCommand[i].pContext, 0);
|
| ︙ | ︙ |
Changes to src/th_tcl.c.
| ︙ | ︙ | |||
30 31 32 33 34 35 36 |
*/
#if (TCL_MAJOR_VERSION > 8) || \
((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 6))
/*
** Workaround NRE-specific issue in Tcl_EvalObjCmd (SF bug #3399564) by using
** Tcl_EvalObjv instead of invoking the objProc directly.
*/
| | | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
*/
#if (TCL_MAJOR_VERSION > 8) || \
((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 6))
/*
** Workaround NRE-specific issue in Tcl_EvalObjCmd (SF bug #3399564) by using
** Tcl_EvalObjv instead of invoking the objProc directly.
*/
# define USE_TCL_EVALOBJV 1
#endif
/*
** These macros are designed to reduce the redundant code required to marshal
** arguments from TH1 to Tcl.
*/
#define USE_ARGV_TO_OBJV() \
|
| ︙ | ︙ | |||
62 63 64 65 66 67 68 69 70 71 72 73 74 75 | /* ** Fetch the Tcl interpreter from the specified void pointer, cast to a Tcl ** context. */ #define GET_CTX_TCL_INTERP(ctx) \ ((struct TclContext *)(ctx))->interp /* ** Creates and initializes a Tcl interpreter for use with the specified TH1 ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied ** by the caller. This must be declared here because quite a few functions in ** this file need to use it before it can be defined. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 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 | /* ** Fetch the Tcl interpreter from the specified void pointer, cast to a Tcl ** context. */ #define GET_CTX_TCL_INTERP(ctx) \ ((struct TclContext *)(ctx))->interp /* ** Define the Tcl shared library name, some exported function names, and some ** cross-platform macros for use with the Tcl stubs mechanism, when enabled. */ #if defined(USE_TCL_STUBS) # if defined(_WIN32) # define WIN32_LEAN_AND_MEAN # include <windows.h> # ifndef TCL_LIBRARY_NAME # define TCL_LIBRARY_NAME "tcl86.dll\0" # endif # ifndef TCL_MINOR_OFFSET # define TCL_MINOR_OFFSET (4) # endif # ifndef dlopen # define dlopen(a,b) (void *)LoadLibrary((a)) # endif # ifndef dlsym # define dlsym(a,b) GetProcAddress((HANDLE)(a),(b)) # endif # ifndef dlclose # define dlclose(a) FreeLibrary((HANDLE)(a)) # endif # else # include <dlfcn.h> # if defined(__CYGWIN__) # ifndef TCL_LIBRARY_NAME # define TCL_LIBRARY_NAME "libtcl8.6.dll\0" # endif # ifndef TCL_MINOR_OFFSET # define TCL_MINOR_OFFSET (8) # endif # elif defined(__APPLE__) # ifndef TCL_LIBRARY_NAME # define TCL_LIBRARY_NAME "libtcl8.6.dylib\0" # endif # ifndef TCL_MINOR_OFFSET # define TCL_MINOR_OFFSET (8) # endif # else # ifndef TCL_LIBRARY_NAME # define TCL_LIBRARY_NAME "libtcl8.6.so\0" # endif # ifndef TCL_MINOR_OFFSET # define TCL_MINOR_OFFSET (8) # endif # endif /* defined(__CYGWIN__) */ # endif /* defined(_WIN32) */ # ifndef TCL_FINDEXECUTABLE_NAME # define TCL_FINDEXECUTABLE_NAME "_Tcl_FindExecutable" # endif # ifndef TCL_CREATEINTERP_NAME # define TCL_CREATEINTERP_NAME "_Tcl_CreateInterp" # endif #endif /* defined(USE_TCL_STUBS) */ /* ** The function types for Tcl_FindExecutable and Tcl_CreateInterp are needed ** when the Tcl library is being loaded dynamically by a stubs-enabled ** application (i.e. the inverse of using a stubs-enabled package). These are ** the only Tcl API functions that MUST be called prior to being able to call ** Tcl_InitStubs (i.e. because it requires a Tcl interpreter). */ typedef void (tcl_FindExecutableProc) (CONST char * argv0); typedef Tcl_Interp *(tcl_CreateInterpProc) (void); /* ** The function types for the "hook" functions to be called before and after a ** TH1 command makes a call to evaluate a Tcl script. If the "pre" function ** returns anything but TH_OK, then evaluation of the Tcl script is skipped and ** that value is used as the return code. If the "post" function returns ** anything other than its rc argument, that will become the new return code ** for the command. */ typedef int (tcl_NotifyProc) ( void *pContext, /* The context for this notification. */ Th_Interp *interp, /* The TH1 interpreter being used. */ void *ctx, /* The original TH1 command context. */ int argc, /* Number of arguments for the TH1 command. */ const char **argv, /* Array of arguments for the TH1 command. */ int *argl, /* Array of lengths for the TH1 command arguments. */ int rc /* Recommended notification return value. */ ); /* ** Creates and initializes a Tcl interpreter for use with the specified TH1 ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied ** by the caller. This must be declared here because quite a few functions in ** this file need to use it before it can be defined. */ |
| ︙ | ︙ | |||
98 99 100 101 102 103 104 |
}
/*
** Tcl context information used by TH1. This structure definition has been
** copied from and should be kept in sync with the one in "main.c".
*/
struct TclContext {
| | | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > | | > > > > | 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 245 246 247 248 249 250 251 252 253 254 255 256 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 |
}
/*
** Tcl context information used by TH1. This structure definition has been
** copied from and should be kept in sync with the one in "main.c".
*/
struct TclContext {
int argc; /* Number of original arguments. */
char **argv; /* Full copy of the original arguments. */
void *library; /* The Tcl library module handle. */
tcl_FindExecutableProc *xFindExecutable; /* Tcl_FindExecutable() pointer. */
tcl_CreateInterpProc *xCreateInterp; /* Tcl_CreateInterp() pointer. */
Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */
char *setup; /* The optional Tcl setup script. */
tcl_NotifyProc *xPreEval; /* Optional, called before Tcl_Eval*(). */
void *pPreContext; /* Optional, provided to xPreEval(). */
tcl_NotifyProc *xPostEval; /* Optional, called after Tcl_Eval*(). */
void *pPostContext; /* Optional, provided to xPostEval(). */
};
/*
** This function calls the configured xPreEval or xPostEval functions, if any.
** May have arbitrary side-effects. This function returns the result of the
** called notification function or the value of the rc argument if there is no
** notification function configured.
*/
static int notifyPreOrPostEval(
int bIsPost,
Th_Interp *interp,
void *ctx,
int argc,
const char **argv,
int *argl,
int rc
){
struct TclContext *tclContext = (struct TclContext *)ctx;
tcl_NotifyProc *xNotifyProc;
if ( !tclContext ){
Th_ErrorMessage(interp,
"Invalid Tcl context", (const char *)"", 0);
return TH_ERROR;
}
xNotifyProc = bIsPost ? tclContext->xPostEval : tclContext->xPreEval;
if ( xNotifyProc ){
rc = xNotifyProc(bIsPost ?
tclContext->pPostContext : tclContext->pPreContext,
interp, ctx, argc, argv, argl, rc);
}
return rc;
}
/*
** Syntax:
**
** tclEval arg ?arg ...?
*/
static int tclEval_command(
Th_Interp *interp,
void *ctx,
int argc,
const char **argv,
int *argl
){
Tcl_Interp *tclInterp;
Tcl_Obj *objPtr;
int rc = TH_OK;
int nResult;
const char *zResult;
if( createTclInterp(interp, ctx)!=TH_OK ){
return TH_ERROR;
}
if( argc<2 ){
return Th_WrongNumArgs(interp, "tclEval arg ?arg ...?");
}
tclInterp = GET_CTX_TCL_INTERP(ctx);
if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
return TH_ERROR;
}
rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc);
if( rc!=TH_OK ){
return rc;
}
Tcl_Preserve((ClientData)tclInterp);
if( argc==2 ){
objPtr = Tcl_NewStringObj(argv[1], argl[1]);
Tcl_IncrRefCount(objPtr);
rc = Tcl_EvalObjEx(tclInterp, objPtr, 0);
Tcl_DecrRefCount(objPtr);
}else{
USE_ARGV_TO_OBJV();
COPY_ARGV_TO_OBJV();
objPtr = Tcl_ConcatObj(objc, objv);
Tcl_IncrRefCount(objPtr);
rc = Tcl_EvalObjEx(tclInterp, objPtr, 0);
Tcl_DecrRefCount(objPtr);
FREE_ARGV_TO_OBJV();
}
zResult = getTclResult(tclInterp, &nResult);
Th_SetResult(interp, zResult, nResult);
Tcl_Release((ClientData)tclInterp);
rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, rc);
return rc;
}
/*
** Syntax:
**
** tclExpr arg ?arg ...?
*/
static int tclExpr_command(
Th_Interp *interp,
void *ctx,
int argc,
const char **argv,
int *argl
){
Tcl_Interp *tclInterp;
Tcl_Obj *objPtr;
Tcl_Obj *resultObjPtr;
int rc = TH_OK;
int nResult;
const char *zResult;
if( createTclInterp(interp, ctx)!=TH_OK ){
return TH_ERROR;
}
if( argc<2 ){
return Th_WrongNumArgs(interp, "tclExpr arg ?arg ...?");
}
tclInterp = GET_CTX_TCL_INTERP(ctx);
if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
return TH_ERROR;
}
rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc);
if( rc!=TH_OK ){
return rc;
}
Tcl_Preserve((ClientData)tclInterp);
if( argc==2 ){
objPtr = Tcl_NewStringObj(argv[1], argl[1]);
Tcl_IncrRefCount(objPtr);
rc = Tcl_ExprObj(tclInterp, objPtr, &resultObjPtr);
Tcl_DecrRefCount(objPtr);
|
| ︙ | ︙ | |||
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
zResult = Tcl_GetStringFromObj(resultObjPtr, &nResult);
}else{
zResult = getTclResult(tclInterp, &nResult);
}
Th_SetResult(interp, zResult, nResult);
if( rc==TCL_OK ) Tcl_DecrRefCount(resultObjPtr);
Tcl_Release((ClientData)tclInterp);
return rc;
}
/*
** Syntax:
**
** tclInvoke command ?arg ...?
*/
static int tclInvoke_command(
Th_Interp *interp,
void *ctx,
int argc,
const char **argv,
int *argl
){
Tcl_Interp *tclInterp;
| > | | | | > > > > | | > | 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 |
zResult = Tcl_GetStringFromObj(resultObjPtr, &nResult);
}else{
zResult = getTclResult(tclInterp, &nResult);
}
Th_SetResult(interp, zResult, nResult);
if( rc==TCL_OK ) Tcl_DecrRefCount(resultObjPtr);
Tcl_Release((ClientData)tclInterp);
rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, rc);
return rc;
}
/*
** Syntax:
**
** tclInvoke command ?arg ...?
*/
static int tclInvoke_command(
Th_Interp *interp,
void *ctx,
int argc,
const char **argv,
int *argl
){
Tcl_Interp *tclInterp;
#if !defined(USE_TCL_EVALOBJV)
Tcl_Command command;
Tcl_CmdInfo cmdInfo;
#endif
int rc = TH_OK;
int nResult;
const char *zResult;
#if !defined(USE_TCL_EVALOBJV)
Tcl_Obj *objPtr;
#endif
USE_ARGV_TO_OBJV();
if( createTclInterp(interp, ctx)!=TH_OK ){
return TH_ERROR;
}
if( argc<2 ){
return Th_WrongNumArgs(interp, "tclInvoke command ?arg ...?");
}
tclInterp = GET_CTX_TCL_INTERP(ctx);
if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
return TH_ERROR;
}
rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc);
if( rc!=TH_OK ){
return rc;
}
Tcl_Preserve((ClientData)tclInterp);
#if !defined(USE_TCL_EVALOBJV)
objPtr = Tcl_NewStringObj(argv[1], argl[1]);
Tcl_IncrRefCount(objPtr);
command = Tcl_GetCommandFromObj(tclInterp, objPtr);
if( !command || Tcl_GetCommandInfoFromToken(command,&cmdInfo)==0 ){
Th_ErrorMessage(interp, "Tcl command not found:", argv[1], argl[1]);
Tcl_DecrRefCount(objPtr);
Tcl_Release((ClientData)tclInterp);
return TH_ERROR;
}
if( !cmdInfo.objProc ){
Th_ErrorMessage(interp, "Cannot invoke Tcl command:", argv[1], argl[1]);
Tcl_DecrRefCount(objPtr);
Tcl_Release((ClientData)tclInterp);
return TH_ERROR;
}
Tcl_DecrRefCount(objPtr);
#endif
COPY_ARGV_TO_OBJV();
#if defined(USE_TCL_EVALOBJV)
rc = Tcl_EvalObjv(tclInterp, objc, objv, 0);
#else
Tcl_ResetResult(tclInterp);
rc = cmdInfo.objProc(cmdInfo.objClientData, tclInterp, objc, objv);
#endif
FREE_ARGV_TO_OBJV();
zResult = getTclResult(tclInterp, &nResult);
Th_SetResult(interp, zResult, nResult);
Tcl_Release((ClientData)tclInterp);
rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, rc);
return rc;
}
/*
** Syntax:
**
** th1Eval arg
|
| ︙ | ︙ | |||
373 374 375 376 377 378 379 380 381 382 383 384 385 386 |
Th_Interp *th1Interp = (Th_Interp *)clientData;
if( !th1Interp ) return;
/* Remove the Tcl integration commands. */
for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){
Th_RenameCommand(th1Interp, aCommand[i].zName, -1, NULL, 0);
}
}
/*
** Sets the "argv0", "argc", and "argv" script variables in the Tcl interpreter
** based on the supplied command line arguments.
*/
static int setTclArguments(
Tcl_Interp *pInterp,
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 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 |
Th_Interp *th1Interp = (Th_Interp *)clientData;
if( !th1Interp ) return;
/* Remove the Tcl integration commands. */
for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){
Th_RenameCommand(th1Interp, aCommand[i].zName, -1, NULL, 0);
}
}
/*
** When Tcl stubs support is enabled, attempts to dynamically load the Tcl
** shared library and fetch the function pointers necessary to create an
** interpreter and initialize the stubs mechanism; otherwise, simply setup
** the function pointers provided by the caller with the statically linked
** functions.
*/
static int loadTcl(
Th_Interp *interp,
void **pLibrary,
tcl_FindExecutableProc **pxFindExecutable,
tcl_CreateInterpProc **pxCreateInterp
){
#if defined(USE_TCL_STUBS)
char fileName[] = TCL_LIBRARY_NAME;
#endif
if( !pLibrary || !pxFindExecutable || !pxCreateInterp ){
Th_ErrorMessage(interp,
"Invalid Tcl loader argument(s)", (const char *)"", 0);
return TH_ERROR;
}
#if defined(USE_TCL_STUBS)
do {
void *library = dlopen(fileName, RTLD_NOW | RTLD_GLOBAL);
if( library ){
tcl_FindExecutableProc *xFindExecutable;
tcl_CreateInterpProc *xCreateInterp;
const char *procName = TCL_FINDEXECUTABLE_NAME;
xFindExecutable = (tcl_FindExecutableProc *)dlsym(library, procName + 1);
if( !xFindExecutable ){
xFindExecutable = (tcl_FindExecutableProc *)dlsym(library, procName);
}
if( !xFindExecutable ){
Th_ErrorMessage(interp,
"Could not locate Tcl_FindExecutable", (const char *)"", 0);
dlclose(library);
return TH_ERROR;
}
procName = TCL_CREATEINTERP_NAME;
xCreateInterp = (tcl_CreateInterpProc *)dlsym(library, procName + 1);
if( !xCreateInterp ){
xCreateInterp = (tcl_CreateInterpProc *)dlsym(library, procName);
}
if( !xCreateInterp ){
Th_ErrorMessage(interp,
"Could not locate Tcl_CreateInterp", (const char *)"", 0);
dlclose(library);
return TH_ERROR;
}
*pLibrary = library;
*pxFindExecutable = xFindExecutable;
*pxCreateInterp = xCreateInterp;
return TH_OK;
}
} while( --fileName[TCL_MINOR_OFFSET]>'3' ); /* Tcl 8.4+ */
Th_ErrorMessage(interp,
"Could not load Tcl shared library \"" TCL_LIBRARY_NAME "\"",
(const char *)"", 0);
return TH_ERROR;
#else
*pLibrary = 0;
*pxFindExecutable = Tcl_FindExecutable;
*pxCreateInterp = Tcl_CreateInterp;
return TH_OK;
#endif
}
/*
** Sets the "argv0", "argc", and "argv" script variables in the Tcl interpreter
** based on the supplied command line arguments.
*/
static int setTclArguments(
Tcl_Interp *pInterp,
|
| ︙ | ︙ | |||
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 |
void *pContext
){
struct TclContext *tclContext = (struct TclContext *)pContext;
int argc;
char **argv;
char *argv0 = 0;
Tcl_Interp *tclInterp;
if ( !tclContext ){
Th_ErrorMessage(interp,
"Invalid Tcl context", (const char *)"", 0);
return TH_ERROR;
}
if ( tclContext->interp ){
return TH_OK;
}
argc = tclContext->argc;
argv = tclContext->argv;
if( argc>0 && argv ){
argv0 = argv[0];
}
| > > > > > | | > > > > | > > > > > > > > > > | 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 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 709 710 711 712 713 714 715 716 717 718 719 720 721 |
void *pContext
){
struct TclContext *tclContext = (struct TclContext *)pContext;
int argc;
char **argv;
char *argv0 = 0;
Tcl_Interp *tclInterp;
char *setup;
if ( !tclContext ){
Th_ErrorMessage(interp,
"Invalid Tcl context", (const char *)"", 0);
return TH_ERROR;
}
if ( tclContext->interp ){
return TH_OK;
}
if( loadTcl(interp, &tclContext->library, &tclContext->xFindExecutable,
&tclContext->xCreateInterp)!=TH_OK ){
return TH_ERROR;
}
argc = tclContext->argc;
argv = tclContext->argv;
if( argc>0 && argv ){
argv0 = argv[0];
}
tclContext->xFindExecutable(argv0);
tclInterp = tclContext->xCreateInterp();
if( !tclInterp ||
#if defined(USE_TCL_STUBS)
!Tcl_InitStubs(tclInterp, "8.4", 0) ||
#endif
Tcl_InterpDeleted(tclInterp) ){
Th_ErrorMessage(interp,
"Could not create Tcl interpreter", (const char *)"", 0);
return TH_ERROR;
}
tclContext->interp = tclInterp;
if( Tcl_Init(tclInterp)!=TCL_OK ){
Th_ErrorMessage(interp,
"Tcl initialization error:", Tcl_GetStringResult(tclInterp), -1);
Tcl_DeleteInterp(tclInterp);
tclContext->interp = tclInterp = 0;
return TH_ERROR;
}
if( setTclArguments(tclInterp, argc, argv)!=TCL_OK ){
Th_ErrorMessage(interp,
"Tcl error setting arguments:", Tcl_GetStringResult(tclInterp), -1);
Tcl_DeleteInterp(tclInterp);
tclContext->interp = tclInterp = 0;
return TH_ERROR;
}
/* Add the TH1 integration commands to Tcl. */
Tcl_CallWhenDeleted(tclInterp, Th1DeleteProc, interp);
Tcl_CreateObjCommand(tclInterp, "th1Eval", Th1EvalObjCmd, interp, NULL);
Tcl_CreateObjCommand(tclInterp, "th1Expr", Th1ExprObjCmd, interp, NULL);
/* If necessary, evaluate the custom Tcl setup script. */
setup = tclContext->setup;
if( setup && Tcl_Eval(tclInterp, setup)!=TCL_OK ){
Th_ErrorMessage(interp,
"Tcl setup script error:", Tcl_GetStringResult(tclInterp), -1);
Tcl_DeleteInterp(tclInterp);
tclContext->interp = tclInterp = 0;
return TH_ERROR;
}
return TH_OK;
}
/*
** Register the Tcl language commands with interpreter interp.
** Usually this is called soon after interpreter creation.
*/
|
| ︙ | ︙ |
Changes to src/timeline.c.
| ︙ | ︙ | |||
915 916 917 918 919 920 921 |
int tagid; /* Tag ID */
int tmFlags; /* Timeline flags */
const char *zThisTag = 0; /* Suppress links to this tag */
const char *zThisUser = 0; /* Suppress links to this user */
HQuery url; /* URL for various branch links */
int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */
int to_rid = name_to_typed_rid(P("to"),"ci"); /* to= for path timelines */
| | | 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 |
int tagid; /* Tag ID */
int tmFlags; /* Timeline flags */
const char *zThisTag = 0; /* Suppress links to this tag */
const char *zThisUser = 0; /* Suppress links to this user */
HQuery url; /* URL for various branch links */
int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */
int to_rid = name_to_typed_rid(P("to"),"ci"); /* to= for path timelines */
int noMerge = P("shortest")==0; /* Follow merge links if shorter */
int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */
int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
int pd_rid;
double rBefore, rAfter, rCirca; /* Boundary times */
/* To view the timeline, must have permission to read project data.
*/
|
| ︙ | ︙ |
Changes to src/update.c.
| ︙ | ︙ | |||
593 594 595 596 597 598 599 | ** Get the contents of a file within the checking "revision". If ** revision==NULL then get the file content for the current checkout. */ int historical_version_of_file( const char *revision, /* The checkin containing the file */ const char *file, /* Full treename of the file */ Blob *content, /* Put the content here */ | | > > | > > > > | 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 |
** Get the contents of a file within the checking "revision". If
** revision==NULL then get the file content for the current checkout.
*/
int historical_version_of_file(
const char *revision, /* The checkin containing the file */
const char *file, /* Full treename of the file */
Blob *content, /* Put the content here */
int *pIsLink, /* Set to true if file is link. */
int *pIsExe, /* Set to true if file is executable */
int *pIsBin, /* Set to true if file is binary */
int errCode /* Error code if file not found. Panic if 0. */
){
Manifest *pManifest;
ManifestFile *pFile;
int rid=0;
if( revision ){
rid = name_to_typed_rid(revision,"ci");
}else{
rid = db_lget_int("checkout", 0);
}
if( !is_a_version(rid) ){
if( errCode>0 ) return errCode;
fossil_fatal("no such checkin: %s", revision);
}
pManifest = manifest_get(rid, CFTYPE_MANIFEST);
if( pManifest ){
pFile = manifest_file_find(pManifest, file);
if( pFile ){
int rc;
rid = uuid_to_rid(pFile->zUuid, 0);
if( pIsExe ) *pIsExe = ( manifest_file_mperm(pFile)==PERM_EXE );
if( pIsLink ) *pIsLink = ( manifest_file_mperm(pFile)==PERM_LNK );
manifest_destroy(pManifest);
rc = content_get(rid, content);
if( rc && pIsBin ){
*pIsBin = looks_like_binary(content);
}
return rc;
}
manifest_destroy(pManifest);
if( errCode<=0 ){
fossil_fatal("file %s does not exist in checkin: %s", file, revision);
}
}else if( errCode<=0 ){
if( revision==0 ){
|
| ︙ | ︙ | |||
710 711 712 713 714 715 716 |
while( db_step(&q)==SQLITE_ROW ){
int isExe = 0;
int isLink = 0;
char *zFull;
zFile = db_column_text(&q, 0);
zFull = mprintf("%/%/", g.zLocalRoot, zFile);
errCode = historical_version_of_file(zRevision, zFile, &record,
| | | 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 |
while( db_step(&q)==SQLITE_ROW ){
int isExe = 0;
int isLink = 0;
char *zFull;
zFile = db_column_text(&q, 0);
zFull = mprintf("%/%/", g.zLocalRoot, zFile);
errCode = historical_version_of_file(zRevision, zFile, &record,
&isLink, &isExe, 0, 2);
if( errCode==2 ){
if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile)==0 ){
fossil_print("UNMANAGE: %s\n", zFile);
}else{
undo_save(zFile);
file_delete(zFull);
fossil_print("DELETE: %s\n", zFile);
|
| ︙ | ︙ |
Changes to test/th1-tcl2.txt.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<th1>
#
# This is a "TH1 fragment" used to test the Tcl integration features of TH1.
# The corresponding test file executes this file using the test-th-render
# Fossil command.
#
# NOTE: This test requires that the SQLite package be available for the Tcl
# interpreter that is linked to the Fossil executable.
#
tclInvoke set repository_name [repository 1]
proc doOut {msg} {puts $msg; puts \n}
doOut [tclEval {
package require sqlite3
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<th1>
#
# This is a "TH1 fragment" used to test the Tcl integration features of TH1.
# The corresponding test file executes this file using the test-th-render
# Fossil command.
#
# NOTE: This test requires that the SQLite package be available for the Tcl
# interpreter that is linked to the Fossil executable.
#
tclInvoke set repository_name [repository 1]
proc doOut {msg} {puts $msg; puts \n}
doOut [tclEval {
package require sqlite3
sqlite3 db $repository_name -readonly true
set x [db eval {SELECT COUNT(*) FROM user;}]
db close
return $x
}]
</th1>
|
Changes to win/Makefile.mingw.
| ︙ | ︙ | |||
45 46 47 48 49 50 51 52 53 54 55 56 57 58 | #### Enable HTTPS support via OpenSSL (links to libssl and libcrypto) # # FOSSIL_ENABLE_SSL = 1 #### Enable scripting support via Tcl/Tk # # FOSSIL_ENABLE_TCL = 1 #### Use the Tcl source directory instead of the install directory? # This is useful when Tcl has been compiled statically with MinGW. # FOSSIL_TCL_SOURCE = 1 #### The directories where the zlib include and library files are located. | > > > > | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | #### Enable HTTPS support via OpenSSL (links to libssl and libcrypto) # # FOSSIL_ENABLE_SSL = 1 #### Enable scripting support via Tcl/Tk # # FOSSIL_ENABLE_TCL = 1 #### Load Tcl using the stubs mechanism # # FOSSIL_ENABLE_TCL_STUBS = 1 #### Use the Tcl source directory instead of the install directory? # This is useful when Tcl has been compiled statically with MinGW. # FOSSIL_TCL_SOURCE = 1 #### The directories where the zlib include and library files are located. |
| ︙ | ︙ | |||
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | # used if the FOSSIL_TCL_SOURCE macro is not defined. # TCLINCDIR = $(TCLDIR)/include TCLLIBDIR = $(TCLDIR)/lib #### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)? # LIBTCL = -ltcl86 #### C Compile and options for use in building executables that # will run on the target platform. This is usually the same # as BCC, unless you are cross-compiling. This C compiler builds # the finished binary for fossil. The BCC compiler above is used # for building intermediate code-generator tools. # | > > > > | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | # used if the FOSSIL_TCL_SOURCE macro is not defined. # TCLINCDIR = $(TCLDIR)/include TCLLIBDIR = $(TCLDIR)/lib #### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)? # ifdef FOSSIL_ENABLE_TCL_STUBS LIBTCL = -ltclstub86 else LIBTCL = -ltcl86 endif #### C Compile and options for use in building executables that # will run on the target platform. This is usually the same # as BCC, unless you are cross-compiling. This C compiler builds # the finished binary for fossil. The BCC compiler above is used # for building intermediate code-generator tools. # |
| ︙ | ︙ | |||
132 133 134 135 136 137 138 | # With HTTPS support ifdef FOSSIL_ENABLE_SSL TCC += -DFOSSIL_ENABLE_SSL=1 RCC += -DFOSSIL_ENABLE_SSL=1 endif | | | > > > > > > > > | 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 | # With HTTPS support ifdef FOSSIL_ENABLE_SSL TCC += -DFOSSIL_ENABLE_SSL=1 RCC += -DFOSSIL_ENABLE_SSL=1 endif # With Tcl support ifdef FOSSIL_ENABLE_TCL TCC += -DFOSSIL_ENABLE_TCL=1 RCC += -DFOSSIL_ENABLE_TCL=1 # Either statically linked or via stubs ifdef FOSSIL_ENABLE_TCL_STUBS TCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS RCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS else TCC += -DSTATIC_BUILD RCC += -DSTATIC_BUILD endif endif # With JSON support ifdef FOSSIL_ENABLE_JSON TCC += -DFOSSIL_ENABLE_JSON=1 RCC += -DFOSSIL_ENABLE_JSON=1 endif |
| ︙ | ︙ | |||
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | # LIB += -lmingwex -lz #### These libraries MUST appear in the same order as they do for Tcl # or linking with it will not work (exact reason unknown). # ifdef FOSSIL_ENABLE_TCL LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32 else LIB += -lkernel32 -lws2_32 endif #### Tcl shell for use in running the fossil test suite. This is only # used for testing. # | > > > > | 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | # LIB += -lmingwex -lz #### These libraries MUST appear in the same order as they do for Tcl # or linking with it will not work (exact reason unknown). # ifdef FOSSIL_ENABLE_TCL ifdef FOSSIL_ENABLE_TCL_STUBS LIB += -lkernel32 -lws2_32 else LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32 endif else LIB += -lkernel32 -lws2_32 endif #### Tcl shell for use in running the fossil test suite. This is only # used for testing. # |
| ︙ | ︙ |
Changes to win/Makefile.mingw.mistachkin.
| ︙ | ︙ | |||
44 45 46 47 48 49 50 | #### Enable HTTPS support via OpenSSL (links to libssl and libcrypto) # FOSSIL_ENABLE_SSL = 1 #### Enable scripting support via Tcl/Tk # | | > > > > | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | #### Enable HTTPS support via OpenSSL (links to libssl and libcrypto) # FOSSIL_ENABLE_SSL = 1 #### Enable scripting support via Tcl/Tk # FOSSIL_ENABLE_TCL = 1 #### Load Tcl using the stubs mechanism # FOSSIL_ENABLE_TCL_STUBS = 1 #### Use the Tcl source directory instead of the install directory? # This is useful when Tcl has been compiled statically with MinGW. # FOSSIL_TCL_SOURCE = 1 #### The directories where the zlib include and library files are located. |
| ︙ | ︙ | |||
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | # used if the FOSSIL_TCL_SOURCE macro is not defined. # TCLINCDIR = $(TCLDIR)/include TCLLIBDIR = $(TCLDIR)/lib #### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)? # LIBTCL = -ltcl86 #### C Compile and options for use in building executables that # will run on the target platform. This is usually the same # as BCC, unless you are cross-compiling. This C compiler builds # the finished binary for fossil. The BCC compiler above is used # for building intermediate code-generator tools. # | > > > > | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | # used if the FOSSIL_TCL_SOURCE macro is not defined. # TCLINCDIR = $(TCLDIR)/include TCLLIBDIR = $(TCLDIR)/lib #### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)? # ifdef FOSSIL_ENABLE_TCL_STUBS LIBTCL = -ltclstub86 else LIBTCL = -ltcl86 endif #### C Compile and options for use in building executables that # will run on the target platform. This is usually the same # as BCC, unless you are cross-compiling. This C compiler builds # the finished binary for fossil. The BCC compiler above is used # for building intermediate code-generator tools. # |
| ︙ | ︙ | |||
132 133 134 135 136 137 138 | # With HTTPS support ifdef FOSSIL_ENABLE_SSL TCC += -DFOSSIL_ENABLE_SSL=1 RCC += -DFOSSIL_ENABLE_SSL=1 endif | | | > > > > > > > > | 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 | # With HTTPS support ifdef FOSSIL_ENABLE_SSL TCC += -DFOSSIL_ENABLE_SSL=1 RCC += -DFOSSIL_ENABLE_SSL=1 endif # With Tcl support ifdef FOSSIL_ENABLE_TCL TCC += -DFOSSIL_ENABLE_TCL=1 RCC += -DFOSSIL_ENABLE_TCL=1 # Either statically linked or via stubs ifdef FOSSIL_ENABLE_TCL_STUBS TCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS RCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS else TCC += -DSTATIC_BUILD RCC += -DSTATIC_BUILD endif endif # With JSON support ifdef FOSSIL_ENABLE_JSON TCC += -DFOSSIL_ENABLE_JSON=1 RCC += -DFOSSIL_ENABLE_JSON=1 endif |
| ︙ | ︙ | |||
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | # LIB += -lmingwex -lz #### These libraries MUST appear in the same order as they do for Tcl # or linking with it will not work (exact reason unknown). # ifdef FOSSIL_ENABLE_TCL LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32 else LIB += -lkernel32 -lws2_32 endif #### Tcl shell for use in running the fossil test suite. This is only # used for testing. # | > > > > | 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | # LIB += -lmingwex -lz #### These libraries MUST appear in the same order as they do for Tcl # or linking with it will not work (exact reason unknown). # ifdef FOSSIL_ENABLE_TCL ifdef FOSSIL_ENABLE_TCL_STUBS LIB += -lkernel32 -lws2_32 else LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32 endif else LIB += -lkernel32 -lws2_32 endif #### Tcl shell for use in running the fossil test suite. This is only # used for testing. # |
| ︙ | ︙ |
Changes to win/fossil.rc.
| ︙ | ︙ | |||
88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
VALUE "SQLiteVersion", "SQLite " SQLITE_VERSION " " SQLITE_SOURCE_ID "\0"
VALUE "ZlibVersion", "zlib " ZLIB_VERSION "\0"
#ifdef FOSSIL_ENABLE_SSL
VALUE "SslEnabled", "Yes, " OPENSSL_VERSION_TEXT "\0"
#endif
#ifdef FOSSIL_ENABLE_TCL
VALUE "TclEnabled", "Yes, Tcl " TCL_PATCH_LEVEL "\0"
#endif
#ifdef FOSSIL_ENABLE_JSON
VALUE "JsonEnabled", "Yes, cson\0"
#endif
END
END
BLOCK "VarFileInfo"
| > > > > > | 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
VALUE "SQLiteVersion", "SQLite " SQLITE_VERSION " " SQLITE_SOURCE_ID "\0"
VALUE "ZlibVersion", "zlib " ZLIB_VERSION "\0"
#ifdef FOSSIL_ENABLE_SSL
VALUE "SslEnabled", "Yes, " OPENSSL_VERSION_TEXT "\0"
#endif
#ifdef FOSSIL_ENABLE_TCL
VALUE "TclEnabled", "Yes, Tcl " TCL_PATCH_LEVEL "\0"
#ifdef FOSSIL_ENABLE_TCL_STUBS
VALUE "TclStubsEnabled", "Yes\0"
#else
VALUE "TclStubsEnabled", "No\0"
#endif
#endif
#ifdef FOSSIL_ENABLE_JSON
VALUE "JsonEnabled", "Yes, cson\0"
#endif
END
END
BLOCK "VarFileInfo"
|
| ︙ | ︙ |