Fossil SCM
Merging from trunk. I still have merge conflicts since my previous merge.
Commit
bdeb633a6ca008ae67823e6e88ea2f5d08179fb1
Parent
b5b8448ecec0ab1…
107 files changed
+2
-1
+2
-1
+1
+1
+1
+1
+1
+10
-15
+10
-15
+36
-19
+36
-19
+36
-19
+1
-1
+1
-1
+3
+3
+61
-62
+61
-62
+61
-62
+33
-2
+33
-2
+10
-47
+10
-47
+10
-47
+7
-3
+7
-3
+7
-3
+2
-3
+2
-3
+52
-34
+52
-34
+6
-2
+6
-2
+4
-10
+4
-10
+51
-6
+51
-6
+3
-2
+3
-2
+3
-2
+1
-3
+1
-3
+1
-1
+1
-1
+1
-1
+1
-1
+17
-28
+17
-28
+1
-1
+1
-1
+34
-15
+34
-15
+71
-8
+71
-8
+71
-8
+16
-1
+16
-1
+8
+8
+38
-24
+38
-24
+2
+2
+9
+9
+6
+6
+381
-172
+381
-172
+1
-1
+1
-1
+104
-6
+104
-6
+20
-35
+20
-35
+20
-35
+73
-16
+73
-16
+2
+2
+1
-1
+35
-11
+35
-11
+10
-1
+10
-1
+19
-3
+19
-3
+26
+26
+5
-5
+5
-5
+29
-5
+29
-5
+30
-7
+30
-7
+3
-3
+3
-3
+2
-2
+2
-2
+40
+40
+2
+2
+1
+1
+7
+7
~
auto.def
~
auto.def
~
compat/zlib/configure
~
compat/zlib/configure
~
compat/zlib/configure
~
src/allrepo.c
~
src/allrepo.c
~
src/blob.c
~
src/blob.c
~
src/checkin.c
~
src/checkin.c
~
src/checkin.c
~
src/configure.c
~
src/configure.c
~
src/cson_amalgamation.h
~
src/cson_amalgamation.h
~
src/db.c
~
src/db.c
~
src/db.c
~
src/descendants.c
~
src/descendants.c
~
src/diff.c
~
src/diff.c
~
src/diff.c
~
src/doc.c
~
src/doc.c
~
src/doc.c
~
src/encode.c
~
src/encode.c
~
src/file.c
~
src/file.c
~
src/http.c
~
src/http.c
~
src/http_ssl.c
~
src/http_ssl.c
~
src/http_transport.c
~
src/http_transport.c
~
src/info.c
~
src/info.c
~
src/info.c
~
src/json.c
~
src/json.c
~
src/json_detail.h
~
src/json_detail.h
~
src/login.c
~
src/login.c
~
src/main.c
~
src/main.c
~
src/main.mk
~
src/main.mk
~
src/makemake.tcl
~
src/makemake.tcl
~
src/merge.c
~
src/merge.c
~
src/merge.c
~
src/printf.c
~
src/printf.c
~
src/rebuild.c
~
src/rebuild.c
~
src/regexp.c
~
src/regexp.c
~
src/schema.c
~
src/schema.c
~
src/setup.c
~
src/setup.c
~
src/shell.c
~
src/shell.c
~
src/sqlite3.c
~
src/sqlite3.c
~
src/sqlite3.h
~
src/sqlite3.h
~
src/stat.c
~
src/stat.c
~
src/timeline.c
~
src/timeline.c
~
src/timeline.c
~
src/tkt.c
~
src/tkt.c
~
src/tktsetup.c
~
src/tktsetup.c
~
src/unicode.c
~
src/update.c
~
src/update.c
~
src/url.c
~
src/url.c
~
src/xfer.c
~
src/xfer.c
~
test/revert.test
~
test/revert.test
~
win/Makefile.mingw
~
win/Makefile.mingw
~
win/Makefile.mingw.mistachkin
~
win/Makefile.mingw.mistachkin
~
win/Makefile.msc
~
win/Makefile.msc
~
win/include/dirent.h
~
win/include/dirent.h
~
www/build.wiki
~
www/build.wiki
~
www/fiveminutes.wiki
~
www/fiveminutes.wiki
~
www/index.wiki
~
www/index.wiki
~
www/mkindex.tcl
~
www/mkindex.tcl
~
www/permutedindex.wiki
~
www/permutedindex.wiki
M
auto.def
+2
-1
| --- auto.def | ||
| +++ auto.def | ||
| @@ -193,11 +193,11 @@ | ||
| 193 | 193 | if {$found} { |
| 194 | 194 | define FOSSIL_ENABLE_SSL |
| 195 | 195 | define-append EXTRA_CFLAGS $cflags |
| 196 | 196 | define-append EXTRA_LDFLAGS $ldflags |
| 197 | 197 | define-append LIBS -lssl -lcrypto |
| 198 | - msg-result "HTTP support enabled" | |
| 198 | + msg-result "HTTPS support enabled" | |
| 199 | 199 | |
| 200 | 200 | # Silence OpenSSL deprecation warnings on Mac OS X 10.7. |
| 201 | 201 | if {[string match *-darwin* [get-define host]]} { |
| 202 | 202 | if {[cctest -cflags {-Wdeprecated-declarations}]} { |
| 203 | 203 | define-append EXTRA_CFLAGS -Wdeprecated-declarations |
| @@ -233,8 +233,9 @@ | ||
| 233 | 233 | # Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars |
| 234 | 234 | if {![cc-check-functions getpassphrase]} { |
| 235 | 235 | # Haiku needs this |
| 236 | 236 | cc-check-function-in-lib getpass bsd |
| 237 | 237 | } |
| 238 | +cc-check-function-in-lib dlopen dl | |
| 238 | 239 | |
| 239 | 240 | make-template Makefile.in |
| 240 | 241 | make-config-header autoconfig.h -auto {USE_* FOSSIL_*} |
| 241 | 242 |
| --- auto.def | |
| +++ auto.def | |
| @@ -193,11 +193,11 @@ | |
| 193 | if {$found} { |
| 194 | define FOSSIL_ENABLE_SSL |
| 195 | define-append EXTRA_CFLAGS $cflags |
| 196 | define-append EXTRA_LDFLAGS $ldflags |
| 197 | define-append LIBS -lssl -lcrypto |
| 198 | msg-result "HTTP support enabled" |
| 199 | |
| 200 | # Silence OpenSSL deprecation warnings on Mac OS X 10.7. |
| 201 | if {[string match *-darwin* [get-define host]]} { |
| 202 | if {[cctest -cflags {-Wdeprecated-declarations}]} { |
| 203 | define-append EXTRA_CFLAGS -Wdeprecated-declarations |
| @@ -233,8 +233,9 @@ | |
| 233 | # Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars |
| 234 | if {![cc-check-functions getpassphrase]} { |
| 235 | # Haiku needs this |
| 236 | cc-check-function-in-lib getpass bsd |
| 237 | } |
| 238 | |
| 239 | make-template Makefile.in |
| 240 | make-config-header autoconfig.h -auto {USE_* FOSSIL_*} |
| 241 |
| --- auto.def | |
| +++ auto.def | |
| @@ -193,11 +193,11 @@ | |
| 193 | if {$found} { |
| 194 | define FOSSIL_ENABLE_SSL |
| 195 | define-append EXTRA_CFLAGS $cflags |
| 196 | define-append EXTRA_LDFLAGS $ldflags |
| 197 | define-append LIBS -lssl -lcrypto |
| 198 | msg-result "HTTPS support enabled" |
| 199 | |
| 200 | # Silence OpenSSL deprecation warnings on Mac OS X 10.7. |
| 201 | if {[string match *-darwin* [get-define host]]} { |
| 202 | if {[cctest -cflags {-Wdeprecated-declarations}]} { |
| 203 | define-append EXTRA_CFLAGS -Wdeprecated-declarations |
| @@ -233,8 +233,9 @@ | |
| 233 | # Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars |
| 234 | if {![cc-check-functions getpassphrase]} { |
| 235 | # Haiku needs this |
| 236 | cc-check-function-in-lib getpass bsd |
| 237 | } |
| 238 | cc-check-function-in-lib dlopen dl |
| 239 | |
| 240 | make-template Makefile.in |
| 241 | make-config-header autoconfig.h -auto {USE_* FOSSIL_*} |
| 242 |
M
auto.def
+2
-1
| --- auto.def | ||
| +++ auto.def | ||
| @@ -193,11 +193,11 @@ | ||
| 193 | 193 | if {$found} { |
| 194 | 194 | define FOSSIL_ENABLE_SSL |
| 195 | 195 | define-append EXTRA_CFLAGS $cflags |
| 196 | 196 | define-append EXTRA_LDFLAGS $ldflags |
| 197 | 197 | define-append LIBS -lssl -lcrypto |
| 198 | - msg-result "HTTP support enabled" | |
| 198 | + msg-result "HTTPS support enabled" | |
| 199 | 199 | |
| 200 | 200 | # Silence OpenSSL deprecation warnings on Mac OS X 10.7. |
| 201 | 201 | if {[string match *-darwin* [get-define host]]} { |
| 202 | 202 | if {[cctest -cflags {-Wdeprecated-declarations}]} { |
| 203 | 203 | define-append EXTRA_CFLAGS -Wdeprecated-declarations |
| @@ -233,8 +233,9 @@ | ||
| 233 | 233 | # Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars |
| 234 | 234 | if {![cc-check-functions getpassphrase]} { |
| 235 | 235 | # Haiku needs this |
| 236 | 236 | cc-check-function-in-lib getpass bsd |
| 237 | 237 | } |
| 238 | +cc-check-function-in-lib dlopen dl | |
| 238 | 239 | |
| 239 | 240 | make-template Makefile.in |
| 240 | 241 | make-config-header autoconfig.h -auto {USE_* FOSSIL_*} |
| 241 | 242 |
| --- auto.def | |
| +++ auto.def | |
| @@ -193,11 +193,11 @@ | |
| 193 | if {$found} { |
| 194 | define FOSSIL_ENABLE_SSL |
| 195 | define-append EXTRA_CFLAGS $cflags |
| 196 | define-append EXTRA_LDFLAGS $ldflags |
| 197 | define-append LIBS -lssl -lcrypto |
| 198 | msg-result "HTTP support enabled" |
| 199 | |
| 200 | # Silence OpenSSL deprecation warnings on Mac OS X 10.7. |
| 201 | if {[string match *-darwin* [get-define host]]} { |
| 202 | if {[cctest -cflags {-Wdeprecated-declarations}]} { |
| 203 | define-append EXTRA_CFLAGS -Wdeprecated-declarations |
| @@ -233,8 +233,9 @@ | |
| 233 | # Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars |
| 234 | if {![cc-check-functions getpassphrase]} { |
| 235 | # Haiku needs this |
| 236 | cc-check-function-in-lib getpass bsd |
| 237 | } |
| 238 | |
| 239 | make-template Makefile.in |
| 240 | make-config-header autoconfig.h -auto {USE_* FOSSIL_*} |
| 241 |
| --- auto.def | |
| +++ auto.def | |
| @@ -193,11 +193,11 @@ | |
| 193 | if {$found} { |
| 194 | define FOSSIL_ENABLE_SSL |
| 195 | define-append EXTRA_CFLAGS $cflags |
| 196 | define-append EXTRA_LDFLAGS $ldflags |
| 197 | define-append LIBS -lssl -lcrypto |
| 198 | msg-result "HTTPS support enabled" |
| 199 | |
| 200 | # Silence OpenSSL deprecation warnings on Mac OS X 10.7. |
| 201 | if {[string match *-darwin* [get-define host]]} { |
| 202 | if {[cctest -cflags {-Wdeprecated-declarations}]} { |
| 203 | define-append EXTRA_CFLAGS -Wdeprecated-declarations |
| @@ -233,8 +233,9 @@ | |
| 233 | # Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars |
| 234 | if {![cc-check-functions getpassphrase]} { |
| 235 | # Haiku needs this |
| 236 | cc-check-function-in-lib getpass bsd |
| 237 | } |
| 238 | cc-check-function-in-lib dlopen dl |
| 239 | |
| 240 | make-template Makefile.in |
| 241 | make-config-header autoconfig.h -auto {USE_* FOSSIL_*} |
| 242 |
| --- compat/zlib/configure | ||
| +++ compat/zlib/configure | ||
| @@ -814,5 +814,6 @@ | ||
| 814 | 814 | /^mandir *=/s#=.*#=$mandir# |
| 815 | 815 | /^LDFLAGS *=/s#=.*#=$LDFLAGS# |
| 816 | 816 | " | sed -e " |
| 817 | 817 | s/\@VERSION\@/$VER/g; |
| 818 | 818 | " > zlib.pc |
| 819 | +# | |
| 819 | 820 |
| --- compat/zlib/configure | |
| +++ compat/zlib/configure | |
| @@ -814,5 +814,6 @@ | |
| 814 | /^mandir *=/s#=.*#=$mandir# |
| 815 | /^LDFLAGS *=/s#=.*#=$LDFLAGS# |
| 816 | " | sed -e " |
| 817 | s/\@VERSION\@/$VER/g; |
| 818 | " > zlib.pc |
| 819 |
| --- compat/zlib/configure | |
| +++ compat/zlib/configure | |
| @@ -814,5 +814,6 @@ | |
| 814 | /^mandir *=/s#=.*#=$mandir# |
| 815 | /^LDFLAGS *=/s#=.*#=$LDFLAGS# |
| 816 | " | sed -e " |
| 817 | s/\@VERSION\@/$VER/g; |
| 818 | " > zlib.pc |
| 819 | # |
| 820 |
| --- compat/zlib/configure | ||
| +++ compat/zlib/configure | ||
| @@ -814,5 +814,6 @@ | ||
| 814 | 814 | /^mandir *=/s#=.*#=$mandir# |
| 815 | 815 | /^LDFLAGS *=/s#=.*#=$LDFLAGS# |
| 816 | 816 | " | sed -e " |
| 817 | 817 | s/\@VERSION\@/$VER/g; |
| 818 | 818 | " > zlib.pc |
| 819 | +# | |
| 819 | 820 |
| --- compat/zlib/configure | |
| +++ compat/zlib/configure | |
| @@ -814,5 +814,6 @@ | |
| 814 | /^mandir *=/s#=.*#=$mandir# |
| 815 | /^LDFLAGS *=/s#=.*#=$LDFLAGS# |
| 816 | " | sed -e " |
| 817 | s/\@VERSION\@/$VER/g; |
| 818 | " > zlib.pc |
| 819 |
| --- compat/zlib/configure | |
| +++ compat/zlib/configure | |
| @@ -814,5 +814,6 @@ | |
| 814 | /^mandir *=/s#=.*#=$mandir# |
| 815 | /^LDFLAGS *=/s#=.*#=$LDFLAGS# |
| 816 | " | sed -e " |
| 817 | s/\@VERSION\@/$VER/g; |
| 818 | " > zlib.pc |
| 819 | # |
| 820 |
| --- compat/zlib/configure | ||
| +++ compat/zlib/configure | ||
| @@ -814,5 +814,6 @@ | ||
| 814 | 814 | /^mandir *=/s#=.*#=$mandir# |
| 815 | 815 | /^LDFLAGS *=/s#=.*#=$LDFLAGS# |
| 816 | 816 | " | sed -e " |
| 817 | 817 | s/\@VERSION\@/$VER/g; |
| 818 | 818 | " > zlib.pc |
| 819 | +# | |
| 819 | 820 |
| --- compat/zlib/configure | |
| +++ compat/zlib/configure | |
| @@ -814,5 +814,6 @@ | |
| 814 | /^mandir *=/s#=.*#=$mandir# |
| 815 | /^LDFLAGS *=/s#=.*#=$LDFLAGS# |
| 816 | " | sed -e " |
| 817 | s/\@VERSION\@/$VER/g; |
| 818 | " > zlib.pc |
| 819 |
| --- compat/zlib/configure | |
| +++ compat/zlib/configure | |
| @@ -814,5 +814,6 @@ | |
| 814 | /^mandir *=/s#=.*#=$mandir# |
| 815 | /^LDFLAGS *=/s#=.*#=$LDFLAGS# |
| 816 | " | sed -e " |
| 817 | s/\@VERSION\@/$VER/g; |
| 818 | " > zlib.pc |
| 819 | # |
| 820 |
+1
| --- src/allrepo.c | ||
| +++ src/allrepo.c | ||
| @@ -144,10 +144,11 @@ | ||
| 144 | 144 | collect_argument(&extra, "compress"); |
| 145 | 145 | collect_argument(&extra, "noverify"); |
| 146 | 146 | collect_argument_value(&extra, "pagesize"); |
| 147 | 147 | collect_argument(&extra, "vacuum"); |
| 148 | 148 | collect_argument(&extra, "deanalyze"); |
| 149 | + collect_argument(&extra, "analyze"); | |
| 149 | 150 | collect_argument(&extra, "wal"); |
| 150 | 151 | collect_argument(&extra, "stat"); |
| 151 | 152 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 152 | 153 | zCmd = "sync -autourl -R"; |
| 153 | 154 | collect_argument(&extra, "verbose"); |
| 154 | 155 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -144,10 +144,11 @@ | |
| 144 | collect_argument(&extra, "compress"); |
| 145 | collect_argument(&extra, "noverify"); |
| 146 | collect_argument_value(&extra, "pagesize"); |
| 147 | collect_argument(&extra, "vacuum"); |
| 148 | collect_argument(&extra, "deanalyze"); |
| 149 | collect_argument(&extra, "wal"); |
| 150 | collect_argument(&extra, "stat"); |
| 151 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 152 | zCmd = "sync -autourl -R"; |
| 153 | collect_argument(&extra, "verbose"); |
| 154 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -144,10 +144,11 @@ | |
| 144 | collect_argument(&extra, "compress"); |
| 145 | collect_argument(&extra, "noverify"); |
| 146 | collect_argument_value(&extra, "pagesize"); |
| 147 | collect_argument(&extra, "vacuum"); |
| 148 | collect_argument(&extra, "deanalyze"); |
| 149 | collect_argument(&extra, "analyze"); |
| 150 | collect_argument(&extra, "wal"); |
| 151 | collect_argument(&extra, "stat"); |
| 152 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 153 | zCmd = "sync -autourl -R"; |
| 154 | collect_argument(&extra, "verbose"); |
| 155 |
+1
| --- src/allrepo.c | ||
| +++ src/allrepo.c | ||
| @@ -144,10 +144,11 @@ | ||
| 144 | 144 | collect_argument(&extra, "compress"); |
| 145 | 145 | collect_argument(&extra, "noverify"); |
| 146 | 146 | collect_argument_value(&extra, "pagesize"); |
| 147 | 147 | collect_argument(&extra, "vacuum"); |
| 148 | 148 | collect_argument(&extra, "deanalyze"); |
| 149 | + collect_argument(&extra, "analyze"); | |
| 149 | 150 | collect_argument(&extra, "wal"); |
| 150 | 151 | collect_argument(&extra, "stat"); |
| 151 | 152 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 152 | 153 | zCmd = "sync -autourl -R"; |
| 153 | 154 | collect_argument(&extra, "verbose"); |
| 154 | 155 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -144,10 +144,11 @@ | |
| 144 | collect_argument(&extra, "compress"); |
| 145 | collect_argument(&extra, "noverify"); |
| 146 | collect_argument_value(&extra, "pagesize"); |
| 147 | collect_argument(&extra, "vacuum"); |
| 148 | collect_argument(&extra, "deanalyze"); |
| 149 | collect_argument(&extra, "wal"); |
| 150 | collect_argument(&extra, "stat"); |
| 151 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 152 | zCmd = "sync -autourl -R"; |
| 153 | collect_argument(&extra, "verbose"); |
| 154 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -144,10 +144,11 @@ | |
| 144 | collect_argument(&extra, "compress"); |
| 145 | collect_argument(&extra, "noverify"); |
| 146 | collect_argument_value(&extra, "pagesize"); |
| 147 | collect_argument(&extra, "vacuum"); |
| 148 | collect_argument(&extra, "deanalyze"); |
| 149 | collect_argument(&extra, "analyze"); |
| 150 | collect_argument(&extra, "wal"); |
| 151 | collect_argument(&extra, "stat"); |
| 152 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 153 | zCmd = "sync -autourl -R"; |
| 154 | collect_argument(&extra, "verbose"); |
| 155 |
+10
-15
| --- src/blob.c | ||
| +++ src/blob.c | ||
| @@ -1104,26 +1104,21 @@ | ||
| 1104 | 1104 | blob_zero(&temp); |
| 1105 | 1105 | blob_append(&temp, zUtf8, -1); |
| 1106 | 1106 | blob_swap(pBlob, &temp); |
| 1107 | 1107 | blob_reset(&temp); |
| 1108 | 1108 | #ifdef _WIN32 |
| 1109 | - }else if( starts_with_utf16le_bom(pBlob, &bomSize) ){ | |
| 1110 | - /* Make sure the blob contains two terminating 0-bytes */ | |
| 1111 | - blob_append(pBlob, "", 1); | |
| 1112 | - zUtf8 = blob_str(pBlob) + bomSize; | |
| 1113 | - zUtf8 = fossil_unicode_to_utf8(zUtf8); | |
| 1114 | - blob_zero(pBlob); | |
| 1115 | - blob_append(pBlob, zUtf8, -1); | |
| 1116 | - fossil_unicode_free(zUtf8); | |
| 1117 | - }else if( starts_with_utf16be_bom(pBlob, &bomSize) ){ | |
| 1118 | - unsigned int i = blob_size(pBlob); | |
| 1109 | + }else if( starts_with_utf16_bom(pBlob, &bomSize) ){ | |
| 1119 | 1110 | zUtf8 = blob_buffer(pBlob); |
| 1120 | - while( i > 0 ){ | |
| 1121 | - /* swap bytes of unicode representation */ | |
| 1122 | - char zTemp = zUtf8[--i]; | |
| 1123 | - zUtf8[i] = zUtf8[i-1]; | |
| 1124 | - zUtf8[--i] = zTemp; | |
| 1111 | + if (*((unsigned short *)zUtf8) == 0xfffe) { | |
| 1112 | + /* Found BOM, but with reversed bytes */ | |
| 1113 | + unsigned int i = blob_size(pBlob); | |
| 1114 | + while( i > 0 ){ | |
| 1115 | + /* swap bytes of unicode representation */ | |
| 1116 | + char zTemp = zUtf8[--i]; | |
| 1117 | + zUtf8[i] = zUtf8[i-1]; | |
| 1118 | + zUtf8[--i] = zTemp; | |
| 1119 | + } | |
| 1125 | 1120 | } |
| 1126 | 1121 | /* Make sure the blob contains two terminating 0-bytes */ |
| 1127 | 1122 | blob_append(pBlob, "", 1); |
| 1128 | 1123 | zUtf8 = blob_str(pBlob) + bomSize; |
| 1129 | 1124 | zUtf8 = fossil_unicode_to_utf8(zUtf8); |
| 1130 | 1125 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -1104,26 +1104,21 @@ | |
| 1104 | blob_zero(&temp); |
| 1105 | blob_append(&temp, zUtf8, -1); |
| 1106 | blob_swap(pBlob, &temp); |
| 1107 | blob_reset(&temp); |
| 1108 | #ifdef _WIN32 |
| 1109 | }else if( starts_with_utf16le_bom(pBlob, &bomSize) ){ |
| 1110 | /* Make sure the blob contains two terminating 0-bytes */ |
| 1111 | blob_append(pBlob, "", 1); |
| 1112 | zUtf8 = blob_str(pBlob) + bomSize; |
| 1113 | zUtf8 = fossil_unicode_to_utf8(zUtf8); |
| 1114 | blob_zero(pBlob); |
| 1115 | blob_append(pBlob, zUtf8, -1); |
| 1116 | fossil_unicode_free(zUtf8); |
| 1117 | }else if( starts_with_utf16be_bom(pBlob, &bomSize) ){ |
| 1118 | unsigned int i = blob_size(pBlob); |
| 1119 | zUtf8 = blob_buffer(pBlob); |
| 1120 | while( i > 0 ){ |
| 1121 | /* swap bytes of unicode representation */ |
| 1122 | char zTemp = zUtf8[--i]; |
| 1123 | zUtf8[i] = zUtf8[i-1]; |
| 1124 | zUtf8[--i] = zTemp; |
| 1125 | } |
| 1126 | /* Make sure the blob contains two terminating 0-bytes */ |
| 1127 | blob_append(pBlob, "", 1); |
| 1128 | zUtf8 = blob_str(pBlob) + bomSize; |
| 1129 | zUtf8 = fossil_unicode_to_utf8(zUtf8); |
| 1130 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -1104,26 +1104,21 @@ | |
| 1104 | blob_zero(&temp); |
| 1105 | blob_append(&temp, zUtf8, -1); |
| 1106 | blob_swap(pBlob, &temp); |
| 1107 | blob_reset(&temp); |
| 1108 | #ifdef _WIN32 |
| 1109 | }else if( starts_with_utf16_bom(pBlob, &bomSize) ){ |
| 1110 | zUtf8 = blob_buffer(pBlob); |
| 1111 | if (*((unsigned short *)zUtf8) == 0xfffe) { |
| 1112 | /* Found BOM, but with reversed bytes */ |
| 1113 | unsigned int i = blob_size(pBlob); |
| 1114 | while( i > 0 ){ |
| 1115 | /* swap bytes of unicode representation */ |
| 1116 | char zTemp = zUtf8[--i]; |
| 1117 | zUtf8[i] = zUtf8[i-1]; |
| 1118 | zUtf8[--i] = zTemp; |
| 1119 | } |
| 1120 | } |
| 1121 | /* Make sure the blob contains two terminating 0-bytes */ |
| 1122 | blob_append(pBlob, "", 1); |
| 1123 | zUtf8 = blob_str(pBlob) + bomSize; |
| 1124 | zUtf8 = fossil_unicode_to_utf8(zUtf8); |
| 1125 |
+10
-15
| --- src/blob.c | ||
| +++ src/blob.c | ||
| @@ -1104,26 +1104,21 @@ | ||
| 1104 | 1104 | blob_zero(&temp); |
| 1105 | 1105 | blob_append(&temp, zUtf8, -1); |
| 1106 | 1106 | blob_swap(pBlob, &temp); |
| 1107 | 1107 | blob_reset(&temp); |
| 1108 | 1108 | #ifdef _WIN32 |
| 1109 | - }else if( starts_with_utf16le_bom(pBlob, &bomSize) ){ | |
| 1110 | - /* Make sure the blob contains two terminating 0-bytes */ | |
| 1111 | - blob_append(pBlob, "", 1); | |
| 1112 | - zUtf8 = blob_str(pBlob) + bomSize; | |
| 1113 | - zUtf8 = fossil_unicode_to_utf8(zUtf8); | |
| 1114 | - blob_zero(pBlob); | |
| 1115 | - blob_append(pBlob, zUtf8, -1); | |
| 1116 | - fossil_unicode_free(zUtf8); | |
| 1117 | - }else if( starts_with_utf16be_bom(pBlob, &bomSize) ){ | |
| 1118 | - unsigned int i = blob_size(pBlob); | |
| 1109 | + }else if( starts_with_utf16_bom(pBlob, &bomSize) ){ | |
| 1119 | 1110 | zUtf8 = blob_buffer(pBlob); |
| 1120 | - while( i > 0 ){ | |
| 1121 | - /* swap bytes of unicode representation */ | |
| 1122 | - char zTemp = zUtf8[--i]; | |
| 1123 | - zUtf8[i] = zUtf8[i-1]; | |
| 1124 | - zUtf8[--i] = zTemp; | |
| 1111 | + if (*((unsigned short *)zUtf8) == 0xfffe) { | |
| 1112 | + /* Found BOM, but with reversed bytes */ | |
| 1113 | + unsigned int i = blob_size(pBlob); | |
| 1114 | + while( i > 0 ){ | |
| 1115 | + /* swap bytes of unicode representation */ | |
| 1116 | + char zTemp = zUtf8[--i]; | |
| 1117 | + zUtf8[i] = zUtf8[i-1]; | |
| 1118 | + zUtf8[--i] = zTemp; | |
| 1119 | + } | |
| 1125 | 1120 | } |
| 1126 | 1121 | /* Make sure the blob contains two terminating 0-bytes */ |
| 1127 | 1122 | blob_append(pBlob, "", 1); |
| 1128 | 1123 | zUtf8 = blob_str(pBlob) + bomSize; |
| 1129 | 1124 | zUtf8 = fossil_unicode_to_utf8(zUtf8); |
| 1130 | 1125 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -1104,26 +1104,21 @@ | |
| 1104 | blob_zero(&temp); |
| 1105 | blob_append(&temp, zUtf8, -1); |
| 1106 | blob_swap(pBlob, &temp); |
| 1107 | blob_reset(&temp); |
| 1108 | #ifdef _WIN32 |
| 1109 | }else if( starts_with_utf16le_bom(pBlob, &bomSize) ){ |
| 1110 | /* Make sure the blob contains two terminating 0-bytes */ |
| 1111 | blob_append(pBlob, "", 1); |
| 1112 | zUtf8 = blob_str(pBlob) + bomSize; |
| 1113 | zUtf8 = fossil_unicode_to_utf8(zUtf8); |
| 1114 | blob_zero(pBlob); |
| 1115 | blob_append(pBlob, zUtf8, -1); |
| 1116 | fossil_unicode_free(zUtf8); |
| 1117 | }else if( starts_with_utf16be_bom(pBlob, &bomSize) ){ |
| 1118 | unsigned int i = blob_size(pBlob); |
| 1119 | zUtf8 = blob_buffer(pBlob); |
| 1120 | while( i > 0 ){ |
| 1121 | /* swap bytes of unicode representation */ |
| 1122 | char zTemp = zUtf8[--i]; |
| 1123 | zUtf8[i] = zUtf8[i-1]; |
| 1124 | zUtf8[--i] = zTemp; |
| 1125 | } |
| 1126 | /* Make sure the blob contains two terminating 0-bytes */ |
| 1127 | blob_append(pBlob, "", 1); |
| 1128 | zUtf8 = blob_str(pBlob) + bomSize; |
| 1129 | zUtf8 = fossil_unicode_to_utf8(zUtf8); |
| 1130 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -1104,26 +1104,21 @@ | |
| 1104 | blob_zero(&temp); |
| 1105 | blob_append(&temp, zUtf8, -1); |
| 1106 | blob_swap(pBlob, &temp); |
| 1107 | blob_reset(&temp); |
| 1108 | #ifdef _WIN32 |
| 1109 | }else if( starts_with_utf16_bom(pBlob, &bomSize) ){ |
| 1110 | zUtf8 = blob_buffer(pBlob); |
| 1111 | if (*((unsigned short *)zUtf8) == 0xfffe) { |
| 1112 | /* Found BOM, but with reversed bytes */ |
| 1113 | unsigned int i = blob_size(pBlob); |
| 1114 | while( i > 0 ){ |
| 1115 | /* swap bytes of unicode representation */ |
| 1116 | char zTemp = zUtf8[--i]; |
| 1117 | zUtf8[i] = zUtf8[i-1]; |
| 1118 | zUtf8[--i] = zTemp; |
| 1119 | } |
| 1120 | } |
| 1121 | /* Make sure the blob contains two terminating 0-bytes */ |
| 1122 | blob_append(pBlob, "", 1); |
| 1123 | zUtf8 = blob_str(pBlob) + bomSize; |
| 1124 | zUtf8 = fossil_unicode_to_utf8(zUtf8); |
| 1125 |
+36
-19
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -619,30 +619,36 @@ | ||
| 619 | 619 | ** of the array. |
| 620 | 620 | ** |
| 621 | 621 | ** If there were no arguments passed to [commit], aCommitFile is not |
| 622 | 622 | ** allocated and remains NULL. Other parts of the code interpret this |
| 623 | 623 | ** to mean "all files". |
| 624 | +** | |
| 625 | +** Returns 1 if there was a warning, 0 otherwise. | |
| 624 | 626 | */ |
| 625 | -void select_commit_files(void){ | |
| 627 | +int select_commit_files(void){ | |
| 628 | + int result = 0; | |
| 626 | 629 | if( g.argc>2 ){ |
| 627 | - int ii; | |
| 630 | + int ii, jj=0; | |
| 628 | 631 | Blob b; |
| 629 | 632 | blob_zero(&b); |
| 630 | 633 | g.aCommitFile = fossil_malloc(sizeof(int)*(g.argc-1)); |
| 631 | 634 | |
| 632 | 635 | for(ii=2; ii<g.argc; ii++){ |
| 633 | 636 | int iId; |
| 634 | 637 | file_tree_name(g.argv[ii], &b, 1); |
| 635 | 638 | iId = db_int(-1, "SELECT id FROM vfile WHERE pathname=%Q", blob_str(&b)); |
| 636 | 639 | if( iId<0 ){ |
| 637 | - fossil_fatal("fossil knows nothing about: %s", g.argv[ii]); | |
| 640 | + fossil_warning("fossil knows nothing about: %s", g.argv[ii]); | |
| 641 | + result = 1; | |
| 642 | + } else { | |
| 643 | + g.aCommitFile[jj++] = iId; | |
| 638 | 644 | } |
| 639 | - g.aCommitFile[ii-2] = iId; | |
| 640 | 645 | blob_reset(&b); |
| 641 | 646 | } |
| 642 | - g.aCommitFile[ii-2] = 0; | |
| 647 | + g.aCommitFile[jj] = 0; | |
| 643 | 648 | } |
| 649 | + return result; | |
| 644 | 650 | } |
| 645 | 651 | |
| 646 | 652 | /* |
| 647 | 653 | ** Make sure the current check-in with timestamp zDate is younger than its |
| 648 | 654 | ** ancestor identified rid and zUuid. Throw a fatal error if not. |
| @@ -898,11 +904,11 @@ | ||
| 898 | 904 | */ |
| 899 | 905 | static int commit_warning( |
| 900 | 906 | Blob *p, /* The content of the file being committed. */ |
| 901 | 907 | int crnlOk, /* Non-zero if CR/NL warnings should be disabled. */ |
| 902 | 908 | int binOk, /* Non-zero if binary warnings should be disabled. */ |
| 903 | - int unicodeOk, /* Non-zero if unicode warnings should be disabled. */ | |
| 909 | + int encodingOk, /* Non-zero if encoding warnings should be disabled. */ | |
| 904 | 910 | const char *zFilename /* The full name of the file being committed. */ |
| 905 | 911 | ){ |
| 906 | 912 | int eType; /* return value of looks_like_utf8/utf16() */ |
| 907 | 913 | int fUnicode; /* return value of starts_with_utf16_bom() */ |
| 908 | 914 | char *zMsg; /* Warning message */ |
| @@ -912,44 +918,50 @@ | ||
| 912 | 918 | if( allOk ) return 0; |
| 913 | 919 | fUnicode = starts_with_utf16_bom(p, 0); |
| 914 | 920 | eType = fUnicode ? looks_like_utf16(p) : looks_like_utf8(p); |
| 915 | 921 | if( eType==0 || eType==-1 || fUnicode ){ |
| 916 | 922 | const char *zWarning; |
| 923 | + const char *zDisable; | |
| 917 | 924 | const char *zConvert = "c=convert/"; |
| 918 | 925 | Blob ans; |
| 919 | 926 | char cReply; |
| 920 | 927 | |
| 921 | 928 | if( eType==-1 && fUnicode ){ |
| 922 | - if ( crnlOk && unicodeOk ){ | |
| 923 | - return 0; /* We don't want Unicode/CR/NL warnings for this file. */ | |
| 929 | + if ( crnlOk && encodingOk ){ | |
| 930 | + return 0; /* We don't want CR/NL and Unicode warnings for this file. */ | |
| 924 | 931 | } |
| 925 | - zWarning = "Unicode and CR/NL line endings"; | |
| 932 | + zWarning = "CR/NL line endings and Unicode"; | |
| 933 | + zDisable = "\"crnl-glob\" and \"encoding-glob\" settings"; | |
| 926 | 934 | }else if( eType==-1 ){ |
| 927 | 935 | if( crnlOk ){ |
| 928 | 936 | return 0; /* We don't want CR/NL warnings for this file. */ |
| 929 | 937 | } |
| 930 | 938 | zWarning = "CR/NL line endings"; |
| 939 | + zDisable = "\"crnl-glob\" setting"; | |
| 931 | 940 | }else if( eType==0 ){ |
| 932 | 941 | if( binOk ){ |
| 933 | 942 | return 0; /* We don't want binary warnings for this file. */ |
| 934 | 943 | } |
| 935 | 944 | zWarning = "binary data"; |
| 945 | + zDisable = "\"binary-glob\" setting"; | |
| 936 | 946 | zConvert = ""; /* We cannot convert binary files. */ |
| 937 | 947 | }else{ |
| 938 | - if ( unicodeOk ){ | |
| 939 | - return 0; /* We don't want unicode warnings for this file. */ | |
| 948 | + if ( encodingOk ){ | |
| 949 | + return 0; /* We don't want encoding warnings for this file. */ | |
| 940 | 950 | } |
| 941 | 951 | zWarning = "Unicode"; |
| 952 | + zDisable = "\"encoding-glob\" setting"; | |
| 942 | 953 | #ifndef _WIN32 |
| 943 | 954 | zConvert = ""; /* On Unix, we cannot easily convert Unicode files. */ |
| 944 | 955 | #endif |
| 945 | 956 | } |
| 946 | 957 | file_relative_name(zFilename, &fname, 0); |
| 947 | 958 | blob_zero(&ans); |
| 948 | 959 | zMsg = mprintf( |
| 949 | - "%s contains %s. commit anyhow (a=all/%sy/N)? ", | |
| 950 | - blob_str(&fname), zWarning, zConvert); | |
| 960 | + "%s contains %s. Use --no-warnings or the %s to disable this warning.\n" | |
| 961 | + "Commit anyhow (a=all/%sy/N)? ", | |
| 962 | + blob_str(&fname), zWarning, zDisable, zConvert); | |
| 951 | 963 | prompt_user(zMsg, &ans); |
| 952 | 964 | fossil_free(zMsg); |
| 953 | 965 | cReply = blob_str(&ans)[0]; |
| 954 | 966 | if( cReply=='a' || cReply=='A' ){ |
| 955 | 967 | allOk = 1; |
| @@ -1196,11 +1208,16 @@ | ||
| 1196 | 1208 | ** After the following function call has returned, the Global.aCommitFile[] |
| 1197 | 1209 | ** array is allocated to contain the "id" field from the vfile table |
| 1198 | 1210 | ** for each file to be committed. Or, if aCommitFile is NULL, all files |
| 1199 | 1211 | ** should be committed. |
| 1200 | 1212 | */ |
| 1201 | - select_commit_files(); | |
| 1213 | + if ( select_commit_files() ){ | |
| 1214 | + blob_zero(&ans); | |
| 1215 | + prompt_user("continue (y/N)? ", &ans); | |
| 1216 | + cReply = blob_str(&ans)[0]; | |
| 1217 | + if( cReply!='y' && cReply!='Y' ) fossil_exit(1);; | |
| 1218 | + } | |
| 1202 | 1219 | /* id=0 means that it introduces a new parent */ |
| 1203 | 1220 | isAMerge = db_exists("SELECT 1 FROM vmerge WHERE id=0"); |
| 1204 | 1221 | if( g.aCommitFile && isAMerge ){ |
| 1205 | 1222 | fossil_fatal("cannot do a partial commit of a graph merge"); |
| 1206 | 1223 | } |
| @@ -1318,25 +1335,25 @@ | ||
| 1318 | 1335 | "SELECT id, %Q || pathname, mrid, %s, chnged, %s, %s FROM vfile " |
| 1319 | 1336 | "WHERE chnged==1 AND NOT deleted AND is_selected(id)", |
| 1320 | 1337 | g.zLocalRoot, |
| 1321 | 1338 | glob_expr("pathname", db_get("crnl-glob","")), |
| 1322 | 1339 | glob_expr("pathname", db_get("binary-glob","")), |
| 1323 | - glob_expr("pathname", db_get("unicode-glob","")) | |
| 1340 | + glob_expr("pathname", db_get("encoding-glob","")) | |
| 1324 | 1341 | ); |
| 1325 | 1342 | while( db_step(&q)==SQLITE_ROW ){ |
| 1326 | 1343 | int id, rid; |
| 1327 | 1344 | const char *zFullname; |
| 1328 | 1345 | Blob content; |
| 1329 | - int crnlOk, binOk, unicodeOk, chnged; | |
| 1346 | + int crnlOk, binOk, encodingOk, chnged; | |
| 1330 | 1347 | |
| 1331 | 1348 | id = db_column_int(&q, 0); |
| 1332 | 1349 | zFullname = db_column_text(&q, 1); |
| 1333 | 1350 | rid = db_column_int(&q, 2); |
| 1334 | 1351 | crnlOk = db_column_int(&q, 3); |
| 1335 | 1352 | chnged = db_column_int(&q, 4); |
| 1336 | 1353 | binOk = db_column_int(&q, 5); |
| 1337 | - unicodeOk = db_column_int(&q, 6); | |
| 1354 | + encodingOk = db_column_int(&q, 6); | |
| 1338 | 1355 | |
| 1339 | 1356 | blob_zero(&content); |
| 1340 | 1357 | if( file_wd_islink(zFullname) ){ |
| 1341 | 1358 | /* Instead of file content, put link destination path */ |
| 1342 | 1359 | blob_read_link(&content, zFullname); |
| @@ -1344,11 +1361,11 @@ | ||
| 1344 | 1361 | blob_read_from_file(&content, zFullname); |
| 1345 | 1362 | } |
| 1346 | 1363 | /* Do not emit any warnings when they are disabled. */ |
| 1347 | 1364 | if( !noWarningFlag ){ |
| 1348 | 1365 | abortCommit |= commit_warning(&content, crnlOk, binOk, |
| 1349 | - unicodeOk, zFullname); | |
| 1366 | + encodingOk, zFullname); | |
| 1350 | 1367 | } |
| 1351 | 1368 | if( chnged==1 && contains_merge_marker(&content) ){ |
| 1352 | 1369 | Blob fname; /* Relative pathname of the file */ |
| 1353 | 1370 | |
| 1354 | 1371 | nConflict++; |
| @@ -1531,11 +1548,11 @@ | ||
| 1531 | 1548 | exit(1); |
| 1532 | 1549 | } |
| 1533 | 1550 | db_end_transaction(0); |
| 1534 | 1551 | |
| 1535 | 1552 | if( !g.markPrivate ){ |
| 1536 | - autosync(SYNC_PUSH); | |
| 1553 | + autosync(SYNC_PUSH|SYNC_PULL); | |
| 1537 | 1554 | } |
| 1538 | 1555 | if( count_nonbranch_children(vid)>1 ){ |
| 1539 | 1556 | fossil_print("**** warning: a fork has occurred *****\n"); |
| 1540 | 1557 | } |
| 1541 | 1558 | } |
| 1542 | 1559 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -619,30 +619,36 @@ | |
| 619 | ** of the array. |
| 620 | ** |
| 621 | ** If there were no arguments passed to [commit], aCommitFile is not |
| 622 | ** allocated and remains NULL. Other parts of the code interpret this |
| 623 | ** to mean "all files". |
| 624 | */ |
| 625 | void select_commit_files(void){ |
| 626 | if( g.argc>2 ){ |
| 627 | int ii; |
| 628 | Blob b; |
| 629 | blob_zero(&b); |
| 630 | g.aCommitFile = fossil_malloc(sizeof(int)*(g.argc-1)); |
| 631 | |
| 632 | for(ii=2; ii<g.argc; ii++){ |
| 633 | int iId; |
| 634 | file_tree_name(g.argv[ii], &b, 1); |
| 635 | iId = db_int(-1, "SELECT id FROM vfile WHERE pathname=%Q", blob_str(&b)); |
| 636 | if( iId<0 ){ |
| 637 | fossil_fatal("fossil knows nothing about: %s", g.argv[ii]); |
| 638 | } |
| 639 | g.aCommitFile[ii-2] = iId; |
| 640 | blob_reset(&b); |
| 641 | } |
| 642 | g.aCommitFile[ii-2] = 0; |
| 643 | } |
| 644 | } |
| 645 | |
| 646 | /* |
| 647 | ** Make sure the current check-in with timestamp zDate is younger than its |
| 648 | ** ancestor identified rid and zUuid. Throw a fatal error if not. |
| @@ -898,11 +904,11 @@ | |
| 898 | */ |
| 899 | static int commit_warning( |
| 900 | Blob *p, /* The content of the file being committed. */ |
| 901 | int crnlOk, /* Non-zero if CR/NL warnings should be disabled. */ |
| 902 | int binOk, /* Non-zero if binary warnings should be disabled. */ |
| 903 | int unicodeOk, /* Non-zero if unicode warnings should be disabled. */ |
| 904 | const char *zFilename /* The full name of the file being committed. */ |
| 905 | ){ |
| 906 | int eType; /* return value of looks_like_utf8/utf16() */ |
| 907 | int fUnicode; /* return value of starts_with_utf16_bom() */ |
| 908 | char *zMsg; /* Warning message */ |
| @@ -912,44 +918,50 @@ | |
| 912 | if( allOk ) return 0; |
| 913 | fUnicode = starts_with_utf16_bom(p, 0); |
| 914 | eType = fUnicode ? looks_like_utf16(p) : looks_like_utf8(p); |
| 915 | if( eType==0 || eType==-1 || fUnicode ){ |
| 916 | const char *zWarning; |
| 917 | const char *zConvert = "c=convert/"; |
| 918 | Blob ans; |
| 919 | char cReply; |
| 920 | |
| 921 | if( eType==-1 && fUnicode ){ |
| 922 | if ( crnlOk && unicodeOk ){ |
| 923 | return 0; /* We don't want Unicode/CR/NL warnings for this file. */ |
| 924 | } |
| 925 | zWarning = "Unicode and CR/NL line endings"; |
| 926 | }else if( eType==-1 ){ |
| 927 | if( crnlOk ){ |
| 928 | return 0; /* We don't want CR/NL warnings for this file. */ |
| 929 | } |
| 930 | zWarning = "CR/NL line endings"; |
| 931 | }else if( eType==0 ){ |
| 932 | if( binOk ){ |
| 933 | return 0; /* We don't want binary warnings for this file. */ |
| 934 | } |
| 935 | zWarning = "binary data"; |
| 936 | zConvert = ""; /* We cannot convert binary files. */ |
| 937 | }else{ |
| 938 | if ( unicodeOk ){ |
| 939 | return 0; /* We don't want unicode warnings for this file. */ |
| 940 | } |
| 941 | zWarning = "Unicode"; |
| 942 | #ifndef _WIN32 |
| 943 | zConvert = ""; /* On Unix, we cannot easily convert Unicode files. */ |
| 944 | #endif |
| 945 | } |
| 946 | file_relative_name(zFilename, &fname, 0); |
| 947 | blob_zero(&ans); |
| 948 | zMsg = mprintf( |
| 949 | "%s contains %s. commit anyhow (a=all/%sy/N)? ", |
| 950 | blob_str(&fname), zWarning, zConvert); |
| 951 | prompt_user(zMsg, &ans); |
| 952 | fossil_free(zMsg); |
| 953 | cReply = blob_str(&ans)[0]; |
| 954 | if( cReply=='a' || cReply=='A' ){ |
| 955 | allOk = 1; |
| @@ -1196,11 +1208,16 @@ | |
| 1196 | ** After the following function call has returned, the Global.aCommitFile[] |
| 1197 | ** array is allocated to contain the "id" field from the vfile table |
| 1198 | ** for each file to be committed. Or, if aCommitFile is NULL, all files |
| 1199 | ** should be committed. |
| 1200 | */ |
| 1201 | select_commit_files(); |
| 1202 | /* id=0 means that it introduces a new parent */ |
| 1203 | isAMerge = db_exists("SELECT 1 FROM vmerge WHERE id=0"); |
| 1204 | if( g.aCommitFile && isAMerge ){ |
| 1205 | fossil_fatal("cannot do a partial commit of a graph merge"); |
| 1206 | } |
| @@ -1318,25 +1335,25 @@ | |
| 1318 | "SELECT id, %Q || pathname, mrid, %s, chnged, %s, %s FROM vfile " |
| 1319 | "WHERE chnged==1 AND NOT deleted AND is_selected(id)", |
| 1320 | g.zLocalRoot, |
| 1321 | glob_expr("pathname", db_get("crnl-glob","")), |
| 1322 | glob_expr("pathname", db_get("binary-glob","")), |
| 1323 | glob_expr("pathname", db_get("unicode-glob","")) |
| 1324 | ); |
| 1325 | while( db_step(&q)==SQLITE_ROW ){ |
| 1326 | int id, rid; |
| 1327 | const char *zFullname; |
| 1328 | Blob content; |
| 1329 | int crnlOk, binOk, unicodeOk, chnged; |
| 1330 | |
| 1331 | id = db_column_int(&q, 0); |
| 1332 | zFullname = db_column_text(&q, 1); |
| 1333 | rid = db_column_int(&q, 2); |
| 1334 | crnlOk = db_column_int(&q, 3); |
| 1335 | chnged = db_column_int(&q, 4); |
| 1336 | binOk = db_column_int(&q, 5); |
| 1337 | unicodeOk = db_column_int(&q, 6); |
| 1338 | |
| 1339 | blob_zero(&content); |
| 1340 | if( file_wd_islink(zFullname) ){ |
| 1341 | /* Instead of file content, put link destination path */ |
| 1342 | blob_read_link(&content, zFullname); |
| @@ -1344,11 +1361,11 @@ | |
| 1344 | blob_read_from_file(&content, zFullname); |
| 1345 | } |
| 1346 | /* Do not emit any warnings when they are disabled. */ |
| 1347 | if( !noWarningFlag ){ |
| 1348 | abortCommit |= commit_warning(&content, crnlOk, binOk, |
| 1349 | unicodeOk, zFullname); |
| 1350 | } |
| 1351 | if( chnged==1 && contains_merge_marker(&content) ){ |
| 1352 | Blob fname; /* Relative pathname of the file */ |
| 1353 | |
| 1354 | nConflict++; |
| @@ -1531,11 +1548,11 @@ | |
| 1531 | exit(1); |
| 1532 | } |
| 1533 | db_end_transaction(0); |
| 1534 | |
| 1535 | if( !g.markPrivate ){ |
| 1536 | autosync(SYNC_PUSH); |
| 1537 | } |
| 1538 | if( count_nonbranch_children(vid)>1 ){ |
| 1539 | fossil_print("**** warning: a fork has occurred *****\n"); |
| 1540 | } |
| 1541 | } |
| 1542 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -619,30 +619,36 @@ | |
| 619 | ** of the array. |
| 620 | ** |
| 621 | ** If there were no arguments passed to [commit], aCommitFile is not |
| 622 | ** allocated and remains NULL. Other parts of the code interpret this |
| 623 | ** to mean "all files". |
| 624 | ** |
| 625 | ** Returns 1 if there was a warning, 0 otherwise. |
| 626 | */ |
| 627 | int select_commit_files(void){ |
| 628 | int result = 0; |
| 629 | if( g.argc>2 ){ |
| 630 | int ii, jj=0; |
| 631 | Blob b; |
| 632 | blob_zero(&b); |
| 633 | g.aCommitFile = fossil_malloc(sizeof(int)*(g.argc-1)); |
| 634 | |
| 635 | for(ii=2; ii<g.argc; ii++){ |
| 636 | int iId; |
| 637 | file_tree_name(g.argv[ii], &b, 1); |
| 638 | iId = db_int(-1, "SELECT id FROM vfile WHERE pathname=%Q", blob_str(&b)); |
| 639 | if( iId<0 ){ |
| 640 | fossil_warning("fossil knows nothing about: %s", g.argv[ii]); |
| 641 | result = 1; |
| 642 | } else { |
| 643 | g.aCommitFile[jj++] = iId; |
| 644 | } |
| 645 | blob_reset(&b); |
| 646 | } |
| 647 | g.aCommitFile[jj] = 0; |
| 648 | } |
| 649 | return result; |
| 650 | } |
| 651 | |
| 652 | /* |
| 653 | ** Make sure the current check-in with timestamp zDate is younger than its |
| 654 | ** ancestor identified rid and zUuid. Throw a fatal error if not. |
| @@ -898,11 +904,11 @@ | |
| 904 | */ |
| 905 | static int commit_warning( |
| 906 | Blob *p, /* The content of the file being committed. */ |
| 907 | int crnlOk, /* Non-zero if CR/NL warnings should be disabled. */ |
| 908 | int binOk, /* Non-zero if binary warnings should be disabled. */ |
| 909 | int encodingOk, /* Non-zero if encoding warnings should be disabled. */ |
| 910 | const char *zFilename /* The full name of the file being committed. */ |
| 911 | ){ |
| 912 | int eType; /* return value of looks_like_utf8/utf16() */ |
| 913 | int fUnicode; /* return value of starts_with_utf16_bom() */ |
| 914 | char *zMsg; /* Warning message */ |
| @@ -912,44 +918,50 @@ | |
| 918 | if( allOk ) return 0; |
| 919 | fUnicode = starts_with_utf16_bom(p, 0); |
| 920 | eType = fUnicode ? looks_like_utf16(p) : looks_like_utf8(p); |
| 921 | if( eType==0 || eType==-1 || fUnicode ){ |
| 922 | const char *zWarning; |
| 923 | const char *zDisable; |
| 924 | const char *zConvert = "c=convert/"; |
| 925 | Blob ans; |
| 926 | char cReply; |
| 927 | |
| 928 | if( eType==-1 && fUnicode ){ |
| 929 | if ( crnlOk && encodingOk ){ |
| 930 | return 0; /* We don't want CR/NL and Unicode warnings for this file. */ |
| 931 | } |
| 932 | zWarning = "CR/NL line endings and Unicode"; |
| 933 | zDisable = "\"crnl-glob\" and \"encoding-glob\" settings"; |
| 934 | }else if( eType==-1 ){ |
| 935 | if( crnlOk ){ |
| 936 | return 0; /* We don't want CR/NL warnings for this file. */ |
| 937 | } |
| 938 | zWarning = "CR/NL line endings"; |
| 939 | zDisable = "\"crnl-glob\" setting"; |
| 940 | }else if( eType==0 ){ |
| 941 | if( binOk ){ |
| 942 | return 0; /* We don't want binary warnings for this file. */ |
| 943 | } |
| 944 | zWarning = "binary data"; |
| 945 | zDisable = "\"binary-glob\" setting"; |
| 946 | zConvert = ""; /* We cannot convert binary files. */ |
| 947 | }else{ |
| 948 | if ( encodingOk ){ |
| 949 | return 0; /* We don't want encoding warnings for this file. */ |
| 950 | } |
| 951 | zWarning = "Unicode"; |
| 952 | zDisable = "\"encoding-glob\" setting"; |
| 953 | #ifndef _WIN32 |
| 954 | zConvert = ""; /* On Unix, we cannot easily convert Unicode files. */ |
| 955 | #endif |
| 956 | } |
| 957 | file_relative_name(zFilename, &fname, 0); |
| 958 | blob_zero(&ans); |
| 959 | zMsg = mprintf( |
| 960 | "%s contains %s. Use --no-warnings or the %s to disable this warning.\n" |
| 961 | "Commit anyhow (a=all/%sy/N)? ", |
| 962 | blob_str(&fname), zWarning, zDisable, zConvert); |
| 963 | prompt_user(zMsg, &ans); |
| 964 | fossil_free(zMsg); |
| 965 | cReply = blob_str(&ans)[0]; |
| 966 | if( cReply=='a' || cReply=='A' ){ |
| 967 | allOk = 1; |
| @@ -1196,11 +1208,16 @@ | |
| 1208 | ** After the following function call has returned, the Global.aCommitFile[] |
| 1209 | ** array is allocated to contain the "id" field from the vfile table |
| 1210 | ** for each file to be committed. Or, if aCommitFile is NULL, all files |
| 1211 | ** should be committed. |
| 1212 | */ |
| 1213 | if ( select_commit_files() ){ |
| 1214 | blob_zero(&ans); |
| 1215 | prompt_user("continue (y/N)? ", &ans); |
| 1216 | cReply = blob_str(&ans)[0]; |
| 1217 | if( cReply!='y' && cReply!='Y' ) fossil_exit(1);; |
| 1218 | } |
| 1219 | /* id=0 means that it introduces a new parent */ |
| 1220 | isAMerge = db_exists("SELECT 1 FROM vmerge WHERE id=0"); |
| 1221 | if( g.aCommitFile && isAMerge ){ |
| 1222 | fossil_fatal("cannot do a partial commit of a graph merge"); |
| 1223 | } |
| @@ -1318,25 +1335,25 @@ | |
| 1335 | "SELECT id, %Q || pathname, mrid, %s, chnged, %s, %s FROM vfile " |
| 1336 | "WHERE chnged==1 AND NOT deleted AND is_selected(id)", |
| 1337 | g.zLocalRoot, |
| 1338 | glob_expr("pathname", db_get("crnl-glob","")), |
| 1339 | glob_expr("pathname", db_get("binary-glob","")), |
| 1340 | glob_expr("pathname", db_get("encoding-glob","")) |
| 1341 | ); |
| 1342 | while( db_step(&q)==SQLITE_ROW ){ |
| 1343 | int id, rid; |
| 1344 | const char *zFullname; |
| 1345 | Blob content; |
| 1346 | int crnlOk, binOk, encodingOk, chnged; |
| 1347 | |
| 1348 | id = db_column_int(&q, 0); |
| 1349 | zFullname = db_column_text(&q, 1); |
| 1350 | rid = db_column_int(&q, 2); |
| 1351 | crnlOk = db_column_int(&q, 3); |
| 1352 | chnged = db_column_int(&q, 4); |
| 1353 | binOk = db_column_int(&q, 5); |
| 1354 | encodingOk = db_column_int(&q, 6); |
| 1355 | |
| 1356 | blob_zero(&content); |
| 1357 | if( file_wd_islink(zFullname) ){ |
| 1358 | /* Instead of file content, put link destination path */ |
| 1359 | blob_read_link(&content, zFullname); |
| @@ -1344,11 +1361,11 @@ | |
| 1361 | blob_read_from_file(&content, zFullname); |
| 1362 | } |
| 1363 | /* Do not emit any warnings when they are disabled. */ |
| 1364 | if( !noWarningFlag ){ |
| 1365 | abortCommit |= commit_warning(&content, crnlOk, binOk, |
| 1366 | encodingOk, zFullname); |
| 1367 | } |
| 1368 | if( chnged==1 && contains_merge_marker(&content) ){ |
| 1369 | Blob fname; /* Relative pathname of the file */ |
| 1370 | |
| 1371 | nConflict++; |
| @@ -1531,11 +1548,11 @@ | |
| 1548 | exit(1); |
| 1549 | } |
| 1550 | db_end_transaction(0); |
| 1551 | |
| 1552 | if( !g.markPrivate ){ |
| 1553 | autosync(SYNC_PUSH|SYNC_PULL); |
| 1554 | } |
| 1555 | if( count_nonbranch_children(vid)>1 ){ |
| 1556 | fossil_print("**** warning: a fork has occurred *****\n"); |
| 1557 | } |
| 1558 | } |
| 1559 |
+36
-19
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -619,30 +619,36 @@ | ||
| 619 | 619 | ** of the array. |
| 620 | 620 | ** |
| 621 | 621 | ** If there were no arguments passed to [commit], aCommitFile is not |
| 622 | 622 | ** allocated and remains NULL. Other parts of the code interpret this |
| 623 | 623 | ** to mean "all files". |
| 624 | +** | |
| 625 | +** Returns 1 if there was a warning, 0 otherwise. | |
| 624 | 626 | */ |
| 625 | -void select_commit_files(void){ | |
| 627 | +int select_commit_files(void){ | |
| 628 | + int result = 0; | |
| 626 | 629 | if( g.argc>2 ){ |
| 627 | - int ii; | |
| 630 | + int ii, jj=0; | |
| 628 | 631 | Blob b; |
| 629 | 632 | blob_zero(&b); |
| 630 | 633 | g.aCommitFile = fossil_malloc(sizeof(int)*(g.argc-1)); |
| 631 | 634 | |
| 632 | 635 | for(ii=2; ii<g.argc; ii++){ |
| 633 | 636 | int iId; |
| 634 | 637 | file_tree_name(g.argv[ii], &b, 1); |
| 635 | 638 | iId = db_int(-1, "SELECT id FROM vfile WHERE pathname=%Q", blob_str(&b)); |
| 636 | 639 | if( iId<0 ){ |
| 637 | - fossil_fatal("fossil knows nothing about: %s", g.argv[ii]); | |
| 640 | + fossil_warning("fossil knows nothing about: %s", g.argv[ii]); | |
| 641 | + result = 1; | |
| 642 | + } else { | |
| 643 | + g.aCommitFile[jj++] = iId; | |
| 638 | 644 | } |
| 639 | - g.aCommitFile[ii-2] = iId; | |
| 640 | 645 | blob_reset(&b); |
| 641 | 646 | } |
| 642 | - g.aCommitFile[ii-2] = 0; | |
| 647 | + g.aCommitFile[jj] = 0; | |
| 643 | 648 | } |
| 649 | + return result; | |
| 644 | 650 | } |
| 645 | 651 | |
| 646 | 652 | /* |
| 647 | 653 | ** Make sure the current check-in with timestamp zDate is younger than its |
| 648 | 654 | ** ancestor identified rid and zUuid. Throw a fatal error if not. |
| @@ -898,11 +904,11 @@ | ||
| 898 | 904 | */ |
| 899 | 905 | static int commit_warning( |
| 900 | 906 | Blob *p, /* The content of the file being committed. */ |
| 901 | 907 | int crnlOk, /* Non-zero if CR/NL warnings should be disabled. */ |
| 902 | 908 | int binOk, /* Non-zero if binary warnings should be disabled. */ |
| 903 | - int unicodeOk, /* Non-zero if unicode warnings should be disabled. */ | |
| 909 | + int encodingOk, /* Non-zero if encoding warnings should be disabled. */ | |
| 904 | 910 | const char *zFilename /* The full name of the file being committed. */ |
| 905 | 911 | ){ |
| 906 | 912 | int eType; /* return value of looks_like_utf8/utf16() */ |
| 907 | 913 | int fUnicode; /* return value of starts_with_utf16_bom() */ |
| 908 | 914 | char *zMsg; /* Warning message */ |
| @@ -912,44 +918,50 @@ | ||
| 912 | 918 | if( allOk ) return 0; |
| 913 | 919 | fUnicode = starts_with_utf16_bom(p, 0); |
| 914 | 920 | eType = fUnicode ? looks_like_utf16(p) : looks_like_utf8(p); |
| 915 | 921 | if( eType==0 || eType==-1 || fUnicode ){ |
| 916 | 922 | const char *zWarning; |
| 923 | + const char *zDisable; | |
| 917 | 924 | const char *zConvert = "c=convert/"; |
| 918 | 925 | Blob ans; |
| 919 | 926 | char cReply; |
| 920 | 927 | |
| 921 | 928 | if( eType==-1 && fUnicode ){ |
| 922 | - if ( crnlOk && unicodeOk ){ | |
| 923 | - return 0; /* We don't want Unicode/CR/NL warnings for this file. */ | |
| 929 | + if ( crnlOk && encodingOk ){ | |
| 930 | + return 0; /* We don't want CR/NL and Unicode warnings for this file. */ | |
| 924 | 931 | } |
| 925 | - zWarning = "Unicode and CR/NL line endings"; | |
| 932 | + zWarning = "CR/NL line endings and Unicode"; | |
| 933 | + zDisable = "\"crnl-glob\" and \"encoding-glob\" settings"; | |
| 926 | 934 | }else if( eType==-1 ){ |
| 927 | 935 | if( crnlOk ){ |
| 928 | 936 | return 0; /* We don't want CR/NL warnings for this file. */ |
| 929 | 937 | } |
| 930 | 938 | zWarning = "CR/NL line endings"; |
| 939 | + zDisable = "\"crnl-glob\" setting"; | |
| 931 | 940 | }else if( eType==0 ){ |
| 932 | 941 | if( binOk ){ |
| 933 | 942 | return 0; /* We don't want binary warnings for this file. */ |
| 934 | 943 | } |
| 935 | 944 | zWarning = "binary data"; |
| 945 | + zDisable = "\"binary-glob\" setting"; | |
| 936 | 946 | zConvert = ""; /* We cannot convert binary files. */ |
| 937 | 947 | }else{ |
| 938 | - if ( unicodeOk ){ | |
| 939 | - return 0; /* We don't want unicode warnings for this file. */ | |
| 948 | + if ( encodingOk ){ | |
| 949 | + return 0; /* We don't want encoding warnings for this file. */ | |
| 940 | 950 | } |
| 941 | 951 | zWarning = "Unicode"; |
| 952 | + zDisable = "\"encoding-glob\" setting"; | |
| 942 | 953 | #ifndef _WIN32 |
| 943 | 954 | zConvert = ""; /* On Unix, we cannot easily convert Unicode files. */ |
| 944 | 955 | #endif |
| 945 | 956 | } |
| 946 | 957 | file_relative_name(zFilename, &fname, 0); |
| 947 | 958 | blob_zero(&ans); |
| 948 | 959 | zMsg = mprintf( |
| 949 | - "%s contains %s. commit anyhow (a=all/%sy/N)? ", | |
| 950 | - blob_str(&fname), zWarning, zConvert); | |
| 960 | + "%s contains %s. Use --no-warnings or the %s to disable this warning.\n" | |
| 961 | + "Commit anyhow (a=all/%sy/N)? ", | |
| 962 | + blob_str(&fname), zWarning, zDisable, zConvert); | |
| 951 | 963 | prompt_user(zMsg, &ans); |
| 952 | 964 | fossil_free(zMsg); |
| 953 | 965 | cReply = blob_str(&ans)[0]; |
| 954 | 966 | if( cReply=='a' || cReply=='A' ){ |
| 955 | 967 | allOk = 1; |
| @@ -1196,11 +1208,16 @@ | ||
| 1196 | 1208 | ** After the following function call has returned, the Global.aCommitFile[] |
| 1197 | 1209 | ** array is allocated to contain the "id" field from the vfile table |
| 1198 | 1210 | ** for each file to be committed. Or, if aCommitFile is NULL, all files |
| 1199 | 1211 | ** should be committed. |
| 1200 | 1212 | */ |
| 1201 | - select_commit_files(); | |
| 1213 | + if ( select_commit_files() ){ | |
| 1214 | + blob_zero(&ans); | |
| 1215 | + prompt_user("continue (y/N)? ", &ans); | |
| 1216 | + cReply = blob_str(&ans)[0]; | |
| 1217 | + if( cReply!='y' && cReply!='Y' ) fossil_exit(1);; | |
| 1218 | + } | |
| 1202 | 1219 | /* id=0 means that it introduces a new parent */ |
| 1203 | 1220 | isAMerge = db_exists("SELECT 1 FROM vmerge WHERE id=0"); |
| 1204 | 1221 | if( g.aCommitFile && isAMerge ){ |
| 1205 | 1222 | fossil_fatal("cannot do a partial commit of a graph merge"); |
| 1206 | 1223 | } |
| @@ -1318,25 +1335,25 @@ | ||
| 1318 | 1335 | "SELECT id, %Q || pathname, mrid, %s, chnged, %s, %s FROM vfile " |
| 1319 | 1336 | "WHERE chnged==1 AND NOT deleted AND is_selected(id)", |
| 1320 | 1337 | g.zLocalRoot, |
| 1321 | 1338 | glob_expr("pathname", db_get("crnl-glob","")), |
| 1322 | 1339 | glob_expr("pathname", db_get("binary-glob","")), |
| 1323 | - glob_expr("pathname", db_get("unicode-glob","")) | |
| 1340 | + glob_expr("pathname", db_get("encoding-glob","")) | |
| 1324 | 1341 | ); |
| 1325 | 1342 | while( db_step(&q)==SQLITE_ROW ){ |
| 1326 | 1343 | int id, rid; |
| 1327 | 1344 | const char *zFullname; |
| 1328 | 1345 | Blob content; |
| 1329 | - int crnlOk, binOk, unicodeOk, chnged; | |
| 1346 | + int crnlOk, binOk, encodingOk, chnged; | |
| 1330 | 1347 | |
| 1331 | 1348 | id = db_column_int(&q, 0); |
| 1332 | 1349 | zFullname = db_column_text(&q, 1); |
| 1333 | 1350 | rid = db_column_int(&q, 2); |
| 1334 | 1351 | crnlOk = db_column_int(&q, 3); |
| 1335 | 1352 | chnged = db_column_int(&q, 4); |
| 1336 | 1353 | binOk = db_column_int(&q, 5); |
| 1337 | - unicodeOk = db_column_int(&q, 6); | |
| 1354 | + encodingOk = db_column_int(&q, 6); | |
| 1338 | 1355 | |
| 1339 | 1356 | blob_zero(&content); |
| 1340 | 1357 | if( file_wd_islink(zFullname) ){ |
| 1341 | 1358 | /* Instead of file content, put link destination path */ |
| 1342 | 1359 | blob_read_link(&content, zFullname); |
| @@ -1344,11 +1361,11 @@ | ||
| 1344 | 1361 | blob_read_from_file(&content, zFullname); |
| 1345 | 1362 | } |
| 1346 | 1363 | /* Do not emit any warnings when they are disabled. */ |
| 1347 | 1364 | if( !noWarningFlag ){ |
| 1348 | 1365 | abortCommit |= commit_warning(&content, crnlOk, binOk, |
| 1349 | - unicodeOk, zFullname); | |
| 1366 | + encodingOk, zFullname); | |
| 1350 | 1367 | } |
| 1351 | 1368 | if( chnged==1 && contains_merge_marker(&content) ){ |
| 1352 | 1369 | Blob fname; /* Relative pathname of the file */ |
| 1353 | 1370 | |
| 1354 | 1371 | nConflict++; |
| @@ -1531,11 +1548,11 @@ | ||
| 1531 | 1548 | exit(1); |
| 1532 | 1549 | } |
| 1533 | 1550 | db_end_transaction(0); |
| 1534 | 1551 | |
| 1535 | 1552 | if( !g.markPrivate ){ |
| 1536 | - autosync(SYNC_PUSH); | |
| 1553 | + autosync(SYNC_PUSH|SYNC_PULL); | |
| 1537 | 1554 | } |
| 1538 | 1555 | if( count_nonbranch_children(vid)>1 ){ |
| 1539 | 1556 | fossil_print("**** warning: a fork has occurred *****\n"); |
| 1540 | 1557 | } |
| 1541 | 1558 | } |
| 1542 | 1559 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -619,30 +619,36 @@ | |
| 619 | ** of the array. |
| 620 | ** |
| 621 | ** If there were no arguments passed to [commit], aCommitFile is not |
| 622 | ** allocated and remains NULL. Other parts of the code interpret this |
| 623 | ** to mean "all files". |
| 624 | */ |
| 625 | void select_commit_files(void){ |
| 626 | if( g.argc>2 ){ |
| 627 | int ii; |
| 628 | Blob b; |
| 629 | blob_zero(&b); |
| 630 | g.aCommitFile = fossil_malloc(sizeof(int)*(g.argc-1)); |
| 631 | |
| 632 | for(ii=2; ii<g.argc; ii++){ |
| 633 | int iId; |
| 634 | file_tree_name(g.argv[ii], &b, 1); |
| 635 | iId = db_int(-1, "SELECT id FROM vfile WHERE pathname=%Q", blob_str(&b)); |
| 636 | if( iId<0 ){ |
| 637 | fossil_fatal("fossil knows nothing about: %s", g.argv[ii]); |
| 638 | } |
| 639 | g.aCommitFile[ii-2] = iId; |
| 640 | blob_reset(&b); |
| 641 | } |
| 642 | g.aCommitFile[ii-2] = 0; |
| 643 | } |
| 644 | } |
| 645 | |
| 646 | /* |
| 647 | ** Make sure the current check-in with timestamp zDate is younger than its |
| 648 | ** ancestor identified rid and zUuid. Throw a fatal error if not. |
| @@ -898,11 +904,11 @@ | |
| 898 | */ |
| 899 | static int commit_warning( |
| 900 | Blob *p, /* The content of the file being committed. */ |
| 901 | int crnlOk, /* Non-zero if CR/NL warnings should be disabled. */ |
| 902 | int binOk, /* Non-zero if binary warnings should be disabled. */ |
| 903 | int unicodeOk, /* Non-zero if unicode warnings should be disabled. */ |
| 904 | const char *zFilename /* The full name of the file being committed. */ |
| 905 | ){ |
| 906 | int eType; /* return value of looks_like_utf8/utf16() */ |
| 907 | int fUnicode; /* return value of starts_with_utf16_bom() */ |
| 908 | char *zMsg; /* Warning message */ |
| @@ -912,44 +918,50 @@ | |
| 912 | if( allOk ) return 0; |
| 913 | fUnicode = starts_with_utf16_bom(p, 0); |
| 914 | eType = fUnicode ? looks_like_utf16(p) : looks_like_utf8(p); |
| 915 | if( eType==0 || eType==-1 || fUnicode ){ |
| 916 | const char *zWarning; |
| 917 | const char *zConvert = "c=convert/"; |
| 918 | Blob ans; |
| 919 | char cReply; |
| 920 | |
| 921 | if( eType==-1 && fUnicode ){ |
| 922 | if ( crnlOk && unicodeOk ){ |
| 923 | return 0; /* We don't want Unicode/CR/NL warnings for this file. */ |
| 924 | } |
| 925 | zWarning = "Unicode and CR/NL line endings"; |
| 926 | }else if( eType==-1 ){ |
| 927 | if( crnlOk ){ |
| 928 | return 0; /* We don't want CR/NL warnings for this file. */ |
| 929 | } |
| 930 | zWarning = "CR/NL line endings"; |
| 931 | }else if( eType==0 ){ |
| 932 | if( binOk ){ |
| 933 | return 0; /* We don't want binary warnings for this file. */ |
| 934 | } |
| 935 | zWarning = "binary data"; |
| 936 | zConvert = ""; /* We cannot convert binary files. */ |
| 937 | }else{ |
| 938 | if ( unicodeOk ){ |
| 939 | return 0; /* We don't want unicode warnings for this file. */ |
| 940 | } |
| 941 | zWarning = "Unicode"; |
| 942 | #ifndef _WIN32 |
| 943 | zConvert = ""; /* On Unix, we cannot easily convert Unicode files. */ |
| 944 | #endif |
| 945 | } |
| 946 | file_relative_name(zFilename, &fname, 0); |
| 947 | blob_zero(&ans); |
| 948 | zMsg = mprintf( |
| 949 | "%s contains %s. commit anyhow (a=all/%sy/N)? ", |
| 950 | blob_str(&fname), zWarning, zConvert); |
| 951 | prompt_user(zMsg, &ans); |
| 952 | fossil_free(zMsg); |
| 953 | cReply = blob_str(&ans)[0]; |
| 954 | if( cReply=='a' || cReply=='A' ){ |
| 955 | allOk = 1; |
| @@ -1196,11 +1208,16 @@ | |
| 1196 | ** After the following function call has returned, the Global.aCommitFile[] |
| 1197 | ** array is allocated to contain the "id" field from the vfile table |
| 1198 | ** for each file to be committed. Or, if aCommitFile is NULL, all files |
| 1199 | ** should be committed. |
| 1200 | */ |
| 1201 | select_commit_files(); |
| 1202 | /* id=0 means that it introduces a new parent */ |
| 1203 | isAMerge = db_exists("SELECT 1 FROM vmerge WHERE id=0"); |
| 1204 | if( g.aCommitFile && isAMerge ){ |
| 1205 | fossil_fatal("cannot do a partial commit of a graph merge"); |
| 1206 | } |
| @@ -1318,25 +1335,25 @@ | |
| 1318 | "SELECT id, %Q || pathname, mrid, %s, chnged, %s, %s FROM vfile " |
| 1319 | "WHERE chnged==1 AND NOT deleted AND is_selected(id)", |
| 1320 | g.zLocalRoot, |
| 1321 | glob_expr("pathname", db_get("crnl-glob","")), |
| 1322 | glob_expr("pathname", db_get("binary-glob","")), |
| 1323 | glob_expr("pathname", db_get("unicode-glob","")) |
| 1324 | ); |
| 1325 | while( db_step(&q)==SQLITE_ROW ){ |
| 1326 | int id, rid; |
| 1327 | const char *zFullname; |
| 1328 | Blob content; |
| 1329 | int crnlOk, binOk, unicodeOk, chnged; |
| 1330 | |
| 1331 | id = db_column_int(&q, 0); |
| 1332 | zFullname = db_column_text(&q, 1); |
| 1333 | rid = db_column_int(&q, 2); |
| 1334 | crnlOk = db_column_int(&q, 3); |
| 1335 | chnged = db_column_int(&q, 4); |
| 1336 | binOk = db_column_int(&q, 5); |
| 1337 | unicodeOk = db_column_int(&q, 6); |
| 1338 | |
| 1339 | blob_zero(&content); |
| 1340 | if( file_wd_islink(zFullname) ){ |
| 1341 | /* Instead of file content, put link destination path */ |
| 1342 | blob_read_link(&content, zFullname); |
| @@ -1344,11 +1361,11 @@ | |
| 1344 | blob_read_from_file(&content, zFullname); |
| 1345 | } |
| 1346 | /* Do not emit any warnings when they are disabled. */ |
| 1347 | if( !noWarningFlag ){ |
| 1348 | abortCommit |= commit_warning(&content, crnlOk, binOk, |
| 1349 | unicodeOk, zFullname); |
| 1350 | } |
| 1351 | if( chnged==1 && contains_merge_marker(&content) ){ |
| 1352 | Blob fname; /* Relative pathname of the file */ |
| 1353 | |
| 1354 | nConflict++; |
| @@ -1531,11 +1548,11 @@ | |
| 1531 | exit(1); |
| 1532 | } |
| 1533 | db_end_transaction(0); |
| 1534 | |
| 1535 | if( !g.markPrivate ){ |
| 1536 | autosync(SYNC_PUSH); |
| 1537 | } |
| 1538 | if( count_nonbranch_children(vid)>1 ){ |
| 1539 | fossil_print("**** warning: a fork has occurred *****\n"); |
| 1540 | } |
| 1541 | } |
| 1542 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -619,30 +619,36 @@ | |
| 619 | ** of the array. |
| 620 | ** |
| 621 | ** If there were no arguments passed to [commit], aCommitFile is not |
| 622 | ** allocated and remains NULL. Other parts of the code interpret this |
| 623 | ** to mean "all files". |
| 624 | ** |
| 625 | ** Returns 1 if there was a warning, 0 otherwise. |
| 626 | */ |
| 627 | int select_commit_files(void){ |
| 628 | int result = 0; |
| 629 | if( g.argc>2 ){ |
| 630 | int ii, jj=0; |
| 631 | Blob b; |
| 632 | blob_zero(&b); |
| 633 | g.aCommitFile = fossil_malloc(sizeof(int)*(g.argc-1)); |
| 634 | |
| 635 | for(ii=2; ii<g.argc; ii++){ |
| 636 | int iId; |
| 637 | file_tree_name(g.argv[ii], &b, 1); |
| 638 | iId = db_int(-1, "SELECT id FROM vfile WHERE pathname=%Q", blob_str(&b)); |
| 639 | if( iId<0 ){ |
| 640 | fossil_warning("fossil knows nothing about: %s", g.argv[ii]); |
| 641 | result = 1; |
| 642 | } else { |
| 643 | g.aCommitFile[jj++] = iId; |
| 644 | } |
| 645 | blob_reset(&b); |
| 646 | } |
| 647 | g.aCommitFile[jj] = 0; |
| 648 | } |
| 649 | return result; |
| 650 | } |
| 651 | |
| 652 | /* |
| 653 | ** Make sure the current check-in with timestamp zDate is younger than its |
| 654 | ** ancestor identified rid and zUuid. Throw a fatal error if not. |
| @@ -898,11 +904,11 @@ | |
| 904 | */ |
| 905 | static int commit_warning( |
| 906 | Blob *p, /* The content of the file being committed. */ |
| 907 | int crnlOk, /* Non-zero if CR/NL warnings should be disabled. */ |
| 908 | int binOk, /* Non-zero if binary warnings should be disabled. */ |
| 909 | int encodingOk, /* Non-zero if encoding warnings should be disabled. */ |
| 910 | const char *zFilename /* The full name of the file being committed. */ |
| 911 | ){ |
| 912 | int eType; /* return value of looks_like_utf8/utf16() */ |
| 913 | int fUnicode; /* return value of starts_with_utf16_bom() */ |
| 914 | char *zMsg; /* Warning message */ |
| @@ -912,44 +918,50 @@ | |
| 918 | if( allOk ) return 0; |
| 919 | fUnicode = starts_with_utf16_bom(p, 0); |
| 920 | eType = fUnicode ? looks_like_utf16(p) : looks_like_utf8(p); |
| 921 | if( eType==0 || eType==-1 || fUnicode ){ |
| 922 | const char *zWarning; |
| 923 | const char *zDisable; |
| 924 | const char *zConvert = "c=convert/"; |
| 925 | Blob ans; |
| 926 | char cReply; |
| 927 | |
| 928 | if( eType==-1 && fUnicode ){ |
| 929 | if ( crnlOk && encodingOk ){ |
| 930 | return 0; /* We don't want CR/NL and Unicode warnings for this file. */ |
| 931 | } |
| 932 | zWarning = "CR/NL line endings and Unicode"; |
| 933 | zDisable = "\"crnl-glob\" and \"encoding-glob\" settings"; |
| 934 | }else if( eType==-1 ){ |
| 935 | if( crnlOk ){ |
| 936 | return 0; /* We don't want CR/NL warnings for this file. */ |
| 937 | } |
| 938 | zWarning = "CR/NL line endings"; |
| 939 | zDisable = "\"crnl-glob\" setting"; |
| 940 | }else if( eType==0 ){ |
| 941 | if( binOk ){ |
| 942 | return 0; /* We don't want binary warnings for this file. */ |
| 943 | } |
| 944 | zWarning = "binary data"; |
| 945 | zDisable = "\"binary-glob\" setting"; |
| 946 | zConvert = ""; /* We cannot convert binary files. */ |
| 947 | }else{ |
| 948 | if ( encodingOk ){ |
| 949 | return 0; /* We don't want encoding warnings for this file. */ |
| 950 | } |
| 951 | zWarning = "Unicode"; |
| 952 | zDisable = "\"encoding-glob\" setting"; |
| 953 | #ifndef _WIN32 |
| 954 | zConvert = ""; /* On Unix, we cannot easily convert Unicode files. */ |
| 955 | #endif |
| 956 | } |
| 957 | file_relative_name(zFilename, &fname, 0); |
| 958 | blob_zero(&ans); |
| 959 | zMsg = mprintf( |
| 960 | "%s contains %s. Use --no-warnings or the %s to disable this warning.\n" |
| 961 | "Commit anyhow (a=all/%sy/N)? ", |
| 962 | blob_str(&fname), zWarning, zDisable, zConvert); |
| 963 | prompt_user(zMsg, &ans); |
| 964 | fossil_free(zMsg); |
| 965 | cReply = blob_str(&ans)[0]; |
| 966 | if( cReply=='a' || cReply=='A' ){ |
| 967 | allOk = 1; |
| @@ -1196,11 +1208,16 @@ | |
| 1208 | ** After the following function call has returned, the Global.aCommitFile[] |
| 1209 | ** array is allocated to contain the "id" field from the vfile table |
| 1210 | ** for each file to be committed. Or, if aCommitFile is NULL, all files |
| 1211 | ** should be committed. |
| 1212 | */ |
| 1213 | if ( select_commit_files() ){ |
| 1214 | blob_zero(&ans); |
| 1215 | prompt_user("continue (y/N)? ", &ans); |
| 1216 | cReply = blob_str(&ans)[0]; |
| 1217 | if( cReply!='y' && cReply!='Y' ) fossil_exit(1);; |
| 1218 | } |
| 1219 | /* id=0 means that it introduces a new parent */ |
| 1220 | isAMerge = db_exists("SELECT 1 FROM vmerge WHERE id=0"); |
| 1221 | if( g.aCommitFile && isAMerge ){ |
| 1222 | fossil_fatal("cannot do a partial commit of a graph merge"); |
| 1223 | } |
| @@ -1318,25 +1335,25 @@ | |
| 1335 | "SELECT id, %Q || pathname, mrid, %s, chnged, %s, %s FROM vfile " |
| 1336 | "WHERE chnged==1 AND NOT deleted AND is_selected(id)", |
| 1337 | g.zLocalRoot, |
| 1338 | glob_expr("pathname", db_get("crnl-glob","")), |
| 1339 | glob_expr("pathname", db_get("binary-glob","")), |
| 1340 | glob_expr("pathname", db_get("encoding-glob","")) |
| 1341 | ); |
| 1342 | while( db_step(&q)==SQLITE_ROW ){ |
| 1343 | int id, rid; |
| 1344 | const char *zFullname; |
| 1345 | Blob content; |
| 1346 | int crnlOk, binOk, encodingOk, chnged; |
| 1347 | |
| 1348 | id = db_column_int(&q, 0); |
| 1349 | zFullname = db_column_text(&q, 1); |
| 1350 | rid = db_column_int(&q, 2); |
| 1351 | crnlOk = db_column_int(&q, 3); |
| 1352 | chnged = db_column_int(&q, 4); |
| 1353 | binOk = db_column_int(&q, 5); |
| 1354 | encodingOk = db_column_int(&q, 6); |
| 1355 | |
| 1356 | blob_zero(&content); |
| 1357 | if( file_wd_islink(zFullname) ){ |
| 1358 | /* Instead of file content, put link destination path */ |
| 1359 | blob_read_link(&content, zFullname); |
| @@ -1344,11 +1361,11 @@ | |
| 1361 | blob_read_from_file(&content, zFullname); |
| 1362 | } |
| 1363 | /* Do not emit any warnings when they are disabled. */ |
| 1364 | if( !noWarningFlag ){ |
| 1365 | abortCommit |= commit_warning(&content, crnlOk, binOk, |
| 1366 | encodingOk, zFullname); |
| 1367 | } |
| 1368 | if( chnged==1 && contains_merge_marker(&content) ){ |
| 1369 | Blob fname; /* Relative pathname of the file */ |
| 1370 | |
| 1371 | nConflict++; |
| @@ -1531,11 +1548,11 @@ | |
| 1548 | exit(1); |
| 1549 | } |
| 1550 | db_end_transaction(0); |
| 1551 | |
| 1552 | if( !g.markPrivate ){ |
| 1553 | autosync(SYNC_PUSH|SYNC_PULL); |
| 1554 | } |
| 1555 | if( count_nonbranch_children(vid)>1 ){ |
| 1556 | fossil_print("**** warning: a fork has occurred *****\n"); |
| 1557 | } |
| 1558 | } |
| 1559 |
+36
-19
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -619,30 +619,36 @@ | ||
| 619 | 619 | ** of the array. |
| 620 | 620 | ** |
| 621 | 621 | ** If there were no arguments passed to [commit], aCommitFile is not |
| 622 | 622 | ** allocated and remains NULL. Other parts of the code interpret this |
| 623 | 623 | ** to mean "all files". |
| 624 | +** | |
| 625 | +** Returns 1 if there was a warning, 0 otherwise. | |
| 624 | 626 | */ |
| 625 | -void select_commit_files(void){ | |
| 627 | +int select_commit_files(void){ | |
| 628 | + int result = 0; | |
| 626 | 629 | if( g.argc>2 ){ |
| 627 | - int ii; | |
| 630 | + int ii, jj=0; | |
| 628 | 631 | Blob b; |
| 629 | 632 | blob_zero(&b); |
| 630 | 633 | g.aCommitFile = fossil_malloc(sizeof(int)*(g.argc-1)); |
| 631 | 634 | |
| 632 | 635 | for(ii=2; ii<g.argc; ii++){ |
| 633 | 636 | int iId; |
| 634 | 637 | file_tree_name(g.argv[ii], &b, 1); |
| 635 | 638 | iId = db_int(-1, "SELECT id FROM vfile WHERE pathname=%Q", blob_str(&b)); |
| 636 | 639 | if( iId<0 ){ |
| 637 | - fossil_fatal("fossil knows nothing about: %s", g.argv[ii]); | |
| 640 | + fossil_warning("fossil knows nothing about: %s", g.argv[ii]); | |
| 641 | + result = 1; | |
| 642 | + } else { | |
| 643 | + g.aCommitFile[jj++] = iId; | |
| 638 | 644 | } |
| 639 | - g.aCommitFile[ii-2] = iId; | |
| 640 | 645 | blob_reset(&b); |
| 641 | 646 | } |
| 642 | - g.aCommitFile[ii-2] = 0; | |
| 647 | + g.aCommitFile[jj] = 0; | |
| 643 | 648 | } |
| 649 | + return result; | |
| 644 | 650 | } |
| 645 | 651 | |
| 646 | 652 | /* |
| 647 | 653 | ** Make sure the current check-in with timestamp zDate is younger than its |
| 648 | 654 | ** ancestor identified rid and zUuid. Throw a fatal error if not. |
| @@ -898,11 +904,11 @@ | ||
| 898 | 904 | */ |
| 899 | 905 | static int commit_warning( |
| 900 | 906 | Blob *p, /* The content of the file being committed. */ |
| 901 | 907 | int crnlOk, /* Non-zero if CR/NL warnings should be disabled. */ |
| 902 | 908 | int binOk, /* Non-zero if binary warnings should be disabled. */ |
| 903 | - int unicodeOk, /* Non-zero if unicode warnings should be disabled. */ | |
| 909 | + int encodingOk, /* Non-zero if encoding warnings should be disabled. */ | |
| 904 | 910 | const char *zFilename /* The full name of the file being committed. */ |
| 905 | 911 | ){ |
| 906 | 912 | int eType; /* return value of looks_like_utf8/utf16() */ |
| 907 | 913 | int fUnicode; /* return value of starts_with_utf16_bom() */ |
| 908 | 914 | char *zMsg; /* Warning message */ |
| @@ -912,44 +918,50 @@ | ||
| 912 | 918 | if( allOk ) return 0; |
| 913 | 919 | fUnicode = starts_with_utf16_bom(p, 0); |
| 914 | 920 | eType = fUnicode ? looks_like_utf16(p) : looks_like_utf8(p); |
| 915 | 921 | if( eType==0 || eType==-1 || fUnicode ){ |
| 916 | 922 | const char *zWarning; |
| 923 | + const char *zDisable; | |
| 917 | 924 | const char *zConvert = "c=convert/"; |
| 918 | 925 | Blob ans; |
| 919 | 926 | char cReply; |
| 920 | 927 | |
| 921 | 928 | if( eType==-1 && fUnicode ){ |
| 922 | - if ( crnlOk && unicodeOk ){ | |
| 923 | - return 0; /* We don't want Unicode/CR/NL warnings for this file. */ | |
| 929 | + if ( crnlOk && encodingOk ){ | |
| 930 | + return 0; /* We don't want CR/NL and Unicode warnings for this file. */ | |
| 924 | 931 | } |
| 925 | - zWarning = "Unicode and CR/NL line endings"; | |
| 932 | + zWarning = "CR/NL line endings and Unicode"; | |
| 933 | + zDisable = "\"crnl-glob\" and \"encoding-glob\" settings"; | |
| 926 | 934 | }else if( eType==-1 ){ |
| 927 | 935 | if( crnlOk ){ |
| 928 | 936 | return 0; /* We don't want CR/NL warnings for this file. */ |
| 929 | 937 | } |
| 930 | 938 | zWarning = "CR/NL line endings"; |
| 939 | + zDisable = "\"crnl-glob\" setting"; | |
| 931 | 940 | }else if( eType==0 ){ |
| 932 | 941 | if( binOk ){ |
| 933 | 942 | return 0; /* We don't want binary warnings for this file. */ |
| 934 | 943 | } |
| 935 | 944 | zWarning = "binary data"; |
| 945 | + zDisable = "\"binary-glob\" setting"; | |
| 936 | 946 | zConvert = ""; /* We cannot convert binary files. */ |
| 937 | 947 | }else{ |
| 938 | - if ( unicodeOk ){ | |
| 939 | - return 0; /* We don't want unicode warnings for this file. */ | |
| 948 | + if ( encodingOk ){ | |
| 949 | + return 0; /* We don't want encoding warnings for this file. */ | |
| 940 | 950 | } |
| 941 | 951 | zWarning = "Unicode"; |
| 952 | + zDisable = "\"encoding-glob\" setting"; | |
| 942 | 953 | #ifndef _WIN32 |
| 943 | 954 | zConvert = ""; /* On Unix, we cannot easily convert Unicode files. */ |
| 944 | 955 | #endif |
| 945 | 956 | } |
| 946 | 957 | file_relative_name(zFilename, &fname, 0); |
| 947 | 958 | blob_zero(&ans); |
| 948 | 959 | zMsg = mprintf( |
| 949 | - "%s contains %s. commit anyhow (a=all/%sy/N)? ", | |
| 950 | - blob_str(&fname), zWarning, zConvert); | |
| 960 | + "%s contains %s. Use --no-warnings or the %s to disable this warning.\n" | |
| 961 | + "Commit anyhow (a=all/%sy/N)? ", | |
| 962 | + blob_str(&fname), zWarning, zDisable, zConvert); | |
| 951 | 963 | prompt_user(zMsg, &ans); |
| 952 | 964 | fossil_free(zMsg); |
| 953 | 965 | cReply = blob_str(&ans)[0]; |
| 954 | 966 | if( cReply=='a' || cReply=='A' ){ |
| 955 | 967 | allOk = 1; |
| @@ -1196,11 +1208,16 @@ | ||
| 1196 | 1208 | ** After the following function call has returned, the Global.aCommitFile[] |
| 1197 | 1209 | ** array is allocated to contain the "id" field from the vfile table |
| 1198 | 1210 | ** for each file to be committed. Or, if aCommitFile is NULL, all files |
| 1199 | 1211 | ** should be committed. |
| 1200 | 1212 | */ |
| 1201 | - select_commit_files(); | |
| 1213 | + if ( select_commit_files() ){ | |
| 1214 | + blob_zero(&ans); | |
| 1215 | + prompt_user("continue (y/N)? ", &ans); | |
| 1216 | + cReply = blob_str(&ans)[0]; | |
| 1217 | + if( cReply!='y' && cReply!='Y' ) fossil_exit(1);; | |
| 1218 | + } | |
| 1202 | 1219 | /* id=0 means that it introduces a new parent */ |
| 1203 | 1220 | isAMerge = db_exists("SELECT 1 FROM vmerge WHERE id=0"); |
| 1204 | 1221 | if( g.aCommitFile && isAMerge ){ |
| 1205 | 1222 | fossil_fatal("cannot do a partial commit of a graph merge"); |
| 1206 | 1223 | } |
| @@ -1318,25 +1335,25 @@ | ||
| 1318 | 1335 | "SELECT id, %Q || pathname, mrid, %s, chnged, %s, %s FROM vfile " |
| 1319 | 1336 | "WHERE chnged==1 AND NOT deleted AND is_selected(id)", |
| 1320 | 1337 | g.zLocalRoot, |
| 1321 | 1338 | glob_expr("pathname", db_get("crnl-glob","")), |
| 1322 | 1339 | glob_expr("pathname", db_get("binary-glob","")), |
| 1323 | - glob_expr("pathname", db_get("unicode-glob","")) | |
| 1340 | + glob_expr("pathname", db_get("encoding-glob","")) | |
| 1324 | 1341 | ); |
| 1325 | 1342 | while( db_step(&q)==SQLITE_ROW ){ |
| 1326 | 1343 | int id, rid; |
| 1327 | 1344 | const char *zFullname; |
| 1328 | 1345 | Blob content; |
| 1329 | - int crnlOk, binOk, unicodeOk, chnged; | |
| 1346 | + int crnlOk, binOk, encodingOk, chnged; | |
| 1330 | 1347 | |
| 1331 | 1348 | id = db_column_int(&q, 0); |
| 1332 | 1349 | zFullname = db_column_text(&q, 1); |
| 1333 | 1350 | rid = db_column_int(&q, 2); |
| 1334 | 1351 | crnlOk = db_column_int(&q, 3); |
| 1335 | 1352 | chnged = db_column_int(&q, 4); |
| 1336 | 1353 | binOk = db_column_int(&q, 5); |
| 1337 | - unicodeOk = db_column_int(&q, 6); | |
| 1354 | + encodingOk = db_column_int(&q, 6); | |
| 1338 | 1355 | |
| 1339 | 1356 | blob_zero(&content); |
| 1340 | 1357 | if( file_wd_islink(zFullname) ){ |
| 1341 | 1358 | /* Instead of file content, put link destination path */ |
| 1342 | 1359 | blob_read_link(&content, zFullname); |
| @@ -1344,11 +1361,11 @@ | ||
| 1344 | 1361 | blob_read_from_file(&content, zFullname); |
| 1345 | 1362 | } |
| 1346 | 1363 | /* Do not emit any warnings when they are disabled. */ |
| 1347 | 1364 | if( !noWarningFlag ){ |
| 1348 | 1365 | abortCommit |= commit_warning(&content, crnlOk, binOk, |
| 1349 | - unicodeOk, zFullname); | |
| 1366 | + encodingOk, zFullname); | |
| 1350 | 1367 | } |
| 1351 | 1368 | if( chnged==1 && contains_merge_marker(&content) ){ |
| 1352 | 1369 | Blob fname; /* Relative pathname of the file */ |
| 1353 | 1370 | |
| 1354 | 1371 | nConflict++; |
| @@ -1531,11 +1548,11 @@ | ||
| 1531 | 1548 | exit(1); |
| 1532 | 1549 | } |
| 1533 | 1550 | db_end_transaction(0); |
| 1534 | 1551 | |
| 1535 | 1552 | if( !g.markPrivate ){ |
| 1536 | - autosync(SYNC_PUSH); | |
| 1553 | + autosync(SYNC_PUSH|SYNC_PULL); | |
| 1537 | 1554 | } |
| 1538 | 1555 | if( count_nonbranch_children(vid)>1 ){ |
| 1539 | 1556 | fossil_print("**** warning: a fork has occurred *****\n"); |
| 1540 | 1557 | } |
| 1541 | 1558 | } |
| 1542 | 1559 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -619,30 +619,36 @@ | |
| 619 | ** of the array. |
| 620 | ** |
| 621 | ** If there were no arguments passed to [commit], aCommitFile is not |
| 622 | ** allocated and remains NULL. Other parts of the code interpret this |
| 623 | ** to mean "all files". |
| 624 | */ |
| 625 | void select_commit_files(void){ |
| 626 | if( g.argc>2 ){ |
| 627 | int ii; |
| 628 | Blob b; |
| 629 | blob_zero(&b); |
| 630 | g.aCommitFile = fossil_malloc(sizeof(int)*(g.argc-1)); |
| 631 | |
| 632 | for(ii=2; ii<g.argc; ii++){ |
| 633 | int iId; |
| 634 | file_tree_name(g.argv[ii], &b, 1); |
| 635 | iId = db_int(-1, "SELECT id FROM vfile WHERE pathname=%Q", blob_str(&b)); |
| 636 | if( iId<0 ){ |
| 637 | fossil_fatal("fossil knows nothing about: %s", g.argv[ii]); |
| 638 | } |
| 639 | g.aCommitFile[ii-2] = iId; |
| 640 | blob_reset(&b); |
| 641 | } |
| 642 | g.aCommitFile[ii-2] = 0; |
| 643 | } |
| 644 | } |
| 645 | |
| 646 | /* |
| 647 | ** Make sure the current check-in with timestamp zDate is younger than its |
| 648 | ** ancestor identified rid and zUuid. Throw a fatal error if not. |
| @@ -898,11 +904,11 @@ | |
| 898 | */ |
| 899 | static int commit_warning( |
| 900 | Blob *p, /* The content of the file being committed. */ |
| 901 | int crnlOk, /* Non-zero if CR/NL warnings should be disabled. */ |
| 902 | int binOk, /* Non-zero if binary warnings should be disabled. */ |
| 903 | int unicodeOk, /* Non-zero if unicode warnings should be disabled. */ |
| 904 | const char *zFilename /* The full name of the file being committed. */ |
| 905 | ){ |
| 906 | int eType; /* return value of looks_like_utf8/utf16() */ |
| 907 | int fUnicode; /* return value of starts_with_utf16_bom() */ |
| 908 | char *zMsg; /* Warning message */ |
| @@ -912,44 +918,50 @@ | |
| 912 | if( allOk ) return 0; |
| 913 | fUnicode = starts_with_utf16_bom(p, 0); |
| 914 | eType = fUnicode ? looks_like_utf16(p) : looks_like_utf8(p); |
| 915 | if( eType==0 || eType==-1 || fUnicode ){ |
| 916 | const char *zWarning; |
| 917 | const char *zConvert = "c=convert/"; |
| 918 | Blob ans; |
| 919 | char cReply; |
| 920 | |
| 921 | if( eType==-1 && fUnicode ){ |
| 922 | if ( crnlOk && unicodeOk ){ |
| 923 | return 0; /* We don't want Unicode/CR/NL warnings for this file. */ |
| 924 | } |
| 925 | zWarning = "Unicode and CR/NL line endings"; |
| 926 | }else if( eType==-1 ){ |
| 927 | if( crnlOk ){ |
| 928 | return 0; /* We don't want CR/NL warnings for this file. */ |
| 929 | } |
| 930 | zWarning = "CR/NL line endings"; |
| 931 | }else if( eType==0 ){ |
| 932 | if( binOk ){ |
| 933 | return 0; /* We don't want binary warnings for this file. */ |
| 934 | } |
| 935 | zWarning = "binary data"; |
| 936 | zConvert = ""; /* We cannot convert binary files. */ |
| 937 | }else{ |
| 938 | if ( unicodeOk ){ |
| 939 | return 0; /* We don't want unicode warnings for this file. */ |
| 940 | } |
| 941 | zWarning = "Unicode"; |
| 942 | #ifndef _WIN32 |
| 943 | zConvert = ""; /* On Unix, we cannot easily convert Unicode files. */ |
| 944 | #endif |
| 945 | } |
| 946 | file_relative_name(zFilename, &fname, 0); |
| 947 | blob_zero(&ans); |
| 948 | zMsg = mprintf( |
| 949 | "%s contains %s. commit anyhow (a=all/%sy/N)? ", |
| 950 | blob_str(&fname), zWarning, zConvert); |
| 951 | prompt_user(zMsg, &ans); |
| 952 | fossil_free(zMsg); |
| 953 | cReply = blob_str(&ans)[0]; |
| 954 | if( cReply=='a' || cReply=='A' ){ |
| 955 | allOk = 1; |
| @@ -1196,11 +1208,16 @@ | |
| 1196 | ** After the following function call has returned, the Global.aCommitFile[] |
| 1197 | ** array is allocated to contain the "id" field from the vfile table |
| 1198 | ** for each file to be committed. Or, if aCommitFile is NULL, all files |
| 1199 | ** should be committed. |
| 1200 | */ |
| 1201 | select_commit_files(); |
| 1202 | /* id=0 means that it introduces a new parent */ |
| 1203 | isAMerge = db_exists("SELECT 1 FROM vmerge WHERE id=0"); |
| 1204 | if( g.aCommitFile && isAMerge ){ |
| 1205 | fossil_fatal("cannot do a partial commit of a graph merge"); |
| 1206 | } |
| @@ -1318,25 +1335,25 @@ | |
| 1318 | "SELECT id, %Q || pathname, mrid, %s, chnged, %s, %s FROM vfile " |
| 1319 | "WHERE chnged==1 AND NOT deleted AND is_selected(id)", |
| 1320 | g.zLocalRoot, |
| 1321 | glob_expr("pathname", db_get("crnl-glob","")), |
| 1322 | glob_expr("pathname", db_get("binary-glob","")), |
| 1323 | glob_expr("pathname", db_get("unicode-glob","")) |
| 1324 | ); |
| 1325 | while( db_step(&q)==SQLITE_ROW ){ |
| 1326 | int id, rid; |
| 1327 | const char *zFullname; |
| 1328 | Blob content; |
| 1329 | int crnlOk, binOk, unicodeOk, chnged; |
| 1330 | |
| 1331 | id = db_column_int(&q, 0); |
| 1332 | zFullname = db_column_text(&q, 1); |
| 1333 | rid = db_column_int(&q, 2); |
| 1334 | crnlOk = db_column_int(&q, 3); |
| 1335 | chnged = db_column_int(&q, 4); |
| 1336 | binOk = db_column_int(&q, 5); |
| 1337 | unicodeOk = db_column_int(&q, 6); |
| 1338 | |
| 1339 | blob_zero(&content); |
| 1340 | if( file_wd_islink(zFullname) ){ |
| 1341 | /* Instead of file content, put link destination path */ |
| 1342 | blob_read_link(&content, zFullname); |
| @@ -1344,11 +1361,11 @@ | |
| 1344 | blob_read_from_file(&content, zFullname); |
| 1345 | } |
| 1346 | /* Do not emit any warnings when they are disabled. */ |
| 1347 | if( !noWarningFlag ){ |
| 1348 | abortCommit |= commit_warning(&content, crnlOk, binOk, |
| 1349 | unicodeOk, zFullname); |
| 1350 | } |
| 1351 | if( chnged==1 && contains_merge_marker(&content) ){ |
| 1352 | Blob fname; /* Relative pathname of the file */ |
| 1353 | |
| 1354 | nConflict++; |
| @@ -1531,11 +1548,11 @@ | |
| 1531 | exit(1); |
| 1532 | } |
| 1533 | db_end_transaction(0); |
| 1534 | |
| 1535 | if( !g.markPrivate ){ |
| 1536 | autosync(SYNC_PUSH); |
| 1537 | } |
| 1538 | if( count_nonbranch_children(vid)>1 ){ |
| 1539 | fossil_print("**** warning: a fork has occurred *****\n"); |
| 1540 | } |
| 1541 | } |
| 1542 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -619,30 +619,36 @@ | |
| 619 | ** of the array. |
| 620 | ** |
| 621 | ** If there were no arguments passed to [commit], aCommitFile is not |
| 622 | ** allocated and remains NULL. Other parts of the code interpret this |
| 623 | ** to mean "all files". |
| 624 | ** |
| 625 | ** Returns 1 if there was a warning, 0 otherwise. |
| 626 | */ |
| 627 | int select_commit_files(void){ |
| 628 | int result = 0; |
| 629 | if( g.argc>2 ){ |
| 630 | int ii, jj=0; |
| 631 | Blob b; |
| 632 | blob_zero(&b); |
| 633 | g.aCommitFile = fossil_malloc(sizeof(int)*(g.argc-1)); |
| 634 | |
| 635 | for(ii=2; ii<g.argc; ii++){ |
| 636 | int iId; |
| 637 | file_tree_name(g.argv[ii], &b, 1); |
| 638 | iId = db_int(-1, "SELECT id FROM vfile WHERE pathname=%Q", blob_str(&b)); |
| 639 | if( iId<0 ){ |
| 640 | fossil_warning("fossil knows nothing about: %s", g.argv[ii]); |
| 641 | result = 1; |
| 642 | } else { |
| 643 | g.aCommitFile[jj++] = iId; |
| 644 | } |
| 645 | blob_reset(&b); |
| 646 | } |
| 647 | g.aCommitFile[jj] = 0; |
| 648 | } |
| 649 | return result; |
| 650 | } |
| 651 | |
| 652 | /* |
| 653 | ** Make sure the current check-in with timestamp zDate is younger than its |
| 654 | ** ancestor identified rid and zUuid. Throw a fatal error if not. |
| @@ -898,11 +904,11 @@ | |
| 904 | */ |
| 905 | static int commit_warning( |
| 906 | Blob *p, /* The content of the file being committed. */ |
| 907 | int crnlOk, /* Non-zero if CR/NL warnings should be disabled. */ |
| 908 | int binOk, /* Non-zero if binary warnings should be disabled. */ |
| 909 | int encodingOk, /* Non-zero if encoding warnings should be disabled. */ |
| 910 | const char *zFilename /* The full name of the file being committed. */ |
| 911 | ){ |
| 912 | int eType; /* return value of looks_like_utf8/utf16() */ |
| 913 | int fUnicode; /* return value of starts_with_utf16_bom() */ |
| 914 | char *zMsg; /* Warning message */ |
| @@ -912,44 +918,50 @@ | |
| 918 | if( allOk ) return 0; |
| 919 | fUnicode = starts_with_utf16_bom(p, 0); |
| 920 | eType = fUnicode ? looks_like_utf16(p) : looks_like_utf8(p); |
| 921 | if( eType==0 || eType==-1 || fUnicode ){ |
| 922 | const char *zWarning; |
| 923 | const char *zDisable; |
| 924 | const char *zConvert = "c=convert/"; |
| 925 | Blob ans; |
| 926 | char cReply; |
| 927 | |
| 928 | if( eType==-1 && fUnicode ){ |
| 929 | if ( crnlOk && encodingOk ){ |
| 930 | return 0; /* We don't want CR/NL and Unicode warnings for this file. */ |
| 931 | } |
| 932 | zWarning = "CR/NL line endings and Unicode"; |
| 933 | zDisable = "\"crnl-glob\" and \"encoding-glob\" settings"; |
| 934 | }else if( eType==-1 ){ |
| 935 | if( crnlOk ){ |
| 936 | return 0; /* We don't want CR/NL warnings for this file. */ |
| 937 | } |
| 938 | zWarning = "CR/NL line endings"; |
| 939 | zDisable = "\"crnl-glob\" setting"; |
| 940 | }else if( eType==0 ){ |
| 941 | if( binOk ){ |
| 942 | return 0; /* We don't want binary warnings for this file. */ |
| 943 | } |
| 944 | zWarning = "binary data"; |
| 945 | zDisable = "\"binary-glob\" setting"; |
| 946 | zConvert = ""; /* We cannot convert binary files. */ |
| 947 | }else{ |
| 948 | if ( encodingOk ){ |
| 949 | return 0; /* We don't want encoding warnings for this file. */ |
| 950 | } |
| 951 | zWarning = "Unicode"; |
| 952 | zDisable = "\"encoding-glob\" setting"; |
| 953 | #ifndef _WIN32 |
| 954 | zConvert = ""; /* On Unix, we cannot easily convert Unicode files. */ |
| 955 | #endif |
| 956 | } |
| 957 | file_relative_name(zFilename, &fname, 0); |
| 958 | blob_zero(&ans); |
| 959 | zMsg = mprintf( |
| 960 | "%s contains %s. Use --no-warnings or the %s to disable this warning.\n" |
| 961 | "Commit anyhow (a=all/%sy/N)? ", |
| 962 | blob_str(&fname), zWarning, zDisable, zConvert); |
| 963 | prompt_user(zMsg, &ans); |
| 964 | fossil_free(zMsg); |
| 965 | cReply = blob_str(&ans)[0]; |
| 966 | if( cReply=='a' || cReply=='A' ){ |
| 967 | allOk = 1; |
| @@ -1196,11 +1208,16 @@ | |
| 1208 | ** After the following function call has returned, the Global.aCommitFile[] |
| 1209 | ** array is allocated to contain the "id" field from the vfile table |
| 1210 | ** for each file to be committed. Or, if aCommitFile is NULL, all files |
| 1211 | ** should be committed. |
| 1212 | */ |
| 1213 | if ( select_commit_files() ){ |
| 1214 | blob_zero(&ans); |
| 1215 | prompt_user("continue (y/N)? ", &ans); |
| 1216 | cReply = blob_str(&ans)[0]; |
| 1217 | if( cReply!='y' && cReply!='Y' ) fossil_exit(1);; |
| 1218 | } |
| 1219 | /* id=0 means that it introduces a new parent */ |
| 1220 | isAMerge = db_exists("SELECT 1 FROM vmerge WHERE id=0"); |
| 1221 | if( g.aCommitFile && isAMerge ){ |
| 1222 | fossil_fatal("cannot do a partial commit of a graph merge"); |
| 1223 | } |
| @@ -1318,25 +1335,25 @@ | |
| 1335 | "SELECT id, %Q || pathname, mrid, %s, chnged, %s, %s FROM vfile " |
| 1336 | "WHERE chnged==1 AND NOT deleted AND is_selected(id)", |
| 1337 | g.zLocalRoot, |
| 1338 | glob_expr("pathname", db_get("crnl-glob","")), |
| 1339 | glob_expr("pathname", db_get("binary-glob","")), |
| 1340 | glob_expr("pathname", db_get("encoding-glob","")) |
| 1341 | ); |
| 1342 | while( db_step(&q)==SQLITE_ROW ){ |
| 1343 | int id, rid; |
| 1344 | const char *zFullname; |
| 1345 | Blob content; |
| 1346 | int crnlOk, binOk, encodingOk, chnged; |
| 1347 | |
| 1348 | id = db_column_int(&q, 0); |
| 1349 | zFullname = db_column_text(&q, 1); |
| 1350 | rid = db_column_int(&q, 2); |
| 1351 | crnlOk = db_column_int(&q, 3); |
| 1352 | chnged = db_column_int(&q, 4); |
| 1353 | binOk = db_column_int(&q, 5); |
| 1354 | encodingOk = db_column_int(&q, 6); |
| 1355 | |
| 1356 | blob_zero(&content); |
| 1357 | if( file_wd_islink(zFullname) ){ |
| 1358 | /* Instead of file content, put link destination path */ |
| 1359 | blob_read_link(&content, zFullname); |
| @@ -1344,11 +1361,11 @@ | |
| 1361 | blob_read_from_file(&content, zFullname); |
| 1362 | } |
| 1363 | /* Do not emit any warnings when they are disabled. */ |
| 1364 | if( !noWarningFlag ){ |
| 1365 | abortCommit |= commit_warning(&content, crnlOk, binOk, |
| 1366 | encodingOk, zFullname); |
| 1367 | } |
| 1368 | if( chnged==1 && contains_merge_marker(&content) ){ |
| 1369 | Blob fname; /* Relative pathname of the file */ |
| 1370 | |
| 1371 | nConflict++; |
| @@ -1531,11 +1548,11 @@ | |
| 1548 | exit(1); |
| 1549 | } |
| 1550 | db_end_transaction(0); |
| 1551 | |
| 1552 | if( !g.markPrivate ){ |
| 1553 | autosync(SYNC_PUSH|SYNC_PULL); |
| 1554 | } |
| 1555 | if( count_nonbranch_children(vid)>1 ){ |
| 1556 | fossil_print("**** warning: a fork has occurred *****\n"); |
| 1557 | } |
| 1558 | } |
| 1559 |
+1
-1
| --- src/configure.c | ||
| +++ src/configure.c | ||
| @@ -103,11 +103,11 @@ | ||
| 103 | 103 | { "project-description", CONFIGSET_PROJ }, |
| 104 | 104 | { "manifest", CONFIGSET_PROJ }, |
| 105 | 105 | { "binary-glob", CONFIGSET_PROJ }, |
| 106 | 106 | { "ignore-glob", CONFIGSET_PROJ }, |
| 107 | 107 | { "crnl-glob", CONFIGSET_PROJ }, |
| 108 | - { "unicode-glob", CONFIGSET_PROJ }, | |
| 108 | + { "encoding-glob", CONFIGSET_PROJ }, | |
| 109 | 109 | { "empty-dirs", CONFIGSET_PROJ }, |
| 110 | 110 | { "allow-symlinks", CONFIGSET_PROJ }, |
| 111 | 111 | |
| 112 | 112 | { "ticket-table", CONFIGSET_TKT }, |
| 113 | 113 | { "ticket-common", CONFIGSET_TKT }, |
| 114 | 114 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -103,11 +103,11 @@ | |
| 103 | { "project-description", CONFIGSET_PROJ }, |
| 104 | { "manifest", CONFIGSET_PROJ }, |
| 105 | { "binary-glob", CONFIGSET_PROJ }, |
| 106 | { "ignore-glob", CONFIGSET_PROJ }, |
| 107 | { "crnl-glob", CONFIGSET_PROJ }, |
| 108 | { "unicode-glob", CONFIGSET_PROJ }, |
| 109 | { "empty-dirs", CONFIGSET_PROJ }, |
| 110 | { "allow-symlinks", CONFIGSET_PROJ }, |
| 111 | |
| 112 | { "ticket-table", CONFIGSET_TKT }, |
| 113 | { "ticket-common", CONFIGSET_TKT }, |
| 114 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -103,11 +103,11 @@ | |
| 103 | { "project-description", CONFIGSET_PROJ }, |
| 104 | { "manifest", CONFIGSET_PROJ }, |
| 105 | { "binary-glob", CONFIGSET_PROJ }, |
| 106 | { "ignore-glob", CONFIGSET_PROJ }, |
| 107 | { "crnl-glob", CONFIGSET_PROJ }, |
| 108 | { "encoding-glob", CONFIGSET_PROJ }, |
| 109 | { "empty-dirs", CONFIGSET_PROJ }, |
| 110 | { "allow-symlinks", CONFIGSET_PROJ }, |
| 111 | |
| 112 | { "ticket-table", CONFIGSET_TKT }, |
| 113 | { "ticket-common", CONFIGSET_TKT }, |
| 114 |
+1
-1
| --- src/configure.c | ||
| +++ src/configure.c | ||
| @@ -103,11 +103,11 @@ | ||
| 103 | 103 | { "project-description", CONFIGSET_PROJ }, |
| 104 | 104 | { "manifest", CONFIGSET_PROJ }, |
| 105 | 105 | { "binary-glob", CONFIGSET_PROJ }, |
| 106 | 106 | { "ignore-glob", CONFIGSET_PROJ }, |
| 107 | 107 | { "crnl-glob", CONFIGSET_PROJ }, |
| 108 | - { "unicode-glob", CONFIGSET_PROJ }, | |
| 108 | + { "encoding-glob", CONFIGSET_PROJ }, | |
| 109 | 109 | { "empty-dirs", CONFIGSET_PROJ }, |
| 110 | 110 | { "allow-symlinks", CONFIGSET_PROJ }, |
| 111 | 111 | |
| 112 | 112 | { "ticket-table", CONFIGSET_TKT }, |
| 113 | 113 | { "ticket-common", CONFIGSET_TKT }, |
| 114 | 114 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -103,11 +103,11 @@ | |
| 103 | { "project-description", CONFIGSET_PROJ }, |
| 104 | { "manifest", CONFIGSET_PROJ }, |
| 105 | { "binary-glob", CONFIGSET_PROJ }, |
| 106 | { "ignore-glob", CONFIGSET_PROJ }, |
| 107 | { "crnl-glob", CONFIGSET_PROJ }, |
| 108 | { "unicode-glob", CONFIGSET_PROJ }, |
| 109 | { "empty-dirs", CONFIGSET_PROJ }, |
| 110 | { "allow-symlinks", CONFIGSET_PROJ }, |
| 111 | |
| 112 | { "ticket-table", CONFIGSET_TKT }, |
| 113 | { "ticket-common", CONFIGSET_TKT }, |
| 114 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -103,11 +103,11 @@ | |
| 103 | { "project-description", CONFIGSET_PROJ }, |
| 104 | { "manifest", CONFIGSET_PROJ }, |
| 105 | { "binary-glob", CONFIGSET_PROJ }, |
| 106 | { "ignore-glob", CONFIGSET_PROJ }, |
| 107 | { "crnl-glob", CONFIGSET_PROJ }, |
| 108 | { "encoding-glob", CONFIGSET_PROJ }, |
| 109 | { "empty-dirs", CONFIGSET_PROJ }, |
| 110 | { "allow-symlinks", CONFIGSET_PROJ }, |
| 111 | |
| 112 | { "ticket-table", CONFIGSET_TKT }, |
| 113 | { "ticket-common", CONFIGSET_TKT }, |
| 114 |
| --- src/cson_amalgamation.h | ||
| +++ src/cson_amalgamation.h | ||
| @@ -1,6 +1,9 @@ | ||
| 1 | 1 | #ifdef FOSSIL_ENABLE_JSON |
| 2 | +#ifndef CSON_FOSSIL_MODE | |
| 3 | +#define CSON_FOSSIL_MODE | |
| 4 | +#endif | |
| 2 | 5 | /* auto-generated! Do not edit! */ |
| 3 | 6 | /* begin file include/wh/cson/cson.h */ |
| 4 | 7 | #if !defined(WANDERINGHORSE_NET_CSON_H_INCLUDED) |
| 5 | 8 | #define WANDERINGHORSE_NET_CSON_H_INCLUDED 1 |
| 6 | 9 | |
| 7 | 10 |
| --- src/cson_amalgamation.h | |
| +++ src/cson_amalgamation.h | |
| @@ -1,6 +1,9 @@ | |
| 1 | #ifdef FOSSIL_ENABLE_JSON |
| 2 | /* auto-generated! Do not edit! */ |
| 3 | /* begin file include/wh/cson/cson.h */ |
| 4 | #if !defined(WANDERINGHORSE_NET_CSON_H_INCLUDED) |
| 5 | #define WANDERINGHORSE_NET_CSON_H_INCLUDED 1 |
| 6 | |
| 7 |
| --- src/cson_amalgamation.h | |
| +++ src/cson_amalgamation.h | |
| @@ -1,6 +1,9 @@ | |
| 1 | #ifdef FOSSIL_ENABLE_JSON |
| 2 | #ifndef CSON_FOSSIL_MODE |
| 3 | #define CSON_FOSSIL_MODE |
| 4 | #endif |
| 5 | /* auto-generated! Do not edit! */ |
| 6 | /* begin file include/wh/cson/cson.h */ |
| 7 | #if !defined(WANDERINGHORSE_NET_CSON_H_INCLUDED) |
| 8 | #define WANDERINGHORSE_NET_CSON_H_INCLUDED 1 |
| 9 | |
| 10 |
| --- src/cson_amalgamation.h | ||
| +++ src/cson_amalgamation.h | ||
| @@ -1,6 +1,9 @@ | ||
| 1 | 1 | #ifdef FOSSIL_ENABLE_JSON |
| 2 | +#ifndef CSON_FOSSIL_MODE | |
| 3 | +#define CSON_FOSSIL_MODE | |
| 4 | +#endif | |
| 2 | 5 | /* auto-generated! Do not edit! */ |
| 3 | 6 | /* begin file include/wh/cson/cson.h */ |
| 4 | 7 | #if !defined(WANDERINGHORSE_NET_CSON_H_INCLUDED) |
| 5 | 8 | #define WANDERINGHORSE_NET_CSON_H_INCLUDED 1 |
| 6 | 9 | |
| 7 | 10 |
| --- src/cson_amalgamation.h | |
| +++ src/cson_amalgamation.h | |
| @@ -1,6 +1,9 @@ | |
| 1 | #ifdef FOSSIL_ENABLE_JSON |
| 2 | /* auto-generated! Do not edit! */ |
| 3 | /* begin file include/wh/cson/cson.h */ |
| 4 | #if !defined(WANDERINGHORSE_NET_CSON_H_INCLUDED) |
| 5 | #define WANDERINGHORSE_NET_CSON_H_INCLUDED 1 |
| 6 | |
| 7 |
| --- src/cson_amalgamation.h | |
| +++ src/cson_amalgamation.h | |
| @@ -1,6 +1,9 @@ | |
| 1 | #ifdef FOSSIL_ENABLE_JSON |
| 2 | #ifndef CSON_FOSSIL_MODE |
| 3 | #define CSON_FOSSIL_MODE |
| 4 | #endif |
| 5 | /* auto-generated! Do not edit! */ |
| 6 | /* begin file include/wh/cson/cson.h */ |
| 7 | #if !defined(WANDERINGHORSE_NET_CSON_H_INCLUDED) |
| 8 | #define WANDERINGHORSE_NET_CSON_H_INCLUDED 1 |
| 9 | |
| 10 |
M
src/db.c
+61
-62
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -22,11 +22,11 @@ | ||
| 22 | 22 | ** |
| 23 | 23 | ** (1) The "user" database in ~/.fossil |
| 24 | 24 | ** |
| 25 | 25 | ** (2) The "repository" database |
| 26 | 26 | ** |
| 27 | -** (3) A local checkout database named "_FOSSIL_" or ".fos" | |
| 27 | +** (3) A local checkout database named "_FOSSIL_" or ".fslckout" | |
| 28 | 28 | ** and located at the root of the local copy of the source tree. |
| 29 | 29 | ** |
| 30 | 30 | */ |
| 31 | 31 | #include "config.h" |
| 32 | 32 | #if ! defined(_WIN32) |
| @@ -89,11 +89,11 @@ | ||
| 89 | 89 | cgi_reply(); |
| 90 | 90 | } |
| 91 | 91 | else if( g.cgiOutput ){ |
| 92 | 92 | g.cgiOutput = 0; |
| 93 | 93 | cgi_printf("<h1>Database Error</h1>\n" |
| 94 | - "<pre>%h</pre><p>%s</p>", z, zRebuildMsg); | |
| 94 | + "<pre>%h</pre>\n<p>%s</p>\n", z, zRebuildMsg); | |
| 95 | 95 | cgi_reply(); |
| 96 | 96 | }else{ |
| 97 | 97 | fprintf(stderr, "%s: %s\n\n%s", g.argv[0], z, zRebuildMsg); |
| 98 | 98 | } |
| 99 | 99 | free(z); |
| @@ -484,20 +484,30 @@ | ||
| 484 | 484 | /* |
| 485 | 485 | ** Execute multiple SQL statements. |
| 486 | 486 | */ |
| 487 | 487 | int db_multi_exec(const char *zSql, ...){ |
| 488 | 488 | Blob sql; |
| 489 | - int rc; | |
| 489 | + int rc = SQLITE_OK; | |
| 490 | 490 | va_list ap; |
| 491 | - char *zErr = 0; | |
| 491 | + const char *z, *zEnd; | |
| 492 | + sqlite3_stmt *pStmt; | |
| 492 | 493 | blob_init(&sql, 0, 0); |
| 493 | 494 | va_start(ap, zSql); |
| 494 | 495 | blob_vappendf(&sql, zSql, ap); |
| 495 | 496 | va_end(ap); |
| 496 | - rc = sqlite3_exec(g.db, blob_buffer(&sql), 0, 0, &zErr); | |
| 497 | - if( rc!=SQLITE_OK ){ | |
| 498 | - db_err("%s\n%s", zErr, blob_buffer(&sql)); | |
| 497 | + z = blob_str(&sql); | |
| 498 | + while( rc==SQLITE_OK && z[0] ){ | |
| 499 | + pStmt = 0; | |
| 500 | + rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd); | |
| 501 | + if( rc!=SQLITE_OK ) break; | |
| 502 | + if( pStmt ){ | |
| 503 | + db.nPrepare++; | |
| 504 | + while( sqlite3_step(pStmt)==SQLITE_ROW ){} | |
| 505 | + rc = sqlite3_finalize(pStmt); | |
| 506 | + if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z); | |
| 507 | + } | |
| 508 | + z = zEnd; | |
| 499 | 509 | } |
| 500 | 510 | blob_reset(&sql); |
| 501 | 511 | return rc; |
| 502 | 512 | } |
| 503 | 513 | |
| @@ -642,15 +652,11 @@ | ||
| 642 | 652 | sqlite3 *db; |
| 643 | 653 | int rc; |
| 644 | 654 | const char *zSql; |
| 645 | 655 | va_list ap; |
| 646 | 656 | |
| 647 | - rc = sqlite3_open(zFileName, &db); | |
| 648 | - if( rc!=SQLITE_OK ){ | |
| 649 | - db_err(sqlite3_errmsg(db)); | |
| 650 | - } | |
| 651 | - sqlite3_busy_timeout(db, 5000); | |
| 657 | + db = db_open(zFileName); | |
| 652 | 658 | sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0); |
| 653 | 659 | rc = sqlite3_exec(db, zSchema, 0, 0, 0); |
| 654 | 660 | if( rc!=SQLITE_OK ){ |
| 655 | 661 | db_err(sqlite3_errmsg(db)); |
| 656 | 662 | } |
| @@ -697,29 +703,43 @@ | ||
| 697 | 703 | |
| 698 | 704 | /* |
| 699 | 705 | ** Open a database file. Return a pointer to the new database |
| 700 | 706 | ** connection. An error results in process abort. |
| 701 | 707 | */ |
| 702 | -static sqlite3 *openDatabase(const char *zDbName){ | |
| 708 | +LOCAL sqlite3 *db_open(const char *zDbName){ | |
| 703 | 709 | int rc; |
| 704 | 710 | const char *zVfs; |
| 705 | 711 | sqlite3 *db; |
| 706 | 712 | |
| 713 | + if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName); | |
| 707 | 714 | zVfs = fossil_getenv("FOSSIL_VFS"); |
| 708 | 715 | rc = sqlite3_open_v2( |
| 709 | 716 | zDbName, &db, |
| 710 | 717 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 711 | 718 | zVfs |
| 712 | 719 | ); |
| 713 | 720 | if( rc!=SQLITE_OK ){ |
| 714 | - db_err(sqlite3_errmsg(db)); | |
| 721 | + db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); | |
| 715 | 722 | } |
| 716 | 723 | sqlite3_busy_timeout(db, 5000); |
| 717 | 724 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 718 | 725 | sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0); |
| 719 | 726 | sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_ANY, 0, |
| 720 | 727 | db_checkin_mtime_function, 0, 0); |
| 728 | + sqlite3_create_function(db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0); | |
| 729 | + sqlite3_create_function(db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0); | |
| 730 | + sqlite3_create_function(db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0); | |
| 731 | + sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); | |
| 732 | + sqlite3_create_function( | |
| 733 | + db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 | |
| 734 | + ); | |
| 735 | + sqlite3_create_function( | |
| 736 | + db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 | |
| 737 | + ); | |
| 738 | + if( g.fSqlTrace ) sqlite3_trace(db, db_sql_trace, 0); | |
| 739 | + re_add_sql_func(db); | |
| 740 | + sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); | |
| 721 | 741 | return db; |
| 722 | 742 | } |
| 723 | 743 | |
| 724 | 744 | |
| 725 | 745 | /* |
| @@ -747,13 +767,12 @@ | ||
| 747 | 767 | const char *zLabel, |
| 748 | 768 | int *pWasAttached |
| 749 | 769 | ){ |
| 750 | 770 | if( !g.db ){ |
| 751 | 771 | assert( g.zMainDbType==0 ); |
| 752 | - g.db = openDatabase(zDbName); | |
| 772 | + g.db = db_open(zDbName); | |
| 753 | 773 | g.zMainDbType = zLabel; |
| 754 | - db_connection_init(); | |
| 755 | 774 | if ( pWasAttached ) *pWasAttached = 0; |
| 756 | 775 | }else{ |
| 757 | 776 | assert( g.zMainDbType!=0 ); |
| 758 | 777 | db_attach(zDbName, zLabel); |
| 759 | 778 | if ( pWasAttached ) *pWasAttached = 1; |
| @@ -820,11 +839,11 @@ | ||
| 820 | 839 | db_open_or_attach(zDbName, "configdb", &g.useAttach); |
| 821 | 840 | g.dbConfig = 0; |
| 822 | 841 | g.zConfigDbType = 0; |
| 823 | 842 | }else{ |
| 824 | 843 | g.useAttach = 0; |
| 825 | - g.dbConfig = openDatabase(zDbName); | |
| 844 | + g.dbConfig = db_open(zDbName); | |
| 826 | 845 | g.zConfigDbType = "configdb"; |
| 827 | 846 | } |
| 828 | 847 | g.configOpen = 1; |
| 829 | 848 | free(zDbName); |
| 830 | 849 | } |
| @@ -1221,10 +1240,13 @@ | ||
| 1221 | 1240 | void db_create_default_users(int setupUserOnly, const char *zDefaultUser){ |
| 1222 | 1241 | const char *zUser = zDefaultUser; |
| 1223 | 1242 | if( zUser==0 ){ |
| 1224 | 1243 | zUser = db_get("default-user", 0); |
| 1225 | 1244 | } |
| 1245 | + if( zUser==0 ){ | |
| 1246 | + zUser = fossil_getenv("FOSSIL_USER"); | |
| 1247 | + } | |
| 1226 | 1248 | if( zUser==0 ){ |
| 1227 | 1249 | #if defined(_WIN32) |
| 1228 | 1250 | zUser = fossil_getenv("USERNAME"); |
| 1229 | 1251 | #else |
| 1230 | 1252 | zUser = fossil_getenv("USER"); |
| @@ -1441,11 +1463,11 @@ | ||
| 1441 | 1463 | ** SQL functions for debugging. |
| 1442 | 1464 | ** |
| 1443 | 1465 | ** The print() function writes its arguments on stdout, but only |
| 1444 | 1466 | ** if the -sqlprint command-line option is turned on. |
| 1445 | 1467 | */ |
| 1446 | -static void db_sql_print( | |
| 1468 | +LOCAL void db_sql_print( | |
| 1447 | 1469 | sqlite3_context *context, |
| 1448 | 1470 | int argc, |
| 1449 | 1471 | sqlite3_value **argv |
| 1450 | 1472 | ){ |
| 1451 | 1473 | int i; |
| @@ -1454,22 +1476,20 @@ | ||
| 1454 | 1476 | char c = i==argc-1 ? '\n' : ' '; |
| 1455 | 1477 | fossil_print("%s%c", sqlite3_value_text(argv[i]), c); |
| 1456 | 1478 | } |
| 1457 | 1479 | } |
| 1458 | 1480 | } |
| 1459 | -static void db_sql_trace(void *notUsed, const char *zSql){ | |
| 1481 | +LOCAL void db_sql_trace(void *notUsed, const char *zSql){ | |
| 1460 | 1482 | int n = strlen(zSql); |
| 1461 | - char *zMsg = mprintf("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";"); | |
| 1462 | - fossil_puts(zMsg, 1); | |
| 1463 | - fossil_free(zMsg); | |
| 1483 | + fossil_trace("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";"); | |
| 1464 | 1484 | } |
| 1465 | 1485 | |
| 1466 | 1486 | /* |
| 1467 | 1487 | ** Implement the user() SQL function. user() takes no arguments and |
| 1468 | 1488 | ** returns the user ID of the current user. |
| 1469 | 1489 | */ |
| 1470 | -static void db_sql_user( | |
| 1490 | +LOCAL void db_sql_user( | |
| 1471 | 1491 | sqlite3_context *context, |
| 1472 | 1492 | int argc, |
| 1473 | 1493 | sqlite3_value **argv |
| 1474 | 1494 | ){ |
| 1475 | 1495 | if( g.zLogin!=0 ){ |
| @@ -1481,11 +1501,11 @@ | ||
| 1481 | 1501 | ** Implement the cgi() SQL function. cgi() takes an argument which is |
| 1482 | 1502 | ** a name of CGI query parameter. The value of that parameter is returned, |
| 1483 | 1503 | ** if available. Optional second argument will be returned if the first |
| 1484 | 1504 | ** doesn't exist as a CGI parameter. |
| 1485 | 1505 | */ |
| 1486 | -static void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){ | |
| 1506 | +LOCAL void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){ | |
| 1487 | 1507 | const char* zP; |
| 1488 | 1508 | if( argc!=1 && argc!=2 ) return; |
| 1489 | 1509 | zP = P((const char*)sqlite3_value_text(argv[0])); |
| 1490 | 1510 | if( zP ){ |
| 1491 | 1511 | sqlite3_result_text(context, zP, -1, SQLITE_STATIC); |
| @@ -1512,11 +1532,11 @@ | ||
| 1512 | 1532 | ** (meaning that id was named on the command-line). |
| 1513 | 1533 | ** |
| 1514 | 1534 | ** In the second form (3 arguments) return argument X if true and Y |
| 1515 | 1535 | ** if false. Except if Y is NULL then always return X. |
| 1516 | 1536 | */ |
| 1517 | -static void file_is_selected( | |
| 1537 | +LOCAL void file_is_selected( | |
| 1518 | 1538 | sqlite3_context *context, |
| 1519 | 1539 | int argc, |
| 1520 | 1540 | sqlite3_value **argv |
| 1521 | 1541 | ){ |
| 1522 | 1542 | int rc = 0; |
| @@ -1600,32 +1620,10 @@ | ||
| 1600 | 1620 | zOut = mprintf("%s", zKey); |
| 1601 | 1621 | } |
| 1602 | 1622 | return zOut; |
| 1603 | 1623 | } |
| 1604 | 1624 | |
| 1605 | -/* | |
| 1606 | -** This function registers auxiliary functions when the SQLite | |
| 1607 | -** database connection is first established. | |
| 1608 | -*/ | |
| 1609 | -void db_connection_init(void){ | |
| 1610 | - sqlite3_exec(g.db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); | |
| 1611 | - sqlite3_create_function(g.db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0); | |
| 1612 | - sqlite3_create_function(g.db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0); | |
| 1613 | - sqlite3_create_function(g.db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0); | |
| 1614 | - sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); | |
| 1615 | - sqlite3_create_function( | |
| 1616 | - g.db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 | |
| 1617 | - ); | |
| 1618 | - sqlite3_create_function( | |
| 1619 | - g.db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 | |
| 1620 | - ); | |
| 1621 | - if( g.fSqlTrace ){ | |
| 1622 | - sqlite3_trace(g.db, db_sql_trace, 0); | |
| 1623 | - } | |
| 1624 | - re_add_sql_func(g.db); | |
| 1625 | -} | |
| 1626 | - | |
| 1627 | 1625 | /* |
| 1628 | 1626 | ** Return true if the string zVal represents "true" (or "false"). |
| 1629 | 1627 | */ |
| 1630 | 1628 | int is_truth(const char *zVal){ |
| 1631 | 1629 | static const char *const azOn[] = { "on", "yes", "true", "1" }; |
| @@ -2066,17 +2064,18 @@ | ||
| 2066 | 2064 | { "default-perms", 0, 16, 0, "u" }, |
| 2067 | 2065 | { "diff-binary", 0, 0, 0, "on" }, |
| 2068 | 2066 | { "diff-command", 0, 40, 0, "" }, |
| 2069 | 2067 | { "dont-push", 0, 0, 0, "off" }, |
| 2070 | 2068 | { "editor", 0, 32, 0, "" }, |
| 2069 | + { "empty-dirs", 0, 40, 1, "" }, | |
| 2070 | + { "encoding-glob", 0, 40, 1, "" }, | |
| 2071 | 2071 | { "gdiff-command", 0, 40, 0, "gdiff" }, |
| 2072 | 2072 | { "gmerge-command",0, 40, 0, "" }, |
| 2073 | + { "http-port", 0, 16, 0, "8080" }, | |
| 2073 | 2074 | { "https-login", 0, 0, 0, "off" }, |
| 2074 | - { "ignore-glob", 0, 40, 1, "" }, | |
| 2075 | - { "empty-dirs", 0, 40, 1, "" }, | |
| 2076 | 2075 | { "href-targets", 0, 0, 0, "on" }, |
| 2077 | - { "http-port", 0, 16, 0, "8080" }, | |
| 2076 | + { "ignore-glob", 0, 40, 1, "" }, | |
| 2078 | 2077 | { "localauth", 0, 0, 0, "off" }, |
| 2079 | 2078 | { "main-branch", 0, 40, 0, "trunk" }, |
| 2080 | 2079 | { "manifest", 0, 0, 1, "off" }, |
| 2081 | 2080 | #ifdef FOSSIL_ENABLE_MARKDOWN |
| 2082 | 2081 | { "markdown", 0, 0, 0, "off" }, |
| @@ -2086,19 +2085,18 @@ | ||
| 2086 | 2085 | { "pgp-command", 0, 40, 0, "gpg --clearsign -o " }, |
| 2087 | 2086 | { "proxy", 0, 32, 0, "off" }, |
| 2088 | 2087 | { "relative-paths",0, 0, 0, "on" }, |
| 2089 | 2088 | { "repo-cksum", 0, 0, 0, "on" }, |
| 2090 | 2089 | { "self-register", 0, 0, 0, "off" }, |
| 2090 | + { "ssh-command", 0, 40, 0, "" }, | |
| 2091 | 2091 | { "ssl-ca-location",0, 40, 0, "" }, |
| 2092 | 2092 | { "ssl-identity", 0, 40, 0, "" }, |
| 2093 | - { "ssh-command", 0, 40, 0, "" }, | |
| 2094 | - { "th1-setup", 0, 40, 0, "" }, | |
| 2095 | 2093 | #ifdef FOSSIL_ENABLE_TCL |
| 2096 | 2094 | { "tcl", 0, 0, 0, "off" }, |
| 2097 | 2095 | { "tcl-setup", 0, 40, 0, "" }, |
| 2098 | 2096 | #endif |
| 2099 | - { "unicode-glob", 0, 40, 1, "" }, | |
| 2097 | + { "th1-setup", 0, 40, 0, "" }, | |
| 2100 | 2098 | { "web-browser", 0, 32, 0, "" }, |
| 2101 | 2099 | { "white-foreground", 0, 0, 0, "off" }, |
| 2102 | 2100 | { 0,0,0,0,0 } |
| 2103 | 2101 | }; |
| 2104 | 2102 | |
| @@ -2175,17 +2173,23 @@ | ||
| 2175 | 2173 | ** diff-command External command to run when performing a diff. |
| 2176 | 2174 | ** If undefined, the internal text diff will be used. |
| 2177 | 2175 | ** |
| 2178 | 2176 | ** dont-push Prevent this repository from pushing from client to |
| 2179 | 2177 | ** server. Useful when setting up a private branch. |
| 2178 | +** | |
| 2179 | +** editor Text editor command used for check-in comments. | |
| 2180 | 2180 | ** |
| 2181 | 2181 | ** empty-dirs A comma or newline-separated list of pathnames. On |
| 2182 | 2182 | ** (versionable) update and checkout commands, if no file or directory |
| 2183 | 2183 | ** exists with that name, an empty directory will be |
| 2184 | 2184 | ** created. |
| 2185 | 2185 | ** |
| 2186 | -** editor Text editor command used for check-in comments. | |
| 2186 | +** encoding-glob The VALUE is a comma or newline-separated list of GLOB | |
| 2187 | +** (versionable) patterns specifying files that the "commit" command will | |
| 2188 | +** ignore when issuing warnings about text files that may | |
| 2189 | +** use another encoding than ASCII or UTF-8. Set to "*" | |
| 2190 | +** to disable encoding checking. | |
| 2187 | 2191 | ** |
| 2188 | 2192 | ** gdiff-command External command to run when performing a graphical |
| 2189 | 2193 | ** diff. If undefined, text diff will be used. |
| 2190 | 2194 | ** |
| 2191 | 2195 | ** gmerge-command A graphical merge conflict resolver command operating |
| @@ -2244,10 +2248,13 @@ | ||
| 2244 | 2248 | ** |
| 2245 | 2249 | ** self-register Allow users to register themselves through the HTTP UI. |
| 2246 | 2250 | ** This is useful if you want to see other names than |
| 2247 | 2251 | ** "Anonymous" in e.g. ticketing system. On the other hand |
| 2248 | 2252 | ** users can not be deleted. Default: off. |
| 2253 | +** | |
| 2254 | +** ssh-command Command used to talk to a remote machine with | |
| 2255 | +** the "ssh://" protocol. | |
| 2249 | 2256 | ** |
| 2250 | 2257 | ** ssl-ca-location The full pathname to a file containing PEM encoded |
| 2251 | 2258 | ** CA root certificates, or a directory of certificates |
| 2252 | 2259 | ** with filenames formed from the certificate hashes as |
| 2253 | 2260 | ** required by OpenSSL. |
| @@ -2263,13 +2270,10 @@ | ||
| 2263 | 2270 | ** the certificate and private key files. |
| 2264 | 2271 | ** This identity will be presented to SSL servers to |
| 2265 | 2272 | ** authenticate this client, in addition to the normal |
| 2266 | 2273 | ** password authentication. |
| 2267 | 2274 | ** |
| 2268 | -** ssh-command Command used to talk to a remote machine with | |
| 2269 | -** the "ssh://" protocol. | |
| 2270 | -** | |
| 2271 | 2275 | ** tcl If enabled (and Fossil was compiled with Tcl support), |
| 2272 | 2276 | ** Tcl integration commands will be added to the TH1 |
| 2273 | 2277 | ** interpreter, allowing arbitrary Tcl expressions and |
| 2274 | 2278 | ** scripts to be evaluated from TH1. Additionally, the Tcl |
| 2275 | 2279 | ** interpreter will be able to evaluate arbitrary TH1 |
| @@ -2281,15 +2285,10 @@ | ||
| 2281 | 2285 | ** |
| 2282 | 2286 | ** th1-setup This is the setup script to be evaluated after creating |
| 2283 | 2287 | ** and initializing the TH1 interpreter. By default, this |
| 2284 | 2288 | ** is empty and no extra setup is performed. |
| 2285 | 2289 | ** |
| 2286 | -** unicode-glob The VALUE is a comma or newline-separated list of GLOB | |
| 2287 | -** (versionable) patterns specifying files that the "commit" command will | |
| 2288 | -** ignore when issuing warnings about text files that may | |
| 2289 | -** contain Unicode. Set to "*" to disable Unicode checking. | |
| 2290 | -** | |
| 2291 | 2290 | ** web-browser A shell command used to launch your preferred |
| 2292 | 2291 | ** web browser when given a URL as an argument. |
| 2293 | 2292 | ** Defaults to "start" on windows, "open" on Mac, |
| 2294 | 2293 | ** and "firefox" on Unix. |
| 2295 | 2294 | ** |
| 2296 | 2295 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -22,11 +22,11 @@ | |
| 22 | ** |
| 23 | ** (1) The "user" database in ~/.fossil |
| 24 | ** |
| 25 | ** (2) The "repository" database |
| 26 | ** |
| 27 | ** (3) A local checkout database named "_FOSSIL_" or ".fos" |
| 28 | ** and located at the root of the local copy of the source tree. |
| 29 | ** |
| 30 | */ |
| 31 | #include "config.h" |
| 32 | #if ! defined(_WIN32) |
| @@ -89,11 +89,11 @@ | |
| 89 | cgi_reply(); |
| 90 | } |
| 91 | else if( g.cgiOutput ){ |
| 92 | g.cgiOutput = 0; |
| 93 | cgi_printf("<h1>Database Error</h1>\n" |
| 94 | "<pre>%h</pre><p>%s</p>", z, zRebuildMsg); |
| 95 | cgi_reply(); |
| 96 | }else{ |
| 97 | fprintf(stderr, "%s: %s\n\n%s", g.argv[0], z, zRebuildMsg); |
| 98 | } |
| 99 | free(z); |
| @@ -484,20 +484,30 @@ | |
| 484 | /* |
| 485 | ** Execute multiple SQL statements. |
| 486 | */ |
| 487 | int db_multi_exec(const char *zSql, ...){ |
| 488 | Blob sql; |
| 489 | int rc; |
| 490 | va_list ap; |
| 491 | char *zErr = 0; |
| 492 | blob_init(&sql, 0, 0); |
| 493 | va_start(ap, zSql); |
| 494 | blob_vappendf(&sql, zSql, ap); |
| 495 | va_end(ap); |
| 496 | rc = sqlite3_exec(g.db, blob_buffer(&sql), 0, 0, &zErr); |
| 497 | if( rc!=SQLITE_OK ){ |
| 498 | db_err("%s\n%s", zErr, blob_buffer(&sql)); |
| 499 | } |
| 500 | blob_reset(&sql); |
| 501 | return rc; |
| 502 | } |
| 503 | |
| @@ -642,15 +652,11 @@ | |
| 642 | sqlite3 *db; |
| 643 | int rc; |
| 644 | const char *zSql; |
| 645 | va_list ap; |
| 646 | |
| 647 | rc = sqlite3_open(zFileName, &db); |
| 648 | if( rc!=SQLITE_OK ){ |
| 649 | db_err(sqlite3_errmsg(db)); |
| 650 | } |
| 651 | sqlite3_busy_timeout(db, 5000); |
| 652 | sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0); |
| 653 | rc = sqlite3_exec(db, zSchema, 0, 0, 0); |
| 654 | if( rc!=SQLITE_OK ){ |
| 655 | db_err(sqlite3_errmsg(db)); |
| 656 | } |
| @@ -697,29 +703,43 @@ | |
| 697 | |
| 698 | /* |
| 699 | ** Open a database file. Return a pointer to the new database |
| 700 | ** connection. An error results in process abort. |
| 701 | */ |
| 702 | static sqlite3 *openDatabase(const char *zDbName){ |
| 703 | int rc; |
| 704 | const char *zVfs; |
| 705 | sqlite3 *db; |
| 706 | |
| 707 | zVfs = fossil_getenv("FOSSIL_VFS"); |
| 708 | rc = sqlite3_open_v2( |
| 709 | zDbName, &db, |
| 710 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 711 | zVfs |
| 712 | ); |
| 713 | if( rc!=SQLITE_OK ){ |
| 714 | db_err(sqlite3_errmsg(db)); |
| 715 | } |
| 716 | sqlite3_busy_timeout(db, 5000); |
| 717 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 718 | sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0); |
| 719 | sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_ANY, 0, |
| 720 | db_checkin_mtime_function, 0, 0); |
| 721 | return db; |
| 722 | } |
| 723 | |
| 724 | |
| 725 | /* |
| @@ -747,13 +767,12 @@ | |
| 747 | const char *zLabel, |
| 748 | int *pWasAttached |
| 749 | ){ |
| 750 | if( !g.db ){ |
| 751 | assert( g.zMainDbType==0 ); |
| 752 | g.db = openDatabase(zDbName); |
| 753 | g.zMainDbType = zLabel; |
| 754 | db_connection_init(); |
| 755 | if ( pWasAttached ) *pWasAttached = 0; |
| 756 | }else{ |
| 757 | assert( g.zMainDbType!=0 ); |
| 758 | db_attach(zDbName, zLabel); |
| 759 | if ( pWasAttached ) *pWasAttached = 1; |
| @@ -820,11 +839,11 @@ | |
| 820 | db_open_or_attach(zDbName, "configdb", &g.useAttach); |
| 821 | g.dbConfig = 0; |
| 822 | g.zConfigDbType = 0; |
| 823 | }else{ |
| 824 | g.useAttach = 0; |
| 825 | g.dbConfig = openDatabase(zDbName); |
| 826 | g.zConfigDbType = "configdb"; |
| 827 | } |
| 828 | g.configOpen = 1; |
| 829 | free(zDbName); |
| 830 | } |
| @@ -1221,10 +1240,13 @@ | |
| 1221 | void db_create_default_users(int setupUserOnly, const char *zDefaultUser){ |
| 1222 | const char *zUser = zDefaultUser; |
| 1223 | if( zUser==0 ){ |
| 1224 | zUser = db_get("default-user", 0); |
| 1225 | } |
| 1226 | if( zUser==0 ){ |
| 1227 | #if defined(_WIN32) |
| 1228 | zUser = fossil_getenv("USERNAME"); |
| 1229 | #else |
| 1230 | zUser = fossil_getenv("USER"); |
| @@ -1441,11 +1463,11 @@ | |
| 1441 | ** SQL functions for debugging. |
| 1442 | ** |
| 1443 | ** The print() function writes its arguments on stdout, but only |
| 1444 | ** if the -sqlprint command-line option is turned on. |
| 1445 | */ |
| 1446 | static void db_sql_print( |
| 1447 | sqlite3_context *context, |
| 1448 | int argc, |
| 1449 | sqlite3_value **argv |
| 1450 | ){ |
| 1451 | int i; |
| @@ -1454,22 +1476,20 @@ | |
| 1454 | char c = i==argc-1 ? '\n' : ' '; |
| 1455 | fossil_print("%s%c", sqlite3_value_text(argv[i]), c); |
| 1456 | } |
| 1457 | } |
| 1458 | } |
| 1459 | static void db_sql_trace(void *notUsed, const char *zSql){ |
| 1460 | int n = strlen(zSql); |
| 1461 | char *zMsg = mprintf("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";"); |
| 1462 | fossil_puts(zMsg, 1); |
| 1463 | fossil_free(zMsg); |
| 1464 | } |
| 1465 | |
| 1466 | /* |
| 1467 | ** Implement the user() SQL function. user() takes no arguments and |
| 1468 | ** returns the user ID of the current user. |
| 1469 | */ |
| 1470 | static void db_sql_user( |
| 1471 | sqlite3_context *context, |
| 1472 | int argc, |
| 1473 | sqlite3_value **argv |
| 1474 | ){ |
| 1475 | if( g.zLogin!=0 ){ |
| @@ -1481,11 +1501,11 @@ | |
| 1481 | ** Implement the cgi() SQL function. cgi() takes an argument which is |
| 1482 | ** a name of CGI query parameter. The value of that parameter is returned, |
| 1483 | ** if available. Optional second argument will be returned if the first |
| 1484 | ** doesn't exist as a CGI parameter. |
| 1485 | */ |
| 1486 | static void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 1487 | const char* zP; |
| 1488 | if( argc!=1 && argc!=2 ) return; |
| 1489 | zP = P((const char*)sqlite3_value_text(argv[0])); |
| 1490 | if( zP ){ |
| 1491 | sqlite3_result_text(context, zP, -1, SQLITE_STATIC); |
| @@ -1512,11 +1532,11 @@ | |
| 1512 | ** (meaning that id was named on the command-line). |
| 1513 | ** |
| 1514 | ** In the second form (3 arguments) return argument X if true and Y |
| 1515 | ** if false. Except if Y is NULL then always return X. |
| 1516 | */ |
| 1517 | static void file_is_selected( |
| 1518 | sqlite3_context *context, |
| 1519 | int argc, |
| 1520 | sqlite3_value **argv |
| 1521 | ){ |
| 1522 | int rc = 0; |
| @@ -1600,32 +1620,10 @@ | |
| 1600 | zOut = mprintf("%s", zKey); |
| 1601 | } |
| 1602 | return zOut; |
| 1603 | } |
| 1604 | |
| 1605 | /* |
| 1606 | ** This function registers auxiliary functions when the SQLite |
| 1607 | ** database connection is first established. |
| 1608 | */ |
| 1609 | void db_connection_init(void){ |
| 1610 | sqlite3_exec(g.db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); |
| 1611 | sqlite3_create_function(g.db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0); |
| 1612 | sqlite3_create_function(g.db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0); |
| 1613 | sqlite3_create_function(g.db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0); |
| 1614 | sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); |
| 1615 | sqlite3_create_function( |
| 1616 | g.db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 1617 | ); |
| 1618 | sqlite3_create_function( |
| 1619 | g.db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 1620 | ); |
| 1621 | if( g.fSqlTrace ){ |
| 1622 | sqlite3_trace(g.db, db_sql_trace, 0); |
| 1623 | } |
| 1624 | re_add_sql_func(g.db); |
| 1625 | } |
| 1626 | |
| 1627 | /* |
| 1628 | ** Return true if the string zVal represents "true" (or "false"). |
| 1629 | */ |
| 1630 | int is_truth(const char *zVal){ |
| 1631 | static const char *const azOn[] = { "on", "yes", "true", "1" }; |
| @@ -2066,17 +2064,18 @@ | |
| 2066 | { "default-perms", 0, 16, 0, "u" }, |
| 2067 | { "diff-binary", 0, 0, 0, "on" }, |
| 2068 | { "diff-command", 0, 40, 0, "" }, |
| 2069 | { "dont-push", 0, 0, 0, "off" }, |
| 2070 | { "editor", 0, 32, 0, "" }, |
| 2071 | { "gdiff-command", 0, 40, 0, "gdiff" }, |
| 2072 | { "gmerge-command",0, 40, 0, "" }, |
| 2073 | { "https-login", 0, 0, 0, "off" }, |
| 2074 | { "ignore-glob", 0, 40, 1, "" }, |
| 2075 | { "empty-dirs", 0, 40, 1, "" }, |
| 2076 | { "href-targets", 0, 0, 0, "on" }, |
| 2077 | { "http-port", 0, 16, 0, "8080" }, |
| 2078 | { "localauth", 0, 0, 0, "off" }, |
| 2079 | { "main-branch", 0, 40, 0, "trunk" }, |
| 2080 | { "manifest", 0, 0, 1, "off" }, |
| 2081 | #ifdef FOSSIL_ENABLE_MARKDOWN |
| 2082 | { "markdown", 0, 0, 0, "off" }, |
| @@ -2086,19 +2085,18 @@ | |
| 2086 | { "pgp-command", 0, 40, 0, "gpg --clearsign -o " }, |
| 2087 | { "proxy", 0, 32, 0, "off" }, |
| 2088 | { "relative-paths",0, 0, 0, "on" }, |
| 2089 | { "repo-cksum", 0, 0, 0, "on" }, |
| 2090 | { "self-register", 0, 0, 0, "off" }, |
| 2091 | { "ssl-ca-location",0, 40, 0, "" }, |
| 2092 | { "ssl-identity", 0, 40, 0, "" }, |
| 2093 | { "ssh-command", 0, 40, 0, "" }, |
| 2094 | { "th1-setup", 0, 40, 0, "" }, |
| 2095 | #ifdef FOSSIL_ENABLE_TCL |
| 2096 | { "tcl", 0, 0, 0, "off" }, |
| 2097 | { "tcl-setup", 0, 40, 0, "" }, |
| 2098 | #endif |
| 2099 | { "unicode-glob", 0, 40, 1, "" }, |
| 2100 | { "web-browser", 0, 32, 0, "" }, |
| 2101 | { "white-foreground", 0, 0, 0, "off" }, |
| 2102 | { 0,0,0,0,0 } |
| 2103 | }; |
| 2104 | |
| @@ -2175,17 +2173,23 @@ | |
| 2175 | ** diff-command External command to run when performing a diff. |
| 2176 | ** If undefined, the internal text diff will be used. |
| 2177 | ** |
| 2178 | ** dont-push Prevent this repository from pushing from client to |
| 2179 | ** server. Useful when setting up a private branch. |
| 2180 | ** |
| 2181 | ** empty-dirs A comma or newline-separated list of pathnames. On |
| 2182 | ** (versionable) update and checkout commands, if no file or directory |
| 2183 | ** exists with that name, an empty directory will be |
| 2184 | ** created. |
| 2185 | ** |
| 2186 | ** editor Text editor command used for check-in comments. |
| 2187 | ** |
| 2188 | ** gdiff-command External command to run when performing a graphical |
| 2189 | ** diff. If undefined, text diff will be used. |
| 2190 | ** |
| 2191 | ** gmerge-command A graphical merge conflict resolver command operating |
| @@ -2244,10 +2248,13 @@ | |
| 2244 | ** |
| 2245 | ** self-register Allow users to register themselves through the HTTP UI. |
| 2246 | ** This is useful if you want to see other names than |
| 2247 | ** "Anonymous" in e.g. ticketing system. On the other hand |
| 2248 | ** users can not be deleted. Default: off. |
| 2249 | ** |
| 2250 | ** ssl-ca-location The full pathname to a file containing PEM encoded |
| 2251 | ** CA root certificates, or a directory of certificates |
| 2252 | ** with filenames formed from the certificate hashes as |
| 2253 | ** required by OpenSSL. |
| @@ -2263,13 +2270,10 @@ | |
| 2263 | ** the certificate and private key files. |
| 2264 | ** This identity will be presented to SSL servers to |
| 2265 | ** authenticate this client, in addition to the normal |
| 2266 | ** password authentication. |
| 2267 | ** |
| 2268 | ** ssh-command Command used to talk to a remote machine with |
| 2269 | ** the "ssh://" protocol. |
| 2270 | ** |
| 2271 | ** tcl If enabled (and Fossil was compiled with Tcl support), |
| 2272 | ** Tcl integration commands will be added to the TH1 |
| 2273 | ** interpreter, allowing arbitrary Tcl expressions and |
| 2274 | ** scripts to be evaluated from TH1. Additionally, the Tcl |
| 2275 | ** interpreter will be able to evaluate arbitrary TH1 |
| @@ -2281,15 +2285,10 @@ | |
| 2281 | ** |
| 2282 | ** th1-setup This is the setup script to be evaluated after creating |
| 2283 | ** and initializing the TH1 interpreter. By default, this |
| 2284 | ** is empty and no extra setup is performed. |
| 2285 | ** |
| 2286 | ** unicode-glob The VALUE is a comma or newline-separated list of GLOB |
| 2287 | ** (versionable) patterns specifying files that the "commit" command will |
| 2288 | ** ignore when issuing warnings about text files that may |
| 2289 | ** contain Unicode. Set to "*" to disable Unicode checking. |
| 2290 | ** |
| 2291 | ** web-browser A shell command used to launch your preferred |
| 2292 | ** web browser when given a URL as an argument. |
| 2293 | ** Defaults to "start" on windows, "open" on Mac, |
| 2294 | ** and "firefox" on Unix. |
| 2295 | ** |
| 2296 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -22,11 +22,11 @@ | |
| 22 | ** |
| 23 | ** (1) The "user" database in ~/.fossil |
| 24 | ** |
| 25 | ** (2) The "repository" database |
| 26 | ** |
| 27 | ** (3) A local checkout database named "_FOSSIL_" or ".fslckout" |
| 28 | ** and located at the root of the local copy of the source tree. |
| 29 | ** |
| 30 | */ |
| 31 | #include "config.h" |
| 32 | #if ! defined(_WIN32) |
| @@ -89,11 +89,11 @@ | |
| 89 | cgi_reply(); |
| 90 | } |
| 91 | else if( g.cgiOutput ){ |
| 92 | g.cgiOutput = 0; |
| 93 | cgi_printf("<h1>Database Error</h1>\n" |
| 94 | "<pre>%h</pre>\n<p>%s</p>\n", z, zRebuildMsg); |
| 95 | cgi_reply(); |
| 96 | }else{ |
| 97 | fprintf(stderr, "%s: %s\n\n%s", g.argv[0], z, zRebuildMsg); |
| 98 | } |
| 99 | free(z); |
| @@ -484,20 +484,30 @@ | |
| 484 | /* |
| 485 | ** Execute multiple SQL statements. |
| 486 | */ |
| 487 | int db_multi_exec(const char *zSql, ...){ |
| 488 | Blob sql; |
| 489 | int rc = SQLITE_OK; |
| 490 | va_list ap; |
| 491 | const char *z, *zEnd; |
| 492 | sqlite3_stmt *pStmt; |
| 493 | blob_init(&sql, 0, 0); |
| 494 | va_start(ap, zSql); |
| 495 | blob_vappendf(&sql, zSql, ap); |
| 496 | va_end(ap); |
| 497 | z = blob_str(&sql); |
| 498 | while( rc==SQLITE_OK && z[0] ){ |
| 499 | pStmt = 0; |
| 500 | rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd); |
| 501 | if( rc!=SQLITE_OK ) break; |
| 502 | if( pStmt ){ |
| 503 | db.nPrepare++; |
| 504 | while( sqlite3_step(pStmt)==SQLITE_ROW ){} |
| 505 | rc = sqlite3_finalize(pStmt); |
| 506 | if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z); |
| 507 | } |
| 508 | z = zEnd; |
| 509 | } |
| 510 | blob_reset(&sql); |
| 511 | return rc; |
| 512 | } |
| 513 | |
| @@ -642,15 +652,11 @@ | |
| 652 | sqlite3 *db; |
| 653 | int rc; |
| 654 | const char *zSql; |
| 655 | va_list ap; |
| 656 | |
| 657 | db = db_open(zFileName); |
| 658 | sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0); |
| 659 | rc = sqlite3_exec(db, zSchema, 0, 0, 0); |
| 660 | if( rc!=SQLITE_OK ){ |
| 661 | db_err(sqlite3_errmsg(db)); |
| 662 | } |
| @@ -697,29 +703,43 @@ | |
| 703 | |
| 704 | /* |
| 705 | ** Open a database file. Return a pointer to the new database |
| 706 | ** connection. An error results in process abort. |
| 707 | */ |
| 708 | LOCAL sqlite3 *db_open(const char *zDbName){ |
| 709 | int rc; |
| 710 | const char *zVfs; |
| 711 | sqlite3 *db; |
| 712 | |
| 713 | if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName); |
| 714 | zVfs = fossil_getenv("FOSSIL_VFS"); |
| 715 | rc = sqlite3_open_v2( |
| 716 | zDbName, &db, |
| 717 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 718 | zVfs |
| 719 | ); |
| 720 | if( rc!=SQLITE_OK ){ |
| 721 | db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); |
| 722 | } |
| 723 | sqlite3_busy_timeout(db, 5000); |
| 724 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 725 | sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0); |
| 726 | sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_ANY, 0, |
| 727 | db_checkin_mtime_function, 0, 0); |
| 728 | sqlite3_create_function(db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0); |
| 729 | sqlite3_create_function(db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0); |
| 730 | sqlite3_create_function(db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0); |
| 731 | sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); |
| 732 | sqlite3_create_function( |
| 733 | db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 734 | ); |
| 735 | sqlite3_create_function( |
| 736 | db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 737 | ); |
| 738 | if( g.fSqlTrace ) sqlite3_trace(db, db_sql_trace, 0); |
| 739 | re_add_sql_func(db); |
| 740 | sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); |
| 741 | return db; |
| 742 | } |
| 743 | |
| 744 | |
| 745 | /* |
| @@ -747,13 +767,12 @@ | |
| 767 | const char *zLabel, |
| 768 | int *pWasAttached |
| 769 | ){ |
| 770 | if( !g.db ){ |
| 771 | assert( g.zMainDbType==0 ); |
| 772 | g.db = db_open(zDbName); |
| 773 | g.zMainDbType = zLabel; |
| 774 | if ( pWasAttached ) *pWasAttached = 0; |
| 775 | }else{ |
| 776 | assert( g.zMainDbType!=0 ); |
| 777 | db_attach(zDbName, zLabel); |
| 778 | if ( pWasAttached ) *pWasAttached = 1; |
| @@ -820,11 +839,11 @@ | |
| 839 | db_open_or_attach(zDbName, "configdb", &g.useAttach); |
| 840 | g.dbConfig = 0; |
| 841 | g.zConfigDbType = 0; |
| 842 | }else{ |
| 843 | g.useAttach = 0; |
| 844 | g.dbConfig = db_open(zDbName); |
| 845 | g.zConfigDbType = "configdb"; |
| 846 | } |
| 847 | g.configOpen = 1; |
| 848 | free(zDbName); |
| 849 | } |
| @@ -1221,10 +1240,13 @@ | |
| 1240 | void db_create_default_users(int setupUserOnly, const char *zDefaultUser){ |
| 1241 | const char *zUser = zDefaultUser; |
| 1242 | if( zUser==0 ){ |
| 1243 | zUser = db_get("default-user", 0); |
| 1244 | } |
| 1245 | if( zUser==0 ){ |
| 1246 | zUser = fossil_getenv("FOSSIL_USER"); |
| 1247 | } |
| 1248 | if( zUser==0 ){ |
| 1249 | #if defined(_WIN32) |
| 1250 | zUser = fossil_getenv("USERNAME"); |
| 1251 | #else |
| 1252 | zUser = fossil_getenv("USER"); |
| @@ -1441,11 +1463,11 @@ | |
| 1463 | ** SQL functions for debugging. |
| 1464 | ** |
| 1465 | ** The print() function writes its arguments on stdout, but only |
| 1466 | ** if the -sqlprint command-line option is turned on. |
| 1467 | */ |
| 1468 | LOCAL void db_sql_print( |
| 1469 | sqlite3_context *context, |
| 1470 | int argc, |
| 1471 | sqlite3_value **argv |
| 1472 | ){ |
| 1473 | int i; |
| @@ -1454,22 +1476,20 @@ | |
| 1476 | char c = i==argc-1 ? '\n' : ' '; |
| 1477 | fossil_print("%s%c", sqlite3_value_text(argv[i]), c); |
| 1478 | } |
| 1479 | } |
| 1480 | } |
| 1481 | LOCAL void db_sql_trace(void *notUsed, const char *zSql){ |
| 1482 | int n = strlen(zSql); |
| 1483 | fossil_trace("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";"); |
| 1484 | } |
| 1485 | |
| 1486 | /* |
| 1487 | ** Implement the user() SQL function. user() takes no arguments and |
| 1488 | ** returns the user ID of the current user. |
| 1489 | */ |
| 1490 | LOCAL void db_sql_user( |
| 1491 | sqlite3_context *context, |
| 1492 | int argc, |
| 1493 | sqlite3_value **argv |
| 1494 | ){ |
| 1495 | if( g.zLogin!=0 ){ |
| @@ -1481,11 +1501,11 @@ | |
| 1501 | ** Implement the cgi() SQL function. cgi() takes an argument which is |
| 1502 | ** a name of CGI query parameter. The value of that parameter is returned, |
| 1503 | ** if available. Optional second argument will be returned if the first |
| 1504 | ** doesn't exist as a CGI parameter. |
| 1505 | */ |
| 1506 | LOCAL void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 1507 | const char* zP; |
| 1508 | if( argc!=1 && argc!=2 ) return; |
| 1509 | zP = P((const char*)sqlite3_value_text(argv[0])); |
| 1510 | if( zP ){ |
| 1511 | sqlite3_result_text(context, zP, -1, SQLITE_STATIC); |
| @@ -1512,11 +1532,11 @@ | |
| 1532 | ** (meaning that id was named on the command-line). |
| 1533 | ** |
| 1534 | ** In the second form (3 arguments) return argument X if true and Y |
| 1535 | ** if false. Except if Y is NULL then always return X. |
| 1536 | */ |
| 1537 | LOCAL void file_is_selected( |
| 1538 | sqlite3_context *context, |
| 1539 | int argc, |
| 1540 | sqlite3_value **argv |
| 1541 | ){ |
| 1542 | int rc = 0; |
| @@ -1600,32 +1620,10 @@ | |
| 1620 | zOut = mprintf("%s", zKey); |
| 1621 | } |
| 1622 | return zOut; |
| 1623 | } |
| 1624 | |
| 1625 | /* |
| 1626 | ** Return true if the string zVal represents "true" (or "false"). |
| 1627 | */ |
| 1628 | int is_truth(const char *zVal){ |
| 1629 | static const char *const azOn[] = { "on", "yes", "true", "1" }; |
| @@ -2066,17 +2064,18 @@ | |
| 2064 | { "default-perms", 0, 16, 0, "u" }, |
| 2065 | { "diff-binary", 0, 0, 0, "on" }, |
| 2066 | { "diff-command", 0, 40, 0, "" }, |
| 2067 | { "dont-push", 0, 0, 0, "off" }, |
| 2068 | { "editor", 0, 32, 0, "" }, |
| 2069 | { "empty-dirs", 0, 40, 1, "" }, |
| 2070 | { "encoding-glob", 0, 40, 1, "" }, |
| 2071 | { "gdiff-command", 0, 40, 0, "gdiff" }, |
| 2072 | { "gmerge-command",0, 40, 0, "" }, |
| 2073 | { "http-port", 0, 16, 0, "8080" }, |
| 2074 | { "https-login", 0, 0, 0, "off" }, |
| 2075 | { "href-targets", 0, 0, 0, "on" }, |
| 2076 | { "ignore-glob", 0, 40, 1, "" }, |
| 2077 | { "localauth", 0, 0, 0, "off" }, |
| 2078 | { "main-branch", 0, 40, 0, "trunk" }, |
| 2079 | { "manifest", 0, 0, 1, "off" }, |
| 2080 | #ifdef FOSSIL_ENABLE_MARKDOWN |
| 2081 | { "markdown", 0, 0, 0, "off" }, |
| @@ -2086,19 +2085,18 @@ | |
| 2085 | { "pgp-command", 0, 40, 0, "gpg --clearsign -o " }, |
| 2086 | { "proxy", 0, 32, 0, "off" }, |
| 2087 | { "relative-paths",0, 0, 0, "on" }, |
| 2088 | { "repo-cksum", 0, 0, 0, "on" }, |
| 2089 | { "self-register", 0, 0, 0, "off" }, |
| 2090 | { "ssh-command", 0, 40, 0, "" }, |
| 2091 | { "ssl-ca-location",0, 40, 0, "" }, |
| 2092 | { "ssl-identity", 0, 40, 0, "" }, |
| 2093 | #ifdef FOSSIL_ENABLE_TCL |
| 2094 | { "tcl", 0, 0, 0, "off" }, |
| 2095 | { "tcl-setup", 0, 40, 0, "" }, |
| 2096 | #endif |
| 2097 | { "th1-setup", 0, 40, 0, "" }, |
| 2098 | { "web-browser", 0, 32, 0, "" }, |
| 2099 | { "white-foreground", 0, 0, 0, "off" }, |
| 2100 | { 0,0,0,0,0 } |
| 2101 | }; |
| 2102 | |
| @@ -2175,17 +2173,23 @@ | |
| 2173 | ** diff-command External command to run when performing a diff. |
| 2174 | ** If undefined, the internal text diff will be used. |
| 2175 | ** |
| 2176 | ** dont-push Prevent this repository from pushing from client to |
| 2177 | ** server. Useful when setting up a private branch. |
| 2178 | ** |
| 2179 | ** editor Text editor command used for check-in comments. |
| 2180 | ** |
| 2181 | ** empty-dirs A comma or newline-separated list of pathnames. On |
| 2182 | ** (versionable) update and checkout commands, if no file or directory |
| 2183 | ** exists with that name, an empty directory will be |
| 2184 | ** created. |
| 2185 | ** |
| 2186 | ** encoding-glob The VALUE is a comma or newline-separated list of GLOB |
| 2187 | ** (versionable) patterns specifying files that the "commit" command will |
| 2188 | ** ignore when issuing warnings about text files that may |
| 2189 | ** use another encoding than ASCII or UTF-8. Set to "*" |
| 2190 | ** to disable encoding checking. |
| 2191 | ** |
| 2192 | ** gdiff-command External command to run when performing a graphical |
| 2193 | ** diff. If undefined, text diff will be used. |
| 2194 | ** |
| 2195 | ** gmerge-command A graphical merge conflict resolver command operating |
| @@ -2244,10 +2248,13 @@ | |
| 2248 | ** |
| 2249 | ** self-register Allow users to register themselves through the HTTP UI. |
| 2250 | ** This is useful if you want to see other names than |
| 2251 | ** "Anonymous" in e.g. ticketing system. On the other hand |
| 2252 | ** users can not be deleted. Default: off. |
| 2253 | ** |
| 2254 | ** ssh-command Command used to talk to a remote machine with |
| 2255 | ** the "ssh://" protocol. |
| 2256 | ** |
| 2257 | ** ssl-ca-location The full pathname to a file containing PEM encoded |
| 2258 | ** CA root certificates, or a directory of certificates |
| 2259 | ** with filenames formed from the certificate hashes as |
| 2260 | ** required by OpenSSL. |
| @@ -2263,13 +2270,10 @@ | |
| 2270 | ** the certificate and private key files. |
| 2271 | ** This identity will be presented to SSL servers to |
| 2272 | ** authenticate this client, in addition to the normal |
| 2273 | ** password authentication. |
| 2274 | ** |
| 2275 | ** tcl If enabled (and Fossil was compiled with Tcl support), |
| 2276 | ** Tcl integration commands will be added to the TH1 |
| 2277 | ** interpreter, allowing arbitrary Tcl expressions and |
| 2278 | ** scripts to be evaluated from TH1. Additionally, the Tcl |
| 2279 | ** interpreter will be able to evaluate arbitrary TH1 |
| @@ -2281,15 +2285,10 @@ | |
| 2285 | ** |
| 2286 | ** th1-setup This is the setup script to be evaluated after creating |
| 2287 | ** and initializing the TH1 interpreter. By default, this |
| 2288 | ** is empty and no extra setup is performed. |
| 2289 | ** |
| 2290 | ** web-browser A shell command used to launch your preferred |
| 2291 | ** web browser when given a URL as an argument. |
| 2292 | ** Defaults to "start" on windows, "open" on Mac, |
| 2293 | ** and "firefox" on Unix. |
| 2294 | ** |
| 2295 |
M
src/db.c
+61
-62
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -22,11 +22,11 @@ | ||
| 22 | 22 | ** |
| 23 | 23 | ** (1) The "user" database in ~/.fossil |
| 24 | 24 | ** |
| 25 | 25 | ** (2) The "repository" database |
| 26 | 26 | ** |
| 27 | -** (3) A local checkout database named "_FOSSIL_" or ".fos" | |
| 27 | +** (3) A local checkout database named "_FOSSIL_" or ".fslckout" | |
| 28 | 28 | ** and located at the root of the local copy of the source tree. |
| 29 | 29 | ** |
| 30 | 30 | */ |
| 31 | 31 | #include "config.h" |
| 32 | 32 | #if ! defined(_WIN32) |
| @@ -89,11 +89,11 @@ | ||
| 89 | 89 | cgi_reply(); |
| 90 | 90 | } |
| 91 | 91 | else if( g.cgiOutput ){ |
| 92 | 92 | g.cgiOutput = 0; |
| 93 | 93 | cgi_printf("<h1>Database Error</h1>\n" |
| 94 | - "<pre>%h</pre><p>%s</p>", z, zRebuildMsg); | |
| 94 | + "<pre>%h</pre>\n<p>%s</p>\n", z, zRebuildMsg); | |
| 95 | 95 | cgi_reply(); |
| 96 | 96 | }else{ |
| 97 | 97 | fprintf(stderr, "%s: %s\n\n%s", g.argv[0], z, zRebuildMsg); |
| 98 | 98 | } |
| 99 | 99 | free(z); |
| @@ -484,20 +484,30 @@ | ||
| 484 | 484 | /* |
| 485 | 485 | ** Execute multiple SQL statements. |
| 486 | 486 | */ |
| 487 | 487 | int db_multi_exec(const char *zSql, ...){ |
| 488 | 488 | Blob sql; |
| 489 | - int rc; | |
| 489 | + int rc = SQLITE_OK; | |
| 490 | 490 | va_list ap; |
| 491 | - char *zErr = 0; | |
| 491 | + const char *z, *zEnd; | |
| 492 | + sqlite3_stmt *pStmt; | |
| 492 | 493 | blob_init(&sql, 0, 0); |
| 493 | 494 | va_start(ap, zSql); |
| 494 | 495 | blob_vappendf(&sql, zSql, ap); |
| 495 | 496 | va_end(ap); |
| 496 | - rc = sqlite3_exec(g.db, blob_buffer(&sql), 0, 0, &zErr); | |
| 497 | - if( rc!=SQLITE_OK ){ | |
| 498 | - db_err("%s\n%s", zErr, blob_buffer(&sql)); | |
| 497 | + z = blob_str(&sql); | |
| 498 | + while( rc==SQLITE_OK && z[0] ){ | |
| 499 | + pStmt = 0; | |
| 500 | + rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd); | |
| 501 | + if( rc!=SQLITE_OK ) break; | |
| 502 | + if( pStmt ){ | |
| 503 | + db.nPrepare++; | |
| 504 | + while( sqlite3_step(pStmt)==SQLITE_ROW ){} | |
| 505 | + rc = sqlite3_finalize(pStmt); | |
| 506 | + if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z); | |
| 507 | + } | |
| 508 | + z = zEnd; | |
| 499 | 509 | } |
| 500 | 510 | blob_reset(&sql); |
| 501 | 511 | return rc; |
| 502 | 512 | } |
| 503 | 513 | |
| @@ -642,15 +652,11 @@ | ||
| 642 | 652 | sqlite3 *db; |
| 643 | 653 | int rc; |
| 644 | 654 | const char *zSql; |
| 645 | 655 | va_list ap; |
| 646 | 656 | |
| 647 | - rc = sqlite3_open(zFileName, &db); | |
| 648 | - if( rc!=SQLITE_OK ){ | |
| 649 | - db_err(sqlite3_errmsg(db)); | |
| 650 | - } | |
| 651 | - sqlite3_busy_timeout(db, 5000); | |
| 657 | + db = db_open(zFileName); | |
| 652 | 658 | sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0); |
| 653 | 659 | rc = sqlite3_exec(db, zSchema, 0, 0, 0); |
| 654 | 660 | if( rc!=SQLITE_OK ){ |
| 655 | 661 | db_err(sqlite3_errmsg(db)); |
| 656 | 662 | } |
| @@ -697,29 +703,43 @@ | ||
| 697 | 703 | |
| 698 | 704 | /* |
| 699 | 705 | ** Open a database file. Return a pointer to the new database |
| 700 | 706 | ** connection. An error results in process abort. |
| 701 | 707 | */ |
| 702 | -static sqlite3 *openDatabase(const char *zDbName){ | |
| 708 | +LOCAL sqlite3 *db_open(const char *zDbName){ | |
| 703 | 709 | int rc; |
| 704 | 710 | const char *zVfs; |
| 705 | 711 | sqlite3 *db; |
| 706 | 712 | |
| 713 | + if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName); | |
| 707 | 714 | zVfs = fossil_getenv("FOSSIL_VFS"); |
| 708 | 715 | rc = sqlite3_open_v2( |
| 709 | 716 | zDbName, &db, |
| 710 | 717 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 711 | 718 | zVfs |
| 712 | 719 | ); |
| 713 | 720 | if( rc!=SQLITE_OK ){ |
| 714 | - db_err(sqlite3_errmsg(db)); | |
| 721 | + db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); | |
| 715 | 722 | } |
| 716 | 723 | sqlite3_busy_timeout(db, 5000); |
| 717 | 724 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 718 | 725 | sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0); |
| 719 | 726 | sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_ANY, 0, |
| 720 | 727 | db_checkin_mtime_function, 0, 0); |
| 728 | + sqlite3_create_function(db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0); | |
| 729 | + sqlite3_create_function(db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0); | |
| 730 | + sqlite3_create_function(db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0); | |
| 731 | + sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); | |
| 732 | + sqlite3_create_function( | |
| 733 | + db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 | |
| 734 | + ); | |
| 735 | + sqlite3_create_function( | |
| 736 | + db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 | |
| 737 | + ); | |
| 738 | + if( g.fSqlTrace ) sqlite3_trace(db, db_sql_trace, 0); | |
| 739 | + re_add_sql_func(db); | |
| 740 | + sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); | |
| 721 | 741 | return db; |
| 722 | 742 | } |
| 723 | 743 | |
| 724 | 744 | |
| 725 | 745 | /* |
| @@ -747,13 +767,12 @@ | ||
| 747 | 767 | const char *zLabel, |
| 748 | 768 | int *pWasAttached |
| 749 | 769 | ){ |
| 750 | 770 | if( !g.db ){ |
| 751 | 771 | assert( g.zMainDbType==0 ); |
| 752 | - g.db = openDatabase(zDbName); | |
| 772 | + g.db = db_open(zDbName); | |
| 753 | 773 | g.zMainDbType = zLabel; |
| 754 | - db_connection_init(); | |
| 755 | 774 | if ( pWasAttached ) *pWasAttached = 0; |
| 756 | 775 | }else{ |
| 757 | 776 | assert( g.zMainDbType!=0 ); |
| 758 | 777 | db_attach(zDbName, zLabel); |
| 759 | 778 | if ( pWasAttached ) *pWasAttached = 1; |
| @@ -820,11 +839,11 @@ | ||
| 820 | 839 | db_open_or_attach(zDbName, "configdb", &g.useAttach); |
| 821 | 840 | g.dbConfig = 0; |
| 822 | 841 | g.zConfigDbType = 0; |
| 823 | 842 | }else{ |
| 824 | 843 | g.useAttach = 0; |
| 825 | - g.dbConfig = openDatabase(zDbName); | |
| 844 | + g.dbConfig = db_open(zDbName); | |
| 826 | 845 | g.zConfigDbType = "configdb"; |
| 827 | 846 | } |
| 828 | 847 | g.configOpen = 1; |
| 829 | 848 | free(zDbName); |
| 830 | 849 | } |
| @@ -1221,10 +1240,13 @@ | ||
| 1221 | 1240 | void db_create_default_users(int setupUserOnly, const char *zDefaultUser){ |
| 1222 | 1241 | const char *zUser = zDefaultUser; |
| 1223 | 1242 | if( zUser==0 ){ |
| 1224 | 1243 | zUser = db_get("default-user", 0); |
| 1225 | 1244 | } |
| 1245 | + if( zUser==0 ){ | |
| 1246 | + zUser = fossil_getenv("FOSSIL_USER"); | |
| 1247 | + } | |
| 1226 | 1248 | if( zUser==0 ){ |
| 1227 | 1249 | #if defined(_WIN32) |
| 1228 | 1250 | zUser = fossil_getenv("USERNAME"); |
| 1229 | 1251 | #else |
| 1230 | 1252 | zUser = fossil_getenv("USER"); |
| @@ -1441,11 +1463,11 @@ | ||
| 1441 | 1463 | ** SQL functions for debugging. |
| 1442 | 1464 | ** |
| 1443 | 1465 | ** The print() function writes its arguments on stdout, but only |
| 1444 | 1466 | ** if the -sqlprint command-line option is turned on. |
| 1445 | 1467 | */ |
| 1446 | -static void db_sql_print( | |
| 1468 | +LOCAL void db_sql_print( | |
| 1447 | 1469 | sqlite3_context *context, |
| 1448 | 1470 | int argc, |
| 1449 | 1471 | sqlite3_value **argv |
| 1450 | 1472 | ){ |
| 1451 | 1473 | int i; |
| @@ -1454,22 +1476,20 @@ | ||
| 1454 | 1476 | char c = i==argc-1 ? '\n' : ' '; |
| 1455 | 1477 | fossil_print("%s%c", sqlite3_value_text(argv[i]), c); |
| 1456 | 1478 | } |
| 1457 | 1479 | } |
| 1458 | 1480 | } |
| 1459 | -static void db_sql_trace(void *notUsed, const char *zSql){ | |
| 1481 | +LOCAL void db_sql_trace(void *notUsed, const char *zSql){ | |
| 1460 | 1482 | int n = strlen(zSql); |
| 1461 | - char *zMsg = mprintf("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";"); | |
| 1462 | - fossil_puts(zMsg, 1); | |
| 1463 | - fossil_free(zMsg); | |
| 1483 | + fossil_trace("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";"); | |
| 1464 | 1484 | } |
| 1465 | 1485 | |
| 1466 | 1486 | /* |
| 1467 | 1487 | ** Implement the user() SQL function. user() takes no arguments and |
| 1468 | 1488 | ** returns the user ID of the current user. |
| 1469 | 1489 | */ |
| 1470 | -static void db_sql_user( | |
| 1490 | +LOCAL void db_sql_user( | |
| 1471 | 1491 | sqlite3_context *context, |
| 1472 | 1492 | int argc, |
| 1473 | 1493 | sqlite3_value **argv |
| 1474 | 1494 | ){ |
| 1475 | 1495 | if( g.zLogin!=0 ){ |
| @@ -1481,11 +1501,11 @@ | ||
| 1481 | 1501 | ** Implement the cgi() SQL function. cgi() takes an argument which is |
| 1482 | 1502 | ** a name of CGI query parameter. The value of that parameter is returned, |
| 1483 | 1503 | ** if available. Optional second argument will be returned if the first |
| 1484 | 1504 | ** doesn't exist as a CGI parameter. |
| 1485 | 1505 | */ |
| 1486 | -static void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){ | |
| 1506 | +LOCAL void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){ | |
| 1487 | 1507 | const char* zP; |
| 1488 | 1508 | if( argc!=1 && argc!=2 ) return; |
| 1489 | 1509 | zP = P((const char*)sqlite3_value_text(argv[0])); |
| 1490 | 1510 | if( zP ){ |
| 1491 | 1511 | sqlite3_result_text(context, zP, -1, SQLITE_STATIC); |
| @@ -1512,11 +1532,11 @@ | ||
| 1512 | 1532 | ** (meaning that id was named on the command-line). |
| 1513 | 1533 | ** |
| 1514 | 1534 | ** In the second form (3 arguments) return argument X if true and Y |
| 1515 | 1535 | ** if false. Except if Y is NULL then always return X. |
| 1516 | 1536 | */ |
| 1517 | -static void file_is_selected( | |
| 1537 | +LOCAL void file_is_selected( | |
| 1518 | 1538 | sqlite3_context *context, |
| 1519 | 1539 | int argc, |
| 1520 | 1540 | sqlite3_value **argv |
| 1521 | 1541 | ){ |
| 1522 | 1542 | int rc = 0; |
| @@ -1600,32 +1620,10 @@ | ||
| 1600 | 1620 | zOut = mprintf("%s", zKey); |
| 1601 | 1621 | } |
| 1602 | 1622 | return zOut; |
| 1603 | 1623 | } |
| 1604 | 1624 | |
| 1605 | -/* | |
| 1606 | -** This function registers auxiliary functions when the SQLite | |
| 1607 | -** database connection is first established. | |
| 1608 | -*/ | |
| 1609 | -void db_connection_init(void){ | |
| 1610 | - sqlite3_exec(g.db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); | |
| 1611 | - sqlite3_create_function(g.db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0); | |
| 1612 | - sqlite3_create_function(g.db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0); | |
| 1613 | - sqlite3_create_function(g.db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0); | |
| 1614 | - sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); | |
| 1615 | - sqlite3_create_function( | |
| 1616 | - g.db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 | |
| 1617 | - ); | |
| 1618 | - sqlite3_create_function( | |
| 1619 | - g.db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 | |
| 1620 | - ); | |
| 1621 | - if( g.fSqlTrace ){ | |
| 1622 | - sqlite3_trace(g.db, db_sql_trace, 0); | |
| 1623 | - } | |
| 1624 | - re_add_sql_func(g.db); | |
| 1625 | -} | |
| 1626 | - | |
| 1627 | 1625 | /* |
| 1628 | 1626 | ** Return true if the string zVal represents "true" (or "false"). |
| 1629 | 1627 | */ |
| 1630 | 1628 | int is_truth(const char *zVal){ |
| 1631 | 1629 | static const char *const azOn[] = { "on", "yes", "true", "1" }; |
| @@ -2066,17 +2064,18 @@ | ||
| 2066 | 2064 | { "default-perms", 0, 16, 0, "u" }, |
| 2067 | 2065 | { "diff-binary", 0, 0, 0, "on" }, |
| 2068 | 2066 | { "diff-command", 0, 40, 0, "" }, |
| 2069 | 2067 | { "dont-push", 0, 0, 0, "off" }, |
| 2070 | 2068 | { "editor", 0, 32, 0, "" }, |
| 2069 | + { "empty-dirs", 0, 40, 1, "" }, | |
| 2070 | + { "encoding-glob", 0, 40, 1, "" }, | |
| 2071 | 2071 | { "gdiff-command", 0, 40, 0, "gdiff" }, |
| 2072 | 2072 | { "gmerge-command",0, 40, 0, "" }, |
| 2073 | + { "http-port", 0, 16, 0, "8080" }, | |
| 2073 | 2074 | { "https-login", 0, 0, 0, "off" }, |
| 2074 | - { "ignore-glob", 0, 40, 1, "" }, | |
| 2075 | - { "empty-dirs", 0, 40, 1, "" }, | |
| 2076 | 2075 | { "href-targets", 0, 0, 0, "on" }, |
| 2077 | - { "http-port", 0, 16, 0, "8080" }, | |
| 2076 | + { "ignore-glob", 0, 40, 1, "" }, | |
| 2078 | 2077 | { "localauth", 0, 0, 0, "off" }, |
| 2079 | 2078 | { "main-branch", 0, 40, 0, "trunk" }, |
| 2080 | 2079 | { "manifest", 0, 0, 1, "off" }, |
| 2081 | 2080 | #ifdef FOSSIL_ENABLE_MARKDOWN |
| 2082 | 2081 | { "markdown", 0, 0, 0, "off" }, |
| @@ -2086,19 +2085,18 @@ | ||
| 2086 | 2085 | { "pgp-command", 0, 40, 0, "gpg --clearsign -o " }, |
| 2087 | 2086 | { "proxy", 0, 32, 0, "off" }, |
| 2088 | 2087 | { "relative-paths",0, 0, 0, "on" }, |
| 2089 | 2088 | { "repo-cksum", 0, 0, 0, "on" }, |
| 2090 | 2089 | { "self-register", 0, 0, 0, "off" }, |
| 2090 | + { "ssh-command", 0, 40, 0, "" }, | |
| 2091 | 2091 | { "ssl-ca-location",0, 40, 0, "" }, |
| 2092 | 2092 | { "ssl-identity", 0, 40, 0, "" }, |
| 2093 | - { "ssh-command", 0, 40, 0, "" }, | |
| 2094 | - { "th1-setup", 0, 40, 0, "" }, | |
| 2095 | 2093 | #ifdef FOSSIL_ENABLE_TCL |
| 2096 | 2094 | { "tcl", 0, 0, 0, "off" }, |
| 2097 | 2095 | { "tcl-setup", 0, 40, 0, "" }, |
| 2098 | 2096 | #endif |
| 2099 | - { "unicode-glob", 0, 40, 1, "" }, | |
| 2097 | + { "th1-setup", 0, 40, 0, "" }, | |
| 2100 | 2098 | { "web-browser", 0, 32, 0, "" }, |
| 2101 | 2099 | { "white-foreground", 0, 0, 0, "off" }, |
| 2102 | 2100 | { 0,0,0,0,0 } |
| 2103 | 2101 | }; |
| 2104 | 2102 | |
| @@ -2175,17 +2173,23 @@ | ||
| 2175 | 2173 | ** diff-command External command to run when performing a diff. |
| 2176 | 2174 | ** If undefined, the internal text diff will be used. |
| 2177 | 2175 | ** |
| 2178 | 2176 | ** dont-push Prevent this repository from pushing from client to |
| 2179 | 2177 | ** server. Useful when setting up a private branch. |
| 2178 | +** | |
| 2179 | +** editor Text editor command used for check-in comments. | |
| 2180 | 2180 | ** |
| 2181 | 2181 | ** empty-dirs A comma or newline-separated list of pathnames. On |
| 2182 | 2182 | ** (versionable) update and checkout commands, if no file or directory |
| 2183 | 2183 | ** exists with that name, an empty directory will be |
| 2184 | 2184 | ** created. |
| 2185 | 2185 | ** |
| 2186 | -** editor Text editor command used for check-in comments. | |
| 2186 | +** encoding-glob The VALUE is a comma or newline-separated list of GLOB | |
| 2187 | +** (versionable) patterns specifying files that the "commit" command will | |
| 2188 | +** ignore when issuing warnings about text files that may | |
| 2189 | +** use another encoding than ASCII or UTF-8. Set to "*" | |
| 2190 | +** to disable encoding checking. | |
| 2187 | 2191 | ** |
| 2188 | 2192 | ** gdiff-command External command to run when performing a graphical |
| 2189 | 2193 | ** diff. If undefined, text diff will be used. |
| 2190 | 2194 | ** |
| 2191 | 2195 | ** gmerge-command A graphical merge conflict resolver command operating |
| @@ -2244,10 +2248,13 @@ | ||
| 2244 | 2248 | ** |
| 2245 | 2249 | ** self-register Allow users to register themselves through the HTTP UI. |
| 2246 | 2250 | ** This is useful if you want to see other names than |
| 2247 | 2251 | ** "Anonymous" in e.g. ticketing system. On the other hand |
| 2248 | 2252 | ** users can not be deleted. Default: off. |
| 2253 | +** | |
| 2254 | +** ssh-command Command used to talk to a remote machine with | |
| 2255 | +** the "ssh://" protocol. | |
| 2249 | 2256 | ** |
| 2250 | 2257 | ** ssl-ca-location The full pathname to a file containing PEM encoded |
| 2251 | 2258 | ** CA root certificates, or a directory of certificates |
| 2252 | 2259 | ** with filenames formed from the certificate hashes as |
| 2253 | 2260 | ** required by OpenSSL. |
| @@ -2263,13 +2270,10 @@ | ||
| 2263 | 2270 | ** the certificate and private key files. |
| 2264 | 2271 | ** This identity will be presented to SSL servers to |
| 2265 | 2272 | ** authenticate this client, in addition to the normal |
| 2266 | 2273 | ** password authentication. |
| 2267 | 2274 | ** |
| 2268 | -** ssh-command Command used to talk to a remote machine with | |
| 2269 | -** the "ssh://" protocol. | |
| 2270 | -** | |
| 2271 | 2275 | ** tcl If enabled (and Fossil was compiled with Tcl support), |
| 2272 | 2276 | ** Tcl integration commands will be added to the TH1 |
| 2273 | 2277 | ** interpreter, allowing arbitrary Tcl expressions and |
| 2274 | 2278 | ** scripts to be evaluated from TH1. Additionally, the Tcl |
| 2275 | 2279 | ** interpreter will be able to evaluate arbitrary TH1 |
| @@ -2281,15 +2285,10 @@ | ||
| 2281 | 2285 | ** |
| 2282 | 2286 | ** th1-setup This is the setup script to be evaluated after creating |
| 2283 | 2287 | ** and initializing the TH1 interpreter. By default, this |
| 2284 | 2288 | ** is empty and no extra setup is performed. |
| 2285 | 2289 | ** |
| 2286 | -** unicode-glob The VALUE is a comma or newline-separated list of GLOB | |
| 2287 | -** (versionable) patterns specifying files that the "commit" command will | |
| 2288 | -** ignore when issuing warnings about text files that may | |
| 2289 | -** contain Unicode. Set to "*" to disable Unicode checking. | |
| 2290 | -** | |
| 2291 | 2290 | ** web-browser A shell command used to launch your preferred |
| 2292 | 2291 | ** web browser when given a URL as an argument. |
| 2293 | 2292 | ** Defaults to "start" on windows, "open" on Mac, |
| 2294 | 2293 | ** and "firefox" on Unix. |
| 2295 | 2294 | ** |
| 2296 | 2295 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -22,11 +22,11 @@ | |
| 22 | ** |
| 23 | ** (1) The "user" database in ~/.fossil |
| 24 | ** |
| 25 | ** (2) The "repository" database |
| 26 | ** |
| 27 | ** (3) A local checkout database named "_FOSSIL_" or ".fos" |
| 28 | ** and located at the root of the local copy of the source tree. |
| 29 | ** |
| 30 | */ |
| 31 | #include "config.h" |
| 32 | #if ! defined(_WIN32) |
| @@ -89,11 +89,11 @@ | |
| 89 | cgi_reply(); |
| 90 | } |
| 91 | else if( g.cgiOutput ){ |
| 92 | g.cgiOutput = 0; |
| 93 | cgi_printf("<h1>Database Error</h1>\n" |
| 94 | "<pre>%h</pre><p>%s</p>", z, zRebuildMsg); |
| 95 | cgi_reply(); |
| 96 | }else{ |
| 97 | fprintf(stderr, "%s: %s\n\n%s", g.argv[0], z, zRebuildMsg); |
| 98 | } |
| 99 | free(z); |
| @@ -484,20 +484,30 @@ | |
| 484 | /* |
| 485 | ** Execute multiple SQL statements. |
| 486 | */ |
| 487 | int db_multi_exec(const char *zSql, ...){ |
| 488 | Blob sql; |
| 489 | int rc; |
| 490 | va_list ap; |
| 491 | char *zErr = 0; |
| 492 | blob_init(&sql, 0, 0); |
| 493 | va_start(ap, zSql); |
| 494 | blob_vappendf(&sql, zSql, ap); |
| 495 | va_end(ap); |
| 496 | rc = sqlite3_exec(g.db, blob_buffer(&sql), 0, 0, &zErr); |
| 497 | if( rc!=SQLITE_OK ){ |
| 498 | db_err("%s\n%s", zErr, blob_buffer(&sql)); |
| 499 | } |
| 500 | blob_reset(&sql); |
| 501 | return rc; |
| 502 | } |
| 503 | |
| @@ -642,15 +652,11 @@ | |
| 642 | sqlite3 *db; |
| 643 | int rc; |
| 644 | const char *zSql; |
| 645 | va_list ap; |
| 646 | |
| 647 | rc = sqlite3_open(zFileName, &db); |
| 648 | if( rc!=SQLITE_OK ){ |
| 649 | db_err(sqlite3_errmsg(db)); |
| 650 | } |
| 651 | sqlite3_busy_timeout(db, 5000); |
| 652 | sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0); |
| 653 | rc = sqlite3_exec(db, zSchema, 0, 0, 0); |
| 654 | if( rc!=SQLITE_OK ){ |
| 655 | db_err(sqlite3_errmsg(db)); |
| 656 | } |
| @@ -697,29 +703,43 @@ | |
| 697 | |
| 698 | /* |
| 699 | ** Open a database file. Return a pointer to the new database |
| 700 | ** connection. An error results in process abort. |
| 701 | */ |
| 702 | static sqlite3 *openDatabase(const char *zDbName){ |
| 703 | int rc; |
| 704 | const char *zVfs; |
| 705 | sqlite3 *db; |
| 706 | |
| 707 | zVfs = fossil_getenv("FOSSIL_VFS"); |
| 708 | rc = sqlite3_open_v2( |
| 709 | zDbName, &db, |
| 710 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 711 | zVfs |
| 712 | ); |
| 713 | if( rc!=SQLITE_OK ){ |
| 714 | db_err(sqlite3_errmsg(db)); |
| 715 | } |
| 716 | sqlite3_busy_timeout(db, 5000); |
| 717 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 718 | sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0); |
| 719 | sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_ANY, 0, |
| 720 | db_checkin_mtime_function, 0, 0); |
| 721 | return db; |
| 722 | } |
| 723 | |
| 724 | |
| 725 | /* |
| @@ -747,13 +767,12 @@ | |
| 747 | const char *zLabel, |
| 748 | int *pWasAttached |
| 749 | ){ |
| 750 | if( !g.db ){ |
| 751 | assert( g.zMainDbType==0 ); |
| 752 | g.db = openDatabase(zDbName); |
| 753 | g.zMainDbType = zLabel; |
| 754 | db_connection_init(); |
| 755 | if ( pWasAttached ) *pWasAttached = 0; |
| 756 | }else{ |
| 757 | assert( g.zMainDbType!=0 ); |
| 758 | db_attach(zDbName, zLabel); |
| 759 | if ( pWasAttached ) *pWasAttached = 1; |
| @@ -820,11 +839,11 @@ | |
| 820 | db_open_or_attach(zDbName, "configdb", &g.useAttach); |
| 821 | g.dbConfig = 0; |
| 822 | g.zConfigDbType = 0; |
| 823 | }else{ |
| 824 | g.useAttach = 0; |
| 825 | g.dbConfig = openDatabase(zDbName); |
| 826 | g.zConfigDbType = "configdb"; |
| 827 | } |
| 828 | g.configOpen = 1; |
| 829 | free(zDbName); |
| 830 | } |
| @@ -1221,10 +1240,13 @@ | |
| 1221 | void db_create_default_users(int setupUserOnly, const char *zDefaultUser){ |
| 1222 | const char *zUser = zDefaultUser; |
| 1223 | if( zUser==0 ){ |
| 1224 | zUser = db_get("default-user", 0); |
| 1225 | } |
| 1226 | if( zUser==0 ){ |
| 1227 | #if defined(_WIN32) |
| 1228 | zUser = fossil_getenv("USERNAME"); |
| 1229 | #else |
| 1230 | zUser = fossil_getenv("USER"); |
| @@ -1441,11 +1463,11 @@ | |
| 1441 | ** SQL functions for debugging. |
| 1442 | ** |
| 1443 | ** The print() function writes its arguments on stdout, but only |
| 1444 | ** if the -sqlprint command-line option is turned on. |
| 1445 | */ |
| 1446 | static void db_sql_print( |
| 1447 | sqlite3_context *context, |
| 1448 | int argc, |
| 1449 | sqlite3_value **argv |
| 1450 | ){ |
| 1451 | int i; |
| @@ -1454,22 +1476,20 @@ | |
| 1454 | char c = i==argc-1 ? '\n' : ' '; |
| 1455 | fossil_print("%s%c", sqlite3_value_text(argv[i]), c); |
| 1456 | } |
| 1457 | } |
| 1458 | } |
| 1459 | static void db_sql_trace(void *notUsed, const char *zSql){ |
| 1460 | int n = strlen(zSql); |
| 1461 | char *zMsg = mprintf("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";"); |
| 1462 | fossil_puts(zMsg, 1); |
| 1463 | fossil_free(zMsg); |
| 1464 | } |
| 1465 | |
| 1466 | /* |
| 1467 | ** Implement the user() SQL function. user() takes no arguments and |
| 1468 | ** returns the user ID of the current user. |
| 1469 | */ |
| 1470 | static void db_sql_user( |
| 1471 | sqlite3_context *context, |
| 1472 | int argc, |
| 1473 | sqlite3_value **argv |
| 1474 | ){ |
| 1475 | if( g.zLogin!=0 ){ |
| @@ -1481,11 +1501,11 @@ | |
| 1481 | ** Implement the cgi() SQL function. cgi() takes an argument which is |
| 1482 | ** a name of CGI query parameter. The value of that parameter is returned, |
| 1483 | ** if available. Optional second argument will be returned if the first |
| 1484 | ** doesn't exist as a CGI parameter. |
| 1485 | */ |
| 1486 | static void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 1487 | const char* zP; |
| 1488 | if( argc!=1 && argc!=2 ) return; |
| 1489 | zP = P((const char*)sqlite3_value_text(argv[0])); |
| 1490 | if( zP ){ |
| 1491 | sqlite3_result_text(context, zP, -1, SQLITE_STATIC); |
| @@ -1512,11 +1532,11 @@ | |
| 1512 | ** (meaning that id was named on the command-line). |
| 1513 | ** |
| 1514 | ** In the second form (3 arguments) return argument X if true and Y |
| 1515 | ** if false. Except if Y is NULL then always return X. |
| 1516 | */ |
| 1517 | static void file_is_selected( |
| 1518 | sqlite3_context *context, |
| 1519 | int argc, |
| 1520 | sqlite3_value **argv |
| 1521 | ){ |
| 1522 | int rc = 0; |
| @@ -1600,32 +1620,10 @@ | |
| 1600 | zOut = mprintf("%s", zKey); |
| 1601 | } |
| 1602 | return zOut; |
| 1603 | } |
| 1604 | |
| 1605 | /* |
| 1606 | ** This function registers auxiliary functions when the SQLite |
| 1607 | ** database connection is first established. |
| 1608 | */ |
| 1609 | void db_connection_init(void){ |
| 1610 | sqlite3_exec(g.db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); |
| 1611 | sqlite3_create_function(g.db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0); |
| 1612 | sqlite3_create_function(g.db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0); |
| 1613 | sqlite3_create_function(g.db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0); |
| 1614 | sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); |
| 1615 | sqlite3_create_function( |
| 1616 | g.db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 1617 | ); |
| 1618 | sqlite3_create_function( |
| 1619 | g.db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 1620 | ); |
| 1621 | if( g.fSqlTrace ){ |
| 1622 | sqlite3_trace(g.db, db_sql_trace, 0); |
| 1623 | } |
| 1624 | re_add_sql_func(g.db); |
| 1625 | } |
| 1626 | |
| 1627 | /* |
| 1628 | ** Return true if the string zVal represents "true" (or "false"). |
| 1629 | */ |
| 1630 | int is_truth(const char *zVal){ |
| 1631 | static const char *const azOn[] = { "on", "yes", "true", "1" }; |
| @@ -2066,17 +2064,18 @@ | |
| 2066 | { "default-perms", 0, 16, 0, "u" }, |
| 2067 | { "diff-binary", 0, 0, 0, "on" }, |
| 2068 | { "diff-command", 0, 40, 0, "" }, |
| 2069 | { "dont-push", 0, 0, 0, "off" }, |
| 2070 | { "editor", 0, 32, 0, "" }, |
| 2071 | { "gdiff-command", 0, 40, 0, "gdiff" }, |
| 2072 | { "gmerge-command",0, 40, 0, "" }, |
| 2073 | { "https-login", 0, 0, 0, "off" }, |
| 2074 | { "ignore-glob", 0, 40, 1, "" }, |
| 2075 | { "empty-dirs", 0, 40, 1, "" }, |
| 2076 | { "href-targets", 0, 0, 0, "on" }, |
| 2077 | { "http-port", 0, 16, 0, "8080" }, |
| 2078 | { "localauth", 0, 0, 0, "off" }, |
| 2079 | { "main-branch", 0, 40, 0, "trunk" }, |
| 2080 | { "manifest", 0, 0, 1, "off" }, |
| 2081 | #ifdef FOSSIL_ENABLE_MARKDOWN |
| 2082 | { "markdown", 0, 0, 0, "off" }, |
| @@ -2086,19 +2085,18 @@ | |
| 2086 | { "pgp-command", 0, 40, 0, "gpg --clearsign -o " }, |
| 2087 | { "proxy", 0, 32, 0, "off" }, |
| 2088 | { "relative-paths",0, 0, 0, "on" }, |
| 2089 | { "repo-cksum", 0, 0, 0, "on" }, |
| 2090 | { "self-register", 0, 0, 0, "off" }, |
| 2091 | { "ssl-ca-location",0, 40, 0, "" }, |
| 2092 | { "ssl-identity", 0, 40, 0, "" }, |
| 2093 | { "ssh-command", 0, 40, 0, "" }, |
| 2094 | { "th1-setup", 0, 40, 0, "" }, |
| 2095 | #ifdef FOSSIL_ENABLE_TCL |
| 2096 | { "tcl", 0, 0, 0, "off" }, |
| 2097 | { "tcl-setup", 0, 40, 0, "" }, |
| 2098 | #endif |
| 2099 | { "unicode-glob", 0, 40, 1, "" }, |
| 2100 | { "web-browser", 0, 32, 0, "" }, |
| 2101 | { "white-foreground", 0, 0, 0, "off" }, |
| 2102 | { 0,0,0,0,0 } |
| 2103 | }; |
| 2104 | |
| @@ -2175,17 +2173,23 @@ | |
| 2175 | ** diff-command External command to run when performing a diff. |
| 2176 | ** If undefined, the internal text diff will be used. |
| 2177 | ** |
| 2178 | ** dont-push Prevent this repository from pushing from client to |
| 2179 | ** server. Useful when setting up a private branch. |
| 2180 | ** |
| 2181 | ** empty-dirs A comma or newline-separated list of pathnames. On |
| 2182 | ** (versionable) update and checkout commands, if no file or directory |
| 2183 | ** exists with that name, an empty directory will be |
| 2184 | ** created. |
| 2185 | ** |
| 2186 | ** editor Text editor command used for check-in comments. |
| 2187 | ** |
| 2188 | ** gdiff-command External command to run when performing a graphical |
| 2189 | ** diff. If undefined, text diff will be used. |
| 2190 | ** |
| 2191 | ** gmerge-command A graphical merge conflict resolver command operating |
| @@ -2244,10 +2248,13 @@ | |
| 2244 | ** |
| 2245 | ** self-register Allow users to register themselves through the HTTP UI. |
| 2246 | ** This is useful if you want to see other names than |
| 2247 | ** "Anonymous" in e.g. ticketing system. On the other hand |
| 2248 | ** users can not be deleted. Default: off. |
| 2249 | ** |
| 2250 | ** ssl-ca-location The full pathname to a file containing PEM encoded |
| 2251 | ** CA root certificates, or a directory of certificates |
| 2252 | ** with filenames formed from the certificate hashes as |
| 2253 | ** required by OpenSSL. |
| @@ -2263,13 +2270,10 @@ | |
| 2263 | ** the certificate and private key files. |
| 2264 | ** This identity will be presented to SSL servers to |
| 2265 | ** authenticate this client, in addition to the normal |
| 2266 | ** password authentication. |
| 2267 | ** |
| 2268 | ** ssh-command Command used to talk to a remote machine with |
| 2269 | ** the "ssh://" protocol. |
| 2270 | ** |
| 2271 | ** tcl If enabled (and Fossil was compiled with Tcl support), |
| 2272 | ** Tcl integration commands will be added to the TH1 |
| 2273 | ** interpreter, allowing arbitrary Tcl expressions and |
| 2274 | ** scripts to be evaluated from TH1. Additionally, the Tcl |
| 2275 | ** interpreter will be able to evaluate arbitrary TH1 |
| @@ -2281,15 +2285,10 @@ | |
| 2281 | ** |
| 2282 | ** th1-setup This is the setup script to be evaluated after creating |
| 2283 | ** and initializing the TH1 interpreter. By default, this |
| 2284 | ** is empty and no extra setup is performed. |
| 2285 | ** |
| 2286 | ** unicode-glob The VALUE is a comma or newline-separated list of GLOB |
| 2287 | ** (versionable) patterns specifying files that the "commit" command will |
| 2288 | ** ignore when issuing warnings about text files that may |
| 2289 | ** contain Unicode. Set to "*" to disable Unicode checking. |
| 2290 | ** |
| 2291 | ** web-browser A shell command used to launch your preferred |
| 2292 | ** web browser when given a URL as an argument. |
| 2293 | ** Defaults to "start" on windows, "open" on Mac, |
| 2294 | ** and "firefox" on Unix. |
| 2295 | ** |
| 2296 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -22,11 +22,11 @@ | |
| 22 | ** |
| 23 | ** (1) The "user" database in ~/.fossil |
| 24 | ** |
| 25 | ** (2) The "repository" database |
| 26 | ** |
| 27 | ** (3) A local checkout database named "_FOSSIL_" or ".fslckout" |
| 28 | ** and located at the root of the local copy of the source tree. |
| 29 | ** |
| 30 | */ |
| 31 | #include "config.h" |
| 32 | #if ! defined(_WIN32) |
| @@ -89,11 +89,11 @@ | |
| 89 | cgi_reply(); |
| 90 | } |
| 91 | else if( g.cgiOutput ){ |
| 92 | g.cgiOutput = 0; |
| 93 | cgi_printf("<h1>Database Error</h1>\n" |
| 94 | "<pre>%h</pre>\n<p>%s</p>\n", z, zRebuildMsg); |
| 95 | cgi_reply(); |
| 96 | }else{ |
| 97 | fprintf(stderr, "%s: %s\n\n%s", g.argv[0], z, zRebuildMsg); |
| 98 | } |
| 99 | free(z); |
| @@ -484,20 +484,30 @@ | |
| 484 | /* |
| 485 | ** Execute multiple SQL statements. |
| 486 | */ |
| 487 | int db_multi_exec(const char *zSql, ...){ |
| 488 | Blob sql; |
| 489 | int rc = SQLITE_OK; |
| 490 | va_list ap; |
| 491 | const char *z, *zEnd; |
| 492 | sqlite3_stmt *pStmt; |
| 493 | blob_init(&sql, 0, 0); |
| 494 | va_start(ap, zSql); |
| 495 | blob_vappendf(&sql, zSql, ap); |
| 496 | va_end(ap); |
| 497 | z = blob_str(&sql); |
| 498 | while( rc==SQLITE_OK && z[0] ){ |
| 499 | pStmt = 0; |
| 500 | rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd); |
| 501 | if( rc!=SQLITE_OK ) break; |
| 502 | if( pStmt ){ |
| 503 | db.nPrepare++; |
| 504 | while( sqlite3_step(pStmt)==SQLITE_ROW ){} |
| 505 | rc = sqlite3_finalize(pStmt); |
| 506 | if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z); |
| 507 | } |
| 508 | z = zEnd; |
| 509 | } |
| 510 | blob_reset(&sql); |
| 511 | return rc; |
| 512 | } |
| 513 | |
| @@ -642,15 +652,11 @@ | |
| 652 | sqlite3 *db; |
| 653 | int rc; |
| 654 | const char *zSql; |
| 655 | va_list ap; |
| 656 | |
| 657 | db = db_open(zFileName); |
| 658 | sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0); |
| 659 | rc = sqlite3_exec(db, zSchema, 0, 0, 0); |
| 660 | if( rc!=SQLITE_OK ){ |
| 661 | db_err(sqlite3_errmsg(db)); |
| 662 | } |
| @@ -697,29 +703,43 @@ | |
| 703 | |
| 704 | /* |
| 705 | ** Open a database file. Return a pointer to the new database |
| 706 | ** connection. An error results in process abort. |
| 707 | */ |
| 708 | LOCAL sqlite3 *db_open(const char *zDbName){ |
| 709 | int rc; |
| 710 | const char *zVfs; |
| 711 | sqlite3 *db; |
| 712 | |
| 713 | if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName); |
| 714 | zVfs = fossil_getenv("FOSSIL_VFS"); |
| 715 | rc = sqlite3_open_v2( |
| 716 | zDbName, &db, |
| 717 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 718 | zVfs |
| 719 | ); |
| 720 | if( rc!=SQLITE_OK ){ |
| 721 | db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); |
| 722 | } |
| 723 | sqlite3_busy_timeout(db, 5000); |
| 724 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 725 | sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0); |
| 726 | sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_ANY, 0, |
| 727 | db_checkin_mtime_function, 0, 0); |
| 728 | sqlite3_create_function(db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0); |
| 729 | sqlite3_create_function(db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0); |
| 730 | sqlite3_create_function(db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0); |
| 731 | sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); |
| 732 | sqlite3_create_function( |
| 733 | db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 734 | ); |
| 735 | sqlite3_create_function( |
| 736 | db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 737 | ); |
| 738 | if( g.fSqlTrace ) sqlite3_trace(db, db_sql_trace, 0); |
| 739 | re_add_sql_func(db); |
| 740 | sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); |
| 741 | return db; |
| 742 | } |
| 743 | |
| 744 | |
| 745 | /* |
| @@ -747,13 +767,12 @@ | |
| 767 | const char *zLabel, |
| 768 | int *pWasAttached |
| 769 | ){ |
| 770 | if( !g.db ){ |
| 771 | assert( g.zMainDbType==0 ); |
| 772 | g.db = db_open(zDbName); |
| 773 | g.zMainDbType = zLabel; |
| 774 | if ( pWasAttached ) *pWasAttached = 0; |
| 775 | }else{ |
| 776 | assert( g.zMainDbType!=0 ); |
| 777 | db_attach(zDbName, zLabel); |
| 778 | if ( pWasAttached ) *pWasAttached = 1; |
| @@ -820,11 +839,11 @@ | |
| 839 | db_open_or_attach(zDbName, "configdb", &g.useAttach); |
| 840 | g.dbConfig = 0; |
| 841 | g.zConfigDbType = 0; |
| 842 | }else{ |
| 843 | g.useAttach = 0; |
| 844 | g.dbConfig = db_open(zDbName); |
| 845 | g.zConfigDbType = "configdb"; |
| 846 | } |
| 847 | g.configOpen = 1; |
| 848 | free(zDbName); |
| 849 | } |
| @@ -1221,10 +1240,13 @@ | |
| 1240 | void db_create_default_users(int setupUserOnly, const char *zDefaultUser){ |
| 1241 | const char *zUser = zDefaultUser; |
| 1242 | if( zUser==0 ){ |
| 1243 | zUser = db_get("default-user", 0); |
| 1244 | } |
| 1245 | if( zUser==0 ){ |
| 1246 | zUser = fossil_getenv("FOSSIL_USER"); |
| 1247 | } |
| 1248 | if( zUser==0 ){ |
| 1249 | #if defined(_WIN32) |
| 1250 | zUser = fossil_getenv("USERNAME"); |
| 1251 | #else |
| 1252 | zUser = fossil_getenv("USER"); |
| @@ -1441,11 +1463,11 @@ | |
| 1463 | ** SQL functions for debugging. |
| 1464 | ** |
| 1465 | ** The print() function writes its arguments on stdout, but only |
| 1466 | ** if the -sqlprint command-line option is turned on. |
| 1467 | */ |
| 1468 | LOCAL void db_sql_print( |
| 1469 | sqlite3_context *context, |
| 1470 | int argc, |
| 1471 | sqlite3_value **argv |
| 1472 | ){ |
| 1473 | int i; |
| @@ -1454,22 +1476,20 @@ | |
| 1476 | char c = i==argc-1 ? '\n' : ' '; |
| 1477 | fossil_print("%s%c", sqlite3_value_text(argv[i]), c); |
| 1478 | } |
| 1479 | } |
| 1480 | } |
| 1481 | LOCAL void db_sql_trace(void *notUsed, const char *zSql){ |
| 1482 | int n = strlen(zSql); |
| 1483 | fossil_trace("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";"); |
| 1484 | } |
| 1485 | |
| 1486 | /* |
| 1487 | ** Implement the user() SQL function. user() takes no arguments and |
| 1488 | ** returns the user ID of the current user. |
| 1489 | */ |
| 1490 | LOCAL void db_sql_user( |
| 1491 | sqlite3_context *context, |
| 1492 | int argc, |
| 1493 | sqlite3_value **argv |
| 1494 | ){ |
| 1495 | if( g.zLogin!=0 ){ |
| @@ -1481,11 +1501,11 @@ | |
| 1501 | ** Implement the cgi() SQL function. cgi() takes an argument which is |
| 1502 | ** a name of CGI query parameter. The value of that parameter is returned, |
| 1503 | ** if available. Optional second argument will be returned if the first |
| 1504 | ** doesn't exist as a CGI parameter. |
| 1505 | */ |
| 1506 | LOCAL void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 1507 | const char* zP; |
| 1508 | if( argc!=1 && argc!=2 ) return; |
| 1509 | zP = P((const char*)sqlite3_value_text(argv[0])); |
| 1510 | if( zP ){ |
| 1511 | sqlite3_result_text(context, zP, -1, SQLITE_STATIC); |
| @@ -1512,11 +1532,11 @@ | |
| 1532 | ** (meaning that id was named on the command-line). |
| 1533 | ** |
| 1534 | ** In the second form (3 arguments) return argument X if true and Y |
| 1535 | ** if false. Except if Y is NULL then always return X. |
| 1536 | */ |
| 1537 | LOCAL void file_is_selected( |
| 1538 | sqlite3_context *context, |
| 1539 | int argc, |
| 1540 | sqlite3_value **argv |
| 1541 | ){ |
| 1542 | int rc = 0; |
| @@ -1600,32 +1620,10 @@ | |
| 1620 | zOut = mprintf("%s", zKey); |
| 1621 | } |
| 1622 | return zOut; |
| 1623 | } |
| 1624 | |
| 1625 | /* |
| 1626 | ** Return true if the string zVal represents "true" (or "false"). |
| 1627 | */ |
| 1628 | int is_truth(const char *zVal){ |
| 1629 | static const char *const azOn[] = { "on", "yes", "true", "1" }; |
| @@ -2066,17 +2064,18 @@ | |
| 2064 | { "default-perms", 0, 16, 0, "u" }, |
| 2065 | { "diff-binary", 0, 0, 0, "on" }, |
| 2066 | { "diff-command", 0, 40, 0, "" }, |
| 2067 | { "dont-push", 0, 0, 0, "off" }, |
| 2068 | { "editor", 0, 32, 0, "" }, |
| 2069 | { "empty-dirs", 0, 40, 1, "" }, |
| 2070 | { "encoding-glob", 0, 40, 1, "" }, |
| 2071 | { "gdiff-command", 0, 40, 0, "gdiff" }, |
| 2072 | { "gmerge-command",0, 40, 0, "" }, |
| 2073 | { "http-port", 0, 16, 0, "8080" }, |
| 2074 | { "https-login", 0, 0, 0, "off" }, |
| 2075 | { "href-targets", 0, 0, 0, "on" }, |
| 2076 | { "ignore-glob", 0, 40, 1, "" }, |
| 2077 | { "localauth", 0, 0, 0, "off" }, |
| 2078 | { "main-branch", 0, 40, 0, "trunk" }, |
| 2079 | { "manifest", 0, 0, 1, "off" }, |
| 2080 | #ifdef FOSSIL_ENABLE_MARKDOWN |
| 2081 | { "markdown", 0, 0, 0, "off" }, |
| @@ -2086,19 +2085,18 @@ | |
| 2085 | { "pgp-command", 0, 40, 0, "gpg --clearsign -o " }, |
| 2086 | { "proxy", 0, 32, 0, "off" }, |
| 2087 | { "relative-paths",0, 0, 0, "on" }, |
| 2088 | { "repo-cksum", 0, 0, 0, "on" }, |
| 2089 | { "self-register", 0, 0, 0, "off" }, |
| 2090 | { "ssh-command", 0, 40, 0, "" }, |
| 2091 | { "ssl-ca-location",0, 40, 0, "" }, |
| 2092 | { "ssl-identity", 0, 40, 0, "" }, |
| 2093 | #ifdef FOSSIL_ENABLE_TCL |
| 2094 | { "tcl", 0, 0, 0, "off" }, |
| 2095 | { "tcl-setup", 0, 40, 0, "" }, |
| 2096 | #endif |
| 2097 | { "th1-setup", 0, 40, 0, "" }, |
| 2098 | { "web-browser", 0, 32, 0, "" }, |
| 2099 | { "white-foreground", 0, 0, 0, "off" }, |
| 2100 | { 0,0,0,0,0 } |
| 2101 | }; |
| 2102 | |
| @@ -2175,17 +2173,23 @@ | |
| 2173 | ** diff-command External command to run when performing a diff. |
| 2174 | ** If undefined, the internal text diff will be used. |
| 2175 | ** |
| 2176 | ** dont-push Prevent this repository from pushing from client to |
| 2177 | ** server. Useful when setting up a private branch. |
| 2178 | ** |
| 2179 | ** editor Text editor command used for check-in comments. |
| 2180 | ** |
| 2181 | ** empty-dirs A comma or newline-separated list of pathnames. On |
| 2182 | ** (versionable) update and checkout commands, if no file or directory |
| 2183 | ** exists with that name, an empty directory will be |
| 2184 | ** created. |
| 2185 | ** |
| 2186 | ** encoding-glob The VALUE is a comma or newline-separated list of GLOB |
| 2187 | ** (versionable) patterns specifying files that the "commit" command will |
| 2188 | ** ignore when issuing warnings about text files that may |
| 2189 | ** use another encoding than ASCII or UTF-8. Set to "*" |
| 2190 | ** to disable encoding checking. |
| 2191 | ** |
| 2192 | ** gdiff-command External command to run when performing a graphical |
| 2193 | ** diff. If undefined, text diff will be used. |
| 2194 | ** |
| 2195 | ** gmerge-command A graphical merge conflict resolver command operating |
| @@ -2244,10 +2248,13 @@ | |
| 2248 | ** |
| 2249 | ** self-register Allow users to register themselves through the HTTP UI. |
| 2250 | ** This is useful if you want to see other names than |
| 2251 | ** "Anonymous" in e.g. ticketing system. On the other hand |
| 2252 | ** users can not be deleted. Default: off. |
| 2253 | ** |
| 2254 | ** ssh-command Command used to talk to a remote machine with |
| 2255 | ** the "ssh://" protocol. |
| 2256 | ** |
| 2257 | ** ssl-ca-location The full pathname to a file containing PEM encoded |
| 2258 | ** CA root certificates, or a directory of certificates |
| 2259 | ** with filenames formed from the certificate hashes as |
| 2260 | ** required by OpenSSL. |
| @@ -2263,13 +2270,10 @@ | |
| 2270 | ** the certificate and private key files. |
| 2271 | ** This identity will be presented to SSL servers to |
| 2272 | ** authenticate this client, in addition to the normal |
| 2273 | ** password authentication. |
| 2274 | ** |
| 2275 | ** tcl If enabled (and Fossil was compiled with Tcl support), |
| 2276 | ** Tcl integration commands will be added to the TH1 |
| 2277 | ** interpreter, allowing arbitrary Tcl expressions and |
| 2278 | ** scripts to be evaluated from TH1. Additionally, the Tcl |
| 2279 | ** interpreter will be able to evaluate arbitrary TH1 |
| @@ -2281,15 +2285,10 @@ | |
| 2285 | ** |
| 2286 | ** th1-setup This is the setup script to be evaluated after creating |
| 2287 | ** and initializing the TH1 interpreter. By default, this |
| 2288 | ** is empty and no extra setup is performed. |
| 2289 | ** |
| 2290 | ** web-browser A shell command used to launch your preferred |
| 2291 | ** web browser when given a URL as an argument. |
| 2292 | ** Defaults to "start" on windows, "open" on Mac, |
| 2293 | ** and "firefox" on Unix. |
| 2294 | ** |
| 2295 |
M
src/db.c
+61
-62
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -22,11 +22,11 @@ | ||
| 22 | 22 | ** |
| 23 | 23 | ** (1) The "user" database in ~/.fossil |
| 24 | 24 | ** |
| 25 | 25 | ** (2) The "repository" database |
| 26 | 26 | ** |
| 27 | -** (3) A local checkout database named "_FOSSIL_" or ".fos" | |
| 27 | +** (3) A local checkout database named "_FOSSIL_" or ".fslckout" | |
| 28 | 28 | ** and located at the root of the local copy of the source tree. |
| 29 | 29 | ** |
| 30 | 30 | */ |
| 31 | 31 | #include "config.h" |
| 32 | 32 | #if ! defined(_WIN32) |
| @@ -89,11 +89,11 @@ | ||
| 89 | 89 | cgi_reply(); |
| 90 | 90 | } |
| 91 | 91 | else if( g.cgiOutput ){ |
| 92 | 92 | g.cgiOutput = 0; |
| 93 | 93 | cgi_printf("<h1>Database Error</h1>\n" |
| 94 | - "<pre>%h</pre><p>%s</p>", z, zRebuildMsg); | |
| 94 | + "<pre>%h</pre>\n<p>%s</p>\n", z, zRebuildMsg); | |
| 95 | 95 | cgi_reply(); |
| 96 | 96 | }else{ |
| 97 | 97 | fprintf(stderr, "%s: %s\n\n%s", g.argv[0], z, zRebuildMsg); |
| 98 | 98 | } |
| 99 | 99 | free(z); |
| @@ -484,20 +484,30 @@ | ||
| 484 | 484 | /* |
| 485 | 485 | ** Execute multiple SQL statements. |
| 486 | 486 | */ |
| 487 | 487 | int db_multi_exec(const char *zSql, ...){ |
| 488 | 488 | Blob sql; |
| 489 | - int rc; | |
| 489 | + int rc = SQLITE_OK; | |
| 490 | 490 | va_list ap; |
| 491 | - char *zErr = 0; | |
| 491 | + const char *z, *zEnd; | |
| 492 | + sqlite3_stmt *pStmt; | |
| 492 | 493 | blob_init(&sql, 0, 0); |
| 493 | 494 | va_start(ap, zSql); |
| 494 | 495 | blob_vappendf(&sql, zSql, ap); |
| 495 | 496 | va_end(ap); |
| 496 | - rc = sqlite3_exec(g.db, blob_buffer(&sql), 0, 0, &zErr); | |
| 497 | - if( rc!=SQLITE_OK ){ | |
| 498 | - db_err("%s\n%s", zErr, blob_buffer(&sql)); | |
| 497 | + z = blob_str(&sql); | |
| 498 | + while( rc==SQLITE_OK && z[0] ){ | |
| 499 | + pStmt = 0; | |
| 500 | + rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd); | |
| 501 | + if( rc!=SQLITE_OK ) break; | |
| 502 | + if( pStmt ){ | |
| 503 | + db.nPrepare++; | |
| 504 | + while( sqlite3_step(pStmt)==SQLITE_ROW ){} | |
| 505 | + rc = sqlite3_finalize(pStmt); | |
| 506 | + if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z); | |
| 507 | + } | |
| 508 | + z = zEnd; | |
| 499 | 509 | } |
| 500 | 510 | blob_reset(&sql); |
| 501 | 511 | return rc; |
| 502 | 512 | } |
| 503 | 513 | |
| @@ -642,15 +652,11 @@ | ||
| 642 | 652 | sqlite3 *db; |
| 643 | 653 | int rc; |
| 644 | 654 | const char *zSql; |
| 645 | 655 | va_list ap; |
| 646 | 656 | |
| 647 | - rc = sqlite3_open(zFileName, &db); | |
| 648 | - if( rc!=SQLITE_OK ){ | |
| 649 | - db_err(sqlite3_errmsg(db)); | |
| 650 | - } | |
| 651 | - sqlite3_busy_timeout(db, 5000); | |
| 657 | + db = db_open(zFileName); | |
| 652 | 658 | sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0); |
| 653 | 659 | rc = sqlite3_exec(db, zSchema, 0, 0, 0); |
| 654 | 660 | if( rc!=SQLITE_OK ){ |
| 655 | 661 | db_err(sqlite3_errmsg(db)); |
| 656 | 662 | } |
| @@ -697,29 +703,43 @@ | ||
| 697 | 703 | |
| 698 | 704 | /* |
| 699 | 705 | ** Open a database file. Return a pointer to the new database |
| 700 | 706 | ** connection. An error results in process abort. |
| 701 | 707 | */ |
| 702 | -static sqlite3 *openDatabase(const char *zDbName){ | |
| 708 | +LOCAL sqlite3 *db_open(const char *zDbName){ | |
| 703 | 709 | int rc; |
| 704 | 710 | const char *zVfs; |
| 705 | 711 | sqlite3 *db; |
| 706 | 712 | |
| 713 | + if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName); | |
| 707 | 714 | zVfs = fossil_getenv("FOSSIL_VFS"); |
| 708 | 715 | rc = sqlite3_open_v2( |
| 709 | 716 | zDbName, &db, |
| 710 | 717 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 711 | 718 | zVfs |
| 712 | 719 | ); |
| 713 | 720 | if( rc!=SQLITE_OK ){ |
| 714 | - db_err(sqlite3_errmsg(db)); | |
| 721 | + db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); | |
| 715 | 722 | } |
| 716 | 723 | sqlite3_busy_timeout(db, 5000); |
| 717 | 724 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 718 | 725 | sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0); |
| 719 | 726 | sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_ANY, 0, |
| 720 | 727 | db_checkin_mtime_function, 0, 0); |
| 728 | + sqlite3_create_function(db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0); | |
| 729 | + sqlite3_create_function(db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0); | |
| 730 | + sqlite3_create_function(db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0); | |
| 731 | + sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); | |
| 732 | + sqlite3_create_function( | |
| 733 | + db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 | |
| 734 | + ); | |
| 735 | + sqlite3_create_function( | |
| 736 | + db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 | |
| 737 | + ); | |
| 738 | + if( g.fSqlTrace ) sqlite3_trace(db, db_sql_trace, 0); | |
| 739 | + re_add_sql_func(db); | |
| 740 | + sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); | |
| 721 | 741 | return db; |
| 722 | 742 | } |
| 723 | 743 | |
| 724 | 744 | |
| 725 | 745 | /* |
| @@ -747,13 +767,12 @@ | ||
| 747 | 767 | const char *zLabel, |
| 748 | 768 | int *pWasAttached |
| 749 | 769 | ){ |
| 750 | 770 | if( !g.db ){ |
| 751 | 771 | assert( g.zMainDbType==0 ); |
| 752 | - g.db = openDatabase(zDbName); | |
| 772 | + g.db = db_open(zDbName); | |
| 753 | 773 | g.zMainDbType = zLabel; |
| 754 | - db_connection_init(); | |
| 755 | 774 | if ( pWasAttached ) *pWasAttached = 0; |
| 756 | 775 | }else{ |
| 757 | 776 | assert( g.zMainDbType!=0 ); |
| 758 | 777 | db_attach(zDbName, zLabel); |
| 759 | 778 | if ( pWasAttached ) *pWasAttached = 1; |
| @@ -820,11 +839,11 @@ | ||
| 820 | 839 | db_open_or_attach(zDbName, "configdb", &g.useAttach); |
| 821 | 840 | g.dbConfig = 0; |
| 822 | 841 | g.zConfigDbType = 0; |
| 823 | 842 | }else{ |
| 824 | 843 | g.useAttach = 0; |
| 825 | - g.dbConfig = openDatabase(zDbName); | |
| 844 | + g.dbConfig = db_open(zDbName); | |
| 826 | 845 | g.zConfigDbType = "configdb"; |
| 827 | 846 | } |
| 828 | 847 | g.configOpen = 1; |
| 829 | 848 | free(zDbName); |
| 830 | 849 | } |
| @@ -1221,10 +1240,13 @@ | ||
| 1221 | 1240 | void db_create_default_users(int setupUserOnly, const char *zDefaultUser){ |
| 1222 | 1241 | const char *zUser = zDefaultUser; |
| 1223 | 1242 | if( zUser==0 ){ |
| 1224 | 1243 | zUser = db_get("default-user", 0); |
| 1225 | 1244 | } |
| 1245 | + if( zUser==0 ){ | |
| 1246 | + zUser = fossil_getenv("FOSSIL_USER"); | |
| 1247 | + } | |
| 1226 | 1248 | if( zUser==0 ){ |
| 1227 | 1249 | #if defined(_WIN32) |
| 1228 | 1250 | zUser = fossil_getenv("USERNAME"); |
| 1229 | 1251 | #else |
| 1230 | 1252 | zUser = fossil_getenv("USER"); |
| @@ -1441,11 +1463,11 @@ | ||
| 1441 | 1463 | ** SQL functions for debugging. |
| 1442 | 1464 | ** |
| 1443 | 1465 | ** The print() function writes its arguments on stdout, but only |
| 1444 | 1466 | ** if the -sqlprint command-line option is turned on. |
| 1445 | 1467 | */ |
| 1446 | -static void db_sql_print( | |
| 1468 | +LOCAL void db_sql_print( | |
| 1447 | 1469 | sqlite3_context *context, |
| 1448 | 1470 | int argc, |
| 1449 | 1471 | sqlite3_value **argv |
| 1450 | 1472 | ){ |
| 1451 | 1473 | int i; |
| @@ -1454,22 +1476,20 @@ | ||
| 1454 | 1476 | char c = i==argc-1 ? '\n' : ' '; |
| 1455 | 1477 | fossil_print("%s%c", sqlite3_value_text(argv[i]), c); |
| 1456 | 1478 | } |
| 1457 | 1479 | } |
| 1458 | 1480 | } |
| 1459 | -static void db_sql_trace(void *notUsed, const char *zSql){ | |
| 1481 | +LOCAL void db_sql_trace(void *notUsed, const char *zSql){ | |
| 1460 | 1482 | int n = strlen(zSql); |
| 1461 | - char *zMsg = mprintf("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";"); | |
| 1462 | - fossil_puts(zMsg, 1); | |
| 1463 | - fossil_free(zMsg); | |
| 1483 | + fossil_trace("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";"); | |
| 1464 | 1484 | } |
| 1465 | 1485 | |
| 1466 | 1486 | /* |
| 1467 | 1487 | ** Implement the user() SQL function. user() takes no arguments and |
| 1468 | 1488 | ** returns the user ID of the current user. |
| 1469 | 1489 | */ |
| 1470 | -static void db_sql_user( | |
| 1490 | +LOCAL void db_sql_user( | |
| 1471 | 1491 | sqlite3_context *context, |
| 1472 | 1492 | int argc, |
| 1473 | 1493 | sqlite3_value **argv |
| 1474 | 1494 | ){ |
| 1475 | 1495 | if( g.zLogin!=0 ){ |
| @@ -1481,11 +1501,11 @@ | ||
| 1481 | 1501 | ** Implement the cgi() SQL function. cgi() takes an argument which is |
| 1482 | 1502 | ** a name of CGI query parameter. The value of that parameter is returned, |
| 1483 | 1503 | ** if available. Optional second argument will be returned if the first |
| 1484 | 1504 | ** doesn't exist as a CGI parameter. |
| 1485 | 1505 | */ |
| 1486 | -static void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){ | |
| 1506 | +LOCAL void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){ | |
| 1487 | 1507 | const char* zP; |
| 1488 | 1508 | if( argc!=1 && argc!=2 ) return; |
| 1489 | 1509 | zP = P((const char*)sqlite3_value_text(argv[0])); |
| 1490 | 1510 | if( zP ){ |
| 1491 | 1511 | sqlite3_result_text(context, zP, -1, SQLITE_STATIC); |
| @@ -1512,11 +1532,11 @@ | ||
| 1512 | 1532 | ** (meaning that id was named on the command-line). |
| 1513 | 1533 | ** |
| 1514 | 1534 | ** In the second form (3 arguments) return argument X if true and Y |
| 1515 | 1535 | ** if false. Except if Y is NULL then always return X. |
| 1516 | 1536 | */ |
| 1517 | -static void file_is_selected( | |
| 1537 | +LOCAL void file_is_selected( | |
| 1518 | 1538 | sqlite3_context *context, |
| 1519 | 1539 | int argc, |
| 1520 | 1540 | sqlite3_value **argv |
| 1521 | 1541 | ){ |
| 1522 | 1542 | int rc = 0; |
| @@ -1600,32 +1620,10 @@ | ||
| 1600 | 1620 | zOut = mprintf("%s", zKey); |
| 1601 | 1621 | } |
| 1602 | 1622 | return zOut; |
| 1603 | 1623 | } |
| 1604 | 1624 | |
| 1605 | -/* | |
| 1606 | -** This function registers auxiliary functions when the SQLite | |
| 1607 | -** database connection is first established. | |
| 1608 | -*/ | |
| 1609 | -void db_connection_init(void){ | |
| 1610 | - sqlite3_exec(g.db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); | |
| 1611 | - sqlite3_create_function(g.db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0); | |
| 1612 | - sqlite3_create_function(g.db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0); | |
| 1613 | - sqlite3_create_function(g.db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0); | |
| 1614 | - sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); | |
| 1615 | - sqlite3_create_function( | |
| 1616 | - g.db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 | |
| 1617 | - ); | |
| 1618 | - sqlite3_create_function( | |
| 1619 | - g.db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 | |
| 1620 | - ); | |
| 1621 | - if( g.fSqlTrace ){ | |
| 1622 | - sqlite3_trace(g.db, db_sql_trace, 0); | |
| 1623 | - } | |
| 1624 | - re_add_sql_func(g.db); | |
| 1625 | -} | |
| 1626 | - | |
| 1627 | 1625 | /* |
| 1628 | 1626 | ** Return true if the string zVal represents "true" (or "false"). |
| 1629 | 1627 | */ |
| 1630 | 1628 | int is_truth(const char *zVal){ |
| 1631 | 1629 | static const char *const azOn[] = { "on", "yes", "true", "1" }; |
| @@ -2066,17 +2064,18 @@ | ||
| 2066 | 2064 | { "default-perms", 0, 16, 0, "u" }, |
| 2067 | 2065 | { "diff-binary", 0, 0, 0, "on" }, |
| 2068 | 2066 | { "diff-command", 0, 40, 0, "" }, |
| 2069 | 2067 | { "dont-push", 0, 0, 0, "off" }, |
| 2070 | 2068 | { "editor", 0, 32, 0, "" }, |
| 2069 | + { "empty-dirs", 0, 40, 1, "" }, | |
| 2070 | + { "encoding-glob", 0, 40, 1, "" }, | |
| 2071 | 2071 | { "gdiff-command", 0, 40, 0, "gdiff" }, |
| 2072 | 2072 | { "gmerge-command",0, 40, 0, "" }, |
| 2073 | + { "http-port", 0, 16, 0, "8080" }, | |
| 2073 | 2074 | { "https-login", 0, 0, 0, "off" }, |
| 2074 | - { "ignore-glob", 0, 40, 1, "" }, | |
| 2075 | - { "empty-dirs", 0, 40, 1, "" }, | |
| 2076 | 2075 | { "href-targets", 0, 0, 0, "on" }, |
| 2077 | - { "http-port", 0, 16, 0, "8080" }, | |
| 2076 | + { "ignore-glob", 0, 40, 1, "" }, | |
| 2078 | 2077 | { "localauth", 0, 0, 0, "off" }, |
| 2079 | 2078 | { "main-branch", 0, 40, 0, "trunk" }, |
| 2080 | 2079 | { "manifest", 0, 0, 1, "off" }, |
| 2081 | 2080 | #ifdef FOSSIL_ENABLE_MARKDOWN |
| 2082 | 2081 | { "markdown", 0, 0, 0, "off" }, |
| @@ -2086,19 +2085,18 @@ | ||
| 2086 | 2085 | { "pgp-command", 0, 40, 0, "gpg --clearsign -o " }, |
| 2087 | 2086 | { "proxy", 0, 32, 0, "off" }, |
| 2088 | 2087 | { "relative-paths",0, 0, 0, "on" }, |
| 2089 | 2088 | { "repo-cksum", 0, 0, 0, "on" }, |
| 2090 | 2089 | { "self-register", 0, 0, 0, "off" }, |
| 2090 | + { "ssh-command", 0, 40, 0, "" }, | |
| 2091 | 2091 | { "ssl-ca-location",0, 40, 0, "" }, |
| 2092 | 2092 | { "ssl-identity", 0, 40, 0, "" }, |
| 2093 | - { "ssh-command", 0, 40, 0, "" }, | |
| 2094 | - { "th1-setup", 0, 40, 0, "" }, | |
| 2095 | 2093 | #ifdef FOSSIL_ENABLE_TCL |
| 2096 | 2094 | { "tcl", 0, 0, 0, "off" }, |
| 2097 | 2095 | { "tcl-setup", 0, 40, 0, "" }, |
| 2098 | 2096 | #endif |
| 2099 | - { "unicode-glob", 0, 40, 1, "" }, | |
| 2097 | + { "th1-setup", 0, 40, 0, "" }, | |
| 2100 | 2098 | { "web-browser", 0, 32, 0, "" }, |
| 2101 | 2099 | { "white-foreground", 0, 0, 0, "off" }, |
| 2102 | 2100 | { 0,0,0,0,0 } |
| 2103 | 2101 | }; |
| 2104 | 2102 | |
| @@ -2175,17 +2173,23 @@ | ||
| 2175 | 2173 | ** diff-command External command to run when performing a diff. |
| 2176 | 2174 | ** If undefined, the internal text diff will be used. |
| 2177 | 2175 | ** |
| 2178 | 2176 | ** dont-push Prevent this repository from pushing from client to |
| 2179 | 2177 | ** server. Useful when setting up a private branch. |
| 2178 | +** | |
| 2179 | +** editor Text editor command used for check-in comments. | |
| 2180 | 2180 | ** |
| 2181 | 2181 | ** empty-dirs A comma or newline-separated list of pathnames. On |
| 2182 | 2182 | ** (versionable) update and checkout commands, if no file or directory |
| 2183 | 2183 | ** exists with that name, an empty directory will be |
| 2184 | 2184 | ** created. |
| 2185 | 2185 | ** |
| 2186 | -** editor Text editor command used for check-in comments. | |
| 2186 | +** encoding-glob The VALUE is a comma or newline-separated list of GLOB | |
| 2187 | +** (versionable) patterns specifying files that the "commit" command will | |
| 2188 | +** ignore when issuing warnings about text files that may | |
| 2189 | +** use another encoding than ASCII or UTF-8. Set to "*" | |
| 2190 | +** to disable encoding checking. | |
| 2187 | 2191 | ** |
| 2188 | 2192 | ** gdiff-command External command to run when performing a graphical |
| 2189 | 2193 | ** diff. If undefined, text diff will be used. |
| 2190 | 2194 | ** |
| 2191 | 2195 | ** gmerge-command A graphical merge conflict resolver command operating |
| @@ -2244,10 +2248,13 @@ | ||
| 2244 | 2248 | ** |
| 2245 | 2249 | ** self-register Allow users to register themselves through the HTTP UI. |
| 2246 | 2250 | ** This is useful if you want to see other names than |
| 2247 | 2251 | ** "Anonymous" in e.g. ticketing system. On the other hand |
| 2248 | 2252 | ** users can not be deleted. Default: off. |
| 2253 | +** | |
| 2254 | +** ssh-command Command used to talk to a remote machine with | |
| 2255 | +** the "ssh://" protocol. | |
| 2249 | 2256 | ** |
| 2250 | 2257 | ** ssl-ca-location The full pathname to a file containing PEM encoded |
| 2251 | 2258 | ** CA root certificates, or a directory of certificates |
| 2252 | 2259 | ** with filenames formed from the certificate hashes as |
| 2253 | 2260 | ** required by OpenSSL. |
| @@ -2263,13 +2270,10 @@ | ||
| 2263 | 2270 | ** the certificate and private key files. |
| 2264 | 2271 | ** This identity will be presented to SSL servers to |
| 2265 | 2272 | ** authenticate this client, in addition to the normal |
| 2266 | 2273 | ** password authentication. |
| 2267 | 2274 | ** |
| 2268 | -** ssh-command Command used to talk to a remote machine with | |
| 2269 | -** the "ssh://" protocol. | |
| 2270 | -** | |
| 2271 | 2275 | ** tcl If enabled (and Fossil was compiled with Tcl support), |
| 2272 | 2276 | ** Tcl integration commands will be added to the TH1 |
| 2273 | 2277 | ** interpreter, allowing arbitrary Tcl expressions and |
| 2274 | 2278 | ** scripts to be evaluated from TH1. Additionally, the Tcl |
| 2275 | 2279 | ** interpreter will be able to evaluate arbitrary TH1 |
| @@ -2281,15 +2285,10 @@ | ||
| 2281 | 2285 | ** |
| 2282 | 2286 | ** th1-setup This is the setup script to be evaluated after creating |
| 2283 | 2287 | ** and initializing the TH1 interpreter. By default, this |
| 2284 | 2288 | ** is empty and no extra setup is performed. |
| 2285 | 2289 | ** |
| 2286 | -** unicode-glob The VALUE is a comma or newline-separated list of GLOB | |
| 2287 | -** (versionable) patterns specifying files that the "commit" command will | |
| 2288 | -** ignore when issuing warnings about text files that may | |
| 2289 | -** contain Unicode. Set to "*" to disable Unicode checking. | |
| 2290 | -** | |
| 2291 | 2290 | ** web-browser A shell command used to launch your preferred |
| 2292 | 2291 | ** web browser when given a URL as an argument. |
| 2293 | 2292 | ** Defaults to "start" on windows, "open" on Mac, |
| 2294 | 2293 | ** and "firefox" on Unix. |
| 2295 | 2294 | ** |
| 2296 | 2295 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -22,11 +22,11 @@ | |
| 22 | ** |
| 23 | ** (1) The "user" database in ~/.fossil |
| 24 | ** |
| 25 | ** (2) The "repository" database |
| 26 | ** |
| 27 | ** (3) A local checkout database named "_FOSSIL_" or ".fos" |
| 28 | ** and located at the root of the local copy of the source tree. |
| 29 | ** |
| 30 | */ |
| 31 | #include "config.h" |
| 32 | #if ! defined(_WIN32) |
| @@ -89,11 +89,11 @@ | |
| 89 | cgi_reply(); |
| 90 | } |
| 91 | else if( g.cgiOutput ){ |
| 92 | g.cgiOutput = 0; |
| 93 | cgi_printf("<h1>Database Error</h1>\n" |
| 94 | "<pre>%h</pre><p>%s</p>", z, zRebuildMsg); |
| 95 | cgi_reply(); |
| 96 | }else{ |
| 97 | fprintf(stderr, "%s: %s\n\n%s", g.argv[0], z, zRebuildMsg); |
| 98 | } |
| 99 | free(z); |
| @@ -484,20 +484,30 @@ | |
| 484 | /* |
| 485 | ** Execute multiple SQL statements. |
| 486 | */ |
| 487 | int db_multi_exec(const char *zSql, ...){ |
| 488 | Blob sql; |
| 489 | int rc; |
| 490 | va_list ap; |
| 491 | char *zErr = 0; |
| 492 | blob_init(&sql, 0, 0); |
| 493 | va_start(ap, zSql); |
| 494 | blob_vappendf(&sql, zSql, ap); |
| 495 | va_end(ap); |
| 496 | rc = sqlite3_exec(g.db, blob_buffer(&sql), 0, 0, &zErr); |
| 497 | if( rc!=SQLITE_OK ){ |
| 498 | db_err("%s\n%s", zErr, blob_buffer(&sql)); |
| 499 | } |
| 500 | blob_reset(&sql); |
| 501 | return rc; |
| 502 | } |
| 503 | |
| @@ -642,15 +652,11 @@ | |
| 642 | sqlite3 *db; |
| 643 | int rc; |
| 644 | const char *zSql; |
| 645 | va_list ap; |
| 646 | |
| 647 | rc = sqlite3_open(zFileName, &db); |
| 648 | if( rc!=SQLITE_OK ){ |
| 649 | db_err(sqlite3_errmsg(db)); |
| 650 | } |
| 651 | sqlite3_busy_timeout(db, 5000); |
| 652 | sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0); |
| 653 | rc = sqlite3_exec(db, zSchema, 0, 0, 0); |
| 654 | if( rc!=SQLITE_OK ){ |
| 655 | db_err(sqlite3_errmsg(db)); |
| 656 | } |
| @@ -697,29 +703,43 @@ | |
| 697 | |
| 698 | /* |
| 699 | ** Open a database file. Return a pointer to the new database |
| 700 | ** connection. An error results in process abort. |
| 701 | */ |
| 702 | static sqlite3 *openDatabase(const char *zDbName){ |
| 703 | int rc; |
| 704 | const char *zVfs; |
| 705 | sqlite3 *db; |
| 706 | |
| 707 | zVfs = fossil_getenv("FOSSIL_VFS"); |
| 708 | rc = sqlite3_open_v2( |
| 709 | zDbName, &db, |
| 710 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 711 | zVfs |
| 712 | ); |
| 713 | if( rc!=SQLITE_OK ){ |
| 714 | db_err(sqlite3_errmsg(db)); |
| 715 | } |
| 716 | sqlite3_busy_timeout(db, 5000); |
| 717 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 718 | sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0); |
| 719 | sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_ANY, 0, |
| 720 | db_checkin_mtime_function, 0, 0); |
| 721 | return db; |
| 722 | } |
| 723 | |
| 724 | |
| 725 | /* |
| @@ -747,13 +767,12 @@ | |
| 747 | const char *zLabel, |
| 748 | int *pWasAttached |
| 749 | ){ |
| 750 | if( !g.db ){ |
| 751 | assert( g.zMainDbType==0 ); |
| 752 | g.db = openDatabase(zDbName); |
| 753 | g.zMainDbType = zLabel; |
| 754 | db_connection_init(); |
| 755 | if ( pWasAttached ) *pWasAttached = 0; |
| 756 | }else{ |
| 757 | assert( g.zMainDbType!=0 ); |
| 758 | db_attach(zDbName, zLabel); |
| 759 | if ( pWasAttached ) *pWasAttached = 1; |
| @@ -820,11 +839,11 @@ | |
| 820 | db_open_or_attach(zDbName, "configdb", &g.useAttach); |
| 821 | g.dbConfig = 0; |
| 822 | g.zConfigDbType = 0; |
| 823 | }else{ |
| 824 | g.useAttach = 0; |
| 825 | g.dbConfig = openDatabase(zDbName); |
| 826 | g.zConfigDbType = "configdb"; |
| 827 | } |
| 828 | g.configOpen = 1; |
| 829 | free(zDbName); |
| 830 | } |
| @@ -1221,10 +1240,13 @@ | |
| 1221 | void db_create_default_users(int setupUserOnly, const char *zDefaultUser){ |
| 1222 | const char *zUser = zDefaultUser; |
| 1223 | if( zUser==0 ){ |
| 1224 | zUser = db_get("default-user", 0); |
| 1225 | } |
| 1226 | if( zUser==0 ){ |
| 1227 | #if defined(_WIN32) |
| 1228 | zUser = fossil_getenv("USERNAME"); |
| 1229 | #else |
| 1230 | zUser = fossil_getenv("USER"); |
| @@ -1441,11 +1463,11 @@ | |
| 1441 | ** SQL functions for debugging. |
| 1442 | ** |
| 1443 | ** The print() function writes its arguments on stdout, but only |
| 1444 | ** if the -sqlprint command-line option is turned on. |
| 1445 | */ |
| 1446 | static void db_sql_print( |
| 1447 | sqlite3_context *context, |
| 1448 | int argc, |
| 1449 | sqlite3_value **argv |
| 1450 | ){ |
| 1451 | int i; |
| @@ -1454,22 +1476,20 @@ | |
| 1454 | char c = i==argc-1 ? '\n' : ' '; |
| 1455 | fossil_print("%s%c", sqlite3_value_text(argv[i]), c); |
| 1456 | } |
| 1457 | } |
| 1458 | } |
| 1459 | static void db_sql_trace(void *notUsed, const char *zSql){ |
| 1460 | int n = strlen(zSql); |
| 1461 | char *zMsg = mprintf("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";"); |
| 1462 | fossil_puts(zMsg, 1); |
| 1463 | fossil_free(zMsg); |
| 1464 | } |
| 1465 | |
| 1466 | /* |
| 1467 | ** Implement the user() SQL function. user() takes no arguments and |
| 1468 | ** returns the user ID of the current user. |
| 1469 | */ |
| 1470 | static void db_sql_user( |
| 1471 | sqlite3_context *context, |
| 1472 | int argc, |
| 1473 | sqlite3_value **argv |
| 1474 | ){ |
| 1475 | if( g.zLogin!=0 ){ |
| @@ -1481,11 +1501,11 @@ | |
| 1481 | ** Implement the cgi() SQL function. cgi() takes an argument which is |
| 1482 | ** a name of CGI query parameter. The value of that parameter is returned, |
| 1483 | ** if available. Optional second argument will be returned if the first |
| 1484 | ** doesn't exist as a CGI parameter. |
| 1485 | */ |
| 1486 | static void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 1487 | const char* zP; |
| 1488 | if( argc!=1 && argc!=2 ) return; |
| 1489 | zP = P((const char*)sqlite3_value_text(argv[0])); |
| 1490 | if( zP ){ |
| 1491 | sqlite3_result_text(context, zP, -1, SQLITE_STATIC); |
| @@ -1512,11 +1532,11 @@ | |
| 1512 | ** (meaning that id was named on the command-line). |
| 1513 | ** |
| 1514 | ** In the second form (3 arguments) return argument X if true and Y |
| 1515 | ** if false. Except if Y is NULL then always return X. |
| 1516 | */ |
| 1517 | static void file_is_selected( |
| 1518 | sqlite3_context *context, |
| 1519 | int argc, |
| 1520 | sqlite3_value **argv |
| 1521 | ){ |
| 1522 | int rc = 0; |
| @@ -1600,32 +1620,10 @@ | |
| 1600 | zOut = mprintf("%s", zKey); |
| 1601 | } |
| 1602 | return zOut; |
| 1603 | } |
| 1604 | |
| 1605 | /* |
| 1606 | ** This function registers auxiliary functions when the SQLite |
| 1607 | ** database connection is first established. |
| 1608 | */ |
| 1609 | void db_connection_init(void){ |
| 1610 | sqlite3_exec(g.db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); |
| 1611 | sqlite3_create_function(g.db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0); |
| 1612 | sqlite3_create_function(g.db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0); |
| 1613 | sqlite3_create_function(g.db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0); |
| 1614 | sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); |
| 1615 | sqlite3_create_function( |
| 1616 | g.db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 1617 | ); |
| 1618 | sqlite3_create_function( |
| 1619 | g.db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 1620 | ); |
| 1621 | if( g.fSqlTrace ){ |
| 1622 | sqlite3_trace(g.db, db_sql_trace, 0); |
| 1623 | } |
| 1624 | re_add_sql_func(g.db); |
| 1625 | } |
| 1626 | |
| 1627 | /* |
| 1628 | ** Return true if the string zVal represents "true" (or "false"). |
| 1629 | */ |
| 1630 | int is_truth(const char *zVal){ |
| 1631 | static const char *const azOn[] = { "on", "yes", "true", "1" }; |
| @@ -2066,17 +2064,18 @@ | |
| 2066 | { "default-perms", 0, 16, 0, "u" }, |
| 2067 | { "diff-binary", 0, 0, 0, "on" }, |
| 2068 | { "diff-command", 0, 40, 0, "" }, |
| 2069 | { "dont-push", 0, 0, 0, "off" }, |
| 2070 | { "editor", 0, 32, 0, "" }, |
| 2071 | { "gdiff-command", 0, 40, 0, "gdiff" }, |
| 2072 | { "gmerge-command",0, 40, 0, "" }, |
| 2073 | { "https-login", 0, 0, 0, "off" }, |
| 2074 | { "ignore-glob", 0, 40, 1, "" }, |
| 2075 | { "empty-dirs", 0, 40, 1, "" }, |
| 2076 | { "href-targets", 0, 0, 0, "on" }, |
| 2077 | { "http-port", 0, 16, 0, "8080" }, |
| 2078 | { "localauth", 0, 0, 0, "off" }, |
| 2079 | { "main-branch", 0, 40, 0, "trunk" }, |
| 2080 | { "manifest", 0, 0, 1, "off" }, |
| 2081 | #ifdef FOSSIL_ENABLE_MARKDOWN |
| 2082 | { "markdown", 0, 0, 0, "off" }, |
| @@ -2086,19 +2085,18 @@ | |
| 2086 | { "pgp-command", 0, 40, 0, "gpg --clearsign -o " }, |
| 2087 | { "proxy", 0, 32, 0, "off" }, |
| 2088 | { "relative-paths",0, 0, 0, "on" }, |
| 2089 | { "repo-cksum", 0, 0, 0, "on" }, |
| 2090 | { "self-register", 0, 0, 0, "off" }, |
| 2091 | { "ssl-ca-location",0, 40, 0, "" }, |
| 2092 | { "ssl-identity", 0, 40, 0, "" }, |
| 2093 | { "ssh-command", 0, 40, 0, "" }, |
| 2094 | { "th1-setup", 0, 40, 0, "" }, |
| 2095 | #ifdef FOSSIL_ENABLE_TCL |
| 2096 | { "tcl", 0, 0, 0, "off" }, |
| 2097 | { "tcl-setup", 0, 40, 0, "" }, |
| 2098 | #endif |
| 2099 | { "unicode-glob", 0, 40, 1, "" }, |
| 2100 | { "web-browser", 0, 32, 0, "" }, |
| 2101 | { "white-foreground", 0, 0, 0, "off" }, |
| 2102 | { 0,0,0,0,0 } |
| 2103 | }; |
| 2104 | |
| @@ -2175,17 +2173,23 @@ | |
| 2175 | ** diff-command External command to run when performing a diff. |
| 2176 | ** If undefined, the internal text diff will be used. |
| 2177 | ** |
| 2178 | ** dont-push Prevent this repository from pushing from client to |
| 2179 | ** server. Useful when setting up a private branch. |
| 2180 | ** |
| 2181 | ** empty-dirs A comma or newline-separated list of pathnames. On |
| 2182 | ** (versionable) update and checkout commands, if no file or directory |
| 2183 | ** exists with that name, an empty directory will be |
| 2184 | ** created. |
| 2185 | ** |
| 2186 | ** editor Text editor command used for check-in comments. |
| 2187 | ** |
| 2188 | ** gdiff-command External command to run when performing a graphical |
| 2189 | ** diff. If undefined, text diff will be used. |
| 2190 | ** |
| 2191 | ** gmerge-command A graphical merge conflict resolver command operating |
| @@ -2244,10 +2248,13 @@ | |
| 2244 | ** |
| 2245 | ** self-register Allow users to register themselves through the HTTP UI. |
| 2246 | ** This is useful if you want to see other names than |
| 2247 | ** "Anonymous" in e.g. ticketing system. On the other hand |
| 2248 | ** users can not be deleted. Default: off. |
| 2249 | ** |
| 2250 | ** ssl-ca-location The full pathname to a file containing PEM encoded |
| 2251 | ** CA root certificates, or a directory of certificates |
| 2252 | ** with filenames formed from the certificate hashes as |
| 2253 | ** required by OpenSSL. |
| @@ -2263,13 +2270,10 @@ | |
| 2263 | ** the certificate and private key files. |
| 2264 | ** This identity will be presented to SSL servers to |
| 2265 | ** authenticate this client, in addition to the normal |
| 2266 | ** password authentication. |
| 2267 | ** |
| 2268 | ** ssh-command Command used to talk to a remote machine with |
| 2269 | ** the "ssh://" protocol. |
| 2270 | ** |
| 2271 | ** tcl If enabled (and Fossil was compiled with Tcl support), |
| 2272 | ** Tcl integration commands will be added to the TH1 |
| 2273 | ** interpreter, allowing arbitrary Tcl expressions and |
| 2274 | ** scripts to be evaluated from TH1. Additionally, the Tcl |
| 2275 | ** interpreter will be able to evaluate arbitrary TH1 |
| @@ -2281,15 +2285,10 @@ | |
| 2281 | ** |
| 2282 | ** th1-setup This is the setup script to be evaluated after creating |
| 2283 | ** and initializing the TH1 interpreter. By default, this |
| 2284 | ** is empty and no extra setup is performed. |
| 2285 | ** |
| 2286 | ** unicode-glob The VALUE is a comma or newline-separated list of GLOB |
| 2287 | ** (versionable) patterns specifying files that the "commit" command will |
| 2288 | ** ignore when issuing warnings about text files that may |
| 2289 | ** contain Unicode. Set to "*" to disable Unicode checking. |
| 2290 | ** |
| 2291 | ** web-browser A shell command used to launch your preferred |
| 2292 | ** web browser when given a URL as an argument. |
| 2293 | ** Defaults to "start" on windows, "open" on Mac, |
| 2294 | ** and "firefox" on Unix. |
| 2295 | ** |
| 2296 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -22,11 +22,11 @@ | |
| 22 | ** |
| 23 | ** (1) The "user" database in ~/.fossil |
| 24 | ** |
| 25 | ** (2) The "repository" database |
| 26 | ** |
| 27 | ** (3) A local checkout database named "_FOSSIL_" or ".fslckout" |
| 28 | ** and located at the root of the local copy of the source tree. |
| 29 | ** |
| 30 | */ |
| 31 | #include "config.h" |
| 32 | #if ! defined(_WIN32) |
| @@ -89,11 +89,11 @@ | |
| 89 | cgi_reply(); |
| 90 | } |
| 91 | else if( g.cgiOutput ){ |
| 92 | g.cgiOutput = 0; |
| 93 | cgi_printf("<h1>Database Error</h1>\n" |
| 94 | "<pre>%h</pre>\n<p>%s</p>\n", z, zRebuildMsg); |
| 95 | cgi_reply(); |
| 96 | }else{ |
| 97 | fprintf(stderr, "%s: %s\n\n%s", g.argv[0], z, zRebuildMsg); |
| 98 | } |
| 99 | free(z); |
| @@ -484,20 +484,30 @@ | |
| 484 | /* |
| 485 | ** Execute multiple SQL statements. |
| 486 | */ |
| 487 | int db_multi_exec(const char *zSql, ...){ |
| 488 | Blob sql; |
| 489 | int rc = SQLITE_OK; |
| 490 | va_list ap; |
| 491 | const char *z, *zEnd; |
| 492 | sqlite3_stmt *pStmt; |
| 493 | blob_init(&sql, 0, 0); |
| 494 | va_start(ap, zSql); |
| 495 | blob_vappendf(&sql, zSql, ap); |
| 496 | va_end(ap); |
| 497 | z = blob_str(&sql); |
| 498 | while( rc==SQLITE_OK && z[0] ){ |
| 499 | pStmt = 0; |
| 500 | rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd); |
| 501 | if( rc!=SQLITE_OK ) break; |
| 502 | if( pStmt ){ |
| 503 | db.nPrepare++; |
| 504 | while( sqlite3_step(pStmt)==SQLITE_ROW ){} |
| 505 | rc = sqlite3_finalize(pStmt); |
| 506 | if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z); |
| 507 | } |
| 508 | z = zEnd; |
| 509 | } |
| 510 | blob_reset(&sql); |
| 511 | return rc; |
| 512 | } |
| 513 | |
| @@ -642,15 +652,11 @@ | |
| 652 | sqlite3 *db; |
| 653 | int rc; |
| 654 | const char *zSql; |
| 655 | va_list ap; |
| 656 | |
| 657 | db = db_open(zFileName); |
| 658 | sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0); |
| 659 | rc = sqlite3_exec(db, zSchema, 0, 0, 0); |
| 660 | if( rc!=SQLITE_OK ){ |
| 661 | db_err(sqlite3_errmsg(db)); |
| 662 | } |
| @@ -697,29 +703,43 @@ | |
| 703 | |
| 704 | /* |
| 705 | ** Open a database file. Return a pointer to the new database |
| 706 | ** connection. An error results in process abort. |
| 707 | */ |
| 708 | LOCAL sqlite3 *db_open(const char *zDbName){ |
| 709 | int rc; |
| 710 | const char *zVfs; |
| 711 | sqlite3 *db; |
| 712 | |
| 713 | if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName); |
| 714 | zVfs = fossil_getenv("FOSSIL_VFS"); |
| 715 | rc = sqlite3_open_v2( |
| 716 | zDbName, &db, |
| 717 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 718 | zVfs |
| 719 | ); |
| 720 | if( rc!=SQLITE_OK ){ |
| 721 | db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); |
| 722 | } |
| 723 | sqlite3_busy_timeout(db, 5000); |
| 724 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 725 | sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0); |
| 726 | sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_ANY, 0, |
| 727 | db_checkin_mtime_function, 0, 0); |
| 728 | sqlite3_create_function(db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0); |
| 729 | sqlite3_create_function(db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0); |
| 730 | sqlite3_create_function(db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0); |
| 731 | sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); |
| 732 | sqlite3_create_function( |
| 733 | db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 734 | ); |
| 735 | sqlite3_create_function( |
| 736 | db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 737 | ); |
| 738 | if( g.fSqlTrace ) sqlite3_trace(db, db_sql_trace, 0); |
| 739 | re_add_sql_func(db); |
| 740 | sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); |
| 741 | return db; |
| 742 | } |
| 743 | |
| 744 | |
| 745 | /* |
| @@ -747,13 +767,12 @@ | |
| 767 | const char *zLabel, |
| 768 | int *pWasAttached |
| 769 | ){ |
| 770 | if( !g.db ){ |
| 771 | assert( g.zMainDbType==0 ); |
| 772 | g.db = db_open(zDbName); |
| 773 | g.zMainDbType = zLabel; |
| 774 | if ( pWasAttached ) *pWasAttached = 0; |
| 775 | }else{ |
| 776 | assert( g.zMainDbType!=0 ); |
| 777 | db_attach(zDbName, zLabel); |
| 778 | if ( pWasAttached ) *pWasAttached = 1; |
| @@ -820,11 +839,11 @@ | |
| 839 | db_open_or_attach(zDbName, "configdb", &g.useAttach); |
| 840 | g.dbConfig = 0; |
| 841 | g.zConfigDbType = 0; |
| 842 | }else{ |
| 843 | g.useAttach = 0; |
| 844 | g.dbConfig = db_open(zDbName); |
| 845 | g.zConfigDbType = "configdb"; |
| 846 | } |
| 847 | g.configOpen = 1; |
| 848 | free(zDbName); |
| 849 | } |
| @@ -1221,10 +1240,13 @@ | |
| 1240 | void db_create_default_users(int setupUserOnly, const char *zDefaultUser){ |
| 1241 | const char *zUser = zDefaultUser; |
| 1242 | if( zUser==0 ){ |
| 1243 | zUser = db_get("default-user", 0); |
| 1244 | } |
| 1245 | if( zUser==0 ){ |
| 1246 | zUser = fossil_getenv("FOSSIL_USER"); |
| 1247 | } |
| 1248 | if( zUser==0 ){ |
| 1249 | #if defined(_WIN32) |
| 1250 | zUser = fossil_getenv("USERNAME"); |
| 1251 | #else |
| 1252 | zUser = fossil_getenv("USER"); |
| @@ -1441,11 +1463,11 @@ | |
| 1463 | ** SQL functions for debugging. |
| 1464 | ** |
| 1465 | ** The print() function writes its arguments on stdout, but only |
| 1466 | ** if the -sqlprint command-line option is turned on. |
| 1467 | */ |
| 1468 | LOCAL void db_sql_print( |
| 1469 | sqlite3_context *context, |
| 1470 | int argc, |
| 1471 | sqlite3_value **argv |
| 1472 | ){ |
| 1473 | int i; |
| @@ -1454,22 +1476,20 @@ | |
| 1476 | char c = i==argc-1 ? '\n' : ' '; |
| 1477 | fossil_print("%s%c", sqlite3_value_text(argv[i]), c); |
| 1478 | } |
| 1479 | } |
| 1480 | } |
| 1481 | LOCAL void db_sql_trace(void *notUsed, const char *zSql){ |
| 1482 | int n = strlen(zSql); |
| 1483 | fossil_trace("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";"); |
| 1484 | } |
| 1485 | |
| 1486 | /* |
| 1487 | ** Implement the user() SQL function. user() takes no arguments and |
| 1488 | ** returns the user ID of the current user. |
| 1489 | */ |
| 1490 | LOCAL void db_sql_user( |
| 1491 | sqlite3_context *context, |
| 1492 | int argc, |
| 1493 | sqlite3_value **argv |
| 1494 | ){ |
| 1495 | if( g.zLogin!=0 ){ |
| @@ -1481,11 +1501,11 @@ | |
| 1501 | ** Implement the cgi() SQL function. cgi() takes an argument which is |
| 1502 | ** a name of CGI query parameter. The value of that parameter is returned, |
| 1503 | ** if available. Optional second argument will be returned if the first |
| 1504 | ** doesn't exist as a CGI parameter. |
| 1505 | */ |
| 1506 | LOCAL void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 1507 | const char* zP; |
| 1508 | if( argc!=1 && argc!=2 ) return; |
| 1509 | zP = P((const char*)sqlite3_value_text(argv[0])); |
| 1510 | if( zP ){ |
| 1511 | sqlite3_result_text(context, zP, -1, SQLITE_STATIC); |
| @@ -1512,11 +1532,11 @@ | |
| 1532 | ** (meaning that id was named on the command-line). |
| 1533 | ** |
| 1534 | ** In the second form (3 arguments) return argument X if true and Y |
| 1535 | ** if false. Except if Y is NULL then always return X. |
| 1536 | */ |
| 1537 | LOCAL void file_is_selected( |
| 1538 | sqlite3_context *context, |
| 1539 | int argc, |
| 1540 | sqlite3_value **argv |
| 1541 | ){ |
| 1542 | int rc = 0; |
| @@ -1600,32 +1620,10 @@ | |
| 1620 | zOut = mprintf("%s", zKey); |
| 1621 | } |
| 1622 | return zOut; |
| 1623 | } |
| 1624 | |
| 1625 | /* |
| 1626 | ** Return true if the string zVal represents "true" (or "false"). |
| 1627 | */ |
| 1628 | int is_truth(const char *zVal){ |
| 1629 | static const char *const azOn[] = { "on", "yes", "true", "1" }; |
| @@ -2066,17 +2064,18 @@ | |
| 2064 | { "default-perms", 0, 16, 0, "u" }, |
| 2065 | { "diff-binary", 0, 0, 0, "on" }, |
| 2066 | { "diff-command", 0, 40, 0, "" }, |
| 2067 | { "dont-push", 0, 0, 0, "off" }, |
| 2068 | { "editor", 0, 32, 0, "" }, |
| 2069 | { "empty-dirs", 0, 40, 1, "" }, |
| 2070 | { "encoding-glob", 0, 40, 1, "" }, |
| 2071 | { "gdiff-command", 0, 40, 0, "gdiff" }, |
| 2072 | { "gmerge-command",0, 40, 0, "" }, |
| 2073 | { "http-port", 0, 16, 0, "8080" }, |
| 2074 | { "https-login", 0, 0, 0, "off" }, |
| 2075 | { "href-targets", 0, 0, 0, "on" }, |
| 2076 | { "ignore-glob", 0, 40, 1, "" }, |
| 2077 | { "localauth", 0, 0, 0, "off" }, |
| 2078 | { "main-branch", 0, 40, 0, "trunk" }, |
| 2079 | { "manifest", 0, 0, 1, "off" }, |
| 2080 | #ifdef FOSSIL_ENABLE_MARKDOWN |
| 2081 | { "markdown", 0, 0, 0, "off" }, |
| @@ -2086,19 +2085,18 @@ | |
| 2085 | { "pgp-command", 0, 40, 0, "gpg --clearsign -o " }, |
| 2086 | { "proxy", 0, 32, 0, "off" }, |
| 2087 | { "relative-paths",0, 0, 0, "on" }, |
| 2088 | { "repo-cksum", 0, 0, 0, "on" }, |
| 2089 | { "self-register", 0, 0, 0, "off" }, |
| 2090 | { "ssh-command", 0, 40, 0, "" }, |
| 2091 | { "ssl-ca-location",0, 40, 0, "" }, |
| 2092 | { "ssl-identity", 0, 40, 0, "" }, |
| 2093 | #ifdef FOSSIL_ENABLE_TCL |
| 2094 | { "tcl", 0, 0, 0, "off" }, |
| 2095 | { "tcl-setup", 0, 40, 0, "" }, |
| 2096 | #endif |
| 2097 | { "th1-setup", 0, 40, 0, "" }, |
| 2098 | { "web-browser", 0, 32, 0, "" }, |
| 2099 | { "white-foreground", 0, 0, 0, "off" }, |
| 2100 | { 0,0,0,0,0 } |
| 2101 | }; |
| 2102 | |
| @@ -2175,17 +2173,23 @@ | |
| 2173 | ** diff-command External command to run when performing a diff. |
| 2174 | ** If undefined, the internal text diff will be used. |
| 2175 | ** |
| 2176 | ** dont-push Prevent this repository from pushing from client to |
| 2177 | ** server. Useful when setting up a private branch. |
| 2178 | ** |
| 2179 | ** editor Text editor command used for check-in comments. |
| 2180 | ** |
| 2181 | ** empty-dirs A comma or newline-separated list of pathnames. On |
| 2182 | ** (versionable) update and checkout commands, if no file or directory |
| 2183 | ** exists with that name, an empty directory will be |
| 2184 | ** created. |
| 2185 | ** |
| 2186 | ** encoding-glob The VALUE is a comma or newline-separated list of GLOB |
| 2187 | ** (versionable) patterns specifying files that the "commit" command will |
| 2188 | ** ignore when issuing warnings about text files that may |
| 2189 | ** use another encoding than ASCII or UTF-8. Set to "*" |
| 2190 | ** to disable encoding checking. |
| 2191 | ** |
| 2192 | ** gdiff-command External command to run when performing a graphical |
| 2193 | ** diff. If undefined, text diff will be used. |
| 2194 | ** |
| 2195 | ** gmerge-command A graphical merge conflict resolver command operating |
| @@ -2244,10 +2248,13 @@ | |
| 2248 | ** |
| 2249 | ** self-register Allow users to register themselves through the HTTP UI. |
| 2250 | ** This is useful if you want to see other names than |
| 2251 | ** "Anonymous" in e.g. ticketing system. On the other hand |
| 2252 | ** users can not be deleted. Default: off. |
| 2253 | ** |
| 2254 | ** ssh-command Command used to talk to a remote machine with |
| 2255 | ** the "ssh://" protocol. |
| 2256 | ** |
| 2257 | ** ssl-ca-location The full pathname to a file containing PEM encoded |
| 2258 | ** CA root certificates, or a directory of certificates |
| 2259 | ** with filenames formed from the certificate hashes as |
| 2260 | ** required by OpenSSL. |
| @@ -2263,13 +2270,10 @@ | |
| 2270 | ** the certificate and private key files. |
| 2271 | ** This identity will be presented to SSL servers to |
| 2272 | ** authenticate this client, in addition to the normal |
| 2273 | ** password authentication. |
| 2274 | ** |
| 2275 | ** tcl If enabled (and Fossil was compiled with Tcl support), |
| 2276 | ** Tcl integration commands will be added to the TH1 |
| 2277 | ** interpreter, allowing arbitrary Tcl expressions and |
| 2278 | ** scripts to be evaluated from TH1. Additionally, the Tcl |
| 2279 | ** interpreter will be able to evaluate arbitrary TH1 |
| @@ -2281,15 +2285,10 @@ | |
| 2285 | ** |
| 2286 | ** th1-setup This is the setup script to be evaluated after creating |
| 2287 | ** and initializing the TH1 interpreter. By default, this |
| 2288 | ** is empty and no extra setup is performed. |
| 2289 | ** |
| 2290 | ** web-browser A shell command used to launch your preferred |
| 2291 | ** web browser when given a URL as an argument. |
| 2292 | ** Defaults to "start" on windows, "open" on Mac, |
| 2293 | ** and "firefox" on Unix. |
| 2294 | ** |
| 2295 |
+33
-2
| --- src/descendants.c | ||
| +++ src/descendants.c | ||
| @@ -348,10 +348,11 @@ | ||
| 348 | 348 | ** repository database to be recomputed. |
| 349 | 349 | ** |
| 350 | 350 | ** Options: |
| 351 | 351 | ** --all show ALL leaves |
| 352 | 352 | ** --closed show only closed leaves |
| 353 | +** --bybranch order output by branch name | |
| 353 | 354 | ** --recompute recompute the "leaf" table in the repository DB |
| 354 | 355 | ** |
| 355 | 356 | ** See also: descendants, finfo, info, branch |
| 356 | 357 | */ |
| 357 | 358 | void leaves_cmd(void){ |
| @@ -358,10 +359,14 @@ | ||
| 358 | 359 | Stmt q; |
| 359 | 360 | Blob sql; |
| 360 | 361 | int showAll = find_option("all", 0, 0)!=0; |
| 361 | 362 | int showClosed = find_option("closed", 0, 0)!=0; |
| 362 | 363 | int recomputeFlag = find_option("recompute",0,0)!=0; |
| 364 | + int byBranch = find_option("bybranch",0,0)!=0; | |
| 365 | + char *zLastBr = 0; | |
| 366 | + int n; | |
| 367 | + char zLineNo[10]; | |
| 363 | 368 | |
| 364 | 369 | db_find_and_open_repository(0,0); |
| 365 | 370 | if( recomputeFlag ) leaf_rebuild(); |
| 366 | 371 | blob_zero(&sql); |
| 367 | 372 | blob_append(&sql, timeline_query_for_tty(), -1); |
| @@ -369,13 +374,39 @@ | ||
| 369 | 374 | if( showClosed ){ |
| 370 | 375 | blob_appendf(&sql," AND %z", leaf_is_closed_sql("blob.rid")); |
| 371 | 376 | }else if( !showAll ){ |
| 372 | 377 | blob_appendf(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid")); |
| 373 | 378 | } |
| 374 | - db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_str(&sql)); | |
| 379 | + if( byBranch ){ | |
| 380 | + db_prepare(&q, "%s ORDER BY nullif(branch,'trunk') COLLATE nocase," | |
| 381 | + " event.mtime DESC", | |
| 382 | + blob_str(&sql)); | |
| 383 | + }else{ | |
| 384 | + db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_str(&sql)); | |
| 385 | + } | |
| 375 | 386 | blob_reset(&sql); |
| 376 | - print_timeline(&q, 2000, 0); | |
| 387 | + n = 0; | |
| 388 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 389 | + const char *zId = db_column_text(&q, 1); | |
| 390 | + const char *zDate = db_column_text(&q, 2); | |
| 391 | + const char *zCom = db_column_text(&q, 3); | |
| 392 | + const char *zBr = db_column_text(&q, 7); | |
| 393 | + char *z; | |
| 394 | + | |
| 395 | + if( byBranch && fossil_strcmp(zBr, zLastBr)!=0 ){ | |
| 396 | + fossil_print("*** %s ***\n", zBr); | |
| 397 | + fossil_free(zLastBr); | |
| 398 | + zLastBr = fossil_strdup(zBr); | |
| 399 | + } | |
| 400 | + n++; | |
| 401 | + sqlite3_snprintf(sizeof(zLineNo), zLineNo, "(%d)", n); | |
| 402 | + fossil_print("%6s ", zLineNo); | |
| 403 | + z = mprintf("%s [%.10s] %s", zDate, zId, zCom); | |
| 404 | + comment_print(z, 7, 79); | |
| 405 | + fossil_free(z); | |
| 406 | + } | |
| 407 | + fossil_free(zLastBr); | |
| 377 | 408 | db_finalize(&q); |
| 378 | 409 | } |
| 379 | 410 | |
| 380 | 411 | /* |
| 381 | 412 | ** WEBPAGE: leaves |
| 382 | 413 |
| --- src/descendants.c | |
| +++ src/descendants.c | |
| @@ -348,10 +348,11 @@ | |
| 348 | ** repository database to be recomputed. |
| 349 | ** |
| 350 | ** Options: |
| 351 | ** --all show ALL leaves |
| 352 | ** --closed show only closed leaves |
| 353 | ** --recompute recompute the "leaf" table in the repository DB |
| 354 | ** |
| 355 | ** See also: descendants, finfo, info, branch |
| 356 | */ |
| 357 | void leaves_cmd(void){ |
| @@ -358,10 +359,14 @@ | |
| 358 | Stmt q; |
| 359 | Blob sql; |
| 360 | int showAll = find_option("all", 0, 0)!=0; |
| 361 | int showClosed = find_option("closed", 0, 0)!=0; |
| 362 | int recomputeFlag = find_option("recompute",0,0)!=0; |
| 363 | |
| 364 | db_find_and_open_repository(0,0); |
| 365 | if( recomputeFlag ) leaf_rebuild(); |
| 366 | blob_zero(&sql); |
| 367 | blob_append(&sql, timeline_query_for_tty(), -1); |
| @@ -369,13 +374,39 @@ | |
| 369 | if( showClosed ){ |
| 370 | blob_appendf(&sql," AND %z", leaf_is_closed_sql("blob.rid")); |
| 371 | }else if( !showAll ){ |
| 372 | blob_appendf(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid")); |
| 373 | } |
| 374 | db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_str(&sql)); |
| 375 | blob_reset(&sql); |
| 376 | print_timeline(&q, 2000, 0); |
| 377 | db_finalize(&q); |
| 378 | } |
| 379 | |
| 380 | /* |
| 381 | ** WEBPAGE: leaves |
| 382 |
| --- src/descendants.c | |
| +++ src/descendants.c | |
| @@ -348,10 +348,11 @@ | |
| 348 | ** repository database to be recomputed. |
| 349 | ** |
| 350 | ** Options: |
| 351 | ** --all show ALL leaves |
| 352 | ** --closed show only closed leaves |
| 353 | ** --bybranch order output by branch name |
| 354 | ** --recompute recompute the "leaf" table in the repository DB |
| 355 | ** |
| 356 | ** See also: descendants, finfo, info, branch |
| 357 | */ |
| 358 | void leaves_cmd(void){ |
| @@ -358,10 +359,14 @@ | |
| 359 | Stmt q; |
| 360 | Blob sql; |
| 361 | int showAll = find_option("all", 0, 0)!=0; |
| 362 | int showClosed = find_option("closed", 0, 0)!=0; |
| 363 | int recomputeFlag = find_option("recompute",0,0)!=0; |
| 364 | int byBranch = find_option("bybranch",0,0)!=0; |
| 365 | char *zLastBr = 0; |
| 366 | int n; |
| 367 | char zLineNo[10]; |
| 368 | |
| 369 | db_find_and_open_repository(0,0); |
| 370 | if( recomputeFlag ) leaf_rebuild(); |
| 371 | blob_zero(&sql); |
| 372 | blob_append(&sql, timeline_query_for_tty(), -1); |
| @@ -369,13 +374,39 @@ | |
| 374 | if( showClosed ){ |
| 375 | blob_appendf(&sql," AND %z", leaf_is_closed_sql("blob.rid")); |
| 376 | }else if( !showAll ){ |
| 377 | blob_appendf(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid")); |
| 378 | } |
| 379 | if( byBranch ){ |
| 380 | db_prepare(&q, "%s ORDER BY nullif(branch,'trunk') COLLATE nocase," |
| 381 | " event.mtime DESC", |
| 382 | blob_str(&sql)); |
| 383 | }else{ |
| 384 | db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_str(&sql)); |
| 385 | } |
| 386 | blob_reset(&sql); |
| 387 | n = 0; |
| 388 | while( db_step(&q)==SQLITE_ROW ){ |
| 389 | const char *zId = db_column_text(&q, 1); |
| 390 | const char *zDate = db_column_text(&q, 2); |
| 391 | const char *zCom = db_column_text(&q, 3); |
| 392 | const char *zBr = db_column_text(&q, 7); |
| 393 | char *z; |
| 394 | |
| 395 | if( byBranch && fossil_strcmp(zBr, zLastBr)!=0 ){ |
| 396 | fossil_print("*** %s ***\n", zBr); |
| 397 | fossil_free(zLastBr); |
| 398 | zLastBr = fossil_strdup(zBr); |
| 399 | } |
| 400 | n++; |
| 401 | sqlite3_snprintf(sizeof(zLineNo), zLineNo, "(%d)", n); |
| 402 | fossil_print("%6s ", zLineNo); |
| 403 | z = mprintf("%s [%.10s] %s", zDate, zId, zCom); |
| 404 | comment_print(z, 7, 79); |
| 405 | fossil_free(z); |
| 406 | } |
| 407 | fossil_free(zLastBr); |
| 408 | db_finalize(&q); |
| 409 | } |
| 410 | |
| 411 | /* |
| 412 | ** WEBPAGE: leaves |
| 413 |
+33
-2
| --- src/descendants.c | ||
| +++ src/descendants.c | ||
| @@ -348,10 +348,11 @@ | ||
| 348 | 348 | ** repository database to be recomputed. |
| 349 | 349 | ** |
| 350 | 350 | ** Options: |
| 351 | 351 | ** --all show ALL leaves |
| 352 | 352 | ** --closed show only closed leaves |
| 353 | +** --bybranch order output by branch name | |
| 353 | 354 | ** --recompute recompute the "leaf" table in the repository DB |
| 354 | 355 | ** |
| 355 | 356 | ** See also: descendants, finfo, info, branch |
| 356 | 357 | */ |
| 357 | 358 | void leaves_cmd(void){ |
| @@ -358,10 +359,14 @@ | ||
| 358 | 359 | Stmt q; |
| 359 | 360 | Blob sql; |
| 360 | 361 | int showAll = find_option("all", 0, 0)!=0; |
| 361 | 362 | int showClosed = find_option("closed", 0, 0)!=0; |
| 362 | 363 | int recomputeFlag = find_option("recompute",0,0)!=0; |
| 364 | + int byBranch = find_option("bybranch",0,0)!=0; | |
| 365 | + char *zLastBr = 0; | |
| 366 | + int n; | |
| 367 | + char zLineNo[10]; | |
| 363 | 368 | |
| 364 | 369 | db_find_and_open_repository(0,0); |
| 365 | 370 | if( recomputeFlag ) leaf_rebuild(); |
| 366 | 371 | blob_zero(&sql); |
| 367 | 372 | blob_append(&sql, timeline_query_for_tty(), -1); |
| @@ -369,13 +374,39 @@ | ||
| 369 | 374 | if( showClosed ){ |
| 370 | 375 | blob_appendf(&sql," AND %z", leaf_is_closed_sql("blob.rid")); |
| 371 | 376 | }else if( !showAll ){ |
| 372 | 377 | blob_appendf(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid")); |
| 373 | 378 | } |
| 374 | - db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_str(&sql)); | |
| 379 | + if( byBranch ){ | |
| 380 | + db_prepare(&q, "%s ORDER BY nullif(branch,'trunk') COLLATE nocase," | |
| 381 | + " event.mtime DESC", | |
| 382 | + blob_str(&sql)); | |
| 383 | + }else{ | |
| 384 | + db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_str(&sql)); | |
| 385 | + } | |
| 375 | 386 | blob_reset(&sql); |
| 376 | - print_timeline(&q, 2000, 0); | |
| 387 | + n = 0; | |
| 388 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 389 | + const char *zId = db_column_text(&q, 1); | |
| 390 | + const char *zDate = db_column_text(&q, 2); | |
| 391 | + const char *zCom = db_column_text(&q, 3); | |
| 392 | + const char *zBr = db_column_text(&q, 7); | |
| 393 | + char *z; | |
| 394 | + | |
| 395 | + if( byBranch && fossil_strcmp(zBr, zLastBr)!=0 ){ | |
| 396 | + fossil_print("*** %s ***\n", zBr); | |
| 397 | + fossil_free(zLastBr); | |
| 398 | + zLastBr = fossil_strdup(zBr); | |
| 399 | + } | |
| 400 | + n++; | |
| 401 | + sqlite3_snprintf(sizeof(zLineNo), zLineNo, "(%d)", n); | |
| 402 | + fossil_print("%6s ", zLineNo); | |
| 403 | + z = mprintf("%s [%.10s] %s", zDate, zId, zCom); | |
| 404 | + comment_print(z, 7, 79); | |
| 405 | + fossil_free(z); | |
| 406 | + } | |
| 407 | + fossil_free(zLastBr); | |
| 377 | 408 | db_finalize(&q); |
| 378 | 409 | } |
| 379 | 410 | |
| 380 | 411 | /* |
| 381 | 412 | ** WEBPAGE: leaves |
| 382 | 413 |
| --- src/descendants.c | |
| +++ src/descendants.c | |
| @@ -348,10 +348,11 @@ | |
| 348 | ** repository database to be recomputed. |
| 349 | ** |
| 350 | ** Options: |
| 351 | ** --all show ALL leaves |
| 352 | ** --closed show only closed leaves |
| 353 | ** --recompute recompute the "leaf" table in the repository DB |
| 354 | ** |
| 355 | ** See also: descendants, finfo, info, branch |
| 356 | */ |
| 357 | void leaves_cmd(void){ |
| @@ -358,10 +359,14 @@ | |
| 358 | Stmt q; |
| 359 | Blob sql; |
| 360 | int showAll = find_option("all", 0, 0)!=0; |
| 361 | int showClosed = find_option("closed", 0, 0)!=0; |
| 362 | int recomputeFlag = find_option("recompute",0,0)!=0; |
| 363 | |
| 364 | db_find_and_open_repository(0,0); |
| 365 | if( recomputeFlag ) leaf_rebuild(); |
| 366 | blob_zero(&sql); |
| 367 | blob_append(&sql, timeline_query_for_tty(), -1); |
| @@ -369,13 +374,39 @@ | |
| 369 | if( showClosed ){ |
| 370 | blob_appendf(&sql," AND %z", leaf_is_closed_sql("blob.rid")); |
| 371 | }else if( !showAll ){ |
| 372 | blob_appendf(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid")); |
| 373 | } |
| 374 | db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_str(&sql)); |
| 375 | blob_reset(&sql); |
| 376 | print_timeline(&q, 2000, 0); |
| 377 | db_finalize(&q); |
| 378 | } |
| 379 | |
| 380 | /* |
| 381 | ** WEBPAGE: leaves |
| 382 |
| --- src/descendants.c | |
| +++ src/descendants.c | |
| @@ -348,10 +348,11 @@ | |
| 348 | ** repository database to be recomputed. |
| 349 | ** |
| 350 | ** Options: |
| 351 | ** --all show ALL leaves |
| 352 | ** --closed show only closed leaves |
| 353 | ** --bybranch order output by branch name |
| 354 | ** --recompute recompute the "leaf" table in the repository DB |
| 355 | ** |
| 356 | ** See also: descendants, finfo, info, branch |
| 357 | */ |
| 358 | void leaves_cmd(void){ |
| @@ -358,10 +359,14 @@ | |
| 359 | Stmt q; |
| 360 | Blob sql; |
| 361 | int showAll = find_option("all", 0, 0)!=0; |
| 362 | int showClosed = find_option("closed", 0, 0)!=0; |
| 363 | int recomputeFlag = find_option("recompute",0,0)!=0; |
| 364 | int byBranch = find_option("bybranch",0,0)!=0; |
| 365 | char *zLastBr = 0; |
| 366 | int n; |
| 367 | char zLineNo[10]; |
| 368 | |
| 369 | db_find_and_open_repository(0,0); |
| 370 | if( recomputeFlag ) leaf_rebuild(); |
| 371 | blob_zero(&sql); |
| 372 | blob_append(&sql, timeline_query_for_tty(), -1); |
| @@ -369,13 +374,39 @@ | |
| 374 | if( showClosed ){ |
| 375 | blob_appendf(&sql," AND %z", leaf_is_closed_sql("blob.rid")); |
| 376 | }else if( !showAll ){ |
| 377 | blob_appendf(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid")); |
| 378 | } |
| 379 | if( byBranch ){ |
| 380 | db_prepare(&q, "%s ORDER BY nullif(branch,'trunk') COLLATE nocase," |
| 381 | " event.mtime DESC", |
| 382 | blob_str(&sql)); |
| 383 | }else{ |
| 384 | db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_str(&sql)); |
| 385 | } |
| 386 | blob_reset(&sql); |
| 387 | n = 0; |
| 388 | while( db_step(&q)==SQLITE_ROW ){ |
| 389 | const char *zId = db_column_text(&q, 1); |
| 390 | const char *zDate = db_column_text(&q, 2); |
| 391 | const char *zCom = db_column_text(&q, 3); |
| 392 | const char *zBr = db_column_text(&q, 7); |
| 393 | char *z; |
| 394 | |
| 395 | if( byBranch && fossil_strcmp(zBr, zLastBr)!=0 ){ |
| 396 | fossil_print("*** %s ***\n", zBr); |
| 397 | fossil_free(zLastBr); |
| 398 | zLastBr = fossil_strdup(zBr); |
| 399 | } |
| 400 | n++; |
| 401 | sqlite3_snprintf(sizeof(zLineNo), zLineNo, "(%d)", n); |
| 402 | fossil_print("%6s ", zLineNo); |
| 403 | z = mprintf("%s [%.10s] %s", zDate, zId, zCom); |
| 404 | comment_print(z, 7, 79); |
| 405 | fossil_free(z); |
| 406 | } |
| 407 | fossil_free(zLastBr); |
| 408 | db_finalize(&q); |
| 409 | } |
| 410 | |
| 411 | /* |
| 412 | ** WEBPAGE: leaves |
| 413 |
+10
-47
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -360,53 +360,19 @@ | ||
| 360 | 360 | ** This function returns non-zero if the blob starts with a UTF-16le or |
| 361 | 361 | ** UTF-16be byte-order-mark (BOM). |
| 362 | 362 | */ |
| 363 | 363 | int starts_with_utf16_bom(const Blob *pContent, int *pnByte){ |
| 364 | 364 | const char *z = blob_buffer(pContent); |
| 365 | - int c1, c2; | |
| 366 | - | |
| 367 | - if( pnByte ) *pnByte = 2; | |
| 368 | - if( blob_size(pContent)<2 ) return 0; | |
| 369 | - c1 = z[0]; c2 = z[1]; | |
| 370 | - if( (c1==(char)0xff) && (c2==(char)0xfe) ){ | |
| 371 | - return 1; | |
| 372 | - }else if( (c1==(char)0xfe) && (c2==(char)0xff) ){ | |
| 373 | - return 1; | |
| 374 | - } | |
| 375 | - return 0; | |
| 376 | -} | |
| 377 | - | |
| 378 | -/* | |
| 379 | -** This function returns non-zero if the blob starts with a UTF-16le | |
| 380 | -** byte-order-mark (BOM). | |
| 381 | -*/ | |
| 382 | -int starts_with_utf16le_bom(const Blob *pContent, int *pnByte){ | |
| 383 | - const char *z = blob_buffer(pContent); | |
| 384 | - int c1, c2; | |
| 385 | - | |
| 386 | - if( pnByte ) *pnByte = 2; | |
| 387 | - if( blob_size(pContent)<2 ) return 0; | |
| 388 | - c1 = z[0]; c2 = z[1]; | |
| 389 | - if( (c1==(char)0xff) && (c2==(char)0xfe) ){ | |
| 390 | - return 1; | |
| 391 | - } | |
| 392 | - return 0; | |
| 393 | -} | |
| 394 | - | |
| 395 | -/* | |
| 396 | -** This function returns non-zero if the blob starts with a UTF-16be | |
| 397 | -** byte-order-mark (BOM). | |
| 398 | -*/ | |
| 399 | -int starts_with_utf16be_bom(const Blob *pContent, int *pnByte){ | |
| 400 | - const char *z = blob_buffer(pContent); | |
| 401 | - int c1, c2; | |
| 402 | - | |
| 403 | - if( pnByte ) *pnByte = 2; | |
| 404 | - if( blob_size(pContent)<2 ) return 0; | |
| 405 | - c1 = z[0]; c2 = z[1]; | |
| 406 | - if( (c1==(char)0xfe) && (c2==(char)0xff) ){ | |
| 407 | - return 1; | |
| 365 | + int c1; | |
| 366 | + | |
| 367 | + if( pnByte ) *pnByte = 2; | |
| 368 | + if( (blob_size(pContent)<2) || (blob_size(pContent)&1)) return 0; | |
| 369 | + c1 = ((unsigned short *)z)[0]; | |
| 370 | + if( (c1==0xfeff) || (c1==0xfffe) ){ | |
| 371 | + if( blob_size(pContent) < 4 ) return 1; | |
| 372 | + c1 = ((unsigned short *)z)[1]; | |
| 373 | + if( c1 != 0 ) return 1; | |
| 408 | 374 | } |
| 409 | 375 | return 0; |
| 410 | 376 | } |
| 411 | 377 | |
| 412 | 378 | /* |
| @@ -424,11 +390,11 @@ | ||
| 424 | 390 | ReCompiled *pRe, /* The regular expression to be matched */ |
| 425 | 391 | DLine *aDLine, /* First of N DLines to compare against */ |
| 426 | 392 | int N /* Number of DLines to check */ |
| 427 | 393 | ){ |
| 428 | 394 | while( N-- ){ |
| 429 | - if( re_execute(pRe, aDLine->z, LENGTH(aDLine)) ){ | |
| 395 | + if( re_match(pRe, (const unsigned char *)aDLine->z, LENGTH(aDLine)) ){ | |
| 430 | 396 | return 1; |
| 431 | 397 | } |
| 432 | 398 | aDLine++; |
| 433 | 399 | } |
| 434 | 400 | return 0; |
| @@ -621,13 +587,11 @@ | ||
| 621 | 587 | |
| 622 | 588 | /* Show the differences */ |
| 623 | 589 | for(i=0; i<nr; i++){ |
| 624 | 590 | m = R[r+i*3+1]; |
| 625 | 591 | for(j=0; j<m; j++){ |
| 626 | - char cMark = '-'; | |
| 627 | 592 | if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html); |
| 628 | - if( pRe && re_dline_match(pRe, &A[a+j], 1)==0 ) cMark = ' '; | |
| 629 | 593 | appendDiffLine(pOut, '-', &A[a+j], html, pRe); |
| 630 | 594 | } |
| 631 | 595 | a += m; |
| 632 | 596 | m = R[r+i*3+2]; |
| 633 | 597 | for(j=0; j<m; j++){ |
| @@ -1965,11 +1929,10 @@ | ||
| 1965 | 1929 | Blob *pOut, /* Write diff here if not NULL */ |
| 1966 | 1930 | ReCompiled *pRe, /* Only output changes where this Regexp matches */ |
| 1967 | 1931 | u64 diffFlags /* DIFF_* flags defined above */ |
| 1968 | 1932 | ){ |
| 1969 | 1933 | int ignoreEolWs; /* Ignore whitespace at the end of lines */ |
| 1970 | - int nContext; /* Amount of context to display */ | |
| 1971 | 1934 | DContext c; |
| 1972 | 1935 | |
| 1973 | 1936 | if( diffFlags & DIFF_INVERT ){ |
| 1974 | 1937 | Blob *pTemp = pA_Blob; |
| 1975 | 1938 | pA_Blob = pB_Blob; |
| 1976 | 1939 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -360,53 +360,19 @@ | |
| 360 | ** This function returns non-zero if the blob starts with a UTF-16le or |
| 361 | ** UTF-16be byte-order-mark (BOM). |
| 362 | */ |
| 363 | int starts_with_utf16_bom(const Blob *pContent, int *pnByte){ |
| 364 | const char *z = blob_buffer(pContent); |
| 365 | int c1, c2; |
| 366 | |
| 367 | if( pnByte ) *pnByte = 2; |
| 368 | if( blob_size(pContent)<2 ) return 0; |
| 369 | c1 = z[0]; c2 = z[1]; |
| 370 | if( (c1==(char)0xff) && (c2==(char)0xfe) ){ |
| 371 | return 1; |
| 372 | }else if( (c1==(char)0xfe) && (c2==(char)0xff) ){ |
| 373 | return 1; |
| 374 | } |
| 375 | return 0; |
| 376 | } |
| 377 | |
| 378 | /* |
| 379 | ** This function returns non-zero if the blob starts with a UTF-16le |
| 380 | ** byte-order-mark (BOM). |
| 381 | */ |
| 382 | int starts_with_utf16le_bom(const Blob *pContent, int *pnByte){ |
| 383 | const char *z = blob_buffer(pContent); |
| 384 | int c1, c2; |
| 385 | |
| 386 | if( pnByte ) *pnByte = 2; |
| 387 | if( blob_size(pContent)<2 ) return 0; |
| 388 | c1 = z[0]; c2 = z[1]; |
| 389 | if( (c1==(char)0xff) && (c2==(char)0xfe) ){ |
| 390 | return 1; |
| 391 | } |
| 392 | return 0; |
| 393 | } |
| 394 | |
| 395 | /* |
| 396 | ** This function returns non-zero if the blob starts with a UTF-16be |
| 397 | ** byte-order-mark (BOM). |
| 398 | */ |
| 399 | int starts_with_utf16be_bom(const Blob *pContent, int *pnByte){ |
| 400 | const char *z = blob_buffer(pContent); |
| 401 | int c1, c2; |
| 402 | |
| 403 | if( pnByte ) *pnByte = 2; |
| 404 | if( blob_size(pContent)<2 ) return 0; |
| 405 | c1 = z[0]; c2 = z[1]; |
| 406 | if( (c1==(char)0xfe) && (c2==(char)0xff) ){ |
| 407 | return 1; |
| 408 | } |
| 409 | return 0; |
| 410 | } |
| 411 | |
| 412 | /* |
| @@ -424,11 +390,11 @@ | |
| 424 | ReCompiled *pRe, /* The regular expression to be matched */ |
| 425 | DLine *aDLine, /* First of N DLines to compare against */ |
| 426 | int N /* Number of DLines to check */ |
| 427 | ){ |
| 428 | while( N-- ){ |
| 429 | if( re_execute(pRe, aDLine->z, LENGTH(aDLine)) ){ |
| 430 | return 1; |
| 431 | } |
| 432 | aDLine++; |
| 433 | } |
| 434 | return 0; |
| @@ -621,13 +587,11 @@ | |
| 621 | |
| 622 | /* Show the differences */ |
| 623 | for(i=0; i<nr; i++){ |
| 624 | m = R[r+i*3+1]; |
| 625 | for(j=0; j<m; j++){ |
| 626 | char cMark = '-'; |
| 627 | if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html); |
| 628 | if( pRe && re_dline_match(pRe, &A[a+j], 1)==0 ) cMark = ' '; |
| 629 | appendDiffLine(pOut, '-', &A[a+j], html, pRe); |
| 630 | } |
| 631 | a += m; |
| 632 | m = R[r+i*3+2]; |
| 633 | for(j=0; j<m; j++){ |
| @@ -1965,11 +1929,10 @@ | |
| 1965 | Blob *pOut, /* Write diff here if not NULL */ |
| 1966 | ReCompiled *pRe, /* Only output changes where this Regexp matches */ |
| 1967 | u64 diffFlags /* DIFF_* flags defined above */ |
| 1968 | ){ |
| 1969 | int ignoreEolWs; /* Ignore whitespace at the end of lines */ |
| 1970 | int nContext; /* Amount of context to display */ |
| 1971 | DContext c; |
| 1972 | |
| 1973 | if( diffFlags & DIFF_INVERT ){ |
| 1974 | Blob *pTemp = pA_Blob; |
| 1975 | pA_Blob = pB_Blob; |
| 1976 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -360,53 +360,19 @@ | |
| 360 | ** This function returns non-zero if the blob starts with a UTF-16le or |
| 361 | ** UTF-16be byte-order-mark (BOM). |
| 362 | */ |
| 363 | int starts_with_utf16_bom(const Blob *pContent, int *pnByte){ |
| 364 | const char *z = blob_buffer(pContent); |
| 365 | int c1; |
| 366 | |
| 367 | if( pnByte ) *pnByte = 2; |
| 368 | if( (blob_size(pContent)<2) || (blob_size(pContent)&1)) return 0; |
| 369 | c1 = ((unsigned short *)z)[0]; |
| 370 | if( (c1==0xfeff) || (c1==0xfffe) ){ |
| 371 | if( blob_size(pContent) < 4 ) return 1; |
| 372 | c1 = ((unsigned short *)z)[1]; |
| 373 | if( c1 != 0 ) return 1; |
| 374 | } |
| 375 | return 0; |
| 376 | } |
| 377 | |
| 378 | /* |
| @@ -424,11 +390,11 @@ | |
| 390 | ReCompiled *pRe, /* The regular expression to be matched */ |
| 391 | DLine *aDLine, /* First of N DLines to compare against */ |
| 392 | int N /* Number of DLines to check */ |
| 393 | ){ |
| 394 | while( N-- ){ |
| 395 | if( re_match(pRe, (const unsigned char *)aDLine->z, LENGTH(aDLine)) ){ |
| 396 | return 1; |
| 397 | } |
| 398 | aDLine++; |
| 399 | } |
| 400 | return 0; |
| @@ -621,13 +587,11 @@ | |
| 587 | |
| 588 | /* Show the differences */ |
| 589 | for(i=0; i<nr; i++){ |
| 590 | m = R[r+i*3+1]; |
| 591 | for(j=0; j<m; j++){ |
| 592 | if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html); |
| 593 | appendDiffLine(pOut, '-', &A[a+j], html, pRe); |
| 594 | } |
| 595 | a += m; |
| 596 | m = R[r+i*3+2]; |
| 597 | for(j=0; j<m; j++){ |
| @@ -1965,11 +1929,10 @@ | |
| 1929 | Blob *pOut, /* Write diff here if not NULL */ |
| 1930 | ReCompiled *pRe, /* Only output changes where this Regexp matches */ |
| 1931 | u64 diffFlags /* DIFF_* flags defined above */ |
| 1932 | ){ |
| 1933 | int ignoreEolWs; /* Ignore whitespace at the end of lines */ |
| 1934 | DContext c; |
| 1935 | |
| 1936 | if( diffFlags & DIFF_INVERT ){ |
| 1937 | Blob *pTemp = pA_Blob; |
| 1938 | pA_Blob = pB_Blob; |
| 1939 |
+10
-47
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -360,53 +360,19 @@ | ||
| 360 | 360 | ** This function returns non-zero if the blob starts with a UTF-16le or |
| 361 | 361 | ** UTF-16be byte-order-mark (BOM). |
| 362 | 362 | */ |
| 363 | 363 | int starts_with_utf16_bom(const Blob *pContent, int *pnByte){ |
| 364 | 364 | const char *z = blob_buffer(pContent); |
| 365 | - int c1, c2; | |
| 366 | - | |
| 367 | - if( pnByte ) *pnByte = 2; | |
| 368 | - if( blob_size(pContent)<2 ) return 0; | |
| 369 | - c1 = z[0]; c2 = z[1]; | |
| 370 | - if( (c1==(char)0xff) && (c2==(char)0xfe) ){ | |
| 371 | - return 1; | |
| 372 | - }else if( (c1==(char)0xfe) && (c2==(char)0xff) ){ | |
| 373 | - return 1; | |
| 374 | - } | |
| 375 | - return 0; | |
| 376 | -} | |
| 377 | - | |
| 378 | -/* | |
| 379 | -** This function returns non-zero if the blob starts with a UTF-16le | |
| 380 | -** byte-order-mark (BOM). | |
| 381 | -*/ | |
| 382 | -int starts_with_utf16le_bom(const Blob *pContent, int *pnByte){ | |
| 383 | - const char *z = blob_buffer(pContent); | |
| 384 | - int c1, c2; | |
| 385 | - | |
| 386 | - if( pnByte ) *pnByte = 2; | |
| 387 | - if( blob_size(pContent)<2 ) return 0; | |
| 388 | - c1 = z[0]; c2 = z[1]; | |
| 389 | - if( (c1==(char)0xff) && (c2==(char)0xfe) ){ | |
| 390 | - return 1; | |
| 391 | - } | |
| 392 | - return 0; | |
| 393 | -} | |
| 394 | - | |
| 395 | -/* | |
| 396 | -** This function returns non-zero if the blob starts with a UTF-16be | |
| 397 | -** byte-order-mark (BOM). | |
| 398 | -*/ | |
| 399 | -int starts_with_utf16be_bom(const Blob *pContent, int *pnByte){ | |
| 400 | - const char *z = blob_buffer(pContent); | |
| 401 | - int c1, c2; | |
| 402 | - | |
| 403 | - if( pnByte ) *pnByte = 2; | |
| 404 | - if( blob_size(pContent)<2 ) return 0; | |
| 405 | - c1 = z[0]; c2 = z[1]; | |
| 406 | - if( (c1==(char)0xfe) && (c2==(char)0xff) ){ | |
| 407 | - return 1; | |
| 365 | + int c1; | |
| 366 | + | |
| 367 | + if( pnByte ) *pnByte = 2; | |
| 368 | + if( (blob_size(pContent)<2) || (blob_size(pContent)&1)) return 0; | |
| 369 | + c1 = ((unsigned short *)z)[0]; | |
| 370 | + if( (c1==0xfeff) || (c1==0xfffe) ){ | |
| 371 | + if( blob_size(pContent) < 4 ) return 1; | |
| 372 | + c1 = ((unsigned short *)z)[1]; | |
| 373 | + if( c1 != 0 ) return 1; | |
| 408 | 374 | } |
| 409 | 375 | return 0; |
| 410 | 376 | } |
| 411 | 377 | |
| 412 | 378 | /* |
| @@ -424,11 +390,11 @@ | ||
| 424 | 390 | ReCompiled *pRe, /* The regular expression to be matched */ |
| 425 | 391 | DLine *aDLine, /* First of N DLines to compare against */ |
| 426 | 392 | int N /* Number of DLines to check */ |
| 427 | 393 | ){ |
| 428 | 394 | while( N-- ){ |
| 429 | - if( re_execute(pRe, aDLine->z, LENGTH(aDLine)) ){ | |
| 395 | + if( re_match(pRe, (const unsigned char *)aDLine->z, LENGTH(aDLine)) ){ | |
| 430 | 396 | return 1; |
| 431 | 397 | } |
| 432 | 398 | aDLine++; |
| 433 | 399 | } |
| 434 | 400 | return 0; |
| @@ -621,13 +587,11 @@ | ||
| 621 | 587 | |
| 622 | 588 | /* Show the differences */ |
| 623 | 589 | for(i=0; i<nr; i++){ |
| 624 | 590 | m = R[r+i*3+1]; |
| 625 | 591 | for(j=0; j<m; j++){ |
| 626 | - char cMark = '-'; | |
| 627 | 592 | if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html); |
| 628 | - if( pRe && re_dline_match(pRe, &A[a+j], 1)==0 ) cMark = ' '; | |
| 629 | 593 | appendDiffLine(pOut, '-', &A[a+j], html, pRe); |
| 630 | 594 | } |
| 631 | 595 | a += m; |
| 632 | 596 | m = R[r+i*3+2]; |
| 633 | 597 | for(j=0; j<m; j++){ |
| @@ -1965,11 +1929,10 @@ | ||
| 1965 | 1929 | Blob *pOut, /* Write diff here if not NULL */ |
| 1966 | 1930 | ReCompiled *pRe, /* Only output changes where this Regexp matches */ |
| 1967 | 1931 | u64 diffFlags /* DIFF_* flags defined above */ |
| 1968 | 1932 | ){ |
| 1969 | 1933 | int ignoreEolWs; /* Ignore whitespace at the end of lines */ |
| 1970 | - int nContext; /* Amount of context to display */ | |
| 1971 | 1934 | DContext c; |
| 1972 | 1935 | |
| 1973 | 1936 | if( diffFlags & DIFF_INVERT ){ |
| 1974 | 1937 | Blob *pTemp = pA_Blob; |
| 1975 | 1938 | pA_Blob = pB_Blob; |
| 1976 | 1939 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -360,53 +360,19 @@ | |
| 360 | ** This function returns non-zero if the blob starts with a UTF-16le or |
| 361 | ** UTF-16be byte-order-mark (BOM). |
| 362 | */ |
| 363 | int starts_with_utf16_bom(const Blob *pContent, int *pnByte){ |
| 364 | const char *z = blob_buffer(pContent); |
| 365 | int c1, c2; |
| 366 | |
| 367 | if( pnByte ) *pnByte = 2; |
| 368 | if( blob_size(pContent)<2 ) return 0; |
| 369 | c1 = z[0]; c2 = z[1]; |
| 370 | if( (c1==(char)0xff) && (c2==(char)0xfe) ){ |
| 371 | return 1; |
| 372 | }else if( (c1==(char)0xfe) && (c2==(char)0xff) ){ |
| 373 | return 1; |
| 374 | } |
| 375 | return 0; |
| 376 | } |
| 377 | |
| 378 | /* |
| 379 | ** This function returns non-zero if the blob starts with a UTF-16le |
| 380 | ** byte-order-mark (BOM). |
| 381 | */ |
| 382 | int starts_with_utf16le_bom(const Blob *pContent, int *pnByte){ |
| 383 | const char *z = blob_buffer(pContent); |
| 384 | int c1, c2; |
| 385 | |
| 386 | if( pnByte ) *pnByte = 2; |
| 387 | if( blob_size(pContent)<2 ) return 0; |
| 388 | c1 = z[0]; c2 = z[1]; |
| 389 | if( (c1==(char)0xff) && (c2==(char)0xfe) ){ |
| 390 | return 1; |
| 391 | } |
| 392 | return 0; |
| 393 | } |
| 394 | |
| 395 | /* |
| 396 | ** This function returns non-zero if the blob starts with a UTF-16be |
| 397 | ** byte-order-mark (BOM). |
| 398 | */ |
| 399 | int starts_with_utf16be_bom(const Blob *pContent, int *pnByte){ |
| 400 | const char *z = blob_buffer(pContent); |
| 401 | int c1, c2; |
| 402 | |
| 403 | if( pnByte ) *pnByte = 2; |
| 404 | if( blob_size(pContent)<2 ) return 0; |
| 405 | c1 = z[0]; c2 = z[1]; |
| 406 | if( (c1==(char)0xfe) && (c2==(char)0xff) ){ |
| 407 | return 1; |
| 408 | } |
| 409 | return 0; |
| 410 | } |
| 411 | |
| 412 | /* |
| @@ -424,11 +390,11 @@ | |
| 424 | ReCompiled *pRe, /* The regular expression to be matched */ |
| 425 | DLine *aDLine, /* First of N DLines to compare against */ |
| 426 | int N /* Number of DLines to check */ |
| 427 | ){ |
| 428 | while( N-- ){ |
| 429 | if( re_execute(pRe, aDLine->z, LENGTH(aDLine)) ){ |
| 430 | return 1; |
| 431 | } |
| 432 | aDLine++; |
| 433 | } |
| 434 | return 0; |
| @@ -621,13 +587,11 @@ | |
| 621 | |
| 622 | /* Show the differences */ |
| 623 | for(i=0; i<nr; i++){ |
| 624 | m = R[r+i*3+1]; |
| 625 | for(j=0; j<m; j++){ |
| 626 | char cMark = '-'; |
| 627 | if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html); |
| 628 | if( pRe && re_dline_match(pRe, &A[a+j], 1)==0 ) cMark = ' '; |
| 629 | appendDiffLine(pOut, '-', &A[a+j], html, pRe); |
| 630 | } |
| 631 | a += m; |
| 632 | m = R[r+i*3+2]; |
| 633 | for(j=0; j<m; j++){ |
| @@ -1965,11 +1929,10 @@ | |
| 1965 | Blob *pOut, /* Write diff here if not NULL */ |
| 1966 | ReCompiled *pRe, /* Only output changes where this Regexp matches */ |
| 1967 | u64 diffFlags /* DIFF_* flags defined above */ |
| 1968 | ){ |
| 1969 | int ignoreEolWs; /* Ignore whitespace at the end of lines */ |
| 1970 | int nContext; /* Amount of context to display */ |
| 1971 | DContext c; |
| 1972 | |
| 1973 | if( diffFlags & DIFF_INVERT ){ |
| 1974 | Blob *pTemp = pA_Blob; |
| 1975 | pA_Blob = pB_Blob; |
| 1976 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -360,53 +360,19 @@ | |
| 360 | ** This function returns non-zero if the blob starts with a UTF-16le or |
| 361 | ** UTF-16be byte-order-mark (BOM). |
| 362 | */ |
| 363 | int starts_with_utf16_bom(const Blob *pContent, int *pnByte){ |
| 364 | const char *z = blob_buffer(pContent); |
| 365 | int c1; |
| 366 | |
| 367 | if( pnByte ) *pnByte = 2; |
| 368 | if( (blob_size(pContent)<2) || (blob_size(pContent)&1)) return 0; |
| 369 | c1 = ((unsigned short *)z)[0]; |
| 370 | if( (c1==0xfeff) || (c1==0xfffe) ){ |
| 371 | if( blob_size(pContent) < 4 ) return 1; |
| 372 | c1 = ((unsigned short *)z)[1]; |
| 373 | if( c1 != 0 ) return 1; |
| 374 | } |
| 375 | return 0; |
| 376 | } |
| 377 | |
| 378 | /* |
| @@ -424,11 +390,11 @@ | |
| 390 | ReCompiled *pRe, /* The regular expression to be matched */ |
| 391 | DLine *aDLine, /* First of N DLines to compare against */ |
| 392 | int N /* Number of DLines to check */ |
| 393 | ){ |
| 394 | while( N-- ){ |
| 395 | if( re_match(pRe, (const unsigned char *)aDLine->z, LENGTH(aDLine)) ){ |
| 396 | return 1; |
| 397 | } |
| 398 | aDLine++; |
| 399 | } |
| 400 | return 0; |
| @@ -621,13 +587,11 @@ | |
| 587 | |
| 588 | /* Show the differences */ |
| 589 | for(i=0; i<nr; i++){ |
| 590 | m = R[r+i*3+1]; |
| 591 | for(j=0; j<m; j++){ |
| 592 | if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html); |
| 593 | appendDiffLine(pOut, '-', &A[a+j], html, pRe); |
| 594 | } |
| 595 | a += m; |
| 596 | m = R[r+i*3+2]; |
| 597 | for(j=0; j<m; j++){ |
| @@ -1965,11 +1929,10 @@ | |
| 1929 | Blob *pOut, /* Write diff here if not NULL */ |
| 1930 | ReCompiled *pRe, /* Only output changes where this Regexp matches */ |
| 1931 | u64 diffFlags /* DIFF_* flags defined above */ |
| 1932 | ){ |
| 1933 | int ignoreEolWs; /* Ignore whitespace at the end of lines */ |
| 1934 | DContext c; |
| 1935 | |
| 1936 | if( diffFlags & DIFF_INVERT ){ |
| 1937 | Blob *pTemp = pA_Blob; |
| 1938 | pA_Blob = pB_Blob; |
| 1939 |
+10
-47
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -360,53 +360,19 @@ | ||
| 360 | 360 | ** This function returns non-zero if the blob starts with a UTF-16le or |
| 361 | 361 | ** UTF-16be byte-order-mark (BOM). |
| 362 | 362 | */ |
| 363 | 363 | int starts_with_utf16_bom(const Blob *pContent, int *pnByte){ |
| 364 | 364 | const char *z = blob_buffer(pContent); |
| 365 | - int c1, c2; | |
| 366 | - | |
| 367 | - if( pnByte ) *pnByte = 2; | |
| 368 | - if( blob_size(pContent)<2 ) return 0; | |
| 369 | - c1 = z[0]; c2 = z[1]; | |
| 370 | - if( (c1==(char)0xff) && (c2==(char)0xfe) ){ | |
| 371 | - return 1; | |
| 372 | - }else if( (c1==(char)0xfe) && (c2==(char)0xff) ){ | |
| 373 | - return 1; | |
| 374 | - } | |
| 375 | - return 0; | |
| 376 | -} | |
| 377 | - | |
| 378 | -/* | |
| 379 | -** This function returns non-zero if the blob starts with a UTF-16le | |
| 380 | -** byte-order-mark (BOM). | |
| 381 | -*/ | |
| 382 | -int starts_with_utf16le_bom(const Blob *pContent, int *pnByte){ | |
| 383 | - const char *z = blob_buffer(pContent); | |
| 384 | - int c1, c2; | |
| 385 | - | |
| 386 | - if( pnByte ) *pnByte = 2; | |
| 387 | - if( blob_size(pContent)<2 ) return 0; | |
| 388 | - c1 = z[0]; c2 = z[1]; | |
| 389 | - if( (c1==(char)0xff) && (c2==(char)0xfe) ){ | |
| 390 | - return 1; | |
| 391 | - } | |
| 392 | - return 0; | |
| 393 | -} | |
| 394 | - | |
| 395 | -/* | |
| 396 | -** This function returns non-zero if the blob starts with a UTF-16be | |
| 397 | -** byte-order-mark (BOM). | |
| 398 | -*/ | |
| 399 | -int starts_with_utf16be_bom(const Blob *pContent, int *pnByte){ | |
| 400 | - const char *z = blob_buffer(pContent); | |
| 401 | - int c1, c2; | |
| 402 | - | |
| 403 | - if( pnByte ) *pnByte = 2; | |
| 404 | - if( blob_size(pContent)<2 ) return 0; | |
| 405 | - c1 = z[0]; c2 = z[1]; | |
| 406 | - if( (c1==(char)0xfe) && (c2==(char)0xff) ){ | |
| 407 | - return 1; | |
| 365 | + int c1; | |
| 366 | + | |
| 367 | + if( pnByte ) *pnByte = 2; | |
| 368 | + if( (blob_size(pContent)<2) || (blob_size(pContent)&1)) return 0; | |
| 369 | + c1 = ((unsigned short *)z)[0]; | |
| 370 | + if( (c1==0xfeff) || (c1==0xfffe) ){ | |
| 371 | + if( blob_size(pContent) < 4 ) return 1; | |
| 372 | + c1 = ((unsigned short *)z)[1]; | |
| 373 | + if( c1 != 0 ) return 1; | |
| 408 | 374 | } |
| 409 | 375 | return 0; |
| 410 | 376 | } |
| 411 | 377 | |
| 412 | 378 | /* |
| @@ -424,11 +390,11 @@ | ||
| 424 | 390 | ReCompiled *pRe, /* The regular expression to be matched */ |
| 425 | 391 | DLine *aDLine, /* First of N DLines to compare against */ |
| 426 | 392 | int N /* Number of DLines to check */ |
| 427 | 393 | ){ |
| 428 | 394 | while( N-- ){ |
| 429 | - if( re_execute(pRe, aDLine->z, LENGTH(aDLine)) ){ | |
| 395 | + if( re_match(pRe, (const unsigned char *)aDLine->z, LENGTH(aDLine)) ){ | |
| 430 | 396 | return 1; |
| 431 | 397 | } |
| 432 | 398 | aDLine++; |
| 433 | 399 | } |
| 434 | 400 | return 0; |
| @@ -621,13 +587,11 @@ | ||
| 621 | 587 | |
| 622 | 588 | /* Show the differences */ |
| 623 | 589 | for(i=0; i<nr; i++){ |
| 624 | 590 | m = R[r+i*3+1]; |
| 625 | 591 | for(j=0; j<m; j++){ |
| 626 | - char cMark = '-'; | |
| 627 | 592 | if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html); |
| 628 | - if( pRe && re_dline_match(pRe, &A[a+j], 1)==0 ) cMark = ' '; | |
| 629 | 593 | appendDiffLine(pOut, '-', &A[a+j], html, pRe); |
| 630 | 594 | } |
| 631 | 595 | a += m; |
| 632 | 596 | m = R[r+i*3+2]; |
| 633 | 597 | for(j=0; j<m; j++){ |
| @@ -1965,11 +1929,10 @@ | ||
| 1965 | 1929 | Blob *pOut, /* Write diff here if not NULL */ |
| 1966 | 1930 | ReCompiled *pRe, /* Only output changes where this Regexp matches */ |
| 1967 | 1931 | u64 diffFlags /* DIFF_* flags defined above */ |
| 1968 | 1932 | ){ |
| 1969 | 1933 | int ignoreEolWs; /* Ignore whitespace at the end of lines */ |
| 1970 | - int nContext; /* Amount of context to display */ | |
| 1971 | 1934 | DContext c; |
| 1972 | 1935 | |
| 1973 | 1936 | if( diffFlags & DIFF_INVERT ){ |
| 1974 | 1937 | Blob *pTemp = pA_Blob; |
| 1975 | 1938 | pA_Blob = pB_Blob; |
| 1976 | 1939 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -360,53 +360,19 @@ | |
| 360 | ** This function returns non-zero if the blob starts with a UTF-16le or |
| 361 | ** UTF-16be byte-order-mark (BOM). |
| 362 | */ |
| 363 | int starts_with_utf16_bom(const Blob *pContent, int *pnByte){ |
| 364 | const char *z = blob_buffer(pContent); |
| 365 | int c1, c2; |
| 366 | |
| 367 | if( pnByte ) *pnByte = 2; |
| 368 | if( blob_size(pContent)<2 ) return 0; |
| 369 | c1 = z[0]; c2 = z[1]; |
| 370 | if( (c1==(char)0xff) && (c2==(char)0xfe) ){ |
| 371 | return 1; |
| 372 | }else if( (c1==(char)0xfe) && (c2==(char)0xff) ){ |
| 373 | return 1; |
| 374 | } |
| 375 | return 0; |
| 376 | } |
| 377 | |
| 378 | /* |
| 379 | ** This function returns non-zero if the blob starts with a UTF-16le |
| 380 | ** byte-order-mark (BOM). |
| 381 | */ |
| 382 | int starts_with_utf16le_bom(const Blob *pContent, int *pnByte){ |
| 383 | const char *z = blob_buffer(pContent); |
| 384 | int c1, c2; |
| 385 | |
| 386 | if( pnByte ) *pnByte = 2; |
| 387 | if( blob_size(pContent)<2 ) return 0; |
| 388 | c1 = z[0]; c2 = z[1]; |
| 389 | if( (c1==(char)0xff) && (c2==(char)0xfe) ){ |
| 390 | return 1; |
| 391 | } |
| 392 | return 0; |
| 393 | } |
| 394 | |
| 395 | /* |
| 396 | ** This function returns non-zero if the blob starts with a UTF-16be |
| 397 | ** byte-order-mark (BOM). |
| 398 | */ |
| 399 | int starts_with_utf16be_bom(const Blob *pContent, int *pnByte){ |
| 400 | const char *z = blob_buffer(pContent); |
| 401 | int c1, c2; |
| 402 | |
| 403 | if( pnByte ) *pnByte = 2; |
| 404 | if( blob_size(pContent)<2 ) return 0; |
| 405 | c1 = z[0]; c2 = z[1]; |
| 406 | if( (c1==(char)0xfe) && (c2==(char)0xff) ){ |
| 407 | return 1; |
| 408 | } |
| 409 | return 0; |
| 410 | } |
| 411 | |
| 412 | /* |
| @@ -424,11 +390,11 @@ | |
| 424 | ReCompiled *pRe, /* The regular expression to be matched */ |
| 425 | DLine *aDLine, /* First of N DLines to compare against */ |
| 426 | int N /* Number of DLines to check */ |
| 427 | ){ |
| 428 | while( N-- ){ |
| 429 | if( re_execute(pRe, aDLine->z, LENGTH(aDLine)) ){ |
| 430 | return 1; |
| 431 | } |
| 432 | aDLine++; |
| 433 | } |
| 434 | return 0; |
| @@ -621,13 +587,11 @@ | |
| 621 | |
| 622 | /* Show the differences */ |
| 623 | for(i=0; i<nr; i++){ |
| 624 | m = R[r+i*3+1]; |
| 625 | for(j=0; j<m; j++){ |
| 626 | char cMark = '-'; |
| 627 | if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html); |
| 628 | if( pRe && re_dline_match(pRe, &A[a+j], 1)==0 ) cMark = ' '; |
| 629 | appendDiffLine(pOut, '-', &A[a+j], html, pRe); |
| 630 | } |
| 631 | a += m; |
| 632 | m = R[r+i*3+2]; |
| 633 | for(j=0; j<m; j++){ |
| @@ -1965,11 +1929,10 @@ | |
| 1965 | Blob *pOut, /* Write diff here if not NULL */ |
| 1966 | ReCompiled *pRe, /* Only output changes where this Regexp matches */ |
| 1967 | u64 diffFlags /* DIFF_* flags defined above */ |
| 1968 | ){ |
| 1969 | int ignoreEolWs; /* Ignore whitespace at the end of lines */ |
| 1970 | int nContext; /* Amount of context to display */ |
| 1971 | DContext c; |
| 1972 | |
| 1973 | if( diffFlags & DIFF_INVERT ){ |
| 1974 | Blob *pTemp = pA_Blob; |
| 1975 | pA_Blob = pB_Blob; |
| 1976 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -360,53 +360,19 @@ | |
| 360 | ** This function returns non-zero if the blob starts with a UTF-16le or |
| 361 | ** UTF-16be byte-order-mark (BOM). |
| 362 | */ |
| 363 | int starts_with_utf16_bom(const Blob *pContent, int *pnByte){ |
| 364 | const char *z = blob_buffer(pContent); |
| 365 | int c1; |
| 366 | |
| 367 | if( pnByte ) *pnByte = 2; |
| 368 | if( (blob_size(pContent)<2) || (blob_size(pContent)&1)) return 0; |
| 369 | c1 = ((unsigned short *)z)[0]; |
| 370 | if( (c1==0xfeff) || (c1==0xfffe) ){ |
| 371 | if( blob_size(pContent) < 4 ) return 1; |
| 372 | c1 = ((unsigned short *)z)[1]; |
| 373 | if( c1 != 0 ) return 1; |
| 374 | } |
| 375 | return 0; |
| 376 | } |
| 377 | |
| 378 | /* |
| @@ -424,11 +390,11 @@ | |
| 390 | ReCompiled *pRe, /* The regular expression to be matched */ |
| 391 | DLine *aDLine, /* First of N DLines to compare against */ |
| 392 | int N /* Number of DLines to check */ |
| 393 | ){ |
| 394 | while( N-- ){ |
| 395 | if( re_match(pRe, (const unsigned char *)aDLine->z, LENGTH(aDLine)) ){ |
| 396 | return 1; |
| 397 | } |
| 398 | aDLine++; |
| 399 | } |
| 400 | return 0; |
| @@ -621,13 +587,11 @@ | |
| 587 | |
| 588 | /* Show the differences */ |
| 589 | for(i=0; i<nr; i++){ |
| 590 | m = R[r+i*3+1]; |
| 591 | for(j=0; j<m; j++){ |
| 592 | if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html); |
| 593 | appendDiffLine(pOut, '-', &A[a+j], html, pRe); |
| 594 | } |
| 595 | a += m; |
| 596 | m = R[r+i*3+2]; |
| 597 | for(j=0; j<m; j++){ |
| @@ -1965,11 +1929,10 @@ | |
| 1929 | Blob *pOut, /* Write diff here if not NULL */ |
| 1930 | ReCompiled *pRe, /* Only output changes where this Regexp matches */ |
| 1931 | u64 diffFlags /* DIFF_* flags defined above */ |
| 1932 | ){ |
| 1933 | int ignoreEolWs; /* Ignore whitespace at the end of lines */ |
| 1934 | DContext c; |
| 1935 | |
| 1936 | if( diffFlags & DIFF_INVERT ){ |
| 1937 | Blob *pTemp = pA_Blob; |
| 1938 | pA_Blob = pB_Blob; |
| 1939 |
+7
-3
| --- src/doc.c | ||
| +++ src/doc.c | ||
| @@ -122,11 +122,13 @@ | ||
| 122 | 122 | { "deb", 3, "application/x-debian-package" }, |
| 123 | 123 | { "dir", 3, "application/x-director" }, |
| 124 | 124 | { "dl", 2, "video/dl" }, |
| 125 | 125 | { "dms", 3, "application/octet-stream" }, |
| 126 | 126 | { "doc", 3, "application/msword" }, |
| 127 | - { "docx", 4, "application/msword" }, | |
| 127 | + { "docx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, | |
| 128 | + { "dot", 3, "application/msword" }, | |
| 129 | + { "dotx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.template"}, | |
| 128 | 130 | { "drw", 3, "application/drafting" }, |
| 129 | 131 | { "dvi", 3, "application/x-dvi" }, |
| 130 | 132 | { "dwg", 3, "application/acad" }, |
| 131 | 133 | { "dxf", 3, "application/dxf" }, |
| 132 | 134 | { "dxr", 3, "application/x-director" }, |
| @@ -202,14 +204,16 @@ | ||
| 202 | 204 | { "pl", 2, "application/x-perl" }, |
| 203 | 205 | { "pm", 2, "application/x-perl" }, |
| 204 | 206 | { "png", 3, "image/png" }, |
| 205 | 207 | { "pnm", 3, "image/x-portable-anymap" }, |
| 206 | 208 | { "pot", 3, "application/mspowerpoint" }, |
| 209 | + { "potx", 4, "application/vnd.openxmlformats-officedocument.presentationml.template"}, | |
| 207 | 210 | { "ppm", 3, "image/x-portable-pixmap" }, |
| 208 | 211 | { "pps", 3, "application/mspowerpoint" }, |
| 212 | + { "ppsx", 4, "application/vnd.openxmlformats-officedocument.presentationml.slideshow"}, | |
| 209 | 213 | { "ppt", 3, "application/mspowerpoint" }, |
| 210 | - { "pptx", 4, "application/mspowerpoint" }, | |
| 214 | + { "pptx", 4, "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, | |
| 211 | 215 | { "ppz", 3, "application/mspowerpoint" }, |
| 212 | 216 | { "pre", 3, "application/x-freelance" }, |
| 213 | 217 | { "prt", 3, "application/pro_eng" }, |
| 214 | 218 | { "ps", 2, "application/postscript" }, |
| 215 | 219 | { "qt", 2, "video/quicktime" }, |
| @@ -280,11 +284,11 @@ | ||
| 280 | 284 | { "xbm", 3, "image/x-xbitmap" }, |
| 281 | 285 | { "xlc", 3, "application/vnd.ms-excel" }, |
| 282 | 286 | { "xll", 3, "application/vnd.ms-excel" }, |
| 283 | 287 | { "xlm", 3, "application/vnd.ms-excel" }, |
| 284 | 288 | { "xls", 3, "application/vnd.ms-excel" }, |
| 285 | - { "xlsx", 4, "application/vnd.ms-excel" }, | |
| 289 | + { "xlsx", 4, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, | |
| 286 | 290 | { "xlw", 3, "application/vnd.ms-excel" }, |
| 287 | 291 | { "xml", 3, "text/xml" }, |
| 288 | 292 | { "xpm", 3, "image/x-xpixmap" }, |
| 289 | 293 | { "xwd", 3, "image/x-xwindowdump" }, |
| 290 | 294 | { "xyz", 3, "chemical/x-pdb" }, |
| 291 | 295 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -122,11 +122,13 @@ | |
| 122 | { "deb", 3, "application/x-debian-package" }, |
| 123 | { "dir", 3, "application/x-director" }, |
| 124 | { "dl", 2, "video/dl" }, |
| 125 | { "dms", 3, "application/octet-stream" }, |
| 126 | { "doc", 3, "application/msword" }, |
| 127 | { "docx", 4, "application/msword" }, |
| 128 | { "drw", 3, "application/drafting" }, |
| 129 | { "dvi", 3, "application/x-dvi" }, |
| 130 | { "dwg", 3, "application/acad" }, |
| 131 | { "dxf", 3, "application/dxf" }, |
| 132 | { "dxr", 3, "application/x-director" }, |
| @@ -202,14 +204,16 @@ | |
| 202 | { "pl", 2, "application/x-perl" }, |
| 203 | { "pm", 2, "application/x-perl" }, |
| 204 | { "png", 3, "image/png" }, |
| 205 | { "pnm", 3, "image/x-portable-anymap" }, |
| 206 | { "pot", 3, "application/mspowerpoint" }, |
| 207 | { "ppm", 3, "image/x-portable-pixmap" }, |
| 208 | { "pps", 3, "application/mspowerpoint" }, |
| 209 | { "ppt", 3, "application/mspowerpoint" }, |
| 210 | { "pptx", 4, "application/mspowerpoint" }, |
| 211 | { "ppz", 3, "application/mspowerpoint" }, |
| 212 | { "pre", 3, "application/x-freelance" }, |
| 213 | { "prt", 3, "application/pro_eng" }, |
| 214 | { "ps", 2, "application/postscript" }, |
| 215 | { "qt", 2, "video/quicktime" }, |
| @@ -280,11 +284,11 @@ | |
| 280 | { "xbm", 3, "image/x-xbitmap" }, |
| 281 | { "xlc", 3, "application/vnd.ms-excel" }, |
| 282 | { "xll", 3, "application/vnd.ms-excel" }, |
| 283 | { "xlm", 3, "application/vnd.ms-excel" }, |
| 284 | { "xls", 3, "application/vnd.ms-excel" }, |
| 285 | { "xlsx", 4, "application/vnd.ms-excel" }, |
| 286 | { "xlw", 3, "application/vnd.ms-excel" }, |
| 287 | { "xml", 3, "text/xml" }, |
| 288 | { "xpm", 3, "image/x-xpixmap" }, |
| 289 | { "xwd", 3, "image/x-xwindowdump" }, |
| 290 | { "xyz", 3, "chemical/x-pdb" }, |
| 291 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -122,11 +122,13 @@ | |
| 122 | { "deb", 3, "application/x-debian-package" }, |
| 123 | { "dir", 3, "application/x-director" }, |
| 124 | { "dl", 2, "video/dl" }, |
| 125 | { "dms", 3, "application/octet-stream" }, |
| 126 | { "doc", 3, "application/msword" }, |
| 127 | { "docx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, |
| 128 | { "dot", 3, "application/msword" }, |
| 129 | { "dotx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.template"}, |
| 130 | { "drw", 3, "application/drafting" }, |
| 131 | { "dvi", 3, "application/x-dvi" }, |
| 132 | { "dwg", 3, "application/acad" }, |
| 133 | { "dxf", 3, "application/dxf" }, |
| 134 | { "dxr", 3, "application/x-director" }, |
| @@ -202,14 +204,16 @@ | |
| 204 | { "pl", 2, "application/x-perl" }, |
| 205 | { "pm", 2, "application/x-perl" }, |
| 206 | { "png", 3, "image/png" }, |
| 207 | { "pnm", 3, "image/x-portable-anymap" }, |
| 208 | { "pot", 3, "application/mspowerpoint" }, |
| 209 | { "potx", 4, "application/vnd.openxmlformats-officedocument.presentationml.template"}, |
| 210 | { "ppm", 3, "image/x-portable-pixmap" }, |
| 211 | { "pps", 3, "application/mspowerpoint" }, |
| 212 | { "ppsx", 4, "application/vnd.openxmlformats-officedocument.presentationml.slideshow"}, |
| 213 | { "ppt", 3, "application/mspowerpoint" }, |
| 214 | { "pptx", 4, "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, |
| 215 | { "ppz", 3, "application/mspowerpoint" }, |
| 216 | { "pre", 3, "application/x-freelance" }, |
| 217 | { "prt", 3, "application/pro_eng" }, |
| 218 | { "ps", 2, "application/postscript" }, |
| 219 | { "qt", 2, "video/quicktime" }, |
| @@ -280,11 +284,11 @@ | |
| 284 | { "xbm", 3, "image/x-xbitmap" }, |
| 285 | { "xlc", 3, "application/vnd.ms-excel" }, |
| 286 | { "xll", 3, "application/vnd.ms-excel" }, |
| 287 | { "xlm", 3, "application/vnd.ms-excel" }, |
| 288 | { "xls", 3, "application/vnd.ms-excel" }, |
| 289 | { "xlsx", 4, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, |
| 290 | { "xlw", 3, "application/vnd.ms-excel" }, |
| 291 | { "xml", 3, "text/xml" }, |
| 292 | { "xpm", 3, "image/x-xpixmap" }, |
| 293 | { "xwd", 3, "image/x-xwindowdump" }, |
| 294 | { "xyz", 3, "chemical/x-pdb" }, |
| 295 |
+7
-3
| --- src/doc.c | ||
| +++ src/doc.c | ||
| @@ -122,11 +122,13 @@ | ||
| 122 | 122 | { "deb", 3, "application/x-debian-package" }, |
| 123 | 123 | { "dir", 3, "application/x-director" }, |
| 124 | 124 | { "dl", 2, "video/dl" }, |
| 125 | 125 | { "dms", 3, "application/octet-stream" }, |
| 126 | 126 | { "doc", 3, "application/msword" }, |
| 127 | - { "docx", 4, "application/msword" }, | |
| 127 | + { "docx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, | |
| 128 | + { "dot", 3, "application/msword" }, | |
| 129 | + { "dotx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.template"}, | |
| 128 | 130 | { "drw", 3, "application/drafting" }, |
| 129 | 131 | { "dvi", 3, "application/x-dvi" }, |
| 130 | 132 | { "dwg", 3, "application/acad" }, |
| 131 | 133 | { "dxf", 3, "application/dxf" }, |
| 132 | 134 | { "dxr", 3, "application/x-director" }, |
| @@ -202,14 +204,16 @@ | ||
| 202 | 204 | { "pl", 2, "application/x-perl" }, |
| 203 | 205 | { "pm", 2, "application/x-perl" }, |
| 204 | 206 | { "png", 3, "image/png" }, |
| 205 | 207 | { "pnm", 3, "image/x-portable-anymap" }, |
| 206 | 208 | { "pot", 3, "application/mspowerpoint" }, |
| 209 | + { "potx", 4, "application/vnd.openxmlformats-officedocument.presentationml.template"}, | |
| 207 | 210 | { "ppm", 3, "image/x-portable-pixmap" }, |
| 208 | 211 | { "pps", 3, "application/mspowerpoint" }, |
| 212 | + { "ppsx", 4, "application/vnd.openxmlformats-officedocument.presentationml.slideshow"}, | |
| 209 | 213 | { "ppt", 3, "application/mspowerpoint" }, |
| 210 | - { "pptx", 4, "application/mspowerpoint" }, | |
| 214 | + { "pptx", 4, "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, | |
| 211 | 215 | { "ppz", 3, "application/mspowerpoint" }, |
| 212 | 216 | { "pre", 3, "application/x-freelance" }, |
| 213 | 217 | { "prt", 3, "application/pro_eng" }, |
| 214 | 218 | { "ps", 2, "application/postscript" }, |
| 215 | 219 | { "qt", 2, "video/quicktime" }, |
| @@ -280,11 +284,11 @@ | ||
| 280 | 284 | { "xbm", 3, "image/x-xbitmap" }, |
| 281 | 285 | { "xlc", 3, "application/vnd.ms-excel" }, |
| 282 | 286 | { "xll", 3, "application/vnd.ms-excel" }, |
| 283 | 287 | { "xlm", 3, "application/vnd.ms-excel" }, |
| 284 | 288 | { "xls", 3, "application/vnd.ms-excel" }, |
| 285 | - { "xlsx", 4, "application/vnd.ms-excel" }, | |
| 289 | + { "xlsx", 4, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, | |
| 286 | 290 | { "xlw", 3, "application/vnd.ms-excel" }, |
| 287 | 291 | { "xml", 3, "text/xml" }, |
| 288 | 292 | { "xpm", 3, "image/x-xpixmap" }, |
| 289 | 293 | { "xwd", 3, "image/x-xwindowdump" }, |
| 290 | 294 | { "xyz", 3, "chemical/x-pdb" }, |
| 291 | 295 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -122,11 +122,13 @@ | |
| 122 | { "deb", 3, "application/x-debian-package" }, |
| 123 | { "dir", 3, "application/x-director" }, |
| 124 | { "dl", 2, "video/dl" }, |
| 125 | { "dms", 3, "application/octet-stream" }, |
| 126 | { "doc", 3, "application/msword" }, |
| 127 | { "docx", 4, "application/msword" }, |
| 128 | { "drw", 3, "application/drafting" }, |
| 129 | { "dvi", 3, "application/x-dvi" }, |
| 130 | { "dwg", 3, "application/acad" }, |
| 131 | { "dxf", 3, "application/dxf" }, |
| 132 | { "dxr", 3, "application/x-director" }, |
| @@ -202,14 +204,16 @@ | |
| 202 | { "pl", 2, "application/x-perl" }, |
| 203 | { "pm", 2, "application/x-perl" }, |
| 204 | { "png", 3, "image/png" }, |
| 205 | { "pnm", 3, "image/x-portable-anymap" }, |
| 206 | { "pot", 3, "application/mspowerpoint" }, |
| 207 | { "ppm", 3, "image/x-portable-pixmap" }, |
| 208 | { "pps", 3, "application/mspowerpoint" }, |
| 209 | { "ppt", 3, "application/mspowerpoint" }, |
| 210 | { "pptx", 4, "application/mspowerpoint" }, |
| 211 | { "ppz", 3, "application/mspowerpoint" }, |
| 212 | { "pre", 3, "application/x-freelance" }, |
| 213 | { "prt", 3, "application/pro_eng" }, |
| 214 | { "ps", 2, "application/postscript" }, |
| 215 | { "qt", 2, "video/quicktime" }, |
| @@ -280,11 +284,11 @@ | |
| 280 | { "xbm", 3, "image/x-xbitmap" }, |
| 281 | { "xlc", 3, "application/vnd.ms-excel" }, |
| 282 | { "xll", 3, "application/vnd.ms-excel" }, |
| 283 | { "xlm", 3, "application/vnd.ms-excel" }, |
| 284 | { "xls", 3, "application/vnd.ms-excel" }, |
| 285 | { "xlsx", 4, "application/vnd.ms-excel" }, |
| 286 | { "xlw", 3, "application/vnd.ms-excel" }, |
| 287 | { "xml", 3, "text/xml" }, |
| 288 | { "xpm", 3, "image/x-xpixmap" }, |
| 289 | { "xwd", 3, "image/x-xwindowdump" }, |
| 290 | { "xyz", 3, "chemical/x-pdb" }, |
| 291 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -122,11 +122,13 @@ | |
| 122 | { "deb", 3, "application/x-debian-package" }, |
| 123 | { "dir", 3, "application/x-director" }, |
| 124 | { "dl", 2, "video/dl" }, |
| 125 | { "dms", 3, "application/octet-stream" }, |
| 126 | { "doc", 3, "application/msword" }, |
| 127 | { "docx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, |
| 128 | { "dot", 3, "application/msword" }, |
| 129 | { "dotx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.template"}, |
| 130 | { "drw", 3, "application/drafting" }, |
| 131 | { "dvi", 3, "application/x-dvi" }, |
| 132 | { "dwg", 3, "application/acad" }, |
| 133 | { "dxf", 3, "application/dxf" }, |
| 134 | { "dxr", 3, "application/x-director" }, |
| @@ -202,14 +204,16 @@ | |
| 204 | { "pl", 2, "application/x-perl" }, |
| 205 | { "pm", 2, "application/x-perl" }, |
| 206 | { "png", 3, "image/png" }, |
| 207 | { "pnm", 3, "image/x-portable-anymap" }, |
| 208 | { "pot", 3, "application/mspowerpoint" }, |
| 209 | { "potx", 4, "application/vnd.openxmlformats-officedocument.presentationml.template"}, |
| 210 | { "ppm", 3, "image/x-portable-pixmap" }, |
| 211 | { "pps", 3, "application/mspowerpoint" }, |
| 212 | { "ppsx", 4, "application/vnd.openxmlformats-officedocument.presentationml.slideshow"}, |
| 213 | { "ppt", 3, "application/mspowerpoint" }, |
| 214 | { "pptx", 4, "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, |
| 215 | { "ppz", 3, "application/mspowerpoint" }, |
| 216 | { "pre", 3, "application/x-freelance" }, |
| 217 | { "prt", 3, "application/pro_eng" }, |
| 218 | { "ps", 2, "application/postscript" }, |
| 219 | { "qt", 2, "video/quicktime" }, |
| @@ -280,11 +284,11 @@ | |
| 284 | { "xbm", 3, "image/x-xbitmap" }, |
| 285 | { "xlc", 3, "application/vnd.ms-excel" }, |
| 286 | { "xll", 3, "application/vnd.ms-excel" }, |
| 287 | { "xlm", 3, "application/vnd.ms-excel" }, |
| 288 | { "xls", 3, "application/vnd.ms-excel" }, |
| 289 | { "xlsx", 4, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, |
| 290 | { "xlw", 3, "application/vnd.ms-excel" }, |
| 291 | { "xml", 3, "text/xml" }, |
| 292 | { "xpm", 3, "image/x-xpixmap" }, |
| 293 | { "xwd", 3, "image/x-xwindowdump" }, |
| 294 | { "xyz", 3, "chemical/x-pdb" }, |
| 295 |
+7
-3
| --- src/doc.c | ||
| +++ src/doc.c | ||
| @@ -122,11 +122,13 @@ | ||
| 122 | 122 | { "deb", 3, "application/x-debian-package" }, |
| 123 | 123 | { "dir", 3, "application/x-director" }, |
| 124 | 124 | { "dl", 2, "video/dl" }, |
| 125 | 125 | { "dms", 3, "application/octet-stream" }, |
| 126 | 126 | { "doc", 3, "application/msword" }, |
| 127 | - { "docx", 4, "application/msword" }, | |
| 127 | + { "docx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, | |
| 128 | + { "dot", 3, "application/msword" }, | |
| 129 | + { "dotx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.template"}, | |
| 128 | 130 | { "drw", 3, "application/drafting" }, |
| 129 | 131 | { "dvi", 3, "application/x-dvi" }, |
| 130 | 132 | { "dwg", 3, "application/acad" }, |
| 131 | 133 | { "dxf", 3, "application/dxf" }, |
| 132 | 134 | { "dxr", 3, "application/x-director" }, |
| @@ -202,14 +204,16 @@ | ||
| 202 | 204 | { "pl", 2, "application/x-perl" }, |
| 203 | 205 | { "pm", 2, "application/x-perl" }, |
| 204 | 206 | { "png", 3, "image/png" }, |
| 205 | 207 | { "pnm", 3, "image/x-portable-anymap" }, |
| 206 | 208 | { "pot", 3, "application/mspowerpoint" }, |
| 209 | + { "potx", 4, "application/vnd.openxmlformats-officedocument.presentationml.template"}, | |
| 207 | 210 | { "ppm", 3, "image/x-portable-pixmap" }, |
| 208 | 211 | { "pps", 3, "application/mspowerpoint" }, |
| 212 | + { "ppsx", 4, "application/vnd.openxmlformats-officedocument.presentationml.slideshow"}, | |
| 209 | 213 | { "ppt", 3, "application/mspowerpoint" }, |
| 210 | - { "pptx", 4, "application/mspowerpoint" }, | |
| 214 | + { "pptx", 4, "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, | |
| 211 | 215 | { "ppz", 3, "application/mspowerpoint" }, |
| 212 | 216 | { "pre", 3, "application/x-freelance" }, |
| 213 | 217 | { "prt", 3, "application/pro_eng" }, |
| 214 | 218 | { "ps", 2, "application/postscript" }, |
| 215 | 219 | { "qt", 2, "video/quicktime" }, |
| @@ -280,11 +284,11 @@ | ||
| 280 | 284 | { "xbm", 3, "image/x-xbitmap" }, |
| 281 | 285 | { "xlc", 3, "application/vnd.ms-excel" }, |
| 282 | 286 | { "xll", 3, "application/vnd.ms-excel" }, |
| 283 | 287 | { "xlm", 3, "application/vnd.ms-excel" }, |
| 284 | 288 | { "xls", 3, "application/vnd.ms-excel" }, |
| 285 | - { "xlsx", 4, "application/vnd.ms-excel" }, | |
| 289 | + { "xlsx", 4, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, | |
| 286 | 290 | { "xlw", 3, "application/vnd.ms-excel" }, |
| 287 | 291 | { "xml", 3, "text/xml" }, |
| 288 | 292 | { "xpm", 3, "image/x-xpixmap" }, |
| 289 | 293 | { "xwd", 3, "image/x-xwindowdump" }, |
| 290 | 294 | { "xyz", 3, "chemical/x-pdb" }, |
| 291 | 295 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -122,11 +122,13 @@ | |
| 122 | { "deb", 3, "application/x-debian-package" }, |
| 123 | { "dir", 3, "application/x-director" }, |
| 124 | { "dl", 2, "video/dl" }, |
| 125 | { "dms", 3, "application/octet-stream" }, |
| 126 | { "doc", 3, "application/msword" }, |
| 127 | { "docx", 4, "application/msword" }, |
| 128 | { "drw", 3, "application/drafting" }, |
| 129 | { "dvi", 3, "application/x-dvi" }, |
| 130 | { "dwg", 3, "application/acad" }, |
| 131 | { "dxf", 3, "application/dxf" }, |
| 132 | { "dxr", 3, "application/x-director" }, |
| @@ -202,14 +204,16 @@ | |
| 202 | { "pl", 2, "application/x-perl" }, |
| 203 | { "pm", 2, "application/x-perl" }, |
| 204 | { "png", 3, "image/png" }, |
| 205 | { "pnm", 3, "image/x-portable-anymap" }, |
| 206 | { "pot", 3, "application/mspowerpoint" }, |
| 207 | { "ppm", 3, "image/x-portable-pixmap" }, |
| 208 | { "pps", 3, "application/mspowerpoint" }, |
| 209 | { "ppt", 3, "application/mspowerpoint" }, |
| 210 | { "pptx", 4, "application/mspowerpoint" }, |
| 211 | { "ppz", 3, "application/mspowerpoint" }, |
| 212 | { "pre", 3, "application/x-freelance" }, |
| 213 | { "prt", 3, "application/pro_eng" }, |
| 214 | { "ps", 2, "application/postscript" }, |
| 215 | { "qt", 2, "video/quicktime" }, |
| @@ -280,11 +284,11 @@ | |
| 280 | { "xbm", 3, "image/x-xbitmap" }, |
| 281 | { "xlc", 3, "application/vnd.ms-excel" }, |
| 282 | { "xll", 3, "application/vnd.ms-excel" }, |
| 283 | { "xlm", 3, "application/vnd.ms-excel" }, |
| 284 | { "xls", 3, "application/vnd.ms-excel" }, |
| 285 | { "xlsx", 4, "application/vnd.ms-excel" }, |
| 286 | { "xlw", 3, "application/vnd.ms-excel" }, |
| 287 | { "xml", 3, "text/xml" }, |
| 288 | { "xpm", 3, "image/x-xpixmap" }, |
| 289 | { "xwd", 3, "image/x-xwindowdump" }, |
| 290 | { "xyz", 3, "chemical/x-pdb" }, |
| 291 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -122,11 +122,13 @@ | |
| 122 | { "deb", 3, "application/x-debian-package" }, |
| 123 | { "dir", 3, "application/x-director" }, |
| 124 | { "dl", 2, "video/dl" }, |
| 125 | { "dms", 3, "application/octet-stream" }, |
| 126 | { "doc", 3, "application/msword" }, |
| 127 | { "docx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, |
| 128 | { "dot", 3, "application/msword" }, |
| 129 | { "dotx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.template"}, |
| 130 | { "drw", 3, "application/drafting" }, |
| 131 | { "dvi", 3, "application/x-dvi" }, |
| 132 | { "dwg", 3, "application/acad" }, |
| 133 | { "dxf", 3, "application/dxf" }, |
| 134 | { "dxr", 3, "application/x-director" }, |
| @@ -202,14 +204,16 @@ | |
| 204 | { "pl", 2, "application/x-perl" }, |
| 205 | { "pm", 2, "application/x-perl" }, |
| 206 | { "png", 3, "image/png" }, |
| 207 | { "pnm", 3, "image/x-portable-anymap" }, |
| 208 | { "pot", 3, "application/mspowerpoint" }, |
| 209 | { "potx", 4, "application/vnd.openxmlformats-officedocument.presentationml.template"}, |
| 210 | { "ppm", 3, "image/x-portable-pixmap" }, |
| 211 | { "pps", 3, "application/mspowerpoint" }, |
| 212 | { "ppsx", 4, "application/vnd.openxmlformats-officedocument.presentationml.slideshow"}, |
| 213 | { "ppt", 3, "application/mspowerpoint" }, |
| 214 | { "pptx", 4, "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, |
| 215 | { "ppz", 3, "application/mspowerpoint" }, |
| 216 | { "pre", 3, "application/x-freelance" }, |
| 217 | { "prt", 3, "application/pro_eng" }, |
| 218 | { "ps", 2, "application/postscript" }, |
| 219 | { "qt", 2, "video/quicktime" }, |
| @@ -280,11 +284,11 @@ | |
| 284 | { "xbm", 3, "image/x-xbitmap" }, |
| 285 | { "xlc", 3, "application/vnd.ms-excel" }, |
| 286 | { "xll", 3, "application/vnd.ms-excel" }, |
| 287 | { "xlm", 3, "application/vnd.ms-excel" }, |
| 288 | { "xls", 3, "application/vnd.ms-excel" }, |
| 289 | { "xlsx", 4, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, |
| 290 | { "xlw", 3, "application/vnd.ms-excel" }, |
| 291 | { "xml", 3, "text/xml" }, |
| 292 | { "xpm", 3, "image/x-xpixmap" }, |
| 293 | { "xwd", 3, "image/x-xwindowdump" }, |
| 294 | { "xyz", 3, "chemical/x-pdb" }, |
| 295 |
+2
-3
| --- src/encode.c | ||
| +++ src/encode.c | ||
| @@ -131,18 +131,17 @@ | ||
| 131 | 131 | static char *EncodeHttp(const char *zIn, int n, int encodeSlash){ |
| 132 | 132 | int c; |
| 133 | 133 | int i = 0; |
| 134 | 134 | int count = 0; |
| 135 | 135 | char *zOut; |
| 136 | - int other; | |
| 137 | 136 | # define IsSafeChar(X) \ |
| 138 | 137 | (fossil_isalnum(X) || (X)=='.' || (X)=='$' \ |
| 139 | - || (X)=='~' || (X)=='-' || (X)=='_' || (X)==other) | |
| 138 | + || (X)=='~' || (X)=='-' || (X)=='_' \ | |
| 139 | + || (!encodeSlash && ((X)=='/' || (X)==':'))) | |
| 140 | 140 | |
| 141 | 141 | if( zIn==0 ) return 0; |
| 142 | 142 | if( n<0 ) n = strlen(zIn); |
| 143 | - other = encodeSlash ? 'a' : '/'; | |
| 144 | 143 | while( i<n && (c = zIn[i])!=0 ){ |
| 145 | 144 | if( IsSafeChar(c) || c==' ' ){ |
| 146 | 145 | count++; |
| 147 | 146 | }else{ |
| 148 | 147 | count += 3; |
| 149 | 148 |
| --- src/encode.c | |
| +++ src/encode.c | |
| @@ -131,18 +131,17 @@ | |
| 131 | static char *EncodeHttp(const char *zIn, int n, int encodeSlash){ |
| 132 | int c; |
| 133 | int i = 0; |
| 134 | int count = 0; |
| 135 | char *zOut; |
| 136 | int other; |
| 137 | # define IsSafeChar(X) \ |
| 138 | (fossil_isalnum(X) || (X)=='.' || (X)=='$' \ |
| 139 | || (X)=='~' || (X)=='-' || (X)=='_' || (X)==other) |
| 140 | |
| 141 | if( zIn==0 ) return 0; |
| 142 | if( n<0 ) n = strlen(zIn); |
| 143 | other = encodeSlash ? 'a' : '/'; |
| 144 | while( i<n && (c = zIn[i])!=0 ){ |
| 145 | if( IsSafeChar(c) || c==' ' ){ |
| 146 | count++; |
| 147 | }else{ |
| 148 | count += 3; |
| 149 |
| --- src/encode.c | |
| +++ src/encode.c | |
| @@ -131,18 +131,17 @@ | |
| 131 | static char *EncodeHttp(const char *zIn, int n, int encodeSlash){ |
| 132 | int c; |
| 133 | int i = 0; |
| 134 | int count = 0; |
| 135 | char *zOut; |
| 136 | # define IsSafeChar(X) \ |
| 137 | (fossil_isalnum(X) || (X)=='.' || (X)=='$' \ |
| 138 | || (X)=='~' || (X)=='-' || (X)=='_' \ |
| 139 | || (!encodeSlash && ((X)=='/' || (X)==':'))) |
| 140 | |
| 141 | if( zIn==0 ) return 0; |
| 142 | if( n<0 ) n = strlen(zIn); |
| 143 | while( i<n && (c = zIn[i])!=0 ){ |
| 144 | if( IsSafeChar(c) || c==' ' ){ |
| 145 | count++; |
| 146 | }else{ |
| 147 | count += 3; |
| 148 |
+2
-3
| --- src/encode.c | ||
| +++ src/encode.c | ||
| @@ -131,18 +131,17 @@ | ||
| 131 | 131 | static char *EncodeHttp(const char *zIn, int n, int encodeSlash){ |
| 132 | 132 | int c; |
| 133 | 133 | int i = 0; |
| 134 | 134 | int count = 0; |
| 135 | 135 | char *zOut; |
| 136 | - int other; | |
| 137 | 136 | # define IsSafeChar(X) \ |
| 138 | 137 | (fossil_isalnum(X) || (X)=='.' || (X)=='$' \ |
| 139 | - || (X)=='~' || (X)=='-' || (X)=='_' || (X)==other) | |
| 138 | + || (X)=='~' || (X)=='-' || (X)=='_' \ | |
| 139 | + || (!encodeSlash && ((X)=='/' || (X)==':'))) | |
| 140 | 140 | |
| 141 | 141 | if( zIn==0 ) return 0; |
| 142 | 142 | if( n<0 ) n = strlen(zIn); |
| 143 | - other = encodeSlash ? 'a' : '/'; | |
| 144 | 143 | while( i<n && (c = zIn[i])!=0 ){ |
| 145 | 144 | if( IsSafeChar(c) || c==' ' ){ |
| 146 | 145 | count++; |
| 147 | 146 | }else{ |
| 148 | 147 | count += 3; |
| 149 | 148 |
| --- src/encode.c | |
| +++ src/encode.c | |
| @@ -131,18 +131,17 @@ | |
| 131 | static char *EncodeHttp(const char *zIn, int n, int encodeSlash){ |
| 132 | int c; |
| 133 | int i = 0; |
| 134 | int count = 0; |
| 135 | char *zOut; |
| 136 | int other; |
| 137 | # define IsSafeChar(X) \ |
| 138 | (fossil_isalnum(X) || (X)=='.' || (X)=='$' \ |
| 139 | || (X)=='~' || (X)=='-' || (X)=='_' || (X)==other) |
| 140 | |
| 141 | if( zIn==0 ) return 0; |
| 142 | if( n<0 ) n = strlen(zIn); |
| 143 | other = encodeSlash ? 'a' : '/'; |
| 144 | while( i<n && (c = zIn[i])!=0 ){ |
| 145 | if( IsSafeChar(c) || c==' ' ){ |
| 146 | count++; |
| 147 | }else{ |
| 148 | count += 3; |
| 149 |
| --- src/encode.c | |
| +++ src/encode.c | |
| @@ -131,18 +131,17 @@ | |
| 131 | static char *EncodeHttp(const char *zIn, int n, int encodeSlash){ |
| 132 | int c; |
| 133 | int i = 0; |
| 134 | int count = 0; |
| 135 | char *zOut; |
| 136 | # define IsSafeChar(X) \ |
| 137 | (fossil_isalnum(X) || (X)=='.' || (X)=='$' \ |
| 138 | || (X)=='~' || (X)=='-' || (X)=='_' \ |
| 139 | || (!encodeSlash && ((X)=='/' || (X)==':'))) |
| 140 | |
| 141 | if( zIn==0 ) return 0; |
| 142 | if( n<0 ) n = strlen(zIn); |
| 143 | while( i<n && (c = zIn[i])!=0 ){ |
| 144 | if( IsSafeChar(c) || c==' ' ){ |
| 145 | count++; |
| 146 | }else{ |
| 147 | count += 3; |
| 148 |
+52
-34
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -490,50 +490,68 @@ | ||
| 490 | 490 | ** * Does not contain two or more "/" characters in a row. |
| 491 | 491 | ** * Contains at least one character |
| 492 | 492 | ** |
| 493 | 493 | ** Invalid UTF8 characters result in a false return if bStrictUtf8 is |
| 494 | 494 | ** true. If bStrictUtf8 is false, invalid UTF8 characters are silently |
| 495 | -** ignored. | |
| 495 | +** ignored. See http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences | |
| 496 | +** and http://en.wikipedia.org/wiki/Unicode (for the noncharacters) | |
| 497 | +** | |
| 498 | +** The bStrictUtf8 flag is true for new inputs, but is false when parsing | |
| 499 | +** legacy manifests, for backwards compatibility. | |
| 496 | 500 | */ |
| 497 | 501 | int file_is_simple_pathname(const char *z, int bStrictUtf8){ |
| 498 | 502 | int i; |
| 499 | - char c = z[0]; | |
| 503 | + unsigned char c = (unsigned char) z[0]; | |
| 500 | 504 | char maskNonAscii = bStrictUtf8 ? 0x80 : 0x00; |
| 501 | 505 | if( c=='/' || c==0 ) return 0; |
| 502 | 506 | if( c=='.' ){ |
| 503 | 507 | if( z[1]=='/' || z[1]==0 ) return 0; |
| 504 | 508 | if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0; |
| 505 | 509 | } |
| 506 | - for(i=0; (c=z[i])!=0; i++){ | |
| 507 | - if( c & maskNonAscii ){ | |
| 508 | - if( (c & 0xf0) == 0xf0 ) { | |
| 509 | - /* Unicode characters > U+FFFF are not supported. | |
| 510 | - * Windows XP and earlier cannot handle them. | |
| 511 | - */ | |
| 512 | - return 0; | |
| 513 | - } | |
| 514 | - if( (c & 0xf0) == 0xe0 ) { | |
| 515 | - /* This is a 3-byte UTF-8 character */ | |
| 516 | - if ( (c & 0xfe) == 0xee ){ | |
| 517 | - /* Range U+E000 - U+FFFF (Starting with 0xee or 0xef in UTF-8 ) */ | |
| 518 | - if ( (c & 1) && ((z[i+1] & 0xff) >= 0xa4) ){ | |
| 519 | - /* But exclude U+F900 - U+FFFF (0xef followed by byte >= 0xa4), | |
| 520 | - * which contain valid characters. */ | |
| 521 | - continue; | |
| 522 | - } | |
| 523 | - /* Unicode character in the range U+E000 - U+F8FF are for | |
| 524 | - * private use, they shouldn't occur in filenames. */ | |
| 525 | - return 0; | |
| 526 | - } | |
| 527 | - if( ((c & 0xff) == 0xed) && ((z[i+1] & 0xe0) == 0xa0) ){ | |
| 528 | - /* Unicode character in the range U+D800 - U+DFFF are for | |
| 529 | - * surrogate pairs, they shouldn't occur in filenames. */ | |
| 530 | - return 0; | |
| 531 | - } | |
| 532 | - } | |
| 533 | - } | |
| 534 | - if( c=='\\' ){ | |
| 510 | + for(i=0; (c=(unsigned char)z[i])!=0; i++){ | |
| 511 | + if( c & maskNonAscii ){ | |
| 512 | + if( (z[++i]&0xc0)!=0x80 ){ | |
| 513 | + /* Invalid first continuation byte */ | |
| 514 | + return 0; | |
| 515 | + } | |
| 516 | + if( c<0xc2 ){ | |
| 517 | + /* Invalid 1-byte UTF-8 sequence, or 2-byte overlong form. */ | |
| 518 | + return 0; | |
| 519 | + }else if( (c&0xe0)==0xe0 ){ | |
| 520 | + /* 3-byte or more */ | |
| 521 | + int unicode; | |
| 522 | + if( c&0x10 ){ | |
| 523 | + /* Unicode characters > U+FFFF are not supported. | |
| 524 | + * Windows XP and earlier cannot handle them. | |
| 525 | + */ | |
| 526 | + return 0; | |
| 527 | + } | |
| 528 | + /* This is a 3-byte UTF-8 character */ | |
| 529 | + unicode = ((c&0x0f)<<12) + ((z[i]&0x3f)<<6) + (z[i+1]&0x3f); | |
| 530 | + if( unicode <= 0x07ff ){ | |
| 531 | + /* overlong form */ | |
| 532 | + return 0; | |
| 533 | + }else if( unicode>=0xe000 ){ | |
| 534 | + /* U+E000..U+FFFF */ | |
| 535 | + if( (unicode<=0xf8ff) || (unicode>=0xfffe) ){ | |
| 536 | + /* U+E000..U+F8FF are for private use. | |
| 537 | + * U+FFFE..U+FFFF are noncharacters. */ | |
| 538 | + return 0; | |
| 539 | + } else if( (unicode>=0xfdd0) && (unicode<=0xfdef) ){ | |
| 540 | + /* U+FDD0..U+FDEF are noncharacters. */ | |
| 541 | + return 0; | |
| 542 | + } | |
| 543 | + }else if( (unicode>=0xd800) && (unicode<=0xdfff) ){ | |
| 544 | + /* U+D800..U+DFFF are for surrogate pairs. */ | |
| 545 | + return 0; | |
| 546 | + } | |
| 547 | + if( (z[++i]&0xc0)!=0x80 ){ | |
| 548 | + /* Invalid second continuation byte */ | |
| 549 | + return 0; | |
| 550 | + } | |
| 551 | + } | |
| 552 | + }else if( bStrictUtf8 && (c=='\\') ){ | |
| 535 | 553 | return 0; |
| 536 | 554 | } |
| 537 | 555 | if( c=='/' ){ |
| 538 | 556 | if( z[i+1]=='/' ) return 0; |
| 539 | 557 | if( z[i+1]=='.' ){ |
| @@ -583,11 +601,11 @@ | ||
| 583 | 601 | if( z[i]=='\\' ) z[i] = '/'; |
| 584 | 602 | } |
| 585 | 603 | #endif |
| 586 | 604 | |
| 587 | 605 | /* Removing trailing "/" characters */ |
| 588 | - if ( !slash ){ | |
| 606 | + if( !slash ){ | |
| 589 | 607 | while( n>1 && z[n-1]=='/' ){ n--; } |
| 590 | 608 | } |
| 591 | 609 | |
| 592 | 610 | /* Remove duplicate '/' characters. Except, two // at the beginning |
| 593 | 611 | ** of a pathname is allowed since this is important on windows. */ |
| @@ -840,11 +858,11 @@ | ||
| 840 | 858 | if( zPwd[i]==0 ){ |
| 841 | 859 | blob_append(pOut, ".", 1); |
| 842 | 860 | }else{ |
| 843 | 861 | blob_append(pOut, "..", 2); |
| 844 | 862 | for(j=i+1; zPwd[j]; j++){ |
| 845 | - if( zPwd[j]=='/' ) { | |
| 863 | + if( zPwd[j]=='/' ){ | |
| 846 | 864 | blob_append(pOut, "/..", 3); |
| 847 | 865 | } |
| 848 | 866 | } |
| 849 | 867 | } |
| 850 | 868 | return; |
| @@ -857,11 +875,11 @@ | ||
| 857 | 875 | return; |
| 858 | 876 | } |
| 859 | 877 | while( zPath[i-1]!='/' ){ i--; } |
| 860 | 878 | blob_set(&tmp, "../"); |
| 861 | 879 | for(j=i; zPwd[j]; j++){ |
| 862 | - if( zPwd[j]=='/' ) { | |
| 880 | + if( zPwd[j]=='/' ){ | |
| 863 | 881 | blob_append(&tmp, "../", 3); |
| 864 | 882 | } |
| 865 | 883 | } |
| 866 | 884 | blob_append(&tmp, &zPath[i], -1); |
| 867 | 885 | blob_reset(pOut); |
| 868 | 886 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -490,50 +490,68 @@ | |
| 490 | ** * Does not contain two or more "/" characters in a row. |
| 491 | ** * Contains at least one character |
| 492 | ** |
| 493 | ** Invalid UTF8 characters result in a false return if bStrictUtf8 is |
| 494 | ** true. If bStrictUtf8 is false, invalid UTF8 characters are silently |
| 495 | ** ignored. |
| 496 | */ |
| 497 | int file_is_simple_pathname(const char *z, int bStrictUtf8){ |
| 498 | int i; |
| 499 | char c = z[0]; |
| 500 | char maskNonAscii = bStrictUtf8 ? 0x80 : 0x00; |
| 501 | if( c=='/' || c==0 ) return 0; |
| 502 | if( c=='.' ){ |
| 503 | if( z[1]=='/' || z[1]==0 ) return 0; |
| 504 | if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0; |
| 505 | } |
| 506 | for(i=0; (c=z[i])!=0; i++){ |
| 507 | if( c & maskNonAscii ){ |
| 508 | if( (c & 0xf0) == 0xf0 ) { |
| 509 | /* Unicode characters > U+FFFF are not supported. |
| 510 | * Windows XP and earlier cannot handle them. |
| 511 | */ |
| 512 | return 0; |
| 513 | } |
| 514 | if( (c & 0xf0) == 0xe0 ) { |
| 515 | /* This is a 3-byte UTF-8 character */ |
| 516 | if ( (c & 0xfe) == 0xee ){ |
| 517 | /* Range U+E000 - U+FFFF (Starting with 0xee or 0xef in UTF-8 ) */ |
| 518 | if ( (c & 1) && ((z[i+1] & 0xff) >= 0xa4) ){ |
| 519 | /* But exclude U+F900 - U+FFFF (0xef followed by byte >= 0xa4), |
| 520 | * which contain valid characters. */ |
| 521 | continue; |
| 522 | } |
| 523 | /* Unicode character in the range U+E000 - U+F8FF are for |
| 524 | * private use, they shouldn't occur in filenames. */ |
| 525 | return 0; |
| 526 | } |
| 527 | if( ((c & 0xff) == 0xed) && ((z[i+1] & 0xe0) == 0xa0) ){ |
| 528 | /* Unicode character in the range U+D800 - U+DFFF are for |
| 529 | * surrogate pairs, they shouldn't occur in filenames. */ |
| 530 | return 0; |
| 531 | } |
| 532 | } |
| 533 | } |
| 534 | if( c=='\\' ){ |
| 535 | return 0; |
| 536 | } |
| 537 | if( c=='/' ){ |
| 538 | if( z[i+1]=='/' ) return 0; |
| 539 | if( z[i+1]=='.' ){ |
| @@ -583,11 +601,11 @@ | |
| 583 | if( z[i]=='\\' ) z[i] = '/'; |
| 584 | } |
| 585 | #endif |
| 586 | |
| 587 | /* Removing trailing "/" characters */ |
| 588 | if ( !slash ){ |
| 589 | while( n>1 && z[n-1]=='/' ){ n--; } |
| 590 | } |
| 591 | |
| 592 | /* Remove duplicate '/' characters. Except, two // at the beginning |
| 593 | ** of a pathname is allowed since this is important on windows. */ |
| @@ -840,11 +858,11 @@ | |
| 840 | if( zPwd[i]==0 ){ |
| 841 | blob_append(pOut, ".", 1); |
| 842 | }else{ |
| 843 | blob_append(pOut, "..", 2); |
| 844 | for(j=i+1; zPwd[j]; j++){ |
| 845 | if( zPwd[j]=='/' ) { |
| 846 | blob_append(pOut, "/..", 3); |
| 847 | } |
| 848 | } |
| 849 | } |
| 850 | return; |
| @@ -857,11 +875,11 @@ | |
| 857 | return; |
| 858 | } |
| 859 | while( zPath[i-1]!='/' ){ i--; } |
| 860 | blob_set(&tmp, "../"); |
| 861 | for(j=i; zPwd[j]; j++){ |
| 862 | if( zPwd[j]=='/' ) { |
| 863 | blob_append(&tmp, "../", 3); |
| 864 | } |
| 865 | } |
| 866 | blob_append(&tmp, &zPath[i], -1); |
| 867 | blob_reset(pOut); |
| 868 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -490,50 +490,68 @@ | |
| 490 | ** * Does not contain two or more "/" characters in a row. |
| 491 | ** * Contains at least one character |
| 492 | ** |
| 493 | ** Invalid UTF8 characters result in a false return if bStrictUtf8 is |
| 494 | ** true. If bStrictUtf8 is false, invalid UTF8 characters are silently |
| 495 | ** ignored. See http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences |
| 496 | ** and http://en.wikipedia.org/wiki/Unicode (for the noncharacters) |
| 497 | ** |
| 498 | ** The bStrictUtf8 flag is true for new inputs, but is false when parsing |
| 499 | ** legacy manifests, for backwards compatibility. |
| 500 | */ |
| 501 | int file_is_simple_pathname(const char *z, int bStrictUtf8){ |
| 502 | int i; |
| 503 | unsigned char c = (unsigned char) z[0]; |
| 504 | char maskNonAscii = bStrictUtf8 ? 0x80 : 0x00; |
| 505 | if( c=='/' || c==0 ) return 0; |
| 506 | if( c=='.' ){ |
| 507 | if( z[1]=='/' || z[1]==0 ) return 0; |
| 508 | if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0; |
| 509 | } |
| 510 | for(i=0; (c=(unsigned char)z[i])!=0; i++){ |
| 511 | if( c & maskNonAscii ){ |
| 512 | if( (z[++i]&0xc0)!=0x80 ){ |
| 513 | /* Invalid first continuation byte */ |
| 514 | return 0; |
| 515 | } |
| 516 | if( c<0xc2 ){ |
| 517 | /* Invalid 1-byte UTF-8 sequence, or 2-byte overlong form. */ |
| 518 | return 0; |
| 519 | }else if( (c&0xe0)==0xe0 ){ |
| 520 | /* 3-byte or more */ |
| 521 | int unicode; |
| 522 | if( c&0x10 ){ |
| 523 | /* Unicode characters > U+FFFF are not supported. |
| 524 | * Windows XP and earlier cannot handle them. |
| 525 | */ |
| 526 | return 0; |
| 527 | } |
| 528 | /* This is a 3-byte UTF-8 character */ |
| 529 | unicode = ((c&0x0f)<<12) + ((z[i]&0x3f)<<6) + (z[i+1]&0x3f); |
| 530 | if( unicode <= 0x07ff ){ |
| 531 | /* overlong form */ |
| 532 | return 0; |
| 533 | }else if( unicode>=0xe000 ){ |
| 534 | /* U+E000..U+FFFF */ |
| 535 | if( (unicode<=0xf8ff) || (unicode>=0xfffe) ){ |
| 536 | /* U+E000..U+F8FF are for private use. |
| 537 | * U+FFFE..U+FFFF are noncharacters. */ |
| 538 | return 0; |
| 539 | } else if( (unicode>=0xfdd0) && (unicode<=0xfdef) ){ |
| 540 | /* U+FDD0..U+FDEF are noncharacters. */ |
| 541 | return 0; |
| 542 | } |
| 543 | }else if( (unicode>=0xd800) && (unicode<=0xdfff) ){ |
| 544 | /* U+D800..U+DFFF are for surrogate pairs. */ |
| 545 | return 0; |
| 546 | } |
| 547 | if( (z[++i]&0xc0)!=0x80 ){ |
| 548 | /* Invalid second continuation byte */ |
| 549 | return 0; |
| 550 | } |
| 551 | } |
| 552 | }else if( bStrictUtf8 && (c=='\\') ){ |
| 553 | return 0; |
| 554 | } |
| 555 | if( c=='/' ){ |
| 556 | if( z[i+1]=='/' ) return 0; |
| 557 | if( z[i+1]=='.' ){ |
| @@ -583,11 +601,11 @@ | |
| 601 | if( z[i]=='\\' ) z[i] = '/'; |
| 602 | } |
| 603 | #endif |
| 604 | |
| 605 | /* Removing trailing "/" characters */ |
| 606 | if( !slash ){ |
| 607 | while( n>1 && z[n-1]=='/' ){ n--; } |
| 608 | } |
| 609 | |
| 610 | /* Remove duplicate '/' characters. Except, two // at the beginning |
| 611 | ** of a pathname is allowed since this is important on windows. */ |
| @@ -840,11 +858,11 @@ | |
| 858 | if( zPwd[i]==0 ){ |
| 859 | blob_append(pOut, ".", 1); |
| 860 | }else{ |
| 861 | blob_append(pOut, "..", 2); |
| 862 | for(j=i+1; zPwd[j]; j++){ |
| 863 | if( zPwd[j]=='/' ){ |
| 864 | blob_append(pOut, "/..", 3); |
| 865 | } |
| 866 | } |
| 867 | } |
| 868 | return; |
| @@ -857,11 +875,11 @@ | |
| 875 | return; |
| 876 | } |
| 877 | while( zPath[i-1]!='/' ){ i--; } |
| 878 | blob_set(&tmp, "../"); |
| 879 | for(j=i; zPwd[j]; j++){ |
| 880 | if( zPwd[j]=='/' ){ |
| 881 | blob_append(&tmp, "../", 3); |
| 882 | } |
| 883 | } |
| 884 | blob_append(&tmp, &zPath[i], -1); |
| 885 | blob_reset(pOut); |
| 886 |
+52
-34
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -490,50 +490,68 @@ | ||
| 490 | 490 | ** * Does not contain two or more "/" characters in a row. |
| 491 | 491 | ** * Contains at least one character |
| 492 | 492 | ** |
| 493 | 493 | ** Invalid UTF8 characters result in a false return if bStrictUtf8 is |
| 494 | 494 | ** true. If bStrictUtf8 is false, invalid UTF8 characters are silently |
| 495 | -** ignored. | |
| 495 | +** ignored. See http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences | |
| 496 | +** and http://en.wikipedia.org/wiki/Unicode (for the noncharacters) | |
| 497 | +** | |
| 498 | +** The bStrictUtf8 flag is true for new inputs, but is false when parsing | |
| 499 | +** legacy manifests, for backwards compatibility. | |
| 496 | 500 | */ |
| 497 | 501 | int file_is_simple_pathname(const char *z, int bStrictUtf8){ |
| 498 | 502 | int i; |
| 499 | - char c = z[0]; | |
| 503 | + unsigned char c = (unsigned char) z[0]; | |
| 500 | 504 | char maskNonAscii = bStrictUtf8 ? 0x80 : 0x00; |
| 501 | 505 | if( c=='/' || c==0 ) return 0; |
| 502 | 506 | if( c=='.' ){ |
| 503 | 507 | if( z[1]=='/' || z[1]==0 ) return 0; |
| 504 | 508 | if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0; |
| 505 | 509 | } |
| 506 | - for(i=0; (c=z[i])!=0; i++){ | |
| 507 | - if( c & maskNonAscii ){ | |
| 508 | - if( (c & 0xf0) == 0xf0 ) { | |
| 509 | - /* Unicode characters > U+FFFF are not supported. | |
| 510 | - * Windows XP and earlier cannot handle them. | |
| 511 | - */ | |
| 512 | - return 0; | |
| 513 | - } | |
| 514 | - if( (c & 0xf0) == 0xe0 ) { | |
| 515 | - /* This is a 3-byte UTF-8 character */ | |
| 516 | - if ( (c & 0xfe) == 0xee ){ | |
| 517 | - /* Range U+E000 - U+FFFF (Starting with 0xee or 0xef in UTF-8 ) */ | |
| 518 | - if ( (c & 1) && ((z[i+1] & 0xff) >= 0xa4) ){ | |
| 519 | - /* But exclude U+F900 - U+FFFF (0xef followed by byte >= 0xa4), | |
| 520 | - * which contain valid characters. */ | |
| 521 | - continue; | |
| 522 | - } | |
| 523 | - /* Unicode character in the range U+E000 - U+F8FF are for | |
| 524 | - * private use, they shouldn't occur in filenames. */ | |
| 525 | - return 0; | |
| 526 | - } | |
| 527 | - if( ((c & 0xff) == 0xed) && ((z[i+1] & 0xe0) == 0xa0) ){ | |
| 528 | - /* Unicode character in the range U+D800 - U+DFFF are for | |
| 529 | - * surrogate pairs, they shouldn't occur in filenames. */ | |
| 530 | - return 0; | |
| 531 | - } | |
| 532 | - } | |
| 533 | - } | |
| 534 | - if( c=='\\' ){ | |
| 510 | + for(i=0; (c=(unsigned char)z[i])!=0; i++){ | |
| 511 | + if( c & maskNonAscii ){ | |
| 512 | + if( (z[++i]&0xc0)!=0x80 ){ | |
| 513 | + /* Invalid first continuation byte */ | |
| 514 | + return 0; | |
| 515 | + } | |
| 516 | + if( c<0xc2 ){ | |
| 517 | + /* Invalid 1-byte UTF-8 sequence, or 2-byte overlong form. */ | |
| 518 | + return 0; | |
| 519 | + }else if( (c&0xe0)==0xe0 ){ | |
| 520 | + /* 3-byte or more */ | |
| 521 | + int unicode; | |
| 522 | + if( c&0x10 ){ | |
| 523 | + /* Unicode characters > U+FFFF are not supported. | |
| 524 | + * Windows XP and earlier cannot handle them. | |
| 525 | + */ | |
| 526 | + return 0; | |
| 527 | + } | |
| 528 | + /* This is a 3-byte UTF-8 character */ | |
| 529 | + unicode = ((c&0x0f)<<12) + ((z[i]&0x3f)<<6) + (z[i+1]&0x3f); | |
| 530 | + if( unicode <= 0x07ff ){ | |
| 531 | + /* overlong form */ | |
| 532 | + return 0; | |
| 533 | + }else if( unicode>=0xe000 ){ | |
| 534 | + /* U+E000..U+FFFF */ | |
| 535 | + if( (unicode<=0xf8ff) || (unicode>=0xfffe) ){ | |
| 536 | + /* U+E000..U+F8FF are for private use. | |
| 537 | + * U+FFFE..U+FFFF are noncharacters. */ | |
| 538 | + return 0; | |
| 539 | + } else if( (unicode>=0xfdd0) && (unicode<=0xfdef) ){ | |
| 540 | + /* U+FDD0..U+FDEF are noncharacters. */ | |
| 541 | + return 0; | |
| 542 | + } | |
| 543 | + }else if( (unicode>=0xd800) && (unicode<=0xdfff) ){ | |
| 544 | + /* U+D800..U+DFFF are for surrogate pairs. */ | |
| 545 | + return 0; | |
| 546 | + } | |
| 547 | + if( (z[++i]&0xc0)!=0x80 ){ | |
| 548 | + /* Invalid second continuation byte */ | |
| 549 | + return 0; | |
| 550 | + } | |
| 551 | + } | |
| 552 | + }else if( bStrictUtf8 && (c=='\\') ){ | |
| 535 | 553 | return 0; |
| 536 | 554 | } |
| 537 | 555 | if( c=='/' ){ |
| 538 | 556 | if( z[i+1]=='/' ) return 0; |
| 539 | 557 | if( z[i+1]=='.' ){ |
| @@ -583,11 +601,11 @@ | ||
| 583 | 601 | if( z[i]=='\\' ) z[i] = '/'; |
| 584 | 602 | } |
| 585 | 603 | #endif |
| 586 | 604 | |
| 587 | 605 | /* Removing trailing "/" characters */ |
| 588 | - if ( !slash ){ | |
| 606 | + if( !slash ){ | |
| 589 | 607 | while( n>1 && z[n-1]=='/' ){ n--; } |
| 590 | 608 | } |
| 591 | 609 | |
| 592 | 610 | /* Remove duplicate '/' characters. Except, two // at the beginning |
| 593 | 611 | ** of a pathname is allowed since this is important on windows. */ |
| @@ -840,11 +858,11 @@ | ||
| 840 | 858 | if( zPwd[i]==0 ){ |
| 841 | 859 | blob_append(pOut, ".", 1); |
| 842 | 860 | }else{ |
| 843 | 861 | blob_append(pOut, "..", 2); |
| 844 | 862 | for(j=i+1; zPwd[j]; j++){ |
| 845 | - if( zPwd[j]=='/' ) { | |
| 863 | + if( zPwd[j]=='/' ){ | |
| 846 | 864 | blob_append(pOut, "/..", 3); |
| 847 | 865 | } |
| 848 | 866 | } |
| 849 | 867 | } |
| 850 | 868 | return; |
| @@ -857,11 +875,11 @@ | ||
| 857 | 875 | return; |
| 858 | 876 | } |
| 859 | 877 | while( zPath[i-1]!='/' ){ i--; } |
| 860 | 878 | blob_set(&tmp, "../"); |
| 861 | 879 | for(j=i; zPwd[j]; j++){ |
| 862 | - if( zPwd[j]=='/' ) { | |
| 880 | + if( zPwd[j]=='/' ){ | |
| 863 | 881 | blob_append(&tmp, "../", 3); |
| 864 | 882 | } |
| 865 | 883 | } |
| 866 | 884 | blob_append(&tmp, &zPath[i], -1); |
| 867 | 885 | blob_reset(pOut); |
| 868 | 886 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -490,50 +490,68 @@ | |
| 490 | ** * Does not contain two or more "/" characters in a row. |
| 491 | ** * Contains at least one character |
| 492 | ** |
| 493 | ** Invalid UTF8 characters result in a false return if bStrictUtf8 is |
| 494 | ** true. If bStrictUtf8 is false, invalid UTF8 characters are silently |
| 495 | ** ignored. |
| 496 | */ |
| 497 | int file_is_simple_pathname(const char *z, int bStrictUtf8){ |
| 498 | int i; |
| 499 | char c = z[0]; |
| 500 | char maskNonAscii = bStrictUtf8 ? 0x80 : 0x00; |
| 501 | if( c=='/' || c==0 ) return 0; |
| 502 | if( c=='.' ){ |
| 503 | if( z[1]=='/' || z[1]==0 ) return 0; |
| 504 | if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0; |
| 505 | } |
| 506 | for(i=0; (c=z[i])!=0; i++){ |
| 507 | if( c & maskNonAscii ){ |
| 508 | if( (c & 0xf0) == 0xf0 ) { |
| 509 | /* Unicode characters > U+FFFF are not supported. |
| 510 | * Windows XP and earlier cannot handle them. |
| 511 | */ |
| 512 | return 0; |
| 513 | } |
| 514 | if( (c & 0xf0) == 0xe0 ) { |
| 515 | /* This is a 3-byte UTF-8 character */ |
| 516 | if ( (c & 0xfe) == 0xee ){ |
| 517 | /* Range U+E000 - U+FFFF (Starting with 0xee or 0xef in UTF-8 ) */ |
| 518 | if ( (c & 1) && ((z[i+1] & 0xff) >= 0xa4) ){ |
| 519 | /* But exclude U+F900 - U+FFFF (0xef followed by byte >= 0xa4), |
| 520 | * which contain valid characters. */ |
| 521 | continue; |
| 522 | } |
| 523 | /* Unicode character in the range U+E000 - U+F8FF are for |
| 524 | * private use, they shouldn't occur in filenames. */ |
| 525 | return 0; |
| 526 | } |
| 527 | if( ((c & 0xff) == 0xed) && ((z[i+1] & 0xe0) == 0xa0) ){ |
| 528 | /* Unicode character in the range U+D800 - U+DFFF are for |
| 529 | * surrogate pairs, they shouldn't occur in filenames. */ |
| 530 | return 0; |
| 531 | } |
| 532 | } |
| 533 | } |
| 534 | if( c=='\\' ){ |
| 535 | return 0; |
| 536 | } |
| 537 | if( c=='/' ){ |
| 538 | if( z[i+1]=='/' ) return 0; |
| 539 | if( z[i+1]=='.' ){ |
| @@ -583,11 +601,11 @@ | |
| 583 | if( z[i]=='\\' ) z[i] = '/'; |
| 584 | } |
| 585 | #endif |
| 586 | |
| 587 | /* Removing trailing "/" characters */ |
| 588 | if ( !slash ){ |
| 589 | while( n>1 && z[n-1]=='/' ){ n--; } |
| 590 | } |
| 591 | |
| 592 | /* Remove duplicate '/' characters. Except, two // at the beginning |
| 593 | ** of a pathname is allowed since this is important on windows. */ |
| @@ -840,11 +858,11 @@ | |
| 840 | if( zPwd[i]==0 ){ |
| 841 | blob_append(pOut, ".", 1); |
| 842 | }else{ |
| 843 | blob_append(pOut, "..", 2); |
| 844 | for(j=i+1; zPwd[j]; j++){ |
| 845 | if( zPwd[j]=='/' ) { |
| 846 | blob_append(pOut, "/..", 3); |
| 847 | } |
| 848 | } |
| 849 | } |
| 850 | return; |
| @@ -857,11 +875,11 @@ | |
| 857 | return; |
| 858 | } |
| 859 | while( zPath[i-1]!='/' ){ i--; } |
| 860 | blob_set(&tmp, "../"); |
| 861 | for(j=i; zPwd[j]; j++){ |
| 862 | if( zPwd[j]=='/' ) { |
| 863 | blob_append(&tmp, "../", 3); |
| 864 | } |
| 865 | } |
| 866 | blob_append(&tmp, &zPath[i], -1); |
| 867 | blob_reset(pOut); |
| 868 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -490,50 +490,68 @@ | |
| 490 | ** * Does not contain two or more "/" characters in a row. |
| 491 | ** * Contains at least one character |
| 492 | ** |
| 493 | ** Invalid UTF8 characters result in a false return if bStrictUtf8 is |
| 494 | ** true. If bStrictUtf8 is false, invalid UTF8 characters are silently |
| 495 | ** ignored. See http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences |
| 496 | ** and http://en.wikipedia.org/wiki/Unicode (for the noncharacters) |
| 497 | ** |
| 498 | ** The bStrictUtf8 flag is true for new inputs, but is false when parsing |
| 499 | ** legacy manifests, for backwards compatibility. |
| 500 | */ |
| 501 | int file_is_simple_pathname(const char *z, int bStrictUtf8){ |
| 502 | int i; |
| 503 | unsigned char c = (unsigned char) z[0]; |
| 504 | char maskNonAscii = bStrictUtf8 ? 0x80 : 0x00; |
| 505 | if( c=='/' || c==0 ) return 0; |
| 506 | if( c=='.' ){ |
| 507 | if( z[1]=='/' || z[1]==0 ) return 0; |
| 508 | if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0; |
| 509 | } |
| 510 | for(i=0; (c=(unsigned char)z[i])!=0; i++){ |
| 511 | if( c & maskNonAscii ){ |
| 512 | if( (z[++i]&0xc0)!=0x80 ){ |
| 513 | /* Invalid first continuation byte */ |
| 514 | return 0; |
| 515 | } |
| 516 | if( c<0xc2 ){ |
| 517 | /* Invalid 1-byte UTF-8 sequence, or 2-byte overlong form. */ |
| 518 | return 0; |
| 519 | }else if( (c&0xe0)==0xe0 ){ |
| 520 | /* 3-byte or more */ |
| 521 | int unicode; |
| 522 | if( c&0x10 ){ |
| 523 | /* Unicode characters > U+FFFF are not supported. |
| 524 | * Windows XP and earlier cannot handle them. |
| 525 | */ |
| 526 | return 0; |
| 527 | } |
| 528 | /* This is a 3-byte UTF-8 character */ |
| 529 | unicode = ((c&0x0f)<<12) + ((z[i]&0x3f)<<6) + (z[i+1]&0x3f); |
| 530 | if( unicode <= 0x07ff ){ |
| 531 | /* overlong form */ |
| 532 | return 0; |
| 533 | }else if( unicode>=0xe000 ){ |
| 534 | /* U+E000..U+FFFF */ |
| 535 | if( (unicode<=0xf8ff) || (unicode>=0xfffe) ){ |
| 536 | /* U+E000..U+F8FF are for private use. |
| 537 | * U+FFFE..U+FFFF are noncharacters. */ |
| 538 | return 0; |
| 539 | } else if( (unicode>=0xfdd0) && (unicode<=0xfdef) ){ |
| 540 | /* U+FDD0..U+FDEF are noncharacters. */ |
| 541 | return 0; |
| 542 | } |
| 543 | }else if( (unicode>=0xd800) && (unicode<=0xdfff) ){ |
| 544 | /* U+D800..U+DFFF are for surrogate pairs. */ |
| 545 | return 0; |
| 546 | } |
| 547 | if( (z[++i]&0xc0)!=0x80 ){ |
| 548 | /* Invalid second continuation byte */ |
| 549 | return 0; |
| 550 | } |
| 551 | } |
| 552 | }else if( bStrictUtf8 && (c=='\\') ){ |
| 553 | return 0; |
| 554 | } |
| 555 | if( c=='/' ){ |
| 556 | if( z[i+1]=='/' ) return 0; |
| 557 | if( z[i+1]=='.' ){ |
| @@ -583,11 +601,11 @@ | |
| 601 | if( z[i]=='\\' ) z[i] = '/'; |
| 602 | } |
| 603 | #endif |
| 604 | |
| 605 | /* Removing trailing "/" characters */ |
| 606 | if( !slash ){ |
| 607 | while( n>1 && z[n-1]=='/' ){ n--; } |
| 608 | } |
| 609 | |
| 610 | /* Remove duplicate '/' characters. Except, two // at the beginning |
| 611 | ** of a pathname is allowed since this is important on windows. */ |
| @@ -840,11 +858,11 @@ | |
| 858 | if( zPwd[i]==0 ){ |
| 859 | blob_append(pOut, ".", 1); |
| 860 | }else{ |
| 861 | blob_append(pOut, "..", 2); |
| 862 | for(j=i+1; zPwd[j]; j++){ |
| 863 | if( zPwd[j]=='/' ){ |
| 864 | blob_append(pOut, "/..", 3); |
| 865 | } |
| 866 | } |
| 867 | } |
| 868 | return; |
| @@ -857,11 +875,11 @@ | |
| 875 | return; |
| 876 | } |
| 877 | while( zPath[i-1]!='/' ){ i--; } |
| 878 | blob_set(&tmp, "../"); |
| 879 | for(j=i; zPwd[j]; j++){ |
| 880 | if( zPwd[j]=='/' ){ |
| 881 | blob_append(&tmp, "../", 3); |
| 882 | } |
| 883 | } |
| 884 | blob_append(&tmp, &zPath[i], -1); |
| 885 | blob_reset(pOut); |
| 886 |
+6
-2
| --- src/http.c | ||
| +++ src/http.c | ||
| @@ -130,11 +130,11 @@ | ||
| 130 | 130 | ** |
| 131 | 131 | ** The server address is contain in the "g" global structure. The |
| 132 | 132 | ** url_parse() routine should have been called prior to this routine |
| 133 | 133 | ** in order to fill this structure appropriately. |
| 134 | 134 | */ |
| 135 | -int http_exchange(Blob *pSend, Blob *pReply, int useLogin){ | |
| 135 | +int http_exchange(Blob *pSend, Blob *pReply, int useLogin, int maxRedirect){ | |
| 136 | 136 | Blob login; /* The login card */ |
| 137 | 137 | Blob payload; /* The complete payload including login card */ |
| 138 | 138 | Blob hdr; /* The HTTP request header */ |
| 139 | 139 | int closeConnection; /* True to close the connection when done */ |
| 140 | 140 | int iLength; /* Length of the reply payload */ |
| @@ -231,10 +231,14 @@ | ||
| 231 | 231 | }else if( c=='k' || c=='K' ){ |
| 232 | 232 | closeConnection = 0; |
| 233 | 233 | } |
| 234 | 234 | }else if( rc==302 && fossil_strnicmp(zLine, "location:", 9)==0 ){ |
| 235 | 235 | int i, j; |
| 236 | + | |
| 237 | + if ( --maxRedirect == 0){ | |
| 238 | + fossil_fatal("redirect limit exceeded"); | |
| 239 | + } | |
| 236 | 240 | for(i=9; zLine[i] && zLine[i]==' '; i++){} |
| 237 | 241 | if( zLine[i]==0 ) fossil_fatal("malformed redirect: %s", zLine); |
| 238 | 242 | j = strlen(zLine) - 1; |
| 239 | 243 | while( j>4 && fossil_strcmp(&zLine[j-4],"/xfer")==0 ){ |
| 240 | 244 | j -= 4; |
| @@ -241,11 +245,11 @@ | ||
| 241 | 245 | zLine[j] = 0; |
| 242 | 246 | } |
| 243 | 247 | fossil_print("redirect to %s\n", &zLine[i]); |
| 244 | 248 | url_parse(&zLine[i]); |
| 245 | 249 | transport_close(); |
| 246 | - return http_exchange(pSend, pReply, useLogin); | |
| 250 | + return http_exchange(pSend, pReply, useLogin, maxRedirect); | |
| 247 | 251 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 248 | 252 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 249 | 253 | isCompressed = 0; |
| 250 | 254 | }else if( fossil_strnicmp(&zLine[14], |
| 251 | 255 | "application/x-fossil-uncompressed", -1)==0 ){ |
| 252 | 256 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -130,11 +130,11 @@ | |
| 130 | ** |
| 131 | ** The server address is contain in the "g" global structure. The |
| 132 | ** url_parse() routine should have been called prior to this routine |
| 133 | ** in order to fill this structure appropriately. |
| 134 | */ |
| 135 | int http_exchange(Blob *pSend, Blob *pReply, int useLogin){ |
| 136 | Blob login; /* The login card */ |
| 137 | Blob payload; /* The complete payload including login card */ |
| 138 | Blob hdr; /* The HTTP request header */ |
| 139 | int closeConnection; /* True to close the connection when done */ |
| 140 | int iLength; /* Length of the reply payload */ |
| @@ -231,10 +231,14 @@ | |
| 231 | }else if( c=='k' || c=='K' ){ |
| 232 | closeConnection = 0; |
| 233 | } |
| 234 | }else if( rc==302 && fossil_strnicmp(zLine, "location:", 9)==0 ){ |
| 235 | int i, j; |
| 236 | for(i=9; zLine[i] && zLine[i]==' '; i++){} |
| 237 | if( zLine[i]==0 ) fossil_fatal("malformed redirect: %s", zLine); |
| 238 | j = strlen(zLine) - 1; |
| 239 | while( j>4 && fossil_strcmp(&zLine[j-4],"/xfer")==0 ){ |
| 240 | j -= 4; |
| @@ -241,11 +245,11 @@ | |
| 241 | zLine[j] = 0; |
| 242 | } |
| 243 | fossil_print("redirect to %s\n", &zLine[i]); |
| 244 | url_parse(&zLine[i]); |
| 245 | transport_close(); |
| 246 | return http_exchange(pSend, pReply, useLogin); |
| 247 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 248 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 249 | isCompressed = 0; |
| 250 | }else if( fossil_strnicmp(&zLine[14], |
| 251 | "application/x-fossil-uncompressed", -1)==0 ){ |
| 252 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -130,11 +130,11 @@ | |
| 130 | ** |
| 131 | ** The server address is contain in the "g" global structure. The |
| 132 | ** url_parse() routine should have been called prior to this routine |
| 133 | ** in order to fill this structure appropriately. |
| 134 | */ |
| 135 | int http_exchange(Blob *pSend, Blob *pReply, int useLogin, int maxRedirect){ |
| 136 | Blob login; /* The login card */ |
| 137 | Blob payload; /* The complete payload including login card */ |
| 138 | Blob hdr; /* The HTTP request header */ |
| 139 | int closeConnection; /* True to close the connection when done */ |
| 140 | int iLength; /* Length of the reply payload */ |
| @@ -231,10 +231,14 @@ | |
| 231 | }else if( c=='k' || c=='K' ){ |
| 232 | closeConnection = 0; |
| 233 | } |
| 234 | }else if( rc==302 && fossil_strnicmp(zLine, "location:", 9)==0 ){ |
| 235 | int i, j; |
| 236 | |
| 237 | if ( --maxRedirect == 0){ |
| 238 | fossil_fatal("redirect limit exceeded"); |
| 239 | } |
| 240 | for(i=9; zLine[i] && zLine[i]==' '; i++){} |
| 241 | if( zLine[i]==0 ) fossil_fatal("malformed redirect: %s", zLine); |
| 242 | j = strlen(zLine) - 1; |
| 243 | while( j>4 && fossil_strcmp(&zLine[j-4],"/xfer")==0 ){ |
| 244 | j -= 4; |
| @@ -241,11 +245,11 @@ | |
| 245 | zLine[j] = 0; |
| 246 | } |
| 247 | fossil_print("redirect to %s\n", &zLine[i]); |
| 248 | url_parse(&zLine[i]); |
| 249 | transport_close(); |
| 250 | return http_exchange(pSend, pReply, useLogin, maxRedirect); |
| 251 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 252 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 253 | isCompressed = 0; |
| 254 | }else if( fossil_strnicmp(&zLine[14], |
| 255 | "application/x-fossil-uncompressed", -1)==0 ){ |
| 256 |
+6
-2
| --- src/http.c | ||
| +++ src/http.c | ||
| @@ -130,11 +130,11 @@ | ||
| 130 | 130 | ** |
| 131 | 131 | ** The server address is contain in the "g" global structure. The |
| 132 | 132 | ** url_parse() routine should have been called prior to this routine |
| 133 | 133 | ** in order to fill this structure appropriately. |
| 134 | 134 | */ |
| 135 | -int http_exchange(Blob *pSend, Blob *pReply, int useLogin){ | |
| 135 | +int http_exchange(Blob *pSend, Blob *pReply, int useLogin, int maxRedirect){ | |
| 136 | 136 | Blob login; /* The login card */ |
| 137 | 137 | Blob payload; /* The complete payload including login card */ |
| 138 | 138 | Blob hdr; /* The HTTP request header */ |
| 139 | 139 | int closeConnection; /* True to close the connection when done */ |
| 140 | 140 | int iLength; /* Length of the reply payload */ |
| @@ -231,10 +231,14 @@ | ||
| 231 | 231 | }else if( c=='k' || c=='K' ){ |
| 232 | 232 | closeConnection = 0; |
| 233 | 233 | } |
| 234 | 234 | }else if( rc==302 && fossil_strnicmp(zLine, "location:", 9)==0 ){ |
| 235 | 235 | int i, j; |
| 236 | + | |
| 237 | + if ( --maxRedirect == 0){ | |
| 238 | + fossil_fatal("redirect limit exceeded"); | |
| 239 | + } | |
| 236 | 240 | for(i=9; zLine[i] && zLine[i]==' '; i++){} |
| 237 | 241 | if( zLine[i]==0 ) fossil_fatal("malformed redirect: %s", zLine); |
| 238 | 242 | j = strlen(zLine) - 1; |
| 239 | 243 | while( j>4 && fossil_strcmp(&zLine[j-4],"/xfer")==0 ){ |
| 240 | 244 | j -= 4; |
| @@ -241,11 +245,11 @@ | ||
| 241 | 245 | zLine[j] = 0; |
| 242 | 246 | } |
| 243 | 247 | fossil_print("redirect to %s\n", &zLine[i]); |
| 244 | 248 | url_parse(&zLine[i]); |
| 245 | 249 | transport_close(); |
| 246 | - return http_exchange(pSend, pReply, useLogin); | |
| 250 | + return http_exchange(pSend, pReply, useLogin, maxRedirect); | |
| 247 | 251 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 248 | 252 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 249 | 253 | isCompressed = 0; |
| 250 | 254 | }else if( fossil_strnicmp(&zLine[14], |
| 251 | 255 | "application/x-fossil-uncompressed", -1)==0 ){ |
| 252 | 256 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -130,11 +130,11 @@ | |
| 130 | ** |
| 131 | ** The server address is contain in the "g" global structure. The |
| 132 | ** url_parse() routine should have been called prior to this routine |
| 133 | ** in order to fill this structure appropriately. |
| 134 | */ |
| 135 | int http_exchange(Blob *pSend, Blob *pReply, int useLogin){ |
| 136 | Blob login; /* The login card */ |
| 137 | Blob payload; /* The complete payload including login card */ |
| 138 | Blob hdr; /* The HTTP request header */ |
| 139 | int closeConnection; /* True to close the connection when done */ |
| 140 | int iLength; /* Length of the reply payload */ |
| @@ -231,10 +231,14 @@ | |
| 231 | }else if( c=='k' || c=='K' ){ |
| 232 | closeConnection = 0; |
| 233 | } |
| 234 | }else if( rc==302 && fossil_strnicmp(zLine, "location:", 9)==0 ){ |
| 235 | int i, j; |
| 236 | for(i=9; zLine[i] && zLine[i]==' '; i++){} |
| 237 | if( zLine[i]==0 ) fossil_fatal("malformed redirect: %s", zLine); |
| 238 | j = strlen(zLine) - 1; |
| 239 | while( j>4 && fossil_strcmp(&zLine[j-4],"/xfer")==0 ){ |
| 240 | j -= 4; |
| @@ -241,11 +245,11 @@ | |
| 241 | zLine[j] = 0; |
| 242 | } |
| 243 | fossil_print("redirect to %s\n", &zLine[i]); |
| 244 | url_parse(&zLine[i]); |
| 245 | transport_close(); |
| 246 | return http_exchange(pSend, pReply, useLogin); |
| 247 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 248 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 249 | isCompressed = 0; |
| 250 | }else if( fossil_strnicmp(&zLine[14], |
| 251 | "application/x-fossil-uncompressed", -1)==0 ){ |
| 252 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -130,11 +130,11 @@ | |
| 130 | ** |
| 131 | ** The server address is contain in the "g" global structure. The |
| 132 | ** url_parse() routine should have been called prior to this routine |
| 133 | ** in order to fill this structure appropriately. |
| 134 | */ |
| 135 | int http_exchange(Blob *pSend, Blob *pReply, int useLogin, int maxRedirect){ |
| 136 | Blob login; /* The login card */ |
| 137 | Blob payload; /* The complete payload including login card */ |
| 138 | Blob hdr; /* The HTTP request header */ |
| 139 | int closeConnection; /* True to close the connection when done */ |
| 140 | int iLength; /* Length of the reply payload */ |
| @@ -231,10 +231,14 @@ | |
| 231 | }else if( c=='k' || c=='K' ){ |
| 232 | closeConnection = 0; |
| 233 | } |
| 234 | }else if( rc==302 && fossil_strnicmp(zLine, "location:", 9)==0 ){ |
| 235 | int i, j; |
| 236 | |
| 237 | if ( --maxRedirect == 0){ |
| 238 | fossil_fatal("redirect limit exceeded"); |
| 239 | } |
| 240 | for(i=9; zLine[i] && zLine[i]==' '; i++){} |
| 241 | if( zLine[i]==0 ) fossil_fatal("malformed redirect: %s", zLine); |
| 242 | j = strlen(zLine) - 1; |
| 243 | while( j>4 && fossil_strcmp(&zLine[j-4],"/xfer")==0 ){ |
| 244 | j -= 4; |
| @@ -241,11 +245,11 @@ | |
| 245 | zLine[j] = 0; |
| 246 | } |
| 247 | fossil_print("redirect to %s\n", &zLine[i]); |
| 248 | url_parse(&zLine[i]); |
| 249 | transport_close(); |
| 250 | return http_exchange(pSend, pReply, useLogin, maxRedirect); |
| 251 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 252 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 253 | isCompressed = 0; |
| 254 | }else if( fossil_strnicmp(&zLine[14], |
| 255 | "application/x-fossil-uncompressed", -1)==0 ){ |
| 256 |
+4
-10
| --- src/http_ssl.c | ||
| +++ src/http_ssl.c | ||
| @@ -1,21 +1,15 @@ | ||
| 1 | 1 | /* |
| 2 | 2 | ** Copyright (c) 2009 D. Richard Hipp |
| 3 | 3 | ** |
| 4 | 4 | ** This program is free software; you can redistribute it and/or |
| 5 | -** modify it under the terms of the GNU General Public | |
| 6 | -** License version 2 as published by the Free Software Foundation. | |
| 5 | +** modify it under the terms of the Simplified BSD License (also | |
| 6 | +** known as the "2-Clause License" or "FreeBSD License".) | |
| 7 | 7 | ** |
| 8 | 8 | ** This program is distributed in the hope that it will be useful, |
| 9 | -** but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 10 | -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 11 | -** General Public License for more details. | |
| 12 | -** | |
| 13 | -** You should have received a copy of the GNU General Public | |
| 14 | -** License along with this library; if not, write to the | |
| 15 | -** Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
| 16 | -** Boston, MA 02111-1307, USA. | |
| 9 | +** but without any warranty; without even the implied warranty of | |
| 10 | +** merchantability or fitness for a particular purpose. | |
| 17 | 11 | ** |
| 18 | 12 | ** Author contact information: |
| 19 | 13 | ** [email protected] |
| 20 | 14 | ** http://www.hwaci.com/drh/ |
| 21 | 15 | ** |
| 22 | 16 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -1,21 +1,15 @@ | |
| 1 | /* |
| 2 | ** Copyright (c) 2009 D. Richard Hipp |
| 3 | ** |
| 4 | ** This program is free software; you can redistribute it and/or |
| 5 | ** modify it under the terms of the GNU General Public |
| 6 | ** License version 2 as published by the Free Software Foundation. |
| 7 | ** |
| 8 | ** This program is distributed in the hope that it will be useful, |
| 9 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 11 | ** General Public License for more details. |
| 12 | ** |
| 13 | ** You should have received a copy of the GNU General Public |
| 14 | ** License along with this library; if not, write to the |
| 15 | ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| 16 | ** Boston, MA 02111-1307, USA. |
| 17 | ** |
| 18 | ** Author contact information: |
| 19 | ** [email protected] |
| 20 | ** http://www.hwaci.com/drh/ |
| 21 | ** |
| 22 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -1,21 +1,15 @@ | |
| 1 | /* |
| 2 | ** Copyright (c) 2009 D. Richard Hipp |
| 3 | ** |
| 4 | ** This program is free software; you can redistribute it and/or |
| 5 | ** modify it under the terms of the Simplified BSD License (also |
| 6 | ** known as the "2-Clause License" or "FreeBSD License".) |
| 7 | ** |
| 8 | ** This program is distributed in the hope that it will be useful, |
| 9 | ** but without any warranty; without even the implied warranty of |
| 10 | ** merchantability or fitness for a particular purpose. |
| 11 | ** |
| 12 | ** Author contact information: |
| 13 | ** [email protected] |
| 14 | ** http://www.hwaci.com/drh/ |
| 15 | ** |
| 16 |
+4
-10
| --- src/http_ssl.c | ||
| +++ src/http_ssl.c | ||
| @@ -1,21 +1,15 @@ | ||
| 1 | 1 | /* |
| 2 | 2 | ** Copyright (c) 2009 D. Richard Hipp |
| 3 | 3 | ** |
| 4 | 4 | ** This program is free software; you can redistribute it and/or |
| 5 | -** modify it under the terms of the GNU General Public | |
| 6 | -** License version 2 as published by the Free Software Foundation. | |
| 5 | +** modify it under the terms of the Simplified BSD License (also | |
| 6 | +** known as the "2-Clause License" or "FreeBSD License".) | |
| 7 | 7 | ** |
| 8 | 8 | ** This program is distributed in the hope that it will be useful, |
| 9 | -** but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 10 | -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 11 | -** General Public License for more details. | |
| 12 | -** | |
| 13 | -** You should have received a copy of the GNU General Public | |
| 14 | -** License along with this library; if not, write to the | |
| 15 | -** Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
| 16 | -** Boston, MA 02111-1307, USA. | |
| 9 | +** but without any warranty; without even the implied warranty of | |
| 10 | +** merchantability or fitness for a particular purpose. | |
| 17 | 11 | ** |
| 18 | 12 | ** Author contact information: |
| 19 | 13 | ** [email protected] |
| 20 | 14 | ** http://www.hwaci.com/drh/ |
| 21 | 15 | ** |
| 22 | 16 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -1,21 +1,15 @@ | |
| 1 | /* |
| 2 | ** Copyright (c) 2009 D. Richard Hipp |
| 3 | ** |
| 4 | ** This program is free software; you can redistribute it and/or |
| 5 | ** modify it under the terms of the GNU General Public |
| 6 | ** License version 2 as published by the Free Software Foundation. |
| 7 | ** |
| 8 | ** This program is distributed in the hope that it will be useful, |
| 9 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 11 | ** General Public License for more details. |
| 12 | ** |
| 13 | ** You should have received a copy of the GNU General Public |
| 14 | ** License along with this library; if not, write to the |
| 15 | ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| 16 | ** Boston, MA 02111-1307, USA. |
| 17 | ** |
| 18 | ** Author contact information: |
| 19 | ** [email protected] |
| 20 | ** http://www.hwaci.com/drh/ |
| 21 | ** |
| 22 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -1,21 +1,15 @@ | |
| 1 | /* |
| 2 | ** Copyright (c) 2009 D. Richard Hipp |
| 3 | ** |
| 4 | ** This program is free software; you can redistribute it and/or |
| 5 | ** modify it under the terms of the Simplified BSD License (also |
| 6 | ** known as the "2-Clause License" or "FreeBSD License".) |
| 7 | ** |
| 8 | ** This program is distributed in the hope that it will be useful, |
| 9 | ** but without any warranty; without even the implied warranty of |
| 10 | ** merchantability or fitness for a particular purpose. |
| 11 | ** |
| 12 | ** Author contact information: |
| 13 | ** [email protected] |
| 14 | ** http://www.hwaci.com/drh/ |
| 15 | ** |
| 16 |
+51
-6
| --- src/http_transport.c | ||
| +++ src/http_transport.c | ||
| @@ -183,10 +183,11 @@ | ||
| 183 | 183 | ** and run an SSH command to talk to the remote machine. |
| 184 | 184 | */ |
| 185 | 185 | const char *zSsh; /* The base SSH command */ |
| 186 | 186 | Blob zCmd; /* The SSH command */ |
| 187 | 187 | char *zHost; /* The host name to contact */ |
| 188 | + int n; /* Size of prefix string */ | |
| 188 | 189 | |
| 189 | 190 | zSsh = db_get("ssh-command", zDefaultSshCmd); |
| 190 | 191 | blob_init(&zCmd, zSsh, -1); |
| 191 | 192 | if( g.urlPort!=g.urlDfltPort ){ |
| 192 | 193 | #ifdef __MINGW32__ |
| @@ -219,22 +220,63 @@ | ||
| 219 | 220 | } |
| 220 | 221 | #endif |
| 221 | 222 | }else{ |
| 222 | 223 | zHost = mprintf("%s", g.urlName); |
| 223 | 224 | } |
| 225 | + n = blob_size(&zCmd); | |
| 224 | 226 | blob_append(&zCmd, " ", 1); |
| 225 | 227 | shell_escape(&zCmd, zHost); |
| 226 | - fossil_print(" %s\n", zHost); /* Show the conclusion of the SSH command */ | |
| 228 | + if( g.urlShell ){ | |
| 229 | + blob_appendf(&zCmd, " %s", g.urlShell); | |
| 230 | + }else{ | |
| 231 | +#if defined(FOSSIL_ENABLE_SSH_FAR_SIDE) | |
| 232 | + /* The following works. But only if the fossil on the remote side | |
| 233 | + ** is recent enough to support the test-ssh-far-side command. That | |
| 234 | + ** command was added on 2013-02-06. We will leave this turned off | |
| 235 | + ** until most fossil servers have upgraded to that version or a later | |
| 236 | + ** version. The sync will still work as long as the shell on the far | |
| 237 | + ** side is bash and not tcsh. And if the default far side shell is | |
| 238 | + ** tcsh, then the shell=/bin/bash query parameter can be used as a | |
| 239 | + ** work-around. Enable this code after about a year... | |
| 240 | + */ | |
| 241 | + blob_appendf(&zCmd, " exec %s test-ssh-far-side", g.urlFossil); | |
| 242 | +#endif | |
| 243 | + } | |
| 244 | + fossil_print("%s\n", blob_str(&zCmd)+n); /* Show tail of SSH command */ | |
| 227 | 245 | free(zHost); |
| 228 | 246 | popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid); |
| 229 | 247 | if( sshPid==0 ){ |
| 230 | 248 | fossil_fatal("cannot start ssh tunnel using [%b]", &zCmd); |
| 231 | 249 | } |
| 232 | 250 | blob_reset(&zCmd); |
| 233 | 251 | transport_ssh_startup(); |
| 234 | 252 | } |
| 235 | 253 | } |
| 254 | + | |
| 255 | +/* | |
| 256 | +** COMMAND: test-ssh-far-side | |
| 257 | +** | |
| 258 | +** Read lines of input text, one by one, and evaluate each line using | |
| 259 | +** system(). The ssh: sync protocol uses this on the far side of the | |
| 260 | +** SSH link. | |
| 261 | +*/ | |
| 262 | +void test_ssh_far_side_cmd(void){ | |
| 263 | + int i = 0; | |
| 264 | + int got; | |
| 265 | + char zLine[5000]; | |
| 266 | + while( i<sizeof(zLine) ){ | |
| 267 | + got = read(0, zLine+i, 1); | |
| 268 | + if( got==0 ) return; | |
| 269 | + if( zLine[i]=='\n' ){ | |
| 270 | + zLine[i] = 0; | |
| 271 | + system(zLine); | |
| 272 | + i = 0; | |
| 273 | + }else{ | |
| 274 | + i++; | |
| 275 | + } | |
| 276 | + } | |
| 277 | +} | |
| 236 | 278 | |
| 237 | 279 | /* |
| 238 | 280 | ** Open a connection to the server. The server is defined by the following |
| 239 | 281 | ** global variables: |
| 240 | 282 | ** |
| @@ -251,13 +293,13 @@ | ||
| 251 | 293 | Blob cmd; |
| 252 | 294 | blob_zero(&cmd); |
| 253 | 295 | shell_escape(&cmd, g.urlFossil); |
| 254 | 296 | blob_append(&cmd, " test-http ", -1); |
| 255 | 297 | shell_escape(&cmd, g.urlPath); |
| 256 | - /* printf("%s\n", blob_str(&cmd)); fflush(stdout); */ | |
| 257 | - fprintf(sshOut, "%s\n", blob_str(&cmd)); | |
| 298 | + fprintf(sshOut, "%s || true\n", blob_str(&cmd)); | |
| 258 | 299 | fflush(sshOut); |
| 300 | + if( g.fSshTrace ) printf("Sent: [%s]\n", blob_str(&cmd)); | |
| 259 | 301 | blob_reset(&cmd); |
| 260 | 302 | }else if( g.urlIsHttps ){ |
| 261 | 303 | #ifdef FOSSIL_ENABLE_SSL |
| 262 | 304 | rc = ssl_open(); |
| 263 | 305 | if( rc==0 ) transport.isOpen = 1; |
| @@ -403,11 +445,10 @@ | ||
| 403 | 445 | int got; |
| 404 | 446 | if( sshIn ){ |
| 405 | 447 | int x; |
| 406 | 448 | int wanted = N; |
| 407 | 449 | got = 0; |
| 408 | - /* printf("want %d bytes...\n", wanted); fflush(stdout); */ | |
| 409 | 450 | while( wanted>0 ){ |
| 410 | 451 | x = read(sshIn, &zBuf[got], wanted); |
| 411 | 452 | if( x<=0 ) break; |
| 412 | 453 | got += x; |
| 413 | 454 | wanted -= x; |
| @@ -438,11 +479,14 @@ | ||
| 438 | 479 | int transport_receive(char *zBuf, int N){ |
| 439 | 480 | int onHand; /* Bytes current held in the transport buffer */ |
| 440 | 481 | int nByte = 0; /* Bytes of content received */ |
| 441 | 482 | |
| 442 | 483 | onHand = transport.nUsed - transport.iCursor; |
| 443 | - /* printf("request %d with %d on hand\n", N, onHand); fflush(stdout); */ | |
| 484 | + if( g.fSshTrace){ | |
| 485 | + printf("Reading %d bytes with %d on hand... ", N, onHand); | |
| 486 | + fflush(stdout); | |
| 487 | + } | |
| 444 | 488 | if( onHand>0 ){ |
| 445 | 489 | int toMove = onHand; |
| 446 | 490 | if( toMove>N ) toMove = N; |
| 447 | 491 | /* printf("bytes on hand: %d of %d\n", toMove, N); fflush(stdout); */ |
| 448 | 492 | memcpy(zBuf, &transport.pBuf[transport.iCursor], toMove); |
| @@ -460,10 +504,11 @@ | ||
| 460 | 504 | if( got>0 ){ |
| 461 | 505 | nByte += got; |
| 462 | 506 | transport.nRcvd += got; |
| 463 | 507 | } |
| 464 | 508 | } |
| 509 | + if( g.fSshTrace ) printf("Got %d bytes\n", nByte); | |
| 465 | 510 | return nByte; |
| 466 | 511 | } |
| 467 | 512 | |
| 468 | 513 | /* |
| 469 | 514 | ** Load up to N new bytes of content into the transport.pBuf buffer. |
| @@ -532,11 +577,11 @@ | ||
| 532 | 577 | } |
| 533 | 578 | break; |
| 534 | 579 | } |
| 535 | 580 | i++; |
| 536 | 581 | } |
| 537 | - /* printf("Got line: [%s]\n", &transport.pBuf[iStart]); */ | |
| 582 | + if( g.fSshTrace ) printf("Got line: [%s]\n", &transport.pBuf[iStart]); | |
| 538 | 583 | return &transport.pBuf[iStart]; |
| 539 | 584 | } |
| 540 | 585 | |
| 541 | 586 | void transport_global_shutdown(void){ |
| 542 | 587 | if( g.urlIsSsh && sshPid ){ |
| 543 | 588 |
| --- src/http_transport.c | |
| +++ src/http_transport.c | |
| @@ -183,10 +183,11 @@ | |
| 183 | ** and run an SSH command to talk to the remote machine. |
| 184 | */ |
| 185 | const char *zSsh; /* The base SSH command */ |
| 186 | Blob zCmd; /* The SSH command */ |
| 187 | char *zHost; /* The host name to contact */ |
| 188 | |
| 189 | zSsh = db_get("ssh-command", zDefaultSshCmd); |
| 190 | blob_init(&zCmd, zSsh, -1); |
| 191 | if( g.urlPort!=g.urlDfltPort ){ |
| 192 | #ifdef __MINGW32__ |
| @@ -219,22 +220,63 @@ | |
| 219 | } |
| 220 | #endif |
| 221 | }else{ |
| 222 | zHost = mprintf("%s", g.urlName); |
| 223 | } |
| 224 | blob_append(&zCmd, " ", 1); |
| 225 | shell_escape(&zCmd, zHost); |
| 226 | fossil_print(" %s\n", zHost); /* Show the conclusion of the SSH command */ |
| 227 | free(zHost); |
| 228 | popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid); |
| 229 | if( sshPid==0 ){ |
| 230 | fossil_fatal("cannot start ssh tunnel using [%b]", &zCmd); |
| 231 | } |
| 232 | blob_reset(&zCmd); |
| 233 | transport_ssh_startup(); |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | /* |
| 238 | ** Open a connection to the server. The server is defined by the following |
| 239 | ** global variables: |
| 240 | ** |
| @@ -251,13 +293,13 @@ | |
| 251 | Blob cmd; |
| 252 | blob_zero(&cmd); |
| 253 | shell_escape(&cmd, g.urlFossil); |
| 254 | blob_append(&cmd, " test-http ", -1); |
| 255 | shell_escape(&cmd, g.urlPath); |
| 256 | /* printf("%s\n", blob_str(&cmd)); fflush(stdout); */ |
| 257 | fprintf(sshOut, "%s\n", blob_str(&cmd)); |
| 258 | fflush(sshOut); |
| 259 | blob_reset(&cmd); |
| 260 | }else if( g.urlIsHttps ){ |
| 261 | #ifdef FOSSIL_ENABLE_SSL |
| 262 | rc = ssl_open(); |
| 263 | if( rc==0 ) transport.isOpen = 1; |
| @@ -403,11 +445,10 @@ | |
| 403 | int got; |
| 404 | if( sshIn ){ |
| 405 | int x; |
| 406 | int wanted = N; |
| 407 | got = 0; |
| 408 | /* printf("want %d bytes...\n", wanted); fflush(stdout); */ |
| 409 | while( wanted>0 ){ |
| 410 | x = read(sshIn, &zBuf[got], wanted); |
| 411 | if( x<=0 ) break; |
| 412 | got += x; |
| 413 | wanted -= x; |
| @@ -438,11 +479,14 @@ | |
| 438 | int transport_receive(char *zBuf, int N){ |
| 439 | int onHand; /* Bytes current held in the transport buffer */ |
| 440 | int nByte = 0; /* Bytes of content received */ |
| 441 | |
| 442 | onHand = transport.nUsed - transport.iCursor; |
| 443 | /* printf("request %d with %d on hand\n", N, onHand); fflush(stdout); */ |
| 444 | if( onHand>0 ){ |
| 445 | int toMove = onHand; |
| 446 | if( toMove>N ) toMove = N; |
| 447 | /* printf("bytes on hand: %d of %d\n", toMove, N); fflush(stdout); */ |
| 448 | memcpy(zBuf, &transport.pBuf[transport.iCursor], toMove); |
| @@ -460,10 +504,11 @@ | |
| 460 | if( got>0 ){ |
| 461 | nByte += got; |
| 462 | transport.nRcvd += got; |
| 463 | } |
| 464 | } |
| 465 | return nByte; |
| 466 | } |
| 467 | |
| 468 | /* |
| 469 | ** Load up to N new bytes of content into the transport.pBuf buffer. |
| @@ -532,11 +577,11 @@ | |
| 532 | } |
| 533 | break; |
| 534 | } |
| 535 | i++; |
| 536 | } |
| 537 | /* printf("Got line: [%s]\n", &transport.pBuf[iStart]); */ |
| 538 | return &transport.pBuf[iStart]; |
| 539 | } |
| 540 | |
| 541 | void transport_global_shutdown(void){ |
| 542 | if( g.urlIsSsh && sshPid ){ |
| 543 |
| --- src/http_transport.c | |
| +++ src/http_transport.c | |
| @@ -183,10 +183,11 @@ | |
| 183 | ** and run an SSH command to talk to the remote machine. |
| 184 | */ |
| 185 | const char *zSsh; /* The base SSH command */ |
| 186 | Blob zCmd; /* The SSH command */ |
| 187 | char *zHost; /* The host name to contact */ |
| 188 | int n; /* Size of prefix string */ |
| 189 | |
| 190 | zSsh = db_get("ssh-command", zDefaultSshCmd); |
| 191 | blob_init(&zCmd, zSsh, -1); |
| 192 | if( g.urlPort!=g.urlDfltPort ){ |
| 193 | #ifdef __MINGW32__ |
| @@ -219,22 +220,63 @@ | |
| 220 | } |
| 221 | #endif |
| 222 | }else{ |
| 223 | zHost = mprintf("%s", g.urlName); |
| 224 | } |
| 225 | n = blob_size(&zCmd); |
| 226 | blob_append(&zCmd, " ", 1); |
| 227 | shell_escape(&zCmd, zHost); |
| 228 | if( g.urlShell ){ |
| 229 | blob_appendf(&zCmd, " %s", g.urlShell); |
| 230 | }else{ |
| 231 | #if defined(FOSSIL_ENABLE_SSH_FAR_SIDE) |
| 232 | /* The following works. But only if the fossil on the remote side |
| 233 | ** is recent enough to support the test-ssh-far-side command. That |
| 234 | ** command was added on 2013-02-06. We will leave this turned off |
| 235 | ** until most fossil servers have upgraded to that version or a later |
| 236 | ** version. The sync will still work as long as the shell on the far |
| 237 | ** side is bash and not tcsh. And if the default far side shell is |
| 238 | ** tcsh, then the shell=/bin/bash query parameter can be used as a |
| 239 | ** work-around. Enable this code after about a year... |
| 240 | */ |
| 241 | blob_appendf(&zCmd, " exec %s test-ssh-far-side", g.urlFossil); |
| 242 | #endif |
| 243 | } |
| 244 | fossil_print("%s\n", blob_str(&zCmd)+n); /* Show tail of SSH command */ |
| 245 | free(zHost); |
| 246 | popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid); |
| 247 | if( sshPid==0 ){ |
| 248 | fossil_fatal("cannot start ssh tunnel using [%b]", &zCmd); |
| 249 | } |
| 250 | blob_reset(&zCmd); |
| 251 | transport_ssh_startup(); |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | /* |
| 256 | ** COMMAND: test-ssh-far-side |
| 257 | ** |
| 258 | ** Read lines of input text, one by one, and evaluate each line using |
| 259 | ** system(). The ssh: sync protocol uses this on the far side of the |
| 260 | ** SSH link. |
| 261 | */ |
| 262 | void test_ssh_far_side_cmd(void){ |
| 263 | int i = 0; |
| 264 | int got; |
| 265 | char zLine[5000]; |
| 266 | while( i<sizeof(zLine) ){ |
| 267 | got = read(0, zLine+i, 1); |
| 268 | if( got==0 ) return; |
| 269 | if( zLine[i]=='\n' ){ |
| 270 | zLine[i] = 0; |
| 271 | system(zLine); |
| 272 | i = 0; |
| 273 | }else{ |
| 274 | i++; |
| 275 | } |
| 276 | } |
| 277 | } |
| 278 | |
| 279 | /* |
| 280 | ** Open a connection to the server. The server is defined by the following |
| 281 | ** global variables: |
| 282 | ** |
| @@ -251,13 +293,13 @@ | |
| 293 | Blob cmd; |
| 294 | blob_zero(&cmd); |
| 295 | shell_escape(&cmd, g.urlFossil); |
| 296 | blob_append(&cmd, " test-http ", -1); |
| 297 | shell_escape(&cmd, g.urlPath); |
| 298 | fprintf(sshOut, "%s || true\n", blob_str(&cmd)); |
| 299 | fflush(sshOut); |
| 300 | if( g.fSshTrace ) printf("Sent: [%s]\n", blob_str(&cmd)); |
| 301 | blob_reset(&cmd); |
| 302 | }else if( g.urlIsHttps ){ |
| 303 | #ifdef FOSSIL_ENABLE_SSL |
| 304 | rc = ssl_open(); |
| 305 | if( rc==0 ) transport.isOpen = 1; |
| @@ -403,11 +445,10 @@ | |
| 445 | int got; |
| 446 | if( sshIn ){ |
| 447 | int x; |
| 448 | int wanted = N; |
| 449 | got = 0; |
| 450 | while( wanted>0 ){ |
| 451 | x = read(sshIn, &zBuf[got], wanted); |
| 452 | if( x<=0 ) break; |
| 453 | got += x; |
| 454 | wanted -= x; |
| @@ -438,11 +479,14 @@ | |
| 479 | int transport_receive(char *zBuf, int N){ |
| 480 | int onHand; /* Bytes current held in the transport buffer */ |
| 481 | int nByte = 0; /* Bytes of content received */ |
| 482 | |
| 483 | onHand = transport.nUsed - transport.iCursor; |
| 484 | if( g.fSshTrace){ |
| 485 | printf("Reading %d bytes with %d on hand... ", N, onHand); |
| 486 | fflush(stdout); |
| 487 | } |
| 488 | if( onHand>0 ){ |
| 489 | int toMove = onHand; |
| 490 | if( toMove>N ) toMove = N; |
| 491 | /* printf("bytes on hand: %d of %d\n", toMove, N); fflush(stdout); */ |
| 492 | memcpy(zBuf, &transport.pBuf[transport.iCursor], toMove); |
| @@ -460,10 +504,11 @@ | |
| 504 | if( got>0 ){ |
| 505 | nByte += got; |
| 506 | transport.nRcvd += got; |
| 507 | } |
| 508 | } |
| 509 | if( g.fSshTrace ) printf("Got %d bytes\n", nByte); |
| 510 | return nByte; |
| 511 | } |
| 512 | |
| 513 | /* |
| 514 | ** Load up to N new bytes of content into the transport.pBuf buffer. |
| @@ -532,11 +577,11 @@ | |
| 577 | } |
| 578 | break; |
| 579 | } |
| 580 | i++; |
| 581 | } |
| 582 | if( g.fSshTrace ) printf("Got line: [%s]\n", &transport.pBuf[iStart]); |
| 583 | return &transport.pBuf[iStart]; |
| 584 | } |
| 585 | |
| 586 | void transport_global_shutdown(void){ |
| 587 | if( g.urlIsSsh && sshPid ){ |
| 588 |
+51
-6
| --- src/http_transport.c | ||
| +++ src/http_transport.c | ||
| @@ -183,10 +183,11 @@ | ||
| 183 | 183 | ** and run an SSH command to talk to the remote machine. |
| 184 | 184 | */ |
| 185 | 185 | const char *zSsh; /* The base SSH command */ |
| 186 | 186 | Blob zCmd; /* The SSH command */ |
| 187 | 187 | char *zHost; /* The host name to contact */ |
| 188 | + int n; /* Size of prefix string */ | |
| 188 | 189 | |
| 189 | 190 | zSsh = db_get("ssh-command", zDefaultSshCmd); |
| 190 | 191 | blob_init(&zCmd, zSsh, -1); |
| 191 | 192 | if( g.urlPort!=g.urlDfltPort ){ |
| 192 | 193 | #ifdef __MINGW32__ |
| @@ -219,22 +220,63 @@ | ||
| 219 | 220 | } |
| 220 | 221 | #endif |
| 221 | 222 | }else{ |
| 222 | 223 | zHost = mprintf("%s", g.urlName); |
| 223 | 224 | } |
| 225 | + n = blob_size(&zCmd); | |
| 224 | 226 | blob_append(&zCmd, " ", 1); |
| 225 | 227 | shell_escape(&zCmd, zHost); |
| 226 | - fossil_print(" %s\n", zHost); /* Show the conclusion of the SSH command */ | |
| 228 | + if( g.urlShell ){ | |
| 229 | + blob_appendf(&zCmd, " %s", g.urlShell); | |
| 230 | + }else{ | |
| 231 | +#if defined(FOSSIL_ENABLE_SSH_FAR_SIDE) | |
| 232 | + /* The following works. But only if the fossil on the remote side | |
| 233 | + ** is recent enough to support the test-ssh-far-side command. That | |
| 234 | + ** command was added on 2013-02-06. We will leave this turned off | |
| 235 | + ** until most fossil servers have upgraded to that version or a later | |
| 236 | + ** version. The sync will still work as long as the shell on the far | |
| 237 | + ** side is bash and not tcsh. And if the default far side shell is | |
| 238 | + ** tcsh, then the shell=/bin/bash query parameter can be used as a | |
| 239 | + ** work-around. Enable this code after about a year... | |
| 240 | + */ | |
| 241 | + blob_appendf(&zCmd, " exec %s test-ssh-far-side", g.urlFossil); | |
| 242 | +#endif | |
| 243 | + } | |
| 244 | + fossil_print("%s\n", blob_str(&zCmd)+n); /* Show tail of SSH command */ | |
| 227 | 245 | free(zHost); |
| 228 | 246 | popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid); |
| 229 | 247 | if( sshPid==0 ){ |
| 230 | 248 | fossil_fatal("cannot start ssh tunnel using [%b]", &zCmd); |
| 231 | 249 | } |
| 232 | 250 | blob_reset(&zCmd); |
| 233 | 251 | transport_ssh_startup(); |
| 234 | 252 | } |
| 235 | 253 | } |
| 254 | + | |
| 255 | +/* | |
| 256 | +** COMMAND: test-ssh-far-side | |
| 257 | +** | |
| 258 | +** Read lines of input text, one by one, and evaluate each line using | |
| 259 | +** system(). The ssh: sync protocol uses this on the far side of the | |
| 260 | +** SSH link. | |
| 261 | +*/ | |
| 262 | +void test_ssh_far_side_cmd(void){ | |
| 263 | + int i = 0; | |
| 264 | + int got; | |
| 265 | + char zLine[5000]; | |
| 266 | + while( i<sizeof(zLine) ){ | |
| 267 | + got = read(0, zLine+i, 1); | |
| 268 | + if( got==0 ) return; | |
| 269 | + if( zLine[i]=='\n' ){ | |
| 270 | + zLine[i] = 0; | |
| 271 | + system(zLine); | |
| 272 | + i = 0; | |
| 273 | + }else{ | |
| 274 | + i++; | |
| 275 | + } | |
| 276 | + } | |
| 277 | +} | |
| 236 | 278 | |
| 237 | 279 | /* |
| 238 | 280 | ** Open a connection to the server. The server is defined by the following |
| 239 | 281 | ** global variables: |
| 240 | 282 | ** |
| @@ -251,13 +293,13 @@ | ||
| 251 | 293 | Blob cmd; |
| 252 | 294 | blob_zero(&cmd); |
| 253 | 295 | shell_escape(&cmd, g.urlFossil); |
| 254 | 296 | blob_append(&cmd, " test-http ", -1); |
| 255 | 297 | shell_escape(&cmd, g.urlPath); |
| 256 | - /* printf("%s\n", blob_str(&cmd)); fflush(stdout); */ | |
| 257 | - fprintf(sshOut, "%s\n", blob_str(&cmd)); | |
| 298 | + fprintf(sshOut, "%s || true\n", blob_str(&cmd)); | |
| 258 | 299 | fflush(sshOut); |
| 300 | + if( g.fSshTrace ) printf("Sent: [%s]\n", blob_str(&cmd)); | |
| 259 | 301 | blob_reset(&cmd); |
| 260 | 302 | }else if( g.urlIsHttps ){ |
| 261 | 303 | #ifdef FOSSIL_ENABLE_SSL |
| 262 | 304 | rc = ssl_open(); |
| 263 | 305 | if( rc==0 ) transport.isOpen = 1; |
| @@ -403,11 +445,10 @@ | ||
| 403 | 445 | int got; |
| 404 | 446 | if( sshIn ){ |
| 405 | 447 | int x; |
| 406 | 448 | int wanted = N; |
| 407 | 449 | got = 0; |
| 408 | - /* printf("want %d bytes...\n", wanted); fflush(stdout); */ | |
| 409 | 450 | while( wanted>0 ){ |
| 410 | 451 | x = read(sshIn, &zBuf[got], wanted); |
| 411 | 452 | if( x<=0 ) break; |
| 412 | 453 | got += x; |
| 413 | 454 | wanted -= x; |
| @@ -438,11 +479,14 @@ | ||
| 438 | 479 | int transport_receive(char *zBuf, int N){ |
| 439 | 480 | int onHand; /* Bytes current held in the transport buffer */ |
| 440 | 481 | int nByte = 0; /* Bytes of content received */ |
| 441 | 482 | |
| 442 | 483 | onHand = transport.nUsed - transport.iCursor; |
| 443 | - /* printf("request %d with %d on hand\n", N, onHand); fflush(stdout); */ | |
| 484 | + if( g.fSshTrace){ | |
| 485 | + printf("Reading %d bytes with %d on hand... ", N, onHand); | |
| 486 | + fflush(stdout); | |
| 487 | + } | |
| 444 | 488 | if( onHand>0 ){ |
| 445 | 489 | int toMove = onHand; |
| 446 | 490 | if( toMove>N ) toMove = N; |
| 447 | 491 | /* printf("bytes on hand: %d of %d\n", toMove, N); fflush(stdout); */ |
| 448 | 492 | memcpy(zBuf, &transport.pBuf[transport.iCursor], toMove); |
| @@ -460,10 +504,11 @@ | ||
| 460 | 504 | if( got>0 ){ |
| 461 | 505 | nByte += got; |
| 462 | 506 | transport.nRcvd += got; |
| 463 | 507 | } |
| 464 | 508 | } |
| 509 | + if( g.fSshTrace ) printf("Got %d bytes\n", nByte); | |
| 465 | 510 | return nByte; |
| 466 | 511 | } |
| 467 | 512 | |
| 468 | 513 | /* |
| 469 | 514 | ** Load up to N new bytes of content into the transport.pBuf buffer. |
| @@ -532,11 +577,11 @@ | ||
| 532 | 577 | } |
| 533 | 578 | break; |
| 534 | 579 | } |
| 535 | 580 | i++; |
| 536 | 581 | } |
| 537 | - /* printf("Got line: [%s]\n", &transport.pBuf[iStart]); */ | |
| 582 | + if( g.fSshTrace ) printf("Got line: [%s]\n", &transport.pBuf[iStart]); | |
| 538 | 583 | return &transport.pBuf[iStart]; |
| 539 | 584 | } |
| 540 | 585 | |
| 541 | 586 | void transport_global_shutdown(void){ |
| 542 | 587 | if( g.urlIsSsh && sshPid ){ |
| 543 | 588 |
| --- src/http_transport.c | |
| +++ src/http_transport.c | |
| @@ -183,10 +183,11 @@ | |
| 183 | ** and run an SSH command to talk to the remote machine. |
| 184 | */ |
| 185 | const char *zSsh; /* The base SSH command */ |
| 186 | Blob zCmd; /* The SSH command */ |
| 187 | char *zHost; /* The host name to contact */ |
| 188 | |
| 189 | zSsh = db_get("ssh-command", zDefaultSshCmd); |
| 190 | blob_init(&zCmd, zSsh, -1); |
| 191 | if( g.urlPort!=g.urlDfltPort ){ |
| 192 | #ifdef __MINGW32__ |
| @@ -219,22 +220,63 @@ | |
| 219 | } |
| 220 | #endif |
| 221 | }else{ |
| 222 | zHost = mprintf("%s", g.urlName); |
| 223 | } |
| 224 | blob_append(&zCmd, " ", 1); |
| 225 | shell_escape(&zCmd, zHost); |
| 226 | fossil_print(" %s\n", zHost); /* Show the conclusion of the SSH command */ |
| 227 | free(zHost); |
| 228 | popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid); |
| 229 | if( sshPid==0 ){ |
| 230 | fossil_fatal("cannot start ssh tunnel using [%b]", &zCmd); |
| 231 | } |
| 232 | blob_reset(&zCmd); |
| 233 | transport_ssh_startup(); |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | /* |
| 238 | ** Open a connection to the server. The server is defined by the following |
| 239 | ** global variables: |
| 240 | ** |
| @@ -251,13 +293,13 @@ | |
| 251 | Blob cmd; |
| 252 | blob_zero(&cmd); |
| 253 | shell_escape(&cmd, g.urlFossil); |
| 254 | blob_append(&cmd, " test-http ", -1); |
| 255 | shell_escape(&cmd, g.urlPath); |
| 256 | /* printf("%s\n", blob_str(&cmd)); fflush(stdout); */ |
| 257 | fprintf(sshOut, "%s\n", blob_str(&cmd)); |
| 258 | fflush(sshOut); |
| 259 | blob_reset(&cmd); |
| 260 | }else if( g.urlIsHttps ){ |
| 261 | #ifdef FOSSIL_ENABLE_SSL |
| 262 | rc = ssl_open(); |
| 263 | if( rc==0 ) transport.isOpen = 1; |
| @@ -403,11 +445,10 @@ | |
| 403 | int got; |
| 404 | if( sshIn ){ |
| 405 | int x; |
| 406 | int wanted = N; |
| 407 | got = 0; |
| 408 | /* printf("want %d bytes...\n", wanted); fflush(stdout); */ |
| 409 | while( wanted>0 ){ |
| 410 | x = read(sshIn, &zBuf[got], wanted); |
| 411 | if( x<=0 ) break; |
| 412 | got += x; |
| 413 | wanted -= x; |
| @@ -438,11 +479,14 @@ | |
| 438 | int transport_receive(char *zBuf, int N){ |
| 439 | int onHand; /* Bytes current held in the transport buffer */ |
| 440 | int nByte = 0; /* Bytes of content received */ |
| 441 | |
| 442 | onHand = transport.nUsed - transport.iCursor; |
| 443 | /* printf("request %d with %d on hand\n", N, onHand); fflush(stdout); */ |
| 444 | if( onHand>0 ){ |
| 445 | int toMove = onHand; |
| 446 | if( toMove>N ) toMove = N; |
| 447 | /* printf("bytes on hand: %d of %d\n", toMove, N); fflush(stdout); */ |
| 448 | memcpy(zBuf, &transport.pBuf[transport.iCursor], toMove); |
| @@ -460,10 +504,11 @@ | |
| 460 | if( got>0 ){ |
| 461 | nByte += got; |
| 462 | transport.nRcvd += got; |
| 463 | } |
| 464 | } |
| 465 | return nByte; |
| 466 | } |
| 467 | |
| 468 | /* |
| 469 | ** Load up to N new bytes of content into the transport.pBuf buffer. |
| @@ -532,11 +577,11 @@ | |
| 532 | } |
| 533 | break; |
| 534 | } |
| 535 | i++; |
| 536 | } |
| 537 | /* printf("Got line: [%s]\n", &transport.pBuf[iStart]); */ |
| 538 | return &transport.pBuf[iStart]; |
| 539 | } |
| 540 | |
| 541 | void transport_global_shutdown(void){ |
| 542 | if( g.urlIsSsh && sshPid ){ |
| 543 |
| --- src/http_transport.c | |
| +++ src/http_transport.c | |
| @@ -183,10 +183,11 @@ | |
| 183 | ** and run an SSH command to talk to the remote machine. |
| 184 | */ |
| 185 | const char *zSsh; /* The base SSH command */ |
| 186 | Blob zCmd; /* The SSH command */ |
| 187 | char *zHost; /* The host name to contact */ |
| 188 | int n; /* Size of prefix string */ |
| 189 | |
| 190 | zSsh = db_get("ssh-command", zDefaultSshCmd); |
| 191 | blob_init(&zCmd, zSsh, -1); |
| 192 | if( g.urlPort!=g.urlDfltPort ){ |
| 193 | #ifdef __MINGW32__ |
| @@ -219,22 +220,63 @@ | |
| 220 | } |
| 221 | #endif |
| 222 | }else{ |
| 223 | zHost = mprintf("%s", g.urlName); |
| 224 | } |
| 225 | n = blob_size(&zCmd); |
| 226 | blob_append(&zCmd, " ", 1); |
| 227 | shell_escape(&zCmd, zHost); |
| 228 | if( g.urlShell ){ |
| 229 | blob_appendf(&zCmd, " %s", g.urlShell); |
| 230 | }else{ |
| 231 | #if defined(FOSSIL_ENABLE_SSH_FAR_SIDE) |
| 232 | /* The following works. But only if the fossil on the remote side |
| 233 | ** is recent enough to support the test-ssh-far-side command. That |
| 234 | ** command was added on 2013-02-06. We will leave this turned off |
| 235 | ** until most fossil servers have upgraded to that version or a later |
| 236 | ** version. The sync will still work as long as the shell on the far |
| 237 | ** side is bash and not tcsh. And if the default far side shell is |
| 238 | ** tcsh, then the shell=/bin/bash query parameter can be used as a |
| 239 | ** work-around. Enable this code after about a year... |
| 240 | */ |
| 241 | blob_appendf(&zCmd, " exec %s test-ssh-far-side", g.urlFossil); |
| 242 | #endif |
| 243 | } |
| 244 | fossil_print("%s\n", blob_str(&zCmd)+n); /* Show tail of SSH command */ |
| 245 | free(zHost); |
| 246 | popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid); |
| 247 | if( sshPid==0 ){ |
| 248 | fossil_fatal("cannot start ssh tunnel using [%b]", &zCmd); |
| 249 | } |
| 250 | blob_reset(&zCmd); |
| 251 | transport_ssh_startup(); |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | /* |
| 256 | ** COMMAND: test-ssh-far-side |
| 257 | ** |
| 258 | ** Read lines of input text, one by one, and evaluate each line using |
| 259 | ** system(). The ssh: sync protocol uses this on the far side of the |
| 260 | ** SSH link. |
| 261 | */ |
| 262 | void test_ssh_far_side_cmd(void){ |
| 263 | int i = 0; |
| 264 | int got; |
| 265 | char zLine[5000]; |
| 266 | while( i<sizeof(zLine) ){ |
| 267 | got = read(0, zLine+i, 1); |
| 268 | if( got==0 ) return; |
| 269 | if( zLine[i]=='\n' ){ |
| 270 | zLine[i] = 0; |
| 271 | system(zLine); |
| 272 | i = 0; |
| 273 | }else{ |
| 274 | i++; |
| 275 | } |
| 276 | } |
| 277 | } |
| 278 | |
| 279 | /* |
| 280 | ** Open a connection to the server. The server is defined by the following |
| 281 | ** global variables: |
| 282 | ** |
| @@ -251,13 +293,13 @@ | |
| 293 | Blob cmd; |
| 294 | blob_zero(&cmd); |
| 295 | shell_escape(&cmd, g.urlFossil); |
| 296 | blob_append(&cmd, " test-http ", -1); |
| 297 | shell_escape(&cmd, g.urlPath); |
| 298 | fprintf(sshOut, "%s || true\n", blob_str(&cmd)); |
| 299 | fflush(sshOut); |
| 300 | if( g.fSshTrace ) printf("Sent: [%s]\n", blob_str(&cmd)); |
| 301 | blob_reset(&cmd); |
| 302 | }else if( g.urlIsHttps ){ |
| 303 | #ifdef FOSSIL_ENABLE_SSL |
| 304 | rc = ssl_open(); |
| 305 | if( rc==0 ) transport.isOpen = 1; |
| @@ -403,11 +445,10 @@ | |
| 445 | int got; |
| 446 | if( sshIn ){ |
| 447 | int x; |
| 448 | int wanted = N; |
| 449 | got = 0; |
| 450 | while( wanted>0 ){ |
| 451 | x = read(sshIn, &zBuf[got], wanted); |
| 452 | if( x<=0 ) break; |
| 453 | got += x; |
| 454 | wanted -= x; |
| @@ -438,11 +479,14 @@ | |
| 479 | int transport_receive(char *zBuf, int N){ |
| 480 | int onHand; /* Bytes current held in the transport buffer */ |
| 481 | int nByte = 0; /* Bytes of content received */ |
| 482 | |
| 483 | onHand = transport.nUsed - transport.iCursor; |
| 484 | if( g.fSshTrace){ |
| 485 | printf("Reading %d bytes with %d on hand... ", N, onHand); |
| 486 | fflush(stdout); |
| 487 | } |
| 488 | if( onHand>0 ){ |
| 489 | int toMove = onHand; |
| 490 | if( toMove>N ) toMove = N; |
| 491 | /* printf("bytes on hand: %d of %d\n", toMove, N); fflush(stdout); */ |
| 492 | memcpy(zBuf, &transport.pBuf[transport.iCursor], toMove); |
| @@ -460,10 +504,11 @@ | |
| 504 | if( got>0 ){ |
| 505 | nByte += got; |
| 506 | transport.nRcvd += got; |
| 507 | } |
| 508 | } |
| 509 | if( g.fSshTrace ) printf("Got %d bytes\n", nByte); |
| 510 | return nByte; |
| 511 | } |
| 512 | |
| 513 | /* |
| 514 | ** Load up to N new bytes of content into the transport.pBuf buffer. |
| @@ -532,11 +577,11 @@ | |
| 577 | } |
| 578 | break; |
| 579 | } |
| 580 | i++; |
| 581 | } |
| 582 | if( g.fSshTrace ) printf("Got line: [%s]\n", &transport.pBuf[iStart]); |
| 583 | return &transport.pBuf[iStart]; |
| 584 | } |
| 585 | |
| 586 | void transport_global_shutdown(void){ |
| 587 | if( g.urlIsSsh && sshPid ){ |
| 588 |
+3
-2
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -208,10 +208,12 @@ | ||
| 208 | 208 | fossil_print("project-code: %s\n", db_get("project-code", "")); |
| 209 | 209 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 210 | 210 | if( vid ){ |
| 211 | 211 | show_common_info(vid, "checkout:", 1, 1); |
| 212 | 212 | } |
| 213 | + fossil_print("checkins: %d\n", | |
| 214 | + db_int(-1, "SELECT count(distinct mid) FROM mlink /*scan*/")); | |
| 213 | 215 | }else{ |
| 214 | 216 | int rid; |
| 215 | 217 | rid = name_to_rid(g.argv[2]); |
| 216 | 218 | if( rid==0 ){ |
| 217 | 219 | fossil_panic("no such object: %s\n", g.argv[2]); |
| @@ -1313,11 +1315,10 @@ | ||
| 1313 | 1315 | char *zV2; |
| 1314 | 1316 | const char *zRe; |
| 1315 | 1317 | ReCompiled *pRe = 0; |
| 1316 | 1318 | u64 diffFlags; |
| 1317 | 1319 | const char *zStyle = "sbsdiff"; |
| 1318 | - const char *zReErr = 0; | |
| 1319 | 1320 | |
| 1320 | 1321 | login_check_credentials(); |
| 1321 | 1322 | if( !g.perm.Read ){ login_needed(); return; } |
| 1322 | 1323 | v1 = name_to_rid_www("v1"); |
| 1323 | 1324 | v2 = name_to_rid_www("v2"); |
| @@ -1340,11 +1341,11 @@ | ||
| 1340 | 1341 | diffFlags |= DIFF_LINENO; |
| 1341 | 1342 | zStyle = "udiff"; |
| 1342 | 1343 | } |
| 1343 | 1344 | } |
| 1344 | 1345 | zRe = P("regex"); |
| 1345 | - if( zRe ) zReErr = re_compile(&pRe, zRe, 0); | |
| 1346 | + if( zRe ) re_compile(&pRe, zRe, 0); | |
| 1346 | 1347 | content_get(v1, &c1); |
| 1347 | 1348 | content_get(v2, &c2); |
| 1348 | 1349 | text_diff(&c1, &c2, pOut, pRe, diffFlags); |
| 1349 | 1350 | blob_reset(&c1); |
| 1350 | 1351 | blob_reset(&c2); |
| 1351 | 1352 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -208,10 +208,12 @@ | |
| 208 | fossil_print("project-code: %s\n", db_get("project-code", "")); |
| 209 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 210 | if( vid ){ |
| 211 | show_common_info(vid, "checkout:", 1, 1); |
| 212 | } |
| 213 | }else{ |
| 214 | int rid; |
| 215 | rid = name_to_rid(g.argv[2]); |
| 216 | if( rid==0 ){ |
| 217 | fossil_panic("no such object: %s\n", g.argv[2]); |
| @@ -1313,11 +1315,10 @@ | |
| 1313 | char *zV2; |
| 1314 | const char *zRe; |
| 1315 | ReCompiled *pRe = 0; |
| 1316 | u64 diffFlags; |
| 1317 | const char *zStyle = "sbsdiff"; |
| 1318 | const char *zReErr = 0; |
| 1319 | |
| 1320 | login_check_credentials(); |
| 1321 | if( !g.perm.Read ){ login_needed(); return; } |
| 1322 | v1 = name_to_rid_www("v1"); |
| 1323 | v2 = name_to_rid_www("v2"); |
| @@ -1340,11 +1341,11 @@ | |
| 1340 | diffFlags |= DIFF_LINENO; |
| 1341 | zStyle = "udiff"; |
| 1342 | } |
| 1343 | } |
| 1344 | zRe = P("regex"); |
| 1345 | if( zRe ) zReErr = re_compile(&pRe, zRe, 0); |
| 1346 | content_get(v1, &c1); |
| 1347 | content_get(v2, &c2); |
| 1348 | text_diff(&c1, &c2, pOut, pRe, diffFlags); |
| 1349 | blob_reset(&c1); |
| 1350 | blob_reset(&c2); |
| 1351 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -208,10 +208,12 @@ | |
| 208 | fossil_print("project-code: %s\n", db_get("project-code", "")); |
| 209 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 210 | if( vid ){ |
| 211 | show_common_info(vid, "checkout:", 1, 1); |
| 212 | } |
| 213 | fossil_print("checkins: %d\n", |
| 214 | db_int(-1, "SELECT count(distinct mid) FROM mlink /*scan*/")); |
| 215 | }else{ |
| 216 | int rid; |
| 217 | rid = name_to_rid(g.argv[2]); |
| 218 | if( rid==0 ){ |
| 219 | fossil_panic("no such object: %s\n", g.argv[2]); |
| @@ -1313,11 +1315,10 @@ | |
| 1315 | char *zV2; |
| 1316 | const char *zRe; |
| 1317 | ReCompiled *pRe = 0; |
| 1318 | u64 diffFlags; |
| 1319 | const char *zStyle = "sbsdiff"; |
| 1320 | |
| 1321 | login_check_credentials(); |
| 1322 | if( !g.perm.Read ){ login_needed(); return; } |
| 1323 | v1 = name_to_rid_www("v1"); |
| 1324 | v2 = name_to_rid_www("v2"); |
| @@ -1340,11 +1341,11 @@ | |
| 1341 | diffFlags |= DIFF_LINENO; |
| 1342 | zStyle = "udiff"; |
| 1343 | } |
| 1344 | } |
| 1345 | zRe = P("regex"); |
| 1346 | if( zRe ) re_compile(&pRe, zRe, 0); |
| 1347 | content_get(v1, &c1); |
| 1348 | content_get(v2, &c2); |
| 1349 | text_diff(&c1, &c2, pOut, pRe, diffFlags); |
| 1350 | blob_reset(&c1); |
| 1351 | blob_reset(&c2); |
| 1352 |
+3
-2
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -208,10 +208,12 @@ | ||
| 208 | 208 | fossil_print("project-code: %s\n", db_get("project-code", "")); |
| 209 | 209 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 210 | 210 | if( vid ){ |
| 211 | 211 | show_common_info(vid, "checkout:", 1, 1); |
| 212 | 212 | } |
| 213 | + fossil_print("checkins: %d\n", | |
| 214 | + db_int(-1, "SELECT count(distinct mid) FROM mlink /*scan*/")); | |
| 213 | 215 | }else{ |
| 214 | 216 | int rid; |
| 215 | 217 | rid = name_to_rid(g.argv[2]); |
| 216 | 218 | if( rid==0 ){ |
| 217 | 219 | fossil_panic("no such object: %s\n", g.argv[2]); |
| @@ -1313,11 +1315,10 @@ | ||
| 1313 | 1315 | char *zV2; |
| 1314 | 1316 | const char *zRe; |
| 1315 | 1317 | ReCompiled *pRe = 0; |
| 1316 | 1318 | u64 diffFlags; |
| 1317 | 1319 | const char *zStyle = "sbsdiff"; |
| 1318 | - const char *zReErr = 0; | |
| 1319 | 1320 | |
| 1320 | 1321 | login_check_credentials(); |
| 1321 | 1322 | if( !g.perm.Read ){ login_needed(); return; } |
| 1322 | 1323 | v1 = name_to_rid_www("v1"); |
| 1323 | 1324 | v2 = name_to_rid_www("v2"); |
| @@ -1340,11 +1341,11 @@ | ||
| 1340 | 1341 | diffFlags |= DIFF_LINENO; |
| 1341 | 1342 | zStyle = "udiff"; |
| 1342 | 1343 | } |
| 1343 | 1344 | } |
| 1344 | 1345 | zRe = P("regex"); |
| 1345 | - if( zRe ) zReErr = re_compile(&pRe, zRe, 0); | |
| 1346 | + if( zRe ) re_compile(&pRe, zRe, 0); | |
| 1346 | 1347 | content_get(v1, &c1); |
| 1347 | 1348 | content_get(v2, &c2); |
| 1348 | 1349 | text_diff(&c1, &c2, pOut, pRe, diffFlags); |
| 1349 | 1350 | blob_reset(&c1); |
| 1350 | 1351 | blob_reset(&c2); |
| 1351 | 1352 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -208,10 +208,12 @@ | |
| 208 | fossil_print("project-code: %s\n", db_get("project-code", "")); |
| 209 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 210 | if( vid ){ |
| 211 | show_common_info(vid, "checkout:", 1, 1); |
| 212 | } |
| 213 | }else{ |
| 214 | int rid; |
| 215 | rid = name_to_rid(g.argv[2]); |
| 216 | if( rid==0 ){ |
| 217 | fossil_panic("no such object: %s\n", g.argv[2]); |
| @@ -1313,11 +1315,10 @@ | |
| 1313 | char *zV2; |
| 1314 | const char *zRe; |
| 1315 | ReCompiled *pRe = 0; |
| 1316 | u64 diffFlags; |
| 1317 | const char *zStyle = "sbsdiff"; |
| 1318 | const char *zReErr = 0; |
| 1319 | |
| 1320 | login_check_credentials(); |
| 1321 | if( !g.perm.Read ){ login_needed(); return; } |
| 1322 | v1 = name_to_rid_www("v1"); |
| 1323 | v2 = name_to_rid_www("v2"); |
| @@ -1340,11 +1341,11 @@ | |
| 1340 | diffFlags |= DIFF_LINENO; |
| 1341 | zStyle = "udiff"; |
| 1342 | } |
| 1343 | } |
| 1344 | zRe = P("regex"); |
| 1345 | if( zRe ) zReErr = re_compile(&pRe, zRe, 0); |
| 1346 | content_get(v1, &c1); |
| 1347 | content_get(v2, &c2); |
| 1348 | text_diff(&c1, &c2, pOut, pRe, diffFlags); |
| 1349 | blob_reset(&c1); |
| 1350 | blob_reset(&c2); |
| 1351 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -208,10 +208,12 @@ | |
| 208 | fossil_print("project-code: %s\n", db_get("project-code", "")); |
| 209 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 210 | if( vid ){ |
| 211 | show_common_info(vid, "checkout:", 1, 1); |
| 212 | } |
| 213 | fossil_print("checkins: %d\n", |
| 214 | db_int(-1, "SELECT count(distinct mid) FROM mlink /*scan*/")); |
| 215 | }else{ |
| 216 | int rid; |
| 217 | rid = name_to_rid(g.argv[2]); |
| 218 | if( rid==0 ){ |
| 219 | fossil_panic("no such object: %s\n", g.argv[2]); |
| @@ -1313,11 +1315,10 @@ | |
| 1315 | char *zV2; |
| 1316 | const char *zRe; |
| 1317 | ReCompiled *pRe = 0; |
| 1318 | u64 diffFlags; |
| 1319 | const char *zStyle = "sbsdiff"; |
| 1320 | |
| 1321 | login_check_credentials(); |
| 1322 | if( !g.perm.Read ){ login_needed(); return; } |
| 1323 | v1 = name_to_rid_www("v1"); |
| 1324 | v2 = name_to_rid_www("v2"); |
| @@ -1340,11 +1341,11 @@ | |
| 1341 | diffFlags |= DIFF_LINENO; |
| 1342 | zStyle = "udiff"; |
| 1343 | } |
| 1344 | } |
| 1345 | zRe = P("regex"); |
| 1346 | if( zRe ) re_compile(&pRe, zRe, 0); |
| 1347 | content_get(v1, &c1); |
| 1348 | content_get(v2, &c2); |
| 1349 | text_diff(&c1, &c2, pOut, pRe, diffFlags); |
| 1350 | blob_reset(&c1); |
| 1351 | blob_reset(&c2); |
| 1352 |
+3
-2
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -208,10 +208,12 @@ | ||
| 208 | 208 | fossil_print("project-code: %s\n", db_get("project-code", "")); |
| 209 | 209 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 210 | 210 | if( vid ){ |
| 211 | 211 | show_common_info(vid, "checkout:", 1, 1); |
| 212 | 212 | } |
| 213 | + fossil_print("checkins: %d\n", | |
| 214 | + db_int(-1, "SELECT count(distinct mid) FROM mlink /*scan*/")); | |
| 213 | 215 | }else{ |
| 214 | 216 | int rid; |
| 215 | 217 | rid = name_to_rid(g.argv[2]); |
| 216 | 218 | if( rid==0 ){ |
| 217 | 219 | fossil_panic("no such object: %s\n", g.argv[2]); |
| @@ -1313,11 +1315,10 @@ | ||
| 1313 | 1315 | char *zV2; |
| 1314 | 1316 | const char *zRe; |
| 1315 | 1317 | ReCompiled *pRe = 0; |
| 1316 | 1318 | u64 diffFlags; |
| 1317 | 1319 | const char *zStyle = "sbsdiff"; |
| 1318 | - const char *zReErr = 0; | |
| 1319 | 1320 | |
| 1320 | 1321 | login_check_credentials(); |
| 1321 | 1322 | if( !g.perm.Read ){ login_needed(); return; } |
| 1322 | 1323 | v1 = name_to_rid_www("v1"); |
| 1323 | 1324 | v2 = name_to_rid_www("v2"); |
| @@ -1340,11 +1341,11 @@ | ||
| 1340 | 1341 | diffFlags |= DIFF_LINENO; |
| 1341 | 1342 | zStyle = "udiff"; |
| 1342 | 1343 | } |
| 1343 | 1344 | } |
| 1344 | 1345 | zRe = P("regex"); |
| 1345 | - if( zRe ) zReErr = re_compile(&pRe, zRe, 0); | |
| 1346 | + if( zRe ) re_compile(&pRe, zRe, 0); | |
| 1346 | 1347 | content_get(v1, &c1); |
| 1347 | 1348 | content_get(v2, &c2); |
| 1348 | 1349 | text_diff(&c1, &c2, pOut, pRe, diffFlags); |
| 1349 | 1350 | blob_reset(&c1); |
| 1350 | 1351 | blob_reset(&c2); |
| 1351 | 1352 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -208,10 +208,12 @@ | |
| 208 | fossil_print("project-code: %s\n", db_get("project-code", "")); |
| 209 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 210 | if( vid ){ |
| 211 | show_common_info(vid, "checkout:", 1, 1); |
| 212 | } |
| 213 | }else{ |
| 214 | int rid; |
| 215 | rid = name_to_rid(g.argv[2]); |
| 216 | if( rid==0 ){ |
| 217 | fossil_panic("no such object: %s\n", g.argv[2]); |
| @@ -1313,11 +1315,10 @@ | |
| 1313 | char *zV2; |
| 1314 | const char *zRe; |
| 1315 | ReCompiled *pRe = 0; |
| 1316 | u64 diffFlags; |
| 1317 | const char *zStyle = "sbsdiff"; |
| 1318 | const char *zReErr = 0; |
| 1319 | |
| 1320 | login_check_credentials(); |
| 1321 | if( !g.perm.Read ){ login_needed(); return; } |
| 1322 | v1 = name_to_rid_www("v1"); |
| 1323 | v2 = name_to_rid_www("v2"); |
| @@ -1340,11 +1341,11 @@ | |
| 1340 | diffFlags |= DIFF_LINENO; |
| 1341 | zStyle = "udiff"; |
| 1342 | } |
| 1343 | } |
| 1344 | zRe = P("regex"); |
| 1345 | if( zRe ) zReErr = re_compile(&pRe, zRe, 0); |
| 1346 | content_get(v1, &c1); |
| 1347 | content_get(v2, &c2); |
| 1348 | text_diff(&c1, &c2, pOut, pRe, diffFlags); |
| 1349 | blob_reset(&c1); |
| 1350 | blob_reset(&c2); |
| 1351 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -208,10 +208,12 @@ | |
| 208 | fossil_print("project-code: %s\n", db_get("project-code", "")); |
| 209 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 210 | if( vid ){ |
| 211 | show_common_info(vid, "checkout:", 1, 1); |
| 212 | } |
| 213 | fossil_print("checkins: %d\n", |
| 214 | db_int(-1, "SELECT count(distinct mid) FROM mlink /*scan*/")); |
| 215 | }else{ |
| 216 | int rid; |
| 217 | rid = name_to_rid(g.argv[2]); |
| 218 | if( rid==0 ){ |
| 219 | fossil_panic("no such object: %s\n", g.argv[2]); |
| @@ -1313,11 +1315,10 @@ | |
| 1315 | char *zV2; |
| 1316 | const char *zRe; |
| 1317 | ReCompiled *pRe = 0; |
| 1318 | u64 diffFlags; |
| 1319 | const char *zStyle = "sbsdiff"; |
| 1320 | |
| 1321 | login_check_credentials(); |
| 1322 | if( !g.perm.Read ){ login_needed(); return; } |
| 1323 | v1 = name_to_rid_www("v1"); |
| 1324 | v2 = name_to_rid_www("v2"); |
| @@ -1340,11 +1341,11 @@ | |
| 1341 | diffFlags |= DIFF_LINENO; |
| 1342 | zStyle = "udiff"; |
| 1343 | } |
| 1344 | } |
| 1345 | zRe = P("regex"); |
| 1346 | if( zRe ) re_compile(&pRe, zRe, 0); |
| 1347 | content_get(v1, &c1); |
| 1348 | content_get(v2, &c2); |
| 1349 | text_diff(&c1, &c2, pOut, pRe, diffFlags); |
| 1350 | blob_reset(&c1); |
| 1351 | blob_reset(&c2); |
| 1352 |
+1
-3
| --- src/json.c | ||
| +++ src/json.c | ||
| @@ -2085,15 +2085,13 @@ | ||
| 2085 | 2085 | cson_object_set(jo, "ticketCount", cson_value_new_integer((cson_int_t)n)); |
| 2086 | 2086 | }/*full*/ |
| 2087 | 2087 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 2088 | 2088 | " + 0.99"); |
| 2089 | 2089 | cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n)); |
| 2090 | - cson_object_set(jo, "ageYears", cson_value_new_double(n/365.24)); | |
| 2090 | + cson_object_set(jo, "ageYears", cson_value_new_double(n/365.2425)); | |
| 2091 | 2091 | sqlite3_snprintf(BufLen, zBuf, db_get("project-code","")); |
| 2092 | 2092 | SETBUF(jo, "projectCode"); |
| 2093 | - sqlite3_snprintf(BufLen, zBuf, db_get("server-code","")); | |
| 2094 | - SETBUF(jo, "serverCode"); | |
| 2095 | 2093 | cson_object_set(jo, "compiler", cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME))); |
| 2096 | 2094 | |
| 2097 | 2095 | jv2 = cson_value_new_object(); |
| 2098 | 2096 | jo2 = cson_value_get_object(jv2); |
| 2099 | 2097 | cson_object_set(jo, "sqlite", jv2); |
| 2100 | 2098 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -2085,15 +2085,13 @@ | |
| 2085 | cson_object_set(jo, "ticketCount", cson_value_new_integer((cson_int_t)n)); |
| 2086 | }/*full*/ |
| 2087 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 2088 | " + 0.99"); |
| 2089 | cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n)); |
| 2090 | cson_object_set(jo, "ageYears", cson_value_new_double(n/365.24)); |
| 2091 | sqlite3_snprintf(BufLen, zBuf, db_get("project-code","")); |
| 2092 | SETBUF(jo, "projectCode"); |
| 2093 | sqlite3_snprintf(BufLen, zBuf, db_get("server-code","")); |
| 2094 | SETBUF(jo, "serverCode"); |
| 2095 | cson_object_set(jo, "compiler", cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME))); |
| 2096 | |
| 2097 | jv2 = cson_value_new_object(); |
| 2098 | jo2 = cson_value_get_object(jv2); |
| 2099 | cson_object_set(jo, "sqlite", jv2); |
| 2100 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -2085,15 +2085,13 @@ | |
| 2085 | cson_object_set(jo, "ticketCount", cson_value_new_integer((cson_int_t)n)); |
| 2086 | }/*full*/ |
| 2087 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 2088 | " + 0.99"); |
| 2089 | cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n)); |
| 2090 | cson_object_set(jo, "ageYears", cson_value_new_double(n/365.2425)); |
| 2091 | sqlite3_snprintf(BufLen, zBuf, db_get("project-code","")); |
| 2092 | SETBUF(jo, "projectCode"); |
| 2093 | cson_object_set(jo, "compiler", cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME))); |
| 2094 | |
| 2095 | jv2 = cson_value_new_object(); |
| 2096 | jo2 = cson_value_get_object(jv2); |
| 2097 | cson_object_set(jo, "sqlite", jv2); |
| 2098 |
+1
-3
| --- src/json.c | ||
| +++ src/json.c | ||
| @@ -2085,15 +2085,13 @@ | ||
| 2085 | 2085 | cson_object_set(jo, "ticketCount", cson_value_new_integer((cson_int_t)n)); |
| 2086 | 2086 | }/*full*/ |
| 2087 | 2087 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 2088 | 2088 | " + 0.99"); |
| 2089 | 2089 | cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n)); |
| 2090 | - cson_object_set(jo, "ageYears", cson_value_new_double(n/365.24)); | |
| 2090 | + cson_object_set(jo, "ageYears", cson_value_new_double(n/365.2425)); | |
| 2091 | 2091 | sqlite3_snprintf(BufLen, zBuf, db_get("project-code","")); |
| 2092 | 2092 | SETBUF(jo, "projectCode"); |
| 2093 | - sqlite3_snprintf(BufLen, zBuf, db_get("server-code","")); | |
| 2094 | - SETBUF(jo, "serverCode"); | |
| 2095 | 2093 | cson_object_set(jo, "compiler", cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME))); |
| 2096 | 2094 | |
| 2097 | 2095 | jv2 = cson_value_new_object(); |
| 2098 | 2096 | jo2 = cson_value_get_object(jv2); |
| 2099 | 2097 | cson_object_set(jo, "sqlite", jv2); |
| 2100 | 2098 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -2085,15 +2085,13 @@ | |
| 2085 | cson_object_set(jo, "ticketCount", cson_value_new_integer((cson_int_t)n)); |
| 2086 | }/*full*/ |
| 2087 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 2088 | " + 0.99"); |
| 2089 | cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n)); |
| 2090 | cson_object_set(jo, "ageYears", cson_value_new_double(n/365.24)); |
| 2091 | sqlite3_snprintf(BufLen, zBuf, db_get("project-code","")); |
| 2092 | SETBUF(jo, "projectCode"); |
| 2093 | sqlite3_snprintf(BufLen, zBuf, db_get("server-code","")); |
| 2094 | SETBUF(jo, "serverCode"); |
| 2095 | cson_object_set(jo, "compiler", cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME))); |
| 2096 | |
| 2097 | jv2 = cson_value_new_object(); |
| 2098 | jo2 = cson_value_get_object(jv2); |
| 2099 | cson_object_set(jo, "sqlite", jv2); |
| 2100 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -2085,15 +2085,13 @@ | |
| 2085 | cson_object_set(jo, "ticketCount", cson_value_new_integer((cson_int_t)n)); |
| 2086 | }/*full*/ |
| 2087 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 2088 | " + 0.99"); |
| 2089 | cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n)); |
| 2090 | cson_object_set(jo, "ageYears", cson_value_new_double(n/365.2425)); |
| 2091 | sqlite3_snprintf(BufLen, zBuf, db_get("project-code","")); |
| 2092 | SETBUF(jo, "projectCode"); |
| 2093 | cson_object_set(jo, "compiler", cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME))); |
| 2094 | |
| 2095 | jv2 = cson_value_new_object(); |
| 2096 | jo2 = cson_value_get_object(jv2); |
| 2097 | cson_object_set(jo, "sqlite", jv2); |
| 2098 |
+1
-1
| --- src/json_detail.h | ||
| +++ src/json_detail.h | ||
| @@ -199,11 +199,11 @@ | ||
| 199 | 199 | char const * requestId; |
| 200 | 200 | char const * resultCode; |
| 201 | 201 | char const * resultText; |
| 202 | 202 | char const * timestamp; |
| 203 | 203 | } FossilJsonKeys_; |
| 204 | -const FossilJsonKeys_ FossilJsonKeys; | |
| 204 | +extern const FossilJsonKeys_ FossilJsonKeys; | |
| 205 | 205 | |
| 206 | 206 | /* |
| 207 | 207 | ** A page/command dispatch helper for fossil_json_f() implementations. |
| 208 | 208 | ** pages must be an array of JsonPageDef commands which we can |
| 209 | 209 | ** dispatch. The final item in the array MUST have a NULL name |
| 210 | 210 |
| --- src/json_detail.h | |
| +++ src/json_detail.h | |
| @@ -199,11 +199,11 @@ | |
| 199 | char const * requestId; |
| 200 | char const * resultCode; |
| 201 | char const * resultText; |
| 202 | char const * timestamp; |
| 203 | } FossilJsonKeys_; |
| 204 | const FossilJsonKeys_ FossilJsonKeys; |
| 205 | |
| 206 | /* |
| 207 | ** A page/command dispatch helper for fossil_json_f() implementations. |
| 208 | ** pages must be an array of JsonPageDef commands which we can |
| 209 | ** dispatch. The final item in the array MUST have a NULL name |
| 210 |
| --- src/json_detail.h | |
| +++ src/json_detail.h | |
| @@ -199,11 +199,11 @@ | |
| 199 | char const * requestId; |
| 200 | char const * resultCode; |
| 201 | char const * resultText; |
| 202 | char const * timestamp; |
| 203 | } FossilJsonKeys_; |
| 204 | extern const FossilJsonKeys_ FossilJsonKeys; |
| 205 | |
| 206 | /* |
| 207 | ** A page/command dispatch helper for fossil_json_f() implementations. |
| 208 | ** pages must be an array of JsonPageDef commands which we can |
| 209 | ** dispatch. The final item in the array MUST have a NULL name |
| 210 |
+1
-1
| --- src/json_detail.h | ||
| +++ src/json_detail.h | ||
| @@ -199,11 +199,11 @@ | ||
| 199 | 199 | char const * requestId; |
| 200 | 200 | char const * resultCode; |
| 201 | 201 | char const * resultText; |
| 202 | 202 | char const * timestamp; |
| 203 | 203 | } FossilJsonKeys_; |
| 204 | -const FossilJsonKeys_ FossilJsonKeys; | |
| 204 | +extern const FossilJsonKeys_ FossilJsonKeys; | |
| 205 | 205 | |
| 206 | 206 | /* |
| 207 | 207 | ** A page/command dispatch helper for fossil_json_f() implementations. |
| 208 | 208 | ** pages must be an array of JsonPageDef commands which we can |
| 209 | 209 | ** dispatch. The final item in the array MUST have a NULL name |
| 210 | 210 |
| --- src/json_detail.h | |
| +++ src/json_detail.h | |
| @@ -199,11 +199,11 @@ | |
| 199 | char const * requestId; |
| 200 | char const * resultCode; |
| 201 | char const * resultText; |
| 202 | char const * timestamp; |
| 203 | } FossilJsonKeys_; |
| 204 | const FossilJsonKeys_ FossilJsonKeys; |
| 205 | |
| 206 | /* |
| 207 | ** A page/command dispatch helper for fossil_json_f() implementations. |
| 208 | ** pages must be an array of JsonPageDef commands which we can |
| 209 | ** dispatch. The final item in the array MUST have a NULL name |
| 210 |
| --- src/json_detail.h | |
| +++ src/json_detail.h | |
| @@ -199,11 +199,11 @@ | |
| 199 | char const * requestId; |
| 200 | char const * resultCode; |
| 201 | char const * resultText; |
| 202 | char const * timestamp; |
| 203 | } FossilJsonKeys_; |
| 204 | extern const FossilJsonKeys_ FossilJsonKeys; |
| 205 | |
| 206 | /* |
| 207 | ** A page/command dispatch helper for fossil_json_f() implementations. |
| 208 | ** pages must be an array of JsonPageDef commands which we can |
| 209 | ** dispatch. The final item in the array MUST have a NULL name |
| 210 |
+1
-1
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -625,11 +625,11 @@ | ||
| 625 | 625 | @ <a href="%s(g.zTop)/register?g=%T(P("G"))">create one</a>. |
| 626 | 626 | } |
| 627 | 627 | if( zAnonPw ){ |
| 628 | 628 | unsigned int uSeed = captcha_seed(); |
| 629 | 629 | char const *zDecoded = captcha_decode(uSeed); |
| 630 | - int bAutoCaptcha = db_get_boolean("auto-captcha", 1); | |
| 630 | + int bAutoCaptcha = db_get_boolean("auto-captcha", 0); | |
| 631 | 631 | char *zCaptcha = captcha_render(zDecoded); |
| 632 | 632 | |
| 633 | 633 | @ <p><input type="hidden" name="cs" value="%u(uSeed)" /> |
| 634 | 634 | @ Visitors may enter <b>anonymous</b> as the user-ID with |
| 635 | 635 | @ the 8-character hexadecimal password shown below:</p> |
| 636 | 636 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -625,11 +625,11 @@ | |
| 625 | @ <a href="%s(g.zTop)/register?g=%T(P("G"))">create one</a>. |
| 626 | } |
| 627 | if( zAnonPw ){ |
| 628 | unsigned int uSeed = captcha_seed(); |
| 629 | char const *zDecoded = captcha_decode(uSeed); |
| 630 | int bAutoCaptcha = db_get_boolean("auto-captcha", 1); |
| 631 | char *zCaptcha = captcha_render(zDecoded); |
| 632 | |
| 633 | @ <p><input type="hidden" name="cs" value="%u(uSeed)" /> |
| 634 | @ Visitors may enter <b>anonymous</b> as the user-ID with |
| 635 | @ the 8-character hexadecimal password shown below:</p> |
| 636 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -625,11 +625,11 @@ | |
| 625 | @ <a href="%s(g.zTop)/register?g=%T(P("G"))">create one</a>. |
| 626 | } |
| 627 | if( zAnonPw ){ |
| 628 | unsigned int uSeed = captcha_seed(); |
| 629 | char const *zDecoded = captcha_decode(uSeed); |
| 630 | int bAutoCaptcha = db_get_boolean("auto-captcha", 0); |
| 631 | char *zCaptcha = captcha_render(zDecoded); |
| 632 | |
| 633 | @ <p><input type="hidden" name="cs" value="%u(uSeed)" /> |
| 634 | @ Visitors may enter <b>anonymous</b> as the user-ID with |
| 635 | @ the 8-character hexadecimal password shown below:</p> |
| 636 |
+1
-1
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -625,11 +625,11 @@ | ||
| 625 | 625 | @ <a href="%s(g.zTop)/register?g=%T(P("G"))">create one</a>. |
| 626 | 626 | } |
| 627 | 627 | if( zAnonPw ){ |
| 628 | 628 | unsigned int uSeed = captcha_seed(); |
| 629 | 629 | char const *zDecoded = captcha_decode(uSeed); |
| 630 | - int bAutoCaptcha = db_get_boolean("auto-captcha", 1); | |
| 630 | + int bAutoCaptcha = db_get_boolean("auto-captcha", 0); | |
| 631 | 631 | char *zCaptcha = captcha_render(zDecoded); |
| 632 | 632 | |
| 633 | 633 | @ <p><input type="hidden" name="cs" value="%u(uSeed)" /> |
| 634 | 634 | @ Visitors may enter <b>anonymous</b> as the user-ID with |
| 635 | 635 | @ the 8-character hexadecimal password shown below:</p> |
| 636 | 636 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -625,11 +625,11 @@ | |
| 625 | @ <a href="%s(g.zTop)/register?g=%T(P("G"))">create one</a>. |
| 626 | } |
| 627 | if( zAnonPw ){ |
| 628 | unsigned int uSeed = captcha_seed(); |
| 629 | char const *zDecoded = captcha_decode(uSeed); |
| 630 | int bAutoCaptcha = db_get_boolean("auto-captcha", 1); |
| 631 | char *zCaptcha = captcha_render(zDecoded); |
| 632 | |
| 633 | @ <p><input type="hidden" name="cs" value="%u(uSeed)" /> |
| 634 | @ Visitors may enter <b>anonymous</b> as the user-ID with |
| 635 | @ the 8-character hexadecimal password shown below:</p> |
| 636 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -625,11 +625,11 @@ | |
| 625 | @ <a href="%s(g.zTop)/register?g=%T(P("G"))">create one</a>. |
| 626 | } |
| 627 | if( zAnonPw ){ |
| 628 | unsigned int uSeed = captcha_seed(); |
| 629 | char const *zDecoded = captcha_decode(uSeed); |
| 630 | int bAutoCaptcha = db_get_boolean("auto-captcha", 0); |
| 631 | char *zCaptcha = captcha_render(zDecoded); |
| 632 | |
| 633 | @ <p><input type="hidden" name="cs" value="%u(uSeed)" /> |
| 634 | @ Visitors may enter <b>anonymous</b> as the user-ID with |
| 635 | @ the 8-character hexadecimal password shown below:</p> |
| 636 |
+17
-28
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -170,11 +170,12 @@ | ||
| 170 | 170 | char *urlPath; /* Pathname for http: */ |
| 171 | 171 | char *urlUser; /* User id for http: */ |
| 172 | 172 | char *urlPasswd; /* Password for http: */ |
| 173 | 173 | char *urlCanonical; /* Canonical representation of the URL */ |
| 174 | 174 | char *urlProxyAuth; /* Proxy-Authorizer: string */ |
| 175 | - char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */ | |
| 175 | + char *urlFossil; /* The fossil query parameter on ssh: */ | |
| 176 | + char *urlShell; /* The shell query parameter on ssh: */ | |
| 176 | 177 | int dontKeepUrl; /* Do not persist the URL */ |
| 177 | 178 | |
| 178 | 179 | const char *zLogin; /* Login name. "" if not logged in. */ |
| 179 | 180 | const char *zSSLIdentity; /* Value of --ssl-identity option, filename of SSL client identity */ |
| 180 | 181 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| @@ -605,14 +606,11 @@ | ||
| 605 | 606 | if( g.cgiOutput && once ){ |
| 606 | 607 | once = 0; |
| 607 | 608 | cgi_printf("<p class=\"generalError\">%h</p>", z); |
| 608 | 609 | cgi_reply(); |
| 609 | 610 | }else if( !g.fQuiet ){ |
| 610 | - char *zOut = mprintf("%s: %s\n", g.argv[0], z); | |
| 611 | - fossil_force_newline(); | |
| 612 | - fossil_puts(zOut, 1); | |
| 613 | - fossil_free(zOut); | |
| 611 | + fossil_trace("%s: %s\n", g.argv[0], z); | |
| 614 | 612 | } |
| 615 | 613 | } |
| 616 | 614 | free(z); |
| 617 | 615 | db_force_rollback(); |
| 618 | 616 | fossil_exit(rc); |
| @@ -636,17 +634,14 @@ | ||
| 636 | 634 | else |
| 637 | 635 | #endif |
| 638 | 636 | { |
| 639 | 637 | if( g.cgiOutput ){ |
| 640 | 638 | g.cgiOutput = 0; |
| 641 | - cgi_printf("<p class=\"generalError\">%h</p>", z); | |
| 639 | + cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z); | |
| 642 | 640 | cgi_reply(); |
| 643 | 641 | }else if( !g.fQuiet ){ |
| 644 | - char *zOut = mprintf("\r%s: %s\n", g.argv[0], z); | |
| 645 | - fossil_force_newline(); | |
| 646 | - fossil_puts(zOut, 1); | |
| 647 | - fossil_free(zOut); | |
| 642 | + fossil_trace("%s: %s\n", g.argv[0], z); | |
| 648 | 643 | } |
| 649 | 644 | } |
| 650 | 645 | free(z); |
| 651 | 646 | db_force_rollback(); |
| 652 | 647 | fossil_exit(rc); |
| @@ -679,17 +674,14 @@ | ||
| 679 | 674 | } else |
| 680 | 675 | #endif |
| 681 | 676 | { |
| 682 | 677 | if( g.cgiOutput ){ |
| 683 | 678 | g.cgiOutput = 0; |
| 684 | - cgi_printf("<p class=\"generalError\">%h</p>", z); | |
| 679 | + cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z); | |
| 685 | 680 | cgi_reply(); |
| 686 | 681 | }else{ |
| 687 | - char *zOut = mprintf("\r%s: %s\n", g.argv[0], z); | |
| 688 | - fossil_force_newline(); | |
| 689 | - fossil_puts(zOut, 1); | |
| 690 | - fossil_free(zOut); | |
| 682 | + fossil_trace("%s: %s\n", g.argv[0], z); | |
| 691 | 683 | } |
| 692 | 684 | } |
| 693 | 685 | db_force_rollback(); |
| 694 | 686 | fossil_exit(rc); |
| 695 | 687 | } |
| @@ -707,16 +699,13 @@ | ||
| 707 | 699 | json_warn( FSL_JSON_W_UNKNOWN, z ); |
| 708 | 700 | }else |
| 709 | 701 | #endif |
| 710 | 702 | { |
| 711 | 703 | if( g.cgiOutput ){ |
| 712 | - cgi_printf("<p class=\"generalError\">%h</p>", z); | |
| 704 | + cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z); | |
| 713 | 705 | }else{ |
| 714 | - char *zOut = mprintf("\r%s: %s\n", g.argv[0], z); | |
| 715 | - fossil_force_newline(); | |
| 716 | - fossil_puts(zOut, 1); | |
| 717 | - fossil_free(zOut); | |
| 706 | + fossil_trace("%s: %s\n", g.argv[0], z); | |
| 718 | 707 | } |
| 719 | 708 | } |
| 720 | 709 | free(z); |
| 721 | 710 | } |
| 722 | 711 | |
| @@ -747,13 +736,11 @@ | ||
| 747 | 736 | ** Who knows why - this is just the way windows works. |
| 748 | 737 | */ |
| 749 | 738 | char *zNewCmd = mprintf("\"%s\"", zOrigCmd); |
| 750 | 739 | WCHAR *zUnicode = fossil_utf8_to_unicode(zNewCmd); |
| 751 | 740 | if( g.fSystemTrace ) { |
| 752 | - char *zOut = mprintf("SYSTEM: %s\n", zNewCmd); | |
| 753 | - fossil_puts(zOut, 1); | |
| 754 | - fossil_free(zOut); | |
| 741 | + fossil_trace("SYSTEM: %s\n", zNewCmd); | |
| 755 | 742 | } |
| 756 | 743 | rc = _wsystem(zUnicode); |
| 757 | 744 | fossil_unicode_free(zUnicode); |
| 758 | 745 | free(zNewCmd); |
| 759 | 746 | #else |
| @@ -977,11 +964,11 @@ | ||
| 977 | 964 | ** |
| 978 | 965 | ** Display information on how to use COMMAND. To display a list of |
| 979 | 966 | ** available commands one of: |
| 980 | 967 | ** |
| 981 | 968 | ** %fossil help Show common commands |
| 982 | -** %fossil help --all Show both command and auxiliary commands | |
| 969 | +** %fossil help --all Show both common and auxiliary commands | |
| 983 | 970 | ** %fossil help --test Show test commands only |
| 984 | 971 | ** %fossil help --aux Show auxiliary commands only |
| 985 | 972 | */ |
| 986 | 973 | void help_cmd(void){ |
| 987 | 974 | int rc, idx; |
| @@ -1225,15 +1212,17 @@ | ||
| 1225 | 1212 | } |
| 1226 | 1213 | zRepo = "/"; |
| 1227 | 1214 | }else{ |
| 1228 | 1215 | for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){} |
| 1229 | 1216 | if( zDir[i]!='/' ) fossil_panic("bad repository name: %s", zRepo); |
| 1230 | - zDir[i] = 0; | |
| 1231 | - if( chdir(zDir) || chroot(zDir) || chdir("/") ){ | |
| 1232 | - fossil_fatal("unable to chroot into %s", zDir); | |
| 1217 | + if( i>0 ){ | |
| 1218 | + zDir[i] = 0; | |
| 1219 | + if( chdir(zDir) || chroot(zDir) || chdir("/") ){ | |
| 1220 | + fossil_fatal("unable to chroot into %s", zDir); | |
| 1221 | + } | |
| 1222 | + zDir[i] = '/'; | |
| 1233 | 1223 | } |
| 1234 | - zDir[i] = '/'; | |
| 1235 | 1224 | zRepo = &zDir[i]; |
| 1236 | 1225 | } |
| 1237 | 1226 | if( stat(zRepo, &sStat)!=0 ){ |
| 1238 | 1227 | fossil_fatal("cannot stat() repository: %s", zRepo); |
| 1239 | 1228 | } |
| 1240 | 1229 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -170,11 +170,12 @@ | |
| 170 | char *urlPath; /* Pathname for http: */ |
| 171 | char *urlUser; /* User id for http: */ |
| 172 | char *urlPasswd; /* Password for http: */ |
| 173 | char *urlCanonical; /* Canonical representation of the URL */ |
| 174 | char *urlProxyAuth; /* Proxy-Authorizer: string */ |
| 175 | char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */ |
| 176 | int dontKeepUrl; /* Do not persist the URL */ |
| 177 | |
| 178 | const char *zLogin; /* Login name. "" if not logged in. */ |
| 179 | const char *zSSLIdentity; /* Value of --ssl-identity option, filename of SSL client identity */ |
| 180 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| @@ -605,14 +606,11 @@ | |
| 605 | if( g.cgiOutput && once ){ |
| 606 | once = 0; |
| 607 | cgi_printf("<p class=\"generalError\">%h</p>", z); |
| 608 | cgi_reply(); |
| 609 | }else if( !g.fQuiet ){ |
| 610 | char *zOut = mprintf("%s: %s\n", g.argv[0], z); |
| 611 | fossil_force_newline(); |
| 612 | fossil_puts(zOut, 1); |
| 613 | fossil_free(zOut); |
| 614 | } |
| 615 | } |
| 616 | free(z); |
| 617 | db_force_rollback(); |
| 618 | fossil_exit(rc); |
| @@ -636,17 +634,14 @@ | |
| 636 | else |
| 637 | #endif |
| 638 | { |
| 639 | if( g.cgiOutput ){ |
| 640 | g.cgiOutput = 0; |
| 641 | cgi_printf("<p class=\"generalError\">%h</p>", z); |
| 642 | cgi_reply(); |
| 643 | }else if( !g.fQuiet ){ |
| 644 | char *zOut = mprintf("\r%s: %s\n", g.argv[0], z); |
| 645 | fossil_force_newline(); |
| 646 | fossil_puts(zOut, 1); |
| 647 | fossil_free(zOut); |
| 648 | } |
| 649 | } |
| 650 | free(z); |
| 651 | db_force_rollback(); |
| 652 | fossil_exit(rc); |
| @@ -679,17 +674,14 @@ | |
| 679 | } else |
| 680 | #endif |
| 681 | { |
| 682 | if( g.cgiOutput ){ |
| 683 | g.cgiOutput = 0; |
| 684 | cgi_printf("<p class=\"generalError\">%h</p>", z); |
| 685 | cgi_reply(); |
| 686 | }else{ |
| 687 | char *zOut = mprintf("\r%s: %s\n", g.argv[0], z); |
| 688 | fossil_force_newline(); |
| 689 | fossil_puts(zOut, 1); |
| 690 | fossil_free(zOut); |
| 691 | } |
| 692 | } |
| 693 | db_force_rollback(); |
| 694 | fossil_exit(rc); |
| 695 | } |
| @@ -707,16 +699,13 @@ | |
| 707 | json_warn( FSL_JSON_W_UNKNOWN, z ); |
| 708 | }else |
| 709 | #endif |
| 710 | { |
| 711 | if( g.cgiOutput ){ |
| 712 | cgi_printf("<p class=\"generalError\">%h</p>", z); |
| 713 | }else{ |
| 714 | char *zOut = mprintf("\r%s: %s\n", g.argv[0], z); |
| 715 | fossil_force_newline(); |
| 716 | fossil_puts(zOut, 1); |
| 717 | fossil_free(zOut); |
| 718 | } |
| 719 | } |
| 720 | free(z); |
| 721 | } |
| 722 | |
| @@ -747,13 +736,11 @@ | |
| 747 | ** Who knows why - this is just the way windows works. |
| 748 | */ |
| 749 | char *zNewCmd = mprintf("\"%s\"", zOrigCmd); |
| 750 | WCHAR *zUnicode = fossil_utf8_to_unicode(zNewCmd); |
| 751 | if( g.fSystemTrace ) { |
| 752 | char *zOut = mprintf("SYSTEM: %s\n", zNewCmd); |
| 753 | fossil_puts(zOut, 1); |
| 754 | fossil_free(zOut); |
| 755 | } |
| 756 | rc = _wsystem(zUnicode); |
| 757 | fossil_unicode_free(zUnicode); |
| 758 | free(zNewCmd); |
| 759 | #else |
| @@ -977,11 +964,11 @@ | |
| 977 | ** |
| 978 | ** Display information on how to use COMMAND. To display a list of |
| 979 | ** available commands one of: |
| 980 | ** |
| 981 | ** %fossil help Show common commands |
| 982 | ** %fossil help --all Show both command and auxiliary commands |
| 983 | ** %fossil help --test Show test commands only |
| 984 | ** %fossil help --aux Show auxiliary commands only |
| 985 | */ |
| 986 | void help_cmd(void){ |
| 987 | int rc, idx; |
| @@ -1225,15 +1212,17 @@ | |
| 1225 | } |
| 1226 | zRepo = "/"; |
| 1227 | }else{ |
| 1228 | for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){} |
| 1229 | if( zDir[i]!='/' ) fossil_panic("bad repository name: %s", zRepo); |
| 1230 | zDir[i] = 0; |
| 1231 | if( chdir(zDir) || chroot(zDir) || chdir("/") ){ |
| 1232 | fossil_fatal("unable to chroot into %s", zDir); |
| 1233 | } |
| 1234 | zDir[i] = '/'; |
| 1235 | zRepo = &zDir[i]; |
| 1236 | } |
| 1237 | if( stat(zRepo, &sStat)!=0 ){ |
| 1238 | fossil_fatal("cannot stat() repository: %s", zRepo); |
| 1239 | } |
| 1240 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -170,11 +170,12 @@ | |
| 170 | char *urlPath; /* Pathname for http: */ |
| 171 | char *urlUser; /* User id for http: */ |
| 172 | char *urlPasswd; /* Password for http: */ |
| 173 | char *urlCanonical; /* Canonical representation of the URL */ |
| 174 | char *urlProxyAuth; /* Proxy-Authorizer: string */ |
| 175 | char *urlFossil; /* The fossil query parameter on ssh: */ |
| 176 | char *urlShell; /* The shell query parameter on ssh: */ |
| 177 | int dontKeepUrl; /* Do not persist the URL */ |
| 178 | |
| 179 | const char *zLogin; /* Login name. "" if not logged in. */ |
| 180 | const char *zSSLIdentity; /* Value of --ssl-identity option, filename of SSL client identity */ |
| 181 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| @@ -605,14 +606,11 @@ | |
| 606 | if( g.cgiOutput && once ){ |
| 607 | once = 0; |
| 608 | cgi_printf("<p class=\"generalError\">%h</p>", z); |
| 609 | cgi_reply(); |
| 610 | }else if( !g.fQuiet ){ |
| 611 | fossil_trace("%s: %s\n", g.argv[0], z); |
| 612 | } |
| 613 | } |
| 614 | free(z); |
| 615 | db_force_rollback(); |
| 616 | fossil_exit(rc); |
| @@ -636,17 +634,14 @@ | |
| 634 | else |
| 635 | #endif |
| 636 | { |
| 637 | if( g.cgiOutput ){ |
| 638 | g.cgiOutput = 0; |
| 639 | cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z); |
| 640 | cgi_reply(); |
| 641 | }else if( !g.fQuiet ){ |
| 642 | fossil_trace("%s: %s\n", g.argv[0], z); |
| 643 | } |
| 644 | } |
| 645 | free(z); |
| 646 | db_force_rollback(); |
| 647 | fossil_exit(rc); |
| @@ -679,17 +674,14 @@ | |
| 674 | } else |
| 675 | #endif |
| 676 | { |
| 677 | if( g.cgiOutput ){ |
| 678 | g.cgiOutput = 0; |
| 679 | cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z); |
| 680 | cgi_reply(); |
| 681 | }else{ |
| 682 | fossil_trace("%s: %s\n", g.argv[0], z); |
| 683 | } |
| 684 | } |
| 685 | db_force_rollback(); |
| 686 | fossil_exit(rc); |
| 687 | } |
| @@ -707,16 +699,13 @@ | |
| 699 | json_warn( FSL_JSON_W_UNKNOWN, z ); |
| 700 | }else |
| 701 | #endif |
| 702 | { |
| 703 | if( g.cgiOutput ){ |
| 704 | cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z); |
| 705 | }else{ |
| 706 | fossil_trace("%s: %s\n", g.argv[0], z); |
| 707 | } |
| 708 | } |
| 709 | free(z); |
| 710 | } |
| 711 | |
| @@ -747,13 +736,11 @@ | |
| 736 | ** Who knows why - this is just the way windows works. |
| 737 | */ |
| 738 | char *zNewCmd = mprintf("\"%s\"", zOrigCmd); |
| 739 | WCHAR *zUnicode = fossil_utf8_to_unicode(zNewCmd); |
| 740 | if( g.fSystemTrace ) { |
| 741 | fossil_trace("SYSTEM: %s\n", zNewCmd); |
| 742 | } |
| 743 | rc = _wsystem(zUnicode); |
| 744 | fossil_unicode_free(zUnicode); |
| 745 | free(zNewCmd); |
| 746 | #else |
| @@ -977,11 +964,11 @@ | |
| 964 | ** |
| 965 | ** Display information on how to use COMMAND. To display a list of |
| 966 | ** available commands one of: |
| 967 | ** |
| 968 | ** %fossil help Show common commands |
| 969 | ** %fossil help --all Show both common and auxiliary commands |
| 970 | ** %fossil help --test Show test commands only |
| 971 | ** %fossil help --aux Show auxiliary commands only |
| 972 | */ |
| 973 | void help_cmd(void){ |
| 974 | int rc, idx; |
| @@ -1225,15 +1212,17 @@ | |
| 1212 | } |
| 1213 | zRepo = "/"; |
| 1214 | }else{ |
| 1215 | for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){} |
| 1216 | if( zDir[i]!='/' ) fossil_panic("bad repository name: %s", zRepo); |
| 1217 | if( i>0 ){ |
| 1218 | zDir[i] = 0; |
| 1219 | if( chdir(zDir) || chroot(zDir) || chdir("/") ){ |
| 1220 | fossil_fatal("unable to chroot into %s", zDir); |
| 1221 | } |
| 1222 | zDir[i] = '/'; |
| 1223 | } |
| 1224 | zRepo = &zDir[i]; |
| 1225 | } |
| 1226 | if( stat(zRepo, &sStat)!=0 ){ |
| 1227 | fossil_fatal("cannot stat() repository: %s", zRepo); |
| 1228 | } |
| 1229 |
+17
-28
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -170,11 +170,12 @@ | ||
| 170 | 170 | char *urlPath; /* Pathname for http: */ |
| 171 | 171 | char *urlUser; /* User id for http: */ |
| 172 | 172 | char *urlPasswd; /* Password for http: */ |
| 173 | 173 | char *urlCanonical; /* Canonical representation of the URL */ |
| 174 | 174 | char *urlProxyAuth; /* Proxy-Authorizer: string */ |
| 175 | - char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */ | |
| 175 | + char *urlFossil; /* The fossil query parameter on ssh: */ | |
| 176 | + char *urlShell; /* The shell query parameter on ssh: */ | |
| 176 | 177 | int dontKeepUrl; /* Do not persist the URL */ |
| 177 | 178 | |
| 178 | 179 | const char *zLogin; /* Login name. "" if not logged in. */ |
| 179 | 180 | const char *zSSLIdentity; /* Value of --ssl-identity option, filename of SSL client identity */ |
| 180 | 181 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| @@ -605,14 +606,11 @@ | ||
| 605 | 606 | if( g.cgiOutput && once ){ |
| 606 | 607 | once = 0; |
| 607 | 608 | cgi_printf("<p class=\"generalError\">%h</p>", z); |
| 608 | 609 | cgi_reply(); |
| 609 | 610 | }else if( !g.fQuiet ){ |
| 610 | - char *zOut = mprintf("%s: %s\n", g.argv[0], z); | |
| 611 | - fossil_force_newline(); | |
| 612 | - fossil_puts(zOut, 1); | |
| 613 | - fossil_free(zOut); | |
| 611 | + fossil_trace("%s: %s\n", g.argv[0], z); | |
| 614 | 612 | } |
| 615 | 613 | } |
| 616 | 614 | free(z); |
| 617 | 615 | db_force_rollback(); |
| 618 | 616 | fossil_exit(rc); |
| @@ -636,17 +634,14 @@ | ||
| 636 | 634 | else |
| 637 | 635 | #endif |
| 638 | 636 | { |
| 639 | 637 | if( g.cgiOutput ){ |
| 640 | 638 | g.cgiOutput = 0; |
| 641 | - cgi_printf("<p class=\"generalError\">%h</p>", z); | |
| 639 | + cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z); | |
| 642 | 640 | cgi_reply(); |
| 643 | 641 | }else if( !g.fQuiet ){ |
| 644 | - char *zOut = mprintf("\r%s: %s\n", g.argv[0], z); | |
| 645 | - fossil_force_newline(); | |
| 646 | - fossil_puts(zOut, 1); | |
| 647 | - fossil_free(zOut); | |
| 642 | + fossil_trace("%s: %s\n", g.argv[0], z); | |
| 648 | 643 | } |
| 649 | 644 | } |
| 650 | 645 | free(z); |
| 651 | 646 | db_force_rollback(); |
| 652 | 647 | fossil_exit(rc); |
| @@ -679,17 +674,14 @@ | ||
| 679 | 674 | } else |
| 680 | 675 | #endif |
| 681 | 676 | { |
| 682 | 677 | if( g.cgiOutput ){ |
| 683 | 678 | g.cgiOutput = 0; |
| 684 | - cgi_printf("<p class=\"generalError\">%h</p>", z); | |
| 679 | + cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z); | |
| 685 | 680 | cgi_reply(); |
| 686 | 681 | }else{ |
| 687 | - char *zOut = mprintf("\r%s: %s\n", g.argv[0], z); | |
| 688 | - fossil_force_newline(); | |
| 689 | - fossil_puts(zOut, 1); | |
| 690 | - fossil_free(zOut); | |
| 682 | + fossil_trace("%s: %s\n", g.argv[0], z); | |
| 691 | 683 | } |
| 692 | 684 | } |
| 693 | 685 | db_force_rollback(); |
| 694 | 686 | fossil_exit(rc); |
| 695 | 687 | } |
| @@ -707,16 +699,13 @@ | ||
| 707 | 699 | json_warn( FSL_JSON_W_UNKNOWN, z ); |
| 708 | 700 | }else |
| 709 | 701 | #endif |
| 710 | 702 | { |
| 711 | 703 | if( g.cgiOutput ){ |
| 712 | - cgi_printf("<p class=\"generalError\">%h</p>", z); | |
| 704 | + cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z); | |
| 713 | 705 | }else{ |
| 714 | - char *zOut = mprintf("\r%s: %s\n", g.argv[0], z); | |
| 715 | - fossil_force_newline(); | |
| 716 | - fossil_puts(zOut, 1); | |
| 717 | - fossil_free(zOut); | |
| 706 | + fossil_trace("%s: %s\n", g.argv[0], z); | |
| 718 | 707 | } |
| 719 | 708 | } |
| 720 | 709 | free(z); |
| 721 | 710 | } |
| 722 | 711 | |
| @@ -747,13 +736,11 @@ | ||
| 747 | 736 | ** Who knows why - this is just the way windows works. |
| 748 | 737 | */ |
| 749 | 738 | char *zNewCmd = mprintf("\"%s\"", zOrigCmd); |
| 750 | 739 | WCHAR *zUnicode = fossil_utf8_to_unicode(zNewCmd); |
| 751 | 740 | if( g.fSystemTrace ) { |
| 752 | - char *zOut = mprintf("SYSTEM: %s\n", zNewCmd); | |
| 753 | - fossil_puts(zOut, 1); | |
| 754 | - fossil_free(zOut); | |
| 741 | + fossil_trace("SYSTEM: %s\n", zNewCmd); | |
| 755 | 742 | } |
| 756 | 743 | rc = _wsystem(zUnicode); |
| 757 | 744 | fossil_unicode_free(zUnicode); |
| 758 | 745 | free(zNewCmd); |
| 759 | 746 | #else |
| @@ -977,11 +964,11 @@ | ||
| 977 | 964 | ** |
| 978 | 965 | ** Display information on how to use COMMAND. To display a list of |
| 979 | 966 | ** available commands one of: |
| 980 | 967 | ** |
| 981 | 968 | ** %fossil help Show common commands |
| 982 | -** %fossil help --all Show both command and auxiliary commands | |
| 969 | +** %fossil help --all Show both common and auxiliary commands | |
| 983 | 970 | ** %fossil help --test Show test commands only |
| 984 | 971 | ** %fossil help --aux Show auxiliary commands only |
| 985 | 972 | */ |
| 986 | 973 | void help_cmd(void){ |
| 987 | 974 | int rc, idx; |
| @@ -1225,15 +1212,17 @@ | ||
| 1225 | 1212 | } |
| 1226 | 1213 | zRepo = "/"; |
| 1227 | 1214 | }else{ |
| 1228 | 1215 | for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){} |
| 1229 | 1216 | if( zDir[i]!='/' ) fossil_panic("bad repository name: %s", zRepo); |
| 1230 | - zDir[i] = 0; | |
| 1231 | - if( chdir(zDir) || chroot(zDir) || chdir("/") ){ | |
| 1232 | - fossil_fatal("unable to chroot into %s", zDir); | |
| 1217 | + if( i>0 ){ | |
| 1218 | + zDir[i] = 0; | |
| 1219 | + if( chdir(zDir) || chroot(zDir) || chdir("/") ){ | |
| 1220 | + fossil_fatal("unable to chroot into %s", zDir); | |
| 1221 | + } | |
| 1222 | + zDir[i] = '/'; | |
| 1233 | 1223 | } |
| 1234 | - zDir[i] = '/'; | |
| 1235 | 1224 | zRepo = &zDir[i]; |
| 1236 | 1225 | } |
| 1237 | 1226 | if( stat(zRepo, &sStat)!=0 ){ |
| 1238 | 1227 | fossil_fatal("cannot stat() repository: %s", zRepo); |
| 1239 | 1228 | } |
| 1240 | 1229 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -170,11 +170,12 @@ | |
| 170 | char *urlPath; /* Pathname for http: */ |
| 171 | char *urlUser; /* User id for http: */ |
| 172 | char *urlPasswd; /* Password for http: */ |
| 173 | char *urlCanonical; /* Canonical representation of the URL */ |
| 174 | char *urlProxyAuth; /* Proxy-Authorizer: string */ |
| 175 | char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */ |
| 176 | int dontKeepUrl; /* Do not persist the URL */ |
| 177 | |
| 178 | const char *zLogin; /* Login name. "" if not logged in. */ |
| 179 | const char *zSSLIdentity; /* Value of --ssl-identity option, filename of SSL client identity */ |
| 180 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| @@ -605,14 +606,11 @@ | |
| 605 | if( g.cgiOutput && once ){ |
| 606 | once = 0; |
| 607 | cgi_printf("<p class=\"generalError\">%h</p>", z); |
| 608 | cgi_reply(); |
| 609 | }else if( !g.fQuiet ){ |
| 610 | char *zOut = mprintf("%s: %s\n", g.argv[0], z); |
| 611 | fossil_force_newline(); |
| 612 | fossil_puts(zOut, 1); |
| 613 | fossil_free(zOut); |
| 614 | } |
| 615 | } |
| 616 | free(z); |
| 617 | db_force_rollback(); |
| 618 | fossil_exit(rc); |
| @@ -636,17 +634,14 @@ | |
| 636 | else |
| 637 | #endif |
| 638 | { |
| 639 | if( g.cgiOutput ){ |
| 640 | g.cgiOutput = 0; |
| 641 | cgi_printf("<p class=\"generalError\">%h</p>", z); |
| 642 | cgi_reply(); |
| 643 | }else if( !g.fQuiet ){ |
| 644 | char *zOut = mprintf("\r%s: %s\n", g.argv[0], z); |
| 645 | fossil_force_newline(); |
| 646 | fossil_puts(zOut, 1); |
| 647 | fossil_free(zOut); |
| 648 | } |
| 649 | } |
| 650 | free(z); |
| 651 | db_force_rollback(); |
| 652 | fossil_exit(rc); |
| @@ -679,17 +674,14 @@ | |
| 679 | } else |
| 680 | #endif |
| 681 | { |
| 682 | if( g.cgiOutput ){ |
| 683 | g.cgiOutput = 0; |
| 684 | cgi_printf("<p class=\"generalError\">%h</p>", z); |
| 685 | cgi_reply(); |
| 686 | }else{ |
| 687 | char *zOut = mprintf("\r%s: %s\n", g.argv[0], z); |
| 688 | fossil_force_newline(); |
| 689 | fossil_puts(zOut, 1); |
| 690 | fossil_free(zOut); |
| 691 | } |
| 692 | } |
| 693 | db_force_rollback(); |
| 694 | fossil_exit(rc); |
| 695 | } |
| @@ -707,16 +699,13 @@ | |
| 707 | json_warn( FSL_JSON_W_UNKNOWN, z ); |
| 708 | }else |
| 709 | #endif |
| 710 | { |
| 711 | if( g.cgiOutput ){ |
| 712 | cgi_printf("<p class=\"generalError\">%h</p>", z); |
| 713 | }else{ |
| 714 | char *zOut = mprintf("\r%s: %s\n", g.argv[0], z); |
| 715 | fossil_force_newline(); |
| 716 | fossil_puts(zOut, 1); |
| 717 | fossil_free(zOut); |
| 718 | } |
| 719 | } |
| 720 | free(z); |
| 721 | } |
| 722 | |
| @@ -747,13 +736,11 @@ | |
| 747 | ** Who knows why - this is just the way windows works. |
| 748 | */ |
| 749 | char *zNewCmd = mprintf("\"%s\"", zOrigCmd); |
| 750 | WCHAR *zUnicode = fossil_utf8_to_unicode(zNewCmd); |
| 751 | if( g.fSystemTrace ) { |
| 752 | char *zOut = mprintf("SYSTEM: %s\n", zNewCmd); |
| 753 | fossil_puts(zOut, 1); |
| 754 | fossil_free(zOut); |
| 755 | } |
| 756 | rc = _wsystem(zUnicode); |
| 757 | fossil_unicode_free(zUnicode); |
| 758 | free(zNewCmd); |
| 759 | #else |
| @@ -977,11 +964,11 @@ | |
| 977 | ** |
| 978 | ** Display information on how to use COMMAND. To display a list of |
| 979 | ** available commands one of: |
| 980 | ** |
| 981 | ** %fossil help Show common commands |
| 982 | ** %fossil help --all Show both command and auxiliary commands |
| 983 | ** %fossil help --test Show test commands only |
| 984 | ** %fossil help --aux Show auxiliary commands only |
| 985 | */ |
| 986 | void help_cmd(void){ |
| 987 | int rc, idx; |
| @@ -1225,15 +1212,17 @@ | |
| 1225 | } |
| 1226 | zRepo = "/"; |
| 1227 | }else{ |
| 1228 | for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){} |
| 1229 | if( zDir[i]!='/' ) fossil_panic("bad repository name: %s", zRepo); |
| 1230 | zDir[i] = 0; |
| 1231 | if( chdir(zDir) || chroot(zDir) || chdir("/") ){ |
| 1232 | fossil_fatal("unable to chroot into %s", zDir); |
| 1233 | } |
| 1234 | zDir[i] = '/'; |
| 1235 | zRepo = &zDir[i]; |
| 1236 | } |
| 1237 | if( stat(zRepo, &sStat)!=0 ){ |
| 1238 | fossil_fatal("cannot stat() repository: %s", zRepo); |
| 1239 | } |
| 1240 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -170,11 +170,12 @@ | |
| 170 | char *urlPath; /* Pathname for http: */ |
| 171 | char *urlUser; /* User id for http: */ |
| 172 | char *urlPasswd; /* Password for http: */ |
| 173 | char *urlCanonical; /* Canonical representation of the URL */ |
| 174 | char *urlProxyAuth; /* Proxy-Authorizer: string */ |
| 175 | char *urlFossil; /* The fossil query parameter on ssh: */ |
| 176 | char *urlShell; /* The shell query parameter on ssh: */ |
| 177 | int dontKeepUrl; /* Do not persist the URL */ |
| 178 | |
| 179 | const char *zLogin; /* Login name. "" if not logged in. */ |
| 180 | const char *zSSLIdentity; /* Value of --ssl-identity option, filename of SSL client identity */ |
| 181 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| @@ -605,14 +606,11 @@ | |
| 606 | if( g.cgiOutput && once ){ |
| 607 | once = 0; |
| 608 | cgi_printf("<p class=\"generalError\">%h</p>", z); |
| 609 | cgi_reply(); |
| 610 | }else if( !g.fQuiet ){ |
| 611 | fossil_trace("%s: %s\n", g.argv[0], z); |
| 612 | } |
| 613 | } |
| 614 | free(z); |
| 615 | db_force_rollback(); |
| 616 | fossil_exit(rc); |
| @@ -636,17 +634,14 @@ | |
| 634 | else |
| 635 | #endif |
| 636 | { |
| 637 | if( g.cgiOutput ){ |
| 638 | g.cgiOutput = 0; |
| 639 | cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z); |
| 640 | cgi_reply(); |
| 641 | }else if( !g.fQuiet ){ |
| 642 | fossil_trace("%s: %s\n", g.argv[0], z); |
| 643 | } |
| 644 | } |
| 645 | free(z); |
| 646 | db_force_rollback(); |
| 647 | fossil_exit(rc); |
| @@ -679,17 +674,14 @@ | |
| 674 | } else |
| 675 | #endif |
| 676 | { |
| 677 | if( g.cgiOutput ){ |
| 678 | g.cgiOutput = 0; |
| 679 | cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z); |
| 680 | cgi_reply(); |
| 681 | }else{ |
| 682 | fossil_trace("%s: %s\n", g.argv[0], z); |
| 683 | } |
| 684 | } |
| 685 | db_force_rollback(); |
| 686 | fossil_exit(rc); |
| 687 | } |
| @@ -707,16 +699,13 @@ | |
| 699 | json_warn( FSL_JSON_W_UNKNOWN, z ); |
| 700 | }else |
| 701 | #endif |
| 702 | { |
| 703 | if( g.cgiOutput ){ |
| 704 | cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z); |
| 705 | }else{ |
| 706 | fossil_trace("%s: %s\n", g.argv[0], z); |
| 707 | } |
| 708 | } |
| 709 | free(z); |
| 710 | } |
| 711 | |
| @@ -747,13 +736,11 @@ | |
| 736 | ** Who knows why - this is just the way windows works. |
| 737 | */ |
| 738 | char *zNewCmd = mprintf("\"%s\"", zOrigCmd); |
| 739 | WCHAR *zUnicode = fossil_utf8_to_unicode(zNewCmd); |
| 740 | if( g.fSystemTrace ) { |
| 741 | fossil_trace("SYSTEM: %s\n", zNewCmd); |
| 742 | } |
| 743 | rc = _wsystem(zUnicode); |
| 744 | fossil_unicode_free(zUnicode); |
| 745 | free(zNewCmd); |
| 746 | #else |
| @@ -977,11 +964,11 @@ | |
| 964 | ** |
| 965 | ** Display information on how to use COMMAND. To display a list of |
| 966 | ** available commands one of: |
| 967 | ** |
| 968 | ** %fossil help Show common commands |
| 969 | ** %fossil help --all Show both common and auxiliary commands |
| 970 | ** %fossil help --test Show test commands only |
| 971 | ** %fossil help --aux Show auxiliary commands only |
| 972 | */ |
| 973 | void help_cmd(void){ |
| 974 | int rc, idx; |
| @@ -1225,15 +1212,17 @@ | |
| 1212 | } |
| 1213 | zRepo = "/"; |
| 1214 | }else{ |
| 1215 | for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){} |
| 1216 | if( zDir[i]!='/' ) fossil_panic("bad repository name: %s", zRepo); |
| 1217 | if( i>0 ){ |
| 1218 | zDir[i] = 0; |
| 1219 | if( chdir(zDir) || chroot(zDir) || chdir("/") ){ |
| 1220 | fossil_fatal("unable to chroot into %s", zDir); |
| 1221 | } |
| 1222 | zDir[i] = '/'; |
| 1223 | } |
| 1224 | zRepo = &zDir[i]; |
| 1225 | } |
| 1226 | if( stat(zRepo, &sStat)!=0 ){ |
| 1227 | fossil_fatal("cannot stat() repository: %s", zRepo); |
| 1228 | } |
| 1229 |
+1
-1
| --- src/main.mk | ||
| +++ src/main.mk | ||
| @@ -1146,11 +1146,11 @@ | ||
| 1146 | 1146 | $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c |
| 1147 | 1147 | $(XTCC) -c $(SRCDIR)/th_tcl.c -o $(OBJDIR)/th_tcl.o |
| 1148 | 1148 | |
| 1149 | 1149 | |
| 1150 | 1150 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1151 | - $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE | |
| 1151 | + $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o | |
| 1152 | 1152 | |
| 1153 | 1153 | # |
| 1154 | 1154 | # The list of all the targets that do not correspond to real files. This stops |
| 1155 | 1155 | # 'make' from getting confused when someone makes an error in a rule. |
| 1156 | 1156 | # |
| 1157 | 1157 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -1146,11 +1146,11 @@ | |
| 1146 | $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c |
| 1147 | $(XTCC) -c $(SRCDIR)/th_tcl.c -o $(OBJDIR)/th_tcl.o |
| 1148 | |
| 1149 | |
| 1150 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1151 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE |
| 1152 | |
| 1153 | # |
| 1154 | # The list of all the targets that do not correspond to real files. This stops |
| 1155 | # 'make' from getting confused when someone makes an error in a rule. |
| 1156 | # |
| 1157 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -1146,11 +1146,11 @@ | |
| 1146 | $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c |
| 1147 | $(XTCC) -c $(SRCDIR)/th_tcl.c -o $(OBJDIR)/th_tcl.o |
| 1148 | |
| 1149 | |
| 1150 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1151 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o |
| 1152 | |
| 1153 | # |
| 1154 | # The list of all the targets that do not correspond to real files. This stops |
| 1155 | # 'make' from getting confused when someone makes an error in a rule. |
| 1156 | # |
| 1157 |
+1
-1
| --- src/main.mk | ||
| +++ src/main.mk | ||
| @@ -1146,11 +1146,11 @@ | ||
| 1146 | 1146 | $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c |
| 1147 | 1147 | $(XTCC) -c $(SRCDIR)/th_tcl.c -o $(OBJDIR)/th_tcl.o |
| 1148 | 1148 | |
| 1149 | 1149 | |
| 1150 | 1150 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1151 | - $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE | |
| 1151 | + $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o | |
| 1152 | 1152 | |
| 1153 | 1153 | # |
| 1154 | 1154 | # The list of all the targets that do not correspond to real files. This stops |
| 1155 | 1155 | # 'make' from getting confused when someone makes an error in a rule. |
| 1156 | 1156 | # |
| 1157 | 1157 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -1146,11 +1146,11 @@ | |
| 1146 | $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c |
| 1147 | $(XTCC) -c $(SRCDIR)/th_tcl.c -o $(OBJDIR)/th_tcl.o |
| 1148 | |
| 1149 | |
| 1150 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1151 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE |
| 1152 | |
| 1153 | # |
| 1154 | # The list of all the targets that do not correspond to real files. This stops |
| 1155 | # 'make' from getting confused when someone makes an error in a rule. |
| 1156 | # |
| 1157 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -1146,11 +1146,11 @@ | |
| 1146 | $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c |
| 1147 | $(XTCC) -c $(SRCDIR)/th_tcl.c -o $(OBJDIR)/th_tcl.o |
| 1148 | |
| 1149 | |
| 1150 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1151 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o |
| 1152 | |
| 1153 | # |
| 1154 | # The list of all the targets that do not correspond to real files. This stops |
| 1155 | # 'make' from getting confused when someone makes an error in a rule. |
| 1156 | # |
| 1157 |
+34
-15
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -308,11 +308,11 @@ | ||
| 308 | 308 | writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$(OBJDIR)/th_tcl.o\n" |
| 309 | 309 | |
| 310 | 310 | set opt {} |
| 311 | 311 | writeln { |
| 312 | 312 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 313 | - $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE | |
| 313 | + $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o | |
| 314 | 314 | |
| 315 | 315 | # |
| 316 | 316 | # The list of all the targets that do not correspond to real files. This stops |
| 317 | 317 | # 'make' from getting confused when someone makes an error in a rule. |
| 318 | 318 | # |
| @@ -340,15 +340,15 @@ | ||
| 340 | 340 | # |
| 341 | 341 | # This file is automatically generated. Instead of editing this |
| 342 | 342 | # file, edit "makemake.tcl" then run "tclsh makemake.tcl" |
| 343 | 343 | # to regenerate this file. |
| 344 | 344 | # |
| 345 | -# This is a makefile for use on Windows/Linux/Darwin/Cygwin using MinGW or | |
| 346 | -# MinGW-w64. | |
| 345 | +# This is a makefile for use on Cygwin/Darwin/FreeBSD/Linux/Windows using | |
| 346 | +# MinGW or MinGW-w64. | |
| 347 | 347 | # |
| 348 | 348 | |
| 349 | -#### Select one of MinGW, MinGW-64 (32-bit) or MinGW-w64 (64-bit) compilers. | |
| 349 | +#### Select one of MinGW, MinGW-w64 (32-bit) or MinGW-w64 (64-bit) compilers. | |
| 350 | 350 | # By default, this is an empty string (i.e. use the native compiler). |
| 351 | 351 | # |
| 352 | 352 | PREFIX = |
| 353 | 353 | # PREFIX = mingw32- |
| 354 | 354 | # PREFIX = i686-pc-mingw32- |
| @@ -694,11 +694,11 @@ | ||
| 694 | 694 | ifdef FOSSIL_ENABLE_TCL |
| 695 | 695 | EXTRAOBJ += $(OBJDIR)/th_tcl.o |
| 696 | 696 | endif |
| 697 | 697 | |
| 698 | 698 | zlib: |
| 699 | - make -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a | |
| 699 | + $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a | |
| 700 | 700 | |
| 701 | 701 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib |
| 702 | 702 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o |
| 703 | 703 | |
| 704 | 704 | # This rule prevents make from using its default rules to try build |
| @@ -751,11 +751,11 @@ | ||
| 751 | 751 | set opt $SQLITE_OPTIONS |
| 752 | 752 | writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n" |
| 753 | 753 | |
| 754 | 754 | set opt {} |
| 755 | 755 | writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c" |
| 756 | -writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE\n" | |
| 756 | +writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o\n" | |
| 757 | 757 | writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_config.o \$(OBJDIR)/json_diff.o \$(OBJDIR)/json_dir.o \$(OBJDIR)/jsos_finfo.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_query.o \$(OBJDIR)/json_report.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h\n" |
| 758 | 758 | |
| 759 | 759 | writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h" |
| 760 | 760 | set opt {-Dmain=sqlite3_shell} |
| 761 | 761 | append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1" |
| @@ -959,18 +959,35 @@ | ||
| 959 | 959 | |
| 960 | 960 | # zlib options |
| 961 | 961 | ZINCDIR = $(B)\compat\zlib |
| 962 | 962 | ZLIBDIR = $(B)\compat\zlib |
| 963 | 963 | ZLIB = zlib.lib |
| 964 | + | |
| 965 | +# Uncomment to enable JSON API | |
| 966 | +# FOSSIL_ENABLE_JSON = 1 | |
| 967 | + | |
| 968 | +# Uncomment to enable markdown support | |
| 969 | +# FOSSIL_ENABLE_MARKDOWN = 1 | |
| 964 | 970 | |
| 965 | 971 | INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR) |
| 966 | 972 | |
| 967 | 973 | CFLAGS = -nologo -MT -O2 |
| 968 | 974 | BCC = $(CC) $(CFLAGS) |
| 969 | 975 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) |
| 976 | +RCC = rc -D_WIN32 -D_MSC_VER $(INCL) | |
| 970 | 977 | LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) |
| 971 | 978 | LIBDIR = -LIBPATH:$(ZLIBDIR) |
| 979 | + | |
| 980 | +!ifdef FOSSIL_ENABLE_JSON | |
| 981 | +TCC = $(TCC) -DFOSSIL_ENABLE_JSON | |
| 982 | +RCC = $(RCC) -DFOSSIL_ENABLE_JSON | |
| 983 | +!endif | |
| 984 | + | |
| 985 | +!ifdef FOSSIL_ENABLE_MARKDOWN | |
| 986 | +TCC = $(TCC) -DFOSSIL_ENABLE_MARKDOWN | |
| 987 | +RCC = $(RCC) -DFOSSIL_ENABLE_MARKDOWN | |
| 988 | +!endif | |
| 972 | 989 | } |
| 973 | 990 | regsub -all {[-]D} $SQLITE_OPTIONS {/D} MSC_SQLITE_OPTIONS |
| 974 | 991 | set j " \\\n " |
| 975 | 992 | writeln "SQLITE_OPTIONS = [join $MSC_SQLITE_OPTIONS $j]\n" |
| 976 | 993 | writeln -nonewline "SRC = " |
| @@ -981,24 +998,22 @@ | ||
| 981 | 998 | writeln -nonewline " " |
| 982 | 999 | } |
| 983 | 1000 | writeln -nonewline "${s}_.c"; incr i |
| 984 | 1001 | } |
| 985 | 1002 | writeln "\n" |
| 1003 | +set AdditionalObj [list shell sqlite3 th th_lang cson_amalgamation] | |
| 986 | 1004 | writeln -nonewline "OBJ = " |
| 987 | 1005 | set i 0 |
| 988 | -foreach s [lsort $src] { | |
| 1006 | +foreach s [lsort [concat $src $AdditionalObj]] { | |
| 989 | 1007 | if {$i > 0} { |
| 990 | 1008 | writeln " \\" |
| 991 | 1009 | writeln -nonewline " " |
| 992 | 1010 | } |
| 993 | 1011 | writeln -nonewline "\$(OX)\\$s\$O"; incr i |
| 994 | 1012 | } |
| 995 | 1013 | writeln " \\" |
| 996 | -writeln " \$(OX)\\shell\$O \\" | |
| 997 | -writeln " \$(OX)\\sqlite3\$O \\" | |
| 998 | -writeln " \$(OX)\\th\$O \\" | |
| 999 | -writeln " \$(OX)\\th_lang\$O" | |
| 1014 | +writeln -nonewline " \$(OX)\\fossil.res\n" | |
| 1000 | 1015 | writeln { |
| 1001 | 1016 | APPNAME = $(OX)\fossil$(E) |
| 1002 | 1017 | |
| 1003 | 1018 | all: $(OX) $(APPNAME) |
| 1004 | 1019 | |
| @@ -1006,15 +1021,15 @@ | ||
| 1006 | 1021 | @echo Building zlib from "$(ZLIBDIR)"... |
| 1007 | 1022 | @pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd |
| 1008 | 1023 | |
| 1009 | 1024 | $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib |
| 1010 | 1025 | cd $(OX) |
| 1011 | - link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj @linkopts | |
| 1026 | + link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts | |
| 1012 | 1027 | |
| 1013 | 1028 | $(OX)\linkopts: $B\win\Makefile.msc} |
| 1014 | 1029 | set redir {>} |
| 1015 | -foreach s [lsort [concat $src {shell sqlite3 th th_lang}]] { | |
| 1030 | +foreach s [lsort [concat $src $AdditionalObj]] { | |
| 1016 | 1031 | writeln "\techo \$(OX)\\$s.obj $redir \$@" |
| 1017 | 1032 | set redir {>>} |
| 1018 | 1033 | } |
| 1019 | 1034 | writeln "\techo \$(LIBS) >> \$@\n\n" |
| 1020 | 1035 | |
| @@ -1047,12 +1062,12 @@ | ||
| 1047 | 1062 | $(OX)\th_lang$O : $(SRCDIR)\th_lang.c |
| 1048 | 1063 | $(TCC) /Fo$@ -c $** |
| 1049 | 1064 | |
| 1050 | 1065 | VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION |
| 1051 | 1066 | $** > $@ |
| 1052 | -$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h | |
| 1053 | - cp $(SRCDIR)\cson_amalgamation.h $@ | |
| 1067 | +$(OX)\cson_amalgamation$O : $(SRCDIR)\cson_amalgamation.c | |
| 1068 | + $(TCC) /Fo$@ -c $** | |
| 1054 | 1069 | |
| 1055 | 1070 | page_index.h: mkindex$E $(SRC) |
| 1056 | 1071 | $** > $@ |
| 1057 | 1072 | |
| 1058 | 1073 | clean: |
| @@ -1062,10 +1077,11 @@ | ||
| 1062 | 1077 | -del *.h |
| 1063 | 1078 | -del *.map |
| 1064 | 1079 | -del *.manifest |
| 1065 | 1080 | -del headers |
| 1066 | 1081 | -del linkopts |
| 1082 | + -del *.res | |
| 1067 | 1083 | |
| 1068 | 1084 | realclean: clean |
| 1069 | 1085 | -del $(APPNAME) |
| 1070 | 1086 | -del translate$E |
| 1071 | 1087 | -del mkindex$E |
| @@ -1092,10 +1108,13 @@ | ||
| 1092 | 1108 | writeln "\$(OX)\\$s\$O : ${s}_.c ${s}.h" |
| 1093 | 1109 | writeln "\t\$(TCC) /Fo\$@ -c ${s}_.c\n" |
| 1094 | 1110 | writeln "${s}_.c : \$(SRCDIR)\\$s.c" |
| 1095 | 1111 | writeln "\ttranslate\$E \$** > \$@\n" |
| 1096 | 1112 | } |
| 1113 | + | |
| 1114 | +writeln "fossil.res : \$B\\win\\fossil.rc" | |
| 1115 | +writeln "\t\$(RCC) -fo \$@ \$**" | |
| 1097 | 1116 | |
| 1098 | 1117 | writeln "headers: makeheaders\$E page_index.h VERSION.h" |
| 1099 | 1118 | writeln -nonewline "\tmakeheaders\$E " |
| 1100 | 1119 | set i 0 |
| 1101 | 1120 | foreach s [lsort $src] { |
| 1102 | 1121 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -308,11 +308,11 @@ | |
| 308 | writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$(OBJDIR)/th_tcl.o\n" |
| 309 | |
| 310 | set opt {} |
| 311 | writeln { |
| 312 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 313 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE |
| 314 | |
| 315 | # |
| 316 | # The list of all the targets that do not correspond to real files. This stops |
| 317 | # 'make' from getting confused when someone makes an error in a rule. |
| 318 | # |
| @@ -340,15 +340,15 @@ | |
| 340 | # |
| 341 | # This file is automatically generated. Instead of editing this |
| 342 | # file, edit "makemake.tcl" then run "tclsh makemake.tcl" |
| 343 | # to regenerate this file. |
| 344 | # |
| 345 | # This is a makefile for use on Windows/Linux/Darwin/Cygwin using MinGW or |
| 346 | # MinGW-w64. |
| 347 | # |
| 348 | |
| 349 | #### Select one of MinGW, MinGW-64 (32-bit) or MinGW-w64 (64-bit) compilers. |
| 350 | # By default, this is an empty string (i.e. use the native compiler). |
| 351 | # |
| 352 | PREFIX = |
| 353 | # PREFIX = mingw32- |
| 354 | # PREFIX = i686-pc-mingw32- |
| @@ -694,11 +694,11 @@ | |
| 694 | ifdef FOSSIL_ENABLE_TCL |
| 695 | EXTRAOBJ += $(OBJDIR)/th_tcl.o |
| 696 | endif |
| 697 | |
| 698 | zlib: |
| 699 | make -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a |
| 700 | |
| 701 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib |
| 702 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o |
| 703 | |
| 704 | # This rule prevents make from using its default rules to try build |
| @@ -751,11 +751,11 @@ | |
| 751 | set opt $SQLITE_OPTIONS |
| 752 | writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n" |
| 753 | |
| 754 | set opt {} |
| 755 | writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c" |
| 756 | writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE\n" |
| 757 | writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_config.o \$(OBJDIR)/json_diff.o \$(OBJDIR)/json_dir.o \$(OBJDIR)/jsos_finfo.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_query.o \$(OBJDIR)/json_report.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h\n" |
| 758 | |
| 759 | writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h" |
| 760 | set opt {-Dmain=sqlite3_shell} |
| 761 | append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1" |
| @@ -959,18 +959,35 @@ | |
| 959 | |
| 960 | # zlib options |
| 961 | ZINCDIR = $(B)\compat\zlib |
| 962 | ZLIBDIR = $(B)\compat\zlib |
| 963 | ZLIB = zlib.lib |
| 964 | |
| 965 | INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR) |
| 966 | |
| 967 | CFLAGS = -nologo -MT -O2 |
| 968 | BCC = $(CC) $(CFLAGS) |
| 969 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) |
| 970 | LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) |
| 971 | LIBDIR = -LIBPATH:$(ZLIBDIR) |
| 972 | } |
| 973 | regsub -all {[-]D} $SQLITE_OPTIONS {/D} MSC_SQLITE_OPTIONS |
| 974 | set j " \\\n " |
| 975 | writeln "SQLITE_OPTIONS = [join $MSC_SQLITE_OPTIONS $j]\n" |
| 976 | writeln -nonewline "SRC = " |
| @@ -981,24 +998,22 @@ | |
| 981 | writeln -nonewline " " |
| 982 | } |
| 983 | writeln -nonewline "${s}_.c"; incr i |
| 984 | } |
| 985 | writeln "\n" |
| 986 | writeln -nonewline "OBJ = " |
| 987 | set i 0 |
| 988 | foreach s [lsort $src] { |
| 989 | if {$i > 0} { |
| 990 | writeln " \\" |
| 991 | writeln -nonewline " " |
| 992 | } |
| 993 | writeln -nonewline "\$(OX)\\$s\$O"; incr i |
| 994 | } |
| 995 | writeln " \\" |
| 996 | writeln " \$(OX)\\shell\$O \\" |
| 997 | writeln " \$(OX)\\sqlite3\$O \\" |
| 998 | writeln " \$(OX)\\th\$O \\" |
| 999 | writeln " \$(OX)\\th_lang\$O" |
| 1000 | writeln { |
| 1001 | APPNAME = $(OX)\fossil$(E) |
| 1002 | |
| 1003 | all: $(OX) $(APPNAME) |
| 1004 | |
| @@ -1006,15 +1021,15 @@ | |
| 1006 | @echo Building zlib from "$(ZLIBDIR)"... |
| 1007 | @pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd |
| 1008 | |
| 1009 | $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib |
| 1010 | cd $(OX) |
| 1011 | link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj @linkopts |
| 1012 | |
| 1013 | $(OX)\linkopts: $B\win\Makefile.msc} |
| 1014 | set redir {>} |
| 1015 | foreach s [lsort [concat $src {shell sqlite3 th th_lang}]] { |
| 1016 | writeln "\techo \$(OX)\\$s.obj $redir \$@" |
| 1017 | set redir {>>} |
| 1018 | } |
| 1019 | writeln "\techo \$(LIBS) >> \$@\n\n" |
| 1020 | |
| @@ -1047,12 +1062,12 @@ | |
| 1047 | $(OX)\th_lang$O : $(SRCDIR)\th_lang.c |
| 1048 | $(TCC) /Fo$@ -c $** |
| 1049 | |
| 1050 | VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION |
| 1051 | $** > $@ |
| 1052 | $(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h |
| 1053 | cp $(SRCDIR)\cson_amalgamation.h $@ |
| 1054 | |
| 1055 | page_index.h: mkindex$E $(SRC) |
| 1056 | $** > $@ |
| 1057 | |
| 1058 | clean: |
| @@ -1062,10 +1077,11 @@ | |
| 1062 | -del *.h |
| 1063 | -del *.map |
| 1064 | -del *.manifest |
| 1065 | -del headers |
| 1066 | -del linkopts |
| 1067 | |
| 1068 | realclean: clean |
| 1069 | -del $(APPNAME) |
| 1070 | -del translate$E |
| 1071 | -del mkindex$E |
| @@ -1092,10 +1108,13 @@ | |
| 1092 | writeln "\$(OX)\\$s\$O : ${s}_.c ${s}.h" |
| 1093 | writeln "\t\$(TCC) /Fo\$@ -c ${s}_.c\n" |
| 1094 | writeln "${s}_.c : \$(SRCDIR)\\$s.c" |
| 1095 | writeln "\ttranslate\$E \$** > \$@\n" |
| 1096 | } |
| 1097 | |
| 1098 | writeln "headers: makeheaders\$E page_index.h VERSION.h" |
| 1099 | writeln -nonewline "\tmakeheaders\$E " |
| 1100 | set i 0 |
| 1101 | foreach s [lsort $src] { |
| 1102 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -308,11 +308,11 @@ | |
| 308 | writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$(OBJDIR)/th_tcl.o\n" |
| 309 | |
| 310 | set opt {} |
| 311 | writeln { |
| 312 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 313 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o |
| 314 | |
| 315 | # |
| 316 | # The list of all the targets that do not correspond to real files. This stops |
| 317 | # 'make' from getting confused when someone makes an error in a rule. |
| 318 | # |
| @@ -340,15 +340,15 @@ | |
| 340 | # |
| 341 | # This file is automatically generated. Instead of editing this |
| 342 | # file, edit "makemake.tcl" then run "tclsh makemake.tcl" |
| 343 | # to regenerate this file. |
| 344 | # |
| 345 | # This is a makefile for use on Cygwin/Darwin/FreeBSD/Linux/Windows using |
| 346 | # MinGW or MinGW-w64. |
| 347 | # |
| 348 | |
| 349 | #### Select one of MinGW, MinGW-w64 (32-bit) or MinGW-w64 (64-bit) compilers. |
| 350 | # By default, this is an empty string (i.e. use the native compiler). |
| 351 | # |
| 352 | PREFIX = |
| 353 | # PREFIX = mingw32- |
| 354 | # PREFIX = i686-pc-mingw32- |
| @@ -694,11 +694,11 @@ | |
| 694 | ifdef FOSSIL_ENABLE_TCL |
| 695 | EXTRAOBJ += $(OBJDIR)/th_tcl.o |
| 696 | endif |
| 697 | |
| 698 | zlib: |
| 699 | $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a |
| 700 | |
| 701 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib |
| 702 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o |
| 703 | |
| 704 | # This rule prevents make from using its default rules to try build |
| @@ -751,11 +751,11 @@ | |
| 751 | set opt $SQLITE_OPTIONS |
| 752 | writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n" |
| 753 | |
| 754 | set opt {} |
| 755 | writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c" |
| 756 | writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o\n" |
| 757 | writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_config.o \$(OBJDIR)/json_diff.o \$(OBJDIR)/json_dir.o \$(OBJDIR)/jsos_finfo.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_query.o \$(OBJDIR)/json_report.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h\n" |
| 758 | |
| 759 | writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h" |
| 760 | set opt {-Dmain=sqlite3_shell} |
| 761 | append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1" |
| @@ -959,18 +959,35 @@ | |
| 959 | |
| 960 | # zlib options |
| 961 | ZINCDIR = $(B)\compat\zlib |
| 962 | ZLIBDIR = $(B)\compat\zlib |
| 963 | ZLIB = zlib.lib |
| 964 | |
| 965 | # Uncomment to enable JSON API |
| 966 | # FOSSIL_ENABLE_JSON = 1 |
| 967 | |
| 968 | # Uncomment to enable markdown support |
| 969 | # FOSSIL_ENABLE_MARKDOWN = 1 |
| 970 | |
| 971 | INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR) |
| 972 | |
| 973 | CFLAGS = -nologo -MT -O2 |
| 974 | BCC = $(CC) $(CFLAGS) |
| 975 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) |
| 976 | RCC = rc -D_WIN32 -D_MSC_VER $(INCL) |
| 977 | LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) |
| 978 | LIBDIR = -LIBPATH:$(ZLIBDIR) |
| 979 | |
| 980 | !ifdef FOSSIL_ENABLE_JSON |
| 981 | TCC = $(TCC) -DFOSSIL_ENABLE_JSON |
| 982 | RCC = $(RCC) -DFOSSIL_ENABLE_JSON |
| 983 | !endif |
| 984 | |
| 985 | !ifdef FOSSIL_ENABLE_MARKDOWN |
| 986 | TCC = $(TCC) -DFOSSIL_ENABLE_MARKDOWN |
| 987 | RCC = $(RCC) -DFOSSIL_ENABLE_MARKDOWN |
| 988 | !endif |
| 989 | } |
| 990 | regsub -all {[-]D} $SQLITE_OPTIONS {/D} MSC_SQLITE_OPTIONS |
| 991 | set j " \\\n " |
| 992 | writeln "SQLITE_OPTIONS = [join $MSC_SQLITE_OPTIONS $j]\n" |
| 993 | writeln -nonewline "SRC = " |
| @@ -981,24 +998,22 @@ | |
| 998 | writeln -nonewline " " |
| 999 | } |
| 1000 | writeln -nonewline "${s}_.c"; incr i |
| 1001 | } |
| 1002 | writeln "\n" |
| 1003 | set AdditionalObj [list shell sqlite3 th th_lang cson_amalgamation] |
| 1004 | writeln -nonewline "OBJ = " |
| 1005 | set i 0 |
| 1006 | foreach s [lsort [concat $src $AdditionalObj]] { |
| 1007 | if {$i > 0} { |
| 1008 | writeln " \\" |
| 1009 | writeln -nonewline " " |
| 1010 | } |
| 1011 | writeln -nonewline "\$(OX)\\$s\$O"; incr i |
| 1012 | } |
| 1013 | writeln " \\" |
| 1014 | writeln -nonewline " \$(OX)\\fossil.res\n" |
| 1015 | writeln { |
| 1016 | APPNAME = $(OX)\fossil$(E) |
| 1017 | |
| 1018 | all: $(OX) $(APPNAME) |
| 1019 | |
| @@ -1006,15 +1021,15 @@ | |
| 1021 | @echo Building zlib from "$(ZLIBDIR)"... |
| 1022 | @pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd |
| 1023 | |
| 1024 | $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib |
| 1025 | cd $(OX) |
| 1026 | link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts |
| 1027 | |
| 1028 | $(OX)\linkopts: $B\win\Makefile.msc} |
| 1029 | set redir {>} |
| 1030 | foreach s [lsort [concat $src $AdditionalObj]] { |
| 1031 | writeln "\techo \$(OX)\\$s.obj $redir \$@" |
| 1032 | set redir {>>} |
| 1033 | } |
| 1034 | writeln "\techo \$(LIBS) >> \$@\n\n" |
| 1035 | |
| @@ -1047,12 +1062,12 @@ | |
| 1062 | $(OX)\th_lang$O : $(SRCDIR)\th_lang.c |
| 1063 | $(TCC) /Fo$@ -c $** |
| 1064 | |
| 1065 | VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION |
| 1066 | $** > $@ |
| 1067 | $(OX)\cson_amalgamation$O : $(SRCDIR)\cson_amalgamation.c |
| 1068 | $(TCC) /Fo$@ -c $** |
| 1069 | |
| 1070 | page_index.h: mkindex$E $(SRC) |
| 1071 | $** > $@ |
| 1072 | |
| 1073 | clean: |
| @@ -1062,10 +1077,11 @@ | |
| 1077 | -del *.h |
| 1078 | -del *.map |
| 1079 | -del *.manifest |
| 1080 | -del headers |
| 1081 | -del linkopts |
| 1082 | -del *.res |
| 1083 | |
| 1084 | realclean: clean |
| 1085 | -del $(APPNAME) |
| 1086 | -del translate$E |
| 1087 | -del mkindex$E |
| @@ -1092,10 +1108,13 @@ | |
| 1108 | writeln "\$(OX)\\$s\$O : ${s}_.c ${s}.h" |
| 1109 | writeln "\t\$(TCC) /Fo\$@ -c ${s}_.c\n" |
| 1110 | writeln "${s}_.c : \$(SRCDIR)\\$s.c" |
| 1111 | writeln "\ttranslate\$E \$** > \$@\n" |
| 1112 | } |
| 1113 | |
| 1114 | writeln "fossil.res : \$B\\win\\fossil.rc" |
| 1115 | writeln "\t\$(RCC) -fo \$@ \$**" |
| 1116 | |
| 1117 | writeln "headers: makeheaders\$E page_index.h VERSION.h" |
| 1118 | writeln -nonewline "\tmakeheaders\$E " |
| 1119 | set i 0 |
| 1120 | foreach s [lsort $src] { |
| 1121 |
+34
-15
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -308,11 +308,11 @@ | ||
| 308 | 308 | writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$(OBJDIR)/th_tcl.o\n" |
| 309 | 309 | |
| 310 | 310 | set opt {} |
| 311 | 311 | writeln { |
| 312 | 312 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 313 | - $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE | |
| 313 | + $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o | |
| 314 | 314 | |
| 315 | 315 | # |
| 316 | 316 | # The list of all the targets that do not correspond to real files. This stops |
| 317 | 317 | # 'make' from getting confused when someone makes an error in a rule. |
| 318 | 318 | # |
| @@ -340,15 +340,15 @@ | ||
| 340 | 340 | # |
| 341 | 341 | # This file is automatically generated. Instead of editing this |
| 342 | 342 | # file, edit "makemake.tcl" then run "tclsh makemake.tcl" |
| 343 | 343 | # to regenerate this file. |
| 344 | 344 | # |
| 345 | -# This is a makefile for use on Windows/Linux/Darwin/Cygwin using MinGW or | |
| 346 | -# MinGW-w64. | |
| 345 | +# This is a makefile for use on Cygwin/Darwin/FreeBSD/Linux/Windows using | |
| 346 | +# MinGW or MinGW-w64. | |
| 347 | 347 | # |
| 348 | 348 | |
| 349 | -#### Select one of MinGW, MinGW-64 (32-bit) or MinGW-w64 (64-bit) compilers. | |
| 349 | +#### Select one of MinGW, MinGW-w64 (32-bit) or MinGW-w64 (64-bit) compilers. | |
| 350 | 350 | # By default, this is an empty string (i.e. use the native compiler). |
| 351 | 351 | # |
| 352 | 352 | PREFIX = |
| 353 | 353 | # PREFIX = mingw32- |
| 354 | 354 | # PREFIX = i686-pc-mingw32- |
| @@ -694,11 +694,11 @@ | ||
| 694 | 694 | ifdef FOSSIL_ENABLE_TCL |
| 695 | 695 | EXTRAOBJ += $(OBJDIR)/th_tcl.o |
| 696 | 696 | endif |
| 697 | 697 | |
| 698 | 698 | zlib: |
| 699 | - make -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a | |
| 699 | + $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a | |
| 700 | 700 | |
| 701 | 701 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib |
| 702 | 702 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o |
| 703 | 703 | |
| 704 | 704 | # This rule prevents make from using its default rules to try build |
| @@ -751,11 +751,11 @@ | ||
| 751 | 751 | set opt $SQLITE_OPTIONS |
| 752 | 752 | writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n" |
| 753 | 753 | |
| 754 | 754 | set opt {} |
| 755 | 755 | writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c" |
| 756 | -writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE\n" | |
| 756 | +writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o\n" | |
| 757 | 757 | writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_config.o \$(OBJDIR)/json_diff.o \$(OBJDIR)/json_dir.o \$(OBJDIR)/jsos_finfo.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_query.o \$(OBJDIR)/json_report.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h\n" |
| 758 | 758 | |
| 759 | 759 | writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h" |
| 760 | 760 | set opt {-Dmain=sqlite3_shell} |
| 761 | 761 | append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1" |
| @@ -959,18 +959,35 @@ | ||
| 959 | 959 | |
| 960 | 960 | # zlib options |
| 961 | 961 | ZINCDIR = $(B)\compat\zlib |
| 962 | 962 | ZLIBDIR = $(B)\compat\zlib |
| 963 | 963 | ZLIB = zlib.lib |
| 964 | + | |
| 965 | +# Uncomment to enable JSON API | |
| 966 | +# FOSSIL_ENABLE_JSON = 1 | |
| 967 | + | |
| 968 | +# Uncomment to enable markdown support | |
| 969 | +# FOSSIL_ENABLE_MARKDOWN = 1 | |
| 964 | 970 | |
| 965 | 971 | INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR) |
| 966 | 972 | |
| 967 | 973 | CFLAGS = -nologo -MT -O2 |
| 968 | 974 | BCC = $(CC) $(CFLAGS) |
| 969 | 975 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) |
| 976 | +RCC = rc -D_WIN32 -D_MSC_VER $(INCL) | |
| 970 | 977 | LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) |
| 971 | 978 | LIBDIR = -LIBPATH:$(ZLIBDIR) |
| 979 | + | |
| 980 | +!ifdef FOSSIL_ENABLE_JSON | |
| 981 | +TCC = $(TCC) -DFOSSIL_ENABLE_JSON | |
| 982 | +RCC = $(RCC) -DFOSSIL_ENABLE_JSON | |
| 983 | +!endif | |
| 984 | + | |
| 985 | +!ifdef FOSSIL_ENABLE_MARKDOWN | |
| 986 | +TCC = $(TCC) -DFOSSIL_ENABLE_MARKDOWN | |
| 987 | +RCC = $(RCC) -DFOSSIL_ENABLE_MARKDOWN | |
| 988 | +!endif | |
| 972 | 989 | } |
| 973 | 990 | regsub -all {[-]D} $SQLITE_OPTIONS {/D} MSC_SQLITE_OPTIONS |
| 974 | 991 | set j " \\\n " |
| 975 | 992 | writeln "SQLITE_OPTIONS = [join $MSC_SQLITE_OPTIONS $j]\n" |
| 976 | 993 | writeln -nonewline "SRC = " |
| @@ -981,24 +998,22 @@ | ||
| 981 | 998 | writeln -nonewline " " |
| 982 | 999 | } |
| 983 | 1000 | writeln -nonewline "${s}_.c"; incr i |
| 984 | 1001 | } |
| 985 | 1002 | writeln "\n" |
| 1003 | +set AdditionalObj [list shell sqlite3 th th_lang cson_amalgamation] | |
| 986 | 1004 | writeln -nonewline "OBJ = " |
| 987 | 1005 | set i 0 |
| 988 | -foreach s [lsort $src] { | |
| 1006 | +foreach s [lsort [concat $src $AdditionalObj]] { | |
| 989 | 1007 | if {$i > 0} { |
| 990 | 1008 | writeln " \\" |
| 991 | 1009 | writeln -nonewline " " |
| 992 | 1010 | } |
| 993 | 1011 | writeln -nonewline "\$(OX)\\$s\$O"; incr i |
| 994 | 1012 | } |
| 995 | 1013 | writeln " \\" |
| 996 | -writeln " \$(OX)\\shell\$O \\" | |
| 997 | -writeln " \$(OX)\\sqlite3\$O \\" | |
| 998 | -writeln " \$(OX)\\th\$O \\" | |
| 999 | -writeln " \$(OX)\\th_lang\$O" | |
| 1014 | +writeln -nonewline " \$(OX)\\fossil.res\n" | |
| 1000 | 1015 | writeln { |
| 1001 | 1016 | APPNAME = $(OX)\fossil$(E) |
| 1002 | 1017 | |
| 1003 | 1018 | all: $(OX) $(APPNAME) |
| 1004 | 1019 | |
| @@ -1006,15 +1021,15 @@ | ||
| 1006 | 1021 | @echo Building zlib from "$(ZLIBDIR)"... |
| 1007 | 1022 | @pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd |
| 1008 | 1023 | |
| 1009 | 1024 | $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib |
| 1010 | 1025 | cd $(OX) |
| 1011 | - link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj @linkopts | |
| 1026 | + link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts | |
| 1012 | 1027 | |
| 1013 | 1028 | $(OX)\linkopts: $B\win\Makefile.msc} |
| 1014 | 1029 | set redir {>} |
| 1015 | -foreach s [lsort [concat $src {shell sqlite3 th th_lang}]] { | |
| 1030 | +foreach s [lsort [concat $src $AdditionalObj]] { | |
| 1016 | 1031 | writeln "\techo \$(OX)\\$s.obj $redir \$@" |
| 1017 | 1032 | set redir {>>} |
| 1018 | 1033 | } |
| 1019 | 1034 | writeln "\techo \$(LIBS) >> \$@\n\n" |
| 1020 | 1035 | |
| @@ -1047,12 +1062,12 @@ | ||
| 1047 | 1062 | $(OX)\th_lang$O : $(SRCDIR)\th_lang.c |
| 1048 | 1063 | $(TCC) /Fo$@ -c $** |
| 1049 | 1064 | |
| 1050 | 1065 | VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION |
| 1051 | 1066 | $** > $@ |
| 1052 | -$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h | |
| 1053 | - cp $(SRCDIR)\cson_amalgamation.h $@ | |
| 1067 | +$(OX)\cson_amalgamation$O : $(SRCDIR)\cson_amalgamation.c | |
| 1068 | + $(TCC) /Fo$@ -c $** | |
| 1054 | 1069 | |
| 1055 | 1070 | page_index.h: mkindex$E $(SRC) |
| 1056 | 1071 | $** > $@ |
| 1057 | 1072 | |
| 1058 | 1073 | clean: |
| @@ -1062,10 +1077,11 @@ | ||
| 1062 | 1077 | -del *.h |
| 1063 | 1078 | -del *.map |
| 1064 | 1079 | -del *.manifest |
| 1065 | 1080 | -del headers |
| 1066 | 1081 | -del linkopts |
| 1082 | + -del *.res | |
| 1067 | 1083 | |
| 1068 | 1084 | realclean: clean |
| 1069 | 1085 | -del $(APPNAME) |
| 1070 | 1086 | -del translate$E |
| 1071 | 1087 | -del mkindex$E |
| @@ -1092,10 +1108,13 @@ | ||
| 1092 | 1108 | writeln "\$(OX)\\$s\$O : ${s}_.c ${s}.h" |
| 1093 | 1109 | writeln "\t\$(TCC) /Fo\$@ -c ${s}_.c\n" |
| 1094 | 1110 | writeln "${s}_.c : \$(SRCDIR)\\$s.c" |
| 1095 | 1111 | writeln "\ttranslate\$E \$** > \$@\n" |
| 1096 | 1112 | } |
| 1113 | + | |
| 1114 | +writeln "fossil.res : \$B\\win\\fossil.rc" | |
| 1115 | +writeln "\t\$(RCC) -fo \$@ \$**" | |
| 1097 | 1116 | |
| 1098 | 1117 | writeln "headers: makeheaders\$E page_index.h VERSION.h" |
| 1099 | 1118 | writeln -nonewline "\tmakeheaders\$E " |
| 1100 | 1119 | set i 0 |
| 1101 | 1120 | foreach s [lsort $src] { |
| 1102 | 1121 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -308,11 +308,11 @@ | |
| 308 | writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$(OBJDIR)/th_tcl.o\n" |
| 309 | |
| 310 | set opt {} |
| 311 | writeln { |
| 312 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 313 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE |
| 314 | |
| 315 | # |
| 316 | # The list of all the targets that do not correspond to real files. This stops |
| 317 | # 'make' from getting confused when someone makes an error in a rule. |
| 318 | # |
| @@ -340,15 +340,15 @@ | |
| 340 | # |
| 341 | # This file is automatically generated. Instead of editing this |
| 342 | # file, edit "makemake.tcl" then run "tclsh makemake.tcl" |
| 343 | # to regenerate this file. |
| 344 | # |
| 345 | # This is a makefile for use on Windows/Linux/Darwin/Cygwin using MinGW or |
| 346 | # MinGW-w64. |
| 347 | # |
| 348 | |
| 349 | #### Select one of MinGW, MinGW-64 (32-bit) or MinGW-w64 (64-bit) compilers. |
| 350 | # By default, this is an empty string (i.e. use the native compiler). |
| 351 | # |
| 352 | PREFIX = |
| 353 | # PREFIX = mingw32- |
| 354 | # PREFIX = i686-pc-mingw32- |
| @@ -694,11 +694,11 @@ | |
| 694 | ifdef FOSSIL_ENABLE_TCL |
| 695 | EXTRAOBJ += $(OBJDIR)/th_tcl.o |
| 696 | endif |
| 697 | |
| 698 | zlib: |
| 699 | make -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a |
| 700 | |
| 701 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib |
| 702 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o |
| 703 | |
| 704 | # This rule prevents make from using its default rules to try build |
| @@ -751,11 +751,11 @@ | |
| 751 | set opt $SQLITE_OPTIONS |
| 752 | writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n" |
| 753 | |
| 754 | set opt {} |
| 755 | writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c" |
| 756 | writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE\n" |
| 757 | writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_config.o \$(OBJDIR)/json_diff.o \$(OBJDIR)/json_dir.o \$(OBJDIR)/jsos_finfo.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_query.o \$(OBJDIR)/json_report.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h\n" |
| 758 | |
| 759 | writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h" |
| 760 | set opt {-Dmain=sqlite3_shell} |
| 761 | append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1" |
| @@ -959,18 +959,35 @@ | |
| 959 | |
| 960 | # zlib options |
| 961 | ZINCDIR = $(B)\compat\zlib |
| 962 | ZLIBDIR = $(B)\compat\zlib |
| 963 | ZLIB = zlib.lib |
| 964 | |
| 965 | INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR) |
| 966 | |
| 967 | CFLAGS = -nologo -MT -O2 |
| 968 | BCC = $(CC) $(CFLAGS) |
| 969 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) |
| 970 | LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) |
| 971 | LIBDIR = -LIBPATH:$(ZLIBDIR) |
| 972 | } |
| 973 | regsub -all {[-]D} $SQLITE_OPTIONS {/D} MSC_SQLITE_OPTIONS |
| 974 | set j " \\\n " |
| 975 | writeln "SQLITE_OPTIONS = [join $MSC_SQLITE_OPTIONS $j]\n" |
| 976 | writeln -nonewline "SRC = " |
| @@ -981,24 +998,22 @@ | |
| 981 | writeln -nonewline " " |
| 982 | } |
| 983 | writeln -nonewline "${s}_.c"; incr i |
| 984 | } |
| 985 | writeln "\n" |
| 986 | writeln -nonewline "OBJ = " |
| 987 | set i 0 |
| 988 | foreach s [lsort $src] { |
| 989 | if {$i > 0} { |
| 990 | writeln " \\" |
| 991 | writeln -nonewline " " |
| 992 | } |
| 993 | writeln -nonewline "\$(OX)\\$s\$O"; incr i |
| 994 | } |
| 995 | writeln " \\" |
| 996 | writeln " \$(OX)\\shell\$O \\" |
| 997 | writeln " \$(OX)\\sqlite3\$O \\" |
| 998 | writeln " \$(OX)\\th\$O \\" |
| 999 | writeln " \$(OX)\\th_lang\$O" |
| 1000 | writeln { |
| 1001 | APPNAME = $(OX)\fossil$(E) |
| 1002 | |
| 1003 | all: $(OX) $(APPNAME) |
| 1004 | |
| @@ -1006,15 +1021,15 @@ | |
| 1006 | @echo Building zlib from "$(ZLIBDIR)"... |
| 1007 | @pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd |
| 1008 | |
| 1009 | $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib |
| 1010 | cd $(OX) |
| 1011 | link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj @linkopts |
| 1012 | |
| 1013 | $(OX)\linkopts: $B\win\Makefile.msc} |
| 1014 | set redir {>} |
| 1015 | foreach s [lsort [concat $src {shell sqlite3 th th_lang}]] { |
| 1016 | writeln "\techo \$(OX)\\$s.obj $redir \$@" |
| 1017 | set redir {>>} |
| 1018 | } |
| 1019 | writeln "\techo \$(LIBS) >> \$@\n\n" |
| 1020 | |
| @@ -1047,12 +1062,12 @@ | |
| 1047 | $(OX)\th_lang$O : $(SRCDIR)\th_lang.c |
| 1048 | $(TCC) /Fo$@ -c $** |
| 1049 | |
| 1050 | VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION |
| 1051 | $** > $@ |
| 1052 | $(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h |
| 1053 | cp $(SRCDIR)\cson_amalgamation.h $@ |
| 1054 | |
| 1055 | page_index.h: mkindex$E $(SRC) |
| 1056 | $** > $@ |
| 1057 | |
| 1058 | clean: |
| @@ -1062,10 +1077,11 @@ | |
| 1062 | -del *.h |
| 1063 | -del *.map |
| 1064 | -del *.manifest |
| 1065 | -del headers |
| 1066 | -del linkopts |
| 1067 | |
| 1068 | realclean: clean |
| 1069 | -del $(APPNAME) |
| 1070 | -del translate$E |
| 1071 | -del mkindex$E |
| @@ -1092,10 +1108,13 @@ | |
| 1092 | writeln "\$(OX)\\$s\$O : ${s}_.c ${s}.h" |
| 1093 | writeln "\t\$(TCC) /Fo\$@ -c ${s}_.c\n" |
| 1094 | writeln "${s}_.c : \$(SRCDIR)\\$s.c" |
| 1095 | writeln "\ttranslate\$E \$** > \$@\n" |
| 1096 | } |
| 1097 | |
| 1098 | writeln "headers: makeheaders\$E page_index.h VERSION.h" |
| 1099 | writeln -nonewline "\tmakeheaders\$E " |
| 1100 | set i 0 |
| 1101 | foreach s [lsort $src] { |
| 1102 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -308,11 +308,11 @@ | |
| 308 | writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$(OBJDIR)/th_tcl.o\n" |
| 309 | |
| 310 | set opt {} |
| 311 | writeln { |
| 312 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 313 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o |
| 314 | |
| 315 | # |
| 316 | # The list of all the targets that do not correspond to real files. This stops |
| 317 | # 'make' from getting confused when someone makes an error in a rule. |
| 318 | # |
| @@ -340,15 +340,15 @@ | |
| 340 | # |
| 341 | # This file is automatically generated. Instead of editing this |
| 342 | # file, edit "makemake.tcl" then run "tclsh makemake.tcl" |
| 343 | # to regenerate this file. |
| 344 | # |
| 345 | # This is a makefile for use on Cygwin/Darwin/FreeBSD/Linux/Windows using |
| 346 | # MinGW or MinGW-w64. |
| 347 | # |
| 348 | |
| 349 | #### Select one of MinGW, MinGW-w64 (32-bit) or MinGW-w64 (64-bit) compilers. |
| 350 | # By default, this is an empty string (i.e. use the native compiler). |
| 351 | # |
| 352 | PREFIX = |
| 353 | # PREFIX = mingw32- |
| 354 | # PREFIX = i686-pc-mingw32- |
| @@ -694,11 +694,11 @@ | |
| 694 | ifdef FOSSIL_ENABLE_TCL |
| 695 | EXTRAOBJ += $(OBJDIR)/th_tcl.o |
| 696 | endif |
| 697 | |
| 698 | zlib: |
| 699 | $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a |
| 700 | |
| 701 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib |
| 702 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o |
| 703 | |
| 704 | # This rule prevents make from using its default rules to try build |
| @@ -751,11 +751,11 @@ | |
| 751 | set opt $SQLITE_OPTIONS |
| 752 | writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n" |
| 753 | |
| 754 | set opt {} |
| 755 | writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c" |
| 756 | writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o\n" |
| 757 | writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_config.o \$(OBJDIR)/json_diff.o \$(OBJDIR)/json_dir.o \$(OBJDIR)/jsos_finfo.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_query.o \$(OBJDIR)/json_report.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h\n" |
| 758 | |
| 759 | writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h" |
| 760 | set opt {-Dmain=sqlite3_shell} |
| 761 | append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1" |
| @@ -959,18 +959,35 @@ | |
| 959 | |
| 960 | # zlib options |
| 961 | ZINCDIR = $(B)\compat\zlib |
| 962 | ZLIBDIR = $(B)\compat\zlib |
| 963 | ZLIB = zlib.lib |
| 964 | |
| 965 | # Uncomment to enable JSON API |
| 966 | # FOSSIL_ENABLE_JSON = 1 |
| 967 | |
| 968 | # Uncomment to enable markdown support |
| 969 | # FOSSIL_ENABLE_MARKDOWN = 1 |
| 970 | |
| 971 | INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR) |
| 972 | |
| 973 | CFLAGS = -nologo -MT -O2 |
| 974 | BCC = $(CC) $(CFLAGS) |
| 975 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) |
| 976 | RCC = rc -D_WIN32 -D_MSC_VER $(INCL) |
| 977 | LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) |
| 978 | LIBDIR = -LIBPATH:$(ZLIBDIR) |
| 979 | |
| 980 | !ifdef FOSSIL_ENABLE_JSON |
| 981 | TCC = $(TCC) -DFOSSIL_ENABLE_JSON |
| 982 | RCC = $(RCC) -DFOSSIL_ENABLE_JSON |
| 983 | !endif |
| 984 | |
| 985 | !ifdef FOSSIL_ENABLE_MARKDOWN |
| 986 | TCC = $(TCC) -DFOSSIL_ENABLE_MARKDOWN |
| 987 | RCC = $(RCC) -DFOSSIL_ENABLE_MARKDOWN |
| 988 | !endif |
| 989 | } |
| 990 | regsub -all {[-]D} $SQLITE_OPTIONS {/D} MSC_SQLITE_OPTIONS |
| 991 | set j " \\\n " |
| 992 | writeln "SQLITE_OPTIONS = [join $MSC_SQLITE_OPTIONS $j]\n" |
| 993 | writeln -nonewline "SRC = " |
| @@ -981,24 +998,22 @@ | |
| 998 | writeln -nonewline " " |
| 999 | } |
| 1000 | writeln -nonewline "${s}_.c"; incr i |
| 1001 | } |
| 1002 | writeln "\n" |
| 1003 | set AdditionalObj [list shell sqlite3 th th_lang cson_amalgamation] |
| 1004 | writeln -nonewline "OBJ = " |
| 1005 | set i 0 |
| 1006 | foreach s [lsort [concat $src $AdditionalObj]] { |
| 1007 | if {$i > 0} { |
| 1008 | writeln " \\" |
| 1009 | writeln -nonewline " " |
| 1010 | } |
| 1011 | writeln -nonewline "\$(OX)\\$s\$O"; incr i |
| 1012 | } |
| 1013 | writeln " \\" |
| 1014 | writeln -nonewline " \$(OX)\\fossil.res\n" |
| 1015 | writeln { |
| 1016 | APPNAME = $(OX)\fossil$(E) |
| 1017 | |
| 1018 | all: $(OX) $(APPNAME) |
| 1019 | |
| @@ -1006,15 +1021,15 @@ | |
| 1021 | @echo Building zlib from "$(ZLIBDIR)"... |
| 1022 | @pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd |
| 1023 | |
| 1024 | $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib |
| 1025 | cd $(OX) |
| 1026 | link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts |
| 1027 | |
| 1028 | $(OX)\linkopts: $B\win\Makefile.msc} |
| 1029 | set redir {>} |
| 1030 | foreach s [lsort [concat $src $AdditionalObj]] { |
| 1031 | writeln "\techo \$(OX)\\$s.obj $redir \$@" |
| 1032 | set redir {>>} |
| 1033 | } |
| 1034 | writeln "\techo \$(LIBS) >> \$@\n\n" |
| 1035 | |
| @@ -1047,12 +1062,12 @@ | |
| 1062 | $(OX)\th_lang$O : $(SRCDIR)\th_lang.c |
| 1063 | $(TCC) /Fo$@ -c $** |
| 1064 | |
| 1065 | VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION |
| 1066 | $** > $@ |
| 1067 | $(OX)\cson_amalgamation$O : $(SRCDIR)\cson_amalgamation.c |
| 1068 | $(TCC) /Fo$@ -c $** |
| 1069 | |
| 1070 | page_index.h: mkindex$E $(SRC) |
| 1071 | $** > $@ |
| 1072 | |
| 1073 | clean: |
| @@ -1062,10 +1077,11 @@ | |
| 1077 | -del *.h |
| 1078 | -del *.map |
| 1079 | -del *.manifest |
| 1080 | -del headers |
| 1081 | -del linkopts |
| 1082 | -del *.res |
| 1083 | |
| 1084 | realclean: clean |
| 1085 | -del $(APPNAME) |
| 1086 | -del translate$E |
| 1087 | -del mkindex$E |
| @@ -1092,10 +1108,13 @@ | |
| 1108 | writeln "\$(OX)\\$s\$O : ${s}_.c ${s}.h" |
| 1109 | writeln "\t\$(TCC) /Fo\$@ -c ${s}_.c\n" |
| 1110 | writeln "${s}_.c : \$(SRCDIR)\\$s.c" |
| 1111 | writeln "\ttranslate\$E \$** > \$@\n" |
| 1112 | } |
| 1113 | |
| 1114 | writeln "fossil.res : \$B\\win\\fossil.rc" |
| 1115 | writeln "\t\$(RCC) -fo \$@ \$**" |
| 1116 | |
| 1117 | writeln "headers: makeheaders\$E page_index.h VERSION.h" |
| 1118 | writeln -nonewline "\tmakeheaders\$E " |
| 1119 | set i 0 |
| 1120 | foreach s [lsort $src] { |
| 1121 |
+71
-8
| --- src/merge.c | ||
| +++ src/merge.c | ||
| @@ -57,19 +57,22 @@ | ||
| 57 | 57 | |
| 58 | 58 | |
| 59 | 59 | /* |
| 60 | 60 | ** COMMAND: merge |
| 61 | 61 | ** |
| 62 | -** Usage: %fossil merge ?OPTIONS? VERSION | |
| 62 | +** Usage: %fossil merge ?OPTIONS? ?VERSION? | |
| 63 | 63 | ** |
| 64 | 64 | ** The argument VERSION is a version that should be merged into the |
| 65 | 65 | ** current checkout. All changes from VERSION back to the nearest |
| 66 | 66 | ** common ancestor are merged. Except, if either of the --cherrypick or |
| 67 | 67 | ** --backout options are used only the changes associated with the |
| 68 | 68 | ** single check-in VERSION are merged. The --backout option causes |
| 69 | 69 | ** the changes associated with VERSION to be removed from the current |
| 70 | 70 | ** checkout rather than added. |
| 71 | +** | |
| 72 | +** If the VERSION argument is omitted, then Fossil attempts to find | |
| 73 | +** a recent fork on the current branch to merge. | |
| 71 | 74 | ** |
| 72 | 75 | ** Only file content is merged. The result continues to use the |
| 73 | 76 | ** file and directory names from the current checkout even if those |
| 74 | 77 | ** names might have been changed in the branch being merged in. |
| 75 | 78 | ** |
| @@ -131,24 +134,84 @@ | ||
| 131 | 134 | zBinGlob = find_option("binary",0,1); |
| 132 | 135 | nochangeFlag = find_option("nochange","n",0)!=0; |
| 133 | 136 | forceFlag = find_option("force","f",0)!=0; |
| 134 | 137 | zPivot = find_option("baseline",0,1); |
| 135 | 138 | capture_case_sensitive_option(); |
| 136 | - if( g.argc!=3 ){ | |
| 137 | - usage("VERSION"); | |
| 138 | - } | |
| 139 | + verify_all_options(); | |
| 139 | 140 | db_must_be_within_tree(); |
| 140 | 141 | caseSensitive = filenames_are_case_sensitive(); |
| 141 | 142 | if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); |
| 142 | 143 | vid = db_lget_int("checkout", 0); |
| 143 | 144 | if( vid==0 ){ |
| 144 | 145 | fossil_fatal("nothing is checked out"); |
| 145 | 146 | } |
| 146 | - mid = name_to_typed_rid(g.argv[2], "ci"); | |
| 147 | - if( mid==0 || !is_a_version(mid) ){ | |
| 148 | - fossil_fatal("not a version: %s", g.argv[2]); | |
| 147 | + | |
| 148 | + /* Find mid, the artifactID of the version to be merged into the current | |
| 149 | + ** check-out */ | |
| 150 | + if( g.argc==3 ){ | |
| 151 | + /* Mid is specified as an argument on the command-line */ | |
| 152 | + mid = name_to_typed_rid(g.argv[2], "ci"); | |
| 153 | + if( mid==0 || !is_a_version(mid) ){ | |
| 154 | + fossil_fatal("not a version: %s", g.argv[2]); | |
| 155 | + } | |
| 156 | + }else if( g.argc==2 ){ | |
| 157 | + /* No version specified on the command-line so pick the most recent | |
| 158 | + ** leaf that is (1) not the version currently checked out and (2) | |
| 159 | + ** has not already been merged into the current checkout and (3) | |
| 160 | + ** the leaf is not closed and (4) the leaf is in the same branch | |
| 161 | + ** as the current checkout. | |
| 162 | + */ | |
| 163 | + Stmt q; | |
| 164 | + if( pickFlag || backoutFlag ){ | |
| 165 | + fossil_fatal("cannot use --cherrypick or --backout with a fork merge"); | |
| 166 | + } | |
| 167 | + mid = db_int(0, | |
| 168 | + "SELECT leaf.rid" | |
| 169 | + " FROM leaf, event" | |
| 170 | + " WHERE leaf.rid=event.objid" | |
| 171 | + " AND leaf.rid!=%d" /* Constraint (1) */ | |
| 172 | + " AND leaf.rid NOT IN (SELECT merge FROM vmerge)" /* Constraint (2) */ | |
| 173 | + " AND NOT EXISTS(SELECT 1 FROM tagxref" /* Constraint (3) */ | |
| 174 | + " WHERE rid=leaf.rid" | |
| 175 | + " AND tagid=%d" | |
| 176 | + " AND tagtype>0)" | |
| 177 | + " AND (SELECT value FROM tagxref" /* Constraint (4) */ | |
| 178 | + " WHERE tagid=%d AND rid=%d AND tagtype>0) =" | |
| 179 | + " (SELECT value FROM tagxref" | |
| 180 | + " WHERE tagid=%d AND rid=leaf.rid AND tagtype>0)" | |
| 181 | + " ORDER BY event.mtime DESC LIMIT 1", | |
| 182 | + vid, TAG_CLOSED, TAG_BRANCH, vid, TAG_BRANCH | |
| 183 | + ); | |
| 184 | + if( mid==0 ){ | |
| 185 | + fossil_fatal("no unmerged forks of branch \"%s\"", | |
| 186 | + db_text(0, "SELECT value FROM tagxref" | |
| 187 | + " WHERE tagid=%d AND rid=%d AND tagtype>0", | |
| 188 | + TAG_BRANCH, vid) | |
| 189 | + ); | |
| 190 | + } | |
| 191 | + db_prepare(&q, | |
| 192 | + "SELECT blob.uuid," | |
| 193 | + " datetime(event.mtime,'localtime')," | |
| 194 | + " coalesce(ecomment, comment)," | |
| 195 | + " coalesce(euser, user)" | |
| 196 | + " FROM event, blob" | |
| 197 | + " WHERE event.objid=%d AND blob.rid=%d", | |
| 198 | + mid, mid | |
| 199 | + ); | |
| 200 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 201 | + char *zCom = mprintf("Merging fork [%S] at %s by %s: \"%s\"", | |
| 202 | + db_column_text(&q, 0), db_column_text(&q, 1), | |
| 203 | + db_column_text(&q, 3), db_column_text(&q, 2)); | |
| 204 | + comment_print(zCom, 0, 79); | |
| 205 | + fossil_free(zCom); | |
| 206 | + } | |
| 207 | + db_finalize(&q); | |
| 208 | + }else{ | |
| 209 | + usage("?OPTIONS? ?VERSION?"); | |
| 210 | + return; | |
| 149 | 211 | } |
| 212 | + | |
| 150 | 213 | if( zPivot ){ |
| 151 | 214 | pid = name_to_typed_rid(zPivot, "ci"); |
| 152 | 215 | if( pid==0 || !is_a_version(pid) ){ |
| 153 | 216 | fossil_fatal("not a version: %s", zPivot); |
| 154 | 217 | } |
| @@ -347,11 +410,11 @@ | ||
| 347 | 410 | "SELECT idm FROM fv WHERE idp=0 AND idv>0 AND idm>0" |
| 348 | 411 | ); |
| 349 | 412 | while( db_step(&q)==SQLITE_ROW ){ |
| 350 | 413 | int idm = db_column_int(&q, 0); |
| 351 | 414 | char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm); |
| 352 | - fossil_warning("WARNING - no common ancestor: %s\n", zName); | |
| 415 | + fossil_warning("WARNING - no common ancestor: %s", zName); | |
| 353 | 416 | free(zName); |
| 354 | 417 | db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm); |
| 355 | 418 | } |
| 356 | 419 | db_finalize(&q); |
| 357 | 420 | |
| 358 | 421 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -57,19 +57,22 @@ | |
| 57 | |
| 58 | |
| 59 | /* |
| 60 | ** COMMAND: merge |
| 61 | ** |
| 62 | ** Usage: %fossil merge ?OPTIONS? VERSION |
| 63 | ** |
| 64 | ** The argument VERSION is a version that should be merged into the |
| 65 | ** current checkout. All changes from VERSION back to the nearest |
| 66 | ** common ancestor are merged. Except, if either of the --cherrypick or |
| 67 | ** --backout options are used only the changes associated with the |
| 68 | ** single check-in VERSION are merged. The --backout option causes |
| 69 | ** the changes associated with VERSION to be removed from the current |
| 70 | ** checkout rather than added. |
| 71 | ** |
| 72 | ** Only file content is merged. The result continues to use the |
| 73 | ** file and directory names from the current checkout even if those |
| 74 | ** names might have been changed in the branch being merged in. |
| 75 | ** |
| @@ -131,24 +134,84 @@ | |
| 131 | zBinGlob = find_option("binary",0,1); |
| 132 | nochangeFlag = find_option("nochange","n",0)!=0; |
| 133 | forceFlag = find_option("force","f",0)!=0; |
| 134 | zPivot = find_option("baseline",0,1); |
| 135 | capture_case_sensitive_option(); |
| 136 | if( g.argc!=3 ){ |
| 137 | usage("VERSION"); |
| 138 | } |
| 139 | db_must_be_within_tree(); |
| 140 | caseSensitive = filenames_are_case_sensitive(); |
| 141 | if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); |
| 142 | vid = db_lget_int("checkout", 0); |
| 143 | if( vid==0 ){ |
| 144 | fossil_fatal("nothing is checked out"); |
| 145 | } |
| 146 | mid = name_to_typed_rid(g.argv[2], "ci"); |
| 147 | if( mid==0 || !is_a_version(mid) ){ |
| 148 | fossil_fatal("not a version: %s", g.argv[2]); |
| 149 | } |
| 150 | if( zPivot ){ |
| 151 | pid = name_to_typed_rid(zPivot, "ci"); |
| 152 | if( pid==0 || !is_a_version(pid) ){ |
| 153 | fossil_fatal("not a version: %s", zPivot); |
| 154 | } |
| @@ -347,11 +410,11 @@ | |
| 347 | "SELECT idm FROM fv WHERE idp=0 AND idv>0 AND idm>0" |
| 348 | ); |
| 349 | while( db_step(&q)==SQLITE_ROW ){ |
| 350 | int idm = db_column_int(&q, 0); |
| 351 | char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm); |
| 352 | fossil_warning("WARNING - no common ancestor: %s\n", zName); |
| 353 | free(zName); |
| 354 | db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm); |
| 355 | } |
| 356 | db_finalize(&q); |
| 357 | |
| 358 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -57,19 +57,22 @@ | |
| 57 | |
| 58 | |
| 59 | /* |
| 60 | ** COMMAND: merge |
| 61 | ** |
| 62 | ** Usage: %fossil merge ?OPTIONS? ?VERSION? |
| 63 | ** |
| 64 | ** The argument VERSION is a version that should be merged into the |
| 65 | ** current checkout. All changes from VERSION back to the nearest |
| 66 | ** common ancestor are merged. Except, if either of the --cherrypick or |
| 67 | ** --backout options are used only the changes associated with the |
| 68 | ** single check-in VERSION are merged. The --backout option causes |
| 69 | ** the changes associated with VERSION to be removed from the current |
| 70 | ** checkout rather than added. |
| 71 | ** |
| 72 | ** If the VERSION argument is omitted, then Fossil attempts to find |
| 73 | ** a recent fork on the current branch to merge. |
| 74 | ** |
| 75 | ** Only file content is merged. The result continues to use the |
| 76 | ** file and directory names from the current checkout even if those |
| 77 | ** names might have been changed in the branch being merged in. |
| 78 | ** |
| @@ -131,24 +134,84 @@ | |
| 134 | zBinGlob = find_option("binary",0,1); |
| 135 | nochangeFlag = find_option("nochange","n",0)!=0; |
| 136 | forceFlag = find_option("force","f",0)!=0; |
| 137 | zPivot = find_option("baseline",0,1); |
| 138 | capture_case_sensitive_option(); |
| 139 | verify_all_options(); |
| 140 | db_must_be_within_tree(); |
| 141 | caseSensitive = filenames_are_case_sensitive(); |
| 142 | if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); |
| 143 | vid = db_lget_int("checkout", 0); |
| 144 | if( vid==0 ){ |
| 145 | fossil_fatal("nothing is checked out"); |
| 146 | } |
| 147 | |
| 148 | /* Find mid, the artifactID of the version to be merged into the current |
| 149 | ** check-out */ |
| 150 | if( g.argc==3 ){ |
| 151 | /* Mid is specified as an argument on the command-line */ |
| 152 | mid = name_to_typed_rid(g.argv[2], "ci"); |
| 153 | if( mid==0 || !is_a_version(mid) ){ |
| 154 | fossil_fatal("not a version: %s", g.argv[2]); |
| 155 | } |
| 156 | }else if( g.argc==2 ){ |
| 157 | /* No version specified on the command-line so pick the most recent |
| 158 | ** leaf that is (1) not the version currently checked out and (2) |
| 159 | ** has not already been merged into the current checkout and (3) |
| 160 | ** the leaf is not closed and (4) the leaf is in the same branch |
| 161 | ** as the current checkout. |
| 162 | */ |
| 163 | Stmt q; |
| 164 | if( pickFlag || backoutFlag ){ |
| 165 | fossil_fatal("cannot use --cherrypick or --backout with a fork merge"); |
| 166 | } |
| 167 | mid = db_int(0, |
| 168 | "SELECT leaf.rid" |
| 169 | " FROM leaf, event" |
| 170 | " WHERE leaf.rid=event.objid" |
| 171 | " AND leaf.rid!=%d" /* Constraint (1) */ |
| 172 | " AND leaf.rid NOT IN (SELECT merge FROM vmerge)" /* Constraint (2) */ |
| 173 | " AND NOT EXISTS(SELECT 1 FROM tagxref" /* Constraint (3) */ |
| 174 | " WHERE rid=leaf.rid" |
| 175 | " AND tagid=%d" |
| 176 | " AND tagtype>0)" |
| 177 | " AND (SELECT value FROM tagxref" /* Constraint (4) */ |
| 178 | " WHERE tagid=%d AND rid=%d AND tagtype>0) =" |
| 179 | " (SELECT value FROM tagxref" |
| 180 | " WHERE tagid=%d AND rid=leaf.rid AND tagtype>0)" |
| 181 | " ORDER BY event.mtime DESC LIMIT 1", |
| 182 | vid, TAG_CLOSED, TAG_BRANCH, vid, TAG_BRANCH |
| 183 | ); |
| 184 | if( mid==0 ){ |
| 185 | fossil_fatal("no unmerged forks of branch \"%s\"", |
| 186 | db_text(0, "SELECT value FROM tagxref" |
| 187 | " WHERE tagid=%d AND rid=%d AND tagtype>0", |
| 188 | TAG_BRANCH, vid) |
| 189 | ); |
| 190 | } |
| 191 | db_prepare(&q, |
| 192 | "SELECT blob.uuid," |
| 193 | " datetime(event.mtime,'localtime')," |
| 194 | " coalesce(ecomment, comment)," |
| 195 | " coalesce(euser, user)" |
| 196 | " FROM event, blob" |
| 197 | " WHERE event.objid=%d AND blob.rid=%d", |
| 198 | mid, mid |
| 199 | ); |
| 200 | if( db_step(&q)==SQLITE_ROW ){ |
| 201 | char *zCom = mprintf("Merging fork [%S] at %s by %s: \"%s\"", |
| 202 | db_column_text(&q, 0), db_column_text(&q, 1), |
| 203 | db_column_text(&q, 3), db_column_text(&q, 2)); |
| 204 | comment_print(zCom, 0, 79); |
| 205 | fossil_free(zCom); |
| 206 | } |
| 207 | db_finalize(&q); |
| 208 | }else{ |
| 209 | usage("?OPTIONS? ?VERSION?"); |
| 210 | return; |
| 211 | } |
| 212 | |
| 213 | if( zPivot ){ |
| 214 | pid = name_to_typed_rid(zPivot, "ci"); |
| 215 | if( pid==0 || !is_a_version(pid) ){ |
| 216 | fossil_fatal("not a version: %s", zPivot); |
| 217 | } |
| @@ -347,11 +410,11 @@ | |
| 410 | "SELECT idm FROM fv WHERE idp=0 AND idv>0 AND idm>0" |
| 411 | ); |
| 412 | while( db_step(&q)==SQLITE_ROW ){ |
| 413 | int idm = db_column_int(&q, 0); |
| 414 | char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm); |
| 415 | fossil_warning("WARNING - no common ancestor: %s", zName); |
| 416 | free(zName); |
| 417 | db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm); |
| 418 | } |
| 419 | db_finalize(&q); |
| 420 | |
| 421 |
+71
-8
| --- src/merge.c | ||
| +++ src/merge.c | ||
| @@ -57,19 +57,22 @@ | ||
| 57 | 57 | |
| 58 | 58 | |
| 59 | 59 | /* |
| 60 | 60 | ** COMMAND: merge |
| 61 | 61 | ** |
| 62 | -** Usage: %fossil merge ?OPTIONS? VERSION | |
| 62 | +** Usage: %fossil merge ?OPTIONS? ?VERSION? | |
| 63 | 63 | ** |
| 64 | 64 | ** The argument VERSION is a version that should be merged into the |
| 65 | 65 | ** current checkout. All changes from VERSION back to the nearest |
| 66 | 66 | ** common ancestor are merged. Except, if either of the --cherrypick or |
| 67 | 67 | ** --backout options are used only the changes associated with the |
| 68 | 68 | ** single check-in VERSION are merged. The --backout option causes |
| 69 | 69 | ** the changes associated with VERSION to be removed from the current |
| 70 | 70 | ** checkout rather than added. |
| 71 | +** | |
| 72 | +** If the VERSION argument is omitted, then Fossil attempts to find | |
| 73 | +** a recent fork on the current branch to merge. | |
| 71 | 74 | ** |
| 72 | 75 | ** Only file content is merged. The result continues to use the |
| 73 | 76 | ** file and directory names from the current checkout even if those |
| 74 | 77 | ** names might have been changed in the branch being merged in. |
| 75 | 78 | ** |
| @@ -131,24 +134,84 @@ | ||
| 131 | 134 | zBinGlob = find_option("binary",0,1); |
| 132 | 135 | nochangeFlag = find_option("nochange","n",0)!=0; |
| 133 | 136 | forceFlag = find_option("force","f",0)!=0; |
| 134 | 137 | zPivot = find_option("baseline",0,1); |
| 135 | 138 | capture_case_sensitive_option(); |
| 136 | - if( g.argc!=3 ){ | |
| 137 | - usage("VERSION"); | |
| 138 | - } | |
| 139 | + verify_all_options(); | |
| 139 | 140 | db_must_be_within_tree(); |
| 140 | 141 | caseSensitive = filenames_are_case_sensitive(); |
| 141 | 142 | if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); |
| 142 | 143 | vid = db_lget_int("checkout", 0); |
| 143 | 144 | if( vid==0 ){ |
| 144 | 145 | fossil_fatal("nothing is checked out"); |
| 145 | 146 | } |
| 146 | - mid = name_to_typed_rid(g.argv[2], "ci"); | |
| 147 | - if( mid==0 || !is_a_version(mid) ){ | |
| 148 | - fossil_fatal("not a version: %s", g.argv[2]); | |
| 147 | + | |
| 148 | + /* Find mid, the artifactID of the version to be merged into the current | |
| 149 | + ** check-out */ | |
| 150 | + if( g.argc==3 ){ | |
| 151 | + /* Mid is specified as an argument on the command-line */ | |
| 152 | + mid = name_to_typed_rid(g.argv[2], "ci"); | |
| 153 | + if( mid==0 || !is_a_version(mid) ){ | |
| 154 | + fossil_fatal("not a version: %s", g.argv[2]); | |
| 155 | + } | |
| 156 | + }else if( g.argc==2 ){ | |
| 157 | + /* No version specified on the command-line so pick the most recent | |
| 158 | + ** leaf that is (1) not the version currently checked out and (2) | |
| 159 | + ** has not already been merged into the current checkout and (3) | |
| 160 | + ** the leaf is not closed and (4) the leaf is in the same branch | |
| 161 | + ** as the current checkout. | |
| 162 | + */ | |
| 163 | + Stmt q; | |
| 164 | + if( pickFlag || backoutFlag ){ | |
| 165 | + fossil_fatal("cannot use --cherrypick or --backout with a fork merge"); | |
| 166 | + } | |
| 167 | + mid = db_int(0, | |
| 168 | + "SELECT leaf.rid" | |
| 169 | + " FROM leaf, event" | |
| 170 | + " WHERE leaf.rid=event.objid" | |
| 171 | + " AND leaf.rid!=%d" /* Constraint (1) */ | |
| 172 | + " AND leaf.rid NOT IN (SELECT merge FROM vmerge)" /* Constraint (2) */ | |
| 173 | + " AND NOT EXISTS(SELECT 1 FROM tagxref" /* Constraint (3) */ | |
| 174 | + " WHERE rid=leaf.rid" | |
| 175 | + " AND tagid=%d" | |
| 176 | + " AND tagtype>0)" | |
| 177 | + " AND (SELECT value FROM tagxref" /* Constraint (4) */ | |
| 178 | + " WHERE tagid=%d AND rid=%d AND tagtype>0) =" | |
| 179 | + " (SELECT value FROM tagxref" | |
| 180 | + " WHERE tagid=%d AND rid=leaf.rid AND tagtype>0)" | |
| 181 | + " ORDER BY event.mtime DESC LIMIT 1", | |
| 182 | + vid, TAG_CLOSED, TAG_BRANCH, vid, TAG_BRANCH | |
| 183 | + ); | |
| 184 | + if( mid==0 ){ | |
| 185 | + fossil_fatal("no unmerged forks of branch \"%s\"", | |
| 186 | + db_text(0, "SELECT value FROM tagxref" | |
| 187 | + " WHERE tagid=%d AND rid=%d AND tagtype>0", | |
| 188 | + TAG_BRANCH, vid) | |
| 189 | + ); | |
| 190 | + } | |
| 191 | + db_prepare(&q, | |
| 192 | + "SELECT blob.uuid," | |
| 193 | + " datetime(event.mtime,'localtime')," | |
| 194 | + " coalesce(ecomment, comment)," | |
| 195 | + " coalesce(euser, user)" | |
| 196 | + " FROM event, blob" | |
| 197 | + " WHERE event.objid=%d AND blob.rid=%d", | |
| 198 | + mid, mid | |
| 199 | + ); | |
| 200 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 201 | + char *zCom = mprintf("Merging fork [%S] at %s by %s: \"%s\"", | |
| 202 | + db_column_text(&q, 0), db_column_text(&q, 1), | |
| 203 | + db_column_text(&q, 3), db_column_text(&q, 2)); | |
| 204 | + comment_print(zCom, 0, 79); | |
| 205 | + fossil_free(zCom); | |
| 206 | + } | |
| 207 | + db_finalize(&q); | |
| 208 | + }else{ | |
| 209 | + usage("?OPTIONS? ?VERSION?"); | |
| 210 | + return; | |
| 149 | 211 | } |
| 212 | + | |
| 150 | 213 | if( zPivot ){ |
| 151 | 214 | pid = name_to_typed_rid(zPivot, "ci"); |
| 152 | 215 | if( pid==0 || !is_a_version(pid) ){ |
| 153 | 216 | fossil_fatal("not a version: %s", zPivot); |
| 154 | 217 | } |
| @@ -347,11 +410,11 @@ | ||
| 347 | 410 | "SELECT idm FROM fv WHERE idp=0 AND idv>0 AND idm>0" |
| 348 | 411 | ); |
| 349 | 412 | while( db_step(&q)==SQLITE_ROW ){ |
| 350 | 413 | int idm = db_column_int(&q, 0); |
| 351 | 414 | char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm); |
| 352 | - fossil_warning("WARNING - no common ancestor: %s\n", zName); | |
| 415 | + fossil_warning("WARNING - no common ancestor: %s", zName); | |
| 353 | 416 | free(zName); |
| 354 | 417 | db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm); |
| 355 | 418 | } |
| 356 | 419 | db_finalize(&q); |
| 357 | 420 | |
| 358 | 421 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -57,19 +57,22 @@ | |
| 57 | |
| 58 | |
| 59 | /* |
| 60 | ** COMMAND: merge |
| 61 | ** |
| 62 | ** Usage: %fossil merge ?OPTIONS? VERSION |
| 63 | ** |
| 64 | ** The argument VERSION is a version that should be merged into the |
| 65 | ** current checkout. All changes from VERSION back to the nearest |
| 66 | ** common ancestor are merged. Except, if either of the --cherrypick or |
| 67 | ** --backout options are used only the changes associated with the |
| 68 | ** single check-in VERSION are merged. The --backout option causes |
| 69 | ** the changes associated with VERSION to be removed from the current |
| 70 | ** checkout rather than added. |
| 71 | ** |
| 72 | ** Only file content is merged. The result continues to use the |
| 73 | ** file and directory names from the current checkout even if those |
| 74 | ** names might have been changed in the branch being merged in. |
| 75 | ** |
| @@ -131,24 +134,84 @@ | |
| 131 | zBinGlob = find_option("binary",0,1); |
| 132 | nochangeFlag = find_option("nochange","n",0)!=0; |
| 133 | forceFlag = find_option("force","f",0)!=0; |
| 134 | zPivot = find_option("baseline",0,1); |
| 135 | capture_case_sensitive_option(); |
| 136 | if( g.argc!=3 ){ |
| 137 | usage("VERSION"); |
| 138 | } |
| 139 | db_must_be_within_tree(); |
| 140 | caseSensitive = filenames_are_case_sensitive(); |
| 141 | if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); |
| 142 | vid = db_lget_int("checkout", 0); |
| 143 | if( vid==0 ){ |
| 144 | fossil_fatal("nothing is checked out"); |
| 145 | } |
| 146 | mid = name_to_typed_rid(g.argv[2], "ci"); |
| 147 | if( mid==0 || !is_a_version(mid) ){ |
| 148 | fossil_fatal("not a version: %s", g.argv[2]); |
| 149 | } |
| 150 | if( zPivot ){ |
| 151 | pid = name_to_typed_rid(zPivot, "ci"); |
| 152 | if( pid==0 || !is_a_version(pid) ){ |
| 153 | fossil_fatal("not a version: %s", zPivot); |
| 154 | } |
| @@ -347,11 +410,11 @@ | |
| 347 | "SELECT idm FROM fv WHERE idp=0 AND idv>0 AND idm>0" |
| 348 | ); |
| 349 | while( db_step(&q)==SQLITE_ROW ){ |
| 350 | int idm = db_column_int(&q, 0); |
| 351 | char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm); |
| 352 | fossil_warning("WARNING - no common ancestor: %s\n", zName); |
| 353 | free(zName); |
| 354 | db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm); |
| 355 | } |
| 356 | db_finalize(&q); |
| 357 | |
| 358 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -57,19 +57,22 @@ | |
| 57 | |
| 58 | |
| 59 | /* |
| 60 | ** COMMAND: merge |
| 61 | ** |
| 62 | ** Usage: %fossil merge ?OPTIONS? ?VERSION? |
| 63 | ** |
| 64 | ** The argument VERSION is a version that should be merged into the |
| 65 | ** current checkout. All changes from VERSION back to the nearest |
| 66 | ** common ancestor are merged. Except, if either of the --cherrypick or |
| 67 | ** --backout options are used only the changes associated with the |
| 68 | ** single check-in VERSION are merged. The --backout option causes |
| 69 | ** the changes associated with VERSION to be removed from the current |
| 70 | ** checkout rather than added. |
| 71 | ** |
| 72 | ** If the VERSION argument is omitted, then Fossil attempts to find |
| 73 | ** a recent fork on the current branch to merge. |
| 74 | ** |
| 75 | ** Only file content is merged. The result continues to use the |
| 76 | ** file and directory names from the current checkout even if those |
| 77 | ** names might have been changed in the branch being merged in. |
| 78 | ** |
| @@ -131,24 +134,84 @@ | |
| 134 | zBinGlob = find_option("binary",0,1); |
| 135 | nochangeFlag = find_option("nochange","n",0)!=0; |
| 136 | forceFlag = find_option("force","f",0)!=0; |
| 137 | zPivot = find_option("baseline",0,1); |
| 138 | capture_case_sensitive_option(); |
| 139 | verify_all_options(); |
| 140 | db_must_be_within_tree(); |
| 141 | caseSensitive = filenames_are_case_sensitive(); |
| 142 | if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); |
| 143 | vid = db_lget_int("checkout", 0); |
| 144 | if( vid==0 ){ |
| 145 | fossil_fatal("nothing is checked out"); |
| 146 | } |
| 147 | |
| 148 | /* Find mid, the artifactID of the version to be merged into the current |
| 149 | ** check-out */ |
| 150 | if( g.argc==3 ){ |
| 151 | /* Mid is specified as an argument on the command-line */ |
| 152 | mid = name_to_typed_rid(g.argv[2], "ci"); |
| 153 | if( mid==0 || !is_a_version(mid) ){ |
| 154 | fossil_fatal("not a version: %s", g.argv[2]); |
| 155 | } |
| 156 | }else if( g.argc==2 ){ |
| 157 | /* No version specified on the command-line so pick the most recent |
| 158 | ** leaf that is (1) not the version currently checked out and (2) |
| 159 | ** has not already been merged into the current checkout and (3) |
| 160 | ** the leaf is not closed and (4) the leaf is in the same branch |
| 161 | ** as the current checkout. |
| 162 | */ |
| 163 | Stmt q; |
| 164 | if( pickFlag || backoutFlag ){ |
| 165 | fossil_fatal("cannot use --cherrypick or --backout with a fork merge"); |
| 166 | } |
| 167 | mid = db_int(0, |
| 168 | "SELECT leaf.rid" |
| 169 | " FROM leaf, event" |
| 170 | " WHERE leaf.rid=event.objid" |
| 171 | " AND leaf.rid!=%d" /* Constraint (1) */ |
| 172 | " AND leaf.rid NOT IN (SELECT merge FROM vmerge)" /* Constraint (2) */ |
| 173 | " AND NOT EXISTS(SELECT 1 FROM tagxref" /* Constraint (3) */ |
| 174 | " WHERE rid=leaf.rid" |
| 175 | " AND tagid=%d" |
| 176 | " AND tagtype>0)" |
| 177 | " AND (SELECT value FROM tagxref" /* Constraint (4) */ |
| 178 | " WHERE tagid=%d AND rid=%d AND tagtype>0) =" |
| 179 | " (SELECT value FROM tagxref" |
| 180 | " WHERE tagid=%d AND rid=leaf.rid AND tagtype>0)" |
| 181 | " ORDER BY event.mtime DESC LIMIT 1", |
| 182 | vid, TAG_CLOSED, TAG_BRANCH, vid, TAG_BRANCH |
| 183 | ); |
| 184 | if( mid==0 ){ |
| 185 | fossil_fatal("no unmerged forks of branch \"%s\"", |
| 186 | db_text(0, "SELECT value FROM tagxref" |
| 187 | " WHERE tagid=%d AND rid=%d AND tagtype>0", |
| 188 | TAG_BRANCH, vid) |
| 189 | ); |
| 190 | } |
| 191 | db_prepare(&q, |
| 192 | "SELECT blob.uuid," |
| 193 | " datetime(event.mtime,'localtime')," |
| 194 | " coalesce(ecomment, comment)," |
| 195 | " coalesce(euser, user)" |
| 196 | " FROM event, blob" |
| 197 | " WHERE event.objid=%d AND blob.rid=%d", |
| 198 | mid, mid |
| 199 | ); |
| 200 | if( db_step(&q)==SQLITE_ROW ){ |
| 201 | char *zCom = mprintf("Merging fork [%S] at %s by %s: \"%s\"", |
| 202 | db_column_text(&q, 0), db_column_text(&q, 1), |
| 203 | db_column_text(&q, 3), db_column_text(&q, 2)); |
| 204 | comment_print(zCom, 0, 79); |
| 205 | fossil_free(zCom); |
| 206 | } |
| 207 | db_finalize(&q); |
| 208 | }else{ |
| 209 | usage("?OPTIONS? ?VERSION?"); |
| 210 | return; |
| 211 | } |
| 212 | |
| 213 | if( zPivot ){ |
| 214 | pid = name_to_typed_rid(zPivot, "ci"); |
| 215 | if( pid==0 || !is_a_version(pid) ){ |
| 216 | fossil_fatal("not a version: %s", zPivot); |
| 217 | } |
| @@ -347,11 +410,11 @@ | |
| 410 | "SELECT idm FROM fv WHERE idp=0 AND idv>0 AND idm>0" |
| 411 | ); |
| 412 | while( db_step(&q)==SQLITE_ROW ){ |
| 413 | int idm = db_column_int(&q, 0); |
| 414 | char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm); |
| 415 | fossil_warning("WARNING - no common ancestor: %s", zName); |
| 416 | free(zName); |
| 417 | db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm); |
| 418 | } |
| 419 | db_finalize(&q); |
| 420 | |
| 421 |
+71
-8
| --- src/merge.c | ||
| +++ src/merge.c | ||
| @@ -57,19 +57,22 @@ | ||
| 57 | 57 | |
| 58 | 58 | |
| 59 | 59 | /* |
| 60 | 60 | ** COMMAND: merge |
| 61 | 61 | ** |
| 62 | -** Usage: %fossil merge ?OPTIONS? VERSION | |
| 62 | +** Usage: %fossil merge ?OPTIONS? ?VERSION? | |
| 63 | 63 | ** |
| 64 | 64 | ** The argument VERSION is a version that should be merged into the |
| 65 | 65 | ** current checkout. All changes from VERSION back to the nearest |
| 66 | 66 | ** common ancestor are merged. Except, if either of the --cherrypick or |
| 67 | 67 | ** --backout options are used only the changes associated with the |
| 68 | 68 | ** single check-in VERSION are merged. The --backout option causes |
| 69 | 69 | ** the changes associated with VERSION to be removed from the current |
| 70 | 70 | ** checkout rather than added. |
| 71 | +** | |
| 72 | +** If the VERSION argument is omitted, then Fossil attempts to find | |
| 73 | +** a recent fork on the current branch to merge. | |
| 71 | 74 | ** |
| 72 | 75 | ** Only file content is merged. The result continues to use the |
| 73 | 76 | ** file and directory names from the current checkout even if those |
| 74 | 77 | ** names might have been changed in the branch being merged in. |
| 75 | 78 | ** |
| @@ -131,24 +134,84 @@ | ||
| 131 | 134 | zBinGlob = find_option("binary",0,1); |
| 132 | 135 | nochangeFlag = find_option("nochange","n",0)!=0; |
| 133 | 136 | forceFlag = find_option("force","f",0)!=0; |
| 134 | 137 | zPivot = find_option("baseline",0,1); |
| 135 | 138 | capture_case_sensitive_option(); |
| 136 | - if( g.argc!=3 ){ | |
| 137 | - usage("VERSION"); | |
| 138 | - } | |
| 139 | + verify_all_options(); | |
| 139 | 140 | db_must_be_within_tree(); |
| 140 | 141 | caseSensitive = filenames_are_case_sensitive(); |
| 141 | 142 | if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); |
| 142 | 143 | vid = db_lget_int("checkout", 0); |
| 143 | 144 | if( vid==0 ){ |
| 144 | 145 | fossil_fatal("nothing is checked out"); |
| 145 | 146 | } |
| 146 | - mid = name_to_typed_rid(g.argv[2], "ci"); | |
| 147 | - if( mid==0 || !is_a_version(mid) ){ | |
| 148 | - fossil_fatal("not a version: %s", g.argv[2]); | |
| 147 | + | |
| 148 | + /* Find mid, the artifactID of the version to be merged into the current | |
| 149 | + ** check-out */ | |
| 150 | + if( g.argc==3 ){ | |
| 151 | + /* Mid is specified as an argument on the command-line */ | |
| 152 | + mid = name_to_typed_rid(g.argv[2], "ci"); | |
| 153 | + if( mid==0 || !is_a_version(mid) ){ | |
| 154 | + fossil_fatal("not a version: %s", g.argv[2]); | |
| 155 | + } | |
| 156 | + }else if( g.argc==2 ){ | |
| 157 | + /* No version specified on the command-line so pick the most recent | |
| 158 | + ** leaf that is (1) not the version currently checked out and (2) | |
| 159 | + ** has not already been merged into the current checkout and (3) | |
| 160 | + ** the leaf is not closed and (4) the leaf is in the same branch | |
| 161 | + ** as the current checkout. | |
| 162 | + */ | |
| 163 | + Stmt q; | |
| 164 | + if( pickFlag || backoutFlag ){ | |
| 165 | + fossil_fatal("cannot use --cherrypick or --backout with a fork merge"); | |
| 166 | + } | |
| 167 | + mid = db_int(0, | |
| 168 | + "SELECT leaf.rid" | |
| 169 | + " FROM leaf, event" | |
| 170 | + " WHERE leaf.rid=event.objid" | |
| 171 | + " AND leaf.rid!=%d" /* Constraint (1) */ | |
| 172 | + " AND leaf.rid NOT IN (SELECT merge FROM vmerge)" /* Constraint (2) */ | |
| 173 | + " AND NOT EXISTS(SELECT 1 FROM tagxref" /* Constraint (3) */ | |
| 174 | + " WHERE rid=leaf.rid" | |
| 175 | + " AND tagid=%d" | |
| 176 | + " AND tagtype>0)" | |
| 177 | + " AND (SELECT value FROM tagxref" /* Constraint (4) */ | |
| 178 | + " WHERE tagid=%d AND rid=%d AND tagtype>0) =" | |
| 179 | + " (SELECT value FROM tagxref" | |
| 180 | + " WHERE tagid=%d AND rid=leaf.rid AND tagtype>0)" | |
| 181 | + " ORDER BY event.mtime DESC LIMIT 1", | |
| 182 | + vid, TAG_CLOSED, TAG_BRANCH, vid, TAG_BRANCH | |
| 183 | + ); | |
| 184 | + if( mid==0 ){ | |
| 185 | + fossil_fatal("no unmerged forks of branch \"%s\"", | |
| 186 | + db_text(0, "SELECT value FROM tagxref" | |
| 187 | + " WHERE tagid=%d AND rid=%d AND tagtype>0", | |
| 188 | + TAG_BRANCH, vid) | |
| 189 | + ); | |
| 190 | + } | |
| 191 | + db_prepare(&q, | |
| 192 | + "SELECT blob.uuid," | |
| 193 | + " datetime(event.mtime,'localtime')," | |
| 194 | + " coalesce(ecomment, comment)," | |
| 195 | + " coalesce(euser, user)" | |
| 196 | + " FROM event, blob" | |
| 197 | + " WHERE event.objid=%d AND blob.rid=%d", | |
| 198 | + mid, mid | |
| 199 | + ); | |
| 200 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 201 | + char *zCom = mprintf("Merging fork [%S] at %s by %s: \"%s\"", | |
| 202 | + db_column_text(&q, 0), db_column_text(&q, 1), | |
| 203 | + db_column_text(&q, 3), db_column_text(&q, 2)); | |
| 204 | + comment_print(zCom, 0, 79); | |
| 205 | + fossil_free(zCom); | |
| 206 | + } | |
| 207 | + db_finalize(&q); | |
| 208 | + }else{ | |
| 209 | + usage("?OPTIONS? ?VERSION?"); | |
| 210 | + return; | |
| 149 | 211 | } |
| 212 | + | |
| 150 | 213 | if( zPivot ){ |
| 151 | 214 | pid = name_to_typed_rid(zPivot, "ci"); |
| 152 | 215 | if( pid==0 || !is_a_version(pid) ){ |
| 153 | 216 | fossil_fatal("not a version: %s", zPivot); |
| 154 | 217 | } |
| @@ -347,11 +410,11 @@ | ||
| 347 | 410 | "SELECT idm FROM fv WHERE idp=0 AND idv>0 AND idm>0" |
| 348 | 411 | ); |
| 349 | 412 | while( db_step(&q)==SQLITE_ROW ){ |
| 350 | 413 | int idm = db_column_int(&q, 0); |
| 351 | 414 | char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm); |
| 352 | - fossil_warning("WARNING - no common ancestor: %s\n", zName); | |
| 415 | + fossil_warning("WARNING - no common ancestor: %s", zName); | |
| 353 | 416 | free(zName); |
| 354 | 417 | db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm); |
| 355 | 418 | } |
| 356 | 419 | db_finalize(&q); |
| 357 | 420 | |
| 358 | 421 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -57,19 +57,22 @@ | |
| 57 | |
| 58 | |
| 59 | /* |
| 60 | ** COMMAND: merge |
| 61 | ** |
| 62 | ** Usage: %fossil merge ?OPTIONS? VERSION |
| 63 | ** |
| 64 | ** The argument VERSION is a version that should be merged into the |
| 65 | ** current checkout. All changes from VERSION back to the nearest |
| 66 | ** common ancestor are merged. Except, if either of the --cherrypick or |
| 67 | ** --backout options are used only the changes associated with the |
| 68 | ** single check-in VERSION are merged. The --backout option causes |
| 69 | ** the changes associated with VERSION to be removed from the current |
| 70 | ** checkout rather than added. |
| 71 | ** |
| 72 | ** Only file content is merged. The result continues to use the |
| 73 | ** file and directory names from the current checkout even if those |
| 74 | ** names might have been changed in the branch being merged in. |
| 75 | ** |
| @@ -131,24 +134,84 @@ | |
| 131 | zBinGlob = find_option("binary",0,1); |
| 132 | nochangeFlag = find_option("nochange","n",0)!=0; |
| 133 | forceFlag = find_option("force","f",0)!=0; |
| 134 | zPivot = find_option("baseline",0,1); |
| 135 | capture_case_sensitive_option(); |
| 136 | if( g.argc!=3 ){ |
| 137 | usage("VERSION"); |
| 138 | } |
| 139 | db_must_be_within_tree(); |
| 140 | caseSensitive = filenames_are_case_sensitive(); |
| 141 | if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); |
| 142 | vid = db_lget_int("checkout", 0); |
| 143 | if( vid==0 ){ |
| 144 | fossil_fatal("nothing is checked out"); |
| 145 | } |
| 146 | mid = name_to_typed_rid(g.argv[2], "ci"); |
| 147 | if( mid==0 || !is_a_version(mid) ){ |
| 148 | fossil_fatal("not a version: %s", g.argv[2]); |
| 149 | } |
| 150 | if( zPivot ){ |
| 151 | pid = name_to_typed_rid(zPivot, "ci"); |
| 152 | if( pid==0 || !is_a_version(pid) ){ |
| 153 | fossil_fatal("not a version: %s", zPivot); |
| 154 | } |
| @@ -347,11 +410,11 @@ | |
| 347 | "SELECT idm FROM fv WHERE idp=0 AND idv>0 AND idm>0" |
| 348 | ); |
| 349 | while( db_step(&q)==SQLITE_ROW ){ |
| 350 | int idm = db_column_int(&q, 0); |
| 351 | char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm); |
| 352 | fossil_warning("WARNING - no common ancestor: %s\n", zName); |
| 353 | free(zName); |
| 354 | db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm); |
| 355 | } |
| 356 | db_finalize(&q); |
| 357 | |
| 358 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -57,19 +57,22 @@ | |
| 57 | |
| 58 | |
| 59 | /* |
| 60 | ** COMMAND: merge |
| 61 | ** |
| 62 | ** Usage: %fossil merge ?OPTIONS? ?VERSION? |
| 63 | ** |
| 64 | ** The argument VERSION is a version that should be merged into the |
| 65 | ** current checkout. All changes from VERSION back to the nearest |
| 66 | ** common ancestor are merged. Except, if either of the --cherrypick or |
| 67 | ** --backout options are used only the changes associated with the |
| 68 | ** single check-in VERSION are merged. The --backout option causes |
| 69 | ** the changes associated with VERSION to be removed from the current |
| 70 | ** checkout rather than added. |
| 71 | ** |
| 72 | ** If the VERSION argument is omitted, then Fossil attempts to find |
| 73 | ** a recent fork on the current branch to merge. |
| 74 | ** |
| 75 | ** Only file content is merged. The result continues to use the |
| 76 | ** file and directory names from the current checkout even if those |
| 77 | ** names might have been changed in the branch being merged in. |
| 78 | ** |
| @@ -131,24 +134,84 @@ | |
| 134 | zBinGlob = find_option("binary",0,1); |
| 135 | nochangeFlag = find_option("nochange","n",0)!=0; |
| 136 | forceFlag = find_option("force","f",0)!=0; |
| 137 | zPivot = find_option("baseline",0,1); |
| 138 | capture_case_sensitive_option(); |
| 139 | verify_all_options(); |
| 140 | db_must_be_within_tree(); |
| 141 | caseSensitive = filenames_are_case_sensitive(); |
| 142 | if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); |
| 143 | vid = db_lget_int("checkout", 0); |
| 144 | if( vid==0 ){ |
| 145 | fossil_fatal("nothing is checked out"); |
| 146 | } |
| 147 | |
| 148 | /* Find mid, the artifactID of the version to be merged into the current |
| 149 | ** check-out */ |
| 150 | if( g.argc==3 ){ |
| 151 | /* Mid is specified as an argument on the command-line */ |
| 152 | mid = name_to_typed_rid(g.argv[2], "ci"); |
| 153 | if( mid==0 || !is_a_version(mid) ){ |
| 154 | fossil_fatal("not a version: %s", g.argv[2]); |
| 155 | } |
| 156 | }else if( g.argc==2 ){ |
| 157 | /* No version specified on the command-line so pick the most recent |
| 158 | ** leaf that is (1) not the version currently checked out and (2) |
| 159 | ** has not already been merged into the current checkout and (3) |
| 160 | ** the leaf is not closed and (4) the leaf is in the same branch |
| 161 | ** as the current checkout. |
| 162 | */ |
| 163 | Stmt q; |
| 164 | if( pickFlag || backoutFlag ){ |
| 165 | fossil_fatal("cannot use --cherrypick or --backout with a fork merge"); |
| 166 | } |
| 167 | mid = db_int(0, |
| 168 | "SELECT leaf.rid" |
| 169 | " FROM leaf, event" |
| 170 | " WHERE leaf.rid=event.objid" |
| 171 | " AND leaf.rid!=%d" /* Constraint (1) */ |
| 172 | " AND leaf.rid NOT IN (SELECT merge FROM vmerge)" /* Constraint (2) */ |
| 173 | " AND NOT EXISTS(SELECT 1 FROM tagxref" /* Constraint (3) */ |
| 174 | " WHERE rid=leaf.rid" |
| 175 | " AND tagid=%d" |
| 176 | " AND tagtype>0)" |
| 177 | " AND (SELECT value FROM tagxref" /* Constraint (4) */ |
| 178 | " WHERE tagid=%d AND rid=%d AND tagtype>0) =" |
| 179 | " (SELECT value FROM tagxref" |
| 180 | " WHERE tagid=%d AND rid=leaf.rid AND tagtype>0)" |
| 181 | " ORDER BY event.mtime DESC LIMIT 1", |
| 182 | vid, TAG_CLOSED, TAG_BRANCH, vid, TAG_BRANCH |
| 183 | ); |
| 184 | if( mid==0 ){ |
| 185 | fossil_fatal("no unmerged forks of branch \"%s\"", |
| 186 | db_text(0, "SELECT value FROM tagxref" |
| 187 | " WHERE tagid=%d AND rid=%d AND tagtype>0", |
| 188 | TAG_BRANCH, vid) |
| 189 | ); |
| 190 | } |
| 191 | db_prepare(&q, |
| 192 | "SELECT blob.uuid," |
| 193 | " datetime(event.mtime,'localtime')," |
| 194 | " coalesce(ecomment, comment)," |
| 195 | " coalesce(euser, user)" |
| 196 | " FROM event, blob" |
| 197 | " WHERE event.objid=%d AND blob.rid=%d", |
| 198 | mid, mid |
| 199 | ); |
| 200 | if( db_step(&q)==SQLITE_ROW ){ |
| 201 | char *zCom = mprintf("Merging fork [%S] at %s by %s: \"%s\"", |
| 202 | db_column_text(&q, 0), db_column_text(&q, 1), |
| 203 | db_column_text(&q, 3), db_column_text(&q, 2)); |
| 204 | comment_print(zCom, 0, 79); |
| 205 | fossil_free(zCom); |
| 206 | } |
| 207 | db_finalize(&q); |
| 208 | }else{ |
| 209 | usage("?OPTIONS? ?VERSION?"); |
| 210 | return; |
| 211 | } |
| 212 | |
| 213 | if( zPivot ){ |
| 214 | pid = name_to_typed_rid(zPivot, "ci"); |
| 215 | if( pid==0 || !is_a_version(pid) ){ |
| 216 | fossil_fatal("not a version: %s", zPivot); |
| 217 | } |
| @@ -347,11 +410,11 @@ | |
| 410 | "SELECT idm FROM fv WHERE idp=0 AND idv>0 AND idm>0" |
| 411 | ); |
| 412 | while( db_step(&q)==SQLITE_ROW ){ |
| 413 | int idm = db_column_int(&q, 0); |
| 414 | char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm); |
| 415 | fossil_warning("WARNING - no common ancestor: %s", zName); |
| 416 | free(zName); |
| 417 | db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm); |
| 418 | } |
| 419 | db_finalize(&q); |
| 420 | |
| 421 |
+16
-1
| --- src/printf.c | ||
| +++ src/printf.c | ||
| @@ -848,11 +848,11 @@ | ||
| 848 | 848 | fwrite(z, 1, n, toStdErr ? stderr : stdout); |
| 849 | 849 | fflush(toStdErr ? stderr : stdout); |
| 850 | 850 | } |
| 851 | 851 | |
| 852 | 852 | /* |
| 853 | -** Force the the standard output cursor to move to the beginning | |
| 853 | +** Force the standard output cursor to move to the beginning | |
| 854 | 854 | ** of a line, if it is not there already. |
| 855 | 855 | */ |
| 856 | 856 | void fossil_force_newline(void){ |
| 857 | 857 | if( g.cgiOutput==0 && stdoutAtBOL==0 ) fossil_puts("\n", 0); |
| 858 | 858 | } |
| @@ -871,10 +871,25 @@ | ||
| 871 | 871 | Blob b = empty_blob; |
| 872 | 872 | vxprintf(&b, zFormat, ap); |
| 873 | 873 | fossil_puts(blob_str(&b), 0); |
| 874 | 874 | blob_reset(&b); |
| 875 | 875 | } |
| 876 | + va_end(ap); | |
| 877 | +} | |
| 878 | + | |
| 879 | +/* | |
| 880 | +** Print a trace message on standard error. | |
| 881 | +*/ | |
| 882 | +void fossil_trace(const char *zFormat, ...){ | |
| 883 | + va_list ap; | |
| 884 | + Blob b; | |
| 885 | + va_start(ap, zFormat); | |
| 886 | + b = empty_blob; | |
| 887 | + vxprintf(&b, zFormat, ap); | |
| 888 | + fossil_puts(blob_str(&b), 1); | |
| 889 | + blob_reset(&b); | |
| 890 | + va_end(ap); | |
| 876 | 891 | } |
| 877 | 892 | |
| 878 | 893 | /* |
| 879 | 894 | ** Like strcmp() except that it accepts NULL pointers. NULL sorts before |
| 880 | 895 | ** all non-NULL string pointers. Also, this strcmp() is a binary comparison |
| 881 | 896 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -848,11 +848,11 @@ | |
| 848 | fwrite(z, 1, n, toStdErr ? stderr : stdout); |
| 849 | fflush(toStdErr ? stderr : stdout); |
| 850 | } |
| 851 | |
| 852 | /* |
| 853 | ** Force the the standard output cursor to move to the beginning |
| 854 | ** of a line, if it is not there already. |
| 855 | */ |
| 856 | void fossil_force_newline(void){ |
| 857 | if( g.cgiOutput==0 && stdoutAtBOL==0 ) fossil_puts("\n", 0); |
| 858 | } |
| @@ -871,10 +871,25 @@ | |
| 871 | Blob b = empty_blob; |
| 872 | vxprintf(&b, zFormat, ap); |
| 873 | fossil_puts(blob_str(&b), 0); |
| 874 | blob_reset(&b); |
| 875 | } |
| 876 | } |
| 877 | |
| 878 | /* |
| 879 | ** Like strcmp() except that it accepts NULL pointers. NULL sorts before |
| 880 | ** all non-NULL string pointers. Also, this strcmp() is a binary comparison |
| 881 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -848,11 +848,11 @@ | |
| 848 | fwrite(z, 1, n, toStdErr ? stderr : stdout); |
| 849 | fflush(toStdErr ? stderr : stdout); |
| 850 | } |
| 851 | |
| 852 | /* |
| 853 | ** Force the standard output cursor to move to the beginning |
| 854 | ** of a line, if it is not there already. |
| 855 | */ |
| 856 | void fossil_force_newline(void){ |
| 857 | if( g.cgiOutput==0 && stdoutAtBOL==0 ) fossil_puts("\n", 0); |
| 858 | } |
| @@ -871,10 +871,25 @@ | |
| 871 | Blob b = empty_blob; |
| 872 | vxprintf(&b, zFormat, ap); |
| 873 | fossil_puts(blob_str(&b), 0); |
| 874 | blob_reset(&b); |
| 875 | } |
| 876 | va_end(ap); |
| 877 | } |
| 878 | |
| 879 | /* |
| 880 | ** Print a trace message on standard error. |
| 881 | */ |
| 882 | void fossil_trace(const char *zFormat, ...){ |
| 883 | va_list ap; |
| 884 | Blob b; |
| 885 | va_start(ap, zFormat); |
| 886 | b = empty_blob; |
| 887 | vxprintf(&b, zFormat, ap); |
| 888 | fossil_puts(blob_str(&b), 1); |
| 889 | blob_reset(&b); |
| 890 | va_end(ap); |
| 891 | } |
| 892 | |
| 893 | /* |
| 894 | ** Like strcmp() except that it accepts NULL pointers. NULL sorts before |
| 895 | ** all non-NULL string pointers. Also, this strcmp() is a binary comparison |
| 896 |
+16
-1
| --- src/printf.c | ||
| +++ src/printf.c | ||
| @@ -848,11 +848,11 @@ | ||
| 848 | 848 | fwrite(z, 1, n, toStdErr ? stderr : stdout); |
| 849 | 849 | fflush(toStdErr ? stderr : stdout); |
| 850 | 850 | } |
| 851 | 851 | |
| 852 | 852 | /* |
| 853 | -** Force the the standard output cursor to move to the beginning | |
| 853 | +** Force the standard output cursor to move to the beginning | |
| 854 | 854 | ** of a line, if it is not there already. |
| 855 | 855 | */ |
| 856 | 856 | void fossil_force_newline(void){ |
| 857 | 857 | if( g.cgiOutput==0 && stdoutAtBOL==0 ) fossil_puts("\n", 0); |
| 858 | 858 | } |
| @@ -871,10 +871,25 @@ | ||
| 871 | 871 | Blob b = empty_blob; |
| 872 | 872 | vxprintf(&b, zFormat, ap); |
| 873 | 873 | fossil_puts(blob_str(&b), 0); |
| 874 | 874 | blob_reset(&b); |
| 875 | 875 | } |
| 876 | + va_end(ap); | |
| 877 | +} | |
| 878 | + | |
| 879 | +/* | |
| 880 | +** Print a trace message on standard error. | |
| 881 | +*/ | |
| 882 | +void fossil_trace(const char *zFormat, ...){ | |
| 883 | + va_list ap; | |
| 884 | + Blob b; | |
| 885 | + va_start(ap, zFormat); | |
| 886 | + b = empty_blob; | |
| 887 | + vxprintf(&b, zFormat, ap); | |
| 888 | + fossil_puts(blob_str(&b), 1); | |
| 889 | + blob_reset(&b); | |
| 890 | + va_end(ap); | |
| 876 | 891 | } |
| 877 | 892 | |
| 878 | 893 | /* |
| 879 | 894 | ** Like strcmp() except that it accepts NULL pointers. NULL sorts before |
| 880 | 895 | ** all non-NULL string pointers. Also, this strcmp() is a binary comparison |
| 881 | 896 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -848,11 +848,11 @@ | |
| 848 | fwrite(z, 1, n, toStdErr ? stderr : stdout); |
| 849 | fflush(toStdErr ? stderr : stdout); |
| 850 | } |
| 851 | |
| 852 | /* |
| 853 | ** Force the the standard output cursor to move to the beginning |
| 854 | ** of a line, if it is not there already. |
| 855 | */ |
| 856 | void fossil_force_newline(void){ |
| 857 | if( g.cgiOutput==0 && stdoutAtBOL==0 ) fossil_puts("\n", 0); |
| 858 | } |
| @@ -871,10 +871,25 @@ | |
| 871 | Blob b = empty_blob; |
| 872 | vxprintf(&b, zFormat, ap); |
| 873 | fossil_puts(blob_str(&b), 0); |
| 874 | blob_reset(&b); |
| 875 | } |
| 876 | } |
| 877 | |
| 878 | /* |
| 879 | ** Like strcmp() except that it accepts NULL pointers. NULL sorts before |
| 880 | ** all non-NULL string pointers. Also, this strcmp() is a binary comparison |
| 881 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -848,11 +848,11 @@ | |
| 848 | fwrite(z, 1, n, toStdErr ? stderr : stdout); |
| 849 | fflush(toStdErr ? stderr : stdout); |
| 850 | } |
| 851 | |
| 852 | /* |
| 853 | ** Force the standard output cursor to move to the beginning |
| 854 | ** of a line, if it is not there already. |
| 855 | */ |
| 856 | void fossil_force_newline(void){ |
| 857 | if( g.cgiOutput==0 && stdoutAtBOL==0 ) fossil_puts("\n", 0); |
| 858 | } |
| @@ -871,10 +871,25 @@ | |
| 871 | Blob b = empty_blob; |
| 872 | vxprintf(&b, zFormat, ap); |
| 873 | fossil_puts(blob_str(&b), 0); |
| 874 | blob_reset(&b); |
| 875 | } |
| 876 | va_end(ap); |
| 877 | } |
| 878 | |
| 879 | /* |
| 880 | ** Print a trace message on standard error. |
| 881 | */ |
| 882 | void fossil_trace(const char *zFormat, ...){ |
| 883 | va_list ap; |
| 884 | Blob b; |
| 885 | va_start(ap, zFormat); |
| 886 | b = empty_blob; |
| 887 | vxprintf(&b, zFormat, ap); |
| 888 | fossil_puts(blob_str(&b), 1); |
| 889 | blob_reset(&b); |
| 890 | va_end(ap); |
| 891 | } |
| 892 | |
| 893 | /* |
| 894 | ** Like strcmp() except that it accepts NULL pointers. NULL sorts before |
| 895 | ** all non-NULL string pointers. Also, this strcmp() is a binary comparison |
| 896 |
+8
| --- src/rebuild.c | ||
| +++ src/rebuild.c | ||
| @@ -524,10 +524,11 @@ | ||
| 524 | 524 | ** --noverify Skip the verification of changes to the BLOB table |
| 525 | 525 | ** --pagesize N Set the database pagesize to N. (512..65536 and power of 2) |
| 526 | 526 | ** --randomize Scan artifacts in a random order |
| 527 | 527 | ** --vacuum Run VACUUM on the database after rebuilding |
| 528 | 528 | ** --deanalyze Remove ANALYZE tables from the database |
| 529 | +** --analyze Run ANALYZE on the database after rebuilding | |
| 529 | 530 | ** --wal Set Write-Ahead-Log journalling mode on the database |
| 530 | 531 | ** --stats Show artifact statistics after rebuilding |
| 531 | 532 | ** |
| 532 | 533 | ** See also: deconstruct, reconstruct |
| 533 | 534 | */ |
| @@ -540,19 +541,21 @@ | ||
| 540 | 541 | const char *zPagesize; |
| 541 | 542 | int newPagesize = 0; |
| 542 | 543 | int activateWal; |
| 543 | 544 | int runVacuum; |
| 544 | 545 | int runDeanalyze; |
| 546 | + int runAnalyze; | |
| 545 | 547 | int runCompress; |
| 546 | 548 | int showStats; |
| 547 | 549 | |
| 548 | 550 | omitVerify = find_option("noverify",0,0)!=0; |
| 549 | 551 | forceFlag = find_option("force","f",0)!=0; |
| 550 | 552 | randomizeFlag = find_option("randomize", 0, 0)!=0; |
| 551 | 553 | doClustering = find_option("cluster", 0, 0)!=0; |
| 552 | 554 | runVacuum = find_option("vacuum",0,0)!=0; |
| 553 | 555 | runDeanalyze = find_option("deanalyze",0,0)!=0; |
| 556 | + runAnalyze = find_option("analyze",0,0)!=0; | |
| 554 | 557 | runCompress = find_option("compress",0,0)!=0; |
| 555 | 558 | zPagesize = find_option("pagesize",0,1); |
| 556 | 559 | showStats = find_option("stats",0,0)!=0; |
| 557 | 560 | if( zPagesize ){ |
| 558 | 561 | newPagesize = atoi(zPagesize); |
| @@ -605,10 +608,15 @@ | ||
| 605 | 608 | } |
| 606 | 609 | if( runDeanalyze ){ |
| 607 | 610 | db_multi_exec("DROP TABLE IF EXISTS sqlite_stat1;" |
| 608 | 611 | "DROP TABLE IF EXISTS sqlite_stat3;"); |
| 609 | 612 | } |
| 613 | + if( runAnalyze ){ | |
| 614 | + fossil_print("Analyzing the database... "); fflush(stdout); | |
| 615 | + db_multi_exec("ANALYZE;"); | |
| 616 | + fossil_print("done\n"); | |
| 617 | + } | |
| 610 | 618 | if( runVacuum ){ |
| 611 | 619 | fossil_print("Vacuuming the database... "); fflush(stdout); |
| 612 | 620 | db_multi_exec("VACUUM"); |
| 613 | 621 | fossil_print("done\n"); |
| 614 | 622 | } |
| 615 | 623 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -524,10 +524,11 @@ | |
| 524 | ** --noverify Skip the verification of changes to the BLOB table |
| 525 | ** --pagesize N Set the database pagesize to N. (512..65536 and power of 2) |
| 526 | ** --randomize Scan artifacts in a random order |
| 527 | ** --vacuum Run VACUUM on the database after rebuilding |
| 528 | ** --deanalyze Remove ANALYZE tables from the database |
| 529 | ** --wal Set Write-Ahead-Log journalling mode on the database |
| 530 | ** --stats Show artifact statistics after rebuilding |
| 531 | ** |
| 532 | ** See also: deconstruct, reconstruct |
| 533 | */ |
| @@ -540,19 +541,21 @@ | |
| 540 | const char *zPagesize; |
| 541 | int newPagesize = 0; |
| 542 | int activateWal; |
| 543 | int runVacuum; |
| 544 | int runDeanalyze; |
| 545 | int runCompress; |
| 546 | int showStats; |
| 547 | |
| 548 | omitVerify = find_option("noverify",0,0)!=0; |
| 549 | forceFlag = find_option("force","f",0)!=0; |
| 550 | randomizeFlag = find_option("randomize", 0, 0)!=0; |
| 551 | doClustering = find_option("cluster", 0, 0)!=0; |
| 552 | runVacuum = find_option("vacuum",0,0)!=0; |
| 553 | runDeanalyze = find_option("deanalyze",0,0)!=0; |
| 554 | runCompress = find_option("compress",0,0)!=0; |
| 555 | zPagesize = find_option("pagesize",0,1); |
| 556 | showStats = find_option("stats",0,0)!=0; |
| 557 | if( zPagesize ){ |
| 558 | newPagesize = atoi(zPagesize); |
| @@ -605,10 +608,15 @@ | |
| 605 | } |
| 606 | if( runDeanalyze ){ |
| 607 | db_multi_exec("DROP TABLE IF EXISTS sqlite_stat1;" |
| 608 | "DROP TABLE IF EXISTS sqlite_stat3;"); |
| 609 | } |
| 610 | if( runVacuum ){ |
| 611 | fossil_print("Vacuuming the database... "); fflush(stdout); |
| 612 | db_multi_exec("VACUUM"); |
| 613 | fossil_print("done\n"); |
| 614 | } |
| 615 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -524,10 +524,11 @@ | |
| 524 | ** --noverify Skip the verification of changes to the BLOB table |
| 525 | ** --pagesize N Set the database pagesize to N. (512..65536 and power of 2) |
| 526 | ** --randomize Scan artifacts in a random order |
| 527 | ** --vacuum Run VACUUM on the database after rebuilding |
| 528 | ** --deanalyze Remove ANALYZE tables from the database |
| 529 | ** --analyze Run ANALYZE on the database after rebuilding |
| 530 | ** --wal Set Write-Ahead-Log journalling mode on the database |
| 531 | ** --stats Show artifact statistics after rebuilding |
| 532 | ** |
| 533 | ** See also: deconstruct, reconstruct |
| 534 | */ |
| @@ -540,19 +541,21 @@ | |
| 541 | const char *zPagesize; |
| 542 | int newPagesize = 0; |
| 543 | int activateWal; |
| 544 | int runVacuum; |
| 545 | int runDeanalyze; |
| 546 | int runAnalyze; |
| 547 | int runCompress; |
| 548 | int showStats; |
| 549 | |
| 550 | omitVerify = find_option("noverify",0,0)!=0; |
| 551 | forceFlag = find_option("force","f",0)!=0; |
| 552 | randomizeFlag = find_option("randomize", 0, 0)!=0; |
| 553 | doClustering = find_option("cluster", 0, 0)!=0; |
| 554 | runVacuum = find_option("vacuum",0,0)!=0; |
| 555 | runDeanalyze = find_option("deanalyze",0,0)!=0; |
| 556 | runAnalyze = find_option("analyze",0,0)!=0; |
| 557 | runCompress = find_option("compress",0,0)!=0; |
| 558 | zPagesize = find_option("pagesize",0,1); |
| 559 | showStats = find_option("stats",0,0)!=0; |
| 560 | if( zPagesize ){ |
| 561 | newPagesize = atoi(zPagesize); |
| @@ -605,10 +608,15 @@ | |
| 608 | } |
| 609 | if( runDeanalyze ){ |
| 610 | db_multi_exec("DROP TABLE IF EXISTS sqlite_stat1;" |
| 611 | "DROP TABLE IF EXISTS sqlite_stat3;"); |
| 612 | } |
| 613 | if( runAnalyze ){ |
| 614 | fossil_print("Analyzing the database... "); fflush(stdout); |
| 615 | db_multi_exec("ANALYZE;"); |
| 616 | fossil_print("done\n"); |
| 617 | } |
| 618 | if( runVacuum ){ |
| 619 | fossil_print("Vacuuming the database... "); fflush(stdout); |
| 620 | db_multi_exec("VACUUM"); |
| 621 | fossil_print("done\n"); |
| 622 | } |
| 623 |
+8
| --- src/rebuild.c | ||
| +++ src/rebuild.c | ||
| @@ -524,10 +524,11 @@ | ||
| 524 | 524 | ** --noverify Skip the verification of changes to the BLOB table |
| 525 | 525 | ** --pagesize N Set the database pagesize to N. (512..65536 and power of 2) |
| 526 | 526 | ** --randomize Scan artifacts in a random order |
| 527 | 527 | ** --vacuum Run VACUUM on the database after rebuilding |
| 528 | 528 | ** --deanalyze Remove ANALYZE tables from the database |
| 529 | +** --analyze Run ANALYZE on the database after rebuilding | |
| 529 | 530 | ** --wal Set Write-Ahead-Log journalling mode on the database |
| 530 | 531 | ** --stats Show artifact statistics after rebuilding |
| 531 | 532 | ** |
| 532 | 533 | ** See also: deconstruct, reconstruct |
| 533 | 534 | */ |
| @@ -540,19 +541,21 @@ | ||
| 540 | 541 | const char *zPagesize; |
| 541 | 542 | int newPagesize = 0; |
| 542 | 543 | int activateWal; |
| 543 | 544 | int runVacuum; |
| 544 | 545 | int runDeanalyze; |
| 546 | + int runAnalyze; | |
| 545 | 547 | int runCompress; |
| 546 | 548 | int showStats; |
| 547 | 549 | |
| 548 | 550 | omitVerify = find_option("noverify",0,0)!=0; |
| 549 | 551 | forceFlag = find_option("force","f",0)!=0; |
| 550 | 552 | randomizeFlag = find_option("randomize", 0, 0)!=0; |
| 551 | 553 | doClustering = find_option("cluster", 0, 0)!=0; |
| 552 | 554 | runVacuum = find_option("vacuum",0,0)!=0; |
| 553 | 555 | runDeanalyze = find_option("deanalyze",0,0)!=0; |
| 556 | + runAnalyze = find_option("analyze",0,0)!=0; | |
| 554 | 557 | runCompress = find_option("compress",0,0)!=0; |
| 555 | 558 | zPagesize = find_option("pagesize",0,1); |
| 556 | 559 | showStats = find_option("stats",0,0)!=0; |
| 557 | 560 | if( zPagesize ){ |
| 558 | 561 | newPagesize = atoi(zPagesize); |
| @@ -605,10 +608,15 @@ | ||
| 605 | 608 | } |
| 606 | 609 | if( runDeanalyze ){ |
| 607 | 610 | db_multi_exec("DROP TABLE IF EXISTS sqlite_stat1;" |
| 608 | 611 | "DROP TABLE IF EXISTS sqlite_stat3;"); |
| 609 | 612 | } |
| 613 | + if( runAnalyze ){ | |
| 614 | + fossil_print("Analyzing the database... "); fflush(stdout); | |
| 615 | + db_multi_exec("ANALYZE;"); | |
| 616 | + fossil_print("done\n"); | |
| 617 | + } | |
| 610 | 618 | if( runVacuum ){ |
| 611 | 619 | fossil_print("Vacuuming the database... "); fflush(stdout); |
| 612 | 620 | db_multi_exec("VACUUM"); |
| 613 | 621 | fossil_print("done\n"); |
| 614 | 622 | } |
| 615 | 623 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -524,10 +524,11 @@ | |
| 524 | ** --noverify Skip the verification of changes to the BLOB table |
| 525 | ** --pagesize N Set the database pagesize to N. (512..65536 and power of 2) |
| 526 | ** --randomize Scan artifacts in a random order |
| 527 | ** --vacuum Run VACUUM on the database after rebuilding |
| 528 | ** --deanalyze Remove ANALYZE tables from the database |
| 529 | ** --wal Set Write-Ahead-Log journalling mode on the database |
| 530 | ** --stats Show artifact statistics after rebuilding |
| 531 | ** |
| 532 | ** See also: deconstruct, reconstruct |
| 533 | */ |
| @@ -540,19 +541,21 @@ | |
| 540 | const char *zPagesize; |
| 541 | int newPagesize = 0; |
| 542 | int activateWal; |
| 543 | int runVacuum; |
| 544 | int runDeanalyze; |
| 545 | int runCompress; |
| 546 | int showStats; |
| 547 | |
| 548 | omitVerify = find_option("noverify",0,0)!=0; |
| 549 | forceFlag = find_option("force","f",0)!=0; |
| 550 | randomizeFlag = find_option("randomize", 0, 0)!=0; |
| 551 | doClustering = find_option("cluster", 0, 0)!=0; |
| 552 | runVacuum = find_option("vacuum",0,0)!=0; |
| 553 | runDeanalyze = find_option("deanalyze",0,0)!=0; |
| 554 | runCompress = find_option("compress",0,0)!=0; |
| 555 | zPagesize = find_option("pagesize",0,1); |
| 556 | showStats = find_option("stats",0,0)!=0; |
| 557 | if( zPagesize ){ |
| 558 | newPagesize = atoi(zPagesize); |
| @@ -605,10 +608,15 @@ | |
| 605 | } |
| 606 | if( runDeanalyze ){ |
| 607 | db_multi_exec("DROP TABLE IF EXISTS sqlite_stat1;" |
| 608 | "DROP TABLE IF EXISTS sqlite_stat3;"); |
| 609 | } |
| 610 | if( runVacuum ){ |
| 611 | fossil_print("Vacuuming the database... "); fflush(stdout); |
| 612 | db_multi_exec("VACUUM"); |
| 613 | fossil_print("done\n"); |
| 614 | } |
| 615 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -524,10 +524,11 @@ | |
| 524 | ** --noverify Skip the verification of changes to the BLOB table |
| 525 | ** --pagesize N Set the database pagesize to N. (512..65536 and power of 2) |
| 526 | ** --randomize Scan artifacts in a random order |
| 527 | ** --vacuum Run VACUUM on the database after rebuilding |
| 528 | ** --deanalyze Remove ANALYZE tables from the database |
| 529 | ** --analyze Run ANALYZE on the database after rebuilding |
| 530 | ** --wal Set Write-Ahead-Log journalling mode on the database |
| 531 | ** --stats Show artifact statistics after rebuilding |
| 532 | ** |
| 533 | ** See also: deconstruct, reconstruct |
| 534 | */ |
| @@ -540,19 +541,21 @@ | |
| 541 | const char *zPagesize; |
| 542 | int newPagesize = 0; |
| 543 | int activateWal; |
| 544 | int runVacuum; |
| 545 | int runDeanalyze; |
| 546 | int runAnalyze; |
| 547 | int runCompress; |
| 548 | int showStats; |
| 549 | |
| 550 | omitVerify = find_option("noverify",0,0)!=0; |
| 551 | forceFlag = find_option("force","f",0)!=0; |
| 552 | randomizeFlag = find_option("randomize", 0, 0)!=0; |
| 553 | doClustering = find_option("cluster", 0, 0)!=0; |
| 554 | runVacuum = find_option("vacuum",0,0)!=0; |
| 555 | runDeanalyze = find_option("deanalyze",0,0)!=0; |
| 556 | runAnalyze = find_option("analyze",0,0)!=0; |
| 557 | runCompress = find_option("compress",0,0)!=0; |
| 558 | zPagesize = find_option("pagesize",0,1); |
| 559 | showStats = find_option("stats",0,0)!=0; |
| 560 | if( zPagesize ){ |
| 561 | newPagesize = atoi(zPagesize); |
| @@ -605,10 +608,15 @@ | |
| 608 | } |
| 609 | if( runDeanalyze ){ |
| 610 | db_multi_exec("DROP TABLE IF EXISTS sqlite_stat1;" |
| 611 | "DROP TABLE IF EXISTS sqlite_stat3;"); |
| 612 | } |
| 613 | if( runAnalyze ){ |
| 614 | fossil_print("Analyzing the database... "); fflush(stdout); |
| 615 | db_multi_exec("ANALYZE;"); |
| 616 | fossil_print("done\n"); |
| 617 | } |
| 618 | if( runVacuum ){ |
| 619 | fossil_print("Vacuuming the database... "); fflush(stdout); |
| 620 | db_multi_exec("VACUUM"); |
| 621 | fossil_print("done\n"); |
| 622 | } |
| 623 |
+38
-24
| --- src/regexp.c | ||
| +++ src/regexp.c | ||
| @@ -32,11 +32,11 @@ | ||
| 32 | 32 | ** X$ X occurring at the end of the string |
| 33 | 33 | ** . Match any single character |
| 34 | 34 | ** \c Character c where c is one of \{}()[]|*+?. |
| 35 | 35 | ** \c C-language escapes for c in afnrtv. ex: \t or \n |
| 36 | 36 | ** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX |
| 37 | -** \xXXX Where XXX is any number of hex digits, unicode value XXX | |
| 37 | +** \xXX Where XX is exactly 2 hex digits, unicode value XX | |
| 38 | 38 | ** [abc] Any single character from the set abc |
| 39 | 39 | ** [^abc] Any single character not in the set abc |
| 40 | 40 | ** [a-z] Any single character in the range a-z |
| 41 | 41 | ** [^a-z] Any single character not in the range a-z |
| 42 | 42 | ** \b Word boundary |
| @@ -134,25 +134,25 @@ | ||
| 134 | 134 | */ |
| 135 | 135 | static unsigned re_next_char(ReInput *p){ |
| 136 | 136 | unsigned c; |
| 137 | 137 | if( p->i>=p->mx ) return 0; |
| 138 | 138 | c = p->z[p->i++]; |
| 139 | - if( c>0x80 ){ | |
| 139 | + if( c>=0x80 ){ | |
| 140 | 140 | if( (c&0xe0)==0xc0 && p->i<p->mx && (p->z[p->i]&0xc0)==0x80 ){ |
| 141 | 141 | c = (c&0x1f)<<6 | (p->z[p->i++]&0x3f); |
| 142 | 142 | if( c<0x80 ) c = 0xfffd; |
| 143 | 143 | }else if( (c&0xf0)==0xe0 && p->i+1<p->mx && (p->z[p->i]&0xc0)==0x80 |
| 144 | 144 | && (p->z[p->i+1]&0xc0)==0x80 ){ |
| 145 | 145 | c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f); |
| 146 | 146 | p->i += 2; |
| 147 | - if( c<0x3ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd; | |
| 147 | + if( c<=0x3ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd; | |
| 148 | 148 | }else if( (c&0xf8)==0xf0 && p->i+3<p->mx && (p->z[p->i]&0xc0)==0x80 |
| 149 | 149 | && (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){ |
| 150 | 150 | c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6) |
| 151 | 151 | | (p->z[p->i+2]&0x3f); |
| 152 | 152 | p->i += 3; |
| 153 | - if( c<0xffff ) c = 0xfffd; | |
| 153 | + if( c<=0xffff || c>0x10ffff ) c = 0xfffd; | |
| 154 | 154 | }else{ |
| 155 | 155 | c = 0xfffd; |
| 156 | 156 | } |
| 157 | 157 | } |
| 158 | 158 | return c; |
| @@ -172,17 +172,17 @@ | ||
| 172 | 172 | return (c>='0' && c<='9'); |
| 173 | 173 | } |
| 174 | 174 | |
| 175 | 175 | /* Return true if c is a perl "space" character: [ \t\r\n\v\f] */ |
| 176 | 176 | static int re_space_char(int c){ |
| 177 | - return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f' ; | |
| 177 | + return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f'; | |
| 178 | 178 | } |
| 179 | 179 | |
| 180 | 180 | /* Run a compiled regular expression on the zero-terminated input |
| 181 | 181 | ** string zIn[]. Return true on a match and false if there is no match. |
| 182 | 182 | */ |
| 183 | -int re_execute(ReCompiled *pRe, const unsigned char *zIn, int nIn){ | |
| 183 | +int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){ | |
| 184 | 184 | ReStateSet aStateSet[2], *pThis, *pNext; |
| 185 | 185 | ReStateNumber aSpace[100]; |
| 186 | 186 | ReStateNumber *pToFree; |
| 187 | 187 | unsigned int i = 0; |
| 188 | 188 | unsigned int iSwap = 0; |
| @@ -192,19 +192,22 @@ | ||
| 192 | 192 | ReInput in; |
| 193 | 193 | |
| 194 | 194 | in.z = zIn; |
| 195 | 195 | in.i = 0; |
| 196 | 196 | in.mx = nIn>=0 ? nIn : strlen((char const*)zIn); |
| 197 | + | |
| 198 | + /* Look for the initial prefix match, if there is one. */ | |
| 197 | 199 | if( pRe->nInit ){ |
| 198 | 200 | unsigned char x = pRe->zInit[0]; |
| 199 | - while( in.i+pRe->nInit<in.mx | |
| 201 | + while( in.i+pRe->nInit<=in.mx | |
| 200 | 202 | && (zIn[in.i]!=x || memcmp(zIn+in.i, pRe->zInit, pRe->nInit)!=0) |
| 201 | 203 | ){ |
| 202 | 204 | in.i++; |
| 203 | 205 | } |
| 204 | - if( in.i+pRe->nInit>=in.mx ) return 0; | |
| 206 | + if( in.i+pRe->nInit>in.mx ) return 0; | |
| 205 | 207 | } |
| 208 | + | |
| 206 | 209 | if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){ |
| 207 | 210 | pToFree = 0; |
| 208 | 211 | aStateSet[0].aState = aSpace; |
| 209 | 212 | }else{ |
| 210 | 213 | pToFree = fossil_malloc( sizeof(ReStateNumber)*2*pRe->nState ); |
| @@ -275,11 +278,11 @@ | ||
| 275 | 278 | re_add_state(pThis, x+pRe->aArg[x]); |
| 276 | 279 | break; |
| 277 | 280 | } |
| 278 | 281 | case RE_OP_ACCEPT: { |
| 279 | 282 | rc = 1; |
| 280 | - goto re_execute_end; | |
| 283 | + goto re_match_end; | |
| 281 | 284 | } |
| 282 | 285 | case RE_OP_CC_INC: |
| 283 | 286 | case RE_OP_CC_EXC: { |
| 284 | 287 | int j = 1; |
| 285 | 288 | int n = pRe->aArg[x]; |
| @@ -307,11 +310,11 @@ | ||
| 307 | 310 | } |
| 308 | 311 | } |
| 309 | 312 | for(i=0; i<pNext->nState; i++){ |
| 310 | 313 | if( pRe->aOp[pNext->aState[i]]==RE_OP_ACCEPT ){ rc = 1; break; } |
| 311 | 314 | } |
| 312 | -re_execute_end: | |
| 315 | +re_match_end: | |
| 313 | 316 | fossil_free(pToFree); |
| 314 | 317 | return rc; |
| 315 | 318 | } |
| 316 | 319 | |
| 317 | 320 | /* Resize the opcode and argument arrays for an RE under construction. |
| @@ -378,21 +381,20 @@ | ||
| 378 | 381 | *pV = (*pV)*16 + (c & 0xff); |
| 379 | 382 | return 1; |
| 380 | 383 | } |
| 381 | 384 | |
| 382 | 385 | /* A backslash character has been seen, read the next character and |
| 383 | -** return its intepretation. | |
| 386 | +** return its interpretation. | |
| 384 | 387 | */ |
| 385 | 388 | static unsigned re_esc_char(ReCompiled *p){ |
| 386 | 389 | static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]"; |
| 387 | 390 | static const char zTrans[] = "\a\f\n\r\t\v"; |
| 388 | 391 | int i, v = 0; |
| 389 | 392 | char c; |
| 390 | 393 | if( p->sIn.i>=p->sIn.mx ) return 0; |
| 391 | - c = p->sIn.z[0]; | |
| 392 | - if( c=='u' && p->sIn.i+5<p->sIn.mx ){ | |
| 393 | - v = 0; | |
| 394 | + c = p->sIn.z[p->sIn.i]; | |
| 395 | + if( c=='u' && p->sIn.i+4<p->sIn.mx ){ | |
| 394 | 396 | const unsigned char *zIn = p->sIn.z + p->sIn.i; |
| 395 | 397 | if( re_hex(zIn[1],&v) |
| 396 | 398 | && re_hex(zIn[2],&v) |
| 397 | 399 | && re_hex(zIn[3],&v) |
| 398 | 400 | && re_hex(zIn[4],&v) |
| @@ -399,15 +401,16 @@ | ||
| 399 | 401 | ){ |
| 400 | 402 | p->sIn.i += 5; |
| 401 | 403 | return v; |
| 402 | 404 | } |
| 403 | 405 | } |
| 404 | - if( c=='x' ){ | |
| 405 | - v = 0; | |
| 406 | - for(i=1; p->sIn.i<p->sIn.mx && re_hex(p->sIn.z[p->sIn.i+i], &v); i++){} | |
| 407 | - if( i>1 ){ | |
| 408 | - p->sIn.i += i; | |
| 406 | + if( c=='x' && p->sIn.i+2<p->sIn.mx ){ | |
| 407 | + const unsigned char *zIn = p->sIn.z + p->sIn.i; | |
| 408 | + if( re_hex(zIn[1],&v) | |
| 409 | + && re_hex(zIn[2],&v) | |
| 410 | + ){ | |
| 411 | + p->sIn.i += 3; | |
| 409 | 412 | return v; |
| 410 | 413 | } |
| 411 | 414 | } |
| 412 | 415 | for(i=0; zEsc[i] && zEsc[i]!=c; i++){} |
| 413 | 416 | if( zEsc[i] ){ |
| @@ -594,16 +597,17 @@ | ||
| 594 | 597 | */ |
| 595 | 598 | void re_free(ReCompiled *pRe){ |
| 596 | 599 | if( pRe ){ |
| 597 | 600 | fossil_free(pRe->aOp); |
| 598 | 601 | fossil_free(pRe->aArg); |
| 602 | + fossil_free(pRe); | |
| 599 | 603 | } |
| 600 | 604 | } |
| 601 | 605 | |
| 602 | 606 | /* |
| 603 | 607 | ** Compile a textual regular expression in zIn[] into a compiled regular |
| 604 | -** expression suitable for us by re_execute() and return a pointer to the | |
| 608 | +** expression suitable for us by re_match() and return a pointer to the | |
| 605 | 609 | ** compiled regular expression in *ppRe. Return NULL on success or an |
| 606 | 610 | ** error message if something goes wrong. |
| 607 | 611 | */ |
| 608 | 612 | const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){ |
| 609 | 613 | ReCompiled *pRe; |
| @@ -632,11 +636,11 @@ | ||
| 632 | 636 | zErr = re_subcompile_re(pRe); |
| 633 | 637 | if( zErr ){ |
| 634 | 638 | re_free(pRe); |
| 635 | 639 | return zErr; |
| 636 | 640 | } |
| 637 | - if( rePeek(pRe)=='$' && pRe->sIn.i+1==pRe->sIn.mx ){ | |
| 641 | + if( rePeek(pRe)=='$' && pRe->sIn.i+1>=pRe->sIn.mx ){ | |
| 638 | 642 | re_append(pRe, RE_OP_MATCH, RE_EOF); |
| 639 | 643 | re_append(pRe, RE_OP_ACCEPT, 0); |
| 640 | 644 | *ppRe = pRe; |
| 641 | 645 | }else if( pRe->sIn.i>=pRe->sIn.mx ){ |
| 642 | 646 | re_append(pRe, RE_OP_ACCEPT, 0); |
| @@ -643,10 +647,19 @@ | ||
| 643 | 647 | *ppRe = pRe; |
| 644 | 648 | }else{ |
| 645 | 649 | re_free(pRe); |
| 646 | 650 | return "unrecognized character"; |
| 647 | 651 | } |
| 652 | + | |
| 653 | + /* The following is a performance optimization. If the regex begins with | |
| 654 | + ** ".*" (if the input regex lacks an initial "^") and afterwards there are | |
| 655 | + ** one or more matching characters, enter those matching characters into | |
| 656 | + ** zInit[]. The re_match() routine can then search ahead in the input | |
| 657 | + ** string looking for the initial match without having to run the whole | |
| 658 | + ** regex engine over the string. Do not worry able trying to match | |
| 659 | + ** unicode characters beyond plane 0 - those are very rare and this is | |
| 660 | + ** just an optimization. */ | |
| 648 | 661 | if( pRe->aOp[0]==RE_OP_ANYSTAR ){ |
| 649 | 662 | for(j=0, i=1; j<sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){ |
| 650 | 663 | unsigned x = pRe->aArg[i]; |
| 651 | 664 | if( x<=127 ){ |
| 652 | 665 | pRe->zInit[j++] = x; |
| @@ -654,15 +667,16 @@ | ||
| 654 | 667 | pRe->zInit[j++] = 0xc0 | (x>>6); |
| 655 | 668 | pRe->zInit[j++] = 0x80 | (x&0x3f); |
| 656 | 669 | }else if( x<=0xffff ){ |
| 657 | 670 | pRe->zInit[j++] = 0xd0 | (x>>12); |
| 658 | 671 | pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f); |
| 659 | - pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f); | |
| 672 | + pRe->zInit[j++] = 0x80 | (x&0x3f); | |
| 660 | 673 | }else{ |
| 661 | 674 | break; |
| 662 | 675 | } |
| 663 | 676 | } |
| 677 | + if( j>0 && pRe->zInit[j-1]==0 ) j--; | |
| 664 | 678 | pRe->nInit = j; |
| 665 | 679 | } |
| 666 | 680 | return pRe->zErr; |
| 667 | 681 | } |
| 668 | 682 | |
| @@ -700,11 +714,11 @@ | ||
| 700 | 714 | } |
| 701 | 715 | sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free); |
| 702 | 716 | } |
| 703 | 717 | zStr = (const unsigned char*)sqlite3_value_text(argv[1]); |
| 704 | 718 | if( zStr!=0 ){ |
| 705 | - sqlite3_result_int(context, re_execute(pRe, zStr, -1)); | |
| 719 | + sqlite3_result_int(context, re_match(pRe, zStr, -1)); | |
| 706 | 720 | } |
| 707 | 721 | } |
| 708 | 722 | |
| 709 | 723 | /* |
| 710 | 724 | ** Invoke this routine in order to install the REGEXP function in an |
| @@ -731,11 +745,11 @@ | ||
| 731 | 745 | char zLine[2000]; |
| 732 | 746 | while( fgets(zLine, sizeof(zLine), in) ){ |
| 733 | 747 | ln++; |
| 734 | 748 | n = (int)strlen(zLine); |
| 735 | 749 | while( n && (zLine[n-1]=='\n' || zLine[n-1]=='\r') ) n--; |
| 736 | - if( re_execute(pRe, (const unsigned char*)zLine, n) ){ | |
| 750 | + if( re_match(pRe, (const unsigned char*)zLine, n) ){ | |
| 737 | 751 | printf("%s:%d:%.*s\n", zFile, ln, n, zLine); |
| 738 | 752 | } |
| 739 | 753 | } |
| 740 | 754 | } |
| 741 | 755 | |
| 742 | 756 |
| --- src/regexp.c | |
| +++ src/regexp.c | |
| @@ -32,11 +32,11 @@ | |
| 32 | ** X$ X occurring at the end of the string |
| 33 | ** . Match any single character |
| 34 | ** \c Character c where c is one of \{}()[]|*+?. |
| 35 | ** \c C-language escapes for c in afnrtv. ex: \t or \n |
| 36 | ** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX |
| 37 | ** \xXXX Where XXX is any number of hex digits, unicode value XXX |
| 38 | ** [abc] Any single character from the set abc |
| 39 | ** [^abc] Any single character not in the set abc |
| 40 | ** [a-z] Any single character in the range a-z |
| 41 | ** [^a-z] Any single character not in the range a-z |
| 42 | ** \b Word boundary |
| @@ -134,25 +134,25 @@ | |
| 134 | */ |
| 135 | static unsigned re_next_char(ReInput *p){ |
| 136 | unsigned c; |
| 137 | if( p->i>=p->mx ) return 0; |
| 138 | c = p->z[p->i++]; |
| 139 | if( c>0x80 ){ |
| 140 | if( (c&0xe0)==0xc0 && p->i<p->mx && (p->z[p->i]&0xc0)==0x80 ){ |
| 141 | c = (c&0x1f)<<6 | (p->z[p->i++]&0x3f); |
| 142 | if( c<0x80 ) c = 0xfffd; |
| 143 | }else if( (c&0xf0)==0xe0 && p->i+1<p->mx && (p->z[p->i]&0xc0)==0x80 |
| 144 | && (p->z[p->i+1]&0xc0)==0x80 ){ |
| 145 | c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f); |
| 146 | p->i += 2; |
| 147 | if( c<0x3ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd; |
| 148 | }else if( (c&0xf8)==0xf0 && p->i+3<p->mx && (p->z[p->i]&0xc0)==0x80 |
| 149 | && (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){ |
| 150 | c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6) |
| 151 | | (p->z[p->i+2]&0x3f); |
| 152 | p->i += 3; |
| 153 | if( c<0xffff ) c = 0xfffd; |
| 154 | }else{ |
| 155 | c = 0xfffd; |
| 156 | } |
| 157 | } |
| 158 | return c; |
| @@ -172,17 +172,17 @@ | |
| 172 | return (c>='0' && c<='9'); |
| 173 | } |
| 174 | |
| 175 | /* Return true if c is a perl "space" character: [ \t\r\n\v\f] */ |
| 176 | static int re_space_char(int c){ |
| 177 | return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f' ; |
| 178 | } |
| 179 | |
| 180 | /* Run a compiled regular expression on the zero-terminated input |
| 181 | ** string zIn[]. Return true on a match and false if there is no match. |
| 182 | */ |
| 183 | int re_execute(ReCompiled *pRe, const unsigned char *zIn, int nIn){ |
| 184 | ReStateSet aStateSet[2], *pThis, *pNext; |
| 185 | ReStateNumber aSpace[100]; |
| 186 | ReStateNumber *pToFree; |
| 187 | unsigned int i = 0; |
| 188 | unsigned int iSwap = 0; |
| @@ -192,19 +192,22 @@ | |
| 192 | ReInput in; |
| 193 | |
| 194 | in.z = zIn; |
| 195 | in.i = 0; |
| 196 | in.mx = nIn>=0 ? nIn : strlen((char const*)zIn); |
| 197 | if( pRe->nInit ){ |
| 198 | unsigned char x = pRe->zInit[0]; |
| 199 | while( in.i+pRe->nInit<in.mx |
| 200 | && (zIn[in.i]!=x || memcmp(zIn+in.i, pRe->zInit, pRe->nInit)!=0) |
| 201 | ){ |
| 202 | in.i++; |
| 203 | } |
| 204 | if( in.i+pRe->nInit>=in.mx ) return 0; |
| 205 | } |
| 206 | if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){ |
| 207 | pToFree = 0; |
| 208 | aStateSet[0].aState = aSpace; |
| 209 | }else{ |
| 210 | pToFree = fossil_malloc( sizeof(ReStateNumber)*2*pRe->nState ); |
| @@ -275,11 +278,11 @@ | |
| 275 | re_add_state(pThis, x+pRe->aArg[x]); |
| 276 | break; |
| 277 | } |
| 278 | case RE_OP_ACCEPT: { |
| 279 | rc = 1; |
| 280 | goto re_execute_end; |
| 281 | } |
| 282 | case RE_OP_CC_INC: |
| 283 | case RE_OP_CC_EXC: { |
| 284 | int j = 1; |
| 285 | int n = pRe->aArg[x]; |
| @@ -307,11 +310,11 @@ | |
| 307 | } |
| 308 | } |
| 309 | for(i=0; i<pNext->nState; i++){ |
| 310 | if( pRe->aOp[pNext->aState[i]]==RE_OP_ACCEPT ){ rc = 1; break; } |
| 311 | } |
| 312 | re_execute_end: |
| 313 | fossil_free(pToFree); |
| 314 | return rc; |
| 315 | } |
| 316 | |
| 317 | /* Resize the opcode and argument arrays for an RE under construction. |
| @@ -378,21 +381,20 @@ | |
| 378 | *pV = (*pV)*16 + (c & 0xff); |
| 379 | return 1; |
| 380 | } |
| 381 | |
| 382 | /* A backslash character has been seen, read the next character and |
| 383 | ** return its intepretation. |
| 384 | */ |
| 385 | static unsigned re_esc_char(ReCompiled *p){ |
| 386 | static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]"; |
| 387 | static const char zTrans[] = "\a\f\n\r\t\v"; |
| 388 | int i, v = 0; |
| 389 | char c; |
| 390 | if( p->sIn.i>=p->sIn.mx ) return 0; |
| 391 | c = p->sIn.z[0]; |
| 392 | if( c=='u' && p->sIn.i+5<p->sIn.mx ){ |
| 393 | v = 0; |
| 394 | const unsigned char *zIn = p->sIn.z + p->sIn.i; |
| 395 | if( re_hex(zIn[1],&v) |
| 396 | && re_hex(zIn[2],&v) |
| 397 | && re_hex(zIn[3],&v) |
| 398 | && re_hex(zIn[4],&v) |
| @@ -399,15 +401,16 @@ | |
| 399 | ){ |
| 400 | p->sIn.i += 5; |
| 401 | return v; |
| 402 | } |
| 403 | } |
| 404 | if( c=='x' ){ |
| 405 | v = 0; |
| 406 | for(i=1; p->sIn.i<p->sIn.mx && re_hex(p->sIn.z[p->sIn.i+i], &v); i++){} |
| 407 | if( i>1 ){ |
| 408 | p->sIn.i += i; |
| 409 | return v; |
| 410 | } |
| 411 | } |
| 412 | for(i=0; zEsc[i] && zEsc[i]!=c; i++){} |
| 413 | if( zEsc[i] ){ |
| @@ -594,16 +597,17 @@ | |
| 594 | */ |
| 595 | void re_free(ReCompiled *pRe){ |
| 596 | if( pRe ){ |
| 597 | fossil_free(pRe->aOp); |
| 598 | fossil_free(pRe->aArg); |
| 599 | } |
| 600 | } |
| 601 | |
| 602 | /* |
| 603 | ** Compile a textual regular expression in zIn[] into a compiled regular |
| 604 | ** expression suitable for us by re_execute() and return a pointer to the |
| 605 | ** compiled regular expression in *ppRe. Return NULL on success or an |
| 606 | ** error message if something goes wrong. |
| 607 | */ |
| 608 | const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){ |
| 609 | ReCompiled *pRe; |
| @@ -632,11 +636,11 @@ | |
| 632 | zErr = re_subcompile_re(pRe); |
| 633 | if( zErr ){ |
| 634 | re_free(pRe); |
| 635 | return zErr; |
| 636 | } |
| 637 | if( rePeek(pRe)=='$' && pRe->sIn.i+1==pRe->sIn.mx ){ |
| 638 | re_append(pRe, RE_OP_MATCH, RE_EOF); |
| 639 | re_append(pRe, RE_OP_ACCEPT, 0); |
| 640 | *ppRe = pRe; |
| 641 | }else if( pRe->sIn.i>=pRe->sIn.mx ){ |
| 642 | re_append(pRe, RE_OP_ACCEPT, 0); |
| @@ -643,10 +647,19 @@ | |
| 643 | *ppRe = pRe; |
| 644 | }else{ |
| 645 | re_free(pRe); |
| 646 | return "unrecognized character"; |
| 647 | } |
| 648 | if( pRe->aOp[0]==RE_OP_ANYSTAR ){ |
| 649 | for(j=0, i=1; j<sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){ |
| 650 | unsigned x = pRe->aArg[i]; |
| 651 | if( x<=127 ){ |
| 652 | pRe->zInit[j++] = x; |
| @@ -654,15 +667,16 @@ | |
| 654 | pRe->zInit[j++] = 0xc0 | (x>>6); |
| 655 | pRe->zInit[j++] = 0x80 | (x&0x3f); |
| 656 | }else if( x<=0xffff ){ |
| 657 | pRe->zInit[j++] = 0xd0 | (x>>12); |
| 658 | pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f); |
| 659 | pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f); |
| 660 | }else{ |
| 661 | break; |
| 662 | } |
| 663 | } |
| 664 | pRe->nInit = j; |
| 665 | } |
| 666 | return pRe->zErr; |
| 667 | } |
| 668 | |
| @@ -700,11 +714,11 @@ | |
| 700 | } |
| 701 | sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free); |
| 702 | } |
| 703 | zStr = (const unsigned char*)sqlite3_value_text(argv[1]); |
| 704 | if( zStr!=0 ){ |
| 705 | sqlite3_result_int(context, re_execute(pRe, zStr, -1)); |
| 706 | } |
| 707 | } |
| 708 | |
| 709 | /* |
| 710 | ** Invoke this routine in order to install the REGEXP function in an |
| @@ -731,11 +745,11 @@ | |
| 731 | char zLine[2000]; |
| 732 | while( fgets(zLine, sizeof(zLine), in) ){ |
| 733 | ln++; |
| 734 | n = (int)strlen(zLine); |
| 735 | while( n && (zLine[n-1]=='\n' || zLine[n-1]=='\r') ) n--; |
| 736 | if( re_execute(pRe, (const unsigned char*)zLine, n) ){ |
| 737 | printf("%s:%d:%.*s\n", zFile, ln, n, zLine); |
| 738 | } |
| 739 | } |
| 740 | } |
| 741 | |
| 742 |
| --- src/regexp.c | |
| +++ src/regexp.c | |
| @@ -32,11 +32,11 @@ | |
| 32 | ** X$ X occurring at the end of the string |
| 33 | ** . Match any single character |
| 34 | ** \c Character c where c is one of \{}()[]|*+?. |
| 35 | ** \c C-language escapes for c in afnrtv. ex: \t or \n |
| 36 | ** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX |
| 37 | ** \xXX Where XX is exactly 2 hex digits, unicode value XX |
| 38 | ** [abc] Any single character from the set abc |
| 39 | ** [^abc] Any single character not in the set abc |
| 40 | ** [a-z] Any single character in the range a-z |
| 41 | ** [^a-z] Any single character not in the range a-z |
| 42 | ** \b Word boundary |
| @@ -134,25 +134,25 @@ | |
| 134 | */ |
| 135 | static unsigned re_next_char(ReInput *p){ |
| 136 | unsigned c; |
| 137 | if( p->i>=p->mx ) return 0; |
| 138 | c = p->z[p->i++]; |
| 139 | if( c>=0x80 ){ |
| 140 | if( (c&0xe0)==0xc0 && p->i<p->mx && (p->z[p->i]&0xc0)==0x80 ){ |
| 141 | c = (c&0x1f)<<6 | (p->z[p->i++]&0x3f); |
| 142 | if( c<0x80 ) c = 0xfffd; |
| 143 | }else if( (c&0xf0)==0xe0 && p->i+1<p->mx && (p->z[p->i]&0xc0)==0x80 |
| 144 | && (p->z[p->i+1]&0xc0)==0x80 ){ |
| 145 | c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f); |
| 146 | p->i += 2; |
| 147 | if( c<=0x3ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd; |
| 148 | }else if( (c&0xf8)==0xf0 && p->i+3<p->mx && (p->z[p->i]&0xc0)==0x80 |
| 149 | && (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){ |
| 150 | c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6) |
| 151 | | (p->z[p->i+2]&0x3f); |
| 152 | p->i += 3; |
| 153 | if( c<=0xffff || c>0x10ffff ) c = 0xfffd; |
| 154 | }else{ |
| 155 | c = 0xfffd; |
| 156 | } |
| 157 | } |
| 158 | return c; |
| @@ -172,17 +172,17 @@ | |
| 172 | return (c>='0' && c<='9'); |
| 173 | } |
| 174 | |
| 175 | /* Return true if c is a perl "space" character: [ \t\r\n\v\f] */ |
| 176 | static int re_space_char(int c){ |
| 177 | return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f'; |
| 178 | } |
| 179 | |
| 180 | /* Run a compiled regular expression on the zero-terminated input |
| 181 | ** string zIn[]. Return true on a match and false if there is no match. |
| 182 | */ |
| 183 | int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){ |
| 184 | ReStateSet aStateSet[2], *pThis, *pNext; |
| 185 | ReStateNumber aSpace[100]; |
| 186 | ReStateNumber *pToFree; |
| 187 | unsigned int i = 0; |
| 188 | unsigned int iSwap = 0; |
| @@ -192,19 +192,22 @@ | |
| 192 | ReInput in; |
| 193 | |
| 194 | in.z = zIn; |
| 195 | in.i = 0; |
| 196 | in.mx = nIn>=0 ? nIn : strlen((char const*)zIn); |
| 197 | |
| 198 | /* Look for the initial prefix match, if there is one. */ |
| 199 | if( pRe->nInit ){ |
| 200 | unsigned char x = pRe->zInit[0]; |
| 201 | while( in.i+pRe->nInit<=in.mx |
| 202 | && (zIn[in.i]!=x || memcmp(zIn+in.i, pRe->zInit, pRe->nInit)!=0) |
| 203 | ){ |
| 204 | in.i++; |
| 205 | } |
| 206 | if( in.i+pRe->nInit>in.mx ) return 0; |
| 207 | } |
| 208 | |
| 209 | if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){ |
| 210 | pToFree = 0; |
| 211 | aStateSet[0].aState = aSpace; |
| 212 | }else{ |
| 213 | pToFree = fossil_malloc( sizeof(ReStateNumber)*2*pRe->nState ); |
| @@ -275,11 +278,11 @@ | |
| 278 | re_add_state(pThis, x+pRe->aArg[x]); |
| 279 | break; |
| 280 | } |
| 281 | case RE_OP_ACCEPT: { |
| 282 | rc = 1; |
| 283 | goto re_match_end; |
| 284 | } |
| 285 | case RE_OP_CC_INC: |
| 286 | case RE_OP_CC_EXC: { |
| 287 | int j = 1; |
| 288 | int n = pRe->aArg[x]; |
| @@ -307,11 +310,11 @@ | |
| 310 | } |
| 311 | } |
| 312 | for(i=0; i<pNext->nState; i++){ |
| 313 | if( pRe->aOp[pNext->aState[i]]==RE_OP_ACCEPT ){ rc = 1; break; } |
| 314 | } |
| 315 | re_match_end: |
| 316 | fossil_free(pToFree); |
| 317 | return rc; |
| 318 | } |
| 319 | |
| 320 | /* Resize the opcode and argument arrays for an RE under construction. |
| @@ -378,21 +381,20 @@ | |
| 381 | *pV = (*pV)*16 + (c & 0xff); |
| 382 | return 1; |
| 383 | } |
| 384 | |
| 385 | /* A backslash character has been seen, read the next character and |
| 386 | ** return its interpretation. |
| 387 | */ |
| 388 | static unsigned re_esc_char(ReCompiled *p){ |
| 389 | static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]"; |
| 390 | static const char zTrans[] = "\a\f\n\r\t\v"; |
| 391 | int i, v = 0; |
| 392 | char c; |
| 393 | if( p->sIn.i>=p->sIn.mx ) return 0; |
| 394 | c = p->sIn.z[p->sIn.i]; |
| 395 | if( c=='u' && p->sIn.i+4<p->sIn.mx ){ |
| 396 | const unsigned char *zIn = p->sIn.z + p->sIn.i; |
| 397 | if( re_hex(zIn[1],&v) |
| 398 | && re_hex(zIn[2],&v) |
| 399 | && re_hex(zIn[3],&v) |
| 400 | && re_hex(zIn[4],&v) |
| @@ -399,15 +401,16 @@ | |
| 401 | ){ |
| 402 | p->sIn.i += 5; |
| 403 | return v; |
| 404 | } |
| 405 | } |
| 406 | if( c=='x' && p->sIn.i+2<p->sIn.mx ){ |
| 407 | const unsigned char *zIn = p->sIn.z + p->sIn.i; |
| 408 | if( re_hex(zIn[1],&v) |
| 409 | && re_hex(zIn[2],&v) |
| 410 | ){ |
| 411 | p->sIn.i += 3; |
| 412 | return v; |
| 413 | } |
| 414 | } |
| 415 | for(i=0; zEsc[i] && zEsc[i]!=c; i++){} |
| 416 | if( zEsc[i] ){ |
| @@ -594,16 +597,17 @@ | |
| 597 | */ |
| 598 | void re_free(ReCompiled *pRe){ |
| 599 | if( pRe ){ |
| 600 | fossil_free(pRe->aOp); |
| 601 | fossil_free(pRe->aArg); |
| 602 | fossil_free(pRe); |
| 603 | } |
| 604 | } |
| 605 | |
| 606 | /* |
| 607 | ** Compile a textual regular expression in zIn[] into a compiled regular |
| 608 | ** expression suitable for us by re_match() and return a pointer to the |
| 609 | ** compiled regular expression in *ppRe. Return NULL on success or an |
| 610 | ** error message if something goes wrong. |
| 611 | */ |
| 612 | const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){ |
| 613 | ReCompiled *pRe; |
| @@ -632,11 +636,11 @@ | |
| 636 | zErr = re_subcompile_re(pRe); |
| 637 | if( zErr ){ |
| 638 | re_free(pRe); |
| 639 | return zErr; |
| 640 | } |
| 641 | if( rePeek(pRe)=='$' && pRe->sIn.i+1>=pRe->sIn.mx ){ |
| 642 | re_append(pRe, RE_OP_MATCH, RE_EOF); |
| 643 | re_append(pRe, RE_OP_ACCEPT, 0); |
| 644 | *ppRe = pRe; |
| 645 | }else if( pRe->sIn.i>=pRe->sIn.mx ){ |
| 646 | re_append(pRe, RE_OP_ACCEPT, 0); |
| @@ -643,10 +647,19 @@ | |
| 647 | *ppRe = pRe; |
| 648 | }else{ |
| 649 | re_free(pRe); |
| 650 | return "unrecognized character"; |
| 651 | } |
| 652 | |
| 653 | /* The following is a performance optimization. If the regex begins with |
| 654 | ** ".*" (if the input regex lacks an initial "^") and afterwards there are |
| 655 | ** one or more matching characters, enter those matching characters into |
| 656 | ** zInit[]. The re_match() routine can then search ahead in the input |
| 657 | ** string looking for the initial match without having to run the whole |
| 658 | ** regex engine over the string. Do not worry able trying to match |
| 659 | ** unicode characters beyond plane 0 - those are very rare and this is |
| 660 | ** just an optimization. */ |
| 661 | if( pRe->aOp[0]==RE_OP_ANYSTAR ){ |
| 662 | for(j=0, i=1; j<sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){ |
| 663 | unsigned x = pRe->aArg[i]; |
| 664 | if( x<=127 ){ |
| 665 | pRe->zInit[j++] = x; |
| @@ -654,15 +667,16 @@ | |
| 667 | pRe->zInit[j++] = 0xc0 | (x>>6); |
| 668 | pRe->zInit[j++] = 0x80 | (x&0x3f); |
| 669 | }else if( x<=0xffff ){ |
| 670 | pRe->zInit[j++] = 0xd0 | (x>>12); |
| 671 | pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f); |
| 672 | pRe->zInit[j++] = 0x80 | (x&0x3f); |
| 673 | }else{ |
| 674 | break; |
| 675 | } |
| 676 | } |
| 677 | if( j>0 && pRe->zInit[j-1]==0 ) j--; |
| 678 | pRe->nInit = j; |
| 679 | } |
| 680 | return pRe->zErr; |
| 681 | } |
| 682 | |
| @@ -700,11 +714,11 @@ | |
| 714 | } |
| 715 | sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free); |
| 716 | } |
| 717 | zStr = (const unsigned char*)sqlite3_value_text(argv[1]); |
| 718 | if( zStr!=0 ){ |
| 719 | sqlite3_result_int(context, re_match(pRe, zStr, -1)); |
| 720 | } |
| 721 | } |
| 722 | |
| 723 | /* |
| 724 | ** Invoke this routine in order to install the REGEXP function in an |
| @@ -731,11 +745,11 @@ | |
| 745 | char zLine[2000]; |
| 746 | while( fgets(zLine, sizeof(zLine), in) ){ |
| 747 | ln++; |
| 748 | n = (int)strlen(zLine); |
| 749 | while( n && (zLine[n-1]=='\n' || zLine[n-1]=='\r') ) n--; |
| 750 | if( re_match(pRe, (const unsigned char*)zLine, n) ){ |
| 751 | printf("%s:%d:%.*s\n", zFile, ln, n, zLine); |
| 752 | } |
| 753 | } |
| 754 | } |
| 755 | |
| 756 |
+38
-24
| --- src/regexp.c | ||
| +++ src/regexp.c | ||
| @@ -32,11 +32,11 @@ | ||
| 32 | 32 | ** X$ X occurring at the end of the string |
| 33 | 33 | ** . Match any single character |
| 34 | 34 | ** \c Character c where c is one of \{}()[]|*+?. |
| 35 | 35 | ** \c C-language escapes for c in afnrtv. ex: \t or \n |
| 36 | 36 | ** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX |
| 37 | -** \xXXX Where XXX is any number of hex digits, unicode value XXX | |
| 37 | +** \xXX Where XX is exactly 2 hex digits, unicode value XX | |
| 38 | 38 | ** [abc] Any single character from the set abc |
| 39 | 39 | ** [^abc] Any single character not in the set abc |
| 40 | 40 | ** [a-z] Any single character in the range a-z |
| 41 | 41 | ** [^a-z] Any single character not in the range a-z |
| 42 | 42 | ** \b Word boundary |
| @@ -134,25 +134,25 @@ | ||
| 134 | 134 | */ |
| 135 | 135 | static unsigned re_next_char(ReInput *p){ |
| 136 | 136 | unsigned c; |
| 137 | 137 | if( p->i>=p->mx ) return 0; |
| 138 | 138 | c = p->z[p->i++]; |
| 139 | - if( c>0x80 ){ | |
| 139 | + if( c>=0x80 ){ | |
| 140 | 140 | if( (c&0xe0)==0xc0 && p->i<p->mx && (p->z[p->i]&0xc0)==0x80 ){ |
| 141 | 141 | c = (c&0x1f)<<6 | (p->z[p->i++]&0x3f); |
| 142 | 142 | if( c<0x80 ) c = 0xfffd; |
| 143 | 143 | }else if( (c&0xf0)==0xe0 && p->i+1<p->mx && (p->z[p->i]&0xc0)==0x80 |
| 144 | 144 | && (p->z[p->i+1]&0xc0)==0x80 ){ |
| 145 | 145 | c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f); |
| 146 | 146 | p->i += 2; |
| 147 | - if( c<0x3ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd; | |
| 147 | + if( c<=0x3ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd; | |
| 148 | 148 | }else if( (c&0xf8)==0xf0 && p->i+3<p->mx && (p->z[p->i]&0xc0)==0x80 |
| 149 | 149 | && (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){ |
| 150 | 150 | c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6) |
| 151 | 151 | | (p->z[p->i+2]&0x3f); |
| 152 | 152 | p->i += 3; |
| 153 | - if( c<0xffff ) c = 0xfffd; | |
| 153 | + if( c<=0xffff || c>0x10ffff ) c = 0xfffd; | |
| 154 | 154 | }else{ |
| 155 | 155 | c = 0xfffd; |
| 156 | 156 | } |
| 157 | 157 | } |
| 158 | 158 | return c; |
| @@ -172,17 +172,17 @@ | ||
| 172 | 172 | return (c>='0' && c<='9'); |
| 173 | 173 | } |
| 174 | 174 | |
| 175 | 175 | /* Return true if c is a perl "space" character: [ \t\r\n\v\f] */ |
| 176 | 176 | static int re_space_char(int c){ |
| 177 | - return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f' ; | |
| 177 | + return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f'; | |
| 178 | 178 | } |
| 179 | 179 | |
| 180 | 180 | /* Run a compiled regular expression on the zero-terminated input |
| 181 | 181 | ** string zIn[]. Return true on a match and false if there is no match. |
| 182 | 182 | */ |
| 183 | -int re_execute(ReCompiled *pRe, const unsigned char *zIn, int nIn){ | |
| 183 | +int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){ | |
| 184 | 184 | ReStateSet aStateSet[2], *pThis, *pNext; |
| 185 | 185 | ReStateNumber aSpace[100]; |
| 186 | 186 | ReStateNumber *pToFree; |
| 187 | 187 | unsigned int i = 0; |
| 188 | 188 | unsigned int iSwap = 0; |
| @@ -192,19 +192,22 @@ | ||
| 192 | 192 | ReInput in; |
| 193 | 193 | |
| 194 | 194 | in.z = zIn; |
| 195 | 195 | in.i = 0; |
| 196 | 196 | in.mx = nIn>=0 ? nIn : strlen((char const*)zIn); |
| 197 | + | |
| 198 | + /* Look for the initial prefix match, if there is one. */ | |
| 197 | 199 | if( pRe->nInit ){ |
| 198 | 200 | unsigned char x = pRe->zInit[0]; |
| 199 | - while( in.i+pRe->nInit<in.mx | |
| 201 | + while( in.i+pRe->nInit<=in.mx | |
| 200 | 202 | && (zIn[in.i]!=x || memcmp(zIn+in.i, pRe->zInit, pRe->nInit)!=0) |
| 201 | 203 | ){ |
| 202 | 204 | in.i++; |
| 203 | 205 | } |
| 204 | - if( in.i+pRe->nInit>=in.mx ) return 0; | |
| 206 | + if( in.i+pRe->nInit>in.mx ) return 0; | |
| 205 | 207 | } |
| 208 | + | |
| 206 | 209 | if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){ |
| 207 | 210 | pToFree = 0; |
| 208 | 211 | aStateSet[0].aState = aSpace; |
| 209 | 212 | }else{ |
| 210 | 213 | pToFree = fossil_malloc( sizeof(ReStateNumber)*2*pRe->nState ); |
| @@ -275,11 +278,11 @@ | ||
| 275 | 278 | re_add_state(pThis, x+pRe->aArg[x]); |
| 276 | 279 | break; |
| 277 | 280 | } |
| 278 | 281 | case RE_OP_ACCEPT: { |
| 279 | 282 | rc = 1; |
| 280 | - goto re_execute_end; | |
| 283 | + goto re_match_end; | |
| 281 | 284 | } |
| 282 | 285 | case RE_OP_CC_INC: |
| 283 | 286 | case RE_OP_CC_EXC: { |
| 284 | 287 | int j = 1; |
| 285 | 288 | int n = pRe->aArg[x]; |
| @@ -307,11 +310,11 @@ | ||
| 307 | 310 | } |
| 308 | 311 | } |
| 309 | 312 | for(i=0; i<pNext->nState; i++){ |
| 310 | 313 | if( pRe->aOp[pNext->aState[i]]==RE_OP_ACCEPT ){ rc = 1; break; } |
| 311 | 314 | } |
| 312 | -re_execute_end: | |
| 315 | +re_match_end: | |
| 313 | 316 | fossil_free(pToFree); |
| 314 | 317 | return rc; |
| 315 | 318 | } |
| 316 | 319 | |
| 317 | 320 | /* Resize the opcode and argument arrays for an RE under construction. |
| @@ -378,21 +381,20 @@ | ||
| 378 | 381 | *pV = (*pV)*16 + (c & 0xff); |
| 379 | 382 | return 1; |
| 380 | 383 | } |
| 381 | 384 | |
| 382 | 385 | /* A backslash character has been seen, read the next character and |
| 383 | -** return its intepretation. | |
| 386 | +** return its interpretation. | |
| 384 | 387 | */ |
| 385 | 388 | static unsigned re_esc_char(ReCompiled *p){ |
| 386 | 389 | static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]"; |
| 387 | 390 | static const char zTrans[] = "\a\f\n\r\t\v"; |
| 388 | 391 | int i, v = 0; |
| 389 | 392 | char c; |
| 390 | 393 | if( p->sIn.i>=p->sIn.mx ) return 0; |
| 391 | - c = p->sIn.z[0]; | |
| 392 | - if( c=='u' && p->sIn.i+5<p->sIn.mx ){ | |
| 393 | - v = 0; | |
| 394 | + c = p->sIn.z[p->sIn.i]; | |
| 395 | + if( c=='u' && p->sIn.i+4<p->sIn.mx ){ | |
| 394 | 396 | const unsigned char *zIn = p->sIn.z + p->sIn.i; |
| 395 | 397 | if( re_hex(zIn[1],&v) |
| 396 | 398 | && re_hex(zIn[2],&v) |
| 397 | 399 | && re_hex(zIn[3],&v) |
| 398 | 400 | && re_hex(zIn[4],&v) |
| @@ -399,15 +401,16 @@ | ||
| 399 | 401 | ){ |
| 400 | 402 | p->sIn.i += 5; |
| 401 | 403 | return v; |
| 402 | 404 | } |
| 403 | 405 | } |
| 404 | - if( c=='x' ){ | |
| 405 | - v = 0; | |
| 406 | - for(i=1; p->sIn.i<p->sIn.mx && re_hex(p->sIn.z[p->sIn.i+i], &v); i++){} | |
| 407 | - if( i>1 ){ | |
| 408 | - p->sIn.i += i; | |
| 406 | + if( c=='x' && p->sIn.i+2<p->sIn.mx ){ | |
| 407 | + const unsigned char *zIn = p->sIn.z + p->sIn.i; | |
| 408 | + if( re_hex(zIn[1],&v) | |
| 409 | + && re_hex(zIn[2],&v) | |
| 410 | + ){ | |
| 411 | + p->sIn.i += 3; | |
| 409 | 412 | return v; |
| 410 | 413 | } |
| 411 | 414 | } |
| 412 | 415 | for(i=0; zEsc[i] && zEsc[i]!=c; i++){} |
| 413 | 416 | if( zEsc[i] ){ |
| @@ -594,16 +597,17 @@ | ||
| 594 | 597 | */ |
| 595 | 598 | void re_free(ReCompiled *pRe){ |
| 596 | 599 | if( pRe ){ |
| 597 | 600 | fossil_free(pRe->aOp); |
| 598 | 601 | fossil_free(pRe->aArg); |
| 602 | + fossil_free(pRe); | |
| 599 | 603 | } |
| 600 | 604 | } |
| 601 | 605 | |
| 602 | 606 | /* |
| 603 | 607 | ** Compile a textual regular expression in zIn[] into a compiled regular |
| 604 | -** expression suitable for us by re_execute() and return a pointer to the | |
| 608 | +** expression suitable for us by re_match() and return a pointer to the | |
| 605 | 609 | ** compiled regular expression in *ppRe. Return NULL on success or an |
| 606 | 610 | ** error message if something goes wrong. |
| 607 | 611 | */ |
| 608 | 612 | const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){ |
| 609 | 613 | ReCompiled *pRe; |
| @@ -632,11 +636,11 @@ | ||
| 632 | 636 | zErr = re_subcompile_re(pRe); |
| 633 | 637 | if( zErr ){ |
| 634 | 638 | re_free(pRe); |
| 635 | 639 | return zErr; |
| 636 | 640 | } |
| 637 | - if( rePeek(pRe)=='$' && pRe->sIn.i+1==pRe->sIn.mx ){ | |
| 641 | + if( rePeek(pRe)=='$' && pRe->sIn.i+1>=pRe->sIn.mx ){ | |
| 638 | 642 | re_append(pRe, RE_OP_MATCH, RE_EOF); |
| 639 | 643 | re_append(pRe, RE_OP_ACCEPT, 0); |
| 640 | 644 | *ppRe = pRe; |
| 641 | 645 | }else if( pRe->sIn.i>=pRe->sIn.mx ){ |
| 642 | 646 | re_append(pRe, RE_OP_ACCEPT, 0); |
| @@ -643,10 +647,19 @@ | ||
| 643 | 647 | *ppRe = pRe; |
| 644 | 648 | }else{ |
| 645 | 649 | re_free(pRe); |
| 646 | 650 | return "unrecognized character"; |
| 647 | 651 | } |
| 652 | + | |
| 653 | + /* The following is a performance optimization. If the regex begins with | |
| 654 | + ** ".*" (if the input regex lacks an initial "^") and afterwards there are | |
| 655 | + ** one or more matching characters, enter those matching characters into | |
| 656 | + ** zInit[]. The re_match() routine can then search ahead in the input | |
| 657 | + ** string looking for the initial match without having to run the whole | |
| 658 | + ** regex engine over the string. Do not worry able trying to match | |
| 659 | + ** unicode characters beyond plane 0 - those are very rare and this is | |
| 660 | + ** just an optimization. */ | |
| 648 | 661 | if( pRe->aOp[0]==RE_OP_ANYSTAR ){ |
| 649 | 662 | for(j=0, i=1; j<sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){ |
| 650 | 663 | unsigned x = pRe->aArg[i]; |
| 651 | 664 | if( x<=127 ){ |
| 652 | 665 | pRe->zInit[j++] = x; |
| @@ -654,15 +667,16 @@ | ||
| 654 | 667 | pRe->zInit[j++] = 0xc0 | (x>>6); |
| 655 | 668 | pRe->zInit[j++] = 0x80 | (x&0x3f); |
| 656 | 669 | }else if( x<=0xffff ){ |
| 657 | 670 | pRe->zInit[j++] = 0xd0 | (x>>12); |
| 658 | 671 | pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f); |
| 659 | - pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f); | |
| 672 | + pRe->zInit[j++] = 0x80 | (x&0x3f); | |
| 660 | 673 | }else{ |
| 661 | 674 | break; |
| 662 | 675 | } |
| 663 | 676 | } |
| 677 | + if( j>0 && pRe->zInit[j-1]==0 ) j--; | |
| 664 | 678 | pRe->nInit = j; |
| 665 | 679 | } |
| 666 | 680 | return pRe->zErr; |
| 667 | 681 | } |
| 668 | 682 | |
| @@ -700,11 +714,11 @@ | ||
| 700 | 714 | } |
| 701 | 715 | sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free); |
| 702 | 716 | } |
| 703 | 717 | zStr = (const unsigned char*)sqlite3_value_text(argv[1]); |
| 704 | 718 | if( zStr!=0 ){ |
| 705 | - sqlite3_result_int(context, re_execute(pRe, zStr, -1)); | |
| 719 | + sqlite3_result_int(context, re_match(pRe, zStr, -1)); | |
| 706 | 720 | } |
| 707 | 721 | } |
| 708 | 722 | |
| 709 | 723 | /* |
| 710 | 724 | ** Invoke this routine in order to install the REGEXP function in an |
| @@ -731,11 +745,11 @@ | ||
| 731 | 745 | char zLine[2000]; |
| 732 | 746 | while( fgets(zLine, sizeof(zLine), in) ){ |
| 733 | 747 | ln++; |
| 734 | 748 | n = (int)strlen(zLine); |
| 735 | 749 | while( n && (zLine[n-1]=='\n' || zLine[n-1]=='\r') ) n--; |
| 736 | - if( re_execute(pRe, (const unsigned char*)zLine, n) ){ | |
| 750 | + if( re_match(pRe, (const unsigned char*)zLine, n) ){ | |
| 737 | 751 | printf("%s:%d:%.*s\n", zFile, ln, n, zLine); |
| 738 | 752 | } |
| 739 | 753 | } |
| 740 | 754 | } |
| 741 | 755 | |
| 742 | 756 |
| --- src/regexp.c | |
| +++ src/regexp.c | |
| @@ -32,11 +32,11 @@ | |
| 32 | ** X$ X occurring at the end of the string |
| 33 | ** . Match any single character |
| 34 | ** \c Character c where c is one of \{}()[]|*+?. |
| 35 | ** \c C-language escapes for c in afnrtv. ex: \t or \n |
| 36 | ** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX |
| 37 | ** \xXXX Where XXX is any number of hex digits, unicode value XXX |
| 38 | ** [abc] Any single character from the set abc |
| 39 | ** [^abc] Any single character not in the set abc |
| 40 | ** [a-z] Any single character in the range a-z |
| 41 | ** [^a-z] Any single character not in the range a-z |
| 42 | ** \b Word boundary |
| @@ -134,25 +134,25 @@ | |
| 134 | */ |
| 135 | static unsigned re_next_char(ReInput *p){ |
| 136 | unsigned c; |
| 137 | if( p->i>=p->mx ) return 0; |
| 138 | c = p->z[p->i++]; |
| 139 | if( c>0x80 ){ |
| 140 | if( (c&0xe0)==0xc0 && p->i<p->mx && (p->z[p->i]&0xc0)==0x80 ){ |
| 141 | c = (c&0x1f)<<6 | (p->z[p->i++]&0x3f); |
| 142 | if( c<0x80 ) c = 0xfffd; |
| 143 | }else if( (c&0xf0)==0xe0 && p->i+1<p->mx && (p->z[p->i]&0xc0)==0x80 |
| 144 | && (p->z[p->i+1]&0xc0)==0x80 ){ |
| 145 | c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f); |
| 146 | p->i += 2; |
| 147 | if( c<0x3ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd; |
| 148 | }else if( (c&0xf8)==0xf0 && p->i+3<p->mx && (p->z[p->i]&0xc0)==0x80 |
| 149 | && (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){ |
| 150 | c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6) |
| 151 | | (p->z[p->i+2]&0x3f); |
| 152 | p->i += 3; |
| 153 | if( c<0xffff ) c = 0xfffd; |
| 154 | }else{ |
| 155 | c = 0xfffd; |
| 156 | } |
| 157 | } |
| 158 | return c; |
| @@ -172,17 +172,17 @@ | |
| 172 | return (c>='0' && c<='9'); |
| 173 | } |
| 174 | |
| 175 | /* Return true if c is a perl "space" character: [ \t\r\n\v\f] */ |
| 176 | static int re_space_char(int c){ |
| 177 | return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f' ; |
| 178 | } |
| 179 | |
| 180 | /* Run a compiled regular expression on the zero-terminated input |
| 181 | ** string zIn[]. Return true on a match and false if there is no match. |
| 182 | */ |
| 183 | int re_execute(ReCompiled *pRe, const unsigned char *zIn, int nIn){ |
| 184 | ReStateSet aStateSet[2], *pThis, *pNext; |
| 185 | ReStateNumber aSpace[100]; |
| 186 | ReStateNumber *pToFree; |
| 187 | unsigned int i = 0; |
| 188 | unsigned int iSwap = 0; |
| @@ -192,19 +192,22 @@ | |
| 192 | ReInput in; |
| 193 | |
| 194 | in.z = zIn; |
| 195 | in.i = 0; |
| 196 | in.mx = nIn>=0 ? nIn : strlen((char const*)zIn); |
| 197 | if( pRe->nInit ){ |
| 198 | unsigned char x = pRe->zInit[0]; |
| 199 | while( in.i+pRe->nInit<in.mx |
| 200 | && (zIn[in.i]!=x || memcmp(zIn+in.i, pRe->zInit, pRe->nInit)!=0) |
| 201 | ){ |
| 202 | in.i++; |
| 203 | } |
| 204 | if( in.i+pRe->nInit>=in.mx ) return 0; |
| 205 | } |
| 206 | if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){ |
| 207 | pToFree = 0; |
| 208 | aStateSet[0].aState = aSpace; |
| 209 | }else{ |
| 210 | pToFree = fossil_malloc( sizeof(ReStateNumber)*2*pRe->nState ); |
| @@ -275,11 +278,11 @@ | |
| 275 | re_add_state(pThis, x+pRe->aArg[x]); |
| 276 | break; |
| 277 | } |
| 278 | case RE_OP_ACCEPT: { |
| 279 | rc = 1; |
| 280 | goto re_execute_end; |
| 281 | } |
| 282 | case RE_OP_CC_INC: |
| 283 | case RE_OP_CC_EXC: { |
| 284 | int j = 1; |
| 285 | int n = pRe->aArg[x]; |
| @@ -307,11 +310,11 @@ | |
| 307 | } |
| 308 | } |
| 309 | for(i=0; i<pNext->nState; i++){ |
| 310 | if( pRe->aOp[pNext->aState[i]]==RE_OP_ACCEPT ){ rc = 1; break; } |
| 311 | } |
| 312 | re_execute_end: |
| 313 | fossil_free(pToFree); |
| 314 | return rc; |
| 315 | } |
| 316 | |
| 317 | /* Resize the opcode and argument arrays for an RE under construction. |
| @@ -378,21 +381,20 @@ | |
| 378 | *pV = (*pV)*16 + (c & 0xff); |
| 379 | return 1; |
| 380 | } |
| 381 | |
| 382 | /* A backslash character has been seen, read the next character and |
| 383 | ** return its intepretation. |
| 384 | */ |
| 385 | static unsigned re_esc_char(ReCompiled *p){ |
| 386 | static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]"; |
| 387 | static const char zTrans[] = "\a\f\n\r\t\v"; |
| 388 | int i, v = 0; |
| 389 | char c; |
| 390 | if( p->sIn.i>=p->sIn.mx ) return 0; |
| 391 | c = p->sIn.z[0]; |
| 392 | if( c=='u' && p->sIn.i+5<p->sIn.mx ){ |
| 393 | v = 0; |
| 394 | const unsigned char *zIn = p->sIn.z + p->sIn.i; |
| 395 | if( re_hex(zIn[1],&v) |
| 396 | && re_hex(zIn[2],&v) |
| 397 | && re_hex(zIn[3],&v) |
| 398 | && re_hex(zIn[4],&v) |
| @@ -399,15 +401,16 @@ | |
| 399 | ){ |
| 400 | p->sIn.i += 5; |
| 401 | return v; |
| 402 | } |
| 403 | } |
| 404 | if( c=='x' ){ |
| 405 | v = 0; |
| 406 | for(i=1; p->sIn.i<p->sIn.mx && re_hex(p->sIn.z[p->sIn.i+i], &v); i++){} |
| 407 | if( i>1 ){ |
| 408 | p->sIn.i += i; |
| 409 | return v; |
| 410 | } |
| 411 | } |
| 412 | for(i=0; zEsc[i] && zEsc[i]!=c; i++){} |
| 413 | if( zEsc[i] ){ |
| @@ -594,16 +597,17 @@ | |
| 594 | */ |
| 595 | void re_free(ReCompiled *pRe){ |
| 596 | if( pRe ){ |
| 597 | fossil_free(pRe->aOp); |
| 598 | fossil_free(pRe->aArg); |
| 599 | } |
| 600 | } |
| 601 | |
| 602 | /* |
| 603 | ** Compile a textual regular expression in zIn[] into a compiled regular |
| 604 | ** expression suitable for us by re_execute() and return a pointer to the |
| 605 | ** compiled regular expression in *ppRe. Return NULL on success or an |
| 606 | ** error message if something goes wrong. |
| 607 | */ |
| 608 | const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){ |
| 609 | ReCompiled *pRe; |
| @@ -632,11 +636,11 @@ | |
| 632 | zErr = re_subcompile_re(pRe); |
| 633 | if( zErr ){ |
| 634 | re_free(pRe); |
| 635 | return zErr; |
| 636 | } |
| 637 | if( rePeek(pRe)=='$' && pRe->sIn.i+1==pRe->sIn.mx ){ |
| 638 | re_append(pRe, RE_OP_MATCH, RE_EOF); |
| 639 | re_append(pRe, RE_OP_ACCEPT, 0); |
| 640 | *ppRe = pRe; |
| 641 | }else if( pRe->sIn.i>=pRe->sIn.mx ){ |
| 642 | re_append(pRe, RE_OP_ACCEPT, 0); |
| @@ -643,10 +647,19 @@ | |
| 643 | *ppRe = pRe; |
| 644 | }else{ |
| 645 | re_free(pRe); |
| 646 | return "unrecognized character"; |
| 647 | } |
| 648 | if( pRe->aOp[0]==RE_OP_ANYSTAR ){ |
| 649 | for(j=0, i=1; j<sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){ |
| 650 | unsigned x = pRe->aArg[i]; |
| 651 | if( x<=127 ){ |
| 652 | pRe->zInit[j++] = x; |
| @@ -654,15 +667,16 @@ | |
| 654 | pRe->zInit[j++] = 0xc0 | (x>>6); |
| 655 | pRe->zInit[j++] = 0x80 | (x&0x3f); |
| 656 | }else if( x<=0xffff ){ |
| 657 | pRe->zInit[j++] = 0xd0 | (x>>12); |
| 658 | pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f); |
| 659 | pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f); |
| 660 | }else{ |
| 661 | break; |
| 662 | } |
| 663 | } |
| 664 | pRe->nInit = j; |
| 665 | } |
| 666 | return pRe->zErr; |
| 667 | } |
| 668 | |
| @@ -700,11 +714,11 @@ | |
| 700 | } |
| 701 | sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free); |
| 702 | } |
| 703 | zStr = (const unsigned char*)sqlite3_value_text(argv[1]); |
| 704 | if( zStr!=0 ){ |
| 705 | sqlite3_result_int(context, re_execute(pRe, zStr, -1)); |
| 706 | } |
| 707 | } |
| 708 | |
| 709 | /* |
| 710 | ** Invoke this routine in order to install the REGEXP function in an |
| @@ -731,11 +745,11 @@ | |
| 731 | char zLine[2000]; |
| 732 | while( fgets(zLine, sizeof(zLine), in) ){ |
| 733 | ln++; |
| 734 | n = (int)strlen(zLine); |
| 735 | while( n && (zLine[n-1]=='\n' || zLine[n-1]=='\r') ) n--; |
| 736 | if( re_execute(pRe, (const unsigned char*)zLine, n) ){ |
| 737 | printf("%s:%d:%.*s\n", zFile, ln, n, zLine); |
| 738 | } |
| 739 | } |
| 740 | } |
| 741 | |
| 742 |
| --- src/regexp.c | |
| +++ src/regexp.c | |
| @@ -32,11 +32,11 @@ | |
| 32 | ** X$ X occurring at the end of the string |
| 33 | ** . Match any single character |
| 34 | ** \c Character c where c is one of \{}()[]|*+?. |
| 35 | ** \c C-language escapes for c in afnrtv. ex: \t or \n |
| 36 | ** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX |
| 37 | ** \xXX Where XX is exactly 2 hex digits, unicode value XX |
| 38 | ** [abc] Any single character from the set abc |
| 39 | ** [^abc] Any single character not in the set abc |
| 40 | ** [a-z] Any single character in the range a-z |
| 41 | ** [^a-z] Any single character not in the range a-z |
| 42 | ** \b Word boundary |
| @@ -134,25 +134,25 @@ | |
| 134 | */ |
| 135 | static unsigned re_next_char(ReInput *p){ |
| 136 | unsigned c; |
| 137 | if( p->i>=p->mx ) return 0; |
| 138 | c = p->z[p->i++]; |
| 139 | if( c>=0x80 ){ |
| 140 | if( (c&0xe0)==0xc0 && p->i<p->mx && (p->z[p->i]&0xc0)==0x80 ){ |
| 141 | c = (c&0x1f)<<6 | (p->z[p->i++]&0x3f); |
| 142 | if( c<0x80 ) c = 0xfffd; |
| 143 | }else if( (c&0xf0)==0xe0 && p->i+1<p->mx && (p->z[p->i]&0xc0)==0x80 |
| 144 | && (p->z[p->i+1]&0xc0)==0x80 ){ |
| 145 | c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f); |
| 146 | p->i += 2; |
| 147 | if( c<=0x3ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd; |
| 148 | }else if( (c&0xf8)==0xf0 && p->i+3<p->mx && (p->z[p->i]&0xc0)==0x80 |
| 149 | && (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){ |
| 150 | c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6) |
| 151 | | (p->z[p->i+2]&0x3f); |
| 152 | p->i += 3; |
| 153 | if( c<=0xffff || c>0x10ffff ) c = 0xfffd; |
| 154 | }else{ |
| 155 | c = 0xfffd; |
| 156 | } |
| 157 | } |
| 158 | return c; |
| @@ -172,17 +172,17 @@ | |
| 172 | return (c>='0' && c<='9'); |
| 173 | } |
| 174 | |
| 175 | /* Return true if c is a perl "space" character: [ \t\r\n\v\f] */ |
| 176 | static int re_space_char(int c){ |
| 177 | return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f'; |
| 178 | } |
| 179 | |
| 180 | /* Run a compiled regular expression on the zero-terminated input |
| 181 | ** string zIn[]. Return true on a match and false if there is no match. |
| 182 | */ |
| 183 | int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){ |
| 184 | ReStateSet aStateSet[2], *pThis, *pNext; |
| 185 | ReStateNumber aSpace[100]; |
| 186 | ReStateNumber *pToFree; |
| 187 | unsigned int i = 0; |
| 188 | unsigned int iSwap = 0; |
| @@ -192,19 +192,22 @@ | |
| 192 | ReInput in; |
| 193 | |
| 194 | in.z = zIn; |
| 195 | in.i = 0; |
| 196 | in.mx = nIn>=0 ? nIn : strlen((char const*)zIn); |
| 197 | |
| 198 | /* Look for the initial prefix match, if there is one. */ |
| 199 | if( pRe->nInit ){ |
| 200 | unsigned char x = pRe->zInit[0]; |
| 201 | while( in.i+pRe->nInit<=in.mx |
| 202 | && (zIn[in.i]!=x || memcmp(zIn+in.i, pRe->zInit, pRe->nInit)!=0) |
| 203 | ){ |
| 204 | in.i++; |
| 205 | } |
| 206 | if( in.i+pRe->nInit>in.mx ) return 0; |
| 207 | } |
| 208 | |
| 209 | if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){ |
| 210 | pToFree = 0; |
| 211 | aStateSet[0].aState = aSpace; |
| 212 | }else{ |
| 213 | pToFree = fossil_malloc( sizeof(ReStateNumber)*2*pRe->nState ); |
| @@ -275,11 +278,11 @@ | |
| 278 | re_add_state(pThis, x+pRe->aArg[x]); |
| 279 | break; |
| 280 | } |
| 281 | case RE_OP_ACCEPT: { |
| 282 | rc = 1; |
| 283 | goto re_match_end; |
| 284 | } |
| 285 | case RE_OP_CC_INC: |
| 286 | case RE_OP_CC_EXC: { |
| 287 | int j = 1; |
| 288 | int n = pRe->aArg[x]; |
| @@ -307,11 +310,11 @@ | |
| 310 | } |
| 311 | } |
| 312 | for(i=0; i<pNext->nState; i++){ |
| 313 | if( pRe->aOp[pNext->aState[i]]==RE_OP_ACCEPT ){ rc = 1; break; } |
| 314 | } |
| 315 | re_match_end: |
| 316 | fossil_free(pToFree); |
| 317 | return rc; |
| 318 | } |
| 319 | |
| 320 | /* Resize the opcode and argument arrays for an RE under construction. |
| @@ -378,21 +381,20 @@ | |
| 381 | *pV = (*pV)*16 + (c & 0xff); |
| 382 | return 1; |
| 383 | } |
| 384 | |
| 385 | /* A backslash character has been seen, read the next character and |
| 386 | ** return its interpretation. |
| 387 | */ |
| 388 | static unsigned re_esc_char(ReCompiled *p){ |
| 389 | static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]"; |
| 390 | static const char zTrans[] = "\a\f\n\r\t\v"; |
| 391 | int i, v = 0; |
| 392 | char c; |
| 393 | if( p->sIn.i>=p->sIn.mx ) return 0; |
| 394 | c = p->sIn.z[p->sIn.i]; |
| 395 | if( c=='u' && p->sIn.i+4<p->sIn.mx ){ |
| 396 | const unsigned char *zIn = p->sIn.z + p->sIn.i; |
| 397 | if( re_hex(zIn[1],&v) |
| 398 | && re_hex(zIn[2],&v) |
| 399 | && re_hex(zIn[3],&v) |
| 400 | && re_hex(zIn[4],&v) |
| @@ -399,15 +401,16 @@ | |
| 401 | ){ |
| 402 | p->sIn.i += 5; |
| 403 | return v; |
| 404 | } |
| 405 | } |
| 406 | if( c=='x' && p->sIn.i+2<p->sIn.mx ){ |
| 407 | const unsigned char *zIn = p->sIn.z + p->sIn.i; |
| 408 | if( re_hex(zIn[1],&v) |
| 409 | && re_hex(zIn[2],&v) |
| 410 | ){ |
| 411 | p->sIn.i += 3; |
| 412 | return v; |
| 413 | } |
| 414 | } |
| 415 | for(i=0; zEsc[i] && zEsc[i]!=c; i++){} |
| 416 | if( zEsc[i] ){ |
| @@ -594,16 +597,17 @@ | |
| 597 | */ |
| 598 | void re_free(ReCompiled *pRe){ |
| 599 | if( pRe ){ |
| 600 | fossil_free(pRe->aOp); |
| 601 | fossil_free(pRe->aArg); |
| 602 | fossil_free(pRe); |
| 603 | } |
| 604 | } |
| 605 | |
| 606 | /* |
| 607 | ** Compile a textual regular expression in zIn[] into a compiled regular |
| 608 | ** expression suitable for us by re_match() and return a pointer to the |
| 609 | ** compiled regular expression in *ppRe. Return NULL on success or an |
| 610 | ** error message if something goes wrong. |
| 611 | */ |
| 612 | const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){ |
| 613 | ReCompiled *pRe; |
| @@ -632,11 +636,11 @@ | |
| 636 | zErr = re_subcompile_re(pRe); |
| 637 | if( zErr ){ |
| 638 | re_free(pRe); |
| 639 | return zErr; |
| 640 | } |
| 641 | if( rePeek(pRe)=='$' && pRe->sIn.i+1>=pRe->sIn.mx ){ |
| 642 | re_append(pRe, RE_OP_MATCH, RE_EOF); |
| 643 | re_append(pRe, RE_OP_ACCEPT, 0); |
| 644 | *ppRe = pRe; |
| 645 | }else if( pRe->sIn.i>=pRe->sIn.mx ){ |
| 646 | re_append(pRe, RE_OP_ACCEPT, 0); |
| @@ -643,10 +647,19 @@ | |
| 647 | *ppRe = pRe; |
| 648 | }else{ |
| 649 | re_free(pRe); |
| 650 | return "unrecognized character"; |
| 651 | } |
| 652 | |
| 653 | /* The following is a performance optimization. If the regex begins with |
| 654 | ** ".*" (if the input regex lacks an initial "^") and afterwards there are |
| 655 | ** one or more matching characters, enter those matching characters into |
| 656 | ** zInit[]. The re_match() routine can then search ahead in the input |
| 657 | ** string looking for the initial match without having to run the whole |
| 658 | ** regex engine over the string. Do not worry able trying to match |
| 659 | ** unicode characters beyond plane 0 - those are very rare and this is |
| 660 | ** just an optimization. */ |
| 661 | if( pRe->aOp[0]==RE_OP_ANYSTAR ){ |
| 662 | for(j=0, i=1; j<sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){ |
| 663 | unsigned x = pRe->aArg[i]; |
| 664 | if( x<=127 ){ |
| 665 | pRe->zInit[j++] = x; |
| @@ -654,15 +667,16 @@ | |
| 667 | pRe->zInit[j++] = 0xc0 | (x>>6); |
| 668 | pRe->zInit[j++] = 0x80 | (x&0x3f); |
| 669 | }else if( x<=0xffff ){ |
| 670 | pRe->zInit[j++] = 0xd0 | (x>>12); |
| 671 | pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f); |
| 672 | pRe->zInit[j++] = 0x80 | (x&0x3f); |
| 673 | }else{ |
| 674 | break; |
| 675 | } |
| 676 | } |
| 677 | if( j>0 && pRe->zInit[j-1]==0 ) j--; |
| 678 | pRe->nInit = j; |
| 679 | } |
| 680 | return pRe->zErr; |
| 681 | } |
| 682 | |
| @@ -700,11 +714,11 @@ | |
| 714 | } |
| 715 | sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free); |
| 716 | } |
| 717 | zStr = (const unsigned char*)sqlite3_value_text(argv[1]); |
| 718 | if( zStr!=0 ){ |
| 719 | sqlite3_result_int(context, re_match(pRe, zStr, -1)); |
| 720 | } |
| 721 | } |
| 722 | |
| 723 | /* |
| 724 | ** Invoke this routine in order to install the REGEXP function in an |
| @@ -731,11 +745,11 @@ | |
| 745 | char zLine[2000]; |
| 746 | while( fgets(zLine, sizeof(zLine), in) ){ |
| 747 | ln++; |
| 748 | n = (int)strlen(zLine); |
| 749 | while( n && (zLine[n-1]=='\n' || zLine[n-1]=='\r') ) n--; |
| 750 | if( re_match(pRe, (const unsigned char*)zLine, n) ){ |
| 751 | printf("%s:%d:%.*s\n", zFile, ln, n, zLine); |
| 752 | } |
| 753 | } |
| 754 | } |
| 755 | |
| 756 |
+2
| --- src/schema.c | ||
| +++ src/schema.c | ||
| @@ -385,10 +385,11 @@ | ||
| 385 | 385 | @ CREATE TABLE ticket( |
| 386 | 386 | @ -- Do not change any column that begins with tkt_ |
| 387 | 387 | @ tkt_id INTEGER PRIMARY KEY, |
| 388 | 388 | @ tkt_uuid TEXT UNIQUE, |
| 389 | 389 | @ tkt_mtime DATE, |
| 390 | +@ tkt_ctime DATE, | |
| 390 | 391 | @ -- Add as many field as required below this line |
| 391 | 392 | @ type TEXT, |
| 392 | 393 | @ status TEXT, |
| 393 | 394 | @ subsystem TEXT, |
| 394 | 395 | @ priority TEXT, |
| @@ -400,10 +401,11 @@ | ||
| 400 | 401 | @ comment TEXT |
| 401 | 402 | @ ); |
| 402 | 403 | @ CREATE TABLE ticketchng( |
| 403 | 404 | @ -- Do not change any column that begins with tkt_ |
| 404 | 405 | @ tkt_id INTEGER REFERENCES ticket, |
| 406 | +@ tkt_rid INTEGER REFERENCES blob, | |
| 405 | 407 | @ tkt_mtime DATE, |
| 406 | 408 | @ -- Add as many fields as required below this line |
| 407 | 409 | @ login TEXT, |
| 408 | 410 | @ username TEXT, |
| 409 | 411 | @ mimetype TEXT, |
| 410 | 412 |
| --- src/schema.c | |
| +++ src/schema.c | |
| @@ -385,10 +385,11 @@ | |
| 385 | @ CREATE TABLE ticket( |
| 386 | @ -- Do not change any column that begins with tkt_ |
| 387 | @ tkt_id INTEGER PRIMARY KEY, |
| 388 | @ tkt_uuid TEXT UNIQUE, |
| 389 | @ tkt_mtime DATE, |
| 390 | @ -- Add as many field as required below this line |
| 391 | @ type TEXT, |
| 392 | @ status TEXT, |
| 393 | @ subsystem TEXT, |
| 394 | @ priority TEXT, |
| @@ -400,10 +401,11 @@ | |
| 400 | @ comment TEXT |
| 401 | @ ); |
| 402 | @ CREATE TABLE ticketchng( |
| 403 | @ -- Do not change any column that begins with tkt_ |
| 404 | @ tkt_id INTEGER REFERENCES ticket, |
| 405 | @ tkt_mtime DATE, |
| 406 | @ -- Add as many fields as required below this line |
| 407 | @ login TEXT, |
| 408 | @ username TEXT, |
| 409 | @ mimetype TEXT, |
| 410 |
| --- src/schema.c | |
| +++ src/schema.c | |
| @@ -385,10 +385,11 @@ | |
| 385 | @ CREATE TABLE ticket( |
| 386 | @ -- Do not change any column that begins with tkt_ |
| 387 | @ tkt_id INTEGER PRIMARY KEY, |
| 388 | @ tkt_uuid TEXT UNIQUE, |
| 389 | @ tkt_mtime DATE, |
| 390 | @ tkt_ctime DATE, |
| 391 | @ -- Add as many field as required below this line |
| 392 | @ type TEXT, |
| 393 | @ status TEXT, |
| 394 | @ subsystem TEXT, |
| 395 | @ priority TEXT, |
| @@ -400,10 +401,11 @@ | |
| 401 | @ comment TEXT |
| 402 | @ ); |
| 403 | @ CREATE TABLE ticketchng( |
| 404 | @ -- Do not change any column that begins with tkt_ |
| 405 | @ tkt_id INTEGER REFERENCES ticket, |
| 406 | @ tkt_rid INTEGER REFERENCES blob, |
| 407 | @ tkt_mtime DATE, |
| 408 | @ -- Add as many fields as required below this line |
| 409 | @ login TEXT, |
| 410 | @ username TEXT, |
| 411 | @ mimetype TEXT, |
| 412 |
+2
| --- src/schema.c | ||
| +++ src/schema.c | ||
| @@ -385,10 +385,11 @@ | ||
| 385 | 385 | @ CREATE TABLE ticket( |
| 386 | 386 | @ -- Do not change any column that begins with tkt_ |
| 387 | 387 | @ tkt_id INTEGER PRIMARY KEY, |
| 388 | 388 | @ tkt_uuid TEXT UNIQUE, |
| 389 | 389 | @ tkt_mtime DATE, |
| 390 | +@ tkt_ctime DATE, | |
| 390 | 391 | @ -- Add as many field as required below this line |
| 391 | 392 | @ type TEXT, |
| 392 | 393 | @ status TEXT, |
| 393 | 394 | @ subsystem TEXT, |
| 394 | 395 | @ priority TEXT, |
| @@ -400,10 +401,11 @@ | ||
| 400 | 401 | @ comment TEXT |
| 401 | 402 | @ ); |
| 402 | 403 | @ CREATE TABLE ticketchng( |
| 403 | 404 | @ -- Do not change any column that begins with tkt_ |
| 404 | 405 | @ tkt_id INTEGER REFERENCES ticket, |
| 406 | +@ tkt_rid INTEGER REFERENCES blob, | |
| 405 | 407 | @ tkt_mtime DATE, |
| 406 | 408 | @ -- Add as many fields as required below this line |
| 407 | 409 | @ login TEXT, |
| 408 | 410 | @ username TEXT, |
| 409 | 411 | @ mimetype TEXT, |
| 410 | 412 |
| --- src/schema.c | |
| +++ src/schema.c | |
| @@ -385,10 +385,11 @@ | |
| 385 | @ CREATE TABLE ticket( |
| 386 | @ -- Do not change any column that begins with tkt_ |
| 387 | @ tkt_id INTEGER PRIMARY KEY, |
| 388 | @ tkt_uuid TEXT UNIQUE, |
| 389 | @ tkt_mtime DATE, |
| 390 | @ -- Add as many field as required below this line |
| 391 | @ type TEXT, |
| 392 | @ status TEXT, |
| 393 | @ subsystem TEXT, |
| 394 | @ priority TEXT, |
| @@ -400,10 +401,11 @@ | |
| 400 | @ comment TEXT |
| 401 | @ ); |
| 402 | @ CREATE TABLE ticketchng( |
| 403 | @ -- Do not change any column that begins with tkt_ |
| 404 | @ tkt_id INTEGER REFERENCES ticket, |
| 405 | @ tkt_mtime DATE, |
| 406 | @ -- Add as many fields as required below this line |
| 407 | @ login TEXT, |
| 408 | @ username TEXT, |
| 409 | @ mimetype TEXT, |
| 410 |
| --- src/schema.c | |
| +++ src/schema.c | |
| @@ -385,10 +385,11 @@ | |
| 385 | @ CREATE TABLE ticket( |
| 386 | @ -- Do not change any column that begins with tkt_ |
| 387 | @ tkt_id INTEGER PRIMARY KEY, |
| 388 | @ tkt_uuid TEXT UNIQUE, |
| 389 | @ tkt_mtime DATE, |
| 390 | @ tkt_ctime DATE, |
| 391 | @ -- Add as many field as required below this line |
| 392 | @ type TEXT, |
| 393 | @ status TEXT, |
| 394 | @ subsystem TEXT, |
| 395 | @ priority TEXT, |
| @@ -400,10 +401,11 @@ | |
| 401 | @ comment TEXT |
| 402 | @ ); |
| 403 | @ CREATE TABLE ticketchng( |
| 404 | @ -- Do not change any column that begins with tkt_ |
| 405 | @ tkt_id INTEGER REFERENCES ticket, |
| 406 | @ tkt_rid INTEGER REFERENCES blob, |
| 407 | @ tkt_mtime DATE, |
| 408 | @ -- Add as many fields as required below this line |
| 409 | @ login TEXT, |
| 410 | @ username TEXT, |
| 411 | @ mimetype TEXT, |
| 412 |
+9
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -917,10 +917,19 @@ | ||
| 917 | 917 | @ to this many bytes, uncompressed. If the client requires more data |
| 918 | 918 | @ than this, then the client will issue multiple HTTP requests. |
| 919 | 919 | @ Values below 1 million are not recommended. 5 million is a |
| 920 | 920 | @ reasonable number.</p> |
| 921 | 921 | |
| 922 | + @ <hr /> | |
| 923 | + entry_attribute("Download time limit", 11, "max-download-time", "mxdwnt", | |
| 924 | + "30"); | |
| 925 | + | |
| 926 | + @ <p>Fossil tries to spend less than this many seconds gathering | |
| 927 | + @ the out-bound data of sync, clone, and pull packets. | |
| 928 | + @ If the client request takes longer, a partial reply is given similar | |
| 929 | + @ to the download packet limit. 30s is a reasonable default.</p> | |
| 930 | + | |
| 922 | 931 | @ <hr /> |
| 923 | 932 | onoff_attribute( |
| 924 | 933 | "Enable hyperlinks for \"nobody\" based on User-Agent and Javascript", |
| 925 | 934 | "auto-hyperlink", "autohyperlink", 1); |
| 926 | 935 | @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users |
| 927 | 936 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -917,10 +917,19 @@ | |
| 917 | @ to this many bytes, uncompressed. If the client requires more data |
| 918 | @ than this, then the client will issue multiple HTTP requests. |
| 919 | @ Values below 1 million are not recommended. 5 million is a |
| 920 | @ reasonable number.</p> |
| 921 | |
| 922 | @ <hr /> |
| 923 | onoff_attribute( |
| 924 | "Enable hyperlinks for \"nobody\" based on User-Agent and Javascript", |
| 925 | "auto-hyperlink", "autohyperlink", 1); |
| 926 | @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users |
| 927 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -917,10 +917,19 @@ | |
| 917 | @ to this many bytes, uncompressed. If the client requires more data |
| 918 | @ than this, then the client will issue multiple HTTP requests. |
| 919 | @ Values below 1 million are not recommended. 5 million is a |
| 920 | @ reasonable number.</p> |
| 921 | |
| 922 | @ <hr /> |
| 923 | entry_attribute("Download time limit", 11, "max-download-time", "mxdwnt", |
| 924 | "30"); |
| 925 | |
| 926 | @ <p>Fossil tries to spend less than this many seconds gathering |
| 927 | @ the out-bound data of sync, clone, and pull packets. |
| 928 | @ If the client request takes longer, a partial reply is given similar |
| 929 | @ to the download packet limit. 30s is a reasonable default.</p> |
| 930 | |
| 931 | @ <hr /> |
| 932 | onoff_attribute( |
| 933 | "Enable hyperlinks for \"nobody\" based on User-Agent and Javascript", |
| 934 | "auto-hyperlink", "autohyperlink", 1); |
| 935 | @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users |
| 936 |
+9
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -917,10 +917,19 @@ | ||
| 917 | 917 | @ to this many bytes, uncompressed. If the client requires more data |
| 918 | 918 | @ than this, then the client will issue multiple HTTP requests. |
| 919 | 919 | @ Values below 1 million are not recommended. 5 million is a |
| 920 | 920 | @ reasonable number.</p> |
| 921 | 921 | |
| 922 | + @ <hr /> | |
| 923 | + entry_attribute("Download time limit", 11, "max-download-time", "mxdwnt", | |
| 924 | + "30"); | |
| 925 | + | |
| 926 | + @ <p>Fossil tries to spend less than this many seconds gathering | |
| 927 | + @ the out-bound data of sync, clone, and pull packets. | |
| 928 | + @ If the client request takes longer, a partial reply is given similar | |
| 929 | + @ to the download packet limit. 30s is a reasonable default.</p> | |
| 930 | + | |
| 922 | 931 | @ <hr /> |
| 923 | 932 | onoff_attribute( |
| 924 | 933 | "Enable hyperlinks for \"nobody\" based on User-Agent and Javascript", |
| 925 | 934 | "auto-hyperlink", "autohyperlink", 1); |
| 926 | 935 | @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users |
| 927 | 936 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -917,10 +917,19 @@ | |
| 917 | @ to this many bytes, uncompressed. If the client requires more data |
| 918 | @ than this, then the client will issue multiple HTTP requests. |
| 919 | @ Values below 1 million are not recommended. 5 million is a |
| 920 | @ reasonable number.</p> |
| 921 | |
| 922 | @ <hr /> |
| 923 | onoff_attribute( |
| 924 | "Enable hyperlinks for \"nobody\" based on User-Agent and Javascript", |
| 925 | "auto-hyperlink", "autohyperlink", 1); |
| 926 | @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users |
| 927 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -917,10 +917,19 @@ | |
| 917 | @ to this many bytes, uncompressed. If the client requires more data |
| 918 | @ than this, then the client will issue multiple HTTP requests. |
| 919 | @ Values below 1 million are not recommended. 5 million is a |
| 920 | @ reasonable number.</p> |
| 921 | |
| 922 | @ <hr /> |
| 923 | entry_attribute("Download time limit", 11, "max-download-time", "mxdwnt", |
| 924 | "30"); |
| 925 | |
| 926 | @ <p>Fossil tries to spend less than this many seconds gathering |
| 927 | @ the out-bound data of sync, clone, and pull packets. |
| 928 | @ If the client request takes longer, a partial reply is given similar |
| 929 | @ to the download packet limit. 30s is a reasonable default.</p> |
| 930 | |
| 931 | @ <hr /> |
| 932 | onoff_attribute( |
| 933 | "Enable hyperlinks for \"nobody\" based on User-Agent and Javascript", |
| 934 | "auto-hyperlink", "autohyperlink", 1); |
| 935 | @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users |
| 936 |
+6
| --- src/shell.c | ||
| +++ src/shell.c | ||
| @@ -1477,10 +1477,16 @@ | ||
| 1477 | 1477 | p->zDbFilename, sqlite3_errmsg(db)); |
| 1478 | 1478 | exit(1); |
| 1479 | 1479 | } |
| 1480 | 1480 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 1481 | 1481 | sqlite3_enable_load_extension(p->db, 1); |
| 1482 | +#endif | |
| 1483 | +#ifdef SQLITE_ENABLE_REGEXP | |
| 1484 | + { | |
| 1485 | + extern int sqlite3_add_regexp_func(sqlite3*); | |
| 1486 | + sqlite3_add_regexp_func(db); | |
| 1487 | + } | |
| 1482 | 1488 | #endif |
| 1483 | 1489 | } |
| 1484 | 1490 | } |
| 1485 | 1491 | |
| 1486 | 1492 | /* |
| 1487 | 1493 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -1477,10 +1477,16 @@ | |
| 1477 | p->zDbFilename, sqlite3_errmsg(db)); |
| 1478 | exit(1); |
| 1479 | } |
| 1480 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 1481 | sqlite3_enable_load_extension(p->db, 1); |
| 1482 | #endif |
| 1483 | } |
| 1484 | } |
| 1485 | |
| 1486 | /* |
| 1487 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -1477,10 +1477,16 @@ | |
| 1477 | p->zDbFilename, sqlite3_errmsg(db)); |
| 1478 | exit(1); |
| 1479 | } |
| 1480 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 1481 | sqlite3_enable_load_extension(p->db, 1); |
| 1482 | #endif |
| 1483 | #ifdef SQLITE_ENABLE_REGEXP |
| 1484 | { |
| 1485 | extern int sqlite3_add_regexp_func(sqlite3*); |
| 1486 | sqlite3_add_regexp_func(db); |
| 1487 | } |
| 1488 | #endif |
| 1489 | } |
| 1490 | } |
| 1491 | |
| 1492 | /* |
| 1493 |
+6
| --- src/shell.c | ||
| +++ src/shell.c | ||
| @@ -1477,10 +1477,16 @@ | ||
| 1477 | 1477 | p->zDbFilename, sqlite3_errmsg(db)); |
| 1478 | 1478 | exit(1); |
| 1479 | 1479 | } |
| 1480 | 1480 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 1481 | 1481 | sqlite3_enable_load_extension(p->db, 1); |
| 1482 | +#endif | |
| 1483 | +#ifdef SQLITE_ENABLE_REGEXP | |
| 1484 | + { | |
| 1485 | + extern int sqlite3_add_regexp_func(sqlite3*); | |
| 1486 | + sqlite3_add_regexp_func(db); | |
| 1487 | + } | |
| 1482 | 1488 | #endif |
| 1483 | 1489 | } |
| 1484 | 1490 | } |
| 1485 | 1491 | |
| 1486 | 1492 | /* |
| 1487 | 1493 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -1477,10 +1477,16 @@ | |
| 1477 | p->zDbFilename, sqlite3_errmsg(db)); |
| 1478 | exit(1); |
| 1479 | } |
| 1480 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 1481 | sqlite3_enable_load_extension(p->db, 1); |
| 1482 | #endif |
| 1483 | } |
| 1484 | } |
| 1485 | |
| 1486 | /* |
| 1487 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -1477,10 +1477,16 @@ | |
| 1477 | p->zDbFilename, sqlite3_errmsg(db)); |
| 1478 | exit(1); |
| 1479 | } |
| 1480 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 1481 | sqlite3_enable_load_extension(p->db, 1); |
| 1482 | #endif |
| 1483 | #ifdef SQLITE_ENABLE_REGEXP |
| 1484 | { |
| 1485 | extern int sqlite3_add_regexp_func(sqlite3*); |
| 1486 | sqlite3_add_regexp_func(db); |
| 1487 | } |
| 1488 | #endif |
| 1489 | } |
| 1490 | } |
| 1491 | |
| 1492 | /* |
| 1493 |
+381
-172
| --- src/sqlite3.c | ||
| +++ src/sqlite3.c | ||
| @@ -673,11 +673,11 @@ | ||
| 673 | 673 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 674 | 674 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 675 | 675 | */ |
| 676 | 676 | #define SQLITE_VERSION "3.7.16" |
| 677 | 677 | #define SQLITE_VERSION_NUMBER 3007016 |
| 678 | -#define SQLITE_SOURCE_ID "2012-12-21 16:15:35 ff6857b6ed6a46671006b75157d8cf853a816ef9" | |
| 678 | +#define SQLITE_SOURCE_ID "2013-01-17 17:20:49 38852f158ab20bb4d7b264af987ec1538052bec3" | |
| 679 | 679 | |
| 680 | 680 | /* |
| 681 | 681 | ** CAPI3REF: Run-Time Library Version Numbers |
| 682 | 682 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 683 | 683 | ** |
| @@ -8238,10 +8238,15 @@ | ||
| 8238 | 8238 | ** A convenience macro that returns the number of elements in |
| 8239 | 8239 | ** an array. |
| 8240 | 8240 | */ |
| 8241 | 8241 | #define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0]))) |
| 8242 | 8242 | |
| 8243 | +/* | |
| 8244 | +** Determine if the argument is a power of two | |
| 8245 | +*/ | |
| 8246 | +#define IsPowerOfTwo(X) (((X)&((X)-1))==0) | |
| 8247 | + | |
| 8243 | 8248 | /* |
| 8244 | 8249 | ** The following value as a destructor means to use sqlite3DbFree(). |
| 8245 | 8250 | ** The sqlite3DbFree() routine requires two parameters instead of the |
| 8246 | 8251 | ** one parameter that destructors normally want. So we have to introduce |
| 8247 | 8252 | ** this magic value that the code knows to handle differently. Any |
| @@ -10042,10 +10047,11 @@ | ||
| 10042 | 10047 | #define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */ |
| 10043 | 10048 | #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ |
| 10044 | 10049 | #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ |
| 10045 | 10050 | #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ |
| 10046 | 10051 | #define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */ |
| 10052 | +#define SQLITE_Transitive 0x0200 /* Transitive constraints */ | |
| 10047 | 10053 | #define SQLITE_AllOpts 0xffff /* All optimizations */ |
| 10048 | 10054 | |
| 10049 | 10055 | /* |
| 10050 | 10056 | ** Macros for testing whether or not optimizations are enabled or disabled. |
| 10051 | 10057 | */ |
| @@ -10553,24 +10559,24 @@ | ||
| 10553 | 10559 | ** and the value of Index.onError indicate the which conflict resolution |
| 10554 | 10560 | ** algorithm to employ whenever an attempt is made to insert a non-unique |
| 10555 | 10561 | ** element. |
| 10556 | 10562 | */ |
| 10557 | 10563 | struct Index { |
| 10558 | - char *zName; /* Name of this index */ | |
| 10559 | - int *aiColumn; /* Which columns are used by this index. 1st is 0 */ | |
| 10560 | - tRowcnt *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */ | |
| 10561 | - Table *pTable; /* The SQL table being indexed */ | |
| 10562 | - char *zColAff; /* String defining the affinity of each column */ | |
| 10563 | - Index *pNext; /* The next index associated with the same table */ | |
| 10564 | - Schema *pSchema; /* Schema containing this index */ | |
| 10565 | - u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */ | |
| 10566 | - char **azColl; /* Array of collation sequence names for index */ | |
| 10567 | - int nColumn; /* Number of columns in the table used by this index */ | |
| 10568 | - int tnum; /* Page containing root of this index in database file */ | |
| 10569 | - u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ | |
| 10570 | - u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ | |
| 10571 | - u8 bUnordered; /* Use this index for == or IN queries only */ | |
| 10564 | + char *zName; /* Name of this index */ | |
| 10565 | + int *aiColumn; /* Which columns are used by this index. 1st is 0 */ | |
| 10566 | + tRowcnt *aiRowEst; /* From ANALYZE: Est. rows selected by each column */ | |
| 10567 | + Table *pTable; /* The SQL table being indexed */ | |
| 10568 | + char *zColAff; /* String defining the affinity of each column */ | |
| 10569 | + Index *pNext; /* The next index associated with the same table */ | |
| 10570 | + Schema *pSchema; /* Schema containing this index */ | |
| 10571 | + u8 *aSortOrder; /* for each column: True==DESC, False==ASC */ | |
| 10572 | + char **azColl; /* Array of collation sequence names for index */ | |
| 10573 | + int tnum; /* DB Page containing root of this index */ | |
| 10574 | + u16 nColumn; /* Number of columns in table used by this index */ | |
| 10575 | + u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ | |
| 10576 | + unsigned autoIndex:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */ | |
| 10577 | + unsigned bUnordered:1; /* Use this index for == or IN queries only */ | |
| 10572 | 10578 | #ifdef SQLITE_ENABLE_STAT3 |
| 10573 | 10579 | int nSample; /* Number of elements in aSample[] */ |
| 10574 | 10580 | tRowcnt avgEq; /* Average nEq value for key values not in aSample */ |
| 10575 | 10581 | IndexSample *aSample; /* Samples of the left-most key */ |
| 10576 | 10582 | #endif |
| @@ -10840,22 +10846,31 @@ | ||
| 10840 | 10846 | ** name. An expr/name combination can be used in several ways, such |
| 10841 | 10847 | ** as the list of "expr AS ID" fields following a "SELECT" or in the |
| 10842 | 10848 | ** list of "ID = expr" items in an UPDATE. A list of expressions can |
| 10843 | 10849 | ** also be used as the argument to a function, in which case the a.zName |
| 10844 | 10850 | ** field is not used. |
| 10851 | +** | |
| 10852 | +** By default the Expr.zSpan field holds a human-readable description of | |
| 10853 | +** the expression that is used in the generation of error messages and | |
| 10854 | +** column labels. In this case, Expr.zSpan is typically the text of a | |
| 10855 | +** column expression as it exists in a SELECT statement. However, if | |
| 10856 | +** the bSpanIsTab flag is set, then zSpan is overloaded to mean the name | |
| 10857 | +** of the result column in the form: DATABASE.TABLE.COLUMN. This later | |
| 10858 | +** form is used for name resolution with nested FROM clauses. | |
| 10845 | 10859 | */ |
| 10846 | 10860 | struct ExprList { |
| 10847 | 10861 | int nExpr; /* Number of expressions on the list */ |
| 10848 | 10862 | int iECursor; /* VDBE Cursor associated with this ExprList */ |
| 10849 | 10863 | struct ExprList_item { /* For each expression in the list */ |
| 10850 | - Expr *pExpr; /* The list of expressions */ | |
| 10851 | - char *zName; /* Token associated with this expression */ | |
| 10852 | - char *zSpan; /* Original text of the expression */ | |
| 10853 | - u8 sortOrder; /* 1 for DESC or 0 for ASC */ | |
| 10854 | - u8 done; /* A flag to indicate when processing is finished */ | |
| 10855 | - u16 iOrderByCol; /* For ORDER BY, column number in result set */ | |
| 10856 | - u16 iAlias; /* Index into Parse.aAlias[] for zName */ | |
| 10864 | + Expr *pExpr; /* The list of expressions */ | |
| 10865 | + char *zName; /* Token associated with this expression */ | |
| 10866 | + char *zSpan; /* Original text of the expression */ | |
| 10867 | + u8 sortOrder; /* 1 for DESC or 0 for ASC */ | |
| 10868 | + unsigned done :1; /* A flag to indicate when processing is finished */ | |
| 10869 | + unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */ | |
| 10870 | + u16 iOrderByCol; /* For ORDER BY, column number in result set */ | |
| 10871 | + u16 iAlias; /* Index into Parse.aAlias[] for zName */ | |
| 10857 | 10872 | } *a; /* Alloc a power of two greater or equal to nExpr */ |
| 10858 | 10873 | }; |
| 10859 | 10874 | |
| 10860 | 10875 | /* |
| 10861 | 10876 | ** An instance of this structure is used by the parser to record both |
| @@ -12141,10 +12156,11 @@ | ||
| 12141 | 12156 | SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *); |
| 12142 | 12157 | SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...); |
| 12143 | 12158 | SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*); |
| 12144 | 12159 | SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *, Expr *, int, int); |
| 12145 | 12160 | SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*); |
| 12161 | +SQLITE_PRIVATE int sqlite3MatchSpanName(const char*, const char*, const char*, const char*); | |
| 12146 | 12162 | SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*); |
| 12147 | 12163 | SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); |
| 12148 | 12164 | SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); |
| 12149 | 12165 | SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int); |
| 12150 | 12166 | SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *); |
| @@ -23289,15 +23305,11 @@ | ||
| 23289 | 23305 | { "pwrite64", (sqlite3_syscall_ptr)0, 0 }, |
| 23290 | 23306 | #endif |
| 23291 | 23307 | #define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\ |
| 23292 | 23308 | aSyscall[13].pCurrent) |
| 23293 | 23309 | |
| 23294 | -#if SQLITE_ENABLE_LOCKING_STYLE | |
| 23295 | 23310 | { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 }, |
| 23296 | -#else | |
| 23297 | - { "fchmod", (sqlite3_syscall_ptr)0, 0 }, | |
| 23298 | -#endif | |
| 23299 | 23311 | #define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) |
| 23300 | 23312 | |
| 23301 | 23313 | #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE |
| 23302 | 23314 | { "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 }, |
| 23303 | 23315 | #else |
| @@ -23318,13 +23330,10 @@ | ||
| 23318 | 23330 | #define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent) |
| 23319 | 23331 | |
| 23320 | 23332 | { "fchown", (sqlite3_syscall_ptr)posixFchown, 0 }, |
| 23321 | 23333 | #define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent) |
| 23322 | 23334 | |
| 23323 | - { "umask", (sqlite3_syscall_ptr)umask, 0 }, | |
| 23324 | -#define osUmask ((mode_t(*)(mode_t))aSyscall[21].pCurrent) | |
| 23325 | - | |
| 23326 | 23335 | }; /* End of the overrideable system calls */ |
| 23327 | 23336 | |
| 23328 | 23337 | /* |
| 23329 | 23338 | ** This is the xSetSystemCall() method of sqlite3_vfs for all of the |
| 23330 | 23339 | ** "unix" VFSes. Return SQLITE_OK opon successfully updating the |
| @@ -23425,31 +23434,29 @@ | ||
| 23425 | 23434 | ** process that is able to write to the database will also be able to |
| 23426 | 23435 | ** recover the hot journals. |
| 23427 | 23436 | */ |
| 23428 | 23437 | static int robust_open(const char *z, int f, mode_t m){ |
| 23429 | 23438 | int fd; |
| 23430 | - mode_t m2; | |
| 23431 | - mode_t origM = 0; | |
| 23432 | - if( m==0 ){ | |
| 23433 | - m2 = SQLITE_DEFAULT_FILE_PERMISSIONS; | |
| 23434 | - }else{ | |
| 23435 | - m2 = m; | |
| 23436 | - origM = osUmask(0); | |
| 23437 | - } | |
| 23439 | + mode_t m2 = m ? m : SQLITE_DEFAULT_FILE_PERMISSIONS; | |
| 23438 | 23440 | do{ |
| 23439 | 23441 | #if defined(O_CLOEXEC) |
| 23440 | 23442 | fd = osOpen(z,f|O_CLOEXEC,m2); |
| 23441 | 23443 | #else |
| 23442 | 23444 | fd = osOpen(z,f,m2); |
| 23443 | 23445 | #endif |
| 23444 | 23446 | }while( fd<0 && errno==EINTR ); |
| 23445 | - if( m ){ | |
| 23446 | - osUmask(origM); | |
| 23447 | - } | |
| 23447 | + if( fd>=0 ){ | |
| 23448 | + if( m!=0 ){ | |
| 23449 | + struct stat statbuf; | |
| 23450 | + if( osFstat(fd, &statbuf)==0 && (statbuf.st_mode&0777)!=m ){ | |
| 23451 | + osFchmod(fd, m); | |
| 23452 | + } | |
| 23453 | + } | |
| 23448 | 23454 | #if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC==0) |
| 23449 | - if( fd>=0 ) osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); | |
| 23455 | + osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); | |
| 23450 | 23456 | #endif |
| 23457 | + } | |
| 23451 | 23458 | return fd; |
| 23452 | 23459 | } |
| 23453 | 23460 | |
| 23454 | 23461 | /* |
| 23455 | 23462 | ** Helper functions to obtain and relinquish the global mutex. The |
| @@ -29871,11 +29878,11 @@ | ||
| 29871 | 29878 | }; |
| 29872 | 29879 | unsigned int i; /* Loop counter */ |
| 29873 | 29880 | |
| 29874 | 29881 | /* Double-check that the aSyscall[] array has been constructed |
| 29875 | 29882 | ** correctly. See ticket [bb3a86e890c8e96ab] */ |
| 29876 | - assert( ArraySize(aSyscall)==22 ); | |
| 29883 | + assert( ArraySize(aSyscall)==21 ); | |
| 29877 | 29884 | |
| 29878 | 29885 | /* Register all VFSes defined in the aVfs[] array */ |
| 29879 | 29886 | for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ |
| 29880 | 29887 | sqlite3_vfs_register(&aVfs[i], i==0); |
| 29881 | 29888 | } |
| @@ -72655,10 +72662,39 @@ | ||
| 72655 | 72662 | } |
| 72656 | 72663 | } |
| 72657 | 72664 | return 0; |
| 72658 | 72665 | } |
| 72659 | 72666 | |
| 72667 | +/* | |
| 72668 | +** Subqueries stores the original database, table and column names for their | |
| 72669 | +** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN". | |
| 72670 | +** Check to see if the zSpan given to this routine matches the zDb, zTab, | |
| 72671 | +** and zCol. If any of zDb, zTab, and zCol are NULL then those fields will | |
| 72672 | +** match anything. | |
| 72673 | +*/ | |
| 72674 | +SQLITE_PRIVATE int sqlite3MatchSpanName( | |
| 72675 | + const char *zSpan, | |
| 72676 | + const char *zCol, | |
| 72677 | + const char *zTab, | |
| 72678 | + const char *zDb | |
| 72679 | +){ | |
| 72680 | + int n; | |
| 72681 | + for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} | |
| 72682 | + if( zDb && sqlite3StrNICmp(zSpan, zDb, n)!=0 ){ | |
| 72683 | + return 0; | |
| 72684 | + } | |
| 72685 | + zSpan += n+1; | |
| 72686 | + for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} | |
| 72687 | + if( zTab && sqlite3StrNICmp(zSpan, zTab, n)!=0 ){ | |
| 72688 | + return 0; | |
| 72689 | + } | |
| 72690 | + zSpan += n+1; | |
| 72691 | + if( zCol && sqlite3StrICmp(zSpan, zCol)!=0 ){ | |
| 72692 | + return 0; | |
| 72693 | + } | |
| 72694 | + return 1; | |
| 72695 | +} | |
| 72660 | 72696 | |
| 72661 | 72697 | /* |
| 72662 | 72698 | ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up |
| 72663 | 72699 | ** that name in the set of source tables in pSrcList and make the pExpr |
| 72664 | 72700 | ** expression node refer back to that source column. The following changes |
| @@ -72710,44 +72746,63 @@ | ||
| 72710 | 72746 | |
| 72711 | 72747 | /* Initialize the node to no-match */ |
| 72712 | 72748 | pExpr->iTable = -1; |
| 72713 | 72749 | pExpr->pTab = 0; |
| 72714 | 72750 | ExprSetIrreducible(pExpr); |
| 72751 | + | |
| 72752 | + /* Translate the schema name in zDb into a pointer to the corresponding | |
| 72753 | + ** schema. If not found, pSchema will remain NULL and nothing will match | |
| 72754 | + ** resulting in an appropriate error message toward the end of this routine | |
| 72755 | + */ | |
| 72756 | + if( zDb ){ | |
| 72757 | + for(i=0; i<db->nDb; i++){ | |
| 72758 | + assert( db->aDb[i].zName ); | |
| 72759 | + if( sqlite3StrICmp(db->aDb[i].zName,zDb)==0 ){ | |
| 72760 | + pSchema = db->aDb[i].pSchema; | |
| 72761 | + break; | |
| 72762 | + } | |
| 72763 | + } | |
| 72764 | + } | |
| 72715 | 72765 | |
| 72716 | 72766 | /* Start at the inner-most context and move outward until a match is found */ |
| 72717 | 72767 | while( pNC && cnt==0 ){ |
| 72718 | 72768 | ExprList *pEList; |
| 72719 | 72769 | SrcList *pSrcList = pNC->pSrcList; |
| 72720 | 72770 | |
| 72721 | 72771 | if( pSrcList ){ |
| 72722 | 72772 | for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){ |
| 72723 | 72773 | Table *pTab; |
| 72724 | - int iDb; | |
| 72725 | 72774 | Column *pCol; |
| 72726 | 72775 | |
| 72727 | 72776 | pTab = pItem->pTab; |
| 72728 | 72777 | assert( pTab!=0 && pTab->zName!=0 ); |
| 72729 | - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); | |
| 72730 | 72778 | assert( pTab->nCol>0 ); |
| 72779 | + if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){ | |
| 72780 | + ExprList *pEList = pItem->pSelect->pEList; | |
| 72781 | + int hit = 0; | |
| 72782 | + for(j=0; j<pEList->nExpr; j++){ | |
| 72783 | + if( sqlite3MatchSpanName(pEList->a[j].zSpan, zCol, zTab, zDb) ){ | |
| 72784 | + cnt++; | |
| 72785 | + cntTab = 2; | |
| 72786 | + pMatch = pItem; | |
| 72787 | + pExpr->iColumn = j; | |
| 72788 | + hit = 1; | |
| 72789 | + } | |
| 72790 | + } | |
| 72791 | + if( hit || zTab==0 ) continue; | |
| 72792 | + } | |
| 72793 | + if( zDb && pTab->pSchema!=pSchema ){ | |
| 72794 | + continue; | |
| 72795 | + } | |
| 72731 | 72796 | if( zTab ){ |
| 72732 | - if( pItem->zAlias ){ | |
| 72733 | - char *zTabName = pItem->zAlias; | |
| 72734 | - if( sqlite3StrICmp(zTabName, zTab)!=0 ) continue; | |
| 72735 | - }else{ | |
| 72736 | - char *zTabName = pTab->zName; | |
| 72737 | - if( NEVER(zTabName==0) || sqlite3StrICmp(zTabName, zTab)!=0 ){ | |
| 72738 | - continue; | |
| 72739 | - } | |
| 72740 | - if( zDb!=0 && sqlite3StrICmp(db->aDb[iDb].zName, zDb)!=0 ){ | |
| 72741 | - continue; | |
| 72742 | - } | |
| 72797 | + const char *zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName; | |
| 72798 | + assert( zTabName!=0 ); | |
| 72799 | + if( sqlite3StrICmp(zTabName, zTab)!=0 ){ | |
| 72800 | + continue; | |
| 72743 | 72801 | } |
| 72744 | 72802 | } |
| 72745 | 72803 | if( 0==(cntTab++) ){ |
| 72746 | - pExpr->iTable = pItem->iCursor; | |
| 72747 | - pExpr->pTab = pTab; | |
| 72748 | - pSchema = pTab->pSchema; | |
| 72749 | 72804 | pMatch = pItem; |
| 72750 | 72805 | } |
| 72751 | 72806 | for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){ |
| 72752 | 72807 | if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ |
| 72753 | 72808 | /* If there has been exactly one prior match and this match |
| @@ -72757,21 +72812,23 @@ | ||
| 72757 | 72812 | if( cnt==1 ){ |
| 72758 | 72813 | if( pItem->jointype & JT_NATURAL ) continue; |
| 72759 | 72814 | if( nameInUsingClause(pItem->pUsing, zCol) ) continue; |
| 72760 | 72815 | } |
| 72761 | 72816 | cnt++; |
| 72762 | - pExpr->iTable = pItem->iCursor; | |
| 72763 | - pExpr->pTab = pTab; | |
| 72764 | 72817 | pMatch = pItem; |
| 72765 | - pSchema = pTab->pSchema; | |
| 72766 | 72818 | /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ |
| 72767 | 72819 | pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; |
| 72768 | 72820 | break; |
| 72769 | 72821 | } |
| 72770 | 72822 | } |
| 72771 | 72823 | } |
| 72772 | - } | |
| 72824 | + if( pMatch ){ | |
| 72825 | + pExpr->iTable = pMatch->iCursor; | |
| 72826 | + pExpr->pTab = pMatch->pTab; | |
| 72827 | + pSchema = pExpr->pTab->pSchema; | |
| 72828 | + } | |
| 72829 | + } /* if( pSrcList ) */ | |
| 72773 | 72830 | |
| 72774 | 72831 | #ifndef SQLITE_OMIT_TRIGGER |
| 72775 | 72832 | /* If we have not already resolved the name, then maybe |
| 72776 | 72833 | ** it is a new.* or old.* trigger argument reference |
| 72777 | 72834 | */ |
| @@ -73102,11 +73159,11 @@ | ||
| 73102 | 73159 | #endif |
| 73103 | 73160 | if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){ |
| 73104 | 73161 | sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId); |
| 73105 | 73162 | pNC->nErr++; |
| 73106 | 73163 | is_agg = 0; |
| 73107 | - }else if( no_such_func ){ | |
| 73164 | + }else if( no_such_func && pParse->db->init.busy==0 ){ | |
| 73108 | 73165 | sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId); |
| 73109 | 73166 | pNC->nErr++; |
| 73110 | 73167 | }else if( wrong_num_args ){ |
| 73111 | 73168 | sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()", |
| 73112 | 73169 | nId, zId); |
| @@ -77065,10 +77122,16 @@ | ||
| 77065 | 77122 | for(i=0; i<pList->nExpr; i++){ |
| 77066 | 77123 | sqlite3ExplainPrintf(pOut, "item[%d] = ", i); |
| 77067 | 77124 | sqlite3ExplainPush(pOut); |
| 77068 | 77125 | sqlite3ExplainExpr(pOut, pList->a[i].pExpr); |
| 77069 | 77126 | sqlite3ExplainPop(pOut); |
| 77127 | + if( pList->a[i].zName ){ | |
| 77128 | + sqlite3ExplainPrintf(pOut, " AS %s", pList->a[i].zName); | |
| 77129 | + } | |
| 77130 | + if( pList->a[i].bSpanIsTab ){ | |
| 77131 | + sqlite3ExplainPrintf(pOut, " (%s)", pList->a[i].zSpan); | |
| 77132 | + } | |
| 77070 | 77133 | if( i<pList->nExpr-1 ){ |
| 77071 | 77134 | sqlite3ExplainNL(pOut); |
| 77072 | 77135 | } |
| 77073 | 77136 | } |
| 77074 | 77137 | sqlite3ExplainPop(pOut); |
| @@ -92739,13 +92802,15 @@ | ||
| 92739 | 92802 | if( sqlite3StrICmp(zLeft, "table_info")==0 && zRight ){ |
| 92740 | 92803 | Table *pTab; |
| 92741 | 92804 | if( sqlite3ReadSchema(pParse) ) goto pragma_out; |
| 92742 | 92805 | pTab = sqlite3FindTable(db, zRight, zDb); |
| 92743 | 92806 | if( pTab ){ |
| 92744 | - int i; | |
| 92807 | + int i, k; | |
| 92745 | 92808 | int nHidden = 0; |
| 92746 | 92809 | Column *pCol; |
| 92810 | + Index *pPk; | |
| 92811 | + for(pPk=pTab->pIndex; pPk && pPk->autoIndex!=2; pPk=pPk->pNext){} | |
| 92747 | 92812 | sqlite3VdbeSetNumCols(v, 6); |
| 92748 | 92813 | pParse->nMem = 6; |
| 92749 | 92814 | sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cid", SQLITE_STATIC); |
| 92750 | 92815 | sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC); |
| 92751 | 92816 | sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "type", SQLITE_STATIC); |
| @@ -92766,12 +92831,18 @@ | ||
| 92766 | 92831 | if( pCol->zDflt ){ |
| 92767 | 92832 | sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)pCol->zDflt, 0); |
| 92768 | 92833 | }else{ |
| 92769 | 92834 | sqlite3VdbeAddOp2(v, OP_Null, 0, 5); |
| 92770 | 92835 | } |
| 92771 | - sqlite3VdbeAddOp2(v, OP_Integer, | |
| 92772 | - (pCol->colFlags&COLFLAG_PRIMKEY)!=0, 6); | |
| 92836 | + if( (pCol->colFlags & COLFLAG_PRIMKEY)==0 ){ | |
| 92837 | + k = 0; | |
| 92838 | + }else if( pPk==0 ){ | |
| 92839 | + k = 1; | |
| 92840 | + }else{ | |
| 92841 | + for(k=1; ALWAYS(k<=pTab->nCol) && pPk->aiColumn[k-1]!=i; k++){} | |
| 92842 | + } | |
| 92843 | + sqlite3VdbeAddOp2(v, OP_Integer, k, 6); | |
| 92773 | 92844 | sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6); |
| 92774 | 92845 | } |
| 92775 | 92846 | } |
| 92776 | 92847 | }else |
| 92777 | 92848 | |
| @@ -93516,11 +93587,11 @@ | ||
| 93516 | 93587 | sqlite3_rekey(db, zKey, i/2); |
| 93517 | 93588 | } |
| 93518 | 93589 | }else |
| 93519 | 93590 | #endif |
| 93520 | 93591 | #if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) |
| 93521 | - if( sqlite3StrICmp(zLeft, "activate_extensions")==0 ){ | |
| 93592 | + if( sqlite3StrICmp(zLeft, "activate_extensions")==0 && zRight ){ | |
| 93522 | 93593 | #ifdef SQLITE_HAS_CODEC |
| 93523 | 93594 | if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){ |
| 93524 | 93595 | sqlite3_activate_see(&zRight[4]); |
| 93525 | 93596 | } |
| 93526 | 93597 | #endif |
| @@ -95756,12 +95827,10 @@ | ||
| 95756 | 95827 | |
| 95757 | 95828 | for(i=0, pCol=aCol; i<nCol; i++, pCol++){ |
| 95758 | 95829 | /* Get an appropriate name for the column |
| 95759 | 95830 | */ |
| 95760 | 95831 | p = sqlite3ExprSkipCollate(pEList->a[i].pExpr); |
| 95761 | - assert( p->pRight==0 || ExprHasProperty(p->pRight, EP_IntValue) | |
| 95762 | - || p->pRight->u.zToken==0 || p->pRight->u.zToken[0]!=0 ); | |
| 95763 | 95832 | if( (zName = pEList->a[i].zName)!=0 ){ |
| 95764 | 95833 | /* If the column contains an "AS <name>" phrase, use <name> as the name */ |
| 95765 | 95834 | zName = sqlite3DbStrDup(db, zName); |
| 95766 | 95835 | }else{ |
| 95767 | 95836 | Expr *pColExpr = p; /* The expression that is the result column name */ |
| @@ -95795,10 +95864,13 @@ | ||
| 95795 | 95864 | */ |
| 95796 | 95865 | nName = sqlite3Strlen30(zName); |
| 95797 | 95866 | for(j=cnt=0; j<i; j++){ |
| 95798 | 95867 | if( sqlite3StrICmp(aCol[j].zName, zName)==0 ){ |
| 95799 | 95868 | char *zNewName; |
| 95869 | + int k; | |
| 95870 | + for(k=nName-1; k>1 && sqlite3Isdigit(zName[k]); k--){} | |
| 95871 | + if( zName[k]==':' ) nName = k; | |
| 95800 | 95872 | zName[nName] = 0; |
| 95801 | 95873 | zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt); |
| 95802 | 95874 | sqlite3DbFree(db, zName); |
| 95803 | 95875 | zName = zNewName; |
| 95804 | 95876 | j = -1; |
| @@ -97711,10 +97783,11 @@ | ||
| 97711 | 97783 | int i, j, k; |
| 97712 | 97784 | SrcList *pTabList; |
| 97713 | 97785 | ExprList *pEList; |
| 97714 | 97786 | struct SrcList_item *pFrom; |
| 97715 | 97787 | sqlite3 *db = pParse->db; |
| 97788 | + Expr *pE, *pRight, *pExpr; | |
| 97716 | 97789 | |
| 97717 | 97790 | if( db->mallocFailed ){ |
| 97718 | 97791 | return WRC_Abort; |
| 97719 | 97792 | } |
| 97720 | 97793 | if( NEVER(p->pSrc==0) || (p->selFlags & SF_Expanded)!=0 ){ |
| @@ -97796,11 +97869,11 @@ | ||
| 97796 | 97869 | ** |
| 97797 | 97870 | ** The first loop just checks to see if there are any "*" operators |
| 97798 | 97871 | ** that need expanding. |
| 97799 | 97872 | */ |
| 97800 | 97873 | for(k=0; k<pEList->nExpr; k++){ |
| 97801 | - Expr *pE = pEList->a[k].pExpr; | |
| 97874 | + pE = pEList->a[k].pExpr; | |
| 97802 | 97875 | if( pE->op==TK_ALL ) break; |
| 97803 | 97876 | assert( pE->op!=TK_DOT || pE->pRight!=0 ); |
| 97804 | 97877 | assert( pE->op!=TK_DOT || (pE->pLeft!=0 && pE->pLeft->op==TK_ID) ); |
| 97805 | 97878 | if( pE->op==TK_DOT && pE->pRight->op==TK_ALL ) break; |
| 97806 | 97879 | } |
| @@ -97814,14 +97887,22 @@ | ||
| 97814 | 97887 | ExprList *pNew = 0; |
| 97815 | 97888 | int flags = pParse->db->flags; |
| 97816 | 97889 | int longNames = (flags & SQLITE_FullColNames)!=0 |
| 97817 | 97890 | && (flags & SQLITE_ShortColNames)==0; |
| 97818 | 97891 | |
| 97892 | + /* When processing FROM-clause subqueries, it is always the case | |
| 97893 | + ** that full_column_names=OFF and short_column_names=ON. The | |
| 97894 | + ** sqlite3ResultSetOfSelect() routine makes it so. */ | |
| 97895 | + assert( (p->selFlags & SF_NestedFrom)==0 | |
| 97896 | + || ((flags & SQLITE_FullColNames)==0 && | |
| 97897 | + (flags & SQLITE_ShortColNames)!=0) ); | |
| 97898 | + | |
| 97819 | 97899 | for(k=0; k<pEList->nExpr; k++){ |
| 97820 | - Expr *pE = a[k].pExpr; | |
| 97821 | - assert( pE->op!=TK_DOT || pE->pRight!=0 ); | |
| 97822 | - if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pE->pRight->op!=TK_ALL) ){ | |
| 97900 | + pE = a[k].pExpr; | |
| 97901 | + pRight = pE->pRight; | |
| 97902 | + assert( pE->op!=TK_DOT || pRight!=0 ); | |
| 97903 | + if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pRight->op!=TK_ALL) ){ | |
| 97823 | 97904 | /* This particular expression does not need to be expanded. |
| 97824 | 97905 | */ |
| 97825 | 97906 | pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr); |
| 97826 | 97907 | if( pNew ){ |
| 97827 | 97908 | pNew->a[pNew->nExpr-1].zName = a[k].zName; |
| @@ -97832,44 +97913,56 @@ | ||
| 97832 | 97913 | a[k].pExpr = 0; |
| 97833 | 97914 | }else{ |
| 97834 | 97915 | /* This expression is a "*" or a "TABLE.*" and needs to be |
| 97835 | 97916 | ** expanded. */ |
| 97836 | 97917 | int tableSeen = 0; /* Set to 1 when TABLE matches */ |
| 97837 | - char *zTName; /* text of name of TABLE */ | |
| 97918 | + char *zTName = 0; /* text of name of TABLE */ | |
| 97838 | 97919 | if( pE->op==TK_DOT ){ |
| 97839 | 97920 | assert( pE->pLeft!=0 ); |
| 97840 | 97921 | assert( !ExprHasProperty(pE->pLeft, EP_IntValue) ); |
| 97841 | 97922 | zTName = pE->pLeft->u.zToken; |
| 97842 | - }else{ | |
| 97843 | - zTName = 0; | |
| 97844 | 97923 | } |
| 97845 | 97924 | for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){ |
| 97846 | 97925 | Table *pTab = pFrom->pTab; |
| 97926 | + Select *pSub = pFrom->pSelect; | |
| 97847 | 97927 | char *zTabName = pFrom->zAlias; |
| 97928 | + const char *zSchemaName = 0; | |
| 97929 | + int iDb; | |
| 97848 | 97930 | if( zTabName==0 ){ |
| 97849 | 97931 | zTabName = pTab->zName; |
| 97850 | 97932 | } |
| 97851 | 97933 | if( db->mallocFailed ) break; |
| 97852 | - if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){ | |
| 97853 | - continue; | |
| 97934 | + if( pSub==0 || (pSub->selFlags & SF_NestedFrom)==0 ){ | |
| 97935 | + pSub = 0; | |
| 97936 | + if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){ | |
| 97937 | + continue; | |
| 97938 | + } | |
| 97939 | + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); | |
| 97940 | + zSchemaName = iDb>=0 ? db->aDb[iDb].zName : "*"; | |
| 97854 | 97941 | } |
| 97855 | - tableSeen = 1; | |
| 97856 | 97942 | for(j=0; j<pTab->nCol; j++){ |
| 97857 | - Expr *pExpr, *pRight; | |
| 97858 | 97943 | char *zName = pTab->aCol[j].zName; |
| 97859 | 97944 | char *zColname; /* The computed column name */ |
| 97860 | 97945 | char *zToFree; /* Malloced string that needs to be freed */ |
| 97861 | 97946 | Token sColname; /* Computed column name as a token */ |
| 97947 | + | |
| 97948 | + assert( zName ); | |
| 97949 | + if( zTName && pSub | |
| 97950 | + && sqlite3MatchSpanName(pSub->pEList->a[j].zSpan, 0, zTName, 0)==0 | |
| 97951 | + ){ | |
| 97952 | + continue; | |
| 97953 | + } | |
| 97862 | 97954 | |
| 97863 | 97955 | /* If a column is marked as 'hidden' (currently only possible |
| 97864 | 97956 | ** for virtual tables), do not include it in the expanded |
| 97865 | 97957 | ** result-set list. |
| 97866 | 97958 | */ |
| 97867 | 97959 | if( IsHiddenColumn(&pTab->aCol[j]) ){ |
| 97868 | 97960 | assert(IsVirtual(pTab)); |
| 97869 | 97961 | continue; |
| 97870 | 97962 | } |
| 97963 | + tableSeen = 1; | |
| 97871 | 97964 | |
| 97872 | 97965 | if( i>0 && zTName==0 ){ |
| 97873 | 97966 | if( (pFrom->jointype & JT_NATURAL)!=0 |
| 97874 | 97967 | && tableAndColumnIndex(pTabList, i, zName, 0, 0) |
| 97875 | 97968 | ){ |
| @@ -97888,10 +97981,14 @@ | ||
| 97888 | 97981 | zToFree = 0; |
| 97889 | 97982 | if( longNames || pTabList->nSrc>1 ){ |
| 97890 | 97983 | Expr *pLeft; |
| 97891 | 97984 | pLeft = sqlite3Expr(db, TK_ID, zTabName); |
| 97892 | 97985 | pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0); |
| 97986 | + if( zSchemaName ){ | |
| 97987 | + pLeft = sqlite3Expr(db, TK_ID, zSchemaName); | |
| 97988 | + pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pExpr, 0); | |
| 97989 | + } | |
| 97893 | 97990 | if( longNames ){ |
| 97894 | 97991 | zColname = sqlite3MPrintf(db, "%s.%s", zTabName, zName); |
| 97895 | 97992 | zToFree = zColname; |
| 97896 | 97993 | } |
| 97897 | 97994 | }else{ |
| @@ -97899,10 +97996,22 @@ | ||
| 97899 | 97996 | } |
| 97900 | 97997 | pNew = sqlite3ExprListAppend(pParse, pNew, pExpr); |
| 97901 | 97998 | sColname.z = zColname; |
| 97902 | 97999 | sColname.n = sqlite3Strlen30(zColname); |
| 97903 | 98000 | sqlite3ExprListSetName(pParse, pNew, &sColname, 0); |
| 98001 | + if( pNew && (p->selFlags & SF_NestedFrom)!=0 ){ | |
| 98002 | + struct ExprList_item *pX = &pNew->a[pNew->nExpr-1]; | |
| 98003 | + if( pSub ){ | |
| 98004 | + pX->zSpan = sqlite3DbStrDup(db, pSub->pEList->a[j].zSpan); | |
| 98005 | + testcase( pX->zSpan==0 ); | |
| 98006 | + }else{ | |
| 98007 | + pX->zSpan = sqlite3MPrintf(db, "%s.%s.%s", | |
| 98008 | + zSchemaName, zTabName, zColname); | |
| 98009 | + testcase( pX->zSpan==0 ); | |
| 98010 | + } | |
| 98011 | + pX->bSpanIsTab = 1; | |
| 98012 | + } | |
| 97904 | 98013 | sqlite3DbFree(db, zToFree); |
| 97905 | 98014 | } |
| 97906 | 98015 | } |
| 97907 | 98016 | if( !tableSeen ){ |
| 97908 | 98017 | if( zTName ){ |
| @@ -102699,12 +102808,12 @@ | ||
| 102699 | 102808 | Expr *pExpr; /* Pointer to the subexpression that is this term */ |
| 102700 | 102809 | int iParent; /* Disable pWC->a[iParent] when this term disabled */ |
| 102701 | 102810 | int leftCursor; /* Cursor number of X in "X <op> <expr>" */ |
| 102702 | 102811 | union { |
| 102703 | 102812 | int leftColumn; /* Column number of X in "X <op> <expr>" */ |
| 102704 | - WhereOrInfo *pOrInfo; /* Extra information if eOperator==WO_OR */ | |
| 102705 | - WhereAndInfo *pAndInfo; /* Extra information if eOperator==WO_AND */ | |
| 102813 | + WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */ | |
| 102814 | + WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */ | |
| 102706 | 102815 | } u; |
| 102707 | 102816 | u16 eOperator; /* A WO_xx value describing <op> */ |
| 102708 | 102817 | u8 wtFlags; /* TERM_xxx bit flags. See below */ |
| 102709 | 102818 | u8 nChild; /* Number of children that must disable us */ |
| 102710 | 102819 | WhereClause *pWC; /* The clause this term is part of */ |
| @@ -102828,10 +102937,11 @@ | ||
| 102828 | 102937 | #define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) |
| 102829 | 102938 | #define WO_MATCH 0x040 |
| 102830 | 102939 | #define WO_ISNULL 0x080 |
| 102831 | 102940 | #define WO_OR 0x100 /* Two or more OR-connected terms */ |
| 102832 | 102941 | #define WO_AND 0x200 /* Two or more AND-connected terms */ |
| 102942 | +#define WO_EQUIV 0x400 /* Of the form A==B, both columns */ | |
| 102833 | 102943 | #define WO_NOOP 0x800 /* This term does not restrict search space */ |
| 102834 | 102944 | |
| 102835 | 102945 | #define WO_ALL 0xfff /* Mask of all possible WO_* values */ |
| 102836 | 102946 | #define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */ |
| 102837 | 102947 | |
| @@ -103230,58 +103340,112 @@ | ||
| 103230 | 103340 | /* |
| 103231 | 103341 | ** Search for a term in the WHERE clause that is of the form "X <op> <expr>" |
| 103232 | 103342 | ** where X is a reference to the iColumn of table iCur and <op> is one of |
| 103233 | 103343 | ** the WO_xx operator codes specified by the op parameter. |
| 103234 | 103344 | ** Return a pointer to the term. Return 0 if not found. |
| 103345 | +** | |
| 103346 | +** The term returned might by Y=<expr> if there is another constraint in | |
| 103347 | +** the WHERE clause that specifies that X=Y. Any such constraints will be | |
| 103348 | +** identified by the WO_EQUIV bit in the pTerm->eOperator field. The | |
| 103349 | +** aEquiv[] array holds X and all its equivalents, with each SQL variable | |
| 103350 | +** taking up two slots in aEquiv[]. The first slot is for the cursor number | |
| 103351 | +** and the second is for the column number. There are 22 slots in aEquiv[] | |
| 103352 | +** so that means we can look for X plus up to 10 other equivalent values. | |
| 103353 | +** Hence a search for X will return <expr> if X=A1 and A1=A2 and A2=A3 | |
| 103354 | +** and ... and A9=A10 and A10=<expr>. | |
| 103355 | +** | |
| 103356 | +** If there are multiple terms in the WHERE clause of the form "X <op> <expr>" | |
| 103357 | +** then try for the one with no dependencies on <expr> - in other words where | |
| 103358 | +** <expr> is a constant expression of some kind. Only return entries of | |
| 103359 | +** the form "X <op> Y" where Y is a column in another table if no terms of | |
| 103360 | +** the form "X <op> <const-expr>" exist. Other than this priority, if there | |
| 103361 | +** are two or more terms that match, then the choice of which term to return | |
| 103362 | +** is arbitrary. | |
| 103235 | 103363 | */ |
| 103236 | 103364 | static WhereTerm *findTerm( |
| 103237 | 103365 | WhereClause *pWC, /* The WHERE clause to be searched */ |
| 103238 | 103366 | int iCur, /* Cursor number of LHS */ |
| 103239 | 103367 | int iColumn, /* Column number of LHS */ |
| 103240 | 103368 | Bitmask notReady, /* RHS must not overlap with this mask */ |
| 103241 | 103369 | u32 op, /* Mask of WO_xx values describing operator */ |
| 103242 | 103370 | Index *pIdx /* Must be compatible with this index, if not NULL */ |
| 103243 | 103371 | ){ |
| 103244 | - WhereTerm *pTerm; | |
| 103245 | - int k; | |
| 103372 | + WhereTerm *pTerm; /* Term being examined as possible result */ | |
| 103373 | + WhereTerm *pResult = 0; /* The answer to return */ | |
| 103374 | + WhereClause *pWCOrig = pWC; /* Original pWC value */ | |
| 103375 | + int j, k; /* Loop counters */ | |
| 103376 | + Expr *pX; /* Pointer to an expression */ | |
| 103377 | + Parse *pParse; /* Parsing context */ | |
| 103378 | + int iOrigCol = iColumn; /* Original value of iColumn */ | |
| 103379 | + int nEquiv = 2; /* Number of entires in aEquiv[] */ | |
| 103380 | + int iEquiv = 2; /* Number of entries of aEquiv[] processed so far */ | |
| 103381 | + int aEquiv[22]; /* iCur,iColumn and up to 10 other equivalents */ | |
| 103382 | + | |
| 103246 | 103383 | assert( iCur>=0 ); |
| 103247 | - op &= WO_ALL; | |
| 103248 | - for(; pWC; pWC=pWC->pOuter){ | |
| 103249 | - for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ | |
| 103250 | - if( pTerm->leftCursor==iCur | |
| 103251 | - && (pTerm->prereqRight & notReady)==0 | |
| 103252 | - && pTerm->u.leftColumn==iColumn | |
| 103253 | - && (pTerm->eOperator & op)!=0 | |
| 103254 | - ){ | |
| 103255 | - if( iColumn>=0 && pIdx && pTerm->eOperator!=WO_ISNULL ){ | |
| 103256 | - Expr *pX = pTerm->pExpr; | |
| 103257 | - CollSeq *pColl; | |
| 103258 | - char idxaff; | |
| 103259 | - int j; | |
| 103260 | - Parse *pParse = pWC->pParse; | |
| 103261 | - | |
| 103262 | - idxaff = pIdx->pTable->aCol[iColumn].affinity; | |
| 103263 | - if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue; | |
| 103264 | - | |
| 103265 | - /* Figure out the collation sequence required from an index for | |
| 103266 | - ** it to be useful for optimising expression pX. Store this | |
| 103267 | - ** value in variable pColl. | |
| 103268 | - */ | |
| 103269 | - assert(pX->pLeft); | |
| 103270 | - pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); | |
| 103271 | - if( pColl==0 ) pColl = pParse->db->pDfltColl; | |
| 103272 | - | |
| 103273 | - for(j=0; pIdx->aiColumn[j]!=iColumn; j++){ | |
| 103274 | - if( NEVER(j>=pIdx->nColumn) ) return 0; | |
| 103275 | - } | |
| 103276 | - if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue; | |
| 103277 | - } | |
| 103278 | - return pTerm; | |
| 103279 | - } | |
| 103280 | - } | |
| 103281 | - } | |
| 103282 | - return 0; | |
| 103384 | + aEquiv[0] = iCur; | |
| 103385 | + aEquiv[1] = iColumn; | |
| 103386 | + for(;;){ | |
| 103387 | + for(pWC=pWCOrig; pWC; pWC=pWC->pOuter){ | |
| 103388 | + for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ | |
| 103389 | + if( pTerm->leftCursor==iCur | |
| 103390 | + && pTerm->u.leftColumn==iColumn | |
| 103391 | + ){ | |
| 103392 | + if( (pTerm->prereqRight & notReady)==0 | |
| 103393 | + && (pTerm->eOperator & op & WO_ALL)!=0 | |
| 103394 | + ){ | |
| 103395 | + if( iOrigCol>=0 && pIdx && (pTerm->eOperator & WO_ISNULL)==0 ){ | |
| 103396 | + CollSeq *pColl; | |
| 103397 | + char idxaff; | |
| 103398 | + | |
| 103399 | + pX = pTerm->pExpr; | |
| 103400 | + pParse = pWC->pParse; | |
| 103401 | + idxaff = pIdx->pTable->aCol[iOrigCol].affinity; | |
| 103402 | + if( !sqlite3IndexAffinityOk(pX, idxaff) ){ | |
| 103403 | + continue; | |
| 103404 | + } | |
| 103405 | + | |
| 103406 | + /* Figure out the collation sequence required from an index for | |
| 103407 | + ** it to be useful for optimising expression pX. Store this | |
| 103408 | + ** value in variable pColl. | |
| 103409 | + */ | |
| 103410 | + assert(pX->pLeft); | |
| 103411 | + pColl = sqlite3BinaryCompareCollSeq(pParse,pX->pLeft,pX->pRight); | |
| 103412 | + if( pColl==0 ) pColl = pParse->db->pDfltColl; | |
| 103413 | + | |
| 103414 | + for(j=0; pIdx->aiColumn[j]!=iOrigCol; j++){ | |
| 103415 | + if( NEVER(j>=pIdx->nColumn) ) return 0; | |
| 103416 | + } | |
| 103417 | + if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ){ | |
| 103418 | + continue; | |
| 103419 | + } | |
| 103420 | + } | |
| 103421 | + pResult = pTerm; | |
| 103422 | + if( pTerm->prereqRight==0 ) goto findTerm_success; | |
| 103423 | + } | |
| 103424 | + if( (pTerm->eOperator & WO_EQUIV)!=0 | |
| 103425 | + && nEquiv<ArraySize(aEquiv) | |
| 103426 | + ){ | |
| 103427 | + pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight); | |
| 103428 | + assert( pX->op==TK_COLUMN ); | |
| 103429 | + for(j=0; j<nEquiv; j+=2){ | |
| 103430 | + if( aEquiv[j]==pX->iTable && aEquiv[j+1]==pX->iColumn ) break; | |
| 103431 | + } | |
| 103432 | + if( j==nEquiv ){ | |
| 103433 | + aEquiv[j] = pX->iTable; | |
| 103434 | + aEquiv[j+1] = pX->iColumn; | |
| 103435 | + nEquiv += 2; | |
| 103436 | + } | |
| 103437 | + } | |
| 103438 | + } | |
| 103439 | + } | |
| 103440 | + } | |
| 103441 | + if( iEquiv>=nEquiv ) break; | |
| 103442 | + iCur = aEquiv[iEquiv++]; | |
| 103443 | + iColumn = aEquiv[iEquiv++]; | |
| 103444 | + } | |
| 103445 | +findTerm_success: | |
| 103446 | + return pResult; | |
| 103283 | 103447 | } |
| 103284 | 103448 | |
| 103285 | 103449 | /* Forward reference */ |
| 103286 | 103450 | static void exprAnalyze(SrcList*, WhereClause*, int); |
| 103287 | 103451 | |
| @@ -103555,11 +103719,10 @@ | ||
| 103555 | 103719 | indexable = ~(Bitmask)0; |
| 103556 | 103720 | chngToIN = ~(pWC->vmask); |
| 103557 | 103721 | for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0 && indexable; i--, pOrTerm++){ |
| 103558 | 103722 | if( (pOrTerm->eOperator & WO_SINGLE)==0 ){ |
| 103559 | 103723 | WhereAndInfo *pAndInfo; |
| 103560 | - assert( pOrTerm->eOperator==0 ); | |
| 103561 | 103724 | assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 ); |
| 103562 | 103725 | chngToIN = 0; |
| 103563 | 103726 | pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo)); |
| 103564 | 103727 | if( pAndInfo ){ |
| 103565 | 103728 | WhereClause *pAndWC; |
| @@ -103594,11 +103757,11 @@ | ||
| 103594 | 103757 | if( pOrTerm->wtFlags & TERM_VIRTUAL ){ |
| 103595 | 103758 | WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent]; |
| 103596 | 103759 | b |= getMask(pMaskSet, pOther->leftCursor); |
| 103597 | 103760 | } |
| 103598 | 103761 | indexable &= b; |
| 103599 | - if( pOrTerm->eOperator!=WO_EQ ){ | |
| 103762 | + if( (pOrTerm->eOperator & WO_EQ)==0 ){ | |
| 103600 | 103763 | chngToIN = 0; |
| 103601 | 103764 | }else{ |
| 103602 | 103765 | chngToIN &= b; |
| 103603 | 103766 | } |
| 103604 | 103767 | } |
| @@ -103645,11 +103808,11 @@ | ||
| 103645 | 103808 | ** and column is found but leave okToChngToIN false if not found. |
| 103646 | 103809 | */ |
| 103647 | 103810 | for(j=0; j<2 && !okToChngToIN; j++){ |
| 103648 | 103811 | pOrTerm = pOrWc->a; |
| 103649 | 103812 | for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){ |
| 103650 | - assert( pOrTerm->eOperator==WO_EQ ); | |
| 103813 | + assert( pOrTerm->eOperator & WO_EQ ); | |
| 103651 | 103814 | pOrTerm->wtFlags &= ~TERM_OR_OK; |
| 103652 | 103815 | if( pOrTerm->leftCursor==iCursor ){ |
| 103653 | 103816 | /* This is the 2-bit case and we are on the second iteration and |
| 103654 | 103817 | ** current term is from the first iteration. So skip this term. */ |
| 103655 | 103818 | assert( j==1 ); |
| @@ -103671,21 +103834,21 @@ | ||
| 103671 | 103834 | } |
| 103672 | 103835 | if( i<0 ){ |
| 103673 | 103836 | /* No candidate table+column was found. This can only occur |
| 103674 | 103837 | ** on the second iteration */ |
| 103675 | 103838 | assert( j==1 ); |
| 103676 | - assert( (chngToIN&(chngToIN-1))==0 ); | |
| 103839 | + assert( IsPowerOfTwo(chngToIN) ); | |
| 103677 | 103840 | assert( chngToIN==getMask(pMaskSet, iCursor) ); |
| 103678 | 103841 | break; |
| 103679 | 103842 | } |
| 103680 | 103843 | testcase( j==1 ); |
| 103681 | 103844 | |
| 103682 | 103845 | /* We have found a candidate table and column. Check to see if that |
| 103683 | 103846 | ** table and column is common to every term in the OR clause */ |
| 103684 | 103847 | okToChngToIN = 1; |
| 103685 | 103848 | for(; i>=0 && okToChngToIN; i--, pOrTerm++){ |
| 103686 | - assert( pOrTerm->eOperator==WO_EQ ); | |
| 103849 | + assert( pOrTerm->eOperator & WO_EQ ); | |
| 103687 | 103850 | if( pOrTerm->leftCursor!=iCursor ){ |
| 103688 | 103851 | pOrTerm->wtFlags &= ~TERM_OR_OK; |
| 103689 | 103852 | }else if( pOrTerm->u.leftColumn!=iColumn ){ |
| 103690 | 103853 | okToChngToIN = 0; |
| 103691 | 103854 | }else{ |
| @@ -103717,11 +103880,11 @@ | ||
| 103717 | 103880 | Expr *pLeft = 0; /* The LHS of the IN operator */ |
| 103718 | 103881 | Expr *pNew; /* The complete IN operator */ |
| 103719 | 103882 | |
| 103720 | 103883 | for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){ |
| 103721 | 103884 | if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue; |
| 103722 | - assert( pOrTerm->eOperator==WO_EQ ); | |
| 103885 | + assert( pOrTerm->eOperator & WO_EQ ); | |
| 103723 | 103886 | assert( pOrTerm->leftCursor==iCursor ); |
| 103724 | 103887 | assert( pOrTerm->u.leftColumn==iColumn ); |
| 103725 | 103888 | pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); |
| 103726 | 103889 | pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup); |
| 103727 | 103890 | pLeft = pOrTerm->pExpr->pLeft; |
| @@ -103746,11 +103909,10 @@ | ||
| 103746 | 103909 | pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */ |
| 103747 | 103910 | } |
| 103748 | 103911 | } |
| 103749 | 103912 | } |
| 103750 | 103913 | #endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */ |
| 103751 | - | |
| 103752 | 103914 | |
| 103753 | 103915 | /* |
| 103754 | 103916 | ** The input to this routine is an WhereTerm structure with only the |
| 103755 | 103917 | ** "pExpr" field filled in. The job of this routine is to analyze the |
| 103756 | 103918 | ** subexpression and populate all the other fields of the WhereTerm |
| @@ -103816,21 +103978,23 @@ | ||
| 103816 | 103978 | } |
| 103817 | 103979 | pTerm->prereqAll = prereqAll; |
| 103818 | 103980 | pTerm->leftCursor = -1; |
| 103819 | 103981 | pTerm->iParent = -1; |
| 103820 | 103982 | pTerm->eOperator = 0; |
| 103821 | - if( allowedOp(op) && (pTerm->prereqRight & prereqLeft)==0 ){ | |
| 103983 | + if( allowedOp(op) ){ | |
| 103822 | 103984 | Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft); |
| 103823 | 103985 | Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight); |
| 103986 | + u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV; | |
| 103824 | 103987 | if( pLeft->op==TK_COLUMN ){ |
| 103825 | 103988 | pTerm->leftCursor = pLeft->iTable; |
| 103826 | 103989 | pTerm->u.leftColumn = pLeft->iColumn; |
| 103827 | - pTerm->eOperator = operatorMask(op); | |
| 103990 | + pTerm->eOperator = operatorMask(op) & opMask; | |
| 103828 | 103991 | } |
| 103829 | 103992 | if( pRight && pRight->op==TK_COLUMN ){ |
| 103830 | 103993 | WhereTerm *pNew; |
| 103831 | 103994 | Expr *pDup; |
| 103995 | + u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */ | |
| 103832 | 103996 | if( pTerm->leftCursor>=0 ){ |
| 103833 | 103997 | int idxNew; |
| 103834 | 103998 | pDup = sqlite3ExprDup(db, pExpr, 0); |
| 103835 | 103999 | if( db->mallocFailed ){ |
| 103836 | 104000 | sqlite3ExprDelete(db, pDup); |
| @@ -103841,10 +104005,17 @@ | ||
| 103841 | 104005 | pNew = &pWC->a[idxNew]; |
| 103842 | 104006 | pNew->iParent = idxTerm; |
| 103843 | 104007 | pTerm = &pWC->a[idxTerm]; |
| 103844 | 104008 | pTerm->nChild = 1; |
| 103845 | 104009 | pTerm->wtFlags |= TERM_COPIED; |
| 104010 | + if( pExpr->op==TK_EQ | |
| 104011 | + && !ExprHasProperty(pExpr, EP_FromJoin) | |
| 104012 | + && OptimizationEnabled(db, SQLITE_Transitive) | |
| 104013 | + ){ | |
| 104014 | + pTerm->eOperator |= WO_EQUIV; | |
| 104015 | + eExtraOp = WO_EQUIV; | |
| 104016 | + } | |
| 103846 | 104017 | }else{ |
| 103847 | 104018 | pDup = pExpr; |
| 103848 | 104019 | pNew = pTerm; |
| 103849 | 104020 | } |
| 103850 | 104021 | exprCommute(pParse, pDup); |
| @@ -103852,11 +104023,11 @@ | ||
| 103852 | 104023 | pNew->leftCursor = pLeft->iTable; |
| 103853 | 104024 | pNew->u.leftColumn = pLeft->iColumn; |
| 103854 | 104025 | testcase( (prereqLeft | extraRight) != prereqLeft ); |
| 103855 | 104026 | pNew->prereqRight = prereqLeft | extraRight; |
| 103856 | 104027 | pNew->prereqAll = prereqAll; |
| 103857 | - pNew->eOperator = operatorMask(pDup->op); | |
| 104028 | + pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask; | |
| 103858 | 104029 | } |
| 103859 | 104030 | } |
| 103860 | 104031 | |
| 103861 | 104032 | #ifndef SQLITE_OMIT_BETWEEN_OPTIMIZATION |
| 103862 | 104033 | /* If a term is the BETWEEN operator, create two new virtual terms |
| @@ -104311,11 +104482,11 @@ | ||
| 104311 | 104482 | return; |
| 104312 | 104483 | } |
| 104313 | 104484 | |
| 104314 | 104485 | /* Search the WHERE clause terms for a usable WO_OR term. */ |
| 104315 | 104486 | for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ |
| 104316 | - if( pTerm->eOperator==WO_OR | |
| 104487 | + if( (pTerm->eOperator & WO_OR)!=0 | |
| 104317 | 104488 | && ((pTerm->prereqAll & ~maskSrc) & p->notReady)==0 |
| 104318 | 104489 | && (pTerm->u.pOrInfo->indexable & maskSrc)!=0 |
| 104319 | 104490 | ){ |
| 104320 | 104491 | WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc; |
| 104321 | 104492 | WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm]; |
| @@ -104332,11 +104503,11 @@ | ||
| 104332 | 104503 | sBOI.ppIdxInfo = 0; |
| 104333 | 104504 | for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){ |
| 104334 | 104505 | WHERETRACE(("... Multi-index OR testing for term %d of %d....\n", |
| 104335 | 104506 | (pOrTerm - pOrWC->a), (pTerm - pWC->a) |
| 104336 | 104507 | )); |
| 104337 | - if( pOrTerm->eOperator==WO_AND ){ | |
| 104508 | + if( (pOrTerm->eOperator& WO_AND)!=0 ){ | |
| 104338 | 104509 | sBOI.pWC = &pOrTerm->u.pAndInfo->wc; |
| 104339 | 104510 | bestIndex(&sBOI); |
| 104340 | 104511 | }else if( pOrTerm->leftCursor==iCur ){ |
| 104341 | 104512 | WhereClause tempWC; |
| 104342 | 104513 | tempWC.pParse = pWC->pParse; |
| @@ -104393,11 +104564,11 @@ | ||
| 104393 | 104564 | struct SrcList_item *pSrc, /* Table we are trying to access */ |
| 104394 | 104565 | Bitmask notReady /* Tables in outer loops of the join */ |
| 104395 | 104566 | ){ |
| 104396 | 104567 | char aff; |
| 104397 | 104568 | if( pTerm->leftCursor!=pSrc->iCursor ) return 0; |
| 104398 | - if( pTerm->eOperator!=WO_EQ ) return 0; | |
| 104569 | + if( (pTerm->eOperator & WO_EQ)==0 ) return 0; | |
| 104399 | 104570 | if( (pTerm->prereqRight & notReady)!=0 ) return 0; |
| 104400 | 104571 | aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; |
| 104401 | 104572 | if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; |
| 104402 | 104573 | return 1; |
| 104403 | 104574 | } |
| @@ -104655,13 +104826,13 @@ | ||
| 104655 | 104826 | |
| 104656 | 104827 | /* Count the number of possible WHERE clause constraints referring |
| 104657 | 104828 | ** to this virtual table */ |
| 104658 | 104829 | for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 104659 | 104830 | if( pTerm->leftCursor != pSrc->iCursor ) continue; |
| 104660 | - assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 ); | |
| 104661 | - testcase( pTerm->eOperator==WO_IN ); | |
| 104662 | - testcase( pTerm->eOperator==WO_ISNULL ); | |
| 104831 | + assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); | |
| 104832 | + testcase( pTerm->eOperator & WO_IN ); | |
| 104833 | + testcase( pTerm->eOperator & WO_ISNULL ); | |
| 104663 | 104834 | if( pTerm->eOperator & (WO_ISNULL) ) continue; |
| 104664 | 104835 | if( pTerm->wtFlags & TERM_VNULL ) continue; |
| 104665 | 104836 | nTerm++; |
| 104666 | 104837 | } |
| 104667 | 104838 | |
| @@ -104708,18 +104879,18 @@ | ||
| 104708 | 104879 | pUsage; |
| 104709 | 104880 | |
| 104710 | 104881 | for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 104711 | 104882 | u8 op; |
| 104712 | 104883 | if( pTerm->leftCursor != pSrc->iCursor ) continue; |
| 104713 | - assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 ); | |
| 104714 | - testcase( pTerm->eOperator==WO_IN ); | |
| 104715 | - testcase( pTerm->eOperator==WO_ISNULL ); | |
| 104884 | + assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); | |
| 104885 | + testcase( pTerm->eOperator & WO_IN ); | |
| 104886 | + testcase( pTerm->eOperator & WO_ISNULL ); | |
| 104716 | 104887 | if( pTerm->eOperator & (WO_ISNULL) ) continue; |
| 104717 | 104888 | if( pTerm->wtFlags & TERM_VNULL ) continue; |
| 104718 | 104889 | pIdxCons[j].iColumn = pTerm->u.leftColumn; |
| 104719 | 104890 | pIdxCons[j].iTermOffset = i; |
| 104720 | - op = (u8)pTerm->eOperator; | |
| 104891 | + op = (u8)pTerm->eOperator & WO_ALL; | |
| 104721 | 104892 | if( op==WO_IN ) op = WO_EQ; |
| 104722 | 104893 | pIdxCons[j].op = op; |
| 104723 | 104894 | /* The direct assignment in the previous line is possible only because |
| 104724 | 104895 | ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The |
| 104725 | 104896 | ** following asserts verify this fact. */ |
| @@ -104885,11 +105056,11 @@ | ||
| 104885 | 105056 | pUsage = pIdxInfo->aConstraintUsage; |
| 104886 | 105057 | for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ |
| 104887 | 105058 | j = pIdxCons->iTermOffset; |
| 104888 | 105059 | pTerm = &pWC->a[j]; |
| 104889 | 105060 | if( (pTerm->prereqRight&p->notReady)==0 |
| 104890 | - && (bAllowIN || pTerm->eOperator!=WO_IN) | |
| 105061 | + && (bAllowIN || (pTerm->eOperator & WO_IN)==0) | |
| 104891 | 105062 | ){ |
| 104892 | 105063 | pIdxCons->usable = 1; |
| 104893 | 105064 | }else{ |
| 104894 | 105065 | pIdxCons->usable = 0; |
| 104895 | 105066 | } |
| @@ -104917,11 +105088,11 @@ | ||
| 104917 | 105088 | for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ |
| 104918 | 105089 | if( pUsage[i].argvIndex>0 ){ |
| 104919 | 105090 | j = pIdxCons->iTermOffset; |
| 104920 | 105091 | pTerm = &pWC->a[j]; |
| 104921 | 105092 | p->cost.used |= pTerm->prereqRight; |
| 104922 | - if( pTerm->eOperator==WO_IN && pUsage[i].omit==0 ){ | |
| 105093 | + if( (pTerm->eOperator & WO_IN)!=0 && pUsage[i].omit==0 ){ | |
| 104923 | 105094 | /* Do not attempt to use an IN constraint if the virtual table |
| 104924 | 105095 | ** says that the equivalent EQ constraint cannot be safely omitted. |
| 104925 | 105096 | ** If we do attempt to use such a constraint, some rows might be |
| 104926 | 105097 | ** repeated in the output. */ |
| 104927 | 105098 | break; |
| @@ -105223,28 +105394,28 @@ | ||
| 105223 | 105394 | u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; |
| 105224 | 105395 | |
| 105225 | 105396 | if( pLower ){ |
| 105226 | 105397 | Expr *pExpr = pLower->pExpr->pRight; |
| 105227 | 105398 | rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); |
| 105228 | - assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE ); | |
| 105399 | + assert( (pLower->eOperator & (WO_GT|WO_GE))!=0 ); | |
| 105229 | 105400 | if( rc==SQLITE_OK |
| 105230 | 105401 | && whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK |
| 105231 | 105402 | ){ |
| 105232 | 105403 | iLower = a[0]; |
| 105233 | - if( pLower->eOperator==WO_GT ) iLower += a[1]; | |
| 105404 | + if( (pLower->eOperator & WO_GT)!=0 ) iLower += a[1]; | |
| 105234 | 105405 | } |
| 105235 | 105406 | sqlite3ValueFree(pRangeVal); |
| 105236 | 105407 | } |
| 105237 | 105408 | if( rc==SQLITE_OK && pUpper ){ |
| 105238 | 105409 | Expr *pExpr = pUpper->pExpr->pRight; |
| 105239 | 105410 | rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); |
| 105240 | - assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE ); | |
| 105411 | + assert( (pUpper->eOperator & (WO_LT|WO_LE))!=0 ); | |
| 105241 | 105412 | if( rc==SQLITE_OK |
| 105242 | 105413 | && whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK |
| 105243 | 105414 | ){ |
| 105244 | 105415 | iUpper = a[0]; |
| 105245 | - if( pUpper->eOperator==WO_LE ) iUpper += a[1]; | |
| 105416 | + if( (pUpper->eOperator & WO_LE)!=0 ) iUpper += a[1]; | |
| 105246 | 105417 | } |
| 105247 | 105418 | sqlite3ValueFree(pRangeVal); |
| 105248 | 105419 | } |
| 105249 | 105420 | if( rc==SQLITE_OK ){ |
| 105250 | 105421 | if( iUpper<=iLower ){ |
| @@ -105548,16 +105719,16 @@ | ||
| 105548 | 105719 | ** if there are any X= or X IS NULL constraints in the WHERE clause. */ |
| 105549 | 105720 | pConstraint = findTerm(p->pWC, base, iColumn, p->notReady, |
| 105550 | 105721 | WO_EQ|WO_ISNULL|WO_IN, pIdx); |
| 105551 | 105722 | if( pConstraint==0 ){ |
| 105552 | 105723 | isEq = 0; |
| 105553 | - }else if( pConstraint->eOperator==WO_IN ){ | |
| 105724 | + }else if( (pConstraint->eOperator & WO_IN)!=0 ){ | |
| 105554 | 105725 | /* Constraints of the form: "X IN ..." cannot be used with an ORDER BY |
| 105555 | 105726 | ** because we do not know in what order the values on the RHS of the IN |
| 105556 | 105727 | ** operator will occur. */ |
| 105557 | 105728 | break; |
| 105558 | - }else if( pConstraint->eOperator==WO_ISNULL ){ | |
| 105729 | + }else if( (pConstraint->eOperator & WO_ISNULL)!=0 ){ | |
| 105559 | 105730 | uniqueNotNull = 0; |
| 105560 | 105731 | isEq = 1; /* "X IS NULL" means X has only a single value */ |
| 105561 | 105732 | }else if( pConstraint->prereqRight==0 ){ |
| 105562 | 105733 | isEq = 1; /* Constraint "X=constant" means X has only a single value */ |
| 105563 | 105734 | }else{ |
| @@ -105903,11 +106074,11 @@ | ||
| 105903 | 106074 | int bRev = 2; |
| 105904 | 106075 | WHERETRACE((" --> before isSortingIndex: nPriorSat=%d\n",nPriorSat)); |
| 105905 | 106076 | pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev); |
| 105906 | 106077 | WHERETRACE((" --> after isSortingIndex: bRev=%d nOBSat=%d\n", |
| 105907 | 106078 | bRev, pc.plan.nOBSat)); |
| 105908 | - if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_UNIQUE)!=0 ){ | |
| 106079 | + if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){ | |
| 105909 | 106080 | pc.plan.wsFlags |= WHERE_ORDERED; |
| 105910 | 106081 | } |
| 105911 | 106082 | if( nOrderBy==pc.plan.nOBSat ){ |
| 105912 | 106083 | bSort = 0; |
| 105913 | 106084 | pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE; |
| @@ -105966,16 +106137,17 @@ | ||
| 105966 | 106137 | */ |
| 105967 | 106138 | if( pc.plan.nRow>(double)1 && pc.plan.nEq==1 |
| 105968 | 106139 | && pFirstTerm!=0 && aiRowEst[1]>1 ){ |
| 105969 | 106140 | assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 ); |
| 105970 | 106141 | if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){ |
| 105971 | - testcase( pFirstTerm->eOperator==WO_EQ ); | |
| 105972 | - testcase( pFirstTerm->eOperator==WO_ISNULL ); | |
| 106142 | + testcase( pFirstTerm->eOperator & WO_EQ ); | |
| 106143 | + testcase( pFirstTerm->eOperator & WO_EQUIV ); | |
| 106144 | + testcase( pFirstTerm->eOperator & WO_ISNULL ); | |
| 105973 | 106145 | whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, |
| 105974 | 106146 | &pc.plan.nRow); |
| 105975 | 106147 | }else if( bInEst==0 ){ |
| 105976 | - assert( pFirstTerm->eOperator==WO_IN ); | |
| 106148 | + assert( pFirstTerm->eOperator & WO_IN ); | |
| 105977 | 106149 | whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, |
| 105978 | 106150 | &pc.plan.nRow); |
| 105979 | 106151 | } |
| 105980 | 106152 | } |
| 105981 | 106153 | #endif /* SQLITE_ENABLE_STAT3 */ |
| @@ -106118,11 +106290,11 @@ | ||
| 106118 | 106290 | ** more selective intentionally because of the subjective |
| 106119 | 106291 | ** observation that indexed range constraints really are more |
| 106120 | 106292 | ** selective in practice, on average. */ |
| 106121 | 106293 | pc.plan.nRow /= 3; |
| 106122 | 106294 | } |
| 106123 | - }else if( pTerm->eOperator!=WO_NOOP ){ | |
| 106295 | + }else if( (pTerm->eOperator & WO_NOOP)==0 ){ | |
| 106124 | 106296 | /* Any other expression lowers the output row count by half */ |
| 106125 | 106297 | pc.plan.nRow /= 2; |
| 106126 | 106298 | } |
| 106127 | 106299 | } |
| 106128 | 106300 | if( pc.plan.nRow<2 ) pc.plan.nRow = 2; |
| @@ -106170,12 +106342,13 @@ | ||
| 106170 | 106342 | assert( pSrc->pIndex==0 |
| 106171 | 106343 | || p->cost.plan.u.pIdx==0 |
| 106172 | 106344 | || p->cost.plan.u.pIdx==pSrc->pIndex |
| 106173 | 106345 | ); |
| 106174 | 106346 | |
| 106175 | - WHERETRACE((" best index is: %s\n", | |
| 106176 | - p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk")); | |
| 106347 | + WHERETRACE((" best index is %s cost=%.1f\n", | |
| 106348 | + p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk", | |
| 106349 | + p->cost.rCost)); | |
| 106177 | 106350 | |
| 106178 | 106351 | bestOrClauseIndex(p); |
| 106179 | 106352 | bestAutomaticIndex(p); |
| 106180 | 106353 | p->cost.plan.wsFlags |= eqTermMask; |
| 106181 | 106354 | } |
| @@ -106753,11 +106926,10 @@ | ||
| 106753 | 106926 | */ |
| 106754 | 106927 | iReleaseReg = sqlite3GetTempReg(pParse); |
| 106755 | 106928 | pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0); |
| 106756 | 106929 | assert( pTerm!=0 ); |
| 106757 | 106930 | assert( pTerm->pExpr!=0 ); |
| 106758 | - assert( pTerm->leftCursor==iCur ); | |
| 106759 | 106931 | assert( omitTable==0 ); |
| 106760 | 106932 | testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ |
| 106761 | 106933 | iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, iReleaseReg); |
| 106762 | 106934 | addrNxt = pLevel->addrNxt; |
| 106763 | 106935 | sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); |
| @@ -107144,11 +107316,11 @@ | ||
| 107144 | 107316 | int ii; /* Loop counter */ |
| 107145 | 107317 | Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ |
| 107146 | 107318 | |
| 107147 | 107319 | pTerm = pLevel->plan.u.pTerm; |
| 107148 | 107320 | assert( pTerm!=0 ); |
| 107149 | - assert( pTerm->eOperator==WO_OR ); | |
| 107321 | + assert( pTerm->eOperator & WO_OR ); | |
| 107150 | 107322 | assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); |
| 107151 | 107323 | pOrWc = &pTerm->u.pOrInfo->wc; |
| 107152 | 107324 | pLevel->op = OP_Return; |
| 107153 | 107325 | pLevel->p1 = regReturn; |
| 107154 | 107326 | |
| @@ -107217,11 +107389,11 @@ | ||
| 107217 | 107389 | } |
| 107218 | 107390 | } |
| 107219 | 107391 | |
| 107220 | 107392 | for(ii=0; ii<pOrWc->nTerm; ii++){ |
| 107221 | 107393 | WhereTerm *pOrTerm = &pOrWc->a[ii]; |
| 107222 | - if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){ | |
| 107394 | + if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){ | |
| 107223 | 107395 | WhereInfo *pSubWInfo; /* Info for single OR-term scan */ |
| 107224 | 107396 | Expr *pOrExpr = pOrTerm->pExpr; |
| 107225 | 107397 | if( pAndExpr ){ |
| 107226 | 107398 | pAndExpr->pLeft = pOrExpr; |
| 107227 | 107399 | pOrExpr = pAndExpr; |
| @@ -107672,10 +107844,11 @@ | ||
| 107672 | 107844 | Index *pIdx; /* Index for FROM table at pTabItem */ |
| 107673 | 107845 | int j; /* For looping over FROM tables */ |
| 107674 | 107846 | int bestJ = -1; /* The value of j */ |
| 107675 | 107847 | Bitmask m; /* Bitmask value for j or bestJ */ |
| 107676 | 107848 | int isOptimal; /* Iterator for optimal/non-optimal search */ |
| 107849 | + int ckOptimal; /* Do the optimal scan check */ | |
| 107677 | 107850 | int nUnconstrained; /* Number tables without INDEXED BY */ |
| 107678 | 107851 | Bitmask notIndexed; /* Mask of tables that cannot use an index */ |
| 107679 | 107852 | |
| 107680 | 107853 | memset(&bestPlan, 0, sizeof(bestPlan)); |
| 107681 | 107854 | bestPlan.rCost = SQLITE_BIG_DBL; |
| @@ -107706,14 +107879,12 @@ | ||
| 107706 | 107879 | ** |
| 107707 | 107880 | ** The second loop iteration is only performed if no optimal scan |
| 107708 | 107881 | ** strategies were found by the first iteration. This second iteration |
| 107709 | 107882 | ** is used to search for the lowest cost scan overall. |
| 107710 | 107883 | ** |
| 107711 | - ** Previous versions of SQLite performed only the second iteration - | |
| 107712 | - ** the next outermost loop was always that with the lowest overall | |
| 107713 | - ** cost. However, this meant that SQLite could select the wrong plan | |
| 107714 | - ** for scripts such as the following: | |
| 107884 | + ** Without the optimal scan step (the first iteration) a suboptimal | |
| 107885 | + ** plan might be chosen for queries like this: | |
| 107715 | 107886 | ** |
| 107716 | 107887 | ** CREATE TABLE t1(a, b); |
| 107717 | 107888 | ** CREATE TABLE t2(c, d); |
| 107718 | 107889 | ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a; |
| 107719 | 107890 | ** |
| @@ -107724,20 +107895,44 @@ | ||
| 107724 | 107895 | ** algorithm may choose to use t2 for the outer loop, which is a much |
| 107725 | 107896 | ** costlier approach. |
| 107726 | 107897 | */ |
| 107727 | 107898 | nUnconstrained = 0; |
| 107728 | 107899 | notIndexed = 0; |
| 107729 | - for(isOptimal=(iFrom<nTabList-1); isOptimal>=0 && bestJ<0; isOptimal--){ | |
| 107900 | + | |
| 107901 | + /* The optimal scan check only occurs if there are two or more tables | |
| 107902 | + ** available to be reordered */ | |
| 107903 | + if( iFrom==nTabList-1 ){ | |
| 107904 | + ckOptimal = 0; /* Common case of just one table in the FROM clause */ | |
| 107905 | + }else{ | |
| 107906 | + ckOptimal = -1; | |
| 107730 | 107907 | for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){ |
| 107731 | - int doNotReorder; /* True if this table should not be reordered */ | |
| 107732 | - | |
| 107733 | - doNotReorder = (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0; | |
| 107734 | - if( j!=iFrom && doNotReorder ) break; | |
| 107735 | 107908 | m = getMask(pMaskSet, sWBI.pSrc->iCursor); |
| 107736 | 107909 | if( (m & sWBI.notValid)==0 ){ |
| 107737 | 107910 | if( j==iFrom ) iFrom++; |
| 107738 | 107911 | continue; |
| 107912 | + } | |
| 107913 | + if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ) break; | |
| 107914 | + if( ++ckOptimal ) break; | |
| 107915 | + if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break; | |
| 107916 | + } | |
| 107917 | + } | |
| 107918 | + assert( ckOptimal==0 || ckOptimal==1 ); | |
| 107919 | + | |
| 107920 | + for(isOptimal=ckOptimal; isOptimal>=0 && bestJ<0; isOptimal--){ | |
| 107921 | + for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){ | |
| 107922 | + if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ){ | |
| 107923 | + /* This break and one like it in the ckOptimal computation loop | |
| 107924 | + ** above prevent table reordering across LEFT and CROSS JOINs. | |
| 107925 | + ** The LEFT JOIN case is necessary for correctness. The prohibition | |
| 107926 | + ** against reordering across a CROSS JOIN is an SQLite feature that | |
| 107927 | + ** allows the developer to control table reordering */ | |
| 107928 | + break; | |
| 107929 | + } | |
| 107930 | + m = getMask(pMaskSet, sWBI.pSrc->iCursor); | |
| 107931 | + if( (m & sWBI.notValid)==0 ){ | |
| 107932 | + assert( j>iFrom ); | |
| 107933 | + continue; | |
| 107739 | 107934 | } |
| 107740 | 107935 | sWBI.notReady = (isOptimal ? m : sWBI.notValid); |
| 107741 | 107936 | if( sWBI.pSrc->pIndex==0 ) nUnconstrained++; |
| 107742 | 107937 | |
| 107743 | 107938 | WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n", |
| @@ -107763,12 +107958,12 @@ | ||
| 107763 | 107958 | if( isOptimal && (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){ |
| 107764 | 107959 | notIndexed |= m; |
| 107765 | 107960 | } |
| 107766 | 107961 | if( isOptimal ){ |
| 107767 | 107962 | pWInfo->a[j].rOptCost = sWBI.cost.rCost; |
| 107768 | - }else if( iFrom<nTabList-1 ){ | |
| 107769 | - /* If two or more tables have nearly the same outer loop cost, | |
| 107963 | + }else if( ckOptimal ){ | |
| 107964 | + /* If two or more tables have nearly the same outer loop cost, but | |
| 107770 | 107965 | ** very different inner loop (optimal) cost, we want to choose |
| 107771 | 107966 | ** for the outer loop that table which benefits the least from |
| 107772 | 107967 | ** being in the inner loop. The following code scales the |
| 107773 | 107968 | ** outer loop cost estimate to accomplish that. */ |
| 107774 | 107969 | WHERETRACE((" scaling cost from %.1f to %.1f\n", |
| @@ -107809,15 +108004,23 @@ | ||
| 107809 | 108004 | sWBI.cost.rCost, sWBI.cost.plan.nRow, |
| 107810 | 108005 | sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags)); |
| 107811 | 108006 | bestPlan = sWBI.cost; |
| 107812 | 108007 | bestJ = j; |
| 107813 | 108008 | } |
| 107814 | - if( doNotReorder ) break; | |
| 108009 | + | |
| 108010 | + /* In a join like "w JOIN x LEFT JOIN y JOIN z" make sure that | |
| 108011 | + ** table y (and not table z) is always the next inner loop inside | |
| 108012 | + ** of table x. */ | |
| 108013 | + if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break; | |
| 107815 | 108014 | } |
| 107816 | 108015 | } |
| 107817 | 108016 | assert( bestJ>=0 ); |
| 107818 | 108017 | assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); |
| 108018 | + assert( bestJ==iFrom || (pTabList->a[iFrom].jointype & JT_LEFT)==0 ); | |
| 108019 | + testcase( bestJ>iFrom && (pTabList->a[iFrom].jointype & JT_CROSS)!=0 ); | |
| 108020 | + testcase( bestJ>iFrom && bestJ<nTabList-1 | |
| 108021 | + && (pTabList->a[bestJ+1].jointype & JT_LEFT)!=0 ); | |
| 107819 | 108022 | WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n" |
| 107820 | 108023 | " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n", |
| 107821 | 108024 | bestJ, pTabList->a[bestJ].pTab->zName, |
| 107822 | 108025 | pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow, |
| 107823 | 108026 | bestPlan.plan.nOBSat, bestPlan.plan.wsFlags)); |
| @@ -136543,11 +136746,12 @@ | ||
| 136543 | 136746 | ** would fit in a single node, use a smaller node-size. |
| 136544 | 136747 | */ |
| 136545 | 136748 | static int getNodeSize( |
| 136546 | 136749 | sqlite3 *db, /* Database handle */ |
| 136547 | 136750 | Rtree *pRtree, /* Rtree handle */ |
| 136548 | - int isCreate /* True for xCreate, false for xConnect */ | |
| 136751 | + int isCreate, /* True for xCreate, false for xConnect */ | |
| 136752 | + char **pzErr /* OUT: Error message, if any */ | |
| 136549 | 136753 | ){ |
| 136550 | 136754 | int rc; |
| 136551 | 136755 | char *zSql; |
| 136552 | 136756 | if( isCreate ){ |
| 136553 | 136757 | int iPageSize = 0; |
| @@ -136556,17 +136760,22 @@ | ||
| 136556 | 136760 | if( rc==SQLITE_OK ){ |
| 136557 | 136761 | pRtree->iNodeSize = iPageSize-64; |
| 136558 | 136762 | if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){ |
| 136559 | 136763 | pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS; |
| 136560 | 136764 | } |
| 136765 | + }else{ | |
| 136766 | + *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); | |
| 136561 | 136767 | } |
| 136562 | 136768 | }else{ |
| 136563 | 136769 | zSql = sqlite3_mprintf( |
| 136564 | 136770 | "SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1", |
| 136565 | 136771 | pRtree->zDb, pRtree->zName |
| 136566 | 136772 | ); |
| 136567 | 136773 | rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize); |
| 136774 | + if( rc!=SQLITE_OK ){ | |
| 136775 | + *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); | |
| 136776 | + } | |
| 136568 | 136777 | } |
| 136569 | 136778 | |
| 136570 | 136779 | sqlite3_free(zSql); |
| 136571 | 136780 | return rc; |
| 136572 | 136781 | } |
| @@ -136626,11 +136835,11 @@ | ||
| 136626 | 136835 | pRtree->eCoordType = eCoordType; |
| 136627 | 136836 | memcpy(pRtree->zDb, argv[1], nDb); |
| 136628 | 136837 | memcpy(pRtree->zName, argv[2], nName); |
| 136629 | 136838 | |
| 136630 | 136839 | /* Figure out the node size to use. */ |
| 136631 | - rc = getNodeSize(db, pRtree, isCreate); | |
| 136840 | + rc = getNodeSize(db, pRtree, isCreate, pzErr); | |
| 136632 | 136841 | |
| 136633 | 136842 | /* Create/Connect to the underlying relational database schema. If |
| 136634 | 136843 | ** that is successful, call sqlite3_declare_vtab() to configure |
| 136635 | 136844 | ** the r-tree table schema. |
| 136636 | 136845 | */ |
| 136637 | 136846 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -673,11 +673,11 @@ | |
| 673 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 674 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 675 | */ |
| 676 | #define SQLITE_VERSION "3.7.16" |
| 677 | #define SQLITE_VERSION_NUMBER 3007016 |
| 678 | #define SQLITE_SOURCE_ID "2012-12-21 16:15:35 ff6857b6ed6a46671006b75157d8cf853a816ef9" |
| 679 | |
| 680 | /* |
| 681 | ** CAPI3REF: Run-Time Library Version Numbers |
| 682 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 683 | ** |
| @@ -8238,10 +8238,15 @@ | |
| 8238 | ** A convenience macro that returns the number of elements in |
| 8239 | ** an array. |
| 8240 | */ |
| 8241 | #define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0]))) |
| 8242 | |
| 8243 | /* |
| 8244 | ** The following value as a destructor means to use sqlite3DbFree(). |
| 8245 | ** The sqlite3DbFree() routine requires two parameters instead of the |
| 8246 | ** one parameter that destructors normally want. So we have to introduce |
| 8247 | ** this magic value that the code knows to handle differently. Any |
| @@ -10042,10 +10047,11 @@ | |
| 10042 | #define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */ |
| 10043 | #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ |
| 10044 | #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ |
| 10045 | #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ |
| 10046 | #define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */ |
| 10047 | #define SQLITE_AllOpts 0xffff /* All optimizations */ |
| 10048 | |
| 10049 | /* |
| 10050 | ** Macros for testing whether or not optimizations are enabled or disabled. |
| 10051 | */ |
| @@ -10553,24 +10559,24 @@ | |
| 10553 | ** and the value of Index.onError indicate the which conflict resolution |
| 10554 | ** algorithm to employ whenever an attempt is made to insert a non-unique |
| 10555 | ** element. |
| 10556 | */ |
| 10557 | struct Index { |
| 10558 | char *zName; /* Name of this index */ |
| 10559 | int *aiColumn; /* Which columns are used by this index. 1st is 0 */ |
| 10560 | tRowcnt *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */ |
| 10561 | Table *pTable; /* The SQL table being indexed */ |
| 10562 | char *zColAff; /* String defining the affinity of each column */ |
| 10563 | Index *pNext; /* The next index associated with the same table */ |
| 10564 | Schema *pSchema; /* Schema containing this index */ |
| 10565 | u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */ |
| 10566 | char **azColl; /* Array of collation sequence names for index */ |
| 10567 | int nColumn; /* Number of columns in the table used by this index */ |
| 10568 | int tnum; /* Page containing root of this index in database file */ |
| 10569 | u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ |
| 10570 | u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ |
| 10571 | u8 bUnordered; /* Use this index for == or IN queries only */ |
| 10572 | #ifdef SQLITE_ENABLE_STAT3 |
| 10573 | int nSample; /* Number of elements in aSample[] */ |
| 10574 | tRowcnt avgEq; /* Average nEq value for key values not in aSample */ |
| 10575 | IndexSample *aSample; /* Samples of the left-most key */ |
| 10576 | #endif |
| @@ -10840,22 +10846,31 @@ | |
| 10840 | ** name. An expr/name combination can be used in several ways, such |
| 10841 | ** as the list of "expr AS ID" fields following a "SELECT" or in the |
| 10842 | ** list of "ID = expr" items in an UPDATE. A list of expressions can |
| 10843 | ** also be used as the argument to a function, in which case the a.zName |
| 10844 | ** field is not used. |
| 10845 | */ |
| 10846 | struct ExprList { |
| 10847 | int nExpr; /* Number of expressions on the list */ |
| 10848 | int iECursor; /* VDBE Cursor associated with this ExprList */ |
| 10849 | struct ExprList_item { /* For each expression in the list */ |
| 10850 | Expr *pExpr; /* The list of expressions */ |
| 10851 | char *zName; /* Token associated with this expression */ |
| 10852 | char *zSpan; /* Original text of the expression */ |
| 10853 | u8 sortOrder; /* 1 for DESC or 0 for ASC */ |
| 10854 | u8 done; /* A flag to indicate when processing is finished */ |
| 10855 | u16 iOrderByCol; /* For ORDER BY, column number in result set */ |
| 10856 | u16 iAlias; /* Index into Parse.aAlias[] for zName */ |
| 10857 | } *a; /* Alloc a power of two greater or equal to nExpr */ |
| 10858 | }; |
| 10859 | |
| 10860 | /* |
| 10861 | ** An instance of this structure is used by the parser to record both |
| @@ -12141,10 +12156,11 @@ | |
| 12141 | SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *); |
| 12142 | SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...); |
| 12143 | SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*); |
| 12144 | SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *, Expr *, int, int); |
| 12145 | SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*); |
| 12146 | SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*); |
| 12147 | SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); |
| 12148 | SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); |
| 12149 | SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int); |
| 12150 | SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *); |
| @@ -23289,15 +23305,11 @@ | |
| 23289 | { "pwrite64", (sqlite3_syscall_ptr)0, 0 }, |
| 23290 | #endif |
| 23291 | #define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\ |
| 23292 | aSyscall[13].pCurrent) |
| 23293 | |
| 23294 | #if SQLITE_ENABLE_LOCKING_STYLE |
| 23295 | { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 }, |
| 23296 | #else |
| 23297 | { "fchmod", (sqlite3_syscall_ptr)0, 0 }, |
| 23298 | #endif |
| 23299 | #define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) |
| 23300 | |
| 23301 | #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE |
| 23302 | { "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 }, |
| 23303 | #else |
| @@ -23318,13 +23330,10 @@ | |
| 23318 | #define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent) |
| 23319 | |
| 23320 | { "fchown", (sqlite3_syscall_ptr)posixFchown, 0 }, |
| 23321 | #define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent) |
| 23322 | |
| 23323 | { "umask", (sqlite3_syscall_ptr)umask, 0 }, |
| 23324 | #define osUmask ((mode_t(*)(mode_t))aSyscall[21].pCurrent) |
| 23325 | |
| 23326 | }; /* End of the overrideable system calls */ |
| 23327 | |
| 23328 | /* |
| 23329 | ** This is the xSetSystemCall() method of sqlite3_vfs for all of the |
| 23330 | ** "unix" VFSes. Return SQLITE_OK opon successfully updating the |
| @@ -23425,31 +23434,29 @@ | |
| 23425 | ** process that is able to write to the database will also be able to |
| 23426 | ** recover the hot journals. |
| 23427 | */ |
| 23428 | static int robust_open(const char *z, int f, mode_t m){ |
| 23429 | int fd; |
| 23430 | mode_t m2; |
| 23431 | mode_t origM = 0; |
| 23432 | if( m==0 ){ |
| 23433 | m2 = SQLITE_DEFAULT_FILE_PERMISSIONS; |
| 23434 | }else{ |
| 23435 | m2 = m; |
| 23436 | origM = osUmask(0); |
| 23437 | } |
| 23438 | do{ |
| 23439 | #if defined(O_CLOEXEC) |
| 23440 | fd = osOpen(z,f|O_CLOEXEC,m2); |
| 23441 | #else |
| 23442 | fd = osOpen(z,f,m2); |
| 23443 | #endif |
| 23444 | }while( fd<0 && errno==EINTR ); |
| 23445 | if( m ){ |
| 23446 | osUmask(origM); |
| 23447 | } |
| 23448 | #if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC==0) |
| 23449 | if( fd>=0 ) osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); |
| 23450 | #endif |
| 23451 | return fd; |
| 23452 | } |
| 23453 | |
| 23454 | /* |
| 23455 | ** Helper functions to obtain and relinquish the global mutex. The |
| @@ -29871,11 +29878,11 @@ | |
| 29871 | }; |
| 29872 | unsigned int i; /* Loop counter */ |
| 29873 | |
| 29874 | /* Double-check that the aSyscall[] array has been constructed |
| 29875 | ** correctly. See ticket [bb3a86e890c8e96ab] */ |
| 29876 | assert( ArraySize(aSyscall)==22 ); |
| 29877 | |
| 29878 | /* Register all VFSes defined in the aVfs[] array */ |
| 29879 | for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ |
| 29880 | sqlite3_vfs_register(&aVfs[i], i==0); |
| 29881 | } |
| @@ -72655,10 +72662,39 @@ | |
| 72655 | } |
| 72656 | } |
| 72657 | return 0; |
| 72658 | } |
| 72659 | |
| 72660 | |
| 72661 | /* |
| 72662 | ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up |
| 72663 | ** that name in the set of source tables in pSrcList and make the pExpr |
| 72664 | ** expression node refer back to that source column. The following changes |
| @@ -72710,44 +72746,63 @@ | |
| 72710 | |
| 72711 | /* Initialize the node to no-match */ |
| 72712 | pExpr->iTable = -1; |
| 72713 | pExpr->pTab = 0; |
| 72714 | ExprSetIrreducible(pExpr); |
| 72715 | |
| 72716 | /* Start at the inner-most context and move outward until a match is found */ |
| 72717 | while( pNC && cnt==0 ){ |
| 72718 | ExprList *pEList; |
| 72719 | SrcList *pSrcList = pNC->pSrcList; |
| 72720 | |
| 72721 | if( pSrcList ){ |
| 72722 | for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){ |
| 72723 | Table *pTab; |
| 72724 | int iDb; |
| 72725 | Column *pCol; |
| 72726 | |
| 72727 | pTab = pItem->pTab; |
| 72728 | assert( pTab!=0 && pTab->zName!=0 ); |
| 72729 | iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| 72730 | assert( pTab->nCol>0 ); |
| 72731 | if( zTab ){ |
| 72732 | if( pItem->zAlias ){ |
| 72733 | char *zTabName = pItem->zAlias; |
| 72734 | if( sqlite3StrICmp(zTabName, zTab)!=0 ) continue; |
| 72735 | }else{ |
| 72736 | char *zTabName = pTab->zName; |
| 72737 | if( NEVER(zTabName==0) || sqlite3StrICmp(zTabName, zTab)!=0 ){ |
| 72738 | continue; |
| 72739 | } |
| 72740 | if( zDb!=0 && sqlite3StrICmp(db->aDb[iDb].zName, zDb)!=0 ){ |
| 72741 | continue; |
| 72742 | } |
| 72743 | } |
| 72744 | } |
| 72745 | if( 0==(cntTab++) ){ |
| 72746 | pExpr->iTable = pItem->iCursor; |
| 72747 | pExpr->pTab = pTab; |
| 72748 | pSchema = pTab->pSchema; |
| 72749 | pMatch = pItem; |
| 72750 | } |
| 72751 | for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){ |
| 72752 | if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ |
| 72753 | /* If there has been exactly one prior match and this match |
| @@ -72757,21 +72812,23 @@ | |
| 72757 | if( cnt==1 ){ |
| 72758 | if( pItem->jointype & JT_NATURAL ) continue; |
| 72759 | if( nameInUsingClause(pItem->pUsing, zCol) ) continue; |
| 72760 | } |
| 72761 | cnt++; |
| 72762 | pExpr->iTable = pItem->iCursor; |
| 72763 | pExpr->pTab = pTab; |
| 72764 | pMatch = pItem; |
| 72765 | pSchema = pTab->pSchema; |
| 72766 | /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ |
| 72767 | pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; |
| 72768 | break; |
| 72769 | } |
| 72770 | } |
| 72771 | } |
| 72772 | } |
| 72773 | |
| 72774 | #ifndef SQLITE_OMIT_TRIGGER |
| 72775 | /* If we have not already resolved the name, then maybe |
| 72776 | ** it is a new.* or old.* trigger argument reference |
| 72777 | */ |
| @@ -73102,11 +73159,11 @@ | |
| 73102 | #endif |
| 73103 | if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){ |
| 73104 | sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId); |
| 73105 | pNC->nErr++; |
| 73106 | is_agg = 0; |
| 73107 | }else if( no_such_func ){ |
| 73108 | sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId); |
| 73109 | pNC->nErr++; |
| 73110 | }else if( wrong_num_args ){ |
| 73111 | sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()", |
| 73112 | nId, zId); |
| @@ -77065,10 +77122,16 @@ | |
| 77065 | for(i=0; i<pList->nExpr; i++){ |
| 77066 | sqlite3ExplainPrintf(pOut, "item[%d] = ", i); |
| 77067 | sqlite3ExplainPush(pOut); |
| 77068 | sqlite3ExplainExpr(pOut, pList->a[i].pExpr); |
| 77069 | sqlite3ExplainPop(pOut); |
| 77070 | if( i<pList->nExpr-1 ){ |
| 77071 | sqlite3ExplainNL(pOut); |
| 77072 | } |
| 77073 | } |
| 77074 | sqlite3ExplainPop(pOut); |
| @@ -92739,13 +92802,15 @@ | |
| 92739 | if( sqlite3StrICmp(zLeft, "table_info")==0 && zRight ){ |
| 92740 | Table *pTab; |
| 92741 | if( sqlite3ReadSchema(pParse) ) goto pragma_out; |
| 92742 | pTab = sqlite3FindTable(db, zRight, zDb); |
| 92743 | if( pTab ){ |
| 92744 | int i; |
| 92745 | int nHidden = 0; |
| 92746 | Column *pCol; |
| 92747 | sqlite3VdbeSetNumCols(v, 6); |
| 92748 | pParse->nMem = 6; |
| 92749 | sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cid", SQLITE_STATIC); |
| 92750 | sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC); |
| 92751 | sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "type", SQLITE_STATIC); |
| @@ -92766,12 +92831,18 @@ | |
| 92766 | if( pCol->zDflt ){ |
| 92767 | sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)pCol->zDflt, 0); |
| 92768 | }else{ |
| 92769 | sqlite3VdbeAddOp2(v, OP_Null, 0, 5); |
| 92770 | } |
| 92771 | sqlite3VdbeAddOp2(v, OP_Integer, |
| 92772 | (pCol->colFlags&COLFLAG_PRIMKEY)!=0, 6); |
| 92773 | sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6); |
| 92774 | } |
| 92775 | } |
| 92776 | }else |
| 92777 | |
| @@ -93516,11 +93587,11 @@ | |
| 93516 | sqlite3_rekey(db, zKey, i/2); |
| 93517 | } |
| 93518 | }else |
| 93519 | #endif |
| 93520 | #if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) |
| 93521 | if( sqlite3StrICmp(zLeft, "activate_extensions")==0 ){ |
| 93522 | #ifdef SQLITE_HAS_CODEC |
| 93523 | if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){ |
| 93524 | sqlite3_activate_see(&zRight[4]); |
| 93525 | } |
| 93526 | #endif |
| @@ -95756,12 +95827,10 @@ | |
| 95756 | |
| 95757 | for(i=0, pCol=aCol; i<nCol; i++, pCol++){ |
| 95758 | /* Get an appropriate name for the column |
| 95759 | */ |
| 95760 | p = sqlite3ExprSkipCollate(pEList->a[i].pExpr); |
| 95761 | assert( p->pRight==0 || ExprHasProperty(p->pRight, EP_IntValue) |
| 95762 | || p->pRight->u.zToken==0 || p->pRight->u.zToken[0]!=0 ); |
| 95763 | if( (zName = pEList->a[i].zName)!=0 ){ |
| 95764 | /* If the column contains an "AS <name>" phrase, use <name> as the name */ |
| 95765 | zName = sqlite3DbStrDup(db, zName); |
| 95766 | }else{ |
| 95767 | Expr *pColExpr = p; /* The expression that is the result column name */ |
| @@ -95795,10 +95864,13 @@ | |
| 95795 | */ |
| 95796 | nName = sqlite3Strlen30(zName); |
| 95797 | for(j=cnt=0; j<i; j++){ |
| 95798 | if( sqlite3StrICmp(aCol[j].zName, zName)==0 ){ |
| 95799 | char *zNewName; |
| 95800 | zName[nName] = 0; |
| 95801 | zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt); |
| 95802 | sqlite3DbFree(db, zName); |
| 95803 | zName = zNewName; |
| 95804 | j = -1; |
| @@ -97711,10 +97783,11 @@ | |
| 97711 | int i, j, k; |
| 97712 | SrcList *pTabList; |
| 97713 | ExprList *pEList; |
| 97714 | struct SrcList_item *pFrom; |
| 97715 | sqlite3 *db = pParse->db; |
| 97716 | |
| 97717 | if( db->mallocFailed ){ |
| 97718 | return WRC_Abort; |
| 97719 | } |
| 97720 | if( NEVER(p->pSrc==0) || (p->selFlags & SF_Expanded)!=0 ){ |
| @@ -97796,11 +97869,11 @@ | |
| 97796 | ** |
| 97797 | ** The first loop just checks to see if there are any "*" operators |
| 97798 | ** that need expanding. |
| 97799 | */ |
| 97800 | for(k=0; k<pEList->nExpr; k++){ |
| 97801 | Expr *pE = pEList->a[k].pExpr; |
| 97802 | if( pE->op==TK_ALL ) break; |
| 97803 | assert( pE->op!=TK_DOT || pE->pRight!=0 ); |
| 97804 | assert( pE->op!=TK_DOT || (pE->pLeft!=0 && pE->pLeft->op==TK_ID) ); |
| 97805 | if( pE->op==TK_DOT && pE->pRight->op==TK_ALL ) break; |
| 97806 | } |
| @@ -97814,14 +97887,22 @@ | |
| 97814 | ExprList *pNew = 0; |
| 97815 | int flags = pParse->db->flags; |
| 97816 | int longNames = (flags & SQLITE_FullColNames)!=0 |
| 97817 | && (flags & SQLITE_ShortColNames)==0; |
| 97818 | |
| 97819 | for(k=0; k<pEList->nExpr; k++){ |
| 97820 | Expr *pE = a[k].pExpr; |
| 97821 | assert( pE->op!=TK_DOT || pE->pRight!=0 ); |
| 97822 | if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pE->pRight->op!=TK_ALL) ){ |
| 97823 | /* This particular expression does not need to be expanded. |
| 97824 | */ |
| 97825 | pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr); |
| 97826 | if( pNew ){ |
| 97827 | pNew->a[pNew->nExpr-1].zName = a[k].zName; |
| @@ -97832,44 +97913,56 @@ | |
| 97832 | a[k].pExpr = 0; |
| 97833 | }else{ |
| 97834 | /* This expression is a "*" or a "TABLE.*" and needs to be |
| 97835 | ** expanded. */ |
| 97836 | int tableSeen = 0; /* Set to 1 when TABLE matches */ |
| 97837 | char *zTName; /* text of name of TABLE */ |
| 97838 | if( pE->op==TK_DOT ){ |
| 97839 | assert( pE->pLeft!=0 ); |
| 97840 | assert( !ExprHasProperty(pE->pLeft, EP_IntValue) ); |
| 97841 | zTName = pE->pLeft->u.zToken; |
| 97842 | }else{ |
| 97843 | zTName = 0; |
| 97844 | } |
| 97845 | for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){ |
| 97846 | Table *pTab = pFrom->pTab; |
| 97847 | char *zTabName = pFrom->zAlias; |
| 97848 | if( zTabName==0 ){ |
| 97849 | zTabName = pTab->zName; |
| 97850 | } |
| 97851 | if( db->mallocFailed ) break; |
| 97852 | if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){ |
| 97853 | continue; |
| 97854 | } |
| 97855 | tableSeen = 1; |
| 97856 | for(j=0; j<pTab->nCol; j++){ |
| 97857 | Expr *pExpr, *pRight; |
| 97858 | char *zName = pTab->aCol[j].zName; |
| 97859 | char *zColname; /* The computed column name */ |
| 97860 | char *zToFree; /* Malloced string that needs to be freed */ |
| 97861 | Token sColname; /* Computed column name as a token */ |
| 97862 | |
| 97863 | /* If a column is marked as 'hidden' (currently only possible |
| 97864 | ** for virtual tables), do not include it in the expanded |
| 97865 | ** result-set list. |
| 97866 | */ |
| 97867 | if( IsHiddenColumn(&pTab->aCol[j]) ){ |
| 97868 | assert(IsVirtual(pTab)); |
| 97869 | continue; |
| 97870 | } |
| 97871 | |
| 97872 | if( i>0 && zTName==0 ){ |
| 97873 | if( (pFrom->jointype & JT_NATURAL)!=0 |
| 97874 | && tableAndColumnIndex(pTabList, i, zName, 0, 0) |
| 97875 | ){ |
| @@ -97888,10 +97981,14 @@ | |
| 97888 | zToFree = 0; |
| 97889 | if( longNames || pTabList->nSrc>1 ){ |
| 97890 | Expr *pLeft; |
| 97891 | pLeft = sqlite3Expr(db, TK_ID, zTabName); |
| 97892 | pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0); |
| 97893 | if( longNames ){ |
| 97894 | zColname = sqlite3MPrintf(db, "%s.%s", zTabName, zName); |
| 97895 | zToFree = zColname; |
| 97896 | } |
| 97897 | }else{ |
| @@ -97899,10 +97996,22 @@ | |
| 97899 | } |
| 97900 | pNew = sqlite3ExprListAppend(pParse, pNew, pExpr); |
| 97901 | sColname.z = zColname; |
| 97902 | sColname.n = sqlite3Strlen30(zColname); |
| 97903 | sqlite3ExprListSetName(pParse, pNew, &sColname, 0); |
| 97904 | sqlite3DbFree(db, zToFree); |
| 97905 | } |
| 97906 | } |
| 97907 | if( !tableSeen ){ |
| 97908 | if( zTName ){ |
| @@ -102699,12 +102808,12 @@ | |
| 102699 | Expr *pExpr; /* Pointer to the subexpression that is this term */ |
| 102700 | int iParent; /* Disable pWC->a[iParent] when this term disabled */ |
| 102701 | int leftCursor; /* Cursor number of X in "X <op> <expr>" */ |
| 102702 | union { |
| 102703 | int leftColumn; /* Column number of X in "X <op> <expr>" */ |
| 102704 | WhereOrInfo *pOrInfo; /* Extra information if eOperator==WO_OR */ |
| 102705 | WhereAndInfo *pAndInfo; /* Extra information if eOperator==WO_AND */ |
| 102706 | } u; |
| 102707 | u16 eOperator; /* A WO_xx value describing <op> */ |
| 102708 | u8 wtFlags; /* TERM_xxx bit flags. See below */ |
| 102709 | u8 nChild; /* Number of children that must disable us */ |
| 102710 | WhereClause *pWC; /* The clause this term is part of */ |
| @@ -102828,10 +102937,11 @@ | |
| 102828 | #define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) |
| 102829 | #define WO_MATCH 0x040 |
| 102830 | #define WO_ISNULL 0x080 |
| 102831 | #define WO_OR 0x100 /* Two or more OR-connected terms */ |
| 102832 | #define WO_AND 0x200 /* Two or more AND-connected terms */ |
| 102833 | #define WO_NOOP 0x800 /* This term does not restrict search space */ |
| 102834 | |
| 102835 | #define WO_ALL 0xfff /* Mask of all possible WO_* values */ |
| 102836 | #define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */ |
| 102837 | |
| @@ -103230,58 +103340,112 @@ | |
| 103230 | /* |
| 103231 | ** Search for a term in the WHERE clause that is of the form "X <op> <expr>" |
| 103232 | ** where X is a reference to the iColumn of table iCur and <op> is one of |
| 103233 | ** the WO_xx operator codes specified by the op parameter. |
| 103234 | ** Return a pointer to the term. Return 0 if not found. |
| 103235 | */ |
| 103236 | static WhereTerm *findTerm( |
| 103237 | WhereClause *pWC, /* The WHERE clause to be searched */ |
| 103238 | int iCur, /* Cursor number of LHS */ |
| 103239 | int iColumn, /* Column number of LHS */ |
| 103240 | Bitmask notReady, /* RHS must not overlap with this mask */ |
| 103241 | u32 op, /* Mask of WO_xx values describing operator */ |
| 103242 | Index *pIdx /* Must be compatible with this index, if not NULL */ |
| 103243 | ){ |
| 103244 | WhereTerm *pTerm; |
| 103245 | int k; |
| 103246 | assert( iCur>=0 ); |
| 103247 | op &= WO_ALL; |
| 103248 | for(; pWC; pWC=pWC->pOuter){ |
| 103249 | for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ |
| 103250 | if( pTerm->leftCursor==iCur |
| 103251 | && (pTerm->prereqRight & notReady)==0 |
| 103252 | && pTerm->u.leftColumn==iColumn |
| 103253 | && (pTerm->eOperator & op)!=0 |
| 103254 | ){ |
| 103255 | if( iColumn>=0 && pIdx && pTerm->eOperator!=WO_ISNULL ){ |
| 103256 | Expr *pX = pTerm->pExpr; |
| 103257 | CollSeq *pColl; |
| 103258 | char idxaff; |
| 103259 | int j; |
| 103260 | Parse *pParse = pWC->pParse; |
| 103261 | |
| 103262 | idxaff = pIdx->pTable->aCol[iColumn].affinity; |
| 103263 | if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue; |
| 103264 | |
| 103265 | /* Figure out the collation sequence required from an index for |
| 103266 | ** it to be useful for optimising expression pX. Store this |
| 103267 | ** value in variable pColl. |
| 103268 | */ |
| 103269 | assert(pX->pLeft); |
| 103270 | pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); |
| 103271 | if( pColl==0 ) pColl = pParse->db->pDfltColl; |
| 103272 | |
| 103273 | for(j=0; pIdx->aiColumn[j]!=iColumn; j++){ |
| 103274 | if( NEVER(j>=pIdx->nColumn) ) return 0; |
| 103275 | } |
| 103276 | if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue; |
| 103277 | } |
| 103278 | return pTerm; |
| 103279 | } |
| 103280 | } |
| 103281 | } |
| 103282 | return 0; |
| 103283 | } |
| 103284 | |
| 103285 | /* Forward reference */ |
| 103286 | static void exprAnalyze(SrcList*, WhereClause*, int); |
| 103287 | |
| @@ -103555,11 +103719,10 @@ | |
| 103555 | indexable = ~(Bitmask)0; |
| 103556 | chngToIN = ~(pWC->vmask); |
| 103557 | for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0 && indexable; i--, pOrTerm++){ |
| 103558 | if( (pOrTerm->eOperator & WO_SINGLE)==0 ){ |
| 103559 | WhereAndInfo *pAndInfo; |
| 103560 | assert( pOrTerm->eOperator==0 ); |
| 103561 | assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 ); |
| 103562 | chngToIN = 0; |
| 103563 | pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo)); |
| 103564 | if( pAndInfo ){ |
| 103565 | WhereClause *pAndWC; |
| @@ -103594,11 +103757,11 @@ | |
| 103594 | if( pOrTerm->wtFlags & TERM_VIRTUAL ){ |
| 103595 | WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent]; |
| 103596 | b |= getMask(pMaskSet, pOther->leftCursor); |
| 103597 | } |
| 103598 | indexable &= b; |
| 103599 | if( pOrTerm->eOperator!=WO_EQ ){ |
| 103600 | chngToIN = 0; |
| 103601 | }else{ |
| 103602 | chngToIN &= b; |
| 103603 | } |
| 103604 | } |
| @@ -103645,11 +103808,11 @@ | |
| 103645 | ** and column is found but leave okToChngToIN false if not found. |
| 103646 | */ |
| 103647 | for(j=0; j<2 && !okToChngToIN; j++){ |
| 103648 | pOrTerm = pOrWc->a; |
| 103649 | for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){ |
| 103650 | assert( pOrTerm->eOperator==WO_EQ ); |
| 103651 | pOrTerm->wtFlags &= ~TERM_OR_OK; |
| 103652 | if( pOrTerm->leftCursor==iCursor ){ |
| 103653 | /* This is the 2-bit case and we are on the second iteration and |
| 103654 | ** current term is from the first iteration. So skip this term. */ |
| 103655 | assert( j==1 ); |
| @@ -103671,21 +103834,21 @@ | |
| 103671 | } |
| 103672 | if( i<0 ){ |
| 103673 | /* No candidate table+column was found. This can only occur |
| 103674 | ** on the second iteration */ |
| 103675 | assert( j==1 ); |
| 103676 | assert( (chngToIN&(chngToIN-1))==0 ); |
| 103677 | assert( chngToIN==getMask(pMaskSet, iCursor) ); |
| 103678 | break; |
| 103679 | } |
| 103680 | testcase( j==1 ); |
| 103681 | |
| 103682 | /* We have found a candidate table and column. Check to see if that |
| 103683 | ** table and column is common to every term in the OR clause */ |
| 103684 | okToChngToIN = 1; |
| 103685 | for(; i>=0 && okToChngToIN; i--, pOrTerm++){ |
| 103686 | assert( pOrTerm->eOperator==WO_EQ ); |
| 103687 | if( pOrTerm->leftCursor!=iCursor ){ |
| 103688 | pOrTerm->wtFlags &= ~TERM_OR_OK; |
| 103689 | }else if( pOrTerm->u.leftColumn!=iColumn ){ |
| 103690 | okToChngToIN = 0; |
| 103691 | }else{ |
| @@ -103717,11 +103880,11 @@ | |
| 103717 | Expr *pLeft = 0; /* The LHS of the IN operator */ |
| 103718 | Expr *pNew; /* The complete IN operator */ |
| 103719 | |
| 103720 | for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){ |
| 103721 | if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue; |
| 103722 | assert( pOrTerm->eOperator==WO_EQ ); |
| 103723 | assert( pOrTerm->leftCursor==iCursor ); |
| 103724 | assert( pOrTerm->u.leftColumn==iColumn ); |
| 103725 | pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); |
| 103726 | pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup); |
| 103727 | pLeft = pOrTerm->pExpr->pLeft; |
| @@ -103746,11 +103909,10 @@ | |
| 103746 | pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */ |
| 103747 | } |
| 103748 | } |
| 103749 | } |
| 103750 | #endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */ |
| 103751 | |
| 103752 | |
| 103753 | /* |
| 103754 | ** The input to this routine is an WhereTerm structure with only the |
| 103755 | ** "pExpr" field filled in. The job of this routine is to analyze the |
| 103756 | ** subexpression and populate all the other fields of the WhereTerm |
| @@ -103816,21 +103978,23 @@ | |
| 103816 | } |
| 103817 | pTerm->prereqAll = prereqAll; |
| 103818 | pTerm->leftCursor = -1; |
| 103819 | pTerm->iParent = -1; |
| 103820 | pTerm->eOperator = 0; |
| 103821 | if( allowedOp(op) && (pTerm->prereqRight & prereqLeft)==0 ){ |
| 103822 | Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft); |
| 103823 | Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight); |
| 103824 | if( pLeft->op==TK_COLUMN ){ |
| 103825 | pTerm->leftCursor = pLeft->iTable; |
| 103826 | pTerm->u.leftColumn = pLeft->iColumn; |
| 103827 | pTerm->eOperator = operatorMask(op); |
| 103828 | } |
| 103829 | if( pRight && pRight->op==TK_COLUMN ){ |
| 103830 | WhereTerm *pNew; |
| 103831 | Expr *pDup; |
| 103832 | if( pTerm->leftCursor>=0 ){ |
| 103833 | int idxNew; |
| 103834 | pDup = sqlite3ExprDup(db, pExpr, 0); |
| 103835 | if( db->mallocFailed ){ |
| 103836 | sqlite3ExprDelete(db, pDup); |
| @@ -103841,10 +104005,17 @@ | |
| 103841 | pNew = &pWC->a[idxNew]; |
| 103842 | pNew->iParent = idxTerm; |
| 103843 | pTerm = &pWC->a[idxTerm]; |
| 103844 | pTerm->nChild = 1; |
| 103845 | pTerm->wtFlags |= TERM_COPIED; |
| 103846 | }else{ |
| 103847 | pDup = pExpr; |
| 103848 | pNew = pTerm; |
| 103849 | } |
| 103850 | exprCommute(pParse, pDup); |
| @@ -103852,11 +104023,11 @@ | |
| 103852 | pNew->leftCursor = pLeft->iTable; |
| 103853 | pNew->u.leftColumn = pLeft->iColumn; |
| 103854 | testcase( (prereqLeft | extraRight) != prereqLeft ); |
| 103855 | pNew->prereqRight = prereqLeft | extraRight; |
| 103856 | pNew->prereqAll = prereqAll; |
| 103857 | pNew->eOperator = operatorMask(pDup->op); |
| 103858 | } |
| 103859 | } |
| 103860 | |
| 103861 | #ifndef SQLITE_OMIT_BETWEEN_OPTIMIZATION |
| 103862 | /* If a term is the BETWEEN operator, create two new virtual terms |
| @@ -104311,11 +104482,11 @@ | |
| 104311 | return; |
| 104312 | } |
| 104313 | |
| 104314 | /* Search the WHERE clause terms for a usable WO_OR term. */ |
| 104315 | for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ |
| 104316 | if( pTerm->eOperator==WO_OR |
| 104317 | && ((pTerm->prereqAll & ~maskSrc) & p->notReady)==0 |
| 104318 | && (pTerm->u.pOrInfo->indexable & maskSrc)!=0 |
| 104319 | ){ |
| 104320 | WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc; |
| 104321 | WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm]; |
| @@ -104332,11 +104503,11 @@ | |
| 104332 | sBOI.ppIdxInfo = 0; |
| 104333 | for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){ |
| 104334 | WHERETRACE(("... Multi-index OR testing for term %d of %d....\n", |
| 104335 | (pOrTerm - pOrWC->a), (pTerm - pWC->a) |
| 104336 | )); |
| 104337 | if( pOrTerm->eOperator==WO_AND ){ |
| 104338 | sBOI.pWC = &pOrTerm->u.pAndInfo->wc; |
| 104339 | bestIndex(&sBOI); |
| 104340 | }else if( pOrTerm->leftCursor==iCur ){ |
| 104341 | WhereClause tempWC; |
| 104342 | tempWC.pParse = pWC->pParse; |
| @@ -104393,11 +104564,11 @@ | |
| 104393 | struct SrcList_item *pSrc, /* Table we are trying to access */ |
| 104394 | Bitmask notReady /* Tables in outer loops of the join */ |
| 104395 | ){ |
| 104396 | char aff; |
| 104397 | if( pTerm->leftCursor!=pSrc->iCursor ) return 0; |
| 104398 | if( pTerm->eOperator!=WO_EQ ) return 0; |
| 104399 | if( (pTerm->prereqRight & notReady)!=0 ) return 0; |
| 104400 | aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; |
| 104401 | if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; |
| 104402 | return 1; |
| 104403 | } |
| @@ -104655,13 +104826,13 @@ | |
| 104655 | |
| 104656 | /* Count the number of possible WHERE clause constraints referring |
| 104657 | ** to this virtual table */ |
| 104658 | for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 104659 | if( pTerm->leftCursor != pSrc->iCursor ) continue; |
| 104660 | assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 ); |
| 104661 | testcase( pTerm->eOperator==WO_IN ); |
| 104662 | testcase( pTerm->eOperator==WO_ISNULL ); |
| 104663 | if( pTerm->eOperator & (WO_ISNULL) ) continue; |
| 104664 | if( pTerm->wtFlags & TERM_VNULL ) continue; |
| 104665 | nTerm++; |
| 104666 | } |
| 104667 | |
| @@ -104708,18 +104879,18 @@ | |
| 104708 | pUsage; |
| 104709 | |
| 104710 | for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 104711 | u8 op; |
| 104712 | if( pTerm->leftCursor != pSrc->iCursor ) continue; |
| 104713 | assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 ); |
| 104714 | testcase( pTerm->eOperator==WO_IN ); |
| 104715 | testcase( pTerm->eOperator==WO_ISNULL ); |
| 104716 | if( pTerm->eOperator & (WO_ISNULL) ) continue; |
| 104717 | if( pTerm->wtFlags & TERM_VNULL ) continue; |
| 104718 | pIdxCons[j].iColumn = pTerm->u.leftColumn; |
| 104719 | pIdxCons[j].iTermOffset = i; |
| 104720 | op = (u8)pTerm->eOperator; |
| 104721 | if( op==WO_IN ) op = WO_EQ; |
| 104722 | pIdxCons[j].op = op; |
| 104723 | /* The direct assignment in the previous line is possible only because |
| 104724 | ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The |
| 104725 | ** following asserts verify this fact. */ |
| @@ -104885,11 +105056,11 @@ | |
| 104885 | pUsage = pIdxInfo->aConstraintUsage; |
| 104886 | for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ |
| 104887 | j = pIdxCons->iTermOffset; |
| 104888 | pTerm = &pWC->a[j]; |
| 104889 | if( (pTerm->prereqRight&p->notReady)==0 |
| 104890 | && (bAllowIN || pTerm->eOperator!=WO_IN) |
| 104891 | ){ |
| 104892 | pIdxCons->usable = 1; |
| 104893 | }else{ |
| 104894 | pIdxCons->usable = 0; |
| 104895 | } |
| @@ -104917,11 +105088,11 @@ | |
| 104917 | for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ |
| 104918 | if( pUsage[i].argvIndex>0 ){ |
| 104919 | j = pIdxCons->iTermOffset; |
| 104920 | pTerm = &pWC->a[j]; |
| 104921 | p->cost.used |= pTerm->prereqRight; |
| 104922 | if( pTerm->eOperator==WO_IN && pUsage[i].omit==0 ){ |
| 104923 | /* Do not attempt to use an IN constraint if the virtual table |
| 104924 | ** says that the equivalent EQ constraint cannot be safely omitted. |
| 104925 | ** If we do attempt to use such a constraint, some rows might be |
| 104926 | ** repeated in the output. */ |
| 104927 | break; |
| @@ -105223,28 +105394,28 @@ | |
| 105223 | u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; |
| 105224 | |
| 105225 | if( pLower ){ |
| 105226 | Expr *pExpr = pLower->pExpr->pRight; |
| 105227 | rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); |
| 105228 | assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE ); |
| 105229 | if( rc==SQLITE_OK |
| 105230 | && whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK |
| 105231 | ){ |
| 105232 | iLower = a[0]; |
| 105233 | if( pLower->eOperator==WO_GT ) iLower += a[1]; |
| 105234 | } |
| 105235 | sqlite3ValueFree(pRangeVal); |
| 105236 | } |
| 105237 | if( rc==SQLITE_OK && pUpper ){ |
| 105238 | Expr *pExpr = pUpper->pExpr->pRight; |
| 105239 | rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); |
| 105240 | assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE ); |
| 105241 | if( rc==SQLITE_OK |
| 105242 | && whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK |
| 105243 | ){ |
| 105244 | iUpper = a[0]; |
| 105245 | if( pUpper->eOperator==WO_LE ) iUpper += a[1]; |
| 105246 | } |
| 105247 | sqlite3ValueFree(pRangeVal); |
| 105248 | } |
| 105249 | if( rc==SQLITE_OK ){ |
| 105250 | if( iUpper<=iLower ){ |
| @@ -105548,16 +105719,16 @@ | |
| 105548 | ** if there are any X= or X IS NULL constraints in the WHERE clause. */ |
| 105549 | pConstraint = findTerm(p->pWC, base, iColumn, p->notReady, |
| 105550 | WO_EQ|WO_ISNULL|WO_IN, pIdx); |
| 105551 | if( pConstraint==0 ){ |
| 105552 | isEq = 0; |
| 105553 | }else if( pConstraint->eOperator==WO_IN ){ |
| 105554 | /* Constraints of the form: "X IN ..." cannot be used with an ORDER BY |
| 105555 | ** because we do not know in what order the values on the RHS of the IN |
| 105556 | ** operator will occur. */ |
| 105557 | break; |
| 105558 | }else if( pConstraint->eOperator==WO_ISNULL ){ |
| 105559 | uniqueNotNull = 0; |
| 105560 | isEq = 1; /* "X IS NULL" means X has only a single value */ |
| 105561 | }else if( pConstraint->prereqRight==0 ){ |
| 105562 | isEq = 1; /* Constraint "X=constant" means X has only a single value */ |
| 105563 | }else{ |
| @@ -105903,11 +106074,11 @@ | |
| 105903 | int bRev = 2; |
| 105904 | WHERETRACE((" --> before isSortingIndex: nPriorSat=%d\n",nPriorSat)); |
| 105905 | pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev); |
| 105906 | WHERETRACE((" --> after isSortingIndex: bRev=%d nOBSat=%d\n", |
| 105907 | bRev, pc.plan.nOBSat)); |
| 105908 | if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_UNIQUE)!=0 ){ |
| 105909 | pc.plan.wsFlags |= WHERE_ORDERED; |
| 105910 | } |
| 105911 | if( nOrderBy==pc.plan.nOBSat ){ |
| 105912 | bSort = 0; |
| 105913 | pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE; |
| @@ -105966,16 +106137,17 @@ | |
| 105966 | */ |
| 105967 | if( pc.plan.nRow>(double)1 && pc.plan.nEq==1 |
| 105968 | && pFirstTerm!=0 && aiRowEst[1]>1 ){ |
| 105969 | assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 ); |
| 105970 | if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){ |
| 105971 | testcase( pFirstTerm->eOperator==WO_EQ ); |
| 105972 | testcase( pFirstTerm->eOperator==WO_ISNULL ); |
| 105973 | whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, |
| 105974 | &pc.plan.nRow); |
| 105975 | }else if( bInEst==0 ){ |
| 105976 | assert( pFirstTerm->eOperator==WO_IN ); |
| 105977 | whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, |
| 105978 | &pc.plan.nRow); |
| 105979 | } |
| 105980 | } |
| 105981 | #endif /* SQLITE_ENABLE_STAT3 */ |
| @@ -106118,11 +106290,11 @@ | |
| 106118 | ** more selective intentionally because of the subjective |
| 106119 | ** observation that indexed range constraints really are more |
| 106120 | ** selective in practice, on average. */ |
| 106121 | pc.plan.nRow /= 3; |
| 106122 | } |
| 106123 | }else if( pTerm->eOperator!=WO_NOOP ){ |
| 106124 | /* Any other expression lowers the output row count by half */ |
| 106125 | pc.plan.nRow /= 2; |
| 106126 | } |
| 106127 | } |
| 106128 | if( pc.plan.nRow<2 ) pc.plan.nRow = 2; |
| @@ -106170,12 +106342,13 @@ | |
| 106170 | assert( pSrc->pIndex==0 |
| 106171 | || p->cost.plan.u.pIdx==0 |
| 106172 | || p->cost.plan.u.pIdx==pSrc->pIndex |
| 106173 | ); |
| 106174 | |
| 106175 | WHERETRACE((" best index is: %s\n", |
| 106176 | p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk")); |
| 106177 | |
| 106178 | bestOrClauseIndex(p); |
| 106179 | bestAutomaticIndex(p); |
| 106180 | p->cost.plan.wsFlags |= eqTermMask; |
| 106181 | } |
| @@ -106753,11 +106926,10 @@ | |
| 106753 | */ |
| 106754 | iReleaseReg = sqlite3GetTempReg(pParse); |
| 106755 | pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0); |
| 106756 | assert( pTerm!=0 ); |
| 106757 | assert( pTerm->pExpr!=0 ); |
| 106758 | assert( pTerm->leftCursor==iCur ); |
| 106759 | assert( omitTable==0 ); |
| 106760 | testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ |
| 106761 | iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, iReleaseReg); |
| 106762 | addrNxt = pLevel->addrNxt; |
| 106763 | sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); |
| @@ -107144,11 +107316,11 @@ | |
| 107144 | int ii; /* Loop counter */ |
| 107145 | Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ |
| 107146 | |
| 107147 | pTerm = pLevel->plan.u.pTerm; |
| 107148 | assert( pTerm!=0 ); |
| 107149 | assert( pTerm->eOperator==WO_OR ); |
| 107150 | assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); |
| 107151 | pOrWc = &pTerm->u.pOrInfo->wc; |
| 107152 | pLevel->op = OP_Return; |
| 107153 | pLevel->p1 = regReturn; |
| 107154 | |
| @@ -107217,11 +107389,11 @@ | |
| 107217 | } |
| 107218 | } |
| 107219 | |
| 107220 | for(ii=0; ii<pOrWc->nTerm; ii++){ |
| 107221 | WhereTerm *pOrTerm = &pOrWc->a[ii]; |
| 107222 | if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){ |
| 107223 | WhereInfo *pSubWInfo; /* Info for single OR-term scan */ |
| 107224 | Expr *pOrExpr = pOrTerm->pExpr; |
| 107225 | if( pAndExpr ){ |
| 107226 | pAndExpr->pLeft = pOrExpr; |
| 107227 | pOrExpr = pAndExpr; |
| @@ -107672,10 +107844,11 @@ | |
| 107672 | Index *pIdx; /* Index for FROM table at pTabItem */ |
| 107673 | int j; /* For looping over FROM tables */ |
| 107674 | int bestJ = -1; /* The value of j */ |
| 107675 | Bitmask m; /* Bitmask value for j or bestJ */ |
| 107676 | int isOptimal; /* Iterator for optimal/non-optimal search */ |
| 107677 | int nUnconstrained; /* Number tables without INDEXED BY */ |
| 107678 | Bitmask notIndexed; /* Mask of tables that cannot use an index */ |
| 107679 | |
| 107680 | memset(&bestPlan, 0, sizeof(bestPlan)); |
| 107681 | bestPlan.rCost = SQLITE_BIG_DBL; |
| @@ -107706,14 +107879,12 @@ | |
| 107706 | ** |
| 107707 | ** The second loop iteration is only performed if no optimal scan |
| 107708 | ** strategies were found by the first iteration. This second iteration |
| 107709 | ** is used to search for the lowest cost scan overall. |
| 107710 | ** |
| 107711 | ** Previous versions of SQLite performed only the second iteration - |
| 107712 | ** the next outermost loop was always that with the lowest overall |
| 107713 | ** cost. However, this meant that SQLite could select the wrong plan |
| 107714 | ** for scripts such as the following: |
| 107715 | ** |
| 107716 | ** CREATE TABLE t1(a, b); |
| 107717 | ** CREATE TABLE t2(c, d); |
| 107718 | ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a; |
| 107719 | ** |
| @@ -107724,20 +107895,44 @@ | |
| 107724 | ** algorithm may choose to use t2 for the outer loop, which is a much |
| 107725 | ** costlier approach. |
| 107726 | */ |
| 107727 | nUnconstrained = 0; |
| 107728 | notIndexed = 0; |
| 107729 | for(isOptimal=(iFrom<nTabList-1); isOptimal>=0 && bestJ<0; isOptimal--){ |
| 107730 | for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){ |
| 107731 | int doNotReorder; /* True if this table should not be reordered */ |
| 107732 | |
| 107733 | doNotReorder = (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0; |
| 107734 | if( j!=iFrom && doNotReorder ) break; |
| 107735 | m = getMask(pMaskSet, sWBI.pSrc->iCursor); |
| 107736 | if( (m & sWBI.notValid)==0 ){ |
| 107737 | if( j==iFrom ) iFrom++; |
| 107738 | continue; |
| 107739 | } |
| 107740 | sWBI.notReady = (isOptimal ? m : sWBI.notValid); |
| 107741 | if( sWBI.pSrc->pIndex==0 ) nUnconstrained++; |
| 107742 | |
| 107743 | WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n", |
| @@ -107763,12 +107958,12 @@ | |
| 107763 | if( isOptimal && (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){ |
| 107764 | notIndexed |= m; |
| 107765 | } |
| 107766 | if( isOptimal ){ |
| 107767 | pWInfo->a[j].rOptCost = sWBI.cost.rCost; |
| 107768 | }else if( iFrom<nTabList-1 ){ |
| 107769 | /* If two or more tables have nearly the same outer loop cost, |
| 107770 | ** very different inner loop (optimal) cost, we want to choose |
| 107771 | ** for the outer loop that table which benefits the least from |
| 107772 | ** being in the inner loop. The following code scales the |
| 107773 | ** outer loop cost estimate to accomplish that. */ |
| 107774 | WHERETRACE((" scaling cost from %.1f to %.1f\n", |
| @@ -107809,15 +108004,23 @@ | |
| 107809 | sWBI.cost.rCost, sWBI.cost.plan.nRow, |
| 107810 | sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags)); |
| 107811 | bestPlan = sWBI.cost; |
| 107812 | bestJ = j; |
| 107813 | } |
| 107814 | if( doNotReorder ) break; |
| 107815 | } |
| 107816 | } |
| 107817 | assert( bestJ>=0 ); |
| 107818 | assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); |
| 107819 | WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n" |
| 107820 | " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n", |
| 107821 | bestJ, pTabList->a[bestJ].pTab->zName, |
| 107822 | pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow, |
| 107823 | bestPlan.plan.nOBSat, bestPlan.plan.wsFlags)); |
| @@ -136543,11 +136746,12 @@ | |
| 136543 | ** would fit in a single node, use a smaller node-size. |
| 136544 | */ |
| 136545 | static int getNodeSize( |
| 136546 | sqlite3 *db, /* Database handle */ |
| 136547 | Rtree *pRtree, /* Rtree handle */ |
| 136548 | int isCreate /* True for xCreate, false for xConnect */ |
| 136549 | ){ |
| 136550 | int rc; |
| 136551 | char *zSql; |
| 136552 | if( isCreate ){ |
| 136553 | int iPageSize = 0; |
| @@ -136556,17 +136760,22 @@ | |
| 136556 | if( rc==SQLITE_OK ){ |
| 136557 | pRtree->iNodeSize = iPageSize-64; |
| 136558 | if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){ |
| 136559 | pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS; |
| 136560 | } |
| 136561 | } |
| 136562 | }else{ |
| 136563 | zSql = sqlite3_mprintf( |
| 136564 | "SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1", |
| 136565 | pRtree->zDb, pRtree->zName |
| 136566 | ); |
| 136567 | rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize); |
| 136568 | } |
| 136569 | |
| 136570 | sqlite3_free(zSql); |
| 136571 | return rc; |
| 136572 | } |
| @@ -136626,11 +136835,11 @@ | |
| 136626 | pRtree->eCoordType = eCoordType; |
| 136627 | memcpy(pRtree->zDb, argv[1], nDb); |
| 136628 | memcpy(pRtree->zName, argv[2], nName); |
| 136629 | |
| 136630 | /* Figure out the node size to use. */ |
| 136631 | rc = getNodeSize(db, pRtree, isCreate); |
| 136632 | |
| 136633 | /* Create/Connect to the underlying relational database schema. If |
| 136634 | ** that is successful, call sqlite3_declare_vtab() to configure |
| 136635 | ** the r-tree table schema. |
| 136636 | */ |
| 136637 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -673,11 +673,11 @@ | |
| 673 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 674 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 675 | */ |
| 676 | #define SQLITE_VERSION "3.7.16" |
| 677 | #define SQLITE_VERSION_NUMBER 3007016 |
| 678 | #define SQLITE_SOURCE_ID "2013-01-17 17:20:49 38852f158ab20bb4d7b264af987ec1538052bec3" |
| 679 | |
| 680 | /* |
| 681 | ** CAPI3REF: Run-Time Library Version Numbers |
| 682 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 683 | ** |
| @@ -8238,10 +8238,15 @@ | |
| 8238 | ** A convenience macro that returns the number of elements in |
| 8239 | ** an array. |
| 8240 | */ |
| 8241 | #define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0]))) |
| 8242 | |
| 8243 | /* |
| 8244 | ** Determine if the argument is a power of two |
| 8245 | */ |
| 8246 | #define IsPowerOfTwo(X) (((X)&((X)-1))==0) |
| 8247 | |
| 8248 | /* |
| 8249 | ** The following value as a destructor means to use sqlite3DbFree(). |
| 8250 | ** The sqlite3DbFree() routine requires two parameters instead of the |
| 8251 | ** one parameter that destructors normally want. So we have to introduce |
| 8252 | ** this magic value that the code knows to handle differently. Any |
| @@ -10042,10 +10047,11 @@ | |
| 10047 | #define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */ |
| 10048 | #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ |
| 10049 | #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ |
| 10050 | #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ |
| 10051 | #define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */ |
| 10052 | #define SQLITE_Transitive 0x0200 /* Transitive constraints */ |
| 10053 | #define SQLITE_AllOpts 0xffff /* All optimizations */ |
| 10054 | |
| 10055 | /* |
| 10056 | ** Macros for testing whether or not optimizations are enabled or disabled. |
| 10057 | */ |
| @@ -10553,24 +10559,24 @@ | |
| 10559 | ** and the value of Index.onError indicate the which conflict resolution |
| 10560 | ** algorithm to employ whenever an attempt is made to insert a non-unique |
| 10561 | ** element. |
| 10562 | */ |
| 10563 | struct Index { |
| 10564 | char *zName; /* Name of this index */ |
| 10565 | int *aiColumn; /* Which columns are used by this index. 1st is 0 */ |
| 10566 | tRowcnt *aiRowEst; /* From ANALYZE: Est. rows selected by each column */ |
| 10567 | Table *pTable; /* The SQL table being indexed */ |
| 10568 | char *zColAff; /* String defining the affinity of each column */ |
| 10569 | Index *pNext; /* The next index associated with the same table */ |
| 10570 | Schema *pSchema; /* Schema containing this index */ |
| 10571 | u8 *aSortOrder; /* for each column: True==DESC, False==ASC */ |
| 10572 | char **azColl; /* Array of collation sequence names for index */ |
| 10573 | int tnum; /* DB Page containing root of this index */ |
| 10574 | u16 nColumn; /* Number of columns in table used by this index */ |
| 10575 | u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ |
| 10576 | unsigned autoIndex:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */ |
| 10577 | unsigned bUnordered:1; /* Use this index for == or IN queries only */ |
| 10578 | #ifdef SQLITE_ENABLE_STAT3 |
| 10579 | int nSample; /* Number of elements in aSample[] */ |
| 10580 | tRowcnt avgEq; /* Average nEq value for key values not in aSample */ |
| 10581 | IndexSample *aSample; /* Samples of the left-most key */ |
| 10582 | #endif |
| @@ -10840,22 +10846,31 @@ | |
| 10846 | ** name. An expr/name combination can be used in several ways, such |
| 10847 | ** as the list of "expr AS ID" fields following a "SELECT" or in the |
| 10848 | ** list of "ID = expr" items in an UPDATE. A list of expressions can |
| 10849 | ** also be used as the argument to a function, in which case the a.zName |
| 10850 | ** field is not used. |
| 10851 | ** |
| 10852 | ** By default the Expr.zSpan field holds a human-readable description of |
| 10853 | ** the expression that is used in the generation of error messages and |
| 10854 | ** column labels. In this case, Expr.zSpan is typically the text of a |
| 10855 | ** column expression as it exists in a SELECT statement. However, if |
| 10856 | ** the bSpanIsTab flag is set, then zSpan is overloaded to mean the name |
| 10857 | ** of the result column in the form: DATABASE.TABLE.COLUMN. This later |
| 10858 | ** form is used for name resolution with nested FROM clauses. |
| 10859 | */ |
| 10860 | struct ExprList { |
| 10861 | int nExpr; /* Number of expressions on the list */ |
| 10862 | int iECursor; /* VDBE Cursor associated with this ExprList */ |
| 10863 | struct ExprList_item { /* For each expression in the list */ |
| 10864 | Expr *pExpr; /* The list of expressions */ |
| 10865 | char *zName; /* Token associated with this expression */ |
| 10866 | char *zSpan; /* Original text of the expression */ |
| 10867 | u8 sortOrder; /* 1 for DESC or 0 for ASC */ |
| 10868 | unsigned done :1; /* A flag to indicate when processing is finished */ |
| 10869 | unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */ |
| 10870 | u16 iOrderByCol; /* For ORDER BY, column number in result set */ |
| 10871 | u16 iAlias; /* Index into Parse.aAlias[] for zName */ |
| 10872 | } *a; /* Alloc a power of two greater or equal to nExpr */ |
| 10873 | }; |
| 10874 | |
| 10875 | /* |
| 10876 | ** An instance of this structure is used by the parser to record both |
| @@ -12141,10 +12156,11 @@ | |
| 12156 | SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *); |
| 12157 | SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...); |
| 12158 | SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*); |
| 12159 | SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *, Expr *, int, int); |
| 12160 | SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*); |
| 12161 | SQLITE_PRIVATE int sqlite3MatchSpanName(const char*, const char*, const char*, const char*); |
| 12162 | SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*); |
| 12163 | SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); |
| 12164 | SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); |
| 12165 | SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int); |
| 12166 | SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *); |
| @@ -23289,15 +23305,11 @@ | |
| 23305 | { "pwrite64", (sqlite3_syscall_ptr)0, 0 }, |
| 23306 | #endif |
| 23307 | #define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\ |
| 23308 | aSyscall[13].pCurrent) |
| 23309 | |
| 23310 | { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 }, |
| 23311 | #define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) |
| 23312 | |
| 23313 | #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE |
| 23314 | { "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 }, |
| 23315 | #else |
| @@ -23318,13 +23330,10 @@ | |
| 23330 | #define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent) |
| 23331 | |
| 23332 | { "fchown", (sqlite3_syscall_ptr)posixFchown, 0 }, |
| 23333 | #define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent) |
| 23334 | |
| 23335 | }; /* End of the overrideable system calls */ |
| 23336 | |
| 23337 | /* |
| 23338 | ** This is the xSetSystemCall() method of sqlite3_vfs for all of the |
| 23339 | ** "unix" VFSes. Return SQLITE_OK opon successfully updating the |
| @@ -23425,31 +23434,29 @@ | |
| 23434 | ** process that is able to write to the database will also be able to |
| 23435 | ** recover the hot journals. |
| 23436 | */ |
| 23437 | static int robust_open(const char *z, int f, mode_t m){ |
| 23438 | int fd; |
| 23439 | mode_t m2 = m ? m : SQLITE_DEFAULT_FILE_PERMISSIONS; |
| 23440 | do{ |
| 23441 | #if defined(O_CLOEXEC) |
| 23442 | fd = osOpen(z,f|O_CLOEXEC,m2); |
| 23443 | #else |
| 23444 | fd = osOpen(z,f,m2); |
| 23445 | #endif |
| 23446 | }while( fd<0 && errno==EINTR ); |
| 23447 | if( fd>=0 ){ |
| 23448 | if( m!=0 ){ |
| 23449 | struct stat statbuf; |
| 23450 | if( osFstat(fd, &statbuf)==0 && (statbuf.st_mode&0777)!=m ){ |
| 23451 | osFchmod(fd, m); |
| 23452 | } |
| 23453 | } |
| 23454 | #if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC==0) |
| 23455 | osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); |
| 23456 | #endif |
| 23457 | } |
| 23458 | return fd; |
| 23459 | } |
| 23460 | |
| 23461 | /* |
| 23462 | ** Helper functions to obtain and relinquish the global mutex. The |
| @@ -29871,11 +29878,11 @@ | |
| 29878 | }; |
| 29879 | unsigned int i; /* Loop counter */ |
| 29880 | |
| 29881 | /* Double-check that the aSyscall[] array has been constructed |
| 29882 | ** correctly. See ticket [bb3a86e890c8e96ab] */ |
| 29883 | assert( ArraySize(aSyscall)==21 ); |
| 29884 | |
| 29885 | /* Register all VFSes defined in the aVfs[] array */ |
| 29886 | for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ |
| 29887 | sqlite3_vfs_register(&aVfs[i], i==0); |
| 29888 | } |
| @@ -72655,10 +72662,39 @@ | |
| 72662 | } |
| 72663 | } |
| 72664 | return 0; |
| 72665 | } |
| 72666 | |
| 72667 | /* |
| 72668 | ** Subqueries stores the original database, table and column names for their |
| 72669 | ** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN". |
| 72670 | ** Check to see if the zSpan given to this routine matches the zDb, zTab, |
| 72671 | ** and zCol. If any of zDb, zTab, and zCol are NULL then those fields will |
| 72672 | ** match anything. |
| 72673 | */ |
| 72674 | SQLITE_PRIVATE int sqlite3MatchSpanName( |
| 72675 | const char *zSpan, |
| 72676 | const char *zCol, |
| 72677 | const char *zTab, |
| 72678 | const char *zDb |
| 72679 | ){ |
| 72680 | int n; |
| 72681 | for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} |
| 72682 | if( zDb && sqlite3StrNICmp(zSpan, zDb, n)!=0 ){ |
| 72683 | return 0; |
| 72684 | } |
| 72685 | zSpan += n+1; |
| 72686 | for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} |
| 72687 | if( zTab && sqlite3StrNICmp(zSpan, zTab, n)!=0 ){ |
| 72688 | return 0; |
| 72689 | } |
| 72690 | zSpan += n+1; |
| 72691 | if( zCol && sqlite3StrICmp(zSpan, zCol)!=0 ){ |
| 72692 | return 0; |
| 72693 | } |
| 72694 | return 1; |
| 72695 | } |
| 72696 | |
| 72697 | /* |
| 72698 | ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up |
| 72699 | ** that name in the set of source tables in pSrcList and make the pExpr |
| 72700 | ** expression node refer back to that source column. The following changes |
| @@ -72710,44 +72746,63 @@ | |
| 72746 | |
| 72747 | /* Initialize the node to no-match */ |
| 72748 | pExpr->iTable = -1; |
| 72749 | pExpr->pTab = 0; |
| 72750 | ExprSetIrreducible(pExpr); |
| 72751 | |
| 72752 | /* Translate the schema name in zDb into a pointer to the corresponding |
| 72753 | ** schema. If not found, pSchema will remain NULL and nothing will match |
| 72754 | ** resulting in an appropriate error message toward the end of this routine |
| 72755 | */ |
| 72756 | if( zDb ){ |
| 72757 | for(i=0; i<db->nDb; i++){ |
| 72758 | assert( db->aDb[i].zName ); |
| 72759 | if( sqlite3StrICmp(db->aDb[i].zName,zDb)==0 ){ |
| 72760 | pSchema = db->aDb[i].pSchema; |
| 72761 | break; |
| 72762 | } |
| 72763 | } |
| 72764 | } |
| 72765 | |
| 72766 | /* Start at the inner-most context and move outward until a match is found */ |
| 72767 | while( pNC && cnt==0 ){ |
| 72768 | ExprList *pEList; |
| 72769 | SrcList *pSrcList = pNC->pSrcList; |
| 72770 | |
| 72771 | if( pSrcList ){ |
| 72772 | for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){ |
| 72773 | Table *pTab; |
| 72774 | Column *pCol; |
| 72775 | |
| 72776 | pTab = pItem->pTab; |
| 72777 | assert( pTab!=0 && pTab->zName!=0 ); |
| 72778 | assert( pTab->nCol>0 ); |
| 72779 | if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){ |
| 72780 | ExprList *pEList = pItem->pSelect->pEList; |
| 72781 | int hit = 0; |
| 72782 | for(j=0; j<pEList->nExpr; j++){ |
| 72783 | if( sqlite3MatchSpanName(pEList->a[j].zSpan, zCol, zTab, zDb) ){ |
| 72784 | cnt++; |
| 72785 | cntTab = 2; |
| 72786 | pMatch = pItem; |
| 72787 | pExpr->iColumn = j; |
| 72788 | hit = 1; |
| 72789 | } |
| 72790 | } |
| 72791 | if( hit || zTab==0 ) continue; |
| 72792 | } |
| 72793 | if( zDb && pTab->pSchema!=pSchema ){ |
| 72794 | continue; |
| 72795 | } |
| 72796 | if( zTab ){ |
| 72797 | const char *zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName; |
| 72798 | assert( zTabName!=0 ); |
| 72799 | if( sqlite3StrICmp(zTabName, zTab)!=0 ){ |
| 72800 | continue; |
| 72801 | } |
| 72802 | } |
| 72803 | if( 0==(cntTab++) ){ |
| 72804 | pMatch = pItem; |
| 72805 | } |
| 72806 | for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){ |
| 72807 | if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ |
| 72808 | /* If there has been exactly one prior match and this match |
| @@ -72757,21 +72812,23 @@ | |
| 72812 | if( cnt==1 ){ |
| 72813 | if( pItem->jointype & JT_NATURAL ) continue; |
| 72814 | if( nameInUsingClause(pItem->pUsing, zCol) ) continue; |
| 72815 | } |
| 72816 | cnt++; |
| 72817 | pMatch = pItem; |
| 72818 | /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ |
| 72819 | pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; |
| 72820 | break; |
| 72821 | } |
| 72822 | } |
| 72823 | } |
| 72824 | if( pMatch ){ |
| 72825 | pExpr->iTable = pMatch->iCursor; |
| 72826 | pExpr->pTab = pMatch->pTab; |
| 72827 | pSchema = pExpr->pTab->pSchema; |
| 72828 | } |
| 72829 | } /* if( pSrcList ) */ |
| 72830 | |
| 72831 | #ifndef SQLITE_OMIT_TRIGGER |
| 72832 | /* If we have not already resolved the name, then maybe |
| 72833 | ** it is a new.* or old.* trigger argument reference |
| 72834 | */ |
| @@ -73102,11 +73159,11 @@ | |
| 73159 | #endif |
| 73160 | if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){ |
| 73161 | sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId); |
| 73162 | pNC->nErr++; |
| 73163 | is_agg = 0; |
| 73164 | }else if( no_such_func && pParse->db->init.busy==0 ){ |
| 73165 | sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId); |
| 73166 | pNC->nErr++; |
| 73167 | }else if( wrong_num_args ){ |
| 73168 | sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()", |
| 73169 | nId, zId); |
| @@ -77065,10 +77122,16 @@ | |
| 77122 | for(i=0; i<pList->nExpr; i++){ |
| 77123 | sqlite3ExplainPrintf(pOut, "item[%d] = ", i); |
| 77124 | sqlite3ExplainPush(pOut); |
| 77125 | sqlite3ExplainExpr(pOut, pList->a[i].pExpr); |
| 77126 | sqlite3ExplainPop(pOut); |
| 77127 | if( pList->a[i].zName ){ |
| 77128 | sqlite3ExplainPrintf(pOut, " AS %s", pList->a[i].zName); |
| 77129 | } |
| 77130 | if( pList->a[i].bSpanIsTab ){ |
| 77131 | sqlite3ExplainPrintf(pOut, " (%s)", pList->a[i].zSpan); |
| 77132 | } |
| 77133 | if( i<pList->nExpr-1 ){ |
| 77134 | sqlite3ExplainNL(pOut); |
| 77135 | } |
| 77136 | } |
| 77137 | sqlite3ExplainPop(pOut); |
| @@ -92739,13 +92802,15 @@ | |
| 92802 | if( sqlite3StrICmp(zLeft, "table_info")==0 && zRight ){ |
| 92803 | Table *pTab; |
| 92804 | if( sqlite3ReadSchema(pParse) ) goto pragma_out; |
| 92805 | pTab = sqlite3FindTable(db, zRight, zDb); |
| 92806 | if( pTab ){ |
| 92807 | int i, k; |
| 92808 | int nHidden = 0; |
| 92809 | Column *pCol; |
| 92810 | Index *pPk; |
| 92811 | for(pPk=pTab->pIndex; pPk && pPk->autoIndex!=2; pPk=pPk->pNext){} |
| 92812 | sqlite3VdbeSetNumCols(v, 6); |
| 92813 | pParse->nMem = 6; |
| 92814 | sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cid", SQLITE_STATIC); |
| 92815 | sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC); |
| 92816 | sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "type", SQLITE_STATIC); |
| @@ -92766,12 +92831,18 @@ | |
| 92831 | if( pCol->zDflt ){ |
| 92832 | sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)pCol->zDflt, 0); |
| 92833 | }else{ |
| 92834 | sqlite3VdbeAddOp2(v, OP_Null, 0, 5); |
| 92835 | } |
| 92836 | if( (pCol->colFlags & COLFLAG_PRIMKEY)==0 ){ |
| 92837 | k = 0; |
| 92838 | }else if( pPk==0 ){ |
| 92839 | k = 1; |
| 92840 | }else{ |
| 92841 | for(k=1; ALWAYS(k<=pTab->nCol) && pPk->aiColumn[k-1]!=i; k++){} |
| 92842 | } |
| 92843 | sqlite3VdbeAddOp2(v, OP_Integer, k, 6); |
| 92844 | sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6); |
| 92845 | } |
| 92846 | } |
| 92847 | }else |
| 92848 | |
| @@ -93516,11 +93587,11 @@ | |
| 93587 | sqlite3_rekey(db, zKey, i/2); |
| 93588 | } |
| 93589 | }else |
| 93590 | #endif |
| 93591 | #if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) |
| 93592 | if( sqlite3StrICmp(zLeft, "activate_extensions")==0 && zRight ){ |
| 93593 | #ifdef SQLITE_HAS_CODEC |
| 93594 | if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){ |
| 93595 | sqlite3_activate_see(&zRight[4]); |
| 93596 | } |
| 93597 | #endif |
| @@ -95756,12 +95827,10 @@ | |
| 95827 | |
| 95828 | for(i=0, pCol=aCol; i<nCol; i++, pCol++){ |
| 95829 | /* Get an appropriate name for the column |
| 95830 | */ |
| 95831 | p = sqlite3ExprSkipCollate(pEList->a[i].pExpr); |
| 95832 | if( (zName = pEList->a[i].zName)!=0 ){ |
| 95833 | /* If the column contains an "AS <name>" phrase, use <name> as the name */ |
| 95834 | zName = sqlite3DbStrDup(db, zName); |
| 95835 | }else{ |
| 95836 | Expr *pColExpr = p; /* The expression that is the result column name */ |
| @@ -95795,10 +95864,13 @@ | |
| 95864 | */ |
| 95865 | nName = sqlite3Strlen30(zName); |
| 95866 | for(j=cnt=0; j<i; j++){ |
| 95867 | if( sqlite3StrICmp(aCol[j].zName, zName)==0 ){ |
| 95868 | char *zNewName; |
| 95869 | int k; |
| 95870 | for(k=nName-1; k>1 && sqlite3Isdigit(zName[k]); k--){} |
| 95871 | if( zName[k]==':' ) nName = k; |
| 95872 | zName[nName] = 0; |
| 95873 | zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt); |
| 95874 | sqlite3DbFree(db, zName); |
| 95875 | zName = zNewName; |
| 95876 | j = -1; |
| @@ -97711,10 +97783,11 @@ | |
| 97783 | int i, j, k; |
| 97784 | SrcList *pTabList; |
| 97785 | ExprList *pEList; |
| 97786 | struct SrcList_item *pFrom; |
| 97787 | sqlite3 *db = pParse->db; |
| 97788 | Expr *pE, *pRight, *pExpr; |
| 97789 | |
| 97790 | if( db->mallocFailed ){ |
| 97791 | return WRC_Abort; |
| 97792 | } |
| 97793 | if( NEVER(p->pSrc==0) || (p->selFlags & SF_Expanded)!=0 ){ |
| @@ -97796,11 +97869,11 @@ | |
| 97869 | ** |
| 97870 | ** The first loop just checks to see if there are any "*" operators |
| 97871 | ** that need expanding. |
| 97872 | */ |
| 97873 | for(k=0; k<pEList->nExpr; k++){ |
| 97874 | pE = pEList->a[k].pExpr; |
| 97875 | if( pE->op==TK_ALL ) break; |
| 97876 | assert( pE->op!=TK_DOT || pE->pRight!=0 ); |
| 97877 | assert( pE->op!=TK_DOT || (pE->pLeft!=0 && pE->pLeft->op==TK_ID) ); |
| 97878 | if( pE->op==TK_DOT && pE->pRight->op==TK_ALL ) break; |
| 97879 | } |
| @@ -97814,14 +97887,22 @@ | |
| 97887 | ExprList *pNew = 0; |
| 97888 | int flags = pParse->db->flags; |
| 97889 | int longNames = (flags & SQLITE_FullColNames)!=0 |
| 97890 | && (flags & SQLITE_ShortColNames)==0; |
| 97891 | |
| 97892 | /* When processing FROM-clause subqueries, it is always the case |
| 97893 | ** that full_column_names=OFF and short_column_names=ON. The |
| 97894 | ** sqlite3ResultSetOfSelect() routine makes it so. */ |
| 97895 | assert( (p->selFlags & SF_NestedFrom)==0 |
| 97896 | || ((flags & SQLITE_FullColNames)==0 && |
| 97897 | (flags & SQLITE_ShortColNames)!=0) ); |
| 97898 | |
| 97899 | for(k=0; k<pEList->nExpr; k++){ |
| 97900 | pE = a[k].pExpr; |
| 97901 | pRight = pE->pRight; |
| 97902 | assert( pE->op!=TK_DOT || pRight!=0 ); |
| 97903 | if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pRight->op!=TK_ALL) ){ |
| 97904 | /* This particular expression does not need to be expanded. |
| 97905 | */ |
| 97906 | pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr); |
| 97907 | if( pNew ){ |
| 97908 | pNew->a[pNew->nExpr-1].zName = a[k].zName; |
| @@ -97832,44 +97913,56 @@ | |
| 97913 | a[k].pExpr = 0; |
| 97914 | }else{ |
| 97915 | /* This expression is a "*" or a "TABLE.*" and needs to be |
| 97916 | ** expanded. */ |
| 97917 | int tableSeen = 0; /* Set to 1 when TABLE matches */ |
| 97918 | char *zTName = 0; /* text of name of TABLE */ |
| 97919 | if( pE->op==TK_DOT ){ |
| 97920 | assert( pE->pLeft!=0 ); |
| 97921 | assert( !ExprHasProperty(pE->pLeft, EP_IntValue) ); |
| 97922 | zTName = pE->pLeft->u.zToken; |
| 97923 | } |
| 97924 | for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){ |
| 97925 | Table *pTab = pFrom->pTab; |
| 97926 | Select *pSub = pFrom->pSelect; |
| 97927 | char *zTabName = pFrom->zAlias; |
| 97928 | const char *zSchemaName = 0; |
| 97929 | int iDb; |
| 97930 | if( zTabName==0 ){ |
| 97931 | zTabName = pTab->zName; |
| 97932 | } |
| 97933 | if( db->mallocFailed ) break; |
| 97934 | if( pSub==0 || (pSub->selFlags & SF_NestedFrom)==0 ){ |
| 97935 | pSub = 0; |
| 97936 | if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){ |
| 97937 | continue; |
| 97938 | } |
| 97939 | iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| 97940 | zSchemaName = iDb>=0 ? db->aDb[iDb].zName : "*"; |
| 97941 | } |
| 97942 | for(j=0; j<pTab->nCol; j++){ |
| 97943 | char *zName = pTab->aCol[j].zName; |
| 97944 | char *zColname; /* The computed column name */ |
| 97945 | char *zToFree; /* Malloced string that needs to be freed */ |
| 97946 | Token sColname; /* Computed column name as a token */ |
| 97947 | |
| 97948 | assert( zName ); |
| 97949 | if( zTName && pSub |
| 97950 | && sqlite3MatchSpanName(pSub->pEList->a[j].zSpan, 0, zTName, 0)==0 |
| 97951 | ){ |
| 97952 | continue; |
| 97953 | } |
| 97954 | |
| 97955 | /* If a column is marked as 'hidden' (currently only possible |
| 97956 | ** for virtual tables), do not include it in the expanded |
| 97957 | ** result-set list. |
| 97958 | */ |
| 97959 | if( IsHiddenColumn(&pTab->aCol[j]) ){ |
| 97960 | assert(IsVirtual(pTab)); |
| 97961 | continue; |
| 97962 | } |
| 97963 | tableSeen = 1; |
| 97964 | |
| 97965 | if( i>0 && zTName==0 ){ |
| 97966 | if( (pFrom->jointype & JT_NATURAL)!=0 |
| 97967 | && tableAndColumnIndex(pTabList, i, zName, 0, 0) |
| 97968 | ){ |
| @@ -97888,10 +97981,14 @@ | |
| 97981 | zToFree = 0; |
| 97982 | if( longNames || pTabList->nSrc>1 ){ |
| 97983 | Expr *pLeft; |
| 97984 | pLeft = sqlite3Expr(db, TK_ID, zTabName); |
| 97985 | pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0); |
| 97986 | if( zSchemaName ){ |
| 97987 | pLeft = sqlite3Expr(db, TK_ID, zSchemaName); |
| 97988 | pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pExpr, 0); |
| 97989 | } |
| 97990 | if( longNames ){ |
| 97991 | zColname = sqlite3MPrintf(db, "%s.%s", zTabName, zName); |
| 97992 | zToFree = zColname; |
| 97993 | } |
| 97994 | }else{ |
| @@ -97899,10 +97996,22 @@ | |
| 97996 | } |
| 97997 | pNew = sqlite3ExprListAppend(pParse, pNew, pExpr); |
| 97998 | sColname.z = zColname; |
| 97999 | sColname.n = sqlite3Strlen30(zColname); |
| 98000 | sqlite3ExprListSetName(pParse, pNew, &sColname, 0); |
| 98001 | if( pNew && (p->selFlags & SF_NestedFrom)!=0 ){ |
| 98002 | struct ExprList_item *pX = &pNew->a[pNew->nExpr-1]; |
| 98003 | if( pSub ){ |
| 98004 | pX->zSpan = sqlite3DbStrDup(db, pSub->pEList->a[j].zSpan); |
| 98005 | testcase( pX->zSpan==0 ); |
| 98006 | }else{ |
| 98007 | pX->zSpan = sqlite3MPrintf(db, "%s.%s.%s", |
| 98008 | zSchemaName, zTabName, zColname); |
| 98009 | testcase( pX->zSpan==0 ); |
| 98010 | } |
| 98011 | pX->bSpanIsTab = 1; |
| 98012 | } |
| 98013 | sqlite3DbFree(db, zToFree); |
| 98014 | } |
| 98015 | } |
| 98016 | if( !tableSeen ){ |
| 98017 | if( zTName ){ |
| @@ -102699,12 +102808,12 @@ | |
| 102808 | Expr *pExpr; /* Pointer to the subexpression that is this term */ |
| 102809 | int iParent; /* Disable pWC->a[iParent] when this term disabled */ |
| 102810 | int leftCursor; /* Cursor number of X in "X <op> <expr>" */ |
| 102811 | union { |
| 102812 | int leftColumn; /* Column number of X in "X <op> <expr>" */ |
| 102813 | WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */ |
| 102814 | WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */ |
| 102815 | } u; |
| 102816 | u16 eOperator; /* A WO_xx value describing <op> */ |
| 102817 | u8 wtFlags; /* TERM_xxx bit flags. See below */ |
| 102818 | u8 nChild; /* Number of children that must disable us */ |
| 102819 | WhereClause *pWC; /* The clause this term is part of */ |
| @@ -102828,10 +102937,11 @@ | |
| 102937 | #define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) |
| 102938 | #define WO_MATCH 0x040 |
| 102939 | #define WO_ISNULL 0x080 |
| 102940 | #define WO_OR 0x100 /* Two or more OR-connected terms */ |
| 102941 | #define WO_AND 0x200 /* Two or more AND-connected terms */ |
| 102942 | #define WO_EQUIV 0x400 /* Of the form A==B, both columns */ |
| 102943 | #define WO_NOOP 0x800 /* This term does not restrict search space */ |
| 102944 | |
| 102945 | #define WO_ALL 0xfff /* Mask of all possible WO_* values */ |
| 102946 | #define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */ |
| 102947 | |
| @@ -103230,58 +103340,112 @@ | |
| 103340 | /* |
| 103341 | ** Search for a term in the WHERE clause that is of the form "X <op> <expr>" |
| 103342 | ** where X is a reference to the iColumn of table iCur and <op> is one of |
| 103343 | ** the WO_xx operator codes specified by the op parameter. |
| 103344 | ** Return a pointer to the term. Return 0 if not found. |
| 103345 | ** |
| 103346 | ** The term returned might by Y=<expr> if there is another constraint in |
| 103347 | ** the WHERE clause that specifies that X=Y. Any such constraints will be |
| 103348 | ** identified by the WO_EQUIV bit in the pTerm->eOperator field. The |
| 103349 | ** aEquiv[] array holds X and all its equivalents, with each SQL variable |
| 103350 | ** taking up two slots in aEquiv[]. The first slot is for the cursor number |
| 103351 | ** and the second is for the column number. There are 22 slots in aEquiv[] |
| 103352 | ** so that means we can look for X plus up to 10 other equivalent values. |
| 103353 | ** Hence a search for X will return <expr> if X=A1 and A1=A2 and A2=A3 |
| 103354 | ** and ... and A9=A10 and A10=<expr>. |
| 103355 | ** |
| 103356 | ** If there are multiple terms in the WHERE clause of the form "X <op> <expr>" |
| 103357 | ** then try for the one with no dependencies on <expr> - in other words where |
| 103358 | ** <expr> is a constant expression of some kind. Only return entries of |
| 103359 | ** the form "X <op> Y" where Y is a column in another table if no terms of |
| 103360 | ** the form "X <op> <const-expr>" exist. Other than this priority, if there |
| 103361 | ** are two or more terms that match, then the choice of which term to return |
| 103362 | ** is arbitrary. |
| 103363 | */ |
| 103364 | static WhereTerm *findTerm( |
| 103365 | WhereClause *pWC, /* The WHERE clause to be searched */ |
| 103366 | int iCur, /* Cursor number of LHS */ |
| 103367 | int iColumn, /* Column number of LHS */ |
| 103368 | Bitmask notReady, /* RHS must not overlap with this mask */ |
| 103369 | u32 op, /* Mask of WO_xx values describing operator */ |
| 103370 | Index *pIdx /* Must be compatible with this index, if not NULL */ |
| 103371 | ){ |
| 103372 | WhereTerm *pTerm; /* Term being examined as possible result */ |
| 103373 | WhereTerm *pResult = 0; /* The answer to return */ |
| 103374 | WhereClause *pWCOrig = pWC; /* Original pWC value */ |
| 103375 | int j, k; /* Loop counters */ |
| 103376 | Expr *pX; /* Pointer to an expression */ |
| 103377 | Parse *pParse; /* Parsing context */ |
| 103378 | int iOrigCol = iColumn; /* Original value of iColumn */ |
| 103379 | int nEquiv = 2; /* Number of entires in aEquiv[] */ |
| 103380 | int iEquiv = 2; /* Number of entries of aEquiv[] processed so far */ |
| 103381 | int aEquiv[22]; /* iCur,iColumn and up to 10 other equivalents */ |
| 103382 | |
| 103383 | assert( iCur>=0 ); |
| 103384 | aEquiv[0] = iCur; |
| 103385 | aEquiv[1] = iColumn; |
| 103386 | for(;;){ |
| 103387 | for(pWC=pWCOrig; pWC; pWC=pWC->pOuter){ |
| 103388 | for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ |
| 103389 | if( pTerm->leftCursor==iCur |
| 103390 | && pTerm->u.leftColumn==iColumn |
| 103391 | ){ |
| 103392 | if( (pTerm->prereqRight & notReady)==0 |
| 103393 | && (pTerm->eOperator & op & WO_ALL)!=0 |
| 103394 | ){ |
| 103395 | if( iOrigCol>=0 && pIdx && (pTerm->eOperator & WO_ISNULL)==0 ){ |
| 103396 | CollSeq *pColl; |
| 103397 | char idxaff; |
| 103398 | |
| 103399 | pX = pTerm->pExpr; |
| 103400 | pParse = pWC->pParse; |
| 103401 | idxaff = pIdx->pTable->aCol[iOrigCol].affinity; |
| 103402 | if( !sqlite3IndexAffinityOk(pX, idxaff) ){ |
| 103403 | continue; |
| 103404 | } |
| 103405 | |
| 103406 | /* Figure out the collation sequence required from an index for |
| 103407 | ** it to be useful for optimising expression pX. Store this |
| 103408 | ** value in variable pColl. |
| 103409 | */ |
| 103410 | assert(pX->pLeft); |
| 103411 | pColl = sqlite3BinaryCompareCollSeq(pParse,pX->pLeft,pX->pRight); |
| 103412 | if( pColl==0 ) pColl = pParse->db->pDfltColl; |
| 103413 | |
| 103414 | for(j=0; pIdx->aiColumn[j]!=iOrigCol; j++){ |
| 103415 | if( NEVER(j>=pIdx->nColumn) ) return 0; |
| 103416 | } |
| 103417 | if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ){ |
| 103418 | continue; |
| 103419 | } |
| 103420 | } |
| 103421 | pResult = pTerm; |
| 103422 | if( pTerm->prereqRight==0 ) goto findTerm_success; |
| 103423 | } |
| 103424 | if( (pTerm->eOperator & WO_EQUIV)!=0 |
| 103425 | && nEquiv<ArraySize(aEquiv) |
| 103426 | ){ |
| 103427 | pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight); |
| 103428 | assert( pX->op==TK_COLUMN ); |
| 103429 | for(j=0; j<nEquiv; j+=2){ |
| 103430 | if( aEquiv[j]==pX->iTable && aEquiv[j+1]==pX->iColumn ) break; |
| 103431 | } |
| 103432 | if( j==nEquiv ){ |
| 103433 | aEquiv[j] = pX->iTable; |
| 103434 | aEquiv[j+1] = pX->iColumn; |
| 103435 | nEquiv += 2; |
| 103436 | } |
| 103437 | } |
| 103438 | } |
| 103439 | } |
| 103440 | } |
| 103441 | if( iEquiv>=nEquiv ) break; |
| 103442 | iCur = aEquiv[iEquiv++]; |
| 103443 | iColumn = aEquiv[iEquiv++]; |
| 103444 | } |
| 103445 | findTerm_success: |
| 103446 | return pResult; |
| 103447 | } |
| 103448 | |
| 103449 | /* Forward reference */ |
| 103450 | static void exprAnalyze(SrcList*, WhereClause*, int); |
| 103451 | |
| @@ -103555,11 +103719,10 @@ | |
| 103719 | indexable = ~(Bitmask)0; |
| 103720 | chngToIN = ~(pWC->vmask); |
| 103721 | for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0 && indexable; i--, pOrTerm++){ |
| 103722 | if( (pOrTerm->eOperator & WO_SINGLE)==0 ){ |
| 103723 | WhereAndInfo *pAndInfo; |
| 103724 | assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 ); |
| 103725 | chngToIN = 0; |
| 103726 | pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo)); |
| 103727 | if( pAndInfo ){ |
| 103728 | WhereClause *pAndWC; |
| @@ -103594,11 +103757,11 @@ | |
| 103757 | if( pOrTerm->wtFlags & TERM_VIRTUAL ){ |
| 103758 | WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent]; |
| 103759 | b |= getMask(pMaskSet, pOther->leftCursor); |
| 103760 | } |
| 103761 | indexable &= b; |
| 103762 | if( (pOrTerm->eOperator & WO_EQ)==0 ){ |
| 103763 | chngToIN = 0; |
| 103764 | }else{ |
| 103765 | chngToIN &= b; |
| 103766 | } |
| 103767 | } |
| @@ -103645,11 +103808,11 @@ | |
| 103808 | ** and column is found but leave okToChngToIN false if not found. |
| 103809 | */ |
| 103810 | for(j=0; j<2 && !okToChngToIN; j++){ |
| 103811 | pOrTerm = pOrWc->a; |
| 103812 | for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){ |
| 103813 | assert( pOrTerm->eOperator & WO_EQ ); |
| 103814 | pOrTerm->wtFlags &= ~TERM_OR_OK; |
| 103815 | if( pOrTerm->leftCursor==iCursor ){ |
| 103816 | /* This is the 2-bit case and we are on the second iteration and |
| 103817 | ** current term is from the first iteration. So skip this term. */ |
| 103818 | assert( j==1 ); |
| @@ -103671,21 +103834,21 @@ | |
| 103834 | } |
| 103835 | if( i<0 ){ |
| 103836 | /* No candidate table+column was found. This can only occur |
| 103837 | ** on the second iteration */ |
| 103838 | assert( j==1 ); |
| 103839 | assert( IsPowerOfTwo(chngToIN) ); |
| 103840 | assert( chngToIN==getMask(pMaskSet, iCursor) ); |
| 103841 | break; |
| 103842 | } |
| 103843 | testcase( j==1 ); |
| 103844 | |
| 103845 | /* We have found a candidate table and column. Check to see if that |
| 103846 | ** table and column is common to every term in the OR clause */ |
| 103847 | okToChngToIN = 1; |
| 103848 | for(; i>=0 && okToChngToIN; i--, pOrTerm++){ |
| 103849 | assert( pOrTerm->eOperator & WO_EQ ); |
| 103850 | if( pOrTerm->leftCursor!=iCursor ){ |
| 103851 | pOrTerm->wtFlags &= ~TERM_OR_OK; |
| 103852 | }else if( pOrTerm->u.leftColumn!=iColumn ){ |
| 103853 | okToChngToIN = 0; |
| 103854 | }else{ |
| @@ -103717,11 +103880,11 @@ | |
| 103880 | Expr *pLeft = 0; /* The LHS of the IN operator */ |
| 103881 | Expr *pNew; /* The complete IN operator */ |
| 103882 | |
| 103883 | for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){ |
| 103884 | if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue; |
| 103885 | assert( pOrTerm->eOperator & WO_EQ ); |
| 103886 | assert( pOrTerm->leftCursor==iCursor ); |
| 103887 | assert( pOrTerm->u.leftColumn==iColumn ); |
| 103888 | pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); |
| 103889 | pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup); |
| 103890 | pLeft = pOrTerm->pExpr->pLeft; |
| @@ -103746,11 +103909,10 @@ | |
| 103909 | pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */ |
| 103910 | } |
| 103911 | } |
| 103912 | } |
| 103913 | #endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */ |
| 103914 | |
| 103915 | /* |
| 103916 | ** The input to this routine is an WhereTerm structure with only the |
| 103917 | ** "pExpr" field filled in. The job of this routine is to analyze the |
| 103918 | ** subexpression and populate all the other fields of the WhereTerm |
| @@ -103816,21 +103978,23 @@ | |
| 103978 | } |
| 103979 | pTerm->prereqAll = prereqAll; |
| 103980 | pTerm->leftCursor = -1; |
| 103981 | pTerm->iParent = -1; |
| 103982 | pTerm->eOperator = 0; |
| 103983 | if( allowedOp(op) ){ |
| 103984 | Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft); |
| 103985 | Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight); |
| 103986 | u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV; |
| 103987 | if( pLeft->op==TK_COLUMN ){ |
| 103988 | pTerm->leftCursor = pLeft->iTable; |
| 103989 | pTerm->u.leftColumn = pLeft->iColumn; |
| 103990 | pTerm->eOperator = operatorMask(op) & opMask; |
| 103991 | } |
| 103992 | if( pRight && pRight->op==TK_COLUMN ){ |
| 103993 | WhereTerm *pNew; |
| 103994 | Expr *pDup; |
| 103995 | u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */ |
| 103996 | if( pTerm->leftCursor>=0 ){ |
| 103997 | int idxNew; |
| 103998 | pDup = sqlite3ExprDup(db, pExpr, 0); |
| 103999 | if( db->mallocFailed ){ |
| 104000 | sqlite3ExprDelete(db, pDup); |
| @@ -103841,10 +104005,17 @@ | |
| 104005 | pNew = &pWC->a[idxNew]; |
| 104006 | pNew->iParent = idxTerm; |
| 104007 | pTerm = &pWC->a[idxTerm]; |
| 104008 | pTerm->nChild = 1; |
| 104009 | pTerm->wtFlags |= TERM_COPIED; |
| 104010 | if( pExpr->op==TK_EQ |
| 104011 | && !ExprHasProperty(pExpr, EP_FromJoin) |
| 104012 | && OptimizationEnabled(db, SQLITE_Transitive) |
| 104013 | ){ |
| 104014 | pTerm->eOperator |= WO_EQUIV; |
| 104015 | eExtraOp = WO_EQUIV; |
| 104016 | } |
| 104017 | }else{ |
| 104018 | pDup = pExpr; |
| 104019 | pNew = pTerm; |
| 104020 | } |
| 104021 | exprCommute(pParse, pDup); |
| @@ -103852,11 +104023,11 @@ | |
| 104023 | pNew->leftCursor = pLeft->iTable; |
| 104024 | pNew->u.leftColumn = pLeft->iColumn; |
| 104025 | testcase( (prereqLeft | extraRight) != prereqLeft ); |
| 104026 | pNew->prereqRight = prereqLeft | extraRight; |
| 104027 | pNew->prereqAll = prereqAll; |
| 104028 | pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask; |
| 104029 | } |
| 104030 | } |
| 104031 | |
| 104032 | #ifndef SQLITE_OMIT_BETWEEN_OPTIMIZATION |
| 104033 | /* If a term is the BETWEEN operator, create two new virtual terms |
| @@ -104311,11 +104482,11 @@ | |
| 104482 | return; |
| 104483 | } |
| 104484 | |
| 104485 | /* Search the WHERE clause terms for a usable WO_OR term. */ |
| 104486 | for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ |
| 104487 | if( (pTerm->eOperator & WO_OR)!=0 |
| 104488 | && ((pTerm->prereqAll & ~maskSrc) & p->notReady)==0 |
| 104489 | && (pTerm->u.pOrInfo->indexable & maskSrc)!=0 |
| 104490 | ){ |
| 104491 | WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc; |
| 104492 | WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm]; |
| @@ -104332,11 +104503,11 @@ | |
| 104503 | sBOI.ppIdxInfo = 0; |
| 104504 | for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){ |
| 104505 | WHERETRACE(("... Multi-index OR testing for term %d of %d....\n", |
| 104506 | (pOrTerm - pOrWC->a), (pTerm - pWC->a) |
| 104507 | )); |
| 104508 | if( (pOrTerm->eOperator& WO_AND)!=0 ){ |
| 104509 | sBOI.pWC = &pOrTerm->u.pAndInfo->wc; |
| 104510 | bestIndex(&sBOI); |
| 104511 | }else if( pOrTerm->leftCursor==iCur ){ |
| 104512 | WhereClause tempWC; |
| 104513 | tempWC.pParse = pWC->pParse; |
| @@ -104393,11 +104564,11 @@ | |
| 104564 | struct SrcList_item *pSrc, /* Table we are trying to access */ |
| 104565 | Bitmask notReady /* Tables in outer loops of the join */ |
| 104566 | ){ |
| 104567 | char aff; |
| 104568 | if( pTerm->leftCursor!=pSrc->iCursor ) return 0; |
| 104569 | if( (pTerm->eOperator & WO_EQ)==0 ) return 0; |
| 104570 | if( (pTerm->prereqRight & notReady)!=0 ) return 0; |
| 104571 | aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; |
| 104572 | if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; |
| 104573 | return 1; |
| 104574 | } |
| @@ -104655,13 +104826,13 @@ | |
| 104826 | |
| 104827 | /* Count the number of possible WHERE clause constraints referring |
| 104828 | ** to this virtual table */ |
| 104829 | for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 104830 | if( pTerm->leftCursor != pSrc->iCursor ) continue; |
| 104831 | assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); |
| 104832 | testcase( pTerm->eOperator & WO_IN ); |
| 104833 | testcase( pTerm->eOperator & WO_ISNULL ); |
| 104834 | if( pTerm->eOperator & (WO_ISNULL) ) continue; |
| 104835 | if( pTerm->wtFlags & TERM_VNULL ) continue; |
| 104836 | nTerm++; |
| 104837 | } |
| 104838 | |
| @@ -104708,18 +104879,18 @@ | |
| 104879 | pUsage; |
| 104880 | |
| 104881 | for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 104882 | u8 op; |
| 104883 | if( pTerm->leftCursor != pSrc->iCursor ) continue; |
| 104884 | assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); |
| 104885 | testcase( pTerm->eOperator & WO_IN ); |
| 104886 | testcase( pTerm->eOperator & WO_ISNULL ); |
| 104887 | if( pTerm->eOperator & (WO_ISNULL) ) continue; |
| 104888 | if( pTerm->wtFlags & TERM_VNULL ) continue; |
| 104889 | pIdxCons[j].iColumn = pTerm->u.leftColumn; |
| 104890 | pIdxCons[j].iTermOffset = i; |
| 104891 | op = (u8)pTerm->eOperator & WO_ALL; |
| 104892 | if( op==WO_IN ) op = WO_EQ; |
| 104893 | pIdxCons[j].op = op; |
| 104894 | /* The direct assignment in the previous line is possible only because |
| 104895 | ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The |
| 104896 | ** following asserts verify this fact. */ |
| @@ -104885,11 +105056,11 @@ | |
| 105056 | pUsage = pIdxInfo->aConstraintUsage; |
| 105057 | for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ |
| 105058 | j = pIdxCons->iTermOffset; |
| 105059 | pTerm = &pWC->a[j]; |
| 105060 | if( (pTerm->prereqRight&p->notReady)==0 |
| 105061 | && (bAllowIN || (pTerm->eOperator & WO_IN)==0) |
| 105062 | ){ |
| 105063 | pIdxCons->usable = 1; |
| 105064 | }else{ |
| 105065 | pIdxCons->usable = 0; |
| 105066 | } |
| @@ -104917,11 +105088,11 @@ | |
| 105088 | for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ |
| 105089 | if( pUsage[i].argvIndex>0 ){ |
| 105090 | j = pIdxCons->iTermOffset; |
| 105091 | pTerm = &pWC->a[j]; |
| 105092 | p->cost.used |= pTerm->prereqRight; |
| 105093 | if( (pTerm->eOperator & WO_IN)!=0 && pUsage[i].omit==0 ){ |
| 105094 | /* Do not attempt to use an IN constraint if the virtual table |
| 105095 | ** says that the equivalent EQ constraint cannot be safely omitted. |
| 105096 | ** If we do attempt to use such a constraint, some rows might be |
| 105097 | ** repeated in the output. */ |
| 105098 | break; |
| @@ -105223,28 +105394,28 @@ | |
| 105394 | u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; |
| 105395 | |
| 105396 | if( pLower ){ |
| 105397 | Expr *pExpr = pLower->pExpr->pRight; |
| 105398 | rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); |
| 105399 | assert( (pLower->eOperator & (WO_GT|WO_GE))!=0 ); |
| 105400 | if( rc==SQLITE_OK |
| 105401 | && whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK |
| 105402 | ){ |
| 105403 | iLower = a[0]; |
| 105404 | if( (pLower->eOperator & WO_GT)!=0 ) iLower += a[1]; |
| 105405 | } |
| 105406 | sqlite3ValueFree(pRangeVal); |
| 105407 | } |
| 105408 | if( rc==SQLITE_OK && pUpper ){ |
| 105409 | Expr *pExpr = pUpper->pExpr->pRight; |
| 105410 | rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); |
| 105411 | assert( (pUpper->eOperator & (WO_LT|WO_LE))!=0 ); |
| 105412 | if( rc==SQLITE_OK |
| 105413 | && whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK |
| 105414 | ){ |
| 105415 | iUpper = a[0]; |
| 105416 | if( (pUpper->eOperator & WO_LE)!=0 ) iUpper += a[1]; |
| 105417 | } |
| 105418 | sqlite3ValueFree(pRangeVal); |
| 105419 | } |
| 105420 | if( rc==SQLITE_OK ){ |
| 105421 | if( iUpper<=iLower ){ |
| @@ -105548,16 +105719,16 @@ | |
| 105719 | ** if there are any X= or X IS NULL constraints in the WHERE clause. */ |
| 105720 | pConstraint = findTerm(p->pWC, base, iColumn, p->notReady, |
| 105721 | WO_EQ|WO_ISNULL|WO_IN, pIdx); |
| 105722 | if( pConstraint==0 ){ |
| 105723 | isEq = 0; |
| 105724 | }else if( (pConstraint->eOperator & WO_IN)!=0 ){ |
| 105725 | /* Constraints of the form: "X IN ..." cannot be used with an ORDER BY |
| 105726 | ** because we do not know in what order the values on the RHS of the IN |
| 105727 | ** operator will occur. */ |
| 105728 | break; |
| 105729 | }else if( (pConstraint->eOperator & WO_ISNULL)!=0 ){ |
| 105730 | uniqueNotNull = 0; |
| 105731 | isEq = 1; /* "X IS NULL" means X has only a single value */ |
| 105732 | }else if( pConstraint->prereqRight==0 ){ |
| 105733 | isEq = 1; /* Constraint "X=constant" means X has only a single value */ |
| 105734 | }else{ |
| @@ -105903,11 +106074,11 @@ | |
| 106074 | int bRev = 2; |
| 106075 | WHERETRACE((" --> before isSortingIndex: nPriorSat=%d\n",nPriorSat)); |
| 106076 | pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev); |
| 106077 | WHERETRACE((" --> after isSortingIndex: bRev=%d nOBSat=%d\n", |
| 106078 | bRev, pc.plan.nOBSat)); |
| 106079 | if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){ |
| 106080 | pc.plan.wsFlags |= WHERE_ORDERED; |
| 106081 | } |
| 106082 | if( nOrderBy==pc.plan.nOBSat ){ |
| 106083 | bSort = 0; |
| 106084 | pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE; |
| @@ -105966,16 +106137,17 @@ | |
| 106137 | */ |
| 106138 | if( pc.plan.nRow>(double)1 && pc.plan.nEq==1 |
| 106139 | && pFirstTerm!=0 && aiRowEst[1]>1 ){ |
| 106140 | assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 ); |
| 106141 | if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){ |
| 106142 | testcase( pFirstTerm->eOperator & WO_EQ ); |
| 106143 | testcase( pFirstTerm->eOperator & WO_EQUIV ); |
| 106144 | testcase( pFirstTerm->eOperator & WO_ISNULL ); |
| 106145 | whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, |
| 106146 | &pc.plan.nRow); |
| 106147 | }else if( bInEst==0 ){ |
| 106148 | assert( pFirstTerm->eOperator & WO_IN ); |
| 106149 | whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, |
| 106150 | &pc.plan.nRow); |
| 106151 | } |
| 106152 | } |
| 106153 | #endif /* SQLITE_ENABLE_STAT3 */ |
| @@ -106118,11 +106290,11 @@ | |
| 106290 | ** more selective intentionally because of the subjective |
| 106291 | ** observation that indexed range constraints really are more |
| 106292 | ** selective in practice, on average. */ |
| 106293 | pc.plan.nRow /= 3; |
| 106294 | } |
| 106295 | }else if( (pTerm->eOperator & WO_NOOP)==0 ){ |
| 106296 | /* Any other expression lowers the output row count by half */ |
| 106297 | pc.plan.nRow /= 2; |
| 106298 | } |
| 106299 | } |
| 106300 | if( pc.plan.nRow<2 ) pc.plan.nRow = 2; |
| @@ -106170,12 +106342,13 @@ | |
| 106342 | assert( pSrc->pIndex==0 |
| 106343 | || p->cost.plan.u.pIdx==0 |
| 106344 | || p->cost.plan.u.pIdx==pSrc->pIndex |
| 106345 | ); |
| 106346 | |
| 106347 | WHERETRACE((" best index is %s cost=%.1f\n", |
| 106348 | p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk", |
| 106349 | p->cost.rCost)); |
| 106350 | |
| 106351 | bestOrClauseIndex(p); |
| 106352 | bestAutomaticIndex(p); |
| 106353 | p->cost.plan.wsFlags |= eqTermMask; |
| 106354 | } |
| @@ -106753,11 +106926,10 @@ | |
| 106926 | */ |
| 106927 | iReleaseReg = sqlite3GetTempReg(pParse); |
| 106928 | pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0); |
| 106929 | assert( pTerm!=0 ); |
| 106930 | assert( pTerm->pExpr!=0 ); |
| 106931 | assert( omitTable==0 ); |
| 106932 | testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ |
| 106933 | iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, iReleaseReg); |
| 106934 | addrNxt = pLevel->addrNxt; |
| 106935 | sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); |
| @@ -107144,11 +107316,11 @@ | |
| 107316 | int ii; /* Loop counter */ |
| 107317 | Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ |
| 107318 | |
| 107319 | pTerm = pLevel->plan.u.pTerm; |
| 107320 | assert( pTerm!=0 ); |
| 107321 | assert( pTerm->eOperator & WO_OR ); |
| 107322 | assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); |
| 107323 | pOrWc = &pTerm->u.pOrInfo->wc; |
| 107324 | pLevel->op = OP_Return; |
| 107325 | pLevel->p1 = regReturn; |
| 107326 | |
| @@ -107217,11 +107389,11 @@ | |
| 107389 | } |
| 107390 | } |
| 107391 | |
| 107392 | for(ii=0; ii<pOrWc->nTerm; ii++){ |
| 107393 | WhereTerm *pOrTerm = &pOrWc->a[ii]; |
| 107394 | if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){ |
| 107395 | WhereInfo *pSubWInfo; /* Info for single OR-term scan */ |
| 107396 | Expr *pOrExpr = pOrTerm->pExpr; |
| 107397 | if( pAndExpr ){ |
| 107398 | pAndExpr->pLeft = pOrExpr; |
| 107399 | pOrExpr = pAndExpr; |
| @@ -107672,10 +107844,11 @@ | |
| 107844 | Index *pIdx; /* Index for FROM table at pTabItem */ |
| 107845 | int j; /* For looping over FROM tables */ |
| 107846 | int bestJ = -1; /* The value of j */ |
| 107847 | Bitmask m; /* Bitmask value for j or bestJ */ |
| 107848 | int isOptimal; /* Iterator for optimal/non-optimal search */ |
| 107849 | int ckOptimal; /* Do the optimal scan check */ |
| 107850 | int nUnconstrained; /* Number tables without INDEXED BY */ |
| 107851 | Bitmask notIndexed; /* Mask of tables that cannot use an index */ |
| 107852 | |
| 107853 | memset(&bestPlan, 0, sizeof(bestPlan)); |
| 107854 | bestPlan.rCost = SQLITE_BIG_DBL; |
| @@ -107706,14 +107879,12 @@ | |
| 107879 | ** |
| 107880 | ** The second loop iteration is only performed if no optimal scan |
| 107881 | ** strategies were found by the first iteration. This second iteration |
| 107882 | ** is used to search for the lowest cost scan overall. |
| 107883 | ** |
| 107884 | ** Without the optimal scan step (the first iteration) a suboptimal |
| 107885 | ** plan might be chosen for queries like this: |
| 107886 | ** |
| 107887 | ** CREATE TABLE t1(a, b); |
| 107888 | ** CREATE TABLE t2(c, d); |
| 107889 | ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a; |
| 107890 | ** |
| @@ -107724,20 +107895,44 @@ | |
| 107895 | ** algorithm may choose to use t2 for the outer loop, which is a much |
| 107896 | ** costlier approach. |
| 107897 | */ |
| 107898 | nUnconstrained = 0; |
| 107899 | notIndexed = 0; |
| 107900 | |
| 107901 | /* The optimal scan check only occurs if there are two or more tables |
| 107902 | ** available to be reordered */ |
| 107903 | if( iFrom==nTabList-1 ){ |
| 107904 | ckOptimal = 0; /* Common case of just one table in the FROM clause */ |
| 107905 | }else{ |
| 107906 | ckOptimal = -1; |
| 107907 | for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){ |
| 107908 | m = getMask(pMaskSet, sWBI.pSrc->iCursor); |
| 107909 | if( (m & sWBI.notValid)==0 ){ |
| 107910 | if( j==iFrom ) iFrom++; |
| 107911 | continue; |
| 107912 | } |
| 107913 | if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ) break; |
| 107914 | if( ++ckOptimal ) break; |
| 107915 | if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break; |
| 107916 | } |
| 107917 | } |
| 107918 | assert( ckOptimal==0 || ckOptimal==1 ); |
| 107919 | |
| 107920 | for(isOptimal=ckOptimal; isOptimal>=0 && bestJ<0; isOptimal--){ |
| 107921 | for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){ |
| 107922 | if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ){ |
| 107923 | /* This break and one like it in the ckOptimal computation loop |
| 107924 | ** above prevent table reordering across LEFT and CROSS JOINs. |
| 107925 | ** The LEFT JOIN case is necessary for correctness. The prohibition |
| 107926 | ** against reordering across a CROSS JOIN is an SQLite feature that |
| 107927 | ** allows the developer to control table reordering */ |
| 107928 | break; |
| 107929 | } |
| 107930 | m = getMask(pMaskSet, sWBI.pSrc->iCursor); |
| 107931 | if( (m & sWBI.notValid)==0 ){ |
| 107932 | assert( j>iFrom ); |
| 107933 | continue; |
| 107934 | } |
| 107935 | sWBI.notReady = (isOptimal ? m : sWBI.notValid); |
| 107936 | if( sWBI.pSrc->pIndex==0 ) nUnconstrained++; |
| 107937 | |
| 107938 | WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n", |
| @@ -107763,12 +107958,12 @@ | |
| 107958 | if( isOptimal && (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){ |
| 107959 | notIndexed |= m; |
| 107960 | } |
| 107961 | if( isOptimal ){ |
| 107962 | pWInfo->a[j].rOptCost = sWBI.cost.rCost; |
| 107963 | }else if( ckOptimal ){ |
| 107964 | /* If two or more tables have nearly the same outer loop cost, but |
| 107965 | ** very different inner loop (optimal) cost, we want to choose |
| 107966 | ** for the outer loop that table which benefits the least from |
| 107967 | ** being in the inner loop. The following code scales the |
| 107968 | ** outer loop cost estimate to accomplish that. */ |
| 107969 | WHERETRACE((" scaling cost from %.1f to %.1f\n", |
| @@ -107809,15 +108004,23 @@ | |
| 108004 | sWBI.cost.rCost, sWBI.cost.plan.nRow, |
| 108005 | sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags)); |
| 108006 | bestPlan = sWBI.cost; |
| 108007 | bestJ = j; |
| 108008 | } |
| 108009 | |
| 108010 | /* In a join like "w JOIN x LEFT JOIN y JOIN z" make sure that |
| 108011 | ** table y (and not table z) is always the next inner loop inside |
| 108012 | ** of table x. */ |
| 108013 | if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break; |
| 108014 | } |
| 108015 | } |
| 108016 | assert( bestJ>=0 ); |
| 108017 | assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); |
| 108018 | assert( bestJ==iFrom || (pTabList->a[iFrom].jointype & JT_LEFT)==0 ); |
| 108019 | testcase( bestJ>iFrom && (pTabList->a[iFrom].jointype & JT_CROSS)!=0 ); |
| 108020 | testcase( bestJ>iFrom && bestJ<nTabList-1 |
| 108021 | && (pTabList->a[bestJ+1].jointype & JT_LEFT)!=0 ); |
| 108022 | WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n" |
| 108023 | " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n", |
| 108024 | bestJ, pTabList->a[bestJ].pTab->zName, |
| 108025 | pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow, |
| 108026 | bestPlan.plan.nOBSat, bestPlan.plan.wsFlags)); |
| @@ -136543,11 +136746,12 @@ | |
| 136746 | ** would fit in a single node, use a smaller node-size. |
| 136747 | */ |
| 136748 | static int getNodeSize( |
| 136749 | sqlite3 *db, /* Database handle */ |
| 136750 | Rtree *pRtree, /* Rtree handle */ |
| 136751 | int isCreate, /* True for xCreate, false for xConnect */ |
| 136752 | char **pzErr /* OUT: Error message, if any */ |
| 136753 | ){ |
| 136754 | int rc; |
| 136755 | char *zSql; |
| 136756 | if( isCreate ){ |
| 136757 | int iPageSize = 0; |
| @@ -136556,17 +136760,22 @@ | |
| 136760 | if( rc==SQLITE_OK ){ |
| 136761 | pRtree->iNodeSize = iPageSize-64; |
| 136762 | if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){ |
| 136763 | pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS; |
| 136764 | } |
| 136765 | }else{ |
| 136766 | *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); |
| 136767 | } |
| 136768 | }else{ |
| 136769 | zSql = sqlite3_mprintf( |
| 136770 | "SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1", |
| 136771 | pRtree->zDb, pRtree->zName |
| 136772 | ); |
| 136773 | rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize); |
| 136774 | if( rc!=SQLITE_OK ){ |
| 136775 | *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); |
| 136776 | } |
| 136777 | } |
| 136778 | |
| 136779 | sqlite3_free(zSql); |
| 136780 | return rc; |
| 136781 | } |
| @@ -136626,11 +136835,11 @@ | |
| 136835 | pRtree->eCoordType = eCoordType; |
| 136836 | memcpy(pRtree->zDb, argv[1], nDb); |
| 136837 | memcpy(pRtree->zName, argv[2], nName); |
| 136838 | |
| 136839 | /* Figure out the node size to use. */ |
| 136840 | rc = getNodeSize(db, pRtree, isCreate, pzErr); |
| 136841 | |
| 136842 | /* Create/Connect to the underlying relational database schema. If |
| 136843 | ** that is successful, call sqlite3_declare_vtab() to configure |
| 136844 | ** the r-tree table schema. |
| 136845 | */ |
| 136846 |
+381
-172
| --- src/sqlite3.c | ||
| +++ src/sqlite3.c | ||
| @@ -673,11 +673,11 @@ | ||
| 673 | 673 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 674 | 674 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 675 | 675 | */ |
| 676 | 676 | #define SQLITE_VERSION "3.7.16" |
| 677 | 677 | #define SQLITE_VERSION_NUMBER 3007016 |
| 678 | -#define SQLITE_SOURCE_ID "2012-12-21 16:15:35 ff6857b6ed6a46671006b75157d8cf853a816ef9" | |
| 678 | +#define SQLITE_SOURCE_ID "2013-01-17 17:20:49 38852f158ab20bb4d7b264af987ec1538052bec3" | |
| 679 | 679 | |
| 680 | 680 | /* |
| 681 | 681 | ** CAPI3REF: Run-Time Library Version Numbers |
| 682 | 682 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 683 | 683 | ** |
| @@ -8238,10 +8238,15 @@ | ||
| 8238 | 8238 | ** A convenience macro that returns the number of elements in |
| 8239 | 8239 | ** an array. |
| 8240 | 8240 | */ |
| 8241 | 8241 | #define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0]))) |
| 8242 | 8242 | |
| 8243 | +/* | |
| 8244 | +** Determine if the argument is a power of two | |
| 8245 | +*/ | |
| 8246 | +#define IsPowerOfTwo(X) (((X)&((X)-1))==0) | |
| 8247 | + | |
| 8243 | 8248 | /* |
| 8244 | 8249 | ** The following value as a destructor means to use sqlite3DbFree(). |
| 8245 | 8250 | ** The sqlite3DbFree() routine requires two parameters instead of the |
| 8246 | 8251 | ** one parameter that destructors normally want. So we have to introduce |
| 8247 | 8252 | ** this magic value that the code knows to handle differently. Any |
| @@ -10042,10 +10047,11 @@ | ||
| 10042 | 10047 | #define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */ |
| 10043 | 10048 | #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ |
| 10044 | 10049 | #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ |
| 10045 | 10050 | #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ |
| 10046 | 10051 | #define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */ |
| 10052 | +#define SQLITE_Transitive 0x0200 /* Transitive constraints */ | |
| 10047 | 10053 | #define SQLITE_AllOpts 0xffff /* All optimizations */ |
| 10048 | 10054 | |
| 10049 | 10055 | /* |
| 10050 | 10056 | ** Macros for testing whether or not optimizations are enabled or disabled. |
| 10051 | 10057 | */ |
| @@ -10553,24 +10559,24 @@ | ||
| 10553 | 10559 | ** and the value of Index.onError indicate the which conflict resolution |
| 10554 | 10560 | ** algorithm to employ whenever an attempt is made to insert a non-unique |
| 10555 | 10561 | ** element. |
| 10556 | 10562 | */ |
| 10557 | 10563 | struct Index { |
| 10558 | - char *zName; /* Name of this index */ | |
| 10559 | - int *aiColumn; /* Which columns are used by this index. 1st is 0 */ | |
| 10560 | - tRowcnt *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */ | |
| 10561 | - Table *pTable; /* The SQL table being indexed */ | |
| 10562 | - char *zColAff; /* String defining the affinity of each column */ | |
| 10563 | - Index *pNext; /* The next index associated with the same table */ | |
| 10564 | - Schema *pSchema; /* Schema containing this index */ | |
| 10565 | - u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */ | |
| 10566 | - char **azColl; /* Array of collation sequence names for index */ | |
| 10567 | - int nColumn; /* Number of columns in the table used by this index */ | |
| 10568 | - int tnum; /* Page containing root of this index in database file */ | |
| 10569 | - u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ | |
| 10570 | - u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ | |
| 10571 | - u8 bUnordered; /* Use this index for == or IN queries only */ | |
| 10564 | + char *zName; /* Name of this index */ | |
| 10565 | + int *aiColumn; /* Which columns are used by this index. 1st is 0 */ | |
| 10566 | + tRowcnt *aiRowEst; /* From ANALYZE: Est. rows selected by each column */ | |
| 10567 | + Table *pTable; /* The SQL table being indexed */ | |
| 10568 | + char *zColAff; /* String defining the affinity of each column */ | |
| 10569 | + Index *pNext; /* The next index associated with the same table */ | |
| 10570 | + Schema *pSchema; /* Schema containing this index */ | |
| 10571 | + u8 *aSortOrder; /* for each column: True==DESC, False==ASC */ | |
| 10572 | + char **azColl; /* Array of collation sequence names for index */ | |
| 10573 | + int tnum; /* DB Page containing root of this index */ | |
| 10574 | + u16 nColumn; /* Number of columns in table used by this index */ | |
| 10575 | + u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ | |
| 10576 | + unsigned autoIndex:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */ | |
| 10577 | + unsigned bUnordered:1; /* Use this index for == or IN queries only */ | |
| 10572 | 10578 | #ifdef SQLITE_ENABLE_STAT3 |
| 10573 | 10579 | int nSample; /* Number of elements in aSample[] */ |
| 10574 | 10580 | tRowcnt avgEq; /* Average nEq value for key values not in aSample */ |
| 10575 | 10581 | IndexSample *aSample; /* Samples of the left-most key */ |
| 10576 | 10582 | #endif |
| @@ -10840,22 +10846,31 @@ | ||
| 10840 | 10846 | ** name. An expr/name combination can be used in several ways, such |
| 10841 | 10847 | ** as the list of "expr AS ID" fields following a "SELECT" or in the |
| 10842 | 10848 | ** list of "ID = expr" items in an UPDATE. A list of expressions can |
| 10843 | 10849 | ** also be used as the argument to a function, in which case the a.zName |
| 10844 | 10850 | ** field is not used. |
| 10851 | +** | |
| 10852 | +** By default the Expr.zSpan field holds a human-readable description of | |
| 10853 | +** the expression that is used in the generation of error messages and | |
| 10854 | +** column labels. In this case, Expr.zSpan is typically the text of a | |
| 10855 | +** column expression as it exists in a SELECT statement. However, if | |
| 10856 | +** the bSpanIsTab flag is set, then zSpan is overloaded to mean the name | |
| 10857 | +** of the result column in the form: DATABASE.TABLE.COLUMN. This later | |
| 10858 | +** form is used for name resolution with nested FROM clauses. | |
| 10845 | 10859 | */ |
| 10846 | 10860 | struct ExprList { |
| 10847 | 10861 | int nExpr; /* Number of expressions on the list */ |
| 10848 | 10862 | int iECursor; /* VDBE Cursor associated with this ExprList */ |
| 10849 | 10863 | struct ExprList_item { /* For each expression in the list */ |
| 10850 | - Expr *pExpr; /* The list of expressions */ | |
| 10851 | - char *zName; /* Token associated with this expression */ | |
| 10852 | - char *zSpan; /* Original text of the expression */ | |
| 10853 | - u8 sortOrder; /* 1 for DESC or 0 for ASC */ | |
| 10854 | - u8 done; /* A flag to indicate when processing is finished */ | |
| 10855 | - u16 iOrderByCol; /* For ORDER BY, column number in result set */ | |
| 10856 | - u16 iAlias; /* Index into Parse.aAlias[] for zName */ | |
| 10864 | + Expr *pExpr; /* The list of expressions */ | |
| 10865 | + char *zName; /* Token associated with this expression */ | |
| 10866 | + char *zSpan; /* Original text of the expression */ | |
| 10867 | + u8 sortOrder; /* 1 for DESC or 0 for ASC */ | |
| 10868 | + unsigned done :1; /* A flag to indicate when processing is finished */ | |
| 10869 | + unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */ | |
| 10870 | + u16 iOrderByCol; /* For ORDER BY, column number in result set */ | |
| 10871 | + u16 iAlias; /* Index into Parse.aAlias[] for zName */ | |
| 10857 | 10872 | } *a; /* Alloc a power of two greater or equal to nExpr */ |
| 10858 | 10873 | }; |
| 10859 | 10874 | |
| 10860 | 10875 | /* |
| 10861 | 10876 | ** An instance of this structure is used by the parser to record both |
| @@ -12141,10 +12156,11 @@ | ||
| 12141 | 12156 | SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *); |
| 12142 | 12157 | SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...); |
| 12143 | 12158 | SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*); |
| 12144 | 12159 | SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *, Expr *, int, int); |
| 12145 | 12160 | SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*); |
| 12161 | +SQLITE_PRIVATE int sqlite3MatchSpanName(const char*, const char*, const char*, const char*); | |
| 12146 | 12162 | SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*); |
| 12147 | 12163 | SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); |
| 12148 | 12164 | SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); |
| 12149 | 12165 | SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int); |
| 12150 | 12166 | SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *); |
| @@ -23289,15 +23305,11 @@ | ||
| 23289 | 23305 | { "pwrite64", (sqlite3_syscall_ptr)0, 0 }, |
| 23290 | 23306 | #endif |
| 23291 | 23307 | #define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\ |
| 23292 | 23308 | aSyscall[13].pCurrent) |
| 23293 | 23309 | |
| 23294 | -#if SQLITE_ENABLE_LOCKING_STYLE | |
| 23295 | 23310 | { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 }, |
| 23296 | -#else | |
| 23297 | - { "fchmod", (sqlite3_syscall_ptr)0, 0 }, | |
| 23298 | -#endif | |
| 23299 | 23311 | #define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) |
| 23300 | 23312 | |
| 23301 | 23313 | #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE |
| 23302 | 23314 | { "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 }, |
| 23303 | 23315 | #else |
| @@ -23318,13 +23330,10 @@ | ||
| 23318 | 23330 | #define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent) |
| 23319 | 23331 | |
| 23320 | 23332 | { "fchown", (sqlite3_syscall_ptr)posixFchown, 0 }, |
| 23321 | 23333 | #define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent) |
| 23322 | 23334 | |
| 23323 | - { "umask", (sqlite3_syscall_ptr)umask, 0 }, | |
| 23324 | -#define osUmask ((mode_t(*)(mode_t))aSyscall[21].pCurrent) | |
| 23325 | - | |
| 23326 | 23335 | }; /* End of the overrideable system calls */ |
| 23327 | 23336 | |
| 23328 | 23337 | /* |
| 23329 | 23338 | ** This is the xSetSystemCall() method of sqlite3_vfs for all of the |
| 23330 | 23339 | ** "unix" VFSes. Return SQLITE_OK opon successfully updating the |
| @@ -23425,31 +23434,29 @@ | ||
| 23425 | 23434 | ** process that is able to write to the database will also be able to |
| 23426 | 23435 | ** recover the hot journals. |
| 23427 | 23436 | */ |
| 23428 | 23437 | static int robust_open(const char *z, int f, mode_t m){ |
| 23429 | 23438 | int fd; |
| 23430 | - mode_t m2; | |
| 23431 | - mode_t origM = 0; | |
| 23432 | - if( m==0 ){ | |
| 23433 | - m2 = SQLITE_DEFAULT_FILE_PERMISSIONS; | |
| 23434 | - }else{ | |
| 23435 | - m2 = m; | |
| 23436 | - origM = osUmask(0); | |
| 23437 | - } | |
| 23439 | + mode_t m2 = m ? m : SQLITE_DEFAULT_FILE_PERMISSIONS; | |
| 23438 | 23440 | do{ |
| 23439 | 23441 | #if defined(O_CLOEXEC) |
| 23440 | 23442 | fd = osOpen(z,f|O_CLOEXEC,m2); |
| 23441 | 23443 | #else |
| 23442 | 23444 | fd = osOpen(z,f,m2); |
| 23443 | 23445 | #endif |
| 23444 | 23446 | }while( fd<0 && errno==EINTR ); |
| 23445 | - if( m ){ | |
| 23446 | - osUmask(origM); | |
| 23447 | - } | |
| 23447 | + if( fd>=0 ){ | |
| 23448 | + if( m!=0 ){ | |
| 23449 | + struct stat statbuf; | |
| 23450 | + if( osFstat(fd, &statbuf)==0 && (statbuf.st_mode&0777)!=m ){ | |
| 23451 | + osFchmod(fd, m); | |
| 23452 | + } | |
| 23453 | + } | |
| 23448 | 23454 | #if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC==0) |
| 23449 | - if( fd>=0 ) osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); | |
| 23455 | + osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); | |
| 23450 | 23456 | #endif |
| 23457 | + } | |
| 23451 | 23458 | return fd; |
| 23452 | 23459 | } |
| 23453 | 23460 | |
| 23454 | 23461 | /* |
| 23455 | 23462 | ** Helper functions to obtain and relinquish the global mutex. The |
| @@ -29871,11 +29878,11 @@ | ||
| 29871 | 29878 | }; |
| 29872 | 29879 | unsigned int i; /* Loop counter */ |
| 29873 | 29880 | |
| 29874 | 29881 | /* Double-check that the aSyscall[] array has been constructed |
| 29875 | 29882 | ** correctly. See ticket [bb3a86e890c8e96ab] */ |
| 29876 | - assert( ArraySize(aSyscall)==22 ); | |
| 29883 | + assert( ArraySize(aSyscall)==21 ); | |
| 29877 | 29884 | |
| 29878 | 29885 | /* Register all VFSes defined in the aVfs[] array */ |
| 29879 | 29886 | for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ |
| 29880 | 29887 | sqlite3_vfs_register(&aVfs[i], i==0); |
| 29881 | 29888 | } |
| @@ -72655,10 +72662,39 @@ | ||
| 72655 | 72662 | } |
| 72656 | 72663 | } |
| 72657 | 72664 | return 0; |
| 72658 | 72665 | } |
| 72659 | 72666 | |
| 72667 | +/* | |
| 72668 | +** Subqueries stores the original database, table and column names for their | |
| 72669 | +** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN". | |
| 72670 | +** Check to see if the zSpan given to this routine matches the zDb, zTab, | |
| 72671 | +** and zCol. If any of zDb, zTab, and zCol are NULL then those fields will | |
| 72672 | +** match anything. | |
| 72673 | +*/ | |
| 72674 | +SQLITE_PRIVATE int sqlite3MatchSpanName( | |
| 72675 | + const char *zSpan, | |
| 72676 | + const char *zCol, | |
| 72677 | + const char *zTab, | |
| 72678 | + const char *zDb | |
| 72679 | +){ | |
| 72680 | + int n; | |
| 72681 | + for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} | |
| 72682 | + if( zDb && sqlite3StrNICmp(zSpan, zDb, n)!=0 ){ | |
| 72683 | + return 0; | |
| 72684 | + } | |
| 72685 | + zSpan += n+1; | |
| 72686 | + for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} | |
| 72687 | + if( zTab && sqlite3StrNICmp(zSpan, zTab, n)!=0 ){ | |
| 72688 | + return 0; | |
| 72689 | + } | |
| 72690 | + zSpan += n+1; | |
| 72691 | + if( zCol && sqlite3StrICmp(zSpan, zCol)!=0 ){ | |
| 72692 | + return 0; | |
| 72693 | + } | |
| 72694 | + return 1; | |
| 72695 | +} | |
| 72660 | 72696 | |
| 72661 | 72697 | /* |
| 72662 | 72698 | ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up |
| 72663 | 72699 | ** that name in the set of source tables in pSrcList and make the pExpr |
| 72664 | 72700 | ** expression node refer back to that source column. The following changes |
| @@ -72710,44 +72746,63 @@ | ||
| 72710 | 72746 | |
| 72711 | 72747 | /* Initialize the node to no-match */ |
| 72712 | 72748 | pExpr->iTable = -1; |
| 72713 | 72749 | pExpr->pTab = 0; |
| 72714 | 72750 | ExprSetIrreducible(pExpr); |
| 72751 | + | |
| 72752 | + /* Translate the schema name in zDb into a pointer to the corresponding | |
| 72753 | + ** schema. If not found, pSchema will remain NULL and nothing will match | |
| 72754 | + ** resulting in an appropriate error message toward the end of this routine | |
| 72755 | + */ | |
| 72756 | + if( zDb ){ | |
| 72757 | + for(i=0; i<db->nDb; i++){ | |
| 72758 | + assert( db->aDb[i].zName ); | |
| 72759 | + if( sqlite3StrICmp(db->aDb[i].zName,zDb)==0 ){ | |
| 72760 | + pSchema = db->aDb[i].pSchema; | |
| 72761 | + break; | |
| 72762 | + } | |
| 72763 | + } | |
| 72764 | + } | |
| 72715 | 72765 | |
| 72716 | 72766 | /* Start at the inner-most context and move outward until a match is found */ |
| 72717 | 72767 | while( pNC && cnt==0 ){ |
| 72718 | 72768 | ExprList *pEList; |
| 72719 | 72769 | SrcList *pSrcList = pNC->pSrcList; |
| 72720 | 72770 | |
| 72721 | 72771 | if( pSrcList ){ |
| 72722 | 72772 | for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){ |
| 72723 | 72773 | Table *pTab; |
| 72724 | - int iDb; | |
| 72725 | 72774 | Column *pCol; |
| 72726 | 72775 | |
| 72727 | 72776 | pTab = pItem->pTab; |
| 72728 | 72777 | assert( pTab!=0 && pTab->zName!=0 ); |
| 72729 | - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); | |
| 72730 | 72778 | assert( pTab->nCol>0 ); |
| 72779 | + if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){ | |
| 72780 | + ExprList *pEList = pItem->pSelect->pEList; | |
| 72781 | + int hit = 0; | |
| 72782 | + for(j=0; j<pEList->nExpr; j++){ | |
| 72783 | + if( sqlite3MatchSpanName(pEList->a[j].zSpan, zCol, zTab, zDb) ){ | |
| 72784 | + cnt++; | |
| 72785 | + cntTab = 2; | |
| 72786 | + pMatch = pItem; | |
| 72787 | + pExpr->iColumn = j; | |
| 72788 | + hit = 1; | |
| 72789 | + } | |
| 72790 | + } | |
| 72791 | + if( hit || zTab==0 ) continue; | |
| 72792 | + } | |
| 72793 | + if( zDb && pTab->pSchema!=pSchema ){ | |
| 72794 | + continue; | |
| 72795 | + } | |
| 72731 | 72796 | if( zTab ){ |
| 72732 | - if( pItem->zAlias ){ | |
| 72733 | - char *zTabName = pItem->zAlias; | |
| 72734 | - if( sqlite3StrICmp(zTabName, zTab)!=0 ) continue; | |
| 72735 | - }else{ | |
| 72736 | - char *zTabName = pTab->zName; | |
| 72737 | - if( NEVER(zTabName==0) || sqlite3StrICmp(zTabName, zTab)!=0 ){ | |
| 72738 | - continue; | |
| 72739 | - } | |
| 72740 | - if( zDb!=0 && sqlite3StrICmp(db->aDb[iDb].zName, zDb)!=0 ){ | |
| 72741 | - continue; | |
| 72742 | - } | |
| 72797 | + const char *zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName; | |
| 72798 | + assert( zTabName!=0 ); | |
| 72799 | + if( sqlite3StrICmp(zTabName, zTab)!=0 ){ | |
| 72800 | + continue; | |
| 72743 | 72801 | } |
| 72744 | 72802 | } |
| 72745 | 72803 | if( 0==(cntTab++) ){ |
| 72746 | - pExpr->iTable = pItem->iCursor; | |
| 72747 | - pExpr->pTab = pTab; | |
| 72748 | - pSchema = pTab->pSchema; | |
| 72749 | 72804 | pMatch = pItem; |
| 72750 | 72805 | } |
| 72751 | 72806 | for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){ |
| 72752 | 72807 | if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ |
| 72753 | 72808 | /* If there has been exactly one prior match and this match |
| @@ -72757,21 +72812,23 @@ | ||
| 72757 | 72812 | if( cnt==1 ){ |
| 72758 | 72813 | if( pItem->jointype & JT_NATURAL ) continue; |
| 72759 | 72814 | if( nameInUsingClause(pItem->pUsing, zCol) ) continue; |
| 72760 | 72815 | } |
| 72761 | 72816 | cnt++; |
| 72762 | - pExpr->iTable = pItem->iCursor; | |
| 72763 | - pExpr->pTab = pTab; | |
| 72764 | 72817 | pMatch = pItem; |
| 72765 | - pSchema = pTab->pSchema; | |
| 72766 | 72818 | /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ |
| 72767 | 72819 | pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; |
| 72768 | 72820 | break; |
| 72769 | 72821 | } |
| 72770 | 72822 | } |
| 72771 | 72823 | } |
| 72772 | - } | |
| 72824 | + if( pMatch ){ | |
| 72825 | + pExpr->iTable = pMatch->iCursor; | |
| 72826 | + pExpr->pTab = pMatch->pTab; | |
| 72827 | + pSchema = pExpr->pTab->pSchema; | |
| 72828 | + } | |
| 72829 | + } /* if( pSrcList ) */ | |
| 72773 | 72830 | |
| 72774 | 72831 | #ifndef SQLITE_OMIT_TRIGGER |
| 72775 | 72832 | /* If we have not already resolved the name, then maybe |
| 72776 | 72833 | ** it is a new.* or old.* trigger argument reference |
| 72777 | 72834 | */ |
| @@ -73102,11 +73159,11 @@ | ||
| 73102 | 73159 | #endif |
| 73103 | 73160 | if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){ |
| 73104 | 73161 | sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId); |
| 73105 | 73162 | pNC->nErr++; |
| 73106 | 73163 | is_agg = 0; |
| 73107 | - }else if( no_such_func ){ | |
| 73164 | + }else if( no_such_func && pParse->db->init.busy==0 ){ | |
| 73108 | 73165 | sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId); |
| 73109 | 73166 | pNC->nErr++; |
| 73110 | 73167 | }else if( wrong_num_args ){ |
| 73111 | 73168 | sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()", |
| 73112 | 73169 | nId, zId); |
| @@ -77065,10 +77122,16 @@ | ||
| 77065 | 77122 | for(i=0; i<pList->nExpr; i++){ |
| 77066 | 77123 | sqlite3ExplainPrintf(pOut, "item[%d] = ", i); |
| 77067 | 77124 | sqlite3ExplainPush(pOut); |
| 77068 | 77125 | sqlite3ExplainExpr(pOut, pList->a[i].pExpr); |
| 77069 | 77126 | sqlite3ExplainPop(pOut); |
| 77127 | + if( pList->a[i].zName ){ | |
| 77128 | + sqlite3ExplainPrintf(pOut, " AS %s", pList->a[i].zName); | |
| 77129 | + } | |
| 77130 | + if( pList->a[i].bSpanIsTab ){ | |
| 77131 | + sqlite3ExplainPrintf(pOut, " (%s)", pList->a[i].zSpan); | |
| 77132 | + } | |
| 77070 | 77133 | if( i<pList->nExpr-1 ){ |
| 77071 | 77134 | sqlite3ExplainNL(pOut); |
| 77072 | 77135 | } |
| 77073 | 77136 | } |
| 77074 | 77137 | sqlite3ExplainPop(pOut); |
| @@ -92739,13 +92802,15 @@ | ||
| 92739 | 92802 | if( sqlite3StrICmp(zLeft, "table_info")==0 && zRight ){ |
| 92740 | 92803 | Table *pTab; |
| 92741 | 92804 | if( sqlite3ReadSchema(pParse) ) goto pragma_out; |
| 92742 | 92805 | pTab = sqlite3FindTable(db, zRight, zDb); |
| 92743 | 92806 | if( pTab ){ |
| 92744 | - int i; | |
| 92807 | + int i, k; | |
| 92745 | 92808 | int nHidden = 0; |
| 92746 | 92809 | Column *pCol; |
| 92810 | + Index *pPk; | |
| 92811 | + for(pPk=pTab->pIndex; pPk && pPk->autoIndex!=2; pPk=pPk->pNext){} | |
| 92747 | 92812 | sqlite3VdbeSetNumCols(v, 6); |
| 92748 | 92813 | pParse->nMem = 6; |
| 92749 | 92814 | sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cid", SQLITE_STATIC); |
| 92750 | 92815 | sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC); |
| 92751 | 92816 | sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "type", SQLITE_STATIC); |
| @@ -92766,12 +92831,18 @@ | ||
| 92766 | 92831 | if( pCol->zDflt ){ |
| 92767 | 92832 | sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)pCol->zDflt, 0); |
| 92768 | 92833 | }else{ |
| 92769 | 92834 | sqlite3VdbeAddOp2(v, OP_Null, 0, 5); |
| 92770 | 92835 | } |
| 92771 | - sqlite3VdbeAddOp2(v, OP_Integer, | |
| 92772 | - (pCol->colFlags&COLFLAG_PRIMKEY)!=0, 6); | |
| 92836 | + if( (pCol->colFlags & COLFLAG_PRIMKEY)==0 ){ | |
| 92837 | + k = 0; | |
| 92838 | + }else if( pPk==0 ){ | |
| 92839 | + k = 1; | |
| 92840 | + }else{ | |
| 92841 | + for(k=1; ALWAYS(k<=pTab->nCol) && pPk->aiColumn[k-1]!=i; k++){} | |
| 92842 | + } | |
| 92843 | + sqlite3VdbeAddOp2(v, OP_Integer, k, 6); | |
| 92773 | 92844 | sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6); |
| 92774 | 92845 | } |
| 92775 | 92846 | } |
| 92776 | 92847 | }else |
| 92777 | 92848 | |
| @@ -93516,11 +93587,11 @@ | ||
| 93516 | 93587 | sqlite3_rekey(db, zKey, i/2); |
| 93517 | 93588 | } |
| 93518 | 93589 | }else |
| 93519 | 93590 | #endif |
| 93520 | 93591 | #if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) |
| 93521 | - if( sqlite3StrICmp(zLeft, "activate_extensions")==0 ){ | |
| 93592 | + if( sqlite3StrICmp(zLeft, "activate_extensions")==0 && zRight ){ | |
| 93522 | 93593 | #ifdef SQLITE_HAS_CODEC |
| 93523 | 93594 | if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){ |
| 93524 | 93595 | sqlite3_activate_see(&zRight[4]); |
| 93525 | 93596 | } |
| 93526 | 93597 | #endif |
| @@ -95756,12 +95827,10 @@ | ||
| 95756 | 95827 | |
| 95757 | 95828 | for(i=0, pCol=aCol; i<nCol; i++, pCol++){ |
| 95758 | 95829 | /* Get an appropriate name for the column |
| 95759 | 95830 | */ |
| 95760 | 95831 | p = sqlite3ExprSkipCollate(pEList->a[i].pExpr); |
| 95761 | - assert( p->pRight==0 || ExprHasProperty(p->pRight, EP_IntValue) | |
| 95762 | - || p->pRight->u.zToken==0 || p->pRight->u.zToken[0]!=0 ); | |
| 95763 | 95832 | if( (zName = pEList->a[i].zName)!=0 ){ |
| 95764 | 95833 | /* If the column contains an "AS <name>" phrase, use <name> as the name */ |
| 95765 | 95834 | zName = sqlite3DbStrDup(db, zName); |
| 95766 | 95835 | }else{ |
| 95767 | 95836 | Expr *pColExpr = p; /* The expression that is the result column name */ |
| @@ -95795,10 +95864,13 @@ | ||
| 95795 | 95864 | */ |
| 95796 | 95865 | nName = sqlite3Strlen30(zName); |
| 95797 | 95866 | for(j=cnt=0; j<i; j++){ |
| 95798 | 95867 | if( sqlite3StrICmp(aCol[j].zName, zName)==0 ){ |
| 95799 | 95868 | char *zNewName; |
| 95869 | + int k; | |
| 95870 | + for(k=nName-1; k>1 && sqlite3Isdigit(zName[k]); k--){} | |
| 95871 | + if( zName[k]==':' ) nName = k; | |
| 95800 | 95872 | zName[nName] = 0; |
| 95801 | 95873 | zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt); |
| 95802 | 95874 | sqlite3DbFree(db, zName); |
| 95803 | 95875 | zName = zNewName; |
| 95804 | 95876 | j = -1; |
| @@ -97711,10 +97783,11 @@ | ||
| 97711 | 97783 | int i, j, k; |
| 97712 | 97784 | SrcList *pTabList; |
| 97713 | 97785 | ExprList *pEList; |
| 97714 | 97786 | struct SrcList_item *pFrom; |
| 97715 | 97787 | sqlite3 *db = pParse->db; |
| 97788 | + Expr *pE, *pRight, *pExpr; | |
| 97716 | 97789 | |
| 97717 | 97790 | if( db->mallocFailed ){ |
| 97718 | 97791 | return WRC_Abort; |
| 97719 | 97792 | } |
| 97720 | 97793 | if( NEVER(p->pSrc==0) || (p->selFlags & SF_Expanded)!=0 ){ |
| @@ -97796,11 +97869,11 @@ | ||
| 97796 | 97869 | ** |
| 97797 | 97870 | ** The first loop just checks to see if there are any "*" operators |
| 97798 | 97871 | ** that need expanding. |
| 97799 | 97872 | */ |
| 97800 | 97873 | for(k=0; k<pEList->nExpr; k++){ |
| 97801 | - Expr *pE = pEList->a[k].pExpr; | |
| 97874 | + pE = pEList->a[k].pExpr; | |
| 97802 | 97875 | if( pE->op==TK_ALL ) break; |
| 97803 | 97876 | assert( pE->op!=TK_DOT || pE->pRight!=0 ); |
| 97804 | 97877 | assert( pE->op!=TK_DOT || (pE->pLeft!=0 && pE->pLeft->op==TK_ID) ); |
| 97805 | 97878 | if( pE->op==TK_DOT && pE->pRight->op==TK_ALL ) break; |
| 97806 | 97879 | } |
| @@ -97814,14 +97887,22 @@ | ||
| 97814 | 97887 | ExprList *pNew = 0; |
| 97815 | 97888 | int flags = pParse->db->flags; |
| 97816 | 97889 | int longNames = (flags & SQLITE_FullColNames)!=0 |
| 97817 | 97890 | && (flags & SQLITE_ShortColNames)==0; |
| 97818 | 97891 | |
| 97892 | + /* When processing FROM-clause subqueries, it is always the case | |
| 97893 | + ** that full_column_names=OFF and short_column_names=ON. The | |
| 97894 | + ** sqlite3ResultSetOfSelect() routine makes it so. */ | |
| 97895 | + assert( (p->selFlags & SF_NestedFrom)==0 | |
| 97896 | + || ((flags & SQLITE_FullColNames)==0 && | |
| 97897 | + (flags & SQLITE_ShortColNames)!=0) ); | |
| 97898 | + | |
| 97819 | 97899 | for(k=0; k<pEList->nExpr; k++){ |
| 97820 | - Expr *pE = a[k].pExpr; | |
| 97821 | - assert( pE->op!=TK_DOT || pE->pRight!=0 ); | |
| 97822 | - if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pE->pRight->op!=TK_ALL) ){ | |
| 97900 | + pE = a[k].pExpr; | |
| 97901 | + pRight = pE->pRight; | |
| 97902 | + assert( pE->op!=TK_DOT || pRight!=0 ); | |
| 97903 | + if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pRight->op!=TK_ALL) ){ | |
| 97823 | 97904 | /* This particular expression does not need to be expanded. |
| 97824 | 97905 | */ |
| 97825 | 97906 | pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr); |
| 97826 | 97907 | if( pNew ){ |
| 97827 | 97908 | pNew->a[pNew->nExpr-1].zName = a[k].zName; |
| @@ -97832,44 +97913,56 @@ | ||
| 97832 | 97913 | a[k].pExpr = 0; |
| 97833 | 97914 | }else{ |
| 97834 | 97915 | /* This expression is a "*" or a "TABLE.*" and needs to be |
| 97835 | 97916 | ** expanded. */ |
| 97836 | 97917 | int tableSeen = 0; /* Set to 1 when TABLE matches */ |
| 97837 | - char *zTName; /* text of name of TABLE */ | |
| 97918 | + char *zTName = 0; /* text of name of TABLE */ | |
| 97838 | 97919 | if( pE->op==TK_DOT ){ |
| 97839 | 97920 | assert( pE->pLeft!=0 ); |
| 97840 | 97921 | assert( !ExprHasProperty(pE->pLeft, EP_IntValue) ); |
| 97841 | 97922 | zTName = pE->pLeft->u.zToken; |
| 97842 | - }else{ | |
| 97843 | - zTName = 0; | |
| 97844 | 97923 | } |
| 97845 | 97924 | for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){ |
| 97846 | 97925 | Table *pTab = pFrom->pTab; |
| 97926 | + Select *pSub = pFrom->pSelect; | |
| 97847 | 97927 | char *zTabName = pFrom->zAlias; |
| 97928 | + const char *zSchemaName = 0; | |
| 97929 | + int iDb; | |
| 97848 | 97930 | if( zTabName==0 ){ |
| 97849 | 97931 | zTabName = pTab->zName; |
| 97850 | 97932 | } |
| 97851 | 97933 | if( db->mallocFailed ) break; |
| 97852 | - if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){ | |
| 97853 | - continue; | |
| 97934 | + if( pSub==0 || (pSub->selFlags & SF_NestedFrom)==0 ){ | |
| 97935 | + pSub = 0; | |
| 97936 | + if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){ | |
| 97937 | + continue; | |
| 97938 | + } | |
| 97939 | + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); | |
| 97940 | + zSchemaName = iDb>=0 ? db->aDb[iDb].zName : "*"; | |
| 97854 | 97941 | } |
| 97855 | - tableSeen = 1; | |
| 97856 | 97942 | for(j=0; j<pTab->nCol; j++){ |
| 97857 | - Expr *pExpr, *pRight; | |
| 97858 | 97943 | char *zName = pTab->aCol[j].zName; |
| 97859 | 97944 | char *zColname; /* The computed column name */ |
| 97860 | 97945 | char *zToFree; /* Malloced string that needs to be freed */ |
| 97861 | 97946 | Token sColname; /* Computed column name as a token */ |
| 97947 | + | |
| 97948 | + assert( zName ); | |
| 97949 | + if( zTName && pSub | |
| 97950 | + && sqlite3MatchSpanName(pSub->pEList->a[j].zSpan, 0, zTName, 0)==0 | |
| 97951 | + ){ | |
| 97952 | + continue; | |
| 97953 | + } | |
| 97862 | 97954 | |
| 97863 | 97955 | /* If a column is marked as 'hidden' (currently only possible |
| 97864 | 97956 | ** for virtual tables), do not include it in the expanded |
| 97865 | 97957 | ** result-set list. |
| 97866 | 97958 | */ |
| 97867 | 97959 | if( IsHiddenColumn(&pTab->aCol[j]) ){ |
| 97868 | 97960 | assert(IsVirtual(pTab)); |
| 97869 | 97961 | continue; |
| 97870 | 97962 | } |
| 97963 | + tableSeen = 1; | |
| 97871 | 97964 | |
| 97872 | 97965 | if( i>0 && zTName==0 ){ |
| 97873 | 97966 | if( (pFrom->jointype & JT_NATURAL)!=0 |
| 97874 | 97967 | && tableAndColumnIndex(pTabList, i, zName, 0, 0) |
| 97875 | 97968 | ){ |
| @@ -97888,10 +97981,14 @@ | ||
| 97888 | 97981 | zToFree = 0; |
| 97889 | 97982 | if( longNames || pTabList->nSrc>1 ){ |
| 97890 | 97983 | Expr *pLeft; |
| 97891 | 97984 | pLeft = sqlite3Expr(db, TK_ID, zTabName); |
| 97892 | 97985 | pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0); |
| 97986 | + if( zSchemaName ){ | |
| 97987 | + pLeft = sqlite3Expr(db, TK_ID, zSchemaName); | |
| 97988 | + pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pExpr, 0); | |
| 97989 | + } | |
| 97893 | 97990 | if( longNames ){ |
| 97894 | 97991 | zColname = sqlite3MPrintf(db, "%s.%s", zTabName, zName); |
| 97895 | 97992 | zToFree = zColname; |
| 97896 | 97993 | } |
| 97897 | 97994 | }else{ |
| @@ -97899,10 +97996,22 @@ | ||
| 97899 | 97996 | } |
| 97900 | 97997 | pNew = sqlite3ExprListAppend(pParse, pNew, pExpr); |
| 97901 | 97998 | sColname.z = zColname; |
| 97902 | 97999 | sColname.n = sqlite3Strlen30(zColname); |
| 97903 | 98000 | sqlite3ExprListSetName(pParse, pNew, &sColname, 0); |
| 98001 | + if( pNew && (p->selFlags & SF_NestedFrom)!=0 ){ | |
| 98002 | + struct ExprList_item *pX = &pNew->a[pNew->nExpr-1]; | |
| 98003 | + if( pSub ){ | |
| 98004 | + pX->zSpan = sqlite3DbStrDup(db, pSub->pEList->a[j].zSpan); | |
| 98005 | + testcase( pX->zSpan==0 ); | |
| 98006 | + }else{ | |
| 98007 | + pX->zSpan = sqlite3MPrintf(db, "%s.%s.%s", | |
| 98008 | + zSchemaName, zTabName, zColname); | |
| 98009 | + testcase( pX->zSpan==0 ); | |
| 98010 | + } | |
| 98011 | + pX->bSpanIsTab = 1; | |
| 98012 | + } | |
| 97904 | 98013 | sqlite3DbFree(db, zToFree); |
| 97905 | 98014 | } |
| 97906 | 98015 | } |
| 97907 | 98016 | if( !tableSeen ){ |
| 97908 | 98017 | if( zTName ){ |
| @@ -102699,12 +102808,12 @@ | ||
| 102699 | 102808 | Expr *pExpr; /* Pointer to the subexpression that is this term */ |
| 102700 | 102809 | int iParent; /* Disable pWC->a[iParent] when this term disabled */ |
| 102701 | 102810 | int leftCursor; /* Cursor number of X in "X <op> <expr>" */ |
| 102702 | 102811 | union { |
| 102703 | 102812 | int leftColumn; /* Column number of X in "X <op> <expr>" */ |
| 102704 | - WhereOrInfo *pOrInfo; /* Extra information if eOperator==WO_OR */ | |
| 102705 | - WhereAndInfo *pAndInfo; /* Extra information if eOperator==WO_AND */ | |
| 102813 | + WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */ | |
| 102814 | + WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */ | |
| 102706 | 102815 | } u; |
| 102707 | 102816 | u16 eOperator; /* A WO_xx value describing <op> */ |
| 102708 | 102817 | u8 wtFlags; /* TERM_xxx bit flags. See below */ |
| 102709 | 102818 | u8 nChild; /* Number of children that must disable us */ |
| 102710 | 102819 | WhereClause *pWC; /* The clause this term is part of */ |
| @@ -102828,10 +102937,11 @@ | ||
| 102828 | 102937 | #define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) |
| 102829 | 102938 | #define WO_MATCH 0x040 |
| 102830 | 102939 | #define WO_ISNULL 0x080 |
| 102831 | 102940 | #define WO_OR 0x100 /* Two or more OR-connected terms */ |
| 102832 | 102941 | #define WO_AND 0x200 /* Two or more AND-connected terms */ |
| 102942 | +#define WO_EQUIV 0x400 /* Of the form A==B, both columns */ | |
| 102833 | 102943 | #define WO_NOOP 0x800 /* This term does not restrict search space */ |
| 102834 | 102944 | |
| 102835 | 102945 | #define WO_ALL 0xfff /* Mask of all possible WO_* values */ |
| 102836 | 102946 | #define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */ |
| 102837 | 102947 | |
| @@ -103230,58 +103340,112 @@ | ||
| 103230 | 103340 | /* |
| 103231 | 103341 | ** Search for a term in the WHERE clause that is of the form "X <op> <expr>" |
| 103232 | 103342 | ** where X is a reference to the iColumn of table iCur and <op> is one of |
| 103233 | 103343 | ** the WO_xx operator codes specified by the op parameter. |
| 103234 | 103344 | ** Return a pointer to the term. Return 0 if not found. |
| 103345 | +** | |
| 103346 | +** The term returned might by Y=<expr> if there is another constraint in | |
| 103347 | +** the WHERE clause that specifies that X=Y. Any such constraints will be | |
| 103348 | +** identified by the WO_EQUIV bit in the pTerm->eOperator field. The | |
| 103349 | +** aEquiv[] array holds X and all its equivalents, with each SQL variable | |
| 103350 | +** taking up two slots in aEquiv[]. The first slot is for the cursor number | |
| 103351 | +** and the second is for the column number. There are 22 slots in aEquiv[] | |
| 103352 | +** so that means we can look for X plus up to 10 other equivalent values. | |
| 103353 | +** Hence a search for X will return <expr> if X=A1 and A1=A2 and A2=A3 | |
| 103354 | +** and ... and A9=A10 and A10=<expr>. | |
| 103355 | +** | |
| 103356 | +** If there are multiple terms in the WHERE clause of the form "X <op> <expr>" | |
| 103357 | +** then try for the one with no dependencies on <expr> - in other words where | |
| 103358 | +** <expr> is a constant expression of some kind. Only return entries of | |
| 103359 | +** the form "X <op> Y" where Y is a column in another table if no terms of | |
| 103360 | +** the form "X <op> <const-expr>" exist. Other than this priority, if there | |
| 103361 | +** are two or more terms that match, then the choice of which term to return | |
| 103362 | +** is arbitrary. | |
| 103235 | 103363 | */ |
| 103236 | 103364 | static WhereTerm *findTerm( |
| 103237 | 103365 | WhereClause *pWC, /* The WHERE clause to be searched */ |
| 103238 | 103366 | int iCur, /* Cursor number of LHS */ |
| 103239 | 103367 | int iColumn, /* Column number of LHS */ |
| 103240 | 103368 | Bitmask notReady, /* RHS must not overlap with this mask */ |
| 103241 | 103369 | u32 op, /* Mask of WO_xx values describing operator */ |
| 103242 | 103370 | Index *pIdx /* Must be compatible with this index, if not NULL */ |
| 103243 | 103371 | ){ |
| 103244 | - WhereTerm *pTerm; | |
| 103245 | - int k; | |
| 103372 | + WhereTerm *pTerm; /* Term being examined as possible result */ | |
| 103373 | + WhereTerm *pResult = 0; /* The answer to return */ | |
| 103374 | + WhereClause *pWCOrig = pWC; /* Original pWC value */ | |
| 103375 | + int j, k; /* Loop counters */ | |
| 103376 | + Expr *pX; /* Pointer to an expression */ | |
| 103377 | + Parse *pParse; /* Parsing context */ | |
| 103378 | + int iOrigCol = iColumn; /* Original value of iColumn */ | |
| 103379 | + int nEquiv = 2; /* Number of entires in aEquiv[] */ | |
| 103380 | + int iEquiv = 2; /* Number of entries of aEquiv[] processed so far */ | |
| 103381 | + int aEquiv[22]; /* iCur,iColumn and up to 10 other equivalents */ | |
| 103382 | + | |
| 103246 | 103383 | assert( iCur>=0 ); |
| 103247 | - op &= WO_ALL; | |
| 103248 | - for(; pWC; pWC=pWC->pOuter){ | |
| 103249 | - for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ | |
| 103250 | - if( pTerm->leftCursor==iCur | |
| 103251 | - && (pTerm->prereqRight & notReady)==0 | |
| 103252 | - && pTerm->u.leftColumn==iColumn | |
| 103253 | - && (pTerm->eOperator & op)!=0 | |
| 103254 | - ){ | |
| 103255 | - if( iColumn>=0 && pIdx && pTerm->eOperator!=WO_ISNULL ){ | |
| 103256 | - Expr *pX = pTerm->pExpr; | |
| 103257 | - CollSeq *pColl; | |
| 103258 | - char idxaff; | |
| 103259 | - int j; | |
| 103260 | - Parse *pParse = pWC->pParse; | |
| 103261 | - | |
| 103262 | - idxaff = pIdx->pTable->aCol[iColumn].affinity; | |
| 103263 | - if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue; | |
| 103264 | - | |
| 103265 | - /* Figure out the collation sequence required from an index for | |
| 103266 | - ** it to be useful for optimising expression pX. Store this | |
| 103267 | - ** value in variable pColl. | |
| 103268 | - */ | |
| 103269 | - assert(pX->pLeft); | |
| 103270 | - pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); | |
| 103271 | - if( pColl==0 ) pColl = pParse->db->pDfltColl; | |
| 103272 | - | |
| 103273 | - for(j=0; pIdx->aiColumn[j]!=iColumn; j++){ | |
| 103274 | - if( NEVER(j>=pIdx->nColumn) ) return 0; | |
| 103275 | - } | |
| 103276 | - if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue; | |
| 103277 | - } | |
| 103278 | - return pTerm; | |
| 103279 | - } | |
| 103280 | - } | |
| 103281 | - } | |
| 103282 | - return 0; | |
| 103384 | + aEquiv[0] = iCur; | |
| 103385 | + aEquiv[1] = iColumn; | |
| 103386 | + for(;;){ | |
| 103387 | + for(pWC=pWCOrig; pWC; pWC=pWC->pOuter){ | |
| 103388 | + for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ | |
| 103389 | + if( pTerm->leftCursor==iCur | |
| 103390 | + && pTerm->u.leftColumn==iColumn | |
| 103391 | + ){ | |
| 103392 | + if( (pTerm->prereqRight & notReady)==0 | |
| 103393 | + && (pTerm->eOperator & op & WO_ALL)!=0 | |
| 103394 | + ){ | |
| 103395 | + if( iOrigCol>=0 && pIdx && (pTerm->eOperator & WO_ISNULL)==0 ){ | |
| 103396 | + CollSeq *pColl; | |
| 103397 | + char idxaff; | |
| 103398 | + | |
| 103399 | + pX = pTerm->pExpr; | |
| 103400 | + pParse = pWC->pParse; | |
| 103401 | + idxaff = pIdx->pTable->aCol[iOrigCol].affinity; | |
| 103402 | + if( !sqlite3IndexAffinityOk(pX, idxaff) ){ | |
| 103403 | + continue; | |
| 103404 | + } | |
| 103405 | + | |
| 103406 | + /* Figure out the collation sequence required from an index for | |
| 103407 | + ** it to be useful for optimising expression pX. Store this | |
| 103408 | + ** value in variable pColl. | |
| 103409 | + */ | |
| 103410 | + assert(pX->pLeft); | |
| 103411 | + pColl = sqlite3BinaryCompareCollSeq(pParse,pX->pLeft,pX->pRight); | |
| 103412 | + if( pColl==0 ) pColl = pParse->db->pDfltColl; | |
| 103413 | + | |
| 103414 | + for(j=0; pIdx->aiColumn[j]!=iOrigCol; j++){ | |
| 103415 | + if( NEVER(j>=pIdx->nColumn) ) return 0; | |
| 103416 | + } | |
| 103417 | + if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ){ | |
| 103418 | + continue; | |
| 103419 | + } | |
| 103420 | + } | |
| 103421 | + pResult = pTerm; | |
| 103422 | + if( pTerm->prereqRight==0 ) goto findTerm_success; | |
| 103423 | + } | |
| 103424 | + if( (pTerm->eOperator & WO_EQUIV)!=0 | |
| 103425 | + && nEquiv<ArraySize(aEquiv) | |
| 103426 | + ){ | |
| 103427 | + pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight); | |
| 103428 | + assert( pX->op==TK_COLUMN ); | |
| 103429 | + for(j=0; j<nEquiv; j+=2){ | |
| 103430 | + if( aEquiv[j]==pX->iTable && aEquiv[j+1]==pX->iColumn ) break; | |
| 103431 | + } | |
| 103432 | + if( j==nEquiv ){ | |
| 103433 | + aEquiv[j] = pX->iTable; | |
| 103434 | + aEquiv[j+1] = pX->iColumn; | |
| 103435 | + nEquiv += 2; | |
| 103436 | + } | |
| 103437 | + } | |
| 103438 | + } | |
| 103439 | + } | |
| 103440 | + } | |
| 103441 | + if( iEquiv>=nEquiv ) break; | |
| 103442 | + iCur = aEquiv[iEquiv++]; | |
| 103443 | + iColumn = aEquiv[iEquiv++]; | |
| 103444 | + } | |
| 103445 | +findTerm_success: | |
| 103446 | + return pResult; | |
| 103283 | 103447 | } |
| 103284 | 103448 | |
| 103285 | 103449 | /* Forward reference */ |
| 103286 | 103450 | static void exprAnalyze(SrcList*, WhereClause*, int); |
| 103287 | 103451 | |
| @@ -103555,11 +103719,10 @@ | ||
| 103555 | 103719 | indexable = ~(Bitmask)0; |
| 103556 | 103720 | chngToIN = ~(pWC->vmask); |
| 103557 | 103721 | for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0 && indexable; i--, pOrTerm++){ |
| 103558 | 103722 | if( (pOrTerm->eOperator & WO_SINGLE)==0 ){ |
| 103559 | 103723 | WhereAndInfo *pAndInfo; |
| 103560 | - assert( pOrTerm->eOperator==0 ); | |
| 103561 | 103724 | assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 ); |
| 103562 | 103725 | chngToIN = 0; |
| 103563 | 103726 | pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo)); |
| 103564 | 103727 | if( pAndInfo ){ |
| 103565 | 103728 | WhereClause *pAndWC; |
| @@ -103594,11 +103757,11 @@ | ||
| 103594 | 103757 | if( pOrTerm->wtFlags & TERM_VIRTUAL ){ |
| 103595 | 103758 | WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent]; |
| 103596 | 103759 | b |= getMask(pMaskSet, pOther->leftCursor); |
| 103597 | 103760 | } |
| 103598 | 103761 | indexable &= b; |
| 103599 | - if( pOrTerm->eOperator!=WO_EQ ){ | |
| 103762 | + if( (pOrTerm->eOperator & WO_EQ)==0 ){ | |
| 103600 | 103763 | chngToIN = 0; |
| 103601 | 103764 | }else{ |
| 103602 | 103765 | chngToIN &= b; |
| 103603 | 103766 | } |
| 103604 | 103767 | } |
| @@ -103645,11 +103808,11 @@ | ||
| 103645 | 103808 | ** and column is found but leave okToChngToIN false if not found. |
| 103646 | 103809 | */ |
| 103647 | 103810 | for(j=0; j<2 && !okToChngToIN; j++){ |
| 103648 | 103811 | pOrTerm = pOrWc->a; |
| 103649 | 103812 | for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){ |
| 103650 | - assert( pOrTerm->eOperator==WO_EQ ); | |
| 103813 | + assert( pOrTerm->eOperator & WO_EQ ); | |
| 103651 | 103814 | pOrTerm->wtFlags &= ~TERM_OR_OK; |
| 103652 | 103815 | if( pOrTerm->leftCursor==iCursor ){ |
| 103653 | 103816 | /* This is the 2-bit case and we are on the second iteration and |
| 103654 | 103817 | ** current term is from the first iteration. So skip this term. */ |
| 103655 | 103818 | assert( j==1 ); |
| @@ -103671,21 +103834,21 @@ | ||
| 103671 | 103834 | } |
| 103672 | 103835 | if( i<0 ){ |
| 103673 | 103836 | /* No candidate table+column was found. This can only occur |
| 103674 | 103837 | ** on the second iteration */ |
| 103675 | 103838 | assert( j==1 ); |
| 103676 | - assert( (chngToIN&(chngToIN-1))==0 ); | |
| 103839 | + assert( IsPowerOfTwo(chngToIN) ); | |
| 103677 | 103840 | assert( chngToIN==getMask(pMaskSet, iCursor) ); |
| 103678 | 103841 | break; |
| 103679 | 103842 | } |
| 103680 | 103843 | testcase( j==1 ); |
| 103681 | 103844 | |
| 103682 | 103845 | /* We have found a candidate table and column. Check to see if that |
| 103683 | 103846 | ** table and column is common to every term in the OR clause */ |
| 103684 | 103847 | okToChngToIN = 1; |
| 103685 | 103848 | for(; i>=0 && okToChngToIN; i--, pOrTerm++){ |
| 103686 | - assert( pOrTerm->eOperator==WO_EQ ); | |
| 103849 | + assert( pOrTerm->eOperator & WO_EQ ); | |
| 103687 | 103850 | if( pOrTerm->leftCursor!=iCursor ){ |
| 103688 | 103851 | pOrTerm->wtFlags &= ~TERM_OR_OK; |
| 103689 | 103852 | }else if( pOrTerm->u.leftColumn!=iColumn ){ |
| 103690 | 103853 | okToChngToIN = 0; |
| 103691 | 103854 | }else{ |
| @@ -103717,11 +103880,11 @@ | ||
| 103717 | 103880 | Expr *pLeft = 0; /* The LHS of the IN operator */ |
| 103718 | 103881 | Expr *pNew; /* The complete IN operator */ |
| 103719 | 103882 | |
| 103720 | 103883 | for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){ |
| 103721 | 103884 | if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue; |
| 103722 | - assert( pOrTerm->eOperator==WO_EQ ); | |
| 103885 | + assert( pOrTerm->eOperator & WO_EQ ); | |
| 103723 | 103886 | assert( pOrTerm->leftCursor==iCursor ); |
| 103724 | 103887 | assert( pOrTerm->u.leftColumn==iColumn ); |
| 103725 | 103888 | pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); |
| 103726 | 103889 | pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup); |
| 103727 | 103890 | pLeft = pOrTerm->pExpr->pLeft; |
| @@ -103746,11 +103909,10 @@ | ||
| 103746 | 103909 | pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */ |
| 103747 | 103910 | } |
| 103748 | 103911 | } |
| 103749 | 103912 | } |
| 103750 | 103913 | #endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */ |
| 103751 | - | |
| 103752 | 103914 | |
| 103753 | 103915 | /* |
| 103754 | 103916 | ** The input to this routine is an WhereTerm structure with only the |
| 103755 | 103917 | ** "pExpr" field filled in. The job of this routine is to analyze the |
| 103756 | 103918 | ** subexpression and populate all the other fields of the WhereTerm |
| @@ -103816,21 +103978,23 @@ | ||
| 103816 | 103978 | } |
| 103817 | 103979 | pTerm->prereqAll = prereqAll; |
| 103818 | 103980 | pTerm->leftCursor = -1; |
| 103819 | 103981 | pTerm->iParent = -1; |
| 103820 | 103982 | pTerm->eOperator = 0; |
| 103821 | - if( allowedOp(op) && (pTerm->prereqRight & prereqLeft)==0 ){ | |
| 103983 | + if( allowedOp(op) ){ | |
| 103822 | 103984 | Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft); |
| 103823 | 103985 | Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight); |
| 103986 | + u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV; | |
| 103824 | 103987 | if( pLeft->op==TK_COLUMN ){ |
| 103825 | 103988 | pTerm->leftCursor = pLeft->iTable; |
| 103826 | 103989 | pTerm->u.leftColumn = pLeft->iColumn; |
| 103827 | - pTerm->eOperator = operatorMask(op); | |
| 103990 | + pTerm->eOperator = operatorMask(op) & opMask; | |
| 103828 | 103991 | } |
| 103829 | 103992 | if( pRight && pRight->op==TK_COLUMN ){ |
| 103830 | 103993 | WhereTerm *pNew; |
| 103831 | 103994 | Expr *pDup; |
| 103995 | + u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */ | |
| 103832 | 103996 | if( pTerm->leftCursor>=0 ){ |
| 103833 | 103997 | int idxNew; |
| 103834 | 103998 | pDup = sqlite3ExprDup(db, pExpr, 0); |
| 103835 | 103999 | if( db->mallocFailed ){ |
| 103836 | 104000 | sqlite3ExprDelete(db, pDup); |
| @@ -103841,10 +104005,17 @@ | ||
| 103841 | 104005 | pNew = &pWC->a[idxNew]; |
| 103842 | 104006 | pNew->iParent = idxTerm; |
| 103843 | 104007 | pTerm = &pWC->a[idxTerm]; |
| 103844 | 104008 | pTerm->nChild = 1; |
| 103845 | 104009 | pTerm->wtFlags |= TERM_COPIED; |
| 104010 | + if( pExpr->op==TK_EQ | |
| 104011 | + && !ExprHasProperty(pExpr, EP_FromJoin) | |
| 104012 | + && OptimizationEnabled(db, SQLITE_Transitive) | |
| 104013 | + ){ | |
| 104014 | + pTerm->eOperator |= WO_EQUIV; | |
| 104015 | + eExtraOp = WO_EQUIV; | |
| 104016 | + } | |
| 103846 | 104017 | }else{ |
| 103847 | 104018 | pDup = pExpr; |
| 103848 | 104019 | pNew = pTerm; |
| 103849 | 104020 | } |
| 103850 | 104021 | exprCommute(pParse, pDup); |
| @@ -103852,11 +104023,11 @@ | ||
| 103852 | 104023 | pNew->leftCursor = pLeft->iTable; |
| 103853 | 104024 | pNew->u.leftColumn = pLeft->iColumn; |
| 103854 | 104025 | testcase( (prereqLeft | extraRight) != prereqLeft ); |
| 103855 | 104026 | pNew->prereqRight = prereqLeft | extraRight; |
| 103856 | 104027 | pNew->prereqAll = prereqAll; |
| 103857 | - pNew->eOperator = operatorMask(pDup->op); | |
| 104028 | + pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask; | |
| 103858 | 104029 | } |
| 103859 | 104030 | } |
| 103860 | 104031 | |
| 103861 | 104032 | #ifndef SQLITE_OMIT_BETWEEN_OPTIMIZATION |
| 103862 | 104033 | /* If a term is the BETWEEN operator, create two new virtual terms |
| @@ -104311,11 +104482,11 @@ | ||
| 104311 | 104482 | return; |
| 104312 | 104483 | } |
| 104313 | 104484 | |
| 104314 | 104485 | /* Search the WHERE clause terms for a usable WO_OR term. */ |
| 104315 | 104486 | for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ |
| 104316 | - if( pTerm->eOperator==WO_OR | |
| 104487 | + if( (pTerm->eOperator & WO_OR)!=0 | |
| 104317 | 104488 | && ((pTerm->prereqAll & ~maskSrc) & p->notReady)==0 |
| 104318 | 104489 | && (pTerm->u.pOrInfo->indexable & maskSrc)!=0 |
| 104319 | 104490 | ){ |
| 104320 | 104491 | WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc; |
| 104321 | 104492 | WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm]; |
| @@ -104332,11 +104503,11 @@ | ||
| 104332 | 104503 | sBOI.ppIdxInfo = 0; |
| 104333 | 104504 | for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){ |
| 104334 | 104505 | WHERETRACE(("... Multi-index OR testing for term %d of %d....\n", |
| 104335 | 104506 | (pOrTerm - pOrWC->a), (pTerm - pWC->a) |
| 104336 | 104507 | )); |
| 104337 | - if( pOrTerm->eOperator==WO_AND ){ | |
| 104508 | + if( (pOrTerm->eOperator& WO_AND)!=0 ){ | |
| 104338 | 104509 | sBOI.pWC = &pOrTerm->u.pAndInfo->wc; |
| 104339 | 104510 | bestIndex(&sBOI); |
| 104340 | 104511 | }else if( pOrTerm->leftCursor==iCur ){ |
| 104341 | 104512 | WhereClause tempWC; |
| 104342 | 104513 | tempWC.pParse = pWC->pParse; |
| @@ -104393,11 +104564,11 @@ | ||
| 104393 | 104564 | struct SrcList_item *pSrc, /* Table we are trying to access */ |
| 104394 | 104565 | Bitmask notReady /* Tables in outer loops of the join */ |
| 104395 | 104566 | ){ |
| 104396 | 104567 | char aff; |
| 104397 | 104568 | if( pTerm->leftCursor!=pSrc->iCursor ) return 0; |
| 104398 | - if( pTerm->eOperator!=WO_EQ ) return 0; | |
| 104569 | + if( (pTerm->eOperator & WO_EQ)==0 ) return 0; | |
| 104399 | 104570 | if( (pTerm->prereqRight & notReady)!=0 ) return 0; |
| 104400 | 104571 | aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; |
| 104401 | 104572 | if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; |
| 104402 | 104573 | return 1; |
| 104403 | 104574 | } |
| @@ -104655,13 +104826,13 @@ | ||
| 104655 | 104826 | |
| 104656 | 104827 | /* Count the number of possible WHERE clause constraints referring |
| 104657 | 104828 | ** to this virtual table */ |
| 104658 | 104829 | for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 104659 | 104830 | if( pTerm->leftCursor != pSrc->iCursor ) continue; |
| 104660 | - assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 ); | |
| 104661 | - testcase( pTerm->eOperator==WO_IN ); | |
| 104662 | - testcase( pTerm->eOperator==WO_ISNULL ); | |
| 104831 | + assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); | |
| 104832 | + testcase( pTerm->eOperator & WO_IN ); | |
| 104833 | + testcase( pTerm->eOperator & WO_ISNULL ); | |
| 104663 | 104834 | if( pTerm->eOperator & (WO_ISNULL) ) continue; |
| 104664 | 104835 | if( pTerm->wtFlags & TERM_VNULL ) continue; |
| 104665 | 104836 | nTerm++; |
| 104666 | 104837 | } |
| 104667 | 104838 | |
| @@ -104708,18 +104879,18 @@ | ||
| 104708 | 104879 | pUsage; |
| 104709 | 104880 | |
| 104710 | 104881 | for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 104711 | 104882 | u8 op; |
| 104712 | 104883 | if( pTerm->leftCursor != pSrc->iCursor ) continue; |
| 104713 | - assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 ); | |
| 104714 | - testcase( pTerm->eOperator==WO_IN ); | |
| 104715 | - testcase( pTerm->eOperator==WO_ISNULL ); | |
| 104884 | + assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); | |
| 104885 | + testcase( pTerm->eOperator & WO_IN ); | |
| 104886 | + testcase( pTerm->eOperator & WO_ISNULL ); | |
| 104716 | 104887 | if( pTerm->eOperator & (WO_ISNULL) ) continue; |
| 104717 | 104888 | if( pTerm->wtFlags & TERM_VNULL ) continue; |
| 104718 | 104889 | pIdxCons[j].iColumn = pTerm->u.leftColumn; |
| 104719 | 104890 | pIdxCons[j].iTermOffset = i; |
| 104720 | - op = (u8)pTerm->eOperator; | |
| 104891 | + op = (u8)pTerm->eOperator & WO_ALL; | |
| 104721 | 104892 | if( op==WO_IN ) op = WO_EQ; |
| 104722 | 104893 | pIdxCons[j].op = op; |
| 104723 | 104894 | /* The direct assignment in the previous line is possible only because |
| 104724 | 104895 | ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The |
| 104725 | 104896 | ** following asserts verify this fact. */ |
| @@ -104885,11 +105056,11 @@ | ||
| 104885 | 105056 | pUsage = pIdxInfo->aConstraintUsage; |
| 104886 | 105057 | for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ |
| 104887 | 105058 | j = pIdxCons->iTermOffset; |
| 104888 | 105059 | pTerm = &pWC->a[j]; |
| 104889 | 105060 | if( (pTerm->prereqRight&p->notReady)==0 |
| 104890 | - && (bAllowIN || pTerm->eOperator!=WO_IN) | |
| 105061 | + && (bAllowIN || (pTerm->eOperator & WO_IN)==0) | |
| 104891 | 105062 | ){ |
| 104892 | 105063 | pIdxCons->usable = 1; |
| 104893 | 105064 | }else{ |
| 104894 | 105065 | pIdxCons->usable = 0; |
| 104895 | 105066 | } |
| @@ -104917,11 +105088,11 @@ | ||
| 104917 | 105088 | for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ |
| 104918 | 105089 | if( pUsage[i].argvIndex>0 ){ |
| 104919 | 105090 | j = pIdxCons->iTermOffset; |
| 104920 | 105091 | pTerm = &pWC->a[j]; |
| 104921 | 105092 | p->cost.used |= pTerm->prereqRight; |
| 104922 | - if( pTerm->eOperator==WO_IN && pUsage[i].omit==0 ){ | |
| 105093 | + if( (pTerm->eOperator & WO_IN)!=0 && pUsage[i].omit==0 ){ | |
| 104923 | 105094 | /* Do not attempt to use an IN constraint if the virtual table |
| 104924 | 105095 | ** says that the equivalent EQ constraint cannot be safely omitted. |
| 104925 | 105096 | ** If we do attempt to use such a constraint, some rows might be |
| 104926 | 105097 | ** repeated in the output. */ |
| 104927 | 105098 | break; |
| @@ -105223,28 +105394,28 @@ | ||
| 105223 | 105394 | u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; |
| 105224 | 105395 | |
| 105225 | 105396 | if( pLower ){ |
| 105226 | 105397 | Expr *pExpr = pLower->pExpr->pRight; |
| 105227 | 105398 | rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); |
| 105228 | - assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE ); | |
| 105399 | + assert( (pLower->eOperator & (WO_GT|WO_GE))!=0 ); | |
| 105229 | 105400 | if( rc==SQLITE_OK |
| 105230 | 105401 | && whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK |
| 105231 | 105402 | ){ |
| 105232 | 105403 | iLower = a[0]; |
| 105233 | - if( pLower->eOperator==WO_GT ) iLower += a[1]; | |
| 105404 | + if( (pLower->eOperator & WO_GT)!=0 ) iLower += a[1]; | |
| 105234 | 105405 | } |
| 105235 | 105406 | sqlite3ValueFree(pRangeVal); |
| 105236 | 105407 | } |
| 105237 | 105408 | if( rc==SQLITE_OK && pUpper ){ |
| 105238 | 105409 | Expr *pExpr = pUpper->pExpr->pRight; |
| 105239 | 105410 | rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); |
| 105240 | - assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE ); | |
| 105411 | + assert( (pUpper->eOperator & (WO_LT|WO_LE))!=0 ); | |
| 105241 | 105412 | if( rc==SQLITE_OK |
| 105242 | 105413 | && whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK |
| 105243 | 105414 | ){ |
| 105244 | 105415 | iUpper = a[0]; |
| 105245 | - if( pUpper->eOperator==WO_LE ) iUpper += a[1]; | |
| 105416 | + if( (pUpper->eOperator & WO_LE)!=0 ) iUpper += a[1]; | |
| 105246 | 105417 | } |
| 105247 | 105418 | sqlite3ValueFree(pRangeVal); |
| 105248 | 105419 | } |
| 105249 | 105420 | if( rc==SQLITE_OK ){ |
| 105250 | 105421 | if( iUpper<=iLower ){ |
| @@ -105548,16 +105719,16 @@ | ||
| 105548 | 105719 | ** if there are any X= or X IS NULL constraints in the WHERE clause. */ |
| 105549 | 105720 | pConstraint = findTerm(p->pWC, base, iColumn, p->notReady, |
| 105550 | 105721 | WO_EQ|WO_ISNULL|WO_IN, pIdx); |
| 105551 | 105722 | if( pConstraint==0 ){ |
| 105552 | 105723 | isEq = 0; |
| 105553 | - }else if( pConstraint->eOperator==WO_IN ){ | |
| 105724 | + }else if( (pConstraint->eOperator & WO_IN)!=0 ){ | |
| 105554 | 105725 | /* Constraints of the form: "X IN ..." cannot be used with an ORDER BY |
| 105555 | 105726 | ** because we do not know in what order the values on the RHS of the IN |
| 105556 | 105727 | ** operator will occur. */ |
| 105557 | 105728 | break; |
| 105558 | - }else if( pConstraint->eOperator==WO_ISNULL ){ | |
| 105729 | + }else if( (pConstraint->eOperator & WO_ISNULL)!=0 ){ | |
| 105559 | 105730 | uniqueNotNull = 0; |
| 105560 | 105731 | isEq = 1; /* "X IS NULL" means X has only a single value */ |
| 105561 | 105732 | }else if( pConstraint->prereqRight==0 ){ |
| 105562 | 105733 | isEq = 1; /* Constraint "X=constant" means X has only a single value */ |
| 105563 | 105734 | }else{ |
| @@ -105903,11 +106074,11 @@ | ||
| 105903 | 106074 | int bRev = 2; |
| 105904 | 106075 | WHERETRACE((" --> before isSortingIndex: nPriorSat=%d\n",nPriorSat)); |
| 105905 | 106076 | pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev); |
| 105906 | 106077 | WHERETRACE((" --> after isSortingIndex: bRev=%d nOBSat=%d\n", |
| 105907 | 106078 | bRev, pc.plan.nOBSat)); |
| 105908 | - if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_UNIQUE)!=0 ){ | |
| 106079 | + if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){ | |
| 105909 | 106080 | pc.plan.wsFlags |= WHERE_ORDERED; |
| 105910 | 106081 | } |
| 105911 | 106082 | if( nOrderBy==pc.plan.nOBSat ){ |
| 105912 | 106083 | bSort = 0; |
| 105913 | 106084 | pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE; |
| @@ -105966,16 +106137,17 @@ | ||
| 105966 | 106137 | */ |
| 105967 | 106138 | if( pc.plan.nRow>(double)1 && pc.plan.nEq==1 |
| 105968 | 106139 | && pFirstTerm!=0 && aiRowEst[1]>1 ){ |
| 105969 | 106140 | assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 ); |
| 105970 | 106141 | if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){ |
| 105971 | - testcase( pFirstTerm->eOperator==WO_EQ ); | |
| 105972 | - testcase( pFirstTerm->eOperator==WO_ISNULL ); | |
| 106142 | + testcase( pFirstTerm->eOperator & WO_EQ ); | |
| 106143 | + testcase( pFirstTerm->eOperator & WO_EQUIV ); | |
| 106144 | + testcase( pFirstTerm->eOperator & WO_ISNULL ); | |
| 105973 | 106145 | whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, |
| 105974 | 106146 | &pc.plan.nRow); |
| 105975 | 106147 | }else if( bInEst==0 ){ |
| 105976 | - assert( pFirstTerm->eOperator==WO_IN ); | |
| 106148 | + assert( pFirstTerm->eOperator & WO_IN ); | |
| 105977 | 106149 | whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, |
| 105978 | 106150 | &pc.plan.nRow); |
| 105979 | 106151 | } |
| 105980 | 106152 | } |
| 105981 | 106153 | #endif /* SQLITE_ENABLE_STAT3 */ |
| @@ -106118,11 +106290,11 @@ | ||
| 106118 | 106290 | ** more selective intentionally because of the subjective |
| 106119 | 106291 | ** observation that indexed range constraints really are more |
| 106120 | 106292 | ** selective in practice, on average. */ |
| 106121 | 106293 | pc.plan.nRow /= 3; |
| 106122 | 106294 | } |
| 106123 | - }else if( pTerm->eOperator!=WO_NOOP ){ | |
| 106295 | + }else if( (pTerm->eOperator & WO_NOOP)==0 ){ | |
| 106124 | 106296 | /* Any other expression lowers the output row count by half */ |
| 106125 | 106297 | pc.plan.nRow /= 2; |
| 106126 | 106298 | } |
| 106127 | 106299 | } |
| 106128 | 106300 | if( pc.plan.nRow<2 ) pc.plan.nRow = 2; |
| @@ -106170,12 +106342,13 @@ | ||
| 106170 | 106342 | assert( pSrc->pIndex==0 |
| 106171 | 106343 | || p->cost.plan.u.pIdx==0 |
| 106172 | 106344 | || p->cost.plan.u.pIdx==pSrc->pIndex |
| 106173 | 106345 | ); |
| 106174 | 106346 | |
| 106175 | - WHERETRACE((" best index is: %s\n", | |
| 106176 | - p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk")); | |
| 106347 | + WHERETRACE((" best index is %s cost=%.1f\n", | |
| 106348 | + p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk", | |
| 106349 | + p->cost.rCost)); | |
| 106177 | 106350 | |
| 106178 | 106351 | bestOrClauseIndex(p); |
| 106179 | 106352 | bestAutomaticIndex(p); |
| 106180 | 106353 | p->cost.plan.wsFlags |= eqTermMask; |
| 106181 | 106354 | } |
| @@ -106753,11 +106926,10 @@ | ||
| 106753 | 106926 | */ |
| 106754 | 106927 | iReleaseReg = sqlite3GetTempReg(pParse); |
| 106755 | 106928 | pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0); |
| 106756 | 106929 | assert( pTerm!=0 ); |
| 106757 | 106930 | assert( pTerm->pExpr!=0 ); |
| 106758 | - assert( pTerm->leftCursor==iCur ); | |
| 106759 | 106931 | assert( omitTable==0 ); |
| 106760 | 106932 | testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ |
| 106761 | 106933 | iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, iReleaseReg); |
| 106762 | 106934 | addrNxt = pLevel->addrNxt; |
| 106763 | 106935 | sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); |
| @@ -107144,11 +107316,11 @@ | ||
| 107144 | 107316 | int ii; /* Loop counter */ |
| 107145 | 107317 | Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ |
| 107146 | 107318 | |
| 107147 | 107319 | pTerm = pLevel->plan.u.pTerm; |
| 107148 | 107320 | assert( pTerm!=0 ); |
| 107149 | - assert( pTerm->eOperator==WO_OR ); | |
| 107321 | + assert( pTerm->eOperator & WO_OR ); | |
| 107150 | 107322 | assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); |
| 107151 | 107323 | pOrWc = &pTerm->u.pOrInfo->wc; |
| 107152 | 107324 | pLevel->op = OP_Return; |
| 107153 | 107325 | pLevel->p1 = regReturn; |
| 107154 | 107326 | |
| @@ -107217,11 +107389,11 @@ | ||
| 107217 | 107389 | } |
| 107218 | 107390 | } |
| 107219 | 107391 | |
| 107220 | 107392 | for(ii=0; ii<pOrWc->nTerm; ii++){ |
| 107221 | 107393 | WhereTerm *pOrTerm = &pOrWc->a[ii]; |
| 107222 | - if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){ | |
| 107394 | + if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){ | |
| 107223 | 107395 | WhereInfo *pSubWInfo; /* Info for single OR-term scan */ |
| 107224 | 107396 | Expr *pOrExpr = pOrTerm->pExpr; |
| 107225 | 107397 | if( pAndExpr ){ |
| 107226 | 107398 | pAndExpr->pLeft = pOrExpr; |
| 107227 | 107399 | pOrExpr = pAndExpr; |
| @@ -107672,10 +107844,11 @@ | ||
| 107672 | 107844 | Index *pIdx; /* Index for FROM table at pTabItem */ |
| 107673 | 107845 | int j; /* For looping over FROM tables */ |
| 107674 | 107846 | int bestJ = -1; /* The value of j */ |
| 107675 | 107847 | Bitmask m; /* Bitmask value for j or bestJ */ |
| 107676 | 107848 | int isOptimal; /* Iterator for optimal/non-optimal search */ |
| 107849 | + int ckOptimal; /* Do the optimal scan check */ | |
| 107677 | 107850 | int nUnconstrained; /* Number tables without INDEXED BY */ |
| 107678 | 107851 | Bitmask notIndexed; /* Mask of tables that cannot use an index */ |
| 107679 | 107852 | |
| 107680 | 107853 | memset(&bestPlan, 0, sizeof(bestPlan)); |
| 107681 | 107854 | bestPlan.rCost = SQLITE_BIG_DBL; |
| @@ -107706,14 +107879,12 @@ | ||
| 107706 | 107879 | ** |
| 107707 | 107880 | ** The second loop iteration is only performed if no optimal scan |
| 107708 | 107881 | ** strategies were found by the first iteration. This second iteration |
| 107709 | 107882 | ** is used to search for the lowest cost scan overall. |
| 107710 | 107883 | ** |
| 107711 | - ** Previous versions of SQLite performed only the second iteration - | |
| 107712 | - ** the next outermost loop was always that with the lowest overall | |
| 107713 | - ** cost. However, this meant that SQLite could select the wrong plan | |
| 107714 | - ** for scripts such as the following: | |
| 107884 | + ** Without the optimal scan step (the first iteration) a suboptimal | |
| 107885 | + ** plan might be chosen for queries like this: | |
| 107715 | 107886 | ** |
| 107716 | 107887 | ** CREATE TABLE t1(a, b); |
| 107717 | 107888 | ** CREATE TABLE t2(c, d); |
| 107718 | 107889 | ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a; |
| 107719 | 107890 | ** |
| @@ -107724,20 +107895,44 @@ | ||
| 107724 | 107895 | ** algorithm may choose to use t2 for the outer loop, which is a much |
| 107725 | 107896 | ** costlier approach. |
| 107726 | 107897 | */ |
| 107727 | 107898 | nUnconstrained = 0; |
| 107728 | 107899 | notIndexed = 0; |
| 107729 | - for(isOptimal=(iFrom<nTabList-1); isOptimal>=0 && bestJ<0; isOptimal--){ | |
| 107900 | + | |
| 107901 | + /* The optimal scan check only occurs if there are two or more tables | |
| 107902 | + ** available to be reordered */ | |
| 107903 | + if( iFrom==nTabList-1 ){ | |
| 107904 | + ckOptimal = 0; /* Common case of just one table in the FROM clause */ | |
| 107905 | + }else{ | |
| 107906 | + ckOptimal = -1; | |
| 107730 | 107907 | for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){ |
| 107731 | - int doNotReorder; /* True if this table should not be reordered */ | |
| 107732 | - | |
| 107733 | - doNotReorder = (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0; | |
| 107734 | - if( j!=iFrom && doNotReorder ) break; | |
| 107735 | 107908 | m = getMask(pMaskSet, sWBI.pSrc->iCursor); |
| 107736 | 107909 | if( (m & sWBI.notValid)==0 ){ |
| 107737 | 107910 | if( j==iFrom ) iFrom++; |
| 107738 | 107911 | continue; |
| 107912 | + } | |
| 107913 | + if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ) break; | |
| 107914 | + if( ++ckOptimal ) break; | |
| 107915 | + if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break; | |
| 107916 | + } | |
| 107917 | + } | |
| 107918 | + assert( ckOptimal==0 || ckOptimal==1 ); | |
| 107919 | + | |
| 107920 | + for(isOptimal=ckOptimal; isOptimal>=0 && bestJ<0; isOptimal--){ | |
| 107921 | + for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){ | |
| 107922 | + if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ){ | |
| 107923 | + /* This break and one like it in the ckOptimal computation loop | |
| 107924 | + ** above prevent table reordering across LEFT and CROSS JOINs. | |
| 107925 | + ** The LEFT JOIN case is necessary for correctness. The prohibition | |
| 107926 | + ** against reordering across a CROSS JOIN is an SQLite feature that | |
| 107927 | + ** allows the developer to control table reordering */ | |
| 107928 | + break; | |
| 107929 | + } | |
| 107930 | + m = getMask(pMaskSet, sWBI.pSrc->iCursor); | |
| 107931 | + if( (m & sWBI.notValid)==0 ){ | |
| 107932 | + assert( j>iFrom ); | |
| 107933 | + continue; | |
| 107739 | 107934 | } |
| 107740 | 107935 | sWBI.notReady = (isOptimal ? m : sWBI.notValid); |
| 107741 | 107936 | if( sWBI.pSrc->pIndex==0 ) nUnconstrained++; |
| 107742 | 107937 | |
| 107743 | 107938 | WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n", |
| @@ -107763,12 +107958,12 @@ | ||
| 107763 | 107958 | if( isOptimal && (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){ |
| 107764 | 107959 | notIndexed |= m; |
| 107765 | 107960 | } |
| 107766 | 107961 | if( isOptimal ){ |
| 107767 | 107962 | pWInfo->a[j].rOptCost = sWBI.cost.rCost; |
| 107768 | - }else if( iFrom<nTabList-1 ){ | |
| 107769 | - /* If two or more tables have nearly the same outer loop cost, | |
| 107963 | + }else if( ckOptimal ){ | |
| 107964 | + /* If two or more tables have nearly the same outer loop cost, but | |
| 107770 | 107965 | ** very different inner loop (optimal) cost, we want to choose |
| 107771 | 107966 | ** for the outer loop that table which benefits the least from |
| 107772 | 107967 | ** being in the inner loop. The following code scales the |
| 107773 | 107968 | ** outer loop cost estimate to accomplish that. */ |
| 107774 | 107969 | WHERETRACE((" scaling cost from %.1f to %.1f\n", |
| @@ -107809,15 +108004,23 @@ | ||
| 107809 | 108004 | sWBI.cost.rCost, sWBI.cost.plan.nRow, |
| 107810 | 108005 | sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags)); |
| 107811 | 108006 | bestPlan = sWBI.cost; |
| 107812 | 108007 | bestJ = j; |
| 107813 | 108008 | } |
| 107814 | - if( doNotReorder ) break; | |
| 108009 | + | |
| 108010 | + /* In a join like "w JOIN x LEFT JOIN y JOIN z" make sure that | |
| 108011 | + ** table y (and not table z) is always the next inner loop inside | |
| 108012 | + ** of table x. */ | |
| 108013 | + if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break; | |
| 107815 | 108014 | } |
| 107816 | 108015 | } |
| 107817 | 108016 | assert( bestJ>=0 ); |
| 107818 | 108017 | assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); |
| 108018 | + assert( bestJ==iFrom || (pTabList->a[iFrom].jointype & JT_LEFT)==0 ); | |
| 108019 | + testcase( bestJ>iFrom && (pTabList->a[iFrom].jointype & JT_CROSS)!=0 ); | |
| 108020 | + testcase( bestJ>iFrom && bestJ<nTabList-1 | |
| 108021 | + && (pTabList->a[bestJ+1].jointype & JT_LEFT)!=0 ); | |
| 107819 | 108022 | WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n" |
| 107820 | 108023 | " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n", |
| 107821 | 108024 | bestJ, pTabList->a[bestJ].pTab->zName, |
| 107822 | 108025 | pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow, |
| 107823 | 108026 | bestPlan.plan.nOBSat, bestPlan.plan.wsFlags)); |
| @@ -136543,11 +136746,12 @@ | ||
| 136543 | 136746 | ** would fit in a single node, use a smaller node-size. |
| 136544 | 136747 | */ |
| 136545 | 136748 | static int getNodeSize( |
| 136546 | 136749 | sqlite3 *db, /* Database handle */ |
| 136547 | 136750 | Rtree *pRtree, /* Rtree handle */ |
| 136548 | - int isCreate /* True for xCreate, false for xConnect */ | |
| 136751 | + int isCreate, /* True for xCreate, false for xConnect */ | |
| 136752 | + char **pzErr /* OUT: Error message, if any */ | |
| 136549 | 136753 | ){ |
| 136550 | 136754 | int rc; |
| 136551 | 136755 | char *zSql; |
| 136552 | 136756 | if( isCreate ){ |
| 136553 | 136757 | int iPageSize = 0; |
| @@ -136556,17 +136760,22 @@ | ||
| 136556 | 136760 | if( rc==SQLITE_OK ){ |
| 136557 | 136761 | pRtree->iNodeSize = iPageSize-64; |
| 136558 | 136762 | if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){ |
| 136559 | 136763 | pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS; |
| 136560 | 136764 | } |
| 136765 | + }else{ | |
| 136766 | + *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); | |
| 136561 | 136767 | } |
| 136562 | 136768 | }else{ |
| 136563 | 136769 | zSql = sqlite3_mprintf( |
| 136564 | 136770 | "SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1", |
| 136565 | 136771 | pRtree->zDb, pRtree->zName |
| 136566 | 136772 | ); |
| 136567 | 136773 | rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize); |
| 136774 | + if( rc!=SQLITE_OK ){ | |
| 136775 | + *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); | |
| 136776 | + } | |
| 136568 | 136777 | } |
| 136569 | 136778 | |
| 136570 | 136779 | sqlite3_free(zSql); |
| 136571 | 136780 | return rc; |
| 136572 | 136781 | } |
| @@ -136626,11 +136835,11 @@ | ||
| 136626 | 136835 | pRtree->eCoordType = eCoordType; |
| 136627 | 136836 | memcpy(pRtree->zDb, argv[1], nDb); |
| 136628 | 136837 | memcpy(pRtree->zName, argv[2], nName); |
| 136629 | 136838 | |
| 136630 | 136839 | /* Figure out the node size to use. */ |
| 136631 | - rc = getNodeSize(db, pRtree, isCreate); | |
| 136840 | + rc = getNodeSize(db, pRtree, isCreate, pzErr); | |
| 136632 | 136841 | |
| 136633 | 136842 | /* Create/Connect to the underlying relational database schema. If |
| 136634 | 136843 | ** that is successful, call sqlite3_declare_vtab() to configure |
| 136635 | 136844 | ** the r-tree table schema. |
| 136636 | 136845 | */ |
| 136637 | 136846 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -673,11 +673,11 @@ | |
| 673 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 674 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 675 | */ |
| 676 | #define SQLITE_VERSION "3.7.16" |
| 677 | #define SQLITE_VERSION_NUMBER 3007016 |
| 678 | #define SQLITE_SOURCE_ID "2012-12-21 16:15:35 ff6857b6ed6a46671006b75157d8cf853a816ef9" |
| 679 | |
| 680 | /* |
| 681 | ** CAPI3REF: Run-Time Library Version Numbers |
| 682 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 683 | ** |
| @@ -8238,10 +8238,15 @@ | |
| 8238 | ** A convenience macro that returns the number of elements in |
| 8239 | ** an array. |
| 8240 | */ |
| 8241 | #define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0]))) |
| 8242 | |
| 8243 | /* |
| 8244 | ** The following value as a destructor means to use sqlite3DbFree(). |
| 8245 | ** The sqlite3DbFree() routine requires two parameters instead of the |
| 8246 | ** one parameter that destructors normally want. So we have to introduce |
| 8247 | ** this magic value that the code knows to handle differently. Any |
| @@ -10042,10 +10047,11 @@ | |
| 10042 | #define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */ |
| 10043 | #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ |
| 10044 | #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ |
| 10045 | #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ |
| 10046 | #define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */ |
| 10047 | #define SQLITE_AllOpts 0xffff /* All optimizations */ |
| 10048 | |
| 10049 | /* |
| 10050 | ** Macros for testing whether or not optimizations are enabled or disabled. |
| 10051 | */ |
| @@ -10553,24 +10559,24 @@ | |
| 10553 | ** and the value of Index.onError indicate the which conflict resolution |
| 10554 | ** algorithm to employ whenever an attempt is made to insert a non-unique |
| 10555 | ** element. |
| 10556 | */ |
| 10557 | struct Index { |
| 10558 | char *zName; /* Name of this index */ |
| 10559 | int *aiColumn; /* Which columns are used by this index. 1st is 0 */ |
| 10560 | tRowcnt *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */ |
| 10561 | Table *pTable; /* The SQL table being indexed */ |
| 10562 | char *zColAff; /* String defining the affinity of each column */ |
| 10563 | Index *pNext; /* The next index associated with the same table */ |
| 10564 | Schema *pSchema; /* Schema containing this index */ |
| 10565 | u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */ |
| 10566 | char **azColl; /* Array of collation sequence names for index */ |
| 10567 | int nColumn; /* Number of columns in the table used by this index */ |
| 10568 | int tnum; /* Page containing root of this index in database file */ |
| 10569 | u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ |
| 10570 | u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ |
| 10571 | u8 bUnordered; /* Use this index for == or IN queries only */ |
| 10572 | #ifdef SQLITE_ENABLE_STAT3 |
| 10573 | int nSample; /* Number of elements in aSample[] */ |
| 10574 | tRowcnt avgEq; /* Average nEq value for key values not in aSample */ |
| 10575 | IndexSample *aSample; /* Samples of the left-most key */ |
| 10576 | #endif |
| @@ -10840,22 +10846,31 @@ | |
| 10840 | ** name. An expr/name combination can be used in several ways, such |
| 10841 | ** as the list of "expr AS ID" fields following a "SELECT" or in the |
| 10842 | ** list of "ID = expr" items in an UPDATE. A list of expressions can |
| 10843 | ** also be used as the argument to a function, in which case the a.zName |
| 10844 | ** field is not used. |
| 10845 | */ |
| 10846 | struct ExprList { |
| 10847 | int nExpr; /* Number of expressions on the list */ |
| 10848 | int iECursor; /* VDBE Cursor associated with this ExprList */ |
| 10849 | struct ExprList_item { /* For each expression in the list */ |
| 10850 | Expr *pExpr; /* The list of expressions */ |
| 10851 | char *zName; /* Token associated with this expression */ |
| 10852 | char *zSpan; /* Original text of the expression */ |
| 10853 | u8 sortOrder; /* 1 for DESC or 0 for ASC */ |
| 10854 | u8 done; /* A flag to indicate when processing is finished */ |
| 10855 | u16 iOrderByCol; /* For ORDER BY, column number in result set */ |
| 10856 | u16 iAlias; /* Index into Parse.aAlias[] for zName */ |
| 10857 | } *a; /* Alloc a power of two greater or equal to nExpr */ |
| 10858 | }; |
| 10859 | |
| 10860 | /* |
| 10861 | ** An instance of this structure is used by the parser to record both |
| @@ -12141,10 +12156,11 @@ | |
| 12141 | SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *); |
| 12142 | SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...); |
| 12143 | SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*); |
| 12144 | SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *, Expr *, int, int); |
| 12145 | SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*); |
| 12146 | SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*); |
| 12147 | SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); |
| 12148 | SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); |
| 12149 | SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int); |
| 12150 | SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *); |
| @@ -23289,15 +23305,11 @@ | |
| 23289 | { "pwrite64", (sqlite3_syscall_ptr)0, 0 }, |
| 23290 | #endif |
| 23291 | #define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\ |
| 23292 | aSyscall[13].pCurrent) |
| 23293 | |
| 23294 | #if SQLITE_ENABLE_LOCKING_STYLE |
| 23295 | { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 }, |
| 23296 | #else |
| 23297 | { "fchmod", (sqlite3_syscall_ptr)0, 0 }, |
| 23298 | #endif |
| 23299 | #define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) |
| 23300 | |
| 23301 | #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE |
| 23302 | { "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 }, |
| 23303 | #else |
| @@ -23318,13 +23330,10 @@ | |
| 23318 | #define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent) |
| 23319 | |
| 23320 | { "fchown", (sqlite3_syscall_ptr)posixFchown, 0 }, |
| 23321 | #define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent) |
| 23322 | |
| 23323 | { "umask", (sqlite3_syscall_ptr)umask, 0 }, |
| 23324 | #define osUmask ((mode_t(*)(mode_t))aSyscall[21].pCurrent) |
| 23325 | |
| 23326 | }; /* End of the overrideable system calls */ |
| 23327 | |
| 23328 | /* |
| 23329 | ** This is the xSetSystemCall() method of sqlite3_vfs for all of the |
| 23330 | ** "unix" VFSes. Return SQLITE_OK opon successfully updating the |
| @@ -23425,31 +23434,29 @@ | |
| 23425 | ** process that is able to write to the database will also be able to |
| 23426 | ** recover the hot journals. |
| 23427 | */ |
| 23428 | static int robust_open(const char *z, int f, mode_t m){ |
| 23429 | int fd; |
| 23430 | mode_t m2; |
| 23431 | mode_t origM = 0; |
| 23432 | if( m==0 ){ |
| 23433 | m2 = SQLITE_DEFAULT_FILE_PERMISSIONS; |
| 23434 | }else{ |
| 23435 | m2 = m; |
| 23436 | origM = osUmask(0); |
| 23437 | } |
| 23438 | do{ |
| 23439 | #if defined(O_CLOEXEC) |
| 23440 | fd = osOpen(z,f|O_CLOEXEC,m2); |
| 23441 | #else |
| 23442 | fd = osOpen(z,f,m2); |
| 23443 | #endif |
| 23444 | }while( fd<0 && errno==EINTR ); |
| 23445 | if( m ){ |
| 23446 | osUmask(origM); |
| 23447 | } |
| 23448 | #if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC==0) |
| 23449 | if( fd>=0 ) osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); |
| 23450 | #endif |
| 23451 | return fd; |
| 23452 | } |
| 23453 | |
| 23454 | /* |
| 23455 | ** Helper functions to obtain and relinquish the global mutex. The |
| @@ -29871,11 +29878,11 @@ | |
| 29871 | }; |
| 29872 | unsigned int i; /* Loop counter */ |
| 29873 | |
| 29874 | /* Double-check that the aSyscall[] array has been constructed |
| 29875 | ** correctly. See ticket [bb3a86e890c8e96ab] */ |
| 29876 | assert( ArraySize(aSyscall)==22 ); |
| 29877 | |
| 29878 | /* Register all VFSes defined in the aVfs[] array */ |
| 29879 | for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ |
| 29880 | sqlite3_vfs_register(&aVfs[i], i==0); |
| 29881 | } |
| @@ -72655,10 +72662,39 @@ | |
| 72655 | } |
| 72656 | } |
| 72657 | return 0; |
| 72658 | } |
| 72659 | |
| 72660 | |
| 72661 | /* |
| 72662 | ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up |
| 72663 | ** that name in the set of source tables in pSrcList and make the pExpr |
| 72664 | ** expression node refer back to that source column. The following changes |
| @@ -72710,44 +72746,63 @@ | |
| 72710 | |
| 72711 | /* Initialize the node to no-match */ |
| 72712 | pExpr->iTable = -1; |
| 72713 | pExpr->pTab = 0; |
| 72714 | ExprSetIrreducible(pExpr); |
| 72715 | |
| 72716 | /* Start at the inner-most context and move outward until a match is found */ |
| 72717 | while( pNC && cnt==0 ){ |
| 72718 | ExprList *pEList; |
| 72719 | SrcList *pSrcList = pNC->pSrcList; |
| 72720 | |
| 72721 | if( pSrcList ){ |
| 72722 | for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){ |
| 72723 | Table *pTab; |
| 72724 | int iDb; |
| 72725 | Column *pCol; |
| 72726 | |
| 72727 | pTab = pItem->pTab; |
| 72728 | assert( pTab!=0 && pTab->zName!=0 ); |
| 72729 | iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| 72730 | assert( pTab->nCol>0 ); |
| 72731 | if( zTab ){ |
| 72732 | if( pItem->zAlias ){ |
| 72733 | char *zTabName = pItem->zAlias; |
| 72734 | if( sqlite3StrICmp(zTabName, zTab)!=0 ) continue; |
| 72735 | }else{ |
| 72736 | char *zTabName = pTab->zName; |
| 72737 | if( NEVER(zTabName==0) || sqlite3StrICmp(zTabName, zTab)!=0 ){ |
| 72738 | continue; |
| 72739 | } |
| 72740 | if( zDb!=0 && sqlite3StrICmp(db->aDb[iDb].zName, zDb)!=0 ){ |
| 72741 | continue; |
| 72742 | } |
| 72743 | } |
| 72744 | } |
| 72745 | if( 0==(cntTab++) ){ |
| 72746 | pExpr->iTable = pItem->iCursor; |
| 72747 | pExpr->pTab = pTab; |
| 72748 | pSchema = pTab->pSchema; |
| 72749 | pMatch = pItem; |
| 72750 | } |
| 72751 | for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){ |
| 72752 | if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ |
| 72753 | /* If there has been exactly one prior match and this match |
| @@ -72757,21 +72812,23 @@ | |
| 72757 | if( cnt==1 ){ |
| 72758 | if( pItem->jointype & JT_NATURAL ) continue; |
| 72759 | if( nameInUsingClause(pItem->pUsing, zCol) ) continue; |
| 72760 | } |
| 72761 | cnt++; |
| 72762 | pExpr->iTable = pItem->iCursor; |
| 72763 | pExpr->pTab = pTab; |
| 72764 | pMatch = pItem; |
| 72765 | pSchema = pTab->pSchema; |
| 72766 | /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ |
| 72767 | pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; |
| 72768 | break; |
| 72769 | } |
| 72770 | } |
| 72771 | } |
| 72772 | } |
| 72773 | |
| 72774 | #ifndef SQLITE_OMIT_TRIGGER |
| 72775 | /* If we have not already resolved the name, then maybe |
| 72776 | ** it is a new.* or old.* trigger argument reference |
| 72777 | */ |
| @@ -73102,11 +73159,11 @@ | |
| 73102 | #endif |
| 73103 | if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){ |
| 73104 | sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId); |
| 73105 | pNC->nErr++; |
| 73106 | is_agg = 0; |
| 73107 | }else if( no_such_func ){ |
| 73108 | sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId); |
| 73109 | pNC->nErr++; |
| 73110 | }else if( wrong_num_args ){ |
| 73111 | sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()", |
| 73112 | nId, zId); |
| @@ -77065,10 +77122,16 @@ | |
| 77065 | for(i=0; i<pList->nExpr; i++){ |
| 77066 | sqlite3ExplainPrintf(pOut, "item[%d] = ", i); |
| 77067 | sqlite3ExplainPush(pOut); |
| 77068 | sqlite3ExplainExpr(pOut, pList->a[i].pExpr); |
| 77069 | sqlite3ExplainPop(pOut); |
| 77070 | if( i<pList->nExpr-1 ){ |
| 77071 | sqlite3ExplainNL(pOut); |
| 77072 | } |
| 77073 | } |
| 77074 | sqlite3ExplainPop(pOut); |
| @@ -92739,13 +92802,15 @@ | |
| 92739 | if( sqlite3StrICmp(zLeft, "table_info")==0 && zRight ){ |
| 92740 | Table *pTab; |
| 92741 | if( sqlite3ReadSchema(pParse) ) goto pragma_out; |
| 92742 | pTab = sqlite3FindTable(db, zRight, zDb); |
| 92743 | if( pTab ){ |
| 92744 | int i; |
| 92745 | int nHidden = 0; |
| 92746 | Column *pCol; |
| 92747 | sqlite3VdbeSetNumCols(v, 6); |
| 92748 | pParse->nMem = 6; |
| 92749 | sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cid", SQLITE_STATIC); |
| 92750 | sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC); |
| 92751 | sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "type", SQLITE_STATIC); |
| @@ -92766,12 +92831,18 @@ | |
| 92766 | if( pCol->zDflt ){ |
| 92767 | sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)pCol->zDflt, 0); |
| 92768 | }else{ |
| 92769 | sqlite3VdbeAddOp2(v, OP_Null, 0, 5); |
| 92770 | } |
| 92771 | sqlite3VdbeAddOp2(v, OP_Integer, |
| 92772 | (pCol->colFlags&COLFLAG_PRIMKEY)!=0, 6); |
| 92773 | sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6); |
| 92774 | } |
| 92775 | } |
| 92776 | }else |
| 92777 | |
| @@ -93516,11 +93587,11 @@ | |
| 93516 | sqlite3_rekey(db, zKey, i/2); |
| 93517 | } |
| 93518 | }else |
| 93519 | #endif |
| 93520 | #if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) |
| 93521 | if( sqlite3StrICmp(zLeft, "activate_extensions")==0 ){ |
| 93522 | #ifdef SQLITE_HAS_CODEC |
| 93523 | if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){ |
| 93524 | sqlite3_activate_see(&zRight[4]); |
| 93525 | } |
| 93526 | #endif |
| @@ -95756,12 +95827,10 @@ | |
| 95756 | |
| 95757 | for(i=0, pCol=aCol; i<nCol; i++, pCol++){ |
| 95758 | /* Get an appropriate name for the column |
| 95759 | */ |
| 95760 | p = sqlite3ExprSkipCollate(pEList->a[i].pExpr); |
| 95761 | assert( p->pRight==0 || ExprHasProperty(p->pRight, EP_IntValue) |
| 95762 | || p->pRight->u.zToken==0 || p->pRight->u.zToken[0]!=0 ); |
| 95763 | if( (zName = pEList->a[i].zName)!=0 ){ |
| 95764 | /* If the column contains an "AS <name>" phrase, use <name> as the name */ |
| 95765 | zName = sqlite3DbStrDup(db, zName); |
| 95766 | }else{ |
| 95767 | Expr *pColExpr = p; /* The expression that is the result column name */ |
| @@ -95795,10 +95864,13 @@ | |
| 95795 | */ |
| 95796 | nName = sqlite3Strlen30(zName); |
| 95797 | for(j=cnt=0; j<i; j++){ |
| 95798 | if( sqlite3StrICmp(aCol[j].zName, zName)==0 ){ |
| 95799 | char *zNewName; |
| 95800 | zName[nName] = 0; |
| 95801 | zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt); |
| 95802 | sqlite3DbFree(db, zName); |
| 95803 | zName = zNewName; |
| 95804 | j = -1; |
| @@ -97711,10 +97783,11 @@ | |
| 97711 | int i, j, k; |
| 97712 | SrcList *pTabList; |
| 97713 | ExprList *pEList; |
| 97714 | struct SrcList_item *pFrom; |
| 97715 | sqlite3 *db = pParse->db; |
| 97716 | |
| 97717 | if( db->mallocFailed ){ |
| 97718 | return WRC_Abort; |
| 97719 | } |
| 97720 | if( NEVER(p->pSrc==0) || (p->selFlags & SF_Expanded)!=0 ){ |
| @@ -97796,11 +97869,11 @@ | |
| 97796 | ** |
| 97797 | ** The first loop just checks to see if there are any "*" operators |
| 97798 | ** that need expanding. |
| 97799 | */ |
| 97800 | for(k=0; k<pEList->nExpr; k++){ |
| 97801 | Expr *pE = pEList->a[k].pExpr; |
| 97802 | if( pE->op==TK_ALL ) break; |
| 97803 | assert( pE->op!=TK_DOT || pE->pRight!=0 ); |
| 97804 | assert( pE->op!=TK_DOT || (pE->pLeft!=0 && pE->pLeft->op==TK_ID) ); |
| 97805 | if( pE->op==TK_DOT && pE->pRight->op==TK_ALL ) break; |
| 97806 | } |
| @@ -97814,14 +97887,22 @@ | |
| 97814 | ExprList *pNew = 0; |
| 97815 | int flags = pParse->db->flags; |
| 97816 | int longNames = (flags & SQLITE_FullColNames)!=0 |
| 97817 | && (flags & SQLITE_ShortColNames)==0; |
| 97818 | |
| 97819 | for(k=0; k<pEList->nExpr; k++){ |
| 97820 | Expr *pE = a[k].pExpr; |
| 97821 | assert( pE->op!=TK_DOT || pE->pRight!=0 ); |
| 97822 | if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pE->pRight->op!=TK_ALL) ){ |
| 97823 | /* This particular expression does not need to be expanded. |
| 97824 | */ |
| 97825 | pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr); |
| 97826 | if( pNew ){ |
| 97827 | pNew->a[pNew->nExpr-1].zName = a[k].zName; |
| @@ -97832,44 +97913,56 @@ | |
| 97832 | a[k].pExpr = 0; |
| 97833 | }else{ |
| 97834 | /* This expression is a "*" or a "TABLE.*" and needs to be |
| 97835 | ** expanded. */ |
| 97836 | int tableSeen = 0; /* Set to 1 when TABLE matches */ |
| 97837 | char *zTName; /* text of name of TABLE */ |
| 97838 | if( pE->op==TK_DOT ){ |
| 97839 | assert( pE->pLeft!=0 ); |
| 97840 | assert( !ExprHasProperty(pE->pLeft, EP_IntValue) ); |
| 97841 | zTName = pE->pLeft->u.zToken; |
| 97842 | }else{ |
| 97843 | zTName = 0; |
| 97844 | } |
| 97845 | for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){ |
| 97846 | Table *pTab = pFrom->pTab; |
| 97847 | char *zTabName = pFrom->zAlias; |
| 97848 | if( zTabName==0 ){ |
| 97849 | zTabName = pTab->zName; |
| 97850 | } |
| 97851 | if( db->mallocFailed ) break; |
| 97852 | if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){ |
| 97853 | continue; |
| 97854 | } |
| 97855 | tableSeen = 1; |
| 97856 | for(j=0; j<pTab->nCol; j++){ |
| 97857 | Expr *pExpr, *pRight; |
| 97858 | char *zName = pTab->aCol[j].zName; |
| 97859 | char *zColname; /* The computed column name */ |
| 97860 | char *zToFree; /* Malloced string that needs to be freed */ |
| 97861 | Token sColname; /* Computed column name as a token */ |
| 97862 | |
| 97863 | /* If a column is marked as 'hidden' (currently only possible |
| 97864 | ** for virtual tables), do not include it in the expanded |
| 97865 | ** result-set list. |
| 97866 | */ |
| 97867 | if( IsHiddenColumn(&pTab->aCol[j]) ){ |
| 97868 | assert(IsVirtual(pTab)); |
| 97869 | continue; |
| 97870 | } |
| 97871 | |
| 97872 | if( i>0 && zTName==0 ){ |
| 97873 | if( (pFrom->jointype & JT_NATURAL)!=0 |
| 97874 | && tableAndColumnIndex(pTabList, i, zName, 0, 0) |
| 97875 | ){ |
| @@ -97888,10 +97981,14 @@ | |
| 97888 | zToFree = 0; |
| 97889 | if( longNames || pTabList->nSrc>1 ){ |
| 97890 | Expr *pLeft; |
| 97891 | pLeft = sqlite3Expr(db, TK_ID, zTabName); |
| 97892 | pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0); |
| 97893 | if( longNames ){ |
| 97894 | zColname = sqlite3MPrintf(db, "%s.%s", zTabName, zName); |
| 97895 | zToFree = zColname; |
| 97896 | } |
| 97897 | }else{ |
| @@ -97899,10 +97996,22 @@ | |
| 97899 | } |
| 97900 | pNew = sqlite3ExprListAppend(pParse, pNew, pExpr); |
| 97901 | sColname.z = zColname; |
| 97902 | sColname.n = sqlite3Strlen30(zColname); |
| 97903 | sqlite3ExprListSetName(pParse, pNew, &sColname, 0); |
| 97904 | sqlite3DbFree(db, zToFree); |
| 97905 | } |
| 97906 | } |
| 97907 | if( !tableSeen ){ |
| 97908 | if( zTName ){ |
| @@ -102699,12 +102808,12 @@ | |
| 102699 | Expr *pExpr; /* Pointer to the subexpression that is this term */ |
| 102700 | int iParent; /* Disable pWC->a[iParent] when this term disabled */ |
| 102701 | int leftCursor; /* Cursor number of X in "X <op> <expr>" */ |
| 102702 | union { |
| 102703 | int leftColumn; /* Column number of X in "X <op> <expr>" */ |
| 102704 | WhereOrInfo *pOrInfo; /* Extra information if eOperator==WO_OR */ |
| 102705 | WhereAndInfo *pAndInfo; /* Extra information if eOperator==WO_AND */ |
| 102706 | } u; |
| 102707 | u16 eOperator; /* A WO_xx value describing <op> */ |
| 102708 | u8 wtFlags; /* TERM_xxx bit flags. See below */ |
| 102709 | u8 nChild; /* Number of children that must disable us */ |
| 102710 | WhereClause *pWC; /* The clause this term is part of */ |
| @@ -102828,10 +102937,11 @@ | |
| 102828 | #define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) |
| 102829 | #define WO_MATCH 0x040 |
| 102830 | #define WO_ISNULL 0x080 |
| 102831 | #define WO_OR 0x100 /* Two or more OR-connected terms */ |
| 102832 | #define WO_AND 0x200 /* Two or more AND-connected terms */ |
| 102833 | #define WO_NOOP 0x800 /* This term does not restrict search space */ |
| 102834 | |
| 102835 | #define WO_ALL 0xfff /* Mask of all possible WO_* values */ |
| 102836 | #define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */ |
| 102837 | |
| @@ -103230,58 +103340,112 @@ | |
| 103230 | /* |
| 103231 | ** Search for a term in the WHERE clause that is of the form "X <op> <expr>" |
| 103232 | ** where X is a reference to the iColumn of table iCur and <op> is one of |
| 103233 | ** the WO_xx operator codes specified by the op parameter. |
| 103234 | ** Return a pointer to the term. Return 0 if not found. |
| 103235 | */ |
| 103236 | static WhereTerm *findTerm( |
| 103237 | WhereClause *pWC, /* The WHERE clause to be searched */ |
| 103238 | int iCur, /* Cursor number of LHS */ |
| 103239 | int iColumn, /* Column number of LHS */ |
| 103240 | Bitmask notReady, /* RHS must not overlap with this mask */ |
| 103241 | u32 op, /* Mask of WO_xx values describing operator */ |
| 103242 | Index *pIdx /* Must be compatible with this index, if not NULL */ |
| 103243 | ){ |
| 103244 | WhereTerm *pTerm; |
| 103245 | int k; |
| 103246 | assert( iCur>=0 ); |
| 103247 | op &= WO_ALL; |
| 103248 | for(; pWC; pWC=pWC->pOuter){ |
| 103249 | for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ |
| 103250 | if( pTerm->leftCursor==iCur |
| 103251 | && (pTerm->prereqRight & notReady)==0 |
| 103252 | && pTerm->u.leftColumn==iColumn |
| 103253 | && (pTerm->eOperator & op)!=0 |
| 103254 | ){ |
| 103255 | if( iColumn>=0 && pIdx && pTerm->eOperator!=WO_ISNULL ){ |
| 103256 | Expr *pX = pTerm->pExpr; |
| 103257 | CollSeq *pColl; |
| 103258 | char idxaff; |
| 103259 | int j; |
| 103260 | Parse *pParse = pWC->pParse; |
| 103261 | |
| 103262 | idxaff = pIdx->pTable->aCol[iColumn].affinity; |
| 103263 | if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue; |
| 103264 | |
| 103265 | /* Figure out the collation sequence required from an index for |
| 103266 | ** it to be useful for optimising expression pX. Store this |
| 103267 | ** value in variable pColl. |
| 103268 | */ |
| 103269 | assert(pX->pLeft); |
| 103270 | pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); |
| 103271 | if( pColl==0 ) pColl = pParse->db->pDfltColl; |
| 103272 | |
| 103273 | for(j=0; pIdx->aiColumn[j]!=iColumn; j++){ |
| 103274 | if( NEVER(j>=pIdx->nColumn) ) return 0; |
| 103275 | } |
| 103276 | if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue; |
| 103277 | } |
| 103278 | return pTerm; |
| 103279 | } |
| 103280 | } |
| 103281 | } |
| 103282 | return 0; |
| 103283 | } |
| 103284 | |
| 103285 | /* Forward reference */ |
| 103286 | static void exprAnalyze(SrcList*, WhereClause*, int); |
| 103287 | |
| @@ -103555,11 +103719,10 @@ | |
| 103555 | indexable = ~(Bitmask)0; |
| 103556 | chngToIN = ~(pWC->vmask); |
| 103557 | for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0 && indexable; i--, pOrTerm++){ |
| 103558 | if( (pOrTerm->eOperator & WO_SINGLE)==0 ){ |
| 103559 | WhereAndInfo *pAndInfo; |
| 103560 | assert( pOrTerm->eOperator==0 ); |
| 103561 | assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 ); |
| 103562 | chngToIN = 0; |
| 103563 | pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo)); |
| 103564 | if( pAndInfo ){ |
| 103565 | WhereClause *pAndWC; |
| @@ -103594,11 +103757,11 @@ | |
| 103594 | if( pOrTerm->wtFlags & TERM_VIRTUAL ){ |
| 103595 | WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent]; |
| 103596 | b |= getMask(pMaskSet, pOther->leftCursor); |
| 103597 | } |
| 103598 | indexable &= b; |
| 103599 | if( pOrTerm->eOperator!=WO_EQ ){ |
| 103600 | chngToIN = 0; |
| 103601 | }else{ |
| 103602 | chngToIN &= b; |
| 103603 | } |
| 103604 | } |
| @@ -103645,11 +103808,11 @@ | |
| 103645 | ** and column is found but leave okToChngToIN false if not found. |
| 103646 | */ |
| 103647 | for(j=0; j<2 && !okToChngToIN; j++){ |
| 103648 | pOrTerm = pOrWc->a; |
| 103649 | for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){ |
| 103650 | assert( pOrTerm->eOperator==WO_EQ ); |
| 103651 | pOrTerm->wtFlags &= ~TERM_OR_OK; |
| 103652 | if( pOrTerm->leftCursor==iCursor ){ |
| 103653 | /* This is the 2-bit case and we are on the second iteration and |
| 103654 | ** current term is from the first iteration. So skip this term. */ |
| 103655 | assert( j==1 ); |
| @@ -103671,21 +103834,21 @@ | |
| 103671 | } |
| 103672 | if( i<0 ){ |
| 103673 | /* No candidate table+column was found. This can only occur |
| 103674 | ** on the second iteration */ |
| 103675 | assert( j==1 ); |
| 103676 | assert( (chngToIN&(chngToIN-1))==0 ); |
| 103677 | assert( chngToIN==getMask(pMaskSet, iCursor) ); |
| 103678 | break; |
| 103679 | } |
| 103680 | testcase( j==1 ); |
| 103681 | |
| 103682 | /* We have found a candidate table and column. Check to see if that |
| 103683 | ** table and column is common to every term in the OR clause */ |
| 103684 | okToChngToIN = 1; |
| 103685 | for(; i>=0 && okToChngToIN; i--, pOrTerm++){ |
| 103686 | assert( pOrTerm->eOperator==WO_EQ ); |
| 103687 | if( pOrTerm->leftCursor!=iCursor ){ |
| 103688 | pOrTerm->wtFlags &= ~TERM_OR_OK; |
| 103689 | }else if( pOrTerm->u.leftColumn!=iColumn ){ |
| 103690 | okToChngToIN = 0; |
| 103691 | }else{ |
| @@ -103717,11 +103880,11 @@ | |
| 103717 | Expr *pLeft = 0; /* The LHS of the IN operator */ |
| 103718 | Expr *pNew; /* The complete IN operator */ |
| 103719 | |
| 103720 | for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){ |
| 103721 | if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue; |
| 103722 | assert( pOrTerm->eOperator==WO_EQ ); |
| 103723 | assert( pOrTerm->leftCursor==iCursor ); |
| 103724 | assert( pOrTerm->u.leftColumn==iColumn ); |
| 103725 | pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); |
| 103726 | pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup); |
| 103727 | pLeft = pOrTerm->pExpr->pLeft; |
| @@ -103746,11 +103909,10 @@ | |
| 103746 | pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */ |
| 103747 | } |
| 103748 | } |
| 103749 | } |
| 103750 | #endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */ |
| 103751 | |
| 103752 | |
| 103753 | /* |
| 103754 | ** The input to this routine is an WhereTerm structure with only the |
| 103755 | ** "pExpr" field filled in. The job of this routine is to analyze the |
| 103756 | ** subexpression and populate all the other fields of the WhereTerm |
| @@ -103816,21 +103978,23 @@ | |
| 103816 | } |
| 103817 | pTerm->prereqAll = prereqAll; |
| 103818 | pTerm->leftCursor = -1; |
| 103819 | pTerm->iParent = -1; |
| 103820 | pTerm->eOperator = 0; |
| 103821 | if( allowedOp(op) && (pTerm->prereqRight & prereqLeft)==0 ){ |
| 103822 | Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft); |
| 103823 | Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight); |
| 103824 | if( pLeft->op==TK_COLUMN ){ |
| 103825 | pTerm->leftCursor = pLeft->iTable; |
| 103826 | pTerm->u.leftColumn = pLeft->iColumn; |
| 103827 | pTerm->eOperator = operatorMask(op); |
| 103828 | } |
| 103829 | if( pRight && pRight->op==TK_COLUMN ){ |
| 103830 | WhereTerm *pNew; |
| 103831 | Expr *pDup; |
| 103832 | if( pTerm->leftCursor>=0 ){ |
| 103833 | int idxNew; |
| 103834 | pDup = sqlite3ExprDup(db, pExpr, 0); |
| 103835 | if( db->mallocFailed ){ |
| 103836 | sqlite3ExprDelete(db, pDup); |
| @@ -103841,10 +104005,17 @@ | |
| 103841 | pNew = &pWC->a[idxNew]; |
| 103842 | pNew->iParent = idxTerm; |
| 103843 | pTerm = &pWC->a[idxTerm]; |
| 103844 | pTerm->nChild = 1; |
| 103845 | pTerm->wtFlags |= TERM_COPIED; |
| 103846 | }else{ |
| 103847 | pDup = pExpr; |
| 103848 | pNew = pTerm; |
| 103849 | } |
| 103850 | exprCommute(pParse, pDup); |
| @@ -103852,11 +104023,11 @@ | |
| 103852 | pNew->leftCursor = pLeft->iTable; |
| 103853 | pNew->u.leftColumn = pLeft->iColumn; |
| 103854 | testcase( (prereqLeft | extraRight) != prereqLeft ); |
| 103855 | pNew->prereqRight = prereqLeft | extraRight; |
| 103856 | pNew->prereqAll = prereqAll; |
| 103857 | pNew->eOperator = operatorMask(pDup->op); |
| 103858 | } |
| 103859 | } |
| 103860 | |
| 103861 | #ifndef SQLITE_OMIT_BETWEEN_OPTIMIZATION |
| 103862 | /* If a term is the BETWEEN operator, create two new virtual terms |
| @@ -104311,11 +104482,11 @@ | |
| 104311 | return; |
| 104312 | } |
| 104313 | |
| 104314 | /* Search the WHERE clause terms for a usable WO_OR term. */ |
| 104315 | for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ |
| 104316 | if( pTerm->eOperator==WO_OR |
| 104317 | && ((pTerm->prereqAll & ~maskSrc) & p->notReady)==0 |
| 104318 | && (pTerm->u.pOrInfo->indexable & maskSrc)!=0 |
| 104319 | ){ |
| 104320 | WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc; |
| 104321 | WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm]; |
| @@ -104332,11 +104503,11 @@ | |
| 104332 | sBOI.ppIdxInfo = 0; |
| 104333 | for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){ |
| 104334 | WHERETRACE(("... Multi-index OR testing for term %d of %d....\n", |
| 104335 | (pOrTerm - pOrWC->a), (pTerm - pWC->a) |
| 104336 | )); |
| 104337 | if( pOrTerm->eOperator==WO_AND ){ |
| 104338 | sBOI.pWC = &pOrTerm->u.pAndInfo->wc; |
| 104339 | bestIndex(&sBOI); |
| 104340 | }else if( pOrTerm->leftCursor==iCur ){ |
| 104341 | WhereClause tempWC; |
| 104342 | tempWC.pParse = pWC->pParse; |
| @@ -104393,11 +104564,11 @@ | |
| 104393 | struct SrcList_item *pSrc, /* Table we are trying to access */ |
| 104394 | Bitmask notReady /* Tables in outer loops of the join */ |
| 104395 | ){ |
| 104396 | char aff; |
| 104397 | if( pTerm->leftCursor!=pSrc->iCursor ) return 0; |
| 104398 | if( pTerm->eOperator!=WO_EQ ) return 0; |
| 104399 | if( (pTerm->prereqRight & notReady)!=0 ) return 0; |
| 104400 | aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; |
| 104401 | if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; |
| 104402 | return 1; |
| 104403 | } |
| @@ -104655,13 +104826,13 @@ | |
| 104655 | |
| 104656 | /* Count the number of possible WHERE clause constraints referring |
| 104657 | ** to this virtual table */ |
| 104658 | for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 104659 | if( pTerm->leftCursor != pSrc->iCursor ) continue; |
| 104660 | assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 ); |
| 104661 | testcase( pTerm->eOperator==WO_IN ); |
| 104662 | testcase( pTerm->eOperator==WO_ISNULL ); |
| 104663 | if( pTerm->eOperator & (WO_ISNULL) ) continue; |
| 104664 | if( pTerm->wtFlags & TERM_VNULL ) continue; |
| 104665 | nTerm++; |
| 104666 | } |
| 104667 | |
| @@ -104708,18 +104879,18 @@ | |
| 104708 | pUsage; |
| 104709 | |
| 104710 | for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 104711 | u8 op; |
| 104712 | if( pTerm->leftCursor != pSrc->iCursor ) continue; |
| 104713 | assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 ); |
| 104714 | testcase( pTerm->eOperator==WO_IN ); |
| 104715 | testcase( pTerm->eOperator==WO_ISNULL ); |
| 104716 | if( pTerm->eOperator & (WO_ISNULL) ) continue; |
| 104717 | if( pTerm->wtFlags & TERM_VNULL ) continue; |
| 104718 | pIdxCons[j].iColumn = pTerm->u.leftColumn; |
| 104719 | pIdxCons[j].iTermOffset = i; |
| 104720 | op = (u8)pTerm->eOperator; |
| 104721 | if( op==WO_IN ) op = WO_EQ; |
| 104722 | pIdxCons[j].op = op; |
| 104723 | /* The direct assignment in the previous line is possible only because |
| 104724 | ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The |
| 104725 | ** following asserts verify this fact. */ |
| @@ -104885,11 +105056,11 @@ | |
| 104885 | pUsage = pIdxInfo->aConstraintUsage; |
| 104886 | for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ |
| 104887 | j = pIdxCons->iTermOffset; |
| 104888 | pTerm = &pWC->a[j]; |
| 104889 | if( (pTerm->prereqRight&p->notReady)==0 |
| 104890 | && (bAllowIN || pTerm->eOperator!=WO_IN) |
| 104891 | ){ |
| 104892 | pIdxCons->usable = 1; |
| 104893 | }else{ |
| 104894 | pIdxCons->usable = 0; |
| 104895 | } |
| @@ -104917,11 +105088,11 @@ | |
| 104917 | for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ |
| 104918 | if( pUsage[i].argvIndex>0 ){ |
| 104919 | j = pIdxCons->iTermOffset; |
| 104920 | pTerm = &pWC->a[j]; |
| 104921 | p->cost.used |= pTerm->prereqRight; |
| 104922 | if( pTerm->eOperator==WO_IN && pUsage[i].omit==0 ){ |
| 104923 | /* Do not attempt to use an IN constraint if the virtual table |
| 104924 | ** says that the equivalent EQ constraint cannot be safely omitted. |
| 104925 | ** If we do attempt to use such a constraint, some rows might be |
| 104926 | ** repeated in the output. */ |
| 104927 | break; |
| @@ -105223,28 +105394,28 @@ | |
| 105223 | u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; |
| 105224 | |
| 105225 | if( pLower ){ |
| 105226 | Expr *pExpr = pLower->pExpr->pRight; |
| 105227 | rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); |
| 105228 | assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE ); |
| 105229 | if( rc==SQLITE_OK |
| 105230 | && whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK |
| 105231 | ){ |
| 105232 | iLower = a[0]; |
| 105233 | if( pLower->eOperator==WO_GT ) iLower += a[1]; |
| 105234 | } |
| 105235 | sqlite3ValueFree(pRangeVal); |
| 105236 | } |
| 105237 | if( rc==SQLITE_OK && pUpper ){ |
| 105238 | Expr *pExpr = pUpper->pExpr->pRight; |
| 105239 | rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); |
| 105240 | assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE ); |
| 105241 | if( rc==SQLITE_OK |
| 105242 | && whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK |
| 105243 | ){ |
| 105244 | iUpper = a[0]; |
| 105245 | if( pUpper->eOperator==WO_LE ) iUpper += a[1]; |
| 105246 | } |
| 105247 | sqlite3ValueFree(pRangeVal); |
| 105248 | } |
| 105249 | if( rc==SQLITE_OK ){ |
| 105250 | if( iUpper<=iLower ){ |
| @@ -105548,16 +105719,16 @@ | |
| 105548 | ** if there are any X= or X IS NULL constraints in the WHERE clause. */ |
| 105549 | pConstraint = findTerm(p->pWC, base, iColumn, p->notReady, |
| 105550 | WO_EQ|WO_ISNULL|WO_IN, pIdx); |
| 105551 | if( pConstraint==0 ){ |
| 105552 | isEq = 0; |
| 105553 | }else if( pConstraint->eOperator==WO_IN ){ |
| 105554 | /* Constraints of the form: "X IN ..." cannot be used with an ORDER BY |
| 105555 | ** because we do not know in what order the values on the RHS of the IN |
| 105556 | ** operator will occur. */ |
| 105557 | break; |
| 105558 | }else if( pConstraint->eOperator==WO_ISNULL ){ |
| 105559 | uniqueNotNull = 0; |
| 105560 | isEq = 1; /* "X IS NULL" means X has only a single value */ |
| 105561 | }else if( pConstraint->prereqRight==0 ){ |
| 105562 | isEq = 1; /* Constraint "X=constant" means X has only a single value */ |
| 105563 | }else{ |
| @@ -105903,11 +106074,11 @@ | |
| 105903 | int bRev = 2; |
| 105904 | WHERETRACE((" --> before isSortingIndex: nPriorSat=%d\n",nPriorSat)); |
| 105905 | pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev); |
| 105906 | WHERETRACE((" --> after isSortingIndex: bRev=%d nOBSat=%d\n", |
| 105907 | bRev, pc.plan.nOBSat)); |
| 105908 | if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_UNIQUE)!=0 ){ |
| 105909 | pc.plan.wsFlags |= WHERE_ORDERED; |
| 105910 | } |
| 105911 | if( nOrderBy==pc.plan.nOBSat ){ |
| 105912 | bSort = 0; |
| 105913 | pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE; |
| @@ -105966,16 +106137,17 @@ | |
| 105966 | */ |
| 105967 | if( pc.plan.nRow>(double)1 && pc.plan.nEq==1 |
| 105968 | && pFirstTerm!=0 && aiRowEst[1]>1 ){ |
| 105969 | assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 ); |
| 105970 | if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){ |
| 105971 | testcase( pFirstTerm->eOperator==WO_EQ ); |
| 105972 | testcase( pFirstTerm->eOperator==WO_ISNULL ); |
| 105973 | whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, |
| 105974 | &pc.plan.nRow); |
| 105975 | }else if( bInEst==0 ){ |
| 105976 | assert( pFirstTerm->eOperator==WO_IN ); |
| 105977 | whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, |
| 105978 | &pc.plan.nRow); |
| 105979 | } |
| 105980 | } |
| 105981 | #endif /* SQLITE_ENABLE_STAT3 */ |
| @@ -106118,11 +106290,11 @@ | |
| 106118 | ** more selective intentionally because of the subjective |
| 106119 | ** observation that indexed range constraints really are more |
| 106120 | ** selective in practice, on average. */ |
| 106121 | pc.plan.nRow /= 3; |
| 106122 | } |
| 106123 | }else if( pTerm->eOperator!=WO_NOOP ){ |
| 106124 | /* Any other expression lowers the output row count by half */ |
| 106125 | pc.plan.nRow /= 2; |
| 106126 | } |
| 106127 | } |
| 106128 | if( pc.plan.nRow<2 ) pc.plan.nRow = 2; |
| @@ -106170,12 +106342,13 @@ | |
| 106170 | assert( pSrc->pIndex==0 |
| 106171 | || p->cost.plan.u.pIdx==0 |
| 106172 | || p->cost.plan.u.pIdx==pSrc->pIndex |
| 106173 | ); |
| 106174 | |
| 106175 | WHERETRACE((" best index is: %s\n", |
| 106176 | p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk")); |
| 106177 | |
| 106178 | bestOrClauseIndex(p); |
| 106179 | bestAutomaticIndex(p); |
| 106180 | p->cost.plan.wsFlags |= eqTermMask; |
| 106181 | } |
| @@ -106753,11 +106926,10 @@ | |
| 106753 | */ |
| 106754 | iReleaseReg = sqlite3GetTempReg(pParse); |
| 106755 | pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0); |
| 106756 | assert( pTerm!=0 ); |
| 106757 | assert( pTerm->pExpr!=0 ); |
| 106758 | assert( pTerm->leftCursor==iCur ); |
| 106759 | assert( omitTable==0 ); |
| 106760 | testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ |
| 106761 | iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, iReleaseReg); |
| 106762 | addrNxt = pLevel->addrNxt; |
| 106763 | sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); |
| @@ -107144,11 +107316,11 @@ | |
| 107144 | int ii; /* Loop counter */ |
| 107145 | Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ |
| 107146 | |
| 107147 | pTerm = pLevel->plan.u.pTerm; |
| 107148 | assert( pTerm!=0 ); |
| 107149 | assert( pTerm->eOperator==WO_OR ); |
| 107150 | assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); |
| 107151 | pOrWc = &pTerm->u.pOrInfo->wc; |
| 107152 | pLevel->op = OP_Return; |
| 107153 | pLevel->p1 = regReturn; |
| 107154 | |
| @@ -107217,11 +107389,11 @@ | |
| 107217 | } |
| 107218 | } |
| 107219 | |
| 107220 | for(ii=0; ii<pOrWc->nTerm; ii++){ |
| 107221 | WhereTerm *pOrTerm = &pOrWc->a[ii]; |
| 107222 | if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){ |
| 107223 | WhereInfo *pSubWInfo; /* Info for single OR-term scan */ |
| 107224 | Expr *pOrExpr = pOrTerm->pExpr; |
| 107225 | if( pAndExpr ){ |
| 107226 | pAndExpr->pLeft = pOrExpr; |
| 107227 | pOrExpr = pAndExpr; |
| @@ -107672,10 +107844,11 @@ | |
| 107672 | Index *pIdx; /* Index for FROM table at pTabItem */ |
| 107673 | int j; /* For looping over FROM tables */ |
| 107674 | int bestJ = -1; /* The value of j */ |
| 107675 | Bitmask m; /* Bitmask value for j or bestJ */ |
| 107676 | int isOptimal; /* Iterator for optimal/non-optimal search */ |
| 107677 | int nUnconstrained; /* Number tables without INDEXED BY */ |
| 107678 | Bitmask notIndexed; /* Mask of tables that cannot use an index */ |
| 107679 | |
| 107680 | memset(&bestPlan, 0, sizeof(bestPlan)); |
| 107681 | bestPlan.rCost = SQLITE_BIG_DBL; |
| @@ -107706,14 +107879,12 @@ | |
| 107706 | ** |
| 107707 | ** The second loop iteration is only performed if no optimal scan |
| 107708 | ** strategies were found by the first iteration. This second iteration |
| 107709 | ** is used to search for the lowest cost scan overall. |
| 107710 | ** |
| 107711 | ** Previous versions of SQLite performed only the second iteration - |
| 107712 | ** the next outermost loop was always that with the lowest overall |
| 107713 | ** cost. However, this meant that SQLite could select the wrong plan |
| 107714 | ** for scripts such as the following: |
| 107715 | ** |
| 107716 | ** CREATE TABLE t1(a, b); |
| 107717 | ** CREATE TABLE t2(c, d); |
| 107718 | ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a; |
| 107719 | ** |
| @@ -107724,20 +107895,44 @@ | |
| 107724 | ** algorithm may choose to use t2 for the outer loop, which is a much |
| 107725 | ** costlier approach. |
| 107726 | */ |
| 107727 | nUnconstrained = 0; |
| 107728 | notIndexed = 0; |
| 107729 | for(isOptimal=(iFrom<nTabList-1); isOptimal>=0 && bestJ<0; isOptimal--){ |
| 107730 | for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){ |
| 107731 | int doNotReorder; /* True if this table should not be reordered */ |
| 107732 | |
| 107733 | doNotReorder = (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0; |
| 107734 | if( j!=iFrom && doNotReorder ) break; |
| 107735 | m = getMask(pMaskSet, sWBI.pSrc->iCursor); |
| 107736 | if( (m & sWBI.notValid)==0 ){ |
| 107737 | if( j==iFrom ) iFrom++; |
| 107738 | continue; |
| 107739 | } |
| 107740 | sWBI.notReady = (isOptimal ? m : sWBI.notValid); |
| 107741 | if( sWBI.pSrc->pIndex==0 ) nUnconstrained++; |
| 107742 | |
| 107743 | WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n", |
| @@ -107763,12 +107958,12 @@ | |
| 107763 | if( isOptimal && (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){ |
| 107764 | notIndexed |= m; |
| 107765 | } |
| 107766 | if( isOptimal ){ |
| 107767 | pWInfo->a[j].rOptCost = sWBI.cost.rCost; |
| 107768 | }else if( iFrom<nTabList-1 ){ |
| 107769 | /* If two or more tables have nearly the same outer loop cost, |
| 107770 | ** very different inner loop (optimal) cost, we want to choose |
| 107771 | ** for the outer loop that table which benefits the least from |
| 107772 | ** being in the inner loop. The following code scales the |
| 107773 | ** outer loop cost estimate to accomplish that. */ |
| 107774 | WHERETRACE((" scaling cost from %.1f to %.1f\n", |
| @@ -107809,15 +108004,23 @@ | |
| 107809 | sWBI.cost.rCost, sWBI.cost.plan.nRow, |
| 107810 | sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags)); |
| 107811 | bestPlan = sWBI.cost; |
| 107812 | bestJ = j; |
| 107813 | } |
| 107814 | if( doNotReorder ) break; |
| 107815 | } |
| 107816 | } |
| 107817 | assert( bestJ>=0 ); |
| 107818 | assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); |
| 107819 | WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n" |
| 107820 | " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n", |
| 107821 | bestJ, pTabList->a[bestJ].pTab->zName, |
| 107822 | pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow, |
| 107823 | bestPlan.plan.nOBSat, bestPlan.plan.wsFlags)); |
| @@ -136543,11 +136746,12 @@ | |
| 136543 | ** would fit in a single node, use a smaller node-size. |
| 136544 | */ |
| 136545 | static int getNodeSize( |
| 136546 | sqlite3 *db, /* Database handle */ |
| 136547 | Rtree *pRtree, /* Rtree handle */ |
| 136548 | int isCreate /* True for xCreate, false for xConnect */ |
| 136549 | ){ |
| 136550 | int rc; |
| 136551 | char *zSql; |
| 136552 | if( isCreate ){ |
| 136553 | int iPageSize = 0; |
| @@ -136556,17 +136760,22 @@ | |
| 136556 | if( rc==SQLITE_OK ){ |
| 136557 | pRtree->iNodeSize = iPageSize-64; |
| 136558 | if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){ |
| 136559 | pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS; |
| 136560 | } |
| 136561 | } |
| 136562 | }else{ |
| 136563 | zSql = sqlite3_mprintf( |
| 136564 | "SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1", |
| 136565 | pRtree->zDb, pRtree->zName |
| 136566 | ); |
| 136567 | rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize); |
| 136568 | } |
| 136569 | |
| 136570 | sqlite3_free(zSql); |
| 136571 | return rc; |
| 136572 | } |
| @@ -136626,11 +136835,11 @@ | |
| 136626 | pRtree->eCoordType = eCoordType; |
| 136627 | memcpy(pRtree->zDb, argv[1], nDb); |
| 136628 | memcpy(pRtree->zName, argv[2], nName); |
| 136629 | |
| 136630 | /* Figure out the node size to use. */ |
| 136631 | rc = getNodeSize(db, pRtree, isCreate); |
| 136632 | |
| 136633 | /* Create/Connect to the underlying relational database schema. If |
| 136634 | ** that is successful, call sqlite3_declare_vtab() to configure |
| 136635 | ** the r-tree table schema. |
| 136636 | */ |
| 136637 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -673,11 +673,11 @@ | |
| 673 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 674 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 675 | */ |
| 676 | #define SQLITE_VERSION "3.7.16" |
| 677 | #define SQLITE_VERSION_NUMBER 3007016 |
| 678 | #define SQLITE_SOURCE_ID "2013-01-17 17:20:49 38852f158ab20bb4d7b264af987ec1538052bec3" |
| 679 | |
| 680 | /* |
| 681 | ** CAPI3REF: Run-Time Library Version Numbers |
| 682 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 683 | ** |
| @@ -8238,10 +8238,15 @@ | |
| 8238 | ** A convenience macro that returns the number of elements in |
| 8239 | ** an array. |
| 8240 | */ |
| 8241 | #define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0]))) |
| 8242 | |
| 8243 | /* |
| 8244 | ** Determine if the argument is a power of two |
| 8245 | */ |
| 8246 | #define IsPowerOfTwo(X) (((X)&((X)-1))==0) |
| 8247 | |
| 8248 | /* |
| 8249 | ** The following value as a destructor means to use sqlite3DbFree(). |
| 8250 | ** The sqlite3DbFree() routine requires two parameters instead of the |
| 8251 | ** one parameter that destructors normally want. So we have to introduce |
| 8252 | ** this magic value that the code knows to handle differently. Any |
| @@ -10042,10 +10047,11 @@ | |
| 10047 | #define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */ |
| 10048 | #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ |
| 10049 | #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ |
| 10050 | #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ |
| 10051 | #define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */ |
| 10052 | #define SQLITE_Transitive 0x0200 /* Transitive constraints */ |
| 10053 | #define SQLITE_AllOpts 0xffff /* All optimizations */ |
| 10054 | |
| 10055 | /* |
| 10056 | ** Macros for testing whether or not optimizations are enabled or disabled. |
| 10057 | */ |
| @@ -10553,24 +10559,24 @@ | |
| 10559 | ** and the value of Index.onError indicate the which conflict resolution |
| 10560 | ** algorithm to employ whenever an attempt is made to insert a non-unique |
| 10561 | ** element. |
| 10562 | */ |
| 10563 | struct Index { |
| 10564 | char *zName; /* Name of this index */ |
| 10565 | int *aiColumn; /* Which columns are used by this index. 1st is 0 */ |
| 10566 | tRowcnt *aiRowEst; /* From ANALYZE: Est. rows selected by each column */ |
| 10567 | Table *pTable; /* The SQL table being indexed */ |
| 10568 | char *zColAff; /* String defining the affinity of each column */ |
| 10569 | Index *pNext; /* The next index associated with the same table */ |
| 10570 | Schema *pSchema; /* Schema containing this index */ |
| 10571 | u8 *aSortOrder; /* for each column: True==DESC, False==ASC */ |
| 10572 | char **azColl; /* Array of collation sequence names for index */ |
| 10573 | int tnum; /* DB Page containing root of this index */ |
| 10574 | u16 nColumn; /* Number of columns in table used by this index */ |
| 10575 | u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ |
| 10576 | unsigned autoIndex:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */ |
| 10577 | unsigned bUnordered:1; /* Use this index for == or IN queries only */ |
| 10578 | #ifdef SQLITE_ENABLE_STAT3 |
| 10579 | int nSample; /* Number of elements in aSample[] */ |
| 10580 | tRowcnt avgEq; /* Average nEq value for key values not in aSample */ |
| 10581 | IndexSample *aSample; /* Samples of the left-most key */ |
| 10582 | #endif |
| @@ -10840,22 +10846,31 @@ | |
| 10846 | ** name. An expr/name combination can be used in several ways, such |
| 10847 | ** as the list of "expr AS ID" fields following a "SELECT" or in the |
| 10848 | ** list of "ID = expr" items in an UPDATE. A list of expressions can |
| 10849 | ** also be used as the argument to a function, in which case the a.zName |
| 10850 | ** field is not used. |
| 10851 | ** |
| 10852 | ** By default the Expr.zSpan field holds a human-readable description of |
| 10853 | ** the expression that is used in the generation of error messages and |
| 10854 | ** column labels. In this case, Expr.zSpan is typically the text of a |
| 10855 | ** column expression as it exists in a SELECT statement. However, if |
| 10856 | ** the bSpanIsTab flag is set, then zSpan is overloaded to mean the name |
| 10857 | ** of the result column in the form: DATABASE.TABLE.COLUMN. This later |
| 10858 | ** form is used for name resolution with nested FROM clauses. |
| 10859 | */ |
| 10860 | struct ExprList { |
| 10861 | int nExpr; /* Number of expressions on the list */ |
| 10862 | int iECursor; /* VDBE Cursor associated with this ExprList */ |
| 10863 | struct ExprList_item { /* For each expression in the list */ |
| 10864 | Expr *pExpr; /* The list of expressions */ |
| 10865 | char *zName; /* Token associated with this expression */ |
| 10866 | char *zSpan; /* Original text of the expression */ |
| 10867 | u8 sortOrder; /* 1 for DESC or 0 for ASC */ |
| 10868 | unsigned done :1; /* A flag to indicate when processing is finished */ |
| 10869 | unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */ |
| 10870 | u16 iOrderByCol; /* For ORDER BY, column number in result set */ |
| 10871 | u16 iAlias; /* Index into Parse.aAlias[] for zName */ |
| 10872 | } *a; /* Alloc a power of two greater or equal to nExpr */ |
| 10873 | }; |
| 10874 | |
| 10875 | /* |
| 10876 | ** An instance of this structure is used by the parser to record both |
| @@ -12141,10 +12156,11 @@ | |
| 12156 | SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *); |
| 12157 | SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...); |
| 12158 | SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*); |
| 12159 | SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *, Expr *, int, int); |
| 12160 | SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*); |
| 12161 | SQLITE_PRIVATE int sqlite3MatchSpanName(const char*, const char*, const char*, const char*); |
| 12162 | SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*); |
| 12163 | SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); |
| 12164 | SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); |
| 12165 | SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int); |
| 12166 | SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *); |
| @@ -23289,15 +23305,11 @@ | |
| 23305 | { "pwrite64", (sqlite3_syscall_ptr)0, 0 }, |
| 23306 | #endif |
| 23307 | #define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\ |
| 23308 | aSyscall[13].pCurrent) |
| 23309 | |
| 23310 | { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 }, |
| 23311 | #define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) |
| 23312 | |
| 23313 | #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE |
| 23314 | { "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 }, |
| 23315 | #else |
| @@ -23318,13 +23330,10 @@ | |
| 23330 | #define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent) |
| 23331 | |
| 23332 | { "fchown", (sqlite3_syscall_ptr)posixFchown, 0 }, |
| 23333 | #define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent) |
| 23334 | |
| 23335 | }; /* End of the overrideable system calls */ |
| 23336 | |
| 23337 | /* |
| 23338 | ** This is the xSetSystemCall() method of sqlite3_vfs for all of the |
| 23339 | ** "unix" VFSes. Return SQLITE_OK opon successfully updating the |
| @@ -23425,31 +23434,29 @@ | |
| 23434 | ** process that is able to write to the database will also be able to |
| 23435 | ** recover the hot journals. |
| 23436 | */ |
| 23437 | static int robust_open(const char *z, int f, mode_t m){ |
| 23438 | int fd; |
| 23439 | mode_t m2 = m ? m : SQLITE_DEFAULT_FILE_PERMISSIONS; |
| 23440 | do{ |
| 23441 | #if defined(O_CLOEXEC) |
| 23442 | fd = osOpen(z,f|O_CLOEXEC,m2); |
| 23443 | #else |
| 23444 | fd = osOpen(z,f,m2); |
| 23445 | #endif |
| 23446 | }while( fd<0 && errno==EINTR ); |
| 23447 | if( fd>=0 ){ |
| 23448 | if( m!=0 ){ |
| 23449 | struct stat statbuf; |
| 23450 | if( osFstat(fd, &statbuf)==0 && (statbuf.st_mode&0777)!=m ){ |
| 23451 | osFchmod(fd, m); |
| 23452 | } |
| 23453 | } |
| 23454 | #if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC==0) |
| 23455 | osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); |
| 23456 | #endif |
| 23457 | } |
| 23458 | return fd; |
| 23459 | } |
| 23460 | |
| 23461 | /* |
| 23462 | ** Helper functions to obtain and relinquish the global mutex. The |
| @@ -29871,11 +29878,11 @@ | |
| 29878 | }; |
| 29879 | unsigned int i; /* Loop counter */ |
| 29880 | |
| 29881 | /* Double-check that the aSyscall[] array has been constructed |
| 29882 | ** correctly. See ticket [bb3a86e890c8e96ab] */ |
| 29883 | assert( ArraySize(aSyscall)==21 ); |
| 29884 | |
| 29885 | /* Register all VFSes defined in the aVfs[] array */ |
| 29886 | for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ |
| 29887 | sqlite3_vfs_register(&aVfs[i], i==0); |
| 29888 | } |
| @@ -72655,10 +72662,39 @@ | |
| 72662 | } |
| 72663 | } |
| 72664 | return 0; |
| 72665 | } |
| 72666 | |
| 72667 | /* |
| 72668 | ** Subqueries stores the original database, table and column names for their |
| 72669 | ** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN". |
| 72670 | ** Check to see if the zSpan given to this routine matches the zDb, zTab, |
| 72671 | ** and zCol. If any of zDb, zTab, and zCol are NULL then those fields will |
| 72672 | ** match anything. |
| 72673 | */ |
| 72674 | SQLITE_PRIVATE int sqlite3MatchSpanName( |
| 72675 | const char *zSpan, |
| 72676 | const char *zCol, |
| 72677 | const char *zTab, |
| 72678 | const char *zDb |
| 72679 | ){ |
| 72680 | int n; |
| 72681 | for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} |
| 72682 | if( zDb && sqlite3StrNICmp(zSpan, zDb, n)!=0 ){ |
| 72683 | return 0; |
| 72684 | } |
| 72685 | zSpan += n+1; |
| 72686 | for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} |
| 72687 | if( zTab && sqlite3StrNICmp(zSpan, zTab, n)!=0 ){ |
| 72688 | return 0; |
| 72689 | } |
| 72690 | zSpan += n+1; |
| 72691 | if( zCol && sqlite3StrICmp(zSpan, zCol)!=0 ){ |
| 72692 | return 0; |
| 72693 | } |
| 72694 | return 1; |
| 72695 | } |
| 72696 | |
| 72697 | /* |
| 72698 | ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up |
| 72699 | ** that name in the set of source tables in pSrcList and make the pExpr |
| 72700 | ** expression node refer back to that source column. The following changes |
| @@ -72710,44 +72746,63 @@ | |
| 72746 | |
| 72747 | /* Initialize the node to no-match */ |
| 72748 | pExpr->iTable = -1; |
| 72749 | pExpr->pTab = 0; |
| 72750 | ExprSetIrreducible(pExpr); |
| 72751 | |
| 72752 | /* Translate the schema name in zDb into a pointer to the corresponding |
| 72753 | ** schema. If not found, pSchema will remain NULL and nothing will match |
| 72754 | ** resulting in an appropriate error message toward the end of this routine |
| 72755 | */ |
| 72756 | if( zDb ){ |
| 72757 | for(i=0; i<db->nDb; i++){ |
| 72758 | assert( db->aDb[i].zName ); |
| 72759 | if( sqlite3StrICmp(db->aDb[i].zName,zDb)==0 ){ |
| 72760 | pSchema = db->aDb[i].pSchema; |
| 72761 | break; |
| 72762 | } |
| 72763 | } |
| 72764 | } |
| 72765 | |
| 72766 | /* Start at the inner-most context and move outward until a match is found */ |
| 72767 | while( pNC && cnt==0 ){ |
| 72768 | ExprList *pEList; |
| 72769 | SrcList *pSrcList = pNC->pSrcList; |
| 72770 | |
| 72771 | if( pSrcList ){ |
| 72772 | for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){ |
| 72773 | Table *pTab; |
| 72774 | Column *pCol; |
| 72775 | |
| 72776 | pTab = pItem->pTab; |
| 72777 | assert( pTab!=0 && pTab->zName!=0 ); |
| 72778 | assert( pTab->nCol>0 ); |
| 72779 | if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){ |
| 72780 | ExprList *pEList = pItem->pSelect->pEList; |
| 72781 | int hit = 0; |
| 72782 | for(j=0; j<pEList->nExpr; j++){ |
| 72783 | if( sqlite3MatchSpanName(pEList->a[j].zSpan, zCol, zTab, zDb) ){ |
| 72784 | cnt++; |
| 72785 | cntTab = 2; |
| 72786 | pMatch = pItem; |
| 72787 | pExpr->iColumn = j; |
| 72788 | hit = 1; |
| 72789 | } |
| 72790 | } |
| 72791 | if( hit || zTab==0 ) continue; |
| 72792 | } |
| 72793 | if( zDb && pTab->pSchema!=pSchema ){ |
| 72794 | continue; |
| 72795 | } |
| 72796 | if( zTab ){ |
| 72797 | const char *zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName; |
| 72798 | assert( zTabName!=0 ); |
| 72799 | if( sqlite3StrICmp(zTabName, zTab)!=0 ){ |
| 72800 | continue; |
| 72801 | } |
| 72802 | } |
| 72803 | if( 0==(cntTab++) ){ |
| 72804 | pMatch = pItem; |
| 72805 | } |
| 72806 | for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){ |
| 72807 | if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ |
| 72808 | /* If there has been exactly one prior match and this match |
| @@ -72757,21 +72812,23 @@ | |
| 72812 | if( cnt==1 ){ |
| 72813 | if( pItem->jointype & JT_NATURAL ) continue; |
| 72814 | if( nameInUsingClause(pItem->pUsing, zCol) ) continue; |
| 72815 | } |
| 72816 | cnt++; |
| 72817 | pMatch = pItem; |
| 72818 | /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ |
| 72819 | pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; |
| 72820 | break; |
| 72821 | } |
| 72822 | } |
| 72823 | } |
| 72824 | if( pMatch ){ |
| 72825 | pExpr->iTable = pMatch->iCursor; |
| 72826 | pExpr->pTab = pMatch->pTab; |
| 72827 | pSchema = pExpr->pTab->pSchema; |
| 72828 | } |
| 72829 | } /* if( pSrcList ) */ |
| 72830 | |
| 72831 | #ifndef SQLITE_OMIT_TRIGGER |
| 72832 | /* If we have not already resolved the name, then maybe |
| 72833 | ** it is a new.* or old.* trigger argument reference |
| 72834 | */ |
| @@ -73102,11 +73159,11 @@ | |
| 73159 | #endif |
| 73160 | if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){ |
| 73161 | sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId); |
| 73162 | pNC->nErr++; |
| 73163 | is_agg = 0; |
| 73164 | }else if( no_such_func && pParse->db->init.busy==0 ){ |
| 73165 | sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId); |
| 73166 | pNC->nErr++; |
| 73167 | }else if( wrong_num_args ){ |
| 73168 | sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()", |
| 73169 | nId, zId); |
| @@ -77065,10 +77122,16 @@ | |
| 77122 | for(i=0; i<pList->nExpr; i++){ |
| 77123 | sqlite3ExplainPrintf(pOut, "item[%d] = ", i); |
| 77124 | sqlite3ExplainPush(pOut); |
| 77125 | sqlite3ExplainExpr(pOut, pList->a[i].pExpr); |
| 77126 | sqlite3ExplainPop(pOut); |
| 77127 | if( pList->a[i].zName ){ |
| 77128 | sqlite3ExplainPrintf(pOut, " AS %s", pList->a[i].zName); |
| 77129 | } |
| 77130 | if( pList->a[i].bSpanIsTab ){ |
| 77131 | sqlite3ExplainPrintf(pOut, " (%s)", pList->a[i].zSpan); |
| 77132 | } |
| 77133 | if( i<pList->nExpr-1 ){ |
| 77134 | sqlite3ExplainNL(pOut); |
| 77135 | } |
| 77136 | } |
| 77137 | sqlite3ExplainPop(pOut); |
| @@ -92739,13 +92802,15 @@ | |
| 92802 | if( sqlite3StrICmp(zLeft, "table_info")==0 && zRight ){ |
| 92803 | Table *pTab; |
| 92804 | if( sqlite3ReadSchema(pParse) ) goto pragma_out; |
| 92805 | pTab = sqlite3FindTable(db, zRight, zDb); |
| 92806 | if( pTab ){ |
| 92807 | int i, k; |
| 92808 | int nHidden = 0; |
| 92809 | Column *pCol; |
| 92810 | Index *pPk; |
| 92811 | for(pPk=pTab->pIndex; pPk && pPk->autoIndex!=2; pPk=pPk->pNext){} |
| 92812 | sqlite3VdbeSetNumCols(v, 6); |
| 92813 | pParse->nMem = 6; |
| 92814 | sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cid", SQLITE_STATIC); |
| 92815 | sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC); |
| 92816 | sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "type", SQLITE_STATIC); |
| @@ -92766,12 +92831,18 @@ | |
| 92831 | if( pCol->zDflt ){ |
| 92832 | sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)pCol->zDflt, 0); |
| 92833 | }else{ |
| 92834 | sqlite3VdbeAddOp2(v, OP_Null, 0, 5); |
| 92835 | } |
| 92836 | if( (pCol->colFlags & COLFLAG_PRIMKEY)==0 ){ |
| 92837 | k = 0; |
| 92838 | }else if( pPk==0 ){ |
| 92839 | k = 1; |
| 92840 | }else{ |
| 92841 | for(k=1; ALWAYS(k<=pTab->nCol) && pPk->aiColumn[k-1]!=i; k++){} |
| 92842 | } |
| 92843 | sqlite3VdbeAddOp2(v, OP_Integer, k, 6); |
| 92844 | sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6); |
| 92845 | } |
| 92846 | } |
| 92847 | }else |
| 92848 | |
| @@ -93516,11 +93587,11 @@ | |
| 93587 | sqlite3_rekey(db, zKey, i/2); |
| 93588 | } |
| 93589 | }else |
| 93590 | #endif |
| 93591 | #if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) |
| 93592 | if( sqlite3StrICmp(zLeft, "activate_extensions")==0 && zRight ){ |
| 93593 | #ifdef SQLITE_HAS_CODEC |
| 93594 | if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){ |
| 93595 | sqlite3_activate_see(&zRight[4]); |
| 93596 | } |
| 93597 | #endif |
| @@ -95756,12 +95827,10 @@ | |
| 95827 | |
| 95828 | for(i=0, pCol=aCol; i<nCol; i++, pCol++){ |
| 95829 | /* Get an appropriate name for the column |
| 95830 | */ |
| 95831 | p = sqlite3ExprSkipCollate(pEList->a[i].pExpr); |
| 95832 | if( (zName = pEList->a[i].zName)!=0 ){ |
| 95833 | /* If the column contains an "AS <name>" phrase, use <name> as the name */ |
| 95834 | zName = sqlite3DbStrDup(db, zName); |
| 95835 | }else{ |
| 95836 | Expr *pColExpr = p; /* The expression that is the result column name */ |
| @@ -95795,10 +95864,13 @@ | |
| 95864 | */ |
| 95865 | nName = sqlite3Strlen30(zName); |
| 95866 | for(j=cnt=0; j<i; j++){ |
| 95867 | if( sqlite3StrICmp(aCol[j].zName, zName)==0 ){ |
| 95868 | char *zNewName; |
| 95869 | int k; |
| 95870 | for(k=nName-1; k>1 && sqlite3Isdigit(zName[k]); k--){} |
| 95871 | if( zName[k]==':' ) nName = k; |
| 95872 | zName[nName] = 0; |
| 95873 | zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt); |
| 95874 | sqlite3DbFree(db, zName); |
| 95875 | zName = zNewName; |
| 95876 | j = -1; |
| @@ -97711,10 +97783,11 @@ | |
| 97783 | int i, j, k; |
| 97784 | SrcList *pTabList; |
| 97785 | ExprList *pEList; |
| 97786 | struct SrcList_item *pFrom; |
| 97787 | sqlite3 *db = pParse->db; |
| 97788 | Expr *pE, *pRight, *pExpr; |
| 97789 | |
| 97790 | if( db->mallocFailed ){ |
| 97791 | return WRC_Abort; |
| 97792 | } |
| 97793 | if( NEVER(p->pSrc==0) || (p->selFlags & SF_Expanded)!=0 ){ |
| @@ -97796,11 +97869,11 @@ | |
| 97869 | ** |
| 97870 | ** The first loop just checks to see if there are any "*" operators |
| 97871 | ** that need expanding. |
| 97872 | */ |
| 97873 | for(k=0; k<pEList->nExpr; k++){ |
| 97874 | pE = pEList->a[k].pExpr; |
| 97875 | if( pE->op==TK_ALL ) break; |
| 97876 | assert( pE->op!=TK_DOT || pE->pRight!=0 ); |
| 97877 | assert( pE->op!=TK_DOT || (pE->pLeft!=0 && pE->pLeft->op==TK_ID) ); |
| 97878 | if( pE->op==TK_DOT && pE->pRight->op==TK_ALL ) break; |
| 97879 | } |
| @@ -97814,14 +97887,22 @@ | |
| 97887 | ExprList *pNew = 0; |
| 97888 | int flags = pParse->db->flags; |
| 97889 | int longNames = (flags & SQLITE_FullColNames)!=0 |
| 97890 | && (flags & SQLITE_ShortColNames)==0; |
| 97891 | |
| 97892 | /* When processing FROM-clause subqueries, it is always the case |
| 97893 | ** that full_column_names=OFF and short_column_names=ON. The |
| 97894 | ** sqlite3ResultSetOfSelect() routine makes it so. */ |
| 97895 | assert( (p->selFlags & SF_NestedFrom)==0 |
| 97896 | || ((flags & SQLITE_FullColNames)==0 && |
| 97897 | (flags & SQLITE_ShortColNames)!=0) ); |
| 97898 | |
| 97899 | for(k=0; k<pEList->nExpr; k++){ |
| 97900 | pE = a[k].pExpr; |
| 97901 | pRight = pE->pRight; |
| 97902 | assert( pE->op!=TK_DOT || pRight!=0 ); |
| 97903 | if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pRight->op!=TK_ALL) ){ |
| 97904 | /* This particular expression does not need to be expanded. |
| 97905 | */ |
| 97906 | pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr); |
| 97907 | if( pNew ){ |
| 97908 | pNew->a[pNew->nExpr-1].zName = a[k].zName; |
| @@ -97832,44 +97913,56 @@ | |
| 97913 | a[k].pExpr = 0; |
| 97914 | }else{ |
| 97915 | /* This expression is a "*" or a "TABLE.*" and needs to be |
| 97916 | ** expanded. */ |
| 97917 | int tableSeen = 0; /* Set to 1 when TABLE matches */ |
| 97918 | char *zTName = 0; /* text of name of TABLE */ |
| 97919 | if( pE->op==TK_DOT ){ |
| 97920 | assert( pE->pLeft!=0 ); |
| 97921 | assert( !ExprHasProperty(pE->pLeft, EP_IntValue) ); |
| 97922 | zTName = pE->pLeft->u.zToken; |
| 97923 | } |
| 97924 | for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){ |
| 97925 | Table *pTab = pFrom->pTab; |
| 97926 | Select *pSub = pFrom->pSelect; |
| 97927 | char *zTabName = pFrom->zAlias; |
| 97928 | const char *zSchemaName = 0; |
| 97929 | int iDb; |
| 97930 | if( zTabName==0 ){ |
| 97931 | zTabName = pTab->zName; |
| 97932 | } |
| 97933 | if( db->mallocFailed ) break; |
| 97934 | if( pSub==0 || (pSub->selFlags & SF_NestedFrom)==0 ){ |
| 97935 | pSub = 0; |
| 97936 | if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){ |
| 97937 | continue; |
| 97938 | } |
| 97939 | iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| 97940 | zSchemaName = iDb>=0 ? db->aDb[iDb].zName : "*"; |
| 97941 | } |
| 97942 | for(j=0; j<pTab->nCol; j++){ |
| 97943 | char *zName = pTab->aCol[j].zName; |
| 97944 | char *zColname; /* The computed column name */ |
| 97945 | char *zToFree; /* Malloced string that needs to be freed */ |
| 97946 | Token sColname; /* Computed column name as a token */ |
| 97947 | |
| 97948 | assert( zName ); |
| 97949 | if( zTName && pSub |
| 97950 | && sqlite3MatchSpanName(pSub->pEList->a[j].zSpan, 0, zTName, 0)==0 |
| 97951 | ){ |
| 97952 | continue; |
| 97953 | } |
| 97954 | |
| 97955 | /* If a column is marked as 'hidden' (currently only possible |
| 97956 | ** for virtual tables), do not include it in the expanded |
| 97957 | ** result-set list. |
| 97958 | */ |
| 97959 | if( IsHiddenColumn(&pTab->aCol[j]) ){ |
| 97960 | assert(IsVirtual(pTab)); |
| 97961 | continue; |
| 97962 | } |
| 97963 | tableSeen = 1; |
| 97964 | |
| 97965 | if( i>0 && zTName==0 ){ |
| 97966 | if( (pFrom->jointype & JT_NATURAL)!=0 |
| 97967 | && tableAndColumnIndex(pTabList, i, zName, 0, 0) |
| 97968 | ){ |
| @@ -97888,10 +97981,14 @@ | |
| 97981 | zToFree = 0; |
| 97982 | if( longNames || pTabList->nSrc>1 ){ |
| 97983 | Expr *pLeft; |
| 97984 | pLeft = sqlite3Expr(db, TK_ID, zTabName); |
| 97985 | pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0); |
| 97986 | if( zSchemaName ){ |
| 97987 | pLeft = sqlite3Expr(db, TK_ID, zSchemaName); |
| 97988 | pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pExpr, 0); |
| 97989 | } |
| 97990 | if( longNames ){ |
| 97991 | zColname = sqlite3MPrintf(db, "%s.%s", zTabName, zName); |
| 97992 | zToFree = zColname; |
| 97993 | } |
| 97994 | }else{ |
| @@ -97899,10 +97996,22 @@ | |
| 97996 | } |
| 97997 | pNew = sqlite3ExprListAppend(pParse, pNew, pExpr); |
| 97998 | sColname.z = zColname; |
| 97999 | sColname.n = sqlite3Strlen30(zColname); |
| 98000 | sqlite3ExprListSetName(pParse, pNew, &sColname, 0); |
| 98001 | if( pNew && (p->selFlags & SF_NestedFrom)!=0 ){ |
| 98002 | struct ExprList_item *pX = &pNew->a[pNew->nExpr-1]; |
| 98003 | if( pSub ){ |
| 98004 | pX->zSpan = sqlite3DbStrDup(db, pSub->pEList->a[j].zSpan); |
| 98005 | testcase( pX->zSpan==0 ); |
| 98006 | }else{ |
| 98007 | pX->zSpan = sqlite3MPrintf(db, "%s.%s.%s", |
| 98008 | zSchemaName, zTabName, zColname); |
| 98009 | testcase( pX->zSpan==0 ); |
| 98010 | } |
| 98011 | pX->bSpanIsTab = 1; |
| 98012 | } |
| 98013 | sqlite3DbFree(db, zToFree); |
| 98014 | } |
| 98015 | } |
| 98016 | if( !tableSeen ){ |
| 98017 | if( zTName ){ |
| @@ -102699,12 +102808,12 @@ | |
| 102808 | Expr *pExpr; /* Pointer to the subexpression that is this term */ |
| 102809 | int iParent; /* Disable pWC->a[iParent] when this term disabled */ |
| 102810 | int leftCursor; /* Cursor number of X in "X <op> <expr>" */ |
| 102811 | union { |
| 102812 | int leftColumn; /* Column number of X in "X <op> <expr>" */ |
| 102813 | WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */ |
| 102814 | WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */ |
| 102815 | } u; |
| 102816 | u16 eOperator; /* A WO_xx value describing <op> */ |
| 102817 | u8 wtFlags; /* TERM_xxx bit flags. See below */ |
| 102818 | u8 nChild; /* Number of children that must disable us */ |
| 102819 | WhereClause *pWC; /* The clause this term is part of */ |
| @@ -102828,10 +102937,11 @@ | |
| 102937 | #define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) |
| 102938 | #define WO_MATCH 0x040 |
| 102939 | #define WO_ISNULL 0x080 |
| 102940 | #define WO_OR 0x100 /* Two or more OR-connected terms */ |
| 102941 | #define WO_AND 0x200 /* Two or more AND-connected terms */ |
| 102942 | #define WO_EQUIV 0x400 /* Of the form A==B, both columns */ |
| 102943 | #define WO_NOOP 0x800 /* This term does not restrict search space */ |
| 102944 | |
| 102945 | #define WO_ALL 0xfff /* Mask of all possible WO_* values */ |
| 102946 | #define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */ |
| 102947 | |
| @@ -103230,58 +103340,112 @@ | |
| 103340 | /* |
| 103341 | ** Search for a term in the WHERE clause that is of the form "X <op> <expr>" |
| 103342 | ** where X is a reference to the iColumn of table iCur and <op> is one of |
| 103343 | ** the WO_xx operator codes specified by the op parameter. |
| 103344 | ** Return a pointer to the term. Return 0 if not found. |
| 103345 | ** |
| 103346 | ** The term returned might by Y=<expr> if there is another constraint in |
| 103347 | ** the WHERE clause that specifies that X=Y. Any such constraints will be |
| 103348 | ** identified by the WO_EQUIV bit in the pTerm->eOperator field. The |
| 103349 | ** aEquiv[] array holds X and all its equivalents, with each SQL variable |
| 103350 | ** taking up two slots in aEquiv[]. The first slot is for the cursor number |
| 103351 | ** and the second is for the column number. There are 22 slots in aEquiv[] |
| 103352 | ** so that means we can look for X plus up to 10 other equivalent values. |
| 103353 | ** Hence a search for X will return <expr> if X=A1 and A1=A2 and A2=A3 |
| 103354 | ** and ... and A9=A10 and A10=<expr>. |
| 103355 | ** |
| 103356 | ** If there are multiple terms in the WHERE clause of the form "X <op> <expr>" |
| 103357 | ** then try for the one with no dependencies on <expr> - in other words where |
| 103358 | ** <expr> is a constant expression of some kind. Only return entries of |
| 103359 | ** the form "X <op> Y" where Y is a column in another table if no terms of |
| 103360 | ** the form "X <op> <const-expr>" exist. Other than this priority, if there |
| 103361 | ** are two or more terms that match, then the choice of which term to return |
| 103362 | ** is arbitrary. |
| 103363 | */ |
| 103364 | static WhereTerm *findTerm( |
| 103365 | WhereClause *pWC, /* The WHERE clause to be searched */ |
| 103366 | int iCur, /* Cursor number of LHS */ |
| 103367 | int iColumn, /* Column number of LHS */ |
| 103368 | Bitmask notReady, /* RHS must not overlap with this mask */ |
| 103369 | u32 op, /* Mask of WO_xx values describing operator */ |
| 103370 | Index *pIdx /* Must be compatible with this index, if not NULL */ |
| 103371 | ){ |
| 103372 | WhereTerm *pTerm; /* Term being examined as possible result */ |
| 103373 | WhereTerm *pResult = 0; /* The answer to return */ |
| 103374 | WhereClause *pWCOrig = pWC; /* Original pWC value */ |
| 103375 | int j, k; /* Loop counters */ |
| 103376 | Expr *pX; /* Pointer to an expression */ |
| 103377 | Parse *pParse; /* Parsing context */ |
| 103378 | int iOrigCol = iColumn; /* Original value of iColumn */ |
| 103379 | int nEquiv = 2; /* Number of entires in aEquiv[] */ |
| 103380 | int iEquiv = 2; /* Number of entries of aEquiv[] processed so far */ |
| 103381 | int aEquiv[22]; /* iCur,iColumn and up to 10 other equivalents */ |
| 103382 | |
| 103383 | assert( iCur>=0 ); |
| 103384 | aEquiv[0] = iCur; |
| 103385 | aEquiv[1] = iColumn; |
| 103386 | for(;;){ |
| 103387 | for(pWC=pWCOrig; pWC; pWC=pWC->pOuter){ |
| 103388 | for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ |
| 103389 | if( pTerm->leftCursor==iCur |
| 103390 | && pTerm->u.leftColumn==iColumn |
| 103391 | ){ |
| 103392 | if( (pTerm->prereqRight & notReady)==0 |
| 103393 | && (pTerm->eOperator & op & WO_ALL)!=0 |
| 103394 | ){ |
| 103395 | if( iOrigCol>=0 && pIdx && (pTerm->eOperator & WO_ISNULL)==0 ){ |
| 103396 | CollSeq *pColl; |
| 103397 | char idxaff; |
| 103398 | |
| 103399 | pX = pTerm->pExpr; |
| 103400 | pParse = pWC->pParse; |
| 103401 | idxaff = pIdx->pTable->aCol[iOrigCol].affinity; |
| 103402 | if( !sqlite3IndexAffinityOk(pX, idxaff) ){ |
| 103403 | continue; |
| 103404 | } |
| 103405 | |
| 103406 | /* Figure out the collation sequence required from an index for |
| 103407 | ** it to be useful for optimising expression pX. Store this |
| 103408 | ** value in variable pColl. |
| 103409 | */ |
| 103410 | assert(pX->pLeft); |
| 103411 | pColl = sqlite3BinaryCompareCollSeq(pParse,pX->pLeft,pX->pRight); |
| 103412 | if( pColl==0 ) pColl = pParse->db->pDfltColl; |
| 103413 | |
| 103414 | for(j=0; pIdx->aiColumn[j]!=iOrigCol; j++){ |
| 103415 | if( NEVER(j>=pIdx->nColumn) ) return 0; |
| 103416 | } |
| 103417 | if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ){ |
| 103418 | continue; |
| 103419 | } |
| 103420 | } |
| 103421 | pResult = pTerm; |
| 103422 | if( pTerm->prereqRight==0 ) goto findTerm_success; |
| 103423 | } |
| 103424 | if( (pTerm->eOperator & WO_EQUIV)!=0 |
| 103425 | && nEquiv<ArraySize(aEquiv) |
| 103426 | ){ |
| 103427 | pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight); |
| 103428 | assert( pX->op==TK_COLUMN ); |
| 103429 | for(j=0; j<nEquiv; j+=2){ |
| 103430 | if( aEquiv[j]==pX->iTable && aEquiv[j+1]==pX->iColumn ) break; |
| 103431 | } |
| 103432 | if( j==nEquiv ){ |
| 103433 | aEquiv[j] = pX->iTable; |
| 103434 | aEquiv[j+1] = pX->iColumn; |
| 103435 | nEquiv += 2; |
| 103436 | } |
| 103437 | } |
| 103438 | } |
| 103439 | } |
| 103440 | } |
| 103441 | if( iEquiv>=nEquiv ) break; |
| 103442 | iCur = aEquiv[iEquiv++]; |
| 103443 | iColumn = aEquiv[iEquiv++]; |
| 103444 | } |
| 103445 | findTerm_success: |
| 103446 | return pResult; |
| 103447 | } |
| 103448 | |
| 103449 | /* Forward reference */ |
| 103450 | static void exprAnalyze(SrcList*, WhereClause*, int); |
| 103451 | |
| @@ -103555,11 +103719,10 @@ | |
| 103719 | indexable = ~(Bitmask)0; |
| 103720 | chngToIN = ~(pWC->vmask); |
| 103721 | for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0 && indexable; i--, pOrTerm++){ |
| 103722 | if( (pOrTerm->eOperator & WO_SINGLE)==0 ){ |
| 103723 | WhereAndInfo *pAndInfo; |
| 103724 | assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 ); |
| 103725 | chngToIN = 0; |
| 103726 | pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo)); |
| 103727 | if( pAndInfo ){ |
| 103728 | WhereClause *pAndWC; |
| @@ -103594,11 +103757,11 @@ | |
| 103757 | if( pOrTerm->wtFlags & TERM_VIRTUAL ){ |
| 103758 | WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent]; |
| 103759 | b |= getMask(pMaskSet, pOther->leftCursor); |
| 103760 | } |
| 103761 | indexable &= b; |
| 103762 | if( (pOrTerm->eOperator & WO_EQ)==0 ){ |
| 103763 | chngToIN = 0; |
| 103764 | }else{ |
| 103765 | chngToIN &= b; |
| 103766 | } |
| 103767 | } |
| @@ -103645,11 +103808,11 @@ | |
| 103808 | ** and column is found but leave okToChngToIN false if not found. |
| 103809 | */ |
| 103810 | for(j=0; j<2 && !okToChngToIN; j++){ |
| 103811 | pOrTerm = pOrWc->a; |
| 103812 | for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){ |
| 103813 | assert( pOrTerm->eOperator & WO_EQ ); |
| 103814 | pOrTerm->wtFlags &= ~TERM_OR_OK; |
| 103815 | if( pOrTerm->leftCursor==iCursor ){ |
| 103816 | /* This is the 2-bit case and we are on the second iteration and |
| 103817 | ** current term is from the first iteration. So skip this term. */ |
| 103818 | assert( j==1 ); |
| @@ -103671,21 +103834,21 @@ | |
| 103834 | } |
| 103835 | if( i<0 ){ |
| 103836 | /* No candidate table+column was found. This can only occur |
| 103837 | ** on the second iteration */ |
| 103838 | assert( j==1 ); |
| 103839 | assert( IsPowerOfTwo(chngToIN) ); |
| 103840 | assert( chngToIN==getMask(pMaskSet, iCursor) ); |
| 103841 | break; |
| 103842 | } |
| 103843 | testcase( j==1 ); |
| 103844 | |
| 103845 | /* We have found a candidate table and column. Check to see if that |
| 103846 | ** table and column is common to every term in the OR clause */ |
| 103847 | okToChngToIN = 1; |
| 103848 | for(; i>=0 && okToChngToIN; i--, pOrTerm++){ |
| 103849 | assert( pOrTerm->eOperator & WO_EQ ); |
| 103850 | if( pOrTerm->leftCursor!=iCursor ){ |
| 103851 | pOrTerm->wtFlags &= ~TERM_OR_OK; |
| 103852 | }else if( pOrTerm->u.leftColumn!=iColumn ){ |
| 103853 | okToChngToIN = 0; |
| 103854 | }else{ |
| @@ -103717,11 +103880,11 @@ | |
| 103880 | Expr *pLeft = 0; /* The LHS of the IN operator */ |
| 103881 | Expr *pNew; /* The complete IN operator */ |
| 103882 | |
| 103883 | for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){ |
| 103884 | if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue; |
| 103885 | assert( pOrTerm->eOperator & WO_EQ ); |
| 103886 | assert( pOrTerm->leftCursor==iCursor ); |
| 103887 | assert( pOrTerm->u.leftColumn==iColumn ); |
| 103888 | pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); |
| 103889 | pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup); |
| 103890 | pLeft = pOrTerm->pExpr->pLeft; |
| @@ -103746,11 +103909,10 @@ | |
| 103909 | pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */ |
| 103910 | } |
| 103911 | } |
| 103912 | } |
| 103913 | #endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */ |
| 103914 | |
| 103915 | /* |
| 103916 | ** The input to this routine is an WhereTerm structure with only the |
| 103917 | ** "pExpr" field filled in. The job of this routine is to analyze the |
| 103918 | ** subexpression and populate all the other fields of the WhereTerm |
| @@ -103816,21 +103978,23 @@ | |
| 103978 | } |
| 103979 | pTerm->prereqAll = prereqAll; |
| 103980 | pTerm->leftCursor = -1; |
| 103981 | pTerm->iParent = -1; |
| 103982 | pTerm->eOperator = 0; |
| 103983 | if( allowedOp(op) ){ |
| 103984 | Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft); |
| 103985 | Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight); |
| 103986 | u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV; |
| 103987 | if( pLeft->op==TK_COLUMN ){ |
| 103988 | pTerm->leftCursor = pLeft->iTable; |
| 103989 | pTerm->u.leftColumn = pLeft->iColumn; |
| 103990 | pTerm->eOperator = operatorMask(op) & opMask; |
| 103991 | } |
| 103992 | if( pRight && pRight->op==TK_COLUMN ){ |
| 103993 | WhereTerm *pNew; |
| 103994 | Expr *pDup; |
| 103995 | u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */ |
| 103996 | if( pTerm->leftCursor>=0 ){ |
| 103997 | int idxNew; |
| 103998 | pDup = sqlite3ExprDup(db, pExpr, 0); |
| 103999 | if( db->mallocFailed ){ |
| 104000 | sqlite3ExprDelete(db, pDup); |
| @@ -103841,10 +104005,17 @@ | |
| 104005 | pNew = &pWC->a[idxNew]; |
| 104006 | pNew->iParent = idxTerm; |
| 104007 | pTerm = &pWC->a[idxTerm]; |
| 104008 | pTerm->nChild = 1; |
| 104009 | pTerm->wtFlags |= TERM_COPIED; |
| 104010 | if( pExpr->op==TK_EQ |
| 104011 | && !ExprHasProperty(pExpr, EP_FromJoin) |
| 104012 | && OptimizationEnabled(db, SQLITE_Transitive) |
| 104013 | ){ |
| 104014 | pTerm->eOperator |= WO_EQUIV; |
| 104015 | eExtraOp = WO_EQUIV; |
| 104016 | } |
| 104017 | }else{ |
| 104018 | pDup = pExpr; |
| 104019 | pNew = pTerm; |
| 104020 | } |
| 104021 | exprCommute(pParse, pDup); |
| @@ -103852,11 +104023,11 @@ | |
| 104023 | pNew->leftCursor = pLeft->iTable; |
| 104024 | pNew->u.leftColumn = pLeft->iColumn; |
| 104025 | testcase( (prereqLeft | extraRight) != prereqLeft ); |
| 104026 | pNew->prereqRight = prereqLeft | extraRight; |
| 104027 | pNew->prereqAll = prereqAll; |
| 104028 | pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask; |
| 104029 | } |
| 104030 | } |
| 104031 | |
| 104032 | #ifndef SQLITE_OMIT_BETWEEN_OPTIMIZATION |
| 104033 | /* If a term is the BETWEEN operator, create two new virtual terms |
| @@ -104311,11 +104482,11 @@ | |
| 104482 | return; |
| 104483 | } |
| 104484 | |
| 104485 | /* Search the WHERE clause terms for a usable WO_OR term. */ |
| 104486 | for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ |
| 104487 | if( (pTerm->eOperator & WO_OR)!=0 |
| 104488 | && ((pTerm->prereqAll & ~maskSrc) & p->notReady)==0 |
| 104489 | && (pTerm->u.pOrInfo->indexable & maskSrc)!=0 |
| 104490 | ){ |
| 104491 | WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc; |
| 104492 | WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm]; |
| @@ -104332,11 +104503,11 @@ | |
| 104503 | sBOI.ppIdxInfo = 0; |
| 104504 | for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){ |
| 104505 | WHERETRACE(("... Multi-index OR testing for term %d of %d....\n", |
| 104506 | (pOrTerm - pOrWC->a), (pTerm - pWC->a) |
| 104507 | )); |
| 104508 | if( (pOrTerm->eOperator& WO_AND)!=0 ){ |
| 104509 | sBOI.pWC = &pOrTerm->u.pAndInfo->wc; |
| 104510 | bestIndex(&sBOI); |
| 104511 | }else if( pOrTerm->leftCursor==iCur ){ |
| 104512 | WhereClause tempWC; |
| 104513 | tempWC.pParse = pWC->pParse; |
| @@ -104393,11 +104564,11 @@ | |
| 104564 | struct SrcList_item *pSrc, /* Table we are trying to access */ |
| 104565 | Bitmask notReady /* Tables in outer loops of the join */ |
| 104566 | ){ |
| 104567 | char aff; |
| 104568 | if( pTerm->leftCursor!=pSrc->iCursor ) return 0; |
| 104569 | if( (pTerm->eOperator & WO_EQ)==0 ) return 0; |
| 104570 | if( (pTerm->prereqRight & notReady)!=0 ) return 0; |
| 104571 | aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; |
| 104572 | if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; |
| 104573 | return 1; |
| 104574 | } |
| @@ -104655,13 +104826,13 @@ | |
| 104826 | |
| 104827 | /* Count the number of possible WHERE clause constraints referring |
| 104828 | ** to this virtual table */ |
| 104829 | for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 104830 | if( pTerm->leftCursor != pSrc->iCursor ) continue; |
| 104831 | assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); |
| 104832 | testcase( pTerm->eOperator & WO_IN ); |
| 104833 | testcase( pTerm->eOperator & WO_ISNULL ); |
| 104834 | if( pTerm->eOperator & (WO_ISNULL) ) continue; |
| 104835 | if( pTerm->wtFlags & TERM_VNULL ) continue; |
| 104836 | nTerm++; |
| 104837 | } |
| 104838 | |
| @@ -104708,18 +104879,18 @@ | |
| 104879 | pUsage; |
| 104880 | |
| 104881 | for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ |
| 104882 | u8 op; |
| 104883 | if( pTerm->leftCursor != pSrc->iCursor ) continue; |
| 104884 | assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); |
| 104885 | testcase( pTerm->eOperator & WO_IN ); |
| 104886 | testcase( pTerm->eOperator & WO_ISNULL ); |
| 104887 | if( pTerm->eOperator & (WO_ISNULL) ) continue; |
| 104888 | if( pTerm->wtFlags & TERM_VNULL ) continue; |
| 104889 | pIdxCons[j].iColumn = pTerm->u.leftColumn; |
| 104890 | pIdxCons[j].iTermOffset = i; |
| 104891 | op = (u8)pTerm->eOperator & WO_ALL; |
| 104892 | if( op==WO_IN ) op = WO_EQ; |
| 104893 | pIdxCons[j].op = op; |
| 104894 | /* The direct assignment in the previous line is possible only because |
| 104895 | ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The |
| 104896 | ** following asserts verify this fact. */ |
| @@ -104885,11 +105056,11 @@ | |
| 105056 | pUsage = pIdxInfo->aConstraintUsage; |
| 105057 | for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ |
| 105058 | j = pIdxCons->iTermOffset; |
| 105059 | pTerm = &pWC->a[j]; |
| 105060 | if( (pTerm->prereqRight&p->notReady)==0 |
| 105061 | && (bAllowIN || (pTerm->eOperator & WO_IN)==0) |
| 105062 | ){ |
| 105063 | pIdxCons->usable = 1; |
| 105064 | }else{ |
| 105065 | pIdxCons->usable = 0; |
| 105066 | } |
| @@ -104917,11 +105088,11 @@ | |
| 105088 | for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ |
| 105089 | if( pUsage[i].argvIndex>0 ){ |
| 105090 | j = pIdxCons->iTermOffset; |
| 105091 | pTerm = &pWC->a[j]; |
| 105092 | p->cost.used |= pTerm->prereqRight; |
| 105093 | if( (pTerm->eOperator & WO_IN)!=0 && pUsage[i].omit==0 ){ |
| 105094 | /* Do not attempt to use an IN constraint if the virtual table |
| 105095 | ** says that the equivalent EQ constraint cannot be safely omitted. |
| 105096 | ** If we do attempt to use such a constraint, some rows might be |
| 105097 | ** repeated in the output. */ |
| 105098 | break; |
| @@ -105223,28 +105394,28 @@ | |
| 105394 | u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; |
| 105395 | |
| 105396 | if( pLower ){ |
| 105397 | Expr *pExpr = pLower->pExpr->pRight; |
| 105398 | rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); |
| 105399 | assert( (pLower->eOperator & (WO_GT|WO_GE))!=0 ); |
| 105400 | if( rc==SQLITE_OK |
| 105401 | && whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK |
| 105402 | ){ |
| 105403 | iLower = a[0]; |
| 105404 | if( (pLower->eOperator & WO_GT)!=0 ) iLower += a[1]; |
| 105405 | } |
| 105406 | sqlite3ValueFree(pRangeVal); |
| 105407 | } |
| 105408 | if( rc==SQLITE_OK && pUpper ){ |
| 105409 | Expr *pExpr = pUpper->pExpr->pRight; |
| 105410 | rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); |
| 105411 | assert( (pUpper->eOperator & (WO_LT|WO_LE))!=0 ); |
| 105412 | if( rc==SQLITE_OK |
| 105413 | && whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK |
| 105414 | ){ |
| 105415 | iUpper = a[0]; |
| 105416 | if( (pUpper->eOperator & WO_LE)!=0 ) iUpper += a[1]; |
| 105417 | } |
| 105418 | sqlite3ValueFree(pRangeVal); |
| 105419 | } |
| 105420 | if( rc==SQLITE_OK ){ |
| 105421 | if( iUpper<=iLower ){ |
| @@ -105548,16 +105719,16 @@ | |
| 105719 | ** if there are any X= or X IS NULL constraints in the WHERE clause. */ |
| 105720 | pConstraint = findTerm(p->pWC, base, iColumn, p->notReady, |
| 105721 | WO_EQ|WO_ISNULL|WO_IN, pIdx); |
| 105722 | if( pConstraint==0 ){ |
| 105723 | isEq = 0; |
| 105724 | }else if( (pConstraint->eOperator & WO_IN)!=0 ){ |
| 105725 | /* Constraints of the form: "X IN ..." cannot be used with an ORDER BY |
| 105726 | ** because we do not know in what order the values on the RHS of the IN |
| 105727 | ** operator will occur. */ |
| 105728 | break; |
| 105729 | }else if( (pConstraint->eOperator & WO_ISNULL)!=0 ){ |
| 105730 | uniqueNotNull = 0; |
| 105731 | isEq = 1; /* "X IS NULL" means X has only a single value */ |
| 105732 | }else if( pConstraint->prereqRight==0 ){ |
| 105733 | isEq = 1; /* Constraint "X=constant" means X has only a single value */ |
| 105734 | }else{ |
| @@ -105903,11 +106074,11 @@ | |
| 106074 | int bRev = 2; |
| 106075 | WHERETRACE((" --> before isSortingIndex: nPriorSat=%d\n",nPriorSat)); |
| 106076 | pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev); |
| 106077 | WHERETRACE((" --> after isSortingIndex: bRev=%d nOBSat=%d\n", |
| 106078 | bRev, pc.plan.nOBSat)); |
| 106079 | if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){ |
| 106080 | pc.plan.wsFlags |= WHERE_ORDERED; |
| 106081 | } |
| 106082 | if( nOrderBy==pc.plan.nOBSat ){ |
| 106083 | bSort = 0; |
| 106084 | pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE; |
| @@ -105966,16 +106137,17 @@ | |
| 106137 | */ |
| 106138 | if( pc.plan.nRow>(double)1 && pc.plan.nEq==1 |
| 106139 | && pFirstTerm!=0 && aiRowEst[1]>1 ){ |
| 106140 | assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 ); |
| 106141 | if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){ |
| 106142 | testcase( pFirstTerm->eOperator & WO_EQ ); |
| 106143 | testcase( pFirstTerm->eOperator & WO_EQUIV ); |
| 106144 | testcase( pFirstTerm->eOperator & WO_ISNULL ); |
| 106145 | whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, |
| 106146 | &pc.plan.nRow); |
| 106147 | }else if( bInEst==0 ){ |
| 106148 | assert( pFirstTerm->eOperator & WO_IN ); |
| 106149 | whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, |
| 106150 | &pc.plan.nRow); |
| 106151 | } |
| 106152 | } |
| 106153 | #endif /* SQLITE_ENABLE_STAT3 */ |
| @@ -106118,11 +106290,11 @@ | |
| 106290 | ** more selective intentionally because of the subjective |
| 106291 | ** observation that indexed range constraints really are more |
| 106292 | ** selective in practice, on average. */ |
| 106293 | pc.plan.nRow /= 3; |
| 106294 | } |
| 106295 | }else if( (pTerm->eOperator & WO_NOOP)==0 ){ |
| 106296 | /* Any other expression lowers the output row count by half */ |
| 106297 | pc.plan.nRow /= 2; |
| 106298 | } |
| 106299 | } |
| 106300 | if( pc.plan.nRow<2 ) pc.plan.nRow = 2; |
| @@ -106170,12 +106342,13 @@ | |
| 106342 | assert( pSrc->pIndex==0 |
| 106343 | || p->cost.plan.u.pIdx==0 |
| 106344 | || p->cost.plan.u.pIdx==pSrc->pIndex |
| 106345 | ); |
| 106346 | |
| 106347 | WHERETRACE((" best index is %s cost=%.1f\n", |
| 106348 | p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk", |
| 106349 | p->cost.rCost)); |
| 106350 | |
| 106351 | bestOrClauseIndex(p); |
| 106352 | bestAutomaticIndex(p); |
| 106353 | p->cost.plan.wsFlags |= eqTermMask; |
| 106354 | } |
| @@ -106753,11 +106926,10 @@ | |
| 106926 | */ |
| 106927 | iReleaseReg = sqlite3GetTempReg(pParse); |
| 106928 | pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0); |
| 106929 | assert( pTerm!=0 ); |
| 106930 | assert( pTerm->pExpr!=0 ); |
| 106931 | assert( omitTable==0 ); |
| 106932 | testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ |
| 106933 | iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, iReleaseReg); |
| 106934 | addrNxt = pLevel->addrNxt; |
| 106935 | sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); |
| @@ -107144,11 +107316,11 @@ | |
| 107316 | int ii; /* Loop counter */ |
| 107317 | Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ |
| 107318 | |
| 107319 | pTerm = pLevel->plan.u.pTerm; |
| 107320 | assert( pTerm!=0 ); |
| 107321 | assert( pTerm->eOperator & WO_OR ); |
| 107322 | assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); |
| 107323 | pOrWc = &pTerm->u.pOrInfo->wc; |
| 107324 | pLevel->op = OP_Return; |
| 107325 | pLevel->p1 = regReturn; |
| 107326 | |
| @@ -107217,11 +107389,11 @@ | |
| 107389 | } |
| 107390 | } |
| 107391 | |
| 107392 | for(ii=0; ii<pOrWc->nTerm; ii++){ |
| 107393 | WhereTerm *pOrTerm = &pOrWc->a[ii]; |
| 107394 | if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){ |
| 107395 | WhereInfo *pSubWInfo; /* Info for single OR-term scan */ |
| 107396 | Expr *pOrExpr = pOrTerm->pExpr; |
| 107397 | if( pAndExpr ){ |
| 107398 | pAndExpr->pLeft = pOrExpr; |
| 107399 | pOrExpr = pAndExpr; |
| @@ -107672,10 +107844,11 @@ | |
| 107844 | Index *pIdx; /* Index for FROM table at pTabItem */ |
| 107845 | int j; /* For looping over FROM tables */ |
| 107846 | int bestJ = -1; /* The value of j */ |
| 107847 | Bitmask m; /* Bitmask value for j or bestJ */ |
| 107848 | int isOptimal; /* Iterator for optimal/non-optimal search */ |
| 107849 | int ckOptimal; /* Do the optimal scan check */ |
| 107850 | int nUnconstrained; /* Number tables without INDEXED BY */ |
| 107851 | Bitmask notIndexed; /* Mask of tables that cannot use an index */ |
| 107852 | |
| 107853 | memset(&bestPlan, 0, sizeof(bestPlan)); |
| 107854 | bestPlan.rCost = SQLITE_BIG_DBL; |
| @@ -107706,14 +107879,12 @@ | |
| 107879 | ** |
| 107880 | ** The second loop iteration is only performed if no optimal scan |
| 107881 | ** strategies were found by the first iteration. This second iteration |
| 107882 | ** is used to search for the lowest cost scan overall. |
| 107883 | ** |
| 107884 | ** Without the optimal scan step (the first iteration) a suboptimal |
| 107885 | ** plan might be chosen for queries like this: |
| 107886 | ** |
| 107887 | ** CREATE TABLE t1(a, b); |
| 107888 | ** CREATE TABLE t2(c, d); |
| 107889 | ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a; |
| 107890 | ** |
| @@ -107724,20 +107895,44 @@ | |
| 107895 | ** algorithm may choose to use t2 for the outer loop, which is a much |
| 107896 | ** costlier approach. |
| 107897 | */ |
| 107898 | nUnconstrained = 0; |
| 107899 | notIndexed = 0; |
| 107900 | |
| 107901 | /* The optimal scan check only occurs if there are two or more tables |
| 107902 | ** available to be reordered */ |
| 107903 | if( iFrom==nTabList-1 ){ |
| 107904 | ckOptimal = 0; /* Common case of just one table in the FROM clause */ |
| 107905 | }else{ |
| 107906 | ckOptimal = -1; |
| 107907 | for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){ |
| 107908 | m = getMask(pMaskSet, sWBI.pSrc->iCursor); |
| 107909 | if( (m & sWBI.notValid)==0 ){ |
| 107910 | if( j==iFrom ) iFrom++; |
| 107911 | continue; |
| 107912 | } |
| 107913 | if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ) break; |
| 107914 | if( ++ckOptimal ) break; |
| 107915 | if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break; |
| 107916 | } |
| 107917 | } |
| 107918 | assert( ckOptimal==0 || ckOptimal==1 ); |
| 107919 | |
| 107920 | for(isOptimal=ckOptimal; isOptimal>=0 && bestJ<0; isOptimal--){ |
| 107921 | for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){ |
| 107922 | if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ){ |
| 107923 | /* This break and one like it in the ckOptimal computation loop |
| 107924 | ** above prevent table reordering across LEFT and CROSS JOINs. |
| 107925 | ** The LEFT JOIN case is necessary for correctness. The prohibition |
| 107926 | ** against reordering across a CROSS JOIN is an SQLite feature that |
| 107927 | ** allows the developer to control table reordering */ |
| 107928 | break; |
| 107929 | } |
| 107930 | m = getMask(pMaskSet, sWBI.pSrc->iCursor); |
| 107931 | if( (m & sWBI.notValid)==0 ){ |
| 107932 | assert( j>iFrom ); |
| 107933 | continue; |
| 107934 | } |
| 107935 | sWBI.notReady = (isOptimal ? m : sWBI.notValid); |
| 107936 | if( sWBI.pSrc->pIndex==0 ) nUnconstrained++; |
| 107937 | |
| 107938 | WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n", |
| @@ -107763,12 +107958,12 @@ | |
| 107958 | if( isOptimal && (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){ |
| 107959 | notIndexed |= m; |
| 107960 | } |
| 107961 | if( isOptimal ){ |
| 107962 | pWInfo->a[j].rOptCost = sWBI.cost.rCost; |
| 107963 | }else if( ckOptimal ){ |
| 107964 | /* If two or more tables have nearly the same outer loop cost, but |
| 107965 | ** very different inner loop (optimal) cost, we want to choose |
| 107966 | ** for the outer loop that table which benefits the least from |
| 107967 | ** being in the inner loop. The following code scales the |
| 107968 | ** outer loop cost estimate to accomplish that. */ |
| 107969 | WHERETRACE((" scaling cost from %.1f to %.1f\n", |
| @@ -107809,15 +108004,23 @@ | |
| 108004 | sWBI.cost.rCost, sWBI.cost.plan.nRow, |
| 108005 | sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags)); |
| 108006 | bestPlan = sWBI.cost; |
| 108007 | bestJ = j; |
| 108008 | } |
| 108009 | |
| 108010 | /* In a join like "w JOIN x LEFT JOIN y JOIN z" make sure that |
| 108011 | ** table y (and not table z) is always the next inner loop inside |
| 108012 | ** of table x. */ |
| 108013 | if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break; |
| 108014 | } |
| 108015 | } |
| 108016 | assert( bestJ>=0 ); |
| 108017 | assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); |
| 108018 | assert( bestJ==iFrom || (pTabList->a[iFrom].jointype & JT_LEFT)==0 ); |
| 108019 | testcase( bestJ>iFrom && (pTabList->a[iFrom].jointype & JT_CROSS)!=0 ); |
| 108020 | testcase( bestJ>iFrom && bestJ<nTabList-1 |
| 108021 | && (pTabList->a[bestJ+1].jointype & JT_LEFT)!=0 ); |
| 108022 | WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n" |
| 108023 | " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n", |
| 108024 | bestJ, pTabList->a[bestJ].pTab->zName, |
| 108025 | pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow, |
| 108026 | bestPlan.plan.nOBSat, bestPlan.plan.wsFlags)); |
| @@ -136543,11 +136746,12 @@ | |
| 136746 | ** would fit in a single node, use a smaller node-size. |
| 136747 | */ |
| 136748 | static int getNodeSize( |
| 136749 | sqlite3 *db, /* Database handle */ |
| 136750 | Rtree *pRtree, /* Rtree handle */ |
| 136751 | int isCreate, /* True for xCreate, false for xConnect */ |
| 136752 | char **pzErr /* OUT: Error message, if any */ |
| 136753 | ){ |
| 136754 | int rc; |
| 136755 | char *zSql; |
| 136756 | if( isCreate ){ |
| 136757 | int iPageSize = 0; |
| @@ -136556,17 +136760,22 @@ | |
| 136760 | if( rc==SQLITE_OK ){ |
| 136761 | pRtree->iNodeSize = iPageSize-64; |
| 136762 | if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){ |
| 136763 | pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS; |
| 136764 | } |
| 136765 | }else{ |
| 136766 | *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); |
| 136767 | } |
| 136768 | }else{ |
| 136769 | zSql = sqlite3_mprintf( |
| 136770 | "SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1", |
| 136771 | pRtree->zDb, pRtree->zName |
| 136772 | ); |
| 136773 | rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize); |
| 136774 | if( rc!=SQLITE_OK ){ |
| 136775 | *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); |
| 136776 | } |
| 136777 | } |
| 136778 | |
| 136779 | sqlite3_free(zSql); |
| 136780 | return rc; |
| 136781 | } |
| @@ -136626,11 +136835,11 @@ | |
| 136835 | pRtree->eCoordType = eCoordType; |
| 136836 | memcpy(pRtree->zDb, argv[1], nDb); |
| 136837 | memcpy(pRtree->zName, argv[2], nName); |
| 136838 | |
| 136839 | /* Figure out the node size to use. */ |
| 136840 | rc = getNodeSize(db, pRtree, isCreate, pzErr); |
| 136841 | |
| 136842 | /* Create/Connect to the underlying relational database schema. If |
| 136843 | ** that is successful, call sqlite3_declare_vtab() to configure |
| 136844 | ** the r-tree table schema. |
| 136845 | */ |
| 136846 |
+1
-1
| --- src/sqlite3.h | ||
| +++ src/sqlite3.h | ||
| @@ -107,11 +107,11 @@ | ||
| 107 | 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | 109 | */ |
| 110 | 110 | #define SQLITE_VERSION "3.7.16" |
| 111 | 111 | #define SQLITE_VERSION_NUMBER 3007016 |
| 112 | -#define SQLITE_SOURCE_ID "2012-12-21 16:15:35 ff6857b6ed6a46671006b75157d8cf853a816ef9" | |
| 112 | +#define SQLITE_SOURCE_ID "2013-01-17 17:20:49 38852f158ab20bb4d7b264af987ec1538052bec3" | |
| 113 | 113 | |
| 114 | 114 | /* |
| 115 | 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | 117 | ** |
| 118 | 118 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -107,11 +107,11 @@ | |
| 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | */ |
| 110 | #define SQLITE_VERSION "3.7.16" |
| 111 | #define SQLITE_VERSION_NUMBER 3007016 |
| 112 | #define SQLITE_SOURCE_ID "2012-12-21 16:15:35 ff6857b6ed6a46671006b75157d8cf853a816ef9" |
| 113 | |
| 114 | /* |
| 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | ** |
| 118 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -107,11 +107,11 @@ | |
| 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | */ |
| 110 | #define SQLITE_VERSION "3.7.16" |
| 111 | #define SQLITE_VERSION_NUMBER 3007016 |
| 112 | #define SQLITE_SOURCE_ID "2013-01-17 17:20:49 38852f158ab20bb4d7b264af987ec1538052bec3" |
| 113 | |
| 114 | /* |
| 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | ** |
| 118 |
+1
-1
| --- src/sqlite3.h | ||
| +++ src/sqlite3.h | ||
| @@ -107,11 +107,11 @@ | ||
| 107 | 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | 109 | */ |
| 110 | 110 | #define SQLITE_VERSION "3.7.16" |
| 111 | 111 | #define SQLITE_VERSION_NUMBER 3007016 |
| 112 | -#define SQLITE_SOURCE_ID "2012-12-21 16:15:35 ff6857b6ed6a46671006b75157d8cf853a816ef9" | |
| 112 | +#define SQLITE_SOURCE_ID "2013-01-17 17:20:49 38852f158ab20bb4d7b264af987ec1538052bec3" | |
| 113 | 113 | |
| 114 | 114 | /* |
| 115 | 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | 117 | ** |
| 118 | 118 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -107,11 +107,11 @@ | |
| 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | */ |
| 110 | #define SQLITE_VERSION "3.7.16" |
| 111 | #define SQLITE_VERSION_NUMBER 3007016 |
| 112 | #define SQLITE_SOURCE_ID "2012-12-21 16:15:35 ff6857b6ed6a46671006b75157d8cf853a816ef9" |
| 113 | |
| 114 | /* |
| 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | ** |
| 118 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -107,11 +107,11 @@ | |
| 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | */ |
| 110 | #define SQLITE_VERSION "3.7.16" |
| 111 | #define SQLITE_VERSION_NUMBER 3007016 |
| 112 | #define SQLITE_SOURCE_ID "2013-01-17 17:20:49 38852f158ab20bb4d7b264af987ec1538052bec3" |
| 113 | |
| 114 | /* |
| 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | ** |
| 118 |
+104
-6
| --- src/stat.c | ||
| +++ src/stat.c | ||
| @@ -66,11 +66,11 @@ | ||
| 66 | 66 | @ </td></tr> |
| 67 | 67 | if( !brief ){ |
| 68 | 68 | @ <tr><th>Number Of Artifacts:</th><td> |
| 69 | 69 | n = db_int(0, "SELECT count(*) FROM blob"); |
| 70 | 70 | m = db_int(0, "SELECT count(*) FROM delta"); |
| 71 | - @ %d(n) (stored as %d(n-m) full text and %d(m) delta blobs) | |
| 71 | + @ %d(n) (%d(n-m) fulltext and %d(m) deltas) | |
| 72 | 72 | @ </td></tr> |
| 73 | 73 | if( n>0 ){ |
| 74 | 74 | int a, b; |
| 75 | 75 | Stmt q; |
| 76 | 76 | @ <tr><th>Uncompressed Artifact Size:</th><td> |
| @@ -94,11 +94,11 @@ | ||
| 94 | 94 | a = t/fsize; |
| 95 | 95 | @ %d(a):%d(b) |
| 96 | 96 | @ </td></tr> |
| 97 | 97 | } |
| 98 | 98 | @ <tr><th>Number Of Check-ins:</th><td> |
| 99 | - n = db_int(0, "SELECT count(distinct mid) FROM mlink /*scan*/"); | |
| 99 | + n = db_int(0, "SELECT count(*) FROM event WHERE type='ci' /*scan*/"); | |
| 100 | 100 | @ %d(n) |
| 101 | 101 | @ </td></tr> |
| 102 | 102 | @ <tr><th>Number Of Files:</th><td> |
| 103 | 103 | n = db_int(0, "SELECT count(*) FROM filename /*scan*/"); |
| 104 | 104 | @ %d(n) |
| @@ -115,17 +115,16 @@ | ||
| 115 | 115 | @ </td></tr> |
| 116 | 116 | } |
| 117 | 117 | @ <tr><th>Duration Of Project:</th><td> |
| 118 | 118 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 119 | 119 | " + 0.99"); |
| 120 | - @ %d(n) days or approximately %.2f(n/365.24) years. | |
| 120 | + @ %d(n) days or approximately %.2f(n/365.2425) years. | |
| 121 | 121 | @ </td></tr> |
| 122 | 122 | @ <tr><th>Project ID:</th><td>%h(db_get("project-code",""))</td></tr> |
| 123 | - @ <tr><th>Server ID:</th><td>%h(db_get("server-code",""))</td></tr> | |
| 124 | 123 | @ <tr><th>Fossil Version:</th><td> |
| 125 | - @ %h(RELEASE_VERSION) %h(MANIFEST_DATE) %h(MANIFEST_VERSION) | |
| 126 | - @ (%h(COMPILER_NAME)) | |
| 124 | + @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION) | |
| 125 | + @ (%h(RELEASE_VERSION)) [compiled using %h(COMPILER_NAME)] | |
| 127 | 126 | @ </td></tr> |
| 128 | 127 | @ <tr><th>SQLite Version:</th><td>%.19s(SQLITE_SOURCE_ID) |
| 129 | 128 | @ [%.10s(&SQLITE_SOURCE_ID[20])] (%s(SQLITE_VERSION))</td></tr> |
| 130 | 129 | @ <tr><th>Database Stats:</th><td> |
| 131 | 130 | zDb = db_name("repository"); |
| @@ -137,10 +136,109 @@ | ||
| 137 | 136 | @ </td></tr> |
| 138 | 137 | |
| 139 | 138 | @ </table> |
| 140 | 139 | style_footer(); |
| 141 | 140 | } |
| 141 | + | |
| 142 | +/* | |
| 143 | +** COMMAND: dbstat* | |
| 144 | +** | |
| 145 | +** Usage: %fossil dbstat ?-brief | -b? | |
| 146 | +** | |
| 147 | +** Shows statistics and global information about the repository. | |
| 148 | +** | |
| 149 | +** The (-brief|-b) option removes any "long-running" statistics, namely | |
| 150 | +** those whose calculations are known to slow down as the repository | |
| 151 | +** grows. | |
| 152 | +** | |
| 153 | +*/ | |
| 154 | +void dbstat_cmd(void){ | |
| 155 | + i64 t, fsize; | |
| 156 | + int n, m; | |
| 157 | + int szMax, szAvg; | |
| 158 | + const char *zDb; | |
| 159 | + int brief; | |
| 160 | + char zBuf[100]; | |
| 161 | + const int colWidth = -20 /* printf alignment/width for left column */; | |
| 162 | + brief = find_option("brief", "b",0)!=0; | |
| 163 | + db_find_and_open_repository(0,0); | |
| 164 | + fsize = file_size(g.zRepositoryName); | |
| 165 | + bigSizeName(sizeof(zBuf), zBuf, fsize); | |
| 166 | + fossil_print( "%*s%s\n", colWidth, "repository-size:", zBuf ); | |
| 167 | + if( !brief ){ | |
| 168 | + n = db_int(0, "SELECT count(*) FROM blob"); | |
| 169 | + m = db_int(0, "SELECT count(*) FROM delta"); | |
| 170 | + fossil_print("%*s%d (stored as %d full text and %d delta blobs)\n", | |
| 171 | + colWidth, "artifact-count:", | |
| 172 | + n, n-m, m); | |
| 173 | + if( n>0 ){ | |
| 174 | + int a, b; | |
| 175 | + Stmt q; | |
| 176 | + db_prepare(&q, "SELECT total(size), avg(size), max(size)" | |
| 177 | + " FROM blob WHERE size>0"); | |
| 178 | + db_step(&q); | |
| 179 | + t = db_column_int64(&q, 0); | |
| 180 | + szAvg = db_column_int(&q, 1); | |
| 181 | + szMax = db_column_int(&q, 2); | |
| 182 | + db_finalize(&q); | |
| 183 | + bigSizeName(sizeof(zBuf), zBuf, t); | |
| 184 | + fossil_print( "%*s%d bytes average, " | |
| 185 | + "%d bytes max, %s total\n", | |
| 186 | + colWidth, "artifact-sizes:", | |
| 187 | + szAvg, szMax, zBuf); | |
| 188 | + if( t/fsize < 5 ){ | |
| 189 | + b = 10; | |
| 190 | + fsize /= 10; | |
| 191 | + }else{ | |
| 192 | + b = 1; | |
| 193 | + } | |
| 194 | + a = t/fsize; | |
| 195 | + fossil_print("%*s%d:%d\n", colWidth, "compression-ratio:", a, b); | |
| 196 | + } | |
| 197 | + n = db_int(0, "SELECT COUNT(*) FROM event e WHERE e.type='ci'"); | |
| 198 | + fossil_print("%*s%d\n", colWidth, "checkins:", n); | |
| 199 | + n = db_int(0, "SELECT count(*) FROM filename /*scan*/"); | |
| 200 | + fossil_print("%*s%d across all branches\n", colWidth, "files:", n); | |
| 201 | + n = db_int(0, "SELECT count(*) FROM tag /*scan*/" | |
| 202 | + " WHERE tagname GLOB 'wiki-*'"); | |
| 203 | + m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='w'"); | |
| 204 | + fossil_print("%*s%d (%d changes)\n", colWidth, "wikipages:", n, m); | |
| 205 | + n = db_int(0, "SELECT count(*) FROM tag /*scan*/" | |
| 206 | + " WHERE tagname GLOB 'tkt-*'"); | |
| 207 | + m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='t'"); | |
| 208 | + fossil_print("%*s%d (%d changes)\n", colWidth, "tickets:", n, m); | |
| 209 | + n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='e'"); | |
| 210 | + fossil_print("%*s%d\n", colWidth, "events:", n); | |
| 211 | + n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='g'"); | |
| 212 | + fossil_print("%*s%d\n", colWidth, "tagchanges:", n); | |
| 213 | + } | |
| 214 | + n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" | |
| 215 | + " + 0.99"); | |
| 216 | + fossil_print("%*s%d days or approximately %.2f years.\n", | |
| 217 | + colWidth, "project-age:", n, n/365.2425); | |
| 218 | + fossil_print("%*s%s\n", colWidth, "project-id:", db_get("project-code","")); | |
| 219 | + fossil_print("%*s%s %s %s (%s)\n", | |
| 220 | + colWidth, "fossil-version:", | |
| 221 | + RELEASE_VERSION, MANIFEST_DATE, MANIFEST_VERSION, | |
| 222 | + COMPILER_NAME); | |
| 223 | + fossil_print("%*s%.19s [%.10s] (%s)\n", | |
| 224 | + colWidth, "sqlite-version:", | |
| 225 | + SQLITE_SOURCE_ID, &SQLITE_SOURCE_ID[20], | |
| 226 | + SQLITE_VERSION); | |
| 227 | + zDb = db_name("repository"); | |
| 228 | + fossil_print("%*s%d pages, %d bytes/page, %d free pages, " | |
| 229 | + "%s, %s mode\n", | |
| 230 | + colWidth, "database-stats:", | |
| 231 | + db_int(0, "PRAGMA %s.page_count", zDb), | |
| 232 | + db_int(0, "PRAGMA %s.page_size", zDb), | |
| 233 | + db_int(0, "PRAGMA %s.freelist_count", zDb), | |
| 234 | + db_text(0, "PRAGMA %s.encoding", zDb), | |
| 235 | + db_text(0, "PRAGMA %s.journal_mode", zDb)); | |
| 236 | + | |
| 237 | +} | |
| 238 | + | |
| 239 | + | |
| 142 | 240 | |
| 143 | 241 | /* |
| 144 | 242 | ** WEBPAGE: urllist |
| 145 | 243 | ** |
| 146 | 244 | ** Show ways in which this repository has been accessed |
| 147 | 245 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -66,11 +66,11 @@ | |
| 66 | @ </td></tr> |
| 67 | if( !brief ){ |
| 68 | @ <tr><th>Number Of Artifacts:</th><td> |
| 69 | n = db_int(0, "SELECT count(*) FROM blob"); |
| 70 | m = db_int(0, "SELECT count(*) FROM delta"); |
| 71 | @ %d(n) (stored as %d(n-m) full text and %d(m) delta blobs) |
| 72 | @ </td></tr> |
| 73 | if( n>0 ){ |
| 74 | int a, b; |
| 75 | Stmt q; |
| 76 | @ <tr><th>Uncompressed Artifact Size:</th><td> |
| @@ -94,11 +94,11 @@ | |
| 94 | a = t/fsize; |
| 95 | @ %d(a):%d(b) |
| 96 | @ </td></tr> |
| 97 | } |
| 98 | @ <tr><th>Number Of Check-ins:</th><td> |
| 99 | n = db_int(0, "SELECT count(distinct mid) FROM mlink /*scan*/"); |
| 100 | @ %d(n) |
| 101 | @ </td></tr> |
| 102 | @ <tr><th>Number Of Files:</th><td> |
| 103 | n = db_int(0, "SELECT count(*) FROM filename /*scan*/"); |
| 104 | @ %d(n) |
| @@ -115,17 +115,16 @@ | |
| 115 | @ </td></tr> |
| 116 | } |
| 117 | @ <tr><th>Duration Of Project:</th><td> |
| 118 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 119 | " + 0.99"); |
| 120 | @ %d(n) days or approximately %.2f(n/365.24) years. |
| 121 | @ </td></tr> |
| 122 | @ <tr><th>Project ID:</th><td>%h(db_get("project-code",""))</td></tr> |
| 123 | @ <tr><th>Server ID:</th><td>%h(db_get("server-code",""))</td></tr> |
| 124 | @ <tr><th>Fossil Version:</th><td> |
| 125 | @ %h(RELEASE_VERSION) %h(MANIFEST_DATE) %h(MANIFEST_VERSION) |
| 126 | @ (%h(COMPILER_NAME)) |
| 127 | @ </td></tr> |
| 128 | @ <tr><th>SQLite Version:</th><td>%.19s(SQLITE_SOURCE_ID) |
| 129 | @ [%.10s(&SQLITE_SOURCE_ID[20])] (%s(SQLITE_VERSION))</td></tr> |
| 130 | @ <tr><th>Database Stats:</th><td> |
| 131 | zDb = db_name("repository"); |
| @@ -137,10 +136,109 @@ | |
| 137 | @ </td></tr> |
| 138 | |
| 139 | @ </table> |
| 140 | style_footer(); |
| 141 | } |
| 142 | |
| 143 | /* |
| 144 | ** WEBPAGE: urllist |
| 145 | ** |
| 146 | ** Show ways in which this repository has been accessed |
| 147 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -66,11 +66,11 @@ | |
| 66 | @ </td></tr> |
| 67 | if( !brief ){ |
| 68 | @ <tr><th>Number Of Artifacts:</th><td> |
| 69 | n = db_int(0, "SELECT count(*) FROM blob"); |
| 70 | m = db_int(0, "SELECT count(*) FROM delta"); |
| 71 | @ %d(n) (%d(n-m) fulltext and %d(m) deltas) |
| 72 | @ </td></tr> |
| 73 | if( n>0 ){ |
| 74 | int a, b; |
| 75 | Stmt q; |
| 76 | @ <tr><th>Uncompressed Artifact Size:</th><td> |
| @@ -94,11 +94,11 @@ | |
| 94 | a = t/fsize; |
| 95 | @ %d(a):%d(b) |
| 96 | @ </td></tr> |
| 97 | } |
| 98 | @ <tr><th>Number Of Check-ins:</th><td> |
| 99 | n = db_int(0, "SELECT count(*) FROM event WHERE type='ci' /*scan*/"); |
| 100 | @ %d(n) |
| 101 | @ </td></tr> |
| 102 | @ <tr><th>Number Of Files:</th><td> |
| 103 | n = db_int(0, "SELECT count(*) FROM filename /*scan*/"); |
| 104 | @ %d(n) |
| @@ -115,17 +115,16 @@ | |
| 115 | @ </td></tr> |
| 116 | } |
| 117 | @ <tr><th>Duration Of Project:</th><td> |
| 118 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 119 | " + 0.99"); |
| 120 | @ %d(n) days or approximately %.2f(n/365.2425) years. |
| 121 | @ </td></tr> |
| 122 | @ <tr><th>Project ID:</th><td>%h(db_get("project-code",""))</td></tr> |
| 123 | @ <tr><th>Fossil Version:</th><td> |
| 124 | @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION) |
| 125 | @ (%h(RELEASE_VERSION)) [compiled using %h(COMPILER_NAME)] |
| 126 | @ </td></tr> |
| 127 | @ <tr><th>SQLite Version:</th><td>%.19s(SQLITE_SOURCE_ID) |
| 128 | @ [%.10s(&SQLITE_SOURCE_ID[20])] (%s(SQLITE_VERSION))</td></tr> |
| 129 | @ <tr><th>Database Stats:</th><td> |
| 130 | zDb = db_name("repository"); |
| @@ -137,10 +136,109 @@ | |
| 136 | @ </td></tr> |
| 137 | |
| 138 | @ </table> |
| 139 | style_footer(); |
| 140 | } |
| 141 | |
| 142 | /* |
| 143 | ** COMMAND: dbstat* |
| 144 | ** |
| 145 | ** Usage: %fossil dbstat ?-brief | -b? |
| 146 | ** |
| 147 | ** Shows statistics and global information about the repository. |
| 148 | ** |
| 149 | ** The (-brief|-b) option removes any "long-running" statistics, namely |
| 150 | ** those whose calculations are known to slow down as the repository |
| 151 | ** grows. |
| 152 | ** |
| 153 | */ |
| 154 | void dbstat_cmd(void){ |
| 155 | i64 t, fsize; |
| 156 | int n, m; |
| 157 | int szMax, szAvg; |
| 158 | const char *zDb; |
| 159 | int brief; |
| 160 | char zBuf[100]; |
| 161 | const int colWidth = -20 /* printf alignment/width for left column */; |
| 162 | brief = find_option("brief", "b",0)!=0; |
| 163 | db_find_and_open_repository(0,0); |
| 164 | fsize = file_size(g.zRepositoryName); |
| 165 | bigSizeName(sizeof(zBuf), zBuf, fsize); |
| 166 | fossil_print( "%*s%s\n", colWidth, "repository-size:", zBuf ); |
| 167 | if( !brief ){ |
| 168 | n = db_int(0, "SELECT count(*) FROM blob"); |
| 169 | m = db_int(0, "SELECT count(*) FROM delta"); |
| 170 | fossil_print("%*s%d (stored as %d full text and %d delta blobs)\n", |
| 171 | colWidth, "artifact-count:", |
| 172 | n, n-m, m); |
| 173 | if( n>0 ){ |
| 174 | int a, b; |
| 175 | Stmt q; |
| 176 | db_prepare(&q, "SELECT total(size), avg(size), max(size)" |
| 177 | " FROM blob WHERE size>0"); |
| 178 | db_step(&q); |
| 179 | t = db_column_int64(&q, 0); |
| 180 | szAvg = db_column_int(&q, 1); |
| 181 | szMax = db_column_int(&q, 2); |
| 182 | db_finalize(&q); |
| 183 | bigSizeName(sizeof(zBuf), zBuf, t); |
| 184 | fossil_print( "%*s%d bytes average, " |
| 185 | "%d bytes max, %s total\n", |
| 186 | colWidth, "artifact-sizes:", |
| 187 | szAvg, szMax, zBuf); |
| 188 | if( t/fsize < 5 ){ |
| 189 | b = 10; |
| 190 | fsize /= 10; |
| 191 | }else{ |
| 192 | b = 1; |
| 193 | } |
| 194 | a = t/fsize; |
| 195 | fossil_print("%*s%d:%d\n", colWidth, "compression-ratio:", a, b); |
| 196 | } |
| 197 | n = db_int(0, "SELECT COUNT(*) FROM event e WHERE e.type='ci'"); |
| 198 | fossil_print("%*s%d\n", colWidth, "checkins:", n); |
| 199 | n = db_int(0, "SELECT count(*) FROM filename /*scan*/"); |
| 200 | fossil_print("%*s%d across all branches\n", colWidth, "files:", n); |
| 201 | n = db_int(0, "SELECT count(*) FROM tag /*scan*/" |
| 202 | " WHERE tagname GLOB 'wiki-*'"); |
| 203 | m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='w'"); |
| 204 | fossil_print("%*s%d (%d changes)\n", colWidth, "wikipages:", n, m); |
| 205 | n = db_int(0, "SELECT count(*) FROM tag /*scan*/" |
| 206 | " WHERE tagname GLOB 'tkt-*'"); |
| 207 | m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='t'"); |
| 208 | fossil_print("%*s%d (%d changes)\n", colWidth, "tickets:", n, m); |
| 209 | n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='e'"); |
| 210 | fossil_print("%*s%d\n", colWidth, "events:", n); |
| 211 | n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='g'"); |
| 212 | fossil_print("%*s%d\n", colWidth, "tagchanges:", n); |
| 213 | } |
| 214 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 215 | " + 0.99"); |
| 216 | fossil_print("%*s%d days or approximately %.2f years.\n", |
| 217 | colWidth, "project-age:", n, n/365.2425); |
| 218 | fossil_print("%*s%s\n", colWidth, "project-id:", db_get("project-code","")); |
| 219 | fossil_print("%*s%s %s %s (%s)\n", |
| 220 | colWidth, "fossil-version:", |
| 221 | RELEASE_VERSION, MANIFEST_DATE, MANIFEST_VERSION, |
| 222 | COMPILER_NAME); |
| 223 | fossil_print("%*s%.19s [%.10s] (%s)\n", |
| 224 | colWidth, "sqlite-version:", |
| 225 | SQLITE_SOURCE_ID, &SQLITE_SOURCE_ID[20], |
| 226 | SQLITE_VERSION); |
| 227 | zDb = db_name("repository"); |
| 228 | fossil_print("%*s%d pages, %d bytes/page, %d free pages, " |
| 229 | "%s, %s mode\n", |
| 230 | colWidth, "database-stats:", |
| 231 | db_int(0, "PRAGMA %s.page_count", zDb), |
| 232 | db_int(0, "PRAGMA %s.page_size", zDb), |
| 233 | db_int(0, "PRAGMA %s.freelist_count", zDb), |
| 234 | db_text(0, "PRAGMA %s.encoding", zDb), |
| 235 | db_text(0, "PRAGMA %s.journal_mode", zDb)); |
| 236 | |
| 237 | } |
| 238 | |
| 239 | |
| 240 | |
| 241 | /* |
| 242 | ** WEBPAGE: urllist |
| 243 | ** |
| 244 | ** Show ways in which this repository has been accessed |
| 245 |
+104
-6
| --- src/stat.c | ||
| +++ src/stat.c | ||
| @@ -66,11 +66,11 @@ | ||
| 66 | 66 | @ </td></tr> |
| 67 | 67 | if( !brief ){ |
| 68 | 68 | @ <tr><th>Number Of Artifacts:</th><td> |
| 69 | 69 | n = db_int(0, "SELECT count(*) FROM blob"); |
| 70 | 70 | m = db_int(0, "SELECT count(*) FROM delta"); |
| 71 | - @ %d(n) (stored as %d(n-m) full text and %d(m) delta blobs) | |
| 71 | + @ %d(n) (%d(n-m) fulltext and %d(m) deltas) | |
| 72 | 72 | @ </td></tr> |
| 73 | 73 | if( n>0 ){ |
| 74 | 74 | int a, b; |
| 75 | 75 | Stmt q; |
| 76 | 76 | @ <tr><th>Uncompressed Artifact Size:</th><td> |
| @@ -94,11 +94,11 @@ | ||
| 94 | 94 | a = t/fsize; |
| 95 | 95 | @ %d(a):%d(b) |
| 96 | 96 | @ </td></tr> |
| 97 | 97 | } |
| 98 | 98 | @ <tr><th>Number Of Check-ins:</th><td> |
| 99 | - n = db_int(0, "SELECT count(distinct mid) FROM mlink /*scan*/"); | |
| 99 | + n = db_int(0, "SELECT count(*) FROM event WHERE type='ci' /*scan*/"); | |
| 100 | 100 | @ %d(n) |
| 101 | 101 | @ </td></tr> |
| 102 | 102 | @ <tr><th>Number Of Files:</th><td> |
| 103 | 103 | n = db_int(0, "SELECT count(*) FROM filename /*scan*/"); |
| 104 | 104 | @ %d(n) |
| @@ -115,17 +115,16 @@ | ||
| 115 | 115 | @ </td></tr> |
| 116 | 116 | } |
| 117 | 117 | @ <tr><th>Duration Of Project:</th><td> |
| 118 | 118 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 119 | 119 | " + 0.99"); |
| 120 | - @ %d(n) days or approximately %.2f(n/365.24) years. | |
| 120 | + @ %d(n) days or approximately %.2f(n/365.2425) years. | |
| 121 | 121 | @ </td></tr> |
| 122 | 122 | @ <tr><th>Project ID:</th><td>%h(db_get("project-code",""))</td></tr> |
| 123 | - @ <tr><th>Server ID:</th><td>%h(db_get("server-code",""))</td></tr> | |
| 124 | 123 | @ <tr><th>Fossil Version:</th><td> |
| 125 | - @ %h(RELEASE_VERSION) %h(MANIFEST_DATE) %h(MANIFEST_VERSION) | |
| 126 | - @ (%h(COMPILER_NAME)) | |
| 124 | + @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION) | |
| 125 | + @ (%h(RELEASE_VERSION)) [compiled using %h(COMPILER_NAME)] | |
| 127 | 126 | @ </td></tr> |
| 128 | 127 | @ <tr><th>SQLite Version:</th><td>%.19s(SQLITE_SOURCE_ID) |
| 129 | 128 | @ [%.10s(&SQLITE_SOURCE_ID[20])] (%s(SQLITE_VERSION))</td></tr> |
| 130 | 129 | @ <tr><th>Database Stats:</th><td> |
| 131 | 130 | zDb = db_name("repository"); |
| @@ -137,10 +136,109 @@ | ||
| 137 | 136 | @ </td></tr> |
| 138 | 137 | |
| 139 | 138 | @ </table> |
| 140 | 139 | style_footer(); |
| 141 | 140 | } |
| 141 | + | |
| 142 | +/* | |
| 143 | +** COMMAND: dbstat* | |
| 144 | +** | |
| 145 | +** Usage: %fossil dbstat ?-brief | -b? | |
| 146 | +** | |
| 147 | +** Shows statistics and global information about the repository. | |
| 148 | +** | |
| 149 | +** The (-brief|-b) option removes any "long-running" statistics, namely | |
| 150 | +** those whose calculations are known to slow down as the repository | |
| 151 | +** grows. | |
| 152 | +** | |
| 153 | +*/ | |
| 154 | +void dbstat_cmd(void){ | |
| 155 | + i64 t, fsize; | |
| 156 | + int n, m; | |
| 157 | + int szMax, szAvg; | |
| 158 | + const char *zDb; | |
| 159 | + int brief; | |
| 160 | + char zBuf[100]; | |
| 161 | + const int colWidth = -20 /* printf alignment/width for left column */; | |
| 162 | + brief = find_option("brief", "b",0)!=0; | |
| 163 | + db_find_and_open_repository(0,0); | |
| 164 | + fsize = file_size(g.zRepositoryName); | |
| 165 | + bigSizeName(sizeof(zBuf), zBuf, fsize); | |
| 166 | + fossil_print( "%*s%s\n", colWidth, "repository-size:", zBuf ); | |
| 167 | + if( !brief ){ | |
| 168 | + n = db_int(0, "SELECT count(*) FROM blob"); | |
| 169 | + m = db_int(0, "SELECT count(*) FROM delta"); | |
| 170 | + fossil_print("%*s%d (stored as %d full text and %d delta blobs)\n", | |
| 171 | + colWidth, "artifact-count:", | |
| 172 | + n, n-m, m); | |
| 173 | + if( n>0 ){ | |
| 174 | + int a, b; | |
| 175 | + Stmt q; | |
| 176 | + db_prepare(&q, "SELECT total(size), avg(size), max(size)" | |
| 177 | + " FROM blob WHERE size>0"); | |
| 178 | + db_step(&q); | |
| 179 | + t = db_column_int64(&q, 0); | |
| 180 | + szAvg = db_column_int(&q, 1); | |
| 181 | + szMax = db_column_int(&q, 2); | |
| 182 | + db_finalize(&q); | |
| 183 | + bigSizeName(sizeof(zBuf), zBuf, t); | |
| 184 | + fossil_print( "%*s%d bytes average, " | |
| 185 | + "%d bytes max, %s total\n", | |
| 186 | + colWidth, "artifact-sizes:", | |
| 187 | + szAvg, szMax, zBuf); | |
| 188 | + if( t/fsize < 5 ){ | |
| 189 | + b = 10; | |
| 190 | + fsize /= 10; | |
| 191 | + }else{ | |
| 192 | + b = 1; | |
| 193 | + } | |
| 194 | + a = t/fsize; | |
| 195 | + fossil_print("%*s%d:%d\n", colWidth, "compression-ratio:", a, b); | |
| 196 | + } | |
| 197 | + n = db_int(0, "SELECT COUNT(*) FROM event e WHERE e.type='ci'"); | |
| 198 | + fossil_print("%*s%d\n", colWidth, "checkins:", n); | |
| 199 | + n = db_int(0, "SELECT count(*) FROM filename /*scan*/"); | |
| 200 | + fossil_print("%*s%d across all branches\n", colWidth, "files:", n); | |
| 201 | + n = db_int(0, "SELECT count(*) FROM tag /*scan*/" | |
| 202 | + " WHERE tagname GLOB 'wiki-*'"); | |
| 203 | + m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='w'"); | |
| 204 | + fossil_print("%*s%d (%d changes)\n", colWidth, "wikipages:", n, m); | |
| 205 | + n = db_int(0, "SELECT count(*) FROM tag /*scan*/" | |
| 206 | + " WHERE tagname GLOB 'tkt-*'"); | |
| 207 | + m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='t'"); | |
| 208 | + fossil_print("%*s%d (%d changes)\n", colWidth, "tickets:", n, m); | |
| 209 | + n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='e'"); | |
| 210 | + fossil_print("%*s%d\n", colWidth, "events:", n); | |
| 211 | + n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='g'"); | |
| 212 | + fossil_print("%*s%d\n", colWidth, "tagchanges:", n); | |
| 213 | + } | |
| 214 | + n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" | |
| 215 | + " + 0.99"); | |
| 216 | + fossil_print("%*s%d days or approximately %.2f years.\n", | |
| 217 | + colWidth, "project-age:", n, n/365.2425); | |
| 218 | + fossil_print("%*s%s\n", colWidth, "project-id:", db_get("project-code","")); | |
| 219 | + fossil_print("%*s%s %s %s (%s)\n", | |
| 220 | + colWidth, "fossil-version:", | |
| 221 | + RELEASE_VERSION, MANIFEST_DATE, MANIFEST_VERSION, | |
| 222 | + COMPILER_NAME); | |
| 223 | + fossil_print("%*s%.19s [%.10s] (%s)\n", | |
| 224 | + colWidth, "sqlite-version:", | |
| 225 | + SQLITE_SOURCE_ID, &SQLITE_SOURCE_ID[20], | |
| 226 | + SQLITE_VERSION); | |
| 227 | + zDb = db_name("repository"); | |
| 228 | + fossil_print("%*s%d pages, %d bytes/page, %d free pages, " | |
| 229 | + "%s, %s mode\n", | |
| 230 | + colWidth, "database-stats:", | |
| 231 | + db_int(0, "PRAGMA %s.page_count", zDb), | |
| 232 | + db_int(0, "PRAGMA %s.page_size", zDb), | |
| 233 | + db_int(0, "PRAGMA %s.freelist_count", zDb), | |
| 234 | + db_text(0, "PRAGMA %s.encoding", zDb), | |
| 235 | + db_text(0, "PRAGMA %s.journal_mode", zDb)); | |
| 236 | + | |
| 237 | +} | |
| 238 | + | |
| 239 | + | |
| 142 | 240 | |
| 143 | 241 | /* |
| 144 | 242 | ** WEBPAGE: urllist |
| 145 | 243 | ** |
| 146 | 244 | ** Show ways in which this repository has been accessed |
| 147 | 245 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -66,11 +66,11 @@ | |
| 66 | @ </td></tr> |
| 67 | if( !brief ){ |
| 68 | @ <tr><th>Number Of Artifacts:</th><td> |
| 69 | n = db_int(0, "SELECT count(*) FROM blob"); |
| 70 | m = db_int(0, "SELECT count(*) FROM delta"); |
| 71 | @ %d(n) (stored as %d(n-m) full text and %d(m) delta blobs) |
| 72 | @ </td></tr> |
| 73 | if( n>0 ){ |
| 74 | int a, b; |
| 75 | Stmt q; |
| 76 | @ <tr><th>Uncompressed Artifact Size:</th><td> |
| @@ -94,11 +94,11 @@ | |
| 94 | a = t/fsize; |
| 95 | @ %d(a):%d(b) |
| 96 | @ </td></tr> |
| 97 | } |
| 98 | @ <tr><th>Number Of Check-ins:</th><td> |
| 99 | n = db_int(0, "SELECT count(distinct mid) FROM mlink /*scan*/"); |
| 100 | @ %d(n) |
| 101 | @ </td></tr> |
| 102 | @ <tr><th>Number Of Files:</th><td> |
| 103 | n = db_int(0, "SELECT count(*) FROM filename /*scan*/"); |
| 104 | @ %d(n) |
| @@ -115,17 +115,16 @@ | |
| 115 | @ </td></tr> |
| 116 | } |
| 117 | @ <tr><th>Duration Of Project:</th><td> |
| 118 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 119 | " + 0.99"); |
| 120 | @ %d(n) days or approximately %.2f(n/365.24) years. |
| 121 | @ </td></tr> |
| 122 | @ <tr><th>Project ID:</th><td>%h(db_get("project-code",""))</td></tr> |
| 123 | @ <tr><th>Server ID:</th><td>%h(db_get("server-code",""))</td></tr> |
| 124 | @ <tr><th>Fossil Version:</th><td> |
| 125 | @ %h(RELEASE_VERSION) %h(MANIFEST_DATE) %h(MANIFEST_VERSION) |
| 126 | @ (%h(COMPILER_NAME)) |
| 127 | @ </td></tr> |
| 128 | @ <tr><th>SQLite Version:</th><td>%.19s(SQLITE_SOURCE_ID) |
| 129 | @ [%.10s(&SQLITE_SOURCE_ID[20])] (%s(SQLITE_VERSION))</td></tr> |
| 130 | @ <tr><th>Database Stats:</th><td> |
| 131 | zDb = db_name("repository"); |
| @@ -137,10 +136,109 @@ | |
| 137 | @ </td></tr> |
| 138 | |
| 139 | @ </table> |
| 140 | style_footer(); |
| 141 | } |
| 142 | |
| 143 | /* |
| 144 | ** WEBPAGE: urllist |
| 145 | ** |
| 146 | ** Show ways in which this repository has been accessed |
| 147 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -66,11 +66,11 @@ | |
| 66 | @ </td></tr> |
| 67 | if( !brief ){ |
| 68 | @ <tr><th>Number Of Artifacts:</th><td> |
| 69 | n = db_int(0, "SELECT count(*) FROM blob"); |
| 70 | m = db_int(0, "SELECT count(*) FROM delta"); |
| 71 | @ %d(n) (%d(n-m) fulltext and %d(m) deltas) |
| 72 | @ </td></tr> |
| 73 | if( n>0 ){ |
| 74 | int a, b; |
| 75 | Stmt q; |
| 76 | @ <tr><th>Uncompressed Artifact Size:</th><td> |
| @@ -94,11 +94,11 @@ | |
| 94 | a = t/fsize; |
| 95 | @ %d(a):%d(b) |
| 96 | @ </td></tr> |
| 97 | } |
| 98 | @ <tr><th>Number Of Check-ins:</th><td> |
| 99 | n = db_int(0, "SELECT count(*) FROM event WHERE type='ci' /*scan*/"); |
| 100 | @ %d(n) |
| 101 | @ </td></tr> |
| 102 | @ <tr><th>Number Of Files:</th><td> |
| 103 | n = db_int(0, "SELECT count(*) FROM filename /*scan*/"); |
| 104 | @ %d(n) |
| @@ -115,17 +115,16 @@ | |
| 115 | @ </td></tr> |
| 116 | } |
| 117 | @ <tr><th>Duration Of Project:</th><td> |
| 118 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 119 | " + 0.99"); |
| 120 | @ %d(n) days or approximately %.2f(n/365.2425) years. |
| 121 | @ </td></tr> |
| 122 | @ <tr><th>Project ID:</th><td>%h(db_get("project-code",""))</td></tr> |
| 123 | @ <tr><th>Fossil Version:</th><td> |
| 124 | @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION) |
| 125 | @ (%h(RELEASE_VERSION)) [compiled using %h(COMPILER_NAME)] |
| 126 | @ </td></tr> |
| 127 | @ <tr><th>SQLite Version:</th><td>%.19s(SQLITE_SOURCE_ID) |
| 128 | @ [%.10s(&SQLITE_SOURCE_ID[20])] (%s(SQLITE_VERSION))</td></tr> |
| 129 | @ <tr><th>Database Stats:</th><td> |
| 130 | zDb = db_name("repository"); |
| @@ -137,10 +136,109 @@ | |
| 136 | @ </td></tr> |
| 137 | |
| 138 | @ </table> |
| 139 | style_footer(); |
| 140 | } |
| 141 | |
| 142 | /* |
| 143 | ** COMMAND: dbstat* |
| 144 | ** |
| 145 | ** Usage: %fossil dbstat ?-brief | -b? |
| 146 | ** |
| 147 | ** Shows statistics and global information about the repository. |
| 148 | ** |
| 149 | ** The (-brief|-b) option removes any "long-running" statistics, namely |
| 150 | ** those whose calculations are known to slow down as the repository |
| 151 | ** grows. |
| 152 | ** |
| 153 | */ |
| 154 | void dbstat_cmd(void){ |
| 155 | i64 t, fsize; |
| 156 | int n, m; |
| 157 | int szMax, szAvg; |
| 158 | const char *zDb; |
| 159 | int brief; |
| 160 | char zBuf[100]; |
| 161 | const int colWidth = -20 /* printf alignment/width for left column */; |
| 162 | brief = find_option("brief", "b",0)!=0; |
| 163 | db_find_and_open_repository(0,0); |
| 164 | fsize = file_size(g.zRepositoryName); |
| 165 | bigSizeName(sizeof(zBuf), zBuf, fsize); |
| 166 | fossil_print( "%*s%s\n", colWidth, "repository-size:", zBuf ); |
| 167 | if( !brief ){ |
| 168 | n = db_int(0, "SELECT count(*) FROM blob"); |
| 169 | m = db_int(0, "SELECT count(*) FROM delta"); |
| 170 | fossil_print("%*s%d (stored as %d full text and %d delta blobs)\n", |
| 171 | colWidth, "artifact-count:", |
| 172 | n, n-m, m); |
| 173 | if( n>0 ){ |
| 174 | int a, b; |
| 175 | Stmt q; |
| 176 | db_prepare(&q, "SELECT total(size), avg(size), max(size)" |
| 177 | " FROM blob WHERE size>0"); |
| 178 | db_step(&q); |
| 179 | t = db_column_int64(&q, 0); |
| 180 | szAvg = db_column_int(&q, 1); |
| 181 | szMax = db_column_int(&q, 2); |
| 182 | db_finalize(&q); |
| 183 | bigSizeName(sizeof(zBuf), zBuf, t); |
| 184 | fossil_print( "%*s%d bytes average, " |
| 185 | "%d bytes max, %s total\n", |
| 186 | colWidth, "artifact-sizes:", |
| 187 | szAvg, szMax, zBuf); |
| 188 | if( t/fsize < 5 ){ |
| 189 | b = 10; |
| 190 | fsize /= 10; |
| 191 | }else{ |
| 192 | b = 1; |
| 193 | } |
| 194 | a = t/fsize; |
| 195 | fossil_print("%*s%d:%d\n", colWidth, "compression-ratio:", a, b); |
| 196 | } |
| 197 | n = db_int(0, "SELECT COUNT(*) FROM event e WHERE e.type='ci'"); |
| 198 | fossil_print("%*s%d\n", colWidth, "checkins:", n); |
| 199 | n = db_int(0, "SELECT count(*) FROM filename /*scan*/"); |
| 200 | fossil_print("%*s%d across all branches\n", colWidth, "files:", n); |
| 201 | n = db_int(0, "SELECT count(*) FROM tag /*scan*/" |
| 202 | " WHERE tagname GLOB 'wiki-*'"); |
| 203 | m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='w'"); |
| 204 | fossil_print("%*s%d (%d changes)\n", colWidth, "wikipages:", n, m); |
| 205 | n = db_int(0, "SELECT count(*) FROM tag /*scan*/" |
| 206 | " WHERE tagname GLOB 'tkt-*'"); |
| 207 | m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='t'"); |
| 208 | fossil_print("%*s%d (%d changes)\n", colWidth, "tickets:", n, m); |
| 209 | n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='e'"); |
| 210 | fossil_print("%*s%d\n", colWidth, "events:", n); |
| 211 | n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='g'"); |
| 212 | fossil_print("%*s%d\n", colWidth, "tagchanges:", n); |
| 213 | } |
| 214 | n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" |
| 215 | " + 0.99"); |
| 216 | fossil_print("%*s%d days or approximately %.2f years.\n", |
| 217 | colWidth, "project-age:", n, n/365.2425); |
| 218 | fossil_print("%*s%s\n", colWidth, "project-id:", db_get("project-code","")); |
| 219 | fossil_print("%*s%s %s %s (%s)\n", |
| 220 | colWidth, "fossil-version:", |
| 221 | RELEASE_VERSION, MANIFEST_DATE, MANIFEST_VERSION, |
| 222 | COMPILER_NAME); |
| 223 | fossil_print("%*s%.19s [%.10s] (%s)\n", |
| 224 | colWidth, "sqlite-version:", |
| 225 | SQLITE_SOURCE_ID, &SQLITE_SOURCE_ID[20], |
| 226 | SQLITE_VERSION); |
| 227 | zDb = db_name("repository"); |
| 228 | fossil_print("%*s%d pages, %d bytes/page, %d free pages, " |
| 229 | "%s, %s mode\n", |
| 230 | colWidth, "database-stats:", |
| 231 | db_int(0, "PRAGMA %s.page_count", zDb), |
| 232 | db_int(0, "PRAGMA %s.page_size", zDb), |
| 233 | db_int(0, "PRAGMA %s.freelist_count", zDb), |
| 234 | db_text(0, "PRAGMA %s.encoding", zDb), |
| 235 | db_text(0, "PRAGMA %s.journal_mode", zDb)); |
| 236 | |
| 237 | } |
| 238 | |
| 239 | |
| 240 | |
| 241 | /* |
| 242 | ** WEBPAGE: urllist |
| 243 | ** |
| 244 | ** Show ways in which this repository has been accessed |
| 245 |
+20
-35
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -210,19 +210,20 @@ | ||
| 210 | 210 | if( tmFlags & TIMELINE_GRAPH ){ |
| 211 | 211 | pGraph = graph_init(); |
| 212 | 212 | /* style is not moved to css, because this is |
| 213 | 213 | ** a technical div for the timeline graph |
| 214 | 214 | */ |
| 215 | - @ <div id="canvas" style="position:relative;width:1px;height:1px;" | |
| 215 | + @ <div id="canvas" style="position:relative;height:0px;width:0px;" | |
| 216 | 216 | @ onclick="clickOnGraph(event)"></div> |
| 217 | 217 | } |
| 218 | 218 | db_static_prepare(&qbranch, |
| 219 | 219 | "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid", |
| 220 | 220 | TAG_BRANCH |
| 221 | 221 | ); |
| 222 | 222 | |
| 223 | - @ <table id="timelineTable" class="timelineTable"> | |
| 223 | + @ <table id="timelineTable" class="timelineTable" | |
| 224 | + @ onclick="clickOnGraph(event)"> | |
| 224 | 225 | blob_zero(&comment); |
| 225 | 226 | while( db_step(pQuery)==SQLITE_ROW ){ |
| 226 | 227 | int rid = db_column_int(pQuery, 0); |
| 227 | 228 | const char *zUuid = db_column_text(pQuery, 1); |
| 228 | 229 | int isLeaf = db_column_int(pQuery, 5); |
| @@ -726,42 +727,16 @@ | ||
| 726 | 727 | @ for(var i in rowinfo){ |
| 727 | 728 | @ rowinfo[i].y = absoluteY("m"+rowinfo[i].id) + 10 - canvasY; |
| 728 | 729 | @ rowinfo[i].x = left + rowinfo[i].r*railPitch; |
| 729 | 730 | @ } |
| 730 | 731 | @ var btm = absoluteY("grbtm") + 10 - canvasY; |
| 731 | -#if 0 | |
| 732 | - @ if( btm<32768 ){ | |
| 733 | - @ canvasDiv.innerHTML = '<canvas id="timeline-canvas" '+ | |
| 734 | - @ 'style="position:absolute;left:'+(left-5)+'px;"' + | |
| 735 | - @ ' width="'+width+'" height="'+btm+'"><'+'/canvas>'; | |
| 736 | - @ realCanvas = gebi('timeline-canvas'); | |
| 737 | - @ }else{ | |
| 738 | - @ realCanvas = 0; | |
| 739 | - @ } | |
| 740 | - @ var context; | |
| 741 | - @ if( realCanvas && realCanvas.getContext | |
| 742 | - @ && (context = realCanvas.getContext('2d'))) { | |
| 743 | - @ drawBox = function(color,x0,y0,x1,y1) { | |
| 744 | - @ if( y0>32767 || y1>32767 ) return; | |
| 745 | - @ if( x0>x1 ){ var t=x0; x0=x1; x1=t; } | |
| 746 | - @ if( y0>y1 ){ var t=y0; y0=y1; y1=t; } | |
| 747 | - @ if(isNaN(x0) || isNaN(y0) || isNaN(x1) || isNaN(y1)) return; | |
| 748 | - @ context.fillStyle = color; | |
| 749 | - @ context.fillRect(x0-left+5,y0,x1-x0+1,y1-y0+1); | |
| 750 | - @ }; | |
| 751 | - @ } | |
| 752 | -#endif | |
| 753 | 732 | @ for(var i in rowinfo){ |
| 754 | 733 | @ drawNode(rowinfo[i], left, btm); |
| 755 | 734 | @ } |
| 756 | 735 | @ if( selRow!=null ) clickOnRow(selRow); |
| 757 | 736 | @ } |
| 758 | 737 | @ function clickOnGraph(event){ |
| 759 | -#ifdef OMIT_IE8_SUPPORT | |
| 760 | - @ var x=event.clientX-absoluteX("canvas")+window.pageXOffset; | |
| 761 | - @ var y=event.clientY-absoluteY("canvas")+window.pageYOffset; | |
| 762 | -#else | |
| 763 | 738 | @ var x=event.clientX-absoluteX("canvas"); |
| 764 | 739 | @ var y=event.clientY-absoluteY("canvas"); |
| 765 | 740 | @ if(window.pageXOffset!=null){ |
| 766 | 741 | @ x += window.pageXOffset; |
| 767 | 742 | @ y += window.pageYOffset; |
| @@ -769,16 +744,18 @@ | ||
| 769 | 744 | @ var d = window.document.documentElement; |
| 770 | 745 | @ if(document.compatMode!="CSS1Compat") d = d.body; |
| 771 | 746 | @ x += d.scrollLeft; |
| 772 | 747 | @ y += d.scrollTop; |
| 773 | 748 | @ } |
| 774 | -#endif | |
| 749 | + if( P("clicktest")!=0 ){ | |
| 750 | + @ alert("click at "+x+","+y) | |
| 751 | + } | |
| 775 | 752 | @ for(var i in rowinfo){ |
| 776 | 753 | @ p = rowinfo[i]; |
| 777 | - @ if( p.y<y-10 ) continue; | |
| 778 | - @ if( p.y>y+10 ) break; | |
| 779 | - @ if( p.x>x-10 && p.x<x+10 ){ | |
| 754 | + @ if( p.y<y-11 ) continue; | |
| 755 | + @ if( p.y>y+9 ) break; | |
| 756 | + @ if( p.x>x-11 && p.x<x+9 ){ | |
| 780 | 757 | @ clickOnRow(p); |
| 781 | 758 | @ break; |
| 782 | 759 | @ } |
| 783 | 760 | @ } |
| 784 | 761 | @ } |
| @@ -1414,10 +1391,12 @@ | ||
| 1414 | 1391 | ** 1. uuid |
| 1415 | 1392 | ** 2. Date/Time |
| 1416 | 1393 | ** 3. Comment string and user |
| 1417 | 1394 | ** 4. Number of non-merge children |
| 1418 | 1395 | ** 5. Number of parents |
| 1396 | +** 6. mtime | |
| 1397 | +** 7. branch | |
| 1419 | 1398 | */ |
| 1420 | 1399 | void print_timeline(Stmt *q, int mxLine, int showfiles){ |
| 1421 | 1400 | int nLine = 0; |
| 1422 | 1401 | char zPrevDate[20]; |
| 1423 | 1402 | const char *zCurrentUuid=0; |
| @@ -1522,15 +1501,21 @@ | ||
| 1522 | 1501 | @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x |
| 1523 | 1502 | @ FROM tag, tagxref |
| 1524 | 1503 | @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid |
| 1525 | 1504 | @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) |
| 1526 | 1505 | @ || ')' as comment, |
| 1527 | - @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) AS primPlinkCount, | |
| 1506 | + @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) | |
| 1507 | + @ AS primPlinkCount, | |
| 1528 | 1508 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount, |
| 1529 | - @ event.mtime AS mtime | |
| 1530 | - @ FROM event, blob | |
| 1509 | + @ event.mtime AS mtime, | |
| 1510 | + @ tagxref.value AS branch | |
| 1511 | + @ FROM tag CROSS JOIN event CROSS JOIN blob | |
| 1512 | + @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid | |
| 1513 | + @ AND tagxref.tagtype>0 | |
| 1514 | + @ AND tagxref.rid=blob.rid | |
| 1531 | 1515 | @ WHERE blob.rid=event.objid |
| 1516 | + @ AND tag.tagname='branch' | |
| 1532 | 1517 | ; |
| 1533 | 1518 | return zBaseSql; |
| 1534 | 1519 | } |
| 1535 | 1520 | |
| 1536 | 1521 | /* |
| 1537 | 1522 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -210,19 +210,20 @@ | |
| 210 | if( tmFlags & TIMELINE_GRAPH ){ |
| 211 | pGraph = graph_init(); |
| 212 | /* style is not moved to css, because this is |
| 213 | ** a technical div for the timeline graph |
| 214 | */ |
| 215 | @ <div id="canvas" style="position:relative;width:1px;height:1px;" |
| 216 | @ onclick="clickOnGraph(event)"></div> |
| 217 | } |
| 218 | db_static_prepare(&qbranch, |
| 219 | "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid", |
| 220 | TAG_BRANCH |
| 221 | ); |
| 222 | |
| 223 | @ <table id="timelineTable" class="timelineTable"> |
| 224 | blob_zero(&comment); |
| 225 | while( db_step(pQuery)==SQLITE_ROW ){ |
| 226 | int rid = db_column_int(pQuery, 0); |
| 227 | const char *zUuid = db_column_text(pQuery, 1); |
| 228 | int isLeaf = db_column_int(pQuery, 5); |
| @@ -726,42 +727,16 @@ | |
| 726 | @ for(var i in rowinfo){ |
| 727 | @ rowinfo[i].y = absoluteY("m"+rowinfo[i].id) + 10 - canvasY; |
| 728 | @ rowinfo[i].x = left + rowinfo[i].r*railPitch; |
| 729 | @ } |
| 730 | @ var btm = absoluteY("grbtm") + 10 - canvasY; |
| 731 | #if 0 |
| 732 | @ if( btm<32768 ){ |
| 733 | @ canvasDiv.innerHTML = '<canvas id="timeline-canvas" '+ |
| 734 | @ 'style="position:absolute;left:'+(left-5)+'px;"' + |
| 735 | @ ' width="'+width+'" height="'+btm+'"><'+'/canvas>'; |
| 736 | @ realCanvas = gebi('timeline-canvas'); |
| 737 | @ }else{ |
| 738 | @ realCanvas = 0; |
| 739 | @ } |
| 740 | @ var context; |
| 741 | @ if( realCanvas && realCanvas.getContext |
| 742 | @ && (context = realCanvas.getContext('2d'))) { |
| 743 | @ drawBox = function(color,x0,y0,x1,y1) { |
| 744 | @ if( y0>32767 || y1>32767 ) return; |
| 745 | @ if( x0>x1 ){ var t=x0; x0=x1; x1=t; } |
| 746 | @ if( y0>y1 ){ var t=y0; y0=y1; y1=t; } |
| 747 | @ if(isNaN(x0) || isNaN(y0) || isNaN(x1) || isNaN(y1)) return; |
| 748 | @ context.fillStyle = color; |
| 749 | @ context.fillRect(x0-left+5,y0,x1-x0+1,y1-y0+1); |
| 750 | @ }; |
| 751 | @ } |
| 752 | #endif |
| 753 | @ for(var i in rowinfo){ |
| 754 | @ drawNode(rowinfo[i], left, btm); |
| 755 | @ } |
| 756 | @ if( selRow!=null ) clickOnRow(selRow); |
| 757 | @ } |
| 758 | @ function clickOnGraph(event){ |
| 759 | #ifdef OMIT_IE8_SUPPORT |
| 760 | @ var x=event.clientX-absoluteX("canvas")+window.pageXOffset; |
| 761 | @ var y=event.clientY-absoluteY("canvas")+window.pageYOffset; |
| 762 | #else |
| 763 | @ var x=event.clientX-absoluteX("canvas"); |
| 764 | @ var y=event.clientY-absoluteY("canvas"); |
| 765 | @ if(window.pageXOffset!=null){ |
| 766 | @ x += window.pageXOffset; |
| 767 | @ y += window.pageYOffset; |
| @@ -769,16 +744,18 @@ | |
| 769 | @ var d = window.document.documentElement; |
| 770 | @ if(document.compatMode!="CSS1Compat") d = d.body; |
| 771 | @ x += d.scrollLeft; |
| 772 | @ y += d.scrollTop; |
| 773 | @ } |
| 774 | #endif |
| 775 | @ for(var i in rowinfo){ |
| 776 | @ p = rowinfo[i]; |
| 777 | @ if( p.y<y-10 ) continue; |
| 778 | @ if( p.y>y+10 ) break; |
| 779 | @ if( p.x>x-10 && p.x<x+10 ){ |
| 780 | @ clickOnRow(p); |
| 781 | @ break; |
| 782 | @ } |
| 783 | @ } |
| 784 | @ } |
| @@ -1414,10 +1391,12 @@ | |
| 1414 | ** 1. uuid |
| 1415 | ** 2. Date/Time |
| 1416 | ** 3. Comment string and user |
| 1417 | ** 4. Number of non-merge children |
| 1418 | ** 5. Number of parents |
| 1419 | */ |
| 1420 | void print_timeline(Stmt *q, int mxLine, int showfiles){ |
| 1421 | int nLine = 0; |
| 1422 | char zPrevDate[20]; |
| 1423 | const char *zCurrentUuid=0; |
| @@ -1522,15 +1501,21 @@ | |
| 1522 | @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x |
| 1523 | @ FROM tag, tagxref |
| 1524 | @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid |
| 1525 | @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) |
| 1526 | @ || ')' as comment, |
| 1527 | @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) AS primPlinkCount, |
| 1528 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount, |
| 1529 | @ event.mtime AS mtime |
| 1530 | @ FROM event, blob |
| 1531 | @ WHERE blob.rid=event.objid |
| 1532 | ; |
| 1533 | return zBaseSql; |
| 1534 | } |
| 1535 | |
| 1536 | /* |
| 1537 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -210,19 +210,20 @@ | |
| 210 | if( tmFlags & TIMELINE_GRAPH ){ |
| 211 | pGraph = graph_init(); |
| 212 | /* style is not moved to css, because this is |
| 213 | ** a technical div for the timeline graph |
| 214 | */ |
| 215 | @ <div id="canvas" style="position:relative;height:0px;width:0px;" |
| 216 | @ onclick="clickOnGraph(event)"></div> |
| 217 | } |
| 218 | db_static_prepare(&qbranch, |
| 219 | "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid", |
| 220 | TAG_BRANCH |
| 221 | ); |
| 222 | |
| 223 | @ <table id="timelineTable" class="timelineTable" |
| 224 | @ onclick="clickOnGraph(event)"> |
| 225 | blob_zero(&comment); |
| 226 | while( db_step(pQuery)==SQLITE_ROW ){ |
| 227 | int rid = db_column_int(pQuery, 0); |
| 228 | const char *zUuid = db_column_text(pQuery, 1); |
| 229 | int isLeaf = db_column_int(pQuery, 5); |
| @@ -726,42 +727,16 @@ | |
| 727 | @ for(var i in rowinfo){ |
| 728 | @ rowinfo[i].y = absoluteY("m"+rowinfo[i].id) + 10 - canvasY; |
| 729 | @ rowinfo[i].x = left + rowinfo[i].r*railPitch; |
| 730 | @ } |
| 731 | @ var btm = absoluteY("grbtm") + 10 - canvasY; |
| 732 | @ for(var i in rowinfo){ |
| 733 | @ drawNode(rowinfo[i], left, btm); |
| 734 | @ } |
| 735 | @ if( selRow!=null ) clickOnRow(selRow); |
| 736 | @ } |
| 737 | @ function clickOnGraph(event){ |
| 738 | @ var x=event.clientX-absoluteX("canvas"); |
| 739 | @ var y=event.clientY-absoluteY("canvas"); |
| 740 | @ if(window.pageXOffset!=null){ |
| 741 | @ x += window.pageXOffset; |
| 742 | @ y += window.pageYOffset; |
| @@ -769,16 +744,18 @@ | |
| 744 | @ var d = window.document.documentElement; |
| 745 | @ if(document.compatMode!="CSS1Compat") d = d.body; |
| 746 | @ x += d.scrollLeft; |
| 747 | @ y += d.scrollTop; |
| 748 | @ } |
| 749 | if( P("clicktest")!=0 ){ |
| 750 | @ alert("click at "+x+","+y) |
| 751 | } |
| 752 | @ for(var i in rowinfo){ |
| 753 | @ p = rowinfo[i]; |
| 754 | @ if( p.y<y-11 ) continue; |
| 755 | @ if( p.y>y+9 ) break; |
| 756 | @ if( p.x>x-11 && p.x<x+9 ){ |
| 757 | @ clickOnRow(p); |
| 758 | @ break; |
| 759 | @ } |
| 760 | @ } |
| 761 | @ } |
| @@ -1414,10 +1391,12 @@ | |
| 1391 | ** 1. uuid |
| 1392 | ** 2. Date/Time |
| 1393 | ** 3. Comment string and user |
| 1394 | ** 4. Number of non-merge children |
| 1395 | ** 5. Number of parents |
| 1396 | ** 6. mtime |
| 1397 | ** 7. branch |
| 1398 | */ |
| 1399 | void print_timeline(Stmt *q, int mxLine, int showfiles){ |
| 1400 | int nLine = 0; |
| 1401 | char zPrevDate[20]; |
| 1402 | const char *zCurrentUuid=0; |
| @@ -1522,15 +1501,21 @@ | |
| 1501 | @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x |
| 1502 | @ FROM tag, tagxref |
| 1503 | @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid |
| 1504 | @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) |
| 1505 | @ || ')' as comment, |
| 1506 | @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) |
| 1507 | @ AS primPlinkCount, |
| 1508 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount, |
| 1509 | @ event.mtime AS mtime, |
| 1510 | @ tagxref.value AS branch |
| 1511 | @ FROM tag CROSS JOIN event CROSS JOIN blob |
| 1512 | @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid |
| 1513 | @ AND tagxref.tagtype>0 |
| 1514 | @ AND tagxref.rid=blob.rid |
| 1515 | @ WHERE blob.rid=event.objid |
| 1516 | @ AND tag.tagname='branch' |
| 1517 | ; |
| 1518 | return zBaseSql; |
| 1519 | } |
| 1520 | |
| 1521 | /* |
| 1522 |
+20
-35
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -210,19 +210,20 @@ | ||
| 210 | 210 | if( tmFlags & TIMELINE_GRAPH ){ |
| 211 | 211 | pGraph = graph_init(); |
| 212 | 212 | /* style is not moved to css, because this is |
| 213 | 213 | ** a technical div for the timeline graph |
| 214 | 214 | */ |
| 215 | - @ <div id="canvas" style="position:relative;width:1px;height:1px;" | |
| 215 | + @ <div id="canvas" style="position:relative;height:0px;width:0px;" | |
| 216 | 216 | @ onclick="clickOnGraph(event)"></div> |
| 217 | 217 | } |
| 218 | 218 | db_static_prepare(&qbranch, |
| 219 | 219 | "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid", |
| 220 | 220 | TAG_BRANCH |
| 221 | 221 | ); |
| 222 | 222 | |
| 223 | - @ <table id="timelineTable" class="timelineTable"> | |
| 223 | + @ <table id="timelineTable" class="timelineTable" | |
| 224 | + @ onclick="clickOnGraph(event)"> | |
| 224 | 225 | blob_zero(&comment); |
| 225 | 226 | while( db_step(pQuery)==SQLITE_ROW ){ |
| 226 | 227 | int rid = db_column_int(pQuery, 0); |
| 227 | 228 | const char *zUuid = db_column_text(pQuery, 1); |
| 228 | 229 | int isLeaf = db_column_int(pQuery, 5); |
| @@ -726,42 +727,16 @@ | ||
| 726 | 727 | @ for(var i in rowinfo){ |
| 727 | 728 | @ rowinfo[i].y = absoluteY("m"+rowinfo[i].id) + 10 - canvasY; |
| 728 | 729 | @ rowinfo[i].x = left + rowinfo[i].r*railPitch; |
| 729 | 730 | @ } |
| 730 | 731 | @ var btm = absoluteY("grbtm") + 10 - canvasY; |
| 731 | -#if 0 | |
| 732 | - @ if( btm<32768 ){ | |
| 733 | - @ canvasDiv.innerHTML = '<canvas id="timeline-canvas" '+ | |
| 734 | - @ 'style="position:absolute;left:'+(left-5)+'px;"' + | |
| 735 | - @ ' width="'+width+'" height="'+btm+'"><'+'/canvas>'; | |
| 736 | - @ realCanvas = gebi('timeline-canvas'); | |
| 737 | - @ }else{ | |
| 738 | - @ realCanvas = 0; | |
| 739 | - @ } | |
| 740 | - @ var context; | |
| 741 | - @ if( realCanvas && realCanvas.getContext | |
| 742 | - @ && (context = realCanvas.getContext('2d'))) { | |
| 743 | - @ drawBox = function(color,x0,y0,x1,y1) { | |
| 744 | - @ if( y0>32767 || y1>32767 ) return; | |
| 745 | - @ if( x0>x1 ){ var t=x0; x0=x1; x1=t; } | |
| 746 | - @ if( y0>y1 ){ var t=y0; y0=y1; y1=t; } | |
| 747 | - @ if(isNaN(x0) || isNaN(y0) || isNaN(x1) || isNaN(y1)) return; | |
| 748 | - @ context.fillStyle = color; | |
| 749 | - @ context.fillRect(x0-left+5,y0,x1-x0+1,y1-y0+1); | |
| 750 | - @ }; | |
| 751 | - @ } | |
| 752 | -#endif | |
| 753 | 732 | @ for(var i in rowinfo){ |
| 754 | 733 | @ drawNode(rowinfo[i], left, btm); |
| 755 | 734 | @ } |
| 756 | 735 | @ if( selRow!=null ) clickOnRow(selRow); |
| 757 | 736 | @ } |
| 758 | 737 | @ function clickOnGraph(event){ |
| 759 | -#ifdef OMIT_IE8_SUPPORT | |
| 760 | - @ var x=event.clientX-absoluteX("canvas")+window.pageXOffset; | |
| 761 | - @ var y=event.clientY-absoluteY("canvas")+window.pageYOffset; | |
| 762 | -#else | |
| 763 | 738 | @ var x=event.clientX-absoluteX("canvas"); |
| 764 | 739 | @ var y=event.clientY-absoluteY("canvas"); |
| 765 | 740 | @ if(window.pageXOffset!=null){ |
| 766 | 741 | @ x += window.pageXOffset; |
| 767 | 742 | @ y += window.pageYOffset; |
| @@ -769,16 +744,18 @@ | ||
| 769 | 744 | @ var d = window.document.documentElement; |
| 770 | 745 | @ if(document.compatMode!="CSS1Compat") d = d.body; |
| 771 | 746 | @ x += d.scrollLeft; |
| 772 | 747 | @ y += d.scrollTop; |
| 773 | 748 | @ } |
| 774 | -#endif | |
| 749 | + if( P("clicktest")!=0 ){ | |
| 750 | + @ alert("click at "+x+","+y) | |
| 751 | + } | |
| 775 | 752 | @ for(var i in rowinfo){ |
| 776 | 753 | @ p = rowinfo[i]; |
| 777 | - @ if( p.y<y-10 ) continue; | |
| 778 | - @ if( p.y>y+10 ) break; | |
| 779 | - @ if( p.x>x-10 && p.x<x+10 ){ | |
| 754 | + @ if( p.y<y-11 ) continue; | |
| 755 | + @ if( p.y>y+9 ) break; | |
| 756 | + @ if( p.x>x-11 && p.x<x+9 ){ | |
| 780 | 757 | @ clickOnRow(p); |
| 781 | 758 | @ break; |
| 782 | 759 | @ } |
| 783 | 760 | @ } |
| 784 | 761 | @ } |
| @@ -1414,10 +1391,12 @@ | ||
| 1414 | 1391 | ** 1. uuid |
| 1415 | 1392 | ** 2. Date/Time |
| 1416 | 1393 | ** 3. Comment string and user |
| 1417 | 1394 | ** 4. Number of non-merge children |
| 1418 | 1395 | ** 5. Number of parents |
| 1396 | +** 6. mtime | |
| 1397 | +** 7. branch | |
| 1419 | 1398 | */ |
| 1420 | 1399 | void print_timeline(Stmt *q, int mxLine, int showfiles){ |
| 1421 | 1400 | int nLine = 0; |
| 1422 | 1401 | char zPrevDate[20]; |
| 1423 | 1402 | const char *zCurrentUuid=0; |
| @@ -1522,15 +1501,21 @@ | ||
| 1522 | 1501 | @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x |
| 1523 | 1502 | @ FROM tag, tagxref |
| 1524 | 1503 | @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid |
| 1525 | 1504 | @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) |
| 1526 | 1505 | @ || ')' as comment, |
| 1527 | - @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) AS primPlinkCount, | |
| 1506 | + @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) | |
| 1507 | + @ AS primPlinkCount, | |
| 1528 | 1508 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount, |
| 1529 | - @ event.mtime AS mtime | |
| 1530 | - @ FROM event, blob | |
| 1509 | + @ event.mtime AS mtime, | |
| 1510 | + @ tagxref.value AS branch | |
| 1511 | + @ FROM tag CROSS JOIN event CROSS JOIN blob | |
| 1512 | + @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid | |
| 1513 | + @ AND tagxref.tagtype>0 | |
| 1514 | + @ AND tagxref.rid=blob.rid | |
| 1531 | 1515 | @ WHERE blob.rid=event.objid |
| 1516 | + @ AND tag.tagname='branch' | |
| 1532 | 1517 | ; |
| 1533 | 1518 | return zBaseSql; |
| 1534 | 1519 | } |
| 1535 | 1520 | |
| 1536 | 1521 | /* |
| 1537 | 1522 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -210,19 +210,20 @@ | |
| 210 | if( tmFlags & TIMELINE_GRAPH ){ |
| 211 | pGraph = graph_init(); |
| 212 | /* style is not moved to css, because this is |
| 213 | ** a technical div for the timeline graph |
| 214 | */ |
| 215 | @ <div id="canvas" style="position:relative;width:1px;height:1px;" |
| 216 | @ onclick="clickOnGraph(event)"></div> |
| 217 | } |
| 218 | db_static_prepare(&qbranch, |
| 219 | "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid", |
| 220 | TAG_BRANCH |
| 221 | ); |
| 222 | |
| 223 | @ <table id="timelineTable" class="timelineTable"> |
| 224 | blob_zero(&comment); |
| 225 | while( db_step(pQuery)==SQLITE_ROW ){ |
| 226 | int rid = db_column_int(pQuery, 0); |
| 227 | const char *zUuid = db_column_text(pQuery, 1); |
| 228 | int isLeaf = db_column_int(pQuery, 5); |
| @@ -726,42 +727,16 @@ | |
| 726 | @ for(var i in rowinfo){ |
| 727 | @ rowinfo[i].y = absoluteY("m"+rowinfo[i].id) + 10 - canvasY; |
| 728 | @ rowinfo[i].x = left + rowinfo[i].r*railPitch; |
| 729 | @ } |
| 730 | @ var btm = absoluteY("grbtm") + 10 - canvasY; |
| 731 | #if 0 |
| 732 | @ if( btm<32768 ){ |
| 733 | @ canvasDiv.innerHTML = '<canvas id="timeline-canvas" '+ |
| 734 | @ 'style="position:absolute;left:'+(left-5)+'px;"' + |
| 735 | @ ' width="'+width+'" height="'+btm+'"><'+'/canvas>'; |
| 736 | @ realCanvas = gebi('timeline-canvas'); |
| 737 | @ }else{ |
| 738 | @ realCanvas = 0; |
| 739 | @ } |
| 740 | @ var context; |
| 741 | @ if( realCanvas && realCanvas.getContext |
| 742 | @ && (context = realCanvas.getContext('2d'))) { |
| 743 | @ drawBox = function(color,x0,y0,x1,y1) { |
| 744 | @ if( y0>32767 || y1>32767 ) return; |
| 745 | @ if( x0>x1 ){ var t=x0; x0=x1; x1=t; } |
| 746 | @ if( y0>y1 ){ var t=y0; y0=y1; y1=t; } |
| 747 | @ if(isNaN(x0) || isNaN(y0) || isNaN(x1) || isNaN(y1)) return; |
| 748 | @ context.fillStyle = color; |
| 749 | @ context.fillRect(x0-left+5,y0,x1-x0+1,y1-y0+1); |
| 750 | @ }; |
| 751 | @ } |
| 752 | #endif |
| 753 | @ for(var i in rowinfo){ |
| 754 | @ drawNode(rowinfo[i], left, btm); |
| 755 | @ } |
| 756 | @ if( selRow!=null ) clickOnRow(selRow); |
| 757 | @ } |
| 758 | @ function clickOnGraph(event){ |
| 759 | #ifdef OMIT_IE8_SUPPORT |
| 760 | @ var x=event.clientX-absoluteX("canvas")+window.pageXOffset; |
| 761 | @ var y=event.clientY-absoluteY("canvas")+window.pageYOffset; |
| 762 | #else |
| 763 | @ var x=event.clientX-absoluteX("canvas"); |
| 764 | @ var y=event.clientY-absoluteY("canvas"); |
| 765 | @ if(window.pageXOffset!=null){ |
| 766 | @ x += window.pageXOffset; |
| 767 | @ y += window.pageYOffset; |
| @@ -769,16 +744,18 @@ | |
| 769 | @ var d = window.document.documentElement; |
| 770 | @ if(document.compatMode!="CSS1Compat") d = d.body; |
| 771 | @ x += d.scrollLeft; |
| 772 | @ y += d.scrollTop; |
| 773 | @ } |
| 774 | #endif |
| 775 | @ for(var i in rowinfo){ |
| 776 | @ p = rowinfo[i]; |
| 777 | @ if( p.y<y-10 ) continue; |
| 778 | @ if( p.y>y+10 ) break; |
| 779 | @ if( p.x>x-10 && p.x<x+10 ){ |
| 780 | @ clickOnRow(p); |
| 781 | @ break; |
| 782 | @ } |
| 783 | @ } |
| 784 | @ } |
| @@ -1414,10 +1391,12 @@ | |
| 1414 | ** 1. uuid |
| 1415 | ** 2. Date/Time |
| 1416 | ** 3. Comment string and user |
| 1417 | ** 4. Number of non-merge children |
| 1418 | ** 5. Number of parents |
| 1419 | */ |
| 1420 | void print_timeline(Stmt *q, int mxLine, int showfiles){ |
| 1421 | int nLine = 0; |
| 1422 | char zPrevDate[20]; |
| 1423 | const char *zCurrentUuid=0; |
| @@ -1522,15 +1501,21 @@ | |
| 1522 | @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x |
| 1523 | @ FROM tag, tagxref |
| 1524 | @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid |
| 1525 | @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) |
| 1526 | @ || ')' as comment, |
| 1527 | @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) AS primPlinkCount, |
| 1528 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount, |
| 1529 | @ event.mtime AS mtime |
| 1530 | @ FROM event, blob |
| 1531 | @ WHERE blob.rid=event.objid |
| 1532 | ; |
| 1533 | return zBaseSql; |
| 1534 | } |
| 1535 | |
| 1536 | /* |
| 1537 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -210,19 +210,20 @@ | |
| 210 | if( tmFlags & TIMELINE_GRAPH ){ |
| 211 | pGraph = graph_init(); |
| 212 | /* style is not moved to css, because this is |
| 213 | ** a technical div for the timeline graph |
| 214 | */ |
| 215 | @ <div id="canvas" style="position:relative;height:0px;width:0px;" |
| 216 | @ onclick="clickOnGraph(event)"></div> |
| 217 | } |
| 218 | db_static_prepare(&qbranch, |
| 219 | "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid", |
| 220 | TAG_BRANCH |
| 221 | ); |
| 222 | |
| 223 | @ <table id="timelineTable" class="timelineTable" |
| 224 | @ onclick="clickOnGraph(event)"> |
| 225 | blob_zero(&comment); |
| 226 | while( db_step(pQuery)==SQLITE_ROW ){ |
| 227 | int rid = db_column_int(pQuery, 0); |
| 228 | const char *zUuid = db_column_text(pQuery, 1); |
| 229 | int isLeaf = db_column_int(pQuery, 5); |
| @@ -726,42 +727,16 @@ | |
| 727 | @ for(var i in rowinfo){ |
| 728 | @ rowinfo[i].y = absoluteY("m"+rowinfo[i].id) + 10 - canvasY; |
| 729 | @ rowinfo[i].x = left + rowinfo[i].r*railPitch; |
| 730 | @ } |
| 731 | @ var btm = absoluteY("grbtm") + 10 - canvasY; |
| 732 | @ for(var i in rowinfo){ |
| 733 | @ drawNode(rowinfo[i], left, btm); |
| 734 | @ } |
| 735 | @ if( selRow!=null ) clickOnRow(selRow); |
| 736 | @ } |
| 737 | @ function clickOnGraph(event){ |
| 738 | @ var x=event.clientX-absoluteX("canvas"); |
| 739 | @ var y=event.clientY-absoluteY("canvas"); |
| 740 | @ if(window.pageXOffset!=null){ |
| 741 | @ x += window.pageXOffset; |
| 742 | @ y += window.pageYOffset; |
| @@ -769,16 +744,18 @@ | |
| 744 | @ var d = window.document.documentElement; |
| 745 | @ if(document.compatMode!="CSS1Compat") d = d.body; |
| 746 | @ x += d.scrollLeft; |
| 747 | @ y += d.scrollTop; |
| 748 | @ } |
| 749 | if( P("clicktest")!=0 ){ |
| 750 | @ alert("click at "+x+","+y) |
| 751 | } |
| 752 | @ for(var i in rowinfo){ |
| 753 | @ p = rowinfo[i]; |
| 754 | @ if( p.y<y-11 ) continue; |
| 755 | @ if( p.y>y+9 ) break; |
| 756 | @ if( p.x>x-11 && p.x<x+9 ){ |
| 757 | @ clickOnRow(p); |
| 758 | @ break; |
| 759 | @ } |
| 760 | @ } |
| 761 | @ } |
| @@ -1414,10 +1391,12 @@ | |
| 1391 | ** 1. uuid |
| 1392 | ** 2. Date/Time |
| 1393 | ** 3. Comment string and user |
| 1394 | ** 4. Number of non-merge children |
| 1395 | ** 5. Number of parents |
| 1396 | ** 6. mtime |
| 1397 | ** 7. branch |
| 1398 | */ |
| 1399 | void print_timeline(Stmt *q, int mxLine, int showfiles){ |
| 1400 | int nLine = 0; |
| 1401 | char zPrevDate[20]; |
| 1402 | const char *zCurrentUuid=0; |
| @@ -1522,15 +1501,21 @@ | |
| 1501 | @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x |
| 1502 | @ FROM tag, tagxref |
| 1503 | @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid |
| 1504 | @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) |
| 1505 | @ || ')' as comment, |
| 1506 | @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) |
| 1507 | @ AS primPlinkCount, |
| 1508 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount, |
| 1509 | @ event.mtime AS mtime, |
| 1510 | @ tagxref.value AS branch |
| 1511 | @ FROM tag CROSS JOIN event CROSS JOIN blob |
| 1512 | @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid |
| 1513 | @ AND tagxref.tagtype>0 |
| 1514 | @ AND tagxref.rid=blob.rid |
| 1515 | @ WHERE blob.rid=event.objid |
| 1516 | @ AND tag.tagname='branch' |
| 1517 | ; |
| 1518 | return zBaseSql; |
| 1519 | } |
| 1520 | |
| 1521 | /* |
| 1522 |
+20
-35
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -210,19 +210,20 @@ | ||
| 210 | 210 | if( tmFlags & TIMELINE_GRAPH ){ |
| 211 | 211 | pGraph = graph_init(); |
| 212 | 212 | /* style is not moved to css, because this is |
| 213 | 213 | ** a technical div for the timeline graph |
| 214 | 214 | */ |
| 215 | - @ <div id="canvas" style="position:relative;width:1px;height:1px;" | |
| 215 | + @ <div id="canvas" style="position:relative;height:0px;width:0px;" | |
| 216 | 216 | @ onclick="clickOnGraph(event)"></div> |
| 217 | 217 | } |
| 218 | 218 | db_static_prepare(&qbranch, |
| 219 | 219 | "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid", |
| 220 | 220 | TAG_BRANCH |
| 221 | 221 | ); |
| 222 | 222 | |
| 223 | - @ <table id="timelineTable" class="timelineTable"> | |
| 223 | + @ <table id="timelineTable" class="timelineTable" | |
| 224 | + @ onclick="clickOnGraph(event)"> | |
| 224 | 225 | blob_zero(&comment); |
| 225 | 226 | while( db_step(pQuery)==SQLITE_ROW ){ |
| 226 | 227 | int rid = db_column_int(pQuery, 0); |
| 227 | 228 | const char *zUuid = db_column_text(pQuery, 1); |
| 228 | 229 | int isLeaf = db_column_int(pQuery, 5); |
| @@ -726,42 +727,16 @@ | ||
| 726 | 727 | @ for(var i in rowinfo){ |
| 727 | 728 | @ rowinfo[i].y = absoluteY("m"+rowinfo[i].id) + 10 - canvasY; |
| 728 | 729 | @ rowinfo[i].x = left + rowinfo[i].r*railPitch; |
| 729 | 730 | @ } |
| 730 | 731 | @ var btm = absoluteY("grbtm") + 10 - canvasY; |
| 731 | -#if 0 | |
| 732 | - @ if( btm<32768 ){ | |
| 733 | - @ canvasDiv.innerHTML = '<canvas id="timeline-canvas" '+ | |
| 734 | - @ 'style="position:absolute;left:'+(left-5)+'px;"' + | |
| 735 | - @ ' width="'+width+'" height="'+btm+'"><'+'/canvas>'; | |
| 736 | - @ realCanvas = gebi('timeline-canvas'); | |
| 737 | - @ }else{ | |
| 738 | - @ realCanvas = 0; | |
| 739 | - @ } | |
| 740 | - @ var context; | |
| 741 | - @ if( realCanvas && realCanvas.getContext | |
| 742 | - @ && (context = realCanvas.getContext('2d'))) { | |
| 743 | - @ drawBox = function(color,x0,y0,x1,y1) { | |
| 744 | - @ if( y0>32767 || y1>32767 ) return; | |
| 745 | - @ if( x0>x1 ){ var t=x0; x0=x1; x1=t; } | |
| 746 | - @ if( y0>y1 ){ var t=y0; y0=y1; y1=t; } | |
| 747 | - @ if(isNaN(x0) || isNaN(y0) || isNaN(x1) || isNaN(y1)) return; | |
| 748 | - @ context.fillStyle = color; | |
| 749 | - @ context.fillRect(x0-left+5,y0,x1-x0+1,y1-y0+1); | |
| 750 | - @ }; | |
| 751 | - @ } | |
| 752 | -#endif | |
| 753 | 732 | @ for(var i in rowinfo){ |
| 754 | 733 | @ drawNode(rowinfo[i], left, btm); |
| 755 | 734 | @ } |
| 756 | 735 | @ if( selRow!=null ) clickOnRow(selRow); |
| 757 | 736 | @ } |
| 758 | 737 | @ function clickOnGraph(event){ |
| 759 | -#ifdef OMIT_IE8_SUPPORT | |
| 760 | - @ var x=event.clientX-absoluteX("canvas")+window.pageXOffset; | |
| 761 | - @ var y=event.clientY-absoluteY("canvas")+window.pageYOffset; | |
| 762 | -#else | |
| 763 | 738 | @ var x=event.clientX-absoluteX("canvas"); |
| 764 | 739 | @ var y=event.clientY-absoluteY("canvas"); |
| 765 | 740 | @ if(window.pageXOffset!=null){ |
| 766 | 741 | @ x += window.pageXOffset; |
| 767 | 742 | @ y += window.pageYOffset; |
| @@ -769,16 +744,18 @@ | ||
| 769 | 744 | @ var d = window.document.documentElement; |
| 770 | 745 | @ if(document.compatMode!="CSS1Compat") d = d.body; |
| 771 | 746 | @ x += d.scrollLeft; |
| 772 | 747 | @ y += d.scrollTop; |
| 773 | 748 | @ } |
| 774 | -#endif | |
| 749 | + if( P("clicktest")!=0 ){ | |
| 750 | + @ alert("click at "+x+","+y) | |
| 751 | + } | |
| 775 | 752 | @ for(var i in rowinfo){ |
| 776 | 753 | @ p = rowinfo[i]; |
| 777 | - @ if( p.y<y-10 ) continue; | |
| 778 | - @ if( p.y>y+10 ) break; | |
| 779 | - @ if( p.x>x-10 && p.x<x+10 ){ | |
| 754 | + @ if( p.y<y-11 ) continue; | |
| 755 | + @ if( p.y>y+9 ) break; | |
| 756 | + @ if( p.x>x-11 && p.x<x+9 ){ | |
| 780 | 757 | @ clickOnRow(p); |
| 781 | 758 | @ break; |
| 782 | 759 | @ } |
| 783 | 760 | @ } |
| 784 | 761 | @ } |
| @@ -1414,10 +1391,12 @@ | ||
| 1414 | 1391 | ** 1. uuid |
| 1415 | 1392 | ** 2. Date/Time |
| 1416 | 1393 | ** 3. Comment string and user |
| 1417 | 1394 | ** 4. Number of non-merge children |
| 1418 | 1395 | ** 5. Number of parents |
| 1396 | +** 6. mtime | |
| 1397 | +** 7. branch | |
| 1419 | 1398 | */ |
| 1420 | 1399 | void print_timeline(Stmt *q, int mxLine, int showfiles){ |
| 1421 | 1400 | int nLine = 0; |
| 1422 | 1401 | char zPrevDate[20]; |
| 1423 | 1402 | const char *zCurrentUuid=0; |
| @@ -1522,15 +1501,21 @@ | ||
| 1522 | 1501 | @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x |
| 1523 | 1502 | @ FROM tag, tagxref |
| 1524 | 1503 | @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid |
| 1525 | 1504 | @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) |
| 1526 | 1505 | @ || ')' as comment, |
| 1527 | - @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) AS primPlinkCount, | |
| 1506 | + @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) | |
| 1507 | + @ AS primPlinkCount, | |
| 1528 | 1508 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount, |
| 1529 | - @ event.mtime AS mtime | |
| 1530 | - @ FROM event, blob | |
| 1509 | + @ event.mtime AS mtime, | |
| 1510 | + @ tagxref.value AS branch | |
| 1511 | + @ FROM tag CROSS JOIN event CROSS JOIN blob | |
| 1512 | + @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid | |
| 1513 | + @ AND tagxref.tagtype>0 | |
| 1514 | + @ AND tagxref.rid=blob.rid | |
| 1531 | 1515 | @ WHERE blob.rid=event.objid |
| 1516 | + @ AND tag.tagname='branch' | |
| 1532 | 1517 | ; |
| 1533 | 1518 | return zBaseSql; |
| 1534 | 1519 | } |
| 1535 | 1520 | |
| 1536 | 1521 | /* |
| 1537 | 1522 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -210,19 +210,20 @@ | |
| 210 | if( tmFlags & TIMELINE_GRAPH ){ |
| 211 | pGraph = graph_init(); |
| 212 | /* style is not moved to css, because this is |
| 213 | ** a technical div for the timeline graph |
| 214 | */ |
| 215 | @ <div id="canvas" style="position:relative;width:1px;height:1px;" |
| 216 | @ onclick="clickOnGraph(event)"></div> |
| 217 | } |
| 218 | db_static_prepare(&qbranch, |
| 219 | "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid", |
| 220 | TAG_BRANCH |
| 221 | ); |
| 222 | |
| 223 | @ <table id="timelineTable" class="timelineTable"> |
| 224 | blob_zero(&comment); |
| 225 | while( db_step(pQuery)==SQLITE_ROW ){ |
| 226 | int rid = db_column_int(pQuery, 0); |
| 227 | const char *zUuid = db_column_text(pQuery, 1); |
| 228 | int isLeaf = db_column_int(pQuery, 5); |
| @@ -726,42 +727,16 @@ | |
| 726 | @ for(var i in rowinfo){ |
| 727 | @ rowinfo[i].y = absoluteY("m"+rowinfo[i].id) + 10 - canvasY; |
| 728 | @ rowinfo[i].x = left + rowinfo[i].r*railPitch; |
| 729 | @ } |
| 730 | @ var btm = absoluteY("grbtm") + 10 - canvasY; |
| 731 | #if 0 |
| 732 | @ if( btm<32768 ){ |
| 733 | @ canvasDiv.innerHTML = '<canvas id="timeline-canvas" '+ |
| 734 | @ 'style="position:absolute;left:'+(left-5)+'px;"' + |
| 735 | @ ' width="'+width+'" height="'+btm+'"><'+'/canvas>'; |
| 736 | @ realCanvas = gebi('timeline-canvas'); |
| 737 | @ }else{ |
| 738 | @ realCanvas = 0; |
| 739 | @ } |
| 740 | @ var context; |
| 741 | @ if( realCanvas && realCanvas.getContext |
| 742 | @ && (context = realCanvas.getContext('2d'))) { |
| 743 | @ drawBox = function(color,x0,y0,x1,y1) { |
| 744 | @ if( y0>32767 || y1>32767 ) return; |
| 745 | @ if( x0>x1 ){ var t=x0; x0=x1; x1=t; } |
| 746 | @ if( y0>y1 ){ var t=y0; y0=y1; y1=t; } |
| 747 | @ if(isNaN(x0) || isNaN(y0) || isNaN(x1) || isNaN(y1)) return; |
| 748 | @ context.fillStyle = color; |
| 749 | @ context.fillRect(x0-left+5,y0,x1-x0+1,y1-y0+1); |
| 750 | @ }; |
| 751 | @ } |
| 752 | #endif |
| 753 | @ for(var i in rowinfo){ |
| 754 | @ drawNode(rowinfo[i], left, btm); |
| 755 | @ } |
| 756 | @ if( selRow!=null ) clickOnRow(selRow); |
| 757 | @ } |
| 758 | @ function clickOnGraph(event){ |
| 759 | #ifdef OMIT_IE8_SUPPORT |
| 760 | @ var x=event.clientX-absoluteX("canvas")+window.pageXOffset; |
| 761 | @ var y=event.clientY-absoluteY("canvas")+window.pageYOffset; |
| 762 | #else |
| 763 | @ var x=event.clientX-absoluteX("canvas"); |
| 764 | @ var y=event.clientY-absoluteY("canvas"); |
| 765 | @ if(window.pageXOffset!=null){ |
| 766 | @ x += window.pageXOffset; |
| 767 | @ y += window.pageYOffset; |
| @@ -769,16 +744,18 @@ | |
| 769 | @ var d = window.document.documentElement; |
| 770 | @ if(document.compatMode!="CSS1Compat") d = d.body; |
| 771 | @ x += d.scrollLeft; |
| 772 | @ y += d.scrollTop; |
| 773 | @ } |
| 774 | #endif |
| 775 | @ for(var i in rowinfo){ |
| 776 | @ p = rowinfo[i]; |
| 777 | @ if( p.y<y-10 ) continue; |
| 778 | @ if( p.y>y+10 ) break; |
| 779 | @ if( p.x>x-10 && p.x<x+10 ){ |
| 780 | @ clickOnRow(p); |
| 781 | @ break; |
| 782 | @ } |
| 783 | @ } |
| 784 | @ } |
| @@ -1414,10 +1391,12 @@ | |
| 1414 | ** 1. uuid |
| 1415 | ** 2. Date/Time |
| 1416 | ** 3. Comment string and user |
| 1417 | ** 4. Number of non-merge children |
| 1418 | ** 5. Number of parents |
| 1419 | */ |
| 1420 | void print_timeline(Stmt *q, int mxLine, int showfiles){ |
| 1421 | int nLine = 0; |
| 1422 | char zPrevDate[20]; |
| 1423 | const char *zCurrentUuid=0; |
| @@ -1522,15 +1501,21 @@ | |
| 1522 | @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x |
| 1523 | @ FROM tag, tagxref |
| 1524 | @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid |
| 1525 | @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) |
| 1526 | @ || ')' as comment, |
| 1527 | @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) AS primPlinkCount, |
| 1528 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount, |
| 1529 | @ event.mtime AS mtime |
| 1530 | @ FROM event, blob |
| 1531 | @ WHERE blob.rid=event.objid |
| 1532 | ; |
| 1533 | return zBaseSql; |
| 1534 | } |
| 1535 | |
| 1536 | /* |
| 1537 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -210,19 +210,20 @@ | |
| 210 | if( tmFlags & TIMELINE_GRAPH ){ |
| 211 | pGraph = graph_init(); |
| 212 | /* style is not moved to css, because this is |
| 213 | ** a technical div for the timeline graph |
| 214 | */ |
| 215 | @ <div id="canvas" style="position:relative;height:0px;width:0px;" |
| 216 | @ onclick="clickOnGraph(event)"></div> |
| 217 | } |
| 218 | db_static_prepare(&qbranch, |
| 219 | "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid", |
| 220 | TAG_BRANCH |
| 221 | ); |
| 222 | |
| 223 | @ <table id="timelineTable" class="timelineTable" |
| 224 | @ onclick="clickOnGraph(event)"> |
| 225 | blob_zero(&comment); |
| 226 | while( db_step(pQuery)==SQLITE_ROW ){ |
| 227 | int rid = db_column_int(pQuery, 0); |
| 228 | const char *zUuid = db_column_text(pQuery, 1); |
| 229 | int isLeaf = db_column_int(pQuery, 5); |
| @@ -726,42 +727,16 @@ | |
| 727 | @ for(var i in rowinfo){ |
| 728 | @ rowinfo[i].y = absoluteY("m"+rowinfo[i].id) + 10 - canvasY; |
| 729 | @ rowinfo[i].x = left + rowinfo[i].r*railPitch; |
| 730 | @ } |
| 731 | @ var btm = absoluteY("grbtm") + 10 - canvasY; |
| 732 | @ for(var i in rowinfo){ |
| 733 | @ drawNode(rowinfo[i], left, btm); |
| 734 | @ } |
| 735 | @ if( selRow!=null ) clickOnRow(selRow); |
| 736 | @ } |
| 737 | @ function clickOnGraph(event){ |
| 738 | @ var x=event.clientX-absoluteX("canvas"); |
| 739 | @ var y=event.clientY-absoluteY("canvas"); |
| 740 | @ if(window.pageXOffset!=null){ |
| 741 | @ x += window.pageXOffset; |
| 742 | @ y += window.pageYOffset; |
| @@ -769,16 +744,18 @@ | |
| 744 | @ var d = window.document.documentElement; |
| 745 | @ if(document.compatMode!="CSS1Compat") d = d.body; |
| 746 | @ x += d.scrollLeft; |
| 747 | @ y += d.scrollTop; |
| 748 | @ } |
| 749 | if( P("clicktest")!=0 ){ |
| 750 | @ alert("click at "+x+","+y) |
| 751 | } |
| 752 | @ for(var i in rowinfo){ |
| 753 | @ p = rowinfo[i]; |
| 754 | @ if( p.y<y-11 ) continue; |
| 755 | @ if( p.y>y+9 ) break; |
| 756 | @ if( p.x>x-11 && p.x<x+9 ){ |
| 757 | @ clickOnRow(p); |
| 758 | @ break; |
| 759 | @ } |
| 760 | @ } |
| 761 | @ } |
| @@ -1414,10 +1391,12 @@ | |
| 1391 | ** 1. uuid |
| 1392 | ** 2. Date/Time |
| 1393 | ** 3. Comment string and user |
| 1394 | ** 4. Number of non-merge children |
| 1395 | ** 5. Number of parents |
| 1396 | ** 6. mtime |
| 1397 | ** 7. branch |
| 1398 | */ |
| 1399 | void print_timeline(Stmt *q, int mxLine, int showfiles){ |
| 1400 | int nLine = 0; |
| 1401 | char zPrevDate[20]; |
| 1402 | const char *zCurrentUuid=0; |
| @@ -1522,15 +1501,21 @@ | |
| 1501 | @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x |
| 1502 | @ FROM tag, tagxref |
| 1503 | @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid |
| 1504 | @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) |
| 1505 | @ || ')' as comment, |
| 1506 | @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) |
| 1507 | @ AS primPlinkCount, |
| 1508 | @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount, |
| 1509 | @ event.mtime AS mtime, |
| 1510 | @ tagxref.value AS branch |
| 1511 | @ FROM tag CROSS JOIN event CROSS JOIN blob |
| 1512 | @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid |
| 1513 | @ AND tagxref.tagtype>0 |
| 1514 | @ AND tagxref.rid=blob.rid |
| 1515 | @ WHERE blob.rid=event.objid |
| 1516 | @ AND tag.tagname='branch' |
| 1517 | ; |
| 1518 | return zBaseSql; |
| 1519 | } |
| 1520 | |
| 1521 | /* |
| 1522 |
+73
-16
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -34,12 +34,15 @@ | ||
| 34 | 34 | char *zAppend; /* Value to append */ |
| 35 | 35 | unsigned mUsed; /* 01: TICKET 02: TICKETCHNG */ |
| 36 | 36 | } *aField; |
| 37 | 37 | #define USEDBY_TICKET 01 |
| 38 | 38 | #define USEDBY_TICKETCHNG 02 |
| 39 | -static int haveTicket = 0; /* True if the TICKET table exists */ | |
| 40 | -static int haveTicketChng = 0; /* True if the TICKETCHNG table exists */ | |
| 39 | +#define USEDBY_BOTH 03 | |
| 40 | +static u8 haveTicket = 0; /* True if the TICKET table exists */ | |
| 41 | +static u8 haveTicketCTime = 0; /* True if TICKET.TKT_CTIME exists */ | |
| 42 | +static u8 haveTicketChng = 0; /* True if the TICKETCHNG table exists */ | |
| 43 | +static u8 haveTicketChngRid = 0; /* True if TICKETCHNG.TKT_RID exists */ | |
| 41 | 44 | |
| 42 | 45 | /* |
| 43 | 46 | ** Compare two entries in aField[] for sorting purposes |
| 44 | 47 | */ |
| 45 | 48 | static int nameCmpr(const void *a, const void *b){ |
| @@ -74,11 +77,14 @@ | ||
| 74 | 77 | once = 1; |
| 75 | 78 | db_prepare(&q, "PRAGMA table_info(ticket)"); |
| 76 | 79 | while( db_step(&q)==SQLITE_ROW ){ |
| 77 | 80 | const char *zFieldName = db_column_text(&q, 1); |
| 78 | 81 | haveTicket = 1; |
| 79 | - if( memcmp(zFieldName,"tkt_",4)==0 ) continue; | |
| 82 | + if( memcmp(zFieldName,"tkt_",4)==0 ){ | |
| 83 | + if( strcmp(zFieldName, "tkt_ctime")==0 ) haveTicketCTime = 1; | |
| 84 | + continue; | |
| 85 | + } | |
| 80 | 86 | if( nField%10==0 ){ |
| 81 | 87 | aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) ); |
| 82 | 88 | } |
| 83 | 89 | aField[nField].zName = mprintf("%s", zFieldName); |
| 84 | 90 | aField[nField].mUsed = USEDBY_TICKET; |
| @@ -87,11 +93,14 @@ | ||
| 87 | 93 | db_finalize(&q); |
| 88 | 94 | db_prepare(&q, "PRAGMA table_info(ticketchng)"); |
| 89 | 95 | while( db_step(&q)==SQLITE_ROW ){ |
| 90 | 96 | const char *zFieldName = db_column_text(&q, 1); |
| 91 | 97 | haveTicketChng = 1; |
| 92 | - if( memcmp(zFieldName,"tkt_",4)==0 ) continue; | |
| 98 | + if( memcmp(zFieldName,"tkt_",4)==0 ){ | |
| 99 | + if( strcmp(zFieldName,"tkt_rid")==0 ) haveTicketChngRid = 1; | |
| 100 | + continue; | |
| 101 | + } | |
| 93 | 102 | if( (i = fieldId(zFieldName))>=0 ){ |
| 94 | 103 | aField[i].mUsed |= USEDBY_TICKETCHNG; |
| 95 | 104 | continue; |
| 96 | 105 | } |
| 97 | 106 | if( nField%10==0 ){ |
| @@ -183,10 +192,11 @@ | ||
| 183 | 192 | */ |
| 184 | 193 | static int ticket_insert(const Manifest *p, int rid, int tktid){ |
| 185 | 194 | Blob sql1, sql2, sql3; |
| 186 | 195 | Stmt q; |
| 187 | 196 | int i, j; |
| 197 | + char *aUsed; | |
| 188 | 198 | |
| 189 | 199 | if( tktid==0 ){ |
| 190 | 200 | db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) " |
| 191 | 201 | "VALUES(%Q, 0)", p->zTicketUuid); |
| 192 | 202 | tktid = db_last_insert_rowid(); |
| @@ -193,22 +203,25 @@ | ||
| 193 | 203 | } |
| 194 | 204 | blob_zero(&sql1); |
| 195 | 205 | blob_zero(&sql2); |
| 196 | 206 | blob_zero(&sql3); |
| 197 | 207 | blob_appendf(&sql1, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime"); |
| 208 | + if( haveTicketCTime ){ | |
| 209 | + blob_appendf(&sql1, ", tkt_ctime=coalesce(tkt_ctime,:mtime)"); | |
| 210 | + } | |
| 211 | + aUsed = fossil_malloc( nField ); | |
| 212 | + memset(aUsed, 0, nField); | |
| 198 | 213 | for(i=0; i<p->nField; i++){ |
| 199 | 214 | const char *zName = p->aField[i].zName; |
| 200 | - if( zName[0]=='+' ){ | |
| 201 | - zName++; | |
| 202 | - if( (j = fieldId(zName))<0 ) continue; | |
| 203 | - if( aField[j].mUsed & USEDBY_TICKET ){ | |
| 215 | + if( (j = fieldId(zName))<0 ) continue; | |
| 216 | + aUsed[j] = 1; | |
| 217 | + if( aField[j].mUsed & USEDBY_TICKET ){ | |
| 218 | + if( zName[0]=='+' ){ | |
| 219 | + zName++; | |
| 204 | 220 | blob_appendf(&sql1,", %s=coalesce(%s,'') || %Q", |
| 205 | 221 | zName, zName, p->aField[i].zValue); |
| 206 | - } | |
| 207 | - }else{ | |
| 208 | - if( (j = fieldId(zName))<0 ) continue; | |
| 209 | - if( aField[j].mUsed & USEDBY_TICKET ){ | |
| 222 | + }else{ | |
| 210 | 223 | blob_appendf(&sql1,", %s=%Q", zName, p->aField[i].zValue); |
| 211 | 224 | } |
| 212 | 225 | } |
| 213 | 226 | if( aField[j].mUsed & USEDBY_TICKETCHNG ){ |
| 214 | 227 | blob_appendf(&sql2, ",%s", zName); |
| @@ -222,20 +235,41 @@ | ||
| 222 | 235 | db_prepare(&q, "%s", blob_str(&sql1)); |
| 223 | 236 | db_bind_double(&q, ":mtime", p->rDate); |
| 224 | 237 | db_step(&q); |
| 225 | 238 | db_finalize(&q); |
| 226 | 239 | blob_reset(&sql1); |
| 227 | - if( blob_size(&sql2)>0 ){ | |
| 228 | - db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)" | |
| 229 | - "VALUES(%d,:mtime%s)", | |
| 230 | - blob_str(&sql2), tktid, blob_str(&sql3)); | |
| 240 | + if( blob_size(&sql2)>0 || haveTicketChngRid ){ | |
| 241 | + int fromTkt = 0; | |
| 242 | + if( haveTicketChngRid ){ | |
| 243 | + blob_append(&sql2, ",tkt_rid", -1); | |
| 244 | + blob_appendf(&sql3, ",%d", rid); | |
| 245 | + } | |
| 246 | + for(i=0; i<nField; i++){ | |
| 247 | + if( aUsed[i]==0 | |
| 248 | + && (aField[i].mUsed & USEDBY_BOTH)==USEDBY_BOTH | |
| 249 | + ){ | |
| 250 | + fromTkt = 1; | |
| 251 | + blob_appendf(&sql2, ",%s", aField[i].zName); | |
| 252 | + blob_appendf(&sql3, ",%s", aField[i].zName); | |
| 253 | + } | |
| 254 | + } | |
| 255 | + if( fromTkt ){ | |
| 256 | + db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)" | |
| 257 | + "SELECT %d,:mtime%s FROM ticket WHERE tkt_id=%d", | |
| 258 | + blob_str(&sql2), tktid, blob_str(&sql3), tktid); | |
| 259 | + }else{ | |
| 260 | + db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)" | |
| 261 | + "VALUES(%d,:mtime%s)", | |
| 262 | + blob_str(&sql2), tktid, blob_str(&sql3)); | |
| 263 | + } | |
| 231 | 264 | db_bind_double(&q, ":mtime", p->rDate); |
| 232 | 265 | db_step(&q); |
| 233 | 266 | db_finalize(&q); |
| 234 | 267 | } |
| 235 | 268 | blob_reset(&sql2); |
| 236 | 269 | blob_reset(&sql3); |
| 270 | + fossil_free(aUsed); | |
| 237 | 271 | return tktid; |
| 238 | 272 | } |
| 239 | 273 | |
| 240 | 274 | /* |
| 241 | 275 | ** Rebuild an entire entry in the TICKET table |
| @@ -268,10 +302,11 @@ | ||
| 268 | 302 | } |
| 269 | 303 | createFlag = 0; |
| 270 | 304 | } |
| 271 | 305 | db_finalize(&q); |
| 272 | 306 | } |
| 307 | + | |
| 273 | 308 | |
| 274 | 309 | /* |
| 275 | 310 | ** Create the TH1 interpreter and load the "common" code. |
| 276 | 311 | */ |
| 277 | 312 | void ticket_init(void){ |
| @@ -328,10 +363,32 @@ | ||
| 328 | 363 | ticket_rebuild_entry(zName); |
| 329 | 364 | } |
| 330 | 365 | db_finalize(&q); |
| 331 | 366 | db_end_transaction(0); |
| 332 | 367 | } |
| 368 | + | |
| 369 | +/* | |
| 370 | +** COMMAND: test-ticket-rebuild | |
| 371 | +** | |
| 372 | +** Usage: %fossil test-ticket-rebuild TICKETID|all | |
| 373 | +** | |
| 374 | +** Rebuild the TICKET and TICKETCHNG tables for the given ticket ID | |
| 375 | +** or for ALL. | |
| 376 | +*/ | |
| 377 | +void test_ticket_rebuild(void){ | |
| 378 | + db_find_and_open_repository(0, 0); | |
| 379 | + if( g.argc!=3 ) usage("TICKETID|all"); | |
| 380 | + if( fossil_strcmp(g.argv[2], "all")==0 ){ | |
| 381 | + ticket_rebuild(); | |
| 382 | + }else{ | |
| 383 | + const char *zUuid; | |
| 384 | + zUuid = db_text(0, "SELECT substr(tagname,5) FROM tag" | |
| 385 | + " WHERE tagname GLOB 'tkt-%q*'", g.argv[2]); | |
| 386 | + if( zUuid==0 ) fossil_fatal("no such ticket: %s", g.argv[2]); | |
| 387 | + ticket_rebuild_entry(zUuid); | |
| 388 | + } | |
| 389 | +} | |
| 333 | 390 | |
| 334 | 391 | /* |
| 335 | 392 | ** For trouble-shooting purposes, render a dump of the aField[] table to |
| 336 | 393 | ** the webpage currently under construction. |
| 337 | 394 | */ |
| 338 | 395 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -34,12 +34,15 @@ | |
| 34 | char *zAppend; /* Value to append */ |
| 35 | unsigned mUsed; /* 01: TICKET 02: TICKETCHNG */ |
| 36 | } *aField; |
| 37 | #define USEDBY_TICKET 01 |
| 38 | #define USEDBY_TICKETCHNG 02 |
| 39 | static int haveTicket = 0; /* True if the TICKET table exists */ |
| 40 | static int haveTicketChng = 0; /* True if the TICKETCHNG table exists */ |
| 41 | |
| 42 | /* |
| 43 | ** Compare two entries in aField[] for sorting purposes |
| 44 | */ |
| 45 | static int nameCmpr(const void *a, const void *b){ |
| @@ -74,11 +77,14 @@ | |
| 74 | once = 1; |
| 75 | db_prepare(&q, "PRAGMA table_info(ticket)"); |
| 76 | while( db_step(&q)==SQLITE_ROW ){ |
| 77 | const char *zFieldName = db_column_text(&q, 1); |
| 78 | haveTicket = 1; |
| 79 | if( memcmp(zFieldName,"tkt_",4)==0 ) continue; |
| 80 | if( nField%10==0 ){ |
| 81 | aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) ); |
| 82 | } |
| 83 | aField[nField].zName = mprintf("%s", zFieldName); |
| 84 | aField[nField].mUsed = USEDBY_TICKET; |
| @@ -87,11 +93,14 @@ | |
| 87 | db_finalize(&q); |
| 88 | db_prepare(&q, "PRAGMA table_info(ticketchng)"); |
| 89 | while( db_step(&q)==SQLITE_ROW ){ |
| 90 | const char *zFieldName = db_column_text(&q, 1); |
| 91 | haveTicketChng = 1; |
| 92 | if( memcmp(zFieldName,"tkt_",4)==0 ) continue; |
| 93 | if( (i = fieldId(zFieldName))>=0 ){ |
| 94 | aField[i].mUsed |= USEDBY_TICKETCHNG; |
| 95 | continue; |
| 96 | } |
| 97 | if( nField%10==0 ){ |
| @@ -183,10 +192,11 @@ | |
| 183 | */ |
| 184 | static int ticket_insert(const Manifest *p, int rid, int tktid){ |
| 185 | Blob sql1, sql2, sql3; |
| 186 | Stmt q; |
| 187 | int i, j; |
| 188 | |
| 189 | if( tktid==0 ){ |
| 190 | db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) " |
| 191 | "VALUES(%Q, 0)", p->zTicketUuid); |
| 192 | tktid = db_last_insert_rowid(); |
| @@ -193,22 +203,25 @@ | |
| 193 | } |
| 194 | blob_zero(&sql1); |
| 195 | blob_zero(&sql2); |
| 196 | blob_zero(&sql3); |
| 197 | blob_appendf(&sql1, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime"); |
| 198 | for(i=0; i<p->nField; i++){ |
| 199 | const char *zName = p->aField[i].zName; |
| 200 | if( zName[0]=='+' ){ |
| 201 | zName++; |
| 202 | if( (j = fieldId(zName))<0 ) continue; |
| 203 | if( aField[j].mUsed & USEDBY_TICKET ){ |
| 204 | blob_appendf(&sql1,", %s=coalesce(%s,'') || %Q", |
| 205 | zName, zName, p->aField[i].zValue); |
| 206 | } |
| 207 | }else{ |
| 208 | if( (j = fieldId(zName))<0 ) continue; |
| 209 | if( aField[j].mUsed & USEDBY_TICKET ){ |
| 210 | blob_appendf(&sql1,", %s=%Q", zName, p->aField[i].zValue); |
| 211 | } |
| 212 | } |
| 213 | if( aField[j].mUsed & USEDBY_TICKETCHNG ){ |
| 214 | blob_appendf(&sql2, ",%s", zName); |
| @@ -222,20 +235,41 @@ | |
| 222 | db_prepare(&q, "%s", blob_str(&sql1)); |
| 223 | db_bind_double(&q, ":mtime", p->rDate); |
| 224 | db_step(&q); |
| 225 | db_finalize(&q); |
| 226 | blob_reset(&sql1); |
| 227 | if( blob_size(&sql2)>0 ){ |
| 228 | db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)" |
| 229 | "VALUES(%d,:mtime%s)", |
| 230 | blob_str(&sql2), tktid, blob_str(&sql3)); |
| 231 | db_bind_double(&q, ":mtime", p->rDate); |
| 232 | db_step(&q); |
| 233 | db_finalize(&q); |
| 234 | } |
| 235 | blob_reset(&sql2); |
| 236 | blob_reset(&sql3); |
| 237 | return tktid; |
| 238 | } |
| 239 | |
| 240 | /* |
| 241 | ** Rebuild an entire entry in the TICKET table |
| @@ -268,10 +302,11 @@ | |
| 268 | } |
| 269 | createFlag = 0; |
| 270 | } |
| 271 | db_finalize(&q); |
| 272 | } |
| 273 | |
| 274 | /* |
| 275 | ** Create the TH1 interpreter and load the "common" code. |
| 276 | */ |
| 277 | void ticket_init(void){ |
| @@ -328,10 +363,32 @@ | |
| 328 | ticket_rebuild_entry(zName); |
| 329 | } |
| 330 | db_finalize(&q); |
| 331 | db_end_transaction(0); |
| 332 | } |
| 333 | |
| 334 | /* |
| 335 | ** For trouble-shooting purposes, render a dump of the aField[] table to |
| 336 | ** the webpage currently under construction. |
| 337 | */ |
| 338 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -34,12 +34,15 @@ | |
| 34 | char *zAppend; /* Value to append */ |
| 35 | unsigned mUsed; /* 01: TICKET 02: TICKETCHNG */ |
| 36 | } *aField; |
| 37 | #define USEDBY_TICKET 01 |
| 38 | #define USEDBY_TICKETCHNG 02 |
| 39 | #define USEDBY_BOTH 03 |
| 40 | static u8 haveTicket = 0; /* True if the TICKET table exists */ |
| 41 | static u8 haveTicketCTime = 0; /* True if TICKET.TKT_CTIME exists */ |
| 42 | static u8 haveTicketChng = 0; /* True if the TICKETCHNG table exists */ |
| 43 | static u8 haveTicketChngRid = 0; /* True if TICKETCHNG.TKT_RID exists */ |
| 44 | |
| 45 | /* |
| 46 | ** Compare two entries in aField[] for sorting purposes |
| 47 | */ |
| 48 | static int nameCmpr(const void *a, const void *b){ |
| @@ -74,11 +77,14 @@ | |
| 77 | once = 1; |
| 78 | db_prepare(&q, "PRAGMA table_info(ticket)"); |
| 79 | while( db_step(&q)==SQLITE_ROW ){ |
| 80 | const char *zFieldName = db_column_text(&q, 1); |
| 81 | haveTicket = 1; |
| 82 | if( memcmp(zFieldName,"tkt_",4)==0 ){ |
| 83 | if( strcmp(zFieldName, "tkt_ctime")==0 ) haveTicketCTime = 1; |
| 84 | continue; |
| 85 | } |
| 86 | if( nField%10==0 ){ |
| 87 | aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) ); |
| 88 | } |
| 89 | aField[nField].zName = mprintf("%s", zFieldName); |
| 90 | aField[nField].mUsed = USEDBY_TICKET; |
| @@ -87,11 +93,14 @@ | |
| 93 | db_finalize(&q); |
| 94 | db_prepare(&q, "PRAGMA table_info(ticketchng)"); |
| 95 | while( db_step(&q)==SQLITE_ROW ){ |
| 96 | const char *zFieldName = db_column_text(&q, 1); |
| 97 | haveTicketChng = 1; |
| 98 | if( memcmp(zFieldName,"tkt_",4)==0 ){ |
| 99 | if( strcmp(zFieldName,"tkt_rid")==0 ) haveTicketChngRid = 1; |
| 100 | continue; |
| 101 | } |
| 102 | if( (i = fieldId(zFieldName))>=0 ){ |
| 103 | aField[i].mUsed |= USEDBY_TICKETCHNG; |
| 104 | continue; |
| 105 | } |
| 106 | if( nField%10==0 ){ |
| @@ -183,10 +192,11 @@ | |
| 192 | */ |
| 193 | static int ticket_insert(const Manifest *p, int rid, int tktid){ |
| 194 | Blob sql1, sql2, sql3; |
| 195 | Stmt q; |
| 196 | int i, j; |
| 197 | char *aUsed; |
| 198 | |
| 199 | if( tktid==0 ){ |
| 200 | db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) " |
| 201 | "VALUES(%Q, 0)", p->zTicketUuid); |
| 202 | tktid = db_last_insert_rowid(); |
| @@ -193,22 +203,25 @@ | |
| 203 | } |
| 204 | blob_zero(&sql1); |
| 205 | blob_zero(&sql2); |
| 206 | blob_zero(&sql3); |
| 207 | blob_appendf(&sql1, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime"); |
| 208 | if( haveTicketCTime ){ |
| 209 | blob_appendf(&sql1, ", tkt_ctime=coalesce(tkt_ctime,:mtime)"); |
| 210 | } |
| 211 | aUsed = fossil_malloc( nField ); |
| 212 | memset(aUsed, 0, nField); |
| 213 | for(i=0; i<p->nField; i++){ |
| 214 | const char *zName = p->aField[i].zName; |
| 215 | if( (j = fieldId(zName))<0 ) continue; |
| 216 | aUsed[j] = 1; |
| 217 | if( aField[j].mUsed & USEDBY_TICKET ){ |
| 218 | if( zName[0]=='+' ){ |
| 219 | zName++; |
| 220 | blob_appendf(&sql1,", %s=coalesce(%s,'') || %Q", |
| 221 | zName, zName, p->aField[i].zValue); |
| 222 | }else{ |
| 223 | blob_appendf(&sql1,", %s=%Q", zName, p->aField[i].zValue); |
| 224 | } |
| 225 | } |
| 226 | if( aField[j].mUsed & USEDBY_TICKETCHNG ){ |
| 227 | blob_appendf(&sql2, ",%s", zName); |
| @@ -222,20 +235,41 @@ | |
| 235 | db_prepare(&q, "%s", blob_str(&sql1)); |
| 236 | db_bind_double(&q, ":mtime", p->rDate); |
| 237 | db_step(&q); |
| 238 | db_finalize(&q); |
| 239 | blob_reset(&sql1); |
| 240 | if( blob_size(&sql2)>0 || haveTicketChngRid ){ |
| 241 | int fromTkt = 0; |
| 242 | if( haveTicketChngRid ){ |
| 243 | blob_append(&sql2, ",tkt_rid", -1); |
| 244 | blob_appendf(&sql3, ",%d", rid); |
| 245 | } |
| 246 | for(i=0; i<nField; i++){ |
| 247 | if( aUsed[i]==0 |
| 248 | && (aField[i].mUsed & USEDBY_BOTH)==USEDBY_BOTH |
| 249 | ){ |
| 250 | fromTkt = 1; |
| 251 | blob_appendf(&sql2, ",%s", aField[i].zName); |
| 252 | blob_appendf(&sql3, ",%s", aField[i].zName); |
| 253 | } |
| 254 | } |
| 255 | if( fromTkt ){ |
| 256 | db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)" |
| 257 | "SELECT %d,:mtime%s FROM ticket WHERE tkt_id=%d", |
| 258 | blob_str(&sql2), tktid, blob_str(&sql3), tktid); |
| 259 | }else{ |
| 260 | db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)" |
| 261 | "VALUES(%d,:mtime%s)", |
| 262 | blob_str(&sql2), tktid, blob_str(&sql3)); |
| 263 | } |
| 264 | db_bind_double(&q, ":mtime", p->rDate); |
| 265 | db_step(&q); |
| 266 | db_finalize(&q); |
| 267 | } |
| 268 | blob_reset(&sql2); |
| 269 | blob_reset(&sql3); |
| 270 | fossil_free(aUsed); |
| 271 | return tktid; |
| 272 | } |
| 273 | |
| 274 | /* |
| 275 | ** Rebuild an entire entry in the TICKET table |
| @@ -268,10 +302,11 @@ | |
| 302 | } |
| 303 | createFlag = 0; |
| 304 | } |
| 305 | db_finalize(&q); |
| 306 | } |
| 307 | |
| 308 | |
| 309 | /* |
| 310 | ** Create the TH1 interpreter and load the "common" code. |
| 311 | */ |
| 312 | void ticket_init(void){ |
| @@ -328,10 +363,32 @@ | |
| 363 | ticket_rebuild_entry(zName); |
| 364 | } |
| 365 | db_finalize(&q); |
| 366 | db_end_transaction(0); |
| 367 | } |
| 368 | |
| 369 | /* |
| 370 | ** COMMAND: test-ticket-rebuild |
| 371 | ** |
| 372 | ** Usage: %fossil test-ticket-rebuild TICKETID|all |
| 373 | ** |
| 374 | ** Rebuild the TICKET and TICKETCHNG tables for the given ticket ID |
| 375 | ** or for ALL. |
| 376 | */ |
| 377 | void test_ticket_rebuild(void){ |
| 378 | db_find_and_open_repository(0, 0); |
| 379 | if( g.argc!=3 ) usage("TICKETID|all"); |
| 380 | if( fossil_strcmp(g.argv[2], "all")==0 ){ |
| 381 | ticket_rebuild(); |
| 382 | }else{ |
| 383 | const char *zUuid; |
| 384 | zUuid = db_text(0, "SELECT substr(tagname,5) FROM tag" |
| 385 | " WHERE tagname GLOB 'tkt-%q*'", g.argv[2]); |
| 386 | if( zUuid==0 ) fossil_fatal("no such ticket: %s", g.argv[2]); |
| 387 | ticket_rebuild_entry(zUuid); |
| 388 | } |
| 389 | } |
| 390 | |
| 391 | /* |
| 392 | ** For trouble-shooting purposes, render a dump of the aField[] table to |
| 393 | ** the webpage currently under construction. |
| 394 | */ |
| 395 |
+73
-16
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -34,12 +34,15 @@ | ||
| 34 | 34 | char *zAppend; /* Value to append */ |
| 35 | 35 | unsigned mUsed; /* 01: TICKET 02: TICKETCHNG */ |
| 36 | 36 | } *aField; |
| 37 | 37 | #define USEDBY_TICKET 01 |
| 38 | 38 | #define USEDBY_TICKETCHNG 02 |
| 39 | -static int haveTicket = 0; /* True if the TICKET table exists */ | |
| 40 | -static int haveTicketChng = 0; /* True if the TICKETCHNG table exists */ | |
| 39 | +#define USEDBY_BOTH 03 | |
| 40 | +static u8 haveTicket = 0; /* True if the TICKET table exists */ | |
| 41 | +static u8 haveTicketCTime = 0; /* True if TICKET.TKT_CTIME exists */ | |
| 42 | +static u8 haveTicketChng = 0; /* True if the TICKETCHNG table exists */ | |
| 43 | +static u8 haveTicketChngRid = 0; /* True if TICKETCHNG.TKT_RID exists */ | |
| 41 | 44 | |
| 42 | 45 | /* |
| 43 | 46 | ** Compare two entries in aField[] for sorting purposes |
| 44 | 47 | */ |
| 45 | 48 | static int nameCmpr(const void *a, const void *b){ |
| @@ -74,11 +77,14 @@ | ||
| 74 | 77 | once = 1; |
| 75 | 78 | db_prepare(&q, "PRAGMA table_info(ticket)"); |
| 76 | 79 | while( db_step(&q)==SQLITE_ROW ){ |
| 77 | 80 | const char *zFieldName = db_column_text(&q, 1); |
| 78 | 81 | haveTicket = 1; |
| 79 | - if( memcmp(zFieldName,"tkt_",4)==0 ) continue; | |
| 82 | + if( memcmp(zFieldName,"tkt_",4)==0 ){ | |
| 83 | + if( strcmp(zFieldName, "tkt_ctime")==0 ) haveTicketCTime = 1; | |
| 84 | + continue; | |
| 85 | + } | |
| 80 | 86 | if( nField%10==0 ){ |
| 81 | 87 | aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) ); |
| 82 | 88 | } |
| 83 | 89 | aField[nField].zName = mprintf("%s", zFieldName); |
| 84 | 90 | aField[nField].mUsed = USEDBY_TICKET; |
| @@ -87,11 +93,14 @@ | ||
| 87 | 93 | db_finalize(&q); |
| 88 | 94 | db_prepare(&q, "PRAGMA table_info(ticketchng)"); |
| 89 | 95 | while( db_step(&q)==SQLITE_ROW ){ |
| 90 | 96 | const char *zFieldName = db_column_text(&q, 1); |
| 91 | 97 | haveTicketChng = 1; |
| 92 | - if( memcmp(zFieldName,"tkt_",4)==0 ) continue; | |
| 98 | + if( memcmp(zFieldName,"tkt_",4)==0 ){ | |
| 99 | + if( strcmp(zFieldName,"tkt_rid")==0 ) haveTicketChngRid = 1; | |
| 100 | + continue; | |
| 101 | + } | |
| 93 | 102 | if( (i = fieldId(zFieldName))>=0 ){ |
| 94 | 103 | aField[i].mUsed |= USEDBY_TICKETCHNG; |
| 95 | 104 | continue; |
| 96 | 105 | } |
| 97 | 106 | if( nField%10==0 ){ |
| @@ -183,10 +192,11 @@ | ||
| 183 | 192 | */ |
| 184 | 193 | static int ticket_insert(const Manifest *p, int rid, int tktid){ |
| 185 | 194 | Blob sql1, sql2, sql3; |
| 186 | 195 | Stmt q; |
| 187 | 196 | int i, j; |
| 197 | + char *aUsed; | |
| 188 | 198 | |
| 189 | 199 | if( tktid==0 ){ |
| 190 | 200 | db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) " |
| 191 | 201 | "VALUES(%Q, 0)", p->zTicketUuid); |
| 192 | 202 | tktid = db_last_insert_rowid(); |
| @@ -193,22 +203,25 @@ | ||
| 193 | 203 | } |
| 194 | 204 | blob_zero(&sql1); |
| 195 | 205 | blob_zero(&sql2); |
| 196 | 206 | blob_zero(&sql3); |
| 197 | 207 | blob_appendf(&sql1, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime"); |
| 208 | + if( haveTicketCTime ){ | |
| 209 | + blob_appendf(&sql1, ", tkt_ctime=coalesce(tkt_ctime,:mtime)"); | |
| 210 | + } | |
| 211 | + aUsed = fossil_malloc( nField ); | |
| 212 | + memset(aUsed, 0, nField); | |
| 198 | 213 | for(i=0; i<p->nField; i++){ |
| 199 | 214 | const char *zName = p->aField[i].zName; |
| 200 | - if( zName[0]=='+' ){ | |
| 201 | - zName++; | |
| 202 | - if( (j = fieldId(zName))<0 ) continue; | |
| 203 | - if( aField[j].mUsed & USEDBY_TICKET ){ | |
| 215 | + if( (j = fieldId(zName))<0 ) continue; | |
| 216 | + aUsed[j] = 1; | |
| 217 | + if( aField[j].mUsed & USEDBY_TICKET ){ | |
| 218 | + if( zName[0]=='+' ){ | |
| 219 | + zName++; | |
| 204 | 220 | blob_appendf(&sql1,", %s=coalesce(%s,'') || %Q", |
| 205 | 221 | zName, zName, p->aField[i].zValue); |
| 206 | - } | |
| 207 | - }else{ | |
| 208 | - if( (j = fieldId(zName))<0 ) continue; | |
| 209 | - if( aField[j].mUsed & USEDBY_TICKET ){ | |
| 222 | + }else{ | |
| 210 | 223 | blob_appendf(&sql1,", %s=%Q", zName, p->aField[i].zValue); |
| 211 | 224 | } |
| 212 | 225 | } |
| 213 | 226 | if( aField[j].mUsed & USEDBY_TICKETCHNG ){ |
| 214 | 227 | blob_appendf(&sql2, ",%s", zName); |
| @@ -222,20 +235,41 @@ | ||
| 222 | 235 | db_prepare(&q, "%s", blob_str(&sql1)); |
| 223 | 236 | db_bind_double(&q, ":mtime", p->rDate); |
| 224 | 237 | db_step(&q); |
| 225 | 238 | db_finalize(&q); |
| 226 | 239 | blob_reset(&sql1); |
| 227 | - if( blob_size(&sql2)>0 ){ | |
| 228 | - db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)" | |
| 229 | - "VALUES(%d,:mtime%s)", | |
| 230 | - blob_str(&sql2), tktid, blob_str(&sql3)); | |
| 240 | + if( blob_size(&sql2)>0 || haveTicketChngRid ){ | |
| 241 | + int fromTkt = 0; | |
| 242 | + if( haveTicketChngRid ){ | |
| 243 | + blob_append(&sql2, ",tkt_rid", -1); | |
| 244 | + blob_appendf(&sql3, ",%d", rid); | |
| 245 | + } | |
| 246 | + for(i=0; i<nField; i++){ | |
| 247 | + if( aUsed[i]==0 | |
| 248 | + && (aField[i].mUsed & USEDBY_BOTH)==USEDBY_BOTH | |
| 249 | + ){ | |
| 250 | + fromTkt = 1; | |
| 251 | + blob_appendf(&sql2, ",%s", aField[i].zName); | |
| 252 | + blob_appendf(&sql3, ",%s", aField[i].zName); | |
| 253 | + } | |
| 254 | + } | |
| 255 | + if( fromTkt ){ | |
| 256 | + db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)" | |
| 257 | + "SELECT %d,:mtime%s FROM ticket WHERE tkt_id=%d", | |
| 258 | + blob_str(&sql2), tktid, blob_str(&sql3), tktid); | |
| 259 | + }else{ | |
| 260 | + db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)" | |
| 261 | + "VALUES(%d,:mtime%s)", | |
| 262 | + blob_str(&sql2), tktid, blob_str(&sql3)); | |
| 263 | + } | |
| 231 | 264 | db_bind_double(&q, ":mtime", p->rDate); |
| 232 | 265 | db_step(&q); |
| 233 | 266 | db_finalize(&q); |
| 234 | 267 | } |
| 235 | 268 | blob_reset(&sql2); |
| 236 | 269 | blob_reset(&sql3); |
| 270 | + fossil_free(aUsed); | |
| 237 | 271 | return tktid; |
| 238 | 272 | } |
| 239 | 273 | |
| 240 | 274 | /* |
| 241 | 275 | ** Rebuild an entire entry in the TICKET table |
| @@ -268,10 +302,11 @@ | ||
| 268 | 302 | } |
| 269 | 303 | createFlag = 0; |
| 270 | 304 | } |
| 271 | 305 | db_finalize(&q); |
| 272 | 306 | } |
| 307 | + | |
| 273 | 308 | |
| 274 | 309 | /* |
| 275 | 310 | ** Create the TH1 interpreter and load the "common" code. |
| 276 | 311 | */ |
| 277 | 312 | void ticket_init(void){ |
| @@ -328,10 +363,32 @@ | ||
| 328 | 363 | ticket_rebuild_entry(zName); |
| 329 | 364 | } |
| 330 | 365 | db_finalize(&q); |
| 331 | 366 | db_end_transaction(0); |
| 332 | 367 | } |
| 368 | + | |
| 369 | +/* | |
| 370 | +** COMMAND: test-ticket-rebuild | |
| 371 | +** | |
| 372 | +** Usage: %fossil test-ticket-rebuild TICKETID|all | |
| 373 | +** | |
| 374 | +** Rebuild the TICKET and TICKETCHNG tables for the given ticket ID | |
| 375 | +** or for ALL. | |
| 376 | +*/ | |
| 377 | +void test_ticket_rebuild(void){ | |
| 378 | + db_find_and_open_repository(0, 0); | |
| 379 | + if( g.argc!=3 ) usage("TICKETID|all"); | |
| 380 | + if( fossil_strcmp(g.argv[2], "all")==0 ){ | |
| 381 | + ticket_rebuild(); | |
| 382 | + }else{ | |
| 383 | + const char *zUuid; | |
| 384 | + zUuid = db_text(0, "SELECT substr(tagname,5) FROM tag" | |
| 385 | + " WHERE tagname GLOB 'tkt-%q*'", g.argv[2]); | |
| 386 | + if( zUuid==0 ) fossil_fatal("no such ticket: %s", g.argv[2]); | |
| 387 | + ticket_rebuild_entry(zUuid); | |
| 388 | + } | |
| 389 | +} | |
| 333 | 390 | |
| 334 | 391 | /* |
| 335 | 392 | ** For trouble-shooting purposes, render a dump of the aField[] table to |
| 336 | 393 | ** the webpage currently under construction. |
| 337 | 394 | */ |
| 338 | 395 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -34,12 +34,15 @@ | |
| 34 | char *zAppend; /* Value to append */ |
| 35 | unsigned mUsed; /* 01: TICKET 02: TICKETCHNG */ |
| 36 | } *aField; |
| 37 | #define USEDBY_TICKET 01 |
| 38 | #define USEDBY_TICKETCHNG 02 |
| 39 | static int haveTicket = 0; /* True if the TICKET table exists */ |
| 40 | static int haveTicketChng = 0; /* True if the TICKETCHNG table exists */ |
| 41 | |
| 42 | /* |
| 43 | ** Compare two entries in aField[] for sorting purposes |
| 44 | */ |
| 45 | static int nameCmpr(const void *a, const void *b){ |
| @@ -74,11 +77,14 @@ | |
| 74 | once = 1; |
| 75 | db_prepare(&q, "PRAGMA table_info(ticket)"); |
| 76 | while( db_step(&q)==SQLITE_ROW ){ |
| 77 | const char *zFieldName = db_column_text(&q, 1); |
| 78 | haveTicket = 1; |
| 79 | if( memcmp(zFieldName,"tkt_",4)==0 ) continue; |
| 80 | if( nField%10==0 ){ |
| 81 | aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) ); |
| 82 | } |
| 83 | aField[nField].zName = mprintf("%s", zFieldName); |
| 84 | aField[nField].mUsed = USEDBY_TICKET; |
| @@ -87,11 +93,14 @@ | |
| 87 | db_finalize(&q); |
| 88 | db_prepare(&q, "PRAGMA table_info(ticketchng)"); |
| 89 | while( db_step(&q)==SQLITE_ROW ){ |
| 90 | const char *zFieldName = db_column_text(&q, 1); |
| 91 | haveTicketChng = 1; |
| 92 | if( memcmp(zFieldName,"tkt_",4)==0 ) continue; |
| 93 | if( (i = fieldId(zFieldName))>=0 ){ |
| 94 | aField[i].mUsed |= USEDBY_TICKETCHNG; |
| 95 | continue; |
| 96 | } |
| 97 | if( nField%10==0 ){ |
| @@ -183,10 +192,11 @@ | |
| 183 | */ |
| 184 | static int ticket_insert(const Manifest *p, int rid, int tktid){ |
| 185 | Blob sql1, sql2, sql3; |
| 186 | Stmt q; |
| 187 | int i, j; |
| 188 | |
| 189 | if( tktid==0 ){ |
| 190 | db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) " |
| 191 | "VALUES(%Q, 0)", p->zTicketUuid); |
| 192 | tktid = db_last_insert_rowid(); |
| @@ -193,22 +203,25 @@ | |
| 193 | } |
| 194 | blob_zero(&sql1); |
| 195 | blob_zero(&sql2); |
| 196 | blob_zero(&sql3); |
| 197 | blob_appendf(&sql1, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime"); |
| 198 | for(i=0; i<p->nField; i++){ |
| 199 | const char *zName = p->aField[i].zName; |
| 200 | if( zName[0]=='+' ){ |
| 201 | zName++; |
| 202 | if( (j = fieldId(zName))<0 ) continue; |
| 203 | if( aField[j].mUsed & USEDBY_TICKET ){ |
| 204 | blob_appendf(&sql1,", %s=coalesce(%s,'') || %Q", |
| 205 | zName, zName, p->aField[i].zValue); |
| 206 | } |
| 207 | }else{ |
| 208 | if( (j = fieldId(zName))<0 ) continue; |
| 209 | if( aField[j].mUsed & USEDBY_TICKET ){ |
| 210 | blob_appendf(&sql1,", %s=%Q", zName, p->aField[i].zValue); |
| 211 | } |
| 212 | } |
| 213 | if( aField[j].mUsed & USEDBY_TICKETCHNG ){ |
| 214 | blob_appendf(&sql2, ",%s", zName); |
| @@ -222,20 +235,41 @@ | |
| 222 | db_prepare(&q, "%s", blob_str(&sql1)); |
| 223 | db_bind_double(&q, ":mtime", p->rDate); |
| 224 | db_step(&q); |
| 225 | db_finalize(&q); |
| 226 | blob_reset(&sql1); |
| 227 | if( blob_size(&sql2)>0 ){ |
| 228 | db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)" |
| 229 | "VALUES(%d,:mtime%s)", |
| 230 | blob_str(&sql2), tktid, blob_str(&sql3)); |
| 231 | db_bind_double(&q, ":mtime", p->rDate); |
| 232 | db_step(&q); |
| 233 | db_finalize(&q); |
| 234 | } |
| 235 | blob_reset(&sql2); |
| 236 | blob_reset(&sql3); |
| 237 | return tktid; |
| 238 | } |
| 239 | |
| 240 | /* |
| 241 | ** Rebuild an entire entry in the TICKET table |
| @@ -268,10 +302,11 @@ | |
| 268 | } |
| 269 | createFlag = 0; |
| 270 | } |
| 271 | db_finalize(&q); |
| 272 | } |
| 273 | |
| 274 | /* |
| 275 | ** Create the TH1 interpreter and load the "common" code. |
| 276 | */ |
| 277 | void ticket_init(void){ |
| @@ -328,10 +363,32 @@ | |
| 328 | ticket_rebuild_entry(zName); |
| 329 | } |
| 330 | db_finalize(&q); |
| 331 | db_end_transaction(0); |
| 332 | } |
| 333 | |
| 334 | /* |
| 335 | ** For trouble-shooting purposes, render a dump of the aField[] table to |
| 336 | ** the webpage currently under construction. |
| 337 | */ |
| 338 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -34,12 +34,15 @@ | |
| 34 | char *zAppend; /* Value to append */ |
| 35 | unsigned mUsed; /* 01: TICKET 02: TICKETCHNG */ |
| 36 | } *aField; |
| 37 | #define USEDBY_TICKET 01 |
| 38 | #define USEDBY_TICKETCHNG 02 |
| 39 | #define USEDBY_BOTH 03 |
| 40 | static u8 haveTicket = 0; /* True if the TICKET table exists */ |
| 41 | static u8 haveTicketCTime = 0; /* True if TICKET.TKT_CTIME exists */ |
| 42 | static u8 haveTicketChng = 0; /* True if the TICKETCHNG table exists */ |
| 43 | static u8 haveTicketChngRid = 0; /* True if TICKETCHNG.TKT_RID exists */ |
| 44 | |
| 45 | /* |
| 46 | ** Compare two entries in aField[] for sorting purposes |
| 47 | */ |
| 48 | static int nameCmpr(const void *a, const void *b){ |
| @@ -74,11 +77,14 @@ | |
| 77 | once = 1; |
| 78 | db_prepare(&q, "PRAGMA table_info(ticket)"); |
| 79 | while( db_step(&q)==SQLITE_ROW ){ |
| 80 | const char *zFieldName = db_column_text(&q, 1); |
| 81 | haveTicket = 1; |
| 82 | if( memcmp(zFieldName,"tkt_",4)==0 ){ |
| 83 | if( strcmp(zFieldName, "tkt_ctime")==0 ) haveTicketCTime = 1; |
| 84 | continue; |
| 85 | } |
| 86 | if( nField%10==0 ){ |
| 87 | aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) ); |
| 88 | } |
| 89 | aField[nField].zName = mprintf("%s", zFieldName); |
| 90 | aField[nField].mUsed = USEDBY_TICKET; |
| @@ -87,11 +93,14 @@ | |
| 93 | db_finalize(&q); |
| 94 | db_prepare(&q, "PRAGMA table_info(ticketchng)"); |
| 95 | while( db_step(&q)==SQLITE_ROW ){ |
| 96 | const char *zFieldName = db_column_text(&q, 1); |
| 97 | haveTicketChng = 1; |
| 98 | if( memcmp(zFieldName,"tkt_",4)==0 ){ |
| 99 | if( strcmp(zFieldName,"tkt_rid")==0 ) haveTicketChngRid = 1; |
| 100 | continue; |
| 101 | } |
| 102 | if( (i = fieldId(zFieldName))>=0 ){ |
| 103 | aField[i].mUsed |= USEDBY_TICKETCHNG; |
| 104 | continue; |
| 105 | } |
| 106 | if( nField%10==0 ){ |
| @@ -183,10 +192,11 @@ | |
| 192 | */ |
| 193 | static int ticket_insert(const Manifest *p, int rid, int tktid){ |
| 194 | Blob sql1, sql2, sql3; |
| 195 | Stmt q; |
| 196 | int i, j; |
| 197 | char *aUsed; |
| 198 | |
| 199 | if( tktid==0 ){ |
| 200 | db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) " |
| 201 | "VALUES(%Q, 0)", p->zTicketUuid); |
| 202 | tktid = db_last_insert_rowid(); |
| @@ -193,22 +203,25 @@ | |
| 203 | } |
| 204 | blob_zero(&sql1); |
| 205 | blob_zero(&sql2); |
| 206 | blob_zero(&sql3); |
| 207 | blob_appendf(&sql1, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime"); |
| 208 | if( haveTicketCTime ){ |
| 209 | blob_appendf(&sql1, ", tkt_ctime=coalesce(tkt_ctime,:mtime)"); |
| 210 | } |
| 211 | aUsed = fossil_malloc( nField ); |
| 212 | memset(aUsed, 0, nField); |
| 213 | for(i=0; i<p->nField; i++){ |
| 214 | const char *zName = p->aField[i].zName; |
| 215 | if( (j = fieldId(zName))<0 ) continue; |
| 216 | aUsed[j] = 1; |
| 217 | if( aField[j].mUsed & USEDBY_TICKET ){ |
| 218 | if( zName[0]=='+' ){ |
| 219 | zName++; |
| 220 | blob_appendf(&sql1,", %s=coalesce(%s,'') || %Q", |
| 221 | zName, zName, p->aField[i].zValue); |
| 222 | }else{ |
| 223 | blob_appendf(&sql1,", %s=%Q", zName, p->aField[i].zValue); |
| 224 | } |
| 225 | } |
| 226 | if( aField[j].mUsed & USEDBY_TICKETCHNG ){ |
| 227 | blob_appendf(&sql2, ",%s", zName); |
| @@ -222,20 +235,41 @@ | |
| 235 | db_prepare(&q, "%s", blob_str(&sql1)); |
| 236 | db_bind_double(&q, ":mtime", p->rDate); |
| 237 | db_step(&q); |
| 238 | db_finalize(&q); |
| 239 | blob_reset(&sql1); |
| 240 | if( blob_size(&sql2)>0 || haveTicketChngRid ){ |
| 241 | int fromTkt = 0; |
| 242 | if( haveTicketChngRid ){ |
| 243 | blob_append(&sql2, ",tkt_rid", -1); |
| 244 | blob_appendf(&sql3, ",%d", rid); |
| 245 | } |
| 246 | for(i=0; i<nField; i++){ |
| 247 | if( aUsed[i]==0 |
| 248 | && (aField[i].mUsed & USEDBY_BOTH)==USEDBY_BOTH |
| 249 | ){ |
| 250 | fromTkt = 1; |
| 251 | blob_appendf(&sql2, ",%s", aField[i].zName); |
| 252 | blob_appendf(&sql3, ",%s", aField[i].zName); |
| 253 | } |
| 254 | } |
| 255 | if( fromTkt ){ |
| 256 | db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)" |
| 257 | "SELECT %d,:mtime%s FROM ticket WHERE tkt_id=%d", |
| 258 | blob_str(&sql2), tktid, blob_str(&sql3), tktid); |
| 259 | }else{ |
| 260 | db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)" |
| 261 | "VALUES(%d,:mtime%s)", |
| 262 | blob_str(&sql2), tktid, blob_str(&sql3)); |
| 263 | } |
| 264 | db_bind_double(&q, ":mtime", p->rDate); |
| 265 | db_step(&q); |
| 266 | db_finalize(&q); |
| 267 | } |
| 268 | blob_reset(&sql2); |
| 269 | blob_reset(&sql3); |
| 270 | fossil_free(aUsed); |
| 271 | return tktid; |
| 272 | } |
| 273 | |
| 274 | /* |
| 275 | ** Rebuild an entire entry in the TICKET table |
| @@ -268,10 +302,11 @@ | |
| 302 | } |
| 303 | createFlag = 0; |
| 304 | } |
| 305 | db_finalize(&q); |
| 306 | } |
| 307 | |
| 308 | |
| 309 | /* |
| 310 | ** Create the TH1 interpreter and load the "common" code. |
| 311 | */ |
| 312 | void ticket_init(void){ |
| @@ -328,10 +363,32 @@ | |
| 363 | ticket_rebuild_entry(zName); |
| 364 | } |
| 365 | db_finalize(&q); |
| 366 | db_end_transaction(0); |
| 367 | } |
| 368 | |
| 369 | /* |
| 370 | ** COMMAND: test-ticket-rebuild |
| 371 | ** |
| 372 | ** Usage: %fossil test-ticket-rebuild TICKETID|all |
| 373 | ** |
| 374 | ** Rebuild the TICKET and TICKETCHNG tables for the given ticket ID |
| 375 | ** or for ALL. |
| 376 | */ |
| 377 | void test_ticket_rebuild(void){ |
| 378 | db_find_and_open_repository(0, 0); |
| 379 | if( g.argc!=3 ) usage("TICKETID|all"); |
| 380 | if( fossil_strcmp(g.argv[2], "all")==0 ){ |
| 381 | ticket_rebuild(); |
| 382 | }else{ |
| 383 | const char *zUuid; |
| 384 | zUuid = db_text(0, "SELECT substr(tagname,5) FROM tag" |
| 385 | " WHERE tagname GLOB 'tkt-%q*'", g.argv[2]); |
| 386 | if( zUuid==0 ) fossil_fatal("no such ticket: %s", g.argv[2]); |
| 387 | ticket_rebuild_entry(zUuid); |
| 388 | } |
| 389 | } |
| 390 | |
| 391 | /* |
| 392 | ** For trouble-shooting purposes, render a dump of the aField[] table to |
| 393 | ** the webpage currently under construction. |
| 394 | */ |
| 395 |
+2
| --- src/tktsetup.c | ||
| +++ src/tktsetup.c | ||
| @@ -67,10 +67,11 @@ | ||
| 67 | 67 | @ CREATE TABLE ticket( |
| 68 | 68 | @ -- Do not change any column that begins with tkt_ |
| 69 | 69 | @ tkt_id INTEGER PRIMARY KEY, |
| 70 | 70 | @ tkt_uuid TEXT UNIQUE, |
| 71 | 71 | @ tkt_mtime DATE, |
| 72 | +@ tkt_ctime DATE, | |
| 72 | 73 | @ -- Add as many fields as required below this line |
| 73 | 74 | @ type TEXT, |
| 74 | 75 | @ status TEXT, |
| 75 | 76 | @ subsystem TEXT, |
| 76 | 77 | @ priority TEXT, |
| @@ -82,10 +83,11 @@ | ||
| 82 | 83 | @ comment TEXT |
| 83 | 84 | @ ); |
| 84 | 85 | @ CREATE TABLE ticketchng( |
| 85 | 86 | @ -- Do not change any column that begins with tkt_ |
| 86 | 87 | @ tkt_id INTEGER REFERENCES ticket, |
| 88 | +@ tkt_rid INTEGER REFERENCES blob, | |
| 87 | 89 | @ tkt_mtime DATE, |
| 88 | 90 | @ -- Add as many fields as required below this line |
| 89 | 91 | @ login TEXT, |
| 90 | 92 | @ username TEXT, |
| 91 | 93 | @ mimetype TEXT, |
| 92 | 94 |
| --- src/tktsetup.c | |
| +++ src/tktsetup.c | |
| @@ -67,10 +67,11 @@ | |
| 67 | @ CREATE TABLE ticket( |
| 68 | @ -- Do not change any column that begins with tkt_ |
| 69 | @ tkt_id INTEGER PRIMARY KEY, |
| 70 | @ tkt_uuid TEXT UNIQUE, |
| 71 | @ tkt_mtime DATE, |
| 72 | @ -- Add as many fields as required below this line |
| 73 | @ type TEXT, |
| 74 | @ status TEXT, |
| 75 | @ subsystem TEXT, |
| 76 | @ priority TEXT, |
| @@ -82,10 +83,11 @@ | |
| 82 | @ comment TEXT |
| 83 | @ ); |
| 84 | @ CREATE TABLE ticketchng( |
| 85 | @ -- Do not change any column that begins with tkt_ |
| 86 | @ tkt_id INTEGER REFERENCES ticket, |
| 87 | @ tkt_mtime DATE, |
| 88 | @ -- Add as many fields as required below this line |
| 89 | @ login TEXT, |
| 90 | @ username TEXT, |
| 91 | @ mimetype TEXT, |
| 92 |
| --- src/tktsetup.c | |
| +++ src/tktsetup.c | |
| @@ -67,10 +67,11 @@ | |
| 67 | @ CREATE TABLE ticket( |
| 68 | @ -- Do not change any column that begins with tkt_ |
| 69 | @ tkt_id INTEGER PRIMARY KEY, |
| 70 | @ tkt_uuid TEXT UNIQUE, |
| 71 | @ tkt_mtime DATE, |
| 72 | @ tkt_ctime DATE, |
| 73 | @ -- Add as many fields as required below this line |
| 74 | @ type TEXT, |
| 75 | @ status TEXT, |
| 76 | @ subsystem TEXT, |
| 77 | @ priority TEXT, |
| @@ -82,10 +83,11 @@ | |
| 83 | @ comment TEXT |
| 84 | @ ); |
| 85 | @ CREATE TABLE ticketchng( |
| 86 | @ -- Do not change any column that begins with tkt_ |
| 87 | @ tkt_id INTEGER REFERENCES ticket, |
| 88 | @ tkt_rid INTEGER REFERENCES blob, |
| 89 | @ tkt_mtime DATE, |
| 90 | @ -- Add as many fields as required below this line |
| 91 | @ login TEXT, |
| 92 | @ username TEXT, |
| 93 | @ mimetype TEXT, |
| 94 |
+2
| --- src/tktsetup.c | ||
| +++ src/tktsetup.c | ||
| @@ -67,10 +67,11 @@ | ||
| 67 | 67 | @ CREATE TABLE ticket( |
| 68 | 68 | @ -- Do not change any column that begins with tkt_ |
| 69 | 69 | @ tkt_id INTEGER PRIMARY KEY, |
| 70 | 70 | @ tkt_uuid TEXT UNIQUE, |
| 71 | 71 | @ tkt_mtime DATE, |
| 72 | +@ tkt_ctime DATE, | |
| 72 | 73 | @ -- Add as many fields as required below this line |
| 73 | 74 | @ type TEXT, |
| 74 | 75 | @ status TEXT, |
| 75 | 76 | @ subsystem TEXT, |
| 76 | 77 | @ priority TEXT, |
| @@ -82,10 +83,11 @@ | ||
| 82 | 83 | @ comment TEXT |
| 83 | 84 | @ ); |
| 84 | 85 | @ CREATE TABLE ticketchng( |
| 85 | 86 | @ -- Do not change any column that begins with tkt_ |
| 86 | 87 | @ tkt_id INTEGER REFERENCES ticket, |
| 88 | +@ tkt_rid INTEGER REFERENCES blob, | |
| 87 | 89 | @ tkt_mtime DATE, |
| 88 | 90 | @ -- Add as many fields as required below this line |
| 89 | 91 | @ login TEXT, |
| 90 | 92 | @ username TEXT, |
| 91 | 93 | @ mimetype TEXT, |
| 92 | 94 |
| --- src/tktsetup.c | |
| +++ src/tktsetup.c | |
| @@ -67,10 +67,11 @@ | |
| 67 | @ CREATE TABLE ticket( |
| 68 | @ -- Do not change any column that begins with tkt_ |
| 69 | @ tkt_id INTEGER PRIMARY KEY, |
| 70 | @ tkt_uuid TEXT UNIQUE, |
| 71 | @ tkt_mtime DATE, |
| 72 | @ -- Add as many fields as required below this line |
| 73 | @ type TEXT, |
| 74 | @ status TEXT, |
| 75 | @ subsystem TEXT, |
| 76 | @ priority TEXT, |
| @@ -82,10 +83,11 @@ | |
| 82 | @ comment TEXT |
| 83 | @ ); |
| 84 | @ CREATE TABLE ticketchng( |
| 85 | @ -- Do not change any column that begins with tkt_ |
| 86 | @ tkt_id INTEGER REFERENCES ticket, |
| 87 | @ tkt_mtime DATE, |
| 88 | @ -- Add as many fields as required below this line |
| 89 | @ login TEXT, |
| 90 | @ username TEXT, |
| 91 | @ mimetype TEXT, |
| 92 |
| --- src/tktsetup.c | |
| +++ src/tktsetup.c | |
| @@ -67,10 +67,11 @@ | |
| 67 | @ CREATE TABLE ticket( |
| 68 | @ -- Do not change any column that begins with tkt_ |
| 69 | @ tkt_id INTEGER PRIMARY KEY, |
| 70 | @ tkt_uuid TEXT UNIQUE, |
| 71 | @ tkt_mtime DATE, |
| 72 | @ tkt_ctime DATE, |
| 73 | @ -- Add as many fields as required below this line |
| 74 | @ type TEXT, |
| 75 | @ status TEXT, |
| 76 | @ subsystem TEXT, |
| 77 | @ priority TEXT, |
| @@ -82,10 +83,11 @@ | |
| 83 | @ comment TEXT |
| 84 | @ ); |
| 85 | @ CREATE TABLE ticketchng( |
| 86 | @ -- Do not change any column that begins with tkt_ |
| 87 | @ tkt_id INTEGER REFERENCES ticket, |
| 88 | @ tkt_rid INTEGER REFERENCES blob, |
| 89 | @ tkt_mtime DATE, |
| 90 | @ -- Add as many fields as required below this line |
| 91 | @ login TEXT, |
| 92 | @ username TEXT, |
| 93 | @ mimetype TEXT, |
| 94 |
+1
-1
| --- src/unicode.c | ||
| +++ src/unicode.c | ||
| @@ -131,11 +131,11 @@ | ||
| 131 | 131 | |
| 132 | 132 | if( c<128 ){ |
| 133 | 133 | return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 ); |
| 134 | 134 | }else if( c<(1<<22) ){ |
| 135 | 135 | unsigned int key = (((unsigned int)c)<<10) | 0x000003FF; |
| 136 | - int iRes; | |
| 136 | + int iRes = 0; | |
| 137 | 137 | int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1; |
| 138 | 138 | int iLo = 0; |
| 139 | 139 | while( iHi>=iLo ){ |
| 140 | 140 | int iTest = (iHi + iLo) / 2; |
| 141 | 141 | if( key >= aEntry[iTest] ){ |
| 142 | 142 |
| --- src/unicode.c | |
| +++ src/unicode.c | |
| @@ -131,11 +131,11 @@ | |
| 131 | |
| 132 | if( c<128 ){ |
| 133 | return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 ); |
| 134 | }else if( c<(1<<22) ){ |
| 135 | unsigned int key = (((unsigned int)c)<<10) | 0x000003FF; |
| 136 | int iRes; |
| 137 | int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1; |
| 138 | int iLo = 0; |
| 139 | while( iHi>=iLo ){ |
| 140 | int iTest = (iHi + iLo) / 2; |
| 141 | if( key >= aEntry[iTest] ){ |
| 142 |
| --- src/unicode.c | |
| +++ src/unicode.c | |
| @@ -131,11 +131,11 @@ | |
| 131 | |
| 132 | if( c<128 ){ |
| 133 | return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 ); |
| 134 | }else if( c<(1<<22) ){ |
| 135 | unsigned int key = (((unsigned int)c)<<10) | 0x000003FF; |
| 136 | int iRes = 0; |
| 137 | int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1; |
| 138 | int iLo = 0; |
| 139 | while( iHi>=iLo ){ |
| 140 | int iTest = (iHi + iLo) / 2; |
| 141 | if( key >= aEntry[iTest] ){ |
| 142 |
+35
-11
| --- src/update.c | ||
| +++ src/update.c | ||
| @@ -667,10 +667,13 @@ | ||
| 667 | 667 | ** Usage: %fossil revert ?-r REVISION? ?FILE ...? |
| 668 | 668 | ** |
| 669 | 669 | ** Revert to the current repository version of FILE, or to |
| 670 | 670 | ** the version associated with baseline REVISION if the -r flag |
| 671 | 671 | ** appears. |
| 672 | +** | |
| 673 | +** If FILE was part of a rename operation, both the original file | |
| 674 | +** and the renamed file are reverted. | |
| 672 | 675 | ** |
| 673 | 676 | ** Revert all files if no file name is provided. |
| 674 | 677 | ** |
| 675 | 678 | ** If a file is reverted accidently, it can be restored using |
| 676 | 679 | ** the "fossil undo" command. |
| @@ -706,23 +709,38 @@ | ||
| 706 | 709 | if( g.argc>2 ){ |
| 707 | 710 | for(i=2; i<g.argc; i++){ |
| 708 | 711 | Blob fname; |
| 709 | 712 | zFile = mprintf("%/", g.argv[i]); |
| 710 | 713 | file_tree_name(zFile, &fname, 1); |
| 711 | - db_multi_exec("REPLACE INTO torevert VALUES(%B)", &fname); | |
| 714 | + db_multi_exec( | |
| 715 | + "REPLACE INTO torevert VALUES(%B);" | |
| 716 | + "INSERT OR IGNORE INTO torevert" | |
| 717 | + " SELECT pathname" | |
| 718 | + " FROM vfile" | |
| 719 | + " WHERE origname IN(%B)" | |
| 720 | + " UNION ALL" | |
| 721 | + " SELECT origname" | |
| 722 | + " FROM vfile" | |
| 723 | + " WHERE pathname IN(%B) AND origname IS NOT NULL;", | |
| 724 | + &fname, &fname, &fname | |
| 725 | + ); | |
| 712 | 726 | blob_reset(&fname); |
| 713 | 727 | } |
| 714 | 728 | }else{ |
| 715 | 729 | int vid; |
| 716 | 730 | vid = db_lget_int("checkout", 0); |
| 717 | 731 | vfile_check_signature(vid, 0); |
| 718 | 732 | db_multi_exec( |
| 719 | 733 | "DELETE FROM vmerge;" |
| 720 | - "INSERT INTO torevert " | |
| 721 | - "SELECT pathname" | |
| 722 | - " FROM vfile " | |
| 723 | - " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;" | |
| 734 | + "INSERT OR IGNORE INTO torevert " | |
| 735 | + " SELECT pathname" | |
| 736 | + " FROM vfile " | |
| 737 | + " WHERE chnged OR deleted OR rid=0 OR pathname!=origname " | |
| 738 | + " UNION ALL " | |
| 739 | + " SELECT origname" | |
| 740 | + " FROM vfile" | |
| 741 | + " WHERE origname!=pathname;" | |
| 724 | 742 | ); |
| 725 | 743 | } |
| 726 | 744 | blob_zero(&record); |
| 727 | 745 | db_prepare(&q, "SELECT name FROM torevert"); |
| 728 | 746 | if( zRevision==0 ){ |
| @@ -736,18 +754,25 @@ | ||
| 736 | 754 | zFile = db_column_text(&q, 0); |
| 737 | 755 | zFull = mprintf("%/%/", g.zLocalRoot, zFile); |
| 738 | 756 | errCode = historical_version_of_file(zRevision, zFile, &record, |
| 739 | 757 | &isLink, &isExe, 0, 2); |
| 740 | 758 | if( errCode==2 ){ |
| 741 | - if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile)==0 ){ | |
| 759 | + if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q OR origname=%Q", | |
| 760 | + zFile, zFile)==0 ){ | |
| 742 | 761 | fossil_print("UNMANAGE: %s\n", zFile); |
| 743 | 762 | }else{ |
| 744 | 763 | undo_save(zFile); |
| 745 | 764 | file_delete(zFull); |
| 746 | 765 | fossil_print("DELETE: %s\n", zFile); |
| 747 | 766 | } |
| 748 | - db_multi_exec("DELETE FROM vfile WHERE pathname=%Q", zFile); | |
| 767 | + db_multi_exec( | |
| 768 | + "UPDATE vfile" | |
| 769 | + " SET pathname=origname, origname=NULL" | |
| 770 | + " WHERE pathname=%Q AND origname!=pathname AND origname IS NOT NULL;" | |
| 771 | + "DELETE FROM vfile WHERE pathname=%Q", | |
| 772 | + zFile, zFile | |
| 773 | + ); | |
| 749 | 774 | }else{ |
| 750 | 775 | sqlite3_int64 mtime; |
| 751 | 776 | undo_save(zFile); |
| 752 | 777 | if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(zFull)) ){ |
| 753 | 778 | file_delete(zFull); |
| @@ -760,18 +785,17 @@ | ||
| 760 | 785 | file_wd_setexe(zFull, isExe); |
| 761 | 786 | fossil_print("REVERTED: %s\n", zFile); |
| 762 | 787 | mtime = file_wd_mtime(zFull); |
| 763 | 788 | db_multi_exec( |
| 764 | 789 | "UPDATE vfile" |
| 765 | - " SET mtime=%lld, chnged=0, deleted=0, isexe=%d, islink=%d,mrid=rid," | |
| 766 | - " pathname=coalesce(origname,pathname), origname=NULL" | |
| 767 | - " WHERE pathname=%Q", | |
| 768 | - mtime, isExe, isLink, zFile | |
| 790 | + " SET mtime=%lld, chnged=0, deleted=0, isexe=%d, islink=%d,mrid=rid" | |
| 791 | + " WHERE pathname=%Q OR origname=%Q", | |
| 792 | + mtime, isExe, isLink, zFile, zFile | |
| 769 | 793 | ); |
| 770 | 794 | } |
| 771 | 795 | blob_reset(&record); |
| 772 | 796 | free(zFull); |
| 773 | 797 | } |
| 774 | 798 | db_finalize(&q); |
| 775 | 799 | undo_finish(); |
| 776 | 800 | db_end_transaction(0); |
| 777 | 801 | } |
| 778 | 802 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -667,10 +667,13 @@ | |
| 667 | ** Usage: %fossil revert ?-r REVISION? ?FILE ...? |
| 668 | ** |
| 669 | ** Revert to the current repository version of FILE, or to |
| 670 | ** the version associated with baseline REVISION if the -r flag |
| 671 | ** appears. |
| 672 | ** |
| 673 | ** Revert all files if no file name is provided. |
| 674 | ** |
| 675 | ** If a file is reverted accidently, it can be restored using |
| 676 | ** the "fossil undo" command. |
| @@ -706,23 +709,38 @@ | |
| 706 | if( g.argc>2 ){ |
| 707 | for(i=2; i<g.argc; i++){ |
| 708 | Blob fname; |
| 709 | zFile = mprintf("%/", g.argv[i]); |
| 710 | file_tree_name(zFile, &fname, 1); |
| 711 | db_multi_exec("REPLACE INTO torevert VALUES(%B)", &fname); |
| 712 | blob_reset(&fname); |
| 713 | } |
| 714 | }else{ |
| 715 | int vid; |
| 716 | vid = db_lget_int("checkout", 0); |
| 717 | vfile_check_signature(vid, 0); |
| 718 | db_multi_exec( |
| 719 | "DELETE FROM vmerge;" |
| 720 | "INSERT INTO torevert " |
| 721 | "SELECT pathname" |
| 722 | " FROM vfile " |
| 723 | " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;" |
| 724 | ); |
| 725 | } |
| 726 | blob_zero(&record); |
| 727 | db_prepare(&q, "SELECT name FROM torevert"); |
| 728 | if( zRevision==0 ){ |
| @@ -736,18 +754,25 @@ | |
| 736 | zFile = db_column_text(&q, 0); |
| 737 | zFull = mprintf("%/%/", g.zLocalRoot, zFile); |
| 738 | errCode = historical_version_of_file(zRevision, zFile, &record, |
| 739 | &isLink, &isExe, 0, 2); |
| 740 | if( errCode==2 ){ |
| 741 | if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile)==0 ){ |
| 742 | fossil_print("UNMANAGE: %s\n", zFile); |
| 743 | }else{ |
| 744 | undo_save(zFile); |
| 745 | file_delete(zFull); |
| 746 | fossil_print("DELETE: %s\n", zFile); |
| 747 | } |
| 748 | db_multi_exec("DELETE FROM vfile WHERE pathname=%Q", zFile); |
| 749 | }else{ |
| 750 | sqlite3_int64 mtime; |
| 751 | undo_save(zFile); |
| 752 | if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(zFull)) ){ |
| 753 | file_delete(zFull); |
| @@ -760,18 +785,17 @@ | |
| 760 | file_wd_setexe(zFull, isExe); |
| 761 | fossil_print("REVERTED: %s\n", zFile); |
| 762 | mtime = file_wd_mtime(zFull); |
| 763 | db_multi_exec( |
| 764 | "UPDATE vfile" |
| 765 | " SET mtime=%lld, chnged=0, deleted=0, isexe=%d, islink=%d,mrid=rid," |
| 766 | " pathname=coalesce(origname,pathname), origname=NULL" |
| 767 | " WHERE pathname=%Q", |
| 768 | mtime, isExe, isLink, zFile |
| 769 | ); |
| 770 | } |
| 771 | blob_reset(&record); |
| 772 | free(zFull); |
| 773 | } |
| 774 | db_finalize(&q); |
| 775 | undo_finish(); |
| 776 | db_end_transaction(0); |
| 777 | } |
| 778 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -667,10 +667,13 @@ | |
| 667 | ** Usage: %fossil revert ?-r REVISION? ?FILE ...? |
| 668 | ** |
| 669 | ** Revert to the current repository version of FILE, or to |
| 670 | ** the version associated with baseline REVISION if the -r flag |
| 671 | ** appears. |
| 672 | ** |
| 673 | ** If FILE was part of a rename operation, both the original file |
| 674 | ** and the renamed file are reverted. |
| 675 | ** |
| 676 | ** Revert all files if no file name is provided. |
| 677 | ** |
| 678 | ** If a file is reverted accidently, it can be restored using |
| 679 | ** the "fossil undo" command. |
| @@ -706,23 +709,38 @@ | |
| 709 | if( g.argc>2 ){ |
| 710 | for(i=2; i<g.argc; i++){ |
| 711 | Blob fname; |
| 712 | zFile = mprintf("%/", g.argv[i]); |
| 713 | file_tree_name(zFile, &fname, 1); |
| 714 | db_multi_exec( |
| 715 | "REPLACE INTO torevert VALUES(%B);" |
| 716 | "INSERT OR IGNORE INTO torevert" |
| 717 | " SELECT pathname" |
| 718 | " FROM vfile" |
| 719 | " WHERE origname IN(%B)" |
| 720 | " UNION ALL" |
| 721 | " SELECT origname" |
| 722 | " FROM vfile" |
| 723 | " WHERE pathname IN(%B) AND origname IS NOT NULL;", |
| 724 | &fname, &fname, &fname |
| 725 | ); |
| 726 | blob_reset(&fname); |
| 727 | } |
| 728 | }else{ |
| 729 | int vid; |
| 730 | vid = db_lget_int("checkout", 0); |
| 731 | vfile_check_signature(vid, 0); |
| 732 | db_multi_exec( |
| 733 | "DELETE FROM vmerge;" |
| 734 | "INSERT OR IGNORE INTO torevert " |
| 735 | " SELECT pathname" |
| 736 | " FROM vfile " |
| 737 | " WHERE chnged OR deleted OR rid=0 OR pathname!=origname " |
| 738 | " UNION ALL " |
| 739 | " SELECT origname" |
| 740 | " FROM vfile" |
| 741 | " WHERE origname!=pathname;" |
| 742 | ); |
| 743 | } |
| 744 | blob_zero(&record); |
| 745 | db_prepare(&q, "SELECT name FROM torevert"); |
| 746 | if( zRevision==0 ){ |
| @@ -736,18 +754,25 @@ | |
| 754 | zFile = db_column_text(&q, 0); |
| 755 | zFull = mprintf("%/%/", g.zLocalRoot, zFile); |
| 756 | errCode = historical_version_of_file(zRevision, zFile, &record, |
| 757 | &isLink, &isExe, 0, 2); |
| 758 | if( errCode==2 ){ |
| 759 | if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q OR origname=%Q", |
| 760 | zFile, zFile)==0 ){ |
| 761 | fossil_print("UNMANAGE: %s\n", zFile); |
| 762 | }else{ |
| 763 | undo_save(zFile); |
| 764 | file_delete(zFull); |
| 765 | fossil_print("DELETE: %s\n", zFile); |
| 766 | } |
| 767 | db_multi_exec( |
| 768 | "UPDATE vfile" |
| 769 | " SET pathname=origname, origname=NULL" |
| 770 | " WHERE pathname=%Q AND origname!=pathname AND origname IS NOT NULL;" |
| 771 | "DELETE FROM vfile WHERE pathname=%Q", |
| 772 | zFile, zFile |
| 773 | ); |
| 774 | }else{ |
| 775 | sqlite3_int64 mtime; |
| 776 | undo_save(zFile); |
| 777 | if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(zFull)) ){ |
| 778 | file_delete(zFull); |
| @@ -760,18 +785,17 @@ | |
| 785 | file_wd_setexe(zFull, isExe); |
| 786 | fossil_print("REVERTED: %s\n", zFile); |
| 787 | mtime = file_wd_mtime(zFull); |
| 788 | db_multi_exec( |
| 789 | "UPDATE vfile" |
| 790 | " SET mtime=%lld, chnged=0, deleted=0, isexe=%d, islink=%d,mrid=rid" |
| 791 | " WHERE pathname=%Q OR origname=%Q", |
| 792 | mtime, isExe, isLink, zFile, zFile |
| 793 | ); |
| 794 | } |
| 795 | blob_reset(&record); |
| 796 | free(zFull); |
| 797 | } |
| 798 | db_finalize(&q); |
| 799 | undo_finish(); |
| 800 | db_end_transaction(0); |
| 801 | } |
| 802 |
+35
-11
| --- src/update.c | ||
| +++ src/update.c | ||
| @@ -667,10 +667,13 @@ | ||
| 667 | 667 | ** Usage: %fossil revert ?-r REVISION? ?FILE ...? |
| 668 | 668 | ** |
| 669 | 669 | ** Revert to the current repository version of FILE, or to |
| 670 | 670 | ** the version associated with baseline REVISION if the -r flag |
| 671 | 671 | ** appears. |
| 672 | +** | |
| 673 | +** If FILE was part of a rename operation, both the original file | |
| 674 | +** and the renamed file are reverted. | |
| 672 | 675 | ** |
| 673 | 676 | ** Revert all files if no file name is provided. |
| 674 | 677 | ** |
| 675 | 678 | ** If a file is reverted accidently, it can be restored using |
| 676 | 679 | ** the "fossil undo" command. |
| @@ -706,23 +709,38 @@ | ||
| 706 | 709 | if( g.argc>2 ){ |
| 707 | 710 | for(i=2; i<g.argc; i++){ |
| 708 | 711 | Blob fname; |
| 709 | 712 | zFile = mprintf("%/", g.argv[i]); |
| 710 | 713 | file_tree_name(zFile, &fname, 1); |
| 711 | - db_multi_exec("REPLACE INTO torevert VALUES(%B)", &fname); | |
| 714 | + db_multi_exec( | |
| 715 | + "REPLACE INTO torevert VALUES(%B);" | |
| 716 | + "INSERT OR IGNORE INTO torevert" | |
| 717 | + " SELECT pathname" | |
| 718 | + " FROM vfile" | |
| 719 | + " WHERE origname IN(%B)" | |
| 720 | + " UNION ALL" | |
| 721 | + " SELECT origname" | |
| 722 | + " FROM vfile" | |
| 723 | + " WHERE pathname IN(%B) AND origname IS NOT NULL;", | |
| 724 | + &fname, &fname, &fname | |
| 725 | + ); | |
| 712 | 726 | blob_reset(&fname); |
| 713 | 727 | } |
| 714 | 728 | }else{ |
| 715 | 729 | int vid; |
| 716 | 730 | vid = db_lget_int("checkout", 0); |
| 717 | 731 | vfile_check_signature(vid, 0); |
| 718 | 732 | db_multi_exec( |
| 719 | 733 | "DELETE FROM vmerge;" |
| 720 | - "INSERT INTO torevert " | |
| 721 | - "SELECT pathname" | |
| 722 | - " FROM vfile " | |
| 723 | - " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;" | |
| 734 | + "INSERT OR IGNORE INTO torevert " | |
| 735 | + " SELECT pathname" | |
| 736 | + " FROM vfile " | |
| 737 | + " WHERE chnged OR deleted OR rid=0 OR pathname!=origname " | |
| 738 | + " UNION ALL " | |
| 739 | + " SELECT origname" | |
| 740 | + " FROM vfile" | |
| 741 | + " WHERE origname!=pathname;" | |
| 724 | 742 | ); |
| 725 | 743 | } |
| 726 | 744 | blob_zero(&record); |
| 727 | 745 | db_prepare(&q, "SELECT name FROM torevert"); |
| 728 | 746 | if( zRevision==0 ){ |
| @@ -736,18 +754,25 @@ | ||
| 736 | 754 | zFile = db_column_text(&q, 0); |
| 737 | 755 | zFull = mprintf("%/%/", g.zLocalRoot, zFile); |
| 738 | 756 | errCode = historical_version_of_file(zRevision, zFile, &record, |
| 739 | 757 | &isLink, &isExe, 0, 2); |
| 740 | 758 | if( errCode==2 ){ |
| 741 | - if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile)==0 ){ | |
| 759 | + if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q OR origname=%Q", | |
| 760 | + zFile, zFile)==0 ){ | |
| 742 | 761 | fossil_print("UNMANAGE: %s\n", zFile); |
| 743 | 762 | }else{ |
| 744 | 763 | undo_save(zFile); |
| 745 | 764 | file_delete(zFull); |
| 746 | 765 | fossil_print("DELETE: %s\n", zFile); |
| 747 | 766 | } |
| 748 | - db_multi_exec("DELETE FROM vfile WHERE pathname=%Q", zFile); | |
| 767 | + db_multi_exec( | |
| 768 | + "UPDATE vfile" | |
| 769 | + " SET pathname=origname, origname=NULL" | |
| 770 | + " WHERE pathname=%Q AND origname!=pathname AND origname IS NOT NULL;" | |
| 771 | + "DELETE FROM vfile WHERE pathname=%Q", | |
| 772 | + zFile, zFile | |
| 773 | + ); | |
| 749 | 774 | }else{ |
| 750 | 775 | sqlite3_int64 mtime; |
| 751 | 776 | undo_save(zFile); |
| 752 | 777 | if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(zFull)) ){ |
| 753 | 778 | file_delete(zFull); |
| @@ -760,18 +785,17 @@ | ||
| 760 | 785 | file_wd_setexe(zFull, isExe); |
| 761 | 786 | fossil_print("REVERTED: %s\n", zFile); |
| 762 | 787 | mtime = file_wd_mtime(zFull); |
| 763 | 788 | db_multi_exec( |
| 764 | 789 | "UPDATE vfile" |
| 765 | - " SET mtime=%lld, chnged=0, deleted=0, isexe=%d, islink=%d,mrid=rid," | |
| 766 | - " pathname=coalesce(origname,pathname), origname=NULL" | |
| 767 | - " WHERE pathname=%Q", | |
| 768 | - mtime, isExe, isLink, zFile | |
| 790 | + " SET mtime=%lld, chnged=0, deleted=0, isexe=%d, islink=%d,mrid=rid" | |
| 791 | + " WHERE pathname=%Q OR origname=%Q", | |
| 792 | + mtime, isExe, isLink, zFile, zFile | |
| 769 | 793 | ); |
| 770 | 794 | } |
| 771 | 795 | blob_reset(&record); |
| 772 | 796 | free(zFull); |
| 773 | 797 | } |
| 774 | 798 | db_finalize(&q); |
| 775 | 799 | undo_finish(); |
| 776 | 800 | db_end_transaction(0); |
| 777 | 801 | } |
| 778 | 802 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -667,10 +667,13 @@ | |
| 667 | ** Usage: %fossil revert ?-r REVISION? ?FILE ...? |
| 668 | ** |
| 669 | ** Revert to the current repository version of FILE, or to |
| 670 | ** the version associated with baseline REVISION if the -r flag |
| 671 | ** appears. |
| 672 | ** |
| 673 | ** Revert all files if no file name is provided. |
| 674 | ** |
| 675 | ** If a file is reverted accidently, it can be restored using |
| 676 | ** the "fossil undo" command. |
| @@ -706,23 +709,38 @@ | |
| 706 | if( g.argc>2 ){ |
| 707 | for(i=2; i<g.argc; i++){ |
| 708 | Blob fname; |
| 709 | zFile = mprintf("%/", g.argv[i]); |
| 710 | file_tree_name(zFile, &fname, 1); |
| 711 | db_multi_exec("REPLACE INTO torevert VALUES(%B)", &fname); |
| 712 | blob_reset(&fname); |
| 713 | } |
| 714 | }else{ |
| 715 | int vid; |
| 716 | vid = db_lget_int("checkout", 0); |
| 717 | vfile_check_signature(vid, 0); |
| 718 | db_multi_exec( |
| 719 | "DELETE FROM vmerge;" |
| 720 | "INSERT INTO torevert " |
| 721 | "SELECT pathname" |
| 722 | " FROM vfile " |
| 723 | " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;" |
| 724 | ); |
| 725 | } |
| 726 | blob_zero(&record); |
| 727 | db_prepare(&q, "SELECT name FROM torevert"); |
| 728 | if( zRevision==0 ){ |
| @@ -736,18 +754,25 @@ | |
| 736 | zFile = db_column_text(&q, 0); |
| 737 | zFull = mprintf("%/%/", g.zLocalRoot, zFile); |
| 738 | errCode = historical_version_of_file(zRevision, zFile, &record, |
| 739 | &isLink, &isExe, 0, 2); |
| 740 | if( errCode==2 ){ |
| 741 | if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile)==0 ){ |
| 742 | fossil_print("UNMANAGE: %s\n", zFile); |
| 743 | }else{ |
| 744 | undo_save(zFile); |
| 745 | file_delete(zFull); |
| 746 | fossil_print("DELETE: %s\n", zFile); |
| 747 | } |
| 748 | db_multi_exec("DELETE FROM vfile WHERE pathname=%Q", zFile); |
| 749 | }else{ |
| 750 | sqlite3_int64 mtime; |
| 751 | undo_save(zFile); |
| 752 | if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(zFull)) ){ |
| 753 | file_delete(zFull); |
| @@ -760,18 +785,17 @@ | |
| 760 | file_wd_setexe(zFull, isExe); |
| 761 | fossil_print("REVERTED: %s\n", zFile); |
| 762 | mtime = file_wd_mtime(zFull); |
| 763 | db_multi_exec( |
| 764 | "UPDATE vfile" |
| 765 | " SET mtime=%lld, chnged=0, deleted=0, isexe=%d, islink=%d,mrid=rid," |
| 766 | " pathname=coalesce(origname,pathname), origname=NULL" |
| 767 | " WHERE pathname=%Q", |
| 768 | mtime, isExe, isLink, zFile |
| 769 | ); |
| 770 | } |
| 771 | blob_reset(&record); |
| 772 | free(zFull); |
| 773 | } |
| 774 | db_finalize(&q); |
| 775 | undo_finish(); |
| 776 | db_end_transaction(0); |
| 777 | } |
| 778 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -667,10 +667,13 @@ | |
| 667 | ** Usage: %fossil revert ?-r REVISION? ?FILE ...? |
| 668 | ** |
| 669 | ** Revert to the current repository version of FILE, or to |
| 670 | ** the version associated with baseline REVISION if the -r flag |
| 671 | ** appears. |
| 672 | ** |
| 673 | ** If FILE was part of a rename operation, both the original file |
| 674 | ** and the renamed file are reverted. |
| 675 | ** |
| 676 | ** Revert all files if no file name is provided. |
| 677 | ** |
| 678 | ** If a file is reverted accidently, it can be restored using |
| 679 | ** the "fossil undo" command. |
| @@ -706,23 +709,38 @@ | |
| 709 | if( g.argc>2 ){ |
| 710 | for(i=2; i<g.argc; i++){ |
| 711 | Blob fname; |
| 712 | zFile = mprintf("%/", g.argv[i]); |
| 713 | file_tree_name(zFile, &fname, 1); |
| 714 | db_multi_exec( |
| 715 | "REPLACE INTO torevert VALUES(%B);" |
| 716 | "INSERT OR IGNORE INTO torevert" |
| 717 | " SELECT pathname" |
| 718 | " FROM vfile" |
| 719 | " WHERE origname IN(%B)" |
| 720 | " UNION ALL" |
| 721 | " SELECT origname" |
| 722 | " FROM vfile" |
| 723 | " WHERE pathname IN(%B) AND origname IS NOT NULL;", |
| 724 | &fname, &fname, &fname |
| 725 | ); |
| 726 | blob_reset(&fname); |
| 727 | } |
| 728 | }else{ |
| 729 | int vid; |
| 730 | vid = db_lget_int("checkout", 0); |
| 731 | vfile_check_signature(vid, 0); |
| 732 | db_multi_exec( |
| 733 | "DELETE FROM vmerge;" |
| 734 | "INSERT OR IGNORE INTO torevert " |
| 735 | " SELECT pathname" |
| 736 | " FROM vfile " |
| 737 | " WHERE chnged OR deleted OR rid=0 OR pathname!=origname " |
| 738 | " UNION ALL " |
| 739 | " SELECT origname" |
| 740 | " FROM vfile" |
| 741 | " WHERE origname!=pathname;" |
| 742 | ); |
| 743 | } |
| 744 | blob_zero(&record); |
| 745 | db_prepare(&q, "SELECT name FROM torevert"); |
| 746 | if( zRevision==0 ){ |
| @@ -736,18 +754,25 @@ | |
| 754 | zFile = db_column_text(&q, 0); |
| 755 | zFull = mprintf("%/%/", g.zLocalRoot, zFile); |
| 756 | errCode = historical_version_of_file(zRevision, zFile, &record, |
| 757 | &isLink, &isExe, 0, 2); |
| 758 | if( errCode==2 ){ |
| 759 | if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q OR origname=%Q", |
| 760 | zFile, zFile)==0 ){ |
| 761 | fossil_print("UNMANAGE: %s\n", zFile); |
| 762 | }else{ |
| 763 | undo_save(zFile); |
| 764 | file_delete(zFull); |
| 765 | fossil_print("DELETE: %s\n", zFile); |
| 766 | } |
| 767 | db_multi_exec( |
| 768 | "UPDATE vfile" |
| 769 | " SET pathname=origname, origname=NULL" |
| 770 | " WHERE pathname=%Q AND origname!=pathname AND origname IS NOT NULL;" |
| 771 | "DELETE FROM vfile WHERE pathname=%Q", |
| 772 | zFile, zFile |
| 773 | ); |
| 774 | }else{ |
| 775 | sqlite3_int64 mtime; |
| 776 | undo_save(zFile); |
| 777 | if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(zFull)) ){ |
| 778 | file_delete(zFull); |
| @@ -760,18 +785,17 @@ | |
| 785 | file_wd_setexe(zFull, isExe); |
| 786 | fossil_print("REVERTED: %s\n", zFile); |
| 787 | mtime = file_wd_mtime(zFull); |
| 788 | db_multi_exec( |
| 789 | "UPDATE vfile" |
| 790 | " SET mtime=%lld, chnged=0, deleted=0, isexe=%d, islink=%d,mrid=rid" |
| 791 | " WHERE pathname=%Q OR origname=%Q", |
| 792 | mtime, isExe, isLink, zFile, zFile |
| 793 | ); |
| 794 | } |
| 795 | blob_reset(&record); |
| 796 | free(zFull); |
| 797 | } |
| 798 | db_finalize(&q); |
| 799 | undo_finish(); |
| 800 | db_end_transaction(0); |
| 801 | } |
| 802 |
+10
-1
| --- src/url.c | ||
| +++ src/url.c | ||
| @@ -63,10 +63,11 @@ | ||
| 63 | 63 | || strncmp(zUrl, "ssh://", 6)==0 |
| 64 | 64 | ){ |
| 65 | 65 | int iStart; |
| 66 | 66 | char *zLogin; |
| 67 | 67 | char *zExe; |
| 68 | + char cQuerySep = '?'; | |
| 68 | 69 | |
| 69 | 70 | g.urlIsFile = 0; |
| 70 | 71 | if( zUrl[4]=='s' ){ |
| 71 | 72 | g.urlIsHttps = 1; |
| 72 | 73 | g.urlProtocol = "https"; |
| @@ -75,10 +76,11 @@ | ||
| 75 | 76 | }else if( zUrl[0]=='s' ){ |
| 76 | 77 | g.urlIsSsh = 1; |
| 77 | 78 | g.urlProtocol = "ssh"; |
| 78 | 79 | g.urlDfltPort = 22; |
| 79 | 80 | g.urlFossil = "fossil"; |
| 81 | + g.urlShell = 0; | |
| 80 | 82 | iStart = 6; |
| 81 | 83 | }else{ |
| 82 | 84 | g.urlIsHttps = 0; |
| 83 | 85 | g.urlProtocol = "http"; |
| 84 | 86 | g.urlDfltPort = 80; |
| @@ -144,11 +146,18 @@ | ||
| 144 | 146 | i++; |
| 145 | 147 | } |
| 146 | 148 | if( fossil_strcmp(zName,"fossil")==0 ){ |
| 147 | 149 | g.urlFossil = zValue; |
| 148 | 150 | dehttpize(g.urlFossil); |
| 149 | - zExe = mprintf("?fossil=%T", g.urlFossil); | |
| 151 | + zExe = mprintf("%cfossil=%T", cQuerySep, g.urlFossil); | |
| 152 | + cQuerySep = '&'; | |
| 153 | + } | |
| 154 | + if( fossil_strcmp(zName,"shell")==0 ){ | |
| 155 | + g.urlShell = zValue; | |
| 156 | + dehttpize(g.urlShell); | |
| 157 | + zExe = mprintf("%cshell=%T", cQuerySep, g.urlFossil); | |
| 158 | + cQuerySep = '&'; | |
| 150 | 159 | } |
| 151 | 160 | } |
| 152 | 161 | |
| 153 | 162 | dehttpize(g.urlPath); |
| 154 | 163 | if( g.urlDfltPort==g.urlPort ){ |
| 155 | 164 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -63,10 +63,11 @@ | |
| 63 | || strncmp(zUrl, "ssh://", 6)==0 |
| 64 | ){ |
| 65 | int iStart; |
| 66 | char *zLogin; |
| 67 | char *zExe; |
| 68 | |
| 69 | g.urlIsFile = 0; |
| 70 | if( zUrl[4]=='s' ){ |
| 71 | g.urlIsHttps = 1; |
| 72 | g.urlProtocol = "https"; |
| @@ -75,10 +76,11 @@ | |
| 75 | }else if( zUrl[0]=='s' ){ |
| 76 | g.urlIsSsh = 1; |
| 77 | g.urlProtocol = "ssh"; |
| 78 | g.urlDfltPort = 22; |
| 79 | g.urlFossil = "fossil"; |
| 80 | iStart = 6; |
| 81 | }else{ |
| 82 | g.urlIsHttps = 0; |
| 83 | g.urlProtocol = "http"; |
| 84 | g.urlDfltPort = 80; |
| @@ -144,11 +146,18 @@ | |
| 144 | i++; |
| 145 | } |
| 146 | if( fossil_strcmp(zName,"fossil")==0 ){ |
| 147 | g.urlFossil = zValue; |
| 148 | dehttpize(g.urlFossil); |
| 149 | zExe = mprintf("?fossil=%T", g.urlFossil); |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | dehttpize(g.urlPath); |
| 154 | if( g.urlDfltPort==g.urlPort ){ |
| 155 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -63,10 +63,11 @@ | |
| 63 | || strncmp(zUrl, "ssh://", 6)==0 |
| 64 | ){ |
| 65 | int iStart; |
| 66 | char *zLogin; |
| 67 | char *zExe; |
| 68 | char cQuerySep = '?'; |
| 69 | |
| 70 | g.urlIsFile = 0; |
| 71 | if( zUrl[4]=='s' ){ |
| 72 | g.urlIsHttps = 1; |
| 73 | g.urlProtocol = "https"; |
| @@ -75,10 +76,11 @@ | |
| 76 | }else if( zUrl[0]=='s' ){ |
| 77 | g.urlIsSsh = 1; |
| 78 | g.urlProtocol = "ssh"; |
| 79 | g.urlDfltPort = 22; |
| 80 | g.urlFossil = "fossil"; |
| 81 | g.urlShell = 0; |
| 82 | iStart = 6; |
| 83 | }else{ |
| 84 | g.urlIsHttps = 0; |
| 85 | g.urlProtocol = "http"; |
| 86 | g.urlDfltPort = 80; |
| @@ -144,11 +146,18 @@ | |
| 146 | i++; |
| 147 | } |
| 148 | if( fossil_strcmp(zName,"fossil")==0 ){ |
| 149 | g.urlFossil = zValue; |
| 150 | dehttpize(g.urlFossil); |
| 151 | zExe = mprintf("%cfossil=%T", cQuerySep, g.urlFossil); |
| 152 | cQuerySep = '&'; |
| 153 | } |
| 154 | if( fossil_strcmp(zName,"shell")==0 ){ |
| 155 | g.urlShell = zValue; |
| 156 | dehttpize(g.urlShell); |
| 157 | zExe = mprintf("%cshell=%T", cQuerySep, g.urlFossil); |
| 158 | cQuerySep = '&'; |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | dehttpize(g.urlPath); |
| 163 | if( g.urlDfltPort==g.urlPort ){ |
| 164 |
+10
-1
| --- src/url.c | ||
| +++ src/url.c | ||
| @@ -63,10 +63,11 @@ | ||
| 63 | 63 | || strncmp(zUrl, "ssh://", 6)==0 |
| 64 | 64 | ){ |
| 65 | 65 | int iStart; |
| 66 | 66 | char *zLogin; |
| 67 | 67 | char *zExe; |
| 68 | + char cQuerySep = '?'; | |
| 68 | 69 | |
| 69 | 70 | g.urlIsFile = 0; |
| 70 | 71 | if( zUrl[4]=='s' ){ |
| 71 | 72 | g.urlIsHttps = 1; |
| 72 | 73 | g.urlProtocol = "https"; |
| @@ -75,10 +76,11 @@ | ||
| 75 | 76 | }else if( zUrl[0]=='s' ){ |
| 76 | 77 | g.urlIsSsh = 1; |
| 77 | 78 | g.urlProtocol = "ssh"; |
| 78 | 79 | g.urlDfltPort = 22; |
| 79 | 80 | g.urlFossil = "fossil"; |
| 81 | + g.urlShell = 0; | |
| 80 | 82 | iStart = 6; |
| 81 | 83 | }else{ |
| 82 | 84 | g.urlIsHttps = 0; |
| 83 | 85 | g.urlProtocol = "http"; |
| 84 | 86 | g.urlDfltPort = 80; |
| @@ -144,11 +146,18 @@ | ||
| 144 | 146 | i++; |
| 145 | 147 | } |
| 146 | 148 | if( fossil_strcmp(zName,"fossil")==0 ){ |
| 147 | 149 | g.urlFossil = zValue; |
| 148 | 150 | dehttpize(g.urlFossil); |
| 149 | - zExe = mprintf("?fossil=%T", g.urlFossil); | |
| 151 | + zExe = mprintf("%cfossil=%T", cQuerySep, g.urlFossil); | |
| 152 | + cQuerySep = '&'; | |
| 153 | + } | |
| 154 | + if( fossil_strcmp(zName,"shell")==0 ){ | |
| 155 | + g.urlShell = zValue; | |
| 156 | + dehttpize(g.urlShell); | |
| 157 | + zExe = mprintf("%cshell=%T", cQuerySep, g.urlFossil); | |
| 158 | + cQuerySep = '&'; | |
| 150 | 159 | } |
| 151 | 160 | } |
| 152 | 161 | |
| 153 | 162 | dehttpize(g.urlPath); |
| 154 | 163 | if( g.urlDfltPort==g.urlPort ){ |
| 155 | 164 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -63,10 +63,11 @@ | |
| 63 | || strncmp(zUrl, "ssh://", 6)==0 |
| 64 | ){ |
| 65 | int iStart; |
| 66 | char *zLogin; |
| 67 | char *zExe; |
| 68 | |
| 69 | g.urlIsFile = 0; |
| 70 | if( zUrl[4]=='s' ){ |
| 71 | g.urlIsHttps = 1; |
| 72 | g.urlProtocol = "https"; |
| @@ -75,10 +76,11 @@ | |
| 75 | }else if( zUrl[0]=='s' ){ |
| 76 | g.urlIsSsh = 1; |
| 77 | g.urlProtocol = "ssh"; |
| 78 | g.urlDfltPort = 22; |
| 79 | g.urlFossil = "fossil"; |
| 80 | iStart = 6; |
| 81 | }else{ |
| 82 | g.urlIsHttps = 0; |
| 83 | g.urlProtocol = "http"; |
| 84 | g.urlDfltPort = 80; |
| @@ -144,11 +146,18 @@ | |
| 144 | i++; |
| 145 | } |
| 146 | if( fossil_strcmp(zName,"fossil")==0 ){ |
| 147 | g.urlFossil = zValue; |
| 148 | dehttpize(g.urlFossil); |
| 149 | zExe = mprintf("?fossil=%T", g.urlFossil); |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | dehttpize(g.urlPath); |
| 154 | if( g.urlDfltPort==g.urlPort ){ |
| 155 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -63,10 +63,11 @@ | |
| 63 | || strncmp(zUrl, "ssh://", 6)==0 |
| 64 | ){ |
| 65 | int iStart; |
| 66 | char *zLogin; |
| 67 | char *zExe; |
| 68 | char cQuerySep = '?'; |
| 69 | |
| 70 | g.urlIsFile = 0; |
| 71 | if( zUrl[4]=='s' ){ |
| 72 | g.urlIsHttps = 1; |
| 73 | g.urlProtocol = "https"; |
| @@ -75,10 +76,11 @@ | |
| 76 | }else if( zUrl[0]=='s' ){ |
| 77 | g.urlIsSsh = 1; |
| 78 | g.urlProtocol = "ssh"; |
| 79 | g.urlDfltPort = 22; |
| 80 | g.urlFossil = "fossil"; |
| 81 | g.urlShell = 0; |
| 82 | iStart = 6; |
| 83 | }else{ |
| 84 | g.urlIsHttps = 0; |
| 85 | g.urlProtocol = "http"; |
| 86 | g.urlDfltPort = 80; |
| @@ -144,11 +146,18 @@ | |
| 146 | i++; |
| 147 | } |
| 148 | if( fossil_strcmp(zName,"fossil")==0 ){ |
| 149 | g.urlFossil = zValue; |
| 150 | dehttpize(g.urlFossil); |
| 151 | zExe = mprintf("%cfossil=%T", cQuerySep, g.urlFossil); |
| 152 | cQuerySep = '&'; |
| 153 | } |
| 154 | if( fossil_strcmp(zName,"shell")==0 ){ |
| 155 | g.urlShell = zValue; |
| 156 | dehttpize(g.urlShell); |
| 157 | zExe = mprintf("%cshell=%T", cQuerySep, g.urlFossil); |
| 158 | cQuerySep = '&'; |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | dehttpize(g.urlPath); |
| 163 | if( g.urlDfltPort==g.urlPort ){ |
| 164 |
+19
-3
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -18,10 +18,18 @@ | ||
| 18 | 18 | ** This file contains code to implement the file transfer protocol. |
| 19 | 19 | */ |
| 20 | 20 | #include "config.h" |
| 21 | 21 | #include "xfer.h" |
| 22 | 22 | |
| 23 | +#include <time.h> | |
| 24 | + | |
| 25 | +/* | |
| 26 | +** Maximum number of HTTP redirects that any http_exchange() call will | |
| 27 | +** follow before throwing a fatal error. Most browsers use a limit of 20. | |
| 28 | +*/ | |
| 29 | +#define MAX_REDIRECTS 20 | |
| 30 | + | |
| 23 | 31 | /* |
| 24 | 32 | ** This structure holds information about the current state of either |
| 25 | 33 | ** a client or a server that is participating in xfer. |
| 26 | 34 | */ |
| 27 | 35 | typedef struct Xfer Xfer; |
| @@ -40,10 +48,11 @@ | ||
| 40 | 48 | int nDeltaRcvd; /* Number of deltas received */ |
| 41 | 49 | int nDanglingFile; /* Number of dangling deltas received */ |
| 42 | 50 | int mxSend; /* Stop sending "file" with pOut reaches this size */ |
| 43 | 51 | u8 syncPrivate; /* True to enable syncing private content */ |
| 44 | 52 | u8 nextIsPrivate; /* If true, next "file" received is a private */ |
| 53 | + time_t maxTime; /* Time when this transfer should be finished */ | |
| 45 | 54 | }; |
| 46 | 55 | |
| 47 | 56 | |
| 48 | 57 | /* |
| 49 | 58 | ** The input blob contains a UUID. Convert it into a record ID. |
| @@ -393,11 +402,12 @@ | ||
| 393 | 402 | } |
| 394 | 403 | if( uuid_is_shunned(blob_str(pUuid)) ){ |
| 395 | 404 | blob_reset(&uuid); |
| 396 | 405 | return; |
| 397 | 406 | } |
| 398 | - if( pXfer->mxSend<=blob_size(pXfer->pOut) ){ | |
| 407 | + if( (pXfer->maxTime != -1 && time(NULL) >= pXfer->maxTime) || | |
| 408 | + pXfer->mxSend<=blob_size(pXfer->pOut) ){ | |
| 399 | 409 | const char *zFormat = isPriv ? "igot %b 1\n" : "igot %b\n"; |
| 400 | 410 | blob_appendf(pXfer->pOut, zFormat, pUuid); |
| 401 | 411 | pXfer->nIGotSent++; |
| 402 | 412 | blob_reset(&uuid); |
| 403 | 413 | return; |
| @@ -867,10 +877,13 @@ | ||
| 867 | 877 | } |
| 868 | 878 | blob_zero(&xfer.err); |
| 869 | 879 | xfer.pIn = &g.cgiIn; |
| 870 | 880 | xfer.pOut = cgi_output_blob(); |
| 871 | 881 | xfer.mxSend = db_get_int("max-download", 5000000); |
| 882 | + xfer.maxTime = db_get_int("max-download-time", 30); | |
| 883 | + if( xfer.maxTime<1 ) xfer.maxTime = 1; | |
| 884 | + xfer.maxTime += time(NULL); | |
| 872 | 885 | g.xferPanic = 1; |
| 873 | 886 | |
| 874 | 887 | db_begin_transaction(); |
| 875 | 888 | db_multi_exec( |
| 876 | 889 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| @@ -1034,11 +1047,12 @@ | ||
| 1034 | 1047 | if( iVers>=3 ){ |
| 1035 | 1048 | cgi_set_content_type("application/x-fossil-uncompressed"); |
| 1036 | 1049 | } |
| 1037 | 1050 | blob_is_int(&xfer.aToken[2], &seqno); |
| 1038 | 1051 | max = db_int(0, "SELECT max(rid) FROM blob"); |
| 1039 | - while( xfer.mxSend>blob_size(xfer.pOut) && seqno<=max ){ | |
| 1052 | + while( xfer.mxSend>blob_size(xfer.pOut) && seqno<=max){ | |
| 1053 | + if( time(NULL) >= xfer.maxTime ) break; | |
| 1040 | 1054 | if( iVers>=3 ){ |
| 1041 | 1055 | send_compressed_file(&xfer, seqno); |
| 1042 | 1056 | }else{ |
| 1043 | 1057 | send_file(&xfer, seqno, 0, 1); |
| 1044 | 1058 | } |
| @@ -1328,10 +1342,11 @@ | ||
| 1328 | 1342 | socket_global_init(); |
| 1329 | 1343 | memset(&xfer, 0, sizeof(xfer)); |
| 1330 | 1344 | xfer.pIn = &recv; |
| 1331 | 1345 | xfer.pOut = &send; |
| 1332 | 1346 | xfer.mxSend = db_get_int("max-upload", 250000); |
| 1347 | + xfer.maxTime = -1; | |
| 1333 | 1348 | if( syncFlags & SYNC_PRIVATE ){ |
| 1334 | 1349 | g.perm.Private = 1; |
| 1335 | 1350 | xfer.syncPrivate = 1; |
| 1336 | 1351 | } |
| 1337 | 1352 | |
| @@ -1471,11 +1486,12 @@ | ||
| 1471 | 1486 | xfer.nIGotSent = 0; |
| 1472 | 1487 | if( syncFlags & SYNC_VERBOSE ){ |
| 1473 | 1488 | fossil_print("waiting for server..."); |
| 1474 | 1489 | } |
| 1475 | 1490 | fflush(stdout); |
| 1476 | - if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0) ){ | |
| 1491 | + if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0, | |
| 1492 | + MAX_REDIRECTS) ){ | |
| 1477 | 1493 | nErr++; |
| 1478 | 1494 | break; |
| 1479 | 1495 | } |
| 1480 | 1496 | lastPctDone = -1; |
| 1481 | 1497 | blob_reset(&send); |
| 1482 | 1498 | |
| 1483 | 1499 | ADDED test/revert.test |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -18,10 +18,18 @@ | |
| 18 | ** This file contains code to implement the file transfer protocol. |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "xfer.h" |
| 22 | |
| 23 | /* |
| 24 | ** This structure holds information about the current state of either |
| 25 | ** a client or a server that is participating in xfer. |
| 26 | */ |
| 27 | typedef struct Xfer Xfer; |
| @@ -40,10 +48,11 @@ | |
| 40 | int nDeltaRcvd; /* Number of deltas received */ |
| 41 | int nDanglingFile; /* Number of dangling deltas received */ |
| 42 | int mxSend; /* Stop sending "file" with pOut reaches this size */ |
| 43 | u8 syncPrivate; /* True to enable syncing private content */ |
| 44 | u8 nextIsPrivate; /* If true, next "file" received is a private */ |
| 45 | }; |
| 46 | |
| 47 | |
| 48 | /* |
| 49 | ** The input blob contains a UUID. Convert it into a record ID. |
| @@ -393,11 +402,12 @@ | |
| 393 | } |
| 394 | if( uuid_is_shunned(blob_str(pUuid)) ){ |
| 395 | blob_reset(&uuid); |
| 396 | return; |
| 397 | } |
| 398 | if( pXfer->mxSend<=blob_size(pXfer->pOut) ){ |
| 399 | const char *zFormat = isPriv ? "igot %b 1\n" : "igot %b\n"; |
| 400 | blob_appendf(pXfer->pOut, zFormat, pUuid); |
| 401 | pXfer->nIGotSent++; |
| 402 | blob_reset(&uuid); |
| 403 | return; |
| @@ -867,10 +877,13 @@ | |
| 867 | } |
| 868 | blob_zero(&xfer.err); |
| 869 | xfer.pIn = &g.cgiIn; |
| 870 | xfer.pOut = cgi_output_blob(); |
| 871 | xfer.mxSend = db_get_int("max-download", 5000000); |
| 872 | g.xferPanic = 1; |
| 873 | |
| 874 | db_begin_transaction(); |
| 875 | db_multi_exec( |
| 876 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| @@ -1034,11 +1047,12 @@ | |
| 1034 | if( iVers>=3 ){ |
| 1035 | cgi_set_content_type("application/x-fossil-uncompressed"); |
| 1036 | } |
| 1037 | blob_is_int(&xfer.aToken[2], &seqno); |
| 1038 | max = db_int(0, "SELECT max(rid) FROM blob"); |
| 1039 | while( xfer.mxSend>blob_size(xfer.pOut) && seqno<=max ){ |
| 1040 | if( iVers>=3 ){ |
| 1041 | send_compressed_file(&xfer, seqno); |
| 1042 | }else{ |
| 1043 | send_file(&xfer, seqno, 0, 1); |
| 1044 | } |
| @@ -1328,10 +1342,11 @@ | |
| 1328 | socket_global_init(); |
| 1329 | memset(&xfer, 0, sizeof(xfer)); |
| 1330 | xfer.pIn = &recv; |
| 1331 | xfer.pOut = &send; |
| 1332 | xfer.mxSend = db_get_int("max-upload", 250000); |
| 1333 | if( syncFlags & SYNC_PRIVATE ){ |
| 1334 | g.perm.Private = 1; |
| 1335 | xfer.syncPrivate = 1; |
| 1336 | } |
| 1337 | |
| @@ -1471,11 +1486,12 @@ | |
| 1471 | xfer.nIGotSent = 0; |
| 1472 | if( syncFlags & SYNC_VERBOSE ){ |
| 1473 | fossil_print("waiting for server..."); |
| 1474 | } |
| 1475 | fflush(stdout); |
| 1476 | if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0) ){ |
| 1477 | nErr++; |
| 1478 | break; |
| 1479 | } |
| 1480 | lastPctDone = -1; |
| 1481 | blob_reset(&send); |
| 1482 | |
| 1483 | DDED test/revert.test |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -18,10 +18,18 @@ | |
| 18 | ** This file contains code to implement the file transfer protocol. |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "xfer.h" |
| 22 | |
| 23 | #include <time.h> |
| 24 | |
| 25 | /* |
| 26 | ** Maximum number of HTTP redirects that any http_exchange() call will |
| 27 | ** follow before throwing a fatal error. Most browsers use a limit of 20. |
| 28 | */ |
| 29 | #define MAX_REDIRECTS 20 |
| 30 | |
| 31 | /* |
| 32 | ** This structure holds information about the current state of either |
| 33 | ** a client or a server that is participating in xfer. |
| 34 | */ |
| 35 | typedef struct Xfer Xfer; |
| @@ -40,10 +48,11 @@ | |
| 48 | int nDeltaRcvd; /* Number of deltas received */ |
| 49 | int nDanglingFile; /* Number of dangling deltas received */ |
| 50 | int mxSend; /* Stop sending "file" with pOut reaches this size */ |
| 51 | u8 syncPrivate; /* True to enable syncing private content */ |
| 52 | u8 nextIsPrivate; /* If true, next "file" received is a private */ |
| 53 | time_t maxTime; /* Time when this transfer should be finished */ |
| 54 | }; |
| 55 | |
| 56 | |
| 57 | /* |
| 58 | ** The input blob contains a UUID. Convert it into a record ID. |
| @@ -393,11 +402,12 @@ | |
| 402 | } |
| 403 | if( uuid_is_shunned(blob_str(pUuid)) ){ |
| 404 | blob_reset(&uuid); |
| 405 | return; |
| 406 | } |
| 407 | if( (pXfer->maxTime != -1 && time(NULL) >= pXfer->maxTime) || |
| 408 | pXfer->mxSend<=blob_size(pXfer->pOut) ){ |
| 409 | const char *zFormat = isPriv ? "igot %b 1\n" : "igot %b\n"; |
| 410 | blob_appendf(pXfer->pOut, zFormat, pUuid); |
| 411 | pXfer->nIGotSent++; |
| 412 | blob_reset(&uuid); |
| 413 | return; |
| @@ -867,10 +877,13 @@ | |
| 877 | } |
| 878 | blob_zero(&xfer.err); |
| 879 | xfer.pIn = &g.cgiIn; |
| 880 | xfer.pOut = cgi_output_blob(); |
| 881 | xfer.mxSend = db_get_int("max-download", 5000000); |
| 882 | xfer.maxTime = db_get_int("max-download-time", 30); |
| 883 | if( xfer.maxTime<1 ) xfer.maxTime = 1; |
| 884 | xfer.maxTime += time(NULL); |
| 885 | g.xferPanic = 1; |
| 886 | |
| 887 | db_begin_transaction(); |
| 888 | db_multi_exec( |
| 889 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| @@ -1034,11 +1047,12 @@ | |
| 1047 | if( iVers>=3 ){ |
| 1048 | cgi_set_content_type("application/x-fossil-uncompressed"); |
| 1049 | } |
| 1050 | blob_is_int(&xfer.aToken[2], &seqno); |
| 1051 | max = db_int(0, "SELECT max(rid) FROM blob"); |
| 1052 | while( xfer.mxSend>blob_size(xfer.pOut) && seqno<=max){ |
| 1053 | if( time(NULL) >= xfer.maxTime ) break; |
| 1054 | if( iVers>=3 ){ |
| 1055 | send_compressed_file(&xfer, seqno); |
| 1056 | }else{ |
| 1057 | send_file(&xfer, seqno, 0, 1); |
| 1058 | } |
| @@ -1328,10 +1342,11 @@ | |
| 1342 | socket_global_init(); |
| 1343 | memset(&xfer, 0, sizeof(xfer)); |
| 1344 | xfer.pIn = &recv; |
| 1345 | xfer.pOut = &send; |
| 1346 | xfer.mxSend = db_get_int("max-upload", 250000); |
| 1347 | xfer.maxTime = -1; |
| 1348 | if( syncFlags & SYNC_PRIVATE ){ |
| 1349 | g.perm.Private = 1; |
| 1350 | xfer.syncPrivate = 1; |
| 1351 | } |
| 1352 | |
| @@ -1471,11 +1486,12 @@ | |
| 1486 | xfer.nIGotSent = 0; |
| 1487 | if( syncFlags & SYNC_VERBOSE ){ |
| 1488 | fossil_print("waiting for server..."); |
| 1489 | } |
| 1490 | fflush(stdout); |
| 1491 | if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0, |
| 1492 | MAX_REDIRECTS) ){ |
| 1493 | nErr++; |
| 1494 | break; |
| 1495 | } |
| 1496 | lastPctDone = -1; |
| 1497 | blob_reset(&send); |
| 1498 | |
| 1499 | DDED test/revert.test |
+19
-3
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -18,10 +18,18 @@ | ||
| 18 | 18 | ** This file contains code to implement the file transfer protocol. |
| 19 | 19 | */ |
| 20 | 20 | #include "config.h" |
| 21 | 21 | #include "xfer.h" |
| 22 | 22 | |
| 23 | +#include <time.h> | |
| 24 | + | |
| 25 | +/* | |
| 26 | +** Maximum number of HTTP redirects that any http_exchange() call will | |
| 27 | +** follow before throwing a fatal error. Most browsers use a limit of 20. | |
| 28 | +*/ | |
| 29 | +#define MAX_REDIRECTS 20 | |
| 30 | + | |
| 23 | 31 | /* |
| 24 | 32 | ** This structure holds information about the current state of either |
| 25 | 33 | ** a client or a server that is participating in xfer. |
| 26 | 34 | */ |
| 27 | 35 | typedef struct Xfer Xfer; |
| @@ -40,10 +48,11 @@ | ||
| 40 | 48 | int nDeltaRcvd; /* Number of deltas received */ |
| 41 | 49 | int nDanglingFile; /* Number of dangling deltas received */ |
| 42 | 50 | int mxSend; /* Stop sending "file" with pOut reaches this size */ |
| 43 | 51 | u8 syncPrivate; /* True to enable syncing private content */ |
| 44 | 52 | u8 nextIsPrivate; /* If true, next "file" received is a private */ |
| 53 | + time_t maxTime; /* Time when this transfer should be finished */ | |
| 45 | 54 | }; |
| 46 | 55 | |
| 47 | 56 | |
| 48 | 57 | /* |
| 49 | 58 | ** The input blob contains a UUID. Convert it into a record ID. |
| @@ -393,11 +402,12 @@ | ||
| 393 | 402 | } |
| 394 | 403 | if( uuid_is_shunned(blob_str(pUuid)) ){ |
| 395 | 404 | blob_reset(&uuid); |
| 396 | 405 | return; |
| 397 | 406 | } |
| 398 | - if( pXfer->mxSend<=blob_size(pXfer->pOut) ){ | |
| 407 | + if( (pXfer->maxTime != -1 && time(NULL) >= pXfer->maxTime) || | |
| 408 | + pXfer->mxSend<=blob_size(pXfer->pOut) ){ | |
| 399 | 409 | const char *zFormat = isPriv ? "igot %b 1\n" : "igot %b\n"; |
| 400 | 410 | blob_appendf(pXfer->pOut, zFormat, pUuid); |
| 401 | 411 | pXfer->nIGotSent++; |
| 402 | 412 | blob_reset(&uuid); |
| 403 | 413 | return; |
| @@ -867,10 +877,13 @@ | ||
| 867 | 877 | } |
| 868 | 878 | blob_zero(&xfer.err); |
| 869 | 879 | xfer.pIn = &g.cgiIn; |
| 870 | 880 | xfer.pOut = cgi_output_blob(); |
| 871 | 881 | xfer.mxSend = db_get_int("max-download", 5000000); |
| 882 | + xfer.maxTime = db_get_int("max-download-time", 30); | |
| 883 | + if( xfer.maxTime<1 ) xfer.maxTime = 1; | |
| 884 | + xfer.maxTime += time(NULL); | |
| 872 | 885 | g.xferPanic = 1; |
| 873 | 886 | |
| 874 | 887 | db_begin_transaction(); |
| 875 | 888 | db_multi_exec( |
| 876 | 889 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| @@ -1034,11 +1047,12 @@ | ||
| 1034 | 1047 | if( iVers>=3 ){ |
| 1035 | 1048 | cgi_set_content_type("application/x-fossil-uncompressed"); |
| 1036 | 1049 | } |
| 1037 | 1050 | blob_is_int(&xfer.aToken[2], &seqno); |
| 1038 | 1051 | max = db_int(0, "SELECT max(rid) FROM blob"); |
| 1039 | - while( xfer.mxSend>blob_size(xfer.pOut) && seqno<=max ){ | |
| 1052 | + while( xfer.mxSend>blob_size(xfer.pOut) && seqno<=max){ | |
| 1053 | + if( time(NULL) >= xfer.maxTime ) break; | |
| 1040 | 1054 | if( iVers>=3 ){ |
| 1041 | 1055 | send_compressed_file(&xfer, seqno); |
| 1042 | 1056 | }else{ |
| 1043 | 1057 | send_file(&xfer, seqno, 0, 1); |
| 1044 | 1058 | } |
| @@ -1328,10 +1342,11 @@ | ||
| 1328 | 1342 | socket_global_init(); |
| 1329 | 1343 | memset(&xfer, 0, sizeof(xfer)); |
| 1330 | 1344 | xfer.pIn = &recv; |
| 1331 | 1345 | xfer.pOut = &send; |
| 1332 | 1346 | xfer.mxSend = db_get_int("max-upload", 250000); |
| 1347 | + xfer.maxTime = -1; | |
| 1333 | 1348 | if( syncFlags & SYNC_PRIVATE ){ |
| 1334 | 1349 | g.perm.Private = 1; |
| 1335 | 1350 | xfer.syncPrivate = 1; |
| 1336 | 1351 | } |
| 1337 | 1352 | |
| @@ -1471,11 +1486,12 @@ | ||
| 1471 | 1486 | xfer.nIGotSent = 0; |
| 1472 | 1487 | if( syncFlags & SYNC_VERBOSE ){ |
| 1473 | 1488 | fossil_print("waiting for server..."); |
| 1474 | 1489 | } |
| 1475 | 1490 | fflush(stdout); |
| 1476 | - if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0) ){ | |
| 1491 | + if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0, | |
| 1492 | + MAX_REDIRECTS) ){ | |
| 1477 | 1493 | nErr++; |
| 1478 | 1494 | break; |
| 1479 | 1495 | } |
| 1480 | 1496 | lastPctDone = -1; |
| 1481 | 1497 | blob_reset(&send); |
| 1482 | 1498 | |
| 1483 | 1499 | ADDED test/revert.test |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -18,10 +18,18 @@ | |
| 18 | ** This file contains code to implement the file transfer protocol. |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "xfer.h" |
| 22 | |
| 23 | /* |
| 24 | ** This structure holds information about the current state of either |
| 25 | ** a client or a server that is participating in xfer. |
| 26 | */ |
| 27 | typedef struct Xfer Xfer; |
| @@ -40,10 +48,11 @@ | |
| 40 | int nDeltaRcvd; /* Number of deltas received */ |
| 41 | int nDanglingFile; /* Number of dangling deltas received */ |
| 42 | int mxSend; /* Stop sending "file" with pOut reaches this size */ |
| 43 | u8 syncPrivate; /* True to enable syncing private content */ |
| 44 | u8 nextIsPrivate; /* If true, next "file" received is a private */ |
| 45 | }; |
| 46 | |
| 47 | |
| 48 | /* |
| 49 | ** The input blob contains a UUID. Convert it into a record ID. |
| @@ -393,11 +402,12 @@ | |
| 393 | } |
| 394 | if( uuid_is_shunned(blob_str(pUuid)) ){ |
| 395 | blob_reset(&uuid); |
| 396 | return; |
| 397 | } |
| 398 | if( pXfer->mxSend<=blob_size(pXfer->pOut) ){ |
| 399 | const char *zFormat = isPriv ? "igot %b 1\n" : "igot %b\n"; |
| 400 | blob_appendf(pXfer->pOut, zFormat, pUuid); |
| 401 | pXfer->nIGotSent++; |
| 402 | blob_reset(&uuid); |
| 403 | return; |
| @@ -867,10 +877,13 @@ | |
| 867 | } |
| 868 | blob_zero(&xfer.err); |
| 869 | xfer.pIn = &g.cgiIn; |
| 870 | xfer.pOut = cgi_output_blob(); |
| 871 | xfer.mxSend = db_get_int("max-download", 5000000); |
| 872 | g.xferPanic = 1; |
| 873 | |
| 874 | db_begin_transaction(); |
| 875 | db_multi_exec( |
| 876 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| @@ -1034,11 +1047,12 @@ | |
| 1034 | if( iVers>=3 ){ |
| 1035 | cgi_set_content_type("application/x-fossil-uncompressed"); |
| 1036 | } |
| 1037 | blob_is_int(&xfer.aToken[2], &seqno); |
| 1038 | max = db_int(0, "SELECT max(rid) FROM blob"); |
| 1039 | while( xfer.mxSend>blob_size(xfer.pOut) && seqno<=max ){ |
| 1040 | if( iVers>=3 ){ |
| 1041 | send_compressed_file(&xfer, seqno); |
| 1042 | }else{ |
| 1043 | send_file(&xfer, seqno, 0, 1); |
| 1044 | } |
| @@ -1328,10 +1342,11 @@ | |
| 1328 | socket_global_init(); |
| 1329 | memset(&xfer, 0, sizeof(xfer)); |
| 1330 | xfer.pIn = &recv; |
| 1331 | xfer.pOut = &send; |
| 1332 | xfer.mxSend = db_get_int("max-upload", 250000); |
| 1333 | if( syncFlags & SYNC_PRIVATE ){ |
| 1334 | g.perm.Private = 1; |
| 1335 | xfer.syncPrivate = 1; |
| 1336 | } |
| 1337 | |
| @@ -1471,11 +1486,12 @@ | |
| 1471 | xfer.nIGotSent = 0; |
| 1472 | if( syncFlags & SYNC_VERBOSE ){ |
| 1473 | fossil_print("waiting for server..."); |
| 1474 | } |
| 1475 | fflush(stdout); |
| 1476 | if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0) ){ |
| 1477 | nErr++; |
| 1478 | break; |
| 1479 | } |
| 1480 | lastPctDone = -1; |
| 1481 | blob_reset(&send); |
| 1482 | |
| 1483 | DDED test/revert.test |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -18,10 +18,18 @@ | |
| 18 | ** This file contains code to implement the file transfer protocol. |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "xfer.h" |
| 22 | |
| 23 | #include <time.h> |
| 24 | |
| 25 | /* |
| 26 | ** Maximum number of HTTP redirects that any http_exchange() call will |
| 27 | ** follow before throwing a fatal error. Most browsers use a limit of 20. |
| 28 | */ |
| 29 | #define MAX_REDIRECTS 20 |
| 30 | |
| 31 | /* |
| 32 | ** This structure holds information about the current state of either |
| 33 | ** a client or a server that is participating in xfer. |
| 34 | */ |
| 35 | typedef struct Xfer Xfer; |
| @@ -40,10 +48,11 @@ | |
| 48 | int nDeltaRcvd; /* Number of deltas received */ |
| 49 | int nDanglingFile; /* Number of dangling deltas received */ |
| 50 | int mxSend; /* Stop sending "file" with pOut reaches this size */ |
| 51 | u8 syncPrivate; /* True to enable syncing private content */ |
| 52 | u8 nextIsPrivate; /* If true, next "file" received is a private */ |
| 53 | time_t maxTime; /* Time when this transfer should be finished */ |
| 54 | }; |
| 55 | |
| 56 | |
| 57 | /* |
| 58 | ** The input blob contains a UUID. Convert it into a record ID. |
| @@ -393,11 +402,12 @@ | |
| 402 | } |
| 403 | if( uuid_is_shunned(blob_str(pUuid)) ){ |
| 404 | blob_reset(&uuid); |
| 405 | return; |
| 406 | } |
| 407 | if( (pXfer->maxTime != -1 && time(NULL) >= pXfer->maxTime) || |
| 408 | pXfer->mxSend<=blob_size(pXfer->pOut) ){ |
| 409 | const char *zFormat = isPriv ? "igot %b 1\n" : "igot %b\n"; |
| 410 | blob_appendf(pXfer->pOut, zFormat, pUuid); |
| 411 | pXfer->nIGotSent++; |
| 412 | blob_reset(&uuid); |
| 413 | return; |
| @@ -867,10 +877,13 @@ | |
| 877 | } |
| 878 | blob_zero(&xfer.err); |
| 879 | xfer.pIn = &g.cgiIn; |
| 880 | xfer.pOut = cgi_output_blob(); |
| 881 | xfer.mxSend = db_get_int("max-download", 5000000); |
| 882 | xfer.maxTime = db_get_int("max-download-time", 30); |
| 883 | if( xfer.maxTime<1 ) xfer.maxTime = 1; |
| 884 | xfer.maxTime += time(NULL); |
| 885 | g.xferPanic = 1; |
| 886 | |
| 887 | db_begin_transaction(); |
| 888 | db_multi_exec( |
| 889 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| @@ -1034,11 +1047,12 @@ | |
| 1047 | if( iVers>=3 ){ |
| 1048 | cgi_set_content_type("application/x-fossil-uncompressed"); |
| 1049 | } |
| 1050 | blob_is_int(&xfer.aToken[2], &seqno); |
| 1051 | max = db_int(0, "SELECT max(rid) FROM blob"); |
| 1052 | while( xfer.mxSend>blob_size(xfer.pOut) && seqno<=max){ |
| 1053 | if( time(NULL) >= xfer.maxTime ) break; |
| 1054 | if( iVers>=3 ){ |
| 1055 | send_compressed_file(&xfer, seqno); |
| 1056 | }else{ |
| 1057 | send_file(&xfer, seqno, 0, 1); |
| 1058 | } |
| @@ -1328,10 +1342,11 @@ | |
| 1342 | socket_global_init(); |
| 1343 | memset(&xfer, 0, sizeof(xfer)); |
| 1344 | xfer.pIn = &recv; |
| 1345 | xfer.pOut = &send; |
| 1346 | xfer.mxSend = db_get_int("max-upload", 250000); |
| 1347 | xfer.maxTime = -1; |
| 1348 | if( syncFlags & SYNC_PRIVATE ){ |
| 1349 | g.perm.Private = 1; |
| 1350 | xfer.syncPrivate = 1; |
| 1351 | } |
| 1352 | |
| @@ -1471,11 +1486,12 @@ | |
| 1486 | xfer.nIGotSent = 0; |
| 1487 | if( syncFlags & SYNC_VERBOSE ){ |
| 1488 | fossil_print("waiting for server..."); |
| 1489 | } |
| 1490 | fflush(stdout); |
| 1491 | if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0, |
| 1492 | MAX_REDIRECTS) ){ |
| 1493 | nErr++; |
| 1494 | break; |
| 1495 | } |
| 1496 | lastPctDone = -1; |
| 1497 | blob_reset(&send); |
| 1498 | |
| 1499 | DDED test/revert.test |
+26
| --- a/test/revert.test | ||
| +++ b/test/revert.test | ||
| @@ -0,0 +1,26 @@ | ||
| 1 | +puts res=$# Fossil will write dexec rm f1po_init# Copytcatch {exec $::exec mvitory} $res]} { | |
| 2 | + puts stderr "Cannot ris test within anif {[llength $args] % 2} { | |
| 3 | + set revertArgs [lindex $args 0] | |
| 4 | + set args [lrange $args 1 end] | |
| 5 | + } else { | |
| 6 | + set revertArgs {} | |
| 7 | + }VERTED: f3 | |
| 8 | + DELETE:j@Uy,1X@Se,4:: f0G@YW,21@UT,3:ED:1w@WW,3:ED:3t@YT,G:ED: f3 | |
| 9 | + DELETE:K@hF,O@YG:ED: f3 | |
| 10 | + DELETE:K@hF,O@Yl,1 | |
| 11 | + DELETE:a@nd,1WvoIq;-testnormalize-status-list] | |
| 12 | + set r] | |
| 13 | + if {$result ne $expected} { | |
| 14 | + set passed 0 | |
| 15 | + protOut " Expected:\n [join $expected "\n "]" | |
| 16 | + protOut " Got:\n [joh {exec $::fossilexe info} res | |
| 17 | +if {![regexp {use --repository} $res]} { | |
| 18 | + puts stderr "Cannot run this test within an open checkout" | |
| 19 | + return | |
| 20 | +} | |
| 21 | +repo_init# Copytcatch {exec $::fossilexe info} res | |
| 22 | +if {![regexp {use --repository} $res]} { | |
| 23 | + puts stderr "Cannot run this test witt# Copytcatch {exec $::fossilexe info} res | |
| 24 | +if {![regexp {use --repository} $res]} { | |
| 25 | + put12 f03 f1 {exec $::fossilexe info}t# Copytcatch {exec $::foss4 f2 {exec $::ft# Copytcatch {exec $::foss5 f3 {exec $::fossilexe info} res | |
| 26 | +if {![re |
| --- a/test/revert.test | |
| +++ b/test/revert.test | |
| @@ -0,0 +1,26 @@ | |
| --- a/test/revert.test | |
| +++ b/test/revert.test | |
| @@ -0,0 +1,26 @@ | |
| 1 | puts res=$# Fossil will write dexec rm f1po_init# Copytcatch {exec $::exec mvitory} $res]} { |
| 2 | puts stderr "Cannot ris test within anif {[llength $args] % 2} { |
| 3 | set revertArgs [lindex $args 0] |
| 4 | set args [lrange $args 1 end] |
| 5 | } else { |
| 6 | set revertArgs {} |
| 7 | }VERTED: f3 |
| 8 | DELETE:j@Uy,1X@Se,4:: f0G@YW,21@UT,3:ED:1w@WW,3:ED:3t@YT,G:ED: f3 |
| 9 | DELETE:K@hF,O@YG:ED: f3 |
| 10 | DELETE:K@hF,O@Yl,1 |
| 11 | DELETE:a@nd,1WvoIq;-testnormalize-status-list] |
| 12 | set r] |
| 13 | if {$result ne $expected} { |
| 14 | set passed 0 |
| 15 | protOut " Expected:\n [join $expected "\n "]" |
| 16 | protOut " Got:\n [joh {exec $::fossilexe info} res |
| 17 | if {![regexp {use --repository} $res]} { |
| 18 | puts stderr "Cannot run this test within an open checkout" |
| 19 | return |
| 20 | } |
| 21 | repo_init# Copytcatch {exec $::fossilexe info} res |
| 22 | if {![regexp {use --repository} $res]} { |
| 23 | puts stderr "Cannot run this test witt# Copytcatch {exec $::fossilexe info} res |
| 24 | if {![regexp {use --repository} $res]} { |
| 25 | put12 f03 f1 {exec $::fossilexe info}t# Copytcatch {exec $::foss4 f2 {exec $::ft# Copytcatch {exec $::foss5 f3 {exec $::fossilexe info} res |
| 26 | if {![re |
+26
| --- a/test/revert.test | ||
| +++ b/test/revert.test | ||
| @@ -0,0 +1,26 @@ | ||
| 1 | +puts res=$# Fossil will write dexec rm f1po_init# Copytcatch {exec $::exec mvitory} $res]} { | |
| 2 | + puts stderr "Cannot ris test within anif {[llength $args] % 2} { | |
| 3 | + set revertArgs [lindex $args 0] | |
| 4 | + set args [lrange $args 1 end] | |
| 5 | + } else { | |
| 6 | + set revertArgs {} | |
| 7 | + }VERTED: f3 | |
| 8 | + DELETE:j@Uy,1X@Se,4:: f0G@YW,21@UT,3:ED:1w@WW,3:ED:3t@YT,G:ED: f3 | |
| 9 | + DELETE:K@hF,O@YG:ED: f3 | |
| 10 | + DELETE:K@hF,O@Yl,1 | |
| 11 | + DELETE:a@nd,1WvoIq;-testnormalize-status-list] | |
| 12 | + set r] | |
| 13 | + if {$result ne $expected} { | |
| 14 | + set passed 0 | |
| 15 | + protOut " Expected:\n [join $expected "\n "]" | |
| 16 | + protOut " Got:\n [joh {exec $::fossilexe info} res | |
| 17 | +if {![regexp {use --repository} $res]} { | |
| 18 | + puts stderr "Cannot run this test within an open checkout" | |
| 19 | + return | |
| 20 | +} | |
| 21 | +repo_init# Copytcatch {exec $::fossilexe info} res | |
| 22 | +if {![regexp {use --repository} $res]} { | |
| 23 | + puts stderr "Cannot run this test witt# Copytcatch {exec $::fossilexe info} res | |
| 24 | +if {![regexp {use --repository} $res]} { | |
| 25 | + put12 f03 f1 {exec $::fossilexe info}t# Copytcatch {exec $::foss4 f2 {exec $::ft# Copytcatch {exec $::foss5 f3 {exec $::fossilexe info} res | |
| 26 | +if {![re |
| --- a/test/revert.test | |
| +++ b/test/revert.test | |
| @@ -0,0 +1,26 @@ | |
| --- a/test/revert.test | |
| +++ b/test/revert.test | |
| @@ -0,0 +1,26 @@ | |
| 1 | puts res=$# Fossil will write dexec rm f1po_init# Copytcatch {exec $::exec mvitory} $res]} { |
| 2 | puts stderr "Cannot ris test within anif {[llength $args] % 2} { |
| 3 | set revertArgs [lindex $args 0] |
| 4 | set args [lrange $args 1 end] |
| 5 | } else { |
| 6 | set revertArgs {} |
| 7 | }VERTED: f3 |
| 8 | DELETE:j@Uy,1X@Se,4:: f0G@YW,21@UT,3:ED:1w@WW,3:ED:3t@YT,G:ED: f3 |
| 9 | DELETE:K@hF,O@YG:ED: f3 |
| 10 | DELETE:K@hF,O@Yl,1 |
| 11 | DELETE:a@nd,1WvoIq;-testnormalize-status-list] |
| 12 | set r] |
| 13 | if {$result ne $expected} { |
| 14 | set passed 0 |
| 15 | protOut " Expected:\n [join $expected "\n "]" |
| 16 | protOut " Got:\n [joh {exec $::fossilexe info} res |
| 17 | if {![regexp {use --repository} $res]} { |
| 18 | puts stderr "Cannot run this test within an open checkout" |
| 19 | return |
| 20 | } |
| 21 | repo_init# Copytcatch {exec $::fossilexe info} res |
| 22 | if {![regexp {use --repository} $res]} { |
| 23 | puts stderr "Cannot run this test witt# Copytcatch {exec $::fossilexe info} res |
| 24 | if {![regexp {use --repository} $res]} { |
| 25 | put12 f03 f1 {exec $::fossilexe info}t# Copytcatch {exec $::foss4 f2 {exec $::ft# Copytcatch {exec $::foss5 f3 {exec $::fossilexe info} res |
| 26 | if {![re |
+5
-5
| --- win/Makefile.mingw | ||
| +++ win/Makefile.mingw | ||
| @@ -6,15 +6,15 @@ | ||
| 6 | 6 | # |
| 7 | 7 | # This file is automatically generated. Instead of editing this |
| 8 | 8 | # file, edit "makemake.tcl" then run "tclsh makemake.tcl" |
| 9 | 9 | # to regenerate this file. |
| 10 | 10 | # |
| 11 | -# This is a makefile for use on Windows/Linux/Darwin/Cygwin using MinGW or | |
| 12 | -# MinGW-w64. | |
| 11 | +# This is a makefile for use on Cygwin/Darwin/FreeBSD/Linux/Windows using | |
| 12 | +# MinGW or MinGW-w64. | |
| 13 | 13 | # |
| 14 | 14 | |
| 15 | -#### Select one of MinGW, MinGW-64 (32-bit) or MinGW-w64 (64-bit) compilers. | |
| 15 | +#### Select one of MinGW, MinGW-w64 (32-bit) or MinGW-w64 (64-bit) compilers. | |
| 16 | 16 | # By default, this is an empty string (i.e. use the native compiler). |
| 17 | 17 | # |
| 18 | 18 | PREFIX = |
| 19 | 19 | # PREFIX = mingw32- |
| 20 | 20 | # PREFIX = i686-pc-mingw32- |
| @@ -657,11 +657,11 @@ | ||
| 657 | 657 | ifdef FOSSIL_ENABLE_TCL |
| 658 | 658 | EXTRAOBJ += $(OBJDIR)/th_tcl.o |
| 659 | 659 | endif |
| 660 | 660 | |
| 661 | 661 | zlib: |
| 662 | - make -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a | |
| 662 | + $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a | |
| 663 | 663 | |
| 664 | 664 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib |
| 665 | 665 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o |
| 666 | 666 | |
| 667 | 667 | # This rule prevents make from using its default rules to try build |
| @@ -1633,11 +1633,11 @@ | ||
| 1633 | 1633 | |
| 1634 | 1634 | $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c |
| 1635 | 1635 | $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o |
| 1636 | 1636 | |
| 1637 | 1637 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1638 | - $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE | |
| 1638 | + $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o | |
| 1639 | 1639 | |
| 1640 | 1640 | $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h |
| 1641 | 1641 | |
| 1642 | 1642 | $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h |
| 1643 | 1643 | $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o |
| 1644 | 1644 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -6,15 +6,15 @@ | |
| 6 | # |
| 7 | # This file is automatically generated. Instead of editing this |
| 8 | # file, edit "makemake.tcl" then run "tclsh makemake.tcl" |
| 9 | # to regenerate this file. |
| 10 | # |
| 11 | # This is a makefile for use on Windows/Linux/Darwin/Cygwin using MinGW or |
| 12 | # MinGW-w64. |
| 13 | # |
| 14 | |
| 15 | #### Select one of MinGW, MinGW-64 (32-bit) or MinGW-w64 (64-bit) compilers. |
| 16 | # By default, this is an empty string (i.e. use the native compiler). |
| 17 | # |
| 18 | PREFIX = |
| 19 | # PREFIX = mingw32- |
| 20 | # PREFIX = i686-pc-mingw32- |
| @@ -657,11 +657,11 @@ | |
| 657 | ifdef FOSSIL_ENABLE_TCL |
| 658 | EXTRAOBJ += $(OBJDIR)/th_tcl.o |
| 659 | endif |
| 660 | |
| 661 | zlib: |
| 662 | make -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a |
| 663 | |
| 664 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib |
| 665 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o |
| 666 | |
| 667 | # This rule prevents make from using its default rules to try build |
| @@ -1633,11 +1633,11 @@ | |
| 1633 | |
| 1634 | $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c |
| 1635 | $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o |
| 1636 | |
| 1637 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1638 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE |
| 1639 | |
| 1640 | $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h |
| 1641 | |
| 1642 | $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h |
| 1643 | $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o |
| 1644 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -6,15 +6,15 @@ | |
| 6 | # |
| 7 | # This file is automatically generated. Instead of editing this |
| 8 | # file, edit "makemake.tcl" then run "tclsh makemake.tcl" |
| 9 | # to regenerate this file. |
| 10 | # |
| 11 | # This is a makefile for use on Cygwin/Darwin/FreeBSD/Linux/Windows using |
| 12 | # MinGW or MinGW-w64. |
| 13 | # |
| 14 | |
| 15 | #### Select one of MinGW, MinGW-w64 (32-bit) or MinGW-w64 (64-bit) compilers. |
| 16 | # By default, this is an empty string (i.e. use the native compiler). |
| 17 | # |
| 18 | PREFIX = |
| 19 | # PREFIX = mingw32- |
| 20 | # PREFIX = i686-pc-mingw32- |
| @@ -657,11 +657,11 @@ | |
| 657 | ifdef FOSSIL_ENABLE_TCL |
| 658 | EXTRAOBJ += $(OBJDIR)/th_tcl.o |
| 659 | endif |
| 660 | |
| 661 | zlib: |
| 662 | $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a |
| 663 | |
| 664 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib |
| 665 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o |
| 666 | |
| 667 | # This rule prevents make from using its default rules to try build |
| @@ -1633,11 +1633,11 @@ | |
| 1633 | |
| 1634 | $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c |
| 1635 | $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o |
| 1636 | |
| 1637 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1638 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o |
| 1639 | |
| 1640 | $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h |
| 1641 | |
| 1642 | $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h |
| 1643 | $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o |
| 1644 |
+5
-5
| --- win/Makefile.mingw | ||
| +++ win/Makefile.mingw | ||
| @@ -6,15 +6,15 @@ | ||
| 6 | 6 | # |
| 7 | 7 | # This file is automatically generated. Instead of editing this |
| 8 | 8 | # file, edit "makemake.tcl" then run "tclsh makemake.tcl" |
| 9 | 9 | # to regenerate this file. |
| 10 | 10 | # |
| 11 | -# This is a makefile for use on Windows/Linux/Darwin/Cygwin using MinGW or | |
| 12 | -# MinGW-w64. | |
| 11 | +# This is a makefile for use on Cygwin/Darwin/FreeBSD/Linux/Windows using | |
| 12 | +# MinGW or MinGW-w64. | |
| 13 | 13 | # |
| 14 | 14 | |
| 15 | -#### Select one of MinGW, MinGW-64 (32-bit) or MinGW-w64 (64-bit) compilers. | |
| 15 | +#### Select one of MinGW, MinGW-w64 (32-bit) or MinGW-w64 (64-bit) compilers. | |
| 16 | 16 | # By default, this is an empty string (i.e. use the native compiler). |
| 17 | 17 | # |
| 18 | 18 | PREFIX = |
| 19 | 19 | # PREFIX = mingw32- |
| 20 | 20 | # PREFIX = i686-pc-mingw32- |
| @@ -657,11 +657,11 @@ | ||
| 657 | 657 | ifdef FOSSIL_ENABLE_TCL |
| 658 | 658 | EXTRAOBJ += $(OBJDIR)/th_tcl.o |
| 659 | 659 | endif |
| 660 | 660 | |
| 661 | 661 | zlib: |
| 662 | - make -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a | |
| 662 | + $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a | |
| 663 | 663 | |
| 664 | 664 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib |
| 665 | 665 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o |
| 666 | 666 | |
| 667 | 667 | # This rule prevents make from using its default rules to try build |
| @@ -1633,11 +1633,11 @@ | ||
| 1633 | 1633 | |
| 1634 | 1634 | $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c |
| 1635 | 1635 | $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o |
| 1636 | 1636 | |
| 1637 | 1637 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1638 | - $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE | |
| 1638 | + $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o | |
| 1639 | 1639 | |
| 1640 | 1640 | $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h |
| 1641 | 1641 | |
| 1642 | 1642 | $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h |
| 1643 | 1643 | $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o |
| 1644 | 1644 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -6,15 +6,15 @@ | |
| 6 | # |
| 7 | # This file is automatically generated. Instead of editing this |
| 8 | # file, edit "makemake.tcl" then run "tclsh makemake.tcl" |
| 9 | # to regenerate this file. |
| 10 | # |
| 11 | # This is a makefile for use on Windows/Linux/Darwin/Cygwin using MinGW or |
| 12 | # MinGW-w64. |
| 13 | # |
| 14 | |
| 15 | #### Select one of MinGW, MinGW-64 (32-bit) or MinGW-w64 (64-bit) compilers. |
| 16 | # By default, this is an empty string (i.e. use the native compiler). |
| 17 | # |
| 18 | PREFIX = |
| 19 | # PREFIX = mingw32- |
| 20 | # PREFIX = i686-pc-mingw32- |
| @@ -657,11 +657,11 @@ | |
| 657 | ifdef FOSSIL_ENABLE_TCL |
| 658 | EXTRAOBJ += $(OBJDIR)/th_tcl.o |
| 659 | endif |
| 660 | |
| 661 | zlib: |
| 662 | make -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a |
| 663 | |
| 664 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib |
| 665 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o |
| 666 | |
| 667 | # This rule prevents make from using its default rules to try build |
| @@ -1633,11 +1633,11 @@ | |
| 1633 | |
| 1634 | $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c |
| 1635 | $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o |
| 1636 | |
| 1637 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1638 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE |
| 1639 | |
| 1640 | $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h |
| 1641 | |
| 1642 | $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h |
| 1643 | $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o |
| 1644 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -6,15 +6,15 @@ | |
| 6 | # |
| 7 | # This file is automatically generated. Instead of editing this |
| 8 | # file, edit "makemake.tcl" then run "tclsh makemake.tcl" |
| 9 | # to regenerate this file. |
| 10 | # |
| 11 | # This is a makefile for use on Cygwin/Darwin/FreeBSD/Linux/Windows using |
| 12 | # MinGW or MinGW-w64. |
| 13 | # |
| 14 | |
| 15 | #### Select one of MinGW, MinGW-w64 (32-bit) or MinGW-w64 (64-bit) compilers. |
| 16 | # By default, this is an empty string (i.e. use the native compiler). |
| 17 | # |
| 18 | PREFIX = |
| 19 | # PREFIX = mingw32- |
| 20 | # PREFIX = i686-pc-mingw32- |
| @@ -657,11 +657,11 @@ | |
| 657 | ifdef FOSSIL_ENABLE_TCL |
| 658 | EXTRAOBJ += $(OBJDIR)/th_tcl.o |
| 659 | endif |
| 660 | |
| 661 | zlib: |
| 662 | $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a |
| 663 | |
| 664 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib |
| 665 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o |
| 666 | |
| 667 | # This rule prevents make from using its default rules to try build |
| @@ -1633,11 +1633,11 @@ | |
| 1633 | |
| 1634 | $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c |
| 1635 | $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o |
| 1636 | |
| 1637 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1638 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o |
| 1639 | |
| 1640 | $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h |
| 1641 | |
| 1642 | $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h |
| 1643 | $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o |
| 1644 |
+29
-5
| --- win/Makefile.mingw.mistachkin | ||
| +++ win/Makefile.mingw.mistachkin | ||
| @@ -6,15 +6,15 @@ | ||
| 6 | 6 | # |
| 7 | 7 | # This file is automatically generated. Instead of editing this |
| 8 | 8 | # file, edit "makemake.tcl" then run "tclsh makemake.tcl" |
| 9 | 9 | # to regenerate this file. |
| 10 | 10 | # |
| 11 | -# This is a makefile for use on Windows/Linux/Darwin/Cygwin using MinGW or | |
| 12 | -# MinGW-w64. | |
| 11 | +# This is a makefile for use on Cygwin/Darwin/FreeBSD/Linux/Windows using | |
| 12 | +# MinGW or MinGW-w64. | |
| 13 | 13 | # |
| 14 | 14 | |
| 15 | -#### Select one of MinGW, MinGW-64 (32-bit) or MinGW-w64 (64-bit) compilers. | |
| 15 | +#### Select one of MinGW, MinGW-w64 (32-bit) or MinGW-w64 (64-bit) compilers. | |
| 16 | 16 | # By default, this is an empty string (i.e. use the native compiler). |
| 17 | 17 | # |
| 18 | 18 | PREFIX = |
| 19 | 19 | # PREFIX = mingw32- |
| 20 | 20 | # PREFIX = i686-pc-mingw32- |
| @@ -322,10 +322,11 @@ | ||
| 322 | 322 | $(SRCDIR)/pivot.c \ |
| 323 | 323 | $(SRCDIR)/popen.c \ |
| 324 | 324 | $(SRCDIR)/pqueue.c \ |
| 325 | 325 | $(SRCDIR)/printf.c \ |
| 326 | 326 | $(SRCDIR)/rebuild.c \ |
| 327 | + $(SRCDIR)/regexp.c \ | |
| 327 | 328 | $(SRCDIR)/report.c \ |
| 328 | 329 | $(SRCDIR)/rss.c \ |
| 329 | 330 | $(SRCDIR)/schema.c \ |
| 330 | 331 | $(SRCDIR)/search.c \ |
| 331 | 332 | $(SRCDIR)/setup.c \ |
| @@ -342,10 +343,11 @@ | ||
| 342 | 343 | $(SRCDIR)/th_main.c \ |
| 343 | 344 | $(SRCDIR)/timeline.c \ |
| 344 | 345 | $(SRCDIR)/tkt.c \ |
| 345 | 346 | $(SRCDIR)/tktsetup.c \ |
| 346 | 347 | $(SRCDIR)/undo.c \ |
| 348 | + $(SRCDIR)/unicode.c \ | |
| 347 | 349 | $(SRCDIR)/update.c \ |
| 348 | 350 | $(SRCDIR)/url.c \ |
| 349 | 351 | $(SRCDIR)/user.c \ |
| 350 | 352 | $(SRCDIR)/utf8.c \ |
| 351 | 353 | $(SRCDIR)/verify.c \ |
| @@ -426,10 +428,11 @@ | ||
| 426 | 428 | $(OBJDIR)/pivot_.c \ |
| 427 | 429 | $(OBJDIR)/popen_.c \ |
| 428 | 430 | $(OBJDIR)/pqueue_.c \ |
| 429 | 431 | $(OBJDIR)/printf_.c \ |
| 430 | 432 | $(OBJDIR)/rebuild_.c \ |
| 433 | + $(OBJDIR)/regexp_.c \ | |
| 431 | 434 | $(OBJDIR)/report_.c \ |
| 432 | 435 | $(OBJDIR)/rss_.c \ |
| 433 | 436 | $(OBJDIR)/schema_.c \ |
| 434 | 437 | $(OBJDIR)/search_.c \ |
| 435 | 438 | $(OBJDIR)/setup_.c \ |
| @@ -446,10 +449,11 @@ | ||
| 446 | 449 | $(OBJDIR)/th_main_.c \ |
| 447 | 450 | $(OBJDIR)/timeline_.c \ |
| 448 | 451 | $(OBJDIR)/tkt_.c \ |
| 449 | 452 | $(OBJDIR)/tktsetup_.c \ |
| 450 | 453 | $(OBJDIR)/undo_.c \ |
| 454 | + $(OBJDIR)/unicode_.c \ | |
| 451 | 455 | $(OBJDIR)/update_.c \ |
| 452 | 456 | $(OBJDIR)/url_.c \ |
| 453 | 457 | $(OBJDIR)/user_.c \ |
| 454 | 458 | $(OBJDIR)/utf8_.c \ |
| 455 | 459 | $(OBJDIR)/verify_.c \ |
| @@ -530,10 +534,11 @@ | ||
| 530 | 534 | $(OBJDIR)/pivot.o \ |
| 531 | 535 | $(OBJDIR)/popen.o \ |
| 532 | 536 | $(OBJDIR)/pqueue.o \ |
| 533 | 537 | $(OBJDIR)/printf.o \ |
| 534 | 538 | $(OBJDIR)/rebuild.o \ |
| 539 | + $(OBJDIR)/regexp.o \ | |
| 535 | 540 | $(OBJDIR)/report.o \ |
| 536 | 541 | $(OBJDIR)/rss.o \ |
| 537 | 542 | $(OBJDIR)/schema.o \ |
| 538 | 543 | $(OBJDIR)/search.o \ |
| 539 | 544 | $(OBJDIR)/setup.o \ |
| @@ -550,10 +555,11 @@ | ||
| 550 | 555 | $(OBJDIR)/th_main.o \ |
| 551 | 556 | $(OBJDIR)/timeline.o \ |
| 552 | 557 | $(OBJDIR)/tkt.o \ |
| 553 | 558 | $(OBJDIR)/tktsetup.o \ |
| 554 | 559 | $(OBJDIR)/undo.o \ |
| 560 | + $(OBJDIR)/unicode.o \ | |
| 555 | 561 | $(OBJDIR)/update.o \ |
| 556 | 562 | $(OBJDIR)/url.o \ |
| 557 | 563 | $(OBJDIR)/user.o \ |
| 558 | 564 | $(OBJDIR)/utf8.o \ |
| 559 | 565 | $(OBJDIR)/verify.o \ |
| @@ -651,11 +657,11 @@ | ||
| 651 | 657 | ifdef FOSSIL_ENABLE_TCL |
| 652 | 658 | EXTRAOBJ += $(OBJDIR)/th_tcl.o |
| 653 | 659 | endif |
| 654 | 660 | |
| 655 | 661 | zlib: |
| 656 | - make -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a | |
| 662 | + $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a | |
| 657 | 663 | |
| 658 | 664 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib |
| 659 | 665 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o |
| 660 | 666 | |
| 661 | 667 | # This rule prevents make from using its default rules to try build |
| @@ -747,10 +753,11 @@ | ||
| 747 | 753 | $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \ |
| 748 | 754 | $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \ |
| 749 | 755 | $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \ |
| 750 | 756 | $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \ |
| 751 | 757 | $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \ |
| 758 | + $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ | |
| 752 | 759 | $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ |
| 753 | 760 | $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ |
| 754 | 761 | $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ |
| 755 | 762 | $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ |
| 756 | 763 | $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ |
| @@ -767,10 +774,11 @@ | ||
| 767 | 774 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| 768 | 775 | $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ |
| 769 | 776 | $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ |
| 770 | 777 | $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ |
| 771 | 778 | $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ |
| 779 | + $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \ | |
| 772 | 780 | $(OBJDIR)/update_.c:$(OBJDIR)/update.h \ |
| 773 | 781 | $(OBJDIR)/url_.c:$(OBJDIR)/url.h \ |
| 774 | 782 | $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ |
| 775 | 783 | $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ |
| 776 | 784 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ |
| @@ -1340,10 +1348,18 @@ | ||
| 1340 | 1348 | |
| 1341 | 1349 | $(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h |
| 1342 | 1350 | $(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c |
| 1343 | 1351 | |
| 1344 | 1352 | $(OBJDIR)/rebuild.h: $(OBJDIR)/headers |
| 1353 | + | |
| 1354 | +$(OBJDIR)/regexp_.c: $(SRCDIR)/regexp.c $(OBJDIR)/translate | |
| 1355 | + $(TRANSLATE) $(SRCDIR)/regexp.c >$(OBJDIR)/regexp_.c | |
| 1356 | + | |
| 1357 | +$(OBJDIR)/regexp.o: $(OBJDIR)/regexp_.c $(OBJDIR)/regexp.h $(SRCDIR)/config.h | |
| 1358 | + $(XTCC) -o $(OBJDIR)/regexp.o -c $(OBJDIR)/regexp_.c | |
| 1359 | + | |
| 1360 | +$(OBJDIR)/regexp.h: $(OBJDIR)/headers | |
| 1345 | 1361 | |
| 1346 | 1362 | $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate |
| 1347 | 1363 | $(TRANSLATE) $(SRCDIR)/report.c >$(OBJDIR)/report_.c |
| 1348 | 1364 | |
| 1349 | 1365 | $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h |
| @@ -1500,10 +1516,18 @@ | ||
| 1500 | 1516 | |
| 1501 | 1517 | $(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h |
| 1502 | 1518 | $(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c |
| 1503 | 1519 | |
| 1504 | 1520 | $(OBJDIR)/undo.h: $(OBJDIR)/headers |
| 1521 | + | |
| 1522 | +$(OBJDIR)/unicode_.c: $(SRCDIR)/unicode.c $(OBJDIR)/translate | |
| 1523 | + $(TRANSLATE) $(SRCDIR)/unicode.c >$(OBJDIR)/unicode_.c | |
| 1524 | + | |
| 1525 | +$(OBJDIR)/unicode.o: $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h | |
| 1526 | + $(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c | |
| 1527 | + | |
| 1528 | +$(OBJDIR)/unicode.h: $(OBJDIR)/headers | |
| 1505 | 1529 | |
| 1506 | 1530 | $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate |
| 1507 | 1531 | $(TRANSLATE) $(SRCDIR)/update.c >$(OBJDIR)/update_.c |
| 1508 | 1532 | |
| 1509 | 1533 | $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h |
| @@ -1609,11 +1633,11 @@ | ||
| 1609 | 1633 | |
| 1610 | 1634 | $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c |
| 1611 | 1635 | $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o |
| 1612 | 1636 | |
| 1613 | 1637 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1614 | - $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE | |
| 1638 | + $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o | |
| 1615 | 1639 | |
| 1616 | 1640 | $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h |
| 1617 | 1641 | |
| 1618 | 1642 | $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h |
| 1619 | 1643 | $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o |
| 1620 | 1644 |
| --- win/Makefile.mingw.mistachkin | |
| +++ win/Makefile.mingw.mistachkin | |
| @@ -6,15 +6,15 @@ | |
| 6 | # |
| 7 | # This file is automatically generated. Instead of editing this |
| 8 | # file, edit "makemake.tcl" then run "tclsh makemake.tcl" |
| 9 | # to regenerate this file. |
| 10 | # |
| 11 | # This is a makefile for use on Windows/Linux/Darwin/Cygwin using MinGW or |
| 12 | # MinGW-w64. |
| 13 | # |
| 14 | |
| 15 | #### Select one of MinGW, MinGW-64 (32-bit) or MinGW-w64 (64-bit) compilers. |
| 16 | # By default, this is an empty string (i.e. use the native compiler). |
| 17 | # |
| 18 | PREFIX = |
| 19 | # PREFIX = mingw32- |
| 20 | # PREFIX = i686-pc-mingw32- |
| @@ -322,10 +322,11 @@ | |
| 322 | $(SRCDIR)/pivot.c \ |
| 323 | $(SRCDIR)/popen.c \ |
| 324 | $(SRCDIR)/pqueue.c \ |
| 325 | $(SRCDIR)/printf.c \ |
| 326 | $(SRCDIR)/rebuild.c \ |
| 327 | $(SRCDIR)/report.c \ |
| 328 | $(SRCDIR)/rss.c \ |
| 329 | $(SRCDIR)/schema.c \ |
| 330 | $(SRCDIR)/search.c \ |
| 331 | $(SRCDIR)/setup.c \ |
| @@ -342,10 +343,11 @@ | |
| 342 | $(SRCDIR)/th_main.c \ |
| 343 | $(SRCDIR)/timeline.c \ |
| 344 | $(SRCDIR)/tkt.c \ |
| 345 | $(SRCDIR)/tktsetup.c \ |
| 346 | $(SRCDIR)/undo.c \ |
| 347 | $(SRCDIR)/update.c \ |
| 348 | $(SRCDIR)/url.c \ |
| 349 | $(SRCDIR)/user.c \ |
| 350 | $(SRCDIR)/utf8.c \ |
| 351 | $(SRCDIR)/verify.c \ |
| @@ -426,10 +428,11 @@ | |
| 426 | $(OBJDIR)/pivot_.c \ |
| 427 | $(OBJDIR)/popen_.c \ |
| 428 | $(OBJDIR)/pqueue_.c \ |
| 429 | $(OBJDIR)/printf_.c \ |
| 430 | $(OBJDIR)/rebuild_.c \ |
| 431 | $(OBJDIR)/report_.c \ |
| 432 | $(OBJDIR)/rss_.c \ |
| 433 | $(OBJDIR)/schema_.c \ |
| 434 | $(OBJDIR)/search_.c \ |
| 435 | $(OBJDIR)/setup_.c \ |
| @@ -446,10 +449,11 @@ | |
| 446 | $(OBJDIR)/th_main_.c \ |
| 447 | $(OBJDIR)/timeline_.c \ |
| 448 | $(OBJDIR)/tkt_.c \ |
| 449 | $(OBJDIR)/tktsetup_.c \ |
| 450 | $(OBJDIR)/undo_.c \ |
| 451 | $(OBJDIR)/update_.c \ |
| 452 | $(OBJDIR)/url_.c \ |
| 453 | $(OBJDIR)/user_.c \ |
| 454 | $(OBJDIR)/utf8_.c \ |
| 455 | $(OBJDIR)/verify_.c \ |
| @@ -530,10 +534,11 @@ | |
| 530 | $(OBJDIR)/pivot.o \ |
| 531 | $(OBJDIR)/popen.o \ |
| 532 | $(OBJDIR)/pqueue.o \ |
| 533 | $(OBJDIR)/printf.o \ |
| 534 | $(OBJDIR)/rebuild.o \ |
| 535 | $(OBJDIR)/report.o \ |
| 536 | $(OBJDIR)/rss.o \ |
| 537 | $(OBJDIR)/schema.o \ |
| 538 | $(OBJDIR)/search.o \ |
| 539 | $(OBJDIR)/setup.o \ |
| @@ -550,10 +555,11 @@ | |
| 550 | $(OBJDIR)/th_main.o \ |
| 551 | $(OBJDIR)/timeline.o \ |
| 552 | $(OBJDIR)/tkt.o \ |
| 553 | $(OBJDIR)/tktsetup.o \ |
| 554 | $(OBJDIR)/undo.o \ |
| 555 | $(OBJDIR)/update.o \ |
| 556 | $(OBJDIR)/url.o \ |
| 557 | $(OBJDIR)/user.o \ |
| 558 | $(OBJDIR)/utf8.o \ |
| 559 | $(OBJDIR)/verify.o \ |
| @@ -651,11 +657,11 @@ | |
| 651 | ifdef FOSSIL_ENABLE_TCL |
| 652 | EXTRAOBJ += $(OBJDIR)/th_tcl.o |
| 653 | endif |
| 654 | |
| 655 | zlib: |
| 656 | make -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a |
| 657 | |
| 658 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib |
| 659 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o |
| 660 | |
| 661 | # This rule prevents make from using its default rules to try build |
| @@ -747,10 +753,11 @@ | |
| 747 | $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \ |
| 748 | $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \ |
| 749 | $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \ |
| 750 | $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \ |
| 751 | $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \ |
| 752 | $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ |
| 753 | $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ |
| 754 | $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ |
| 755 | $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ |
| 756 | $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ |
| @@ -767,10 +774,11 @@ | |
| 767 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| 768 | $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ |
| 769 | $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ |
| 770 | $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ |
| 771 | $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ |
| 772 | $(OBJDIR)/update_.c:$(OBJDIR)/update.h \ |
| 773 | $(OBJDIR)/url_.c:$(OBJDIR)/url.h \ |
| 774 | $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ |
| 775 | $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ |
| 776 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ |
| @@ -1340,10 +1348,18 @@ | |
| 1340 | |
| 1341 | $(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h |
| 1342 | $(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c |
| 1343 | |
| 1344 | $(OBJDIR)/rebuild.h: $(OBJDIR)/headers |
| 1345 | |
| 1346 | $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate |
| 1347 | $(TRANSLATE) $(SRCDIR)/report.c >$(OBJDIR)/report_.c |
| 1348 | |
| 1349 | $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h |
| @@ -1500,10 +1516,18 @@ | |
| 1500 | |
| 1501 | $(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h |
| 1502 | $(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c |
| 1503 | |
| 1504 | $(OBJDIR)/undo.h: $(OBJDIR)/headers |
| 1505 | |
| 1506 | $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate |
| 1507 | $(TRANSLATE) $(SRCDIR)/update.c >$(OBJDIR)/update_.c |
| 1508 | |
| 1509 | $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h |
| @@ -1609,11 +1633,11 @@ | |
| 1609 | |
| 1610 | $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c |
| 1611 | $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o |
| 1612 | |
| 1613 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1614 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE |
| 1615 | |
| 1616 | $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h |
| 1617 | |
| 1618 | $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h |
| 1619 | $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o |
| 1620 |
| --- win/Makefile.mingw.mistachkin | |
| +++ win/Makefile.mingw.mistachkin | |
| @@ -6,15 +6,15 @@ | |
| 6 | # |
| 7 | # This file is automatically generated. Instead of editing this |
| 8 | # file, edit "makemake.tcl" then run "tclsh makemake.tcl" |
| 9 | # to regenerate this file. |
| 10 | # |
| 11 | # This is a makefile for use on Cygwin/Darwin/FreeBSD/Linux/Windows using |
| 12 | # MinGW or MinGW-w64. |
| 13 | # |
| 14 | |
| 15 | #### Select one of MinGW, MinGW-w64 (32-bit) or MinGW-w64 (64-bit) compilers. |
| 16 | # By default, this is an empty string (i.e. use the native compiler). |
| 17 | # |
| 18 | PREFIX = |
| 19 | # PREFIX = mingw32- |
| 20 | # PREFIX = i686-pc-mingw32- |
| @@ -322,10 +322,11 @@ | |
| 322 | $(SRCDIR)/pivot.c \ |
| 323 | $(SRCDIR)/popen.c \ |
| 324 | $(SRCDIR)/pqueue.c \ |
| 325 | $(SRCDIR)/printf.c \ |
| 326 | $(SRCDIR)/rebuild.c \ |
| 327 | $(SRCDIR)/regexp.c \ |
| 328 | $(SRCDIR)/report.c \ |
| 329 | $(SRCDIR)/rss.c \ |
| 330 | $(SRCDIR)/schema.c \ |
| 331 | $(SRCDIR)/search.c \ |
| 332 | $(SRCDIR)/setup.c \ |
| @@ -342,10 +343,11 @@ | |
| 343 | $(SRCDIR)/th_main.c \ |
| 344 | $(SRCDIR)/timeline.c \ |
| 345 | $(SRCDIR)/tkt.c \ |
| 346 | $(SRCDIR)/tktsetup.c \ |
| 347 | $(SRCDIR)/undo.c \ |
| 348 | $(SRCDIR)/unicode.c \ |
| 349 | $(SRCDIR)/update.c \ |
| 350 | $(SRCDIR)/url.c \ |
| 351 | $(SRCDIR)/user.c \ |
| 352 | $(SRCDIR)/utf8.c \ |
| 353 | $(SRCDIR)/verify.c \ |
| @@ -426,10 +428,11 @@ | |
| 428 | $(OBJDIR)/pivot_.c \ |
| 429 | $(OBJDIR)/popen_.c \ |
| 430 | $(OBJDIR)/pqueue_.c \ |
| 431 | $(OBJDIR)/printf_.c \ |
| 432 | $(OBJDIR)/rebuild_.c \ |
| 433 | $(OBJDIR)/regexp_.c \ |
| 434 | $(OBJDIR)/report_.c \ |
| 435 | $(OBJDIR)/rss_.c \ |
| 436 | $(OBJDIR)/schema_.c \ |
| 437 | $(OBJDIR)/search_.c \ |
| 438 | $(OBJDIR)/setup_.c \ |
| @@ -446,10 +449,11 @@ | |
| 449 | $(OBJDIR)/th_main_.c \ |
| 450 | $(OBJDIR)/timeline_.c \ |
| 451 | $(OBJDIR)/tkt_.c \ |
| 452 | $(OBJDIR)/tktsetup_.c \ |
| 453 | $(OBJDIR)/undo_.c \ |
| 454 | $(OBJDIR)/unicode_.c \ |
| 455 | $(OBJDIR)/update_.c \ |
| 456 | $(OBJDIR)/url_.c \ |
| 457 | $(OBJDIR)/user_.c \ |
| 458 | $(OBJDIR)/utf8_.c \ |
| 459 | $(OBJDIR)/verify_.c \ |
| @@ -530,10 +534,11 @@ | |
| 534 | $(OBJDIR)/pivot.o \ |
| 535 | $(OBJDIR)/popen.o \ |
| 536 | $(OBJDIR)/pqueue.o \ |
| 537 | $(OBJDIR)/printf.o \ |
| 538 | $(OBJDIR)/rebuild.o \ |
| 539 | $(OBJDIR)/regexp.o \ |
| 540 | $(OBJDIR)/report.o \ |
| 541 | $(OBJDIR)/rss.o \ |
| 542 | $(OBJDIR)/schema.o \ |
| 543 | $(OBJDIR)/search.o \ |
| 544 | $(OBJDIR)/setup.o \ |
| @@ -550,10 +555,11 @@ | |
| 555 | $(OBJDIR)/th_main.o \ |
| 556 | $(OBJDIR)/timeline.o \ |
| 557 | $(OBJDIR)/tkt.o \ |
| 558 | $(OBJDIR)/tktsetup.o \ |
| 559 | $(OBJDIR)/undo.o \ |
| 560 | $(OBJDIR)/unicode.o \ |
| 561 | $(OBJDIR)/update.o \ |
| 562 | $(OBJDIR)/url.o \ |
| 563 | $(OBJDIR)/user.o \ |
| 564 | $(OBJDIR)/utf8.o \ |
| 565 | $(OBJDIR)/verify.o \ |
| @@ -651,11 +657,11 @@ | |
| 657 | ifdef FOSSIL_ENABLE_TCL |
| 658 | EXTRAOBJ += $(OBJDIR)/th_tcl.o |
| 659 | endif |
| 660 | |
| 661 | zlib: |
| 662 | $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a |
| 663 | |
| 664 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib |
| 665 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o |
| 666 | |
| 667 | # This rule prevents make from using its default rules to try build |
| @@ -747,10 +753,11 @@ | |
| 753 | $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \ |
| 754 | $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \ |
| 755 | $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \ |
| 756 | $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \ |
| 757 | $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \ |
| 758 | $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ |
| 759 | $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ |
| 760 | $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ |
| 761 | $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ |
| 762 | $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ |
| 763 | $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ |
| @@ -767,10 +774,11 @@ | |
| 774 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| 775 | $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ |
| 776 | $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ |
| 777 | $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ |
| 778 | $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ |
| 779 | $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \ |
| 780 | $(OBJDIR)/update_.c:$(OBJDIR)/update.h \ |
| 781 | $(OBJDIR)/url_.c:$(OBJDIR)/url.h \ |
| 782 | $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ |
| 783 | $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ |
| 784 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ |
| @@ -1340,10 +1348,18 @@ | |
| 1348 | |
| 1349 | $(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h |
| 1350 | $(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c |
| 1351 | |
| 1352 | $(OBJDIR)/rebuild.h: $(OBJDIR)/headers |
| 1353 | |
| 1354 | $(OBJDIR)/regexp_.c: $(SRCDIR)/regexp.c $(OBJDIR)/translate |
| 1355 | $(TRANSLATE) $(SRCDIR)/regexp.c >$(OBJDIR)/regexp_.c |
| 1356 | |
| 1357 | $(OBJDIR)/regexp.o: $(OBJDIR)/regexp_.c $(OBJDIR)/regexp.h $(SRCDIR)/config.h |
| 1358 | $(XTCC) -o $(OBJDIR)/regexp.o -c $(OBJDIR)/regexp_.c |
| 1359 | |
| 1360 | $(OBJDIR)/regexp.h: $(OBJDIR)/headers |
| 1361 | |
| 1362 | $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate |
| 1363 | $(TRANSLATE) $(SRCDIR)/report.c >$(OBJDIR)/report_.c |
| 1364 | |
| 1365 | $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h |
| @@ -1500,10 +1516,18 @@ | |
| 1516 | |
| 1517 | $(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h |
| 1518 | $(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c |
| 1519 | |
| 1520 | $(OBJDIR)/undo.h: $(OBJDIR)/headers |
| 1521 | |
| 1522 | $(OBJDIR)/unicode_.c: $(SRCDIR)/unicode.c $(OBJDIR)/translate |
| 1523 | $(TRANSLATE) $(SRCDIR)/unicode.c >$(OBJDIR)/unicode_.c |
| 1524 | |
| 1525 | $(OBJDIR)/unicode.o: $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h |
| 1526 | $(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c |
| 1527 | |
| 1528 | $(OBJDIR)/unicode.h: $(OBJDIR)/headers |
| 1529 | |
| 1530 | $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate |
| 1531 | $(TRANSLATE) $(SRCDIR)/update.c >$(OBJDIR)/update_.c |
| 1532 | |
| 1533 | $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h |
| @@ -1609,11 +1633,11 @@ | |
| 1633 | |
| 1634 | $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c |
| 1635 | $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o |
| 1636 | |
| 1637 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1638 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o |
| 1639 | |
| 1640 | $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h |
| 1641 | |
| 1642 | $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h |
| 1643 | $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o |
| 1644 |
+29
-5
| --- win/Makefile.mingw.mistachkin | ||
| +++ win/Makefile.mingw.mistachkin | ||
| @@ -6,15 +6,15 @@ | ||
| 6 | 6 | # |
| 7 | 7 | # This file is automatically generated. Instead of editing this |
| 8 | 8 | # file, edit "makemake.tcl" then run "tclsh makemake.tcl" |
| 9 | 9 | # to regenerate this file. |
| 10 | 10 | # |
| 11 | -# This is a makefile for use on Windows/Linux/Darwin/Cygwin using MinGW or | |
| 12 | -# MinGW-w64. | |
| 11 | +# This is a makefile for use on Cygwin/Darwin/FreeBSD/Linux/Windows using | |
| 12 | +# MinGW or MinGW-w64. | |
| 13 | 13 | # |
| 14 | 14 | |
| 15 | -#### Select one of MinGW, MinGW-64 (32-bit) or MinGW-w64 (64-bit) compilers. | |
| 15 | +#### Select one of MinGW, MinGW-w64 (32-bit) or MinGW-w64 (64-bit) compilers. | |
| 16 | 16 | # By default, this is an empty string (i.e. use the native compiler). |
| 17 | 17 | # |
| 18 | 18 | PREFIX = |
| 19 | 19 | # PREFIX = mingw32- |
| 20 | 20 | # PREFIX = i686-pc-mingw32- |
| @@ -322,10 +322,11 @@ | ||
| 322 | 322 | $(SRCDIR)/pivot.c \ |
| 323 | 323 | $(SRCDIR)/popen.c \ |
| 324 | 324 | $(SRCDIR)/pqueue.c \ |
| 325 | 325 | $(SRCDIR)/printf.c \ |
| 326 | 326 | $(SRCDIR)/rebuild.c \ |
| 327 | + $(SRCDIR)/regexp.c \ | |
| 327 | 328 | $(SRCDIR)/report.c \ |
| 328 | 329 | $(SRCDIR)/rss.c \ |
| 329 | 330 | $(SRCDIR)/schema.c \ |
| 330 | 331 | $(SRCDIR)/search.c \ |
| 331 | 332 | $(SRCDIR)/setup.c \ |
| @@ -342,10 +343,11 @@ | ||
| 342 | 343 | $(SRCDIR)/th_main.c \ |
| 343 | 344 | $(SRCDIR)/timeline.c \ |
| 344 | 345 | $(SRCDIR)/tkt.c \ |
| 345 | 346 | $(SRCDIR)/tktsetup.c \ |
| 346 | 347 | $(SRCDIR)/undo.c \ |
| 348 | + $(SRCDIR)/unicode.c \ | |
| 347 | 349 | $(SRCDIR)/update.c \ |
| 348 | 350 | $(SRCDIR)/url.c \ |
| 349 | 351 | $(SRCDIR)/user.c \ |
| 350 | 352 | $(SRCDIR)/utf8.c \ |
| 351 | 353 | $(SRCDIR)/verify.c \ |
| @@ -426,10 +428,11 @@ | ||
| 426 | 428 | $(OBJDIR)/pivot_.c \ |
| 427 | 429 | $(OBJDIR)/popen_.c \ |
| 428 | 430 | $(OBJDIR)/pqueue_.c \ |
| 429 | 431 | $(OBJDIR)/printf_.c \ |
| 430 | 432 | $(OBJDIR)/rebuild_.c \ |
| 433 | + $(OBJDIR)/regexp_.c \ | |
| 431 | 434 | $(OBJDIR)/report_.c \ |
| 432 | 435 | $(OBJDIR)/rss_.c \ |
| 433 | 436 | $(OBJDIR)/schema_.c \ |
| 434 | 437 | $(OBJDIR)/search_.c \ |
| 435 | 438 | $(OBJDIR)/setup_.c \ |
| @@ -446,10 +449,11 @@ | ||
| 446 | 449 | $(OBJDIR)/th_main_.c \ |
| 447 | 450 | $(OBJDIR)/timeline_.c \ |
| 448 | 451 | $(OBJDIR)/tkt_.c \ |
| 449 | 452 | $(OBJDIR)/tktsetup_.c \ |
| 450 | 453 | $(OBJDIR)/undo_.c \ |
| 454 | + $(OBJDIR)/unicode_.c \ | |
| 451 | 455 | $(OBJDIR)/update_.c \ |
| 452 | 456 | $(OBJDIR)/url_.c \ |
| 453 | 457 | $(OBJDIR)/user_.c \ |
| 454 | 458 | $(OBJDIR)/utf8_.c \ |
| 455 | 459 | $(OBJDIR)/verify_.c \ |
| @@ -530,10 +534,11 @@ | ||
| 530 | 534 | $(OBJDIR)/pivot.o \ |
| 531 | 535 | $(OBJDIR)/popen.o \ |
| 532 | 536 | $(OBJDIR)/pqueue.o \ |
| 533 | 537 | $(OBJDIR)/printf.o \ |
| 534 | 538 | $(OBJDIR)/rebuild.o \ |
| 539 | + $(OBJDIR)/regexp.o \ | |
| 535 | 540 | $(OBJDIR)/report.o \ |
| 536 | 541 | $(OBJDIR)/rss.o \ |
| 537 | 542 | $(OBJDIR)/schema.o \ |
| 538 | 543 | $(OBJDIR)/search.o \ |
| 539 | 544 | $(OBJDIR)/setup.o \ |
| @@ -550,10 +555,11 @@ | ||
| 550 | 555 | $(OBJDIR)/th_main.o \ |
| 551 | 556 | $(OBJDIR)/timeline.o \ |
| 552 | 557 | $(OBJDIR)/tkt.o \ |
| 553 | 558 | $(OBJDIR)/tktsetup.o \ |
| 554 | 559 | $(OBJDIR)/undo.o \ |
| 560 | + $(OBJDIR)/unicode.o \ | |
| 555 | 561 | $(OBJDIR)/update.o \ |
| 556 | 562 | $(OBJDIR)/url.o \ |
| 557 | 563 | $(OBJDIR)/user.o \ |
| 558 | 564 | $(OBJDIR)/utf8.o \ |
| 559 | 565 | $(OBJDIR)/verify.o \ |
| @@ -651,11 +657,11 @@ | ||
| 651 | 657 | ifdef FOSSIL_ENABLE_TCL |
| 652 | 658 | EXTRAOBJ += $(OBJDIR)/th_tcl.o |
| 653 | 659 | endif |
| 654 | 660 | |
| 655 | 661 | zlib: |
| 656 | - make -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a | |
| 662 | + $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a | |
| 657 | 663 | |
| 658 | 664 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib |
| 659 | 665 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o |
| 660 | 666 | |
| 661 | 667 | # This rule prevents make from using its default rules to try build |
| @@ -747,10 +753,11 @@ | ||
| 747 | 753 | $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \ |
| 748 | 754 | $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \ |
| 749 | 755 | $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \ |
| 750 | 756 | $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \ |
| 751 | 757 | $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \ |
| 758 | + $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ | |
| 752 | 759 | $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ |
| 753 | 760 | $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ |
| 754 | 761 | $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ |
| 755 | 762 | $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ |
| 756 | 763 | $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ |
| @@ -767,10 +774,11 @@ | ||
| 767 | 774 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| 768 | 775 | $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ |
| 769 | 776 | $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ |
| 770 | 777 | $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ |
| 771 | 778 | $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ |
| 779 | + $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \ | |
| 772 | 780 | $(OBJDIR)/update_.c:$(OBJDIR)/update.h \ |
| 773 | 781 | $(OBJDIR)/url_.c:$(OBJDIR)/url.h \ |
| 774 | 782 | $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ |
| 775 | 783 | $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ |
| 776 | 784 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ |
| @@ -1340,10 +1348,18 @@ | ||
| 1340 | 1348 | |
| 1341 | 1349 | $(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h |
| 1342 | 1350 | $(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c |
| 1343 | 1351 | |
| 1344 | 1352 | $(OBJDIR)/rebuild.h: $(OBJDIR)/headers |
| 1353 | + | |
| 1354 | +$(OBJDIR)/regexp_.c: $(SRCDIR)/regexp.c $(OBJDIR)/translate | |
| 1355 | + $(TRANSLATE) $(SRCDIR)/regexp.c >$(OBJDIR)/regexp_.c | |
| 1356 | + | |
| 1357 | +$(OBJDIR)/regexp.o: $(OBJDIR)/regexp_.c $(OBJDIR)/regexp.h $(SRCDIR)/config.h | |
| 1358 | + $(XTCC) -o $(OBJDIR)/regexp.o -c $(OBJDIR)/regexp_.c | |
| 1359 | + | |
| 1360 | +$(OBJDIR)/regexp.h: $(OBJDIR)/headers | |
| 1345 | 1361 | |
| 1346 | 1362 | $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate |
| 1347 | 1363 | $(TRANSLATE) $(SRCDIR)/report.c >$(OBJDIR)/report_.c |
| 1348 | 1364 | |
| 1349 | 1365 | $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h |
| @@ -1500,10 +1516,18 @@ | ||
| 1500 | 1516 | |
| 1501 | 1517 | $(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h |
| 1502 | 1518 | $(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c |
| 1503 | 1519 | |
| 1504 | 1520 | $(OBJDIR)/undo.h: $(OBJDIR)/headers |
| 1521 | + | |
| 1522 | +$(OBJDIR)/unicode_.c: $(SRCDIR)/unicode.c $(OBJDIR)/translate | |
| 1523 | + $(TRANSLATE) $(SRCDIR)/unicode.c >$(OBJDIR)/unicode_.c | |
| 1524 | + | |
| 1525 | +$(OBJDIR)/unicode.o: $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h | |
| 1526 | + $(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c | |
| 1527 | + | |
| 1528 | +$(OBJDIR)/unicode.h: $(OBJDIR)/headers | |
| 1505 | 1529 | |
| 1506 | 1530 | $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate |
| 1507 | 1531 | $(TRANSLATE) $(SRCDIR)/update.c >$(OBJDIR)/update_.c |
| 1508 | 1532 | |
| 1509 | 1533 | $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h |
| @@ -1609,11 +1633,11 @@ | ||
| 1609 | 1633 | |
| 1610 | 1634 | $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c |
| 1611 | 1635 | $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o |
| 1612 | 1636 | |
| 1613 | 1637 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1614 | - $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE | |
| 1638 | + $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o | |
| 1615 | 1639 | |
| 1616 | 1640 | $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h |
| 1617 | 1641 | |
| 1618 | 1642 | $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h |
| 1619 | 1643 | $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o |
| 1620 | 1644 |
| --- win/Makefile.mingw.mistachkin | |
| +++ win/Makefile.mingw.mistachkin | |
| @@ -6,15 +6,15 @@ | |
| 6 | # |
| 7 | # This file is automatically generated. Instead of editing this |
| 8 | # file, edit "makemake.tcl" then run "tclsh makemake.tcl" |
| 9 | # to regenerate this file. |
| 10 | # |
| 11 | # This is a makefile for use on Windows/Linux/Darwin/Cygwin using MinGW or |
| 12 | # MinGW-w64. |
| 13 | # |
| 14 | |
| 15 | #### Select one of MinGW, MinGW-64 (32-bit) or MinGW-w64 (64-bit) compilers. |
| 16 | # By default, this is an empty string (i.e. use the native compiler). |
| 17 | # |
| 18 | PREFIX = |
| 19 | # PREFIX = mingw32- |
| 20 | # PREFIX = i686-pc-mingw32- |
| @@ -322,10 +322,11 @@ | |
| 322 | $(SRCDIR)/pivot.c \ |
| 323 | $(SRCDIR)/popen.c \ |
| 324 | $(SRCDIR)/pqueue.c \ |
| 325 | $(SRCDIR)/printf.c \ |
| 326 | $(SRCDIR)/rebuild.c \ |
| 327 | $(SRCDIR)/report.c \ |
| 328 | $(SRCDIR)/rss.c \ |
| 329 | $(SRCDIR)/schema.c \ |
| 330 | $(SRCDIR)/search.c \ |
| 331 | $(SRCDIR)/setup.c \ |
| @@ -342,10 +343,11 @@ | |
| 342 | $(SRCDIR)/th_main.c \ |
| 343 | $(SRCDIR)/timeline.c \ |
| 344 | $(SRCDIR)/tkt.c \ |
| 345 | $(SRCDIR)/tktsetup.c \ |
| 346 | $(SRCDIR)/undo.c \ |
| 347 | $(SRCDIR)/update.c \ |
| 348 | $(SRCDIR)/url.c \ |
| 349 | $(SRCDIR)/user.c \ |
| 350 | $(SRCDIR)/utf8.c \ |
| 351 | $(SRCDIR)/verify.c \ |
| @@ -426,10 +428,11 @@ | |
| 426 | $(OBJDIR)/pivot_.c \ |
| 427 | $(OBJDIR)/popen_.c \ |
| 428 | $(OBJDIR)/pqueue_.c \ |
| 429 | $(OBJDIR)/printf_.c \ |
| 430 | $(OBJDIR)/rebuild_.c \ |
| 431 | $(OBJDIR)/report_.c \ |
| 432 | $(OBJDIR)/rss_.c \ |
| 433 | $(OBJDIR)/schema_.c \ |
| 434 | $(OBJDIR)/search_.c \ |
| 435 | $(OBJDIR)/setup_.c \ |
| @@ -446,10 +449,11 @@ | |
| 446 | $(OBJDIR)/th_main_.c \ |
| 447 | $(OBJDIR)/timeline_.c \ |
| 448 | $(OBJDIR)/tkt_.c \ |
| 449 | $(OBJDIR)/tktsetup_.c \ |
| 450 | $(OBJDIR)/undo_.c \ |
| 451 | $(OBJDIR)/update_.c \ |
| 452 | $(OBJDIR)/url_.c \ |
| 453 | $(OBJDIR)/user_.c \ |
| 454 | $(OBJDIR)/utf8_.c \ |
| 455 | $(OBJDIR)/verify_.c \ |
| @@ -530,10 +534,11 @@ | |
| 530 | $(OBJDIR)/pivot.o \ |
| 531 | $(OBJDIR)/popen.o \ |
| 532 | $(OBJDIR)/pqueue.o \ |
| 533 | $(OBJDIR)/printf.o \ |
| 534 | $(OBJDIR)/rebuild.o \ |
| 535 | $(OBJDIR)/report.o \ |
| 536 | $(OBJDIR)/rss.o \ |
| 537 | $(OBJDIR)/schema.o \ |
| 538 | $(OBJDIR)/search.o \ |
| 539 | $(OBJDIR)/setup.o \ |
| @@ -550,10 +555,11 @@ | |
| 550 | $(OBJDIR)/th_main.o \ |
| 551 | $(OBJDIR)/timeline.o \ |
| 552 | $(OBJDIR)/tkt.o \ |
| 553 | $(OBJDIR)/tktsetup.o \ |
| 554 | $(OBJDIR)/undo.o \ |
| 555 | $(OBJDIR)/update.o \ |
| 556 | $(OBJDIR)/url.o \ |
| 557 | $(OBJDIR)/user.o \ |
| 558 | $(OBJDIR)/utf8.o \ |
| 559 | $(OBJDIR)/verify.o \ |
| @@ -651,11 +657,11 @@ | |
| 651 | ifdef FOSSIL_ENABLE_TCL |
| 652 | EXTRAOBJ += $(OBJDIR)/th_tcl.o |
| 653 | endif |
| 654 | |
| 655 | zlib: |
| 656 | make -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a |
| 657 | |
| 658 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib |
| 659 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o |
| 660 | |
| 661 | # This rule prevents make from using its default rules to try build |
| @@ -747,10 +753,11 @@ | |
| 747 | $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \ |
| 748 | $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \ |
| 749 | $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \ |
| 750 | $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \ |
| 751 | $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \ |
| 752 | $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ |
| 753 | $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ |
| 754 | $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ |
| 755 | $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ |
| 756 | $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ |
| @@ -767,10 +774,11 @@ | |
| 767 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| 768 | $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ |
| 769 | $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ |
| 770 | $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ |
| 771 | $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ |
| 772 | $(OBJDIR)/update_.c:$(OBJDIR)/update.h \ |
| 773 | $(OBJDIR)/url_.c:$(OBJDIR)/url.h \ |
| 774 | $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ |
| 775 | $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ |
| 776 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ |
| @@ -1340,10 +1348,18 @@ | |
| 1340 | |
| 1341 | $(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h |
| 1342 | $(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c |
| 1343 | |
| 1344 | $(OBJDIR)/rebuild.h: $(OBJDIR)/headers |
| 1345 | |
| 1346 | $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate |
| 1347 | $(TRANSLATE) $(SRCDIR)/report.c >$(OBJDIR)/report_.c |
| 1348 | |
| 1349 | $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h |
| @@ -1500,10 +1516,18 @@ | |
| 1500 | |
| 1501 | $(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h |
| 1502 | $(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c |
| 1503 | |
| 1504 | $(OBJDIR)/undo.h: $(OBJDIR)/headers |
| 1505 | |
| 1506 | $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate |
| 1507 | $(TRANSLATE) $(SRCDIR)/update.c >$(OBJDIR)/update_.c |
| 1508 | |
| 1509 | $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h |
| @@ -1609,11 +1633,11 @@ | |
| 1609 | |
| 1610 | $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c |
| 1611 | $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o |
| 1612 | |
| 1613 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1614 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE |
| 1615 | |
| 1616 | $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h |
| 1617 | |
| 1618 | $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h |
| 1619 | $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o |
| 1620 |
| --- win/Makefile.mingw.mistachkin | |
| +++ win/Makefile.mingw.mistachkin | |
| @@ -6,15 +6,15 @@ | |
| 6 | # |
| 7 | # This file is automatically generated. Instead of editing this |
| 8 | # file, edit "makemake.tcl" then run "tclsh makemake.tcl" |
| 9 | # to regenerate this file. |
| 10 | # |
| 11 | # This is a makefile for use on Cygwin/Darwin/FreeBSD/Linux/Windows using |
| 12 | # MinGW or MinGW-w64. |
| 13 | # |
| 14 | |
| 15 | #### Select one of MinGW, MinGW-w64 (32-bit) or MinGW-w64 (64-bit) compilers. |
| 16 | # By default, this is an empty string (i.e. use the native compiler). |
| 17 | # |
| 18 | PREFIX = |
| 19 | # PREFIX = mingw32- |
| 20 | # PREFIX = i686-pc-mingw32- |
| @@ -322,10 +322,11 @@ | |
| 322 | $(SRCDIR)/pivot.c \ |
| 323 | $(SRCDIR)/popen.c \ |
| 324 | $(SRCDIR)/pqueue.c \ |
| 325 | $(SRCDIR)/printf.c \ |
| 326 | $(SRCDIR)/rebuild.c \ |
| 327 | $(SRCDIR)/regexp.c \ |
| 328 | $(SRCDIR)/report.c \ |
| 329 | $(SRCDIR)/rss.c \ |
| 330 | $(SRCDIR)/schema.c \ |
| 331 | $(SRCDIR)/search.c \ |
| 332 | $(SRCDIR)/setup.c \ |
| @@ -342,10 +343,11 @@ | |
| 343 | $(SRCDIR)/th_main.c \ |
| 344 | $(SRCDIR)/timeline.c \ |
| 345 | $(SRCDIR)/tkt.c \ |
| 346 | $(SRCDIR)/tktsetup.c \ |
| 347 | $(SRCDIR)/undo.c \ |
| 348 | $(SRCDIR)/unicode.c \ |
| 349 | $(SRCDIR)/update.c \ |
| 350 | $(SRCDIR)/url.c \ |
| 351 | $(SRCDIR)/user.c \ |
| 352 | $(SRCDIR)/utf8.c \ |
| 353 | $(SRCDIR)/verify.c \ |
| @@ -426,10 +428,11 @@ | |
| 428 | $(OBJDIR)/pivot_.c \ |
| 429 | $(OBJDIR)/popen_.c \ |
| 430 | $(OBJDIR)/pqueue_.c \ |
| 431 | $(OBJDIR)/printf_.c \ |
| 432 | $(OBJDIR)/rebuild_.c \ |
| 433 | $(OBJDIR)/regexp_.c \ |
| 434 | $(OBJDIR)/report_.c \ |
| 435 | $(OBJDIR)/rss_.c \ |
| 436 | $(OBJDIR)/schema_.c \ |
| 437 | $(OBJDIR)/search_.c \ |
| 438 | $(OBJDIR)/setup_.c \ |
| @@ -446,10 +449,11 @@ | |
| 449 | $(OBJDIR)/th_main_.c \ |
| 450 | $(OBJDIR)/timeline_.c \ |
| 451 | $(OBJDIR)/tkt_.c \ |
| 452 | $(OBJDIR)/tktsetup_.c \ |
| 453 | $(OBJDIR)/undo_.c \ |
| 454 | $(OBJDIR)/unicode_.c \ |
| 455 | $(OBJDIR)/update_.c \ |
| 456 | $(OBJDIR)/url_.c \ |
| 457 | $(OBJDIR)/user_.c \ |
| 458 | $(OBJDIR)/utf8_.c \ |
| 459 | $(OBJDIR)/verify_.c \ |
| @@ -530,10 +534,11 @@ | |
| 534 | $(OBJDIR)/pivot.o \ |
| 535 | $(OBJDIR)/popen.o \ |
| 536 | $(OBJDIR)/pqueue.o \ |
| 537 | $(OBJDIR)/printf.o \ |
| 538 | $(OBJDIR)/rebuild.o \ |
| 539 | $(OBJDIR)/regexp.o \ |
| 540 | $(OBJDIR)/report.o \ |
| 541 | $(OBJDIR)/rss.o \ |
| 542 | $(OBJDIR)/schema.o \ |
| 543 | $(OBJDIR)/search.o \ |
| 544 | $(OBJDIR)/setup.o \ |
| @@ -550,10 +555,11 @@ | |
| 555 | $(OBJDIR)/th_main.o \ |
| 556 | $(OBJDIR)/timeline.o \ |
| 557 | $(OBJDIR)/tkt.o \ |
| 558 | $(OBJDIR)/tktsetup.o \ |
| 559 | $(OBJDIR)/undo.o \ |
| 560 | $(OBJDIR)/unicode.o \ |
| 561 | $(OBJDIR)/update.o \ |
| 562 | $(OBJDIR)/url.o \ |
| 563 | $(OBJDIR)/user.o \ |
| 564 | $(OBJDIR)/utf8.o \ |
| 565 | $(OBJDIR)/verify.o \ |
| @@ -651,11 +657,11 @@ | |
| 657 | ifdef FOSSIL_ENABLE_TCL |
| 658 | EXTRAOBJ += $(OBJDIR)/th_tcl.o |
| 659 | endif |
| 660 | |
| 661 | zlib: |
| 662 | $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc libz.a |
| 663 | |
| 664 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o zlib |
| 665 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o |
| 666 | |
| 667 | # This rule prevents make from using its default rules to try build |
| @@ -747,10 +753,11 @@ | |
| 753 | $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \ |
| 754 | $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \ |
| 755 | $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \ |
| 756 | $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \ |
| 757 | $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \ |
| 758 | $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ |
| 759 | $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ |
| 760 | $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ |
| 761 | $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ |
| 762 | $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ |
| 763 | $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ |
| @@ -767,10 +774,11 @@ | |
| 774 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| 775 | $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ |
| 776 | $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ |
| 777 | $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ |
| 778 | $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ |
| 779 | $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \ |
| 780 | $(OBJDIR)/update_.c:$(OBJDIR)/update.h \ |
| 781 | $(OBJDIR)/url_.c:$(OBJDIR)/url.h \ |
| 782 | $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ |
| 783 | $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ |
| 784 | $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ |
| @@ -1340,10 +1348,18 @@ | |
| 1348 | |
| 1349 | $(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h |
| 1350 | $(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c |
| 1351 | |
| 1352 | $(OBJDIR)/rebuild.h: $(OBJDIR)/headers |
| 1353 | |
| 1354 | $(OBJDIR)/regexp_.c: $(SRCDIR)/regexp.c $(OBJDIR)/translate |
| 1355 | $(TRANSLATE) $(SRCDIR)/regexp.c >$(OBJDIR)/regexp_.c |
| 1356 | |
| 1357 | $(OBJDIR)/regexp.o: $(OBJDIR)/regexp_.c $(OBJDIR)/regexp.h $(SRCDIR)/config.h |
| 1358 | $(XTCC) -o $(OBJDIR)/regexp.o -c $(OBJDIR)/regexp_.c |
| 1359 | |
| 1360 | $(OBJDIR)/regexp.h: $(OBJDIR)/headers |
| 1361 | |
| 1362 | $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate |
| 1363 | $(TRANSLATE) $(SRCDIR)/report.c >$(OBJDIR)/report_.c |
| 1364 | |
| 1365 | $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h |
| @@ -1500,10 +1516,18 @@ | |
| 1516 | |
| 1517 | $(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h |
| 1518 | $(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c |
| 1519 | |
| 1520 | $(OBJDIR)/undo.h: $(OBJDIR)/headers |
| 1521 | |
| 1522 | $(OBJDIR)/unicode_.c: $(SRCDIR)/unicode.c $(OBJDIR)/translate |
| 1523 | $(TRANSLATE) $(SRCDIR)/unicode.c >$(OBJDIR)/unicode_.c |
| 1524 | |
| 1525 | $(OBJDIR)/unicode.o: $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h |
| 1526 | $(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c |
| 1527 | |
| 1528 | $(OBJDIR)/unicode.h: $(OBJDIR)/headers |
| 1529 | |
| 1530 | $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate |
| 1531 | $(TRANSLATE) $(SRCDIR)/update.c >$(OBJDIR)/update_.c |
| 1532 | |
| 1533 | $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h |
| @@ -1609,11 +1633,11 @@ | |
| 1633 | |
| 1634 | $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c |
| 1635 | $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o |
| 1636 | |
| 1637 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c |
| 1638 | $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o |
| 1639 | |
| 1640 | $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h |
| 1641 | |
| 1642 | $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h |
| 1643 | $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o |
| 1644 |
+30
-7
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -22,18 +22,35 @@ | ||
| 22 | 22 | |
| 23 | 23 | # zlib options |
| 24 | 24 | ZINCDIR = $(B)\compat\zlib |
| 25 | 25 | ZLIBDIR = $(B)\compat\zlib |
| 26 | 26 | ZLIB = zlib.lib |
| 27 | + | |
| 28 | +# Uncomment to enable JSON API | |
| 29 | +# FOSSIL_ENABLE_JSON = 1 | |
| 30 | + | |
| 31 | +# Uncomment to enable markdown support | |
| 32 | +# FOSSIL_ENABLE_MARKDOWN = 1 | |
| 27 | 33 | |
| 28 | 34 | INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR) |
| 29 | 35 | |
| 30 | 36 | CFLAGS = -nologo -MT -O2 |
| 31 | 37 | BCC = $(CC) $(CFLAGS) |
| 32 | 38 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) |
| 39 | +RCC = rc -D_WIN32 -D_MSC_VER $(INCL) | |
| 33 | 40 | LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) |
| 34 | 41 | LIBDIR = -LIBPATH:$(ZLIBDIR) |
| 42 | + | |
| 43 | +!ifdef FOSSIL_ENABLE_JSON | |
| 44 | +TCC = $(TCC) -DFOSSIL_ENABLE_JSON | |
| 45 | +RCC = $(RCC) -DFOSSIL_ENABLE_JSON | |
| 46 | +!endif | |
| 47 | + | |
| 48 | +!ifdef FOSSIL_ENABLE_MARKDOWN | |
| 49 | +TCC = $(TCC) -DFOSSIL_ENABLE_MARKDOWN | |
| 50 | +RCC = $(RCC) -DFOSSIL_ENABLE_MARKDOWN | |
| 51 | +!endif | |
| 35 | 52 | |
| 36 | 53 | SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 \ |
| 37 | 54 | /DSQLITE_THREADSAFE=0 \ |
| 38 | 55 | /DSQLITE_DEFAULT_FILE_FORMAT=4 \ |
| 39 | 56 | /DSQLITE_ENABLE_STAT3 \ |
| @@ -160,10 +177,11 @@ | ||
| 160 | 177 | $(OX)\clearsign$O \ |
| 161 | 178 | $(OX)\clone$O \ |
| 162 | 179 | $(OX)\comformat$O \ |
| 163 | 180 | $(OX)\configure$O \ |
| 164 | 181 | $(OX)\content$O \ |
| 182 | + $(OX)\cson_amalgamation$O \ | |
| 165 | 183 | $(OX)\db$O \ |
| 166 | 184 | $(OX)\delta$O \ |
| 167 | 185 | $(OX)\deltacmd$O \ |
| 168 | 186 | $(OX)\descendants$O \ |
| 169 | 187 | $(OX)\diff$O \ |
| @@ -219,19 +237,23 @@ | ||
| 219 | 237 | $(OX)\rss$O \ |
| 220 | 238 | $(OX)\schema$O \ |
| 221 | 239 | $(OX)\search$O \ |
| 222 | 240 | $(OX)\setup$O \ |
| 223 | 241 | $(OX)\sha1$O \ |
| 242 | + $(OX)\shell$O \ | |
| 224 | 243 | $(OX)\shun$O \ |
| 225 | 244 | $(OX)\skins$O \ |
| 226 | 245 | $(OX)\sqlcmd$O \ |
| 246 | + $(OX)\sqlite3$O \ | |
| 227 | 247 | $(OX)\stash$O \ |
| 228 | 248 | $(OX)\stat$O \ |
| 229 | 249 | $(OX)\style$O \ |
| 230 | 250 | $(OX)\sync$O \ |
| 231 | 251 | $(OX)\tag$O \ |
| 232 | 252 | $(OX)\tar$O \ |
| 253 | + $(OX)\th$O \ | |
| 254 | + $(OX)\th_lang$O \ | |
| 233 | 255 | $(OX)\th_main$O \ |
| 234 | 256 | $(OX)\timeline$O \ |
| 235 | 257 | $(OX)\tkt$O \ |
| 236 | 258 | $(OX)\tktsetup$O \ |
| 237 | 259 | $(OX)\undo$O \ |
| @@ -247,14 +269,11 @@ | ||
| 247 | 269 | $(OX)\winhttp$O \ |
| 248 | 270 | $(OX)\wysiwyg$O \ |
| 249 | 271 | $(OX)\xfer$O \ |
| 250 | 272 | $(OX)\xfersetup$O \ |
| 251 | 273 | $(OX)\zip$O \ |
| 252 | - $(OX)\shell$O \ | |
| 253 | - $(OX)\sqlite3$O \ | |
| 254 | - $(OX)\th$O \ | |
| 255 | - $(OX)\th_lang$O | |
| 274 | + $(OX)\fossil.res | |
| 256 | 275 | |
| 257 | 276 | APPNAME = $(OX)\fossil$(E) |
| 258 | 277 | |
| 259 | 278 | all: $(OX) $(APPNAME) |
| 260 | 279 | |
| @@ -262,11 +281,11 @@ | ||
| 262 | 281 | @echo Building zlib from "$(ZLIBDIR)"... |
| 263 | 282 | @pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd |
| 264 | 283 | |
| 265 | 284 | $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib |
| 266 | 285 | cd $(OX) |
| 267 | - link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj @linkopts | |
| 286 | + link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts | |
| 268 | 287 | |
| 269 | 288 | $(OX)\linkopts: $B\win\Makefile.msc |
| 270 | 289 | echo $(OX)\add.obj > $@ |
| 271 | 290 | echo $(OX)\allrepo.obj >> $@ |
| 272 | 291 | echo $(OX)\attach.obj >> $@ |
| @@ -282,10 +301,11 @@ | ||
| 282 | 301 | echo $(OX)\clearsign.obj >> $@ |
| 283 | 302 | echo $(OX)\clone.obj >> $@ |
| 284 | 303 | echo $(OX)\comformat.obj >> $@ |
| 285 | 304 | echo $(OX)\configure.obj >> $@ |
| 286 | 305 | echo $(OX)\content.obj >> $@ |
| 306 | + echo $(OX)\cson_amalgamation.obj >> $@ | |
| 287 | 307 | echo $(OX)\db.obj >> $@ |
| 288 | 308 | echo $(OX)\delta.obj >> $@ |
| 289 | 309 | echo $(OX)\deltacmd.obj >> $@ |
| 290 | 310 | echo $(OX)\descendants.obj >> $@ |
| 291 | 311 | echo $(OX)\diff.obj >> $@ |
| @@ -407,12 +427,12 @@ | ||
| 407 | 427 | $(OX)\th_lang$O : $(SRCDIR)\th_lang.c |
| 408 | 428 | $(TCC) /Fo$@ -c $** |
| 409 | 429 | |
| 410 | 430 | VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION |
| 411 | 431 | $** > $@ |
| 412 | -$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h | |
| 413 | - cp $(SRCDIR)\cson_amalgamation.h $@ | |
| 432 | +$(OX)\cson_amalgamation$O : $(SRCDIR)\cson_amalgamation.c | |
| 433 | + $(TCC) /Fo$@ -c $** | |
| 414 | 434 | |
| 415 | 435 | page_index.h: mkindex$E $(SRC) |
| 416 | 436 | $** > $@ |
| 417 | 437 | |
| 418 | 438 | clean: |
| @@ -422,10 +442,11 @@ | ||
| 422 | 442 | -del *.h |
| 423 | 443 | -del *.map |
| 424 | 444 | -del *.manifest |
| 425 | 445 | -del headers |
| 426 | 446 | -del linkopts |
| 447 | + -del *.res | |
| 427 | 448 | |
| 428 | 449 | realclean: clean |
| 429 | 450 | -del $(APPNAME) |
| 430 | 451 | -del translate$E |
| 431 | 452 | -del mkindex$E |
| @@ -1070,10 +1091,12 @@ | ||
| 1070 | 1091 | $(TCC) /Fo$@ -c zip_.c |
| 1071 | 1092 | |
| 1072 | 1093 | zip_.c : $(SRCDIR)\zip.c |
| 1073 | 1094 | translate$E $** > $@ |
| 1074 | 1095 | |
| 1096 | +fossil.res : $B\win\fossil.rc | |
| 1097 | + $(RCC) -fo $@ $** | |
| 1075 | 1098 | headers: makeheaders$E page_index.h VERSION.h |
| 1076 | 1099 | makeheaders$E add_.c:add.h \ |
| 1077 | 1100 | allrepo_.c:allrepo.h \ |
| 1078 | 1101 | attach_.c:attach.h \ |
| 1079 | 1102 | bag_.c:bag.h \ |
| 1080 | 1103 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -22,18 +22,35 @@ | |
| 22 | |
| 23 | # zlib options |
| 24 | ZINCDIR = $(B)\compat\zlib |
| 25 | ZLIBDIR = $(B)\compat\zlib |
| 26 | ZLIB = zlib.lib |
| 27 | |
| 28 | INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR) |
| 29 | |
| 30 | CFLAGS = -nologo -MT -O2 |
| 31 | BCC = $(CC) $(CFLAGS) |
| 32 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) |
| 33 | LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) |
| 34 | LIBDIR = -LIBPATH:$(ZLIBDIR) |
| 35 | |
| 36 | SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 \ |
| 37 | /DSQLITE_THREADSAFE=0 \ |
| 38 | /DSQLITE_DEFAULT_FILE_FORMAT=4 \ |
| 39 | /DSQLITE_ENABLE_STAT3 \ |
| @@ -160,10 +177,11 @@ | |
| 160 | $(OX)\clearsign$O \ |
| 161 | $(OX)\clone$O \ |
| 162 | $(OX)\comformat$O \ |
| 163 | $(OX)\configure$O \ |
| 164 | $(OX)\content$O \ |
| 165 | $(OX)\db$O \ |
| 166 | $(OX)\delta$O \ |
| 167 | $(OX)\deltacmd$O \ |
| 168 | $(OX)\descendants$O \ |
| 169 | $(OX)\diff$O \ |
| @@ -219,19 +237,23 @@ | |
| 219 | $(OX)\rss$O \ |
| 220 | $(OX)\schema$O \ |
| 221 | $(OX)\search$O \ |
| 222 | $(OX)\setup$O \ |
| 223 | $(OX)\sha1$O \ |
| 224 | $(OX)\shun$O \ |
| 225 | $(OX)\skins$O \ |
| 226 | $(OX)\sqlcmd$O \ |
| 227 | $(OX)\stash$O \ |
| 228 | $(OX)\stat$O \ |
| 229 | $(OX)\style$O \ |
| 230 | $(OX)\sync$O \ |
| 231 | $(OX)\tag$O \ |
| 232 | $(OX)\tar$O \ |
| 233 | $(OX)\th_main$O \ |
| 234 | $(OX)\timeline$O \ |
| 235 | $(OX)\tkt$O \ |
| 236 | $(OX)\tktsetup$O \ |
| 237 | $(OX)\undo$O \ |
| @@ -247,14 +269,11 @@ | |
| 247 | $(OX)\winhttp$O \ |
| 248 | $(OX)\wysiwyg$O \ |
| 249 | $(OX)\xfer$O \ |
| 250 | $(OX)\xfersetup$O \ |
| 251 | $(OX)\zip$O \ |
| 252 | $(OX)\shell$O \ |
| 253 | $(OX)\sqlite3$O \ |
| 254 | $(OX)\th$O \ |
| 255 | $(OX)\th_lang$O |
| 256 | |
| 257 | APPNAME = $(OX)\fossil$(E) |
| 258 | |
| 259 | all: $(OX) $(APPNAME) |
| 260 | |
| @@ -262,11 +281,11 @@ | |
| 262 | @echo Building zlib from "$(ZLIBDIR)"... |
| 263 | @pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd |
| 264 | |
| 265 | $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib |
| 266 | cd $(OX) |
| 267 | link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj @linkopts |
| 268 | |
| 269 | $(OX)\linkopts: $B\win\Makefile.msc |
| 270 | echo $(OX)\add.obj > $@ |
| 271 | echo $(OX)\allrepo.obj >> $@ |
| 272 | echo $(OX)\attach.obj >> $@ |
| @@ -282,10 +301,11 @@ | |
| 282 | echo $(OX)\clearsign.obj >> $@ |
| 283 | echo $(OX)\clone.obj >> $@ |
| 284 | echo $(OX)\comformat.obj >> $@ |
| 285 | echo $(OX)\configure.obj >> $@ |
| 286 | echo $(OX)\content.obj >> $@ |
| 287 | echo $(OX)\db.obj >> $@ |
| 288 | echo $(OX)\delta.obj >> $@ |
| 289 | echo $(OX)\deltacmd.obj >> $@ |
| 290 | echo $(OX)\descendants.obj >> $@ |
| 291 | echo $(OX)\diff.obj >> $@ |
| @@ -407,12 +427,12 @@ | |
| 407 | $(OX)\th_lang$O : $(SRCDIR)\th_lang.c |
| 408 | $(TCC) /Fo$@ -c $** |
| 409 | |
| 410 | VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION |
| 411 | $** > $@ |
| 412 | $(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h |
| 413 | cp $(SRCDIR)\cson_amalgamation.h $@ |
| 414 | |
| 415 | page_index.h: mkindex$E $(SRC) |
| 416 | $** > $@ |
| 417 | |
| 418 | clean: |
| @@ -422,10 +442,11 @@ | |
| 422 | -del *.h |
| 423 | -del *.map |
| 424 | -del *.manifest |
| 425 | -del headers |
| 426 | -del linkopts |
| 427 | |
| 428 | realclean: clean |
| 429 | -del $(APPNAME) |
| 430 | -del translate$E |
| 431 | -del mkindex$E |
| @@ -1070,10 +1091,12 @@ | |
| 1070 | $(TCC) /Fo$@ -c zip_.c |
| 1071 | |
| 1072 | zip_.c : $(SRCDIR)\zip.c |
| 1073 | translate$E $** > $@ |
| 1074 | |
| 1075 | headers: makeheaders$E page_index.h VERSION.h |
| 1076 | makeheaders$E add_.c:add.h \ |
| 1077 | allrepo_.c:allrepo.h \ |
| 1078 | attach_.c:attach.h \ |
| 1079 | bag_.c:bag.h \ |
| 1080 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -22,18 +22,35 @@ | |
| 22 | |
| 23 | # zlib options |
| 24 | ZINCDIR = $(B)\compat\zlib |
| 25 | ZLIBDIR = $(B)\compat\zlib |
| 26 | ZLIB = zlib.lib |
| 27 | |
| 28 | # Uncomment to enable JSON API |
| 29 | # FOSSIL_ENABLE_JSON = 1 |
| 30 | |
| 31 | # Uncomment to enable markdown support |
| 32 | # FOSSIL_ENABLE_MARKDOWN = 1 |
| 33 | |
| 34 | INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR) |
| 35 | |
| 36 | CFLAGS = -nologo -MT -O2 |
| 37 | BCC = $(CC) $(CFLAGS) |
| 38 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) |
| 39 | RCC = rc -D_WIN32 -D_MSC_VER $(INCL) |
| 40 | LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) |
| 41 | LIBDIR = -LIBPATH:$(ZLIBDIR) |
| 42 | |
| 43 | !ifdef FOSSIL_ENABLE_JSON |
| 44 | TCC = $(TCC) -DFOSSIL_ENABLE_JSON |
| 45 | RCC = $(RCC) -DFOSSIL_ENABLE_JSON |
| 46 | !endif |
| 47 | |
| 48 | !ifdef FOSSIL_ENABLE_MARKDOWN |
| 49 | TCC = $(TCC) -DFOSSIL_ENABLE_MARKDOWN |
| 50 | RCC = $(RCC) -DFOSSIL_ENABLE_MARKDOWN |
| 51 | !endif |
| 52 | |
| 53 | SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 \ |
| 54 | /DSQLITE_THREADSAFE=0 \ |
| 55 | /DSQLITE_DEFAULT_FILE_FORMAT=4 \ |
| 56 | /DSQLITE_ENABLE_STAT3 \ |
| @@ -160,10 +177,11 @@ | |
| 177 | $(OX)\clearsign$O \ |
| 178 | $(OX)\clone$O \ |
| 179 | $(OX)\comformat$O \ |
| 180 | $(OX)\configure$O \ |
| 181 | $(OX)\content$O \ |
| 182 | $(OX)\cson_amalgamation$O \ |
| 183 | $(OX)\db$O \ |
| 184 | $(OX)\delta$O \ |
| 185 | $(OX)\deltacmd$O \ |
| 186 | $(OX)\descendants$O \ |
| 187 | $(OX)\diff$O \ |
| @@ -219,19 +237,23 @@ | |
| 237 | $(OX)\rss$O \ |
| 238 | $(OX)\schema$O \ |
| 239 | $(OX)\search$O \ |
| 240 | $(OX)\setup$O \ |
| 241 | $(OX)\sha1$O \ |
| 242 | $(OX)\shell$O \ |
| 243 | $(OX)\shun$O \ |
| 244 | $(OX)\skins$O \ |
| 245 | $(OX)\sqlcmd$O \ |
| 246 | $(OX)\sqlite3$O \ |
| 247 | $(OX)\stash$O \ |
| 248 | $(OX)\stat$O \ |
| 249 | $(OX)\style$O \ |
| 250 | $(OX)\sync$O \ |
| 251 | $(OX)\tag$O \ |
| 252 | $(OX)\tar$O \ |
| 253 | $(OX)\th$O \ |
| 254 | $(OX)\th_lang$O \ |
| 255 | $(OX)\th_main$O \ |
| 256 | $(OX)\timeline$O \ |
| 257 | $(OX)\tkt$O \ |
| 258 | $(OX)\tktsetup$O \ |
| 259 | $(OX)\undo$O \ |
| @@ -247,14 +269,11 @@ | |
| 269 | $(OX)\winhttp$O \ |
| 270 | $(OX)\wysiwyg$O \ |
| 271 | $(OX)\xfer$O \ |
| 272 | $(OX)\xfersetup$O \ |
| 273 | $(OX)\zip$O \ |
| 274 | $(OX)\fossil.res |
| 275 | |
| 276 | APPNAME = $(OX)\fossil$(E) |
| 277 | |
| 278 | all: $(OX) $(APPNAME) |
| 279 | |
| @@ -262,11 +281,11 @@ | |
| 281 | @echo Building zlib from "$(ZLIBDIR)"... |
| 282 | @pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd |
| 283 | |
| 284 | $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib |
| 285 | cd $(OX) |
| 286 | link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts |
| 287 | |
| 288 | $(OX)\linkopts: $B\win\Makefile.msc |
| 289 | echo $(OX)\add.obj > $@ |
| 290 | echo $(OX)\allrepo.obj >> $@ |
| 291 | echo $(OX)\attach.obj >> $@ |
| @@ -282,10 +301,11 @@ | |
| 301 | echo $(OX)\clearsign.obj >> $@ |
| 302 | echo $(OX)\clone.obj >> $@ |
| 303 | echo $(OX)\comformat.obj >> $@ |
| 304 | echo $(OX)\configure.obj >> $@ |
| 305 | echo $(OX)\content.obj >> $@ |
| 306 | echo $(OX)\cson_amalgamation.obj >> $@ |
| 307 | echo $(OX)\db.obj >> $@ |
| 308 | echo $(OX)\delta.obj >> $@ |
| 309 | echo $(OX)\deltacmd.obj >> $@ |
| 310 | echo $(OX)\descendants.obj >> $@ |
| 311 | echo $(OX)\diff.obj >> $@ |
| @@ -407,12 +427,12 @@ | |
| 427 | $(OX)\th_lang$O : $(SRCDIR)\th_lang.c |
| 428 | $(TCC) /Fo$@ -c $** |
| 429 | |
| 430 | VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION |
| 431 | $** > $@ |
| 432 | $(OX)\cson_amalgamation$O : $(SRCDIR)\cson_amalgamation.c |
| 433 | $(TCC) /Fo$@ -c $** |
| 434 | |
| 435 | page_index.h: mkindex$E $(SRC) |
| 436 | $** > $@ |
| 437 | |
| 438 | clean: |
| @@ -422,10 +442,11 @@ | |
| 442 | -del *.h |
| 443 | -del *.map |
| 444 | -del *.manifest |
| 445 | -del headers |
| 446 | -del linkopts |
| 447 | -del *.res |
| 448 | |
| 449 | realclean: clean |
| 450 | -del $(APPNAME) |
| 451 | -del translate$E |
| 452 | -del mkindex$E |
| @@ -1070,10 +1091,12 @@ | |
| 1091 | $(TCC) /Fo$@ -c zip_.c |
| 1092 | |
| 1093 | zip_.c : $(SRCDIR)\zip.c |
| 1094 | translate$E $** > $@ |
| 1095 | |
| 1096 | fossil.res : $B\win\fossil.rc |
| 1097 | $(RCC) -fo $@ $** |
| 1098 | headers: makeheaders$E page_index.h VERSION.h |
| 1099 | makeheaders$E add_.c:add.h \ |
| 1100 | allrepo_.c:allrepo.h \ |
| 1101 | attach_.c:attach.h \ |
| 1102 | bag_.c:bag.h \ |
| 1103 |
+30
-7
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -22,18 +22,35 @@ | ||
| 22 | 22 | |
| 23 | 23 | # zlib options |
| 24 | 24 | ZINCDIR = $(B)\compat\zlib |
| 25 | 25 | ZLIBDIR = $(B)\compat\zlib |
| 26 | 26 | ZLIB = zlib.lib |
| 27 | + | |
| 28 | +# Uncomment to enable JSON API | |
| 29 | +# FOSSIL_ENABLE_JSON = 1 | |
| 30 | + | |
| 31 | +# Uncomment to enable markdown support | |
| 32 | +# FOSSIL_ENABLE_MARKDOWN = 1 | |
| 27 | 33 | |
| 28 | 34 | INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR) |
| 29 | 35 | |
| 30 | 36 | CFLAGS = -nologo -MT -O2 |
| 31 | 37 | BCC = $(CC) $(CFLAGS) |
| 32 | 38 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) |
| 39 | +RCC = rc -D_WIN32 -D_MSC_VER $(INCL) | |
| 33 | 40 | LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) |
| 34 | 41 | LIBDIR = -LIBPATH:$(ZLIBDIR) |
| 42 | + | |
| 43 | +!ifdef FOSSIL_ENABLE_JSON | |
| 44 | +TCC = $(TCC) -DFOSSIL_ENABLE_JSON | |
| 45 | +RCC = $(RCC) -DFOSSIL_ENABLE_JSON | |
| 46 | +!endif | |
| 47 | + | |
| 48 | +!ifdef FOSSIL_ENABLE_MARKDOWN | |
| 49 | +TCC = $(TCC) -DFOSSIL_ENABLE_MARKDOWN | |
| 50 | +RCC = $(RCC) -DFOSSIL_ENABLE_MARKDOWN | |
| 51 | +!endif | |
| 35 | 52 | |
| 36 | 53 | SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 \ |
| 37 | 54 | /DSQLITE_THREADSAFE=0 \ |
| 38 | 55 | /DSQLITE_DEFAULT_FILE_FORMAT=4 \ |
| 39 | 56 | /DSQLITE_ENABLE_STAT3 \ |
| @@ -160,10 +177,11 @@ | ||
| 160 | 177 | $(OX)\clearsign$O \ |
| 161 | 178 | $(OX)\clone$O \ |
| 162 | 179 | $(OX)\comformat$O \ |
| 163 | 180 | $(OX)\configure$O \ |
| 164 | 181 | $(OX)\content$O \ |
| 182 | + $(OX)\cson_amalgamation$O \ | |
| 165 | 183 | $(OX)\db$O \ |
| 166 | 184 | $(OX)\delta$O \ |
| 167 | 185 | $(OX)\deltacmd$O \ |
| 168 | 186 | $(OX)\descendants$O \ |
| 169 | 187 | $(OX)\diff$O \ |
| @@ -219,19 +237,23 @@ | ||
| 219 | 237 | $(OX)\rss$O \ |
| 220 | 238 | $(OX)\schema$O \ |
| 221 | 239 | $(OX)\search$O \ |
| 222 | 240 | $(OX)\setup$O \ |
| 223 | 241 | $(OX)\sha1$O \ |
| 242 | + $(OX)\shell$O \ | |
| 224 | 243 | $(OX)\shun$O \ |
| 225 | 244 | $(OX)\skins$O \ |
| 226 | 245 | $(OX)\sqlcmd$O \ |
| 246 | + $(OX)\sqlite3$O \ | |
| 227 | 247 | $(OX)\stash$O \ |
| 228 | 248 | $(OX)\stat$O \ |
| 229 | 249 | $(OX)\style$O \ |
| 230 | 250 | $(OX)\sync$O \ |
| 231 | 251 | $(OX)\tag$O \ |
| 232 | 252 | $(OX)\tar$O \ |
| 253 | + $(OX)\th$O \ | |
| 254 | + $(OX)\th_lang$O \ | |
| 233 | 255 | $(OX)\th_main$O \ |
| 234 | 256 | $(OX)\timeline$O \ |
| 235 | 257 | $(OX)\tkt$O \ |
| 236 | 258 | $(OX)\tktsetup$O \ |
| 237 | 259 | $(OX)\undo$O \ |
| @@ -247,14 +269,11 @@ | ||
| 247 | 269 | $(OX)\winhttp$O \ |
| 248 | 270 | $(OX)\wysiwyg$O \ |
| 249 | 271 | $(OX)\xfer$O \ |
| 250 | 272 | $(OX)\xfersetup$O \ |
| 251 | 273 | $(OX)\zip$O \ |
| 252 | - $(OX)\shell$O \ | |
| 253 | - $(OX)\sqlite3$O \ | |
| 254 | - $(OX)\th$O \ | |
| 255 | - $(OX)\th_lang$O | |
| 274 | + $(OX)\fossil.res | |
| 256 | 275 | |
| 257 | 276 | APPNAME = $(OX)\fossil$(E) |
| 258 | 277 | |
| 259 | 278 | all: $(OX) $(APPNAME) |
| 260 | 279 | |
| @@ -262,11 +281,11 @@ | ||
| 262 | 281 | @echo Building zlib from "$(ZLIBDIR)"... |
| 263 | 282 | @pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd |
| 264 | 283 | |
| 265 | 284 | $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib |
| 266 | 285 | cd $(OX) |
| 267 | - link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj @linkopts | |
| 286 | + link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts | |
| 268 | 287 | |
| 269 | 288 | $(OX)\linkopts: $B\win\Makefile.msc |
| 270 | 289 | echo $(OX)\add.obj > $@ |
| 271 | 290 | echo $(OX)\allrepo.obj >> $@ |
| 272 | 291 | echo $(OX)\attach.obj >> $@ |
| @@ -282,10 +301,11 @@ | ||
| 282 | 301 | echo $(OX)\clearsign.obj >> $@ |
| 283 | 302 | echo $(OX)\clone.obj >> $@ |
| 284 | 303 | echo $(OX)\comformat.obj >> $@ |
| 285 | 304 | echo $(OX)\configure.obj >> $@ |
| 286 | 305 | echo $(OX)\content.obj >> $@ |
| 306 | + echo $(OX)\cson_amalgamation.obj >> $@ | |
| 287 | 307 | echo $(OX)\db.obj >> $@ |
| 288 | 308 | echo $(OX)\delta.obj >> $@ |
| 289 | 309 | echo $(OX)\deltacmd.obj >> $@ |
| 290 | 310 | echo $(OX)\descendants.obj >> $@ |
| 291 | 311 | echo $(OX)\diff.obj >> $@ |
| @@ -407,12 +427,12 @@ | ||
| 407 | 427 | $(OX)\th_lang$O : $(SRCDIR)\th_lang.c |
| 408 | 428 | $(TCC) /Fo$@ -c $** |
| 409 | 429 | |
| 410 | 430 | VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION |
| 411 | 431 | $** > $@ |
| 412 | -$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h | |
| 413 | - cp $(SRCDIR)\cson_amalgamation.h $@ | |
| 432 | +$(OX)\cson_amalgamation$O : $(SRCDIR)\cson_amalgamation.c | |
| 433 | + $(TCC) /Fo$@ -c $** | |
| 414 | 434 | |
| 415 | 435 | page_index.h: mkindex$E $(SRC) |
| 416 | 436 | $** > $@ |
| 417 | 437 | |
| 418 | 438 | clean: |
| @@ -422,10 +442,11 @@ | ||
| 422 | 442 | -del *.h |
| 423 | 443 | -del *.map |
| 424 | 444 | -del *.manifest |
| 425 | 445 | -del headers |
| 426 | 446 | -del linkopts |
| 447 | + -del *.res | |
| 427 | 448 | |
| 428 | 449 | realclean: clean |
| 429 | 450 | -del $(APPNAME) |
| 430 | 451 | -del translate$E |
| 431 | 452 | -del mkindex$E |
| @@ -1070,10 +1091,12 @@ | ||
| 1070 | 1091 | $(TCC) /Fo$@ -c zip_.c |
| 1071 | 1092 | |
| 1072 | 1093 | zip_.c : $(SRCDIR)\zip.c |
| 1073 | 1094 | translate$E $** > $@ |
| 1074 | 1095 | |
| 1096 | +fossil.res : $B\win\fossil.rc | |
| 1097 | + $(RCC) -fo $@ $** | |
| 1075 | 1098 | headers: makeheaders$E page_index.h VERSION.h |
| 1076 | 1099 | makeheaders$E add_.c:add.h \ |
| 1077 | 1100 | allrepo_.c:allrepo.h \ |
| 1078 | 1101 | attach_.c:attach.h \ |
| 1079 | 1102 | bag_.c:bag.h \ |
| 1080 | 1103 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -22,18 +22,35 @@ | |
| 22 | |
| 23 | # zlib options |
| 24 | ZINCDIR = $(B)\compat\zlib |
| 25 | ZLIBDIR = $(B)\compat\zlib |
| 26 | ZLIB = zlib.lib |
| 27 | |
| 28 | INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR) |
| 29 | |
| 30 | CFLAGS = -nologo -MT -O2 |
| 31 | BCC = $(CC) $(CFLAGS) |
| 32 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) |
| 33 | LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) |
| 34 | LIBDIR = -LIBPATH:$(ZLIBDIR) |
| 35 | |
| 36 | SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 \ |
| 37 | /DSQLITE_THREADSAFE=0 \ |
| 38 | /DSQLITE_DEFAULT_FILE_FORMAT=4 \ |
| 39 | /DSQLITE_ENABLE_STAT3 \ |
| @@ -160,10 +177,11 @@ | |
| 160 | $(OX)\clearsign$O \ |
| 161 | $(OX)\clone$O \ |
| 162 | $(OX)\comformat$O \ |
| 163 | $(OX)\configure$O \ |
| 164 | $(OX)\content$O \ |
| 165 | $(OX)\db$O \ |
| 166 | $(OX)\delta$O \ |
| 167 | $(OX)\deltacmd$O \ |
| 168 | $(OX)\descendants$O \ |
| 169 | $(OX)\diff$O \ |
| @@ -219,19 +237,23 @@ | |
| 219 | $(OX)\rss$O \ |
| 220 | $(OX)\schema$O \ |
| 221 | $(OX)\search$O \ |
| 222 | $(OX)\setup$O \ |
| 223 | $(OX)\sha1$O \ |
| 224 | $(OX)\shun$O \ |
| 225 | $(OX)\skins$O \ |
| 226 | $(OX)\sqlcmd$O \ |
| 227 | $(OX)\stash$O \ |
| 228 | $(OX)\stat$O \ |
| 229 | $(OX)\style$O \ |
| 230 | $(OX)\sync$O \ |
| 231 | $(OX)\tag$O \ |
| 232 | $(OX)\tar$O \ |
| 233 | $(OX)\th_main$O \ |
| 234 | $(OX)\timeline$O \ |
| 235 | $(OX)\tkt$O \ |
| 236 | $(OX)\tktsetup$O \ |
| 237 | $(OX)\undo$O \ |
| @@ -247,14 +269,11 @@ | |
| 247 | $(OX)\winhttp$O \ |
| 248 | $(OX)\wysiwyg$O \ |
| 249 | $(OX)\xfer$O \ |
| 250 | $(OX)\xfersetup$O \ |
| 251 | $(OX)\zip$O \ |
| 252 | $(OX)\shell$O \ |
| 253 | $(OX)\sqlite3$O \ |
| 254 | $(OX)\th$O \ |
| 255 | $(OX)\th_lang$O |
| 256 | |
| 257 | APPNAME = $(OX)\fossil$(E) |
| 258 | |
| 259 | all: $(OX) $(APPNAME) |
| 260 | |
| @@ -262,11 +281,11 @@ | |
| 262 | @echo Building zlib from "$(ZLIBDIR)"... |
| 263 | @pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd |
| 264 | |
| 265 | $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib |
| 266 | cd $(OX) |
| 267 | link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj @linkopts |
| 268 | |
| 269 | $(OX)\linkopts: $B\win\Makefile.msc |
| 270 | echo $(OX)\add.obj > $@ |
| 271 | echo $(OX)\allrepo.obj >> $@ |
| 272 | echo $(OX)\attach.obj >> $@ |
| @@ -282,10 +301,11 @@ | |
| 282 | echo $(OX)\clearsign.obj >> $@ |
| 283 | echo $(OX)\clone.obj >> $@ |
| 284 | echo $(OX)\comformat.obj >> $@ |
| 285 | echo $(OX)\configure.obj >> $@ |
| 286 | echo $(OX)\content.obj >> $@ |
| 287 | echo $(OX)\db.obj >> $@ |
| 288 | echo $(OX)\delta.obj >> $@ |
| 289 | echo $(OX)\deltacmd.obj >> $@ |
| 290 | echo $(OX)\descendants.obj >> $@ |
| 291 | echo $(OX)\diff.obj >> $@ |
| @@ -407,12 +427,12 @@ | |
| 407 | $(OX)\th_lang$O : $(SRCDIR)\th_lang.c |
| 408 | $(TCC) /Fo$@ -c $** |
| 409 | |
| 410 | VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION |
| 411 | $** > $@ |
| 412 | $(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h |
| 413 | cp $(SRCDIR)\cson_amalgamation.h $@ |
| 414 | |
| 415 | page_index.h: mkindex$E $(SRC) |
| 416 | $** > $@ |
| 417 | |
| 418 | clean: |
| @@ -422,10 +442,11 @@ | |
| 422 | -del *.h |
| 423 | -del *.map |
| 424 | -del *.manifest |
| 425 | -del headers |
| 426 | -del linkopts |
| 427 | |
| 428 | realclean: clean |
| 429 | -del $(APPNAME) |
| 430 | -del translate$E |
| 431 | -del mkindex$E |
| @@ -1070,10 +1091,12 @@ | |
| 1070 | $(TCC) /Fo$@ -c zip_.c |
| 1071 | |
| 1072 | zip_.c : $(SRCDIR)\zip.c |
| 1073 | translate$E $** > $@ |
| 1074 | |
| 1075 | headers: makeheaders$E page_index.h VERSION.h |
| 1076 | makeheaders$E add_.c:add.h \ |
| 1077 | allrepo_.c:allrepo.h \ |
| 1078 | attach_.c:attach.h \ |
| 1079 | bag_.c:bag.h \ |
| 1080 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -22,18 +22,35 @@ | |
| 22 | |
| 23 | # zlib options |
| 24 | ZINCDIR = $(B)\compat\zlib |
| 25 | ZLIBDIR = $(B)\compat\zlib |
| 26 | ZLIB = zlib.lib |
| 27 | |
| 28 | # Uncomment to enable JSON API |
| 29 | # FOSSIL_ENABLE_JSON = 1 |
| 30 | |
| 31 | # Uncomment to enable markdown support |
| 32 | # FOSSIL_ENABLE_MARKDOWN = 1 |
| 33 | |
| 34 | INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(ZINCDIR) |
| 35 | |
| 36 | CFLAGS = -nologo -MT -O2 |
| 37 | BCC = $(CC) $(CFLAGS) |
| 38 | TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) |
| 39 | RCC = rc -D_WIN32 -D_MSC_VER $(INCL) |
| 40 | LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) |
| 41 | LIBDIR = -LIBPATH:$(ZLIBDIR) |
| 42 | |
| 43 | !ifdef FOSSIL_ENABLE_JSON |
| 44 | TCC = $(TCC) -DFOSSIL_ENABLE_JSON |
| 45 | RCC = $(RCC) -DFOSSIL_ENABLE_JSON |
| 46 | !endif |
| 47 | |
| 48 | !ifdef FOSSIL_ENABLE_MARKDOWN |
| 49 | TCC = $(TCC) -DFOSSIL_ENABLE_MARKDOWN |
| 50 | RCC = $(RCC) -DFOSSIL_ENABLE_MARKDOWN |
| 51 | !endif |
| 52 | |
| 53 | SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 \ |
| 54 | /DSQLITE_THREADSAFE=0 \ |
| 55 | /DSQLITE_DEFAULT_FILE_FORMAT=4 \ |
| 56 | /DSQLITE_ENABLE_STAT3 \ |
| @@ -160,10 +177,11 @@ | |
| 177 | $(OX)\clearsign$O \ |
| 178 | $(OX)\clone$O \ |
| 179 | $(OX)\comformat$O \ |
| 180 | $(OX)\configure$O \ |
| 181 | $(OX)\content$O \ |
| 182 | $(OX)\cson_amalgamation$O \ |
| 183 | $(OX)\db$O \ |
| 184 | $(OX)\delta$O \ |
| 185 | $(OX)\deltacmd$O \ |
| 186 | $(OX)\descendants$O \ |
| 187 | $(OX)\diff$O \ |
| @@ -219,19 +237,23 @@ | |
| 237 | $(OX)\rss$O \ |
| 238 | $(OX)\schema$O \ |
| 239 | $(OX)\search$O \ |
| 240 | $(OX)\setup$O \ |
| 241 | $(OX)\sha1$O \ |
| 242 | $(OX)\shell$O \ |
| 243 | $(OX)\shun$O \ |
| 244 | $(OX)\skins$O \ |
| 245 | $(OX)\sqlcmd$O \ |
| 246 | $(OX)\sqlite3$O \ |
| 247 | $(OX)\stash$O \ |
| 248 | $(OX)\stat$O \ |
| 249 | $(OX)\style$O \ |
| 250 | $(OX)\sync$O \ |
| 251 | $(OX)\tag$O \ |
| 252 | $(OX)\tar$O \ |
| 253 | $(OX)\th$O \ |
| 254 | $(OX)\th_lang$O \ |
| 255 | $(OX)\th_main$O \ |
| 256 | $(OX)\timeline$O \ |
| 257 | $(OX)\tkt$O \ |
| 258 | $(OX)\tktsetup$O \ |
| 259 | $(OX)\undo$O \ |
| @@ -247,14 +269,11 @@ | |
| 269 | $(OX)\winhttp$O \ |
| 270 | $(OX)\wysiwyg$O \ |
| 271 | $(OX)\xfer$O \ |
| 272 | $(OX)\xfersetup$O \ |
| 273 | $(OX)\zip$O \ |
| 274 | $(OX)\fossil.res |
| 275 | |
| 276 | APPNAME = $(OX)\fossil$(E) |
| 277 | |
| 278 | all: $(OX) $(APPNAME) |
| 279 | |
| @@ -262,11 +281,11 @@ | |
| 281 | @echo Building zlib from "$(ZLIBDIR)"... |
| 282 | @pushd "$(ZLIBDIR)" && nmake /f win32\Makefile.msc $(ZLIB) && popd |
| 283 | |
| 284 | $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts zlib |
| 285 | cd $(OX) |
| 286 | link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts |
| 287 | |
| 288 | $(OX)\linkopts: $B\win\Makefile.msc |
| 289 | echo $(OX)\add.obj > $@ |
| 290 | echo $(OX)\allrepo.obj >> $@ |
| 291 | echo $(OX)\attach.obj >> $@ |
| @@ -282,10 +301,11 @@ | |
| 301 | echo $(OX)\clearsign.obj >> $@ |
| 302 | echo $(OX)\clone.obj >> $@ |
| 303 | echo $(OX)\comformat.obj >> $@ |
| 304 | echo $(OX)\configure.obj >> $@ |
| 305 | echo $(OX)\content.obj >> $@ |
| 306 | echo $(OX)\cson_amalgamation.obj >> $@ |
| 307 | echo $(OX)\db.obj >> $@ |
| 308 | echo $(OX)\delta.obj >> $@ |
| 309 | echo $(OX)\deltacmd.obj >> $@ |
| 310 | echo $(OX)\descendants.obj >> $@ |
| 311 | echo $(OX)\diff.obj >> $@ |
| @@ -407,12 +427,12 @@ | |
| 427 | $(OX)\th_lang$O : $(SRCDIR)\th_lang.c |
| 428 | $(TCC) /Fo$@ -c $** |
| 429 | |
| 430 | VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION |
| 431 | $** > $@ |
| 432 | $(OX)\cson_amalgamation$O : $(SRCDIR)\cson_amalgamation.c |
| 433 | $(TCC) /Fo$@ -c $** |
| 434 | |
| 435 | page_index.h: mkindex$E $(SRC) |
| 436 | $** > $@ |
| 437 | |
| 438 | clean: |
| @@ -422,10 +442,11 @@ | |
| 442 | -del *.h |
| 443 | -del *.map |
| 444 | -del *.manifest |
| 445 | -del headers |
| 446 | -del linkopts |
| 447 | -del *.res |
| 448 | |
| 449 | realclean: clean |
| 450 | -del $(APPNAME) |
| 451 | -del translate$E |
| 452 | -del mkindex$E |
| @@ -1070,10 +1091,12 @@ | |
| 1091 | $(TCC) /Fo$@ -c zip_.c |
| 1092 | |
| 1093 | zip_.c : $(SRCDIR)\zip.c |
| 1094 | translate$E $** > $@ |
| 1095 | |
| 1096 | fossil.res : $B\win\fossil.rc |
| 1097 | $(RCC) -fo $@ $** |
| 1098 | headers: makeheaders$E page_index.h VERSION.h |
| 1099 | makeheaders$E add_.c:add.h \ |
| 1100 | allrepo_.c:allrepo.h \ |
| 1101 | attach_.c:attach.h \ |
| 1102 | bag_.c:bag.h \ |
| 1103 |
+3
-3
| --- win/include/dirent.h | ||
| +++ win/include/dirent.h | ||
| @@ -866,18 +866,18 @@ | ||
| 866 | 866 | /* Set errno variable */ |
| 867 | 867 | static void |
| 868 | 868 | dirent_set_errno( |
| 869 | 869 | int error) |
| 870 | 870 | { |
| 871 | -#if defined(_MSC_VER) | |
| 871 | +#if defined(_MSC_VER) && _MSC_VER >= 1400 | |
| 872 | 872 | |
| 873 | - /* Microsoft Visual Studio */ | |
| 873 | + /* Microsoft Visual Studio 2005 and later */ | |
| 874 | 874 | _set_errno (error); |
| 875 | 875 | |
| 876 | 876 | #else |
| 877 | 877 | |
| 878 | - /* Non-Microsoft compiler */ | |
| 878 | + /* Non-Microsoft compiler or older Microsoft compiler */ | |
| 879 | 879 | errno = error; |
| 880 | 880 | |
| 881 | 881 | #endif |
| 882 | 882 | } |
| 883 | 883 | |
| 884 | 884 |
| --- win/include/dirent.h | |
| +++ win/include/dirent.h | |
| @@ -866,18 +866,18 @@ | |
| 866 | /* Set errno variable */ |
| 867 | static void |
| 868 | dirent_set_errno( |
| 869 | int error) |
| 870 | { |
| 871 | #if defined(_MSC_VER) |
| 872 | |
| 873 | /* Microsoft Visual Studio */ |
| 874 | _set_errno (error); |
| 875 | |
| 876 | #else |
| 877 | |
| 878 | /* Non-Microsoft compiler */ |
| 879 | errno = error; |
| 880 | |
| 881 | #endif |
| 882 | } |
| 883 | |
| 884 |
| --- win/include/dirent.h | |
| +++ win/include/dirent.h | |
| @@ -866,18 +866,18 @@ | |
| 866 | /* Set errno variable */ |
| 867 | static void |
| 868 | dirent_set_errno( |
| 869 | int error) |
| 870 | { |
| 871 | #if defined(_MSC_VER) && _MSC_VER >= 1400 |
| 872 | |
| 873 | /* Microsoft Visual Studio 2005 and later */ |
| 874 | _set_errno (error); |
| 875 | |
| 876 | #else |
| 877 | |
| 878 | /* Non-Microsoft compiler or older Microsoft compiler */ |
| 879 | errno = error; |
| 880 | |
| 881 | #endif |
| 882 | } |
| 883 | |
| 884 |
+3
-3
| --- win/include/dirent.h | ||
| +++ win/include/dirent.h | ||
| @@ -866,18 +866,18 @@ | ||
| 866 | 866 | /* Set errno variable */ |
| 867 | 867 | static void |
| 868 | 868 | dirent_set_errno( |
| 869 | 869 | int error) |
| 870 | 870 | { |
| 871 | -#if defined(_MSC_VER) | |
| 871 | +#if defined(_MSC_VER) && _MSC_VER >= 1400 | |
| 872 | 872 | |
| 873 | - /* Microsoft Visual Studio */ | |
| 873 | + /* Microsoft Visual Studio 2005 and later */ | |
| 874 | 874 | _set_errno (error); |
| 875 | 875 | |
| 876 | 876 | #else |
| 877 | 877 | |
| 878 | - /* Non-Microsoft compiler */ | |
| 878 | + /* Non-Microsoft compiler or older Microsoft compiler */ | |
| 879 | 879 | errno = error; |
| 880 | 880 | |
| 881 | 881 | #endif |
| 882 | 882 | } |
| 883 | 883 | |
| 884 | 884 |
| --- win/include/dirent.h | |
| +++ win/include/dirent.h | |
| @@ -866,18 +866,18 @@ | |
| 866 | /* Set errno variable */ |
| 867 | static void |
| 868 | dirent_set_errno( |
| 869 | int error) |
| 870 | { |
| 871 | #if defined(_MSC_VER) |
| 872 | |
| 873 | /* Microsoft Visual Studio */ |
| 874 | _set_errno (error); |
| 875 | |
| 876 | #else |
| 877 | |
| 878 | /* Non-Microsoft compiler */ |
| 879 | errno = error; |
| 880 | |
| 881 | #endif |
| 882 | } |
| 883 | |
| 884 |
| --- win/include/dirent.h | |
| +++ win/include/dirent.h | |
| @@ -866,18 +866,18 @@ | |
| 866 | /* Set errno variable */ |
| 867 | static void |
| 868 | dirent_set_errno( |
| 869 | int error) |
| 870 | { |
| 871 | #if defined(_MSC_VER) && _MSC_VER >= 1400 |
| 872 | |
| 873 | /* Microsoft Visual Studio 2005 and later */ |
| 874 | _set_errno (error); |
| 875 | |
| 876 | #else |
| 877 | |
| 878 | /* Non-Microsoft compiler or older Microsoft compiler */ |
| 879 | errno = error; |
| 880 | |
| 881 | #endif |
| 882 | } |
| 883 | |
| 884 |
+2
-2
| --- www/build.wiki | ||
| +++ www/build.wiki | ||
| @@ -54,11 +54,11 @@ | ||
| 54 | 54 | </ol> |
| 55 | 55 | |
| 56 | 56 | <h2>2.0 Compiling</h2> |
| 57 | 57 | |
| 58 | 58 | <ol> |
| 59 | -<li value="6"> | |
| 59 | +<li value="5"> | |
| 60 | 60 | <p>Unpack the ZIP or tarball you downloaded then |
| 61 | 61 | <b>cd</b> into the directory created.</p></li> |
| 62 | 62 | |
| 63 | 63 | <li><i>(Optional, unix only)</i> |
| 64 | 64 | Run <b>./configure</b> to construct a makefile. |
| @@ -101,11 +101,11 @@ | ||
| 101 | 101 | </ol> |
| 102 | 102 | |
| 103 | 103 | <h2>3.0 Installing</h2> |
| 104 | 104 | |
| 105 | 105 | <ol> |
| 106 | -<li value="9"> | |
| 106 | +<li value="8"> | |
| 107 | 107 | <p>The finished binary is named "fossil" (or "fossil.exe" on windows). |
| 108 | 108 | Put this binary in a |
| 109 | 109 | directory that is somewhere on your PATH environment variable. |
| 110 | 110 | It does not matter where.</p> |
| 111 | 111 | |
| 112 | 112 | |
| 113 | 113 | ADDED www/fiveminutes.wiki |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -54,11 +54,11 @@ | |
| 54 | </ol> |
| 55 | |
| 56 | <h2>2.0 Compiling</h2> |
| 57 | |
| 58 | <ol> |
| 59 | <li value="6"> |
| 60 | <p>Unpack the ZIP or tarball you downloaded then |
| 61 | <b>cd</b> into the directory created.</p></li> |
| 62 | |
| 63 | <li><i>(Optional, unix only)</i> |
| 64 | Run <b>./configure</b> to construct a makefile. |
| @@ -101,11 +101,11 @@ | |
| 101 | </ol> |
| 102 | |
| 103 | <h2>3.0 Installing</h2> |
| 104 | |
| 105 | <ol> |
| 106 | <li value="9"> |
| 107 | <p>The finished binary is named "fossil" (or "fossil.exe" on windows). |
| 108 | Put this binary in a |
| 109 | directory that is somewhere on your PATH environment variable. |
| 110 | It does not matter where.</p> |
| 111 | |
| 112 | |
| 113 | DDED www/fiveminutes.wiki |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -54,11 +54,11 @@ | |
| 54 | </ol> |
| 55 | |
| 56 | <h2>2.0 Compiling</h2> |
| 57 | |
| 58 | <ol> |
| 59 | <li value="5"> |
| 60 | <p>Unpack the ZIP or tarball you downloaded then |
| 61 | <b>cd</b> into the directory created.</p></li> |
| 62 | |
| 63 | <li><i>(Optional, unix only)</i> |
| 64 | Run <b>./configure</b> to construct a makefile. |
| @@ -101,11 +101,11 @@ | |
| 101 | </ol> |
| 102 | |
| 103 | <h2>3.0 Installing</h2> |
| 104 | |
| 105 | <ol> |
| 106 | <li value="8"> |
| 107 | <p>The finished binary is named "fossil" (or "fossil.exe" on windows). |
| 108 | Put this binary in a |
| 109 | directory that is somewhere on your PATH environment variable. |
| 110 | It does not matter where.</p> |
| 111 | |
| 112 | |
| 113 | DDED www/fiveminutes.wiki |
+2
-2
| --- www/build.wiki | ||
| +++ www/build.wiki | ||
| @@ -54,11 +54,11 @@ | ||
| 54 | 54 | </ol> |
| 55 | 55 | |
| 56 | 56 | <h2>2.0 Compiling</h2> |
| 57 | 57 | |
| 58 | 58 | <ol> |
| 59 | -<li value="6"> | |
| 59 | +<li value="5"> | |
| 60 | 60 | <p>Unpack the ZIP or tarball you downloaded then |
| 61 | 61 | <b>cd</b> into the directory created.</p></li> |
| 62 | 62 | |
| 63 | 63 | <li><i>(Optional, unix only)</i> |
| 64 | 64 | Run <b>./configure</b> to construct a makefile. |
| @@ -101,11 +101,11 @@ | ||
| 101 | 101 | </ol> |
| 102 | 102 | |
| 103 | 103 | <h2>3.0 Installing</h2> |
| 104 | 104 | |
| 105 | 105 | <ol> |
| 106 | -<li value="9"> | |
| 106 | +<li value="8"> | |
| 107 | 107 | <p>The finished binary is named "fossil" (or "fossil.exe" on windows). |
| 108 | 108 | Put this binary in a |
| 109 | 109 | directory that is somewhere on your PATH environment variable. |
| 110 | 110 | It does not matter where.</p> |
| 111 | 111 | |
| 112 | 112 | |
| 113 | 113 | ADDED www/fiveminutes.wiki |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -54,11 +54,11 @@ | |
| 54 | </ol> |
| 55 | |
| 56 | <h2>2.0 Compiling</h2> |
| 57 | |
| 58 | <ol> |
| 59 | <li value="6"> |
| 60 | <p>Unpack the ZIP or tarball you downloaded then |
| 61 | <b>cd</b> into the directory created.</p></li> |
| 62 | |
| 63 | <li><i>(Optional, unix only)</i> |
| 64 | Run <b>./configure</b> to construct a makefile. |
| @@ -101,11 +101,11 @@ | |
| 101 | </ol> |
| 102 | |
| 103 | <h2>3.0 Installing</h2> |
| 104 | |
| 105 | <ol> |
| 106 | <li value="9"> |
| 107 | <p>The finished binary is named "fossil" (or "fossil.exe" on windows). |
| 108 | Put this binary in a |
| 109 | directory that is somewhere on your PATH environment variable. |
| 110 | It does not matter where.</p> |
| 111 | |
| 112 | |
| 113 | DDED www/fiveminutes.wiki |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -54,11 +54,11 @@ | |
| 54 | </ol> |
| 55 | |
| 56 | <h2>2.0 Compiling</h2> |
| 57 | |
| 58 | <ol> |
| 59 | <li value="5"> |
| 60 | <p>Unpack the ZIP or tarball you downloaded then |
| 61 | <b>cd</b> into the directory created.</p></li> |
| 62 | |
| 63 | <li><i>(Optional, unix only)</i> |
| 64 | Run <b>./configure</b> to construct a makefile. |
| @@ -101,11 +101,11 @@ | |
| 101 | </ol> |
| 102 | |
| 103 | <h2>3.0 Installing</h2> |
| 104 | |
| 105 | <ol> |
| 106 | <li value="8"> |
| 107 | <p>The finished binary is named "fossil" (or "fossil.exe" on windows). |
| 108 | Put this binary in a |
| 109 | directory that is somewhere on your PATH environment variable. |
| 110 | It does not matter where.</p> |
| 111 | |
| 112 | |
| 113 | DDED www/fiveminutes.wiki |
+40
| --- a/www/fiveminutes.wiki | ||
| +++ b/www/fiveminutes.wiki | ||
| @@ -0,0 +1,40 @@ | ||
| 1 | +<title>Up and running in 5 minutes as a single user</title> | |
| 2 | + | |
| 3 | +<p align="center"><b><i> | |
| 4 | +The following document was contributed by Gilles Ganault on 2013-01-08. | |
| 5 | +</i></b> | |
| 6 | +</p><hr> | |
| 7 | + | |
| 8 | +<h1>Up and running in 5 min<p>tes as a single user</h1> | |
| 9 | + | |
| 10 | +This short document explains the main basic F ossil comands for a single | |
| 11 | +user, i.e. with no additional users, with no need to s | |
| 12 | +repository, and no </p>need for branching/forking. | |
| 13 | + | |
| 14 | +<h2>Cr<p>fossil new c:\test.repo</p> | |
| 15 | +<p>sil new c:\test.repo</tt> | |
| 16 | + | |
| 17 | +This will create the new SQLite binary file that holds the repository, itc. It can be located anywhere, although it's considered | |
| 18 | +best practice to keep it outside the wse to keep it outside the work directory where you will work on files here you will work on files | |
| 19 | +after they've been che</p> | |
| 20 | + | |
| 21 | +<h2>Open the repository</h2> | |
| 22 | +<p>cd c:\temp\test.fossil</p> | |
| 23 | +<p>fossil open c:\test.repo</p> | |
| 24 | +<p>il open c:\test.repo</tt> | |
| 25 | + | |
| 26 | +This will check out the last revision of all t | |
| 27 | +if any, into the current work directory. In addition, it will create a binary | |
| 28 | +file _UUIDIL_ to keep track of changes (on</p>called | |
| 29 | +<tt>.fslckout</tt><p>fossilUUID#1 --to UUIDes to the repository. The files aren't actually | |
| 30 | +added until you run "commit" | |
| 31 | + | |
| 32 | +<h2>Close the repository</h2> | |
| 33 | +<p>fossil c repository</h2> | |
| 34 | +<p>fossil close_FOSSIL_ at the root of the work di>.fslckout</tt>< to the repository. The files aren'es in the repository, | |
| 35 | +if any, into the current work directory. In addition--to UUIDes to the repository. The files aren't actually | |
| 36 | +added until you run "commit" | |
| 37 | + | |
| 38 | +<h2>Close the repository</h2> | |
| 39 | +<p>fossil c repository</h2> | |
| 40 | +<p>fossil close_FOSSIL_ at the root of the work di>.fslckout</tt>< to the repository. The files ar |
| --- a/www/fiveminutes.wiki | |
| +++ b/www/fiveminutes.wiki | |
| @@ -0,0 +1,40 @@ | |
| --- a/www/fiveminutes.wiki | |
| +++ b/www/fiveminutes.wiki | |
| @@ -0,0 +1,40 @@ | |
| 1 | <title>Up and running in 5 minutes as a single user</title> |
| 2 | |
| 3 | <p align="center"><b><i> |
| 4 | The following document was contributed by Gilles Ganault on 2013-01-08. |
| 5 | </i></b> |
| 6 | </p><hr> |
| 7 | |
| 8 | <h1>Up and running in 5 min<p>tes as a single user</h1> |
| 9 | |
| 10 | This short document explains the main basic F ossil comands for a single |
| 11 | user, i.e. with no additional users, with no need to s |
| 12 | repository, and no </p>need for branching/forking. |
| 13 | |
| 14 | <h2>Cr<p>fossil new c:\test.repo</p> |
| 15 | <p>sil new c:\test.repo</tt> |
| 16 | |
| 17 | This will create the new SQLite binary file that holds the repository, itc. It can be located anywhere, although it's considered |
| 18 | best practice to keep it outside the wse to keep it outside the work directory where you will work on files here you will work on files |
| 19 | after they've been che</p> |
| 20 | |
| 21 | <h2>Open the repository</h2> |
| 22 | <p>cd c:\temp\test.fossil</p> |
| 23 | <p>fossil open c:\test.repo</p> |
| 24 | <p>il open c:\test.repo</tt> |
| 25 | |
| 26 | This will check out the last revision of all t |
| 27 | if any, into the current work directory. In addition, it will create a binary |
| 28 | file _UUIDIL_ to keep track of changes (on</p>called |
| 29 | <tt>.fslckout</tt><p>fossilUUID#1 --to UUIDes to the repository. The files aren't actually |
| 30 | added until you run "commit" |
| 31 | |
| 32 | <h2>Close the repository</h2> |
| 33 | <p>fossil c repository</h2> |
| 34 | <p>fossil close_FOSSIL_ at the root of the work di>.fslckout</tt>< to the repository. The files aren'es in the repository, |
| 35 | if any, into the current work directory. In addition--to UUIDes to the repository. The files aren't actually |
| 36 | added until you run "commit" |
| 37 | |
| 38 | <h2>Close the repository</h2> |
| 39 | <p>fossil c repository</h2> |
| 40 | <p>fossil close_FOSSIL_ at the root of the work di>.fslckout</tt>< to the repository. The files ar |
+40
| --- a/www/fiveminutes.wiki | ||
| +++ b/www/fiveminutes.wiki | ||
| @@ -0,0 +1,40 @@ | ||
| 1 | +<title>Up and running in 5 minutes as a single user</title> | |
| 2 | + | |
| 3 | +<p align="center"><b><i> | |
| 4 | +The following document was contributed by Gilles Ganault on 2013-01-08. | |
| 5 | +</i></b> | |
| 6 | +</p><hr> | |
| 7 | + | |
| 8 | +<h1>Up and running in 5 min<p>tes as a single user</h1> | |
| 9 | + | |
| 10 | +This short document explains the main basic F ossil comands for a single | |
| 11 | +user, i.e. with no additional users, with no need to s | |
| 12 | +repository, and no </p>need for branching/forking. | |
| 13 | + | |
| 14 | +<h2>Cr<p>fossil new c:\test.repo</p> | |
| 15 | +<p>sil new c:\test.repo</tt> | |
| 16 | + | |
| 17 | +This will create the new SQLite binary file that holds the repository, itc. It can be located anywhere, although it's considered | |
| 18 | +best practice to keep it outside the wse to keep it outside the work directory where you will work on files here you will work on files | |
| 19 | +after they've been che</p> | |
| 20 | + | |
| 21 | +<h2>Open the repository</h2> | |
| 22 | +<p>cd c:\temp\test.fossil</p> | |
| 23 | +<p>fossil open c:\test.repo</p> | |
| 24 | +<p>il open c:\test.repo</tt> | |
| 25 | + | |
| 26 | +This will check out the last revision of all t | |
| 27 | +if any, into the current work directory. In addition, it will create a binary | |
| 28 | +file _UUIDIL_ to keep track of changes (on</p>called | |
| 29 | +<tt>.fslckout</tt><p>fossilUUID#1 --to UUIDes to the repository. The files aren't actually | |
| 30 | +added until you run "commit" | |
| 31 | + | |
| 32 | +<h2>Close the repository</h2> | |
| 33 | +<p>fossil c repository</h2> | |
| 34 | +<p>fossil close_FOSSIL_ at the root of the work di>.fslckout</tt>< to the repository. The files aren'es in the repository, | |
| 35 | +if any, into the current work directory. In addition--to UUIDes to the repository. The files aren't actually | |
| 36 | +added until you run "commit" | |
| 37 | + | |
| 38 | +<h2>Close the repository</h2> | |
| 39 | +<p>fossil c repository</h2> | |
| 40 | +<p>fossil close_FOSSIL_ at the root of the work di>.fslckout</tt>< to the repository. The files ar |
| --- a/www/fiveminutes.wiki | |
| +++ b/www/fiveminutes.wiki | |
| @@ -0,0 +1,40 @@ | |
| --- a/www/fiveminutes.wiki | |
| +++ b/www/fiveminutes.wiki | |
| @@ -0,0 +1,40 @@ | |
| 1 | <title>Up and running in 5 minutes as a single user</title> |
| 2 | |
| 3 | <p align="center"><b><i> |
| 4 | The following document was contributed by Gilles Ganault on 2013-01-08. |
| 5 | </i></b> |
| 6 | </p><hr> |
| 7 | |
| 8 | <h1>Up and running in 5 min<p>tes as a single user</h1> |
| 9 | |
| 10 | This short document explains the main basic F ossil comands for a single |
| 11 | user, i.e. with no additional users, with no need to s |
| 12 | repository, and no </p>need for branching/forking. |
| 13 | |
| 14 | <h2>Cr<p>fossil new c:\test.repo</p> |
| 15 | <p>sil new c:\test.repo</tt> |
| 16 | |
| 17 | This will create the new SQLite binary file that holds the repository, itc. It can be located anywhere, although it's considered |
| 18 | best practice to keep it outside the wse to keep it outside the work directory where you will work on files here you will work on files |
| 19 | after they've been che</p> |
| 20 | |
| 21 | <h2>Open the repository</h2> |
| 22 | <p>cd c:\temp\test.fossil</p> |
| 23 | <p>fossil open c:\test.repo</p> |
| 24 | <p>il open c:\test.repo</tt> |
| 25 | |
| 26 | This will check out the last revision of all t |
| 27 | if any, into the current work directory. In addition, it will create a binary |
| 28 | file _UUIDIL_ to keep track of changes (on</p>called |
| 29 | <tt>.fslckout</tt><p>fossilUUID#1 --to UUIDes to the repository. The files aren't actually |
| 30 | added until you run "commit" |
| 31 | |
| 32 | <h2>Close the repository</h2> |
| 33 | <p>fossil c repository</h2> |
| 34 | <p>fossil close_FOSSIL_ at the root of the work di>.fslckout</tt>< to the repository. The files aren'es in the repository, |
| 35 | if any, into the current work directory. In addition--to UUIDes to the repository. The files aren't actually |
| 36 | added until you run "commit" |
| 37 | |
| 38 | <h2>Close the repository</h2> |
| 39 | <p>fossil c repository</h2> |
| 40 | <p>fossil close_FOSSIL_ at the root of the work di>.fslckout</tt>< to the repository. The files ar |
+2
| --- www/index.wiki | ||
| +++ www/index.wiki | ||
| @@ -149,10 +149,12 @@ | ||
| 149 | 149 | * How to [./server.wiki | set up a server] for your repository. |
| 150 | 150 | * Customizing the [./custom_ticket.wiki | ticket system]. |
| 151 | 151 | * Methods to [./checkin_names.wiki | identify a specific check-in]. |
| 152 | 152 | * [./inout.wiki | Import and export] from and to Git. |
| 153 | 153 | * [./fossil-v-git.wiki | Fossil versus Git]. |
| 154 | + * [./fiveminutes.wiki | Up and running in 5 minutes as a single user] | |
| 155 | + (contributed by Gilles Ganault on 2013-01-08). | |
| 154 | 156 | |
| 155 | 157 | <h3>Links For Fossil Developer:</h3> |
| 156 | 158 | |
| 157 | 159 | * [./contribute.wiki | Contributing] code or documentation to the |
| 158 | 160 | Fossil project. |
| 159 | 161 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -149,10 +149,12 @@ | |
| 149 | * How to [./server.wiki | set up a server] for your repository. |
| 150 | * Customizing the [./custom_ticket.wiki | ticket system]. |
| 151 | * Methods to [./checkin_names.wiki | identify a specific check-in]. |
| 152 | * [./inout.wiki | Import and export] from and to Git. |
| 153 | * [./fossil-v-git.wiki | Fossil versus Git]. |
| 154 | |
| 155 | <h3>Links For Fossil Developer:</h3> |
| 156 | |
| 157 | * [./contribute.wiki | Contributing] code or documentation to the |
| 158 | Fossil project. |
| 159 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -149,10 +149,12 @@ | |
| 149 | * How to [./server.wiki | set up a server] for your repository. |
| 150 | * Customizing the [./custom_ticket.wiki | ticket system]. |
| 151 | * Methods to [./checkin_names.wiki | identify a specific check-in]. |
| 152 | * [./inout.wiki | Import and export] from and to Git. |
| 153 | * [./fossil-v-git.wiki | Fossil versus Git]. |
| 154 | * [./fiveminutes.wiki | Up and running in 5 minutes as a single user] |
| 155 | (contributed by Gilles Ganault on 2013-01-08). |
| 156 | |
| 157 | <h3>Links For Fossil Developer:</h3> |
| 158 | |
| 159 | * [./contribute.wiki | Contributing] code or documentation to the |
| 160 | Fossil project. |
| 161 |
+2
| --- www/index.wiki | ||
| +++ www/index.wiki | ||
| @@ -149,10 +149,12 @@ | ||
| 149 | 149 | * How to [./server.wiki | set up a server] for your repository. |
| 150 | 150 | * Customizing the [./custom_ticket.wiki | ticket system]. |
| 151 | 151 | * Methods to [./checkin_names.wiki | identify a specific check-in]. |
| 152 | 152 | * [./inout.wiki | Import and export] from and to Git. |
| 153 | 153 | * [./fossil-v-git.wiki | Fossil versus Git]. |
| 154 | + * [./fiveminutes.wiki | Up and running in 5 minutes as a single user] | |
| 155 | + (contributed by Gilles Ganault on 2013-01-08). | |
| 154 | 156 | |
| 155 | 157 | <h3>Links For Fossil Developer:</h3> |
| 156 | 158 | |
| 157 | 159 | * [./contribute.wiki | Contributing] code or documentation to the |
| 158 | 160 | Fossil project. |
| 159 | 161 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -149,10 +149,12 @@ | |
| 149 | * How to [./server.wiki | set up a server] for your repository. |
| 150 | * Customizing the [./custom_ticket.wiki | ticket system]. |
| 151 | * Methods to [./checkin_names.wiki | identify a specific check-in]. |
| 152 | * [./inout.wiki | Import and export] from and to Git. |
| 153 | * [./fossil-v-git.wiki | Fossil versus Git]. |
| 154 | |
| 155 | <h3>Links For Fossil Developer:</h3> |
| 156 | |
| 157 | * [./contribute.wiki | Contributing] code or documentation to the |
| 158 | Fossil project. |
| 159 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -149,10 +149,12 @@ | |
| 149 | * How to [./server.wiki | set up a server] for your repository. |
| 150 | * Customizing the [./custom_ticket.wiki | ticket system]. |
| 151 | * Methods to [./checkin_names.wiki | identify a specific check-in]. |
| 152 | * [./inout.wiki | Import and export] from and to Git. |
| 153 | * [./fossil-v-git.wiki | Fossil versus Git]. |
| 154 | * [./fiveminutes.wiki | Up and running in 5 minutes as a single user] |
| 155 | (contributed by Gilles Ganault on 2013-01-08). |
| 156 | |
| 157 | <h3>Links For Fossil Developer:</h3> |
| 158 | |
| 159 | * [./contribute.wiki | Contributing] code or documentation to the |
| 160 | Fossil project. |
| 161 |
+1
| --- www/mkindex.tcl | ||
| +++ www/mkindex.tcl | ||
| @@ -21,10 +21,11 @@ | ||
| 21 | 21 | delta_format.wiki {Fossil Delta Format} |
| 22 | 22 | embeddeddoc.wiki {Embedded Project Documentation} |
| 23 | 23 | event.wiki {Events} |
| 24 | 24 | faq.wiki {Frequently Asked Questions} |
| 25 | 25 | fileformat.wiki {Fossil File Format} |
| 26 | + fiveminutes.wiki {Update and Running in 5 Minutes as a Single User} | |
| 26 | 27 | foss-cklist.wiki {Checklist For Successful Open-Source Projects} |
| 27 | 28 | fossil-v-git.wiki {Fossil Versus Git} |
| 28 | 29 | index.wiki {Home Page} |
| 29 | 30 | inout.wiki {Import And Export To And From Git} |
| 30 | 31 | makefile.wiki {The Fossil Build Process} |
| 31 | 32 |
| --- www/mkindex.tcl | |
| +++ www/mkindex.tcl | |
| @@ -21,10 +21,11 @@ | |
| 21 | delta_format.wiki {Fossil Delta Format} |
| 22 | embeddeddoc.wiki {Embedded Project Documentation} |
| 23 | event.wiki {Events} |
| 24 | faq.wiki {Frequently Asked Questions} |
| 25 | fileformat.wiki {Fossil File Format} |
| 26 | foss-cklist.wiki {Checklist For Successful Open-Source Projects} |
| 27 | fossil-v-git.wiki {Fossil Versus Git} |
| 28 | index.wiki {Home Page} |
| 29 | inout.wiki {Import And Export To And From Git} |
| 30 | makefile.wiki {The Fossil Build Process} |
| 31 |
| --- www/mkindex.tcl | |
| +++ www/mkindex.tcl | |
| @@ -21,10 +21,11 @@ | |
| 21 | delta_format.wiki {Fossil Delta Format} |
| 22 | embeddeddoc.wiki {Embedded Project Documentation} |
| 23 | event.wiki {Events} |
| 24 | faq.wiki {Frequently Asked Questions} |
| 25 | fileformat.wiki {Fossil File Format} |
| 26 | fiveminutes.wiki {Update and Running in 5 Minutes as a Single User} |
| 27 | foss-cklist.wiki {Checklist For Successful Open-Source Projects} |
| 28 | fossil-v-git.wiki {Fossil Versus Git} |
| 29 | index.wiki {Home Page} |
| 30 | inout.wiki {Import And Export To And From Git} |
| 31 | makefile.wiki {The Fossil Build Process} |
| 32 |
+1
| --- www/mkindex.tcl | ||
| +++ www/mkindex.tcl | ||
| @@ -21,10 +21,11 @@ | ||
| 21 | 21 | delta_format.wiki {Fossil Delta Format} |
| 22 | 22 | embeddeddoc.wiki {Embedded Project Documentation} |
| 23 | 23 | event.wiki {Events} |
| 24 | 24 | faq.wiki {Frequently Asked Questions} |
| 25 | 25 | fileformat.wiki {Fossil File Format} |
| 26 | + fiveminutes.wiki {Update and Running in 5 Minutes as a Single User} | |
| 26 | 27 | foss-cklist.wiki {Checklist For Successful Open-Source Projects} |
| 27 | 28 | fossil-v-git.wiki {Fossil Versus Git} |
| 28 | 29 | index.wiki {Home Page} |
| 29 | 30 | inout.wiki {Import And Export To And From Git} |
| 30 | 31 | makefile.wiki {The Fossil Build Process} |
| 31 | 32 |
| --- www/mkindex.tcl | |
| +++ www/mkindex.tcl | |
| @@ -21,10 +21,11 @@ | |
| 21 | delta_format.wiki {Fossil Delta Format} |
| 22 | embeddeddoc.wiki {Embedded Project Documentation} |
| 23 | event.wiki {Events} |
| 24 | faq.wiki {Frequently Asked Questions} |
| 25 | fileformat.wiki {Fossil File Format} |
| 26 | foss-cklist.wiki {Checklist For Successful Open-Source Projects} |
| 27 | fossil-v-git.wiki {Fossil Versus Git} |
| 28 | index.wiki {Home Page} |
| 29 | inout.wiki {Import And Export To And From Git} |
| 30 | makefile.wiki {The Fossil Build Process} |
| 31 |
| --- www/mkindex.tcl | |
| +++ www/mkindex.tcl | |
| @@ -21,10 +21,11 @@ | |
| 21 | delta_format.wiki {Fossil Delta Format} |
| 22 | embeddeddoc.wiki {Embedded Project Documentation} |
| 23 | event.wiki {Events} |
| 24 | faq.wiki {Frequently Asked Questions} |
| 25 | fileformat.wiki {Fossil File Format} |
| 26 | fiveminutes.wiki {Update and Running in 5 Minutes as a Single User} |
| 27 | foss-cklist.wiki {Checklist For Successful Open-Source Projects} |
| 28 | fossil-v-git.wiki {Fossil Versus Git} |
| 29 | index.wiki {Home Page} |
| 30 | inout.wiki {Import And Export To And From Git} |
| 31 | makefile.wiki {The Fossil Build Process} |
| 32 |
| --- www/permutedindex.wiki | ||
| +++ www/permutedindex.wiki | ||
| @@ -10,13 +10,15 @@ | ||
| 10 | 10 | <li> [/help | Command-line help] |
| 11 | 11 | </ul> |
| 12 | 12 | <a name="pindex"></a> |
| 13 | 13 | <h2>Permuted Index:</h2> |
| 14 | 14 | <ul> |
| 15 | +<li><a href="fiveminutes.wiki">5 Minutes as a Single User — Update and Running in</a></li> | |
| 15 | 16 | <li><a href="tech_overview.wiki">A Technical Overview Of The Design And Implementation Of Fossil</a></li> |
| 16 | 17 | <li><a href="copyright-release.html">Agreement — Contributor License</a></li> |
| 17 | 18 | <li><a href="delta_encoder_algorithm.wiki">Algorithm — Fossil Delta Encoding</a></li> |
| 19 | +<li><a href="fiveminutes.wiki">as a Single User — Update and Running in 5 Minutes</a></li> | |
| 18 | 20 | <li><a href="faq.wiki">Asked Questions — Frequently</a></li> |
| 19 | 21 | <li><a href="password.wiki">Authentication — Password Management And</a></li> |
| 20 | 22 | <li><a href="private.wiki">Branches — Creating, Syncing, and Deleting Private</a></li> |
| 21 | 23 | <li><a href="branching.wiki">Branching, Forking, Merging, and Tagging</a></li> |
| 22 | 24 | <li><a href="bugtheory.wiki">Bug Tracking In Fossil</a></li> |
| @@ -90,10 +92,11 @@ | ||
| 90 | 92 | <li><a href="selfcheck.wiki">Integrity Self Checks — Fossil Repository</a></li> |
| 91 | 93 | <li><a href="webui.wiki">Interface — The Fossil Web</a></li> |
| 92 | 94 | <li><a href="copyright-release.html">License Agreement — Contributor</a></li> |
| 93 | 95 | <li><a href="password.wiki">Management And Authentication — Password</a></li> |
| 94 | 96 | <li><a href="branching.wiki">Merging, and Tagging — Branching, Forking,</a></li> |
| 97 | +<li><a href="fiveminutes.wiki">Minutes as a Single User — Update and Running in 5</a></li> | |
| 95 | 98 | <li><a href="checkin_names.wiki">Names — Checkin And Version</a></li> |
| 96 | 99 | <li><a href="newrepo.wiki">New Fossil Repository — How To Create A</a></li> |
| 97 | 100 | <li><a href="foss-cklist.wiki">Open-Source Projects — Checklist For Successful</a></li> |
| 98 | 101 | <li><a href="pop.wiki">Operations — Principles Of</a></li> |
| 99 | 102 | <li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil — A Technical</a></li> |
| @@ -115,16 +118,18 @@ | ||
| 115 | 118 | <li><a href="quotes.wiki">Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</a></li> |
| 116 | 119 | <li><a href="selfhost.wiki">Repositories — Fossil Self Hosting</a></li> |
| 117 | 120 | <li><a href="newrepo.wiki">Repository — How To Create A New Fossil</a></li> |
| 118 | 121 | <li><a href="selfcheck.wiki">Repository Integrity Self Checks — Fossil</a></li> |
| 119 | 122 | <li><a href="reviews.wiki">Reviews</a></li> |
| 123 | +<li><a href="fiveminutes.wiki">Running in 5 Minutes as a Single User — Update and</a></li> | |
| 120 | 124 | <li><a href="quotes.wiki">Saying About Fossil, Git, and DVCSes in General — Quotes: What People Are</a></li> |
| 121 | 125 | <li><a href="selfcheck.wiki">Self Checks — Fossil Repository Integrity</a></li> |
| 122 | 126 | <li><a href="selfhost.wiki">Self Hosting Repositories — Fossil</a></li> |
| 123 | 127 | <li><a href="server.wiki">Server — How To Configure A Fossil</a></li> |
| 124 | 128 | <li><a href="settings.wiki">Settings — Fossil</a></li> |
| 125 | 129 | <li><a href="shunning.wiki">Shunning: Deleting Content From Fossil</a></li> |
| 130 | +<li><a href="fiveminutes.wiki">Single User — Update and Running in 5 Minutes as a</a></li> | |
| 126 | 131 | <li><a href="style.wiki">Source Code Style Guidelines</a></li> |
| 127 | 132 | <li><a href="tech_overview.wiki">SQLite Databases Used By Fossil</a></li> |
| 128 | 133 | <li><a href="ssl.wiki">SSL with Fossil — Using</a></li> |
| 129 | 134 | <li><a href="quickstart.wiki">Start Guide — Fossil Quick</a></li> |
| 130 | 135 | <li><a href="stats.wiki">Statistics — Performance</a></li> |
| @@ -140,13 +145,15 @@ | ||
| 140 | 145 | <li><a href="sync.wiki">The Fossil Sync Protocol</a></li> |
| 141 | 146 | <li><a href="webui.wiki">The Fossil Web Interface</a></li> |
| 142 | 147 | <li><a href="theory1.wiki">Thoughts On The Design Of The Fossil DVCS</a></li> |
| 143 | 148 | <li><a href="custom_ticket.wiki">Ticket System — Customizing The</a></li> |
| 144 | 149 | <li><a href="bugtheory.wiki">Tracking In Fossil — Bug</a></li> |
| 150 | +<li><a href="fiveminutes.wiki">Update and Running in 5 Minutes as a Single User</a></li> | |
| 151 | +<li><a href="fiveminutes.wiki">User — Update and Running in 5 Minutes as a Single</a></li> | |
| 145 | 152 | <li><a href="ssl.wiki">Using SSL with Fossil</a></li> |
| 146 | 153 | <li><a href="checkin_names.wiki">Version Names — Checkin And</a></li> |
| 147 | 154 | <li><a href="fossil-v-git.wiki">Versus Git — Fossil</a></li> |
| 148 | 155 | <li><a href="webui.wiki">Web Interface — The Fossil</a></li> |
| 149 | 156 | <li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General — Quotes:</a></li> |
| 150 | 157 | <li><a href="wikitheory.wiki">Wiki In Fossil</a></li> |
| 151 | 158 | <li><a href="ssl.wiki">with Fossil — Using SSL</a></li> |
| 152 | 159 | </ul> |
| 153 | 160 |
| --- www/permutedindex.wiki | |
| +++ www/permutedindex.wiki | |
| @@ -10,13 +10,15 @@ | |
| 10 | <li> [/help | Command-line help] |
| 11 | </ul> |
| 12 | <a name="pindex"></a> |
| 13 | <h2>Permuted Index:</h2> |
| 14 | <ul> |
| 15 | <li><a href="tech_overview.wiki">A Technical Overview Of The Design And Implementation Of Fossil</a></li> |
| 16 | <li><a href="copyright-release.html">Agreement — Contributor License</a></li> |
| 17 | <li><a href="delta_encoder_algorithm.wiki">Algorithm — Fossil Delta Encoding</a></li> |
| 18 | <li><a href="faq.wiki">Asked Questions — Frequently</a></li> |
| 19 | <li><a href="password.wiki">Authentication — Password Management And</a></li> |
| 20 | <li><a href="private.wiki">Branches — Creating, Syncing, and Deleting Private</a></li> |
| 21 | <li><a href="branching.wiki">Branching, Forking, Merging, and Tagging</a></li> |
| 22 | <li><a href="bugtheory.wiki">Bug Tracking In Fossil</a></li> |
| @@ -90,10 +92,11 @@ | |
| 90 | <li><a href="selfcheck.wiki">Integrity Self Checks — Fossil Repository</a></li> |
| 91 | <li><a href="webui.wiki">Interface — The Fossil Web</a></li> |
| 92 | <li><a href="copyright-release.html">License Agreement — Contributor</a></li> |
| 93 | <li><a href="password.wiki">Management And Authentication — Password</a></li> |
| 94 | <li><a href="branching.wiki">Merging, and Tagging — Branching, Forking,</a></li> |
| 95 | <li><a href="checkin_names.wiki">Names — Checkin And Version</a></li> |
| 96 | <li><a href="newrepo.wiki">New Fossil Repository — How To Create A</a></li> |
| 97 | <li><a href="foss-cklist.wiki">Open-Source Projects — Checklist For Successful</a></li> |
| 98 | <li><a href="pop.wiki">Operations — Principles Of</a></li> |
| 99 | <li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil — A Technical</a></li> |
| @@ -115,16 +118,18 @@ | |
| 115 | <li><a href="quotes.wiki">Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</a></li> |
| 116 | <li><a href="selfhost.wiki">Repositories — Fossil Self Hosting</a></li> |
| 117 | <li><a href="newrepo.wiki">Repository — How To Create A New Fossil</a></li> |
| 118 | <li><a href="selfcheck.wiki">Repository Integrity Self Checks — Fossil</a></li> |
| 119 | <li><a href="reviews.wiki">Reviews</a></li> |
| 120 | <li><a href="quotes.wiki">Saying About Fossil, Git, and DVCSes in General — Quotes: What People Are</a></li> |
| 121 | <li><a href="selfcheck.wiki">Self Checks — Fossil Repository Integrity</a></li> |
| 122 | <li><a href="selfhost.wiki">Self Hosting Repositories — Fossil</a></li> |
| 123 | <li><a href="server.wiki">Server — How To Configure A Fossil</a></li> |
| 124 | <li><a href="settings.wiki">Settings — Fossil</a></li> |
| 125 | <li><a href="shunning.wiki">Shunning: Deleting Content From Fossil</a></li> |
| 126 | <li><a href="style.wiki">Source Code Style Guidelines</a></li> |
| 127 | <li><a href="tech_overview.wiki">SQLite Databases Used By Fossil</a></li> |
| 128 | <li><a href="ssl.wiki">SSL with Fossil — Using</a></li> |
| 129 | <li><a href="quickstart.wiki">Start Guide — Fossil Quick</a></li> |
| 130 | <li><a href="stats.wiki">Statistics — Performance</a></li> |
| @@ -140,13 +145,15 @@ | |
| 140 | <li><a href="sync.wiki">The Fossil Sync Protocol</a></li> |
| 141 | <li><a href="webui.wiki">The Fossil Web Interface</a></li> |
| 142 | <li><a href="theory1.wiki">Thoughts On The Design Of The Fossil DVCS</a></li> |
| 143 | <li><a href="custom_ticket.wiki">Ticket System — Customizing The</a></li> |
| 144 | <li><a href="bugtheory.wiki">Tracking In Fossil — Bug</a></li> |
| 145 | <li><a href="ssl.wiki">Using SSL with Fossil</a></li> |
| 146 | <li><a href="checkin_names.wiki">Version Names — Checkin And</a></li> |
| 147 | <li><a href="fossil-v-git.wiki">Versus Git — Fossil</a></li> |
| 148 | <li><a href="webui.wiki">Web Interface — The Fossil</a></li> |
| 149 | <li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General — Quotes:</a></li> |
| 150 | <li><a href="wikitheory.wiki">Wiki In Fossil</a></li> |
| 151 | <li><a href="ssl.wiki">with Fossil — Using SSL</a></li> |
| 152 | </ul> |
| 153 |
| --- www/permutedindex.wiki | |
| +++ www/permutedindex.wiki | |
| @@ -10,13 +10,15 @@ | |
| 10 | <li> [/help | Command-line help] |
| 11 | </ul> |
| 12 | <a name="pindex"></a> |
| 13 | <h2>Permuted Index:</h2> |
| 14 | <ul> |
| 15 | <li><a href="fiveminutes.wiki">5 Minutes as a Single User — Update and Running in</a></li> |
| 16 | <li><a href="tech_overview.wiki">A Technical Overview Of The Design And Implementation Of Fossil</a></li> |
| 17 | <li><a href="copyright-release.html">Agreement — Contributor License</a></li> |
| 18 | <li><a href="delta_encoder_algorithm.wiki">Algorithm — Fossil Delta Encoding</a></li> |
| 19 | <li><a href="fiveminutes.wiki">as a Single User — Update and Running in 5 Minutes</a></li> |
| 20 | <li><a href="faq.wiki">Asked Questions — Frequently</a></li> |
| 21 | <li><a href="password.wiki">Authentication — Password Management And</a></li> |
| 22 | <li><a href="private.wiki">Branches — Creating, Syncing, and Deleting Private</a></li> |
| 23 | <li><a href="branching.wiki">Branching, Forking, Merging, and Tagging</a></li> |
| 24 | <li><a href="bugtheory.wiki">Bug Tracking In Fossil</a></li> |
| @@ -90,10 +92,11 @@ | |
| 92 | <li><a href="selfcheck.wiki">Integrity Self Checks — Fossil Repository</a></li> |
| 93 | <li><a href="webui.wiki">Interface — The Fossil Web</a></li> |
| 94 | <li><a href="copyright-release.html">License Agreement — Contributor</a></li> |
| 95 | <li><a href="password.wiki">Management And Authentication — Password</a></li> |
| 96 | <li><a href="branching.wiki">Merging, and Tagging — Branching, Forking,</a></li> |
| 97 | <li><a href="fiveminutes.wiki">Minutes as a Single User — Update and Running in 5</a></li> |
| 98 | <li><a href="checkin_names.wiki">Names — Checkin And Version</a></li> |
| 99 | <li><a href="newrepo.wiki">New Fossil Repository — How To Create A</a></li> |
| 100 | <li><a href="foss-cklist.wiki">Open-Source Projects — Checklist For Successful</a></li> |
| 101 | <li><a href="pop.wiki">Operations — Principles Of</a></li> |
| 102 | <li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil — A Technical</a></li> |
| @@ -115,16 +118,18 @@ | |
| 118 | <li><a href="quotes.wiki">Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</a></li> |
| 119 | <li><a href="selfhost.wiki">Repositories — Fossil Self Hosting</a></li> |
| 120 | <li><a href="newrepo.wiki">Repository — How To Create A New Fossil</a></li> |
| 121 | <li><a href="selfcheck.wiki">Repository Integrity Self Checks — Fossil</a></li> |
| 122 | <li><a href="reviews.wiki">Reviews</a></li> |
| 123 | <li><a href="fiveminutes.wiki">Running in 5 Minutes as a Single User — Update and</a></li> |
| 124 | <li><a href="quotes.wiki">Saying About Fossil, Git, and DVCSes in General — Quotes: What People Are</a></li> |
| 125 | <li><a href="selfcheck.wiki">Self Checks — Fossil Repository Integrity</a></li> |
| 126 | <li><a href="selfhost.wiki">Self Hosting Repositories — Fossil</a></li> |
| 127 | <li><a href="server.wiki">Server — How To Configure A Fossil</a></li> |
| 128 | <li><a href="settings.wiki">Settings — Fossil</a></li> |
| 129 | <li><a href="shunning.wiki">Shunning: Deleting Content From Fossil</a></li> |
| 130 | <li><a href="fiveminutes.wiki">Single User — Update and Running in 5 Minutes as a</a></li> |
| 131 | <li><a href="style.wiki">Source Code Style Guidelines</a></li> |
| 132 | <li><a href="tech_overview.wiki">SQLite Databases Used By Fossil</a></li> |
| 133 | <li><a href="ssl.wiki">SSL with Fossil — Using</a></li> |
| 134 | <li><a href="quickstart.wiki">Start Guide — Fossil Quick</a></li> |
| 135 | <li><a href="stats.wiki">Statistics — Performance</a></li> |
| @@ -140,13 +145,15 @@ | |
| 145 | <li><a href="sync.wiki">The Fossil Sync Protocol</a></li> |
| 146 | <li><a href="webui.wiki">The Fossil Web Interface</a></li> |
| 147 | <li><a href="theory1.wiki">Thoughts On The Design Of The Fossil DVCS</a></li> |
| 148 | <li><a href="custom_ticket.wiki">Ticket System — Customizing The</a></li> |
| 149 | <li><a href="bugtheory.wiki">Tracking In Fossil — Bug</a></li> |
| 150 | <li><a href="fiveminutes.wiki">Update and Running in 5 Minutes as a Single User</a></li> |
| 151 | <li><a href="fiveminutes.wiki">User — Update and Running in 5 Minutes as a Single</a></li> |
| 152 | <li><a href="ssl.wiki">Using SSL with Fossil</a></li> |
| 153 | <li><a href="checkin_names.wiki">Version Names — Checkin And</a></li> |
| 154 | <li><a href="fossil-v-git.wiki">Versus Git — Fossil</a></li> |
| 155 | <li><a href="webui.wiki">Web Interface — The Fossil</a></li> |
| 156 | <li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General — Quotes:</a></li> |
| 157 | <li><a href="wikitheory.wiki">Wiki In Fossil</a></li> |
| 158 | <li><a href="ssl.wiki">with Fossil — Using SSL</a></li> |
| 159 | </ul> |
| 160 |
| --- www/permutedindex.wiki | ||
| +++ www/permutedindex.wiki | ||
| @@ -10,13 +10,15 @@ | ||
| 10 | 10 | <li> [/help | Command-line help] |
| 11 | 11 | </ul> |
| 12 | 12 | <a name="pindex"></a> |
| 13 | 13 | <h2>Permuted Index:</h2> |
| 14 | 14 | <ul> |
| 15 | +<li><a href="fiveminutes.wiki">5 Minutes as a Single User — Update and Running in</a></li> | |
| 15 | 16 | <li><a href="tech_overview.wiki">A Technical Overview Of The Design And Implementation Of Fossil</a></li> |
| 16 | 17 | <li><a href="copyright-release.html">Agreement — Contributor License</a></li> |
| 17 | 18 | <li><a href="delta_encoder_algorithm.wiki">Algorithm — Fossil Delta Encoding</a></li> |
| 19 | +<li><a href="fiveminutes.wiki">as a Single User — Update and Running in 5 Minutes</a></li> | |
| 18 | 20 | <li><a href="faq.wiki">Asked Questions — Frequently</a></li> |
| 19 | 21 | <li><a href="password.wiki">Authentication — Password Management And</a></li> |
| 20 | 22 | <li><a href="private.wiki">Branches — Creating, Syncing, and Deleting Private</a></li> |
| 21 | 23 | <li><a href="branching.wiki">Branching, Forking, Merging, and Tagging</a></li> |
| 22 | 24 | <li><a href="bugtheory.wiki">Bug Tracking In Fossil</a></li> |
| @@ -90,10 +92,11 @@ | ||
| 90 | 92 | <li><a href="selfcheck.wiki">Integrity Self Checks — Fossil Repository</a></li> |
| 91 | 93 | <li><a href="webui.wiki">Interface — The Fossil Web</a></li> |
| 92 | 94 | <li><a href="copyright-release.html">License Agreement — Contributor</a></li> |
| 93 | 95 | <li><a href="password.wiki">Management And Authentication — Password</a></li> |
| 94 | 96 | <li><a href="branching.wiki">Merging, and Tagging — Branching, Forking,</a></li> |
| 97 | +<li><a href="fiveminutes.wiki">Minutes as a Single User — Update and Running in 5</a></li> | |
| 95 | 98 | <li><a href="checkin_names.wiki">Names — Checkin And Version</a></li> |
| 96 | 99 | <li><a href="newrepo.wiki">New Fossil Repository — How To Create A</a></li> |
| 97 | 100 | <li><a href="foss-cklist.wiki">Open-Source Projects — Checklist For Successful</a></li> |
| 98 | 101 | <li><a href="pop.wiki">Operations — Principles Of</a></li> |
| 99 | 102 | <li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil — A Technical</a></li> |
| @@ -115,16 +118,18 @@ | ||
| 115 | 118 | <li><a href="quotes.wiki">Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</a></li> |
| 116 | 119 | <li><a href="selfhost.wiki">Repositories — Fossil Self Hosting</a></li> |
| 117 | 120 | <li><a href="newrepo.wiki">Repository — How To Create A New Fossil</a></li> |
| 118 | 121 | <li><a href="selfcheck.wiki">Repository Integrity Self Checks — Fossil</a></li> |
| 119 | 122 | <li><a href="reviews.wiki">Reviews</a></li> |
| 123 | +<li><a href="fiveminutes.wiki">Running in 5 Minutes as a Single User — Update and</a></li> | |
| 120 | 124 | <li><a href="quotes.wiki">Saying About Fossil, Git, and DVCSes in General — Quotes: What People Are</a></li> |
| 121 | 125 | <li><a href="selfcheck.wiki">Self Checks — Fossil Repository Integrity</a></li> |
| 122 | 126 | <li><a href="selfhost.wiki">Self Hosting Repositories — Fossil</a></li> |
| 123 | 127 | <li><a href="server.wiki">Server — How To Configure A Fossil</a></li> |
| 124 | 128 | <li><a href="settings.wiki">Settings — Fossil</a></li> |
| 125 | 129 | <li><a href="shunning.wiki">Shunning: Deleting Content From Fossil</a></li> |
| 130 | +<li><a href="fiveminutes.wiki">Single User — Update and Running in 5 Minutes as a</a></li> | |
| 126 | 131 | <li><a href="style.wiki">Source Code Style Guidelines</a></li> |
| 127 | 132 | <li><a href="tech_overview.wiki">SQLite Databases Used By Fossil</a></li> |
| 128 | 133 | <li><a href="ssl.wiki">SSL with Fossil — Using</a></li> |
| 129 | 134 | <li><a href="quickstart.wiki">Start Guide — Fossil Quick</a></li> |
| 130 | 135 | <li><a href="stats.wiki">Statistics — Performance</a></li> |
| @@ -140,13 +145,15 @@ | ||
| 140 | 145 | <li><a href="sync.wiki">The Fossil Sync Protocol</a></li> |
| 141 | 146 | <li><a href="webui.wiki">The Fossil Web Interface</a></li> |
| 142 | 147 | <li><a href="theory1.wiki">Thoughts On The Design Of The Fossil DVCS</a></li> |
| 143 | 148 | <li><a href="custom_ticket.wiki">Ticket System — Customizing The</a></li> |
| 144 | 149 | <li><a href="bugtheory.wiki">Tracking In Fossil — Bug</a></li> |
| 150 | +<li><a href="fiveminutes.wiki">Update and Running in 5 Minutes as a Single User</a></li> | |
| 151 | +<li><a href="fiveminutes.wiki">User — Update and Running in 5 Minutes as a Single</a></li> | |
| 145 | 152 | <li><a href="ssl.wiki">Using SSL with Fossil</a></li> |
| 146 | 153 | <li><a href="checkin_names.wiki">Version Names — Checkin And</a></li> |
| 147 | 154 | <li><a href="fossil-v-git.wiki">Versus Git — Fossil</a></li> |
| 148 | 155 | <li><a href="webui.wiki">Web Interface — The Fossil</a></li> |
| 149 | 156 | <li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General — Quotes:</a></li> |
| 150 | 157 | <li><a href="wikitheory.wiki">Wiki In Fossil</a></li> |
| 151 | 158 | <li><a href="ssl.wiki">with Fossil — Using SSL</a></li> |
| 152 | 159 | </ul> |
| 153 | 160 |
| --- www/permutedindex.wiki | |
| +++ www/permutedindex.wiki | |
| @@ -10,13 +10,15 @@ | |
| 10 | <li> [/help | Command-line help] |
| 11 | </ul> |
| 12 | <a name="pindex"></a> |
| 13 | <h2>Permuted Index:</h2> |
| 14 | <ul> |
| 15 | <li><a href="tech_overview.wiki">A Technical Overview Of The Design And Implementation Of Fossil</a></li> |
| 16 | <li><a href="copyright-release.html">Agreement — Contributor License</a></li> |
| 17 | <li><a href="delta_encoder_algorithm.wiki">Algorithm — Fossil Delta Encoding</a></li> |
| 18 | <li><a href="faq.wiki">Asked Questions — Frequently</a></li> |
| 19 | <li><a href="password.wiki">Authentication — Password Management And</a></li> |
| 20 | <li><a href="private.wiki">Branches — Creating, Syncing, and Deleting Private</a></li> |
| 21 | <li><a href="branching.wiki">Branching, Forking, Merging, and Tagging</a></li> |
| 22 | <li><a href="bugtheory.wiki">Bug Tracking In Fossil</a></li> |
| @@ -90,10 +92,11 @@ | |
| 90 | <li><a href="selfcheck.wiki">Integrity Self Checks — Fossil Repository</a></li> |
| 91 | <li><a href="webui.wiki">Interface — The Fossil Web</a></li> |
| 92 | <li><a href="copyright-release.html">License Agreement — Contributor</a></li> |
| 93 | <li><a href="password.wiki">Management And Authentication — Password</a></li> |
| 94 | <li><a href="branching.wiki">Merging, and Tagging — Branching, Forking,</a></li> |
| 95 | <li><a href="checkin_names.wiki">Names — Checkin And Version</a></li> |
| 96 | <li><a href="newrepo.wiki">New Fossil Repository — How To Create A</a></li> |
| 97 | <li><a href="foss-cklist.wiki">Open-Source Projects — Checklist For Successful</a></li> |
| 98 | <li><a href="pop.wiki">Operations — Principles Of</a></li> |
| 99 | <li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil — A Technical</a></li> |
| @@ -115,16 +118,18 @@ | |
| 115 | <li><a href="quotes.wiki">Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</a></li> |
| 116 | <li><a href="selfhost.wiki">Repositories — Fossil Self Hosting</a></li> |
| 117 | <li><a href="newrepo.wiki">Repository — How To Create A New Fossil</a></li> |
| 118 | <li><a href="selfcheck.wiki">Repository Integrity Self Checks — Fossil</a></li> |
| 119 | <li><a href="reviews.wiki">Reviews</a></li> |
| 120 | <li><a href="quotes.wiki">Saying About Fossil, Git, and DVCSes in General — Quotes: What People Are</a></li> |
| 121 | <li><a href="selfcheck.wiki">Self Checks — Fossil Repository Integrity</a></li> |
| 122 | <li><a href="selfhost.wiki">Self Hosting Repositories — Fossil</a></li> |
| 123 | <li><a href="server.wiki">Server — How To Configure A Fossil</a></li> |
| 124 | <li><a href="settings.wiki">Settings — Fossil</a></li> |
| 125 | <li><a href="shunning.wiki">Shunning: Deleting Content From Fossil</a></li> |
| 126 | <li><a href="style.wiki">Source Code Style Guidelines</a></li> |
| 127 | <li><a href="tech_overview.wiki">SQLite Databases Used By Fossil</a></li> |
| 128 | <li><a href="ssl.wiki">SSL with Fossil — Using</a></li> |
| 129 | <li><a href="quickstart.wiki">Start Guide — Fossil Quick</a></li> |
| 130 | <li><a href="stats.wiki">Statistics — Performance</a></li> |
| @@ -140,13 +145,15 @@ | |
| 140 | <li><a href="sync.wiki">The Fossil Sync Protocol</a></li> |
| 141 | <li><a href="webui.wiki">The Fossil Web Interface</a></li> |
| 142 | <li><a href="theory1.wiki">Thoughts On The Design Of The Fossil DVCS</a></li> |
| 143 | <li><a href="custom_ticket.wiki">Ticket System — Customizing The</a></li> |
| 144 | <li><a href="bugtheory.wiki">Tracking In Fossil — Bug</a></li> |
| 145 | <li><a href="ssl.wiki">Using SSL with Fossil</a></li> |
| 146 | <li><a href="checkin_names.wiki">Version Names — Checkin And</a></li> |
| 147 | <li><a href="fossil-v-git.wiki">Versus Git — Fossil</a></li> |
| 148 | <li><a href="webui.wiki">Web Interface — The Fossil</a></li> |
| 149 | <li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General — Quotes:</a></li> |
| 150 | <li><a href="wikitheory.wiki">Wiki In Fossil</a></li> |
| 151 | <li><a href="ssl.wiki">with Fossil — Using SSL</a></li> |
| 152 | </ul> |
| 153 |
| --- www/permutedindex.wiki | |
| +++ www/permutedindex.wiki | |
| @@ -10,13 +10,15 @@ | |
| 10 | <li> [/help | Command-line help] |
| 11 | </ul> |
| 12 | <a name="pindex"></a> |
| 13 | <h2>Permuted Index:</h2> |
| 14 | <ul> |
| 15 | <li><a href="fiveminutes.wiki">5 Minutes as a Single User — Update and Running in</a></li> |
| 16 | <li><a href="tech_overview.wiki">A Technical Overview Of The Design And Implementation Of Fossil</a></li> |
| 17 | <li><a href="copyright-release.html">Agreement — Contributor License</a></li> |
| 18 | <li><a href="delta_encoder_algorithm.wiki">Algorithm — Fossil Delta Encoding</a></li> |
| 19 | <li><a href="fiveminutes.wiki">as a Single User — Update and Running in 5 Minutes</a></li> |
| 20 | <li><a href="faq.wiki">Asked Questions — Frequently</a></li> |
| 21 | <li><a href="password.wiki">Authentication — Password Management And</a></li> |
| 22 | <li><a href="private.wiki">Branches — Creating, Syncing, and Deleting Private</a></li> |
| 23 | <li><a href="branching.wiki">Branching, Forking, Merging, and Tagging</a></li> |
| 24 | <li><a href="bugtheory.wiki">Bug Tracking In Fossil</a></li> |
| @@ -90,10 +92,11 @@ | |
| 92 | <li><a href="selfcheck.wiki">Integrity Self Checks — Fossil Repository</a></li> |
| 93 | <li><a href="webui.wiki">Interface — The Fossil Web</a></li> |
| 94 | <li><a href="copyright-release.html">License Agreement — Contributor</a></li> |
| 95 | <li><a href="password.wiki">Management And Authentication — Password</a></li> |
| 96 | <li><a href="branching.wiki">Merging, and Tagging — Branching, Forking,</a></li> |
| 97 | <li><a href="fiveminutes.wiki">Minutes as a Single User — Update and Running in 5</a></li> |
| 98 | <li><a href="checkin_names.wiki">Names — Checkin And Version</a></li> |
| 99 | <li><a href="newrepo.wiki">New Fossil Repository — How To Create A</a></li> |
| 100 | <li><a href="foss-cklist.wiki">Open-Source Projects — Checklist For Successful</a></li> |
| 101 | <li><a href="pop.wiki">Operations — Principles Of</a></li> |
| 102 | <li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil — A Technical</a></li> |
| @@ -115,16 +118,18 @@ | |
| 118 | <li><a href="quotes.wiki">Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</a></li> |
| 119 | <li><a href="selfhost.wiki">Repositories — Fossil Self Hosting</a></li> |
| 120 | <li><a href="newrepo.wiki">Repository — How To Create A New Fossil</a></li> |
| 121 | <li><a href="selfcheck.wiki">Repository Integrity Self Checks — Fossil</a></li> |
| 122 | <li><a href="reviews.wiki">Reviews</a></li> |
| 123 | <li><a href="fiveminutes.wiki">Running in 5 Minutes as a Single User — Update and</a></li> |
| 124 | <li><a href="quotes.wiki">Saying About Fossil, Git, and DVCSes in General — Quotes: What People Are</a></li> |
| 125 | <li><a href="selfcheck.wiki">Self Checks — Fossil Repository Integrity</a></li> |
| 126 | <li><a href="selfhost.wiki">Self Hosting Repositories — Fossil</a></li> |
| 127 | <li><a href="server.wiki">Server — How To Configure A Fossil</a></li> |
| 128 | <li><a href="settings.wiki">Settings — Fossil</a></li> |
| 129 | <li><a href="shunning.wiki">Shunning: Deleting Content From Fossil</a></li> |
| 130 | <li><a href="fiveminutes.wiki">Single User — Update and Running in 5 Minutes as a</a></li> |
| 131 | <li><a href="style.wiki">Source Code Style Guidelines</a></li> |
| 132 | <li><a href="tech_overview.wiki">SQLite Databases Used By Fossil</a></li> |
| 133 | <li><a href="ssl.wiki">SSL with Fossil — Using</a></li> |
| 134 | <li><a href="quickstart.wiki">Start Guide — Fossil Quick</a></li> |
| 135 | <li><a href="stats.wiki">Statistics — Performance</a></li> |
| @@ -140,13 +145,15 @@ | |
| 145 | <li><a href="sync.wiki">The Fossil Sync Protocol</a></li> |
| 146 | <li><a href="webui.wiki">The Fossil Web Interface</a></li> |
| 147 | <li><a href="theory1.wiki">Thoughts On The Design Of The Fossil DVCS</a></li> |
| 148 | <li><a href="custom_ticket.wiki">Ticket System — Customizing The</a></li> |
| 149 | <li><a href="bugtheory.wiki">Tracking In Fossil — Bug</a></li> |
| 150 | <li><a href="fiveminutes.wiki">Update and Running in 5 Minutes as a Single User</a></li> |
| 151 | <li><a href="fiveminutes.wiki">User — Update and Running in 5 Minutes as a Single</a></li> |
| 152 | <li><a href="ssl.wiki">Using SSL with Fossil</a></li> |
| 153 | <li><a href="checkin_names.wiki">Version Names — Checkin And</a></li> |
| 154 | <li><a href="fossil-v-git.wiki">Versus Git — Fossil</a></li> |
| 155 | <li><a href="webui.wiki">Web Interface — The Fossil</a></li> |
| 156 | <li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General — Quotes:</a></li> |
| 157 | <li><a href="wikitheory.wiki">Wiki In Fossil</a></li> |
| 158 | <li><a href="ssl.wiki">with Fossil — Using SSL</a></li> |
| 159 | </ul> |
| 160 |